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