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 :
40 : using namespace ::com::sun::star;
41 :
42 : #define FPS_BOUNDS Rectangle(0,0,130,90)
43 : #define INFO_COLOR COL_RED
44 :
45 : namespace vclcanvas
46 : {
47 : namespace
48 : {
49 : /** Sprite redraw at original position
50 :
51 : Used to repaint the whole canvas (background and all
52 : sprites)
53 : */
54 0 : void spriteRedraw( OutputDevice& rOutDev,
55 : const ::canvas::Sprite::Reference& rSprite )
56 : {
57 : // downcast to derived vclcanvas::Sprite interface, which
58 : // provides the actual redraw methods.
59 0 : ::boost::polymorphic_downcast< Sprite* >(rSprite.get())->redraw(rOutDev,
60 0 : true);
61 0 : }
62 :
63 0 : double calcNumPixel( const ::canvas::Sprite::Reference& rSprite )
64 : {
65 : const ::basegfx::B2DSize& rSize(
66 0 : ::boost::polymorphic_downcast< Sprite* >(rSprite.get())->getSizePixel() );
67 :
68 0 : return rSize.getX() * rSize.getY();
69 : }
70 :
71 0 : void repaintBackground( OutputDevice& rOutDev,
72 : OutputDevice& rBackBuffer,
73 : const ::basegfx::B2DRange& rArea )
74 : {
75 0 : const ::Point& rPos( ::vcl::unotools::pointFromB2DPoint( rArea.getMinimum()) );
76 0 : const ::Size& rSize( ::vcl::unotools::sizeFromB2DSize( rArea.getRange()) );
77 :
78 0 : rOutDev.DrawOutDev( rPos, rSize, rPos, rSize, rBackBuffer );
79 0 : }
80 :
81 0 : void opaqueUpdateSpriteArea( const ::canvas::Sprite::Reference& rSprite,
82 : OutputDevice& rOutDev,
83 : const ::basegfx::B2IRange& rArea )
84 : {
85 : const Rectangle& rRequestedArea(
86 0 : ::vcl::unotools::rectangleFromB2IRectangle( rArea ) );
87 :
88 : // clip output to actual update region (otherwise a)
89 : // wouldn't save much render time, and b) will clutter
90 : // scrolled sprite content outside this area)
91 0 : rOutDev.EnableMapMode( sal_False );
92 0 : rOutDev.SetClipRegion( 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 ::rtl::OUString& rStr,
162 : const Point& rPos )
163 : {
164 0 : 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 : sal_Bool SpriteCanvasHelper::updateScreen( sal_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 sal_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 : Window* pTargetWindow = NULL;
270 0 : if( rOutDev.GetOutDevType() == OUTDEV_WINDOW )
271 : {
272 0 : pTargetWindow = &static_cast<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 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( sal_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( sal_False );
326 0 : rOutDev.SetClipRegion();
327 : rOutDev.DrawOutDev( aEmptyPoint, aOutDevSize,
328 : aEmptyPoint, aOutDevSize,
329 0 : *maVDev );
330 : }
331 :
332 : // change record vector must be cleared, for the next turn of
333 : // rendering and sprite changing
334 0 : mpRedrawManager->clearChangeRecords();
335 :
336 0 : io_bSurfaceDirty = false;
337 :
338 0 : if( mbShowFrameInfo )
339 : {
340 0 : renderFrameCounter( rOutDev );
341 0 : renderSpriteCount( rOutDev );
342 0 : renderMemUsage( rOutDev );
343 : }
344 :
345 : #if OSL_DEBUG_LEVEL > 2
346 : static ::canvas::tools::ElapsedTime aElapsedTime;
347 :
348 : // log time immediately after surface flip
349 : OSL_TRACE( "SpriteCanvasHelper::updateScreen(): flip done at %f",
350 : aElapsedTime.getElapsedTime() );
351 : #endif
352 :
353 : // sync output with screen, to ensure that we don't queue up
354 : // render requests (calling code might rely on timing,
355 : // i.e. assume that things are visible on screen after
356 : // updateScreen() returns).
357 0 : if( pTargetWindow )
358 : {
359 : // commit to screen
360 0 : pTargetWindow->Sync();
361 : }
362 :
363 0 : return sal_True;
364 : }
365 :
366 0 : void SpriteCanvasHelper::backgroundPaint( const ::basegfx::B2DRange& rUpdateRect )
367 : {
368 0 : ENSURE_OR_THROW( mpOwningSpriteCanvas &&
369 : mpOwningSpriteCanvas->getBackBuffer() &&
370 : mpOwningSpriteCanvas->getFrontBuffer(),
371 : "SpriteCanvasHelper::backgroundPaint(): NULL device pointer " );
372 :
373 0 : OutputDevice& rOutDev( mpOwningSpriteCanvas->getFrontBuffer()->getOutDev() );
374 0 : BackBufferSharedPtr pBackBuffer( mpOwningSpriteCanvas->getBackBuffer() );
375 0 : OutputDevice& rBackOutDev( pBackBuffer->getOutDev() );
376 :
377 0 : repaintBackground( rOutDev, rBackOutDev, rUpdateRect );
378 0 : }
379 :
380 0 : void SpriteCanvasHelper::scrollUpdate( const ::basegfx::B2DRange& rMoveStart,
381 : const ::basegfx::B2DRange& rMoveEnd,
382 : const ::canvas::SpriteRedrawManager::UpdateArea& rUpdateArea )
383 : {
384 0 : ENSURE_OR_THROW( mpOwningSpriteCanvas &&
385 : mpOwningSpriteCanvas->getBackBuffer() &&
386 : mpOwningSpriteCanvas->getFrontBuffer(),
387 : "SpriteCanvasHelper::scrollUpdate(): NULL device pointer " );
388 :
389 0 : OutputDevice& rOutDev( mpOwningSpriteCanvas->getFrontBuffer()->getOutDev() );
390 0 : BackBufferSharedPtr pBackBuffer( mpOwningSpriteCanvas->getBackBuffer() );
391 0 : OutputDevice& rBackOutDev( pBackBuffer->getOutDev() );
392 :
393 0 : const Size& rTargetSizePixel( rOutDev.GetOutputSizePixel() );
394 : const ::basegfx::B2IRange aOutputBounds( 0,0,
395 : rTargetSizePixel.Width(),
396 0 : rTargetSizePixel.Height() );
397 :
398 : // round rectangles to integer pixel. Note: have to be
399 : // extremely careful here, to avoid off-by-one errors for
400 : // the destination area: otherwise, the next scroll update
401 : // would copy pixel that are not supposed to be part of
402 : // the sprite.
403 : ::basegfx::B2IRange aSourceRect(
404 0 : ::canvas::tools::spritePixelAreaFromB2DRange( rMoveStart ) );
405 : const ::basegfx::B2IRange& rDestRect(
406 0 : ::canvas::tools::spritePixelAreaFromB2DRange( rMoveEnd ) );
407 0 : ::basegfx::B2IPoint aDestPos( rDestRect.getMinimum() );
408 :
409 0 : ::std::vector< ::basegfx::B2IRange > aUnscrollableAreas;
410 :
411 : // Since strictly speaking, this scroll algorithm is plain
412 : // buggy, the scrolled area might actually lie _below_ another
413 : // window - we've made this feature configurable via
414 : // mbIsUnsafeScrolling.
415 :
416 : // clip to output bounds (cannot properly scroll stuff
417 : // _outside_ our screen area)
418 0 : if( !mbIsUnsafeScrolling ||
419 : !::canvas::tools::clipScrollArea( aSourceRect,
420 : aDestPos,
421 : aUnscrollableAreas,
422 0 : aOutputBounds ) )
423 : {
424 : // fully clipped scroll area: cannot simply scroll
425 : // then. Perform normal opaque update (can use that, since
426 : // one of the preconditions for scrollable update is
427 : // opaque sprite content)
428 :
429 : // repaint all affected sprites directly to output device
430 : ::std::for_each( rUpdateArea.maComponentList.begin(),
431 : rUpdateArea.maComponentList.end(),
432 : ::boost::bind(
433 : &spriteRedrawStub3,
434 : ::boost::ref( rOutDev ),
435 0 : _1 ) );
436 : }
437 : else
438 : {
439 : // scroll rOutDev content
440 : rOutDev.CopyArea( ::vcl::unotools::pointFromB2IPoint( aDestPos ),
441 0 : ::vcl::unotools::pointFromB2IPoint( aSourceRect.getMinimum() ),
442 : // TODO(Q2): use numeric_cast to check range
443 0 : ::Size( static_cast<sal_Int32>(aSourceRect.getRange().getX()),
444 0 : static_cast<sal_Int32>(aSourceRect.getRange().getY()) ) );
445 :
446 : const ::canvas::SpriteRedrawManager::SpriteConnectedRanges::ComponentListType::const_iterator
447 0 : aFirst( rUpdateArea.maComponentList.begin() );
448 :
449 0 : ENSURE_OR_THROW( aFirst->second.getSprite().is(),
450 : "VCLCanvas::scrollUpdate(): no sprite" );
451 :
452 : // repaint uncovered areas from sprite. Need to actually
453 : // clip here, since we're only repainting _parts_ of the
454 : // sprite
455 0 : rOutDev.Push( PUSH_CLIPREGION );
456 : ::std::for_each( aUnscrollableAreas.begin(),
457 : aUnscrollableAreas.end(),
458 : ::boost::bind( &opaqueUpdateSpriteArea,
459 0 : ::boost::cref(aFirst->second.getSprite()),
460 : ::boost::ref(rOutDev),
461 0 : _1 ) );
462 0 : rOutDev.Pop();
463 : }
464 :
465 : // repaint uncovered areas from backbuffer - take the
466 : // _rounded_ rectangles from above, to have the update
467 : // consistent with the scroll above.
468 0 : ::std::vector< ::basegfx::B2DRange > aUncoveredAreas;
469 : ::basegfx::computeSetDifference( aUncoveredAreas,
470 : rUpdateArea.maTotalBounds,
471 0 : ::basegfx::B2DRange( rDestRect ) );
472 : ::std::for_each( aUncoveredAreas.begin(),
473 : aUncoveredAreas.end(),
474 : ::boost::bind( &repaintBackground,
475 : ::boost::ref(rOutDev),
476 : ::boost::ref(rBackOutDev),
477 0 : _1 ) );
478 0 : }
479 :
480 0 : void SpriteCanvasHelper::opaqueUpdate( SAL_UNUSED_PARAMETER const ::basegfx::B2DRange&,
481 : const ::std::vector< ::canvas::Sprite::Reference >& rSortedUpdateSprites )
482 : {
483 0 : ENSURE_OR_THROW( mpOwningSpriteCanvas &&
484 : mpOwningSpriteCanvas->getBackBuffer() &&
485 : mpOwningSpriteCanvas->getFrontBuffer(),
486 : "SpriteCanvasHelper::opaqueUpdate(): NULL device pointer " );
487 :
488 0 : OutputDevice& rOutDev( mpOwningSpriteCanvas->getFrontBuffer()->getOutDev() );
489 :
490 : // no need to clip output to actual update region - there will
491 : // always be ALL sprites contained in the rectangular update
492 : // area containd in rTotalArea (that's the way
493 : // B2DConnectedRanges work). If rTotalArea appears to be
494 : // smaller than the sprite - then this sprite carries a clip,
495 : // and the update will be constrained to that rect.
496 :
497 : // repaint all affected sprites directly to output device
498 : ::std::for_each( rSortedUpdateSprites.begin(),
499 : rSortedUpdateSprites.end(),
500 : ::boost::bind(
501 : &spriteRedrawStub,
502 : ::boost::ref( rOutDev ),
503 0 : _1 ) );
504 0 : }
505 :
506 0 : void SpriteCanvasHelper::genericUpdate( const ::basegfx::B2DRange& rRequestedArea,
507 : const ::std::vector< ::canvas::Sprite::Reference >& rSortedUpdateSprites )
508 : {
509 0 : ENSURE_OR_THROW( mpOwningSpriteCanvas &&
510 : mpOwningSpriteCanvas->getBackBuffer() &&
511 : mpOwningSpriteCanvas->getFrontBuffer(),
512 : "SpriteCanvasHelper::genericUpdate(): NULL device pointer " );
513 :
514 0 : OutputDevice& rOutDev( mpOwningSpriteCanvas->getFrontBuffer()->getOutDev() );
515 0 : BackBufferSharedPtr pBackBuffer( mpOwningSpriteCanvas->getBackBuffer() );
516 0 : OutputDevice& rBackOutDev( pBackBuffer->getOutDev() );
517 :
518 : // limit size of update VDev to target outdev's size
519 0 : const Size& rTargetSizePixel( rOutDev.GetOutputSizePixel() );
520 :
521 : // round output position towards zero. Don't want to truncate
522 : // a fraction of a sprite pixel... Clip position at origin,
523 : // otherwise, truncation of size below might leave visible
524 : // areas uncovered by VDev.
525 : const ::Point aOutputPosition(
526 : ::std::max( sal_Int32( 0 ),
527 0 : static_cast< sal_Int32 >(rRequestedArea.getMinX()) ),
528 : ::std::max( sal_Int32( 0 ),
529 0 : static_cast< sal_Int32 >(rRequestedArea.getMinY()) ) );
530 : // round output size towards +infty. Don't want to truncate a
531 : // fraction of a sprite pixel... Limit coverage of VDev to
532 : // output device's area (i.e. not only to total size, but to
533 : // cover _only_ the visible parts).
534 : const ::Size aOutputSize(
535 : ::std::max( sal_Int32( 0 ),
536 0 : ::std::min( static_cast< sal_Int32 >(rTargetSizePixel.Width() - aOutputPosition.X()),
537 0 : ::canvas::tools::roundUp( rRequestedArea.getMaxX() - aOutputPosition.X() ))),
538 : ::std::max( sal_Int32( 0 ),
539 0 : ::std::min( static_cast< sal_Int32 >(rTargetSizePixel.Height() - aOutputPosition.Y()),
540 0 : ::canvas::tools::roundUp( rRequestedArea.getMaxY() - aOutputPosition.Y() ))));
541 :
542 : // early exit for empty output area.
543 0 : if( aOutputSize.Width() == 0 &&
544 0 : aOutputSize.Height() == 0 )
545 : {
546 0 : return;
547 : }
548 :
549 0 : const Point aEmptyPoint(0,0);
550 0 : const Size aCurrOutputSize( maVDev->GetOutputSizePixel() );
551 :
552 : // adapt maVDev's size to the area that actually needs the
553 : // repaint.
554 0 : if( aCurrOutputSize.Width() < aOutputSize.Width() ||
555 0 : aCurrOutputSize.Height() < aOutputSize.Height() )
556 : {
557 : // TODO(P1): Come up with a clever tactic to reduce maVDev
558 : // from time to time. Reduction with threshold (say, if
559 : // maVDev is more than twice too large) is not wise, as
560 : // this might then toggle within the same updateScreen(),
561 : // but for different disjunct sprite areas.
562 0 : maVDev->SetOutputSizePixel( aOutputSize );
563 : }
564 :
565 : // paint background
566 0 : maVDev->EnableMapMode( sal_False );
567 0 : maVDev->SetClipRegion();
568 0 : maVDev->DrawOutDev( aEmptyPoint, aOutputSize,
569 : aOutputPosition, aOutputSize,
570 0 : rBackOutDev );
571 :
572 : // repaint all affected sprites on top of background into
573 : // VDev.
574 : ::std::for_each( rSortedUpdateSprites.begin(),
575 : rSortedUpdateSprites.end(),
576 : ::boost::bind( &spriteRedrawStub2,
577 0 : ::boost::ref( maVDev.get() ),
578 : ::boost::cref(
579 : ::vcl::unotools::b2DPointFromPoint(aOutputPosition)),
580 0 : _1 ) );
581 :
582 : // flush to screen
583 0 : rOutDev.EnableMapMode( sal_False );
584 : rOutDev.DrawOutDev( aOutputPosition, aOutputSize,
585 : aEmptyPoint, aOutputSize,
586 0 : *maVDev );
587 : }
588 :
589 0 : void SpriteCanvasHelper::renderFrameCounter( OutputDevice& rOutDev )
590 : {
591 0 : const double denominator( maLastUpdate.getElapsedTime() );
592 0 : maLastUpdate.reset();
593 :
594 : ::rtl::OUString text( ::rtl::math::doubleToUString( denominator == 0.0 ? 100.0 : 1.0/denominator,
595 : rtl_math_StringFormat_F,
596 0 : 2,'.',NULL,' ') );
597 :
598 : // pad with leading space
599 0 : while( text.getLength() < 6 )
600 0 : text = ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM (" ")) + text;
601 :
602 0 : text += ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM (" fps"));
603 :
604 : renderInfoText( rOutDev,
605 : text,
606 0 : Point(0, 0) );
607 0 : }
608 :
609 : namespace
610 : {
611 : template< typename T > struct Adder
612 : {
613 : typedef void result_type;
614 :
615 0 : Adder( T& rAdderTarget,
616 : T nIncrement ) :
617 : mpTarget( &rAdderTarget ),
618 0 : mnIncrement( nIncrement )
619 : {
620 0 : }
621 :
622 : void operator()() { *mpTarget += mnIncrement; }
623 0 : void operator()( const ::canvas::Sprite::Reference& ) { *mpTarget += mnIncrement; }
624 0 : void operator()( T nIncrement ) { *mpTarget += nIncrement; }
625 :
626 : T* mpTarget;
627 : T mnIncrement;
628 : };
629 :
630 0 : template< typename T> Adder<T> makeAdder( T& rAdderTarget,
631 : T nIncrement )
632 : {
633 0 : return Adder<T>(rAdderTarget, nIncrement);
634 : }
635 : }
636 :
637 0 : void SpriteCanvasHelper::renderSpriteCount( OutputDevice& rOutDev )
638 : {
639 0 : if( mpRedrawManager )
640 : {
641 0 : sal_Int32 nCount(0);
642 :
643 0 : mpRedrawManager->forEachSprite( makeAdder(nCount,sal_Int32(1)) );
644 : ::rtl::OUString text(
645 : ::rtl::OUString::valueOf(
646 : // disambiguate overload...
647 0 : static_cast<sal_Int64>(nCount) ) );
648 :
649 : // pad with leading space
650 0 : while( text.getLength() < 3 )
651 0 : text = ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM (" ")) + text;
652 :
653 0 : text = ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM ("Sprites: ")) + text;
654 :
655 : renderInfoText( rOutDev,
656 : text,
657 0 : Point(0, 30) );
658 : }
659 0 : }
660 :
661 0 : void SpriteCanvasHelper::renderMemUsage( OutputDevice& rOutDev )
662 : {
663 0 : BackBufferSharedPtr pBackBuffer( mpOwningSpriteCanvas->getBackBuffer() );
664 :
665 0 : if( mpRedrawManager &&
666 0 : pBackBuffer )
667 : {
668 0 : double nPixel(0.0);
669 :
670 : // accumulate pixel count for each sprite into fCount
671 : mpRedrawManager->forEachSprite( ::boost::bind(
672 : makeAdder(nPixel,1.0),
673 : ::boost::bind(
674 : &calcNumPixel,
675 0 : _1 ) ) );
676 :
677 : static const int NUM_VIRDEV(2);
678 : static const int BYTES_PER_PIXEL(3);
679 :
680 0 : const Size& rVDevSize( maVDev->GetOutputSizePixel() );
681 0 : const Size& rBackBufferSize( pBackBuffer->getOutDev().GetOutputSizePixel() );
682 :
683 : const double nMemUsage( nPixel * NUM_VIRDEV * BYTES_PER_PIXEL +
684 0 : rVDevSize.Width()*rVDevSize.Height() * BYTES_PER_PIXEL +
685 0 : rBackBufferSize.Width()*rBackBufferSize.Height() * BYTES_PER_PIXEL );
686 :
687 : ::rtl::OUString text( ::rtl::math::doubleToUString( nMemUsage / 1048576.0,
688 : rtl_math_StringFormat_F,
689 0 : 2,'.',NULL,' ') );
690 :
691 : // pad with leading space
692 0 : while( text.getLength() < 4 )
693 0 : text = ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM (" ")) + text;
694 :
695 : text = ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM ("Mem: ")) +
696 0 : text +
697 0 : ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM ("MB"));
698 :
699 : renderInfoText( rOutDev,
700 : text,
701 0 : Point(0, 60) );
702 0 : }
703 0 : }
704 0 : }
705 :
706 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|