]> git.sesse.net Git - casparcg/commitdiff
[mixer] Rewrote the chroma key code to support variable hue, instead of fixed green...
authorHelge Norberg <helge.norberg@svt.se>
Thu, 12 Jan 2017 16:54:13 +0000 (17:54 +0100)
committerHelge Norberg <helge.norberg@svt.se>
Thu, 12 Jan 2017 16:54:13 +0000 (17:54 +0100)
CHANGELOG
accelerator/ogl/image/blending_glsl.h
accelerator/ogl/image/image_kernel.cpp
accelerator/ogl/image/image_shader.cpp
core/frame/frame_transform.cpp
core/frame/frame_transform.h
core/producer/scene/scene_producer.cpp
core/producer/scene/scene_producer.h
core/producer/scene/xml_scene_producer.cpp
protocol/amcp/AMCPCommandsImpl.cpp

index 24901f20673a9d1070c1707d507b6c21ec8fce37..d641b8c6b5e2ea6d17d7a5fdf9622cde378d7ebe 100644 (file)
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -40,12 +40,19 @@ Mixer
 \r
   o Fixed bug in the contrast/saturation/brightness code where the wrong luma\r
     coefficients was used.\r
+  o Rewrote the chroma key code to support variable hue, instead of fixed green\r
+    or blue. Threshold setting was removed in favour of separate hue width,\r
+    minimum saturation and minimum brightness constraints.\r
 \r
 AMCP\r
 ----\r
 \r
   o INFO PATHS now adds all the path elements even if they are using the default\r
     values.\r
+  o MIXER CHROMA syntax deprecated (still supported) in favour of the more\r
+    advanced syntax required by the rewritten chroma key code.\r
+\r
+\r
 \r
 CasparCG 2.1.0 Beta 1 (w.r.t 2.0.7 Stable)\r
 ==========================================\r
index 2dc65f4ccf69b1ecff6704b48019d7084245dbd5..7f3429d7f68dfa4435d56b4cb22519b1b9ed3bfd 100644 (file)
@@ -281,40 +281,70 @@ static std::string get_chroma_glsl()
                //      by F. van den Bergh & V. Lalioti
                // but as a pixel shader algorithm.
                //
-
-               float       chroma_blend_w = chroma_blend.y - chroma_blend.x;
-               const vec4  grey_xfer  = vec4(0.3, 0.59, 0.11, 0.0);
-
-               float fma(float a, float b, float c) { return a*b + c; }
+               vec4  grey_xfer  = is_hd
+                               ? vec4(0.2126, 0.7152, 0.0722, 0)
+                               : vec4(0.299,  0.587,  0.114, 0);
 
                // This allows us to implement the paper's alphaMap curve in software
                // rather than a largeish array
                float alpha_map(float d)
                {
-                   return 1.0-smoothstep(chroma_blend.x, chroma_blend.y, d);
+                   return 1.0 - smoothstep(1.0, chroma_softness, d);
                }
 
                vec4 supress_spill(vec4 c, float d)
                {
-                   float ds = smoothstep(chroma_spill, 1.0, d/chroma_blend.y);
+                       float ds        = smoothstep(chroma_spill, clamp(chroma_spill + 0.2, 0, 1), d / chroma_softness);
                    float gl = dot(grey_xfer, c);
-                   return mix(c, vec4(vec3(gl*gl), gl), ds);
+                   return mix(c, vec4(vec3(pow(gl, chroma_spill_darken)), gl), ds);
+               }
+
+               // http://stackoverflow.com/questions/15095909/from-rgb-to-hsv-in-opengl-glsl
+               vec3 rgb2hsv(vec3 c)
+               {
+                       vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);
+                       vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g));
+                       vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r));
+
+                       float d = q.x - min(q.w, q.y);
+                       float e = 1.0e-10;
+                       return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);
+               }
+
+               float AngleDiff(float angle1, float angle2)
+               {
+                       return 0.5 - abs(abs(angle1 - angle2) - 0.5);
+               }
+
+               float Distance(float actual, float target)
+               {
+                       return min(0.0, target - actual);
                }
 
-               // Key on green
-               vec4 ChromaOnGreen(vec4 c)
+               float ColorDistance(vec3 hsv)
                {
-                   float d = fma(2.0, c.g, -c.r - c.b)/2.0;
-                   c *= alpha_map(d);
-                   return supress_spill(c, d);
+                       float hueDiff                                   = AngleDiff(hsv.x, chroma_target_hue) * 2;
+                       float saturationDiff                    = Distance(hsv.y, chroma_min_saturation);
+                       float brightnessDiff                    = Distance(hsv.z, chroma_min_brightness);
+
+                       float saturationBrightnessScore = max(brightnessDiff, saturationDiff);
+                       float hueScore                                  = hueDiff - chroma_hue_width;
+
+                       return -hueScore * saturationBrightnessScore;
                }
 
-               //Key on blue
-               vec4 ChromaOnBlue(vec4 c)
+               // Key on any color
+               vec4 ChromaOnCustomColor(vec4 c)
                {
-                   float d = fma(2.0, c.b, -c.r - c.g)/2.0;
-                   c *= alpha_map(d);
-                   return supress_spill(c, d);
+                       vec3 hsv                = rgb2hsv(c.rgb);
+                       float distance  = ColorDistance(hsv);
+                       float d                 = distance * -2.0 + 1.0;
+                   vec4 suppressed     = supress_spill(c.rgba, d).rgba;
+                       float alpha             = alpha_map(d);
+
+                       suppressed *= alpha;
+
+                       return chroma_show_mask ? vec4(suppressed.a, suppressed.a, suppressed.a, 1) : suppressed;
                }
        )shader";
 
index b10accd133e0ecfe1778e6cce55bfcbd77567528..37bb44ff7552bab1d0f240de2cba5f1c49a54f46 100644 (file)
@@ -123,15 +123,15 @@ GLubyte upper_pattern[] = {
        0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
        0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
        0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00};
-               
+
 GLubyte lower_pattern[] = {
-       0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 
+       0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
        0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
        0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
        0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff};
 
 struct image_kernel::impl
-{      
+{
        spl::shared_ptr<device> ogl_;
        spl::shared_ptr<shader> shader_;
        bool                                    blend_modes_;
@@ -148,8 +148,8 @@ struct image_kernel::impl
 
        void draw(draw_params params)
        {
-               static const double epsilon = 0.001;            
-               
+               static const double epsilon = 0.001;
+
                CASPAR_ASSERT(params.pix_desc.planes.size() == params.textures.size());
 
                if(params.textures.empty() || !params.background)
@@ -240,12 +240,12 @@ struct image_kernel::impl
 
                if(params.local_key)
                        params.local_key->bind(static_cast<int>(texture_id::local_key));
-               
+
                if(params.layer_key)
                        params.layer_key->bind(static_cast<int>(texture_id::layer_key));
-                       
+
                // Setup shader
-                                                               
+
                shader_->use();
 
                shader_->set("post_processing", false);
@@ -254,8 +254,8 @@ struct image_kernel::impl
                shader_->set("plane[2]",                texture_id::plane2);
                shader_->set("plane[3]",                texture_id::plane3);
                for (int n = 0; n < params.textures.size(); ++n)
-                       shader_->set("plane_size[" + boost::lexical_cast<std::string>(n) + "]", 
-                                                static_cast<float>(params.textures[n]->width()), 
+                       shader_->set("plane_size[" + boost::lexical_cast<std::string>(n) + "]",
+                                                static_cast<float>(params.textures[n]->width()),
                                                 static_cast<float>(params.textures[n]->height()));
 
                shader_->set("local_key",               texture_id::local_key);
@@ -263,22 +263,28 @@ struct image_kernel::impl
                shader_->set("is_hd",                   params.pix_desc.planes.at(0).height > 700 ? 1 : 0);
                shader_->set("has_local_key",   static_cast<bool>(params.local_key));
                shader_->set("has_layer_key",   static_cast<bool>(params.layer_key));
-               shader_->set("pixel_format",    params.pix_desc.format);
-               shader_->set("opacity",                 params.transform.is_key ? 1.0 : params.transform.opacity);      
+               shader_->set("pixel_format",            params.pix_desc.format);
+               shader_->set("opacity",                 params.transform.is_key ? 1.0 : params.transform.opacity);
 
-               if (params.transform.chroma.key != core::chroma::type::none)
+               if (params.transform.chroma.enable)
                {
-                       shader_->set("chroma", true);
-                       shader_->set("chroma_mode", static_cast<int>(params.transform.chroma.key));
-                       shader_->set("chroma_blend", params.transform.chroma.threshold, params.transform.chroma.softness);
-                       shader_->set("chroma_spill", params.transform.chroma.spill);
+                       shader_->set("chroma",                                          true);
+
+                       shader_->set("chroma_show_mask",                        params.transform.chroma.show_mask);
+                       shader_->set("chroma_target_hue",               params.transform.chroma.target_hue / 360.0);
+                       shader_->set("chroma_hue_width",                        params.transform.chroma.hue_width);
+                       shader_->set("chroma_min_saturation",   params.transform.chroma.min_saturation);
+                       shader_->set("chroma_min_brightness",   params.transform.chroma.min_brightness);
+                       shader_->set("chroma_softness",                 1.0 + params.transform.chroma.softness);
+                       shader_->set("chroma_spill",                            params.transform.chroma.spill);
+                       shader_->set("chroma_spill_darken",             params.transform.chroma.spill_darken);
                }
                else
                        shader_->set("chroma", false);
 
 
                // Setup blend_func
-               
+
                if(params.transform.is_key)
                        params.blend_mode = core::blend_mode::normal;
 
@@ -297,48 +303,48 @@ struct image_kernel::impl
                        switch(params.keyer)
                        {
                        case keyer::additive:
-                               GL(glBlendFunc(GL_ONE, GL_ONE));        
+                               GL(glBlendFunc(GL_ONE, GL_ONE));
                                break;
                        case keyer::linear:
-                       default:                        
+                       default:
                                GL(glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA));
-                       }               
+                       }
                }
 
                // Setup image-adjustements
-               
+
                if (params.transform.levels.min_input  > epsilon                ||
                        params.transform.levels.max_input  < 1.0-epsilon        ||
                        params.transform.levels.min_output > epsilon            ||
                        params.transform.levels.max_output < 1.0-epsilon        ||
                        std::abs(params.transform.levels.gamma - 1.0) > epsilon)
                {
-                       shader_->set("levels", true);   
-                       shader_->set("min_input",       params.transform.levels.min_input);     
+                       shader_->set("levels", true);
+                       shader_->set("min_input",       params.transform.levels.min_input);
                        shader_->set("max_input",       params.transform.levels.max_input);
                        shader_->set("min_output",      params.transform.levels.min_output);
                        shader_->set("max_output",      params.transform.levels.max_output);
                        shader_->set("gamma",           params.transform.levels.gamma);
                }
                else
-                       shader_->set("levels", false);  
+                       shader_->set("levels", false);
 
                if (std::abs(params.transform.brightness - 1.0) > epsilon ||
                        std::abs(params.transform.saturation - 1.0) > epsilon ||
                        std::abs(params.transform.contrast - 1.0)   > epsilon)
                {
-                       shader_->set("csb",     true);  
-                       
-                       shader_->set("brt", params.transform.brightness);       
+                       shader_->set("csb",     true);
+
+                       shader_->set("brt", params.transform.brightness);
                        shader_->set("sat", params.transform.saturation);
                        shader_->set("con", params.transform.contrast);
                }
                else
-                       shader_->set("csb",     false); 
-               
+                       shader_->set("csb",     false);
+
                // Setup interlacing
-               
-               if (params.transform.field_mode != core::field_mode::progressive)       
+
+               if (params.transform.field_mode != core::field_mode::progressive)
                {
                        GL(glEnable(GL_POLYGON_STIPPLE));
 
@@ -349,10 +355,10 @@ struct image_kernel::impl
                }
 
                // Setup drawing area
-               
+
                GL(glViewport(0, 0, params.background->width(), params.background->height()));
                glDisable(GL_DEPTH_TEST);
-                                                                               
+
                auto m_p = params.transform.clip_translation;
                auto m_s = params.transform.clip_scale;
 
@@ -363,18 +369,18 @@ struct image_kernel::impl
                {
                        double w = static_cast<double>(params.background->width());
                        double h = static_cast<double>(params.background->height());
-               
+
                        GL(glEnable(GL_SCISSOR_TEST));
                        glScissor(static_cast<int>(m_p[0] * w), static_cast<int>(m_p[1] * h), std::max(0, static_cast<int>(m_s[0] * w)), std::max(0, static_cast<int>(m_s[1] * h)));
                }
 
                // Synchronize and set render target
-                                                               
+
                if (blend_modes_)
                {
                        // http://www.opengl.org/registry/specs/NV/texture_barrier.txt
                        // This allows us to use framebuffer (background) both as source and target while blending.
-                       glTextureBarrierNV(); 
+                       glTextureBarrierNV();
                }
 
                params.background->attach();
@@ -451,7 +457,7 @@ struct image_kernel::impl
                default:
                        break;
                }
-               
+
                // Cleanup
                GL(glDisable(GL_SCISSOR_TEST));
                GL(glDisable(GL_POLYGON_STIPPLE));
index dfc47a0b2ef6675e79eb1e68129210ebc5032c9c..c4a5ecdc3519fe3d76fb22b699c2faf761ca9b60 100644 (file)
@@ -103,7 +103,7 @@ std::string get_blend_color_func()
                                }
                )shader";
 }
-               
+
 std::string get_simple_blend_color_func()
 {
        return
@@ -125,20 +125,13 @@ std::string get_chroma_func()
        return
 
                get_chroma_glsl()
-               
+
                +
-               
+
                R"shader(
                                vec4 chroma_key(vec4 c)
                                {
-                                       switch (chroma_mode)
-                                       {
-                                       case 0: return c;
-                                       case 1: return ChromaOnGreen(c.bgra).bgra;
-                                       case 2: return ChromaOnBlue(c.bgra).bgra;
-                                       }
-
-                                       return c;
+                                       return ChromaOnCustomColor(c.bgra).bgra;
                                }
                )shader";
 }
@@ -211,9 +204,14 @@ std::string get_fragment(bool blend_modes, bool post_processing)
                        uniform bool            straighten_alpha;
 
                        uniform bool            chroma;
-                       uniform int                     chroma_mode;
-                       uniform vec2            chroma_blend;
+                       uniform bool            chroma_show_mask;
+                       uniform float           chroma_target_hue;
+                       uniform float           chroma_hue_width;
+                       uniform float           chroma_min_saturation;
+                       uniform float           chroma_min_brightness;
+                       uniform float           chroma_softness;
                        uniform float           chroma_spill;
+                       uniform float           chroma_spill_darken;
        )shader"
 
        +
@@ -319,7 +317,7 @@ std::string get_fragment(bool blend_modes, bool post_processing)
                                        color.rgb /= color.a + 0.0000001;
 
                                return color;
-                       }       
+                       }
 
                        void main()
                        {
@@ -383,9 +381,9 @@ std::shared_ptr<shader> get_image_shader(
                                delete p;
                        });
        };
-               
+
        try
-       {                               
+       {
                g_blend_modes  = glTextureBarrierNV ? blend_modes_wanted : false;
                g_post_processing = straight_alpha_wanted;
                existing_shader.reset(new shader(get_vertex(), get_fragment(g_blend_modes, g_post_processing)), deleter);
@@ -394,7 +392,7 @@ std::shared_ptr<shader> get_image_shader(
        {
                CASPAR_LOG_CURRENT_EXCEPTION();
                CASPAR_LOG(warning) << "Failed to compile shader. Trying to compile without blend-modes.";
-                               
+
                g_blend_modes = false;
                existing_shader.reset(new shader(get_vertex(), get_fragment(g_blend_modes, g_post_processing)), deleter);
        }
index 4ba1899202eea6d4daca30d18753c03145785d6f..393214b69aa1326a3c6f7f6d6f27fc30ee770fca 100644 (file)
@@ -53,7 +53,7 @@ void transform_corners(corners& self, const corners& other)
 
 image_transform& image_transform::operator*=(const image_transform &other)
 {
-       opacity                                 *= other.opacity;       
+       opacity                                 *= other.opacity;
        brightness                              *= other.brightness;
        contrast                                *= other.contrast;
        saturation                              *= other.saturation;
@@ -87,14 +87,19 @@ image_transform& image_transform::operator*=(const image_transform &other)
        transform_corners(perspective, other.perspective);
 
        levels.min_input                 = std::max(levels.min_input,  other.levels.min_input);
-       levels.max_input                 = std::min(levels.max_input,  other.levels.max_input); 
+       levels.max_input                 = std::min(levels.max_input,  other.levels.max_input);
        levels.min_output                = std::max(levels.min_output, other.levels.min_output);
        levels.max_output                = std::min(levels.max_output, other.levels.max_output);
        levels.gamma                    *= other.levels.gamma;
-       chroma.key                               = std::max(chroma.key, other.chroma.key);
-       chroma.threshold                += other.chroma.threshold;
-       chroma.softness                 += other.chroma.softness;
-       chroma.spill                    += other.chroma.spill;
+       chroma.enable                   |= other.chroma.enable;
+       chroma.show_mask                |= other.chroma.show_mask;
+       chroma.target_hue                = std::max(other.chroma.target_hue, chroma.target_hue);
+       chroma.min_saturation    = std::max(other.chroma.min_saturation, chroma.min_saturation);
+       chroma.min_brightness    = std::max(other.chroma.min_brightness, chroma.min_brightness);
+       chroma.hue_width                 = std::max(other.chroma.hue_width, chroma.hue_width);
+       chroma.softness                  = std::max(other.chroma.softness, chroma.softness);
+       chroma.spill                     = std::min(other.chroma.spill, chroma.spill);
+       chroma.spill_darken              = std::max(other.chroma.spill_darken, chroma.spill_darken);
        field_mode                               = field_mode & other.field_mode;
        is_key                                  |= other.is_key;
        is_mix                                  |= other.is_mix;
@@ -137,39 +142,44 @@ void do_tween_corners(const corners& source, const corners& dest, corners& out,
 
 image_transform image_transform::tween(double time, const image_transform& source, const image_transform& dest, double duration, const tweener& tween)
 {
-       image_transform result; 
-
-       result.brightness                       = do_tween(time, source.brightness,                             dest.brightness,                        duration, tween);
-       result.contrast                         = do_tween(time, source.contrast,                               dest.contrast,                          duration, tween);
-       result.saturation                       = do_tween(time, source.saturation,                             dest.saturation,                        duration, tween);
-       result.opacity                          = do_tween(time, source.opacity,                                dest.opacity,                           duration, tween);
-       result.anchor[0]                        = do_tween(time, source.anchor[0],                              dest.anchor[0],                         duration, tween);
-       result.anchor[1]                        = do_tween(time, source.anchor[1],                              dest.anchor[1],                         duration, tween);
-       result.fill_translation[0]      = do_tween(time, source.fill_translation[0],    dest.fill_translation[0],       duration, tween);
-       result.fill_translation[1]      = do_tween(time, source.fill_translation[1],    dest.fill_translation[1],       duration, tween);
-       result.fill_scale[0]            = do_tween(time, source.fill_scale[0],                  dest.fill_scale[0],                     duration, tween);
-       result.fill_scale[1]            = do_tween(time, source.fill_scale[1],                  dest.fill_scale[1],                     duration, tween);
-       result.clip_translation[0]      = do_tween(time, source.clip_translation[0],    dest.clip_translation[0],       duration, tween);
-       result.clip_translation[1]      = do_tween(time, source.clip_translation[1],    dest.clip_translation[1],       duration, tween);
-       result.clip_scale[0]            = do_tween(time, source.clip_scale[0],                  dest.clip_scale[0],                     duration, tween);
-       result.clip_scale[1]            = do_tween(time, source.clip_scale[1],                  dest.clip_scale[1],                     duration, tween);
-       result.angle                            = do_tween(time, source.angle,                                  dest.angle,                                     duration, tween);
-       result.levels.max_input         = do_tween(time, source.levels.max_input,               dest.levels.max_input,          duration, tween);
-       result.levels.min_input         = do_tween(time, source.levels.min_input,               dest.levels.min_input,          duration, tween);
-       result.levels.max_output        = do_tween(time, source.levels.max_output,              dest.levels.max_output,         duration, tween);
-       result.levels.min_output        = do_tween(time, source.levels.min_output,              dest.levels.min_output,         duration, tween);
-       result.levels.gamma                     = do_tween(time, source.levels.gamma,                   dest.levels.gamma,                      duration, tween);
-       result.chroma.threshold         = do_tween(time, source.chroma.threshold,               dest.chroma.threshold,          duration, tween);
-       result.chroma.softness          = do_tween(time, source.chroma.softness,                dest.chroma.softness,           duration, tween);
-       result.chroma.spill                     = do_tween(time, source.chroma.spill,                   dest.chroma.spill,                      duration, tween);
-       result.chroma.key                       = dest.chroma.key;
-       result.field_mode                       = source.field_mode & dest.field_mode;
-       result.is_key                           = source.is_key | dest.is_key;
-       result.is_mix                           = source.is_mix | dest.is_mix;
-       result.is_still                         = source.is_still | dest.is_still;
-       result.use_mipmap                       = source.use_mipmap | dest.use_mipmap;
-       result.blend_mode                       = std::max(source.blend_mode, dest.blend_mode);
-       result.layer_depth                      = dest.layer_depth;
+       image_transform result;
+
+       result.brightness                               = do_tween(time, source.brightness,                             dest.brightness,                        duration, tween);
+       result.contrast                                 = do_tween(time, source.contrast,                               dest.contrast,                          duration, tween);
+       result.saturation                               = do_tween(time, source.saturation,                             dest.saturation,                        duration, tween);
+       result.opacity                                  = do_tween(time, source.opacity,                                dest.opacity,                           duration, tween);
+       result.anchor[0]                                = do_tween(time, source.anchor[0],                              dest.anchor[0],                         duration, tween);
+       result.anchor[1]                                = do_tween(time, source.anchor[1],                              dest.anchor[1],                         duration, tween);
+       result.fill_translation[0]              = do_tween(time, source.fill_translation[0],    dest.fill_translation[0],       duration, tween);
+       result.fill_translation[1]              = do_tween(time, source.fill_translation[1],    dest.fill_translation[1],       duration, tween);
+       result.fill_scale[0]                    = do_tween(time, source.fill_scale[0],                  dest.fill_scale[0],                     duration, tween);
+       result.fill_scale[1]                    = do_tween(time, source.fill_scale[1],                  dest.fill_scale[1],                     duration, tween);
+       result.clip_translation[0]              = do_tween(time, source.clip_translation[0],    dest.clip_translation[0],       duration, tween);
+       result.clip_translation[1]              = do_tween(time, source.clip_translation[1],    dest.clip_translation[1],       duration, tween);
+       result.clip_scale[0]                    = do_tween(time, source.clip_scale[0],                  dest.clip_scale[0],                     duration, tween);
+       result.clip_scale[1]                    = do_tween(time, source.clip_scale[1],                  dest.clip_scale[1],                     duration, tween);
+       result.angle                                    = do_tween(time, source.angle,                                  dest.angle,                                     duration, tween);
+       result.levels.max_input                 = do_tween(time, source.levels.max_input,               dest.levels.max_input,          duration, tween);
+       result.levels.min_input                 = do_tween(time, source.levels.min_input,               dest.levels.min_input,          duration, tween);
+       result.levels.max_output                = do_tween(time, source.levels.max_output,              dest.levels.max_output,         duration, tween);
+       result.levels.min_output                = do_tween(time, source.levels.min_output,              dest.levels.min_output,         duration, tween);
+       result.levels.gamma                             = do_tween(time, source.levels.gamma,                   dest.levels.gamma,                      duration, tween);
+       result.chroma.target_hue                = do_tween(time, source.chroma.target_hue,              dest.chroma.target_hue,         duration, tween);
+       result.chroma.hue_width                 = do_tween(time, source.chroma.hue_width,               dest.chroma.hue_width,          duration, tween);
+       result.chroma.min_saturation    = do_tween(time, source.chroma.min_saturation,  dest.chroma.min_saturation,     duration, tween);
+       result.chroma.min_brightness    = do_tween(time, source.chroma.min_brightness,  dest.chroma.min_brightness,     duration, tween);
+       result.chroma.softness                  = do_tween(time, source.chroma.softness,                dest.chroma.softness,           duration, tween);
+       result.chroma.spill                             = do_tween(time, source.chroma.spill,                   dest.chroma.spill,                      duration, tween);
+       result.chroma.spill_darken              = do_tween(time, source.chroma.spill_darken,    dest.chroma.spill_darken,       duration, tween);
+       result.chroma.enable                    = dest.chroma.enable;
+       result.chroma.show_mask                 = dest.chroma.show_mask;
+       result.field_mode                               = source.field_mode & dest.field_mode;
+       result.is_key                                   = source.is_key | dest.is_key;
+       result.is_mix                                   = source.is_mix | dest.is_mix;
+       result.is_still                                 = source.is_still | dest.is_still;
+       result.use_mipmap                               = source.use_mipmap | dest.use_mipmap;
+       result.blend_mode                               = std::max(source.blend_mode, dest.blend_mode);
+       result.layer_depth                              = dest.layer_depth;
 
        do_tween_rectangle(source.crop, dest.crop, result.crop, time, duration, tween);
        do_tween_corners(source.perspective, dest.perspective, result.perspective, time, duration, tween);
@@ -200,7 +210,7 @@ bool operator==(const rectangle& lhs, const rectangle& rhs)
 
 bool operator==(const image_transform& lhs, const image_transform& rhs)
 {
-       return 
+       return
                eq(lhs.opacity, rhs.opacity) &&
                eq(lhs.contrast, rhs.contrast) &&
                eq(lhs.brightness, rhs.brightness) &&
@@ -218,6 +228,15 @@ bool operator==(const image_transform& lhs, const image_transform& rhs)
                lhs.use_mipmap == rhs.use_mipmap &&
                lhs.blend_mode == rhs.blend_mode &&
                lhs.layer_depth == rhs.layer_depth &&
+               lhs.chroma.enable == rhs.chroma.enable &&
+               lhs.chroma.show_mask == rhs.chroma.show_mask &&
+               eq(lhs.chroma.target_hue, rhs.chroma.target_hue) &&
+               eq(lhs.chroma.hue_width, rhs.chroma.hue_width) &&
+               eq(lhs.chroma.min_saturation, rhs.chroma.min_saturation) &&
+               eq(lhs.chroma.min_brightness, rhs.chroma.min_brightness) &&
+               eq(lhs.chroma.softness, rhs.chroma.softness) &&
+               eq(lhs.chroma.spill, rhs.chroma.spill) &&
+               eq(lhs.chroma.spill_darken, rhs.chroma.spill_darken) &&
                lhs.crop == rhs.crop &&
                lhs.perspective == rhs.perspective;
 }
@@ -231,7 +250,7 @@ bool operator!=(const image_transform& lhs, const image_transform& rhs)
 
 audio_transform& audio_transform::operator*=(const audio_transform &other)
 {
-       volume   *= other.volume;       
+       volume   *= other.volume;
        is_still |= other.is_still;
        return *this;
 }
@@ -242,11 +261,11 @@ audio_transform audio_transform::operator*(const audio_transform &other) const
 }
 
 audio_transform audio_transform::tween(double time, const audio_transform& source, const audio_transform& dest, double duration, const tweener& tween)
-{      
+{
        audio_transform result;
        result.is_still                 = source.is_still | dest.is_still;
        result.volume                   = do_tween(time, source.volume,                         dest.volume,                    duration, tween);
-       
+
        return result;
 }
 
@@ -287,7 +306,7 @@ frame_transform frame_transform::tween(double time, const frame_transform& sourc
 
 bool operator==(const frame_transform& lhs, const frame_transform& rhs)
 {
-       return  lhs.image_transform == rhs.image_transform && 
+       return  lhs.image_transform == rhs.image_transform &&
                        lhs.audio_transform == rhs.audio_transform;
 }
 
@@ -297,31 +316,16 @@ bool operator!=(const frame_transform& lhs, const frame_transform& rhs)
 }
 
 
-core::chroma::type get_chroma_mode(const std::wstring& str)
+boost::optional<core::chroma::legacy_type> get_chroma_mode(const std::wstring& str)
 {
        if (boost::iequals(str, L"none"))
-               return core::chroma::type::none;
+               return core::chroma::legacy_type::none;
        else if (boost::iequals(str, L"green"))
-               return core::chroma::type::green;
+               return core::chroma::legacy_type::green;
        else if (boost::iequals(str, L"blue"))
-               return core::chroma::type::blue;
+               return core::chroma::legacy_type::blue;
        else
-               CASPAR_THROW_EXCEPTION(user_error() << msg_info("chroma mode has to be one of none, green or blue"));
-}
-
-std::wstring get_chroma_mode(core::chroma::type type)
-{
-       switch (type)
-       {
-       case core::chroma::type::none:
-               return L"none";
-       case core::chroma::type::green:
-               return L"green";
-       case core::chroma::type::blue:
-               return L"blue";
-       default:
-               CASPAR_THROW_EXCEPTION(programming_error() << msg_info("Unhandled enum constant"));
-       };
+               return boost::none;
 }
 
 namespace detail {
index e21e17fb2bd60d276b32ef534381b62d61455ab8..724d35cd1f9b92396f9d01be98227392b718594a 100644 (file)
 #include <core/mixer/image/blend_modes.h>
 
 #include <boost/array.hpp>
+#include <boost/optional.hpp>
 #include <boost/property_tree/ptree.hpp>
 
 namespace caspar { namespace core {
 
 struct chroma
 {
-       enum class type
+       enum class legacy_type
        {
                none,
                green,
                blue
        };
 
-       type    key                     = type::none;
-       double  threshold       = 0.0;
-       double  softness        = 0.0;
-       double  spill           = 0.0;
+       bool            enable                          = false;
+       bool            show_mask                       = false;
+       double          target_hue                      = 0.0;
+       double          hue_width                       = 0.0;
+       double          min_saturation          = 0.0;
+       double          min_brightness          = 0.0;
+       double          softness                        = 0.0;
+       double          spill                           = 1.0;
+       double          spill_darken            = 0.0;
 };
 
 struct levels final
@@ -98,7 +104,7 @@ struct image_transform final
        bool                                    use_mipmap                      = false;
        core::blend_mode                blend_mode                      = core::blend_mode::normal;
        int                                             layer_depth                     = 0;
-       
+
        image_transform& operator*=(const image_transform &other);
        image_transform operator*(const image_transform &other) const;
 
@@ -112,7 +118,7 @@ struct audio_transform final
 {
        double  volume          = 1.0;
        bool    is_still        = false;
-       
+
        audio_transform& operator*=(const audio_transform &other);
        audio_transform operator*(const audio_transform &other) const;
 
@@ -122,17 +128,17 @@ struct audio_transform final
 bool operator==(const audio_transform& lhs, const audio_transform& rhs);
 bool operator!=(const audio_transform& lhs, const audio_transform& rhs);
 
-//__declspec(align(16)) 
+//__declspec(align(16))
 struct frame_transform final
 {
 public:
        frame_transform();
-       
+
        core::image_transform image_transform;
        core::audio_transform audio_transform;
 
        //char padding[(sizeof(core::image_transform) + sizeof(core::audio_transform)) % 16];
-       
+
        frame_transform& operator*=(const frame_transform &other);
        frame_transform operator*(const frame_transform &other) const;
 
@@ -149,7 +155,7 @@ class tweened_transform
        int duration_;
        int time_;
        tweener tweener_;
-public:        
+public:
        tweened_transform()
                : duration_(0)
                , time_(0)
@@ -170,21 +176,20 @@ public:
        {
                return dest_;
        }
-       
+
        frame_transform fetch()
        {
                return time_ == duration_ ? dest_ : frame_transform::tween(static_cast<double>(time_), source_, dest_, static_cast<double>(duration_), tweener_);
        }
 
        frame_transform fetch_and_tick(int num)
-       {                                               
+       {
                time_ = std::min(time_+num, duration_);
                return fetch();
        }
 };
 
-chroma::type get_chroma_mode(const std::wstring& str);
-std::wstring get_chroma_mode(chroma::type type);
+boost::optional<chroma::legacy_type> get_chroma_mode(const std::wstring& str);
 
 namespace detail {
 
index fd99bb96b299f327fc221c3480f5c5dd37551f39..f5f2a625edf0b0257f329578f703799009d78183 100644 (file)
@@ -149,7 +149,7 @@ struct scene_producer::impl
                auto speed_variable = std::make_shared<core::variable_impl<double>>(L"1.0", true, 1.0);
                store_variable(L"scene_speed", speed_variable);
                speed_ = speed_variable->value();
-               
+
                auto frame_variable = std::make_shared<core::variable_impl<double>>(L"-1", true, -1);
                store_variable(L"frame", frame_variable);
                frame_number_ = frame_variable->value();
@@ -256,14 +256,18 @@ struct scene_producer::impl
 
                angle           = layer.rotation.get() * PI / 180.0;
 
-               transform.image_transform.opacity                       = layer.adjustments.opacity.get();
-               transform.image_transform.is_key                        = layer.is_key.get();
-               transform.image_transform.use_mipmap            = layer.use_mipmap.get();
-               transform.image_transform.blend_mode            = layer.blend_mode.get();
-               transform.image_transform.chroma.key            = layer.chroma_key.key.get();
-               transform.image_transform.chroma.threshold      = layer.chroma_key.threshold.get();
-               transform.image_transform.chroma.softness       = layer.chroma_key.softness.get();
-               transform.image_transform.chroma.spill          = layer.chroma_key.spill.get();
+               transform.image_transform.opacity                               = layer.adjustments.opacity.get();
+               transform.image_transform.is_key                                = layer.is_key.get();
+               transform.image_transform.use_mipmap                    = layer.use_mipmap.get();
+               transform.image_transform.blend_mode                    = layer.blend_mode.get();
+               transform.image_transform.chroma.enable                 = layer.chroma_key.enable.get();
+               transform.image_transform.chroma.target_hue             = layer.chroma_key.target_hue.get();
+               transform.image_transform.chroma.hue_width              = layer.chroma_key.hue_width.get();
+               transform.image_transform.chroma.min_saturation = layer.chroma_key.min_saturation.get();
+               transform.image_transform.chroma.min_brightness = layer.chroma_key.min_brightness.get();
+               transform.image_transform.chroma.softness               = layer.chroma_key.softness.get();
+               transform.image_transform.chroma.spill                  = layer.chroma_key.spill.get();
+               transform.image_transform.chroma.spill_darken   = layer.chroma_key.spill_darken.get();
 
                // Mark as sublayer, so it will be composited separately by the mixer.
                transform.image_transform.layer_depth = 1;
@@ -316,9 +320,9 @@ struct scene_producer::impl
                {
                        prec_timer timer;
                        timer.tick_millis(0);
-                       
+
                        auto field1 = render_progressive_frame();
-                       
+
                        timer.tick(0.5 / format_desc_.fps);
 
                        auto field2 = render_progressive_frame();
@@ -432,7 +436,7 @@ struct scene_producer::impl
                return boost::optional<interaction_target>();
        }
 
-       std::future<std::wstring> call(const std::vector<std::wstring>& params) 
+       std::future<std::wstring> call(const std::vector<std::wstring>& params)
        {
                if (!params.empty() && boost::ends_with(params.at(0), L"()"))
                        return make_ready_future(handle_call(params));
@@ -531,7 +535,7 @@ struct scene_producer::impl
        {
                return producer_name_;
        }
-       
+
        boost::property_tree::wptree info() const
        {
                boost::property_tree::wptree info;
@@ -636,7 +640,7 @@ boost::property_tree::wptree scene_producer::info() const
        return impl_->info();
 }
 
-std::future<std::wstring> scene_producer::call(const std::vector<std::wstring>& params) 
+std::future<std::wstring> scene_producer::call(const std::vector<std::wstring>& params)
 {
        return impl_->call(params);
 }
index ccfad7fe9c8c2a742272543428754123fa276a64..aca30f6b60fb720b191d178d585e36d19e95815c 100644 (file)
@@ -60,10 +60,14 @@ struct adjustments
 
 struct chroma_key
 {
-       binding<core::chroma::type>     key;
-       binding<double>                         threshold;
+       binding<bool>                           enable;
+       binding<double>                         target_hue;
+       binding<double>                         hue_width;
+       binding<double>                         min_saturation;
+       binding<double>                         min_brightness;
        binding<double>                         softness;
        binding<double>                         spill;
+       binding<double>                         spill_darken;
 };
 
 struct layer
@@ -195,7 +199,7 @@ public:
                                                        static_cast<double>(duration)));
 
                                        to_affect.set(tweened);
-                                       
+
                                        //CASPAR_LOG(info) << relative_frame << L" " << *start_value << L" " << duration << L" " << tweened;
                                };
 
index 5eacb6c368f92359bdfe35703f16d27b6e57ae4f..49f1ee4d83648aaf1e0800dcbc649f36e24da5dc 100644 (file)
@@ -170,10 +170,14 @@ spl::shared_ptr<core::frame_producer> create_xml_scene_producer(
                layer.is_key                                    = scene->create_variable<bool>(variable_prefix + L"is_key", false, elem.second.get(L"is_key", L"false"));
                layer.use_mipmap                                = scene->create_variable<bool>(variable_prefix + L"use_mipmap", false, elem.second.get(L"use_mipmap", L"false"));
                layer.blend_mode                                = scene->create_variable<std::wstring>(variable_prefix + L"blend_mode", false, elem.second.get(L"blend_mode", L"normal")).transformed([](const std::wstring& b) { return get_blend_mode(b); });
-               layer.chroma_key.key                    = scene->create_variable<std::wstring>(variable_prefix + L"chroma_key.key", false, elem.second.get(L"chroma_key.key", L"none")).transformed([](const std::wstring& k) { return get_chroma_mode(k); });
-               layer.chroma_key.threshold              = scene->create_variable<double>(variable_prefix + L"chroma_key.threshold", false, elem.second.get(L"chroma_key.threshold", L"0.0"));
+               layer.chroma_key.enable                 = scene->create_variable<bool>(variable_prefix + L"chroma_key.enable", false, elem.second.get(L"chroma_key.enable", L"false"));
+               layer.chroma_key.target_hue             = scene->create_variable<double>(variable_prefix + L"chroma_key.target_hue", false, elem.second.get(L"chroma_key.target_hue", L"120.0"));
+               layer.chroma_key.hue_width              = scene->create_variable<double>(variable_prefix + L"chroma_key.hue_width", false, elem.second.get(L"chroma_key.hue_width", L"0.1"));
+               layer.chroma_key.min_saturation = scene->create_variable<double>(variable_prefix + L"chroma_key.min_saturation", false, elem.second.get(L"chroma_key.min_saturation", L"0.0"));
+               layer.chroma_key.min_brightness = scene->create_variable<double>(variable_prefix + L"chroma_key.min_brightness", false, elem.second.get(L"chroma_key.min_brightness", L"0.0"));
                layer.chroma_key.softness               = scene->create_variable<double>(variable_prefix + L"chroma_key.softness", false, elem.second.get(L"chroma_key.softness", L"0.0"));
-               layer.chroma_key.spill                  = scene->create_variable<double>(variable_prefix + L"chroma_key.spill", false, elem.second.get(L"chroma_key.spill", L"0.0"));
+               layer.chroma_key.spill                  = scene->create_variable<double>(variable_prefix + L"chroma_key.spill", false, elem.second.get(L"chroma_key.spill", L"1.0"));
+               layer.chroma_key.spill_darken   = scene->create_variable<double>(variable_prefix + L"chroma_key.spill_darken", false, elem.second.get(L"chroma_key.spill_darken", L"2.0"));
 
                scene->create_variable<double>(variable_prefix + L"width", false) = layer.producer.get()->pixel_constraints().width;
                scene->create_variable<double>(variable_prefix + L"height", false) = layer.producer.get()->pixel_constraints().height;
@@ -234,7 +238,7 @@ spl::shared_ptr<core::frame_producer> create_xml_scene_producer(
 
        auto repo = [&scene](const std::wstring& name) -> variable&
        {
-               return scene->get_variable(name); 
+               return scene->get_variable(name);
        };
 
        for (auto& var_name : scene->get_variables())
index a490da5d714ffb71db4de3eff5a9ac36d1c51e6d..b1e6536c70fab344c315f14098047c0997f7e41b 100644 (file)
@@ -1272,16 +1272,33 @@ std::wstring ANIMATION_SYNTAX = L" {[duration:int] {[tween:string]|linear}|0 lin
 void mixer_chroma_describer(core::help_sink& sink, const core::help_repository& repo)
 {
        sink.short_description(L"Enable chroma keying on a layer.");
-       sink.syntax(L"MIXER [video_channel:int]{-[layer:int]|-0} CHROMA {[color:none,green,blue] {[threshold:float] [softness:float] {[spill:float]}}" + ANIMATION_SYNTAX);
+       sink.syntax(L"MIXER [video_channel:int]{-[layer:int]|-0} CHROMA {[enable:0,1] {[target_hue:float] [hue_width:float] [min_saturation:float] [min_brightness:float] [softness:float] [spill:float] [spill_darken:float] [show_mask:0,1]}}" + ANIMATION_SYNTAX);
        sink.para()
                ->text(L"Enables or disables chroma keying on the specified video layer. Giving no parameters returns the current chroma settings.");
-       sink.para()->text(L"Examples:");
-       sink.example(L">> MIXER 1-1 CHROMA green 0.10 0.20 25 easeinsine");
-       sink.example(L">> MIXER 1-1 CHROMA none");
+       sink.para()->text(L"The chroma keying is done in the HSB/HSV color space.");
+       sink.para()->text(L"Parameters:");
+       sink.definitions()
+               ->item(L"enable", L"0 to disable chroma keying on layer. The rest of the parameters should not be given when disabling.")
+               ->item(L"target_hue", L"The hue in degrees between 0-360 where the center of the hue window will open up.")
+               ->item(L"hue_width", L"The width of the hue window within 0.0-1.0 where 1.0 means 100% of 360 degrees around target_hue.")
+               ->item(L"min_saturation", L"The minimum saturation within 0.0-1.0 required for a color to be within the chroma window.")
+               ->item(L"min_brightness", L"The minimum brightness within 0.0-1.0 required for a color to be within the chroma window.")
+               ->item(L"softness", L"The softness of the chroma keying window.")
+               ->item(L"spill", L"Controls the amount of spill. A value of 1.0 does not suppress any spill. A lower value gradually turns the spill into grayscale and more transparent.")
+               ->item(L"spill_darken", L"Controls the shade of gray that the spill suppression is done towards. Lower values goes towards white and higher values goes towards black.")
+               ->item(L"show_mask", L"If enabled, only shows the mask. Useful while editing the chroma key settings.")
+               ;
+       sink.example(L">> MIXER 1-1 CHROMA 1 120 0.1 0 0 0.1 1 2 0", L"for enabling chroma keying centered around a hue of 120 degrees (green) and with a 10% hue width");
+       sink.example(L">> MIXER 1-1 CHROMA 0", L"for disabling chroma keying");
        sink.example(
-               L">> MIXER 1-1 BLEND\n"
-               L"<< 201 MIXER OK\n"
-               L"<< SCREEN", L"for getting the current blend mode");
+               L">> MIXER 1-1 CHROMA 0\n"
+               L"<< 202 MIXER OK\n"
+               L"<< 1 120 0.1 0 0 0.1 1 2 0", L"for getting the current chroma key mode");
+       sink.para()->text(L"Deprecated legacy syntax:");
+       sink.syntax(L"MIXER [video_channel:int]{-[layer:int]|-0} CHROMA {[color:none,green,blue] {[threshold:float] [softness:float] [spill:float]}}" + ANIMATION_SYNTAX);
+       sink.para()->text(L"Deprecated legacy examples:");
+       sink.example(L">> MIXER 1-1 CHROMA green 0.10 0.20 1.0 25 easeinsine");
+       sink.example(L">> MIXER 1-1 CHROMA none");
 }
 
 std::wstring mixer_chroma_command(command_context& ctx)
@@ -1290,26 +1307,72 @@ std::wstring mixer_chroma_command(command_context& ctx)
        {
                auto chroma = get_current_transform(ctx).image_transform.chroma;
                return L"201 MIXER OK\r\n"
-                       + core::get_chroma_mode(chroma.key) + L" "
-                       + boost::lexical_cast<std::wstring>(chroma.threshold) + L" "
+                       + std::wstring(chroma.enable ? L"1 " : L"0 ")
+                       + boost::lexical_cast<std::wstring>(chroma.target_hue) + L" "
+                       + boost::lexical_cast<std::wstring>(chroma.hue_width) + L" "
+                       + boost::lexical_cast<std::wstring>(chroma.min_saturation) + L" "
+                       + boost::lexical_cast<std::wstring>(chroma.min_brightness) + L" "
                        + boost::lexical_cast<std::wstring>(chroma.softness) + L" "
-                       + boost::lexical_cast<std::wstring>(chroma.spill) + L"\r\n";
+                       + boost::lexical_cast<std::wstring>(chroma.spill) + L" "
+                       + boost::lexical_cast<std::wstring>(chroma.spill_darken) + L" "
+                       + std::wstring(chroma.show_mask ? L"1" : L"0") + L"\r\n";
        }
 
        transforms_applier transforms(ctx);
-       int duration = ctx.parameters.size() > 4 ? boost::lexical_cast<int>(ctx.parameters.at(4)) : 0;
-       std::wstring tween = ctx.parameters.size() > 5 ? ctx.parameters.at(5) : L"linear";
-
        core::chroma chroma;
-       chroma.key = get_chroma_mode(ctx.parameters.at(0));
 
-       if (chroma.key != core::chroma::type::none)
+       int duration;
+       std::wstring tween;
+
+       auto legacy_mode = core::get_chroma_mode(ctx.parameters.at(0));
+
+       if (legacy_mode)
        {
-               chroma.threshold = boost::lexical_cast<double>(ctx.parameters.at(1));
-               chroma.softness = boost::lexical_cast<double>(ctx.parameters.at(2));
-               chroma.spill = ctx.parameters.size() > 3 ? boost::lexical_cast<double>(ctx.parameters.at(3)) : 0.0;
+
+               duration = ctx.parameters.size() > 4 ? boost::lexical_cast<int>(ctx.parameters.at(4)) : 0;
+               tween = ctx.parameters.size() > 5 ? ctx.parameters.at(5) : L"linear";
+
+               if (*legacy_mode == chroma::legacy_type::none)
+               {
+                       chroma.enable = false;
+               }
+               else
+               {
+                       chroma.enable = true;
+                       chroma.hue_width = 0.5 - boost::lexical_cast<double>(ctx.parameters.at(1)) * 0.5;
+                       chroma.min_brightness = boost::lexical_cast<double>(ctx.parameters.at(1));
+                       chroma.min_saturation = boost::lexical_cast<double>(ctx.parameters.at(1));
+                       chroma.softness = boost::lexical_cast<double>(ctx.parameters.at(2)) - boost::lexical_cast<double>(ctx.parameters.at(1));
+                       chroma.spill = boost::lexical_cast<double>(ctx.parameters.at(3));
+                       chroma.spill_darken = 2;
+
+                       if (*legacy_mode == chroma::legacy_type::green)
+                               chroma.target_hue = 120;
+                       else if (*legacy_mode == chroma::legacy_type::blue)
+                               chroma.target_hue = 240;
+               }
+       }
+       else
+       {
+               duration = ctx.parameters.size() > 9 ? boost::lexical_cast<int>(ctx.parameters.at(9)) : 0;
+               tween = ctx.parameters.size() > 10 ? ctx.parameters.at(10) : L"linear";
+
+               chroma.enable = ctx.parameters.at(0) == L"1";
+
+               if (chroma.enable)
+               {
+                       chroma.target_hue               = boost::lexical_cast<double>(ctx.parameters.at(1));
+                       chroma.hue_width                = boost::lexical_cast<double>(ctx.parameters.at(2));
+                       chroma.min_saturation   = boost::lexical_cast<double>(ctx.parameters.at(3));
+                       chroma.min_brightness   = boost::lexical_cast<double>(ctx.parameters.at(4));
+                       chroma.softness                 = boost::lexical_cast<double>(ctx.parameters.at(5));
+                       chroma.spill                    = boost::lexical_cast<double>(ctx.parameters.at(6));
+                       chroma.spill_darken             = boost::lexical_cast<double>(ctx.parameters.at(7));
+                       chroma.show_mask                = boost::lexical_cast<double>(ctx.parameters.at(8));
+               }
        }
 
+
        transforms.add(stage::transform_tuple_t(ctx.layer_index(), [=](frame_transform transform) -> frame_transform
        {
                transform.image_transform.chroma = chroma;