/*
* 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->targe
|