X-Git-Url: https://git.carlh.net/gitweb/?a=blobdiff_plain;f=src%2Fwx%2Fgl_video_view.cc;h=397693f51f1fbd7afe26a5fb796f788e60488564;hb=d0188ed3e1ea5536e729e93b87d54114368120ac;hp=559cff4136c1bfea40459e4b2892ecb2abe5d7be;hpb=081525ef1c392465fc5a0bb4be79af8e84655bc4;p=dcpomatic.git diff --git a/src/wx/gl_video_view.cc b/src/wx/gl_video_view.cc index 559cff413..397693f51 100644 --- a/src/wx/gl_video_view.cc +++ b/src/wx/gl_video_view.cc @@ -30,12 +30,13 @@ #include "film_viewer.h" #include "wx_util.h" -#include "lib/image.h" +#include "lib/butler.h" +#include "lib/cross.h" #include "lib/dcpomatic_assert.h" +#include "lib/dcpomatic_log.h" #include "lib/exceptions.h" -#include "lib/cross.h" +#include "lib/image.h" #include "lib/player_video.h" -#include "lib/butler.h" #include #include @@ -78,6 +79,7 @@ check_gl_error (char const * last) GLVideoView::GLVideoView (FilmViewer* viewer, wxWindow *parent) : VideoView (viewer) , _context (nullptr) + , _rec2020(false) , _vsync_enabled (false) , _playing (false) , _one_shot (false) @@ -193,20 +195,25 @@ static constexpr char fragment_source[] = "in vec2 TexCoord;\n" "\n" "uniform sampler2D texture_sampler;\n" -/* type = 0: draw border - * type = 1: draw XYZ image - * type = 2: draw RGB image +/* type = 0: draw outline content rectangle + * type = 1: draw crop guess rectangle + * type = 2: draw XYZ image + * type = 3: draw RGB image (with sRGB/Rec709 primaries) + * type = 4: draw RGB image (converting from Rec2020 primaries) + * See FragmentType enum below. */ "uniform int type = 0;\n" -"uniform vec4 border_colour;\n" -"uniform mat4 colour_conversion;\n" +"uniform vec4 outline_content_colour;\n" +"uniform vec4 crop_guess_colour;\n" +"uniform mat4 xyz_rec709_colour_conversion;\n" +"uniform mat4 rec2020_rec709_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 IN_GAMMA 2.6\n" +"#define OUT_GAMMA 0.454545455\n" // 1 / 2.2 "#define DCI_COEFFICIENT 0.91655528\n" // 48 / 53.37 "\n" "{\n" @@ -257,25 +264,42 @@ static constexpr char fragment_source[] = "{\n" " switch (type) {\n" " case 0:\n" -" FragColor = border_colour;\n" +" FragColor = outline_content_colour;\n" " break;\n" " case 1:\n" +" FragColor = crop_guess_colour;\n" +" break;\n" +" case 2:\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 = xyz_rec709_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" +" case 3:\n" " FragColor = texture_bicubic(texture_sampler, TexCoord);\n" +" break;\n" +" case 4:\n" +" FragColor = texture_bicubic(texture_sampler, TexCoord);\n" +" FragColor = rec2020_rec709_colour_conversion * FragColor;\n" " break;\n" " }\n" "}\n"; +enum class FragmentType +{ + OUTLINE_CONTENT = 0, + CROP_GUESS = 1, + XYZ_IMAGE = 2, + REC709_IMAGE = 3, + REC2020_IMAGE = 4, +}; + + void GLVideoView::ensure_context () { @@ -290,24 +314,37 @@ GLVideoView::ensure_context () } -/* Offset of video texture triangles in indices */ -static constexpr int indices_video_texture = 0; -/* Offset of subtitle texture triangles in indices */ -static constexpr int indices_subtitle_texture = 6; -/* Offset of border lines in indices */ -static constexpr int indices_border = 12; +/* Offset and number of indices for the things in the indices array below */ +static constexpr int indices_video_texture_offset = 0; +static constexpr int indices_video_texture_number = 6; +static constexpr int indices_subtitle_texture_offset = indices_video_texture_offset + indices_video_texture_number; +static constexpr int indices_subtitle_texture_number = 6; +static constexpr int indices_outline_content_offset = indices_subtitle_texture_offset + indices_subtitle_texture_number; +static constexpr int indices_outline_content_number = 8; +static constexpr int indices_crop_guess_offset = indices_outline_content_offset + indices_outline_content_number; +static constexpr int indices_crop_guess_number = 8; static constexpr unsigned int indices[] = { 0, 1, 3, // video texture triangle #1 1, 2, 3, // video texture triangle #2 4, 5, 7, // subtitle texture triangle #1 5, 6, 7, // subtitle texture triangle #2 - 8, 9, // border line #1 - 9, 10, // border line #2 - 10, 11, // border line #3 - 11, 8, // border line #4 + 8, 9, // outline content line #1 + 9, 10, // outline content line #2 + 10, 11, // outline content line #3 + 11, 8, // outline content line #4 + 12, 13, // crop guess line #1 + 13, 14, // crop guess line #2 + 14, 15, // crop guess line #3 + 15, 12, // crop guess line #4 }; +/* Offsets of things in the GL_ARRAY_BUFFER */ +static constexpr int array_buffer_video_offset = 0; +static constexpr int array_buffer_subtitle_offset = array_buffer_video_offset + 4 * 5 * sizeof(float); +static constexpr int array_buffer_outline_content_offset = array_buffer_subtitle_offset + 4 * 5 * sizeof(float); +static constexpr int array_buffer_crop_guess_offset = array_buffer_outline_content_offset + 4 * 5 * sizeof(float); + void GLVideoView::setup_shaders () @@ -379,10 +416,9 @@ GLVideoView::setup_shaders () glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &log_length); string log; if (log_length > 0) { - char* log_char = new char[log_length]; - glGetShaderInfoLog(shader, log_length, nullptr, log_char); - log = string(log_char); - delete[] log_char; + std::vector log_char(log_length); + glGetShaderInfoLog(shader, log_length, nullptr, log_char.data()); + log = string(log_char.data()); } glDeleteShader(shader); throw GLError(String::compose("Could not compile shader (%1)", log).c_str(), -1); @@ -408,10 +444,9 @@ GLVideoView::setup_shaders () glGetProgramiv(program, GL_INFO_LOG_LENGTH, &log_length); string log; if (log_length > 0) { - char* log_char = new char[log_length]; - glGetProgramInfoLog(program, log_length, nullptr, log_char); - log = string(log_char); - delete[] log_char; + std::vector log_char(log_length); + glGetProgramInfoLog(program, log_length, nullptr, log_char.data()); + log = string(log_char.data()); } glDeleteProgram (program); throw GLError(String::compose("Could not link shader (%1)", log).c_str(), -1); @@ -423,20 +458,51 @@ GLVideoView::setup_shaders () _fragment_type = glGetUniformLocation (program, "type"); check_gl_error ("glGetUniformLocation"); - set_border_colour (program); - - auto conversion = dcp::ColourConversion::rec709_to_xyz(); - boost::numeric::ublas::matrix matrix = conversion.xyz_to_rgb (); - GLfloat gl_matrix[] = { - static_cast(matrix(0, 0)), static_cast(matrix(0, 1)), static_cast(matrix(0, 2)), 0.0f, - static_cast(matrix(1, 0)), static_cast(matrix(1, 1)), static_cast(matrix(1, 2)), 0.0f, - static_cast(matrix(2, 0)), static_cast(matrix(2, 1)), static_cast(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); + set_outline_content_colour (program); + set_crop_guess_colour (program); + + auto ublas_to_gl = [](boost::numeric::ublas::matrix const& ublas, GLfloat* gl) { + gl[0] = static_cast(ublas(0, 0)); + gl[1] = static_cast(ublas(0, 1)); + gl[2] = static_cast(ublas(0, 2)); + gl[3] = 0.0f; + gl[4] = static_cast(ublas(1, 0)); + gl[5] = static_cast(ublas(1, 1)); + gl[6] = static_cast(ublas(1, 2)); + gl[7] = 0.0f; + gl[8] = static_cast(ublas(2, 0)); + gl[9] = static_cast(ublas(2, 1)); + gl[10] = static_cast(ublas(2, 2)); + gl[11] = 0.0f; + gl[12] = 0.0f; + gl[13] = 0.0f; + gl[14] = 0.0f; + gl[15] = 1.0f; + }; + + { + auto conversion = dcp::ColourConversion::rec709_to_xyz(); + boost::numeric::ublas::matrix matrix = conversion.xyz_to_rgb (); + GLfloat gl_matrix[16]; + ublas_to_gl(matrix, gl_matrix); + + auto xyz_rec709_colour_conversion = glGetUniformLocation(program, "xyz_rec709_colour_conversion"); + check_gl_error ("glGetUniformLocation"); + glUniformMatrix4fv(xyz_rec709_colour_conversion, 1, GL_TRUE, gl_matrix); + } + + { + auto xyz_rec709 = dcp::ColourConversion::rec709_to_xyz().xyz_to_rgb(); + auto rec2020_xyz = dcp::ColourConversion::rec2020_to_xyz().rgb_to_xyz(); + auto product = boost::numeric::ublas::prod(xyz_rec709, rec2020_xyz); + + GLfloat gl_matrix[16]; + ublas_to_gl(product, gl_matrix); + + auto rec2020_rec709_colour_conversion = glGetUniformLocation(program, "rec2020_rec709_colour_conversion"); + check_gl_error("glGetUniformLocation"); + glUniformMatrix4fv(rec2020_rec709_colour_conversion, 1, GL_TRUE, gl_matrix); + } glLineWidth (1.0f); check_gl_error ("glLineWidth"); @@ -446,15 +512,15 @@ GLVideoView::setup_shaders () check_gl_error ("glBlendFunc"); /* Reserve space for the GL_ARRAY_BUFFER */ - glBufferData(GL_ARRAY_BUFFER, 12 * 5 * sizeof(float), nullptr, GL_STATIC_DRAW); + glBufferData(GL_ARRAY_BUFFER, 16 * 5 * sizeof(float), nullptr, GL_STATIC_DRAW); check_gl_error ("glBufferData"); } void -GLVideoView::set_border_colour (GLuint program) +GLVideoView::set_outline_content_colour (GLuint program) { - auto uniform = glGetUniformLocation (program, "border_colour"); + auto uniform = glGetUniformLocation (program, "outline_content_colour"); check_gl_error ("glGetUniformLocation"); auto colour = outline_content_colour (); glUniform4f (uniform, colour.Red() / 255.0f, colour.Green() / 255.0f, colour.Blue() / 255.0f, 1.0f); @@ -462,6 +528,17 @@ GLVideoView::set_border_colour (GLuint program) } +void +GLVideoView::set_crop_guess_colour (GLuint program) +{ + auto uniform = glGetUniformLocation (program, "crop_guess_colour"); + check_gl_error ("glGetUniformLocation"); + auto colour = crop_guess_colour (); + glUniform4f (uniform, colour.Red() / 255.0f, colour.Green() / 255.0f, colour.Blue() / 255.0f, 1.0f); + check_gl_error ("glUniform4f"); +} + + void GLVideoView::draw () { @@ -483,17 +560,28 @@ GLVideoView::draw () glBindVertexArray(_vao); check_gl_error ("glBindVertexArray"); - glUniform1i(_fragment_type, _optimise_for_j2k ? 1 : 2); + if (_optimise_for_j2k) { + glUniform1i(_fragment_type, static_cast(FragmentType::XYZ_IMAGE)); + } else if (_rec2020) { + glUniform1i(_fragment_type, static_cast(FragmentType::REC2020_IMAGE)); + } else { + glUniform1i(_fragment_type, static_cast(FragmentType::REC709_IMAGE)); + } _video_texture->bind(); - glDrawElements (GL_TRIANGLES, 6, GL_UNSIGNED_INT, reinterpret_cast(indices_video_texture * sizeof(int))); + glDrawElements (GL_TRIANGLES, indices_video_texture_number, GL_UNSIGNED_INT, reinterpret_cast(indices_video_texture_offset * sizeof(int))); if (_have_subtitle_to_render) { - glUniform1i(_fragment_type, 2); + glUniform1i(_fragment_type, static_cast(FragmentType::REC709_IMAGE)); _subtitle_texture->bind(); - glDrawElements (GL_TRIANGLES, 6, GL_UNSIGNED_INT, reinterpret_cast(indices_subtitle_texture * sizeof(int))); + glDrawElements (GL_TRIANGLES, indices_subtitle_texture_number, GL_UNSIGNED_INT, reinterpret_cast(indices_subtitle_texture_offset * sizeof(int))); } if (_viewer->outline_content()) { - glUniform1i(_fragment_type, 0); - glDrawElements (GL_LINES, 8, GL_UNSIGNED_INT, reinterpret_cast(indices_border * sizeof(int))); + glUniform1i(_fragment_type, static_cast(FragmentType::OUTLINE_CONTENT)); + glDrawElements (GL_LINES, indices_outline_content_number, GL_UNSIGNED_INT, reinterpret_cast(indices_outline_content_offset * sizeof(int))); + check_gl_error ("glDrawElements"); + } + if (auto guess = _viewer->crop_guess()) { + glUniform1i(_fragment_type, static_cast(FragmentType::CROP_GUESS)); + glDrawElements (GL_LINES, indices_crop_guess_number, GL_UNSIGNED_INT, reinterpret_cast(indices_crop_guess_offset * sizeof(int))); check_gl_error ("glDrawElements"); } @@ -507,7 +595,7 @@ GLVideoView::draw () void GLVideoView::set_image (shared_ptr pv) { - shared_ptr video = _optimise_for_j2k ? pv->raw_image() : pv->image(bind(&PlayerVideo::force, _1, AV_PIX_FMT_RGB24), VideoRange::FULL, true); + shared_ptr video = _optimise_for_j2k ? pv->raw_image() : pv->image(boost::bind(&PlayerVideo::force, AV_PIX_FMT_RGB24), VideoRange::FULL, true); /* Only the player's black frames should be aligned at this stage, so this should * almost always have no work to do. @@ -516,11 +604,9 @@ GLVideoView::set_image (shared_ptr pv) /** 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. + * Otherwise we render a RGB image without any shader-side processing. */ - /* XXX: video range conversion */ - _video_texture->set (video); auto const text = pv->text(); @@ -538,6 +624,7 @@ GLVideoView::set_image (shared_ptr pv) auto const inter_position = player_video().first->inter_position(); auto const inter_size = player_video().first->inter_size(); auto const out_size = player_video().first->out_size(); + auto const crop_guess = _viewer->crop_guess(); auto x_offset = std::max(0, (canvas_width - out_size.width) / 2); auto y_offset = std::max(0, (canvas_height - out_size.height) / 2); @@ -547,6 +634,7 @@ GLVideoView::set_image (shared_ptr pv) _last_inter_position.set_next (inter_position); _last_inter_size.set_next (inter_size); _last_out_size.set_next (out_size); + _last_crop_guess.set_next (crop_guess); class Rectangle { @@ -616,26 +704,40 @@ GLVideoView::set_image (shared_ptr pv) float _vertices[20]; }; - if (_last_canvas_size.changed() || _last_inter_position.changed() || _last_inter_size.changed() || _last_out_size.changed()) { + auto const sizing_changed = _last_canvas_size.changed() || _last_inter_position.changed() || _last_inter_size.changed() || _last_out_size.changed(); + if (sizing_changed) { const auto video = _optimise_for_j2k ? Rectangle(canvas_size, inter_position.x + x_offset, inter_position.y + y_offset, inter_size) : Rectangle(canvas_size, x_offset, y_offset, out_size); - glBufferSubData (GL_ARRAY_BUFFER, 0, video.size(), video.vertices()); + glBufferSubData (GL_ARRAY_BUFFER, array_buffer_video_offset, video.size(), video.vertices()); check_gl_error ("glBufferSubData (video)"); - const auto border = Rectangle(canvas_size, inter_position.x + x_offset, inter_position.y + y_offset, inter_size); - glBufferSubData (GL_ARRAY_BUFFER, 8 * 5 * sizeof(float), border.size(), border.vertices()); - check_gl_error ("glBufferSubData (border)"); + const auto outline_content = Rectangle(canvas_size, inter_position.x + x_offset, inter_position.y + y_offset, inter_size); + glBufferSubData (GL_ARRAY_BUFFER, array_buffer_outline_content_offset, outline_content.size(), outline_content.vertices()); + check_gl_error ("glBufferSubData (outline_content)"); + } + + if ((sizing_changed || _last_crop_guess.changed()) && crop_guess) { + auto const crop_guess_rectangle = Rectangle( + canvas_size, + inter_position.x + x_offset + inter_size.width * crop_guess->x, + inter_position.y + y_offset + inter_size.height * crop_guess->y, + dcp::Size(inter_size.width * crop_guess->width, inter_size.height * crop_guess->height) + ); + glBufferSubData (GL_ARRAY_BUFFER, array_buffer_crop_guess_offset, crop_guess_rectangle.size(), crop_guess_rectangle.vertices()); + check_gl_error ("glBufferSubData (crop_guess_rectangle)"); } if (_have_subtitle_to_render) { const auto subtitle = Rectangle(canvas_size, inter_position.x + x_offset + text->position.x, inter_position.y + y_offset + text->position.y, text->image->size()); - glBufferSubData (GL_ARRAY_BUFFER, 4 * 5 * sizeof(float), subtitle.size(), subtitle.vertices()); + glBufferSubData (GL_ARRAY_BUFFER, array_buffer_subtitle_offset, subtitle.size(), subtitle.vertices()); check_gl_error ("glBufferSubData (subtitle)"); } + _rec2020 = pv->colour_conversion() && pv->colour_conversion()->about_equal(dcp::ColourConversion::rec2020_to_xyz(), 1e-6); + /* opt: where should these go? */ glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);