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  return;
199 
200  // logic size for impDrawMember call
201  basegfx::B2DRange aBufferRememberedRangeLogic(
202  maBufferRememberedRangePixel.getMinX(), maBufferRememberedRangePixel.getMinY(),
203  maBufferRememberedRangePixel.getMaxX(), maBufferRememberedRangePixel.getMaxY());
204  aBufferRememberedRangeLogic.transform(getOutputDevice().GetInverseViewTransformation());
205 
206  // prepare cursor handling
207  const bool bTargetIsWindow(OUTDEV_WINDOW == mrOutputDevice.GetOutDevType());
208  bool bCursorWasEnabled(false);
209 
210  // #i80730# switch off VCL cursor during overlay refresh
211  if(bTargetIsWindow)
212  {
213  vcl::Window& rWindow = static_cast< vcl::Window& >(mrOutputDevice);
214  vcl::Cursor* pCursor = rWindow.GetCursor();
215 
216  if(pCursor && pCursor->IsVisible())
217  {
218  pCursor->Hide();
219  bCursorWasEnabled = true;
220  }
221  }
222 
223  // refresh with prerendering
224  {
225  // #i73602# ensure valid and sized mpOutputBufferDevice
226  const Size aDestinationSizePixel(mpBufferDevice->GetOutputSizePixel());
227  const Size aOutputBufferSizePixel(mpOutputBufferDevice->GetOutputSizePixel());
228 
229  if(aDestinationSizePixel != aOutputBufferSizePixel)
230  {
231  mpOutputBufferDevice->SetOutputSizePixel(aDestinationSizePixel);
232  }
233 
234  mpOutputBufferDevice->SetMapMode(getOutputDevice().GetMapMode());
235  mpOutputBufferDevice->EnableMapMode(false);
236  mpOutputBufferDevice->SetDrawMode(mpBufferDevice->GetDrawMode());
237  mpOutputBufferDevice->SetSettings(mpBufferDevice->GetSettings());
238  mpOutputBufferDevice->SetAntialiasing(mpBufferDevice->GetAntialiasing());
239 
240  // calculate sizes
241  tools::Rectangle aRegionRectanglePixel(
242  maBufferRememberedRangePixel.getMinX(), maBufferRememberedRangePixel.getMinY(),
243  maBufferRememberedRangePixel.getMaxX(), maBufferRememberedRangePixel.getMaxY());
244 
245  // truncate aRegionRectanglePixel to destination pixel size, more does
246  // not need to be prepared since destination is a buffer for a window. So,
247  // maximum size indirectly shall be limited to getOutputDevice().GetOutputSizePixel()
248  if(aRegionRectanglePixel.Left() < 0)
249  {
250  aRegionRectanglePixel.SetLeft( 0 );
251  }
252 
253  if(aRegionRectanglePixel.Top() < 0)
254  {
255  aRegionRectanglePixel.SetTop( 0 );
256  }
257 
258  if(aRegionRectanglePixel.Right() > aDestinationSizePixel.getWidth())
259  {
260  aRegionRectanglePixel.SetRight( aDestinationSizePixel.getWidth() );
261  }
262 
263  if(aRegionRectanglePixel.Bottom() > aDestinationSizePixel.getHeight())
264  {
265  aRegionRectanglePixel.SetBottom( aDestinationSizePixel.getHeight() );
266  }
267 
268  // get sizes
269  const Point aTopLeft(aRegionRectanglePixel.TopLeft());
270  const Size aSize(aRegionRectanglePixel.GetSize());
271 
272  {
273  const bool bMapModeWasEnabledDest(mpBufferDevice->IsMapModeEnabled());
274  mpBufferDevice->EnableMapMode(false);
275 
276  mpOutputBufferDevice->DrawOutDev(
277  aTopLeft, aSize, // destination
278  aTopLeft, aSize, // source
279  *mpBufferDevice);
280 
281  // restore MapModes
282  mpBufferDevice->EnableMapMode(bMapModeWasEnabledDest);
283  }
284 
285  // paint overlay content for remembered region, use
286  // method from base class directly
287  mpOutputBufferDevice->EnableMapMode();
288  OverlayManager::ImpDrawMembers(aBufferRememberedRangeLogic, *mpOutputBufferDevice);
289  mpOutputBufferDevice->EnableMapMode(false);
290 
291  // copy to output
292  {
293  const bool bMapModeWasEnabledDest(getOutputDevice().IsMapModeEnabled());
294  getOutputDevice().EnableMapMode(false);
295 
296  getOutputDevice().DrawOutDev(
297  aTopLeft, aSize, // destination
298  aTopLeft, aSize, // source
299  *mpOutputBufferDevice);
300 
301  // debug
302  /*getOutputDevice().SetLineCOL_RED);
303  getOutputDevice().SetFillColor();
304  getOutputDevice().DrawRect(Rectangle(aTopLeft, aSize));*/
305 
306  // restore MapModes
307  getOutputDevice().EnableMapMode(bMapModeWasEnabledDest);
308  }
309  }
310 
311  // VCL hack for transparent child windows
312  // Problem is e.g. a radiobutton form control in life mode. The used window
313  // is a transparence vcl childwindow. This flag only allows the parent window to
314  // paint into the child windows area, but there is no mechanism which takes
315  // care for a repaint of the child window. A transparent child window is NOT
316  // a window which always keeps it's content consistent over the parent, but it's
317  // more like just a paint flag for the parent.
318  // To get the update, the windows in question are updated manually here.
319  if(bTargetIsWindow)
320  {
321  vcl::Window& rWindow = static_cast< vcl::Window& >(mrOutputDevice);
322 
323  const tools::Rectangle aRegionRectanglePixel(
324  maBufferRememberedRangePixel.getMinX(),
325  maBufferRememberedRangePixel.getMinY(),
326  maBufferRememberedRangePixel.getMaxX(),
327  maBufferRememberedRangePixel.getMaxY());
328  PaintTransparentChildren(rWindow, aRegionRectanglePixel);
329  }
330 
331  // #i80730# restore visibility of VCL cursor
332  if(bCursorWasEnabled)
333  {
334  vcl::Window& rWindow = static_cast< vcl::Window& >(mrOutputDevice);
335  vcl::Cursor* pCursor = rWindow.GetCursor();
336 
337  if(pCursor)
338  {
339  // check if cursor still exists. It may have been deleted from someone
340  pCursor->Show();
341  }
342  }
343 
344  // forget remembered Region
345  maBufferRememberedRangePixel.reset();
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  return;
401 
402  // buffered output, do not invalidate but use the timer
403  // to trigger a timer event for refresh
405 
406  // add the discrete range to the remembered region
407  // #i75163# use double precision and floor/ceil rounding to get overlapped pixel region, even
408  // when the given logic region has a width/height of 0.0. This does NOT work with LogicToPixel
409  // since it just transforms the top left and bottom right points equally without taking
410  // discrete pixel coverage into account. An empty B2DRange and thus empty logic Rectangle translated
411  // to an also empty discrete pixel rectangle - what is wrong.
412  basegfx::B2DRange aDiscreteRange(rRange);
413  aDiscreteRange.transform(getOutputDevice().GetViewTransformation());
414 
416  {
417  // assume AA needs one pixel more and invalidate one pixel more
418  const double fDiscreteOne(getDiscreteOne());
419  const basegfx::B2IPoint aTopLeft(
420  static_cast<sal_Int32>(floor(aDiscreteRange.getMinX() - fDiscreteOne)),
421  static_cast<sal_Int32>(floor(aDiscreteRange.getMinY() - fDiscreteOne)));
422  const basegfx::B2IPoint aBottomRight(
423  static_cast<sal_Int32>(ceil(aDiscreteRange.getMaxX() + fDiscreteOne)),
424  static_cast<sal_Int32>(ceil(aDiscreteRange.getMaxY() + fDiscreteOne)));
425 
427  maBufferRememberedRangePixel.expand(aBottomRight);
428  }
429  else
430  {
431  const basegfx::B2IPoint aTopLeft(static_cast<sal_Int32>(floor(aDiscreteRange.getMinX())), static_cast<sal_Int32>(floor(aDiscreteRange.getMinY())));
432  const basegfx::B2IPoint aBottomRight(static_cast<sal_Int32>(ceil(aDiscreteRange.getMaxX())), static_cast<sal_Int32>(ceil(aDiscreteRange.getMaxY())));
433 
435  maBufferRememberedRangePixel.expand(aBottomRight);
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
void SetLeft(tools::Long v)
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
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