1 /* === S Y N F I G ========================================================= */
2 /*!	\file template.cpp
3  **	\brief Template File
4  **
5  **	$Id$
6  **
7  **	\legal
8  **	Copyright (c) 2012-2013 Carlos López
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 "cairo_operators.h"
33 #include "surface.h"
34 #include "general.h"
35 #include <synfig/localization.h>
36 
37 
38 #endif
39 
40 /* === U S I N G =========================================================== */
41 
42 using namespace std;
43 using namespace etl;
44 using namespace synfig;
45 
46 /* === M A C R O S ========================================================= */
47 
48 /* === G L O B A L S ======================================================= */
49 
50 /* === P R O C E D U R E S ================================================= */
51 
cairo_paint_with_alpha_operator(cairo_t * acr,float alpha,Color::BlendMethod method)52 void cairo_paint_with_alpha_operator(cairo_t* acr, float alpha, Color::BlendMethod method)
53 {
54 	cairo_t* cr=cairo_reference(acr);
55 	switch (method)
56 	{
57 		case Color::BLEND_COMPOSITE:
58 		{
59 			cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
60 			cairo_paint_with_alpha(cr, alpha);
61 			break;
62 		}
63 		case Color::BLEND_STRAIGHT:
64 		{
65 			cairo_save(cr);
66 
67 			cairo_set_source_rgba(cr, 1.0, 1.0, 1.0, 1.0-alpha);
68 			cairo_set_operator(cr, CAIRO_OPERATOR_DEST_IN);
69 			cairo_paint(cr);
70 
71 			cairo_restore(cr);
72 
73 			cairo_set_operator(cr, CAIRO_OPERATOR_ADD);
74 			cairo_paint_with_alpha(cr, alpha);
75 			break;
76 		}
77 		case Color::BLEND_MULTIPLY:
78 		{
79 			cairo_push_group(cr);
80 			cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
81 			cairo_paint_with_alpha(cr, alpha);
82 			cairo_pop_group_to_source(cr);
83 
84 			cairo_matrix_t matrix;
85 			cairo_get_matrix(cr, &matrix);
86 			cairo_set_operator(cr, CAIRO_OPERATOR_MULTIPLY);
87 			cairo_pattern_t* pattern=cairo_pattern_create_for_surface(cairo_get_target(cr));
88 			cairo_pattern_set_matrix(pattern, &matrix);
89 			cairo_mask(cr, pattern);
90 
91 			cairo_pattern_destroy(pattern);
92 			break;
93 		}
94 		case Color::BLEND_HUE:
95 		{
96 			cairo_push_group(cr);
97 			cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
98 			cairo_paint_with_alpha(cr, alpha);
99 			cairo_pop_group_to_source(cr);
100 
101 			cairo_matrix_t matrix;
102 			cairo_get_matrix(cr, &matrix);
103 			cairo_set_operator(cr, CAIRO_OPERATOR_HSL_HUE);
104 			cairo_pattern_t* pattern=cairo_pattern_create_for_surface(cairo_get_target(cr));
105 			cairo_pattern_set_matrix(pattern, &matrix);
106 			cairo_mask(cr, pattern);
107 
108 			cairo_pattern_destroy(pattern);
109 			break;
110 		}
111 		case Color::BLEND_SATURATION:
112 		{
113 			cairo_push_group(cr);
114 			cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
115 			cairo_paint_with_alpha(cr, alpha);
116 			cairo_pop_group_to_source(cr);
117 
118 			cairo_matrix_t matrix;
119 			cairo_get_matrix(cr, &matrix);
120 			cairo_set_operator(cr, CAIRO_OPERATOR_HSL_SATURATION);
121 			cairo_pattern_t* pattern=cairo_pattern_create_for_surface(cairo_get_target(cr));
122 			cairo_pattern_set_matrix(pattern, &matrix);
123 			cairo_mask(cr, pattern);
124 
125 			cairo_pattern_destroy(pattern);
126 			break;
127 		}
128 		case Color::BLEND_LUMINANCE:
129 		{
130 			cairo_push_group(cr);
131 			cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
132 			cairo_paint_with_alpha(cr, alpha);
133 			cairo_pop_group_to_source(cr);
134 
135 			cairo_matrix_t matrix;
136 			cairo_get_matrix(cr, &matrix);
137 			cairo_set_operator(cr, CAIRO_OPERATOR_HSL_LUMINOSITY);
138 			cairo_pattern_t* pattern=cairo_pattern_create_for_surface(cairo_get_target(cr));
139 			cairo_pattern_set_matrix(pattern, &matrix);
140 			cairo_mask(cr, pattern);
141 
142 			cairo_pattern_destroy(pattern);
143 			break;
144 		}
145 		case Color::BLEND_BEHIND:
146 		{
147 			cairo_set_operator(cr, CAIRO_OPERATOR_DEST_OVER);
148 			cairo_paint_with_alpha(cr, alpha);
149 			break;
150 		}
151 		case Color::BLEND_ONTO:
152 		{
153 			cairo_set_operator(cr, CAIRO_OPERATOR_ATOP);
154 			cairo_paint_with_alpha(cr, alpha);
155 			break;
156 		}
157 		case Color::BLEND_SCREEN:
158 		{
159 			cairo_push_group(cr);
160 			cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
161 			cairo_paint_with_alpha(cr, alpha);
162 			cairo_pop_group_to_source(cr);
163 
164 			cairo_matrix_t matrix;
165 			cairo_get_matrix(cr, &matrix);
166 			cairo_set_operator(cr, CAIRO_OPERATOR_SCREEN);
167 			cairo_pattern_t* pattern=cairo_pattern_create_for_surface(cairo_get_target(cr));
168 			cairo_pattern_set_matrix(pattern, &matrix);
169 			cairo_mask(cr, pattern);
170 
171 			cairo_pattern_destroy(pattern);
172 			break;
173 		}
174 		case Color::BLEND_HARD_LIGHT:
175 		{
176 			cairo_push_group(cr);
177 			cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
178 			cairo_paint_with_alpha(cr, alpha);
179 			cairo_pop_group_to_source(cr);
180 
181 			cairo_matrix_t matrix;
182 			cairo_get_matrix(cr, &matrix);
183 			cairo_set_operator(cr, CAIRO_OPERATOR_HARD_LIGHT);
184 			cairo_pattern_t* pattern=cairo_pattern_create_for_surface(cairo_get_target(cr));
185 			cairo_pattern_set_matrix(pattern, &matrix);
186 			cairo_mask(cr, pattern);
187 
188 			cairo_pattern_destroy(pattern);
189 			break;
190 		}
191 		case Color::BLEND_ALPHA_OVER:
192 		{
193 			cairo_set_operator(cr, CAIRO_OPERATOR_DEST_OUT);
194 			cairo_paint_with_alpha(cr, alpha);
195 			break;
196 		}
197 		case Color::BLEND_STRAIGHT_ONTO: // I don't find a suitable way to produce the
198 		// render method so I fall back to pixel operations.
199 //		{
200 //			cairo_push_group(cr);
201 //			cairo_save(cr);
202 //
203 //			cairo_set_source_rgba(cr, 1.0, 1.0, 1.0, 1.0-alpha);
204 //			cairo_set_operator(cr, CAIRO_OPERATOR_DEST_IN);
205 //			cairo_paint(cr);
206 //
207 //			cairo_restore(cr);
208 //
209 //			cairo_set_operator(cr, CAIRO_OPERATOR_ADD);
210 //			cairo_paint_with_alpha(cr, alpha);
211 //			cairo_pop_group_to_source(cr);
212 //
213 //			cairo_matrix_t matrix;
214 //			cairo_get_matrix(cr, &matrix);
215 //			cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
216 //			cairo_pattern_t* pattern=cairo_pattern_create_for_surface(cairo_get_target(cr));
217 //			cairo_pattern_set_matrix(pattern, &matrix);
218 //			cairo_mask(cr, pattern);
219 //
220 //			cairo_pattern_destroy(pattern);
221 //			break;
222 //		}
223 		case Color::BLEND_OVERLAY:
224 		{
225 			cairo_push_group(cr);
226 			cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
227 			cairo_paint(cr);
228 			cairo_pattern_t* pattern=cairo_pop_group(cr);
229 
230 			cairo_surface_t* source;
231 			cairo_status_t status;
232 			status=cairo_pattern_get_surface(pattern, &source);
233 			if(status)
234 			{
235 				// return gracefully
236 				synfig::error("%s", cairo_status_to_string(status));
237 				cairo_pattern_destroy(pattern);
238 				return;
239 			}
240 			CairoSurface csource(source);
241 			CairoSurface cdest(cairo_get_target(cr));
242 
243 			if(!cdest.map_cairo_image())
244 			{
245 					// return gracefully
246 					cairo_pattern_destroy(pattern);
247 					return;
248 			}
249 			if(!csource.map_cairo_image())
250 			   {
251 				   // return gracefully
252 				   cairo_pattern_destroy(pattern);
253 				   cdest.unmap_cairo_image();
254 				   return;
255 			   }
256 
257 			double x1, y1, x2, y2, x0, y0;
258 			cairo_clip_extents(cr, &x1, &y1, &x2, &y2);
259 			cairo_user_to_device(cr, &x1, &y1);
260 			cairo_user_to_device(cr, &x2, &y2);
261 			x0=x2<x1?x2:x1;
262 			y0=y2<y1?y2:y1;
263 
264 			int w=csource.get_w();
265 			int h=csource.get_h();
266 			int h0=(int)y0;
267 			int w0=(int)x0;
268 			for(int y=0;y<h;y++)
269 				for(int x=0;x<w;x++)
270 				{
271 					CairoColor ret=CairoColor::blend(csource[y][x].demult_alpha(), cdest[h0+y][w0+x].demult_alpha(), alpha,	method);
272 					csource[y][x]=ret.premult_alpha();
273 				}
274 			csource.unmap_cairo_image();
275 			cdest.unmap_cairo_image();
276 
277 			cairo_save(cr);
278 			cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
279 			cairo_reset_clip(cr);
280 			cairo_identity_matrix(cr);
281 			cairo_set_source_surface(cr, source, 0, 0);
282 			cairo_paint(cr);
283 			cairo_restore(cr);
284 
285 			cairo_pattern_destroy(pattern);
286 			break;
287 
288 		}
289 		case Color::BLEND_BRIGHTEN:
290 		case Color::BLEND_DARKEN:
291 		case Color::BLEND_ADD:
292 		case Color::BLEND_SUBTRACT:
293 		case Color::BLEND_DIFFERENCE:
294 		case Color::BLEND_DIVIDE:
295 		case Color::BLEND_ALPHA_DARKEN:
296 		case Color::BLEND_ALPHA_BRIGHTEN:
297 		{
298 			cairo_push_group(cr);
299 			cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
300 			cairo_paint(cr);
301 			cairo_pattern_t* pattern=cairo_pop_group(cr);
302 
303 			cairo_surface_t* source;
304 			cairo_status_t status;
305 			status=cairo_pattern_get_surface(pattern, &source);
306 			if(status)
307 			{
308 				// return gracefully
309 				synfig::error("%s", cairo_status_to_string(status));
310 				cairo_pattern_destroy(pattern);
311 				return;
312 			}
313 			CairoSurface csource(source);
314 			CairoSurface cdest(cairo_get_target(cr));
315 
316 			if(!cdest.map_cairo_image())
317 			{
318 				// return gracefully
319 				cairo_pattern_destroy(pattern);
320 				return;
321 			}
322 			if(!csource.map_cairo_image())
323 			{
324 				// return gracefully
325 				cairo_pattern_destroy(pattern);
326 				cdest.unmap_cairo_image();
327 				return;
328 			}
329 			double x1, y1, x2, y2, x0, y0;
330 			cairo_clip_extents(cr, &x1, &y1, &x2, &y2);
331 			cairo_user_to_device(cr, &x1, &y1);
332 			cairo_user_to_device(cr, &x2, &y2);
333 			x0=x2<x1?x2:x1;
334 			y0=y2<y1?y2:y1;
335 
336 			int w=csource.get_w();
337 			int h=csource.get_h();
338 			int h0=(int)y0;
339 			int w0=(int)x0;
340 			for(int y=0;y<h;y++)
341 				for(int x=0;x<w;x++)
342 				{
343 					CairoColor src=csource[y][x].demult_alpha();
344 					CairoColor dest=cdest[h0+y][w0+x].demult_alpha();
345 					CairoColor ret=CairoColor::blend(src, dest, alpha, method);
346 					ret=ret.premult_alpha();
347 					cdest[h0+y][w0+x]=ret;
348 				}
349 			csource.unmap_cairo_image();
350 			cdest.unmap_cairo_image();
351 
352 			cairo_pattern_destroy(pattern);
353 			break;
354 		}
355 		case Color::BLEND_COLOR:
356 		default:
357 		{
358 			cairo_push_group(cr);
359 			cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
360 			cairo_paint(cr);
361 			cairo_pattern_t* pattern=cairo_pop_group(cr);
362 
363 			cairo_surface_t* source;
364 			cairo_status_t status;
365 			status=cairo_pattern_get_surface(pattern, &source);
366 			if(status)
367 			{
368 				// return gracefully
369 				synfig::error("%s", cairo_status_to_string(status));
370 				cairo_pattern_destroy(pattern);
371 				return;
372 			}
373 			CairoSurface csource(source);
374 			CairoSurface cdest(cairo_get_target(cr));
375 
376 			if(!cdest.map_cairo_image())
377 			{
378 				// return gracefully
379 				cairo_pattern_destroy(pattern);
380 				return;
381 			}
382 			if(!csource.map_cairo_image())
383 			{
384 				// return gracefully
385 				cairo_pattern_destroy(pattern);
386 				cdest.unmap_cairo_image();
387 				return;
388 			}
389 
390 			double x1, y1, x2, y2, x0, y0;
391 			cairo_clip_extents(cr, &x1, &y1, &x2, &y2);
392 			cairo_user_to_device(cr, &x1, &y1);
393 			cairo_user_to_device(cr, &x2, &y2);
394 			x0=x2<x1?x2:x1;
395 			y0=y2<y1?y2:y1;
396 
397 			int w=csource.get_w();
398 			int h=csource.get_h();
399 			int h0=(int)y0;
400 			int w0=(int)x0;
401 
402 			for(int y=0;y<h;y++)
403 				for(int x=0;x<w;x++)
404 				{
405 					Color src=Color(csource[y][x].demult_alpha());
406 					Color dst=Color(cdest[h0+y][w0+x].demult_alpha());
407 					cdest[h0+y][w0+x]=CairoColor(Color::blend(src, dst, alpha, method).clamped().premult_alpha());
408 				}
409 			csource.unmap_cairo_image();
410 			cdest.unmap_cairo_image();
411 
412 			cairo_pattern_destroy(pattern);
413 			break;
414 		}
415 	}
416 	cairo_destroy(cr);
417 }
418 
cairo_copy_surface(cairo_surface_t * source,cairo_surface_t * dest,float alpha)419 void cairo_copy_surface(cairo_surface_t* source, cairo_surface_t* dest, float alpha)
420 {
421 	cairo_t* cr=cairo_create(dest);
422 	cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
423 	cairo_set_source_surface(cr, source, 0, 0);
424 	cairo_paint_with_alpha(cr, alpha);
425 	cairo_destroy(cr);
426 }
427 
cairo_copy_target_image(cairo_surface_t * target,float alpha)428 cairo_surface_t* cairo_copy_target_image(cairo_surface_t* target, float alpha)
429 {
430 	cairo_surface_t* targetimage=cairo_surface_map_to_image(target, NULL);
431 	cairo_surface_flush(targetimage);
432 	int w=cairo_image_surface_get_width(targetimage);
433 	int h=cairo_image_surface_get_height(targetimage);
434 	cairo_surface_t* image=cairo_image_surface_create(CAIRO_FORMAT_ARGB32, w, h);
435 	cairo_copy_surface(targetimage, image, alpha);
436 	cairo_surface_mark_dirty(targetimage);
437 	cairo_surface_unmap_image(target, targetimage);
438 	return image;
439 }
440 
cairo_surface_mask_alpha(cairo_surface_t * image,float alpha)441 void cairo_surface_mask_alpha(cairo_surface_t* image, float alpha)
442 {
443 	cairo_t* cr=cairo_create(image);
444 	cairo_set_source_rgba(cr, 1.0, 1.0, 1.0, 1.0);
445 	cairo_set_operator(cr, CAIRO_OPERATOR_ATOP);
446 	cairo_paint_with_alpha(cr, alpha);
447 	cairo_destroy(cr);
448 
449 }
450 
451 
452 /* === M E T H O D S ======================================================= */
453 
454 /* === E N T R Y P O I N T ================================================= */
455