Coverage Report

Created: 2025-11-16 07:20

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ffmpeg/libavcodec/brenderpix.c
Line
Count
Source
1
/*
2
 * BRender PIX (.pix) image decoder
3
 * Copyright (c) 2012 Aleksi Nurmi
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
/* Tested against samples from I-War / Independence War and Defiance. */
23
24
#include "libavutil/imgutils.h"
25
26
#include "avcodec.h"
27
#include "bytestream.h"
28
#include "codec_internal.h"
29
#include "decode.h"
30
31
455k
#define HEADER1_CHUNK    0x03
32
107k
#define HEADER2_CHUNK    0x3D
33
230k
#define IMAGE_DATA_CHUNK 0x21
34
35
/* In 8-bit colour mode, 256 colours are available at any time. Which 256
36
 * colours are available is determined by the contents of the hardware palette
37
 * (or CLUT). In this case, the palette supplied with BRender (std.pal) has
38
 * been loaded into the CLUT.
39
 *
40
 * The 256 colours in std.pal are divided into seven ranges, or `colour ramps'.
41
 * The first 64 colours represent shades of grey ranging from very dark grey
42
 * (black) to very light grey (white). The following colours are 32-element
43
 * ramps for six colours as shown below.
44
 */
45
static const uint32_t std_pal_table[256] = {
46
    // gray
47
    0xFF000000, 0xFF030303, 0xFF060606, 0xFF090909, 0xFF0C0C0C, 0xFF0F0F0F,
48
    0xFF121212, 0xFF151515, 0xFF181818, 0xFF1B1B1B, 0xFF1E1E1E, 0xFF212121,
49
    0xFF242424, 0xFF272727, 0xFF2A2A2A, 0xFF2D2D2D, 0xFF313131, 0xFF343434,
50
    0xFF373737, 0xFF3A3A3A, 0xFF3D3D3D, 0xFF404040, 0xFF434343, 0xFF464646,
51
    0xFF494949, 0xFF4C4C4C, 0xFF4F4F4F, 0xFF525252, 0xFF555555, 0xFF585858,
52
    0xFF5B5B5B, 0xFF5E5E5E, 0xFF626262, 0xFF656565, 0xFF686868, 0xFF6B6B6B,
53
    0xFF6E6E6E, 0xFF717171, 0xFF747474, 0xFF777777, 0xFF7A7A7A, 0xFF7D7D7D,
54
    0xFF808080, 0xFF838383, 0xFF868686, 0xFF898989, 0xFF8C8C8C, 0xFF8F8F8F,
55
    0xFF939393, 0xFF999999, 0xFFA0A0A0, 0xFFA7A7A7, 0xFFAEAEAE, 0xFFB4B4B4,
56
    0xFFBBBBBB, 0xFFC2C2C2, 0xFFC9C9C9, 0xFFCFCFCF, 0xFFD6D6D6, 0xFFDDDDDD,
57
    0xFFE4E4E4, 0xFFEAEAEA, 0xFFF1F1F1, 0xFFF8F8F8,
58
59
    // blue
60
    0xFF000000, 0xFF020209, 0xFF050513, 0xFF07071D, 0xFF0A0A27, 0xFF0C0C31,
61
    0xFF0F0F3B, 0xFF111145, 0xFF14144F, 0xFF161659, 0xFF181863, 0xFF1B1B6D,
62
    0xFF1E1E77, 0xFF202080, 0xFF22228A, 0xFF252594, 0xFF28289E, 0xFF2A2AA8,
63
    0xFF2D2DB2, 0xFF2F2FBC, 0xFF3131C6, 0xFF3434D0, 0xFF3737DA, 0xFF3939E4,
64
    0xFF3C3CEE, 0xFF5454F0, 0xFF6C6CF2, 0xFF8585F4, 0xFF9D9DF6, 0xFFB5B5F8,
65
    0xFFCECEFA, 0xFFE6E6FC,
66
67
    // green
68
    0xFF000000, 0xFF020902, 0xFF051305, 0xFF071D07, 0xFF0A270A, 0xFF0C310C,
69
    0xFF0F3B0F, 0xFF114511, 0xFF144F14, 0xFF165916, 0xFF186318, 0xFF1B6D1B,
70
    0xFF1E771E, 0xFF208020, 0xFF228A22, 0xFF259425, 0xFF289E28, 0xFF2AA82A,
71
    0xFF2DB22D, 0xFF2FBC2F, 0xFF31C631, 0xFF34D034, 0xFF37DA37, 0xFF39E439,
72
    0xFF3CEE3C, 0xFF54F054, 0xFF6CF26C, 0xFF85F485, 0xFF9DF69D, 0xFFB5F8B5,
73
    0xFFCEFACE, 0xFFE6FCE6,
74
75
    // cyan
76
    0xFF000000, 0xFF020909, 0xFF051313, 0xFF071D1D, 0xFF0A2727, 0xFF0C3131,
77
    0xFF0F3B3B, 0xFF114545, 0xFF144F4F, 0xFF165959, 0xFF186363, 0xFF1B6D6D,
78
    0xFF1E7777, 0xFF208080, 0xFF228A8A, 0xFF259494, 0xFF289E9E, 0xFF2AA8A8,
79
    0xFF2DB2B2, 0xFF2FBCBC, 0xFF31C6C6, 0xFF34D0D0, 0xFF37DADA, 0xFF39E4E4,
80
    0xFF3CEEEE, 0xFF54F0F0, 0xFF6CF2F2, 0xFF85F4F4, 0xFF9DF6F6, 0xFFB5F8F8,
81
    0xFFCEFAFA, 0xFFE6FCFC,
82
83
    // red
84
    0xFF000000, 0xFF090202, 0xFF130505, 0xFF1D0707, 0xFF270A0A, 0xFF310C0C,
85
    0xFF3B0F0F, 0xFF451111, 0xFF4F1414, 0xFF591616, 0xFF631818, 0xFF6D1B1B,
86
    0xFF771E1E, 0xFF802020, 0xFF8A2222, 0xFF942525, 0xFF9E2828, 0xFFA82A2A,
87
    0xFFB22D2D, 0xFFBC2F2F, 0xFFC63131, 0xFFD03434, 0xFFDA3737, 0xFFE43939,
88
    0xFFEE3C3C, 0xFFF05454, 0xFFF26C6C, 0xFFF48585, 0xFFF69D9D, 0xFFF8B5B5,
89
    0xFFFACECE, 0xFFFCE6E6,
90
91
    // magenta
92
    0xFF000000, 0xFF090209, 0xFF130513, 0xFF1D071D, 0xFF270A27, 0xFF310C31,
93
    0xFF3B0F3B, 0xFF451145, 0xFF4F144F, 0xFF591659, 0xFF631863, 0xFF6D1B6D,
94
    0xFF771E77, 0xFF802080, 0xFF8A228A, 0xFF942594, 0xFF9E289E, 0xFFA82AA8,
95
    0xFFB22DB2, 0xFFBC2FBC, 0xFFC631C6, 0xFFD034D0, 0xFFDA37DA, 0xFFE439E4,
96
    0xFFEE3CEE, 0xFFF054F0, 0xFFF26CF2, 0xFFF485F4, 0xFFF69DF6, 0xFFF8B5F8,
97
    0xFFFACEFA, 0xFFFCE6FC,
98
99
    // yellow
100
    0xFF000000, 0xFF090902, 0xFF131305, 0xFF1D1D07, 0xFF27270A, 0xFF31310C,
101
    0xFF3B3B0F, 0xFF454511, 0xFF4F4F14, 0xFF595916, 0xFF636318, 0xFF6D6D1B,
102
    0xFF77771E, 0xFF808020, 0xFF8A8A22, 0xFF949425, 0xFF9E9E28, 0xFFA8A82A,
103
    0xFFB2B22D, 0xFFBCBC2F, 0xFFC6C631, 0xFFD0D034, 0xFFDADA37, 0xFFE4E439,
104
    0xFFEEEE3C, 0xFFF0F054, 0xFFF2F26C, 0xFFF4F485, 0xFFF6F69D, 0xFFF8F8B5,
105
    0xFFFAFACE, 0xFFFCFCE6,
106
};
107
108
typedef struct PixHeader {
109
    int width;
110
    int height;
111
    int format;
112
} PixHeader;
113
114
static int pix_decode_header(PixHeader *out, GetByteContext *pgb)
115
121k
{
116
121k
    unsigned int header_len = bytestream2_get_be32(pgb);
117
118
121k
    out->format = bytestream2_get_byte(pgb);
119
121k
    bytestream2_skip(pgb, 2);
120
121k
    out->width  = bytestream2_get_be16(pgb);
121
121k
    out->height = bytestream2_get_be16(pgb);
122
123
    // the header is at least 11 bytes long; we read the first 7
124
121k
    if (header_len < 11)
125
845
        return AVERROR_INVALIDDATA;
126
127
    // skip the rest of the header
128
120k
    bytestream2_skip(pgb, header_len - 7);
129
130
120k
    return 0;
131
121k
}
132
133
static int pix_decode_frame(AVCodecContext *avctx, AVFrame *frame,
134
                            int *got_frame, AVPacket *avpkt)
135
229k
{
136
229k
    int ret, i;
137
229k
    GetByteContext gb;
138
139
229k
    unsigned int bytes_pp;
140
229k
    unsigned int magic[4];
141
229k
    unsigned int chunk_type;
142
229k
    unsigned int data_len;
143
229k
    unsigned int bytes_per_scanline;
144
229k
    unsigned int bytes_left;
145
229k
    PixHeader hdr;
146
147
229k
    bytestream2_init(&gb, avpkt->data, avpkt->size);
148
149
229k
    magic[0] = bytestream2_get_be32(&gb);
150
229k
    magic[1] = bytestream2_get_be32(&gb);
151
229k
    magic[2] = bytestream2_get_be32(&gb);
152
229k
    magic[3] = bytestream2_get_be32(&gb);
153
154
229k
    if (magic[0] != 0x12 ||
155
118k
        magic[1] != 0x08 ||
156
117k
        magic[2] != 0x02 ||
157
117k
        magic[3] != 0x02) {
158
112k
        av_log(avctx, AV_LOG_ERROR, "Not a BRender PIX file.\n");
159
112k
        return AVERROR_INVALIDDATA;
160
112k
    }
161
162
116k
    chunk_type = bytestream2_get_be32(&gb);
163
116k
    if (chunk_type != HEADER1_CHUNK && chunk_type != HEADER2_CHUNK) {
164
493
        av_log(avctx, AV_LOG_ERROR, "Invalid chunk type %d.\n", chunk_type);
165
493
        return AVERROR_INVALIDDATA;
166
493
    }
167
168
116k
    ret = pix_decode_header(&hdr, &gb);
169
116k
    if (ret < 0) {
170
445
        av_log(avctx, AV_LOG_ERROR, "Invalid header length.\n");
171
445
        return ret;
172
445
    }
173
115k
    switch (hdr.format) {
174
112k
    case 3:
175
112k
        avctx->pix_fmt = AV_PIX_FMT_PAL8;
176
112k
        bytes_pp = 1;
177
112k
        break;
178
695
    case 4:
179
695
        avctx->pix_fmt = AV_PIX_FMT_RGB555BE;
180
695
        bytes_pp = 2;
181
695
        break;
182
1.17k
    case 5:
183
1.17k
        avctx->pix_fmt = AV_PIX_FMT_RGB565BE;
184
1.17k
        bytes_pp = 2;
185
1.17k
        break;
186
376
    case 6:
187
376
        avctx->pix_fmt = AV_PIX_FMT_RGB24;
188
376
        bytes_pp = 3;
189
376
        break;
190
13
    case 7:
191
13
        avctx->pix_fmt = AV_PIX_FMT_0RGB;
192
13
        bytes_pp = 4;
193
13
        break;
194
386
    case 8: // ARGB
195
386
        avctx->pix_fmt = AV_PIX_FMT_ARGB;
196
386
        bytes_pp = 4;
197
386
        break;
198
141
    case 18:
199
141
        avctx->pix_fmt = AV_PIX_FMT_YA8;
200
141
        bytes_pp = 2;
201
141
        break;
202
857
    default:
203
857
        avpriv_request_sample(avctx, "Format %d", hdr.format);
204
857
        return AVERROR_PATCHWELCOME;
205
115k
    }
206
114k
    bytes_per_scanline = bytes_pp * hdr.width;
207
208
114k
    if (bytestream2_get_bytes_left(&gb) < hdr.height * bytes_per_scanline)
209
525
        return AVERROR_INVALIDDATA;
210
211
114k
    if ((ret = ff_set_dimensions(avctx, hdr.width, hdr.height)) < 0)
212
1.38k
        return ret;
213
214
113k
    if ((ret = ff_get_buffer(avctx, frame, 0)) < 0)
215
0
        return ret;
216
217
113k
    chunk_type = bytestream2_get_be32(&gb);
218
219
113k
    if (avctx->pix_fmt == AV_PIX_FMT_PAL8 &&
220
110k
        (chunk_type == HEADER1_CHUNK ||
221
106k
         chunk_type == HEADER2_CHUNK)) {
222
        /* read palette data from data[1] */
223
5.29k
        PixHeader palhdr;
224
5.29k
        uint32_t *pal_out = (uint32_t *)frame->data[1];
225
226
5.29k
        ret = pix_decode_header(&palhdr, &gb);
227
5.29k
        if (ret < 0) {
228
400
            av_log(avctx, AV_LOG_ERROR, "Invalid palette header length.\n");
229
400
            return ret;
230
400
        }
231
4.89k
        if (palhdr.format != 7)
232
2.21k
            avpriv_request_sample(avctx, "Palette not in RGB format");
233
234
4.89k
        chunk_type = bytestream2_get_be32(&gb);
235
4.89k
        data_len = bytestream2_get_be32(&gb);
236
4.89k
        bytestream2_skip(&gb, 8);
237
4.89k
        if (chunk_type != IMAGE_DATA_CHUNK || data_len != 1032 ||
238
2.81k
            bytestream2_get_bytes_left(&gb) < 1032) {
239
2.39k
            av_log(avctx, AV_LOG_ERROR, "Invalid palette data.\n");
240
2.39k
            return AVERROR_INVALIDDATA;
241
2.39k
        }
242
        // palette data is surrounded by 8 null bytes (both top and bottom)
243
        // convert 0RGB to machine endian format (ARGB32)
244
641k
        for (i = 0; i < 256; ++i)
245
639k
            *pal_out++ = (0xFFU << 24) | bytestream2_get_be32u(&gb);
246
2.49k
        bytestream2_skip(&gb, 8);
247
248
2.49k
        chunk_type = bytestream2_get_be32(&gb);
249
107k
    } else if (avctx->pix_fmt == AV_PIX_FMT_PAL8) {
250
        /* no palette supplied, use the default one */
251
105k
        uint32_t *pal_out = (uint32_t *)frame->data[1];
252
253
        // TODO: add an AVOption to load custom palette files
254
105k
        av_log(avctx, AV_LOG_WARNING,
255
105k
               "Using default palette, colors might be off.\n");
256
105k
        memcpy(pal_out, std_pal_table, sizeof(uint32_t) * 256);
257
105k
    }
258
259
110k
    data_len = bytestream2_get_be32(&gb);
260
110k
    bytestream2_skip(&gb, 8);
261
262
    // read the image data to the buffer
263
110k
    bytes_left = bytestream2_get_bytes_left(&gb);
264
265
110k
    if (chunk_type != IMAGE_DATA_CHUNK || data_len != bytes_left ||
266
102k
        bytes_left / bytes_per_scanline < hdr.height) {
267
7.49k
        av_log(avctx, AV_LOG_ERROR, "Invalid image data.\n");
268
7.49k
        return AVERROR_INVALIDDATA;
269
7.49k
    }
270
271
102k
    av_image_copy_plane(frame->data[0], frame->linesize[0],
272
102k
                        avpkt->data + bytestream2_tell(&gb),
273
102k
                        bytes_per_scanline,
274
102k
                        bytes_per_scanline, hdr.height);
275
276
102k
    *got_frame = 1;
277
278
102k
    return avpkt->size;
279
110k
}
280
281
const FFCodec ff_brender_pix_decoder = {
282
    .p.name         = "brender_pix",
283
    CODEC_LONG_NAME("BRender PIX image"),
284
    .p.type         = AVMEDIA_TYPE_VIDEO,
285
    .p.id           = AV_CODEC_ID_BRENDER_PIX,
286
    .p.capabilities = AV_CODEC_CAP_DR1,
287
    FF_CODEC_DECODE_CB(pix_decode_frame),
288
};