1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org 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
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_canvas.hxx"
30 
31 #include <canvas/debug.hxx>
32 #include <tools/diagnose_ex.h>
33 
34 #include <rtl/logfile.hxx>
35 #include <rtl/math.hxx>
36 #include <rtl/instance.hxx>
37 
38 #include <com/sun/star/util/Endianness.hpp>
39 #include <com/sun/star/rendering/TexturingMode.hpp>
40 #include <com/sun/star/rendering/CompositeOperation.hpp>
41 #include <com/sun/star/rendering/RepaintResult.hpp>
42 #include <com/sun/star/rendering/PathCapType.hpp>
43 #include <com/sun/star/rendering/PathJoinType.hpp>
44 #include <com/sun/star/rendering/XIntegerBitmapColorSpace.hpp>
45 #include <com/sun/star/rendering/IntegerBitmapLayout.hpp>
46 #include <com/sun/star/rendering/ColorSpaceType.hpp>
47 #include <com/sun/star/rendering/ColorComponentTag.hpp>
48 #include <com/sun/star/rendering/RenderingIntent.hpp>
49 
50 #include <basegfx/matrix/b2dhommatrix.hxx>
51 #include <basegfx/point/b2dpoint.hxx>
52 #include <basegfx/polygon/b2dpolygon.hxx>
53 #include <basegfx/polygon/b2dpolypolygon.hxx>
54 #include <basegfx/polygon/b2dpolygontools.hxx>
55 #include <basegfx/tools/canvastools.hxx>
56 #include <basegfx/tools/keystoplerp.hxx>
57 #include <basegfx/tools/lerp.hxx>
58 
59 #include <comphelper/sequence.hxx>
60 #include <cppuhelper/compbase1.hxx>
61 
62 #include <canvas/canvastools.hxx>
63 #include <canvas/parametricpolypolygon.hxx>
64 
65 #include <vcl/canvastools.hxx>
66 #include <vcl/bitmapex.hxx>
67 #include <vcl/bmpacc.hxx>
68 #include <vcl/virdev.hxx>
69 
70 #include "cairo_spritecanvas.hxx"
71 #include "cairo_cachedbitmap.hxx"
72 #include "cairo_canvashelper.hxx"
73 #include "cairo_canvasbitmap.hxx"
74 
75 #include <boost/tuple/tuple.hpp>
76 #include <algorithm>
77 
78 using namespace ::cairo;
79 using namespace ::com::sun::star;
80 
81 namespace cairocanvas
82 {
83     CanvasHelper::CanvasHelper() :
84         mpSurfaceProvider(NULL),
85         mpDevice(NULL),
86 		mpVirtualDevice(),
87         mbHaveAlpha(),
88 		mpCairo(),
89 		mpSurface(),
90         maSize()
91     {
92     }
93 
94     void CanvasHelper::disposing()
95     {
96 		mpSurface.reset();
97 		mpCairo.reset();
98         mpVirtualDevice.reset();
99         mpDevice = NULL;
100         mpSurfaceProvider = NULL;
101     }
102 
103     void CanvasHelper::init( const ::basegfx::B2ISize&  rSizePixel,
104                              SurfaceProvider&           rSurfaceProvider,
105                              rendering::XGraphicDevice* pDevice )
106     {
107         maSize = rSizePixel;
108         mpSurfaceProvider = &rSurfaceProvider;
109         mpDevice = pDevice;
110     }
111 
112     void CanvasHelper::setSize( const ::basegfx::B2ISize& rSize )
113     {
114         maSize = rSize;
115     }
116 
117     void CanvasHelper::setSurface( const SurfaceSharedPtr& pSurface, bool bHasAlpha )
118     {
119         mbHaveAlpha = bHasAlpha;
120         mpVirtualDevice.reset();
121 		mpSurface = pSurface;
122 		mpCairo = pSurface->getCairo();
123     }
124 
125     static void setColor( Cairo* pCairo,
126                           const uno::Sequence<double>& rColor )
127     {
128         if( rColor.getLength() > 3 )
129         {
130             const double alpha = rColor[3];
131 
132             cairo_set_source_rgba( pCairo,
133                                    alpha*rColor[0],
134                                    alpha*rColor[1],
135                                    alpha*rColor[2],
136                                    alpha );
137         }
138         else if( rColor.getLength() == 3 )
139             cairo_set_source_rgb( pCairo,
140                                   rColor[0],
141                                   rColor[1],
142                                   rColor[2] );
143     }
144 
145     void CanvasHelper::useStates( const rendering::ViewState& viewState,
146                                   const rendering::RenderState& renderState,
147                                   bool bSetColor )
148     {
149         Matrix aViewMatrix;
150         Matrix aRenderMatrix;
151         Matrix aCombinedMatrix;
152 
153         cairo_matrix_init( &aViewMatrix,
154                            viewState.AffineTransform.m00, viewState.AffineTransform.m10, viewState.AffineTransform.m01,
155                            viewState.AffineTransform.m11, viewState.AffineTransform.m02, viewState.AffineTransform.m12);
156         cairo_matrix_init( &aRenderMatrix,
157                            renderState.AffineTransform.m00, renderState.AffineTransform.m10, renderState.AffineTransform.m01,
158                            renderState.AffineTransform.m11, renderState.AffineTransform.m02, renderState.AffineTransform.m12);
159         cairo_matrix_multiply( &aCombinedMatrix, &aRenderMatrix, &aViewMatrix);
160 
161         if( viewState.Clip.is() ) {
162             OSL_TRACE ("view clip");
163 
164             aViewMatrix.x0 = basegfx::fround( aViewMatrix.x0 );
165             aViewMatrix.y0 = basegfx::fround( aViewMatrix.y0 );
166             cairo_set_matrix( mpCairo.get(), &aViewMatrix );
167             doPolyPolygonPath( viewState.Clip, Clip );
168         }
169 
170         aCombinedMatrix.x0 = basegfx::fround( aCombinedMatrix.x0 );
171         aCombinedMatrix.y0 = basegfx::fround( aCombinedMatrix.y0 );
172         cairo_set_matrix( mpCairo.get(), &aCombinedMatrix );
173 
174         if( renderState.Clip.is() ) {
175             OSL_TRACE ("render clip BEGIN");
176 
177             doPolyPolygonPath( renderState.Clip, Clip );
178             OSL_TRACE ("render clip END");
179         }
180 
181         if( bSetColor )
182             setColor(mpCairo.get(),renderState.DeviceColor);
183 
184         cairo_operator_t compositingMode( CAIRO_OPERATOR_OVER );
185         switch( renderState.CompositeOperation )
186         {
187             case rendering::CompositeOperation::CLEAR:
188                 compositingMode = CAIRO_OPERATOR_CLEAR;
189                 break;
190             case rendering::CompositeOperation::SOURCE:
191                 compositingMode = CAIRO_OPERATOR_SOURCE;
192                 break;
193             case rendering::CompositeOperation::DESTINATION:
194                 compositingMode = CAIRO_OPERATOR_DEST;
195                 break;
196             case rendering::CompositeOperation::OVER:
197                 compositingMode = CAIRO_OPERATOR_OVER;
198                 break;
199             case rendering::CompositeOperation::UNDER:
200                 compositingMode = CAIRO_OPERATOR_DEST;
201                 break;
202             case rendering::CompositeOperation::INSIDE:
203                 compositingMode = CAIRO_OPERATOR_IN;
204                 break;
205             case rendering::CompositeOperation::INSIDE_REVERSE:
206                 compositingMode = CAIRO_OPERATOR_OUT;
207                 break;
208             case rendering::CompositeOperation::OUTSIDE:
209                 compositingMode = CAIRO_OPERATOR_DEST_OVER;
210                 break;
211             case rendering::CompositeOperation::OUTSIDE_REVERSE:
212                 compositingMode = CAIRO_OPERATOR_DEST_OUT;
213                 break;
214             case rendering::CompositeOperation::ATOP:
215                 compositingMode = CAIRO_OPERATOR_ATOP;
216                 break;
217             case rendering::CompositeOperation::ATOP_REVERSE:
218                 compositingMode = CAIRO_OPERATOR_DEST_ATOP;
219                 break;
220             case rendering::CompositeOperation::XOR:
221                 compositingMode = CAIRO_OPERATOR_XOR;
222                 break;
223             case rendering::CompositeOperation::ADD:
224                 compositingMode = CAIRO_OPERATOR_ADD;
225                 break;
226             case rendering::CompositeOperation::SATURATE:
227                 compositingMode = CAIRO_OPERATOR_SATURATE;
228                 break;
229         }
230         cairo_set_operator( mpCairo.get(), compositingMode );
231     }
232 
233     void CanvasHelper::clear()
234     {
235         OSL_TRACE ("clear whole area: %d x %d", maSize.getX(), maSize.getY() );
236 
237         if( mpCairo )
238         {
239             cairo_save( mpCairo.get() );
240 
241             cairo_identity_matrix( mpCairo.get() );
242             // this does not really differ from all-zero, as cairo
243             // internally converts to premultiplied alpha. but anyway,
244             // this keeps it consistent with the other canvas impls
245             if( mbHaveAlpha )
246                 cairo_set_source_rgba( mpCairo.get(), 1.0, 1.0, 1.0, 0.0 );
247             else
248                 cairo_set_source_rgb( mpCairo.get(), 1.0, 1.0, 1.0 );
249             cairo_set_operator( mpCairo.get(), CAIRO_OPERATOR_SOURCE );
250 
251 			cairo_rectangle( mpCairo.get(), 0, 0, maSize.getX(), maSize.getY() );
252             cairo_fill( mpCairo.get() );
253 
254             cairo_restore( mpCairo.get() );
255         }
256     }
257 
258     void CanvasHelper::drawPoint( const rendering::XCanvas* 	,
259                                   const geometry::RealPoint2D& 	,
260                                   const rendering::ViewState& 	,
261                                   const rendering::RenderState&	 )
262     {
263     }
264 
265     void CanvasHelper::drawLine( const rendering::XCanvas* 		/*pCanvas*/,
266                                  const geometry::RealPoint2D& 	aStartPoint,
267                                  const geometry::RealPoint2D& 	aEndPoint,
268                                  const rendering::ViewState& 	viewState,
269                                  const rendering::RenderState& 	renderState )
270     {
271 	if( mpCairo ) {
272 	    cairo_save( mpCairo.get() );
273 
274 	    cairo_set_line_width( mpCairo.get(), 1 );
275 
276 	    useStates( viewState, renderState, true );
277 
278 	    cairo_move_to( mpCairo.get(), aStartPoint.X + 0.5, aStartPoint.Y + 0.5 );
279 	    cairo_line_to( mpCairo.get(), aEndPoint.X + 0.5, aEndPoint.Y + 0.5 );
280 	    cairo_stroke( mpCairo.get() );
281 
282 	    cairo_restore( mpCairo.get() );
283 	}
284     }
285 
286     void CanvasHelper::drawBezier( const rendering::XCanvas* 			,
287                                    const geometry::RealBezierSegment2D&	aBezierSegment,
288                                    const geometry::RealPoint2D& 		aEndPoint,
289                                    const rendering::ViewState& 			viewState,
290                                    const rendering::RenderState& 		renderState )
291     {
292 	if( mpCairo ) {
293 	    cairo_save( mpCairo.get() );
294 
295 	    cairo_set_line_width( mpCairo.get(), 1 );
296 
297 	    useStates( viewState, renderState, true );
298 
299 	    cairo_move_to( mpCairo.get(), aBezierSegment.Px + 0.5, aBezierSegment.Py + 0.5 );
300         cairo_curve_to( mpCairo.get(),
301                         aBezierSegment.C1x + 0.5, aBezierSegment.C1y + 0.5,
302                         aBezierSegment.C2x + 0.5, aBezierSegment.C2y + 0.5,
303                         aEndPoint.X + 0.5, aEndPoint.Y + 0.5 );
304 	    cairo_stroke( mpCairo.get() );
305 
306 	    cairo_restore( mpCairo.get() );
307 	}
308     }
309 
310 #define CANVASBITMAP_IMPLEMENTATION_NAME "CairoCanvas::CanvasBitmap"
311 #define PARAMETRICPOLYPOLYGON_IMPLEMENTATION_NAME "Canvas::ParametricPolyPolygon"
312 
313 
314     /** surfaceFromXBitmap Create a surface from XBitmap
315      * @param xBitmap bitmap image that will be used for the surface
316      * @param bHasAlpha will be set to true if resulting surface has alpha
317      *
318      * This is a helper function for the other surfaceFromXBitmap().
319      * This function tries to create surface from xBitmap by checking if xBitmap is CanvasBitmap or SpriteCanvas.
320      *
321      * @return created surface or NULL
322      **/
323     static SurfaceSharedPtr surfaceFromXBitmap( const uno::Reference< rendering::XBitmap >& xBitmap )
324     {
325 		CanvasBitmap* pBitmapImpl = dynamic_cast< CanvasBitmap* >( xBitmap.get() );
326 		if( pBitmapImpl )
327 			return pBitmapImpl->getSurface();
328 
329         SurfaceProvider* pSurfaceProvider = dynamic_cast<SurfaceProvider*>( xBitmap.get() );
330         if( pSurfaceProvider )
331             return pSurfaceProvider->getSurface();
332 
333 		return SurfaceSharedPtr();
334     }
335 
336     static ::BitmapEx bitmapExFromXBitmap( const uno::Reference< rendering::XBitmap >& xBitmap )
337     {
338         // TODO(F1): Add support for floating point bitmap formats
339         uno::Reference<rendering::XIntegerReadOnlyBitmap> xIntBmp(xBitmap,
340                                                                   uno::UNO_QUERY_THROW);
341         ::BitmapEx aBmpEx = ::vcl::unotools::bitmapExFromXBitmap(xIntBmp);
342         if( !!aBmpEx )
343             return aBmpEx;
344 
345         // TODO(F1): extract pixel from XBitmap interface
346         ENSURE_OR_THROW( false,
347                          "bitmapExFromXBitmap(): could not extract BitmapEx" );
348 
349         return ::BitmapEx();
350     }
351 
352     static bool readAlpha( BitmapReadAccess* pAlphaReadAcc, long nY, const long nWidth, unsigned char* data, long nOff )
353     {
354 	bool bIsAlpha = false;
355 	long nX;
356 	int nAlpha;
357 	Scanline pReadScan;
358 
359 	nOff += 3;
360 
361 	switch( pAlphaReadAcc->GetScanlineFormat() ) {
362 	case BMP_FORMAT_8BIT_TC_MASK:
363 	    pReadScan = pAlphaReadAcc->GetScanline( nY );
364 	    for( nX = 0; nX < nWidth; nX++ ) {
365 		nAlpha = data[ nOff ] = 255 - ( *pReadScan++ );
366 		if( nAlpha != 255 )
367 		    bIsAlpha = true;
368 		nOff += 4;
369 	    }
370 	    break;
371 	case BMP_FORMAT_8BIT_PAL:
372 	    pReadScan = pAlphaReadAcc->GetScanline( nY );
373 	    for( nX = 0; nX < nWidth; nX++ ) {
374 		nAlpha = data[ nOff ] = 255 - ( pAlphaReadAcc->GetPaletteColor( *pReadScan++ ).GetBlue() );
375 		if( nAlpha != 255 )
376 		    bIsAlpha = true;
377 		nOff += 4;
378 	    }
379 	    break;
380 	default:
381 	    OSL_TRACE( "fallback to GetColor for alpha - slow, format: %d", pAlphaReadAcc->GetScanlineFormat() );
382 	    for( nX = 0; nX < nWidth; nX++ ) {
383 		nAlpha = data[ nOff ] = 255 - pAlphaReadAcc->GetColor( nY, nX ).GetBlue();
384 		if( nAlpha != 255 )
385 		    bIsAlpha = true;
386 		nOff += 4;
387 	    }
388 	}
389 
390 	return bIsAlpha;
391     }
392 
393 
394     /** surfaceFromXBitmap Create a surface from XBitmap
395      * @param xBitmap bitmap image that will be used for the surface
396      * @param rDevice reference to the device into which we want to draw
397      * @param data will be filled with alpha data, if xBitmap is alpha/transparent image
398      * @param bHasAlpha will be set to true if resulting surface has alpha
399      *
400      * This function tries various methods for creating a surface from xBitmap. It also uses
401      * the helper function surfaceFromXBitmap( xBitmap, bHasAlpha )
402      *
403      * @return created surface or NULL
404      **/
405     static SurfaceSharedPtr surfaceFromXBitmap( const uno::Reference< rendering::XBitmap >& xBitmap, const SurfaceProviderRef& rSurfaceProvider, unsigned char*& data, bool& bHasAlpha )
406     {
407         bHasAlpha = xBitmap->hasAlpha();
408 		SurfaceSharedPtr pSurface = surfaceFromXBitmap( xBitmap );
409 		if( pSurface )
410 			data = NULL;
411 		else
412         {
413 			::BitmapEx aBmpEx = bitmapExFromXBitmap(xBitmap);
414 			::Bitmap aBitmap = aBmpEx.GetBitmap();
415 
416 			// there's no pixmap for alpha bitmap. we might still
417 			// use rgb pixmap and only access alpha pixels the
418 			// slow way. now we just speedup rgb bitmaps
419 			if( !aBmpEx.IsTransparent() && !aBmpEx.IsAlpha() ) {
420 				pSurface = rSurfaceProvider->createSurface( aBitmap );
421 				data = NULL;
422 				bHasAlpha = false;
423 			}
424 
425 			if( !pSurface ) {
426                 AlphaMask aAlpha = aBmpEx.GetAlpha();
427 
428 				::BitmapReadAccess*	pBitmapReadAcc = aBitmap.AcquireReadAccess();
429 				::BitmapReadAccess*	pAlphaReadAcc = NULL;
430 				const long		nWidth = pBitmapReadAcc->Width();
431 				const long		nHeight = pBitmapReadAcc->Height();
432 				long nX, nY;
433 				bool bIsAlpha = false;
434 
435 				if( aBmpEx.IsTransparent() || aBmpEx.IsAlpha() )
436 					pAlphaReadAcc = aAlpha.AcquireReadAccess();
437 
438 				data = (unsigned char*) malloc( nWidth*nHeight*4 );
439 
440 				long nOff = 0;
441 				::Color aColor;
442 				unsigned int nAlpha = 255;
443 
444 				for( nY = 0; nY < nHeight; nY++ ) {
445 					::Scanline pReadScan;
446 
447 					switch( pBitmapReadAcc->GetScanlineFormat() ) {
448 					case BMP_FORMAT_8BIT_PAL:
449 						pReadScan = pBitmapReadAcc->GetScanline( nY );
450 						if( pAlphaReadAcc )
451 							if( readAlpha( pAlphaReadAcc, nY, nWidth, data, nOff ) )
452 								bIsAlpha = true;
453 
454 						for( nX = 0; nX < nWidth; nX++ ) {
455 #ifdef OSL_BIGENDIAN
456 							if( pAlphaReadAcc )
457 								nAlpha = data[ nOff++ ];
458 							else
459 								nAlpha = data[ nOff++ ] = 255;
460 #else
461 							if( pAlphaReadAcc )
462 								nAlpha = data[ nOff + 3 ];
463 							else
464 								nAlpha = data[ nOff + 3 ] = 255;
465 #endif
466 							aColor = pBitmapReadAcc->GetPaletteColor( *pReadScan++ );
467 
468 #ifdef OSL_BIGENDIAN
469 							data[ nOff++ ] = sal::static_int_cast<unsigned char>(( nAlpha*( aColor.GetRed() ) )/255 );
470 							data[ nOff++ ] = sal::static_int_cast<unsigned char>(( nAlpha*( aColor.GetGreen() ) )/255 );
471 							data[ nOff++ ] = sal::static_int_cast<unsigned char>(( nAlpha*( aColor.GetBlue() ) )/255 );
472 #else
473 							data[ nOff++ ] = sal::static_int_cast<unsigned char>(( nAlpha*( aColor.GetBlue() ) )/255 );
474 							data[ nOff++ ] = sal::static_int_cast<unsigned char>(( nAlpha*( aColor.GetGreen() ) )/255 );
475 							data[ nOff++ ] = sal::static_int_cast<unsigned char>(( nAlpha*( aColor.GetRed() ) )/255 );
476 							nOff++;
477 #endif
478 						}
479 						break;
480 					case BMP_FORMAT_24BIT_TC_BGR:
481 						pReadScan = pBitmapReadAcc->GetScanline( nY );
482 						if( pAlphaReadAcc )
483 							if( readAlpha( pAlphaReadAcc, nY, nWidth, data, nOff ) )
484 								bIsAlpha = true;
485 
486 						for( nX = 0; nX < nWidth; nX++ ) {
487 #ifdef OSL_BIGENDIAN
488 							if( pAlphaReadAcc )
489 								nAlpha = data[ nOff ];
490 							else
491 								nAlpha = data[ nOff ] = 255;
492 							data[ nOff + 3 ] = sal::static_int_cast<unsigned char>(( nAlpha*( *pReadScan++ ) )/255 );
493 							data[ nOff + 2 ] = sal::static_int_cast<unsigned char>(( nAlpha*( *pReadScan++ ) )/255 );
494 							data[ nOff + 1 ] = sal::static_int_cast<unsigned char>(( nAlpha*( *pReadScan++ ) )/255 );
495 							nOff += 4;
496 #else
497 							if( pAlphaReadAcc )
498 								nAlpha = data[ nOff + 3 ];
499 							else
500 								nAlpha = data[ nOff + 3 ] = 255;
501 							data[ nOff++ ] = sal::static_int_cast<unsigned char>(( nAlpha*( *pReadScan++ ) )/255 );
502 							data[ nOff++ ] = sal::static_int_cast<unsigned char>(( nAlpha*( *pReadScan++ ) )/255 );
503 							data[ nOff++ ] = sal::static_int_cast<unsigned char>(( nAlpha*( *pReadScan++ ) )/255 );
504 							nOff++;
505 #endif
506 						}
507 						break;
508 					case BMP_FORMAT_24BIT_TC_RGB:
509 						pReadScan = pBitmapReadAcc->GetScanline( nY );
510 						if( pAlphaReadAcc )
511 							if( readAlpha( pAlphaReadAcc, nY, nWidth, data, nOff ) )
512 								bIsAlpha = true;
513 
514 						for( nX = 0; nX < nWidth; nX++ ) {
515 #ifdef OSL_BIGENDIAN
516 							if( pAlphaReadAcc )
517 								nAlpha = data[ nOff++ ];
518 							else
519 								nAlpha = data[ nOff++ ] = 255;
520 							data[ nOff++ ] = sal::static_int_cast<unsigned char>(( nAlpha*( *pReadScan++ ) )/255 );
521 							data[ nOff++ ] = sal::static_int_cast<unsigned char>(( nAlpha*( *pReadScan++ ) )/255 );
522 							data[ nOff++ ] = sal::static_int_cast<unsigned char>(( nAlpha*( *pReadScan++ ) )/255 );
523 #else
524 							if( pAlphaReadAcc )
525 								nAlpha = data[ nOff + 3 ];
526 							else
527 								nAlpha = data[ nOff + 3 ] = 255;
528 							data[ nOff++ ] = sal::static_int_cast<unsigned char>(( nAlpha*( pReadScan[ 2 ] ) )/255 );
529 							data[ nOff++ ] = sal::static_int_cast<unsigned char>(( nAlpha*( pReadScan[ 1 ] ) )/255 );
530 							data[ nOff++ ] = sal::static_int_cast<unsigned char>(( nAlpha*( pReadScan[ 0 ] ) )/255 );
531 							pReadScan += 3;
532 							nOff++;
533 #endif
534 						}
535 						break;
536 					case BMP_FORMAT_32BIT_TC_BGRA:
537 						pReadScan = pBitmapReadAcc->GetScanline( nY );
538 						if( pAlphaReadAcc )
539 							if( readAlpha( pAlphaReadAcc, nY, nWidth, data, nOff ) )
540 								bIsAlpha = true;
541 
542 						for( nX = 0; nX < nWidth; nX++ ) {
543 #ifdef OSL_BIGENDIAN
544 							if( pAlphaReadAcc )
545 								nAlpha = data[ nOff++ ];
546 							else
547 								nAlpha = data[ nOff++ ] = pReadScan[ 3 ];
548 							data[ nOff++ ] = sal::static_int_cast<unsigned char>(( nAlpha*( pReadScan[ 2 ] ) )/255 );
549 							data[ nOff++ ] = sal::static_int_cast<unsigned char>(( nAlpha*( pReadScan[ 1 ] ) )/255 );
550 							data[ nOff++ ] = sal::static_int_cast<unsigned char>(( nAlpha*( pReadScan[ 0 ] ) )/255 );
551 							pReadScan += 4;
552 #else
553 							if( pAlphaReadAcc )
554 								nAlpha = data[ nOff + 3 ];
555 							else
556 								nAlpha = data[ nOff + 3 ] = pReadScan[ 3 ];
557 							data[ nOff++ ] = sal::static_int_cast<unsigned char>(( nAlpha*( *pReadScan++ ) )/255 );
558 							data[ nOff++ ] = sal::static_int_cast<unsigned char>(( nAlpha*( *pReadScan++ ) )/255 );
559 							data[ nOff++ ] = sal::static_int_cast<unsigned char>(( nAlpha*( *pReadScan++ ) )/255 );
560 							pReadScan++;
561 							nOff++;
562 #endif
563 						}
564 						break;
565 					case BMP_FORMAT_32BIT_TC_RGBA:
566 						pReadScan = pBitmapReadAcc->GetScanline( nY );
567 						if( pAlphaReadAcc )
568 							if( readAlpha( pAlphaReadAcc, nY, nWidth, data, nOff ) )
569 								bIsAlpha = true;
570 
571 						for( nX = 0; nX < nWidth; nX++ ) {
572 #ifdef OSL_BIGENDIAN
573 							if( pAlphaReadAcc )
574 								nAlpha = data[ nOff ++ ];
575 							else
576 								nAlpha = data[ nOff ++ ] = 255;
577 							data[ nOff++ ] = sal::static_int_cast<unsigned char>(( nAlpha*( *pReadScan++ ) )/255 );
578 							data[ nOff++ ] = sal::static_int_cast<unsigned char>(( nAlpha*( *pReadScan++ ) )/255 );
579 							data[ nOff++ ] = sal::static_int_cast<unsigned char>(( nAlpha*( *pReadScan++ ) )/255 );
580 							pReadScan++;
581 #else
582 							if( pAlphaReadAcc )
583 								nAlpha = data[ nOff + 3 ];
584 							else
585 								nAlpha = data[ nOff + 3 ] = 255;
586 							data[ nOff++ ] = sal::static_int_cast<unsigned char>(( nAlpha*( pReadScan[ 2 ] ) )/255 );
587 							data[ nOff++ ] = sal::static_int_cast<unsigned char>(( nAlpha*( pReadScan[ 1 ] ) )/255 );
588 							data[ nOff++ ] = sal::static_int_cast<unsigned char>(( nAlpha*( pReadScan[ 0 ] ) )/255 );
589 							pReadScan += 4;
590 							nOff++;
591 #endif
592 						}
593 						break;
594 					default:
595 						OSL_TRACE( "fallback to GetColor - slow, format: %d", pBitmapReadAcc->GetScanlineFormat() );
596 
597 						if( pAlphaReadAcc )
598 							if( readAlpha( pAlphaReadAcc, nY, nWidth, data, nOff ) )
599 								bIsAlpha = true;
600 
601 						for( nX = 0; nX < nWidth; nX++ ) {
602 							aColor = pBitmapReadAcc->GetColor( nY, nX );
603 
604 							// cairo need premultiplied color values
605 							// TODO(rodo) handle endianess
606 #ifdef OSL_BIGENDIAN
607 							if( pAlphaReadAcc )
608 								nAlpha = data[ nOff++ ];
609 							else
610 								nAlpha = data[ nOff++ ] = 255;
611 							data[ nOff++ ] = sal::static_int_cast<unsigned char>(( nAlpha*aColor.GetRed() )/255 );
612 							data[ nOff++ ] = sal::static_int_cast<unsigned char>(( nAlpha*aColor.GetGreen() )/255 );
613                             data[ nOff++ ] = sal::static_int_cast<unsigned char>(( nAlpha*aColor.GetBlue() )/255 );
614 #else
615 							if( pAlphaReadAcc )
616 								nAlpha = data[ nOff + 3 ];
617 							else
618 								nAlpha = data[ nOff + 3 ] = 255;
619 							data[ nOff++ ] = sal::static_int_cast<unsigned char>(( nAlpha*aColor.GetBlue() )/255 );
620 							data[ nOff++ ] = sal::static_int_cast<unsigned char>(( nAlpha*aColor.GetGreen() )/255 );
621 							data[ nOff++ ] = sal::static_int_cast<unsigned char>(( nAlpha*aColor.GetRed() )/255 );
622 							nOff ++;
623 #endif
624 						}
625 					}
626 				}
627 
628 				aBitmap.ReleaseAccess( pBitmapReadAcc );
629 				if( pAlphaReadAcc )
630 					aAlpha.ReleaseAccess( pAlphaReadAcc );
631 
632 				SurfaceSharedPtr pImageSurface = createSurface(
633                     CairoSurfaceSharedPtr(
634                         cairo_image_surface_create_for_data(
635                             data,
636                             bIsAlpha ? CAIRO_FORMAT_ARGB32 : CAIRO_FORMAT_RGB24,
637                             nWidth, nHeight, nWidth*4 ),
638                         &cairo_surface_destroy) );
639 
640 				// 		pSurface = rSurfaceProvider->getSurface( ::basegfx::B2ISize( nWidth, nHeight ), bIsAlpha ? CAIRO_CONTENT_COLOR_ALPHA : CAIRO_CONTENT_COLOR );
641 				// 		Cairo* pTargetCairo = cairo_create( pSurface );
642 				// 		cairo_set_source_surface( pTargetCairo, pImageSurface, 0, 0 );
643 
644 				// 				    //if( !bIsAlpha )
645 				// 				    //cairo_set_operator( pTargetCairo, CAIRO_OPERATOR_SOURCE );
646 
647 				// 		cairo_paint( pTargetCairo );
648 				// 		cairo_destroy( pTargetCairo );
649 				// 		cairo_surface_destroy( pImageSurface );
650 				pSurface = pImageSurface;
651 
652 				bHasAlpha = bIsAlpha;
653 
654 				OSL_TRACE("image: %d x %d alpha: %d alphaRead %p", nWidth, nHeight, bIsAlpha, pAlphaReadAcc);
655 			}
656 		}
657 
658 		return pSurface;
659     }
660 
661     static void addColorStops( Pattern* pPattern, const uno::Sequence< uno::Sequence< double > >& rColors, const uno::Sequence< double >& rStops, bool bReverseStops = false )
662     {
663         float stop;
664         int i;
665 
666         OSL_ASSERT( rColors.getLength() == rStops.getLength() );
667 
668         for( i = 0; i < rColors.getLength(); i++ ) {
669             const uno::Sequence< double >& rColor( rColors[i] );
670             stop = bReverseStops ? 1 - rStops[i] : rStops[i];
671             if( rColor.getLength() == 3 )
672                 cairo_pattern_add_color_stop_rgb( pPattern, stop, rColor[0], rColor[1], rColor[2] );
673             else if( rColor.getLength() == 4 ) {
674                 double alpha = rColor[3];
675                 // cairo expects premultiplied alpha
676                 cairo_pattern_add_color_stop_rgba( pPattern, stop, rColor[0]*alpha, rColor[1]*alpha, rColor[2]*alpha, alpha );
677             }
678         }
679     }
680 
681     static uno::Sequence<double> lerp(const uno::Sequence<double>& rLeft, const uno::Sequence<double>& rRight, double fAlpha)
682     {
683         if( rLeft.getLength() == 3 )
684         {
685             uno::Sequence<double> aRes(3);
686             aRes[0] = basegfx::tools::lerp(rLeft[0],rRight[0],fAlpha);
687             aRes[1] = basegfx::tools::lerp(rLeft[1],rRight[1],fAlpha);
688             aRes[2] = basegfx::tools::lerp(rLeft[2],rRight[2],fAlpha);
689             return aRes;
690         }
691         else if( rLeft.getLength() == 4 )
692         {
693             uno::Sequence<double> aRes(4);
694             aRes[0] = basegfx::tools::lerp(rLeft[0],rRight[0],fAlpha);
695             aRes[1] = basegfx::tools::lerp(rLeft[1],rRight[1],fAlpha);
696             aRes[2] = basegfx::tools::lerp(rLeft[2],rRight[2],fAlpha);
697             aRes[3] = basegfx::tools::lerp(rLeft[3],rRight[3],fAlpha);
698             return aRes;
699         }
700 
701         return uno::Sequence<double>();
702     }
703 
704     static Pattern* patternFromParametricPolyPolygon( ::canvas::ParametricPolyPolygon& rPolygon )
705     {
706 	Pattern* pPattern = NULL;
707 	const ::canvas::ParametricPolyPolygon::Values aValues = rPolygon.getValues();
708 	double x0, x1, y0, y1, cx, cy, r0, r1;
709 
710 // undef macros from vclenum.hxx which conflicts with GradientType enum values
711 #undef GRADIENT_LINEAR
712 #undef GRADIENT_ELLIPTICAL
713 
714 	switch( aValues.meType ) {
715 	case ::canvas::ParametricPolyPolygon::GRADIENT_LINEAR:
716 	    x0 = 0;
717 	    y0 = 0;
718 	    x1 = 1;
719 	    y1 = 0;
720 	    pPattern = cairo_pattern_create_linear( x0, y0, x1, y1 );
721 	    addColorStops( pPattern, aValues.maColors, aValues.maStops );
722 	    break;
723 
724 	case ::canvas::ParametricPolyPolygon::GRADIENT_ELLIPTICAL:
725 	    cx = 0;
726 	    cy = 0;
727 	    r0 = 0;
728 	    r1 = 1;
729 
730 	    pPattern = cairo_pattern_create_radial( cx, cy, r0, cy, cy, r1 );
731 	    addColorStops( pPattern, aValues.maColors, aValues.maStops, true );
732 	    break;
733     default:
734         break;
735 	}
736 
737 	return pPattern;
738     }
739 
740     static void doOperation( Operation aOperation,
741                              Cairo* pCairo,
742                              const uno::Sequence< rendering::Texture >* pTextures,
743                              const SurfaceProviderRef& pDevice,
744                              const basegfx::B2DRange& rBounds )
745     {
746 	switch( aOperation ) {
747 	case Fill:
748 		/* TODO: multitexturing */
749 	    if( pTextures ) {
750 		const ::com::sun::star::rendering::Texture& aTexture ( (*pTextures)[0] );
751 		if( aTexture.Bitmap.is() ) {
752 		    unsigned char* data = NULL;
753 		    bool bHasAlpha = false;
754 		    SurfaceSharedPtr pSurface = surfaceFromXBitmap( (*pTextures)[0].Bitmap, pDevice, data, bHasAlpha );
755 
756 		    if( pSurface ) {
757 			cairo_pattern_t* pPattern;
758 
759 			cairo_save( pCairo );
760 
761 			::com::sun::star::geometry::AffineMatrix2D aTransform( aTexture.AffineTransform );
762 			Matrix aScaleMatrix, aTextureMatrix, aScaledTextureMatrix;
763 
764 			cairo_matrix_init( &aTextureMatrix,
765 					   aTransform.m00, aTransform.m10, aTransform.m01,
766 					   aTransform.m11, aTransform.m02, aTransform.m12);
767 
768 			geometry::IntegerSize2D aSize = aTexture.Bitmap->getSize();
769 
770 			cairo_matrix_init_scale( &aScaleMatrix, 1.0/aSize.Width, 1.0/aSize.Height );
771 			cairo_matrix_multiply( &aScaledTextureMatrix, &aTextureMatrix, &aScaleMatrix );
772 			cairo_matrix_invert( &aScaledTextureMatrix );
773 
774 			// we don't care about repeat mode yet, so the workaround is disabled for now
775 			pPattern = cairo_pattern_create_for_surface( pSurface->getCairoSurface().get() );
776 
777  			if( aTexture.RepeatModeX == rendering::TexturingMode::REPEAT &&
778 			    aTexture.RepeatModeY == rendering::TexturingMode::REPEAT )
779             {
780 			    cairo_pattern_set_extend( pPattern, CAIRO_EXTEND_REPEAT );
781             }
782             else if ( aTexture.RepeatModeX == rendering::TexturingMode::NONE &&
783                       aTexture.RepeatModeY == rendering::TexturingMode::NONE )
784             {
785 			    cairo_pattern_set_extend( pPattern, CAIRO_EXTEND_NONE );
786             }
787             else if ( aTexture.RepeatModeX == rendering::TexturingMode::CLAMP &&
788                       aTexture.RepeatModeY == rendering::TexturingMode::CLAMP )
789             {
790 			    cairo_pattern_set_extend( pPattern, CAIRO_EXTEND_PAD );
791             }
792 
793 			aScaledTextureMatrix.x0 = basegfx::fround( aScaledTextureMatrix.x0 );
794 			aScaledTextureMatrix.y0 = basegfx::fround( aScaledTextureMatrix.y0 );
795 			cairo_pattern_set_matrix( pPattern, &aScaledTextureMatrix );
796 
797 			cairo_set_source( pCairo, pPattern );
798 			if( !bHasAlpha )
799 			    cairo_set_operator( pCairo, CAIRO_OPERATOR_SOURCE );
800 			cairo_fill( pCairo );
801 
802 			cairo_restore( pCairo );
803 
804 			cairo_pattern_destroy( pPattern );
805 		    }
806 
807 		    if( data )
808 			free( data );
809 		} else if( aTexture.Gradient.is() ) {
810 		    uno::Reference< lang::XServiceInfo > xRef( aTexture.Gradient, uno::UNO_QUERY );
811 
812 		    OSL_TRACE( "gradient fill" );
813 		    if( xRef.is() &&
814 			xRef->getImplementationName().equals( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( PARAMETRICPOLYPOLYGON_IMPLEMENTATION_NAME ) ) ) ) {
815 				// TODO(Q1): Maybe use dynamic_cast here
816 
817 				// TODO(E1): Return value
818 				// TODO(F1): FillRule
819 			OSL_TRACE( "known implementation" );
820 
821 			::canvas::ParametricPolyPolygon* pPolyImpl = static_cast< ::canvas::ParametricPolyPolygon* >( aTexture.Gradient.get() );
822 			::com::sun::star::geometry::AffineMatrix2D aTransform( aTexture.AffineTransform );
823 			Matrix aTextureMatrix;
824 
825 			cairo_matrix_init( &aTextureMatrix,
826 					   aTransform.m00, aTransform.m10, aTransform.m01,
827 					   aTransform.m11, aTransform.m02, aTransform.m12);
828             if( pPolyImpl->getValues().meType == canvas::ParametricPolyPolygon::GRADIENT_RECTANGULAR )
829             {
830                 // no general path gradient yet in cairo; emulate then
831                 cairo_save( pCairo );
832                 cairo_clip( pCairo );
833 
834                 // fill bound rect with start color
835                 cairo_rectangle( pCairo, rBounds.getMinX(), rBounds.getMinY(),
836                                  rBounds.getWidth(), rBounds.getHeight() );
837                 setColor(pCairo,pPolyImpl->getValues().maColors[0]);
838                 cairo_fill(pCairo);
839 
840                 cairo_transform( pCairo, &aTextureMatrix );
841 
842                 // longest line in gradient bound rect
843                 const unsigned int nGradientSize(
844                     static_cast<unsigned int>(
845                         ::basegfx::B2DVector(rBounds.getMinimum() - rBounds.getMaximum()).getLength() + 1.0 ) );
846 
847                 // typical number for pixel of the same color (strip size)
848                 const unsigned int nStripSize( nGradientSize < 50 ? 2 : 4 );
849 
850                 // use at least three steps, and at utmost the number of color
851                 // steps
852                 const unsigned int nStepCount(
853                     ::std::max(
854                         3U,
855                         ::std::min(
856                             nGradientSize / nStripSize,
857                             128U )) + 1 );
858 
859                 const uno::Sequence<double>* pColors=&pPolyImpl->getValues().maColors[0];
860                 basegfx::tools::KeyStopLerp aLerper(pPolyImpl->getValues().maStops);
861                 for( unsigned int i=1; i<nStepCount; ++i )
862                 {
863                     const double fT( i/double(nStepCount) );
864 
865                     std::ptrdiff_t nIndex;
866                     double fAlpha;
867                     boost::tuples::tie(nIndex,fAlpha)=aLerper.lerp(fT);
868 
869                     setColor(pCairo, lerp(pColors[nIndex], pColors[nIndex+1], fAlpha));
870                     cairo_rectangle( pCairo, -1+fT, -1+fT, 2-2*fT, 2-2*fT );
871                     cairo_fill(pCairo);
872                 }
873 
874                 cairo_restore( pCairo );
875             }
876             else
877             {
878                 Pattern* pPattern = patternFromParametricPolyPolygon( *pPolyImpl );
879 
880                 if( pPattern ) {
881                     OSL_TRACE( "filling with pattern" );
882 
883                     cairo_save( pCairo );
884 
885                     cairo_transform( pCairo, &aTextureMatrix );
886                     cairo_set_source( pCairo, pPattern );
887                     cairo_fill( pCairo );
888                     cairo_restore( pCairo );
889 
890                     cairo_pattern_destroy( pPattern );
891                 }
892 		    }
893             }
894 		}
895 	    } else
896 		cairo_fill( pCairo );
897 	    OSL_TRACE("fill");
898 	break;
899 	case Stroke:
900 	    cairo_stroke( pCairo );
901 	    OSL_TRACE("stroke");
902 	break;
903 	case Clip:
904 	    cairo_clip( pCairo );
905 	    OSL_TRACE("clip");
906 	break;
907 	}
908     }
909 
910     static void clipNULL( Cairo *pCairo )
911     {
912 	OSL_TRACE("clipNULL");
913 	Matrix aOrigMatrix, aIdentityMatrix;
914 
915 	/* we set identity matrix here to overcome bug in cairo 0.9.2
916 	   where XCreatePixmap is called with zero width and height.
917 
918 	   it also reaches faster path in cairo clipping code.
919 	*/
920 	cairo_matrix_init_identity( &aIdentityMatrix );
921 	cairo_get_matrix( pCairo, &aOrigMatrix );
922 	cairo_set_matrix( pCairo, &aIdentityMatrix );
923 
924 	cairo_reset_clip( pCairo );
925 	cairo_rectangle( pCairo, 0, 0, 1, 1 );
926 	cairo_clip( pCairo );
927 	cairo_rectangle( pCairo, 2, 0, 1, 1 );
928 	cairo_clip( pCairo );
929 
930 	/* restore the original matrix */
931 	cairo_set_matrix( pCairo, &aOrigMatrix );
932     }
933 
934     void doPolyPolygonImplementation( ::basegfx::B2DPolyPolygon aPolyPolygon,
935                                       Operation aOperation,
936                                       Cairo* pCairo,
937                                       const uno::Sequence< rendering::Texture >* pTextures,
938                                       const SurfaceProviderRef& pDevice,
939                                       rendering::FillRule eFillrule )
940     {
941 		if( pTextures )
942 			ENSURE_ARG_OR_THROW( pTextures->getLength(),
943 							 "CanvasHelper::fillTexturedPolyPolygon: empty texture sequence");
944 
945 	bool bOpToDo = false;
946 	Matrix aOrigMatrix, aIdentityMatrix;
947 	double nX, nY, nBX, nBY, nAX, nAY;
948 
949 	cairo_get_matrix( pCairo, &aOrigMatrix );
950 	cairo_matrix_init_identity( &aIdentityMatrix );
951 	cairo_set_matrix( pCairo, &aIdentityMatrix );
952 
953     cairo_set_fill_rule( pCairo,
954                          eFillrule == rendering::FillRule_EVEN_ODD ?
955                          CAIRO_FILL_RULE_EVEN_ODD : CAIRO_FILL_RULE_WINDING );
956 
957 	for( sal_uInt32 nPolygonIndex = 0; nPolygonIndex < aPolyPolygon.count(); nPolygonIndex++ ) {
958 	    ::basegfx::B2DPolygon aPolygon( aPolyPolygon.getB2DPolygon( nPolygonIndex ) );
959 	    const sal_uInt32 nPointCount( aPolygon.count() );
960         // to correctly render closed curves, need to output first
961         // point twice (so output one additional point)
962 	    const sal_uInt32 nExtendedPointCount( nPointCount +
963                                               aPolygon.isClosed()*aPolygon.areControlPointsUsed() );
964 
965 	    if( nPointCount > 1) {
966 		bool bIsBezier = aPolygon.areControlPointsUsed();
967         bool bIsRectangle = ::basegfx::tools::isRectangle( aPolygon );
968 		::basegfx::B2DPoint aA, aB, aP;
969 
970 		for( sal_uInt32 j=0; j < nExtendedPointCount; j++ ) {
971 		    aP = aPolygon.getB2DPoint( j % nPointCount );
972 
973 		    nX = aP.getX();
974 		    nY = aP.getY();
975 		    cairo_matrix_transform_point( &aOrigMatrix, &nX, &nY );
976 
977  		    if( ! bIsBezier && (bIsRectangle || aOperation == Clip) ) {
978                 nX = basegfx::fround( nX );
979                 nY = basegfx::fround( nY );
980 		    }
981 
982 		    if( aOperation == Stroke ) {
983                 nX += 0.5;
984                 nY += 0.5;
985 		    }
986 
987             if( j==0 )
988             {
989                 cairo_move_to( pCairo, nX, nY );
990                 OSL_TRACE( "move to %f,%f", nX, nY );
991             }
992             else {
993                 if( bIsBezier ) {
994                     aA = aPolygon.getNextControlPoint( (j-1) % nPointCount );
995                     aB = aPolygon.getPrevControlPoint( j % nPointCount );
996 
997                     nAX = aA.getX();
998                     nAY = aA.getY();
999                     nBX = aB.getX();
1000                     nBY = aB.getY();
1001 
1002                     if( aOperation == Stroke ) {
1003                         nAX += 0.5;
1004                         nAY += 0.5;
1005                         nBX += 0.5;
1006                         nBY += 0.5;
1007                     }
1008                     cairo_matrix_transform_point( &aOrigMatrix, &nAX, &nAY );
1009                     cairo_matrix_transform_point( &aOrigMatrix, &nBX, &nBY );
1010                     cairo_curve_to( pCairo, nAX, nAY, nBX, nBY, nX, nY );
1011                 } else {
1012                     cairo_line_to( pCairo, nX, nY );
1013                     OSL_TRACE( "line to %f,%f", nX, nY );
1014                 }
1015                 bOpToDo = true;
1016             }
1017 		}
1018 
1019 		if( aPolygon.isClosed() )
1020 		    cairo_close_path( pCairo );
1021 
1022 		if( aOperation == Fill && pTextures ) {
1023 		    cairo_set_matrix( pCairo, &aOrigMatrix );
1024 		    doOperation( aOperation, pCairo, pTextures, pDevice, aPolyPolygon.getB2DRange() );
1025 		    cairo_set_matrix( pCairo, &aIdentityMatrix );
1026 		}
1027 	    } else {
1028 		OSL_TRACE( "empty polygon for op: %d\n", aOperation );
1029 		if( aOperation == Clip ) {
1030 		    clipNULL( pCairo );
1031 
1032 		    return;
1033 		}
1034 	    }
1035 	}
1036 	if( bOpToDo && ( aOperation != Fill || !pTextures ) )
1037 	    doOperation( aOperation, pCairo, pTextures, pDevice, aPolyPolygon.getB2DRange() );
1038 
1039 	cairo_set_matrix( pCairo, &aOrigMatrix );
1040 
1041 	if( aPolyPolygon.count() == 0 && aOperation == Clip )
1042  	    clipNULL( pCairo );
1043     }
1044 
1045     void CanvasHelper::doPolyPolygonPath( const uno::Reference< rendering::XPolyPolygon2D >& xPolyPolygon,
1046 					    Operation aOperation,
1047                         bool bNoLineJoin,
1048 					    const uno::Sequence< rendering::Texture >* pTextures,
1049 					    Cairo* pCairo ) const
1050     {
1051         const ::basegfx::B2DPolyPolygon& rPolyPoly(
1052             ::basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D(xPolyPolygon) );
1053 
1054         if( !pCairo )
1055             pCairo = mpCairo.get();
1056 
1057         if(bNoLineJoin && Stroke == aOperation)
1058         {
1059             // emulate rendering::PathJoinType::NONE by painting single edges
1060             for(sal_uInt32 a(0); a < rPolyPoly.count(); a++)
1061             {
1062                 const basegfx::B2DPolygon aCandidate(rPolyPoly.getB2DPolygon(a));
1063                 const sal_uInt32 nPointCount(aCandidate.count());
1064 
1065                 if(nPointCount)
1066                 {
1067                     const sal_uInt32 nEdgeCount(aCandidate.isClosed() ? nPointCount + 1: nPointCount);
1068                     basegfx::B2DPolygon aEdge;
1069                     aEdge.append(aCandidate.getB2DPoint(0));
1070                     aEdge.append(basegfx::B2DPoint(0.0, 0.0));
1071 
1072                     for(sal_uInt32 b(0); b < nEdgeCount; b++)
1073 					{
1074 						const sal_uInt32 nNextIndex((b + 1) % nPointCount);
1075                         aEdge.setB2DPoint(1, aCandidate.getB2DPoint(nNextIndex));
1076                         aEdge.setNextControlPoint(0, aCandidate.getNextControlPoint(b));
1077                         aEdge.setPrevControlPoint(1, aCandidate.getPrevControlPoint(nNextIndex));
1078 
1079                         doPolyPolygonImplementation( basegfx::B2DPolyPolygon(aEdge),
1080                                                      aOperation,
1081                                                      pCairo, pTextures,
1082                                                      mpSurfaceProvider,
1083                                                      xPolyPolygon->getFillRule() );
1084 
1085                         // prepare next step
1086                         aEdge.setB2DPoint(0, aEdge.getB2DPoint(1));
1087 					}
1088                 }
1089             }
1090         }
1091         else
1092         {
1093             doPolyPolygonImplementation( rPolyPoly, aOperation,
1094                                          pCairo, pTextures,
1095                                          mpSurfaceProvider,
1096                                          xPolyPolygon->getFillRule() );
1097         }
1098     }
1099 
1100     uno::Reference< rendering::XCachedPrimitive > CanvasHelper::drawPolyPolygon( const rendering::XCanvas* 							,
1101                                                                                  const uno::Reference< rendering::XPolyPolygon2D >& xPolyPolygon,
1102                                                                                  const rendering::ViewState& 						viewState,
1103                                                                                  const rendering::RenderState& 						renderState )
1104     {
1105 #ifdef CAIRO_CANVAS_PERF_TRACE
1106         struct timespec aTimer;
1107         mxDevice->startPerfTrace( &aTimer );
1108 #endif
1109 
1110         if( mpCairo ) {
1111             cairo_save( mpCairo.get() );
1112 
1113             cairo_set_line_width( mpCairo.get(), 1 );
1114 
1115             useStates( viewState, renderState, true );
1116             doPolyPolygonPath( xPolyPolygon, Stroke );
1117 
1118             cairo_restore( mpCairo.get() );
1119         } else
1120             OSL_TRACE ("CanvasHelper called after it was disposed");
1121 
1122 #ifdef CAIRO_CANVAS_PERF_TRACE
1123         mxDevice->stopPerfTrace( &aTimer, "drawPolyPolygon" );
1124 #endif
1125 
1126         return uno::Reference< rendering::XCachedPrimitive >(NULL);
1127     }
1128 
1129     uno::Reference< rendering::XCachedPrimitive > CanvasHelper::strokePolyPolygon( const rendering::XCanvas* 							,
1130                                                                                    const uno::Reference< rendering::XPolyPolygon2D >& 	xPolyPolygon,
1131                                                                                    const rendering::ViewState& 							viewState,
1132                                                                                    const rendering::RenderState& 						renderState,
1133                                                                                    const rendering::StrokeAttributes& 					strokeAttributes )
1134     {
1135 	#ifdef CAIRO_CANVAS_PERF_TRACE
1136 	struct timespec aTimer;
1137 	mxDevice->startPerfTrace( &aTimer );
1138         #endif
1139 
1140 	if( mpCairo ) {
1141 	    cairo_save( mpCairo.get() );
1142 
1143 	    useStates( viewState, renderState, true );
1144 
1145         Matrix aMatrix;
1146         double w = strokeAttributes.StrokeWidth, h = 0;
1147         cairo_get_matrix( mpCairo.get(), &aMatrix );
1148         cairo_matrix_transform_distance( &aMatrix, &w, &h );
1149  	    cairo_set_line_width( mpCairo.get(), w );
1150 
1151 	    cairo_set_miter_limit( mpCairo.get(), strokeAttributes.MiterLimit );
1152 
1153 	    // FIXME: cairo doesn't handle end cap so far (rodo)
1154 	    switch( strokeAttributes.StartCapType ) {
1155             case rendering::PathCapType::BUTT:
1156                 cairo_set_line_cap( mpCairo.get(), CAIRO_LINE_CAP_BUTT );
1157                 break;
1158             case rendering::PathCapType::ROUND:
1159                 cairo_set_line_cap( mpCairo.get(), CAIRO_LINE_CAP_ROUND );
1160                 break;
1161             case rendering::PathCapType::SQUARE:
1162                 cairo_set_line_cap( mpCairo.get(), CAIRO_LINE_CAP_SQUARE );
1163                 break;
1164 	    }
1165 
1166         bool bNoLineJoin(false);
1167 
1168 	    switch( strokeAttributes.JoinType ) {
1169             // cairo doesn't have join type NONE so we use MITER as it's pretty close
1170             case rendering::PathJoinType::NONE:
1171                 bNoLineJoin = true;
1172             case rendering::PathJoinType::MITER:
1173                 cairo_set_line_join( mpCairo.get(), CAIRO_LINE_JOIN_MITER );
1174                 break;
1175             case rendering::PathJoinType::ROUND:
1176                 cairo_set_line_join( mpCairo.get(), CAIRO_LINE_JOIN_ROUND );
1177                 break;
1178             case rendering::PathJoinType::BEVEL:
1179                 cairo_set_line_join( mpCairo.get(), CAIRO_LINE_JOIN_BEVEL );
1180                 break;
1181 	    }
1182 
1183 	    if( strokeAttributes.DashArray.getLength() > 0 ) {
1184             double* pDashArray = new double[ strokeAttributes.DashArray.getLength() ];
1185             for( sal_Int32 i=0; i<strokeAttributes.DashArray.getLength(); i++ )
1186                 pDashArray[i]=strokeAttributes.DashArray[i];
1187             cairo_set_dash( mpCairo.get(), pDashArray, strokeAttributes.DashArray.getLength(), 0 );
1188             delete[] pDashArray;
1189 	    }
1190 
1191 	    // TODO(rodo) use LineArray of strokeAttributes
1192 
1193 	    doPolyPolygonPath( xPolyPolygon, Stroke, bNoLineJoin );
1194 
1195 	    cairo_restore( mpCairo.get() );
1196 	} else
1197 	    OSL_TRACE ("CanvasHelper called after it was disposed");
1198 
1199 #ifdef CAIRO_CANVAS_PERF_TRACE
1200 	    mxDevice->stopPerfTrace( &aTimer, "strokePolyPolygon" );
1201 #endif
1202 
1203         // TODO(P1): Provide caching here.
1204         return uno::Reference< rendering::XCachedPrimitive >(NULL);
1205     }
1206 
1207     uno::Reference< rendering::XCachedPrimitive > CanvasHelper::strokeTexturedPolyPolygon( const rendering::XCanvas* 							,
1208                                                                                            const uno::Reference< rendering::XPolyPolygon2D >& 	/*xPolyPolygon*/,
1209                                                                                            const rendering::ViewState& 							/*viewState*/,
1210                                                                                            const rendering::RenderState& 						/*renderState*/,
1211                                                                                            const uno::Sequence< rendering::Texture >& 			/*textures*/,
1212                                                                                            const rendering::StrokeAttributes& 					/*strokeAttributes*/ )
1213     {
1214         // TODO
1215         return uno::Reference< rendering::XCachedPrimitive >(NULL);
1216     }
1217 
1218     uno::Reference< rendering::XCachedPrimitive > CanvasHelper::strokeTextureMappedPolyPolygon( const rendering::XCanvas* 							,
1219                                                                                                 const uno::Reference< rendering::XPolyPolygon2D >&	/*xPolyPolygon*/,
1220                                                                                                 const rendering::ViewState& 						/*viewState*/,
1221                                                                                                 const rendering::RenderState& 						/*renderState*/,
1222                                                                                                 const uno::Sequence< rendering::Texture >& 			/*textures*/,
1223                                                                                                 const uno::Reference< geometry::XMapping2D >& 		/*xMapping*/,
1224                                                                                                 const rendering::StrokeAttributes& 					/*strokeAttributes*/ )
1225     {
1226         // TODO
1227         return uno::Reference< rendering::XCachedPrimitive >(NULL);
1228     }
1229 
1230     uno::Reference< rendering::XPolyPolygon2D >   CanvasHelper::queryStrokeShapes( const rendering::XCanvas* 							,
1231                                                                                    const uno::Reference< rendering::XPolyPolygon2D >& 	/*xPolyPolygon*/,
1232                                                                                    const rendering::ViewState& 							/*viewState*/,
1233                                                                                    const rendering::RenderState& 						/*renderState*/,
1234                                                                                    const rendering::StrokeAttributes& 					/*strokeAttributes*/ )
1235     {
1236         // TODO
1237         return uno::Reference< rendering::XPolyPolygon2D >(NULL);
1238     }
1239 
1240     uno::Reference< rendering::XCachedPrimitive > CanvasHelper::fillPolyPolygon( const rendering::XCanvas* 							,
1241                                                                                  const uno::Reference< rendering::XPolyPolygon2D >& xPolyPolygon,
1242                                                                                  const rendering::ViewState& 						viewState,
1243                                                                                  const rendering::RenderState& 						renderState )
1244     {
1245 	#ifdef CAIRO_CANVAS_PERF_TRACE
1246 	struct timespec aTimer;
1247 	mxDevice->startPerfTrace( &aTimer );
1248         #endif
1249 
1250 	if( mpCairo ) {
1251 	    cairo_save( mpCairo.get() );
1252 
1253 	    useStates( viewState, renderState, true );
1254 	    doPolyPolygonPath( xPolyPolygon, Fill );
1255 
1256 	    cairo_restore( mpCairo.get() );
1257 	} else
1258 	    OSL_TRACE ("CanvasHelper called after it was disposed");
1259 
1260 	#ifdef CAIRO_CANVAS_PERF_TRACE
1261 	mxDevice->stopPerfTrace( &aTimer, "fillPolyPolygon" );
1262         #endif
1263 
1264         return uno::Reference< rendering::XCachedPrimitive >(NULL);
1265     }
1266 
1267     uno::Reference< rendering::XCachedPrimitive > CanvasHelper::fillTexturedPolyPolygon( const rendering::XCanvas* 							,
1268                                                                                          const uno::Reference< rendering::XPolyPolygon2D >& xPolyPolygon,
1269                                                                                          const rendering::ViewState& 						viewState,
1270                                                                                          const rendering::RenderState& 						renderState,
1271                                                                                          const uno::Sequence< rendering::Texture >& 		textures )
1272     {
1273 	if( mpCairo ) {
1274 	    cairo_save( mpCairo.get() );
1275 
1276 	    useStates( viewState, renderState, true );
1277 	    doPolyPolygonPath( xPolyPolygon, Fill, false, &textures );
1278 
1279 	    cairo_restore( mpCairo.get() );
1280 	}
1281 
1282         return uno::Reference< rendering::XCachedPrimitive >(NULL);
1283     }
1284 
1285     uno::Reference< rendering::XCachedPrimitive > CanvasHelper::fillTextureMappedPolyPolygon( const rendering::XCanvas* 							,
1286                                                                                               const uno::Reference< rendering::XPolyPolygon2D >& 	/*xPolyPolygon*/,
1287                                                                                               const rendering::ViewState& 							/*viewState*/,
1288                                                                                               const rendering::RenderState& 						/*renderState*/,
1289                                                                                               const uno::Sequence< rendering::Texture >& 			/*textures*/,
1290                                                                                               const uno::Reference< geometry::XMapping2D >& 		/*xMapping*/ )
1291     {
1292         // TODO
1293         return uno::Reference< rendering::XCachedPrimitive >(NULL);
1294     }
1295 
1296     uno::Reference< rendering::XCachedPrimitive > CanvasHelper::implDrawBitmapSurface( const rendering::XCanvas* 	    pCanvas,
1297                                                                                        const SurfaceSharedPtr&        pInputSurface,
1298 																					   const rendering::ViewState&      viewState,
1299 																					   const rendering::RenderState&    renderState,
1300 																					   const geometry::IntegerSize2D&   rSize,
1301 																					   bool bModulateColors,
1302 																					   bool bHasAlpha )
1303     {
1304         SurfaceSharedPtr pSurface=pInputSurface;
1305 		uno::Reference< rendering::XCachedPrimitive > rv = uno::Reference< rendering::XCachedPrimitive >(NULL);
1306         geometry::IntegerSize2D aBitmapSize = rSize;
1307 
1308 		if( mpCairo ) {
1309 			cairo_save( mpCairo.get() );
1310 
1311 			cairo_rectangle( mpCairo.get(), 0, 0, maSize.getX(), maSize.getY() );
1312 			cairo_clip( mpCairo.get() );
1313 
1314 			useStates( viewState, renderState, true );
1315 
1316 			//   	    if( !bHasAlpha )
1317 			//   		cairo_set_operator( mpCairo.get(), CAIRO_OPERATOR_SOURCE );
1318 
1319 			Matrix aMatrix;
1320 
1321 			cairo_get_matrix( mpCairo.get(), &aMatrix );
1322 			if( ! ::rtl::math::approxEqual( aMatrix.xx, 1 ) &&
1323 				! ::rtl::math::approxEqual( aMatrix.yy, 1 ) &&
1324 				::rtl::math::approxEqual( aMatrix.x0, 0 ) &&
1325 				::rtl::math::approxEqual( aMatrix.y0, 0 ) &&
1326 				basegfx::fround( rSize.Width * aMatrix.xx ) > 8 &&
1327 				basegfx::fround( rSize.Height* aMatrix.yy ) > 8 )
1328             {
1329                 double dWidth, dHeight;
1330 
1331                 dWidth = basegfx::fround( rSize.Width * aMatrix.xx );
1332                 dHeight = basegfx::fround( rSize.Height* aMatrix.yy );
1333                 aBitmapSize.Width = static_cast<sal_Int32>( dWidth );
1334                 aBitmapSize.Height = static_cast<sal_Int32>( dHeight );
1335 
1336                 SurfaceSharedPtr pScaledSurface = mpSurfaceProvider->createSurface(
1337                     ::basegfx::B2ISize( aBitmapSize.Width, aBitmapSize.Height ),
1338                     bHasAlpha ? CAIRO_CONTENT_COLOR_ALPHA : CAIRO_CONTENT_COLOR );
1339                 CairoSharedPtr pCairo = pScaledSurface->getCairo();
1340 
1341                 cairo_set_operator( pCairo.get(), CAIRO_OPERATOR_SOURCE );
1342                 // add 0.5px to size to avoid rounding errors in cairo, leading sometimes to random data on the image right/bottom borders
1343                 cairo_scale( pCairo.get(), (dWidth+0.5)/rSize.Width, (dHeight+0.5)/rSize.Height );
1344                 cairo_set_source_surface( pCairo.get(), pSurface->getCairoSurface().get(), 0, 0 );
1345                 cairo_paint( pCairo.get() );
1346 
1347                 pSurface = pScaledSurface;
1348 
1349                 aMatrix.xx = aMatrix.yy = 1;
1350                 cairo_set_matrix( mpCairo.get(), &aMatrix );
1351 
1352                 rv = uno::Reference< rendering::XCachedPrimitive >(
1353                     new CachedBitmap( pSurface, viewState, renderState,
1354                                       // cast away const, need to
1355                                       // change refcount (as this is
1356                                       // ~invisible to client code,
1357                                       // still logically const)
1358                                       const_cast< rendering::XCanvas* >(pCanvas)) );
1359             }
1360 
1361 			if( !bHasAlpha && mbHaveAlpha )
1362             {
1363 				double x, y, width, height;
1364 
1365 				x = y = 0;
1366 				width = aBitmapSize.Width;
1367 				height = aBitmapSize.Height;
1368 				cairo_matrix_transform_point( &aMatrix, &x, &y );
1369 				cairo_matrix_transform_distance( &aMatrix, &width, &height );
1370 
1371 				// in case the bitmap doesn't have alpha and covers whole area
1372 				// we try to change surface to plain rgb
1373 				OSL_TRACE ("chance to change surface to rgb, %f, %f, %f x %f (%d x %d)", x, y, width, height, maSize.getX(), maSize.getY() );
1374 				if( x <= 0 && y <= 0 && x + width >= maSize.getX() && y + height >= maSize.getY() )
1375 				{
1376 					OSL_TRACE ("trying to change surface to rgb");
1377 					if( mpSurfaceProvider ) {
1378 						SurfaceSharedPtr pNewSurface = mpSurfaceProvider->changeSurface( false, false );
1379 
1380 						if( pNewSurface )
1381 							setSurface( pNewSurface, false );
1382 
1383 						// set state to new mpCairo.get()
1384 						useStates( viewState, renderState, true );
1385 						// use the possibly modified matrix
1386 						cairo_set_matrix( mpCairo.get(), &aMatrix );
1387 					}
1388 				}
1389 			}
1390 
1391 			cairo_set_source_surface( mpCairo.get(), pSurface->getCairoSurface().get(), 0, 0 );
1392  			if( !bHasAlpha &&
1393 				::rtl::math::approxEqual( aMatrix.xx, 1 ) &&
1394 				::rtl::math::approxEqual( aMatrix.yy, 1 ) &&
1395 				::rtl::math::approxEqual( aMatrix.x0, 0 ) &&
1396 				::rtl::math::approxEqual( aMatrix.y0, 0 ) )
1397  				cairo_set_operator( mpCairo.get(), CAIRO_OPERATOR_SOURCE );
1398 			cairo_pattern_set_extend( cairo_get_source(mpCairo.get()), CAIRO_EXTEND_PAD );
1399 			cairo_rectangle( mpCairo.get(), 0, 0, aBitmapSize.Width, aBitmapSize.Height );
1400 			cairo_clip( mpCairo.get() );
1401 
1402             if( bModulateColors )
1403                 cairo_paint_with_alpha( mpCairo.get(), renderState.DeviceColor[3] );
1404             else
1405                 cairo_paint( mpCairo.get() );
1406 			cairo_restore( mpCairo.get() );
1407 		} else
1408 			OSL_TRACE ("CanvasHelper called after it was disposed");
1409 
1410         return rv; // uno::Reference< rendering::XCachedPrimitive >(NULL);
1411     }
1412 
1413     uno::Reference< rendering::XCachedPrimitive > CanvasHelper::drawBitmap( const rendering::XCanvas* 					pCanvas,
1414                                                                             const uno::Reference< rendering::XBitmap >& xBitmap,
1415                                                                             const rendering::ViewState& 				viewState,
1416                                                                             const rendering::RenderState& 				renderState )
1417     {
1418 	#ifdef CAIRO_CANVAS_PERF_TRACE
1419 	struct timespec aTimer;
1420 	mxDevice->startPerfTrace( &aTimer );
1421         #endif
1422 
1423 	uno::Reference< rendering::XCachedPrimitive > rv;
1424 	unsigned char* data = NULL;
1425 	bool bHasAlpha = false;
1426 	SurfaceSharedPtr pSurface = surfaceFromXBitmap( xBitmap, mpSurfaceProvider, data, bHasAlpha );
1427 	geometry::IntegerSize2D aSize = xBitmap->getSize();
1428 
1429 	if( pSurface ) {
1430 	    rv = implDrawBitmapSurface( pCanvas, pSurface, viewState, renderState, aSize, false, bHasAlpha );
1431 
1432 	    if( data )
1433 		free( data );
1434 	} else
1435 	    rv = uno::Reference< rendering::XCachedPrimitive >(NULL);
1436 
1437 	#ifdef CAIRO_CANVAS_PERF_TRACE
1438 	mxDevice->stopPerfTrace( &aTimer, "drawBitmap" );
1439         #endif
1440 
1441 	return rv;
1442     }
1443 
1444     uno::Reference< rendering::XCachedPrimitive > CanvasHelper::drawBitmapModulated( const rendering::XCanvas* 						pCanvas,
1445                                                                                      const uno::Reference< rendering::XBitmap >& 	xBitmap,
1446                                                                                      const rendering::ViewState& 					viewState,
1447                                                                                      const rendering::RenderState& 					renderState )
1448     {
1449 #ifdef CAIRO_CANVAS_PERF_TRACE
1450         struct timespec aTimer;
1451         mxDevice->startPerfTrace( &aTimer );
1452 #endif
1453 
1454         uno::Reference< rendering::XCachedPrimitive > rv;
1455         unsigned char* data = NULL;
1456         bool bHasAlpha = false;
1457         SurfaceSharedPtr pSurface = surfaceFromXBitmap( xBitmap, mpSurfaceProvider, data, bHasAlpha );
1458         geometry::IntegerSize2D aSize = xBitmap->getSize();
1459 
1460         if( pSurface ) {
1461             rv = implDrawBitmapSurface( pCanvas, pSurface, viewState, renderState, aSize, true, bHasAlpha );
1462 
1463             if( data )
1464                 free( data );
1465         } else
1466             rv = uno::Reference< rendering::XCachedPrimitive >(NULL);
1467 
1468 #ifdef CAIRO_CANVAS_PERF_TRACE
1469         mxDevice->stopPerfTrace( &aTimer, "drawBitmap" );
1470 #endif
1471 
1472         return rv;
1473     }
1474 
1475     uno::Reference< rendering::XGraphicDevice > CanvasHelper::getDevice()
1476     {
1477         return uno::Reference< rendering::XGraphicDevice >(mpDevice);
1478     }
1479 
1480     void CanvasHelper::copyRect( const rendering::XCanvas* 							,
1481                                  const uno::Reference< rendering::XBitmapCanvas >&	/*sourceCanvas*/,
1482                                  const geometry::RealRectangle2D& 					/*sourceRect*/,
1483                                  const rendering::ViewState& 						/*sourceViewState*/,
1484                                  const rendering::RenderState& 						/*sourceRenderState*/,
1485                                  const geometry::RealRectangle2D& 					/*destRect*/,
1486                                  const rendering::ViewState& 						/*destViewState*/,
1487                                  const rendering::RenderState& 						/*destRenderState*/ )
1488     {
1489         // TODO(F2): copyRect NYI
1490     }
1491 
1492     geometry::IntegerSize2D CanvasHelper::getSize()
1493     {
1494         if( !mpSurfaceProvider )
1495             geometry::IntegerSize2D(1, 1); // we're disposed
1496 
1497         return ::basegfx::unotools::integerSize2DFromB2ISize( maSize );
1498     }
1499 
1500     uno::Reference< rendering::XBitmap > CanvasHelper::getScaledBitmap( const geometry::RealSize2D&	newSize,
1501                                                                         sal_Bool 					/*beFast*/ )
1502     {
1503 #ifdef CAIRO_CANVAS_PERF_TRACE
1504 		struct timespec aTimer;
1505 		mxDevice->startPerfTrace( &aTimer );
1506 #endif
1507 
1508 		if( mpCairo ) {
1509 			return uno::Reference< rendering::XBitmap >( new CanvasBitmap( ::basegfx::B2ISize( ::canvas::tools::roundUp( newSize.Width ),
1510 																							   ::canvas::tools::roundUp( newSize.Height ) ),
1511 																		   mpSurfaceProvider, mpDevice, false ) );
1512 		} else
1513 			OSL_TRACE ("CanvasHelper called after it was disposed");
1514 
1515 #ifdef CAIRO_CANVAS_PERF_TRACE
1516 		mxDevice->stopPerfTrace( &aTimer, "getScaledBitmap" );
1517 #endif
1518 
1519 		return uno::Reference< rendering::XBitmap >();
1520     }
1521 
1522     uno::Sequence< sal_Int8 > CanvasHelper::getData( rendering::IntegerBitmapLayout&     aLayout,
1523                                                      const geometry::IntegerRectangle2D& rect )
1524     {
1525         if( mpCairo ) {
1526             aLayout = getMemoryLayout();
1527 
1528             const sal_Int32 nWidth( rect.X2 - rect.X1 );
1529             const sal_Int32 nHeight( rect.Y2 - rect.Y1 );
1530             uno::Sequence< sal_Int8 > aRes( 4*nWidth*nHeight );
1531             sal_Int8* pData = aRes.getArray();
1532             cairo_surface_t* pImageSurface = cairo_image_surface_create_for_data( (unsigned char *) pData,
1533                                                                                   CAIRO_FORMAT_ARGB32,
1534                                                                                   nWidth, nHeight, 4*nWidth );
1535             cairo_t* pCairo = cairo_create( pImageSurface );
1536             cairo_set_source_surface( pCairo, mpSurface->getCairoSurface().get(), -rect.X1, -rect.Y1);
1537             cairo_paint( pCairo );
1538             cairo_destroy( pCairo );
1539             cairo_surface_destroy( pImageSurface );
1540 
1541             aLayout.ScanLines = nHeight;
1542             aLayout.ScanLineBytes = nWidth*4;
1543             aLayout.ScanLineStride = aLayout.ScanLineBytes;
1544 
1545             return aRes;
1546         }
1547 
1548         return uno::Sequence< sal_Int8 >();
1549     }
1550 
1551     void CanvasHelper::setData( const uno::Sequence< sal_Int8 >& 		/*data*/,
1552                                 const rendering::IntegerBitmapLayout&   /*bitmapLayout*/,
1553                                 const geometry::IntegerRectangle2D& 	/*rect*/ )
1554     {
1555     }
1556 
1557     void CanvasHelper::setPixel( const uno::Sequence< sal_Int8 >&       /*color*/,
1558                                  const rendering::IntegerBitmapLayout&  /*bitmapLayout*/,
1559                                  const geometry::IntegerPoint2D&        /*pos*/ )
1560     {
1561     }
1562 
1563     uno::Sequence< sal_Int8 > CanvasHelper::getPixel( rendering::IntegerBitmapLayout&   /*bitmapLayout*/,
1564                                                       const geometry::IntegerPoint2D&   /*pos*/ )
1565     {
1566         return uno::Sequence< sal_Int8 >();
1567     }
1568 
1569     uno::Reference< rendering::XBitmapPalette > CanvasHelper::getPalette()
1570     {
1571         // TODO(F1): Palette bitmaps NYI
1572         return uno::Reference< rendering::XBitmapPalette >();
1573     }
1574 
1575     namespace
1576     {
1577         class CairoColorSpace : public cppu::WeakImplHelper1< com::sun::star::rendering::XIntegerBitmapColorSpace >
1578         {
1579         private:
1580             uno::Sequence< sal_Int8 >  maComponentTags;
1581             uno::Sequence< sal_Int32 > maBitCounts;
1582 
1583             virtual ::sal_Int8 SAL_CALL getType(  ) throw (uno::RuntimeException)
1584             {
1585                 return rendering::ColorSpaceType::RGB;
1586             }
1587             virtual uno::Sequence< ::sal_Int8 > SAL_CALL getComponentTags(  ) throw (uno::RuntimeException)
1588             {
1589                 return maComponentTags;
1590             }
1591             virtual ::sal_Int8 SAL_CALL getRenderingIntent(  ) throw (uno::RuntimeException)
1592             {
1593                 return rendering::RenderingIntent::PERCEPTUAL;
1594             }
1595             virtual uno::Sequence< beans::PropertyValue > SAL_CALL getProperties(  ) throw (uno::RuntimeException)
1596             {
1597                 return uno::Sequence< beans::PropertyValue >();
1598             }
1599             virtual uno::Sequence< double > SAL_CALL convertColorSpace( const uno::Sequence< double >& deviceColor,
1600                                                                         const uno::Reference< rendering::XColorSpace >& targetColorSpace ) throw (lang::IllegalArgumentException,
1601                                                                                                                                                   uno::RuntimeException)
1602             {
1603                 // TODO(P3): if we know anything about target
1604                 // colorspace, this can be greatly sped up
1605                 uno::Sequence<rendering::ARGBColor> aIntermediate(
1606                     convertToARGB(deviceColor));
1607                 return targetColorSpace->convertFromARGB(aIntermediate);
1608             }
1609             virtual uno::Sequence< rendering::RGBColor > SAL_CALL convertToRGB( const uno::Sequence< double >& deviceColor ) throw (lang::IllegalArgumentException, uno::RuntimeException)
1610             {
1611                 const double*  pIn( deviceColor.getConstArray() );
1612                 const sal_Size nLen( deviceColor.getLength() );
1613                 ENSURE_ARG_OR_THROW2(nLen%4==0,
1614                                      "number of channels no multiple of 4",
1615                                      static_cast<rendering::XColorSpace*>(this), 0);
1616 
1617                 uno::Sequence< rendering::RGBColor > aRes(nLen/4);
1618                 rendering::RGBColor* pOut( aRes.getArray() );
1619                 for( sal_Size i=0; i<nLen; i+=4 )
1620                 {
1621                     const double fAlpha(pIn[3]);
1622                     if( fAlpha == 0.0 )
1623                         *pOut++ = rendering::RGBColor(0.0, 0.0, 0.0);
1624                     else
1625                         *pOut++ = rendering::RGBColor(pIn[2]/fAlpha,pIn[1]/fAlpha,pIn[0]/fAlpha);
1626                     pIn += 4;
1627                 }
1628                 return aRes;
1629             }
1630             virtual uno::Sequence< rendering::ARGBColor > SAL_CALL convertToARGB( const uno::Sequence< double >& deviceColor ) throw (lang::IllegalArgumentException, uno::RuntimeException)
1631             {
1632                 const double*  pIn( deviceColor.getConstArray() );
1633                 const sal_Size nLen( deviceColor.getLength() );
1634                 ENSURE_ARG_OR_THROW2(nLen%4==0,
1635                                      "number of channels no multiple of 4",
1636                                      static_cast<rendering::XColorSpace*>(this), 0);
1637 
1638                 uno::Sequence< rendering::ARGBColor > aRes(nLen/4);
1639                 rendering::ARGBColor* pOut( aRes.getArray() );
1640                 for( sal_Size i=0; i<nLen; i+=4 )
1641                 {
1642                     const double fAlpha(pIn[3]);
1643                     if( fAlpha == 0.0 )
1644                         *pOut++ = rendering::ARGBColor(0.0, 0.0, 0.0, 0.0);
1645                     else
1646                         *pOut++ = rendering::ARGBColor(fAlpha,pIn[2]/fAlpha,pIn[1]/fAlpha,pIn[0]/fAlpha);
1647                     pIn += 4;
1648                 }
1649                 return aRes;
1650             }
1651             virtual uno::Sequence< rendering::ARGBColor > SAL_CALL convertToPARGB( const uno::Sequence< double >& deviceColor ) throw (lang::IllegalArgumentException, uno::RuntimeException)
1652             {
1653                 const double*  pIn( deviceColor.getConstArray() );
1654                 const sal_Size nLen( deviceColor.getLength() );
1655                 ENSURE_ARG_OR_THROW2(nLen%4==0,
1656                                      "number of channels no multiple of 4",
1657                                      static_cast<rendering::XColorSpace*>(this), 0);
1658 
1659                 uno::Sequence< rendering::ARGBColor > aRes(nLen/4);
1660                 rendering::ARGBColor* pOut( aRes.getArray() );
1661                 for( sal_Size i=0; i<nLen; i+=4 )
1662                 {
1663                     *pOut++ = rendering::ARGBColor(pIn[3],pIn[2],pIn[1],pIn[1]);
1664                     pIn += 4;
1665                 }
1666                 return aRes;
1667             }
1668             virtual uno::Sequence< double > SAL_CALL convertFromRGB( const uno::Sequence< rendering::RGBColor >& rgbColor ) throw (lang::IllegalArgumentException, uno::RuntimeException)
1669             {
1670                 const rendering::RGBColor* pIn( rgbColor.getConstArray() );
1671                 const sal_Size             nLen( rgbColor.getLength() );
1672 
1673                 uno::Sequence< double > aRes(nLen*4);
1674                 double* pColors=aRes.getArray();
1675                 for( sal_Size i=0; i<nLen; ++i )
1676                 {
1677                     *pColors++ = pIn->Blue;
1678                     *pColors++ = pIn->Green;
1679                     *pColors++ = pIn->Red;
1680                     *pColors++ = 1.0;
1681                     ++pIn;
1682                 }
1683                 return aRes;
1684             }
1685             virtual uno::Sequence< double > SAL_CALL convertFromARGB( const uno::Sequence< rendering::ARGBColor >& rgbColor ) throw (lang::IllegalArgumentException, uno::RuntimeException)
1686             {
1687                 const rendering::ARGBColor* pIn( rgbColor.getConstArray() );
1688                 const sal_Size              nLen( rgbColor.getLength() );
1689 
1690                 uno::Sequence< double > aRes(nLen*4);
1691                 double* pColors=aRes.getArray();
1692                 for( sal_Size i=0; i<nLen; ++i )
1693                 {
1694                     *pColors++ = pIn->Alpha*pIn->Blue;
1695                     *pColors++ = pIn->Alpha*pIn->Green;
1696                     *pColors++ = pIn->Alpha*pIn->Red;
1697                     *pColors++ = pIn->Alpha;
1698                     ++pIn;
1699                 }
1700                 return aRes;
1701             }
1702             virtual uno::Sequence< double > SAL_CALL convertFromPARGB( const uno::Sequence< rendering::ARGBColor >& rgbColor ) throw (lang::IllegalArgumentException, uno::RuntimeException)
1703             {
1704                 const rendering::ARGBColor* pIn( rgbColor.getConstArray() );
1705                 const sal_Size              nLen( rgbColor.getLength() );
1706 
1707                 uno::Sequence< double > aRes(nLen*4);
1708                 double* pColors=aRes.getArray();
1709                 for( sal_Size i=0; i<nLen; ++i )
1710                 {
1711                     *pColors++ = pIn->Blue;
1712                     *pColors++ = pIn->Green;
1713                     *pColors++ = pIn->Red;
1714                     *pColors++ = pIn->Alpha;
1715                     ++pIn;
1716                 }
1717                 return aRes;
1718             }
1719 
1720             // XIntegerBitmapColorSpace
1721             virtual ::sal_Int32 SAL_CALL getBitsPerPixel(  ) throw (uno::RuntimeException)
1722             {
1723                 return 32;
1724             }
1725             virtual uno::Sequence< ::sal_Int32 > SAL_CALL getComponentBitCounts(  ) throw (uno::RuntimeException)
1726             {
1727                 return maBitCounts;
1728             }
1729             virtual ::sal_Int8 SAL_CALL getEndianness(  ) throw (uno::RuntimeException)
1730             {
1731                 return util::Endianness::LITTLE;
1732             }
1733             virtual uno::Sequence<double> SAL_CALL convertFromIntegerColorSpace( const uno::Sequence< ::sal_Int8 >& deviceColor,
1734                                                                                  const uno::Reference< rendering::XColorSpace >& targetColorSpace ) throw (lang::IllegalArgumentException,
1735                                                                                                                                                            uno::RuntimeException)
1736             {
1737                 if( dynamic_cast<CairoColorSpace*>(targetColorSpace.get()) )
1738                 {
1739                     const sal_Int8* pIn( deviceColor.getConstArray() );
1740                     const sal_Size  nLen( deviceColor.getLength() );
1741                     ENSURE_ARG_OR_THROW2(nLen%4==0,
1742                                          "number of channels no multiple of 4",
1743                                          static_cast<rendering::XColorSpace*>(this), 0);
1744 
1745                     uno::Sequence<double> aRes(nLen);
1746                     double* pOut( aRes.getArray() );
1747                     for( sal_Size i=0; i<nLen; i+=4 )
1748                     {
1749                         *pOut++ = vcl::unotools::toDoubleColor(*pIn++);
1750                         *pOut++ = vcl::unotools::toDoubleColor(*pIn++);
1751                         *pOut++ = vcl::unotools::toDoubleColor(*pIn++);
1752                         *pOut++ = vcl::unotools::toDoubleColor(*pIn++);
1753                     }
1754                     return aRes;
1755                 }
1756                 else
1757                 {
1758                     // TODO(P3): if we know anything about target
1759                     // colorspace, this can be greatly sped up
1760                     uno::Sequence<rendering::ARGBColor> aIntermediate(
1761                         convertIntegerToARGB(deviceColor));
1762                     return targetColorSpace->convertFromARGB(aIntermediate);
1763                 }
1764             }
1765             virtual uno::Sequence< ::sal_Int8 > SAL_CALL convertToIntegerColorSpace( const uno::Sequence< ::sal_Int8 >& deviceColor,
1766                                                                                      const uno::Reference< rendering::XIntegerBitmapColorSpace >& targetColorSpace ) throw (lang::IllegalArgumentException,
1767                                                                                                                                                                             uno::RuntimeException)
1768             {
1769                 if( dynamic_cast<CairoColorSpace*>(targetColorSpace.get()) )
1770                 {
1771                     // it's us, so simply pass-through the data
1772                     return deviceColor;
1773                 }
1774                 else
1775                 {
1776                     // TODO(P3): if we know anything about target
1777                     // colorspace, this can be greatly sped up
1778                     uno::Sequence<rendering::ARGBColor> aIntermediate(
1779                         convertIntegerToARGB(deviceColor));
1780                     return targetColorSpace->convertIntegerFromARGB(aIntermediate);
1781                 }
1782             }
1783             virtual uno::Sequence< rendering::RGBColor > SAL_CALL convertIntegerToRGB( const uno::Sequence< ::sal_Int8 >& deviceColor ) throw (lang::IllegalArgumentException, uno::RuntimeException)
1784             {
1785                 const sal_Int8* pIn( deviceColor.getConstArray() );
1786                 const sal_Size  nLen( deviceColor.getLength() );
1787                 ENSURE_ARG_OR_THROW2(nLen%4==0,
1788                                      "number of channels no multiple of 4",
1789                                      static_cast<rendering::XColorSpace*>(this), 0);
1790 
1791                 uno::Sequence< rendering::RGBColor > aRes(nLen/4);
1792                 rendering::RGBColor* pOut( aRes.getArray() );
1793                 for( sal_Size i=0; i<nLen; i+=4 )
1794                 {
1795                     const double fAlpha((sal_uInt8)pIn[3]);
1796                     if( fAlpha )
1797                         *pOut++ = rendering::RGBColor(
1798                             pIn[2]/fAlpha,
1799                             pIn[1]/fAlpha,
1800                             pIn[0]/fAlpha);
1801                     else
1802                         *pOut++ = rendering::RGBColor(0,0,0);
1803                     pIn += 4;
1804                 }
1805                 return aRes;
1806             }
1807 
1808             virtual uno::Sequence< rendering::ARGBColor > SAL_CALL convertIntegerToARGB( const uno::Sequence< ::sal_Int8 >& deviceColor ) throw (lang::IllegalArgumentException, uno::RuntimeException)
1809             {
1810                 const sal_Int8* pIn( deviceColor.getConstArray() );
1811                 const sal_Size  nLen( deviceColor.getLength() );
1812                 ENSURE_ARG_OR_THROW2(nLen%4==0,
1813                                      "number of channels no multiple of 4",
1814                                      static_cast<rendering::XColorSpace*>(this), 0);
1815 
1816                 uno::Sequence< rendering::ARGBColor > aRes(nLen/4);
1817                 rendering::ARGBColor* pOut( aRes.getArray() );
1818                 for( sal_Size i=0; i<nLen; i+=4 )
1819                 {
1820                     const double fAlpha((sal_uInt8)pIn[3]);
1821                     if( fAlpha )
1822                         *pOut++ = rendering::ARGBColor(
1823                             fAlpha/255.0,
1824                             pIn[2]/fAlpha,
1825                             pIn[1]/fAlpha,
1826                             pIn[0]/fAlpha);
1827                     else
1828                         *pOut++ = rendering::ARGBColor(0,0,0,0);
1829                     pIn += 4;
1830                 }
1831                 return aRes;
1832             }
1833             virtual uno::Sequence< rendering::ARGBColor > SAL_CALL convertIntegerToPARGB( const uno::Sequence< ::sal_Int8 >& deviceColor ) throw (lang::IllegalArgumentException, uno::RuntimeException)
1834             {
1835                 const sal_Int8* pIn( deviceColor.getConstArray() );
1836                 const sal_Size  nLen( deviceColor.getLength() );
1837                 ENSURE_ARG_OR_THROW2(nLen%4==0,
1838                                      "number of channels no multiple of 4",
1839                                      static_cast<rendering::XColorSpace*>(this), 0);
1840 
1841                 uno::Sequence< rendering::ARGBColor > aRes(nLen/4);
1842                 rendering::ARGBColor* pOut( aRes.getArray() );
1843                 for( sal_Size i=0; i<nLen; i+=4 )
1844                 {
1845                     *pOut++ = rendering::ARGBColor(
1846                         vcl::unotools::toDoubleColor(pIn[3]),
1847                         vcl::unotools::toDoubleColor(pIn[2]),
1848                         vcl::unotools::toDoubleColor(pIn[1]),
1849                         vcl::unotools::toDoubleColor(pIn[0]));
1850                     pIn += 4;
1851                 }
1852                 return aRes;
1853             }
1854 
1855             virtual uno::Sequence< ::sal_Int8 > SAL_CALL convertIntegerFromRGB( const uno::Sequence< rendering::RGBColor >& rgbColor ) throw (lang::IllegalArgumentException, uno::RuntimeException)
1856             {
1857                 const rendering::RGBColor* pIn( rgbColor.getConstArray() );
1858                 const sal_Size             nLen( rgbColor.getLength() );
1859 
1860                 uno::Sequence< sal_Int8 > aRes(nLen*4);
1861                 sal_Int8* pColors=aRes.getArray();
1862                 for( sal_Size i=0; i<nLen; ++i )
1863                 {
1864                     *pColors++ = vcl::unotools::toByteColor(pIn->Blue);
1865                     *pColors++ = vcl::unotools::toByteColor(pIn->Green);
1866                     *pColors++ = vcl::unotools::toByteColor(pIn->Red);
1867                     *pColors++ = 255;
1868                     ++pIn;
1869                 }
1870                 return aRes;
1871             }
1872 
1873             virtual uno::Sequence< ::sal_Int8 > SAL_CALL convertIntegerFromARGB( const uno::Sequence< rendering::ARGBColor >& rgbColor ) throw (lang::IllegalArgumentException, uno::RuntimeException)
1874             {
1875                 const rendering::ARGBColor* pIn( rgbColor.getConstArray() );
1876                 const sal_Size              nLen( rgbColor.getLength() );
1877 
1878                 uno::Sequence< sal_Int8 > aRes(nLen*4);
1879                 sal_Int8* pColors=aRes.getArray();
1880                 for( sal_Size i=0; i<nLen; ++i )
1881                 {
1882                     const double fAlpha(pIn->Alpha);
1883                     *pColors++ = vcl::unotools::toByteColor(fAlpha*pIn->Blue);
1884                     *pColors++ = vcl::unotools::toByteColor(fAlpha*pIn->Green);
1885                     *pColors++ = vcl::unotools::toByteColor(fAlpha*pIn->Red);
1886                     *pColors++ = vcl::unotools::toByteColor(fAlpha);
1887                     ++pIn;
1888                 }
1889                 return aRes;
1890             }
1891             virtual uno::Sequence< ::sal_Int8 > SAL_CALL convertIntegerFromPARGB( const uno::Sequence< rendering::ARGBColor >& rgbColor ) throw (lang::IllegalArgumentException, uno::RuntimeException)
1892             {
1893                 const rendering::ARGBColor* pIn( rgbColor.getConstArray() );
1894                 const sal_Size              nLen( rgbColor.getLength() );
1895 
1896                 uno::Sequence< sal_Int8 > aRes(nLen*4);
1897                 sal_Int8* pColors=aRes.getArray();
1898                 for( sal_Size i=0; i<nLen; ++i )
1899                 {
1900                     *pColors++ = vcl::unotools::toByteColor(pIn->Blue);
1901                     *pColors++ = vcl::unotools::toByteColor(pIn->Green);
1902                     *pColors++ = vcl::unotools::toByteColor(pIn->Red);
1903                     *pColors++ = vcl::unotools::toByteColor(pIn->Alpha);
1904                     ++pIn;
1905                 }
1906                 return aRes;
1907             }
1908 
1909         public:
1910             CairoColorSpace() :
1911                 maComponentTags(4),
1912                 maBitCounts(4)
1913             {
1914                 sal_Int8*  pTags = maComponentTags.getArray();
1915                 sal_Int32* pBitCounts = maBitCounts.getArray();
1916                 pTags[0] = rendering::ColorComponentTag::RGB_BLUE;
1917                 pTags[1] = rendering::ColorComponentTag::RGB_GREEN;
1918                 pTags[2] = rendering::ColorComponentTag::RGB_RED;
1919                 pTags[3] = rendering::ColorComponentTag::PREMULTIPLIED_ALPHA;
1920 
1921                 pBitCounts[0] =
1922                     pBitCounts[1] =
1923                     pBitCounts[2] =
1924                     pBitCounts[3] = 8;
1925             }
1926         };
1927 
1928         struct CairoColorSpaceHolder : public rtl::StaticWithInit<uno::Reference<rendering::XIntegerBitmapColorSpace>,
1929                                                                      CairoColorSpaceHolder>
1930         {
1931             uno::Reference<rendering::XIntegerBitmapColorSpace> operator()()
1932             {
1933                 return new CairoColorSpace();
1934             }
1935         };
1936     }
1937 
1938     rendering::IntegerBitmapLayout CanvasHelper::getMemoryLayout()
1939     {
1940         if( !mpCairo )
1941             return rendering::IntegerBitmapLayout(); // we're disposed
1942 
1943         const geometry::IntegerSize2D aSize(getSize());
1944         rendering::IntegerBitmapLayout aLayout;
1945 
1946         aLayout.ScanLines = aSize.Height;
1947         aLayout.ScanLineBytes = aSize.Width*4;
1948         aLayout.ScanLineStride = aLayout.ScanLineBytes;
1949         aLayout.PlaneStride = 0;
1950         aLayout.ColorSpace = CairoColorSpaceHolder::get();
1951         aLayout.Palette.clear();
1952         aLayout.IsMsbFirst = sal_False;
1953 
1954         return aLayout;
1955     }
1956 
1957     void CanvasHelper::flush() const
1958     {
1959     }
1960 
1961     bool CanvasHelper::hasAlpha() const
1962     {
1963         return mbHaveAlpha;
1964     }
1965 
1966     bool CanvasHelper::repaint( const SurfaceSharedPtr& pSurface,
1967 								const rendering::ViewState&      viewState,
1968 								const rendering::RenderState&	 renderState )
1969     {
1970 		OSL_TRACE("CanvasHelper::repaint");
1971 
1972 		if( mpCairo ) {
1973 			cairo_save( mpCairo.get() );
1974 
1975 			cairo_rectangle( mpCairo.get(), 0, 0, maSize.getX(), maSize.getY() );
1976 			cairo_clip( mpCairo.get() );
1977 
1978 			useStates( viewState, renderState, true );
1979 
1980 			Matrix aMatrix;
1981 
1982 			cairo_get_matrix( mpCairo.get(), &aMatrix );
1983 			aMatrix.xx = aMatrix.yy = 1;
1984 			cairo_set_matrix( mpCairo.get(), &aMatrix );
1985 
1986 			//   	    if( !bHasAlpha )
1987 			//   		cairo_set_operator( mpCairo.get(), CAIRO_OPERATOR_SOURCE );
1988 
1989 			cairo_set_source_surface( mpCairo.get(), pSurface->getCairoSurface().get(), 0, 0 );
1990 			cairo_paint( mpCairo.get() );
1991 			cairo_restore( mpCairo.get() );
1992 		}
1993 
1994 		return true;
1995     }
1996 }
1997