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