diff options
Diffstat (limited to 'sub')
-rw-r--r-- | sub/ass_mp.c | 53 | ||||
-rw-r--r-- | sub/ass_mp.h | 11 | ||||
-rw-r--r-- | sub/dec_sub.c | 26 | ||||
-rw-r--r-- | sub/dec_sub.h | 39 | ||||
-rw-r--r-- | sub/draw_bmp.c | 651 | ||||
-rw-r--r-- | sub/draw_bmp.h | 17 | ||||
-rw-r--r-- | sub/find_subfiles.c | 1 | ||||
-rw-r--r-- | sub/img_convert.c | 89 | ||||
-rw-r--r-- | sub/img_convert.h | 15 | ||||
-rw-r--r-- | sub/osd_dummy.c | 22 | ||||
-rw-r--r-- | sub/osd_libass.c | 234 | ||||
-rw-r--r-- | sub/sd.h | 5 | ||||
-rw-r--r-- | sub/sd_ass.c | 22 | ||||
-rw-r--r-- | sub/sd_lavc.c | 133 | ||||
-rw-r--r-- | sub/spudec.c | 821 | ||||
-rw-r--r-- | sub/spudec.h | 21 | ||||
-rw-r--r-- | sub/sub.c | 427 | ||||
-rw-r--r-- | sub/sub.h | 264 |
18 files changed, 1405 insertions, 1446 deletions
diff --git a/sub/ass_mp.c b/sub/ass_mp.c index 908a552acf..1867880f38 100644 --- a/sub/ass_mp.c +++ b/sub/ass_mp.c @@ -61,17 +61,6 @@ ASS_Track *mp_ass_default_track(ASS_Library *library, struct MPOpts *opts) style->treat_fontname_as_pattern = 1; double fs = track->PlayResY * text_font_scale_factor / 100.; - /* The font size is always proportional to video height only; - * real -subfont-autoscale behavior is not implemented. - * Apply a correction that corresponds to about 4:3 aspect ratio - * video to get a size somewhat closer to what non-libass rendering - * would produce with the same text_font_scale_factor - * and subtitle_autoscale. - */ - if (subtitle_autoscale == 2) - fs *= 1.3; - else if (subtitle_autoscale == 3) - fs *= 1.7; uint32_t c1 = 0xFFFFFF00; uint32_t c2 = 0x00000000; @@ -228,7 +217,7 @@ ASS_Track *mp_ass_read_stream(ASS_Library *library, const char *fname, } void mp_ass_configure(ASS_Renderer *priv, struct MPOpts *opts, - struct mp_eosd_res *dim, bool unscaled) + struct mp_osd_res *dim) { ass_set_frame_size(priv, dim->w, dim->h); ass_set_margins(priv, dim->mt, dim->mb, dim->ml, dim->mr); @@ -243,10 +232,7 @@ void mp_ass_configure(ASS_Renderer *priv, struct MPOpts *opts, set_sub_pos = 100 - sub_pos; set_line_spacing = opts->ass_line_spacing; set_font_scale = opts->ass_font_scale; - if (!unscaled && (opts->ass_hinting & 4)) - set_hinting = 0; - else - set_hinting = opts->ass_hinting & 3; + set_hinting = opts->ass_hinting & 3; // +4 was for no hinting if scaled } ass_set_use_margins(priv, set_use_margins); @@ -281,6 +267,41 @@ void mp_ass_configure_fonts(ASS_Renderer *priv) free(family); } +void mp_ass_render_frame(ASS_Renderer *renderer, ASS_Track *track, double time, + struct sub_bitmap **parts, struct sub_bitmaps *res) +{ + int changed; + ASS_Image *imgs = ass_render_frame(renderer, track, time, &changed); + if (changed == 2) + res->bitmap_id = ++res->bitmap_pos_id; + else if (changed) + res->bitmap_pos_id++; + res->format = SUBBITMAP_LIBASS; + + res->parts = *parts; + res->num_parts = 0; + int num_parts_alloc = MP_TALLOC_ELEMS(res->parts); + for (struct ass_image *img = imgs; img; img = img->next) { + if (img->w == 0 || img->h == 0) + continue; + if (res->num_parts >= num_parts_alloc) { + num_parts_alloc = FFMAX(num_parts_alloc * 2, 32); + res->parts = talloc_realloc(NULL, res->parts, struct sub_bitmap, + num_parts_alloc); + } + struct sub_bitmap *p = &res->parts[res->num_parts]; + p->bitmap = img->bitmap; + p->stride = img->stride; + p->libass.color = img->color; + p->dw = p->w = img->w; + p->dh = p->h = img->h; + p->x = img->dst_x; + p->y = img->dst_y; + res->num_parts++; + } + *parts = res->parts; +} + static int map_ass_level[] = { MSGL_ERR, // 0 "FATAL errors" MSGL_WARN, diff --git a/sub/ass_mp.h b/sub/ass_mp.h index 3cfbe147b7..c3dbc5e28f 100644 --- a/sub/ass_mp.h +++ b/sub/ass_mp.h @@ -32,7 +32,7 @@ #include <ass/ass_types.h> struct MPOpts; -struct mp_eosd_res; +struct mp_osd_res; ASS_Track *mp_ass_default_track(ASS_Library *library, struct MPOpts *opts); ASS_Track *mp_ass_read_subdata(ASS_Library *library, struct MPOpts *opts, @@ -42,13 +42,18 @@ ASS_Track *mp_ass_read_stream(ASS_Library *library, const char *fname, struct MPOpts; void mp_ass_configure(ASS_Renderer *priv, struct MPOpts *opts, - struct mp_eosd_res *dim, bool unscaled); + struct mp_osd_res *dim); void mp_ass_configure_fonts(ASS_Renderer *priv); ASS_Library *mp_ass_init(struct MPOpts *opts); +struct sub_bitmap; +struct sub_bitmaps; +void mp_ass_render_frame(ASS_Renderer *renderer, ASS_Track *track, double time, + struct sub_bitmap **parts, struct sub_bitmaps *res); + #else /* CONFIG_ASS */ -/* Needed for EOSD code using this type to compile */ +/* Needed for OSD code using this type to compile */ typedef struct ass_image { int w, h; diff --git a/sub/dec_sub.c b/sub/dec_sub.c index 4a048b27a6..5ceb3b2422 100644 --- a/sub/dec_sub.c +++ b/sub/dec_sub.c @@ -39,13 +39,13 @@ void sub_init(struct sh_sub *sh, struct osd_state *osd) if (opts->ass_enabled && is_text_sub(sh->type)) sh->sd_driver = &sd_ass; #endif - if (strchr("bpx", sh->type)) + if (strchr("bpxv", sh->type)) sh->sd_driver = &sd_lavc; if (sh->sd_driver) { if (sh->sd_driver->init(sh, osd) < 0) return; osd->sh_sub = sh; - osd->bitmap_id = ++osd->bitmap_pos_id; + osd->switch_sub_id++; sh->initialized = true; sh->active = true; } @@ -58,13 +58,12 @@ void sub_decode(struct sh_sub *sh, struct osd_state *osd, void *data, sh->sd_driver->decode(sh, osd, data, data_len, pts, duration); } -void sub_get_bitmaps(struct osd_state *osd, struct sub_bitmaps *res) +void sub_get_bitmaps(struct osd_state *osd, struct mp_osd_res dim, double pts, + struct sub_bitmaps *res) { struct MPOpts *opts = osd->opts; - *res = (struct sub_bitmaps){ .type = SUBBITMAP_EMPTY, - .bitmap_id = osd->bitmap_id, - .bitmap_pos_id = osd->bitmap_pos_id }; + *res = (struct sub_bitmaps) {0}; if (!opts->sub_visibility || !osd->sh_sub || !osd->sh_sub->active) { /* Change ID in case we just switched from visible subtitles * to current state. Hopefully, unnecessarily claiming that @@ -72,14 +71,15 @@ void sub_get_bitmaps(struct osd_state *osd, struct sub_bitmaps *res) * Increase osd-> values ahead so that _next_ returned id * is also guaranteed to differ from this one. */ - res->bitmap_id = ++res->bitmap_pos_id; - osd->bitmap_id = osd->bitmap_pos_id += 2; - return; + osd->switch_sub_id++; + } else { + if (osd->sh_sub->sd_driver->get_bitmaps) + osd->sh_sub->sd_driver->get_bitmaps(osd->sh_sub, osd, dim, pts, res); } - if (osd->sh_sub->sd_driver->get_bitmaps) - osd->sh_sub->sd_driver->get_bitmaps(osd->sh_sub, osd, res); - osd->bitmap_id = res->bitmap_id; - osd->bitmap_pos_id = res->bitmap_pos_id; + + res->bitmap_id += osd->switch_sub_id; + res->bitmap_pos_id += osd->switch_sub_id; + osd->switch_sub_id = 0; } void sub_reset(struct sh_sub *sh, struct osd_state *osd) diff --git a/sub/dec_sub.h b/sub/dec_sub.h index c71a2348aa..f66f05c021 100644 --- a/sub/dec_sub.h +++ b/sub/dec_sub.h @@ -1,38 +1,14 @@ #ifndef MPLAYER_DEC_SUB_H #define MPLAYER_DEC_SUB_H -struct sh_sub; -struct osd_state; -struct ass_track; - -enum sub_bitmap_type { - SUBBITMAP_EMPTY, - SUBBITMAP_LIBASS, - SUBBITMAP_RGBA, -}; - -typedef struct mp_eosd_res { - int w, h; // screen dimensions, including black borders - int mt, mb, ml, mr; // borders (top, bottom, left, right) -} mp_eosd_res_t; +#include <stdbool.h> +#include <stdint.h> -typedef struct sub_bitmaps { - enum sub_bitmap_type type; +#include "sub/sub.h" - struct ass_image *imgs; - - struct sub_bitmap { - int w, h; - int x, y; - // Note: not clipped, going outside the screen area is allowed - int dw, dh; - void *bitmap; - } *parts; - int part_count; - - bool scaled; - int bitmap_id, bitmap_pos_id; -} mp_eosd_images_t; +struct sh_sub; +struct ass_track; +struct MPOpts; static inline bool is_text_sub(int type) { @@ -41,7 +17,8 @@ static inline bool is_text_sub(int type) void sub_decode(struct sh_sub *sh, struct osd_state *osd, void *data, int data_len, double pts, double duration); -void sub_get_bitmaps(struct osd_state *osd, struct sub_bitmaps *res); +void sub_get_bitmaps(struct osd_state *osd, struct mp_osd_res dim, double pts, + struct sub_bitmaps *res); void sub_init(struct sh_sub *sh, struct osd_state *osd); void sub_reset(struct sh_sub *sh, struct osd_state *osd); void sub_switchoff(struct sh_sub *sh, struct osd_state *osd); diff --git a/sub/draw_bmp.c b/sub/draw_bmp.c new file mode 100644 index 0000000000..d58033fceb --- /dev/null +++ b/sub/draw_bmp.c @@ -0,0 +1,651 @@ +/* + * This file is part of mpv. + * + * mpv is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * mpv is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with mpv; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "sub/draw_bmp.h" + +#include <stddef.h> +#include <stdbool.h> +#include <assert.h> +#include <math.h> + +#include "sub/sub.h" +#include "libmpcodecs/mp_image.h" +#include "libmpcodecs/sws_utils.h" +#include "libmpcodecs/img_format.h" +#include "libvo/csputils.h" + +const bool mp_draw_sub_formats[SUBBITMAP_COUNT] = { + [SUBBITMAP_LIBASS] = true, + [SUBBITMAP_RGBA] = true, +}; + +struct sub_cache { + struct mp_image *i, *a; +}; + +struct part { + int bitmap_pos_id; + int num_imgs; + struct sub_cache *imgs; +}; + +struct mp_draw_sub_cache +{ + struct part *parts[MAX_OSD_PARTS]; +}; + +#define ACCURATE +#define CONDITIONAL +#define CONDITIONAL2 + +static void blend_const16_alpha(uint8_t *dst, + ssize_t dstRowStride, + uint16_t srcp, + const uint8_t *srca, + ssize_t srcaRowStride, + uint8_t srcamul, int rows, + int cols) +{ + int i, j; +#ifdef CONDITIONAL + if (!srcamul) + return; +#endif + for (i = 0; i < rows; ++i) { + uint16_t *dstr = (uint16_t *) (dst + dstRowStride * i); + const uint8_t *srcar = srca + srcaRowStride * i; + for (j = 0; j < cols; ++j) { + uint32_t srcap = srcar[j]; + // 32bit to force the math ops to operate on 32 bit +#ifdef CONDITIONAL + if (!srcap) + continue; +#endif +#ifdef CONDITIONAL2 + if (srcap == 255 && srcamul == 255) { + dstr[j] = srcp; + continue; + } +#endif + uint16_t dstp = dstr[j]; + srcap *= srcamul; // now 0..65025 + uint16_t outp = + (srcp * srcap + dstp * (65025 - srcap) + 32512) / 65025; + dstr[j] = outp; + } + } +} + +static void blend_src16_alpha(uint8_t *dst, + ssize_t dstRowStride, + const uint8_t *src, + ssize_t srcRowStride, + const uint8_t *srca, + ssize_t srcaRowStride, + int rows, + int cols) +{ + int i, j; + for (i = 0; i < rows; ++i) { + uint16_t *dstr = (uint16_t *) (dst + dstRowStride * i); + const uint16_t *srcr = (const uint16_t *) (src + srcRowStride * i); + const uint8_t *srcar = srca + srcaRowStride * i; + for (j = 0; j < cols; ++j) { + uint32_t srcap = srcar[j]; + // 32bit to force the math ops to operate on 32 bit +#ifdef CONDITIONAL + if (!srcap) + continue; +#endif + uint16_t srcp = srcr[j]; +#ifdef CONDITIONAL2 + if (srcap == 255) { + dstr[j] = srcp; + continue; + } +#endif + uint16_t dstp = dstr[j]; + uint16_t outp = + (srcp * srcap + dstp * (255 - srcap) + 127) / 255; + dstr[j] = outp; + } + } +} + +static void blend_const8_alpha(uint8_t *dst, + ssize_t dstRowStride, + uint16_t srcp, + const uint8_t *srca, + ssize_t srcaRowStride, + uint8_t srcamul, int rows, + int cols) +{ + int i, j; +#ifdef CONDITIONAL + if (!srcamul) + return; +#endif + for (i = 0; i < rows; ++i) { + uint8_t *dstr = dst + dstRowStride * i; + const uint8_t *srcar = srca + srcaRowStride * i; + for (j = 0; j < cols; ++j) { + uint32_t srcap = srcar[j]; + // 32bit to force the math ops to operate on 32 bit +#ifdef CONDITIONAL + if (!srcap) + continue; +#endif +#ifdef CONDITIONAL2 + if (srcap == 255 && srcamul == 255) { + dstr[j] = srcp; + continue; + } +#endif + uint8_t dstp = dstr[j]; +#ifdef ACCURATE + srcap *= srcamul; // now 0..65025 + uint8_t outp = + (srcp * srcap + dstp * (65025 - srcap) + 32512) / 65025; + dstr[j] = outp; +#else + srcap = (srcap * srcamul + 255) >> 8; + uint8_t outp = + (srcp * srcap + dstp * (255 - srcap) + 255) >> 8; + dstr[j] = outp; +#endif + } + } +} + +static void blend_src8_alpha(uint8_t *dst, + ssize_t dstRowStride, + const uint8_t *src, + ssize_t srcRowStride, + const uint8_t *srca, + ssize_t srcaRowStride, + int rows, + int cols) +{ + int i, j; + for (i = 0; i < rows; ++i) { + uint8_t *dstr = dst + dstRowStride * i; + const uint8_t *srcr = src + srcRowStride * i; + const uint8_t *srcar = srca + srcaRowStride * i; + for (j = 0; j < cols; ++j) { + uint16_t srcap = srcar[j]; + // 16bit to force the math ops to operate on 16 bit +#ifdef CONDITIONAL + if (!srcap) + continue; +#endif + uint8_t srcp = srcr[j]; +#ifdef CONDITIONAL2 + if (srcap == 255) { + dstr[j] = srcp; + continue; + } +#endif + uint8_t dstp = dstr[j]; +#ifdef ACCURATE + uint8_t outp = + (srcp * srcap + dstp * (255 - srcap) + 127) / 255; +#else + uint8_t outp = + (srcp * srcap + dstp * (255 - srcap) + 255) >> 8; +#endif + dstr[j] = outp; + } + } +} + +static void blend_src_alpha(uint8_t *dst, ssize_t dstRowStride, + const uint8_t *src, ssize_t srcRowStride, + const uint8_t *srca, ssize_t srcaRowStride, + int rows, int cols, int bytes) +{ + if (bytes == 2) { + blend_src16_alpha(dst, dstRowStride, src, + srcRowStride, srca, + srcaRowStride, rows, cols); + } else if (bytes == 1) { + blend_src8_alpha(dst, dstRowStride, src, + srcRowStride, srca, + srcaRowStride, rows, cols); + } +} + +static void blend_const_alpha(uint8_t *dst, ssize_t dstRowStride, + uint16_t srcp, + const uint8_t *srca, ssize_t srcaRowStride, + uint8_t srcamul, + int rows, int cols, int bytes) +{ + if (bytes == 2) { + blend_const16_alpha(dst, dstRowStride, srcp, + srca, srcaRowStride, + srcamul, rows, + cols); + } else if (bytes == 1) { + blend_const8_alpha(dst, dstRowStride, srcp, + srca, srcaRowStride, srcamul, + rows, + cols); + } +} + +static inline int min(int x, int y) +{ + if (x < y) + return x; + else + return y; +} + +static void unpremultiply_and_split_IMGFMT_BGR32(mp_image_t *img, + mp_image_t *alpha) +{ + int x, y; + for (y = 0; y < img->h; ++y) { + uint32_t *irow = (uint32_t *) &img->planes[0][img->stride[0] * y]; + unsigned char *arow = &alpha->planes[0][alpha->stride[0] * y]; + for (x = 0; x < img->w; ++x) { + uint32_t pval = irow[x]; + unsigned char aval = (pval >> 24); + unsigned char rval = (pval >> 16) & 0xFF; + unsigned char gval = (pval >> 8) & 0xFF; + unsigned char 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 = min(255, (rval * 255 + add) / div); + gval = min(255, (gval * 255 + add) / div); + bval = min(255, (bval * 255 + add) / div); + irow[x] = bval + (gval << 8) + (rval << 16) + (aval << 24); + } + arow[x] = aval; + } + } +} + +static bool sub_bitmap_to_mp_images(struct mp_image **sbi, int *color_yuv, + int *color_a, struct mp_image **sba, + struct sub_bitmap *sb, + int format, struct mp_csp_details *csp, + float rgb2yuv[3][4], int imgfmt, int bits) +{ + *sbi = NULL; + *sba = NULL; + if (format == SUBBITMAP_RGBA && sb->w >= 8) { + // >= 8 because of libswscale madness + // swscale the bitmap from w*h to dw*dh, changing BGRA8 into YUV444P16 + // and make a scaled copy of A8 + mp_image_t *sbisrc = new_mp_image(sb->w, sb->h); + mp_image_setfmt(sbisrc, IMGFMT_BGR32); + sbisrc->planes[0] = sb->bitmap; + sbisrc->stride[0] = sb->stride; + mp_image_t *sbisrc2 = alloc_mpi(sb->dw, sb->dh, IMGFMT_BGR32); + mp_image_swscale(sbisrc2, sbisrc, csp, SWS_BILINEAR); + + // sbisrc2 now is the original image in premultiplied alpha, but + // properly scaled... + // now, un-premultiply so we can work in YUV color space, also extract + // alpha + *sba = alloc_mpi(sb->dw, sb->dh, IMGFMT_Y8); + unpremultiply_and_split_IMGFMT_BGR32(sbisrc2, *sba); + + // convert to the output format + *sbi = alloc_mpi(sb->dw, sb->dh, imgfmt); + mp_image_swscale(*sbi, sbisrc2, csp, SWS_BILINEAR); + + free_mp_image(sbisrc); + free_mp_image(sbisrc2); + + color_yuv[0] = 255; + color_yuv[1] = 128; + color_yuv[2] = 128; + *color_a = 255; + return true; + } else if (format == SUBBITMAP_LIBASS && + sb->w == sb->dw && sb->h == sb->dh) { + // swscale alpha only + *sba = new_mp_image(sb->w, sb->h); + mp_image_setfmt(*sba, IMGFMT_Y8); + (*sba)->planes[0] = sb->bitmap; + (*sba)->stride[0] = sb->stride; + int r = (sb->libass.color >> 24) & 0xFF; + int g = (sb->libass.color >> 16) & 0xFF; + int b = (sb->libass.color >> 8) & 0xFF; + int a = sb->libass.color & 0xFF; + color_yuv[0] = + rint(MP_MAP_RGB2YUV_COLOR(rgb2yuv, r, g, b, 255, 0) + * (1 << (bits - 8))); + color_yuv[1] = + rint(MP_MAP_RGB2YUV_COLOR(rgb2yuv, r, g, b, 255, 1) + * (1 << (bits - 8))); + color_yuv[2] = + rint(MP_MAP_RGB2YUV_COLOR(rgb2yuv, r, g, b, 255, 2) + * (1 << (bits - 8))); + *color_a = 255 - a; + // NOTE: these overflows can actually happen (when subtitles use color + // 0,0,0 while output levels only allows 16,16,16 upwards...) + if (color_yuv[0] < 0) + color_yuv[0] = 0; + if (color_yuv[1] < 0) + color_yuv[1] = 0; + if (color_yuv[2] < 0) + color_yuv[2] = 0; + if (*color_a < 0) + *color_a = 0; + if (color_yuv[0] > ((1 << bits) - 1)) + color_yuv[0] = ((1 << bits) - 1); + if (color_yuv[1] > ((1 << bits) - 1)) + color_yuv[1] = ((1 << bits) - 1); + if (color_yuv[2] > ((1 << bits) - 1)) + color_yuv[2] = ((1 << bits) - 1); + if (*color_a > 255) + *color_a = 255; + return true; + } else + return false; +} + +static void mp_image_crop(struct mp_image *img, int x, int y, int w, int h) +{ + int p; + for (p = 0; p < img->num_planes; ++p) { + int bits = MP_IMAGE_BITS_PER_PIXEL_ON_PLANE(img, p); + img->planes[p] += + (y >> (p ? img->chroma_y_shift : 0)) * img->stride[p] + + ((x >> (p ? img->chroma_x_shift : 0)) * bits) / 8; + } + img->w = w; + img->h = h; +} + +static bool clip_to_bounds(int *x, int *y, int *w, int *h, + int bx, int by, int bw, int bh) +{ + if (*x < bx) { + *w += *x - bx; + *x = bx; + } + if (*y < 0) { + *h += *y - by; + *y = by; + } + if (*x + *w > bx + bw) + *w = bx + bw - *x; + if (*y + *h > by + bh) + *h = by + bh - *y; + + if (*w <= 0 || *h <= 0) + return false; // nothing left + + return true; +} + +static void get_swscale_requirements(int *sx, int *sy, + const struct mp_image *img) +{ + int p; + + if (img->chroma_x_shift == 31) + *sx = 1; + else + *sx = (1 << img->chroma_x_shift); + + if (img->chroma_y_shift == 31) + *sy = 1; + else + *sy = (1 << img->chroma_y_shift); + + for (p = 0; p < img->num_planes; ++p) { + int bits = MP_IMAGE_BITS_PER_PIXEL_ON_PLANE(img, p); + // the * 2 fixes problems with writing past the destination width + while (((*sx >> img->chroma_x_shift) * bits) % (SWS_MIN_BYTE_ALIGN * 8 * 2)) + *sx *= 2; + } +} + +static void align_bbox(int *x1, int *y1, int *x2, int *y2, int xstep, int ystep) +{ + *x1 -= (*x1 % xstep); + *y1 -= (*y1 % ystep); + + *x2 += xstep - 1; + *y2 += ystep - 1; + *x2 -= (*x2 % xstep); + *y2 -= (*y2 % ystep); +} + +static bool align_bbox_to_swscale_requirements(int *x1, int *y1, + int *x2, int *y2, + struct mp_image *img) +{ + int xstep, ystep; + get_swscale_requirements(&xstep, &ystep, img); + align_bbox(x1, y1, x2, y2, xstep, ystep); + + if (*x1 < 0) + *x1 = 0; + if (*y1 < 0) + *y1 = 0; + if (*x2 > img->w) + *x2 = img->w; + if (*y2 > img->h) + *y2 = img->h; + + return (*x2 > *x1) && (*y2 > *y1); +} + +// 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, + struct sub_bitmaps *sbs, struct mp_csp_details *csp) +{ + int i; + int x1, y1, x2, y2; + int color_yuv[3]; + int color_a; + float yuv2rgb[3][4]; + float rgb2yuv[3][4]; + + if (!mp_sws_supported_format(dst->imgfmt)) + return; + + if (cache && !*cache) + *cache = talloc_zero(NULL, struct mp_draw_sub_cache); + + struct part *part = NULL; + + bool use_cache = sbs->format == SUBBITMAP_RGBA; + if (cache && use_cache) { + part = (*cache)->parts[sbs->render_index]; + if (part && part->bitmap_pos_id != sbs->bitmap_pos_id) { + talloc_free(part); + part = NULL; + } + if (!part) { + part = talloc_zero(*cache, struct part); + part->bitmap_pos_id = sbs->bitmap_pos_id; + part->num_imgs = sbs->num_parts; + 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; + } + +#ifdef ACCURATE + int format = IMGFMT_444P16; + int bits = 16; + // however, we can try matching 8bit, 9bit, 10bit yuv formats! + if (dst->flags & MP_IMGFLAG_YUV) { + if (mp_get_chroma_shift(dst->imgfmt, NULL, NULL, &bits)) { + switch (bits) { + case 8: + format = IMGFMT_444P; + break; + case 9: + format = IMGFMT_444P9; + break; + case 10: + format = IMGFMT_444P10; + break; + default: + // revert back + bits = 16; + break; + } + } else + bits = 16; + } +#else + int format = IMGFMT_444P; + int bits = 8; +#endif + int bytes = (bits + 7) / 8; + + struct mp_csp_params cspar = { + .colorspace = *csp, + .brightness = 0, .contrast = 1, + .hue = 0, .saturation = 1, + .rgamma = 1, .ggamma = 1, .bgamma = 1, + .texture_bits = 8, .input_bits = 8 + }; + + // prepare YUV/RGB conversion values + mp_get_yuv2rgb_coeffs(&cspar, yuv2rgb); + mp_invert_yuv2rgb(rgb2yuv, yuv2rgb); + + //mp_msg(MSGT_VO, MSGL_ERR, "%f %f %f %f // %f %f %f %f // %f %f %f %f\n", + // rgb2yuv[0][0], + // rgb2yuv[0][1], + // rgb2yuv[0][2], + // rgb2yuv[0][3], + // rgb2yuv[1][0], + // rgb2yuv[1][1], + // rgb2yuv[1][2], + // rgb2yuv[1][3], + // rgb2yuv[2][0], + // rgb2yuv[2][1], + // rgb2yuv[2][2], + // rgb2yuv[2][3]); + + // calculate bounding range + if (!sub_bitmaps_bb(sbs, &x1, &y1, &x2, &y2)) + return; + + if (!align_bbox_to_swscale_requirements(&x1, &y1, &x2, &y2, dst)) + return; // nothing to do + + // convert to a temp image + mp_image_t *temp; + mp_image_t dst_region = *dst; + if (dst->imgfmt == format) { + mp_image_crop(&dst_region, x1, y1, x2 - x1, y2 - y1); + temp = &dst_region; + } else { + mp_image_crop(&dst_region, x1, y1, x2 - x1, y2 - y1); + temp = alloc_mpi(x2 - x1, y2 - y1, format); + mp_image_swscale(temp, &dst_region, csp, SWS_POINT); // chroma up + } + + for (i = 0; i < sbs->num_parts; ++i) { + struct sub_bitmap *sb = &sbs->parts[i]; + mp_image_t *sbi = NULL; + mp_image_t *sba = NULL; + + // cut off areas outside the image + int dst_x = sb->x - x1; // coordinates are relative to the bbox + int dst_y = sb->y - y1; // coordinates are relative to the bbox + int dst_w = sb->dw; + int dst_h = sb->dh; + if (!clip_to_bounds(&dst_x, &dst_y, &dst_w, &dst_h, + 0, 0, temp->w, temp->h)) + continue; + + if (part) { + sbi = part->imgs[i].i; + sba = part->imgs[i].a; + } + + if (!(sbi && sba)) { + if (!sub_bitmap_to_mp_images(&sbi, color_yuv, &color_a, &sba, sb, + sbs->format, csp, rgb2yuv, format, + bits)) + { + mp_msg(MSGT_VO, MSGL_ERR, + "render_sub_bitmap: invalid sub bitmap type\n"); + continue; + } + } + + // call blend_alpha 3 times + int p; + int src_x = (dst_x + x1) - sb->x; + int src_y = (dst_y + y1) - sb->y; + unsigned char *alpha_p = + sba->planes[0] + src_y * sba->stride[0] + src_x; + for (p = 0; p < 3; ++p) { + unsigned char *dst_p = + temp->planes[p] + dst_y * temp->stride[p] + dst_x * bytes; + if (sbi) { + unsigned char *src_p = + sbi->planes[p] + src_y * sbi->stride[p] + src_x * bytes; + blend_src_alpha( + dst_p, temp->stride[p], + src_p, sbi->stride[p], + alpha_p, sba->stride[0], + dst_h, dst_w, bytes + ); + } else { + blend_const_alpha( + dst_p, temp->stride[p], + color_yuv[p], + alpha_p, sba->stride[0], color_a, + dst_h, dst_w, bytes + ); + |