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