mirror of
https://github.com/mpv-player/mpv
synced 2025-01-24 19:37:30 +01:00
vo_gl3: move OSD code to gl_osd.c
Other OpenGL-using VOs can use this. gl_osd.c includes some code for vo_gl.c. The next commit actually makes use of it.
This commit is contained in:
parent
8f8f6e6d9d
commit
cadff3eec7
2
Makefile
2
Makefile
@ -284,7 +284,7 @@ SRCS_MPLAYER-$(COREAUDIO) += libao2/ao_coreaudio.c
|
||||
SRCS_MPLAYER-$(COREVIDEO) += libvo/vo_corevideo.m
|
||||
SRCS_MPLAYER-$(DIRECT3D) += libvo/vo_direct3d.c libvo/w32_common.c
|
||||
SRCS_MPLAYER-$(GL) += libvo/gl_common.c libvo/vo_gl.c libvo/vo_gl3.c \
|
||||
pnm_loader.c
|
||||
pnm_loader.c libvo/gl_osd.c
|
||||
SRCS_MPLAYER-$(ENCODING) += libvo/vo_lavc.c libao2/ao_lavc.c encode_lavc.c
|
||||
SRCS_MPLAYER-$(GL_WIN32) += libvo/w32_common.c
|
||||
SRCS_MPLAYER-$(GL_X11) += libvo/x11_common.c
|
||||
|
@ -20,6 +20,7 @@
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <libavutil/common.h>
|
||||
|
||||
@ -29,6 +30,9 @@
|
||||
#include "mpcommon.h"
|
||||
#include "sub/ass_mp.h"
|
||||
#include "sub/dec_sub.h"
|
||||
#include "fastmemcpy.h"
|
||||
|
||||
#define IS_POWER_OF_2(x) (((x) > 0) && !(((x) - 1) & (x)))
|
||||
|
||||
void packer_reset(struct bitmap_packer *packer)
|
||||
{
|
||||
@ -160,6 +164,8 @@ int packer_pack(struct bitmap_packer *packer)
|
||||
// No padding at edges
|
||||
packer->used_width = FFMIN(used_width, packer->w);
|
||||
packer->used_height = FFMIN(y, packer->h);
|
||||
assert(packer->w == 0 || IS_POWER_OF_2(packer->w));
|
||||
assert(packer->h == 0 || IS_POWER_OF_2(packer->h));
|
||||
return packer->w != w_orig || packer->h != h_orig;
|
||||
}
|
||||
if (packer->w <= packer->h && packer->w != packer->w_max)
|
||||
@ -201,3 +207,22 @@ int packer_pack_from_subbitmaps(struct bitmap_packer *packer,
|
||||
packer->in[i] = (struct pos){b->parts[i].w + a, b->parts[i].h + a};
|
||||
return packer_pack(packer);
|
||||
}
|
||||
|
||||
void packer_copy_subbitmaps(struct bitmap_packer *packer, struct sub_bitmaps *b,
|
||||
void *data, int pixel_stride, int stride)
|
||||
{
|
||||
assert(packer->count == b->num_parts);
|
||||
if (packer->padding) {
|
||||
struct pos bb[2];
|
||||
packer_get_bb(packer, bb);
|
||||
memset_pic(data, 0, bb[1].x * pixel_stride, bb[1].y, stride);
|
||||
}
|
||||
for (int n = 0; n < packer->count; n++) {
|
||||
struct sub_bitmap *s = &b->parts[n];
|
||||
struct pos p = packer->result[n];
|
||||
|
||||
void *pdata = (uint8_t *)data + p.y * stride + p.x * pixel_stride;
|
||||
memcpy_pic(pdata, s->bitmap, s->w * pixel_stride, s->h,
|
||||
stride, s->stride);
|
||||
}
|
||||
}
|
||||
|
@ -43,6 +43,7 @@ void packer_set_size(struct bitmap_packer *packer, int size);
|
||||
* Write input sizes in packer->in.
|
||||
* Resulting packing will be written in packer->result.
|
||||
* w and h will be increased if necessary for successful packing.
|
||||
* There is a strong guarantee that w and h will be powers of 2 (or set to 0).
|
||||
* Return value is -1 if packing failed because w and h were set to max
|
||||
* values but that wasn't enough, 1 if w or h was increased, and 0 otherwise.
|
||||
*/
|
||||
@ -55,4 +56,13 @@ int packer_pack(struct bitmap_packer *packer);
|
||||
int packer_pack_from_subbitmaps(struct bitmap_packer *packer,
|
||||
struct sub_bitmaps *b);
|
||||
|
||||
// Copy the (already packed) sub-bitmaps from b to the image in data.
|
||||
// data must point to an image that is at least (packer->w, packer->h) big.
|
||||
// The image has the given stride (bytes between (x, y) to (x, y + 1)), and the
|
||||
// pixel format used by both the sub-bitmaps and the image uses pixel_stride
|
||||
// bytes per pixel (bytes between (x, y) to (x + 1, y)).
|
||||
// If packer->padding is set, the padding borders are cleared with 0.
|
||||
void packer_copy_subbitmaps(struct bitmap_packer *packer, struct sub_bitmaps *b,
|
||||
void *data, int pixel_stride, int stride);
|
||||
|
||||
#endif
|
||||
|
@ -38,6 +38,7 @@
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <stdbool.h>
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
#include "talloc.h"
|
||||
#include "gl_common.h"
|
||||
@ -45,6 +46,8 @@
|
||||
#include "aspect.h"
|
||||
#include "pnm_loader.h"
|
||||
#include "options.h"
|
||||
#include "sub/sub.h"
|
||||
#include "bitmap_packer.h"
|
||||
|
||||
//! \defgroup glgeneral OpenGL general helper functions
|
||||
|
||||
|
322
libvo/gl_osd.c
Normal file
322
libvo/gl_osd.c
Normal file
@ -0,0 +1,322 @@
|
||||
/*
|
||||
* This file is part of mplayer.
|
||||
*
|
||||
* mplayer is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* mplayer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with mplayer. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <libavutil/common.h>
|
||||
|
||||
#include "bitmap_packer.h"
|
||||
|
||||
#include "gl_osd.h"
|
||||
|
||||
struct osd_fmt_entry {
|
||||
GLint internal_format;
|
||||
GLint format;
|
||||
GLenum type;
|
||||
};
|
||||
|
||||
// glBlendFunc() arguments
|
||||
static const int blend_factors[SUBBITMAP_COUNT][2] = {
|
||||
[SUBBITMAP_LIBASS] = {GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA},
|
||||
[SUBBITMAP_RGBA] = {GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA},
|
||||
[SUBBITMAP_OLD] = {GL_ONE, GL_ONE_MINUS_SRC_ALPHA},
|
||||
};
|
||||
|
||||
static const struct osd_fmt_entry osd_to_gl3_formats[SUBBITMAP_COUNT] = {
|
||||
[SUBBITMAP_LIBASS] = {GL_RED, GL_RED, GL_UNSIGNED_BYTE},
|
||||
[SUBBITMAP_RGBA] = {GL_RGBA, GL_BGRA, GL_UNSIGNED_BYTE},
|
||||
[SUBBITMAP_OLD] = {GL_RG, GL_RG, GL_UNSIGNED_BYTE},
|
||||
};
|
||||
|
||||
static const struct osd_fmt_entry osd_to_gl_legacy_formats[SUBBITMAP_COUNT] = {
|
||||
[SUBBITMAP_LIBASS] = {GL_ALPHA, GL_ALPHA, GL_UNSIGNED_BYTE},
|
||||
[SUBBITMAP_RGBA] = {GL_RGBA, GL_BGRA, GL_UNSIGNED_BYTE},
|
||||
[SUBBITMAP_OLD] =
|
||||
{GL_LUMINANCE_ALPHA, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE},
|
||||
};
|
||||
|
||||
struct mpgl_osd *mpgl_osd_init(GL *gl, bool legacy)
|
||||
{
|
||||
GLint max_texture_size;
|
||||
gl->GetIntegerv(GL_MAX_TEXTURE_SIZE, &max_texture_size);
|
||||
|
||||
struct mpgl_osd *ctx = talloc_ptrtype(NULL, ctx);
|
||||
*ctx = (struct mpgl_osd) {
|
||||
.gl = gl,
|
||||
.fmt_table = legacy ? osd_to_gl_legacy_formats : osd_to_gl3_formats,
|
||||
.scratch = talloc_zero_size(ctx, 1),
|
||||
};
|
||||
|
||||
for (int n = 0; n < MAX_OSD_PARTS; n++) {
|
||||
struct mpgl_osd_part *p = talloc_ptrtype(ctx, p);
|
||||
*p = (struct mpgl_osd_part) {
|
||||
.packer = talloc_struct(p, struct bitmap_packer, {
|
||||
.w_max = max_texture_size,
|
||||
.h_max = max_texture_size,
|
||||
}),
|
||||
};
|
||||
ctx->parts[n] = p;
|
||||
}
|
||||
|
||||
return ctx;
|
||||
}
|
||||
|
||||
void mpgl_osd_destroy(struct mpgl_osd *ctx)
|
||||
{
|
||||
GL *gl = ctx->gl;
|
||||
|
||||
for (int n = 0; n < MAX_OSD_PARTS; n++) {
|
||||
struct mpgl_osd_part *p = ctx->parts[n];
|
||||
gl->DeleteTextures(1, &p->texture);
|
||||
if (gl->DeleteBuffers)
|
||||
gl->DeleteBuffers(1, &p->buffer);
|
||||
}
|
||||
talloc_free(ctx);
|
||||
}
|
||||
|
||||
bool mpgl_osd_query_format(struct mpgl_osd *ctx, int osd_format)
|
||||
{
|
||||
return ctx->fmt_table[osd_format].type != 0;
|
||||
}
|
||||
|
||||
static bool upload_pbo(struct mpgl_osd *ctx, struct mpgl_osd_part *osd,
|
||||
struct sub_bitmaps *imgs)
|
||||
{
|
||||
GL *gl = ctx->gl;
|
||||
bool success = true;
|
||||
struct osd_fmt_entry fmt = ctx->fmt_table[imgs->format];
|
||||
int pix_stride = glFmt2bpp(fmt.format, fmt.type);
|
||||
|
||||
if (!osd->buffer) {
|
||||
gl->GenBuffers(1, &osd->buffer);
|
||||
gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, osd->buffer);
|
||||
gl->BufferData(GL_PIXEL_UNPACK_BUFFER, osd->w * osd->h * pix_stride,
|
||||
NULL, GL_DYNAMIC_COPY);
|
||||
gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
||||
}
|
||||
|
||||
gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, osd->buffer);
|
||||
char *data = gl->MapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY);
|
||||
if (!data) {
|
||||
success = false;
|
||||
} else {
|
||||
struct pos bb[2];
|
||||
packer_get_bb(osd->packer, bb);
|
||||
size_t stride = osd->w * pix_stride;
|
||||
packer_copy_subbitmaps(osd->packer, imgs, data, pix_stride, stride);
|
||||
if (!gl->UnmapBuffer(GL_PIXEL_UNPACK_BUFFER))
|
||||
success = false;
|
||||
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);
|
||||
}
|
||||
gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
||||
|
||||
if (!success) {
|
||||
mp_msg(MSGT_VO, MSGL_FATAL, "[gl] Error: can't upload subtitles! "
|
||||
"Remove the 'pbo' suboption.\n");
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
static void upload_tex(struct mpgl_osd *ctx, struct mpgl_osd_part *osd,
|
||||
struct sub_bitmaps *imgs)
|
||||
{
|
||||
struct osd_fmt_entry fmt = ctx->fmt_table[imgs->format];
|
||||
if (osd->packer->padding) {
|
||||
struct pos bb[2];
|
||||
packer_get_bb(osd->packer, bb);
|
||||
glClearTex(ctx->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, &ctx->scratch);
|
||||
}
|
||||
for (int n = 0; n < osd->packer->count; n++) {
|
||||
struct sub_bitmap *s = &imgs->parts[n];
|
||||
struct pos p = osd->packer->result[n];
|
||||
|
||||
glUploadTex(ctx->gl, GL_TEXTURE_2D, fmt.format, fmt.type,
|
||||
s->bitmap, s->stride, p.x, p.y, s->w, s->h, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static bool upload_osd(struct mpgl_osd *ctx, struct mpgl_osd_part *osd,
|
||||
struct sub_bitmaps *imgs)
|
||||
{
|
||||
GL *gl = ctx->gl;
|
||||
|
||||
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 false;
|
||||
}
|
||||
|
||||
struct osd_fmt_entry fmt = ctx->fmt_table[imgs->format];
|
||||
assert(fmt.type != 0);
|
||||
|
||||
if (!osd->texture)
|
||||
gl->GenTextures(1, &osd->texture);
|
||||
|
||||
gl->BindTexture(GL_TEXTURE_2D, osd->texture);
|
||||
|
||||
if (osd->packer->w > osd->w || osd->packer->h > osd->h
|
||||
|| osd->format != imgs->format)
|
||||
{
|
||||
osd->format = imgs->format;
|
||||
osd->w = FFMAX(32, osd->packer->w);
|
||||
osd->h = FFMAX(32, osd->packer->h);
|
||||
|
||||
gl->TexImage2D(GL_TEXTURE_2D, 0, fmt.internal_format, osd->w, osd->h,
|
||||
0, fmt.format, fmt.type, NULL);
|
||||
|
||||
gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
|
||||
if (gl->DeleteBuffers)
|
||||
gl->DeleteBuffers(1, &osd->buffer);
|
||||
osd->buffer = 0;
|
||||
}
|
||||
|
||||
bool uploaded = false;
|
||||
if (ctx->use_pbo)
|
||||
uploaded = upload_pbo(ctx, osd, imgs);
|
||||
if (!uploaded)
|
||||
upload_tex(ctx, osd, imgs);
|
||||
|
||||
gl->BindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
struct mpgl_osd_part *mpgl_osd_generate(struct mpgl_osd *ctx,
|
||||
struct sub_bitmaps *imgs)
|
||||
{
|
||||
if (imgs->num_parts == 0 || !mpgl_osd_query_format(ctx, imgs->format))
|
||||
return NULL;
|
||||
|
||||
struct mpgl_osd_part *osd = ctx->parts[imgs->render_index];
|
||||
|
||||
if (imgs->bitmap_pos_id != osd->bitmap_pos_id) {
|
||||
if (imgs->bitmap_id != osd->bitmap_id) {
|
||||
if (!upload_osd(ctx, osd, imgs))
|
||||
osd->packer->count = 0;
|
||||
}
|
||||
|
||||
osd->bitmap_id = imgs->bitmap_id;
|
||||
osd->bitmap_pos_id = imgs->bitmap_pos_id;
|
||||
osd->num_vertices = 0;
|
||||
}
|
||||
|
||||
return osd->packer->count ? osd : NULL;
|
||||
}
|
||||
|
||||
void mpgl_osd_gl_set_state(struct mpgl_osd *ctx, struct mpgl_osd_part *p)
|
||||
{
|
||||
GL *gl = ctx->gl;
|
||||
|
||||
gl->BindTexture(GL_TEXTURE_2D, p->texture);
|
||||
gl->Enable(GL_BLEND);
|
||||
gl->BlendFunc(blend_factors[p->format][0], blend_factors[p->format][1]);
|
||||
}
|
||||
|
||||
void mpgl_osd_gl_unset_state(struct mpgl_osd *ctx, struct mpgl_osd_part *p)
|
||||
{
|
||||
GL *gl = ctx->gl;
|
||||
|
||||
gl->Disable(GL_BLEND);
|
||||
gl->BindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
|
||||
struct vertex {
|
||||
float position[2];
|
||||
uint8_t color[4];
|
||||
float texcoord[2];
|
||||
};
|
||||
|
||||
void mpgl_osd_draw_legacy(struct mpgl_osd *ctx, struct sub_bitmaps *imgs)
|
||||
{
|
||||
struct mpgl_osd_part *osd = mpgl_osd_generate(ctx, imgs);
|
||||
if (!osd)
|
||||
return;
|
||||
|
||||
if (!osd->num_vertices) {
|
||||
// 2 triangles primitives per quad = 6 vertices per quad
|
||||
// not using GL_QUADS, as it is deprecated in OpenGL 3.x and later
|
||||
osd->vertices = talloc_realloc(osd, osd->vertices, struct vertex,
|
||||
osd->packer->count * 6);
|
||||
|
||||
struct vertex *va = osd->vertices;
|
||||
float tex_w = osd->w;
|
||||
float tex_h = osd->h;
|
||||
|
||||
for (int n = 0; n < osd->packer->count; n++) {
|
||||
struct sub_bitmap *b = &imgs->parts[n];
|
||||
struct pos p = osd->packer->result[n];
|
||||
|
||||
uint32_t c = imgs->format == SUBBITMAP_LIBASS
|
||||
? b->libass.color : 0xFFFFFF00;
|
||||
uint8_t color[4] = { c >> 24, (c >> 16) & 0xff,
|
||||
(c >> 8) & 0xff, 255 - (c & 0xff) };
|
||||
|
||||
float x0 = b->x;
|
||||
float y0 = b->y;
|
||||
float x1 = b->x + b->dw;
|
||||
float y1 = b->y + b->dh;
|
||||
float tx0 = p.x / tex_w;
|
||||
float ty0 = p.y / tex_h;
|
||||
float tx1 = (p.x + b->w) / tex_w;
|
||||
float ty1 = (p.y + b->h) / tex_h;
|
||||
|
||||
#define COLOR_INIT {color[0], color[1], color[2], color[3]}
|
||||
struct vertex *v = &va[osd->num_vertices];
|
||||
v[0] = (struct vertex) { {x0, y0}, COLOR_INIT, {tx0, ty0} };
|
||||
v[1] = (struct vertex) { {x0, y1}, COLOR_INIT, {tx0, ty1} };
|
||||
v[2] = (struct vertex) { {x1, y0}, COLOR_INIT, {tx1, ty0} };
|
||||
v[3] = (struct vertex) { {x1, y1}, COLOR_INIT, {tx1, ty1} };
|
||||
v[4] = v[2];
|
||||
v[5] = v[1];
|
||||
#undef COLOR_INIT
|
||||
osd->num_vertices += 6;
|
||||
}
|
||||
}
|
||||
|
||||
GL *gl = ctx->gl;
|
||||
|
||||
struct vertex *va = osd->vertices;
|
||||
size_t stride = sizeof(va[0]);
|
||||
|
||||
gl->VertexPointer(2, GL_FLOAT, stride, &va[0].position[0]);
|
||||
gl->ColorPointer(4, GL_UNSIGNED_BYTE, stride, &va[0].color[0]);
|
||||
gl->TexCoordPointer(2, GL_FLOAT, stride, &va[0].texcoord[0]);
|
||||
|
||||
gl->EnableClientState(GL_VERTEX_ARRAY);
|
||||
gl->EnableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||
gl->EnableClientState(GL_COLOR_ARRAY);
|
||||
|
||||
mpgl_osd_gl_set_state(ctx, osd);
|
||||
gl->DrawArrays(GL_TRIANGLES, 0, osd->num_vertices);
|
||||
mpgl_osd_gl_unset_state(ctx, osd);
|
||||
|
||||
gl->DisableClientState(GL_VERTEX_ARRAY);
|
||||
gl->DisableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||
gl->DisableClientState(GL_COLOR_ARRAY);
|
||||
}
|
41
libvo/gl_osd.h
Normal file
41
libvo/gl_osd.h
Normal file
@ -0,0 +1,41 @@
|
||||
#ifndef MPLAYER_GL_OSD_H
|
||||
#define MPLAYER_GL_OSD_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#include "gl_common.h"
|
||||
#include "sub/sub.h"
|
||||
|
||||
struct mpgl_osd_part {
|
||||
enum sub_bitmap_format format;
|
||||
int bitmap_id, bitmap_pos_id;
|
||||
GLuint texture;
|
||||
int w, h;
|
||||
GLuint buffer;
|
||||
int num_vertices;
|
||||
void *vertices;
|
||||
struct bitmap_packer *packer;
|
||||
};
|
||||
|
||||
struct mpgl_osd {
|
||||
GL *gl;
|
||||
bool use_pbo;
|
||||
struct mpgl_osd_part *parts[MAX_OSD_PARTS];
|
||||
const struct osd_fmt_entry *fmt_table;
|
||||
void *scratch;
|
||||
};
|
||||
|
||||
struct mpgl_osd *mpgl_osd_init(GL *gl, bool legacy);
|
||||
void mpgl_osd_destroy(struct mpgl_osd *ctx);
|
||||
|
||||
bool mpgl_osd_query_format(struct mpgl_osd *ctx, int osd_format);
|
||||
|
||||
void mpgl_osd_draw_legacy(struct mpgl_osd *ctx, struct sub_bitmaps *b);
|
||||
struct mpgl_osd_part *mpgl_osd_generate(struct mpgl_osd *ctx,
|
||||
struct sub_bitmaps *b);
|
||||
|
||||
void mpgl_osd_gl_set_state(struct mpgl_osd *ctx, struct mpgl_osd_part *p);
|
||||
void mpgl_osd_gl_unset_state(struct mpgl_osd *ctx, struct mpgl_osd_part *p);
|
||||
|
||||
#endif
|
261
libvo/vo_gl3.c
261
libvo/vo_gl3.c
@ -50,6 +50,7 @@
|
||||
#include "bitmap_packer.h"
|
||||
|
||||
#include "gl_common.h"
|
||||
#include "gl_osd.h"
|
||||
#include "filter_kernels.h"
|
||||
#include "aspect.h"
|
||||
#include "fastmemcpy.h"
|
||||
@ -142,17 +143,6 @@ 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;
|
||||
@ -186,7 +176,7 @@ struct gl_priv {
|
||||
GLuint osd_programs[SUBBITMAP_COUNT];
|
||||
GLuint indirect_program, scale_sep_program, final_program;
|
||||
|
||||
struct osd_render *osd[MAX_OSD_PARTS];
|
||||
struct mpgl_osd *osd;
|
||||
|
||||
GLuint lut_3d_texture;
|
||||
int lut_3d_w, lut_3d_h, lut_3d_d;
|
||||
@ -260,23 +250,10 @@ 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 *osd_shaders[SUBBITMAP_COUNT] = {
|
||||
[SUBBITMAP_LIBASS] = "frag_osd_libass",
|
||||
[SUBBITMAP_RGBA] = "frag_osd_rgba",
|
||||
[SUBBITMAP_OLD] = "frag_osd_old",
|
||||
};
|
||||
|
||||
|
||||
@ -678,11 +655,11 @@ static void compile_shaders(struct gl_priv *p)
|
||||
shader_def_opt(&header_osd, "USE_3DLUT", p->use_lut_3d);
|
||||
|
||||
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);
|
||||
const char *name = osd_shaders[n];
|
||||
if (name) {
|
||||
char *s_osd = get_section(tmp, src, name);
|
||||
p->osd_programs[n] =
|
||||
create_program(gl, fmt.shader, header_osd, vertex_shader, s_osd);
|
||||
create_program(gl, name, header_osd, vertex_shader, s_osd);
|
||||
}
|
||||
}
|
||||
|
||||
@ -947,6 +924,11 @@ static void reinit_rendering(struct gl_priv *p)
|
||||
|
||||
if (p->indirect_program && !p->indirect_fbo.fbo)
|
||||
fbotex_init(p, &p->indirect_fbo, p->texture_width, p->texture_height);
|
||||
|
||||
if (!p->osd) {
|
||||
p->osd = mpgl_osd_init(p->gl, false);
|
||||
p->osd->use_pbo = p->use_pbo;
|
||||
}
|
||||
}
|
||||
|
||||
static void uninit_rendering(struct gl_priv *p)
|
||||
@ -964,6 +946,10 @@ static void uninit_rendering(struct gl_priv *p)
|
||||
|
||||
gl->DeleteTextures(1, &p->dither_texture);
|
||||
p->dither_texture = 0;
|
||||
|
||||
if (p->osd)
|
||||
mpgl_osd_destroy(p->osd);
|
||||
p->osd = NULL;
|
||||
}
|
||||
|
||||
static void init_lut_3d(struct gl_priv *p)
|
||||
@ -1409,166 +1395,51 @@ static mp_image_t *get_window_screenshot(struct gl_priv *p)
|
||||
return image;
|
||||
}
|
||||
|
||||
static void gen_eosd(struct gl_priv *p, struct osd_render *osd,
|
||||
struct sub_bitmaps *imgs)
|
||||
{
|
||||
GL *gl = p->gl;
|
||||
|
||||
if (imgs->bitmap_pos_id == osd->bitmap_pos_id)
|
||||
return;
|
||||
|
||||
osd->num_vertices = 0;
|
||||
|
||||
if (imgs->format == SUBBITMAP_EMPTY || imgs->num_parts == 0)
|
||||
return;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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, 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,
|
||||
osd->width * osd->height * fmt.stride,
|
||||
NULL, GL_DYNAMIC_COPY);
|
||||
gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
||||
}
|
||||
|
||||
struct pos bb[2];
|
||||
packer_get_bb(osd->packer, bb);
|
||||
|
||||
if (need_upload && p->use_pbo) {
|
||||
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! "
|
||||
"Remove the 'pbo' suboption.\n");
|
||||
} else {
|
||||
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];
|
||||
|
||||
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");
|
||||
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);
|
||||
}
|
||||
if (need_upload) {
|
||||
// non-PBO upload
|
||||
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, fmt.format, fmt.type,
|
||||
b->bitmap, b->stride, p.x, p.y, b->w, b->h, 0);
|
||||
}
|
||||
}
|
||||
|
||||
gl->BindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
debug_check_gl(p, "EOSD upload");
|
||||
|
||||
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];
|
||||
|
||||
// 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, struct sub_bitmaps *imgs)
|
||||
{
|
||||
GL *gl = p->gl;
|
||||
|
||||
struct osd_render *osd = p->osd[imgs->render_index];
|
||||
assert(p->osd);
|
||||
|
||||
gen_eosd(p, osd, imgs);
|
||||
|
||||
if (osd->num_vertices == 0)
|
||||
struct mpgl_osd_part *osd = mpgl_osd_generate(p->osd, imgs);
|
||||
if (!osd)
|
||||
return;
|
||||
|
||||
assert(osd->format != SUBBITMAP_EMPTY);
|
||||
|
||||
gl->Enable(GL_BLEND);
|
||||
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);
|
||||
if (!osd->num_vertices) {
|
||||
osd->vertices = talloc_realloc(osd, osd->vertices, struct vertex,
|
||||
osd->packer->count * VERTICES_PER_QUAD);
|
||||
|
||||
struct vertex *va = osd->vertices;
|
||||
|
||||
for (int n = 0; n < osd->packer->count; n++) {
|
||||
struct sub_bitmap *b = &imgs->parts[n];
|
||||
struct pos p = osd->packer->result[n];
|
||||
|
||||
// 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(&va[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->w, osd->h, color, false);
|
||||
osd->num_vertices += VERTICES_PER_QUAD;
|
||||
}
|
||||
}
|
||||
gl->BindTexture(GL_TEXTURE_2D, osd->texture);
|
||||
|
||||
debug_check_gl(p, "before drawing osd");
|
||||
|
||||
gl->UseProgram(p->osd_programs[osd->format]);
|
||||
mpgl_osd_gl_set_state(p->osd, osd);
|
||||
draw_triangles(p, osd->vertices, osd->num_vertices);
|
||||
mpgl_osd_gl_unset_state(p->osd, osd);
|
||||
gl->UseProgram(0);
|
||||
gl->BindTexture(GL_TEXTURE_2D, 0);
|
||||
gl->Disable(GL_BLEND);
|
||||
|
||||
debug_check_gl(p, "after drawing osd");
|
||||
}
|
||||
|
||||
static void setup_vertex_array(GL *gl)
|
||||
@ -1630,21 +1501,6 @@ static int init_gl(struct gl_priv *p)
|
||||
gl->BindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
gl->BindVertexArray(0);
|
||||
|
||||
GLint max_texture_size;
|
||||
gl->GetIntegerv(GL_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);
|
||||
|
||||
@ -1668,16 +1524,6 @@ static void uninit_gl(struct gl_priv *p)
|
||||
gl->DeleteBuffers(1, &p->vertex_buffer);
|
||||
p->vertex_buffer = 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;
|
||||
}
|
||||
@ -1852,8 +1698,9 @@ static int control(struct vo *vo, uint32_t request, void *data)
|
||||
return VO_TRUE;
|
||||
}
|
||||
case VOCTRL_QUERY_EOSD_FORMAT: {
|
||||
int *p = data;
|
||||
return osd_to_gl_formats[*p].shader ? VO_TRUE : VO_NOTIMPL;
|
||||
int f = *(int *)data;
|
||||
return (mpgl_osd_query_format(p->osd, f) && osd_shaders[f])
|
||||
? VO_TRUE : VO_NOTIMPL;
|
||||
}
|
||||
case VOCTRL_ONTOP:
|
||||
if (!p->glctx->ontop)
|
||||
|
Loading…
Reference in New Issue
Block a user