mpv/test/repack.c

520 lines
21 KiB
C
Raw Normal View History

#include <libavutil/pixfmt.h>
#include "common/common.h"
draw_bmp: rewrite draw_bmp.c is the software blender for subtitles and OSD. It's used by encoding mode (burning subtitles), and some VOs, like vo_drm, vo_x11, vo_xv, and possibly more. This changes the algorithm from upsampling the video to 4:4:4 and then blending to downsampling the OSD and then blending directly to video. This has far-reaching consequences for its internals, and results in an effective rewrite. Since I wanted to avoid un-premultiplying, all blending is done with premultiplied alpha. That's actually the sane thing to do. The old code just didn't do it, because it's very weird in YUV fixed point. Essentially, you'd have to compensate for the chroma centering constant by subtracting src_alpha/255*128. This seemed so hairy (especially with correct rounding and high bit depths involved) that I went for using float. I think it turned out mostly OK, although it's more complex and less maintainable than before. reinit() is certainly a bit too long. While it should be possible to optimize the RGB path more (for example by blending directly instead of doing the stupid float conversion), this is probably slower. vo_xv users probably lose in this, because it takes the slowest path (due to subsampling requirements and using YUV). Why this rewrite? Nobody knows. I simply forgot the reason. But you'll have it anyway. Whether or not this would have required a full rewrite, at least it supports target alpha now (you can for example hard sub transparent PNGs, if you ever wanted to use mpv for this). Remove the check in vf_sub. The new draw_bmp.c is not as reliant on libswscale anymore (mostly uses repack.c now), and osd.c shows an error message on missing support instead now. Formats with chroma subsampling of 4 are not supported, because FFmpeg doesn't provide pixfmt definitions for alpha variants. We could provide those ourselves (relatively trivial), but why bother.
2020-05-09 16:01:07 +00:00
#include "sub/draw_bmp.h"
#include "sub/osd.h"
#include "tests.h"
#include "video/fmt-conversion.h"
#include "video/img_format.h"
#include "video/repack.h"
#include "video/sws_utils.h"
#include "video/zimg.h"
// Excuse the utter stupidity.
#define UNFUCK(v) ((v) > 0 ? (v) : pixfmt2imgfmt(-(v)))
static_assert(IMGFMT_START > 0, "");
#define IMGFMT_GBRP (-AV_PIX_FMT_GBRP)
#define IMGFMT_GBRAP (-AV_PIX_FMT_GBRAP)
struct entry {
int w, h;
int fmt_a;
const void *const a[4];
int fmt_b;
const void *const b[4];
int flags;
};
#define P8(...) (const uint8_t[]){__VA_ARGS__}
#define P16(...) (const uint16_t[]){__VA_ARGS__}
#define P32(...) (const uint32_t[]){__VA_ARGS__}
#define SW16(v) ((((v) & 0xFF) << 8) | ((v) >> 8))
#define SW32(v) ((SW16((v) & 0xFFFFu) << 16) | (SW16(((v) | 0u) >> 16)))
// Warning: only entries that match existing conversions are tested.
static const struct entry repack_tests[] = {
// Note: the '0' tests rely on 0 being written, although by definition the
// contents of this padding is undefined. The repacker always writes
// it this way, though.
{1, 1, IMGFMT_RGB0, {P8(1, 2, 3, 0)},
IMGFMT_GBRP, {P8(2), P8(3), P8(1)}},
{1, 1, IMGFMT_BGR0, {P8(1, 2, 3, 0)},
IMGFMT_GBRP, {P8(2), P8(1), P8(3)}},
{1, 1, IMGFMT_0RGB, {P8(0, 1, 2, 3)},
IMGFMT_GBRP, {P8(2), P8(3), P8(1)}},
{1, 1, IMGFMT_0BGR, {P8(0, 1, 2, 3)},
IMGFMT_GBRP, {P8(2), P8(1), P8(3)}},
{1, 1, IMGFMT_RGBA, {P8(1, 2, 3, 4)},
IMGFMT_GBRAP, {P8(2), P8(3), P8(1), P8(4)}},
{1, 1, IMGFMT_BGRA, {P8(1, 2, 3, 4)},
IMGFMT_GBRAP, {P8(2), P8(1), P8(3), P8(4)}},
{1, 1, IMGFMT_ARGB, {P8(4, 1, 2, 3)},
IMGFMT_GBRAP, {P8(2), P8(3), P8(1), P8(4)}},
{1, 1, IMGFMT_ABGR, {P8(4, 1, 2, 3)},
IMGFMT_GBRAP, {P8(2), P8(1), P8(3), P8(4)}},
{1, 1, IMGFMT_BGR24, {P8(1, 2, 3)},
IMGFMT_GBRP, {P8(2), P8(1), P8(3)}},
{1, 1, IMGFMT_RGB24, {P8(1, 2, 3)},
IMGFMT_GBRP, {P8(2), P8(3), P8(1)}},
{1, 1, IMGFMT_RGBA64, {P16(0x1a1b, 0x2a2b, 0x3a3b, 0x4a4b)},
-AV_PIX_FMT_GBRAP16, {P16(0x2a2b), P16(0x3a3b),
P16(0x1a1b), P16(0x4a4b)}},
{1, 1, -AV_PIX_FMT_BGRA64LE, {P16(0x1a1b, 0x2a2b, 0x3a3b, 0x4a4b)},
-AV_PIX_FMT_GBRAP16, {P16(0x2a2b), P16(0x1a1b),
P16(0x3a3b), P16(0x4a4b)}},
{1, 1, -AV_PIX_FMT_RGBA64BE, {P16(0x1b1a, 0x2b2a, 0x3b3a, 0x4b4a)},
-AV_PIX_FMT_GBRAP16, {P16(0x2a2b), P16(0x3a3b),
P16(0x1a1b), P16(0x4a4b)}},
{1, 1, -AV_PIX_FMT_BGRA64BE, {P16(0x1b1a, 0x2b2a, 0x3b3a, 0x4b4a)},
-AV_PIX_FMT_GBRAP16, {P16(0x2a2b), P16(0x1a1b),
P16(0x3a3b), P16(0x4a4b)}},
{1, 1, -AV_PIX_FMT_RGB48BE, {P16(0x1a1b, 0x2a2b, 0x3a3b)},
-AV_PIX_FMT_GBRP16, {P16(0x2b2a), P16(0x3b3a), P16(0x1b1a)}},
{1, 1, -AV_PIX_FMT_RGB48LE, {P16(0x1a1b, 0x2a2b, 0x3a3b)},
-AV_PIX_FMT_GBRP16, {P16(0x2a2b), P16(0x3a3b), P16(0x1a1b)}},
{1, 1, -AV_PIX_FMT_BGR48BE, {P16(0x1a1b, 0x2a2b, 0x3a3b)},
-AV_PIX_FMT_GBRP16, {P16(0x2b2a), P16(0x1b1a), P16(0x3b3a)}},
{1, 1, -AV_PIX_FMT_BGR48LE, {P16(0x1a1b, 0x2a2b, 0x3a3b)},
-AV_PIX_FMT_GBRP16, {P16(0x2a2b), P16(0x1a1b), P16(0x3a3b)}},
{1, 1, -AV_PIX_FMT_XYZ12LE, {P16(0x1a1b, 0x2a2b, 0x3a3b)},
-AV_PIX_FMT_GBRP16, {P16(0x2a2b), P16(0x3a3b), P16(0x1a1b)}},
{1, 1, -AV_PIX_FMT_XYZ12BE, {P16(0x1b1a, 0x2b2a, 0x3b3a)},
-AV_PIX_FMT_GBRP16, {P16(0x2a2b), P16(0x3a3b), P16(0x1a1b)}},
{3, 1, -AV_PIX_FMT_BGR8, {P8(7, (7 << 3), (3 << 6))},
IMGFMT_GBRP, {P8(0,0xFF,0), P8(0,0,0xFF), P8(0xFF,0,0)},
.flags = REPACK_CREATE_EXPAND_8BIT},
{3, 1, -AV_PIX_FMT_RGB8, {P8(3, (7 << 2), (7 << 5))},
IMGFMT_GBRP, {P8(0,0xFF,0), P8(0xFF,0,0), P8(0,0,0xFF)},
.flags = REPACK_CREATE_EXPAND_8BIT},
{3, 1, -AV_PIX_FMT_BGR4_BYTE, {P8(1, (3 << 1), (1 << 3))},
IMGFMT_GBRP, {P8(0,0xFF,0), P8(0,0,0xFF), P8(0xFF,0,0)},
.flags = REPACK_CREATE_EXPAND_8BIT},
{3, 1, -AV_PIX_FMT_RGB4_BYTE, {P8(1, (3 << 1), (1 << 3))},
IMGFMT_GBRP, {P8(0,0xFF,0), P8(0xFF,0,0), P8(0,0,0xFF)},
.flags = REPACK_CREATE_EXPAND_8BIT},
{3, 1, -AV_PIX_FMT_RGB565LE, {P16((31), (63 << 5), (31 << 11))},
IMGFMT_GBRP, {P8(0,0xFF,0), P8(0xFF,0,0), P8(0,0,0xFF)},
.flags = REPACK_CREATE_EXPAND_8BIT},
{3, 1, -AV_PIX_FMT_RGB565BE, {P16(SW16(31), SW16(63 << 5), SW16(31 << 11))},
IMGFMT_GBRP, {P8(0,0xFF,0), P8(0xFF,0,0), P8(0,0,0xFF)},
.flags = REPACK_CREATE_EXPAND_8BIT},
{3, 1, -AV_PIX_FMT_BGR565LE, {P16((31), (63 << 5), (31 << 11))},
IMGFMT_GBRP, {P8(0,0xFF,0), P8(0,0,0xFF), P8(0xFF,0,0)},
.flags = REPACK_CREATE_EXPAND_8BIT},
{3, 1, -AV_PIX_FMT_BGR565BE, {P16(SW16(31), SW16(63 << 5), SW16(31 << 11))},
IMGFMT_GBRP, {P8(0,0xFF,0), P8(0,0,0xFF), P8(0xFF,0,0)},
.flags = REPACK_CREATE_EXPAND_8BIT},
{3, 1, -AV_PIX_FMT_RGB555LE, {P16((31), (31 << 5), (31 << 10))},
IMGFMT_GBRP, {P8(0,0xFF,0), P8(0xFF,0,0), P8(0,0,0xFF)},
.flags = REPACK_CREATE_EXPAND_8BIT},
{3, 1, -AV_PIX_FMT_RGB555BE, {P16(SW16(31), SW16(31 << 5), SW16(31 << 10))},
IMGFMT_GBRP, {P8(0,0xFF,0), P8(0xFF,0,0), P8(0,0,0xFF)},
.flags = REPACK_CREATE_EXPAND_8BIT},
{3, 1, -AV_PIX_FMT_BGR555LE, {P16((31), (31 << 5), (31 << 10))},
IMGFMT_GBRP, {P8(0,0xFF,0), P8(0,0,0xFF), P8(0xFF,0,0)},
.flags = REPACK_CREATE_EXPAND_8BIT},
{3, 1, -AV_PIX_FMT_BGR555BE, {P16(SW16(31), SW16(31 << 5), SW16(31 << 10))},
IMGFMT_GBRP, {P8(0,0xFF,0), P8(0,0,0xFF), P8(0xFF,0,0)},
.flags = REPACK_CREATE_EXPAND_8BIT},
{3, 1, -AV_PIX_FMT_RGB444LE, {P16((15), (15 << 4), (15 << 8))},
IMGFMT_GBRP, {P8(0,0xFF,0), P8(0xFF,0,0), P8(0,0,0xFF)},
.flags = REPACK_CREATE_EXPAND_8BIT},
{3, 1, -AV_PIX_FMT_RGB444BE, {P16(SW16(15), SW16(15 << 4), SW16(15 << 8))},
IMGFMT_GBRP, {P8(0,0xFF,0), P8(0xFF,0,0), P8(0,0,0xFF)},
.flags = REPACK_CREATE_EXPAND_8BIT},
{3, 1, -AV_PIX_FMT_BGR444LE, {P16((15), (15 << 4), (15 << 8))},
IMGFMT_GBRP, {P8(0,0xFF,0), P8(0,0,0xFF), P8(0xFF,0,0)},
.flags = REPACK_CREATE_EXPAND_8BIT},
{3, 1, -AV_PIX_FMT_BGR444BE, {P16(SW16(15), SW16(15 << 4), SW16(15 << 8))},
IMGFMT_GBRP, {P8(0,0xFF,0), P8(0,0,0xFF), P8(0xFF,0,0)},
.flags = REPACK_CREATE_EXPAND_8BIT},
{1, 1, IMGFMT_RGB30, {P32((3 << 20) | (2 << 10) | 1)},
-AV_PIX_FMT_GBRP10, {P16(2), P16(1), P16(3)}},
{1, 1, -AV_PIX_FMT_X2RGB10BE, {P32(SW32((3 << 20) | (2 << 10) | 1))},
-AV_PIX_FMT_GBRP10, {P16(2), P16(1), P16(3)}},
{8, 1, -AV_PIX_FMT_MONOWHITE, {P8(0xAA)},
IMGFMT_Y1, {P8(0, 1, 0, 1, 0, 1, 0, 1)}},
{8, 1, -AV_PIX_FMT_MONOBLACK, {P8(0xAA)},
IMGFMT_Y1, {P8(1, 0, 1, 0, 1, 0, 1, 0)}},
{2, 2, IMGFMT_NV12, {P8(1, 2, 3, 4), P8(5, 6)},
IMGFMT_420P, {P8(1, 2, 3, 4), P8(5), P8(6)}},
{2, 2, -AV_PIX_FMT_NV21, {P8(1, 2, 3, 4), P8(5, 6)},
IMGFMT_420P, {P8(1, 2, 3, 4), P8(6), P8(5)}},
{1, 1, -AV_PIX_FMT_AYUV64LE, {P16(1, 2, 3, 4)},
-AV_PIX_FMT_YUVA444P16, {P16(2), P16(3), P16(4), P16(1)}},
{1, 1, -AV_PIX_FMT_AYUV64BE, {P16(0x0100, 0x0200, 0x0300, 0x0400)},
-AV_PIX_FMT_YUVA444P16, {P16(2), P16(3), P16(4), P16(1)}},
{4, 1, -AV_PIX_FMT_YUYV422, {P8(1, 2, 3, 4, 5, 6, 7, 8)},
-AV_PIX_FMT_YUV422P, {P8(1, 3, 5, 7), P8(2, 6), P8(4, 8)}},
{2, 1, -AV_PIX_FMT_YVYU422, {P8(1, 2, 3, 4)},
-AV_PIX_FMT_YUV422P, {P8(1, 3), P8(4), P8(2)}},
{2, 1, -AV_PIX_FMT_UYVY422, {P8(1, 2, 3, 4)},
-AV_PIX_FMT_YUV422P, {P8(2, 4), P8(1), P8(3)}},
{2, 1, -AV_PIX_FMT_Y210LE, {P16(0x1a1b, 0x2a2b, 0x3a3b, 0x4a4b)},
-AV_PIX_FMT_YUV422P16, {P16(0x1a1b, 0x3a3b), P16(0x2a2b), P16(0x4a4b)}},
{2, 1, -AV_PIX_FMT_Y210BE, {P16(0x1b1a, 0x2b2a, 0x3b3a, 0x4b4a)},
-AV_PIX_FMT_YUV422P16, {P16(0x1a1b, 0x3a3b), P16(0x2a2b), P16(0x4a4b)}},
{1, 1, -AV_PIX_FMT_YA8, {P8(1, 2)},
IMGFMT_YAP8, {P8(1), P8(2)}},
{1, 1, -AV_PIX_FMT_YA16, {P16(0x1a1b, 0x2a2b)},
IMGFMT_YAP16, {P16(0x1a1b), P16(0x2a2b)}},
{2, 1, -AV_PIX_FMT_YUV422P16BE, {P16(0x1a1b, 0x2a2b), P16(0x3a3b),
P16(0x4a4b)},
-AV_PIX_FMT_YUV422P16, {P16(0x1b1a, 0x2b2a), P16(0x3b3a),
P16(0x4b4a)}},
{8, 1, -AV_PIX_FMT_UYYVYY411, {P8(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12)},
-AV_PIX_FMT_YUV411P, {P8(2, 3, 5, 6, 8, 9, 11, 12),
P8(1, 7), P8(4, 10)}},
};
static bool is_true_planar(int imgfmt)
{
struct mp_regular_imgfmt desc;
if (!mp_get_regular_imgfmt(&desc, imgfmt))
return false;
for (int n = 0; n < desc.num_planes; n++) {
if (desc.planes[n].num_components != 1)
return false;
}
return true;
}
static int try_repack(struct test_ctx *ctx, FILE *f, int imgfmt, int flags,
int not_if_fmt)
{
char *head = mp_tprintf(80, "%-15s =>", mp_imgfmt_to_name(imgfmt));
struct mp_repack *un = mp_repack_create_planar(imgfmt, false, flags);
struct mp_repack *pa = mp_repack_create_planar(imgfmt, true, flags);
// If both exists, they must be always symmetric.
if (un && pa) {
assert(mp_repack_get_format_src(pa) == mp_repack_get_format_dst(un));
assert(mp_repack_get_format_src(un) == mp_repack_get_format_dst(pa));
assert(mp_repack_get_align_x(pa) == mp_repack_get_align_x(un));
assert(mp_repack_get_align_y(pa) == mp_repack_get_align_y(un));
}
int a = 0;
int b = 0;
if (un) {
a = mp_repack_get_format_src(un);
b = mp_repack_get_format_dst(un);
} else if (pa) {
a = mp_repack_get_format_dst(pa);
b = mp_repack_get_format_src(pa);
}
// Skip the identity ones because they're uninteresting, and add too much
// noise. But still make sure they behave as expected.
if (a == imgfmt && b == imgfmt) {
assert(is_true_planar(imgfmt));
// (note that we require alpha-enabled zimg)
assert(mp_zimg_supports_in_format(imgfmt));
assert(un && pa);
talloc_free(pa);
talloc_free(un);
return b;
}
struct mp_repack *rp = pa ? pa : un;
if (!rp) {
if (!flags)
fprintf(f, "%s no\n", head);
return 0;
}
assert(a == imgfmt);
if (b && b == not_if_fmt) {
talloc_free(pa);
talloc_free(un);
return 0;
}
fprintf(f, "%s %4s %4s %-15s |", head, pa ? "[pa]" : "", un ? "[un]" : "",
mp_imgfmt_to_name(b));
fprintf(f, " a=%d:%d", mp_repack_get_align_x(rp), mp_repack_get_align_y(rp));
if (flags & REPACK_CREATE_PLANAR_F32)
fprintf(f, " [planar-f32]");
if (flags & REPACK_CREATE_ROUND_DOWN)
fprintf(f, " [round-down]");
if (flags & REPACK_CREATE_EXPAND_8BIT)
fprintf(f, " [expand-8bit]");
// LCM of alignment of all packers.
int ax = mp_repack_get_align_x(rp);
int ay = mp_repack_get_align_y(rp);
if (pa && un) {
ax = MPMAX(mp_repack_get_align_x(pa), mp_repack_get_align_x(un));
ay = MPMAX(mp_repack_get_align_y(pa), mp_repack_get_align_y(un));
}
for (int n = 0; n < MP_ARRAY_SIZE(repack_tests); n++) {
const struct entry *e = &repack_tests[n];
int fmt_a = UNFUCK(e->fmt_a);
int fmt_b = UNFUCK(e->fmt_b);
if (!(fmt_a == a && fmt_b == b && e->flags == flags))
continue;
// We convert a "random" macro pixel to catch potential addressing bugs
// that might be ignored with (0, 0) origins.
struct mp_image *ia = mp_image_alloc(fmt_a, e->w * 5 * ax, e->h * 5 * ay);
struct mp_image *ib = mp_image_alloc(fmt_b, e->w * 7 * ax, e->h * 6 * ay);
int sx = 4 * ax, sy = 3 * ay, dx = 3 * ax, dy = 2 * ay;
assert(ia && ib);
mp_image_params_guess_csp(&ia->params);
mp_image_params_guess_csp(&ib->params);
for (int pack = 0; pack < 2; pack++) {
struct mp_repack *repacker = pack ? pa : un;
if (!repacker)
continue;
mp_image_clear(ia, 0, 0, ia->w, ia->h);
mp_image_clear(ib, 0, 0, ib->w, ib->h);
const void *const *dstd = pack ? e->a : e->b;
const void *const *srcd = pack ? e->b : e->a;
struct mp_image *dsti = pack ? ia : ib;
struct mp_image *srci = pack ? ib : ia;
bool r = repack_config_buffers(repacker, 0, dsti, 0, srci, NULL);
assert(r);
for (int p = 0; p < srci->num_planes; p++) {
uint8_t *ptr = mp_image_pixel_ptr(srci, p, sx, sy);
for (int y = 0; y < e->h >> srci->fmt.ys[p]; y++) {
int wb = mp_image_plane_bytes(srci, p, 0, e->w);
const void *cptr = (uint8_t *)srcd[p] + wb * y;
memcpy(ptr + srci->stride[p] * y, cptr, wb);
}
}
repack_line(repacker, dx, dy, sx, sy, e->w);
for (int p = 0; p < dsti->num_planes; p++) {
uint8_t *ptr = mp_image_pixel_ptr(dsti, p, dx, dy);
for (int y = 0; y < e->h >> dsti->fmt.ys[p]; y++) {
int wb = mp_image_plane_bytes(dsti, p, 0, e->w);
const void *cptr = (uint8_t *)dstd[p] + wb * y;
assert_memcmp(ptr + dsti->stride[p] * y, cptr, wb);
}
}
fprintf(f, " [t%s]", pack ? "p" : "u");
}
talloc_free(ia);
talloc_free(ib);
}
fprintf(f, "\n");
talloc_free(pa);
talloc_free(un);
return b;
}
static void check_float_repack(int imgfmt, enum mp_csp csp,
enum mp_csp_levels levels)
{
imgfmt = UNFUCK(imgfmt);
struct mp_regular_imgfmt desc = {0};
mp_get_regular_imgfmt(&desc, imgfmt);
int bpp = desc.component_size;
int comp_bits = desc.component_size * 8 + MPMIN(desc.component_pad, 0);
assert(bpp == 1 || bpp == 2);
int w = 1 << (bpp * 8);
struct mp_image *src = mp_image_alloc(imgfmt, w, 1);
assert(src);
src->params.color.space = csp;
src->params.color.levels = levels;
mp_image_params_guess_csp(&src->params);
// mpv may not allow all combinations
assert(src->params.color.space == csp);
assert(src->params.color.levels == levels);
for (int p = 0; p < src->num_planes; p++) {
int val = 0;
for (int x = 0; x < w >> src->fmt.xs[p]; x++) {
val = MPMIN(val, (1 << comp_bits) - 1);
void *pixel = mp_image_pixel_ptr(src, p, x, 0);
if (bpp == 1) {
*(uint8_t *)pixel = val;
} else {
*(uint16_t *)pixel = val;
}
val++;
}
}
struct mp_repack *to_f =
mp_repack_create_planar(src->imgfmt, false, REPACK_CREATE_PLANAR_F32);
struct mp_repack *from_f =
mp_repack_create_planar(src->imgfmt, true, REPACK_CREATE_PLANAR_F32);
assert(to_f && from_f);
struct mp_image *z_f = mp_image_alloc(mp_repack_get_format_dst(to_f), w, 1);
struct mp_image *r_f = mp_image_alloc(z_f->imgfmt, w, 1);
struct mp_image *z_i = mp_image_alloc(src->imgfmt, w, 1);
struct mp_image *r_i = mp_image_alloc(src->imgfmt, w, 1);
assert(z_f && r_f && z_i && r_i);
z_f->params.color = r_f->params.color = z_i->params.color =
r_i->params.color = src->params.color;
// The idea is to use zimg to cross-check conversion.
struct mp_sws_context *s = mp_sws_alloc(NULL);
s->force_scaler = MP_SWS_ZIMG;
struct zimg_opts opts = zimg_opts_defaults;
opts.dither = ZIMG_DITHER_NONE;
s->zimg_opts = &opts;
mp_sws_scale(s, z_f, src);
mp_sws_scale(s, z_i, z_f);
talloc_free(s);
repack_config_buffers(to_f, 0, r_f, 0, src, NULL);
repack_line(to_f, 0, 0, 0, 0, w);
repack_config_buffers(from_f, 0, r_i, 0, r_f, NULL);
repack_line(from_f, 0, 0, 0, 0, w);
for (int p = 0; p < src->num_planes; p++) {
for (int x = 0; x < w >> src->fmt.xs[p]; x++) {
uint32_t src_val, z_i_val, r_i_val;
if (bpp == 1) {
src_val = *(uint8_t *)mp_image_pixel_ptr(src, p, x, 0);
z_i_val = *(uint8_t *)mp_image_pixel_ptr(z_i, p, x, 0);
r_i_val = *(uint8_t *)mp_image_pixel_ptr(r_i, p, x, 0);
} else {
src_val = *(uint16_t *)mp_image_pixel_ptr(src, p, x, 0);
z_i_val = *(uint16_t *)mp_image_pixel_ptr(z_i, p, x, 0);
r_i_val = *(uint16_t *)mp_image_pixel_ptr(r_i, p, x, 0);
}
float z_f_val = *(float *)mp_image_pixel_ptr(z_f, p, x, 0);
float r_f_val = *(float *)mp_image_pixel_ptr(r_f, p, x, 0);
assert_int_equal(src_val, z_i_val);
assert_int_equal(src_val, r_i_val);
double tolerance = 1.0 / (1 << (bpp * 8)) / 4;
assert_float_equal(r_f_val, z_f_val, tolerance);
}
}
talloc_free(src);
talloc_free(z_i);
talloc_free(z_f);
talloc_free(r_i);
talloc_free(r_f);
talloc_free(to_f);
talloc_free(from_f);
}
static bool try_draw_bmp(struct mpv_global *g, FILE *f, int imgfmt)
draw_bmp: rewrite draw_bmp.c is the software blender for subtitles and OSD. It's used by encoding mode (burning subtitles), and some VOs, like vo_drm, vo_x11, vo_xv, and possibly more. This changes the algorithm from upsampling the video to 4:4:4 and then blending to downsampling the OSD and then blending directly to video. This has far-reaching consequences for its internals, and results in an effective rewrite. Since I wanted to avoid un-premultiplying, all blending is done with premultiplied alpha. That's actually the sane thing to do. The old code just didn't do it, because it's very weird in YUV fixed point. Essentially, you'd have to compensate for the chroma centering constant by subtracting src_alpha/255*128. This seemed so hairy (especially with correct rounding and high bit depths involved) that I went for using float. I think it turned out mostly OK, although it's more complex and less maintainable than before. reinit() is certainly a bit too long. While it should be possible to optimize the RGB path more (for example by blending directly instead of doing the stupid float conversion), this is probably slower. vo_xv users probably lose in this, because it takes the slowest path (due to subsampling requirements and using YUV). Why this rewrite? Nobody knows. I simply forgot the reason. But you'll have it anyway. Whether or not this would have required a full rewrite, at least it supports target alpha now (you can for example hard sub transparent PNGs, if you ever wanted to use mpv for this). Remove the check in vf_sub. The new draw_bmp.c is not as reliant on libswscale anymore (mostly uses repack.c now), and osd.c shows an error message on missing support instead now. Formats with chroma subsampling of 4 are not supported, because FFmpeg doesn't provide pixfmt definitions for alpha variants. We could provide those ourselves (relatively trivial), but why bother.
2020-05-09 16:01:07 +00:00
{
bool ok = false;
struct mp_image *dst = mp_image_alloc(imgfmt, 64, 64);
if (!dst)
goto done;
draw_bmp: rewrite draw_bmp.c is the software blender for subtitles and OSD. It's used by encoding mode (burning subtitles), and some VOs, like vo_drm, vo_x11, vo_xv, and possibly more. This changes the algorithm from upsampling the video to 4:4:4 and then blending to downsampling the OSD and then blending directly to video. This has far-reaching consequences for its internals, and results in an effective rewrite. Since I wanted to avoid un-premultiplying, all blending is done with premultiplied alpha. That's actually the sane thing to do. The old code just didn't do it, because it's very weird in YUV fixed point. Essentially, you'd have to compensate for the chroma centering constant by subtracting src_alpha/255*128. This seemed so hairy (especially with correct rounding and high bit depths involved) that I went for using float. I think it turned out mostly OK, although it's more complex and less maintainable than before. reinit() is certainly a bit too long. While it should be possible to optimize the RGB path more (for example by blending directly instead of doing the stupid float conversion), this is probably slower. vo_xv users probably lose in this, because it takes the slowest path (due to subsampling requirements and using YUV). Why this rewrite? Nobody knows. I simply forgot the reason. But you'll have it anyway. Whether or not this would have required a full rewrite, at least it supports target alpha now (you can for example hard sub transparent PNGs, if you ever wanted to use mpv for this). Remove the check in vf_sub. The new draw_bmp.c is not as reliant on libswscale anymore (mostly uses repack.c now), and osd.c shows an error message on missing support instead now. Formats with chroma subsampling of 4 are not supported, because FFmpeg doesn't provide pixfmt definitions for alpha variants. We could provide those ourselves (relatively trivial), but why bother.
2020-05-09 16:01:07 +00:00
struct sub_bitmap sb = {
.bitmap = &(uint8_t[]){123},
.stride = 1,
.x = 1,
.y = 1,
.w = 1, .dw = 1,
.h = 1, .dh = 1,
.libass = { .color = 0xDEDEDEDE },
};
struct sub_bitmaps sbs = {
.format = SUBBITMAP_LIBASS,
.parts = &sb,
.num_parts = 1,
.change_id = 1,
};
struct sub_bitmap_list sbs_list = {
.change_id = 1,
.w = dst->w,
.h = dst->h,
.items = (struct sub_bitmaps *[]){&sbs},
.num_items = 1,
};
struct mp_draw_sub_cache *c = mp_draw_sub_alloc(NULL, g);
if (mp_draw_sub_bitmaps(c, dst, &sbs_list)) {
draw_bmp: rewrite draw_bmp.c is the software blender for subtitles and OSD. It's used by encoding mode (burning subtitles), and some VOs, like vo_drm, vo_x11, vo_xv, and possibly more. This changes the algorithm from upsampling the video to 4:4:4 and then blending to downsampling the OSD and then blending directly to video. This has far-reaching consequences for its internals, and results in an effective rewrite. Since I wanted to avoid un-premultiplying, all blending is done with premultiplied alpha. That's actually the sane thing to do. The old code just didn't do it, because it's very weird in YUV fixed point. Essentially, you'd have to compensate for the chroma centering constant by subtracting src_alpha/255*128. This seemed so hairy (especially with correct rounding and high bit depths involved) that I went for using float. I think it turned out mostly OK, although it's more complex and less maintainable than before. reinit() is certainly a bit too long. While it should be possible to optimize the RGB path more (for example by blending directly instead of doing the stupid float conversion), this is probably slower. vo_xv users probably lose in this, because it takes the slowest path (due to subsampling requirements and using YUV). Why this rewrite? Nobody knows. I simply forgot the reason. But you'll have it anyway. Whether or not this would have required a full rewrite, at least it supports target alpha now (you can for example hard sub transparent PNGs, if you ever wanted to use mpv for this). Remove the check in vf_sub. The new draw_bmp.c is not as reliant on libswscale anymore (mostly uses repack.c now), and osd.c shows an error message on missing support instead now. Formats with chroma subsampling of 4 are not supported, because FFmpeg doesn't provide pixfmt definitions for alpha variants. We could provide those ourselves (relatively trivial), but why bother.
2020-05-09 16:01:07 +00:00
char *info = mp_draw_sub_get_dbg_info(c);
fprintf(f, "%s\n", info);
talloc_free(info);
ok = true;
}
talloc_free(c);
talloc_free(dst);
done:
if (!ok)
fprintf(f, "no\n");
return ok;
}
static void run(struct test_ctx *ctx)
{
FILE *f = test_open_out(ctx, "repack.txt");
init_imgfmts_list();
for (int n = 0; n < num_imgfmts; n++) {
int imgfmt = imgfmts[n];
int other = try_repack(ctx, f, imgfmt, 0, 0);
try_repack(ctx, f, imgfmt, REPACK_CREATE_ROUND_DOWN, other);
try_repack(ctx, f, imgfmt, REPACK_CREATE_EXPAND_8BIT, other);
try_repack(ctx, f, imgfmt, REPACK_CREATE_PLANAR_F32, other);
}
fclose(f);
assert_text_files_equal(ctx, "repack.txt", "repack.txt",
"This can fail if FFmpeg/libswscale adds or removes pixfmts.");
check_float_repack(-AV_PIX_FMT_GBRAP, MP_CSP_RGB, MP_CSP_LEVELS_PC);
check_float_repack(-AV_PIX_FMT_GBRAP10, MP_CSP_RGB, MP_CSP_LEVELS_PC);
check_float_repack(-AV_PIX_FMT_GBRAP16, MP_CSP_RGB, MP_CSP_LEVELS_PC);
check_float_repack(-AV_PIX_FMT_YUVA444P, MP_CSP_BT_709, MP_CSP_LEVELS_PC);
check_float_repack(-AV_PIX_FMT_YUVA444P, MP_CSP_BT_709, MP_CSP_LEVELS_TV);
check_float_repack(-AV_PIX_FMT_YUVA444P10, MP_CSP_BT_709, MP_CSP_LEVELS_PC);
check_float_repack(-AV_PIX_FMT_YUVA444P10, MP_CSP_BT_709, MP_CSP_LEVELS_TV);
check_float_repack(-AV_PIX_FMT_YUVA444P16, MP_CSP_BT_709, MP_CSP_LEVELS_PC);
check_float_repack(-AV_PIX_FMT_YUVA444P16, MP_CSP_BT_709, MP_CSP_LEVELS_TV);
draw_bmp: rewrite draw_bmp.c is the software blender for subtitles and OSD. It's used by encoding mode (burning subtitles), and some VOs, like vo_drm, vo_x11, vo_xv, and possibly more. This changes the algorithm from upsampling the video to 4:4:4 and then blending to downsampling the OSD and then blending directly to video. This has far-reaching consequences for its internals, and results in an effective rewrite. Since I wanted to avoid un-premultiplying, all blending is done with premultiplied alpha. That's actually the sane thing to do. The old code just didn't do it, because it's very weird in YUV fixed point. Essentially, you'd have to compensate for the chroma centering constant by subtracting src_alpha/255*128. This seemed so hairy (especially with correct rounding and high bit depths involved) that I went for using float. I think it turned out mostly OK, although it's more complex and less maintainable than before. reinit() is certainly a bit too long. While it should be possible to optimize the RGB path more (for example by blending directly instead of doing the stupid float conversion), this is probably slower. vo_xv users probably lose in this, because it takes the slowest path (due to subsampling requirements and using YUV). Why this rewrite? Nobody knows. I simply forgot the reason. But you'll have it anyway. Whether or not this would have required a full rewrite, at least it supports target alpha now (you can for example hard sub transparent PNGs, if you ever wanted to use mpv for this). Remove the check in vf_sub. The new draw_bmp.c is not as reliant on libswscale anymore (mostly uses repack.c now), and osd.c shows an error message on missing support instead now. Formats with chroma subsampling of 4 are not supported, because FFmpeg doesn't provide pixfmt definitions for alpha variants. We could provide those ourselves (relatively trivial), but why bother.
2020-05-09 16:01:07 +00:00
// Determine the list of possible draw_bmp input formats. Do this here
// because it mostly depends on repack and imgformat stuff.
f = test_open_out(ctx, "draw_bmp.txt");
for (int n = 0; n < num_imgfmts; n++) {
int imgfmt = imgfmts[n];
fprintf(f, "%-12s= ", mp_imgfmt_to_name(imgfmt));
try_draw_bmp(ctx->global, f, imgfmt);
draw_bmp: rewrite draw_bmp.c is the software blender for subtitles and OSD. It's used by encoding mode (burning subtitles), and some VOs, like vo_drm, vo_x11, vo_xv, and possibly more. This changes the algorithm from upsampling the video to 4:4:4 and then blending to downsampling the OSD and then blending directly to video. This has far-reaching consequences for its internals, and results in an effective rewrite. Since I wanted to avoid un-premultiplying, all blending is done with premultiplied alpha. That's actually the sane thing to do. The old code just didn't do it, because it's very weird in YUV fixed point. Essentially, you'd have to compensate for the chroma centering constant by subtracting src_alpha/255*128. This seemed so hairy (especially with correct rounding and high bit depths involved) that I went for using float. I think it turned out mostly OK, although it's more complex and less maintainable than before. reinit() is certainly a bit too long. While it should be possible to optimize the RGB path more (for example by blending directly instead of doing the stupid float conversion), this is probably slower. vo_xv users probably lose in this, because it takes the slowest path (due to subsampling requirements and using YUV). Why this rewrite? Nobody knows. I simply forgot the reason. But you'll have it anyway. Whether or not this would have required a full rewrite, at least it supports target alpha now (you can for example hard sub transparent PNGs, if you ever wanted to use mpv for this). Remove the check in vf_sub. The new draw_bmp.c is not as reliant on libswscale anymore (mostly uses repack.c now), and osd.c shows an error message on missing support instead now. Formats with chroma subsampling of 4 are not supported, because FFmpeg doesn't provide pixfmt definitions for alpha variants. We could provide those ourselves (relatively trivial), but why bother.
2020-05-09 16:01:07 +00:00
}
fclose(f);
assert_text_files_equal(ctx, "draw_bmp.txt", "draw_bmp.txt",
"This can fail if FFmpeg/libswscale adds or removes pixfmts.");
}
const struct unittest test_repack = {
.name = "repack",
.run = run,
};