]> git.sesse.net Git - casparcg/blob - modules/image/util/image_algorithms.h
Merged image_scroll_producer changes to 2.1
[casparcg] / modules / image / util / image_algorithms.h
1 /*\r
2 * Copyright (c) 2011 Sveriges Television AB <info@casparcg.com>\r
3 *\r
4 * This file is part of CasparCG (www.casparcg.com).\r
5 *\r
6 * CasparCG is free software: you can redistribute it and/or modify\r
7 * it under the terms of the GNU General Public License as published by\r
8 * the Free Software Foundation, either version 3 of the License, or\r
9 * (at your option) any later version.\r
10 *\r
11 * CasparCG is distributed in the hope that it will be useful,\r
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
14 * GNU General Public License for more details.\r
15 *\r
16 * You should have received a copy of the GNU General Public License\r
17 * along with CasparCG. If not, see <http://www.gnu.org/licenses/>.\r
18 *\r
19 * Author: Helge Norberg, helge.norberg@svt.se\r
20 */\r
21 \r
22 #pragma once\r
23 \r
24 #include <common/tweener.h>\r
25 \r
26 #include <cmath>\r
27 #include <boost/foreach.hpp>\r
28 \r
29 namespace caspar { namespace image {\r
30 \r
31 /**\r
32  * Helper for calculating the color of a pixel given any number of of other\r
33  * pixels (each with their own weight).\r
34  */\r
35 class rgba_weighting\r
36 {\r
37         int r, g, b, a;\r
38         int total_weight;\r
39 public:\r
40         rgba_weighting()\r
41                 : r(0), g(0), b(0), a(0), total_weight(0)\r
42         {\r
43         }\r
44 \r
45         template<class RGBAPixel>\r
46         inline void add_pixel(const RGBAPixel& pixel, uint8_t weight)\r
47         {\r
48                 r += pixel.r() * weight;\r
49                 g += pixel.g() * weight;\r
50                 b += pixel.b() * weight;\r
51                 a += pixel.a() * weight;\r
52 \r
53                 total_weight += weight;\r
54         }\r
55 \r
56         template<class RGBAPixel>\r
57         inline void store_result(RGBAPixel& pixel)\r
58         {\r
59                 pixel.r() = static_cast<uint8_t>(r / total_weight);\r
60                 pixel.g() = static_cast<uint8_t>(g / total_weight);\r
61                 pixel.b() = static_cast<uint8_t>(b / total_weight);\r
62                 pixel.a() = static_cast<uint8_t>(a / total_weight);\r
63         }\r
64 };\r
65 \r
66 template<class T>\r
67 std::vector<T> get_tweened_values(const core::tweener& tweener, size_t num_values, T from, T to)\r
68 {\r
69         std::vector<T> result;\r
70         result.reserve(num_values);\r
71 \r
72         double start = static_cast<double>(from);\r
73         double delta = static_cast<double>(to - from);\r
74         double duration = static_cast<double>(num_values);\r
75 \r
76         for (double t = 0; t < duration; ++t)\r
77         {\r
78                 result.push_back(static_cast<T>(tweener(t, start, delta, duration - 1.0)));\r
79         }\r
80 \r
81         return std::move(result);\r
82 }\r
83 \r
84 /**\r
85  * Blur a source image and store the blurred result in a destination image.\r
86  * <p>\r
87  * The blur is done by weighting each relative pixel from a destination pixel\r
88  * position using a vector of relative x-y pairs. The further away a related\r
89  * pixel is the less weight it gets. A tweener is used to calculate the actual\r
90  * weights of each related pixel.\r
91  *\r
92  * @param src                      The source view. Has to model the ImageView\r
93  *                                 concept and have a pixel type modelling the\r
94  *                                 RGBAPixel concept.\r
95  * @param dst                      The destination view. Has to model the\r
96  *                                 ImageView concept and have a pixel type\r
97  *                                 modelling the RGBAPixel concept.\r
98  * @param motion_trail_coordinates The relative x-y positions to weight in for\r
99  *                                 each pixel.\r
100  * @param tweener                  The tweener to use for calculating the\r
101  *                                 weights of each relative position in the\r
102  *                                 motion trail.\r
103  */\r
104 template<class SrcView, class DstView>\r
105 void blur(\r
106         const SrcView& src,\r
107         DstView& dst,\r
108         const std::vector<std::pair<int, int>> motion_trail_coordinates, \r
109         const core::tweener& tweener)\r
110 {\r
111         auto blur_px = motion_trail_coordinates.size();\r
112         auto tweened_weights_y = get_tweened_values<uint8_t>(tweener, blur_px + 2, 255, 0);\r
113         tweened_weights_y.pop_back();\r
114         tweened_weights_y.erase(tweened_weights_y.begin());\r
115 \r
116         auto src_end = src.end();\r
117         auto dst_iter = dst.begin();\r
118 \r
119         for (auto src_iter = src.begin(); src_iter != src_end; ++src_iter, ++dst_iter)\r
120         {\r
121                 rgba_weighting w;\r
122 \r
123                 for (int i = 0; i < blur_px; ++i)\r
124                 {\r
125                         auto& coordinate = motion_trail_coordinates[i];\r
126                         auto other_pixel = src.relative(src_iter, coordinate.first, coordinate.second);\r
127 \r
128                         if (other_pixel == nullptr)\r
129                                 break;\r
130 \r
131                         w.add_pixel(*other_pixel, tweened_weights_y[i]);\r
132                 }\r
133 \r
134                 w.add_pixel(*src_iter, 255);\r
135                 w.store_result(*dst_iter);\r
136         }\r
137 }\r
138 \r
139 /**\r
140  * Calculate relative x-y coordinates of a straight line with a given angle and\r
141  * a given number of points.\r
142  *\r
143  * @param num_pixels    The number of pixels/points to create.\r
144  * @param angle_radians The angle of the line in radians.\r
145  *\r
146  * @return the x-y pairs.\r
147  */\r
148 std::vector<std::pair<int, int>> get_line_points(int num_pixels, double angle_radians)\r
149 {\r
150         std::vector<std::pair<int, int>> line_points;\r
151         line_points.reserve(num_pixels);\r
152 \r
153         double delta_x = std::cos(angle_radians);\r
154         double delta_y = -std::sin(angle_radians); // In memory is revered\r
155         double max_delta = std::max(std::abs(delta_x), std::abs(delta_y));\r
156         double amplification = 1.0 / max_delta;\r
157         delta_x *= amplification;\r
158         delta_y *= amplification;\r
159 \r
160         for (int i = 1; i <= num_pixels; ++i)\r
161                 line_points.push_back(std::make_pair(\r
162                         static_cast<int>(std::floor(delta_x * static_cast<double>(i) + 0.5)), \r
163                         static_cast<int>(std::floor(delta_y * static_cast<double>(i) + 0.5))));\r
164 \r
165         return std::move(line_points);\r
166 }\r
167 \r
168 /**\r
169  * Directionally blur a source image modelling the ImageView concept and store\r
170  * the blurred image to a destination image also modelling the ImageView\r
171  * concept.\r
172  * <p>\r
173  * The pixel type of the views must model the RGBAPixel concept.\r
174  *\r
175  * @param src           The source image view. Has to model the ImageView\r
176  *                      concept and have a pixel type that models RGBAPixel.\r
177  * @param dst           The destiation image view. Has to model the ImageView\r
178  *                      concept and have a pixel type that models RGBAPixel.\r
179  * @param angle_radians The angle in radians to directionally blur the image.\r
180  * @param blur_px       The number of pixels of the blur.\r
181  * @param tweener       The tweener to use to create a pixel weighting curve\r
182  *                      with.\r
183  */\r
184 template<class SrcView, class DstView>\r
185 void blur(\r
186         const SrcView& src,\r
187         DstView& dst,\r
188         double angle_radians,\r
189         int blur_px, \r
190         const core::tweener& tweener)\r
191 {\r
192         auto motion_trail = get_line_points(blur_px, angle_radians);\r
193 \r
194         blur(src, dst, motion_trail, tweener);\r
195 }\r
196 \r
197 /**\r
198  * Premultiply with alpha for each pixel in an ImageView. The modifications is\r
199  * done in place. The pixel type of the ImageView must model the RGBAPixel\r
200  * concept.\r
201  *\r
202  * @param view_to_modify The image view to premultiply in place. Has to model\r
203  *                       the ImageView concept and have a pixel type that\r
204  *                       models RGBAPixel.\r
205  */\r
206 template<class SrcDstView>\r
207 void premultiply(SrcDstView& view_to_modify)\r
208 {\r
209         std::for_each(view_to_modify.begin(), view_to_modify.end(), [&](SrcDstView::pixel_type& pixel)\r
210         {\r
211                 int alpha = static_cast<int>(pixel.a());\r
212 \r
213                 if (alpha != 255) // Performance optimization\r
214                 {\r
215                         // We don't event try to premultiply 0 since it will be unaffected.\r
216                         if (pixel.r())\r
217                                 pixel.r() = static_cast<uint8_t>(static_cast<int>(pixel.r()) * alpha / 255);\r
218 \r
219                         if (pixel.g())\r
220                                 pixel.g() = static_cast<uint8_t>(static_cast<int>(pixel.g()) * alpha / 255);\r
221 \r
222                         if (pixel.b())\r
223                                 pixel.b() = static_cast<uint8_t>(static_cast<int>(pixel.b()) * alpha / 255);\r
224                 }\r
225         });\r
226 }\r
227 \r
228 }}\r