X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=core%2Fmixer%2Fimage%2Fimage_kernel.cpp;h=2777bb3c4546465af7421cf19d30bfed071a7b42;hb=517174ad8f15d5b6f71cdf8bc04a158cbe4bd278;hp=bf8d4939de7cb9fb2a9216ebc211fb1c6f5644c2;hpb=2a36a79b24795e2a69f32432e272cc0eb7324c76;p=casparcg diff --git a/core/mixer/image/image_kernel.cpp b/core/mixer/image/image_kernel.cpp index bf8d4939d..2777bb3c4 100644 --- a/core/mixer/image/image_kernel.cpp +++ b/core/mixer/image/image_kernel.cpp @@ -1,250 +1,476 @@ -#include "../../StdAfx.h" +/* +* copyright (c) 2010 Sveriges Television AB +* +* This file is part of CasparCG. +* +* CasparCG 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. +* +* CasparCG 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 CasparCG. If not, see . +* +*/ +#include "../../stdafx.h" #include "image_kernel.h" -#include "image_mixer.h" +#include "blending_glsl.h" +#include "../gpu/shader.h" +#include "../gpu/device_buffer.h" +#include "../gpu/ogl_device.h" #include #include +#include -#include +#include +#include +#include + +#include #include +#include + #include namespace caspar { namespace core { - -class shader_program -{ - GLuint program_; -public: - - shader_program() : program_(0) {} - shader_program(const std::string& vertex_source_str, const std::string& fragment_source_str) : program_(0) - { - GLint success; - const char* vertex_source = vertex_source_str.c_str(); - - auto vertex_shader = glCreateShaderObjectARB(GL_VERTEX_SHADER_ARB); - - GL(glShaderSourceARB(vertex_shader, 1, &vertex_source, NULL)); - GL(glCompileShaderARB(vertex_shader)); +GLubyte upper_pattern[] = { + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00}; + +GLubyte lower_pattern[] = { + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff}; - GL(glGetObjectParameterivARB(vertex_shader, GL_OBJECT_COMPILE_STATUS_ARB, &success)); - if (success == GL_FALSE) - { - char info[2048]; - GL(glGetInfoLogARB(vertex_shader, sizeof(info), 0, info)); - GL(glDeleteObjectARB(vertex_shader)); - std::stringstream str; - str << "Failed to compile vertex shader:" << std::endl << info << std::endl; - BOOST_THROW_EXCEPTION(gl::gl_error() << msg_info(str.str())); - } +struct image_kernel::implementation : boost::noncopyable +{ + std::unique_ptr shader_; + bool blend_modes_; - const char* fragment_source = fragment_source_str.c_str(); - - auto fragmemt_shader = glCreateShaderObjectARB(GL_FRAGMENT_SHADER_ARB); - - GL(glShaderSourceARB(fragmemt_shader, 1, &fragment_source, NULL)); - GL(glCompileShaderARB(fragmemt_shader)); + implementation() : blend_modes_(true) + { + } - GL(glGetObjectParameterivARB(fragmemt_shader, GL_OBJECT_COMPILE_STATUS_ARB, &success)); - if (success == GL_FALSE) - { - char info[2048]; - GL(glGetInfoLogARB(fragmemt_shader, sizeof(info), 0, info)); - GL(glDeleteObjectARB(fragmemt_shader)); - std::stringstream str; - str << "Failed to compile fragment shader:" << std::endl << info << std::endl; - BOOST_THROW_EXCEPTION(gl::gl_error() << msg_info(str.str())); - } - - program_ = glCreateProgramObjectARB(); + std::string get_blend_color_func() + { + return - GL(glAttachObjectARB(program_, vertex_shader)); - GL(glAttachObjectARB(program_, fragmemt_shader)); + get_adjustement_glsl() + + + - GL(glLinkProgramARB(program_)); + get_blend_glsl() + + + - GL(glDeleteObjectARB(vertex_shader)); - GL(glDeleteObjectARB(fragmemt_shader)); + "vec3 get_blend_color(vec3 back, vec3 fore) \n" + "{ \n" + " switch(blend_mode) \n" + " { \n" + " case 0: return BlendNormal(back, fore); \n" + " case 1: return BlendLighten(back, fore); \n" + " case 2: return BlendDarken(back, fore); \n" + " case 3: return BlendMultiply(back, fore); \n" + " case 4: return BlendAverage(back, fore); \n" + " case 5: return BlendAdd(back, fore); \n" + " case 6: return BlendSubstract(back, fore); \n" + " case 7: return BlendDifference(back, fore); \n" + " case 8: return BlendNegation(back, fore); \n" + " case 9: return BlendExclusion(back, fore); \n" + " case 10: return BlendScreen(back, fore); \n" + " case 11: return BlendOverlay(back, fore); \n" + //" case 12: return BlendSoftLight(back, fore); \n" + " case 13: return BlendHardLight(back, fore); \n" + " case 14: return BlendColorDodge(back, fore); \n" + " case 15: return BlendColorBurn(back, fore); \n" + " case 16: return BlendLinearDodge(back, fore); \n" + " case 17: return BlendLinearBurn(back, fore); \n" + " case 18: return BlendLinearLight(back, fore); \n" + " case 19: return BlendVividLight(back, fore); \n" + " case 20: return BlendPinLight(back, fore); \n" + " case 21: return BlendHardMix(back, fore); \n" + " case 22: return BlendReflect(back, fore); \n" + " case 23: return BlendGlow(back, fore); \n" + " case 24: return BlendPhoenix(back, fore); \n" + " case 25: return BlendHue(back, fore); \n" + " case 26: return BlendSaturation(back, fore); \n" + " case 27: return BlendColor(back, fore); \n" + " case 28: return BlendLuminosity(back, fore); \n" + " } \n" + " return BlendNormal(back, fore); \n" + "} \n" + " \n" + "vec4 blend(vec4 fore) \n" + "{ \n" + " vec4 back = texture2D(background, gl_TexCoord[1].st); \n" + " fore.rgb = get_blend_color(back.rgb, fore.rgb); \n" + " return vec4(mix(back.bgr, fore.rgb, fore.a), back.a + fore.a); \n" + "} \n"; + } + + std::string get_simple_blend_color_func() + { + return + + get_adjustement_glsl() + + + - GL(glGetObjectParameterivARB(program_, GL_OBJECT_LINK_STATUS_ARB, &success)); - if (success == GL_FALSE) - { - char info[2048]; - GL(glGetInfoLogARB(program_, sizeof(info), 0, info)); - GL(glDeleteObjectARB(program_)); - std::stringstream str; - str << "Failed to link shader program:" << std::endl << info << std::endl; - BOOST_THROW_EXCEPTION(gl::gl_error() << msg_info(str.str())); - } - GL(glUseProgramObjectARB(program_)); - glUniform1i(glGetUniformLocation(program_, "plane[0]"), 0); - glUniform1i(glGetUniformLocation(program_, "plane[1]"), 1); - glUniform1i(glGetUniformLocation(program_, "plane[2]"), 2); - glUniform1i(glGetUniformLocation(program_, "plane[3]"), 3); + "vec4 blend(vec4 fore) \n" + "{ \n" + " return fore; \n" + "} \n"; } - GLint get_location(const char* name) + std::string get_vertex() { - GLint loc = glGetUniformLocation(program_, name); - return loc; + return + + "void main() \n" + "{ \n" + " gl_TexCoord[0] = gl_MultiTexCoord0; \n" + " gl_TexCoord[1] = gl_MultiTexCoord1; \n" + " gl_FrontColor = gl_Color; \n" + " gl_Position = ftransform(); \n" + "} \n"; } - shader_program& operator=(shader_program&& other) + std::string get_fragment() { - program_ = other.program_; - other.program_ = 0; - return *this; + return + + "#version 120 \n" + "uniform sampler2D background; \n" + "uniform sampler2D plane[4]; \n" + "uniform sampler2D local_key; \n" + "uniform sampler2D layer_key; \n" + " \n" + "uniform bool is_hd; \n" + "uniform bool has_local_key; \n" + "uniform bool has_layer_key; \n" + "uniform int blend_mode; \n" + "uniform int alpha_mode; \n" + "uniform int pixel_format; \n" + " \n" + "uniform bool levels; \n" + "uniform float min_input; \n" + "uniform float max_input; \n" + "uniform float gamma; \n" + "uniform float min_output; \n" + "uniform float max_output; \n" + " \n" + "uniform bool csb; \n" + "uniform float brt; \n" + "uniform float sat; \n" + "uniform float con; \n" + " \n" + + + + + (blend_modes_ ? get_blend_color_func() : get_simple_blend_color_func()) + + + + + " \n" + "//http://slouken.blogspot.com/2011/02/mpeg-acceleration-with-glsl.html \n" + "vec4 ycbcra_to_rgba_sd(float y, float cb, float cr, float a) \n" + "{ \n" + " // YUV offset \n" + " const vec3 offset = vec3(-0.0625, -0.5, -0.5); \n" + " \n" + " // RGB coefficients \n" + " const vec3 Rcoeff = vec3(1.164, 0.000, 1.596); \n" + " const vec3 Gcoeff = vec3(1.164, -0.391, -0.813); \n" + " const vec3 Bcoeff = vec3(1.164, 2.018, 0.000); \n" + " \n" + " vec3 yuv = vec3(y, cr, cb); \n" + " vec4 rgba; \n" + " \n" + " yuv += offset; \n" + " rgba.r = dot(yuv, Rcoeff); \n" + " rgba.g = dot(yuv, Gcoeff); \n" + " rgba.b = dot(yuv, Bcoeff); \n" + " rgba.a = a; \n" + " \n" + " return rgba; \n" + "} \n" + " \n" + "vec4 ycbcra_to_rgba_hd(float y, float cb, float cr, float a) \n" + "{ \n" + " // YUV offset \n" + " const vec3 offset = vec3(-0.0625, -0.5, -0.5); \n" + " \n" + " // RGB coefficients \n" + " const vec3 Rcoeff = vec3(1.164, 0.000, 1.793); \n" + " const vec3 Gcoeff = vec3(1.164, -0.213, -0.534); \n" + " const vec3 Bcoeff = vec3(1.164, 2.115, 0.000); \n" + " \n" + " vec3 yuv = vec3(y, cr, cb); \n" + " vec4 rgba; \n" + " \n" + " yuv += offset; \n" + " rgba.r = dot(yuv, Rcoeff); \n" + " rgba.g = dot(yuv, Gcoeff); \n" + " rgba.b = dot(yuv, Bcoeff); \n" + " rgba.a = a; \n" + " \n" + " return rgba; \n" + "} \n" + " \n" + "vec4 ycbcra_to_rgba(float y, float cb, float cr, float a) \n" + "{ \n" + " if(is_hd) \n" + " return ycbcra_to_rgba_hd(y, cb, cr, a); \n" + " else \n" + " return ycbcra_to_rgba_sd(y, cb, cr, a); \n" + "} \n" + " \n" + "vec4 get_rgba_color() \n" + "{ \n" + " switch(pixel_format) \n" + " { \n" + " case 0: //gray \n" + " return vec4(texture2D(plane[0], gl_TexCoord[0].st).rrr, 1.0); \n" + " case 1: //bgra, \n" + " return texture2D(plane[0], gl_TexCoord[0].st).bgra; \n" + " case 2: //rgba, \n" + " return texture2D(plane[0], gl_TexCoord[0].st).rgba; \n" + " case 3: //argb, \n" + " return texture2D(plane[0], gl_TexCoord[0].st).argb; \n" + " case 4: //abgr, \n" + " return texture2D(plane[0], gl_TexCoord[0].st).gbar; \n" + " case 5: //ycbcr, \n" + " { \n" + " float y = texture2D(plane[0], gl_TexCoord[0].st).r; \n" + " float cb = texture2D(plane[1], gl_TexCoord[0].st).r; \n" + " float cr = texture2D(plane[2], gl_TexCoord[0].st).r; \n" + " return ycbcra_to_rgba(y, cb, cr, 1.0); \n" + " } \n" + " case 6: //ycbcra \n" + " { \n" + " float y = texture2D(plane[0], gl_TexCoord[0].st).r; \n" + " float cb = texture2D(plane[1], gl_TexCoord[0].st).r; \n" + " float cr = texture2D(plane[2], gl_TexCoord[0].st).r; \n" + " float a = texture2D(plane[3], gl_TexCoord[0].st).r; \n" + " return ycbcra_to_rgba(y, cb, cr, a); \n" + " } \n" + " case 7: //luma \n" + " { \n" + " vec3 y3 = texture2D(plane[0], gl_TexCoord[0].st).rrr; \n" + " return vec4((y3-0.065)/0.859, 1.0); \n" + " } \n" + " } \n" + " return vec4(0.0, 0.0, 0.0, 0.0); \n" + "} \n" + " \n" + "void main() \n" + "{ \n" + " vec4 color = get_rgba_color(); \n" + " if(levels) \n" + " color.rgb = LevelsControl(color.rgb, min_input, max_input, gamma, min_output, max_output); \n" + " if(csb) \n" + " color.rgb = ContrastSaturationBrightness(color.rgb, brt, sat, con); \n" + " if(has_local_key) \n" + " color.a *= texture2D(local_key, gl_TexCoord[1].st).r; \n" + " if(has_layer_key) \n" + " color.a *= texture2D(layer_key, gl_TexCoord[1].st).r; \n" + " color *= gl_Color; \n" + " color = blend(color); \n" + " gl_FragColor = color.bgra; \n" + "} \n"; } - ~shader_program() + void init_shader(ogl_device& ogl) { - glDeleteProgram(program_); - } + try + { + blend_modes_ = glTextureBarrierNV ? env::properties().get("configuration.mixers.blend-modes", false) : false; + shader_.reset(new shader(get_vertex(), get_fragment())); + } + catch(...) + { + CASPAR_LOG_CURRENT_EXCEPTION(); + CASPAR_LOG(warning) << "Failed to compile shader. Trying to compile without blend-modes."; + + blend_modes_ = false; + shader_.reset(new shader(get_vertex(), get_fragment())); + } + + ogl.enable(GL_TEXTURE_2D); - void use() - { - GL(glUseProgramObjectARB(program_)); + if(!blend_modes_) + { + ogl.enable(GL_BLEND); + GL(glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE)); + CASPAR_LOG(info) << L"[shader] Blend-modes are disabled."; + } } -}; + + void draw(ogl_device& ogl, + render_item&& item, + const safe_ptr& background, + const std::shared_ptr& local_key, + const std::shared_ptr& layer_key) + { + static const double epsilon = 0.001; -GLubyte progressive_pattern[] = { - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xFF, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; - -GLubyte upper_pattern[] = { - 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, - 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, - 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, - 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00}; - -GLubyte lower_pattern[] = { - 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, - 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, - 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, - 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff}; + CASPAR_ASSERT(item.pix_desc.planes.size() == item.textures.size()); -struct image_kernel::implementation -{ - std::unordered_map shaders_; + if(item.textures.empty()) + return; -public: - std::unordered_map& shaders() - { - GL(glEnable(GL_POLYGON_STIPPLE)); - GL(glEnable(GL_BLEND)); - GL(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); + if(item.transform.get_opacity() < epsilon) + return; - if(shaders_.empty()) - { - std::string common_vertex = - "void main() " - "{ " - " gl_TexCoord[0] = gl_MultiTexCoord0; " - " gl_FrontColor = gl_Color; " - " gl_Position = ftransform(); " - "} "; - - std::string common_fragment = - "uniform sampler2D plane[4]; " - "uniform float color_gain; " - - // NOTE: YCbCr, ITU-R, http://www.intersil.com/data/an/an9717.pdf - // TODO: Support for more yuv formats might be needed. - "vec4 ycbcra_to_bgra(float y, float cb, float cr, float a) " - "{ " - " cb -= 0.5; " - " cr -= 0.5; " - " y = 1.164*(y-0.0625); " - " " - " vec4 color; " - " color.r = y + 1.596 * cr; " - " color.g = y - 0.813 * cr - 0.337633 * cb; " - " color.b = y + 2.017 * cb; " - " color.a = a; " - " " - " return color; " - "} " - " "; + if(!shader_) + init_shader(ogl); - shaders_[pixel_format::abgr] = shader_program(common_vertex, common_fragment + + if(item.mode == core::video_mode::progressive) + ogl.disable(GL_POLYGON_STIPPLE); + else + { + ogl.enable(GL_POLYGON_STIPPLE); - "void main() " - "{ " - " vec4 abgr = texture2D(plane[0], gl_TexCoord[0].st); " - " gl_FragColor = abgr.argb * color_gain; " - "} "); - - shaders_[pixel_format::argb]= shader_program(common_vertex, common_fragment + + if(item.mode == core::video_mode::upper) + ogl.stipple_pattern(upper_pattern); + else if(item.mode == core::video_mode::lower) + ogl.stipple_pattern(lower_pattern); + } - "void main() " - "{ " - " vec4 argb = texture2D(plane[0], gl_TexCoord[0].st); " - " gl_FragColor = argb.grab * gl_Color * color_gain; " - "} "); - - shaders_[pixel_format::bgra]= shader_program(common_vertex, common_fragment + + // Bind textures - "void main() " - "{ " - " vec4 bgra = texture2D(plane[0], gl_TexCoord[0].st); " - " gl_FragColor = bgra.rgba * gl_Color * color_gain; " - "} "); + for(size_t n = 0; n < item.textures.size(); ++n) + item.textures[n]->bind(n); + + if(local_key) + local_key->bind(4); + + if(layer_key) + layer_key->bind(5); + + // Setup shader + + ogl.use(*shader_); + + shader_->set("plane[0]", 0); + shader_->set("plane[1]", 1); + shader_->set("plane[2]", 2); + shader_->set("plane[3]", 3); + shader_->set("local_key", 4); + shader_->set("layer_key", 5); + shader_->set("is_hd", item.pix_desc.planes.at(0).height > 700 ? 1 : 0); + shader_->set("has_local_key", local_key ? 1 : 0); + shader_->set("has_layer_key", layer_key ? 1 : 0); + shader_->set("pixel_format", item.pix_desc.pix_fmt); - shaders_[pixel_format::rgba] = shader_program(common_vertex, common_fragment + + // Setup blend_func + + if(blend_modes_) + { + background->bind(6); + + shader_->set("background", 6); + shader_->set("blend_mode", item.transform.get_is_key() ? core::image_transform::blend_mode::normal : item.transform.get_blend_mode()); + shader_->set("alpha_mode", item.transform.get_alpha_mode()); + } + + auto levels = item.transform.get_levels(); + + if(levels.min_input > epsilon || + levels.max_input < 1.0-epsilon || + levels.min_output > epsilon || + levels.max_output < 1.0-epsilon || + std::abs(levels.gamma - 1.0) > epsilon) + { + shader_->set("levels", true); + shader_->set("min_input", levels.min_input); + shader_->set("max_input", levels.max_input); + shader_->set("min_output", levels.min_output); + shader_->set("max_output", levels.max_output); + shader_->set("gamma", levels.gamma); + } + else + shader_->set("levels", false); - "void main() " - "{ " - " vec4 rgba = texture2D(plane[0], gl_TexCoord[0].st); " - " gl_FragColor = rgba.bgra * gl_Color * color_gain; " - "} "); + if(std::abs(item.transform.get_brightness() - 1.0) > epsilon || + std::abs(item.transform.get_saturation() - 1.0) > epsilon || + std::abs(item.transform.get_contrast() - 1.0) > epsilon) + { + shader_->set("csb", true); + + shader_->set("brt", item.transform.get_brightness()); + shader_->set("sat", item.transform.get_saturation()); + shader_->set("con", item.transform.get_contrast()); + } + else + shader_->set("csb", false); - shaders_[pixel_format::ycbcr] = shader_program(common_vertex, common_fragment + - - "void main() " - "{ " - " float y = texture2D(plane[0], gl_TexCoord[0].st).r;" - " float cb = texture2D(plane[1], gl_TexCoord[0].st).r;" - " float cr = texture2D(plane[2], gl_TexCoord[0].st).r;" - " float a = 1.0; " - " gl_FragColor = ycbcra_to_bgra(y, cb, cr, a) * gl_Color * color_gain;" - "} "); + // Setup drawing area - shaders_[pixel_format::ycbcra] = shader_program(common_vertex, common_fragment + - - "void main() " - "{ " - " float y = texture2D(plane[0], gl_TexCoord[0].st).r;" - " float cb = texture2D(plane[1], gl_TexCoord[0].st).r;" - " float cr = texture2D(plane[2], gl_TexCoord[0].st).r;" - " float a = texture2D(plane[3], gl_TexCoord[0].st).r;" - " gl_FragColor = ycbcra_to_bgra(y, cb, cr, a) * gl_Color * color_gain;" - "} "); + ogl.viewport(0, 0, background->width(), background->height()); + + GL(glColor4d(item.transform.get_gain(), item.transform.get_gain(), item.transform.get_gain(), item.transform.get_opacity())); + + auto m_p = item.transform.get_clip_translation(); + auto m_s = item.transform.get_clip_scale(); + + bool scissor = m_p[0] > std::numeric_limits::epsilon() || m_p[1] > std::numeric_limits::epsilon() && + m_s[0] < 1.0 - std::numeric_limits::epsilon() || m_s[1] < 1.0 - std::numeric_limits::epsilon(); + + if(scissor) + { + double w = static_cast(background->width()); + double h = static_cast(background->height()); + + ogl.enable(GL_SCISSOR_TEST); + ogl.scissor(static_cast(m_p[0]*w), static_cast(m_p[1]*h), static_cast(m_s[0]*w), static_cast(m_s[1]*h)); } - return shaders_; + + auto f_p = item.transform.get_fill_translation(); + auto f_s = item.transform.get_fill_scale(); + + // Draw + + glBegin(GL_QUADS); + glMultiTexCoord2d(GL_TEXTURE0, 0.0, 0.0); glMultiTexCoord2d(GL_TEXTURE1, f_p[0] , f_p[1] ); glVertex2d( f_p[0] *2.0-1.0, f_p[1] *2.0-1.0); + glMultiTexCoord2d(GL_TEXTURE0, 1.0, 0.0); glMultiTexCoord2d(GL_TEXTURE1, (f_p[0]+f_s[0]), f_p[1] ); glVertex2d((f_p[0]+f_s[0])*2.0-1.0, f_p[1] *2.0-1.0); + glMultiTexCoord2d(GL_TEXTURE0, 1.0, 1.0); glMultiTexCoord2d(GL_TEXTURE1, (f_p[0]+f_s[0]), (f_p[1]+f_s[1])); glVertex2d((f_p[0]+f_s[0])*2.0-1.0, (f_p[1]+f_s[1])*2.0-1.0); + glMultiTexCoord2d(GL_TEXTURE0, 0.0, 1.0); glMultiTexCoord2d(GL_TEXTURE1, f_p[0] , (f_p[1]+f_s[1])); glVertex2d( f_p[0] *2.0-1.0, (f_p[1]+f_s[1])*2.0-1.0); + glEnd(); + + ogl.disable(GL_SCISSOR_TEST); + + item.textures.clear(); + ogl.yield(); // Return resources to pool as early as possible. + + if(!blend_modes_) + glTextureBarrierNV(); // This allows us to use framebuffer both as source and target while blending. } }; image_kernel::image_kernel() : impl_(new implementation()){} - -void image_kernel::apply(pixel_format::type pix_fmt, const image_transform& transform) +void image_kernel::draw(ogl_device& ogl, render_item&& item, const safe_ptr& background, const std::shared_ptr& local_key, const std::shared_ptr& layer_key) { - impl_->shaders()[pix_fmt].use(); - - GL(glUniform1f(impl_->shaders()[pix_fmt].get_location("color_gain"), static_cast(transform.gain))); + impl_->draw(ogl, std::move(item), background, local_key, layer_key); +} - if(transform.mode == video_mode::upper) - glPolygonStipple(upper_pattern); - else if(transform.mode == video_mode::lower) - glPolygonStipple(lower_pattern); - else - glPolygonStipple(progressive_pattern); +bool operator==(const render_item& lhs, const render_item& rhs) +{ + return lhs.textures == rhs.textures && lhs.transform == rhs.transform && lhs.tag == rhs.tag && lhs.mode == rhs.mode; } }} \ No newline at end of file