voidoperator()( const SpriteRedrawManager::SpriteChangeRecord& rSpriteRecord )
{ // only deal with change events from the currently // affected sprite if( rSpriteRecord.mpAffectedSprite != mpAffectedSprite ) return;
switch( rSpriteRecord.meChangeType )
{ case SpriteRedrawManager::SpriteChangeRecord::ChangeType::move: if( !mbIsMove )
{ // no move yet - this must be the first one
maMoveStartArea = ::basegfx::B2DRectangle(
rSpriteRecord.maOldPos,
rSpriteRecord.maOldPos + rSpriteRecord.maUpdateArea.getRange() );
mbIsMove = true;
}
case SpriteRedrawManager::SpriteChangeRecord::ChangeType::update: // update end update area of the // sprite. Thus, every update() action // _after_ the last move will correctly // update the final repaint area. And this // does not interfere with subsequent // moves, because moves always perform a // hard set of maMoveEndArea to their // stored value
maMoveEndArea.expand( rSpriteRecord.maUpdateArea );
mbIsGenericUpdate = true; break;
default:
ENSURE_OR_THROW( false, "Unexpected case in SpriteUpdater::operator()" ); break;
}
}
void commit( SpriteRedrawManager::SpriteConnectedRanges& rUpdateCollector ) const
{ if( mbIsMove )
{ if( !maMoveStartArea.isEmpty() ||
!maMoveEndArea.isEmpty() )
{ // if mbIsGenericUpdate is false, this is a // pure move (i.e. no other update // operations). Pass that information on to // the SpriteInfo constbool bIsPureMove( !mbIsGenericUpdate );
// ignore the case that start and end update // area overlap - the b2dconnectedranges // handle that, anyway. doing it this way // ensures that we have both old and new area // stored
// round all given range up to enclosing // integer rectangle - since the whole thing // here is about
// first, draw the new sprite position
rUpdateCollector.addRange(
::basegfx::unotools::b2DSurroundingIntegerRangeFromB2DRange( maMoveEndArea ),
SpriteRedrawManager::SpriteInfo(
mpAffectedSprite,
maMoveEndArea, true,
bIsPureMove ) );
/// True, if at least one move was encountered bool mbIsMove;
/// True, if at least one generic update was encountered bool mbIsGenericUpdate;
};
/** SpriteChecker functor, which for every sprite checks the givenupdatevectorfornecessaryscreenupdates
*/ class SpriteUpdater
{ public: /** Generate update area list
void SpriteRedrawManager::setupUpdateAreas( SpriteConnectedRanges& rUpdateAreas ) const
{ // TODO(T3): This is NOT thread safe at all. This only works // under the assumption that NOBODY changes ANYTHING // concurrently, while this method is on the stack. We should // really rework the canvas::Sprite interface, in such a way // that it dumps ALL its state with a single, atomic // call. Then, we store that state locally. This prolly goes // in line with the problem of having sprite state available // for the frame before the last frame; plus, it avoids // frequent locks of the object mutexes
SpriteWeakOrder aSpriteComparator;
// put all sprites that have changed content into update areas for( constauto& pSprite : maSprites )
{ if( pSprite->isContentChanged() ) const_cast< SpriteRedrawManager* >( this )->updateSprite( pSprite,
pSprite->getPosPixel(),
pSprite->getUpdateArea() );
}
// extract all referenced sprites from the maChangeRecords // (copy sprites, make the list unique, regarding the // sprite pointer). This assumes that, until this scope // ends, nobody changes the maChangeRecords vector!
VectorOfSprites aUpdatableSprites; for( constauto& rChangeRecord : maChangeRecords )
{ const Sprite::Reference& rSprite( rChangeRecord.getSprite() ); if( rSprite.is() )
aUpdatableSprites.push_back( rSprite );
}
// for each unique sprite, check the change event vector, // calculate the update operation from that, and add the // result to the aUpdateArea.
std::for_each( aUpdatableSprites.begin(),
aEnd,
SpriteUpdater( rUpdateAreas,
maChangeRecords) );
// TODO(P2): Implement your own output iterator adapter, to // avoid that totally superfluous temp aUnchangedSprites // vector.
// add all sprites to rUpdateAreas, that are _not_ already // contained in the uniquified vector of changed ones // (i.e. the difference between aSortedSpriteVector and // aUpdatableSprites).
VectorOfSprites aUnchangedSprites;
std::set_difference( aSortedSpriteVector.begin(),
aSortedSpriteVector.end(),
aUpdatableSprites.begin(),
aEnd,
std::back_insert_iterator< VectorOfSprites >(aUnchangedSprites),
aSpriteComparator );
// add each remaining unchanged sprite to connected ranges, // marked as "don't need update" for( constauto& pUnchangedSprite : aUnchangedSprites )
{ const ::basegfx::B2DRange aUpdateArea( pUnchangedSprite->getUpdateArea() );
rUpdateAreas.addRange(
::basegfx::unotools::b2DSurroundingIntegerRangeFromB2DRange( aUpdateArea ),
SpriteInfo( pUnchangedSprite,
aUpdateArea, false ) );
}
}
bool SpriteRedrawManager::isAreaUpdateScroll( ::basegfx::B2DRectangle& o_rMoveStart,
::basegfx::B2DRectangle& o_rMoveEnd, const UpdateArea& rUpdateArea,
std::size_t nNumSprites ) const
{ // check for a solitary move, which consists of exactly two // pure-move entries, the first with valid, the second with // invalid sprite (see SpriteTracer::commit()). Note that we // cannot simply store some flag in SpriteTracer::commit() // above and just check that here, since during the connected // range calculations, other sprites might get merged into the // same region (thus spoiling the scrolling move // optimization). if( nNumSprites != 2 ) returnfalse;
if( !aFirst->second.isPureMove() ||
!aSecond->second.isPureMove() ||
!aFirst->second.getSprite().is() || // use _true_ update area, not the rounded version
!aFirst->second.getSprite()->isAreaUpdateOpaque( aFirst->second.getUpdateArea() ) ||
aSecond->second.getSprite().is() )
{ // either no move update, or incorrect sprite, or sprite // content not fully opaque over update region. returnfalse;
}
SAL_WARN_IF(!impIsEqualB2DRange(rUpdateArea.maTotalBounds, basegfx::unotools::b2DSurroundingIntegerRangeFromB2DRange(aTotalBounds), 0.5), "canvas", "SpriteRedrawManager::isAreaUpdateScroll(): sprite area and total area mismatch");
SAL_WARN_IF(!impIsEqualB2DVector(o_rMoveStart.getRange(), o_rMoveEnd.getRange(), 0.5), "canvas", "SpriteRedrawManager::isAreaUpdateScroll(): scroll start and end area have mismatching size"); #endif
bool SpriteRedrawManager::isAreaUpdateOpaque( const UpdateArea& rUpdateArea,
std::size_t nNumSprites ) const
{ // check whether the sprites in the update area's list will // fully cover the given area _and_ do that in an opaque way // (i.e. no alpha, no non-rectangular sprite content).
// TODO(P1): Come up with a smarter early-exit criterion here // (though, I think, the case that _lots_ of sprites _fully_ // cover a rectangular area _without_ any holes is extremely // improbable)
// avoid checking large number of sprites (and probably fail, // anyway). Note: the case nNumSprites < 1 should normally not // happen, as handleArea() calls backgroundPaint() then. if( nNumSprites > 3 || nNumSprites < 1 ) returnfalse;
// now, calc the _true_ update area, by merging all sprite's // true update areas into one rectangle
::basegfx::B2DRange aTrueArea( rUpdateArea.maComponentList.begin()->second.getUpdateArea() ); for( constauto& rArea : rUpdateArea.maComponentList )
aTrueArea.expand(rArea.second.getUpdateArea());
// and check whether _any_ of the sprites tells that its area // update will not be opaque. return std::none_of( rUpdateArea.maComponentList.begin(),
aEnd,
[&aTrueArea, this]( const std::pair< ::basegfx::B2DRange, SpriteInfo >& cp )
{ returnthis->isAreaUpdateNotOpaque(aTrueArea, cp); } );
}
bool SpriteRedrawManager::areSpritesChanged( const UpdateArea& rUpdateArea ) const
{ // check whether SpriteInfo::needsUpdate returns false for // all elements of this area's contained sprites
// if not a single changed sprite found - just ignore this // component (return false) const SpriteConnectedRanges::ComponentListType::const_iterator aEnd(
rUpdateArea.maComponentList.end() ); return std::any_of( rUpdateArea.maComponentList.begin(),
aEnd,
[]( const std::pair< ::basegfx::B2DRange, SpriteInfo >& cp )
{ return cp.second.needsUpdate(); } );
}
SpriteRedrawManager::SpriteRedrawManager()
{
}
void SpriteRedrawManager::disposing()
{ // drop all references
maChangeRecords.clear();
// dispose all sprites - the spritecanvas, and by delegation, // this object, is the owner of the sprites. After all, a // sprite without a canvas to render into makes not terribly // much sense. for( constauto& rCurr : boost::adaptors::reverse(maSprites) )
rCurr->dispose();
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.