struct caspar_exception : virtual boost::exception, virtual std::exception \r
{\r
caspar_exception(){}\r
+ virtual ~caspar_exception() throw() {}
explicit caspar_exception(const char* msg) : std::exception(msg) {}\r
};\r
\r
\r
#include <common/gl/gl_check.h>\r
\r
-#include <gl/glew.h>\r
+#include <GL/glew.h>
\r
#include <unordered_map>\r
\r
GL(glUniform1f(get_location(name.c_str()), value));\r
}\r
\r
- void set(const std::string& name, double value)\r
+ void set(const std::string& name, float value1, float value2)
+ {
+ GL(glUniform2f(get_location(name.c_str()), value1, value2));
+ }
+
+ void set(const std::string& name, float value1, float value2, float value3)
+ {
+ GL(glUniform3f(get_location(name.c_str()), value1, value2, value3));
+ }
+
+ void set(const std::string& name, float value1, float value2, float value3, float value4)
+ {
+ GL(glUniform4f(get_location(name.c_str()), value1, value2, value3, value4));
+ }
+
+ void set(const std::string& name, double value)
{\r
GL(glUniform1f(get_location(name.c_str()), static_cast<float>(value)));\r
}\r
+
+ void set(const std::string& name, double value1, double value2)
+ {
+ GL(glUniform2f(get_location(name.c_str()), static_cast<float>(value1), static_cast<float>(value2)));
+ }
};\r
\r
\r
void shader::set(const std::string& name, bool value){impl_->set(name, value);}\r
void shader::set(const std::string& name, int value){impl_->set(name, value);}\r
void shader::set(const std::string& name, float value){impl_->set(name, value);}\r
+void shader::set(const std::string& name, float value1, float value2){impl_->set(name, value1, value2);}
+void shader::set(const std::string& name, float value1, float value2, float value3){impl_->set(name, value1, value2, value3);}
+void shader::set(const std::string& name, float value1, float value2, float value3, float value4){impl_->set(name, value1, value2, value3, value4);}
void shader::set(const std::string& name, double value){impl_->set(name, value);}\r
+void shader::set(const std::string& name, double value1, double value2){impl_->set(name, value1, value2);}
int shader::id() const{return impl_->program_;}\r
\r
-}}
\ No newline at end of file
+}}
void set(const std::string& name, bool value);\r
void set(const std::string& name, int value);\r
void set(const std::string& name, float value);\r
- void set(const std::string& name, double value);\r
+ void set(const std::string& name, float value1, float value2);
+ void set(const std::string& name, float value1, float value2, float value3);
+ void set(const std::string& name, float value1, float value2, float value3, float value4);
+ void set(const std::string& name, double value);
+ void set(const std::string& name, double value1, double value2);
private:\r
friend class ogl_device;\r
struct implementation;\r
int id() const;\r
};\r
\r
-}}
\ No newline at end of file
+}}
\r
namespace caspar { namespace core {\r
\r
-blend_mode::type get_blend_mode(const std::wstring& str)\r
+blend_mode::type get_blend_mode(const std::wstring& str)
{\r
if(boost::iequals(str, L"normal"))\r
return blend_mode::normal;\r
return blend_mode::normal;\r
}\r
\r
-}}
\ No newline at end of file
+chroma::type get_chroma_mode(const std::wstring& str)
+{
+ if (boost::iequals(str, L"none"))
+ return chroma::none;
+ else if(boost::iequals(str, L"red"))
+ return chroma::red;
+ else if(boost::iequals(str, L"yellow"))
+ return chroma::yellow;
+ else if(boost::iequals(str, L"green"))
+ return chroma::green;
+ else if(boost::iequals(str, L"torquise"))
+ return chroma::torquise;
+ else if(boost::iequals(str, L"blue"))
+ return chroma::blue;
+ else if(boost::iequals(str, L"magenta"))
+ return chroma::magenta;
+
+ return chroma::none;
+}
+
+}}
\r
namespace caspar { namespace core {\r
\r
+struct chroma
+{
+ enum type
+ {
+ none = 0x000000,
+ red = 0xff0000,
+ yellow = 0xffff00,
+ green = 0x00ff00,
+ torquise = 0x00ffff,
+ blue = 0x0000ff,
+ magenta = 0xff00ff
+ };
+
+ unsigned int key;
+ float threshold,
+ softness,
+ spill,
+ blur;
+ bool show_mask;
+
+ chroma(type m=none)
+ : key(m), threshold(0.0), softness(0.0), spill(0.0),
+ blur(0.0), show_mask(false) {}
+};
+
struct blend_mode\r
{\r
enum type \r
mix,\r
blend_mode_count \r
};\r
-};\r
\r
-blend_mode::type get_blend_mode(const std::wstring& str);\r
+ type mode;
+ chroma chroma;
\r
-}}
\ No newline at end of file
+ blend_mode(type t = normal) : mode(t) {}
+};
+
+blend_mode::type get_blend_mode(const std::wstring& str);
+
+chroma::type get_chroma_mode(const std::wstring& str);
+
+}}
shader_->set("local_key", texture_id::local_key);\r
shader_->set("layer_key", texture_id::layer_key);\r
shader_->set("is_hd", params.pix_desc.planes.at(0).height > 700 ? 1 : 0);\r
- shader_->set("has_local_key", params.local_key);\r
- shader_->set("has_layer_key", params.layer_key);\r
+ shader_->set("has_local_key", bool(params.local_key));
+ shader_->set("has_layer_key", bool(params.layer_key));
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("chroma_mode", params.blend_mode.chroma.key == chroma::green ? 1 : (params.blend_mode.chroma.key == chroma::blue ? 2 : 0));
+ shader_->set("chroma_blend", params.blend_mode.chroma.threshold, params.blend_mode.chroma.softness);
+ shader_->set("chroma_spill", params.blend_mode.chroma.spill);
+// shader_->set("chroma.key", ((params.blend_mode.chroma.key >> 24) && 0xff)/255.0f,
+// ((params.blend_mode.chroma.key >> 16) && 0xff)/255.0f,
+// (params.blend_mode.chroma.key & 0xff)/255.0f);
+// if (params.blend_mode.chroma.key != chroma::none)
+// {
+// shader_->set("chroma.threshold", params.blend_mode.chroma.threshold);
+// shader_->set("chroma.softness", params.blend_mode.chroma.softness);
+// shader_->set("chroma.blur", params.blend_mode.chroma.blur);
+// shader_->set("chroma.spill", params.blend_mode.chroma.spill);
+// shader_->set("chroma.show_mask", params.blend_mode.chroma.show_mask);
+// }
\r
- // Setup blend_func\r
- \r
+ // Setup blend_func
if(params.transform.is_key)\r
params.blend_mode = blend_mode::normal;\r
\r
params.background->bind(6);\r
\r
shader_->set("background", texture_id::background);\r
- shader_->set("blend_mode", params.blend_mode);\r
+ shader_->set("blend_mode", params.blend_mode.mode);
shader_->set("keyer", params.keyer);\r
}\r
else\r
impl_->draw(std::move(params));\r
}\r
\r
-}}
\ No newline at end of file
+}}
pixel_format_desc pix_desc;\r
std::vector<safe_ptr<device_buffer>> textures;\r
frame_transform transform;\r
- blend_mode::type blend_mode;\r
+ blend_mode blend_mode;
keyer::type keyer;\r
std::shared_ptr<device_buffer> background;\r
std::shared_ptr<device_buffer> local_key;\r
frame_transform transform;\r
};\r
\r
-typedef std::pair<blend_mode::type, std::vector<item>> layer;\r
+typedef std::pair<blend_mode, std::vector<item>> layer;
\r
class image_renderer\r
{\r
std::shared_ptr<device_buffer> local_key_buffer;\r
std::shared_ptr<device_buffer> local_mix_buffer;\r
\r
- if(layer.first != blend_mode::normal)\r
+ if(layer.first.mode != blend_mode::normal || layer.first.chroma.key != chroma::none)
{\r
auto layer_draw_buffer = create_mixer_buffer(4, format_desc);\r
\r
BOOST_FOREACH(auto& item, layer.second) \r
draw_item(std::move(item), draw_buffer, layer_key_buffer, local_key_buffer, local_mix_buffer, format_desc); \r
\r
- draw_mixer_buffer(draw_buffer, std::move(local_mix_buffer), blend_mode::normal);\r
+ draw_mixer_buffer(draw_buffer, std::move(local_mix_buffer), layer.first);
} \r
\r
layer_key_buffer = std::move(local_key_buffer);\r
\r
void draw_mixer_buffer(safe_ptr<device_buffer>& draw_buffer, \r
std::shared_ptr<device_buffer>&& source_buffer, \r
- blend_mode::type blend_mode = blend_mode::normal)\r
+ blend_mode blend_mode = blend_mode::normal)
{\r
if(!source_buffer)\r
return;\r
{\r
}\r
\r
- void begin_layer(blend_mode::type blend_mode)\r
+ void begin_layer(blend_mode blend_mode)
{\r
layers_.push_back(std::make_pair(blend_mode, std::vector<item>()));\r
}\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
-void image_mixer::begin_layer(blend_mode::type blend_mode){impl_->begin_layer(blend_mode);}\r
+void image_mixer::begin_layer(blend_mode blend_mode){impl_->begin_layer(blend_mode);}
void image_mixer::end_layer(){impl_->end_layer();}\r
\r
}}
\ No newline at end of file
virtual void visit(core::write_frame& frame);\r
virtual void end();\r
\r
- void begin_layer(blend_mode::type blend_mode);\r
+ void begin_layer(blend_mode blend_mode);
void end_layer();\r
\r
boost::unique_future<safe_ptr<host_buffer>> operator()(const video_format_desc& format_desc);\r
;\r
\r
return glsl;\r
-}
\ No newline at end of file
+}
+
+static std::string get_chroma_glsl()
+{
+ static std::string glsl =
+ "// Chroma keying \n"
+ "// Author: Tim Eves <timseves@googlemail.com> \n"
+ "// \n"
+ "// This implements the Chroma key algorithm described in the paper: \n"
+ "// 'Software Chroma Keying in an Imersive Virtual Environment' \n"
+ "// by F. van den Bergh & V. Lalioti \n"
+ "// but as a pixel shader algorithm. \n"
+ "// \n"
+ " \n"
+ "float chroma_blend_w = chroma_blend.y - chroma_blend.x; \n"
+ "const vec4 grey_xfer = vec4(0.3, 0.59, 0.11, 0.0); \n"
+ " \n"
+ "float fma(float a, float b, float c) { return a*b + c; } \n"
+ " \n"
+ "// This allows us to implement the paper's alphaMap curve in software \n"
+ "// rather than a largeish array \n"
+ "float alpha_map(float d) \n"
+ "{ \n"
+ " return 1.0-smoothstep(chroma_blend.x, chroma_blend.y, d); \n"
+ "} \n"
+ " \n"
+ "vec4 supress_spill(vec4 c, float d) \n"
+ "{ \n"
+ " float ds = smoothstep(chroma_spill, 1.0, d/chroma_blend.y); \n"
+ " float gl = dot(grey_xfer, c); \n"
+ " return mix(c, vec4(vec3(gl*gl), gl), ds); \n"
+ "} \n"
+ " \n"
+ "// Key on green \n"
+ "vec4 ChromaOnGreen(vec4 c) \n"
+ "{ \n"
+ " float d = fma(2.0, c.g, -c.r - c.b)/2.0; \n"
+ " c *= alpha_map(d); \n"
+ " return supress_spill(c, d); \n"
+ "} \n"
+ " \n"
+ "//Key on blue \n"
+ "vec4 ChromaOnBlue(vec4 c) \n"
+ "{ \n"
+ " float d = fma(2.0, c.b, -c.r - c.g)/2.0; \n"
+ " c *= alpha_map(d); \n"
+ " return supress_spill(c, d); \n"
+ "} \n"
+ ;
+
+ return glsl;
+}
"} \n";\r
}\r
\r
+std::string get_chroma_func()
+{
+ return get_chroma_glsl()
+
+ +
+
+ "vec4 chroma_key(vec4 c) \n"
+ "{ \n"
+ " switch (chroma_mode) \n"
+ " { \n"
+ " case 0: return c; \n"
+ " case 1: return ChromaOnGreen(c.bgra).bgra; \n"
+ " case 2: return ChromaOnBlue(c.bgra).bgra; \n"
+ " } \n"
+ " return c; \n"
+ "} \n";
+}
+
std::string get_fragment(bool blend_modes)\r
{\r
return\r
"uniform float sat; \n"\r
"uniform float con; \n"\r
" \n" \r
+ "uniform int chroma_mode; \n"
+ "uniform vec2 chroma_blend; \n"
+ "uniform float chroma_spill; \n"
\r
+\r
\r
(blend_modes ? get_blend_color_func() : get_simple_blend_color_func())\r
\r
+
+ +
+
+ get_chroma_func()
+
+\r
\r
" \n"\r
"void main() \n"\r
"{ \n"\r
" vec4 color = get_rgba_color(); \n"\r
+ " color = chroma_key(color); \n"
" if(levels) \n"\r
" color.rgb = LevelsControl(color.rgb, min_input, max_input, gamma, min_output, max_output); \n"\r
" if(csb) \n"\r
\r
#include <common/memory/safe_ptr.h>\r
\r
+#define SHADER_PROGRAM(prog) #prog
+
namespace caspar { namespace core {\r
\r
class shader;\r
audio_mixer audio_mixer_;\r
image_mixer image_mixer_;\r
\r
- std::unordered_map<int, blend_mode::type> blend_modes_;\r
+ std::unordered_map<int, blend_mode> blend_modes_;\r
\r
executor executor_;\r
\r
{\r
executor_.begin_invoke([=]\r
{\r
- blend_modes_[index] = value;\r
+ blend_modes_[index].mode = value;\r
}, high_priority);\r
}\r
\r
}, high_priority);\r
}\r
\r
+ void set_chroma(int index, const chroma & value)\r
+ {\r
+ executor_.begin_invoke([=]\r
+ {\r
+ blend_modes_[index].chroma = value;\r
+ }, high_priority);\r
+ }\r
+\r
void set_master_volume(float volume)\r
{\r
executor_.begin_invoke([=]\r
core::video_format_desc mixer::get_video_format_desc() const { return impl_->get_video_format_desc(); }\r
safe_ptr<core::write_frame> mixer::create_frame(const void* tag, const core::pixel_format_desc& desc, const channel_layout& audio_channel_layout){ return impl_->create_frame(tag, desc, audio_channel_layout); } \r
void mixer::set_blend_mode(int index, blend_mode::type value){impl_->set_blend_mode(index, value);}\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_master_volume(float volume) { impl_->set_master_volume(volume); }\r
core::video_format_desc get_video_format_desc() const; // nothrow\r
void set_video_format_desc(const video_format_desc& format_desc);\r
\r
- void set_blend_mode(int index, blend_mode::type value);\r
+ void set_blend_mode(int index, blend_mode::type value);
+ void set_chroma(int index, const chroma & value);
void clear_blend_mode(int index);\r
void clear_blend_modes();\r
\r
field_mode = static_cast<field_mode::type>(field_mode & other.field_mode);\r
is_key |= other.is_key;\r
is_mix |= other.is_mix;\r
+
return *this;\r
}\r
\r
result.field_mode = static_cast<field_mode::type>(source.field_mode & dest.field_mode);\r
result.is_key = source.is_key | dest.is_key;\r
result.is_mix = source.is_mix | dest.is_mix;\r
- \r
return result;\r
}\r
\r
bool operator==(const frame_transform& lhs, const frame_transform& rhs);\r
bool operator!=(const frame_transform& lhs, const frame_transform& rhs);\r
\r
-}}
\ No newline at end of file
+}}
\r
/* Return codes\r
\r
-100 [action] Information om att något har hänt \r
-101 [action] Information om att något har hänt, en rad data skickas \r
+100 [action] Information om att n�got har h�nt \r
+101 [action] Information om att n�got har h�nt, en rad data skickas \r
\r
-202 [kommando] OK Kommandot har utförts \r
-201 [kommando] OK Kommandot har utförts, och en rad data skickas tillbaka \r
-200 [kommando] OK Kommandot har utförts, och flera rader data skickas tillbaka. Avslutas med tomrad \r
+202 [kommando] OK Kommandot har utf�rts \r
+201 [kommando] OK Kommandot har utf�rts, och en rad data skickas tillbaka \r
+200 [kommando] OK Kommandot har utf�rts, och flera rader data skickas tillbaka. Avslutas med tomrad \r
\r
-400 ERROR Kommandot kunde inte förstås \r
+400 ERROR Kommandot kunde inte f�rst�s \r
401 [kommando] ERROR Ogiltig kanal \r
402 [kommando] ERROR Parameter saknas \r
403 [kommando] ERROR Ogiltig parameter \r
\r
500 FAILED Internt configurationfel \r
501 [kommando] FAILED Internt configurationfel \r
-502 [kommando] FAILED Oläslig mediafil \r
+502 [kommando] FAILED Ol�slig mediafil \r
\r
600 [kommando] FAILED funktion ej implementerad\r
*/\r
{\r
auto blend_str = _parameters.at(1); \r
int layer = GetLayerIndex();\r
- GetChannel()->mixer()->set_blend_mode(GetLayerIndex(), get_blend_mode(blend_str)); \r
+ blend_mode::type && blend = get_blend_mode(blend_str);\r
+ GetChannel()->mixer()->set_blend_mode(GetLayerIndex(), blend); \r
}\r
+ else if(_parameters[0] == L"CHROMA")\r
+ {\r
+ int layer = GetLayerIndex();\r
+ chroma chroma;\r
+ chroma.key = get_chroma_mode(_parameters[1]);\r
+ chroma.threshold = boost::lexical_cast<double>(_parameters[2]);\r
+ chroma.softness = boost::lexical_cast<double>(_parameters[3]);\r
+ chroma.spill = _parameters.size() > 4 ? boost::lexical_cast<double>(_parameters[4]) : 0.0f;\r
+ chroma.blur = _parameters.size() > 5 ? boost::lexical_cast<double>(_parameters[5]) : 0.0f;\r
+ chroma.show_mask = _parameters.size() > 6 ? bool(boost::lexical_cast<int>(_parameters[6])) : false;\r
+ GetChannel()->mixer()->set_chroma(GetLayerIndex(), chroma);\r
+ }\r
else if(_parameters[0] == L"MASTERVOLUME")\r
{\r
float master_volume = boost::lexical_cast<float>(_parameters.at(1));\r
}\r
\r
} //namespace amcp\r
-}} //namespace caspar
\ No newline at end of file
+}} //namespace caspar\r