Coverage Report

Created: 2026-01-16 07:48

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ffmpeg/libavcodec/cdxl.c
Line
Count
Source
1
/*
2
 * CDXL video decoder
3
 * Copyright (c) 2011-2012 Paul B Mahol
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
 * Commodore CDXL video decoder
25
 * @author Paul B Mahol
26
 */
27
28
#define UNCHECKED_BITSTREAM_READER 1
29
30
#include "libavutil/intreadwrite.h"
31
#include "libavutil/mem.h"
32
#include "avcodec.h"
33
#include "bytestream.h"
34
#include "codec_internal.h"
35
#include "decode.h"
36
#include "get_bits.h"
37
38
371k
#define BIT_PLANAR   0x00
39
258k
#define CHUNKY       0x20
40
#define BYTE_PLANAR  0x40
41
140k
#define BIT_LINE     0x80
42
#define BYTE_LINE    0xC0
43
44
typedef struct CDXLVideoContext {
45
    AVCodecContext *avctx;
46
    int            bpp;
47
    int            type;
48
    int            format;
49
    int            padded_bits;
50
    const uint8_t  *palette;
51
    int            palette_size;
52
    const uint8_t  *video;
53
    int            video_size;
54
    uint8_t        *new_video;
55
    int            new_video_size;
56
} CDXLVideoContext;
57
58
static av_cold int cdxl_decode_init(AVCodecContext *avctx)
59
1.64k
{
60
1.64k
    CDXLVideoContext *c = avctx->priv_data;
61
62
1.64k
    c->new_video_size = 0;
63
1.64k
    c->avctx          = avctx;
64
65
1.64k
    return 0;
66
1.64k
}
67
68
static void import_palette(CDXLVideoContext *c, uint32_t *new_palette)
69
121k
{
70
121k
    if (c->type == 1) {
71
88.8k
        for (int i = 0; i < c->palette_size / 2; i++) {
72
78.3k
            unsigned rgb = AV_RB16(&c->palette[i * 2]);
73
78.3k
            unsigned r   = ((rgb >> 8) & 0xF) * 0x11;
74
78.3k
            unsigned g   = ((rgb >> 4) & 0xF) * 0x11;
75
78.3k
            unsigned b   =  (rgb       & 0xF) * 0x11;
76
78.3k
            AV_WN32(&new_palette[i], (0xFFU << 24) | (r << 16) | (g << 8) | b);
77
78.3k
        }
78
111k
    } else {
79
140k
        for (int i = 0; i < c->palette_size / 3; i++) {
80
29.7k
            unsigned rgb = AV_RB24(&c->palette[i * 3]);
81
29.7k
            AV_WN32(&new_palette[i], (0xFFU << 24) | rgb);
82
29.7k
        }
83
111k
    }
84
121k
}
85
86
static void bitplanar2chunky(CDXLVideoContext *c, int linesize, uint8_t *out)
87
117k
{
88
117k
    GetBitContext gb;
89
117k
    int x, y, plane;
90
91
117k
    if (init_get_bits8(&gb, c->video, c->video_size) < 0)
92
0
        return;
93
248k
    for (plane = 0; plane < c->bpp; plane++) {
94
1.21M
        for (y = 0; y < c->avctx->height; y++) {
95
34.1M
            for (x = 0; x < c->avctx->width; x++)
96
33.1M
                out[linesize * y + x] |= get_bits1(&gb) << plane;
97
1.08M
            skip_bits(&gb, c->padded_bits);
98
1.08M
        }
99
131k
    }
100
117k
}
101
102
static void bitline2chunky(CDXLVideoContext *c, int linesize, uint8_t *out)
103
4.42k
{
104
4.42k
    GetBitContext  gb;
105
4.42k
    int x, y, plane;
106
107
4.42k
    if (init_get_bits8(&gb, c->video, c->video_size) < 0)
108
0
        return;
109
163k
    for (y = 0; y < c->avctx->height; y++) {
110
328k
        for (plane = 0; plane < c->bpp; plane++) {
111
4.10M
            for (x = 0; x < c->avctx->width; x++)
112
3.93M
                out[linesize * y + x] |= get_bits1(&gb) << plane;
113
168k
            skip_bits(&gb, c->padded_bits);
114
168k
        }
115
159k
    }
116
4.42k
}
117
118
static void chunky2chunky(CDXLVideoContext *c, int linesize, uint8_t *out)
119
1.54k
{
120
1.54k
    GetByteContext gb;
121
1.54k
    int y;
122
123
1.54k
    bytestream2_init(&gb, c->video, c->video_size);
124
8.55k
    for (y = 0; y < c->avctx->height; y++) {
125
7.00k
        bytestream2_get_buffer(&gb, out + linesize * y, c->avctx->width * 3);
126
7.00k
    }
127
1.54k
}
128
129
static void import_format(CDXLVideoContext *c, ptrdiff_t linesize, uint8_t *out)
130
123k
{
131
1.08M
    for (int y = 0; y < c->avctx->height; y++)
132
964k
        memset(out + y * linesize, 0, c->avctx->width);
133
134
123k
    switch (c->format) {
135
117k
    case BIT_PLANAR:
136
117k
        bitplanar2chunky(c, linesize, out);
137
117k
        break;
138
4.42k
    case BIT_LINE:
139
4.42k
        bitline2chunky(c, linesize, out);
140
4.42k
        break;
141
1.54k
    case CHUNKY:
142
1.54k
        chunky2chunky(c, linesize, out);
143
1.54k
        break;
144
123k
    }
145
123k
}
146
147
static void cdxl_decode_rgb(CDXLVideoContext *c, AVFrame *frame)
148
118k
{
149
118k
    uint32_t *new_palette = (uint32_t *)frame->data[1];
150
151
118k
    memset(frame->data[1], 0, AVPALETTE_SIZE);
152
118k
    import_palette(c, new_palette);
153
118k
    import_format(c, frame->linesize[0], frame->data[0]);
154
118k
}
155
156
static void cdxl_decode_raw(CDXLVideoContext *c, AVFrame *frame)
157
1.54k
{
158
1.54k
    import_format(c, frame->linesize[0], frame->data[0]);
159
1.54k
}
160
161
static void cdxl_decode_ham6(CDXLVideoContext *c, AVFrame *frame)
162
1.99k
{
163
1.99k
    AVCodecContext *avctx = c->avctx;
164
1.99k
    uint32_t new_palette[16], r, g, b;
165
1.99k
    uint8_t *ptr, *out, index, op;
166
1.99k
    int x, y;
167
168
1.99k
    ptr = c->new_video;
169
1.99k
    out = frame->data[0];
170
171
1.99k
    import_palette(c, new_palette);
172
1.99k
    import_format(c, avctx->width, c->new_video);
173
174
57.8k
    for (y = 0; y < avctx->height; y++) {
175
55.8k
        r = new_palette[0] & 0xFF0000;
176
55.8k
        g = new_palette[0] & 0xFF00;
177
55.8k
        b = new_palette[0] & 0xFF;
178
3.74M
        for (x = 0; x < avctx->width; x++) {
179
3.68M
            index  = *ptr++;
180
3.68M
            op     = index >> 4;
181
3.68M
            index &= 15;
182
3.68M
            switch (op) {
183
3.26M
            case 0:
184
3.26M
                r = new_palette[index] & 0xFF0000;
185
3.26M
                g = new_palette[index] & 0xFF00;
186
3.26M
                b = new_palette[index] & 0xFF;
187
3.26M
                break;
188
153k
            case 1:
189
153k
                b = index * 0x11;
190
153k
                break;
191
199k
            case 2:
192
199k
                r = index * 0x11 << 16;
193
199k
                break;
194
71.3k
            case 3:
195
71.3k
                g = index * 0x11 << 8;
196
71.3k
                break;
197
3.68M
            }
198
3.68M
            AV_WL24(out + x * 3, r | g | b);
199
3.68M
        }
200
55.8k
        out += frame->linesize[0];
201
55.8k
    }
202
1.99k
}
203
204
static void cdxl_decode_ham8(CDXLVideoContext *c, AVFrame *frame)
205
1.39k
{
206
1.39k
    AVCodecContext *avctx = c->avctx;
207
1.39k
    uint32_t new_palette[64], r, g, b;
208
1.39k
    uint8_t *ptr, *out, index, op;
209
1.39k
    int x, y;
210
211
1.39k
    ptr = c->new_video;
212
1.39k
    out = frame->data[0];
213
214
1.39k
    import_palette(c, new_palette);
215
1.39k
    import_format(c, avctx->width, c->new_video);
216
217
3.86k
    for (y = 0; y < avctx->height; y++) {
218
2.47k
        r = new_palette[0] & 0xFF0000;
219
2.47k
        g = new_palette[0] & 0xFF00;
220
2.47k
        b = new_palette[0] & 0xFF;
221
1.02M
        for (x = 0; x < avctx->width; x++) {
222
1.02M
            index  = *ptr++;
223
1.02M
            op     = index >> 6;
224
1.02M
            index &= 63;
225
1.02M
            switch (op) {
226
760k
            case 0:
227
760k
                r = new_palette[index] & 0xFF0000;
228
760k
                g = new_palette[index] & 0xFF00;
229
760k
                b = new_palette[index] & 0xFF;
230
760k
                break;
231
120k
            case 1:
232
120k
                b = (index <<  2) | (b & 3);
233
120k
                break;
234
112k
            case 2:
235
112k
                r = (index << 18) | (r & (3 << 16));
236
112k
                break;
237
33.6k
            case 3:
238
33.6k
                g = (index << 10) | (g & (3 << 8));
239
33.6k
                break;
240
1.02M
            }
241
1.02M
            AV_WL24(out + x * 3, r | g | b);
242
1.02M
        }
243
2.47k
        out += frame->linesize[0];
244
2.47k
    }
245
1.39k
}
246
247
static int cdxl_decode_frame(AVCodecContext *avctx, AVFrame *p,
248
                             int *got_frame, AVPacket *pkt)
249
250k
{
250
250k
    CDXLVideoContext *c = avctx->priv_data;
251
250k
    int ret, w, h, encoding, aligned_width, buf_size = pkt->size;
252
250k
    const uint8_t *buf = pkt->data;
253
254
250k
    if (buf_size < 32)
255
119k
        return AVERROR_INVALIDDATA;
256
131k
    c->type         = buf[0];
257
131k
    encoding        = buf[1] & 7;
258
131k
    c->format       = buf[1] & 0xE0;
259
131k
    w               = AV_RB16(&buf[14]);
260
131k
    h               = AV_RB16(&buf[16]);
261
131k
    c->bpp          = buf[19];
262
131k
    c->palette_size = AV_RB16(&buf[20]);
263
131k
    c->palette      = buf + 32;
264
131k
    c->video        = c->palette + c->palette_size;
265
131k
    c->video_size   = buf_size - c->palette_size - 32;
266
267
131k
    if (c->type > 1)
268
2.52k
        return AVERROR_INVALIDDATA;
269
128k
    if (c->type == 1 && c->palette_size > 512)
270
409
        return AVERROR_INVALIDDATA;
271
128k
    if (c->type == 0 && c->palette_size > 768)
272
474
        return AVERROR_INVALIDDATA;
273
128k
    if (buf_size < c->palette_size + 32)
274
396
        return AVERROR_INVALIDDATA;
275
127k
    if (c->bpp < 1)
276
394
        return AVERROR_INVALIDDATA;
277
127k
    if (c->format != BIT_PLANAR && c->format != BIT_LINE && c->format != CHUNKY) {
278
243
        avpriv_request_sample(avctx, "Pixel format 0x%0x", c->format);
279
243
        return AVERROR_PATCHWELCOME;
280
243
    }
281
282
127k
    if ((ret = ff_set_dimensions(avctx, w, h)) < 0)
283
1.31k
        return ret;
284
285
125k
    if (c->format == CHUNKY)
286
2.96k
        aligned_width = avctx->width;
287
122k
    else
288
122k
        aligned_width = FFALIGN(c->avctx->width, 16);
289
125k
    c->padded_bits  = aligned_width - c->avctx->width;
290
125k
    if (c->video_size < aligned_width * avctx->height * (int64_t)c->bpp / 8)
291
479
        return AVERROR_INVALIDDATA;
292
125k
    if (!encoding && c->palette_size && c->bpp <= 8 && c->format != CHUNKY) {
293
118k
        avctx->pix_fmt = AV_PIX_FMT_PAL8;
294
118k
    } else if (encoding == 1 && (c->bpp == 6 || c->bpp == 8) && c->format != CHUNKY) {
295
3.62k
        if (c->palette_size != (1 << (c->bpp - 1)))
296
240
            return AVERROR_INVALIDDATA;
297
3.38k
        avctx->pix_fmt = AV_PIX_FMT_BGR24;
298
3.38k
    } else if (!encoding && c->bpp == 24 && c->format == CHUNKY &&
299
1.75k
               !c->palette_size) {
300
1.54k
        avctx->pix_fmt = AV_PIX_FMT_RGB24;
301
1.78k
    } else {
302
1.78k
        avpriv_request_sample(avctx, "Encoding %d, bpp %d and format 0x%x",
303
1.78k
                              encoding, c->bpp, c->format);
304
1.78k
        return AVERROR_PATCHWELCOME;
305
1.78k
    }
306
307
123k
    if ((ret = ff_get_buffer(avctx, p, 0)) < 0)
308
0
        return ret;
309
310
123k
    if (encoding) {
311
3.38k
        av_fast_padded_malloc(&c->new_video, &c->new_video_size,
312
3.38k
                              h * w + AV_INPUT_BUFFER_PADDING_SIZE);
313
3.38k
        if (!c->new_video)
314
0
            return AVERROR(ENOMEM);
315
3.38k
        if (c->bpp == 8)
316
1.39k
            cdxl_decode_ham8(c, p);
317
1.99k
        else
318
1.99k
            cdxl_decode_ham6(c, p);
319
119k
    } else if (avctx->pix_fmt == AV_PIX_FMT_PAL8) {
320
118k
        cdxl_decode_rgb(c, p);
321
118k
    } else {
322
1.54k
        cdxl_decode_raw(c, p);
323
1.54k
    }
324
123k
    *got_frame = 1;
325
326
123k
    return buf_size;
327
123k
}
328
329
static av_cold int cdxl_decode_end(AVCodecContext *avctx)
330
1.64k
{
331
1.64k
    CDXLVideoContext *c = avctx->priv_data;
332
333
1.64k
    av_freep(&c->new_video);
334
335
1.64k
    return 0;
336
1.64k
}
337
338
const FFCodec ff_cdxl_decoder = {
339
    .p.name         = "cdxl",
340
    CODEC_LONG_NAME("Commodore CDXL video"),
341
    .p.type         = AVMEDIA_TYPE_VIDEO,
342
    .p.id           = AV_CODEC_ID_CDXL,
343
    .priv_data_size = sizeof(CDXLVideoContext),
344
    .init           = cdxl_decode_init,
345
    .close          = cdxl_decode_end,
346
    FF_CODEC_DECODE_CB(cdxl_decode_frame),
347
    .p.capabilities = AV_CODEC_CAP_DR1,
348
};