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