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