xref: /trunk/main/svx/source/sdr/overlay/overlaymanagerbuffered.cxx (revision 1ecadb572e7010ff3b3382ad9bf179dbc6efadbb)
1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_svx.hxx"
30 #include <svx/sdr/overlay/overlaymanagerbuffered.hxx>
31 #include <vcl/outdev.hxx>
32 #include <basegfx/point/b2dpoint.hxx>
33 #include <basegfx/range/b2drange.hxx>
34 #include <vcl/salbtype.hxx>
35 #include <vcl/window.hxx>
36 #include <vcl/bitmap.hxx>
37 #include <tools/stream.hxx>
38 #include <basegfx/matrix/b2dhommatrix.hxx>
39 #include <vcl/cursor.hxx>
40 
41 //////////////////////////////////////////////////////////////////////////////
42 
43 namespace sdr
44 {
45     namespace overlay
46     {
47         void OverlayManagerBuffered::ImpPrepareBufferDevice()
48         {
49             // compare size of maBufferDevice with size of visible area
50             if(maBufferDevice.GetOutputSizePixel() != getOutputDevice().GetOutputSizePixel())
51             {
52                 // set new buffer size, copy as much content as possible (use bool parameter for vcl).
53                 // Newly uncovered regions will be repainted.
54                 maBufferDevice.SetOutputSizePixel(getOutputDevice().GetOutputSizePixel(), false);
55             }
56 
57             // compare the MapModes for zoom/scroll changes
58             if(maBufferDevice.GetMapMode() != getOutputDevice().GetMapMode())
59             {
60                 const bool bZoomed(
61                     maBufferDevice.GetMapMode().GetScaleX() != getOutputDevice().GetMapMode().GetScaleX()
62                     || maBufferDevice.GetMapMode().GetScaleY() != getOutputDevice().GetMapMode().GetScaleY());
63 
64                 if(!bZoomed)
65                 {
66                     const Point& rOriginOld = maBufferDevice.GetMapMode().GetOrigin();
67                     const Point& rOriginNew = getOutputDevice().GetMapMode().GetOrigin();
68                     const bool bScrolled(rOriginOld != rOriginNew);
69 
70                     if(bScrolled)
71                     {
72                         // get pixel bounds
73                         const Point aOriginOldPixel(maBufferDevice.LogicToPixel(rOriginOld));
74                         const Point aOriginNewPixel(maBufferDevice.LogicToPixel(rOriginNew));
75                         const Size aOutputSizePixel(maBufferDevice.GetOutputSizePixel());
76 
77                         // remember and switch off MapMode
78                         const bool bMapModeWasEnabled(maBufferDevice.IsMapModeEnabled());
79                         maBufferDevice.EnableMapMode(false);
80 
81                         // scroll internally buffered stuff
82                         const Point aDestinationOffsetPixel(aOriginNewPixel - aOriginOldPixel);
83                         maBufferDevice.DrawOutDev(
84                             aDestinationOffsetPixel, aOutputSizePixel, // destination
85                             Point(), aOutputSizePixel); // source
86 
87                         // restore MapMode
88                         maBufferDevice.EnableMapMode(bMapModeWasEnabled);
89 
90                         // scroll remembered region, too.
91                         if(!maBufferRememberedRangePixel.isEmpty())
92                         {
93                             const basegfx::B2IPoint aIPointDestinationOffsetPixel(aDestinationOffsetPixel.X(), aDestinationOffsetPixel.Y());
94                             const basegfx::B2IPoint aNewMinimum(maBufferRememberedRangePixel.getMinimum() + aIPointDestinationOffsetPixel);
95                             const basegfx::B2IPoint aNewMaximum(maBufferRememberedRangePixel.getMaximum() + aIPointDestinationOffsetPixel);
96                             maBufferRememberedRangePixel = basegfx::B2IRange(aNewMinimum, aNewMaximum);
97                         }
98                     }
99                 }
100 
101                 // copy new MapMode
102                 maBufferDevice.SetMapMode(getOutputDevice().GetMapMode());
103             }
104 
105             // #i29186#
106             maBufferDevice.SetDrawMode(getOutputDevice().GetDrawMode());
107             maBufferDevice.SetSettings(getOutputDevice().GetSettings());
108             maBufferDevice.SetAntialiasing(getOutputDevice().GetAntialiasing());
109         }
110 
111         void OverlayManagerBuffered::ImpRestoreBackground() const
112         {
113             const Rectangle aRegionRectanglePixel(
114                 maBufferRememberedRangePixel.getMinX(), maBufferRememberedRangePixel.getMinY(),
115                 maBufferRememberedRangePixel.getMaxX(), maBufferRememberedRangePixel.getMaxY());
116             const Region aRegionPixel(aRegionRectanglePixel);
117 
118             ImpRestoreBackground(aRegionPixel);
119         }
120 
121         void OverlayManagerBuffered::ImpRestoreBackground(const Region& rRegionPixel) const
122         {
123             // local region
124             Region aRegionPixel(rRegionPixel);
125             RegionHandle aRegionHandle(aRegionPixel.BeginEnumRects());
126             Rectangle aRegionRectanglePixel;
127 
128             // MapModes off
129             const bool bMapModeWasEnabledDest(getOutputDevice().IsMapModeEnabled());
130             const bool bMapModeWasEnabledSource(maBufferDevice.IsMapModeEnabled());
131             getOutputDevice().EnableMapMode(false);
132             ((OverlayManagerBuffered*)this)->maBufferDevice.EnableMapMode(false);
133 
134             while(aRegionPixel.GetEnumRects(aRegionHandle, aRegionRectanglePixel))
135             {
136 #ifdef DBG_UTIL
137                 // #i72754# possible graphical region test only with non-pro
138                 static bool bDoPaintForVisualControl(false);
139                 if(bDoPaintForVisualControl)
140                 {
141                     getOutputDevice().SetLineColor(COL_LIGHTGREEN);
142                     getOutputDevice().SetFillColor();
143                     getOutputDevice().DrawRect(aRegionRectanglePixel);
144                 }
145 #endif
146 
147                 // restore the area
148                 const Point aTopLeft(aRegionRectanglePixel.TopLeft());
149                 const Size aSize(aRegionRectanglePixel.GetSize());
150 
151                 getOutputDevice().DrawOutDev(
152                     aTopLeft, aSize, // destination
153                     aTopLeft, aSize, // source
154                     maBufferDevice);
155             }
156 
157             aRegionPixel.EndEnumRects(aRegionHandle);
158 
159             // restore MapModes
160             getOutputDevice().EnableMapMode(bMapModeWasEnabledDest);
161             ((OverlayManagerBuffered*)this)->maBufferDevice.EnableMapMode(bMapModeWasEnabledSource);
162         }
163 
164         void OverlayManagerBuffered::ImpSaveBackground(const Region& rRegion, OutputDevice* pPreRenderDevice)
165         {
166             // prepare source
167             OutputDevice& rSource = (pPreRenderDevice) ? *pPreRenderDevice : getOutputDevice();
168 
169             // Ensure buffer is valid
170             ImpPrepareBufferDevice();
171 
172             // build region which needs to be copied
173             Region aRegion(rSource.LogicToPixel(rRegion));
174 
175             // limit to PaintRegion if it's a window. This will be evtl. the expanded one,
176             // but always the exact redraw area
177             if(OUTDEV_WINDOW == rSource.GetOutDevType())
178             {
179                 Window& rWindow = (Window&)rSource;
180                 Region aPaintRegionPixel = rWindow.LogicToPixel(rWindow.GetPaintRegion());
181                 aRegion.Intersect(aPaintRegionPixel);
182 
183                 // #i72754# Make sure content is completetly rendered, the window
184                 // will be used as source of a DrawOutDev soon
185                 rWindow.Flush();
186             }
187 
188             // also limit to buffer size
189             const Rectangle aBufferDeviceRectanglePixel = Rectangle(Point(), maBufferDevice.GetOutputSizePixel());
190             aRegion.Intersect(aBufferDeviceRectanglePixel);
191 
192             // prepare to iterate over the rectangles from the region in pixels
193             RegionHandle aRegionHandle(aRegion.BeginEnumRects());
194             Rectangle aRegionRectanglePixel;
195 
196             // MapModes off
197             const bool bMapModeWasEnabledDest(rSource.IsMapModeEnabled());
198             const bool bMapModeWasEnabledSource(maBufferDevice.IsMapModeEnabled());
199             rSource.EnableMapMode(false);
200             maBufferDevice.EnableMapMode(false);
201 
202             while(aRegion.GetEnumRects(aRegionHandle, aRegionRectanglePixel))
203             {
204                 // for each rectangle, save the area
205                 Point aTopLeft(aRegionRectanglePixel.TopLeft());
206                 Size aSize(aRegionRectanglePixel.GetSize());
207 
208                 maBufferDevice.DrawOutDev(
209                     aTopLeft, aSize, // destination
210                     aTopLeft, aSize, // source
211                     rSource);
212 
213 #ifdef DBG_UTIL
214                 // #i72754# possible graphical region test only with non-pro
215                 static bool bDoPaintForVisualControl(false);
216                 if(bDoPaintForVisualControl)
217                 {
218                     const bool bMapModeWasEnabledTest(getOutputDevice().IsMapModeEnabled());
219                     getOutputDevice().EnableMapMode(false);
220                     getOutputDevice().SetLineColor(COL_LIGHTRED);
221                     getOutputDevice().SetFillColor();
222                     getOutputDevice().DrawRect(aRegionRectanglePixel);
223                     getOutputDevice().EnableMapMode(bMapModeWasEnabledTest);
224                 }
225 
226                 static bool bDoSaveForVisualControl(false);
227                 if(bDoSaveForVisualControl)
228                 {
229                     const Bitmap aBitmap(maBufferDevice.GetBitmap(aTopLeft, aSize));
230                     SvFileStream aNew((const String&)String(ByteString( "c:\\test.bmp" ), RTL_TEXTENCODING_UTF8), STREAM_WRITE|STREAM_TRUNC);
231                     aNew << aBitmap;
232                 }
233 #endif
234             }
235 
236             aRegion.EndEnumRects(aRegionHandle);
237 
238             // restore MapModes
239             rSource.EnableMapMode(bMapModeWasEnabledDest);
240             maBufferDevice.EnableMapMode(bMapModeWasEnabledSource);
241         }
242 
243         IMPL_LINK(OverlayManagerBuffered, ImpBufferTimerHandler, AutoTimer*, /*pTimer*/)
244         {
245             // stop timer
246             maBufferTimer.Stop();
247 
248             if(!maBufferRememberedRangePixel.isEmpty())
249             {
250                 // logic size for impDrawMember call
251                 basegfx::B2DRange aBufferRememberedRangeLogic(
252                     maBufferRememberedRangePixel.getMinX(), maBufferRememberedRangePixel.getMinY(),
253                     maBufferRememberedRangePixel.getMaxX(), maBufferRememberedRangePixel.getMaxY());
254                 aBufferRememberedRangeLogic.transform(getOutputDevice().GetInverseViewTransformation());
255 
256                 // prepare cursor handling
257                 const bool bTargetIsWindow(OUTDEV_WINDOW == rmOutputDevice.GetOutDevType());
258                 bool bCursorWasEnabled(false);
259 
260                 // #i80730# switch off VCL cursor during overlay refresh
261                 if(bTargetIsWindow)
262                 {
263                     Window& rWindow = static_cast< Window& >(rmOutputDevice);
264                     Cursor* pCursor = rWindow.GetCursor();
265 
266                     if(pCursor && pCursor->IsVisible())
267                     {
268                         pCursor->Hide();
269                         bCursorWasEnabled = true;
270                     }
271                 }
272 
273                 if(DoRefreshWithPreRendering())
274                 {
275                     // #i73602# ensure valid and sized maOutputBufferDevice
276                     const Size aDestinationSizePixel(maBufferDevice.GetOutputSizePixel());
277                     const Size aOutputBufferSizePixel(maOutputBufferDevice.GetOutputSizePixel());
278 
279                     if(aDestinationSizePixel != aOutputBufferSizePixel)
280                     {
281                         maOutputBufferDevice.SetOutputSizePixel(aDestinationSizePixel);
282                     }
283 
284                     maOutputBufferDevice.SetMapMode(getOutputDevice().GetMapMode());
285                     maOutputBufferDevice.EnableMapMode(false);
286                     maOutputBufferDevice.SetDrawMode(maBufferDevice.GetDrawMode());
287                     maOutputBufferDevice.SetSettings(maBufferDevice.GetSettings());
288                     maOutputBufferDevice.SetAntialiasing(maBufferDevice.GetAntialiasing());
289 
290                     // calculate sizes
291                     Rectangle aRegionRectanglePixel(
292                         maBufferRememberedRangePixel.getMinX(), maBufferRememberedRangePixel.getMinY(),
293                         maBufferRememberedRangePixel.getMaxX(), maBufferRememberedRangePixel.getMaxY());
294 
295                     // truncate aRegionRectanglePixel to destination pixel size, more does
296                     // not need to be prepared since destination is a buffer for a window. So,
297                     // maximum size indirectly shall be limited to getOutputDevice().GetOutputSizePixel()
298                     if(aRegionRectanglePixel.Left() < 0L)
299                     {
300                         aRegionRectanglePixel.Left() = 0L;
301                     }
302 
303                     if(aRegionRectanglePixel.Top() < 0L)
304                     {
305                         aRegionRectanglePixel.Top() = 0L;
306                     }
307 
308                     if(aRegionRectanglePixel.Right() > aDestinationSizePixel.getWidth())
309                     {
310                         aRegionRectanglePixel.Right() = aDestinationSizePixel.getWidth();
311                     }
312 
313                     if(aRegionRectanglePixel.Bottom() > aDestinationSizePixel.getHeight())
314                     {
315                         aRegionRectanglePixel.Bottom() = aDestinationSizePixel.getHeight();
316                     }
317 
318                     // get sizes
319                     const Point aTopLeft(aRegionRectanglePixel.TopLeft());
320                     const Size aSize(aRegionRectanglePixel.GetSize());
321 
322                     {
323                         const bool bMapModeWasEnabledDest(maBufferDevice.IsMapModeEnabled());
324                         maBufferDevice.EnableMapMode(false);
325 
326                         maOutputBufferDevice.DrawOutDev(
327                             aTopLeft, aSize, // destination
328                             aTopLeft, aSize, // source
329                             maBufferDevice);
330 
331                         // restore MapModes
332                         maBufferDevice.EnableMapMode(bMapModeWasEnabledDest);
333                     }
334 
335                     // paint overlay content for remembered region, use
336                     // method from base class directly
337                     maOutputBufferDevice.EnableMapMode(true);
338                     OverlayManager::ImpDrawMembers(aBufferRememberedRangeLogic, maOutputBufferDevice);
339                     maOutputBufferDevice.EnableMapMode(false);
340 
341                     // copy to output
342                     {
343                         const bool bMapModeWasEnabledDest(getOutputDevice().IsMapModeEnabled());
344                         getOutputDevice().EnableMapMode(false);
345 
346                         getOutputDevice().DrawOutDev(
347                             aTopLeft, aSize, // destination
348                             aTopLeft, aSize, // source
349                             maOutputBufferDevice);
350 
351                         // debug
352                         /*getOutputDevice().SetLineColor(COL_RED);
353                         getOutputDevice().SetFillColor();
354                         getOutputDevice().DrawRect(Rectangle(aTopLeft, aSize));*/
355 
356                         // restore MapModes
357                         getOutputDevice().EnableMapMode(bMapModeWasEnabledDest);
358                     }
359                 }
360                 else
361                 {
362                     // Restore all rectangles for remembered region from buffer
363                     ImpRestoreBackground();
364 
365                     // paint overlay content for remembered region, use
366                     // method from base class directly
367                     OverlayManager::ImpDrawMembers(aBufferRememberedRangeLogic, getOutputDevice());
368                 }
369 
370                 // VCL hack for transparent child windows
371                 // Problem is e.g. a radiobuttion form control in life mode. The used window
372                 // is a transparence vcl childwindow. This flag only allows the parent window to
373                 // paint into the child windows area, but there is no mechanism which takes
374                 // care for a repaint of the child window. A transparent child window is NOT
375                 // a window which always keeps it's content consistent over the parent, but it's
376                 // more like just a paint flag for the parent.
377                 // To get the update, the windows in question are updated manulally here.
378                 if(bTargetIsWindow)
379                 {
380                     Window& rWindow = static_cast< Window& >(rmOutputDevice);
381 
382                     if(rWindow.IsChildTransparentModeEnabled() && rWindow.GetChildCount())
383                     {
384                         const Rectangle aRegionRectanglePixel(
385                             maBufferRememberedRangePixel.getMinX(), maBufferRememberedRangePixel.getMinY(),
386                             maBufferRememberedRangePixel.getMaxX(), maBufferRememberedRangePixel.getMaxY());
387 
388                         for(sal_uInt16 a(0); a < rWindow.GetChildCount(); a++)
389                         {
390                             Window* pCandidate = rWindow.GetChild(a);
391 
392                             if(pCandidate && pCandidate->IsPaintTransparent())
393                             {
394                                 const Rectangle aCandidatePosSizePixel(pCandidate->GetPosPixel(), pCandidate->GetSizePixel());
395 
396                                 if(aCandidatePosSizePixel.IsOver(aRegionRectanglePixel))
397                                 {
398                                     pCandidate->Invalidate(INVALIDATE_NOTRANSPARENT|INVALIDATE_CHILDREN);
399                                     pCandidate->Update();
400                                 }
401                             }
402                         }
403                     }
404                 }
405 
406                 // #i80730# restore visibility of VCL cursor
407                 if(bCursorWasEnabled)
408                 {
409                     Window& rWindow = static_cast< Window& >(rmOutputDevice);
410                     Cursor* pCursor = rWindow.GetCursor();
411 
412                     if(pCursor)
413                     {
414                         // check if cursor still exists. It may have been deleted from someone
415                         pCursor->Show();
416                     }
417                 }
418 
419                 // forget remembered Region
420                 maBufferRememberedRangePixel.reset();
421             }
422 
423             return 0;
424         }
425 
426         OverlayManagerBuffered::OverlayManagerBuffered(
427             OutputDevice& rOutputDevice,
428             OverlayManager* pOldOverlayManager,
429             bool bRefreshWithPreRendering)
430         :   OverlayManager(rOutputDevice, pOldOverlayManager),
431             mbRefreshWithPreRendering(bRefreshWithPreRendering)
432         {
433             // Init timer
434             maBufferTimer.SetTimeout(1);
435             maBufferTimer.SetTimeoutHdl(LINK(this, OverlayManagerBuffered, ImpBufferTimerHandler));
436         }
437 
438         OverlayManagerBuffered::~OverlayManagerBuffered()
439         {
440             // Clear timer
441             maBufferTimer.Stop();
442 
443             if(!maBufferRememberedRangePixel.isEmpty())
444             {
445                 // Restore all rectangles for remembered region from buffer
446                 ImpRestoreBackground();
447             }
448         }
449 
450         void OverlayManagerBuffered::completeRedraw(const Region& rRegion, OutputDevice* pPreRenderDevice) const
451         {
452             if(!rRegion.IsEmpty())
453             {
454                 // save new background
455                 ((OverlayManagerBuffered*)this)->ImpSaveBackground(rRegion, pPreRenderDevice);
456             }
457 
458             // call parent
459             OverlayManager::completeRedraw(rRegion, pPreRenderDevice);
460         }
461 
462         void OverlayManagerBuffered::flush()
463         {
464             // call timer handler direct
465             ImpBufferTimerHandler(0);
466         }
467 
468         // #i68597# part of content gets copied, react on it
469         void OverlayManagerBuffered::copyArea(const Point& rDestPt, const Point& rSrcPt, const Size& rSrcSize)
470         {
471             // scroll local buffered area
472             maBufferDevice.CopyArea(rDestPt, rSrcPt, rSrcSize);
473         }
474 
475         void OverlayManagerBuffered::restoreBackground(const Region& rRegion) const
476         {
477             // restore
478             const Region aRegionPixel(getOutputDevice().LogicToPixel(rRegion));
479             ImpRestoreBackground(aRegionPixel);
480 
481             // call parent
482             OverlayManager::restoreBackground(rRegion);
483         }
484 
485         void OverlayManagerBuffered::invalidateRange(const basegfx::B2DRange& rRange)
486         {
487             if(!rRange.isEmpty())
488             {
489                 // buffered output, do not invalidate but use the timer
490                 // to trigger a timer event for refresh
491                 maBufferTimer.Start();
492 
493                 // add the discrete range to the remembered region
494                 // #i75163# use double precision and floor/ceil rounding to get overlapped pixel region, even
495                 // when the given logic region has a width/height of 0.0. This does NOT work with LogicToPixel
496                 // since it just transforms the top left and bottom right points equally without taking
497                 // discrete pixel coverage into account. An empty B2DRange and thus empty logic Rectangle translated
498                 // to an also empty discrete pixel rectangle - what is wrong.
499                 basegfx::B2DRange aDiscreteRange(rRange);
500                 aDiscreteRange.transform(getOutputDevice().GetViewTransformation());
501 
502                 if(maDrawinglayerOpt.IsAntiAliasing())
503                 {
504                     // assume AA needs one pixel more and invalidate one pixel more
505                     const double fDiscreteOne(getDiscreteOne());
506                     const basegfx::B2IPoint aTopLeft(
507                         (sal_Int32)floor(aDiscreteRange.getMinX() - fDiscreteOne),
508                         (sal_Int32)floor(aDiscreteRange.getMinY() - fDiscreteOne));
509                     const basegfx::B2IPoint aBottomRight(
510                         (sal_Int32)ceil(aDiscreteRange.getMaxX() + fDiscreteOne),
511                         (sal_Int32)ceil(aDiscreteRange.getMaxY() + fDiscreteOne));
512 
513                     maBufferRememberedRangePixel.expand(aTopLeft);
514                     maBufferRememberedRangePixel.expand(aBottomRight);
515                 }
516                 else
517                 {
518                     const basegfx::B2IPoint aTopLeft((sal_Int32)floor(aDiscreteRange.getMinX()), (sal_Int32)floor(aDiscreteRange.getMinY()));
519                     const basegfx::B2IPoint aBottomRight((sal_Int32)ceil(aDiscreteRange.getMaxX()), (sal_Int32)ceil(aDiscreteRange.getMaxY()));
520 
521                     maBufferRememberedRangePixel.expand(aTopLeft);
522                     maBufferRememberedRangePixel.expand(aBottomRight);
523                 }
524             }
525         }
526 
527         void OverlayManagerBuffered::SetRefreshWithPreRendering(bool bNew)
528         {
529             if((bool)mbRefreshWithPreRendering != bNew)
530             {
531                 mbRefreshWithPreRendering = bNew;
532             }
533         }
534     } // end of namespace overlay
535 } // end of namespace sdr
536 
537 //////////////////////////////////////////////////////////////////////////////
538 // eof
539