]> git.sesse.net Git - casparcg/commitdiff
2.0. image_mixer: Refactored blend-modes.
authorRonag <Ronag@362d55ac-95cf-4e76-9f9a-cbaa9c17b72d>
Fri, 19 Aug 2011 20:56:00 +0000 (20:56 +0000)
committerRonag <Ronag@362d55ac-95cf-4e76-9f9a-cbaa9c17b72d>
Fri, 19 Aug 2011 20:56:00 +0000 (20:56 +0000)
git-svn-id: https://casparcg.svn.sourceforge.net/svnroot/casparcg/server/branches/2.0.0.2@1239 362d55ac-95cf-4e76-9f9a-cbaa9c17b72d

13 files changed:
core/core.vcxproj
core/core.vcxproj.filters
core/mixer/image/blend_modes.cpp [new file with mode: 0644]
core/mixer/image/blend_modes.h [new file with mode: 0644]
core/mixer/image/image_kernel.cpp
core/mixer/image/image_kernel.h
core/mixer/image/image_mixer.cpp
core/mixer/image/image_mixer.h
core/mixer/mixer.cpp
core/mixer/mixer.h
core/producer/frame/image_transform.cpp
core/producer/frame/image_transform.h
protocol/amcp/AMCPCommandsImpl.cpp

index 36394f632f832028bd9b272553b40a3e58317418..64d8785a93183cf5cd5d83f6b50cea07be9a3ddd 100644 (file)
     <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
index f6a85347947fbe5d3b2eb875539f108df58abd72..24ab8366557fe487301eaea48519eb9cdc09be8c 100644 (file)
     <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
diff --git a/core/mixer/image/blend_modes.cpp b/core/mixer/image/blend_modes.cpp
new file mode 100644 (file)
index 0000000..d60d411
--- /dev/null
@@ -0,0 +1,73 @@
+#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
diff --git a/core/mixer/image/blend_modes.h b/core/mixer/image/blend_modes.h
new file mode 100644 (file)
index 0000000..1684639
--- /dev/null
@@ -0,0 +1,45 @@
+#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
index dae98a0e0bec41535e52591fadd939d63536ed55..4a837172d2581cb08d3e103fdfa1e55c04c0f515 100644 (file)
@@ -59,7 +59,7 @@ GLubyte lower_pattern[] = {
 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
@@ -97,7 +97,7 @@ struct image_kernel::implementation : boost::noncopyable
                // 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
@@ -111,30 +111,28 @@ struct image_kernel::implementation : boost::noncopyable
                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
@@ -230,7 +228,7 @@ struct image_kernel::implementation : boost::noncopyable
                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
@@ -240,7 +238,11 @@ struct image_kernel::implementation : boost::noncopyable
 };\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
index 07078e870b9c90ea4cb1340590b3a1e7243e98c0..9debd7fd36551ca304b718d44c99018ee009caf4 100644 (file)
@@ -19,6 +19,8 @@
 */\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
@@ -38,19 +40,22 @@ struct render_item
        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
@@ -59,7 +64,11 @@ class image_kernel : boost::noncopyable
 {\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
index 61920dbd586c7eff13538fc1c3ab5224c79ec38a..edf38213358bf778182108df2786b70fb24c1000 100644 (file)
@@ -58,6 +58,7 @@ struct image_mixer::implementation : boost::noncopyable
 \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
@@ -91,7 +92,7 @@ public:
                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
@@ -105,13 +106,15 @@ public:
                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
@@ -151,34 +154,34 @@ public:
                        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
@@ -197,11 +200,7 @@ public:
                                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
@@ -219,49 +218,52 @@ public:
        }\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
@@ -282,7 +284,7 @@ void image_mixer::visit(core::write_frame& frame){impl_->visit(frame);}
 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
index 2900276a836b81607a6d26a8da04a601db177494..15beb66c0ac96ea07a5d202ae8848195abfe72de 100644 (file)
@@ -19,6 +19,8 @@
 */\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
@@ -43,7 +45,7 @@ public:
        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
index 3a793932030140a0dcf105fa18d676b297d18d33..e06bd2df1c9ff0c5fb0e7c1f86d8a02b6f11b17e 100644 (file)
@@ -100,6 +100,8 @@ struct mixer::implementation : boost::noncopyable
        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
@@ -184,6 +186,11 @@ public:
                });\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
@@ -197,7 +204,8 @@ private:
                \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
@@ -252,5 +260,5 @@ void mixer::set_image_transform(int index, const core::image_transform& transfor
 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
index f0f1c47965ce1f6af5fedeb3f4d3c7a5ade6dd89..198935c87813c2694870004dda3b022c715d300e 100644 (file)
@@ -19,6 +19,8 @@
 */\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
@@ -42,6 +44,7 @@ struct pixel_format;
 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
@@ -58,6 +61,8 @@ public:
        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
index 7ca46e75c047576d01cc170dbddbe59a8a4c917b..369da5891c1f29e27574f5fe0e46bd3e0f382f42 100644 (file)
@@ -24,8 +24,6 @@
 \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
@@ -34,7 +32,6 @@ image_transform::image_transform()
        , 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
@@ -137,20 +134,9 @@ std::array<double, 2> image_transform::get_clip_scale() const
        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
@@ -191,7 +177,6 @@ image_transform tween(double time, const image_transform& source, const image_tr
        };\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
@@ -218,70 +203,6 @@ image_transform tween(double time, const image_transform& source, const image_tr
        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
index 97b34d37d6c8caa2a0da74fd1f147fc4c33f8a8f..f116d383e27cdf2d4ef7810c3d29850726fe254e 100644 (file)
@@ -33,44 +33,6 @@ class image_transform
 {\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
@@ -122,10 +84,7 @@ public:
 \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
@@ -140,11 +99,8 @@ private:
        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
index 51a7aadeeaae09feaaa67bb6bcbe18ee194a0d5b..cb136da7822eadf7aefc154b3e0bf6de03e88743 100644 (file)
@@ -316,16 +316,9 @@ bool MixerCommand::DoExecute()
                        }\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