From ffb7a2fe17af204635db6694b5b49b6368be91e6 Mon Sep 17 00:00:00 2001 From: wm4 Date: Fri, 28 Sep 2012 21:19:36 +0200 Subject: sub: create sub_bitmap array even when using libass One sub_bitmaps struct could contain either a libass ASS_Image list, or a mplayer native list of sub-bitmaps. This caused code duplication in vo_vdpau.c and bitmap_packer.c. Avoid this by creating such a sub_bitmap array even with libass. This basically copies the list and recreates it in mplayer's native format. It gets rid of the code duplication, and will make implementing extended subtitle and OSD rendering in other VOs easier. Also do some cosmetic changes and other preparations for the following commits. --- sub/dec_sub.c | 3 ++- sub/dec_sub.h | 49 +++++++++++++++++++++++++++++++++++-------------- sub/sd_ass.c | 28 +++++++++++++++++++++++++++- sub/sd_lavc.c | 5 +++-- 4 files changed, 67 insertions(+), 18 deletions(-) (limited to 'sub') diff --git a/sub/dec_sub.c b/sub/dec_sub.c index 4a048b27a6..31b06b9b80 100644 --- a/sub/dec_sub.c +++ b/sub/dec_sub.c @@ -62,7 +62,8 @@ void sub_get_bitmaps(struct osd_state *osd, struct sub_bitmaps *res) { struct MPOpts *opts = osd->opts; - *res = (struct sub_bitmaps){ .type = SUBBITMAP_EMPTY, + *res = (struct sub_bitmaps){ .render_index = 0, + .format = SUBBITMAP_EMPTY, .bitmap_id = osd->bitmap_id, .bitmap_pos_id = osd->bitmap_pos_id }; if (!opts->sub_visibility || !osd->sh_sub || !osd->sh_sub->active) { diff --git a/sub/dec_sub.h b/sub/dec_sub.h index c71a2348aa..df6aaf9b91 100644 --- a/sub/dec_sub.h +++ b/sub/dec_sub.h @@ -1,14 +1,22 @@ #ifndef MPLAYER_DEC_SUB_H #define MPLAYER_DEC_SUB_H +#include +#include + +#define MAX_OSD_PARTS 8 + struct sh_sub; struct osd_state; struct ass_track; -enum sub_bitmap_type { +enum sub_bitmap_format { SUBBITMAP_EMPTY, - SUBBITMAP_LIBASS, - SUBBITMAP_RGBA, + SUBBITMAP_LIBASS, // A8, with a per-surface blend color (libass.color) + SUBBITMAP_RGBA, // B8G8R8A8 + SUBBITMAP_OLD, // I8A8 (monochrome), premultiplied alpha + + SUBBITMAP_COUNT }; typedef struct mp_eosd_res { @@ -16,21 +24,34 @@ typedef struct mp_eosd_res { int mt, mb, ml, mr; // borders (top, bottom, left, right) } mp_eosd_res_t; +struct sub_bitmap { + void *bitmap; + int stride; + int w, h; + int x, y; + // Note: not clipped, going outside the screen area is allowed + int dw, dh; + + union { + struct { + uint32_t color; + } libass; + }; +}; + typedef struct sub_bitmaps { - enum sub_bitmap_type type; + int render_index; // for VO cache state (limited by MAX_OSD_PARTS) - struct ass_image *imgs; + enum sub_bitmap_format format; + bool scaled; // if false, dw==w && dh==h - 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; + struct sub_bitmap *parts; + int num_parts; + + // Provided for VOs with old code + struct ass_image *imgs; - bool scaled; + // Incremented on each change int bitmap_id, bitmap_pos_id; } mp_eosd_images_t; diff --git a/sub/sd_ass.c b/sub/sd_ass.c index 9295cab07d..aa190ee4ac 100644 --- a/sub/sd_ass.c +++ b/sub/sd_ass.c @@ -36,6 +36,7 @@ struct sd_ass_priv { struct ass_track *ass_track; bool vsfilter_aspect; bool incomplete_event; + struct sub_bitmap *parts; }; static void free_last_event(ASS_Track *track) @@ -147,7 +148,32 @@ static void get_bitmaps(struct sh_sub *sh, struct osd_state *osd, res->bitmap_id = ++res->bitmap_pos_id; else if (changed) res->bitmap_pos_id++; - res->type = SUBBITMAP_LIBASS; + res->format = SUBBITMAP_LIBASS; + + int num_parts = 0; + int num_parts_alloc = MP_TALLOC_ELEMS(ctx->parts); + struct ass_image *img = res->imgs; + while (img) { + if (img->w == 0 || img->h == 0) + continue; + if (num_parts >= num_parts_alloc) { + num_parts_alloc = FFMAX(num_parts_alloc * 2, 32); + ctx->parts = talloc_realloc(ctx, ctx->parts, struct sub_bitmap, + num_parts_alloc); + } + struct sub_bitmap *p = &ctx->parts[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; + img = img->next; + num_parts++; + } + res->parts = ctx->parts; + res->num_parts = num_parts; } static void reset(struct sh_sub *sh, struct osd_state *osd) diff --git a/sub/sd_lavc.c b/sub/sd_lavc.c index 1da33ffca1..39e4891fb1 100644 --- a/sub/sd_lavc.c +++ b/sub/sd_lavc.c @@ -175,6 +175,7 @@ static void decode(struct sh_sub *sh, struct osd_state *osd, void *data, uint32_t *outbmp = talloc_size(priv->inbitmaps, r->w * r->h * 4); b->bitmap = outbmp; + b->stride = r->w * 4; b->w = r->w; b->h = r->h; b->x = r->x; @@ -233,13 +234,13 @@ static void get_bitmaps(struct sh_sub *sh, struct osd_state *osd, SET(bo->dh, bi->h * yscale); } res->parts = priv->outbitmaps; - res->part_count = priv->count; + res->num_parts = priv->count; if (priv->bitmaps_changed) res->bitmap_id = ++res->bitmap_pos_id; else if (pos_changed) res->bitmap_pos_id++; priv->bitmaps_changed = false; - res->type = SUBBITMAP_RGBA; + res->format = SUBBITMAP_RGBA; res->scaled = xscale != 1 || yscale != 1; } -- cgit v1.2.3 From 2a5fcd2801ccaff48ad360613cef2d0edf80543c Mon Sep 17 00:00:00 2001 From: wm4 Date: Fri, 28 Sep 2012 21:33:26 +0200 Subject: sub: add preliminary emulation layer to draw OSD with EOSD This basically pushes the old OSD bitmaps via VOCTRL_DRAW_EOSD to the VO, instead of using the old callback-based interface. Future commits will change the code such that sub.c pushes images rendered by libass directly, rather than converting them to the old OSD format first. --- sub/sub.c | 73 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ sub/sub.h | 9 ++++++++ 2 files changed, 82 insertions(+) (limited to 'sub') diff --git a/sub/sub.c b/sub/sub.c index c527503ef1..8b89ca497c 100644 --- a/sub/sub.c +++ b/sub/sub.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -253,6 +254,7 @@ struct osd_state *osd_create(struct MPOpts *opts, struct ass_library *asslib) .ass_library = asslib, }; // temp hack, should be moved to mplayer later + new_osd_obj(OSDTYPE_ASS); new_osd_obj(OSDTYPE_OSD); new_osd_obj(OSDTYPE_SUBTITLE); new_osd_obj(OSDTYPE_PROGBAR); @@ -369,3 +371,74 @@ int vo_osd_check_range_update(int x1,int y1,int x2,int y2){ } return 0; } + +struct draw_osd_closure { + struct vo *vo; + struct osd_state *osd; + int render_index; +}; + +static void eosd_draw_osd_part(void *ctx, int x0, int y0, int w, int h, + unsigned char *src, unsigned char *srca, + int stride) +{ + struct draw_osd_closure *c = ctx; + + assert(c->render_index < MAX_OSD_PARTS); + assert(w > 0 && h > 0); + + size_t scratch_size = talloc_get_size(c->osd->scratch); + size_t new_size = stride * h * 2; + if (new_size > scratch_size) { + scratch_size = new_size; + c->osd->scratch = talloc_realloc(c->osd, c->osd->scratch, char *, + new_size); + } + + unsigned char *tmp = c->osd->scratch; + + for (int y = 0; y < h; y++) { + unsigned char *y_src = src + stride * y; + unsigned char *y_srca = srca + stride * y; + unsigned char *cur = tmp + y * w * 2; + for (int x = 0; x < w; x++) { + cur[x*2+0] = y_src[x]; + cur[x*2+1] = -y_srca[x]; + } + } + + struct sub_bitmaps *imgs = &c->osd->eosd[c->render_index]; + imgs->render_index = c->render_index; + imgs->format = SUBBITMAP_OLD; + imgs->bitmap_id++; + imgs->bitmap_pos_id++; + if (!imgs->num_parts) { + imgs->num_parts = 1; + imgs->parts = talloc_array(c->osd, struct sub_bitmap, imgs->num_parts); + } + + imgs->parts[0] = (struct sub_bitmap) { + .bitmap = tmp, + .stride = w * 2, + .x = x0, .y = y0, + .w = w, .h = h, + .dw = w, .dh = h, + }; + + vo_control(c->vo, VOCTRL_DRAW_EOSD, imgs); + + c->render_index++; +} + +// draw old-OSD using EOSD +void emulate_draw_osd(struct vo *vo, struct osd_state *osd) +{ + mp_eosd_res_t res = {0}; + if (vo_control(vo, VOCTRL_GET_EOSD_RES, &res) != VO_TRUE) + return; + + struct draw_osd_closure c = {vo, osd}; + c.render_index = 1; // 0 is the "normal" EOSD renderer for subtitles + osd_draw_text_ext(osd, res.w, res.h, res.ml, res.mt, res.mr, res.mb, 0, 0, + eosd_draw_osd_part, &c); +} diff --git a/sub/sub.h b/sub/sub.h index fc0047bba6..74d2b5a37a 100644 --- a/sub/sub.h +++ b/sub/sub.h @@ -24,10 +24,13 @@ #include "subreader.h" #include "dec_sub.h" +struct vo; + typedef struct mp_osd_bbox_s { int x1,y1,x2,y2; } mp_osd_bbox_t; +#define OSDTYPE_ASS 0 #define OSDTYPE_OSD 1 #define OSDTYPE_SUBTITLE 2 #define OSDTYPE_PROGBAR 3 @@ -78,6 +81,10 @@ struct osd_state { char *osd_text; int w, h; + struct sub_bitmaps eosd[MAX_OSD_PARTS]; + + void *scratch; + struct MPOpts *opts; }; @@ -153,6 +160,8 @@ void osd_draw_text_ext(struct osd_state *osd, int dxs, int dys, int stride), void *ctx); +void emulate_draw_osd(struct vo *vo, struct osd_state *osd); + struct osd_state *osd_create(struct MPOpts *opts, struct ass_library *asslib); void osd_set_text(struct osd_state *osd, const char *text); int osd_update(struct osd_state *osd, int dxs, int dys); -- cgit v1.2.3 From 3365514951e9c07ec3a21bb3898e5796c214f8b7 Mon Sep 17 00:00:00 2001 From: wm4 Date: Fri, 28 Sep 2012 21:38:52 +0200 Subject: sub: allow rendering OSD in ASS image format directly, simplify Before this commit, the OSD was drawn using libass, but the resulting bitmaps were converted to the internal mplayer OSD format. We want to get rid of the old OSD format, because it's monochrome, and can't even be rendered directly using modern video output methods (like with OpenGL/Direct3D/VDPAU). Change it so that VOs can get the ASS images directly, without additional conversions. (This also has the consequence that the OSD can render colors now.) Currently, this is vo_gl3 only. The other VOs still use the old method. Also, the old OSD format is still used for all VOs with DVD subtitles (spudec). Rewrite sub.c. Remove all the awkward flags and bounding boxes and change detection things. It turns out that much of that isn't needed. Move code related to converting subtitle images to img_convert.c. (It has to be noted that all of these conversions were already done before in some places, and that the new code actually makes less use of them.) --- sub/ass_mp.c | 35 +++++ sub/ass_mp.h | 5 + sub/dec_sub.h | 13 +- sub/img_convert.c | 214 ++++++++++++++++++++++++++ sub/img_convert.h | 16 ++ sub/osd_dummy.c | 22 +-- sub/osd_libass.c | 195 ++++++++--------------- sub/sd_ass.c | 36 +---- sub/spudec.c | 40 ++++- sub/spudec.h | 3 + sub/sub.c | 453 ++++++++++++++++++++---------------------------------- sub/sub.h | 83 +++++----- 12 files changed, 593 insertions(+), 522 deletions(-) create mode 100644 sub/img_convert.c create mode 100644 sub/img_convert.h (limited to 'sub') diff --git a/sub/ass_mp.c b/sub/ass_mp.c index 202664578b..5766a847f8 100644 --- a/sub/ass_mp.c +++ b/sub/ass_mp.c @@ -264,6 +264,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; + res->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 = res->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..805e9d7310 100644 --- a/sub/ass_mp.h +++ b/sub/ass_mp.h @@ -46,6 +46,11 @@ void mp_ass_configure(ASS_Renderer *priv, struct MPOpts *opts, 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 */ diff --git a/sub/dec_sub.h b/sub/dec_sub.h index df6aaf9b91..9c75506c4c 100644 --- a/sub/dec_sub.h +++ b/sub/dec_sub.h @@ -4,21 +4,26 @@ #include #include -#define MAX_OSD_PARTS 8 - struct sh_sub; struct osd_state; struct ass_track; enum sub_bitmap_format { - SUBBITMAP_EMPTY, + SUBBITMAP_EMPTY = 0,// no bitmaps; always has num_parts==0 SUBBITMAP_LIBASS, // A8, with a per-surface blend color (libass.color) - SUBBITMAP_RGBA, // B8G8R8A8 + SUBBITMAP_RGBA, // B8G8R8A8, can be scaled SUBBITMAP_OLD, // I8A8 (monochrome), premultiplied alpha + SUBBITMAP_OLD_PLANAR, // like previous, but bitmap points to old_osd_planar SUBBITMAP_COUNT }; +// For SUBBITMAP_OLD_PANAR +struct old_osd_planar { + unsigned char *bitmap; + unsigned char *alpha; +}; + typedef struct mp_eosd_res { int w, h; // screen dimensions, including black borders int mt, mb, ml, mr; // borders (top, bottom, left, right) diff --git a/sub/img_convert.c b/sub/img_convert.c new file mode 100644 index 0000000000..38a367253c --- /dev/null +++ b/sub/img_convert.c @@ -0,0 +1,214 @@ +/* + * 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 . + */ + +#include +#include + +#include +#include + +#include "talloc.h" + +#include "img_convert.h" +#include "sub.h" + +struct osd_conv_cache { + struct sub_bitmap part; + // for osd_conv_cache_alloc_old_p() (SUBBITMAP_PLANAR) + int allocated, stride; + struct old_osd_planar bmp; + // for osd_conv_cache_alloc_old() (SUBBITMAP_OLD_PLANAR) + unsigned char *packed; +}; + +static int osd_conv_cache_destroy(void *p) +{ + struct osd_conv_cache *c = p; + av_free(c->bmp.bitmap); + av_free(c->bmp.alpha); + return 0; +} + +struct osd_conv_cache *osd_conv_cache_new(void) +{ + struct osd_conv_cache *c = talloc_zero(NULL, struct osd_conv_cache); + talloc_set_destructor(c, &osd_conv_cache_destroy); + return c; +} + +// allocates/enlarges the alpha/bitmap buffer +static void osd_conv_cache_alloc_old_p(struct osd_conv_cache *c, int w, int h) +{ + assert(w > 0 && h > 0); + c->stride = (w + 7) & (~7); + int len = c->stride * h; + if (c->allocated < len) { + av_free(c->bmp.bitmap); + av_free(c->bmp.alpha); + c->allocated = len; + c->bmp.bitmap = av_malloc(len); + c->bmp.alpha = av_malloc(len); + } + memset(c->bmp.bitmap, sub_bg_color, len); + memset(c->bmp.alpha, sub_bg_alpha, len); + c->part = (struct sub_bitmap) { + .bitmap = &c->bmp, + .stride = c->stride, + .w = w, .h = h, + .dw = w, .dh = h, + }; +} + +static void osd_conv_cache_alloc_old(struct osd_conv_cache *c, int w, int h) +{ + size_t size = talloc_get_size(c->packed); + size_t new_size = w * 2 * h; + if (new_size > size) + c->packed = talloc_realloc(c, c->packed, unsigned char, new_size); + c->part = (struct sub_bitmap) { + .bitmap = c->packed, + .stride = w * 2, + .w = w, .h = h, + .dw = w, .dh = h, + }; +} + +static void draw_alpha_ass_to_old(unsigned char *src, int src_w, int src_h, + int src_stride, unsigned char *dst_a, + unsigned char *dst_i, size_t dst_stride, + int dst_x, int dst_y, 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); + + int gray = (r + g + b) / 3; // not correct + + dst_a += dst_y * dst_stride + dst_x; + dst_i += dst_y * dst_stride + dst_x; + + int src_skip = src_stride - src_w; + int dst_skip = dst_stride - src_w; + + for (int y = 0; y < src_h; y++) { + for (int x = 0; x < src_w; x++) { + unsigned char as = (*src * a) >> 8; + unsigned char bs = (gray * as) >> 8; + // to mplayer scale + as = -as; + + unsigned char *a = dst_a; + unsigned char *b = dst_i; + + // NOTE: many special cases, because alpha=0 means transparency, + // while alpha=1..255 is opaque..transparent + if (as) { + *b = ((*b * as) >> 8) + bs; + if (*a) { + *a = (*a * as) >> 8; + if (*a < 1) + *a = 1; + } else { + *a = as; + } + } + + dst_a++; + dst_i++; + src++; + } + dst_a += dst_skip; + dst_i += dst_skip; + src += src_skip; + } +} + +static void render_ass_to_old(unsigned char *a, unsigned char *i, + size_t stride, int x, int y, + struct sub_bitmaps *imgs) +{ + for (int n = 0; n < imgs->num_parts; n++) { + struct sub_bitmap *p = &imgs->parts[n]; + draw_alpha_ass_to_old(p->bitmap, p->w, p->h, p->stride, a, i, stride, + x + p->x, y + p->y, p->libass.color); + } +} + +// SUBBITMAP_LIBASS -> SUBBITMAP_OLD_PLANAR +bool osd_conv_ass_to_old_p(struct osd_conv_cache *c, struct sub_bitmaps *imgs) +{ + struct sub_bitmaps src = *imgs; + if (src.format != SUBBITMAP_LIBASS || src.scaled) + return false; + + imgs->format = SUBBITMAP_OLD_PLANAR; + imgs->num_parts = 0; + imgs->parts = NULL; + + int x1, y1, x2, y2; + if (!sub_bitmaps_bb(&src, &x1, &y1, &x2, &y2)) + return true; + + osd_conv_cache_alloc_old_p(c, x2 - x1, y2 - y1); + + render_ass_to_old(c->bmp.alpha, c->bmp.bitmap, c->stride, -x1, -y1, &src); + + c->part.x = x1; + c->part.y = y1; + + imgs->parts = &c->part; + imgs->num_parts = 1; + return true; +} + +// SUBBITMAP_OLD_PLANAR -> SUBBITMAP_OLD +bool osd_conv_old_p_to_old(struct osd_conv_cache *c, struct sub_bitmaps *imgs) +{ + struct sub_bitmaps src = *imgs; + if (src.format != SUBBITMAP_OLD_PLANAR || src.num_parts > 1) + return false; + + imgs->format = SUBBITMAP_OLD; + imgs->num_parts = 0; + imgs->parts = NULL; + + if (src.num_parts == 0) + return true; + + struct sub_bitmap *s = &src.parts[0]; + struct old_osd_planar *p = s->bitmap; + + osd_conv_cache_alloc_old(c, s->w, s->h); + + for (int y = 0; y < s->h; y++) { + unsigned char *y_src = p->bitmap + s->stride * y; + unsigned char *y_srca = p->alpha + s->stride * y; + unsigned char *cur = c->packed + y * s->w * 2; + for (int x = 0; x < s->w; x++) { + cur[x*2+0] = y_src[x]; + cur[x*2+1] = -y_srca[x]; + } + } + + c->part.x = s->x; + c->part.y = s->y; + + imgs->parts = &c->part; + imgs->num_parts = 1; + return true; +} diff --git a/sub/img_convert.h b/sub/img_convert.h new file mode 100644 index 0000000000..c40a8de2e4 --- /dev/null +++ b/sub/img_convert.h @@ -0,0 +1,16 @@ +#ifndef MPLAYER_SUB_IMG_CONVERT_H +#define MPLAYER_SUB_IMG_CONVERT_H + +#include + +struct osd_conv_cache; +struct sub_bitmaps; + +struct osd_conv_cache *osd_conv_cache_new(void); + +// These functions convert from one OSD format to another. On success, they copy +// the converted image data into c, and change imgs to point to the data. +bool osd_conv_old_p_to_old(struct osd_conv_cache *c, struct sub_bitmaps *imgs); +bool osd_conv_ass_to_old_p(struct osd_conv_cache *c, struct sub_bitmaps *imgs); + +#endif diff --git a/sub/osd_dummy.c b/sub/osd_dummy.c index d869fe16c8..782ce21942 100644 --- a/sub/osd_dummy.c +++ b/sub/osd_dummy.c @@ -6,18 +6,6 @@ #include "talloc.h" #include "sub.h" -void vo_update_text_osd(struct osd_state *osd, mp_osd_obj_t *obj) -{ -} - -void vo_update_text_progbar(struct osd_state *osd, mp_osd_obj_t *obj) -{ -} - -void vo_update_text_sub(struct osd_state *osd, mp_osd_obj_t *obj) -{ -} - void osd_init_backend(struct osd_state *osd) { } @@ -26,14 +14,12 @@ void osd_destroy_backend(struct osd_state *osd) { } -void osd_font_invalidate(void) -{ -} - -void osd_font_load(struct osd_state *osd) +void osd_get_function_sym(char *buffer, size_t buffer_size, int osd_function) { } -void osd_get_function_sym(char *buffer, size_t buffer_size, int osd_function) +void osd_object_get_bitmaps(struct osd_state *osd, struct osd_object *obj, + struct sub_bitmaps *out_imgs) { + *out_imgs = (struct sub_bitmaps) {0}; } diff --git a/sub/osd_libass.c b/sub/osd_libass.c index e770215ce6..2d596a6516 100644 --- a/sub/osd_libass.c +++ b/sub/osd_libass.c @@ -55,117 +55,13 @@ void osd_init_backend(struct osd_state *osd) void osd_destroy_backend(struct osd_state *osd) { - if (osd) { - if (osd->osd_render) - ass_renderer_done(osd->osd_render); - osd->osd_render = NULL; - ass_library_done(osd->osd_ass_library); - osd->osd_ass_library = NULL; - } -} - -static void eosd_draw_alpha_a8i8(unsigned char *src, - int src_w, int src_h, - int src_stride, - unsigned char *dst_a, - unsigned char *dst_i, - size_t dst_stride, - int dst_x, int dst_y, - 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); - - int gray = (r + g + b) / 3; // not correct - - dst_a += dst_y * dst_stride + dst_x; - dst_i += dst_y * dst_stride + dst_x; - - int src_skip = src_stride - src_w; - int dst_skip = dst_stride - src_w; - - for (int y = 0; y < src_h; y++) { - for (int x = 0; x < src_w; x++) { - unsigned char as = (*src * a) >> 8; - unsigned char bs = (gray * as) >> 8; - // to mplayer scale - as = -as; - - unsigned char *a = dst_a; - unsigned char *b = dst_i; - - // NOTE: many special cases, because alpha=0 means transparency, - // while alpha=1..255 is opaque..transparent - if (as) { - *b = ((*b * as) >> 8) + bs; - if (*a) { - *a = (*a * as) >> 8; - if (*a < 1) - *a = 1; - } else { - *a = as; - } - } - - dst_a++; - dst_i++; - src++; - } - dst_a += dst_skip; - dst_i += dst_skip; - src += src_skip; - } -} - -static void eosd_render_a8i8(unsigned char *a, unsigned char *i, size_t stride, - int x, int y, ASS_Image *imgs) -{ - for (ASS_Image *p = imgs; p; p = p->next) { - eosd_draw_alpha_a8i8(p->bitmap, p->w, p->h, p->stride, a, i, stride, - x + p->dst_x, y + p->dst_y, p->color); - } -} - -static bool ass_bb(ASS_Image *imgs, int *x1, int *y1, int *x2, int *y2) -{ - *x1 = *y1 = INT_MAX; - *x2 = *y2 = INT_MIN; - for (ASS_Image *p = imgs; p; p = p->next) { - *x1 = FFMIN(*x1, p->dst_x); - *y1 = FFMIN(*y1, p->dst_y); - *x2 = FFMAX(*x2, p->dst_x + p->w); - *y2 = FFMAX(*y2, p->dst_y + p->h); - } - return *x1 < *x2 && *y1 < *y2; -} - -static void draw_ass_osd(struct osd_state *osd, mp_osd_obj_t *obj) -{ - ass_set_frame_size(osd->osd_render, osd->w, osd->h); - - ASS_Image *imgs = ass_render_frame(osd->osd_render, obj->osd_track, 0, - NULL); - - int x1, y1, x2, y2; - if (!ass_bb(imgs, &x1, &y1, &x2, &y2)) { - obj->flags &= ~OSDFLAG_VISIBLE; - return; - } - - obj->bbox.x1 = x1; - obj->bbox.y1 = y1; - obj->bbox.x2 = x2; - obj->bbox.y2 = y2; - obj->flags |= OSDFLAG_BBOX; - osd_alloc_buf(obj); - - eosd_render_a8i8(obj->alpha_buffer, obj->bitmap_buffer, obj->stride, - -x1, -y1, imgs); + if (osd->osd_render) + ass_renderer_done(osd->osd_render); + osd->osd_render = NULL; + ass_library_done(osd->osd_ass_library); + osd->osd_ass_library = NULL; } - static void update_font_scale(ASS_Track *track, ASS_Style *style, double factor) { // duplicated from ass_mp.c @@ -211,9 +107,16 @@ static ASS_Event *get_osd_ass_event(ASS_Track *track) event->Start = 0; event->Duration = 100; event->Style = track->default_style; + assert(event->Text == NULL); return event; } +static void clear_obj(struct osd_object *obj) +{ + if (obj->osd_track) + ass_flush_events(obj->osd_track); +} + static char *append_utf8_buffer(char *buffer, uint32_t codepoint) { char data[8]; @@ -257,25 +160,27 @@ static char *mangle_ass(const char *in) return res; } -void vo_update_text_osd(struct osd_state *osd, mp_osd_obj_t* obj) +static void update_osd(struct osd_state *osd, struct osd_object *obj) { + if (!osd->osd_text[0]) { + clear_obj(obj); + return; + } + if (!obj->osd_track) obj->osd_track = create_osd_ass_track(osd); ASS_Event *event = get_osd_ass_event(obj->osd_track); - event->Text = mangle_ass(osd->osd_text); - draw_ass_osd(osd, obj); - talloc_free(event->Text); - event->Text = NULL; + char *text = mangle_ass(osd->osd_text); + event->Text = strdup(text); + talloc_free(text); } #define OSDBAR_ELEMS 46 -void vo_update_text_progbar(struct osd_state *osd, mp_osd_obj_t* obj) +static void update_progbar(struct osd_state *osd, struct osd_object *obj) { - obj->flags |= OSDFLAG_CHANGED | OSDFLAG_VISIBLE; - if (vo_osd_progbar_type < 0) { - obj->flags &= ~OSDFLAG_VISIBLE; + clear_obj(obj); return; } @@ -322,21 +227,16 @@ void vo_update_text_progbar(struct osd_state *osd, mp_osd_obj_t* obj) text = append_utf8_buffer(text, OSD_CODEPOINTS + OSD_PB_END); ASS_Event *event = get_osd_ass_event(obj->osd_track); - event->Text = text; - draw_ass_osd(osd, obj); - event->Text = NULL; - + event->Text = strdup(text); talloc_free(text); } -void vo_update_text_sub(struct osd_state *osd, mp_osd_obj_t* obj) +static void update_sub(struct osd_state *osd, struct osd_object *obj) { struct MPOpts *opts = osd->opts; - obj->flags |= OSDFLAG_CHANGED | OSDFLAG_VISIBLE; - - if (!vo_sub || !opts->sub_visibility) { - obj->flags &= ~OSDFLAG_VISIBLE; + if (!(vo_sub && opts->sub_visibility)) { + clear_obj(obj); return; } @@ -354,14 +254,39 @@ void vo_update_text_sub(struct osd_state *osd, mp_osd_obj_t* obj) text = talloc_asprintf_append_buffer(text, "%s\n", vo_sub->text[n]); ASS_Event *event = get_osd_ass_event(obj->osd_track); - event->Text = mangle_ass(text); - draw_ass_osd(osd, obj); - talloc_free(event->Text); - event->Text = NULL; - + char *escaped_text = mangle_ass(text); + event->Text = strdup(escaped_text); + talloc_free(escaped_text); talloc_free(text); } -// unneeded -void osd_font_invalidate(void) {} -void osd_font_load(struct osd_state *osd) {} +static void update_object(struct osd_state *osd, struct osd_object *obj) +{ + switch (obj->type) { + case OSDTYPE_OSD: + update_osd(osd, obj); + break; + case OSDTYPE_SUBTITLE: + update_sub(osd, obj); + break; + case OSDTYPE_PROGBAR: + update_progbar(osd, obj); + break; + } +} + +void osd_object_get_bitmaps(struct osd_state *osd, struct osd_object *obj, + struct sub_bitmaps *out_imgs) +{ + if (obj->force_redraw) + update_object(osd, obj); + + *out_imgs = (struct sub_bitmaps) {0}; + if (!obj->osd_track) + return; + + ass_set_frame_size(osd->osd_render, osd->w, osd->h); + mp_ass_render_frame(osd->osd_render, obj->osd_track, 0, + &obj->parts_cache, out_imgs); + talloc_steal(obj, obj->parts_cache); +} diff --git a/sub/sd_ass.c b/sub/sd_ass.c index aa190ee4ac..478b1c96a9 100644 --- a/sub/sd_ass.c +++ b/sub/sd_ass.c @@ -141,39 +141,9 @@ static void get_bitmaps(struct sh_sub *sh, struct osd_state *osd, ASS_Renderer *renderer = osd->ass_renderer; mp_ass_configure(renderer, opts, &osd->dim, osd->unscaled); ass_set_aspect_ratio(renderer, scale, 1); - int changed; - res->imgs = ass_render_frame(renderer, ctx->ass_track, - osd->sub_pts * 1000 + .5, &changed); - if (changed == 2) - res->bitmap_id = ++res->bitmap_pos_id; - else if (changed) - res->bitmap_pos_id++; - res->format = SUBBITMAP_LIBASS; - - int num_parts = 0; - int num_parts_alloc = MP_TALLOC_ELEMS(ctx->parts); - struct ass_image *img = res->imgs; - while (img) { - if (img->w == 0 || img->h == 0) - continue; - if (num_parts >= num_parts_alloc) { - num_parts_alloc = FFMAX(num_parts_alloc * 2, 32); - ctx->parts = talloc_realloc(ctx, ctx->parts, struct sub_bitmap, - num_parts_alloc); - } - struct sub_bitmap *p = &ctx->parts[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; - img = img->next; - num_parts++; - } - res->parts = ctx->parts; - res->num_parts = num_parts; + mp_ass_render_frame(renderer, ctx->ass_track, osd->sub_pts * 1000 + .5, + &ctx->parts, res); + talloc_steal(ctx, ctx->parts); } static void reset(struct sh_sub *sh, struct osd_state *osd) diff --git a/sub/spudec.c b/sub/spudec.c index 47e1676e2e..a58df60b17 100644 --- a/sub/spudec.c +++ b/sub/spudec.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include @@ -44,6 +45,7 @@ #include "spudec.h" #include "vobsub.h" +#include "sub.h" #include "mpcommon.h" /* Valid values for spu_aamode: @@ -57,7 +59,6 @@ int spu_aamode = 3; int spu_alignment = -1; float spu_gaussvar = 1.0; -extern int sub_pos; typedef struct spu_packet_t packet_t; struct spu_packet_t { @@ -123,6 +124,9 @@ typedef struct { unsigned int is_forced_sub; /* true if current subtitle is a forced subtitle */ struct palette_crop_cache palette_crop_cache; + + struct sub_bitmap borrowed_sub_part; + struct old_osd_planar borrowed_sub_image; } spudec_handle_t; static void spudec_queue_packet(spudec_handle_t *this, packet_t *packet) @@ -722,6 +726,40 @@ void spudec_set_forced_subs_only(void * const this, const unsigned int flag) } } +static void get_data(void *ctx, int x0,int y0, int w,int h, unsigned char* src, + unsigned char *srca, int stride) +{ + struct sub_bitmaps *bmp = ctx; + assert(bmp->num_parts == 0); + bmp->num_parts = 1; + struct sub_bitmap *s = &bmp->parts[0]; + struct old_osd_planar *p = s->bitmap; + // We know that the data stays valid until the next SPU related call + p->bitmap = src; + p->alpha = srca; + *s = (struct sub_bitmap) { + .bitmap = p, .stride = stride, + .x = x0, .y = y0, + .w = w, .h = h, + .dw = w, .dh = h, + }; +} + +void spudec_get_bitmap(void *this, int w, int h, struct sub_bitmaps *res) +{ + spudec_handle_t *spu = this; + *res = (struct sub_bitmaps) { + .format = SUBBITMAP_OLD_PLANAR, + .parts = &spu->borrowed_sub_part, + }; + res->parts[0].bitmap = &spu->borrowed_sub_image; + if (w == -1 && h == -1) { + spudec_draw(this, get_data, res); + } else { + spudec_draw_scaled(this, w, h, get_data, res); + } +} + void spudec_draw(void *this, void (*draw_alpha)(void *ctx, int x0,int y0, int w,int h, unsigned char* src, unsigned char *srca, int stride), void *ctx) { spudec_handle_t *spu = this; diff --git a/sub/spudec.h b/sub/spudec.h index 6104ad7228..2e2542128a 100644 --- a/sub/spudec.h +++ b/sub/spudec.h @@ -21,10 +21,13 @@ #include +struct sub_bitmaps; + void spudec_heartbeat(void *this, unsigned int pts100); void spudec_assemble(void *this, unsigned char *packet, unsigned int len, int pts100); void spudec_draw(void *this, void (*draw_alpha)(void *ctx, int x0,int y0, int w,int h, unsigned char* src, unsigned char *srca, int stride), void *ctx); void spudec_draw_scaled(void *this, unsigned int dxs, unsigned int dys, void (*draw_alpha)(void *ctx, int x0,int y0, int w,int h, unsigned char* src, unsigned char *srca, int stride), void *ctx); +void spudec_get_bitmap(void *this, int w, int h, struct sub_bitmaps *res); int spudec_apply_palette_crop(void *this, uint32_t palette, int sx, int ex, int sy, int ey); void *spudec_new_scaled(unsigned int *palette, unsigned int frame_width, unsigned int frame_height, uint8_t *extradata, int extradata_len); void *spudec_new(unsigned int *palette); diff --git a/sub/sub.c b/sub/sub.c index 8b89ca497c..3f1b08c185 100644 --- a/sub/sub.c +++ b/sub/sub.c @@ -21,7 +21,6 @@ #include #include -#include #include #include "config.h" @@ -37,6 +36,7 @@ #include "libvo/video_out.h" #include "sub.h" #include "sub/ass_mp.h" +#include "img_convert.h" #include "spudec.h" @@ -86,185 +86,57 @@ float font_factor = 0.75; float sub_delay = 0; float sub_fps = 0; -// allocates/enlarges the alpha/bitmap buffer -void osd_alloc_buf(mp_osd_obj_t* obj) -{ - int len; - if (obj->bbox.x2 < obj->bbox.x1) obj->bbox.x2 = obj->bbox.x1; - if (obj->bbox.y2 < obj->bbox.y1) obj->bbox.y2 = obj->bbox.y1; - obj->stride = ((obj->bbox.x2-obj->bbox.x1)+7)&(~7); - len = obj->stride*(obj->bbox.y2-obj->bbox.y1); - if (obj->allocatedallocated = len; - av_free(obj->bitmap_buffer); - av_free(obj->alpha_buffer); - obj->bitmap_buffer = av_malloc(len); - obj->alpha_buffer = av_malloc(len); - } - memset(obj->bitmap_buffer, sub_bg_color, len); - memset(obj->alpha_buffer, sub_bg_alpha, len); -} - -// renders the buffer -void vo_draw_text_from_buffer(mp_osd_obj_t* obj,void (*draw_alpha)(void *ctx, int x0,int y0, int w,int h, unsigned char* src, unsigned char *srca, int stride), void *ctx) -{ - if (obj->allocated > 0) { - draw_alpha(ctx, - obj->bbox.x1,obj->bbox.y1, - obj->bbox.x2-obj->bbox.x1, - obj->bbox.y2-obj->bbox.y1, - obj->bitmap_buffer, - obj->alpha_buffer, - obj->stride); - } -} - -inline static void vo_update_spudec_sub(struct osd_state *osd, mp_osd_obj_t* obj) -{ - unsigned int bbox[4]; - spudec_calc_bbox(vo_spudec, osd->w, osd->h, bbox); - obj->bbox.x1 = bbox[0]; - obj->bbox.x2 = bbox[1]; - obj->bbox.y1 = bbox[2]; - obj->bbox.y2 = bbox[3]; - obj->flags |= OSDFLAG_BBOX; -} - -inline static void vo_draw_spudec_sub(mp_osd_obj_t* obj, void (*draw_alpha)(void *ctx, int x0, int y0, int w, int h, unsigned char* src, unsigned char* srca, int stride), void *ctx) -{ - spudec_draw_scaled(vo_spudec, obj->dxs, obj->dys, draw_alpha, ctx); -} - void *vo_spudec=NULL; void *vo_vobsub=NULL; -mp_osd_obj_t* vo_osd_list=NULL; - -static mp_osd_obj_t* new_osd_obj(int type){ - mp_osd_obj_t* osd=malloc(sizeof(mp_osd_obj_t)); - memset(osd,0,sizeof(mp_osd_obj_t)); - osd->next=vo_osd_list; - vo_osd_list=osd; - osd->type=type; - osd->alpha_buffer = NULL; - osd->bitmap_buffer = NULL; - osd->allocated = -1; - return osd; -} +static struct osd_state *global_osd; -void osd_free(struct osd_state *osd) -{ - osd_destroy_backend(osd); - mp_osd_obj_t* obj=vo_osd_list; - while(obj){ - mp_osd_obj_t* next=obj->next; - av_free(obj->alpha_buffer); - av_free(obj->bitmap_buffer); - free(obj); - obj=next; - } - vo_osd_list=NULL; - talloc_free(osd); -} -static int osd_update_ext(struct osd_state *osd, int dxs, int dys, - int left_border, int top_border, int right_border, - int bottom_border, int orig_w, int orig_h) +static void osd_update_ext(struct osd_state *osd, struct mp_eosd_res res) { - struct MPOpts *opts = osd->opts; - mp_osd_obj_t* obj=vo_osd_list; - int chg=0; - - osd->w = dxs; - osd->h = dys; - - osd_font_load(osd); - - while(obj){ - if(dxs!=obj->dxs || dys!=obj->dys || obj->flags&OSDFLAG_FORCE_UPDATE){ - int vis=obj->flags&OSDFLAG_VISIBLE; - obj->flags&=~OSDFLAG_BBOX; - switch(obj->type){ - case OSDTYPE_SUBTITLE: - vo_update_text_sub(osd, obj); - break; - case OSDTYPE_PROGBAR: - vo_update_text_progbar(osd, obj); - break; - case OSDTYPE_SPU: - if (opts->sub_visibility && vo_spudec && spudec_visible(vo_spudec)){ - vo_update_spudec_sub(osd, obj); - obj->flags|=OSDFLAG_VISIBLE|OSDFLAG_CHANGED; - } - else - obj->flags&=~OSDFLAG_VISIBLE; - break; - case OSDTYPE_OSD: - if(osd->osd_text[0]){ - vo_update_text_osd(osd, obj); - obj->flags|=OSDFLAG_VISIBLE|OSDFLAG_CHANGED; - } else - obj->flags&=~OSDFLAG_VISIBLE; - break; - } - // check bbox: - if(!(obj->flags&OSDFLAG_BBOX)){ - // we don't know, so assume the whole screen changed :( - obj->bbox.x1=obj->bbox.y1=0; - obj->bbox.x2=dxs; - obj->bbox.y2=dys; - obj->flags|=OSDFLAG_BBOX; - } else { - // check bbox, reduce it if it's out of bounds (corners): - if(obj->bbox.x1<0) obj->bbox.x1=0; - if(obj->bbox.y1<0) obj->bbox.y1=0; - if(obj->bbox.x2>dxs) obj->bbox.x2=dxs; - if(obj->bbox.y2>dys) obj->bbox.y2=dys; - if(obj->flags&OSDFLAG_VISIBLE) - // debug: - mp_msg(MSGT_OSD,MSGL_DBG2,"OSD update: %d;%d %dx%d \n", - obj->bbox.x1,obj->bbox.y1,obj->bbox.x2-obj->bbox.x1, - obj->bbox.y2-obj->bbox.y1); - } - // check if visibility changed: - if(vis != (obj->flags&OSDFLAG_VISIBLE) ) obj->flags|=OSDFLAG_CHANGED; - // remove the cause of automatic update: - obj->dxs=dxs; obj->dys=dys; - obj->flags&=~OSDFLAG_FORCE_UPDATE; - } - if(obj->flags&OSDFLAG_CHANGED){ - chg|=1<type; - mp_msg(MSGT_OSD,MSGL_DBG2,"OSD chg: %d V: %s pb:%d \n",obj->type,(obj->flags&OSDFLAG_VISIBLE)?"yes":"no",vo_osd_progbar_type); - } - obj=obj->next; + if (osd->w != res.w || osd->h != res.h) { + osd->w = res.w; + osd->h = res.h; + for (int n = 0; n < MAX_OSD_PARTS; n++) + osd->objs[n]->force_redraw = true; } - return chg; } -int osd_update(struct osd_state *osd, int dxs, int dys) +void osd_update(struct osd_state *osd, int w, int h) { - return osd_update_ext(osd, dxs, dys, 0, 0, 0, 0, dxs, dys); + osd_update_ext(osd, (struct mp_eosd_res) {.w = w, .h = h}); } struct osd_state *osd_create(struct MPOpts *opts, struct ass_library *asslib) { struct osd_state *osd = talloc_zero(NULL, struct osd_state); - *osd = (struct osd_state){ + *osd = (struct osd_state) { .opts = opts, .ass_library = asslib, }; - // temp hack, should be moved to mplayer later - new_osd_obj(OSDTYPE_ASS); - new_osd_obj(OSDTYPE_OSD); - new_osd_obj(OSDTYPE_SUBTITLE); - new_osd_obj(OSDTYPE_PROGBAR); - new_osd_obj(OSDTYPE_SPU); - osd_font_invalidate(); + for (int n = 0; n < MAX_OSD_PARTS; n++) { + struct osd_object *obj = talloc_struct(osd, struct osd_object, { + .type = n, + }); + for (int i = 0; i < OSD_CONV_CACHE_MAX; i++) + obj->cache[i] = talloc_steal(obj, osd_conv_cache_new()); + osd->objs[n] = obj; + } osd->osd_text = talloc_strdup(osd, ""); osd_init_backend(osd); + global_osd = osd; return osd; } +void osd_free(struct osd_state *osd) +{ + if (!osd) + return; + osd_destroy_backend(osd); + talloc_free(osd); + global_osd = NULL; +} + void osd_set_text(struct osd_state *osd, const char *text) { if (!text) @@ -276,169 +148,180 @@ void osd_set_text(struct osd_state *osd, const char *text) vo_osd_changed(OSDTYPE_OSD); } -void osd_draw_text_ext(struct osd_state *osd, int dxs, int dys, - int left_border, int top_border, int right_border, - int bottom_border, int orig_w, int orig_h, +static bool spu_visible(struct osd_state *osd, struct osd_object *obj) +{ + struct MPOpts *opts = osd->opts; + return opts->sub_visibility && vo_spudec && spudec_visible(vo_spudec); +} + +// Return true if *out_imgs has been filled with valid values. +// Return false on format mismatch, or if nothing to be renderer. +static bool render_object(struct osd_state *osd, struct osd_object *obj, + struct sub_bitmaps *out_imgs, + const bool formats[SUBBITMAP_COUNT]) +{ + memset(out_imgs, 0x55, sizeof(*out_imgs)); + + if (obj->type == OSDTYPE_SPU) { + *out_imgs = (struct sub_bitmaps) {0}; + if (spu_visible(osd, obj)) + spudec_get_bitmap(vo_spudec, osd->w, osd->h, out_imgs); + // Normal change-detection (sub. dec. calls vo_osd_changed(OSDTYPE_SPU)) + if (obj->force_redraw) { + out_imgs->bitmap_id++; + out_imgs->bitmap_pos_id++; + } + } else { + osd_object_get_bitmaps(osd, obj, out_imgs); + } + + obj->force_redraw = false; + obj->vo_bitmap_id += out_imgs->bitmap_id; + obj->vo_bitmap_pos_id += out_imgs->bitmap_pos_id; + + if (out_imgs->num_parts == 0) + return false; + + if (out_imgs->bitmap_id == 0 && out_imgs->bitmap_pos_id == 0 + && obj->cached.bitmap_id == obj->vo_bitmap_id + && obj->cached.bitmap_pos_id == obj->vo_bitmap_pos_id + && formats[obj->cached.format]) + { + *out_imgs = obj->cached; + return true; + } + + out_imgs->render_index = obj->type; + out_imgs->bitmap_id = obj->vo_bitmap_id; + out_imgs->bitmap_pos_id = obj->vo_bitmap_pos_id; + + if (formats[out_imgs->format]) + return true; + + bool cached = false; // do we have a copy of all the image data? + + if ((formats[SUBBITMAP_OLD_PLANAR] || formats[SUBBITMAP_OLD]) + && out_imgs->format == SUBBITMAP_LIBASS) + { + cached |= osd_conv_ass_to_old_p(obj->cache[0], out_imgs); + } + + if (formats[SUBBITMAP_OLD] && out_imgs->format == SUBBITMAP_OLD_PLANAR) { + cached |= osd_conv_old_p_to_old(obj->cache[1], out_imgs); + } + + if (cached) + obj->cached = *out_imgs; + + return formats[out_imgs->format]; +} + +void draw_osd_with_eosd(struct vo *vo, struct osd_state *osd) +{ + mp_eosd_res_t res = {0}; + if (vo_control(vo, VOCTRL_GET_EOSD_RES, &res) != VO_TRUE) + return; + + bool formats[SUBBITMAP_COUNT]; + for (int n = 0; n < SUBBITMAP_COUNT; n++) { + int data = n; + formats[n] = vo_control(vo, VOCTRL_QUERY_EOSD_FORMAT, &data) == VO_TRUE; + } + + osd_update_ext(osd, res); + + for (int n = 0; n < MAX_OSD_PARTS; n++) { + struct osd_object *obj = osd->objs[n]; + struct sub_bitmaps imgs; + if (render_object(osd, obj, &imgs, formats)) + vo_control(vo, VOCTRL_DRAW_EOSD, &imgs); + } +} + +void osd_draw_text_ext(struct osd_state *osd, int w, int h, + int ml, int mt, int mr, int mb, int unused0, int unused1, void (*draw_alpha)(void *ctx, int x0, int y0, int w, int h, unsigned char* src, unsigned char *srca, int stride), void *ctx) { - mp_osd_obj_t* obj=vo_osd_list; - osd_update_ext(osd, dxs, dys, left_border, top_border, right_border, - bottom_border, orig_w, orig_h); - while(obj){ - if(obj->flags&OSDFLAG_VISIBLE){ - switch(obj->type){ - case OSDTYPE_SPU: - if (vo_spudec) - vo_draw_spudec_sub(obj, draw_alpha, ctx); // FIXME - break; - case OSDTYPE_OSD: - case OSDTYPE_SUBTITLE: - case OSDTYPE_PROGBAR: - vo_draw_text_from_buffer(obj, draw_alpha, ctx); - break; - } - obj->old_bbox=obj->bbox; - obj->flags|=OSDFLAG_OLD_BBOX; - } - obj->flags&=~OSDFLAG_CHANGED; - obj=obj->next; + struct mp_eosd_res res = + {.w = w, .h = h, .ml = ml, .mt = mt, .mr = mr, .mb = mb}; + osd_update_ext(osd, res); + for (int n = 0; n < MAX_OSD_PARTS; n++) { + struct osd_object *obj = osd->objs[n]; + struct sub_bitmaps imgs; + bool formats[SUBBITMAP_COUNT] = {[SUBBITMAP_OLD_PLANAR] = true}; + if (render_object(osd, obj, &imgs, formats)) { + assert(imgs.num_parts == 1); + struct sub_bitmap *part = &imgs.parts[0]; + struct old_osd_planar *bmp = part->bitmap; + draw_alpha(ctx, part->x, part->y, part->w, part->h, + bmp->bitmap, bmp->alpha, part->stride); + } } } -void osd_draw_text(struct osd_state *osd, int dxs, int dys, +void osd_draw_text(struct osd_state *osd, int w, int h, void (*draw_alpha)(void *ctx, int x0, int y0, int w, int h, unsigned char* src, unsigned char *srca, int stride), void *ctx) { - osd_draw_text_ext(osd, dxs, dys, 0, 0, 0, 0, dxs, dys, draw_alpha, ctx); + osd_draw_text_ext(osd, w, h, 0, 0, 0, 0, 0, 0, draw_alpha, ctx); } void vo_osd_changed(int new_value) { - mp_osd_obj_t* obj=vo_osd_list; - - while(obj){ - if(obj->type==new_value) obj->flags|=OSDFLAG_FORCE_UPDATE; - obj=obj->next; - } -} - -void vo_osd_reset_changed(void) -{ - mp_osd_obj_t* obj = vo_osd_list; - while (obj) { - obj->flags = obj->flags & ~OSDFLAG_FORCE_UPDATE; - obj = obj->next; + struct osd_state *osd = global_osd; + for (int n = 0; n < MAX_OSD_PARTS; n++) { + if (osd->objs[n]->type == new_value) + osd->objs[n]->force_redraw = true; } } bool vo_osd_has_changed(struct osd_state *osd) { - mp_osd_obj_t* obj = vo_osd_list; - while (obj) { - if (obj->flags & OSDFLAG_FORCE_UPDATE) + for (int n = 0; n < MAX_OSD_PARTS; n++) { + if (osd->objs[n]->force_redraw) return true; - obj = obj->next; } return false; } -void vo_osd_resized() +// Needed for VOs using the old OSD API (osd_draw_text_[ext]). +void vo_osd_reset_changed(void) { - // font needs to be adjusted - osd_font_invalidate(); - // OSD needs to be drawn fresh for new size - vo_osd_changed(OSDTYPE_OSD); - vo_osd_changed(OSDTYPE_SUBTITLE); + struct osd_state *osd = global_osd; + for (int n = 0; n < MAX_OSD_PARTS; n++) + osd->objs[n]->force_redraw = false; } -// return TRUE if we have osd in the specified rectangular area: -int vo_osd_check_range_update(int x1,int y1,int x2,int y2){ - mp_osd_obj_t* obj=vo_osd_list; - while(obj){ - if(obj->flags&OSDFLAG_VISIBLE){ - if( (obj->bbox.x1<=x2 && obj->bbox.x2>=x1) && - (obj->bbox.y1<=y2 && obj->bbox.y2>=y1) && - obj->bbox.y2 > obj->bbox.y1 && obj->bbox.x2 > obj->bbox.x1 - ) return 1; - } - obj=obj->next; - } - return 0; +void vo_osd_resized(void) +{ + // Counter the typical vo_osd_has_changed(osd) call in VO's draw_osd() + struct osd_state *osd = global_osd; + for (int n = 0; n < MAX_OSD_PARTS; n++) + osd->objs[n]->force_redraw = true; } -struct draw_osd_closure { - struct vo *vo; - struct osd_state *osd; - int render_index; -}; - -static void eosd_draw_osd_part(void *ctx, int x0, int y0, int w, int h, - unsigned char *src, unsigned char *srca, - int stride) +bool sub_bitmaps_bb(struct sub_bitmaps *imgs, int *x1, int *y1, + int *x2, int *y2) { - struct draw_osd_closure *c = ctx; - - assert(c->render_index < MAX_OSD_PARTS); - assert(w > 0 && h > 0); - - size_t scratch_size = talloc_get_size(c->osd->scratch); - size_t new_size = stride * h * 2; - if (new_size > scratch_size) { - scratch_size = new_size; - c->osd->scratch = talloc_realloc(c->osd, c->osd->scratch, char *, - new_size); - } - - unsigned char *tmp = c->osd->scratch; - - for (int y = 0; y < h; y++) { - unsigned char *y_src = src + stride * y; - unsigned char *y_srca = srca + stride * y; - unsigned char *cur = tmp + y * w * 2; - for (int x = 0; x < w; x++) { - cur[x*2+0] = y_src[x]; - cur[x*2+1] = -y_srca[x]; - } + *x1 = *y1 = INT_MAX; + *x2 = *y2 = INT_MIN; + for (int n = 0; n < imgs->num_parts; n++) { + struct sub_bitmap *p = &imgs->parts[n]; + *x1 = FFMIN(*x1, p->x); + *y1 = FFMIN(*y1, p->y); + *x2 = FFMAX(*x2, p->x + p->dw); + *y2 = FFMAX(*y2, p->y + p->dh); } - struct sub_bitmaps *imgs = &c->osd->eosd[c->render_index]; - imgs->render_index = c->render_index; - imgs->format = SUBBITMAP_OLD; - imgs->bitmap_id++; - imgs->bitmap_pos_id++; - if (!imgs->num_parts) { - imgs->num_parts = 1; - imgs->parts = talloc_array(c->osd, struct sub_bitmap, imgs->num_parts); - } - - imgs->parts[0] = (struct sub_bitmap) { - .bitmap = tmp, - .stride = w * 2, - .x = x0, .y = y0, - .w = w, .h = h, - .dw = w, .dh = h, - }; - - vo_control(c->vo, VOCTRL_DRAW_EOSD, imgs); - - c->render_index++; -} - -// draw old-OSD using EOSD -void emulate_draw_osd(struct vo *vo, struct osd_state *osd) -{ - mp_eosd_res_t res = {0}; - if (vo_control(vo, VOCTRL_GET_EOSD_RES, &res) != VO_TRUE) - return; + // avoid degenerate bounding box if empty + *x1 = FFMIN(*x1, *x2); + *y1 = FFMIN(*y1, *y2); - struct draw_osd_closure c = {vo, osd}; - c.render_index = 1; // 0 is the "normal" EOSD renderer for subtitles - osd_draw_text_ext(osd, res.w, res.h, res.ml, res.mt, res.mr, res.mb, 0, 0, - eosd_draw_osd_part, &c); + return *x1 < *x2 && *y1 < *y2; } diff --git a/sub/sub.h b/sub/sub.h index 74d2b5a37a..1dee0c8a8f 100644 --- a/sub/sub.h +++ b/sub/sub.h @@ -26,48 +26,40 @@ struct vo; -typedef struct mp_osd_bbox_s { - int x1,y1,x2,y2; -} mp_osd_bbox_t; - #define OSDTYPE_ASS 0 #define OSDTYPE_OSD 1 #define OSDTYPE_SUBTITLE 2 #define OSDTYPE_PROGBAR 3 #define OSDTYPE_SPU 4 -#define OSDFLAG_VISIBLE 1 -#define OSDFLAG_CHANGED 2 -#define OSDFLAG_BBOX 4 -#define OSDFLAG_OLD_BBOX 8 -#define OSDFLAG_FORCE_UPDATE 16 - -#define MAX_UCS 1600 -#define MAX_UCSLINES 16 - -typedef struct mp_osd_obj_s { - struct mp_osd_obj_s* next; - unsigned char type; - unsigned short flags; - int x,y; - int dxs,dys; - mp_osd_bbox_t bbox; // bounding box - mp_osd_bbox_t old_bbox; // the renderer will save bbox here - int stride; - - int allocated; - unsigned char *alpha_buffer; - unsigned char *bitmap_buffer; +#define MAX_OSD_PARTS 5 + +#define OSD_CONV_CACHE_MAX 2 + +struct osd_object { + int type; // OSDTYPE_* + bool force_redraw; + + // caches for OSD conversion (internal to render_object()) + struct osd_conv_cache *cache[OSD_CONV_CACHE_MAX]; + + struct sub_bitmaps cached; + // VO cache state + int vo_bitmap_id; + int vo_bitmap_pos_id; + + // Internally used by osd_libass.c struct ass_track *osd_track; -} mp_osd_obj_t; + struct sub_bitmap *parts_cache; +}; struct osd_state { + struct osd_object *objs[MAX_OSD_PARTS]; + struct ass_library *ass_library; struct ass_renderer *ass_renderer; struct sh_sub *sh_sub; - int bitmap_id; - int bitmap_pos_id; double sub_pts; double sub_offset; struct mp_eosd_res dim; @@ -76,16 +68,19 @@ struct osd_state { bool unscaled; bool support_rgba; - struct ass_renderer *osd_render; - struct ass_library *osd_ass_library; - char *osd_text; int w, h; - struct sub_bitmaps eosd[MAX_OSD_PARTS]; + char *osd_text; // OSDTYPE_OSD - void *scratch; + // temporary for sub decoders + int bitmap_id; + int bitmap_pos_id; struct MPOpts *opts; + + // Internally used by osd_libass.c + struct ass_renderer *osd_render; + struct ass_library *osd_ass_library; }; extern subtitle* vo_sub; @@ -160,29 +155,25 @@ void osd_draw_text_ext(struct osd_state *osd, int dxs, int dys, int stride), void *ctx); -void emulate_draw_osd(struct vo *vo, struct osd_state *osd); +void draw_osd_with_eosd(struct vo *vo, struct osd_state *osd); struct osd_state *osd_create(struct MPOpts *opts, struct ass_library *asslib); void osd_set_text(struct osd_state *osd, const char *text); -int osd_update(struct osd_state *osd, int dxs, int dys); +void osd_update(struct osd_state *osd, int dxs, int dys); void vo_osd_changed(int new_value); void vo_osd_reset_changed(void); bool vo_osd_has_changed(struct osd_state *osd); void vo_osd_resized(void); -int vo_osd_check_range_update(int,int,int,int); void osd_free(struct osd_state *osd); -// used only by osd_ft.c or osd_libass.c -void osd_alloc_buf(mp_osd_obj_t* obj); -void vo_draw_text_from_buffer(mp_osd_obj_t* obj,void (*draw_alpha)(void *ctx, int x0,int y0, int w,int h, unsigned char* src, unsigned char *srca, int stride), void *ctx); +bool sub_bitmaps_bb(struct sub_bitmaps *imgs, int *x1, int *y1, + int *x2, int *y2); + +// defined in osd_libass.c and osd_dummy.c -// defined in osd_ft.c or osd_libass.c -void vo_update_text_osd(struct osd_state *osd, mp_osd_obj_t *obj); -void vo_update_text_progbar(struct osd_state *osd, mp_osd_obj_t *obj); -void vo_update_text_sub(struct osd_state *osd, mp_osd_obj_t *obj); +void osd_object_get_bitmaps(struct osd_state *osd, struct osd_object *obj, + struct sub_bitmaps *out_imgs); void osd_get_function_sym(char *buffer, size_t buffer_size, int osd_function); -void osd_font_invalidate(void); -void osd_font_load(struct osd_state *osd); void osd_init_backend(struct osd_state *osd); void osd_destroy_backend(struct osd_state *osd); -- cgit v1.2.3 From 5fc5ae752bfaa6bddf76aa1912b4b9724578f5dd Mon Sep 17 00:00:00 2001 From: wm4 Date: Fri, 28 Sep 2012 21:48:30 +0200 Subject: sub: allow converting DVD subs to RGBA The mplayer DVD sub decoder is the only remaining OSD image producer that still requires the old mplayer OSD format (SUBBITMAP_OLD_PLANAR). To make supporting this format optional in VOs, add a step that allows converting these images to RGBA in case the VO doesn't have direct support for it. Note: the mplayer DVD sub decoder uses the old mplayer OSD format (SUBBITMAP_OLD_PLANAR), which is assumed to use premultiplied alpha. However, it seems DVDs allow only binary transparency, so the rendered result will be the same. --- sub/img_convert.c | 52 +++++++++++++++++++++++++++++++++++++++++++++++----- sub/img_convert.h | 1 + sub/sub.c | 4 ++++ sub/sub.h | 2 +- 4 files changed, 53 insertions(+), 6 deletions(-) (limited to 'sub') diff --git a/sub/img_convert.c b/sub/img_convert.c index 38a367253c..6eef4a01a3 100644 --- a/sub/img_convert.c +++ b/sub/img_convert.c @@ -31,7 +31,7 @@ struct osd_conv_cache { // for osd_conv_cache_alloc_old_p() (SUBBITMAP_PLANAR) int allocated, stride; struct old_osd_planar bmp; - // for osd_conv_cache_alloc_old() (SUBBITMAP_OLD_PLANAR) + // for osd_conv_cache_alloc_bmp() (various other formats) unsigned char *packed; }; @@ -73,15 +73,16 @@ static void osd_conv_cache_alloc_old_p(struct osd_conv_cache *c, int w, int h) }; } -static void osd_conv_cache_alloc_old(struct osd_conv_cache *c, int w, int h) +static void osd_conv_cache_alloc_bmp(struct osd_conv_cache *c, int w, int h, + int bpp) { size_t size = talloc_get_size(c->packed); - size_t new_size = w * 2 * h; + size_t new_size = w * bpp * h; if (new_size > size) c->packed = talloc_realloc(c, c->packed, unsigned char, new_size); c->part = (struct sub_bitmap) { .bitmap = c->packed, - .stride = w * 2, + .stride = w * bpp, .w = w, .h = h, .dw = w, .dh = h, }; @@ -176,6 +177,47 @@ bool osd_conv_ass_to_old_p(struct osd_conv_cache *c, struct sub_bitmaps *imgs) return true; } +// SUBBITMAP_OLD_PLANAR -> SUBBITMAP_RGBA +bool osd_conv_old_p_to_rgba(struct osd_conv_cache *c, struct sub_bitmaps *imgs) +{ + struct sub_bitmaps src = *imgs; + if (src.format != SUBBITMAP_OLD_PLANAR || src.num_parts > 1) + return false; + + imgs->format = SUBBITMAP_RGBA; + imgs->num_parts = 0; + imgs->parts = NULL; + + if (src.num_parts == 0) + return true; + + struct sub_bitmap *s = &src.parts[0]; + struct old_osd_planar *p = s->bitmap; + + osd_conv_cache_alloc_bmp(c, s->w, s->h, 4); + + for (int y = 0; y < s->h; y++) { + unsigned char *y_src = p->bitmap + s->stride * y; + unsigned char *y_srca = p->alpha + s->stride * y; + unsigned char *cur = c->packed + y * s->w * 4; + for (int x = 0; x < s->w; x++) { + // This is incorrect, as input is premultiplied alpha, but output + // has to be non-premultiplied. However, this code is for + // compatibility with spudec.c only, and DVD subtitles have + // binary transparency only - the rendered result will be the same. + cur[x*4+0] = cur[x*4+1] = cur[x*4+2] = y_src[x]; + cur[x*4+3] = -y_srca[x]; + } + } + + c->part.x = s->x; + c->part.y = s->y; + + imgs->parts = &c->part; + imgs->num_parts = 1; + return true; +} + // SUBBITMAP_OLD_PLANAR -> SUBBITMAP_OLD bool osd_conv_old_p_to_old(struct osd_conv_cache *c, struct sub_bitmaps *imgs) { @@ -193,7 +235,7 @@ bool osd_conv_old_p_to_old(struct osd_conv_cache *c, struct sub_bitmaps *imgs) struct sub_bitmap *s = &src.parts[0]; struct old_osd_planar *p = s->bitmap; - osd_conv_cache_alloc_old(c, s->w, s->h); + osd_conv_cache_alloc_bmp(c, s->w, s->h, 2); for (int y = 0; y < s->h; y++) { unsigned char *y_src = p->bitmap + s->stride * y; diff --git a/sub/img_convert.h b/sub/img_convert.h index c40a8de2e4..b4201f606c 100644 --- a/sub/img_convert.h +++ b/sub/img_convert.h @@ -11,6 +11,7 @@ struct osd_conv_cache *osd_conv_cache_new(void); // These functions convert from one OSD format to another. On success, they copy // the converted image data into c, and change imgs to point to the data. bool osd_conv_old_p_to_old(struct osd_conv_cache *c, struct sub_bitmaps *imgs); +bool osd_conv_old_p_to_rgba(struct osd_conv_cache *c, struct sub_bitmaps *imgs); bool osd_conv_ass_to_old_p(struct osd_conv_cache *c, struct sub_bitmaps *imgs); #endif diff --git a/sub/sub.c b/sub/sub.c index 3f1b08c185..182215310f 100644 --- a/sub/sub.c +++ b/sub/sub.c @@ -210,6 +210,10 @@ static bool render_object(struct osd_state *osd, struct osd_object *obj, cached |= osd_conv_old_p_to_old(obj->cache[1], out_imgs); } + if (formats[SUBBITMAP_RGBA] && out_imgs->format == SUBBITMAP_OLD_PLANAR) { + cached |= osd_conv_old_p_to_rgba(obj->cache[2], out_imgs); + } + if (cached) obj->cached = *out_imgs; diff --git a/sub/sub.h b/sub/sub.h index 1dee0c8a8f..7deaf73b8e 100644 --- a/sub/sub.h +++ b/sub/sub.h @@ -34,7 +34,7 @@ struct vo; #define MAX_OSD_PARTS 5 -#define OSD_CONV_CACHE_MAX 2 +#define OSD_CONV_CACHE_MAX 3 struct osd_object { int type; // OSDTYPE_* -- cgit v1.2.3 From e62b3a175016eaf93ef5ead7ea3891bc85327a55 Mon Sep 17 00:00:00 2001 From: wm4 Date: Sat, 29 Sep 2012 09:53:28 +0200 Subject: sub: cosmetics: turn some defines into enums --- sub/osd_libass.c | 3 --- sub/sub.h | 59 ++++++++++++++++++++++++++++++++------------------------ 2 files changed, 34 insertions(+), 28 deletions(-) (limited to 'sub') diff --git a/sub/osd_libass.c b/sub/osd_libass.c index 2d596a6516..6c325642c5 100644 --- a/sub/osd_libass.c +++ b/sub/osd_libass.c @@ -35,9 +35,6 @@ static const char osd_font_pfb[] = #include "mp_core.h" -// Map OSD symbols (e.g. OSD_PLAY) to the glyphs in osd_font_pfb[]. -#define OSD_CODEPOINTS 0xE000 - // NOTE: \fs-5 to reduce the size of the symbols in relation to normal text. // Done because libass doesn't center characters that are too high. #define ASS_USE_OSD_FONT "{\\fnOSD\\fs-5}" diff --git a/sub/sub.h b/sub/sub.h index 7deaf73b8e..ceecce4017 100644 --- a/sub/sub.h +++ b/sub/sub.h @@ -26,13 +26,15 @@ struct vo; -#define OSDTYPE_ASS 0 -#define OSDTYPE_OSD 1 -#define OSDTYPE_SUBTITLE 2 -#define OSDTYPE_PROGBAR 3 -#define OSDTYPE_SPU 4 - -#define MAX_OSD_PARTS 5 +enum mp_osdtype { + OSDTYPE_ASS, + OSDTYPE_OSD, + OSDTYPE_SUBTITLE, + OSDTYPE_PROGBAR, + OSDTYPE_SPU, + + MAX_OSD_PARTS +}; #define OSD_CONV_CACHE_MAX 3 @@ -91,24 +93,31 @@ extern int vo_osd_progbar_value; // 0..255 extern void* vo_spudec; extern void* vo_vobsub; -#define OSD_PLAY 0x01 -#define OSD_PAUSE 0x02 -#define OSD_STOP 0x03 -#define OSD_REW 0x04 -#define OSD_FFW 0x05 -#define OSD_CLOCK 0x06 -#define OSD_CONTRAST 0x07 -#define OSD_SATURATION 0x08 -#define OSD_VOLUME 0x09 -#define OSD_BRIGHTNESS 0x0A -#define OSD_HUE 0x0B -#define OSD_BALANCE 0x0C -#define OSD_PANSCAN 0x50 - -#define OSD_PB_START 0x10 -#define OSD_PB_0 0x11 -#define OSD_PB_END 0x12 -#define OSD_PB_1 0x13 +// Start of OSD symbols in osd_font.pfb +#define OSD_CODEPOINTS 0xE000 + +// OSD symbols. osd_font.pfb has them starting from codepoint OSD_CODEPOINTS. +// Symbols with a value >= 32 are normal unicode codepoints. +enum mp_osd_font_codepoints { + OSD_PLAY = 0x01, + OSD_PA