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 };
71 
72 AnimatedExtractingProcessor2D::AnimatedExtractingProcessor2D(
73  const drawinglayer::geometry::ViewInformation2D& rViewInformation,
74  bool bTextAnimationAllowed,
75  bool bGraphicAnimationAllowed)
76 : drawinglayer::processor2d::BaseProcessor2D(rViewInformation),
77  maPrimitive2DSequence(),
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  maObjectRange(),
150  mxPrimitive2DSequence(),
151  maGridOffset(0.0, 0.0),
152  mbLazyInvalidate(false)
153 {
154  // make the ViewContact remember me
156 
157  // make the ObjectContact remember me
159 }
160 
162 {
163  // invalidate in view
164  if(!maObjectRange.isEmpty())
165  {
167  }
168 
169  // delete PrimitiveAnimation
170  mpPrimitiveAnimation.reset();
171 
172  // take care of remembered ObjectContact. Remove from
173  // OC first. The VC removal (below) CAN trigger a StopGettingViewed()
174  // which (depending of its implementation) may destroy other OCs. This
175  // can trigger the deletion of the helper OC of a page visualising object
176  // which IS the OC of this object. Eventually StopGettingViewed() needs
177  // to get asynchron later
179 
180  // take care of remembered ViewContact
182 }
183 
185 {
186  if(maObjectRange.isEmpty())
187  {
189  basegfx::B2DRange aTempRange = GetViewContact().getRange(rViewInfo2D);
190  if (!aTempRange.isEmpty())
191  {
192  const_cast< ViewObjectContact* >(this)->maObjectRange = aTempRange;
193  }
194  else
195  {
196  // if range is not computed (new or LazyInvalidate objects), force it
197  const DisplayInfo aDisplayInfo;
199 
200  if(!xSequence.empty())
201  {
202  const_cast< ViewObjectContact* >(this)->maObjectRange =
203  xSequence.getB2DRange(rViewInfo2D);
204  }
205  }
206  }
207 
208  return maObjectRange;
209 }
210 
212 {
213  if(mbLazyInvalidate)
214  return;
215 
216  // set local flag
217  mbLazyInvalidate = true;
218 
219  // force ObjectRange
220  getObjectRange();
221 
222  if(!maObjectRange.isEmpty())
223  {
224  // invalidate current valid range
226 
227  // reset gridOffset, it needs to be recalculated
228  if (GetObjectContact().supportsGridOffsets())
229  resetGridOffset();
230  else
232  }
233 
234  // register at OC for lazy invalidate
236 }
237 
239 {
240  if(!mbLazyInvalidate)
241  return;
242 
243  // reset flag
244  mbLazyInvalidate = false;
245 
246  // force ObjectRange
247  getObjectRange();
248 
249  if(!maObjectRange.isEmpty())
250  {
251  // invalidate current valid range
253  }
254 }
255 
256 // Take some action when new objects are inserted
258 {
259  // force creation of the new VOC and trigger it's refresh, so it
260  // will take part in LazyInvalidate immediately
262 
263  // forward action to ObjectContact
264  // const ViewObjectContact& rChildVOC = rChild.GetViewObjectContact(GetObjectContact());
265  // GetObjectContact().InvalidatePartOfView(rChildVOC.getObjectRange());
266 }
267 
269 {
270  // remove old one
271  mpPrimitiveAnimation.reset();
272 
273  // check for animated primitives
274  if(mxPrimitive2DSequence.empty())
275  return;
276 
277  const bool bTextAnimationAllowed(GetObjectContact().IsTextAnimationAllowed());
278  const bool bGraphicAnimationAllowed(GetObjectContact().IsGraphicAnimationAllowed());
279 
280  if(bTextAnimationAllowed || bGraphicAnimationAllowed)
281  {
282  AnimatedExtractingProcessor2D aAnimatedExtractor(GetObjectContact().getViewInformation2D(),
283  bTextAnimationAllowed, bGraphicAnimationAllowed);
284  aAnimatedExtractor.process(mxPrimitive2DSequence);
285 
286  if(!aAnimatedExtractor.getPrimitive2DSequence().empty())
287  {
288  // derived primitiveList is animated, setup new PrimitiveAnimation
289  mpPrimitiveAnimation.reset( new sdr::animation::PrimitiveAnimation(*this, aAnimatedExtractor.getPrimitive2DSequence()) );
290  }
291  }
292 }
293 
295 {
296  // get the view-independent Primitive from the viewContact
297  drawinglayer::primitive2d::Primitive2DContainer xRetval(GetViewContact().getViewIndependentPrimitive2DContainer());
298 
299  if(!xRetval.empty())
300  {
301  // handle GluePoint
303  {
304  const drawinglayer::primitive2d::Primitive2DContainer xGlue(GetViewContact().createGluePointPrimitive2DSequence());
305 
306  if(!xGlue.empty())
307  {
308  xRetval.append(xGlue);
309  }
310  }
311 
312  // handle ghosted
313  if(isPrimitiveGhosted(rDisplayInfo))
314  {
315  const basegfx::BColor aRGBWhite(1.0, 1.0, 1.0);
316  const basegfx::BColorModifierSharedPtr aBColorModifier =
317  std::make_shared<basegfx::BColorModifier_interpolate>(
318  aRGBWhite,
319  0.5);
322  xRetval,
323  aBColorModifier));
324 
325  xRetval = drawinglayer::primitive2d::Primitive2DContainer { xReference };
326  }
327  }
328 
329  return xRetval;
330 }
331 
333 {
335 
336  // take care of redirectors and create new list
338 
339  if(pRedirector)
340  {
341  xNewPrimitiveSequence = pRedirector->createRedirectedPrimitive2DSequence(*this, rDisplayInfo);
342  }
343  else
344  {
345  xNewPrimitiveSequence = createPrimitive2DSequence(rDisplayInfo);
346  }
347 
348  // local up-to-date checks. New list different from local one?
349  if(mxPrimitive2DSequence == xNewPrimitiveSequence)
350  return mxPrimitive2DSequence;
351 
352  // has changed, copy content
353  const_cast< ViewObjectContact* >(this)->mxPrimitive2DSequence = std::move(xNewPrimitiveSequence);
354 
355  // check for animated stuff
356  const_cast< ViewObjectContact* >(this)->checkForPrimitive2DAnimations();
357 
358  // always update object range when PrimitiveSequence changes
359  const drawinglayer::geometry::ViewInformation2D& rViewInformation2D(GetObjectContact().getViewInformation2D());
360  const_cast< ViewObjectContact* >(this)->maObjectRange = mxPrimitive2DSequence.getB2DRange(rViewInformation2D);
361 
362  // check and eventually embed to GridOffset transform primitive
363  if(GetObjectContact().supportsGridOffsets())
364  {
365  const basegfx::B2DVector& rGridOffset(getGridOffset());
366 
367  if(0.0 != rGridOffset.getX() || 0.0 != rGridOffset.getY())
368  {
369  const basegfx::B2DHomMatrix aTranslateGridOffset(
371  rGridOffset));
374  aTranslateGridOffset,
376 
377  // Set values at local data. So for now, the mechanism is to reset some of the
378  // defining things (mxPrimitive2DSequence, maGridOffset) and re-create the
379  // buffered data (including maObjectRange). It *could* be changed to keep
380  // the unmodified PrimitiveSequence and only update the GridOffset, but this
381  // would require a 2nd instance of maObjectRange and mxPrimitive2DSequence. I
382  // started doing so, but it just makes the code more complicated. For now,
383  // just allow re-creation of the PrimitiveSequence (and removing buffered
384  // decomposed content of it). May be optimized, though. OTOH it only happens
385  // in calc which traditionally does not have a huge amount of DrawObjects anyways.
387  const_cast< ViewObjectContact* >(this)->maObjectRange.transform(aTranslateGridOffset);
388  }
389  }
390 
391  // return current Primitive2DContainer
392  return mxPrimitive2DSequence;
393 }
394 
395 bool ViewObjectContact::isPrimitiveVisible(const DisplayInfo& /*rDisplayInfo*/) const
396 {
397  // default: always visible
398  return true;
399 }
400 
401 bool ViewObjectContact::isPrimitiveGhosted(const DisplayInfo& rDisplayInfo) const
402 {
403  // default: standard check
404  return (GetObjectContact().DoVisualizeEnteredGroup() && !GetObjectContact().isOutputToPrinter() && rDisplayInfo.IsGhostedDrawModeActive());
405 }
406 
408 {
410 
411  // check model-view visibility
412  if(isPrimitiveVisible(rDisplayInfo))
413  {
414  xRetval = getPrimitive2DSequence(rDisplayInfo);
415 
416  if(!xRetval.empty())
417  {
418  // get ranges
419  const drawinglayer::geometry::ViewInformation2D& rViewInformation2D(GetObjectContact().getViewInformation2D());
420  const basegfx::B2DRange aObjectRange(xRetval.getB2DRange(rViewInformation2D));
421  const basegfx::B2DRange aViewRange(rViewInformation2D.getViewport());
422 
423  // check geometrical visibility
424  bool bVisible = aViewRange.isEmpty() || aViewRange.overlaps(aObjectRange);
425  if(!bVisible)
426  {
427  // not visible, release
428  xRetval.clear();
429  }
430  }
431  }
432 
433  return xRetval;
434 }
435 
437 {
438  const sal_uInt32 nSubHierarchyCount(GetViewContact().GetObjectCount());
440 
441  for(sal_uInt32 a(0); a < nSubHierarchyCount; a++)
442  {
443  const ViewObjectContact& rCandidate(GetViewContact().GetViewContact(a).GetViewObjectContact(GetObjectContact()));
444 
445  xSeqRetval.append(rCandidate.getPrimitive2DSequenceHierarchy(rDisplayInfo));
446  }
447 
448  return xSeqRetval;
449 }
450 
451 // Support getting a GridOffset per object and view for non-linear ViewToDevice
452 // transformation (calc). On-demand created by delegating to the ObjectContact
453 // (->View) that has then all needed information
455 {
457  {
458  // create on-demand
459  GetObjectContact().calculateGridOffsetForViewOjectContact(const_cast<ViewObjectContact*>(this)->maGridOffset, *this);
460  }
461 
462  return maGridOffset;
463 }
464 
466 {
467  // reset buffered GridOffset itself
468  maGridOffset.setX(0.0);
469  maGridOffset.setY(0.0);
470 
471  // also reset sequence to get a re-calculation when GridOffset changes
472  mxPrimitive2DSequence.clear();
474 }
475 
476 }
477 
478 /* 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
virtual drawinglayer::primitive2d::Primitive2DContainer getPrimitive2DSequenceHierarchy(DisplayInfo &rDisplayInfo) const
ViewObjectContact & GetViewObjectContact(ObjectContact &rObjectContact)
Definition: viewcontact.cxx:67
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:95
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
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
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)
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