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 			bool bRefreshWithPreRendering)
425 		:	OverlayManager(rOutputDevice),
426 			mbRefreshWithPreRendering(bRefreshWithPreRendering)
427 		{
428 			// Init timer
429 			maBufferTimer.SetTimeout(1);
430 			maBufferTimer.SetTimeoutHdl(LINK(this, OverlayManagerBuffered, ImpBufferTimerHandler));
431 		}
432 
433 		OverlayManagerBuffered::~OverlayManagerBuffered()
434 		{
435 			// Clear timer
436 			maBufferTimer.Stop();
437 
438 			if(!maBufferRememberedRangePixel.isEmpty())
439 			{
440 				// Restore all rectangles for remembered region from buffer
441 				ImpRestoreBackground();
442 			}
443 		}
444 
445 		void OverlayManagerBuffered::completeRedraw(const Region& rRegion, OutputDevice* pPreRenderDevice) const
446 		{
447 			if(!rRegion.IsEmpty())
448 			{
449 				// save new background
450 				((OverlayManagerBuffered*)this)->ImpSaveBackground(rRegion, pPreRenderDevice);
451 			}
452 
453 			// call parent
454 			OverlayManager::completeRedraw(rRegion, pPreRenderDevice);
455 		}
456 
457 		void OverlayManagerBuffered::flush()
458 		{
459 			// call timer handler direct
460 			ImpBufferTimerHandler(0);
461 		}
462 
463 		// #i68597# part of content gets copied, react on it
464 		void OverlayManagerBuffered::copyArea(const Point& rDestPt, const Point& rSrcPt, const Size& rSrcSize)
465 		{
466 			// scroll local buffered area
467 			maBufferDevice.CopyArea(rDestPt, rSrcPt, rSrcSize);
468 		}
469 
470 		void OverlayManagerBuffered::restoreBackground(const Region& rRegion) const
471 		{
472 			// restore
473 			const Region aRegionPixel(getOutputDevice().LogicToPixel(rRegion));
474 			ImpRestoreBackground(aRegionPixel);
475 
476 			// call parent
477 			OverlayManager::restoreBackground(rRegion);
478 		}
479 
480 		void OverlayManagerBuffered::invalidateRange(const basegfx::B2DRange& rRange)
481 		{
482             if(!rRange.isEmpty())
483             {
484 			    // buffered output, do not invalidate but use the timer
485 			    // to trigger a timer event for refresh
486 			    maBufferTimer.Start();
487 
488 			    // add the discrete range to the remembered region
489 			    // #i75163# use double precision and floor/ceil rounding to get overlapped pixel region, even
490 			    // when the given logic region has a width/height of 0.0. This does NOT work with LogicToPixel
491 			    // since it just transforms the top left and bottom right points equally without taking
492 			    // discrete pixel coverage into account. An empty B2DRange and thus empty logic Rectangle translated
493 			    // to an also empty discrete pixel rectangle - what is wrong.
494 			    basegfx::B2DRange aDiscreteRange(rRange);
495 			    aDiscreteRange.transform(getOutputDevice().GetViewTransformation());
496 
497 			    if(maDrawinglayerOpt.IsAntiAliasing())
498 			    {
499 				    // assume AA needs one pixel more and invalidate one pixel more
500                     const double fDiscreteOne(getDiscreteOne());
501 				    const basegfx::B2IPoint aTopLeft(
502 					    (sal_Int32)floor(aDiscreteRange.getMinX() - fDiscreteOne),
503 					    (sal_Int32)floor(aDiscreteRange.getMinY() - fDiscreteOne));
504 				    const basegfx::B2IPoint aBottomRight(
505 					    (sal_Int32)ceil(aDiscreteRange.getMaxX() + fDiscreteOne),
506 					    (sal_Int32)ceil(aDiscreteRange.getMaxY() + fDiscreteOne));
507 
508 				    maBufferRememberedRangePixel.expand(aTopLeft);
509 				    maBufferRememberedRangePixel.expand(aBottomRight);
510 			    }
511 			    else
512 			    {
513 				    const basegfx::B2IPoint aTopLeft((sal_Int32)floor(aDiscreteRange.getMinX()), (sal_Int32)floor(aDiscreteRange.getMinY()));
514 				    const basegfx::B2IPoint aBottomRight((sal_Int32)ceil(aDiscreteRange.getMaxX()), (sal_Int32)ceil(aDiscreteRange.getMaxY()));
515 
516 				    maBufferRememberedRangePixel.expand(aTopLeft);
517 				    maBufferRememberedRangePixel.expand(aBottomRight);
518 			    }
519             }
520 		}
521 
522 		void OverlayManagerBuffered::SetRefreshWithPreRendering(bool bNew)
523 		{
524 			if((bool)mbRefreshWithPreRendering != bNew)
525 			{
526 				mbRefreshWithPreRendering = bNew;
527 			}
528 		}
529 	} // end of namespace overlay
530 } // end of namespace sdr
531 
532 //////////////////////////////////////////////////////////////////////////////
533 // eof
534