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