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