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