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