]> git.sesse.net Git - casparcg/commitdiff
[chroma key] Implemented much better chroma spill suppression using the HSV color...
authorHelge Norberg <helge.norberg@svt.se>
Fri, 3 Mar 2017 16:39:08 +0000 (17:39 +0100)
committerHelge Norberg <helge.norberg@svt.se>
Fri, 3 Mar 2017 16:39:08 +0000 (17:39 +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.xsd
core/producer/scene/scene_producer.cpp
core/producer/scene/scene_producer.h
core/producer/scene/xml_scene_producer.cpp
protocol/amcp/AMCPCommandsImpl.cpp

index 8f9af28664a5e5250fab9497d0d89cbe715a6c91..5b0cfbf54c5c073dd581bb31225466cba137331f 100644 (file)
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -73,7 +73,8 @@ Mixer
     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
+    minimum saturation and minimum brightness constraints. Also a much more\r
+    effective spill suppression method was implemented.\r
   o Fixed bug where glReadPixels() was done from the last drawn to texture\r
     instead of always from the target texture. This means that for example a\r
     MIXER KEYER layer without a layer above to key, as well as a separate alpha\r
index 7f3429d7f68dfa4435d56b4cb22519b1b9ed3bfd..7d82f7c6d383aca7aa7e1f90035f6cb103065b0a 100644 (file)
@@ -292,13 +292,6 @@ static std::string get_chroma_glsl()
                    return 1.0 - smoothstep(1.0, chroma_softness, d);
                }
 
-               vec4 supress_spill(vec4 c, float d)
-               {
-                       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(pow(gl, chroma_spill_darken)), gl), ds);
-               }
-
                // http://stackoverflow.com/questions/15095909/from-rgb-to-hsv-in-opengl-glsl
                vec3 rgb2hsv(vec3 c)
                {
@@ -311,11 +304,28 @@ static std::string get_chroma_glsl()
                        return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);
                }
 
+               // From the same page
+               vec3 hsv2rgb(vec3 c)
+               {
+                       vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
+                       vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
+                       return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
+               }
+
                float AngleDiff(float angle1, float angle2)
                {
                        return 0.5 - abs(abs(angle1 - angle2) - 0.5);
                }
 
+               float AngleDiffDirectional(float angle1, float angle2)
+               {
+                       float diff = angle1 - angle2;
+
+                       return diff < -0.5
+                                       ? diff + 1.0
+                                       : (diff > 0.5 ? diff - 1.0 : diff);
+               }
+
                float Distance(float actual, float target)
                {
                        return min(0.0, target - actual);
@@ -333,13 +343,30 @@ static std::string get_chroma_glsl()
                        return -hueScore * saturationBrightnessScore;
                }
 
+               vec3 supress_spill(vec3 c)
+               {
+                       float hue               = c.x;
+                       float diff              = AngleDiffDirectional(hue, chroma_target_hue);
+                       float distance  = abs(diff) / chroma_spill_suppress;
+
+                       if (distance < 1)
+                       {
+                               c.x = diff < 0
+                                               ? chroma_target_hue - chroma_spill_suppress
+                                               : chroma_target_hue + chroma_spill_suppress;
+                               c.y *= min(1.0, distance + chroma_spill_suppress_saturation);
+                       }
+
+                       return c;
+               }
+
                // Key on any color
                vec4 ChromaOnCustomColor(vec4 c)
                {
                        vec3 hsv                = rgb2hsv(c.rgb);
                        float distance  = ColorDistance(hsv);
                        float d                 = distance * -2.0 + 1.0;
-                   vec4 suppressed     = supress_spill(c.rgba, d).rgba;
+                   vec4 suppressed     = vec4(hsv2rgb(supress_spill(hsv)), 1.0);
                        float alpha             = alpha_map(d);
 
                        suppressed *= alpha;
index 37bb44ff7552bab1d0f240de2cba5f1c49a54f46..6caab47480deea00ce7dc94dab525632889440c1 100644 (file)
@@ -270,14 +270,14 @@ struct image_kernel::impl
                {
                        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);
+                       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_suppress",                           params.transform.chroma.spill_suppress / 360.0);
+                       shader_->set("chroma_spill_suppress_saturation",                params.transform.chroma.spill_suppress_saturation);
                }
                else
                        shader_->set("chroma", false);
index c4a5ecdc3519fe3d76fb22b699c2faf761ca9b60..3258a45c877fa252977ee7ba4fe817a4418e4518 100644 (file)
@@ -210,8 +210,8 @@ std::string get_fragment(bool blend_modes, bool post_processing)
                        uniform float           chroma_min_saturation;
                        uniform float           chroma_min_brightness;
                        uniform float           chroma_softness;
-                       uniform float           chroma_spill;
-                       uniform float           chroma_spill_darken;
+                       uniform float           chroma_spill_suppress;
+                       uniform float           chroma_spill_suppress_saturation;
        )shader"
 
        +
index 393214b69aa1326a3c6f7f6d6f27fc30ee770fca..aba667b59324e8fa71ad9e9d690ad36c30c470e4 100644 (file)
@@ -86,27 +86,27 @@ image_transform& image_transform::operator*=(const image_transform &other)
        transform_rect(crop, other.crop);
        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.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.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;
-       is_still                                |= other.is_still;
-       use_mipmap                              |= other.use_mipmap;
-       blend_mode                               = std::max(blend_mode, other.blend_mode);
-       layer_depth                             += other.layer_depth;
+       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.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.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_suppress                            = std::max(other.chroma.spill_suppress, chroma.spill_suppress);
+       chroma.spill_suppress_saturation         = std::min(other.chroma.spill_suppress_saturation, chroma.spill_suppress_saturation);
+       field_mode                                                       = field_mode & other.field_mode;
+       is_key                                                          |= other.is_key;
+       is_mix                                                          |= other.is_mix;
+       is_still                                                        |= other.is_still;
+       use_mipmap                                                      |= other.use_mipmap;
+       blend_mode                                                       = std::max(blend_mode, other.blend_mode);
+       layer_depth                                                     += other.layer_depth;
 
        return *this;
 }
@@ -144,42 +144,42 @@ image_transform image_transform::tween(double time, const image_transform& sourc
 {
        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;
+       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_suppress                    = do_tween(time, source.chroma.spill_suppress,                          dest.chroma.spill_suppress,                             duration, tween);
+       result.chroma.spill_suppress_saturation = do_tween(time, source.chroma.spill_suppress_saturation,       dest.chroma.spill_suppress_saturation,  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);
@@ -235,8 +235,8 @@ bool operator==(const image_transform& lhs, const image_transform& rhs)
                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) &&
+               eq(lhs.chroma.spill_suppress, rhs.chroma.spill_suppress) &&
+               eq(lhs.chroma.spill_suppress_saturation, rhs.chroma.spill_suppress_saturation) &&
                lhs.crop == rhs.crop &&
                lhs.perspective == rhs.perspective;
 }
index 724d35cd1f9b92396f9d01be98227392b718594a..4bea1ca88042667dec2ef864b13d37b9ec4f4b10 100644 (file)
@@ -42,15 +42,15 @@ struct chroma
                blue
        };
 
-       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;
+       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_suppress                          = 0.0;
+       double          spill_suppress_saturation       = 1.0;
 };
 
 struct levels final
index bd43ff0e1d05dc4f2c54835c5a3d905507a00013..269c788041b7312df2ccc30bb14cfc59df545009 100644 (file)
               <xs:element name="layer" minOccurs="0" maxOccurs="unbounded">
                 <xs:complexType>
                   <xs:all>
-                    <xs:element type="producer_string" name="producer">                                             <xs:annotation><xs:documentation>The same syntax as in AMCP after for example PLAY 1-10. Cannot be an expression.</xs:documentation></xs:annotation></xs:element>
-                    <xs:element type="bool_expression" name="hidden" minOccurs="0" default="false">                 <xs:annotation><xs:documentation>Hides the layer if true. Can be an expression.</xs:documentation></xs:annotation></xs:element>
-                    <xs:element type="number_expression" name="x">                                                  <xs:annotation><xs:documentation>The X coordinate of the layer in the scene coordinate system. Can be an expression.</xs:documentation></xs:annotation></xs:element>
-                    <xs:element type="number_expression" name="y">                                                  <xs:annotation><xs:documentation>The Y coordinate of the layer in the scene coordinate system. Can be an expression.</xs:documentation></xs:annotation></xs:element>
-                    <xs:element type="number_expression" name="width" minOccurs="0">                                <xs:annotation><xs:documentation>The width of the layer. Is by default calculated by the producer itself but can be overridden. Can be an expression.</xs:documentation></xs:annotation></xs:element>
-                    <xs:element type="number_expression" name="height" minOccurs="0">                               <xs:annotation><xs:documentation>The height of the layer. Is by default calculated by the producer itself but can be overridden. Can be an expression.</xs:documentation></xs:annotation></xs:element>
-                    <xs:element type="number_expression" name="anchor_x" minOccurs="0" default="0">                 <xs:annotation><xs:documentation>The X anchor within the layer coordinate system where position and rotation are done relative to/around. Can be an expression.</xs:documentation></xs:annotation></xs:element>
-                    <xs:element type="number_expression" name="anchor_y" minOccurs="0" default="0">                 <xs:annotation><xs:documentation>The Y anchor within the layer coordinate system where position and rotation are done relative to/around. Can be an expression.</xs:documentation></xs:annotation></xs:element>
-                    <xs:element type="number_expression" name="rotation" minOccurs="0" default="0.0">               <xs:annotation><xs:documentation>The rotation of the layer around anchor_x/anchor_y expressed in degrees. Can be an expression.</xs:documentation></xs:annotation></xs:element>
-                    <xs:element type="number_expression" name="crop_upper_left_x" minOccurs="0" default="0">        <xs:annotation><xs:documentation>The X position within the layer coordinate system to crop the upper left corner. Can be an expression.</xs:documentation></xs:annotation></xs:element>
-                    <xs:element type="number_expression" name="crop_upper_left_y" minOccurs="0" default="0">        <xs:annotation><xs:documentation>The Y position within the layer coordinate system to crop the upper left corner. Can be an expression.</xs:documentation></xs:annotation></xs:element>
-                    <xs:element type="number_expression" name="crop_lower_right_x" minOccurs="0">                   <xs:annotation><xs:documentation>The X position within the layer coordinate system to crop the lower right corner. Can be an expression.</xs:documentation></xs:annotation></xs:element>
-                    <xs:element type="number_expression" name="crop_lower_right_y" minOccurs="0">                   <xs:annotation><xs:documentation>The Y position within the layer coordinate system to crop the lower right corner. Can be an expression.</xs:documentation></xs:annotation></xs:element>
-                    <xs:element type="number_expression" name="perspective_upper_left_x" minOccurs="0" default="0"> <xs:annotation><xs:documentation>The X position within the layer coordinate system to corner pin the upper left corner. Can be an expression.</xs:documentation></xs:annotation></xs:element>
-                    <xs:element type="number_expression" name="perspective_upper_left_y" minOccurs="0" default="0"> <xs:annotation><xs:documentation>The Y position within the layer coordinate system to corner pin the upper left corner. Can be an expression.</xs:documentation></xs:annotation></xs:element>
-                    <xs:element type="number_expression" name="perspective_upper_right_x" minOccurs="0">            <xs:annotation><xs:documentation>The X position within the layer coordinate system to corner pin the upper right corner. Can be an expression.</xs:documentation></xs:annotation></xs:element>
-                    <xs:element type="number_expression" name="perspective_upper_right_y" minOccurs="0" default="0"><xs:annotation><xs:documentation>The Y position within the layer coordinate system to corner pin the upper right corner. Can be an expression.</xs:documentation></xs:annotation></xs:element>
-                    <xs:element type="number_expression" name="perspective_lower_right_x" minOccurs="0">            <xs:annotation><xs:documentation>The X position within the layer coordinate system to corner pin the lower right corner. Can be an expression.</xs:documentation></xs:annotation></xs:element>
-                    <xs:element type="number_expression" name="perspective_lower_right_y" minOccurs="0">            <xs:annotation><xs:documentation>The Y position within the layer coordinate system to corner pin the lower right corner. Can be an expression.</xs:documentation></xs:annotation></xs:element>
-                    <xs:element type="number_expression" name="perspective_lower_left_x" minOccurs="0" default="0"> <xs:annotation><xs:documentation>The X position within the layer coordinate system to corner pin the lower left corner. Can be an expression.</xs:documentation></xs:annotation></xs:element>
-                    <xs:element type="number_expression" name="perspective_lower_left_y" minOccurs="0">             <xs:annotation><xs:documentation>The Y position within the layer coordinate system to corner pin the lower left corner. Can be an expression.</xs:documentation></xs:annotation></xs:element>
+                    <xs:element type="producer_string" name="producer">                                                     <xs:annotation><xs:documentation>The same syntax as in AMCP after for example PLAY 1-10. Cannot be an expression.</xs:documentation></xs:annotation></xs:element>
+                    <xs:element type="bool_expression" name="hidden" minOccurs="0" default="false">                         <xs:annotation><xs:documentation>Hides the layer if true. Can be an expression.</xs:documentation></xs:annotation></xs:element>
+                    <xs:element type="number_expression" name="x">                                                          <xs:annotation><xs:documentation>The X coordinate of the layer in the scene coordinate system. Can be an expression.</xs:documentation></xs:annotation></xs:element>
+                    <xs:element type="number_expression" name="y">                                                          <xs:annotation><xs:documentation>The Y coordinate of the layer in the scene coordinate system. Can be an expression.</xs:documentation></xs:annotation></xs:element>
+                    <xs:element type="number_expression" name="width" minOccurs="0">                                        <xs:annotation><xs:documentation>The width of the layer. Is by default calculated by the producer itself but can be overridden. Can be an expression.</xs:documentation></xs:annotation></xs:element>
+                    <xs:element type="number_expression" name="height" minOccurs="0">                                       <xs:annotation><xs:documentation>The height of the layer. Is by default calculated by the producer itself but can be overridden. Can be an expression.</xs:documentation></xs:annotation></xs:element>
+                    <xs:element type="number_expression" name="anchor_x" minOccurs="0" default="0">                         <xs:annotation><xs:documentation>The X anchor within the layer coordinate system where position and rotation are done relative to/around. Can be an expression.</xs:documentation></xs:annotation></xs:element>
+                    <xs:element type="number_expression" name="anchor_y" minOccurs="0" default="0">                         <xs:annotation><xs:documentation>The Y anchor within the layer coordinate system where position and rotation are done relative to/around. Can be an expression.</xs:documentation></xs:annotation></xs:element>
+                    <xs:element type="number_expression" name="rotation" minOccurs="0" default="0.0">                       <xs:annotation><xs:documentation>The rotation of the layer around anchor_x/anchor_y expressed in degrees. Can be an expression.</xs:documentation></xs:annotation></xs:element>
+                    <xs:element type="number_expression" name="crop_upper_left_x" minOccurs="0" default="0">                <xs:annotation><xs:documentation>The X position within the layer coordinate system to crop the upper left corner. Can be an expression.</xs:documentation></xs:annotation></xs:element>
+                    <xs:element type="number_expression" name="crop_upper_left_y" minOccurs="0" default="0">                <xs:annotation><xs:documentation>The Y position within the layer coordinate system to crop the upper left corner. Can be an expression.</xs:documentation></xs:annotation></xs:element>
+                    <xs:element type="number_expression" name="crop_lower_right_x" minOccurs="0">                           <xs:annotation><xs:documentation>The X position within the layer coordinate system to crop the lower right corner. Can be an expression.</xs:documentation></xs:annotation></xs:element>
+                    <xs:element type="number_expression" name="crop_lower_right_y" minOccurs="0">                           <xs:annotation><xs:documentation>The Y position within the layer coordinate system to crop the lower right corner. Can be an expression.</xs:documentation></xs:annotation></xs:element>
+                    <xs:element type="number_expression" name="perspective_upper_left_x" minOccurs="0" default="0">         <xs:annotation><xs:documentation>The X position within the layer coordinate system to corner pin the upper left corner. Can be an expression.</xs:documentation></xs:annotation></xs:element>
+                    <xs:element type="number_expression" name="perspective_upper_left_y" minOccurs="0" default="0">         <xs:annotation><xs:documentation>The Y position within the layer coordinate system to corner pin the upper left corner. Can be an expression.</xs:documentation></xs:annotation></xs:element>
+                    <xs:element type="number_expression" name="perspective_upper_right_x" minOccurs="0">                    <xs:annotation><xs:documentation>The X position within the layer coordinate system to corner pin the upper right corner. Can be an expression.</xs:documentation></xs:annotation></xs:element>
+                    <xs:element type="number_expression" name="perspective_upper_right_y" minOccurs="0" default="0">        <xs:annotation><xs:documentation>The Y position within the layer coordinate system to corner pin the upper right corner. Can be an expression.</xs:documentation></xs:annotation></xs:element>
+                    <xs:element type="number_expression" name="perspective_lower_right_x" minOccurs="0">                    <xs:annotation><xs:documentation>The X position within the layer coordinate system to corner pin the lower right corner. Can be an expression.</xs:documentation></xs:annotation></xs:element>
+                    <xs:element type="number_expression" name="perspective_lower_right_y" minOccurs="0">                    <xs:annotation><xs:documentation>The Y position within the layer coordinate system to corner pin the lower right corner. Can be an expression.</xs:documentation></xs:annotation></xs:element>
+                    <xs:element type="number_expression" name="perspective_lower_left_x" minOccurs="0" default="0">         <xs:annotation><xs:documentation>The X position within the layer coordinate system to corner pin the lower left corner. Can be an expression.</xs:documentation></xs:annotation></xs:element>
+                    <xs:element type="number_expression" name="perspective_lower_left_y" minOccurs="0">                     <xs:annotation><xs:documentation>The Y position within the layer coordinate system to corner pin the lower left corner. Can be an expression.</xs:documentation></xs:annotation></xs:element>
                     <xs:element name="adjustments" minOccurs="0">
                       <xs:annotation>
                         <xs:documentation>
                       </xs:annotation>
                       <xs:complexType>
                         <xs:all>
-                          <xs:element type="number_expression" name="opacity" minOccurs="0" default="1.0">          <xs:annotation><xs:documentation>The opacity of the layer. Can be an expression.</xs:documentation></xs:annotation></xs:element>
+                          <xs:element type="number_expression" name="opacity" minOccurs="0" default="1.0">                  <xs:annotation><xs:documentation>The opacity of the layer. Can be an expression.</xs:documentation></xs:annotation></xs:element>
                         </xs:all>
                       </xs:complexType>
                     </xs:element>
-                    <xs:element type="bool_expression" name="is_key" minOccurs="0" default="false">                 <xs:annotation><xs:documentation>If true causes the layer to key the layer above. Can be an expression.</xs:documentation></xs:annotation></xs:element>
-                    <xs:element type="bool_expression" name="use_mipmap" minOccurs="0" default="false">             <xs:annotation><xs:documentation>Whether to enable mipmapping on the layer or not. Can be an expression.</xs:documentation></xs:annotation></xs:element>
-                    <xs:element type="blend_mode" name="blend_mode" minOccurs="0" default="normal">                 <xs:annotation><xs:documentation>The blend mode to use. Can be an expression.</xs:documentation></xs:annotation></xs:element>
+                    <xs:element type="bool_expression" name="is_key" minOccurs="0" default="false">                         <xs:annotation><xs:documentation>If true causes the layer to key the layer above. Can be an expression.</xs:documentation></xs:annotation></xs:element>
+                    <xs:element type="bool_expression" name="use_mipmap" minOccurs="0" default="false">                     <xs:annotation><xs:documentation>Whether to enable mipmapping on the layer or not. Can be an expression.</xs:documentation></xs:annotation></xs:element>
+                    <xs:element type="blend_mode" name="blend_mode" minOccurs="0" default="normal">                         <xs:annotation><xs:documentation>The blend mode to use. Can be an expression.</xs:documentation></xs:annotation></xs:element>
                     <xs:element name="chroma_key" minOccurs="0">
                       <xs:complexType>
                         <xs:all>
-                          <xs:element type="bool_expression" name="enable" minOccurs="0" default="false">           <xs:annotation><xs:documentation>Whether to enable chroma keying on the layer or not. Can be an expression.</xs:documentation></xs:annotation></xs:element>
-                          <xs:element type="number_expression" name="target_hue" minOccurs="0" default="120.0">     <xs:annotation><xs:documentation>The degrees within 0-360 of the hue window centrum of the color to key. Can be an expression.</xs:documentation></xs:annotation></xs:element>
-                          <xs:element type="number_expression" name="hue_width" minOccurs="0" default="0.1">        <xs:annotation><xs:documentation>How wide within 0.0-1.0 the hue window of the color to key should be. Can be an expression.</xs:documentation></xs:annotation></xs:element>
-                          <xs:element type="number_expression" name="min_saturation" minOccurs="0" default="0.0">   <xs:annotation><xs:documentation>The minimum saturation within 0.0-1.0 of the color to key. Can be an expression.</xs:documentation></xs:annotation></xs:element>
-                          <xs:element type="number_expression" name="min_brightness" minOccurs="0" default="0.0">   <xs:annotation><xs:documentation>The minimum brightness within 0.0-1.0 of the color to key. Can be an expression.</xs:documentation></xs:annotation></xs:element>
-                          <xs:element type="number_expression" name="softness" minOccurs="0" default="0.0">         <xs:annotation><xs:documentation>How soft the chroma key window should be within 0.0-1.0. Can be an expression.</xs:documentation></xs:annotation></xs:element>
-                          <xs:element type="number_expression" name="spill" minOccurs="0" default="1.0">            <xs:annotation><xs:documentation>How much of the chroma fringing should be left within 0.0-1.0. Values below 1.0 gradually replaces the fringe with a gray scale and more transparent equivalent. Can be an expression.</xs:documentation></xs:annotation></xs:element>
-                          <xs:element type="number_expression" name="spill_darken" minOccurs="0" default="2.0">     <xs:annotation><xs:documentation>Defined how dark the spill replacement gray scale should be. The default is 2.0 but can be anywhere between 0.0 and up. Can be an expression.</xs:documentation></xs:annotation></xs:element>
+                          <xs:element type="bool_expression" name="enable" minOccurs="0" default="false">                   <xs:annotation><xs:documentation>Whether to enable chroma keying on the layer or not. Can be an expression.</xs:documentation></xs:annotation></xs:element>
+                          <xs:element type="number_expression" name="target_hue" minOccurs="0" default="120.0">             <xs:annotation><xs:documentation>The degrees within 0-360 of the hue window centrum of the color to key. Can be an expression.</xs:documentation></xs:annotation></xs:element>
+                          <xs:element type="number_expression" name="hue_width" minOccurs="0" default="0.1">                <xs:annotation><xs:documentation>How wide within 0.0-1.0 the hue window of the color to key should be. Can be an expression.</xs:documentation></xs:annotation></xs:element>
+                          <xs:element type="number_expression" name="min_saturation" minOccurs="0" default="0.0">           <xs:annotation><xs:documentation>The minimum saturation within 0.0-1.0 of the color to key. Can be an expression.</xs:documentation></xs:annotation></xs:element>
+                          <xs:element type="number_expression" name="min_brightness" minOccurs="0" default="0.0">           <xs:annotation><xs:documentation>The minimum brightness within 0.0-1.0 of the color to key. Can be an expression.</xs:documentation></xs:annotation></xs:element>
+                          <xs:element type="number_expression" name="softness" minOccurs="0" default="0.0">                 <xs:annotation><xs:documentation>How soft the chroma key window should be within 0.0-1.0. Can be an expression.</xs:documentation></xs:annotation></xs:element>
+                          <xs:element type="number_expression" name="spill_suppress" minOccurs="0" default="0.0">           <xs:annotation><xs:documentation>How much to suppress spill by within 0.0-180.0. It works by taking all hue values within +- this value from target_hue and clamps it to either target_hue - this value or target_hue + this value depending on which side it is closest to. Can be an expression.</xs:documentation></xs:annotation></xs:element>
+                          <xs:element type="number_expression" name="spill_suppress_saturation" minOccurs="0" default="1.0"><xs:annotation><xs:documentation>Controls how much saturation should be kept on colors affected by spill_suppress within 0.0-1.0. Full saturation may not always be desirable to be kept on suppressed colors. Can be an expression.</xs:documentation></xs:annotation></xs:element>
                         </xs:all>
                       </xs:complexType>
                     </xs:element>
index 1d2538f946990f0f21f7627b1594f5defad0f47f..c2ddc3e88b7b3172b77697e306cdc8c1a12883fe 100644 (file)
@@ -304,18 +304,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.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();
+               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_suppress                         = layer.chroma_key.spill_suppress.get();
+               transform.image_transform.chroma.spill_suppress_saturation      = layer.chroma_key.spill_suppress_saturation.get();
 
                // Mark as sublayer, so it will be composited separately by the mixer.
                transform.image_transform.layer_depth = 1;
index a7c37710c31e7d7a1674cdfadccf0f4b2e8352a8..c954f12ccc01923b3e8cfa0c411165d41e67936e 100644 (file)
@@ -60,14 +60,14 @@ struct adjustments
 
 struct chroma_key
 {
-       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;
+       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_suppress;
+       binding<double> spill_suppress_saturation;
 };
 
 struct layer
index f1cebd1af84bdb22e9722fb4f60b57a9029bad33..089e036f8190b81a029050078315dabef3a62c8e 100644 (file)
@@ -161,37 +161,37 @@ spl::shared_ptr<core::frame_producer> create_xml_scene_producer(
                else
                        scene->create_variable<double>(variable_prefix + L"height", false) = layer.producer.get()->pixel_constraints().height;
 
-               layer.hidden                                    = scene->create_variable<bool>(variable_prefix + L"hidden", false, elem.second.get(L"hidden", L"false"));
-               layer.position.x                                = scene->create_variable<double>(variable_prefix + L"x", false, ptree_get<std::wstring>(elem.second, L"x"));
-               layer.position.y                                = scene->create_variable<double>(variable_prefix + L"y", false, ptree_get<std::wstring>(elem.second, L"y"));
-               layer.anchor.x                                  = scene->create_variable<double>(variable_prefix + L"anchor_x", false, elem.second.get<std::wstring>(L"anchor_x", L"0.0"));
-               layer.anchor.y                                  = scene->create_variable<double>(variable_prefix + L"anchor_y", false, elem.second.get<std::wstring>(L"anchor_y", L"0.0"));
-               layer.rotation                                  = scene->create_variable<double>(variable_prefix + L"rotation", false, elem.second.get<std::wstring>(L"rotation", L"0.0"));
-               layer.crop.upper_left.x                 = scene->create_variable<double>(variable_prefix + L"crop_upper_left_x", false, elem.second.get<std::wstring>(L"crop_upper_left_x", L"0.0"));
-               layer.crop.upper_left.y                 = scene->create_variable<double>(variable_prefix + L"crop_upper_left_y", false, elem.second.get<std::wstring>(L"crop_upper_left_y", L"0.0"));
-               layer.crop.lower_right.x                = scene->create_variable<double>(variable_prefix + L"crop_lower_right_x", false, elem.second.get<std::wstring>(L"crop_lower_right_x", L"${" + variable_prefix + L"width}"));
-               layer.crop.lower_right.y                = scene->create_variable<double>(variable_prefix + L"crop_lower_right_y", false, elem.second.get<std::wstring>(L"crop_lower_right_y", L"${" + variable_prefix + L"height}"));
-               layer.perspective.upper_left.x  = scene->create_variable<double>(variable_prefix + L"perspective_upper_left_x", false, elem.second.get<std::wstring>(L"perspective_upper_left_x", L"0.0"));
-               layer.perspective.upper_left.y  = scene->create_variable<double>(variable_prefix + L"perspective_upper_left_y", false, elem.second.get<std::wstring>(L"perspective_upper_left_y", L"0.0"));
-               layer.perspective.upper_right.x = scene->create_variable<double>(variable_prefix + L"perspective_upper_right_x", false, elem.second.get<std::wstring>(L"perspective_upper_right_x", L"${" + variable_prefix + L"width}"));
-               layer.perspective.upper_right.y = scene->create_variable<double>(variable_prefix + L"perspective_upper_right_y", false, elem.second.get<std::wstring>(L"perspective_upper_right_y", L"0.0"));
-               layer.perspective.lower_right.x = scene->create_variable<double>(variable_prefix + L"perspective_lower_right_x", false, elem.second.get<std::wstring>(L"perspective_lower_right_x", L"${" + variable_prefix + L"width}"));
-               layer.perspective.lower_right.y = scene->create_variable<double>(variable_prefix + L"perspective_lower_right_y", false, elem.second.get<std::wstring>(L"perspective_lower_right_y", L"${" + variable_prefix + L"height}"));
-               layer.perspective.lower_left.x  = scene->create_variable<double>(variable_prefix + L"perspective_lower_left_x", false, elem.second.get<std::wstring>(L"perspective_lower_left_x", L"0.0"));
-               layer.perspective.lower_left.y  = scene->create_variable<double>(variable_prefix + L"perspective_lower_left_y", false, elem.second.get<std::wstring>(L"perspective_lower_left_y", L"${" + variable_prefix + L"height}"));
-
-               layer.adjustments.opacity               = scene->create_variable<double>(variable_prefix + L"adjustment.opacity", false, elem.second.get(L"adjustments.opacity", L"1.0"));
-               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.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"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"));
+               layer.hidden                                                            = scene->create_variable<bool>(variable_prefix + L"hidden", false, elem.second.get(L"hidden", L"false"));
+               layer.position.x                                                        = scene->create_variable<double>(variable_prefix + L"x", false, ptree_get<std::wstring>(elem.second, L"x"));
+               layer.position.y                                                        = scene->create_variable<double>(variable_prefix + L"y", false, ptree_get<std::wstring>(elem.second, L"y"));
+               layer.anchor.x                                                          = scene->create_variable<double>(variable_prefix + L"anchor_x", false, elem.second.get<std::wstring>(L"anchor_x", L"0.0"));
+               layer.anchor.y                                                          = scene->create_variable<double>(variable_prefix + L"anchor_y", false, elem.second.get<std::wstring>(L"anchor_y", L"0.0"));
+               layer.rotation                                                          = scene->create_variable<double>(variable_prefix + L"rotation", false, elem.second.get<std::wstring>(L"rotation", L"0.0"));
+               layer.crop.upper_left.x                                         = scene->create_variable<double>(variable_prefix + L"crop_upper_left_x", false, elem.second.get<std::wstring>(L"crop_upper_left_x", L"0.0"));
+               layer.crop.upper_left.y                                         = scene->create_variable<double>(variable_prefix + L"crop_upper_left_y", false, elem.second.get<std::wstring>(L"crop_upper_left_y", L"0.0"));
+               layer.crop.lower_right.x                                        = scene->create_variable<double>(variable_prefix + L"crop_lower_right_x", false, elem.second.get<std::wstring>(L"crop_lower_right_x", L"${" + variable_prefix + L"width}"));
+               layer.crop.lower_right.y                                        = scene->create_variable<double>(variable_prefix + L"crop_lower_right_y", false, elem.second.get<std::wstring>(L"crop_lower_right_y", L"${" + variable_prefix + L"height}"));
+               layer.perspective.upper_left.x                          = scene->create_variable<double>(variable_prefix + L"perspective_upper_left_x", false, elem.second.get<std::wstring>(L"perspective_upper_left_x", L"0.0"));
+               layer.perspective.upper_left.y                          = scene->create_variable<double>(variable_prefix + L"perspective_upper_left_y", false, elem.second.get<std::wstring>(L"perspective_upper_left_y", L"0.0"));
+               layer.perspective.upper_right.x                         = scene->create_variable<double>(variable_prefix + L"perspective_upper_right_x", false, elem.second.get<std::wstring>(L"perspective_upper_right_x", L"${" + variable_prefix + L"width}"));
+               layer.perspective.upper_right.y                         = scene->create_variable<double>(variable_prefix + L"perspective_upper_right_y", false, elem.second.get<std::wstring>(L"perspective_upper_right_y", L"0.0"));
+               layer.perspective.lower_right.x                         = scene->create_variable<double>(variable_prefix + L"perspective_lower_right_x", false, elem.second.get<std::wstring>(L"perspective_lower_right_x", L"${" + variable_prefix + L"width}"));
+               layer.perspective.lower_right.y                         = scene->create_variable<double>(variable_prefix + L"perspective_lower_right_y", false, elem.second.get<std::wstring>(L"perspective_lower_right_y", L"${" + variable_prefix + L"height}"));
+               layer.perspective.lower_left.x                          = scene->create_variable<double>(variable_prefix + L"perspective_lower_left_x", false, elem.second.get<std::wstring>(L"perspective_lower_left_x", L"0.0"));
+               layer.perspective.lower_left.y                          = scene->create_variable<double>(variable_prefix + L"perspective_lower_left_y", false, elem.second.get<std::wstring>(L"perspective_lower_left_y", L"${" + variable_prefix + L"height}"));
+
+               layer.adjustments.opacity                                       = scene->create_variable<double>(variable_prefix + L"adjustment.opacity", false, elem.second.get(L"adjustments.opacity", L"1.0"));
+               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.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_suppress                         = scene->create_variable<double>(variable_prefix + L"chroma_key.spill_suppress", false, elem.second.get(L"chroma_key.spill_suppress", L"0.0"));
+               layer.chroma_key.spill_suppress_saturation      = scene->create_variable<double>(variable_prefix + L"chroma_key.spill_suppress_saturation", false, elem.second.get(L"chroma_key.spill_suppress_saturation", L"1.0"));
 
                for (auto& var_name : producer->get_variables())
                {
index b877bf90d4d2fc10924bd77f6c41b6b1b746293d..4b222b9c2c7f5213053d92e7f46cf3a7269d8a80 100644 (file)
@@ -1294,28 +1294,28 @@ 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 {[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.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_suppress:float] [spill_suppress_saturation: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"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.")
+               ->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_suppress",                               L"How much to suppress spill by within 0.0-180.0. It works by taking all hue values within +- this value from target_hue and clamps it to either target_hue - this value or target_hue + this value depending on which side it is closest to.")
+               ->item(L"spill_suppress_saturation",            L"Controls how much saturation should be kept on colors affected by spill_suppress within 0.0-1.0. Full saturation may not always be desirable to be kept on suppressed colors.")
+               ->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 1 120 0.1 0 0 0.1 0.1 0.7 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 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");
+               L"<< 1 120 0.1 0 0 0.1 0 1 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:");
@@ -1335,8 +1335,8 @@ std::wstring mixer_chroma_command(command_context& ctx)
                        + 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" "
-                       + boost::lexical_cast<std::wstring>(chroma.spill_darken) + L" "
+                       + boost::lexical_cast<std::wstring>(chroma.spill_suppress) + L" "
+                       + boost::lexical_cast<std::wstring>(chroma.spill_suppress_saturation) + L" "
                        + std::wstring(chroma.show_mask ? L"1" : L"0") + L"\r\n";
        }
 
@@ -1360,13 +1360,13 @@ std::wstring mixer_chroma_command(command_context& ctx)
                }
                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;
+                       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_suppress                           = 180.0 - boost::lexical_cast<double>(ctx.parameters.at(3)) * 180.0;
+                       chroma.spill_suppress_saturation        = 1;
 
                        if (*legacy_mode == chroma::legacy_type::green)
                                chroma.target_hue = 120;
@@ -1383,14 +1383,14 @@ std::wstring mixer_chroma_command(command_context& ctx)
 
                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));
+                       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_suppress                           = boost::lexical_cast<double>(ctx.parameters.at(6));
+                       chroma.spill_suppress_saturation        = boost::lexical_cast<double>(ctx.parameters.at(7));
+                       chroma.show_mask                                        = boost::lexical_cast<double>(ctx.parameters.at(8));
                }
        }