GLVideoView::GLVideoView (FilmViewer* viewer, wxWindow *parent)
: VideoView (viewer)
, _context (nullptr)
- , _have_storage (false)
, _vsync_enabled (false)
, _playing (false)
, _one_shot (false)
_thread.interrupt ();
_thread.join ();
} catch (...) {}
-
- glDeleteTextures (1, &_texture);
}
void
"in vec2 TexCoord;\n"
"\n"
"uniform sampler2D texture_sampler;\n"
-"uniform int draw_border;\n"
+/* type = 0: draw border
+ * type = 1: draw XYZ image
+ * type = 2: draw RGB image
+ */
+"uniform int type = 0;\n"
"uniform vec4 border_colour;\n"
+"uniform mat4 colour_conversion;\n"
"\n"
"out vec4 FragColor;\n"
"\n"
"vec4 cubic(float x)\n"
+"\n"
+"#define IN_GAMMA 2.2\n"
+"#define OUT_GAMMA 0.384615385\n" // 1 / 2.6
+"#define DCI_COEFFICIENT 0.91655528\n" // 48 / 53.37
+"\n"
"{\n"
" float x2 = x * x;\n"
" float x3 = x2 * x;\n"
"\n"
"void main()\n"
"{\n"
-" if (draw_border == 1) {\n"
-" FragColor = border_colour;\n"
-" } else {\n"
-" FragColor = texture_bicubic(texture_sampler, TexCoord);\n"
+" switch (type) {\n"
+" case 0:\n"
+" FragColor = border_colour;\n"
+" break;\n"
+" case 1:\n"
+" FragColor = texture_bicubic(texture_sampler, TexCoord);\n"
+" FragColor.x = pow(FragColor.x, IN_GAMMA) / DCI_COEFFICIENT;\n"
+" FragColor.y = pow(FragColor.y, IN_GAMMA) / DCI_COEFFICIENT;\n"
+" FragColor.z = pow(FragColor.z, IN_GAMMA) / DCI_COEFFICIENT;\n"
+" FragColor = colour_conversion * FragColor;\n"
+" FragColor.x = pow(FragColor.x, OUT_GAMMA);\n"
+" FragColor.y = pow(FragColor.y, OUT_GAMMA);\n"
+" FragColor.z = pow(FragColor.z, OUT_GAMMA);\n"
+" break;\n"
+" case 2:\n"
+" FragColor = texture_bicubic(texture_sampler, TexCoord);\n"
+" break;\n"
" }\n"
"}\n";
}
}
+
+/* Offset of video texture triangles in indices */
+static constexpr int indices_video_texture = 0;
+/* Offset of border lines in indices */
+static constexpr int indices_border = 6;
+
+static constexpr unsigned int indices[] = {
+ 0, 1, 3, // video texture triangle #1
+ 1, 2, 3, // video texture triangle #2
+ 4, 5, // border line #1
+ 5, 6, // border line #2
+ 6, 7, // border line #3
+ 7, 4, // border line #4
+};
+
+
void
GLVideoView::setup_shaders ()
{
get_information (GL_VERSION);
get_information (GL_SHADING_LANGUAGE_VERSION);
- unsigned int indices[] = {
- 0, 1, 3, // texture triangle #1
- 1, 2, 3, // texture triangle #2
- 4, 5, // border line #1
- 5, 6, // border line #2
- 6, 7, // border line #3
- 7, 4, // border line #4
- };
-
glGenVertexArrays(1, &_vao);
check_gl_error ("glGenVertexArrays");
GLuint vbo;
glUseProgram (program);
- _draw_border = glGetUniformLocation (program, "draw_border");
+ _fragment_type = glGetUniformLocation (program, "type");
check_gl_error ("glGetUniformLocation");
set_border_colour (program);
+ auto conversion = dcp::ColourConversion::rec709_to_xyz();
+ boost::numeric::ublas::matrix<double> matrix = conversion.xyz_to_rgb ();
+ GLfloat gl_matrix[] = {
+ static_cast<float>(matrix(0, 0)), static_cast<float>(matrix(0, 1)), static_cast<float>(matrix(0, 2)), 0.0f,
+ static_cast<float>(matrix(1, 0)), static_cast<float>(matrix(1, 1)), static_cast<float>(matrix(1, 2)), 0.0f,
+ static_cast<float>(matrix(2, 0)), static_cast<float>(matrix(2, 1)), static_cast<float>(matrix(2, 2)), 0.0f,
+ 0.0f, 0.0f, 0.0f, 1.0f
+ };
+
+ auto colour_conversion = glGetUniformLocation (program, "colour_conversion");
+ check_gl_error ("glGetUniformLocation");
+ glUniformMatrix4fv (colour_conversion, 1, GL_TRUE, gl_matrix);
+
glLineWidth (2.0f);
}
glViewport (0, 0, width, height);
check_gl_error ("glViewport");
- glBindTexture(GL_TEXTURE_2D, _texture);
glBindVertexArray(_vao);
check_gl_error ("glBindVertexArray");
- glUniform1i(_draw_border, 0);
- glDrawElements (GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
+ glUniform1i(_fragment_type, _optimise_for_j2k ? 1 : 2);
+ glDrawElements (GL_TRIANGLES, 6, GL_UNSIGNED_INT, reinterpret_cast<void*>(indices_video_texture));
if (_viewer->outline_content()) {
- glUniform1i(_draw_border, 1);
- glDrawElements (GL_LINES, 8, GL_UNSIGNED_INT, reinterpret_cast<void*>(6 * sizeof(int)));
+ glUniform1i(_fragment_type, 1);
+ glDrawElements (GL_LINES, 8, GL_UNSIGNED_INT, reinterpret_cast<void*>(indices_border * sizeof(int)));
check_gl_error ("glDrawElements");
}
void
-GLVideoView::set_image (shared_ptr<const Image> image)
+GLVideoView::set_image (shared_ptr<const PlayerVideo> pv)
{
- if (!image) {
- _size = optional<dcp::Size>();
- return;
- }
-
+ auto image = _optimise_for_j2k ? pv->raw_image() : pv->image(bind(&PlayerVideo::force, _1, AV_PIX_FMT_RGB24), VideoRange::FULL, false, true);
- DCPOMATIC_ASSERT (image->pixel_format() == AV_PIX_FMT_RGB24);
DCPOMATIC_ASSERT (!image->aligned());
- if (image->size() != _size) {
- _have_storage = false;
- }
+ /** If _optimise_for_j2k is true we render a XYZ image, doing the colourspace
+ * conversion, scaling and video range conversion in the GL shader.
+ * Othewise we render a RGB image without any shader-side processing.
+ */
- _size = image->size ();
- glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
- check_gl_error ("glPixelStorei");
+ /* XXX: video range conversion */
+ /* XXX: subs */
- if (_have_storage) {
- glTexSubImage2D (GL_TEXTURE_2D, 0, 0, 0, _size->width, _size->height, GL_RGB, GL_UNSIGNED_BYTE, image->data()[0]);
- check_gl_error ("glTexSubImage2D");
- } else {
- glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA8, _size->width, _size->height, 0, GL_RGB, GL_UNSIGNED_BYTE, image->data()[0]);
- check_gl_error ("glTexImage2D");
+ auto const changed = _video_texture->set (image);
+ if (changed) {
auto const canvas_size = _canvas_size.load();
int const canvas_width = canvas_size.GetWidth();
int const canvas_height = canvas_size.GetHeight();
- float const image_x = float(_size->width) / canvas_width;
- float const image_y = float(_size->height) / canvas_height;
+ float const image_x = float(image->size().width) / canvas_width;
+ float const image_y = float(image->size().height) / canvas_height;
auto x_pixels_to_gl = [canvas_width](int x) {
return (x * 2.0f / canvas_width) - 1.0f;
float vertices[] = {
// positions // texture coords
- image_x, image_y, 0.0f, 1.0f, 0.0f, // top right (index 0)
- image_x, -image_y, 0.0f, 1.0f, 1.0f, // bottom right (index 1)
- -image_x, -image_y, 0.0f, 0.0f, 1.0f, // bottom left (index 2)
- -image_x, image_y, 0.0f, 0.0f, 0.0f, // top left (index 3)
- border_x1, border_y1, 0.0f, 0.0f, 0.0f, // border bottom left (index 4)
- border_x1, border_y2, 0.0f, 0.0f, 0.0f, // border top left (index 5)
- border_x2, border_y2, 0.0f, 0.0f, 0.0f, // border top right (index 6)
- border_x2, border_y1, 0.0f, 0.0f, 0.0f, // border bottom right (index 7)
+ image_x, image_y, 0.0f, 1.0f, 0.0f, // video texture top right (index 0)
+ image_x, -image_y, 0.0f, 1.0f, 1.0f, // video texture bottom right (index 1)
+ -image_x, -image_y, 0.0f, 0.0f, 1.0f, // video texture bottom left (index 2)
+ -image_x, image_y, 0.0f, 0.0f, 0.0f, // video texture top left (index 3)
+ border_x1, border_y1, 0.0f, 0.0f, 0.0f, // border bottom left (index 4)
+ border_x1, border_y2, 0.0f, 0.0f, 0.0f, // border top left (index 5)
+ border_x2, border_y2, 0.0f, 0.0f, 0.0f, // border top right (index 6)
+ border_x2, border_y1, 0.0f, 0.0f, 0.0f, // border bottom right (index 7)
};
/* Set the vertex shader's input data (GL_ARRAY_BUFFER) */
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
check_gl_error ("glBufferData");
-
- _have_storage = true;
}
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
check_gl_error ("glTexParameterf");
}
+
void
GLVideoView::start ()
{
{
auto pv = player_video().first;
if (pv) {
- set_image (pv->image(bind(&PlayerVideo::force, _1, AV_PIX_FMT_RGB24), VideoRange::FULL, false, true));
+ set_image (pv);
draw (pv->inter_position(), pv->inter_size());
_viewer->image_changed (pv);
}
_vsync_enabled = true;
#endif
- glGenTextures (1, &_texture);
- check_gl_error ("glGenTextures");
- glBindTexture (GL_TEXTURE_2D, _texture);
- check_gl_error ("glBindTexture");
+ _video_texture.reset(new Texture(_optimise_for_j2k ? 2 : 1));
+ _video_texture->bind();
while (true) {
boost::mutex::scoped_lock lm (_playing_mutex);
_thread_work_condition.notify_all ();
}
+
+Texture::Texture (GLint unpack_alignment)
+ : _unpack_alignment (unpack_alignment)
+{
+ glGenTextures (1, &_name);
+ check_gl_error ("glGenTextures");
+}
+
+
+Texture::~Texture ()
+{
+ glDeleteTextures (1, &_name);
+}
+
+
+void
+Texture::bind ()
+{
+ glBindTexture(GL_TEXTURE_2D, _name);
+ check_gl_error ("glBindTexture");
+}
+
+
+bool
+Texture::set (shared_ptr<const Image> image)
+{
+ auto const create = !_size || image->size() != _size;
+ _size = image->size();
+
+ glPixelStorei (GL_UNPACK_ALIGNMENT, _unpack_alignment);
+ check_gl_error ("glPixelStorei");
+
+ auto const format = image->pixel_format() == AV_PIX_FMT_RGB24 ? GL_UNSIGNED_BYTE : GL_UNSIGNED_SHORT;
+ auto const internal_format = image->pixel_format() == AV_PIX_FMT_RGB24 ? GL_RGBA8 : GL_RGBA12;
+
+ if (create) {
+ glTexImage2D (GL_TEXTURE_2D, 0, internal_format, _size->width, _size->height, 0, GL_RGB, format, image->data()[0]);
+ check_gl_error ("glTexImage2D");
+ } else {
+ glTexSubImage2D (GL_TEXTURE_2D, 0, 0, 0, _size->width, _size->height, GL_RGB, format, image->data()[0]);
+ check_gl_error ("glTexSubImage2D");
+ }
+
+ return create;
+}
+