\r
o Added support for chroma keying.\r
o Fixed bug where MIXER CONTRAST set to < 1 can cause transparency issues.\r
+ o Experimental support for straight alpha output.\r
\r
Consumers\r
---------\r
o Fixed bug where MIXER FILL overrides any previous MIXER CLIP on the same\r
layer. The bugfix also has the side effect of supporting negative scale on\r
MIXER FILL, causing the image to be flipped.\r
+ o MIXER <ch> STRAIGHT_ALPHA_OUTPUT added to control whether to output straight\r
+ alpha or not.\r
\r
OSC\r
---\r
safe_ptr<ogl_device> ogl_;\r
safe_ptr<shader> shader_;\r
bool blend_modes_;\r
+ bool supports_texture_barrier_;\r
\r
implementation(const safe_ptr<ogl_device>& ogl)\r
: ogl_(ogl)\r
, shader_(ogl_->invoke([&]{return get_image_shader(*ogl, blend_modes_);}))\r
+ , supports_texture_barrier_(glTextureBarrierNV != 0)\r
{\r
+ if (!supports_texture_barrier_)\r
+ CASPAR_LOG(warning) << L"[image_mixer] TextureBarrierNV not supported. Post processing will not be available";\r
}\r
\r
void draw(draw_params&& params)\r
shader_->set("has_layer_key", bool(params.layer_key));\r
shader_->set("pixel_format", params.pix_desc.pix_fmt); \r
shader_->set("opacity", params.transform.is_key ? 1.0 : params.transform.opacity); \r
+ shader_->set("post_processing", false);\r
\r
- shader_->set("chroma_mode", params.blend_mode.chroma.key == chroma::green ? 1 : (params.blend_mode.chroma.key == chroma::blue ? 2 : 0));\r
+ shader_->set("chroma_mode", params.blend_mode.chroma.key == chroma::green ? 1 : (params.blend_mode.chroma.key == chroma::blue ? 2 : 0));\r
shader_->set("chroma_blend", params.blend_mode.chroma.threshold, params.blend_mode.chroma.softness);\r
shader_->set("chroma_spill", params.blend_mode.chroma.spill);\r
// shader_->set("chroma.key", ((params.blend_mode.chroma.key >> 24) && 0xff)/255.0f,\r
\r
if(blend_modes_)\r
{\r
- params.background->bind(6);\r
+ params.background->bind(texture_id::background);\r
\r
shader_->set("background", texture_id::background);\r
shader_->set("blend_mode", params.blend_mode.mode);\r
glTextureBarrierNV(); \r
}\r
}\r
+\r
+ void post_process(\r
+ const safe_ptr<device_buffer>& background, bool straighten_alpha)\r
+ {\r
+ bool should_post_process = \r
+ supports_texture_barrier_ && straighten_alpha;\r
+\r
+ if (!should_post_process)\r
+ return;\r
+\r
+ if (!blend_modes_)\r
+ ogl_->disable(GL_BLEND);\r
+\r
+ ogl_->disable(GL_POLYGON_STIPPLE);\r
+\r
+ ogl_->attach(*background);\r
+\r
+ background->bind(texture_id::background);\r
+\r
+ ogl_->use(*shader_);\r
+ shader_->set("background", texture_id::background);\r
+ shader_->set("post_processing", should_post_process);\r
+ shader_->set("straighten_alpha", straighten_alpha);\r
+\r
+ ogl_->viewport(0, 0, background->width(), background->height());\r
+\r
+ glBegin(GL_QUADS);\r
+ glMultiTexCoord2d(GL_TEXTURE0, 0.0, 0.0); glVertex2d(-1.0, -1.0);\r
+ glMultiTexCoord2d(GL_TEXTURE0, 1.0, 0.0); glVertex2d( 1.0, -1.0);\r
+ glMultiTexCoord2d(GL_TEXTURE0, 1.0, 1.0); glVertex2d( 1.0, 1.0);\r
+ glMultiTexCoord2d(GL_TEXTURE0, 0.0, 1.0); glVertex2d(-1.0, 1.0);\r
+ glEnd();\r
+\r
+ glTextureBarrierNV();\r
+\r
+ if (!blend_modes_)\r
+ ogl_->enable(GL_BLEND);\r
+ }\r
};\r
\r
image_kernel::image_kernel(const safe_ptr<ogl_device>& ogl) : impl_(new implementation(ogl)){}\r
impl_->draw(std::move(params));\r
}\r
\r
+void image_kernel::post_process(\r
+ const safe_ptr<device_buffer>& background, bool straighten_alpha)\r
+{\r
+ impl_->post_process(background, straighten_alpha);\r
+}\r
+\r
}}\r
public:\r
image_kernel(const safe_ptr<ogl_device>& ogl);\r
void draw(draw_params&& params);\r
+ void post_process(\r
+ const safe_ptr<device_buffer>& background, bool straighten_alpha);\r
private:\r
struct implementation;\r
safe_ptr<implementation> impl_;\r
{\r
}\r
\r
- boost::unique_future<safe_ptr<host_buffer>> operator()(std::vector<layer>&& layers, const video_format_desc& format_desc)\r
+ boost::unique_future<safe_ptr<host_buffer>> operator()(\r
+ std::vector<layer>&& layers,\r
+ const video_format_desc& format_desc,\r
+ bool straighten_alpha)\r
{ \r
auto layers2 = make_move_on_copy(std::move(layers));\r
return ogl_->begin_invoke([=]\r
{\r
- return do_render(std::move(layers2.value), format_desc);\r
+ return do_render(\r
+ std::move(layers2.value), format_desc, straighten_alpha);\r
});\r
}\r
\r
private:\r
- safe_ptr<host_buffer> do_render(std::vector<layer>&& layers, const video_format_desc& format_desc)\r
+ safe_ptr<host_buffer> do_render(std::vector<layer>&& layers, const video_format_desc& format_desc, bool straighten_alpha)\r
{\r
auto draw_buffer = create_mixer_buffer(4, format_desc);\r
\r
draw(std::move(layers), draw_buffer, format_desc);\r
}\r
\r
+ kernel_.post_process(draw_buffer, straighten_alpha);\r
+\r
auto host_buffer = ogl_->create_host_buffer(format_desc.size, host_buffer::read_only);\r
ogl_->attach(*draw_buffer);\r
ogl_->read_buffer(*draw_buffer);\r
{ \r
}\r
\r
- boost::unique_future<safe_ptr<host_buffer>> render(const video_format_desc& format_desc)\r
+ boost::unique_future<safe_ptr<host_buffer>> render(const video_format_desc& format_desc, bool straighten_alpha)\r
{\r
- return renderer_(std::move(layers_), format_desc);\r
+ return renderer_(std::move(layers_), format_desc, straighten_alpha);\r
}\r
};\r
\r
void image_mixer::begin(basic_frame& frame){impl_->begin(frame);}\r
void image_mixer::visit(write_frame& frame){impl_->visit(frame);}\r
void image_mixer::end(){impl_->end();}\r
-boost::unique_future<safe_ptr<host_buffer>> image_mixer::operator()(const video_format_desc& format_desc){return impl_->render(format_desc);}\r
+boost::unique_future<safe_ptr<host_buffer>> image_mixer::operator()(const video_format_desc& format_desc, bool straighten_alpha){return impl_->render(format_desc, straighten_alpha);}\r
void image_mixer::begin_layer(blend_mode blend_mode){impl_->begin_layer(blend_mode);}\r
void image_mixer::end_layer(){impl_->end_layer();}\r
\r
void begin_layer(blend_mode blend_mode);\r
void end_layer();\r
\r
- boost::unique_future<safe_ptr<host_buffer>> operator()(const video_format_desc& format_desc);\r
+ boost::unique_future<safe_ptr<host_buffer>> operator()(\r
+ const video_format_desc& format_desc, bool straighten_alpha);\r
\r
private:\r
struct implementation;\r
"\n const float AvgLumB = 0.5; "\r
"\n "\r
"\n const vec3 LumCoeff = vec3(0.2125, 0.7154, 0.0721); "\r
- "\n bool demultiply_remultiply = con < 1.0; "\r
+ "\n bool demultiply_remultiply = con < 1.0 && color.a != 1.0; "\r
"\n "\r
"\n if (demultiply_remultiply && color.a > 0.0) "\r
"\n color.rgb /= color.a; "\r
"uniform float sat; \n"\r
"uniform float con; \n"\r
" \n" \r
+ "uniform bool post_processing; \n"\r
+ "uniform bool straighten_alpha; \n"\r
+ " \n" \r
"uniform int chroma_mode; \n"\r
"uniform vec2 chroma_blend; \n"\r
"uniform float chroma_spill; \n"\r
" return vec4(0.0, 0.0, 0.0, 0.0); \n"\r
"} \n"\r
" \n"\r
+ "vec4 post_process() \n"\r
+ "{ \n"\r
+ " vec4 color = texture2D(background, gl_TexCoord[0].st).bgra; \n"\r
+ " \n"\r
+ " if (straighten_alpha && color.a > 0.0 && color.a != 1.0) \n"\r
+ " color.rgb /= color.a; \n"\r
+ " \n"\r
+ " return color; \n"\r
+ "} \n"\r
+ " \n"\r
"void main() \n"\r
"{ \n"\r
+ " if (post_processing) \n"\r
+ " { \n"\r
+ " gl_FragColor = post_process().bgra; \n"\r
+ " return; \n"\r
+ " } \n"\r
+ " \n"\r
" vec4 color = get_rgba_color(); \n"\r
" color = chroma_key(color); \n"\r
" if(levels) \n"\r
video_format_desc format_desc_;\r
safe_ptr<ogl_device> ogl_;\r
channel_layout audio_channel_layout_;\r
+ bool straighten_alpha_;\r
\r
audio_mixer audio_mixer_;\r
image_mixer image_mixer_;\r
, format_desc_(format_desc)\r
, ogl_(ogl)\r
, audio_channel_layout_(audio_channel_layout)\r
- , image_mixer_(ogl)\r
+ , straighten_alpha_(false)\r
, audio_mixer_(graph_)\r
+ , image_mixer_(ogl)\r
, executor_(L"mixer")\r
{ \r
graph_->set_color("mix-time", diagnostics::color(1.0f, 0.0f, 0.9f, 0.8));\r
image_mixer_.end_layer();\r
}\r
\r
- auto image = image_mixer_(format_desc_);\r
+ auto image = image_mixer_(format_desc_, straighten_alpha_);\r
auto audio = audio_mixer_(format_desc_, audio_channel_layout_);\r
image.wait();\r
\r
}, high_priority);\r
}\r
\r
+ void set_straight_alpha_output(bool value)\r
+ {\r
+ executor_.begin_invoke([=]\r
+ {\r
+ straighten_alpha_ = value;\r
+ }, high_priority);\r
+ }\r
+\r
+ bool get_straight_alpha_output()\r
+ {\r
+ return executor_.invoke([=]\r
+ {\r
+ return straighten_alpha_;\r
+ });\r
+ }\r
+\r
float get_master_volume()\r
{\r
return executor_.invoke([=]\r
void mixer::set_chroma(int index, const chroma & value){impl_->set_chroma(index, value);}\r
void mixer::clear_blend_mode(int index) { impl_->clear_blend_mode(index); }\r
void mixer::clear_blend_modes() { impl_->clear_blend_modes(); }\r
+void mixer::set_straight_alpha_output(bool value) { impl_->set_straight_alpha_output(value); }\r
+bool mixer::get_straight_alpha_output() { return impl_->get_straight_alpha_output(); }\r
float mixer::get_master_volume() { return impl_->get_master_volume(); }\r
void mixer::set_master_volume(float volume) { impl_->set_master_volume(volume); }\r
void mixer::set_video_format_desc(const video_format_desc& format_desc){impl_->set_video_format_desc(format_desc);}\r
void set_chroma(int index, const chroma& value);\r
void clear_blend_mode(int index);\r
void clear_blend_modes();\r
+ void set_straight_alpha_output(bool value);\r
+ bool get_straight_alpha_output();\r
\r
float get_master_volume();\r
void set_master_volume(float volume);\r
return transform;\r
}, duration, tween));\r
}\r
+ else if(_parameters[0] == L"STRAIGHT_ALPHA_OUTPUT")\r
+ {\r
+ if (_parameters.size() == 1)\r
+ {\r
+ SetReplyString(L"201 MIXER OK\r\n"\r
+ + lexical_cast<std::wstring>(\r
+ GetChannel()->mixer()->get_straight_alpha_output())\r
+ + L"\r\n");\r
+ return true;\r
+ }\r
+\r
+ bool value = boost::lexical_cast<bool>(_parameters[1]);\r
+ GetChannel()->mixer()->set_straight_alpha_output(value);\r
+ }\r
else if(_parameters[0] == L"VOLUME")\r
{\r
if (_parameters.size() == 1)\r
<channel>\r
<video-mode> PAL [PAL|NTSC|576p2500|720p2398|720p2400|720p2500|720p5000|720p2997|720p5994|720p3000|720p6000|1080p2398|1080p2400|1080i5000|1080i5994|1080i6000|1080p2500|1080p2997|1080p3000|1080p5000|1080p5994|1080p6000|2k2398|2k2400|2k2500|4k2398|4k2400|4k2500|4k2997|4k3000] </video-mode>\r
<channel-layout>stereo [mono|stereo|dts|dolbye|dolbydigital|smpte|passthru]</channel-layout>\r
+ <straight-alpha-output>false [true|false]</straight-alpha-output>\r
<consumers>\r
<decklink>\r
<device>[1..]</device>\r
\r
#include <core/mixer/gpu/ogl_device.h>\r
#include <core/mixer/audio/audio_util.h>\r
+#include <core/mixer/mixer.h>\r
#include <core/video_channel.h>\r
#include <core/producer/stage.h>\r
#include <core/consumer/output.h>\r
channels_.push_back(make_safe<video_channel>(channels_.size()+1, format_desc, ogl_, audio_channel_layout));\r
\r
channels_.back()->monitor_output().link_target(&monitor_subject_);\r
+ channels_.back()->mixer()->set_straight_alpha_output(\r
+ xml_channel.second.get(L"straight-alpha-output", false));\r
\r
create_consumers(\r
xml_channel.second.get_child(L"consumers"),\r