/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
if (!mpCurrMtf)
mpCurrMtf = std::make_shared<GDIMetaFile>();
// TODO(F1): Currently, the scroll metafile will // never contain any verbose text comments. Thus, // can only display the full mtf content, no // subsets.
maSubsetting.reset( mpCurrMtf );
// adapt maBounds. the requested scroll text metafile // will typically have dimension different from the // actual shape
::basegfx::B2DRectangle aScrollRect, aPaintRect;
ENSURE_OR_THROW( getRectanglesFromScrollMtf( aScrollRect,
aPaintRect,
mpCurrMtf ), "DrawShape::forceScrollTextMetaFile(): Could " "not extract scroll anim rectangles from mtf" );
// take the larger one of the two rectangles (that // should be the bound rect of the retrieved // metafile) if( aScrollRect.isInside( aPaintRect ) )
maBounds = aScrollRect; else
maBounds = aPaintRect;
} return mpCurrMtf;
}
void DrawShape::updateStateIds() const
{ // Update the states, we've just redrawn or created a new // attribute layer. if( mpAttributeLayer )
{
mnAttributeTransformationState = mpAttributeLayer->getTransformationState();
mnAttributeClipState = mpAttributeLayer->getClipState();
mnAttributeAlphaState = mpAttributeLayer->getAlphaState();
mnAttributePositionState = mpAttributeLayer->getPositionState();
mnAttributeContentState = mpAttributeLayer->getContentState();
mnAttributeVisibilityState = mpAttributeLayer->getVisibilityState();
}
}
// will perform the update now, clear update-enforcing // flags
mbForceUpdate = false;
mbAttributeLayerRevoked = false;
ENSURE_OR_RETURN_FALSE( !maViewShapes.empty(), "DrawShape::implRender(): render called on DrawShape without views" );
if( maBounds.isEmpty() )
{ // zero-sized shapes are effectively invisible, // thus, we save us the rendering... returntrue;
}
// redraw all view shapes, by calling their update() method
ViewShape::RenderArgs renderArgs( getViewRenderArgs() ); bool bVisible = isVisible(); if( o3tl::make_unsigned(::std::count_if( maViewShapes.begin(),
maViewShapes.end(),
[this, &bVisible, &renderArgs, &nUpdateFlags]
( const ViewShapeSharedPtr& pShape )
{ return pShape->update( this->mpCurrMtf,
renderArgs,
nUpdateFlags,
bVisible ); } ))
!= maViewShapes.size() )
{ // at least one of the ViewShape::update() calls did return // false - update failed on at least one ViewLayer returnfalse;
}
// successfully redrawn - update state IDs to detect next changes
updateStateIds();
returntrue;
}
UpdateFlags DrawShape::getUpdateFlags() const
{ // default: update nothing, unless ShapeAttributeStack // tells us below, or if the attribute layer was revoked
UpdateFlags nUpdateFlags(UpdateFlags::NONE);
// do we have an attribute layer? if( mpAttributeLayer )
{ // Prevent nUpdateFlags to be modified when the shape is not // visible, except when it just was hidden. if (mpAttributeLayer->getVisibility()
|| mpAttributeLayer->getVisibilityState() != mnAttributeVisibilityState )
{ if (mpAttributeLayer->getVisibilityState() != mnAttributeVisibilityState )
{ // Change of the visibility state is mapped to // content change because when the visibility // changes then usually a sprite is shown or hidden // and the background under has to be painted once.
nUpdateFlags |= UpdateFlags::Content;
}
::basegfx::B2DRectangle DrawShape::getActualUnitShapeBounds() const
{
ENSURE_OR_THROW( !maViewShapes.empty(), "DrawShape::getActualUnitShapeBounds(): called on DrawShape without views" );
// perform the cheapest check first if( rSubsets.empty() )
{ // if subset contains the whole shape, no need to call // the somewhat expensive bound calculation, since as // long as the subset is empty, this branch will be // taken. return aDefaultBounds;
} else
{
OSL_ENSURE( rSubsets.size() != 1 ||
!rSubsets.front().isEmpty(), "DrawShape::getActualUnitShapeBounds() expects a " "_non-empty_ subset vector for a subsetted shape!" );
// are the cached bounds still valid? if( !maCurrentShapeUnitBounds )
{ // no, (re)generate them // =====================
// setup cached values to defaults (might fail to // retrieve true bounds below)
maCurrentShapeUnitBounds = aDefaultBounds;
// TODO(P2): the subset of the master shape (that from // which the subsets are subtracted) changes // relatively often (every time a subset shape is // added or removed). Maybe we should exclude it here, // always assuming full bounds?
// TODO(Q2): Although this _is_ currently // view-agnostic, it might not stay like // that. Maybe this method should again be moved // to the ViewShape
::cppcanvas::RendererSharedPtr pRenderer(
maViewShapes.front()->getRenderer(
pDestinationCanvas, mpCurrMtf, mpAttributeLayer ) );
// If we cannot not prefetch, be defensive and assume // full shape size if( pRenderer )
{ // temporarily, switch total transformation to identity // (need the bounds in the [0,1]x[0,1] unit coordinate // system.
::basegfx::B2DHomMatrix aEmptyTransformation;
// really make sure no shape appears larger than its // original bounds (there _are_ some pathologic cases, // especially when imported from PPT, that have // e.g. obscenely large polygon bounds)
aTotalBounds.intersect(
::basegfx::B2DRange( 0.0, 0.0, 1.0, 1.0 ));
// must NOT be called from within initializer list, uses // state from mnCurrMtfLoadFlags!
mpCurrMtf = getMetaFile(uno::Reference<lang::XComponent>(xShape, uno::UNO_QUERY),
xContainingPage, mnCurrMtfLoadFlags,
mxComponentContext ); if (!mpCurrMtf)
mpCurrMtf = std::make_shared<GDIMetaFile>();
maSubsetting.reset( mpCurrMtf );
prepareHyperlinkIndices();
if(mbContainsPageField && !maBounds.isEmpty())
{ // tdf#150402 Use mbContainsPageField that gets set in prepareHyperlinkIndices // which has to be run anyways, so this will cause no harm in execution speed. // It lets us detect the potential error case that a PageField is contained in // the Text of the Shape. That is a hint that maBounds contains the wrong Range // and needs to be corrected. The correct size is in the PrefSize of the metafile. // For more background information please refer to tdf#150402, Comment 16. constdouble fWidthDiff(fabs(mpCurrMtf->GetPrefSize().Width() - maBounds.getWidth())); constdouble fHeightDiff(fabs(mpCurrMtf->GetPrefSize().Height() - maBounds.getHeight()));
// if the Animation is bigger then 5 million pixels, we do not load the // whole animation now. if (nBitmapPixels * aAnimation.Count() > 5000000)
{
nFramesToLoad = 5000000 / nBitmapPixels; if (nFramesToLoad < 10)
nFramesToLoad = 10;
}
mpGraphicLoader = ::std::make_unique<DelayedGraphicLoader>(pGraphic);
getSomeAnimationFramesFromGraphic(nFramesToLoad);
// xxx todo: currently not implemented for subsetted shapes; // would mean modifying set of hyperlink regions when // subsetting text portions. N.B.: there's already an // issue for this #i72828#
}
bool DrawShape::render() const
{ // force redraw. Have to also pass on the update flags, // because e.g. content update (regeneration of the // metafile renderer) is normally not performed. A simple // UpdateFlags::Force would only paint the metafile in its // old state. return implRender( UpdateFlags::Force | getUpdateFlags() );
}
::basegfx::B2DRectangle DrawShape::getBounds() const
{ // little optimization: for non-modified shapes, we don't // create an ShapeAttributeStack, and therefore also don't // have to check it. return getShapePosSize( maBounds,
mpAttributeLayer );
}
// an already empty shape bound need no further // treatment. In fact, any changes applied below would // actually remove the special empty state, thus, don't // change! if( !maBounds.isEmpty() )
{
basegfx::B2DRectangle aUnitBounds(0.0,0.0,1.0,1.0);
if( !aUnitBounds.isEmpty() )
{ if( mpAttributeLayer )
{ // calc actual shape area (in user coordinate // space) from the transformation as given by the // shape attribute layer
aBounds = getShapeUpdateArea( aUnitBounds,
getShapeTransformation( getBounds(),
mpAttributeLayer ),
mpAttributeLayer );
} else
{ // no attribute layer, thus, the true shape bounds // can be directly derived from the XShape bound // attribute
aBounds = getShapeUpdateArea( aUnitBounds,
maBounds );
}
if( !maViewShapes.empty() )
{ // determine border needed for antialiasing the shape
::basegfx::B2DSize aAABorder(0.0,0.0);
// for every view, get AA border and 'expand' aAABorder // appropriately. for( constauto& rViewShape : maViewShapes )
{ const ::basegfx::B2DSize rShapeBorder( rViewShape->getAntialiasingBorder() );
if( mpAttributeLayer )
{ // check whether visibility and alpha are not default // (mpAttributeLayer->isVisibilityValid() returns true // then): bVisible becomes true, if shape visibility // is on and alpha is not 0.0 (fully transparent) if( mpAttributeLayer->isVisibilityValid() )
bIsVisible = mpAttributeLayer->getVisibility();
// only touch bIsVisible, if the shape is still // visible - if getVisibility already made us // invisible, no alpha value will make us appear // again. if( bIsVisible && mpAttributeLayer->isAlphaValid() )
bIsVisible = !::basegfx::fTools::equalZero( mpAttributeLayer->getAlpha() );
}
void DrawShape::enterAnimationMode()
{
OSL_ENSURE( !maViewShapes.empty(), "DrawShape::enterAnimationMode(): called on DrawShape without views" );
if( mnIsAnimatedCount == 0 )
{ // notify all ViewShapes, by calling their enterAnimationMode method. // We're now entering animation mode for( constauto& rViewShape : maViewShapes )
rViewShape->enterAnimationMode();
}
++mnIsAnimatedCount;
}
void DrawShape::leaveAnimationMode()
{
OSL_ENSURE( !maViewShapes.empty(), "DrawShape::leaveAnimationMode(): called on DrawShape without views" );
--mnIsAnimatedCount;
if( mnIsAnimatedCount == 0 )
{ // notify all ViewShapes, by calling their leaveAnimationMode method. // we're now leaving animation mode for( constauto& rViewShape : maViewShapes )
rViewShape->leaveAnimationMode();
}
}
// AttributableShape methods
ShapeAttributeLayerSharedPtr DrawShape::createAttributeLayer()
{ // create new layer, with last as its new child
mpAttributeLayer = std::make_shared<ShapeAttributeLayer>( mpAttributeLayer );
// Update the local state ids to reflect those of the new layer.
updateStateIds();
bool DrawShape::createSubset( AttributableShapeSharedPtr& o_rSubset, const DocTreeNode& rTreeNode )
{ // subset shape already created for this DocTreeNode?
AttributableShapeSharedPtr pSubset( maSubsetting.getSubsetShape( rTreeNode ) );
// when true, this method has created a new subset // DrawShape bool bNewlyCreated( false );
if( pSubset )
{
o_rSubset = std::move(pSubset);
// reusing existing subset
} else
{ // not yet created, init entry
o_rSubset.reset( new DrawShape( *this,
rTreeNode, // TODO(Q3): That's a // hack. We assume // that start and end // index will always // be less than 65535
mnPriority +
rTreeNode.getStartIndex()/double(SAL_MAX_INT16) ));
bNewlyCreated = true; // subset newly created
}
// always register shape at DrawShapeSubsetting, to keep // refcount up-to-date
maSubsetting.addSubsetShape( o_rSubset );
// forward to delegate if( maSubsetting.revokeSubsetShape( rShape ) )
{ // force redraw, our content has possibly changed (as // one of the subsets now display within our shape // again).
mbForceUpdate = true;
// #i47428# TEMP FIX: synchronize visibility of subset // with parent.
// TODO(F3): Remove here, and implement // TEXT_ONLY/BACKGROUND_ONLY with the proverbial // additional level of indirection: create a // persistent subset, containing all text/only the // background respectively. From _that_ object, // generate the temporary character subset shapes. const ShapeAttributeLayerSharedPtr xAttrLayer(
rShape->getTopmostAttributeLayer() ); if( xAttrLayer &&
xAttrLayer->isVisibilityValid() &&
xAttrLayer->getVisibility() != isVisible() )
{ constbool bVisibility( xAttrLayer->getVisibility() );
// If the Animation is fully loaded, no need to load anymore. if (mpGraphicLoader->mnLoadedFrames >= maAnimationFrames.size())
{
mpGraphicLoader.reset();
}
}
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.