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
85         const drawinglayer::primitive2d::Primitive2DSequence& getPrimitive2DSequence() const { return maPrimitive2DSequence; }
86 		bool isTextAnimationAllowed() const { return mbTextAnimationAllowed; }
87 		bool isGraphicAnimationAllowed() const { return mbGraphicAnimationAllowed; }
88 	};
89 
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 
101     AnimatedExtractingProcessor2D::~AnimatedExtractingProcessor2D()
102     {
103     }
104 
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 	{
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 
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 
213 		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 
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 
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
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 
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 
316 		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::BColorModifier aBColorModifier(aRGBWhite, 0.5, basegfx::BCOLORMODIFYMODE_INTERPOLATE);
339                     const drawinglayer::primitive2d::Primitive2DReference xReference(new drawinglayer::primitive2d::ModifiedColorPrimitive2D(xRetval, aBColorModifier));
340                     xRetval = drawinglayer::primitive2d::Primitive2DSequence(&xReference, 1);
341 				}
342 			}
343 
344 			return xRetval;
345 		}
346 
347 		drawinglayer::primitive2d::Primitive2DSequence ViewObjectContact::getPrimitive2DSequence(const DisplayInfo& rDisplayInfo) const
348 		{
349 			drawinglayer::primitive2d::Primitive2DSequence xNewPrimitiveSequence;
350 
351 			// take care of redirectors and create new list
352 			ViewObjectContactRedirector* pRedirector = GetObjectContact().GetViewObjectContactRedirector();
353 
354 			if(pRedirector)
355 			{
356 				xNewPrimitiveSequence = pRedirector->createRedirectedPrimitive2DSequence(*this, rDisplayInfo);
357 			}
358 			else
359 			{
360 				xNewPrimitiveSequence = createPrimitive2DSequence(rDisplayInfo);
361 			}
362 
363 			// local up-to-date checks. New list different from local one?
364 			if(!drawinglayer::primitive2d::arePrimitive2DSequencesEqual(mxPrimitive2DSequence, xNewPrimitiveSequence))
365 			{
366 				// has changed, copy content
367 				const_cast< ViewObjectContact* >(this)->mxPrimitive2DSequence = xNewPrimitiveSequence;
368 
369                 // check for animated stuff
370 				const_cast< ViewObjectContact* >(this)->checkForPrimitive2DAnimations();
371 
372 				// always update object range when PrimitiveSequence changes
373 				const drawinglayer::geometry::ViewInformation2D& rViewInformation2D(GetObjectContact().getViewInformation2D());
374                 const_cast< ViewObjectContact* >(this)->maObjectRange =
375                     drawinglayer::primitive2d::getB2DRangeFromPrimitive2DSequence(mxPrimitive2DSequence, rViewInformation2D);
376 			}
377 
378 			// return current Primitive2DSequence
379 			return mxPrimitive2DSequence;
380 		}
381 
382 		bool ViewObjectContact::isPrimitiveVisible(const DisplayInfo& /*rDisplayInfo*/) const
383 		{
384 			// default: always visible
385 			return true;
386 		}
387 
388 		bool ViewObjectContact::isPrimitiveGhosted(const DisplayInfo& rDisplayInfo) const
389 		{
390 			// default: standard check
391 			return (GetObjectContact().DoVisualizeEnteredGroup() && !GetObjectContact().isOutputToPrinter() && rDisplayInfo.IsGhostedDrawModeActive());
392 		}
393 
394 		drawinglayer::primitive2d::Primitive2DSequence ViewObjectContact::getPrimitive2DSequenceHierarchy(DisplayInfo& rDisplayInfo) const
395 		{
396 			drawinglayer::primitive2d::Primitive2DSequence xRetval;
397 
398 			// check model-view visibility
399 			if(isPrimitiveVisible(rDisplayInfo))
400 			{
401 				xRetval = getPrimitive2DSequence(rDisplayInfo);
402 
403 				if(xRetval.hasElements())
404 				{
405 					// get ranges
406 					const drawinglayer::geometry::ViewInformation2D& rViewInformation2D(GetObjectContact().getViewInformation2D());
407 					const basegfx::B2DRange aObjectRange(drawinglayer::primitive2d::getB2DRangeFromPrimitive2DSequence(xRetval, rViewInformation2D));
408 					const basegfx::B2DRange aViewRange(rViewInformation2D.getViewport());
409 
410 					// check geometrical visibility
411 					if(!aViewRange.isEmpty() && !aViewRange.overlaps(aObjectRange))
412 					{
413 						// not visible, release
414 						xRetval.realloc(0);
415 					}
416 				}
417 			}
418 
419 			return xRetval;
420 		}
421 
422 		drawinglayer::primitive2d::Primitive2DSequence ViewObjectContact::getPrimitive2DSequenceSubHierarchy(DisplayInfo& rDisplayInfo) const
423 		{
424 			const sal_uInt32 nSubHierarchyCount(GetViewContact().GetObjectCount());
425 			drawinglayer::primitive2d::Primitive2DSequence xSeqRetval;
426 
427 			for(sal_uInt32 a(0); a < nSubHierarchyCount; a++)
428 			{
429 				const ViewObjectContact& rCandidate(GetViewContact().GetViewContact(a).GetViewObjectContact(GetObjectContact()));
430 
431                 drawinglayer::primitive2d::appendPrimitive2DSequenceToPrimitive2DSequence(xSeqRetval, rCandidate.getPrimitive2DSequenceHierarchy(rDisplayInfo));
432 			}
433 
434 			return xSeqRetval;
435 		}
436 	} // end of namespace contact
437 } // end of namespace sdr
438 
439 //////////////////////////////////////////////////////////////////////////////
440 // eof
441