Commit | Line | Data |
---|---|---|
2ba45a60 DM |
1 | /* |
2 | * PC Paintbrush PCX (.pcx) image decoder | |
3 | * Copyright (c) 2007, 2008 Ivo van Poorten | |
4 | * | |
5 | * This decoder does not support CGA palettes. I am unable to find samples | |
6 | * and Netpbm cannot generate them. | |
7 | * | |
8 | * This file is part of FFmpeg. | |
9 | * | |
10 | * FFmpeg is free software; you can redistribute it and/or | |
11 | * modify it under the terms of the GNU Lesser General Public | |
12 | * License as published by the Free Software Foundation; either | |
13 | * version 2.1 of the License, or (at your option) any later version. | |
14 | * | |
15 | * FFmpeg is distributed in the hope that it will be useful, | |
16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
18 | * Lesser General Public License for more details. | |
19 | * | |
20 | * You should have received a copy of the GNU Lesser General Public | |
21 | * License along with FFmpeg; if not, write to the Free Software | |
22 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |
23 | */ | |
24 | ||
25 | #include "libavutil/imgutils.h" | |
26 | #include "avcodec.h" | |
27 | #include "bytestream.h" | |
28 | #include "get_bits.h" | |
29 | #include "internal.h" | |
30 | ||
31 | static void pcx_rle_decode(GetByteContext *gb, | |
32 | uint8_t *dst, | |
33 | unsigned int bytes_per_scanline, | |
34 | int compressed) | |
35 | { | |
36 | unsigned int i = 0; | |
37 | unsigned char run, value; | |
38 | ||
39 | if (compressed) { | |
40 | while (i < bytes_per_scanline && bytestream2_get_bytes_left(gb)>0) { | |
41 | run = 1; | |
42 | value = bytestream2_get_byte(gb); | |
43 | if (value >= 0xc0 && bytestream2_get_bytes_left(gb)>0) { | |
44 | run = value & 0x3f; | |
45 | value = bytestream2_get_byte(gb); | |
46 | } | |
47 | while (i < bytes_per_scanline && run--) | |
48 | dst[i++] = value; | |
49 | } | |
50 | } else { | |
51 | bytestream2_get_buffer(gb, dst, bytes_per_scanline); | |
52 | } | |
53 | } | |
54 | ||
55 | static void pcx_palette(GetByteContext *gb, uint32_t *dst, int pallen) | |
56 | { | |
57 | int i; | |
58 | ||
59 | pallen = FFMIN(pallen, bytestream2_get_bytes_left(gb) / 3); | |
60 | for (i = 0; i < pallen; i++) | |
61 | *dst++ = 0xFF000000 | bytestream2_get_be24u(gb); | |
62 | if (pallen < 256) | |
63 | memset(dst, 0, (256 - pallen) * sizeof(*dst)); | |
64 | } | |
65 | ||
66 | static int pcx_decode_frame(AVCodecContext *avctx, void *data, int *got_frame, | |
67 | AVPacket *avpkt) | |
68 | { | |
69 | GetByteContext gb; | |
70 | AVFrame * const p = data; | |
71 | int compressed, xmin, ymin, xmax, ymax; | |
72 | int ret; | |
73 | unsigned int w, h, bits_per_pixel, bytes_per_line, nplanes, stride, y, x, | |
74 | bytes_per_scanline; | |
75 | uint8_t *ptr, *scanline; | |
76 | ||
77 | if (avpkt->size < 128) | |
78 | return AVERROR_INVALIDDATA; | |
79 | ||
80 | bytestream2_init(&gb, avpkt->data, avpkt->size); | |
81 | ||
82 | if (bytestream2_get_byteu(&gb) != 0x0a || bytestream2_get_byteu(&gb) > 5) { | |
83 | av_log(avctx, AV_LOG_ERROR, "this is not PCX encoded data\n"); | |
84 | return AVERROR_INVALIDDATA; | |
85 | } | |
86 | ||
87 | compressed = bytestream2_get_byteu(&gb); | |
88 | bits_per_pixel = bytestream2_get_byteu(&gb); | |
89 | xmin = bytestream2_get_le16u(&gb); | |
90 | ymin = bytestream2_get_le16u(&gb); | |
91 | xmax = bytestream2_get_le16u(&gb); | |
92 | ymax = bytestream2_get_le16u(&gb); | |
93 | avctx->sample_aspect_ratio.num = bytestream2_get_le16u(&gb); | |
94 | avctx->sample_aspect_ratio.den = bytestream2_get_le16u(&gb); | |
95 | ||
96 | if (xmax < xmin || ymax < ymin) { | |
97 | av_log(avctx, AV_LOG_ERROR, "invalid image dimensions\n"); | |
98 | return AVERROR_INVALIDDATA; | |
99 | } | |
100 | ||
101 | w = xmax - xmin + 1; | |
102 | h = ymax - ymin + 1; | |
103 | ||
104 | bytestream2_skipu(&gb, 49); | |
105 | nplanes = bytestream2_get_byteu(&gb); | |
106 | bytes_per_line = bytestream2_get_le16u(&gb); | |
107 | bytes_per_scanline = nplanes * bytes_per_line; | |
108 | ||
109 | if (bytes_per_scanline < (w * bits_per_pixel * nplanes + 7) / 8 || | |
110 | (!compressed && bytes_per_scanline > bytestream2_get_bytes_left(&gb) / h)) { | |
111 | av_log(avctx, AV_LOG_ERROR, "PCX data is corrupted\n"); | |
112 | return AVERROR_INVALIDDATA; | |
113 | } | |
114 | ||
115 | switch ((nplanes << 8) + bits_per_pixel) { | |
116 | case 0x0308: | |
117 | avctx->pix_fmt = AV_PIX_FMT_RGB24; | |
118 | break; | |
119 | case 0x0108: | |
120 | case 0x0104: | |
121 | case 0x0102: | |
122 | case 0x0101: | |
123 | case 0x0401: | |
124 | case 0x0301: | |
125 | case 0x0201: | |
126 | avctx->pix_fmt = AV_PIX_FMT_PAL8; | |
127 | break; | |
128 | default: | |
129 | av_log(avctx, AV_LOG_ERROR, "invalid PCX file\n"); | |
130 | return AVERROR_INVALIDDATA; | |
131 | } | |
132 | ||
133 | bytestream2_skipu(&gb, 60); | |
134 | ||
135 | if ((ret = ff_set_dimensions(avctx, w, h)) < 0) | |
136 | return ret; | |
137 | ||
138 | if ((ret = ff_get_buffer(avctx, p, 0)) < 0) | |
139 | return ret; | |
140 | ||
141 | p->pict_type = AV_PICTURE_TYPE_I; | |
142 | ||
143 | ptr = p->data[0]; | |
144 | stride = p->linesize[0]; | |
145 | ||
146 | scanline = av_malloc(bytes_per_scanline + FF_INPUT_BUFFER_PADDING_SIZE); | |
147 | if (!scanline) | |
148 | return AVERROR(ENOMEM); | |
149 | ||
150 | if (nplanes == 3 && bits_per_pixel == 8) { | |
151 | for (y = 0; y < h; y++) { | |
152 | pcx_rle_decode(&gb, scanline, bytes_per_scanline, compressed); | |
153 | ||
154 | for (x = 0; x < w; x++) { | |
155 | ptr[3 * x] = scanline[x]; | |
156 | ptr[3 * x + 1] = scanline[x + bytes_per_line]; | |
157 | ptr[3 * x + 2] = scanline[x + (bytes_per_line << 1)]; | |
158 | } | |
159 | ||
160 | ptr += stride; | |
161 | } | |
162 | } else if (nplanes == 1 && bits_per_pixel == 8) { | |
163 | int palstart = avpkt->size - 769; | |
164 | ||
165 | if (avpkt->size < 769) { | |
166 | av_log(avctx, AV_LOG_ERROR, "File is too short\n"); | |
167 | ret = avctx->err_recognition & AV_EF_EXPLODE ? | |
168 | AVERROR_INVALIDDATA : avpkt->size; | |
169 | goto end; | |
170 | } | |
171 | ||
172 | for (y = 0; y < h; y++, ptr += stride) { | |
173 | pcx_rle_decode(&gb, scanline, bytes_per_scanline, compressed); | |
174 | memcpy(ptr, scanline, w); | |
175 | } | |
176 | ||
177 | if (bytestream2_tell(&gb) != palstart) { | |
178 | av_log(avctx, AV_LOG_WARNING, "image data possibly corrupted\n"); | |
179 | bytestream2_seek(&gb, palstart, SEEK_SET); | |
180 | } | |
181 | if (bytestream2_get_byte(&gb) != 12) { | |
182 | av_log(avctx, AV_LOG_ERROR, "expected palette after image data\n"); | |
183 | ret = avctx->err_recognition & AV_EF_EXPLODE ? | |
184 | AVERROR_INVALIDDATA : avpkt->size; | |
185 | goto end; | |
186 | } | |
187 | } else if (nplanes == 1) { /* all packed formats, max. 16 colors */ | |
188 | GetBitContext s; | |
189 | ||
190 | for (y = 0; y < h; y++) { | |
191 | init_get_bits8(&s, scanline, bytes_per_scanline); | |
192 | ||
193 | pcx_rle_decode(&gb, scanline, bytes_per_scanline, compressed); | |
194 | ||
195 | for (x = 0; x < w; x++) | |
196 | ptr[x] = get_bits(&s, bits_per_pixel); | |
197 | ptr += stride; | |
198 | } | |
199 | } else { /* planar, 4, 8 or 16 colors */ | |
200 | int i; | |
201 | ||
202 | for (y = 0; y < h; y++) { | |
203 | pcx_rle_decode(&gb, scanline, bytes_per_scanline, compressed); | |
204 | ||
205 | for (x = 0; x < w; x++) { | |
206 | int m = 0x80 >> (x & 7), v = 0; | |
207 | for (i = nplanes - 1; i >= 0; i--) { | |
208 | v <<= 1; | |
209 | v += !!(scanline[i * bytes_per_line + (x >> 3)] & m); | |
210 | } | |
211 | ptr[x] = v; | |
212 | } | |
213 | ptr += stride; | |
214 | } | |
215 | } | |
216 | ||
217 | ret = bytestream2_tell(&gb); | |
218 | if (nplanes == 1 && bits_per_pixel == 8) { | |
219 | pcx_palette(&gb, (uint32_t *)p->data[1], 256); | |
220 | ret += 256 * 3; | |
221 | } else if (bits_per_pixel * nplanes == 1) { | |
222 | AV_WN32A(p->data[1] , 0xFF000000); | |
223 | AV_WN32A(p->data[1]+4, 0xFFFFFFFF); | |
224 | } else if (bits_per_pixel < 8) { | |
225 | bytestream2_seek(&gb, 16, SEEK_SET); | |
226 | pcx_palette(&gb, (uint32_t *)p->data[1], 16); | |
227 | } | |
228 | ||
229 | *got_frame = 1; | |
230 | ||
231 | end: | |
232 | av_free(scanline); | |
233 | return ret; | |
234 | } | |
235 | ||
236 | AVCodec ff_pcx_decoder = { | |
237 | .name = "pcx", | |
238 | .long_name = NULL_IF_CONFIG_SMALL("PC Paintbrush PCX image"), | |
239 | .type = AVMEDIA_TYPE_VIDEO, | |
240 | .id = AV_CODEC_ID_PCX, | |
241 | .decode = pcx_decode_frame, | |
242 | .capabilities = CODEC_CAP_DR1, | |
243 | }; |