Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Reorganize video output, to avoid invalid color handling.

Now the color handling and the rendering are done in two distinct steps,
and invlaid interpolations in non-linear color spaces are avoided. See
the overview comment in video_output_opengl.cpp.
  • Loading branch information...
commit bd47c6416c710d1988617c9fdfad7bff5b629bcf 1 parent c7aa104
@marlam marlam authored
View
6 src/Makefile.am
@@ -38,13 +38,15 @@ EXTRA_DIST = \
icons/b.ipe icons/b.png \
icons/bb.ipe icons/bb.png \
icons/bbb.ipe icons/bbb.png \
- video_output_opengl.fs.glsl
+ video_output_opengl_color.fs.glsl \
+ video_output_opengl_render.fs.glsl
nodist_bino_SOURCES = \
qt_resources-rcc.cpp \
player_qt-moc.cpp \
video_output_opengl_qt-moc.cpp \
- video_output_opengl.fs.glsl.h
+ video_output_opengl_color.fs.glsl.h \
+ video_output_opengl_render.fs.glsl.h
BUILT_SOURCES = $(nodist_bino_SOURCES)
View
2  src/player_equalizer.cpp
@@ -924,8 +924,6 @@ class eq_channel : public eq::Channel
}
// Display
- glEnable(GL_TEXTURE_2D);
- glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
eq_window *window = static_cast<eq_window *>(getWindow());
window->display(getEye() == eq::EYE_RIGHT ? video_output::mono_right : video_output::mono_left,
quad_x, quad_y, quad_w, quad_h);
View
479 src/video_output_opengl.cpp
@@ -32,10 +32,46 @@
#include "timer.h"
#include "video_output_opengl.h"
-#include "video_output_opengl.fs.glsl.h"
+#include "video_output_opengl_color.fs.glsl.h"
+#include "video_output_opengl_render.fs.glsl.h"
#include "xgl.h"
+/* Video output overview:
+ *
+ * Video output happens in three steps: video data input, color correction,
+ * and rendering.
+ *
+ * Step 1: Video data input.
+ * We have two texture sets for input: one holding the current video frame,
+ * and one for preparing the next video frame. Each texture set has textures
+ * for the left and right view. The video data is transferred to texture
+ * memory using pixel buffer objects, for better performance.
+ *
+ * Step 2: Color correction.
+ * The input data is first converted to YUV (for the most common yuv420p
+ * input format, this just means gathering of the three components from the
+ * three planes). Then color adjustment in the YUV space is performed.
+ * Finally the result is converted to sRGB and stored in an GL_SRGB texture.
+ * In this color correction step, no interpolation is done, because we're
+ * dealing with non-linear values, and interpolating them would lead to
+ * errors. We do not convert to linear RGB (as opposed to sRGB) in this step
+ * because storing linear RGB in a GL_RGB texture would lose some precision
+ * when compared to the non-linear input data.
+ *
+ * Step 3: Rendering.
+ * This step reads from the sRGB textures created in the previous step, which
+ * means that the GL will transform the input to linear RGB automatically and
+ * handle hardware accelerated bilinear interpolation correctly. Thus,
+ * magnification or minification are safe in this step. Furthermore, we can
+ * do interpolation on the linear RGB values for the masking output modes.
+ * We then transform the resulting linear RGB values back to non-linear sRGB
+ * values for output. We do not use the GL_ARB_framebuffer_sRGB extension for
+ * this purpose because 1) we need computations on non-linear values for the
+ * anaglyph methods and 2) sRGB framebuffers are not yet widely supported.
+ */
+
+
video_output_opengl::video_output_opengl(bool receive_notifications) throw ()
: video_output(receive_notifications), _initialized(false)
{
@@ -132,107 +168,36 @@ void video_output_opengl::initialize()
deinitialize();
}
+ // Step 1: input of video data
+ _active_tex_set = 0;
+ _input_is_mono = false;
_have_valid_data[0] = false;
_have_valid_data[1] = false;
-
- std::string mode_str = (
- _mode == even_odd_rows ? "mode_even_odd_rows"
- : _mode == even_odd_columns ? "mode_even_odd_columns"
- : _mode == checkerboard ? "mode_checkerboard"
- : _mode == anaglyph_red_cyan_monochrome ? "mode_anaglyph_monochrome"
- : _mode == anaglyph_red_cyan_full_color ? "mode_anaglyph_full_color"
- : _mode == anaglyph_red_cyan_half_color ? "mode_anaglyph_half_color"
- : _mode == anaglyph_red_cyan_dubois ? "mode_anaglyph_dubois"
- : "mode_onechannel");
- std::string input_str = (frame_format() == decoder::frame_format_yuv420p ? "input_yuv420p" : "input_bgra32");
- std::string fs_src = xgl::ShaderSourcePrep(VIDEO_OUTPUT_OPENGL_FS_GLSL_STR,
- std::string("$mode=") + mode_str + ", $input=" + input_str);
- _prg = xgl::CreateProgram("video_output", "", "", fs_src);
- xgl::LinkProgram("video_output", _prg);
- glUseProgram(_prg);
- if (_mode == even_odd_rows || _mode == even_odd_columns || _mode == checkerboard)
- {
- GLubyte even_odd_rows_mask[4] = { 0xff, 0xff, 0x00, 0x00 };
- GLubyte even_odd_columns_mask[4] = { 0xff, 0x00, 0xff, 0x00 };
- GLubyte checkerboard_mask[4] = { 0xff, 0x00, 0x00, 0xff };
- glGenTextures(1, &_mask_tex);
- glBindTexture(GL_TEXTURE_2D, _mask_tex);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
- glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
- glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
- glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE8, 2, 2, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE,
- _mode == even_odd_rows ? even_odd_rows_mask
- : _mode == even_odd_columns ? even_odd_columns_mask
- : checkerboard_mask);
- }
- if (frame_format() == decoder::frame_format_yuv420p)
- {
- glUniform1i(glGetUniformLocation(_prg, "y_l"), 0);
- glUniform1i(glGetUniformLocation(_prg, "u_l"), 1);
- glUniform1i(glGetUniformLocation(_prg, "v_l"), 2);
- if (_mode == even_odd_rows
- || _mode == even_odd_columns
- || _mode == checkerboard
- || _mode == anaglyph_red_cyan_monochrome
- || _mode == anaglyph_red_cyan_full_color
- || _mode == anaglyph_red_cyan_half_color
- || _mode == anaglyph_red_cyan_dubois)
- {
- glUniform1i(glGetUniformLocation(_prg, "y_r"), 3);
- glUniform1i(glGetUniformLocation(_prg, "u_r"), 4);
- glUniform1i(glGetUniformLocation(_prg, "v_r"), 5);
- }
- if (_mode == even_odd_rows || _mode == even_odd_columns || _mode == checkerboard)
- {
- glUniform1i(glGetUniformLocation(_prg, "mask_tex"), 6);
- }
- }
- else
- {
- glUniform1i(glGetUniformLocation(_prg, "rgb_l"), 0);
- if (_mode == even_odd_rows
- || _mode == even_odd_columns
- || _mode == checkerboard
- || _mode == anaglyph_red_cyan_monochrome
- || _mode == anaglyph_red_cyan_full_color
- || _mode == anaglyph_red_cyan_half_color
- || _mode == anaglyph_red_cyan_dubois)
- {
- glUniform1i(glGetUniformLocation(_prg, "rgb_r"), 1);
- }
- if (_mode == even_odd_rows || _mode == even_odd_columns || _mode == checkerboard)
- {
- glUniform1i(glGetUniformLocation(_prg, "mask_tex"), 2);
- }
- }
-
+ glGenBuffers(1, &_pbo);
if (frame_format() == decoder::frame_format_yuv420p)
{
for (int i = 0; i < 2; i++)
{
for (int j = 0; j < 2; j++)
{
- glGenTextures(1, &(_y_tex[i][j]));
- glBindTexture(GL_TEXTURE_2D, _y_tex[i][j]);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glGenTextures(1, &(_yuv420p_y_tex[i][j]));
+ glBindTexture(GL_TEXTURE_2D, _yuv420p_y_tex[i][j]);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE8, _src_width, _src_height, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, NULL);
- glGenTextures(1, &(_u_tex[i][j]));
- glBindTexture(GL_TEXTURE_2D, _u_tex[i][j]);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glGenTextures(1, &(_yuv420p_u_tex[i][j]));
+ glBindTexture(GL_TEXTURE_2D, _yuv420p_u_tex[i][j]);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE8, _src_width / 2, _src_height / 2, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, NULL);
- glGenTextures(1, &(_v_tex[i][j]));
- glBindTexture(GL_TEXTURE_2D, _v_tex[i][j]);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glGenTextures(1, &(_yuv420p_v_tex[i][j]));
+ glBindTexture(GL_TEXTURE_2D, _yuv420p_v_tex[i][j]);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE8, _src_width / 2, _src_height / 2, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, NULL);
@@ -245,24 +210,75 @@ void video_output_opengl::initialize()
{
for (int j = 0; j < 2; j++)
{
- glGenTextures(1, &(_rgb_tex[i][j]));
- glBindTexture(GL_TEXTURE_2D, _rgb_tex[i][j]);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glGenTextures(1, &(_bgra32_tex[i][j]));
+ glBindTexture(GL_TEXTURE_2D, _bgra32_tex[i][j]);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, _src_width, _src_height, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, NULL);
}
}
}
- _active_tex_set = 0;
- glGenBuffers(1, &_pbo);
+ // Step 2: color-correction
+ std::string input_str = (frame_format() == decoder::frame_format_yuv420p
+ ? "input_yuv420p" : "input_bgra32");
+ std::string color_fs_src = xgl::ShaderSourcePrep(
+ VIDEO_OUTPUT_OPENGL_COLOR_FS_GLSL_STR,
+ std::string("$input=") + input_str);
+ _color_prg = xgl::CreateProgram("video_output_color", "", "", color_fs_src);
+ xgl::LinkProgram("video_output_color", _color_prg);
+ glGenFramebuffersEXT(1, &_color_fbo);
+ for (int j = 0; j < 2; j++)
+ {
+ glGenTextures(1, &(_srgb_tex[j]));
+ glBindTexture(GL_TEXTURE_2D, _srgb_tex[j]);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_SRGB8, _src_width, _src_height, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, NULL);
+ }
- glEnable(GL_TEXTURE_2D);
- glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
+ // Step 3: rendering
+ std::string mode_str = (
+ _mode == even_odd_rows ? "mode_even_odd_rows"
+ : _mode == even_odd_columns ? "mode_even_odd_columns"
+ : _mode == checkerboard ? "mode_checkerboard"
+ : _mode == anaglyph_red_cyan_monochrome ? "mode_anaglyph_monochrome"
+ : _mode == anaglyph_red_cyan_full_color ? "mode_anaglyph_full_color"
+ : _mode == anaglyph_red_cyan_half_color ? "mode_anaglyph_half_color"
+ : _mode == anaglyph_red_cyan_dubois ? "mode_anaglyph_dubois"
+ : "mode_onechannel");
+ std::string render_fs_src = xgl::ShaderSourcePrep(
+ VIDEO_OUTPUT_OPENGL_RENDER_FS_GLSL_STR,
+ std::string("$mode=") + mode_str);
+ _render_prg = xgl::CreateProgram("video_output_render", "", "", render_fs_src);
+ xgl::LinkProgram("video_output_render", _render_prg);
+ if (_mode == even_odd_rows || _mode == even_odd_columns || _mode == checkerboard)
+ {
+ GLubyte even_odd_rows_mask[4] = { 0xff, 0xff, 0x00, 0x00 };
+ GLubyte even_odd_columns_mask[4] = { 0xff, 0x00, 0xff, 0x00 };
+ GLubyte checkerboard_mask[4] = { 0xff, 0x00, 0x00, 0xff };
+ glGenTextures(1, &_mask_tex);
+ glBindTexture(GL_TEXTURE_2D, _mask_tex);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE8, 2, 2, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE,
+ _mode == even_odd_rows ? even_odd_rows_mask
+ : _mode == even_odd_columns ? even_odd_columns_mask
+ : checkerboard_mask);
+ }
+
+ // Initialize GL things
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
+
_initialized = true;
}
@@ -273,30 +289,27 @@ void video_output_opengl::deinitialize()
return;
}
- if (_prg != 0)
- {
- xgl::DeleteProgram(_prg);
- }
+ _have_valid_data[0] = false;
+ _have_valid_data[1] = false;
+ glDeleteBuffers(1, &_pbo);
if (frame_format() == decoder::frame_format_yuv420p)
{
- glDeleteTextures(2 * 2, reinterpret_cast<GLuint *>(_y_tex));
- glDeleteTextures(2 * 2, reinterpret_cast<GLuint *>(_u_tex));
- glDeleteTextures(2 * 2, reinterpret_cast<GLuint *>(_v_tex));
+ glDeleteTextures(2 * 2, reinterpret_cast<GLuint *>(_yuv420p_y_tex));
+ glDeleteTextures(2 * 2, reinterpret_cast<GLuint *>(_yuv420p_u_tex));
+ glDeleteTextures(2 * 2, reinterpret_cast<GLuint *>(_yuv420p_v_tex));
}
else
{
- glDeleteTextures(2 * 2, reinterpret_cast<GLuint *>(_rgb_tex));
+ glDeleteTextures(2 * 2, reinterpret_cast<GLuint *>(_bgra32_tex));
}
+ xgl::DeleteProgram(_color_prg);
+ glDeleteFramebuffersEXT(1, &_color_fbo);
+ glDeleteTextures(2, reinterpret_cast<GLuint *>(_srgb_tex));
+ xgl::DeleteProgram(_render_prg);
if (_mode == even_odd_rows || _mode == even_odd_columns || _mode == checkerboard)
{
glDeleteTextures(1, &_mask_tex);
}
- if (_pbo != 0)
- {
- glDeleteBuffers(1, &_pbo);
- }
- _have_valid_data[0] = false;
- _have_valid_data[1] = false;
_initialized = false;
}
@@ -305,34 +318,16 @@ enum decoder::video_frame_format video_output_opengl::frame_format() const
return _src_preferred_frame_format;
}
-void video_output_opengl::bind_textures(int unitset, int index)
-{
- if (frame_format() == decoder::frame_format_yuv420p)
- {
- glActiveTexture(GL_TEXTURE0 + 3 * unitset + 0);
- glBindTexture(GL_TEXTURE_2D, _y_tex[_active_tex_set][index]);
- glActiveTexture(GL_TEXTURE0 + 3 * unitset + 1);
- glBindTexture(GL_TEXTURE_2D, _u_tex[_active_tex_set][index]);
- glActiveTexture(GL_TEXTURE0 + 3 * unitset + 2);
- glBindTexture(GL_TEXTURE_2D, _v_tex[_active_tex_set][index]);
- }
- else
- {
- glActiveTexture(GL_TEXTURE0 + unitset);
- glBindTexture(GL_TEXTURE_2D, _rgb_tex[_active_tex_set][index]);
- }
-}
-
-void video_output_opengl::draw_quad(float x, float y, float w, float h)
+static void draw_quad(float x, float y, float w, float h)
{
glBegin(GL_QUADS);
- glTexCoord2f(0.0f, 1.0f);
+ glTexCoord2f(0.0f, 0.0f);
glVertex2f(x, y);
- glTexCoord2f(1.0f, 1.0f);
- glVertex2f(x + w, y);
glTexCoord2f(1.0f, 0.0f);
+ glVertex2f(x + w, y);
+ glTexCoord2f(1.0f, 1.0f);
glVertex2f(x + w, y + h);
- glTexCoord2f(0.0f, 0.0f);
+ glTexCoord2f(0.0f, 1.0f);
glVertex2f(x, y + h);
glEnd();
}
@@ -345,13 +340,14 @@ void video_output_opengl::display(enum video_output::mode mode, float x, float y
return;
}
+ /* Use correct left and right view indices */
+
int left = 0;
int right = (_input_is_mono ? 0 : 1);
if (_state.swap_eyes)
{
std::swap(left, right);
}
-
int viewport[4];
glGetIntegerv(GL_VIEWPORT, viewport);
if ((mode == even_odd_rows || mode == checkerboard) && (screen_pos_y() + viewport[1]) % 2 == 0)
@@ -363,45 +359,139 @@ void video_output_opengl::display(enum video_output::mode mode, float x, float y
std::swap(left, right);
}
- glUniform1f(glGetUniformLocation(_prg, "contrast"), _state.contrast);
- glUniform1f(glGetUniformLocation(_prg, "brightness"), _state.brightness);
- glUniform1f(glGetUniformLocation(_prg, "saturation"), _state.saturation);
- glUniform1f(glGetUniformLocation(_prg, "cos_hue"), std::cos(_state.hue * M_PI));
- glUniform1f(glGetUniformLocation(_prg, "sin_hue"), std::sin(_state.hue * M_PI));
+ /* Initialize GL things */
+
+ glEnable(GL_TEXTURE_2D);
+ glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
+
+ /* Step 2: color-correction */
+
+ GLboolean scissor_test = glIsEnabled(GL_SCISSOR_TEST);
+ glDisable(GL_SCISSOR_TEST);
+ glMatrixMode(GL_MODELVIEW);
+ glPushMatrix();
+ glLoadIdentity();
+ glMatrixMode(GL_PROJECTION);
+ glPushMatrix();
+ glLoadIdentity();
+ glViewport(0, 0, _src_width, _src_height);
+ glUseProgram(_color_prg);
+ if (frame_format() == decoder::frame_format_yuv420p)
+ {
+ glUniform1i(glGetUniformLocation(_color_prg, "y_tex"), 0);
+ glUniform1i(glGetUniformLocation(_color_prg, "u_tex"), 1);
+ glUniform1i(glGetUniformLocation(_color_prg, "v_tex"), 2);
+ }
+ else
+ {
+ glUniform1i(glGetUniformLocation(_color_prg, "srgb_tex"), 0);
+ }
+ glUniform1f(glGetUniformLocation(_color_prg, "contrast"), _state.contrast);
+ glUniform1f(glGetUniformLocation(_color_prg, "brightness"), _state.brightness);
+ glUniform1f(glGetUniformLocation(_color_prg, "saturation"), _state.saturation);
+ glUniform1f(glGetUniformLocation(_color_prg, "cos_hue"), std::cos(_state.hue * M_PI));
+ glUniform1f(glGetUniformLocation(_color_prg, "sin_hue"), std::sin(_state.hue * M_PI));
+ glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, _color_fbo);
+ // left view: render into _srgb_tex[0]
+ if (frame_format() == decoder::frame_format_yuv420p)
+ {
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, _yuv420p_y_tex[_active_tex_set][left]);
+ glActiveTexture(GL_TEXTURE1);
+ glBindTexture(GL_TEXTURE_2D, _yuv420p_u_tex[_active_tex_set][left]);
+ glActiveTexture(GL_TEXTURE2);
+ glBindTexture(GL_TEXTURE_2D, _yuv420p_v_tex[_active_tex_set][left]);
+ }
+ else
+ {
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, _bgra32_tex[_active_tex_set][left]);
+ }
+ glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT,
+ GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, _srgb_tex[0], 0);
+ draw_quad(-1.0f, +1.0f, +2.0f, -2.0f);
+ // right view: render into _srgb_tex[1]
+ if (left != right)
+ {
+ if (frame_format() == decoder::frame_format_yuv420p)
+ {
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, _yuv420p_y_tex[_active_tex_set][right]);
+ glActiveTexture(GL_TEXTURE1);
+ glBindTexture(GL_TEXTURE_2D, _yuv420p_u_tex[_active_tex_set][right]);
+ glActiveTexture(GL_TEXTURE2);
+ glBindTexture(GL_TEXTURE_2D, _yuv420p_v_tex[_active_tex_set][right]);
+ }
+ else
+ {
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, _bgra32_tex[_active_tex_set][right]);
+ }
+ glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT,
+ GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, _srgb_tex[1], 0);
+ draw_quad(-1.0f, +1.0f, +2.0f, -2.0f);
+ }
+ glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
+ glViewport(viewport[0], viewport[1], viewport[2], viewport[3]);
+ glMatrixMode(GL_PROJECTION);
+ glPopMatrix();
+ glMatrixMode(GL_MODELVIEW);
+ glPopMatrix();
+ if (scissor_test)
+ {
+ glEnable(GL_SCISSOR_TEST);
+ }
+
+ // at this point, the left view is in _srgb_tex[0],
+ // and the right view (if it exists) is in _srgb_tex[1]
+ right = (left != right ? 1 : 0);
+ left = 0;
+
+ // Step 3: rendering
+ glUseProgram(_render_prg);
+ glUniform1i(glGetUniformLocation(_render_prg, "rgb_l"), left);
+ glUniform1i(glGetUniformLocation(_render_prg, "rgb_r"), right);
if (mode == even_odd_rows || mode == even_odd_columns || mode == checkerboard)
{
- glUniform1f(glGetUniformLocation(_prg, "step_x"), 1.0f / static_cast<float>(viewport[2]));
- glUniform1f(glGetUniformLocation(_prg, "step_y"), 1.0f / static_cast<float>(viewport[3]));
+ glUniform1i(glGetUniformLocation(_render_prg, "mask_tex"), 2);
+ glUniform1f(glGetUniformLocation(_render_prg, "step_x"), 1.0f / static_cast<float>(viewport[2]));
+ glUniform1f(glGetUniformLocation(_render_prg, "step_y"), 1.0f / static_cast<float>(viewport[3]));
}
if (mode == stereo)
{
+ glActiveTexture(GL_TEXTURE0);
glDrawBuffer(GL_BACK_LEFT);
- bind_textures(0, left);
+ glBindTexture(GL_TEXTURE_2D, _srgb_tex[left]);
draw_quad(x, y, w, h);
glDrawBuffer(GL_BACK_RIGHT);
- bind_textures(0, right);
+ glBindTexture(GL_TEXTURE_2D, _srgb_tex[right]);
draw_quad(x, y, w, h);
}
else if (mode == even_odd_rows || mode == even_odd_columns || mode == checkerboard)
{
float vpw = static_cast<float>(viewport[2]);
float vph = static_cast<float>(viewport[3]);
- bind_textures(0, left);
- bind_textures(1, right);
- glActiveTexture(GL_TEXTURE0 + (frame_format() == decoder::frame_format_yuv420p ? 6 : 2));
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, _srgb_tex[left]);
+ if (left != right)
+ {
+ glActiveTexture(GL_TEXTURE1);
+ glBindTexture(GL_TEXTURE_2D, _srgb_tex[right]);
+ }
+ glActiveTexture(GL_TEXTURE2);
glBindTexture(GL_TEXTURE_2D, _mask_tex);
glBegin(GL_QUADS);
- glTexCoord2f(0.0f, 1.0f);
+ glTexCoord2f(0.0f, 0.0f);
glMultiTexCoord2f(GL_TEXTURE1, 0.0f, 0.0f);
glVertex2f(x, y);
- glTexCoord2f(1.0f, 1.0f);
+ glTexCoord2f(1.0f, 0.0f);
glMultiTexCoord2f(GL_TEXTURE1, vpw / 2.0f, 0.0f);
glVertex2f(x + w, y);
- glTexCoord2f(1.0f, 0.0f);
+ glTexCoord2f(1.0f, 1.0f);
glMultiTexCoord2f(GL_TEXTURE1, vpw / 2.0f, vph / 2.0f);
glVertex2f(x + w, y + h);
- glTexCoord2f(0.0f, 0.0f);
+ glTexCoord2f(0.0f, 1.0f);
glMultiTexCoord2f(GL_TEXTURE1, 0.0f, vph / 2.0f);
glVertex2f(x, y + h);
glEnd();
@@ -411,69 +501,44 @@ void video_output_opengl::display(enum video_output::mode mode, float x, float y
|| mode == anaglyph_red_cyan_half_color
|| mode == anaglyph_red_cyan_dubois)
{
- bind_textures(0, left);
- bind_textures(1, right);
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, _srgb_tex[left]);
+ if (left != right)
+ {
+ glActiveTexture(GL_TEXTURE1);
+ glBindTexture(GL_TEXTURE_2D, _srgb_tex[right]);
+ }
draw_quad(x, y, w, h);
}
else if (mode == mono_left)
{
- bind_textures(0, left);
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, _srgb_tex[left]);
draw_quad(x, y, w, h);
}
else if (mode == mono_right)
{
- bind_textures(0, right);
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, _srgb_tex[right]);
draw_quad(x, y, w, h);
}
else if (mode == left_right || mode == left_right_half)
{
- bind_textures(0, left);
- glBegin(GL_QUADS);
- glTexCoord2f(0.0f, 0.0f);
- glVertex2f(-1.0f, 1.0f);
- glTexCoord2f(1.0f, 0.0f);
- glVertex2f(0.0f, 1.0f);
- glTexCoord2f(1.0f, 1.0f);
- glVertex2f(0.0f, -1.0f);
- glTexCoord2f(0.0f, 1.0f);
- glVertex2f(-1.0f, -1.0f);
- glEnd();
- bind_textures(0, right);
- glBegin(GL_QUADS);
- glTexCoord2f(0.0f, 0.0f);
- glVertex2f(0.0f, 1.0f);
- glTexCoord2f(1.0f, 0.0f);
- glVertex2f(1.0f, 1.0f);
- glTexCoord2f(1.0f, 1.0f);
- glVertex2f(1.0f, -1.0f);
- glTexCoord2f(0.0f, 1.0f);
- glVertex2f(0.0f, -1.0f);
- glEnd();
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, _srgb_tex[left]);
+ draw_quad(-1.0f, -1.0f, 1.0f, 2.0f);
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, _srgb_tex[right]);
+ draw_quad(0.0f, -1.0f, 1.0f, 2.0f);
}
else if (mode == top_bottom || mode == top_bottom_half)
{
- bind_textures(0, left);
- glBegin(GL_QUADS);
- glTexCoord2f(0.0f, 0.0f);
- glVertex2f(-1.0f, 1.0f);
- glTexCoord2f(1.0f, 0.0f);
- glVertex2f(1.0f, 1.0f);
- glTexCoord2f(1.0f, 1.0f);
- glVertex2f(1.0f, 0.0f);
- glTexCoord2f(0.0f, 1.0f);
- glVertex2f(-1.0f, 0.0f);
- glEnd();
- bind_textures(0, right);
- glBegin(GL_QUADS);
- glTexCoord2f(0.0f, 0.0f);
- glVertex2f(-1.0f, 0.0f);
- glTexCoord2f(1.0f, 0.0f);
- glVertex2f(1.0f, 0.0f);
- glTexCoord2f(1.0f, 1.0f);
- glVertex2f(1.0f, -1.0f);
- glTexCoord2f(0.0f, 1.0f);
- glVertex2f(-1.0f, -1.0f);
- glEnd();
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, _srgb_tex[left]);
+ draw_quad(-1.0f, 0.0f, 2.0f, 1.0f);
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, _srgb_tex[right]);
+ draw_quad(-1.0f, -1.0f, 2.0f, 1.0f);
}
}
@@ -565,39 +630,41 @@ void video_output_opengl::prepare(
}
_input_is_mono = (l_data[0] == r_data[0] && l_data[1] == r_data[1] && l_data[2] == r_data[2]);
+ /* Step 1: input of video data */
+
glActiveTexture(GL_TEXTURE0);
if (frame_format() == decoder::frame_format_yuv420p)
{
- upload_texture(_y_tex[tex_set][0], _pbo,
+ upload_texture(_yuv420p_y_tex[tex_set][0], _pbo,
_src_width, _src_height, 1, l_line_size[0],
GL_LUMINANCE, GL_UNSIGNED_BYTE, l_data[0]);
- upload_texture(_u_tex[tex_set][0], _pbo,
+ upload_texture(_yuv420p_u_tex[tex_set][0], _pbo,
_src_width / 2, _src_height / 2, 1, l_line_size[1],
GL_LUMINANCE, GL_UNSIGNED_BYTE, l_data[1]);
- upload_texture(_v_tex[tex_set][0], _pbo,
+ upload_texture(_yuv420p_v_tex[tex_set][0], _pbo,
_src_width / 2, _src_height / 2, 1, l_line_size[2],
GL_LUMINANCE, GL_UNSIGNED_BYTE, l_data[2]);
if (!_input_is_mono)
{
- upload_texture(_y_tex[tex_set][1], _pbo,
+ upload_texture(_yuv420p_y_tex[tex_set][1], _pbo,
_src_width, _src_height, 1, r_line_size[0],
GL_LUMINANCE, GL_UNSIGNED_BYTE, r_data[0]);
- upload_texture(_u_tex[tex_set][1], _pbo,
+ upload_texture(_yuv420p_u_tex[tex_set][1], _pbo,
_src_width / 2, _src_height / 2, 1, r_line_size[1],
GL_LUMINANCE, GL_UNSIGNED_BYTE, r_data[1]);
- upload_texture(_v_tex[tex_set][1], _pbo,
+ upload_texture(_yuv420p_v_tex[tex_set][1], _pbo,
_src_width / 2, _src_height / 2, 1, r_line_size[2],
GL_LUMINANCE, GL_UNSIGNED_BYTE, r_data[2]);
}
}
else if (frame_format() == decoder::frame_format_bgra32)
{
- upload_texture(_rgb_tex[tex_set][0], _pbo,
+ upload_texture(_bgra32_tex[tex_set][0], _pbo,
_src_width, _src_height, 4, l_line_size[0],
GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, l_data[0]);
if (!_input_is_mono)
{
- upload_texture(_rgb_tex[tex_set][1], _pbo,
+ upload_texture(_bgra32_tex[tex_set][1], _pbo,
_src_width, _src_height, 4, r_line_size[0],
GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, r_data[0]);
}
View
236 src/video_output_opengl.fs.glsl
@@ -1,236 +0,0 @@
-/*
- * This file is part of bino, a program to play stereoscopic videos.
- *
- * Copyright (C) 2010 Martin Lambers <marlam@marlam.de>
- *
- * This program 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 3 of the License, or
- * (at your option) any later version.
- *
- * This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#version 110
-
-// input_bgra32
-// input_yuv420p
-#define $input
-
-// mode_onechannel
-// mode_anaglyph_monochrome
-// mode_anaglyph_full_color
-// mode_anaglyph_half_color
-// mode_anaglyph_dubois
-// mode_even_odd_rows
-// mode_even_odd_columns
-// mode_checkerboard
-#define $mode
-
-#if defined(input_bgra32)
-uniform sampler2D rgb_l;
-# if !defined(mode_onechannel)
-uniform sampler2D rgb_r;
-# endif
-#elif defined(input_yuv420p)
-uniform sampler2D y_l;
-uniform sampler2D u_l;
-uniform sampler2D v_l;
-# if !defined(mode_onechannel)
-uniform sampler2D y_r;
-uniform sampler2D u_r;
-uniform sampler2D v_r;
-# endif
-#endif
-
-#if defined(mode_even_odd_rows) || defined(mode_even_odd_columns) || defined(mode_checkerboard)
-uniform sampler2D mask_tex;
-uniform float step_x;
-uniform float step_y;
-#endif
-
-uniform float contrast;
-uniform float brightness;
-uniform float saturation;
-uniform float cos_hue;
-uniform float sin_hue;
-
-#if defined(input_bgra32)
-vec3 rgb_to_yuv(vec3 rgb)
-{
- // Values taken from http://www.fourcc.org/fccyvrgb.php
- float y = dot(rgb, vec3(0.299, 0.587, 0.114));
- float u = 0.564 * (rgb.b - y) + 0.5;
- float v = 0.713 * (rgb.r - y) + 0.5;
- return vec3(y, u, v);
-}
-#endif
-
-vec3 yuv_to_rgb(vec3 yuv)
-{
- // Values taken from http://www.fourcc.org/fccyvrgb.php
- float r = yuv.x + 1.403 * (yuv.z - 0.5);
- float g = yuv.x - 0.344 * (yuv.y - 0.5) - 0.714 * (yuv.z - 0.5);
- float b = yuv.x + 1.770 * (yuv.y - 0.5);
- return vec3(r, g, b);
-}
-
-vec3 adjust_yuv(vec3 yuv)
-{
- // Adapted from http://www.silicontrip.net/~mark/lavtools/yuvadjust.c
- // (Copyright 2002 Alfonso Garcia-Patiño Barbolani, released under GPLv2 or later)
-
- // brightness and contrast
- float ay = (yuv.x - 0.5) * (contrast + 1.0) + brightness + 0.5;
- // hue and saturation
- float au = (cos_hue * (yuv.y - 0.5) - sin_hue * (yuv.z - 0.5)) * (saturation + 1.0) + 0.5;
- float av = (sin_hue * (yuv.y - 0.5) + cos_hue * (yuv.z - 0.5)) * (saturation + 1.0) + 0.5;
-
- return vec3(ay, au, av);
-}
-
-vec3 get_yuv_l(vec2 tex_coord)
-{
-#if defined(input_bgra32)
- return rgb_to_yuv(texture2D(rgb_l, tex_coord).xyz);
-#elif defined(input_yuv420p)
- return vec3(
- texture2D(y_l, tex_coord).x,
- texture2D(u_l, tex_coord).x,
- texture2D(v_l, tex_coord).x);
-#endif
-}
-
-#if !defined(mode_onechannel)
-vec3 get_yuv_r(vec2 tex_coord)
-{
-# if defined(input_bgra32)
- return rgb_to_yuv(texture2D(rgb_r, tex_coord).xyz);
-# elif defined(input_yuv420p)
- return vec3(
- texture2D(y_r, tex_coord).x,
- texture2D(u_r, tex_coord).x,
- texture2D(v_r, tex_coord).x);
-# endif
-}
-#endif
-
-void main()
-{
- vec3 rgb;
-
-#if defined(mode_even_odd_rows) || defined(mode_even_odd_columns) || defined(mode_checkerboard)
-
- /* This implementation of the masked modes works around many different problems and therefore may seem strange.
- * Why not use stencil buffers?
- * - Because we want to filter, to account for masked out features
- * - Because stencil did not work with some drivers when switching fullscreen on/off
- * Why not use polygon stipple?
- * - Because we want to filter, to account for masked out features
- * - Because polygon stippling may not be hardware accelerated and is currently broken with many free drivers
- * Why use a mask texture? Why not use the mod() function to check for even/odd pixels?
- * - Because mod() is broken with many drivers, and I found no reliable way to work around it. Some
- * drivers seem to use extremely low precision arithmetic in the shaders; too low for reliable pixel
- * position computations.
- * Why use local variables in the if/else branches, and a local computation of the rgb value?
- * - To work around driver bugs.
- */
- float m = texture2D(mask_tex, gl_TexCoord[1].xy).x;
-# if defined(mode_even_odd_rows)
- if (m > 0.5)
- {
- vec3 yuv0 = get_yuv_l(gl_TexCoord[0].xy - vec2(0.0, step_y));
- vec3 yuv1 = get_yuv_l(gl_TexCoord[0].xy);
- vec3 yuv2 = get_yuv_l(gl_TexCoord[0].xy + vec2(0.0, step_y));
- rgb = yuv_to_rgb(adjust_yuv((yuv0 + 2.0 * yuv1 + yuv2) / 4.0));
- }
- else
- {
- vec3 yuv0 = get_yuv_r(gl_TexCoord[0].xy - vec2(0.0, step_y));
- vec3 yuv1 = get_yuv_r(gl_TexCoord[0].xy);
- vec3 yuv2 = get_yuv_r(gl_TexCoord[0].xy + vec2(0.0, step_y));
- rgb = yuv_to_rgb(adjust_yuv((yuv0 + 2.0 * yuv1 + yuv2) / 4.0));
- }
-# elif defined(mode_even_odd_columns)
- if (m > 0.5)
- {
- vec3 yuv0 = get_yuv_l(gl_TexCoord[0].xy - vec2(step_x, 0.0));
- vec3 yuv1 = get_yuv_l(gl_TexCoord[0].xy);
- vec3 yuv2 = get_yuv_l(gl_TexCoord[0].xy + vec2(step_x, 0.0));
- rgb = yuv_to_rgb(adjust_yuv((yuv0 + 2.0 * yuv1 + yuv2) / 4.0));
- }
- else
- {
- vec3 yuv0 = get_yuv_r(gl_TexCoord[0].xy - vec2(step_x, 0.0));
- vec3 yuv1 = get_yuv_r(gl_TexCoord[0].xy);
- vec3 yuv2 = get_yuv_r(gl_TexCoord[0].xy + vec2(step_x, 0.0));
- rgb = yuv_to_rgb(adjust_yuv((yuv0 + 2.0 * yuv1 + yuv2) / 4.0));
- }
-# elif defined(mode_checkerboard)
- if (m > 0.5)
- {
- vec3 yuv0 = get_yuv_l(gl_TexCoord[0].xy - vec2(0.0, step_y));
- vec3 yuv1 = get_yuv_l(gl_TexCoord[0].xy - vec2(step_x, 0.0));
- vec3 yuv2 = get_yuv_l(gl_TexCoord[0].xy);
- vec3 yuv3 = get_yuv_l(gl_TexCoord[0].xy + vec2(step_x, 0.0));
- vec3 yuv4 = get_yuv_l(gl_TexCoord[0].xy + vec2(0.0, step_y));
- rgb = yuv_to_rgb(adjust_yuv((yuv0 + yuv1 + 4.0 * yuv2 + yuv3 + yuv4) / 8.0));
- }
- else
- {
- vec3 yuv0 = get_yuv_r(gl_TexCoord[0].xy - vec2(0.0, step_y));
- vec3 yuv1 = get_yuv_r(gl_TexCoord[0].xy - vec2(step_x, 0.0));
- vec3 yuv2 = get_yuv_r(gl_TexCoord[0].xy);
- vec3 yuv3 = get_yuv_r(gl_TexCoord[0].xy + vec2(step_x, 0.0));
- vec3 yuv4 = get_yuv_r(gl_TexCoord[0].xy + vec2(0.0, step_y));
- rgb = yuv_to_rgb(adjust_yuv((yuv0 + yuv1 + 4.0 * yuv2 + yuv3 + yuv4) / 8.0));
- }
-# endif
-
-#else
-
- vec3 yuv_l = adjust_yuv(get_yuv_l(gl_TexCoord[0].xy));
-# if !defined(mode_onechannel)
- vec3 yuv_r = adjust_yuv(get_yuv_r(gl_TexCoord[0].xy));
-# endif
-
-# if defined(mode_onechannel)
- rgb = yuv_to_rgb(yuv_l);
-# elif defined(mode_anaglyph_monochrome)
- rgb = vec3(yuv_l.x, yuv_r.x, yuv_r.x);
-# elif defined(mode_anaglyph_full_color)
- rgb = vec3(yuv_to_rgb(yuv_l).r, yuv_to_rgb(yuv_r).gb);
-# elif defined(mode_anaglyph_half_color)
- rgb = vec3(yuv_l.x, yuv_to_rgb(yuv_r).gb);
-# elif defined(mode_anaglyph_dubois)
- // Dubois anaglyph method.
- // Authors page: http://www.site.uottawa.ca/~edubois/anaglyph/
- // This method depends on the characteristics of the display device
- // and the anaglyph glasses.
- // The matrices below are taken from "A Uniform Metric for Anaglyph Calculation"
- // by Zhe Zhang and David F. McAllister, Proc. Electronic Imaging 2006, available
- // here: http://research.csc.ncsu.edu/stereographics/ei06.pdf
- // These matrices are supposed to work fine for LCD monitors and most red-cyan
- // glasses. See also the remarks in http://research.csc.ncsu.edu/stereographics/LS.pdf
- // (where slightly different values are used).
- mat3 m0 = mat3(
- 0.4155, -0.0458, -0.0545,
- 0.4710, -0.0484, -0.0614,
- 0.1670, -0.0258, 0.0128);
- mat3 m1 = mat3(
- -0.0109, 0.3756, -0.0651,
- -0.0365, 0.7333, -0.1286,
- -0.0060, 0.0111, 1.2968);
- rgb = m0 * yuv_to_rgb(yuv_l) + m1 * yuv_to_rgb(yuv_r);
-# endif
-
-#endif
-
- gl_FragColor = vec4(rgb, 1.0);
-}
View
44 src/video_output_opengl.h
@@ -33,31 +33,43 @@
class video_output_opengl : public video_output
{
private:
- enum video_output::mode _mode;
+ // Video properties (fixed during playback)
int _src_width;
int _src_height;
float _src_aspect_ratio;
enum decoder::video_frame_format _src_preferred_frame_format;
+ // Screen properties (fixed during playback)
int _screen_width;
int _screen_height;
float _screen_pixel_aspect_ratio;
+ // Video output mode (fixed during playback)
+ enum video_output::mode _mode;
+ // Video output properties (variable)
+ video_output_state _state;
int _win_width;
int _win_height;
- bool _initialized;
- GLuint _prg;
- GLuint _rgb_tex[2][2];
- GLuint _y_tex[2][2];
- GLuint _u_tex[2][2];
- GLuint _v_tex[2][2];
- GLuint _mask_tex;
- int _active_tex_set;
- bool _input_is_mono;
- video_output_state _state;
- GLuint _pbo;
- bool _have_valid_data[2];
- void bind_textures(int unitset, int index);
- void draw_quad(float x, float y, float w, float h);
+ /* We have two texture sets for input: one holding the current video frame
+ * for output, and one for preparing the next video frame. Each texture set
+ * has textures for the left and right view. */
+ int _active_tex_set; // 0 or 1
+ bool _input_is_mono; // left view == right view
+ bool _have_valid_data[2]; // do we have valid data in the given texture set?
+ // Step 1: input of video data
+ GLuint _pbo; // pixel-buffer object for texture uploading
+ GLuint _yuv420p_y_tex[2][2];// for yuv420p format: y component
+ GLuint _yuv420p_u_tex[2][2];// for yuv420p format: u component
+ GLuint _yuv420p_v_tex[2][2];// for yuv420p format: v component
+ GLuint _bgra32_tex[2][2]; // for bgra32 format
+ // Step 2: color-correction
+ GLuint _color_prg; // color space transformation, color adjustment
+ GLuint _color_fbo; // framebuffer object to render into the sRGB texture
+ GLuint _srgb_tex[2]; // output: sRGB texture
+ // Step 3: rendering
+ GLuint _render_prg; // reads sRGB texture, renders according to _mode
+ GLuint _mask_tex; // for the masking modes even-odd-{rows,columns}, checkerboard
+ // Is the GL stuff initialized?
+ bool _initialized;
protected:
/* In a sub-class that provides an OpenGL context, call the following
@@ -72,7 +84,7 @@ class video_output_opengl : public video_output
// Swap the texture sets (one active, one for preparing the next video frame)
void swap_tex_set();
- // Display the current texture
+ // Display the current texture set
void display(enum video_output::mode mode, float x, float y, float w, float h);
void display() { display(_mode, -1.0f, -1.0f, 2.0f, 2.0f); }
// Call this when the GL window was resized:
View
92 src/video_output_opengl_color.fs.glsl
@@ -0,0 +1,92 @@
+/*
+ * This file is part of bino, a program to play stereoscopic videos.
+ *
+ * Copyright (C) 2010 Martin Lambers <marlam@marlam.de>
+ *
+ * This program 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#version 120
+
+// input_bgra32
+// input_yuv420p
+#define $input
+
+#if defined(input_yuv420p)
+uniform sampler2D y_tex;
+uniform sampler2D u_tex;
+uniform sampler2D v_tex;
+#elif defined(input_bgra32)
+uniform sampler2D srgb_tex;
+#endif
+
+uniform float contrast;
+uniform float brightness;
+uniform float saturation;
+uniform float cos_hue;
+uniform float sin_hue;
+
+#if defined(input_bgra32)
+vec3 srgb_to_yuv(vec3 srgb)
+{
+ // Values taken from http://www.fourcc.org/fccyvrgb.php
+ float y = dot(srgb, vec3(0.299, 0.587, 0.114));
+ float u = 0.564 * (srgb.b - y) + 0.5;
+ float v = 0.713 * (srgb.r - y) + 0.5;
+ return vec3(y, u, v);
+}
+#endif
+
+vec3 yuv_to_srgb(vec3 yuv)
+{
+ // Values taken from http://www.fourcc.org/fccyvrgb.php
+ float r = yuv.x + 1.403 * (yuv.z - 0.5);
+ float g = yuv.x - 0.344 * (yuv.y - 0.5) - 0.714 * (yuv.z - 0.5);
+ float b = yuv.x + 1.770 * (yuv.y - 0.5);
+ return vec3(r, g, b);
+}
+
+vec3 adjust_yuv(vec3 yuv)
+{
+ // Adapted from http://www.silicontrip.net/~mark/lavtools/yuvadjust.c
+ // (Copyright 2002 Alfonso Garcia-Patiño Barbolani, released under GPLv2 or later)
+
+ // brightness and contrast
+ float ay = (yuv.x - 0.5) * (contrast + 1.0) + brightness + 0.5;
+ // hue and saturation
+ float au = (cos_hue * (yuv.y - 0.5) - sin_hue * (yuv.z - 0.5)) * (saturation + 1.0) + 0.5;
+ float av = (sin_hue * (yuv.y - 0.5) + cos_hue * (yuv.z - 0.5)) * (saturation + 1.0) + 0.5;
+
+ return vec3(ay, au, av);
+}
+
+vec3 get_yuv(vec2 tex_coord)
+{
+#if defined(input_bgra32)
+ return srgb_to_yuv(texture2D(srgb_tex, tex_coord).xyz);
+#elif defined(input_yuv420p)
+ return vec3(
+ texture2D(y_tex, tex_coord).x,
+ texture2D(u_tex, tex_coord).x,
+ texture2D(v_tex, tex_coord).x);
+#endif
+}
+
+void main()
+{
+ vec3 yuv = get_yuv(gl_TexCoord[0].xy);
+ vec3 adjusted_yuv = adjust_yuv(yuv);
+ vec3 srgb = yuv_to_srgb(adjusted_yuv);
+ gl_FragColor = vec4(srgb, 1.0);
+}
View
180 src/video_output_opengl_render.fs.glsl
@@ -0,0 +1,180 @@
+/*
+ * This file is part of bino, a program to play stereoscopic videos.
+ *
+ * Copyright (C) 2010 Martin Lambers <marlam@marlam.de>
+ *
+ * This program 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#version 120
+
+// mode_onechannel
+// mode_anaglyph_monochrome
+// mode_anaglyph_full_color
+// mode_anaglyph_half_color
+// mode_anaglyph_dubois
+// mode_even_odd_rows
+// mode_even_odd_columns
+// mode_checkerboard
+#define $mode
+
+uniform sampler2D rgb_l;
+uniform sampler2D rgb_r;
+
+#if defined(mode_even_odd_rows) || defined(mode_even_odd_columns) || defined(mode_checkerboard)
+uniform sampler2D mask_tex;
+uniform float step_x;
+uniform float step_y;
+#endif
+
+#if defined(mode_anaglyph_monochrome) || defined(mode_anaglyph_half_color)
+float srgb_to_lum(vec3 srgb)
+{
+ // Values taken from http://www.fourcc.org/fccyvrgb.php
+ return dot(srgb, vec3(0.299, 0.587, 0.114));
+}
+#endif
+
+vec3 rgb_to_srgb(vec3 rgb)
+{
+#if 1
+ // Correct variant, see GL_ARB_framebuffer_sRGB extension
+ return vec3(
+ (rgb.r <= 0.0031308 ? (rgb.r * 12.92) : (1.055 * pow(rgb.r, 1.0 / 2.4) - 0.055)),
+ (rgb.g <= 0.0031308 ? (rgb.g * 12.92) : (1.055 * pow(rgb.g, 1.0 / 2.4) - 0.055)),
+ (rgb.b <= 0.0031308 ? (rgb.b * 12.92) : (1.055 * pow(rgb.b, 1.0 / 2.4) - 0.055)));
+#endif
+#if 0
+ // Faster variant
+ return pow(rgb, 1.0 / 2.2);
+#endif
+#if 0
+ // Even faster variant, assuming gamma = 2.0
+ return sqrt(rgb);
+#endif
+}
+
+void main()
+{
+ vec3 srgb;
+
+#if defined(mode_even_odd_rows) || defined(mode_even_odd_columns) || defined(mode_checkerboard)
+
+ /* This implementation of the masked modes works around many different problems and therefore may seem strange.
+ * Why not use stencil buffers?
+ * - Because we want to filter, to account for masked out features
+ * - Because stencil did not work with some drivers when switching fullscreen on/off
+ * Why not use polygon stipple?
+ * - Because we want to filter, to account for masked out features
+ * - Because polygon stippling may not be hardware accelerated and is currently broken with many free drivers
+ * Why use a mask texture? Why not use the mod() function to check for even/odd pixels?
+ * - Because mod() is broken with many drivers, and I found no reliable way to work around it. Some
+ * drivers seem to use extremely low precision arithmetic in the shaders; too low for reliable pixel
+ * position computations.
+ * Why use local variables in the if/else branches, and a local computation of the srgb value?
+ * - To work around driver bugs.
+ */
+ float m = texture2D(mask_tex, gl_TexCoord[1].xy).x;
+# if defined(mode_even_odd_rows)
+ if (m > 0.5)
+ {
+ vec3 rgb0 = texture2D(rgb_l, gl_TexCoord[0].xy - vec2(0.0, step_y)).rgb;
+ vec3 rgb1 = texture2D(rgb_l, gl_TexCoord[0].xy).rgb;
+ vec3 rgb2 = texture2D(rgb_l, gl_TexCoord[0].xy + vec2(0.0, step_y)).rgb;
+ srgb = rgb_to_srgb((rgb0 + 2.0 * rgb1 + rgb2) / 4.0);
+ }
+ else
+ {
+ vec3 rgb0 = texture2D(rgb_r, gl_TexCoord[0].xy - vec2(0.0, step_y)).rgb;
+ vec3 rgb1 = texture2D(rgb_r, gl_TexCoord[0].xy).rgb;
+ vec3 rgb2 = texture2D(rgb_r, gl_TexCoord[0].xy + vec2(0.0, step_y)).rgb;
+ srgb = rgb_to_srgb((rgb0 + 2.0 * rgb1 + rgb2) / 4.0);
+ }
+# elif defined(mode_even_odd_columns)
+ if (m > 0.5)
+ {
+ vec3 rgb0 = texture2D(rgb_l, gl_TexCoord[0].xy - vec2(step_x, 0.0)).rgb;
+ vec3 rgb1 = texture2D(rgb_l, gl_TexCoord[0].xy).rgb;
+ vec3 rgb2 = texture2D(rgb_l, gl_TexCoord[0].xy + vec2(step_x, 0.0)).rgb;
+ srgb = rgb_to_srgb((rgb0 + 2.0 * rgb1 + rgb2) / 4.0);
+ }
+ else
+ {
+ vec3 rgb0 = texture2D(rgb_r, gl_TexCoord[0].xy - vec2(step_x, 0.0)).rgb;
+ vec3 rgb1 = texture2D(rgb_r, gl_TexCoord[0].xy).rgb;
+ vec3 rgb2 = texture2D(rgb_r, gl_TexCoord[0].xy + vec2(step_x, 0.0)).rgb;
+ srgb = rgb_to_srgb((rgb0 + 2.0 * rgb1 + rgb2) / 4.0);
+ }
+# elif defined(mode_checkerboard)
+ if (m > 0.5)
+ {
+ vec3 rgb0 = texture2D(rgb_l, gl_TexCoord[0].xy - vec2(0.0, step_y)).rgb;
+ vec3 rgb1 = texture2D(rgb_l, gl_TexCoord[0].xy - vec2(step_x, 0.0)).rgb;
+ vec3 rgb2 = texture2D(rgb_l, gl_TexCoord[0].xy).rgb;
+ vec3 rgb3 = texture2D(rgb_l, gl_TexCoord[0].xy + vec2(step_x, 0.0)).rgb;
+ vec3 rgb4 = texture2D(rgb_l, gl_TexCoord[0].xy + vec2(0.0, step_y)).rgb;
+ srgb = rgb_to_srgb((rgb0 + rgb1 + 4.0 * rgb2 + rgb3 + rgb4) / 8.0);
+ }
+ else
+ {
+ vec3 rgb0 = texture2D(rgb_r, gl_TexCoord[0].xy - vec2(0.0, step_y)).rgb;
+ vec3 rgb1 = texture2D(rgb_r, gl_TexCoord[0].xy - vec2(step_x, 0.0)).rgb;
+ vec3 rgb2 = texture2D(rgb_r, gl_TexCoord[0].xy).rgb;
+ vec3 rgb3 = texture2D(rgb_r, gl_TexCoord[0].xy + vec2(step_x, 0.0)).rgb;
+ vec3 rgb4 = texture2D(rgb_r, gl_TexCoord[0].xy + vec2(0.0, step_y)).rgb;
+ srgb = rgb_to_srgb((rgb0 + rgb1 + 4.0 * rgb2 + rgb3 + rgb4) / 8.0);
+ }
+# endif
+
+#else
+
+ vec3 l = rgb_to_srgb(texture2D(rgb_l, gl_TexCoord[0].xy).rgb);
+# if !defined(mode_onechannel)
+ vec3 r = rgb_to_srgb(texture2D(rgb_r, gl_TexCoord[0].xy).rgb);
+# endif
+
+# if defined(mode_onechannel)
+ srgb = l;
+# elif defined(mode_anaglyph_monochrome)
+ srgb = vec3(srgb_to_lum(l), srgb_to_lum(r), srgb_to_lum(r));
+# elif defined(mode_anaglyph_full_color)
+ srgb = vec3(l.r, r.gb);
+# elif defined(mode_anaglyph_half_color)
+ srgb = vec3(srgb_to_lum(l), r.gb);
+# elif defined(mode_anaglyph_dubois)
+ // Dubois anaglyph method.
+ // Authors page: http://www.site.uottawa.ca/~edubois/anaglyph/
+ // This method depends on the characteristics of the display device
+ // and the anaglyph glasses.
+ // The matrices below are taken from "A Uniform Metric for Anaglyph Calculation"
+ // by Zhe Zhang and David F. McAllister, Proc. Electronic Imaging 2006, available
+ // here: http://research.csc.ncsu.edu/stereographics/ei06.pdf
+ // These matrices are supposed to work fine for LCD monitors and most red-cyan
+ // glasses. See also the remarks in http://research.csc.ncsu.edu/stereographics/LS.pdf
+ // (where slightly different values are used).
+ mat3 m0 = mat3(
+ 0.4155, -0.0458, -0.0545,
+ 0.4710, -0.0484, -0.0614,
+ 0.1670, -0.0258, 0.0128);
+ mat3 m1 = mat3(
+ -0.0109, 0.3756, -0.0651,
+ -0.0365, 0.7333, -0.1286,
+ -0.0060, 0.0111, 1.2968);
+ srgb = m0 * l + m1 * r;
+# endif
+
+#endif
+
+ gl_FragColor = vec4(srgb, 1.0);
+}
Please sign in to comment.
Something went wrong with that request. Please try again.