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( AntialiasingFlags::EnableB2dDraw );
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 : SpriteCanvasHelper::~SpriteCanvasHelper()
193 : {
194 0 : SolarMutexGuard aGuard;
195 0 : maVDev.disposeAndClear();
196 0 : }
197 :
198 0 : void SpriteCanvasHelper::init( const OutDevProviderSharedPtr& rOutDev,
199 : SpriteCanvas& rOwningSpriteCanvas,
200 : ::canvas::SpriteRedrawManager& rManager,
201 : bool bProtect,
202 : bool bHaveAlpha )
203 : {
204 0 : mpOwningSpriteCanvas = &rOwningSpriteCanvas;
205 0 : mpRedrawManager = &rManager;
206 :
207 0 : CanvasHelper::init(rOwningSpriteCanvas,rOutDev,bProtect,bHaveAlpha);
208 0 : }
209 :
210 0 : void SpriteCanvasHelper::disposing()
211 : {
212 0 : mpRedrawManager = NULL;
213 0 : mpOwningSpriteCanvas = NULL;
214 :
215 : // forward to base
216 0 : CanvasHelper::disposing();
217 0 : }
218 :
219 0 : uno::Reference< rendering::XAnimatedSprite > SpriteCanvasHelper::createSpriteFromAnimation(
220 : const uno::Reference< rendering::XAnimation >& )
221 : {
222 0 : return uno::Reference< rendering::XAnimatedSprite >();
223 : }
224 :
225 0 : uno::Reference< rendering::XAnimatedSprite > SpriteCanvasHelper::createSpriteFromBitmaps(
226 : const uno::Sequence< uno::Reference< rendering::XBitmap > >& ,
227 : sal_Int8 )
228 : {
229 0 : return uno::Reference< rendering::XAnimatedSprite >();
230 : }
231 :
232 0 : uno::Reference< rendering::XCustomSprite > SpriteCanvasHelper::createCustomSprite( const geometry::RealSize2D& spriteSize )
233 : {
234 0 : if( !mpRedrawManager || !mpDevice )
235 0 : return uno::Reference< rendering::XCustomSprite >(); // we're disposed
236 :
237 : return uno::Reference< rendering::XCustomSprite >(
238 : new CanvasCustomSprite( spriteSize,
239 : *mpDevice,
240 : mpOwningSpriteCanvas,
241 : mpOwningSpriteCanvas->getFrontBuffer(),
242 0 : mbShowSpriteBounds ) );
243 : }
244 :
245 0 : uno::Reference< rendering::XSprite > SpriteCanvasHelper::createClonedSprite( const uno::Reference< rendering::XSprite >& )
246 : {
247 0 : return uno::Reference< rendering::XSprite >();
248 : }
249 :
250 0 : bool SpriteCanvasHelper::updateScreen( bool bUpdateAll,
251 : bool& io_bSurfaceDirty )
252 : {
253 0 : if( !mpRedrawManager ||
254 0 : !mpOwningSpriteCanvas ||
255 0 : !mpOwningSpriteCanvas->getFrontBuffer() ||
256 0 : !mpOwningSpriteCanvas->getBackBuffer() )
257 : {
258 0 : return false; // disposed, or otherwise dysfunctional
259 : }
260 :
261 : // commit to backbuffer
262 0 : flush();
263 :
264 0 : OutputDevice& rOutDev( mpOwningSpriteCanvas->getFrontBuffer()->getOutDev() );
265 0 : BackBufferSharedPtr pBackBuffer( mpOwningSpriteCanvas->getBackBuffer() );
266 0 : OutputDevice& rBackOutDev( pBackBuffer->getOutDev() );
267 :
268 : // actual OutputDevice is a shared resource - restore its
269 : // state when done.
270 0 : tools::OutDevStateKeeper aStateKeeper( rOutDev );
271 :
272 0 : const Size aOutDevSize( rBackOutDev.GetOutputSizePixel() );
273 0 : const Point aEmptyPoint(0,0);
274 :
275 0 : vcl::Window* pTargetWindow = NULL;
276 0 : if( rOutDev.GetOutDevType() == OUTDEV_WINDOW )
277 : {
278 0 : pTargetWindow = &static_cast<vcl::Window&>(rOutDev); // TODO(Q3): Evil downcast.
279 :
280 : // we're double-buffered, thus no need for paint area-limiting
281 : // clips. besides that, will interfere with animations (as for
282 : // Window-invalidate repaints, only parts of the window will
283 : // be redrawn otherwise)
284 : const vcl::Region aFullWindowRegion( Rectangle(aEmptyPoint,
285 0 : aOutDevSize) );
286 0 : pTargetWindow->ExpandPaintClipRegion(aFullWindowRegion);
287 : }
288 :
289 : // TODO(P1): Might be worthwile to track areas of background
290 : // changes, too.
291 0 : if( !bUpdateAll && !io_bSurfaceDirty )
292 : {
293 0 : if( mbShowFrameInfo )
294 : {
295 : // also repaint background below frame counter (fake
296 : // that as a sprite vanishing in this area)
297 : mpRedrawManager->updateSprite( ::canvas::Sprite::Reference(),
298 : ::basegfx::B2DPoint(),
299 : ::basegfx::B2DRectangle( 0.0, 0.0,
300 0 : FPS_BOUNDS.Right(),
301 0 : FPS_BOUNDS.Bottom() ) );
302 : }
303 :
304 : // background has not changed, so we're free to optimize
305 : // repaint to areas where a sprite has changed
306 :
307 : // process each independent area of overlapping sprites
308 : // separately.
309 0 : mpRedrawManager->forEachSpriteArea( *this );
310 : }
311 : else
312 : {
313 : // background has changed, so we currently have no choice
314 : // but repaint everything (or caller requested that)
315 :
316 0 : maVDev->SetOutputSizePixel( aOutDevSize );
317 0 : maVDev->EnableMapMode( false );
318 0 : maVDev->DrawOutDev( aEmptyPoint, aOutDevSize,
319 : aEmptyPoint, aOutDevSize,
320 0 : rBackOutDev );
321 :
322 : // repaint all active sprites on top of background into
323 : // VDev.
324 : mpRedrawManager->forEachSprite(
325 : ::boost::bind(
326 : &spriteRedraw,
327 0 : ::boost::ref( *maVDev.get() ),
328 0 : _1 ) );
329 :
330 : // flush to screen
331 0 : rOutDev.EnableMapMode( false );
332 0 : rOutDev.SetAntialiasing( AntialiasingFlags::EnableB2dDraw );
333 0 : rOutDev.SetClipRegion();
334 : rOutDev.DrawOutDev( aEmptyPoint, aOutDevSize,
335 : aEmptyPoint, aOutDevSize,
336 0 : *maVDev );
337 : }
338 :
339 : // change record vector must be cleared, for the next turn of
340 : // rendering and sprite changing
341 0 : mpRedrawManager->clearChangeRecords();
342 :
343 0 : io_bSurfaceDirty = false;
344 :
345 0 : if( mbShowFrameInfo )
346 : {
347 0 : renderFrameCounter( rOutDev );
348 0 : renderSpriteCount( rOutDev );
349 0 : renderMemUsage( rOutDev );
350 : }
351 :
352 : #if OSL_DEBUG_LEVEL > 2
353 : static ::canvas::tools::ElapsedTime aElapsedTime;
354 :
355 : // log time immediately after surface flip
356 : OSL_TRACE( "SpriteCanvasHelper::updateScreen(): flip done at %f",
357 : aElapsedTime.getElapsedTime() );
358 : #endif
359 :
360 : // sync output with screen, to ensure that we don't queue up
361 : // render requests (calling code might rely on timing,
362 : // i.e. assume that things are visible on screen after
363 : // updateScreen() returns).
364 0 : if( pTargetWindow )
365 : {
366 : // commit to screen
367 0 : pTargetWindow->Sync();
368 : }
369 :
370 0 : return true;
371 : }
372 :
373 0 : void SpriteCanvasHelper::backgroundPaint( const ::basegfx::B2DRange& rUpdateRect )
374 : {
375 0 : ENSURE_OR_THROW( mpOwningSpriteCanvas &&
376 : mpOwningSpriteCanvas->getBackBuffer() &&
377 : mpOwningSpriteCanvas->getFrontBuffer(),
378 : "SpriteCanvasHelper::backgroundPaint(): NULL device pointer " );
379 :
380 0 : OutputDevice& rOutDev( mpOwningSpriteCanvas->getFrontBuffer()->getOutDev() );
381 0 : BackBufferSharedPtr pBackBuffer( mpOwningSpriteCanvas->getBackBuffer() );
382 0 : OutputDevice& rBackOutDev( pBackBuffer->getOutDev() );
383 :
384 0 : repaintBackground( rOutDev, rBackOutDev, rUpdateRect );
385 0 : }
386 :
387 0 : void SpriteCanvasHelper::scrollUpdate( const ::basegfx::B2DRange& rMoveStart,
388 : const ::basegfx::B2DRange& rMoveEnd,
389 : const ::canvas::SpriteRedrawManager::UpdateArea& rUpdateArea )
390 : {
391 0 : ENSURE_OR_THROW( mpOwningSpriteCanvas &&
392 : mpOwningSpriteCanvas->getBackBuffer() &&
393 : mpOwningSpriteCanvas->getFrontBuffer(),
394 : "SpriteCanvasHelper::scrollUpdate(): NULL device pointer " );
395 :
396 0 : OutputDevice& rOutDev( mpOwningSpriteCanvas->getFrontBuffer()->getOutDev() );
397 0 : BackBufferSharedPtr pBackBuffer( mpOwningSpriteCanvas->getBackBuffer() );
398 0 : OutputDevice& rBackOutDev( pBackBuffer->getOutDev() );
399 :
400 0 : const Size& rTargetSizePixel( rOutDev.GetOutputSizePixel() );
401 : const ::basegfx::B2IRange aOutputBounds( 0,0,
402 0 : rTargetSizePixel.Width(),
403 0 : rTargetSizePixel.Height() );
404 :
405 : // round rectangles to integer pixel. Note: have to be
406 : // extremely careful here, to avoid off-by-one errors for
407 : // the destination area: otherwise, the next scroll update
408 : // would copy pixel that are not supposed to be part of
409 : // the sprite.
410 : ::basegfx::B2IRange aSourceRect(
411 0 : ::canvas::tools::spritePixelAreaFromB2DRange( rMoveStart ) );
412 : const ::basegfx::B2IRange& rDestRect(
413 0 : ::canvas::tools::spritePixelAreaFromB2DRange( rMoveEnd ) );
414 0 : ::basegfx::B2IPoint aDestPos( rDestRect.getMinimum() );
415 :
416 0 : ::std::vector< ::basegfx::B2IRange > aUnscrollableAreas;
417 :
418 : // Since strictly speaking, this scroll algorithm is plain
419 : // buggy, the scrolled area might actually lie _below_ another
420 : // window - we've made this feature configurable via
421 : // mbIsUnsafeScrolling.
422 :
423 : // clip to output bounds (cannot properly scroll stuff
424 : // _outside_ our screen area)
425 0 : if( !mbIsUnsafeScrolling ||
426 : !::canvas::tools::clipScrollArea( aSourceRect,
427 : aDestPos,
428 : aUnscrollableAreas,
429 0 : aOutputBounds ) )
430 : {
431 : // fully clipped scroll area: cannot simply scroll
432 : // then. Perform normal opaque update (can use that, since
433 : // one of the preconditions for scrollable update is
434 : // opaque sprite content)
435 :
436 : // repaint all affected sprites directly to output device
437 : ::std::for_each( rUpdateArea.maComponentList.begin(),
438 : rUpdateArea.maComponentList.end(),
439 : ::boost::bind(
440 : &spriteRedrawStub3,
441 : ::boost::ref( rOutDev ),
442 0 : _1 ) );
443 : }
444 : else
445 : {
446 : // scroll rOutDev content
447 : rOutDev.CopyArea( vcl::unotools::pointFromB2IPoint( aDestPos ),
448 0 : vcl::unotools::pointFromB2IPoint( aSourceRect.getMinimum() ),
449 : // TODO(Q2): use numeric_cast to check range
450 0 : ::Size( static_cast<sal_Int32>(aSourceRect.getRange().getX()),
451 0 : static_cast<sal_Int32>(aSourceRect.getRange().getY()) ) );
452 :
453 : const ::canvas::SpriteRedrawManager::SpriteConnectedRanges::ComponentListType::const_iterator
454 0 : aFirst( rUpdateArea.maComponentList.begin() );
455 :
456 0 : ENSURE_OR_THROW( aFirst->second.getSprite().is(),
457 : "VCLCanvas::scrollUpdate(): no sprite" );
458 :
459 : // repaint uncovered areas from sprite. Need to actually
460 : // clip here, since we're only repainting _parts_ of the
461 : // sprite
462 0 : rOutDev.Push( PushFlags::CLIPREGION );
463 : ::std::for_each( aUnscrollableAreas.begin(),
464 : aUnscrollableAreas.end(),
465 : ::boost::bind( &opaqueUpdateSpriteArea,
466 0 : ::boost::cref(aFirst->second.getSprite()),
467 : ::boost::ref(rOutDev),
468 0 : _1 ) );
469 0 : rOutDev.Pop();
470 : }
471 :
472 : // repaint uncovered areas from backbuffer - take the
473 : // _rounded_ rectangles from above, to have the update
474 : // consistent with the scroll above.
475 0 : ::std::vector< ::basegfx::B2DRange > aUncoveredAreas;
476 : ::basegfx::computeSetDifference( aUncoveredAreas,
477 : rUpdateArea.maTotalBounds,
478 0 : ::basegfx::B2DRange( rDestRect ) );
479 : ::std::for_each( aUncoveredAreas.begin(),
480 : aUncoveredAreas.end(),
481 : ::boost::bind( &repaintBackground,
482 : ::boost::ref(rOutDev),
483 : ::boost::ref(rBackOutDev),
484 0 : _1 ) );
485 0 : }
486 :
487 0 : void SpriteCanvasHelper::opaqueUpdate( SAL_UNUSED_PARAMETER const ::basegfx::B2DRange&,
488 : const ::std::vector< ::canvas::Sprite::Reference >& rSortedUpdateSprites )
489 : {
490 0 : ENSURE_OR_THROW( mpOwningSpriteCanvas &&
491 : mpOwningSpriteCanvas->getBackBuffer() &&
492 : mpOwningSpriteCanvas->getFrontBuffer(),
493 : "SpriteCanvasHelper::opaqueUpdate(): NULL device pointer " );
494 :
495 0 : OutputDevice& rOutDev( mpOwningSpriteCanvas->getFrontBuffer()->getOutDev() );
496 :
497 : // no need to clip output to actual update region - there will
498 : // always be ALL sprites contained in the rectangular update
499 : // area containd in rTotalArea (that's the way
500 : // B2DConnectedRanges work). If rTotalArea appears to be
501 : // smaller than the sprite - then this sprite carries a clip,
502 : // and the update will be constrained to that rect.
503 :
504 : // repaint all affected sprites directly to output device
505 : ::std::for_each( rSortedUpdateSprites.begin(),
506 : rSortedUpdateSprites.end(),
507 : ::boost::bind(
508 : &spriteRedrawStub,
509 : ::boost::ref( rOutDev ),
510 0 : _1 ) );
511 0 : }
512 :
513 0 : void SpriteCanvasHelper::genericUpdate( const ::basegfx::B2DRange& rRequestedArea,
514 : const ::std::vector< ::canvas::Sprite::Reference >& rSortedUpdateSprites )
515 : {
516 0 : ENSURE_OR_THROW( mpOwningSpriteCanvas &&
517 : mpOwningSpriteCanvas->getBackBuffer() &&
518 : mpOwningSpriteCanvas->getFrontBuffer(),
519 : "SpriteCanvasHelper::genericUpdate(): NULL device pointer " );
520 :
521 0 : OutputDevice& rOutDev( mpOwningSpriteCanvas->getFrontBuffer()->getOutDev() );
522 0 : BackBufferSharedPtr pBackBuffer( mpOwningSpriteCanvas->getBackBuffer() );
523 0 : OutputDevice& rBackOutDev( pBackBuffer->getOutDev() );
524 :
525 : // limit size of update VDev to target outdev's size
526 0 : const Size& rTargetSizePixel( rOutDev.GetOutputSizePixel() );
527 :
528 : // round output position towards zero. Don't want to truncate
529 : // a fraction of a sprite pixel... Clip position at origin,
530 : // otherwise, truncation of size below might leave visible
531 : // areas uncovered by VDev.
532 : const ::Point aOutputPosition(
533 : ::std::max( sal_Int32( 0 ),
534 0 : static_cast< sal_Int32 >(rRequestedArea.getMinX()) ),
535 : ::std::max( sal_Int32( 0 ),
536 0 : static_cast< sal_Int32 >(rRequestedArea.getMinY()) ) );
537 : // round output size towards +infty. Don't want to truncate a
538 : // fraction of a sprite pixel... Limit coverage of VDev to
539 : // output device's area (i.e. not only to total size, but to
540 : // cover _only_ the visible parts).
541 : const ::Size aOutputSize(
542 : ::std::max( sal_Int32( 0 ),
543 0 : ::std::min( static_cast< sal_Int32 >(rTargetSizePixel.Width() - aOutputPosition.X()),
544 0 : ::canvas::tools::roundUp( rRequestedArea.getMaxX() - aOutputPosition.X() ))),
545 : ::std::max( sal_Int32( 0 ),
546 0 : ::std::min( static_cast< sal_Int32 >(rTargetSizePixel.Height() - aOutputPosition.Y()),
547 0 : ::canvas::tools::roundUp( rRequestedArea.getMaxY() - aOutputPosition.Y() ))));
548 :
549 : // early exit for empty output area.
550 0 : if( aOutputSize.Width() == 0 &&
551 0 : aOutputSize.Height() == 0 )
552 : {
553 0 : return;
554 : }
555 :
556 0 : const Point aEmptyPoint(0,0);
557 0 : const Size aCurrOutputSize( maVDev->GetOutputSizePixel() );
558 :
559 : // adapt maVDev's size to the area that actually needs the
560 : // repaint.
561 0 : if( aCurrOutputSize.Width() < aOutputSize.Width() ||
562 0 : aCurrOutputSize.Height() < aOutputSize.Height() )
563 : {
564 : // TODO(P1): Come up with a clever tactic to reduce maVDev
565 : // from time to time. Reduction with threshold (say, if
566 : // maVDev is more than twice too large) is not wise, as
567 : // this might then toggle within the same updateScreen(),
568 : // but for different disjunct sprite areas.
569 0 : maVDev->SetOutputSizePixel( aOutputSize );
570 : }
571 :
572 : // paint background
573 0 : maVDev->EnableMapMode( false );
574 0 : maVDev->SetAntialiasing( AntialiasingFlags::EnableB2dDraw );
575 0 : maVDev->SetClipRegion();
576 0 : maVDev->DrawOutDev( aEmptyPoint, aOutputSize,
577 : aOutputPosition, aOutputSize,
578 0 : rBackOutDev );
579 :
580 : // repaint all affected sprites on top of background into
581 : // VDev.
582 : ::std::for_each( rSortedUpdateSprites.begin(),
583 : rSortedUpdateSprites.end(),
584 : ::boost::bind( &spriteRedrawStub2,
585 0 : ::boost::ref( *maVDev.get() ),
586 : vcl::unotools::b2DPointFromPoint(
587 : aOutputPosition),
588 0 : _1 ) );
589 :
590 : // flush to screen
591 0 : rOutDev.EnableMapMode( false );
592 0 : rOutDev.SetAntialiasing( AntialiasingFlags::EnableB2dDraw );
593 : rOutDev.DrawOutDev( aOutputPosition, aOutputSize,
594 : aEmptyPoint, aOutputSize,
595 0 : *maVDev );
596 : }
597 :
598 0 : void SpriteCanvasHelper::renderFrameCounter( OutputDevice& rOutDev )
599 : {
600 0 : const double denominator( maLastUpdate.getElapsedTime() );
601 0 : maLastUpdate.reset();
602 :
603 : OUString text( ::rtl::math::doubleToUString( denominator == 0.0 ? 100.0 : 1.0/denominator,
604 : rtl_math_StringFormat_F,
605 0 : 2,'.',NULL,' ') );
606 :
607 : // pad with leading space
608 0 : while( text.getLength() < 6 )
609 0 : text = " " + text;
610 :
611 0 : text += " fps";
612 :
613 : renderInfoText( rOutDev,
614 : text,
615 0 : Point(0, 0) );
616 0 : }
617 :
618 : namespace
619 : {
620 : template< typename T > struct Adder
621 : {
622 : typedef void result_type;
623 :
624 0 : Adder( T& rAdderTarget,
625 : T nIncrement ) :
626 : mpTarget( &rAdderTarget ),
627 0 : mnIncrement( nIncrement )
628 : {
629 0 : }
630 :
631 : void operator()() { *mpTarget += mnIncrement; }
632 0 : void operator()( const ::canvas::Sprite::Reference& ) { *mpTarget += mnIncrement; }
633 0 : void operator()( T nIncrement ) { *mpTarget += nIncrement; }
634 :
635 : T* mpTarget;
636 : T mnIncrement;
637 : };
638 :
639 0 : template< typename T> Adder<T> makeAdder( T& rAdderTarget,
640 : T nIncrement )
641 : {
642 0 : return Adder<T>(rAdderTarget, nIncrement);
643 : }
644 : }
645 :
646 0 : void SpriteCanvasHelper::renderSpriteCount( OutputDevice& rOutDev )
647 : {
648 0 : if( mpRedrawManager )
649 : {
650 0 : sal_Int32 nCount(0);
651 :
652 0 : mpRedrawManager->forEachSprite( makeAdder(nCount,sal_Int32(1)) );
653 0 : OUString text( OUString::number(nCount) );
654 :
655 : // pad with leading space
656 0 : while( text.getLength() < 3 )
657 0 : text = " " + text;
658 :
659 0 : text = "Sprites: " + text;
660 :
661 : renderInfoText( rOutDev,
662 : text,
663 0 : Point(0, 30) );
664 : }
665 0 : }
666 :
667 0 : void SpriteCanvasHelper::renderMemUsage( OutputDevice& rOutDev )
668 : {
669 0 : BackBufferSharedPtr pBackBuffer( mpOwningSpriteCanvas->getBackBuffer() );
670 :
671 0 : if( mpRedrawManager &&
672 0 : pBackBuffer )
673 : {
674 0 : double nPixel(0.0);
675 :
676 : // accumulate pixel count for each sprite into fCount
677 : mpRedrawManager->forEachSprite( ::boost::bind(
678 : makeAdder(nPixel,1.0),
679 : ::boost::bind(
680 : &calcNumPixel,
681 0 : _1 ) ) );
682 :
683 : static const int NUM_VIRDEV(2);
684 : static const int BYTES_PER_PIXEL(3);
685 :
686 0 : const Size& rVDevSize( maVDev->GetOutputSizePixel() );
687 0 : const Size& rBackBufferSize( pBackBuffer->getOutDev().GetOutputSizePixel() );
688 :
689 0 : const double nMemUsage( nPixel * NUM_VIRDEV * BYTES_PER_PIXEL +
690 0 : rVDevSize.Width()*rVDevSize.Height() * BYTES_PER_PIXEL +
691 0 : rBackBufferSize.Width()*rBackBufferSize.Height() * BYTES_PER_PIXEL );
692 :
693 : OUString text( ::rtl::math::doubleToUString( nMemUsage / 1048576.0,
694 : rtl_math_StringFormat_F,
695 0 : 2,'.',NULL,' ') );
696 :
697 : // pad with leading space
698 0 : while( text.getLength() < 4 )
699 0 : text = " " + text;
700 :
701 0 : text = "Mem: " + text + "MB";
702 :
703 : renderInfoText( rOutDev,
704 : text,
705 0 : Point(0, 60) );
706 0 : }
707 0 : }
708 0 : }
709 :
710 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|