summaryrefslogtreecommitdiffstats
path: root/sub/sub.c
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2012-09-28 21:38:52 +0200
committerwm4 <wm4@nowhere>2012-10-16 07:26:30 +0200
commit3365514951e9c07ec3a21bb3898e5796c214f8b7 (patch)
tree168eec4a47cbd32fa6e6485a884203e350ba8474 /sub/sub.c
parent3099498154a06c6df0c365de2cc0af09686cd6e1 (diff)
downloadmpv-3365514951e9c07ec3a21bb3898e5796c214f8b7.tar.bz2
mpv-3365514951e9c07ec3a21bb3898e5796c214f8b7.tar.xz
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.)
Diffstat (limited to 'sub/sub.c')
-rw-r--r--sub/sub.c453
1 files changed, 168 insertions, 285 deletions
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 <string.h>
#include <assert.h>
-#include <libavutil/mem.h>
#include <libavutil/common.h>
#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->allocated<len) {
- obj->allocated = 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<<obj->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;
}