spritecanvashelper.cxx 26.5 KB
Newer Older
1
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */
19

20
#include <sal/config.h>
21
#include <sal/log.hxx>
22

23
#include <boost/cast.hpp>
24

25
#include <basegfx/range/b2drectangle.hxx>
26
#include <basegfx/utils/canvastools.hxx>
27 28
#include <tools/diagnose_ex.h>
#include <vcl/bitmapex.hxx>
29 30 31 32
#include <vcl/canvastools.hxx>
#include <vcl/outdev.hxx>
#include <vcl/window.hxx>

33
#include <canvas/canvastools.hxx>
34 35

#include "canvascustomsprite.hxx"
36
#include "spritecanvashelper.hxx"
37 38 39

using namespace ::com::sun::star;

40
#define FPS_BOUNDS ::tools::Rectangle(0,0,130,90)
41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
#define INFO_COLOR COL_RED

namespace vclcanvas
{
    namespace
    {
        /** Sprite redraw at original position

            Used to repaint the whole canvas (background and all
            sprites)
         */
        void spriteRedraw( OutputDevice&                      rOutDev,
                           const ::canvas::Sprite::Reference& rSprite )
        {
            // downcast to derived vclcanvas::Sprite interface, which
            // provides the actual redraw methods.
            ::boost::polymorphic_downcast< Sprite* >(rSprite.get())->redraw(rOutDev,
                                                                            true);
        }

        double calcNumPixel( const ::canvas::Sprite::Reference& rSprite )
        {
            const ::basegfx::B2DSize& rSize(
                ::boost::polymorphic_downcast< Sprite* >(rSprite.get())->getSizePixel() );

            return rSize.getX() * rSize.getY();
        }

        void repaintBackground( OutputDevice&               rOutDev,
70
                                OutputDevice const &        rBackBuffer,
71 72
                                const ::basegfx::B2DRange&  rArea )
        {
73 74
            const ::Point& rPos( vcl::unotools::pointFromB2DPoint( rArea.getMinimum()) );
            const ::Size& rSize( vcl::unotools::sizeFromB2DSize( rArea.getRange()) );
75 76 77 78 79 80 81 82

            rOutDev.DrawOutDev( rPos, rSize, rPos, rSize, rBackBuffer );
        }

        void opaqueUpdateSpriteArea( const ::canvas::Sprite::Reference& rSprite,
                                     OutputDevice&                      rOutDev,
                                     const ::basegfx::B2IRange&         rArea )
        {
83
            const ::tools::Rectangle& rRequestedArea(
84
                vcl::unotools::rectangleFromB2IRectangle( rArea ) );
85 86 87 88

            // clip output to actual update region (otherwise a)
            // wouldn't save much render time, and b) will clutter
            // scrolled sprite content outside this area)
Stephan Bergmann's avatar
Stephan Bergmann committed
89
            rOutDev.EnableMapMode( false );
90
            rOutDev.SetAntialiasing( AntialiasingFlags::EnableB2dDraw );
Noel Grandin's avatar
Noel Grandin committed
91
            rOutDev.SetClipRegion(vcl::Region(rRequestedArea));
92 93 94 95 96 97 98 99 100 101 102

            // repaint affected sprite directly to output device (at
            // the actual screen output position)
            ::boost::polymorphic_downcast< Sprite* >(
                rSprite.get() )->redraw( rOutDev,
                                         false ); // rendering
                                                  // directly to
                                                  // frontbuffer
        }

        void renderInfoText( OutputDevice&          rOutDev,
103
                             const OUString& rStr,
104 105
                             const Point&           rPos )
        {
Noel Grandin's avatar
Noel Grandin committed
106
            vcl::Font aVCLFont;
107
            aVCLFont.SetFontHeight( 20 );
108
            aVCLFont.SetColor( INFO_COLOR );
109

110
            rOutDev.SetTextAlign(ALIGN_TOP);
111
            rOutDev.SetTextColor( INFO_COLOR );
112 113 114 115 116 117 118 119
            rOutDev.SetFont( aVCLFont );

            rOutDev.DrawText( rPos, rStr );
        }

    }

    SpriteCanvasHelper::SpriteCanvasHelper() :
120 121
        mpRedrawManager( nullptr ),
        mpOwningSpriteCanvas( nullptr ),
122
        maVDev(VclPtr<VirtualDevice>::Create()),
123 124 125 126 127
        maLastUpdate(),
        mbShowFrameInfo( false ),
        mbShowSpriteBounds( false ),
        mbIsUnsafeScrolling( false )
    {
128
#if OSL_DEBUG_LEVEL > 0
129 130 131 132 133
        // inverse defaults for verbose debug mode
        mbShowSpriteBounds = mbShowFrameInfo = true;
#endif
    }

134 135 136 137 138 139
    SpriteCanvasHelper::~SpriteCanvasHelper()
    {
        SolarMutexGuard aGuard;
        maVDev.disposeAndClear();
    }

140 141 142 143 144
    void SpriteCanvasHelper::init( const OutDevProviderSharedPtr& rOutDev,
                                   SpriteCanvas&                  rOwningSpriteCanvas,
                                   ::canvas::SpriteRedrawManager& rManager,
                                   bool                           bProtect,
                                   bool                           bHaveAlpha )
145
    {
146
        mpOwningSpriteCanvas = &rOwningSpriteCanvas;
147
        mpRedrawManager = &rManager;
148 149

        CanvasHelper::init(rOwningSpriteCanvas,rOutDev,bProtect,bHaveAlpha);
150 151 152 153
    }

    void SpriteCanvasHelper::disposing()
    {
154 155
        mpRedrawManager = nullptr;
        mpOwningSpriteCanvas = nullptr;
156 157 158 159 160 161

        // forward to base
        CanvasHelper::disposing();
    }

    uno::Reference< rendering::XAnimatedSprite > SpriteCanvasHelper::createSpriteFromAnimation(
162
        const uno::Reference< rendering::XAnimation >&  )
163 164 165 166 167
    {
        return uno::Reference< rendering::XAnimatedSprite >();
    }

    uno::Reference< rendering::XAnimatedSprite > SpriteCanvasHelper::createSpriteFromBitmaps(
168 169
        const uno::Sequence< uno::Reference< rendering::XBitmap > >& ,
        sal_Int8                                                      )
170 171 172 173 174 175
    {
        return uno::Reference< rendering::XAnimatedSprite >();
    }

    uno::Reference< rendering::XCustomSprite > SpriteCanvasHelper::createCustomSprite( const geometry::RealSize2D& spriteSize )
    {
176
        if( !mpRedrawManager || !mpDevice )
177 178 179 180
            return uno::Reference< rendering::XCustomSprite >(); // we're disposed

        return uno::Reference< rendering::XCustomSprite >(
            new CanvasCustomSprite( spriteSize,
181 182 183
                                    *mpDevice,
                                    mpOwningSpriteCanvas,
                                    mpOwningSpriteCanvas->getFrontBuffer(),
184 185 186
                                    mbShowSpriteBounds ) );
    }

187
    uno::Reference< rendering::XSprite > SpriteCanvasHelper::createClonedSprite( const uno::Reference< rendering::XSprite >&  )
188 189 190 191
    {
        return uno::Reference< rendering::XSprite >();
    }

Noel Grandin's avatar
Noel Grandin committed
192 193
    bool SpriteCanvasHelper::updateScreen( bool bUpdateAll,
                                           bool&    io_bSurfaceDirty )
194 195
    {
        if( !mpRedrawManager ||
196 197 198
            !mpOwningSpriteCanvas ||
            !mpOwningSpriteCanvas->getFrontBuffer() ||
            !mpOwningSpriteCanvas->getBackBuffer() )
199
        {
Noel Grandin's avatar
Noel Grandin committed
200
            return false; // disposed, or otherwise dysfunctional
201 202 203 204 205
        }

        // commit to backbuffer
        flush();

206 207
        OutputDevice&       rOutDev( mpOwningSpriteCanvas->getFrontBuffer()->getOutDev() );
        BackBufferSharedPtr pBackBuffer( mpOwningSpriteCanvas->getBackBuffer() );
208 209 210 211 212 213 214 215 216
        OutputDevice&       rBackOutDev( pBackBuffer->getOutDev() );

        // actual OutputDevice is a shared resource - restore its
        // state when done.
        tools::OutDevStateKeeper aStateKeeper( rOutDev );

        const Size  aOutDevSize( rBackOutDev.GetOutputSizePixel() );
        const Point aEmptyPoint(0,0);

217
        vcl::Window* pTargetWindow = nullptr;
218 219
        if( rOutDev.GetOutDevType() == OUTDEV_WINDOW )
        {
Noel Grandin's avatar
Noel Grandin committed
220
            pTargetWindow = &static_cast<vcl::Window&>(rOutDev); // TODO(Q3): Evil downcast.
221 222 223 224 225

            // we're double-buffered, thus no need for paint area-limiting
            // clips. besides that, will interfere with animations (as for
            // Window-invalidate repaints, only parts of the window will
            // be redrawn otherwise)
226
            const vcl::Region aFullWindowRegion( ::tools::Rectangle(aEmptyPoint,
227 228 229 230
                                                      aOutDevSize) );
            pTargetWindow->ExpandPaintClipRegion(aFullWindowRegion);
        }

Andrea Gelmini's avatar
Andrea Gelmini committed
231
        // TODO(P1): Might be worthwhile to track areas of background
232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258
        // changes, too.
        if( !bUpdateAll && !io_bSurfaceDirty )
        {
            if( mbShowFrameInfo )
            {
                // also repaint background below frame counter (fake
                // that as a sprite vanishing in this area)
                mpRedrawManager->updateSprite( ::canvas::Sprite::Reference(),
                                               ::basegfx::B2DPoint(),
                                               ::basegfx::B2DRectangle( 0.0, 0.0,
                                                                        FPS_BOUNDS.Right(),
                                                                        FPS_BOUNDS.Bottom() ) );
            }

            // background has not changed, so we're free to optimize
            // repaint to areas where a sprite has changed

            // process each independent area of overlapping sprites
            // separately.
            mpRedrawManager->forEachSpriteArea( *this );
        }
        else
        {
            // background has changed, so we currently have no choice
            // but repaint everything (or caller requested that)

            maVDev->SetOutputSizePixel( aOutDevSize );
Stephan Bergmann's avatar
Stephan Bergmann committed
259
            maVDev->EnableMapMode( false );
260 261 262 263 264 265
            maVDev->DrawOutDev( aEmptyPoint, aOutDevSize,
                                aEmptyPoint, aOutDevSize,
                                rBackOutDev );

            // repaint all active sprites on top of background into
            // VDev.
266
            OutputDevice& rTmpOutDev( *maVDev );
267
            mpRedrawManager->forEachSprite(
268 269 270
                    [&rTmpOutDev]( const ::canvas::Sprite::Reference& rSprite )
                    { spriteRedraw( rTmpOutDev, rSprite ); }
                    );
271 272

            // flush to screen
Stephan Bergmann's avatar
Stephan Bergmann committed
273
            rOutDev.EnableMapMode( false );
274
            rOutDev.SetAntialiasing( AntialiasingFlags::EnableB2dDraw );
275
            rOutDev.SetClipRegion();
276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293
            rOutDev.DrawOutDev( aEmptyPoint, aOutDevSize,
                                aEmptyPoint, aOutDevSize,
                                *maVDev );
        }

        // change record vector must be cleared, for the next turn of
        // rendering and sprite changing
        mpRedrawManager->clearChangeRecords();

        io_bSurfaceDirty = false;

        if( mbShowFrameInfo )
        {
            renderFrameCounter( rOutDev );
            renderSpriteCount( rOutDev );
            renderMemUsage( rOutDev );
        }

294
#if OSL_DEBUG_LEVEL > 0
295 296 297
        static ::canvas::tools::ElapsedTime aElapsedTime;

        // log time immediately after surface flip
298
        SAL_INFO("canvas.vcl", "SpriteCanvasHelper::updateScreen(): flip done at " <<
299 300 301
                   aElapsedTime.getElapsedTime() );
#endif

302 303 304 305
        // sync output with screen, to ensure that we don't queue up
        // render requests (calling code might rely on timing,
        // i.e. assume that things are visible on screen after
        // updateScreen() returns).
306
        if( pTargetWindow )
307
        {
308
            // commit to screen
309
            pTargetWindow->Flush();
310 311
        }

Noel Grandin's avatar
Noel Grandin committed
312
        return true;
313 314 315 316
    }

    void SpriteCanvasHelper::backgroundPaint( const ::basegfx::B2DRange& rUpdateRect )
    {
317 318 319 320
        ENSURE_OR_THROW( mpOwningSpriteCanvas &&
                         mpOwningSpriteCanvas->getBackBuffer() &&
                         mpOwningSpriteCanvas->getFrontBuffer(),
                         "SpriteCanvasHelper::backgroundPaint(): NULL device pointer " );
321

322 323
        OutputDevice&       rOutDev( mpOwningSpriteCanvas->getFrontBuffer()->getOutDev() );
        BackBufferSharedPtr pBackBuffer( mpOwningSpriteCanvas->getBackBuffer() );
324 325 326 327 328 329 330 331 332
        OutputDevice&       rBackOutDev( pBackBuffer->getOutDev() );

        repaintBackground( rOutDev, rBackOutDev, rUpdateRect );
    }

    void SpriteCanvasHelper::scrollUpdate( const ::basegfx::B2DRange&                       rMoveStart,
                                           const ::basegfx::B2DRange&                       rMoveEnd,
                                           const ::canvas::SpriteRedrawManager::UpdateArea& rUpdateArea )
    {
333 334 335 336
        ENSURE_OR_THROW( mpOwningSpriteCanvas &&
                         mpOwningSpriteCanvas->getBackBuffer() &&
                         mpOwningSpriteCanvas->getFrontBuffer(),
                         "SpriteCanvasHelper::scrollUpdate(): NULL device pointer " );
337

338 339
        OutputDevice&       rOutDev( mpOwningSpriteCanvas->getFrontBuffer()->getOutDev() );
        BackBufferSharedPtr pBackBuffer( mpOwningSpriteCanvas->getBackBuffer() );
340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357
        OutputDevice&       rBackOutDev( pBackBuffer->getOutDev() );

        const Size&                rTargetSizePixel( rOutDev.GetOutputSizePixel() );
        const ::basegfx::B2IRange  aOutputBounds( 0,0,
                                                  rTargetSizePixel.Width(),
                                                  rTargetSizePixel.Height() );

        // round rectangles to integer pixel. Note: have to be
        // extremely careful here, to avoid off-by-one errors for
        // the destination area: otherwise, the next scroll update
        // would copy pixel that are not supposed to be part of
        // the sprite.
        ::basegfx::B2IRange aSourceRect(
            ::canvas::tools::spritePixelAreaFromB2DRange( rMoveStart ) );
        const ::basegfx::B2IRange& rDestRect(
            ::canvas::tools::spritePixelAreaFromB2DRange( rMoveEnd ) );
        ::basegfx::B2IPoint aDestPos( rDestRect.getMinimum() );

358
        std::vector< ::basegfx::B2IRange > aUnscrollableAreas;
359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378

        // Since strictly speaking, this scroll algorithm is plain
        // buggy, the scrolled area might actually lie _below_ another
        // window - we've made this feature configurable via
        // mbIsUnsafeScrolling.

        // clip to output bounds (cannot properly scroll stuff
        // _outside_ our screen area)
        if( !mbIsUnsafeScrolling ||
            !::canvas::tools::clipScrollArea( aSourceRect,
                                              aDestPos,
                                              aUnscrollableAreas,
                                              aOutputBounds ) )
        {
            // fully clipped scroll area: cannot simply scroll
            // then. Perform normal opaque update (can use that, since
            // one of the preconditions for scrollable update is
            // opaque sprite content)

            // repaint all affected sprites directly to output device
379
            for( const auto& rComponent : rUpdateArea.maComponentList )
380 381 382 383 384 385 386 387
            {
                const ::canvas::Sprite::Reference& rSprite( rComponent.second.getSprite() );

                if( rSprite.is() )
                    ::boost::polymorphic_downcast< Sprite* >(
                        rSprite.get() )->redraw( rOutDev,
                                                 false );
            }
388 389 390 391
        }
        else
        {
            // scroll rOutDev content
392 393
            rOutDev.CopyArea( vcl::unotools::pointFromB2IPoint( aDestPos ),
                              vcl::unotools::pointFromB2IPoint( aSourceRect.getMinimum() ),
394 395 396 397 398 399 400
                              // TODO(Q2): use numeric_cast to check range
                              ::Size( static_cast<sal_Int32>(aSourceRect.getRange().getX()),
                                      static_cast<sal_Int32>(aSourceRect.getRange().getY()) ) );

            const ::canvas::SpriteRedrawManager::SpriteConnectedRanges::ComponentListType::const_iterator
                aFirst( rUpdateArea.maComponentList.begin() );

401
            ENSURE_OR_THROW( aFirst->second.getSprite().is(),
402 403 404 405 406
                              "VCLCanvas::scrollUpdate(): no sprite" );

            // repaint uncovered areas from sprite. Need to actually
            // clip here, since we're only repainting _parts_ of the
            // sprite
407
            rOutDev.Push( PushFlags::CLIPREGION );
408 409 410 411 412

            for( const auto& rArea : aUnscrollableAreas )
                opaqueUpdateSpriteArea( aFirst->second.getSprite(),
                                        rOutDev, rArea );

413 414 415 416 417 418
            rOutDev.Pop();
        }

        // repaint uncovered areas from backbuffer - take the
        // _rounded_ rectangles from above, to have the update
        // consistent with the scroll above.
419
        std::vector< ::basegfx::B2DRange > aUncoveredAreas;
420 421 422
        ::basegfx::computeSetDifference( aUncoveredAreas,
                                         rUpdateArea.maTotalBounds,
                                         ::basegfx::B2DRange( rDestRect ) );
423 424 425

        for( const auto& rArea : aUncoveredAreas )
            repaintBackground( rOutDev, rBackOutDev, rArea );
426 427
    }

428
    void SpriteCanvasHelper::opaqueUpdate( SAL_UNUSED_PARAMETER const ::basegfx::B2DRange&,
429
                                           const std::vector< ::canvas::Sprite::Reference >& rSortedUpdateSprites )
430
    {
431 432 433 434
        ENSURE_OR_THROW( mpOwningSpriteCanvas &&
                         mpOwningSpriteCanvas->getBackBuffer() &&
                         mpOwningSpriteCanvas->getFrontBuffer(),
                         "SpriteCanvasHelper::opaqueUpdate(): NULL device pointer " );
435

436
        OutputDevice& rOutDev( mpOwningSpriteCanvas->getFrontBuffer()->getOutDev() );
437 438 439

        // no need to clip output to actual update region - there will
        // always be ALL sprites contained in the rectangular update
Andrea Gelmini's avatar
Andrea Gelmini committed
440
        // area contained in rTotalArea (that's the way
441 442 443 444 445
        // B2DConnectedRanges work). If rTotalArea appears to be
        // smaller than the sprite - then this sprite carries a clip,
        // and the update will be constrained to that rect.

        // repaint all affected sprites directly to output device
446 447 448 449 450 451 452
        for( const auto& rSprite : rSortedUpdateSprites )
        {
            if( rSprite.is() )
                ::boost::polymorphic_downcast< Sprite* >(
                    rSprite.get() )->redraw( rOutDev,
                                             false );
        }
453 454 455
    }

    void SpriteCanvasHelper::genericUpdate( const ::basegfx::B2DRange&                          rRequestedArea,
456
                                            const std::vector< ::canvas::Sprite::Reference >& rSortedUpdateSprites )
457
    {
458 459 460 461
        ENSURE_OR_THROW( mpOwningSpriteCanvas &&
                         mpOwningSpriteCanvas->getBackBuffer() &&
                         mpOwningSpriteCanvas->getFrontBuffer(),
                         "SpriteCanvasHelper::genericUpdate(): NULL device pointer " );
462

463 464
        OutputDevice&       rOutDev( mpOwningSpriteCanvas->getFrontBuffer()->getOutDev() );
        BackBufferSharedPtr pBackBuffer( mpOwningSpriteCanvas->getBackBuffer() );
465 466 467 468 469 470 471 472 473 474
        OutputDevice&       rBackOutDev( pBackBuffer->getOutDev() );

        // limit size of update VDev to target outdev's size
        const Size& rTargetSizePixel( rOutDev.GetOutputSizePixel() );

        // round output position towards zero. Don't want to truncate
        // a fraction of a sprite pixel...  Clip position at origin,
        // otherwise, truncation of size below might leave visible
        // areas uncovered by VDev.
        const ::Point aOutputPosition(
475
            std::max( sal_Int32( 0 ),
476
                        static_cast< sal_Int32 >(rRequestedArea.getMinX()) ),
477
            std::max( sal_Int32( 0 ),
478 479 480 481 482 483
                        static_cast< sal_Int32 >(rRequestedArea.getMinY()) ) );
        // round output size towards +infty. Don't want to truncate a
        // fraction of a sprite pixel... Limit coverage of VDev to
        // output device's area (i.e. not only to total size, but to
        // cover _only_ the visible parts).
        const ::Size aOutputSize(
484 485
            std::max( sal_Int32( 0 ),
                        std::min( static_cast< sal_Int32 >(rTargetSizePixel.Width() - aOutputPosition.X()),
486
                                    ::canvas::tools::roundUp( rRequestedArea.getMaxX() - aOutputPosition.X() ))),
487 488
            std::max( sal_Int32( 0 ),
                        std::min( static_cast< sal_Int32 >(rTargetSizePixel.Height() - aOutputPosition.Y()),
489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514
                                    ::canvas::tools::roundUp( rRequestedArea.getMaxY() - aOutputPosition.Y() ))));

        // early exit for empty output area.
        if( aOutputSize.Width() == 0 &&
            aOutputSize.Height() == 0 )
        {
            return;
        }

        const Point aEmptyPoint(0,0);
        const Size  aCurrOutputSize( maVDev->GetOutputSizePixel() );

        // adapt maVDev's size to the area that actually needs the
        // repaint.
        if( aCurrOutputSize.Width() < aOutputSize.Width() ||
            aCurrOutputSize.Height() < aOutputSize.Height() )
        {
            // TODO(P1): Come up with a clever tactic to reduce maVDev
            // from time to time. Reduction with threshold (say, if
            // maVDev is more than twice too large) is not wise, as
            // this might then toggle within the same updateScreen(),
            // but for different disjunct sprite areas.
            maVDev->SetOutputSizePixel( aOutputSize );
        }

        // paint background
Stephan Bergmann's avatar
Stephan Bergmann committed
515
        maVDev->EnableMapMode( false );
516
        maVDev->SetAntialiasing( AntialiasingFlags::EnableB2dDraw );
517 518 519 520 521 522 523
        maVDev->SetClipRegion();
        maVDev->DrawOutDev( aEmptyPoint, aOutputSize,
                            aOutputPosition, aOutputSize,
                            rBackOutDev );

        // repaint all affected sprites on top of background into
        // VDev.
524 525 526 527 528 529 530 531 532 533 534 535 536
        for( const auto& rSprite : rSortedUpdateSprites )
        {
            if( rSprite.is() )
            {
                Sprite* pSprite = ::boost::polymorphic_downcast< Sprite* >( rSprite.get() );

                // calc relative sprite position in rUpdateArea (which
                // need not be the whole screen!)
                const ::basegfx::B2DPoint& rSpriteScreenPos( pSprite->getPosPixel() );
                const ::basegfx::B2DPoint& rSpriteRenderPos(
                        rSpriteScreenPos - vcl::unotools::b2DPointFromPoint(aOutputPosition)
                        );

537
                pSprite->redraw( *maVDev, rSpriteRenderPos, true );
538 539
            }
        }
540 541

        // flush to screen
Stephan Bergmann's avatar
Stephan Bergmann committed
542
        rOutDev.EnableMapMode( false );
543
        rOutDev.SetAntialiasing( AntialiasingFlags::EnableB2dDraw );
544 545 546 547 548 549 550 551 552 553
        rOutDev.DrawOutDev( aOutputPosition, aOutputSize,
                            aEmptyPoint, aOutputSize,
                            *maVDev );
    }

    void SpriteCanvasHelper::renderFrameCounter( OutputDevice& rOutDev )
    {
        const double denominator( maLastUpdate.getElapsedTime() );
        maLastUpdate.reset();

554
        OUString text( ::rtl::math::doubleToUString( denominator == 0.0 ? 100.0 : 1.0/denominator,
555
                                                            rtl_math_StringFormat_F,
556
                                                            2,'.',nullptr,' ') );
557 558 559

        // pad with leading space
        while( text.getLength() < 6 )
560
            text = " " + text;
561

562
        text += " fps";
563 564 565 566 567 568

        renderInfoText( rOutDev,
                        text,
                        Point(0, 0) );
    }

569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595
    namespace
    {
        template< typename T > struct Adder
        {
            typedef void result_type;

            Adder( T& rAdderTarget,
                   T  nIncrement ) :
                mpTarget( &rAdderTarget ),
                mnIncrement( nIncrement )
            {
            }

            void operator()( const ::canvas::Sprite::Reference& ) { *mpTarget += mnIncrement; }
            void operator()( T nIncrement ) { *mpTarget += nIncrement; }

            T* mpTarget;
            T  mnIncrement;
        };

        template< typename T> Adder<T> makeAdder( T& rAdderTarget,
                                                  T  nIncrement )
        {
            return Adder<T>(rAdderTarget, nIncrement);
        }
    }

596 597 598 599 600 601
    void SpriteCanvasHelper::renderSpriteCount( OutputDevice& rOutDev )
    {
        if( mpRedrawManager )
        {
            sal_Int32 nCount(0);

602
            mpRedrawManager->forEachSprite( makeAdder(nCount,sal_Int32(1)) );
603
            OUString text( OUString::number(nCount) );
604 605 606

            // pad with leading space
            while( text.getLength() < 3 )
607
                text = " " + text;
608

609
            text = "Sprites: " + text;
610 611 612 613 614 615 616 617 618

            renderInfoText( rOutDev,
                            text,
                            Point(0, 30) );
        }
    }

    void SpriteCanvasHelper::renderMemUsage( OutputDevice& rOutDev )
    {
619
        BackBufferSharedPtr pBackBuffer( mpOwningSpriteCanvas->getBackBuffer() );
620 621 622 623 624 625 626

        if( mpRedrawManager &&
            pBackBuffer )
        {
            double nPixel(0.0);

            // accumulate pixel count for each sprite into fCount
627 628 629 630
            mpRedrawManager->forEachSprite(
                    [&nPixel]( const ::canvas::Sprite::Reference& rSprite )
                    { makeAdder( nPixel, 1.0 )( calcNumPixel(rSprite) ); }
                    );
631 632 633 634 635 636 637 638 639 640 641

            static const int NUM_VIRDEV(2);
            static const int BYTES_PER_PIXEL(3);

            const Size& rVDevSize( maVDev->GetOutputSizePixel() );
            const Size& rBackBufferSize( pBackBuffer->getOutDev().GetOutputSizePixel() );

            const double nMemUsage( nPixel * NUM_VIRDEV * BYTES_PER_PIXEL +
                                    rVDevSize.Width()*rVDevSize.Height() * BYTES_PER_PIXEL +
                                    rBackBufferSize.Width()*rBackBufferSize.Height() * BYTES_PER_PIXEL );

642
            OUString text( ::rtl::math::doubleToUString( nMemUsage / 1048576.0,
643
                                                                rtl_math_StringFormat_F,
644
                                                                2,'.',nullptr,' ') );
645 646 647

            // pad with leading space
            while( text.getLength() < 4 )
648
                text = " " + text;
649

650
            text = "Mem: " + text + "MB";
651 652 653 654 655 656 657

            renderInfoText( rOutDev,
                            text,
                            Point(0, 60) );
        }
    }
}
658 659

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */