1 /* === S Y N F I G ========================================================= */
2 /*! \file surface.cpp
3 ** \brief Template File
4 **
5 ** $Id$
6 **
7 ** \legal
8 ** Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley
9 ** Copyright (c) 2012-2013 Carlos López
10 **
11 ** This package is free software; you can redistribute it and/or
12 ** modify it under the terms of the GNU General Public License as
13 ** published by the Free Software Foundation; either version 2 of
14 ** the License, or (at your option) any later version.
15 **
16 ** This package is distributed in the hope that it will be useful,
17 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
18 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 ** General Public License for more details.
20 ** \endlegal
21 **
22 ** === N O T E S ===========================================================
23 **
24 ** ========================================================================= */
25
26 /* === H E A D E R S ======================================================= */
27
28 #ifdef USING_PCH
29 # include "pch.h"
30 #else
31 #ifdef HAVE_CONFIG_H
32 # include <config.h>
33 #endif
34
35 #include "canvas.h"
36 #include "surface.h"
37 #include "target_scanline.h"
38 #include "target_cairo.h"
39 #include "target_tile.h"
40 #include "general.h"
41 #include <synfig/localization.h>
42
43 #ifdef HAS_VIMAGE
44 #include <Accelerate/Accelerate.h>
45 #endif
46
47 #endif
48
49 using namespace synfig;
50 using namespace std;
51 using namespace etl;
52
53 /* === M A C R O S ========================================================= */
54
55 /* === G L O B A L S ======================================================= */
56
57 class target2surface : public synfig::Target_Tile
58 {
59 public:
60 String engine;
61 Surface *surface;
62 bool sized;
63 public:
target2surface(Surface * surface,String engine)64 target2surface(Surface *surface, String engine):
65 surface(surface), sized() { set_engine(engine); }
66
start_frame(synfig::ProgressCallback *)67 virtual bool start_frame(synfig::ProgressCallback * /* cb */)
68 {
69 set_tile_w(desc.get_w());
70 set_tile_h(desc.get_h());
71 return true;
72 }
73
add_tile(const synfig::Surface & surface,int,int)74 bool add_tile(const synfig::Surface &surface, int /* x */, int /* y */)
75 {
76 *this->surface = surface;
77 return true;
78 }
79
end_frame()80 void end_frame() { }
81 };
82
83 class target2surface_scanline : public synfig::Target_Scanline
84 {
85 public:
86 Surface *surface;
87 bool sized;
88
target2surface_scanline(Surface * surface)89 target2surface_scanline(Surface *surface):
90 surface(surface), sized() { }
91
set_rend_desc(synfig::RendDesc * newdesc)92 virtual bool set_rend_desc(synfig::RendDesc *newdesc)
93 {
94 assert(newdesc);
95 assert(surface);
96 return synfig::Target_Scanline::set_rend_desc(newdesc);
97 }
98
start_frame(synfig::ProgressCallback *)99 virtual bool start_frame(synfig::ProgressCallback * /* cb */)
100 {
101 if(surface->get_w() != desc.get_w() || surface->get_h() != desc.get_h())
102 surface->set_wh(desc.get_w(),desc.get_h());
103 return true;
104 }
105
end_frame()106 virtual void end_frame()
107 { }
108
start_scanline(int scanline)109 virtual Color * start_scanline(int scanline)
110 { return (*surface)[scanline]; }
111
end_scanline()112 virtual bool end_scanline()
113 { return true; }
114 };
115
116
117 class target2cairo_image: public synfig::Target_Cairo
118 {
119 public:
120 cairo_surface_t** image;
121
target2cairo_image(cairo_surface_t ** s)122 target2cairo_image(cairo_surface_t ** s):
123 image(s) { }
124
set_rend_desc(synfig::RendDesc * newdesc)125 virtual bool set_rend_desc(synfig::RendDesc *newdesc)
126 {
127 assert(newdesc);
128 return synfig::Target_Cairo::set_rend_desc(newdesc);
129 }
130
obtain_surface(cairo_surface_t * & s)131 virtual bool obtain_surface(cairo_surface_t*& s)
132 {
133 int sw=cairo_image_surface_get_width(*image);
134 int sh=cairo_image_surface_get_height(*image);
135 int w=desc.get_w(), h=desc.get_h();
136
137 if(sw!=w || sh!=h)
138 {
139 cairo_surface_destroy(*image);
140 *image = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, w, h);
141 }
142 s=cairo_surface_reference(*image);
143 return true;
144 }
145 };
146
147 /* === P R O C E D U R E S ================================================= */
148
149 /* === M E T H O D S ======================================================= */
150
151 Target_Tile::Handle
surface_target(Surface * surface,const String & engine)152 synfig::surface_target(Surface *surface, const String &engine)
153 { return Target_Tile::Handle(new target2surface(surface, engine)); }
154
155 Target_Scanline::Handle
surface_target_scanline(Surface * surface)156 synfig::surface_target_scanline(Surface *surface)
157 { return Target_Scanline::Handle(new target2surface_scanline(surface)); }
158
159 Target_Cairo::Handle
cairo_image_target(cairo_surface_t ** surface)160 synfig::cairo_image_target(cairo_surface_t **surface)
161 { return Target_Cairo::Handle(new target2cairo_image(surface)); }
162
163
164 void
clear()165 synfig::Surface::clear()
166 {
167 #ifdef HAS_VIMAGE
168 fill(Color(0.5,0.5,0.5,0.0000001));
169 #else
170 etl::surface<Color, ColorAccumulator, ColorPrep>::clear();
171 #endif
172 }
173
174 void
blit_to(alpha_pen & pen,int x,int y,int w,int h)175 synfig::Surface::blit_to(alpha_pen& pen, int x, int y, int w, int h)
176 {
177 static const float epsilon(0.00001);
178 const float alpha(pen.get_alpha());
179 if( pen.get_blend_method()==Color::BLEND_STRAIGHT && fabs(alpha-1.0f)<epsilon )
180 {
181 if(x>=get_w() || y>=get_w())
182 return;
183
184 //clip source origin
185 if(x<0)
186 {
187 w+=x; //decrease
188 x=0;
189 }
190
191 if(y<0)
192 {
193 h+=y; //decrease
194 y=0;
195 }
196
197 //clip width against dest width
198 w = min((long)w,(long)(pen.end_x()-pen.x()));
199 h = min((long)h,(long)(pen.end_y()-pen.y()));
200
201 //clip width against src width
202 w = min(w,get_w()-x);
203 h = min(h,get_h()-y);
204
205 if(w<=0 || h<=0)
206 return;
207
208 for(int i=0;i<h;i++)
209 {
210 char* src(static_cast<char*>(static_cast<void*>(operator[](y)+x))+i*get_w()*sizeof(Color));
211 char* dest(static_cast<char*>(static_cast<void*>(pen.x()))+i*pen.get_width()*sizeof(Color));
212 memcpy(dest,src,w*sizeof(Color));
213 }
214 return;
215 }
216
217 #ifdef HAS_VIMAGE
218 if( pen.get_blend_method()==Color::BLEND_COMPOSITE && fabs(alpha-1.0f)<epsilon )
219 {
220 if(x>=get_w() || y>=get_w())
221 return;
222
223 //clip source origin
224 if(x<0)
225 {
226 //u-=x; //increase
227 w+=x; //decrease
228 x=0;
229 }
230
231 if(y<0)
232 {
233 //v-=y; //increase
234 h+=y; //decrease
235 y=0;
236 }
237
238 //clip width against dest width
239 w = min(w,pen.end_x()-pen.x());
240 h = min(h,pen.end_y()-pen.y());
241
242 //clip width against src width
243 w = min(w,get_w()-x);
244 h = min(h,get_h()-y);
245
246 if(w<=0 || h<=0)
247 return;
248
249
250
251 vImage_Buffer top,bottom;
252 vImage_Buffer& dest(bottom);
253
254 top.data=static_cast<void*>(operator[](y)+x);
255 top.height=h;
256 top.width=w;
257 //top.rowBytes=get_w()*sizeof(Color); //! \todo this should get the pitch!!
258 top.rowBytes=get_pitch();
259
260 bottom.data=static_cast<void*>(pen.x());
261 bottom.height=h;
262 bottom.width=w;
263 //bottom.rowBytes=pen.get_width()*sizeof(Color); //! \todo this should get the pitch!!
264 bottom.rowBytes=pen.get_pitch(); //! \todo this should get the pitch!!
265
266 vImage_Error ret;
267 ret=vImageAlphaBlend_ARGBFFFF(&top,&bottom,&dest,kvImageNoFlags);
268
269 assert(ret!=kvImageNoError);
270
271 return;
272 }
273 #endif
274 etl::surface<Color, ColorAccumulator, ColorPrep>::blit_to(pen,x,y,w,h);
275 }
276
277 void
blit_to(alpha_pen & pen,int x,int y,int w,int h)278 synfig::CairoSurface::blit_to(alpha_pen& pen, int x, int y, int w, int h)
279 {
280 static const float epsilon(0.00001);
281 const float alpha(pen.get_alpha());
282 if( pen.get_blend_method()==Color::BLEND_STRAIGHT && fabs(alpha-1.0f)<epsilon )
283 {
284 if(x>=get_w() || y>=get_w())
285 return;
286
287 //clip source origin
288 if(x<0)
289 {
290 w+=x; //decrease
291 x=0;
292 }
293
294 if(y<0)
295 {
296 h+=y; //decrease
297 y=0;
298 }
299
300 //clip width against dest width
301 w = min((long)w,(long)(pen.end_x()-pen.x()));
302 h = min((long)h,(long)(pen.end_y()-pen.y()));
303
304 //clip width against src width
305 w = min(w,get_w()-x);
306 h = min(h,get_h()-y);
307
308 if(w<=0 || h<=0)
309 return;
310
311 for(int i=0;i<h;i++)
312 {
313 char* src(static_cast<char*>(static_cast<void*>(operator[](y)+x))+i*get_w()*sizeof(CairoColor));
314 char* dest(static_cast<char*>(static_cast<void*>(pen.x()))+i*pen.get_width()*sizeof(CairoColor));
315 memcpy(dest,src,w*sizeof(CairoColor));
316 }
317 return;
318 }
319 else
320 etl::surface<CairoColor, CairoColorAccumulator, CairoColorPrep>::blit_to(pen,x,y,w,h);
321 }
322
323 void
set_wh(int w,int h,int)324 CairoSurface::set_wh(int w, int h, int /*pitch*/)
325 {
326 // If cs_ has been set then don't resize it.
327 // Only the target which created the specific cairo surface can do that.
328 if(cs_!= NULL)
329 {
330 synfig::warning("Cannot resize a Cairo Surface directly. Use its Target_Cairo instance");
331 return;
332 }
333 // decrease reference counter in case we have a valid cs_image_
334 if(cs_image_!=NULL)
335 cairo_surface_destroy(cs_image_);
336 // create a new cairo_surface image type
337 cs_image_=cairo_image_surface_create(CAIRO_FORMAT_ARGB32, w, h);
338 // check wheter the new cairo surface is sane
339 if(!cairo_surface_status(cs_image_))
340 {
341 int stride(cairo_image_surface_get_stride(cs_image_));
342 unsigned char* data(cairo_image_surface_get_data(cs_image_));
343 // pass the infromation to the etl::surface to be used directly.
344 set_wh(w, h, data, stride);
345 }
346 else
347 synfig::warning("CairoSurface::set_wh failed");
348 }
349
350 void
set_cairo_surface(cairo_surface_t * cs)351 CairoSurface::set_cairo_surface(cairo_surface_t *cs)
352 {
353 if(cs==NULL)
354 {
355 if(is_mapped())
356 unmap_cairo_image();
357 cairo_surface_destroy(cs_);
358 cs_=NULL;
359 return;
360 }
361 if(cairo_surface_status(cs))
362 {
363 synfig::error("CairoSurface received a non valid cairo_surface_t");
364 if(is_mapped())
365 unmap_cairo_image();
366 cairo_surface_destroy(cs_);
367 cs_=NULL;
368 return;
369 }
370 else
371 {
372 if(is_mapped())
373 unmap_cairo_image();
374 cairo_surface_destroy(cs_);
375 cs_=cairo_surface_reference(cs);
376 }
377 }
378
379 cairo_surface_t*
get_cairo_surface() const380 CairoSurface::get_cairo_surface()const
381 {
382 if(cs_==NULL)
383 return NULL;
384 else
385 return cairo_surface_reference(cs_);
386 }
387
388 cairo_surface_t*
get_cairo_image_surface() const389 CairoSurface::get_cairo_image_surface()const
390 {
391 if(cs_image_==NULL)
392 return NULL;
393 else
394 return cairo_surface_reference(cs_image_);
395 }
396
397
398 bool
map_cairo_image()399 CairoSurface::map_cairo_image()
400 {
401 if(!cs_ || cs_image_)
402 {
403 synfig::error("Attempting to map a NULL surface or a surface already mapped)");
404 return false;
405 }
406 if(cairo_surface_get_type(cs_) != CAIRO_SURFACE_TYPE_IMAGE)
407 {
408 #if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 12, 0)
409 cs_image_=cairo_surface_map_to_image (cs_, NULL);
410 #else
411 assert(false); // Old versions of cairo are not supported
412 #endif
413 }
414 else
415 {
416 cs_image_=cairo_surface_reference(cs_);
417 }
418 if(cs_image_!=NULL)
419 {
420 cairo_surface_flush(cs_image_);
421 cairo_format_t t=cairo_image_surface_get_format(cs_image_);
422 if(t==CAIRO_FORMAT_ARGB32 || t==CAIRO_FORMAT_RGB24)
423 {
424 unsigned char* data(cairo_image_surface_get_data(cs_image_));
425 int w(cairo_image_surface_get_width(cs_image_));
426 int h(cairo_image_surface_get_height(cs_image_));
427 int stride(cairo_image_surface_get_stride(cs_image_));
428 set_wh(w, h, data, stride);
429 return true;
430 }
431 return false;
432 }
433 return false;
434 }
435
436 void
unmap_cairo_image()437 CairoSurface::unmap_cairo_image()
438 {
439 if(!cs_ || !cs_image_)
440 {
441 synfig::error("Attempting to unmap a NULL surface or a surface not mapped)");
442 return;
443 }
444 cairo_surface_mark_dirty(cs_image_);
445 if(cairo_surface_get_type(cs_) != CAIRO_SURFACE_TYPE_IMAGE)
446 {
447 #if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 12, 0)
448 // this will destroy cs_image_
449 cairo_surface_unmap_image(cs_, cs_image_);
450 cs_image_=NULL;
451 #else
452 assert(false); // Old versions of cairo are not supported
453 #endif
454 }
455 else
456 {
457 cairo_surface_destroy(cs_image_);
458 cs_image_ = NULL;
459 }
460 }
461
462
463 bool
is_mapped() const464 CairoSurface::is_mapped()const
465 {
466 return (cs_image_!=NULL);
467 }
468
469
470