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 22 #include <boost/cast.hpp> 23 24 #include <basegfx/range/b2drectangle.hxx> 25 #include <basegfx/utils/canvastools.hxx> 26 #include <comphelper/scopeguard.hxx> 27 #include <comphelper/diagnose_ex.hxx> 28 29 #include <canvas/canvastools.hxx> 30 31 #include "dx_canvascustomsprite.hxx" 32 #include "dx_spritecanvashelper.hxx" 33 34 using namespace ::com::sun::star; 35 36 namespace dxcanvas 37 { 38 namespace 39 { repaintBackground(const::basegfx::B2DRange & rUpdateArea,const::basegfx::B2IRange & rOutputArea,const DXSurfaceBitmapSharedPtr & rBackBuffer)40 void repaintBackground( const ::basegfx::B2DRange& rUpdateArea, 41 const ::basegfx::B2IRange& rOutputArea, 42 const DXSurfaceBitmapSharedPtr& rBackBuffer ) 43 { 44 // TODO(E1): Use numeric_cast to catch overflow here 45 ::basegfx::B2IRange aActualArea( 0, 0, 46 static_cast<sal_Int32>(rOutputArea.getWidth()), 47 static_cast<sal_Int32>(rOutputArea.getHeight()) ); 48 aActualArea.intersect( fround( rUpdateArea ) ); 49 50 // repaint the given area of the screen with background content 51 rBackBuffer->draw(aActualArea); 52 } 53 spriteRedraw(const::canvas::Sprite::Reference & rSprite)54 void spriteRedraw( const ::canvas::Sprite::Reference& rSprite ) 55 { 56 // downcast to derived dxcanvas::Sprite interface, which 57 // provides the actual redraw methods. 58 ::boost::polymorphic_downcast< Sprite* >( 59 rSprite.get() )->redraw(); 60 } 61 } 62 SpriteCanvasHelper()63 SpriteCanvasHelper::SpriteCanvasHelper() : 64 mpSpriteSurface( nullptr ), 65 mpRedrawManager( nullptr ), 66 mpRenderModule(), 67 mpSurfaceProxy(), 68 mpBackBuffer(), 69 maUpdateRect(), 70 maScrapRect(), 71 mbShowSpriteBounds( false ) 72 { 73 #if OSL_DEBUG_LEVEL > 0 74 // inverse default for verbose debug mode 75 mbShowSpriteBounds = true; 76 #endif 77 } 78 init(SpriteCanvas & rParent,::canvas::SpriteRedrawManager & rManager,const IDXRenderModuleSharedPtr & rRenderModule,const std::shared_ptr<canvas::ISurfaceProxyManager> & rSurfaceProxy,const DXSurfaceBitmapSharedPtr & rBackBuffer,const::basegfx::B2ISize & rOutputOffset)79 void SpriteCanvasHelper::init( SpriteCanvas& rParent, 80 ::canvas::SpriteRedrawManager& rManager, 81 const IDXRenderModuleSharedPtr& rRenderModule, 82 const std::shared_ptr<canvas::ISurfaceProxyManager>& rSurfaceProxy, 83 const DXSurfaceBitmapSharedPtr& rBackBuffer, 84 const ::basegfx::B2ISize& rOutputOffset ) 85 { 86 // init base 87 setDevice( rParent ); 88 setTarget( rBackBuffer, rOutputOffset ); 89 90 mpSpriteSurface = &rParent; 91 mpRedrawManager = &rManager; 92 mpRenderModule = rRenderModule; 93 mpSurfaceProxy = rSurfaceProxy; 94 mpBackBuffer = rBackBuffer; 95 } 96 disposing()97 void SpriteCanvasHelper::disposing() 98 { 99 if(mpRenderModule) 100 mpRenderModule->disposing(); 101 102 mpBackBuffer.reset(); 103 mpRenderModule.reset(); 104 mpRedrawManager = nullptr; 105 mpSpriteSurface = nullptr; 106 107 // forward to base 108 CanvasHelper::disposing(); 109 } 110 createSpriteFromAnimation(const uno::Reference<rendering::XAnimation> &)111 uno::Reference< rendering::XAnimatedSprite > SpriteCanvasHelper::createSpriteFromAnimation( 112 const uno::Reference< rendering::XAnimation >& /*animation*/ ) 113 { 114 return uno::Reference< rendering::XAnimatedSprite >(); 115 } 116 createSpriteFromBitmaps(const uno::Sequence<uno::Reference<rendering::XBitmap>> &,sal_Int8)117 uno::Reference< rendering::XAnimatedSprite > SpriteCanvasHelper::createSpriteFromBitmaps( 118 const uno::Sequence< uno::Reference< rendering::XBitmap > >& /*animationBitmaps*/, 119 sal_Int8 /*interpolationMode*/ ) 120 { 121 return uno::Reference< rendering::XAnimatedSprite >(); 122 } 123 createCustomSprite(const geometry::RealSize2D & spriteSize)124 uno::Reference< rendering::XCustomSprite > SpriteCanvasHelper::createCustomSprite( const geometry::RealSize2D& spriteSize ) 125 { 126 if( !mpRedrawManager ) 127 return uno::Reference< rendering::XCustomSprite >(); // we're disposed 128 129 return uno::Reference< rendering::XCustomSprite >( 130 new CanvasCustomSprite( spriteSize, 131 mpSpriteSurface, 132 mpRenderModule, 133 mpSurfaceProxy, 134 mbShowSpriteBounds ) ); 135 } 136 createClonedSprite(const uno::Reference<rendering::XSprite> &)137 uno::Reference< rendering::XSprite > SpriteCanvasHelper::createClonedSprite( const uno::Reference< rendering::XSprite >& /*original*/ ) 138 { 139 return uno::Reference< rendering::XSprite >(); 140 } 141 updateScreen(const::basegfx::B2IRectangle & rCurrArea,bool bUpdateAll,bool & io_bSurfaceDirty)142 bool SpriteCanvasHelper::updateScreen( const ::basegfx::B2IRectangle& rCurrArea, 143 bool bUpdateAll, 144 bool& io_bSurfaceDirty ) 145 { 146 if( !mpRedrawManager || 147 !mpRenderModule || 148 !mpBackBuffer ) 149 { 150 return false; // disposed, or otherwise dysfunctional 151 } 152 153 // store current output area (need to tunnel that to the 154 // background, scroll, opaque and general sprite repaint 155 // routines) 156 maScrapRect = rCurrArea; 157 158 // clear area that needs to be blitted to screen beforehand 159 maUpdateRect.reset(); 160 161 // TODO(P1): Might be worthwhile to track areas of background 162 // changes, too. 163 164 // TODO(P2): Might be worthwhile to use page-flipping only if 165 // a certain percentage of screen area has changed - and 166 // compose directly to the front buffer otherwise. 167 if( !bUpdateAll && !io_bSurfaceDirty ) 168 { 169 // background has not changed, so we're free to optimize 170 // repaint to areas where a sprite has changed 171 172 // process each independent area of overlapping sprites 173 // separately. 174 mpRedrawManager->forEachSpriteArea( *this ); 175 176 // flip primary surface to screen 177 // ============================== 178 179 // perform buffer flipping 180 mpRenderModule->flip( maUpdateRect, 181 rCurrArea ); 182 } 183 else 184 { 185 // limit update to parent window area (ignored for fullscreen) 186 // TODO(E1): Use numeric_cast to catch overflow here 187 const ::basegfx::B2IRectangle aUpdateArea( 0,0, 188 static_cast<sal_Int32>(rCurrArea.getWidth()), 189 static_cast<sal_Int32>(rCurrArea.getHeight()) ); 190 191 // background has changed, or called requested full 192 // update, or we're performing double buffering via page 193 // flipping, so we currently have no choice but repaint 194 // everything 195 196 // repaint the whole screen with background content 197 mpBackBuffer->draw(aUpdateArea); 198 199 // redraw sprites 200 mpRedrawManager->forEachSprite( &spriteRedraw ); 201 202 // flip primary surface to screen 203 // ============================== 204 205 // perform buffer flipping 206 mpRenderModule->flip( aUpdateArea, 207 rCurrArea ); 208 } 209 210 // change record vector must be cleared, for the next turn of 211 // rendering and sprite changing 212 mpRedrawManager->clearChangeRecords(); 213 214 io_bSurfaceDirty = false; 215 216 return true; 217 } 218 backgroundPaint(const::basegfx::B2DRange & rUpdateRect)219 void SpriteCanvasHelper::backgroundPaint( const ::basegfx::B2DRange& rUpdateRect ) 220 { 221 ENSURE_OR_THROW( mpRenderModule && 222 mpBackBuffer, 223 "SpriteCanvasHelper::backgroundPaint(): NULL device pointer " ); 224 225 repaintBackground( rUpdateRect, 226 maScrapRect, 227 mpBackBuffer ); 228 } 229 scrollUpdate(const::basegfx::B2DRange &,const::basegfx::B2DRange & rMoveEnd,const::canvas::SpriteRedrawManager::UpdateArea & rUpdateArea)230 void SpriteCanvasHelper::scrollUpdate( const ::basegfx::B2DRange& /*rMoveStart*/, 231 const ::basegfx::B2DRange& rMoveEnd, 232 const ::canvas::SpriteRedrawManager::UpdateArea& rUpdateArea ) 233 { 234 ENSURE_OR_THROW( mpRenderModule && 235 mpBackBuffer, 236 "SpriteCanvasHelper::scrollUpdate(): NULL device pointer " ); 237 238 // round rectangles to integer pixel. Note: have to be 239 // extremely careful here, to avoid off-by-one errors for 240 // the destination area: otherwise, the next scroll update 241 // would copy pixel that are not supposed to be part of 242 // the sprite. 243 const ::basegfx::B2IRange& rDestRect( 244 ::canvas::tools::spritePixelAreaFromB2DRange( rMoveEnd ) ); 245 246 // not much sense in really implementing scrollUpdate here, 247 // since outputting a sprite only partially would result in 248 // expensive clipping. Furthermore, we cannot currently render 249 // 3D directly to the front buffer, thus, would have to blit 250 // the full sprite area, anyway. But at least optimized in the 251 // sense that unnecessary background paints behind the sprites 252 // are avoided. 253 for( const auto& rComponent : rUpdateArea.maComponentList ) 254 { 255 const ::canvas::Sprite::Reference& rSprite( rComponent.second.getSprite() ); 256 257 if( rSprite.is() ) 258 { 259 // downcast to derived dxcanvas::Sprite interface, which 260 // provides the actual redraw methods. 261 ::boost::polymorphic_downcast< Sprite* >( rSprite.get() )->redraw(); 262 } 263 } 264 265 // repaint uncovered areas from backbuffer - take the 266 // _rounded_ rectangles from above, to have the update 267 // consistent with the scroll above. 268 std::vector< ::basegfx::B2DRange > aUncoveredAreas; 269 ::basegfx::computeSetDifference( aUncoveredAreas, 270 rUpdateArea.maTotalBounds, 271 ::basegfx::B2DRange( rDestRect ) ); 272 for( const auto& rUncoveredArea : aUncoveredAreas ) 273 repaintBackground( rUncoveredArea, maScrapRect, mpBackBuffer ); 274 275 // TODO(E1): Use numeric_cast to catch overflow here 276 ::basegfx::B2IRange aActualArea( 0, 0, 277 static_cast<sal_Int32>(maScrapRect.getWidth()), 278 static_cast<sal_Int32>(maScrapRect.getHeight()) ); 279 aActualArea.intersect( fround( rUpdateArea.maTotalBounds ) ); 280 281 // add given update area to the 'blit to foreground' rect 282 maUpdateRect.expand( aActualArea ); 283 } 284 opaqueUpdate(const::basegfx::B2DRange & rTotalArea,const std::vector<::canvas::Sprite::Reference> & rSortedUpdateSprites)285 void SpriteCanvasHelper::opaqueUpdate( const ::basegfx::B2DRange& rTotalArea, 286 const std::vector< ::canvas::Sprite::Reference >& rSortedUpdateSprites ) 287 { 288 ENSURE_OR_THROW( mpRenderModule && 289 mpBackBuffer, 290 "SpriteCanvasHelper::opaqueUpdate(): NULL device pointer " ); 291 292 // TODO(P2): optimize this by truly rendering to the front 293 // buffer. Currently, we've the 3D device only for the back 294 // buffer. 295 for( const auto& rSprite : rSortedUpdateSprites ) 296 { 297 if( rSprite.is() ) 298 { 299 // downcast to derived dxcanvas::Sprite interface, which 300 // provides the actual redraw methods. 301 ::boost::polymorphic_downcast< Sprite* >( rSprite.get() )->redraw(); 302 } 303 } 304 305 // TODO(E1): Use numeric_cast to catch overflow here 306 ::basegfx::B2IRange aActualArea( 0, 0, 307 static_cast<sal_Int32>(maScrapRect.getWidth()), 308 static_cast<sal_Int32>(maScrapRect.getHeight()) ); 309 aActualArea.intersect( fround( rTotalArea ) ); 310 311 // add given update area to the 'blit to foreground' rect 312 maUpdateRect.expand( aActualArea ); 313 } 314 genericUpdate(const::basegfx::B2DRange & rTotalArea,const std::vector<::canvas::Sprite::Reference> & rSortedUpdateSprites)315 void SpriteCanvasHelper::genericUpdate( const ::basegfx::B2DRange& rTotalArea, 316 const std::vector< ::canvas::Sprite::Reference >& rSortedUpdateSprites ) 317 { 318 ENSURE_OR_THROW( mpRenderModule && 319 mpBackBuffer, 320 "SpriteCanvasHelper::genericUpdate(): NULL device pointer " ); 321 322 // paint background 323 // ================ 324 325 // TODO(E1): Use numeric_cast to catch overflow here 326 ::basegfx::B2IRange aActualArea( 0, 0, 327 static_cast<sal_Int32>(maScrapRect.getWidth()), 328 static_cast<sal_Int32>(maScrapRect.getHeight()) ); 329 aActualArea.intersect( fround( rTotalArea ) ); 330 331 // repaint the given area of the screen with background content 332 mpBackBuffer->draw(aActualArea); 333 334 // paint sprite 335 // ============ 336 337 for( const auto& rSprite : rSortedUpdateSprites ) 338 { 339 if( rSprite.is() ) 340 { 341 // downcast to derived dxcanvas::Sprite interface, which 342 // provides the actual redraw methods. 343 ::boost::polymorphic_downcast< Sprite* >( rSprite.get() )->redraw(); 344 } 345 } 346 347 // add given update area to the 'blit to foreground' rect 348 maUpdateRect.expand( aActualArea ); 349 } 350 } 351 352 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ 353
