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