1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 <svx/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 <vcl/bitmap.hxx>
27 #include <tools/stream.hxx>
28 #include <basegfx/matrix/b2dhommatrix.hxx>
29 #include <vcl/cursor.hxx>
31 //////////////////////////////////////////////////////////////////////////////
37 void OverlayManagerBuffered::ImpPrepareBufferDevice()
39 // compare size of maBufferDevice with size of visible area
40 if(maBufferDevice
.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 maBufferDevice
.SetOutputSizePixel(getOutputDevice().GetOutputSizePixel(), false);
47 // compare the MapModes for zoom/scroll changes
48 if(maBufferDevice
.GetMapMode() != getOutputDevice().GetMapMode())
51 maBufferDevice
.GetMapMode().GetScaleX() != getOutputDevice().GetMapMode().GetScaleX()
52 || maBufferDevice
.GetMapMode().GetScaleY() != getOutputDevice().GetMapMode().GetScaleY());
56 const Point
& rOriginOld
= maBufferDevice
.GetMapMode().GetOrigin();
57 const Point
& rOriginNew
= getOutputDevice().GetMapMode().GetOrigin();
58 const bool bScrolled(rOriginOld
!= rOriginNew
);
63 const Point
aOriginOldPixel(maBufferDevice
.LogicToPixel(rOriginOld
));
64 const Point
aOriginNewPixel(maBufferDevice
.LogicToPixel(rOriginNew
));
65 const Size
aOutputSizePixel(maBufferDevice
.GetOutputSizePixel());
67 // remember and switch off MapMode
68 const bool bMapModeWasEnabled(maBufferDevice
.IsMapModeEnabled());
69 maBufferDevice
.EnableMapMode(false);
71 // scroll internally buffered stuff
72 const Point
aDestinationOffsetPixel(aOriginNewPixel
- aOriginOldPixel
);
73 maBufferDevice
.DrawOutDev(
74 aDestinationOffsetPixel
, aOutputSizePixel
, // destination
75 Point(), aOutputSizePixel
); // source
78 maBufferDevice
.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
);
92 maBufferDevice
.SetMapMode(getOutputDevice().GetMapMode());
96 maBufferDevice
.SetDrawMode(getOutputDevice().GetDrawMode());
97 maBufferDevice
.SetSettings(getOutputDevice().GetSettings());
98 maBufferDevice
.SetAntialiasing(getOutputDevice().GetAntialiasing());
101 void OverlayManagerBuffered::ImpRestoreBackground() const
103 const Rectangle
aRegionRectanglePixel(
104 maBufferRememberedRangePixel
.getMinX(), maBufferRememberedRangePixel
.getMinY(),
105 maBufferRememberedRangePixel
.getMaxX(), maBufferRememberedRangePixel
.getMaxY());
106 const Region
aRegionPixel(aRegionRectanglePixel
);
108 ImpRestoreBackground(aRegionPixel
);
111 void OverlayManagerBuffered::ImpRestoreBackground(const Region
& rRegionPixel
) const
114 Region
aRegionPixel(rRegionPixel
);
115 RegionHandle
aRegionHandle(aRegionPixel
.BeginEnumRects());
116 Rectangle aRegionRectanglePixel
;
119 const bool bMapModeWasEnabledDest(getOutputDevice().IsMapModeEnabled());
120 const bool bMapModeWasEnabledSource(maBufferDevice
.IsMapModeEnabled());
121 getOutputDevice().EnableMapMode(false);
122 ((OverlayManagerBuffered
*)this)->maBufferDevice
.EnableMapMode(false);
124 while(aRegionPixel
.GetEnumRects(aRegionHandle
, aRegionRectanglePixel
))
127 // #i72754# possible graphical region test only with non-pro
128 static bool bDoPaintForVisualControl(false);
129 if(bDoPaintForVisualControl
)
131 getOutputDevice().SetLineColor(COL_LIGHTGREEN
);
132 getOutputDevice().SetFillColor();
133 getOutputDevice().DrawRect(aRegionRectanglePixel
);
138 const Point
aTopLeft(aRegionRectanglePixel
.TopLeft());
139 const Size
aSize(aRegionRectanglePixel
.GetSize());
141 getOutputDevice().DrawOutDev(
142 aTopLeft
, aSize
, // destination
143 aTopLeft
, aSize
, // source
147 aRegionPixel
.EndEnumRects(aRegionHandle
);
150 getOutputDevice().EnableMapMode(bMapModeWasEnabledDest
);
151 ((OverlayManagerBuffered
*)this)->maBufferDevice
.EnableMapMode(bMapModeWasEnabledSource
);
154 void OverlayManagerBuffered::ImpSaveBackground(const Region
& rRegion
, OutputDevice
* pPreRenderDevice
)
157 OutputDevice
& rSource
= (pPreRenderDevice
) ? *pPreRenderDevice
: getOutputDevice();
159 // Ensure buffer is valid
160 ImpPrepareBufferDevice();
162 // build region which needs to be copied
163 Region
aRegion(rSource
.LogicToPixel(rRegion
));
165 // limit to PaintRegion if it's a window. This will be evtl. the expanded one,
166 // but always the exact redraw area
167 if(OUTDEV_WINDOW
== rSource
.GetOutDevType())
169 Window
& rWindow
= (Window
&)rSource
;
170 Region aPaintRegionPixel
= rWindow
.LogicToPixel(rWindow
.GetPaintRegion());
171 aRegion
.Intersect(aPaintRegionPixel
);
173 // #i72754# Make sure content is completetly rendered, the window
174 // will be used as source of a DrawOutDev soon
178 // also limit to buffer size
179 const Rectangle aBufferDeviceRectanglePixel
= Rectangle(Point(), maBufferDevice
.GetOutputSizePixel());
180 aRegion
.Intersect(aBufferDeviceRectanglePixel
);
182 // prepare to iterate over the rectangles from the region in pixels
183 RegionHandle
aRegionHandle(aRegion
.BeginEnumRects());
184 Rectangle aRegionRectanglePixel
;
187 const bool bMapModeWasEnabledDest(rSource
.IsMapModeEnabled());
188 const bool bMapModeWasEnabledSource(maBufferDevice
.IsMapModeEnabled());
189 rSource
.EnableMapMode(false);
190 maBufferDevice
.EnableMapMode(false);
192 while(aRegion
.GetEnumRects(aRegionHandle
, aRegionRectanglePixel
))
194 // for each rectangle, save the area
195 Point
aTopLeft(aRegionRectanglePixel
.TopLeft());
196 Size
aSize(aRegionRectanglePixel
.GetSize());
198 maBufferDevice
.DrawOutDev(
199 aTopLeft
, aSize
, // destination
200 aTopLeft
, aSize
, // source
204 aRegion
.EndEnumRects(aRegionHandle
);
207 rSource
.EnableMapMode(bMapModeWasEnabledDest
);
208 maBufferDevice
.EnableMapMode(bMapModeWasEnabledSource
);
211 IMPL_LINK(OverlayManagerBuffered
, ImpBufferTimerHandler
, AutoTimer
*, /*pTimer*/)
213 //Resolves: fdo#46728 ensure this exists until end of scope
214 rtl::Reference
<OverlayManager
> xRef(this);
217 maBufferTimer
.Stop();
219 if(!maBufferRememberedRangePixel
.isEmpty())
221 // logic size for impDrawMember call
222 basegfx::B2DRange
aBufferRememberedRangeLogic(
223 maBufferRememberedRangePixel
.getMinX(), maBufferRememberedRangePixel
.getMinY(),
224 maBufferRememberedRangePixel
.getMaxX(), maBufferRememberedRangePixel
.getMaxY());
225 aBufferRememberedRangeLogic
.transform(getOutputDevice().GetInverseViewTransformation());
227 // prepare cursor handling
228 const bool bTargetIsWindow(OUTDEV_WINDOW
== rmOutputDevice
.GetOutDevType());
229 bool bCursorWasEnabled(false);
231 // #i80730# switch off VCL cursor during overlay refresh
234 Window
& rWindow
= static_cast< Window
& >(rmOutputDevice
);
235 Cursor
* pCursor
= rWindow
.GetCursor();
237 if(pCursor
&& pCursor
->IsVisible())
240 bCursorWasEnabled
= true;
244 if(DoRefreshWithPreRendering())
246 // #i73602# ensure valid and sized maOutputBufferDevice
247 const Size
aDestinationSizePixel(maBufferDevice
.GetOutputSizePixel());
248 const Size
aOutputBufferSizePixel(maOutputBufferDevice
.GetOutputSizePixel());
250 if(aDestinationSizePixel
!= aOutputBufferSizePixel
)
252 maOutputBufferDevice
.SetOutputSizePixel(aDestinationSizePixel
);
255 maOutputBufferDevice
.SetMapMode(getOutputDevice().GetMapMode());
256 maOutputBufferDevice
.EnableMapMode(false);
257 maOutputBufferDevice
.SetDrawMode(maBufferDevice
.GetDrawMode());
258 maOutputBufferDevice
.SetSettings(maBufferDevice
.GetSettings());
259 maOutputBufferDevice
.SetAntialiasing(maBufferDevice
.GetAntialiasing());
262 Rectangle
aRegionRectanglePixel(
263 maBufferRememberedRangePixel
.getMinX(), maBufferRememberedRangePixel
.getMinY(),
264 maBufferRememberedRangePixel
.getMaxX(), maBufferRememberedRangePixel
.getMaxY());
266 // truncate aRegionRectanglePixel to destination pixel size, more does
267 // not need to be prepared since destination is a buffer for a window. So,
268 // maximum size indirectly shall be limited to getOutputDevice().GetOutputSizePixel()
269 if(aRegionRectanglePixel
.Left() < 0L)
271 aRegionRectanglePixel
.Left() = 0L;
274 if(aRegionRectanglePixel
.Top() < 0L)
276 aRegionRectanglePixel
.Top() = 0L;
279 if(aRegionRectanglePixel
.Right() > aDestinationSizePixel
.getWidth())
281 aRegionRectanglePixel
.Right() = aDestinationSizePixel
.getWidth();
284 if(aRegionRectanglePixel
.Bottom() > aDestinationSizePixel
.getHeight())
286 aRegionRectanglePixel
.Bottom() = aDestinationSizePixel
.getHeight();
290 const Point
aTopLeft(aRegionRectanglePixel
.TopLeft());
291 const Size
aSize(aRegionRectanglePixel
.GetSize());
294 const bool bMapModeWasEnabledDest(maBufferDevice
.IsMapModeEnabled());
295 maBufferDevice
.EnableMapMode(false);
297 maOutputBufferDevice
.DrawOutDev(
298 aTopLeft
, aSize
, // destination
299 aTopLeft
, aSize
, // source
303 maBufferDevice
.EnableMapMode(bMapModeWasEnabledDest
);
306 // paint overlay content for remembered region, use
307 // method from base class directly
308 maOutputBufferDevice
.EnableMapMode(true);
309 OverlayManager::ImpDrawMembers(aBufferRememberedRangeLogic
, maOutputBufferDevice
);
310 maOutputBufferDevice
.EnableMapMode(false);
314 const bool bMapModeWasEnabledDest(getOutputDevice().IsMapModeEnabled());
315 getOutputDevice().EnableMapMode(false);
317 getOutputDevice().DrawOutDev(
318 aTopLeft
, aSize
, // destination
319 aTopLeft
, aSize
, // source
320 maOutputBufferDevice
);
323 /*getOutputDevice().SetLineColor(COL_RED);
324 getOutputDevice().SetFillColor();
325 getOutputDevice().DrawRect(Rectangle(aTopLeft, aSize));*/
328 getOutputDevice().EnableMapMode(bMapModeWasEnabledDest
);
333 // Restore all rectangles for remembered region from buffer
334 ImpRestoreBackground();
336 // paint overlay content for remembered region, use
337 // method from base class directly
338 OverlayManager::ImpDrawMembers(aBufferRememberedRangeLogic
, getOutputDevice());
341 // VCL hack for transparent child windows
342 // Problem is e.g. a radiobuttion form control in life mode. The used window
343 // is a transparence vcl childwindow. This flag only allows the parent window to
344 // paint into the child windows area, but there is no mechanism which takes
345 // care for a repaint of the child window. A transparent child window is NOT
346 // a window which always keeps it's content consistent over the parent, but it's
347 // more like just a paint flag for the parent.
348 // To get the update, the windows in question are updated manulally here.
351 Window
& rWindow
= static_cast< Window
& >(rmOutputDevice
);
353 const Rectangle
aRegionRectanglePixel(
354 maBufferRememberedRangePixel
.getMinX(),
355 maBufferRememberedRangePixel
.getMinY(),
356 maBufferRememberedRangePixel
.getMaxX(),
357 maBufferRememberedRangePixel
.getMaxY());
358 PaintTransparentChildren(rWindow
, aRegionRectanglePixel
);
361 // #i80730# restore visibility of VCL cursor
362 if(bCursorWasEnabled
)
364 Window
& rWindow
= static_cast< Window
& >(rmOutputDevice
);
365 Cursor
* pCursor
= rWindow
.GetCursor();
369 // check if cursor still exists. It may have been deleted from someone
374 // forget remembered Region
375 maBufferRememberedRangePixel
.reset();
381 OverlayManagerBuffered::OverlayManagerBuffered(
382 OutputDevice
& rOutputDevice
,
383 bool bRefreshWithPreRendering
)
384 : OverlayManager(rOutputDevice
),
385 mbRefreshWithPreRendering(bRefreshWithPreRendering
)
388 maBufferTimer
.SetTimeout(1);
389 maBufferTimer
.SetTimeoutHdl(LINK(this, OverlayManagerBuffered
, ImpBufferTimerHandler
));
392 rtl::Reference
<OverlayManager
> OverlayManagerBuffered::create(
393 OutputDevice
& rOutputDevice
,
394 bool bRefreshWithPreRendering
)
396 return rtl::Reference
<OverlayManager
>(new OverlayManagerBuffered(rOutputDevice
,
397 bRefreshWithPreRendering
));
400 OverlayManagerBuffered::~OverlayManagerBuffered()
403 maBufferTimer
.Stop();
405 if(!maBufferRememberedRangePixel
.isEmpty())
407 // Restore all rectangles for remembered region from buffer
408 ImpRestoreBackground();
412 void OverlayManagerBuffered::completeRedraw(const Region
& rRegion
, OutputDevice
* pPreRenderDevice
) const
414 if(!rRegion
.IsEmpty())
416 // save new background
417 ((OverlayManagerBuffered
*)this)->ImpSaveBackground(rRegion
, pPreRenderDevice
);
421 OverlayManager::completeRedraw(rRegion
, pPreRenderDevice
);
424 void OverlayManagerBuffered::flush()
426 // call timer handler direct
427 ImpBufferTimerHandler(0);
430 // #i68597# part of content gets copied, react on it
431 void OverlayManagerBuffered::copyArea(const Point
& rDestPt
, const Point
& rSrcPt
, const Size
& rSrcSize
)
433 // scroll local buffered area
434 maBufferDevice
.CopyArea(rDestPt
, rSrcPt
, rSrcSize
);
437 void OverlayManagerBuffered::restoreBackground(const Region
& rRegion
) const
440 const Region
aRegionPixel(getOutputDevice().LogicToPixel(rRegion
));
441 ImpRestoreBackground(aRegionPixel
);
444 OverlayManager::restoreBackground(rRegion
);
447 void OverlayManagerBuffered::invalidateRange(const basegfx::B2DRange
& rRange
)
449 if(!rRange
.isEmpty())
451 // buffered output, do not invalidate but use the timer
452 // to trigger a timer event for refresh
453 maBufferTimer
.Start();
455 // add the discrete range to the remembered region
456 // #i75163# use double precision and floor/ceil rounding to get overlapped pixel region, even
457 // when the given logic region has a width/height of 0.0. This does NOT work with LogicToPixel
458 // since it just transforms the top left and bottom right points equally without taking
459 // discrete pixel coverage into account. An empty B2DRange and thus empty logic Rectangle translated
460 // to an also empty discrete pixel rectangle - what is wrong.
461 basegfx::B2DRange
aDiscreteRange(rRange
);
462 aDiscreteRange
.transform(getOutputDevice().GetViewTransformation());
464 if(maDrawinglayerOpt
.IsAntiAliasing())
466 // assume AA needs one pixel more and invalidate one pixel more
467 const double fDiscreteOne(getDiscreteOne());
468 const basegfx::B2IPoint
aTopLeft(
469 (sal_Int32
)floor(aDiscreteRange
.getMinX() - fDiscreteOne
),
470 (sal_Int32
)floor(aDiscreteRange
.getMinY() - fDiscreteOne
));
471 const basegfx::B2IPoint
aBottomRight(
472 (sal_Int32
)ceil(aDiscreteRange
.getMaxX() + fDiscreteOne
),
473 (sal_Int32
)ceil(aDiscreteRange
.getMaxY() + fDiscreteOne
));
475 maBufferRememberedRangePixel
.expand(aTopLeft
);
476 maBufferRememberedRangePixel
.expand(aBottomRight
);
480 const basegfx::B2IPoint
aTopLeft((sal_Int32
)floor(aDiscreteRange
.getMinX()), (sal_Int32
)floor(aDiscreteRange
.getMinY()));
481 const basegfx::B2IPoint
aBottomRight((sal_Int32
)ceil(aDiscreteRange
.getMaxX()), (sal_Int32
)ceil(aDiscreteRange
.getMaxY()));
483 maBufferRememberedRangePixel
.expand(aTopLeft
);
484 maBufferRememberedRangePixel
.expand(aBottomRight
);
488 } // end of namespace overlay
489 } // end of namespace sdr
491 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */