FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavcodec/pictordec.c
Date: 2026-09-24 14:13:09
Exec Total Coverage
Lines: 95 165 57.6%
Functions: 2 3 66.7%
Branches: 46 118 39.0%

Line Branch Exec Source
1 /*
2 * Pictor/PC Paint decoder
3 * Copyright (c) 2010 Peter Ross <pross@xvid.org>
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 * Pictor/PC Paint decoder
25 */
26
27 #include "libavutil/imgutils.h"
28 #include "libavutil/mem.h"
29 #include "avcodec.h"
30 #include "bytestream.h"
31 #include "cga_data.h"
32 #include "codec_internal.h"
33 #include "decode.h"
34
35 typedef struct PicContext {
36 int width, height;
37 int nb_planes;
38 GetByteContext g;
39 } PicContext;
40
41 ✗ static void picmemset_8bpp(PicContext *s, AVFrame *frame, int value, int run,
42 int *x, int *y)
43 {
44 ✗ while (run > 0) {
45 ✗ uint8_t *d = frame->data[0] + *y * frame->linesize[0];
46 ✗ if (*x + run >= s->width) {
47 ✗ int n = s->width - *x;
48 ✗ memset(d + *x, value, n);
49 ✗ run -= n;
50 ✗ *x = 0;
51 ✗ *y -= 1;
52 ✗ if (*y < 0)
53 ✗ break;
54 } else {
55 ✗ memset(d + *x, value, run);
56 ✗ *x += run;
57 ✗ break;
58 }
59 }
60 ✗ }
61
62 46974 static void picmemset(PicContext *s, AVFrame *frame, unsigned value, int run,
63 int *x, int *y, int *plane, int bits_per_plane)
64 {
65 uint8_t *d;
66 46974 int shift = *plane * bits_per_plane;
67 46974 unsigned mask = ((1U << bits_per_plane) - 1) << shift;
68 46974 int xl = *x;
69 46974 int yl = *y;
70 46974 int planel = *plane;
71 46974 int pixels_per_value = 8/bits_per_plane;
72 46974 value <<= shift;
73
74 46974 d = frame->data[0] + yl * frame->linesize[0];
75
2/2
✓ Branch 0 taken 64272 times.
✓ Branch 1 taken 46972 times.
111244 while (run > 0) {
76 int j;
77
2/2
✓ Branch 0 taken 514176 times.
✓ Branch 1 taken 64270 times.
578446 for (j = 8-bits_per_plane; j >= 0; j -= bits_per_plane) {
78 514176 d[xl] |= (value >> j) & mask;
79 514176 xl += 1;
80
2/2
✓ Branch 0 taken 1648 times.
✓ Branch 1 taken 514174 times.
515822 while (xl == s->width) {
81 1648 yl -= 1;
82 1648 xl = 0;
83
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 1640 times.
1648 if (yl < 0) {
84 8 yl = s->height - 1;
85 8 planel += 1;
86
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 6 times.
8 if (planel >= s->nb_planes)
87 2 goto end;
88 6 value <<= bits_per_plane;
89 6 mask <<= bits_per_plane;
90 }
91 1646 d = frame->data[0] + yl * frame->linesize[0];
92
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1646 times.
1646 if (s->nb_planes == 1 &&
93 ✗ run*pixels_per_value >= s->width &&
94 ✗ pixels_per_value < (s->width / pixels_per_value * pixels_per_value)
95 ) {
96 ✗ for (; xl < pixels_per_value; xl ++) {
97 ✗ j = (j < bits_per_plane ? 8 : j) - bits_per_plane;
98 ✗ d[xl] |= (value >> j) & mask;
99 }
100 ✗ av_memcpy_backptr(d+xl, pixels_per_value, s->width - xl);
101 ✗ run -= s->width / pixels_per_value;
102 ✗ xl = s->width / pixels_per_value * pixels_per_value;
103 }
104 }
105 }
106 64270 run--;
107 }
108 46972 end:
109 46974 *x = xl;
110 46974 *y = yl;
111 46974 *plane = planel;
112 46974 }
113
114 static const uint8_t cga_mode45_index[6][4] = {
115 [0] = { 0, 3, 5, 7 }, // mode4, palette#1, low intensity
116 [1] = { 0, 2, 4, 6 }, // mode4, palette#2, low intensity
117 [2] = { 0, 3, 4, 7 }, // mode5, low intensity
118 [3] = { 0, 11, 13, 15 }, // mode4, palette#1, high intensity
119 [4] = { 0, 10, 12, 14 }, // mode4, palette#2, high intensity
120 [5] = { 0, 11, 12, 15 }, // mode5, high intensity
121 };
122
123 2 static int decode_frame(AVCodecContext *avctx, AVFrame *frame,
124 int *got_frame, AVPacket *avpkt)
125 {
126 2 PicContext *s = avctx->priv_data;
127 uint32_t *palette;
128 int bits_per_plane, bpp, etype, esize, npal, pos_after_pal;
129 int i, x, y, plane, tmp, ret, val;
130
131 2 bytestream2_init(&s->g, avpkt->data, avpkt->size);
132
133
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if (bytestream2_get_bytes_left(&s->g) < 11)
134 ✗ return AVERROR_INVALIDDATA;
135
136
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if (bytestream2_get_le16u(&s->g) != 0x1234)
137 ✗ return AVERROR_INVALIDDATA;
138
139 2 s->width = bytestream2_get_le16u(&s->g);
140 2 s->height = bytestream2_get_le16u(&s->g);
141 2 bytestream2_skip(&s->g, 4);
142 2 tmp = bytestream2_get_byteu(&s->g);
143 2 bits_per_plane = tmp & 0xF;
144 2 s->nb_planes = (tmp >> 4) + 1;
145 2 bpp = bits_per_plane * s->nb_planes;
146
3/6
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 2 times.
2 if (bits_per_plane > 8 || bpp < 1 || bpp > 32) {
147 ✗ avpriv_request_sample(avctx, "Unsupported bit depth");
148 ✗ return AVERROR_PATCHWELCOME;
149 }
150
151
1/8
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
2 if (bytestream2_peek_byte(&s->g) == 0xFF || bpp == 1 || bpp == 4 || bpp == 8) {
152 2 bytestream2_skip(&s->g, 2);
153 2 etype = bytestream2_get_le16(&s->g);
154 2 esize = bytestream2_get_le16(&s->g);
155
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if (bytestream2_get_bytes_left(&s->g) < esize)
156 ✗ return AVERROR_INVALIDDATA;
157 } else {
158 ✗ etype = -1;
159 ✗ esize = 0;
160 }
161
162 2 avctx->pix_fmt = AV_PIX_FMT_PAL8;
163
164
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if (av_image_check_size(s->width, s->height, 0, avctx) < 0)
165 ✗ return -1;
166
167 /*
168 There are 2 coding modes, RLE and RAW.
169 Undamaged RAW should be proportional to W*H and thus bigger than RLE
170 RLE codes the most compressed runs by
171 1 byte for val (=marker)
172 1 byte run (=0)
173 2 bytes run
174 1 byte val
175 that's 5 bytes and the maximum run we can code is 65535
176
177 The RLE decoder can exit prematurly but it does not on any image available
178 Based on this the formula is assumed correct for undamaged images.
179 If an image is found which exploits the special end
180 handling and breaks this formula then this needs to be adapted.
181 */
182
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if (bytestream2_get_bytes_left(&s->g) < s->width * s->height / 65535 * 5)
183 ✗ return AVERROR_INVALIDDATA;
184
185
3/4
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
2 if (s->width != avctx->width || s->height != avctx->height) {
186 1 ret = ff_set_dimensions(avctx, s->width, s->height);
187
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (ret < 0)
188 ✗ return ret;
189 }
190
191
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if ((ret = ff_get_buffer(avctx, frame, 0)) < 0)
192 ✗ return ret;
193 2 memset(frame->data[0], 0, s->height * frame->linesize[0]);
194 2 frame->pict_type = AV_PICTURE_TYPE_I;
195
196 2 pos_after_pal = bytestream2_tell(&s->g) + esize;
197 2 palette = (uint32_t*)frame->data[1];
198
1/6
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
2 if (etype == 1 && esize > 1 && bytestream2_peek_byte(&s->g) < 6) {
199 ✗ int idx = bytestream2_get_byte(&s->g);
200 ✗ npal = 4;
201 ✗ for (i = 0; i < npal; i++)
202 ✗ palette[i] = ff_cga_palette[ cga_mode45_index[idx][i] ];
203
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 } else if (etype == 2) {
204 ✗ npal = FFMIN(esize, 16);
205 ✗ for (i = 0; i < npal; i++) {
206 ✗ int pal_idx = bytestream2_get_byte(&s->g);
207 ✗ palette[i] = ff_cga_palette[FFMIN(pal_idx, 15)];
208 }
209
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 } else if (etype == 3) {
210 ✗ npal = FFMIN(esize, 16);
211 ✗ for (i = 0; i < npal; i++) {
212 ✗ int pal_idx = bytestream2_get_byte(&s->g);
213 ✗ palette[i] = ff_ega_palette[FFMIN(pal_idx, 63)];
214 }
215
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
2 } else if (etype == 4 || etype == 5) {
216
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 npal = FFMIN(esize / 3, 256);
217
2/2
✓ Branch 0 taken 32 times.
✓ Branch 1 taken 2 times.
34 for (i = 0; i < npal; i++) {
218 32 palette[i] = bytestream2_get_be24(&s->g) << 2;
219 32 palette[i] |= 0xFFU << 24 | palette[i] >> 6 & 0x30303;
220 }
221 } else {
222 ✗ if (bpp == 1) {
223 ✗ npal = 2;
224 ✗ palette[0] = 0xFF000000;
225 ✗ palette[1] = 0xFFFFFFFF;
226 ✗ } else if (bpp == 2) {
227 ✗ npal = 4;
228 ✗ for (i = 0; i < npal; i++)
229 ✗ palette[i] = ff_cga_palette[ cga_mode45_index[0][i] ];
230 } else {
231 ✗ npal = 16;
232 ✗ memcpy(palette, ff_cga_palette, npal * 4);
233 }
234 }
235 // fill remaining palette entries
236 2 memset(palette + npal, 0, AVPALETTE_SIZE - npal * 4);
237 // skip remaining palette bytes
238 2 bytestream2_seek(&s->g, pos_after_pal, SEEK_SET);
239
240 2 val = 0;
241 2 y = s->height - 1;
242
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 if (bytestream2_get_le16(&s->g)) {
243 2 x = 0;
244 2 plane = 0;
245
2/2
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 2 times.
10 while (bytestream2_get_bytes_left(&s->g) >= 6) {
246 int stop_size, marker, t1, t2;
247
248 8 t1 = bytestream2_get_bytes_left(&s->g);
249 8 t2 = bytestream2_get_le16(&s->g);
250 8 stop_size = t1 - FFMIN(t1, t2);
251 // ignore uncompressed block size
252 8 bytestream2_skip(&s->g, 2);
253 8 marker = bytestream2_get_byte(&s->g);
254
255
4/4
✓ Branch 0 taken 46980 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 46974 times.
✓ Branch 3 taken 6 times.
93962 while (plane < s->nb_planes &&
256 46980 bytestream2_get_bytes_left(&s->g) > stop_size) {
257 46974 int run = 1;
258 46974 val = bytestream2_get_byte(&s->g);
259
2/2
✓ Branch 0 taken 2136 times.
✓ Branch 1 taken 44838 times.
46974 if (val == marker) {
260 2136 run = bytestream2_get_byte(&s->g);
261
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2136 times.
2136 if (run == 0)
262 ✗ run = bytestream2_get_le16(&s->g);
263 2136 val = bytestream2_get_byte(&s->g);
264 }
265
266
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 46974 times.
46974 if (bits_per_plane == 8) {
267 ✗ picmemset_8bpp(s, frame, val, run, &x, &y);
268 ✗ if (y < 0)
269 ✗ goto finish;
270 } else {
271 46974 picmemset(s, frame, val, run, &x, &y, &plane, bits_per_plane);
272 }
273 }
274 }
275
276
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (s->nb_planes - plane > 1)
277 ✗ return AVERROR_INVALIDDATA;
278
279
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
2 if (plane < s->nb_planes && x < avctx->width) {
280 ✗ int run = (y + 1) * avctx->width - x;
281 ✗ if (bits_per_plane == 8)
282 ✗ picmemset_8bpp(s, frame, val, run, &x, &y);
283 else
284 ✗ picmemset(s, frame, val, run / (8 / bits_per_plane), &x, &y, &plane, bits_per_plane);
285 }
286 } else {
287 ✗ while (y >= 0 && bytestream2_get_bytes_left(&s->g) > 0) {
288 ✗ memcpy(frame->data[0] + y * frame->linesize[0], s->g.buffer, FFMIN(avctx->width, bytestream2_get_bytes_left(&s->g)));
289 ✗ bytestream2_skip(&s->g, avctx->width);
290 ✗ y--;
291 }
292 }
293 ✗ finish:
294
295 2 *got_frame = 1;
296 2 return avpkt->size;
297 }
298
299 const FFCodec ff_pictor_decoder = {
300 .p.name = "pictor",
301 CODEC_LONG_NAME("Pictor/PC Paint"),
302 .p.type = AVMEDIA_TYPE_VIDEO,
303 .p.id = AV_CODEC_ID_PICTOR,
304 .p.capabilities = AV_CODEC_CAP_DR1,
305 .priv_data_size = sizeof(PicContext),
306 FF_CODEC_DECODE_CB(decode_frame),
307 };
308