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