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. --- libmpcodecs/vfcap.h | 1 - libvo/bitmap_packer.c | 47 ++++++++++++++-------------- libvo/bitmap_packer.h | 9 +++++- libvo/vo_corevideo.m | 7 ++--- libvo/vo_gl.c | 3 -- libvo/vo_gl3.c | 3 -- libvo/vo_vdpau.c | 84 ++++++++++++++++++--------------------------------- mplayer.c | 2 -- sub/dec_sub.c | 3 +- sub/dec_sub.h | 49 +++++++++++++++++++++--------- sub/sd_ass.c | 28 ++++++++++++++++- sub/sd_lavc.c | 5 +-- 12 files changed, 130 insertions(+), 111 deletions(-) diff --git a/libmpcodecs/vfcap.h b/libmpcodecs/vfcap.h index aef75840ae..0bb255b3cd 100644 --- a/libmpcodecs/vfcap.h +++ b/libmpcodecs/vfcap.h @@ -46,7 +46,6 @@ #define VFCAP_EOSD_UNSCALED 0x4000 // used by libvo and vf_vo, indicates the VO does not support draw_slice for this format #define VOCAP_NOSLICES 0x8000 -#define VFCAP_OSD_FILTER 0x10000 // OSD is drawn in filter chain #define VFCAP_EOSD_FILTER 0x20000 // EOSD is drawn in filter chain #define VFCAP_EOSD_RGBA 0x40000 diff --git a/libvo/bitmap_packer.c b/libvo/bitmap_packer.c index eedc2e2242..5e5bafea0c 100644 --- a/libvo/bitmap_packer.c +++ b/libvo/bitmap_packer.c @@ -30,6 +30,24 @@ #include "sub/ass_mp.h" #include "sub/dec_sub.h" +void packer_reset(struct bitmap_packer *packer) +{ + struct bitmap_packer old = *packer; + *packer = (struct bitmap_packer) { + .w_max = old.w_max, + .h_max = old.h_max, + }; + talloc_free_children(packer); +} + +void packer_get_bb(struct bitmap_packer *packer, struct pos out_bb[2]) +{ + out_bb[0] = (struct pos) {0}; + out_bb[1] = (struct pos) { + FFMIN(packer->used_width + packer->padding, packer->w), + FFMIN(packer->used_height + packer->padding, packer->h), + }; +} #define HEIGHT_SORT_BITS 4 static int size_index(int s) @@ -171,36 +189,15 @@ void packer_set_size(struct bitmap_packer *packer, int size) packer->asize + 16); } -static int packer_pack_from_assimg(struct bitmap_packer *packer, - struct ass_image *imglist) -{ - int count = 0; - struct ass_image *img = imglist; - while (img) { - if (count >= packer->asize) - packer_set_size(packer, FFMAX(packer->asize * 2, 32)); - packer->in[count].x = img->w; - packer->in[count].y = img->h; - img = img->next; - count++; - } - packer->count = count; - return packer_pack(packer); -} - int packer_pack_from_subbitmaps(struct bitmap_packer *packer, - struct sub_bitmaps *b, int padding_pixels) + struct sub_bitmaps *b) { - packer->padding = 0; packer->count = 0; - if (b->type == SUBBITMAP_EMPTY) + if (b->format == SUBBITMAP_EMPTY) return 0; - if (b->type == SUBBITMAP_LIBASS) - return packer_pack_from_assimg(packer, b->imgs); - packer->padding = padding_pixels; - packer_set_size(packer, b->part_count); + packer_set_size(packer, b->num_parts); int a = packer->padding; - for (int i = 0; i < b->part_count; i++) + for (int i = 0; i < b->num_parts; i++) packer->in[i] = (struct pos){b->parts[i].w + a, b->parts[i].h + a}; return packer_pack(packer); } diff --git a/libvo/bitmap_packer.h b/libvo/bitmap_packer.h index c7c377cbd0..99c7b514b4 100644 --- a/libvo/bitmap_packer.h +++ b/libvo/bitmap_packer.h @@ -26,6 +26,13 @@ struct bitmap_packer { struct ass_image; struct sub_bitmaps; +// Clear all internal state. Leave the following fields: w_max, h_max +void packer_reset(struct bitmap_packer *packer); + +// Get the bounding box used for bitmap data (including padding). +// The bounding box doesn't exceed (0,0)-(packer->w,packer->h). +void packer_get_bb(struct bitmap_packer *packer, struct pos out_bb[2]); + /* Reallocate packer->in for at least to desired number of items. * Also sets packer->count to the same value. */ @@ -46,6 +53,6 @@ int packer_pack(struct bitmap_packer *packer); * given image list. */ int packer_pack_from_subbitmaps(struct bitmap_packer *packer, - struct sub_bitmaps *b, int padding_pixels); + struct sub_bitmaps *b); #endif diff --git a/libvo/vo_corevideo.m b/libvo/vo_corevideo.m index 1d10330bcb..fd667acb61 100644 --- a/libvo/vo_corevideo.m +++ b/libvo/vo_corevideo.m @@ -45,11 +45,10 @@ struct quad { }; #define CV_VERTICES_PER_QUAD 6 -#define CV_MAX_OSD_PARTS 20 struct osd_p { - GLuint tex[CV_MAX_OSD_PARTS]; - NSRect tex_rect[CV_MAX_OSD_PARTS]; + GLuint tex[MAX_OSD_PARTS]; + NSRect tex_rect[MAX_OSD_PARTS]; int tex_cnt; }; @@ -185,7 +184,7 @@ static void create_osd_texture(void *ctx, int x0, int y0, int w, int h, return; } - if (osd->tex_cnt >= CV_MAX_OSD_PARTS) { + if (osd->tex_cnt >= MAX_OSD_PARTS) { mp_msg(MSGT_VO, MSGL_ERR, "Too many OSD parts, contact the" " developers!\n"); return; diff --git a/libvo/vo_gl.c b/libvo/vo_gl.c index 5453943fe9..236471d537 100644 --- a/libvo/vo_gl.c +++ b/libvo/vo_gl.c @@ -44,9 +44,6 @@ #include "fastmemcpy.h" #include "sub/ass_mp.h" -//! How many parts the OSD may consist of at most -#define MAX_OSD_PARTS 20 - //for gl_priv.use_yuv #define MASK_ALL_YUV (~(1 << YUV_CONVERSION_NONE)) #define MASK_NOT_COMBINERS (~((1 << YUV_CONVERSION_NONE) | (1 << YUV_CONVERSION_COMBINERS))) diff --git a/libvo/vo_gl3.c b/libvo/vo_gl3.c index dea0c6637a..ebce32677b 100644 --- a/libvo/vo_gl3.c +++ b/libvo/vo_gl3.c @@ -59,9 +59,6 @@ static const char vo_gl3_shaders[] = #include "libvo/vo_gl3_shaders.h" ; -// How many parts the OSD may consist of at most. -#define MAX_OSD_PARTS 20 - // Pixel width of 1D lookup textures. #define LOOKUP_TEXTURE_SIZE 256 diff --git a/libvo/vo_vdpau.c b/libvo/vo_vdpau.c index 594829b394..d5856e2dfa 100644 --- a/libvo/vo_vdpau.c +++ b/libvo/vo_vdpau.c @@ -92,8 +92,6 @@ struct vdp_functions { #undef VDP_FUNCTION }; -#define MAX_OLD_OSD_BITMAPS 6 - struct vdpctx { struct vdp_functions *vdp; @@ -165,7 +163,7 @@ struct vdpctx { int x0, y0, w, h; unsigned char *src, *srca; int stride; - } old_osd_elements[MAX_OLD_OSD_BITMAPS]; + } old_osd_elements[MAX_OSD_PARTS]; int old_osd_count; unsigned char *osd_data_temp; int osd_data_size; @@ -184,7 +182,7 @@ struct vdpctx { VdpRect source; VdpRect dest; VdpColor color; - } *eosd_targets, osd_targets[MAX_OLD_OSD_BITMAPS][2]; + } *eosd_targets, osd_targets[MAX_OSD_PARTS][2]; int eosd_targets_size; int eosd_render_count; int bitmap_id; @@ -1008,7 +1006,7 @@ static void generate_eosd(struct vo *vo, mp_eosd_images_t *imgs) vc->eosd_render_count = 0; - if (imgs->type == SUBBITMAP_EMPTY) + if (imgs->format == SUBBITMAP_EMPTY) return; if (imgs->bitmap_id == vc->bitmap_id) @@ -1017,7 +1015,7 @@ static void generate_eosd(struct vo *vo, mp_eosd_images_t *imgs) need_upload = true; VdpRGBAFormat format; int format_size; - switch (imgs->type) { + switch (imgs->format) { case SUBBITMAP_LIBASS: format = VDP_RGBA_FORMAT_A8; format_size = 1; @@ -1036,7 +1034,8 @@ static void generate_eosd(struct vo *vo, mp_eosd_images_t *imgs) sfc->format = format; if (!sfc->packer) sfc->packer = make_packer(vo, format); - int r = packer_pack_from_subbitmaps(sfc->packer, imgs, imgs->scaled); + sfc->packer->padding = imgs->scaled; // assume 2x2 filter on scaling + int r = packer_pack_from_subbitmaps(sfc->packer, imgs); if (r < 0) { mp_msg(MSGT_VO, MSGL_ERR, "[vdpau] EOSD bitmaps do not fit on " "a surface with the maximum supported size\n"); @@ -1072,57 +1071,34 @@ eosd_skip_upload: vc->eosd_targets_size = sfc->packer->count; vc->eosd_targets = talloc_size(vc, vc->eosd_targets_size * sizeof(*vc->eosd_targets)); - } + } - if (imgs->type == SUBBITMAP_LIBASS) { - int i = 0; - for (ASS_Image *p = imgs->imgs; p; p = p->next, i++) { - if (p->w == 0 || p->h == 0) - continue; - struct eosd_target *target = vc->eosd_targets + - vc->eosd_render_count; - int x = sfc->packer->result[i].x; - int y = sfc->packer->result[i].y; - target->source = (VdpRect){x, y, x + p->w, y + p->h}; - if (need_upload) { - vdp_st = vdp-> - bitmap_surface_put_bits_native(sfc->surface, - (const void *) &p->bitmap, - &p->stride, &target->source); - CHECK_ST_WARNING("EOSD: putbits failed"); - } - // Render dest, color, etc. - target->color.alpha = 1.0 - ((p->color >> 0) & 0xff) / 255.0; - target->color.blue = ((p->color >> 8) & 0xff) / 255.0; - target->color.green = ((p->color >> 16) & 0xff) / 255.0; - target->color.red = ((p->color >> 24) & 0xff) / 255.0; - target->dest.x0 = p->dst_x; - target->dest.y0 = p->dst_y; - target->dest.x1 = p->w + p->dst_x; - target->dest.y1 = p->h + p->dst_y; - vc->eosd_render_count++; + for (int i = 0 ;i < sfc->packer->count; i++) { + struct sub_bitmap *b = &imgs->parts[i]; + struct eosd_target *target = vc->eosd_targets + vc->eosd_render_count; + int x = sfc->packer->result[i].x; + int y = sfc->packer->result[i].y; + target->source = (VdpRect){x, y, x + b->w, y + b->h}; + target->dest = (VdpRect){b->x, b->y, b->x + b->dw, b->y + b->dh}; + target->color = (VdpColor){1, 1, 1, 1}; + if (imgs->format == SUBBITMAP_LIBASS) { + uint32_t color = b->libass.color; + target->color.alpha = 1.0 - ((color >> 0) & 0xff) / 255.0; + target->color.blue = ((color >> 8) & 0xff) / 255.0; + target->color.green = ((color >> 16) & 0xff) / 255.0; + target->color.red = ((color >> 24) & 0xff) / 255.0; } - } else { - for (int i = 0 ;i < sfc->packer->count; i++) { - struct sub_bitmap *b = &imgs->parts[i]; - struct eosd_target *target = vc->eosd_targets + - vc->eosd_render_count; - int x = sfc->packer->result[i].x; - int y = sfc->packer->result[i].y; - target->source = (VdpRect){x, y, x + b->w, y + b->h}; - if (need_upload) { - vdp_st = vdp-> - bitmap_surface_put_bits_native(sfc->surface, - &(const void *){b->bitmap}, - &(uint32_t){b->w * 4}, - &target->source); + if (need_upload) { + vdp_st = vdp-> + bitmap_surface_put_bits_native(sfc->surface, + &(const void *){b->bitmap}, + &(uint32_t){b->stride}, + &target->source); CHECK_ST_WARNING("EOSD: putbits failed"); - } - target->color = (VdpColor){1, 1, 1, 1}; - target->dest = (VdpRect){b->x, b->y, b->x + b->dw, b->y + b->dh}; - vc->eosd_render_count++; } + vc->eosd_render_count++; } + vc->bitmap_id = imgs->bitmap_id; vc->bitmap_pos_id = imgs->bitmap_pos_id; } @@ -1133,7 +1109,7 @@ static void record_osd(void *ctx, int x0, int y0, int w, int h, struct vo *vo = ctx; struct vdpctx *vc = vo->priv; - assert(vc->old_osd_count < MAX_OLD_OSD_BITMAPS); + assert(vc->old_osd_count < MAX_OSD_PARTS); if (!w || !h) return; vc->old_osd_elements[vc->old_osd_count++] = (struct old_osd){ diff --git a/mplayer.c b/mplayer.c index d17be7c357..5c08cdf330 100644 --- a/mplayer.c +++ b/mplayer.c @@ -2638,8 +2638,6 @@ static int redraw_osd(struct MPContext *mpctx) { struct sh_video *sh_video = mpctx->sh_video; struct vf_instance *vf = sh_video->vfilter; - if (sh_video->output_flags & VFCAP_OSD_FILTER) - return -1; if (vo_redraw_frame(mpctx->video_out) < 0) return -1; mpctx->osd->sub_pts = mpctx->video_pts; 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 3c9c1790fee177cc9c9661475746a92ab6ce9bea Mon Sep 17 00:00:00 2001 From: wm4 Date: Fri, 28 Sep 2012 21:25:26 +0200 Subject: vo_gl3: support RGBA EOSD This also adds support for multiple EOSD renderers. This capability is unused yet, but important for the following commits. --- libvo/fastmemcpy.h | 13 +++ libvo/gl_common.c | 26 +++++ libvo/gl_common.h | 3 + libvo/vo_gl3.c | 290 +++++++++++++++++++++++++++++++--------------- libvo/vo_gl3_shaders.glsl | 18 ++- 5 files changed, 251 insertions(+), 99 deletions(-) diff --git a/libvo/fastmemcpy.h b/libvo/fastmemcpy.h index 5d05d37043..36fada39fe 100644 --- a/libvo/fastmemcpy.h +++ b/libvo/fastmemcpy.h @@ -64,4 +64,17 @@ static inline void * memcpy_pic2(void * dst, const void * src, return retval; } +static inline void memset_pic(void *dst, int fill, int bytesPerLine, int height, + int stride) +{ + if (bytesPerLine == stride) { + memset(dst, fill, stride * height); + } else { + for (int i = 0; i < height; i++) { + memset(dst, fill, bytesPerLine); + dst = (uint8_t *)dst + stride; + } + } +} + #endif /* MPLAYER_FASTMEMCPY_H */ diff --git a/libvo/gl_common.c b/libvo/gl_common.c index 1ad7017e10..c56b0c8ffc 100644 --- a/libvo/gl_common.c +++ b/libvo/gl_common.c @@ -427,6 +427,7 @@ static const extfunc_desc_t extfuncs[] = { DEF_GL3_DESC(FramebufferTexture2D), DEF_GL3_DESC(Uniform1f), DEF_GL3_DESC(Uniform3f), + DEF_GL3_DESC(Uniform4f), DEF_GL3_DESC(Uniform1i), DEF_GL3_DESC(UniformMatrix3fv), DEF_GL3_DESC(UniformMatrix4x3fv), @@ -674,6 +675,31 @@ void glUploadTex(GL *gl, GLenum target, GLenum format, GLenum type, gl->TexSubImage2D(target, 0, x, y, w, y_max - y, format, type, data); } +// Like glUploadTex, but upload a byte array with all elements set to val. +// If scratch is not NULL, points to a resizeable talloc memory block than can +// be freely used by the function (for avoiding temporary memory allocations). +void glClearTex(GL *gl, GLenum target, GLenum format, GLenum type, + int x, int y, int w, int h, uint8_t val, void **scratch) +{ + int bpp = glFmt2bpp(format, type); + int stride = w * bpp; + int size = h * stride; + if (size < 1) + return; + void *data = scratch ? *scratch : NULL; + if (talloc_get_size(data) < size) + data = talloc_realloc(NULL, data, char *, size); + memset(data, val, size); + glAdjustAlignment(gl, stride); + gl->PixelStorei(GL_UNPACK_ROW_LENGTH, w); + gl->TexSubImage2D(target, 0, x, y, w, h, format, type, data); + if (scratch) { + *scratch = data; + } else { + talloc_free(data); + } +} + /** * \brief download a texture, handling things like stride and slices * \param target texture target, usually GL_TEXTURE_2D diff --git a/libvo/gl_common.h b/libvo/gl_common.h index f42caa8fd1..fa4b6dbf25 100644 --- a/libvo/gl_common.h +++ b/libvo/gl_common.h @@ -60,6 +60,8 @@ int glCreatePPMTex(GL *gl, GLenum target, GLenum fmt, GLint filter, void glUploadTex(GL *gl, GLenum target, GLenum format, GLenum type, const void *dataptr, int stride, int x, int y, int w, int h, int slice); +void glClearTex(GL *gl, GLenum target, GLenum format, GLenum type, + int x, int y, int w, int h, uint8_t val, void **scratch); void glDownloadTex(GL *gl, GLenum target, GLenum format, GLenum type, void *dataptr, int stride); void glDrawTex(GL *gl, GLfloat x, GLfloat y, GLfloat w, GLfloat h, @@ -351,6 +353,7 @@ struct GL { void (GLAPIENTRY *Uniform1f)(GLint, GLfloat); void (GLAPIENTRY *Uniform3f)(GLint, GLfloat, GLfloat, GLfloat); + void (GLAPIENTRY *Uniform4f)(GLint, GLfloat, GLfloat, GLfloat, GLfloat); void (GLAPIENTRY *Uniform1i)(GLint, GLint); void (GLAPIENTRY *UniformMatrix3fv)(GLint, GLsizei, GLboolean, const GLfloat *); diff --git a/libvo/vo_gl3.c b/libvo/vo_gl3.c index ebce32677b..e3d3b9af62 100644 --- a/libvo/vo_gl3.c +++ b/libvo/vo_gl3.c @@ -37,6 +37,7 @@ #endif #include "talloc.h" +#include "mpcommon.h" #include "bstr.h" #include "mp_msg.h" #include "subopt-helper.h" @@ -46,7 +47,7 @@ #include "geometry.h" #include "osd.h" #include "sub/sub.h" -#include "eosd_packer.h" +#include "bitmap_packer.h" #include "gl_common.h" #include "filter_kernels.h" @@ -111,7 +112,7 @@ struct vertex { #define VERTEX_ATTRIB_TEXCOORD 2 // 2 triangles primitives per quad = 6 vertices per quad -// (GL_QUAD is deprecated, strips can't be used with EOSD image lists) +// (GL_QUAD is deprecated, strips can't be used with OSD image lists) #define VERTICES_PER_QUAD 6 struct texplane { @@ -141,6 +142,17 @@ struct fbotex { int vp_w, vp_h; // viewport of fbo / used part of the texture }; +struct osd_render { + enum sub_bitmap_format format; + int bitmap_id, bitmap_pos_id; + GLuint texture; + int width, height; + GLuint buffer; + int num_vertices; + struct vertex *vertices; + struct bitmap_packer *packer; +}; + struct gl_priv { struct vo *vo; MPGLContext *glctx; @@ -171,18 +183,15 @@ struct gl_priv { GLuint vertex_buffer; GLuint vao; - GLuint osd_program, eosd_program; + GLuint osd_programs[SUBBITMAP_COUNT]; GLuint indirect_program, scale_sep_program, final_program; + // old OSD code - should go away GLuint osd_textures[MAX_OSD_PARTS]; int osd_textures_count; struct vertex osd_va[MAX_OSD_PARTS * VERTICES_PER_QUAD]; - GLuint eosd_texture; - int eosd_texture_width, eosd_texture_height; - GLuint eosd_buffer; - struct vertex *eosd_va; - struct eosd_packer *eosd; + struct osd_render *osd[MAX_OSD_PARTS]; GLuint lut_3d_texture; int lut_3d_w, lut_3d_h, lut_3d_d; @@ -231,6 +240,8 @@ struct gl_priv { struct vo_rect dst_rect; // video rectangle on output window int border_x, border_y; // OSD borders int vp_x, vp_y, vp_w, vp_h; // GL viewport + + void *scratch; }; struct fmt_entry { @@ -254,6 +265,25 @@ static const struct fmt_entry mp_to_gl_formats[] = { {0}, }; +struct osd_fmt_entry { + GLint internal_format; + GLint format; + int stride; // bytes per pixel + GLenum type; + const char *shader; // shader entry in the .glsl file +}; + +static const struct osd_fmt_entry osd_to_gl_formats[] = { + [SUBBITMAP_LIBASS] + = {GL_RED, GL_RED, 1, GL_UNSIGNED_BYTE, "frag_osd_libass"}, + [SUBBITMAP_RGBA] + = {GL_RGBA, GL_BGRA, 4, GL_UNSIGNED_BYTE, "frag_osd_rgba"}, + [SUBBITMAP_OLD] + = {GL_RG, GL_RG, 2, GL_UNSIGNED_BYTE, "frag_osd_old"}, + // Make array long enough to contain all formats + [SUBBITMAP_COUNT] = {0} +}; + static const char help_text[]; @@ -464,6 +494,15 @@ static void update_uniforms(struct gl_priv *p, GLuint program) gl->Uniform1f(gl->GetUniformLocation(program, "filter_param1"), isnan(sparam1) ? 0.5f : sparam1); + loc = gl->GetUniformLocation(program, "osd_color"); + if (loc >= 0) { + int r = (p->osd_color >> 16) & 0xff; + int g = (p->osd_color >> 8) & 0xff; + int b = p->osd_color & 0xff; + int a = 0xff - (p->osd_color >> 24); + gl->Uniform4f(loc, r / 255.0f, g / 255.0f, b / 255.0f, a / 255.0f); + } + gl->UseProgram(0); debug_check_gl(p, "update_uniforms()"); @@ -471,8 +510,8 @@ static void update_uniforms(struct gl_priv *p, GLuint program) static void update_all_uniforms(struct gl_priv *p) { - update_uniforms(p, p->osd_program); - update_uniforms(p, p->eosd_program); + for (int n = 0; n < SUBBITMAP_COUNT; n++) + update_uniforms(p, p->osd_programs[n]); update_uniforms(p, p->indirect_program); update_uniforms(p, p->scale_sep_program); update_uniforms(p, p->final_program); @@ -636,20 +675,21 @@ static void compile_shaders(struct gl_priv *p) char *vertex_shader = get_section(tmp, src, "vertex_all"); char *shader_prelude = get_section(tmp, src, "prelude"); char *s_video = get_section(tmp, src, "frag_video"); - char *s_eosd = get_section(tmp, src, "frag_eosd"); - char *s_osd = get_section(tmp, src, "frag_osd"); char *header = talloc_asprintf(tmp, "#version %s\n%s", p->shader_version, shader_prelude); - char *header_eosd = talloc_strdup(tmp, header); - shader_def_opt(&header_eosd, "USE_3DLUT", p->use_lut_3d); + char *header_osd = talloc_strdup(tmp, header); + shader_def_opt(&header_osd, "USE_3DLUT", p->use_lut_3d); - p->eosd_program = - create_program(gl, "eosd", header_eosd, vertex_shader, s_eosd); - - p->osd_program = - create_program(gl, "osd", header, vertex_shader, s_osd); + for (int n = 0; n < SUBBITMAP_COUNT; n++) { + struct osd_fmt_entry fmt = osd_to_gl_formats[n]; + if (fmt.shader) { + char *s_osd = get_section(tmp, src, fmt.shader); + p->osd_programs[n] = + create_program(gl, fmt.shader, header_osd, vertex_shader, s_osd); + } + } char *header_conv = talloc_strdup(tmp, ""); char *header_final = talloc_strdup(tmp, ""); @@ -750,8 +790,8 @@ static void delete_shaders(struct gl_priv *p) { GL *gl = p->gl; - delete_program(gl, &p->osd_program); - delete_program(gl, &p->eosd_program); + for (int n = 0; n < SUBBITMAP_COUNT; n++) + delete_program(gl, &p->osd_programs[n]); delete_program(gl, &p->indirect_program); delete_program(gl, &p->scale_sep_program); delete_program(gl, &p->final_program); @@ -1449,7 +1489,7 @@ static void draw_osd(struct vo *vo, struct osd_state *osd) // OSD bitmaps use premultiplied alpha. gl->BlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); - gl->UseProgram(p->osd_program); + gl->UseProgram(p->osd_programs[SUBBITMAP_OLD]); for (int n = 0; n < p->osd_textures_count; n++) { gl->BindTexture(GL_TEXTURE_2D, p->osd_textures[n]); @@ -1464,80 +1504,111 @@ static void draw_osd(struct vo *vo, struct osd_state *osd) } } -static void gen_eosd(struct gl_priv *p, mp_eosd_images_t *imgs) +static void gen_eosd(struct gl_priv *p, struct osd_render *osd, + struct sub_bitmaps *imgs) { GL *gl = p->gl; - bool need_repos, need_upload, need_allocate; - eosd_packer_generate(p->eosd, imgs, &need_repos, &need_upload, - &need_allocate); + if (imgs->bitmap_pos_id == osd->bitmap_pos_id) + return; + + osd->num_vertices = 0; - if (!need_repos) + if (imgs->format == SUBBITMAP_EMPTY) return; - if (!p->eosd_texture) { - gl->GenTextures(1, &p->eosd_texture); - gl->GenBuffers(1, &p->eosd_buffer); + bool need_upload = imgs->bitmap_id != osd->bitmap_id; + bool need_allocate = false; + + if (imgs->format != osd->format) { + packer_reset(osd->packer); + osd->format = imgs->format; + need_allocate = true; } - gl->BindTexture(GL_TEXTURE_2D, p->eosd_texture); + osd->bitmap_id = imgs->bitmap_id; + osd->bitmap_pos_id = imgs->bitmap_pos_id; + + osd->packer->padding = imgs->scaled; // assume 2x2 filter on scaling + int r = packer_pack_from_subbitmaps(osd->packer, imgs); + if (r < 0) { + mp_msg(MSGT_VO, MSGL_ERR, "[gl] EOSD bitmaps do not fit on " + "a surface with the maximum supported size %dx%d.\n", + osd->packer->w_max, osd->packer->h_max); + return; + } else if (r > 0) { + need_allocate = true; + } + + struct osd_fmt_entry fmt = osd_to_gl_formats[imgs->format]; + assert(fmt.shader); + + if (!osd->texture) { + gl->GenTextures(1, &osd->texture); + gl->GenBuffers(1, &osd->buffer); + } + + gl->BindTexture(GL_TEXTURE_2D, osd->texture); if (need_allocate) { - tex_size(p, p->eosd->surface.w, p->eosd->surface.h, - &p->eosd_texture_width, &p->eosd_texture_height); - gl->TexImage2D(GL_TEXTURE_2D, 0, GL_RED, - p->eosd_texture_width, p->eosd_texture_height, 0, - GL_RED, GL_UNSIGNED_BYTE, NULL); - default_tex_params(gl, GL_TEXTURE_2D, GL_NEAREST); - gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, p->eosd_buffer); + tex_size(p, osd->packer->w, osd->packer->h, &osd->width, &osd->height); + gl->TexImage2D(GL_TEXTURE_2D, 0, fmt.internal_format, osd->width, + osd->height, 0, fmt.format, fmt.type, NULL); + default_tex_params(gl, GL_TEXTURE_2D, GL_LINEAR); + gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, osd->buffer); gl->BufferData(GL_PIXEL_UNPACK_BUFFER, - p->eosd->surface.w * p->eosd->surface.h, - NULL, - GL_DYNAMIC_COPY); + osd->width * osd->height * fmt.stride, + NULL, GL_DYNAMIC_COPY); gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); } - p->eosd_va = talloc_realloc_size(p->eosd, p->eosd_va, - p->eosd->targets_count - * sizeof(struct vertex) - * VERTICES_PER_QUAD); + struct pos bb[2]; + packer_get_bb(osd->packer, bb); if (need_upload && p->use_pbo) { - gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, p->eosd_buffer); + gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, osd->buffer); char *data = gl->MapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY); + size_t stride = osd->width * fmt.stride; if (!data) { mp_msg(MSGT_VO, MSGL_FATAL, "[gl] Error: can't upload subtitles! " - "Subtitles will look corrupted.\n"); + "Remove the 'pbo' suboption.\n"); } else { - for (int n = 0; n < p->eosd->targets_count; n++) { - struct eosd_target *target = &p->eosd->targets[n]; - ASS_Image *i = target->ass_img; - - void *pdata = data + target->source.y0 * p->eosd->surface.w - + target->source.x0; + if (imgs->scaled) { + int w = bb[1].x - bb[0].x; + int h = bb[1].y - bb[0].y; + memset_pic(data, 0, w * fmt.stride, h, stride); + } + for (int n = 0; n < osd->packer->count; n++) { + struct sub_bitmap *b = &imgs->parts[n]; + struct pos p = osd->packer->result[n]; - memcpy_pic(pdata, i->bitmap, i->w, i->h, - p->eosd->surface.w, i->stride); + void *pdata = data + p.y * stride + p.x * fmt.stride; + memcpy_pic(pdata, b->bitmap, b->w * fmt.stride, b->h, + stride, b->stride); } if (!gl->UnmapBuffer(GL_PIXEL_UNPACK_BUFFER)) mp_msg(MSGT_VO, MSGL_FATAL, "[gl] EOSD PBO upload failed. " "Remove the 'pbo' suboption.\n"); - struct eosd_rect rc; - eosd_packer_calculate_source_bb(p->eosd, &rc); - glUploadTex(gl, GL_TEXTURE_2D, GL_RED, GL_UNSIGNED_BYTE, NULL, - p->eosd->surface.w, rc.x0, rc.y0, - rc.x1 - rc.x0, rc.y1 - rc.y0, 0); + glUploadTex(gl, GL_TEXTURE_2D, fmt.format, fmt.type, NULL, stride, + bb[0].x, bb[0].y, bb[1].x - bb[0].x, bb[1].y - bb[0].y, + 0); + need_upload = false; } gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); - } else if (need_upload) { + } + if (need_upload) { // non-PBO upload - for (int n = 0; n < p->eosd->targets_count; n++) { - struct eosd_target *target = &p->eosd->targets[n]; - ASS_Image *i = target->ass_img; + if (imgs->scaled) { + glClearTex(gl, GL_TEXTURE_2D, fmt.format, fmt.type, + bb[0].x, bb[0].y, bb[1].x - bb[0].y, bb[1].y - bb[0].y, + 0, &p->scratch); + } + for (int n = 0; n < osd->packer->count; n++) { + struct sub_bitmap *b = &imgs->parts[n]; + struct pos p = osd->packer->result[n]; - glUploadTex(gl, GL_TEXTURE_2D, GL_RED, GL_UNSIGNED_BYTE, i->bitmap, - i->stride, target->source.x0, target->source.y0, - i->w, i->h, 0); + glUploadTex(gl, GL_TEXTURE_2D, fmt.format, fmt.type, + b->bitmap, b->stride, p.x, p.y, b->w, b->h, 0); } } @@ -1545,36 +1616,51 @@ static void gen_eosd(struct gl_priv *p, mp_eosd_images_t *imgs) debug_check_gl(p, "EOSD upload"); - for (int n = 0; n < p->eosd->targets_count; n++) { - struct eosd_target *target = &p->eosd->targets[n]; - ASS_Image *i = target->ass_img; - uint8_t color[4] = { i->color >> 24, (i->color >> 16) & 0xff, - (i->color >> 8) & 0xff, 255 - (i->color & 0xff) }; + osd->vertices = talloc_realloc_size(osd, osd->vertices, + osd->packer->count + * sizeof(struct vertex) + * VERTICES_PER_QUAD); + + for (int n = 0; n < osd->packer->count; n++) { + struct sub_bitmap *b = &imgs->parts[n]; + struct pos p = osd->packer->result[n]; - write_quad(&p->eosd_va[n * VERTICES_PER_QUAD], - target->dest.x0, target->dest.y0, - target->dest.x1, target->dest.y1, - target->source.x0, target->source.y0, - target->source.x1, target->source.y1, - p->eosd_texture_width, p->eosd_texture_height, - color, false); + // NOTE: the blend color is used with SUBBITMAP_LIBASS only, so it + // doesn't matter that we upload garbage for the other formats + uint32_t c = b->libass.color; + uint8_t color[4] = { c >> 24, (c >> 16) & 0xff, + (c >> 8) & 0xff, 255 - (c & 0xff) }; + + write_quad(&osd->vertices[osd->num_vertices], + b->x, b->y, b->x + b->dw, b->y + b->dh, + p.x, p.y, p.x + b->w, p.y + b->h, + osd->width, osd->height, color, false); + osd->num_vertices += VERTICES_PER_QUAD; } } -static void draw_eosd(struct gl_priv *p, mp_eosd_images_t *imgs) +static void draw_eosd(struct gl_priv *p, struct sub_bitmaps *imgs) { GL *gl = p->gl; - gen_eosd(p, imgs); + struct osd_render *osd = p->osd[imgs->render_index]; + + gen_eosd(p, osd, imgs); - if (p->eosd->targets_count == 0) + if (osd->num_vertices == 0) return; + assert(osd->format != SUBBITMAP_EMPTY); + gl->Enable(GL_BLEND); - gl->BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - gl->BindTexture(GL_TEXTURE_2D, p->eosd_texture); - gl->UseProgram(p->eosd_program); - draw_triangles(p, p->eosd_va, p->eosd->targets_count * VERTICES_PER_QUAD); + if (osd->format == SUBBITMAP_OLD) { + gl->BlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + } else { + gl->BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + } + gl->BindTexture(GL_TEXTURE_2D, osd->texture); + gl->UseProgram(p->osd_programs[osd->format]); + draw_triangles(p, osd->vertices, osd->num_vertices); gl->UseProgram(0); gl->BindTexture(GL_TEXTURE_2D, 0); gl->Disable(GL_BLEND); @@ -1641,7 +1727,18 @@ static int init_gl(struct gl_priv *p) GLint max_texture_size; gl->GetIntegerv(GL_MAX_TEXTURE_SIZE, &max_texture_size); - eosd_packer_reinit(p->eosd, max_texture_size, max_texture_size); + + for (int n = 0; n < MAX_OSD_PARTS; n++) { + assert(!p->osd[n]); + struct osd_render *osd = talloc_ptrtype(p, osd); + *osd = (struct osd_render) { + .packer = talloc_struct(osd, struct bitmap_packer, { + .w_max = max_texture_size, + .h_max = max_texture_size, + }), + }; + p->osd[n] = osd; + } gl->ClearColor(0.0f, 0.0f, 0.0f, 0.0f); gl->Clear(GL_COLOR_BUFFER_BIT); @@ -1667,11 +1764,15 @@ static void uninit_gl(struct gl_priv *p) p->vertex_buffer = 0; clear_osd(p); - gl->DeleteTextures(1, &p->eosd_texture); - p->eosd_texture = 0; - gl->DeleteBuffers(1, &p->eosd_buffer); - p->eosd_buffer = 0; - eosd_packer_reinit(p->eosd, 0, 0); + for (int n = 0; n < MAX_OSD_PARTS; n++) { + struct osd_render *osd = p->osd[n]; + if (!osd) + continue; + gl->DeleteTextures(1, &osd->texture); + gl->DeleteBuffers(1, &osd->buffer); + talloc_free(osd); + p->osd[n] = NULL; + } gl->DeleteTextures(1, &p->lut_3d_texture); p->lut_3d_texture = 0; @@ -1753,7 +1854,7 @@ static int query_format(uint32_t format) { int caps = VFCAP_CSP_SUPPORTED | VFCAP_CSP_SUPPORTED_BY_HW | VFCAP_FLIP | VFCAP_HWSCALE_UP | VFCAP_HWSCALE_DOWN | VFCAP_ACCEPT_STRIDE | - VFCAP_OSD | VFCAP_EOSD | VFCAP_EOSD_UNSCALED; + VFCAP_OSD | VFCAP_EOSD | VFCAP_EOSD_UNSCALED | VFCAP_EOSD_RGBA; if (!init_format(format, NULL)) return 0; return caps; @@ -2244,6 +2345,7 @@ static int preinit(struct vo *vo, const char *arg) { .index = 1, .name = "bilinear" }, }, .scaler_params = {NAN, NAN}, + .scratch = talloc_zero_array(p, char *, 1), }; p->defaults = talloc(p, struct gl_priv); @@ -2323,8 +2425,6 @@ static int preinit(struct vo *vo, const char *arg) p->orig_cmdline = talloc(p, struct gl_priv); *p->orig_cmdline = *p; - p->eosd = eosd_packer_create(vo); - p->glctx = init_mpglcontext(backend, vo); if (!p->glctx) goto err_out; diff --git a/libvo/vo_gl3_shaders.glsl b/libvo/vo_gl3_shaders.glsl index f67e55e6f5..20f8c597ad 100644 --- a/libvo/vo_gl3_shaders.glsl +++ b/libvo/vo_gl3_shaders.glsl @@ -45,7 +45,7 @@ void main() { texcoord = vertex_texcoord; } -#!section frag_eosd +#!section frag_osd_libass uniform sampler2D texture1; in vec2 texcoord; @@ -56,15 +56,25 @@ void main() { out_color = vec4(color.rgb, color.a * texture(texture1, texcoord).r); } -#!section frag_osd +#!section frag_osd_rgba uniform sampler2D texture1; in vec2 texcoord; -in vec4 color; out vec4 out_color; void main() { - out_color = texture(texture1, texcoord).rrrg * color; + out_color = texture(texture1, texcoord); +} + +#!section frag_osd_old +uniform sampler2D texture1; +uniform vec4 osd_color; + +in vec2 texcoord; +out vec4 out_color; + +void main() { + out_color = texture(texture1, texcoord).rrrg * osd_color; } #!section frag_video -- 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(+) 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 3099498154a06c6df0c365de2cc0af09686cd6e1 Mon Sep 17 00:00:00 2001 From: wm4 Date: Fri, 28 Sep 2012 21:36:36 +0200 Subject: vo_gl3: use old OSD using the emulation layer This still renders the OSD using essentially the same mechanisms, except that the EOSD code for texture handling and rendering is reused. --- libvo/vo_gl3.c | 98 +--------------------------------------------------------- 1 file changed, 1 insertion(+), 97 deletions(-) diff --git a/libvo/vo_gl3.c b/libvo/vo_gl3.c index e3d3b9af62..08f1514094 100644 --- a/libvo/vo_gl3.c +++ b/libvo/vo_gl3.c @@ -186,11 +186,6 @@ struct gl_priv { GLuint osd_programs[SUBBITMAP_COUNT]; GLuint indirect_program, scale_sep_program, final_program; - // old OSD code - should go away - GLuint osd_textures[MAX_OSD_PARTS]; - int osd_textures_count; - struct vertex osd_va[MAX_OSD_PARTS * VERTICES_PER_QUAD]; - struct osd_render *osd[MAX_OSD_PARTS]; GLuint lut_3d_texture; @@ -1414,96 +1409,6 @@ static mp_image_t *get_window_screenshot(struct gl_priv *p) return image; } -static void clear_osd(struct gl_priv *p) -{ - GL *gl = p->gl; - - if (!p->osd_textures_count) - return; - gl->DeleteTextures(p->osd_textures_count, p->osd_textures); - p->osd_textures_count = 0; -} - -static void create_osd_texture(void *ctx, int x0, int y0, int w, int h, - unsigned char *src, unsigned char *srca, - int stride) -{ - struct gl_priv *p = ctx; - GL *gl = p->gl; - - if (w <= 0 || h <= 0 || stride < w) { - mp_msg(MSGT_VO, MSGL_V, "Invalid dimensions OSD for part!\n"); - return; - } - - if (p->osd_textures_count >= MAX_OSD_PARTS) { - mp_msg(MSGT_VO, MSGL_ERR, "Too many OSD parts, contact the developers!\n"); - return; - } - - int sx, sy; - tex_size(p, w, h, &sx, &sy); - - gl->GenTextures(1, &p->osd_textures[p->osd_textures_count]); - gl->BindTexture(GL_TEXTURE_2D, p->osd_textures[p->osd_textures_count]); - gl->TexImage2D(GL_TEXTURE_2D, 0, GL_RG, sx, sy, 0, GL_RG, GL_UNSIGNED_BYTE, - NULL); - default_tex_params(gl, GL_TEXTURE_2D, GL_NEAREST); - unsigned char *tmp = malloc(stride * h * 2); - // Convert alpha from weird MPlayer scale. - for (int i = 0; i < h * stride; i++) { - tmp[i*2+0] = src[i]; - tmp[i*2+1] = -srca[i]; - } - glUploadTex(gl, GL_TEXTURE_2D, GL_RG, GL_UNSIGNED_BYTE, tmp, stride * 2, - 0, 0, w, h, 0); - free(tmp); - - gl->BindTexture(GL_TEXTURE_2D, 0); - - uint8_t color[4] = {(p->osd_color >> 16) & 0xff, (p->osd_color >> 8) & 0xff, - p->osd_color & 0xff, 0xff - (p->osd_color >> 24)}; - - write_quad(&p->osd_va[p->osd_textures_count * VERTICES_PER_QUAD], - x0, y0, x0 + w, y0 + h, 0, 0, w, h, - sx, sy, color, false); - - p->osd_textures_count++; -} - -static void draw_osd(struct vo *vo, struct osd_state *osd) -{ - struct gl_priv *p = vo->priv; - GL *gl = p->gl; - - if (vo_osd_has_changed(osd)) { - clear_osd(p); - osd_draw_text_ext(osd, vo->dwidth, vo->dheight, p->border_x, - p->border_y, p->border_x, - p->border_y, p->image_width, - p->image_height, create_osd_texture, p); - } - - if (p->osd_textures_count > 0) { - gl->Enable(GL_BLEND); - // OSD bitmaps use premultiplied alpha. - gl->BlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); - - gl->UseProgram(p->osd_programs[SUBBITMAP_OLD]); - - for (int n = 0; n < p->osd_textures_count; n++) { - gl->BindTexture(GL_TEXTURE_2D, p->osd_textures[n]); - draw_triangles(p, &p->osd_va[n * VERTICES_PER_QUAD], - VERTICES_PER_QUAD); - } - - gl->UseProgram(0); - - gl->Disable(GL_BLEND); - gl->BindTexture(GL_TEXTURE_2D, 0); - } -} - static void gen_eosd(struct gl_priv *p, struct osd_render *osd, struct sub_bitmaps *imgs) { @@ -1763,7 +1668,6 @@ static void uninit_gl(struct gl_priv *p) gl->DeleteBuffers(1, &p->vertex_buffer); p->vertex_buffer = 0; - clear_osd(p); for (int n = 0; n < MAX_OSD_PARTS; n++) { struct osd_render *osd = p->osd[n]; if (!osd) @@ -2462,7 +2366,7 @@ const struct vo_driver video_out_gl3 = { .config = config, .control = control, .draw_slice = draw_slice, - .draw_osd = draw_osd, + .draw_osd = emulate_draw_osd, .flip_page = flip_page, .check_events = check_events, .uninit = uninit, -- 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.) --- Makefile | 1 + libvo/video_out.h | 1 + libvo/vo_gl3.c | 6 +- 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 +++++----- 15 files changed, 600 insertions(+), 523 deletions(-) create mode 100644 sub/img_convert.c create mode 100644 sub/img_convert.h diff --git a/Makefile b/Makefile index c93fa6b31c..27aa1997dd 100644 --- a/Makefile +++ b/Makefile @@ -266,6 +266,7 @@ SRCS_COMMON = asxparser.c \ sub/sd_lavc.c \ sub/spudec.c \ sub/sub.c \ + sub/img_convert.c \ sub/subassconvert.c \ sub/subreader.c \ sub/vobsub.c \ diff --git a/libvo/video_out.h b/libvo/video_out.h index 2cd314f281..cdda6ec79f 100644 --- a/libvo/video_out.h +++ b/libvo/video_out.h @@ -66,6 +66,7 @@ enum mp_voctrl { VOCTRL_BORDER, VOCTRL_DRAW_EOSD, VOCTRL_GET_EOSD_RES, // struct mp_eosd_res + VOCTRL_QUERY_EOSD_FORMAT, // int VOCTRL_SET_DEINTERLACE, VOCTRL_GET_DEINTERLACE, diff --git a/libvo/vo_gl3.c b/libvo/vo_gl3.c index 08f1514094..2af63700b4 100644 --- a/libvo/vo_gl3.c +++ b/libvo/vo_gl3.c @@ -1851,6 +1851,10 @@ static int control(struct vo *vo, uint32_t request, void *data) r->mt = r->mb = p->border_y; return VO_TRUE; } + case VOCTRL_QUERY_EOSD_FORMAT: { + int *p = data; + return osd_to_gl_formats[*p].shader ? VO_TRUE : VO_NOTIMPL; + } case VOCTRL_ONTOP: if (!p->glctx->ontop) break; @@ -2366,7 +2370,7 @@ const struct vo_driver video_out_gl3 = { .config = config, .control = control, .draw_slice = draw_slice, - .draw_osd = emulate_draw_osd, + .draw_osd = draw_osd_with_eosd, .flip_page = flip_page, .check_events = check_events, .uninit = uninit, 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_o