1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_svx.hxx"
26 #include <svx/sdr/contact/viewobjectcontact.hxx>
27 #include <svx/sdr/contact/viewcontact.hxx>
28 #include <svx/sdr/contact/objectcontact.hxx>
29 #include <svx/sdr/contact/displayinfo.hxx>
30 #include <vcl/region.hxx>
31 #include <svx/sdr/animation/objectanimator.hxx>
32 #include <svx/sdr/animation/animationstate.hxx>
33 #include <svx/sdr/contact/viewobjectcontactredirector.hxx>
34 #include <basegfx/numeric/ftools.hxx>
35 #include <basegfx/color/bcolor.hxx>
36 #include <drawinglayer/primitive2d/modifiedcolorprimitive2d.hxx>
37 #include <basegfx/tools/canvastools.hxx>
38 #include <drawinglayer/primitive2d/animatedprimitive2d.hxx>
39 #include <drawinglayer/processor2d/baseprocessor2d.hxx>
40 #include <svx/sdr/primitive2d/svx_primitivetypes2d.hxx>
41 #include <svx/sdr/contact/viewobjectcontactredirector.hxx>
42 
43 //////////////////////////////////////////////////////////////////////////////
44 
45 using namespace com::sun::star;
46 
47 //////////////////////////////////////////////////////////////////////////////
48 
49 namespace
50 {
51 	// animated extractor
52 
53 	// Necessary to filter a sequence of animated primitives from
54 	// a sequence of primitives to find out if animated or not. The decision for
55 	// what to decompose is hard-coded and only done for knowingly animated primitives
56 	// to not decompose too deeply and unnecessarily. This implies that the list
57 	// which is view-specific needs to be expanded by hand when new animated objects
58 	// are added. This may eventually be changed to a dynamically configurable approach
59 	// if necessary.
60 	class AnimatedExtractingProcessor2D : public drawinglayer::processor2d::BaseProcessor2D
61 	{
62 	protected:
63         // the found animated primitives
64         drawinglayer::primitive2d::Primitive2DSequence	maPrimitive2DSequence;
65 
66         // bitfield
67 		// text animation allowed?
68 		unsigned										mbTextAnimationAllowed : 1;
69 
70 		// graphic animation allowed?
71 		unsigned										mbGraphicAnimationAllowed : 1;
72 
73 		// as tooling, the process() implementation takes over API handling and calls this
74 		// virtual render method when the primitive implementation is BasePrimitive2D-based.
75 		virtual void processBasePrimitive2D(const drawinglayer::primitive2d::BasePrimitive2D& rCandidate);
76 
77 	public:
78 		AnimatedExtractingProcessor2D(
79 			const drawinglayer::geometry::ViewInformation2D& rViewInformation,
80 			bool bTextAnimationAllowed,
81 			bool bGraphicAnimationAllowed);
82         virtual ~AnimatedExtractingProcessor2D();
83 
84 		// data access
getPrimitive2DSequence() const85         const drawinglayer::primitive2d::Primitive2DSequence& getPrimitive2DSequence() const { return maPrimitive2DSequence; }
isTextAnimationAllowed() const86 		bool isTextAnimationAllowed() const { return mbTextAnimationAllowed; }
isGraphicAnimationAllowed() const87 		bool isGraphicAnimationAllowed() const { return mbGraphicAnimationAllowed; }
88 	};
89 
AnimatedExtractingProcessor2D(const drawinglayer::geometry::ViewInformation2D & rViewInformation,bool bTextAnimationAllowed,bool bGraphicAnimationAllowed)90     AnimatedExtractingProcessor2D::AnimatedExtractingProcessor2D(
91 		const drawinglayer::geometry::ViewInformation2D& rViewInformation,
92 		bool bTextAnimationAllowed,
93 		bool bGraphicAnimationAllowed)
94 	:	drawinglayer::processor2d::BaseProcessor2D(rViewInformation),
95         maPrimitive2DSequence(),
96 		mbTextAnimationAllowed(bTextAnimationAllowed),
97 		mbGraphicAnimationAllowed(bGraphicAnimationAllowed)
98 	{
99     }
100 
~AnimatedExtractingProcessor2D()101     AnimatedExtractingProcessor2D::~AnimatedExtractingProcessor2D()
102     {
103     }
104 
processBasePrimitive2D(const drawinglayer::primitive2d::BasePrimitive2D & rCandidate)105     void AnimatedExtractingProcessor2D::processBasePrimitive2D(const drawinglayer::primitive2d::BasePrimitive2D& rCandidate)
106 	{
107 		// known implementation, access directly
108 		switch(rCandidate.getPrimitive2DID())
109 		{
110 			// add and accept animated primitives directly, no need to decompose
111 			case PRIMITIVE2D_ID_ANIMATEDSWITCHPRIMITIVE2D :
112 			case PRIMITIVE2D_ID_ANIMATEDBLINKPRIMITIVE2D :
113 			case PRIMITIVE2D_ID_ANIMATEDINTERPOLATEPRIMITIVE2D :
114 			{
115 				const drawinglayer::primitive2d::AnimatedSwitchPrimitive2D& rSwitchPrimitive = static_cast< const drawinglayer::primitive2d::AnimatedSwitchPrimitive2D& >(rCandidate);
116 
117 				if((rSwitchPrimitive.isTextAnimation() && isTextAnimationAllowed())
118 					|| (rSwitchPrimitive.isGraphicAnimation() && isGraphicAnimationAllowed()))
119 				{
120 					const drawinglayer::primitive2d::Primitive2DReference xReference(const_cast< drawinglayer::primitive2d::BasePrimitive2D* >(&rCandidate));
121                     drawinglayer::primitive2d::appendPrimitive2DReferenceToPrimitive2DSequence(maPrimitive2DSequence, xReference);
122 				}
123 				break;
124 			}
125 
126 			// decompose animated gifs where SdrGrafPrimitive2D produces a GraphicPrimitive2D
127             // which then produces the animation infos (all when used/needed)
128 			case PRIMITIVE2D_ID_SDRGRAFPRIMITIVE2D :
129             case PRIMITIVE2D_ID_GRAPHICPRIMITIVE2D :
130 
131 			// decompose SdrObjects with evtl. animated text
132 			case PRIMITIVE2D_ID_SDRCAPTIONPRIMITIVE2D :
133 			case PRIMITIVE2D_ID_SDRCONNECTORPRIMITIVE2D :
134 			case PRIMITIVE2D_ID_SDRCUSTOMSHAPEPRIMITIVE2D :
135 			case PRIMITIVE2D_ID_SDRELLIPSEPRIMITIVE2D :
136 			case PRIMITIVE2D_ID_SDRELLIPSESEGMENTPRIMITIVE2D :
137 			case PRIMITIVE2D_ID_SDRMEASUREPRIMITIVE2D :
138 			case PRIMITIVE2D_ID_SDRPATHPRIMITIVE2D :
139 			case PRIMITIVE2D_ID_SDRRECTANGLEPRIMITIVE2D :
140 
141             // #121194# With Graphic as Bitmap FillStyle, also check
142             // for primitives filled with animated graphics
143             case PRIMITIVE2D_ID_POLYPOLYGONGRAPHICPRIMITIVE2D:
144             case PRIMITIVE2D_ID_FILLGRAPHICPRIMITIVE2D:
145             case PRIMITIVE2D_ID_TRANSFORMPRIMITIVE2D:
146 
147 			// decompose evtl. animated text contained in MaskPrimitive2D
148 			// or group rimitives
149 			case PRIMITIVE2D_ID_MASKPRIMITIVE2D :
150 			case PRIMITIVE2D_ID_GROUPPRIMITIVE2D :
151 			{
152 				process(rCandidate.get2DDecomposition(getViewInformation2D()));
153 				break;
154 			}
155 
156 			default :
157 			{
158 				// nothing to do for the rest
159 				break;
160 			}
161 		}
162 	}
163 } // end of anonymous namespace
164 
165 //////////////////////////////////////////////////////////////////////////////
166 
167 namespace sdr
168 {
169 	namespace contact
170 	{
ViewObjectContact(ObjectContact & rObjectContact,ViewContact & rViewContact)171 		ViewObjectContact::ViewObjectContact(ObjectContact& rObjectContact, ViewContact& rViewContact)
172 		:	mrObjectContact(rObjectContact),
173 			mrViewContact(rViewContact),
174 			maObjectRange(),
175 			mxPrimitive2DSequence(),
176 			mpPrimitiveAnimation(0),
177 			mbLazyInvalidate(false)
178 		{
179 			// make the ViewContact remember me
180 			mrViewContact.AddViewObjectContact(*this);
181 
182 			// make the ObjectContact remember me
183 			mrObjectContact.AddViewObjectContact(*this);
184 		}
185 
~ViewObjectContact()186 		ViewObjectContact::~ViewObjectContact()
187 		{
188 			// invalidate in view
189             if(!maObjectRange.isEmpty())
190             {
191     			GetObjectContact().InvalidatePartOfView(maObjectRange);
192             }
193 
194 			// delete PrimitiveAnimation
195 			if(mpPrimitiveAnimation)
196 			{
197 				delete mpPrimitiveAnimation;
198 				mpPrimitiveAnimation = 0;
199 			}
200 
201 			// take care of remebered ObjectContact. Remove from
202             // OC first. The VC removal (below) CAN trigger a StopGettingViewed()
203             // which (depending of it's implementation) may destroy other OCs. This
204             // can trigger the deletion of the helper OC of a page visualising object
205             // which IS the OC of this object. Eventually StopGettingViewed() needs
206             // to get asynchron later
207 			GetObjectContact().RemoveViewObjectContact(*this);
208 
209 			// take care of remebered ViewContact
210 			GetViewContact().RemoveViewObjectContact(*this);
211 		}
212 
getObjectRange() const213 		const basegfx::B2DRange& ViewObjectContact::getObjectRange() const
214 		{
215 			if(maObjectRange.isEmpty())
216 			{
217 				// if range is not computed (new or LazyInvalidate objects), force it
218 				const DisplayInfo aDisplayInfo;
219 				const drawinglayer::primitive2d::Primitive2DSequence xSequence(getPrimitive2DSequence(aDisplayInfo));
220 
221                 if(xSequence.hasElements())
222                 {
223 					const drawinglayer::geometry::ViewInformation2D& rViewInformation2D(GetObjectContact().getViewInformation2D());
224                     const_cast< ViewObjectContact* >(this)->maObjectRange =
225                         drawinglayer::primitive2d::getB2DRangeFromPrimitive2DSequence(xSequence, rViewInformation2D);
226 				}
227 			}
228 
229 			return maObjectRange;
230 		}
231 
ActionChanged()232 		void ViewObjectContact::ActionChanged()
233 		{
234 			if(!mbLazyInvalidate)
235 			{
236 				// set local flag
237 				mbLazyInvalidate = true;
238 
239 				// force ObjectRange
240 				getObjectRange();
241 
242 				if(!maObjectRange.isEmpty())
243 				{
244 					// invalidate current valid range
245 					GetObjectContact().InvalidatePartOfView(maObjectRange);
246 
247 					// reset ObjectRange, it needs to be recalculated
248 					maObjectRange.reset();
249 				}
250 
251 				// register at OC for lazy invalidate
252 				GetObjectContact().setLazyInvalidate(*this);
253 			}
254 		}
255 
triggerLazyInvalidate()256 		void ViewObjectContact::triggerLazyInvalidate()
257 		{
258 			if(mbLazyInvalidate)
259 			{
260 				// reset flag
261 				mbLazyInvalidate = false;
262 
263 				// force ObjectRange
264 				getObjectRange();
265 
266 				if(!maObjectRange.isEmpty())
267 				{
268 					// invalidate current valid range
269 					GetObjectContact().InvalidatePartOfView(maObjectRange);
270 				}
271 			}
272 		}
273 
274 		// Take some action when new objects are inserted
ActionChildInserted(ViewContact & rChild)275 		void ViewObjectContact::ActionChildInserted(ViewContact& rChild)
276 		{
277 			// force creation of the new VOC and trigger it's refresh, so it
278 			// will take part in LazyInvalidate immediately
279 			rChild.GetViewObjectContact(GetObjectContact()).ActionChanged();
280 
281 			// forward action to ObjectContact
282 			// const ViewObjectContact& rChildVOC = rChild.GetViewObjectContact(GetObjectContact());
283 			// GetObjectContact().InvalidatePartOfView(rChildVOC.getObjectRange());
284 		}
285 
checkForPrimitive2DAnimations()286 		void ViewObjectContact::checkForPrimitive2DAnimations()
287 		{
288 			// remove old one
289 			if(mpPrimitiveAnimation)
290 			{
291 				delete mpPrimitiveAnimation;
292 				mpPrimitiveAnimation = 0;
293 			}
294 
295 			// check for animated primitives
296 			if(mxPrimitive2DSequence.hasElements())
297 			{
298                 const bool bTextAnimationAllowed(GetObjectContact().IsTextAnimationAllowed());
299                 const bool bGraphicAnimationAllowed(GetObjectContact().IsGraphicAnimationAllowed());
300 
301                 if(bTextAnimationAllowed || bGraphicAnimationAllowed)
302                 {
303 				    AnimatedExtractingProcessor2D aAnimatedExtractor(GetObjectContact().getViewInformation2D(),
304 					    bTextAnimationAllowed, bGraphicAnimationAllowed);
305 				    aAnimatedExtractor.process(mxPrimitive2DSequence);
306 
307 				    if(aAnimatedExtractor.getPrimitive2DSequence().hasElements())
308 				    {
309 					    // dervied primitiveList is animated, setup new PrimitiveAnimation
310 					    mpPrimitiveAnimation =  new sdr::animation::PrimitiveAnimation(*this, aAnimatedExtractor.getPrimitive2DSequence());
311 				    }
312                 }
313 			}
314 		}
315 
createPrimitive2DSequence(const DisplayInfo & rDisplayInfo) const316         drawinglayer::primitive2d::Primitive2DSequence ViewObjectContact::createPrimitive2DSequence(const DisplayInfo& rDisplayInfo) const
317         {
318             // get the view-independent Primitive from the viewContact
319             drawinglayer::primitive2d::Primitive2DSequence xRetval(GetViewContact().getViewIndependentPrimitive2DSequence());
320 
321             if(xRetval.hasElements())
322             {
323                 // handle GluePoint
324                 if(!GetObjectContact().isOutputToPrinter() && GetObjectContact().AreGluePointsVisible())
325                 {
326                     const drawinglayer::primitive2d::Primitive2DSequence xGlue(GetViewContact().createGluePointPrimitive2DSequence());
327 
328                     if(xGlue.hasElements())
329                     {
330                         drawinglayer::primitive2d::appendPrimitive2DSequenceToPrimitive2DSequence(xRetval, xGlue);
331                     }
332                 }
333 
334                 // handle ghosted
335                 if(isPrimitiveGhosted(rDisplayInfo))
336                 {
337                     const basegfx::BColor aRGBWhite(1.0, 1.0, 1.0);
338                     const basegfx::BColorModifierSharedPtr aBColorModifier(
339                         new basegfx::BColorModifier_interpolate(
340                             aRGBWhite,
341                             0.5));
342                     const drawinglayer::primitive2d::Primitive2DReference xReference(
343                         new drawinglayer::primitive2d::ModifiedColorPrimitive2D(
344                             xRetval,
345                             aBColorModifier));
346 
347                     xRetval = drawinglayer::primitive2d::Primitive2DSequence(&xReference, 1);
348                 }
349             }
350 
351 			return xRetval;
352 		}
353 
getPrimitive2DSequence(const DisplayInfo & rDisplayInfo) const354 		drawinglayer::primitive2d::Primitive2DSequence ViewObjectContact::getPrimitive2DSequence(const DisplayInfo& rDisplayInfo) const
355 		{
356 			drawinglayer::primitive2d::Primitive2DSequence xNewPrimitiveSequence;
357 
358 			// take care of redirectors and create new list
359 			ViewObjectContactRedirector* pRedirector = GetObjectContact().GetViewObjectContactRedirector();
360 
361 			if(pRedirector)
362 			{
363 				xNewPrimitiveSequence = pRedirector->createRedirectedPrimitive2DSequence(*this, rDisplayInfo);
364 			}
365 			else
366 			{
367 				xNewPrimitiveSequence = createPrimitive2DSequence(rDisplayInfo);
368 			}
369 
370 			// local up-to-date checks. New list different from local one?
371 			if(!drawinglayer::primitive2d::arePrimitive2DSequencesEqual(mxPrimitive2DSequence, xNewPrimitiveSequence))
372 			{
373 				// has changed, copy content
374 				const_cast< ViewObjectContact* >(this)->mxPrimitive2DSequence = xNewPrimitiveSequence;
375 
376                 // check for animated stuff
377 				const_cast< ViewObjectContact* >(this)->checkForPrimitive2DAnimations();
378 
379 				// always update object range when PrimitiveSequence changes
380 				const drawinglayer::geometry::ViewInformation2D& rViewInformation2D(GetObjectContact().getViewInformation2D());
381                 const_cast< ViewObjectContact* >(this)->maObjectRange =
382                     drawinglayer::primitive2d::getB2DRangeFromPrimitive2DSequence(mxPrimitive2DSequence, rViewInformation2D);
383 			}
384 
385 			// return current Primitive2DSequence
386 			return mxPrimitive2DSequence;
387 		}
388 
isPrimitiveVisible(const DisplayInfo &) const389 		bool ViewObjectContact::isPrimitiveVisible(const DisplayInfo& /*rDisplayInfo*/) const
390 		{
391 			// default: always visible
392 			return true;
393 		}
394 
isPrimitiveGhosted(const DisplayInfo & rDisplayInfo) const395 		bool ViewObjectContact::isPrimitiveGhosted(const DisplayInfo& rDisplayInfo) const
396 		{
397 			// default: standard check
398 			return (GetObjectContact().DoVisualizeEnteredGroup() && !GetObjectContact().isOutputToPrinter() && rDisplayInfo.IsGhostedDrawModeActive());
399 		}
400 
getPrimitive2DSequenceHierarchy(DisplayInfo & rDisplayInfo) const401 		drawinglayer::primitive2d::Primitive2DSequence ViewObjectContact::getPrimitive2DSequenceHierarchy(DisplayInfo& rDisplayInfo) const
402 		{
403 			drawinglayer::primitive2d::Primitive2DSequence xRetval;
404 
405 			// check model-view visibility
406 			if(isPrimitiveVisible(rDisplayInfo))
407 			{
408 				xRetval = getPrimitive2DSequence(rDisplayInfo);
409 
410 				if(xRetval.hasElements())
411 				{
412 					// get ranges
413 					const drawinglayer::geometry::ViewInformation2D& rViewInformation2D(GetObjectContact().getViewInformation2D());
414 					const basegfx::B2DRange aObjectRange(drawinglayer::primitive2d::getB2DRangeFromPrimitive2DSequence(xRetval, rViewInformation2D));
415 					const basegfx::B2DRange aViewRange(rViewInformation2D.getViewport());
416 
417 					// check geometrical visibility
418 					if(!aViewRange.isEmpty() && !aViewRange.overlaps(aObjectRange))
419 					{
420 						// not visible, release
421 						xRetval.realloc(0);
422 					}
423 				}
424 			}
425 
426 			return xRetval;
427 		}
428 
getPrimitive2DSequenceSubHierarchy(DisplayInfo & rDisplayInfo) const429 		drawinglayer::primitive2d::Primitive2DSequence ViewObjectContact::getPrimitive2DSequenceSubHierarchy(DisplayInfo& rDisplayInfo) const
430 		{
431 			const sal_uInt32 nSubHierarchyCount(GetViewContact().GetObjectCount());
432 			drawinglayer::primitive2d::Primitive2DSequence xSeqRetval;
433 
434 			for(sal_uInt32 a(0); a < nSubHierarchyCount; a++)
435 			{
436 				const ViewObjectContact& rCandidate(GetViewContact().GetViewContact(a).GetViewObjectContact(GetObjectContact()));
437 
438                 drawinglayer::primitive2d::appendPrimitive2DSequenceToPrimitive2DSequence(xSeqRetval, rCandidate.getPrimitive2DSequenceHierarchy(rDisplayInfo));
439 			}
440 
441 			return xSeqRetval;
442 		}
443 	} // end of namespace contact
444 } // end of namespace sdr
445 
446 //////////////////////////////////////////////////////////////////////////////
447 // eof
448