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 worthwile 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 ::std::for_each( aUnscrollableAreas.begin(), 467 aUnscrollableAreas.end(), 468 ::boost::bind( &opaqueUpdateSpriteArea, 469 ::boost::cref(aFirst->second.getSprite()), 470 ::boost::ref(rOutDev), 471 _1 ) ); 472 rOutDev.Pop(); 473 } 474 475 // repaint uncovered areas from backbuffer - take the 476 // _rounded_ rectangles from above, to have the update 477 // consistent with the scroll above. 478 ::std::vector< ::basegfx::B2DRange > aUncoveredAreas; 479 ::basegfx::computeSetDifference( aUncoveredAreas, 480 rUpdateArea.maTotalBounds, 481 ::basegfx::B2DRange( rDestRect ) ); 482 ::std::for_each( aUncoveredAreas.begin(), 483 aUncoveredAreas.end(), 484 ::boost::bind( &repaintBackground, 485 ::boost::ref(rOutDev), 486 ::boost::ref(rBackOutDev), 487 _1 ) ); 488 } 489 opaqueUpdate(const::basegfx::B2DRange & rTotalArea,const::std::vector<::canvas::Sprite::Reference> & rSortedUpdateSprites)490 void SpriteCanvasHelper::opaqueUpdate( const ::basegfx::B2DRange& rTotalArea, 491 const ::std::vector< ::canvas::Sprite::Reference >& rSortedUpdateSprites ) 492 { 493 (void)rTotalArea; 494 495 ENSURE_OR_THROW( mpOwningSpriteCanvas && 496 mpOwningSpriteCanvas->getBackBuffer() && 497 mpOwningSpriteCanvas->getFrontBuffer(), 498 "SpriteCanvasHelper::opaqueUpdate(): NULL device pointer " ); 499 500 OutputDevice& rOutDev( mpOwningSpriteCanvas->getFrontBuffer()->getOutDev() ); 501 502 // no need to clip output to actual update region - there will 503 // always be ALL sprites contained in the rectangular update 504 // area containd in rTotalArea (that's the way 505 // B2DConnectedRanges work). If rTotalArea appears to be 506 // smaller than the sprite - then this sprite carries a clip, 507 // and the update will be constrained to that rect. 508 509 // repaint all affected sprites directly to output device 510 ::std::for_each( rSortedUpdateSprites.begin(), 511 rSortedUpdateSprites.end(), 512 ::boost::bind( 513 &spriteRedrawStub, 514 ::boost::ref( rOutDev ), 515 _1 ) ); 516 } 517 genericUpdate(const::basegfx::B2DRange & rRequestedArea,const::std::vector<::canvas::Sprite::Reference> & rSortedUpdateSprites)518 void SpriteCanvasHelper::genericUpdate( const ::basegfx::B2DRange& rRequestedArea, 519 const ::std::vector< ::canvas::Sprite::Reference >& rSortedUpdateSprites ) 520 { 521 ENSURE_OR_THROW( mpOwningSpriteCanvas && 522 mpOwningSpriteCanvas->getBackBuffer() && 523 mpOwningSpriteCanvas->getFrontBuffer(), 524 "SpriteCanvasHelper::genericUpdate(): NULL device pointer " ); 525 526 OutputDevice& rOutDev( mpOwningSpriteCanvas->getFrontBuffer()->getOutDev() ); 527 BackBufferSharedPtr pBackBuffer( mpOwningSpriteCanvas->getBackBuffer() ); 528 OutputDevice& rBackOutDev( pBackBuffer->getOutDev() ); 529 530 // limit size of update VDev to target outdev's size 531 const Size& rTargetSizePixel( rOutDev.GetOutputSizePixel() ); 532 533 // round output position towards zero. Don't want to truncate 534 // a fraction of a sprite pixel... Clip position at origin, 535 // otherwise, truncation of size below might leave visible 536 // areas uncovered by VDev. 537 const ::Point aOutputPosition( 538 ::std::max( sal_Int32( 0 ), 539 static_cast< sal_Int32 >(rRequestedArea.getMinX()) ), 540 ::std::max( sal_Int32( 0 ), 541 static_cast< sal_Int32 >(rRequestedArea.getMinY()) ) ); 542 // round output size towards +infty. Don't want to truncate a 543 // fraction of a sprite pixel... Limit coverage of VDev to 544 // output device's area (i.e. not only to total size, but to 545 // cover _only_ the visible parts). 546 const ::Size aOutputSize( 547 ::std::max( sal_Int32( 0 ), 548 ::std::min( static_cast< sal_Int32 >(rTargetSizePixel.Width() - aOutputPosition.X()), 549 ::canvas::tools::roundUp( rRequestedArea.getMaxX() - aOutputPosition.X() ))), 550 ::std::max( sal_Int32( 0 ), 551 ::std::min( static_cast< sal_Int32 >(rTargetSizePixel.Height() - aOutputPosition.Y()), 552 ::canvas::tools::roundUp( rRequestedArea.getMaxY() - aOutputPosition.Y() )))); 553 554 // early exit for empty output area. 555 if( aOutputSize.Width() == 0 && 556 aOutputSize.Height() == 0 ) 557 { 558 return; 559 } 560 561 const Point aEmptyPoint(0,0); 562 const Size aCurrOutputSize( maVDev->GetOutputSizePixel() ); 563 564 // adapt maVDev's size to the area that actually needs the 565 // repaint. 566 if( aCurrOutputSize.Width() < aOutputSize.Width() || 567 aCurrOutputSize.Height() < aOutputSize.Height() ) 568 { 569 // TODO(P1): Come up with a clever tactic to reduce maVDev 570 // from time to time. Reduction with threshold (say, if 571 // maVDev is more than twice too large) is not wise, as 572 // this might then toggle within the same updateScreen(), 573 // but for different disjunct sprite areas. 574 maVDev->SetOutputSizePixel( aOutputSize ); 575 } 576 577 // paint background 578 maVDev->EnableMapMode( sal_False ); 579 maVDev->SetAntialiasing( ANTIALIASING_ENABLE_B2DDRAW ); 580 maVDev->SetClipRegion(); 581 maVDev->DrawOutDev( aEmptyPoint, aOutputSize, 582 aOutputPosition, aOutputSize, 583 rBackOutDev ); 584 585 // repaint all affected sprites on top of background into 586 // VDev. 587 ::std::for_each( rSortedUpdateSprites.begin(), 588 rSortedUpdateSprites.end(), 589 ::boost::bind( &spriteRedrawStub2, 590 ::boost::ref( maVDev.get() ), 591 ::boost::cref( 592 ::vcl::unotools::b2DPointFromPoint(aOutputPosition)), 593 _1 ) ); 594 595 // flush to screen 596 rOutDev.EnableMapMode( sal_False ); 597 rOutDev.SetAntialiasing( ANTIALIASING_ENABLE_B2DDRAW ); 598 rOutDev.DrawOutDev( aOutputPosition, aOutputSize, 599 aEmptyPoint, aOutputSize, 600 *maVDev ); 601 } 602 renderFrameCounter(OutputDevice & rOutDev)603 void SpriteCanvasHelper::renderFrameCounter( OutputDevice& rOutDev ) 604 { 605 const double denominator( maLastUpdate.getElapsedTime() ); 606 maLastUpdate.reset(); 607 608 ::rtl::OUString text( ::rtl::math::doubleToUString( denominator == 0.0 ? 100.0 : 1.0/denominator, 609 rtl_math_StringFormat_F, 610 2,'.',NULL,' ') ); 611 612 // pad with leading space 613 while( text.getLength() < 6 ) 614 text = ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM (" ")) + text; 615 616 text += ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM (" fps")); 617 618 renderInfoText( rOutDev, 619 text, 620 Point(0, 0) ); 621 } 622 623 namespace 624 { 625 template< typename T > struct Adder 626 { 627 typedef void result_type; 628 Addervclcanvas::__anonb5a613490211::Adder629 Adder( T& rAdderTarget, 630 T nIncrement ) : 631 mpTarget( &rAdderTarget ), 632 mnIncrement( nIncrement ) 633 { 634 } 635 operator ()vclcanvas::__anonb5a613490211::Adder636 void operator()() { *mpTarget += mnIncrement; } operator ()vclcanvas::__anonb5a613490211::Adder637 void operator()( const ::canvas::Sprite::Reference& ) { *mpTarget += mnIncrement; } operator ()vclcanvas::__anonb5a613490211::Adder638 void operator()( T nIncrement ) { *mpTarget += nIncrement; } 639 640 T* mpTarget; 641 T mnIncrement; 642 }; 643 makeAdder(T & rAdderTarget,T nIncrement)644 template< typename T> Adder<T> makeAdder( T& rAdderTarget, 645 T nIncrement ) 646 { 647 return Adder<T>(rAdderTarget, nIncrement); 648 } 649 } 650 renderSpriteCount(OutputDevice & rOutDev)651 void SpriteCanvasHelper::renderSpriteCount( OutputDevice& rOutDev ) 652 { 653 if( mpRedrawManager ) 654 { 655 sal_Int32 nCount(0); 656 657 mpRedrawManager->forEachSprite( makeAdder(nCount,sal_Int32(1)) ); 658 ::rtl::OUString text( 659 ::rtl::OUString::valueOf( 660 // disambiguate overload... 661 static_cast<sal_Int64>(nCount) ) ); 662 663 // pad with leading space 664 while( text.getLength() < 3 ) 665 text = ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM (" ")) + text; 666 667 text = ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM ("Sprites: ")) + text; 668 669 renderInfoText( rOutDev, 670 text, 671 Point(0, 30) ); 672 } 673 } 674 renderMemUsage(OutputDevice & rOutDev)675 void SpriteCanvasHelper::renderMemUsage( OutputDevice& rOutDev ) 676 { 677 BackBufferSharedPtr pBackBuffer( mpOwningSpriteCanvas->getBackBuffer() ); 678 679 if( mpRedrawManager && 680 pBackBuffer ) 681 { 682 double nPixel(0.0); 683 684 // accumulate pixel count for each sprite into fCount 685 mpRedrawManager->forEachSprite( ::boost::bind( 686 makeAdder(nPixel,1.0), 687 ::boost::bind( 688 &calcNumPixel, 689 _1 ) ) ); 690 691 static const int NUM_VIRDEV(2); 692 static const int BYTES_PER_PIXEL(3); 693 694 const Size& rVDevSize( maVDev->GetOutputSizePixel() ); 695 const Size& rBackBufferSize( pBackBuffer->getOutDev().GetOutputSizePixel() ); 696 697 const double nMemUsage( nPixel * NUM_VIRDEV * BYTES_PER_PIXEL + 698 rVDevSize.Width()*rVDevSize.Height() * BYTES_PER_PIXEL + 699 rBackBufferSize.Width()*rBackBufferSize.Height() * BYTES_PER_PIXEL ); 700 701 ::rtl::OUString text( ::rtl::math::doubleToUString( nMemUsage / 1048576.0, 702 rtl_math_StringFormat_F, 703 2,'.',NULL,' ') ); 704 705 // pad with leading space 706 while( text.getLength() < 4 ) 707 text = ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM (" ")) + text; 708 709 text = ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM ("Mem: ")) + 710 text + 711 ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM ("MB")); 712 713 renderInfoText( rOutDev, 714 text, 715 Point(0, 60) ); 716 } 717 } 718 } 719