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