LibreOffice Module slideshow (master) 1
targetpropertiescreator.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
20#include <com/sun/star/animations/XIterateContainer.hpp>
21#include <com/sun/star/presentation/ParagraphTarget.hpp>
22#include <com/sun/star/drawing/XShape.hpp>
23#include <com/sun/star/animations/AnimationNodeType.hpp>
24#include <com/sun/star/animations/XAnimate.hpp>
26
27#include <unordered_map>
28#include <utility>
29#include <vector>
30
32#include <tools.hxx>
33
34namespace slideshow::internal
35{
36 namespace
37 {
38 // Vector containing all properties for a given shape
39 typedef ::std::vector< beans::NamedValue > VectorOfNamedValues;
40
47 struct ShapeHashKey
48 {
50 uno::Reference< drawing::XShape > mxRef;
51
58
60 bool operator==( const ShapeHashKey& rRHS ) const
61 {
62 return mxRef == rRHS.mxRef && mnParagraphIndex == rRHS.mnParagraphIndex;
63 }
64 };
65
66 // A hash functor for ShapeHashKey objects
67 struct ShapeKeyHasher
68 {
69 ::std::size_t operator()( const ShapeHashKey& rKey ) const
70 {
71 // TODO(P2): Maybe a better hash function would be to
72 // spread mnParagraphIndex to 32 bit: a0b0c0d0e0... Hakmem
73 // should have a formula.
74
75 // Yes it has:
76 // x = (x & 0x0000FF00) << 8) | (x >> 8) & 0x0000FF00 | x & 0xFF0000FF;
77 // x = (x & 0x00F000F0) << 4) | (x >> 4) & 0x00F000F0 | x & 0xF00FF00F;
78 // x = (x & 0x0C0C0C0C) << 2) | (x >> 2) & 0x0C0C0C0C | x & 0xC3C3C3C3;
79 // x = (x & 0x22222222) << 1) | (x >> 1) & 0x22222222 | x & 0x99999999;
80
81 // Costs about 17 cycles on a RISC machine with infinite
82 // instruction level parallelism (~42 basic
83 // instructions). Thus I truly doubt this pays off...
84 return reinterpret_cast< ::std::size_t >(rKey.mxRef.get()) ^ (rKey.mnParagraphIndex << 16);
85 }
86 };
87
88 // A hash map which maps a XShape to the corresponding vector of initial properties
89 typedef std::unordered_map< ShapeHashKey, VectorOfNamedValues, ShapeKeyHasher > XShapeToNamedValuesMap;
90
91
92 class NodeFunctor
93 {
94 public:
95 explicit NodeFunctor(
96 XShapeToNamedValuesMap& rShapeHash,
97 bool bInitial )
98 : mrShapeHash( rShapeHash ),
100 mnParagraphIndex( -1 ),
101 mbInitial( bInitial)
102 {
103 }
104
105 NodeFunctor( XShapeToNamedValuesMap& rShapeHash,
106 uno::Reference< drawing::XShape > xTargetShape,
107 sal_Int16 nParagraphIndex,
108 bool bInitial) :
109 mrShapeHash( rShapeHash ),
110 mxTargetShape(std::move( xTargetShape )),
111 mnParagraphIndex( nParagraphIndex ),
112 mbInitial( bInitial )
113 {
114 }
115
116 void operator()( const uno::Reference< animations::XAnimationNode >& xNode ) const
117 {
118 if( !xNode.is() )
119 {
120 OSL_FAIL( "AnimCore: NodeFunctor::operator(): invalid XAnimationNode" );
121 return;
122 }
123
124 uno::Reference< drawing::XShape > xTargetShape( mxTargetShape );
125 sal_Int16 nParagraphIndex( mnParagraphIndex );
126
127 switch( xNode->getType() )
128 {
129 case animations::AnimationNodeType::ITERATE:
130 {
131 // extract target shape from iterate node
132 // (will override the target for all children)
133
134 uno::Reference< animations::XIterateContainer > xIterNode( xNode,
135 uno::UNO_QUERY );
136
137 // TODO(E1): I'm not too sure what to expect here...
138 if( !xIterNode->getTarget().hasValue() )
139 {
140 OSL_FAIL( "animcore: NodeFunctor::operator(): no target on ITERATE node" );
141 return;
142 }
143
144 xTargetShape.set( xIterNode->getTarget(),
145 uno::UNO_QUERY );
146
147 if( !xTargetShape.is() )
148 {
149 css::presentation::ParagraphTarget aTarget;
150
151 // no shape provided. Maybe a ParagraphTarget?
152 if( !(xIterNode->getTarget() >>= aTarget) )
153 {
154 OSL_FAIL( "animcore: NodeFunctor::operator(): could not extract any "
155 "target information" );
156 return;
157 }
158
159 xTargetShape = aTarget.Shape;
160 nParagraphIndex = aTarget.Paragraph;
161
162 if( !xTargetShape.is() )
163 {
164 OSL_FAIL( "animcore: NodeFunctor::operator(): invalid shape in ParagraphTarget" );
165 return;
166 }
167 }
168 [[fallthrough]];
169 }
170 case animations::AnimationNodeType::PAR:
171 case animations::AnimationNodeType::SEQ:
172 {
174 NodeFunctor aFunctor( mrShapeHash,
175 xTargetShape,
176 nParagraphIndex,
177 mbInitial );
178 if( !for_each_childNode( xNode, aFunctor ) )
179 {
180 OSL_FAIL( "AnimCore: NodeFunctor::operator(): child node iteration failed, "
181 "or extraneous container nodes encountered" );
182 }
183 }
184 break;
185
186 case animations::AnimationNodeType::CUSTOM:
187 case animations::AnimationNodeType::ANIMATE:
188 case animations::AnimationNodeType::ANIMATEMOTION:
189 case animations::AnimationNodeType::ANIMATECOLOR:
190 case animations::AnimationNodeType::ANIMATETRANSFORM:
191 case animations::AnimationNodeType::TRANSITIONFILTER:
192 case animations::AnimationNodeType::AUDIO:
193 /*default:
194 // ignore this node, no valuable content for now.
195 break;*/
196
197 case animations::AnimationNodeType::SET:
198 {
199 // evaluate set node content
200 uno::Reference< animations::XAnimate > xAnimateNode( xNode,
201 uno::UNO_QUERY );
202
203 if( !xAnimateNode.is() )
204 break; // invalid node
205
206 // determine target shape (if any)
207 ShapeHashKey aTarget;
208 if( xTargetShape.is() )
209 {
210 // override target shape with parent-supplied
211 aTarget.mxRef = xTargetShape;
212 aTarget.mnParagraphIndex = nParagraphIndex;
213 }
214 else
215 {
216 // no parent-supplied target, retrieve
217 // node target
218 if( xAnimateNode->getTarget() >>= aTarget.mxRef )
219 {
220 // pure shape target - set paragraph
221 // index to magic
222 aTarget.mnParagraphIndex = -1;
223 }
224 else
225 {
226 // not a pure shape target - maybe a
227 // ParagraphTarget?
228 presentation::ParagraphTarget aUnoTarget;
229
230 if( !(xAnimateNode->getTarget() >>= aUnoTarget) )
231 {
232 OSL_FAIL( "AnimCore: NodeFunctor::operator(): unknown target type encountered" );
233 break;
234 }
235
236 aTarget.mxRef = aUnoTarget.Shape;
237 aTarget.mnParagraphIndex = aUnoTarget.Paragraph;
238 }
239 }
240
241 if( !aTarget.mxRef.is() )
242 {
243 OSL_FAIL( "AnimCore: NodeFunctor::operator(): Found target, but XShape is NULL" );
244 break; // invalid target XShape
245 }
246
247 // check whether we already have an entry for
248 // this target (we only take the first set
249 // effect for every shape) - but keep going if
250 // we're requested the final state (which
251 // eventually gets overwritten in the
252 // unordered list, see tdf#96083)
253 if( mbInitial && mrShapeHash.find( aTarget ) != mrShapeHash.end() )
254 break; // already an entry in existence for given XShape
255
256 // if this is an appear effect, hide shape
257 // initially. This is currently the only place
258 // where a shape effect influences shape
259 // attributes outside it's effective duration.
260 bool bVisible( false );
261 if( xAnimateNode->getAttributeName().equalsIgnoreAsciiCase("visibility") )
262 {
263
264 uno::Any aAny( xAnimateNode->getTo() );
265
266 // try to extract bool value
267 if( !(aAny >>= bVisible) )
268 {
269 // try to extract string
270 OUString aString;
271 if( aAny >>= aString )
272 {
273 // we also take the strings "true" and "false",
274 // as well as "on" and "off" here
275 if( aString.equalsIgnoreAsciiCase("true") ||
276 aString.equalsIgnoreAsciiCase("on") )
277 {
278 bVisible = true;
279 }
280 if( aString.equalsIgnoreAsciiCase("false") ||
281 aString.equalsIgnoreAsciiCase("off") )
282 {
283 bVisible = false;
284 }
285 }
286 }
287 }
288
289 // if initial anim sets shape visible, set it
290 // to invisible. If we're asked for the final
291 // state, don't do anything obviously
292 if(mbInitial)
294
295 // target is set the 'visible' value,
296 // so we should record the opposite value
297 mrShapeHash.emplace(
298 aTarget,
299 VectorOfNamedValues(
300 1,
301 beans::NamedValue(
302 //xAnimateNode->getAttributeName(),
303 "visibility",
304 uno::Any( bVisible ) ) ) );
305 break;
306 }
307 }
308 }
309
310 private:
311 XShapeToNamedValuesMap& mrShapeHash;
312 uno::Reference< drawing::XShape > mxTargetShape;
313 sal_Int16 mnParagraphIndex;
314
315 // get initial or final state
317 };
318 }
319
321 (
323 bool bInitial
324 ) //throw (uno::RuntimeException, std::exception)
325 {
326 // scan all nodes for visibility changes, and record first
327 // 'visibility=true' for each shape
328 XShapeToNamedValuesMap aShapeHash( 101 );
329
330 NodeFunctor aFunctor(
331 aShapeHash,
332 bInitial );
333
334 // TODO(F1): Maybe limit functor application to main sequence
335 // alone (CL said something that shape visibility is only
336 // affected by effects in the main sequence for PPT).
337
338 // OTOH, client code can pass us only the main sequence (which
339 // it actually does right now, for the slideshow implementation).
340 aFunctor( xRootNode );
341
342 // output to result sequence
343 uno::Sequence< animations::TargetProperties > aRes( aShapeHash.size() );
344 auto aResRange = asNonConstRange(aRes);
345
346 ::std::size_t nCurrIndex(0);
347 for( const auto& rIter : aShapeHash )
348 {
349 animations::TargetProperties& rCurrProps( aResRange[ nCurrIndex++ ] );
350
351 if( rIter.first.mnParagraphIndex == -1 )
352 {
353 rCurrProps.Target <<= rIter.first.mxRef;
354 }
355 else
356 {
357 rCurrProps.Target <<=
358 presentation::ParagraphTarget(
359 rIter.first.mxRef,
360 rIter.first.mnParagraphIndex );
361 }
362
363 rCurrProps.Properties = ::comphelper::containerToSequence( rIter.second );
364 }
365
366 return aRes;
367 }
368
369} // namespace slideshow::internal
370
371/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
uno::Sequence< animations::TargetProperties > createTargetProperties(const uno::Reference< animations::XAnimationNode > &rootNode, bool bInitial)
Generate shape property list - set bInitial to true for initial slide state.
bool operator==(const HSLColor &rLHS, const HSLColor &rRHS)
Definition: color.cxx:189
bool for_each_childNode(const css::uno::Reference< css::animations::XAnimationNode > &xNode, Functor &rFunctor)
Apply given functor to every animation node child.
Definition: tools.hxx:365
uno::Reference< drawing::XShape > mxTargetShape
XShapeToNamedValuesMap & mrShapeHash
uno::Reference< drawing::XShape > mxRef
Shape target.
sal_Int16 mnParagraphIndex
Paragraph index.
bool bVisible