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  {
215  // set local flag
216  mbLazyInvalidate = true;
217 
218  // force ObjectRange
219  getObjectRange();
220 
221  if(!maObjectRange.isEmpty())
222  {
223  // invalidate current valid range
225 
226  // reset ObjectRange, it needs to be recalculated
228  }
229 
230  // register at OC for lazy invalidate
232  }
233 }
234 
236 {
237  if(mbLazyInvalidate)
238  {
239  // reset flag
240  mbLazyInvalidate = false;
241 
242  // force ObjectRange
243  getObjectRange();
244 
245  if(!maObjectRange.isEmpty())
246  {
247  // invalidate current valid range
249  }
250  }
251 }
252 
253 // Take some action when new objects are inserted
255 {
256  // force creation of the new VOC and trigger it's refresh, so it
257  // will take part in LazyInvalidate immediately
259 
260  // forward action to ObjectContact
261  // const ViewObjectContact& rChildVOC = rChild.GetViewObjectContact(GetObjectContact());
262  // GetObjectContact().InvalidatePartOfView(rChildVOC.getObjectRange());
263 }
264 
266 {
267  // remove old one
268  mpPrimitiveAnimation.reset();
269 
270  // check for animated primitives
271  if(!mxPrimitive2DSequence.empty())
272  {
273  const bool bTextAnimationAllowed(GetObjectContact().IsTextAnimationAllowed());
274  const bool bGraphicAnimationAllowed(GetObjectContact().IsGraphicAnimationAllowed());
275 
276  if(bTextAnimationAllowed || bGraphicAnimationAllowed)
277  {
278  AnimatedExtractingProcessor2D aAnimatedExtractor(GetObjectContact().getViewInformation2D(),
279  bTextAnimationAllowed, bGraphicAnimationAllowed);
280  aAnimatedExtractor.process(mxPrimitive2DSequence);
281 
282  if(!aAnimatedExtractor.getPrimitive2DSequence().empty())
283  {
284  // derived primitiveList is animated, setup new PrimitiveAnimation
285  mpPrimitiveAnimation.reset( new sdr::animation::PrimitiveAnimation(*this, aAnimatedExtractor.getPrimitive2DSequence()) );
286  }
287  }
288  }
289 }
290 
292 {
293  // get the view-independent Primitive from the viewContact
294  drawinglayer::primitive2d::Primitive2DContainer xRetval(GetViewContact().getViewIndependentPrimitive2DContainer());
295 
296  if(!xRetval.empty())
297  {
298  // handle GluePoint
300  {
301  const drawinglayer::primitive2d::Primitive2DContainer xGlue(GetViewContact().createGluePointPrimitive2DSequence());
302 
303  if(!xGlue.empty())
304  {
305  xRetval.append(xGlue);
306  }
307  }
308 
309  // handle ghosted
310  if(isPrimitiveGhosted(rDisplayInfo))
311  {
312  const basegfx::BColor aRGBWhite(1.0, 1.0, 1.0);
313  const basegfx::BColorModifierSharedPtr aBColorModifier =
314  std::make_shared<basegfx::BColorModifier_interpolate>(
315  aRGBWhite,
316  0.5);
319  xRetval,
320  aBColorModifier));
321 
322  xRetval = drawinglayer::primitive2d::Primitive2DContainer { xReference };
323  }
324  }
325 
326  return xRetval;
327 }
328 
330 {
332 
333  // take care of redirectors and create new list
335 
336  if(pRedirector)
337  {
338  xNewPrimitiveSequence = pRedirector->createRedirectedPrimitive2DSequence(*this, rDisplayInfo);
339  }
340  else
341  {
342  xNewPrimitiveSequence = createPrimitive2DSequence(rDisplayInfo);
343  }
344 
345  // local up-to-date checks. New list different from local one?
346  if(mxPrimitive2DSequence != xNewPrimitiveSequence)
347  {
348  // has changed, copy content
349  const_cast< ViewObjectContact* >(this)->mxPrimitive2DSequence = xNewPrimitiveSequence;
350 
351  // check for animated stuff
352  const_cast< ViewObjectContact* >(this)->checkForPrimitive2DAnimations();
353 
354  // always update object range when PrimitiveSequence changes
355  const drawinglayer::geometry::ViewInformation2D& rViewInformation2D(GetObjectContact().getViewInformation2D());
356  const_cast< ViewObjectContact* >(this)->maObjectRange = mxPrimitive2DSequence.getB2DRange(rViewInformation2D);
357 
358  // check and eventually embed to GridOffset transform primitive
359  if(GetObjectContact().supportsGridOffsets())
360  {
361  const basegfx::B2DVector& rGridOffset(getGridOffset());
362 
363  if(0.0 != rGridOffset.getX() || 0.0 != rGridOffset.getY())
364  {
365  const basegfx::B2DHomMatrix aTranslateGridOffset(
367  rGridOffset));
370  aTranslateGridOffset,
372 
373  // Set values at local data. So for now, the mechanism is to reset some of the
374  // defining things (mxPrimitive2DSequence, maGridOffset) and re-create the
375  // buffered data (including maObjectRange). It *could* be changed to keep
376  // the unmodified PrimitiveSequence and only update the GridOffset, but this
377  // would require a 2nd instance of maObjectRange and mxPrimitive2DSequence. I
378  // started doing so, but it just makes the code more complicated. For now,
379  // just allow re-creation of the PrimitiveSequence (and removing buffered
380  // decomposed content of it). May be optimized, though. OTOH it only happens
381  // in calc which traditionally does not have a huge amount of DrawObjects anyways.
383  const_cast< ViewObjectContact* >(this)->maObjectRange.transform(aTranslateGridOffset);
384  }
385  }
386  }
387 
388  // return current Primitive2DContainer
389  return mxPrimitive2DSequence;
390 }
391 
392 bool ViewObjectContact::isPrimitiveVisible(const DisplayInfo& /*rDisplayInfo*/) const
393 {
394  // default: always visible
395  return true;
396 }
397 
398 bool ViewObjectContact::isPrimitiveGhosted(const DisplayInfo& rDisplayInfo) const
399 {
400  // default: standard check
401  return (GetObjectContact().DoVisualizeEnteredGroup() && !GetObjectContact().isOutputToPrinter() && rDisplayInfo.IsGhostedDrawModeActive());
402 }
403 
405 {
407 
408  // check model-view visibility
409  if(isPrimitiveVisible(rDisplayInfo))
410  {
411  xRetval = getPrimitive2DSequence(rDisplayInfo);
412 
413  if(!xRetval.empty())
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  {
424  // not visible, release
425  xRetval.clear();
426  }
427  }
428  }
429 
430  return xRetval;
431 }
432 
434 {
435  const sal_uInt32 nSubHierarchyCount(GetViewContact().GetObjectCount());
437 
438  for(sal_uInt32 a(0); a < nSubHierarchyCount; a++)
439  {
440  const ViewObjectContact& rCandidate(GetViewContact().GetViewContact(a).GetViewObjectContact(GetObjectContact()));
441 
442  xSeqRetval.append(rCandidate.getPrimitive2DSequenceHierarchy(rDisplayInfo));
443  }
444 
445  return xSeqRetval;
446 }
447 
448 // Support getting a GridOffset per object and view for non-linear ViewToDevice
449 // transformation (calc). On-demand created by delegating to the ObjectContact
450 // (->View) that has then all needed information
452 {
454  {
455  // create on-demand
456  GetObjectContact().calculateGridOffsetForViewOjectContact(const_cast<ViewObjectContact*>(this)->maGridOffset, *this);
457  }
458 
459  return maGridOffset;
460 }
461 
463 {
464  // reset buffered GridOffset itself
465  maGridOffset.setX(0.0);
466  maGridOffset.setY(0.0);
467 
468  // also reset sequence to get a re-calculation when GridOffset changes
469  mxPrimitive2DSequence.clear();
470 }
471 
472 }
473 
474 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
virtual void InvalidatePartOfView(const basegfx::B2DRange &rRange) const
#define PRIMITIVE2D_ID_GROUPPRIMITIVE2D
void setX(double fX)
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
double getX() const
#define PRIMITIVE2D_ID_SDRELLIPSEPRIMITIVE2D
virtual drawinglayer::primitive2d::Primitive2DContainer getPrimitive2DSequenceHierarchy(DisplayInfo &rDisplayInfo) const
ViewObjectContact & GetViewObjectContact(ObjectContact &rObjectContact)
Definition: viewcontact.cxx:71
double getY() const
virtual bool isPrimitiveGhosted(const DisplayInfo &rDisplayInfo) const
virtual void setLazyInvalidate(ViewObjectContact &rVOC)
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:99
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
#define PRIMITIVE2D_ID_POLYPOLYGONGRAPHICPRIMITIVE2D
css::uno::Reference< css::graphic::XPrimitive2D > Primitive2DReference
std::unique_ptr< sdr::animation::PrimitiveAnimation > mpPrimitiveAnimation
void setY(double fY)
bool IsGhostedDrawModeActive() const
Definition: displayinfo.hxx:95
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
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