diff options
Diffstat (limited to 'libvo/vo_opengl_old.c')
-rw-r--r-- | libvo/vo_opengl_old.c | 1495 |
1 files changed, 1495 insertions, 0 deletions
diff --git a/libvo/vo_opengl_old.c b/libvo/vo_opengl_old.c new file mode 100644 index 0000000000..f24448ac6c --- /dev/null +++ b/libvo/vo_opengl_old.c @@ -0,0 +1,1495 @@ +/* + * 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, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * You can alternatively redistribute this file and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <math.h> +#include <stdbool.h> + +#include "config.h" +#include "talloc.h" +#include "mp_msg.h" +#include "subopt-helper.h" +#include "video_out.h" +#include "libmpcodecs/vfcap.h" +#include "libmpcodecs/mp_image.h" +#include "geometry.h" +#include "osd.h" +#include "sub/sub.h" +#include "eosd_packer.h" + +#include "gl_common.h" +#include "aspect.h" +#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))) +#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; + + int allow_sw; + + int use_osd; + int scaled_osd; + //! Textures for OSD + GLuint osdtex[MAX_OSD_PARTS]; + //! Alpha textures for OSD + GLuint osdatex[MAX_OSD_PARTS]; + 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]; + GLuint osdaDispList[MAX_OSD_PARTS]; + //! How many parts the OSD currently consists of + int osdtexCnt; + int osd_color; + + int use_ycbcr; + int use_yuv; + struct mp_csp_details colorspace; + int is_yuv; + int lscale; + int cscale; + float filter_strength; + float noise_strength; + int yuvconvtype; + int use_rectangle; + int err_shown; + uint32_t image_width; + uint32_t image_height; + uint32_t image_format; + uint32_t image_d_width; + uint32_t image_d_height; + int many_fmts; + int have_texture_rg; + int ati_hack; + int force_pbo; + int use_glFinish; + int swap_interval; + GLenum target; + GLint texfmt; + GLenum gl_format; + GLenum gl_type; + GLuint buffer; + GLuint buffer_uv[2]; + int buffersize; + int buffersize_uv; + void *bufferptr; + void *bufferptr_uv[2]; + GLuint fragprog; + GLuint default_texs[22]; + char *custom_prog; + char *custom_tex; + int custom_tlin; + int custom_trect; + int mipmap_gen; + int stereo_mode; + + struct mp_csp_equalizer video_eq; + + int texture_width; + int texture_height; + int mpi_flipped; + int vo_flipped; + int ass_border_x, ass_border_y; + + unsigned int slice_height; +}; + +static void resize(struct vo *vo, int x, int y) +{ + struct gl_priv *p = vo->priv; + GL *gl = p->gl; + + mp_msg(MSGT_VO, MSGL_V, "[gl] Resize: %dx%d\n", x, y); + if (WinID >= 0) { + int left = 0, top = 0, w = x, h = y; + geometry(&left, &top, &w, &h, vo->dwidth, vo->dheight); + top = y - h - top; + gl->Viewport(left, top, w, h); + } else + gl->Viewport(0, 0, x, y); + + gl->MatrixMode(GL_PROJECTION); + gl->LoadIdentity(); + p->ass_border_x = p->ass_border_y = 0; + if (aspect_scaling()) { + int new_w, new_h; + GLdouble scale_x, scale_y; + aspect(vo, &new_w, &new_h, A_WINZOOM); + panscan_calc_windowed(vo); + new_w += vo->panscan_x; + new_h += vo->panscan_y; + scale_x = (GLdouble)new_w / (GLdouble)x; + scale_y = (GLdouble)new_h / (GLdouble)y; + gl->Scaled(scale_x, scale_y, 1); + p->ass_border_x = (vo->dwidth - new_w) / 2; + p->ass_border_y = (vo->dheight - new_h) / 2; + } + gl->Ortho(0, p->image_width, p->image_height, 0, -1, 1); + + gl->MatrixMode(GL_MODELVIEW); + gl->LoadIdentity(); + + vo_osd_resized(); + gl->Clear(GL_COLOR_BUFFER_BIT); + vo->want_redraw = true; +} + +static void texSize(struct vo *vo, int w, int h, int *texw, int *texh) +{ + struct gl_priv *p = vo->priv; + + if (p->use_rectangle) { + *texw = w; + *texh = h; + } else { + *texw = 32; + while (*texw < w) + *texw *= 2; + *texh = 32; + while (*texh < h) + *texh *= 2; + } + if (p->ati_hack) + *texw = (*texw + 511) & ~511; +} + +//! maximum size of custom fragment program +#define MAX_CUSTOM_PROG_SIZE (1024 * 1024) +static void update_yuvconv(struct vo *vo) +{ + struct gl_priv *p = vo->priv; + GL *gl = p->gl; + + int xs, ys, depth; + struct mp_csp_params cparams = { .colorspace = p->colorspace }; + mp_csp_copy_equalizer_values(&cparams, &p->video_eq); + gl_conversion_params_t params = { + p->target, p->yuvconvtype, cparams, + p->texture_width, p->texture_height, 0, 0, p->filter_strength, + p->noise_strength + }; + mp_get_chroma_shift(p->image_format, &xs, &ys, &depth); + params.chrom_texw = params.texw >> xs; + params.chrom_texh = params.texh >> ys; + params.csp_params.input_bits = depth; + params.csp_params.texture_bits = depth+7 & ~7; + glSetupYUVConversion(gl, ¶ms); + if (p->custom_prog) { + FILE *f = fopen(p->custom_prog, "rb"); + if (!f) { + mp_msg(MSGT_VO, MSGL_WARN, + "[gl] Could not read customprog %s\n", p->custom_prog); + } else { + char *prog = calloc(1, MAX_CUSTOM_PROG_SIZE + 1); + fread(prog, 1, MAX_CUSTOM_PROG_SIZE, f); + fclose(f); + loadGPUProgram(gl, GL_FRAGMENT_PROGRAM, prog); + free(prog); + } + gl->ProgramEnvParameter4f(GL_FRAGMENT_PROGRAM, 0, + 1.0 / p->texture_width, + 1.0 / p->texture_height, + p->texture_width, p->texture_height); + } + if (p->custom_tex) { + FILE *f = fopen(p->custom_tex, "rb"); + if (!f) { + mp_msg(MSGT_VO, MSGL_WARN, + "[gl] Could not read customtex %s\n", p->custom_tex); + } else { + int width, height, maxval; + gl->ActiveTexture(GL_TEXTURE3); + if (glCreatePPMTex(gl, p->custom_trect ? GL_TEXTURE_RECTANGLE : GL_TEXTURE_2D, + 0, p->custom_tlin ? GL_LINEAR : GL_NEAREST, + f, &width, &height, &maxval)) { + gl->ProgramEnvParameter4f(GL_FRAGMENT_PROGRAM, 1, + 1.0 / width, 1.0 / height, + width, height); + } else + mp_msg(MSGT_VO, MSGL_WARN, + "[gl] Error parsing customtex %s\n", p->custom_tex); + fclose(f); + gl->ActiveTexture(GL_TEXTURE0); + } + } +} + +/** + * \brief remove all OSD textures and display-lists, thus clearing it. + */ +static void clearOSD(struct vo *vo) +{ + struct gl_priv *p = vo->priv; + GL *gl = p->gl; + + int i; + if (!p->osdtexCnt) + return; + gl->DeleteTextures(p->osdtexCnt, p->osdtex); + gl->DeleteTextures(p->osdtexCnt, p->osdatex); + for (i = 0; i < p->osdtexCnt; i++) + gl->DeleteLists(p->osdaDispList[i], 1); + for (i = 0; i < p->osdtexCnt; i++) + gl->DeleteLists(p->osdDispList[i], 1); + p->osdtexCnt = 0; +} + +/** + * \brief construct display list from ass image list + * \param img image list to create OSD from. + */ +static void genEOSD(struct vo *vo, mp_eosd_images_t *imgs) +{ + struct gl_priv *p = vo->priv; + GL *gl = p->gl; + + bool need_repos, need_upload, need_allocate; + eosd_packer_generate(p->eosd, imgs, &need_repos, &need_upload, + &need_allocate); + + if (!need_repos) + return; + + if (!p->eosd_texture) + gl->GenTextures(1, &p->eosd_texture); + + gl->BindTexture(p->target, p->eosd_texture); + + 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); +} + +// 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; + + if (p->eosd->targets_count == 0) + return; + + 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); +} + +/** + * \brief uninitialize OpenGL context, freeing textures, buffers etc. + */ +static void uninitGl(struct vo *vo) +{ + struct gl_priv *p = vo->priv; + GL *gl = p->gl; + + if (!gl) + return; + + int i = 0; + if (gl->DeletePrograms && p->fragprog) + gl->DeletePrograms(1, &p->fragprog); + p->fragprog = 0; + while (p->default_texs[i] != 0) + i++; + if (i) + gl->DeleteTextures(i, p->default_texs); + p->default_texs[0] = 0; + clearOSD(vo); + 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; + p->buffersize = 0; + p->bufferptr = NULL; + if (gl->DeleteBuffers && p->buffer_uv[0]) + gl->DeleteBuffers(2, p->buffer_uv); + p->buffer_uv[0] = p->buffer_uv[1] = 0; + p->buffersize_uv = 0; + p->bufferptr_uv[0] = p->bufferptr_uv[1] = 0; + p->err_shown = 0; +} + +static void autodetectGlExtensions(struct vo *vo) +{ + struct gl_priv *p = vo->priv; + GL *gl = p->gl; + + const char *extensions = gl->GetString(GL_EXTENSIONS); + const char *vendor = gl->GetString(GL_VENDOR); + const char *version = gl->GetString(GL_VERSION); + const char *renderer = gl->GetString(GL_RENDERER); + int is_ati = vendor && strstr(vendor, "ATI") != NULL; + int ati_broken_pbo = 0; + mp_msg(MSGT_VO, MSGL_V, "[gl] Running on OpenGL '%s' by '%s', version '%s'\n", + renderer, vendor, version); + if (is_ati && strncmp(version, "2.1.", 4) == 0) { + int ver = atoi(version + 4); + mp_msg(MSGT_VO, MSGL_V, "[gl] Detected ATI driver version: %i\n", ver); + ati_broken_pbo = ver && ver < 8395; + } + if (p->ati_hack == -1) + p->ati_hack = ati_broken_pbo; + if (p->force_pbo == -1) { + p->force_pbo = 0; + if (extensions && strstr(extensions, "_pixel_buffer_object")) + p->force_pbo = is_ati; + } + p->have_texture_rg = extensions && strstr(extensions, "GL_ARB_texture_rg"); + if (p->use_rectangle == -1) { + p->use_rectangle = 0; + if (extensions) { +// if (strstr(extensions, "_texture_non_power_of_two")) + if (strstr(extensions, "_texture_rectangle")) + p->use_rectangle = renderer + && strstr(renderer, "Mesa DRI R200") ? 1 : 0; + } + } + if (p->use_osd == -1) + p->use_osd = gl->BindTexture != NULL; + if (p->use_yuv == -1) + p->use_yuv = glAutodetectYUVConversion(gl); + + int eq_caps = 0; + int yuv_mask = (1 << p->use_yuv); + if (!(yuv_mask & MASK_NOT_COMBINERS)) { + // combiners + eq_caps = (1 << MP_CSP_EQ_HUE) | (1 << MP_CSP_EQ_SATURATION); + } else if (yuv_mask & MASK_ALL_YUV) { + eq_caps = MP_CSP_EQ_CAPS_COLORMATRIX; + if (yuv_mask & MASK_GAMMA_SUPPORT) + eq_caps |= MP_CSP_EQ_CAPS_GAMMA; + } + p->video_eq.capabilities = eq_caps; + + if (is_ati && (p->lscale == 1 || p->lscale == 2 || p->cscale == 1 || p->cscale == 2)) + mp_msg(MSGT_VO, MSGL_WARN, "[gl] Selected scaling mode may be broken on" + " ATI cards.\n" + "Tell _them_ to fix GL_REPEAT if you have issues.\n"); + mp_msg(MSGT_VO, MSGL_V, "[gl] Settings after autodetection: ati-hack = %i, " + "force-pbo = %i, rectangle = %i, yuv = %i\n", + p->ati_hack, p->force_pbo, p->use_rectangle, p->use_yuv); +} + +static GLint get_scale_type(struct vo *vo, int chroma) +{ + struct gl_priv *p = vo->priv; + + int nearest = (chroma ? p->cscale : p->lscale) & 64; + if (nearest) + return p->mipmap_gen ? GL_NEAREST_MIPMAP_NEAREST : GL_NEAREST; + return p->mipmap_gen ? GL_LINEAR_MIPMAP_NEAREST : GL_LINEAR; +} + +// Return the high byte of the value that represents white in chroma (U/V) +static int get_chroma_clear_val(int bit_depth) +{ + return 1 << (bit_depth - 1 & 7); +} + +/** + * \brief Initialize a (new or reused) OpenGL context. + * set global gl-related variables to their default values + */ +static int initGl(struct vo *vo, uint32_t d_width, uint32_t d_height) +{ + struct gl_priv *p = vo->priv; + GL *gl = p->gl; + + GLint scale_type = get_scale_type(vo, 0); + autodetectGlExtensions(vo); + p->target = p->use_rectangle == 1 ? GL_TEXTURE_RECTANGLE : GL_TEXTURE_2D; + p->yuvconvtype = SET_YUV_CONVERSION(p->use_yuv) | + SET_YUV_LUM_SCALER(p->lscale) | + SET_YUV_CHROM_SCALER(p->cscale); + + texSize(vo, p->image_width, p->image_height, + &p->texture_width, &p->texture_height); + + gl->Disable(GL_BLEND); + gl->Disable(GL_DEPTH_TEST); + gl->DepthMask(GL_FALSE); + gl->Disable(GL_CULL_FACE); + gl->Enable(p->target); + gl->DrawBuffer(vo_doublebuffering ? GL_BACK : GL_FRONT); + gl->TexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + + mp_msg(MSGT_VO, MSGL_V, "[gl] Creating %dx%d texture...\n", + p->texture_width, p->texture_height); + + glCreateClearTex(gl, p->target, p->texfmt, p->gl_format, + p->gl_type, scale_type, + p->texture_width, p->texture_height, 0); + + if (p->mipmap_gen) + gl->TexParameteri(p->target, GL_GENERATE_MIPMAP, GL_TRUE); + + if (p->is_yuv) { + int i; + int xs, ys, depth; + scale_type = get_scale_type(vo, 1); + mp_get_chroma_shift(p->image_format, &xs, &ys, &depth); + int clear = get_chroma_clear_val(depth); + gl->GenTextures(21, p->default_texs); + p->default_texs[21] = 0; + for (i = 0; i < 7; i++) { + gl->ActiveTexture(GL_TEXTURE1 + i); + gl->BindTexture(GL_TEXTURE_2D, p->default_texs[i]); + gl->BindTexture(GL_TEXTURE_RECTANGLE, p->default_texs[i + 7]); + gl->BindTexture(GL_TEXTURE_3D, p->default_texs[i + 14]); + } + gl->ActiveTexture(GL_TEXTURE1); + glCreateClearTex(gl, p->target, p->texfmt, p->gl_format, + p->gl_type, scale_type, + p->texture_width >> xs, p->texture_height >> ys, + clear); + if (p->mipmap_gen) + gl->TexParameteri(p->target, GL_GENERATE_MIPMAP, GL_TRUE); + gl->ActiveTexture(GL_TEXTURE2); + glCreateClearTex(gl, p->target, p->texfmt, p->gl_format, + p->gl_type, scale_type, + p->texture_width >> xs, p->texture_height >> ys, + clear); + if (p->mipmap_gen) + gl->TexParameteri(p->target, GL_GENERATE_MIPMAP, GL_TRUE); + gl->ActiveTexture(GL_TEXTURE0); + gl->BindTexture(p->target, 0); + } + if (p->is_yuv || p->custom_prog) { + if ((MASK_NOT_COMBINERS & (1 << p->use_yuv)) || p->custom_prog) { + if (!gl->GenPrograms || !gl->BindProgram) + mp_msg(MSGT_VO, MSGL_ERR, + "[gl] fragment program functions missing!\n"); + else { + gl->GenPrograms(1, &p->fragprog); + gl->BindProgram(GL_FRAGMENT_PROGRAM, p->fragprog); + } + } + 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); + gl->Clear(GL_COLOR_BUFFER_BIT); + if (gl->SwapInterval && p->swap_interval >= 0) + gl->SwapInterval(p->swap_interval); + return 1; +} + +static bool create_window(struct vo *vo, uint32_t d_width, uint32_t d_height, + uint32_t flags) +{ + struct gl_priv *p = vo->priv; + + if (p->stereo_mode == GL_3D_QUADBUFFER) + flags |= VOFLAG_STEREO; + + int mpgl_caps = MPGL_CAP_GL_LEGACY; + if (!p->allow_sw) + mpgl_caps |= MPGL_CAP_NO_SW; + return mpgl_create_window(p->glctx, mpgl_caps, d_width, d_height, flags); +} + +static int config(struct vo *vo, uint32_t width, uint32_t height, + uint32_t d_width, uint32_t d_height, uint32_t flags, + uint32_t format) +{ + struct gl_priv *p = vo->priv; + + int xs, ys; + p->image_height = height; + p->image_width = width; + p->image_format = format; + p->image_d_width = d_width; + p->image_d_height = d_height; + p->is_yuv = mp_get_chroma_shift(p->image_format, &xs, &ys, NULL) > 0; + p->is_yuv |= (xs << 8) | (ys << 16); + glFindFormat(format, p->have_texture_rg, NULL, &p->texfmt, &p->gl_format, + &p->gl_type); + + p->vo_flipped = !!(flags & VOFLAG_FLIPPING); + + if (vo->config_count) + uninitGl(vo); + + if (!create_window(vo, d_width, d_height, flags)) + return -1; + + initGl(vo, vo->dwidth, vo->dheight); + + return 0; +} + +static void check_events(struct vo *vo) +{ + struct gl_priv *p = vo->priv; + + int e = p->glctx->check_events(vo); + if (e & VO_EVENT_REINIT) { + uninitGl(vo); + initGl(vo, vo->dwidth, vo->dheight); + } + if (e & VO_EVENT_RESIZE) + resize(vo, vo->dwidth, vo->dheight); + if (e & VO_EVENT_EXPOSE) + vo->want_redraw = true; +} + +/** + * Creates the textures and the display list needed for displaying + * an OSD part. + * Callback function for osd_draw_text_ext(). + */ +static void create_osd_texture(void *ctx, int x0, int y0, int w, int h, + unsigned char *src, unsigned char *srca, + int stride) +{ + struct vo *vo = ctx; + struct gl_priv *p = vo->priv; + GL *gl = p->gl; + + // initialize to 8 to avoid special-casing on alignment + int sx = 8, sy = 8; + GLint scale_type = p->scaled_osd ? GL_LINEAR : GL_NEAREST; + + if (w <= 0 || h <= 0 || stride < w) { + mp_msg(MSGT_VO, MSGL_V, "Invalid dimensions OSD for part!\n"); + return; + } + texSize(vo, w, h, &sx, &sy); + + if (p->osdtexCnt >= MAX_OSD_PARTS) { + mp_msg(MSGT_VO, MSGL_ERR, "Too many OSD parts, contact the developers!\n"); + return; + } + + // create Textures for OSD part + gl->GenTextures(1, &p->osdtex[p->osdtexCnt]); + gl->BindTexture(p->target, p->osdtex[p->osdtexCnt]); + glCreateClearTex(gl, p->target, GL_LUMINANCE, GL_LUMINANCE, + GL_UNSIGNED_BYTE, scale_type, sx, sy, 0); + glUploadTex(gl, p->target, GL_LUMINANCE, GL_UNSIGNED_BYTE, src, stride, + 0, 0, w, h, 0); + + gl->GenTextures(1, &p->osdatex[p->osdtexCnt]); + gl->BindTexture(p->target, p->osdatex[p->osdtexCnt]); + glCreateClearTex(gl, p->target, GL_ALPHA, GL_ALPHA, GL_UNSIGNED_BYTE, + scale_type, sx, sy, 0); + { + int i; + char *tmp = malloc(stride * h); + // convert alpha from weird MPlayer scale. + // in-place is not possible since it is reused for future OSDs + for (i = h * stride - 1; i >= 0; i--) + tmp[i] = -srca[i]; + glUploadTex(gl, p->target, GL_ALPHA, GL_UNSIGNED_BYTE, tmp, stride, + 0, 0, w, h, 0); + free(tmp); + } + + gl->BindTexture(p->target, 0); + + // Create a list for rendering this OSD part + p->osdaDispList[p->osdtexCnt] = gl->GenLists(1); + gl->NewList(p->osdaDispList[p->osdtexCnt], GL_COMPILE); + // render alpha + gl->BindTexture(p->target, p->osdatex[p->osdtexCnt]); + glDrawTex(gl, x0, y0, w, h, 0, 0, w, h, sx, sy, p->use_rectangle == 1, 0, 0); + gl->EndList(); + + p->osdDispList[p->osdtexCnt] = gl->GenLists(1); + gl->NewList(p->osdDispList[p->osdtexCnt], GL_COMPILE); + // render OSD + gl->BindTexture(p->target, p->osdtex[p->osdtexCnt]); + glDrawTex(gl, x0, y0, w, h, 0, 0, w, h, sx, sy, p->use_rectangle == 1, 0, 0); + gl->EndList(); + + p->osdtexCnt++; +} + +#define RENDER_OSD 1 +#define RENDER_EOSD 2 + +/** + * \param type bit 0: render OSD, bit 1: render EOSD + */ +static void do_render_osd(struct vo *vo, int type) +{ + struct gl_priv *p = vo->priv; + GL *gl = p->gl; + + int draw_osd = (type & RENDER_OSD) && p->osdtexCnt > 0; + int draw_eosd = (type & RENDER_EOSD); + if (!draw_osd && !draw_eosd) + return; + // set special rendering parameters + if (!p->scaled_osd) { + gl->MatrixMode(GL_PROJECTION); + gl->PushMatrix(); + gl->LoadIdentity(); + gl->Ortho(0, vo->dwidth, vo->dheight, 0, -1, 1); + } + gl->Enable(GL_BLEND); + if (draw_eosd) { + gl->BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + drawEOSD(vo); + } + if (draw_osd) { + gl->Color4ub((p->osd_color >> 16) & 0xff, (p->osd_color >> 8) & 0xff, + p->osd_color & 0xff, 0xff - (p->osd_color >> 24)); + // draw OSD + gl->BlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_ALPHA); + gl->CallLists(p->osdtexCnt, GL_UNSIGNED_INT, p->osdaDispList); + gl->BlendFunc(GL_SRC_ALPHA, GL_ONE); + gl->CallLists(p->osdtexCnt, GL_UNSIGNED_INT, p->osdDispList); + } + // set rendering parameters back to defaults + gl->Disable(GL_BLEND); + if (!p->scaled_osd) + gl->PopMatrix(); + gl->BindTexture(p->target, 0); +} + +static void draw_osd(struct vo *vo, struct osd_state *osd) +{ + struct gl_priv *p = vo->priv; + + if (!p->use_osd) + return; + if (vo_osd_has_changed(osd)) { + int osd_h, osd_w; + clearOSD(vo); + osd_w = p->scaled_osd ? p->image_width : vo->dwidth; + osd_h = p->scaled_osd ? p->image_height : vo->dheight; + osd_draw_text_ext(osd, osd_w, osd_h, p->ass_border_x, + p->ass_border_y, p->ass_border_x, + p->ass_border_y, p->image_width, + p->image_height, create_osd_texture, vo); + } + if (vo_doublebuffering) + do_render_osd(vo, RENDER_OSD); +} + +static void do_render(struct vo *vo) +{ + struct gl_priv *p = vo->priv; + GL *gl = p->gl; + +// Enable(GL_TEXTURE_2D); +// BindTexture(GL_TEXTURE_2D, texture_id); + + gl->Color4f(1, 1, 1, 1); + if (p->is_yuv || p->custom_prog) + glEnableYUVConversion(gl, p->target, p->yuvconvtype); + if (p->stereo_mode) { + glEnable3DLeft(gl, p->stereo_mode); + glDrawTex(gl, 0, 0, p->image_width, p->image_height, + 0, 0, p->image_width >> 1, p->image_height, + p->texture_width, p->texture_height, + p->use_rectangle == 1, p->is_yuv, + p->mpi_flipped ^ p->vo_flipped); + glEnable3DRight(gl, p->stereo_mode); + glDrawTex(gl, 0, 0, p->image_width, p->image_height, + p->image_width >> 1, 0, p->image_width >> 1, + p->image_height, p->texture_width, p->texture_height, + p->use_rectangle == 1, p->is_yuv, + p->mpi_flipped ^ p->vo_flipped); + glDisable3D(gl, p->stereo_mode); + } else { + glDrawTex(gl, 0, 0, p->image_width, p->image_height, + 0, 0, p->image_width, p->image_height, + p->texture_width, p->texture_height, + p->use_rectangle == 1, p->is_yuv, + p->mpi_flipped ^ p->vo_flipped); + } + if (p->is_yuv || p->custom_prog) + glDisableYUVConversion(gl, p->target, p->yuvconvtype); +} + +static void flip_page(struct vo *vo) +{ + struct gl_priv *p = vo->priv; + GL *gl = p->gl; + + if (vo_doublebuffering) { + if (p->use_glFinish) + gl->Finish(); + p->glctx->swapGlBuffers(p->glctx); + if (aspect_scaling()) + gl->Clear(GL_COLOR_BUFFER_BIT); + } else { + do_render(vo); + do_render_osd(vo, RENDER_OSD | RENDER_EOSD); + if (p->use_glFinish) + gl->Finish(); + else + gl->Flush(); + } +} + +static int draw_slice(struct vo *vo, uint8_t *src[], int stride[], int w, int h, + int x, int y) +{ + struct gl_priv *p = vo->priv; + GL *gl = p->gl; + + p->mpi_flipped = stride[0] < 0; + glUploadTex(gl, p->target, p->gl_format, p->gl_type, src[0], stride[0], + x, y, w, h, p->slice_height); + if (p->is_yuv) { + int xs, ys; + mp_get_chroma_shift(p->image_format, &xs, &ys, NULL); + gl->ActiveTexture(GL_TEXTURE1); + glUploadTex(gl, p->target, p->gl_format, p->gl_type, src[1], stride[1], + x >> xs, y >> ys, w >> xs, h >> ys, p->slice_height); + gl->ActiveTexture(GL_TEXTURE2); + glUploadTex(gl, p->target, p->gl_format, p->gl_type, src[2], stride[2], + x >> xs, y >> ys, w >> xs, h >> ys, p->slice_height); + gl->ActiveTexture(GL_TEXTURE0); + } + return 0; +} + +static uint32_t get_image(struct vo *vo, mp_image_t *mpi) +{ + struct gl_priv *p = vo->priv; + GL *gl = p->gl; + + int needed_size; + if (!gl->GenBuffers || !gl->BindBuffer || !gl->BufferData || !gl->MapBuffer) { + if (!p->err_shown) + mp_msg(MSGT_VO, MSGL_ERR, "[gl] extensions missing for dr\n" + "Expect a _major_ speed penalty\n"); + p->err_shown = 1; + return VO_FALSE; + } + if (mpi->flags & MP_IMGFLAG_READABLE) + return VO_FALSE; + if (mpi->type != MP_IMGTYPE_STATIC && mpi->type != MP_IMGTYPE_TEMP && + (mpi->type != MP_IMGTYPE_NUMBERED || mpi->number)) + return VO_FALSE; + if (p->ati_hack) { + mpi->width = p->texture_width; + mpi->height = p->texture_height; + } + mpi->stride[0] = mpi->width * mpi->bpp / 8; + needed_size = mpi->stride[0] * mpi->height; + if (!p->buffer) + gl->GenBuffers(1, &p->buffer); + gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, p->buffer); + if (needed_size > p->buffersize) { + p->buffersize = needed_size; + gl->BufferData(GL_PIXEL_UNPACK_BUFFER, p->buffersize, + NULL, GL_DYNAMIC_DRAW); + } + if (!p->bufferptr) + p->bufferptr = gl->MapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY); + mpi->planes[0] = p->bufferptr; + gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); + if (!mpi->planes[0]) { + if (!p->err_shown) + mp_msg(MSGT_VO, MSGL_ERR, "[gl] could not acquire buffer for dr\n" + "Expect a _major_ speed penalty\n"); + p->err_shown = 1; + return VO_FALSE; + } + if (p->is_yuv) { + // planar YUV + int xs, ys, component_bits; + mp_get_chroma_shift(p->image_format, &xs, &ys, &component_bits); + int bp = (component_bits + 7) / 8; + mpi->flags |= MP_IMGFLAG_COMMON_STRIDE | MP_IMGFLAG_COMMON_PLANE; + mpi->stride[0] = mpi->width * bp; + mpi->planes[1] = mpi->planes[0] + mpi->stride[0] * mpi->height; + mpi->stride[1] = (mpi->width >> xs) * bp; + mpi->planes[2] = mpi->planes[1] + mpi->stride[1] * (mpi->height >> ys); + mpi->stride[2] = (mpi->width >> xs) * bp; + if (p->ati_hack) { + mpi->flags &= ~MP_IMGFLAG_COMMON_PLANE; + if (!p->buffer_uv[0]) + gl->GenBuffers(2, p->buffer_uv); + int buffer_size = mpi->stride[1] * mpi->height; + if (buffer_size > p->buffersize_uv) { + gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, p->buffer_uv[0]); + gl->BufferData(GL_PIXEL_UNPACK_BUFFER, buffer_size, NULL, + GL_DYNAMIC_DRAW); + gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, p->buffer_uv[1]); + gl->BufferData(GL_PIXEL_UNPACK_BUFFER, buffer_size, NULL, + GL_DYNAMIC_DRAW); + p->buffersize_uv = buffer_size; + } + if (!p->bufferptr_uv[0]) { + gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, p->buffer_uv[0]); + p->bufferptr_uv[0] = gl->MapBuffer(GL_PIXEL_UNPACK_BUFFER, + GL_WRITE_ONLY); + gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, p->buffer_uv[1]); + p->bufferptr_uv[1] = gl->MapBuffer(GL_PIXEL_UNPACK_BUFFER, + GL_WRITE_ONLY); + } + mpi->planes[1] = p->bufferptr_uv[0]; + mpi->planes[2] = p->bufferptr_uv[1]; + } + } + mpi->flags |= MP_IMGFLAG_DIRECT; + return VO_TRUE; +} + +static void clear_border(struct vo *vo, uint8_t *dst, int start, int stride, + int height, int full_height, int value) +{ + int right_border = stride - start; + int bottom_border = full_height - height; + while (height > 0) { + if (right_border > 0) + memset(dst + start, value, right_border); + dst += stride; + height--; + } + if (bottom_border > 0) + memset(dst, value, stride * bottom_border); +} + +static uint32_t draw_image(struct vo *vo, mp_image_t *mpi) +{ + struct gl_priv *p = vo->priv; + GL *gl = p->gl; + + int slice = p->slice_height; + int stride[3]; + unsigned char *planes[3]; + mp_image_t mpi2 = *mpi; + int w = mpi->w, h = mpi->h; + if (mpi->flags & MP_IMGFLAG_DRAW_CALLBACK) + goto skip_upload; + mpi2.flags = 0; + mpi2.type = MP_IMGTYPE_TEMP; + mpi2.width = mpi2.w; + mpi2.height = mpi2.h; + if (p->force_pbo && !(mpi->flags & MP_IMGFLAG_DIRECT) && !p->bufferptr + && get_image(vo, &mpi2) == VO_TRUE) + { + int bp = mpi->bpp / 8; + int xs, ys, component_bits; + mp_get_chroma_shift(p->image_format, &xs, &ys, &component_bits); + if (p->is_yuv) |