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