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