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 #include <canvas/verbosetrace.hxx> 34 #include <canvas/canvastools.hxx> 35 36 #include <rtl/math.hxx> 37 38 #include <basegfx/matrix/b2dhommatrix.hxx> 39 #include <basegfx/point/b2dpoint.hxx> 40 #include <basegfx/tools/canvastools.hxx> 41 #include <basegfx/polygon/b2dpolygon.hxx> 42 #include <basegfx/polygon/b2dpolygontools.hxx> 43 #include <basegfx/polygon/b2dpolypolygontools.hxx> 44 #include <basegfx/numeric/ftools.hxx> 45 46 #include <canvas/base/canvascustomspritehelper.hxx> 47 48 using namespace ::com::sun::star; 49 50 51 namespace canvas 52 { 53 bool CanvasCustomSpriteHelper::updateClipState( const Sprite::Reference& rSprite ) 54 { 55 if( !mxClipPoly.is() ) 56 { 57 // empty clip polygon -> everything is visible now 58 maCurrClipBounds.reset(); 59 mbIsCurrClipRectangle = true; 60 } 61 else 62 { 63 const sal_Int32 nNumClipPolygons( mxClipPoly->getNumberOfPolygons() ); 64 65 // clip is not empty - determine actual update area 66 ::basegfx::B2DPolyPolygon aClipPath( 67 polyPolygonFromXPolyPolygon2D( mxClipPoly ) ); 68 69 // apply sprite transformation also to clip! 70 aClipPath.transform( maTransform ); 71 72 // clip which is about to be set, expressed as a 73 // b2drectangle 74 const ::basegfx::B2DRectangle& rClipBounds( 75 ::basegfx::tools::getRange( aClipPath ) ); 76 77 const ::basegfx::B2DRectangle aBounds( 0.0, 0.0, 78 maSize.getX(), 79 maSize.getY() ); 80 81 // rectangular area which is actually covered by the sprite. 82 // coordinates are relative to the sprite origin. 83 ::basegfx::B2DRectangle aSpriteRectPixel; 84 ::canvas::tools::calcTransformedRectBounds( aSpriteRectPixel, 85 aBounds, 86 maTransform ); 87 88 // aClipBoundsA = new clip bound rect, intersected 89 // with sprite area 90 ::basegfx::B2DRectangle aClipBoundsA(rClipBounds); 91 aClipBoundsA.intersect( aSpriteRectPixel ); 92 93 if( nNumClipPolygons != 1 ) 94 { 95 // clip cannot be a single rectangle -> cannot 96 // optimize update 97 mbIsCurrClipRectangle = false; 98 maCurrClipBounds = aClipBoundsA; 99 } 100 else 101 { 102 // new clip could be a single rectangle - check 103 // that now: 104 const bool bNewClipIsRect( 105 ::basegfx::tools::isRectangle( aClipPath.getB2DPolygon(0) ) ); 106 107 // both new and old clip are truly rectangles 108 // - can now take the optimized path 109 const bool bUseOptimizedUpdate( bNewClipIsRect && 110 mbIsCurrClipRectangle ); 111 112 const ::basegfx::B2DRectangle aOldBounds( maCurrClipBounds ); 113 114 // store new current clip type 115 maCurrClipBounds = aClipBoundsA; 116 mbIsCurrClipRectangle = bNewClipIsRect; 117 118 if( mbActive && 119 bUseOptimizedUpdate ) 120 { 121 // aClipBoundsB = maCurrClipBounds, i.e. last 122 // clip, intersected with sprite area 123 typedef ::std::vector< ::basegfx::B2DRectangle > VectorOfRects; 124 VectorOfRects aClipDifferences; 125 126 // get all rectangles covered by exactly one 127 // of the polygons (aka XOR) 128 ::basegfx::computeSetDifference(aClipDifferences, 129 aClipBoundsA, 130 aOldBounds); 131 132 // aClipDifferences now contains the final 133 // update areas, coordinates are still relative 134 // to the sprite origin. before submitting 135 // this area to 'updateSprite()' we need to 136 // translate this area to the final position, 137 // coordinates need to be relative to the 138 // spritecanvas. 139 VectorOfRects::const_iterator aCurr( aClipDifferences.begin() ); 140 const VectorOfRects::const_iterator aEnd( aClipDifferences.end() ); 141 while( aCurr != aEnd ) 142 { 143 mpSpriteCanvas->updateSprite( 144 rSprite, 145 maPosition, 146 ::basegfx::B2DRectangle( 147 maPosition + aCurr->getMinimum(), 148 maPosition + aCurr->getMaximum() ) ); 149 ++aCurr; 150 } 151 152 // update calls all done 153 return true; 154 } 155 } 156 } 157 158 // caller needs to perform update calls 159 return false; 160 } 161 162 CanvasCustomSpriteHelper::CanvasCustomSpriteHelper() : 163 mpSpriteCanvas(), 164 maCurrClipBounds(), 165 maPosition(), 166 maSize(), 167 maTransform(), 168 mxClipPoly(), 169 mfPriority(0.0), 170 mfAlpha(0.0), 171 mbActive(false), 172 mbIsCurrClipRectangle(true), 173 mbIsContentFullyOpaque( false ), 174 mbAlphaDirty( true ), 175 mbPositionDirty( true ), 176 mbTransformDirty( true ), 177 mbClipDirty( true ), 178 mbPrioDirty( true ), 179 mbVisibilityDirty( true ) 180 { 181 } 182 183 void CanvasCustomSpriteHelper::init( const geometry::RealSize2D& rSpriteSize, 184 const SpriteSurface::Reference& rOwningSpriteCanvas ) 185 { 186 ENSURE_OR_THROW( rOwningSpriteCanvas.get(), 187 "CanvasCustomSpriteHelper::init(): Invalid owning sprite canvas" ); 188 189 mpSpriteCanvas = rOwningSpriteCanvas; 190 maSize.setX( ::std::max( 1.0, 191 ceil( rSpriteSize.Width ) ) ); // round up to nearest int, 192 // enforce sprite to have at 193 // least (1,1) pixel size 194 maSize.setY( ::std::max( 1.0, 195 ceil( rSpriteSize.Height ) ) ); 196 } 197 198 void CanvasCustomSpriteHelper::disposing() 199 { 200 mpSpriteCanvas.clear(); 201 } 202 203 void CanvasCustomSpriteHelper::clearingContent( const Sprite::Reference& /*rSprite*/ ) 204 { 205 // about to clear content to fully transparent 206 mbIsContentFullyOpaque = false; 207 } 208 209 void CanvasCustomSpriteHelper::checkDrawBitmap( const Sprite::Reference& rSprite, 210 const uno::Reference< rendering::XBitmap >& xBitmap, 211 const rendering::ViewState& viewState, 212 const rendering::RenderState& renderState ) 213 { 214 // check whether bitmap is non-alpha, and whether its 215 // transformed size covers the whole sprite. 216 if( !xBitmap->hasAlpha() ) 217 { 218 const geometry::IntegerSize2D& rInputSize( 219 xBitmap->getSize() ); 220 const ::basegfx::B2DSize& rOurSize( 221 rSprite->getSizePixel() ); 222 223 ::basegfx::B2DHomMatrix aTransform; 224 if( tools::isInside( 225 ::basegfx::B2DRectangle( 0.0,0.0, 226 rOurSize.getX(), 227 rOurSize.getY() ), 228 ::basegfx::B2DRectangle( 0.0,0.0, 229 rInputSize.Width, 230 rInputSize.Height ), 231 ::canvas::tools::mergeViewAndRenderTransform(aTransform, 232 viewState, 233 renderState) ) ) 234 { 235 // bitmap is opaque and will fully cover the sprite, 236 // set flag appropriately 237 mbIsContentFullyOpaque = true; 238 } 239 } 240 } 241 242 void CanvasCustomSpriteHelper::setAlpha( const Sprite::Reference& rSprite, 243 double alpha ) 244 { 245 if( !mpSpriteCanvas.get() ) 246 return; // we're disposed 247 248 if( alpha != mfAlpha ) 249 { 250 mfAlpha = alpha; 251 252 if( mbActive ) 253 { 254 mpSpriteCanvas->updateSprite( rSprite, 255 maPosition, 256 getUpdateArea() ); 257 } 258 259 mbAlphaDirty = true; 260 } 261 } 262 263 void CanvasCustomSpriteHelper::move( const Sprite::Reference& rSprite, 264 const geometry::RealPoint2D& aNewPos, 265 const rendering::ViewState& viewState, 266 const rendering::RenderState& renderState ) 267 { 268 if( !mpSpriteCanvas.get() ) 269 return; // we're disposed 270 271 ::basegfx::B2DHomMatrix aTransform; 272 ::canvas::tools::mergeViewAndRenderTransform(aTransform, 273 viewState, 274 renderState); 275 276 // convert position to device pixel 277 ::basegfx::B2DPoint aPoint( 278 ::basegfx::unotools::b2DPointFromRealPoint2D(aNewPos) ); 279 aPoint *= aTransform; 280 281 if( aPoint != maPosition ) 282 { 283 const ::basegfx::B2DRectangle& rBounds( getFullSpriteRect() ); 284 285 if( mbActive ) 286 { 287 mpSpriteCanvas->moveSprite( rSprite, 288 rBounds.getMinimum(), 289 rBounds.getMinimum() - maPosition + aPoint, 290 rBounds.getRange() ); 291 } 292 293 maPosition = aPoint; 294 mbPositionDirty = true; 295 } 296 } 297 298 void CanvasCustomSpriteHelper::transform( const Sprite::Reference& rSprite, 299 const geometry::AffineMatrix2D& aTransformation ) 300 { 301 ::basegfx::B2DHomMatrix aMatrix; 302 ::basegfx::unotools::homMatrixFromAffineMatrix(aMatrix, 303 aTransformation); 304 305 if( maTransform != aMatrix ) 306 { 307 // retrieve bounds before and after transformation change. 308 const ::basegfx::B2DRectangle& rPrevBounds( getUpdateArea() ); 309 310 maTransform = aMatrix; 311 312 if( !updateClipState( rSprite ) && 313 mbActive ) 314 { 315 mpSpriteCanvas->updateSprite( rSprite, 316 maPosition, 317 rPrevBounds ); 318 mpSpriteCanvas->updateSprite( rSprite, 319 maPosition, 320 getUpdateArea() ); 321 } 322 323 mbTransformDirty = true; 324 } 325 } 326 327 void CanvasCustomSpriteHelper::clip( const Sprite::Reference& rSprite, 328 const uno::Reference< rendering::XPolyPolygon2D >& xClip ) 329 { 330 // NULL xClip explicitely allowed here (to clear clipping) 331 332 // retrieve bounds before and after clip change. 333 const ::basegfx::B2DRectangle& rPrevBounds( getUpdateArea() ); 334 335 mxClipPoly = xClip; 336 337 if( !updateClipState( rSprite ) && 338 mbActive ) 339 { 340 mpSpriteCanvas->updateSprite( rSprite, 341 maPosition, 342 rPrevBounds ); 343 mpSpriteCanvas->updateSprite( rSprite, 344 maPosition, 345 getUpdateArea() ); 346 } 347 348 mbClipDirty = true; 349 } 350 351 void CanvasCustomSpriteHelper::setPriority( const Sprite::Reference& rSprite, 352 double nPriority ) 353 { 354 if( !mpSpriteCanvas.get() ) 355 return; // we're disposed 356 357 if( nPriority != mfPriority ) 358 { 359 mfPriority = nPriority; 360 361 if( mbActive ) 362 { 363 mpSpriteCanvas->updateSprite( rSprite, 364 maPosition, 365 getUpdateArea() ); 366 } 367 368 mbPrioDirty = true; 369 } 370 } 371 372 void CanvasCustomSpriteHelper::show( const Sprite::Reference& rSprite ) 373 { 374 if( !mpSpriteCanvas.get() ) 375 return; // we're disposed 376 377 if( !mbActive ) 378 { 379 mpSpriteCanvas->showSprite( rSprite ); 380 mbActive = true; 381 382 // TODO(P1): if clip is the NULL clip (nothing visible), 383 // also save us the update call. 384 385 if( mfAlpha != 0.0 ) 386 { 387 mpSpriteCanvas->updateSprite( rSprite, 388 maPosition, 389 getUpdateArea() ); 390 } 391 392 mbVisibilityDirty = true; 393 } 394 } 395 396 void CanvasCustomSpriteHelper::hide( const Sprite::Reference& rSprite ) 397 { 398 if( !mpSpriteCanvas.get() ) 399 return; // we're disposed 400 401 if( mbActive ) 402 { 403 mpSpriteCanvas->hideSprite( rSprite ); 404 mbActive = false; 405 406 // TODO(P1): if clip is the NULL clip (nothing visible), 407 // also save us the update call. 408 409 if( mfAlpha != 0.0 ) 410 { 411 mpSpriteCanvas->updateSprite( rSprite, 412 maPosition, 413 getUpdateArea() ); 414 } 415 416 mbVisibilityDirty = true; 417 } 418 } 419 420 // Sprite interface 421 bool CanvasCustomSpriteHelper::isAreaUpdateOpaque( const ::basegfx::B2DRange& rUpdateArea ) const 422 { 423 if( !mbIsCurrClipRectangle || 424 !mbIsContentFullyOpaque || 425 !::rtl::math::approxEqual(mfAlpha, 1.0) ) 426 { 427 // sprite either transparent, or clip rect does not 428 // represent exact bounds -> update might not be fully 429 // opaque 430 return false; 431 } 432 else 433 { 434 // make sure sprite rect fully covers update area - 435 // although the update area originates from the sprite, 436 // it's by no means guaranteed that it's limited to this 437 // sprite's update area - after all, other sprites might 438 // have been merged, or this sprite is moving. 439 return getUpdateArea().isInside( rUpdateArea ); 440 } 441 } 442 443 ::basegfx::B2DPoint CanvasCustomSpriteHelper::getPosPixel() const 444 { 445 return maPosition; 446 } 447 448 ::basegfx::B2DVector CanvasCustomSpriteHelper::getSizePixel() const 449 { 450 return maSize; 451 } 452 453 ::basegfx::B2DRange CanvasCustomSpriteHelper::getUpdateArea( const ::basegfx::B2DRange& rBounds ) const 454 { 455 // Internal! Only call with locked object mutex! 456 ::basegfx::B2DHomMatrix aTransform( maTransform ); 457 aTransform.translate( maPosition.getX(), 458 maPosition.getY() ); 459 460 // transform bounds at origin, as the sprite transformation is 461 // formulated that way 462 ::basegfx::B2DRectangle aTransformedBounds; 463 return ::canvas::tools::calcTransformedRectBounds( aTransformedBounds, 464 rBounds, 465 aTransform ); 466 } 467 468 ::basegfx::B2DRange CanvasCustomSpriteHelper::getUpdateArea() const 469 { 470 // Internal! Only call with locked object mutex! 471 472 // return effective sprite rect, i.e. take active clip into 473 // account 474 if( maCurrClipBounds.isEmpty() ) 475 return getUpdateArea( ::basegfx::B2DRectangle( 0.0, 0.0, 476 maSize.getX(), 477 maSize.getY() ) ); 478 else 479 return ::basegfx::B2DRectangle( 480 maPosition + maCurrClipBounds.getMinimum(), 481 maPosition + maCurrClipBounds.getMaximum() ); 482 } 483 484 double CanvasCustomSpriteHelper::getPriority() const 485 { 486 return mfPriority; 487 } 488 489 ::basegfx::B2DRange CanvasCustomSpriteHelper::getFullSpriteRect() const 490 { 491 // Internal! Only call with locked object mutex! 492 return getUpdateArea( ::basegfx::B2DRectangle( 0.0, 0.0, 493 maSize.getX(), 494 maSize.getY() ) ); 495 } 496 } 497