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 
33 using namespace com::sun::star;
34 
35 namespace {
36 
37 // animated extractor
38 
39 // Necessary to filter a sequence of animated primitives from
40 // a sequence of primitives to find out if animated or not. The decision for
41 // what to decompose is hard-coded and only done for knowingly animated primitives
42 // to not decompose too deeply and unnecessarily. This implies that the list
43 // which is view-specific needs to be expanded by hand when new animated objects
44 // are added. This may eventually be changed to a dynamically configurable approach
45 // if necessary.
46 class AnimatedExtractingProcessor2D : public drawinglayer::processor2d::BaseProcessor2D
47 {
48 protected:
49  // the found animated primitives
51 
52  // text animation allowed?
53  bool mbTextAnimationAllowed : 1;
54 
55  // graphic animation allowed?
56  bool mbGraphicAnimationAllowed : 1;
57 
58  // as tooling, the process() implementation takes over API handling and calls this
59  // virtual render method when the primitive implementation is BasePrimitive2D-based.
60  virtual void processBasePrimitive2D(const drawinglayer::primitive2d::BasePrimitive2D& rCandidate) override;
61 
62 public:
63  AnimatedExtractingProcessor2D(
64  const drawinglayer::geometry::ViewInformation2D& rViewInformation,
65  bool bTextAnimationAllowed,
66  bool bGraphicAnimationAllowed);
67 
68  // data access
69  const drawinglayer::primitive2d::Primitive2DContainer& getPrimitive2DSequence() const { return maPrimitive2DSequence; }
70  drawinglayer::primitive2d::Primitive2DContainer extractPrimitive2DSequence() const { return std::move(maPrimitive2DSequence); }
71 };
72 
73 AnimatedExtractingProcessor2D::AnimatedExtractingProcessor2D(
74  const drawinglayer::geometry::ViewInformation2D& rViewInformation,
75  bool bTextAnimationAllowed,
76  bool bGraphicAnimationAllowed)
77 : drawinglayer::processor2d::BaseProcessor2D(rViewInformation),
78  mbTextAnimationAllowed(bTextAnimationAllowed),
79  mbGraphicAnimationAllowed(bGraphicAnimationAllowed)
80 {
81 }
82 
83 void AnimatedExtractingProcessor2D::processBasePrimitive2D(const drawinglayer::primitive2d::BasePrimitive2D& rCandidate)
84 {
85  // known implementation, access directly
86  switch(rCandidate.getPrimitive2DID())
87  {
88  // add and accept animated primitives directly, no need to decompose
92  {
94 
95  if((rSwitchPrimitive.isTextAnimation() && mbTextAnimationAllowed)
96  || (rSwitchPrimitive.isGraphicAnimation() && mbGraphicAnimationAllowed))
97  {
98  const drawinglayer::primitive2d::Primitive2DReference xReference(const_cast< drawinglayer::primitive2d::BasePrimitive2D* >(&rCandidate));
99  maPrimitive2DSequence.push_back(xReference);
100  }
101  break;
102  }
103 
104  // decompose animated gifs where SdrGrafPrimitive2D produces a GraphicPrimitive2D
105  // which then produces the animation infos (all when used/needed)
108 
109  // decompose SdrObjects with evtl. animated text
118 
119  // #121194# With Graphic as Bitmap FillStyle, also check
120  // for primitives filled with animated graphics
124 
125  // decompose evtl. animated text contained in MaskPrimitive2D
126  // or group primitives
129  {
130  process(rCandidate);
131  break;
132  }
133 
134  default :
135  {
136  // nothing to do for the rest
137  break;
138  }
139  }
140 }
141 
142 } // end of anonymous namespace
143 
144 namespace sdr::contact {
145 
147 : mrObjectContact(rObjectContact),
148  mrViewContact(rViewContact),
149  maGridOffset(0.0, 0.0),
150  mbLazyInvalidate(false)
151 {
152  // make the ViewContact remember me
154 
155  // make the ObjectContact remember me
157 }
158 
160 {
161  // invalidate in view
162  if(!maObjectRange.isEmpty())
163  {
165  }
166 
167  // delete PrimitiveAnimation
168  mpPrimitiveAnimation.reset();
169 
170  // take care of remembered ObjectContact. Remove from
171  // OC first. The VC removal (below) CAN trigger a StopGettingViewed()
172  // which (depending of its implementation) may destroy other OCs. This
173  // can trigger the deletion of the helper OC of a page visualising object
174  // which IS the OC of this object. Eventually StopGettingViewed() needs
175  // to get asynchron later
177 
178  // take care of remembered ViewContact
180 }
181 
183 {
184  if(maObjectRange.isEmpty())
185  {
187  basegfx::B2DRange aTempRange = GetViewContact().getRange(rViewInfo2D);
188  if (!aTempRange.isEmpty())
189  {
190  const_cast< ViewObjectContact* >(this)->maObjectRange = aTempRange;
191  }
192  else
193  {
194  // if range is not computed (new or LazyInvalidate objects), force it
195  const DisplayInfo aDisplayInfo;
197 
198  if(!xSequence.empty())
199  {
200  const_cast< ViewObjectContact* >(this)->maObjectRange =
201  xSequence.getB2DRange(rViewInfo2D);
202  }
203  }
204  }
205 
206  return maObjectRange;
207 }
208 
210 {
211  if(mbLazyInvalidate)
212  return;
213 
214  // set local flag
215  mbLazyInvalidate = true;
216 
217  // force ObjectRange
218  getObjectRange();
219 
220  if(!maObjectRange.isEmpty())
221  {
222  // invalidate current valid range
224 
225  // reset gridOffset, it needs to be recalculated
226  if (GetObjectContact().supportsGridOffsets())
227  resetGridOffset();
228  else
230  }
231 
232  // register at OC for lazy invalidate
234 }
235 
237 {
238  if(!mbLazyInvalidate)
239  return;
240 
241  // reset flag
242  mbLazyInvalidate = false;
243 
244  // force ObjectRange
245  getObjectRange();
246 
247  if(!maObjectRange.isEmpty())
248  {
249  // invalidate current valid range
251  }
252 }
253 
254 // Take some action when new objects are inserted
256 {
257  // force creation of the new VOC and trigger it's refresh, so it
258  // will take part in LazyInvalidate immediately
260 
261  // forward action to ObjectContact
262  // const ViewObjectContact& rChildVOC = rChild.GetViewObjectContact(GetObjectContact());
263  // GetObjectContact().InvalidatePartOfView(rChildVOC.getObjectRange());
264 }
265 
267 {
268  // remove old one
269  mpPrimitiveAnimation.reset();
270 
271  // check for animated primitives
272  if(mxPrimitive2DSequence.empty())
273  return;
274 
275  const bool bTextAnimationAllowed(GetObjectContact().IsTextAnimationAllowed());
276  const bool bGraphicAnimationAllowed(GetObjectContact().IsGraphicAnimationAllowed());
277 
278  if(bTextAnimationAllowed || bGraphicAnimationAllowed)
279  {
280  AnimatedExtractingProcessor2D aAnimatedExtractor(GetObjectContact().getViewInformation2D(),
281  bTextAnimationAllowed, bGraphicAnimationAllowed);
282  aAnimatedExtractor.process(mxPrimitive2DSequence);
283 
284  if(!aAnimatedExtractor.getPrimitive2DSequence().empty())
285  {
286  // derived primitiveList is animated, setup new PrimitiveAnimation
287  mpPrimitiveAnimation.reset( new sdr::animation::PrimitiveAnimation(*this, aAnimatedExtractor.extractPrimitive2DSequence()) );
288  }
289  }
290 }
291 
293 {
294  // get the view-independent Primitive from the viewContact
295  drawinglayer::primitive2d::Primitive2DContainer xRetval(GetViewContact().getViewIndependentPrimitive2DContainer());
296 
297  if(!xRetval.empty())
298  {
299  // handle GluePoint
301  {
302  const drawinglayer::primitive2d::Primitive2DContainer xGlue(GetViewContact().createGluePointPrimitive2DSequence());
303 
304  if(!xGlue.empty())
305  {
306  xRetval.append(xGlue);
307  }
308  }
309 
310  // handle ghosted
311  if(isPrimitiveGhosted(rDisplayInfo))
312  {
313  const basegfx::BColor aRGBWhite(1.0, 1.0, 1.0);
314  const basegfx::BColorModifierSharedPtr aBColorModifier =
315  std::make_shared<basegfx::BColorModifier_interpolate>(
316  aRGBWhite,
317  0.5);
320  std::move(xRetval),
321  aBColorModifier));
322 
323  xRetval = drawinglayer::primitive2d::Primitive2DContainer { xReference };
324  }
325  }
326 
327  return xRetval;
328 }
329 
331 {
333 
334  // take care of redirectors and create new list
336 
337  if(pRedirector)
338  {
339  xNewPrimitiveSequence = pRedirector->createRedirectedPrimitive2DSequence(*this, rDisplayInfo);
340  }
341  else
342  {
343  xNewPrimitiveSequence = createPrimitive2DSequence(rDisplayInfo);
344  }
345 
346  // local up-to-date checks. New list different from local one?
347  if(mxPrimitive2DSequence == xNewPrimitiveSequence)
348  return mxPrimitive2DSequence;
349 
350  // has changed, copy content
351  const_cast< ViewObjectContact* >(this)->mxPrimitive2DSequence = std::move(xNewPrimitiveSequence);
352 
353  // check for animated stuff
354  const_cast< ViewObjectContact* >(this)->checkForPrimitive2DAnimations();
355 
356  // always update object range when PrimitiveSequence changes
357  const drawinglayer::geometry::ViewInformation2D& rViewInformation2D(GetObjectContact().getViewInformation2D());
358  const_cast< ViewObjectContact* >(this)->maObjectRange = mxPrimitive2DSequence.getB2DRange(rViewInformation2D);
359 
360  // check and eventually embed to GridOffset transform primitive
361  if(GetObjectContact().supportsGridOffsets())
362  {
363  const basegfx::B2DVector& rGridOffset(getGridOffset());
364 
365  if(0.0 != rGridOffset.getX() || 0.0 != rGridOffset.getY())
366  {
367  const basegfx::B2DHomMatrix aTranslateGridOffset(
369  rGridOffset));
372  aTranslateGridOffset,
374 
375  // Set values at local data. So for now, the mechanism is to reset some of the
376  // defining things (mxPrimitive2DSequence, maGridOffset) and re-create the
377  // buffered data (including maObjectRange). It *could* be changed to keep
378  // the unmodified PrimitiveSequence and only update the GridOffset, but this
379  // would require a 2nd instance of maObjectRange and mxPrimitive2DSequence. I
380  // started doing so, but it just makes the code more complicated. For now,
381  // just allow re-creation of the PrimitiveSequence (and removing buffered
382  // decomposed content of it). May be optimized, though. OTOH it only happens
383  // in calc which traditionally does not have a huge amount of DrawObjects anyways.
385  const_cast< ViewObjectContact* >(this)->maObjectRange.transform(aTranslateGridOffset);
386  }
387  }
388 
389  // return current Primitive2DContainer
390  return mxPrimitive2DSequence;
391 }
392 
393 bool ViewObjectContact::isPrimitiveVisible(const DisplayInfo& /*rDisplayInfo*/) const
394 {
395  // default: always visible
396  return true;
397 }
398 
399 bool ViewObjectContact::isPrimitiveGhosted(const DisplayInfo& rDisplayInfo) const
400 {
401  // default: standard check
402  return (GetObjectContact().DoVisualizeEnteredGroup() && !GetObjectContact().isOutputToPrinter() && rDisplayInfo.IsGhostedDrawModeActive());
403 }
404 
406 {
407  // check model-view visibility
408  if(!isPrimitiveVisible(rDisplayInfo))
409  return;
410 
412  if(xRetval.empty())
413  return;
414 
415  // get ranges
416  const drawinglayer::geometry::ViewInformation2D& rViewInformation2D(GetObjectContact().getViewInformation2D());
417  const basegfx::B2DRange aObjectRange(xRetval.getB2DRange(rViewInformation2D));
418  const basegfx::B2DRange& aViewRange(rViewInformation2D.getViewport());
419 
420  // check geometrical visibility
421  bool bVisible = aViewRange.isEmpty() || aViewRange.overlaps(aObjectRange);
422  if(!bVisible)
423  return;
424 
425  rVisitor.append(xRetval);
426 }
427 
429 {
430  const sal_uInt32 nSubHierarchyCount(GetViewContact().GetObjectCount());
432 
433  for(sal_uInt32 a(0); a < nSubHierarchyCount; a++)
434  {
435  const ViewObjectContact& rCandidate(GetViewContact().GetViewContact(a).GetViewObjectContact(GetObjectContact()));
436 
437  rCandidate.getPrimitive2DSequenceHierarchy(rDisplayInfo, xSeqRetval);
438  }
439 
440  return xSeqRetval;
441 }
442 
443 // Support getting a GridOffset per object and view for non-linear ViewToDevice
444 // transformation (calc). On-demand created by delegating to the ObjectContact
445 // (->View) that has then all needed information
447 {
449  {
450  // create on-demand
451  GetObjectContact().calculateGridOffsetForViewOjectContact(const_cast<ViewObjectContact*>(this)->maGridOffset, *this);
452  }
453 
454  return maGridOffset;
455 }
456 
458 {
459  // reset buffered GridOffset itself
460  maGridOffset.setX(0.0);
461  maGridOffset.setY(0.0);
462 
463  // also reset sequence to get a re-calculation when GridOffset changes
464  mxPrimitive2DSequence.clear();
466 }
467 
468 }
469 
470 /* 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
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:64
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
drawinglayer::primitive2d::Primitive2DContainer getPrimitive2DSequenceSubHierarchy(DisplayInfo &rDisplayInfo) const
BASEGFX_DLLPUBLIC void transform(const B2DHomMatrix &rMatrix)
void AddViewObjectContact(ViewObjectContact &rVOContact)
Definition: viewcontact.cxx:92
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
#define PRIMITIVE2D_ID_MASKPRIMITIVE2D
#define PRIMITIVE2D_ID_SDRELLIPSESEGMENTPRIMITIVE2D
#define PRIMITIVE2D_ID_TRANSFORMPRIMITIVE2D
virtual bool isOutputToPrinter() const
ObjectContact & GetObjectContact() const
#define PRIMITIVE2D_ID_SDRCUSTOMSHAPEPRIMITIVE2D
bool isEmpty() const
#define PRIMITIVE2D_ID_SDRCONNECTORPRIMITIVE2D
#define PRIMITIVE2D_ID_ANIMATEDSWITCHPRIMITIVE2D
virtual void getPrimitive2DSequenceHierarchy(DisplayInfo &rDisplayInfo, drawinglayer::primitive2d::Primitive2DDecompositionVisitor &rVisitor) const
uno_Any a
#define PRIMITIVE2D_ID_GRAPHICPRIMITIVE2D
const drawinglayer::geometry::ViewInformation2D & getViewInformation2D() const
#define PRIMITIVE2D_ID_ANIMATEDBLINKPRIMITIVE2D
virtual drawinglayer::primitive2d::Primitive2DContainer createRedirectedPrimitive2DSequence(const sdr::contact::ViewObjectContact &rOriginal, const sdr::contact::DisplayInfo &rDisplayInfo)
virtual void append(const Primitive2DReference &) override
virtual bool isPrimitiveVisible(const DisplayInfo &rDisplayInfo) const
ViewObjectContact(ObjectContact &rObjectContact, ViewContact &rViewContact)
virtual sal_uInt32 getPrimitive2DID() const =0
void AddViewObjectContact(ViewObjectContact &rVOContact)
#define PRIMITIVE2D_ID_SDRGRAFPRIMITIVE2D
void setY(double fY)
#define PRIMITIVE2D_ID_POLYPOLYGONGRAPHICPRIMITIVE2D
css::uno::Reference< css::graphic::XPrimitive2D > Primitive2DReference
virtual void append(const Primitive2DReference &)=0
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:98
ViewContact & GetViewContact() const
#define PRIMITIVE2D_ID_SDRCAPTIONPRIMITIVE2D
double getX() const
basegfx::B2DRange getB2DRange(const geometry::ViewInformation2D &aViewInformation) const
B2DHomMatrix createTranslateB2DHomMatrix(double fTranslateX, double fTranslateY)
virtual drawinglayer::primitive2d::Primitive2DContainer createPrimitive2DSequence(const DisplayInfo &rDisplayInfo) const
const basegfx::B2DRange & getObjectRange() const
#define PRIMITIVE2D_ID_SDRRECTANGLEPRIMITIVE2D