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