diff --git a/sub/draw_bmp.c b/sub/draw_bmp.c index ba027838ec..e1caea1e0a 100644 --- a/sub/draw_bmp.c +++ b/sub/draw_bmp.c @@ -21,12 +21,11 @@ #include #include -#include - #include "common/common.h" #include "draw_bmp.h" #include "img_convert.h" #include "video/mp_image.h" +#include "video/repack.h" #include "video/sws_utils.h" #include "video/img_format.h" #include "video/csputils.h" @@ -36,517 +35,761 @@ const bool mp_draw_sub_formats[SUBBITMAP_COUNT] = { [SUBBITMAP_RGBA] = true, }; -struct sub_cache { - struct mp_image *i, *a; -}; - struct part { int change_id; - int imgfmt; - enum mp_csp colorspace; - enum mp_csp_levels levels; + // Sub-bitmaps scaled to final sizes. int num_imgs; - struct sub_cache *imgs; + struct mp_image **imgs; +}; + +// Must be a power of 2. Height is 1, but mark_rect() effectively operates on +// multiples of chroma sized macro-pixels. (E.g. 4:2:0 -> every second line is +// the same as the previous one, and x0%2==x1%2==0.) +#define SLICE_W 256u + +// Whether to scale in tiles. Faster, but can't use correct chroma position. +// Should be a runtime option. SLICE_W is used as tile width. The tile size +// should probably be small; too small or too big will cause overhead when +// scaling. +#define SCALE_IN_TILES 1 +#define TILE_H 4u + +struct slice { + uint16_t x0, x1; }; struct mp_draw_sub_cache { - struct part *parts[MAX_OSD_PARTS]; - struct mp_image *upsample_img; - struct mp_image upsample_temp; + // Possibly cached parts. Also implies what's in the video_overlay. + struct part parts[MAX_OSD_PARTS]; + int64_t change_id; + + struct mp_image_params params; // target image params + + int w, h; // like params.w/h, but rounded up to chroma + unsigned align_x, align_y; // alignment for all video pixels + + struct mp_image *rgba_overlay; // all OSD in RGBA + struct mp_image *video_overlay; // rgba_overlay converted to video colorspace + struct mp_image *alpha_overlay; // alpha plane ref. to video_overlay + struct mp_image *calpha_overlay; // alpha_overlay scaled to chroma plane size + + unsigned s_w; // number of slices per line + struct slice *slices; // slices[y * s_w + x / SLICE_W] + bool any_osd; + + struct mp_sws_context *rgba_to_overlay; // scaler for rgba -> video csp. + struct mp_sws_context *alpha_to_calpha; // scaler for overlay -> calpha + bool scale_in_tiles; + + struct mp_sws_context *sub_scale; // scaler for SUBBITMAP_RGBA + + struct mp_repack *overlay_to_f32; // convert video_overlay to float + struct mp_image *overlay_tmp; // slice in float32 + + struct mp_repack *calpha_to_f32; // convert video_overlay to float + struct mp_image *calpha_tmp; // slice in float32 + + struct mp_repack *video_to_f32; // convert video to float + struct mp_repack *video_from_f32; // convert float back to video + struct mp_image *video_tmp; // slice in float32 + + struct mp_sws_context *premul; // video -> premultiplied video + struct mp_sws_context *unpremul; // reverse + struct mp_image *premul_tmp; + + // Function that works on the _f32 data. + void (*blend_line)(void *dst, void *src, void *src_a, int w); }; - -static struct part *get_cache(struct mp_draw_sub_cache *cache, - struct sub_bitmaps *sbs, struct mp_image *format); -static bool get_sub_area(struct mp_rect bb, struct mp_image *temp, - struct sub_bitmap *sb, struct mp_image *out_area, - int *out_src_x, int *out_src_y); - -#define CONDITIONAL 1 - -#define BLEND_CONST_ALPHA(TYPE) \ - TYPE *dst_r = dst_rp; \ - for (int x = 0; x < w; x++) { \ - uint32_t srcap = srca_r[x]; \ - if (CONDITIONAL && !srcap) continue; \ - srcap *= srcamul; /* now 0..65025 */ \ - dst_r[x] = (srcp * srcap + dst_r[x] * (65025 - srcap) + 32512) / 65025; \ - } - -// dst = srcp * (srca * srcamul) + dst * (1 - (srca * srcamul)) -static void blend_const_alpha(void *dst, int dst_stride, int srcp, - uint8_t *srca, int srca_stride, uint8_t srcamul, - int w, int h, int bytes) +static void blend_line_f32(void *dst, void *src, void *src_a, int w) { - if (!srcamul) - return; - for (int y = 0; y < h; y++) { - void *dst_rp = (uint8_t *)dst + dst_stride * y; - uint8_t *srca_r = srca + srca_stride * y; - if (bytes == 2) { - BLEND_CONST_ALPHA(uint16_t) - } else if (bytes == 1) { - BLEND_CONST_ALPHA(uint8_t) + float *dst_f = dst; + float *src_f = src; + float *src_a_f = src_a; + + for (int x = 0; x < w; x++) + dst_f[x] = src_f[x] + dst_f[x] * (1.0f - src_a_f[x]); +} + +static void blend_slice(struct mp_draw_sub_cache *p, int rgb_y) +{ + struct mp_image *ov = p->overlay_tmp; + struct mp_image *ca = p->calpha_tmp; + struct mp_image *vid = p->video_tmp; + + for (int plane = 0; plane < vid->num_planes; plane++) { + int xs = vid->fmt.xs[plane]; + int ys = vid->fmt.ys[plane]; + int h = (1 << vid->fmt.chroma_ys) - (1 << ys) + 1; + int cw = mp_chroma_div_up(vid->w, xs); + for (int y = 0; y < h; y++) { + p->blend_line(mp_image_pixel_ptr(vid, plane, 0, y), + mp_image_pixel_ptr(ov, plane, 0, y), + xs || ys ? mp_image_pixel_ptr(ca, 0, 0, y) + : mp_image_pixel_ptr(ov, ov->num_planes - 1, 0, y), + cw); } } } -#define BLEND_SRC_ALPHA(TYPE) \ - TYPE *dst_r = dst_rp, *src_r = src_rp; \ - for (int x = 0; x < w; x++) { \ - uint32_t srcap = srca_r[x]; \ - if (CONDITIONAL && !srcap) continue; \ - dst_r[x] = (src_r[x] * srcap + dst_r[x] * (255 - srcap) + 127) / 255; \ - } - -// dst = src * srca + dst * (1 - srca) -static void blend_src_alpha(void *dst, int dst_stride, void *src, - int src_stride, uint8_t *srca, int srca_stride, - int w, int h, int bytes) +static bool blend_overlay_with_video(struct mp_draw_sub_cache *p, + struct mp_image *dst) { - for (int y = 0; y < h; y++) { - void *dst_rp = (uint8_t *)dst + dst_stride * y; - void *src_rp = (uint8_t *)src + src_stride * y; - uint8_t *srca_r = srca + srca_stride * y; - if (bytes == 2) { - BLEND_SRC_ALPHA(uint16_t) - } else if (bytes == 1) { - BLEND_SRC_ALPHA(uint8_t) - } - } -} - -#define BLEND_SRC_DST_MUL(TYPE, MAX) \ - TYPE *dst_r = dst_rp; \ - for (int x = 0; x < w; x++) { \ - uint16_t srcp = src_r[x] * srcmul; /* now 0..65025 */ \ - dst_r[x] = (srcp * (MAX) + dst_r[x] * (65025 - srcp) + 32512) / 65025; \ - } - -// dst = src * srcmul + dst * (1 - src * srcmul) -static void blend_src_dst_mul(void *dst, int dst_stride, - uint8_t *src, int src_stride, uint8_t srcmul, - int w, int h, int dst_bytes) -{ - for (int y = 0; y < h; y++) { - void *dst_rp = (uint8_t *)dst + dst_stride * y; - uint8_t *src_r = (uint8_t *)src + src_stride * y; - if (dst_bytes == 2) { - BLEND_SRC_DST_MUL(uint16_t, 65025) - } else if (dst_bytes == 1) { - BLEND_SRC_DST_MUL(uint8_t, 255) - } - } -} - -static void unpremultiply_and_split_BGR32(struct mp_image *img, - struct mp_image *alpha) -{ - for (int y = 0; y < img->h; ++y) { - uint32_t *irow = (uint32_t *) &img->planes[0][img->stride[0] * y]; - uint8_t *arow = &alpha->planes[0][alpha->stride[0] * y]; - for (int x = 0; x < img->w; ++x) { - uint32_t pval = irow[x]; - uint32_t aval = (pval >> 24); - uint32_t rval = (pval >> 16) & 0xFF; - uint32_t gval = (pval >> 8) & 0xFF; - uint32_t bval = pval & 0xFF; - // multiplied = separate * alpha / 255 - // separate = rint(multiplied * 255 / alpha) - // = floor(multiplied * 255 / alpha + 0.5) - // = floor((multiplied * 255 + 0.5 * alpha) / alpha) - // = floor((multiplied * 255 + floor(0.5 * alpha)) / alpha) - int div = (int) aval; - int add = div / 2; - if (aval) { - rval = MPMIN(255, (rval * 255 + add) / div); - gval = MPMIN(255, (gval * 255 + add) / div); - bval = MPMIN(255, (bval * 255 + add) / div); - irow[x] = bval + (gval << 8) + (rval << 16) + (aval << 24); - } - arow[x] = aval; - } - } -} - -// dst_format merely contains the target colorspace/format information -static void scale_sb_rgba(struct sub_bitmap *sb, const struct mp_image *dst_format, - struct mp_image **out_sbi, struct mp_image **out_sba) -{ - struct mp_image sbisrc = {0}; - mp_image_setfmt(&sbisrc, IMGFMT_BGR32); - mp_image_set_size(&sbisrc, sb->w, sb->h); - sbisrc.planes[0] = sb->bitmap; - sbisrc.stride[0] = sb->stride; - struct mp_image *sbisrc2 = mp_image_alloc(IMGFMT_BGR32, sb->dw, sb->dh); - struct mp_image *sba = mp_image_alloc(IMGFMT_Y8, sb->dw, sb->dh); - struct mp_image *sbi = mp_image_alloc(dst_format->imgfmt, sb->dw, sb->dh); - if (!sbisrc2 || !sba || !sbi) { - talloc_free(sbisrc2); - talloc_free(sba); - talloc_free(sbi); - return; - } - - mp_image_swscale(sbisrc2, &sbisrc, SWS_BILINEAR); - unpremultiply_and_split_BGR32(sbisrc2, sba); - - sbi->params.color = dst_format->params.color; - mp_image_swscale(sbi, sbisrc2, SWS_BILINEAR); - - talloc_free(sbisrc2); - - *out_sbi = sbi; - *out_sba = sba; -} - -static void draw_rgba(struct mp_draw_sub_cache *cache, struct mp_rect bb, - struct mp_image *temp, int bits, - struct sub_bitmaps *sbs) -{ - struct part *part = get_cache(cache, sbs, temp); - assert(part); - - for (int i = 0; i < sbs->num_parts; ++i) { - struct sub_bitmap *sb = &sbs->parts[i]; - - if (sb->w < 1 || sb->h < 1) - continue; - - struct mp_image dst; - int src_x, src_y; - if (!get_sub_area(bb, temp, sb, &dst, &src_x, &src_y)) - continue; - - struct mp_image *sbi = part->imgs[i].i; - struct mp_image *sba = part->imgs[i].a; - - if (!(sbi && sba)) - scale_sb_rgba(sb, temp, &sbi, &sba); - // on OOM, skip drawing - if (!(sbi && sba)) - continue; - - int bytes = (bits + 7) / 8; - uint8_t *alpha_p = sba->planes[0] + src_y * sba->stride[0] + src_x; - for (int p = 0; p < (temp->num_planes > 2 ? 3 : 1); p++) { - void *src = sbi->planes[p] + src_y * sbi->stride[p] + src_x * bytes; - blend_src_alpha(dst.planes[p], dst.stride[p], src, sbi->stride[p], - alpha_p, sba->stride[0], dst.w, dst.h, bytes); - } - if (temp->num_planes >= 4) { - blend_src_dst_mul(dst.planes[3], dst.stride[3], alpha_p, - sba->stride[0], 255, dst.w, dst.h, bytes); - } - - part->imgs[i].i = talloc_steal(part, sbi); - part->imgs[i].a = talloc_steal(part, sba); - } -} - -static void draw_ass(struct mp_draw_sub_cache *cache, struct mp_rect bb, - struct mp_image *temp, int bits, struct sub_bitmaps *sbs) -{ - struct mp_csp_params cspar = MP_CSP_PARAMS_DEFAULTS; - mp_csp_set_image_params(&cspar, &temp->params); - cspar.levels_out = MP_CSP_LEVELS_PC; // RGB (libass.color) - cspar.input_bits = bits; - cspar.texture_bits = (bits + 7) / 8 * 8; - - struct mp_cmat yuv2rgb, rgb2yuv; - bool need_conv = temp->fmt.flags & MP_IMGFLAG_YUV; - if (need_conv) { - mp_get_csp_matrix(&cspar, &yuv2rgb); - mp_invert_cmat(&rgb2yuv, &yuv2rgb); - } - - for (int i = 0; i < sbs->num_parts; ++i) { - struct sub_bitmap *sb = &sbs->parts[i]; - - struct mp_image dst; - int src_x, src_y; - if (!get_sub_area(bb, temp, sb, &dst, &src_x, &src_y)) - continue; - - int r = (sb->libass.color >> 24) & 0xFF; - int g = (sb->libass.color >> 16) & 0xFF; - int b = (sb->libass.color >> 8) & 0xFF; - int a = 255 - (sb->libass.color & 0xFF); - int color_yuv[3]; - if (need_conv) { - int rgb[3] = {r, g, b}; - mp_map_fixp_color(&rgb2yuv, 8, rgb, cspar.texture_bits, color_yuv); - } else { - const int shift = (bits > 8) ? bits - 8 : 0; - color_yuv[0] = g << shift; - color_yuv[1] = b << shift; - color_yuv[2] = r << shift; - } - - int bytes = (bits + 7) / 8; - uint8_t *alpha_p = (uint8_t *)sb->bitmap + src_y * sb->stride + src_x; - for (int p = 0; p < (temp->num_planes > 2 ? 3 : 1); p++) { - blend_const_alpha(dst.planes[p], dst.stride[p], color_yuv[p], - alpha_p, sb->stride, a, dst.w, dst.h, bytes); - } - if (temp->num_planes >= 4) { - blend_src_dst_mul(dst.planes[3], dst.stride[3], alpha_p, - sb->stride, a, dst.w, dst.h, bytes); - } - } -} - -static void get_swscale_alignment(const struct mp_image *img, int *out_xstep, - int *out_ystep) -{ - int sx = (1 << img->fmt.chroma_xs); - int sy = (1 << img->fmt.chroma_ys); - - for (int p = 0; p < img->num_planes; ++p) { - int bits = img->fmt.bpp[p]; - // the * 2 fixes problems with writing past the destination width - while (((sx >> img->fmt.chroma_xs) * bits) % (SWS_MIN_BYTE_ALIGN * 8 * 2)) - sx *= 2; - } - - *out_xstep = sx; - *out_ystep = sy; -} - -static void align_bbox(int xstep, int ystep, struct mp_rect *rc) -{ - rc->x0 = rc->x0 & ~(xstep - 1); - rc->y0 = rc->y0 & ~(ystep - 1); - rc->x1 = FFALIGN(rc->x1, xstep); - rc->y1 = FFALIGN(rc->y1, ystep); -} - -// Post condition, if true returned: rc is inside img -static bool align_bbox_for_swscale(struct mp_image *img, struct mp_rect *rc) -{ - struct mp_rect img_rect = {0, 0, img->w, img->h}; - // Get rid of negative coordinates - if (!mp_rect_intersection(rc, &img_rect)) + if (!repack_config_buffers(p->video_to_f32, 0, p->video_tmp, 0, dst, NULL)) return false; - int xstep, ystep; - get_swscale_alignment(img, &xstep, &ystep); - align_bbox(xstep, ystep, rc); - return mp_rect_intersection(rc, &img_rect); -} - -// Try to find best/closest YUV 444 format (or similar) for imgfmt -static void get_closest_y444_format(int imgfmt, int *out_format, int *out_bits) -{ - struct mp_imgfmt_desc desc = mp_imgfmt_get_desc(imgfmt); - int planes = desc.flags & MP_IMGFLAG_ALPHA ? 4 : 3; - if (desc.flags & MP_IMGFLAG_RGB) { - // For RGB try to match the amount of bits exactly (but no less than 8, or larger than 16) - int bits = (desc.component_bits > 8) ? desc.component_bits : 8; - if (bits > 16) - bits = 16; - *out_format = mp_imgfmt_find(0, 0, planes, bits, MP_IMGFLAG_RGB_P); - if (!mp_sws_supported_format(*out_format)) - *out_format = mp_imgfmt_find(0, 0, planes, 8, MP_IMGFLAG_RGB_P); - } else if (desc.flags & MP_IMGFLAG_YUV_P) { - const int bits = (desc.component_bits > 8) ? 16 : 8; - *out_format = mp_imgfmt_find(0, 0, planes, bits, MP_IMGFLAG_YUV_P); - } else { - *out_format = 0; - } - if (!mp_sws_supported_format(*out_format)) - *out_format = IMGFMT_444P; // generic fallback - *out_bits = mp_imgfmt_get_desc(*out_format).component_bits; -} - -static struct part *get_cache(struct mp_draw_sub_cache *cache, - struct sub_bitmaps *sbs, struct mp_image *format) -{ - struct part *part = NULL; - - bool use_cache = sbs->format == SUBBITMAP_RGBA; - if (use_cache) { - part = cache->parts[sbs->render_index]; - if (part) { - if (part->change_id != sbs->change_id - || part->imgfmt != format->imgfmt - || part->colorspace != format->params.color.space - || part->levels != format->params.color.levels) - { - talloc_free(part); - part = NULL; - } - } - if (!part) { - part = talloc(cache, struct part); - *part = (struct part) { - .change_id = sbs->change_id, - .num_imgs = sbs->num_parts, - .imgfmt = format->imgfmt, - .levels = format->params.color.levels, - .colorspace = format->params.color.space, - }; - part->imgs = talloc_zero_array(part, struct sub_cache, - part->num_imgs); - } - assert(part->num_imgs == sbs->num_parts); - cache->parts[sbs->render_index] = part; - } - - return part; -} - -// Return area of intersection between target and sub-bitmap as cropped image -static bool get_sub_area(struct mp_rect bb, struct mp_image *temp, - struct sub_bitmap *sb, struct mp_image *out_area, - int *out_src_x, int *out_src_y) -{ - // coordinates are relative to the bbox - struct mp_rect dst = {sb->x - bb.x0, sb->y - bb.y0}; - dst.x1 = dst.x0 + sb->dw; - dst.y1 = dst.y0 + sb->dh; - if (!mp_rect_intersection(&dst, &(struct mp_rect){0, 0, temp->w, temp->h})) + if (!repack_config_buffers(p->video_from_f32, 0, dst, 0, p->video_tmp, NULL)) return false; - *out_src_x = (dst.x0 - sb->x) + bb.x0; - *out_src_y = (dst.y0 - sb->y) + bb.y0; - *out_area = *temp; - mp_image_crop_rc(out_area, dst); + int xs = dst->fmt.chroma_xs; + int ys = dst->fmt.chroma_ys; + + for (int y = 0; y < dst->h; y += p->align_y) { + struct slice *line = &p->slices[y * p->s_w]; + + for (int sx = 0; sx < p->s_w; sx++) { + struct slice *s = &line[sx]; + + int w = s->x1 - s->x0; + if (w <= 0) + continue; + int x = sx * SLICE_W + s->x0; + + assert(MP_IS_ALIGNED(x, p->align_x)); + assert(MP_IS_ALIGNED(w, p->align_x)); + assert(x + w <= p->w); + + repack_line(p->overlay_to_f32, 0, 0, x, y, w); + repack_line(p->video_to_f32, 0, 0, x, y, w); + if (p->calpha_to_f32) + repack_line(p->calpha_to_f32, 0, 0, x >> xs, y >> ys, w >> xs); + + blend_slice(p, y); + + repack_line(p->video_from_f32, x, y, 0, 0, w); + } + } return true; } -// Convert the src image to imgfmt (which should be a 444 format) -static struct mp_image *chroma_up(struct mp_draw_sub_cache *cache, int imgfmt, - struct mp_image *src) +static bool convert_overlay_part(struct mp_draw_sub_cache *p, + int x0, int y0, int w, int h) { - if (src->imgfmt == imgfmt) - return src; + struct mp_image src = *p->rgba_overlay; + struct mp_image dst = *p->video_overlay; - if (!cache->upsample_img || cache->upsample_img->imgfmt != imgfmt || - cache->upsample_img->w < src->w || cache->upsample_img->h < src->h) - { - talloc_free(cache->upsample_img); - cache->upsample_img = mp_image_alloc(imgfmt, src->w, src->h); - talloc_steal(cache, cache->upsample_img); - if (!cache->upsample_img) - return NULL; + mp_image_crop(&src, x0, y0, x0 + w, y0 + h); + mp_image_crop(&dst, x0, y0, x0 + w, y0 + h); + + if (mp_sws_scale(p->rgba_to_overlay, &dst, &src) < 0) + return false; + + if (p->calpha_overlay) { + src = *p->alpha_overlay; + dst = *p->calpha_overlay; + + int xs = p->video_overlay->fmt.chroma_xs; + int ys = p->video_overlay->fmt.chroma_ys; + mp_image_crop(&src, x0, y0, x0 + w, y0 + h); + mp_image_crop(&dst, x0 >> xs, y0 >> ys, (x0 + w) >> xs, (y0 + h) >> ys); + + if (mp_sws_scale(p->alpha_to_calpha, &dst, &src) < 0) + return false; } - cache->upsample_temp = *cache->upsample_img; - struct mp_image *temp = &cache->upsample_temp; - mp_image_set_size(temp, src->w, src->h); - - // The temp image is always YUV, but src not necessarily. - // Reduce amount of conversions in YUV case (upsampling/shifting only) - if (src->fmt.flags & MP_IMGFLAG_YUV) - temp->params.color = src->params.color; - - if (src->imgfmt == IMGFMT_420P) { - assert(imgfmt == IMGFMT_444P); - // Faster upsampling: keep Y plane, upsample chroma planes only - // The whole point is not having swscale copy the Y plane - struct mp_image t_dst = *temp; - mp_image_setfmt(&t_dst, IMGFMT_Y8); - mp_image_set_size(&t_dst, temp->w, temp->h); - struct mp_image t_src = t_dst; - mp_image_set_size(&t_src, src->w >> 1, src->h >> 1); - for (int c = 0; c < 2; c++) { - t_dst.planes[0] = temp->planes[1 + c]; - t_dst.stride[0] = temp->stride[1 + c]; - t_src.planes[0] = src->planes[1 + c]; - t_src.stride[0] = src->stride[1 + c]; - mp_image_swscale(&t_dst, &t_src, SWS_POINT); - } - temp->planes[0] = src->planes[0]; - temp->stride[0] = src->stride[0]; - } else { - mp_image_swscale(temp, src, SWS_POINT); - } - - return temp; + return true; } -// Undo chroma_up() (copy temp to old_src if needed) -static void chroma_down(struct mp_image *old_src, struct mp_image *temp) +static bool convert_to_video_overlay(struct mp_draw_sub_cache *p) { - assert(old_src->w == temp->w && old_src->h == temp->h); - if (temp != old_src) { - if (old_src->imgfmt == IMGFMT_420P) { - // Downsampling, skipping the Y plane (see chroma_up()) - assert(temp->imgfmt == IMGFMT_444P); - assert(temp->planes[0] == old_src->planes[0]); - struct mp_image t_dst = *temp; - mp_image_setfmt(&t_dst, IMGFMT_Y8); - mp_image_set_size(&t_dst, old_src->w >> 1, old_src->h >> 1); - struct mp_image t_src = t_dst; - mp_image_set_size(&t_src, temp->w, temp->h); - for (int c = 0; c < 2; c++) { - t_dst.planes[0] = old_src->planes[1 + c]; - t_dst.stride[0] = old_src->stride[1 + c]; - t_src.planes[0] = temp->planes[1 + c]; - t_src.stride[0] = temp->stride[1 + c]; - mp_image_swscale(&t_dst, &t_src, SWS_AREA); + if (!p->video_overlay) + return true; + + if (p->scale_in_tiles) { + int t_h = p->rgba_overlay->h / TILE_H; + for (int ty = 0; ty < t_h; ty++) { + for (int sx = 0; sx < p->s_w; sx++) { + struct slice *s = &p->slices[ty * TILE_H * p->s_w + sx]; + bool pixels_set = false; + for (int y = 0; y < TILE_H; y++) { + if (s[0].x0 < s[0].x1) { + pixels_set = true; + break; + } + s += p->s_w; + } + if (!pixels_set) + continue; + if (!convert_overlay_part(p, sx * SLICE_W, ty * TILE_H, + SLICE_W, TILE_H)) + return false; } - } else { - mp_image_swscale(old_src, temp, SWS_AREA); // chroma down } - } -} - -static void draw_sbs(struct mp_draw_sub_cache **cache, struct mp_image *dst, - struct sub_bitmaps *sbs) -{ - assert(mp_draw_sub_formats[sbs->format]); - if (!mp_sws_supported_format(dst->imgfmt)) - return; - - struct mp_draw_sub_cache *cache_ = cache ? *cache : NULL; - if (!cache_) - cache_ = talloc_zero(NULL, struct mp_draw_sub_cache); - - int format, bits; - get_closest_y444_format(dst->imgfmt, &format, &bits); - - struct mp_rect rc_list[MP_SUB_BB_LIST_MAX]; - int num_rc = mp_get_sub_bb_list(sbs, rc_list, MP_SUB_BB_LIST_MAX); - - for (int r = 0; r < num_rc; r++) { - struct mp_rect bb = rc_list[r]; - - if (!align_bbox_for_swscale(dst, &bb)) - return; - - struct mp_image dst_region = *dst; - mp_image_crop_rc(&dst_region, bb); - struct mp_image *temp = chroma_up(cache_, format, &dst_region); - if (!temp) - continue; // on OOM, skip region - - if (sbs->format == SUBBITMAP_RGBA) { - draw_rgba(cache_, bb, temp, bits, sbs); - } else if (sbs->format == SUBBITMAP_LIBASS) { - draw_ass(cache_, bb, temp, bits, sbs); - } - - chroma_down(&dst_region, temp); - } - - if (cache) { - *cache = cache_; } else { - talloc_free(cache_); + if (!convert_overlay_part(p, 0, 0, p->rgba_overlay->w, p->rgba_overlay->h)) + return false; + } + + return true; +} + +// Mark the given rectangle of pixels as possibly non-transparent. +// The rectangle must have been pre-clipped. +static void mark_rect(struct mp_draw_sub_cache *p, int x0, int y0, int x1, int y1) +{ + x0 = MP_ALIGN_DOWN(x0, p->align_x); + y0 = MP_ALIGN_DOWN(y0, p->align_y); + x1 = MP_ALIGN_UP(x1, p->align_x); + y1 = MP_ALIGN_UP(y1, p->align_y); + + assert(x0 >= 0 && x0 <= x1 && x1 <= p->w); + assert(y0 >= 0 && y0 <= y1 && y1 <= p->h); + + int sx0 = x0 / SLICE_W; + int sx1 = x1 / SLICE_W; + + for (int y = y0; y < y1; y++) { + struct slice *line = &p->slices[y * p->s_w]; + + struct slice *s0 = &line[sx0]; + struct slice *s1 = &line[sx1]; + + s0->x0 = MPMIN(s0->x0, x0 % SLICE_W); + s1->x1 = MPMAX(s1->x1, x1 % SLICE_W); + + if (s0 != s1) { + s0->x1 = SLICE_W; + s1->x0 = 0; + + for (int x = sx0 + 1; x < sx1; x++) { + struct slice *s = &line[x]; + s->x0 = 0; + s->x1 = SLICE_W; + } + } + + p->any_osd = true; } } -// cache: if not NULL, the function will set *cache to a talloc-allocated cache -// containing scaled versions of sbs contents - free the cache with -// talloc_free() -void mp_draw_sub_bitmaps(struct mp_draw_sub_cache **cache, struct mp_image *dst, +static void draw_ass_rgba(uint8_t *dst, ptrdiff_t dst_stride, + uint8_t *src, ptrdiff_t src_stride, + int w, int h, uint32_t color) +{ + const unsigned int r = (color >> 24) & 0xff; + const unsigned int g = (color >> 16) & 0xff; + const unsigned int b = (color >> 8) & 0xff; + const unsigned int a = 0xff - (color & 0xff); + + for (int y = 0; y < h; y++) { + uint32_t *dstrow = (uint32_t *) dst; + for (int x = 0; x < w; x++) { + const unsigned int v = src[x]; + unsigned int aa = a * v; + uint32_t dstpix = dstrow[x]; + unsigned int dstb = dstpix & 0xFF; + unsigned int dstg = (dstpix >> 8) & 0xFF; + unsigned int dstr = (dstpix >> 16) & 0xFF; + unsigned int dsta = (dstpix >> 24) & 0xFF; + dstb = (v * b * a + dstb * (255 * 255 - aa)) / (255 * 255); + dstg = (v * g * a + dstg * (255 * 255 - aa)) / (255 * 255); + dstr = (v * r * a + dstr * (255 * 255 - aa)) / (255 * 255); + dsta = (aa * 255 + dsta * (255 * 255 - aa)) / (255 * 255); + dstrow[x] = dstb | (dstg << 8) | (dstr << 16) | (dsta << 24); + } + dst += dst_stride; + src += src_stride; + } +} + +static void render_ass(struct mp_draw_sub_cache *p, struct sub_bitmaps *sb) +{ + assert(sb->format == SUBBITMAP_LIBASS); + + for (int i = 0; i < sb->num_parts; i++) { + struct sub_bitmap *s = &sb->parts[i]; + + draw_ass_rgba(mp_image_pixel_ptr(p->rgba_overlay, 0, s->x, s->y), + p->rgba_overlay->stride[0], s->bitmap, s->stride, + s->w, s->h, s->libass.color); + + mark_rect(p, s->x, s->y, s->x + s->w, s->y + s->h); + } +} + +static void draw_rgba(uint8_t *dst, ptrdiff_t dst_stride, + uint8_t *src, ptrdiff_t src_stride, int w, int h) +{ + for (int y = 0; y < h; y++) { + uint32_t *srcrow = (uint32_t *)src; + uint32_t *dstrow = (uint32_t *)dst; + for (int x = 0; x < w; x++) { + uint32_t srcpix = srcrow[x]; + uint32_t dstpix = dstrow[x]; + unsigned int srcb = srcpix & 0xFF; + unsigned int srcg = (srcpix >> 8) & 0xFF; + unsigned int srcr = (srcpix >> 16) & 0xFF; + unsigned int srca = (srcpix >> 24) & 0xFF; + unsigned int dstb = dstpix & 0xFF; + unsigned int dstg = (dstpix >> 8) & 0xFF; + unsigned int dstr = (dstpix >> 16) & 0xFF; + unsigned int dsta = (dstpix >> 24) & 0xFF; + dstb = srcb + dstb * (255 * 255 - srca) / (255 * 255); + dstg = srcg + dstg * (255 * 255 - srca) / (255 * 255); + dstr = srcr + dstr * (255 * 255 - srca) / (255 * 255); + dsta = srca + dsta * (255 * 255 - srca) / (255 * 255); + dstrow[x] = dstb | (dstg << 8) | (dstr << 16) | (dsta << 24); + } + dst += dst_stride; + src += src_stride; + } +} + +static bool render_rgba(struct mp_draw_sub_cache *p, struct part *part, + struct sub_bitmaps *sb) +{ + assert(sb->format == SUBBITMAP_RGBA); + + if (part->change_id != sb->change_id) { + for (int n = 0; n < part->num_imgs; n++) + talloc_free(part->imgs[n]); + part->num_imgs = sb->num_parts; + MP_TARRAY_GROW(p, part->imgs, part->num_imgs); + for (int n = 0; n < part->num_imgs; n++) + part->imgs[n] = NULL; + + part->change_id = sb->change_id; + } + + for (int i = 0; i < sb->num_parts; i++) { + struct sub_bitmap *s = &sb->parts[i]; + + // Clipping is rare but necessary. + int sx0 = s->x; + int sy0 = s->y; + int sx1 = s->x + s->dw; + int sy1 = s->y + s->dh; + + int x0 = MPCLAMP(sx0, 0, p->w); + int y0 = MPCLAMP(sy0, 0, p->h); + int x1 = MPCLAMP(sx1, 0, p->w); + int y1 = MPCLAMP(sy1, 0, p->h); + + int dw = x1 - x0; + int dh = y1 - y0; + if (dw <= 0 || dh <= 0) + continue; + + // We clip the source instead of the scaled image, because that might + // avoid excessive memory usage when applying a ridiculous scale factor, + // even if that stretches it to up to 1 pixel due to integer rounding. + int sx = 0; + int sy = 0; + int sw = s->w; + int sh = s->h; + if (x0 != sx0 || y0 != sy0 || x1 != sx1 || y1 != sy1) { + double fx = s->dw / (double)s->w; + double fy = s->dh / (double)s->h; + sx = MPCLAMP((x0 - sx0) / fx, 0, s->w); + sy = MPCLAMP((y0 - sy0) / fy, 0, s->h); + sw = MPCLAMP(dw / fx, 1, s->w); + sh = MPCLAMP(dh / fy, 1, s->h); + } + + assert(sx >= 0 && sw > 0 && sx + sw <= s->w); + assert(sy >= 0 && sh > 0 && sy + sh <= s->h); + + ptrdiff_t s_stride = s->stride; + void *s_ptr = (char *)s->bitmap + s_stride * sy + sx * 4; + + if (dw != sw || dh != sh) { + struct mp_image *scaled = part->imgs[i]; + + if (!scaled) { + struct mp_image src_img = {0}; + mp_image_setfmt(&src_img, IMGFMT_BGR32); + mp_image_set_size(&src_img, sw, sh); + src_img.planes[0] = s_ptr; + src_img.stride[0] = s_stride; + src_img.params.alpha = MP_ALPHA_PREMUL; + + scaled = mp_image_alloc(IMGFMT_BGR32, dw, dh); + if (!scaled) + return false; + part->imgs[i] = talloc_steal(p, scaled); + mp_image_copy_attributes(scaled, &src_img); + + if (mp_sws_scale(p->sub_scale, scaled, &src_img) < 0) + return false; + } + + assert(scaled->w == dw); + assert(scaled->h == dh); + + s_stride = scaled->stride[0]; + s_ptr = scaled->planes[0]; + } + + draw_rgba(mp_image_pixel_ptr(p->rgba_overlay, 0, x0, y0), + p->rgba_overlay->stride[0], s_ptr, s_stride, dw, dh); + + mark_rect(p, x0, y0, x1, y1); + } + + return true; +} + +static bool render_sb(struct mp_draw_sub_cache *p, struct sub_bitmaps *sb) +{ + struct part *part = &p->parts[sb->render_index]; + + switch (sb->format) { + case SUBBITMAP_LIBASS: + render_ass(p, sb); + return true; + case SUBBITMAP_RGBA: + return render_rgba(p, part, sb); + } + + return false; +} + +static void clear_rgba_overlay(struct mp_draw_sub_cache *p) +{ + assert(p->rgba_overlay->imgfmt == IMGFMT_BGR32); + + for (int y = 0; y < p->rgba_overlay->h; y++) { + uint32_t *px = mp_image_pixel_ptr(p->rgba_overlay, 0, 0, y); + struct slice *line = &p->slices[y * p->s_w]; + + for (int sx = 0; sx < p->s_w; sx++) { + struct slice *s = &line[sx]; + + if (s->x0 <= s->x1) { + memset(px + s->x0, 0, (s->x1 - s->x0) * 4); + *s = (struct slice){SLICE_W, 0}; + } + + px += SLICE_W; + } + } + + p->any_osd = false; +} + +static bool reinit(struct mp_draw_sub_cache *p, struct mp_image_params *params) +{ + talloc_free_children(p); + *p = (struct mp_draw_sub_cache){.params = *params}; + + bool need_premul = params->alpha != MP_ALPHA_PREMUL && + (mp_imgfmt_get_desc(params->imgfmt).flags & MP_IMGFLAG_ALPHA); + + int rflags = REPACK_CREATE_EXPAND_8BIT | REPACK_CREATE_PLANAR_F32; + p->blend_line = blend_line_f32; + + p->video_to_f32 = mp_repack_create_planar(params->imgfmt, false, rflags); + talloc_steal(p, p->video_to_f32); + if (!p->video_to_f32) + return false; + + p->scale_in_tiles = SCALE_IN_TILES; + + int vid_f32_fmt = mp_repack_get_format_dst(p->video_to_f32); + + p->video_from_f32 = mp_repack_create_planar(params->imgfmt, true, rflags); + talloc_steal(p, p->video_from_f32); + if (!p->video_from_f32) + return false; + + assert(mp_repack_get_format_dst(p->video_to_f32) == + mp_repack_get_format_src(p->video_from_f32)); + + // Find a reasonable intermediate format for video_overlay. Requirements: + // - same subsampling + // - has alpha + // - uses video colorspace + // - REPACK_CREATE_PLANAR_F32 support + // - probably not using float (vaguely wastes memory) + struct mp_regular_imgfmt vfdesc = {0}; + mp_get_regular_imgfmt(&vfdesc, mp_repack_get_format_dst(p->video_to_f32)); + assert(vfdesc.component_type == MP_COMPONENT_TYPE_FLOAT); + + int overlay_fmt = 0; + if (params->color.space == MP_CSP_RGB && vfdesc.num_planes >= 3) { + // No point in doing anything fancy. + overlay_fmt = IMGFMT_BGR32; + p->scale_in_tiles = false; + } else { + struct mp_regular_imgfmt odesc = vfdesc; + // Just use 8 bit as well (should be fine, may use less memory). + odesc.component_type = MP_COMPONENT_TYPE_UINT; + odesc.component_size = 1; + odesc.component_pad = 0; + + // Ensure there's alpha. + if (odesc.planes[odesc.num_planes - 1].components[0] != 4) { + if (odesc.num_planes >= 4) + return false; // wat + odesc.planes[odesc.num_planes++] = + (struct mp_regular_imgfmt_plane){1, {4}}; + } + + overlay_fmt = mp_find_regular_imgfmt(&odesc); + p->scale_in_tiles = odesc.chroma_xs || odesc.chroma_ys; + } + if (!overlay_fmt) + return false; + + p->overlay_to_f32 = mp_repack_create_planar(overlay_fmt, false, rflags); + talloc_steal(p, p->overlay_to_f32); + if (!p->overlay_to_f32) + return false; + + int render_fmt = mp_repack_get_format_dst(p->overlay_to_f32); + + struct mp_regular_imgfmt ofdesc = {0}; + mp_get_regular_imgfmt(&ofdesc, render_fmt); + + if (ofdesc.planes[ofdesc.num_planes - 1].components[0] != 4) + return false; + + // The formats must be the same, minus possible lack of alpha in vfdesc. + if (ofdesc.num_planes != vfdesc.num_planes && + ofdesc.num_planes - 1 != vfdesc.num_planes) + return false; + for (int n = 0; n < vfdesc.num_planes; n++) { + if (vfdesc.planes[n].components[0] != ofdesc.planes[n].components[0]) + return false; + } + + p->align_x = mp_repack_get_align_x(p->video_to_f32); + p->align_y = mp_repack_get_align_y(p->video_to_f32); + + assert(p->align_x >= mp_repack_get_align_x(p->overlay_to_f32)); + assert(p->align_y >= mp_repack_get_align_y(p->overlay_to_f32)); + + if (p->align_x > SLICE_W || p->align_y > TILE_H) + return false; + + p->w = MP_ALIGN_UP(params->w, p->align_x); + int slice_h = p->align_y; + p->h = MP_ALIGN_UP(params->h, slice_h); + + // Size of the overlay. If scaling in tiles, round up to tiles, so we don't + // need to reinit the scale for right/bottom tiles. + int w = p->w; + int h = p->h; + if (p->scale_in_tiles) { + w = MP_ALIGN_UP(w, SLICE_W); + h = MP_ALIGN_UP(h, TILE_H); + } + + p->rgba_overlay = talloc_steal(p, mp_image_alloc(IMGFMT_BGR32, w, h)); + p->overlay_tmp = talloc_steal(p, mp_image_alloc(render_fmt, SLICE_W, slice_h)); + p->video_tmp = talloc_steal(p, mp_image_alloc(vid_f32_fmt, SLICE_W, slice_h)); + if (!p->rgba_overlay || !p->overlay_tmp || !p->video_tmp) + return false; + + mp_image_params_guess_csp(&p->rgba_overlay->params); + p->rgba_overlay->params.alpha = MP_ALPHA_PREMUL; + + p->overlay_tmp->params.color = params->color; + p->video_tmp->params.color = params->color; + + if (p->rgba_overlay->imgfmt == overlay_fmt) { + if (!repack_config_buffers(p->overlay_to_f32, 0, p->overlay_tmp, + 0, p->rgba_overlay, NULL)) + return false; + } else { + p->video_overlay = talloc_steal(p, mp_image_alloc(overlay_fmt, w, h)); + if (!p->video_overlay) + return false; + + p->video_overlay->params.color = params->color; + p->video_overlay->params.chroma_location = params->chroma_location; + p->video_overlay->params.alpha = MP_ALPHA_PREMUL; + + if (p->scale_in_tiles) + p->video_overlay->params.chroma_location = MP_CHROMA_CENTER; + + p->rgba_to_overlay = mp_sws_alloc(p); + p->rgba_to_overlay->allow_zimg = true; + if (!mp_sws_supports_formats(p->rgba_to_overlay, + p->video_overlay->imgfmt, p->rgba_overlay->imgfmt)) + return false; + + if (!repack_config_buffers(p->overlay_to_f32, 0, p->overlay_tmp, + 0, p->video_overlay, NULL)) + return false; + + // Setup a scaled alpha plane if chroma-subsampling is present. + int xs = p->video_overlay->fmt.chroma_xs; + int ys = p->video_overlay->fmt.chroma_ys; + if (xs || ys) { + // For extracting the alpha plane, construct a gray format that is + // compatible with the alpha one. + struct mp_regular_imgfmt odesc = {0}; + mp_get_regular_imgfmt(&odesc, overlay_fmt); + assert(odesc.component_size); + int aplane = odesc.num_planes - 1; + assert(odesc.planes[aplane].num_components == 1); + assert(odesc.planes[aplane].components[0] == 4); + struct mp_regular_imgfmt cadesc = odesc; + cadesc.num_planes = 1; + cadesc.planes[0] = (struct mp_regular_imgfmt_plane){1, {1}}; + cadesc.chroma_xs = cadesc.chroma_ys = 0; + + int calpha_fmt = mp_find_regular_imgfmt(&cadesc); + if (!calpha_fmt) + return false; + + // Unscaled alpha plane from p->video_overlay. + p->alpha_overlay = talloc_zero(p, struct mp_image); + mp_image_setfmt(p->alpha_overlay, calpha_fmt); + mp_image_set_size(p->alpha_overlay, w, h); + p->alpha_overlay->planes[0] = p->video_overlay->planes[aplane]; + p->alpha_overlay->stride[0] = p->video_overlay->stride[aplane]; + + // Full range gray always has the same range as alpha. + p->alpha_overlay->params.color.levels = MP_CSP_LEVELS_PC; + mp_image_params_guess_csp(&p->alpha_overlay->params); + + p->calpha_overlay = + talloc_steal(p, mp_image_alloc(calpha_fmt, w >> xs, h >> ys)); + if (!p->calpha_overlay) + return false; + p->calpha_overlay->params.color = p->alpha_overlay->params.color; + + p->calpha_to_f32 = mp_repack_create_planar(calpha_fmt, false, rflags); + talloc_steal(p, p->calpha_to_f32); + if (!p->calpha_to_f32) + return false; + + int af32_fmt = mp_repack_get_format_dst(p->calpha_to_f32); + p->calpha_tmp = talloc_steal(p, mp_image_alloc(af32_fmt, SLICE_W, 1)); + if (!p->calpha_tmp) + return false; + + if (!repack_config_buffers(p->calpha_to_f32, 0, p->calpha_tmp, + 0, p->calpha_overlay, NULL)) + return false; + + p->alpha_to_calpha = mp_sws_alloc(p); + if (!mp_sws_supports_formats(p->alpha_to_calpha, + calpha_fmt, calpha_fmt)) + return false; + } + } + + p->sub_scale = mp_sws_alloc(p); + + p->s_w = MP_ALIGN_UP(p->rgba_overlay->w, SLICE_W) / SLICE_W; + + p->slices = talloc_zero_array(p, struct slice, p->s_w * p->rgba_overlay->h); + + mp_image_clear(p->rgba_overlay, 0, 0, w, h); + clear_rgba_overlay(p); + + if (need_premul) { + p->premul = mp_sws_alloc(p); + p->unpremul = mp_sws_alloc(p); + p->premul_tmp = mp_image_alloc(params->imgfmt, params->w, params->h); + talloc_steal(p, p->premul_tmp); + if (!p->premul_tmp) + return false; + mp_image_set_params(p->premul_tmp, params); + p->premul_tmp->params.alpha = MP_ALPHA_PREMUL; + + // Only zimg supports this. + p->premul->force_scaler = MP_SWS_ZIMG; + p->unpremul->force_scaler = MP_SWS_ZIMG; + } + + return true; +} + +char *mp_draw_sub_get_dbg_info(struct mp_draw_sub_cache *p) +{ + assert(p); + + return talloc_asprintf(NULL, + "align=%d:%d ov=%-7s, ov_f=%s, v_f=%s, a=%s, ca=%s, ca_f=%s", + p->align_x, p->align_y, + mp_imgfmt_to_name(p->video_overlay ? p->video_overlay->imgfmt : 0), + mp_imgfmt_to_name(p->overlay_tmp->imgfmt), + mp_imgfmt_to_name(p->video_tmp->imgfmt), + mp_imgfmt_to_name(p->alpha_overlay ? p->alpha_overlay->imgfmt : 0), + mp_imgfmt_to_name(p->calpha_overlay ? p->calpha_overlay->imgfmt : 0), + mp_imgfmt_to_name(p->calpha_tmp ? p->calpha_tmp->imgfmt : 0)); +} + +// p_cache: if not NULL, the function will set *p to a talloc-allocated p +// containing scaled versions of sbs contents - free the p with +// talloc_free() +bool mp_draw_sub_bitmaps(struct mp_draw_sub_cache **p_cache, struct mp_image *dst, struct sub_bitmap_list *sbs_list) { - for (int n = 0; n < sbs_list->num_items; n++) - draw_sbs(cache, dst, sbs_list->items[n]); + bool ok = false; + + // dst must at least be as large as the bounding box, or you may get memory + // corruption. + assert(dst->w >= sbs_list->w); + assert(dst->h >= sbs_list->h); + + struct mp_draw_sub_cache *p = p_cache ? *p_cache : NULL; + if (!p) + p = talloc_zero(NULL, struct mp_draw_sub_cache); + + if (!mp_image_params_equal(&p->params, &dst->params) || !p->video_tmp) + { + if (!reinit(p, &dst->params)) { + talloc_free_children(p); + *p = (struct mp_draw_sub_cache){0}; + goto done; + } + } + + if (p->change_id != sbs_list->change_id) { + p->change_id = sbs_list->change_id; + + clear_rgba_overlay(p); + + for (int n = 0; n < sbs_list->num_items; n++) { + if (!render_sb(p, sbs_list->items[n])) + goto done; + } + + if (!convert_to_video_overlay(p)) + goto done; + } + + struct mp_image *target = dst; + if (p->any_osd && p->premul_tmp) { + if (mp_sws_scale(p->premul, p->premul_tmp, dst) < 0) + goto done; + target = p->premul_tmp; + } + + if (!blend_overlay_with_video(p, target)) + goto done; + + if (p->any_osd && p->premul_tmp) { + if (mp_sws_scale(p->unpremul, dst, p->premul_tmp) < 0) + goto done; + } + + ok = true; + +done: + if (p_cache) { + *p_cache = p; + } else { + talloc_free(p); + } + + return ok; } // vim: ts=4 sw=4 et tw=80 diff --git a/sub/draw_bmp.h b/sub/draw_bmp.h index 6adfd2c6c5..6833d42cf7 100644 --- a/sub/draw_bmp.h +++ b/sub/draw_bmp.h @@ -5,8 +5,9 @@ struct mp_image; struct mp_draw_sub_cache; -void mp_draw_sub_bitmaps(struct mp_draw_sub_cache **cache, struct mp_image *dst, +bool mp_draw_sub_bitmaps(struct mp_draw_sub_cache **cache, struct mp_image *dst, struct sub_bitmap_list *sbs_list); +char *mp_draw_sub_get_dbg_info(struct mp_draw_sub_cache *c); extern const bool mp_draw_sub_formats[SUBBITMAP_COUNT]; diff --git a/sub/osd.c b/sub/osd.c index 15ad5d20d7..52b69461ff 100644 --- a/sub/osd.c +++ b/sub/osd.c @@ -427,7 +427,8 @@ void osd_draw_on_image_p(struct osd_state *osd, struct mp_osd_res res, stats_time_start(osd->stats, "draw-bmp"); - mp_draw_sub_bitmaps(&osd->draw_cache, dest, list); + if (!mp_draw_sub_bitmaps(&osd->draw_cache, dest, list)) + MP_WARN(osd, "Failed rendering OSD.\n"); talloc_steal(osd, osd->draw_cache); stats_time_end(osd->stats, "draw-bmp"); diff --git a/test/ref/draw_bmp.txt b/test/ref/draw_bmp.txt new file mode 100644 index 0000000000..c56a57e836 --- /dev/null +++ b/test/ref/draw_bmp.txt @@ -0,0 +1,218 @@ +0bgr = align=1:1 ov=gbrap , ov_f=gbrapf32, v_f=gbrpf32, a=unknown, ca=unknown, ca_f=unknown +0rgb = align=1:1 ov=gbrap , ov_f=gbrapf32, v_f=gbrpf32, a=unknown, ca=unknown, ca_f=unknown +abgr = align=1:1 ov=gbrap , ov_f=gbrapf32, v_f=gbrapf32, a=unknown, ca=unknown, ca_f=unknown +argb = align=1:1 ov=gbrap , ov_f=gbrapf32, v_f=gbrapf32, a=unknown, ca=unknown, ca_f=unknown +ayuv64 = align=1:1 ov=yuva444p, ov_f=yuva444pf, v_f=yuva444pf, a=unknown, ca=unknown, ca_f=unknown +ayuv64be = align=1:1 ov=yuva444p, ov_f=yuva444pf, v_f=yuva444pf, a=unknown, ca=unknown, ca_f=unknown +bayer_bggr16= no +bayer_bggr16be= no +bayer_bggr8 = no +bayer_gbrg16= no +bayer_gbrg16be= no +bayer_gbrg8 = no +bayer_grbg16= no +bayer_grbg16be= no +bayer_grbg8 = no +bayer_rggb16= no +bayer_rggb16be= no +bayer_rggb8 = no +bgr0 = align=1:1 ov=gbrap , ov_f=gbrapf32, v_f=gbrpf32, a=unknown, ca=unknown, ca_f=unknown +bgr24 = align=1:1 ov=gbrap , ov_f=gbrapf32, v_f=gbrpf32, a=unknown, ca=unknown, ca_f=unknown +bgr4 = no +bgr444 = align=1:1 ov=gbrap , ov_f=gbrapf32, v_f=gbrpf32, a=unknown, ca=unknown, ca_f=unknown +bgr444be = align=1:1 ov=gbrap , ov_f=gbrapf32, v_f=gbrpf32, a=unknown, ca=unknown, ca_f=unknown +bgr48 = align=1:1 ov=gbrap , ov_f=gbrapf32, v_f=gbrpf32, a=unknown, ca=unknown, ca_f=unknown +bgr48be = align=1:1 ov=gbrap , ov_f=gbrapf32, v_f=gbrpf32, a=unknown, ca=unknown, ca_f=unknown +bgr4_byte = align=1:1 ov=gbrap , ov_f=gbrapf32, v_f=gbrpf32, a=unknown, ca=unknown, ca_f=unknown +bgr555 = align=1:1 ov=gbrap , ov_f=gbrapf32, v_f=gbrpf32, a=unknown, ca=unknown, ca_f=unknown +bgr555be = align=1:1 ov=gbrap , ov_f=gbrapf32, v_f=gbrpf32, a=unknown, ca=unknown, ca_f=unknown +bgr565 = align=1:1 ov=gbrap , ov_f=gbrapf32, v_f=gbrpf32, a=unknown, ca=unknown, ca_f=unknown +bgr565be = align=1:1 ov=gbrap , ov_f=gbrapf32, v_f=gbrpf32, a=unknown, ca=unknown, ca_f=unknown +bgr8 = align=1:1 ov=gbrap , ov_f=gbrapf32, v_f=gbrpf32, a=unknown, ca=unknown, ca_f=unknown +bgra = align=1:1 ov=gbrap , ov_f=gbrapf32, v_f=gbrapf32, a=unknown, ca=unknown, ca_f=unknown +bgra64 = align=1:1 ov=gbrap , ov_f=gbrapf32, v_f=gbrapf32, a=unknown, ca=unknown, ca_f=unknown +bgra64be = align=1:1 ov=gbrap , ov_f=gbrapf32, v_f=gbrapf32, a=unknown, ca=unknown, ca_f=unknown +cuda = no +d3d11 = no +d3d11va_vld = no +drm_prime = no +dxva2_vld = no +gbrap = align=1:1 ov=gbrap , ov_f=gbrapf32, v_f=gbrapf32, a=unknown, ca=unknown, ca_f=unknown +gbrap10 = align=1:1 ov=gbrap , ov_f=gbrapf32, v_f=gbrapf32, a=unknown, ca=unknown, ca_f=unknown +gbrap10be = align=1:1 ov=gbrap , ov_f=gbrapf32, v_f=gbrapf32, a=unknown, ca=unknown, ca_f=unknown +gbrap12 = align=1:1 ov=gbrap , ov_f=gbrapf32, v_f=gbrapf32, a=unknown, ca=unknown, ca_f=unknown +gbrap12be = align=1:1 ov=gbrap , ov_f=gbrapf32, v_f=gbrapf32, a=unknown, ca=unknown, ca_f=unknown +gbrap16 = align=1:1 ov=gbrap , ov_f=gbrapf32, v_f=gbrapf32, a=unknown, ca=unknown, ca_f=unknown +gbrap16be = align=1:1 ov=gbrap , ov_f=gbrapf32, v_f=gbrapf32, a=unknown, ca=unknown, ca_f=unknown +gbrapf32 = align=1:1 ov=gbrap , ov_f=gbrapf32, v_f=gbrapf32, a=unknown, ca=unknown, ca_f=unknown +gbrapf32be = align=1:1 ov=gbrap , ov_f=gbrapf32, v_f=gbrapf32, a=unknown, ca=unknown, ca_f=unknown +gbrp = align=1:1 ov=gbrap , ov_f=gbrapf32, v_f=gbrpf32, a=unknown, ca=unknown, ca_f=unknown +gbrp1 = align=1:1 ov=gbrap , ov_f=gbrapf32, v_f=gbrpf32, a=unknown, ca=unknown, ca_f=unknown +gbrp10 = align=1:1 ov=gbrap , ov_f=gbrapf32, v_f=gbrpf32, a=unknown, ca=unknown, ca_f=unknown +gbrp10be = align=1:1 ov=gbrap , ov_f=gbrapf32, v_f=gbrpf32, a=unknown, ca=unknown, ca_f=unknown +gbrp12 = align=1:1 ov=gbrap , ov_f=gbrapf32, v_f=gbrpf32, a=unknown, ca=unknown, ca_f=unknown +gbrp12be = align=1:1 ov=gbrap , ov_f=gbrapf32, v_f=gbrpf32, a=unknown, ca=unknown, ca_f=unknown +gbrp14 = align=1:1 ov=gbrap , ov_f=gbrapf32, v_f=gbrpf32, a=unknown, ca=unknown, ca_f=unknown +gbrp14be = align=1:1 ov=gbrap , ov_f=gbrapf32, v_f=gbrpf32, a=unknown, ca=unknown, ca_f=unknown +gbrp16 = align=1:1 ov=gbrap , ov_f=gbrapf32, v_f=gbrpf32, a=unknown, ca=unknown, ca_f=unknown +gbrp16be = align=1:1 ov=gbrap , ov_f=gbrapf32, v_f=gbrpf32, a=unknown, ca=unknown, ca_f=unknown +gbrp2 = align=1:1 ov=gbrap , ov_f=gbrapf32, v_f=gbrpf32, a=unknown, ca=unknown, ca_f=unknown +gbrp3 = align=1:1 ov=gbrap , ov_f=gbrapf32, v_f=gbrpf32, a=unknown, ca=unknown, ca_f=unknown +gbrp4 = align=1:1 ov=gbrap , ov_f=gbrapf32, v_f=gbrpf32, a=unknown, ca=unknown, ca_f=unknown +gbrp5 = align=1:1 ov=gbrap , ov_f=gbrapf32, v_f=gbrpf32, a=unknown, ca=unknown, ca_f=unknown +gbrp6 = align=1:1 ov=gbrap , ov_f=gbrapf32, v_f=gbrpf32, a=unknown, ca=unknown, ca_f=unknown +gbrp9 = align=1:1 ov=gbrap , ov_f=gbrapf32, v_f=gbrpf32, a=unknown, ca=unknown, ca_f=unknown +gbrp9be = align=1:1 ov=gbrap , ov_f=gbrapf32, v_f=gbrpf32, a=unknown, ca=unknown, ca_f=unknown +gbrpf32 = align=1:1 ov=gbrap , ov_f=gbrapf32, v_f=gbrpf32, a=unknown, ca=unknown, ca_f=unknown +gbrpf32be = align=1:1 ov=gbrap , ov_f=gbrapf32, v_f=gbrpf32, a=unknown, ca=unknown, ca_f=unknown +gray = align=1:1 ov=yap8 , ov_f=grayaf32, v_f=grayf32, a=unknown, ca=unknown, ca_f=unknown +gray10 = align=1:1 ov=yap8 , ov_f=grayaf32, v_f=grayf32, a=unknown, ca=unknown, ca_f=unknown +gray10be = align=1:1 ov=yap8 , ov_f=grayaf32, v_f=grayf32, a=unknown, ca=unknown, ca_f=unknown +gray12 = align=1:1 ov=yap8 , ov_f=grayaf32, v_f=grayf32, a=unknown, ca=unknown, ca_f=unknown +gray12be = align=1:1 ov=yap8 , ov_f=grayaf32, v_f=grayf32, a=unknown, ca=unknown, ca_f=unknown +gray14 = align=1:1 ov=yap8 , ov_f=grayaf32, v_f=grayf32, a=unknown, ca=unknown, ca_f=unknown +gray14be = align=1:1 ov=yap8 , ov_f=grayaf32, v_f=grayf32, a=unknown, ca=unknown, ca_f=unknown +gray16 = align=1:1 ov=yap8 , ov_f=grayaf32, v_f=grayf32, a=unknown, ca=unknown, ca_f=unknown +gray16be = align=1:1 ov=yap8 , ov_f=grayaf32, v_f=grayf32, a=unknown, ca=unknown, ca_f=unknown +gray9 = align=1:1 ov=yap8 , ov_f=grayaf32, v_f=grayf32, a=unknown, ca=unknown, ca_f=unknown +gray9be = align=1:1 ov=yap8 , ov_f=grayaf32, v_f=grayf32, a=unknown, ca=unknown, ca_f=unknown +grayaf32 = align=1:1 ov=yap8 , ov_f=grayaf32, v_f=grayaf32, a=unknown, ca=unknown, ca_f=unknown +grayf32 = align=1:1 ov=yap8 , ov_f=grayaf32, v_f=grayf32, a=unknown, ca=unknown, ca_f=unknown +grayf32be = align=1:1 ov=yap8 , ov_f=grayaf32, v_f=grayf32, a=unknown, ca=unknown, ca_f=unknown +mediacodec = no +mmal = no +monob = align=8:1 ov=yap8 , ov_f=grayaf32, v_f=grayf32, a=unknown, ca=unknown, ca_f=unknown +monow = align=8:1 ov=yap8 , ov_f=grayaf32, v_f=grayf32, a=unknown, ca=unknown, ca_f=unknown +nv12 = align=2:2 ov=yuva420p, ov_f=yuva420pf, v_f=yuv420pf, a=gray, ca=gray, ca_f=grayf32 +nv16 = align=2:1 ov=yuva422p, ov_f=yuva422pf, v_f=yuv422pf, a=gray, ca=gray, ca_f=grayf32 +nv20 = align=2:1 ov=yuva422p, ov_f=yuva422pf, v_f=yuv422pf, a=gray, ca=gray, ca_f=grayf32 +nv20be = align=2:1 ov=yuva422p, ov_f=yuva422pf, v_f=yuv422pf, a=gray, ca=gray, ca_f=grayf32 +nv21 = align=2:2 ov=yuva420p, ov_f=yuva420pf, v_f=yuv420pf, a=gray, ca=gray, ca_f=grayf32 +nv24 = align=1:1 ov=yuva444p, ov_f=yuva444pf, v_f=yuv444pf, a=unknown, ca=unknown, ca_f=unknown +nv42 = align=1:1 ov=yuva444p, ov_f=yuva444pf, v_f=yuv444pf, a=unknown, ca=unknown, ca_f=unknown +opencl = no +p010 = align=2:2 ov=yuva420p, ov_f=yuva420pf, v_f=yuv420pf, a=gray, ca=gray, ca_f=grayf32 +p010be = align=2:2 ov=yuva420p, ov_f=yuva420pf, v_f=yuv420pf, a=gray, ca=gray, ca_f=grayf32 +p016 = align=2:2 ov=yuva420p, ov_f=yuva420pf, v_f=yuv420pf, a=gray, ca=gray, ca_f=grayf32 +p016be = align=2:2 ov=yuva420p, ov_f=yuva420pf, v_f=yuv420pf, a=gray, ca=gray, ca_f=grayf32 +pal8 = no +qsv = no +rgb0 = align=1:1 ov=gbrap , ov_f=gbrapf32, v_f=gbrpf32, a=unknown, ca=unknown, ca_f=unknown +rgb24 = align=1:1 ov=gbrap , ov_f=gbrapf32, v_f=gbrpf32, a=unknown, ca=unknown, ca_f=unknown +rgb30 = align=1:1 ov=gbrap , ov_f=gbrapf32, v_f=gbrpf32, a=unknown, ca=unknown, ca_f=unknown +rgb4 = no +rgb444 = align=1:1 ov=gbrap , ov_f=gbrapf32, v_f=gbrpf32, a=unknown, ca=unknown, ca_f=unknown +rgb444be = align=1:1 ov=gbrap , ov_f=gbrapf32, v_f=gbrpf32, a=unknown, ca=unknown, ca_f=unknown +rgb48 = align=1:1 ov=gbrap , ov_f=gbrapf32, v_f=gbrpf32, a=unknown, ca=unknown, ca_f=unknown +rgb48be = align=1:1 ov=gbrap , ov_f=gbrapf32, v_f=gbrpf32, a=unknown, ca=unknown, ca_f=unknown +rgb4_byte = align=1:1 ov=gbrap , ov_f=gbrapf32, v_f=gbrpf32, a=unknown, ca=unknown, ca_f=unknown +rgb555 = align=1:1 ov=gbrap , ov_f=gbrapf32, v_f=gbrpf32, a=unknown, ca=unknown, ca_f=unknown +rgb555be = align=1:1 ov=gbrap , ov_f=gbrapf32, v_f=gbrpf32, a=unknown, ca=unknown, ca_f=unknown +rgb565 = align=1:1 ov=gbrap , ov_f=gbrapf32, v_f=gbrpf32, a=unknown, ca=unknown, ca_f=unknown +rgb565be = align=1:1 ov=gbrap , ov_f=gbrapf32, v_f=gbrpf32, a=unknown, ca=unknown, ca_f=unknown +rgb8 = align=1:1 ov=gbrap , ov_f=gbrapf32, v_f=gbrpf32, a=unknown, ca=unknown, ca_f=unknown +rgba = align=1:1 ov=gbrap , ov_f=gbrapf32, v_f=gbrapf32, a=unknown, ca=unknown, ca_f=unknown +rgba64 = align=1:1 ov=gbrap , ov_f=gbrapf32, v_f=gbrapf32, a=unknown, ca=unknown, ca_f=unknown +rgba64be = align=1:1 ov=gbrap , ov_f=gbrapf32, v_f=gbrapf32, a=unknown, ca=unknown, ca_f=unknown +uyvy422 = align=2:1 ov=yuva422p, ov_f=yuva422pf, v_f=yuv422pf, a=gray, ca=gray, ca_f=grayf32 +uyyvyy411 = no +vaapi = no +vaapi_idct = no +vaapi_moco = no +vdpau = no +vdpau_output= no +videotoolbox= no +vulkan = no +xvmc = no +xyz12 = align=1:1 ov=gbrap , ov_f=gbrapf32, v_f=gbrpf32, a=unknown, ca=unknown, ca_f=unknown +xyz12be = align=1:1 ov=gbrap , ov_f=gbrapf32, v_f=gbrpf32, a=unknown, ca=unknown, ca_f=unknown +y1 = no +y210 = align=2:1 ov=yuva422p, ov_f=yuva422pf, v_f=yuv422pf, a=gray, ca=gray, ca_f=grayf32 +y210be = align=2:1 ov=yuva422p, ov_f=yuva422pf, v_f=yuv422pf, a=gray, ca=gray, ca_f=grayf32 +ya16 = align=1:1 ov=yap8 , ov_f=grayaf32, v_f=grayaf32, a=unknown, ca=unknown, ca_f=unknown +ya16be = align=1:1 ov=yap8 , ov_f=grayaf32, v_f=grayaf32, a=unknown, ca=unknown, ca_f=unknown +ya8 = align=1:1 ov=yap8 , ov_f=grayaf32, v_f=grayaf32, a=unknown, ca=unknown, ca_f=unknown +yap16 = align=1:1 ov=yap8 , ov_f=grayaf32, v_f=grayaf32, a=unknown, ca=unknown, ca_f=unknown +yap8 = align=1:1 ov=yap8 , ov_f=grayaf32, v_f=grayaf32, a=unknown, ca=unknown, ca_f=unknown +yuv410p = no +yuv410pf = no +yuv411p = no +yuv411pf = no +yuv420p = align=2:2 ov=yuva420p, ov_f=yuva420pf, v_f=yuv420pf, a=gray, ca=gray, ca_f=grayf32 +yuv420p10 = align=2:2 ov=yuva420p, ov_f=yuva420pf, v_f=yuv420pf, a=gray, ca=gray, ca_f=grayf32 +yuv420p10be = align=2:2 ov=yuva420p, ov_f=yuva420pf, v_f=yuv420pf, a=gray, ca=gray, ca_f=grayf32 +yuv420p12 = align=2:2 ov=yuva420p, ov_f=yuva420pf, v_f=yuv420pf, a=gray, ca=gray, ca_f=grayf32 +yuv420p12be = align=2:2 ov=yuva420p, ov_f=yuva420pf, v_f=yuv420pf, a=gray, ca=gray, ca_f=grayf32 +yuv420p14 = align=2:2 ov=yuva420p, ov_f=yuva420pf, v_f=yuv420pf, a=gray, ca=gray, ca_f=grayf32 +yuv420p14be = align=2:2 ov=yuva420p, ov_f=yuva420pf, v_f=yuv420pf, a=gray, ca=gray, ca_f=grayf32 +yuv420p16 = align=2:2 ov=yuva420p, ov_f=yuva420pf, v_f=yuv420pf, a=gray, ca=gray, ca_f=grayf32 +yuv420p16be = align=2:2 ov=yuva420p, ov_f=yuva420pf, v_f=yuv420pf, a=gray, ca=gray, ca_f=grayf32 +yuv420p9 = align=2:2 ov=yuva420p, ov_f=yuva420pf, v_f=yuv420pf, a=gray, ca=gray, ca_f=grayf32 +yuv420p9be = align=2:2 ov=yuva420p, ov_f=yuva420pf, v_f=yuv420pf, a=gray, ca=gray, ca_f=grayf32 +yuv420pf = align=2:2 ov=yuva420p, ov_f=yuva420pf, v_f=yuv420pf, a=gray, ca=gray, ca_f=grayf32 +yuv422p = align=2:1 ov=yuva422p, ov_f=yuva422pf, v_f=yuv422pf, a=gray, ca=gray, ca_f=grayf32 +yuv422p10 = align=2:1 ov=yuva422p, ov_f=yuva422pf, v_f=yuv422pf, a=gray, ca=gray, ca_f=grayf32 +yuv422p10be = align=2:1 ov=yuva422p, ov_f=yuva422pf, v_f=yuv422pf, a=gray, ca=gray, ca_f=grayf32 +yuv422p12 = align=2:1 ov=yuva422p, ov_f=yuva422pf, v_f=yuv422pf, a=gray, ca=gray, ca_f=grayf32 +yuv422p12be = align=2:1 ov=yuva422p, ov_f=yuva422pf, v_f=yuv422pf, a=gray, ca=gray, ca_f=grayf32 +yuv422p14 = align=2:1 ov=yuva422p, ov_f=yuva422pf, v_f=yuv422pf, a=gray, ca=gray, ca_f=grayf32 +yuv422p14be = align=2:1 ov=yuva422p, ov_f=yuva422pf, v_f=yuv422pf, a=gray, ca=gray, ca_f=grayf32 +yuv422p16 = align=2:1 ov=yuva422p, ov_f=yuva422pf, v_f=yuv422pf, a=gray, ca=gray, ca_f=grayf32 +yuv422p16be = align=2:1 ov=yuva422p, ov_f=yuva422pf, v_f=yuv422pf, a=gray, ca=gray, ca_f=grayf32 +yuv422p9 = align=2:1 ov=yuva422p, ov_f=yuva422pf, v_f=yuv422pf, a=gray, ca=gray, ca_f=grayf32 +yuv422p9be = align=2:1 ov=yuva422p, ov_f=yuva422pf, v_f=yuv422pf, a=gray, ca=gray, ca_f=grayf32 +yuv422pf = align=2:1 ov=yuva422p, ov_f=yuva422pf, v_f=yuv422pf, a=gray, ca=gray, ca_f=grayf32 +yuv440p = no +yuv440p10 = no +yuv440p10be = no +yuv440p12 = no +yuv440p12be = no +yuv440pf = no +yuv444p = align=1:1 ov=yuva444p, ov_f=yuva444pf, v_f=yuv444pf, a=unknown, ca=unknown, ca_f=unknown +yuv444p10 = align=1:1 ov=yuva444p, ov_f=yuva444pf, v_f=yuv444pf, a=unknown, ca=unknown, ca_f=unknown +yuv444p10be = align=1:1 ov=yuva444p, ov_f=yuva444pf, v_f=yuv444pf, a=unknown, ca=unknown, ca_f=unknown +yuv444p12 = align=1:1 ov=yuva444p, ov_f=yuva444pf, v_f=yuv444pf, a=unknown, ca=unknown, ca_f=unknown +yuv444p12be = align=1:1 ov=yuva444p, ov_f=yuva444pf, v_f=yuv444pf, a=unknown, ca=unknown, ca_f=unknown +yuv444p14 = align=1:1 ov=yuva444p, ov_f=yuva444pf, v_f=yuv444pf, a=unknown, ca=unknown, ca_f=unknown +yuv444p14be = align=1:1 ov=yuva444p, ov_f=yuva444pf, v_f=yuv444pf, a=unknown, ca=unknown, ca_f=unknown +yuv444p16 = align=1:1 ov=yuva444p, ov_f=yuva444pf, v_f=yuv444pf, a=unknown, ca=unknown, ca_f=unknown +yuv444p16be = align=1:1 ov=yuva444p, ov_f=yuva444pf, v_f=yuv444pf, a=unknown, ca=unknown, ca_f=unknown +yuv444p9 = align=1:1 ov=yuva444p, ov_f=yuva444pf, v_f=yuv444pf, a=unknown, ca=unknown, ca_f=unknown +yuv444p9be = align=1:1 ov=yuva444p, ov_f=yuva444pf, v_f=yuv444pf, a=unknown, ca=unknown, ca_f=unknown +yuv444pf = align=1:1 ov=yuva444p, ov_f=yuva444pf, v_f=yuv444pf, a=unknown, ca=unknown, ca_f=unknown +yuva410pf = no +yuva411pf = no +yuva420p = align=2:2 ov=yuva420p, ov_f=yuva420pf, v_f=yuva420pf, a=gray, ca=gray, ca_f=grayf32 +yuva420p10 = align=2:2 ov=yuva420p, ov_f=yuva420pf, v_f=yuva420pf, a=gray, ca=gray, ca_f=grayf32 +yuva420p10be= align=2:2 ov=yuva420p, ov_f=yuva420pf, v_f=yuva420pf, a=gray, ca=gray, ca_f=grayf32 +yuva420p16 = align=2:2 ov=yuva420p, ov_f=yuva420pf, v_f=yuva420pf, a=gray, ca=gray, ca_f=grayf32 +yuva420p16be= align=2:2 ov=yuva420p, ov_f=yuva420pf, v_f=yuva420pf, a=gray, ca=gray, ca_f=grayf32 +yuva420p9 = align=2:2 ov=yuva420p, ov_f=yuva420pf, v_f=yuva420pf, a=gray, ca=gray, ca_f=grayf32 +yuva420p9be = align=2:2 ov=yuva420p, ov_f=yuva420pf, v_f=yuva420pf, a=gray, ca=gray, ca_f=grayf32 +yuva420pf = align=2:2 ov=yuva420p, ov_f=yuva420pf, v_f=yuva420pf, a=gray, ca=gray, ca_f=grayf32 +yuva422p = align=2:1 ov=yuva422p, ov_f=yuva422pf, v_f=yuva422pf, a=gray, ca=gray, ca_f=grayf32 +yuva422p10 = align=2:1 ov=yuva422p, ov_f=yuva422pf, v_f=yuva422pf, a=gray, ca=gray, ca_f=grayf32 +yuva422p10be= align=2:1 ov=yuva422p, ov_f=yuva422pf, v_f=yuva422pf, a=gray, ca=gray, ca_f=grayf32 +yuva422p12 = align=2:1 ov=yuva422p, ov_f=yuva422pf, v_f=yuva422pf, a=gray, ca=gray, ca_f=grayf32 +yuva422p12be= align=2:1 ov=yuva422p, ov_f=yuva422pf, v_f=yuva422pf, a=gray, ca=gray, ca_f=grayf32 +yuva422p16 = align=2:1 ov=yuva422p, ov_f=yuva422pf, v_f=yuva422pf, a=gray, ca=gray, ca_f=grayf32 +yuva422p16be= align=2:1 ov=yuva422p, ov_f=yuva422pf, v_f=yuva422pf, a=gray, ca=gray, ca_f=grayf32 +yuva422p9 = align=2:1 ov=yuva422p, ov_f=yuva422pf, v_f=yuva422pf, a=gray, ca=gray, ca_f=grayf32 +yuva422p9be = align=2:1 ov=yuva422p, ov_f=yuva422pf, v_f=yuva422pf, a=gray, ca=gray, ca_f=grayf32 +yuva422pf = align=2:1 ov=yuva422p, ov_f=yuva422pf, v_f=yuva422pf, a=gray, ca=gray, ca_f=grayf32 +yuva440pf = no +yuva444p = align=1:1 ov=yuva444p, ov_f=yuva444pf, v_f=yuva444pf, a=unknown, ca=unknown, ca_f=unknown +yuva444p10 = align=1:1 ov=yuva444p, ov_f=yuva444pf, v_f=yuva444pf, a=unknown, ca=unknown, ca_f=unknown +yuva444p10be= align=1:1 ov=yuva444p, ov_f=yuva444pf, v_f=yuva444pf, a=unknown, ca=unknown, ca_f=unknown +yuva444p12 = align=1:1 ov=yuva444p, ov_f=yuva444pf, v_f=yuva444pf, a=unknown, ca=unknown, ca_f=unknown +yuva444p12be= align=1:1 ov=yuva444p, ov_f=yuva444pf, v_f=yuva444pf, a=unknown, ca=unknown, ca_f=unknown +yuva444p16 = align=1:1 ov=yuva444p, ov_f=yuva444pf, v_f=yuva444pf, a=unknown, ca=unknown, ca_f=unknown +yuva444p16be= align=1:1 ov=yuva444p, ov_f=yuva444pf, v_f=yuva444pf, a=unknown, ca=unknown, ca_f=unknown +yuva444p9 = align=1:1 ov=yuva444p, ov_f=yuva444pf, v_f=yuva444pf, a=unknown, ca=unknown, ca_f=unknown +yuva444p9be = align=1:1 ov=yuva444p, ov_f=yuva444pf, v_f=yuva444pf, a=unknown, ca=unknown, ca_f=unknown +yuva444pf = align=1:1 ov=yuva444p, ov_f=yuva444pf, v_f=yuva444pf, a=unknown, ca=unknown, ca_f=unknown +yuvj411p = no +yuvj422p = align=2:1 ov=yuva422p, ov_f=yuva422pf, v_f=yuv422pf, a=gray, ca=gray, ca_f=grayf32 +yuvj440p = no +yuyv422 = align=2:1 ov=yuva422p, ov_f=yuva422pf, v_f=yuv422pf, a=gray, ca=gray, ca_f=grayf32 +yvyu422 = align=2:1 ov=yuva422p, ov_f=yuva422pf, v_f=yuv422pf, a=gray, ca=gray, ca_f=grayf32 diff --git a/test/repack.c b/test/repack.c index 4489bd1ea1..aa9c804308 100644 --- a/test/repack.c +++ b/test/repack.c @@ -1,6 +1,8 @@ #include #include "common/common.h" +#include "sub/draw_bmp.h" +#include "sub/osd.h" #include "tests.h" #include "video/fmt-conversion.h" #include "video/img_format.h" @@ -331,6 +333,55 @@ static void check_float_repack(int imgfmt, enum mp_csp csp, talloc_free(from_f); } +static bool try_draw_bmp(FILE *f, int imgfmt) +{ + bool ok = false; + + struct mp_image *dst = mp_image_alloc(imgfmt, 64, 64); + if (!dst) + goto done; + + 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 = NULL; + if (mp_draw_sub_bitmaps(&c, dst, &sbs_list)) { + 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"); @@ -359,6 +410,22 @@ static void run(struct test_ctx *ctx) 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); + + // 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(f, imgfmt); + } + + 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 = { diff --git a/video/filter/vf_sub.c b/video/filter/vf_sub.c index ab2e308563..5c49ac56a3 100644 --- a/video/filter/vf_sub.c +++ b/video/filter/vf_sub.c @@ -79,9 +79,6 @@ static void vf_sub_process(struct mp_filter *f) struct mp_image *mpi = frame.data; - if (!mp_sws_supported_format(mpi->imgfmt)) - goto error; - struct mp_osd_res dim = { .w = mpi->w, .h = mpi->h + priv->opts->top_margin + priv->opts->bottom_margin,