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 #ifndef INCLUDED_CANVAS_SPRITEREDRAWMANAGER_HXX 25 #define INCLUDED_CANVAS_SPRITEREDRAWMANAGER_HXX 26 27 #include <basegfx/range/b2dconnectedranges.hxx> 28 #include <basegfx/point/b2dpoint.hxx> 29 #include <basegfx/vector/b2dvector.hxx> 30 #include <basegfx/range/b2drange.hxx> 31 #include <basegfx/range/b2irange.hxx> 32 #include <basegfx/matrix/b2dhommatrix.hxx> 33 #include <canvas/base/spritesurface.hxx> 34 35 #include <list> 36 #include <vector> 37 #include <algorithm> 38 39 #include <boost/utility.hpp> 40 #include <boost/bind.hpp> 41 42 #include <canvas/canvastoolsdllapi.h> 43 44 /* Definition of SpriteRedrawManager class */ 45 46 namespace canvas 47 { 48 /** This class manages smooth SpriteCanvas updates 49 50 Use this class to handle the ::canvas::SpriteSurface methods, 51 that track and process sprite update events. Recorded update 52 events are later grouped by connected areas (i.e. all sprites 53 that somehow overlap over a rectangular area are grouped 54 together); the forEachSpriteArea() method calls the passed 55 functor for each of those connected areas. 56 57 Note that, although this class generally works with IEEE 58 doubles, the calculation of connected areas happens in the 59 integer domain - it is generally expected that repaints can 60 only be divided at pixel boundaries, without causing visible 61 artifacts. Therefore, sprites that touch the same pixel (but 62 don't necessarily have the same floating point coordinates 63 there) will reside in a common sprite area and handled 64 together in the forEachSpriteArea functor call. 65 */ 66 class CANVASTOOLS_DLLPUBLIC SpriteRedrawManager : private ::boost::noncopyable 67 { 68 public: 69 /** Data container for the connected components list 70 */ 71 class SpriteInfo 72 { 73 public: ~SpriteInfo()74 ~SpriteInfo() {} 75 76 /** Create sprite info 77 78 @param rRef 79 Sprite this info represents (might be the NULL ref) 80 81 @param rTrueUpdateArea 82 True (un-rounded) update area this sprite has recorded 83 84 @param bNeedsUpdate 85 When false, this sprite is not a member of the change 86 record list. Thus, it only needs redraw if within the 87 update area of other, changed sprites. 88 89 @internal 90 */ SpriteInfo(const Sprite::Reference & rRef,const::basegfx::B2DRange & rTrueUpdateArea,bool bNeedsUpdate)91 SpriteInfo( const Sprite::Reference& rRef, 92 const ::basegfx::B2DRange& rTrueUpdateArea, 93 bool bNeedsUpdate ) : 94 mpSprite( rRef ), 95 maTrueUpdateArea( rTrueUpdateArea ), 96 mbNeedsUpdate( bNeedsUpdate ), 97 mbIsPureMove( false ) 98 { 99 } 100 101 /** Create sprite info, specify move type 102 103 @param rRef 104 Sprite this info represents (might be the NULL ref) 105 106 @param rTrueUpdateArea 107 True (un-rounded) update area this sprite has recorded 108 109 @param bNeedsUpdate 110 When false, this sprite is not a member of the change 111 record list. Thus, it only needs redraw if within the 112 update area of other, changed sprites. 113 114 @param bIsPureMove 115 When true, this sprite is _only_ moved, no other 116 changes happened. 117 118 @internal 119 */ SpriteInfo(const Sprite::Reference & rRef,const::basegfx::B2DRange & rTrueUpdateArea,bool bNeedsUpdate,bool bIsPureMove)120 SpriteInfo( const Sprite::Reference& rRef, 121 const ::basegfx::B2DRange& rTrueUpdateArea, 122 bool bNeedsUpdate, 123 bool bIsPureMove ) : 124 mpSprite( rRef ), 125 maTrueUpdateArea( rTrueUpdateArea ), 126 mbNeedsUpdate( bNeedsUpdate ), 127 mbIsPureMove( bIsPureMove ) 128 { 129 } 130 getSprite() const131 const Sprite::Reference& getSprite() const { return mpSprite; } 132 133 // #i61843# need to return by value here, to be used safely from bind getUpdateArea() const134 ::basegfx::B2DRange getUpdateArea() const { return maTrueUpdateArea; } needsUpdate() const135 bool needsUpdate() const { return mbNeedsUpdate; } isPureMove() const136 bool isPureMove() const { return mbIsPureMove; } 137 138 private: 139 Sprite::Reference mpSprite; 140 ::basegfx::B2DRange maTrueUpdateArea; 141 bool mbNeedsUpdate; 142 bool mbIsPureMove; 143 }; 144 145 146 /** Helper struct for SpriteTracer template 147 148 This struct stores change information to a sprite's visual 149 appearance (move, content updated, and the like). 150 */ 151 struct SpriteChangeRecord 152 { 153 typedef enum{ none=0, move, update } ChangeType; 154 SpriteChangeRecordcanvas::SpriteRedrawManager::SpriteChangeRecord155 SpriteChangeRecord() : 156 meChangeType( none ), 157 mpAffectedSprite(), 158 maOldPos(), 159 maUpdateArea() 160 { 161 } 162 SpriteChangeRecordcanvas::SpriteRedrawManager::SpriteChangeRecord163 SpriteChangeRecord( const Sprite::Reference& rSprite, 164 const ::basegfx::B2DPoint& rOldPos, 165 const ::basegfx::B2DPoint& rNewPos, 166 const ::basegfx::B2DVector& rSpriteSize ) : 167 meChangeType( move ), 168 mpAffectedSprite( rSprite ), 169 maOldPos( rOldPos ), 170 maUpdateArea( rNewPos.getX(), 171 rNewPos.getY(), 172 rNewPos.getX() + rSpriteSize.getX(), 173 rNewPos.getY() + rSpriteSize.getY() ) 174 { 175 } 176 SpriteChangeRecordcanvas::SpriteRedrawManager::SpriteChangeRecord177 SpriteChangeRecord( const Sprite::Reference& rSprite, 178 const ::basegfx::B2DPoint& rPos, 179 const ::basegfx::B2DRange& rUpdateArea ) : 180 meChangeType( update ), 181 mpAffectedSprite( rSprite ), 182 maOldPos( rPos ), 183 maUpdateArea( rUpdateArea ) 184 { 185 } 186 getSpritecanvas::SpriteRedrawManager::SpriteChangeRecord187 Sprite::Reference getSprite() const { return mpAffectedSprite; } 188 189 ChangeType meChangeType; 190 Sprite::Reference mpAffectedSprite; 191 ::basegfx::B2DPoint maOldPos; 192 ::basegfx::B2DRange maUpdateArea; 193 }; 194 195 typedef ::std::vector< SpriteChangeRecord > VectorOfChangeRecords; 196 typedef ::std::list< Sprite::Reference > ListOfSprites; 197 typedef ::basegfx::B2DConnectedRanges< SpriteInfo > SpriteConnectedRanges; 198 typedef SpriteConnectedRanges::ComponentType AreaComponent; 199 typedef SpriteConnectedRanges::ConnectedComponents UpdateArea; 200 typedef ::std::vector< Sprite::Reference > VectorOfSprites; 201 202 SpriteRedrawManager(); 203 204 /** Must be called when user of this object gets 205 disposed. Frees all internal references. 206 */ 207 void disposing(); 208 209 /** Functor, to be used from forEachSpriteArea 210 */ 211 template< typename Functor > struct AreaUpdateCaller 212 { AreaUpdateCallercanvas::SpriteRedrawManager::AreaUpdateCaller213 AreaUpdateCaller( Functor& rFunc, 214 const SpriteRedrawManager& rManager ) : 215 mrFunc( rFunc ), 216 mrManager( rManager ) 217 { 218 } 219 operator ()canvas::SpriteRedrawManager::AreaUpdateCaller220 void operator()( const UpdateArea& rUpdateArea ) 221 { 222 mrManager.handleArea( mrFunc, rUpdateArea ); 223 } 224 225 Functor& mrFunc; 226 const SpriteRedrawManager& mrManager; 227 }; 228 229 /** Call given functor for each sprite area that needs an 230 update. 231 232 This method calls the given functor for each update area 233 (calculated from the sprite change records). 234 235 @tpl Functor 236 Must provide the following four methods: 237 <pre> 238 void backgroundPaint( ::basegfx::B2DRange aUpdateRect ); 239 void scrollUpdate( ::basegfx::B2DRange& o_rMoveStart, 240 ::basegfx::B2DRange& o_rMoveEnd, 241 UpdateArea aUpdateArea ); 242 void opaqueUpdate( const ::basegfx::B2DRange& rTotalArea, 243 const ::std::vector< ::canvas::Sprite::Reference >& rSortedUpdateSprites ); 244 void genericUpdate( const ::basegfx::B2DRange& rTotalArea, 245 const ::std::vector< ::canvas::Sprite::Reference >& rSortedUpdateSprites ); 246 </pre> 247 The backgroundPaint() method is called to simply repaint 248 background content, the scrollUpdate() method is used to 249 scroll a given area, and paint background in the uncovered 250 areas, the opaqueUpdate() method is called when a sprite 251 can be painted in the given area without taking background 252 content into account, and finally, genericUpdate() is 253 called for complex updates, where first the background and 254 then all sprites consecutively have to be repainted. 255 */ forEachSpriteArea(Functor & rFunc) const256 template< typename Functor > void forEachSpriteArea( Functor& rFunc ) const 257 { 258 SpriteConnectedRanges aUpdateAreas; 259 260 setupUpdateAreas( aUpdateAreas ); 261 262 aUpdateAreas.forEachAggregate( 263 AreaUpdateCaller< Functor >( rFunc, *this ) ); 264 } 265 266 /** Call given functor for each active sprite. 267 268 This method calls the given functor for each active 269 sprite, in the order of sprite priority. 270 271 @tpl Functor 272 Must provide a Functor::operator( Sprite::Reference& ) 273 method. 274 */ forEachSprite(const Functor & rFunc) const275 template< typename Functor > void forEachSprite( const Functor& rFunc ) const 276 { 277 ::std::for_each( maSprites.begin(), 278 maSprites.end(), 279 rFunc ); 280 } 281 282 /// Clear sprite change records (typically directly after a screen update) 283 void clearChangeRecords(); 284 285 // SpriteSurface interface, is delegated to e.g. from SpriteCanvas 286 void showSprite( const Sprite::Reference& rSprite ); 287 void hideSprite( const Sprite::Reference& rSprite ); 288 void moveSprite( const Sprite::Reference& rSprite, 289 const ::basegfx::B2DPoint& rOldPos, 290 const ::basegfx::B2DPoint& rNewPos, 291 const ::basegfx::B2DVector& rSpriteSize ); 292 void updateSprite( const Sprite::Reference& rSprite, 293 const ::basegfx::B2DPoint& rPos, 294 const ::basegfx::B2DRange& rUpdateArea ); 295 296 /** Internal, handles each distinct component for forEachAggregate() 297 298 The reason why this must be public is that it needs to be 299 accessible from the AreaUpdateCaller functor. 300 301 @internal 302 */ handleArea(Functor & rFunc,const UpdateArea & rUpdateArea) const303 template< typename Functor > void handleArea( Functor& rFunc, 304 const UpdateArea& rUpdateArea ) const 305 { 306 // check whether this area contains changed sprites at all 307 // (if not, just ignore it) 308 if( areSpritesChanged( rUpdateArea ) ) 309 { 310 // at least one of the sprites actually needs an 311 // update - process whole area. 312 313 // check whether this area could be handled special 314 // (background paint, direct update, scroll, etc.) 315 ::basegfx::B2DRange aMoveStart; 316 ::basegfx::B2DRange aMoveEnd; 317 if( rUpdateArea.maComponentList.empty() ) 318 { 319 rFunc.backgroundPaint( rUpdateArea.maTotalBounds ); 320 } 321 else 322 { 323 // cache number of sprites in this area (it's a 324 // list, and both isAreaUpdateScroll() and 325 // isAreaUpdateOpaque() need it). 326 const ::std::size_t nNumSprites( 327 rUpdateArea.maComponentList.size() ); 328 329 if( isAreaUpdateScroll( aMoveStart, 330 aMoveEnd, 331 rUpdateArea, 332 nNumSprites ) ) 333 { 334 rFunc.scrollUpdate( aMoveStart, 335 aMoveEnd, 336 rUpdateArea ); 337 } 338 else 339 { 340 // potentially, more than a single sprite 341 // involved. Have to sort component lists for 342 // sprite prio. 343 VectorOfSprites aSortedUpdateSprites; 344 SpriteConnectedRanges::ComponentListType::const_iterator aCurr( 345 rUpdateArea.maComponentList.begin() ); 346 const SpriteConnectedRanges::ComponentListType::const_iterator aEnd( 347 rUpdateArea.maComponentList.end() ); 348 while( aCurr != aEnd ) 349 { 350 const Sprite::Reference& rSprite( aCurr->second.getSprite() ); 351 if( rSprite.is() ) 352 aSortedUpdateSprites.push_back( rSprite ); 353 354 ++aCurr; 355 } 356 357 ::std::sort( aSortedUpdateSprites.begin(), 358 aSortedUpdateSprites.end(), 359 SpriteComparator() ); 360 361 if( isAreaUpdateOpaque( rUpdateArea, 362 nNumSprites ) ) 363 { 364 rFunc.opaqueUpdate( rUpdateArea.maTotalBounds, 365 aSortedUpdateSprites ); 366 } 367 else 368 { 369 rFunc.genericUpdate( rUpdateArea.maTotalBounds, 370 aSortedUpdateSprites ); 371 } 372 } 373 } 374 } 375 } 376 377 private: 378 /** Central method of this class. Calculates the set of 379 disjunct components that need an update. 380 */ 381 void setupUpdateAreas( SpriteConnectedRanges& rUpdateAreas ) const; 382 383 bool areSpritesChanged( const UpdateArea& rUpdateArea ) const; 384 385 bool isAreaUpdateNotOpaque( const ::basegfx::B2DRange& rUpdateRect, 386 const AreaComponent& rComponent ) const; 387 388 bool isAreaUpdateOpaque( const UpdateArea& rUpdateArea, 389 ::std::size_t nNumSprites ) const; 390 391 /** Check whether given update area can be handled by a simple 392 scroll 393 394 @param o_rMoveStart 395 Start rect of the move 396 397 @param o_rMoveEnd 398 End rect of the move. The content must be moved from start 399 to end rect 400 401 @param rUpdateArea 402 Area to check for scroll update optimization 403 */ 404 bool isAreaUpdateScroll( ::basegfx::B2DRange& o_rMoveStart, 405 ::basegfx::B2DRange& o_rMoveEnd, 406 const UpdateArea& rUpdateArea, 407 ::std::size_t nNumSprites ) const; 408 409 410 ListOfSprites maSprites; // list of active 411 // sprite 412 // objects. this 413 // list is only 414 // used for full 415 // repaints, 416 // otherwise, we 417 // rely on the 418 // active sprites 419 // itself to notify 420 // us. 421 422 VectorOfChangeRecords maChangeRecords; // vector of 423 // sprites 424 // changes 425 // since last 426 // updateScreen() 427 // call 428 }; 429 } 430 431 #endif /* INCLUDED_CANVAS_SPRITEREDRAWMANAGER_HXX */ 432