1
0
mirror of https://github.com/mpv-player/mpv synced 2025-03-31 15:59:34 +00:00

vo_opengl: support rotation

This turned out much more complicated than I thought. It's not just a
matter of adjusting the texture coordinates, but you also have to
consider separated scaling and panscan clipping, which make everything
complicated.

This actually still doesn't clip 100% correctly, but the bug is only
visible when rotating (or flipping with --vf=flip), and using something
like --video-pan-x/y at the same time.
This commit is contained in:
wm4 2014-04-20 21:37:18 +02:00
parent ef2885e771
commit 7791b5cf7c
2 changed files with 48 additions and 15 deletions

View File

@ -194,6 +194,7 @@ struct gl_video {
struct mp_image_params image_params; struct mp_image_params image_params;
struct mp_rect src_rect; // displayed part of the source video struct mp_rect src_rect; // displayed part of the source video
struct mp_rect src_rect_rot;// compensated for optional rotation
struct mp_rect dst_rect; // video rectangle on output window struct mp_rect dst_rect; // video rectangle on output window
struct mp_osd_res osd_rect; // OSD size/margins struct mp_osd_res osd_rect; // OSD size/margins
int vp_x, vp_y, vp_w, vp_h; // GL viewport int vp_x, vp_y, vp_w, vp_h; // GL viewport
@ -423,12 +424,12 @@ static void draw_triangles(struct gl_video *p, struct vertex *vb, int vert_count
// tx0, ty0, tx1, ty1 = source texture coordinates (usually in pixels) // tx0, ty0, tx1, ty1 = source texture coordinates (usually in pixels)
// texture_w, texture_h = size of the texture, or an inverse factor // texture_w, texture_h = size of the texture, or an inverse factor
// color = optional color for all vertices, NULL for opaque white // color = optional color for all vertices, NULL for opaque white
// flip = flip vertically // flags = bits 0-1: rotate, bits 2: flip vertically
static void write_quad(struct vertex *va, static void write_quad(struct vertex *va,
float x0, float y0, float x1, float y1, float x0, float y0, float x1, float y1,
float tx0, float ty0, float tx1, float ty1, float tx0, float ty0, float tx1, float ty1,
float texture_w, float texture_h, float texture_w, float texture_h,
const uint8_t color[4], GLenum target, bool flip) const uint8_t color[4], GLenum target, int flags)
{ {
static const uint8_t white[4] = { 255, 255, 255, 255 }; static const uint8_t white[4] = { 255, 255, 255, 255 };
@ -442,7 +443,7 @@ static void write_quad(struct vertex *va,
ty1 /= texture_h; ty1 /= texture_h;
} }
if (flip) { if (flags & 4) {
float tmp = ty0; float tmp = ty0;
ty0 = ty1; ty0 = ty1;
ty1 = tmp; ty1 = tmp;
@ -456,6 +457,14 @@ static void write_quad(struct vertex *va,
va[4] = va[2]; va[4] = va[2];
va[5] = va[1]; va[5] = va[1];
#undef COLOR_INIT #undef COLOR_INIT
int rot = flags & 3;
while (rot--) {
static const int perm[6] = {1, 3, 0, 2, 0, 3};
struct vertex vb[6];
memcpy(vb, va, sizeof(vb));
for (int n = 0; n < 6; n++)
memcpy(va[n].texcoord, vb[perm[n]].texcoord, sizeof(float[2]));
}
} }
static bool fbotex_init(struct gl_video *p, struct fbotex *fbo, int w, int h, static bool fbotex_init(struct gl_video *p, struct fbotex *fbo, int w, int h,
@ -1190,9 +1199,11 @@ static void reinit_rendering(struct gl_video *p)
compile_shaders(p); compile_shaders(p);
update_all_uniforms(p); update_all_uniforms(p);
int w = p->image_w;
int h = p->image_h;
if (p->indirect_program && !p->indirect_fbo.fbo) if (p->indirect_program && !p->indirect_fbo.fbo)
fbotex_init(p, &p->indirect_fbo, p->image_w, p->image_h, fbotex_init(p, &p->indirect_fbo, w, h, p->opts.fbo_format);
p->opts.fbo_format);
recreate_osd(p); recreate_osd(p);
} }
@ -1417,13 +1428,14 @@ static void change_dither_trafo(struct gl_video *p)
} }
struct pass { struct pass {
int num;
// Not necessarily a FBO; we just abuse this struct because it's convenient. // Not necessarily a FBO; we just abuse this struct because it's convenient.
// It specifies the source texture/sub-rectangle for the next pass. // It specifies the source texture/sub-rectangle for the next pass.
struct fbotex f; struct fbotex f;
// If true, render source (f) to dst, instead of the full dest. fbo viewport // If true, render source (f) to dst, instead of the full dest. fbo viewport
bool use_dst; bool use_dst;
struct mp_rect dst; struct mp_rect dst;
bool flip; int flags; // for write_quad
bool render_stereo; bool render_stereo;
}; };
@ -1457,6 +1469,12 @@ static void handle_pass(struct gl_video *p, struct pass *chain,
if (chain->use_dst) if (chain->use_dst)
dst = chain->dst; dst = chain->dst;
MP_TRACE(p, "Pass %d: [%d,%d,%d,%d] -> [%d,%d,%d,%d][%d,%d@%dx%d/%dx%d] (%d)\n",
chain->num, src.x0, src.y0, src.x1, src.y1,
dst.x0, dst.y0, dst.x1, dst.y1,
fbo->vp_x, fbo->vp_y, fbo->vp_w, fbo->vp_h,
fbo->tex_w, fbo->tex_h, chain->flags);
if (chain->render_stereo && p->opts.stereo_mode) { if (chain->render_stereo && p->opts.stereo_mode) {
int w = src.x1 - src.x0; int w = src.x1 - src.x0;
int imgw = p->image_w; int imgw = p->image_w;
@ -1467,7 +1485,7 @@ static void handle_pass(struct gl_video *p, struct pass *chain,
dst.x0, dst.y0, dst.x1, dst.y1, dst.x0, dst.y0, dst.x1, dst.y1,
src.x0 / 2, src.y0, src.x0 / 2, src.y0,
src.x0 / 2 + w / 2, src.y1, src.x0 / 2 + w / 2, src.y1,
tex_w, tex_h, NULL, p->gl_target, chain->flip); tex_w, tex_h, NULL, p->gl_target, chain->flags);
draw_triangles(p, vb, VERTICES_PER_QUAD); draw_triangles(p, vb, VERTICES_PER_QUAD);
glEnable3DRight(gl, p->opts.stereo_mode); glEnable3DRight(gl, p->opts.stereo_mode);
@ -1476,7 +1494,7 @@ static void handle_pass(struct gl_video *p, struct pass *chain,
dst.x0, dst.y0, dst.x1, dst.y1, dst.x0, dst.y0, dst.x1, dst.y1,
src.x0 / 2 + imgw / 2, src.y0, src.x0 / 2 + imgw / 2, src.y0,
src.x0 / 2 + imgw / 2 + w / 2, src.y1, src.x0 / 2 + imgw / 2 + w / 2, src.y1,
tex_w, tex_h, NULL, p->gl_target, chain->flip); tex_w, tex_h, NULL, p->gl_target, chain->flags);
draw_triangles(p, vb, VERTICES_PER_QUAD); draw_triangles(p, vb, VERTICES_PER_QUAD);
glDisable3D(gl, p->opts.stereo_mode); glDisable3D(gl, p->opts.stereo_mode);
@ -1484,11 +1502,12 @@ static void handle_pass(struct gl_video *p, struct pass *chain,
write_quad(vb, write_quad(vb,
dst.x0, dst.y0, dst.x1, dst.y1, dst.x0, dst.y0, dst.x1, dst.y1,
src.x0, src.y0, src.x1, src.y1, src.x0, src.y0, src.x1, src.y1,
tex_w, tex_h, NULL, p->gl_target, chain->flip); tex_w, tex_h, NULL, p->gl_target, chain->flags);
draw_triangles(p, vb, VERTICES_PER_QUAD); draw_triangles(p, vb, VERTICES_PER_QUAD);
} }
*chain = (struct pass){ *chain = (struct pass){
.num = chain->num + 1,
.f = *fbo, .f = *fbo,
}; };
} }
@ -1534,8 +1553,8 @@ void gl_video_render_frame(struct gl_video *p)
// Clip to visible height so that separate scaling scales the visible part // Clip to visible height so that separate scaling scales the visible part
// only (and the target FBO texture can have a bounded size). // only (and the target FBO texture can have a bounded size).
// Don't clamp width; too hard to get correct final scaling on l/r borders. // Don't clamp width; too hard to get correct final scaling on l/r borders.
chain.f.vp_y = p->src_rect.y0; chain.f.vp_y = p->src_rect_rot.y0;
chain.f.vp_h = p->src_rect.y1 - p->src_rect.y0; chain.f.vp_h = p->src_rect_rot.y1 - p->src_rect_rot.y0;
handle_pass(p, &chain, &p->scale_sep_fbo, p->scale_sep_program); handle_pass(p, &chain, &p->scale_sep_fbo, p->scale_sep_program);
@ -1551,12 +1570,13 @@ void gl_video_render_frame(struct gl_video *p)
// correct origin/height before. // correct origin/height before.
// For X direction, assume the texture wasn't scaled yet, so we can // For X direction, assume the texture wasn't scaled yet, so we can
// select the correct portion, which will be scaled to screen. // select the correct portion, which will be scaled to screen.
chain.f.vp_x = p->src_rect.x0; chain.f.vp_x = p->src_rect_rot.x0;
chain.f.vp_w = p->src_rect.x1 - p->src_rect.x0; chain.f.vp_w = p->src_rect_rot.x1 - p->src_rect_rot.x0;
chain.use_dst = true; chain.use_dst = true;
chain.dst = p->dst_rect; chain.dst = p->dst_rect;
chain.flip = vimg->image_flipped; chain.flags = (p->image_params.rotate % 90 ? 0 : p->image_params.rotate / 90)
| (vimg->image_flipped ? 4 : 0);
chain.render_stereo = true; chain.render_stereo = true;
handle_pass(p, &chain, &screen, p->final_program); handle_pass(p, &chain, &screen, p->final_program);
@ -1575,7 +1595,10 @@ void gl_video_render_frame(struct gl_video *p)
static void update_window_sized_objects(struct gl_video *p) static void update_window_sized_objects(struct gl_video *p)
{ {
if (p->scale_sep_program) { if (p->scale_sep_program) {
int w = p->dst_rect.x1 - p->dst_rect.x0;
int h = p->dst_rect.y1 - p->dst_rect.y0; int h = p->dst_rect.y1 - p->dst_rect.y0;
if ((p->image_params.rotate % 180) == 90)
MPSWAP(int, w, h);
if (h > p->scale_sep_fbo.tex_h) { if (h > p->scale_sep_fbo.tex_h) {
fbotex_uninit(p, &p->scale_sep_fbo); fbotex_uninit(p, &p->scale_sep_fbo);
// Round up to an arbitrary alignment to make window resizing or // Round up to an arbitrary alignment to make window resizing or
@ -1628,9 +1651,15 @@ void gl_video_resize(struct gl_video *p, struct mp_rect *window,
struct mp_osd_res *osd) struct mp_osd_res *osd)
{ {
p->src_rect = *src; p->src_rect = *src;
p->src_rect_rot = *src;
p->dst_rect = *dst; p->dst_rect = *dst;
p->osd_rect = *osd; p->osd_rect = *osd;
if ((p->image_params.rotate % 180) == 90) {
MPSWAP(int, p->src_rect_rot.x0, p->src_rect_rot.y0);
MPSWAP(int, p->src_rect_rot.x1, p->src_rect_rot.y1);
}
p->vp_x = window->x0; p->vp_x = window->x0;
p->vp_y = window->y0; p->vp_y = window->y0;
p->vp_w = window->x1 - window->x0; p->vp_w = window->x1 - window->x0;
@ -1788,7 +1817,7 @@ static void draw_osd_cb(void *ctx, struct mpgl_osd_part *osd,
write_quad(&va[osd->num_vertices], write_quad(&va[osd->num_vertices],
b->x, b->y, b->x + b->dw, b->y + b->dh, b->x, b->y, b->x + b->dw, b->y + b->dh,
pos.x, pos.y, pos.x + b->w, pos.y + b->h, pos.x, pos.y, pos.x + b->w, pos.y + b->h,
osd->w, osd->h, color, GL_TEXTURE_2D, false); osd->w, osd->h, color, GL_TEXTURE_2D, 0);
osd->num_vertices += VERTICES_PER_QUAD; osd->num_vertices += VERTICES_PER_QUAD;
} }
} }

View File

@ -464,9 +464,12 @@ const struct m_option options[] = {
{0}, {0},
}; };
#define CAPS VO_CAP_ROTATE90
const struct vo_driver video_out_opengl = { const struct vo_driver video_out_opengl = {
.description = "Extended OpenGL Renderer", .description = "Extended OpenGL Renderer",
.name = "opengl", .name = "opengl",
.caps = CAPS,
.preinit = preinit, .preinit = preinit,
.query_format = query_format, .query_format = query_format,
.reconfig = reconfig, .reconfig = reconfig,
@ -482,6 +485,7 @@ const struct vo_driver video_out_opengl = {
const struct vo_driver video_out_opengl_hq = { const struct vo_driver video_out_opengl_hq = {
.description = "Extended OpenGL Renderer (high quality rendering preset)", .description = "Extended OpenGL Renderer (high quality rendering preset)",
.name = "opengl-hq", .name = "opengl-hq",
.caps = CAPS,
.preinit = preinit, .preinit = preinit,
.query_format = query_format, .query_format = query_format,
.reconfig = reconfig, .reconfig = reconfig,