]> git.sesse.net Git - casparcg/blob - modules/image/util/image_algorithms.h
Merge branch '2.1.0' of https://github.com/CasparCG/Server into 2.1.0
[casparcg] / modules / image / util / image_algorithms.h
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: Helge Norberg, helge.norberg@svt.se
20 */
21
22 #pragma once
23
24 #include <common/tweener.h>
25
26 #include <cmath>
27 #include <cstdint>
28 #include <boost/foreach.hpp>
29
30 namespace caspar { namespace image {
31
32 /**
33  * Helper for calculating the color of a pixel given any number of of other
34  * pixels (each with their own weight).
35  */
36 class rgba_weighting
37 {
38         int r, g, b, a;
39         int total_weight;
40 public:
41         rgba_weighting()
42                 : r(0), g(0), b(0), a(0), total_weight(0)
43         {
44         }
45
46         template<class RGBAPixel>
47         inline void add_pixel(const RGBAPixel& pixel, uint8_t weight)
48         {
49                 r += pixel.r() * weight;
50                 g += pixel.g() * weight;
51                 b += pixel.b() * weight;
52                 a += pixel.a() * weight;
53
54                 total_weight += weight;
55         }
56
57         template<class RGBAPixel>
58         inline void store_result(RGBAPixel& pixel)
59         {
60                 pixel.r() = static_cast<uint8_t>(r / total_weight);
61                 pixel.g() = static_cast<uint8_t>(g / total_weight);
62                 pixel.b() = static_cast<uint8_t>(b / total_weight);
63                 pixel.a() = static_cast<uint8_t>(a / total_weight);
64         }
65 };
66
67 template<class T>
68 std::vector<T> get_tweened_values(const core::tweener& tweener, size_t num_values, T from, T to)
69 {
70         std::vector<T> result;
71         result.reserve(num_values);
72
73         double start = static_cast<double>(from);
74         double delta = static_cast<double>(to - from);
75         double duration = static_cast<double>(num_values);
76
77         for (double t = 0; t < duration; ++t)
78         {
79                 result.push_back(static_cast<T>(tweener(t, start, delta, duration - 1.0)));
80         }
81
82         return std::move(result);
83 }
84
85 /**
86  * Blur a source image and store the blurred result in a destination image.
87  * <p>
88  * The blur is done by weighting each relative pixel from a destination pixel
89  * position using a vector of relative x-y pairs. The further away a related
90  * pixel is the less weight it gets. A tweener is used to calculate the actual
91  * weights of each related pixel.
92  *
93  * @param src                      The source view. Has to model the ImageView
94  *                                 concept and have a pixel type modelling the
95  *                                 RGBAPixel concept.
96  * @param dst                      The destination view. Has to model the
97  *                                 ImageView concept and have a pixel type
98  *                                 modelling the RGBAPixel concept.
99  * @param motion_trail_coordinates The relative x-y positions to weight in for
100  *                                 each pixel.
101  * @param tweener                  The tweener to use for calculating the
102  *                                 weights of each relative position in the
103  *                                 motion trail.
104  */
105 template<class SrcView, class DstView>
106 void blur(
107         const SrcView& src,
108         DstView& dst,
109         const std::vector<std::pair<int, int>>& motion_trail_coordinates, 
110         const core::tweener& tweener)
111 {
112         auto blur_px = motion_trail_coordinates.size();
113         auto tweened_weights_y = get_tweened_values<uint8_t>(tweener, blur_px + 2, 255, 0);
114         tweened_weights_y.pop_back();
115         tweened_weights_y.erase(tweened_weights_y.begin());
116
117         auto src_end = src.end();
118         auto dst_iter = dst.begin();
119
120         for (auto src_iter = src.begin(); src_iter != src_end; ++src_iter, ++dst_iter)
121         {
122                 rgba_weighting w;
123
124                 for (int i = 0; i < blur_px; ++i)
125                 {
126                         auto& coordinate = motion_trail_coordinates[i];
127                         auto other_pixel = src.relative(src_iter, coordinate.first, coordinate.second);
128
129                         if (other_pixel == nullptr)
130                                 break;
131
132                         w.add_pixel(*other_pixel, tweened_weights_y[i]);
133                 }
134
135                 w.add_pixel(*src_iter, 255);
136                 w.store_result(*dst_iter);
137         }
138 }
139
140 /**
141  * Calculate relative x-y coordinates of a straight line with a given angle and
142  * a given number of points.
143  *
144  * @param num_pixels    The number of pixels/points to create.
145  * @param angle_radians The angle of the line in radians.
146  *
147  * @return the x-y pairs.
148  */
149 std::vector<std::pair<int, int>> get_line_points(int num_pixels, double angle_radians);
150
151 /**
152  * Directionally blur a source image modelling the ImageView concept and store
153  * the blurred image to a destination image also modelling the ImageView
154  * concept.
155  * <p>
156  * The pixel type of the views must model the RGBAPixel concept.
157  *
158  * @param src           The source image view. Has to model the ImageView
159  *                      concept and have a pixel type that models RGBAPixel.
160  * @param dst           The destiation image view. Has to model the ImageView
161  *                      concept and have a pixel type that models RGBAPixel.
162  * @param angle_radians The angle in radians to directionally blur the image.
163  * @param blur_px       The number of pixels of the blur.
164  * @param tweener       The tweener to use to create a pixel weighting curve
165  *                      with.
166  */
167 template<class SrcView, class DstView>
168 void blur(
169         const SrcView& src,
170         DstView& dst,
171         double angle_radians,
172         int blur_px, 
173         const core::tweener& tweener)
174 {
175         auto motion_trail = get_line_points(blur_px, angle_radians);
176
177         blur(src, dst, motion_trail, tweener);
178 }
179
180 /**
181  * Premultiply with alpha for each pixel in an ImageView. The modifications is
182  * done in place. The pixel type of the ImageView must model the RGBAPixel
183  * concept.
184  *
185  * @param view_to_modify The image view to premultiply in place. Has to model
186  *                       the ImageView concept and have a pixel type that
187  *                       models RGBAPixel.
188  */
189 template<class SrcDstView>
190 void premultiply(SrcDstView& view_to_modify)
191 {
192         std::for_each(view_to_modify.begin(), view_to_modify.end(), [&](SrcDstView::pixel_type& pixel)
193         {
194                 int alpha = static_cast<int>(pixel.a());
195
196                 if (alpha != 255) // Performance optimization
197                 {
198                         // We don't event try to premultiply 0 since it will be unaffected.
199                         if (pixel.r())
200                                 pixel.r() = static_cast<uint8_t>(static_cast<int>(pixel.r()) * alpha / 255);
201
202                         if (pixel.g())
203                                 pixel.g() = static_cast<uint8_t>(static_cast<int>(pixel.g()) * alpha / 255);
204
205                         if (pixel.b())
206                                 pixel.b() = static_cast<uint8_t>(static_cast<int>(pixel.b()) * alpha / 255);
207                 }
208         });
209 }
210
211 }}