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