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