Line data Source code
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 : #ifndef INCLUDED_CANVAS_SPRITEREDRAWMANAGER_HXX
21 : #define INCLUDED_CANVAS_SPRITEREDRAWMANAGER_HXX
22 :
23 : #include <basegfx/range/b2dconnectedranges.hxx>
24 : #include <basegfx/point/b2dpoint.hxx>
25 : #include <basegfx/vector/b2dvector.hxx>
26 : #include <basegfx/range/b2drange.hxx>
27 : #include <basegfx/range/b2irange.hxx>
28 : #include <basegfx/matrix/b2dhommatrix.hxx>
29 : #include <canvas/base/spritesurface.hxx>
30 :
31 : #include <list>
32 : #include <vector>
33 : #include <algorithm>
34 :
35 : #include <boost/utility.hpp>
36 : #include <boost/bind.hpp>
37 :
38 : #include <canvas/canvastoolsdllapi.h>
39 :
40 : /* Definition of SpriteRedrawManager class */
41 :
42 : namespace canvas
43 : {
44 : /** This class manages smooth SpriteCanvas updates
45 :
46 : Use this class to handle the ::canvas::SpriteSurface methods,
47 : that track and process sprite update events. Recorded update
48 : events are later grouped by connected areas (i.e. all sprites
49 : that somehow overlap over a rectangular area are grouped
50 : together); the forEachSpriteArea() method calls the passed
51 : functor for each of those connected areas.
52 :
53 : Note that, although this class generally works with IEEE
54 : doubles, the calculation of connected areas happens in the
55 : integer domain - it is generally expected that repaints can
56 : only be divided at pixel boundaries, without causing visible
57 : artifacts. Therefore, sprites that touch the same pixel (but
58 : don't necessarily have the same floating point coordinates
59 : there) will reside in a common sprite area and handled
60 : together in the forEachSpriteArea functor call.
61 : */
62 0 : class CANVASTOOLS_DLLPUBLIC SpriteRedrawManager : private ::boost::noncopyable
63 : {
64 : public:
65 : /** Data container for the connected components list
66 : */
67 0 : class SpriteInfo
68 : {
69 : public:
70 0 : ~SpriteInfo() {}
71 :
72 : /** Create sprite info
73 :
74 : @param rRef
75 : Sprite this info represents (might be the NULL ref)
76 :
77 : @param rTrueUpdateArea
78 : True (un-rounded) update area this sprite has recorded
79 :
80 : @param bNeedsUpdate
81 : When false, this sprite is not a member of the change
82 : record list. Thus, it only needs redraw if within the
83 : update area of other, changed sprites.
84 :
85 : @internal
86 : */
87 0 : SpriteInfo( const Sprite::Reference& rRef,
88 : const ::basegfx::B2DRange& rTrueUpdateArea,
89 : bool bNeedsUpdate ) :
90 : mpSprite( rRef ),
91 : maTrueUpdateArea( rTrueUpdateArea ),
92 : mbNeedsUpdate( bNeedsUpdate ),
93 0 : mbIsPureMove( false )
94 : {
95 0 : }
96 :
97 : /** Create sprite info, specify move type
98 :
99 : @param rRef
100 : Sprite this info represents (might be the NULL ref)
101 :
102 : @param rTrueUpdateArea
103 : True (un-rounded) update area this sprite has recorded
104 :
105 : @param bNeedsUpdate
106 : When false, this sprite is not a member of the change
107 : record list. Thus, it only needs redraw if within the
108 : update area of other, changed sprites.
109 :
110 : @param bIsPureMove
111 : When true, this sprite is _only_ moved, no other
112 : changes happened.
113 :
114 : @internal
115 : */
116 0 : SpriteInfo( const Sprite::Reference& rRef,
117 : const ::basegfx::B2DRange& rTrueUpdateArea,
118 : bool bNeedsUpdate,
119 : bool bIsPureMove ) :
120 : mpSprite( rRef ),
121 : maTrueUpdateArea( rTrueUpdateArea ),
122 : mbNeedsUpdate( bNeedsUpdate ),
123 0 : mbIsPureMove( bIsPureMove )
124 : {
125 0 : }
126 :
127 0 : const Sprite::Reference& getSprite() const { return mpSprite; }
128 :
129 : // #i61843# need to return by value here, to be used safely from bind
130 0 : ::basegfx::B2DRange getUpdateArea() const { return maTrueUpdateArea; }
131 0 : bool needsUpdate() const { return mbNeedsUpdate; }
132 0 : bool isPureMove() const { return mbIsPureMove; }
133 :
134 : private:
135 : Sprite::Reference mpSprite;
136 : ::basegfx::B2DRange maTrueUpdateArea;
137 : bool mbNeedsUpdate;
138 : bool mbIsPureMove;
139 : };
140 :
141 :
142 : /** Helper struct for SpriteTracer template
143 :
144 : This struct stores change information to a sprite's visual
145 : appearance (move, content updated, and the like).
146 : */
147 0 : struct SpriteChangeRecord
148 : {
149 : typedef enum{ none=0, move, update } ChangeType;
150 :
151 : SpriteChangeRecord() :
152 : meChangeType( none ),
153 : mpAffectedSprite(),
154 : maOldPos(),
155 : maUpdateArea()
156 : {
157 : }
158 :
159 0 : SpriteChangeRecord( const Sprite::Reference& rSprite,
160 : const ::basegfx::B2DPoint& rOldPos,
161 : const ::basegfx::B2DPoint& rNewPos,
162 : const ::basegfx::B2DVector& rSpriteSize ) :
163 : meChangeType( move ),
164 : mpAffectedSprite( rSprite ),
165 : maOldPos( rOldPos ),
166 : maUpdateArea( rNewPos.getX(),
167 : rNewPos.getY(),
168 0 : rNewPos.getX() + rSpriteSize.getX(),
169 0 : rNewPos.getY() + rSpriteSize.getY() )
170 : {
171 0 : }
172 :
173 0 : SpriteChangeRecord( const Sprite::Reference& rSprite,
174 : const ::basegfx::B2DPoint& rPos,
175 : const ::basegfx::B2DRange& rUpdateArea ) :
176 : meChangeType( update ),
177 : mpAffectedSprite( rSprite ),
178 : maOldPos( rPos ),
179 0 : maUpdateArea( rUpdateArea )
180 : {
181 0 : }
182 :
183 0 : Sprite::Reference getSprite() const { return mpAffectedSprite; }
184 :
185 : ChangeType meChangeType;
186 : Sprite::Reference mpAffectedSprite;
187 : ::basegfx::B2DPoint maOldPos;
188 : ::basegfx::B2DRange maUpdateArea;
189 : };
190 :
191 : typedef ::std::vector< SpriteChangeRecord > VectorOfChangeRecords;
192 : typedef ::std::list< Sprite::Reference > ListOfSprites;
193 : typedef ::basegfx::B2DConnectedRanges< SpriteInfo > SpriteConnectedRanges;
194 : typedef SpriteConnectedRanges::ComponentType AreaComponent;
195 : typedef SpriteConnectedRanges::ConnectedComponents UpdateArea;
196 : typedef ::std::vector< Sprite::Reference > VectorOfSprites;
197 :
198 : SpriteRedrawManager();
199 :
200 : /** Must be called when user of this object gets
201 : disposed. Frees all internal references.
202 : */
203 : void disposing();
204 :
205 : /** Functor, to be used from forEachSpriteArea
206 : */
207 : template< typename Functor > struct AreaUpdateCaller
208 : {
209 0 : AreaUpdateCaller( Functor& rFunc,
210 : const SpriteRedrawManager& rManager ) :
211 : mrFunc( rFunc ),
212 0 : mrManager( rManager )
213 : {
214 0 : }
215 :
216 0 : void operator()( const UpdateArea& rUpdateArea )
217 : {
218 0 : mrManager.handleArea( mrFunc, rUpdateArea );
219 0 : }
220 :
221 : Functor& mrFunc;
222 : const SpriteRedrawManager& mrManager;
223 : };
224 :
225 : /** Call given functor for each sprite area that needs an
226 : update.
227 :
228 : This method calls the given functor for each update area
229 : (calculated from the sprite change records).
230 :
231 : @tpl Functor
232 : Must provide the following four methods:
233 : <pre>
234 : void backgroundPaint( ::basegfx::B2DRange aUpdateRect );
235 : void scrollUpdate( ::basegfx::B2DRange& o_rMoveStart,
236 : ::basegfx::B2DRange& o_rMoveEnd,
237 : UpdateArea aUpdateArea );
238 : void opaqueUpdate( const ::basegfx::B2DRange& rTotalArea,
239 : const ::std::vector< ::canvas::Sprite::Reference >& rSortedUpdateSprites );
240 : void genericUpdate( const ::basegfx::B2DRange& rTotalArea,
241 : const ::std::vector< ::canvas::Sprite::Reference >& rSortedUpdateSprites );
242 : </pre>
243 : The backgroundPaint() method is called to simply repaint
244 : background content, the scrollUpdate() method is used to
245 : scroll a given area, and paint background in the uncovered
246 : areas, the opaqueUpdate() method is called when a sprite
247 : can be painted in the given area without taking background
248 : content into account, and finally, genericUpdate() is
249 : called for complex updates, where first the background and
250 : then all sprites consecutively have to be repainted.
251 : */
252 0 : template< typename Functor > void forEachSpriteArea( Functor& rFunc ) const
253 : {
254 0 : SpriteConnectedRanges aUpdateAreas;
255 :
256 0 : setupUpdateAreas( aUpdateAreas );
257 :
258 0 : aUpdateAreas.forEachAggregate(
259 0 : AreaUpdateCaller< Functor >( rFunc, *this ) );
260 0 : }
261 :
262 : /** Call given functor for each active sprite.
263 :
264 : This method calls the given functor for each active
265 : sprite, in the order of sprite priority.
266 :
267 : @tpl Functor
268 : Must provide a Functor::operator( Sprite::Reference& )
269 : method.
270 : */
271 0 : template< typename Functor > void forEachSprite( const Functor& rFunc ) const
272 : {
273 0 : ::std::for_each( maSprites.begin(),
274 : maSprites.end(),
275 0 : rFunc );
276 0 : }
277 :
278 : /// Clear sprite change records (typically directly after a screen update)
279 : void clearChangeRecords();
280 :
281 : // SpriteSurface interface, is delegated to e.g. from SpriteCanvas
282 : void showSprite( const Sprite::Reference& rSprite );
283 : void hideSprite( const Sprite::Reference& rSprite );
284 : void moveSprite( const Sprite::Reference& rSprite,
285 : const ::basegfx::B2DPoint& rOldPos,
286 : const ::basegfx::B2DPoint& rNewPos,
287 : const ::basegfx::B2DVector& rSpriteSize );
288 : void updateSprite( const Sprite::Reference& rSprite,
289 : const ::basegfx::B2DPoint& rPos,
290 : const ::basegfx::B2DRange& rUpdateArea );
291 :
292 : /** Internal, handles each distinct component for forEachAggregate()
293 :
294 : The reason why this must be public is that it needs to be
295 : accessible from the AreaUpdateCaller functor.
296 :
297 : @internal
298 : */
299 0 : template< typename Functor > void handleArea( Functor& rFunc,
300 : const UpdateArea& rUpdateArea ) const
301 : {
302 : // check whether this area contains changed sprites at all
303 : // (if not, just ignore it)
304 0 : if( areSpritesChanged( rUpdateArea ) )
305 : {
306 : // at least one of the sprites actually needs an
307 : // update - process whole area.
308 :
309 : // check whether this area could be handled special
310 : // (background paint, direct update, scroll, etc.)
311 0 : ::basegfx::B2DRange aMoveStart;
312 0 : ::basegfx::B2DRange aMoveEnd;
313 0 : if( rUpdateArea.maComponentList.empty() )
314 : {
315 0 : rFunc.backgroundPaint( rUpdateArea.maTotalBounds );
316 : }
317 : else
318 : {
319 : // cache number of sprites in this area (it's a
320 : // list, and both isAreaUpdateScroll() and
321 : // isAreaUpdateOpaque() need it).
322 : const ::std::size_t nNumSprites(
323 0 : rUpdateArea.maComponentList.size() );
324 :
325 0 : if( isAreaUpdateScroll( aMoveStart,
326 : aMoveEnd,
327 : rUpdateArea,
328 : nNumSprites ) )
329 : {
330 0 : rFunc.scrollUpdate( aMoveStart,
331 : aMoveEnd,
332 : rUpdateArea );
333 : }
334 : else
335 : {
336 : // potentially, more than a single sprite
337 : // involved. Have to sort component lists for
338 : // sprite prio.
339 0 : VectorOfSprites aSortedUpdateSprites;
340 : SpriteConnectedRanges::ComponentListType::const_iterator aCurr(
341 0 : rUpdateArea.maComponentList.begin() );
342 : const SpriteConnectedRanges::ComponentListType::const_iterator aEnd(
343 0 : rUpdateArea.maComponentList.end() );
344 0 : while( aCurr != aEnd )
345 : {
346 0 : const Sprite::Reference& rSprite( aCurr->second.getSprite() );
347 0 : if( rSprite.is() )
348 0 : aSortedUpdateSprites.push_back( rSprite );
349 :
350 0 : ++aCurr;
351 : }
352 :
353 0 : ::std::sort( aSortedUpdateSprites.begin(),
354 : aSortedUpdateSprites.end(),
355 0 : SpriteWeakOrder() );
356 :
357 0 : if( isAreaUpdateOpaque( rUpdateArea,
358 : nNumSprites ) )
359 : {
360 0 : rFunc.opaqueUpdate( rUpdateArea.maTotalBounds,
361 : aSortedUpdateSprites );
362 : }
363 : else
364 : {
365 0 : rFunc.genericUpdate( rUpdateArea.maTotalBounds,
366 : aSortedUpdateSprites );
367 0 : }
368 : }
369 : }
370 : }
371 0 : }
372 :
373 : private:
374 : /** Central method of this class. Calculates the set of
375 : disjunct components that need an update.
376 : */
377 : void setupUpdateAreas( SpriteConnectedRanges& rUpdateAreas ) const;
378 :
379 : bool areSpritesChanged( const UpdateArea& rUpdateArea ) const;
380 :
381 : bool isAreaUpdateNotOpaque( const ::basegfx::B2DRange& rUpdateRect,
382 : const AreaComponent& rComponent ) const;
383 :
384 : bool isAreaUpdateOpaque( const UpdateArea& rUpdateArea,
385 : ::std::size_t nNumSprites ) const;
386 :
387 : /** Check whether given update area can be handled by a simple
388 : scroll
389 :
390 : @param o_rMoveStart
391 : Start rect of the move
392 :
393 : @param o_rMoveEnd
394 : End rect of the move. The content must be moved from start
395 : to end rect
396 :
397 : @param rUpdateArea
398 : Area to check for scroll update optimization
399 : */
400 : bool isAreaUpdateScroll( ::basegfx::B2DRange& o_rMoveStart,
401 : ::basegfx::B2DRange& o_rMoveEnd,
402 : const UpdateArea& rUpdateArea,
403 : ::std::size_t nNumSprites ) const;
404 :
405 :
406 : ListOfSprites maSprites; // list of active
407 : // sprite
408 : // objects. this
409 : // list is only
410 : // used for full
411 : // repaints,
412 : // otherwise, we
413 : // rely on the
414 : // active sprites
415 : // itself to notify
416 : // us.
417 :
418 : VectorOfChangeRecords maChangeRecords; // vector of
419 : // sprites
420 : // changes
421 : // since last
422 : // updateScreen()
423 : // call
424 : };
425 : }
426 :
427 : #endif /* INCLUDED_CANVAS_SPRITEREDRAWMANAGER_HXX */
428 :
429 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|