LibreOffice Module svx (master)  1
viewobjectcontact.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 
26 #include <basegfx/color/bcolor.hxx>
32 #include <svx/svdobj.hxx>
33 #include <svx/svdmodel.hxx>
34 
35 using namespace com::sun::star;
36 
37 namespace {
38 
39 // animated extractor
40 
41 // Necessary to filter a sequence of animated primitives from
42 // a sequence of primitives to find out if animated or not. The decision for
43 // what to decompose is hard-coded and only done for knowingly animated primitives
44 // to not decompose too deeply and unnecessarily. This implies that the list
45 // which is view-specific needs to be expanded by hand when new animated objects
46 // are added. This may eventually be changed to a dynamically configurable approach
47 // if necessary.
48 class AnimatedExtractingProcessor2D : public drawinglayer::processor2d::BaseProcessor2D
49 {
50 protected:
51  // the found animated primitives
53 
54  // text animation allowed?
55  bool mbTextAnimationAllowed : 1;
56 
57  // graphic animation allowed?
58  bool mbGraphicAnimationAllowed : 1;
59 
60  // as tooling, the process() implementation takes over API handling and calls this
61  // virtual render method when the primitive implementation is BasePrimitive2D-based.
62  virtual void processBasePrimitive2D(const drawinglayer::primitive2d::BasePrimitive2D& rCandidate) override;
63 
64 public:
65  AnimatedExtractingProcessor2D(
66  const drawinglayer::geometry::ViewInformation2D& rViewInformation,
67  bool bTextAnimationAllowed,
68  bool bGraphicAnimationAllowed);
69 
70  // data access
71  const drawinglayer::primitive2d::Primitive2DContainer& getPrimitive2DSequence() const { return maPrimitive2DSequence; }
72  drawinglayer::primitive2d::Primitive2DContainer extractPrimitive2DSequence() const { return std::move(maPrimitive2DSequence); }
73 };
74 
75 AnimatedExtractingProcessor2D::AnimatedExtractingProcessor2D(
76  const drawinglayer::geometry::ViewInformation2D& rViewInformation,
77  bool bTextAnimationAllowed,
78  bool bGraphicAnimationAllowed)
79 : drawinglayer::processor2d::BaseProcessor2D(rViewInformation),
80  mbTextAnimationAllowed(bTextAnimationAllowed),
81  mbGraphicAnimationAllowed(bGraphicAnimationAllowed)
82 {
83 }
84 
85 void AnimatedExtractingProcessor2D::processBasePrimitive2D(const drawinglayer::primitive2d::BasePrimitive2D& rCandidate)
86 {
87  // known implementation, access directly
88  switch(rCandidate.getPrimitive2DID())
89  {
90  // add and accept animated primitives directly, no need to decompose
94  {
96 
97  if((rSwitchPrimitive.isTextAnimation() && mbTextAnimationAllowed)
98  || (rSwitchPrimitive.isGraphicAnimation() && mbGraphicAnimationAllowed))
99  {
100  const drawinglayer::primitive2d::Primitive2DReference xReference(const_cast< drawinglayer::primitive2d::BasePrimitive2D* >(&rCandidate));
101  maPrimitive2DSequence.push_back(xReference);
102  }
103  break;
104  }
105 
106  // decompose animated gifs where SdrGrafPrimitive2D produces a GraphicPrimitive2D
107  // which then produces the animation infos (all when used/needed)
110 
111  // decompose SdrObjects with evtl. animated text
120 
121  // #121194# With Graphic as Bitmap FillStyle, also check
122  // for primitives filled with animated graphics
126 
127  // decompose evtl. animated text contained in MaskPrimitive2D
128  // or group primitives
131  {
132  process(rCandidate);
133  break;
134  }
135 
136  default :
137  {
138  // nothing to do for the rest
139  break;
140  }
141  }
142 }
143 
144 } // end of anonymous namespace
145 
146 namespace sdr::contact {
147 
149 : mrObjectContact(rObjectContact),
150  mrViewContact(rViewContact),
151  maGridOffset(0.0, 0.0),
152  mnActionChangedCount(0),
153  mbLazyInvalidate(false)
154 {
155  // make the ViewContact remember me
157 
158  // make the ObjectContact remember me
160 }
161 
163 {
164  // invalidate in view
165  if(!getObjectRange().isEmpty())
166  {
168  }
169 
170  // delete PrimitiveAnimation
171  mpPrimitiveAnimation.reset();
172 
173  // take care of remembered ObjectContact. Remove from
174  // OC first. The VC removal (below) CAN trigger a StopGettingViewed()
175  // which (depending of its implementation) may destroy other OCs. This
176  // can trigger the deletion of the helper OC of a page visualising object
177  // which IS the OC of this object. Eventually StopGettingViewed() needs
178  // to get asynchron later
180 
181  // take care of remembered ViewContact
183 }
184 
186 {
187  if(maObjectRange.isEmpty())
188  {
190  basegfx::B2DRange aTempRange = GetViewContact().getRange(rViewInfo2D);
191  if (!aTempRange.isEmpty())
192  {
193  const_cast< ViewObjectContact* >(this)->maObjectRange = aTempRange;
194  }
195  else
196  {
197  // if range is not computed (new or LazyInvalidate objects), force it
198  const DisplayInfo aDisplayInfo;
200 
201  if(!xSequence.empty())
202  {
203  const_cast< ViewObjectContact* >(this)->maObjectRange =
204  xSequence.getB2DRange(rViewInfo2D);
205  }
206  }
207  }
208 
209  return maObjectRange;
210 }
211 
213 {
214  // clear cached primitives
215  mxPrimitive2DSequence.clear();
217 
218  if(mbLazyInvalidate)
219  return;
220 
221  // set local flag
222  mbLazyInvalidate = true;
223 
224  // force ObjectRange
225  getObjectRange();
226 
227  if(!getObjectRange().isEmpty())
228  {
229  // invalidate current valid range
231 
232  // reset gridOffset, it needs to be recalculated
233  if (GetObjectContact().supportsGridOffsets())
234  resetGridOffset();
235  else
237  }
238 
239  // register at OC for lazy invalidate
241 }
242 
244 {
245  if(!mbLazyInvalidate)
246  return;
247 
248  // reset flag
249  mbLazyInvalidate = false;
250 
251  // force ObjectRange
252  getObjectRange();
253 
254  if(!getObjectRange().isEmpty())
255  {
256  // invalidate current valid range
258  }
259 }
260 
261 // Take some action when new objects are inserted
263 {
264  // force creation of the new VOC and trigger it's refresh, so it
265  // will take part in LazyInvalidate immediately
267 
268  // forward action to ObjectContact
269  // const ViewObjectContact& rChildVOC = rChild.GetViewObjectContact(GetObjectContact());
270  // GetObjectContact().InvalidatePartOfView(rChildVOC.getObjectRange());
271 }
272 
274 {
275  // remove old one
276  mpPrimitiveAnimation.reset();
277 
278  // check for animated primitives
279  if(mxPrimitive2DSequence.empty())
280  return;
281 
282  const bool bTextAnimationAllowed(GetObjectContact().IsTextAnimationAllowed());
283  const bool bGraphicAnimationAllowed(GetObjectContact().IsGraphicAnimationAllowed());
284 
285  if(bTextAnimationAllowed || bGraphicAnimationAllowed)
286  {
287  AnimatedExtractingProcessor2D aAnimatedExtractor(GetObjectContact().getViewInformation2D(),
288  bTextAnimationAllowed, bGraphicAnimationAllowed);
289  aAnimatedExtractor.process(mxPrimitive2DSequence);
290 
291  if(!aAnimatedExtractor.getPrimitive2DSequence().empty())
292  {
293  // derived primitiveList is animated, setup new PrimitiveAnimation
294  mpPrimitiveAnimation.reset( new sdr::animation::PrimitiveAnimation(*this, aAnimatedExtractor.extractPrimitive2DSequence()) );
295  }
296  }
297 }
298 
300 {
301  // get the view-independent Primitive from the viewContact
304 
305  if(!xRetval.empty())
306  {
307  // handle GluePoint
309  {
310  const drawinglayer::primitive2d::Primitive2DContainer xGlue(GetViewContact().createGluePointPrimitive2DSequence());
311 
312  if(!xGlue.empty())
313  {
314  xRetval.append(xGlue);
315  }
316  }
317 
318  // handle ghosted
319  if(isPrimitiveGhosted(rDisplayInfo))
320  {
321  const basegfx::BColor aRGBWhite(1.0, 1.0, 1.0);
322  const basegfx::BColorModifierSharedPtr aBColorModifier =
323  std::make_shared<basegfx::BColorModifier_interpolate>(
324  aRGBWhite,
325  0.5);
328  std::move(xRetval),
329  aBColorModifier));
330 
331  xRetval = drawinglayer::primitive2d::Primitive2DContainer { xReference };
332  }
333  }
334 
335  rVisitor.visit(xRetval);
336 }
337 
339 {
340  // only some of the top-level apps are any good at reliably invalidating us (e.g. writer is not)
341  if (SdrObject* pSdrObj = mrViewContact.TryToGetSdrObject())
342  if (pSdrObj->getSdrModelFromSdrObject().IsVOCInvalidationIsReliable())
343  {
344  if (!mxPrimitive2DSequence.empty())
345  return mxPrimitive2DSequence;
346  }
347 
355 
356  // take care of redirectors and create new list
358 
359  if(pRedirector)
360  {
361  pRedirector->createRedirectedPrimitive2DSequence(*this, rDisplayInfo, xNewPrimitiveSequence);
362  }
363  else
364  {
365  createPrimitive2DSequence(rDisplayInfo, xNewPrimitiveSequence);
366  }
367 
368  // local up-to-date checks. New list different from local one?
369  if(mxPrimitive2DSequence == xNewPrimitiveSequence)
370  return mxPrimitive2DSequence;
371 
372  // has changed, copy content
373  const_cast< ViewObjectContact* >(this)->mxPrimitive2DSequence = std::move(xNewPrimitiveSequence);
374 
375  // check for animated stuff
376  const_cast< ViewObjectContact* >(this)->checkForPrimitive2DAnimations();
377 
378  // always update object range when PrimitiveSequence changes
379  const drawinglayer::geometry::ViewInformation2D& rViewInformation2D(GetObjectContact().getViewInformation2D());
380  const_cast< ViewObjectContact* >(this)->maObjectRange = mxPrimitive2DSequence.getB2DRange(rViewInformation2D);
381 
382  // check and eventually embed to GridOffset transform primitive
383  if(GetObjectContact().supportsGridOffsets())
384  {
385  const basegfx::B2DVector& rGridOffset(getGridOffset());
386 
387  if(0.0 != rGridOffset.getX() || 0.0 != rGridOffset.getY())
388  {
389  const basegfx::B2DHomMatrix aTranslateGridOffset(
391  rGridOffset));
394  aTranslateGridOffset,
395  std::move(const_cast< ViewObjectContact* >(this)->mxPrimitive2DSequence)));
396 
397  // Set values at local data. So for now, the mechanism is to reset some of the
398  // defining things (mxPrimitive2DSequence, maGridOffset) and re-create the
399  // buffered data (including maObjectRange). It *could* be changed to keep
400  // the unmodified PrimitiveSequence and only update the GridOffset, but this
401  // would require a 2nd instance of maObjectRange and mxPrimitive2DSequence. I
402  // started doing so, but it just makes the code more complicated. For now,
403  // just allow re-creation of the PrimitiveSequence (and removing buffered
404  // decomposed content of it). May be optimized, though. OTOH it only happens
405  // in calc which traditionally does not have a huge amount of DrawObjects anyways.
407  const_cast< ViewObjectContact* >(this)->maObjectRange.transform(aTranslateGridOffset);
408  }
409  }
410 
411  // return current Primitive2DContainer
412  return mxPrimitive2DSequence;
413 }
414 
415 bool ViewObjectContact::isPrimitiveVisible(const DisplayInfo& /*rDisplayInfo*/) const
416 {
417  // default: always visible
418  return true;
419 }
420 
421 bool ViewObjectContact::isPrimitiveGhosted(const DisplayInfo& rDisplayInfo) const
422 {
423  // default: standard check
424  return (GetObjectContact().DoVisualizeEnteredGroup() && !GetObjectContact().isOutputToPrinter() && rDisplayInfo.IsGhostedDrawModeActive());
425 }
426 
428 {
429  // check model-view visibility
430  if(!isPrimitiveVisible(rDisplayInfo))
431  return;
432 
433  getPrimitive2DSequence(rDisplayInfo);
434  if(mxPrimitive2DSequence.empty())
435  return;
436 
437  // get ranges
438  const drawinglayer::geometry::ViewInformation2D& rViewInformation2D(GetObjectContact().getViewInformation2D());
439  // tdf#147164 cannot use maObjectRange here, it is unreliable
440  const basegfx::B2DRange aObjectRange(mxPrimitive2DSequence.getB2DRange(rViewInformation2D));
441  const basegfx::B2DRange& aViewRange(rViewInformation2D.getViewport());
442 
443  // check geometrical visibility
444  bool bVisible = aViewRange.isEmpty() || aViewRange.overlaps(aObjectRange);
445  if(!bVisible)
446  return;
447 
448  // temporarily take over the mxPrimitive2DSequence, in case it gets invalidated while we want to iterate over it
449  auto tmp = std::move(const_cast<ViewObjectContact*>(this)->mxPrimitive2DSequence);
450  int nPrevCount = mnActionChangedCount;
451 
452  rVisitor.visit(tmp);
453 
454  // if we received ActionChanged() calls while walking the primitives, then leave it empty, otherwise move it back
455  if (mnActionChangedCount == nPrevCount)
456  const_cast<ViewObjectContact*>(this)->mxPrimitive2DSequence = std::move(tmp);
457 }
458 
460 {
461  const sal_uInt32 nSubHierarchyCount(GetViewContact().GetObjectCount());
462 
463  for(sal_uInt32 a(0); a < nSubHierarchyCount; a++)
464  {
465  const ViewObjectContact& rCandidate(GetViewContact().GetViewContact(a).GetViewObjectContact(GetObjectContact()));
466 
467  rCandidate.getPrimitive2DSequenceHierarchy(rDisplayInfo, rVisitor);
468  }
469 }
470 
471 // Support getting a GridOffset per object and view for non-linear ViewToDevice
472 // transformation (calc). On-demand created by delegating to the ObjectContact
473 // (->View) that has then all needed information
475 {
477  {
478  // create on-demand
479  GetObjectContact().calculateGridOffsetForViewOjectContact(const_cast<ViewObjectContact*>(this)->maGridOffset, *this);
480  }
481 
482  return maGridOffset;
483 }
484 
486 {
487  // reset buffered GridOffset itself
488  maGridOffset.setX(0.0);
489  maGridOffset.setY(0.0);
490 
491  // also reset sequence to get a re-calculation when GridOffset changes
492  mxPrimitive2DSequence.clear();
494 }
495 
496 }
497 
498 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
virtual void InvalidatePartOfView(const basegfx::B2DRange &rRange) const
double getY() const
#define PRIMITIVE2D_ID_GROUPPRIMITIVE2D
void ActionChildInserted(ViewContact &rChild)
bool bVisible
const basegfx::B2DRange & getViewport() const
drawinglayer::primitive2d::Primitive2DContainer mxPrimitive2DSequence
#define PRIMITIVE2D_ID_FILLGRAPHICPRIMITIVE2D
virtual bool AreGluePointsVisible() const
ViewObjectContactRedirector * GetViewObjectContactRedirector() const
virtual SdrObject * TryToGetSdrObject() const
void RemoveViewObjectContact(ViewObjectContact &rVOContact)
#define PRIMITIVE2D_ID_SDRPATHPRIMITIVE2D
virtual void calculateGridOffsetForViewOjectContact(basegfx::B2DVector &rTarget, const ViewObjectContact &rClient) const
#define PRIMITIVE2D_ID_SDRELLIPSEPRIMITIVE2D
ViewObjectContact & GetViewObjectContact(ObjectContact &rObjectContact)
Definition: viewcontact.cxx:65
virtual void createRedirectedPrimitive2DSequence(const sdr::contact::ViewObjectContact &rOriginal, const sdr::contact::DisplayInfo &rDisplayInfo, drawinglayer::primitive2d::Primitive2DDecompositionVisitor &rVisitor)
virtual bool isPrimitiveGhosted(const DisplayInfo &rDisplayInfo) const
virtual void setLazyInvalidate(ViewObjectContact &rVOC)
void setX(double fX)
drawinglayer::primitive2d::Primitive2DContainer const & getPrimitive2DSequence(const DisplayInfo &rDisplayInfo) const
virtual basegfx::B2DRange getRange(const drawinglayer::geometry::ViewInformation2D &rViewInfo2D) const
BASEGFX_DLLPUBLIC void transform(const B2DHomMatrix &rMatrix)
void AddViewObjectContact(ViewObjectContact &rVOContact)
Definition: viewcontact.cxx:93
class SAL_WARN_UNUSED UNLESS_MERGELIBS(BASEGFX_DLLPUBLIC) BColorModifier_black_and_white final class SAL_WARN_UNUSED UNLESS_MERGELIBS(BASEGFX_DLLPUBLIC) BColorModifier_gamma final class SAL_WARN_UNUSED UNLESS_MERGELIBS(BASEGFX_DLLPUBLIC) BColorModifier_RGBLuminanceContrast final typedef std::shared_ptr< BColorModifier > BColorModifierSharedPtr
void getPrimitive2DSequenceSubHierarchy(DisplayInfo &rDisplayInfo, drawinglayer::primitive2d::Primitive2DDecompositionVisitor &rVisitor) const
#define PRIMITIVE2D_ID_MASKPRIMITIVE2D
#define PRIMITIVE2D_ID_SDRELLIPSESEGMENTPRIMITIVE2D
#define PRIMITIVE2D_ID_TRANSFORMPRIMITIVE2D
virtual bool isOutputToPrinter() const
ObjectContact & GetObjectContact() const
#define PRIMITIVE2D_ID_SDRCUSTOMSHAPEPRIMITIVE2D
#define PRIMITIVE2D_ID_SDRCONNECTORPRIMITIVE2D
#define PRIMITIVE2D_ID_ANIMATEDSWITCHPRIMITIVE2D
void append(const Primitive2DReference &)
virtual void createPrimitive2DSequence(const DisplayInfo &rDisplayInfo, drawinglayer::primitive2d::Primitive2DDecompositionVisitor &rVisitor) const
virtual void getPrimitive2DSequenceHierarchy(DisplayInfo &rDisplayInfo, drawinglayer::primitive2d::Primitive2DDecompositionVisitor &rVisitor) const
uno_Any a
#define PRIMITIVE2D_ID_GRAPHICPRIMITIVE2D
const drawinglayer::geometry::ViewInformation2D & getViewInformation2D() const
virtual void visit(const Primitive2DReference &)=0
#define PRIMITIVE2D_ID_ANIMATEDBLINKPRIMITIVE2D
virtual bool isPrimitiveVisible(const DisplayInfo &rDisplayInfo) const
ViewObjectContact(ObjectContact &rObjectContact, ViewContact &rViewContact)
virtual sal_uInt32 getPrimitive2DID() const =0
Abstract DrawObject.
Definition: svdobj.hxx:260
void AddViewObjectContact(ViewObjectContact &rVOContact)
#define PRIMITIVE2D_ID_SDRGRAFPRIMITIVE2D
void setY(double fY)
#define PRIMITIVE2D_ID_POLYPOLYGONGRAPHICPRIMITIVE2D
void getViewIndependentPrimitive2DContainer(drawinglayer::primitive2d::Primitive2DDecompositionVisitor &rVisitor) const
std::unique_ptr< sdr::animation::PrimitiveAnimation > mpPrimitiveAnimation
bool IsGhostedDrawModeActive() const
Definition: displayinfo.hxx:93
const basegfx::B2DVector & getGridOffset() const
virtual bool supportsGridOffsets() const
#define PRIMITIVE2D_ID_ANIMATEDINTERPOLATEPRIMITIVE2D
#define PRIMITIVE2D_ID_SDRMEASUREPRIMITIVE2D
void RemoveViewObjectContact(ViewObjectContact &rVOContact)
Definition: viewcontact.cxx:99
ViewContact & GetViewContact() const
#define PRIMITIVE2D_ID_SDRCAPTIONPRIMITIVE2D
double getX() const
basegfx::B2DRange getB2DRange(const geometry::ViewInformation2D &aViewInformation) const
B2DHomMatrix createTranslateB2DHomMatrix(double fTranslateX, double fTranslateY)
const basegfx::B2DRange & getObjectRange() const
#define PRIMITIVE2D_ID_SDRRECTANGLEPRIMITIVE2D
bool m_bDetectedRangeSegmentation false