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