346 lines
15 KiB
C++
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: */
|