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