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
22#include <vcl/outdev.hxx>
24#include <vcl/window.hxx>
25#include <tools/fract.hxx>
26#include <vcl/cursor.hxx>
28
29
30namespace 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
56 if(bScrolled)
57 {
58 // get pixel bounds (tdf#149322 do subtraction in logic units before converting result back to pixel)
59 const Point aLogicOriginDiff(rOriginNew - rOriginOld);
60 const Size aPixelOriginDiff(mpBufferDevice->LogicToPixel(Size(aLogicOriginDiff.X(), aLogicOriginDiff.Y())));
61 const Point aDestinationOffsetPixel(aPixelOriginDiff.Width(), aPixelOriginDiff.Height());
62 const Size aOutputSizePixel(mpBufferDevice->GetOutputSizePixel());
63
64 // remember and switch off MapMode
65 const bool bMapModeWasEnabled(mpBufferDevice->IsMapModeEnabled());
66 mpBufferDevice->EnableMapMode(false);
67
68 // scroll internally buffered stuff
69 mpBufferDevice->DrawOutDev(
70 aDestinationOffsetPixel, aOutputSizePixel, // destination
71 Point(), aOutputSizePixel); // source
72
73 // restore MapMode
74 mpBufferDevice->EnableMapMode(bMapModeWasEnabled);
75
76 // scroll remembered region, too.
78 {
79 const basegfx::B2IPoint aIPointDestinationOffsetPixel(aDestinationOffsetPixel.X(), aDestinationOffsetPixel.Y());
80 const basegfx::B2IPoint aNewMinimum(maBufferRememberedRangePixel.getMinimum() + aIPointDestinationOffsetPixel);
81 const basegfx::B2IPoint aNewMaximum(maBufferRememberedRangePixel.getMaximum() + aIPointDestinationOffsetPixel);
82 maBufferRememberedRangePixel = basegfx::B2IRange(aNewMinimum, aNewMaximum);
83 }
84 }
85 }
86
87 // copy new MapMode
88 mpBufferDevice->SetMapMode(getOutputDevice().GetMapMode());
89 }
90
91 // #i29186#
92 mpBufferDevice->SetDrawMode(getOutputDevice().GetDrawMode());
93 mpBufferDevice->SetSettings(getOutputDevice().GetSettings());
94 mpBufferDevice->SetAntialiasing(getOutputDevice().GetAntialiasing());
95 }
96
98 {
99 const tools::Rectangle aRegionRectanglePixel(
102 const vcl::Region aRegionPixel(aRegionRectanglePixel);
103
104 ImpRestoreBackground(aRegionPixel);
105 }
106
108 {
109 // MapModes off
110 const bool bMapModeWasEnabledDest(getOutputDevice().IsMapModeEnabled());
111 const bool bMapModeWasEnabledSource(mpBufferDevice->IsMapModeEnabled());
113 const_cast<OverlayManagerBuffered*>(this)->mpBufferDevice->EnableMapMode(false);
114
115 // local region
116 RectangleVector aRectangles;
117 rRegionPixel.GetRegionRectangles(aRectangles);
118
119 for(const auto& rRect : aRectangles)
120 {
121 // restore the area
122 const Point aTopLeft(rRect.TopLeft());
123 const Size aSize(rRect.GetSize());
124
126 aTopLeft, aSize, // destination
127 aTopLeft, aSize, // source
129 }
130
131 // restore MapModes
132 getOutputDevice().EnableMapMode(bMapModeWasEnabledDest);
133 const_cast<OverlayManagerBuffered*>(this)->mpBufferDevice->EnableMapMode(bMapModeWasEnabledSource);
134 }
135
137 {
138 // prepare source
139 OutputDevice& rSource = pPreRenderDevice ? *pPreRenderDevice : getOutputDevice();
140
141 // Ensure buffer is valid
143
144 // build region which needs to be copied
145 vcl::Region aRegion(rSource.LogicToPixel(rRegion));
146
147 // limit to PaintRegion if it's a window. This will be evtl. the expanded one,
148 // but always the exact redraw area
149 if(OUTDEV_WINDOW == rSource.GetOutDevType())
150 {
151 vcl::Window& rWindow = *rSource.GetOwnerWindow();
152 vcl::Region aPaintRegionPixel = rWindow.LogicToPixel(rWindow.GetPaintRegion());
153 aRegion.Intersect(aPaintRegionPixel);
154
155 // #i72754# Make sure content is completely rendered, the window
156 // will be used as source of a DrawOutDev soon
157 rWindow.GetOutDev()->Flush();
158 }
159
160 // also limit to buffer size
161 const tools::Rectangle aBufferDeviceRectanglePixel(Point(), mpBufferDevice->GetOutputSizePixel());
162 aRegion.Intersect(aBufferDeviceRectanglePixel);
163
164 // MapModes off
165 const bool bMapModeWasEnabledDest(rSource.IsMapModeEnabled());
166 const bool bMapModeWasEnabledSource(mpBufferDevice->IsMapModeEnabled());
167 rSource.EnableMapMode(false);
168 mpBufferDevice->EnableMapMode(false);
169
170 // prepare to iterate over the rectangles from the region in pixels
171 RectangleVector aRectangles;
172 aRegion.GetRegionRectangles(aRectangles);
173
174 for(const auto& rRect : aRectangles)
175 {
176 // for each rectangle, save the area
177 const Point aTopLeft(rRect.TopLeft());
178 const Size aSize(rRect.GetSize());
179
180 mpBufferDevice->DrawOutDev(
181 aTopLeft, aSize, // destination
182 aTopLeft, aSize, // source
183 rSource);
184 }
185
186 // restore MapModes
187 rSource.EnableMapMode(bMapModeWasEnabledDest);
188 mpBufferDevice->EnableMapMode(bMapModeWasEnabledSource);
189 }
190
191 IMPL_LINK_NOARG(OverlayManagerBuffered, ImpBufferTimerHandler, Timer*, void)
192 {
193 //Resolves: fdo#46728 ensure this exists until end of scope
194 rtl::Reference<OverlayManager> xKeepAlive(this);
195
196 // stop timer
197 maBufferIdle.Stop();
198
199 if(maBufferRememberedRangePixel.isEmpty())
200 return;
201
202 // logic size for impDrawMember call
203 basegfx::B2DRange aBufferRememberedRangeLogic(
204 maBufferRememberedRangePixel.getMinX(), maBufferRememberedRangePixel.getMinY(),
205 maBufferRememberedRangePixel.getMaxX(), maBufferRememberedRangePixel.getMaxY());
206 aBufferRememberedRangeLogic.transform(getOutputDevice().GetInverseViewTransformation());
207
208 // prepare cursor handling
209 const bool bTargetIsWindow(OUTDEV_WINDOW == mrOutputDevice.GetOutDevType());
210 bool bCursorWasEnabled(false);
211
212 // #i80730# switch off VCL cursor during overlay refresh
213 if(bTargetIsWindow)
214 {
215 vcl::Window& rWindow = *mrOutputDevice.GetOwnerWindow();
216 vcl::Cursor* pCursor = rWindow.GetCursor();
217
218 if(pCursor && pCursor->IsVisible())
219 {
220 pCursor->Hide();
221 bCursorWasEnabled = true;
222 }
223 }
224
225 // refresh with prerendering
226 {
227 // #i73602# ensure valid and sized mpOutputBufferDevice
228 const Size aDestinationSizePixel(mpBufferDevice->GetOutputSizePixel());
229 const Size aOutputBufferSizePixel(mpOutputBufferDevice->GetOutputSizePixel());
230
231 if(aDestinationSizePixel != aOutputBufferSizePixel)
232 {
233 mpOutputBufferDevice->SetOutputSizePixel(aDestinationSizePixel);
234 }
235
236 mpOutputBufferDevice->SetMapMode(getOutputDevice().GetMapMode());
237 mpOutputBufferDevice->EnableMapMode(false);
238 mpOutputBufferDevice->SetDrawMode(mpBufferDevice->GetDrawMode());
239 mpOutputBufferDevice->SetSettings(mpBufferDevice->GetSettings());
240 mpOutputBufferDevice->SetAntialiasing(mpBufferDevice->GetAntialiasing());
241
242 // calculate sizes
243 tools::Rectangle aRegionRectanglePixel(
244 maBufferRememberedRangePixel.getMinX(), maBufferRememberedRangePixel.getMinY(),
245 maBufferRememberedRangePixel.getMaxX(), maBufferRememberedRangePixel.getMaxY());
246
247 // truncate aRegionRectanglePixel to destination pixel size, more does
248 // not need to be prepared since destination is a buffer for a window. So,
249 // maximum size indirectly shall be limited to getOutputDevice().GetOutputSizePixel()
250 if(aRegionRectanglePixel.Left() < 0)
251 {
252 aRegionRectanglePixel.SetLeft( 0 );
253 }
254
255 if(aRegionRectanglePixel.Top() < 0)
256 {
257 aRegionRectanglePixel.SetTop( 0 );
258 }
259
260 if(aRegionRectanglePixel.Right() > aDestinationSizePixel.getWidth())
261 {
262 aRegionRectanglePixel.SetRight( aDestinationSizePixel.getWidth() );
263 }
264
265 if(aRegionRectanglePixel.Bottom() > aDestinationSizePixel.getHeight())
266 {
267 aRegionRectanglePixel.SetBottom( aDestinationSizePixel.getHeight() );
268 }
269
270 // get sizes
271 const Point aTopLeft(aRegionRectanglePixel.TopLeft());
272 const Size aSize(aRegionRectanglePixel.GetSize());
273
274 {
275 const bool bMapModeWasEnabledDest(mpBufferDevice->IsMapModeEnabled());
276 mpBufferDevice->EnableMapMode(false);
277
278 mpOutputBufferDevice->DrawOutDev(
279 aTopLeft, aSize, // destination
280 aTopLeft, aSize, // source
281 *mpBufferDevice);
282
283 // restore MapModes
284 mpBufferDevice->EnableMapMode(bMapModeWasEnabledDest);
285 }
286
287 // paint overlay content for remembered region, use
288 // method from base class directly
289 mpOutputBufferDevice->EnableMapMode();
290 OverlayManager::ImpDrawMembers(aBufferRememberedRangeLogic, *mpOutputBufferDevice);
291 mpOutputBufferDevice->EnableMapMode(false);
292
293 // copy to output
294 {
295 const bool bMapModeWasEnabledDest(getOutputDevice().IsMapModeEnabled());
296 getOutputDevice().EnableMapMode(false);
297
298 getOutputDevice().DrawOutDev(
299 aTopLeft, aSize, // destination
300 aTopLeft, aSize, // source
301 *mpOutputBufferDevice);
302
303 // debug
304 /*getOutputDevice().SetLineCOL_RED);
305 getOutputDevice().SetFillColor();
306 getOutputDevice().DrawRect(Rectangle(aTopLeft, aSize));*/
307
308 // restore MapModes
309 getOutputDevice().EnableMapMode(bMapModeWasEnabledDest);
310 }
311 }
312
313 // VCL hack for transparent child windows
314 // Problem is e.g. a radiobutton form control in life mode. The used window
315 // is a transparence vcl childwindow. This flag only allows the parent window to
316 // paint into the child windows area, but there is no mechanism which takes
317 // care for a repaint of the child window. A transparent child window is NOT
318 // a window which always keeps it's content consistent over the parent, but it's
319 // more like just a paint flag for the parent.
320 // To get the update, the windows in question are updated manually here.
321 if(bTargetIsWindow)
322 {
323 vcl::Window& rWindow = *mrOutputDevice.GetOwnerWindow();
324
325 const tools::Rectangle aRegionRectanglePixel(
326 maBufferRememberedRangePixel.getMinX(),
327 maBufferRememberedRangePixel.getMinY(),
328 maBufferRememberedRangePixel.getMaxX(),
329 maBufferRememberedRangePixel.getMaxY());
330 PaintTransparentChildren(rWindow, aRegionRectanglePixel);
331 }
332
333 // #i80730# restore visibility of VCL cursor
334 if(bCursorWasEnabled)
335 {
336 vcl::Window& rWindow = *mrOutputDevice.GetOwnerWindow();
337 vcl::Cursor* pCursor = rWindow.GetCursor();
338
339 if(pCursor)
340 {
341 // check if cursor still exists. It may have been deleted from someone
342 pCursor->Show();
343 }
344 }
345
346 // forget remembered Region
347 maBufferRememberedRangePixel.reset();
348 }
349
351 OutputDevice& rOutputDevice)
352 : OverlayManager(rOutputDevice),
353 mpBufferDevice(VclPtr<VirtualDevice>::Create()),
354 mpOutputBufferDevice(VclPtr<VirtualDevice>::Create()),
355 maBufferIdle( "sdr::overlay::OverlayManagerBuffered maBufferIdle" )
356 {
357 // Init timer
358 maBufferIdle.SetPriority( TaskPriority::POST_PAINT );
359 maBufferIdle.SetInvokeHandler(LINK(this, OverlayManagerBuffered, ImpBufferTimerHandler));
360 }
361
363 OutputDevice& rOutputDevice)
364 {
366 }
367
369 {
370 // Clear timer
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
416 if(getCurrentViewInformation2D().getUseAntiAliasing())
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
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
437 }
438 }
439} // end of namespace
440
441/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
virtual void Start(bool bStartTimer=true) override
const Point & GetOrigin() const
void EnableMapMode(bool bEnable=true)
SAL_WARN_UNUSED_RESULT Point LogicToPixel(const Point &rLogicPt) const
SAL_DLLPRIVATE void DrawOutDev(const Point &, const Size &, const Point &, const Size &, const Printer &)=delete
const MapMode & GetMapMode() const
virtual void Flush()
OutDevType GetOutDevType() const
virtual vcl::Window * GetOwnerWindow() const
bool IsMapModeEnabled() const
constexpr tools::Long Y() const
constexpr tools::Long X() const
constexpr tools::Long getHeight() const
constexpr tools::Long Height() const
constexpr tools::Long getWidth() const
constexpr tools::Long Width() const
void SetPriority(TaskPriority ePriority)
void Stop()
void SetInvokeHandler(const Link< Timer *, void > &rLink)
BASEGFX_DLLPUBLIC void transform(const B2DHomMatrix &rMatrix)
B2IPoint getMinimum() const
B2IPoint getMaximum() const
TYPE getMaxX() const
TYPE getMinX() const
TYPE getMinY() const
void expand(const Tuple2D< TYPE > &rTuple)
TYPE getMaxY() const
bool isEmpty() const
virtual void completeRedraw(const vcl::Region &rRegion, OutputDevice *pPreRenderDevice=nullptr) const override
virtual void invalidateRange(const basegfx::B2DRange &rRange) override
OverlayManagerBuffered(OutputDevice &rOutputDevice)
ScopedVclPtr< VirtualDevice > mpBufferDevice
void ImpSaveBackground(const vcl::Region &rRegion, OutputDevice *pPreRenderDevice)
static rtl::Reference< OverlayManager > create(OutputDevice &rOutputDevice)
drawinglayer::geometry::ViewInformation2D const & getCurrentViewInformation2D() const
void ImpDrawMembers(const basegfx::B2DRange &rRange, OutputDevice &rDestinationDevice) const
OutputDevice & getOutputDevice() const
virtual void completeRedraw(const vcl::Region &rRegion, OutputDevice *pPreRenderDevice=nullptr) const
constexpr void SetLeft(tools::Long v)
constexpr void SetTop(tools::Long v)
constexpr tools::Long Top() const
constexpr Point TopLeft() const
constexpr void SetRight(tools::Long v)
constexpr Size GetSize() const
constexpr tools::Long Right() const
constexpr void SetBottom(tools::Long v)
constexpr tools::Long Left() const
constexpr tools::Long Bottom() const
bool IsVisible() const
void Intersect(const tools::Rectangle &rRegion)
bool IsEmpty() const
void GetRegionRectangles(RectangleVector &rTarget) const
Point LogicToPixel(const Point &rLogicPt) const
vcl::Cursor * GetCursor() const
::OutputDevice const * GetOutDev() const
vcl::Region GetPaintRegion() const
void Create(SwFormatVertOrient &rItem, SvStream &rStrm, sal_uInt16 nVersionAbusedAsSize)
IMPL_LINK_NOARG(OverlayManagerBuffered, ImpBufferTimerHandler, Timer *, void)
OUTDEV_WINDOW
std::vector< tools::Rectangle > RectangleVector
void PaintTransparentChildren(vcl::Window const &rWindow, tools::Rectangle const &rPixelRect)
paint the transparent children of rWin that overlap rPixelRect (for example, transparent form control...