1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <comphelper/diagnose_ex.hxx>
22 #include <sal/log.hxx>
23 #include <com/sun/star/beans/XPropertySet.hpp>
24 #include <o3tl/safeint.hxx>
27 #include <vcl/metaact.hxx>
28 #include <vcl/gdimtf.hxx>
29 #include <vcl/graph.hxx>
31 #include <basegfx/numeric/ftools.hxx>
33 #include <com/sun/star/drawing/TextAnimationKind.hpp>
35 #include <comphelper/scopeguard.hxx>
41 #include "drawshapesubsetting.hxx"
42 #include "drawshape.hxx"
43 #include <eventqueue.hxx>
44 #include <wakeupevent.hxx>
45 #include <subsettableshapemanager.hxx>
46 #include "intrinsicanimationactivity.hxx"
48 #include "gdimtftools.hxx"
49 #include "drawinglayeranimation.hxx"
51 using namespace ::com::sun::star
;
54 namespace slideshow::internal
61 GDIMetaFileSharedPtr
const & DrawShape::forceScrollTextMetaFile()
63 if ((mnCurrMtfLoadFlags
& MTF_LOAD_SCROLL_TEXT_MTF
) != MTF_LOAD_SCROLL_TEXT_MTF
)
65 // reload with added flags:
66 mnCurrMtfLoadFlags
|= MTF_LOAD_SCROLL_TEXT_MTF
;
67 mpCurrMtf
= getMetaFile(uno::Reference
<lang::XComponent
>(mxShape
, uno::UNO_QUERY
),
68 mxPage
, mnCurrMtfLoadFlags
,
72 mpCurrMtf
= std::make_shared
<GDIMetaFile
>();
74 // TODO(F1): Currently, the scroll metafile will
75 // never contain any verbose text comments. Thus,
76 // can only display the full mtf content, no
78 maSubsetting
.reset( mpCurrMtf
);
80 // adapt maBounds. the requested scroll text metafile
81 // will typically have dimension different from the
83 ::basegfx::B2DRectangle aScrollRect
, aPaintRect
;
84 ENSURE_OR_THROW( getRectanglesFromScrollMtf( aScrollRect
,
87 "DrawShape::forceScrollTextMetaFile(): Could "
88 "not extract scroll anim rectangles from mtf" );
90 // take the larger one of the two rectangles (that
91 // should be the bound rect of the retrieved
93 if( aScrollRect
.isInside( aPaintRect
) )
94 maBounds
= aScrollRect
;
96 maBounds
= aPaintRect
;
101 void DrawShape::updateStateIds() const
103 // Update the states, we've just redrawn or created a new
105 if( mpAttributeLayer
)
107 mnAttributeTransformationState
= mpAttributeLayer
->getTransformationState();
108 mnAttributeClipState
= mpAttributeLayer
->getClipState();
109 mnAttributeAlphaState
= mpAttributeLayer
->getAlphaState();
110 mnAttributePositionState
= mpAttributeLayer
->getPositionState();
111 mnAttributeContentState
= mpAttributeLayer
->getContentState();
112 mnAttributeVisibilityState
= mpAttributeLayer
->getVisibilityState();
116 ViewShape::RenderArgs
DrawShape::getViewRenderArgs() const
118 return ViewShape::RenderArgs(
122 getActualUnitShapeBounds(),
124 maSubsetting
.getActiveSubsets(),
128 bool DrawShape::implRender( UpdateFlags nUpdateFlags
) const
130 SAL_INFO( "slideshow", "::presentation::internal::DrawShape::implRender()" );
131 SAL_INFO( "slideshow", "::presentation::internal::DrawShape: 0x" << std::hex
<< this );
133 // will perform the update now, clear update-enforcing
135 mbForceUpdate
= false;
136 mbAttributeLayerRevoked
= false;
138 ENSURE_OR_RETURN_FALSE( !maViewShapes
.empty(),
139 "DrawShape::implRender(): render called on DrawShape without views" );
141 if( maBounds
.isEmpty() )
143 // zero-sized shapes are effectively invisible,
144 // thus, we save us the rendering...
148 // redraw all view shapes, by calling their update() method
149 ViewShape::RenderArgs
renderArgs( getViewRenderArgs() );
150 bool bVisible
= isVisible();
151 if( o3tl::make_unsigned(::std::count_if( maViewShapes
.begin(),
153 [this, &bVisible
, &renderArgs
, &nUpdateFlags
]
154 ( const ViewShapeSharedPtr
& pShape
)
155 { return pShape
->update( this->mpCurrMtf
,
159 != maViewShapes
.size() )
161 // at least one of the ViewShape::update() calls did return
162 // false - update failed on at least one ViewLayer
166 // successfully redrawn - update state IDs to detect next changes
172 UpdateFlags
DrawShape::getUpdateFlags() const
174 // default: update nothing, unless ShapeAttributeStack
175 // tells us below, or if the attribute layer was revoked
176 UpdateFlags
nUpdateFlags(UpdateFlags::NONE
);
178 // possibly the whole shape content changed
179 if( mbAttributeLayerRevoked
)
180 nUpdateFlags
= UpdateFlags::Content
;
183 // determine what has to be updated
186 // do we have an attribute layer?
187 if( mpAttributeLayer
)
189 // Prevent nUpdateFlags to be modified when the shape is not
190 // visible, except when it just was hidden.
191 if (mpAttributeLayer
->getVisibility()
192 || mpAttributeLayer
->getVisibilityState() != mnAttributeVisibilityState
)
194 if (mpAttributeLayer
->getVisibilityState() != mnAttributeVisibilityState
)
196 // Change of the visibility state is mapped to
197 // content change because when the visibility
198 // changes then usually a sprite is shown or hidden
199 // and the background under has to be painted once.
200 nUpdateFlags
|= UpdateFlags::Content
;
203 // TODO(P1): This can be done without conditional branching.
205 if( mpAttributeLayer
->getPositionState() != mnAttributePositionState
)
207 nUpdateFlags
|= UpdateFlags::Position
;
209 if( mpAttributeLayer
->getAlphaState() != mnAttributeAlphaState
)
211 nUpdateFlags
|= UpdateFlags::Alpha
;
213 if( mpAttributeLayer
->getClipState() != mnAttributeClipState
)
215 nUpdateFlags
|= UpdateFlags::Clip
;
217 if( mpAttributeLayer
->getTransformationState() != mnAttributeTransformationState
)
219 nUpdateFlags
|= UpdateFlags::Transformation
;
221 if( mpAttributeLayer
->getContentState() != mnAttributeContentState
)
223 nUpdateFlags
|= UpdateFlags::Content
;
231 ::basegfx::B2DRectangle
DrawShape::getActualUnitShapeBounds() const
233 ENSURE_OR_THROW( !maViewShapes
.empty(),
234 "DrawShape::getActualUnitShapeBounds(): called on DrawShape without views" );
236 const VectorOfDocTreeNodes
& rSubsets(
237 maSubsetting
.getActiveSubsets() );
239 const ::basegfx::B2DRectangle
aDefaultBounds( 0.0,0.0,1.0,1.0 );
241 // perform the cheapest check first
242 if( rSubsets
.empty() )
244 // if subset contains the whole shape, no need to call
245 // the somewhat expensive bound calculation, since as
246 // long as the subset is empty, this branch will be
248 return aDefaultBounds
;
252 OSL_ENSURE( rSubsets
.size() != 1 ||
253 !rSubsets
.front().isEmpty(),
254 "DrawShape::getActualUnitShapeBounds() expects a "
255 "_non-empty_ subset vector for a subsetted shape!" );
257 // are the cached bounds still valid?
258 if( !maCurrentShapeUnitBounds
)
260 // no, (re)generate them
261 // =====================
263 // setup cached values to defaults (might fail to
264 // retrieve true bounds below)
265 maCurrentShapeUnitBounds
= aDefaultBounds
;
267 // TODO(P2): the subset of the master shape (that from
268 // which the subsets are subtracted) changes
269 // relatively often (every time a subset shape is
270 // added or removed). Maybe we should exclude it here,
271 // always assuming full bounds?
273 ::cppcanvas::CanvasSharedPtr
pDestinationCanvas(
274 maViewShapes
.front()->getViewLayer()->getCanvas() );
276 // TODO(Q2): Although this _is_ currently
277 // view-agnostic, it might not stay like
278 // that. Maybe this method should again be moved
280 ::cppcanvas::RendererSharedPtr
pRenderer(
281 maViewShapes
.front()->getRenderer(
282 pDestinationCanvas
, mpCurrMtf
, mpAttributeLayer
) );
284 // If we cannot not prefetch, be defensive and assume
288 // temporarily, switch total transformation to identity
289 // (need the bounds in the [0,1]x[0,1] unit coordinate
291 ::basegfx::B2DHomMatrix aEmptyTransformation
;
293 ::basegfx::B2DHomMatrix
aOldTransform( pDestinationCanvas
->getTransformation() );
294 pDestinationCanvas
->setTransformation( aEmptyTransformation
);
295 pRenderer
->setTransformation( aEmptyTransformation
);
297 // restore old transformation when leaving the scope
298 const ::comphelper::ScopeGuard
aGuard(
299 [&pDestinationCanvas
, &aOldTransform
]()
300 { return pDestinationCanvas
->setTransformation( aOldTransform
); } );
303 // retrieve bounds for subset of whole metafile
306 ::basegfx::B2DRange aTotalBounds
;
308 // cannot use ::boost::bind, ::basegfx::B2DRange::expand()
310 for( const auto& rDocTreeNode
: rSubsets
)
311 aTotalBounds
.expand( pRenderer
->getSubsetArea(
312 rDocTreeNode
.getStartIndex(),
313 rDocTreeNode
.getEndIndex() ) );
315 OSL_ENSURE( aTotalBounds
.getMinX() >= -0.1 &&
316 aTotalBounds
.getMinY() >= -0.1 &&
317 aTotalBounds
.getMaxX() <= 1.1 &&
318 aTotalBounds
.getMaxY() <= 1.1,
319 "DrawShape::getActualUnitShapeBounds(): bounds noticeably larger than original shape - clipping!" );
321 // really make sure no shape appears larger than its
322 // original bounds (there _are_ some pathologic cases,
323 // especially when imported from PPT, that have
324 // e.g. obscenely large polygon bounds)
325 aTotalBounds
.intersect(
326 ::basegfx::B2DRange( 0.0, 0.0,
329 maCurrentShapeUnitBounds
= aTotalBounds
;
333 return *maCurrentShapeUnitBounds
;
337 DrawShape::DrawShape( const uno::Reference
< drawing::XShape
>& xShape
,
338 const uno::Reference
< drawing::XDrawPage
>& xContainingPage
,
341 const SlideShowContext
& rContext
) :
343 mxPage( xContainingPage
),
344 maAnimationFrames(), // empty, we don't have no intrinsic animation
348 mnCurrMtfLoadFlags( bForeignSource
349 ? MTF_LOAD_FOREIGN_SOURCE
: MTF_LOAD_NONE
),
350 maCurrentShapeUnitBounds(),
351 mnPriority( nPrio
), // TODO(F1): When ZOrder someday becomes usable: make this ( getAPIShapePrio( xShape ) ),
352 maBounds( getAPIShapeBounds( xShape
) ),
354 mpIntrinsicAnimationActivity(),
355 mnAttributeTransformationState(0),
356 mnAttributeClipState(0),
357 mnAttributeAlphaState(0),
358 mnAttributePositionState(0),
359 mnAttributeContentState(0),
360 mnAttributeVisibilityState(0),
362 mxComponentContext( rContext
.mxComponentContext
),
363 maHyperlinkIndices(),
364 maHyperlinkRegions(),
366 mnIsAnimatedCount(0),
367 mnAnimationLoopCount(0),
369 mbForceUpdate( false ),
370 mbAttributeLayerRevoked( false ),
371 mbDrawingLayerAnim( false ),
372 mbContainsPageField( false )
374 ENSURE_OR_THROW( mxShape
.is(), "DrawShape::DrawShape(): Invalid XShape" );
375 ENSURE_OR_THROW( mxPage
.is(), "DrawShape::DrawShape(): Invalid containing page" );
377 // check for drawing layer animations:
378 drawing::TextAnimationKind eKind
= drawing::TextAnimationKind_NONE
;
379 uno::Reference
<beans::XPropertySet
> xPropSet( mxShape
,
382 getPropertyValue( eKind
, xPropSet
,
383 u
"TextAnimationKind"_ustr
);
384 mbDrawingLayerAnim
= (eKind
!= drawing::TextAnimationKind_NONE
);
386 // must NOT be called from within initializer list, uses
387 // state from mnCurrMtfLoadFlags!
388 mpCurrMtf
= getMetaFile(uno::Reference
<lang::XComponent
>(xShape
, uno::UNO_QUERY
),
389 xContainingPage
, mnCurrMtfLoadFlags
,
390 mxComponentContext
);
392 mpCurrMtf
= std::make_shared
<GDIMetaFile
>();
394 maSubsetting
.reset( mpCurrMtf
);
396 prepareHyperlinkIndices();
398 if(mbContainsPageField
&& !maBounds
.isEmpty())
400 // tdf#150402 Use mbContainsPageField that gets set in prepareHyperlinkIndices
401 // which has to be run anyways, so this will cause no harm in execution speed.
402 // It lets us detect the potential error case that a PageField is contained in
403 // the Text of the Shape. That is a hint that maBounds contains the wrong Range
404 // and needs to be corrected. The correct size is in the PrefSize of the metafile.
405 // For more background information please refer to tdf#150402, Comment 16.
406 const double fWidthDiff(fabs(mpCurrMtf
->GetPrefSize().Width() - maBounds
.getWidth()));
407 const double fHeightDiff(fabs(mpCurrMtf
->GetPrefSize().Height() - maBounds
.getHeight()));
409 if(fWidthDiff
> 1.0 || fHeightDiff
> 1.0)
411 maBounds
= basegfx::B2DRange(
412 maBounds
.getMinX(), maBounds
.getMinY(),
413 maBounds
.getMinX() + mpCurrMtf
->GetPrefSize().Width(),
414 maBounds
.getMinY() + mpCurrMtf
->GetPrefSize().Height());
419 DrawShape::DrawShape( const uno::Reference
< drawing::XShape
>& xShape
,
420 uno::Reference
< drawing::XDrawPage
> xContainingPage
,
422 std::shared_ptr
<Graphic
> pGraphic
,
423 const SlideShowContext
& rContext
) :
425 mxPage(std::move( xContainingPage
)),
430 mnCurrMtfLoadFlags( MTF_LOAD_NONE
),
431 maCurrentShapeUnitBounds(),
432 mnPriority( nPrio
), // TODO(F1): When ZOrder someday becomes usable: make this ( getAPIShapePrio( xShape ) ),
433 maBounds( getAPIShapeBounds( xShape
) ),
435 mpIntrinsicAnimationActivity(),
436 mnAttributeTransformationState(0),
437 mnAttributeClipState(0),
438 mnAttributeAlphaState(0),
439 mnAttributePositionState(0),
440 mnAttributeContentState(0),
441 mnAttributeVisibilityState(0),
443 mxComponentContext( rContext
.mxComponentContext
),
444 maHyperlinkIndices(),
445 maHyperlinkRegions(),
447 mnIsAnimatedCount(0),
448 mnAnimationLoopCount(0),
450 mbForceUpdate( false ),
451 mbAttributeLayerRevoked( false ),
452 mbDrawingLayerAnim( false ),
453 mbContainsPageField( false )
455 ENSURE_OR_THROW( pGraphic
->IsAnimated(),
456 "DrawShape::DrawShape(): Graphic is no animation" );
458 ::Animation
aAnimation(pGraphic
->GetAnimation());
459 const Size
aAnimSize(aAnimation
.GetDisplaySizePixel());
460 tools::Long nBitmapPixels
= aAnimSize
.getWidth() * aAnimSize
.getHeight();
462 tools::Long nFramesToLoad
= aAnimation
.Count();
464 // if the Animation is bigger then 5 million pixels, we do not load the
465 // whole animation now.
466 if (nBitmapPixels
* aAnimation
.Count() > 5000000)
468 nFramesToLoad
= 5000000 / nBitmapPixels
;
469 if (nFramesToLoad
< 10)
472 mpGraphicLoader
= ::std::make_unique
<DelayedGraphicLoader
>(pGraphic
);
473 getSomeAnimationFramesFromGraphic(nFramesToLoad
);
475 ENSURE_OR_THROW( !maAnimationFrames
.empty() &&
476 maAnimationFrames
.front().mpMtf
,
477 "DrawShape::DrawShape(): " );
478 mpCurrMtf
= maAnimationFrames
.front().mpMtf
;
480 ENSURE_OR_THROW( mxShape
.is(), "DrawShape::DrawShape(): Invalid XShape" );
481 ENSURE_OR_THROW( mxPage
.is(), "DrawShape::DrawShape(): Invalid containing page" );
482 ENSURE_OR_THROW( mpCurrMtf
, "DrawShape::DrawShape(): Invalid metafile" );
485 DrawShape::DrawShape( const DrawShape
& rSrc
,
486 const DocTreeNode
& rTreeNode
,
488 mxShape( rSrc
.mxShape
),
489 mxPage( rSrc
.mxPage
),
490 maAnimationFrames(), // don't copy animations for subsets,
491 // only the current frame!
494 mpCurrMtf( rSrc
.mpCurrMtf
),
495 mnCurrMtfLoadFlags( rSrc
.mnCurrMtfLoadFlags
),
496 maCurrentShapeUnitBounds(),
498 maBounds( rSrc
.maBounds
),
500 mpIntrinsicAnimationActivity(),
501 mnAttributeTransformationState(0),
502 mnAttributeClipState(0),
503 mnAttributeAlphaState(0),
504 mnAttributePositionState(0),
505 mnAttributeContentState(0),
506 mnAttributeVisibilityState(0),
508 mxComponentContext( rSrc
.mxComponentContext
),
509 maHyperlinkIndices(),
510 maHyperlinkRegions(),
511 maSubsetting( rTreeNode
, mpCurrMtf
),
512 mnIsAnimatedCount(0),
513 mnAnimationLoopCount(0),
514 mbIsVisible( rSrc
.mbIsVisible
),
515 mbForceUpdate( false ),
516 mbAttributeLayerRevoked( false ),
517 mbDrawingLayerAnim( false ),
518 mbContainsPageField( false )
520 ENSURE_OR_THROW( mxShape
.is(), "DrawShape::DrawShape(): Invalid XShape" );
521 ENSURE_OR_THROW( mpCurrMtf
, "DrawShape::DrawShape(): Invalid metafile" );
523 // xxx todo: currently not implemented for subsetted shapes;
524 // would mean modifying set of hyperlink regions when
525 // subsetting text portions. N.B.: there's already an
526 // issue for this #i72828#
533 DrawShapeSharedPtr
DrawShape::create(
534 const uno::Reference
< drawing::XShape
>& xShape
,
535 const uno::Reference
< drawing::XDrawPage
>& xContainingPage
,
538 const SlideShowContext
& rContext
)
540 DrawShapeSharedPtr
pShape( new DrawShape(xShape
,
546 if( pShape
->hasIntrinsicAnimation() )
548 OSL_ASSERT( pShape
->maAnimationFrames
.empty() );
549 if( pShape
->getNumberOfTreeNodes(
550 DocTreeNode::NodeType::LogicalParagraph
) > 0 )
552 pShape
->mpIntrinsicAnimationActivity
=
553 createDrawingLayerAnimActivity(
559 if( pShape
->hasHyperlinks() )
560 rContext
.mpSubsettableShapeManager
->addHyperlinkArea( pShape
);
565 DrawShapeSharedPtr
DrawShape::create(
566 const uno::Reference
< drawing::XShape
>& xShape
,
567 const uno::Reference
< drawing::XDrawPage
>& xContainingPage
,
569 std::shared_ptr
<Graphic
> pGraphic
,
570 const SlideShowContext
& rContext
)
572 DrawShapeSharedPtr
pShape( new DrawShape(xShape
,
578 if( pShape
->hasIntrinsicAnimation() )
580 OSL_ASSERT( !pShape
->maAnimationFrames
.empty() );
582 std::vector
<double> aTimeout
;
584 pShape
->maAnimationFrames
.begin(),
585 pShape
->maAnimationFrames
.end(),
586 std::back_insert_iterator
< std::vector
<double> >( aTimeout
),
587 std::mem_fn(&MtfAnimationFrame::getDuration
) );
589 WakeupEventSharedPtr pWakeupEvent
=
590 std::make_shared
<WakeupEvent
>( rContext
.mrEventQueue
.getTimer(),
591 rContext
.mrActivitiesQueue
);
593 ActivitySharedPtr pActivity
=
594 createIntrinsicAnimationActivity(
599 pShape
->mnAnimationLoopCount
);
601 pWakeupEvent
->setActivity( pActivity
);
602 pShape
->mpIntrinsicAnimationActivity
= pActivity
;
605 OSL_ENSURE( !pShape
->hasHyperlinks(),
606 "DrawShape::create(): graphic-only shapes must not have hyperlinks!" );
611 DrawShape::~DrawShape()
615 // dispose intrinsic animation activity, else, it will
617 ActivitySharedPtr
pActivity( mpIntrinsicAnimationActivity
.lock() );
619 pActivity
->dispose();
621 catch (uno::Exception
const &)
623 DBG_UNHANDLED_EXCEPTION("slideshow");
627 uno::Reference
< drawing::XShape
> DrawShape::getXShape() const
632 void DrawShape::addViewLayer( const ViewLayerSharedPtr
& rNewLayer
,
636 if( ::std::any_of( maViewShapes
.begin(),
639 ( const ViewShapeSharedPtr
& pShape
)
640 { return rNewLayer
== pShape
->getViewLayer(); } ) )
642 // yes, nothing to do
646 ViewShapeSharedPtr pNewShape
= std::make_shared
<ViewShape
>( rNewLayer
);
648 maViewShapes
.push_back( pNewShape
);
650 // pass on animation state
651 if( mnIsAnimatedCount
)
653 for( int i
=0; i
<mnIsAnimatedCount
; ++i
)
654 pNewShape
->enterAnimationMode();
657 // render the Shape on the newly added ViewLayer
660 pNewShape
->update( mpCurrMtf
,
667 bool DrawShape::removeViewLayer( const ViewLayerSharedPtr
& rLayer
)
669 const ViewShapeVector::iterator
aEnd( maViewShapes
.end() );
671 OSL_ENSURE( ::std::count_if(maViewShapes
.begin(),
674 ( const ViewShapeSharedPtr
& pShape
)
675 { return rLayer
== pShape
->getViewLayer(); } ) < 2,
676 "DrawShape::removeViewLayer(): Duplicate ViewLayer entries!" );
679 // TODO : needed for the moment since ANDROID doesn't know size_t return from std::erase_if
681 ViewShapeVector::iterator aIter
;
683 if( (aIter
=::std::remove_if( maViewShapes
.begin(),
686 ( const ViewShapeSharedPtr
& pShape
)
687 { return rLayer
== pShape
->getViewLayer(); } ) ) == aEnd
)
689 // view layer seemingly was not added, failed
693 // actually erase from container
694 maViewShapes
.erase( aIter
, aEnd
);
698 size_t nb
= std::erase_if(maViewShapes
,
700 ( const ViewShapeSharedPtr
& pShape
)
701 { return rLayer
== pShape
->getViewLayer(); } );
702 // if nb == 0, it means view shape seemingly was not added, failed
707 void DrawShape::clearAllViewLayers()
709 maViewShapes
.clear();
712 bool DrawShape::update() const
720 return implRender( getUpdateFlags() );
724 bool DrawShape::render() const
726 // force redraw. Have to also pass on the update flags,
727 // because e.g. content update (regeneration of the
728 // metafile renderer) is normally not performed. A simple
729 // UpdateFlags::Force would only paint the metafile in its
731 return implRender( UpdateFlags::Force
| getUpdateFlags() );
734 bool DrawShape::isContentChanged() const
736 return mbForceUpdate
||
737 getUpdateFlags() != UpdateFlags::NONE
;
741 ::basegfx::B2DRectangle
DrawShape::getBounds() const
743 // little optimization: for non-modified shapes, we don't
744 // create an ShapeAttributeStack, and therefore also don't
746 return getShapePosSize( maBounds
,
750 ::basegfx::B2DRectangle
DrawShape::getDomBounds() const
755 ::basegfx::B2DRectangle
DrawShape::getUpdateArea() const
757 ::basegfx::B2DRectangle aBounds
;
759 // an already empty shape bound need no further
760 // treatment. In fact, any changes applied below would
761 // actually remove the special empty state, thus, don't
763 if( !maBounds
.isEmpty() )
765 basegfx::B2DRectangle
aUnitBounds(0.0,0.0,1.0,1.0);
767 if( !maViewShapes
.empty() )
768 aUnitBounds
= getActualUnitShapeBounds();
770 if( !aUnitBounds
.isEmpty() )
772 if( mpAttributeLayer
)
774 // calc actual shape area (in user coordinate
775 // space) from the transformation as given by the
776 // shape attribute layer
777 aBounds
= getShapeUpdateArea( aUnitBounds
,
778 getShapeTransformation( getBounds(),
784 // no attribute layer, thus, the true shape bounds
785 // can be directly derived from the XShape bound
787 aBounds
= getShapeUpdateArea( aUnitBounds
,
791 if( !maViewShapes
.empty() )
793 // determine border needed for antialiasing the shape
794 ::basegfx::B2DSize
aAABorder(0.0,0.0);
796 // for every view, get AA border and 'expand' aAABorder
798 for( const auto& rViewShape
: maViewShapes
)
800 const ::basegfx::B2DSize
rShapeBorder( rViewShape
->getAntialiasingBorder() );
802 aAABorder
.setWidth( ::std::max(
803 rShapeBorder
.getWidth(),
804 aAABorder
.getWidth() ) );
805 aAABorder
.setHeight( ::std::max(
806 rShapeBorder
.getHeight(),
807 aAABorder
.getHeight() ) );
810 // add calculated AA border to aBounds
811 aBounds
= ::basegfx::B2DRectangle( aBounds
.getMinX() - aAABorder
.getWidth(),
812 aBounds
.getMinY() - aAABorder
.getHeight(),
813 aBounds
.getMaxX() + aAABorder
.getWidth(),
814 aBounds
.getMaxY() + aAABorder
.getHeight() );
822 bool DrawShape::isVisible() const
824 bool bIsVisible( mbIsVisible
);
826 if( mpAttributeLayer
)
828 // check whether visibility and alpha are not default
829 // (mpAttributeLayer->isVisibilityValid() returns true
830 // then): bVisible becomes true, if shape visibility
831 // is on and alpha is not 0.0 (fully transparent)
832 if( mpAttributeLayer
->isVisibilityValid() )
833 bIsVisible
= mpAttributeLayer
->getVisibility();
835 // only touch bIsVisible, if the shape is still
836 // visible - if getVisibility already made us
837 // invisible, no alpha value will make us appear
839 if( bIsVisible
&& mpAttributeLayer
->isAlphaValid() )
840 bIsVisible
= !::basegfx::fTools::equalZero( mpAttributeLayer
->getAlpha() );
846 double DrawShape::getPriority() const
851 bool DrawShape::isBackgroundDetached() const
853 return mnIsAnimatedCount
> 0;
856 bool DrawShape::hasIntrinsicAnimation() const
858 return (!maAnimationFrames
.empty() || mbDrawingLayerAnim
);
861 void DrawShape::setIntrinsicAnimationFrame( ::std::size_t nCurrFrame
)
863 ENSURE_OR_RETURN_VOID( nCurrFrame
< maAnimationFrames
.size(),
864 "DrawShape::setIntrinsicAnimationFrame(): frame index out of bounds" );
866 // Load 1 more frame if needed. (make sure the current frame is loaded)
868 getSomeAnimationFramesFromGraphic(1, nCurrFrame
);
870 if( mnCurrFrame
!= nCurrFrame
)
872 mnCurrFrame
= nCurrFrame
;
873 mpCurrMtf
= maAnimationFrames
[ mnCurrFrame
].mpMtf
;
874 mbForceUpdate
= true;
879 void DrawShape::prepareHyperlinkIndices() const
881 if ( !maHyperlinkIndices
.empty())
883 maHyperlinkIndices
.clear();
884 maHyperlinkRegions
.clear();
887 sal_Int32 nIndex
= 0;
888 for ( MetaAction
* pCurrAct
= mpCurrMtf
->FirstAction();
889 pCurrAct
!= nullptr; pCurrAct
= mpCurrMtf
->NextAction() )
891 if (pCurrAct
->GetType() == MetaActionType::COMMENT
) {
892 MetaCommentAction
* pAct
=
893 static_cast<MetaCommentAction
*>(pCurrAct
);
894 // skip comment if not a special XTEXT comment
895 if (pAct
->GetComment().equalsIgnoreAsciiCase("FIELD_SEQ_BEGIN") &&
896 // e.g. date field doesn't have data!
897 // currently assuming that only url field, this is
898 // somehow fragile! xxx todo if possible
899 pAct
->GetData() != nullptr &&
900 pAct
->GetDataSize() > 0)
902 if (!maHyperlinkIndices
.empty() &&
903 maHyperlinkIndices
.back().second
== -1) {
904 SAL_WARN( "slideshow", "### pending FIELD_SEQ_END!" );
905 maHyperlinkIndices
.pop_back();
906 maHyperlinkRegions
.pop_back();
908 maHyperlinkIndices
.emplace_back( nIndex
+ 1,
909 -1 /* to be filled below */ );
910 maHyperlinkRegions
.emplace_back(
911 basegfx::B2DRectangle(),
913 reinterpret_cast<sal_Unicode
const*>(
915 pAct
->GetDataSize() / sizeof(sal_Unicode
) )
918 else if (pAct
->GetComment().equalsIgnoreAsciiCase("FIELD_SEQ_END") &&
919 // pending end is expected:
920 !maHyperlinkIndices
.empty() &&
921 maHyperlinkIndices
.back().second
== -1)
923 maHyperlinkIndices
.back().second
= nIndex
;
925 else if (pAct
->GetComment().equalsIgnoreAsciiCase("FIELD_SEQ_BEGIN;PageField"))
927 mbContainsPageField
= true;
932 nIndex
+= getNextActionOffset(pCurrAct
);
934 if (!maHyperlinkIndices
.empty() &&
935 maHyperlinkIndices
.back().second
== -1) {
936 SAL_WARN( "slideshow", "### pending FIELD_SEQ_END!" );
937 maHyperlinkIndices
.pop_back();
938 maHyperlinkRegions
.pop_back();
940 OSL_ASSERT( maHyperlinkIndices
.size() == maHyperlinkRegions
.size());
943 bool DrawShape::hasHyperlinks() const
945 return ! maHyperlinkRegions
.empty();
948 HyperlinkArea::HyperlinkRegions
DrawShape::getHyperlinkRegions() const
950 OSL_ASSERT( !maViewShapes
.empty() );
953 return HyperlinkArea::HyperlinkRegions();
955 // late init, determine regions:
956 if( !maHyperlinkRegions
.empty() &&
957 !maViewShapes
.empty() &&
958 // region already inited?
959 maHyperlinkRegions
.front().first
.getWidth() == 0 &&
960 maHyperlinkRegions
.front().first
.getHeight() == 0 &&
961 maHyperlinkRegions
.size() == maHyperlinkIndices
.size() )
963 // TODO(Q2): Although this _is_ currently
964 // view-agnostic, it might not stay like that.
965 ViewShapeSharedPtr
const& pViewShape
= maViewShapes
.front();
966 cppcanvas::CanvasSharedPtr
const pCanvas(
967 pViewShape
->getViewLayer()->getCanvas() );
969 // reuse Renderer of first view shape:
970 cppcanvas::RendererSharedPtr
const pRenderer(
971 pViewShape
->getRenderer(
972 pCanvas
, mpCurrMtf
, mpAttributeLayer
) );
974 OSL_ASSERT( pRenderer
);
978 basegfx::B2DHomMatrix
const aOldTransform(
979 pCanvas
->getTransformation() );
980 basegfx::B2DHomMatrix aTransform
;
981 pCanvas
->setTransformation( aTransform
/* empty */ );
984 ::cppcanvas::Canvas
* pTmpCanvas
= pCanvas
.get();
985 comphelper::ScopeGuard
const resetOldTransformation(
986 [&aOldTransform
, &pTmpCanvas
]()
987 { return pTmpCanvas
->setTransformation( aOldTransform
); } );
989 aTransform
.scale( maBounds
.getWidth(),
990 maBounds
.getHeight() );
991 pRenderer
->setTransformation( aTransform
);
992 pRenderer
->setClip();
994 for( std::size_t pos
= maHyperlinkRegions
.size(); pos
--; )
997 HyperlinkIndexPair
const& rIndices
= maHyperlinkIndices
[pos
];
998 basegfx::B2DRectangle
const region(
999 pRenderer
->getSubsetArea( rIndices
.first
,
1001 maHyperlinkRegions
[pos
].first
= region
;
1006 // shift shape-relative hyperlink regions to
1007 // slide-absolute position
1009 HyperlinkRegions aTranslatedRegions
;
1011 // increase capacity to same size as the container for
1012 // shape-relative hyperlink regions to avoid reallocation
1013 aTranslatedRegions
.reserve( maHyperlinkRegions
.size() );
1014 const basegfx::B2DPoint
aOffset(getBounds().getMinimum());
1015 for( const auto& cp
: maHyperlinkRegions
)
1017 basegfx::B2DRange
const& relRegion( cp
.first
);
1018 aTranslatedRegions
.emplace_back(
1020 relRegion
.getMinimum() + aOffset
,
1021 relRegion
.getMaximum() + aOffset
),
1025 return aTranslatedRegions
;
1028 double DrawShape::getHyperlinkPriority() const
1030 return getPriority();
1034 // AnimatableShape methods
1037 void DrawShape::enterAnimationMode()
1039 OSL_ENSURE( !maViewShapes
.empty(),
1040 "DrawShape::enterAnimationMode(): called on DrawShape without views" );
1042 if( mnIsAnimatedCount
== 0 )
1044 // notify all ViewShapes, by calling their enterAnimationMode method.
1045 // We're now entering animation mode
1046 for( const auto& rViewShape
: maViewShapes
)
1047 rViewShape
->enterAnimationMode();
1050 ++mnIsAnimatedCount
;
1053 void DrawShape::leaveAnimationMode()
1055 OSL_ENSURE( !maViewShapes
.empty(),
1056 "DrawShape::leaveAnimationMode(): called on DrawShape without views" );
1058 --mnIsAnimatedCount
;
1060 if( mnIsAnimatedCount
== 0 )
1062 // notify all ViewShapes, by calling their leaveAnimationMode method.
1063 // we're now leaving animation mode
1064 for( const auto& rViewShape
: maViewShapes
)
1065 rViewShape
->leaveAnimationMode();
1070 // AttributableShape methods
1073 ShapeAttributeLayerSharedPtr
DrawShape::createAttributeLayer()
1075 // create new layer, with last as its new child
1076 mpAttributeLayer
= std::make_shared
<ShapeAttributeLayer
>( mpAttributeLayer
);
1078 // Update the local state ids to reflect those of the new layer.
1081 return mpAttributeLayer
;
1084 bool DrawShape::revokeAttributeLayer( const ShapeAttributeLayerSharedPtr
& rLayer
)
1086 if( !mpAttributeLayer
)
1087 return false; // no layers
1089 if( mpAttributeLayer
== rLayer
)
1091 // it's the toplevel layer
1092 mpAttributeLayer
= mpAttributeLayer
->getChildLayer();
1094 // force content redraw, all state variables have
1096 mbAttributeLayerRevoked
= true;
1102 // pass on to the layer, to try its children
1103 return mpAttributeLayer
->revokeChildLayer( rLayer
);
1107 ShapeAttributeLayerSharedPtr
DrawShape::getTopmostAttributeLayer() const
1109 return mpAttributeLayer
;
1112 void DrawShape::setVisibility( bool bVisible
)
1114 if( mbIsVisible
!= bVisible
)
1116 mbIsVisible
= bVisible
;
1117 mbForceUpdate
= true;
1121 const DocTreeNodeSupplier
& DrawShape::getTreeNodeSupplier() const
1126 DocTreeNodeSupplier
& DrawShape::getTreeNodeSupplier()
1131 DocTreeNode
DrawShape::getSubsetNode() const
1133 // forward to delegate
1134 return maSubsetting
.getSubsetNode();
1137 AttributableShapeSharedPtr
DrawShape::getSubset( const DocTreeNode
& rTreeNode
) const
1139 // forward to delegate
1140 return maSubsetting
.getSubsetShape( rTreeNode
);
1143 bool DrawShape::createSubset( AttributableShapeSharedPtr
& o_rSubset
,
1144 const DocTreeNode
& rTreeNode
)
1146 // subset shape already created for this DocTreeNode?
1147 AttributableShapeSharedPtr
pSubset( maSubsetting
.getSubsetShape( rTreeNode
) );
1149 // when true, this method has created a new subset
1151 bool bNewlyCreated( false );
1155 o_rSubset
= std::move(pSubset
);
1157 // reusing existing subset
1161 // not yet created, init entry
1162 o_rSubset
.reset( new DrawShape( *this,
1164 // TODO(Q3): That's a
1166 // that start and end
1167 // index will always
1168 // be less than 65535
1170 rTreeNode
.getStartIndex()/double(SAL_MAX_INT16
) ));
1172 bNewlyCreated
= true; // subset newly created
1175 // always register shape at DrawShapeSubsetting, to keep
1176 // refcount up-to-date
1177 maSubsetting
.addSubsetShape( o_rSubset
);
1179 // flush bounds cache
1180 maCurrentShapeUnitBounds
.reset();
1182 return bNewlyCreated
;
1185 bool DrawShape::revokeSubset( const AttributableShapeSharedPtr
& rShape
)
1187 // flush bounds cache
1188 maCurrentShapeUnitBounds
.reset();
1190 // forward to delegate
1191 if( maSubsetting
.revokeSubsetShape( rShape
) )
1193 // force redraw, our content has possibly changed (as
1194 // one of the subsets now display within our shape
1196 mbForceUpdate
= true;
1198 // #i47428# TEMP FIX: synchronize visibility of subset
1201 // TODO(F3): Remove here, and implement
1202 // TEXT_ONLY/BACKGROUND_ONLY with the proverbial
1203 // additional level of indirection: create a
1204 // persistent subset, containing all text/only the
1205 // background respectively. From _that_ object,
1206 // generate the temporary character subset shapes.
1207 const ShapeAttributeLayerSharedPtr
xAttrLayer(
1208 rShape
->getTopmostAttributeLayer() );
1210 xAttrLayer
->isVisibilityValid() &&
1211 xAttrLayer
->getVisibility() != isVisible() )
1213 const bool bVisibility( xAttrLayer
->getVisibility() );
1215 // visibilities differ - adjust ours, then
1216 if( mpAttributeLayer
)
1217 mpAttributeLayer
->setVisibility( bVisibility
);
1219 mbIsVisible
= bVisibility
;
1230 sal_Int32
DrawShape::getNumberOfTreeNodes( DocTreeNode::NodeType eNodeType
) const // throw ShapeLoadFailedException
1232 return maSubsetting
.getNumberOfTreeNodes( eNodeType
);
1235 DocTreeNode
DrawShape::getTreeNode( sal_Int32 nNodeIndex
,
1236 DocTreeNode::NodeType eNodeType
) const // throw ShapeLoadFailedException
1238 if ( hasHyperlinks())
1240 prepareHyperlinkIndices();
1243 return maSubsetting
.getTreeNode( nNodeIndex
, eNodeType
);
1246 sal_Int32
DrawShape::getNumberOfSubsetTreeNodes ( const DocTreeNode
& rParentNode
,
1247 DocTreeNode::NodeType eNodeType
) const // throw ShapeLoadFailedException
1249 return maSubsetting
.getNumberOfSubsetTreeNodes( rParentNode
, eNodeType
);
1252 DocTreeNode
DrawShape::getSubsetTreeNode( const DocTreeNode
& rParentNode
,
1253 sal_Int32 nNodeIndex
,
1254 DocTreeNode::NodeType eNodeType
) const // throw ShapeLoadFailedException
1256 return maSubsetting
.getSubsetTreeNode( rParentNode
, nNodeIndex
, eNodeType
);
1259 void DrawShape::getSomeAnimationFramesFromGraphic(::std::size_t nFrameCount
,
1260 ::std::size_t nLastToLoad
/* = 0*/)
1262 OSL_ASSERT(mpGraphicLoader
);
1264 //load nFrameCount frames or to nLastToLoad
1265 ::std::size_t nFramesToLoad
= nFrameCount
;
1266 if (nLastToLoad
> mpGraphicLoader
->mnLoadedFrames
+ nFrameCount
)
1267 nFramesToLoad
= nLastToLoad
- mpGraphicLoader
->mnLoadedFrames
;
1269 getAnimationFromGraphic(maAnimationFrames
, mnAnimationLoopCount
,
1270 mpGraphicLoader
->mpGraphic
, mpGraphicLoader
->mpVDev
,
1271 mpGraphicLoader
->mpVDevMask
, mpGraphicLoader
->mnLoadedFrames
,
1274 // If the Animation is fully loaded, no need to load anymore.
1275 if (mpGraphicLoader
->mnLoadedFrames
>= maAnimationFrames
.size())
1277 mpGraphicLoader
.reset();
1281 DelayedGraphicLoader::DelayedGraphicLoader(std::shared_ptr
<Graphic
> pGraphic
)
1282 : mpGraphic(std::move(pGraphic
))
1283 , mpVDevMask(DeviceFormat::WITHOUT_ALPHA
)
1288 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */