1 #include <libavcodec/avcodec.h>
2
3 #include "scale_test.h"
4 #include "video/image_writer.h"
5 #include "video/sws_utils.h"
6
gen_repack_test_img(int w,int h,int bytes,bool rgb,bool alpha)7 static struct mp_image *gen_repack_test_img(int w, int h, int bytes, bool rgb,
8 bool alpha)
9 {
10 struct mp_regular_imgfmt planar_desc = {
11 .component_type = MP_COMPONENT_TYPE_UINT,
12 .component_size = bytes,
13 .forced_csp = rgb ? MP_CSP_RGB : 0,
14 .num_planes = alpha ? 4 : 3,
15 .planes = {
16 {1, {rgb ? 2 : 1}},
17 {1, {rgb ? 3 : 2}},
18 {1, {rgb ? 1 : 3}},
19 {1, {4}},
20 },
21 };
22 int mpfmt = mp_find_regular_imgfmt(&planar_desc);
23 assert(mpfmt);
24 struct mp_image *mpi = mp_image_alloc(mpfmt, w, h);
25 assert(mpi);
26
27 // Well, I have no idea what makes a good test image. So here's some crap.
28 // This contains bars/tiles of solid colors. For each of R/G/B, it toggles
29 // though 0/100% range, so 2*2*2 = 8 combinations (16 with alpha).
30 int b_h = 16, b_w = 16;
31
32 for (int y = 0; y < h; y++) {
33 for (int p = 0; p < mpi->num_planes; p++) {
34 void *line = mpi->planes[p] + mpi->stride[p] * (ptrdiff_t)y;
35
36 for (int x = 0; x < w; x += b_w) {
37 unsigned i = x / b_w + y / b_h * 2;
38 int c = ((i >> p) & 1);
39 if (bytes == 1) {
40 c *= (1 << 8) - 1;
41 for (int xs = x; xs < x + b_w; xs++)
42 ((uint8_t *)line)[xs] = c;
43 } else if (bytes == 2) {
44 c *= (1 << 16) - 1;
45 for (int xs = x; xs < x + b_w; xs++)
46 ((uint16_t *)line)[xs] = c;
47 }
48 }
49 }
50 }
51
52 return mpi;
53 }
54
dump_image(struct scale_test * stest,const char * name,struct mp_image * img)55 static void dump_image(struct scale_test *stest, const char *name,
56 struct mp_image *img)
57 {
58 char *path = mp_tprintf(4096, "%s/%s.png", stest->ctx->out_path, name);
59
60 struct image_writer_opts opts = image_writer_opts_defaults;
61 opts.format = AV_CODEC_ID_PNG;
62
63 if (!write_image(img, &opts, path, stest->ctx->global, stest->ctx->log)) {
64 MP_FATAL(stest->ctx, "Failed to write '%s'.\n", path);
65 abort();
66 }
67 }
68
69 // Compare 2 images (same format and size) for exact pixel data match.
70 // Does generally not work with formats that include undefined padding.
71 // Does not work with non-byte aligned formats.
assert_imgs_equal(struct scale_test * stest,FILE * f,struct mp_image * ref,struct mp_image * new)72 static void assert_imgs_equal(struct scale_test *stest, FILE *f,
73 struct mp_image *ref, struct mp_image *new)
74 {
75 assert(ref->imgfmt == new->imgfmt);
76 assert(ref->w == new->w);
77 assert(ref->h == new->h);
78
79 assert(ref->fmt.flags & MP_IMGFLAG_BYTE_ALIGNED);
80 assert(ref->fmt.bpp[0]);
81
82 for (int p = 0; p < ref->num_planes; p++) {
83 for (int y = 0; y < ref->h; y++) {
84 void *line_r = ref->planes[p] + ref->stride[p] * (ptrdiff_t)y;
85 void *line_o = new->planes[p] + new->stride[p] * (ptrdiff_t)y;
86 size_t size = mp_image_plane_bytes(ref, p, 0, new->w);
87
88 bool ok = memcmp(line_r, line_o, size) == 0;
89 if (!ok) {
90 stest->fail += 1;
91 char *fn_a = mp_tprintf(80, "img%d_ref", stest->fail);
92 char *fn_b = mp_tprintf(80, "img%d_new", stest->fail);
93 fprintf(f, "Images mismatching, dumping to %s/%s\n", fn_a, fn_b);
94 dump_image(stest, fn_a, ref);
95 dump_image(stest, fn_b, new);
96 return;
97 }
98 }
99 }
100 }
101
repack_test_run(struct scale_test * stest)102 void repack_test_run(struct scale_test *stest)
103 {
104 char *logname = mp_tprintf(80, "%s.log", stest->test_name);
105 FILE *f = test_open_out(stest->ctx, logname);
106
107 if (!stest->sws) {
108 init_imgfmts_list();
109
110 stest->sws = mp_sws_alloc(stest);
111
112 stest->img_repack_rgb8 = gen_repack_test_img(256, 128, 1, true, false);
113 stest->img_repack_rgba8 = gen_repack_test_img(256, 128, 1, true, true);
114 stest->img_repack_rgb16 = gen_repack_test_img(256, 128, 2, true, false);
115 stest->img_repack_rgba16 = gen_repack_test_img(256, 128, 2, true, true);
116
117 talloc_steal(stest, stest->img_repack_rgb8);
118 talloc_steal(stest, stest->img_repack_rgba8);
119 talloc_steal(stest, stest->img_repack_rgb16);
120 talloc_steal(stest, stest->img_repack_rgba16);
121 }
122
123 for (int a = 0; a < num_imgfmts; a++) {
124 int mpfmt = imgfmts[a];
125 struct mp_imgfmt_desc fmtdesc = mp_imgfmt_get_desc(mpfmt);
126 struct mp_regular_imgfmt rdesc;
127 if (!mp_get_regular_imgfmt(&rdesc, mpfmt)) {
128 int ofmt = mp_find_other_endian(mpfmt);
129 if (!mp_get_regular_imgfmt(&rdesc, ofmt))
130 continue;
131 }
132 if (rdesc.num_planes > 1 || rdesc.forced_csp != MP_CSP_RGB)
133 continue;
134
135 struct mp_image *test_img = NULL;
136 bool alpha = fmtdesc.flags & MP_IMGFLAG_ALPHA;
137 bool hidepth = rdesc.component_size > 1;
138 if (alpha) {
139 test_img = hidepth ? stest->img_repack_rgba16 : stest->img_repack_rgba8;
140 } else {
141 test_img = hidepth ? stest->img_repack_rgb16 : stest->img_repack_rgb8;
142 }
143
144 if (test_img->imgfmt == mpfmt)
145 continue;
146
147 if (!stest->fns->supports_fmts(stest->fns_priv, mpfmt, test_img->imgfmt))
148 continue;
149
150 if (!mp_sws_supports_formats(stest->sws, mpfmt, test_img->imgfmt))
151 continue;
152
153 fprintf(f, "%s using %s\n", mp_imgfmt_to_name(mpfmt),
154 mp_imgfmt_to_name(test_img->imgfmt));
155
156 struct mp_image *dst = mp_image_alloc(mpfmt, test_img->w, test_img->h);
157 assert(dst);
158
159 // This tests packing.
160 bool ok = stest->fns->scale(stest->fns_priv, dst, test_img);
161 assert(ok);
162
163 // Cross-check with swscale in the other direction.
164 // (Mostly so we don't have to worry about padding.)
165 struct mp_image *src2 =
166 mp_image_alloc(test_img->imgfmt, test_img->w, test_img->h);
167 assert(src2);
168 ok = mp_sws_scale(stest->sws, src2, dst) >= 0;
169 assert_imgs_equal(stest, f, test_img, src2);
170
171 // Assume the other conversion direction also works.
172 assert(stest->fns->supports_fmts(stest->fns_priv, test_img->imgfmt, mpfmt));
173
174 struct mp_image *back = mp_image_alloc(test_img->imgfmt, dst->w, dst->h);
175 assert(back);
176
177 // This tests unpacking.
178 ok = stest->fns->scale(stest->fns_priv, back, dst);
179 assert(ok);
180
181 assert_imgs_equal(stest, f, test_img, back);
182
183 talloc_free(back);
184 talloc_free(src2);
185 talloc_free(dst);
186 }
187
188 fclose(f);
189
190 assert_text_files_equal(stest->ctx, logname, logname,
191 "This can fail if FFmpeg adds or removes pixfmts.");
192 }
193