History

video: separate repacking code from zimg and make it independent

For whatever purpose. If anything, this makes the zimg wrapper cleaner. The added tests are not particular exhaustive, but nice to have. This also makes the scale_zimg.c test pretty useless, because it only tests repacking (going through the zimg wrapper). In theory, the repack_tests things could also be used on scalers, but I guess it doesn't matter. Some things are added over the previous zimg wrapper code. For example, some fringe formats can now be expanded to 8 bit per component for convenience.

wm4 committed 6 years ago

Commit d8002f11 parent d61ced39 files · +1688 −895Browse files

Unified · Split

9 files
  1. test/ref/repack.txt +163 −0
  2. test/repack.c +249 −0
  3. test/tests.c +23 −0
  4. test/tests.h +7 −0
  5. video/img_format.h +2 −1
  6. video/repack.c +1110 −0
  7. video/repack.h +72 −0
  8. video/zimg.c +60 −894
  9. wscript_build.py +2 −0
test/ref/repack.txt+163−0
@@ -0,0 +1,163 @@
10bgr => [pa] [un] gbrp | a=1:1 [tu] [tp]
20rgb => [pa] [un] gbrp | a=1:1 [tu] [tp]
3abgr => [pa] [un] gbrap | a=1:1 [tu] [tp]
4argb => [pa] [un] gbrap | a=1:1 [tu] [tp]
5ayuv64 => [pa] [un] yuva444p16 | a=1:1 [tu] [tp]
6ayuv64be => [pa] [un] yuva444p16 | a=1:1 [tu] [tp]
7bayer_bggr16 => no
8bayer_bggr16be => no
9bayer_bggr8 => no
10bayer_gbrg16 => no
11bayer_gbrg16be => no
12bayer_gbrg8 => no
13bayer_grbg16 => no
14bayer_grbg16be => no
15bayer_grbg8 => no
16bayer_rggb16 => no
17bayer_rggb16be => no
18bayer_rggb8 => no
19bgr0 => [pa] [un] gbrp | a=1:1 [tu] [tp]
20bgr24 => [pa] [un] gbrp | a=1:1
21bgr4 => no
22bgr444 => [pa] [un] gbrp4 | a=1:1
23bgr444 => [pa] [un] gbrp | a=1:1 [expand-8bit]
24bgr444be => [pa] [un] gbrp4 | a=1:1
25bgr444be => [pa] [un] gbrp | a=1:1 [expand-8bit]
26bgr48 => [pa] [un] gbrp16 | a=1:1
27bgr48be => [pa] [un] gbrp16 | a=1:1
28bgr4_byte => [pa] [un] gbrp2 | a=1:1
29bgr4_byte => [pa] [un] gbrp1 | a=1:1 [round-down]
30bgr4_byte => [pa] [un] gbrp | a=1:1 [expand-8bit]
31bgr555 => [pa] [un] gbrp5 | a=1:1
32bgr555 => [pa] [un] gbrp | a=1:1 [expand-8bit]
33bgr555be => [pa] [un] gbrp5 | a=1:1
34bgr555be => [pa] [un] gbrp | a=1:1 [expand-8bit]
35bgr565 => [pa] [un] gbrp6 | a=1:1
36bgr565 => [pa] [un] gbrp5 | a=1:1 [round-down]
37bgr565 => [pa] [un] gbrp | a=1:1 [expand-8bit]
38bgr565be => [pa] [un] gbrp6 | a=1:1
39bgr565be => [pa] [un] gbrp5 | a=1:1 [round-down]
40bgr565be => [pa] [un] gbrp | a=1:1 [expand-8bit]
41bgr8 => [pa] [un] gbrp3 | a=1:1
42bgr8 => [pa] [un] gbrp2 | a=1:1 [round-down]
43bgr8 => [pa] [un] gbrp | a=1:1 [expand-8bit]
44bgra => [pa] [un] gbrap | a=1:1 [tu] [tp]
45bgra64 => [pa] [un] gbrap16 | a=1:1
46bgra64be => [pa] [un] gbrap16 | a=1:1
47cuda => no
48d3d11 => no
49d3d11va_vld => no
50drm_prime => no
51dxva2_vld => no
52gbrap10be => [pa] [un] gbrap10 | a=1:1
53gbrap12be => [pa] [un] gbrap12 | a=1:1
54gbrap16be => [pa] [un] gbrap16 | a=1:1
55gbrapf32be => [pa] [un] gbrapf32 | a=1:1
56gbrp10be => [pa] [un] gbrp10 | a=1:1
57gbrp12be => [pa] [un] gbrp12 | a=1:1
58gbrp14be => [pa] [un] gbrp14 | a=1:1
59gbrp16be => [pa] [un] gbrp16 | a=1:1
60gbrp9be => [pa] [un] gbrp9 | a=1:1
61gbrpf32be => [pa] [un] gbrpf32 | a=1:1
62gray10be => [pa] [un] gray10 | a=1:1
63gray12be => [pa] [un] gray12 | a=1:1
64gray14be => [pa] [un] gray14 | a=1:1
65gray16be => [pa] [un] gray16 | a=1:1
66gray9be => [pa] [un] gray9 | a=1:1
67grayf32be => [pa] [un] grayf32 | a=1:1
68mediacodec => no
69mmal => no
70monob => [pa] [un] y1 | a=8:1 [tu] [tp]
71monob => [pa] [un] gray | a=8:1 [expand-8bit]
72monow => [pa] [un] y1 | a=8:1 [tu] [tp]
73monow => [pa] [un] gray | a=8:1 [expand-8bit]
74nv12 => [pa] [un] yuv420p | a=2:2 [tu] [tp]
75nv16 => [pa] [un] yuv422p | a=2:1
76nv20 => [pa] [un] yuv422p10 | a=2:1
77nv20be => [pa] [un] yuv422p10 | a=2:1
78nv21 => [pa] [un] yuv420p | a=2:2 [tu] [tp]
79nv24 => [pa] [un] yuv444p | a=1:1
80nv42 => [pa] [un] yuv444p | a=1:1
81opencl => no
82p010 => [pa] [un] yuv420p16 | a=2:2
83p010be => [pa] [un] yuv420p16 | a=2:2
84p016 => [pa] [un] yuv420p16 | a=2:2
85p016be => [pa] [un] yuv420p16 | a=2:2
86pal8 => [un] gbrap | a=1:1
87qsv => no
88rgb0 => [pa] [un] gbrp | a=1:1 [tu] [tp]
89rgb24 => [pa] [un] gbrp | a=1:1
90rgb30 => [pa] [un] gbrp10 | a=1:1
91rgb4 => no
92rgb444 => [pa] [un] gbrp4 | a=1:1
93rgb444 => [pa] [un] gbrp | a=1:1 [expand-8bit]
94rgb444be => [pa] [un] gbrp4 | a=1:1
95rgb444be => [pa] [un] gbrp | a=1:1 [expand-8bit]
96rgb48 => [pa] [un] gbrp16 | a=1:1
97rgb48be => [pa] [un] gbrp16 | a=1:1 [tu] [tp]
98rgb4_byte => [pa] [un] gbrp2 | a=1:1
99rgb4_byte => [pa] [un] gbrp1 | a=1:1 [round-down]
100rgb4_byte => [pa] [un] gbrp | a=1:1 [expand-8bit]
101rgb555 => [pa] [un] gbrp5 | a=1:1
102rgb555 => [pa] [un] gbrp | a=1:1 [expand-8bit]
103rgb555be => [pa] [un] gbrp5 | a=1:1
104rgb555be => [pa] [un] gbrp | a=1:1 [expand-8bit]
105rgb565 => [pa] [un] gbrp6 | a=1:1
106rgb565 => [pa] [un] gbrp5 | a=1:1 [round-down]
107rgb565 => [pa] [un] gbrp | a=1:1 [expand-8bit]
108rgb565be => [pa] [un] gbrp6 | a=1:1
109rgb565be => [pa] [un] gbrp5 | a=1:1 [round-down]
110rgb565be => [pa] [un] gbrp | a=1:1 [expand-8bit]
111rgb8 => [pa] [un] gbrp3 | a=1:1
112rgb8 => [pa] [un] gbrp2 | a=1:1 [round-down]
113rgb8 => [pa] [un] gbrp | a=1:1 [expand-8bit]
114rgba => [pa] [un] gbrap | a=1:1 [tu] [tp]
115rgba64 => [pa] [un] gbrap16 | a=1:1 [tu] [tp]
116rgba64be => [pa] [un] gbrap16 | a=1:1
117uyvy422 => [pa] [un] yuv422p | a=2:1
118uyyvyy411 => no
119vaapi => no
120vaapi_idct => no
121vaapi_moco => no
122vdpau => no
123vdpau_output => no
124videotoolbox => no
125vulkan => no
126xvmc => no
127xyz12 => [pa] [un] gbrp16 | a=1:1
128xyz12be => [pa] [un] gbrp16 | a=1:1
129y210 => [pa] [un] yuv422p16 | a=2:1
130y210be => [pa] [un] yuv422p16 | a=2:1
131ya16 => [pa] [un] yap16 | a=1:1 [tu] [tp]
132ya16be => [pa] [un] yap16 | a=1:1
133ya8 => [pa] [un] yap8 | a=1:1
134yuv420p10be => [pa] [un] yuv420p10 | a=2:2
135yuv420p12be => [pa] [un] yuv420p12 | a=2:2
136yuv420p14be => [pa] [un] yuv420p14 | a=2:2
137yuv420p16be => [pa] [un] yuv420p16 | a=2:2
138yuv420p9be => [pa] [un] yuv420p9 | a=2:2
139yuv422p10be => [pa] [un] yuv422p10 | a=2:1
140yuv422p12be => [pa] [un] yuv422p12 | a=2:1
141yuv422p14be => [pa] [un] yuv422p14 | a=2:1
142yuv422p16be => [pa] [un] yuv422p16 | a=2:1 [tu] [tp]
143yuv422p9be => [pa] [un] yuv422p9 | a=2:1
144yuv440p10be => [pa] [un] yuv440p10 | a=1:2
145yuv440p12be => [pa] [un] yuv440p12 | a=1:2
146yuv444p10be => [pa] [un] yuv444p10 | a=1:1
147yuv444p12be => [pa] [un] yuv444p12 | a=1:1
148yuv444p14be => [pa] [un] yuv444p14 | a=1:1
149yuv444p16be => [pa] [un] yuv444p16 | a=1:1
150yuv444p9be => [pa] [un] yuv444p9 | a=1:1
151yuva420p10be => [pa] [un] yuva420p10 | a=2:2
152yuva420p16be => [pa] [un] yuva420p16 | a=2:2
153yuva420p9be => [pa] [un] yuva420p9 | a=2:2
154yuva422p10be => [pa] [un] yuva422p10 | a=2:1
155yuva422p12be => [pa] [un] yuva422p12 | a=2:1
156yuva422p16be => [pa] [un] yuva422p16 | a=2:1
157yuva422p9be => [pa] [un] yuva422p9 | a=2:1
158yuva444p10be => [pa] [un] yuva444p10 | a=1:1
159yuva444p12be => [pa] [un] yuva444p12 | a=1:1
160yuva444p16be => [pa] [un] yuva444p16 | a=1:1
161yuva444p9be => [pa] [un] yuva444p9 | a=1:1
162yuyv422 => [pa] [un] yuv422p | a=2:1
163yvyu422 => [pa] [un] yuv422p | a=2:1 [tu] [tp]
test/repack.c+249−0
@@ -0,0 +1,249 @@
1#include <libavutil/pixfmt.h>
2
3#include "common/common.h"
4#include "tests.h"
5#include "video/fmt-conversion.h"
6#include "video/img_format.h"
7#include "video/repack.h"
8#include "video/zimg.h"
9
10// Excuse the utter stupidity.
11#define UNFUCK(v) ((v) > 0 ? (v) : pixfmt2imgfmt(-(v)))
12static_assert(IMGFMT_START > 0, "");
13#define IMGFMT_GBRP (-AV_PIX_FMT_GBRP)
14#define IMGFMT_GBRAP (-AV_PIX_FMT_GBRAP)
15
16struct entry {
17 int w, h;
18 int fmt_a;
19 const void *const a[4];
20 int fmt_b;
21 const void *const b[4];
22 int flags;
23};
24
25#define P8(...) (const uint8_t[]){__VA_ARGS__}
26#define P16(...) (const uint16_t[]){__VA_ARGS__}
27
28// Warning: only entries that match existing conversions are tested.
29static const struct entry repack_tests[] = {
30 // Note: the '0' tests rely on 0 being written, although by definition the
31 // contents of this padding is undefined. The repacker always writes
32 // it this way, though.
33 {1, 1, IMGFMT_RGB0, {P8(1, 2, 3, 0)},
34 IMGFMT_GBRP, {P8(2), P8(3), P8(1)}},
35 {1, 1, IMGFMT_BGR0, {P8(1, 2, 3, 0)},
36 IMGFMT_GBRP, {P8(2), P8(1), P8(3)}},
37 {1, 1, IMGFMT_0RGB, {P8(0, 1, 2, 3)},
38 IMGFMT_GBRP, {P8(2), P8(3), P8(1)}},
39 {1, 1, IMGFMT_0BGR, {P8(0, 1, 2, 3)},
40 IMGFMT_GBRP, {P8(2), P8(1), P8(3)}},
41 {1, 1, IMGFMT_RGBA, {P8(1, 2, 3, 4)},
42 IMGFMT_GBRAP, {P8(2), P8(3), P8(1), P8(4)}},
43 {1, 1, IMGFMT_BGRA, {P8(1, 2, 3, 4)},
44 IMGFMT_GBRAP, {P8(2), P8(1), P8(3), P8(4)}},
45 {1, 1, IMGFMT_ARGB, {P8(4, 1, 2, 3)},
46 IMGFMT_GBRAP, {P8(2), P8(3), P8(1), P8(4)}},
47 {1, 1, IMGFMT_ABGR, {P8(4, 1, 2, 3)},
48 IMGFMT_GBRAP, {P8(2), P8(1), P8(3), P8(4)}},
49 {1, 1, IMGFMT_RGBA64, {P16(0x1a1b, 0x2a2b, 0x3a3b, 0x4a4b)},
50 -AV_PIX_FMT_GBRAP16, {P16(0x2a2b), P16(0x3a3b),
51 P16(0x1a1b), P16(0x4a4b)}},
52 {1, 1, -AV_PIX_FMT_RGB48BE, {P16(0x1a1b, 0x2a2b, 0x3a3b)},
53 -AV_PIX_FMT_GBRP16, {P16(0x2b2a), P16(0x3b3a),
54 P16(0x1b1a)}},
55 {8, 1, -AV_PIX_FMT_MONOWHITE, {P8(0xAA)},
56 IMGFMT_Y1, {P8(0, 1, 0, 1, 0, 1, 0, 1)}},
57 {8, 1, -AV_PIX_FMT_MONOBLACK, {P8(0xAA)},
58 IMGFMT_Y1, {P8(1, 0, 1, 0, 1, 0, 1, 0)}},
59 {2, 2, IMGFMT_NV12, {P8(1, 2, 3, 4), P8(5, 6)},
60 IMGFMT_420P, {P8(1, 2, 3, 4), P8(5), P8(6)}},
61 {2, 2, -AV_PIX_FMT_NV21, {P8(1, 2, 3, 4), P8(5, 6)},
62 IMGFMT_420P, {P8(1, 2, 3, 4), P8(6), P8(5)}},
63 {1, 1, -AV_PIX_FMT_AYUV64, {P16(1, 2, 3, 4)},
64 -AV_PIX_FMT_YUVA444P16, {P16(2), P16(3), P16(4), P16(1)}},
65 {1, 1, -AV_PIX_FMT_AYUV64BE, {P16(0x0100, 0x0200, 0x0300, 0x0400)},
66 -AV_PIX_FMT_YUVA444P16, {P16(2), P16(3), P16(4), P16(1)}},
67 {2, 1, -AV_PIX_FMT_YVYU422, {P8(1, 2, 3, 4)},
68 -AV_PIX_FMT_YUV422P, {P8(1, 3), P8(4), P8(2)}},
69 {1, 1, -AV_PIX_FMT_YA16, {P16(1, 2)},
70 IMGFMT_YAP16, {P16(1), P16(2)}},
71 {2, 1, -AV_PIX_FMT_YUV422P16BE, {P16(0x1a1b, 0x2a2b), P16(0x3a3b),
72 P16(0x4a4b)},
73 -AV_PIX_FMT_YUV422P16, {P16(0x1b1a, 0x2b2a), P16(0x3b3a),
74 P16(0x4b4a)}},
75};
76
77static bool is_true_planar(int imgfmt)
78{
79 struct mp_regular_imgfmt desc;
80 if (!mp_get_regular_imgfmt(&desc, imgfmt))
81 return false;
82
83 for (int n = 0; n < desc.num_planes; n++) {
84 if (desc.planes[n].num_components != 1)
85 return false;
86 }
87
88 return true;
89}
90
91static int try_repack(struct test_ctx *ctx, FILE *f, int imgfmt, int flags,
92 int not_if_fmt)
93{
94 char *head = mp_tprintf(80, "%-15s =>", mp_imgfmt_to_name(imgfmt));
95 struct mp_repack *un = mp_repack_create_planar(imgfmt, false, flags);
96 struct mp_repack *pa = mp_repack_create_planar(imgfmt, true, flags);
97
98 // If both exists, they must be always symmetric.
99 if (un && pa) {
100 assert(mp_repack_get_format_src(pa) == mp_repack_get_format_dst(un));
101 assert(mp_repack_get_format_src(un) == mp_repack_get_format_dst(pa));
102 assert(mp_repack_get_align_x(pa) == mp_repack_get_align_x(un));
103 assert(mp_repack_get_align_y(pa) == mp_repack_get_align_y(un));
104 }
105
106 int a = 0;
107 int b = 0;
108 if (un) {
109 a = mp_repack_get_format_src(un);
110 b = mp_repack_get_format_dst(un);
111 } else if (pa) {
112 a = mp_repack_get_format_dst(pa);
113 b = mp_repack_get_format_src(pa);
114 }
115
116 // Skip the identity ones because they're uninteresting, and add too much
117 // noise. But still make sure they behave as expected.
118 if (is_true_planar(imgfmt)) {
119 // (note that we require alpha-enabled zimg)
120 assert(mp_zimg_supports_in_format(imgfmt));
121 assert(un && pa);
122 assert(a == imgfmt && b == imgfmt);
123 talloc_free(pa);
124 talloc_free(un);
125 return 0;
126 }
127
128 struct mp_repack *rp = pa ? pa : un;
129 if (!rp) {
130 if (!flags)
131 fprintf(f, "%s no\n", head);
132 return 0;
133 }
134
135 assert(a == imgfmt);
136 if (b && b == not_if_fmt) {
137 talloc_free(pa);
138 talloc_free(un);
139 return 0;
140 }
141
142 fprintf(f, "%s %4s %4s %-15s |", head, pa ? "[pa]" : "", un ? "[un]" : "",
143 mp_imgfmt_to_name(b));
144
145 fprintf(f, " a=%d:%d", mp_repack_get_align_x(rp), mp_repack_get_align_y(rp));
146
147 if (flags & REPACK_CREATE_ROUND_DOWN)
148 fprintf(f, " [round-down]");
149 if (flags & REPACK_CREATE_EXPAND_8BIT)
150 fprintf(f, " [expand-8bit]");
151
152 // LCM of alignment of all packers.
153 int ax = mp_repack_get_align_x(rp);
154 int ay = mp_repack_get_align_y(rp);
155 if (pa && un) {
156 ax = MPMAX(mp_repack_get_align_x(pa), mp_repack_get_align_x(un));
157 ay = MPMAX(mp_repack_get_align_y(pa), mp_repack_get_align_y(un));
158 }
159
160 for (int n = 0; n < MP_ARRAY_SIZE(repack_tests); n++) {
161 const struct entry *e = &repack_tests[n];
162 int fmt_a = UNFUCK(e->fmt_a);
163 int fmt_b = UNFUCK(e->fmt_b);
164 if (!(fmt_a == a && fmt_b == b && e->flags == flags))
165 continue;
166
167 // We convert a "random" macro pixel to catch potential addressing bugs
168 // that might be ignored with (0, 0) origins.
169 struct mp_image *ia = mp_image_alloc(fmt_a, e->w * 5 * ax, e->h * 5 * ay);
170 struct mp_image *ib = mp_image_alloc(fmt_b, e->w * 7 * ax, e->h * 6 * ay);
171 int sx = 4 * ax, sy = 3 * ay, dx = 3 * ax, dy = 2 * ay;
172
173 assert(ia && ib);
174
175 for (int pack = 0; pack < 2; pack++) {
176 struct mp_repack *repacker = pack ? pa : un;
177 if (!repacker)
178 continue;
179
180 mp_image_clear(ia, 0, 0, ia->w, ia->h);
181 mp_image_clear(ib, 0, 0, ib->w, ib->h);
182
183 const void *const *dstd = pack ? e->a : e->b;
184 const void *const *srcd = pack ? e->b : e->a;
185 struct mp_image *dsti = pack ? ia : ib;
186 struct mp_image *srci = pack ? ib : ia;
187
188 bool r = repack_config_buffers(repacker, 0, dsti, 0, srci, NULL);
189 assert(r);
190
191 for (int p = 0; p < srci->num_planes; p++) {
192 uint8_t *ptr = mp_image_pixel_ptr(srci, p, sx, sy);
193 for (int y = 0; y < e->h >> srci->fmt.ys[p]; y++) {
194 int w = e->w >> srci->fmt.xs[p];
195 int wb = (w * srci->fmt.bpp[p] + 7) / 8;
196 const void *cptr = (uint8_t *)srcd[p] + wb * y;
197 memcpy(ptr + srci->stride[p] * y, cptr, wb);
198 }
199 }
200
201 repack_line(repacker, dx, dy, sx, sy, e->w);
202
203 for (int p = 0; p < dsti->num_planes; p++) {
204 uint8_t *ptr = mp_image_pixel_ptr(dsti, p, dx, dy);
205 for (int y = 0; y < e->h >> dsti->fmt.ys[p]; y++) {
206 int w = e->w >> dsti->fmt.xs[p];
207 int wb = (w * dsti->fmt.bpp[p] + 7) / 8;
208 const void *cptr = (uint8_t *)dstd[p] + wb * y;
209 assert_memcmp(ptr + dsti->stride[p] * y, cptr, wb);
210 }
211 }
212
213 fprintf(f, " [t%s]", pack ? "p" : "u");
214 }
215
216 talloc_free(ia);
217 talloc_free(ib);
218 }
219
220 fprintf(f, "\n");
221
222 talloc_free(pa);
223 talloc_free(un);
224 return b;
225}
226
227static void run(struct test_ctx *ctx)
228{
229 FILE *f = test_open_out(ctx, "repack.txt");
230
231 init_imgfmts_list();
232 for (int n = 0; n < num_imgfmts; n++) {
233 int imgfmt = imgfmts[n];
234
235 int other = try_repack(ctx, f, imgfmt, 0, 0);
236 try_repack(ctx, f, imgfmt, REPACK_CREATE_ROUND_DOWN, other);
237 try_repack(ctx, f, imgfmt, REPACK_CREATE_EXPAND_8BIT, other);
238 }
239
240 fclose(f);
241
242 assert_text_files_equal(ctx, "repack.txt", "repack.txt",
243 "This can fail if FFmpeg/libswscale adds or removes pixfmts.");
244}
245
246const struct unittest test_repack = {
247 .name = "repack",
248 .run = run,
249};
test/tests.c+23−0
@@ -12,6 +12,7 @@ static const struct unittest *unittests[] = { · 11 unmodified lines
12 &test_paths,12 &test_paths,
13 &test_repack_sws,13 &test_repack_sws,
14#if HAVE_ZIMG14#if HAVE_ZIMG
15 &test_repack, // zimg only due to cross-checking with zimg.c
15 &test_repack_zimg,16 &test_repack_zimg,
16#endif17#endif
17 NULL18 NULL
@@ -128,3 +129,25 @@ void assert_text_files_equal_impl(const char *file, int line, · 110 unmodified lines
128 abort();129 abort();
129 }130 }
130}131}
132
133static void hexdump(const uint8_t *d, size_t size)
134{
135 printf("|");
136 while (size--) {
137 printf(" %02x", d[0]);
138 d++;
139 }
140 printf(" |\n");
141}
142
143void assert_memcmp_impl(const char *file, int line,
144 const void *a, const void *b, size_t size)
145{
146 if (memcmp(a, b, size) == 0)
147 return;
148
149 printf("%s:%d: mismatching data:\n", file, line);
150 hexdump(a, size);
151 hexdump(b, size);
152 abort();
153}
test/tests.h+7−0
@@ -43,6 +43,7 @@ extern const struct unittest test_json; · 42 unmodified lines
43extern const struct unittest test_linked_list;43extern const struct unittest test_linked_list;
44extern const struct unittest test_repack_sws;44extern const struct unittest test_repack_sws;
45extern const struct unittest test_repack_zimg;45extern const struct unittest test_repack_zimg;
46extern const struct unittest test_repack;
46extern const struct unittest test_paths;47extern const struct unittest test_paths;
47 48
48#define assert_true(x) assert(x)49#define assert_true(x) assert(x)
@@ -54,6 +55,10 @@ extern const struct unittest test_paths; · 5 unmodified lines
54#define assert_float_equal(a, b, tolerance) \55#define assert_float_equal(a, b, tolerance) \
55 assert_float_equal_impl(__FILE__, __LINE__, (a), (b), (tolerance))56 assert_float_equal_impl(__FILE__, __LINE__, (a), (b), (tolerance))
56 57
58// Assert that memcmp(a,b,s)==0, or hexdump output on failure.
59#define assert_memcmp(a, b, s) \
60 assert_memcmp_impl(__FILE__, __LINE__, (a), (b), (s))
61
57// Require that the files "ref" and "new" are the same. The paths can be62// Require that the files "ref" and "new" are the same. The paths can be
58// relative to ref_path and out_path respectively. If they're not the same,63// relative to ref_path and out_path respectively. If they're not the same,
59// the output of "diff" is shown, the err message (if not NULL), and the test64// the output of "diff" is shown, the err message (if not NULL), and the test
@@ -69,6 +74,8 @@ void assert_float_equal_impl(const char *file, int line, · 9 unmodified lines
69void assert_text_files_equal_impl(const char *file, int line,74void assert_text_files_equal_impl(const char *file, int line,
70 struct test_ctx *ctx, const char *ref,75 struct test_ctx *ctx, const char *ref,
71 const char *new, const char *err);76 const char *new, const char *err);
77void assert_memcmp_impl(const char *file, int line,
78 const void *a, const void *b, size_t size);
72 79
73// Open a new file in the out_path. Always succeeds.80// Open a new file in the out_path. Always succeeds.
74FILE *test_open_out(struct test_ctx *ctx, const char *name);81FILE *test_open_out(struct test_ctx *ctx, const char *name);
video/img_format.h+2−1
@@ -69,8 +69,9 @@ struct mp_imgfmt_desc { · 68 unmodified lines
69 int flags; // MP_IMGFLAG_* bitfield69 int flags; // MP_IMGFLAG_* bitfield
70 int8_t num_planes;70 int8_t num_planes;
71 int8_t chroma_xs, chroma_ys; // chroma shift (i.e. log2 of chroma pixel size)71 int8_t chroma_xs, chroma_ys; // chroma shift (i.e. log2 of chroma pixel size)
72 int8_t align_x, align_y; // pixel size to get byte alignment and to get72 int8_t align_x, align_y; // pixel count to get byte alignment and to get
73 // to a pixel pos where luma & chroma aligns73 // to a pixel pos where luma & chroma aligns
74 // always power of 2
74 int8_t bytes[MP_MAX_PLANES]; // bytes per pixel (MP_IMGFLAG_BYTE_ALIGNED)75 int8_t bytes[MP_MAX_PLANES]; // bytes per pixel (MP_IMGFLAG_BYTE_ALIGNED)
75 int8_t bpp[MP_MAX_PLANES]; // bits per pixel76 int8_t bpp[MP_MAX_PLANES]; // bits per pixel
76 int8_t plane_bits; // number of bits in use for plane 077 int8_t plane_bits; // number of bits in use for plane 0
video/repack.c+1110−0
@@ -0,0 +1,1110 @@
1/*
2 * This file is part of mpv.
3 *
4 * mpv is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * mpv is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with mpv. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#include <math.h>
19
20#include <libavutil/bswap.h>
21#include <libavutil/pixfmt.h>
22
23#include "common/common.h"
24#include "repack.h"
25#include "video/fmt-conversion.h"
26#include "video/img_format.h"
27#include "video/mp_image.h"
28
29enum repack_step_type {
30 REPACK_STEP_REPACK,
31 REPACK_STEP_ENDIAN,
32};
33
34struct repack_step {
35 enum repack_step_type type;
36 // 0=input, 1=output
37 struct mp_image *buf[2];
38 bool user_buf[2]; // user_buf[n]==true if buf[n] = user src/dst buffer
39 struct mp_imgfmt_desc fmt[2];
40 struct mp_image *tmp; // output buffer, if needed
41};
42
43struct mp_repack {
44 bool pack; // if false, this is for unpacking
45 int flags;
46 int imgfmt_user; // original mp format (unchanged endian)
47 int imgfmt_a; // original mp format (possibly packed format,
48 // swapped endian)
49 int imgfmt_b; // equivalent unpacked/planar format
50 struct mp_imgfmt_desc fmt_a;// ==imgfmt_a
51 struct mp_imgfmt_desc fmt_b;// ==imgfmt_b
52
53 void (*repack)(struct mp_repack *rp,
54 struct mp_image *a, int a_x, int a_y,
55 struct mp_image *b, int b_x, int b_y, int w);
56
57 bool passthrough_y; // possible luma plane optimization for e.g. nv12
58 int endian_size; // endian swap; 0=none, 2/4=swap word size
59
60 // For packed_repack.
61 int components[4]; // b[n] = mp_image.planes[components[n]]
62 // pack: a is dst, b is src
63 // unpack: a is src, b is dst
64 void (*packed_repack_scanline)(void *a, void *b[], int w);
65
66 // Fringe RGB/YUV.
67 uint8_t comp_size;
68 uint8_t *comp_map;
69 uint8_t comp_shifts[3];
70 uint8_t *comp_lut;
71
72 // REPACK_STEP_REPACK: if true, need to copy this plane
73 bool copy_buf[4];
74
75 struct repack_step steps[4];
76 int num_steps;
77
78 bool configured;
79};
80
81// depth = number of LSB in use
82static int find_gbrp_format(int depth, int num_planes)
83{
84 if (num_planes != 3 && num_planes != 4)
85 return 0;
86 struct mp_regular_imgfmt desc = {
87 .component_type = MP_COMPONENT_TYPE_UINT,
88 .forced_csp = MP_CSP_RGB,
89 .component_size = depth > 8 ? 2 : 1,
90 .component_pad = depth - (depth > 8 ? 16 : 8),
91 .num_planes = num_planes,
92 .planes = { {1, {2}}, {1, {3}}, {1, {1}}, {1, {4}} },
93 };
94 return mp_find_regular_imgfmt(&desc);
95}
96
97// depth = number of LSB in use
98static int find_yuv_format(int depth, int num_planes)
99{
100 if (num_planes < 1 || num_planes > 4)
101 return 0;
102 struct mp_regular_imgfmt desc = {
103 .component_type = MP_COMPONENT_TYPE_UINT,
104 .component_size = depth > 8 ? 2 : 1,
105 .component_pad = depth - (depth > 8 ? 16 : 8),
106 .num_planes = num_planes,
107 .planes = { {1, {1}}, {1, {2}}, {1, {3}}, {1, {4}} },
108 };
109 if (num_planes == 2)
110 desc.planes[1].components[0] = 4;
111 return mp_find_regular_imgfmt(&desc);
112}
113
114// Copy one line on the plane p.
115static void copy_plane(struct mp_image *dst, int dst_x, int dst_y,
116 struct mp_image *src, int src_x, int src_y,
117 int w, int p)
118{
119 // Number of lines on this plane.
120 int h = (1 << dst->fmt.chroma_ys) - (1 << dst->fmt.ys[p]) + 1;
121 size_t size = mp_image_plane_bytes(dst, p, dst_x, w);
122
123 assert(dst->fmt.bpp[p] == src->fmt.bpp[p]);
124
125 for (int y = 0; y < h; y++) {
126 void *pd = mp_image_pixel_ptr(dst, p, dst_x, dst_y + y);
127 void *ps = mp_image_pixel_ptr(src, p, src_x, src_y + y);
128 memcpy(pd, ps, size);
129 }
130}
131
132// Swap endian for one line.
133static void swap_endian(struct mp_image *dst, int dst_x, int dst_y,
134 struct mp_image *src, int src_x, int src_y,
135 int w, int endian_size)
136{
137 assert(src->fmt.num_planes == dst->fmt.num_planes);
138
139 for (int p = 0; p < dst->fmt.num_planes; p++) {
140 int xs = dst->fmt.xs[p];
141 int bpp = dst->fmt.bytes[p];
142 int words_per_pixel = bpp / endian_size;
143 int num_words = ((w + (1 << xs) - 1) >> xs) * words_per_pixel;
144 // Number of lines on this plane.
145 int h = (1 << dst->fmt.chroma_ys) - (1 << dst->fmt.ys[p]) + 1;
146
147 assert(src->fmt.bytes[p] == bpp);
148
149 for (int y = 0; y < h; y++) {
150 void *s = mp_image_pixel_ptr(src, p, src_x, src_y + y);
151 void *d = mp_image_pixel_ptr(dst, p, dst_x, dst_y + y);
152 switch (endian_size) {
153 case 2:
154 for (int x = 0; x < num_words; x++)
155 ((uint16_t *)d)[x] = av_bswap16(((uint16_t *)s)[x]);
156 break;
157 case 4:
158 for (int x = 0; x < num_words; x++)
159 ((uint32_t *)d)[x] = av_bswap32(((uint32_t *)s)[x]);
160 break;
161 default:
162 assert(0);
163 }
164 }
165 }
166}
167
168// PA = PAck, copy planar input to single packed array
169// UN = UNpack, copy packed input to planar output
170// Naming convention:
171// pa_/un_ prefix to identify conversion direction.
172// Left (LSB, lowest byte address) -> Right (MSB, highest byte address).
173// (This is unusual; MSB to LSB is more commonly used to describe formats,
174// but our convention makes more sense for byte access in little endian.)
175// "c" identifies a color component.
176// "z" identifies known zero padding.
177// "x" identifies uninitialized padding.
178// A component is followed by its size in bits.
179// Size can be omitted for multiple uniform components (c8c8c8 == ccc8).
180// Unpackers will often use "x" for padding, because they ignore it, while
181// packers will use "z" because they write zero.
182
183#define PA_WORD_4(name, packed_t, plane_t, sh_c0, sh_c1, sh_c2, sh_c3) \
184 static void name(void *dst, void *src[], int w) { \
185 for (int x = 0; x < w; x++) { \
186 ((packed_t *)dst)[x] = \
187 ((packed_t)((plane_t *)src[0])[x] << (sh_c0)) | \
188 ((packed_t)((plane_t *)src[1])[x] << (sh_c1)) | \
189 ((packed_t)((plane_t *)src[2])[x] << (sh_c2)) | \
190 ((packed_t)((plane_t *)src[3])[x] << (sh_c3)); \
191 } \
192 }
193
194#define UN_WORD_4(name, packed_t, plane_t, sh_c0, sh_c1, sh_c2, sh_c3, mask)\
195 static void name(void *src, void *dst[], int w) { \
196 for (int x = 0; x < w; x++) { \
197 packed_t c = ((packed_t *)src)[x]; \
198 ((plane_t *)dst[0])[x] = (c >> (sh_c0)) & (mask); \
199 ((plane_t *)dst[1])[x] = (c >> (sh_c1)) & (mask); \
200 ((plane_t *)dst[2])[x] = (c >> (sh_c2)) & (mask); \
201 ((plane_t *)dst[3])[x] = (c >> (sh_c3)) & (mask); \
202 } \
203 }
204
205
206#define PA_WORD_3(name, packed_t, plane_t, sh_c0, sh_c1, sh_c2, pad) \
207 static void name(void *dst, void *src[], int w) { \
208 for (int x = 0; x < w; x++) { \
209 ((packed_t *)dst)[x] = (pad) | \
210 ((packed_t)((plane_t *)src[0])[x] << (sh_c0)) | \
211 ((packed_t)((plane_t *)src[1])[x] << (sh_c1)) | \
212 ((packed_t)((plane_t *)src[2])[x] << (sh_c2)); \
213 } \
214 }
215
216UN_WORD_4(un_cccc8, uint32_t, uint8_t, 0, 8, 16, 24, 0xFFu)
217PA_WORD_4(pa_cccc8, uint32_t, uint8_t, 0, 8, 16, 24)
218// Not sure if this is a good idea; there may be no alignment guarantee.
219UN_WORD_4(un_cccc16, uint64_t, uint16_t, 0, 16, 32, 48, 0xFFFFu)
220PA_WORD_4(pa_cccc16, uint64_t, uint16_t, 0, 16, 32, 48)
221
222#define UN_WORD_3(name, packed_t, plane_t, sh_c0, sh_c1, sh_c2, mask) \
223 static void name(void *src, void *dst[], int w) { \
224 for (int x = 0; x < w; x++) { \
225 packed_t c = ((packed_t *)src)[x]; \
226 ((plane_t *)dst[0])[x] = (c >> (sh_c0)) & (mask); \
227 ((plane_t *)dst[1])[x] = (c >> (sh_c1)) & (mask); \
228 ((plane_t *)dst[2])[x] = (c >> (sh_c2)) & (mask); \
229 } \
230 }
231
232UN_WORD_3(un_ccc8x8, uint32_t, uint8_t, 0, 8, 16, 0xFFu)
233PA_WORD_3(pa_ccc8z8, uint32_t, uint8_t, 0, 8, 16, 0)
234UN_WORD_3(un_x8ccc8, uint32_t, uint8_t, 8, 16, 24, 0xFFu)
235PA_WORD_3(pa_z8ccc8, uint32_t, uint8_t, 8, 16, 24, 0)
236UN_WORD_3(un_ccc10x2, uint32_t, uint16_t, 0, 10, 20, 0x3FFu)
237PA_WORD_3(pa_ccc10z2, uint32_t, uint16_t, 20, 10, 0, 0)
238
239#define PA_WORD_2(name, packed_t, plane_t, sh_c0, sh_c1, pad) \
240 static void name(void *dst, void *src[], int w) { \
241 for (int x = 0; x < w; x++) { \
242 ((packed_t *)dst)[x] = (pad) | \
243 ((packed_t)((plane_t *)src[0])[x] << (sh_c0)) | \
244 ((packed_t)((plane_t *)src[1])[x] << (sh_c1)); \
245 } \
246 }
247
248#define UN_WORD_2(name, packed_t, plane_t, sh_c0, sh_c1, mask) \
249 static void name(void *src, void *dst[], int w) { \
250 for (int x = 0; x < w; x++) { \
251 packed_t c = ((packed_t *)src)[x]; \
252 ((plane_t *)dst[0])[x] = (c >> (sh_c0)) & (mask); \
253 ((plane_t *)dst[1])[x] = (c >> (sh_c1)) & (mask); \
254 } \
255 }
256
257UN_WORD_2(un_cc8, uint16_t, uint8_t, 0, 8, 0xFFu)
258PA_WORD_2(pa_cc8, uint16_t, uint8_t, 0, 8, 0)
259UN_WORD_2(un_cc16, uint32_t, uint16_t, 0, 16, 0xFFFFu)
260PA_WORD_2(pa_cc16, uint32_t, uint16_t, 0, 16, 0)
261
262#define PA_SEQ_3(name, comp_t) \
263 static void name(void *dst, void *src[], int w) { \
264 comp_t *r = dst; \
265 for (int x = 0; x < w; x++) { \
266 *r++ = ((comp_t *)src[0])[x]; \
267 *r++ = ((comp_t *)src[1])[x]; \
268 *r++ = ((comp_t *)src[2])[x]; \
269 } \
270 }
271
272#define UN_SEQ_3(name, comp_t) \
273 static void name(void *src, void *dst[], int w) { \
274 comp_t *r = src; \
275 for (int x = 0; x < w; x++) { \
276 ((comp_t *)dst[0])[x] = *r++; \
277 ((comp_t *)dst[1])[x] = *r++; \
278 ((comp_t *)dst[2])[x] = *r++; \
279 } \
280 }
281
282UN_SEQ_3(un_ccc8, uint8_t)
283PA_SEQ_3(pa_ccc8, uint8_t)
284UN_SEQ_3(un_ccc16, uint16_t)
285PA_SEQ_3(pa_ccc16, uint16_t)
286
287// "regular": single packed plane, all components have same width (except padding)
288struct regular_repacker {
289 int packed_width; // number of bits of the packed pixel
290 int component_width; // number of bits for a single component
291 int prepadding; // number of bits of LSB padding
292 int num_components; // number of components that can be accessed
293 void (*pa_scanline)(void *a, void *b[], int w);
294 void (*un_scanline)(void *a, void *b[], int w);
295};
296
297static const struct regular_repacker regular_repackers[] = {
298 {32, 8, 0, 3, pa_ccc8z8, un_ccc8x8},
299 {32, 8, 8, 3, pa_z8ccc8, un_x8ccc8},
300 {32, 8, 0, 4, pa_cccc8, un_cccc8},
301 {64, 16, 0, 4, pa_cccc16, un_cccc16},
302 {24, 8, 0, 3, pa_ccc8, un_ccc8},
303 {48, 16, 0, 3, pa_ccc16, un_ccc16},
304 {16, 8, 0, 2, pa_cc8, un_cc8},
305 {32, 16, 0, 2, pa_cc16, un_cc16},
306 {32, 10, 0, 3, pa_ccc10z2, un_ccc10x2},
307};
308
309static void packed_repack(struct mp_repack *rp,
310 struct mp_image *a, int a_x, int a_y,
311 struct mp_image *b, int b_x, int b_y, int w)
312{
313 uint32_t *pa = mp_image_pixel_ptr(a, 0, a_x, a_y);
314
315 void *pb[4] = {0};
316 for (int p = 0; p < b->num_planes; p++) {
317 int s = rp->components[p];
318 pb[p] = mp_image_pixel_ptr(b, s, b_x, b_y);
319 }
320
321 rp->packed_repack_scanline(pa, pb, w);
322}
323
324// Tries to set a packer/unpacker for component-wise byte aligned formats.
325static void setup_packed_packer(struct mp_repack *rp)
326{
327 struct mp_regular_imgfmt desc;
328 if (!mp_get_regular_imgfmt(&desc, rp->imgfmt_a))
329 return;
330
331 if (desc.num_planes != 1 || desc.planes[0].num_components < 2)
332 return;
333 struct mp_regular_imgfmt_plane *p = &desc.planes[0];
334
335 int num_real_components = 0;
336 bool has_alpha = false;
337 for (int n = 0; n < p->num_components; n++) {
338 if (p->components[n]) {
339 has_alpha |= p->components[n] == 4;
340 num_real_components += 1;
341 } else {
342 // padding must be in MSB or LSB
343 if (n != 0 && n != p->num_components - 1)
344 return;
345 }
346 }
347
348 int depth = desc.component_size * 8 + MPMIN(0, desc.component_pad);
349
350 static const int reorder_gbrp[] = {0, 3, 1, 2, 4};
351 static const int reorder_yuv[] = {0, 1, 2, 3, 4};
352 int planar_fmt = 0;
353 const int *reorder = NULL;
354 if (desc.forced_csp) {
355 if (desc.forced_csp != MP_CSP_RGB && desc.forced_csp != MP_CSP_XYZ)
356 return;
357 planar_fmt = find_gbrp_format(depth, num_real_components);
358 reorder = reorder_gbrp;
359 } else {
360 planar_fmt = find_yuv_format(depth, num_real_components);
361 reorder = reorder_yuv;
362 }
363 if (!planar_fmt)
364 return;
365
366 for (int i = 0; i < MP_ARRAY_SIZE(regular_repackers); i++) {
367 const struct regular_repacker *pa = &regular_repackers[i];
368
369 // The following may assume little endian (because some repack backends
370 // use word access, while the metadata here uses byte access).
371
372 int prepad = p->components[0] ? 0 : 8;
373 int first_comp = p->components[0] ? 0 : 1;
374 void (*repack_cb)(void *pa, void *pb[], int w) =
375 rp->pack ? pa->pa_scanline : pa->un_scanline;
376
377 if (pa->packed_width != desc.component_size * p->num_components * 8 ||
378 pa->component_width != depth ||
379 pa->num_components != num_real_components ||
380 pa->prepadding != prepad ||
381 !repack_cb)
382 continue;
383
384 rp->repack = packed_repack;
385 rp->packed_repack_scanline = repack_cb;
386 rp->imgfmt_b = planar_fmt;
387 for (int n = 0; n < num_real_components; n++) {
388 // Determine permutation that maps component order between the two
389 // formats, with has_alpha special case (see above).
390 int c = reorder[p->components[first_comp + n]];
391 rp->components[n] = c == 4 ? num_real_components - 1 : c - 1;
392 }
393 return;
394 }
395}
396
397struct fringe_rgb_repacker {
398 // To avoid making a mess of IMGFMT_*, we use av formats directly.
399 enum AVPixelFormat avfmt;
400 // If true, use BGR instead of RGB.
401 // False: LSB - R - G - B - pad - MSB
402 // True: LSB - B - G - R - pad - MSB
403 bool rev_order;
404 // Size in bit for each component, strictly from LSB to MSB.
405 int bits[3];
406 bool be;
407};
408
409static const struct fringe_rgb_repacker fringe_rgb_repackers[] = {
410 {AV_PIX_FMT_BGR4_BYTE, false, {1, 2, 1}},
411 {AV_PIX_FMT_RGB4_BYTE, true, {1, 2, 1}},
412 {AV_PIX_FMT_BGR8, false, {3, 3, 2}},
413 {AV_PIX_FMT_RGB8, true, {2, 3, 3}}, // pixdesc desc. and doc. bug?
414 {AV_PIX_FMT_RGB444LE, true, {4, 4, 4}},
415 {AV_PIX_FMT_RGB444BE, true, {4, 4, 4}, .be = true},
416 {AV_PIX_FMT_BGR444LE, false, {4, 4, 4}},
417 {AV_PIX_FMT_BGR444BE, false, {4, 4, 4}, .be = true},
418 {AV_PIX_FMT_BGR565LE, false, {5, 6, 5}},
419 {AV_PIX_FMT_BGR565BE, false, {5, 6, 5}, .be = true},
420 {AV_PIX_FMT_RGB565LE, true, {5, 6, 5}},
421 {AV_PIX_FMT_RGB565BE, true, {5, 6, 5}, .be = true},
422 {AV_PIX_FMT_BGR555LE, false, {5, 5, 5}},
423 {AV_PIX_FMT_BGR555BE, false, {5, 5, 5}, .be = true},
424 {AV_PIX_FMT_RGB555LE, true, {5, 5, 5}},
425 {AV_PIX_FMT_RGB555BE, true, {5, 5, 5}, .be = true},
426};
427
428#define PA_SHIFT_LUT8(name, packed_t) \
429 static void name(void *dst, void *src[], int w, uint8_t *lut, \
430 uint8_t s0, uint8_t s1, uint8_t s2) { \
431 for (int x = 0; x < w; x++) { \
432 ((packed_t *)dst)[x] = \
433 (lut[((uint8_t *)src[0])[x] + 256 * 0] << s0) | \
434 (lut[((uint8_t *)src[1])[x] + 256 * 1] << s1) | \
435 (lut[((uint8_t *)src[2])[x] + 256 * 2] << s2); \
436 } \
437 }
438
439
440#define UN_SHIFT_LUT8(name, packed_t) \
441 static void name(void *src, void *dst[], int w, uint8_t *lut, \
442 uint8_t s0, uint8_t s1, uint8_t s2) { \
443 for (int x = 0; x < w; x++) { \
444 packed_t c = ((packed_t *)src)[x]; \
445 ((uint8_t *)dst[0])[x] = lut[((c >> s0) & 0xFF) + 256 * 0]; \
446 ((uint8_t *)dst[1])[x] = lut[((c >> s1) & 0xFF) + 256 * 1]; \
447 ((uint8_t *)dst[2])[x] = lut[((c >> s2) & 0xFF) + 256 * 2]; \
448 } \
449 }
450
451PA_SHIFT_LUT8(pa_shift_lut8_8, uint8_t)
452PA_SHIFT_LUT8(pa_shift_lut8_16, uint16_t)
453UN_SHIFT_LUT8(un_shift_lut8_8, uint8_t)
454UN_SHIFT_LUT8(un_shift_lut8_16, uint16_t)
455
456static void fringe_rgb_repack(struct mp_repack *rp,
457 struct mp_image *a, int a_x, int a_y,
458 struct mp_image *b, int b_x, int b_y, int w)
459{
460 void *pa = mp_image_pixel_ptr(a, 0, a_x, a_y);
461
462 void *pb[4] = {0};
463 for (int p = 0; p < b->num_planes; p++) {
464 int s = rp->components[p];
465 pb[p] = mp_image_pixel_ptr(b, s, b_x, b_y);
466 }
467
468 assert(rp->comp_size == 1 || rp->comp_size == 2);
469
470 void (*repack)(void *pa, void *pb[], int w, uint8_t *lut,
471 uint8_t s0, uint8_t s1, uint8_t s2) = NULL;
472 if (rp->pack) {
473 repack = rp->comp_size == 1 ? pa_shift_lut8_8 : pa_shift_lut8_16;
474 } else {
475 repack = rp->comp_size == 1 ? un_shift_lut8_8 : un_shift_lut8_16;
476 }
477 repack(pa, pb, w, rp->comp_lut,
478 rp->comp_shifts[0], rp->comp_shifts[1], rp->comp_shifts[2]);
479}
480
481static void setup_fringe_rgb_packer(struct mp_repack *rp)
482{
483 enum AVPixelFormat avfmt = imgfmt2pixfmt(rp->imgfmt_a);
484
485 const struct fringe_rgb_repacker *fmt = NULL;
486 for (int n = 0; n < MP_ARRAY_SIZE(fringe_rgb_repackers); n++) {
487 if (fringe_rgb_repackers[n].avfmt == avfmt) {
488 fmt = &fringe_rgb_repackers[n];
489 break;
490 }
491 }
492
493 if (!fmt)
494 return;
495
496 int depth = fmt->bits[0];
497 for (int n = 0; n < 3; n++) {
498 if (rp->flags & REPACK_CREATE_ROUND_DOWN) {
499 depth = MPMIN(depth, fmt->bits[n]);
500 } else {
501 depth = MPMAX(depth, fmt->bits[n]);
502 }
503 }
504 if (rp->flags & REPACK_CREATE_EXPAND_8BIT)
505 depth = 8;
506
507 rp->imgfmt_b = find_gbrp_format(depth, 3);
508 if (!rp->imgfmt_b)
509 return;
510 rp->comp_lut = talloc_array(rp, uint8_t, 256 * 3);
511 rp->repack = fringe_rgb_repack;
512 static const int c_order_rgb[] = {3, 1, 2};
513 static const int c_order_bgr[] = {2, 1, 3};
514 for (int n = 0; n < 3; n++)
515 rp->components[n] = (fmt->rev_order ? c_order_bgr : c_order_rgb)[n] - 1;
516
517 int bitpos = 0;
518 for (int n = 0; n < 3; n++) {
519 int bits = fmt->bits[n];
520 rp->comp_shifts[n] = bitpos;
521 if (rp->comp_lut) {
522 uint8_t *lut = rp->comp_lut + 256 * n;
523 uint8_t zmax = (1 << depth) - 1;
524 uint8_t cmax = (1 << bits) - 1;
525 for (int v = 0; v < 256; v++) {
526 if (rp->pack) {
527 lut[v] = (v * cmax + zmax / 2) / zmax;
528 } else {
529 lut[v] = (v & cmax) * zmax / cmax;
530 }
531 }
532 }
533 bitpos += bits;
534 }
535
536 rp->comp_size = (bitpos + 7) / 8;
537 assert(rp->comp_size == 1 || rp->comp_size == 2);
538
539 if (fmt->be) {
540 assert(rp->comp_size == 2);
541 rp->endian_size = 2;
542 }
543}
544
545static void unpack_pal(struct mp_repack *rp,
546 struct mp_image *a, int a_x, int a_y,
547 struct mp_image *b, int b_x, int b_y, int w)
548{
549 uint8_t *src = mp_image_pixel_ptr(a, 0, a_x, a_y);
550 uint32_t *pal = (void *)a->planes[1];
551
552 uint8_t *dst[4] = {0};
553 for (int p = 0; p < b->num_planes; p++)
554 dst[p] = mp_image_pixel_ptr(b, p, b_x, b_y);
555
556 for (int x = 0; x < w; x++) {
557 uint32_t c = pal[src[x]];
558 dst[0][x] = (c >> 8) & 0xFF; // G
559 dst[1][x] = (c >> 0) & 0xFF; // B
560 dst[2][x] = (c >> 16) & 0xFF; // R
561 dst[3][x] = (c >> 24) & 0xFF; // A
562 }
563}
564
565static void bitmap_repack(struct mp_repack *rp,
566 struct mp_image *a, int a_x, int a_y,
567 struct mp_image *b, int b_x, int b_y, int w)
568{
569 uint8_t *pa = mp_image_pixel_ptr(a, 0, a_x, a_y);
570 uint8_t *pb = mp_image_pixel_ptr(b, 0, b_x, b_y);
571
572 if (rp->pack) {
573 for (unsigned x = 0; x < w; x += 8) {
574 uint8_t d = 0;
575 int max_b = MPMIN(8, w - x);
576 for (int bp = 0; bp < max_b; bp++)
577 d |= (rp->comp_lut[pb[x + bp]]) << (7 - bp);
578 pa[x / 8] = d;
579 }
580 } else {
581 for (unsigned x = 0; x < w; x += 8) {
582 uint8_t d = pa[x / 8];
583 int max_b = MPMIN(8, w - x);
584 for (int bp = 0; bp < max_b; bp++)
585 pb[x + bp] = rp->comp_lut[d & (1 << (7 - bp))];
586 }
587 }
588}
589
590static void setup_misc_packer(struct mp_repack *rp)
591{
592 // Although it's in regular_repackers[], the generic mpv imgfmt metadata
593 // can't handle it yet.
594 if (rp->imgfmt_a == IMGFMT_RGB30) {
595 int planar_fmt = find_gbrp_format(10, 3);
596 if (!planar_fmt)
597 return;
598 rp->imgfmt_b = planar_fmt;
599 rp->repack = packed_repack;
600 rp->packed_repack_scanline = rp->pack ? pa_ccc10z2 : un_ccc10x2;
601 static int c_order[] = {3, 2, 1};
602 for (int n = 0; n < 3; n++)
603 rp->components[n] = c_order[n] - 1;
604 } else if (rp->imgfmt_a == IMGFMT_PAL8 && !rp->pack) {
605 int grap_fmt = find_gbrp_format(8, 4);
606 if (!grap_fmt)
607 return;
608 rp->imgfmt_b = grap_fmt;
609 rp->repack = unpack_pal;
610 } else {
611 enum AVPixelFormat avfmt = imgfmt2pixfmt(rp->imgfmt_a);
612 if (avfmt == AV_PIX_FMT_MONOWHITE || avfmt == AV_PIX_FMT_MONOBLACK) {
613 rp->comp_lut = talloc_array(rp, uint8_t, 256);
614 rp->imgfmt_b = IMGFMT_Y1;
615 int max = 1;
616 if (rp->flags & REPACK_CREATE_EXPAND_8BIT) {
617 rp->imgfmt_b = IMGFMT_Y8;
618 max = 255;
619 }
620 bool inv = avfmt == AV_PIX_FMT_MONOWHITE;
621 for (int n = 0; n < 256; n++) {
622 rp->comp_lut[n] = rp->pack ? (inv ^ (n >= (max + 1) / 2))
623 : ((inv ^ !!n) ? max : 0);
624 }
625 rp->repack = bitmap_repack;
626 return;
627 }
628 }
629}
630
631struct fringe_yuv422_repacker {
632 // To avoid making a mess of IMGFMT_*, we use av formats directly.
633 enum AVPixelFormat avfmt;
634 // In bits (depth/8 rounded up gives byte size)
635 int8_t depth;
636 // Word index of each sample: {y0, y1, cb, cr}
637 uint8_t comp[4];
638 bool be;
639};
640
641static const struct fringe_yuv422_repacker fringe_yuv422_repackers[] = {
642 {AV_PIX_FMT_YUYV422, 8, {0, 2, 1, 3}},
643 {AV_PIX_FMT_UYVY422, 8, {1, 3, 0, 2}},
644 {AV_PIX_FMT_YVYU422, 8, {0, 2, 3, 1}},
645#ifdef AV_PIX_FMT_Y210
646 {AV_PIX_FMT_Y210LE, 10, {0, 2, 1, 3}},
647 {AV_PIX_FMT_Y210BE, 10, {0, 2, 1, 3}, .be = true},
648#endif
649};
650
651#define PA_P422(name, comp_t) \
652 static void name(void *dst, void *src[], int w, uint8_t *c) { \
653 for (int x = 0; x < w; x += 2) { \
654 ((comp_t *)dst)[x * 2 + c[0]] = ((comp_t *)src[0])[x + 0]; \
655 ((comp_t *)dst)[x * 2 + c[1]] = ((comp_t *)src[0])[x + 1]; \
656 ((comp_t *)dst)[x * 2 + c[2]] = ((comp_t *)src[1])[x >> 1]; \
657 ((comp_t *)dst)[x * 2 + c[3]] = ((comp_t *)src[2])[x >> 1]; \
658 } \
659 }
660
661
662#define UN_P422(name, comp_t) \
663 static void name(void *src, void *dst[], int w, uint8_t *c) { \
664 for (int x = 0; x < w; x += 2) { \
665 ((comp_t *)dst[0])[x + 0] = ((comp_t *)src)[x * 2 + c[0]]; \
666 ((comp_t *)dst[0])[x + 1] = ((comp_t *)src)[x * 2 + c[1]]; \
667 ((comp_t *)dst[1])[x >> 1] = ((comp_t *)src)[x * 2 + c[2]]; \
668 ((comp_t *)dst[2])[x >> 1] = ((comp_t *)src)[x * 2 + c[3]]; \
669 } \
670 }
671
672PA_P422(pa_p422_8, uint8_t)
673PA_P422(pa_p422_16, uint16_t)
674UN_P422(un_p422_8, uint8_t)
675UN_P422(un_p422_16, uint16_t)
676
677static void fringe_yuv422_repack(struct mp_repack *rp,
678 struct mp_image *a, int a_x, int a_y,
679 struct mp_image *b, int b_x, int b_y, int w)
680{
681 void *pa = mp_image_pixel_ptr(a, 0, a_x, a_y);
682
683 void *pb[4] = {0};
684 for (int p = 0; p < b->num_planes; p++)
685 pb[p] = mp_image_pixel_ptr(b, p, b_x, b_y);
686
687 assert(rp->comp_size == 1 || rp->comp_size == 2);
688
689 void (*repack)(void *a, void *b[], int w, uint8_t *c) = NULL;
690 if (rp->pack) {
691 repack = rp->comp_size == 1 ? pa_p422_8 : pa_p422_16;
692 } else {
693 repack = rp->comp_size == 1 ? un_p422_8 : un_p422_16;
694 }
695 repack(pa, pb, w, rp->comp_map);
696}
697
698static void setup_fringe_yuv422_packer(struct mp_repack *rp)
699{
700 enum AVPixelFormat avfmt = imgfmt2pixfmt(rp->imgfmt_a);
701
702 const struct fringe_yuv422_repacker *fmt = NULL;
703 for (int n = 0; n < MP_ARRAY_SIZE(fringe_yuv422_repackers); n++) {
704 if (fringe_yuv422_repackers[n].avfmt == avfmt) {
705 fmt = &fringe_yuv422_repackers[n];
706 break;
707 }
708 }
709
710 if (!fmt)
711 return;
712
713 rp->comp_size = (fmt->depth + 7) / 8;
714 assert(rp->comp_size == 1 || rp->comp_size == 2);
715
716 struct mp_regular_imgfmt yuvfmt = {
717 .component_type = MP_COMPONENT_TYPE_UINT,
718 // NB: same problem with P010 and not clearing padding.
719 .component_size = rp->comp_size,
720 .num_planes = 3,
721 .planes = { {1, {1}}, {1, {2}}, {1, {3}} },
722 .chroma_xs = 1,
723 .chroma_ys = 0,
724 };
725 rp->imgfmt_b = mp_find_regular_imgfmt(&yuvfmt);
726 rp->repack = fringe_yuv422_repack;
727 rp->comp_map = (uint8_t *)fmt->comp;
728
729 if (fmt->be) {
730 assert(rp->comp_size == 2);
731 rp->endian_size = 2;
732 }
733}
734
735static void repack_nv(struct mp_repack *rp,
736 struct mp_image *a, int a_x, int a_y,
737 struct mp_image *b, int b_x, int b_y, int w)
738{
739 int xs = a->fmt.chroma_xs;
740
741 uint32_t *pa = mp_image_pixel_ptr(a, 1, a_x, a_y);
742
743 void *pb[2];
744 for (int p = 0; p < 2; p++) {
745 int s = rp->components[p];
746 pb[p] = mp_image_pixel_ptr(b, s, b_x, b_y);
747 }
748
749 rp->packed_repack_scanline(pa, pb, (w + (1 << xs) - 1) >> xs);
750}
751
752static void setup_nv_packer(struct mp_repack *rp)
753{
754 struct mp_regular_imgfmt desc;
755 if (!mp_get_regular_imgfmt(&desc, rp->imgfmt_a))
756 return;
757
758 // Check for NV.
759 if (desc.num_planes != 2)
760 return;
761 if (desc.planes[0].num_components != 1 || desc.planes[0].components[0] != 1)
762 return;
763 if (desc.planes[1].num_components != 2)
764 return;
765 int cr0 = desc.planes[1].components[0];
766 int cr1 = desc.planes[1].components[1];
767 if (cr0 > cr1)
768 MPSWAP(int, cr0, cr1);
769 if (cr0 != 2 || cr1 != 3)
770 return;
771
772 // Construct equivalent planar format.
773 struct mp_regular_imgfmt desc2 = desc;
774 desc2.num_planes = 3;
775 desc2.planes[1].num_components = 1;
776 desc2.planes[1].components[0] = 2;
777 desc2.planes[2].num_components = 1;
778 desc2.planes[2].components[0] = 3;
779 // For P010. Strangely this concept exists only for the NV format.
780 if (desc2.component_pad > 0)
781 desc2.component_pad = 0;
782
783 int planar_fmt = mp_find_regular_imgfmt(&desc2);
784 if (!planar_fmt)
785 return;
786
787 for (int i = 0; i < MP_ARRAY_SIZE(regular_repackers); i++) {
788 const struct regular_repacker *pa = &regular_repackers[i];
789
790 void (*repack_cb)(void *pa, void *pb[], int w) =
791 rp->pack ? pa->pa_scanline : pa->un_scanline;
792
793 if (pa->packed_width != desc.component_size * 2 * 8 ||
794 pa->component_width != desc.component_size * 8 ||
795 pa->num_components != 2 ||
796 pa->prepadding != 0 ||
797 !repack_cb)
798 continue;
799
800 rp->repack = repack_nv;
801 rp->passthrough_y = true;
802 rp->packed_repack_scanline = repack_cb;
803 rp->imgfmt_b = planar_fmt;
804 rp->components[0] = desc.planes[1].components[0] - 1;
805 rp->components[1] = desc.planes[1].components[1] - 1;
806 return;
807 }
808}
809
810void repack_line(struct mp_repack *rp, int dst_x, int dst_y,
811 int src_x, int src_y, int w)
812{
813 assert(rp->configured);
814
815 struct repack_step *first = &rp->steps[0];
816 struct repack_step *last = &rp->steps[rp->num_steps - 1];
817
818 assert(dst_x >= 0 && dst_y >= 0 && src_x >= 0 && src_y >= 0 && w >= 0);
819 assert(dst_x + w <= MP_ALIGN_UP(last->buf[1]->w, last->fmt[1].align_x));
820 assert(src_x + w <= MP_ALIGN_UP(first->buf[1]->w, first->fmt[0].align_x));
821 assert(dst_y < last->buf[1]->h);
822 assert(src_y < first->buf[0]->h);
823 assert(!(dst_x & (last->fmt[1].align_x - 1)));
824 assert(!(src_x & (first->fmt[0].align_x - 1)));
825 assert(!(w & ((1 << first->fmt[0].chroma_xs) - 1)));
826 assert(!(dst_y & (last->fmt[1].align_y - 1)));
827 assert(!(src_y & (first->fmt[0].align_y - 1)));
828
829 for (int n = 0; n < rp->num_steps; n++) {
830 struct repack_step *rs = &rp->steps[n];
831
832 // When writing to temporary buffers, always write to the start (maybe
833 // helps with locality).
834 int sx = rs->user_buf[0] ? src_x : 0;
835 int sy = rs->user_buf[0] ? src_y : 0;
836 int dx = rs->user_buf[1] ? dst_x : 0;
837 int dy = rs->user_buf[1] ? dst_y : 0;
838
839 struct mp_image *buf_a = rs->buf[rp->pack];
840 struct mp_image *buf_b = rs->buf[!rp->pack];
841 int a_x = rp->pack ? dx : sx;
842 int a_y = rp->pack ? dy : sy;
843 int b_x = rp->pack ? sx : dx;
844 int b_y = rp->pack ? sy : dy;
845
846 switch (rs->type) {
847 case REPACK_STEP_REPACK: {
848 if (rp->repack)
849 rp->repack(rp, buf_a, a_x, a_y, buf_b, b_x, b_y, w);
850
851 for (int p = 0; p < rs->fmt[0].num_planes; p++) {
852 if (rp->copy_buf[p])
853 copy_plane(rs->buf[1], dx, dy, rs->buf[0], sx, sy, w, p);
854 }
855 break;
856 }
857 case REPACK_STEP_ENDIAN:
858 swap_endian(rs->buf[1], dx, dy, rs->buf[0], sx, sy, w,
859 rp->endian_size);
860 break;
861 }
862 }
863}
864
865static bool setup_format_ne(struct mp_repack *rp)
866{
867 if (!rp->imgfmt_b)
868 setup_nv_packer(rp);
869 if (!rp->imgfmt_b)
870 setup_misc_packer(rp);
871 if (!rp->imgfmt_b)
872 setup_packed_packer(rp);
873 if (!rp->imgfmt_b)
874 setup_fringe_rgb_packer(rp);
875 if (!rp->imgfmt_b)
876 setup_fringe_yuv422_packer(rp);
877 if (!rp->imgfmt_b)
878 rp->imgfmt_b = rp->imgfmt_a; // maybe it was planar after all
879
880 struct mp_regular_imgfmt desc;
881 if (!mp_get_regular_imgfmt(&desc, rp->imgfmt_b))
882 return false;
883
884 // no weird stuff
885 if (desc.num_planes > 4)
886 return false;
887
888 // Endian swapping.
889 if (rp->imgfmt_a != rp->imgfmt_user) {
890 struct mp_regular_imgfmt ndesc;
891 if (!mp_get_regular_imgfmt(&ndesc, rp->imgfmt_a) || ndesc.num_planes > 4)
892 return false;
893 rp->endian_size = ndesc.component_size;
894 if (rp->endian_size != 2 && rp->endian_size != 4)
895 return false;
896 }
897
898 // Accept only true planar formats (with known components and no padding).
899 for (int n = 0; n < desc.num_planes; n++) {
900 if (desc.planes[n].num_components != 1)
901 return false;
902 int c = desc.planes[n].components[0];
903 if (c < 1 || c > 4)
904 return false;
905 }
906
907 rp->fmt_a = mp_imgfmt_get_desc(rp->imgfmt_a);
908 rp->fmt_b = mp_imgfmt_get_desc(rp->imgfmt_b);
909
910 // This is if we did a pack step.
911
912 rp->steps[rp->num_steps++] = (struct repack_step) {
913 .type = REPACK_STEP_REPACK,
914 .fmt = { rp->fmt_b, rp->fmt_a },
915 };
916
917 if (rp->endian_size) {
918 rp->steps[rp->num_steps++] = (struct repack_step) {
919 .type = REPACK_STEP_ENDIAN,
920 .fmt = {
921 rp->fmt_a,
922 mp_imgfmt_get_desc(rp->imgfmt_user),
923 },
924 };
925 }
926
927 // Reverse if unpack (to reflect actual data flow)
928 if (!rp->pack) {
929 for (int n = 0; n < rp->num_steps / 2; n++) {
930 MPSWAP(struct repack_step, rp->steps[n],
931 rp->steps[rp->num_steps - 1 - n]);
932 }
933 for (int n = 0; n < rp->num_steps; n++) {
934 struct repack_step *rs = &rp->steps[n];
935 MPSWAP(struct mp_imgfmt_desc, rs->fmt[0], rs->fmt[1]);
936 }
937 }
938
939 for (int n = 0; n < rp->num_steps - 1; n++)
940 assert(rp->steps[n].fmt[1].id == rp->steps[n + 1].fmt[0].id);
941
942 return true;
943}
944
945static void reset_params(struct mp_repack *rp)
946{
947 rp->num_steps = 0;
948 rp->imgfmt_b = 0;
949 rp->repack = NULL;
950 rp->passthrough_y = false;
951 rp->endian_size = 0;
952 rp->packed_repack_scanline = NULL;
953 rp->comp_size = 0;
954 rp->comp_map = NULL;
955 talloc_free(rp->comp_lut);
956 rp->comp_lut = NULL;
957}
958
959static bool setup_format(struct mp_repack *rp)
960{
961 reset_params(rp);
962 rp->imgfmt_a = rp->imgfmt_user;
963 if (setup_format_ne(rp))
964 return true;
965 // Try reverse endian.
966 reset_params(rp);
967 rp->imgfmt_a = mp_find_other_endian(rp->imgfmt_user);
968 return rp->imgfmt_a && setup_format_ne(rp);
969}
970
971struct mp_repack *mp_repack_create_planar(int imgfmt, bool pack, int flags)
972{
973 struct mp_repack *rp = talloc_zero(NULL, struct mp_repack);
974 rp->imgfmt_user = imgfmt;
975 rp->pack = pack;
976 rp->flags = flags;
977
978 if (!setup_format(rp)) {
979 talloc_free(rp);
980 return NULL;
981 }
982
983 return rp;
984}
985
986int mp_repack_get_format_src(struct mp_repack *rp)
987{
988 return rp->steps[0].fmt[0].id;
989}
990
991int mp_repack_get_format_dst(struct mp_repack *rp)
992{
993 return rp->steps[rp->num_steps - 1].fmt[1].id;
994}
995
996int mp_repack_get_align_x(struct mp_repack *rp)
997{
998 // We really want the LCM between those, but since only one of them is
999 // packed (or they're the same format), and the chroma subsampling is the
1000 // same for both, only the packed one matters.
1001 return rp->fmt_a.align_x;
1002}
1003
1004int mp_repack_get_align_y(struct mp_repack *rp)
1005{
1006 return rp->fmt_a.align_y; // should be the same for packed/planar formats
1007}
1008
1009static void image_realloc(struct mp_image **img, int fmt, int w, int h)
1010{
1011 if (*img && (*img)->imgfmt == fmt && (*img)->w == w && (*img)->h == h)
1012 return;
1013 talloc_free(*img);
1014 *img = mp_image_alloc(fmt, w, h);
1015}
1016
1017bool repack_config_buffers(struct mp_repack *rp,
1018 int dst_flags, struct mp_image *dst,
1019 int src_flags, struct mp_image *src,
1020 bool *enable_passthrough)
1021{
1022 struct repack_step *rs_first = &rp->steps[0];
1023 struct repack_step *rs_last = &rp->steps[rp->num_steps - 1];
1024
1025 rp->configured = false;
1026
1027 assert(dst && src);
1028
1029 int buf_w = MPMAX(dst->w, src->w);
1030
1031 assert(dst->imgfmt == rs_last->fmt[1].id);
1032 assert(src->imgfmt == rs_first->fmt[0].id);
1033
1034 // Chain/allocate buffers.
1035
1036 for (int n = 0; n < rp->num_steps; n++)
1037 rp->steps[n].buf[0] = rp->steps[n].buf[1] = NULL;
1038
1039 rs_first->buf[0] = src;
1040 rs_last->buf[1] = dst;
1041
1042 for (int n = 0; n < rp->num_steps; n++) {
1043 struct repack_step *rs = &rp->steps[n];
1044
1045 if (!rs->buf[0]) {
1046 assert(n > 0);
1047 rs->buf[0] = rp->steps[n - 1].buf[1];
1048 }
1049
1050 if (rs->buf[1])
1051 continue;
1052
1053 // Note: since repack_line() can have different src/dst offsets, we
1054 // can't do true in-place in general.
1055 bool can_inplace = rs->type == REPACK_STEP_ENDIAN &&
1056 rs->buf[0] != src && rs->buf[0] != dst;
1057 if (can_inplace) {
1058 rs->buf[1] = rs->buf[0];
1059 continue;
1060 }
1061
1062 if (rs != rs_last) {
1063 struct repack_step *next = &rp->steps[n + 1];
1064 if (next->buf[0]) {
1065 rs->buf[1] = next->buf[0];
1066 continue;
1067 }
1068 }
1069
1070 image_realloc(&rs->tmp, rs->fmt[1].id, buf_w, rs->fmt[1].align_y);
1071 if (!rs->tmp)
1072 return false;
1073 talloc_steal(rp, rs->tmp);
1074 rs->buf[1] = rs->tmp;
1075 }
1076
1077 for (int n = 0; n < rp->num_steps; n++) {
1078 struct repack_step *rs = &rp->steps[n];
1079 rs->user_buf[0] = rs->buf[0] == src || rs->buf[0] == dst;
1080 rs->user_buf[1] = rs->buf[1] == src || rs->buf[1] == dst;
1081 }
1082
1083 // If repacking is the only operation. It's also responsible for simply
1084 // copying src to dst if absolutely no filtering is done.
1085 bool may_passthrough =
1086 rp->num_steps == 1 && rp->steps[0].type == REPACK_STEP_REPACK;
1087
1088 for (int p = 0; p < rp->fmt_b.num_planes; p++) {
1089 // (All repack callbacks copy, except nv12 does not copy luma.)
1090 bool repack_copies_plane = rp->repack && !(rp->passthrough_y && p == 0);
1091
1092 bool can_pt = may_passthrough && !repack_copies_plane &&
1093 enable_passthrough && enable_passthrough[p];
1094
1095 // Copy if needed, unless the repack callback does it anyway.
1096 rp->copy_buf[p] = !repack_copies_plane && !can_pt;
1097
1098 if (enable_passthrough)
1099 enable_passthrough[p] = can_pt && !rp->copy_buf[p];
1100 }
1101
1102 if (enable_passthrough) {
1103 for (int n = rp->fmt_b.num_planes; n < MP_MAX_PLANES; n++)
1104 enable_passthrough[n] = false;
1105 }
1106
1107 rp->configured = true;
1108
1109 return true;
1110}
video/repack.h+72−0
@@ -0,0 +1,72 @@
1#pragma once
2
3#include <stdbool.h>
4
5enum {
6 // This controls bheavior with different bit widths per component (like
7 // RGB565). If ROUND_DOWN is specified, the planar format will use the min.
8 // bit width of all components, otherwise the transformation is lossless.
9 REPACK_CREATE_ROUND_DOWN = (1 << 0),
10
11 // Expand some (not all) low bit depth fringe formats to 8 bit on unpack.
12 REPACK_CREATE_EXPAND_8BIT = (1 << 1),
13};
14
15struct mp_repack;
16struct mp_image;
17
18// Create a repacker between any format (imgfmt parameter) and an equivalent
19// planar format (that is native endian). If pack==true, imgfmt is the output,
20// otherwise it is the input. The respective other input/output is the planar
21// format. The planar format can be queried with mp_repack_get_format_*().
22// Note that some formats may change the "implied" colorspace (for example,
23// packed xyz unpacks as rgb).
24// If imgfmt is already planar, a passthrough repacker may be created.
25// imgfmt: src or dst format (usually packed, non-planar, etc.)
26// pack: true if imgfmt is dst, false if imgfmt is src
27// flags: any of REPACK_CREATE_* flags
28// returns: NULL on failure, otherwise free with talloc_free().
29struct mp_repack *mp_repack_create_planar(int imgfmt, bool pack, int flags);
30
31// Return input and output formats for which rp was created.
32int mp_repack_get_format_src(struct mp_repack *rp);
33int mp_repack_get_format_dst(struct mp_repack *rp);
34
35// Return pixel alignment. For x, this is a lowest pixel count at which there is
36// a byte boundary and a full chroma pixel (horizontal subsampling) on src/dst.
37// For y, this is the pixel height of the vertical subsampling.
38// Always returns a power of 2.
39int mp_repack_get_align_x(struct mp_repack *rp);
40int mp_repack_get_align_y(struct mp_repack *rp);
41
42// Repack a single line from dst to src, as set in repack_config_buffers().
43// For subsampled chroma formats, this copies as many luma/alpha rows as needed
44// for a complete line (e.g. 2 luma lines, 1 chroma line for 4:2:0).
45// dst_x, src_x, y must be aligned to the pixel alignment. w may be unaligned
46// if at the right crop-border of the image, but must be always aligned to
47// horiz. sub-sampling. y is subject to hslice.
48void repack_line(struct mp_repack *rp, int dst_x, int dst_y,
49 int src_x, int src_y, int w);
50
51// Configure with a source and target buffer. The rp instance will keep the
52// mp_image pointers and access them on repack_line() calls. Refcounting is
53// not respected - the caller needs to make sure dst is always writable.
54// The images can have different sizes (as repack_line() lets you use different
55// target coordinates for dst/src).
56// This also allocaters potentially required temporary buffers.
57// dst_flags: REPACK_BUF_* flags for dst
58// dst: where repack_line() writes to
59// src_flags: REPACK_BUF_* flags for src
60// src: where repack_line() reads from
61// enable_passthrough: if non-NULL, an bool array of size MP_MAX_PLANES indexed
62// by plane; a true entry requests disabling copying the
63// plane data to the dst plane. The function will write to
64// this array whether the plane can really be passed through
65// (i.e. will set array entries from true to false if pass-
66// through is not possible). It writes to all MP_MAX_PLANES
67// entries. If NULL, all entries are implicitly false.
68// returns: success (fails on OOM)
69bool repack_config_buffers(struct mp_repack *rp,
70 int dst_flags, struct mp_image *dst,
71 int src_flags, struct mp_image *src,
72 bool *enable_passthrough);
video/zimg.c+60−894
@@ -25,6 +25,7 @@ · 24 unmodified lines
25#include "csputils.h"25#include "csputils.h"
26#include "options/m_config.h"26#include "options/m_config.h"
27#include "options/m_option.h"27#include "options/m_option.h"
28#include "repack.h"
28#include "video/fmt-conversion.h"29#include "video/fmt-conversion.h"
29#include "video/img_format.h"30#include "video/img_format.h"
30#include "zimg.h"31#include "zimg.h"
@@ -83,50 +84,14 @@ struct mp_zimg_repack { · 52 unmodified lines
83 int num_planes; // number of planes involved84 int num_planes; // number of planes involved
84 unsigned zmask[4]; // zmask[mp_index] = zimg mask (using mp index!)85 unsigned zmask[4]; // zmask[mp_index] = zimg mask (using mp index!)
85 int z_planes[4]; // z_planes[zimg_index] = mp_index (or -1)86 int z_planes[4]; // z_planes[zimg_index] = mp_index (or -1)
86 bool pass_through_y; // luma plane optimization for e.g. nv12
87 87
88 // If set, the pack/unpack callback to pass to zimg.88 struct mp_repack *repack; // converting to/from planar
89 // Called with user==mp_zimg_repack.
90 zimg_filter_graph_callback repack;
91
92 // Endian-swap (done before/after actual repacker).
93 int endian_size; // 0=no swapping, 2/4=word byte size to swap
94 int endian_items[4]; // number of words per pixel/plane
95
96 // For packed_repack.
97 int components[4]; // p2[n] = mp_image.planes[components[n]]
98 // pack: p1 is dst, p2 is src
99 // unpack: p1 is src, p2 is dst
100 void (*packed_repack_scanline)(void *p1, void *p2[], int x0, int x1);
101
102 // Fringe RGB/YUV.
103 uint8_t comp_size;
104 uint8_t *comp_map;
105 uint8_t comp_shifts[3];
106 uint8_t *comp_lut; // 256 * 3
107 89
108 // Temporary memory for slice-wise repacking. This may be set even if repack90 // Temporary memory for slice-wise repacking. This may be set even if repack
109 // is not set (then it may be used to avoid alignment issues). This has91 // is not set (then it may be used to avoid alignment issues). This has
110 // about one slice worth of data.92 // about one slice worth of data.
111 struct mp_image *tmp;93 struct mp_image *tmp;
112 94
113 // Temporary memory for endian swapping. This has about one slice worth
114 // of data; set and used only if endian swapping is used (endian_size>0).
115 // It's also used only for pack==false; packers do this in-place.
116 struct mp_image *tmp_endian;
117
118 // Temporary, per-call source/target frame.
119 struct mp_image *mpi;
120 // Y coordinate of first line in mpi; usually 0 if mpi==user_mpi, or the
121 // start of the current slice (in the current repack cb).
122 // repackers should use: mpi->data[p] + mpi->stride[p] * (i - mpi_y0)
123 int mpi_y0;
124
125 struct mp_image *user_mpi;
126
127 // Also temporary, per-call. use_buf[n] == plane n uses tmp (and not mpi).
128 bool use_buf[4];
129
130 int real_w, real_h; // aligned size95 int real_w, real_h; // aligned size
131};96};
132 97
@@ -243,532 +208,44 @@ void mp_zimg_enable_cmdline_opts(struct mp_zimg_context *ctx, · 110 unmodified lines
243 mp_zimg_update_from_cmdline(ctx); // first update208 mp_zimg_update_from_cmdline(ctx); // first update
244}209}
245 210
246static int repack_align(void *user, unsigned i, unsigned x0, unsigned x1)
247{
248 struct mp_zimg_repack *r = user;
249
250 for (int p = 0; p < r->mpi->fmt.num_planes; p++) {
251 if (!r->use_buf[p])
252 continue;
253
254 int bpp = r->mpi->fmt.bytes[p];
255 int xs = r->mpi->fmt.xs[p];
256 int ys = r->mpi->fmt.ys[p];
257 // Number of lines on this plane.
258 int h = (1 << r->mpi->fmt.chroma_ys) - (1 << ys) + 1;
259
260 for (int y = i; y < i + h; y++) {
261 void *a = r->mpi->planes[p] +
262 r->mpi->stride[p] * (ptrdiff_t)((y - r->mpi_y0) >> ys) +
263 bpp * (x0 >> xs);
264 void *b = r->tmp->planes[p] +
265 r->tmp->stride[p] * (ptrdiff_t)((y >> ys) & r->zmask[p]) +
266 bpp * (x0 >> xs);
267 size_t size = ((x1 - x0) >> xs) * bpp;
268 if (r->pack) {
269 memcpy(a, b, size);
270 } else {
271 memcpy(b, a, size);
272 }
273 }
274 }
275
276 return 0;
277}
278
279// Swap endian for one line.
280static void swap_endian(struct mp_zimg_repack *r, struct mp_image *dst, int dst_y,
281 struct mp_image *src, int src_y, int x0, int x1)
282{
283 for (int p = 0; p < dst->fmt.num_planes; p++) {
284 int xs = dst->fmt.xs[p];
285 int ys = dst->fmt.ys[p];
286 int words_per_pixel = r->endian_items[p];
287 int bpp = words_per_pixel * r->endian_size;
288 // Number of lines on this plane.
289 int h = (1 << dst->fmt.chroma_ys) - (1 << ys) + 1;
290 int num_words = ((x1 - x0) >> xs) * words_per_pixel;
291
292 for (int y = 0; y < h; y++) {
293 void *s = src->planes[p] +
294 src->stride[p] * (ptrdiff_t)((y + src_y) >> ys) +
295 bpp * (x0 >> xs);
296 void *d = dst->planes[p] +
297 dst->stride[p] * (ptrdiff_t)((y + dst_y) >> ys) +
298 bpp * (x0 >> xs);
299 switch (r->endian_size) {
300 case 2:
301 for (int w = 0; w < num_words; w++)
302 ((uint16_t *)d)[w] = av_bswap16(((uint16_t *)s)[w]);
303 break;
304 case 4:
305 for (int w = 0; w < num_words; w++)
306 ((uint32_t *)d)[w] = av_bswap32(((uint32_t *)s)[w]);
307 break;
308 default:
309 assert(0);
310 }
311 }
312 }
313}
314
315// PA = PAck, copy planar input to single packed array
316// UN = UNpack, copy packed input to planar output
317// Naming convention:
318// pa_/un_ prefix to identify conversion direction.
319// Left (LSB, lowest byte address) -> Right (MSB, highest byte address).
320// (This is unusual; MSB to LSB is more commonly used to describe formats,
321// but our convention makes more sense for byte access in little endian.)
322// "c" identifies a color component.
323// "z" identifies known zero padding.
324// "x" identifies uninitialized padding.
325// A component is followed by its size in bits.
326// Size can be omitted for multiple uniform components (c8c8c8 == ccc8).
327// Unpackers will often use "x" for padding, because they ignore it, while
328// packers will use "z" because they write zero.
329
330#define PA_WORD_4(name, packed_t, plane_t, sh_c0, sh_c1, sh_c2, sh_c3) \
331 static void name(void *dst, void *src[], int x0, int x1) { \
332 for (int x = x0; x < x1; x++) { \
333 ((packed_t *)dst)[x] = \
334 ((packed_t)((plane_t *)src[0])[x] << (sh_c0)) | \
335 ((packed_t)((plane_t *)src[1])[x] << (sh_c1)) | \
336 ((packed_t)((plane_t *)src[2])[x] << (sh_c2)) | \
337 ((packed_t)((plane_t *)src[3])[x] << (sh_c3)); \
338 } \
339 }
340
341#define UN_WORD_4(name, packed_t, plane_t, sh_c0, sh_c1, sh_c2, sh_c3, mask)\
342 static void name(void *src, void *dst[], int x0, int x1) { \
343 for (int x = x0; x < x1; x++) { \
344 packed_t c = ((packed_t *)src)[x]; \
345 ((plane_t *)dst[0])[x] = (c >> (sh_c0)) & (mask); \
346 ((plane_t *)dst[1])[x] = (c >> (sh_c1)) & (mask); \
347 ((plane_t *)dst[2])[x] = (c >> (sh_c2)) & (mask); \
348 ((plane_t *)dst[3])[x] = (c >> (sh_c3)) & (mask); \
349 } \
350 }
351
352
353#define PA_WORD_3(name, packed_t, plane_t, sh_c0, sh_c1, sh_c2, pad) \
354 static void name(void *dst, void *src[], int x0, int x1) { \
355 for (int x = x0; x < x1; x++) { \
356 ((packed_t *)dst)[x] = (pad) | \
357 ((packed_t)((plane_t *)src[0])[x] << (sh_c0)) | \
358 ((packed_t)((plane_t *)src[1])[x] << (sh_c1)) | \
359 ((packed_t)((plane_t *)src[2])[x] << (sh_c2)); \
360 } \
361 }
362
363UN_WORD_4(un_cccc8, uint32_t, uint8_t, 0, 8, 16, 24, 0xFFu)
364PA_WORD_4(pa_cccc8, uint32_t, uint8_t, 0, 8, 16, 24)
365// Not sure if this is a good idea; there may be no alignment guarantee.
366UN_WORD_4(un_cccc16, uint64_t, uint16_t, 0, 16, 32, 48, 0xFFFFu)
367PA_WORD_4(pa_cccc16, uint64_t, uint16_t, 0, 16, 32, 48)
368
369#define UN_WORD_3(name, packed_t, plane_t, sh_c0, sh_c1, sh_c2, mask) \
370 static void name(void *src, void *dst[], int x0, int x1) { \
371 for (int x = x0; x < x1; x++) { \
372 packed_t c = ((packed_t *)src)[x]; \
373 ((plane_t *)dst[0])[x] = (c >> (sh_c0)) & (mask); \
374 ((plane_t *)dst[1])[x] = (c >> (sh_c1)) & (mask); \
375 ((plane_t *)dst[2])[x] = (c >> (sh_c2)) & (mask); \
376 } \
377 }
378
379UN_WORD_3(un_ccc8x8, uint32_t, uint8_t, 0, 8, 16, 0xFFu)
380PA_WORD_3(pa_ccc8z8, uint32_t, uint8_t, 0, 8, 16, 0)
381UN_WORD_3(un_x8ccc8, uint32_t, uint8_t, 8, 16, 24, 0xFFu)
382PA_WORD_3(pa_z8ccc8, uint32_t, uint8_t, 8, 16, 24, 0)
383UN_WORD_3(un_ccc10x2, uint32_t, uint16_t, 0, 10, 20, 0x3FFu)
384PA_WORD_3(pa_ccc10z2, uint32_t, uint16_t, 20, 10, 0, 0)
385
386#define PA_WORD_2(name, packed_t, plane_t, sh_c0, sh_c1, pad) \
387 static void name(void *dst, void *src[], int x0, int x1) { \
388 for (int x = x0; x < x1; x++) { \
389 ((packed_t *)dst)[x] = (pad) | \
390 ((packed_t)((plane_t *)src[0])[x] << (sh_c0)) | \
391 ((packed_t)((plane_t *)src[1])[x] << (sh_c1)); \
392 } \
393 }
394
395#define UN_WORD_2(name, packed_t, plane_t, sh_c0, sh_c1, mask) \
396 static void name(void *src, void *dst[], int x0, int x1) { \
397 for (int x = x0; x < x1; x++) { \
398 packed_t c = ((packed_t *)src)[x]; \
399 ((plane_t *)dst[0])[x] = (c >> (sh_c0)) & (mask); \
400 ((plane_t *)dst[1])[x] = (c >> (sh_c1)) & (mask); \
401 } \
402 }
403
404UN_WORD_2(un_cc8, uint16_t, uint8_t, 0, 8, 0xFFu)
405PA_WORD_2(pa_cc8, uint16_t, uint8_t, 0, 8, 0)
406UN_WORD_2(un_cc16, uint32_t, uint16_t, 0, 16, 0xFFFFu)
407PA_WORD_2(pa_cc16, uint32_t, uint16_t, 0, 16, 0)
408
409#define PA_SEQ_3(name, comp_t) \
410 static void name(void *dst, void *src[], int x0, int x1) { \
411 comp_t *r = dst; \
412 for (int x = x0; x < x1; x++) { \
413 *r++ = ((comp_t *)src[0])[x]; \
414 *r++ = ((comp_t *)src[1])[x]; \
415 *r++ = ((comp_t *)src[2])[x]; \
416 } \
417 }
418
419#define UN_SEQ_3(name, comp_t) \
420 static void name(void *src, void *dst[], int x0, int x1) { \
421 comp_t *r = src; \
422 for (int x = x0; x < x1; x++) { \
423 ((comp_t *)dst[0])[x] = *r++; \
424 ((comp_t *)dst[1])[x] = *r++; \
425 ((comp_t *)dst[2])[x] = *r++; \
426 } \
427 }
428
429UN_SEQ_3(un_ccc8, uint8_t)
430PA_SEQ_3(pa_ccc8, uint8_t)
431UN_SEQ_3(un_ccc16, uint16_t)
432PA_SEQ_3(pa_ccc16, uint16_t)
433
434// "regular": single packed plane, all components have same width (except padding)
435struct regular_repacker {
436 int packed_width; // number of bits of the packed pixel
437 int component_width; // number of bits for a single component
438 int prepadding; // number of bits of LSB padding
439 int num_components; // number of components that can be accessed
440 void (*pa_scanline)(void *p1, void *p2[], int x0, int x1);
441 void (*un_scanline)(void *p1, void *p2[], int x0, int x1);
442};
443
444static const struct regular_repacker regular_repackers[] = {
445 {32, 8, 0, 3, pa_ccc8z8, un_ccc8x8},
446 {32, 8, 8, 3, pa_z8ccc8, un_x8ccc8},
447 {32, 8, 0, 4, pa_cccc8, un_cccc8},
448 {64, 16, 0, 4, pa_cccc16, un_cccc16},
449 {24, 8, 0, 3, pa_ccc8, un_ccc8},
450 {48, 16, 0, 3, pa_ccc16, un_ccc16},
451 {16, 8, 0, 2, pa_cc8, un_cc8},
452 {32, 16, 0, 2, pa_cc16, un_cc16},
453 {32, 10, 0, 3, pa_ccc10z2, un_ccc10x2},
454};
455
456static int packed_repack(void *user, unsigned i, unsigned x0, unsigned x1)
457{
458 struct mp_zimg_repack *r = user;
459
460 uint32_t *p1 = (void *)(r->mpi->planes[0] +
461 r->mpi->stride[0] * (ptrdiff_t)(i - r->mpi_y0));
462
463 void *p2[4] = {0};
464 for (int p = 0; p < r->num_planes; p++) {
465 int s = r->components[p];
466 p2[p] = r->tmp->planes[s] +
467 r->tmp->stride[s] * (ptrdiff_t)(i & r->zmask[s]);
468 }
469
470 r->packed_repack_scanline(p1, p2, x0, x1);
471
472 return 0;
473}
474
475struct fringe_rgb_repacker {
476 // To avoid making a mess of IMGFMT_*, we use av formats directly.
477 enum AVPixelFormat avfmt;
478 // If true, use BGR instead of RGB.
479 // False: LSB - R - G - B - pad - MSB
480 // True: LSB - B - G - R - pad - MSB
481 bool rev_order;
482 // Size in bit for each component, strictly from LSB to MSB.
483 int bits[3];
484 bool be;
485};
486
487static const struct fringe_rgb_repacker fringe_rgb_repackers[] = {
488 {AV_PIX_FMT_BGR4_BYTE, false, {1, 2, 1}},
489 {AV_PIX_FMT_RGB4_BYTE, true, {1, 2, 1}},
490 {AV_PIX_FMT_BGR8, false, {3, 3, 2}},
491 {AV_PIX_FMT_RGB8, true, {2, 3, 3}}, // pixdesc desc. and doc. bug?
492 {AV_PIX_FMT_RGB444LE, true, {4, 4, 4}},
493 {AV_PIX_FMT_RGB444BE, true, {4, 4, 4}, .be = true},
494 {AV_PIX_FMT_BGR444LE, false, {4, 4, 4}},
495 {AV_PIX_FMT_BGR444BE, false, {4, 4, 4}, .be = true},
496 {AV_PIX_FMT_BGR565LE, false, {5, 6, 5}},
497 {AV_PIX_FMT_BGR565BE, false, {5, 6, 5}, .be = true},
498 {AV_PIX_FMT_RGB565LE, true, {5, 6, 5}},
499 {AV_PIX_FMT_RGB565BE, true, {5, 6, 5}, .be = true},
500 {AV_PIX_FMT_BGR555LE, false, {5, 5, 5}},
501 {AV_PIX_FMT_BGR555BE, false, {5, 5, 5}, .be = true},
502 {AV_PIX_FMT_RGB555LE, true, {5, 5, 5}},
503 {AV_PIX_FMT_RGB555BE, true, {5, 5, 5}, .be = true},
504};
505
506#define PA_SHIFT_LUT8(name, packed_t) \
507 static void name(void *dst, void *src[], int x0, int x1, uint8_t *lut, \
508 uint8_t s0, uint8_t s1, uint8_t s2) { \
509 for (int x = x0; x < x1; x++) { \
510 ((packed_t *)dst)[x] = \
511 (lut[((uint8_t *)src[0])[x] + 256 * 0] << s0) | \
512 (lut[((uint8_t *)src[1])[x] + 256 * 1] << s1) | \
513 (lut[((uint8_t *)src[2])[x] + 256 * 2] << s2); \
514 } \
515 }
516
517
518#define UN_SHIFT_LUT8(name, packed_t) \
519 static void name(void *src, void *dst[], int x0, int x1, uint8_t *lut, \
520 uint8_t s0, uint8_t s1, uint8_t s2) { \
521 for (int x = x0; x < x1; x++) { \
522 packed_t c = ((packed_t *)src)[x]; \
523 ((uint8_t *)dst[0])[x] = lut[((c >> s0) & 0xFF) + 256 * 0]; \
524 ((uint8_t *)dst[1])[x] = lut[((c >> s1) & 0xFF) + 256 * 1]; \
525 ((uint8_t *)dst[2])[x] = lut[((c >> s2) & 0xFF) + 256 * 2]; \
526 } \
527 }
528
529PA_SHIFT_LUT8(pa_shift_lut8_8, uint8_t)
530PA_SHIFT_LUT8(pa_shift_lut8_16, uint16_t)
531UN_SHIFT_LUT8(un_shift_lut8_8, uint8_t)
532UN_SHIFT_LUT8(un_shift_lut8_16, uint16_t)
533
534static int fringe_rgb_repack(void *user, unsigned i, unsigned x0, unsigned x1)
535{
536 struct mp_zimg_repack *r = user;
537
538 void *p1 = r->mpi->planes[0] + r->mpi->stride[0] * (ptrdiff_t)(i - r->mpi_y0);
539
540 void *p2[4] = {0};
541 for (int p = 0; p < r->num_planes; p++) {
542 int s = r->components[p];
543 p2[p] = r->tmp->planes[s] +
544 r->tmp->stride[s] * (ptrdiff_t)(i & r->zmask[s]);
545 }
546
547 assert(r->comp_size == 1 || r->comp_size == 2);
548
549 void (*repack)(void *p1, void *p2[], int x0, int x1, uint8_t *lut,
550 uint8_t s0, uint8_t s1, uint8_t s2) = NULL;
551 if (r->pack) {
552 repack = r->comp_size == 1 ? pa_shift_lut8_8 : pa_shift_lut8_16;
553 } else {
554 repack = r->comp_size == 1 ? un_shift_lut8_8 : un_shift_lut8_16;
555 }
556 repack(p1, p2, x0, x1, r->comp_lut,
557 r->comp_shifts[0], r->comp_shifts[1], r->comp_shifts[2]);
558
559 return 0;
560}
561
562static int bitmap_repack(void *user, unsigned i, unsigned x0, unsigned x1)
563{
564 struct mp_zimg_repack *r = user;
565
566 uint8_t *p1 =
567 r->mpi->planes[0] + r->mpi->stride[0] * (ptrdiff_t)(i - r->mpi_y0);
568 uint8_t *p2 =
569 r->tmp->planes[0] + r->tmp->stride[0] * (ptrdiff_t)(i & r->zmask[0]);
570
571 uint8_t swap = r->comp_size ? 0xFF : 0;
572 if (r->pack) {
573 // Supposedly zimg aligns this at least on 64 byte boundaries. Simplifies a
574 // lot for us.
575 assert(!(x0 & 7));
576
577 for (int x = x0; x < x1; x += 8) {
578 uint8_t d = 0;
579 int max_b = MPMIN(8, x1 - x);
580 for (int b = 0; b < max_b; b++)
581 d |= (!!p2[x + b]) << (7 - b);
582 p1[x / 8] = d ^ swap;
583 }
584 } else {
585 x0 &= ~0x7;
586
587 for (int x = x0; x < x1; x += 8) {
588 uint8_t d = p1[x / 8] ^ swap;
589 int max_b = MPMIN(8, x1 - x);
590 for (int b = 0; b < max_b; b++)
591 p2[x + b] = !!(d & (1 << (7 - b)));
592 }
593 }
594
595 return 0;
596}
597
598static int unpack_pal(void *user, unsigned i, unsigned x0, unsigned x1)
599{
600 struct mp_zimg_repack *r = user;
601
602 uint8_t *src = (void *)(r->mpi->planes[0] +
603 r->mpi->stride[0] * (ptrdiff_t)(i - r->mpi_y0));
604 uint32_t *pal = (void *)r->mpi->planes[1];
605
606 uint8_t *dst[4] = {0};
607 for (int p = 0; p < r->num_planes; p++) {
608 dst[p] = r->tmp->planes[p] +
609 r->tmp->stride[p] * (ptrdiff_t)(i & r->zmask[p]);
610 }
611
612 for (int x = x0; x < x1; x++) {
613 uint32_t c = pal[src[x]];
614 dst[0][x] = (c >> 8) & 0xFF; // G
615 dst[1][x] = (c >> 0) & 0xFF; // B
616 dst[2][x] = (c >> 16) & 0xFF; // R
617 dst[3][x] = (c >> 24) & 0xFF; // A
618 }
619
620 return 0;
621}
622
623struct fringe_yuv422_repacker {
624 // To avoid making a mess of IMGFMT_*, we use av formats directly.
625 enum AVPixelFormat avfmt;
626 // In bits (depth/8 rounded up gives byte size)
627 int8_t depth;
628 // Word index of each sample: {y0, y1, cb, cr}
629 uint8_t comp[4];
630 bool be;
631};
632
633static const struct fringe_yuv422_repacker fringe_yuv422_repackers[] = {
634 {AV_PIX_FMT_YUYV422, 8, {0, 2, 1, 3}},
635 {AV_PIX_FMT_UYVY422, 8, {1, 3, 0, 2}},
636 {AV_PIX_FMT_YVYU422, 8, {0, 2, 3, 1}},
637#ifdef AV_PIX_FMT_Y210
638 {AV_PIX_FMT_Y210LE, 10, {0, 2, 1, 3}},
639 {AV_PIX_FMT_Y210BE, 10, {0, 2, 1, 3}, .be = true},
640#endif
641};
642
643#define PA_P422(name, comp_t) \
644 static void name(void *dst, void *src[], int x0, int x1, uint8_t *c) { \
645 for (int x = x0; x < x1; x += 2) { \
646 ((comp_t *)dst)[x * 2 + c[0]] = ((comp_t *)src[0])[x + 0]; \
647 ((comp_t *)dst)[x * 2 + c[1]] = ((comp_t *)src[0])[x + 1]; \
648 ((comp_t *)dst)[x * 2 + c[2]] = ((comp_t *)src[1])[x >> 1]; \
649 ((comp_t *)dst)[x * 2 + c[3]] = ((comp_t *)src[2])[x >> 1]; \
650 } \
651 }
652
653
654#define UN_P422(name, comp_t) \
655 static void name(void *src, void *dst[], int x0, int x1, uint8_t *c) { \
656 for (int x = x0; x < x1; x += 2) { \
657 ((comp_t *)dst[0])[x + 0] = ((comp_t *)src)[x * 2 + c[0]]; \
658 ((comp_t *)dst[0])[x + 1] = ((comp_t *)src)[x * 2 + c[1]]; \
659 ((comp_t *)dst[1])[x >> 1] = ((comp_t *)src)[x * 2 + c[2]]; \
660 ((comp_t *)dst[2])[x >> 1] = ((comp_t *)src)[x * 2 + c[3]]; \
661 } \
662 }
663
664PA_P422(pa_p422_8, uint8_t)
665PA_P422(pa_p422_16, uint16_t)
666UN_P422(un_p422_8, uint8_t)
667UN_P422(un_p422_16, uint16_t)
668
669static int fringe_yuv422_repack(void *user, unsigned i, unsigned x0, unsigned x1)
670{
671 struct mp_zimg_repack *r = user;
672
673 void *p1 = r->mpi->planes[0] + r->mpi->stride[0] * (ptrdiff_t)(i - r->mpi_y0);
674
675 void *p2[4] = {0};
676 for (int p = 0; p < r->num_planes; p++) {
677 p2[p] = r->tmp->planes[p] +
678 r->tmp->stride[p] * (ptrdiff_t)(i & r->zmask[p]);
679 }
680
681 assert(r->comp_size == 1 || r->comp_size == 2);
682
683 void (*repack)(void *p1, void *p2[], int x0, int x1, uint8_t *c) = NULL;
684 if (r->pack) {
685 repack = r->comp_size == 1 ? pa_p422_8 : pa_p422_16;
686 } else {
687 repack = r->comp_size == 1 ? un_p422_8 : un_p422_16;
688 }
689 repack(p1, p2, x0, x1, r->comp_map);
690
691 return 0;
692}
693
694static int repack_nv(void *user, unsigned i, unsigned x0, unsigned x1)
695{
696 struct mp_zimg_repack *r = user;
697
698 int xs = r->mpi->fmt.chroma_xs;
699 int ys = r->mpi->fmt.chroma_ys;
700
701 if (r->use_buf[0]) {
702 // Copy Y.
703 int l_h = 1 << ys;
704 for (int y = i; y < i + l_h; y++) {
705 ptrdiff_t bpp = r->mpi->fmt.bytes[0];
706 void *a = r->mpi->planes[0] +
707 r->mpi->stride[0] * (ptrdiff_t)(y - r->mpi_y0) + bpp * x0;
708 void *b = r->tmp->planes[0] +
709 r->tmp->stride[0] * (ptrdiff_t)(y & r->zmask[0]) + bpp * x0;
710 size_t size = (x1 - x0) * bpp;
711 if (r->pack) {
712 memcpy(a, b, size);
713 } else {
714 memcpy(b, a, size);
715 }
716 }
717 }
718
719 uint32_t *p1 = (void *)(r->mpi->planes[1] +
720 r->mpi->stride[1] * (ptrdiff_t)((i - r->mpi_y0) >> ys));
721
722 void *p2[2];
723 for (int p = 0; p < 2; p++) {
724 int s = r->components[p];
725 p2[p] = r->tmp->planes[s] +
726 r->tmp->stride[s] * (ptrdiff_t)((i >> ys) & r->zmask[s]);
727 }
728
729 r->packed_repack_scanline(p1, p2, x0 >> xs, x1 >> xs);
730
731 return 0;
732}
733
734static int repack_entrypoint(void *user, unsigned i, unsigned x0, unsigned x1)211static int repack_entrypoint(void *user, unsigned i, unsigned x0, unsigned x1)
735{212{
736 struct mp_zimg_repack *r = user;213 struct mp_zimg_repack *r = user;
737 214
738 if (r->endian_size && !r->pack) {215 // If reading is not aligned, just read slightly more data.
739 r->mpi = r->tmp_endian;216 if (!r->pack)
740 r->mpi_y0 = i;217 x0 &= ~(unsigned)(mp_repack_get_align_x(r->repack) - 1);
741 swap_endian(r, r->mpi, 0, r->user_mpi, i, x0, x1);
742 } else {
743 r->mpi = r->user_mpi;
744 r->mpi_y0 = 0;
745 }
746 218
747 if (r->repack) {219 // mp_repack requirements and zimg guarantees.
748 r->repack(r, i, x0, x1);220 assert(!(i & (mp_repack_get_align_y(r->repack) - 1)));
749 } else {221 assert(!(x0 & (mp_repack_get_align_x(r->repack) - 1)));
750 repack_align(r, i, x0, x1);222
751 }223 unsigned i_src = i & (r->pack ? r->zmask[0] : ZIMG_BUFFER_MAX);
224 unsigned i_dst = i & (r->pack ? ZIMG_BUFFER_MAX : r->zmask[0]);
752 225
753 if (r->endian_size && r->pack)226 repack_line(r->repack, x0, i_dst, x0, i_src, x1 - x0);
754 swap_endian(r, r->user_mpi, i, r->mpi, i - r->mpi_y0, x0, x1);
755 227
756 r->mpi = NULL;
757 return 0;228 return 0;
758}229}
759 230
760static void wrap_buffer(struct mp_zimg_repack *r,231static bool wrap_buffer(struct mp_zimg_repack *r,
761 zimg_image_buffer *buf,232 zimg_image_buffer *buf,
762 struct mp_image *mpi)233 struct mp_image *mpi)
763{234{
764 *buf = (zimg_image_buffer){ZIMG_API_VERSION};235 *buf = (zimg_image_buffer){ZIMG_API_VERSION};
765 236
766 bool plane_aligned[4] = {0};237 bool direct[MP_MAX_PLANES] = {0};
767 for (int n = 0; n < r->num_planes; n++) {238
768 plane_aligned[n] = !((uintptr_t)mpi->planes[n] % ZIMG_ALIGN) &&239 for (int p = 0; p < mpi->num_planes; p++) {
769 !(mpi->stride[n] % ZIMG_ALIGN);240 // If alignment is good, try to avoid copy.
241 direct[p] = !((uintptr_t)mpi->planes[p] % ZIMG_ALIGN) &&
242 !(mpi->stride[p] % ZIMG_ALIGN);
770 }243 }
771 244
245 if (!repack_config_buffers(r->repack, 0, r->pack ? mpi : r->tmp,
246 0, r->pack ? r->tmp : mpi, direct))
247 return false;
248
772 for (int n = 0; n < MP_ARRAY_SIZE(buf->plane); n++) {249 for (int n = 0; n < MP_ARRAY_SIZE(buf->plane); n++) {
773 // Note: this is really the only place we have to care about plane250 // Note: this is really the only place we have to care about plane
774 // permutation (zimg_image_buffer may have a different plane order251 // permutation (zimg_image_buffer may have a different plane order
@@ -778,355 +255,67 @@ static void wrap_buffer(struct mp_zimg_repack *r, · 3 unmodified lines
778 if (mplane < 0)255 if (mplane < 0)
779 continue;256 continue;
780 257
781 r->use_buf[mplane] = !plane_aligned[mplane] || r->endian_size;258 struct mp_image *tmpi = direct[mplane] ? mpi : r->tmp;
782 if (!(r->pass_through_y && mplane == 0))
783 r->use_buf[mplane] |= !!r->repack;
784
785 struct mp_image *tmpi = r->use_buf[mplane] ? r->tmp : mpi;
786 buf->plane[n].data = tmpi->planes[mplane];259 buf->plane[n].data = tmpi->planes[mplane];
787 buf->plane[n].stride = tmpi->stride[mplane];260 buf->plane[n].stride = tmpi->stride[mplane];
788 buf->plane[n].mask = r->use_buf[mplane] ? r->zmask[mplane]261 buf->plane[n].mask = direct[mplane] ? ZIMG_BUFFER_MAX : r->zmask[mplane];
789 : ZIMG_BUFFER_MAX;
790 }262 }
791 263
792 r->user_mpi = mpi;264 return true;
793}
794
795// depth = number of LSB in use
796static int find_gbrp_format(int depth, int num_planes)
797{
798 if (num_planes != 3 && num_planes != 4)
799 return 0;
800 struct mp_regular_imgfmt desc = {
801 .component_type = MP_COMPONENT_TYPE_UINT,
802 .forced_csp = MP_CSP_RGB,
803 .component_size = depth > 8 ? 2 : 1,
804 .component_pad = depth - (depth > 8 ? 16 : 8),
805 .num_planes = num_planes,
806 .planes = { {1, {2}}, {1, {3}}, {1, {1}}, {1, {4}} },
807 };
808 return mp_find_regular_imgfmt(&desc);
809}
810
811// depth = number of LSB in use
812static int find_gray_format(int depth, int num_planes)
813{
814 if (num_planes != 1 && num_planes != 2)
815 return 0;
816 struct mp_regular_imgfmt desc = {
817 .component_type = MP_COMPONENT_TYPE_UINT,
818 .component_size = depth > 8 ? 2 : 1,
819 .component_pad = depth - (depth > 8 ? 16 : 8),
820 .num_planes = num_planes,
821 .planes = { {1, {1}}, {1, {4}} },
822 };
823 return mp_find_regular_imgfmt(&desc);
824}265}
825 266
826static void setup_fringe_rgb_packer(struct mp_zimg_repack *r,267// (ctx can be NULL for probing.)
827 struct mp_zimg_context *ctx)268static bool setup_format(zimg_image_format *zfmt, struct mp_zimg_repack *r,
269 bool pack, struct mp_image_params *user_fmt,
270 struct mp_zimg_context *ctx)
828{271{
829 enum AVPixelFormat avfmt = imgfmt2pixfmt(r->zimgfmt);272 r->fmt = *user_fmt;
273 r->pack = pack;
830 274
831 const struct fringe_rgb_repacker *fmt = NULL;275 zimg_image_format_default(zfmt, ZIMG_API_VERSION);
832 for (int n = 0; n < MP_ARRAY_SIZE(fringe_rgb_repackers); n++) {
833 if (fringe_rgb_repackers[n].avfmt == avfmt) {
834 fmt = &fringe_rgb_repackers[n];
835 break;
836 }
837 }
838 276
839 if (!fmt)277 int rp_flags = 0;
840 return;
841 278
842 int depth = 8;279 // For e.g. RGB565, go to lowest depth on pack for less weird dithering.
843 if (r->pack) {280 if (r->pack) {
844 // Dither to lowest depth - loses some precision, but result is saner.281 rp_flags |= REPACK_CREATE_ROUND_DOWN;
845 depth = fmt->bits[0];
846 for (int n = 0; n < 3; n++)
847 depth = MPMIN(depth, fmt->bits[n]);
848 }
849
850 r->zimgfmt = find_gbrp_format(depth, 3);
851 if (!r->zimgfmt)
852 return;
853 if (ctx)
854 r->comp_lut = talloc_array(ctx, uint8_t, 256 * 3);
855 r->repack = fringe_rgb_repack;
856 static const int c_order_rgb[] = {3, 1, 2};
857 static const int c_order_bgr[] = {2, 1, 3};
858 for (int n = 0; n < 3; n++)
859 r->components[n] = (fmt->rev_order ? c_order_bgr : c_order_rgb)[n] - 1;
860
861 int bitpos = 0;
862 for (int n = 0; n < 3; n++) {
863 int bits = fmt->bits[n];
864 r->comp_shifts[n] = bitpos;
865 if (r->comp_lut) {
866 uint8_t *lut = r->comp_lut + 256 * n;
867 uint8_t zmax = (1 << depth) - 1;
868 uint8_t cmax = (1 << bits) - 1;
869 for (int v = 0; v < 256; v++) {
870 if (r->pack) {
871 lut[v] = (v * cmax + zmax / 2) / zmax;
872 } else {
873 lut[v] = (v & cmax) * zmax / cmax;
874 }
875 }
876 }
877 bitpos += bits;
878 }
879
880 r->comp_size = (bitpos + 7) / 8;
881 assert(r->comp_size == 1 || r->comp_size == 2);
882
883 if (fmt->be) {
884 assert(r->comp_size == 2);
885 r->endian_size = 2;
886 r->endian_items[0] = 1;
887 }
888}
889
890static void setup_fringe_yuv422_packer(struct mp_zimg_repack *r)
891{
892 enum AVPixelFormat avfmt = imgfmt2pixfmt(r->zimgfmt);
893
894 const struct fringe_yuv422_repacker *fmt = NULL;
895 for (int n = 0; n < MP_ARRAY_SIZE(fringe_yuv422_repackers); n++) {
896 if (fringe_yuv422_repackers[n].avfmt == avfmt) {
897 fmt = &fringe_yuv422_repackers[n];
898 break;
899 }
900 }
901
902 if (!fmt)
903 return;
904
905 r->comp_size = (fmt->depth + 7) / 8;
906 assert(r->comp_size == 1 || r->comp_size == 2);
907
908 struct mp_regular_imgfmt yuvfmt = {
909 .component_type = MP_COMPONENT_TYPE_UINT,
910 // NB: same problem with P010 and not clearing padding.
911 .component_size = r->comp_size,
912 .num_planes = 3,
913 .planes = { {1, {1}}, {1, {2}}, {1, {3}} },
914 .chroma_xs = 1,
915 .chroma_ys = 0,
916 };
917 r->zimgfmt = mp_find_regular_imgfmt(&yuvfmt);
918 r->repack = fringe_yuv422_repack;
919 r->comp_map = (uint8_t *)fmt->comp;
920
921 if (fmt->be) {
922 assert(r->comp_size == 2);
923 r->endian_size = 2;
924 r->endian_items[0] = 4;
925 }
926}
927
928static void setup_nv_packer(struct mp_zimg_repack *r)
929{
930 struct mp_regular_imgfmt desc;
931 if (!mp_get_regular_imgfmt(&desc, r->zimgfmt))
932 return;
933
934 // Check for NV.
935 if (desc.num_planes != 2)
936 return;
937 if (desc.planes[0].num_components != 1 || desc.planes[0].components[0] != 1)
938 return;
939 if (desc.planes[1].num_components != 2)
940 return;
941 int cr0 = desc.planes[1].components[0];
942 int cr1 = desc.planes[1].components[1];
943 if (cr0 > cr1)
944 MPSWAP(int, cr0, cr1);
945 if (cr0 != 2 || cr1 != 3)
946 return;
947
948 // Construct equivalent planar format.
949 struct mp_regular_imgfmt desc2 = desc;
950 desc2.num_planes = 3;
951 desc2.planes[1].num_components = 1;
952 desc2.planes[1].components[0] = 2;
953 desc2.planes[2].num_components = 1;
954 desc2.planes[2].components[0] = 3;
955 // For P010. Strangely this concept exists only for the NV format.
956 if (desc2.component_pad > 0)
957 desc2.component_pad = 0;
958
959 int planar_fmt = mp_find_regular_imgfmt(&desc2);
960 if (!planar_fmt)
961 return;
962
963 for (int i = 0; i < MP_ARRAY_SIZE(regular_repackers); i++) {
964 const struct regular_repacker *pa = &regular_repackers[i];
965
966 void (*repack_cb)(void *p1, void *p2[], int x0, int x1) =
967 r->pack ? pa->pa_scanline : pa->un_scanline;
968
969 if (pa->packed_width != desc.component_size * 2 * 8 ||
970 pa->component_width != desc.component_size * 8 ||
971 pa->num_components != 2 ||
972 pa->prepadding != 0 ||
973 !repack_cb)
974 continue;
975
976 r->repack = repack_nv;
977 r->pass_through_y = true;
978 r->packed_repack_scanline = repack_cb;
979 r->zimgfmt = planar_fmt;
980 r->components[0] = desc.planes[1].components[0] - 1;
981 r->components[1] = desc.planes[1].components[1] - 1;
982 return;
983 }
984}
985
986static void setup_misc_packer(struct mp_zimg_repack *r)
987{
988 // Although it's in regular_repackers[], the generic mpv imgfmt metadata
989 // can't handle it yet.
990 if (r->zimgfmt == IMGFMT_RGB30) {
991 int planar_fmt = find_gbrp_format(10, 3);
992 if (!planar_fmt)
993 return;
994 r->zimgfmt = planar_fmt;
995 r->repack = packed_repack;
996 r->packed_repack_scanline = r->pack ? pa_ccc10z2 : un_ccc10x2;
997 static int c_order[] = {3, 2, 1};
998 for (int n = 0; n < 3; n++)
999 r->components[n] = c_order[n] - 1;
1000 } else if (r->zimgfmt == IMGFMT_PAL8 && !r->pack) {
1001 int grap_fmt = find_gbrp_format(8, 4);
1002 if (!grap_fmt)
1003 return;
1004 r->zimgfmt = grap_fmt;
1005 r->repack = unpack_pal;
1006 } else {282 } else {
1007 enum AVPixelFormat avfmt = imgfmt2pixfmt(r->zimgfmt);283 rp_flags |= REPACK_CREATE_EXPAND_8BIT;
1008 if (avfmt == AV_PIX_FMT_MONOWHITE || avfmt == AV_PIX_FMT_MONOBLACK) {
1009 r->zimgfmt = IMGFMT_Y1;
1010 r->repack = bitmap_repack;
1011 r->comp_size = avfmt == AV_PIX_FMT_MONOWHITE; // abuse to pass a flag
1012 return;
1013 }
1014 }284 }
1015}
1016
1017// Tries to set a packer/unpacker for component-wise byte aligned RGB formats.
1018static void setup_regular_rgb_packer(struct mp_zimg_repack *r)
1019{
1020 struct mp_regular_imgfmt desc;
1021 if (!mp_get_regular_imgfmt(&desc, r->zimgfmt))
1022 return;
1023 285
1024 if (desc.num_planes != 1 || desc.planes[0].num_components < 2)286 r->repack = mp_repack_create_planar(r->fmt.imgfmt, r->pack, rp_flags);
1025 return;287 if (!r->repack)
1026 struct mp_regular_imgfmt_plane *p = &desc.planes[0];288 return false;
1027
1028 int num_real_components = 0;
1029 bool has_alpha = false;
1030 for (int n = 0; n < p->num_components; n++) {
1031 if (p->components[n]) {
1032 has_alpha |= p->components[n] == 4;
1033 num_real_components += 1;
1034 } else {
1035 // padding must be in MSB or LSB
1036 if (n != 0 && n != p->num_components - 1)
1037 return;
1038 }
1039 }
1040 289
1041 int depth = desc.component_size * 8 + MPMIN(0, desc.component_pad);290 int align_x = mp_repack_get_align_x(r->repack);
1042 291
1043 int planar_fmt = num_real_components > 2292 r->zimgfmt = r->pack ? mp_repack_get_format_src(r->repack)
1044 ? find_gbrp_format(depth, num_real_components)293 : mp_repack_get_format_dst(r->repack);
1045 : find_gray_format(depth, num_real_components);
1046 if (!planar_fmt)
1047 return;
1048 static const int reorder_gbrp[] = {0, 3, 1, 2, 4};
1049 static const int reorder_gray[] = {0, 1, 0, 0, 4};
1050 const int *reorder = num_real_components > 2 ? reorder_gbrp : reorder_gray;
1051
1052 for (int i = 0; i < MP_ARRAY_SIZE(regular_repackers); i++) {
1053 const struct regular_repacker *pa = &regular_repackers[i];
1054
1055 // The following may assume little endian (because some repack backends
1056 // use word access, while the metadata here uses byte access).
1057
1058 int prepad = p->components[0] ? 0 : 8;
1059 int first_comp = p->components[0] ? 0 : 1;
1060 void (*repack_cb)(void *p1, void *p2[], int x0, int x1) =
1061 r->pack ? pa->pa_scanline : pa->un_scanline;
1062
1063 if (pa->packed_width != desc.component_size * p->num_components * 8 ||
1064 pa->component_width != depth ||
1065 pa->num_components != num_real_components ||
1066 pa->prepadding != prepad ||
1067 !repack_cb)
1068 continue;
1069 294
1070 r->repack = packed_repack;295 if (ctx) {
1071 r->packed_repack_scanline = repack_cb;296 talloc_steal(r, r->repack);
1072 r->zimgfmt = planar_fmt;297 } else {
1073 for (int n = 0; n < num_real_components; n++) {298 TA_FREEP(&r->repack);
1074 // Determine permutation that maps component order between the two
1075 // formats, with has_alpha special case (see above).
1076 int c = reorder[p->components[first_comp + n]];
1077 r->components[n] = c == 4 ? num_real_components - 1 : c - 1;
1078 }
1079 return;
1080 }299 }
1081}
1082
1083// (If native_fmt!=r->fmt.imgfmt, this is the swap-endian case; native_fmt is NE.)
1084// (ctx can be NULL for the sake of probing.)
1085static bool setup_format_ne(zimg_image_format *zfmt, struct mp_zimg_repack *r,
1086 int native_fmt, struct mp_zimg_context *ctx)
1087{
1088 zimg_image_format_default(zfmt, ZIMG_API_VERSION);
1089 300
1090 struct mp_image_params fmt = r->fmt;301 struct mp_image_params fmt = r->fmt;
1091 mp_image_params_guess_csp(&fmt);302 mp_image_params_guess_csp(&fmt);
1092 303
1093 r->zimgfmt = native_fmt;
1094
1095 if (!r->repack)
1096 setup_nv_packer(r);
1097 if (!r->repack)
1098 setup_misc_packer(r);
1099 if (!r->repack)
1100 setup_regular_rgb_packer(r);
1101 if (!r->repack)
1102 setup_fringe_rgb_packer(r, ctx);
1103 if (!r->repack)
1104 setup_fringe_yuv422_packer(r);
1105
1106 struct mp_regular_imgfmt desc;304 struct mp_regular_imgfmt desc;
1107 if (!mp_get_regular_imgfmt(&desc, r->zimgfmt))305 if (!mp_get_regular_imgfmt(&desc, r->zimgfmt))
1108 return false;306 return false;
1109 307
308 // Relies on zimg callbacks reading on 64 byte alignment.
309 if (!MP_IS_POWER_OF_2(align_x) || align_x > 64 / desc.component_size)
310 return false;
311
1110 // no weird stuff312 // no weird stuff
1111 if (desc.num_planes > 4)313 if (desc.num_planes > 4)
1112 return false;314 return false;
1113 315
1114 // Endian swapping.
1115 if (native_fmt != fmt.imgfmt) {
1116 struct mp_regular_imgfmt ndesc;
1117 if (!mp_get_regular_imgfmt(&ndesc, native_fmt) || ndesc.num_planes > 4)
1118 return false;
1119 r->endian_size = ndesc.component_size;
1120 if (r->endian_size != 2 && r->endian_size != 4)
1121 return false;
1122 for (int n = 0; n < ndesc.num_planes; n++)
1123 r->endian_items[n] = ndesc.planes[n].num_components;
1124 }
1125
1126 for (int n = 0; n < 4; n++)316 for (int n = 0; n < 4; n++)
1127 r->z_planes[n] = -1;317 r->z_planes[n] = -1;
1128 318
1129 // Accept only true planar formats.
1130 for (int n = 0; n < desc.num_planes; n++) {319 for (int n = 0; n < desc.num_planes; n++) {
1131 if (desc.planes[n].num_components != 1)320 if (desc.planes[n].num_components != 1)
1132 return false;321 return false;
@@ -1219,25 +408,6 @@ static bool setup_format_ne(zimg_image_format *zfmt, struct mp_zimg_repack *r, · 86 unmodified lines
1219 return true;408 return true;
1220}409}
1221 410
1222static bool setup_format(zimg_image_format *zfmt, struct mp_zimg_repack *r,
1223 bool pack, struct mp_image_params *fmt,
1224 struct mp_zimg_context *ctx)
1225{
1226 struct mp_zimg_repack repack_init = {
1227 .pack = pack,
1228 .fmt = *fmt,
1229 };
1230 *r = repack_init;
1231 if (setup_format_ne(zfmt, r, fmt->imgfmt, ctx))
1232 return true;
1233 // Try reverse endian.
1234 int nimgfmt = mp_find_other_endian(fmt->imgfmt);
1235 if (!nimgfmt)
1236 return false;
1237 *r = repack_init;
1238 return setup_format_ne(zfmt, r, nimgfmt, ctx);
1239}
1240
1241static bool allocate_buffer(struct mp_zimg_context *ctx,411static bool allocate_buffer(struct mp_zimg_context *ctx,
1242 struct mp_zimg_repack *r)412 struct mp_zimg_repack *r)
1243{413{
@@ -1269,20 +439,15 @@ static bool allocate_buffer(struct mp_zimg_context *ctx, · 25 unmodified lines
1269 if (!r->tmp)439 if (!r->tmp)
1270 return false;440 return false;
1271 441
442 // Note: although zimg doesn't require that the chroma plane's zmask is
443 // divided by the full size zmask, the repack callback requires it,
444 // since mp_repack can handle only proper slices.
1272 for (int n = 1; n < r->tmp->fmt.num_planes; n++) {445 for (int n = 1; n < r->tmp->fmt.num_planes; n++) {
1273 r->zmask[n] = r->zmask[0];446 r->zmask[n] = r->zmask[0];
1274 if (r->zmask[0] != ZIMG_BUFFER_MAX)447 if (r->zmask[0] != ZIMG_BUFFER_MAX)
1275 r->zmask[n] = r->zmask[n] >> r->tmp->fmt.ys[n];448 r->zmask[n] = r->zmask[n] >> r->tmp->fmt.ys[n];
1276 }449 }
1277 450
1278 if (r->endian_size && !r->pack) {
1279 r->tmp_endian = mp_image_alloc(r->fmt.imgfmt, r->fmt.w, h);
1280 talloc_steal(r, r->tmp_endian);
1281
1282 if (!r->tmp_endian)
1283 return false;
1284 }
1285
1286 return true;451 return true;
1287}452}
1288 453
@@ -1382,8 +547,12 @@ bool mp_zimg_convert(struct mp_zimg_context *ctx, struct mp_image *dst, · 93 unmodified lines
1382 assert(ctx->zimg_graph);547 assert(ctx->zimg_graph);
1383 548
1384 zimg_image_buffer zsrc, zdst;549 zimg_image_buffer zsrc, zdst;
1385 wrap_buffer(ctx->zimg_src, &zsrc, src);550 if (!wrap_buffer(ctx->zimg_src, &zsrc, src) ||
1386 wrap_buffer(ctx->zimg_dst, &zdst, dst);551 !wrap_buffer(ctx->zimg_dst, &zdst, dst))
552 {
553 MP_ERR(ctx, "zimg repacker initialization failed.\n");
554 return false;
555 }
1387 556
1388 // An annoyance.557 // An annoyance.
1389 zimg_image_buffer_const zsrc_c = {ZIMG_API_VERSION};558 zimg_image_buffer_const zsrc_c = {ZIMG_API_VERSION};
@@ -1400,9 +569,6 @@ bool mp_zimg_convert(struct mp_zimg_context *ctx, struct mp_image *dst, · 10 unmodified lines
1400 repack_entrypoint, ctx->zimg_src,569 repack_entrypoint, ctx->zimg_src,
1401 repack_entrypoint, ctx->zimg_dst);570 repack_entrypoint, ctx->zimg_dst);
1402 571
1403 ctx->zimg_src->user_mpi = NULL;
1404 ctx->zimg_dst->user_mpi = NULL;
1405
1406 return true;572 return true;
1407}573}
1408 574
wscript_build.py+2−0
@@ -402,6 +402,7 @@ def build(ctx): · 401 unmodified lines
402 ( "test/json.c", "tests" ),402 ( "test/json.c", "tests" ),
403 ( "test/linked_list.c", "tests" ),403 ( "test/linked_list.c", "tests" ),
404 ( "test/paths.c", "tests" ),404 ( "test/paths.c", "tests" ),
405 ( "test/repack.c", "tests && zimg" ),
405 ( "test/scale_sws.c", "tests" ),406 ( "test/scale_sws.c", "tests" ),
406 ( "test/scale_test.c", "tests" ),407 ( "test/scale_test.c", "tests" ),
407 ( "test/scale_zimg.c", "tests && zimg" ),408 ( "test/scale_zimg.c", "tests && zimg" ),
@@ -529,6 +530,7 @@ def build(ctx): · 121 unmodified lines
529 ( "video/out/win32/droptarget.c", "win32-desktop" ),530 ( "video/out/win32/droptarget.c", "win32-desktop" ),
530 ( "video/out/win_state.c"),531 ( "video/out/win_state.c"),
531 ( "video/out/x11_common.c", "x11" ),532 ( "video/out/x11_common.c", "x11" ),
533 ( "video/repack.c" ),
532 ( "video/sws_utils.c" ),534 ( "video/sws_utils.c" ),
533 ( "video/zimg.c", "zimg" ),535 ( "video/zimg.c", "zimg" ),
534 ( "video/vaapi.c", "vaapi" ),536 ( "video/vaapi.c", "vaapi" ),

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t