1 /* === S Y N F I G ========================================================= */
2 /*!	\file colormatrix.cpp
3 **	\brief Template File
4 **
5 **	$Id$
6 **
7 **	\legal
8 **  ......... ... 2016 Ivan Mahonin
9 **
10 **	This package is free software; you can redistribute it and/or
11 **	modify it under the terms of the GNU General Public License as
12 **	published by the Free Software Foundation; either version 2 of
13 **	the License, or (at your option) any later version.
14 **
15 **	This package is distributed in the hope that it will be useful,
16 **	but WITHOUT ANY WARRANTY; without even the implied warranty of
17 **	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 **	General Public License for more details.
19 **	\endlegal
20 */
21 /* ========================================================================= */
22 
23 /* === H E A D E R S ======================================================= */
24 
25 #ifdef USING_PCH
26 #	include "pch.h"
27 #else
28 #ifdef HAVE_CONFIG_H
29 #	include <config.h>
30 #endif
31 
32 #include <cstring>
33 
34 #include <synfig/general.h>
35 #include "colormatrix.h"
36 
37 #endif
38 
39 /* === U S I N G =========================================================== */
40 
41 using namespace std;
42 using namespace etl;
43 using namespace synfig;
44 
45 /* === M A C R O S ========================================================= */
46 
47 /* === G L O B A L S ======================================================= */
48 
49 /* === M E T H O D S ======================================================= */
50 
51 
52 // Internal
53 
54 namespace {
55 	class Internal
56 	{
57 	public:
58 		template<int channel, int mode_r, int mode_g, int mode_b, int mode_a, int mode_o>
transform(const ColorMatrix & m,const Color & c)59 		static inline ColorMatrix::value_type INRETRNAL_FUNC transform(const ColorMatrix &m, const Color &c)
60 		{
61 			ColorMatrix::value_type x(mode_o ? m[4][channel] : ColorMatrix::value_type(0.0));
62 
63 			if (mode_r == -1) x -= c.get_r();
64 			if (mode_r ==  1) x += c.get_r();
65 			if (mode_r ==  2) x += m[0][channel]*c.get_r();
66 
67 			if (mode_g == -1) x -= c.get_g();
68 			if (mode_g ==  1) x += c.get_g();
69 			if (mode_g ==  2) x += m[1][channel]*c.get_g();
70 
71 			if (mode_b == -1) x -= c.get_b();
72 			if (mode_b ==  1) x += c.get_b();
73 			if (mode_b ==  2) x += m[2][channel]*c.get_b();
74 
75 			if (mode_a == -1) x -= c.get_a();
76 			if (mode_a ==  1) x += c.get_a();
77 			if (mode_a ==  2) x += m[3][channel]*c.get_a();
78 
79 			return x;
80 		}
81 
82 		template<int channel, int mode_r, int mode_g, int mode_b, int mode_a, int mode_o>
batch_transform(const ColorMatrix & m,ColorMatrix::value_type * dest,const Color * src,const Color * src_end)83 		static inline void INRETRNAL_FUNC batch_transform(const ColorMatrix &m, ColorMatrix::value_type *dest, const Color *src, const Color *src_end)
84 		{
85 			for(; src < src_end; dest += 4, ++src)
86 				*dest = transform<channel, mode_r, mode_g, mode_b, mode_a, mode_o>(m, *src);
87 		}
88 
89 
90 		// transfrom func generator
91 
92 		template<int channel, int mode_r, int mode_g, int mode_b, int mode_a, int mode_o>
get_transform_func_crgbao()93 		static ColorMatrix::transform_func_ptr INRETRNAL_FUNC get_transform_func_crgbao()
94 			{ return transform<channel, mode_r, mode_g, mode_b, mode_a, mode_o>; }
95 
96 		template<int channel, int mode_r, int mode_g, int mode_b, int mode_a>
get_transform_func_crgba(const ColorMatrix & m)97 		static ColorMatrix::transform_func_ptr INRETRNAL_FUNC get_transform_func_crgba(const ColorMatrix &m)
98 		{
99 			return approximate_equal_lp(m[4][channel], ColorMatrix::value_type( 0.0)) ? get_transform_func_crgbao<channel, mode_r, mode_g, mode_b, mode_a,  0>()
100 				 : approximate_equal_lp(m[4][channel], ColorMatrix::value_type( 1.0)) ? get_transform_func_crgbao<channel, mode_r, mode_g, mode_b, mode_a,  1>()
101 				 : approximate_equal_lp(m[4][channel], ColorMatrix::value_type(-1.0)) ? get_transform_func_crgbao<channel, mode_r, mode_g, mode_b, mode_a, -1>()
102 				                                                                      : get_transform_func_crgbao<channel, mode_r, mode_g, mode_b, mode_a,  2>();
103 		}
104 
105 		template<int channel, int mode_r, int mode_g, int mode_b>
get_transform_func_crgb(const ColorMatrix & m)106 		static ColorMatrix::transform_func_ptr INRETRNAL_FUNC get_transform_func_crgb(const ColorMatrix &m)
107 		{
108 			return approximate_equal_lp(m[3][channel], ColorMatrix::value_type( 0.0)) ? get_transform_func_crgba<channel, mode_r, mode_g, mode_b,  0>(m)
109 				 : approximate_equal_lp(m[3][channel], ColorMatrix::value_type( 1.0)) ? get_transform_func_crgba<channel, mode_r, mode_g, mode_b,  1>(m)
110 				 : approximate_equal_lp(m[3][channel], ColorMatrix::value_type(-1.0)) ? get_transform_func_crgba<channel, mode_r, mode_g, mode_b, -1>(m)
111 				                                                                      : get_transform_func_crgba<channel, mode_r, mode_g, mode_b,  2>(m);
112 		}
113 
114 		template<int channel, int mode_r, int mode_g>
get_transform_func_crg(const ColorMatrix & m)115 		static ColorMatrix::transform_func_ptr INRETRNAL_FUNC get_transform_func_crg(const ColorMatrix &m)
116 		{
117 			return approximate_equal_lp(m[2][channel], ColorMatrix::value_type( 0.0)) ? get_transform_func_crgb<channel, mode_r, mode_g,  0>(m)
118 				 : approximate_equal_lp(m[2][channel], ColorMatrix::value_type( 1.0)) ? get_transform_func_crgb<channel, mode_r, mode_g,  1>(m)
119 				 : approximate_equal_lp(m[2][channel], ColorMatrix::value_type(-1.0)) ? get_transform_func_crgb<channel, mode_r, mode_g, -1>(m)
120 				                                                                      : get_transform_func_crgb<channel, mode_r, mode_g,  2>(m);
121 		}
122 
123 		template<int channel, int mode_r>
get_transform_func_cr(const ColorMatrix & m)124 		static ColorMatrix::transform_func_ptr INRETRNAL_FUNC get_transform_func_cr(const ColorMatrix &m)
125 		{
126 			return approximate_equal_lp(m[1][channel], ColorMatrix::value_type( 0.0)) ? get_transform_func_crg<channel, mode_r,  0>(m)
127 				 : approximate_equal_lp(m[1][channel], ColorMatrix::value_type( 1.0)) ? get_transform_func_crg<channel, mode_r,  1>(m)
128 				 : approximate_equal_lp(m[1][channel], ColorMatrix::value_type(-1.0)) ? get_transform_func_crg<channel, mode_r, -1>(m)
129 				                                                                      : get_transform_func_crg<channel, mode_r,  2>(m);
130 		}
131 
132 		template<int channel>
get_transform_func_c(const ColorMatrix & m)133 		static ColorMatrix::transform_func_ptr INRETRNAL_FUNC get_transform_func_c(const ColorMatrix &m)
134 		{
135 			return approximate_equal_lp(m[0][channel], ColorMatrix::value_type( 0.0)) ? get_transform_func_cr<channel,  0>(m)
136 				 : approximate_equal_lp(m[0][channel], ColorMatrix::value_type( 1.0)) ? get_transform_func_cr<channel,  1>(m)
137 				 : approximate_equal_lp(m[0][channel], ColorMatrix::value_type(-1.0)) ? get_transform_func_cr<channel, -1>(m)
138 				                                                                      : get_transform_func_cr<channel,  2>(m);
139 		}
140 
141 
142 		// batch func generator
143 
144 		template<int channel, int mode_r, int mode_g, int mode_b, int mode_a, int mode_o>
get_batch_func_crgbao()145 		static ColorMatrix::batch_func_ptr INRETRNAL_FUNC get_batch_func_crgbao()
146 			{ return batch_transform<channel, mode_r, mode_g, mode_b, mode_a, mode_o>; }
147 
148 		template<int channel, int mode_r, int mode_g, int mode_b, int mode_a>
get_batch_func_crgba(const ColorMatrix & m)149 		static ColorMatrix::batch_func_ptr INRETRNAL_FUNC get_batch_func_crgba(const ColorMatrix &m)
150 		{
151 			return approximate_equal_lp(m[4][channel], ColorMatrix::value_type( 0.0)) ? get_batch_func_crgbao<channel, mode_r, mode_g, mode_b, mode_a,  0>()
152 				 : approximate_equal_lp(m[4][channel], ColorMatrix::value_type( 1.0)) ? get_batch_func_crgbao<channel, mode_r, mode_g, mode_b, mode_a,  1>()
153 				 : approximate_equal_lp(m[4][channel], ColorMatrix::value_type(-1.0)) ? get_batch_func_crgbao<channel, mode_r, mode_g, mode_b, mode_a, -1>()
154 				                                                                      : get_batch_func_crgbao<channel, mode_r, mode_g, mode_b, mode_a,  2>();
155 		}
156 
157 		template<int channel, int mode_r, int mode_g, int mode_b>
get_batch_func_crgb(const ColorMatrix & m)158 		static ColorMatrix::batch_func_ptr INRETRNAL_FUNC get_batch_func_crgb(const ColorMatrix &m)
159 		{
160 			return approximate_equal_lp(m[3][channel], ColorMatrix::value_type( 0.0)) ? get_batch_func_crgba<channel, mode_r, mode_g, mode_b,  0>(m)
161 				 : approximate_equal_lp(m[3][channel], ColorMatrix::value_type( 1.0)) ? get_batch_func_crgba<channel, mode_r, mode_g, mode_b,  1>(m)
162 				 : approximate_equal_lp(m[3][channel], ColorMatrix::value_type(-1.0)) ? get_batch_func_crgba<channel, mode_r, mode_g, mode_b, -1>(m)
163 				                                                                      : get_batch_func_crgba<channel, mode_r, mode_g, mode_b,  2>(m);
164 		}
165 
166 		template<int channel, int mode_r, int mode_g>
get_batch_func_crg(const ColorMatrix & m)167 		static ColorMatrix::batch_func_ptr INRETRNAL_FUNC get_batch_func_crg(const ColorMatrix &m)
168 		{
169 			return approximate_equal_lp(m[2][channel], ColorMatrix::value_type( 0.0)) ? get_batch_func_crgb<channel, mode_r, mode_g,  0>(m)
170 				 : approximate_equal_lp(m[2][channel], ColorMatrix::value_type( 1.0)) ? get_batch_func_crgb<channel, mode_r, mode_g,  1>(m)
171 				 : approximate_equal_lp(m[2][channel], ColorMatrix::value_type(-1.0)) ? get_batch_func_crgb<channel, mode_r, mode_g, -1>(m)
172 				                                                                      : get_batch_func_crgb<channel, mode_r, mode_g,  2>(m);
173 		}
174 
175 		template<int channel, int mode_r>
get_batch_func_cr(const ColorMatrix & m)176 		static ColorMatrix::batch_func_ptr INRETRNAL_FUNC get_batch_func_cr(const ColorMatrix &m)
177 		{
178 			return approximate_equal_lp(m[1][channel], ColorMatrix::value_type( 0.0)) ? get_batch_func_crg<channel, mode_r,  0>(m)
179 				 : approximate_equal_lp(m[1][channel], ColorMatrix::value_type( 1.0)) ? get_batch_func_crg<channel, mode_r,  1>(m)
180 				 : approximate_equal_lp(m[1][channel], ColorMatrix::value_type(-1.0)) ? get_batch_func_crg<channel, mode_r, -1>(m)
181 				                                                                      : get_batch_func_crg<channel, mode_r,  2>(m);
182 		}
183 
184 		template<int channel>
get_batch_func_c(const ColorMatrix & m)185 		static ColorMatrix::batch_func_ptr INRETRNAL_FUNC get_batch_func_c(const ColorMatrix &m)
186 		{
187 			return approximate_equal_lp(m[0][channel], ColorMatrix::value_type( 0.0)) ? get_batch_func_cr<channel,  0>(m)
188 				 : approximate_equal_lp(m[0][channel], ColorMatrix::value_type( 1.0)) ? get_batch_func_cr<channel,  1>(m)
189 				 : approximate_equal_lp(m[0][channel], ColorMatrix::value_type(-1.0)) ? get_batch_func_cr<channel, -1>(m)
190 				                                                                      : get_batch_func_cr<channel,  2>(m);
191 		}
192 	};
193 }
194 
195 // BatchProcessor
196 
BatchProcessor(const ColorMatrix & matrix)197 ColorMatrix::BatchProcessor::BatchProcessor(const ColorMatrix &matrix):
198 	matrix(matrix),
199 	zero_all(matrix.is_zero()),
200 	zero_r(matrix.is_zero(0)),
201 	zero_g(matrix.is_zero(1)),
202 	zero_b(matrix.is_zero(2)),
203 	zero_a(matrix.is_zero(3)),
204 	constant_value(matrix.get_constant()),
205 	constant_all(matrix.is_constant()),
206 	constant_r(matrix.is_constant(0)),
207 	constant_g(matrix.is_constant(1)),
208 	constant_b(matrix.is_constant(2)),
209 	constant_a(matrix.is_constant(3)),
210 	copy_all(matrix.is_copy()),
211 	copy_r(matrix.is_copy(0)),
212 	copy_g(matrix.is_copy(1)),
213 	copy_b(matrix.is_copy(2)),
214 	copy_a(matrix.is_copy(3)),
215 	affects_transparent(matrix.is_affects_transparent()),
216 	transform_func_r(Internal::get_transform_func_c<0>(matrix)),
217 	transform_func_g(Internal::get_transform_func_c<1>(matrix)),
218 	transform_func_b(Internal::get_transform_func_c<2>(matrix)),
219 	transform_func_a(Internal::get_transform_func_c<3>(matrix)),
220 	batch_func_r(Internal::get_batch_func_c<0>(matrix)),
221 	batch_func_g(Internal::get_batch_func_c<1>(matrix)),
222 	batch_func_b(Internal::get_batch_func_c<2>(matrix)),
223 	batch_func_a(Internal::get_batch_func_c<3>(matrix))
224 { }
225 
226 
227 void
process(Color * dest,int dest_stride,const Color * src,int src_stride,int width,int height) const228 ColorMatrix::BatchProcessor::process(Color *dest, int dest_stride, const Color *src, int src_stride, int width, int height) const
229 {
230 	if (width <= 0 || height <= 0) return;
231 	int dest_dr = dest_stride - width;
232 	int src_dr = src_stride - width;
233 	Color *dest_end = dest + dest_stride*height;
234 	#ifndef NDEBUG
235 	const Color *src_end = src + src_stride*height;
236 	#endif
237 
238 	if (zero_all)
239 	{
240 		if (dest_dr)
241 			for(; dest != dest_end; dest += dest_stride)
242 				memset(dest, 0, sizeof(*dest)*width);
243 		else
244 			memset(dest, 0, sizeof(*dest)*width*height);
245 	}
246 	else
247 	if (copy_all)
248 	{
249 		if (dest == src)
250 		{
251 			assert(src_stride == dest_stride);
252 		}
253 		else
254 		{
255 			assert(src_end <= dest || dest_end <= src);
256 			if (dest_dr || src_dr)
257 				for(; dest != dest_end; dest += dest_stride, src += src_stride)
258 					memcpy(dest, src, sizeof(*dest)*width);
259 			else
260 				memcpy(dest, src, sizeof(*dest)*width*height);
261 		}
262 	}
263 	else
264 	if (constant_all)
265 	{
266 		if (dest_dr)
267 			for(; dest != dest_end; dest += dest_dr)
268 				for(Color *dest_row_end = dest + width; dest < dest_row_end; ++dest)
269 					*dest = constant_value;
270 		else
271 			for(; dest != dest_end; ++dest)
272 				*dest = constant_value;
273 	}
274 	else
275 	if (dest != src)
276 	{
277 		assert(src_end <= dest || dest_end <= src);
278 		for(; dest != dest_end; dest += dest_dr, src += src_dr)
279 		{
280 			const Color *src_end = src + width;
281 			batch_func_r(matrix, (value_type*)dest + 0, src, src_end);
282 			batch_func_g(matrix, (value_type*)dest + 1, src, src_end);
283 			batch_func_b(matrix, (value_type*)dest + 2, src, src_end);
284 			batch_func_a(matrix, (value_type*)dest + 3, src, src_end);
285 		}
286 	}
287 	else
288 	{
289 		assert(src_stride == dest_stride);
290 		Color c;
291 		for(; dest != dest_end; dest += dest_dr)
292 			for(Color *dest_row_end = dest + width; dest < dest_row_end; ++dest)
293 			{
294 				c.set_r(transform_func_r(matrix, *dest));
295 				c.set_g(transform_func_g(matrix, *dest));
296 				c.set_b(transform_func_b(matrix, *dest));
297 				c.set_a(transform_func_a(matrix, *dest));
298 				*dest = c;
299 			}
300 	}
301 }
302 
303 
304 // ColorMatrix
305 
306 bool
is_constant(int channel) const307 ColorMatrix::is_constant(int channel) const
308 {
309 	return approximate_equal_lp(m[0][channel], value_type(0.0))
310 		&& approximate_equal_lp(m[1][channel], value_type(0.0))
311 		&& approximate_equal_lp(m[2][channel], value_type(0.0))
312 		&& approximate_equal_lp(m[3][channel], value_type(0.0));
313 }
314 
315 bool
is_constant() const316 ColorMatrix::is_constant() const
317 {
318 	return is_constant(0)
319 		&& is_constant(1)
320 		&& is_constant(2)
321 		&& is_constant(3);
322 }
323 
324 bool
is_zero(int channel) const325 ColorMatrix::is_zero(int channel) const
326 {
327 	return is_constant(channel)
328 	    && approximate_equal_lp(m[4][channel], value_type(0.0));
329 }
330 
331 bool
is_zero() const332 ColorMatrix::is_zero() const
333 {
334 	return is_zero(0)
335 		&& is_zero(1)
336 		&& is_zero(2)
337 		&& is_zero(3);
338 }
339 
340 bool
is_copy(int channel) const341 ColorMatrix::is_copy(int channel) const
342 {
343 	return approximate_equal_lp(m[0][channel], value_type(channel == 0 ? 1.0 : 0.0))
344 		&& approximate_equal_lp(m[1][channel], value_type(channel == 1 ? 1.0 : 0.0))
345 		&& approximate_equal_lp(m[2][channel], value_type(channel == 2 ? 1.0 : 0.0))
346 		&& approximate_equal_lp(m[3][channel], value_type(channel == 3 ? 1.0 : 0.0))
347 		&& approximate_equal_lp(m[4][channel], value_type(channel == 4 ? 1.0 : 0.0));
348 }
349 
350 bool
is_copy() const351 ColorMatrix::is_copy() const
352 {
353 	return is_copy(0)
354 		&& is_copy(1)
355 		&& is_copy(2)
356 		&& is_copy(3);
357 }
358 
is_affects_transparent() const359 bool ColorMatrix::is_affects_transparent() const
360 {
361 	return approximate_equal_lp(m03, value_type(0.0))
362 		|| approximate_equal_lp(m13, value_type(0.0))
363 		|| approximate_equal_lp(m23, value_type(0.0))
364 		|| approximate_equal_lp(m43, value_type(0.0));
365 }
366 
367 ColorMatrix&
set_scale(value_type r,value_type g,value_type b,value_type a)368 ColorMatrix::set_scale(value_type r, value_type g, value_type b, value_type a)
369 {
370 	m00=r;   m01=0.0; m02=0.0; m03=0.0; m04=0.0;
371 	m10=0.0; m11=g;   m12=0.0; m13=0.0; m14=0.0;
372 	m20=0.0; m21=0.0; m22=b;   m23=0.0; m24=0.0;
373 	m30=0.0; m31=0.0; m32=0.0; m33=a;   m34=0.0;
374 	m40=0.0; m41=0.0; m42=0.0; m43=0.0; m44=1.0;
375 	return *this;
376 }
377 
378 ColorMatrix&
set_translate(value_type r,value_type g,value_type b,value_type a)379 ColorMatrix::set_translate(value_type r, value_type g, value_type b, value_type a)
380 {
381 	m00=1.0; m01=0.0; m02=0.0; m03=0.0; m04=0.0;
382 	m10=0.0; m11=1.0; m12=0.0; m13=0.0; m14=0.0;
383 	m20=0.0; m21=0.0; m22=1.0; m23=0.0; m24=0.0;
384 	m30=0.0; m31=0.0; m32=0.0; m33=1.0; m34=0.0;
385 	m40=r;   m41=g;   m42=b;   m43=a;   m44=1.0;
386 	return *this;
387 }
388 
389 ColorMatrix&
set_encode_yuv()390 ColorMatrix::set_encode_yuv()
391 {
392 	m00 = 0.299; m01 = -0.168736; m02 =  0.5;      m03=0.0; m04=0.0;
393 	m10 = 0.587; m11 = -0.331264; m12 = -0.418688; m13=0.0; m14=0.0;
394 	m20 = 0.114; m21 =  0.5;      m22 = -0.081312; m23=0.0; m24=0.0;
395 	m30 = 0.0;   m31 =  0.0;      m32 =  0.0;      m33=1.0; m34=0.0;
396 	m40 = 0.0;   m41 =  0.0;      m42 =  0.0;      m43=0.0; m44=1.0;
397 	return *this;
398 }
399 
400 ColorMatrix&
set_decode_yuv()401 ColorMatrix::set_decode_yuv()
402 {
403 	m00 = 1.0;   m01 =  1.0;      m02 = 1.0;   m03=0.0; m04=0.0;
404 	m10 = 0.0;   m11 = -0.344136; m12 = 1.772; m13=0.0; m14=0.0;
405 	m20 = 1.402; m21 = -0.714136; m22 = 0.0;   m23=0.0; m24=0.0;
406 	m30 = 0.0;   m31 =  0.0;      m32 = 0.0;   m33=1.0; m34=0.0;
407 	m40 = 0.0;   m41 =  0.0;      m42 = 0.0;   m43=0.0; m44=1.0;
408 	return *this;
409 }
410 
411 ColorMatrix&
set_rotate_uv(const Angle & a)412 ColorMatrix::set_rotate_uv(const Angle &a)
413 {
414 	value_type c(Angle::cos(a).get());
415 	value_type s(Angle::sin(a).get());
416 	m00 = 1.0; m01 =  0.0; m02 = 0.0; m03=0.0; m04=0.0;
417 	m10 = 0.0; m11 =  c;   m12 = s;   m13=0.0; m14=0.0;
418 	m20 = 0.0; m21 = -s;   m22 = c;   m23=0.0; m24=0.0;
419 	m30 = 0.0; m31 =  0.0; m32 = 0.0; m33=1.0; m34=0.0;
420 	m40 = 0.0; m41 =  0.0; m42 = 0.0; m43=0.0; m44=1.0;
421 	return *this;
422 }
423 
424 ColorMatrix&
set_constant(const Color & c)425 ColorMatrix::set_constant(const Color &c)
426 {
427 	m00 = 0.0;       m01 = 0.0;       m02 = 0.0;       m03 = 0.0;       m04 = 0.0;
428 	m10 = 0.0;       m11 = 0.0;       m12 = 0.0;       m13 = 0.0;       m14 = 0.0;
429 	m20 = 0.0;       m21 = 0.0;       m22 = 0.0;       m23 = 0.0;       m24 = 0.0;
430 	m30 = 0.0;       m31 = 0.0;       m32 = 0.0;       m33 = 0.0;       m34 = 0.0;
431 	m40 = c.get_r(); m41 = c.get_g(); m42 = c.get_b(); m43 = c.get_a(); m44 = 1.0;
432 	return *this;
433 }
434 
435 ColorMatrix&
set_replace_color(const Color & c)436 ColorMatrix::set_replace_color(const Color &c)
437 {
438 	m00 = 0.0;       m01 = 0.0;       m02 = 0.0;       m03 = 0.0; m04 = 0.0;
439 	m10 = 0.0;       m11 = 0.0;       m12 = 0.0;       m13 = 0.0; m14 = 0.0;
440 	m20 = 0.0;       m21 = 0.0;       m22 = 0.0;       m23 = 0.0; m24 = 0.0;
441 	m30 = 0.0;       m31 = 0.0;       m32 = 0.0;       m33 = 1.0; m34 = 0.0;
442 	m40 = c.get_r(); m41 = c.get_g(); m42 = c.get_b(); m43 = 0.0; m44 = 1.0;
443 	return *this;
444 }
445 
446 ColorMatrix&
set_replace_alpha(value_type x)447 ColorMatrix::set_replace_alpha(value_type x)
448 {
449 	m00 = 1.0; m01 = 0.0; m02 = 0.0; m03 = 0.0; m04 = 0.0;
450 	m10 = 0.0; m11 = 1.0; m12 = 0.0; m13 = 0.0; m14 = 0.0;
451 	m20 = 0.0; m21 = 0.0; m22 = 1.0; m23 = 0.0; m24 = 0.0;
452 	m30 = 0.0; m31 = 0.0; m32 = 0.0; m33 = 0.0; m34 = 0.0;
453 	m40 = 0.0; m41 = 0.0; m42 = 0.0; m43 = x;   m44 = 1.0;
454 	return *this;
455 }
456 
457 ColorMatrix&
set_brightness(value_type x)458 ColorMatrix::set_brightness(value_type x)
459 	{ return set_translate(x, x, x); }
460 
461 ColorMatrix&
set_contrast(value_type x)462 ColorMatrix::set_contrast(value_type x)
463 {
464 	set_translate(-0.5, -0.5, -0.5);
465 	*this *= ColorMatrix().set_scale(x, x, x);
466 	*this *= ColorMatrix().set_translate(0.5, 0.5, 0.5);
467 	return *this;
468 }
469 
470 ColorMatrix&
set_exposure(value_type x)471 ColorMatrix::set_exposure(value_type x)
472 	{ return set_scale_rgb(exp(x)); }
473 
474 ColorMatrix&
set_hue_saturation(const Angle & hue,value_type saturation)475 ColorMatrix::set_hue_saturation(const Angle &hue, value_type saturation)
476 {
477 	set_encode_yuv();
478 	*this *= ColorMatrix().set_rotate_uv(hue);
479 	*this *= ColorMatrix().set_scale(1.0, saturation, saturation);
480 	*this *= ColorMatrix().set_decode_yuv();
481 	return *this;
482 }
483 
484 ColorMatrix&
set_invert_color()485 ColorMatrix::set_invert_color()
486 {
487 	m00 = -1.0; m01 =  0.0; m02 =  0.0; m03 = 0.0; m04 = 0.0;
488 	m10 =  0.0; m11 = -1.0; m12 =  0.0; m13 = 0.0; m14 = 0.0;
489 	m20 =  0.0; m21 =  0.0; m22 = -1.0; m23 = 0.0; m24 = 0.0;
490 	m30 =  0.0; m31 =  0.0; m32 =  0.0; m33 = 1.0; m34 = 0.0;
491 	m40 =  1.0; m41 =  1.0; m42 =  1.0; m43 = 0.0; m44 = 1.0;
492 	return *this;
493 }
494 
495 ColorMatrix&
set_invert_alpha()496 ColorMatrix::set_invert_alpha()
497 {
498 	m00 = 1.0; m01 = 0.0; m02 = 0.0; m03 =  0.0; m04 = 0.0;
499 	m10 = 0.0; m11 = 1.0; m12 = 0.0; m13 =  0.0; m14 = 0.0;
500 	m20 = 0.0; m21 = 0.0; m22 = 1.0; m23 =  0.0; m24 = 0.0;
501 	m30 = 0.0; m31 = 0.0; m32 = 0.0; m33 = -1.0; m34 = 0.0;
502 	m40 = 0.0; m41 = 0.0; m42 = 0.0; m43 =  1.0; m44 = 1.0;
503 	return *this;
504 }
505 
506 Color
get_transformed(Color color) const507 ColorMatrix::get_transformed(Color color) const
508 {
509 	Color out;
510 	out.set_r( color.get_r()*m00 + color.get_g()*m10  + color.get_b()*m20 + color.get_a()*m30 + m40 );
511 	out.set_g( color.get_r()*m01 + color.get_g()*m11  + color.get_b()*m21 + color.get_a()*m31 + m41 );
512 	out.set_b( color.get_r()*m02 + color.get_g()*m12  + color.get_b()*m22 + color.get_a()*m32 + m42 );
513 	out.set_a( color.get_r()*m03 + color.get_g()*m13  + color.get_b()*m23 + color.get_a()*m33 + m43 );
514 	return out;
515 }
516 
517 bool
operator ==(const ColorMatrix & rhs) const518 ColorMatrix::operator==(const ColorMatrix &rhs) const
519 {
520 	for(int i = 0; i < 25; ++i)
521 		if (!approximate_equal_lp(c[i], rhs.c[i]))
522 			return false;
523 	return true;
524 }
525 
526 ColorMatrix
operator *=(const ColorMatrix & rhs)527 ColorMatrix::operator*=(const ColorMatrix &rhs)
528 {
529 	value_type r, g, b, a, w;
530 
531 	r = m00; g = m01; b = m02; a = m03; w = m04;
532 	m00 = r*rhs.m00 + g*rhs.m10 + b*rhs.m20 + a*rhs.m30 + w*rhs.m40;
533 	m01 = r*rhs.m01 + g*rhs.m11 + b*rhs.m21 + a*rhs.m31 + w*rhs.m41;
534 	m02 = r*rhs.m02 + g*rhs.m12 + b*rhs.m22 + a*rhs.m32 + w*rhs.m42;
535 	m03 = r*rhs.m03 + g*rhs.m13 + b*rhs.m23 + a*rhs.m33 + w*rhs.m43;
536 	m04 = r*rhs.m04 + g*rhs.m14 + b*rhs.m24 + a*rhs.m34 + w*rhs.m44;
537 
538 	r = m10; g = m11; b = m12; a = m13; w = m14;
539 	m10 = r*rhs.m00 + g*rhs.m10 + b*rhs.m20 + a*rhs.m30 + w*rhs.m40;
540 	m11 = r*rhs.m01 + g*rhs.m11 + b*rhs.m21 + a*rhs.m31 + w*rhs.m41;
541 	m12 = r*rhs.m02 + g*rhs.m12 + b*rhs.m22 + a*rhs.m32 + w*rhs.m42;
542 	m13 = r*rhs.m03 + g*rhs.m13 + b*rhs.m23 + a*rhs.m33 + w*rhs.m43;
543 	m14 = r*rhs.m04 + g*rhs.m14 + b*rhs.m24 + a*rhs.m34 + w*rhs.m44;
544 
545 	r = m20; g = m21; b = m22; a = m23; w = m24;
546 	m20 = r*rhs.m00 + g*rhs.m10 + b*rhs.m20 + a*rhs.m30 + w*rhs.m40;
547 	m21 = r*rhs.m01 + g*rhs.m11 + b*rhs.m21 + a*rhs.m31 + w*rhs.m41;
548 	m22 = r*rhs.m02 + g*rhs.m12 + b*rhs.m22 + a*rhs.m32 + w*rhs.m42;
549 	m23 = r*rhs.m03 + g*rhs.m13 + b*rhs.m23 + a*rhs.m33 + w*rhs.m43;
550 	m24 = r*rhs.m04 + g*rhs.m14 + b*rhs.m24 + a*rhs.m34 + w*rhs.m44;
551 
552 	r = m30; g = m31; b = m32; a = m33; w = m34;
553 	m30 = r*rhs.m00 + g*rhs.m10 + b*rhs.m20 + a*rhs.m30 + w*rhs.m40;
554 	m31 = r*rhs.m01 + g*rhs.m11 + b*rhs.m21 + a*rhs.m31 + w*rhs.m41;
555 	m32 = r*rhs.m02 + g*rhs.m12 + b*rhs.m22 + a*rhs.m32 + w*rhs.m42;
556 	m33 = r*rhs.m03 + g*rhs.m13 + b*rhs.m23 + a*rhs.m33 + w*rhs.m43;
557 	m34 = r*rhs.m04 + g*rhs.m14 + b*rhs.m24 + a*rhs.m34 + w*rhs.m44;
558 
559 	r = m40; g = m41; b = m42; a = m43; w = m44;
560 	m40 = r*rhs.m00 + g*rhs.m10 + b*rhs.m20 + a*rhs.m30 + w*rhs.m40;
561 	m41 = r*rhs.m01 + g*rhs.m11 + b*rhs.m21 + a*rhs.m31 + w*rhs.m41;
562 	m42 = r*rhs.m02 + g*rhs.m12 + b*rhs.m22 + a*rhs.m32 + w*rhs.m42;
563 	m43 = r*rhs.m03 + g*rhs.m13 + b*rhs.m23 + a*rhs.m33 + w*rhs.m43;
564 	m44 = r*rhs.m04 + g*rhs.m14 + b*rhs.m24 + a*rhs.m34 + w*rhs.m44;
565 
566 	return *this;
567 }
568 
569 ColorMatrix
operator *=(const value_type & rhs)570 ColorMatrix::operator*=(const value_type &rhs)
571 {
572 	m00 *= rhs; m01 *= rhs; m02 *= rhs; m03 *= rhs; m04 *= rhs;
573 	m10 *= rhs; m11 *= rhs; m12 *= rhs; m13 *= rhs; m14 *= rhs;
574 	m20 *= rhs; m21 *= rhs; m22 *= rhs; m23 *= rhs; m24 *= rhs;
575 	m30 *= rhs; m31 *= rhs; m32 *= rhs; m33 *= rhs; m34 *= rhs;
576 	m40 *= rhs; m41 *= rhs; m42 *= rhs; m43 *= rhs; m44 *= rhs;
577 	return *this;
578 }
579 
580 String
get_string(int spaces,String before,String after) const581 ColorMatrix::get_string(int spaces, String before, String after)const
582 {
583 	return etl::strprintf(
584 		"%*s [%7.2f %7.2f %7.2f %7.2f %7.2f] %s\n"
585 		"%*s [%7.2f %7.2f %7.2f %7.2f %7.2f] %s\n"
586 		"%*s [%7.2f %7.2f %7.2f %7.2f %7.2f] %s\n"
587 		"%*s [%7.2f %7.2f %7.2f %7.2f %7.2f] %s\n"
588 		"%*s [%7.2f %7.2f %7.2f %7.2f %7.2f] %s\n",
589 		spaces, before.c_str(), m00, m01, m02, m03, m04, after.c_str(),
590 		spaces, before.c_str(), m10, m11, m12, m13, m14, after.c_str(),
591 		spaces, before.c_str(), m20, m21, m22, m23, m24, after.c_str(),
592 		spaces, before.c_str(), m30, m31, m32, m33, m34, after.c_str(),
593 		spaces, before.c_str(), m40, m41, m42, m43, m44, after.c_str() );
594 }
595