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