summaryrefslogtreecommitdiffstats
path: root/sub
diff options
context:
space:
mode:
Diffstat (limited to 'sub')
-rw-r--r--sub/ass_mp.c53
-rw-r--r--sub/ass_mp.h11
-rw-r--r--sub/dec_sub.c26
-rw-r--r--sub/dec_sub.h39
-rw-r--r--sub/draw_bmp.c651
-rw-r--r--sub/draw_bmp.h17
-rw-r--r--sub/find_subfiles.c1
-rw-r--r--sub/img_convert.c89
-rw-r--r--sub/img_convert.h15
-rw-r--r--sub/osd_dummy.c22
-rw-r--r--sub/osd_libass.c234
-rw-r--r--sub/sd.h5
-rw-r--r--sub/sd_ass.c22
-rw-r--r--sub/sd_lavc.c133
-rw-r--r--sub/spudec.c821
-rw-r--r--sub/spudec.h21
-rw-r--r--sub/sub.c427
-rw-r--r--sub/sub.h264
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
+ );
+ }
+ }
+
+ if (part) {
+ part->imgs[i].i = talloc_steal(part, sbi);
+ part->