summaryrefslogtreecommitdiffstats
path: root/sub
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2012-11-01 02:12:47 +0100
committerwm4 <wm4@nowhere>2012-11-01 02:12:47 +0100
commit84829a4ea1903e5db5782b72861fabc503a589cb (patch)
tree26b4acbaf6dd4b255278dcc67f28bd83357c3b86 /sub
parente45dd051c304dec189d0d4d792a89c2988c3fa71 (diff)
parentf4069259cf7ffd24ac2a5b64e26a386185e94c7b (diff)
downloadmpv-84829a4ea1903e5db5782b72861fabc503a589cb.tar.bz2
mpv-84829a4ea1903e5db5782b72861fabc503a589cb.tar.xz
Merge branch 'osd_changes' into master
Conflicts: DOCS/man/en/options.rst
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.c530
-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.c823
-rw-r--r--sub/spudec.h21
-rw-r--r--sub/sub.c426
-rw-r--r--sub/sub.h262
18 files changed, 1282 insertions, 1447 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..9a72a5b738
--- /dev/null
+++ b/sub/draw_bmp.c
@@ -0,0 +1,530 @@
+/*
+ * 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 <stddef.h>
+#include <stdbool.h>
+#include <assert.h>
+#include <math.h>
+#include <inttypes.h>
+
+#include <libavutil/common.h>
+
+#include "mpcommon.h"
+#include "sub/draw_bmp.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 imgfmt;
+ enum mp_csp colorspace;
+ enum mp_csp_levels levels;
+ int num_imgs;
+ struct sub_cache *imgs;
+};
+
+struct mp_draw_sub_cache
+{
+ struct part *parts[MAX_OSD_PARTS];
+};
+
+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 ACCURATE
+#define CONDITIONAL
+
+static void blend_const16_alpha(void *dst, int dst_stride, uint16_t srcp,
+ uint8_t *srca, int srca_stride, uint8_t srcamul,
+ int w, int h)
+{
+ if (!srcamul)
+ return;
+ for (int y = 0; y < h; y++) {
+ uint16_t *dst_r = (uint16_t *)((uint8_t *)dst + dst_stride * y);
+ uint8_t *srca_r = srca + srca_stride * y;
+ for (int x = 0; x < w; x++) {
+ uint32_t srcap = srca_r[x];
+#ifdef CONDITIONAL
+ if (!srcap)
+ continue;
+#endif
+ srcap *= srcamul; // now 0..65025
+ dst_r[x] = (srcp * srcap + dst_r[x] * (65025 - srcap) + 32512) / 65025;
+ }
+ }
+}
+
+static void blend_const8_alpha(void *dst, int dst_stride, uint16_t srcp,
+ uint8_t *srca, int srca_stride, uint8_t srcamul,
+ int w, int h)
+{
+ if (!srcamul)
+ return;
+ for (int y = 0; y < h; y++) {
+ uint8_t *dst_r = (uint8_t *)dst + dst_stride * y;
+ uint8_t *srca_r = srca + srca_stride * y;
+ for (int x = 0; x < w; x++) {
+ uint32_t srcap = srca_r[x];
+#ifdef CONDITIONAL
+ if (!srcap)
+ continue;
+#endif
+#ifdef ACCURATE
+ srcap *= srcamul; // now 0..65025
+ dst_r[x] = (srcp * srcap + dst_r[x] * (65025 - srcap) + 32512) / 65025;
+#else
+ srcap = (srcap * srcamul + 255) >> 8;
+ dst_r[x] = (srcp * srcap + dst_r[x] * (255 - srcap) + 255) >> 8;
+#endif
+ }
+ }
+}
+
+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)
+{
+ if (bytes == 2) {
+ blend_const16_alpha(dst, dst_stride, srcp, srca, srca_stride, srcamul,
+ w, h);
+ } else if (bytes == 1) {
+ blend_const8_alpha(dst, dst_stride, srcp, srca, srca_stride, srcamul,
+ w, h);
+ }
+}
+
+static void blend_src16_alpha(void *dst, int dst_stride, void *src,
+ int src_stride, uint8_t *srca, int srca_stride,
+ int w, int h)
+{
+ for (int y = 0; y < h; y++) {
+ uint16_t *dst_r = (uint16_t *)((uint8_t *)dst + dst_stride * y);
+ uint16_t *src_r = (uint16_t *)((uint8_t *)src + src_stride * y);
+ uint8_t *srca_r = srca + srca_stride * y;
+ for (int x = 0; x < w; x++) {
+ uint32_t srcap = srca_r[x];
+#ifdef CONDITIONAL
+ if (!srcap)
+ continue;
+#endif
+ dst_r[x] = (src_r[x] * srcap + dst_r[x] * (255 - srcap) + 127) / 255;
+ }
+ }
+}
+
+static void blend_src8_alpha(void *dst, int dst_stride, void *src,
+ int src_stride, uint8_t *srca, int srca_stride,
+ int w, int h)
+{
+ for (int y = 0; y < h; y++) {
+ uint8_t *dst_r = (uint8_t *)dst + dst_stride * y;
+ uint8_t *src_r = (uint8_t *)src + src_stride * y;
+ uint8_t *srca_r = srca + srca_stride * y;
+ for (int x = 0; x < w; x++) {
+ uint16_t srcap = srca_r[x];
+#ifdef CONDITIONAL
+ if (!srcap)
+ continue;
+#endif
+#ifdef ACCURATE
+ dst_r[x] = (src_r[x] * srcap + dst_r[x] * (255 - srcap) + 127) / 255;
+#else
+ dst_r[x] = (src_r[x] * srcap + dst_r[x] * (255 - srcap) + 255) >> 8;
+#endif
+ }
+ }
+}
+
+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)
+{
+ if (bytes == 2) {
+ blend_src16_alpha(dst, dst_stride, src, src_stride, srca, srca_stride,
+ w, h);
+ } else if (bytes == 1) {
+ blend_src8_alpha(dst, dst_stride, src, src_stride, srca, srca_stride,
+ w, h);
+ }
+}
+
+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];
+ uint8_t aval = (pval >> 24);
+ uint8_t rval = (pval >> 16) & 0xFF;
+ uint8_t gval = (pval >> 8) & 0xFF;
+ uint8_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 = FFMIN(255, (rval * 255 + add) / div);
+ gval = FFMIN(255, (gval * 255 + add) / div);
+ bval = FFMIN(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, struct mp_image *dst_format,
+ struct mp_image **out_sbi, struct mp_image **out_sba)
+{
+ struct mp_image *sbisrc = new_mp_image(sb->w, sb->h);
+ mp_image_setfmt(sbisrc, IMGFMT_BGR32);
+ sbisrc->planes[0] = sb->bitmap;
+ sbisrc->stride[0] = sb->stride;
+ struct mp_image *sbisrc2 = alloc_mpi(sb->dw, sb->dh, IMGFMT_BGR32);
+ mp_image_swscale(sbisrc2, sbisrc, SWS_BILINEAR);
+
+ struct mp_image *sba = alloc_mpi(sb->dw, sb->dh, IMGFMT_Y8);
+ unpremultiply_and_split_BGR32(sbisrc2, sba);
+
+ struct mp_image *sbi = alloc_mpi(sb->dw, sb->dh, dst_format->imgfmt);
+ sbi->colorspace = dst_format->colorspace;
+ sbi->levels = dst_format->levels;
+ mp_image_swscale(sbi, sbisrc2, SWS_BILINEAR);
+
+ free_mp_image(sbisrc);
+ free_mp_image(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);
+
+ 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 = NULL;
+ struct mp_image *sba = NULL;
+ if (part) {
+ sbi = part->imgs[i].i;
+ sba = part->imgs[i].a;
+ }
+
+ if (!(sbi && sba))
+ scale_sb_rgba(sb, temp, &sbi, &sba);
+
+ int bytes = (bits + 7) / 8;
+ uint8_t *alpha_p = sba->planes[0] + src_y * sba->stride[0] + src_x;
+ for (int p = 0; p < 3; 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 (part) {
+ part->imgs[i].i = talloc_steal(part, sbi);
+ part->imgs[i].a = talloc_steal(part, sba);
+ } else {
+ free_mp_image(sbi);
+ free_mp_image(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;
+ cspar.colorspace.format = temp->colorspace;
+ cspar.colorspace.levels_in = temp->levels;
+ cspar.colorspace.levels_out = MP_CSP_LEVELS_PC; // RGB (libass.color)
+ cspar.int_bits_in = bits;
+ cspar.int_bits_out = 8;
+
+ float yuv2rgb[3][4], rgb2yuv[3][4];
+ mp_get_yuv2rgb_coeffs(&cspar, yuv2rgb);
+ mp_invert_yuv2rgb(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] = {r, g, b};
+ mp_map_int_color(rgb2yuv, bits, color_yuv);
+
+ int bytes = (bits + 7) / 8;
+ uint8_t *alpha_p = (uint8_t *)sb->bitmap + src_y * sb->stride + src_x;
+ for (int p = 0; p < 3; p++) {
+ blend_const_alpha(dst.planes[p], dst.stride[p], color_yuv[p],
+ alpha_p, sb->stride, a, dst.w, dst.h, bytes);
+ }
+ }
+}
+
+static void mp_image_crop(struct mp_image *img, struct mp_rect rc)
+{
+ for (int p = 0; p < img->num_planes; ++p) {
+ int bits = MP_IMAGE_BITS_PER_PIXEL_ON_PLANE(img, p);
+ img->planes[p] +=
+ (rc.y0 >> (p ? img->chroma_y_shift : 0)) * img->stride[p] +
+ (rc.x0 >> (p ? img->chroma_x_shift : 0)) * bits / 8;
+ }
+ img->w = rc.x1 - rc.x0;
+ img->h = rc.y1 - rc.y0;
+ img->chroma_width = img->w >> img->chroma_x_shift;
+ img->chroma_height = img->h >> img->chroma_y_shift;
+ img->display_w = img->display_h = 0;
+}
+
+static bool clip_to_bb(struct mp_rect bb, struct mp_rect *rc)
+{
+ rc->x0 = FFMAX(bb.x0, rc->x0);
+ rc->y0 = FFMAX(bb.y0, rc->y0);
+ rc->x1 = FFMIN(bb.x1, rc->x1);
+ rc->y1 = FFMIN(bb.y1, rc->y1);
+
+ return rc->x1 > rc->x0 && rc->y1 > rc->y0;
+}
+
+static void get_swscale_alignment(const struct mp_image *img, int *out_xstep,
+ int *out_ystep)
+{
+ int sx = (1 << img->chroma_x_shift);
+ int sy = (1 << img->chroma_y_shift);
+
+ // Hack for IMGFMT_Y8
+ if (img->chroma_x_shift == 31 && img->chroma_y_shift == 31) {
+ sx = 1;
+ sy = 1;
+ }
+
+ for (int 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;
+ }
+
+ *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);
+}
+
+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 (!clip_to_bb(img_rect, rc))
+ return false;
+ int xstep, ystep;
+ get_swscale_alignment(img, &xstep, &ystep);
+ align_bbox(xstep, ystep, rc);
+ return clip_to_bb(img_rect, rc);
+}
+
+// Try to find best/closest YUV 444 format for imgfmt
+static void get_closest_y444_format(int imgfmt, int *out_format, int *out_bits)
+{
+#ifdef ACCURATE
+ struct mp_image tmp = {0};
+ mp_image_setfmt(&tmp, imgfmt);
+ if (tmp.flags & MP_IMGFLAG_YUV) {
+ int bits;
+ if (mp_get_chroma_shift(imgfmt, NULL, NULL, &bits)) {
+ switch (bits) {
+ case 8:
+ *out_format = IMGFMT_444P;
+ *out_bits = 8;
+ return;
+ case 9:
+ *out_format = IMGFMT_444P9;
+ *out_bits = 9;
+ return;
+ case 10:
+ *out_format = IMGFMT_444P10;
+ *out_bits = 10;
+ return;
+ }
+ }
+ }
+ *out_format = IMGFMT_444P16;
+ *out_bits = 16;
+#else
+ *out_format = IMGFMT_444P;
+ *out_bits = 8;
+#endif
+}
+
+static struct part *get_cache(struct mp_draw_sub_cache **cache,
+ struct sub_bitmaps *sbs, struct mp_image *format)
+{
+ 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) {
+ if (part->bitmap_pos_id != sbs->bitmap_pos_id
+ || part->imgfmt != format->imgfmt
+ || part->colorspace != format->colorspace
+ || part->levels != format->levels)
+ {
+ talloc_free(part);
+ part = NULL;
+ }
+ }
+ if (!part) {
+ part = talloc(*cache, struct part);
+ *part = (struct part) {
+ .bitmap_pos_id = sbs->bitmap_pos_id,
+ .num_imgs = sbs->num_parts,
+ .imgfmt = format->imgfmt,
+ .levels = format->levels,
+ .colorspace = format->colorspace,
+ };
+ 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 (!clip_to_bb((struct mp_rect){0, 0, temp->w, temp->h}, &dst))
+ 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(out_area, dst);
+
+ return 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,
+ struct sub_bitmaps *sbs)
+{
+ assert(mp_draw_sub_formats[sbs->format]);
+ if (!mp_sws_supported_format(dst->imgfmt))
+ return;
+
+ int format, bits;
+ get_closest_y444_format(dst->imgfmt, &format, &bits);
+
+ struct mp_rect bb;
+ if (!sub_bitmaps_bb(sbs, &bb))
+ return;
+
+ if (!align_bbox_for_swscale(dst, &bb))
+ return;
+
+ struct mp_image *temp;
+ struct mp_image dst_region = *dst;
+ mp_image_crop(&dst_region, bb);
+ if (dst->imgfmt == format) {
+ temp = &dst_region;
+ } else {
+ temp = alloc_mpi(bb.x1 - bb.x0, bb.y1 - bb.y0, format);
+ // temp is always YUV, dst_region not
+ // reduce amount of conversions in YUV case (upsampling/shifting only)
+ if (dst_region.flags & MP_IMGFLAG_YUV) {
+ temp->colorspace = dst_region.colorspace;
+ temp->levels = dst_region.levels;
+ }
+ mp_image_swscale(temp, &dst_region, SWS_POINT); // chroma up
+ }
+
+ 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);
+ }
+
+ if (temp != &dst_region) {
+ mp_image_swscale(&dst_region, temp, SWS_AREA); // chroma down
+ free_mp_image(temp);
+ }
+}
+
+// vim: ts=4 sw=4 et tw=80
diff --git a/sub/draw_bmp.h b/sub/draw_bmp.h
new file mode 100644
index 0000000000..489e91f666
--- /dev/null
+++ b/sub/draw_bmp.h
@@ -0,0 +1,17 @@
+#ifndef MPLAYER_DRAW_BMP_H
+#define MPLAYER_DRAW_BMP_H
+
+#include "sub/sub.h"
+
+struct mp_image;
+struct sub_bitmaps;
+struct mp_csp_details;
+struct mp_draw_sub_cache;
+void mp_draw_sub_bitmaps(struct mp_draw_sub_cache **cache, struct mp_image *dst,
+ struct sub_bitmaps *sbs);
+
+extern const bool mp_draw_sub_formats[SUBBITMAP_COUNT];
+
+#endif /* MPLAYER_DRAW_BMP_H */
+
+// vim: ts=4 sw=4 et tw=80
diff --git a/sub/find_subfiles.c b/sub/find_subfiles.c
index b073a722c6..2e705bd4d1 100644
--- a/sub/find_subfiles.c
+++ b/sub/find_subfiles.c
@@ -12,6 +12,7 @@
#include "mpcommon.h"
#include "sub/find_subfiles.h"
#include "sub/sub.h"
+#include "sub/subreader.h"
static struct bstr strip_ext(struct bstr str)
{
diff --git a/sub/img_convert.c b/sub/img_convert.c
new file mode 100644
index 0000000000..aa5c89401a
--- /dev/null
+++ b/sub/img_convert.c
@@ -0,0 +1,89 @@
+/*
+ * This file is part of mplayer.
+ *
+ * mplayer 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.
+ *
+ * mplayer 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 mplayer. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <string.h>
+#include <assert.h>
+
+#include <libavutil/mem.h>
+#include <libavutil/common.h>
+
+#include "talloc.h"
+
+#include "img_convert.h"
+#include "sub.h"
+#include "spudec.h"
+#include "libmpcodecs/img_format.h"
+#include "libmpcodecs/mp_image.h"
+#include "libmpcodecs/sws_utils.h"
+
+struct osd_conv_cache {
+ struct sub_bitmap part;
+ struct sub_bitmap *parts;
+};
+
+struct osd_conv_cache *osd_conv_cache_new(void)
+{
+ return talloc_zero(NULL, struct osd_conv_cache);
+}
+
+static void rgba_to_premultiplied_rgba(uint32_t *colors, size_t count)
+{
+ for (int n = 0; n < count; n++) {
+ uint32_t c = colors[n];
+ int b = c & 0xFF;
+ int g = (c >> 8) & 0xFF