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 @@
1+0bgr => [pa] [un] gbrp | a=1:1 [tu] [tp]
2+0rgb => [pa] [un] gbrp | a=1:1 [tu] [tp]
3+abgr => [pa] [un] gbrap | a=1:1 [tu] [tp]
4+argb => [pa] [un] gbrap | a=1:1 [tu] [tp]
5+ayuv64 => [pa] [un] yuva444p16 | a=1:1 [tu] [tp]
6+ayuv64be => [pa] [un] yuva444p16 | a=1:1 [tu] [tp]
7+bayer_bggr16 => no
8+bayer_bggr16be => no
9+bayer_bggr8 => no
10+bayer_gbrg16 => no
11+bayer_gbrg16be => no
12+bayer_gbrg8 => no
13+bayer_grbg16 => no
14+bayer_grbg16be => no
15+bayer_grbg8 => no
16+bayer_rggb16 => no
17+bayer_rggb16be => no
18+bayer_rggb8 => no
19+bgr0 => [pa] [un] gbrp | a=1:1 [tu] [tp]
20+bgr24 => [pa] [un] gbrp | a=1:1
21+bgr4 => no
22+bgr444 => [pa] [un] gbrp4 | a=1:1
23+bgr444 => [pa] [un] gbrp | a=1:1 [expand-8bit]
24+bgr444be => [pa] [un] gbrp4 | a=1:1
25+bgr444be => [pa] [un] gbrp | a=1:1 [expand-8bit]
26+bgr48 => [pa] [un] gbrp16 | a=1:1
27+bgr48be => [pa] [un] gbrp16 | a=1:1
28+bgr4_byte => [pa] [un] gbrp2 | a=1:1
29+bgr4_byte => [pa] [un] gbrp1 | a=1:1 [round-down]
30+bgr4_byte => [pa] [un] gbrp | a=1:1 [expand-8bit]
31+bgr555 => [pa] [un] gbrp5 | a=1:1
32+bgr555 => [pa] [un] gbrp | a=1:1 [expand-8bit]
33+bgr555be => [pa] [un] gbrp5 | a=1:1
34+bgr555be => [pa] [un] gbrp | a=1:1 [expand-8bit]
35+bgr565 => [pa] [un] gbrp6 | a=1:1
36+bgr565 => [pa] [un] gbrp5 | a=1:1 [round-down]
37+bgr565 => [pa] [un] gbrp | a=1:1 [expand-8bit]
38+bgr565be => [pa] [un] gbrp6 | a=1:1
39+bgr565be => [pa] [un] gbrp5 | a=1:1 [round-down]
40+bgr565be => [pa] [un] gbrp | a=1:1 [expand-8bit]
41+bgr8 => [pa] [un] gbrp3 | a=1:1
42+bgr8 => [pa] [un] gbrp2 | a=1:1 [round-down]
43+bgr8 => [pa] [un] gbrp | a=1:1 [expand-8bit]
44+bgra => [pa] [un] gbrap | a=1:1 [tu] [tp]
45+bgra64 => [pa] [un] gbrap16 | a=1:1
46+bgra64be => [pa] [un] gbrap16 | a=1:1
47+cuda => no
48+d3d11 => no
49+d3d11va_vld => no
50+drm_prime => no
51+dxva2_vld => no
52+gbrap10be => [pa] [un] gbrap10 | a=1:1
53+gbrap12be => [pa] [un] gbrap12 | a=1:1
54+gbrap16be => [pa] [un] gbrap16 | a=1:1
55+gbrapf32be => [pa] [un] gbrapf32 | a=1:1
56+gbrp10be => [pa] [un] gbrp10 | a=1:1
57+gbrp12be => [pa] [un] gbrp12 | a=1:1
58+gbrp14be => [pa] [un] gbrp14 | a=1:1
59+gbrp16be => [pa] [un] gbrp16 | a=1:1
60+gbrp9be => [pa] [un] gbrp9 | a=1:1
61+gbrpf32be => [pa] [un] gbrpf32 | a=1:1
62+gray10be => [pa] [un] gray10 | a=1:1
63+gray12be => [pa] [un] gray12 | a=1:1
64+gray14be => [pa] [un] gray14 | a=1:1
65+gray16be => [pa] [un] gray16 | a=1:1
66+gray9be => [pa] [un] gray9 | a=1:1
67+grayf32be => [pa] [un] grayf32 | a=1:1
68+mediacodec => no
69+mmal => no
70+monob => [pa] [un] y1 | a=8:1 [tu] [tp]
71+monob => [pa] [un] gray | a=8:1 [expand-8bit]
72+monow => [pa] [un] y1 | a=8:1 [tu] [tp]
73+monow => [pa] [un] gray | a=8:1 [expand-8bit]
74+nv12 => [pa] [un] yuv420p | a=2:2 [tu] [tp]
75+nv16 => [pa] [un] yuv422p | a=2:1
76+nv20 => [pa] [un] yuv422p10 | a=2:1
77+nv20be => [pa] [un] yuv422p10 | a=2:1
78+nv21 => [pa] [un] yuv420p | a=2:2 [tu] [tp]
79+nv24 => [pa] [un] yuv444p | a=1:1
80+nv42 => [pa] [un] yuv444p | a=1:1
81+opencl => no
82+p010 => [pa] [un] yuv420p16 | a=2:2
83+p010be => [pa] [un] yuv420p16 | a=2:2
84+p016 => [pa] [un] yuv420p16 | a=2:2
85+p016be => [pa] [un] yuv420p16 | a=2:2
86+pal8 => [un] gbrap | a=1:1
87+qsv => no
88+rgb0 => [pa] [un] gbrp | a=1:1 [tu] [tp]
89+rgb24 => [pa] [un] gbrp | a=1:1
90+rgb30 => [pa] [un] gbrp10 | a=1:1
91+rgb4 => no
92+rgb444 => [pa] [un] gbrp4 | a=1:1
93+rgb444 => [pa] [un] gbrp | a=1:1 [expand-8bit]
94+rgb444be => [pa] [un] gbrp4 | a=1:1
95+rgb444be => [pa] [un] gbrp | a=1:1 [expand-8bit]
96+rgb48 => [pa] [un] gbrp16 | a=1:1
97+rgb48be => [pa] [un] gbrp16 | a=1:1 [tu] [tp]
98+rgb4_byte => [pa] [un] gbrp2 | a=1:1
99+rgb4_byte => [pa] [un] gbrp1 | a=1:1 [round-down]
100+rgb4_byte => [pa] [un] gbrp | a=1:1 [expand-8bit]
101+rgb555 => [pa] [un] gbrp5 | a=1:1
102+rgb555 => [pa] [un] gbrp | a=1:1 [expand-8bit]
103+rgb555be => [pa] [un] gbrp5 | a=1:1
104+rgb555be => [pa] [un] gbrp | a=1:1 [expand-8bit]
105+rgb565 => [pa] [un] gbrp6 | a=1:1
106+rgb565 => [pa] [un] gbrp5 | a=1:1 [round-down]
107+rgb565 => [pa] [un] gbrp | a=1:1 [expand-8bit]
108+rgb565be => [pa] [un] gbrp6 | a=1:1
109+rgb565be => [pa] [un] gbrp5 | a=1:1 [round-down]
110+rgb565be => [pa] [un] gbrp | a=1:1 [expand-8bit]
111+rgb8 => [pa] [un] gbrp3 | a=1:1
112+rgb8 => [pa] [un] gbrp2 | a=1:1 [round-down]
113+rgb8 => [pa] [un] gbrp | a=1:1 [expand-8bit]
114+rgba => [pa] [un] gbrap | a=1:1 [tu] [tp]
115+rgba64 => [pa] [un] gbrap16 | a=1:1 [tu] [tp]
116+rgba64be => [pa] [un] gbrap16 | a=1:1
117+uyvy422 => [pa] [un] yuv422p | a=2:1
118+uyyvyy411 => no
119+vaapi => no
120+vaapi_idct => no
121+vaapi_moco => no
122+vdpau => no
123+vdpau_output => no
124+videotoolbox => no
125+vulkan => no
126+xvmc => no
127+xyz12 => [pa] [un] gbrp16 | a=1:1
128+xyz12be => [pa] [un] gbrp16 | a=1:1
129+y210 => [pa] [un] yuv422p16 | a=2:1
130+y210be => [pa] [un] yuv422p16 | a=2:1
131+ya16 => [pa] [un] yap16 | a=1:1 [tu] [tp]
132+ya16be => [pa] [un] yap16 | a=1:1
133+ya8 => [pa] [un] yap8 | a=1:1
134+yuv420p10be => [pa] [un] yuv420p10 | a=2:2
135+yuv420p12be => [pa] [un] yuv420p12 | a=2:2
136+yuv420p14be => [pa] [un] yuv420p14 | a=2:2
137+yuv420p16be => [pa] [un] yuv420p16 | a=2:2
138+yuv420p9be => [pa] [un] yuv420p9 | a=2:2
139+yuv422p10be => [pa] [un] yuv422p10 | a=2:1
140+yuv422p12be => [pa] [un] yuv422p12 | a=2:1
141+yuv422p14be => [pa] [un] yuv422p14 | a=2:1
142+yuv422p16be => [pa] [un] yuv422p16 | a=2:1 [tu] [tp]
143+yuv422p9be => [pa] [un] yuv422p9 | a=2:1
144+yuv440p10be => [pa] [un] yuv440p10 | a=1:2
145+yuv440p12be => [pa] [un] yuv440p12 | a=1:2
146+yuv444p10be => [pa] [un] yuv444p10 | a=1:1
147+yuv444p12be => [pa] [un] yuv444p12 | a=1:1
148+yuv444p14be => [pa] [un] yuv444p14 | a=1:1
149+yuv444p16be => [pa] [un] yuv444p16 | a=1:1
150+yuv444p9be => [pa] [un] yuv444p9 | a=1:1
151+yuva420p10be => [pa] [un] yuva420p10 | a=2:2
152+yuva420p16be => [pa] [un] yuva420p16 | a=2:2
153+yuva420p9be => [pa] [un] yuva420p9 | a=2:2
154+yuva422p10be => [pa] [un] yuva422p10 | a=2:1
155+yuva422p12be => [pa] [un] yuva422p12 | a=2:1
156+yuva422p16be => [pa] [un] yuva422p16 | a=2:1
157+yuva422p9be => [pa] [un] yuva422p9 | a=2:1
158+yuva444p10be => [pa] [un] yuva444p10 | a=1:1
159+yuva444p12be => [pa] [un] yuva444p12 | a=1:1
160+yuva444p16be => [pa] [un] yuva444p16 | a=1:1
161+yuva444p9be => [pa] [un] yuva444p9 | a=1:1
162+yuyv422 => [pa] [un] yuv422p | a=2:1
163+yvyu422 => [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)))
12+static_assert(IMGFMT_START > 0, "");
13+#define IMGFMT_GBRP (-AV_PIX_FMT_GBRP)
14+#define IMGFMT_GBRAP (-AV_PIX_FMT_GBRAP)
15+
16+struct 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.
29+static 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+
77+static 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+
91+static 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+
227+static 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+
246+const 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
1212 &test_paths,
1313 &test_repack_sws,
1414 #if HAVE_ZIMG
15+ &test_repack, // zimg only due to cross-checking with zimg.c
1516 &test_repack_zimg,
1617 #endif
1718 NULL
@@ -128,3 +129,25 @@ void assert_text_files_equal_impl(const char *file, int line, · 110 unmodified lines
128129 abort();
129130 }
130131 }
132+
133+static 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+
143+void 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
4343 extern const struct unittest test_linked_list;
4444 extern const struct unittest test_repack_sws;
4545 extern const struct unittest test_repack_zimg;
46+extern const struct unittest test_repack;
4647 extern const struct unittest test_paths;
4748
4849 #define assert_true(x) assert(x)
@@ -54,6 +55,10 @@ extern const struct unittest test_paths; · 5 unmodified lines
5455 #define assert_float_equal(a, b, tolerance) \
5556 assert_float_equal_impl(__FILE__, __LINE__, (a), (b), (tolerance))
5657
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+
5762 // Require that the files "ref" and "new" are the same. The paths can be
5863 // relative to ref_path and out_path respectively. If they're not the same,
5964 // 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
6974 void assert_text_files_equal_impl(const char *file, int line,
7075 struct test_ctx *ctx, const char *ref,
7176 const char *new, const char *err);
77+void assert_memcmp_impl(const char *file, int line,
78+ const void *a, const void *b, size_t size);
7279
7380 // Open a new file in the out_path. Always succeeds.
7481 FILE *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
6969 int flags; // MP_IMGFLAG_* bitfield
7070 int8_t num_planes;
7171 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 get
72+ int8_t align_x, align_y; // pixel count to get byte alignment and to get
7373 // to a pixel pos where luma & chroma aligns
74+ // always power of 2
7475 int8_t bytes[MP_MAX_PLANES]; // bytes per pixel (MP_IMGFLAG_BYTE_ALIGNED)
7576 int8_t bpp[MP_MAX_PLANES]; // bits per pixel
7677 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+
29+enum repack_step_type {
30+ REPACK_STEP_REPACK,
31+ REPACK_STEP_ENDIAN,
32+};
33+
34+struct 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+
43+struct 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
82+static 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
98+static 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.
115+static 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.
133+static 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+
216+UN_WORD_4(un_cccc8, uint32_t, uint8_t, 0, 8, 16, 24, 0xFFu)
217+PA_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.
219+UN_WORD_4(un_cccc16, uint64_t, uint16_t, 0, 16, 32, 48, 0xFFFFu)
220+PA_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+
232+UN_WORD_3(un_ccc8x8, uint32_t, uint8_t, 0, 8, 16, 0xFFu)
233+PA_WORD_3(pa_ccc8z8, uint32_t, uint8_t, 0, 8, 16, 0)
234+UN_WORD_3(un_x8ccc8, uint32_t, uint8_t, 8, 16, 24, 0xFFu)
235+PA_WORD_3(pa_z8ccc8, uint32_t, uint8_t, 8, 16, 24, 0)
236+UN_WORD_3(un_ccc10x2, uint32_t, uint16_t, 0, 10, 20, 0x3FFu)
237+PA_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+
257+UN_WORD_2(un_cc8, uint16_t, uint8_t, 0, 8, 0xFFu)
258+PA_WORD_2(pa_cc8, uint16_t, uint8_t, 0, 8, 0)
259+UN_WORD_2(un_cc16, uint32_t, uint16_t, 0, 16, 0xFFFFu)
260+PA_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+
282+UN_SEQ_3(un_ccc8, uint8_t)
283+PA_SEQ_3(pa_ccc8, uint8_t)
284+UN_SEQ_3(un_ccc16, uint16_t)
285+PA_SEQ_3(pa_ccc16, uint16_t)
286+
287+// "regular": single packed plane, all components have same width (except padding)
288+struct 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+
297+static 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+
309+static 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.
325+static 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+
397+struct 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+
409+static 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+
451+PA_SHIFT_LUT8(pa_shift_lut8_8, uint8_t)
452+PA_SHIFT_LUT8(pa_shift_lut8_16, uint16_t)
453+UN_SHIFT_LUT8(un_shift_lut8_8, uint8_t)
454+UN_SHIFT_LUT8(un_shift_lut8_16, uint16_t)
455+
456+static 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+
481+static 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+
545+static 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+
565+static 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+
590+static 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+
631+struct 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+
641+static 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+
672+PA_P422(pa_p422_8, uint8_t)
673+PA_P422(pa_p422_16, uint16_t)
674+UN_P422(un_p422_8, uint8_t)
675+UN_P422(un_p422_16, uint16_t)
676+
677+static 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+
698+static 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+
735+static 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+
752+static 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+
810+void 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+
865+static 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+
945+static 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+
959+static 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+
971+struct 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+
986+int mp_repack_get_format_src(struct mp_repack *rp)
987+{
988+ return rp->steps[0].fmt[0].id;
989+}
990+
991+int mp_repack_get_format_dst(struct mp_repack *rp)
992+{
993+ return rp->steps[rp->num_steps - 1].fmt[1].id;
994+}
995+
996+int 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+
1004+int 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+
1009+static 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+
1017+bool 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+
5+enum {
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+
15+struct mp_repack;
16+struct 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().
29+struct mp_repack *mp_repack_create_planar(int imgfmt, bool pack, int flags);
30+
31+// Return input and output formats for which rp was created.
32+int mp_repack_get_format_src(struct mp_repack *rp);
33+int 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.
39+int mp_repack_get_align_x(struct mp_repack *rp);
40+int 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.
48+void 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)
69+bool 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
2525 #include "csputils.h"
2626 #include "options/m_config.h"
2727 #include "options/m_option.h"
28+#include "repack.h"
2829 #include "video/fmt-conversion.h"
2930 #include "video/img_format.h"
3031 #include "zimg.h"
@@ -83,50 +84,14 @@ struct mp_zimg_repack { · 52 unmodified lines
8384 int num_planes; // number of planes involved
8485 unsigned zmask[4]; // zmask[mp_index] = zimg mask (using mp index!)
8586 int z_planes[4]; // z_planes[zimg_index] = mp_index (or -1)
86− bool pass_through_y; // luma plane optimization for e.g. nv12
8787
88− // If set, the pack/unpack callback to pass to zimg.
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
88+ struct mp_repack *repack; // converting to/from planar
10789
10890 // Temporary memory for slice-wise repacking. This may be set even if repack
10991 // is not set (then it may be used to avoid alignment issues). This has
11092 // about one slice worth of data.
11193 struct mp_image *tmp;
11294
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−
13095 int real_w, real_h; // aligned size
13196 };
13297
@@ -243,532 +208,44 @@ void mp_zimg_enable_cmdline_opts(struct mp_zimg_context *ctx, · 110 unmodified lines
243208 mp_zimg_update_from_cmdline(ctx); // first update
244209 }
245210
246−static 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.
280−static 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−
363−UN_WORD_4(un_cccc8, uint32_t, uint8_t, 0, 8, 16, 24, 0xFFu)
364−PA_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.
366−UN_WORD_4(un_cccc16, uint64_t, uint16_t, 0, 16, 32, 48, 0xFFFFu)
367−PA_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−
379−UN_WORD_3(un_ccc8x8, uint32_t, uint8_t, 0, 8, 16, 0xFFu)
380−PA_WORD_3(pa_ccc8z8, uint32_t, uint8_t, 0, 8, 16, 0)
381−UN_WORD_3(un_x8ccc8, uint32_t, uint8_t, 8, 16, 24, 0xFFu)
382−PA_WORD_3(pa_z8ccc8, uint32_t, uint8_t, 8, 16, 24, 0)
383−UN_WORD_3(un_ccc10x2, uint32_t, uint16_t, 0, 10, 20, 0x3FFu)
384−PA_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−
404−UN_WORD_2(un_cc8, uint16_t, uint8_t, 0, 8, 0xFFu)
405−PA_WORD_2(pa_cc8, uint16_t, uint8_t, 0, 8, 0)
406−UN_WORD_2(un_cc16, uint32_t, uint16_t, 0, 16, 0xFFFFu)
407−PA_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−
429−UN_SEQ_3(un_ccc8, uint8_t)
430−PA_SEQ_3(pa_ccc8, uint8_t)
431−UN_SEQ_3(un_ccc16, uint16_t)
432−PA_SEQ_3(pa_ccc16, uint16_t)
433−
434−// "regular": single packed plane, all components have same width (except padding)
435−struct 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−
444−static 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−
456−static 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−
475−struct 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−
487−static 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−
529−PA_SHIFT_LUT8(pa_shift_lut8_8, uint8_t)
530−PA_SHIFT_LUT8(pa_shift_lut8_16, uint16_t)
531−UN_SHIFT_LUT8(un_shift_lut8_8, uint8_t)
532−UN_SHIFT_LUT8(un_shift_lut8_16, uint16_t)
533−
534−static 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−
562−static 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−
598−static 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−
623−struct 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−
633−static 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−
664−PA_P422(pa_p422_8, uint8_t)
665−PA_P422(pa_p422_16, uint16_t)
666−UN_P422(un_p422_8, uint8_t)
667−UN_P422(un_p422_16, uint16_t)
668−
669−static 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−
694−static 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−
734211 static int repack_entrypoint(void *user, unsigned i, unsigned x0, unsigned x1)
735212 {
736213 struct mp_zimg_repack *r = user;
737214
738− if (r->endian_size && !r->pack) {
739− r->mpi = r->tmp_endian;
740− r->mpi_y0 = i;
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− }
215+ // If reading is not aligned, just read slightly more data.
216+ if (!r->pack)
217+ x0 &= ~(unsigned)(mp_repack_get_align_x(r->repack) - 1);
746218
747− if (r->repack) {
748− r->repack(r, i, x0, x1);
749− } else {
750− repack_align(r, i, x0, x1);
751− }
219+ // mp_repack requirements and zimg guarantees.
220+ assert(!(i & (mp_repack_get_align_y(r->repack) - 1)));
221+ assert(!(x0 & (mp_repack_get_align_x(r->repack) - 1)));
222+
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]);
752225
753− if (r->endian_size && r->pack)
754− swap_endian(r, r->user_mpi, i, r->mpi, i - r->mpi_y0, x0, x1);
226+ repack_line(r->repack, x0, i_dst, x0, i_src, x1 - x0);
755227
756− r->mpi = NULL;
757228 return 0;
758229 }
759230
760−static void wrap_buffer(struct mp_zimg_repack *r,
231+static bool wrap_buffer(struct mp_zimg_repack *r,
761232 zimg_image_buffer *buf,
762233 struct mp_image *mpi)
763234 {
764235 *buf = (zimg_image_buffer){ZIMG_API_VERSION};
765236
766− bool plane_aligned[4] = {0};
767− for (int n = 0; n < r->num_planes; n++) {
768− plane_aligned[n] = !((uintptr_t)mpi->planes[n] % ZIMG_ALIGN) &&
769− !(mpi->stride[n] % ZIMG_ALIGN);
237+ bool direct[MP_MAX_PLANES] = {0};
238+
239+ for (int p = 0; p < mpi->num_planes; p++) {
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);
770243 }
771244
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+
772249 for (int n = 0; n < MP_ARRAY_SIZE(buf->plane); n++) {
773250 // Note: this is really the only place we have to care about plane
774251 // 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
778255 if (mplane < 0)
779256 continue;
780257
781− r->use_buf[mplane] = !plane_aligned[mplane] || r->endian_size;
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;
258+ struct mp_image *tmpi = direct[mplane] ? mpi : r->tmp;
786259 buf->plane[n].data = tmpi->planes[mplane];
787260 buf->plane[n].stride = tmpi->stride[mplane];
788− buf->plane[n].mask = r->use_buf[mplane] ? r->zmask[mplane]
789− : ZIMG_BUFFER_MAX;
261+ buf->plane[n].mask = direct[mplane] ? ZIMG_BUFFER_MAX : r->zmask[mplane];
790262 }
791263
792− r->user_mpi = mpi;
793−}
794−
795−// depth = number of LSB in use
796−static 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
812−static 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);
264+ return true;
824265 }
825266
826−static void setup_fringe_rgb_packer(struct mp_zimg_repack *r,
827− struct mp_zimg_context *ctx)
267+// (ctx can be NULL for probing.)
268+static 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)
828271 {
829− enum AVPixelFormat avfmt = imgfmt2pixfmt(r->zimgfmt);
272+ r->fmt = *user_fmt;
273+ r->pack = pack;
830274
831− const struct fringe_rgb_repacker *fmt = NULL;
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− }
275+ zimg_image_format_default(zfmt, ZIMG_API_VERSION);
838276
839− if (!fmt)
840− return;
277+ int rp_flags = 0;
841278
842− int depth = 8;
279+ // For e.g. RGB565, go to lowest depth on pack for less weird dithering.
843280 if (r->pack) {
844− // Dither to lowest depth - loses some precision, but result is saner.
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−
890−static 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−
928−static 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−
986−static 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;
281+ rp_flags |= REPACK_CREATE_ROUND_DOWN;
1006282 } else {
1007− enum AVPixelFormat avfmt = imgfmt2pixfmt(r->zimgfmt);
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− }
283+ rp_flags |= REPACK_CREATE_EXPAND_8BIT;
1014284 }
1015−}
1016−
1017−// Tries to set a packer/unpacker for component-wise byte aligned RGB formats.
1018−static 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;
1023285
1024− if (desc.num_planes != 1 || desc.planes[0].num_components < 2)
1025− return;
1026− struct mp_regular_imgfmt_plane *p = &desc.planes[0];
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− }
286+ r->repack = mp_repack_create_planar(r->fmt.imgfmt, r->pack, rp_flags);
287+ if (!r->repack)
288+ return false;
1040289
1041− int depth = desc.component_size * 8 + MPMIN(0, desc.component_pad);
290+ int align_x = mp_repack_get_align_x(r->repack);
1042291
1043− int planar_fmt = num_real_components > 2
1044− ? find_gbrp_format(depth, num_real_components)
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;
292+ r->zimgfmt = r->pack ? mp_repack_get_format_src(r->repack)
293+ : mp_repack_get_format_dst(r->repack);
1069294
1070− r->repack = packed_repack;
1071− r->packed_repack_scanline = repack_cb;
1072− r->zimgfmt = planar_fmt;
1073− for (int n = 0; n < num_real_components; n++) {
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;
295+ if (ctx) {
296+ talloc_steal(r, r->repack);
297+ } else {
298+ TA_FREEP(&r->repack);
1080299 }
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.)
1085−static 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);
1089300
1090301 struct mp_image_params fmt = r->fmt;
1091302 mp_image_params_guess_csp(&fmt);
1092303
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−
1106304 struct mp_regular_imgfmt desc;
1107305 if (!mp_get_regular_imgfmt(&desc, r->zimgfmt))
1108306 return false;
1109307
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+
1110312 // no weird stuff
1111313 if (desc.num_planes > 4)
1112314 return false;
1113315
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−
1126316 for (int n = 0; n < 4; n++)
1127317 r->z_planes[n] = -1;
1128318
1129− // Accept only true planar formats.
1130319 for (int n = 0; n < desc.num_planes; n++) {
1131320 if (desc.planes[n].num_components != 1)
1132321 return false;
@@ -1219,25 +408,6 @@ static bool setup_format_ne(zimg_image_format *zfmt, struct mp_zimg_repack *r, · 86 unmodified lines
1219408 return true;
1220409 }
1221410
1222−static 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−
1241411 static bool allocate_buffer(struct mp_zimg_context *ctx,
1242412 struct mp_zimg_repack *r)
1243413 {
@@ -1269,20 +439,15 @@ static bool allocate_buffer(struct mp_zimg_context *ctx, · 25 unmodified lines
1269439 if (!r->tmp)
1270440 return false;
1271441
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.
1272445 for (int n = 1; n < r->tmp->fmt.num_planes; n++) {
1273446 r->zmask[n] = r->zmask[0];
1274447 if (r->zmask[0] != ZIMG_BUFFER_MAX)
1275448 r->zmask[n] = r->zmask[n] >> r->tmp->fmt.ys[n];
1276449 }
1277450
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−
1286451 return true;
1287452 }
1288453
@@ -1382,8 +547,12 @@ bool mp_zimg_convert(struct mp_zimg_context *ctx, struct mp_image *dst, · 93 unmodified lines
1382547 assert(ctx->zimg_graph);
1383548
1384549 zimg_image_buffer zsrc, zdst;
1385− wrap_buffer(ctx->zimg_src, &zsrc, src);
1386− wrap_buffer(ctx->zimg_dst, &zdst, dst);
550+ if (!wrap_buffer(ctx->zimg_src, &zsrc, src) ||
551+ !wrap_buffer(ctx->zimg_dst, &zdst, dst))
552+ {
553+ MP_ERR(ctx, "zimg repacker initialization failed.\n");
554+ return false;
555+ }
1387556
1388557 // An annoyance.
1389558 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
1400569 repack_entrypoint, ctx->zimg_src,
1401570 repack_entrypoint, ctx->zimg_dst);
1402571
1403− ctx->zimg_src->user_mpi = NULL;
1404− ctx->zimg_dst->user_mpi = NULL;
1405−
1406572 return true;
1407573 }
1408574
wscript_build.py+2−0
@@ -402,6 +402,7 @@ def build(ctx): · 401 unmodified lines
402402 ( "test/json.c", "tests" ),
403403 ( "test/linked_list.c", "tests" ),
404404 ( "test/paths.c", "tests" ),
405+ ( "test/repack.c", "tests && zimg" ),
405406 ( "test/scale_sws.c", "tests" ),
406407 ( "test/scale_test.c", "tests" ),
407408 ( "test/scale_zimg.c", "tests && zimg" ),
@@ -529,6 +530,7 @@ def build(ctx): · 121 unmodified lines
529530 ( "video/out/win32/droptarget.c", "win32-desktop" ),
530531 ( "video/out/win_state.c"),
531532 ( "video/out/x11_common.c", "x11" ),
533+ ( "video/repack.c" ),
532534 ( "video/sws_utils.c" ),
533535 ( "video/zimg.c", "zimg" ),
534536 ( "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