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_canvas.hxx" 26 27 #include <canvas/debug.hxx> 28 #include <tools/diagnose_ex.h> 29 #include <canvas/verbosetrace.hxx> 30 #include <canvas/canvastools.hxx> 31 32 #include <vcl/canvastools.hxx> 33 #include <vcl/outdev.hxx> 34 #include <vcl/window.hxx> 35 #include <vcl/bitmapex.hxx> 36 37 #include <basegfx/range/b2drectangle.hxx> 38 #include <basegfx/tools/canvastools.hxx> 39 40 #include <boost/cast.hpp> 41 42 #include "spritecanvashelper.hxx" 43 #include "canvascustomsprite.hxx" 44 45 46 using namespace ::com::sun::star; 47 48 #define FPS_BOUNDS Rectangle(0,0,130,90) 49 #define INFO_COLOR COL_RED 50 51 namespace vclcanvas 52 { 53 namespace 54 { 55 /** Sprite redraw at original position 56 57 Used to repaint the whole canvas (background and all 58 sprites) 59 */ spriteRedraw(OutputDevice & rOutDev,const::canvas::Sprite::Reference & rSprite)60 void spriteRedraw( OutputDevice& rOutDev, 61 const ::canvas::Sprite::Reference& rSprite ) 62 { 63 // downcast to derived vclcanvas::Sprite interface, which 64 // provides the actual redraw methods. 65 ::boost::polymorphic_downcast< Sprite* >(rSprite.get())->redraw(rOutDev, 66 true); 67 } 68 calcNumPixel(const::canvas::Sprite::Reference & rSprite)69 double calcNumPixel( const ::canvas::Sprite::Reference& rSprite ) 70 { 71 const ::basegfx::B2DSize& rSize( 72 ::boost::polymorphic_downcast< Sprite* >(rSprite.get())->getSizePixel() ); 73 74 return rSize.getX() * rSize.getY(); 75 } 76 repaintBackground(OutputDevice & rOutDev,OutputDevice & rBackBuffer,const::basegfx::B2DRange & rArea)77 void repaintBackground( OutputDevice& rOutDev, 78 OutputDevice& rBackBuffer, 79 const ::basegfx::B2DRange& rArea ) 80 { 81 const ::Point& rPos( ::vcl::unotools::pointFromB2DPoint( rArea.getMinimum()) ); 82 const ::Size& rSize( ::vcl::unotools::sizeFromB2DSize( rArea.getRange()) ); 83 84 rOutDev.DrawOutDev( rPos, rSize, rPos, rSize, rBackBuffer ); 85 } 86 opaqueUpdateSpriteArea(const::canvas::Sprite::Reference & rSprite,OutputDevice & rOutDev,const::basegfx::B2IRange & rArea)87 void opaqueUpdateSpriteArea( const ::canvas::Sprite::Reference& rSprite, 88 OutputDevice& rOutDev, 89 const ::basegfx::B2IRange& rArea ) 90 { 91 const Rectangle& rRequestedArea( 92 ::vcl::unotools::rectangleFromB2IRectangle( rArea ) ); 93 94 // clip output to actual update region (otherwise a) 95 // wouldn't save much render time, and b) will clutter 96 // scrolled sprite content outside this area) 97 rOutDev.EnableMapMode( sal_False ); 98 rOutDev.SetAntialiasing( ANTIALIASING_ENABLE_B2DDRAW ); 99 rOutDev.SetClipRegion( rRequestedArea ); 100 101 // repaint affected sprite directly to output device (at 102 // the actual screen output position) 103 ::boost::polymorphic_downcast< Sprite* >( 104 rSprite.get() )->redraw( rOutDev, 105 false ); // rendering 106 // directly to 107 // frontbuffer 108 } 109 110 /** Repaint sprite at original position 111 112 Used for opaque updates, which render directly to the 113 front buffer. 114 */ spriteRedrawStub(OutputDevice & rOutDev,const::canvas::Sprite::Reference & rSprite)115 void spriteRedrawStub( OutputDevice& rOutDev, 116 const ::canvas::Sprite::Reference& rSprite ) 117 { 118 if( rSprite.is() ) 119 { 120 ::boost::polymorphic_downcast< Sprite* >( 121 rSprite.get() )->redraw( rOutDev, 122 false ); 123 } 124 } 125 126 /** Repaint sprite at given position 127 128 Used for generic update, which renders into vdev of 129 adapted size. 130 */ spriteRedrawStub2(OutputDevice & rOutDev,const::basegfx::B2DPoint & rOutPos,const::canvas::Sprite::Reference & rSprite)131 void spriteRedrawStub2( OutputDevice& rOutDev, 132 const ::basegfx::B2DPoint& rOutPos, 133 const ::canvas::Sprite::Reference& rSprite ) 134 { 135 if( rSprite.is() ) 136 { 137 Sprite* pSprite = ::boost::polymorphic_downcast< Sprite* >( 138 rSprite.get() ); 139 140 // calc relative sprite position in rUpdateArea (which 141 // need not be the whole screen!) 142 const ::basegfx::B2DPoint& rSpriteScreenPos( pSprite->getPosPixel() ); 143 const ::basegfx::B2DPoint& rSpriteRenderPos( rSpriteScreenPos - rOutPos ); 144 145 pSprite->redraw( rOutDev, rSpriteRenderPos, true ); 146 } 147 } 148 149 /** Repaint sprite at original position 150 151 Used for opaque updates from scrollUpdate(), which render 152 directly to the front buffer. 153 */ spriteRedrawStub3(OutputDevice & rOutDev,const::canvas::SpriteRedrawManager::AreaComponent & rComponent)154 void spriteRedrawStub3( OutputDevice& rOutDev, 155 const ::canvas::SpriteRedrawManager::AreaComponent& rComponent ) 156 { 157 const ::canvas::Sprite::Reference& rSprite( rComponent.second.getSprite() ); 158 159 if( rSprite.is() ) 160 { 161 ::boost::polymorphic_downcast< Sprite* >( 162 rSprite.get() )->redraw( rOutDev, 163 false ); 164 } 165 } 166 renderInfoText(OutputDevice & rOutDev,const::rtl::OUString & rStr,const Point & rPos)167 void renderInfoText( OutputDevice& rOutDev, 168 const ::rtl::OUString& rStr, 169 const Point& rPos ) 170 { 171 Font aVCLFont; 172 aVCLFont.SetHeight( 20 ); 173 aVCLFont.SetColor( Color( INFO_COLOR ) ); 174 175 rOutDev.SetTextAlign(ALIGN_TOP); 176 rOutDev.SetTextColor( Color( INFO_COLOR ) ); 177 rOutDev.SetFont( aVCLFont ); 178 179 rOutDev.DrawText( rPos, rStr ); 180 } 181 182 } 183 SpriteCanvasHelper()184 SpriteCanvasHelper::SpriteCanvasHelper() : 185 mpRedrawManager( NULL ), 186 mpOwningSpriteCanvas( NULL ), 187 maVDev(), 188 maLastUpdate(), 189 mbShowFrameInfo( false ), 190 mbShowSpriteBounds( false ), 191 mbIsUnsafeScrolling( false ) 192 { 193 #if defined(VERBOSE) && OSL_DEBUG_LEVEL > 0 194 // inverse defaults for verbose debug mode 195 mbShowSpriteBounds = mbShowFrameInfo = true; 196 #endif 197 } 198 init(const OutDevProviderSharedPtr & rOutDev,SpriteCanvas & rOwningSpriteCanvas,::canvas::SpriteRedrawManager & rManager,bool bProtect,bool bHaveAlpha)199 void SpriteCanvasHelper::init( const OutDevProviderSharedPtr& rOutDev, 200 SpriteCanvas& rOwningSpriteCanvas, 201 ::canvas::SpriteRedrawManager& rManager, 202 bool bProtect, 203 bool bHaveAlpha ) 204 { 205 mpOwningSpriteCanvas = &rOwningSpriteCanvas; 206 mpRedrawManager = &rManager; 207 208 CanvasHelper::init(rOwningSpriteCanvas,rOutDev,bProtect,bHaveAlpha); 209 } 210 disposing()211 void SpriteCanvasHelper::disposing() 212 { 213 mpRedrawManager = NULL; 214 mpOwningSpriteCanvas = NULL; 215 216 // forward to base 217 CanvasHelper::disposing(); 218 } 219 createSpriteFromAnimation(const uno::Reference<rendering::XAnimation> &)220 uno::Reference< rendering::XAnimatedSprite > SpriteCanvasHelper::createSpriteFromAnimation( 221 const uno::Reference< rendering::XAnimation >& ) 222 { 223 return uno::Reference< rendering::XAnimatedSprite >(); 224 } 225 createSpriteFromBitmaps(const uno::Sequence<uno::Reference<rendering::XBitmap>> &,sal_Int8)226 uno::Reference< rendering::XAnimatedSprite > SpriteCanvasHelper::createSpriteFromBitmaps( 227 const uno::Sequence< uno::Reference< rendering::XBitmap > >& , 228 sal_Int8 ) 229 { 230 return uno::Reference< rendering::XAnimatedSprite >(); 231 } 232 createCustomSprite(const geometry::RealSize2D & spriteSize)233 uno::Reference< rendering::XCustomSprite > SpriteCanvasHelper::createCustomSprite( const geometry::RealSize2D& spriteSize ) 234 { 235 if( !mpRedrawManager || !mpDevice ) 236 return uno::Reference< rendering::XCustomSprite >(); // we're disposed 237 238 return uno::Reference< rendering::XCustomSprite >( 239 new CanvasCustomSprite( spriteSize, 240 *mpDevice, 241 mpOwningSpriteCanvas, 242 mpOwningSpriteCanvas->getFrontBuffer(), 243 mbShowSpriteBounds ) ); 244 } 245 createClonedSprite(const uno::Reference<rendering::XSprite> &)246 uno::Reference< rendering::XSprite > SpriteCanvasHelper::createClonedSprite( const uno::Reference< rendering::XSprite >& ) 247 { 248 return uno::Reference< rendering::XSprite >(); 249 } 250 updateScreen(sal_Bool bUpdateAll,bool & io_bSurfaceDirty)251 sal_Bool SpriteCanvasHelper::updateScreen( sal_Bool bUpdateAll, 252 bool& io_bSurfaceDirty ) 253 { 254 if( !mpRedrawManager || 255 !mpOwningSpriteCanvas || 256 !mpOwningSpriteCanvas->getFrontBuffer() || 257 !mpOwningSpriteCanvas->getBackBuffer() ) 258 { 259 return sal_False; // disposed, or otherwise dysfunctional 260 } 261 262 // commit to backbuffer 263 flush(); 264 265 OutputDevice& rOutDev( mpOwningSpriteCanvas->getFrontBuffer()->getOutDev() ); 266 BackBufferSharedPtr pBackBuffer( mpOwningSpriteCanvas->getBackBuffer() ); 267 OutputDevice& rBackOutDev( pBackBuffer->getOutDev() ); 268 269 // actual OutputDevice is a shared resource - restore its 270 // state when done. 271 tools::OutDevStateKeeper aStateKeeper( rOutDev ); 272 273 const Size aOutDevSize( rBackOutDev.GetOutputSizePixel() ); 274 const Point aEmptyPoint(0,0); 275 276 Window* pTargetWindow = NULL; 277 if( rOutDev.GetOutDevType() == OUTDEV_WINDOW ) 278 { 279 pTargetWindow = &static_cast<Window&>(rOutDev); // TODO(Q3): Evil downcast. 280 281 // we're double-buffered, thus no need for paint area-limiting 282 // clips. besides that, will interfere with animations (as for 283 // Window-invalidate repaints, only parts of the window will 284 // be redrawn otherwise) 285 const Region aFullWindowRegion( Rectangle(aEmptyPoint, 286 aOutDevSize) ); 287 pTargetWindow->ExpandPaintClipRegion(aFullWindowRegion); 288 } 289 290 // TODO(P1): Might be worthwhile to track areas of background 291 // changes, too. 292 if( !bUpdateAll && !io_bSurfaceDirty ) 293 { 294 if( mbShowFrameInfo ) 295 { 296 // also repaint background below frame counter (fake 297 // that as a sprite vanishing in this area) 298 mpRedrawManager->updateSprite( ::canvas::Sprite::Reference(), 299 ::basegfx::B2DPoint(), 300 ::basegfx::B2DRectangle( 0.0, 0.0, 301 FPS_BOUNDS.Right(), 302 FPS_BOUNDS.Bottom() ) ); 303 } 304 305 // background has not changed, so we're free to optimize 306 // repaint to areas where a sprite has changed 307 308 // process each independent area of overlapping sprites 309 // separately. 310 mpRedrawManager->forEachSpriteArea( *this ); 311 } 312 else 313 { 314 // background has changed, so we currently have no choice 315 // but repaint everything (or caller requested that) 316 317 maVDev->SetOutputSizePixel( aOutDevSize ); 318 maVDev->EnableMapMode( sal_False ); 319 maVDev->DrawOutDev( aEmptyPoint, aOutDevSize, 320 aEmptyPoint, aOutDevSize, 321 rBackOutDev ); 322 323 // repaint all active sprites on top of background into 324 // VDev. 325 mpRedrawManager->forEachSprite( 326 ::boost::bind( 327 &spriteRedraw, 328 ::boost::ref( maVDev.get() ), 329 _1 ) ); 330 331 // flush to screen 332 rOutDev.EnableMapMode( sal_False ); 333 rOutDev.SetAntialiasing( ANTIALIASING_ENABLE_B2DDRAW ); 334 rOutDev.SetClipRegion(); 335 rOutDev.DrawOutDev( aEmptyPoint, aOutDevSize, 336 aEmptyPoint, aOutDevSize, 337 *maVDev ); 338 } 339 340 // change record vector must be cleared, for the next turn of 341 // rendering and sprite changing 342 mpRedrawManager->clearChangeRecords(); 343 344 io_bSurfaceDirty = false; 345 346 if( mbShowFrameInfo ) 347 { 348 renderFrameCounter( rOutDev ); 349 renderSpriteCount( rOutDev ); 350 renderMemUsage( rOutDev ); 351 } 352 353 #if defined(VERBOSE) && OSL_DEBUG_LEVEL > 0 354 static ::canvas::tools::ElapsedTime aElapsedTime; 355 356 // log time immediately after surface flip 357 OSL_TRACE( "SpriteCanvasHelper::updateScreen(): flip done at %f", 358 aElapsedTime.getElapsedTime() ); 359 #endif 360 361 // sync output with screen, to ensure that we don't queue up 362 // render requests (calling code might rely on timing, 363 // i.e. assume that things are visible on screen after 364 // updateScreen() returns). 365 if( pTargetWindow ) 366 { 367 // commit to screen 368 pTargetWindow->Sync(); 369 } 370 371 return sal_True; 372 } 373 backgroundPaint(const::basegfx::B2DRange & rUpdateRect)374 void SpriteCanvasHelper::backgroundPaint( const ::basegfx::B2DRange& rUpdateRect ) 375 { 376 ENSURE_OR_THROW( mpOwningSpriteCanvas && 377 mpOwningSpriteCanvas->getBackBuffer() && 378 mpOwningSpriteCanvas->getFrontBuffer(), 379 "SpriteCanvasHelper::backgroundPaint(): NULL device pointer " ); 380 381 OutputDevice& rOutDev( mpOwningSpriteCanvas->getFrontBuffer()->getOutDev() ); 382 BackBufferSharedPtr pBackBuffer( mpOwningSpriteCanvas->getBackBuffer() ); 383 OutputDevice& rBackOutDev( pBackBuffer->getOutDev() ); 384 385 repaintBackground( rOutDev, rBackOutDev, rUpdateRect ); 386 } 387 scrollUpdate(const::basegfx::B2DRange & rMoveStart,const::basegfx::B2DRange & rMoveEnd,const::canvas::SpriteRedrawManager::UpdateArea & rUpdateArea)388 void SpriteCanvasHelper::scrollUpdate( const ::basegfx::B2DRange& rMoveStart, 389 const ::basegfx::B2DRange& rMoveEnd, 390 const ::canvas::SpriteRedrawManager::UpdateArea& rUpdateArea ) 391 { 392 ENSURE_OR_THROW( mpOwningSpriteCanvas && 393 mpOwningSpriteCanvas->getBackBuffer() && 394 mpOwningSpriteCanvas->getFrontBuffer(), 395 "SpriteCanvasHelper::scrollUpdate(): NULL device pointer " ); 396 397 OutputDevice& rOutDev( mpOwningSpriteCanvas->getFrontBuffer()->getOutDev() ); 398 BackBufferSharedPtr pBackBuffer( mpOwningSpriteCanvas->getBackBuffer() ); 399 OutputDevice& rBackOutDev( pBackBuffer->getOutDev() ); 400 401 const Size& rTargetSizePixel( rOutDev.GetOutputSizePixel() ); 402 const ::basegfx::B2IRange aOutputBounds( 0,0, 403 rTargetSizePixel.Width(), 404 rTargetSizePixel.Height() ); 405 406 // round rectangles to integer pixel. Note: have to be 407 // extremely careful here, to avoid off-by-one errors for 408 // the destination area: otherwise, the next scroll update 409 // would copy pixel that are not supposed to be part of 410 // the sprite. 411 ::basegfx::B2IRange aSourceRect( 412 ::canvas::tools::spritePixelAreaFromB2DRange( rMoveStart ) ); 413 const ::basegfx::B2IRange& rDestRect( 414 ::canvas::tools::spritePixelAreaFromB2DRange( rMoveEnd ) ); 415 ::basegfx::B2IPoint aDestPos( rDestRect.getMinimum() ); 416 417 ::std::vector< ::basegfx::B2IRange > aUnscrollableAreas; 418 419 // Since strictly speaking, this scroll algorithm is plain 420 // buggy, the scrolled area might actually lie _below_ another 421 // window - we've made this feature configurable via 422 // mbIsUnsafeScrolling. 423 424 // clip to output bounds (cannot properly scroll stuff 425 // _outside_ our screen area) 426 if( !mbIsUnsafeScrolling || 427 !::canvas::tools::clipScrollArea( aSourceRect, 428 aDestPos, 429 aUnscrollableAreas, 430 aOutputBounds ) ) 431 { 432 // fully clipped scroll area: cannot simply scroll 433 // then. Perform normal opaque update (can use that, since 434 // one of the preconditions for scrollable update is 435 // opaque sprite content) 436 437 // repaint all affected sprites directly to output device 438 ::std::for_each( rUpdateArea.maComponentList.begin(), 439 rUpdateArea.maComponentList.end(), 440 ::boost::bind( 441 &spriteRedrawStub3, 442 ::boost::ref( rOutDev ), 443 _1 ) ); 444 } 445 else 446 { 447 // scroll rOutDev content 448 rOutDev.CopyArea( ::vcl::unotools::pointFromB2IPoint( aDestPos ), 449 ::vcl::unotools::pointFromB2IPoint( aSourceRect.getMinimum() ), 450 // TODO(Q2): use numeric_cast to check range 451 ::Size( static_cast<sal_Int32>(aSourceRect.getRange().getX()), 452 static_cast<sal_Int32>(aSourceRect.getRange().getY()) ) ); 453 454 const ::canvas::SpriteRedrawManager::SpriteConnectedRanges::ComponentListType::const_iterator 455 aFirst( rUpdateArea.maComponentList.begin() ); 456 ::canvas::SpriteRedrawManager::SpriteConnectedRanges::ComponentListType::const_iterator 457 aSecond( aFirst ); ++aSecond; 458 459 ENSURE_OR_THROW( aFirst->second.getSprite().is(), 460 "VCLCanvas::scrollUpdate(): no sprite" ); 461 462 // repaint uncovered areas from sprite. Need to actually 463 // clip here, since we're only repainting _parts_ of the 464 // sprite 465 rOutDev.Push( PUSH_CLIPREGION ); 466 const Sprite::Reference& rSprite = aFirst->second.getSprite(); 467 ::std::for_each( aUnscrollableAreas.begin(), 468 aUnscrollableAreas.end(), 469 ::boost::bind( &opaqueUpdateSpriteArea, 470 ::boost::cref(rSprite), 471 ::boost::ref(rOutDev), 472 _1 ) ); 473 rOutDev.Pop(); 474 } 475 476 // repaint uncovered areas from backbuffer - take the 477 // _rounded_ rectangles from above, to have the update 478 // consistent with the scroll above. 479 ::std::vector< ::basegfx::B2DRange > aUncoveredAreas; 480 ::basegfx::computeSetDifference( aUncoveredAreas, 481 rUpdateArea.maTotalBounds, 482 ::basegfx::B2DRange( rDestRect ) ); 483 ::std::for_each( aUncoveredAreas.begin(), 484 aUncoveredAreas.end(), 485 ::boost::bind( &repaintBackground, 486 ::boost::ref(rOutDev), 487 ::boost::ref(rBackOutDev), 488 _1 ) ); 489 } 490 opaqueUpdate(const::basegfx::B2DRange & rTotalArea,const::std::vector<::canvas::Sprite::Reference> & rSortedUpdateSprites)491 void SpriteCanvasHelper::opaqueUpdate( const ::basegfx::B2DRange& rTotalArea, 492 const ::std::vector< ::canvas::Sprite::Reference >& rSortedUpdateSprites ) 493 { 494 (void)rTotalArea; 495 496 ENSURE_OR_THROW( mpOwningSpriteCanvas && 497 mpOwningSpriteCanvas->getBackBuffer() && 498 mpOwningSpriteCanvas->getFrontBuffer(), 499 "SpriteCanvasHelper::opaqueUpdate(): NULL device pointer " ); 500 501 OutputDevice& rOutDev( mpOwningSpriteCanvas->getFrontBuffer()->getOutDev() ); 502 503 // no need to clip output to actual update region - there will 504 // always be ALL sprites contained in the rectangular update 505 // area containd in rTotalArea (that's the way 506 // B2DConnectedRanges work). If rTotalArea appears to be 507 // smaller than the sprite - then this sprite carries a clip, 508 // and the update will be constrained to that rect. 509 510 // repaint all affected sprites directly to output device 511 ::std::for_each( rSortedUpdateSprites.begin(), 512 rSortedUpdateSprites.end(), 513 ::boost::bind( 514 &spriteRedrawStub, 515 ::boost::ref( rOutDev ), 516 _1 ) ); 517 } 518 genericUpdate(const::basegfx::B2DRange & rRequestedArea,const::std::vector<::canvas::Sprite::Reference> & rSortedUpdateSprites)519 void SpriteCanvasHelper::genericUpdate( const ::basegfx::B2DRange& rRequestedArea, 520 const ::std::vector< ::canvas::Sprite::Reference >& rSortedUpdateSprites ) 521 { 522 ENSURE_OR_THROW( mpOwningSpriteCanvas && 523 mpOwningSpriteCanvas->getBackBuffer() && 524 mpOwningSpriteCanvas->getFrontBuffer(), 525 "SpriteCanvasHelper::genericUpdate(): NULL device pointer " ); 526 527 OutputDevice& rOutDev( mpOwningSpriteCanvas->getFrontBuffer()->getOutDev() ); 528 BackBufferSharedPtr pBackBuffer( mpOwningSpriteCanvas->getBackBuffer() ); 529 OutputDevice& rBackOutDev( pBackBuffer->getOutDev() ); 530 531 // limit size of update VDev to target outdev's size 532 const Size& rTargetSizePixel( rOutDev.GetOutputSizePixel() ); 533 534 // round output position towards zero. Don't want to truncate 535 // a fraction of a sprite pixel... Clip position at origin, 536 // otherwise, truncation of size below might leave visible 537 // areas uncovered by VDev. 538 const ::Point aOutputPosition( 539 ::std::max( sal_Int32( 0 ), 540 static_cast< sal_Int32 >(rRequestedArea.getMinX()) ), 541 ::std::max( sal_Int32( 0 ), 542 static_cast< sal_Int32 >(rRequestedArea.getMinY()) ) ); 543 // round output size towards +infty. Don't want to truncate a 544 // fraction of a sprite pixel... Limit coverage of VDev to 545 // output device's area (i.e. not only to total size, but to 546 // cover _only_ the visible parts). 547 const ::Size aOutputSize( 548 ::std::max( sal_Int32( 0 ), 549 ::std::min( static_cast< sal_Int32 >(rTargetSizePixel.Width() - aOutputPosition.X()), 550 ::canvas::tools::roundUp( rRequestedArea.getMaxX() - aOutputPosition.X() ))), 551 ::std::max( sal_Int32( 0 ), 552 ::std::min( static_cast< sal_Int32 >(rTargetSizePixel.Height() - aOutputPosition.Y()), 553 ::canvas::tools::roundUp( rRequestedArea.getMaxY() - aOutputPosition.Y() )))); 554 555 // early exit for empty output area. 556 if( aOutputSize.Width() == 0 && 557 aOutputSize.Height() == 0 ) 558 { 559 return; 560 } 561 562 const Point aEmptyPoint(0,0); 563 const Size aCurrOutputSize( maVDev->GetOutputSizePixel() ); 564 565 // adapt maVDev's size to the area that actually needs the 566 // repaint. 567 if( aCurrOutputSize.Width() < aOutputSize.Width() || 568 aCurrOutputSize.Height() < aOutputSize.Height() ) 569 { 570 // TODO(P1): Come up with a clever tactic to reduce maVDev 571 // from time to time. Reduction with threshold (say, if 572 // maVDev is more than twice too large) is not wise, as 573 // this might then toggle within the same updateScreen(), 574 // but for different disjunct sprite areas. 575 maVDev->SetOutputSizePixel( aOutputSize ); 576 } 577 578 // paint background 579 maVDev->EnableMapMode( sal_False ); 580 maVDev->SetAntialiasing( ANTIALIASING_ENABLE_B2DDRAW ); 581 maVDev->SetClipRegion(); 582 maVDev->DrawOutDev( aEmptyPoint, aOutputSize, 583 aOutputPosition, aOutputSize, 584 rBackOutDev ); 585 586 // repaint all affected sprites on top of background into 587 // VDev. 588 ::basegfx::B2DPoint aPoint = ::vcl::unotools::b2DPointFromPoint(aOutputPosition); 589 ::std::for_each( rSortedUpdateSprites.begin(), 590 rSortedUpdateSprites.end(), 591 ::boost::bind( &spriteRedrawStub2, 592 ::boost::ref( maVDev.get() ), 593 ::boost::cref( aPoint ), 594 _1 ) ); 595 596 // flush to screen 597 rOutDev.EnableMapMode( sal_False ); 598 rOutDev.SetAntialiasing( ANTIALIASING_ENABLE_B2DDRAW ); 599 rOutDev.DrawOutDev( aOutputPosition, aOutputSize, 600 aEmptyPoint, aOutputSize, 601 *maVDev ); 602 } 603 renderFrameCounter(OutputDevice & rOutDev)604 void SpriteCanvasHelper::renderFrameCounter( OutputDevice& rOutDev ) 605 { 606 const double denominator( maLastUpdate.getElapsedTime() ); 607 maLastUpdate.reset(); 608 609 ::rtl::OUString text( ::rtl::math::doubleToUString( denominator == 0.0 ? 100.0 : 1.0/denominator, 610 rtl_math_StringFormat_F, 611 2,'.',NULL,' ') ); 612 613 // pad with leading space 614 while( text.getLength() < 6 ) 615 text = ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM (" ")) + text; 616 617 text += ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM (" fps")); 618 619 renderInfoText( rOutDev, 620 text, 621 Point(0, 0) ); 622 } 623 624 namespace 625 { 626 template< typename T > struct Adder 627 { 628 typedef void result_type; 629 Addervclcanvas::__anonfc3bd9610211::Adder630 Adder( T& rAdderTarget, 631 T nIncrement ) : 632 mpTarget( &rAdderTarget ), 633 mnIncrement( nIncrement ) 634 { 635 } 636 operator ()vclcanvas::__anonfc3bd9610211::Adder637 void operator()() { *mpTarget += mnIncrement; } operator ()vclcanvas::__anonfc3bd9610211::Adder638 void operator()( const ::canvas::Sprite::Reference& ) { *mpTarget += mnIncrement; } operator ()vclcanvas::__anonfc3bd9610211::Adder639 void operator()( T nIncrement ) { *mpTarget += nIncrement; } 640 641 T* mpTarget; 642 T mnIncrement; 643 }; 644 makeAdder(T & rAdderTarget,T nIncrement)645 template< typename T> Adder<T> makeAdder( T& rAdderTarget, 646 T nIncrement ) 647 { 648 return Adder<T>(rAdderTarget, nIncrement); 649 } 650 } 651 renderSpriteCount(OutputDevice & rOutDev)652 void SpriteCanvasHelper::renderSpriteCount( OutputDevice& rOutDev ) 653 { 654 if( mpRedrawManager ) 655 { 656 sal_Int32 nCount(0); 657 658 mpRedrawManager->forEachSprite( makeAdder(nCount,sal_Int32(1)) ); 659 ::rtl::OUString text( 660 ::rtl::OUString::valueOf( 661 // disambiguate overload... 662 static_cast<sal_Int64>(nCount) ) ); 663 664 // pad with leading space 665 while( text.getLength() < 3 ) 666 text = ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM (" ")) + text; 667 668 text = ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM ("Sprites: ")) + text; 669 670 renderInfoText( rOutDev, 671 text, 672 Point(0, 30) ); 673 } 674 } 675 renderMemUsage(OutputDevice & rOutDev)676 void SpriteCanvasHelper::renderMemUsage( OutputDevice& rOutDev ) 677 { 678 BackBufferSharedPtr pBackBuffer( mpOwningSpriteCanvas->getBackBuffer() ); 679 680 if( mpRedrawManager && 681 pBackBuffer ) 682 { 683 double nPixel(0.0); 684 685 // accumulate pixel count for each sprite into fCount 686 mpRedrawManager->forEachSprite( ::boost::bind( 687 makeAdder(nPixel,1.0), 688 ::boost::bind( 689 &calcNumPixel, 690 _1 ) ) ); 691 692 static const int NUM_VIRDEV(2); 693 static const int BYTES_PER_PIXEL(3); 694 695 const Size& rVDevSize( maVDev->GetOutputSizePixel() ); 696 const Size& rBackBufferSize( pBackBuffer->getOutDev().GetOutputSizePixel() ); 697 698 const double nMemUsage( nPixel * NUM_VIRDEV * BYTES_PER_PIXEL + 699 rVDevSize.Width()*rVDevSize.Height() * BYTES_PER_PIXEL + 700 rBackBufferSize.Width()*rBackBufferSize.Height() * BYTES_PER_PIXEL ); 701 702 ::rtl::OUString text( ::rtl::math::doubleToUString( nMemUsage / 1048576.0, 703 rtl_math_StringFormat_F, 704 2,'.',NULL,' ') ); 705 706 // pad with leading space 707 while( text.getLength() < 4 ) 708 text = ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM (" ")) + text; 709 710 text = ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM ("Mem: ")) + 711 text + 712 ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM ("MB")); 713 714 renderInfoText( rOutDev, 715 text, 716 Point(0, 60) ); 717 } 718 } 719 } 720