2 * Copyright (c) 2011 Sveriges Television AB <info@casparcg.com>
4 * This file is part of CasparCG (www.casparcg.com).
6 * CasparCG is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * CasparCG is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with CasparCG. If not, see <http://www.gnu.org/licenses/>.
19 * Author: Robert Nagy, ronag89@gmail.com
22 #include "../StdAfx.h"
24 #include "frame_transform.h"
26 #include <boost/range/algorithm/equal.hpp>
27 #include <boost/thread.hpp>
29 namespace caspar { namespace core {
33 template<typename Rect>
34 void transform_rect(Rect& self, const Rect& other)
36 self.ul[0] += other.ul[0];
37 self.ul[1] += other.ul[1];
38 self.lr[0] *= other.lr[0];
39 self.lr[1] *= other.lr[1];
42 void transform_corners(corners& self, const corners& other)
44 transform_rect(self, other);
46 self.ur[0] *= other.ur[0];
47 self.ur[1] += other.ur[1];
48 self.ll[0] += other.ll[0];
49 self.ll[1] *= other.ll[1];
51 // TODO: figure out the math to compose perspective transforms correctly.
54 image_transform& image_transform::operator*=(const image_transform &other)
56 opacity *= other.opacity;
57 brightness *= other.brightness;
58 contrast *= other.contrast;
59 saturation *= other.saturation;
61 // TODO: can this be done in any way without knowing the aspect ratio of the
62 // actual video mode? Thread local to the rescue
63 auto aspect_ratio = detail::get_current_aspect_ratio();
64 aspect_ratio *= fill_scale[0] / fill_scale[1];
66 boost::array<double, 2> rotated;
68 auto orig_x = other.fill_translation[0];
69 auto orig_y = other.fill_translation[1] / aspect_ratio;
70 rotated[0] = orig_x * std::cos(angle) - orig_y * std::sin(angle);
71 rotated[1] = orig_x * std::sin(angle) + orig_y * std::cos(angle);
72 rotated[1] *= aspect_ratio;
74 anchor[0] += other.anchor[0] * fill_scale[0];
75 anchor[1] += other.anchor[1] * fill_scale[1];
76 fill_translation[0] += rotated[0] * fill_scale[0];
77 fill_translation[1] += rotated[1] * fill_scale[1];
78 fill_scale[0] *= other.fill_scale[0];
79 fill_scale[1] *= other.fill_scale[1];
80 clip_translation[0] += other.clip_translation[0] * clip_scale[0];
81 clip_translation[1] += other.clip_translation[1] * clip_scale[1];
82 clip_scale[0] *= other.clip_scale[0];
83 clip_scale[1] *= other.clip_scale[1];
86 transform_rect(crop, other.crop);
87 transform_corners(perspective, other.perspective);
89 levels.min_input = std::max(levels.min_input, other.levels.min_input);
90 levels.max_input = std::min(levels.max_input, other.levels.max_input);
91 levels.min_output = std::max(levels.min_output, other.levels.min_output);
92 levels.max_output = std::min(levels.max_output, other.levels.max_output);
93 levels.gamma *= other.levels.gamma;
94 chroma.key = std::max(chroma.key, other.chroma.key);
95 chroma.threshold += other.chroma.threshold;
96 chroma.softness += other.chroma.softness;
97 chroma.spill += other.chroma.spill;
98 field_mode = field_mode & other.field_mode;
99 is_key |= other.is_key;
100 is_mix |= other.is_mix;
101 is_still |= other.is_still;
102 use_mipmap |= other.use_mipmap;
103 blend_mode = std::max(blend_mode, other.blend_mode);
104 layer_depth += other.layer_depth;
109 image_transform image_transform::operator*(const image_transform &other) const
111 return image_transform(*this) *= other;
114 double do_tween(double time, double source, double dest, double duration, const tweener& tween)
116 return tween(time, source, dest - source, duration);
119 template<typename Rect>
120 void do_tween_rectangle(const Rect& source, const Rect& dest, Rect& out, double time, double duration, const tweener& tweener)
122 out.ul[0] = do_tween(time, source.ul[0], dest.ul[0], duration, tweener);
123 out.ul[1] = do_tween(time, source.ul[1], dest.ul[1], duration, tweener);
124 out.lr[0] = do_tween(time, source.lr[0], dest.lr[0], duration, tweener);
125 out.lr[1] = do_tween(time, source.lr[1], dest.lr[1], duration, tweener);
128 void do_tween_corners(const corners& source, const corners& dest, corners& out, double time, double duration, const tweener& tweener)
130 do_tween_rectangle(source, dest, out, time, duration, tweener);
132 out.ur[0] = do_tween(time, source.ur[0], dest.ur[0], duration, tweener);
133 out.ur[1] = do_tween(time, source.ur[1], dest.ur[1], duration, tweener);
134 out.ll[0] = do_tween(time, source.ll[0], dest.ll[0], duration, tweener);
135 out.ll[1] = do_tween(time, source.ll[1], dest.ll[1], duration, tweener);
138 image_transform image_transform::tween(double time, const image_transform& source, const image_transform& dest, double duration, const tweener& tween)
140 image_transform result;
142 result.brightness = do_tween(time, source.brightness, dest.brightness, duration, tween);
143 result.contrast = do_tween(time, source.contrast, dest.contrast, duration, tween);
144 result.saturation = do_tween(time, source.saturation, dest.saturation, duration, tween);
145 result.opacity = do_tween(time, source.opacity, dest.opacity, duration, tween);
146 result.anchor[0] = do_tween(time, source.anchor[0], dest.anchor[0], duration, tween);
147 result.anchor[1] = do_tween(time, source.anchor[1], dest.anchor[1], duration, tween);
148 result.fill_translation[0] = do_tween(time, source.fill_translation[0], dest.fill_translation[0], duration, tween);
149 result.fill_translation[1] = do_tween(time, source.fill_translation[1], dest.fill_translation[1], duration, tween);
150 result.fill_scale[0] = do_tween(time, source.fill_scale[0], dest.fill_scale[0], duration, tween);
151 result.fill_scale[1] = do_tween(time, source.fill_scale[1], dest.fill_scale[1], duration, tween);
152 result.clip_translation[0] = do_tween(time, source.clip_translation[0], dest.clip_translation[0], duration, tween);
153 result.clip_translation[1] = do_tween(time, source.clip_translation[1], dest.clip_translation[1], duration, tween);
154 result.clip_scale[0] = do_tween(time, source.clip_scale[0], dest.clip_scale[0], duration, tween);
155 result.clip_scale[1] = do_tween(time, source.clip_scale[1], dest.clip_scale[1], duration, tween);
156 result.angle = do_tween(time, source.angle, dest.angle, duration, tween);
157 result.levels.max_input = do_tween(time, source.levels.max_input, dest.levels.max_input, duration, tween);
158 result.levels.min_input = do_tween(time, source.levels.min_input, dest.levels.min_input, duration, tween);
159 result.levels.max_output = do_tween(time, source.levels.max_output, dest.levels.max_output, duration, tween);
160 result.levels.min_output = do_tween(time, source.levels.min_output, dest.levels.min_output, duration, tween);
161 result.levels.gamma = do_tween(time, source.levels.gamma, dest.levels.gamma, duration, tween);
162 result.chroma.threshold = do_tween(time, source.chroma.threshold, dest.chroma.threshold, duration, tween);
163 result.chroma.softness = do_tween(time, source.chroma.softness, dest.chroma.softness, duration, tween);
164 result.chroma.spill = do_tween(time, source.chroma.spill, dest.chroma.spill, duration, tween);
165 result.chroma.key = dest.chroma.key;
166 result.field_mode = source.field_mode & dest.field_mode;
167 result.is_key = source.is_key | dest.is_key;
168 result.is_mix = source.is_mix | dest.is_mix;
169 result.is_still = source.is_still | dest.is_still;
170 result.use_mipmap = source.use_mipmap | dest.use_mipmap;
171 result.blend_mode = std::max(source.blend_mode, dest.blend_mode);
172 result.layer_depth = dest.layer_depth;
174 do_tween_rectangle(source.crop, dest.crop, result.crop, time, duration, tween);
175 do_tween_corners(source.perspective, dest.perspective, result.perspective, time, duration, tween);
180 bool eq(double lhs, double rhs)
182 return std::abs(lhs - rhs) < 5e-8;
185 bool operator==(const corners& lhs, const corners& rhs)
188 boost::range::equal(lhs.ul, rhs.ul, eq) &&
189 boost::range::equal(lhs.ur, rhs.ur, eq) &&
190 boost::range::equal(lhs.lr, rhs.lr, eq) &&
191 boost::range::equal(lhs.ll, rhs.ll, eq);
194 bool operator==(const rectangle& lhs, const rectangle& rhs)
197 boost::range::equal(lhs.ul, rhs.ul, eq) &&
198 boost::range::equal(lhs.lr, rhs.lr, eq);
201 bool operator==(const image_transform& lhs, const image_transform& rhs)
204 eq(lhs.opacity, rhs.opacity) &&
205 eq(lhs.contrast, rhs.contrast) &&
206 eq(lhs.brightness, rhs.brightness) &&
207 eq(lhs.saturation, rhs.saturation) &&
208 boost::range::equal(lhs.anchor, rhs.anchor, eq) &&
209 boost::range::equal(lhs.fill_translation, rhs.fill_translation, eq) &&
210 boost::range::equal(lhs.fill_scale, rhs.fill_scale, eq) &&
211 boost::range::equal(lhs.clip_translation, rhs.clip_translation, eq) &&
212 boost::range::equal(lhs.clip_scale, rhs.clip_scale, eq) &&
213 eq(lhs.angle, rhs.angle) &&
214 lhs.field_mode == rhs.field_mode &&
215 lhs.is_key == rhs.is_key &&
216 lhs.is_mix == rhs.is_mix &&
217 lhs.is_still == rhs.is_still &&
218 lhs.use_mipmap == rhs.use_mipmap &&
219 lhs.blend_mode == rhs.blend_mode &&
220 lhs.layer_depth == rhs.layer_depth &&
221 lhs.crop == rhs.crop &&
222 lhs.perspective == rhs.perspective;
225 bool operator!=(const image_transform& lhs, const image_transform& rhs)
227 return !(lhs == rhs);
232 audio_transform& audio_transform::operator*=(const audio_transform &other)
234 volume *= other.volume;
235 is_still |= other.is_still;
239 audio_transform audio_transform::operator*(const audio_transform &other) const
241 return audio_transform(*this) *= other;
244 audio_transform audio_transform::tween(double time, const audio_transform& source, const audio_transform& dest, double duration, const tweener& tween)
246 audio_transform result;
247 result.is_still = source.is_still | dest.is_still;
248 result.volume = do_tween(time, source.volume, dest.volume, duration, tween);
253 bool operator==(const audio_transform& lhs, const audio_transform& rhs)
255 return eq(lhs.volume, rhs.volume) && lhs.is_still == rhs.is_still;
258 bool operator!=(const audio_transform& lhs, const audio_transform& rhs)
260 return !(lhs == rhs);
264 frame_transform::frame_transform()
268 frame_transform& frame_transform::operator*=(const frame_transform &other)
270 image_transform *= other.image_transform;
271 audio_transform *= other.audio_transform;
275 frame_transform frame_transform::operator*(const frame_transform &other) const
277 return frame_transform(*this) *= other;
280 frame_transform frame_transform::tween(double time, const frame_transform& source, const frame_transform& dest, double duration, const tweener& tween)
282 frame_transform result;
283 result.image_transform = image_transform::tween(time, source.image_transform, dest.image_transform, duration, tween);
284 result.audio_transform = audio_transform::tween(time, source.audio_transform, dest.audio_transform, duration, tween);
288 bool operator==(const frame_transform& lhs, const frame_transform& rhs)
290 return lhs.image_transform == rhs.image_transform &&
291 lhs.audio_transform == rhs.audio_transform;
294 bool operator!=(const frame_transform& lhs, const frame_transform& rhs)
296 return !(lhs == rhs);
300 core::chroma::type get_chroma_mode(const std::wstring& str)
302 if (boost::iequals(str, L"none"))
303 return core::chroma::type::none;
304 else if (boost::iequals(str, L"green"))
305 return core::chroma::type::green;
306 else if (boost::iequals(str, L"blue"))
307 return core::chroma::type::blue;
309 CASPAR_THROW_EXCEPTION(user_error() << msg_info("chroma mode has to be one of none, green or blue"));
312 std::wstring get_chroma_mode(core::chroma::type type)
316 case core::chroma::type::none:
318 case core::chroma::type::green:
320 case core::chroma::type::blue:
323 CASPAR_THROW_EXCEPTION(programming_error() << msg_info("Unhandled enum constant"));
329 boost::thread_specific_ptr<double>& get_thread_local_aspect_ratio()
331 static boost::thread_specific_ptr<double> aspect_ratio;
333 if (!aspect_ratio.get())
334 aspect_ratio.reset(new double(1.0));
339 void set_current_aspect_ratio(double aspect_ratio)
341 *get_thread_local_aspect_ratio() = aspect_ratio;
344 double get_current_aspect_ratio()
346 return *get_thread_local_aspect_ratio();