diff options
-rw-r--r-- | Makefile | 1 | ||||
-rw-r--r-- | libvo/eosd_packer.c | 254 | ||||
-rw-r--r-- | libvo/eosd_packer.h | 71 | ||||
-rw-r--r-- | libvo/gl_common.c | 6 | ||||
-rw-r--r-- | libvo/gl_common.h | 7 | ||||
-rw-r--r-- | libvo/vo_gl.c | 256 | ||||
-rw-r--r-- | libvo/vo_vdpau.c | 255 |
7 files changed, 504 insertions, 346 deletions
@@ -403,6 +403,7 @@ SRCS_COMMON = asxparser.c \ libmpdemux/yuv4mpeg.c \ libmpdemux/yuv4mpeg_ratio.c \ libvo/osd.c \ + libvo/eosd_packer.c \ osdep/numcores.c \ osdep/$(GETCH) \ osdep/$(TIMER) \ diff --git a/libvo/eosd_packer.c b/libvo/eosd_packer.c new file mode 100644 index 0000000000..103648b7c4 --- /dev/null +++ b/libvo/eosd_packer.c @@ -0,0 +1,254 @@ +/* + * Common code for packing EOSD images into larger surfaces. + * + * This file is part of mplayer2. + * + * mplayer2 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. + * + * mplayer2 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 mplayer2; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <libavutil/common.h> +#include "talloc.h" +#include "mp_msg.h" +#include "eosd_packer.h" + +// Initial size of EOSD surface in pixels (x*x) +#define EOSD_SURFACE_INITIAL_SIZE 256 + +// Allocate an eosd_packer, which can be used to layout and cache the list of +// EOSD images contained in a mp_eosd_images_t into a flat surface. +// It can be free'd with talloc_free(). +// Don't forget to call eosd_init() before using it. +struct eosd_packer *eosd_packer_create(void *talloc_ctx) { + return talloc_zero(talloc_ctx, struct eosd_packer); +} + +// Call this when you need to completely reinitialize the EOSD state, e.g. when +// when your EOSD surface was deleted. +// max_width and max_height are the maximum surface sizes that should be +// allowed. +void eosd_packer_reinit(struct eosd_packer *state, uint32_t max_width, + uint32_t max_height) +{ + state->max_surface_width = max_width; + state->max_surface_height = max_height; + state->surface.w = 0; + state->surface.h = 0; + state->targets_count = 0; +} + +#define HEIGHT_SORT_BITS 4 +static int size_index(struct eosd_target *r) +{ + unsigned int h = r->source.y1; + int n = av_log2_16bit(h); + return (n << HEIGHT_SORT_BITS) + + (- 1 - (h << HEIGHT_SORT_BITS >> n) & (1 << HEIGHT_SORT_BITS) - 1); +} + +/* Pack the given rectangles into an area of size w * h. + * The size of each rectangle is read from .source.x1/.source.y1. + * The height of each rectangle must be at least 1 and less than 65536. + * The .source rectangle is then set corresponding to the packed position. + * 'scratch' must point to work memory for num_rects+16 ints. + * Return 0 on success, -1 if the rectangles did not fit in w*h. + * + * The rectangles are placed in rows in order approximately sorted by + * height (the approximate sorting is simpler than a full one would be, + * and allows the algorithm to work in linear time). Additionally, to + * reduce wasted space when there are a few tall rectangles, empty + * lower-right parts of rows are filled recursively when the size of + * rectangles in the row drops past a power-of-two threshold. So if a + * row starts with rectangles of size 3x50, 10x40 and 5x20 then the + * free rectangle with corners (13, 20)-(w, 50) is filled recursively. + */ +static int pack_rectangles(struct eosd_target *rects, int num_rects, + int w, int h, int *scratch) +{ + int bins[16 << HEIGHT_SORT_BITS]; + int sizes[16 << HEIGHT_SORT_BITS] = {}; + for (int i = 0; i < num_rects; i++) + sizes[size_index(rects + i)]++; + int idx = 0; + for (int i = 0; i < 16 << HEIGHT_SORT_BITS; i += 1 << HEIGHT_SORT_BITS) { + for (int j = 0; j < 1 << HEIGHT_SORT_BITS; j++) { + bins[i + j] = idx; + idx += sizes[i + j]; + } + scratch[idx++] = -1; + } + for (int i = 0; i < num_rects; i++) + scratch[bins[size_index(rects + i)]++] = i; + for (int i = 0; i < 16; i++) + bins[i] = bins[i << HEIGHT_SORT_BITS] - sizes[i << HEIGHT_SORT_BITS]; + struct { + int size, x, bottom; + } stack[16] = {{15, 0, h}}, s = {}; + int stackpos = 1; + int y; + while (stackpos) { + y = s.bottom; + s = stack[--stackpos]; + s.size++; + while (s.size--) { + int maxy = -1; + int obj; + while ((obj = scratch[bins[s.size]]) >= 0) { + int bottom = y + rects[obj].source.y1; + if (bottom > s.bottom) + break; + int right = s.x + rects[obj].source.x1; + if (right > w) + break; + bins[s.size]++; + rects[obj].source.x0 = s.x; + rects[obj].source.x1 += s.x; + rects[obj].source.y0 = y; + rects[obj].source.y1 += y; + num_rects--; + if (maxy <= 0) + stack[stackpos++] = s; + s.x = right; + maxy = FFMAX(maxy, bottom); + } + if (maxy > 0) + s.bottom = maxy; + } + } + return num_rects ? -1 : 0; +} + +// padding to reduce interpolation artifacts when doing scaling & filtering +#define EOSD_PADDING 0 + +// Release all previous images, and packs the images in imgs into state. The +// caller must check the change variables: +// *out_need_reposition == true: sub-image positions changed +// *out_need_upload == true: upload all sub-images again +// *out_need_reallocate == true: resize the EOSD texture to state->surface.w/h +// Logical implications: need_reallocate => need_upload => need_reposition +void eosd_packer_generate(struct eosd_packer *state, mp_eosd_images_t *imgs, + bool *out_need_reposition, bool *out_need_upload, + bool *out_need_reallocate) +{ + int i; + ASS_Image *img = imgs->imgs; + ASS_Image *p; + struct eosd_surface *sfc = &state->surface; + + *out_need_reposition = false; + *out_need_upload = false; + *out_need_reallocate = false; + + int change_state = imgs->changed; + + // eosd_reinit() was probably called, force full reupload. + if (state->targets_count == 0 && img) + change_state = 2; + + if (change_state == 0) + return; // Nothing changed, no need to redraw + + state->targets_count = 0; + + *out_need_reposition = true; + + if (!img) + return; // There's nothing to render! + + if (change_state == 1) + goto eosd_skip_upload; + + *out_need_upload = true; + while (1) { + for (p = img, i = 0; p; p = p->next) { + if (p->w <= 0 || p->h <= 0) + continue; + // Allocate new space for surface/target arrays + if (i >= state->targets_size) { + state->targets_size = FFMAX(state->targets_size * 2, 512); + state->targets = + talloc_realloc_size(state, state->targets, + state->targets_size + * sizeof(*state->targets)); + state->scratch = + talloc_realloc_size(state, state->scratch, + (state->targets_size + 16) + * sizeof(*state->scratch)); + } + state->targets[i].source.x1 = p->w + EOSD_PADDING; + state->targets[i].source.y1 = p->h + EOSD_PADDING; + i++; + } + if (pack_rectangles(state->targets, i, sfc->w, sfc->h, + state->scratch) >= 0) + break; + int w = FFMIN(FFMAX(sfc->w * 2, EOSD_SURFACE_INITIAL_SIZE), + state->max_surface_width); + int h = FFMIN(FFMAX(sfc->h * 2, EOSD_SURFACE_INITIAL_SIZE), + state->max_surface_height); + if (w == sfc->w && h == sfc->h) { + mp_msg(MSGT_VO, MSGL_ERR, "[eosd] EOSD bitmaps do not fit on " + "a surface with the maximum supported size\n"); + return; + } + sfc->w = w; + sfc->h = h; + *out_need_reallocate = true; + } + if (*out_need_reallocate) { + mp_msg(MSGT_VO, MSGL_V, "[eosd] Allocate a %dx%d surface for " + "EOSD bitmaps.\n", sfc->w, sfc->h); + } + +eosd_skip_upload: + for (p = img; p; p = p->next) { + if (p->w <= 0 || p->h <= 0) + continue; + struct eosd_target *target = &state->targets[state->targets_count]; + target->source.x1 -= EOSD_PADDING; + target->source.y1 -= EOSD_PADDING; + 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; + target->color = p->color; + target->ass_img = p; + state->targets_count++; + } +} + +// Calculate the bounding box of all sub-rectangles in the EOSD surface that +// will be used for EOSD rendering. +// If the bounding box is empty, return false. +bool eosd_packer_calculate_source_bb(struct eosd_packer *state, + struct eosd_rect *out_bb) +{ + struct eosd_rect bb = { state->surface.w, state->surface.h, 0, 0 }; + + for (int n = 0; n < state->targets_count; n++) { + struct eosd_rect s = state->targets[n].source; + bb.x0 = FFMIN(bb.x0, s.x0); + bb.y0 = FFMIN(bb.y0, s.y0); + bb.x1 = FFMAX(bb.x1, s.x1); + bb.y1 = FFMAX(bb.y1, s.y1); + } + + // avoid degenerate bounding box if empty + bb.x0 = FFMIN(bb.x0, bb.x1); + bb.y0 = FFMIN(bb.y0, bb.y1); + + *out_bb = bb; + return state->targets_count > 0; +} diff --git a/libvo/eosd_packer.h b/libvo/eosd_packer.h new file mode 100644 index 0000000000..e207a4a2dd --- /dev/null +++ b/libvo/eosd_packer.h @@ -0,0 +1,71 @@ +/* + * This file is part of mplayer2. + * + * mplayer2 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. + * + * mplayer2 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 mplayer2; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPLAYER_EOSD_PACKER_H +#define MPLAYER_EOSD_PACKER_H + +#include <inttypes.h> +#include <stdbool.h> + +#include "sub/ass_mp.h" + +// Pool of surfaces +struct eosd_surface { + //void *native_surface; + int w; + int h; +}; + +struct eosd_rect { + int x0, y0, x1, y1; +}; + +// List of surfaces to be rendered +struct eosd_target { + struct eosd_rect source; // position in EOSD surface + struct eosd_rect dest; // position on screen + uint32_t color; // libass-style color of the image + // NOTE: This must not be accessed after you return from your VO's + // VOCTRL_DRAW_EOSD call - libass will free or reuse the associated + // memory. Feel free to set this to NULL to make erroneous accesses to + // this member fail early. + ASS_Image *ass_img; +}; + +struct eosd_packer { + struct eosd_surface surface; + struct eosd_target *targets; + int targets_count; // number of valid elements in targets + int targets_size; // number of allocated elements in targets + + uint32_t max_surface_width; + uint32_t max_surface_height; + + int *scratch; +}; + +struct eosd_packer *eosd_packer_create(void *talloc_ctx); +void eosd_packer_reinit(struct eosd_packer *state, uint32_t max_width, + uint32_t max_height); +void eosd_packer_generate(struct eosd_packer *state, mp_eosd_images_t *imgs, + bool *out_need_reposition, bool *out_need_upload, + bool *out_need_reallocate); +bool eosd_packer_calculate_source_bb(struct eosd_packer *state, + struct eosd_rect *out_bb); + +#endif /* MPLAYER_EOSD_PACKER_H */ diff --git a/libvo/gl_common.c b/libvo/gl_common.c index 7cca800d40..aa34521802 100644 --- a/libvo/gl_common.c +++ b/libvo/gl_common.c @@ -360,6 +360,12 @@ static const extfunc_desc_t extfuncs[] = { DEF_FUNC_DESC(ColorMask), DEF_FUNC_DESC(ReadPixels), DEF_FUNC_DESC(ReadBuffer), + DEF_FUNC_DESC(VertexPointer), + DEF_FUNC_DESC(ColorPointer), + DEF_FUNC_DESC(TexCoordPointer), + DEF_FUNC_DESC(DrawArrays), + DEF_FUNC_DESC(EnableClientState), + DEF_FUNC_DESC(DisableClientState), DEF_EXT_DESC(GenBuffers, NULL, ("glGenBuffers", "glGenBuffersARB")), diff --git a/libvo/gl_common.h b/libvo/gl_common.h index 8e3294f441..5a0fd9b8a8 100644 --- a/libvo/gl_common.h +++ b/libvo/gl_common.h @@ -463,6 +463,13 @@ struct GL { void (GLAPIENTRY *ReadPixels)(GLint, GLint, GLsizei, GLsizei, GLenum, GLenum, GLvoid *); void (GLAPIENTRY *ReadBuffer)(GLenum); + void (GLAPIENTRY *VertexPointer)(GLint, GLenum, GLsizei, const GLvoid *); + void (GLAPIENTRY *ColorPointer)(GLint, GLenum, GLsizei, const GLvoid *); + void (GLAPIENTRY *TexCoordPointer)(GLint, GLenum, GLsizei, const GLvoid *); + void (GLAPIENTRY *DrawArrays)(GLenum, GLint, GLsizei); + void (GLAPIENTRY *EnableClientState)(GLenum); + void (GLAPIENTRY *DisableClientState)(GLenum); + // OpenGL extension functions void (GLAPIENTRY *GenBuffers)(GLsizei, GLuint *); diff --git a/libvo/vo_gl.c b/libvo/vo_gl.c index e68465f009..7bf5cf2ec1 100644 --- a/libvo/vo_gl.c +++ b/libvo/vo_gl.c @@ -38,6 +38,7 @@ #include "osd.h" #include "sub/font_load.h" #include "sub/sub.h" +#include "eosd_packer.h" #include "gl_common.h" #include "aspect.h" @@ -49,19 +50,17 @@ static int preinit_nosw(struct vo *vo, const char *arg); //! How many parts the OSD may consist of at most #define MAX_OSD_PARTS 20 -#define LARGE_EOSD_TEX_SIZE 512 -#define TINYTEX_SIZE 16 -#define TINYTEX_COLS (LARGE_EOSD_TEX_SIZE / TINYTEX_SIZE) -#define TINYTEX_MAX (TINYTEX_COLS * TINYTEX_COLS) -#define SMALLTEX_SIZE 32 -#define SMALLTEX_COLS (LARGE_EOSD_TEX_SIZE / SMALLTEX_SIZE) -#define SMALLTEX_MAX (SMALLTEX_COLS * SMALLTEX_COLS) - //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))) #define MASK_GAMMA_SUPPORT (MASK_NOT_COMBINERS & ~(1 << YUV_CONVERSION_FRAGMENT)) +struct vertex_eosd { + float x, y; + uint8_t color[4]; + float u, v; +}; + struct gl_priv { MPGLContext *glctx; GL *gl; @@ -74,17 +73,17 @@ struct gl_priv { //! Alpha textures for OSD GLuint osdatex[MAX_OSD_PARTS]; #endif - GLuint *eosdtex; - GLuint largeeosdtex[2]; + GLuint eosd_texture; + int eosd_texture_width, eosd_texture_height; + struct eosd_packer *eosd; + struct vertex_eosd *eosd_va; //! Display lists that draw the OSD parts GLuint osdDispList[MAX_OSD_PARTS]; #ifndef FAST_OSD GLuint osdaDispList[MAX_OSD_PARTS]; #endif - GLuint eosdDispList; //! How many parts the OSD currently consists of int osdtexCnt; - int eosdtexCnt; int osd_color; int use_ycbcr; @@ -285,150 +284,111 @@ static void clearOSD(struct vo *vo) } /** - * \brief remove textures, display list and free memory used by EOSD + * \brief construct display list from ass image list + * \param img image list to create OSD from. */ -static void clearEOSD(struct vo *vo) +static void genEOSD(struct vo *vo, mp_eosd_images_t *imgs) { struct gl_priv *p = vo->priv; GL *gl = p->gl; - if (p->eosdDispList) - gl->DeleteLists(p->eosdDispList, 1); - p->eosdDispList = 0; - if (p->eosdtexCnt) - gl->DeleteTextures(p->eosdtexCnt, p->eosdtex); - p->eosdtexCnt = 0; - free(p->eosdtex); - p->eosdtex = NULL; -} + bool need_repos, need_upload, need_allocate; + eosd_packer_generate(p->eosd, imgs, &need_repos, &need_upload, + &need_allocate); -static inline int is_tinytex(ASS_Image *i, int tinytexcur) -{ - return i->w < TINYTEX_SIZE && i->h < TINYTEX_SIZE - && tinytexcur < TINYTEX_MAX; -} + if (!need_repos) + return; -static inline int is_smalltex(ASS_Image *i, int smalltexcur) -{ - return i->w < SMALLTEX_SIZE && i->h < SMALLTEX_SIZE - && smalltexcur < SMALLTEX_MAX; -} + if (!p->eosd_texture) + gl->GenTextures(1, &p->eosd_texture); -static inline void tinytex_pos(int tinytexcur, int *x, int *y) -{ - *x = (tinytexcur % TINYTEX_COLS) * TINYTEX_SIZE; - *y = (tinytexcur / TINYTEX_COLS) * TINYTEX_SIZE; -} + gl->BindTexture(p->target, p->eosd_texture); -static inline void smalltex_pos(int smalltexcur, int *x, int *y) -{ - *x = (smalltexcur % SMALLTEX_COLS) * SMALLTEX_SIZE; - *y = (smalltexcur / SMALLTEX_COLS) * SMALLTEX_SIZE; + if (need_allocate) { + texSize(vo, p->eosd->surface.w, p->eosd->surface.h, + &p->eosd_texture_width, &p->eosd_texture_height); + // xxx it doesn't need to be cleared, that's a waste of time + glCreateClearTex(gl, p->target, GL_ALPHA, GL_ALPHA, GL_UNSIGNED_BYTE, + GL_NEAREST, p->eosd_texture_width, + p->eosd_texture_height, 0); + } + + // 2 triangles primitives per quad = 6 vertices per quad + // not using GL_QUADS, as it is deprecated in OpenGL 3.x and later + p->eosd_va = talloc_realloc_size(p->eosd, p->eosd_va, + p->eosd->targets_count + * sizeof(struct vertex_eosd) * 6); + + float eosd_w = p->eosd_texture_width; + float eosd_h = p->eosd_texture_height; + + if (p->use_rectangle == 1) + eosd_w = eosd_h = 1.0f; + + 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 (need_upload) { + glUploadTex(gl, p->target, GL_ALPHA, GL_UNSIGNED_BYTE, i->bitmap, + i->stride, target->source.x0, target->source.y0, + i->w, i->h, 0); + } + + uint8_t color[4] = { i->color >> 24, (i->color >> 16) & 0xff, + (i->color >> 8) & 0xff, 255 - (i->color & 0xff) }; + + float x0 = target->dest.x0; + float y0 = target->dest.y0; + float x1 = target->dest.x1; + float y1 = target->dest.y1; + float tx0 = target->source.x0 / eosd_w; + float ty0 = target->source.y0 / eosd_h; + float tx1 = target->source.x1 / eosd_w; + float ty1 = target->source.y1 / eosd_h; + +#define COLOR_INIT {color[0], color[1], color[2], color[3]} + struct vertex_eosd *va = &p->eosd_va[n * 6]; + va[0] = (struct vertex_eosd) { x0, y0, COLOR_INIT, tx0, ty0 }; + va[1] = (struct vertex_eosd) { x0, y1, COLOR_INIT, tx0, ty1 }; + va[2] = (struct vertex_eosd) { x1, y0, COLOR_INIT, tx1, ty0 }; + va[3] = (struct vertex_eosd) { x1, y1, COLOR_INIT, tx1, ty1 }; + va[4] = va[2]; + va[5] = va[1]; +#undef COLOR_INIT + } + + gl->BindTexture(p->target, 0); } -/** - * \brief construct display list from ass image list - * \param img image list to create OSD from. - * A value of NULL has the same effect as clearEOSD() - */ -static void genEOSD(struct vo *vo, mp_eosd_images_t *imgs) +// Note: relies on state being setup, like projection matrix and blending +static void drawEOSD(struct vo *vo) { struct gl_priv *p = vo->priv; GL *gl = p->gl; - int sx, sy; - int tinytexcur = 0; - int smalltexcur = 0; - GLuint *curtex; - GLint scale_type = p->scaled_osd ? GL_LINEAR : GL_NEAREST; - ASS_Image *img = imgs->imgs; - ASS_Image *i; - - if (imgs->changed == 0) // there are elements, but they are unchanged + if (p->eosd->targets_count == 0) return; - if (img && imgs->changed == 1) // there are elements, but they just moved - goto skip_upload; - clearEOSD(vo); - if (!img) - return; - if (!p->largeeosdtex[0]) { - gl->GenTextures(2, p->largeeosdtex); - for (int n = 0; n < 2; n++) { - gl->BindTexture(p->target, p->largeeosdtex[n]); - glCreateClearTex(gl, p->target, GL_ALPHA, GL_ALPHA, - GL_UNSIGNED_BYTE, scale_type, - LARGE_EOSD_TEX_SIZE, LARGE_EOSD_TEX_SIZE, 0); - } - } - for (i = img; i; i = i->next) { - if (i->w <= 0 || i->h <= 0 || i->stride < i->w) - continue; - if (is_tinytex(i, tinytexcur)) - tinytexcur++; - else if (is_smalltex(i, smalltexcur)) - smalltexcur++; - else - p->eosdtexCnt++; - } - mp_msg(MSGT_VO, MSGL_DBG2, "EOSD counts (tiny, small, all): %i, %i, %i\n", - tinytexcur, smalltexcur, p->eosdtexCnt); - if (p->eosdtexCnt) { - p->eosdtex = calloc(p->eosdtexCnt, sizeof(GLuint)); - gl->GenTextures(p->eosdtexCnt, p->eosdtex); - } - tinytexcur = smalltexcur = 0; - for (i = img, curtex = p->eosdtex; i; i = i->next) { - int x = 0, y = 0; - if (i->w <= 0 || i->h <= 0 || i->stride < i->w) { - mp_msg(MSGT_VO, MSGL_V, "Invalid dimensions OSD for part!\n"); - continue; - } - if (is_tinytex(i, tinytexcur)) { - tinytex_pos(tinytexcur, &x, &y); - gl->BindTexture(p->target, p->largeeosdtex[0]); - tinytexcur++; - } else if (is_smalltex(i, smalltexcur)) { - smalltex_pos(smalltexcur, &x, &y); - gl->BindTexture(p->target, p->largeeosdtex[1]); - smalltexcur++; - } else { - texSize(vo, i->w, i->h, &sx, &sy); - gl->BindTexture(p->target, *curtex++); - glCreateClearTex(gl, p->target, GL_ALPHA, GL_ALPHA, - GL_UNSIGNED_BYTE, scale_type, sx, sy, 0); - } - glUploadTex(gl, p->target, GL_ALPHA, GL_UNSIGNED_BYTE, i->bitmap, - i->stride, x, y, i->w, i->h, 0); - } - p->eosdDispList = gl->GenLists(1); -skip_upload: - gl->NewList(p->eosdDispList, GL_COMPILE); - tinytexcur = smalltexcur = 0; - for (i = img, curtex = p->eosdtex; i; i = i->next) { - int x = 0, y = 0; - if (i->w <= 0 || i->h <= 0 || i->stride < i->w) - continue; - gl->Color4ub(i->color >> 24, (i->color >> 16) & 0xff, - (i->color >> 8) & 0xff, 255 - (i->color & 0xff)); - if (is_tinytex(i, tinytexcur)) { - tinytex_pos(tinytexcur, &x, &y); - sx = sy = LARGE_EOSD_TEX_SIZE; - gl->BindTexture(p->target, p->largeeosdtex[0]); - tinytexcur++; - } else if (is_smalltex(i, smalltexcur)) { - smalltex_pos(smalltexcur, &x, &y); - sx = sy = LARGE_EOSD_TEX_SIZE; - gl->BindTexture(p->target, p->largeeosdtex[1]); - smalltexcur++; - } else { - texSize(vo, i->w, i->h, &sx, &sy); - gl->BindTexture(p->target, *curtex++); - } - glDrawTex(gl, i->dst_x, i->dst_y, i->w, i->h, x, y, i->w, i->h, sx, sy, - p->use_rectangle == 1, 0, 0); - } - gl->EndList(); + gl->BindTexture(p->target, p->eosd_texture); + + struct vertex_eosd *va = p->eosd_va; + size_t stride = sizeof(struct vertex_eosd); + + gl->VertexPointer(2, GL_FLOAT, stride, &va[0].x); + gl->ColorPointer(4, GL_UNSIGNED_BYTE, stride, &va[0].color[0]); + gl->TexCoordPointer(2, GL_FLOAT, stride, &va[0].u); + + gl->EnableClientState(GL_VERTEX_ARRAY); + gl->EnableClientState(GL_TEXTURE_COORD_ARRAY); + gl->EnableClientState(GL_COLOR_ARRAY); + + gl->DrawArrays(GL_TRIANGLES, 0, p->eosd->targets_count * 6); + + gl->DisableClientState(GL_VERTEX_ARRAY); + gl->DisableClientState(GL_TEXTURE_COORD_ARRAY); + gl->DisableClientState(GL_COLOR_ARRAY); + gl->BindTexture(p->target, 0); } @@ -450,10 +410,10 @@ static void uninitGl(struct vo *vo) gl->DeleteTextures(i, p->default_texs); p->default_texs[0] = 0; clearOSD(vo); - clearEOSD(vo); - if (p->largeeosdtex[0]) - gl->DeleteTextures(2, p->largeeosdtex); - p->largeeosdtex[0] = 0; + if (p->eosd_texture) + gl->DeleteTextures(1, &p->eosd_texture); + eosd_packer_reinit(p->eosd, 0, 0); + p->eosd_texture = 0; if (gl->DeleteBuffers && p->buffer) gl->DeleteBuffers(1, &p->buffer); p->buffer = 0; @@ -633,6 +593,10 @@ static int initGl(struct vo *vo, uint32_t d_width, uint32_t d_height) update_yuvconv(vo); } + GLint max_texture_size; + gl->GetIntegerv(GL_MAX_TEXTURE_SIZE, &max_texture_size); + eosd_packer_reinit(p->eosd, max_texture_size, max_texture_size); + resize(vo, d_width, d_height); gl->ClearColor(0.0f, 0.0f, 0.0f, 0.0f); @@ -786,7 +750,7 @@ static void do_render_osd(struct vo *vo, int type) GL *gl = p->gl; int draw_osd = (type & RENDER_OSD) && p->osdtexCnt > 0; - int draw_eosd = (type & RENDER_EOSD) && p->eosdDispList; + int draw_eosd = (type & RENDER_EOSD); if (!draw_osd && !draw_eosd) return; // set special rendering parameters @@ -799,7 +763,7 @@ static void do_render_osd(struct vo *vo, int type) gl->Enable(GL_BLEND); if (draw_eosd) { gl->BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - gl->CallList(p->eosdDispList); + drawEOSD(vo); } if (draw_osd) { gl->Color4ub((p->osd_color >> 16) & 0xff, (p->osd_color >> 8) & 0xff, @@ -1239,6 +1203,8 @@ static int preinit_internal(struct vo *vo, const char *arg, int allow_sw, .osd_color = 0xffffff, }; + p->eosd = eosd_packer_create(vo); + //essentially unused; for legacy warnings only int user_colorspace = 0; int levelconv = -1; diff --git a/libvo/vo_vdpau.c b/libvo/vo_vdpau.c index f8b70a4255..7b2f974ac2 100644 --- a/libvo/vo_vdpau.c +++ b/libvo/vo_vdpau.c @@ -56,6 +56,7 @@ #include "libavutil/mathematics.h" #include "sub/ass_mp.h" +#include "eosd_packer.h" #define WRAP_ADD(x, a, m) ((a) < 0 \ ? ((x)+(a)+(m) < (m) ? (x)+(a)+(m) : (x)+(a)) \ @@ -85,9 +86,6 @@ /* number of palette entries */ #define PALETTE_SIZE 256 -/* Initial size of EOSD surface in pixels (x*x) */ -#define EOSD_SURFACE_INITIAL_SIZE 256 - /* Pixelformat used for output surfaces */ #define OUTPUT_RGBA_FORMAT VDP_RGBA_FORMAT_B8G8R8A8 @@ -173,24 +171,8 @@ struct vdpctx { // EOSD // Pool of surfaces - struct eosd_bitmap_surface { - VdpBitmapSurface surface; - int w; - int h; - uint32_t max_width; - uint32_t max_height; - } eosd_surface; - - // List of surfaces to be rendered - struct eosd_target { - VdpRect source; - VdpRect dest; - VdpColor color; - } *eosd_targets; - int eosd_targets_size; - int *eosd_scratch; - - int eosd_render_count; + VdpBitmapSurface eosd_surface; + struct eosd_packer *eosd_packer; // Video equalizer struct mp_csp_equalizer video_eq; @@ -796,13 +778,16 @@ static int initialize_vdpau_objects(struct vo *vo) if (create_vdp_mixer(vo, vc->vdp_chroma_type) < 0) return -1; + int max_width = 0, max_height = 0; vdp_st = vdp-> bitmap_surface_query_capabilities(vc->vdp_device, VDP_RGBA_FORMAT_A8, &(VdpBool){0}, - &vc->eosd_surface.max_width, - &vc->eosd_surface.max_height); + &max_width, + &max_height); CHECK_ST_WARNING("Query to get max EOSD surface size failed"); + eosd_packer_reinit(vc->eosd_packer, max_width, max_height); + forget_frames(vo); resize(vo); return 0; @@ -822,11 +807,9 @@ static void mark_vdpau_objects_uninitialized(struct vo *vo) for (int i = 0; i <= MAX_OUTPUT_SURFACES; i++) vc->output_surfaces[i] = VDP_INVALID_HANDLE; vc->vdp_device = VDP_INVALID_HANDLE; - vc->eosd_surface = (struct eosd_bitmap_surface){ - .surface = VDP_INVALID_HANDLE, - }; + vc->eosd_surface = VDP_INVALID_HANDLE; + eosd_packer_reinit(vc->eosd_packer, 0, 0); vc->output_surface_width = vc->output_surface_height = -1; - vc->eosd_render_count = 0; } static int handle_preemption(struct vo *vo) @@ -1022,6 +1005,8 @@ static void draw_osd_I8A8(void *ctx, int x0, int y0, int w, int h, "vdp_output_surface_render_output_surface"); } +#define EOSD_VDP_RC(r) &(VdpRect){r.x0, r.y0, r.x1, r.y1} + static void draw_eosd(struct vo *vo) { struct vdpctx *vc = vo->priv; @@ -1044,200 +1029,66 @@ static void draw_eosd(struct vo *vo) .blend_equation_alpha = VDP_OUTPUT_SURFACE_RENDER_BLEND_EQUATION_ADD, }; - for (i = 0; i < vc->eosd_render_count; i++) { + for (i = 0; i < vc->eosd_packer->targets_count; i++) { + struct eosd_target *target = &vc->eosd_packer->targets[i]; + VdpColor color = { + .alpha = 1.0 - ((target->color >> 0) & 0xff) / 255.0, + .blue = ((target->color >> 8) & 0xff) / 255.0, + .green = ((target->color >> 16) & 0xff) / 255.0, + .red = ((target->color >> 24) & 0xff) / 255.0, + }; vdp_st = vdp-> output_surface_render_bitmap_surface(output_surface, - &vc->eosd_targets[i].dest, - vc->eosd_surface.surface, - &vc->eosd_targets[i].source, - &vc->eosd_targets[i].color, + EOSD_VDP_RC(target->dest), + vc->eosd_surface, + EOSD_VDP_RC(target->source), + &color, &blend_state, VDP_OUTPUT_SURFACE_RENDER_ROTATE_0); CHECK_ST_WARNING("EOSD: Error when rendering"); } } -#define HEIGHT_SORT_BITS 4 -static int size_index(struct eosd_target *r) -{ - unsigned int h = r->source.y1; - int n = av_log2_16bit(h); - return (n << HEIGHT_SORT_BITS) - + (- 1 - (h << HEIGHT_SORT_BITS >> n) & (1 << HEIGHT_SORT_BITS) - 1); -} - -/* Pack the given rectangles into an area of size w * h. - * The size of each rectangle is read from .source.x1/.source.y1. - * The height of each rectangle must be at least 1 and less than 65536. - * The .source rectangle is then set corresponding to the packed position. - * 'scratch' must point to work memory for num_rects+16 ints. - * Return 0 on success, -1 if the rectangles did not fit in w*h. - * - * The rectangles are placed in rows in order approximately sorted by - * height (the approximate sorting is simpler than a full one would be, - * and allows the algorithm to work in linear time). Additionally, to - * reduce wasted space when there are a few tall rectangles, empty - * lower-right parts of rows are filled recursively when the size of - * rectangles in the row drops past a power-of-two threshold. So if a - * row starts with rectangles of size 3x50, 10x40 and 5x20 then the - * free rectangle with corners (13, 20)-(w, 50) is filled recursively. - */ -static int pack_rectangles(struct eosd_target *rects, int num_rects, - int w, int h, int *scratch) -{ - int bins[16 << HEIGHT_SORT_BITS]; - int sizes[16 << HEIGHT_SORT_BITS] = {}; - for (int i = 0; i < num_rects; i++) - sizes[size_index(rects + i)]++; - int idx = 0; - for (int i = 0; i < 16 << HEIGHT_SORT_BITS; i += 1 << HEIGHT_SORT_BITS) { - for (int j = 0; j < 1 << HEIGHT_SORT_BITS; j++) { - bins[i + j] = idx; - idx += sizes[i + j]; - } - scratch[idx++] = -1; - } - for (int i = 0; i < num_rects; i++) - scratch[bins[size_index(rects + i)]++] = i; - for (int i = 0; i < 16; i++) - bins[i] = bins[i << HEIGHT_SORT_BITS] - sizes[i << HEIGHT_SORT_BITS]; - struct { - int size, x, bottom; - } stack[16] = {{15, 0, h}}, s = {}; - int stackpos = 1; - int y; - while (stackpos) { - y = s.bottom; - s = stack[--stackpos]; - s.size++; - while (s.size--) { - int maxy = -1; - int obj; - while ((obj = scratch[bins[s.size]]) >= 0) { - int bottom = y + rects[obj].source.y1; - if (bottom > s.bottom) - break; - int right = s.x + rects[obj].source.x1; - |