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:
~SpriteInfo()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              */
SpriteInfo(const Sprite::Reference & rRef,const::basegfx::B2DRange & rTrueUpdateArea,bool bNeedsUpdate)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              */
SpriteInfo(const Sprite::Reference & rRef,const::basegfx::B2DRange & rTrueUpdateArea,bool bNeedsUpdate,bool bIsPureMove)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 
getSprite() const130             const Sprite::Reference&	getSprite() const { return mpSprite; }
131 
132             // #i61843# need to return by value here, to be used safely from bind
getUpdateArea() const133             ::basegfx::B2DRange         getUpdateArea() const { return maTrueUpdateArea; }
needsUpdate() const134             bool						needsUpdate() const { return mbNeedsUpdate; }
isPureMove() const135             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 
SpriteChangeRecordcanvas::SpriteRedrawManager::SpriteChangeRecord154             SpriteChangeRecord() :
155                 meChangeType( none ),
156                 mpAffectedSprite(),
157                 maOldPos(),
158                 maUpdateArea()
159             {
160             }
161 
SpriteChangeRecordcanvas::SpriteRedrawManager::SpriteChangeRecord162             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 
SpriteChangeRecordcanvas::SpriteRedrawManager::SpriteChangeRecord176             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 
getSpritecanvas::SpriteRedrawManager::SpriteChangeRecord186             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         {
AreaUpdateCallercanvas::SpriteRedrawManager::AreaUpdateCaller212             AreaUpdateCaller( Functor& 						rFunc,
213                               const SpriteRedrawManager&	rManager ) :
214                 mrFunc( rFunc ),
215                 mrManager( rManager )
216             {
217             }
218 
operator ()canvas::SpriteRedrawManager::AreaUpdateCaller219             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          */
forEachSpriteArea(Functor & rFunc) const255         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          */
forEachSprite(const Functor & rFunc) const274         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          */
handleArea(Functor & rFunc,const UpdateArea & rUpdateArea) const302         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