1
0
Fork 0
libreoffice/slideshow/source/engine/slide/targetpropertiescreator.cxx
Daniel Baumann 8e63e14cf6
Adding upstream version 4:25.2.3.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
2025-06-22 16:20:04 +02:00

346 lines
15 KiB
C++

/* -*- 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 .
*/
#include <animations/animationnodehelper.hxx>
#include <com/sun/star/animations/XIterateContainer.hpp>
#include <com/sun/star/presentation/ParagraphTarget.hpp>
#include <com/sun/star/drawing/XShape.hpp>
#include <com/sun/star/animations/AnimationNodeType.hpp>
#include <com/sun/star/animations/XAnimate.hpp>
#include <comphelper/sequence.hxx>
#include <unordered_map>
#include <utility>
#include <vector>
#include "targetpropertiescreator.hxx"
#include <tools.hxx>
namespace slideshow::internal
{
namespace
{
// Vector containing all properties for a given shape
typedef ::std::vector< beans::NamedValue > VectorOfNamedValues;
/** The hash map key
This key contains both XShape reference and a paragraph
index, as we somehow have to handle shape and paragraph
targets with the same data structure.
*/
struct ShapeHashKey
{
/// Shape target
uno::Reference< drawing::XShape > mxRef;
/** Paragraph index.
If this is a pure shape target, mnParagraphIndex is
set to -1.
*/
sal_Int16 mnParagraphIndex;
/// Comparison needed for unordered_map
bool operator==( const ShapeHashKey& rRHS ) const
{
return mxRef == rRHS.mxRef && mnParagraphIndex == rRHS.mnParagraphIndex;
}
};
// A hash functor for ShapeHashKey objects
struct ShapeKeyHasher
{
::std::size_t operator()( const ShapeHashKey& rKey ) const
{
// TODO(P2): Maybe a better hash function would be to
// spread mnParagraphIndex to 32 bit: a0b0c0d0e0... Hakmem
// should have a formula.
// Yes it has:
// x = (x & 0x0000FF00) << 8) | (x >> 8) & 0x0000FF00 | x & 0xFF0000FF;
// x = (x & 0x00F000F0) << 4) | (x >> 4) & 0x00F000F0 | x & 0xF00FF00F;
// x = (x & 0x0C0C0C0C) << 2) | (x >> 2) & 0x0C0C0C0C | x & 0xC3C3C3C3;
// x = (x & 0x22222222) << 1) | (x >> 1) & 0x22222222 | x & 0x99999999;
// Costs about 17 cycles on a RISC machine with infinite
// instruction level parallelism (~42 basic
// instructions). Thus I truly doubt this pays off...
return reinterpret_cast< ::std::size_t >(rKey.mxRef.get()) ^ (rKey.mnParagraphIndex << 16);
}
};
// A hash map which maps a XShape to the corresponding vector of initial properties
typedef std::unordered_map< ShapeHashKey, VectorOfNamedValues, ShapeKeyHasher > XShapeToNamedValuesMap;
class NodeFunctor
{
public:
explicit NodeFunctor(
XShapeToNamedValuesMap& rShapeHash,
bool bInitial )
: mrShapeHash( rShapeHash ),
mxTargetShape(),
mnParagraphIndex( -1 ),
mbInitial( bInitial)
{
}
NodeFunctor( XShapeToNamedValuesMap& rShapeHash,
uno::Reference< drawing::XShape > xTargetShape,
sal_Int16 nParagraphIndex,
bool bInitial) :
mrShapeHash( rShapeHash ),
mxTargetShape(std::move( xTargetShape )),
mnParagraphIndex( nParagraphIndex ),
mbInitial( bInitial )
{
}
void operator()( const uno::Reference< animations::XAnimationNode >& xNode ) const
{
if( !xNode.is() )
{
OSL_FAIL( "AnimCore: NodeFunctor::operator(): invalid XAnimationNode" );
return;
}
uno::Reference< drawing::XShape > xTargetShape( mxTargetShape );
sal_Int16 nParagraphIndex( mnParagraphIndex );
switch( xNode->getType() )
{
case animations::AnimationNodeType::ITERATE:
{
// extract target shape from iterate node
// (will override the target for all children)
uno::Reference< animations::XIterateContainer > xIterNode( xNode,
uno::UNO_QUERY );
// TODO(E1): I'm not too sure what to expect here...
if( !xIterNode->getTarget().hasValue() )
{
OSL_FAIL( "animcore: NodeFunctor::operator(): no target on ITERATE node" );
return;
}
xTargetShape.set( xIterNode->getTarget(),
uno::UNO_QUERY );
if( !xTargetShape.is() )
{
css::presentation::ParagraphTarget aTarget;
// no shape provided. Maybe a ParagraphTarget?
if( !(xIterNode->getTarget() >>= aTarget) )
{
OSL_FAIL( "animcore: NodeFunctor::operator(): could not extract any "
"target information" );
return;
}
xTargetShape = aTarget.Shape;
nParagraphIndex = aTarget.Paragraph;
if( !xTargetShape.is() )
{
OSL_FAIL( "animcore: NodeFunctor::operator(): invalid shape in ParagraphTarget" );
return;
}
}
[[fallthrough]];
}
case animations::AnimationNodeType::PAR:
case animations::AnimationNodeType::SEQ:
{
/// forward bInitial
NodeFunctor aFunctor( mrShapeHash,
xTargetShape,
nParagraphIndex,
mbInitial );
if( !for_each_childNode( xNode, aFunctor ) )
{
OSL_FAIL( "AnimCore: NodeFunctor::operator(): child node iteration failed, "
"or extraneous container nodes encountered" );
}
}
break;
case animations::AnimationNodeType::CUSTOM:
case animations::AnimationNodeType::ANIMATE:
case animations::AnimationNodeType::ANIMATEMOTION:
case animations::AnimationNodeType::ANIMATECOLOR:
case animations::AnimationNodeType::ANIMATETRANSFORM:
case animations::AnimationNodeType::TRANSITIONFILTER:
case animations::AnimationNodeType::AUDIO:
/*default:
// ignore this node, no valuable content for now.
break;*/
case animations::AnimationNodeType::SET:
{
// evaluate set node content
uno::Reference< animations::XAnimate > xAnimateNode( xNode,
uno::UNO_QUERY );
if( !xAnimateNode.is() )
break; // invalid node
// determine target shape (if any)
ShapeHashKey aTarget;
if( xTargetShape.is() )
{
// override target shape with parent-supplied
aTarget.mxRef = std::move(xTargetShape);
aTarget.mnParagraphIndex = nParagraphIndex;
}
else
{
// no parent-supplied target, retrieve
// node target
if( xAnimateNode->getTarget() >>= aTarget.mxRef )
{
// pure shape target - set paragraph
// index to magic
aTarget.mnParagraphIndex = -1;
}
else
{
// not a pure shape target - maybe a
// ParagraphTarget?
presentation::ParagraphTarget aUnoTarget;
if( !(xAnimateNode->getTarget() >>= aUnoTarget) )
{
OSL_FAIL( "AnimCore: NodeFunctor::operator(): unknown target type encountered" );
break;
}
aTarget.mxRef = aUnoTarget.Shape;
aTarget.mnParagraphIndex = aUnoTarget.Paragraph;
}
}
if( !aTarget.mxRef.is() )
{
OSL_FAIL( "AnimCore: NodeFunctor::operator(): Found target, but XShape is NULL" );
break; // invalid target XShape
}
// check whether we already have an entry for
// this target (we only take the first set
// effect for every shape) - but keep going if
// we're requested the final state (which
// eventually gets overwritten in the
// unordered list, see tdf#96083)
if( mbInitial && mrShapeHash.find( aTarget ) != mrShapeHash.end() )
break; // already an entry in existence for given XShape
// if this is an appear effect, hide shape
// initially. This is currently the only place
// where a shape effect influences shape
// attributes outside it's effective duration.
bool bVisible = false;
anim::getVisibilityProperty(xAnimateNode, bVisible);
// if initial anim sets shape visible, set it
// to invisible. If we're asked for the final
// state, don't do anything obviously
if(mbInitial)
bVisible = !bVisible;
// target is set the 'visible' value,
// so we should record the opposite value
mrShapeHash.emplace(
aTarget,
VectorOfNamedValues(
1,
beans::NamedValue(
//xAnimateNode->getAttributeName(),
u"visibility"_ustr,
uno::Any( bVisible ) ) ) );
break;
}
}
}
private:
XShapeToNamedValuesMap& mrShapeHash;
uno::Reference< drawing::XShape > mxTargetShape;
sal_Int16 mnParagraphIndex;
// get initial or final state
bool mbInitial;
};
}
uno::Sequence< animations::TargetProperties > TargetPropertiesCreator::createTargetProperties
(
const uno::Reference< animations::XAnimationNode >& xRootNode,
bool bInitial
) //throw (uno::RuntimeException, std::exception)
{
// scan all nodes for visibility changes, and record first
// 'visibility=true' for each shape
XShapeToNamedValuesMap aShapeHash( 101 );
NodeFunctor aFunctor(
aShapeHash,
bInitial );
// TODO(F1): Maybe limit functor application to main sequence
// alone (CL said something that shape visibility is only
// affected by effects in the main sequence for PPT).
// OTOH, client code can pass us only the main sequence (which
// it actually does right now, for the slideshow implementation).
aFunctor( xRootNode );
// output to result sequence
uno::Sequence< animations::TargetProperties > aRes( aShapeHash.size() );
auto aResRange = asNonConstRange(aRes);
::std::size_t nCurrIndex(0);
for( const auto& rIter : aShapeHash )
{
animations::TargetProperties& rCurrProps( aResRange[ nCurrIndex++ ] );
if( rIter.first.mnParagraphIndex == -1 )
{
rCurrProps.Target <<= rIter.first.mxRef;
}
else
{
rCurrProps.Target <<=
presentation::ParagraphTarget(
rIter.first.mxRef,
rIter.first.mnParagraphIndex );
}
rCurrProps.Properties = ::comphelper::containerToSequence( rIter.second );
}
return aRes;
}
} // namespace slideshow::internal
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */