<ClInclude Include="mixer\gpu\fence.h" />\r
<ClInclude Include="mixer\gpu\shader.h" />\r
<ClInclude Include="mixer\image\blending_glsl.h" />\r
+ <ClInclude Include="mixer\image\blend_modes.h" />\r
<ClInclude Include="mixer\image\image_shader.h" />\r
<ClInclude Include="video_channel.h" />\r
<ClInclude Include="video_channel_context.h" />\r
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Develop|Win32'">../../../StdAfx.h</PrecompiledHeaderFile>\r
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">../../../StdAfx.h</PrecompiledHeaderFile>\r
</ClCompile>\r
+ <ClCompile Include="mixer\image\blend_modes.cpp">\r
+ <PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Profile|Win32'">../../StdAfx.h</PrecompiledHeaderFile>\r
+ <PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">../../StdAfx.h</PrecompiledHeaderFile>\r
+ <PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Develop|Win32'">../../StdAfx.h</PrecompiledHeaderFile>\r
+ <PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">../../StdAfx.h</PrecompiledHeaderFile>\r
+ </ClCompile>\r
<ClCompile Include="mixer\image\image_shader.cpp">\r
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Profile|Win32'">../../StdAfx.h</PrecompiledHeaderFile>\r
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">../../StdAfx.h</PrecompiledHeaderFile>\r
<ClInclude Include="mixer\image\image_shader.h">\r
<Filter>source\mixer\image</Filter>\r
</ClInclude>\r
+ <ClInclude Include="mixer\image\blend_modes.h">\r
+ <Filter>source\mixer\image</Filter>\r
+ </ClInclude>\r
</ItemGroup>\r
<ItemGroup>\r
<ClCompile Include="producer\transition\transition_producer.cpp">\r
<ClCompile Include="mixer\image\image_shader.cpp">\r
<Filter>source\mixer\image</Filter>\r
</ClCompile>\r
+ <ClCompile Include="mixer\image\blend_modes.cpp">\r
+ <Filter>source\mixer\image</Filter>\r
+ </ClCompile>\r
</ItemGroup>\r
</Project>
\ No newline at end of file
--- /dev/null
+#include "../../StdAfx.h"\r
+\r
+#include "blend_modes.h"\r
+\r
+#include <boost/algorithm/string.hpp>\r
+\r
+namespace caspar { namespace core {\r
+ \r
+blend_mode::type get_blend_mode(const std::wstring& str)\r
+{\r
+ if(boost::iequals(str, L"normal"))\r
+ return blend_mode::normal;\r
+ else if(boost::iequals(str, L"lighten"))\r
+ return blend_mode::lighten;\r
+ else if(boost::iequals(str, L"darken"))\r
+ return blend_mode::darken;\r
+ else if(boost::iequals(str, L"multiply"))\r
+ return blend_mode::multiply;\r
+ else if(boost::iequals(str, L"average"))\r
+ return blend_mode::average;\r
+ else if(boost::iequals(str, L"add"))\r
+ return blend_mode::add;\r
+ else if(boost::iequals(str, L"subtract"))\r
+ return blend_mode::subtract;\r
+ else if(boost::iequals(str, L"difference"))\r
+ return blend_mode::difference;\r
+ else if(boost::iequals(str, L"negation"))\r
+ return blend_mode::negation;\r
+ else if(boost::iequals(str, L"exclusion"))\r
+ return blend_mode::exclusion;\r
+ else if(boost::iequals(str, L"screen"))\r
+ return blend_mode::screen;\r
+ else if(boost::iequals(str, L"overlay"))\r
+ return blend_mode::overlay;\r
+ else if(boost::iequals(str, L"soft_light"))\r
+ return blend_mode::soft_light;\r
+ else if(boost::iequals(str, L"hard_light"))\r
+ return blend_mode::hard_light;\r
+ else if(boost::iequals(str, L"color_dodge"))\r
+ return blend_mode::color_dodge;\r
+ else if(boost::iequals(str, L"color_burn"))\r
+ return blend_mode::color_burn;\r
+ else if(boost::iequals(str, L"linear_dodge"))\r
+ return blend_mode::linear_dodge;\r
+ else if(boost::iequals(str, L"linear_burn"))\r
+ return blend_mode::linear_burn;\r
+ else if(boost::iequals(str, L"linear_light"))\r
+ return blend_mode::linear_light;\r
+ else if(boost::iequals(str, L"vivid_light"))\r
+ return blend_mode::vivid_light;\r
+ else if(boost::iequals(str, L"pin_light"))\r
+ return blend_mode::pin_light;\r
+ else if(boost::iequals(str, L"hard_mix"))\r
+ return blend_mode::hard_mix;\r
+ else if(boost::iequals(str, L"reflect"))\r
+ return blend_mode::reflect;\r
+ else if(boost::iequals(str, L"glow"))\r
+ return blend_mode::glow;\r
+ else if(boost::iequals(str, L"phoenix"))\r
+ return blend_mode::phoenix;\r
+ else if(boost::iequals(str, L"contrast"))\r
+ return blend_mode::contrast;\r
+ else if(boost::iequals(str, L"saturation"))\r
+ return blend_mode::saturation;\r
+ else if(boost::iequals(str, L"color"))\r
+ return blend_mode::color;\r
+ else if(boost::iequals(str, L"luminosity"))\r
+ return blend_mode::luminosity;\r
+ \r
+ return blend_mode::normal;\r
+}\r
+\r
+}}
\ No newline at end of file
--- /dev/null
+#pragma once\r
+\r
+namespace caspar { namespace core {\r
+ \r
+struct blend_mode\r
+{\r
+ enum type \r
+ {\r
+ normal = 0,\r
+ lighten,\r
+ darken,\r
+ multiply,\r
+ average,\r
+ add,\r
+ subtract,\r
+ difference,\r
+ negation,\r
+ exclusion,\r
+ screen,\r
+ overlay,\r
+ soft_light,\r
+ hard_light,\r
+ color_dodge,\r
+ color_burn,\r
+ linear_dodge,\r
+ linear_burn,\r
+ linear_light,\r
+ vivid_light,\r
+ pin_light,\r
+ hard_mix,\r
+ reflect,\r
+ glow,\r
+ phoenix,\r
+ contrast,\r
+ saturation,\r
+ color,\r
+ luminosity,\r
+ replace,\r
+ blend_mode_count \r
+ };\r
+};\r
+\r
+blend_mode::type get_blend_mode(const std::wstring& str);\r
+\r
+}}
\ No newline at end of file
struct image_kernel::implementation : boost::noncopyable\r
{ \r
std::shared_ptr<shader> shader_;\r
- bool advanced_blend_modes_;\r
+ bool blend_modes_;\r
\r
void draw(ogl_device& ogl,\r
render_item&& item,\r
// Setup shader\r
\r
if(!shader_)\r
- shader_ = get_image_shader(ogl, advanced_blend_modes_);\r
+ shader_ = get_image_shader(ogl, blend_modes_);\r
\r
ogl.use(*shader_);\r
\r
shader_->set("has_local_key", local_key);\r
shader_->set("has_layer_key", layer_key);\r
shader_->set("pixel_format", item.pix_desc.pix_fmt); \r
- shader_->set("opacity", item.transform.get_opacity()); \r
+ shader_->set("opacity", item.transform.get_is_key() ? 1.0 : item.transform.get_opacity()); \r
\r
// Setup blend_func\r
\r
- if(advanced_blend_modes_)\r
+ if(item.transform.get_is_key())\r
+ item.blend_mode = blend_mode::normal;\r
+\r
+ if(blend_modes_)\r
{\r
background->bind(6);\r
\r
shader_->set("background", texture_id::background);\r
- shader_->set("blend_mode", item.transform.get_is_key() ? core::image_transform::blend_mode::normal : item.transform.get_blend_mode());\r
+ shader_->set("blend_mode", item.blend_mode);\r
}\r
else\r
{\r
- switch(item.transform.get_blend_mode())\r
+ switch(item.blend_mode)\r
{\r
- case image_transform::blend_mode::add: \r
- ogl.blend_func_separate(GL_ONE, GL_ONE, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);\r
- break;\r
- case image_transform::blend_mode::replace: \r
+ case blend_mode::replace: \r
ogl.blend_func_separate(GL_ONE, GL_ZERO, GL_ONE, GL_ONE);\r
break;\r
- case image_transform::blend_mode::screen:\r
- ogl.blend_func_separate(GL_ONE, GL_ONE_MINUS_SRC_COLOR, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);\r
- case image_transform::blend_mode::normal:\r
+ case blend_mode::normal:\r
default:\r
ogl.blend_func_separate(GL_ONE, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);\r
break;\r
item.textures.clear();\r
ogl.yield(); // Return resources to pool as early as possible.\r
\r
- if(advanced_blend_modes_)\r
+ if(blend_modes_)\r
{\r
// http://www.opengl.org/registry/specs/NV/texture_barrier.txt\r
// This allows us to use framebuffer (background) both as source and target while blending.\r
};\r
\r
image_kernel::image_kernel() : impl_(new implementation()){}\r
-void image_kernel::draw(ogl_device& ogl, render_item&& item, const safe_ptr<device_buffer>& background, const std::shared_ptr<device_buffer>& local_key, const std::shared_ptr<device_buffer>& layer_key)\r
+void image_kernel::draw(ogl_device& ogl, \r
+ render_item&& item, \r
+ const safe_ptr<device_buffer>& background,\r
+ const std::shared_ptr<device_buffer>& local_key, \r
+ const std::shared_ptr<device_buffer>& layer_key)\r
{\r
impl_->draw(ogl, std::move(item), background, local_key, layer_key);\r
}\r
*/\r
#pragma once\r
\r
+#include "blend_modes.h"\r
+\r
#include <common/memory/safe_ptr.h>\r
\r
#include <core/producer/frame/pixel_format.h>\r
image_transform transform;\r
video_mode::type mode;\r
const void* tag;\r
+ blend_mode::type blend_mode;\r
\r
- render_item(const pixel_format_desc& pix_desc, const std::vector<safe_ptr<device_buffer>>& textures, const image_transform& transform, video_mode::type mode, const void* tag)\r
+ render_item(const pixel_format_desc& pix_desc, const std::vector<safe_ptr<device_buffer>>& textures, const image_transform& transform, video_mode::type mode, const void* tag, blend_mode::type blend_mode)\r
: pix_desc(pix_desc)\r
, textures(textures)\r
, transform(transform)\r
, mode(mode)\r
- , tag(tag){}\r
+ , tag(tag)\r
+ , blend_mode(blend_mode){}\r
render_item(render_item&& other)\r
: pix_desc(other.pix_desc)\r
, textures(std::move(other.textures))\r
, transform(other.transform)\r
, mode(other.mode)\r
- , tag(other.tag){}\r
+ , tag(other.tag)\r
+ , blend_mode(blend_mode){}\r
};\r
\r
bool operator==(const render_item& lhs, const render_item& rhs);\r
{\r
public:\r
image_kernel();\r
- void draw(ogl_device& ogl, render_item&& item, const safe_ptr<device_buffer>& background, const std::shared_ptr<device_buffer>& local_key = nullptr, const std::shared_ptr<device_buffer>& layer_key = nullptr);\r
+ void draw(ogl_device& ogl, \r
+ render_item&& item, \r
+ const safe_ptr<device_buffer>& background, \r
+ const std::shared_ptr<device_buffer>& local_key = nullptr, \r
+ const std::shared_ptr<device_buffer>& layer_key = nullptr);\r
private:\r
struct implementation;\r
safe_ptr<implementation> impl_;\r
\r
std::vector<image_transform> transform_stack_;\r
std::vector<video_mode::type> mode_stack_;\r
+ std::stack<blend_mode::type> blend_stack_;\r
\r
std::deque<std::deque<render_item>> layers_; // layer/stream/items\r
\r
if(boost::range::find(mode_stack_, video_mode::upper) != mode_stack_.end() && boost::range::find(mode_stack_, video_mode::lower) != mode_stack_.end())\r
return;\r
\r
- core::render_item item(frame.get_pixel_format_desc(), frame.get_textures(), transform_stack_.back(), mode_stack_.back(), frame.tag()); \r
+ core::render_item item(frame.get_pixel_format_desc(), frame.get_textures(), transform_stack_.back(), mode_stack_.back(), frame.tag(), blend_stack_.top()); \r
\r
auto& layer = layers_.back();\r
\r
mode_stack_.pop_back();\r
}\r
\r
- void begin_layer()\r
+ void begin_layer(blend_mode::type blend_mode)\r
{\r
+ blend_stack_.push(blend_mode);\r
layers_.push_back(layer());\r
}\r
\r
void end_layer()\r
{\r
+ blend_stack_.pop();\r
}\r
\r
boost::unique_future<safe_ptr<host_buffer>> render()\r
return;\r
\r
std::pair<int, std::shared_ptr<device_buffer>> local_key_buffer;\r
- \r
- //if(has_overlapping_items(layer, layer.front().transform.get_blend_mode()))\r
- //{\r
- // auto local_draw_buffer = create_device_buffer(4); \r
+ \r
+ if(has_overlapping_items(layer, layer.front().blend_mode))\r
+ {\r
+ auto local_draw_buffer = create_device_buffer(4); \r
\r
- // auto local_blend_mode = layer.front().transform.get_blend_mode();\r
+ auto local_blend_mode = layer.front().blend_mode;\r
\r
- // int fields = 0;\r
- // BOOST_FOREACH(auto& item, layer)\r
- // {\r
- // if(fields & item.mode)\r
- // item.transform.set_blend_mode(image_transform::blend_mode::normal); // Disable blending, it will be used when merging back into render stack.\r
- // else\r
- // {\r
- // item.transform.set_blend_mode(image_transform::blend_mode::replace); // Target field is empty, no blending, just copy\r
- // fields |= item.mode;\r
- // }\r
-\r
- // draw_item(std::move(item), local_draw_buffer, local_key_buffer, layer_key_buffer); \r
- // }\r
-\r
- // kernel_.draw(channel_.ogl(), create_render_item(local_draw_buffer, local_blend_mode), draw_buffer, nullptr, nullptr);\r
- //}\r
- //else // fast path\r
- //{\r
+ int fields = 0;\r
+ BOOST_FOREACH(auto& item, layer)\r
+ {\r
+ if(fields & item.mode)\r
+ item.blend_mode = blend_mode::normal; // Disable blending, it will be used when merging back into render stack.\r
+ else\r
+ {\r
+ item.blend_mode = blend_mode::replace; // Target field is empty, no blending, just copy\r
+ fields |= item.mode;\r
+ }\r
+\r
+ draw_item(std::move(item), local_draw_buffer, local_key_buffer, layer_key_buffer); \r
+ }\r
+\r
+ kernel_.draw(channel_.ogl(), create_render_item(local_draw_buffer, local_blend_mode), draw_buffer, nullptr, nullptr);\r
+ }\r
+ else // fast path\r
+ {\r
BOOST_FOREACH(auto& item, layer) \r
draw_item(std::move(item), draw_buffer, local_key_buffer, layer_key_buffer); \r
- //} \r
+ } \r
\r
CASPAR_ASSERT(local_key_buffer.first == 0 || local_key_buffer.first == core::video_mode::progressive);\r
\r
local_key_buffer.first = 0;\r
local_key_buffer.second = create_device_buffer(1);\r
}\r
-\r
- // No transparency for key\r
- item.transform.set_opacity(1.0);\r
- item.transform.set_blend_mode(image_transform::blend_mode::normal);\r
-\r
+ \r
local_key_buffer.first |= item.mode;\r
kernel_.draw(channel_.ogl(), std::move(item), make_safe(local_key_buffer.second), nullptr, nullptr);\r
}\r
}\r
\r
//// TODO: Optimize\r
- //bool has_overlapping_items(const layer& layer, image_transform::blend_mode::type blend_mode)\r
- //{\r
- // if(layer.size() < 2)\r
- // return false; \r
- // \r
- // implementation::layer fill;\r
-\r
- // std::copy_if(layer.begin(), layer.end(), std::back_inserter(fill), [&](const render_item& item)\r
- // {\r
- // return !item.transform.get_is_key();\r
- // });\r
- // \r
- // if(blend_mode == image_transform::blend_mode::normal) // Only overlap if opacity\r
- // {\r
- // return std::any_of(fill.begin(), fill.end(), [&](const render_item& item)\r
- // {\r
- // return item.transform.get_opacity() < 1.0 - 0.001;\r
- // });\r
- // }\r
-\r
- // // Simple solution, just check if we have differnt video streams / tags.\r
- // return std::any_of(fill.begin(), fill.end(), [&](const render_item& item)\r
- // {\r
- // return item.tag != fill.front().tag;\r
- // });\r
- //} \r
- // \r
- //render_item create_render_item(const safe_ptr<device_buffer>& buffer, image_transform::blend_mode::type blend_mode)\r
- //{\r
- // CASPAR_ASSERT(buffer->stride() == 4 && "Only used for bgra textures");\r
-\r
- // pixel_format_desc desc;\r
- // desc.pix_fmt = pixel_format::bgra;\r
- // desc.planes.push_back(pixel_format_desc::plane(channel_.get_format_desc().width, channel_.get_format_desc().height, 4));\r
-\r
- // std::vector<safe_ptr<device_buffer>> textures;\r
- // textures.push_back(buffer);\r
- // \r
- // image_transform transform;\r
- // transform.set_blend_mode(blend_mode);\r
-\r
- // return render_item(desc, std::move(textures), transform, video_mode::progressive, nullptr); \r
- //}\r
+ bool has_overlapping_items(const layer& layer, blend_mode::type blend_mode)\r
+ {\r
+ if(layer.size() < 2)\r
+ return false; \r
+ \r
+ if(blend_mode == blend_mode::normal)\r
+ return false;\r
+ \r
+ return std::any_of(layer.begin(), layer.end(), [&](const render_item& item)\r
+ {\r
+ return item.tag != layer.front().tag;\r
+ });\r
+\r
+ //std::copy_if(layer.begin(), layer.end(), std::back_inserter(fill), [&](const render_item& item)\r
+ //{\r
+ // return !item.transform.get_is_key();\r
+ //});\r
+ // \r
+ //if(blend_mode == blend_mode::normal) // only overlap if opacity\r
+ //{\r
+ // return std::any_of(fill.begin(), fill.end(), [&](const render_item& item)\r
+ // {\r
+ // return item.transform.get_opacity() < 1.0 - 0.001;\r
+ // });\r
+ //}\r
+\r
+ //// simple solution, just check if we have differnt video streams / tags.\r
+ //return std::any_of(fill.begin(), fill.end(), [&](const render_item& item)\r
+ //{\r
+ // return item.tag != fill.front().tag;\r
+ //});\r
+ } \r
+ \r
+ render_item create_render_item(const safe_ptr<device_buffer>& buffer, blend_mode::type blend_mode)\r
+ {\r
+ CASPAR_ASSERT(buffer->stride() == 4 && "Only used for bgra textures");\r
+\r
+ pixel_format_desc desc;\r
+ desc.pix_fmt = pixel_format::bgra;\r
+ desc.planes.push_back(pixel_format_desc::plane(channel_.get_format_desc().width, channel_.get_format_desc().height, 4));\r
+\r
+ std::vector<safe_ptr<device_buffer>> textures;\r
+ textures.push_back(buffer);\r
+ \r
+ return render_item(desc, std::move(textures), image_transform(), video_mode::progressive, nullptr, blend_mode); \r
+ }\r
\r
safe_ptr<device_buffer> create_device_buffer(size_t stride)\r
{\r
void image_mixer::end(){impl_->end();}\r
boost::unique_future<safe_ptr<host_buffer>> image_mixer::render(){return impl_->render();}\r
safe_ptr<write_frame> image_mixer::create_frame(const void* tag, const core::pixel_format_desc& desc){return impl_->create_frame(tag, desc);}\r
-void image_mixer::begin_layer(){impl_->begin_layer();}\r
+void image_mixer::begin_layer(blend_mode::type blend_mode){impl_->begin_layer(blend_mode);}\r
void image_mixer::end_layer(){impl_->end_layer();}\r
image_mixer& image_mixer::operator=(image_mixer&& other)\r
{\r
*/\r
#pragma once\r
\r
+#include "blend_modes.h"\r
+\r
#include <common/memory/safe_ptr.h>\r
\r
#include <core/producer/frame/frame_visitor.h>\r
virtual void visit(core::write_frame& frame);\r
virtual void end();\r
\r
- void begin_layer();\r
+ void begin_layer(blend_mode::type blend_mode);\r
void end_layer();\r
\r
image_mixer& operator=(image_mixer&& other);\r
boost::fusion::map<boost::fusion::pair<core::image_transform, image_transforms>,\r
boost::fusion::pair<core::audio_transform, audio_transforms>> transforms_;\r
\r
+ std::unordered_map<int, blend_mode::type> blend_modes_;\r
+\r
std::queue<std::pair<boost::unique_future<safe_ptr<host_buffer>>, std::vector<int16_t>>> buffer_;\r
\r
const size_t buffer_size_;\r
});\r
}\r
\r
+ void set_blend_mode(int index, blend_mode::type value)\r
+ {\r
+ blend_modes_[index] = value;\r
+ }\r
+\r
std::wstring print() const\r
{\r
return L"mixer";\r
\r
BOOST_FOREACH(auto& frame, frames)\r
{\r
- image_mixer_.begin_layer();\r
+ auto blend_it = blend_modes_.find(frame.first);\r
+ image_mixer_.begin_layer(blend_it != blend_modes_.end() ? blend_it->second : blend_mode::normal);\r
\r
auto frame1 = make_safe<core::basic_frame>(frame.second);\r
frame1->get_image_transform() = image_transforms[frame.first].fetch_and_tick(1);\r
void mixer::set_audio_transform(int index, const core::audio_transform& transform, unsigned int mix_duration, const std::wstring& tween){impl_->set_transform<core::audio_transform>(index, transform, mix_duration, tween);}\r
void mixer::apply_image_transform(int index, const std::function<core::image_transform(core::image_transform)>& transform, unsigned int mix_duration, const std::wstring& tween){impl_->apply_transform<core::image_transform>(index, transform, mix_duration, tween);}\r
void mixer::apply_audio_transform(int index, const std::function<core::audio_transform(core::audio_transform)>& transform, unsigned int mix_duration, const std::wstring& tween){impl_->apply_transform<core::audio_transform>(index, transform, mix_duration, tween);}\r
-\r
+void mixer::set_blend_mode(int index, blend_mode::type value){impl_->set_blend_mode(index, value);}\r
}}
\ No newline at end of file
*/\r
#pragma once\r
\r
+#include "image/blend_modes.h"\r
+\r
#include "../producer/frame/frame_factory.h"\r
\r
#include <common/memory/safe_ptr.h>\r
class mixer : public core::frame_factory\r
{\r
public: \r
+\r
explicit mixer(video_channel_context& video_channel);\r
\r
safe_ptr<core::read_frame> execute(const std::map<int, safe_ptr<core::basic_frame>>& frames); // nothrow\r
void apply_image_transform(int index, const std::function<core::image_transform(core::image_transform)>& transform, unsigned int mix_duration = 0, const std::wstring& tween = L"linear");\r
void apply_audio_transform(int index, const std::function<core::audio_transform(core::audio_transform)>& transform, unsigned int mix_duration = 0, const std::wstring& tween = L"linear");\r
\r
+ void set_blend_mode(int index, blend_mode::type value);\r
+\r
private:\r
struct implementation;\r
safe_ptr<implementation> impl_;\r
\r
#include <common/utility/assert.h>\r
\r
-#include <boost/algorithm/string.hpp>\r
-\r
namespace caspar { namespace core {\r
\r
image_transform::image_transform() \r
, contrast_(1.0)\r
, saturation_(1.0)\r
, is_key_(false)\r
- , blend_mode_(image_transform::blend_mode::normal)\r
{\r
std::fill(fill_translation_.begin(), fill_translation_.end(), 0.0);\r
std::fill(fill_scale_.begin(), fill_scale_.end(), 1.0);\r
return clip_scale_;\r
}\r
\r
-void image_transform::set_blend_mode(image_transform::blend_mode::type value)\r
-{\r
- blend_mode_ = value;\r
-}\r
-\r
-image_transform::blend_mode::type image_transform::get_blend_mode() const\r
-{\r
- return blend_mode_;\r
-}\r
-\r
image_transform& image_transform::operator*=(const image_transform &other)\r
{\r
opacity_ *= other.opacity_; \r
- blend_mode_ = std::max(blend_mode_, other.blend_mode_);\r
brightness_ *= other.brightness_;\r
contrast_ *= other.contrast_;\r
saturation_ *= other.saturation_;\r
};\r
\r
image_transform result; \r
- result.set_blend_mode (std::max(source.get_blend_mode(), dest.get_blend_mode()));\r
result.set_is_key (source.get_is_key() | dest.get_is_key());\r
result.set_brightness (do_tween(time, source.get_brightness(), dest.get_brightness(), duration, tweener));\r
result.set_contrast (do_tween(time, source.get_contrast(), dest.get_contrast(), duration, tweener));\r
return result;\r
}\r
\r
-image_transform::blend_mode::type get_blend_mode(const std::wstring& str)\r
-{\r
- if(boost::iequals(str, L"normal"))\r
- return image_transform::blend_mode::normal;\r
- else if(boost::iequals(str, L"lighten"))\r
- return image_transform::blend_mode::lighten;\r
- else if(boost::iequals(str, L"darken"))\r
- return image_transform::blend_mode::darken;\r
- else if(boost::iequals(str, L"multiply"))\r
- return image_transform::blend_mode::multiply;\r
- else if(boost::iequals(str, L"average"))\r
- return image_transform::blend_mode::average;\r
- else if(boost::iequals(str, L"add"))\r
- return image_transform::blend_mode::add;\r
- else if(boost::iequals(str, L"subtract"))\r
- return image_transform::blend_mode::subtract;\r
- else if(boost::iequals(str, L"difference"))\r
- return image_transform::blend_mode::difference;\r
- else if(boost::iequals(str, L"negation"))\r
- return image_transform::blend_mode::negation;\r
- else if(boost::iequals(str, L"exclusion"))\r
- return image_transform::blend_mode::exclusion;\r
- else if(boost::iequals(str, L"screen"))\r
- return image_transform::blend_mode::screen;\r
- else if(boost::iequals(str, L"overlay"))\r
- return image_transform::blend_mode::overlay;\r
- else if(boost::iequals(str, L"soft_light"))\r
- return image_transform::blend_mode::soft_light;\r
- else if(boost::iequals(str, L"hard_light"))\r
- return image_transform::blend_mode::hard_light;\r
- else if(boost::iequals(str, L"color_dodge"))\r
- return image_transform::blend_mode::color_dodge;\r
- else if(boost::iequals(str, L"color_burn"))\r
- return image_transform::blend_mode::color_burn;\r
- else if(boost::iequals(str, L"linear_dodge"))\r
- return image_transform::blend_mode::linear_dodge;\r
- else if(boost::iequals(str, L"linear_burn"))\r
- return image_transform::blend_mode::linear_burn;\r
- else if(boost::iequals(str, L"linear_light"))\r
- return image_transform::blend_mode::linear_light;\r
- else if(boost::iequals(str, L"vivid_light"))\r
- return image_transform::blend_mode::vivid_light;\r
- else if(boost::iequals(str, L"pin_light"))\r
- return image_transform::blend_mode::pin_light;\r
- else if(boost::iequals(str, L"hard_mix"))\r
- return image_transform::blend_mode::hard_mix;\r
- else if(boost::iequals(str, L"reflect"))\r
- return image_transform::blend_mode::reflect;\r
- else if(boost::iequals(str, L"glow"))\r
- return image_transform::blend_mode::glow;\r
- else if(boost::iequals(str, L"phoenix"))\r
- return image_transform::blend_mode::phoenix;\r
- else if(boost::iequals(str, L"contrast"))\r
- return image_transform::blend_mode::contrast;\r
- else if(boost::iequals(str, L"saturation"))\r
- return image_transform::blend_mode::saturation;\r
- else if(boost::iequals(str, L"color"))\r
- return image_transform::blend_mode::color;\r
- else if(boost::iequals(str, L"luminosity"))\r
- return image_transform::blend_mode::luminosity;\r
- \r
- return image_transform::blend_mode::normal;\r
-}\r
-\r
bool operator<(const image_transform& lhs, const image_transform& rhs)\r
{\r
return memcmp(&lhs, &rhs, sizeof(image_transform)) < 0;\r
{\r
public:\r
\r
- struct blend_mode\r
- {\r
- enum type \r
- {\r
- normal = 0,\r
- lighten,\r
- darken,\r
- multiply,\r
- average,\r
- add,\r
- subtract,\r
- difference,\r
- negation,\r
- exclusion,\r
- screen,\r
- overlay,\r
- soft_light,\r
- hard_light,\r
- color_dodge,\r
- color_burn,\r
- linear_dodge,\r
- linear_burn,\r
- linear_light,\r
- vivid_light,\r
- pin_light,\r
- hard_mix,\r
- reflect,\r
- glow,\r
- phoenix,\r
- contrast,\r
- saturation,\r
- color,\r
- luminosity,\r
- replace,\r
- blend_mode_count \r
- };\r
- };\r
- \r
struct levels\r
{\r
levels() \r
\r
void set_is_key(bool value);\r
bool get_is_key() const;\r
-\r
- void set_blend_mode(blend_mode::type value);\r
- blend_mode::type get_blend_mode() const;\r
- \r
+ \r
private:\r
double opacity_;\r
double gain_;\r
std::array<double, 2> clip_scale_; \r
video_mode::type mode_;\r
bool is_key_;\r
- blend_mode::type blend_mode_;\r
};\r
\r
-image_transform::blend_mode::type get_blend_mode(const std::wstring& str);\r
-\r
image_transform tween(double time, const image_transform& source, const image_transform& dest, double duration, const tweener_t& tweener);\r
\r
bool operator<(const image_transform& lhs, const image_transform& rhs);\r
}\r
else if(_parameters[1] == L"BLEND")\r
{\r
- auto blend_str = _parameters.at(2);\r
-\r
- auto transform = [=](image_transform transform) -> image_transform\r
- {\r
- transform.set_blend_mode(get_blend_mode(blend_str));\r
- return transform;\r
- };\r
- \r
+ auto blend_str = _parameters.at(2); \r
int layer = GetLayerIndex();\r
- GetChannel()->mixer()->apply_image_transform(GetLayerIndex(), transform); \r
+ GetChannel()->mixer()->set_blend_mode(GetLayerIndex(), get_blend_mode(blend_str)); \r
}\r
else if(_parameters[1] == L"BRIGHTNESS")\r
{\r