2 * PC Paintbrush PCX (.pcx) image decoder
3 * Copyright (c) 2007, 2008 Ivo van Poorten
5 * This decoder does not support CGA palettes. I am unable to find samples
6 * and Netpbm cannot generate them.
8 * This file is part of FFmpeg.
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.
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.
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
25 #include "libavutil/imgutils.h"
27 #include "bytestream.h"
31 static void pcx_rle_decode(GetByteContext
*gb
,
33 unsigned int bytes_per_scanline
,
37 unsigned char run
, value
;
40 while (i
< bytes_per_scanline
&& bytestream2_get_bytes_left(gb
)>0) {
42 value
= bytestream2_get_byte(gb
);
43 if (value
>= 0xc0 && bytestream2_get_bytes_left(gb
)>0) {
45 value
= bytestream2_get_byte(gb
);
47 while (i
< bytes_per_scanline
&& run
--)
51 bytestream2_get_buffer(gb
, dst
, bytes_per_scanline
);
55 static void pcx_palette(GetByteContext
*gb
, uint32_t *dst
, int pallen
)
59 pallen
= FFMIN(pallen
, bytestream2_get_bytes_left(gb
) / 3);
60 for (i
= 0; i
< pallen
; i
++)
61 *dst
++ = 0xFF000000 | bytestream2_get_be24u(gb
);
63 memset(dst
, 0, (256 - pallen
) * sizeof(*dst
));
66 static int pcx_decode_frame(AVCodecContext
*avctx
, void *data
, int *got_frame
,
70 AVFrame
* const p
= data
;
71 int compressed
, xmin
, ymin
, xmax
, ymax
;
73 unsigned int w
, h
, bits_per_pixel
, bytes_per_line
, nplanes
, stride
, y
, x
,
75 uint8_t *ptr
, *scanline
;
77 if (avpkt
->size
< 128)
78 return AVERROR_INVALIDDATA
;
80 bytestream2_init(&gb
, avpkt
->data
, avpkt
->size
);
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
;
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
);
96 if (xmax
< xmin
|| ymax
< ymin
) {
97 av_log(avctx
, AV_LOG_ERROR
, "invalid image dimensions\n");
98 return AVERROR_INVALIDDATA
;
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
;
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
;
115 switch ((nplanes
<< 8) + bits_per_pixel
) {
117 avctx
->pix_fmt
= AV_PIX_FMT_RGB24
;
126 avctx
->pix_fmt
= AV_PIX_FMT_PAL8
;
129 av_log(avctx
, AV_LOG_ERROR
, "invalid PCX file\n");
130 return AVERROR_INVALIDDATA
;
133 bytestream2_skipu(&gb
, 60);
135 if ((ret
= ff_set_dimensions(avctx
, w
, h
)) < 0)
138 if ((ret
= ff_get_buffer(avctx
, p
, 0)) < 0)
141 p
->pict_type
= AV_PICTURE_TYPE_I
;
144 stride
= p
->linesize
[0];
146 scanline
= av_malloc(bytes_per_scanline
+ FF_INPUT_BUFFER_PADDING_SIZE
);
148 return AVERROR(ENOMEM
);
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
);
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)];
162 } else if (nplanes
== 1 && bits_per_pixel
== 8) {
163 int palstart
= avpkt
->size
- 769;
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
;
172 for (y
= 0; y
< h
; y
++, ptr
+= stride
) {
173 pcx_rle_decode(&gb
, scanline
, bytes_per_scanline
, compressed
);
174 memcpy(ptr
, scanline
, w
);
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
);
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
;
187 } else if (nplanes
== 1) { /* all packed formats, max. 16 colors */
190 for (y
= 0; y
< h
; y
++) {
191 init_get_bits8(&s
, scanline
, bytes_per_scanline
);
193 pcx_rle_decode(&gb
, scanline
, bytes_per_scanline
, compressed
);
195 for (x
= 0; x
< w
; x
++)
196 ptr
[x
] = get_bits(&s
, bits_per_pixel
);
199 } else { /* planar, 4, 8 or 16 colors */
202 for (y
= 0; y
< h
; y
++) {
203 pcx_rle_decode(&gb
, scanline
, bytes_per_scanline
, compressed
);
205 for (x
= 0; x
< w
; x
++) {
206 int m
= 0x80 >> (x
& 7), v
= 0;
207 for (i
= nplanes
- 1; i
>= 0; i
--) {
209 v
+= !!(scanline
[i
* bytes_per_line
+ (x
>> 3)] & m
);
217 ret
= bytestream2_tell(&gb
);
218 if (nplanes
== 1 && bits_per_pixel
== 8) {
219 pcx_palette(&gb
, (uint32_t *)p
->data
[1], 256);
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);
236 AVCodec ff_pcx_decoder
= {
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
,