LibreOffice Module svx (master)  1
overlaymanagerbuffered.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 
21 #include <svx/sdrpaintwindow.hxx>
22 #include <vcl/outdev.hxx>
24 #include <vcl/window.hxx>
25 #include <tools/fract.hxx>
26 #include <vcl/cursor.hxx>
27 
28 
29 namespace sdr::overlay
30 {
32  {
33  // compare size of mpBufferDevice with size of visible area
34  if(mpBufferDevice->GetOutputSizePixel() != getOutputDevice().GetOutputSizePixel())
35  {
36  // set new buffer size, copy as much content as possible (use bool parameter for vcl).
37  // Newly uncovered regions will be repainted.
38  mpBufferDevice->SetOutputSizePixel(getOutputDevice().GetOutputSizePixel(), false);
39  }
40 
41  // compare the MapModes for zoom/scroll changes
42  if(mpBufferDevice->GetMapMode() != getOutputDevice().GetMapMode())
43  {
44  const bool bZoomed(
45  mpBufferDevice->GetMapMode().GetScaleX() != getOutputDevice().GetMapMode().GetScaleX()
46  || mpBufferDevice->GetMapMode().GetScaleY() != getOutputDevice().GetMapMode().GetScaleY());
47 
48  if(!bZoomed)
49  {
50  const Point& rOriginOld = mpBufferDevice->GetMapMode().GetOrigin();
51  const Point& rOriginNew = getOutputDevice().GetMapMode().GetOrigin();
52  const bool bScrolled(rOriginOld != rOriginNew);
53 
54  if(bScrolled)
55  {
56  // get pixel bounds
57  const Point aOriginOldPixel(mpBufferDevice->LogicToPixel(rOriginOld));
58  const Point aOriginNewPixel(mpBufferDevice->LogicToPixel(rOriginNew));
59  const Size aOutputSizePixel(mpBufferDevice->GetOutputSizePixel());
60 
61  // remember and switch off MapMode
62  const bool bMapModeWasEnabled(mpBufferDevice->IsMapModeEnabled());
63  mpBufferDevice->EnableMapMode(false);
64 
65  // scroll internally buffered stuff
66  const Point aDestinationOffsetPixel(aOriginNewPixel - aOriginOldPixel);
67  mpBufferDevice->DrawOutDev(
68  aDestinationOffsetPixel, aOutputSizePixel, // destination
69  Point(), aOutputSizePixel); // source
70 
71  // restore MapMode
72  mpBufferDevice->EnableMapMode(bMapModeWasEnabled);
73 
74  // scroll remembered region, too.
76  {
77  const basegfx::B2IPoint aIPointDestinationOffsetPixel(aDestinationOffsetPixel.X(), aDestinationOffsetPixel.Y());
78  const basegfx::B2IPoint aNewMinimum(maBufferRememberedRangePixel.getMinimum() + aIPointDestinationOffsetPixel);
79  const basegfx::B2IPoint aNewMaximum(maBufferRememberedRangePixel.getMaximum() + aIPointDestinationOffsetPixel);
80  maBufferRememberedRangePixel = basegfx::B2IRange(aNewMinimum, aNewMaximum);
81  }
82  }
83  }
84 
85  // copy new MapMode
86  mpBufferDevice->SetMapMode(getOutputDevice().GetMapMode());
87  }
88 
89  // #i29186#
90  mpBufferDevice->SetDrawMode(getOutputDevice().GetDrawMode());
91  mpBufferDevice->SetSettings(getOutputDevice().GetSettings());
92  mpBufferDevice->SetAntialiasing(getOutputDevice().GetAntialiasing());
93  }
94 
96  {
97  const tools::Rectangle aRegionRectanglePixel(
100  const vcl::Region aRegionPixel(aRegionRectanglePixel);
101 
102  ImpRestoreBackground(aRegionPixel);
103  }
104 
106  {
107  // MapModes off
108  const bool bMapModeWasEnabledDest(getOutputDevice().IsMapModeEnabled());
109  const bool bMapModeWasEnabledSource(mpBufferDevice->IsMapModeEnabled());
111  const_cast<OverlayManagerBuffered*>(this)->mpBufferDevice->EnableMapMode(false);
112 
113  // local region
114  RectangleVector aRectangles;
115  rRegionPixel.GetRegionRectangles(aRectangles);
116 
117  for(const auto& rRect : aRectangles)
118  {
119  // restore the area
120  const Point aTopLeft(rRect.TopLeft());
121  const Size aSize(rRect.GetSize());
122 
124  aTopLeft, aSize, // destination
125  aTopLeft, aSize, // source
126  *mpBufferDevice);
127  }
128 
129  // restore MapModes
130  getOutputDevice().EnableMapMode(bMapModeWasEnabledDest);
131  const_cast<OverlayManagerBuffered*>(this)->mpBufferDevice->EnableMapMode(bMapModeWasEnabledSource);
132  }
133 
135  {
136  // prepare source
137  OutputDevice& rSource = pPreRenderDevice ? *pPreRenderDevice : getOutputDevice();
138 
139  // Ensure buffer is valid
141 
142  // build region which needs to be copied
143  vcl::Region aRegion(rSource.LogicToPixel(rRegion));
144 
145  // limit to PaintRegion if it's a window. This will be evtl. the expanded one,
146  // but always the exact redraw area
147  if(OUTDEV_WINDOW == rSource.GetOutDevType())
148  {
149  vcl::Window& rWindow = static_cast<vcl::Window&>(rSource);
150  vcl::Region aPaintRegionPixel = rWindow.LogicToPixel(rWindow.GetPaintRegion());
151  aRegion.Intersect(aPaintRegionPixel);
152 
153  // #i72754# Make sure content is completely rendered, the window
154  // will be used as source of a DrawOutDev soon
155  rWindow.Flush();
156  }
157 
158  // also limit to buffer size
159  const tools::Rectangle aBufferDeviceRectanglePixel(Point(), mpBufferDevice->GetOutputSizePixel());
160  aRegion.Intersect(aBufferDeviceRectanglePixel);
161 
162  // MapModes off
163  const bool bMapModeWasEnabledDest(rSource.IsMapModeEnabled());
164  const bool bMapModeWasEnabledSource(mpBufferDevice->IsMapModeEnabled());
165  rSource.EnableMapMode(false);
166  mpBufferDevice->EnableMapMode(false);
167 
168  // prepare to iterate over the rectangles from the region in pixels
169  RectangleVector aRectangles;
170  aRegion.GetRegionRectangles(aRectangles);
171 
172  for(const auto& rRect : aRectangles)
173  {
174  // for each rectangle, save the area
175  const Point aTopLeft(rRect.TopLeft());
176  const Size aSize(rRect.GetSize());
177 
178  mpBufferDevice->DrawOutDev(
179  aTopLeft, aSize, // destination
180  aTopLeft, aSize, // source
181  rSource);
182  }
183 
184  // restore MapModes
185  rSource.EnableMapMode(bMapModeWasEnabledDest);
186  mpBufferDevice->EnableMapMode(bMapModeWasEnabledSource);
187  }
188 
189  IMPL_LINK_NOARG(OverlayManagerBuffered, ImpBufferTimerHandler, Timer*, void)
190  {
191  //Resolves: fdo#46728 ensure this exists until end of scope
192  rtl::Reference<OverlayManager> xKeepAlive(this);
193 
194  // stop timer
195  maBufferIdle.Stop();
196 
197  if(!maBufferRememberedRangePixel.isEmpty())
198  {
199  // logic size for impDrawMember call
200  basegfx::B2DRange aBufferRememberedRangeLogic(
201  maBufferRememberedRangePixel.getMinX(), maBufferRememberedRangePixel.getMinY(),
202  maBufferRememberedRangePixel.getMaxX(), maBufferRememberedRangePixel.getMaxY());
203  aBufferRememberedRangeLogic.transform(getOutputDevice().GetInverseViewTransformation());
204 
205  // prepare cursor handling
206  const bool bTargetIsWindow(OUTDEV_WINDOW == mrOutputDevice.GetOutDevType());
207  bool bCursorWasEnabled(false);
208 
209  // #i80730# switch off VCL cursor during overlay refresh
210  if(bTargetIsWindow)
211  {
212  vcl::Window& rWindow = static_cast< vcl::Window& >(mrOutputDevice);
213  vcl::Cursor* pCursor = rWindow.GetCursor();
214 
215  if(pCursor && pCursor->IsVisible())
216  {
217  pCursor->Hide();
218  bCursorWasEnabled = true;
219  }
220  }
221 
222  // refresh with prerendering
223  {
224  // #i73602# ensure valid and sized mpOutputBufferDevice
225  const Size aDestinationSizePixel(mpBufferDevice->GetOutputSizePixel());
226  const Size aOutputBufferSizePixel(mpOutputBufferDevice->GetOutputSizePixel());
227 
228  if(aDestinationSizePixel != aOutputBufferSizePixel)
229  {
230  mpOutputBufferDevice->SetOutputSizePixel(aDestinationSizePixel);
231  }
232 
233  mpOutputBufferDevice->SetMapMode(getOutputDevice().GetMapMode());
234  mpOutputBufferDevice->EnableMapMode(false);
235  mpOutputBufferDevice->SetDrawMode(mpBufferDevice->GetDrawMode());
236  mpOutputBufferDevice->SetSettings(mpBufferDevice->GetSettings());
237  mpOutputBufferDevice->SetAntialiasing(mpBufferDevice->GetAntialiasing());
238 
239  // calculate sizes
240  tools::Rectangle aRegionRectanglePixel(
241  maBufferRememberedRangePixel.getMinX(), maBufferRememberedRangePixel.getMinY(),
242  maBufferRememberedRangePixel.getMaxX(), maBufferRememberedRangePixel.getMaxY());
243 
244  // truncate aRegionRectanglePixel to destination pixel size, more does
245  // not need to be prepared since destination is a buffer for a window. So,
246  // maximum size indirectly shall be limited to getOutputDevice().GetOutputSizePixel()
247  if(aRegionRectanglePixel.Left() < 0)
248  {
249  aRegionRectanglePixel.SetLeft( 0 );
250  }
251 
252  if(aRegionRectanglePixel.Top() < 0)
253  {
254  aRegionRectanglePixel.SetTop( 0 );
255  }
256 
257  if(aRegionRectanglePixel.Right() > aDestinationSizePixel.getWidth())
258  {
259  aRegionRectanglePixel.SetRight( aDestinationSizePixel.getWidth() );
260  }
261 
262  if(aRegionRectanglePixel.Bottom() > aDestinationSizePixel.getHeight())
263  {
264  aRegionRectanglePixel.SetBottom( aDestinationSizePixel.getHeight() );
265  }
266 
267  // get sizes
268  const Point aTopLeft(aRegionRectanglePixel.TopLeft());
269  const Size aSize(aRegionRectanglePixel.GetSize());
270 
271  {
272  const bool bMapModeWasEnabledDest(mpBufferDevice->IsMapModeEnabled());
273  mpBufferDevice->EnableMapMode(false);
274 
275  mpOutputBufferDevice->DrawOutDev(
276  aTopLeft, aSize, // destination
277  aTopLeft, aSize, // source
278  *mpBufferDevice);
279 
280  // restore MapModes
281  mpBufferDevice->EnableMapMode(bMapModeWasEnabledDest);
282  }
283 
284  // paint overlay content for remembered region, use
285  // method from base class directly
286  mpOutputBufferDevice->EnableMapMode();
287  OverlayManager::ImpDrawMembers(aBufferRememberedRangeLogic, *mpOutputBufferDevice);
288  mpOutputBufferDevice->EnableMapMode(false);
289 
290  // copy to output
291  {
292  const bool bMapModeWasEnabledDest(getOutputDevice().IsMapModeEnabled());
293  getOutputDevice().EnableMapMode(false);
294 
295  getOutputDevice().DrawOutDev(
296  aTopLeft, aSize, // destination
297  aTopLeft, aSize, // source
298  *mpOutputBufferDevice);
299 
300  // debug
301  /*getOutputDevice().SetLineCOL_RED);
302  getOutputDevice().SetFillColor();
303  getOutputDevice().DrawRect(Rectangle(aTopLeft, aSize));*/
304 
305  // restore MapModes
306  getOutputDevice().EnableMapMode(bMapModeWasEnabledDest);
307  }
308  }
309 
310  // VCL hack for transparent child windows
311  // Problem is e.g. a radiobutton form control in life mode. The used window
312  // is a transparence vcl childwindow. This flag only allows the parent window to
313  // paint into the child windows area, but there is no mechanism which takes
314  // care for a repaint of the child window. A transparent child window is NOT
315  // a window which always keeps it's content consistent over the parent, but it's
316  // more like just a paint flag for the parent.
317  // To get the update, the windows in question are updated manually here.
318  if(bTargetIsWindow)
319  {
320  vcl::Window& rWindow = static_cast< vcl::Window& >(mrOutputDevice);
321 
322  const tools::Rectangle aRegionRectanglePixel(
323  maBufferRememberedRangePixel.getMinX(),
324  maBufferRememberedRangePixel.getMinY(),
325  maBufferRememberedRangePixel.getMaxX(),
326  maBufferRememberedRangePixel.getMaxY());
327  PaintTransparentChildren(rWindow, aRegionRectanglePixel);
328  }
329 
330  // #i80730# restore visibility of VCL cursor
331  if(bCursorWasEnabled)
332  {
333  vcl::Window& rWindow = static_cast< vcl::Window& >(mrOutputDevice);
334  vcl::Cursor* pCursor = rWindow.GetCursor();
335 
336  if(pCursor)
337  {
338  // check if cursor still exists. It may have been deleted from someone
339  pCursor->Show();
340  }
341  }
342 
343  // forget remembered Region
344  maBufferRememberedRangePixel.reset();
345  }
346  }
347 
349  OutputDevice& rOutputDevice)
350  : OverlayManager(rOutputDevice),
351  mpBufferDevice(VclPtr<VirtualDevice>::Create()),
352  mpOutputBufferDevice(VclPtr<VirtualDevice>::Create()),
353  maBufferIdle("sdr overlay OverlayManagerBuffered Idle")
354  {
355  // Init timer
356  maBufferIdle.SetPriority( TaskPriority::POST_PAINT );
357  maBufferIdle.SetInvokeHandler(LINK(this, OverlayManagerBuffered, ImpBufferTimerHandler));
358  maBufferIdle.SetDebugName( "sdr::overlay::OverlayManagerBuffered maBufferIdle" );
359  }
360 
362  OutputDevice& rOutputDevice)
363  {
364  return rtl::Reference<OverlayManager>(new OverlayManagerBuffered(rOutputDevice));
365  }
366 
368  {
369  // Clear timer
370  maBufferIdle.Stop();
371 
373  {
374  // Restore all rectangles for remembered region from buffer
376  }
377  }
378 
379  void OverlayManagerBuffered::completeRedraw(const vcl::Region& rRegion, OutputDevice* pPreRenderDevice) const
380  {
381  if(!rRegion.IsEmpty())
382  {
383  // save new background
384  const_cast<OverlayManagerBuffered*>(this)->ImpSaveBackground(rRegion, pPreRenderDevice);
385  }
386 
387  // call parent
388  OverlayManager::completeRedraw(rRegion, pPreRenderDevice);
389  }
390 
392  {
393  // call timer handler direct
394  ImpBufferTimerHandler(nullptr);
395  }
396 
398  {
399  if(!rRange.isEmpty())
400  {
401  // buffered output, do not invalidate but use the timer
402  // to trigger a timer event for refresh
404 
405  // add the discrete range to the remembered region
406  // #i75163# use double precision and floor/ceil rounding to get overlapped pixel region, even
407  // when the given logic region has a width/height of 0.0. This does NOT work with LogicToPixel
408  // since it just transforms the top left and bottom right points equally without taking
409  // discrete pixel coverage into account. An empty B2DRange and thus empty logic Rectangle translated
410  // to an also empty discrete pixel rectangle - what is wrong.
411  basegfx::B2DRange aDiscreteRange(rRange);
412  aDiscreteRange.transform(getOutputDevice().GetViewTransformation());
413 
415  {
416  // assume AA needs one pixel more and invalidate one pixel more
417  const double fDiscreteOne(getDiscreteOne());
418  const basegfx::B2IPoint aTopLeft(
419  static_cast<sal_Int32>(floor(aDiscreteRange.getMinX() - fDiscreteOne)),
420  static_cast<sal_Int32>(floor(aDiscreteRange.getMinY() - fDiscreteOne)));
421  const basegfx::B2IPoint aBottomRight(
422  static_cast<sal_Int32>(ceil(aDiscreteRange.getMaxX() + fDiscreteOne)),
423  static_cast<sal_Int32>(ceil(aDiscreteRange.getMaxY() + fDiscreteOne)));
424 
426  maBufferRememberedRangePixel.expand(aBottomRight);
427  }
428  else
429  {
430  const basegfx::B2IPoint aTopLeft(static_cast<sal_Int32>(floor(aDiscreteRange.getMinX())), static_cast<sal_Int32>(floor(aDiscreteRange.getMinY())));
431  const basegfx::B2IPoint aBottomRight(static_cast<sal_Int32>(ceil(aDiscreteRange.getMaxX())), static_cast<sal_Int32>(ceil(aDiscreteRange.getMaxY())));
432 
434  maBufferRememberedRangePixel.expand(aBottomRight);
435  }
436  }
437  }
438 } // end of namespace
439 
440 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
const Fraction & GetScaleX() const
OutDevType GetOutDevType() const
ScopedVclPtr< VirtualDevice > mpBufferDevice
void Create(SwFormatVertOrient &rItem, SvStream &rStrm, sal_uInt16 nVersionAbusedAsSize)
bool IsMapModeEnabled() const
void Flush() override
sal_Int32 getMinY() const
const MapMode & GetMapMode() const
vcl::Region GetPaintRegion() const
void EnableMapMode(bool bEnable=true)
double getMaxX() const
void PaintTransparentChildren(vcl::Window const &rWindow, tools::Rectangle const &rPixelRect)
paint the transparent children of rWin that overlap rPixelRect (for example, transparent form control...
std::vector< tools::Rectangle > RectangleVector
sal_Int32 getMaxX() const
OUTDEV_WINDOW
const Fraction & GetScaleY() const
BASEGFX_DLLPUBLIC void transform(const B2DHomMatrix &rMatrix)
virtual void Start() override
double getMaxY() const
void SetDebugName(const char *pDebugName)
bool isEmpty() const
bool IsEmpty() const
void expand(const B2ITuple &rTuple)
virtual void completeRedraw(const vcl::Region &rRegion, OutputDevice *pPreRenderDevice=nullptr) const override
B2IPoint getMinimum() const
void ImpSaveBackground(const vcl::Region &rRegion, OutputDevice *pPreRenderDevice)
static rtl::Reference< OverlayManager > create(OutputDevice &rOutputDevice)
vcl::Cursor * GetCursor() const
sal_Int32 getMinX() const
Size GetOutputSizePixel() const
Point LogicToPixel(const Point &rLogicPt) const
virtual void invalidateRange(const basegfx::B2DRange &rRange) override
OutputDevice & getOutputDevice() const
double getMinY() const
SvtOptionsDrawinglayer maDrawinglayerOpt
void Stop()
virtual void completeRedraw(const vcl::Region &rRegion, OutputDevice *pPreRenderDevice=nullptr) const
OverlayManagerBuffered(OutputDevice &rOutputDevice)
void ImpDrawMembers(const basegfx::B2DRange &rRange, OutputDevice &rDestinationDevice) const
void GetRegionRectangles(RectangleVector &rTarget) const
bool isEmpty() const
void SetInvokeHandler(const Link< Timer *, void > &rLink)
const Point & GetOrigin() const
bool IsVisible() const
bool IsAntiAliasing() const
sal_Int32 getMaxY() const
void SetLeft(long v)
double getMinX() const
void SetPriority(TaskPriority ePriority)
SAL_DLLPRIVATE void DrawOutDev(const Point &, const Size &, const Point &, const Size &, const Printer &)=delete
IMPL_LINK_NOARG(OverlayManagerBuffered, ImpBufferTimerHandler, Timer *, void)
B2IPoint getMaximum() const