FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavcodec/brenderpix.c
Date: 2024-11-20 23:03:26
Exec Total Coverage
Lines: 72 108 66.7%
Functions: 2 2 100.0%
Branches: 32 56 57.1%

Line Branch Exec 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 #define HEADER1_CHUNK 0x03
32 #define HEADER2_CHUNK 0x3D
33 #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 12 static int pix_decode_header(PixHeader *out, GetByteContext *pgb)
115 {
116 12 unsigned int header_len = bytestream2_get_be32(pgb);
117
118 12 out->format = bytestream2_get_byte(pgb);
119 12 bytestream2_skip(pgb, 2);
120 12 out->width = bytestream2_get_be16(pgb);
121 12 out->height = bytestream2_get_be16(pgb);
122
123 // the header is at least 11 bytes long; we read the first 7
124
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
12 if (header_len < 11)
125 return AVERROR_INVALIDDATA;
126
127 // skip the rest of the header
128 12 bytestream2_skip(pgb, header_len - 7);
129
130 12 return 0;
131 }
132
133 10 static int pix_decode_frame(AVCodecContext *avctx, AVFrame *frame,
134 int *got_frame, AVPacket *avpkt)
135 {
136 int ret, i;
137 GetByteContext gb;
138
139 unsigned int bytes_pp;
140 unsigned int magic[4];
141 unsigned int chunk_type;
142 unsigned int data_len;
143 unsigned int bytes_per_scanline;
144 unsigned int bytes_left;
145 PixHeader hdr;
146
147 10 bytestream2_init(&gb, avpkt->data, avpkt->size);
148
149 10 magic[0] = bytestream2_get_be32(&gb);
150 10 magic[1] = bytestream2_get_be32(&gb);
151 10 magic[2] = bytestream2_get_be32(&gb);
152 10 magic[3] = bytestream2_get_be32(&gb);
153
154
1/2
✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
10 if (magic[0] != 0x12 ||
155
1/2
✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
10 magic[1] != 0x08 ||
156
1/2
✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
10 magic[2] != 0x02 ||
157
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
10 magic[3] != 0x02) {
158 av_log(avctx, AV_LOG_ERROR, "Not a BRender PIX file.\n");
159 return AVERROR_INVALIDDATA;
160 }
161
162 10 chunk_type = bytestream2_get_be32(&gb);
163
3/4
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 6 times.
10 if (chunk_type != HEADER1_CHUNK && chunk_type != HEADER2_CHUNK) {
164 av_log(avctx, AV_LOG_ERROR, "Invalid chunk type %d.\n", chunk_type);
165 return AVERROR_INVALIDDATA;
166 }
167
168 10 ret = pix_decode_header(&hdr, &gb);
169
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
10 if (ret < 0) {
170 av_log(avctx, AV_LOG_ERROR, "Invalid header length.\n");
171 return ret;
172 }
173
3/8
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 2 times.
✗ Branch 7 not taken.
10 switch (hdr.format) {
174 4 case 3:
175 4 avctx->pix_fmt = AV_PIX_FMT_PAL8;
176 4 bytes_pp = 1;
177 4 break;
178 case 4:
179 avctx->pix_fmt = AV_PIX_FMT_RGB555BE;
180 bytes_pp = 2;
181 break;
182 4 case 5:
183 4 avctx->pix_fmt = AV_PIX_FMT_RGB565BE;
184 4 bytes_pp = 2;
185 4 break;
186 case 6:
187 avctx->pix_fmt = AV_PIX_FMT_RGB24;
188 bytes_pp = 3;
189 break;
190 case 7:
191 avctx->pix_fmt = AV_PIX_FMT_0RGB;
192 bytes_pp = 4;
193 break;
194 case 8: // ARGB
195 avctx->pix_fmt = AV_PIX_FMT_ARGB;
196 bytes_pp = 4;
197 break;
198 2 case 18:
199 2 avctx->pix_fmt = AV_PIX_FMT_YA8;
200 2 bytes_pp = 2;
201 2 break;
202 default:
203 avpriv_request_sample(avctx, "Format %d", hdr.format);
204 return AVERROR_PATCHWELCOME;
205 }
206 10 bytes_per_scanline = bytes_pp * hdr.width;
207
208
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 10 times.
10 if (bytestream2_get_bytes_left(&gb) < hdr.height * bytes_per_scanline)
209 return AVERROR_INVALIDDATA;
210
211
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 10 times.
10 if ((ret = ff_set_dimensions(avctx, hdr.width, hdr.height)) < 0)
212 return ret;
213
214
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 10 times.
10 if ((ret = ff_get_buffer(avctx, frame, 0)) < 0)
215 return ret;
216
217 10 chunk_type = bytestream2_get_be32(&gb);
218
219
4/4
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 2 times.
10 if (avctx->pix_fmt == AV_PIX_FMT_PAL8 &&
220
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 (chunk_type == HEADER1_CHUNK ||
221 2 chunk_type == HEADER2_CHUNK)) {
222 /* read palette data from data[1] */
223 PixHeader palhdr;
224 2 uint32_t *pal_out = (uint32_t *)frame->data[1];
225
226 2 ret = pix_decode_header(&palhdr, &gb);
227
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (ret < 0) {
228 av_log(avctx, AV_LOG_ERROR, "Invalid palette header length.\n");
229 return ret;
230 }
231
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (palhdr.format != 7)
232 avpriv_request_sample(avctx, "Palette not in RGB format");
233
234 2 chunk_type = bytestream2_get_be32(&gb);
235 2 data_len = bytestream2_get_be32(&gb);
236 2 bytestream2_skip(&gb, 8);
237
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.
4 if (chunk_type != IMAGE_DATA_CHUNK || data_len != 1032 ||
238 2 bytestream2_get_bytes_left(&gb) < 1032) {
239 av_log(avctx, AV_LOG_ERROR, "Invalid palette data.\n");
240 return AVERROR_INVALIDDATA;
241 }
242 // palette data is surrounded by 8 null bytes (both top and bottom)
243 // convert 0RGB to machine endian format (ARGB32)
244
2/2
✓ Branch 0 taken 512 times.
✓ Branch 1 taken 2 times.
514 for (i = 0; i < 256; ++i)
245 512 *pal_out++ = (0xFFU << 24) | bytestream2_get_be32u(&gb);
246 2 bytestream2_skip(&gb, 8);
247
248 #if FF_API_PALETTE_HAS_CHANGED
249 FF_DISABLE_DEPRECATION_WARNINGS
250 2 frame->palette_has_changed = 1;
251 FF_ENABLE_DEPRECATION_WARNINGS
252 #endif
253
254 2 chunk_type = bytestream2_get_be32(&gb);
255
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 6 times.
8 } else if (avctx->pix_fmt == AV_PIX_FMT_PAL8) {
256 /* no palette supplied, use the default one */
257 2 uint32_t *pal_out = (uint32_t *)frame->data[1];
258
259 // TODO: add an AVOption to load custom palette files
260 2 av_log(avctx, AV_LOG_WARNING,
261 "Using default palette, colors might be off.\n");
262 2 memcpy(pal_out, std_pal_table, sizeof(uint32_t) * 256);
263
264 #if FF_API_PALETTE_HAS_CHANGED
265 FF_DISABLE_DEPRECATION_WARNINGS
266 2 frame->palette_has_changed = 1;
267 FF_ENABLE_DEPRECATION_WARNINGS
268 #endif
269 }
270
271 10 data_len = bytestream2_get_be32(&gb);
272 10 bytestream2_skip(&gb, 8);
273
274 // read the image data to the buffer
275 10 bytes_left = bytestream2_get_bytes_left(&gb);
276
277
2/4
✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 10 times.
✗ Branch 3 not taken.
10 if (chunk_type != IMAGE_DATA_CHUNK || data_len != bytes_left ||
278
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
10 bytes_left / bytes_per_scanline < hdr.height) {
279 av_log(avctx, AV_LOG_ERROR, "Invalid image data.\n");
280 return AVERROR_INVALIDDATA;
281 }
282
283 20 av_image_copy_plane(frame->data[0], frame->linesize[0],
284 10 avpkt->data + bytestream2_tell(&gb),
285 bytes_per_scanline,
286 bytes_per_scanline, hdr.height);
287
288 10 *got_frame = 1;
289
290 10 return avpkt->size;
291 }
292
293 const FFCodec ff_brender_pix_decoder = {
294 .p.name = "brender_pix",
295 CODEC_LONG_NAME("BRender PIX image"),
296 .p.type = AVMEDIA_TYPE_VIDEO,
297 .p.id = AV_CODEC_ID_BRENDER_PIX,
298 .p.capabilities = AV_CODEC_CAP_DR1,
299 FF_CODEC_DECODE_CB(pix_decode_frame),
300 };
301