1 #include <libavutil/pixfmt.h>
2
3 #include "common/common.h"
4 #include "sub/draw_bmp.h"
5 #include "sub/osd.h"
6 #include "tests.h"
7 #include "video/fmt-conversion.h"
8 #include "video/img_format.h"
9 #include "video/repack.h"
10 #include "video/sws_utils.h"
11 #include "video/zimg.h"
12
13 // Excuse the utter stupidity.
14 #define UNFUCK(v) ((v) > 0 ? (v) : pixfmt2imgfmt(-(v)))
15 static_assert(IMGFMT_START > 0, "");
16 #define IMGFMT_GBRP (-AV_PIX_FMT_GBRP)
17 #define IMGFMT_GBRAP (-AV_PIX_FMT_GBRAP)
18
19 struct entry {
20 int w, h;
21 int fmt_a;
22 const void *const a[4];
23 int fmt_b;
24 const void *const b[4];
25 int flags;
26 };
27
28 #define P8(...) (const uint8_t[]){__VA_ARGS__}
29 #define P16(...) (const uint16_t[]){__VA_ARGS__}
30 #define P32(...) (const uint32_t[]){__VA_ARGS__}
31 #define SW16(v) ((((v) & 0xFF) << 8) | ((v) >> 8))
32 #define SW32(v) ((SW16((v) & 0xFFFFu) << 16) | (SW16(((v) | 0u) >> 16)))
33
34 // Warning: only entries that match existing conversions are tested.
35 static const struct entry repack_tests[] = {
36 // Note: the '0' tests rely on 0 being written, although by definition the
37 // contents of this padding is undefined. The repacker always writes
38 // it this way, though.
39 {1, 1, IMGFMT_RGB0, {P8(1, 2, 3, 0)},
40 IMGFMT_GBRP, {P8(2), P8(3), P8(1)}},
41 {1, 1, IMGFMT_BGR0, {P8(1, 2, 3, 0)},
42 IMGFMT_GBRP, {P8(2), P8(1), P8(3)}},
43 {1, 1, IMGFMT_0RGB, {P8(0, 1, 2, 3)},
44 IMGFMT_GBRP, {P8(2), P8(3), P8(1)}},
45 {1, 1, IMGFMT_0BGR, {P8(0, 1, 2, 3)},
46 IMGFMT_GBRP, {P8(2), P8(1), P8(3)}},
47 {1, 1, IMGFMT_RGBA, {P8(1, 2, 3, 4)},
48 IMGFMT_GBRAP, {P8(2), P8(3), P8(1), P8(4)}},
49 {1, 1, IMGFMT_BGRA, {P8(1, 2, 3, 4)},
50 IMGFMT_GBRAP, {P8(2), P8(1), P8(3), P8(4)}},
51 {1, 1, IMGFMT_ARGB, {P8(4, 1, 2, 3)},
52 IMGFMT_GBRAP, {P8(2), P8(3), P8(1), P8(4)}},
53 {1, 1, IMGFMT_ABGR, {P8(4, 1, 2, 3)},
54 IMGFMT_GBRAP, {P8(2), P8(1), P8(3), P8(4)}},
55 {1, 1, IMGFMT_BGR24, {P8(1, 2, 3)},
56 IMGFMT_GBRP, {P8(2), P8(1), P8(3)}},
57 {1, 1, IMGFMT_RGB24, {P8(1, 2, 3)},
58 IMGFMT_GBRP, {P8(2), P8(3), P8(1)}},
59 {1, 1, IMGFMT_RGBA64, {P16(0x1a1b, 0x2a2b, 0x3a3b, 0x4a4b)},
60 -AV_PIX_FMT_GBRAP16, {P16(0x2a2b), P16(0x3a3b),
61 P16(0x1a1b), P16(0x4a4b)}},
62 {1, 1, -AV_PIX_FMT_BGRA64LE, {P16(0x1a1b, 0x2a2b, 0x3a3b, 0x4a4b)},
63 -AV_PIX_FMT_GBRAP16, {P16(0x2a2b), P16(0x1a1b),
64 P16(0x3a3b), P16(0x4a4b)}},
65 {1, 1, -AV_PIX_FMT_RGBA64BE, {P16(0x1b1a, 0x2b2a, 0x3b3a, 0x4b4a)},
66 -AV_PIX_FMT_GBRAP16, {P16(0x2a2b), P16(0x3a3b),
67 P16(0x1a1b), P16(0x4a4b)}},
68 {1, 1, -AV_PIX_FMT_BGRA64BE, {P16(0x1b1a, 0x2b2a, 0x3b3a, 0x4b4a)},
69 -AV_PIX_FMT_GBRAP16, {P16(0x2a2b), P16(0x1a1b),
70 P16(0x3a3b), P16(0x4a4b)}},
71 {1, 1, -AV_PIX_FMT_RGB48BE, {P16(0x1a1b, 0x2a2b, 0x3a3b)},
72 -AV_PIX_FMT_GBRP16, {P16(0x2b2a), P16(0x3b3a), P16(0x1b1a)}},
73 {1, 1, -AV_PIX_FMT_RGB48LE, {P16(0x1a1b, 0x2a2b, 0x3a3b)},
74 -AV_PIX_FMT_GBRP16, {P16(0x2a2b), P16(0x3a3b), P16(0x1a1b)}},
75 {1, 1, -AV_PIX_FMT_BGR48BE, {P16(0x1a1b, 0x2a2b, 0x3a3b)},
76 -AV_PIX_FMT_GBRP16, {P16(0x2b2a), P16(0x1b1a), P16(0x3b3a)}},
77 {1, 1, -AV_PIX_FMT_BGR48LE, {P16(0x1a1b, 0x2a2b, 0x3a3b)},
78 -AV_PIX_FMT_GBRP16, {P16(0x2a2b), P16(0x1a1b), P16(0x3a3b)}},
79 {1, 1, -AV_PIX_FMT_XYZ12LE, {P16(0x1a1b, 0x2a2b, 0x3a3b)},
80 -AV_PIX_FMT_GBRP16, {P16(0x2a2b), P16(0x3a3b), P16(0x1a1b)}},
81 {1, 1, -AV_PIX_FMT_XYZ12BE, {P16(0x1b1a, 0x2b2a, 0x3b3a)},
82 -AV_PIX_FMT_GBRP16, {P16(0x2a2b), P16(0x3a3b), P16(0x1a1b)}},
83 {3, 1, -AV_PIX_FMT_BGR8, {P8(7, (7 << 3), (3 << 6))},
84 IMGFMT_GBRP, {P8(0,0xFF,0), P8(0,0,0xFF), P8(0xFF,0,0)},
85 .flags = REPACK_CREATE_EXPAND_8BIT},
86 {3, 1, -AV_PIX_FMT_RGB8, {P8(3, (7 << 2), (7 << 5))},
87 IMGFMT_GBRP, {P8(0,0xFF,0), P8(0xFF,0,0), P8(0,0,0xFF)},
88 .flags = REPACK_CREATE_EXPAND_8BIT},
89 {3, 1, -AV_PIX_FMT_BGR4_BYTE, {P8(1, (3 << 1), (1 << 3))},
90 IMGFMT_GBRP, {P8(0,0xFF,0), P8(0,0,0xFF), P8(0xFF,0,0)},
91 .flags = REPACK_CREATE_EXPAND_8BIT},
92 {3, 1, -AV_PIX_FMT_RGB4_BYTE, {P8(1, (3 << 1), (1 << 3))},
93 IMGFMT_GBRP, {P8(0,0xFF,0), P8(0xFF,0,0), P8(0,0,0xFF)},
94 .flags = REPACK_CREATE_EXPAND_8BIT},
95 {3, 1, -AV_PIX_FMT_RGB565LE, {P16((31), (63 << 5), (31 << 11))},
96 IMGFMT_GBRP, {P8(0,0xFF,0), P8(0xFF,0,0), P8(0,0,0xFF)},
97 .flags = REPACK_CREATE_EXPAND_8BIT},
98 {3, 1, -AV_PIX_FMT_RGB565BE, {P16(SW16(31), SW16(63 << 5), SW16(31 << 11))},
99 IMGFMT_GBRP, {P8(0,0xFF,0), P8(0xFF,0,0), P8(0,0,0xFF)},
100 .flags = REPACK_CREATE_EXPAND_8BIT},
101 {3, 1, -AV_PIX_FMT_BGR565LE, {P16((31), (63 << 5), (31 << 11))},
102 IMGFMT_GBRP, {P8(0,0xFF,0), P8(0,0,0xFF), P8(0xFF,0,0)},
103 .flags = REPACK_CREATE_EXPAND_8BIT},
104 {3, 1, -AV_PIX_FMT_BGR565BE, {P16(SW16(31), SW16(63 << 5), SW16(31 << 11))},
105 IMGFMT_GBRP, {P8(0,0xFF,0), P8(0,0,0xFF), P8(0xFF,0,0)},
106 .flags = REPACK_CREATE_EXPAND_8BIT},
107 {3, 1, -AV_PIX_FMT_RGB555LE, {P16((31), (31 << 5), (31 << 10))},
108 IMGFMT_GBRP, {P8(0,0xFF,0), P8(0xFF,0,0), P8(0,0,0xFF)},
109 .flags = REPACK_CREATE_EXPAND_8BIT},
110 {3, 1, -AV_PIX_FMT_RGB555BE, {P16(SW16(31), SW16(31 << 5), SW16(31 << 10))},
111 IMGFMT_GBRP, {P8(0,0xFF,0), P8(0xFF,0,0), P8(0,0,0xFF)},
112 .flags = REPACK_CREATE_EXPAND_8BIT},
113 {3, 1, -AV_PIX_FMT_BGR555LE, {P16((31), (31 << 5), (31 << 10))},
114 IMGFMT_GBRP, {P8(0,0xFF,0), P8(0,0,0xFF), P8(0xFF,0,0)},
115 .flags = REPACK_CREATE_EXPAND_8BIT},
116 {3, 1, -AV_PIX_FMT_BGR555BE, {P16(SW16(31), SW16(31 << 5), SW16(31 << 10))},
117 IMGFMT_GBRP, {P8(0,0xFF,0), P8(0,0,0xFF), P8(0xFF,0,0)},
118 .flags = REPACK_CREATE_EXPAND_8BIT},
119 {3, 1, -AV_PIX_FMT_RGB444LE, {P16((15), (15 << 4), (15 << 8))},
120 IMGFMT_GBRP, {P8(0,0xFF,0), P8(0xFF,0,0), P8(0,0,0xFF)},
121 .flags = REPACK_CREATE_EXPAND_8BIT},
122 {3, 1, -AV_PIX_FMT_RGB444BE, {P16(SW16(15), SW16(15 << 4), SW16(15 << 8))},
123 IMGFMT_GBRP, {P8(0,0xFF,0), P8(0xFF,0,0), P8(0,0,0xFF)},
124 .flags = REPACK_CREATE_EXPAND_8BIT},
125 {3, 1, -AV_PIX_FMT_BGR444LE, {P16((15), (15 << 4), (15 << 8))},
126 IMGFMT_GBRP, {P8(0,0xFF,0), P8(0,0,0xFF), P8(0xFF,0,0)},
127 .flags = REPACK_CREATE_EXPAND_8BIT},
128 {3, 1, -AV_PIX_FMT_BGR444BE, {P16(SW16(15), SW16(15 << 4), SW16(15 << 8))},
129 IMGFMT_GBRP, {P8(0,0xFF,0), P8(0,0,0xFF), P8(0xFF,0,0)},
130 .flags = REPACK_CREATE_EXPAND_8BIT},
131 {1, 1, IMGFMT_RGB30, {P32((3 << 20) | (2 << 10) | 1)},
132 -AV_PIX_FMT_GBRP10, {P16(2), P16(1), P16(3)}},
133 {1, 1, -AV_PIX_FMT_X2RGB10BE, {P32(SW32((3 << 20) | (2 << 10) | 1))},
134 -AV_PIX_FMT_GBRP10, {P16(2), P16(1), P16(3)}},
135 {8, 1, -AV_PIX_FMT_MONOWHITE, {P8(0xAA)},
136 IMGFMT_Y1, {P8(0, 1, 0, 1, 0, 1, 0, 1)}},
137 {8, 1, -AV_PIX_FMT_MONOBLACK, {P8(0xAA)},
138 IMGFMT_Y1, {P8(1, 0, 1, 0, 1, 0, 1, 0)}},
139 {2, 2, IMGFMT_NV12, {P8(1, 2, 3, 4), P8(5, 6)},
140 IMGFMT_420P, {P8(1, 2, 3, 4), P8(5), P8(6)}},
141 {2, 2, -AV_PIX_FMT_NV21, {P8(1, 2, 3, 4), P8(5, 6)},
142 IMGFMT_420P, {P8(1, 2, 3, 4), P8(6), P8(5)}},
143 {1, 1, -AV_PIX_FMT_AYUV64LE, {P16(1, 2, 3, 4)},
144 -AV_PIX_FMT_YUVA444P16, {P16(2), P16(3), P16(4), P16(1)}},
145 {1, 1, -AV_PIX_FMT_AYUV64BE, {P16(0x0100, 0x0200, 0x0300, 0x0400)},
146 -AV_PIX_FMT_YUVA444P16, {P16(2), P16(3), P16(4), P16(1)}},
147 {4, 1, -AV_PIX_FMT_YUYV422, {P8(1, 2, 3, 4, 5, 6, 7, 8)},
148 -AV_PIX_FMT_YUV422P, {P8(1, 3, 5, 7), P8(2, 6), P8(4, 8)}},
149 {2, 1, -AV_PIX_FMT_YVYU422, {P8(1, 2, 3, 4)},
150 -AV_PIX_FMT_YUV422P, {P8(1, 3), P8(4), P8(2)}},
151 {2, 1, -AV_PIX_FMT_UYVY422, {P8(1, 2, 3, 4)},
152 -AV_PIX_FMT_YUV422P, {P8(2, 4), P8(1), P8(3)}},
153 {2, 1, -AV_PIX_FMT_Y210LE, {P16(0x1a1b, 0x2a2b, 0x3a3b, 0x4a4b)},
154 -AV_PIX_FMT_YUV422P16, {P16(0x1a1b, 0x3a3b), P16(0x2a2b), P16(0x4a4b)}},
155 {2, 1, -AV_PIX_FMT_Y210BE, {P16(0x1b1a, 0x2b2a, 0x3b3a, 0x4b4a)},
156 -AV_PIX_FMT_YUV422P16, {P16(0x1a1b, 0x3a3b), P16(0x2a2b), P16(0x4a4b)}},
157 {1, 1, -AV_PIX_FMT_YA8, {P8(1, 2)},
158 IMGFMT_YAP8, {P8(1), P8(2)}},
159 {1, 1, -AV_PIX_FMT_YA16, {P16(0x1a1b, 0x2a2b)},
160 IMGFMT_YAP16, {P16(0x1a1b), P16(0x2a2b)}},
161 {2, 1, -AV_PIX_FMT_YUV422P16BE, {P16(0x1a1b, 0x2a2b), P16(0x3a3b),
162 P16(0x4a4b)},
163 -AV_PIX_FMT_YUV422P16, {P16(0x1b1a, 0x2b2a), P16(0x3b3a),
164 P16(0x4b4a)}},
165 {8, 1, -AV_PIX_FMT_UYYVYY411, {P8(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12)},
166 -AV_PIX_FMT_YUV411P, {P8(2, 3, 5, 6, 8, 9, 11, 12),
167 P8(1, 7), P8(4, 10)}},
168 };
169
is_true_planar(int imgfmt)170 static bool is_true_planar(int imgfmt)
171 {
172 struct mp_regular_imgfmt desc;
173 if (!mp_get_regular_imgfmt(&desc, imgfmt))
174 return false;
175
176 for (int n = 0; n < desc.num_planes; n++) {
177 if (desc.planes[n].num_components != 1)
178 return false;
179 }
180
181 return true;
182 }
183
try_repack(struct test_ctx * ctx,FILE * f,int imgfmt,int flags,int not_if_fmt)184 static int try_repack(struct test_ctx *ctx, FILE *f, int imgfmt, int flags,
185 int not_if_fmt)
186 {
187 char *head = mp_tprintf(80, "%-15s =>", mp_imgfmt_to_name(imgfmt));
188 struct mp_repack *un = mp_repack_create_planar(imgfmt, false, flags);
189 struct mp_repack *pa = mp_repack_create_planar(imgfmt, true, flags);
190
191 // If both exists, they must be always symmetric.
192 if (un && pa) {
193 assert(mp_repack_get_format_src(pa) == mp_repack_get_format_dst(un));
194 assert(mp_repack_get_format_src(un) == mp_repack_get_format_dst(pa));
195 assert(mp_repack_get_align_x(pa) == mp_repack_get_align_x(un));
196 assert(mp_repack_get_align_y(pa) == mp_repack_get_align_y(un));
197 }
198
199 int a = 0;
200 int b = 0;
201 if (un) {
202 a = mp_repack_get_format_src(un);
203 b = mp_repack_get_format_dst(un);
204 } else if (pa) {
205 a = mp_repack_get_format_dst(pa);
206 b = mp_repack_get_format_src(pa);
207 }
208
209 // Skip the identity ones because they're uninteresting, and add too much
210 // noise. But still make sure they behave as expected.
211 if (a == imgfmt && b == imgfmt) {
212 assert(is_true_planar(imgfmt));
213 // (note that we require alpha-enabled zimg)
214 assert(mp_zimg_supports_in_format(imgfmt));
215 assert(un && pa);
216 talloc_free(pa);
217 talloc_free(un);
218 return b;
219 }
220
221 struct mp_repack *rp = pa ? pa : un;
222 if (!rp) {
223 if (!flags)
224 fprintf(f, "%s no\n", head);
225 return 0;
226 }
227
228 assert(a == imgfmt);
229 if (b && b == not_if_fmt) {
230 talloc_free(pa);
231 talloc_free(un);
232 return 0;
233 }
234
235 fprintf(f, "%s %4s %4s %-15s |", head, pa ? "[pa]" : "", un ? "[un]" : "",
236 mp_imgfmt_to_name(b));
237
238 fprintf(f, " a=%d:%d", mp_repack_get_align_x(rp), mp_repack_get_align_y(rp));
239
240 if (flags & REPACK_CREATE_PLANAR_F32)
241 fprintf(f, " [planar-f32]");
242 if (flags & REPACK_CREATE_ROUND_DOWN)
243 fprintf(f, " [round-down]");
244 if (flags & REPACK_CREATE_EXPAND_8BIT)
245 fprintf(f, " [expand-8bit]");
246
247 // LCM of alignment of all packers.
248 int ax = mp_repack_get_align_x(rp);
249 int ay = mp_repack_get_align_y(rp);
250 if (pa && un) {
251 ax = MPMAX(mp_repack_get_align_x(pa), mp_repack_get_align_x(un));
252 ay = MPMAX(mp_repack_get_align_y(pa), mp_repack_get_align_y(un));
253 }
254
255 for (int n = 0; n < MP_ARRAY_SIZE(repack_tests); n++) {
256 const struct entry *e = &repack_tests[n];
257 int fmt_a = UNFUCK(e->fmt_a);
258 int fmt_b = UNFUCK(e->fmt_b);
259 if (!(fmt_a == a && fmt_b == b && e->flags == flags))
260 continue;
261
262 // We convert a "random" macro pixel to catch potential addressing bugs
263 // that might be ignored with (0, 0) origins.
264 struct mp_image *ia = mp_image_alloc(fmt_a, e->w * 5 * ax, e->h * 5 * ay);
265 struct mp_image *ib = mp_image_alloc(fmt_b, e->w * 7 * ax, e->h * 6 * ay);
266 int sx = 4 * ax, sy = 3 * ay, dx = 3 * ax, dy = 2 * ay;
267
268 assert(ia && ib);
269
270 mp_image_params_guess_csp(&ia->params);
271 mp_image_params_guess_csp(&ib->params);
272
273 for (int pack = 0; pack < 2; pack++) {
274 struct mp_repack *repacker = pack ? pa : un;
275 if (!repacker)
276 continue;
277
278 mp_image_clear(ia, 0, 0, ia->w, ia->h);
279 mp_image_clear(ib, 0, 0, ib->w, ib->h);
280
281 const void *const *dstd = pack ? e->a : e->b;
282 const void *const *srcd = pack ? e->b : e->a;
283 struct mp_image *dsti = pack ? ia : ib;
284 struct mp_image *srci = pack ? ib : ia;
285
286 bool r = repack_config_buffers(repacker, 0, dsti, 0, srci, NULL);
287 assert(r);
288
289 for (int p = 0; p < srci->num_planes; p++) {
290 uint8_t *ptr = mp_image_pixel_ptr(srci, p, sx, sy);
291 for (int y = 0; y < e->h >> srci->fmt.ys[p]; y++) {
292 int wb = mp_image_plane_bytes(srci, p, 0, e->w);
293 const void *cptr = (uint8_t *)srcd[p] + wb * y;
294 memcpy(ptr + srci->stride[p] * y, cptr, wb);
295 }
296 }
297
298 repack_line(repacker, dx, dy, sx, sy, e->w);
299
300 for (int p = 0; p < dsti->num_planes; p++) {
301 uint8_t *ptr = mp_image_pixel_ptr(dsti, p, dx, dy);
302 for (int y = 0; y < e->h >> dsti->fmt.ys[p]; y++) {
303 int wb = mp_image_plane_bytes(dsti, p, 0, e->w);
304 const void *cptr = (uint8_t *)dstd[p] + wb * y;
305 assert_memcmp(ptr + dsti->stride[p] * y, cptr, wb);
306 }
307 }
308
309 fprintf(f, " [t%s]", pack ? "p" : "u");
310 }
311
312 talloc_free(ia);
313 talloc_free(ib);
314 }
315
316 fprintf(f, "\n");
317
318 talloc_free(pa);
319 talloc_free(un);
320 return b;
321 }
322
check_float_repack(int imgfmt,enum mp_csp csp,enum mp_csp_levels levels)323 static void check_float_repack(int imgfmt, enum mp_csp csp,
324 enum mp_csp_levels levels)
325 {
326 imgfmt = UNFUCK(imgfmt);
327
328 struct mp_regular_imgfmt desc = {0};
329 mp_get_regular_imgfmt(&desc, imgfmt);
330 int bpp = desc.component_size;
331 int comp_bits = desc.component_size * 8 + MPMIN(desc.component_pad, 0);
332
333 assert(bpp == 1 || bpp == 2);
334
335 int w = 1 << (bpp * 8);
336 struct mp_image *src = mp_image_alloc(imgfmt, w, 1);
337 assert(src);
338
339 src->params.color.space = csp;
340 src->params.color.levels = levels;
341 mp_image_params_guess_csp(&src->params);
342 // mpv may not allow all combinations
343 assert(src->params.color.space == csp);
344 assert(src->params.color.levels == levels);
345
346 for (int p = 0; p < src->num_planes; p++) {
347 int val = 0;
348 for (int x = 0; x < w >> src->fmt.xs[p]; x++) {
349 val = MPMIN(val, (1 << comp_bits) - 1);
350 void *pixel = mp_image_pixel_ptr(src, p, x, 0);
351 if (bpp == 1) {
352 *(uint8_t *)pixel = val;
353 } else {
354 *(uint16_t *)pixel = val;
355 }
356 val++;
357 }
358 }
359
360 struct mp_repack *to_f =
361 mp_repack_create_planar(src->imgfmt, false, REPACK_CREATE_PLANAR_F32);
362 struct mp_repack *from_f =
363 mp_repack_create_planar(src->imgfmt, true, REPACK_CREATE_PLANAR_F32);
364 assert(to_f && from_f);
365
366 struct mp_image *z_f = mp_image_alloc(mp_repack_get_format_dst(to_f), w, 1);
367 struct mp_image *r_f = mp_image_alloc(z_f->imgfmt, w, 1);
368 struct mp_image *z_i = mp_image_alloc(src->imgfmt, w, 1);
369 struct mp_image *r_i = mp_image_alloc(src->imgfmt, w, 1);
370 assert(z_f && r_f && z_i && r_i);
371
372 z_f->params.color = r_f->params.color = z_i->params.color =
373 r_i->params.color = src->params.color;
374
375 // The idea is to use zimg to cross-check conversion.
376 struct mp_sws_context *s = mp_sws_alloc(NULL);
377 s->force_scaler = MP_SWS_ZIMG;
378 struct zimg_opts opts = zimg_opts_defaults;
379 opts.dither = ZIMG_DITHER_NONE;
380 s->zimg_opts = &opts;
381 mp_sws_scale(s, z_f, src);
382 mp_sws_scale(s, z_i, z_f);
383 talloc_free(s);
384
385 repack_config_buffers(to_f, 0, r_f, 0, src, NULL);
386 repack_line(to_f, 0, 0, 0, 0, w);
387 repack_config_buffers(from_f, 0, r_i, 0, r_f, NULL);
388 repack_line(from_f, 0, 0, 0, 0, w);
389
390 for (int p = 0; p < src->num_planes; p++) {
391 for (int x = 0; x < w >> src->fmt.xs[p]; x++) {
392 uint32_t src_val, z_i_val, r_i_val;
393 if (bpp == 1) {
394 src_val = *(uint8_t *)mp_image_pixel_ptr(src, p, x, 0);
395 z_i_val = *(uint8_t *)mp_image_pixel_ptr(z_i, p, x, 0);
396 r_i_val = *(uint8_t *)mp_image_pixel_ptr(r_i, p, x, 0);
397 } else {
398 src_val = *(uint16_t *)mp_image_pixel_ptr(src, p, x, 0);
399 z_i_val = *(uint16_t *)mp_image_pixel_ptr(z_i, p, x, 0);
400 r_i_val = *(uint16_t *)mp_image_pixel_ptr(r_i, p, x, 0);
401 }
402 float z_f_val = *(float *)mp_image_pixel_ptr(z_f, p, x, 0);
403 float r_f_val = *(float *)mp_image_pixel_ptr(r_f, p, x, 0);
404
405 assert_int_equal(src_val, z_i_val);
406 assert_int_equal(src_val, r_i_val);
407 double tolerance = 1.0 / (1 << (bpp * 8)) / 4;
408 assert_float_equal(r_f_val, z_f_val, tolerance);
409 }
410 }
411
412 talloc_free(src);
413 talloc_free(z_i);
414 talloc_free(z_f);
415 talloc_free(r_i);
416 talloc_free(r_f);
417 talloc_free(to_f);
418 talloc_free(from_f);
419 }
420
try_draw_bmp(struct mpv_global * g,FILE * f,int imgfmt)421 static bool try_draw_bmp(struct mpv_global *g, FILE *f, int imgfmt)
422 {
423 bool ok = false;
424
425 struct mp_image *dst = mp_image_alloc(imgfmt, 64, 64);
426 if (!dst)
427 goto done;
428
429 struct sub_bitmap sb = {
430 .bitmap = &(uint8_t[]){123},
431 .stride = 1,
432 .x = 1,
433 .y = 1,
434 .w = 1, .dw = 1,
435 .h = 1, .dh = 1,
436
437 .libass = { .color = 0xDEDEDEDE },
438 };
439 struct sub_bitmaps sbs = {
440 .format = SUBBITMAP_LIBASS,
441 .parts = &sb,
442 .num_parts = 1,
443 .change_id = 1,
444 };
445 struct sub_bitmap_list sbs_list = {
446 .change_id = 1,
447 .w = dst->w,
448 .h = dst->h,
449 .items = (struct sub_bitmaps *[]){&sbs},
450 .num_items = 1,
451 };
452
453 struct mp_draw_sub_cache *c = mp_draw_sub_alloc(NULL, g);
454 if (mp_draw_sub_bitmaps(c, dst, &sbs_list)) {
455 char *info = mp_draw_sub_get_dbg_info(c);
456 fprintf(f, "%s\n", info);
457 talloc_free(info);
458 ok = true;
459 }
460
461 talloc_free(c);
462 talloc_free(dst);
463
464 done:
465 if (!ok)
466 fprintf(f, "no\n");
467 return ok;
468 }
469
run(struct test_ctx * ctx)470 static void run(struct test_ctx *ctx)
471 {
472 FILE *f = test_open_out(ctx, "repack.txt");
473
474 init_imgfmts_list();
475 for (int n = 0; n < num_imgfmts; n++) {
476 int imgfmt = imgfmts[n];
477
478 int other = try_repack(ctx, f, imgfmt, 0, 0);
479 try_repack(ctx, f, imgfmt, REPACK_CREATE_ROUND_DOWN, other);
480 try_repack(ctx, f, imgfmt, REPACK_CREATE_EXPAND_8BIT, other);
481 try_repack(ctx, f, imgfmt, REPACK_CREATE_PLANAR_F32, other);
482 }
483
484 fclose(f);
485
486 assert_text_files_equal(ctx, "repack.txt", "repack.txt",
487 "This can fail if FFmpeg/libswscale adds or removes pixfmts.");
488
489 check_float_repack(-AV_PIX_FMT_GBRAP, MP_CSP_RGB, MP_CSP_LEVELS_PC);
490 check_float_repack(-AV_PIX_FMT_GBRAP10, MP_CSP_RGB, MP_CSP_LEVELS_PC);
491 check_float_repack(-AV_PIX_FMT_GBRAP16, MP_CSP_RGB, MP_CSP_LEVELS_PC);
492 check_float_repack(-AV_PIX_FMT_YUVA444P, MP_CSP_BT_709, MP_CSP_LEVELS_PC);
493 check_float_repack(-AV_PIX_FMT_YUVA444P, MP_CSP_BT_709, MP_CSP_LEVELS_TV);
494 check_float_repack(-AV_PIX_FMT_YUVA444P10, MP_CSP_BT_709, MP_CSP_LEVELS_PC);
495 check_float_repack(-AV_PIX_FMT_YUVA444P10, MP_CSP_BT_709, MP_CSP_LEVELS_TV);
496 check_float_repack(-AV_PIX_FMT_YUVA444P16, MP_CSP_BT_709, MP_CSP_LEVELS_PC);
497 check_float_repack(-AV_PIX_FMT_YUVA444P16, MP_CSP_BT_709, MP_CSP_LEVELS_TV);
498
499 // Determine the list of possible draw_bmp input formats. Do this here
500 // because it mostly depends on repack and imgformat stuff.
501 f = test_open_out(ctx, "draw_bmp.txt");
502
503 for (int n = 0; n < num_imgfmts; n++) {
504 int imgfmt = imgfmts[n];
505
506 fprintf(f, "%-12s= ", mp_imgfmt_to_name(imgfmt));
507 try_draw_bmp(ctx->global, f, imgfmt);
508 }
509
510 fclose(f);
511
512 assert_text_files_equal(ctx, "draw_bmp.txt", "draw_bmp.txt",
513 "This can fail if FFmpeg/libswscale adds or removes pixfmts.");
514 }
515
516 const struct unittest test_repack = {
517 .name = "repack",
518 .run = run,
519 };
520