nss: upgrade to release 3.73
[LibreOffice.git] / canvas / source / cairo / cairo_canvashelper.cxx
blob08066c27537ff8bf9cdb476f0d17d40db3c2ff9f
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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>
23 #include <algorithm>
24 #include <tuple>
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>
52 #include <canvas/canvastools.hxx>
53 #include <parametricpolypolygon.hxx>
54 #include <cairo.h>
56 #include "cairo_cachedbitmap.hxx"
57 #include "cairo_canvasbitmap.hxx"
58 #include "cairo_canvashelper.hxx"
60 using namespace ::cairo;
61 using namespace ::com::sun::star;
63 namespace cairocanvas
65 CanvasHelper::CanvasHelper() :
66 mpSurfaceProvider(nullptr),
67 mpDevice(nullptr),
68 mpVirtualDevice(),
69 mbHaveAlpha(),
70 mpCairo(),
71 mpSurface(),
72 maSize()
76 void CanvasHelper::disposing()
78 mpSurface.reset();
79 mpCairo.reset();
80 mpVirtualDevice.disposeAndClear();
81 mpDevice = nullptr;
82 mpSurfaceProvider = nullptr;
85 void CanvasHelper::init( const ::basegfx::B2ISize& rSizePixel,
86 SurfaceProvider& rSurfaceProvider,
87 rendering::XGraphicDevice* pDevice )
89 maSize = rSizePixel;
90 mpSurfaceProvider = &rSurfaceProvider;
91 mpDevice = pDevice;
94 void CanvasHelper::setSize( const ::basegfx::B2ISize& rSize )
96 maSize = rSize;
99 void CanvasHelper::setSurface( const SurfaceSharedPtr& pSurface, bool bHasAlpha )
101 mbHaveAlpha = bHasAlpha;
102 mpVirtualDevice.disposeAndClear();
103 mpSurface = pSurface;
104 mpCairo = pSurface->getCairo();
107 static void setColor( cairo_t* pCairo,
108 const uno::Sequence<double>& rColor )
110 if( rColor.getLength() > 3 )
112 cairo_set_source_rgba( pCairo,
113 rColor[0],
114 rColor[1],
115 rColor[2],
116 rColor[3] );
118 else if( rColor.getLength() == 3 )
119 cairo_set_source_rgb( pCairo,
120 rColor[0],
121 rColor[1],
122 rColor[2] );
125 void CanvasHelper::useStates( const rendering::ViewState& viewState,
126 const rendering::RenderState& renderState,
127 bool bSetColor )
129 cairo_matrix_t aViewMatrix;
130 cairo_matrix_t aRenderMatrix;
131 cairo_matrix_t aCombinedMatrix;
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);
141 if( viewState.Clip.is() )
143 SAL_INFO( "canvas.cairo", "view clip");
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 );
151 aCombinedMatrix.x0 = basegfx::fround( aCombinedMatrix.x0 );
152 aCombinedMatrix.y0 = basegfx::fround( aCombinedMatrix.y0 );
153 cairo_set_matrix( mpCairo.get(), &aCombinedMatrix );
155 if( renderState.Clip.is() )
157 SAL_INFO( "canvas.cairo", "render clip BEGIN");
159 doPolyPolygonPath( renderState.Clip, Clip );
160 SAL_INFO( "canvas.cairo", "render clip END");
163 if( bSetColor )
164 setColor(mpCairo.get(),renderState.DeviceColor);
166 cairo_operator_t compositingMode( CAIRO_OPERATOR_OVER );
167 switch( renderState.CompositeOperation )
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;
212 cairo_set_operator( mpCairo.get(), compositingMode );
215 void CanvasHelper::clear()
217 SAL_INFO( "canvas.cairo", "clear whole area: " << maSize.getX() << " x " << maSize.getY() );
219 if( !mpCairo )
220 return;
222 cairo_save( mpCairo.get() );
224 cairo_identity_matrix( mpCairo.get() );
225 // this does not really differ from all-zero, as cairo
226 // internally converts to premultiplied alpha. but anyway,
227 // this keeps it consistent with the other canvas impls
228 if( mbHaveAlpha )
229 cairo_set_source_rgba( mpCairo.get(), 1.0, 1.0, 1.0, 0.0 );
230 else
231 cairo_set_source_rgb( mpCairo.get(), 1.0, 1.0, 1.0 );
232 cairo_set_operator( mpCairo.get(), CAIRO_OPERATOR_SOURCE );
234 cairo_rectangle( mpCairo.get(), 0, 0, maSize.getX(), maSize.getY() );
235 cairo_fill( mpCairo.get() );
237 cairo_restore( mpCairo.get() );
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 )
246 if( !mpCairo )
247 return;
249 cairo_save( mpCairo.get() );
251 cairo_set_line_width( mpCairo.get(), 1 );
253 useStates( viewState, renderState, true );
255 cairo_move_to( mpCairo.get(), aStartPoint.X + 0.5, aStartPoint.Y + 0.5 );
256 cairo_line_to( mpCairo.get(), aEndPoint.X + 0.5, aEndPoint.Y + 0.5 );
257 cairo_stroke( mpCairo.get() );
259 cairo_restore( mpCairo.get() );
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 )
268 if( !mpCairo )
269 return;
271 cairo_save( mpCairo.get() );
273 cairo_set_line_width( mpCairo.get(), 1 );
275 useStates( viewState, renderState, true );
277 cairo_move_to( mpCairo.get(), aBezierSegment.Px + 0.5, aBezierSegment.Py + 0.5 );
278 // tdf#99165 correction of control points not needed here, only hairlines drawn
279 // (see cairo_set_line_width above)
280 cairo_curve_to( mpCairo.get(),
281 aBezierSegment.C1x + 0.5, aBezierSegment.C1y + 0.5,
282 aBezierSegment.C2x + 0.5, aBezierSegment.C2y + 0.5,
283 aEndPoint.X + 0.5, aEndPoint.Y + 0.5 );
284 cairo_stroke( mpCairo.get() );
286 cairo_restore( mpCairo.get() );
289 #define PARAMETRICPOLYPOLYGON_IMPLEMENTATION_NAME "Canvas::ParametricPolyPolygon"
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
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.
298 * @return created surface or NULL
300 static SurfaceSharedPtr surfaceFromXBitmap( const uno::Reference< rendering::XBitmap >& xBitmap )
302 CanvasBitmap* pBitmapImpl = dynamic_cast< CanvasBitmap* >( xBitmap.get() );
303 if( pBitmapImpl )
304 return pBitmapImpl->getSurface();
306 SurfaceProvider* pSurfaceProvider = dynamic_cast<SurfaceProvider*>( xBitmap.get() );
307 if( pSurfaceProvider )
308 return pSurfaceProvider->getSurface();
310 return SurfaceSharedPtr();
313 static ::BitmapEx bitmapExFromXBitmap( const uno::Reference< rendering::XBitmap >& xBitmap )
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;
322 // TODO(F1): extract pixel from XBitmap interface
323 ENSURE_OR_THROW( false,
324 "bitmapExFromXBitmap(): could not extract BitmapEx" );
326 return ::BitmapEx();
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
335 * This function tries various methods for creating a surface from xBitmap. It also uses
336 * the helper function surfaceFromXBitmap( xBitmap, bHasAlpha )
338 * @return created surface or NULL
340 static SurfaceSharedPtr surfaceFromXBitmap( const uno::Reference< rendering::XBitmap >& xBitmap, const SurfaceProviderRef& rSurfaceProvider, unsigned char*& data, bool& bHasAlpha )
342 bHasAlpha = xBitmap->hasAlpha();
343 SurfaceSharedPtr pSurface = surfaceFromXBitmap( xBitmap );
344 if( pSurface )
345 data = nullptr;
346 else
348 ::BitmapEx aBmpEx = bitmapExFromXBitmap(xBitmap);
349 ::Bitmap aBitmap = aBmpEx.GetBitmap();
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() )
356 pSurface = rSurfaceProvider->createSurface( aBitmap );
357 data = nullptr;
358 bHasAlpha = false;
361 if( !pSurface )
363 tools::Long nWidth;
364 tools::Long nHeight;
365 vcl::bitmap::CanvasCairoExtractBitmapData(aBmpEx, aBitmap, data, bHasAlpha, nWidth, nHeight);
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;
376 SAL_INFO( "canvas.cairo","image: " << nWidth << " x " << nHeight << " alpha: " << bHasAlpha);
380 return pSurface;
383 static void addColorStops( cairo_pattern_t* pPattern, const uno::Sequence< uno::Sequence< double > >& rColors, const uno::Sequence< double >& rStops, bool bReverseStops )
385 int i;
387 OSL_ASSERT( rColors.getLength() == rStops.getLength() );
389 for( i = 0; i < rColors.getLength(); i++ )
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 )
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 );
404 static uno::Sequence<double> lerp(const uno::Sequence<double>& rLeft, const uno::Sequence<double>& rRight, double fAlpha)
406 if( rLeft.getLength() == 3 )
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;
414 else if( rLeft.getLength() == 4 )
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;
424 return uno::Sequence<double>();
427 static cairo_pattern_t* patternFromParametricPolyPolygon( ::canvas::ParametricPolyPolygon const & rPolygon )
429 cairo_pattern_t* pPattern = nullptr;
430 const ::canvas::ParametricPolyPolygon::Values aValues = rPolygon.getValues();
431 double x0, x1, y0, y1, cx, cy, r0, r1;
433 switch( aValues.meType )
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;
444 case ::canvas::ParametricPolyPolygon::GradientType::Elliptical:
445 cx = 0;
446 cy = 0;
447 r0 = 0;
448 r1 = 1;
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;
457 return pPattern;
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 )
466 switch( aOperation )
468 case Fill:
469 /* TODO: multitexturing */
470 if( pTextures )
472 const css::rendering::Texture& aTexture ( (*pTextures)[0] );
473 if( aTexture.Bitmap.is() )
475 unsigned char* data = nullptr;
476 bool bHasAlpha = false;
477 SurfaceSharedPtr pSurface = surfaceFromXBitmap( (*pTextures)[0].Bitmap, pDevice, data, bHasAlpha );
479 if( pSurface )
481 cairo_pattern_t* pPattern;
483 cairo_save( pCairo );
485 css::geometry::AffineMatrix2D aTransform( aTexture.AffineTransform );
486 cairo_matrix_t aScaleMatrix, aTextureMatrix, aScaledTextureMatrix;
488 cairo_matrix_init( &aTextureMatrix,
489 aTransform.m00, aTransform.m10, aTransform.m01,
490 aTransform.m11, aTransform.m02, aTransform.m12);
492 geometry::IntegerSize2D aSize = aTexture.Bitmap->getSize();
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 );
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() );
501 if( aTexture.RepeatModeX == rendering::TexturingMode::REPEAT &&
502 aTexture.RepeatModeY == rendering::TexturingMode::REPEAT )
504 cairo_pattern_set_extend( pPattern, CAIRO_EXTEND_REPEAT );
506 else if ( aTexture.RepeatModeX == rendering::TexturingMode::NONE &&
507 aTexture.RepeatModeY == rendering::TexturingMode::NONE )
509 cairo_pattern_set_extend( pPattern, CAIRO_EXTEND_NONE );
511 else if ( aTexture.RepeatModeX == rendering::TexturingMode::CLAMP &&
512 aTexture.RepeatModeY == rendering::TexturingMode::CLAMP )
514 cairo_pattern_set_extend( pPattern, CAIRO_EXTEND_PAD );
517 aScaledTextureMatrix.x0 = basegfx::fround( aScaledTextureMatrix.x0 );
518 aScaledTextureMatrix.y0 = basegfx::fround( aScaledTextureMatrix.y0 );
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);
525 cairo_pattern_set_matrix( pPattern, &aScaledTextureMatrix );
527 cairo_set_source( pCairo, pPattern );
528 if( !bHasAlpha )
529 cairo_set_operator( pCairo, CAIRO_OPERATOR_SOURCE );
530 cairo_fill( pCairo );
532 cairo_restore( pCairo );
534 cairo_pattern_destroy( pPattern );
537 if( data )
538 free( data );
540 else if( aTexture.Gradient.is() )
542 uno::Reference< lang::XServiceInfo > xRef( aTexture.Gradient, uno::UNO_QUERY );
544 SAL_INFO( "canvas.cairo", "gradient fill" );
545 if( xRef.is() && xRef->getImplementationName() == PARAMETRICPOLYPOLYGON_IMPLEMENTATION_NAME )
547 // TODO(Q1): Maybe use dynamic_cast here
549 // TODO(E1): Return value
550 // TODO(F1): FillRule
551 SAL_INFO( "canvas.cairo", "known implementation" );
553 ::canvas::ParametricPolyPolygon* pPolyImpl = static_cast< ::canvas::ParametricPolyPolygon* >( aTexture.Gradient.get() );
554 css::geometry::AffineMatrix2D aTransform( aTexture.AffineTransform );
555 cairo_matrix_t aTextureMatrix;
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 )
562 // no general path gradient yet in cairo; emulate then
563 cairo_save( pCairo );
564 cairo_clip( pCairo );
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);
572 cairo_transform( pCairo, &aTextureMatrix );
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 ) );
579 // typical number for pixel of the same color (strip size)
580 const unsigned int nStripSize( nGradientSize < 50 ? 2 : 4 );
582 // use at least three steps, and at utmost the number of color
583 // steps
584 const unsigned int nStepCount(
585 std::max(
587 std::min(
588 nGradientSize / nStripSize,
589 128U )) + 1 );
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 )
595 const double fT( i/double(nStepCount) );
597 std::ptrdiff_t nIndex;
598 double fAlpha;
599 std::tie(nIndex,fAlpha)=aLerper.lerp(fT);
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);
606 cairo_restore( pCairo );
608 else
610 cairo_pattern_t* pPattern = patternFromParametricPolyPolygon( *pPolyImpl );
612 if( pPattern )
614 SAL_INFO( "canvas.cairo", "filling with pattern" );
616 cairo_save( pCairo );
618 cairo_transform( pCairo, &aTextureMatrix );
619 cairo_set_source( pCairo, pPattern );
620 cairo_fill( pCairo );
621 cairo_restore( pCairo );
623 cairo_pattern_destroy( pPattern );
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;
644 static void clipNULL( cairo_t *pCairo )
646 SAL_INFO( "canvas.cairo", "clipNULL");
647 cairo_matrix_t aOrigMatrix, aIdentityMatrix;
649 /* we set identity matrix here to overcome bug in cairo 0.9.2
650 where XCreatePixmap is called with zero width and height.
652 it also reaches faster path in cairo clipping code.
654 cairo_matrix_init_identity( &aIdentityMatrix );
655 cairo_get_matrix( pCairo, &aOrigMatrix );
656 cairo_set_matrix( pCairo, &aIdentityMatrix );
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 );
664 /* restore the original matrix */
665 cairo_set_matrix( pCairo, &aOrigMatrix );
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 )
675 if( pTextures )
676 ENSURE_ARG_OR_THROW( pTextures->hasElements(),
677 "CanvasHelper::fillTexturedPolyPolygon: empty texture sequence");
679 bool bOpToDo = false;
680 cairo_matrix_t aOrigMatrix, aIdentityMatrix;
681 double nX, nY, nBX, nBY, nAX, nAY, nLastX(0.0), nLastY(0.0);
683 cairo_get_matrix( pCairo, &aOrigMatrix );
684 cairo_matrix_init_identity( &aIdentityMatrix );
685 cairo_set_matrix( pCairo, &aIdentityMatrix );
687 cairo_set_fill_rule( pCairo,
688 eFillrule == rendering::FillRule_EVEN_ODD ?
689 CAIRO_FILL_RULE_EVEN_ODD : CAIRO_FILL_RULE_WINDING );
691 for( sal_uInt32 nPolygonIndex = 0; nPolygonIndex < aPolyPolygon.count(); nPolygonIndex++ )
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()) );
700 if( nPointCount > 1)
702 bool bIsBezier = aPolygon.areControlPointsUsed();
703 ::basegfx::B2DPoint aA, aB, aP;
705 for( sal_uInt32 j=0; j < nExtendedPointCount; j++ )
707 aP = aPolygon.getB2DPoint( j % nPointCount );
709 nX = aP.getX();
710 nY = aP.getY();
711 cairo_matrix_transform_point( &aOrigMatrix, &nX, &nY );
713 if (!bIsBezier && aOperation == Clip)
715 nX = basegfx::fround( nX );
716 nY = basegfx::fround( nY );
719 if( aOperation == Stroke )
721 nX += 0.5;
722 nY += 0.5;
725 if( j==0 )
727 cairo_move_to( pCairo, nX, nY );
728 SAL_INFO( "canvas.cairo", "move to " << nX << "," << nY );
730 else
732 if( bIsBezier )
734 aA = aPolygon.getNextControlPoint( (j-1) % nPointCount );
735 aB = aPolygon.getPrevControlPoint( j % nPointCount );
737 nAX = aA.getX();
738 nAY = aA.getY();
739 nBX = aB.getX();
740 nBY = aB.getY();
742 cairo_matrix_transform_point( &aOrigMatrix, &nAX, &nAY );
743 cairo_matrix_transform_point( &aOrigMatrix, &nBX, &nBY );
745 if( aOperation == Stroke )
747 nAX += 0.5;
748 nAY += 0.5;
749 nBX += 0.5;
750 nBY += 0.5;
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))
759 nAX = nLastX + ((nBX - nLastX) * 0.0005);
760 nAY = nLastY + ((nBY - nLastY) * 0.0005);
763 if(basegfx::fTools::equal(nBX, nX) && basegfx::fTools::equal(nBY, nY))
765 nBX = nX + ((nAX - nX) * 0.0005);
766 nBY = nY + ((nAY - nY) * 0.0005);
769 cairo_curve_to( pCairo, nAX, nAY, nBX, nBY, nX, nY );
771 else
773 cairo_line_to( pCairo, nX, nY );
774 SAL_INFO( "canvas.cairo", "line to " << nX << "," << nY );
776 bOpToDo = true;
779 nLastX = nX;
780 nLastY = nY;
783 if( aPolygon.isClosed() )
784 cairo_close_path( pCairo );
787 else
789 SAL_INFO( "canvas.cairo", "empty polygon for op: " << aOperation );
790 if( aOperation == Clip )
792 clipNULL( pCairo );
794 return;
799 if( aOperation == Fill && pTextures )
801 cairo_set_matrix( pCairo, &aOrigMatrix );
802 doOperation( aOperation, pCairo, pTextures, pDevice, aPolyPolygon.getB2DRange() );
803 cairo_set_matrix( pCairo, &aIdentityMatrix );
806 if( bOpToDo && ( aOperation != Fill || !pTextures ) )
807 doOperation( aOperation, pCairo, pTextures, pDevice, aPolyPolygon.getB2DRange() );
809 cairo_set_matrix( pCairo, &aOrigMatrix );
811 if( aPolyPolygon.count() == 0 && aOperation == Clip )
812 clipNULL( pCairo );
815 void CanvasHelper::doPolyPolygonPath( const uno::Reference< rendering::XPolyPolygon2D >& xPolyPolygon,
816 Operation aOperation,
817 bool bNoLineJoin,
818 const uno::Sequence< rendering::Texture >* pTextures ) const
820 const ::basegfx::B2DPolyPolygon& rPolyPoly(
821 ::basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D(xPolyPolygon) );
823 cairo_t* pCairo = mpCairo.get();
825 if(bNoLineJoin && aOperation == Stroke)
827 // emulate rendering::PathJoinType::NONE by painting single edges
828 for(sal_uInt32 a(0); a < rPolyPoly.count(); a++)
830 const basegfx::B2DPolygon& aCandidate(rPolyPoly.getB2DPolygon(a));
831 const sal_uInt32 nPointCount(aCandidate.count());
833 if(nPointCount)
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));
840 for(sal_uInt32 b(0); b < nEdgeCount; b++)
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));
847 doPolyPolygonImplementation( basegfx::B2DPolyPolygon(aEdge),
848 aOperation,
849 pCairo, pTextures,
850 mpSurfaceProvider,
851 xPolyPolygon->getFillRule() );
853 // prepare next step
854 aEdge.setB2DPoint(0, aEdge.getB2DPoint(1));
859 else
861 doPolyPolygonImplementation( rPolyPoly, aOperation,
862 pCairo, pTextures,
863 mpSurfaceProvider,
864 xPolyPolygon->getFillRule() );
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 )
873 #ifdef CAIRO_CANVAS_PERF_TRACE
874 struct timespec aTimer;
875 mxDevice->startPerfTrace( &aTimer );
876 #endif
878 if( mpCairo )
880 cairo_save( mpCairo.get() );
882 cairo_set_line_width( mpCairo.get(), 1 );
884 useStates( viewState, renderState, true );
885 doPolyPolygonPath( xPolyPolygon, Stroke );
887 cairo_restore( mpCairo.get() );
889 else
890 SAL_INFO( "canvas.cairo", "CanvasHelper called after it was disposed");
892 #ifdef CAIRO_CANVAS_PERF_TRACE
893 mxDevice->stopPerfTrace( &aTimer, "drawPolyPolygon" );
894 #endif
896 return uno::Reference< rendering::XCachedPrimitive >(nullptr);
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 )
905 #ifdef CAIRO_CANVAS_PERF_TRACE
906 struct timespec aTimer;
907 mxDevice->startPerfTrace( &aTimer );
908 #endif
910 if( mpCairo )
912 cairo_save( mpCairo.get() );
914 useStates( viewState, renderState, true );
916 cairo_matrix_t aMatrix;
917 cairo_get_matrix( mpCairo.get(), &aMatrix );
918 double scaleFactorX = 1;
919 double scaleFactorY = 0;
920 cairo_matrix_transform_distance( &aMatrix, &scaleFactorX, &scaleFactorY );
921 double scaleFactor = basegfx::B2DVector( scaleFactorX, scaleFactorY ).getLength();
922 cairo_set_line_width( mpCairo.get(), strokeAttributes.StrokeWidth * scaleFactor );
924 cairo_set_miter_limit( mpCairo.get(), strokeAttributes.MiterLimit );
926 // FIXME: cairo doesn't handle end cap so far (rodo)
927 switch( strokeAttributes.StartCapType )
929 case rendering::PathCapType::BUTT:
930 cairo_set_line_cap( mpCairo.get(), CAIRO_LINE_CAP_BUTT );
931 break;
932 case rendering::PathCapType::ROUND:
933 cairo_set_line_cap( mpCairo.get(), CAIRO_LINE_CAP_ROUND );
934 break;
935 case rendering::PathCapType::SQUARE:
936 cairo_set_line_cap( mpCairo.get(), CAIRO_LINE_CAP_SQUARE );
937 break;
940 bool bNoLineJoin(false);
942 switch( strokeAttributes.JoinType )
944 case rendering::PathJoinType::NONE:
945 bNoLineJoin = true;
946 [[fallthrough]]; // cairo doesn't have join type NONE so we use MITER as it's pretty close
947 case rendering::PathJoinType::MITER:
948 cairo_set_line_join( mpCairo.get(), CAIRO_LINE_JOIN_MITER );
949 break;
950 case rendering::PathJoinType::ROUND:
951 cairo_set_line_join( mpCairo.get(), CAIRO_LINE_JOIN_ROUND );
952 break;
953 case rendering::PathJoinType::BEVEL:
954 cairo_set_line_join( mpCairo.get(), CAIRO_LINE_JOIN_BEVEL );
955 break;
958 //tdf#103026 If the scaling is 0, then all dashes become zero so
959 //cairo will set the cairo_t status to CAIRO_STATUS_INVALID_DASH
960 //and no further drawing will occur
961 if (strokeAttributes.DashArray.hasElements() && scaleFactor > 0.0)
963 auto aDashArray(comphelper::sequenceToContainer<std::vector<double>>(strokeAttributes.DashArray));
964 for (auto& rDash : aDashArray)
965 rDash *= scaleFactor;
966 cairo_set_dash(mpCairo.get(), aDashArray.data(), aDashArray.size(), 0);
969 // TODO(rodo) use LineArray of strokeAttributes
971 doPolyPolygonPath( xPolyPolygon, Stroke, bNoLineJoin );
973 cairo_restore( mpCairo.get() );
975 else
976 SAL_INFO( "canvas.cairo", "CanvasHelper called after it was disposed");
978 #ifdef CAIRO_CANVAS_PERF_TRACE
979 mxDevice->stopPerfTrace( &aTimer, "strokePolyPolygon" );
980 #endif
982 // TODO(P1): Provide caching here.
983 return uno::Reference< rendering::XCachedPrimitive >(nullptr);
986 uno::Reference< rendering::XCachedPrimitive > CanvasHelper::strokeTexturedPolyPolygon( const rendering::XCanvas* ,
987 const uno::Reference< rendering::XPolyPolygon2D >& /*xPolyPolygon*/,
988 const rendering::ViewState& /*viewState*/,
989 const rendering::RenderState& /*renderState*/,
990 const uno::Sequence< rendering::Texture >& /*textures*/,
991 const rendering::StrokeAttributes& /*strokeAttributes*/ )
993 // TODO
994 return uno::Reference< rendering::XCachedPrimitive >(nullptr);
997 uno::Reference< rendering::XCachedPrimitive > CanvasHelper::strokeTextureMappedPolyPolygon( const rendering::XCanvas* ,
998 const uno::Reference< rendering::XPolyPolygon2D >& /*xPolyPolygon*/,
999 const rendering::ViewState& /*viewState*/,
1000 const rendering::RenderState& /*renderState*/,
1001 const uno::Sequence< rendering::Texture >& /*textures*/,
1002 const uno::Reference< geometry::XMapping2D >& /*xMapping*/,
1003 const rendering::StrokeAttributes& /*strokeAttributes*/ )
1005 // TODO
1006 return uno::Reference< rendering::XCachedPrimitive >(nullptr);
1009 uno::Reference< rendering::XPolyPolygon2D > CanvasHelper::queryStrokeShapes( const rendering::XCanvas* ,
1010 const uno::Reference< rendering::XPolyPolygon2D >& /*xPolyPolygon*/,
1011 const rendering::ViewState& /*viewState*/,
1012 const rendering::RenderState& /*renderState*/,
1013 const rendering::StrokeAttributes& /*strokeAttributes*/ )
1015 // TODO
1016 return uno::Reference< rendering::XPolyPolygon2D >(nullptr);
1019 uno::Reference< rendering::XCachedPrimitive > CanvasHelper::fillPolyPolygon( const rendering::XCanvas* ,
1020 const uno::Reference< rendering::XPolyPolygon2D >& xPolyPolygon,
1021 const rendering::ViewState& viewState,
1022 const rendering::RenderState& renderState )
1024 #ifdef CAIRO_CANVAS_PERF_TRACE
1025 struct timespec aTimer;
1026 mxDevice->startPerfTrace( &aTimer );
1027 #endif
1029 if( mpCairo )
1031 cairo_save( mpCairo.get() );
1033 useStates( viewState, renderState, true );
1034 doPolyPolygonPath( xPolyPolygon, Fill );
1036 cairo_restore( mpCairo.get() );
1038 else
1039 SAL_INFO( "canvas.cairo", "CanvasHelper called after it was disposed");
1041 #ifdef CAIRO_CANVAS_PERF_TRACE
1042 mxDevice->stopPerfTrace( &aTimer, "fillPolyPolygon" );
1043 #endif
1045 return uno::Reference< rendering::XCachedPrimitive >(nullptr);
1048 uno::Reference< rendering::XCachedPrimitive > CanvasHelper::fillTexturedPolyPolygon( const rendering::XCanvas* ,
1049 const uno::Reference< rendering::XPolyPolygon2D >& xPolyPolygon,
1050 const rendering::ViewState& viewState,
1051 const rendering::RenderState& renderState,
1052 const uno::Sequence< rendering::Texture >& textures )
1054 if( mpCairo )
1056 cairo_save( mpCairo.get() );
1058 useStates( viewState, renderState, true );
1059 doPolyPolygonPath( xPolyPolygon, Fill, false, &textures );
1061 cairo_restore( mpCairo.get() );
1064 return uno::Reference< rendering::XCachedPrimitive >(nullptr);
1067 uno::Reference< rendering::XCachedPrimitive > CanvasHelper::fillTextureMappedPolyPolygon( const rendering::XCanvas* ,
1068 const uno::Reference< rendering::XPolyPolygon2D >& /*xPolyPolygon*/,
1069 const rendering::ViewState& /*viewState*/,
1070 const rendering::RenderState& /*renderState*/,
1071 const uno::Sequence< rendering::Texture >& /*textures*/,
1072 const uno::Reference< geometry::XMapping2D >& /*xMapping*/ )
1074 // TODO
1075 return uno::Reference< rendering::XCachedPrimitive >(nullptr);
1078 uno::Reference< rendering::XCachedPrimitive > CanvasHelper::implDrawBitmapSurface( const rendering::XCanvas* pCanvas,
1079 const SurfaceSharedPtr& pInputSurface,
1080 const rendering::ViewState& viewState,
1081 const rendering::RenderState& renderState,
1082 const geometry::IntegerSize2D& rSize,
1083 bool bModulateColors,
1084 bool bHasAlpha )
1086 SurfaceSharedPtr pSurface=pInputSurface;
1087 uno::Reference< rendering::XCachedPrimitive > rv;
1088 geometry::IntegerSize2D aBitmapSize = rSize;
1090 if( mpCairo )
1092 cairo_save( mpCairo.get() );
1094 cairo_rectangle( mpCairo.get(), 0, 0, maSize.getX(), maSize.getY() );
1095 cairo_clip( mpCairo.get() );
1097 useStates( viewState, renderState, true );
1099 cairo_matrix_t aMatrix;
1101 cairo_get_matrix( mpCairo.get(), &aMatrix );
1102 if( ! ::rtl::math::approxEqual( aMatrix.xx, 1 ) &&
1103 ! ::rtl::math::approxEqual( aMatrix.yy, 1 ) &&
1104 ::rtl::math::approxEqual( aMatrix.x0, 0 ) &&
1105 ::rtl::math::approxEqual( aMatrix.y0, 0 ) &&
1106 basegfx::fround( rSize.Width * aMatrix.xx ) > 8 &&
1107 basegfx::fround( rSize.Height* aMatrix.yy ) > 8 )
1109 double dWidth, dHeight;
1111 dWidth = basegfx::fround( rSize.Width * aMatrix.xx );
1112 dHeight = basegfx::fround( rSize.Height* aMatrix.yy );
1113 aBitmapSize.Width = static_cast<sal_Int32>( dWidth );
1114 aBitmapSize.Height = static_cast<sal_Int32>( dHeight );
1116 SurfaceSharedPtr pScaledSurface = mpSurfaceProvider->createSurface(
1117 ::basegfx::B2ISize( aBitmapSize.Width, aBitmapSize.Height ),
1118 bHasAlpha ? CAIRO_CONTENT_COLOR_ALPHA : CAIRO_CONTENT_COLOR );
1119 CairoSharedPtr pCairo = pScaledSurface->getCairo();
1121 cairo_set_operator( pCairo.get(), CAIRO_OPERATOR_SOURCE );
1122 // add 0.5px to size to avoid rounding errors in cairo, leading sometimes to random data on the image right/bottom borders
1123 cairo_scale( pCairo.get(), (dWidth+0.5)/rSize.Width, (dHeight+0.5)/rSize.Height );
1124 cairo_set_source_surface( pCairo.get(), pSurface->getCairoSurface().get(), 0, 0 );
1125 cairo_paint( pCairo.get() );
1127 pSurface = pScaledSurface;
1129 aMatrix.xx = aMatrix.yy = 1;
1130 cairo_set_matrix( mpCairo.get(), &aMatrix );
1132 rv.set(
1133 new CachedBitmap( pSurface, viewState, renderState,
1134 // cast away const, need to
1135 // change refcount (as this is
1136 // ~invisible to client code,
1137 // still logically const)
1138 const_cast< rendering::XCanvas* >(pCanvas)) );
1141 if( !bHasAlpha && mbHaveAlpha )
1143 double x, y, width, height;
1145 x = y = 0;
1146 width = aBitmapSize.Width;
1147 height = aBitmapSize.Height;
1148 cairo_matrix_transform_point( &aMatrix, &x, &y );
1149 cairo_matrix_transform_distance( &aMatrix, &width, &height );
1151 // in case the bitmap doesn't have alpha and covers whole area
1152 // we try to change surface to plain rgb
1153 SAL_INFO( "canvas.cairo","chance to change surface to rgb, " << x << ", " << y << ", " << width << " x " << height << " (" << maSize.getX() << " x " << maSize.getY() << ")" );
1154 if( x <= 0 && y <= 0 && x + width >= maSize.getX() && y + height >= maSize.getY() )
1156 SAL_INFO( "canvas.cairo","trying to change surface to rgb");
1157 if( mpSurfaceProvider ) {
1158 SurfaceSharedPtr pNewSurface = mpSurfaceProvider->changeSurface();
1160 if( pNewSurface )
1161 setSurface( pNewSurface, false );
1163 // set state to new mpCairo.get()
1164 useStates( viewState, renderState, true );
1165 // use the possibly modified matrix
1166 cairo_set_matrix( mpCairo.get(), &aMatrix );
1171 cairo_set_source_surface( mpCairo.get(), pSurface->getCairoSurface().get(), 0, 0 );
1172 if( !bHasAlpha &&
1173 ::rtl::math::approxEqual( aMatrix.xx, 1 ) &&
1174 ::rtl::math::approxEqual( aMatrix.yy, 1 ) &&
1175 ::rtl::math::approxEqual( aMatrix.x0, 0 ) &&
1176 ::rtl::math::approxEqual( aMatrix.y0, 0 ) )
1177 cairo_set_operator( mpCairo.get(), CAIRO_OPERATOR_SOURCE );
1178 cairo_pattern_set_extend( cairo_get_source(mpCairo.get()), CAIRO_EXTEND_PAD );
1179 cairo_rectangle( mpCairo.get(), 0, 0, aBitmapSize.Width, aBitmapSize.Height );
1180 cairo_clip( mpCairo.get() );
1182 int nPixelWidth = std::round(rSize.Width * aMatrix.xx);
1183 int nPixelHeight = std::round(rSize.Height * aMatrix.yy);
1184 if (std::abs(nPixelWidth) > 0 && std::abs(nPixelHeight) > 0)
1186 // Only render the image if it's at least 1x1 px sized.
1187 if (bModulateColors)
1188 cairo_paint_with_alpha(mpCairo.get(), renderState.DeviceColor[3]);
1189 else
1191 cairo_paint(mpCairo.get());
1192 if (cairo_status(mpCairo.get()) != CAIRO_STATUS_SUCCESS)
1194 SAL_WARN("canvas.cairo", "cairo_paint() failed: " << cairo_status_to_string(
1195 cairo_status(mpCairo.get())));
1199 cairo_restore( mpCairo.get() );
1201 else
1202 SAL_INFO( "canvas.cairo", "CanvasHelper called after it was disposed");
1204 return rv; // uno::Reference< rendering::XCachedPrimitive >(NULL);
1207 uno::Reference< rendering::XCachedPrimitive > CanvasHelper::drawBitmap( const rendering::XCanvas* pCanvas,
1208 const uno::Reference< rendering::XBitmap >& xBitmap,
1209 const rendering::ViewState& viewState,
1210 const rendering::RenderState& renderState )
1212 #ifdef CAIRO_CANVAS_PERF_TRACE
1213 struct timespec aTimer;
1214 mxDevice->startPerfTrace( &aTimer );
1215 #endif
1217 uno::Reference< rendering::XCachedPrimitive > rv;
1218 unsigned char* data = nullptr;
1219 bool bHasAlpha = false;
1220 SurfaceSharedPtr pSurface = surfaceFromXBitmap( xBitmap, mpSurfaceProvider, data, bHasAlpha );
1221 geometry::IntegerSize2D aSize = xBitmap->getSize();
1223 if( pSurface )
1225 rv = implDrawBitmapSurface( pCanvas, pSurface, viewState, renderState, aSize, false, bHasAlpha );
1227 if( data )
1228 free( data );
1230 else
1231 rv.set(nullptr);
1233 #ifdef CAIRO_CANVAS_PERF_TRACE
1234 mxDevice->stopPerfTrace( &aTimer, "drawBitmap" );
1235 #endif
1237 return rv;
1240 uno::Reference< rendering::XCachedPrimitive > CanvasHelper::drawBitmapModulated( const rendering::XCanvas* pCanvas,
1241 const uno::Reference< rendering::XBitmap >& xBitmap,
1242 const rendering::ViewState& viewState,
1243 const rendering::RenderState& renderState )
1245 #ifdef CAIRO_CANVAS_PERF_TRACE
1246 struct timespec aTimer;
1247 mxDevice->startPerfTrace( &aTimer );
1248 #endif
1250 uno::Reference< rendering::XCachedPrimitive > rv;
1251 unsigned char* data = nullptr;
1252 bool bHasAlpha = false;
1253 SurfaceSharedPtr pSurface = surfaceFromXBitmap( xBitmap, mpSurfaceProvider, data, bHasAlpha );
1254 geometry::IntegerSize2D aSize = xBitmap->getSize();
1256 if( pSurface )
1258 rv = implDrawBitmapSurface( pCanvas, pSurface, viewState, renderState, aSize, true, bHasAlpha );
1260 if( data )
1261 free( data );
1263 else
1264 rv.set(nullptr);
1266 #ifdef CAIRO_CANVAS_PERF_TRACE
1267 mxDevice->stopPerfTrace( &aTimer, "drawBitmap" );
1268 #endif
1270 return rv;
1274 geometry::IntegerSize2D CanvasHelper::getSize() const
1276 if( !mpSurfaceProvider )
1277 return geometry::IntegerSize2D(1, 1); // we're disposed
1279 return ::basegfx::unotools::integerSize2DFromB2ISize( maSize );
1282 uno::Reference< rendering::XBitmap > CanvasHelper::getScaledBitmap( const geometry::RealSize2D& newSize,
1283 bool /*beFast*/ )
1285 #ifdef CAIRO_CANVAS_PERF_TRACE
1286 struct timespec aTimer;
1287 mxDevice->startPerfTrace( &aTimer );
1288 #endif
1290 if( mpCairo )
1292 return uno::Reference< rendering::XBitmap >( new CanvasBitmap( ::basegfx::B2ISize( ::canvas::tools::roundUp( newSize.Width ),
1293 ::canvas::tools::roundUp( newSize.Height ) ),
1294 mpSurfaceProvider, mpDevice, false ) );
1296 else
1297 SAL_INFO( "canvas.cairo", "CanvasHelper called after it was disposed");
1299 #ifdef CAIRO_CANVAS_PERF_TRACE
1300 mxDevice->stopPerfTrace( &aTimer, "getScaledBitmap" );
1301 #endif
1303 return uno::Reference< rendering::XBitmap >();
1306 uno::Sequence< sal_Int8 > CanvasHelper::getData( rendering::IntegerBitmapLayout& aLayout,
1307 const geometry::IntegerRectangle2D& rect )
1309 if( mpCairo )
1311 const sal_Int32 nWidth( rect.X2 - rect.X1 );
1312 const sal_Int32 nHeight( rect.Y2 - rect.Y1 );
1313 const cairo_format_t eFormat( mbHaveAlpha ? CAIRO_FORMAT_ARGB32 : CAIRO_FORMAT_RGB24 );
1314 uno::Sequence< sal_Int8 > aRes( 4*nWidth*nHeight );
1315 sal_Int8* pData = aRes.getArray();
1316 cairo_surface_t* pImageSurface = cairo_image_surface_create_for_data( reinterpret_cast<unsigned char *>(pData),
1317 eFormat,
1318 nWidth, nHeight, 4*nWidth );
1319 cairo_t* pCairo = cairo_create( pImageSurface );
1320 cairo_set_source_surface( pCairo, mpSurface->getCairoSurface().get(), -rect.X1, -rect.Y1);
1321 cairo_paint( pCairo );
1322 cairo_destroy( pCairo );
1323 cairo_surface_destroy( pImageSurface );
1325 aLayout = impl_getMemoryLayout( nWidth, nHeight );
1327 return aRes;
1330 return uno::Sequence< sal_Int8 >();
1333 uno::Sequence< sal_Int8 > CanvasHelper::getPixel( rendering::IntegerBitmapLayout& /*bitmapLayout*/,
1334 const geometry::IntegerPoint2D& /*pos*/ )
1336 return uno::Sequence< sal_Int8 >();
1339 namespace
1341 class CairoColorSpace : public cppu::WeakImplHelper< css::rendering::XIntegerBitmapColorSpace >
1343 private:
1344 uno::Sequence< sal_Int8 > maComponentTags;
1345 uno::Sequence< sal_Int32 > maBitCounts;
1347 virtual ::sal_Int8 SAL_CALL getType( ) override
1349 return rendering::ColorSpaceType::RGB;
1351 virtual uno::Sequence< ::sal_Int8 > SAL_CALL getComponentTags( ) override
1353 return maComponentTags;
1355 virtual ::sal_Int8 SAL_CALL getRenderingIntent( ) override
1357 return rendering::RenderingIntent::PERCEPTUAL;
1359 virtual uno::Sequence< beans::PropertyValue > SAL_CALL getProperties( ) override
1361 return uno::Sequence< beans::PropertyValue >();
1363 virtual uno::Sequence< double > SAL_CALL convertColorSpace( const uno::Sequence< double >& deviceColor,
1364 const uno::Reference< rendering::XColorSpace >& targetColorSpace ) override
1366 // TODO(P3): if we know anything about target
1367 // colorspace, this can be greatly sped up
1368 uno::Sequence<rendering::ARGBColor> aIntermediate(
1369 convertToARGB(deviceColor));
1370 return targetColorSpace->convertFromARGB(aIntermediate);
1372 virtual uno::Sequence< rendering::RGBColor > SAL_CALL convertToRGB( const uno::Sequence< double >& deviceColor ) override
1374 const double* pIn( deviceColor.getConstArray() );
1375 const std::size_t nLen( deviceColor.getLength() );
1376 ENSURE_ARG_OR_THROW2(nLen%4==0,
1377 "number of channels no multiple of 4",
1378 static_cast<rendering::XColorSpace*>(this), 0);
1380 uno::Sequence< rendering::RGBColor > aRes(nLen/4);
1381 rendering::RGBColor* pOut( aRes.getArray() );
1382 for( std::size_t i=0; i<nLen; i+=4 )
1384 const double fAlpha(pIn[3]);
1385 if( fAlpha == 0.0 )
1386 *pOut++ = rendering::RGBColor(0.0, 0.0, 0.0);
1387 else
1388 *pOut++ = rendering::RGBColor(pIn[2]/fAlpha,pIn[1]/fAlpha,pIn[0]/fAlpha);
1389 pIn += 4;
1391 return aRes;
1393 virtual uno::Sequence< rendering::ARGBColor > SAL_CALL convertToARGB( const uno::Sequence< double >& deviceColor ) override
1395 const double* pIn( deviceColor.getConstArray() );
1396 const std::size_t nLen( deviceColor.getLength() );
1397 ENSURE_ARG_OR_THROW2(nLen%4==0,
1398 "number of channels no multiple of 4",
1399 static_cast<rendering::XColorSpace*>(this), 0);
1401 uno::Sequence< rendering::ARGBColor > aRes(nLen/4);
1402 rendering::ARGBColor* pOut( aRes.getArray() );
1403 for( std::size_t i=0; i<nLen; i+=4 )
1405 const double fAlpha(pIn[3]);
1406 if( fAlpha == 0.0 )
1407 *pOut++ = rendering::ARGBColor(0.0, 0.0, 0.0, 0.0);
1408 else
1409 *pOut++ = rendering::ARGBColor(fAlpha,pIn[2]/fAlpha,pIn[1]/fAlpha,pIn[0]/fAlpha);
1410 pIn += 4;
1412 return aRes;
1414 virtual uno::Sequence< rendering::ARGBColor > SAL_CALL convertToPARGB( const uno::Sequence< double >& deviceColor ) override
1416 const double* pIn( deviceColor.getConstArray() );
1417 const std::size_t nLen( deviceColor.getLength() );
1418 ENSURE_ARG_OR_THROW2(nLen%4==0,
1419 "number of channels no multiple of 4",
1420 static_cast<rendering::XColorSpace*>(this), 0);
1422 uno::Sequence< rendering::ARGBColor > aRes(nLen/4);
1423 rendering::ARGBColor* pOut( aRes.getArray() );
1424 for( std::size_t i=0; i<nLen; i+=4 )
1426 *pOut++ = rendering::ARGBColor(pIn[3],pIn[2],pIn[1],pIn[1]);
1427 pIn += 4;
1429 return aRes;
1431 virtual uno::Sequence< double > SAL_CALL convertFromRGB( const uno::Sequence< rendering::RGBColor >& rgbColor ) override
1433 const rendering::RGBColor* pIn( rgbColor.getConstArray() );
1434 const std::size_t nLen( rgbColor.getLength() );
1436 uno::Sequence< double > aRes(nLen*4);
1437 double* pColors=aRes.getArray();
1438 for( std::size_t i=0; i<nLen; ++i )
1440 *pColors++ = pIn->Blue;
1441 *pColors++ = pIn->Green;
1442 *pColors++ = pIn->Red;
1443 *pColors++ = 1.0;
1444 ++pIn;
1446 return aRes;
1448 virtual uno::Sequence< double > SAL_CALL convertFromARGB( const uno::Sequence< rendering::ARGBColor >& rgbColor ) override
1450 const rendering::ARGBColor* pIn( rgbColor.getConstArray() );
1451 const std::size_t nLen( rgbColor.getLength() );
1453 uno::Sequence< double > aRes(nLen*4);
1454 double* pColors=aRes.getArray();
1455 for( std::size_t i=0; i<nLen; ++i )
1457 *pColors++ = pIn->Alpha*pIn->Blue;
1458 *pColors++ = pIn->Alpha*pIn->Green;
1459 *pColors++ = pIn->Alpha*pIn->Red;
1460 *pColors++ = pIn->Alpha;
1461 ++pIn;
1463 return aRes;
1465 virtual uno::Sequence< double > SAL_CALL convertFromPARGB( const uno::Sequence< rendering::ARGBColor >& rgbColor ) override
1467 const rendering::ARGBColor* pIn( rgbColor.getConstArray() );
1468 const std::size_t nLen( rgbColor.getLength() );
1470 uno::Sequence< double > aRes(nLen*4);
1471 double* pColors=aRes.getArray();
1472 for( std::size_t i=0; i<nLen; ++i )
1474 *pColors++ = pIn->Blue;
1475 *pColors++ = pIn->Green;
1476 *pColors++ = pIn->Red;
1477 *pColors++ = pIn->Alpha;
1478 ++pIn;
1480 return aRes;
1483 // XIntegerBitmapColorSpace
1484 virtual ::sal_Int32 SAL_CALL getBitsPerPixel( ) override
1486 return 32;
1488 virtual uno::Sequence< ::sal_Int32 > SAL_CALL getComponentBitCounts( ) override
1490 return maBitCounts;
1492 virtual ::sal_Int8 SAL_CALL getEndianness( ) override
1494 return util::Endianness::LITTLE;
1496 virtual uno::Sequence<double> SAL_CALL convertFromIntegerColorSpace( const uno::Sequence< ::sal_Int8 >& deviceColor,
1497 const uno::Reference< rendering::XColorSpace >& targetColorSpace ) override
1499 if( dynamic_cast<CairoColorSpace*>(targetColorSpace.get()) )
1501 const sal_Int8* pIn( deviceColor.getConstArray() );
1502 const std::size_t nLen( deviceColor.getLength() );
1503 ENSURE_ARG_OR_THROW2(nLen%4==0,
1504 "number of channels no multiple of 4",
1505 static_cast<rendering::XColorSpace*>(this), 0);
1507 uno::Sequence<double> aRes(nLen);
1508 double* pOut( aRes.getArray() );
1509 for( std::size_t i=0; i<nLen; i+=4 )
1511 *pOut++ = vcl::unotools::toDoubleColor(*pIn++);
1512 *pOut++ = vcl::unotools::toDoubleColor(*pIn++);
1513 *pOut++ = vcl::unotools::toDoubleColor(*pIn++);
1514 *pOut++ = vcl::unotools::toDoubleColor(*pIn++);
1516 return aRes;
1518 else
1520 // TODO(P3): if we know anything about target
1521 // colorspace, this can be greatly sped up
1522 uno::Sequence<rendering::ARGBColor> aIntermediate(
1523 convertIntegerToARGB(deviceColor));
1524 return targetColorSpace->convertFromARGB(aIntermediate);
1527 virtual uno::Sequence< ::sal_Int8 > SAL_CALL convertToIntegerColorSpace( const uno::Sequence< ::sal_Int8 >& deviceColor,
1528 const uno::Reference< rendering::XIntegerBitmapColorSpace >& targetColorSpace ) override
1530 if( dynamic_cast<CairoColorSpace*>(targetColorSpace.get()) )
1532 // it's us, so simply pass-through the data
1533 return deviceColor;
1535 else
1537 // TODO(P3): if we know anything about target
1538 // colorspace, this can be greatly sped up
1539 uno::Sequence<rendering::ARGBColor> aIntermediate(
1540 convertIntegerToARGB(deviceColor));
1541 return targetColorSpace->convertIntegerFromARGB(aIntermediate);
1544 virtual uno::Sequence< rendering::RGBColor > SAL_CALL convertIntegerToRGB( const uno::Sequence< ::sal_Int8 >& deviceColor ) override
1546 const sal_Int8* pIn( deviceColor.getConstArray() );
1547 const std::size_t nLen( deviceColor.getLength() );
1548 ENSURE_ARG_OR_THROW2(nLen%4==0,
1549 "number of channels no multiple of 4",
1550 static_cast<rendering::XColorSpace*>(this), 0);
1552 uno::Sequence< rendering::RGBColor > aRes(nLen/4);
1553 rendering::RGBColor* pOut( aRes.getArray() );
1554 for( std::size_t i=0; i<nLen; i+=4 )
1556 const double fAlpha(static_cast<sal_uInt8>(pIn[3]));
1557 if( fAlpha )
1558 *pOut++ = rendering::RGBColor(
1559 pIn[2]/fAlpha,
1560 pIn[1]/fAlpha,
1561 pIn[0]/fAlpha);
1562 else
1563 *pOut++ = rendering::RGBColor(0,0,0);
1564 pIn += 4;
1566 return aRes;
1569 virtual uno::Sequence< rendering::ARGBColor > SAL_CALL convertIntegerToARGB( const uno::Sequence< ::sal_Int8 >& deviceColor ) override
1571 const sal_Int8* pIn( deviceColor.getConstArray() );
1572 const std::size_t nLen( deviceColor.getLength() );
1573 ENSURE_ARG_OR_THROW2(nLen%4==0,
1574 "number of channels no multiple of 4",
1575 static_cast<rendering::XColorSpace*>(this), 0);
1577 uno::Sequence< rendering::ARGBColor > aRes(nLen/4);
1578 rendering::ARGBColor* pOut( aRes.getArray() );
1579 for( std::size_t i=0; i<nLen; i+=4 )
1581 const double fAlpha(static_cast<sal_uInt8>(pIn[3]));
1582 if( fAlpha )
1583 *pOut++ = rendering::ARGBColor(
1584 fAlpha/255.0,
1585 pIn[2]/fAlpha,
1586 pIn[1]/fAlpha,
1587 pIn[0]/fAlpha);
1588 else
1589 *pOut++ = rendering::ARGBColor(0,0,0,0);
1590 pIn += 4;
1592 return aRes;
1594 virtual uno::Sequence< rendering::ARGBColor > SAL_CALL convertIntegerToPARGB( const uno::Sequence< ::sal_Int8 >& deviceColor ) override
1596 const sal_Int8* pIn( deviceColor.getConstArray() );
1597 const std::size_t nLen( deviceColor.getLength() );
1598 ENSURE_ARG_OR_THROW2(nLen%4==0,
1599 "number of channels no multiple of 4",
1600 static_cast<rendering::XColorSpace*>(this), 0);
1602 uno::Sequence< rendering::ARGBColor > aRes(nLen/4);
1603 rendering::ARGBColor* pOut( aRes.getArray() );
1604 for( std::size_t i=0; i<nLen; i+=4 )
1606 *pOut++ = rendering::ARGBColor(
1607 vcl::unotools::toDoubleColor(pIn[3]),
1608 vcl::unotools::toDoubleColor(pIn[2]),
1609 vcl::unotools::toDoubleColor(pIn[1]),
1610 vcl::unotools::toDoubleColor(pIn[0]));
1611 pIn += 4;
1613 return aRes;
1616 virtual uno::Sequence< ::sal_Int8 > SAL_CALL convertIntegerFromRGB( const uno::Sequence< rendering::RGBColor >& rgbColor ) override
1618 const rendering::RGBColor* pIn( rgbColor.getConstArray() );
1619 const std::size_t nLen( rgbColor.getLength() );
1621 uno::Sequence< sal_Int8 > aRes(nLen*4);
1622 sal_Int8* pColors=aRes.getArray();
1623 for( std::size_t i=0; i<nLen; ++i )
1625 *pColors++ = vcl::unotools::toByteColor(pIn->Blue);
1626 *pColors++ = vcl::unotools::toByteColor(pIn->Green);
1627 *pColors++ = vcl::unotools::toByteColor(pIn->Red);
1628 *pColors++ = -1;
1629 ++pIn;
1631 return aRes;
1634 virtual uno::Sequence< ::sal_Int8 > SAL_CALL convertIntegerFromARGB( const uno::Sequence< rendering::ARGBColor >& rgbColor ) override
1636 const rendering::ARGBColor* pIn( rgbColor.getConstArray() );
1637 const std::size_t nLen( rgbColor.getLength() );
1639 uno::Sequence< sal_Int8 > aRes(nLen*4);
1640 sal_Int8* pColors=aRes.getArray();
1641 for( std::size_t i=0; i<nLen; ++i )
1643 const double fAlpha(pIn->Alpha);
1644 *pColors++ = vcl::unotools::toByteColor(fAlpha*pIn->Blue);
1645 *pColors++ = vcl::unotools::toByteColor(fAlpha*pIn->Green);
1646 *pColors++ = vcl::unotools::toByteColor(fAlpha*pIn->Red);
1647 *pColors++ = vcl::unotools::toByteColor(fAlpha);
1648 ++pIn;
1650 return aRes;
1652 virtual uno::Sequence< ::sal_Int8 > SAL_CALL convertIntegerFromPARGB( const uno::Sequence< rendering::ARGBColor >& rgbColor ) override
1654 const rendering::ARGBColor* pIn( rgbColor.getConstArray() );
1655 const std::size_t nLen( rgbColor.getLength() );
1657 uno::Sequence< sal_Int8 > aRes(nLen*4);
1658 sal_Int8* pColors=aRes.getArray();
1659 for( std::size_t i=0; i<nLen; ++i )
1661 *pColors++ = vcl::unotools::toByteColor(pIn->Blue);
1662 *pColors++ = vcl::unotools::toByteColor(pIn->Green);
1663 *pColors++ = vcl::unotools::toByteColor(pIn->Red);
1664 *pColors++ = vcl::unotools::toByteColor(pIn->Alpha);
1665 ++pIn;
1667 return aRes;
1670 public:
1671 CairoColorSpace() :
1672 maComponentTags(4),
1673 maBitCounts(4)
1675 sal_Int8* pTags = maComponentTags.getArray();
1676 sal_Int32* pBitCounts = maBitCounts.getArray();
1677 pTags[0] = rendering::ColorComponentTag::RGB_BLUE;
1678 pTags[1] = rendering::ColorComponentTag::RGB_GREEN;
1679 pTags[2] = rendering::ColorComponentTag::RGB_RED;
1680 pTags[3] = rendering::ColorComponentTag::PREMULTIPLIED_ALPHA;
1682 pBitCounts[0] =
1683 pBitCounts[1] =
1684 pBitCounts[2] =
1685 pBitCounts[3] = 8;
1689 class CairoNoAlphaColorSpace : public cppu::WeakImplHelper< css::rendering::XIntegerBitmapColorSpace >
1691 private:
1692 uno::Sequence< sal_Int8 > maComponentTags;
1693 uno::Sequence< sal_Int32 > maBitCounts;
1695 virtual ::sal_Int8 SAL_CALL getType( ) override
1697 return rendering::ColorSpaceType::RGB;
1699 virtual uno::Sequence< ::sal_Int8 > SAL_CALL getComponentTags( ) override
1701 return maComponentTags;
1703 virtual ::sal_Int8 SAL_CALL getRenderingIntent( ) override
1705 return rendering::RenderingIntent::PERCEPTUAL;
1707 virtual uno::Sequence< beans::PropertyValue > SAL_CALL getProperties( ) override
1709 return uno::Sequence< beans::PropertyValue >();
1711 virtual uno::Sequence< double > SAL_CALL convertColorSpace( const uno::Sequence< double >& deviceColor,
1712 const uno::Reference< rendering::XColorSpace >& targetColorSpace ) override
1714 // TODO(P3): if we know anything about target
1715 // colorspace, this can be greatly sped up
1716 uno::Sequence<rendering::ARGBColor> aIntermediate(
1717 convertToARGB(deviceColor));
1718 return targetColorSpace->convertFromARGB(aIntermediate);
1720 virtual uno::Sequence< rendering::RGBColor > SAL_CALL convertToRGB( const uno::Sequence< double >& deviceColor ) override
1722 const double* pIn( deviceColor.getConstArray() );
1723 const std::size_t nLen( deviceColor.getLength() );
1724 ENSURE_ARG_OR_THROW2(nLen%4==0,
1725 "number of channels no multiple of 4",
1726 static_cast<rendering::XColorSpace*>(this), 0);
1728 uno::Sequence< rendering::RGBColor > aRes(nLen/4);
1729 rendering::RGBColor* pOut( aRes.getArray() );
1730 for( std::size_t i=0; i<nLen; i+=4 )
1732 *pOut++ = rendering::RGBColor(pIn[2], pIn[1], pIn[0]);
1733 pIn += 4;
1735 return aRes;
1737 uno::Sequence< rendering::ARGBColor > impl_convertToARGB( const uno::Sequence< double >& deviceColor )
1739 const double* pIn( deviceColor.getConstArray() );
1740 const std::size_t nLen( deviceColor.getLength() );
1741 ENSURE_ARG_OR_THROW2(nLen%4==0,
1742 "number of channels no multiple of 4",
1743 static_cast<rendering::XColorSpace*>(this), 0);
1745 uno::Sequence< rendering::ARGBColor > aRes(nLen/4);
1746 rendering::ARGBColor* pOut( aRes.getArray() );
1747 for( std::size_t i=0; i<nLen; i+=4 )
1749 *pOut++ = rendering::ARGBColor(1.0, pIn[2], pIn[1], pIn[0]);
1750 pIn += 4;
1752 return aRes;
1754 virtual uno::Sequence< rendering::ARGBColor > SAL_CALL convertToARGB( const uno::Sequence< double >& deviceColor ) override
1756 return impl_convertToARGB( deviceColor );
1758 virtual uno::Sequence< rendering::ARGBColor > SAL_CALL convertToPARGB( const uno::Sequence< double >& deviceColor ) override
1760 return impl_convertToARGB( deviceColor );
1762 virtual uno::Sequence< double > SAL_CALL convertFromRGB( const uno::Sequence< rendering::RGBColor >& rgbColor ) override
1764 const rendering::RGBColor* pIn( rgbColor.getConstArray() );
1765 const std::size_t nLen( rgbColor.getLength() );
1767 uno::Sequence< double > aRes(nLen*4);
1768 double* pColors=aRes.getArray();
1769 for( std::size_t i=0; i<nLen; ++i )
1771 *pColors++ = pIn->Blue;
1772 *pColors++ = pIn->Green;
1773 *pColors++ = pIn->Red;
1774 *pColors++ = 1.0; // the value does not matter
1775 ++pIn;
1777 return aRes;
1779 uno::Sequence< double > impl_convertFromARGB( const uno::Sequence< rendering::ARGBColor >& rgbColor )
1781 const rendering::ARGBColor* pIn( rgbColor.getConstArray() );
1782 const std::size_t nLen( rgbColor.getLength() );
1784 uno::Sequence< double > aRes(nLen*4);
1785 double* pColors=aRes.getArray();
1786 for( std::size_t i=0; i<nLen; ++i )
1788 *pColors++ = pIn->Blue;
1789 *pColors++ = pIn->Green;
1790 *pColors++ = pIn->Red;
1791 *pColors++ = 1.0; // the value does not matter
1792 ++pIn;
1794 return aRes;
1796 virtual uno::Sequence< double > SAL_CALL convertFromARGB( const uno::Sequence< rendering::ARGBColor >& rgbColor ) override
1798 return impl_convertFromARGB( rgbColor );
1800 virtual uno::Sequence< double > SAL_CALL convertFromPARGB( const uno::Sequence< rendering::ARGBColor >& rgbColor ) override
1802 return impl_convertFromARGB( rgbColor );
1805 // XIntegerBitmapColorSpace
1806 virtual ::sal_Int32 SAL_CALL getBitsPerPixel( ) override
1808 return 32;
1810 virtual uno::Sequence< ::sal_Int32 > SAL_CALL getComponentBitCounts( ) override
1812 return maBitCounts;
1814 virtual ::sal_Int8 SAL_CALL getEndianness( ) override
1816 return util::Endianness::LITTLE;
1818 virtual uno::Sequence<double> SAL_CALL convertFromIntegerColorSpace( const uno::Sequence< ::sal_Int8 >& deviceColor,
1819 const uno::Reference< rendering::XColorSpace >& targetColorSpace ) override
1821 if( dynamic_cast<CairoColorSpace*>(targetColorSpace.get()) )
1823 const sal_Int8* pIn( deviceColor.getConstArray() );
1824 const std::size_t nLen( deviceColor.getLength() );
1825 ENSURE_ARG_OR_THROW2(nLen%4==0,
1826 "number of channels no multiple of 4",
1827 static_cast<rendering::XColorSpace*>(this), 0);
1829 uno::Sequence<double> aRes(nLen);
1830 double* pOut( aRes.getArray() );
1831 for( std::size_t i=0; i<nLen; i+=4 )
1833 *pOut++ = vcl::unotools::toDoubleColor(*pIn++);
1834 *pOut++ = vcl::unotools::toDoubleColor(*pIn++);
1835 *pOut++ = vcl::unotools::toDoubleColor(*pIn++);
1836 *pOut++ = 1.0; pIn++; // the value does not matter
1838 return aRes;
1840 else
1842 // TODO(P3): if we know anything about target
1843 // colorspace, this can be greatly sped up
1844 uno::Sequence<rendering::ARGBColor> aIntermediate(
1845 convertIntegerToARGB(deviceColor));
1846 return targetColorSpace->convertFromARGB(aIntermediate);
1849 virtual uno::Sequence< ::sal_Int8 > SAL_CALL convertToIntegerColorSpace( const uno::Sequence< ::sal_Int8 >& deviceColor,
1850 const uno::Reference< rendering::XIntegerBitmapColorSpace >& targetColorSpace ) override
1852 if( dynamic_cast<CairoNoAlphaColorSpace*>(targetColorSpace.get()) )
1854 // it's us, so simply pass-through the data
1855 return deviceColor;
1857 else
1859 // TODO(P3): if we know anything about target
1860 // colorspace, this can be greatly sped up
1861 uno::Sequence<rendering::ARGBColor> aIntermediate(
1862 convertIntegerToARGB(deviceColor));
1863 return targetColorSpace->convertIntegerFromARGB(aIntermediate);
1866 virtual uno::Sequence< rendering::RGBColor > SAL_CALL convertIntegerToRGB( const uno::Sequence< ::sal_Int8 >& deviceColor ) override
1868 const sal_Int8* pIn( deviceColor.getConstArray() );
1869 const std::size_t nLen( deviceColor.getLength() );
1870 ENSURE_ARG_OR_THROW2(nLen%4==0,
1871 "number of channels no multiple of 4",
1872 static_cast<rendering::XColorSpace*>(this), 0);
1874 uno::Sequence< rendering::RGBColor > aRes(nLen/4);
1875 rendering::RGBColor* pOut( aRes.getArray() );
1876 for( std::size_t i=0; i<nLen; i+=4 )
1878 *pOut++ = rendering::RGBColor( pIn[2], pIn[1], pIn[0] );
1879 pIn += 4;
1881 return aRes;
1884 virtual uno::Sequence< rendering::ARGBColor > SAL_CALL convertIntegerToARGB( const uno::Sequence< ::sal_Int8 >& deviceColor ) override
1886 return impl_convertIntegerToARGB( deviceColor );
1888 virtual uno::Sequence< rendering::ARGBColor > SAL_CALL convertIntegerToPARGB( const uno::Sequence< ::sal_Int8 >& deviceColor ) override
1890 return impl_convertIntegerToARGB( deviceColor );
1892 uno::Sequence< rendering::ARGBColor > impl_convertIntegerToARGB( const uno::Sequence< ::sal_Int8 >& deviceColor )
1894 const sal_Int8* pIn( deviceColor.getConstArray() );
1895 const std::size_t nLen( deviceColor.getLength() );
1896 ENSURE_ARG_OR_THROW2(nLen%4==0,
1897 "number of channels no multiple of 4",
1898 static_cast<rendering::XColorSpace*>(this), 0);
1900 uno::Sequence< rendering::ARGBColor > aRes(nLen/4);
1901 rendering::ARGBColor* pOut( aRes.getArray() );
1902 for( std::size_t i=0; i<nLen; i+=4 )
1904 *pOut++ = rendering::ARGBColor(
1905 1.0,
1906 vcl::unotools::toDoubleColor(pIn[2]),
1907 vcl::unotools::toDoubleColor(pIn[1]),
1908 vcl::unotools::toDoubleColor(pIn[0]));
1909 pIn += 4;
1911 return aRes;
1914 virtual uno::Sequence< ::sal_Int8 > SAL_CALL convertIntegerFromRGB( const uno::Sequence< rendering::RGBColor >& rgbColor ) override
1916 const rendering::RGBColor* pIn( rgbColor.getConstArray() );
1917 const std::size_t nLen( rgbColor.getLength() );
1919 uno::Sequence< sal_Int8 > aRes(nLen*4);
1920 sal_Int8* pColors=aRes.getArray();
1921 for( std::size_t i=0; i<nLen; ++i )
1923 *pColors++ = vcl::unotools::toByteColor(pIn->Blue);
1924 *pColors++ = vcl::unotools::toByteColor(pIn->Green);
1925 *pColors++ = vcl::unotools::toByteColor(pIn->Red);
1926 *pColors++ = -1; // the value does not matter
1927 ++pIn;
1929 return aRes;
1932 virtual uno::Sequence< ::sal_Int8 > SAL_CALL convertIntegerFromARGB( const uno::Sequence< rendering::ARGBColor >& rgbColor ) override
1934 return impl_convertIntegerFromARGB( rgbColor );
1936 virtual uno::Sequence< ::sal_Int8 > SAL_CALL convertIntegerFromPARGB( const uno::Sequence< rendering::ARGBColor >& rgbColor ) override
1938 return impl_convertIntegerFromARGB( rgbColor );
1940 uno::Sequence< ::sal_Int8 > impl_convertIntegerFromARGB( const uno::Sequence< rendering::ARGBColor >& rgbColor )
1942 const rendering::ARGBColor* pIn( rgbColor.getConstArray() );
1943 const std::size_t nLen( rgbColor.getLength() );
1945 uno::Sequence< sal_Int8 > aRes(nLen*4);
1946 sal_Int8* pColors=aRes.getArray();
1947 for( std::size_t i=0; i<nLen; ++i )
1949 *pColors++ = vcl::unotools::toByteColor(pIn->Blue);
1950 *pColors++ = vcl::unotools::toByteColor(pIn->Green);
1951 *pColors++ = vcl::unotools::toByteColor(pIn->Red);
1952 *pColors++ = -1; // the value does not matter
1953 ++pIn;
1955 return aRes;
1958 public:
1959 CairoNoAlphaColorSpace() :
1960 maComponentTags(3),
1961 maBitCounts(3)
1963 sal_Int8* pTags = maComponentTags.getArray();
1964 sal_Int32* pBitCounts = maBitCounts.getArray();
1965 pTags[0] = rendering::ColorComponentTag::RGB_BLUE;
1966 pTags[1] = rendering::ColorComponentTag::RGB_GREEN;
1967 pTags[2] = rendering::ColorComponentTag::RGB_RED;
1969 pBitCounts[0] =
1970 pBitCounts[1] =
1971 pBitCounts[2] = 8;
1975 struct CairoNoAlphaColorSpaceHolder : public rtl::StaticWithInit<uno::Reference<rendering::XIntegerBitmapColorSpace>,
1976 CairoNoAlphaColorSpaceHolder>
1978 uno::Reference<rendering::XIntegerBitmapColorSpace> operator()()
1980 return new CairoNoAlphaColorSpace();
1984 struct CairoColorSpaceHolder : public rtl::StaticWithInit<uno::Reference<rendering::XIntegerBitmapColorSpace>,
1985 CairoColorSpaceHolder>
1987 uno::Reference<rendering::XIntegerBitmapColorSpace> operator()()
1989 return new CairoColorSpace();
1995 rendering::IntegerBitmapLayout CanvasHelper::getMemoryLayout()
1997 if( !mpCairo )
1998 return rendering::IntegerBitmapLayout(); // we're disposed
2000 const geometry::IntegerSize2D aSize(getSize());
2002 return impl_getMemoryLayout( aSize.Width, aSize.Height );
2005 rendering::IntegerBitmapLayout
2006 CanvasHelper::impl_getMemoryLayout( const sal_Int32 nWidth, const sal_Int32 nHeight )
2008 rendering::IntegerBitmapLayout aLayout;
2010 aLayout.ScanLines = nHeight;
2011 aLayout.ScanLineBytes = nWidth*4;
2012 aLayout.ScanLineStride = aLayout.ScanLineBytes;
2013 aLayout.PlaneStride = 0;
2014 aLayout.ColorSpace = mbHaveAlpha ? CairoColorSpaceHolder::get() : CairoNoAlphaColorSpaceHolder::get();
2015 aLayout.Palette.clear();
2016 aLayout.IsMsbFirst = false;
2018 return aLayout;
2022 bool CanvasHelper::repaint( const SurfaceSharedPtr& pSurface,
2023 const rendering::ViewState& viewState,
2024 const rendering::RenderState& renderState )
2026 SAL_INFO( "canvas.cairo", "CanvasHelper::repaint");
2028 if( mpCairo )
2030 cairo_save( mpCairo.get() );
2032 cairo_rectangle( mpCairo.get(), 0, 0, maSize.getX(), maSize.getY() );
2033 cairo_clip( mpCairo.get() );
2035 useStates( viewState, renderState, true );
2037 cairo_matrix_t aMatrix;
2039 cairo_get_matrix( mpCairo.get(), &aMatrix );
2040 aMatrix.xx = aMatrix.yy = 1;
2041 cairo_set_matrix( mpCairo.get(), &aMatrix );
2043 cairo_set_source_surface( mpCairo.get(), pSurface->getCairoSurface().get(), 0, 0 );
2044 cairo_paint( mpCairo.get() );
2045 cairo_restore( mpCairo.get() );
2048 return true;
2052 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */