xref: /AOO41X/main/canvas/source/cairo/cairo_canvashelper.cxx (revision 25ea7f451e822ec0589487f23a9b6cc31f03fcc3)
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 endianess
602 #ifdef OSL_BIGENDIAN
603                             if( pAlphaReadAcc )
604                                 nAlpha = data[ nOff++ ];
605                             else
606                                 nAlpha = data[ nOff++ ] = 255;
607                             data[ nOff++ ] = sal::static_int_cast<unsigned char>(( nAlpha*aColor.GetRed() )/255 );
608                             data[ nOff++ ] = sal::static_int_cast<unsigned char>(( nAlpha*aColor.GetGreen() )/255 );
609                             data[ nOff++ ] = sal::static_int_cast<unsigned char>(( nAlpha*aColor.GetBlue() )/255 );
610 #else
611                             if( pAlphaReadAcc )
612                                 nAlpha = data[ nOff + 3 ];
613                             else
614                                 nAlpha = data[ nOff + 3 ] = 255;
615                             data[ nOff++ ] = sal::static_int_cast<unsigned char>(( nAlpha*aColor.GetBlue() )/255 );
616                             data[ nOff++ ] = sal::static_int_cast<unsigned char>(( nAlpha*aColor.GetGreen() )/255 );
617                             data[ nOff++ ] = sal::static_int_cast<unsigned char>(( nAlpha*aColor.GetRed() )/255 );
618                             nOff ++;
619 #endif
620                         }
621                     }
622                 }
623 
624                 aBitmap.ReleaseAccess( pBitmapReadAcc );
625                 if( pAlphaReadAcc )
626                     aAlpha.ReleaseAccess( pAlphaReadAcc );
627 
628                 SurfaceSharedPtr pImageSurface = createSurface(
629                     CairoSurfaceSharedPtr(
630                         cairo_image_surface_create_for_data(
631                             data,
632                             bIsAlpha ? CAIRO_FORMAT_ARGB32 : CAIRO_FORMAT_RGB24,
633                             nWidth, nHeight, nWidth*4 ),
634                         &cairo_surface_destroy) );
635 
636                 //      pSurface = rSurfaceProvider->getSurface( ::basegfx::B2ISize( nWidth, nHeight ), bIsAlpha ? CAIRO_CONTENT_COLOR_ALPHA : CAIRO_CONTENT_COLOR );
637                 //      Cairo* pTargetCairo = cairo_create( pSurface );
638                 //      cairo_set_source_surface( pTargetCairo, pImageSurface, 0, 0 );
639 
640                 //                  //if( !bIsAlpha )
641                 //                  //cairo_set_operator( pTargetCairo, CAIRO_OPERATOR_SOURCE );
642 
643                 //      cairo_paint( pTargetCairo );
644                 //      cairo_destroy( pTargetCairo );
645                 //      cairo_surface_destroy( pImageSurface );
646                 pSurface = pImageSurface;
647 
648                 bHasAlpha = bIsAlpha;
649 
650                 OSL_TRACE("image: %d x %d alpha: %d alphaRead %p", nWidth, nHeight, bIsAlpha, pAlphaReadAcc);
651             }
652         }
653 
654         return pSurface;
655     }
656 
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(  ) throw (uno::RuntimeException)
1580             {
1581                 return rendering::ColorSpaceType::RGB;
1582             }
getComponentTags()1583             virtual uno::Sequence< ::sal_Int8 > SAL_CALL getComponentTags(  ) throw (uno::RuntimeException)
1584             {
1585                 return maComponentTags;
1586             }
getRenderingIntent()1587             virtual ::sal_Int8 SAL_CALL getRenderingIntent(  ) throw (uno::RuntimeException)
1588             {
1589                 return rendering::RenderingIntent::PERCEPTUAL;
1590             }
getProperties()1591             virtual uno::Sequence< beans::PropertyValue > SAL_CALL getProperties(  ) throw (uno::RuntimeException)
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 ) throw (lang::IllegalArgumentException,
1597                                                                                                                                                   uno::RuntimeException)
1598             {
1599                 // TODO(P3): if we know anything about target
1600                 // colorspace, this can be greatly sped up
1601                 uno::Sequence<rendering::ARGBColor> aIntermediate(
1602                     convertToARGB(deviceColor));
1603                 return targetColorSpace->convertFromARGB(aIntermediate);
1604             }
convertToRGB(const uno::Sequence<double> & deviceColor)1605             virtual uno::Sequence< rendering::RGBColor > SAL_CALL convertToRGB( const uno::Sequence< double >& deviceColor ) throw (lang::IllegalArgumentException, uno::RuntimeException)
1606             {
1607                 const double*  pIn( deviceColor.getConstArray() );
1608                 const sal_Size nLen( deviceColor.getLength() );
1609                 ENSURE_ARG_OR_THROW2(nLen%4==0,
1610                                      "number of channels no multiple of 4",
1611                                      static_cast<rendering::XColorSpace*>(this), 0);
1612 
1613                 uno::Sequence< rendering::RGBColor > aRes(nLen/4);
1614                 rendering::RGBColor* pOut( aRes.getArray() );
1615                 for( sal_Size i=0; i<nLen; i+=4 )
1616                 {
1617                     const double fAlpha(pIn[3]);
1618                     if( fAlpha == 0.0 )
1619                         *pOut++ = rendering::RGBColor(0.0, 0.0, 0.0);
1620                     else
1621                         *pOut++ = rendering::RGBColor(pIn[2]/fAlpha,pIn[1]/fAlpha,pIn[0]/fAlpha);
1622                     pIn += 4;
1623                 }
1624                 return aRes;
1625             }
convertToARGB(const uno::Sequence<double> & deviceColor)1626             virtual uno::Sequence< rendering::ARGBColor > SAL_CALL convertToARGB( const uno::Sequence< double >& deviceColor ) throw (lang::IllegalArgumentException, uno::RuntimeException)
1627             {
1628                 const double*  pIn( deviceColor.getConstArray() );
1629                 const sal_Size nLen( deviceColor.getLength() );
1630                 ENSURE_ARG_OR_THROW2(nLen%4==0,
1631                                      "number of channels no multiple of 4",
1632                                      static_cast<rendering::XColorSpace*>(this), 0);
1633 
1634                 uno::Sequence< rendering::ARGBColor > aRes(nLen/4);
1635                 rendering::ARGBColor* pOut( aRes.getArray() );
1636                 for( sal_Size i=0; i<nLen; i+=4 )
1637                 {
1638                     const double fAlpha(pIn[3]);
1639                     if( fAlpha == 0.0 )
1640                         *pOut++ = rendering::ARGBColor(0.0, 0.0, 0.0, 0.0);
1641                     else
1642                         *pOut++ = rendering::ARGBColor(fAlpha,pIn[2]/fAlpha,pIn[1]/fAlpha,pIn[0]/fAlpha);
1643                     pIn += 4;
1644                 }
1645                 return aRes;
1646             }
convertToPARGB(const uno::Sequence<double> & deviceColor)1647             virtual uno::Sequence< rendering::ARGBColor > SAL_CALL convertToPARGB( const uno::Sequence< double >& deviceColor ) throw (lang::IllegalArgumentException, uno::RuntimeException)
1648             {
1649                 const double*  pIn( deviceColor.getConstArray() );
1650                 const sal_Size nLen( deviceColor.getLength() );
1651                 ENSURE_ARG_OR_THROW2(nLen%4==0,
1652                                      "number of channels no multiple of 4",
1653                                      static_cast<rendering::XColorSpace*>(this), 0);
1654 
1655                 uno::Sequence< rendering::ARGBColor > aRes(nLen/4);
1656                 rendering::ARGBColor* pOut( aRes.getArray() );
1657                 for( sal_Size i=0; i<nLen; i+=4 )
1658                 {
1659                     *pOut++ = rendering::ARGBColor(pIn[3],pIn[2],pIn[1],pIn[1]);
1660                     pIn += 4;
1661                 }
1662                 return aRes;
1663             }
convertFromRGB(const uno::Sequence<rendering::RGBColor> & rgbColor)1664             virtual uno::Sequence< double > SAL_CALL convertFromRGB( const uno::Sequence< rendering::RGBColor >& rgbColor ) throw (lang::IllegalArgumentException, uno::RuntimeException)
1665             {
1666                 const rendering::RGBColor* pIn( rgbColor.getConstArray() );
1667                 const sal_Size             nLen( rgbColor.getLength() );
1668 
1669                 uno::Sequence< double > aRes(nLen*4);
1670                 double* pColors=aRes.getArray();
1671                 for( sal_Size i=0; i<nLen; ++i )
1672                 {
1673                     *pColors++ = pIn->Blue;
1674                     *pColors++ = pIn->Green;
1675                     *pColors++ = pIn->Red;
1676                     *pColors++ = 1.0;
1677                     ++pIn;
1678                 }
1679                 return aRes;
1680             }
convertFromARGB(const uno::Sequence<rendering::ARGBColor> & rgbColor)1681             virtual uno::Sequence< double > SAL_CALL convertFromARGB( const uno::Sequence< rendering::ARGBColor >& rgbColor ) throw (lang::IllegalArgumentException, uno::RuntimeException)
1682             {
1683                 const rendering::ARGBColor* pIn( rgbColor.getConstArray() );
1684                 const sal_Size              nLen( rgbColor.getLength() );
1685 
1686                 uno::Sequence< double > aRes(nLen*4);
1687                 double* pColors=aRes.getArray();
1688                 for( sal_Size i=0; i<nLen; ++i )
1689                 {
1690                     *pColors++ = pIn->Alpha*pIn->Blue;
1691                     *pColors++ = pIn->Alpha*pIn->Green;
1692                     *pColors++ = pIn->Alpha*pIn->Red;
1693                     *pColors++ = pIn->Alpha;
1694                     ++pIn;
1695                 }
1696                 return aRes;
1697             }
convertFromPARGB(const uno::Sequence<rendering::ARGBColor> & rgbColor)1698             virtual uno::Sequence< double > SAL_CALL convertFromPARGB( const uno::Sequence< rendering::ARGBColor >& rgbColor ) throw (lang::IllegalArgumentException, uno::RuntimeException)
1699             {
1700                 const rendering::ARGBColor* pIn( rgbColor.getConstArray() );
1701                 const sal_Size              nLen( rgbColor.getLength() );
1702 
1703                 uno::Sequence< double > aRes(nLen*4);
1704                 double* pColors=aRes.getArray();
1705                 for( sal_Size i=0; i<nLen; ++i )
1706                 {
1707                     *pColors++ = pIn->Blue;
1708                     *pColors++ = pIn->Green;
1709                     *pColors++ = pIn->Red;
1710                     *pColors++ = pIn->Alpha;
1711                     ++pIn;
1712                 }
1713                 return aRes;
1714             }
1715 
1716             // XIntegerBitmapColorSpace
getBitsPerPixel()1717             virtual ::sal_Int32 SAL_CALL getBitsPerPixel(  ) throw (uno::RuntimeException)
1718             {
1719                 return 32;
1720             }
getComponentBitCounts()1721             virtual uno::Sequence< ::sal_Int32 > SAL_CALL getComponentBitCounts(  ) throw (uno::RuntimeException)
1722             {
1723                 return maBitCounts;
1724             }
getEndianness()1725             virtual ::sal_Int8 SAL_CALL getEndianness(  ) throw (uno::RuntimeException)
1726             {
1727                 return util::Endianness::LITTLE;
1728             }
convertFromIntegerColorSpace(const uno::Sequence<::sal_Int8> & deviceColor,const uno::Reference<rendering::XColorSpace> & targetColorSpace)1729             virtual uno::Sequence<double> SAL_CALL convertFromIntegerColorSpace( const uno::Sequence< ::sal_Int8 >& deviceColor,
1730                                                                                  const uno::Reference< rendering::XColorSpace >& targetColorSpace ) throw (lang::IllegalArgumentException,
1731                                                                                                                                                            uno::RuntimeException)
1732             {
1733                 if( dynamic_cast<CairoColorSpace*>(targetColorSpace.get()) )
1734                 {
1735                     const sal_Int8* pIn( deviceColor.getConstArray() );
1736                     const sal_Size  nLen( deviceColor.getLength() );
1737                     ENSURE_ARG_OR_THROW2(nLen%4==0,
1738                                          "number of channels no multiple of 4",
1739                                          static_cast<rendering::XColorSpace*>(this), 0);
1740 
1741                     uno::Sequence<double> aRes(nLen);
1742                     double* pOut( aRes.getArray() );
1743                     for( sal_Size i=0; i<nLen; i+=4 )
1744                     {
1745                         *pOut++ = vcl::unotools::toDoubleColor(*pIn++);
1746                         *pOut++ = vcl::unotools::toDoubleColor(*pIn++);
1747                         *pOut++ = vcl::unotools::toDoubleColor(*pIn++);
1748                         *pOut++ = vcl::unotools::toDoubleColor(*pIn++);
1749                     }
1750                     return aRes;
1751                 }
1752                 else
1753                 {
1754                     // TODO(P3): if we know anything about target
1755                     // colorspace, this can be greatly sped up
1756                     uno::Sequence<rendering::ARGBColor> aIntermediate(
1757                         convertIntegerToARGB(deviceColor));
1758                     return targetColorSpace->convertFromARGB(aIntermediate);
1759                 }
1760             }
convertToIntegerColorSpace(const uno::Sequence<::sal_Int8> & deviceColor,const uno::Reference<rendering::XIntegerBitmapColorSpace> & targetColorSpace)1761             virtual uno::Sequence< ::sal_Int8 > SAL_CALL convertToIntegerColorSpace( const uno::Sequence< ::sal_Int8 >& deviceColor,
1762                                                                                      const uno::Reference< rendering::XIntegerBitmapColorSpace >& targetColorSpace ) throw (lang::IllegalArgumentException,
1763                                                                                                                                                                             uno::RuntimeException)
1764             {
1765                 if( dynamic_cast<CairoColorSpace*>(targetColorSpace.get()) )
1766                 {
1767                     // it's us, so simply pass-through the data
1768                     return deviceColor;
1769                 }
1770                 else
1771                 {
1772                     // TODO(P3): if we know anything about target
1773                     // colorspace, this can be greatly sped up
1774                     uno::Sequence<rendering::ARGBColor> aIntermediate(
1775                         convertIntegerToARGB(deviceColor));
1776                     return targetColorSpace->convertIntegerFromARGB(aIntermediate);
1777                 }
1778             }
convertIntegerToRGB(const uno::Sequence<::sal_Int8> & deviceColor)1779             virtual uno::Sequence< rendering::RGBColor > SAL_CALL convertIntegerToRGB( const uno::Sequence< ::sal_Int8 >& deviceColor ) throw (lang::IllegalArgumentException, uno::RuntimeException)
1780             {
1781                 const sal_Int8* pIn( deviceColor.getConstArray() );
1782                 const sal_Size  nLen( deviceColor.getLength() );
1783                 ENSURE_ARG_OR_THROW2(nLen%4==0,
1784                                      "number of channels no multiple of 4",
1785                                      static_cast<rendering::XColorSpace*>(this), 0);
1786 
1787                 uno::Sequence< rendering::RGBColor > aRes(nLen/4);
1788                 rendering::RGBColor* pOut( aRes.getArray() );
1789                 for( sal_Size i=0; i<nLen; i+=4 )
1790                 {
1791                     const double fAlpha((sal_uInt8)pIn[3]);
1792                     if( fAlpha )
1793                         *pOut++ = rendering::RGBColor(
1794                             pIn[2]/fAlpha,
1795                             pIn[1]/fAlpha,
1796                             pIn[0]/fAlpha);
1797                     else
1798                         *pOut++ = rendering::RGBColor(0,0,0);
1799                     pIn += 4;
1800                 }
1801                 return aRes;
1802             }
1803 
convertIntegerToARGB(const uno::Sequence<::sal_Int8> & deviceColor)1804             virtual uno::Sequence< rendering::ARGBColor > SAL_CALL convertIntegerToARGB( const uno::Sequence< ::sal_Int8 >& deviceColor ) throw (lang::IllegalArgumentException, uno::RuntimeException)
1805             {
1806                 const sal_Int8* pIn( deviceColor.getConstArray() );
1807                 const sal_Size  nLen( deviceColor.getLength() );
1808                 ENSURE_ARG_OR_THROW2(nLen%4==0,
1809                                      "number of channels no multiple of 4",
1810                                      static_cast<rendering::XColorSpace*>(this), 0);
1811 
1812                 uno::Sequence< rendering::ARGBColor > aRes(nLen/4);
1813                 rendering::ARGBColor* pOut( aRes.getArray() );
1814                 for( sal_Size i=0; i<nLen; i+=4 )
1815                 {
1816                     const double fAlpha((sal_uInt8)pIn[3]);
1817                     if( fAlpha )
1818                         *pOut++ = rendering::ARGBColor(
1819                             fAlpha/255.0,
1820                             pIn[2]/fAlpha,
1821                             pIn[1]/fAlpha,
1822                             pIn[0]/fAlpha);
1823                     else
1824                         *pOut++ = rendering::ARGBColor(0,0,0,0);
1825                     pIn += 4;
1826                 }
1827                 return aRes;
1828             }
convertIntegerToPARGB(const uno::Sequence<::sal_Int8> & deviceColor)1829             virtual uno::Sequence< rendering::ARGBColor > SAL_CALL convertIntegerToPARGB( const uno::Sequence< ::sal_Int8 >& deviceColor ) throw (lang::IllegalArgumentException, uno::RuntimeException)
1830             {
1831                 const sal_Int8* pIn( deviceColor.getConstArray() );
1832                 const sal_Size  nLen( deviceColor.getLength() );
1833                 ENSURE_ARG_OR_THROW2(nLen%4==0,
1834                                      "number of channels no multiple of 4",
1835                                      static_cast<rendering::XColorSpace*>(this), 0);
1836 
1837                 uno::Sequence< rendering::ARGBColor > aRes(nLen/4);
1838                 rendering::ARGBColor* pOut( aRes.getArray() );
1839                 for( sal_Size i=0; i<nLen; i+=4 )
1840                 {
1841                     *pOut++ = rendering::ARGBColor(
1842                         vcl::unotools::toDoubleColor(pIn[3]),
1843                         vcl::unotools::toDoubleColor(pIn[2]),
1844                         vcl::unotools::toDoubleColor(pIn[1]),
1845                         vcl::unotools::toDoubleColor(pIn[0]));
1846                     pIn += 4;
1847                 }
1848                 return aRes;
1849             }
1850 
convertIntegerFromRGB(const uno::Sequence<rendering::RGBColor> & rgbColor)1851             virtual uno::Sequence< ::sal_Int8 > SAL_CALL convertIntegerFromRGB( const uno::Sequence< rendering::RGBColor >& rgbColor ) throw (lang::IllegalArgumentException, uno::RuntimeException)
1852             {
1853                 const rendering::RGBColor* pIn( rgbColor.getConstArray() );
1854                 const sal_Size             nLen( rgbColor.getLength() );
1855 
1856                 uno::Sequence< sal_Int8 > aRes(nLen*4);
1857                 sal_Int8* pColors=aRes.getArray();
1858                 for( sal_Size i=0; i<nLen; ++i )
1859                 {
1860                     *pColors++ = vcl::unotools::toByteColor(pIn->Blue);
1861                     *pColors++ = vcl::unotools::toByteColor(pIn->Green);
1862                     *pColors++ = vcl::unotools::toByteColor(pIn->Red);
1863                     *pColors++ = 255;
1864                     ++pIn;
1865                 }
1866                 return aRes;
1867             }
1868 
convertIntegerFromARGB(const uno::Sequence<rendering::ARGBColor> & rgbColor)1869             virtual uno::Sequence< ::sal_Int8 > SAL_CALL convertIntegerFromARGB( const uno::Sequence< rendering::ARGBColor >& rgbColor ) throw (lang::IllegalArgumentException, uno::RuntimeException)
1870             {
1871                 const rendering::ARGBColor* pIn( rgbColor.getConstArray() );
1872                 const sal_Size              nLen( rgbColor.getLength() );
1873 
1874                 uno::Sequence< sal_Int8 > aRes(nLen*4);
1875                 sal_Int8* pColors=aRes.getArray();
1876                 for( sal_Size i=0; i<nLen; ++i )
1877                 {
1878                     const double fAlpha(pIn->Alpha);
1879                     *pColors++ = vcl::unotools::toByteColor(fAlpha*pIn->Blue);
1880                     *pColors++ = vcl::unotools::toByteColor(fAlpha*pIn->Green);
1881                     *pColors++ = vcl::unotools::toByteColor(fAlpha*pIn->Red);
1882                     *pColors++ = vcl::unotools::toByteColor(fAlpha);
1883                     ++pIn;
1884                 }
1885                 return aRes;
1886             }
convertIntegerFromPARGB(const uno::Sequence<rendering::ARGBColor> & rgbColor)1887             virtual uno::Sequence< ::sal_Int8 > SAL_CALL convertIntegerFromPARGB( const uno::Sequence< rendering::ARGBColor >& rgbColor ) throw (lang::IllegalArgumentException, uno::RuntimeException)
1888             {
1889                 const rendering::ARGBColor* pIn( rgbColor.getConstArray() );
1890                 const sal_Size              nLen( rgbColor.getLength() );
1891 
1892                 uno::Sequence< sal_Int8 > aRes(nLen*4);
1893                 sal_Int8* pColors=aRes.getArray();
1894                 for( sal_Size i=0; i<nLen; ++i )
1895                 {
1896                     *pColors++ = vcl::unotools::toByteColor(pIn->Blue);
1897                     *pColors++ = vcl::unotools::toByteColor(pIn->Green);
1898                     *pColors++ = vcl::unotools::toByteColor(pIn->Red);
1899                     *pColors++ = vcl::unotools::toByteColor(pIn->Alpha);
1900                     ++pIn;
1901                 }
1902                 return aRes;
1903             }
1904 
1905         public:
CairoColorSpace()1906             CairoColorSpace() :
1907                 maComponentTags(4),
1908                 maBitCounts(4)
1909             {
1910                 sal_Int8*  pTags = maComponentTags.getArray();
1911                 sal_Int32* pBitCounts = maBitCounts.getArray();
1912                 pTags[0] = rendering::ColorComponentTag::RGB_BLUE;
1913                 pTags[1] = rendering::ColorComponentTag::RGB_GREEN;
1914                 pTags[2] = rendering::ColorComponentTag::RGB_RED;
1915                 pTags[3] = rendering::ColorComponentTag::PREMULTIPLIED_ALPHA;
1916 
1917                 pBitCounts[0] =
1918                     pBitCounts[1] =
1919                     pBitCounts[2] =
1920                     pBitCounts[3] = 8;
1921             }
1922         };
1923 
1924         struct CairoColorSpaceHolder : public rtl::StaticWithInit<uno::Reference<rendering::XIntegerBitmapColorSpace>,
1925                                                                      CairoColorSpaceHolder>
1926         {
operator ()cairocanvas::__anonc7ad6b900111::CairoColorSpaceHolder1927             uno::Reference<rendering::XIntegerBitmapColorSpace> operator()()
1928             {
1929                 return new CairoColorSpace();
1930             }
1931         };
1932     }
1933 
getMemoryLayout()1934     rendering::IntegerBitmapLayout CanvasHelper::getMemoryLayout()
1935     {
1936         if( !mpCairo )
1937             return rendering::IntegerBitmapLayout(); // we're disposed
1938 
1939         const geometry::IntegerSize2D aSize(getSize());
1940         rendering::IntegerBitmapLayout aLayout;
1941 
1942         aLayout.ScanLines = aSize.Height;
1943         aLayout.ScanLineBytes = aSize.Width*4;
1944         aLayout.ScanLineStride = aLayout.ScanLineBytes;
1945         aLayout.PlaneStride = 0;
1946         aLayout.ColorSpace = CairoColorSpaceHolder::get();
1947         aLayout.Palette.clear();
1948         aLayout.IsMsbFirst = sal_False;
1949 
1950         return aLayout;
1951     }
1952 
flush() const1953     void CanvasHelper::flush() const
1954     {
1955     }
1956 
hasAlpha() const1957     bool CanvasHelper::hasAlpha() const
1958     {
1959         return mbHaveAlpha;
1960     }
1961 
repaint(const SurfaceSharedPtr & pSurface,const rendering::ViewState & viewState,const rendering::RenderState & renderState)1962     bool CanvasHelper::repaint( const SurfaceSharedPtr& pSurface,
1963                                 const rendering::ViewState&      viewState,
1964                                 const rendering::RenderState&    renderState )
1965     {
1966         OSL_TRACE("CanvasHelper::repaint");
1967 
1968         if( mpCairo ) {
1969             cairo_save( mpCairo.get() );
1970 
1971             cairo_rectangle( mpCairo.get(), 0, 0, maSize.getX(), maSize.getY() );
1972             cairo_clip( mpCairo.get() );
1973 
1974             useStates( viewState, renderState, true );
1975 
1976             Matrix aMatrix;
1977 
1978             cairo_get_matrix( mpCairo.get(), &aMatrix );
1979             aMatrix.xx = aMatrix.yy = 1;
1980             cairo_set_matrix( mpCairo.get(), &aMatrix );
1981 
1982             //          if( !bHasAlpha )
1983             //          cairo_set_operator( mpCairo.get(), CAIRO_OPERATOR_SOURCE );
1984 
1985             cairo_set_source_surface( mpCairo.get(), pSurface->getCairoSurface().get(), 0, 0 );
1986             cairo_paint( mpCairo.get() );
1987             cairo_restore( mpCairo.get() );
1988         }
1989 
1990         return true;
1991     }
1992 }
1993