Coverage Report

Created: 2026-09-14 08:00

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ffmpeg/libavcodec/smc.c
Line
Count
Source
1
/*
2
 * Quicktime Graphics (SMC) Video Decoder
3
 * Copyright (C) 2003 The FFmpeg project
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
 * QT SMC Video Decoder by Mike Melanson (melanson@pcisys.net)
25
 * For more information about the SMC format, visit:
26
 *   http://www.pcisys.net/~melanson/codecs/
27
 *
28
 * The SMC decoder outputs PAL8 colorspace data.
29
 */
30
31
#include <string.h>
32
33
#include "avcodec.h"
34
#include "bytestream.h"
35
#include "codec_internal.h"
36
#include "decode.h"
37
38
2.33M
#define CPAIR 2
39
1.37M
#define CQUAD 4
40
1.53M
#define COCTET 8
41
42
608k
#define COLORS_PER_TABLE 256
43
44
typedef struct SmcContext {
45
46
    AVCodecContext *avctx;
47
    AVFrame *frame;
48
49
    /* SMC color tables */
50
    uint8_t color_pairs[COLORS_PER_TABLE * CPAIR];
51
    uint8_t color_quads[COLORS_PER_TABLE * CQUAD];
52
    uint8_t color_octets[COLORS_PER_TABLE * COCTET];
53
54
    uint32_t pal[256];
55
} SmcContext;
56
57
#define GET_BLOCK_COUNT() \
58
1.95M
  (opcode & 0x10) ? (1 + bytestream2_get_byte(gb)) : 1 + (opcode & 0x0F);
59
60
45.1M
#define ADVANCE_BLOCK() \
61
45.1M
{ \
62
45.1M
    pixel_ptr += 4; \
63
45.1M
    if (pixel_ptr >= width) \
64
45.1M
    { \
65
2.85M
        pixel_ptr = 0; \
66
2.85M
        row_ptr += stride * 4; \
67
2.85M
    } \
68
45.1M
    total_blocks--; \
69
45.1M
    if (total_blocks < !!n_blocks) \
70
45.1M
    { \
71
3.42k
        av_log(s->avctx, AV_LOG_ERROR, "block counter just went negative (this should not happen)\n"); \
72
3.42k
        return AVERROR_INVALIDDATA; \
73
3.42k
    } \
74
45.1M
}
75
76
static int smc_decode_stream(SmcContext *s, GetByteContext *gb)
77
170k
{
78
170k
    int width = s->avctx->width;
79
170k
    int height = s->avctx->height;
80
170k
    int stride = s->frame->linesize[0];
81
170k
    int i;
82
170k
    int chunk_size;
83
170k
    int buf_size = bytestream2_size(gb);
84
170k
    uint8_t opcode;
85
170k
    int n_blocks;
86
170k
    unsigned int color_flags;
87
170k
    unsigned int color_flags_a;
88
170k
    unsigned int color_flags_b;
89
170k
    unsigned int flag_mask;
90
91
170k
    uint8_t * const pixels = s->frame->data[0];
92
93
170k
    int image_size = height * s->frame->linesize[0];
94
170k
    int row_ptr = 0;
95
170k
    int pixel_ptr = 0;
96
170k
    int pixel_x, pixel_y;
97
170k
    int row_inc = stride - 4;
98
170k
    int block_ptr;
99
170k
    int prev_block_ptr;
100
170k
    int prev_block_ptr1, prev_block_ptr2;
101
170k
    int prev_block_flag;
102
170k
    int total_blocks;
103
170k
    int color_table_index;  /* indexes to color pair, quad, or octet tables */
104
170k
    int pixel;
105
106
170k
    int color_pair_index = 0;
107
170k
    int color_quad_index = 0;
108
170k
    int color_octet_index = 0;
109
110
    /* make the palette available */
111
170k
    memcpy(s->frame->data[1], s->pal, AVPALETTE_SIZE);
112
113
170k
    bytestream2_skip(gb, 1);
114
170k
    chunk_size = bytestream2_get_be24(gb);
115
170k
    if (chunk_size != buf_size)
116
170k
        av_log(s->avctx, AV_LOG_WARNING, "MOV chunk size != encoded chunk size (%d != %d); using MOV chunk size\n",
117
170k
            chunk_size, buf_size);
118
119
170k
    chunk_size = buf_size;
120
170k
    total_blocks = ((s->avctx->width + 3) / 4) * ((s->avctx->height + 3) / 4);
121
122
    /* traverse through the blocks */
123
2.77M
    while (total_blocks) {
124
        /* sanity checks */
125
        /* make sure the row pointer hasn't gone wild */
126
2.65M
        if (row_ptr >= image_size) {
127
0
            av_log(s->avctx, AV_LOG_ERROR, "just went out of bounds (row ptr = %d, height = %d)\n",
128
0
                row_ptr, image_size);
129
0
            return AVERROR_INVALIDDATA;
130
0
        }
131
2.65M
        if (bytestream2_get_bytes_left(gb) < 1) {
132
46.3k
            av_log(s->avctx, AV_LOG_ERROR, "input too small\n");
133
46.3k
            return AVERROR_INVALIDDATA;
134
46.3k
        }
135
136
2.61M
        opcode = bytestream2_get_byteu(gb);
137
2.61M
        switch (opcode & 0xF0) {
138
        /* skip n blocks */
139
822k
        case 0x00:
140
847k
        case 0x10:
141
847k
            n_blocks = GET_BLOCK_COUNT();
142
3.80M
            while (n_blocks--) {
143
2.95M
                ADVANCE_BLOCK();
144
2.95M
            }
145
847k
            break;
146
147
        /* repeat last block n times */
148
847k
        case 0x20:
149
540k
        case 0x30:
150
540k
            n_blocks = GET_BLOCK_COUNT();
151
152
            /* sanity check */
153
540k
            if ((row_ptr == 0) && (pixel_ptr == 0)) {
154
2.30k
                av_log(s->avctx, AV_LOG_ERROR, "encountered repeat block opcode (%02X) but no blocks rendered yet\n",
155
2.30k
                    opcode & 0xF0);
156
2.30k
                return AVERROR_INVALIDDATA;
157
2.30k
            }
158
159
            /* figure out where the previous block started */
160
537k
            if (pixel_ptr == 0)
161
18.2k
                prev_block_ptr1 =
162
18.2k
                    (row_ptr - s->avctx->width * 4) + s->avctx->width - 4;
163
519k
            else
164
519k
                prev_block_ptr1 = row_ptr + pixel_ptr - 4;
165
166
19.7M
            while (n_blocks--) {
167
19.2M
                block_ptr = row_ptr + pixel_ptr;
168
19.2M
                prev_block_ptr = prev_block_ptr1;
169
96.0M
                for (pixel_y = 0; pixel_y < 4; pixel_y++) {
170
384M
                    for (pixel_x = 0; pixel_x < 4; pixel_x++) {
171
307M
                        pixels[block_ptr++] = pixels[prev_block_ptr++];
172
307M
                    }
173
76.8M
                    block_ptr += row_inc;
174
76.8M
                    prev_block_ptr += row_inc;
175
76.8M
                }
176
19.2M
                ADVANCE_BLOCK();
177
19.2M
            }
178
537k
            break;
179
180
        /* repeat previous pair of blocks n times */
181
537k
        case 0x40:
182
500k
        case 0x50:
183
500k
            n_blocks = GET_BLOCK_COUNT();
184
500k
            n_blocks *= 2;
185
186
            /* sanity check */
187
500k
            if ((row_ptr == 0) && (pixel_ptr < 2 * 4)) {
188
5.17k
                av_log(s->avctx, AV_LOG_ERROR, "encountered repeat block opcode (%02X) but not enough blocks rendered yet\n",
189
5.17k
                    opcode & 0xF0);
190
5.17k
                return AVERROR_INVALIDDATA;
191
5.17k
            }
192
193
            /* figure out where the previous 2 blocks started */
194
495k
            if (pixel_ptr == 0)
195
67.8k
                prev_block_ptr1 = (row_ptr - s->avctx->width * 4) +
196
67.8k
                    s->avctx->width - 4 * 2;
197
428k
            else if (pixel_ptr == 4)
198
2.58k
                prev_block_ptr1 = (row_ptr - s->avctx->width * 4) + row_inc;
199
425k
            else
200
425k
                prev_block_ptr1 = row_ptr + pixel_ptr - 4 * 2;
201
202
495k
            if (pixel_ptr == 0)
203
67.8k
                prev_block_ptr2 = (row_ptr - s->avctx->width * 4) + row_inc;
204
428k
            else
205
428k
                prev_block_ptr2 = row_ptr + pixel_ptr - 4;
206
207
495k
            prev_block_flag = 0;
208
15.9M
            while (n_blocks--) {
209
15.4M
                block_ptr = row_ptr + pixel_ptr;
210
15.4M
                if (prev_block_flag)
211
7.73M
                    prev_block_ptr = prev_block_ptr2;
212
7.73M
                else
213
7.73M
                    prev_block_ptr = prev_block_ptr1;
214
15.4M
                prev_block_flag = !prev_block_flag;
215
216
77.3M
                for (pixel_y = 0; pixel_y < 4; pixel_y++) {
217
309M
                    for (pixel_x = 0; pixel_x < 4; pixel_x++) {
218
247M
                        pixels[block_ptr++] = pixels[prev_block_ptr++];
219
247M
                    }
220
61.8M
                    block_ptr += row_inc;
221
61.8M
                    prev_block_ptr += row_inc;
222
61.8M
                }
223
15.4M
                ADVANCE_BLOCK();
224
15.4M
            }
225
494k
            break;
226
227
        /* 1-color block encoding */
228
494k
        case 0x60:
229
64.5k
        case 0x70:
230
64.5k
            n_blocks = GET_BLOCK_COUNT();
231
64.5k
            pixel = bytestream2_get_byte(gb);
232
233
6.35M
            while (n_blocks--) {
234
6.29M
                block_ptr = row_ptr + pixel_ptr;
235
31.4M
                for (pixel_y = 0; pixel_y < 4; pixel_y++) {
236
125M
                    for (pixel_x = 0; pixel_x < 4; pixel_x++) {
237
100M
                        pixels[block_ptr++] = pixel;
238
100M
                    }
239
25.1M
                    block_ptr += row_inc;
240
25.1M
                }
241
6.29M
                ADVANCE_BLOCK();
242
6.29M
            }
243
64.3k
            break;
244
245
        /* 2-color block encoding */
246
387k
        case 0x80:
247
395k
        case 0x90:
248
395k
            n_blocks = (opcode & 0x0F) + 1;
249
250
            /* figure out which color pair to use to paint the 2-color block */
251
395k
            if ((opcode & 0xF0) == 0x80) {
252
                /* fetch the next 2 colors from bytestream and store in next
253
                 * available entry in the color pair table */
254
1.16M
                for (i = 0; i < CPAIR; i++) {
255
774k
                    pixel = bytestream2_get_byte(gb);
256
774k
                    color_table_index = CPAIR * color_pair_index + i;
257
774k
                    s->color_pairs[color_table_index] = pixel;
258
774k
                }
259
                /* this is the base index to use for this block */
260
387k
                color_table_index = CPAIR * color_pair_index;
261
387k
                color_pair_index++;
262
                /* wraparound */
263
387k
                if (color_pair_index == COLORS_PER_TABLE)
264
1.48k
                    color_pair_index = 0;
265
387k
            } else
266
8.53k
                color_table_index = CPAIR * bytestream2_get_byte(gb);
267
268
931k
            while (n_blocks--) {
269
536k
                color_flags = bytestream2_get_be16(gb);
270
536k
                flag_mask = 0x8000;
271
536k
                block_ptr = row_ptr + pixel_ptr;
272
2.68M
                for (pixel_y = 0; pixel_y < 4; pixel_y++) {
273
10.7M
                    for (pixel_x = 0; pixel_x < 4; pixel_x++) {
274
8.58M
                        if (color_flags & flag_mask)
275
1.99M
                            pixel = color_table_index + 1;
276
6.58M
                        else
277
6.58M
                            pixel = color_table_index;
278
8.58M
                        flag_mask >>= 1;
279
8.58M
                        pixels[block_ptr++] = s->color_pairs[pixel];
280
8.58M
                    }
281
2.14M
                    block_ptr += row_inc;
282
2.14M
                }
283
536k
                ADVANCE_BLOCK();
284
535k
            }
285
395k
            break;
286
287
        /* 4-color block encoding */
288
395k
        case 0xA0:
289
150k
        case 0xB0:
290
150k
            n_blocks = (opcode & 0x0F) + 1;
291
292
            /* figure out which color quad to use to paint the 4-color block */
293
150k
            if ((opcode & 0xF0) == 0xA0) {
294
                /* fetch the next 4 colors from bytestream and store in next
295
                 * available entry in the color quad table */
296
678k
                for (i = 0; i < CQUAD; i++) {
297
542k
                    pixel = bytestream2_get_byte(gb);
298
542k
                    color_table_index = CQUAD * color_quad_index + i;
299
542k
                    s->color_quads[color_table_index] = pixel;
300
542k
                }
301
                /* this is the base index to use for this block */
302
135k
                color_table_index = CQUAD * color_quad_index;
303
135k
                color_quad_index++;
304
                /* wraparound */
305
135k
                if (color_quad_index == COLORS_PER_TABLE)
306
503
                    color_quad_index = 0;
307
135k
            } else
308
14.8k
                color_table_index = CQUAD * bytestream2_get_byte(gb);
309
310
390k
            while (n_blocks--) {
311
240k
                color_flags = bytestream2_get_be32(gb);
312
                /* flag mask actually acts as a bit shift count here */
313
240k
                flag_mask = 30;
314
240k
                block_ptr = row_ptr + pixel_ptr;
315
1.20M
                for (pixel_y = 0; pixel_y < 4; pixel_y++) {
316
4.80M
                    for (pixel_x = 0; pixel_x < 4; pixel_x++) {
317
3.84M
                        pixel = color_table_index +
318
3.84M
                            ((color_flags >> flag_mask) & 0x03);
319
3.84M
                        flag_mask -= 2;
320
3.84M
                        pixels[block_ptr++] = s->color_quads[pixel];
321
3.84M
                    }
322
960k
                    block_ptr += row_inc;
323
960k
                }
324
240k
                ADVANCE_BLOCK();
325
240k
            }
326
150k
            break;
327
328
        /* 8-color block encoding */
329
150k
        case 0xC0:
330
88.7k
        case 0xD0:
331
88.7k
            n_blocks = (opcode & 0x0F) + 1;
332
333
            /* figure out which color octet to use to paint the 8-color block */
334
88.7k
            if ((opcode & 0xF0) == 0xC0) {
335
                /* fetch the next 8 colors from bytestream and store in next
336
                 * available entry in the color octet table */
337
767k
                for (i = 0; i < COCTET; i++) {
338
682k
                    pixel = bytestream2_get_byte(gb);
339
682k
                    color_table_index = COCTET * color_octet_index + i;
340
682k
                    s->color_octets[color_table_index] = pixel;
341
682k
                }
342
                /* this is the base index to use for this block */
343
85.2k
                color_table_index = COCTET * color_octet_index;
344
85.2k
                color_octet_index++;
345
                /* wraparound */
346
85.2k
                if (color_octet_index == COLORS_PER_TABLE)
347
295
                    color_octet_index = 0;
348
85.2k
            } else
349
3.52k
                color_table_index = COCTET * bytestream2_get_byte(gb);
350
351
213k
            while (n_blocks--) {
352
                /*
353
                  For this input of 6 hex bytes:
354
                    01 23 45 67 89 AB
355
                  Mangle it to this output:
356
                    flags_a = xx012456, flags_b = xx89A37B
357
                */
358
                /* build the color flags */
359
124k
                int val1 = bytestream2_get_be16(gb);
360
124k
                int val2 = bytestream2_get_be16(gb);
361
124k
                int val3 = bytestream2_get_be16(gb);
362
124k
                color_flags_a = ((val1 & 0xFFF0) << 8) | (val2 >> 4);
363
124k
                color_flags_b = ((val3 & 0xFFF0) << 8) |
364
124k
                    ((val1 & 0x0F) << 8) | ((val2 & 0x0F) << 4) | (val3 & 0x0F);
365
366
124k
                color_flags = color_flags_a;
367
                /* flag mask actually acts as a bit shift count here */
368
124k
                flag_mask = 21;
369
124k
                block_ptr = row_ptr + pixel_ptr;
370
624k
                for (pixel_y = 0; pixel_y < 4; pixel_y++) {
371
                    /* reload flags at third row (iteration pixel_y == 2) */
372
499k
                    if (pixel_y == 2) {
373
124k
                        color_flags = color_flags_b;
374
124k
                        flag_mask = 21;
375
124k
                    }
376
2.49M
                    for (pixel_x = 0; pixel_x < 4; pixel_x++) {
377
1.99M
                        pixel = color_table_index +
378
1.99M
                            ((color_flags >> flag_mask) & 0x07);
379
1.99M
                        flag_mask -= 3;
380
1.99M
                        pixels[block_ptr++] = s->color_octets[pixel];
381
1.99M
                    }
382
499k
                    block_ptr += row_inc;
383
499k
                }
384
124k
                ADVANCE_BLOCK();
385
124k
            }
386
88.3k
            break;
387
388
        /* 16-color block encoding (every pixel is a different color) */
389
88.3k
        case 0xE0:
390
22.9k
        case 0xF0:
391
22.9k
            n_blocks = (opcode & 0x0F) + 1;
392
393
355k
            while (n_blocks--) {
394
332k
                block_ptr = row_ptr + pixel_ptr;
395
1.66M
                for (pixel_y = 0; pixel_y < 4; pixel_y++) {
396
6.65M
                    for (pixel_x = 0; pixel_x < 4; pixel_x++) {
397
5.32M
                        pixels[block_ptr++] = bytestream2_get_byte(gb);
398
5.32M
                    }
399
1.33M
                    block_ptr += row_inc;
400
1.33M
                }
401
332k
                ADVANCE_BLOCK();
402
332k
            }
403
22.6k
            break;
404
2.61M
        }
405
2.61M
    }
406
407
113k
    return 0;
408
170k
}
409
410
static av_cold int smc_decode_init(AVCodecContext *avctx)
411
1.06k
{
412
1.06k
    SmcContext *s = avctx->priv_data;
413
414
1.06k
    s->avctx = avctx;
415
1.06k
    avctx->pix_fmt = AV_PIX_FMT_PAL8;
416
417
1.06k
    s->frame = av_frame_alloc();
418
1.06k
    if (!s->frame)
419
0
        return AVERROR(ENOMEM);
420
421
1.06k
    return 0;
422
1.06k
}
423
424
static int smc_decode_frame(AVCodecContext *avctx, AVFrame *rframe,
425
                            int *got_frame, AVPacket *avpkt)
426
635k
{
427
635k
    const uint8_t *buf = avpkt->data;
428
635k
    int buf_size = avpkt->size;
429
635k
    SmcContext *s = avctx->priv_data;
430
635k
    GetByteContext gb;
431
635k
    int ret;
432
635k
    int total_blocks = ((s->avctx->width + 3) / 4) * ((s->avctx->height + 3) / 4);
433
434
635k
    if (total_blocks / 1024 > avpkt->size)
435
393k
        return AVERROR_INVALIDDATA;
436
437
242k
    if ((ret = ff_reget_buffer(avctx, s->frame, 0)) < 0)
438
71.7k
        return ret;
439
440
170k
    ff_copy_palette(s->pal, avpkt, avctx);
441
442
170k
    bytestream2_init(&gb, buf, buf_size);
443
170k
    ret = smc_decode_stream(s, &gb);
444
170k
    if (ret < 0)
445
57.2k
        return ret;
446
447
113k
    *got_frame      = 1;
448
113k
    if ((ret = av_frame_ref(rframe, s->frame)) < 0)
449
0
        return ret;
450
451
    /* always report that the buffer was completely consumed */
452
113k
    return buf_size;
453
113k
}
454
455
static av_cold int smc_decode_end(AVCodecContext *avctx)
456
1.06k
{
457
1.06k
    SmcContext *s = avctx->priv_data;
458
459
1.06k
    av_frame_free(&s->frame);
460
461
1.06k
    return 0;
462
1.06k
}
463
464
const FFCodec ff_smc_decoder = {
465
    .p.name         = "smc",
466
    CODEC_LONG_NAME("QuickTime Graphics (SMC)"),
467
    .p.type         = AVMEDIA_TYPE_VIDEO,
468
    .p.id           = AV_CODEC_ID_SMC,
469
    .priv_data_size = sizeof(SmcContext),
470
    .init           = smc_decode_init,
471
    .close          = smc_decode_end,
472
    FF_CODEC_DECODE_CB(smc_decode_frame),
473
    .p.capabilities = AV_CODEC_CAP_DR1,
474
};