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