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