1*25ea7f45SAndrew Rist /**************************************************************
2cdf0e10cSrcweir  *
3*25ea7f45SAndrew Rist  * Licensed to the Apache Software Foundation (ASF) under one
4*25ea7f45SAndrew Rist  * or more contributor license agreements.  See the NOTICE file
5*25ea7f45SAndrew Rist  * distributed with this work for additional information
6*25ea7f45SAndrew Rist  * regarding copyright ownership.  The ASF licenses this file
7*25ea7f45SAndrew Rist  * to you under the Apache License, Version 2.0 (the
8*25ea7f45SAndrew Rist  * "License"); you may not use this file except in compliance
9*25ea7f45SAndrew Rist  * with the License.  You may obtain a copy of the License at
10*25ea7f45SAndrew Rist  *
11*25ea7f45SAndrew Rist  *   http://www.apache.org/licenses/LICENSE-2.0
12*25ea7f45SAndrew Rist  *
13*25ea7f45SAndrew Rist  * Unless required by applicable law or agreed to in writing,
14*25ea7f45SAndrew Rist  * software distributed under the License is distributed on an
15*25ea7f45SAndrew Rist  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16*25ea7f45SAndrew Rist  * KIND, either express or implied.  See the License for the
17*25ea7f45SAndrew Rist  * specific language governing permissions and limitations
18*25ea7f45SAndrew Rist  * under the License.
19*25ea7f45SAndrew Rist  *
20*25ea7f45SAndrew Rist  *************************************************************/
21*25ea7f45SAndrew Rist 
22*25ea7f45SAndrew Rist 
23cdf0e10cSrcweir 
24cdf0e10cSrcweir // MARKER(update_precomp.py): autogen include statement, do not remove
25cdf0e10cSrcweir #include "precompiled_canvas.hxx"
26cdf0e10cSrcweir 
27cdf0e10cSrcweir #include <canvas/debug.hxx>
28cdf0e10cSrcweir #include <tools/diagnose_ex.h>
29cdf0e10cSrcweir #include <canvas/spriteredrawmanager.hxx>
30cdf0e10cSrcweir 
31cdf0e10cSrcweir #include <basegfx/range/b2drectangle.hxx>
32cdf0e10cSrcweir #include <basegfx/tools/canvastools.hxx>
33cdf0e10cSrcweir #include <basegfx/vector/b2dsize.hxx>
34cdf0e10cSrcweir #include <basegfx/range/rangeexpander.hxx>
35cdf0e10cSrcweir 
36cdf0e10cSrcweir #include <algorithm>
37cdf0e10cSrcweir #include <functional>
38cdf0e10cSrcweir #include <boost/bind.hpp>
39cdf0e10cSrcweir 
40cdf0e10cSrcweir 
41cdf0e10cSrcweir namespace canvas
42cdf0e10cSrcweir {
43cdf0e10cSrcweir     namespace
44cdf0e10cSrcweir     {
45cdf0e10cSrcweir         /** Helper class to condense sprite updates into a single action
46cdf0e10cSrcweir 
47cdf0e10cSrcweir         	This class tracks the sprite changes over the recorded
48cdf0e10cSrcweir         	change list, and generates a single update action from
49cdf0e10cSrcweir         	that (note that per screen update, several moves,
50cdf0e10cSrcweir         	visibility changes and content updates might happen)
51cdf0e10cSrcweir          */
52cdf0e10cSrcweir         class SpriteTracer
53cdf0e10cSrcweir         {
54cdf0e10cSrcweir         public:
SpriteTracer(const Sprite::Reference & rAffectedSprite)55cdf0e10cSrcweir             SpriteTracer( const Sprite::Reference& rAffectedSprite ) :
56cdf0e10cSrcweir                 mpAffectedSprite(rAffectedSprite),
57cdf0e10cSrcweir                 maMoveStartArea(),
58cdf0e10cSrcweir                 maMoveEndArea(),
59cdf0e10cSrcweir                 mbIsMove( false ),
60cdf0e10cSrcweir                 mbIsGenericUpdate( false )
61cdf0e10cSrcweir             {
62cdf0e10cSrcweir             }
63cdf0e10cSrcweir 
operator ()(const SpriteRedrawManager::SpriteChangeRecord & rSpriteRecord)64cdf0e10cSrcweir             void operator()( const SpriteRedrawManager::SpriteChangeRecord& rSpriteRecord )
65cdf0e10cSrcweir             {
66cdf0e10cSrcweir                 // only deal with change events from the currently
67cdf0e10cSrcweir                 // affected sprite
68cdf0e10cSrcweir                 if( rSpriteRecord.mpAffectedSprite == mpAffectedSprite )
69cdf0e10cSrcweir                 {
70cdf0e10cSrcweir                     switch( rSpriteRecord.meChangeType )
71cdf0e10cSrcweir                     {
72cdf0e10cSrcweir                         case SpriteRedrawManager::SpriteChangeRecord::move:
73cdf0e10cSrcweir                             if( !mbIsMove )
74cdf0e10cSrcweir                             {
75cdf0e10cSrcweir                                 // no move yet - this must be the first one
76cdf0e10cSrcweir                                 maMoveStartArea = ::basegfx::B2DRectangle(
77cdf0e10cSrcweir                                     rSpriteRecord.maOldPos,
78cdf0e10cSrcweir                                     rSpriteRecord.maOldPos + rSpriteRecord.maUpdateArea.getRange() );
79cdf0e10cSrcweir                                 mbIsMove	 	= true;
80cdf0e10cSrcweir                             }
81cdf0e10cSrcweir 
82cdf0e10cSrcweir                             maMoveEndArea	= rSpriteRecord.maUpdateArea;
83cdf0e10cSrcweir                             break;
84cdf0e10cSrcweir 
85cdf0e10cSrcweir                         case SpriteRedrawManager::SpriteChangeRecord::update:
86cdf0e10cSrcweir                             // update end update area of the
87cdf0e10cSrcweir                             // sprite. Thus, every update() action
88cdf0e10cSrcweir                             // _after_ the last move will correctly
89cdf0e10cSrcweir                             // update the final repaint area. And this
90cdf0e10cSrcweir                             // does not interfere with subsequent
91cdf0e10cSrcweir                             // moves, because moves always perform a
92cdf0e10cSrcweir                             // hard set of maMoveEndArea to their
93cdf0e10cSrcweir                             // stored value
94cdf0e10cSrcweir                             maMoveEndArea.expand( rSpriteRecord.maUpdateArea );
95cdf0e10cSrcweir                             mbIsGenericUpdate = true;
96cdf0e10cSrcweir                             break;
97cdf0e10cSrcweir 
98cdf0e10cSrcweir                         default:
99cdf0e10cSrcweir                             ENSURE_OR_THROW( false,
100cdf0e10cSrcweir                                               "Unexpected case in SpriteUpdater::operator()" );
101cdf0e10cSrcweir                             break;
102cdf0e10cSrcweir                     }
103cdf0e10cSrcweir                 }
104cdf0e10cSrcweir             }
105cdf0e10cSrcweir 
commit(SpriteRedrawManager::SpriteConnectedRanges & rUpdateCollector) const106cdf0e10cSrcweir             void commit( SpriteRedrawManager::SpriteConnectedRanges& rUpdateCollector ) const
107cdf0e10cSrcweir             {
108cdf0e10cSrcweir                 if( mbIsMove )
109cdf0e10cSrcweir                 {
110cdf0e10cSrcweir                     if( !maMoveStartArea.isEmpty() ||
111cdf0e10cSrcweir                         !maMoveEndArea.isEmpty() )
112cdf0e10cSrcweir                     {
113cdf0e10cSrcweir                         // if mbIsGenericUpdate is false, this is a
114cdf0e10cSrcweir                         // pure move (i.e. no other update
115cdf0e10cSrcweir                         // operations). Pass that information on to
116cdf0e10cSrcweir                         // the SpriteInfo
117cdf0e10cSrcweir                         const bool bIsPureMove( !mbIsGenericUpdate );
118cdf0e10cSrcweir 
119cdf0e10cSrcweir                         // ignore the case that start and end update
120cdf0e10cSrcweir                         // area overlap - the b2dconnectedranges
121cdf0e10cSrcweir                         // handle that, anyway. doing it this way
122cdf0e10cSrcweir                         // ensures that we have both old and new area
123cdf0e10cSrcweir                         // stored
124cdf0e10cSrcweir 
125cdf0e10cSrcweir                         // round all given range up to enclosing
126cdf0e10cSrcweir                         // integer rectangle - since the whole thing
127cdf0e10cSrcweir                         // here is about
128cdf0e10cSrcweir 
129cdf0e10cSrcweir                         // first, draw the new sprite position
130cdf0e10cSrcweir                         rUpdateCollector.addRange(
131cdf0e10cSrcweir                             ::basegfx::unotools::b2DSurroundingIntegerRangeFromB2DRange( maMoveEndArea ),
132cdf0e10cSrcweir                             SpriteRedrawManager::SpriteInfo(
133cdf0e10cSrcweir                                 mpAffectedSprite,
134cdf0e10cSrcweir                                 maMoveEndArea,
135cdf0e10cSrcweir                                 true,
136cdf0e10cSrcweir                                 bIsPureMove ) );
137cdf0e10cSrcweir 
138cdf0e10cSrcweir                         // then, clear the old place (looks smoother
139cdf0e10cSrcweir                         // this way)
140cdf0e10cSrcweir                         rUpdateCollector.addRange(
141cdf0e10cSrcweir                             ::basegfx::unotools::b2DSurroundingIntegerRangeFromB2DRange( maMoveStartArea ),
142cdf0e10cSrcweir                             SpriteRedrawManager::SpriteInfo(
143cdf0e10cSrcweir                                 Sprite::Reference(),
144cdf0e10cSrcweir                                 maMoveStartArea,
145cdf0e10cSrcweir                                 true,
146cdf0e10cSrcweir                                 bIsPureMove ) );
147cdf0e10cSrcweir                     }
148cdf0e10cSrcweir                 }
149cdf0e10cSrcweir                 else if( mbIsGenericUpdate &&
150cdf0e10cSrcweir                          !maMoveEndArea.isEmpty() )
151cdf0e10cSrcweir                 {
152cdf0e10cSrcweir                     rUpdateCollector.addRange(
153cdf0e10cSrcweir                         ::basegfx::unotools::b2DSurroundingIntegerRangeFromB2DRange( maMoveEndArea ),
154cdf0e10cSrcweir                         SpriteRedrawManager::SpriteInfo(
155cdf0e10cSrcweir                             mpAffectedSprite,
156cdf0e10cSrcweir                             maMoveEndArea,
157cdf0e10cSrcweir                             true ) );
158cdf0e10cSrcweir                 }
159cdf0e10cSrcweir             }
160cdf0e10cSrcweir 
161cdf0e10cSrcweir         private:
162cdf0e10cSrcweir             Sprite::Reference		mpAffectedSprite;
163cdf0e10cSrcweir             ::basegfx::B2DRectangle	maMoveStartArea;
164cdf0e10cSrcweir             ::basegfx::B2DRectangle	maMoveEndArea;
165cdf0e10cSrcweir 
166cdf0e10cSrcweir             /// True, if at least one move was encountered
167cdf0e10cSrcweir             bool					mbIsMove;
168cdf0e10cSrcweir 
169cdf0e10cSrcweir             /// True, if at least one generic update was encountered
170cdf0e10cSrcweir             bool					mbIsGenericUpdate;
171cdf0e10cSrcweir         };
172cdf0e10cSrcweir 
173cdf0e10cSrcweir 
174cdf0e10cSrcweir         /** SpriteChecker functor, which for every sprite checks the
175cdf0e10cSrcweir             given update vector for necessary screen updates
176cdf0e10cSrcweir          */
177cdf0e10cSrcweir         class SpriteUpdater
178cdf0e10cSrcweir         {
179cdf0e10cSrcweir         public:
180cdf0e10cSrcweir             /** Generate update area list
181cdf0e10cSrcweir 
182cdf0e10cSrcweir             	@param rUpdater
183cdf0e10cSrcweir                 Reference to an updater object, which will receive the
184cdf0e10cSrcweir                 update areas.
185cdf0e10cSrcweir 
186cdf0e10cSrcweir                 @param rChangeContainer
187cdf0e10cSrcweir                 Container with all sprite change requests
188cdf0e10cSrcweir 
189cdf0e10cSrcweir              */
SpriteUpdater(SpriteRedrawManager::SpriteConnectedRanges & rUpdater,const SpriteRedrawManager::VectorOfChangeRecords & rChangeContainer)190cdf0e10cSrcweir             SpriteUpdater( SpriteRedrawManager::SpriteConnectedRanges&			rUpdater,
191cdf0e10cSrcweir                            const SpriteRedrawManager::VectorOfChangeRecords&	rChangeContainer ) :
192cdf0e10cSrcweir                 mrUpdater( rUpdater ),
193cdf0e10cSrcweir                 mrChangeContainer( rChangeContainer )
194cdf0e10cSrcweir             {
195cdf0e10cSrcweir             }
196cdf0e10cSrcweir 
197cdf0e10cSrcweir             /** Call this method for every sprite on your screen
198cdf0e10cSrcweir 
199cdf0e10cSrcweir             	This method scans the change container, collecting all
200cdf0e10cSrcweir             	update info for the given sprite into one or two
201cdf0e10cSrcweir             	update operations, which in turn are inserted into the
202cdf0e10cSrcweir             	connected ranges processor.
203cdf0e10cSrcweir 
204cdf0e10cSrcweir                 @param rSprite
205cdf0e10cSrcweir                 Current sprite to collect update info for.
206cdf0e10cSrcweir              */
operator ()(const Sprite::Reference & rSprite)207cdf0e10cSrcweir             void operator()( const Sprite::Reference& rSprite )
208cdf0e10cSrcweir             {
209cdf0e10cSrcweir                 const SpriteTracer aSpriteTracer(
210cdf0e10cSrcweir                     ::std::for_each( mrChangeContainer.begin(),
211cdf0e10cSrcweir                                      mrChangeContainer.end(),
212cdf0e10cSrcweir                                      SpriteTracer( rSprite ) ) );
213cdf0e10cSrcweir 
214cdf0e10cSrcweir                 aSpriteTracer.commit( mrUpdater );
215cdf0e10cSrcweir             }
216cdf0e10cSrcweir 
217cdf0e10cSrcweir         private:
218cdf0e10cSrcweir             SpriteRedrawManager::SpriteConnectedRanges&			mrUpdater;
219cdf0e10cSrcweir             const SpriteRedrawManager::VectorOfChangeRecords&	mrChangeContainer;
220cdf0e10cSrcweir         };
221cdf0e10cSrcweir     }
222cdf0e10cSrcweir 
setupUpdateAreas(SpriteConnectedRanges & rUpdateAreas) const223cdf0e10cSrcweir     void SpriteRedrawManager::setupUpdateAreas( SpriteConnectedRanges& rUpdateAreas ) const
224cdf0e10cSrcweir     {
225cdf0e10cSrcweir         // TODO(T3): This is NOT thread safe at all. This only works
226cdf0e10cSrcweir         // under the assumption that NOBODY changes ANYTHING
227cdf0e10cSrcweir         // concurrently, while this method is on the stack. We should
228cdf0e10cSrcweir         // really rework the canvas::Sprite interface, in such a way
229cdf0e10cSrcweir         // that it dumps ALL its state with a single, atomic
230cdf0e10cSrcweir         // call. Then, we store that state locally. This prolly goes
231cdf0e10cSrcweir         // in line with the problem of having sprite state available
232cdf0e10cSrcweir         // for the frame before the last frame; plus, it avoids
233cdf0e10cSrcweir         // frequent locks of the object mutices
234cdf0e10cSrcweir         SpriteComparator aSpriteComparator;
235cdf0e10cSrcweir 
236cdf0e10cSrcweir         // put all sprites that have changed content into update areas
237cdf0e10cSrcweir         ListOfSprites::const_iterator       aCurrSprite( maSprites.begin() );
238cdf0e10cSrcweir         const ListOfSprites::const_iterator aEndSprite ( maSprites.end() );
239cdf0e10cSrcweir         while( aCurrSprite != aEndSprite )
240cdf0e10cSrcweir         {
241cdf0e10cSrcweir             if( (*aCurrSprite)->isContentChanged() )
242cdf0e10cSrcweir                 const_cast<SpriteRedrawManager*>(this)->updateSprite( *aCurrSprite,
243cdf0e10cSrcweir                                                                       (*aCurrSprite)->getPosPixel(),
244cdf0e10cSrcweir                                                                       (*aCurrSprite)->getUpdateArea() );
245cdf0e10cSrcweir             ++aCurrSprite;
246cdf0e10cSrcweir         }
247cdf0e10cSrcweir 
248cdf0e10cSrcweir         // sort sprites after prio
249cdf0e10cSrcweir         VectorOfSprites aSortedSpriteVector;
250cdf0e10cSrcweir         ::std::copy( maSprites.begin(),
251cdf0e10cSrcweir                      maSprites.end(),
252cdf0e10cSrcweir                      ::std::back_insert_iterator< VectorOfSprites >(aSortedSpriteVector) );
253cdf0e10cSrcweir         ::std::sort( aSortedSpriteVector.begin(),
254cdf0e10cSrcweir                      aSortedSpriteVector.end(),
255cdf0e10cSrcweir                      aSpriteComparator );
256cdf0e10cSrcweir 
257cdf0e10cSrcweir         // extract all referenced sprites from the maChangeRecords
258cdf0e10cSrcweir         // (copy sprites, make the list unique, regarding the
259cdf0e10cSrcweir         // sprite pointer). This assumes that, until this scope
260cdf0e10cSrcweir         // ends, nobody changes the maChangeRecords vector!
261cdf0e10cSrcweir         VectorOfSprites aUpdatableSprites;
262cdf0e10cSrcweir         VectorOfChangeRecords::const_iterator       aCurrRecord( maChangeRecords.begin() );
263cdf0e10cSrcweir         const VectorOfChangeRecords::const_iterator aEndRecords( maChangeRecords.end() );
264cdf0e10cSrcweir         while( aCurrRecord != aEndRecords )
265cdf0e10cSrcweir         {
266cdf0e10cSrcweir             const Sprite::Reference& rSprite( aCurrRecord->getSprite() );
267cdf0e10cSrcweir             if( rSprite.is() )
268cdf0e10cSrcweir                 aUpdatableSprites.push_back( rSprite );
269cdf0e10cSrcweir             ++aCurrRecord;
270cdf0e10cSrcweir         }
271cdf0e10cSrcweir 
272cdf0e10cSrcweir         VectorOfSprites::iterator aBegin( aUpdatableSprites.begin() );
273cdf0e10cSrcweir         VectorOfSprites::iterator aEnd  ( aUpdatableSprites.end() );
274cdf0e10cSrcweir         ::std::sort( aBegin,
275cdf0e10cSrcweir                      aEnd,
276cdf0e10cSrcweir                      aSpriteComparator );
277cdf0e10cSrcweir 
278cdf0e10cSrcweir         aEnd = ::std::unique( aBegin, aEnd );
279cdf0e10cSrcweir 
280cdf0e10cSrcweir         // for each unique sprite, check the change event vector,
281cdf0e10cSrcweir         // calculate the update operation from that, and add the
282cdf0e10cSrcweir         // result to the aUpdateArea.
283cdf0e10cSrcweir         ::std::for_each( aBegin,
284cdf0e10cSrcweir                          aEnd,
285cdf0e10cSrcweir                          SpriteUpdater( rUpdateAreas,
286cdf0e10cSrcweir                                         maChangeRecords) );
287cdf0e10cSrcweir 
288cdf0e10cSrcweir         // TODO(P2): Implement your own output iterator adapter, to
289cdf0e10cSrcweir         // avoid that totally superfluous temp aUnchangedSprites
290cdf0e10cSrcweir         // vector.
291cdf0e10cSrcweir 
292cdf0e10cSrcweir         // add all sprites to rUpdateAreas, that are _not_ already
293cdf0e10cSrcweir         // contained in the uniquified vector of changed ones
294cdf0e10cSrcweir         // (i.e. the difference between aSortedSpriteVector and
295cdf0e10cSrcweir         // aUpdatableSprites).
296cdf0e10cSrcweir         VectorOfSprites aUnchangedSprites;
297cdf0e10cSrcweir         ::std::set_difference( aSortedSpriteVector.begin(),
298cdf0e10cSrcweir                                aSortedSpriteVector.end(),
299cdf0e10cSrcweir                                aBegin, aEnd,
300cdf0e10cSrcweir                                ::std::back_insert_iterator< VectorOfSprites >(aUnchangedSprites) );
301cdf0e10cSrcweir 
302cdf0e10cSrcweir         // add each remaining unchanged sprite to connected ranges,
303cdf0e10cSrcweir         // marked as "don't need update"
304cdf0e10cSrcweir         VectorOfSprites::const_iterator 		aCurr( aUnchangedSprites.begin() );
305cdf0e10cSrcweir         const VectorOfSprites::const_iterator	aEnd2( aUnchangedSprites.end() );
306cdf0e10cSrcweir         while( aCurr != aEnd2 )
307cdf0e10cSrcweir         {
308cdf0e10cSrcweir             const ::basegfx::B2DRange& rUpdateArea( (*aCurr)->getUpdateArea() );
309cdf0e10cSrcweir             rUpdateAreas.addRange(
310cdf0e10cSrcweir                 ::basegfx::unotools::b2DSurroundingIntegerRangeFromB2DRange( rUpdateArea ),
311cdf0e10cSrcweir                 SpriteInfo(*aCurr,
312cdf0e10cSrcweir                            rUpdateArea,
313cdf0e10cSrcweir                            false) );
314cdf0e10cSrcweir             ++aCurr;
315cdf0e10cSrcweir         }
316cdf0e10cSrcweir     }
317cdf0e10cSrcweir 
318cdf0e10cSrcweir #if OSL_DEBUG_LEVEL > 0
impIsEqualB2DRange(const basegfx::B2DRange & rRangeA,const basegfx::B2DRange & rRangeB,double fSmallValue)319cdf0e10cSrcweir     bool impIsEqualB2DRange(const basegfx::B2DRange& rRangeA, const basegfx::B2DRange& rRangeB, double fSmallValue)
320cdf0e10cSrcweir     {
321cdf0e10cSrcweir         return fabs(rRangeB.getMinX() - rRangeA.getMinX()) <= fSmallValue
322cdf0e10cSrcweir             && fabs(rRangeB.getMinY() - rRangeA.getMinY()) <= fSmallValue
323cdf0e10cSrcweir             && fabs(rRangeB.getMaxX() - rRangeA.getMaxX()) <= fSmallValue
324cdf0e10cSrcweir             && fabs(rRangeB.getMaxY() - rRangeA.getMaxY()) <= fSmallValue;
325cdf0e10cSrcweir     }
326cdf0e10cSrcweir 
impIsEqualB2DVector(const basegfx::B2DVector & rVecA,const basegfx::B2DVector & rVecB,double fSmallValue)327cdf0e10cSrcweir     bool impIsEqualB2DVector(const basegfx::B2DVector& rVecA, const basegfx::B2DVector& rVecB, double fSmallValue)
328cdf0e10cSrcweir     {
329cdf0e10cSrcweir         return fabs(rVecB.getX() - rVecA.getX()) <= fSmallValue
330cdf0e10cSrcweir             && fabs(rVecB.getY() - rVecA.getY()) <= fSmallValue;
331cdf0e10cSrcweir     }
332cdf0e10cSrcweir #endif
333cdf0e10cSrcweir 
isAreaUpdateScroll(::basegfx::B2DRectangle & o_rMoveStart,::basegfx::B2DRectangle & o_rMoveEnd,const UpdateArea & rUpdateArea,::std::size_t nNumSprites) const334cdf0e10cSrcweir     bool SpriteRedrawManager::isAreaUpdateScroll( ::basegfx::B2DRectangle& 	o_rMoveStart,
335cdf0e10cSrcweir                                                   ::basegfx::B2DRectangle& 	o_rMoveEnd,
336cdf0e10cSrcweir                                                   const UpdateArea& 		rUpdateArea,
337cdf0e10cSrcweir                                                   ::std::size_t				nNumSprites ) const
338cdf0e10cSrcweir     {
339cdf0e10cSrcweir         // check for a solitary move, which consists of exactly two
340cdf0e10cSrcweir         // pure-move entries, the first with valid, the second with
341cdf0e10cSrcweir         // invalid sprite (see SpriteTracer::commit()).  Note that we
342cdf0e10cSrcweir         // cannot simply store some flag in SpriteTracer::commit()
343cdf0e10cSrcweir         // above and just check that here, since during the connected
344cdf0e10cSrcweir         // range calculations, other sprites might get merged into the
345cdf0e10cSrcweir         // same region (thus spoiling the scrolling move
346cdf0e10cSrcweir         // optimization).
347cdf0e10cSrcweir         if( nNumSprites != 2 )
348cdf0e10cSrcweir             return false;
349cdf0e10cSrcweir 
350cdf0e10cSrcweir         const SpriteConnectedRanges::ComponentListType::const_iterator aFirst(
351cdf0e10cSrcweir             rUpdateArea.maComponentList.begin() );
352cdf0e10cSrcweir         SpriteConnectedRanges::ComponentListType::const_iterator aSecond(
353cdf0e10cSrcweir             aFirst ); ++aSecond;
354cdf0e10cSrcweir 
355cdf0e10cSrcweir         if( !aFirst->second.isPureMove() ||
356cdf0e10cSrcweir             !aSecond->second.isPureMove() ||
357cdf0e10cSrcweir             !aFirst->second.getSprite().is() ||
358cdf0e10cSrcweir             // use _true_ update area, not the rounded version
359cdf0e10cSrcweir             !aFirst->second.getSprite()->isAreaUpdateOpaque( aFirst->second.getUpdateArea() ) ||
360cdf0e10cSrcweir             aSecond->second.getSprite().is() )
361cdf0e10cSrcweir         {
362cdf0e10cSrcweir             // either no move update, or incorrect sprite, or sprite
363cdf0e10cSrcweir             // content not fully opaque over update region.
364cdf0e10cSrcweir             return false;
365cdf0e10cSrcweir         }
366cdf0e10cSrcweir 
367cdf0e10cSrcweir         o_rMoveStart 	  = aSecond->second.getUpdateArea();
368cdf0e10cSrcweir         o_rMoveEnd   	  = aFirst->second.getUpdateArea();
369cdf0e10cSrcweir 
370cdf0e10cSrcweir #if OSL_DEBUG_LEVEL > 0
371cdf0e10cSrcweir         ::basegfx::B2DRectangle aTotalBounds( o_rMoveStart );
372cdf0e10cSrcweir         aTotalBounds.expand( o_rMoveEnd );
373cdf0e10cSrcweir 
374cdf0e10cSrcweir         OSL_POSTCOND(impIsEqualB2DRange(rUpdateArea.maTotalBounds, basegfx::unotools::b2DSurroundingIntegerRangeFromB2DRange(aTotalBounds), 0.5),
375cdf0e10cSrcweir             "SpriteRedrawManager::isAreaUpdateScroll(): sprite area and total area mismatch");
376cdf0e10cSrcweir         OSL_POSTCOND(impIsEqualB2DVector(o_rMoveStart.getRange(), o_rMoveEnd.getRange(), 0.5),
377cdf0e10cSrcweir             "SpriteRedrawManager::isAreaUpdateScroll(): scroll start and end area have mismatching size");
378cdf0e10cSrcweir #endif
379cdf0e10cSrcweir 
380cdf0e10cSrcweir         return true;
381cdf0e10cSrcweir     }
382cdf0e10cSrcweir 
isAreaUpdateNotOpaque(const::basegfx::B2DRectangle & rUpdateRect,const AreaComponent & rComponent) const383cdf0e10cSrcweir     bool SpriteRedrawManager::isAreaUpdateNotOpaque( const ::basegfx::B2DRectangle& rUpdateRect,
384cdf0e10cSrcweir                                                      const AreaComponent&			rComponent ) const
385cdf0e10cSrcweir     {
386cdf0e10cSrcweir         const Sprite::Reference& pAffectedSprite( rComponent.second.getSprite() );
387cdf0e10cSrcweir 
388cdf0e10cSrcweir         if( !pAffectedSprite.is() )
389cdf0e10cSrcweir             return true; // no sprite, no opaque update!
390cdf0e10cSrcweir 
391cdf0e10cSrcweir         return !pAffectedSprite->isAreaUpdateOpaque( rUpdateRect );
392cdf0e10cSrcweir     }
393cdf0e10cSrcweir 
isAreaUpdateOpaque(const UpdateArea & rUpdateArea,::std::size_t nNumSprites) const394cdf0e10cSrcweir     bool SpriteRedrawManager::isAreaUpdateOpaque( const UpdateArea&	rUpdateArea,
395cdf0e10cSrcweir                                                   ::std::size_t		nNumSprites ) const
396cdf0e10cSrcweir     {
397cdf0e10cSrcweir         // check whether the sprites in the update area's list will
398cdf0e10cSrcweir         // fully cover the given area _and_ do that in an opaque way
399cdf0e10cSrcweir         // (i.e. no alpha, no non-rectangular sprite content).
400cdf0e10cSrcweir 
401cdf0e10cSrcweir         // TODO(P1): Come up with a smarter early-exit criterion here
402cdf0e10cSrcweir         // (though, I think, the case that _lots_ of sprites _fully_
403cdf0e10cSrcweir         // cover a rectangular area _without_ any holes is extremely
404cdf0e10cSrcweir         // improbable)
405cdf0e10cSrcweir 
406cdf0e10cSrcweir         // avoid checking large number of sprites (and probably fail,
407cdf0e10cSrcweir         // anyway). Note: the case nNumSprites < 1 should normally not
408cdf0e10cSrcweir         // happen, as handleArea() calls backgroundPaint() then.
409cdf0e10cSrcweir         if( nNumSprites > 3 || nNumSprites < 1 )
410cdf0e10cSrcweir             return false;
411cdf0e10cSrcweir 
412cdf0e10cSrcweir         const SpriteConnectedRanges::ComponentListType::const_iterator aBegin(
413cdf0e10cSrcweir             rUpdateArea.maComponentList.begin() );
414cdf0e10cSrcweir         const SpriteConnectedRanges::ComponentListType::const_iterator aEnd(
415cdf0e10cSrcweir             rUpdateArea.maComponentList.end() );
416cdf0e10cSrcweir 
417cdf0e10cSrcweir         // now, calc the _true_ update area, by merging all sprite's
418cdf0e10cSrcweir         // true update areas into one rectangle
419cdf0e10cSrcweir         ::basegfx::B2DRange aTrueArea( aBegin->second.getUpdateArea() );
420cdf0e10cSrcweir         ::std::for_each( aBegin,
421cdf0e10cSrcweir                          aEnd,
422cdf0e10cSrcweir                          ::boost::bind( ::basegfx::B2DRangeExpander(aTrueArea),
423cdf0e10cSrcweir                                         ::boost::bind( &SpriteInfo::getUpdateArea,
424cdf0e10cSrcweir                                                        ::boost::bind( ::std::select2nd<AreaComponent>(),
425cdf0e10cSrcweir                                                                       _1 ) ) ) );
426cdf0e10cSrcweir 
427cdf0e10cSrcweir         // and check whether _any_ of the sprites tells that its area
428cdf0e10cSrcweir         // update will not be opaque.
429cdf0e10cSrcweir         return (::std::find_if( aBegin,
430cdf0e10cSrcweir                                 aEnd,
431cdf0e10cSrcweir                                 ::boost::bind( &SpriteRedrawManager::isAreaUpdateNotOpaque,
432cdf0e10cSrcweir                                                this,
433cdf0e10cSrcweir                                                ::boost::cref(aTrueArea),
434cdf0e10cSrcweir                                                _1 ) ) == aEnd );
435cdf0e10cSrcweir     }
436cdf0e10cSrcweir 
areSpritesChanged(const UpdateArea & rUpdateArea) const437cdf0e10cSrcweir     bool SpriteRedrawManager::areSpritesChanged( const UpdateArea& rUpdateArea ) const
438cdf0e10cSrcweir     {
439cdf0e10cSrcweir         // check whether SpriteInfo::needsUpdate returns false for
440cdf0e10cSrcweir         // all elements of this area's contained sprites
441cdf0e10cSrcweir         //
442cdf0e10cSrcweir         // if not a single changed sprite found - just ignore this
443cdf0e10cSrcweir         // component (return false)
444cdf0e10cSrcweir         const SpriteConnectedRanges::ComponentListType::const_iterator aEnd(
445cdf0e10cSrcweir             rUpdateArea.maComponentList.end() );
446cdf0e10cSrcweir         return (::std::find_if( rUpdateArea.maComponentList.begin(),
447cdf0e10cSrcweir                                 aEnd,
448cdf0e10cSrcweir                                 ::boost::bind( &SpriteInfo::needsUpdate,
449cdf0e10cSrcweir                                                ::boost::bind(
450cdf0e10cSrcweir                                                    ::std::select2nd<SpriteConnectedRanges::ComponentType>(),
451cdf0e10cSrcweir                                                    _1 ) ) ) != aEnd );
452cdf0e10cSrcweir     }
453cdf0e10cSrcweir 
SpriteRedrawManager()454cdf0e10cSrcweir     SpriteRedrawManager::SpriteRedrawManager() :
455cdf0e10cSrcweir         maSprites(),
456cdf0e10cSrcweir         maChangeRecords()
457cdf0e10cSrcweir     {
458cdf0e10cSrcweir     }
459cdf0e10cSrcweir 
disposing()460cdf0e10cSrcweir     void SpriteRedrawManager::disposing()
461cdf0e10cSrcweir     {
462cdf0e10cSrcweir         // drop all references
463cdf0e10cSrcweir         maChangeRecords.clear();
464cdf0e10cSrcweir 
465cdf0e10cSrcweir         // dispose all sprites - the spritecanvas, and by delegation,
466cdf0e10cSrcweir         // this object, is the owner of the sprites. After all, a
467cdf0e10cSrcweir         // sprite without a canvas to render into makes not terribly
468cdf0e10cSrcweir         // much sense.
469cdf0e10cSrcweir 
470cdf0e10cSrcweir         // TODO(Q3): Once boost 1.33 is in, change back to for_each
471cdf0e10cSrcweir         // with ::boost::mem_fn. For the time being, explicit loop due
472cdf0e10cSrcweir         // to cdecl declaration of all UNO methods.
473cdf0e10cSrcweir         ListOfSprites::reverse_iterator aCurr( maSprites.rbegin() );
474cdf0e10cSrcweir         ListOfSprites::reverse_iterator aEnd( maSprites.rend() );
475cdf0e10cSrcweir         while( aCurr != aEnd )
476cdf0e10cSrcweir             (*aCurr++)->dispose();
477cdf0e10cSrcweir 
478cdf0e10cSrcweir         maSprites.clear();
479cdf0e10cSrcweir     }
480cdf0e10cSrcweir 
clearChangeRecords()481cdf0e10cSrcweir     void SpriteRedrawManager::clearChangeRecords()
482cdf0e10cSrcweir     {
483cdf0e10cSrcweir         maChangeRecords.clear();
484cdf0e10cSrcweir     }
485cdf0e10cSrcweir 
showSprite(const Sprite::Reference & rSprite)486cdf0e10cSrcweir     void SpriteRedrawManager::showSprite( const Sprite::Reference& rSprite )
487cdf0e10cSrcweir     {
488cdf0e10cSrcweir         maSprites.push_back( rSprite );
489cdf0e10cSrcweir     }
490cdf0e10cSrcweir 
hideSprite(const Sprite::Reference & rSprite)491cdf0e10cSrcweir     void SpriteRedrawManager::hideSprite( const Sprite::Reference& rSprite )
492cdf0e10cSrcweir     {
493cdf0e10cSrcweir         maSprites.remove( rSprite );
494cdf0e10cSrcweir     }
495cdf0e10cSrcweir 
moveSprite(const Sprite::Reference & rSprite,const::basegfx::B2DPoint & rOldPos,const::basegfx::B2DPoint & rNewPos,const::basegfx::B2DVector & rSpriteSize)496cdf0e10cSrcweir     void SpriteRedrawManager::moveSprite( const Sprite::Reference&		rSprite,
497cdf0e10cSrcweir                                           const ::basegfx::B2DPoint& 	rOldPos,
498cdf0e10cSrcweir                                           const ::basegfx::B2DPoint&	rNewPos,
499cdf0e10cSrcweir                                           const ::basegfx::B2DVector& 	rSpriteSize )
500cdf0e10cSrcweir     {
501cdf0e10cSrcweir         maChangeRecords.push_back( SpriteChangeRecord( rSprite,
502cdf0e10cSrcweir                                                        rOldPos,
503cdf0e10cSrcweir                                                        rNewPos,
504cdf0e10cSrcweir                                                        rSpriteSize ) );
505cdf0e10cSrcweir     }
506cdf0e10cSrcweir 
updateSprite(const Sprite::Reference & rSprite,const::basegfx::B2DPoint & rPos,const::basegfx::B2DRange & rUpdateArea)507cdf0e10cSrcweir     void SpriteRedrawManager::updateSprite( const Sprite::Reference& 	rSprite,
508cdf0e10cSrcweir                                             const ::basegfx::B2DPoint& 	rPos,
509cdf0e10cSrcweir                                             const ::basegfx::B2DRange&	rUpdateArea )
510cdf0e10cSrcweir     {
511cdf0e10cSrcweir         maChangeRecords.push_back( SpriteChangeRecord( rSprite,
512cdf0e10cSrcweir                                                        rPos,
513cdf0e10cSrcweir                                                        rUpdateArea ) );
514cdf0e10cSrcweir     }
515cdf0e10cSrcweir 
516cdf0e10cSrcweir }
517