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