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