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