Update git submodules
[LibreOffice.git] / canvas / source / cairo / cairo_canvashelper.cxx
blob701ad1e4785999698976a48e92ff7e6132357627
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/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>
53 #include <cairo.h>
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;
62 namespace cairocanvas
64 CanvasHelper::CanvasHelper() :
65 mpSurfaceProvider(nullptr),
66 mpDevice(nullptr),
67 mbHaveAlpha()
71 void CanvasHelper::disposing()
73 mpSurface.reset();
74 mpCairo.reset();
75 mpVirtualDevice.disposeAndClear();
76 mpDevice = nullptr;
77 mpSurfaceProvider = nullptr;
80 void CanvasHelper::init( const ::basegfx::B2ISize& rSizePixel,
81 SurfaceProvider& rSurfaceProvider,
82 rendering::XGraphicDevice* pDevice )
84 maSize = rSizePixel;
85 mpSurfaceProvider = &rSurfaceProvider;
86 mpDevice = pDevice;
89 void CanvasHelper::setSize( const ::basegfx::B2ISize& rSize )
91 maSize = rSize;
94 void CanvasHelper::setSurface( const SurfaceSharedPtr& pSurface, bool bHasAlpha )
96 mbHaveAlpha = bHasAlpha;
97 mpVirtualDevice.disposeAndClear();
98 mpSurface = pSurface;
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,
108 rColor[0],
109 rColor[1],
110 rColor[2],
111 rColor[3] );
113 else if( rColor.getLength() == 3 )
114 cairo_set_source_rgb( pCairo,
115 rColor[0],
116 rColor[1],
117 rColor[2] );
120 void CanvasHelper::useStates( const rendering::ViewState& viewState,
121 const rendering::RenderState& renderState,
122 bool bSetColor )
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");
158 if( bSetColor )
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;
166 break;
167 case rendering::CompositeOperation::SOURCE:
168 compositingMode = CAIRO_OPERATOR_SOURCE;
169 break;
170 case rendering::CompositeOperation::DESTINATION:
171 case rendering::CompositeOperation::UNDER:
172 compositingMode = CAIRO_OPERATOR_DEST;
173 break;
174 case rendering::CompositeOperation::OVER:
175 compositingMode = CAIRO_OPERATOR_OVER;
176 break;
177 case rendering::CompositeOperation::INSIDE:
178 compositingMode = CAIRO_OPERATOR_IN;
179 break;
180 case rendering::CompositeOperation::INSIDE_REVERSE:
181 compositingMode = CAIRO_OPERATOR_OUT;
182 break;
183 case rendering::CompositeOperation::OUTSIDE:
184 compositingMode = CAIRO_OPERATOR_DEST_OVER;
185 break;
186 case rendering::CompositeOperation::OUTSIDE_REVERSE:
187 compositingMode = CAIRO_OPERATOR_DEST_OUT;
188 break;
189 case rendering::CompositeOperation::ATOP:
190 compositingMode = CAIRO_OPERATOR_ATOP;
191 break;
192 case rendering::CompositeOperation::ATOP_REVERSE:
193 compositingMode = CAIRO_OPERATOR_DEST_ATOP;
194 break;
195 case rendering::CompositeOperation::XOR:
196 compositingMode = CAIRO_OPERATOR_XOR;
197 break;
198 case rendering::CompositeOperation::ADD:
199 compositingMode = CAIRO_OPERATOR_ADD;
200 break;
201 case rendering::CompositeOperation::SATURATE:
202 compositingMode = CAIRO_OPERATOR_SATURATE;
203 break;
205 cairo_set_operator( mpCairo.get(), compositingMode );
208 void CanvasHelper::clear()
210 SAL_INFO( "canvas.cairo", "clear whole area: " << maSize.getWidth() << " x " << maSize.getHeight() );
212 if( !mpCairo )
213 return;
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
221 if( mbHaveAlpha )
222 cairo_set_source_rgba( mpCairo.get(), 1.0, 1.0, 1.0, 0.0 );
223 else
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 )
239 if( !mpCairo )
240 return;
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 )
261 if( !mpCairo )
262 return;
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() );
296 if( pBitmapImpl )
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() )
313 return aBmpEx;
315 // TODO(F1): extract pixel from XBitmap interface
316 ENSURE_OR_THROW( false,
317 "bitmapExFromXBitmap(): could not extract BitmapEx" );
319 return ::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 );
337 if( pSurface )
338 data = nullptr;
339 else
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 );
350 data = nullptr;
351 bHasAlpha = false;
354 if( !pSurface )
356 tools::Long nWidth;
357 tools::Long nHeight;
358 vcl::bitmap::CanvasCairoExtractBitmapData(aBmpEx, aBitmap, data, bHasAlpha, nWidth, nHeight);
360 pSurface = rSurfaceProvider->getOutputDevice()->CreateSurface(
361 CairoSurfaceSharedPtr(
362 cairo_image_surface_create_for_data(
363 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);
372 return pSurface;
375 static void addColorStops( cairo_pattern_t* pPattern, const uno::Sequence< uno::Sequence< double > >& rColors, const uno::Sequence< double >& rStops, bool bReverseStops )
377 int i;
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 )
400 return
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 )
409 return
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)
418 return {};
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:
430 x0 = 0;
431 y0 = 0;
432 x1 = 1;
433 y1 = 0;
434 pPattern = cairo_pattern_create_linear( x0, y0, x1, y1 );
435 addColorStops( pPattern, aValues.maColors, aValues.maStops, false );
436 break;
438 case ::canvas::ParametricPolyPolygon::GradientType::Elliptical:
439 cx = 0;
440 cy = 0;
441 r0 = 0;
442 r1 = 1;
444 pPattern = cairo_pattern_create_radial( cx, cy, r0, cy, cy, r1 );
445 addColorStops( pPattern, aValues.maColors, aValues.maStops, true );
446 break;
447 default:
448 break;
451 return pPattern;
454 static void doOperation( Operation aOperation,
455 cairo_t* pCairo,
456 const uno::Sequence< rendering::Texture >* pTextures,
457 const SurfaceProviderRef& pDevice,
458 const basegfx::B2DRange& rBounds )
460 switch( aOperation )
462 case Fill:
463 /* TODO: multitexturing */
464 if( pTextures )
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 );
473 if( pSurface )
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 );
522 if( !bHasAlpha )
523 cairo_set_operator( pCairo, CAIRO_OPERATOR_SOURCE );
524 cairo_fill( pCairo );
526 cairo_restore( pCairo );
528 cairo_pattern_destroy( pPattern );
531 if( data )
532 free( data );
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]);
564 cairo_fill(pCairo);
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
577 // steps
578 const unsigned int nStepCount(
579 std::max(
581 std::min(
582 nGradientSize / nStripSize,
583 128U )) + 1 );
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;
592 double fAlpha;
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 );
597 cairo_fill(pCairo);
600 cairo_restore( pCairo );
602 else
604 cairo_pattern_t* pPattern = patternFromParametricPolyPolygon( *pPolyImpl );
606 if( pPattern )
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 );
623 else
624 cairo_fill( pCairo );
625 SAL_INFO( "canvas.cairo", "fill");
626 break;
627 case Stroke:
628 cairo_stroke( pCairo );
629 SAL_INFO( "canvas.cairo", "stroke");
630 break;
631 case Clip:
632 cairo_clip( pCairo );
633 SAL_INFO( "canvas.cairo", "clip");
634 break;
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,
664 cairo_t* pCairo,
665 const uno::Sequence< rendering::Texture >* pTextures,
666 const SurfaceProviderRef& pDevice,
667 rendering::FillRule eFillrule )
669 if( pTextures )
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()) );
694 if( nPointCount > 1)
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 );
703 nX = aP.getX();
704 nY = aP.getY();
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 )
715 nX += 0.5;
716 nY += 0.5;
719 if( j==0 )
721 cairo_move_to( pCairo, nX, nY );
722 SAL_INFO( "canvas.cairo", "move to " << nX << "," << nY );
724 else
726 if( bIsBezier )
728 aA = aPolygon.getNextControlPoint( (j-1) % nPointCount );
729 aB = aPolygon.getPrevControlPoint( j % nPointCount );
731 nAX = aA.getX();
732 nAY = aA.getY();
733 nBX = aB.getX();
734 nBY = aB.getY();
736 cairo_matrix_transform_point( &aOrigMatrix, &nAX, &nAY );
737 cairo_matrix_transform_point( &aOrigMatrix, &nBX, &nBY );
739 if( aOperation == Stroke )
741 nAX += 0.5;
742 nAY += 0.5;
743 nBX += 0.5;
744 nBY += 0.5;
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 );
765 else
767 cairo_line_to( pCairo, nX, nY );
768 SAL_INFO( "canvas.cairo", "line to " << nX << "," << nY );
770 bOpToDo = true;
773 nLastX = nX;
774 nLastY = nY;
777 if( aPolygon.isClosed() )
778 cairo_close_path( pCairo );
781 else
783 SAL_INFO( "canvas.cairo", "empty polygon for op: " << aOperation );
784 if( aOperation == Clip )
786 clipNULL( pCairo );
788 return;
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 )
806 clipNULL( pCairo );
809 void CanvasHelper::doPolyPolygonPath( const uno::Reference< rendering::XPolyPolygon2D >& xPolyPolygon,
810 Operation aOperation,
811 bool bNoLineJoin,
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());
827 if(nPointCount)
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),
842 aOperation,
843 pCairo, pTextures,
844 mpSurfaceProvider,
845 xPolyPolygon->getFillRule() );
847 // prepare next step
848 aEdge.setB2DPoint(0, aEdge.getB2DPoint(1));
853 else
855 doPolyPolygonImplementation( aPolyPoly, aOperation,
856 pCairo, pTextures,
857 mpSurfaceProvider,
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 );
870 #endif
872 if( mpCairo )
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() );
883 else
884 SAL_INFO( "canvas.cairo", "CanvasHelper called after it was disposed");
886 #ifdef CAIRO_CANVAS_PERF_TRACE
887 mxDevice->stopPerfTrace( &aTimer, "drawPolyPolygon" );
888 #endif
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 );
902 #endif
904 if( mpCairo )
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 );
925 break;
926 case rendering::PathCapType::ROUND:
927 cairo_set_line_cap( mpCairo.get(), CAIRO_LINE_CAP_ROUND );
928 break;
929 case rendering::PathCapType::SQUARE:
930 cairo_set_line_cap( mpCairo.get(), CAIRO_LINE_CAP_SQUARE );
931 break;
934 bool bNoLineJoin(false);
936 switch( strokeAttributes.JoinType )
938 case rendering::PathJoinType::NONE:
939 bNoLineJoin = true;
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 );
943 break;
944 case rendering::PathJoinType::ROUND:
945 cairo_set_line_join( mpCairo.get(), CAIRO_LINE_JOIN_ROUND );
946 break;
947 case rendering::PathJoinType::BEVEL:
948 cairo_set_line_join( mpCairo.get(), CAIRO_LINE_JOIN_BEVEL );
949 break;
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() );
969 else
970 SAL_INFO( "canvas.cairo", "CanvasHelper called after it was disposed");
972 #ifdef CAIRO_CANVAS_PERF_TRACE
973 mxDevice->stopPerfTrace( &aTimer, "strokePolyPolygon" );
974 #endif
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*/ )
987 // TODO
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*/ )
999 // TODO
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*/ )
1009 // TODO
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 );
1021 #endif
1023 if( mpCairo )
1025 cairo_save( mpCairo.get() );
1027 useStates( viewState, renderState, true );
1028 doPolyPolygonPath( xPolyPolygon, Fill );
1030 cairo_restore( mpCairo.get() );
1032 else
1033 SAL_INFO( "canvas.cairo", "CanvasHelper called after it was disposed");
1035 #ifdef CAIRO_CANVAS_PERF_TRACE
1036 mxDevice->stopPerfTrace( &aTimer, "fillPolyPolygon" );
1037 #endif
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 )
1048 if( mpCairo )
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*/ )
1068 // TODO
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,
1078 bool bHasAlpha )
1080 SurfaceSharedPtr pSurface=pInputSurface;
1081 uno::Reference< rendering::XCachedPrimitive > rv;
1082 geometry::IntegerSize2D aBitmapSize = rSize;
1084 if( mpCairo )
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 );
1126 rv.set(
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;
1139 x = y = 0;
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();
1154 if( pNewSurface )
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 );
1166 if( !bHasAlpha &&
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]);
1188 else
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() );
1200 else
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 );
1214 #endif
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();
1222 if( pSurface )
1224 rv = implDrawBitmapSurface( pCanvas, pSurface, viewState, renderState, aSize, false, bHasAlpha );
1226 if( data )
1227 free( data );
1229 else
1230 rv.clear();
1232 #ifdef CAIRO_CANVAS_PERF_TRACE
1233 mxDevice->stopPerfTrace( &aTimer, "drawBitmap" );
1234 #endif
1236 return rv;
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 );
1247 #endif
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();
1255 if( pSurface )
1257 rv = implDrawBitmapSurface( pCanvas, pSurface, viewState, renderState, aSize, true, bHasAlpha );
1259 if( data )
1260 free( data );
1262 else
1263 rv.clear();
1265 #ifdef CAIRO_CANVAS_PERF_TRACE
1266 mxDevice->stopPerfTrace( &aTimer, "drawBitmap" );
1267 #endif
1269 return rv;
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,
1282 bool /*beFast*/ )
1284 #ifdef CAIRO_CANVAS_PERF_TRACE
1285 struct timespec aTimer;
1286 mxDevice->startPerfTrace( &aTimer );
1287 #endif
1289 if( mpCairo )
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 ) );
1295 else
1296 SAL_INFO( "canvas.cairo", "CanvasHelper called after it was disposed");
1298 #ifdef CAIRO_CANVAS_PERF_TRACE
1299 mxDevice->stopPerfTrace( &aTimer, "getScaledBitmap" );
1300 #endif
1302 return uno::Reference< rendering::XBitmap >();
1305 uno::Sequence< sal_Int8 > CanvasHelper::getData( rendering::IntegerBitmapLayout& aLayout,
1306 const geometry::IntegerRectangle2D& rect )
1308 if( mpCairo )
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),
1316 eFormat,
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 );
1326 return aRes;
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 >();
1338 namespace
1340 class CairoColorSpace : public cppu::WeakImplHelper< css::rendering::XIntegerBitmapColorSpace >
1342 private:
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]);
1384 if( fAlpha == 0.0 )
1385 *pOut++ = rendering::RGBColor(0.0, 0.0, 0.0);
1386 else
1387 *pOut++ = rendering::RGBColor(pIn[2]/fAlpha,pIn[1]/fAlpha,pIn[0]/fAlpha);
1388 pIn += 4;
1390 return aRes;
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]);
1405 if( fAlpha == 0.0 )
1406 *pOut++ = rendering::ARGBColor(0.0, 0.0, 0.0, 0.0);
1407 else
1408 *pOut++ = rendering::ARGBColor(fAlpha,pIn[2]/fAlpha,pIn[1]/fAlpha,pIn[0]/fAlpha);
1409 pIn += 4;
1411 return aRes;
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]);
1426 pIn += 4;
1428 return aRes;
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;
1442 *pColors++ = 1.0;
1443 ++pIn;
1445 return aRes;
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;
1460 ++pIn;
1462 return aRes;
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;
1477 ++pIn;
1479 return aRes;
1482 // XIntegerBitmapColorSpace
1483 virtual ::sal_Int32 SAL_CALL getBitsPerPixel( ) override
1485 return 32;
1487 virtual uno::Sequence< ::sal_Int32 > SAL_CALL getComponentBitCounts( ) override
1489 return maBitCounts;
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++);
1515 return aRes;
1517 else
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
1532 return deviceColor;
1534 else
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]));
1556 if( fAlpha )
1557 *pOut++ = rendering::RGBColor(
1558 pIn[2]/fAlpha,
1559 pIn[1]/fAlpha,
1560 pIn[0]/fAlpha);
1561 else
1562 *pOut++ = rendering::RGBColor(0,0,0);
1563 pIn += 4;
1565 return aRes;
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]));
1581 if( fAlpha )
1582 *pOut++ = rendering::ARGBColor(
1583 fAlpha/255.0,
1584 pIn[2]/fAlpha,
1585 pIn[1]/fAlpha,
1586 pIn[0]/fAlpha);
1587 else
1588 *pOut++ = rendering::ARGBColor(0,0,0,0);
1589 pIn += 4;
1591 return aRes;
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]));
1610 pIn += 4;
1612 return aRes;
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);
1627 *pColors++ = -1;
1628 ++pIn;
1630 return aRes;
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);
1647 ++pIn;
1649 return aRes;
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);
1664 ++pIn;
1666 return aRes;
1669 public:
1670 CairoColorSpace() :
1671 maComponentTags(4),
1672 maBitCounts(4)
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;
1681 pBitCounts[0] =
1682 pBitCounts[1] =
1683 pBitCounts[2] =
1684 pBitCounts[3] = 8;
1688 class CairoNoAlphaColorSpace : public cppu::WeakImplHelper< css::rendering::XIntegerBitmapColorSpace >
1690 private:
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]);
1732 pIn += 4;
1734 return aRes;
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]);
1749 pIn += 4;
1751 return aRes;
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
1774 ++pIn;
1776 return aRes;
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
1791 ++pIn;
1793 return aRes;
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
1807 return 32;
1809 virtual uno::Sequence< ::sal_Int32 > SAL_CALL getComponentBitCounts( ) override
1811 return maBitCounts;
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
1837 return aRes;
1839 else
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
1854 return deviceColor;
1856 else
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] );
1878 pIn += 4;
1880 return aRes;
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(
1904 1.0,
1905 vcl::unotools::toDoubleColor(pIn[2]),
1906 vcl::unotools::toDoubleColor(pIn[1]),
1907 vcl::unotools::toDoubleColor(pIn[0]));
1908 pIn += 4;
1910 return aRes;
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
1926 ++pIn;
1928 return aRes;
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
1952 ++pIn;
1954 return aRes;
1957 public:
1958 CairoNoAlphaColorSpace() :
1959 maComponentTags(3),
1960 maBitCounts(3)
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;
1968 pBitCounts[0] =
1969 pBitCounts[1] =
1970 pBitCounts[2] = 8;
1974 uno::Reference<rendering::XIntegerBitmapColorSpace>& GetCairoNoAlphaColorSpace()
1976 static uno::Reference<rendering::XIntegerBitmapColorSpace> SPACE = new CairoNoAlphaColorSpace();
1977 return SPACE;
1980 uno::Reference<rendering::XIntegerBitmapColorSpace>& GetCairoColorSpace()
1982 static uno::Reference<rendering::XIntegerBitmapColorSpace> SPACE = new CairoColorSpace();
1983 return SPACE;
1988 rendering::IntegerBitmapLayout CanvasHelper::getMemoryLayout()
1990 if( !mpCairo )
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;
2011 return aLayout;
2015 bool CanvasHelper::repaint( const SurfaceSharedPtr& pSurface,
2016 const rendering::ViewState& viewState,
2017 const rendering::RenderState& renderState )
2019 SAL_INFO( "canvas.cairo", "CanvasHelper::repaint");
2021 if( !mpCairo )
2022 return true;
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() );
2041 return true;
2045 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */