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