]> git.sesse.net Git - casparcg/blob - core/frame/frame_transform.cpp
Merge pull request #501 from dimitry-ishenko-casparcg/next
[casparcg] / core / frame / frame_transform.cpp
1 /*
2 * Copyright (c) 2011 Sveriges Television AB <info@casparcg.com>
3 *
4 * This file is part of CasparCG (www.casparcg.com).
5 *
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.
10 *
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.
15 *
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/>.
18 *
19 * Author: Robert Nagy, ronag89@gmail.com
20 */
21
22 #include "../StdAfx.h"
23
24 #include "frame_transform.h"
25
26 #include <boost/range/algorithm/equal.hpp>
27 #include <boost/thread.hpp>
28
29 namespace caspar { namespace core {
30
31 // image_transform
32
33 template<typename Rect>
34 void transform_rect(Rect& self, const Rect& other)
35 {
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];
40 }
41
42 void transform_corners(corners& self, const corners& other)
43 {
44         transform_rect(self, other);
45
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];
50
51         // TODO: figure out the math to compose perspective transforms correctly.
52 }
53
54 image_transform& image_transform::operator*=(const image_transform &other)
55 {
56         opacity                                 *= other.opacity;
57         brightness                              *= other.brightness;
58         contrast                                *= other.contrast;
59         saturation                              *= other.saturation;
60
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];
65
66         boost::array<double, 2> rotated;
67
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;
73
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];
84         angle                                   += other.angle;
85
86         transform_rect(crop, other.crop);
87         transform_corners(perspective, other.perspective);
88
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.enable                   |= other.chroma.enable;
95         chroma.show_mask                |= other.chroma.show_mask;
96         chroma.target_hue                = std::max(other.chroma.target_hue, chroma.target_hue);
97         chroma.min_saturation    = std::max(other.chroma.min_saturation, chroma.min_saturation);
98         chroma.min_brightness    = std::max(other.chroma.min_brightness, chroma.min_brightness);
99         chroma.hue_width                 = std::max(other.chroma.hue_width, chroma.hue_width);
100         chroma.softness                  = std::max(other.chroma.softness, chroma.softness);
101         chroma.spill                     = std::min(other.chroma.spill, chroma.spill);
102         chroma.spill_darken              = std::max(other.chroma.spill_darken, chroma.spill_darken);
103         field_mode                               = field_mode & other.field_mode;
104         is_key                                  |= other.is_key;
105         is_mix                                  |= other.is_mix;
106         is_still                                |= other.is_still;
107         use_mipmap                              |= other.use_mipmap;
108         blend_mode                               = std::max(blend_mode, other.blend_mode);
109         layer_depth                             += other.layer_depth;
110
111         return *this;
112 }
113
114 image_transform image_transform::operator*(const image_transform &other) const
115 {
116         return image_transform(*this) *= other;
117 }
118
119 double do_tween(double time, double source, double dest, double duration, const tweener& tween)
120 {
121         return tween(time, source, dest - source, duration);
122 };
123
124 template<typename Rect>
125 void do_tween_rectangle(const Rect& source, const Rect& dest, Rect& out, double time, double duration, const tweener& tweener)
126 {
127         out.ul[0] = do_tween(time, source.ul[0], dest.ul[0], duration, tweener);
128         out.ul[1] = do_tween(time, source.ul[1], dest.ul[1], duration, tweener);
129         out.lr[0] = do_tween(time, source.lr[0], dest.lr[0], duration, tweener);
130         out.lr[1] = do_tween(time, source.lr[1], dest.lr[1], duration, tweener);
131 }
132
133 void do_tween_corners(const corners& source, const corners& dest, corners& out, double time, double duration, const tweener& tweener)
134 {
135         do_tween_rectangle(source, dest, out, time, duration, tweener);
136
137         out.ur[0] = do_tween(time, source.ur[0], dest.ur[0], duration, tweener);
138         out.ur[1] = do_tween(time, source.ur[1], dest.ur[1], duration, tweener);
139         out.ll[0] = do_tween(time, source.ll[0], dest.ll[0], duration, tweener);
140         out.ll[1] = do_tween(time, source.ll[1], dest.ll[1], duration, tweener);
141 };
142
143 image_transform image_transform::tween(double time, const image_transform& source, const image_transform& dest, double duration, const tweener& tween)
144 {
145         image_transform result;
146
147         result.brightness                               = do_tween(time, source.brightness,                             dest.brightness,                        duration, tween);
148         result.contrast                                 = do_tween(time, source.contrast,                               dest.contrast,                          duration, tween);
149         result.saturation                               = do_tween(time, source.saturation,                             dest.saturation,                        duration, tween);
150         result.opacity                                  = do_tween(time, source.opacity,                                dest.opacity,                           duration, tween);
151         result.anchor[0]                                = do_tween(time, source.anchor[0],                              dest.anchor[0],                         duration, tween);
152         result.anchor[1]                                = do_tween(time, source.anchor[1],                              dest.anchor[1],                         duration, tween);
153         result.fill_translation[0]              = do_tween(time, source.fill_translation[0],    dest.fill_translation[0],       duration, tween);
154         result.fill_translation[1]              = do_tween(time, source.fill_translation[1],    dest.fill_translation[1],       duration, tween);
155         result.fill_scale[0]                    = do_tween(time, source.fill_scale[0],                  dest.fill_scale[0],                     duration, tween);
156         result.fill_scale[1]                    = do_tween(time, source.fill_scale[1],                  dest.fill_scale[1],                     duration, tween);
157         result.clip_translation[0]              = do_tween(time, source.clip_translation[0],    dest.clip_translation[0],       duration, tween);
158         result.clip_translation[1]              = do_tween(time, source.clip_translation[1],    dest.clip_translation[1],       duration, tween);
159         result.clip_scale[0]                    = do_tween(time, source.clip_scale[0],                  dest.clip_scale[0],                     duration, tween);
160         result.clip_scale[1]                    = do_tween(time, source.clip_scale[1],                  dest.clip_scale[1],                     duration, tween);
161         result.angle                                    = do_tween(time, source.angle,                                  dest.angle,                                     duration, tween);
162         result.levels.max_input                 = do_tween(time, source.levels.max_input,               dest.levels.max_input,          duration, tween);
163         result.levels.min_input                 = do_tween(time, source.levels.min_input,               dest.levels.min_input,          duration, tween);
164         result.levels.max_output                = do_tween(time, source.levels.max_output,              dest.levels.max_output,         duration, tween);
165         result.levels.min_output                = do_tween(time, source.levels.min_output,              dest.levels.min_output,         duration, tween);
166         result.levels.gamma                             = do_tween(time, source.levels.gamma,                   dest.levels.gamma,                      duration, tween);
167         result.chroma.target_hue                = do_tween(time, source.chroma.target_hue,              dest.chroma.target_hue,         duration, tween);
168         result.chroma.hue_width                 = do_tween(time, source.chroma.hue_width,               dest.chroma.hue_width,          duration, tween);
169         result.chroma.min_saturation    = do_tween(time, source.chroma.min_saturation,  dest.chroma.min_saturation,     duration, tween);
170         result.chroma.min_brightness    = do_tween(time, source.chroma.min_brightness,  dest.chroma.min_brightness,     duration, tween);
171         result.chroma.softness                  = do_tween(time, source.chroma.softness,                dest.chroma.softness,           duration, tween);
172         result.chroma.spill                             = do_tween(time, source.chroma.spill,                   dest.chroma.spill,                      duration, tween);
173         result.chroma.spill_darken              = do_tween(time, source.chroma.spill_darken,    dest.chroma.spill_darken,       duration, tween);
174         result.chroma.enable                    = dest.chroma.enable;
175         result.chroma.show_mask                 = dest.chroma.show_mask;
176         result.field_mode                               = source.field_mode & dest.field_mode;
177         result.is_key                                   = source.is_key | dest.is_key;
178         result.is_mix                                   = source.is_mix | dest.is_mix;
179         result.is_still                                 = source.is_still | dest.is_still;
180         result.use_mipmap                               = source.use_mipmap | dest.use_mipmap;
181         result.blend_mode                               = std::max(source.blend_mode, dest.blend_mode);
182         result.layer_depth                              = dest.layer_depth;
183
184         do_tween_rectangle(source.crop, dest.crop, result.crop, time, duration, tween);
185         do_tween_corners(source.perspective, dest.perspective, result.perspective, time, duration, tween);
186
187         return result;
188 }
189
190 bool eq(double lhs, double rhs)
191 {
192         return std::abs(lhs - rhs) < 5e-8;
193 };
194
195 bool operator==(const corners& lhs, const corners& rhs)
196 {
197         return
198                 boost::range::equal(lhs.ul, rhs.ul, eq) &&
199                 boost::range::equal(lhs.ur, rhs.ur, eq) &&
200                 boost::range::equal(lhs.lr, rhs.lr, eq) &&
201                 boost::range::equal(lhs.ll, rhs.ll, eq);
202 }
203
204 bool operator==(const rectangle& lhs, const rectangle& rhs)
205 {
206         return
207                 boost::range::equal(lhs.ul, rhs.ul, eq) &&
208                 boost::range::equal(lhs.lr, rhs.lr, eq);
209 }
210
211 bool operator==(const image_transform& lhs, const image_transform& rhs)
212 {
213         return
214                 eq(lhs.opacity, rhs.opacity) &&
215                 eq(lhs.contrast, rhs.contrast) &&
216                 eq(lhs.brightness, rhs.brightness) &&
217                 eq(lhs.saturation, rhs.saturation) &&
218                 boost::range::equal(lhs.anchor, rhs.anchor, eq) &&
219                 boost::range::equal(lhs.fill_translation, rhs.fill_translation, eq) &&
220                 boost::range::equal(lhs.fill_scale, rhs.fill_scale, eq) &&
221                 boost::range::equal(lhs.clip_translation, rhs.clip_translation, eq) &&
222                 boost::range::equal(lhs.clip_scale, rhs.clip_scale, eq) &&
223                 eq(lhs.angle, rhs.angle) &&
224                 lhs.field_mode == rhs.field_mode &&
225                 lhs.is_key == rhs.is_key &&
226                 lhs.is_mix == rhs.is_mix &&
227                 lhs.is_still == rhs.is_still &&
228                 lhs.use_mipmap == rhs.use_mipmap &&
229                 lhs.blend_mode == rhs.blend_mode &&
230                 lhs.layer_depth == rhs.layer_depth &&
231                 lhs.chroma.enable == rhs.chroma.enable &&
232                 lhs.chroma.show_mask == rhs.chroma.show_mask &&
233                 eq(lhs.chroma.target_hue, rhs.chroma.target_hue) &&
234                 eq(lhs.chroma.hue_width, rhs.chroma.hue_width) &&
235                 eq(lhs.chroma.min_saturation, rhs.chroma.min_saturation) &&
236                 eq(lhs.chroma.min_brightness, rhs.chroma.min_brightness) &&
237                 eq(lhs.chroma.softness, rhs.chroma.softness) &&
238                 eq(lhs.chroma.spill, rhs.chroma.spill) &&
239                 eq(lhs.chroma.spill_darken, rhs.chroma.spill_darken) &&
240                 lhs.crop == rhs.crop &&
241                 lhs.perspective == rhs.perspective;
242 }
243
244 bool operator!=(const image_transform& lhs, const image_transform& rhs)
245 {
246         return !(lhs == rhs);
247 }
248
249 // audio_transform
250
251 audio_transform& audio_transform::operator*=(const audio_transform &other)
252 {
253         volume   *= other.volume;
254         is_still |= other.is_still;
255         return *this;
256 }
257
258 audio_transform audio_transform::operator*(const audio_transform &other) const
259 {
260         return audio_transform(*this) *= other;
261 }
262
263 audio_transform audio_transform::tween(double time, const audio_transform& source, const audio_transform& dest, double duration, const tweener& tween)
264 {
265         audio_transform result;
266         result.is_still                 = source.is_still | dest.is_still;
267         result.volume                   = do_tween(time, source.volume,                         dest.volume,                    duration, tween);
268
269         return result;
270 }
271
272 bool operator==(const audio_transform& lhs, const audio_transform& rhs)
273 {
274         return eq(lhs.volume, rhs.volume) && lhs.is_still == rhs.is_still;
275 }
276
277 bool operator!=(const audio_transform& lhs, const audio_transform& rhs)
278 {
279         return !(lhs == rhs);
280 }
281
282 // frame_transform
283 frame_transform::frame_transform()
284 {
285 }
286
287 frame_transform& frame_transform::operator*=(const frame_transform &other)
288 {
289         image_transform *= other.image_transform;
290         audio_transform *= other.audio_transform;
291         return *this;
292 }
293
294 frame_transform frame_transform::operator*(const frame_transform &other) const
295 {
296         return frame_transform(*this) *= other;
297 }
298
299 frame_transform frame_transform::tween(double time, const frame_transform& source, const frame_transform& dest, double duration, const tweener& tween)
300 {
301         frame_transform result;
302         result.image_transform = image_transform::tween(time, source.image_transform, dest.image_transform, duration, tween);
303         result.audio_transform = audio_transform::tween(time, source.audio_transform, dest.audio_transform, duration, tween);
304         return result;
305 }
306
307 bool operator==(const frame_transform& lhs, const frame_transform& rhs)
308 {
309         return  lhs.image_transform == rhs.image_transform &&
310                         lhs.audio_transform == rhs.audio_transform;
311 }
312
313 bool operator!=(const frame_transform& lhs, const frame_transform& rhs)
314 {
315         return !(lhs == rhs);
316 }
317
318
319 boost::optional<core::chroma::legacy_type> get_chroma_mode(const std::wstring& str)
320 {
321         if (boost::iequals(str, L"none"))
322                 return core::chroma::legacy_type::none;
323         else if (boost::iequals(str, L"green"))
324                 return core::chroma::legacy_type::green;
325         else if (boost::iequals(str, L"blue"))
326                 return core::chroma::legacy_type::blue;
327         else
328                 return boost::none;
329 }
330
331 namespace detail {
332
333 boost::thread_specific_ptr<double>& get_thread_local_aspect_ratio()
334 {
335         static boost::thread_specific_ptr<double> aspect_ratio;
336
337         if (!aspect_ratio.get())
338                 aspect_ratio.reset(new double(1.0));
339
340         return aspect_ratio;
341 }
342
343 void set_current_aspect_ratio(double aspect_ratio)
344 {
345         *get_thread_local_aspect_ratio() = aspect_ratio;
346 }
347
348 double get_current_aspect_ratio()
349 {
350         return *get_thread_local_aspect_ratio();
351 }
352
353 }}}