Bump version to 6.4-15
[LibreOffice.git] / svx / source / sdr / overlay / overlaymanagerbuffered.cxx
blob6aa571b3486d01cd5deca5b2e4d065f686ca536d
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <sdr/overlay/overlaymanagerbuffered.hxx>
21 #include <svx/sdrpaintwindow.hxx>
22 #include <vcl/outdev.hxx>
23 #include <basegfx/point/b2dpoint.hxx>
24 #include <basegfx/range/b2drange.hxx>
25 #include <vcl/window.hxx>
26 #include <tools/stream.hxx>
27 #include <tools/fract.hxx>
28 #include <basegfx/matrix/b2dhommatrix.hxx>
29 #include <vcl/cursor.hxx>
30 #include <vcl/dibtools.hxx>
33 namespace sdr
35 namespace overlay
37 void OverlayManagerBuffered::ImpPrepareBufferDevice()
39 // compare size of mpBufferDevice with size of visible area
40 if(mpBufferDevice->GetOutputSizePixel() != getOutputDevice().GetOutputSizePixel())
42 // set new buffer size, copy as much content as possible (use bool parameter for vcl).
43 // Newly uncovered regions will be repainted.
44 mpBufferDevice->SetOutputSizePixel(getOutputDevice().GetOutputSizePixel(), false);
47 // compare the MapModes for zoom/scroll changes
48 if(mpBufferDevice->GetMapMode() != getOutputDevice().GetMapMode())
50 const bool bZoomed(
51 mpBufferDevice->GetMapMode().GetScaleX() != getOutputDevice().GetMapMode().GetScaleX()
52 || mpBufferDevice->GetMapMode().GetScaleY() != getOutputDevice().GetMapMode().GetScaleY());
54 if(!bZoomed)
56 const Point& rOriginOld = mpBufferDevice->GetMapMode().GetOrigin();
57 const Point& rOriginNew = getOutputDevice().GetMapMode().GetOrigin();
58 const bool bScrolled(rOriginOld != rOriginNew);
60 if(bScrolled)
62 // get pixel bounds
63 const Point aOriginOldPixel(mpBufferDevice->LogicToPixel(rOriginOld));
64 const Point aOriginNewPixel(mpBufferDevice->LogicToPixel(rOriginNew));
65 const Size aOutputSizePixel(mpBufferDevice->GetOutputSizePixel());
67 // remember and switch off MapMode
68 const bool bMapModeWasEnabled(mpBufferDevice->IsMapModeEnabled());
69 mpBufferDevice->EnableMapMode(false);
71 // scroll internally buffered stuff
72 const Point aDestinationOffsetPixel(aOriginNewPixel - aOriginOldPixel);
73 mpBufferDevice->DrawOutDev(
74 aDestinationOffsetPixel, aOutputSizePixel, // destination
75 Point(), aOutputSizePixel); // source
77 // restore MapMode
78 mpBufferDevice->EnableMapMode(bMapModeWasEnabled);
80 // scroll remembered region, too.
81 if(!maBufferRememberedRangePixel.isEmpty())
83 const basegfx::B2IPoint aIPointDestinationOffsetPixel(aDestinationOffsetPixel.X(), aDestinationOffsetPixel.Y());
84 const basegfx::B2IPoint aNewMinimum(maBufferRememberedRangePixel.getMinimum() + aIPointDestinationOffsetPixel);
85 const basegfx::B2IPoint aNewMaximum(maBufferRememberedRangePixel.getMaximum() + aIPointDestinationOffsetPixel);
86 maBufferRememberedRangePixel = basegfx::B2IRange(aNewMinimum, aNewMaximum);
91 // copy new MapMode
92 mpBufferDevice->SetMapMode(getOutputDevice().GetMapMode());
95 // #i29186#
96 mpBufferDevice->SetDrawMode(getOutputDevice().GetDrawMode());
97 mpBufferDevice->SetSettings(getOutputDevice().GetSettings());
98 mpBufferDevice->SetAntialiasing(getOutputDevice().GetAntialiasing());
101 void OverlayManagerBuffered::ImpRestoreBackground() const
103 const tools::Rectangle aRegionRectanglePixel(
104 maBufferRememberedRangePixel.getMinX(), maBufferRememberedRangePixel.getMinY(),
105 maBufferRememberedRangePixel.getMaxX(), maBufferRememberedRangePixel.getMaxY());
106 const vcl::Region aRegionPixel(aRegionRectanglePixel);
108 ImpRestoreBackground(aRegionPixel);
111 void OverlayManagerBuffered::ImpRestoreBackground(const vcl::Region& rRegionPixel) const
113 // MapModes off
114 const bool bMapModeWasEnabledDest(getOutputDevice().IsMapModeEnabled());
115 const bool bMapModeWasEnabledSource(mpBufferDevice->IsMapModeEnabled());
116 getOutputDevice().EnableMapMode(false);
117 const_cast<OverlayManagerBuffered*>(this)->mpBufferDevice->EnableMapMode(false);
119 // local region
120 RectangleVector aRectangles;
121 rRegionPixel.GetRegionRectangles(aRectangles);
123 for(const auto& rRect : aRectangles)
125 // restore the area
126 const Point aTopLeft(rRect.TopLeft());
127 const Size aSize(rRect.GetSize());
129 getOutputDevice().DrawOutDev(
130 aTopLeft, aSize, // destination
131 aTopLeft, aSize, // source
132 *mpBufferDevice);
135 // restore MapModes
136 getOutputDevice().EnableMapMode(bMapModeWasEnabledDest);
137 const_cast<OverlayManagerBuffered*>(this)->mpBufferDevice->EnableMapMode(bMapModeWasEnabledSource);
140 void OverlayManagerBuffered::ImpSaveBackground(const vcl::Region& rRegion, OutputDevice* pPreRenderDevice)
142 // prepare source
143 OutputDevice& rSource = pPreRenderDevice ? *pPreRenderDevice : getOutputDevice();
145 // Ensure buffer is valid
146 ImpPrepareBufferDevice();
148 // build region which needs to be copied
149 vcl::Region aRegion(rSource.LogicToPixel(rRegion));
151 // limit to PaintRegion if it's a window. This will be evtl. the expanded one,
152 // but always the exact redraw area
153 if(OUTDEV_WINDOW == rSource.GetOutDevType())
155 vcl::Window& rWindow = static_cast<vcl::Window&>(rSource);
156 vcl::Region aPaintRegionPixel = rWindow.LogicToPixel(rWindow.GetPaintRegion());
157 aRegion.Intersect(aPaintRegionPixel);
159 // #i72754# Make sure content is completely rendered, the window
160 // will be used as source of a DrawOutDev soon
161 rWindow.Flush();
164 // also limit to buffer size
165 const tools::Rectangle aBufferDeviceRectanglePixel(Point(), mpBufferDevice->GetOutputSizePixel());
166 aRegion.Intersect(aBufferDeviceRectanglePixel);
168 // MapModes off
169 const bool bMapModeWasEnabledDest(rSource.IsMapModeEnabled());
170 const bool bMapModeWasEnabledSource(mpBufferDevice->IsMapModeEnabled());
171 rSource.EnableMapMode(false);
172 mpBufferDevice->EnableMapMode(false);
174 // prepare to iterate over the rectangles from the region in pixels
175 RectangleVector aRectangles;
176 aRegion.GetRegionRectangles(aRectangles);
178 for(const auto& rRect : aRectangles)
180 // for each rectangle, save the area
181 const Point aTopLeft(rRect.TopLeft());
182 const Size aSize(rRect.GetSize());
184 mpBufferDevice->DrawOutDev(
185 aTopLeft, aSize, // destination
186 aTopLeft, aSize, // source
187 rSource);
190 // restore MapModes
191 rSource.EnableMapMode(bMapModeWasEnabledDest);
192 mpBufferDevice->EnableMapMode(bMapModeWasEnabledSource);
195 IMPL_LINK_NOARG(OverlayManagerBuffered, ImpBufferTimerHandler, Timer*, void)
197 //Resolves: fdo#46728 ensure this exists until end of scope
198 rtl::Reference<OverlayManager> xRef(this);
200 // stop timer
201 maBufferIdle.Stop();
203 if(!maBufferRememberedRangePixel.isEmpty())
205 // logic size for impDrawMember call
206 basegfx::B2DRange aBufferRememberedRangeLogic(
207 maBufferRememberedRangePixel.getMinX(), maBufferRememberedRangePixel.getMinY(),
208 maBufferRememberedRangePixel.getMaxX(), maBufferRememberedRangePixel.getMaxY());
209 aBufferRememberedRangeLogic.transform(getOutputDevice().GetInverseViewTransformation());
211 // prepare cursor handling
212 const bool bTargetIsWindow(OUTDEV_WINDOW == mrOutputDevice.GetOutDevType());
213 bool bCursorWasEnabled(false);
215 // #i80730# switch off VCL cursor during overlay refresh
216 if(bTargetIsWindow)
218 vcl::Window& rWindow = static_cast< vcl::Window& >(mrOutputDevice);
219 vcl::Cursor* pCursor = rWindow.GetCursor();
221 if(pCursor && pCursor->IsVisible())
223 pCursor->Hide();
224 bCursorWasEnabled = true;
228 // refresh with prerendering
230 // #i73602# ensure valid and sized mpOutputBufferDevice
231 const Size aDestinationSizePixel(mpBufferDevice->GetOutputSizePixel());
232 const Size aOutputBufferSizePixel(mpOutputBufferDevice->GetOutputSizePixel());
234 if(aDestinationSizePixel != aOutputBufferSizePixel)
236 mpOutputBufferDevice->SetOutputSizePixel(aDestinationSizePixel);
239 mpOutputBufferDevice->SetMapMode(getOutputDevice().GetMapMode());
240 mpOutputBufferDevice->EnableMapMode(false);
241 mpOutputBufferDevice->SetDrawMode(mpBufferDevice->GetDrawMode());
242 mpOutputBufferDevice->SetSettings(mpBufferDevice->GetSettings());
243 mpOutputBufferDevice->SetAntialiasing(mpBufferDevice->GetAntialiasing());
245 // calculate sizes
246 tools::Rectangle aRegionRectanglePixel(
247 maBufferRememberedRangePixel.getMinX(), maBufferRememberedRangePixel.getMinY(),
248 maBufferRememberedRangePixel.getMaxX(), maBufferRememberedRangePixel.getMaxY());
250 // truncate aRegionRectanglePixel to destination pixel size, more does
251 // not need to be prepared since destination is a buffer for a window. So,
252 // maximum size indirectly shall be limited to getOutputDevice().GetOutputSizePixel()
253 if(aRegionRectanglePixel.Left() < 0)
255 aRegionRectanglePixel.SetLeft( 0 );
258 if(aRegionRectanglePixel.Top() < 0)
260 aRegionRectanglePixel.SetTop( 0 );
263 if(aRegionRectanglePixel.Right() > aDestinationSizePixel.getWidth())
265 aRegionRectanglePixel.SetRight( aDestinationSizePixel.getWidth() );
268 if(aRegionRectanglePixel.Bottom() > aDestinationSizePixel.getHeight())
270 aRegionRectanglePixel.SetBottom( aDestinationSizePixel.getHeight() );
273 // get sizes
274 const Point aTopLeft(aRegionRectanglePixel.TopLeft());
275 const Size aSize(aRegionRectanglePixel.GetSize());
278 const bool bMapModeWasEnabledDest(mpBufferDevice->IsMapModeEnabled());
279 mpBufferDevice->EnableMapMode(false);
281 mpOutputBufferDevice->DrawOutDev(
282 aTopLeft, aSize, // destination
283 aTopLeft, aSize, // source
284 *mpBufferDevice);
286 // restore MapModes
287 mpBufferDevice->EnableMapMode(bMapModeWasEnabledDest);
290 // paint overlay content for remembered region, use
291 // method from base class directly
292 mpOutputBufferDevice->EnableMapMode();
293 OverlayManager::ImpDrawMembers(aBufferRememberedRangeLogic, *mpOutputBufferDevice);
294 mpOutputBufferDevice->EnableMapMode(false);
296 // copy to output
298 const bool bMapModeWasEnabledDest(getOutputDevice().IsMapModeEnabled());
299 getOutputDevice().EnableMapMode(false);
301 getOutputDevice().DrawOutDev(
302 aTopLeft, aSize, // destination
303 aTopLeft, aSize, // source
304 *mpOutputBufferDevice);
306 // debug
307 /*getOutputDevice().SetLineCOL_RED);
308 getOutputDevice().SetFillColor();
309 getOutputDevice().DrawRect(Rectangle(aTopLeft, aSize));*/
311 // restore MapModes
312 getOutputDevice().EnableMapMode(bMapModeWasEnabledDest);
316 // VCL hack for transparent child windows
317 // Problem is e.g. a radiobutton form control in life mode. The used window
318 // is a transparence vcl childwindow. This flag only allows the parent window to
319 // paint into the child windows area, but there is no mechanism which takes
320 // care for a repaint of the child window. A transparent child window is NOT
321 // a window which always keeps it's content consistent over the parent, but it's
322 // more like just a paint flag for the parent.
323 // To get the update, the windows in question are updated manually here.
324 if(bTargetIsWindow)
326 vcl::Window& rWindow = static_cast< vcl::Window& >(mrOutputDevice);
328 const tools::Rectangle aRegionRectanglePixel(
329 maBufferRememberedRangePixel.getMinX(),
330 maBufferRememberedRangePixel.getMinY(),
331 maBufferRememberedRangePixel.getMaxX(),
332 maBufferRememberedRangePixel.getMaxY());
333 PaintTransparentChildren(rWindow, aRegionRectanglePixel);
336 // #i80730# restore visibility of VCL cursor
337 if(bCursorWasEnabled)
339 vcl::Window& rWindow = static_cast< vcl::Window& >(mrOutputDevice);
340 vcl::Cursor* pCursor = rWindow.GetCursor();
342 if(pCursor)
344 // check if cursor still exists. It may have been deleted from someone
345 pCursor->Show();
349 // forget remembered Region
350 maBufferRememberedRangePixel.reset();
354 OverlayManagerBuffered::OverlayManagerBuffered(
355 OutputDevice& rOutputDevice)
356 : OverlayManager(rOutputDevice),
357 mpBufferDevice(VclPtr<VirtualDevice>::Create()),
358 mpOutputBufferDevice(VclPtr<VirtualDevice>::Create()),
359 maBufferIdle("sdr overlay OverlayManagerBuffered Idle")
361 // Init timer
362 maBufferIdle.SetPriority( TaskPriority::POST_PAINT );
363 maBufferIdle.SetInvokeHandler(LINK(this, OverlayManagerBuffered, ImpBufferTimerHandler));
364 maBufferIdle.SetDebugName( "sdr::overlay::OverlayManagerBuffered maBufferIdle" );
367 rtl::Reference<OverlayManager> OverlayManagerBuffered::create(
368 OutputDevice& rOutputDevice)
370 return rtl::Reference<OverlayManager>(new OverlayManagerBuffered(rOutputDevice));
373 OverlayManagerBuffered::~OverlayManagerBuffered()
375 // Clear timer
376 maBufferIdle.Stop();
378 if(!maBufferRememberedRangePixel.isEmpty())
380 // Restore all rectangles for remembered region from buffer
381 ImpRestoreBackground();
385 void OverlayManagerBuffered::completeRedraw(const vcl::Region& rRegion, OutputDevice* pPreRenderDevice) const
387 if(!rRegion.IsEmpty())
389 // save new background
390 const_cast<OverlayManagerBuffered*>(this)->ImpSaveBackground(rRegion, pPreRenderDevice);
393 // call parent
394 OverlayManager::completeRedraw(rRegion, pPreRenderDevice);
397 void OverlayManagerBuffered::flush()
399 // call timer handler direct
400 ImpBufferTimerHandler(nullptr);
403 void OverlayManagerBuffered::invalidateRange(const basegfx::B2DRange& rRange)
405 if(!rRange.isEmpty())
407 // buffered output, do not invalidate but use the timer
408 // to trigger a timer event for refresh
409 maBufferIdle.Start();
411 // add the discrete range to the remembered region
412 // #i75163# use double precision and floor/ceil rounding to get overlapped pixel region, even
413 // when the given logic region has a width/height of 0.0. This does NOT work with LogicToPixel
414 // since it just transforms the top left and bottom right points equally without taking
415 // discrete pixel coverage into account. An empty B2DRange and thus empty logic Rectangle translated
416 // to an also empty discrete pixel rectangle - what is wrong.
417 basegfx::B2DRange aDiscreteRange(rRange);
418 aDiscreteRange.transform(getOutputDevice().GetViewTransformation());
420 if(maDrawinglayerOpt.IsAntiAliasing())
422 // assume AA needs one pixel more and invalidate one pixel more
423 const double fDiscreteOne(getDiscreteOne());
424 const basegfx::B2IPoint aTopLeft(
425 static_cast<sal_Int32>(floor(aDiscreteRange.getMinX() - fDiscreteOne)),
426 static_cast<sal_Int32>(floor(aDiscreteRange.getMinY() - fDiscreteOne)));
427 const basegfx::B2IPoint aBottomRight(
428 static_cast<sal_Int32>(ceil(aDiscreteRange.getMaxX() + fDiscreteOne)),
429 static_cast<sal_Int32>(ceil(aDiscreteRange.getMaxY() + fDiscreteOne)));
431 maBufferRememberedRangePixel.expand(aTopLeft);
432 maBufferRememberedRangePixel.expand(aBottomRight);
434 else
436 const basegfx::B2IPoint aTopLeft(static_cast<sal_Int32>(floor(aDiscreteRange.getMinX())), static_cast<sal_Int32>(floor(aDiscreteRange.getMinY())));
437 const basegfx::B2IPoint aBottomRight(static_cast<sal_Int32>(ceil(aDiscreteRange.getMaxX())), static_cast<sal_Int32>(ceil(aDiscreteRange.getMaxY())));
439 maBufferRememberedRangePixel.expand(aTopLeft);
440 maBufferRememberedRangePixel.expand(aBottomRight);
444 } // end of namespace overlay
445 } // end of namespace sdr
447 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */