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