| /************************************************************** |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| *************************************************************/ |
| |
| |
| |
| // MARKER(update_precomp.py): autogen include statement, do not remove |
| #include "precompiled_canvas.hxx" |
| |
| #include <canvas/debug.hxx> |
| #include <canvas/verbosetrace.hxx> |
| #include <canvas/canvastools.hxx> |
| #include <tools/diagnose_ex.h> |
| |
| #include <comphelper/scopeguard.hxx> |
| |
| #include <basegfx/range/b2drectangle.hxx> |
| #include <basegfx/tools/canvastools.hxx> |
| |
| #include <boost/cast.hpp> |
| |
| #include "dx_spritecanvashelper.hxx" |
| #include "dx_canvascustomsprite.hxx" |
| |
| #if defined(DX_DEBUG_IMAGES) |
| # if OSL_DEBUG_LEVEL > 0 |
| # include <imdebug.h> |
| # undef min |
| # undef max |
| # endif |
| #endif |
| |
| using namespace ::com::sun::star; |
| |
| namespace dxcanvas |
| { |
| namespace |
| { |
| void repaintBackground( const ::basegfx::B2DRange& rUpdateArea, |
| const ::basegfx::B2IRange& rOutputArea, |
| const DXSurfaceBitmapSharedPtr& rBackBuffer ) |
| { |
| // TODO(E1): Use numeric_cast to catch overflow here |
| ::basegfx::B2IRange aActualArea( 0, 0, |
| static_cast<sal_Int32>(rOutputArea.getWidth()), |
| static_cast<sal_Int32>(rOutputArea.getHeight()) ); |
| aActualArea.intersect( fround( rUpdateArea ) ); |
| |
| // repaint the given area of the screen with background content |
| rBackBuffer->draw(aActualArea); |
| } |
| |
| void spriteRedraw( const ::canvas::Sprite::Reference& rSprite ) |
| { |
| // downcast to derived dxcanvas::Sprite interface, which |
| // provides the actual redraw methods. |
| ::boost::polymorphic_downcast< Sprite* >( |
| rSprite.get() )->redraw(); |
| } |
| |
| void spriteRedrawStub( const ::canvas::Sprite::Reference& rSprite ) |
| { |
| if( rSprite.is() ) |
| { |
| // downcast to derived dxcanvas::Sprite interface, which |
| // provides the actual redraw methods. |
| ::boost::polymorphic_downcast< Sprite* >( |
| rSprite.get() )->redraw(); |
| } |
| } |
| |
| void spriteRedrawStub2( const ::canvas::SpriteRedrawManager::AreaComponent& rComponent ) |
| { |
| if( rComponent.second.getSprite().is() ) |
| { |
| // downcast to derived dxcanvas::Sprite interface, which |
| // provides the actual redraw methods. |
| ::boost::polymorphic_downcast< Sprite* >( |
| rComponent.second.getSprite().get() )->redraw(); |
| } |
| } |
| } |
| |
| SpriteCanvasHelper::SpriteCanvasHelper() : |
| mpSpriteSurface( NULL ), |
| mpRedrawManager( NULL ), |
| mpRenderModule(), |
| mpSurfaceProxy(), |
| mpBackBuffer(), |
| maUpdateRect(), |
| maScrapRect(), |
| mbShowSpriteBounds( false ) |
| { |
| #if defined(VERBOSE) && defined(DBG_UTIL) |
| // inverse default for verbose debug mode |
| mbShowSpriteBounds = true; |
| #endif |
| } |
| |
| void SpriteCanvasHelper::init( SpriteCanvas& rParent, |
| ::canvas::SpriteRedrawManager& rManager, |
| const IDXRenderModuleSharedPtr& rRenderModule, |
| const ::canvas::ISurfaceProxyManagerSharedPtr& rSurfaceProxy, |
| const DXSurfaceBitmapSharedPtr& rBackBuffer, |
| const ::basegfx::B2ISize& rOutputOffset ) |
| { |
| // init base |
| setDevice( rParent ); |
| setTarget( rBackBuffer, rOutputOffset ); |
| |
| mpSpriteSurface = &rParent; |
| mpRedrawManager = &rManager; |
| mpRenderModule = rRenderModule; |
| mpSurfaceProxy = rSurfaceProxy; |
| mpBackBuffer = rBackBuffer; |
| } |
| |
| void SpriteCanvasHelper::disposing() |
| { |
| if(mpRenderModule) |
| mpRenderModule->disposing(); |
| |
| mpBackBuffer.reset(); |
| mpRenderModule.reset(); |
| mpRedrawManager = NULL; |
| mpSpriteSurface = NULL; |
| |
| // forward to base |
| CanvasHelper::disposing(); |
| } |
| |
| uno::Reference< rendering::XAnimatedSprite > SpriteCanvasHelper::createSpriteFromAnimation( |
| const uno::Reference< rendering::XAnimation >& /*animation*/ ) |
| { |
| return uno::Reference< rendering::XAnimatedSprite >(); |
| } |
| |
| uno::Reference< rendering::XAnimatedSprite > SpriteCanvasHelper::createSpriteFromBitmaps( |
| const uno::Sequence< uno::Reference< rendering::XBitmap > >& /*animationBitmaps*/, |
| sal_Int8 /*interpolationMode*/ ) |
| { |
| return uno::Reference< rendering::XAnimatedSprite >(); |
| } |
| |
| uno::Reference< rendering::XCustomSprite > SpriteCanvasHelper::createCustomSprite( const geometry::RealSize2D& spriteSize ) |
| { |
| if( !mpRedrawManager ) |
| return uno::Reference< rendering::XCustomSprite >(); // we're disposed |
| |
| return uno::Reference< rendering::XCustomSprite >( |
| new CanvasCustomSprite( spriteSize, |
| mpSpriteSurface, |
| mpRenderModule, |
| mpSurfaceProxy, |
| mbShowSpriteBounds ) ); |
| } |
| |
| uno::Reference< rendering::XSprite > SpriteCanvasHelper::createClonedSprite( const uno::Reference< rendering::XSprite >& /*original*/ ) |
| { |
| return uno::Reference< rendering::XSprite >(); |
| } |
| |
| sal_Bool SpriteCanvasHelper::updateScreen( const ::basegfx::B2IRectangle& rCurrArea, |
| sal_Bool bUpdateAll, |
| bool& io_bSurfaceDirty ) |
| { |
| if( !mpRedrawManager || |
| !mpRenderModule || |
| !mpBackBuffer ) |
| { |
| return sal_False; // disposed, or otherwise dysfunctional |
| } |
| |
| #if defined(DX_DEBUG_IMAGES) |
| # if OSL_DEBUG_LEVEL > 0 |
| mpBackBuffer->imageDebugger(); |
| # endif |
| #endif |
| |
| // store current output area (need to tunnel that to the |
| // background, scroll, opaque and general sprite repaint |
| // routines) |
| maScrapRect = rCurrArea; |
| |
| // clear area that needs to be blitted to screen beforehand |
| maUpdateRect.reset(); |
| |
| // TODO(P1): Might be worthwile to track areas of background |
| // changes, too. |
| |
| // TODO(P2): Might be worthwhile to use page-flipping only if |
| // a certain percentage of screen area has changed - and |
| // compose directly to the front buffer otherwise. |
| if( !bUpdateAll && !io_bSurfaceDirty ) |
| { |
| // background has not changed, so we're free to optimize |
| // repaint to areas where a sprite has changed |
| |
| // process each independent area of overlapping sprites |
| // separately. |
| mpRedrawManager->forEachSpriteArea( *this ); |
| |
| // flip primary surface to screen |
| // ============================== |
| |
| // perform buffer flipping |
| mpRenderModule->flip( maUpdateRect, |
| rCurrArea ); |
| } |
| else |
| { |
| // limit update to parent window area (ignored for fullscreen) |
| // TODO(E1): Use numeric_cast to catch overflow here |
| const ::basegfx::B2IRectangle aUpdateArea( 0,0, |
| static_cast<sal_Int32>(rCurrArea.getWidth()), |
| static_cast<sal_Int32>(rCurrArea.getHeight()) ); |
| |
| // background has changed, or called requested full |
| // update, or we're performing double buffering via page |
| // flipping, so we currently have no choice but repaint |
| // everything |
| |
| // repaint the whole screen with background content |
| mpBackBuffer->draw(aUpdateArea); |
| |
| // redraw sprites |
| mpRedrawManager->forEachSprite(::std::ptr_fun( &spriteRedraw ) ); |
| |
| // flip primary surface to screen |
| // ============================== |
| |
| // perform buffer flipping |
| mpRenderModule->flip( aUpdateArea, |
| rCurrArea ); |
| } |
| |
| // change record vector must be cleared, for the next turn of |
| // rendering and sprite changing |
| mpRedrawManager->clearChangeRecords(); |
| |
| io_bSurfaceDirty = false; |
| |
| return sal_True; |
| } |
| |
| void SpriteCanvasHelper::backgroundPaint( const ::basegfx::B2DRange& rUpdateRect ) |
| { |
| ENSURE_OR_THROW( mpRenderModule && |
| mpBackBuffer, |
| "SpriteCanvasHelper::backgroundPaint(): NULL device pointer " ); |
| |
| repaintBackground( rUpdateRect, |
| maScrapRect, |
| mpBackBuffer ); |
| } |
| |
| void SpriteCanvasHelper::scrollUpdate( const ::basegfx::B2DRange& /*rMoveStart*/, |
| const ::basegfx::B2DRange& rMoveEnd, |
| const ::canvas::SpriteRedrawManager::UpdateArea& rUpdateArea ) |
| { |
| ENSURE_OR_THROW( mpRenderModule && |
| mpBackBuffer, |
| "SpriteCanvasHelper::scrollUpdate(): NULL device pointer " ); |
| |
| // round rectangles to integer pixel. Note: have to be |
| // extremely careful here, to avoid off-by-one errors for |
| // the destination area: otherwise, the next scroll update |
| // would copy pixel that are not supposed to be part of |
| // the sprite. |
| const ::basegfx::B2IRange& rDestRect( |
| ::canvas::tools::spritePixelAreaFromB2DRange( rMoveEnd ) ); |
| |
| // not much sense in really implementing scrollUpdate here, |
| // since outputting a sprite only partially would result in |
| // expensive clipping. Furthermore, we cannot currently render |
| // 3D directly to the front buffer, thus, would have to blit |
| // the full sprite area, anyway. But at least optimized in the |
| // sense that unnecessary background paints behind the sprites |
| // are avoided. |
| ::std::for_each( rUpdateArea.maComponentList.begin(), |
| rUpdateArea.maComponentList.end(), |
| ::std::ptr_fun( &spriteRedrawStub2 ) ); |
| |
| // repaint uncovered areas from backbuffer - take the |
| // _rounded_ rectangles from above, to have the update |
| // consistent with the scroll above. |
| ::std::vector< ::basegfx::B2DRange > aUncoveredAreas; |
| ::basegfx::computeSetDifference( aUncoveredAreas, |
| rUpdateArea.maTotalBounds, |
| ::basegfx::B2DRange( rDestRect ) ); |
| ::std::for_each( aUncoveredAreas.begin(), |
| aUncoveredAreas.end(), |
| ::boost::bind( &repaintBackground, |
| _1, |
| ::boost::cref(maScrapRect), |
| ::boost::cref(mpBackBuffer) ) ); |
| |
| // TODO(E1): Use numeric_cast to catch overflow here |
| ::basegfx::B2IRange aActualArea( 0, 0, |
| static_cast<sal_Int32>(maScrapRect.getWidth()), |
| static_cast<sal_Int32>(maScrapRect.getHeight()) ); |
| aActualArea.intersect( fround( rUpdateArea.maTotalBounds ) ); |
| |
| // add given update area to the 'blit to foreground' rect |
| maUpdateRect.expand( aActualArea ); |
| } |
| |
| void SpriteCanvasHelper::opaqueUpdate( const ::basegfx::B2DRange& rTotalArea, |
| const ::std::vector< ::canvas::Sprite::Reference >& rSortedUpdateSprites ) |
| { |
| ENSURE_OR_THROW( mpRenderModule && |
| mpBackBuffer, |
| "SpriteCanvasHelper::opaqueUpdate(): NULL device pointer " ); |
| |
| // TODO(P2): optimize this by truly rendering to the front |
| // buffer. Currently, we've the 3D device only for the back |
| // buffer. |
| ::std::for_each( rSortedUpdateSprites.begin(), |
| rSortedUpdateSprites.end(), |
| ::std::ptr_fun( &spriteRedrawStub ) ); |
| |
| // TODO(E1): Use numeric_cast to catch overflow here |
| ::basegfx::B2IRange aActualArea( 0, 0, |
| static_cast<sal_Int32>(maScrapRect.getWidth()), |
| static_cast<sal_Int32>(maScrapRect.getHeight()) ); |
| aActualArea.intersect( fround( rTotalArea ) ); |
| |
| // add given update area to the 'blit to foreground' rect |
| maUpdateRect.expand( aActualArea ); |
| } |
| |
| void SpriteCanvasHelper::genericUpdate( const ::basegfx::B2DRange& rTotalArea, |
| const ::std::vector< ::canvas::Sprite::Reference >& rSortedUpdateSprites ) |
| { |
| ENSURE_OR_THROW( mpRenderModule && |
| mpBackBuffer, |
| "SpriteCanvasHelper::genericUpdate(): NULL device pointer " ); |
| |
| // paint background |
| // ================ |
| |
| // TODO(E1): Use numeric_cast to catch overflow here |
| ::basegfx::B2IRange aActualArea( 0, 0, |
| static_cast<sal_Int32>(maScrapRect.getWidth()), |
| static_cast<sal_Int32>(maScrapRect.getHeight()) ); |
| aActualArea.intersect( fround( rTotalArea ) ); |
| |
| // repaint the given area of the screen with background content |
| mpBackBuffer->draw(aActualArea); |
| |
| // paint sprite |
| // ============ |
| |
| ::std::for_each( rSortedUpdateSprites.begin(), |
| rSortedUpdateSprites.end(), |
| ::std::ptr_fun( &spriteRedrawStub ) ); |
| |
| // add given update area to the 'blit to foreground' rect |
| maUpdateRect.expand( aActualArea ); |
| } |
| } |