]> git.sesse.net Git - casparcg/blobdiff - core/frame/frame_transform.cpp
[bluefish_consumer] Install GPF handler for custom thread.
[casparcg] / core / frame / frame_transform.cpp
index f9c14f3aa3f1fa288d273015d61a7a9492098b07..aba667b59324e8fa71ad9e9d690ad36c30c470e4 100644 (file)
 * Author: Robert Nagy, ronag89@gmail.com
 */
 
-#include "../stdafx.h"
+#include "../StdAfx.h"
 
 #include "frame_transform.h"
 
 #include <boost/range/algorithm/equal.hpp>
-#include <boost/range/algorithm/fill.hpp>
+#include <boost/thread.hpp>
 
 namespace caspar { namespace core {
-               
+
 // image_transform
 
-image_transform::image_transform() 
-       : opacity(1.0)
-       , brightness(1.0)
-       , contrast(1.0)
-       , saturation(1.0)
-       , field_mode(field_mode::progressive)
-       , is_key(false)
-       , is_mix(false)
-       , is_still(false)
+template<typename Rect>
+void transform_rect(Rect& self, const Rect& other)
 {
-       boost::range::fill(fill_translation, 0.0);
-       boost::range::fill(fill_scale, 1.0);
-       boost::range::fill(clip_translation, 0.0);
-       boost::range::fill(clip_scale, 1.0);
+       self.ul[0] += other.ul[0];
+       self.ul[1] += other.ul[1];
+       self.lr[0] *= other.lr[0];
+       self.lr[1] *= other.lr[1];
+}
+
+void transform_corners(corners& self, const corners& other)
+{
+       transform_rect(self, other);
+
+       self.ur[0] *= other.ur[0];
+       self.ur[1] += other.ur[1];
+       self.ll[0] += other.ll[0];
+       self.ll[1] *= other.ll[1];
+
+       // TODO: figure out the math to compose perspective transforms correctly.
 }
 
 image_transform& image_transform::operator*=(const image_transform &other)
 {
-       opacity                                 *= other.opacity;       
+       opacity                                 *= other.opacity;
        brightness                              *= other.brightness;
        contrast                                *= other.contrast;
        saturation                              *= other.saturation;
-       fill_translation[0]             += other.fill_translation[0]*fill_scale[0];
-       fill_translation[1]             += other.fill_translation[1]*fill_scale[1];
+
+       // TODO: can this be done in any way without knowing the aspect ratio of the
+       // actual video mode? Thread local to the rescue
+       auto aspect_ratio                = detail::get_current_aspect_ratio();
+       aspect_ratio                    *= fill_scale[0] / fill_scale[1];
+
+       boost::array<double, 2> rotated;
+
+       auto orig_x                              = other.fill_translation[0];
+       auto orig_y                              = other.fill_translation[1] / aspect_ratio;
+       rotated[0]                               = orig_x * std::cos(angle) - orig_y * std::sin(angle);
+       rotated[1]                               = orig_x * std::sin(angle) + orig_y * std::cos(angle);
+       rotated[1]                              *= aspect_ratio;
+
+       anchor[0]                               += other.anchor[0] * fill_scale[0];
+       anchor[1]                               += other.anchor[1] * fill_scale[1];
+       fill_translation[0]             += rotated[0] * fill_scale[0];
+       fill_translation[1]             += rotated[1] * fill_scale[1];
        fill_scale[0]                   *= other.fill_scale[0];
        fill_scale[1]                   *= other.fill_scale[1];
-       clip_translation[0]             += other.clip_translation[0]*clip_scale[0];
-       clip_translation[1]             += other.clip_translation[1]*clip_scale[1];
+       clip_translation[0]             += other.clip_translation[0] * clip_scale[0];
+       clip_translation[1]             += other.clip_translation[1] * clip_scale[1];
        clip_scale[0]                   *= other.clip_scale[0];
        clip_scale[1]                   *= other.clip_scale[1];
-       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;
-       field_mode                               = static_cast<core::field_mode>(field_mode & other.field_mode);
-       is_key                                  |= other.is_key;
-       is_mix                                  |= other.is_mix;
-       is_still                                |= other.is_still;
+       angle                                   += other.angle;
+
+       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_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;
 }
 
@@ -77,59 +116,129 @@ image_transform image_transform::operator*(const image_transform &other) const
        return image_transform(*this) *= other;
 }
 
+double do_tween(double time, double source, double dest, double duration, const tweener& tween)
+{
+       return tween(time, source, dest - source, duration);
+};
+
+template<typename Rect>
+void do_tween_rectangle(const Rect& source, const Rect& dest, Rect& out, double time, double duration, const tweener& tweener)
+{
+       out.ul[0] = do_tween(time, source.ul[0], dest.ul[0], duration, tweener);
+       out.ul[1] = do_tween(time, source.ul[1], dest.ul[1], duration, tweener);
+       out.lr[0] = do_tween(time, source.lr[0], dest.lr[0], duration, tweener);
+       out.lr[1] = do_tween(time, source.lr[1], dest.lr[1], duration, tweener);
+}
+
+void do_tween_corners(const corners& source, const corners& dest, corners& out, double time, double duration, const tweener& tweener)
+{
+       do_tween_rectangle(source, dest, out, time, duration, tweener);
+
+       out.ur[0] = do_tween(time, source.ur[0], dest.ur[0], duration, tweener);
+       out.ur[1] = do_tween(time, source.ur[1], dest.ur[1], duration, tweener);
+       out.ll[0] = do_tween(time, source.ll[0], dest.ll[0], duration, tweener);
+       out.ll[1] = do_tween(time, source.ll[1], dest.ll[1], duration, tweener);
+};
+
 image_transform image_transform::tween(double time, const image_transform& source, const image_transform& dest, double duration, const tweener& tween)
-{      
-       auto do_tween = [](double time, double source, double dest, double duration, const tweener& tween)
-       {
-               return tween(time, source, dest-source, duration);
-       };
-       
-       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.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.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.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;
-       
+{
+       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_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);
+
        return result;
 }
 
-bool operator==(const image_transform& lhs, const image_transform& rhs)
+bool eq(double lhs, double rhs)
 {
-       auto eq = [](double lhs, double rhs)
-       {
-               return std::abs(lhs - rhs) < 5e-8;
-       };
+       return std::abs(lhs - rhs) < 5e-8;
+};
 
-       return 
+bool operator==(const corners& lhs, const corners& rhs)
+{
+       return
+               boost::range::equal(lhs.ul, rhs.ul, eq) &&
+               boost::range::equal(lhs.ur, rhs.ur, eq) &&
+               boost::range::equal(lhs.lr, rhs.lr, eq) &&
+               boost::range::equal(lhs.ll, rhs.ll, eq);
+}
+
+bool operator==(const rectangle& lhs, const rectangle& rhs)
+{
+       return
+               boost::range::equal(lhs.ul, rhs.ul, eq) &&
+               boost::range::equal(lhs.lr, rhs.lr, eq);
+}
+
+bool operator==(const image_transform& lhs, const image_transform& rhs)
+{
+       return
                eq(lhs.opacity, rhs.opacity) &&
                eq(lhs.contrast, rhs.contrast) &&
                eq(lhs.brightness, rhs.brightness) &&
                eq(lhs.saturation, rhs.saturation) &&
+               boost::range::equal(lhs.anchor, rhs.anchor, eq) &&
                boost::range::equal(lhs.fill_translation, rhs.fill_translation, eq) &&
                boost::range::equal(lhs.fill_scale, rhs.fill_scale, eq) &&
                boost::range::equal(lhs.clip_translation, rhs.clip_translation, eq) &&
                boost::range::equal(lhs.clip_scale, rhs.clip_scale, eq) &&
+               eq(lhs.angle, rhs.angle) &&
                lhs.field_mode == rhs.field_mode &&
                lhs.is_key == rhs.is_key &&
                lhs.is_mix == rhs.is_mix &&
-               lhs.is_still == rhs.is_still;
+               lhs.is_still == rhs.is_still &&
+               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_suppress, rhs.chroma.spill_suppress) &&
+               eq(lhs.chroma.spill_suppress_saturation, rhs.chroma.spill_suppress_saturation) &&
+               lhs.crop == rhs.crop &&
+               lhs.perspective == rhs.perspective;
 }
 
 bool operator!=(const image_transform& lhs, const image_transform& rhs)
@@ -138,16 +247,10 @@ bool operator!=(const image_transform& lhs, const image_transform& rhs)
 }
 
 // audio_transform
-               
-audio_transform::audio_transform() 
-       : volume(1.0)
-       , is_still(false)
-{
-}
 
 audio_transform& audio_transform::operator*=(const audio_transform &other)
 {
-       volume   *= other.volume;       
+       volume   *= other.volume;
        is_still |= other.is_still;
        return *this;
 }
@@ -158,26 +261,16 @@ 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)
-{      
-       auto do_tween = [](double time, double source, double dest, double duration, const tweener& tween)
-       {
-               return tween(time, source, dest-source, duration);
-       };
-       
-       audio_transform result; 
+{
+       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;
 }
 
 bool operator==(const audio_transform& lhs, const audio_transform& rhs)
 {
-       auto eq = [](double lhs, double rhs)
-       {
-               return std::abs(lhs - rhs) < 5e-8;
-       };
-
        return eq(lhs.volume, rhs.volume) && lhs.is_still == rhs.is_still;
 }
 
@@ -213,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;
 }
 
@@ -222,4 +315,39 @@ bool operator!=(const frame_transform& lhs, const frame_transform& rhs)
        return !(lhs == rhs);
 }
 
-}}
\ No newline at end of file
+
+boost::optional<core::chroma::legacy_type> get_chroma_mode(const std::wstring& str)
+{
+       if (boost::iequals(str, L"none"))
+               return core::chroma::legacy_type::none;
+       else if (boost::iequals(str, L"green"))
+               return core::chroma::legacy_type::green;
+       else if (boost::iequals(str, L"blue"))
+               return core::chroma::legacy_type::blue;
+       else
+               return boost::none;
+}
+
+namespace detail {
+
+boost::thread_specific_ptr<double>& get_thread_local_aspect_ratio()
+{
+       static boost::thread_specific_ptr<double> aspect_ratio;
+
+       if (!aspect_ratio.get())
+               aspect_ratio.reset(new double(1.0));
+
+       return aspect_ratio;
+}
+
+void set_current_aspect_ratio(double aspect_ratio)
+{
+       *get_thread_local_aspect_ratio() = aspect_ratio;
+}
+
+double get_current_aspect_ratio()
+{
+       return *get_thread_local_aspect_ratio();
+}
+
+}}}