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