1 /* === S Y N F I G ========================================================= */
2 /*!	\file synfig/rendering/software/function/blurtemplates.h
3 **	\brief Blur Templates Header
4 **
5 **	$Id$
6 **
7 **	\legal
8 **	......... ... 2015 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 /* === S T A R T =========================================================== */
24 
25 #ifndef __SYNFIG_RENDERING_SOFTWARE_BLURTEMPLATES_H
26 #define __SYNFIG_RENDERING_SOFTWARE_BLURTEMPLATES_H
27 
28 /* === H E A D E R S ======================================================= */
29 
30 #include <cassert>
31 
32 #include <algorithm>
33 #include <deque>
34 
35 #include "array.h"
36 
37 #include <synfig/angle.h>
38 #include <synfig/surface.h>
39 
40 /* === M A C R O S ========================================================= */
41 
42 /* === T Y P E D E F S ===================================================== */
43 
44 /* === C L A S S E S & S T R U C T S ======================================= */
45 
46 namespace synfig
47 {
48 namespace rendering
49 {
50 namespace software
51 {
52 
53 class BlurTemplates
54 {
55 public:
56 	template<typename T>
amplifier_box()57 	static T amplifier_box() { return T(0.25)*sqrt(T(PI)); }
58 	template<typename T>
amplifier_cross()59 	static T amplifier_cross() { return T(1.0); }
60 	template<typename T>
amplifier_disk()61 	static T amplifier_disk() { return T(0.5); }
62 	template<typename T>
amplifier_gauss()63 	static T amplifier_gauss() { return T(3.0)/T(8.0); }
64 	template<typename T>
amplifier_fastgauss()65 	static T amplifier_fastgauss() { return amplifier_gauss<T>(); }
66 
67 
68 	template<typename T>
operatorAbs69 	struct Abs { T operator() (const T &x) { return abs(x); } };
70 
71 	template<typename T>
gauss(const T & x,const T & r)72 	static T gauss(const T &x, const T &r)
73 	{
74 		static const T precision(1e-10);
75 		static const T k( T(1.0)/sqrt(T(2.0)*PI) );
76 		if (fabs(r) < precision)
77 			return fabs(x) < precision ? T(1.0) : T(0.0);
78 		return k*exp(T(-0.5)*x*x/(r*r))/r;
79 	}
80 
81 	template<typename T>
ungauss(const T & x)82 	static T ungauss(const T &x)
83 	{
84 		static const T k( T(1.0)/sqrt(T(2.0)*PI) );
85 		return sqrt(-T(2.0)*log(x/k));
86 	}
87 
88 	template<typename T>
fill_pattern_box(const Array<T,1> & x,const T & size)89 	static void fill_pattern_box(const Array<T, 1> &x, const T &size)
90 	{
91 		typedef Array<T, 1> A;
92 
93 		const T precision(1e-10);
94 		T s(fabs(size));
95 		int sizei = 1 + (int)floor(s + precision);
96 
97 		/// assume that pattern already contains zeros
98 		for(typename A::Iterator i(x, 0, std::min(sizei, x.count)); i; ++i)
99 			*i = T(1.0);
100 
101 		// antialiasing
102 		if (sizei < x.count)
103 			x[sizei] = T(sizei-1) - s;
104 	}
105 
106 	template<typename T>
fill_pattern_gauss(const Array<T,1> & x,T size)107 	static void fill_pattern_gauss(const Array<T, 1> &x, T size)
108 	{
109 		if (x.count <= 0) return;
110 		T s = T(0.5)+fabs(size);
111 		x[0] = gauss(T(0.0), s);
112 		for(int i = 0; i < x.count; ++i)
113 			x[i] = gauss(T(i), s);
114 	}
115 
116 	template<typename T>
fill_pattern_2d_disk(const Array<T,2> & x,const T & size_x,const T & size_y)117 	static void fill_pattern_2d_disk(const Array<T, 2> &x, const T &size_x, const T &size_y)
118 	{
119 		const T precision(1e-10);
120 
121 		T sx(T(0.5) + fabs(size_x));
122 		T sy(T(0.5) + fabs(size_y));
123 
124 		int sizec = std::min(x.get_count(1)-1, (int)ceil(sx - precision));
125 		int sizer = std::min(x.get_count(0)-1, (int)ceil(sy - precision));
126 
127 		// draw sector
128 		T k0x(sx - T(0.5)), k0y(sy - T(0.5));
129 		T k1x(sx + T(0.5)), k1y(sy + T(0.5));
130 		T kk0x( k0x > precision ? T(1.0)/k0x : T(0.0) );
131 		T kk0y( k0y > precision ? T(1.0)/k0y : T(0.0) );
132 		T kk1x( k1x > precision ? T(1.0)/k1x : T(0.0) );
133 		T kk1y( k1y > precision ? T(1.0)/k1y : T(0.0) );
134 		T pp0x(0.0), pp0y(0.0);
135 		T pp1x(0.0), pp1y(0.0);
136 		for(int r = 0; r <= sizer; ++r)
137 		{
138 			for(int c = 0; c <= sizec; ++c)
139 			{
140 				T &v = x[r][c];
141 				T r1_squared = pp1x*pp1x + pp1y*pp1y;
142 				/// assume that pattern already contains zeros
143 				if (r1_squared < T(1.0))
144 				{
145 					T r0_squared = pp0x*pp0x + pp0y*pp0y;
146 					if (r0_squared <= T(1.0))
147 					{
148 						v = T(1.0);
149 					}
150 					else
151 					{
152 						T rr0 = sqrt(r0_squared);
153 						T rr1 = sqrt(r1_squared);
154 						T drr = rr0 - rr1;
155 						v = drr > precision ? rr0*(1.0 - rr1)/drr : T(0.0);
156 					}
157 				}
158 				pp0x += kk0x;
159 				pp1x += kk1x;
160 			}
161 			pp0x = T(0.0); pp0y += kk0y;
162 			pp1x = T(0.0); pp1y += kk1y;
163 		}
164 
165 		/// assume that pattern already contains zeros
166 		/// and don't touch other cells
167 	}
168 
169 	template<typename T>
mirror_pattern(const Array<T,1> & x)170 	static void mirror_pattern(const Array<T, 1> &x)
171 	{
172 		typedef Array<T, 1> A;
173 		int count = (x.count-1)/2;
174 		typename A::ReverseIterator j(x);
175 		for(typename A::Iterator i(x, 1, 1 + count); i; ++i, ++j)
176 			*j = *i;
177 	}
178 
179 	template<typename T>
mirror_pattern_2d(const Array<T,2> & x)180 	static void mirror_pattern_2d(const Array<T, 2> &x)
181 	{
182 		typedef Array<T, 2> A;
183 		int count = (x.count-1)/2;
184 		for(typename A::Iterator i(x, 1, 1 + count); i; ++i)
185 			mirror_pattern(*i);
186 		for(typename A::Iterator i(x.reorder(1, 0)); i; ++i)
187 			mirror_pattern(*i);
188 	}
189 
190 	template<typename T>
normalize_full_pattern(const Array<T,1> & x)191 	static void normalize_full_pattern(const Array<T, 1> &x)
192 	{
193 		const T precision(1e-10);
194 		typedef Array<T, 1> A;
195 		T sum = T(0.0);
196 		for(typename A::Iterator i(x); i; ++i)
197 			sum += *i;
198 		if (sum > precision)
199 		{
200 			T k = T(1.0)/sum;
201 			for(typename A::Iterator i(x); i; ++i)
202 				*i *= k;
203 		}
204 	}
205 
206 	template<typename T>
normalize_half_pattern(const Array<T,1> & x)207 	static void normalize_half_pattern(const Array<T, 1> &x)
208 	{
209 		if (x.count <= 0) return;
210 		const T precision(1e-10);
211 		typedef Array<T, 1> A;
212 		T sum = x[0];
213 		for(typename A::Iterator i(x, 1); i; ++i)
214 			sum += T(2.0)*(*i);
215 		if (sum > precision)
216 		{
217 			T k = T(1.0)/sum;
218 			for(typename A::Iterator i(x); i; ++i)
219 				*i *= k;
220 		}
221 	}
222 
223 	template<typename T>
normalize_full_pattern_2d(const Array<T,2> & x)224 	static void normalize_full_pattern_2d(const Array<T, 2> &x)
225 	{
226 		if (x.get_count(0) <= 0 || x.get_count(1) <= 0) return;
227 		const T precision(1e-10);
228 		typedef Array<T, 2> A;
229 		typedef Array<T, 1> B;
230 		T sum = T(0.0);
231 		for(typename A::Iterator i(x); i; ++i)
232 		for(typename B::Iterator j(*i); j; ++j)
233 			sum += *j;
234 		if (sum > precision)
235 		{
236 			T k = T(1.0)/sum;
237 			for(typename A::Iterator i(x); i; ++i)
238 			for(typename B::Iterator j(*i); j; ++j)
239 				*j *= k;
240 		}
241 	}
242 
243 	template<typename T>
normalize_half_pattern_2d(const Array<T,2> & x)244 	static void normalize_half_pattern_2d(const Array<T, 2> &x)
245 	{
246 		if (x.get_count(0) <= 0 || x.get_count(1) <= 0) return;
247 		const T precision(1e-10);
248 		typedef Array<T, 2> A;
249 		typedef Array<T, 1> B;
250 		T sum = x[0][0];
251 		for(typename A::Iterator i(x, 1); i; ++i)
252 			sum += T(2.0)*(*i)[0];
253 		for(typename B::Iterator j(x[0], 1); j; ++j)
254 			sum += T(2.0)*(*j);
255 		for(typename A::Iterator i(x, 1); i; ++i)
256 		for(typename B::Iterator j(*i, 1); j; ++j)
257 			sum += T(4.0)*(*j);
258 		if (sum > precision)
259 		{
260 			T k = T(1.0)/sum;
261 			for(typename A::Iterator i(x); i; ++i)
262 			for(typename B::Iterator j(*i); j; ++j)
263 				*j *= k;
264 		}
265 	}
266 
267 	template<typename T>
blur_pattern(const Array<T,1> & dst,const Array<T,1> & src,const Array<T,1> & pattern)268 	static void blur_pattern(const Array<T, 1> &dst, const Array<T, 1> &src, const Array<T, 1> &pattern)
269 	{
270 		typedef Array<T, 1> A;
271 		if (pattern.count <= 0)
272 		{
273 			dst.assign(src);
274 			return;
275 		}
276 
277 		int pattern_size = pattern.count - 1;
278 		int end = std::min(src.count, dst.count) - pattern_size;
279 
280 		int si = pattern_size;
281 		for(typename A::Iterator di(dst, pattern_size, end); di; ++di, ++si)
282 		{
283 			(*di) += src[si] * pattern[0];
284 			for(int i = 1; i <= pattern_size; ++i)
285 				(*di) += (src[si - i] + src[si + i])*pattern[i];
286 		}
287 	}
288 
289 	template<typename T>
blur_2d_pattern(const Array<T,2> & dst,const Array<T,2> & src,const Array<T,2> & pattern)290 	static void blur_2d_pattern(const Array<T, 2> &dst, const Array<T, 2> &src, const Array<T, 2> &pattern)
291 	{
292 		typedef Array<T, 2> A;
293 		typedef Array<T, 1> B;
294 		if (pattern.get_count(0) <= 0 || pattern.get_count(1) <= 0)
295 		{
296 			dst.assign(src);
297 			return;
298 		}
299 
300 		int pattern_size0 = pattern.get_count(0) - 1;
301 		int pattern_size1 = pattern.get_count(1) - 1;
302 		int end0 = std::min(src.get_count(0), dst.get_count(0)) - pattern_size0;
303 		int end1 = std::min(src.get_count(1), dst.get_count(1)) - pattern_size1;
304 
305 		int si0 = pattern_size0;
306 		int si1 = pattern_size1;
307 		for(typename A::Iterator di0(dst, pattern_size0, end0); di0; ++di0, ++si0, si1 = pattern_size1)
308 		for(typename B::Iterator di1(*di0, pattern_size1, end1); di1; ++di1, ++si1)
309 		{
310 			(*di1) += src[si0][si1]*pattern[0][0];
311 
312 			for(int i0 = 1; i0 <= pattern_size0; ++i0)
313 				(*di1) += (src[si0 - i0][si1] + src[si0 + i0][si1])*pattern[i0][0];
314 			for(int i1 = 1; i1 <= pattern_size1; ++i1)
315 				(*di1) += (src[si0][si1 - i1] + src[si0][si1 + i1])*pattern[0][i1];
316 
317 			for(int i0 = 1; i0 <= pattern_size0; ++i0)
318 			for(int i1 = 1; i1 <= pattern_size1; ++i1)
319 				(*di1) += pattern[i0][i1]*( src[si0 - i0][si1 - i1]
320 						                  + src[si0 - i0][si1 + i1]
321 										  + src[si0 + i0][si1 - i1]
322 										  + src[si0 + i0][si1 + i1] );
323 		}
324 	}
325 
326 	template<typename T>
blur_box_discrete(const Array<T,1> & x,std::deque<T> & q,const int size)327 	static void blur_box_discrete(const Array<T, 1> &x, std::deque<T> &q, const int size)
328 	{
329 		typedef Array<T, 1> A;
330 		if (size == 0) return;
331 
332 		int s = abs(size);
333 		int full_size = 1 + 2*s;
334 		if (x.count < full_size) return;
335 		T w(T(1.0)/T(full_size));
336 		T sum(0.0);
337 		q.clear();
338 		for(typename A::Iterator i(x, 0, full_size); i; ++i)
339 			{ q.push_back(*i); sum += *i; }
340 		for(typename A::Iterator i(x, full_size), j(x, s); i; ++i, ++j)
341 		{
342 			*j = w*sum;
343 			sum += *i - q.front();
344 			q.pop_front();
345 			q.push_back(*i);
346 		}
347 	}
348 
349 	template<typename T>
blur_box_discrete(const Array<T,1> & dst,const Array<const T,1> & src,const int size,const int offset)350 	static void blur_box_discrete(const Array<T, 1> &dst, const Array<const T, 1> &src, const int size, const int offset)
351 	{
352 		typedef Array<T, 1> A;
353 		typedef Array<const T, 1> B;
354 
355 		int s = abs(size);
356 		int o0 = std::max(offset - s, 0);
357 		int o1 = std::max(s - offset, 0);
358 		if (o0 >= src.count) return;
359 		if (o1 >= dst.count) return;
360 
361 		if (s == 0)
362 		{
363 			// copy
364 			typename B::Iterator j(dst, o1);
365 			for(typename A::Iterator i(src, o0); i && j; ++i, ++j)
366 				*j = *i;
367 		}
368 		else
369 		{
370 			int full_size = 1 + 2*s;
371 			if (full_size + o0 > src.count) return;
372 			T w(T(1.0)/T(full_size));
373 			T sum(0.0);
374 			for(typename A::Iterator i(src, o0, full_size+o0); i; ++i)
375 				sum += *i;
376 			typename B::Iterator j(dst, o1);
377 			for(typename A::Iterator i0(src, o0), i1(src, full_size+o0); i1 && j; ++i0, ++j, ++i1)
378 			{
379 				*j = w*sum;
380 				sum += *i1 - *i0;
381 			}
382 		}
383 	}
384 
385 	template<typename T>
blur_box_aa(const Array<T,1> & x,std::deque<T> & q,const T & size)386 	static void blur_box_aa(const Array<T, 1> &x, std::deque<T> &q, const T &size)
387 	{
388 		typedef Array<T, 1> A;
389 
390 		const T precision(1e-10);
391 		T s(fabs(size));
392 		if (s < precision) return;
393 
394 		int sizei = 1 + (int)floor(s + precision);
395 		if (x.count <= 2*sizei) return;
396 
397 		T w(T(1.0)/(T(1.0) + T(2.0)*s));
398 		T aa(T(sizei) - s);
399 		T sum(0.0);
400 		T aa_out = x[0];
401 		q.clear();
402 		for(typename A::Iterator i(x, 1, 2*sizei); i; ++i)
403 			{ q.push_back(*i); sum += *i; }
404 		q.push_back(x[2*sizei]);
405 		sum += aa*(aa_out + q.back());
406 		for(typename A::Iterator i(x, 2*sizei+1), j(x, sizei); i; ++i, ++j)
407 		{
408 			*j = w*sum;
409 			T d = q.back() - q.front();
410 			sum += aa*(*i - aa_out - d) + d;
411 			aa_out = q.front();
412 			q.pop_front();
413 			q.push_back(*i);
414 		}
415 	}
416 
417 	template<typename T>
blur_box_aa(const Array<T,1> & dst,const Array<const T,1> & src,const T & size,const int offset)418 	static void blur_box_aa(const Array<T, 1> &dst, const Array<const T, 1> &src, const T &size, const int offset)
419 	{
420 		typedef Array<T, 1> A;
421 		typedef Array<const T, 1> B;
422 		const T precision(1e-10);
423 
424 		T s(fabs(size));
425 		int sizei = 1 + (int)floor(s + precision);
426 		int o0 = std::max(offset - sizei - 1, 0);
427 		int o1 = std::max(sizei + 1 - offset, 0);
428 		if (o0 >= src.count) return;
429 		if (o1 >= dst.count) return;
430 
431 		if (s < precision)
432 		{
433 			// copy
434 			typename B::Iterator j(dst, o1);
435 			for(typename A::Iterator i(src, o0); i && j; ++i, ++j)
436 				*j = *i;
437 		}
438 		else
439 		{
440 			if (2*sizei + o0 >= src.count) return;
441 			T w(T(1.0)/(T(1.0) + T(2.0)*s));
442 			T aa(T(sizei) - s);
443 			T sum(0.0);
444 			T aa_out = src[o0];
445 			for(typename A::Iterator i(src, o0, 2*sizei+o0); i; ++i)
446 				sum += *i;
447 			T aa_in = src[2*sizei+o0];
448 			sum += aa*(aa_out + aa_in);
449 			typename B::Iterator j(dst, o1);
450 			for(typename A::Iterator i0(src, o0+1), i1(src, 2*sizei+o0+1); i1 && j; ++i0, ++j, ++i1)
451 			{
452 				*j = w*sum;
453 				T d = aa_in - *i0;
454 				sum += aa(*i1 - aa_out - d) + d;
455 				aa_out = *i0;
456 				aa_in = *i1;
457 			}
458 		}
459 	}
460 
461 	template<typename T>
blur_box(const Array<T,1> & x,std::deque<T> & q,const T & size)462 	static void blur_box(const Array<T, 1> &x, std::deque<T> &q, const T &size)
463 	{
464 		const T precision(1e-5);
465 		if (fabs(size - round(size)) < precision)
466 			blur_box_discrete(x, q, (int)round(size));
467 		else
468 			blur_box_aa(x, q, size);
469 	}
470 
471 	template<typename T>
blur_box(const Array<T,1> & dst,const Array<const T,1> & src,const T & size,const int offset)472 	static void blur_box(const Array<T, 1> &dst, const Array<const T, 1> &src, const T &size, const int offset)
473 	{
474 		const T precision(1e-5);
475 		if (fabs(size - round(size)) < precision)
476 			blur_box_discrete(dst, src, (int)round(size), offset);
477 		else
478 			blur_box_aa(dst, src, size, offset);
479 	}
480 
481 	template<typename T>
blur_iir(const Array<T,1> & x,const T & k0,const T & k1,const T & k2,const T & k3)482 	static void blur_iir(const Array<T, 1> &x, const T &k0, const T &k1, const T &k2, const T &k3)
483 	{
484 		typedef Array<T, 1> A;
485 		T d3(0.0), d2(0.0), d1(0.0), d0;
486 		for(typename A::Iterator i(x); i; ++i)
487 			*i = d0 = k0*(*i) + k1*d1 + k2*d2 + k3*d3, d3 = d2, d2 = d1, d1 = d0;
488 		d3 = d2 = d1 = T(0.0);
489 		for(typename A::ReverseIterator i(x); i; ++i)
490 			*i = d0 = k0*(*i) + k1*d1 + k2*d2 + k3*d3, d3 = d2, d2 = d1, d1 = d0;
491 	}
492 
493 	template<typename T>
blur_iir(const Array<T,1> & dst,const Array<T,1> & src,const T & k0,const T & k1,const T & k2,const T & k3)494 	static void blur_iir(const Array<T, 1> &dst, const Array<T, 1> &src, const T &k0, const T &k1, const T &k2, const T &k3)
495 	{
496 		typedef Array<T, 1> A;
497 		int count = std::min(dst.count, src.count);
498 		T d3(0.0), d2(0.0), d1(0.0), d0;
499 		dst[0] = dst[1] = dst[2] = dst[dst.count - 1] = dst[dst.count - 2] = dst[dst.count - 3] = 0.0;
500 		for(typename A::Iterator j(dst, count), i(src, count); j; ++j, ++i)
501 			*j = d0 = k0*(*i) + k1*d1 + k2*d2 + k3*d3, d3 = d2, d2 = d1, d1 = d0;
502 		d3 = d2 = d1 = T(0.0);
503 		for(typename A::ReverseIterator j(dst, dst.count - count), i(src, src.count - count); j; ++j, ++i)
504 			*j = d0 = k0*(*i) + k1*d1 + k2*d2 + k3*d3, d3 = d2, d2 = d1, d1 = d0;
505 	}
506 
surface_as_array(Array<const Color,2> & a,const synfig::Surface & src,const RectInt & r)507 	static void surface_as_array(Array<const Color, 2> &a, const synfig::Surface &src, const RectInt &r)
508 	{
509 		assert(src.is_valid() && r.is_valid());
510 		assert(r.minx >= 0 && r.miny >= 0);
511 		assert(r.maxx <= src.get_w() && r.miny <= src.get_h());
512 		a.pointer = &src[r.miny][r.minx];
513 		a.set_dim(r.maxy - r.miny, src.get_pitch()/sizeof(Color))
514 		 .set_dim(r.maxx - r.minx, 1);
515 	}
516 
surface_as_array(Array<Color,2> & a,synfig::Surface & src,const RectInt & r)517 	static void surface_as_array(Array<Color, 2> &a, synfig::Surface &src, const RectInt &r)
518 	{
519 		assert(src.is_valid() && r.is_valid());
520 		assert(r.minx >= 0 && r.miny >= 0);
521 		assert(r.maxx <= src.get_w() && r.miny <= src.get_h());
522 		a.pointer = &src[r.miny][r.minx];
523 		a.set_dim(r.maxy - r.miny, src.get_pitch()/sizeof(Color))
524 		 .set_dim(r.maxx - r.minx, 1);
525 	}
526 
surface_as_array(synfig::Surface & src)527 	static Array<Color, 2> surface_as_array(synfig::Surface &src)
528 		{ Array<Color, 2> a; surface_as_array(a, src, RectInt(0, 0, src.get_w(), src.get_h())); return a; }
surface_as_array(synfig::Surface & src,const RectInt & r)529 	static Array<Color, 2> surface_as_array(synfig::Surface &src, const RectInt &r)
530 		{ Array<Color, 2> a; surface_as_array(a, src, r); return a; }
531 
surface_as_array(const synfig::Surface & src)532 	static Array<const Color, 2> surface_as_array(const synfig::Surface &src)
533 		{ Array<const Color, 2> a; surface_as_array(a, src, RectInt(0, 0, src.get_w(), src.get_h())); return a; }
surface_as_array(const synfig::Surface & src,const RectInt & r)534 	static Array<const Color, 2> surface_as_array(const synfig::Surface &src, const RectInt &r)
535 		{ Array<const Color, 2> a; surface_as_array(a, src, r); return a; }
536 
537 	template<typename T>
surface_read(const Array<T,3> & dst,const Array<const Color,2> & src)538 	static void surface_read(
539 		const Array<T, 3> &dst,
540 		const Array<const Color, 2> &src )
541 	{
542 		assert(dst.count == src.count);
543 		assert(dst.sub().count == src.sub().count);
544 		assert(dst.sub().sub().count == 4);
545 		Array<const Color, 2>::Iterator rr(src);
546 		for(typename Array<T, 3>::Iterator r(dst); r; ++r, ++rr)
547 		{
548 			Array<const Color, 1>::Iterator cc(*rr);
549 			for(typename Array<T, 2>::Iterator c(*r); c; ++c, ++cc)
550 			{
551 				T a = cc->get_a();
552 				(*c)[0] = cc->get_r()*a;
553 				(*c)[1] = cc->get_g()*a;
554 				(*c)[2] = cc->get_b()*a;
555 				(*c)[3] = a;
556 			}
557 		}
558 	}
559 
560 	template<typename T>
surface_read(const Array<T,3> & dst,const synfig::Surface & src,const VectorInt & dst_offset,const RectInt & src_rect)561 	static void surface_read(
562 		const Array<T, 3> &dst,
563 		const synfig::Surface &src,
564 		const VectorInt &dst_offset,
565 		const RectInt &src_rect )
566 	{
567 		RectInt dst_rect = src_rect - src_rect.get_min() + dst_offset;
568 		Array<T, 3> dst_range = dst.get_range(1, dst_rect.minx, dst_rect.maxx)
569 								   .get_range(0, dst_rect.miny, dst_rect.maxy);
570 		Array<const Color, 2> src_range = surface_as_array(src, src_rect);
571 		surface_read(dst_range, src_range);
572 	}
573 
574 	template<typename T>
surface_write(const Array<Color,2> & dst,const Array<T,3> & src)575 	static void surface_write(
576 		const Array<Color, 2> &dst,
577 		const Array<T, 3> &src )
578 	{
579 		assert(dst.count == src.count);
580 		assert(dst.sub().count == src.sub().count);
581 		assert(src.sub().sub().count == 4);
582 		const ColorReal precision = 1e-10;
583 		Array<Color, 2>::Iterator rr(dst);
584 		for(typename Array<T, 3>::Iterator r(src); r; ++r, ++rr)
585 		{
586 			Array<Color, 1>::Iterator cc(*rr);
587 			for(typename Array<T, 2>::Iterator c(*r); c; ++c, ++cc)
588 			{
589 				Real a = (*c)[3];
590 				Real one_div_a = fabs(a) < precision ? 0.0 : 1.0/a;
591 				Color color((*c)[0]*one_div_a, (*c)[1]*one_div_a, (*c)[2]*one_div_a, a);
592 				*cc = color;
593 			}
594 		}
595 	}
596 
597 	template<typename T>
surface_write(const Array<Color,2> & dst,const Array<T,3> & src,Color::BlendMethod blend_method,ColorReal amount)598 	static void surface_write(
599 		const Array<Color, 2> &dst,
600 		const Array<T, 3> &src,
601 		Color::BlendMethod blend_method,
602 		ColorReal amount )
603 	{
604 		assert(dst.count == src.count);
605 		assert(dst.sub().count == src.sub().count);
606 		assert(src.sub().sub().count == 4);
607 		const ColorReal precision = 1e-10;
608 		Array<Color, 2>::Iterator rr(dst);
609 		for(typename Array<T, 3>::Iterator r(src); r; ++r, ++rr)
610 		{
611 			Array<Color, 1>::Iterator cc(*rr);
612 			for(typename Array<T, 2>::Iterator c(*r); c; ++c, ++cc)
613 			{
614 				ColorReal a = (*c)[3];
615 				Real one_div_a = fabs(a) < precision ? 0.0 : 1.0/a;
616 				Color color((*c)[0]*one_div_a, (*c)[1]*one_div_a, (*c)[2]*one_div_a, a);
617 				*cc = Color::blend(color, *cc, amount, blend_method);
618 			}
619 		}
620 	}
621 
622 	template<typename T>
surface_write(synfig::Surface & dst,const Array<T,3> & src,const RectInt & dst_rect,const VectorInt & src_offset,bool blend,Color::BlendMethod blend_method,ColorReal amount)623 	static void surface_write(
624 		synfig::Surface &dst,
625 		const Array<T, 3> &src,
626 		const RectInt &dst_rect,
627 		const VectorInt &src_offset,
628 		bool blend,
629 		Color::BlendMethod blend_method,
630 		ColorReal amount )
631 	{
632 		RectInt src_rect = dst_rect - dst_rect.get_min() + src_offset;
633 		Array<Color, 2> dst_range = surface_as_array(dst, dst_rect);
634 		Array<T, 3> src_range = src.get_range(1, src_rect.minx, src_rect.maxx)
635 								   .get_range(0, src_rect.miny, src_rect.maxy);
636 		if (blend) surface_write(dst_range, src_range, blend_method, amount);
637 			  else surface_write(dst_range, src_range);
638 	}
639 };
640 
641 } /* end namespace software */
642 } /* end namespace rendering */
643 } /* end namespace synfig */
644 
645 /* -- E N D ----------------------------------------------------------------- */
646 
647 #endif
648