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