1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <sal/config.h>
21 #include <sal/log.hxx>
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/math.hxx>
45 #include <comphelper/diagnose_ex.hxx>
46 #include <vcl/bitmapex.hxx>
47 #include <vcl/BitmapTools.hxx>
48 #include <vcl/canvastools.hxx>
49 #include <vcl/virdev.hxx>
51 #include <canvas/canvastools.hxx>
52 #include <parametricpolypolygon.hxx>
55 #include "cairo_cachedbitmap.hxx"
56 #include "cairo_canvasbitmap.hxx"
57 #include "cairo_canvashelper.hxx"
59 using namespace ::cairo
;
60 using namespace ::com::sun::star
;
64 CanvasHelper::CanvasHelper() :
65 mpSurfaceProvider(nullptr),
71 void CanvasHelper::disposing()
75 mpVirtualDevice
.disposeAndClear();
77 mpSurfaceProvider
= nullptr;
80 void CanvasHelper::init( const ::basegfx::B2ISize
& rSizePixel
,
81 SurfaceProvider
& rSurfaceProvider
,
82 rendering::XGraphicDevice
* pDevice
)
85 mpSurfaceProvider
= &rSurfaceProvider
;
89 void CanvasHelper::setSize( const ::basegfx::B2ISize
& rSize
)
94 void CanvasHelper::setSurface( const SurfaceSharedPtr
& pSurface
, bool bHasAlpha
)
96 mbHaveAlpha
= bHasAlpha
;
97 mpVirtualDevice
.disposeAndClear();
99 mpCairo
= pSurface
->getCairo();
102 static void setColor( cairo_t
* pCairo
,
103 const uno::Sequence
<double>& rColor
)
105 if( rColor
.getLength() > 3 )
107 cairo_set_source_rgba( pCairo
,
113 else if( rColor
.getLength() == 3 )
114 cairo_set_source_rgb( pCairo
,
120 void CanvasHelper::useStates( const rendering::ViewState
& viewState
,
121 const rendering::RenderState
& renderState
,
124 cairo_matrix_t aViewMatrix
;
125 cairo_matrix_t aRenderMatrix
;
126 cairo_matrix_t aCombinedMatrix
;
128 cairo_matrix_init( &aViewMatrix
,
129 viewState
.AffineTransform
.m00
, viewState
.AffineTransform
.m10
, viewState
.AffineTransform
.m01
,
130 viewState
.AffineTransform
.m11
, viewState
.AffineTransform
.m02
, viewState
.AffineTransform
.m12
);
131 cairo_matrix_init( &aRenderMatrix
,
132 renderState
.AffineTransform
.m00
, renderState
.AffineTransform
.m10
, renderState
.AffineTransform
.m01
,
133 renderState
.AffineTransform
.m11
, renderState
.AffineTransform
.m02
, renderState
.AffineTransform
.m12
);
134 cairo_matrix_multiply( &aCombinedMatrix
, &aRenderMatrix
, &aViewMatrix
);
136 if( viewState
.Clip
.is() )
138 SAL_INFO( "canvas.cairo", "view clip");
140 aViewMatrix
.x0
= basegfx::fround( aViewMatrix
.x0
);
141 aViewMatrix
.y0
= basegfx::fround( aViewMatrix
.y0
);
142 cairo_set_matrix( mpCairo
.get(), &aViewMatrix
);
143 doPolyPolygonPath( viewState
.Clip
, Clip
);
146 aCombinedMatrix
.x0
= basegfx::fround( aCombinedMatrix
.x0
);
147 aCombinedMatrix
.y0
= basegfx::fround( aCombinedMatrix
.y0
);
148 cairo_set_matrix( mpCairo
.get(), &aCombinedMatrix
);
150 if( renderState
.Clip
.is() )
152 SAL_INFO( "canvas.cairo", "render clip BEGIN");
154 doPolyPolygonPath( renderState
.Clip
, Clip
);
155 SAL_INFO( "canvas.cairo", "render clip END");
159 setColor(mpCairo
.get(),renderState
.DeviceColor
);
161 cairo_operator_t
compositingMode( CAIRO_OPERATOR_OVER
);
162 switch( renderState
.CompositeOperation
)
164 case rendering::CompositeOperation::CLEAR
:
165 compositingMode
= CAIRO_OPERATOR_CLEAR
;
167 case rendering::CompositeOperation::SOURCE
:
168 compositingMode
= CAIRO_OPERATOR_SOURCE
;
170 case rendering::CompositeOperation::DESTINATION
:
171 case rendering::CompositeOperation::UNDER
:
172 compositingMode
= CAIRO_OPERATOR_DEST
;
174 case rendering::CompositeOperation::OVER
:
175 compositingMode
= CAIRO_OPERATOR_OVER
;
177 case rendering::CompositeOperation::INSIDE
:
178 compositingMode
= CAIRO_OPERATOR_IN
;
180 case rendering::CompositeOperation::INSIDE_REVERSE
:
181 compositingMode
= CAIRO_OPERATOR_OUT
;
183 case rendering::CompositeOperation::OUTSIDE
:
184 compositingMode
= CAIRO_OPERATOR_DEST_OVER
;
186 case rendering::CompositeOperation::OUTSIDE_REVERSE
:
187 compositingMode
= CAIRO_OPERATOR_DEST_OUT
;
189 case rendering::CompositeOperation::ATOP
:
190 compositingMode
= CAIRO_OPERATOR_ATOP
;
192 case rendering::CompositeOperation::ATOP_REVERSE
:
193 compositingMode
= CAIRO_OPERATOR_DEST_ATOP
;
195 case rendering::CompositeOperation::XOR
:
196 compositingMode
= CAIRO_OPERATOR_XOR
;
198 case rendering::CompositeOperation::ADD
:
199 compositingMode
= CAIRO_OPERATOR_ADD
;
201 case rendering::CompositeOperation::SATURATE
:
202 compositingMode
= CAIRO_OPERATOR_SATURATE
;
205 cairo_set_operator( mpCairo
.get(), compositingMode
);
208 void CanvasHelper::clear()
210 SAL_INFO( "canvas.cairo", "clear whole area: " << maSize
.getWidth() << " x " << maSize
.getHeight() );
215 cairo_save( mpCairo
.get() );
217 cairo_identity_matrix( mpCairo
.get() );
218 // this does not really differ from all-zero, as cairo
219 // internally converts to premultiplied alpha. but anyway,
220 // this keeps it consistent with the other canvas impls
222 cairo_set_source_rgba( mpCairo
.get(), 1.0, 1.0, 1.0, 0.0 );
224 cairo_set_source_rgb( mpCairo
.get(), 1.0, 1.0, 1.0 );
225 cairo_set_operator( mpCairo
.get(), CAIRO_OPERATOR_SOURCE
);
227 cairo_rectangle( mpCairo
.get(), 0, 0, maSize
.getWidth(), maSize
.getHeight() );
228 cairo_fill( mpCairo
.get() );
230 cairo_restore( mpCairo
.get() );
233 void CanvasHelper::drawLine( const rendering::XCanvas
* /*pCanvas*/,
234 const geometry::RealPoint2D
& aStartPoint
,
235 const geometry::RealPoint2D
& aEndPoint
,
236 const rendering::ViewState
& viewState
,
237 const rendering::RenderState
& renderState
)
242 cairo_save( mpCairo
.get() );
244 cairo_set_line_width( mpCairo
.get(), 1 );
246 useStates( viewState
, renderState
, true );
248 cairo_move_to( mpCairo
.get(), aStartPoint
.X
+ 0.5, aStartPoint
.Y
+ 0.5 );
249 cairo_line_to( mpCairo
.get(), aEndPoint
.X
+ 0.5, aEndPoint
.Y
+ 0.5 );
250 cairo_stroke( mpCairo
.get() );
252 cairo_restore( mpCairo
.get() );
255 void CanvasHelper::drawBezier( const rendering::XCanvas
* ,
256 const geometry::RealBezierSegment2D
& aBezierSegment
,
257 const geometry::RealPoint2D
& aEndPoint
,
258 const rendering::ViewState
& viewState
,
259 const rendering::RenderState
& renderState
)
264 cairo_save( mpCairo
.get() );
266 cairo_set_line_width( mpCairo
.get(), 1 );
268 useStates( viewState
, renderState
, true );
270 cairo_move_to( mpCairo
.get(), aBezierSegment
.Px
+ 0.5, aBezierSegment
.Py
+ 0.5 );
271 // tdf#99165 correction of control points not needed here, only hairlines drawn
272 // (see cairo_set_line_width above)
273 cairo_curve_to( mpCairo
.get(),
274 aBezierSegment
.C1x
+ 0.5, aBezierSegment
.C1y
+ 0.5,
275 aBezierSegment
.C2x
+ 0.5, aBezierSegment
.C2y
+ 0.5,
276 aEndPoint
.X
+ 0.5, aEndPoint
.Y
+ 0.5 );
277 cairo_stroke( mpCairo
.get() );
279 cairo_restore( mpCairo
.get() );
282 constexpr OUStringLiteral PARAMETRICPOLYPOLYGON_IMPLEMENTATION_NAME
= u
"Canvas::ParametricPolyPolygon";
284 /** surfaceFromXBitmap Create a surface from XBitmap
285 * @param xBitmap bitmap image that will be used for the surface
286 * @param bHasAlpha will be set to true if resulting surface has alpha
288 * This is a helper function for the other surfaceFromXBitmap().
289 * This function tries to create surface from xBitmap by checking if xBitmap is CanvasBitmap or SpriteCanvas.
291 * @return created surface or NULL
293 static SurfaceSharedPtr
surfaceFromXBitmap( const uno::Reference
< rendering::XBitmap
>& xBitmap
)
295 CanvasBitmap
* pBitmapImpl
= dynamic_cast< CanvasBitmap
* >( xBitmap
.get() );
297 return pBitmapImpl
->getSurface();
299 SurfaceProvider
* pSurfaceProvider
= dynamic_cast<SurfaceProvider
*>( xBitmap
.get() );
300 if( pSurfaceProvider
)
301 return pSurfaceProvider
->getSurface();
303 return SurfaceSharedPtr();
306 static ::BitmapEx
bitmapExFromXBitmap( const uno::Reference
< rendering::XBitmap
>& xBitmap
)
308 // TODO(F1): Add support for floating point bitmap formats
309 uno::Reference
<rendering::XIntegerReadOnlyBitmap
> xIntBmp(xBitmap
,
310 uno::UNO_QUERY_THROW
);
311 ::BitmapEx aBmpEx
= vcl::unotools::bitmapExFromXBitmap(xIntBmp
);
312 if( !aBmpEx
.IsEmpty() )
315 // TODO(F1): extract pixel from XBitmap interface
316 ENSURE_OR_THROW( false,
317 "bitmapExFromXBitmap(): could not extract BitmapEx" );
322 /** surfaceFromXBitmap Create a surface from XBitmap
323 * @param xBitmap bitmap image that will be used for the surface
324 * @param rDevice reference to the device into which we want to draw
325 * @param data will be filled with alpha data, if xBitmap is alpha/transparent image
326 * @param bHasAlpha will be set to true if resulting surface has alpha
328 * This function tries various methods for creating a surface from xBitmap. It also uses
329 * the helper function surfaceFromXBitmap( xBitmap, bHasAlpha )
331 * @return created surface or NULL
333 static SurfaceSharedPtr
surfaceFromXBitmap( const uno::Reference
< rendering::XBitmap
>& xBitmap
, const SurfaceProviderRef
& rSurfaceProvider
, unsigned char*& data
, bool& bHasAlpha
)
335 bHasAlpha
= xBitmap
->hasAlpha();
336 SurfaceSharedPtr pSurface
= surfaceFromXBitmap( xBitmap
);
341 ::BitmapEx aBmpEx
= bitmapExFromXBitmap(xBitmap
);
342 ::Bitmap aBitmap
= aBmpEx
.GetBitmap();
344 // there's no pixmap for alpha bitmap. we might still
345 // use rgb pixmap and only access alpha pixels the
346 // slow way. now we just speedup rgb bitmaps
347 if( !aBmpEx
.IsAlpha() )
349 pSurface
= rSurfaceProvider
->createSurface( aBitmap
);
358 vcl::bitmap::CanvasCairoExtractBitmapData(aBmpEx
, aBitmap
, data
, bHasAlpha
, nWidth
, nHeight
);
360 pSurface
= rSurfaceProvider
->getOutputDevice()->CreateSurface(
361 CairoSurfaceSharedPtr(
362 cairo_image_surface_create_for_data(
364 bHasAlpha
? CAIRO_FORMAT_ARGB32
: CAIRO_FORMAT_RGB24
,
365 nWidth
, nHeight
, nWidth
*4 ),
366 &cairo_surface_destroy
) );
368 SAL_INFO( "canvas.cairo","image: " << nWidth
<< " x " << nHeight
<< " alpha: " << bHasAlpha
);
375 static void addColorStops( cairo_pattern_t
* pPattern
, const uno::Sequence
< uno::Sequence
< double > >& rColors
, const uno::Sequence
< double >& rStops
, bool bReverseStops
)
379 OSL_ASSERT( rColors
.getLength() == rStops
.getLength() );
381 for( i
= 0; i
< rColors
.getLength(); i
++ )
383 const uno::Sequence
< double >& rColor( rColors
[i
] );
384 float stop
= bReverseStops
? 1 - rStops
[i
] : rStops
[i
];
385 if( rColor
.getLength() == 3 )
386 cairo_pattern_add_color_stop_rgb( pPattern
, stop
, rColor
[0], rColor
[1], rColor
[2] );
387 else if( rColor
.getLength() == 4 )
389 double alpha
= rColor
[3];
390 // cairo expects premultiplied alpha
391 cairo_pattern_add_color_stop_rgba( pPattern
, stop
, rColor
[0]*alpha
, rColor
[1]*alpha
, rColor
[2]*alpha
, alpha
);
396 static uno::Sequence
<double> lerp(const uno::Sequence
<double>& rLeft
, const uno::Sequence
<double>& rRight
, double fAlpha
)
398 if( rLeft
.getLength() == 3 )
402 basegfx::utils::lerp(rLeft
[0],rRight
[0],fAlpha
),
403 basegfx::utils::lerp(rLeft
[1],rRight
[1],fAlpha
),
404 basegfx::utils::lerp(rLeft
[2],rRight
[2],fAlpha
)
407 else if( rLeft
.getLength() == 4 )
411 basegfx::utils::lerp(rLeft
[0],rRight
[0],fAlpha
),
412 basegfx::utils::lerp(rLeft
[1],rRight
[1],fAlpha
),
413 basegfx::utils::lerp(rLeft
[2],rRight
[2],fAlpha
),
414 basegfx::utils::lerp(rLeft
[3],rRight
[3],fAlpha
)
421 static cairo_pattern_t
* patternFromParametricPolyPolygon( ::canvas::ParametricPolyPolygon
const & rPolygon
)
423 cairo_pattern_t
* pPattern
= nullptr;
424 const ::canvas::ParametricPolyPolygon::Values
& aValues
= rPolygon
.getValues();
425 double x0
, x1
, y0
, y1
, cx
, cy
, r0
, r1
;
427 switch( aValues
.meType
)
429 case ::canvas::ParametricPolyPolygon::GradientType::Linear
:
434 pPattern
= cairo_pattern_create_linear( x0
, y0
, x1
, y1
);
435 addColorStops( pPattern
, aValues
.maColors
, aValues
.maStops
, false );
438 case ::canvas::ParametricPolyPolygon::GradientType::Elliptical
:
444 pPattern
= cairo_pattern_create_radial( cx
, cy
, r0
, cy
, cy
, r1
);
445 addColorStops( pPattern
, aValues
.maColors
, aValues
.maStops
, true );
454 static void doOperation( Operation aOperation
,
456 const uno::Sequence
< rendering::Texture
>* pTextures
,
457 const SurfaceProviderRef
& pDevice
,
458 const basegfx::B2DRange
& rBounds
)
463 /* TODO: multitexturing */
466 const css::rendering::Texture
& aTexture ( (*pTextures
)[0] );
467 if( aTexture
.Bitmap
.is() )
469 unsigned char* data
= nullptr;
470 bool bHasAlpha
= false;
471 SurfaceSharedPtr pSurface
= surfaceFromXBitmap( (*pTextures
)[0].Bitmap
, pDevice
, data
, bHasAlpha
);
475 cairo_pattern_t
* pPattern
;
477 cairo_save( pCairo
);
479 css::geometry::AffineMatrix2D
aTransform( aTexture
.AffineTransform
);
480 cairo_matrix_t aScaleMatrix
, aTextureMatrix
, aScaledTextureMatrix
;
482 cairo_matrix_init( &aTextureMatrix
,
483 aTransform
.m00
, aTransform
.m10
, aTransform
.m01
,
484 aTransform
.m11
, aTransform
.m02
, aTransform
.m12
);
486 geometry::IntegerSize2D aSize
= aTexture
.Bitmap
->getSize();
488 cairo_matrix_init_scale( &aScaleMatrix
, 1.0/aSize
.Width
, 1.0/aSize
.Height
);
489 cairo_matrix_multiply( &aScaledTextureMatrix
, &aScaleMatrix
, &aTextureMatrix
);
490 cairo_matrix_invert( &aScaledTextureMatrix
);
492 // we don't care about repeat mode yet, so the workaround is disabled for now
493 pPattern
= cairo_pattern_create_for_surface( pSurface
->getCairoSurface().get() );
495 if( aTexture
.RepeatModeX
== rendering::TexturingMode::REPEAT
&&
496 aTexture
.RepeatModeY
== rendering::TexturingMode::REPEAT
)
498 cairo_pattern_set_extend( pPattern
, CAIRO_EXTEND_REPEAT
);
500 else if ( aTexture
.RepeatModeX
== rendering::TexturingMode::NONE
&&
501 aTexture
.RepeatModeY
== rendering::TexturingMode::NONE
)
503 cairo_pattern_set_extend( pPattern
, CAIRO_EXTEND_NONE
);
505 else if ( aTexture
.RepeatModeX
== rendering::TexturingMode::CLAMP
&&
506 aTexture
.RepeatModeY
== rendering::TexturingMode::CLAMP
)
508 cairo_pattern_set_extend( pPattern
, CAIRO_EXTEND_PAD
);
511 aScaledTextureMatrix
.x0
= basegfx::fround( aScaledTextureMatrix
.x0
);
512 aScaledTextureMatrix
.y0
= basegfx::fround( aScaledTextureMatrix
.y0
);
514 double x1
, y1
, x2
, y2
;
515 cairo_path_extents(pCairo
, &x1
, &y1
, &x2
, &y2
);
516 aScaledTextureMatrix
.x0
-= (x1
* aScaledTextureMatrix
.xx
);
517 aScaledTextureMatrix
.y0
-= (y1
* aScaledTextureMatrix
.yy
);
519 cairo_pattern_set_matrix( pPattern
, &aScaledTextureMatrix
);
521 cairo_set_source( pCairo
, pPattern
);
523 cairo_set_operator( pCairo
, CAIRO_OPERATOR_SOURCE
);
524 cairo_fill( pCairo
);
526 cairo_restore( pCairo
);
528 cairo_pattern_destroy( pPattern
);
534 else if( aTexture
.Gradient
.is() )
536 uno::Reference
< lang::XServiceInfo
> xRef( aTexture
.Gradient
, uno::UNO_QUERY
);
538 SAL_INFO( "canvas.cairo", "gradient fill" );
539 if( xRef
.is() && xRef
->getImplementationName() == PARAMETRICPOLYPOLYGON_IMPLEMENTATION_NAME
)
541 // TODO(Q1): Maybe use dynamic_cast here
543 // TODO(E1): Return value
544 // TODO(F1): FillRule
545 SAL_INFO( "canvas.cairo", "known implementation" );
547 ::canvas::ParametricPolyPolygon
* pPolyImpl
= static_cast< ::canvas::ParametricPolyPolygon
* >( aTexture
.Gradient
.get() );
548 css::geometry::AffineMatrix2D
aTransform( aTexture
.AffineTransform
);
549 cairo_matrix_t aTextureMatrix
;
551 cairo_matrix_init( &aTextureMatrix
,
552 aTransform
.m00
, aTransform
.m10
, aTransform
.m01
,
553 aTransform
.m11
, aTransform
.m02
, aTransform
.m12
);
554 if( pPolyImpl
->getValues().meType
== canvas::ParametricPolyPolygon::GradientType::Rectangular
)
556 // no general path gradient yet in cairo; emulate then
557 cairo_save( pCairo
);
558 cairo_clip( pCairo
);
560 // fill bound rect with start color
561 cairo_rectangle( pCairo
, rBounds
.getMinX(), rBounds
.getMinY(),
562 rBounds
.getWidth(), rBounds
.getHeight() );
563 setColor(pCairo
,pPolyImpl
->getValues().maColors
[0]);
566 cairo_transform( pCairo
, &aTextureMatrix
);
568 // longest line in gradient bound rect
569 const unsigned int nGradientSize(
570 static_cast<unsigned int>(
571 ::basegfx::B2DVector(rBounds
.getMinimum() - rBounds
.getMaximum()).getLength() + 1.0 ) );
573 // typical number for pixel of the same color (strip size)
574 const unsigned int nStripSize( nGradientSize
< 50 ? 2 : 4 );
576 // use at least three steps, and at utmost the number of color
578 const unsigned int nStepCount(
582 nGradientSize
/ nStripSize
,
585 const uno::Sequence
<double>* pColors
=&pPolyImpl
->getValues().maColors
[0];
586 basegfx::utils::KeyStopLerp
aLerper(pPolyImpl
->getValues().maStops
);
587 for( unsigned int i
=1; i
<nStepCount
; ++i
)
589 const double fT( i
/double(nStepCount
) );
591 std::ptrdiff_t nIndex
;
593 std::tie(nIndex
,fAlpha
)=aLerper
.lerp(fT
);
595 setColor(pCairo
, lerp(pColors
[nIndex
], pColors
[nIndex
+1], fAlpha
));
596 cairo_rectangle( pCairo
, -1+fT
, -1+fT
, 2-2*fT
, 2-2*fT
);
600 cairo_restore( pCairo
);
604 cairo_pattern_t
* pPattern
= patternFromParametricPolyPolygon( *pPolyImpl
);
608 SAL_INFO( "canvas.cairo", "filling with pattern" );
610 cairo_save( pCairo
);
612 cairo_transform( pCairo
, &aTextureMatrix
);
613 cairo_set_source( pCairo
, pPattern
);
614 cairo_fill( pCairo
);
615 cairo_restore( pCairo
);
617 cairo_pattern_destroy( pPattern
);
624 cairo_fill( pCairo
);
625 SAL_INFO( "canvas.cairo", "fill");
628 cairo_stroke( pCairo
);
629 SAL_INFO( "canvas.cairo", "stroke");
632 cairo_clip( pCairo
);
633 SAL_INFO( "canvas.cairo", "clip");
638 static void clipNULL( cairo_t
*pCairo
)
640 SAL_INFO( "canvas.cairo", "clipNULL");
641 cairo_matrix_t aOrigMatrix
, aIdentityMatrix
;
643 /* we set identity matrix here to overcome bug in cairo 0.9.2
644 where XCreatePixmap is called with zero width and height.
646 it also reaches faster path in cairo clipping code.
648 cairo_matrix_init_identity( &aIdentityMatrix
);
649 cairo_get_matrix( pCairo
, &aOrigMatrix
);
650 cairo_set_matrix( pCairo
, &aIdentityMatrix
);
652 cairo_reset_clip( pCairo
);
653 cairo_rectangle( pCairo
, 0, 0, 1, 1 );
654 cairo_clip( pCairo
);
655 cairo_rectangle( pCairo
, 2, 0, 1, 1 );
656 cairo_clip( pCairo
);
658 /* restore the original matrix */
659 cairo_set_matrix( pCairo
, &aOrigMatrix
);
662 void doPolyPolygonImplementation( const ::basegfx::B2DPolyPolygon
& aPolyPolygon
,
663 Operation aOperation
,
665 const uno::Sequence
< rendering::Texture
>* pTextures
,
666 const SurfaceProviderRef
& pDevice
,
667 rendering::FillRule eFillrule
)
670 ENSURE_ARG_OR_THROW( pTextures
->hasElements(),
671 "CanvasHelper::fillTexturedPolyPolygon: empty texture sequence");
673 bool bOpToDo
= false;
674 cairo_matrix_t aOrigMatrix
, aIdentityMatrix
;
675 double nX
, nY
, nBX
, nBY
, nAX
, nAY
, nLastX(0.0), nLastY(0.0);
677 cairo_get_matrix( pCairo
, &aOrigMatrix
);
678 cairo_matrix_init_identity( &aIdentityMatrix
);
679 cairo_set_matrix( pCairo
, &aIdentityMatrix
);
681 cairo_set_fill_rule( pCairo
,
682 eFillrule
== rendering::FillRule_EVEN_ODD
?
683 CAIRO_FILL_RULE_EVEN_ODD
: CAIRO_FILL_RULE_WINDING
);
685 for( sal_uInt32 nPolygonIndex
= 0; nPolygonIndex
< aPolyPolygon
.count(); nPolygonIndex
++ )
687 const ::basegfx::B2DPolygon
& aPolygon( aPolyPolygon
.getB2DPolygon( nPolygonIndex
) );
688 const sal_uInt32
nPointCount( aPolygon
.count() );
689 // to correctly render closed curves, need to output first
690 // point twice (so output one additional point)
691 const sal_uInt32
nExtendedPointCount( nPointCount
+
692 int(aPolygon
.isClosed() && aPolygon
.areControlPointsUsed()) );
696 bool bIsBezier
= aPolygon
.areControlPointsUsed();
697 ::basegfx::B2DPoint aA
, aB
, aP
;
699 for( sal_uInt32 j
=0; j
< nExtendedPointCount
; j
++ )
701 aP
= aPolygon
.getB2DPoint( j
% nPointCount
);
705 cairo_matrix_transform_point( &aOrigMatrix
, &nX
, &nY
);
707 if (!bIsBezier
&& aOperation
== Clip
)
709 nX
= basegfx::fround( nX
);
710 nY
= basegfx::fround( nY
);
713 if( aOperation
== Stroke
)
721 cairo_move_to( pCairo
, nX
, nY
);
722 SAL_INFO( "canvas.cairo", "move to " << nX
<< "," << nY
);
728 aA
= aPolygon
.getNextControlPoint( (j
-1) % nPointCount
);
729 aB
= aPolygon
.getPrevControlPoint( j
% nPointCount
);
736 cairo_matrix_transform_point( &aOrigMatrix
, &nAX
, &nAY
);
737 cairo_matrix_transform_point( &aOrigMatrix
, &nBX
, &nBY
);
739 if( aOperation
== Stroke
)
747 // tdf#99165 if the control points are 'empty', create the mathematical
748 // correct replacement ones to avoid problems with the graphical sub-system
749 // tdf#101026 The 1st attempt to create a mathematically correct replacement control
750 // vector was wrong. Best alternative is one as close as possible which means short.
751 if (basegfx::fTools::equal(nAX
, nLastX
) && basegfx::fTools::equal(nAY
, nLastY
))
753 nAX
= nLastX
+ ((nBX
- nLastX
) * 0.0005);
754 nAY
= nLastY
+ ((nBY
- nLastY
) * 0.0005);
757 if(basegfx::fTools::equal(nBX
, nX
) && basegfx::fTools::equal(nBY
, nY
))
759 nBX
= nX
+ ((nAX
- nX
) * 0.0005);
760 nBY
= nY
+ ((nAY
- nY
) * 0.0005);
763 cairo_curve_to( pCairo
, nAX
, nAY
, nBX
, nBY
, nX
, nY
);
767 cairo_line_to( pCairo
, nX
, nY
);
768 SAL_INFO( "canvas.cairo", "line to " << nX
<< "," << nY
);
777 if( aPolygon
.isClosed() )
778 cairo_close_path( pCairo
);
783 SAL_INFO( "canvas.cairo", "empty polygon for op: " << aOperation
);
784 if( aOperation
== Clip
)
793 if( aOperation
== Fill
&& pTextures
)
795 cairo_set_matrix( pCairo
, &aOrigMatrix
);
796 doOperation( aOperation
, pCairo
, pTextures
, pDevice
, aPolyPolygon
.getB2DRange() );
797 cairo_set_matrix( pCairo
, &aIdentityMatrix
);
800 if( bOpToDo
&& ( aOperation
!= Fill
|| !pTextures
) )
801 doOperation( aOperation
, pCairo
, pTextures
, pDevice
, aPolyPolygon
.getB2DRange() );
803 cairo_set_matrix( pCairo
, &aOrigMatrix
);
805 if( aPolyPolygon
.count() == 0 && aOperation
== Clip
)
809 void CanvasHelper::doPolyPolygonPath( const uno::Reference
< rendering::XPolyPolygon2D
>& xPolyPolygon
,
810 Operation aOperation
,
812 const uno::Sequence
< rendering::Texture
>* pTextures
) const
814 const ::basegfx::B2DPolyPolygon
aPolyPoly(
815 ::basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D(xPolyPolygon
) );
817 cairo_t
* pCairo
= mpCairo
.get();
819 if(bNoLineJoin
&& aOperation
== Stroke
)
821 // emulate rendering::PathJoinType::NONE by painting single edges
822 for(sal_uInt32
a(0); a
< aPolyPoly
.count(); a
++)
824 const basegfx::B2DPolygon
& aCandidate(aPolyPoly
.getB2DPolygon(a
));
825 const sal_uInt32
nPointCount(aCandidate
.count());
829 const sal_uInt32
nEdgeCount(aCandidate
.isClosed() ? nPointCount
: nPointCount
- 1);
830 basegfx::B2DPolygon aEdge
;
831 aEdge
.append(aCandidate
.getB2DPoint(0));
832 aEdge
.append(basegfx::B2DPoint(0.0, 0.0));
834 for(sal_uInt32
b(0); b
< nEdgeCount
; b
++)
836 const sal_uInt32
nNextIndex((b
+ 1) % nPointCount
);
837 aEdge
.setB2DPoint(1, aCandidate
.getB2DPoint(nNextIndex
));
838 aEdge
.setNextControlPoint(0, aCandidate
.getNextControlPoint(b
% nPointCount
));
839 aEdge
.setPrevControlPoint(1, aCandidate
.getPrevControlPoint(nNextIndex
));
841 doPolyPolygonImplementation( basegfx::B2DPolyPolygon(aEdge
),
845 xPolyPolygon
->getFillRule() );
848 aEdge
.setB2DPoint(0, aEdge
.getB2DPoint(1));
855 doPolyPolygonImplementation( aPolyPoly
, aOperation
,
858 xPolyPolygon
->getFillRule() );
862 uno::Reference
< rendering::XCachedPrimitive
> CanvasHelper::drawPolyPolygon( const rendering::XCanvas
* ,
863 const uno::Reference
< rendering::XPolyPolygon2D
>& xPolyPolygon
,
864 const rendering::ViewState
& viewState
,
865 const rendering::RenderState
& renderState
)
867 #ifdef CAIRO_CANVAS_PERF_TRACE
868 struct timespec aTimer
;
869 mxDevice
->startPerfTrace( &aTimer
);
874 cairo_save( mpCairo
.get() );
876 cairo_set_line_width( mpCairo
.get(), 1 );
878 useStates( viewState
, renderState
, true );
879 doPolyPolygonPath( xPolyPolygon
, Stroke
);
881 cairo_restore( mpCairo
.get() );
884 SAL_INFO( "canvas.cairo", "CanvasHelper called after it was disposed");
886 #ifdef CAIRO_CANVAS_PERF_TRACE
887 mxDevice
->stopPerfTrace( &aTimer
, "drawPolyPolygon" );
890 return uno::Reference
< rendering::XCachedPrimitive
>(nullptr);
893 uno::Reference
< rendering::XCachedPrimitive
> CanvasHelper::strokePolyPolygon( const rendering::XCanvas
* ,
894 const uno::Reference
< rendering::XPolyPolygon2D
>& xPolyPolygon
,
895 const rendering::ViewState
& viewState
,
896 const rendering::RenderState
& renderState
,
897 const rendering::StrokeAttributes
& strokeAttributes
)
899 #ifdef CAIRO_CANVAS_PERF_TRACE
900 struct timespec aTimer
;
901 mxDevice
->startPerfTrace( &aTimer
);
906 cairo_save( mpCairo
.get() );
908 useStates( viewState
, renderState
, true );
910 cairo_matrix_t aMatrix
;
911 cairo_get_matrix( mpCairo
.get(), &aMatrix
);
912 double scaleFactorX
= 1;
913 double scaleFactorY
= 0;
914 cairo_matrix_transform_distance( &aMatrix
, &scaleFactorX
, &scaleFactorY
);
915 double scaleFactor
= basegfx::B2DVector( scaleFactorX
, scaleFactorY
).getLength();
916 cairo_set_line_width( mpCairo
.get(), strokeAttributes
.StrokeWidth
* scaleFactor
);
918 cairo_set_miter_limit( mpCairo
.get(), strokeAttributes
.MiterLimit
);
920 // FIXME: cairo doesn't handle end cap so far (rodo)
921 switch( strokeAttributes
.StartCapType
)
923 case rendering::PathCapType::BUTT
:
924 cairo_set_line_cap( mpCairo
.get(), CAIRO_LINE_CAP_BUTT
);
926 case rendering::PathCapType::ROUND
:
927 cairo_set_line_cap( mpCairo
.get(), CAIRO_LINE_CAP_ROUND
);
929 case rendering::PathCapType::SQUARE
:
930 cairo_set_line_cap( mpCairo
.get(), CAIRO_LINE_CAP_SQUARE
);
934 bool bNoLineJoin(false);
936 switch( strokeAttributes
.JoinType
)
938 case rendering::PathJoinType::NONE
:
940 [[fallthrough
]]; // cairo doesn't have join type NONE so we use MITER as it's pretty close
941 case rendering::PathJoinType::MITER
:
942 cairo_set_line_join( mpCairo
.get(), CAIRO_LINE_JOIN_MITER
);
944 case rendering::PathJoinType::ROUND
:
945 cairo_set_line_join( mpCairo
.get(), CAIRO_LINE_JOIN_ROUND
);
947 case rendering::PathJoinType::BEVEL
:
948 cairo_set_line_join( mpCairo
.get(), CAIRO_LINE_JOIN_BEVEL
);
952 //tdf#103026 If the scaling is 0, then all dashes become zero so
953 //cairo will set the cairo_t status to CAIRO_STATUS_INVALID_DASH
954 //and no further drawing will occur
955 if (strokeAttributes
.DashArray
.hasElements() && scaleFactor
> 0.0)
957 auto aDashArray(comphelper::sequenceToContainer
<std::vector
<double>>(strokeAttributes
.DashArray
));
958 for (auto& rDash
: aDashArray
)
959 rDash
*= scaleFactor
;
960 cairo_set_dash(mpCairo
.get(), aDashArray
.data(), aDashArray
.size(), 0);
963 // TODO(rodo) use LineArray of strokeAttributes
965 doPolyPolygonPath( xPolyPolygon
, Stroke
, bNoLineJoin
);
967 cairo_restore( mpCairo
.get() );
970 SAL_INFO( "canvas.cairo", "CanvasHelper called after it was disposed");
972 #ifdef CAIRO_CANVAS_PERF_TRACE
973 mxDevice
->stopPerfTrace( &aTimer
, "strokePolyPolygon" );
976 // TODO(P1): Provide caching here.
977 return uno::Reference
< rendering::XCachedPrimitive
>(nullptr);
980 uno::Reference
< rendering::XCachedPrimitive
> CanvasHelper::strokeTexturedPolyPolygon( const rendering::XCanvas
* ,
981 const uno::Reference
< rendering::XPolyPolygon2D
>& /*xPolyPolygon*/,
982 const rendering::ViewState
& /*viewState*/,
983 const rendering::RenderState
& /*renderState*/,
984 const uno::Sequence
< rendering::Texture
>& /*textures*/,
985 const rendering::StrokeAttributes
& /*strokeAttributes*/ )
988 return uno::Reference
< rendering::XCachedPrimitive
>(nullptr);
991 uno::Reference
< rendering::XCachedPrimitive
> CanvasHelper::strokeTextureMappedPolyPolygon( const rendering::XCanvas
* ,
992 const uno::Reference
< rendering::XPolyPolygon2D
>& /*xPolyPolygon*/,
993 const rendering::ViewState
& /*viewState*/,
994 const rendering::RenderState
& /*renderState*/,
995 const uno::Sequence
< rendering::Texture
>& /*textures*/,
996 const uno::Reference
< geometry::XMapping2D
>& /*xMapping*/,
997 const rendering::StrokeAttributes
& /*strokeAttributes*/ )
1000 return uno::Reference
< rendering::XCachedPrimitive
>(nullptr);
1003 uno::Reference
< rendering::XPolyPolygon2D
> CanvasHelper::queryStrokeShapes( const rendering::XCanvas
* ,
1004 const uno::Reference
< rendering::XPolyPolygon2D
>& /*xPolyPolygon*/,
1005 const rendering::ViewState
& /*viewState*/,
1006 const rendering::RenderState
& /*renderState*/,
1007 const rendering::StrokeAttributes
& /*strokeAttributes*/ )
1010 return uno::Reference
< rendering::XPolyPolygon2D
>(nullptr);
1013 uno::Reference
< rendering::XCachedPrimitive
> CanvasHelper::fillPolyPolygon( const rendering::XCanvas
* ,
1014 const uno::Reference
< rendering::XPolyPolygon2D
>& xPolyPolygon
,
1015 const rendering::ViewState
& viewState
,
1016 const rendering::RenderState
& renderState
)
1018 #ifdef CAIRO_CANVAS_PERF_TRACE
1019 struct timespec aTimer
;
1020 mxDevice
->startPerfTrace( &aTimer
);
1025 cairo_save( mpCairo
.get() );
1027 useStates( viewState
, renderState
, true );
1028 doPolyPolygonPath( xPolyPolygon
, Fill
);
1030 cairo_restore( mpCairo
.get() );
1033 SAL_INFO( "canvas.cairo", "CanvasHelper called after it was disposed");
1035 #ifdef CAIRO_CANVAS_PERF_TRACE
1036 mxDevice
->stopPerfTrace( &aTimer
, "fillPolyPolygon" );
1039 return uno::Reference
< rendering::XCachedPrimitive
>(nullptr);
1042 uno::Reference
< rendering::XCachedPrimitive
> CanvasHelper::fillTexturedPolyPolygon( const rendering::XCanvas
* ,
1043 const uno::Reference
< rendering::XPolyPolygon2D
>& xPolyPolygon
,
1044 const rendering::ViewState
& viewState
,
1045 const rendering::RenderState
& renderState
,
1046 const uno::Sequence
< rendering::Texture
>& textures
)
1050 cairo_save( mpCairo
.get() );
1052 useStates( viewState
, renderState
, true );
1053 doPolyPolygonPath( xPolyPolygon
, Fill
, false, &textures
);
1055 cairo_restore( mpCairo
.get() );
1058 return uno::Reference
< rendering::XCachedPrimitive
>(nullptr);
1061 uno::Reference
< rendering::XCachedPrimitive
> CanvasHelper::fillTextureMappedPolyPolygon( const rendering::XCanvas
* ,
1062 const uno::Reference
< rendering::XPolyPolygon2D
>& /*xPolyPolygon*/,
1063 const rendering::ViewState
& /*viewState*/,
1064 const rendering::RenderState
& /*renderState*/,
1065 const uno::Sequence
< rendering::Texture
>& /*textures*/,
1066 const uno::Reference
< geometry::XMapping2D
>& /*xMapping*/ )
1069 return uno::Reference
< rendering::XCachedPrimitive
>(nullptr);
1072 uno::Reference
< rendering::XCachedPrimitive
> CanvasHelper::implDrawBitmapSurface( const rendering::XCanvas
* pCanvas
,
1073 const SurfaceSharedPtr
& pInputSurface
,
1074 const rendering::ViewState
& viewState
,
1075 const rendering::RenderState
& renderState
,
1076 const geometry::IntegerSize2D
& rSize
,
1077 bool bModulateColors
,
1080 SurfaceSharedPtr pSurface
=pInputSurface
;
1081 uno::Reference
< rendering::XCachedPrimitive
> rv
;
1082 geometry::IntegerSize2D aBitmapSize
= rSize
;
1086 cairo_save( mpCairo
.get() );
1088 cairo_rectangle( mpCairo
.get(), 0, 0, maSize
.getWidth(), maSize
.getHeight() );
1089 cairo_clip( mpCairo
.get() );
1091 useStates( viewState
, renderState
, true );
1093 cairo_matrix_t aMatrix
;
1095 cairo_get_matrix( mpCairo
.get(), &aMatrix
);
1096 if( ! ::rtl::math::approxEqual( aMatrix
.xx
, 1 ) &&
1097 ! ::rtl::math::approxEqual( aMatrix
.yy
, 1 ) &&
1098 ::rtl::math::approxEqual( aMatrix
.x0
, 0 ) &&
1099 ::rtl::math::approxEqual( aMatrix
.y0
, 0 ) &&
1100 basegfx::fround( rSize
.Width
* aMatrix
.xx
) > 8 &&
1101 basegfx::fround( rSize
.Height
* aMatrix
.yy
) > 8 )
1103 double dWidth
, dHeight
;
1105 dWidth
= basegfx::fround( rSize
.Width
* aMatrix
.xx
);
1106 dHeight
= basegfx::fround( rSize
.Height
* aMatrix
.yy
);
1107 aBitmapSize
.Width
= static_cast<sal_Int32
>( dWidth
);
1108 aBitmapSize
.Height
= static_cast<sal_Int32
>( dHeight
);
1110 SurfaceSharedPtr pScaledSurface
= mpSurfaceProvider
->createSurface(
1111 ::basegfx::B2ISize( aBitmapSize
.Width
, aBitmapSize
.Height
),
1112 bHasAlpha
? CAIRO_CONTENT_COLOR_ALPHA
: CAIRO_CONTENT_COLOR
);
1113 CairoSharedPtr pCairo
= pScaledSurface
->getCairo();
1115 cairo_set_operator( pCairo
.get(), CAIRO_OPERATOR_SOURCE
);
1116 // add 0.5px to size to avoid rounding errors in cairo, leading sometimes to random data on the image right/bottom borders
1117 cairo_scale( pCairo
.get(), (dWidth
+0.5)/rSize
.Width
, (dHeight
+0.5)/rSize
.Height
);
1118 cairo_set_source_surface( pCairo
.get(), pSurface
->getCairoSurface().get(), 0, 0 );
1119 cairo_paint( pCairo
.get() );
1121 pSurface
= std::move(pScaledSurface
);
1123 aMatrix
.xx
= aMatrix
.yy
= 1;
1124 cairo_set_matrix( mpCairo
.get(), &aMatrix
);
1127 new CachedBitmap( pSurface
, viewState
, renderState
,
1128 // cast away const, need to
1129 // change refcount (as this is
1130 // ~invisible to client code,
1131 // still logically const)
1132 const_cast< rendering::XCanvas
* >(pCanvas
)) );
1135 if( !bHasAlpha
&& mbHaveAlpha
)
1137 double x
, y
, width
, height
;
1140 width
= aBitmapSize
.Width
;
1141 height
= aBitmapSize
.Height
;
1142 cairo_matrix_transform_point( &aMatrix
, &x
, &y
);
1143 cairo_matrix_transform_distance( &aMatrix
, &width
, &height
);
1145 // in case the bitmap doesn't have alpha and covers whole area
1146 // we try to change surface to plain rgb
1147 SAL_INFO( "canvas.cairo","chance to change surface to rgb, " << x
<< ", " << y
<< ", " << width
<< " x " << height
<< " (" << maSize
.getWidth() << " x " << maSize
.getHeight() << ")" );
1148 if( x
<= 0 && y
<= 0 && x
+ width
>= maSize
.getWidth() && y
+ height
>= maSize
.getHeight() )
1150 SAL_INFO( "canvas.cairo","trying to change surface to rgb");
1151 if( mpSurfaceProvider
) {
1152 SurfaceSharedPtr pNewSurface
= mpSurfaceProvider
->changeSurface();
1155 setSurface( pNewSurface
, false );
1157 // set state to new mpCairo.get()
1158 useStates( viewState
, renderState
, true );
1159 // use the possibly modified matrix
1160 cairo_set_matrix( mpCairo
.get(), &aMatrix
);
1165 cairo_set_source_surface( mpCairo
.get(), pSurface
->getCairoSurface().get(), 0, 0 );
1167 ::rtl::math::approxEqual( aMatrix
.xx
, 1 ) &&
1168 ::rtl::math::approxEqual( aMatrix
.yy
, 1 ) &&
1169 ::rtl::math::approxEqual( aMatrix
.x0
, 0 ) &&
1170 ::rtl::math::approxEqual( aMatrix
.y0
, 0 ) )
1171 cairo_set_operator( mpCairo
.get(), CAIRO_OPERATOR_SOURCE
);
1172 cairo_pattern_set_extend( cairo_get_source(mpCairo
.get()), CAIRO_EXTEND_PAD
);
1173 cairo_rectangle( mpCairo
.get(), 0, 0, aBitmapSize
.Width
, aBitmapSize
.Height
);
1174 cairo_clip( mpCairo
.get() );
1176 // Use cairo_matrix_transform_distance() to determine the scaling, as that works even if
1177 // the matrix also has rotation.
1178 double fPixelWidth
= rSize
.Width
;
1179 double fPixelHeight
= rSize
.Height
;
1180 cairo_matrix_transform_distance(&aMatrix
, &fPixelWidth
, &fPixelHeight
);
1181 int nPixelWidth
= std::round(fPixelWidth
);
1182 int nPixelHeight
= std::round(fPixelHeight
);
1183 if (std::abs(nPixelWidth
) > 0 && std::abs(nPixelHeight
) > 0)
1185 // Only render the image if it's at least 1x1 px sized.
1186 if (bModulateColors
)
1187 cairo_paint_with_alpha(mpCairo
.get(), renderState
.DeviceColor
[3]);
1190 cairo_paint(mpCairo
.get());
1191 if (cairo_status(mpCairo
.get()) != CAIRO_STATUS_SUCCESS
)
1193 SAL_WARN("canvas.cairo", "cairo_paint() failed: " << cairo_status_to_string(
1194 cairo_status(mpCairo
.get())));
1198 cairo_restore( mpCairo
.get() );
1201 SAL_INFO( "canvas.cairo", "CanvasHelper called after it was disposed");
1203 return rv
; // uno::Reference< rendering::XCachedPrimitive >(NULL);
1206 uno::Reference
< rendering::XCachedPrimitive
> CanvasHelper::drawBitmap( const rendering::XCanvas
* pCanvas
,
1207 const uno::Reference
< rendering::XBitmap
>& xBitmap
,
1208 const rendering::ViewState
& viewState
,
1209 const rendering::RenderState
& renderState
)
1211 #ifdef CAIRO_CANVAS_PERF_TRACE
1212 struct timespec aTimer
;
1213 mxDevice
->startPerfTrace( &aTimer
);
1216 uno::Reference
< rendering::XCachedPrimitive
> rv
;
1217 unsigned char* data
= nullptr;
1218 bool bHasAlpha
= false;
1219 SurfaceSharedPtr pSurface
= surfaceFromXBitmap( xBitmap
, mpSurfaceProvider
, data
, bHasAlpha
);
1220 geometry::IntegerSize2D aSize
= xBitmap
->getSize();
1224 rv
= implDrawBitmapSurface( pCanvas
, pSurface
, viewState
, renderState
, aSize
, false, bHasAlpha
);
1232 #ifdef CAIRO_CANVAS_PERF_TRACE
1233 mxDevice
->stopPerfTrace( &aTimer
, "drawBitmap" );
1239 uno::Reference
< rendering::XCachedPrimitive
> CanvasHelper::drawBitmapModulated( const rendering::XCanvas
* pCanvas
,
1240 const uno::Reference
< rendering::XBitmap
>& xBitmap
,
1241 const rendering::ViewState
& viewState
,
1242 const rendering::RenderState
& renderState
)
1244 #ifdef CAIRO_CANVAS_PERF_TRACE
1245 struct timespec aTimer
;
1246 mxDevice
->startPerfTrace( &aTimer
);
1249 uno::Reference
< rendering::XCachedPrimitive
> rv
;
1250 unsigned char* data
= nullptr;
1251 bool bHasAlpha
= false;
1252 SurfaceSharedPtr pSurface
= surfaceFromXBitmap( xBitmap
, mpSurfaceProvider
, data
, bHasAlpha
);
1253 geometry::IntegerSize2D aSize
= xBitmap
->getSize();
1257 rv
= implDrawBitmapSurface( pCanvas
, pSurface
, viewState
, renderState
, aSize
, true, bHasAlpha
);
1265 #ifdef CAIRO_CANVAS_PERF_TRACE
1266 mxDevice
->stopPerfTrace( &aTimer
, "drawBitmap" );
1273 geometry::IntegerSize2D
CanvasHelper::getSize() const
1275 if( !mpSurfaceProvider
)
1276 return geometry::IntegerSize2D(1, 1); // we're disposed
1278 return ::basegfx::unotools::integerSize2DFromB2ISize( maSize
);
1281 uno::Reference
< rendering::XBitmap
> CanvasHelper::getScaledBitmap( const geometry::RealSize2D
& newSize
,
1284 #ifdef CAIRO_CANVAS_PERF_TRACE
1285 struct timespec aTimer
;
1286 mxDevice
->startPerfTrace( &aTimer
);
1291 return uno::Reference
< rendering::XBitmap
>( new CanvasBitmap( ::basegfx::B2ISize( ::canvas::tools::roundUp( newSize
.Width
),
1292 ::canvas::tools::roundUp( newSize
.Height
) ),
1293 mpSurfaceProvider
, mpDevice
, false ) );
1296 SAL_INFO( "canvas.cairo", "CanvasHelper called after it was disposed");
1298 #ifdef CAIRO_CANVAS_PERF_TRACE
1299 mxDevice
->stopPerfTrace( &aTimer
, "getScaledBitmap" );
1302 return uno::Reference
< rendering::XBitmap
>();
1305 uno::Sequence
< sal_Int8
> CanvasHelper::getData( rendering::IntegerBitmapLayout
& aLayout
,
1306 const geometry::IntegerRectangle2D
& rect
)
1310 const sal_Int32
nWidth( rect
.X2
- rect
.X1
);
1311 const sal_Int32
nHeight( rect
.Y2
- rect
.Y1
);
1312 const cairo_format_t
eFormat( mbHaveAlpha
? CAIRO_FORMAT_ARGB32
: CAIRO_FORMAT_RGB24
);
1313 uno::Sequence
< sal_Int8
> aRes( 4*nWidth
*nHeight
);
1314 sal_Int8
* pData
= aRes
.getArray();
1315 cairo_surface_t
* pImageSurface
= cairo_image_surface_create_for_data( reinterpret_cast<unsigned char *>(pData
),
1317 nWidth
, nHeight
, 4*nWidth
);
1318 cairo_t
* pCairo
= cairo_create( pImageSurface
);
1319 cairo_set_source_surface( pCairo
, mpSurface
->getCairoSurface().get(), -rect
.X1
, -rect
.Y1
);
1320 cairo_paint( pCairo
);
1321 cairo_destroy( pCairo
);
1322 cairo_surface_destroy( pImageSurface
);
1324 aLayout
= impl_getMemoryLayout( nWidth
, nHeight
);
1329 return uno::Sequence
< sal_Int8
>();
1332 uno::Sequence
< sal_Int8
> CanvasHelper::getPixel( rendering::IntegerBitmapLayout
& /*bitmapLayout*/,
1333 const geometry::IntegerPoint2D
& /*pos*/ )
1335 return uno::Sequence
< sal_Int8
>();
1340 class CairoColorSpace
: public cppu::WeakImplHelper
< css::rendering::XIntegerBitmapColorSpace
>
1343 uno::Sequence
< sal_Int8
> maComponentTags
;
1344 uno::Sequence
< sal_Int32
> maBitCounts
;
1346 virtual ::sal_Int8 SAL_CALL
getType( ) override
1348 return rendering::ColorSpaceType::RGB
;
1350 virtual uno::Sequence
< ::sal_Int8
> SAL_CALL
getComponentTags( ) override
1352 return maComponentTags
;
1354 virtual ::sal_Int8 SAL_CALL
getRenderingIntent( ) override
1356 return rendering::RenderingIntent::PERCEPTUAL
;
1358 virtual uno::Sequence
< beans::PropertyValue
> SAL_CALL
getProperties( ) override
1360 return uno::Sequence
< beans::PropertyValue
>();
1362 virtual uno::Sequence
< double > SAL_CALL
convertColorSpace( const uno::Sequence
< double >& deviceColor
,
1363 const uno::Reference
< rendering::XColorSpace
>& targetColorSpace
) override
1365 // TODO(P3): if we know anything about target
1366 // colorspace, this can be greatly sped up
1367 uno::Sequence
<rendering::ARGBColor
> aIntermediate(
1368 convertToARGB(deviceColor
));
1369 return targetColorSpace
->convertFromARGB(aIntermediate
);
1371 virtual uno::Sequence
< rendering::RGBColor
> SAL_CALL
convertToRGB( const uno::Sequence
< double >& deviceColor
) override
1373 const double* pIn( deviceColor
.getConstArray() );
1374 const std::size_t nLen( deviceColor
.getLength() );
1375 ENSURE_ARG_OR_THROW2(nLen
%4==0,
1376 "number of channels no multiple of 4",
1377 static_cast<rendering::XColorSpace
*>(this), 0);
1379 uno::Sequence
< rendering::RGBColor
> aRes(nLen
/4);
1380 rendering::RGBColor
* pOut( aRes
.getArray() );
1381 for( std::size_t i
=0; i
<nLen
; i
+=4 )
1383 const double fAlpha(pIn
[3]);
1385 *pOut
++ = rendering::RGBColor(0.0, 0.0, 0.0);
1387 *pOut
++ = rendering::RGBColor(pIn
[2]/fAlpha
,pIn
[1]/fAlpha
,pIn
[0]/fAlpha
);
1392 virtual uno::Sequence
< rendering::ARGBColor
> SAL_CALL
convertToARGB( const uno::Sequence
< double >& deviceColor
) override
1394 const double* pIn( deviceColor
.getConstArray() );
1395 const std::size_t nLen( deviceColor
.getLength() );
1396 ENSURE_ARG_OR_THROW2(nLen
%4==0,
1397 "number of channels no multiple of 4",
1398 static_cast<rendering::XColorSpace
*>(this), 0);
1400 uno::Sequence
< rendering::ARGBColor
> aRes(nLen
/4);
1401 rendering::ARGBColor
* pOut( aRes
.getArray() );
1402 for( std::size_t i
=0; i
<nLen
; i
+=4 )
1404 const double fAlpha(pIn
[3]);
1406 *pOut
++ = rendering::ARGBColor(0.0, 0.0, 0.0, 0.0);
1408 *pOut
++ = rendering::ARGBColor(fAlpha
,pIn
[2]/fAlpha
,pIn
[1]/fAlpha
,pIn
[0]/fAlpha
);
1413 virtual uno::Sequence
< rendering::ARGBColor
> SAL_CALL
convertToPARGB( const uno::Sequence
< double >& deviceColor
) override
1415 const double* pIn( deviceColor
.getConstArray() );
1416 const std::size_t nLen( deviceColor
.getLength() );
1417 ENSURE_ARG_OR_THROW2(nLen
%4==0,
1418 "number of channels no multiple of 4",
1419 static_cast<rendering::XColorSpace
*>(this), 0);
1421 uno::Sequence
< rendering::ARGBColor
> aRes(nLen
/4);
1422 rendering::ARGBColor
* pOut( aRes
.getArray() );
1423 for( std::size_t i
=0; i
<nLen
; i
+=4 )
1425 *pOut
++ = rendering::ARGBColor(pIn
[3],pIn
[2],pIn
[1],pIn
[1]);
1430 virtual uno::Sequence
< double > SAL_CALL
convertFromRGB( const uno::Sequence
< rendering::RGBColor
>& rgbColor
) override
1432 const rendering::RGBColor
* pIn( rgbColor
.getConstArray() );
1433 const std::size_t nLen( rgbColor
.getLength() );
1435 uno::Sequence
< double > aRes(nLen
*4);
1436 double* pColors
=aRes
.getArray();
1437 for( std::size_t i
=0; i
<nLen
; ++i
)
1439 *pColors
++ = pIn
->Blue
;
1440 *pColors
++ = pIn
->Green
;
1441 *pColors
++ = pIn
->Red
;
1447 virtual uno::Sequence
< double > SAL_CALL
convertFromARGB( const uno::Sequence
< rendering::ARGBColor
>& rgbColor
) override
1449 const rendering::ARGBColor
* pIn( rgbColor
.getConstArray() );
1450 const std::size_t nLen( rgbColor
.getLength() );
1452 uno::Sequence
< double > aRes(nLen
*4);
1453 double* pColors
=aRes
.getArray();
1454 for( std::size_t i
=0; i
<nLen
; ++i
)
1456 *pColors
++ = pIn
->Alpha
*pIn
->Blue
;
1457 *pColors
++ = pIn
->Alpha
*pIn
->Green
;
1458 *pColors
++ = pIn
->Alpha
*pIn
->Red
;
1459 *pColors
++ = pIn
->Alpha
;
1464 virtual uno::Sequence
< double > SAL_CALL
convertFromPARGB( const uno::Sequence
< rendering::ARGBColor
>& rgbColor
) override
1466 const rendering::ARGBColor
* pIn( rgbColor
.getConstArray() );
1467 const std::size_t nLen( rgbColor
.getLength() );
1469 uno::Sequence
< double > aRes(nLen
*4);
1470 double* pColors
=aRes
.getArray();
1471 for( std::size_t i
=0; i
<nLen
; ++i
)
1473 *pColors
++ = pIn
->Blue
;
1474 *pColors
++ = pIn
->Green
;
1475 *pColors
++ = pIn
->Red
;
1476 *pColors
++ = pIn
->Alpha
;
1482 // XIntegerBitmapColorSpace
1483 virtual ::sal_Int32 SAL_CALL
getBitsPerPixel( ) override
1487 virtual uno::Sequence
< ::sal_Int32
> SAL_CALL
getComponentBitCounts( ) override
1491 virtual ::sal_Int8 SAL_CALL
getEndianness( ) override
1493 return util::Endianness::LITTLE
;
1495 virtual uno::Sequence
<double> SAL_CALL
convertFromIntegerColorSpace( const uno::Sequence
< ::sal_Int8
>& deviceColor
,
1496 const uno::Reference
< rendering::XColorSpace
>& targetColorSpace
) override
1498 if( dynamic_cast<CairoColorSpace
*>(targetColorSpace
.get()) )
1500 const sal_Int8
* pIn( deviceColor
.getConstArray() );
1501 const std::size_t nLen( deviceColor
.getLength() );
1502 ENSURE_ARG_OR_THROW2(nLen
%4==0,
1503 "number of channels no multiple of 4",
1504 static_cast<rendering::XColorSpace
*>(this), 0);
1506 uno::Sequence
<double> aRes(nLen
);
1507 double* pOut( aRes
.getArray() );
1508 for( std::size_t i
=0; i
<nLen
; i
+=4 )
1510 *pOut
++ = vcl::unotools::toDoubleColor(*pIn
++);
1511 *pOut
++ = vcl::unotools::toDoubleColor(*pIn
++);
1512 *pOut
++ = vcl::unotools::toDoubleColor(*pIn
++);
1513 *pOut
++ = vcl::unotools::toDoubleColor(*pIn
++);
1519 // TODO(P3): if we know anything about target
1520 // colorspace, this can be greatly sped up
1521 uno::Sequence
<rendering::ARGBColor
> aIntermediate(
1522 convertIntegerToARGB(deviceColor
));
1523 return targetColorSpace
->convertFromARGB(aIntermediate
);
1526 virtual uno::Sequence
< ::sal_Int8
> SAL_CALL
convertToIntegerColorSpace( const uno::Sequence
< ::sal_Int8
>& deviceColor
,
1527 const uno::Reference
< rendering::XIntegerBitmapColorSpace
>& targetColorSpace
) override
1529 if( dynamic_cast<CairoColorSpace
*>(targetColorSpace
.get()) )
1531 // it's us, so simply pass-through the data
1536 // TODO(P3): if we know anything about target
1537 // colorspace, this can be greatly sped up
1538 uno::Sequence
<rendering::ARGBColor
> aIntermediate(
1539 convertIntegerToARGB(deviceColor
));
1540 return targetColorSpace
->convertIntegerFromARGB(aIntermediate
);
1543 virtual uno::Sequence
< rendering::RGBColor
> SAL_CALL
convertIntegerToRGB( const uno::Sequence
< ::sal_Int8
>& deviceColor
) override
1545 const sal_Int8
* pIn( deviceColor
.getConstArray() );
1546 const std::size_t nLen( deviceColor
.getLength() );
1547 ENSURE_ARG_OR_THROW2(nLen
%4==0,
1548 "number of channels no multiple of 4",
1549 static_cast<rendering::XColorSpace
*>(this), 0);
1551 uno::Sequence
< rendering::RGBColor
> aRes(nLen
/4);
1552 rendering::RGBColor
* pOut( aRes
.getArray() );
1553 for( std::size_t i
=0; i
<nLen
; i
+=4 )
1555 const double fAlpha(static_cast<sal_uInt8
>(pIn
[3]));
1557 *pOut
++ = rendering::RGBColor(
1562 *pOut
++ = rendering::RGBColor(0,0,0);
1568 virtual uno::Sequence
< rendering::ARGBColor
> SAL_CALL
convertIntegerToARGB( const uno::Sequence
< ::sal_Int8
>& deviceColor
) override
1570 const sal_Int8
* pIn( deviceColor
.getConstArray() );
1571 const std::size_t nLen( deviceColor
.getLength() );
1572 ENSURE_ARG_OR_THROW2(nLen
%4==0,
1573 "number of channels no multiple of 4",
1574 static_cast<rendering::XColorSpace
*>(this), 0);
1576 uno::Sequence
< rendering::ARGBColor
> aRes(nLen
/4);
1577 rendering::ARGBColor
* pOut( aRes
.getArray() );
1578 for( std::size_t i
=0; i
<nLen
; i
+=4 )
1580 const double fAlpha(static_cast<sal_uInt8
>(pIn
[3]));
1582 *pOut
++ = rendering::ARGBColor(
1588 *pOut
++ = rendering::ARGBColor(0,0,0,0);
1593 virtual uno::Sequence
< rendering::ARGBColor
> SAL_CALL
convertIntegerToPARGB( const uno::Sequence
< ::sal_Int8
>& deviceColor
) override
1595 const sal_Int8
* pIn( deviceColor
.getConstArray() );
1596 const std::size_t nLen( deviceColor
.getLength() );
1597 ENSURE_ARG_OR_THROW2(nLen
%4==0,
1598 "number of channels no multiple of 4",
1599 static_cast<rendering::XColorSpace
*>(this), 0);
1601 uno::Sequence
< rendering::ARGBColor
> aRes(nLen
/4);
1602 rendering::ARGBColor
* pOut( aRes
.getArray() );
1603 for( std::size_t i
=0; i
<nLen
; i
+=4 )
1605 *pOut
++ = rendering::ARGBColor(
1606 vcl::unotools::toDoubleColor(pIn
[3]),
1607 vcl::unotools::toDoubleColor(pIn
[2]),
1608 vcl::unotools::toDoubleColor(pIn
[1]),
1609 vcl::unotools::toDoubleColor(pIn
[0]));
1615 virtual uno::Sequence
< ::sal_Int8
> SAL_CALL
convertIntegerFromRGB( const uno::Sequence
< rendering::RGBColor
>& rgbColor
) override
1617 const rendering::RGBColor
* pIn( rgbColor
.getConstArray() );
1618 const std::size_t nLen( rgbColor
.getLength() );
1620 uno::Sequence
< sal_Int8
> aRes(nLen
*4);
1621 sal_Int8
* pColors
=aRes
.getArray();
1622 for( std::size_t i
=0; i
<nLen
; ++i
)
1624 *pColors
++ = vcl::unotools::toByteColor(pIn
->Blue
);
1625 *pColors
++ = vcl::unotools::toByteColor(pIn
->Green
);
1626 *pColors
++ = vcl::unotools::toByteColor(pIn
->Red
);
1633 virtual uno::Sequence
< ::sal_Int8
> SAL_CALL
convertIntegerFromARGB( const uno::Sequence
< rendering::ARGBColor
>& rgbColor
) override
1635 const rendering::ARGBColor
* pIn( rgbColor
.getConstArray() );
1636 const std::size_t nLen( rgbColor
.getLength() );
1638 uno::Sequence
< sal_Int8
> aRes(nLen
*4);
1639 sal_Int8
* pColors
=aRes
.getArray();
1640 for( std::size_t i
=0; i
<nLen
; ++i
)
1642 const double fAlpha(pIn
->Alpha
);
1643 *pColors
++ = vcl::unotools::toByteColor(fAlpha
*pIn
->Blue
);
1644 *pColors
++ = vcl::unotools::toByteColor(fAlpha
*pIn
->Green
);
1645 *pColors
++ = vcl::unotools::toByteColor(fAlpha
*pIn
->Red
);
1646 *pColors
++ = vcl::unotools::toByteColor(fAlpha
);
1651 virtual uno::Sequence
< ::sal_Int8
> SAL_CALL
convertIntegerFromPARGB( const uno::Sequence
< rendering::ARGBColor
>& rgbColor
) override
1653 const rendering::ARGBColor
* pIn( rgbColor
.getConstArray() );
1654 const std::size_t nLen( rgbColor
.getLength() );
1656 uno::Sequence
< sal_Int8
> aRes(nLen
*4);
1657 sal_Int8
* pColors
=aRes
.getArray();
1658 for( std::size_t i
=0; i
<nLen
; ++i
)
1660 *pColors
++ = vcl::unotools::toByteColor(pIn
->Blue
);
1661 *pColors
++ = vcl::unotools::toByteColor(pIn
->Green
);
1662 *pColors
++ = vcl::unotools::toByteColor(pIn
->Red
);
1663 *pColors
++ = vcl::unotools::toByteColor(pIn
->Alpha
);
1674 sal_Int8
* pTags
= maComponentTags
.getArray();
1675 sal_Int32
* pBitCounts
= maBitCounts
.getArray();
1676 pTags
[0] = rendering::ColorComponentTag::RGB_BLUE
;
1677 pTags
[1] = rendering::ColorComponentTag::RGB_GREEN
;
1678 pTags
[2] = rendering::ColorComponentTag::RGB_RED
;
1679 pTags
[3] = rendering::ColorComponentTag::PREMULTIPLIED_ALPHA
;
1688 class CairoNoAlphaColorSpace
: public cppu::WeakImplHelper
< css::rendering::XIntegerBitmapColorSpace
>
1691 uno::Sequence
< sal_Int8
> maComponentTags
;
1692 uno::Sequence
< sal_Int32
> maBitCounts
;
1694 virtual ::sal_Int8 SAL_CALL
getType( ) override
1696 return rendering::ColorSpaceType::RGB
;
1698 virtual uno::Sequence
< ::sal_Int8
> SAL_CALL
getComponentTags( ) override
1700 return maComponentTags
;
1702 virtual ::sal_Int8 SAL_CALL
getRenderingIntent( ) override
1704 return rendering::RenderingIntent::PERCEPTUAL
;
1706 virtual uno::Sequence
< beans::PropertyValue
> SAL_CALL
getProperties( ) override
1708 return uno::Sequence
< beans::PropertyValue
>();
1710 virtual uno::Sequence
< double > SAL_CALL
convertColorSpace( const uno::Sequence
< double >& deviceColor
,
1711 const uno::Reference
< rendering::XColorSpace
>& targetColorSpace
) override
1713 // TODO(P3): if we know anything about target
1714 // colorspace, this can be greatly sped up
1715 uno::Sequence
<rendering::ARGBColor
> aIntermediate(
1716 convertToARGB(deviceColor
));
1717 return targetColorSpace
->convertFromARGB(aIntermediate
);
1719 virtual uno::Sequence
< rendering::RGBColor
> SAL_CALL
convertToRGB( const uno::Sequence
< double >& deviceColor
) override
1721 const double* pIn( deviceColor
.getConstArray() );
1722 const std::size_t nLen( deviceColor
.getLength() );
1723 ENSURE_ARG_OR_THROW2(nLen
%4==0,
1724 "number of channels no multiple of 4",
1725 static_cast<rendering::XColorSpace
*>(this), 0);
1727 uno::Sequence
< rendering::RGBColor
> aRes(nLen
/4);
1728 rendering::RGBColor
* pOut( aRes
.getArray() );
1729 for( std::size_t i
=0; i
<nLen
; i
+=4 )
1731 *pOut
++ = rendering::RGBColor(pIn
[2], pIn
[1], pIn
[0]);
1736 uno::Sequence
< rendering::ARGBColor
> impl_convertToARGB( const uno::Sequence
< double >& deviceColor
)
1738 const double* pIn( deviceColor
.getConstArray() );
1739 const std::size_t nLen( deviceColor
.getLength() );
1740 ENSURE_ARG_OR_THROW2(nLen
%4==0,
1741 "number of channels no multiple of 4",
1742 static_cast<rendering::XColorSpace
*>(this), 0);
1744 uno::Sequence
< rendering::ARGBColor
> aRes(nLen
/4);
1745 rendering::ARGBColor
* pOut( aRes
.getArray() );
1746 for( std::size_t i
=0; i
<nLen
; i
+=4 )
1748 *pOut
++ = rendering::ARGBColor(1.0, pIn
[2], pIn
[1], pIn
[0]);
1753 virtual uno::Sequence
< rendering::ARGBColor
> SAL_CALL
convertToARGB( const uno::Sequence
< double >& deviceColor
) override
1755 return impl_convertToARGB( deviceColor
);
1757 virtual uno::Sequence
< rendering::ARGBColor
> SAL_CALL
convertToPARGB( const uno::Sequence
< double >& deviceColor
) override
1759 return impl_convertToARGB( deviceColor
);
1761 virtual uno::Sequence
< double > SAL_CALL
convertFromRGB( const uno::Sequence
< rendering::RGBColor
>& rgbColor
) override
1763 const rendering::RGBColor
* pIn( rgbColor
.getConstArray() );
1764 const std::size_t nLen( rgbColor
.getLength() );
1766 uno::Sequence
< double > aRes(nLen
*4);
1767 double* pColors
=aRes
.getArray();
1768 for( std::size_t i
=0; i
<nLen
; ++i
)
1770 *pColors
++ = pIn
->Blue
;
1771 *pColors
++ = pIn
->Green
;
1772 *pColors
++ = pIn
->Red
;
1773 *pColors
++ = 1.0; // the value does not matter
1778 uno::Sequence
< double > impl_convertFromARGB( const uno::Sequence
< rendering::ARGBColor
>& rgbColor
)
1780 const rendering::ARGBColor
* pIn( rgbColor
.getConstArray() );
1781 const std::size_t nLen( rgbColor
.getLength() );
1783 uno::Sequence
< double > aRes(nLen
*4);
1784 double* pColors
=aRes
.getArray();
1785 for( std::size_t i
=0; i
<nLen
; ++i
)
1787 *pColors
++ = pIn
->Blue
;
1788 *pColors
++ = pIn
->Green
;
1789 *pColors
++ = pIn
->Red
;
1790 *pColors
++ = 1.0; // the value does not matter
1795 virtual uno::Sequence
< double > SAL_CALL
convertFromARGB( const uno::Sequence
< rendering::ARGBColor
>& rgbColor
) override
1797 return impl_convertFromARGB( rgbColor
);
1799 virtual uno::Sequence
< double > SAL_CALL
convertFromPARGB( const uno::Sequence
< rendering::ARGBColor
>& rgbColor
) override
1801 return impl_convertFromARGB( rgbColor
);
1804 // XIntegerBitmapColorSpace
1805 virtual ::sal_Int32 SAL_CALL
getBitsPerPixel( ) override
1809 virtual uno::Sequence
< ::sal_Int32
> SAL_CALL
getComponentBitCounts( ) override
1813 virtual ::sal_Int8 SAL_CALL
getEndianness( ) override
1815 return util::Endianness::LITTLE
;
1817 virtual uno::Sequence
<double> SAL_CALL
convertFromIntegerColorSpace( const uno::Sequence
< ::sal_Int8
>& deviceColor
,
1818 const uno::Reference
< rendering::XColorSpace
>& targetColorSpace
) override
1820 if( dynamic_cast<CairoColorSpace
*>(targetColorSpace
.get()) )
1822 const sal_Int8
* pIn( deviceColor
.getConstArray() );
1823 const std::size_t nLen( deviceColor
.getLength() );
1824 ENSURE_ARG_OR_THROW2(nLen
%4==0,
1825 "number of channels no multiple of 4",
1826 static_cast<rendering::XColorSpace
*>(this), 0);
1828 uno::Sequence
<double> aRes(nLen
);
1829 double* pOut( aRes
.getArray() );
1830 for( std::size_t i
=0; i
<nLen
; i
+=4 )
1832 *pOut
++ = vcl::unotools::toDoubleColor(*pIn
++);
1833 *pOut
++ = vcl::unotools::toDoubleColor(*pIn
++);
1834 *pOut
++ = vcl::unotools::toDoubleColor(*pIn
++);
1835 *pOut
++ = 1.0; pIn
++; // the value does not matter
1841 // TODO(P3): if we know anything about target
1842 // colorspace, this can be greatly sped up
1843 uno::Sequence
<rendering::ARGBColor
> aIntermediate(
1844 convertIntegerToARGB(deviceColor
));
1845 return targetColorSpace
->convertFromARGB(aIntermediate
);
1848 virtual uno::Sequence
< ::sal_Int8
> SAL_CALL
convertToIntegerColorSpace( const uno::Sequence
< ::sal_Int8
>& deviceColor
,
1849 const uno::Reference
< rendering::XIntegerBitmapColorSpace
>& targetColorSpace
) override
1851 if( dynamic_cast<CairoNoAlphaColorSpace
*>(targetColorSpace
.get()) )
1853 // it's us, so simply pass-through the data
1858 // TODO(P3): if we know anything about target
1859 // colorspace, this can be greatly sped up
1860 uno::Sequence
<rendering::ARGBColor
> aIntermediate(
1861 convertIntegerToARGB(deviceColor
));
1862 return targetColorSpace
->convertIntegerFromARGB(aIntermediate
);
1865 virtual uno::Sequence
< rendering::RGBColor
> SAL_CALL
convertIntegerToRGB( const uno::Sequence
< ::sal_Int8
>& deviceColor
) override
1867 const sal_Int8
* pIn( deviceColor
.getConstArray() );
1868 const std::size_t nLen( deviceColor
.getLength() );
1869 ENSURE_ARG_OR_THROW2(nLen
%4==0,
1870 "number of channels no multiple of 4",
1871 static_cast<rendering::XColorSpace
*>(this), 0);
1873 uno::Sequence
< rendering::RGBColor
> aRes(nLen
/4);
1874 rendering::RGBColor
* pOut( aRes
.getArray() );
1875 for( std::size_t i
=0; i
<nLen
; i
+=4 )
1877 *pOut
++ = rendering::RGBColor( pIn
[2], pIn
[1], pIn
[0] );
1883 virtual uno::Sequence
< rendering::ARGBColor
> SAL_CALL
convertIntegerToARGB( const uno::Sequence
< ::sal_Int8
>& deviceColor
) override
1885 return impl_convertIntegerToARGB( deviceColor
);
1887 virtual uno::Sequence
< rendering::ARGBColor
> SAL_CALL
convertIntegerToPARGB( const uno::Sequence
< ::sal_Int8
>& deviceColor
) override
1889 return impl_convertIntegerToARGB( deviceColor
);
1891 uno::Sequence
< rendering::ARGBColor
> impl_convertIntegerToARGB( const uno::Sequence
< ::sal_Int8
>& deviceColor
)
1893 const sal_Int8
* pIn( deviceColor
.getConstArray() );
1894 const std::size_t nLen( deviceColor
.getLength() );
1895 ENSURE_ARG_OR_THROW2(nLen
%4==0,
1896 "number of channels no multiple of 4",
1897 static_cast<rendering::XColorSpace
*>(this), 0);
1899 uno::Sequence
< rendering::ARGBColor
> aRes(nLen
/4);
1900 rendering::ARGBColor
* pOut( aRes
.getArray() );
1901 for( std::size_t i
=0; i
<nLen
; i
+=4 )
1903 *pOut
++ = rendering::ARGBColor(
1905 vcl::unotools::toDoubleColor(pIn
[2]),
1906 vcl::unotools::toDoubleColor(pIn
[1]),
1907 vcl::unotools::toDoubleColor(pIn
[0]));
1913 virtual uno::Sequence
< ::sal_Int8
> SAL_CALL
convertIntegerFromRGB( const uno::Sequence
< rendering::RGBColor
>& rgbColor
) override
1915 const rendering::RGBColor
* pIn( rgbColor
.getConstArray() );
1916 const std::size_t nLen( rgbColor
.getLength() );
1918 uno::Sequence
< sal_Int8
> aRes(nLen
*4);
1919 sal_Int8
* pColors
=aRes
.getArray();
1920 for( std::size_t i
=0; i
<nLen
; ++i
)
1922 *pColors
++ = vcl::unotools::toByteColor(pIn
->Blue
);
1923 *pColors
++ = vcl::unotools::toByteColor(pIn
->Green
);
1924 *pColors
++ = vcl::unotools::toByteColor(pIn
->Red
);
1925 *pColors
++ = -1; // the value does not matter
1931 virtual uno::Sequence
< ::sal_Int8
> SAL_CALL
convertIntegerFromARGB( const uno::Sequence
< rendering::ARGBColor
>& rgbColor
) override
1933 return impl_convertIntegerFromARGB( rgbColor
);
1935 virtual uno::Sequence
< ::sal_Int8
> SAL_CALL
convertIntegerFromPARGB( const uno::Sequence
< rendering::ARGBColor
>& rgbColor
) override
1937 return impl_convertIntegerFromARGB( rgbColor
);
1939 uno::Sequence
< ::sal_Int8
> impl_convertIntegerFromARGB( const uno::Sequence
< rendering::ARGBColor
>& rgbColor
)
1941 const rendering::ARGBColor
* pIn( rgbColor
.getConstArray() );
1942 const std::size_t nLen( rgbColor
.getLength() );
1944 uno::Sequence
< sal_Int8
> aRes(nLen
*4);
1945 sal_Int8
* pColors
=aRes
.getArray();
1946 for( std::size_t i
=0; i
<nLen
; ++i
)
1948 *pColors
++ = vcl::unotools::toByteColor(pIn
->Blue
);
1949 *pColors
++ = vcl::unotools::toByteColor(pIn
->Green
);
1950 *pColors
++ = vcl::unotools::toByteColor(pIn
->Red
);
1951 *pColors
++ = -1; // the value does not matter
1958 CairoNoAlphaColorSpace() :
1962 sal_Int8
* pTags
= maComponentTags
.getArray();
1963 sal_Int32
* pBitCounts
= maBitCounts
.getArray();
1964 pTags
[0] = rendering::ColorComponentTag::RGB_BLUE
;
1965 pTags
[1] = rendering::ColorComponentTag::RGB_GREEN
;
1966 pTags
[2] = rendering::ColorComponentTag::RGB_RED
;
1974 uno::Reference
<rendering::XIntegerBitmapColorSpace
>& GetCairoNoAlphaColorSpace()
1976 static uno::Reference
<rendering::XIntegerBitmapColorSpace
> SPACE
= new CairoNoAlphaColorSpace();
1980 uno::Reference
<rendering::XIntegerBitmapColorSpace
>& GetCairoColorSpace()
1982 static uno::Reference
<rendering::XIntegerBitmapColorSpace
> SPACE
= new CairoColorSpace();
1988 rendering::IntegerBitmapLayout
CanvasHelper::getMemoryLayout()
1991 return rendering::IntegerBitmapLayout(); // we're disposed
1993 const geometry::IntegerSize2D
aSize(getSize());
1995 return impl_getMemoryLayout( aSize
.Width
, aSize
.Height
);
1998 rendering::IntegerBitmapLayout
1999 CanvasHelper::impl_getMemoryLayout( const sal_Int32 nWidth
, const sal_Int32 nHeight
)
2001 rendering::IntegerBitmapLayout aLayout
;
2003 aLayout
.ScanLines
= nHeight
;
2004 aLayout
.ScanLineBytes
= nWidth
*4;
2005 aLayout
.ScanLineStride
= aLayout
.ScanLineBytes
;
2006 aLayout
.PlaneStride
= 0;
2007 aLayout
.ColorSpace
= mbHaveAlpha
? GetCairoColorSpace() : GetCairoNoAlphaColorSpace();
2008 aLayout
.Palette
.clear();
2009 aLayout
.IsMsbFirst
= false;
2015 bool CanvasHelper::repaint( const SurfaceSharedPtr
& pSurface
,
2016 const rendering::ViewState
& viewState
,
2017 const rendering::RenderState
& renderState
)
2019 SAL_INFO( "canvas.cairo", "CanvasHelper::repaint");
2024 cairo_save( mpCairo
.get() );
2026 cairo_rectangle( mpCairo
.get(), 0, 0, maSize
.getWidth(), maSize
.getHeight() );
2027 cairo_clip( mpCairo
.get() );
2029 useStates( viewState
, renderState
, true );
2031 cairo_matrix_t aMatrix
;
2033 cairo_get_matrix( mpCairo
.get(), &aMatrix
);
2034 aMatrix
.xx
= aMatrix
.yy
= 1;
2035 cairo_set_matrix( mpCairo
.get(), &aMatrix
);
2037 cairo_set_source_surface( mpCairo
.get(), pSurface
->getCairoSurface().get(), 0, 0 );
2038 cairo_paint( mpCairo
.get() );
2039 cairo_restore( mpCairo
.get() );
2045 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */