FFmpeg
Loading...
Searching...
No Matches
apnggen.c
Go to the documentation of this file.
1/*
2 * Generate the sub-8-bit APNG streams used by the FATE tests.
3 *
4 * The frames are palette images. The first frame fills the canvas. Every
5 * later frame is a smaller rectangle whose x offset is odd, so it is not
6 * aligned to the byte that holds the packed samples. These files are built
7 * here, the same way audiogen builds its wave files, so the tests do not
8 * need media from the fate-suite.
9 *
10 * Copyright (c) 2026 Jun Zhao
11 *
12 * This file is part of FFmpeg.
13 *
14 * FFmpeg is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU Lesser General Public
16 * License as published by the Free Software Foundation; either
17 * version 2.1 of the License, or (at your option) any later version.
18 *
19 * FFmpeg is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 * Lesser General Public License for more details.
23 *
24 * You should have received a copy of the GNU Lesser General Public
25 * License along with FFmpeg; if not, write to the Free Software
26 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
27 */
28
29#include <errno.h>
30#include <stdint.h>
31#include <stdio.h>
32#include <stdlib.h>
33#include <string.h>
34
35enum { CANVAS_W = 24, CANVAS_H = 20, FRAMES = 6 };
36
37static const uint8_t palette[16][3] = {
38 { 0, 0, 0 }, { 255, 0, 0 }, { 0, 255, 0 }, { 0, 0, 255 },
39 { 255, 255, 0 }, { 255, 0, 255 }, { 0, 255, 255 }, { 255, 128, 0 },
40 { 128, 0, 255 }, { 128, 255, 0 }, { 0, 128, 255 }, { 255, 0, 128 },
41 { 64, 64, 64 }, { 192, 192, 192 }, { 128, 64, 0 }, { 255, 255, 255 },
42};
43
44typedef struct {
45 uint8_t data[2048];
46 int len;
47} Buf;
48
49static void die(const char *path)
50{
51 fprintf(stderr, "apnggen: %s: %s\n", path, strerror(errno));
52 exit(1);
53}
54
55static uint32_t crc32(const uint8_t *data, size_t size)
56{
57 static uint32_t table[256];
58 uint32_t crc = 0xFFFFFFFFu;
59 size_t i;
60
61 if (!table[1]) {
62 for (i = 0; i < 256; i++) {
63 uint32_t c = i;
64 int k;
65
66 for (k = 0; k < 8; k++)
67 c = (c & 1) ? 0xEDB88320u ^ (c >> 1) : c >> 1;
68 table[i] = c;
69 }
70 }
71 for (i = 0; i < size; i++)
72 crc = table[(crc ^ data[i]) & 0xFF] ^ (crc >> 8);
73 return crc ^ 0xFFFFFFFFu;
74}
75
76static void put(Buf *b, const void *src, int n)
77{
78 if (n < 0 || b->len > (int)sizeof(b->data) - n) {
79 fprintf(stderr, "apnggen: output too large\n");
80 exit(1);
81 }
82 memcpy(b->data + b->len, src, n);
83 b->len += n;
84}
85
86static void put_be32(Buf *b, uint32_t v)
87{
88 uint8_t p[4] = { v >> 24, v >> 16, v >> 8, v };
89
90 put(b, p, 4);
91}
92
93static void chunk(Buf *b, const char type[4], const uint8_t *data, int n)
94{
95 int start = b->len + 4;
96
97 put_be32(b, n);
98 put(b, type, 4);
99 if (n)
100 put(b, data, n);
101 put_be32(b, crc32(b->data + start, 4 + n));
102}
103
104/* First frame covers the canvas. Later frames are 7x5 rectangles on odd x
105 * offsets, one pixel further right each frame. */
106static int sample(int depth, int frame, int x, int y)
107{
108 int max = (1 << depth) - 1;
109
110 if (!frame)
111 return (x / 4 + y / 5) & max;
112 if (depth == 1)
113 return (x + y + frame) & 1;
114 return 1 + (x + y + frame) % max;
115}
116
117static int pack_frame(uint8_t *dst, int depth, int frame, int w, int h)
118{
119 int row_bytes = (w * depth + 7) / 8;
120 int out = 0, y, x;
121
122 for (y = 0; y < h; y++) {
123 dst[out++] = 0;
124 memset(dst + out, 0, row_bytes);
125 for (x = 0; x < w; x++) {
126 int bit = x * depth;
127
128 dst[out + bit / 8] |= sample(depth, frame, x, y) << (8 - depth - bit % 8);
129 }
130 out += row_bytes;
131 }
132 return out;
133}
134
135/* zlib wrapper around one stored deflate block. No compressor needed. */
136static void put_stored(Buf *b, const uint8_t *raw, int n)
137{
138 unsigned neg = ~n & 0xFFFF;
139 uint8_t hdr[7] = { 0x78, 0x01, 0x01, n, n >> 8, neg, neg >> 8 };
140 uint32_t a = 1, s = 0;
141 int i;
142
143 put(b, hdr, 7);
144 put(b, raw, n);
145 for (i = 0; i < n; i++) {
146 a += raw[i];
147 if (a >= 65521)
148 a -= 65521;
149 s += a;
150 if (s >= 65521)
151 s -= 65521;
152 }
153 put_be32(b, (s << 16) | a);
154}
155
156static void write_apng(const char *path, int depth)
157{
158 static const uint8_t sig[8] = { 137, 80, 78, 71, 13, 10, 26, 10 };
159 uint8_t ihdr[13] = { 0, 0, 0, CANVAS_W, 0, 0, 0, CANVAS_H, depth, 3 };
160 uint8_t actl[8] = { 0, 0, 0, FRAMES, 0, 0, 0, 1 };
161 uint8_t plte[16 * 3], raw[512], fctl[26];
162 Buf out = { 0 }, image = { 0 };
163 FILE *f;
164 int colors = 1 << depth, seq = 0, frame, i;
165
166 for (i = 0; i < colors; i++)
167 memcpy(plte + 3 * i, palette[i], 3);
168
169 put(&out, sig, 8);
170 chunk(&out, "IHDR", ihdr, sizeof(ihdr));
171 chunk(&out, "acTL", actl, sizeof(actl));
172 chunk(&out, "PLTE", plte, colors * 3);
173
174 for (frame = 0; frame < FRAMES; frame++) {
175 int sub = frame != 0;
176 int x = sub ? 1 + 2 * (frame - 1) : 0;
177 int y = sub ? 1 + (frame - 1) : 0;
178 int w = sub ? 7 : CANVAS_W;
179 int h = sub ? 5 : CANVAS_H;
180
181 memset(fctl, 0, sizeof(fctl));
182 fctl[3] = seq++;
183 fctl[7] = w;
184 fctl[11] = h;
185 fctl[15] = x;
186 fctl[19] = y;
187 fctl[21] = 1; /* delay numerator */
188 fctl[23] = 10; /* delay denominator: 1/10 s */
189 chunk(&out, "fcTL", fctl, sizeof(fctl));
190
191 image.len = 0;
192 if (sub)
193 put_be32(&image, seq++);
194 put_stored(&image, raw, pack_frame(raw, depth, frame, w, h));
195 chunk(&out, sub ? "fdAT" : "IDAT", image.data, image.len);
196 }
197 chunk(&out, "IEND", NULL, 0);
198
199 f = fopen(path, "wb");
200 if (!f || fwrite(out.data, 1, out.len, f) != (size_t)out.len || fclose(f))
201 die(path);
202}
203
204int main(int argc, char **argv)
205{
206 const char *path = argc == 2 ? argv[1] : NULL;
207 int depth = 0;
208
209 if (!path) {
210 fprintf(stderr, "usage: apnggen OUTPUT.png\n");
211 return 1;
212 }
213 if (strstr(path, "1bit"))
214 depth = 1;
215 else if (strstr(path, "2bit"))
216 depth = 2;
217 else if (strstr(path, "4bit"))
218 depth = 4;
219 if (!depth) {
220 fprintf(stderr, "apnggen: unsupported file name %s\n", path);
221 return 1;
222 }
223 write_apng(path, depth);
224 return 0;
225}
uint8_t ptrdiff_t const uint8_t ptrdiff_t int intptr_t intptr_t int int16_t * dst
Definition dsp.h:97
static FILE * out
static int pack_frame(uint8_t *dst, int depth, int frame, int w, int h)
Definition apnggen.c:117
static void put(Buf *b, const void *src, int n)
Definition apnggen.c:76
static void put_stored(Buf *b, const uint8_t *raw, int n)
Definition apnggen.c:136
static void chunk(Buf *b, const char type[4], const uint8_t *data, int n)
Definition apnggen.c:93
static void die(const char *path)
Definition apnggen.c:49
static const uint8_t palette[16][3]
Definition apnggen.c:37
static void put_be32(Buf *b, uint32_t v)
Definition apnggen.c:86
static uint32_t crc32(const uint8_t *data, size_t size)
Definition apnggen.c:55
static void write_apng(const char *path, int depth)
Definition apnggen.c:156
@ FRAMES
Definition apnggen.c:35
@ CANVAS_W
Definition apnggen.c:35
@ CANVAS_H
Definition apnggen.c:35
#define i(width, name, range_min, range_max)
Definition cbs_h264.c:63
#define bit(string, value)
Definition cbs_mpeg2.c:56
#define f(width, name)
Definition cbs_vp8.c:236
#define s(width, name)
Definition cbs_vp9.c:198
#define NULL
Definition coverity.c:32
#define max(a, b)
static AVFrame * frame
int main
Definition dovi_rpuenc.c:38
char * path
Definition ffplay.c:385
#define sample
int a
#define b
Definition input.c:43
uint32_t type
Definition jpegmpfenc.c:80
uint8_t w
Definition llvidencdsp.c:39
const char data[16]
Definition mxf.c:149
static const uint16_t table[]
Definition prosumer.c:203
static volatile sig_atomic_t sig
Definition signal.c:48
Definition apnggen.c:44
uint8_t data[2048]
Definition apnggen.c:45
int len
Definition apnggen.c:46
#define src
Definition vp8dsp.c:248
int size
int len
static double c[64]