]> git.sesse.net Git - ffmpeg/blob - libavcodec/pcxenc.c
Support reading 64bit sgi images.
[ffmpeg] / libavcodec / pcxenc.c
1 /*
2  * PC Paintbrush PCX (.pcx) image encoder
3  * Copyright (c) 2009 Daniel Verkamp <daniel at drv.nu>
4  *
5  * This file is part of FFmpeg.
6  *
7  * FFmpeg is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * FFmpeg is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with FFmpeg; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20  */
21
22 /**
23  * @file
24  * PCX image encoder
25  * @author Daniel Verkamp
26  * @see http://www.qzx.com/pc-gpe/pcx.txt
27  */
28
29 #include "avcodec.h"
30 #include "bytestream.h"
31 #include "libavutil/imgutils.h"
32
33 typedef struct PCXContext {
34     AVFrame picture;
35 } PCXContext;
36
37 static const uint32_t monoblack_pal[16] = { 0x000000, 0xFFFFFF };
38
39 static av_cold int pcx_encode_init(AVCodecContext *avctx)
40 {
41     PCXContext *s = avctx->priv_data;
42
43     avcodec_get_frame_defaults(&s->picture);
44     avctx->coded_frame = &s->picture;
45
46     return 0;
47 }
48
49 /**
50  * PCX run-length encoder
51  * @param dst output buffer
52  * @param dst_size size of output buffer
53  * @param src input buffer
54  * @param src_plane_size size of one plane of input buffer in bytes
55  * @param nplanes number of planes in input buffer
56  * @return number of bytes written to dst or -1 on error
57  * @bug will not work for nplanes != 1 && bpp != 8
58  */
59 static int pcx_rle_encode(      uint8_t *dst, int dst_size,
60                           const uint8_t *src, int src_plane_size, int nplanes)
61 {
62     int p;
63     const uint8_t *dst_start = dst;
64
65     // check worst-case upper bound on dst_size
66     if (dst_size < 2LL * src_plane_size * nplanes || src_plane_size <= 0)
67         return -1;
68
69     for (p = 0; p < nplanes; p++) {
70         int count = 1;
71         const uint8_t *src_plane = src + p;
72         const uint8_t *src_plane_end = src_plane + src_plane_size * nplanes;
73         uint8_t prev = *src_plane;
74         src_plane += nplanes;
75
76         for (; ; src_plane += nplanes) {
77             if (src_plane < src_plane_end && *src_plane == prev && count < 0x3F) {
78                 // current byte is same as prev
79                 ++count;
80             } else {
81                 // output prev * count
82                 if (count != 1 || prev >= 0xC0)
83                     *dst++ = 0xC0 | count;
84                 *dst++ = prev;
85
86                 if (src_plane == src_plane_end)
87                     break;
88
89                 // start new run
90                 count = 1;
91                 prev = *src_plane;
92             }
93         }
94     }
95
96     return dst - dst_start;
97 }
98
99 static int pcx_encode_frame(AVCodecContext *avctx,
100                             unsigned char *buf, int buf_size, void *data)
101 {
102     PCXContext *s = avctx->priv_data;
103     AVFrame *const pict = &s->picture;
104     const uint8_t *buf_start = buf;
105     const uint8_t *buf_end   = buf + buf_size;
106
107     int bpp, nplanes, i, y, line_bytes, written;
108     const uint32_t *pal = NULL;
109     uint32_t palette256[256];
110     const uint8_t *src;
111
112     *pict = *(AVFrame *)data;
113     pict->pict_type = AV_PICTURE_TYPE_I;
114     pict->key_frame = 1;
115
116     if (avctx->width > 65535 || avctx->height > 65535) {
117         av_log(avctx, AV_LOG_ERROR, "image dimensions do not fit in 16 bits\n");
118         return -1;
119     }
120
121     switch (avctx->pix_fmt) {
122     case PIX_FMT_RGB24:
123         bpp = 8;
124         nplanes = 3;
125         break;
126     case PIX_FMT_RGB8:
127     case PIX_FMT_BGR8:
128     case PIX_FMT_RGB4_BYTE:
129     case PIX_FMT_BGR4_BYTE:
130     case PIX_FMT_GRAY8:
131         bpp = 8;
132         nplanes = 1;
133         ff_set_systematic_pal2(palette256, avctx->pix_fmt);
134         pal = palette256;
135         break;
136     case PIX_FMT_PAL8:
137         bpp = 8;
138         nplanes = 1;
139         pal = (uint32_t *)pict->data[1];
140         break;
141     case PIX_FMT_MONOBLACK:
142         bpp = 1;
143         nplanes = 1;
144         pal = monoblack_pal;
145         break;
146     default:
147         av_log(avctx, AV_LOG_ERROR, "unsupported pixfmt\n");
148         return -1;
149     }
150
151     line_bytes = (avctx->width * bpp + 7) >> 3;
152     line_bytes = (line_bytes + 1) & ~1;
153
154     bytestream_put_byte(&buf, 10);                  // manufacturer
155     bytestream_put_byte(&buf, 5);                   // version
156     bytestream_put_byte(&buf, 1);                   // encoding
157     bytestream_put_byte(&buf, bpp);                 // bits per pixel per plane
158     bytestream_put_le16(&buf, 0);                   // x min
159     bytestream_put_le16(&buf, 0);                   // y min
160     bytestream_put_le16(&buf, avctx->width - 1);    // x max
161     bytestream_put_le16(&buf, avctx->height - 1);   // y max
162     bytestream_put_le16(&buf, 0);                   // horizontal DPI
163     bytestream_put_le16(&buf, 0);                   // vertical DPI
164     for (i = 0; i < 16; i++)
165         bytestream_put_be24(&buf, pal ? pal[i] : 0);// palette (<= 16 color only)
166     bytestream_put_byte(&buf, 0);                   // reserved
167     bytestream_put_byte(&buf, nplanes);             // number of planes
168     bytestream_put_le16(&buf, line_bytes);          // scanline plane size in bytes
169
170     while (buf - buf_start < 128)
171         *buf++= 0;
172
173     src = pict->data[0];
174
175     for (y = 0; y < avctx->height; y++) {
176         if ((written = pcx_rle_encode(buf, buf_end - buf,
177                                       src, line_bytes, nplanes)) < 0) {
178             av_log(avctx, AV_LOG_ERROR, "buffer too small\n");
179             return -1;
180         }
181         buf += written;
182         src += pict->linesize[0];
183     }
184
185     if (nplanes == 1 && bpp == 8) {
186         if (buf_end - buf < 257) {
187             av_log(avctx, AV_LOG_ERROR, "buffer too small\n");
188             return -1;
189         }
190         bytestream_put_byte(&buf, 12);
191         for (i = 0; i < 256; i++) {
192             bytestream_put_be24(&buf, pal[i]);
193         }
194     }
195
196     return buf - buf_start;
197 }
198
199 AVCodec ff_pcx_encoder = {
200     .name           = "pcx",
201     .type           = AVMEDIA_TYPE_VIDEO,
202     .id             = CODEC_ID_PCX,
203     .priv_data_size = sizeof(PCXContext),
204     .init           = pcx_encode_init,
205     .encode         = pcx_encode_frame,
206     .pix_fmts = (const enum PixelFormat[]){
207         PIX_FMT_RGB24,
208         PIX_FMT_RGB8, PIX_FMT_BGR8, PIX_FMT_RGB4_BYTE, PIX_FMT_BGR4_BYTE, PIX_FMT_GRAY8, PIX_FMT_PAL8,
209         PIX_FMT_MONOBLACK,
210         PIX_FMT_NONE},
211     .long_name = NULL_IF_CONFIG_SMALL("PC Paintbrush PCX image"),
212 };