Branch data 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 : :
21 : : #include <canvas/debug.hxx>
22 : : #include <tools/diagnose_ex.h>
23 : : #include <canvas/verbosetrace.hxx>
24 : : #include <canvas/canvastools.hxx>
25 : :
26 : : #include <rtl/math.hxx>
27 : :
28 : : #include <basegfx/matrix/b2dhommatrix.hxx>
29 : : #include <basegfx/point/b2dpoint.hxx>
30 : : #include <basegfx/tools/canvastools.hxx>
31 : : #include <basegfx/polygon/b2dpolygon.hxx>
32 : : #include <basegfx/polygon/b2dpolygontools.hxx>
33 : : #include <basegfx/polygon/b2dpolypolygontools.hxx>
34 : : #include <basegfx/numeric/ftools.hxx>
35 : :
36 : : #include <canvas/base/canvascustomspritehelper.hxx>
37 : :
38 : : using namespace ::com::sun::star;
39 : :
40 : :
41 : : namespace canvas
42 : : {
43 : 0 : bool CanvasCustomSpriteHelper::updateClipState( const Sprite::Reference& rSprite )
44 : : {
45 [ # # ]: 0 : if( !mxClipPoly.is() )
46 : : {
47 : : // empty clip polygon -> everything is visible now
48 : 0 : maCurrClipBounds.reset();
49 : 0 : mbIsCurrClipRectangle = true;
50 : : }
51 : : else
52 : : {
53 [ # # ][ # # ]: 0 : const sal_Int32 nNumClipPolygons( mxClipPoly->getNumberOfPolygons() );
54 : :
55 : : // clip is not empty - determine actual update area
56 : : ::basegfx::B2DPolyPolygon aClipPath(
57 [ # # ]: 0 : polyPolygonFromXPolyPolygon2D( mxClipPoly ) );
58 : :
59 : : // apply sprite transformation also to clip!
60 [ # # ]: 0 : aClipPath.transform( maTransform );
61 : :
62 : : // clip which is about to be set, expressed as a
63 : : // b2drectangle
64 : : const ::basegfx::B2DRectangle& rClipBounds(
65 [ # # ]: 0 : ::basegfx::tools::getRange( aClipPath ) );
66 : :
67 : : const ::basegfx::B2DRectangle aBounds( 0.0, 0.0,
68 : : maSize.getX(),
69 [ # # ]: 0 : maSize.getY() );
70 : :
71 : : // rectangular area which is actually covered by the sprite.
72 : : // coordinates are relative to the sprite origin.
73 [ # # ]: 0 : ::basegfx::B2DRectangle aSpriteRectPixel;
74 : : ::canvas::tools::calcTransformedRectBounds( aSpriteRectPixel,
75 : : aBounds,
76 [ # # ]: 0 : maTransform );
77 : :
78 : : // aClipBoundsA = new clip bound rect, intersected
79 : : // with sprite area
80 : 0 : ::basegfx::B2DRectangle aClipBoundsA(rClipBounds);
81 [ # # ]: 0 : aClipBoundsA.intersect( aSpriteRectPixel );
82 : :
83 [ # # ]: 0 : if( nNumClipPolygons != 1 )
84 : : {
85 : : // clip cannot be a single rectangle -> cannot
86 : : // optimize update
87 : 0 : mbIsCurrClipRectangle = false;
88 : 0 : maCurrClipBounds = aClipBoundsA;
89 : : }
90 : : else
91 : : {
92 : : // new clip could be a single rectangle - check
93 : : // that now:
94 : : const bool bNewClipIsRect(
95 [ # # ][ # # ]: 0 : ::basegfx::tools::isRectangle( aClipPath.getB2DPolygon(0) ) );
[ # # ]
96 : :
97 : : // both new and old clip are truly rectangles
98 : : // - can now take the optimized path
99 : : const bool bUseOptimizedUpdate( bNewClipIsRect &&
100 [ # # ][ # # ]: 0 : mbIsCurrClipRectangle );
101 : :
102 : 0 : const ::basegfx::B2DRectangle aOldBounds( maCurrClipBounds );
103 : :
104 : : // store new current clip type
105 : 0 : maCurrClipBounds = aClipBoundsA;
106 : 0 : mbIsCurrClipRectangle = bNewClipIsRect;
107 : :
108 [ # # ][ # # ]: 0 : if( mbActive &&
109 : : bUseOptimizedUpdate )
110 : : {
111 : : // aClipBoundsB = maCurrClipBounds, i.e. last
112 : : // clip, intersected with sprite area
113 : : typedef ::std::vector< ::basegfx::B2DRectangle > VectorOfRects;
114 [ # # ]: 0 : VectorOfRects aClipDifferences;
115 : :
116 : : // get all rectangles covered by exactly one
117 : : // of the polygons (aka XOR)
118 : : ::basegfx::computeSetDifference(aClipDifferences,
119 : : aClipBoundsA,
120 [ # # ]: 0 : aOldBounds);
121 : :
122 : : // aClipDifferences now contains the final
123 : : // update areas, coordinates are still relative
124 : : // to the sprite origin. before submitting
125 : : // this area to 'updateSprite()' we need to
126 : : // translate this area to the final position,
127 : : // coordinates need to be relative to the
128 : : // spritecanvas.
129 [ # # ]: 0 : VectorOfRects::const_iterator aCurr( aClipDifferences.begin() );
130 [ # # ]: 0 : const VectorOfRects::const_iterator aEnd( aClipDifferences.end() );
131 [ # # ][ # # ]: 0 : while( aCurr != aEnd )
132 : : {
133 : 0 : mpSpriteCanvas->updateSprite(
134 : : rSprite,
135 : : maPosition,
136 : : ::basegfx::B2DRectangle(
137 : : maPosition + aCurr->getMinimum(),
138 [ # # ][ # # ]: 0 : maPosition + aCurr->getMaximum() ) );
[ # # ][ # # ]
139 : 0 : ++aCurr;
140 : : }
141 : :
142 : : // update calls all done
143 : 0 : return true;
144 : : }
145 [ # # ][ # # ]: 0 : }
146 : : }
147 : :
148 : : // caller needs to perform update calls
149 : 0 : return false;
150 : : }
151 : :
152 : 0 : CanvasCustomSpriteHelper::CanvasCustomSpriteHelper() :
153 : : mpSpriteCanvas(),
154 : : maCurrClipBounds(),
155 : : maPosition(),
156 : : maSize(),
157 : : maTransform(),
158 : : mxClipPoly(),
159 : : mfPriority(0.0),
160 : : mfAlpha(0.0),
161 : : mbActive(false),
162 : : mbIsCurrClipRectangle(true),
163 : : mbIsContentFullyOpaque( false ),
164 : : mbAlphaDirty( true ),
165 : : mbPositionDirty( true ),
166 : : mbTransformDirty( true ),
167 : : mbClipDirty( true ),
168 : : mbPrioDirty( true ),
169 [ # # ][ # # ]: 0 : mbVisibilityDirty( true )
170 : : {
171 : 0 : }
172 : :
173 : 0 : void CanvasCustomSpriteHelper::init( const geometry::RealSize2D& rSpriteSize,
174 : : const SpriteSurface::Reference& rOwningSpriteCanvas )
175 : : {
176 [ # # ][ # # ]: 0 : ENSURE_OR_THROW( rOwningSpriteCanvas.get(),
[ # # ][ # # ]
177 : : "CanvasCustomSpriteHelper::init(): Invalid owning sprite canvas" );
178 : :
179 : 0 : mpSpriteCanvas = rOwningSpriteCanvas;
180 : : maSize.setX( ::std::max( 1.0,
181 [ # # ]: 0 : ceil( rSpriteSize.Width ) ) ); // round up to nearest int,
182 : : // enforce sprite to have at
183 : : // least (1,1) pixel size
184 : : maSize.setY( ::std::max( 1.0,
185 [ # # ]: 0 : ceil( rSpriteSize.Height ) ) );
186 : 0 : }
187 : :
188 : 0 : void CanvasCustomSpriteHelper::disposing()
189 : : {
190 : 0 : mpSpriteCanvas.clear();
191 : 0 : }
192 : :
193 : 0 : void CanvasCustomSpriteHelper::clearingContent( const Sprite::Reference& /*rSprite*/ )
194 : : {
195 : : // about to clear content to fully transparent
196 : 0 : mbIsContentFullyOpaque = false;
197 : 0 : }
198 : :
199 : 0 : void CanvasCustomSpriteHelper::checkDrawBitmap( const Sprite::Reference& rSprite,
200 : : const uno::Reference< rendering::XBitmap >& xBitmap,
201 : : const rendering::ViewState& viewState,
202 : : const rendering::RenderState& renderState )
203 : : {
204 : : // check whether bitmap is non-alpha, and whether its
205 : : // transformed size covers the whole sprite.
206 [ # # ]: 0 : if( !xBitmap->hasAlpha() )
207 : : {
208 : : const geometry::IntegerSize2D& rInputSize(
209 [ # # ][ # # ]: 0 : xBitmap->getSize() );
210 : : const ::basegfx::B2DSize& rOurSize(
211 [ # # ]: 0 : rSprite->getSizePixel() );
212 : :
213 [ # # ]: 0 : ::basegfx::B2DHomMatrix aTransform;
214 [ # # ][ # # ]: 0 : if( tools::isInside(
215 : : ::basegfx::B2DRectangle( 0.0,0.0,
216 : : rOurSize.getX(),
217 : : rOurSize.getY() ),
218 : : ::basegfx::B2DRectangle( 0.0,0.0,
219 : : rInputSize.Width,
220 : : rInputSize.Height ),
221 : : ::canvas::tools::mergeViewAndRenderTransform(aTransform,
222 : : viewState,
223 [ # # ][ # # ]: 0 : renderState) ) )
[ # # ]
224 : : {
225 : : // bitmap is opaque and will fully cover the sprite,
226 : : // set flag appropriately
227 : 0 : mbIsContentFullyOpaque = true;
228 [ # # ]: 0 : }
229 : : }
230 : 0 : }
231 : :
232 : 0 : void CanvasCustomSpriteHelper::setAlpha( const Sprite::Reference& rSprite,
233 : : double alpha )
234 : : {
235 [ # # ]: 0 : if( !mpSpriteCanvas.get() )
236 : 0 : return; // we're disposed
237 : :
238 [ # # ]: 0 : if( alpha != mfAlpha )
239 : : {
240 : 0 : mfAlpha = alpha;
241 : :
242 [ # # ]: 0 : if( mbActive )
243 : : {
244 : 0 : mpSpriteCanvas->updateSprite( rSprite,
245 : : maPosition,
246 [ # # ]: 0 : getUpdateArea() );
247 : : }
248 : :
249 : 0 : mbAlphaDirty = true;
250 : : }
251 : : }
252 : :
253 : 0 : void CanvasCustomSpriteHelper::move( const Sprite::Reference& rSprite,
254 : : const geometry::RealPoint2D& aNewPos,
255 : : const rendering::ViewState& viewState,
256 : : const rendering::RenderState& renderState )
257 : : {
258 [ # # ]: 0 : if( !mpSpriteCanvas.get() )
259 : 0 : return; // we're disposed
260 : :
261 [ # # ]: 0 : ::basegfx::B2DHomMatrix aTransform;
262 : : ::canvas::tools::mergeViewAndRenderTransform(aTransform,
263 : : viewState,
264 [ # # ]: 0 : renderState);
265 : :
266 : : // convert position to device pixel
267 : : ::basegfx::B2DPoint aPoint(
268 [ # # ]: 0 : ::basegfx::unotools::b2DPointFromRealPoint2D(aNewPos) );
269 [ # # ]: 0 : aPoint *= aTransform;
270 : :
271 [ # # ]: 0 : if( aPoint != maPosition )
272 : : {
273 [ # # ]: 0 : const ::basegfx::B2DRectangle& rBounds( getFullSpriteRect() );
274 : :
275 [ # # ]: 0 : if( mbActive )
276 : : {
277 : 0 : mpSpriteCanvas->moveSprite( rSprite,
278 : : rBounds.getMinimum(),
279 : : rBounds.getMinimum() - maPosition + aPoint,
280 [ # # ][ # # ]: 0 : rBounds.getRange() );
[ # # ][ # # ]
281 : : }
282 : :
283 : 0 : maPosition = aPoint;
284 : 0 : mbPositionDirty = true;
285 [ # # ]: 0 : }
286 : : }
287 : :
288 : 0 : void CanvasCustomSpriteHelper::transform( const Sprite::Reference& rSprite,
289 : : const geometry::AffineMatrix2D& aTransformation )
290 : : {
291 [ # # ]: 0 : ::basegfx::B2DHomMatrix aMatrix;
292 : : ::basegfx::unotools::homMatrixFromAffineMatrix(aMatrix,
293 [ # # ]: 0 : aTransformation);
294 : :
295 [ # # ][ # # ]: 0 : if( maTransform != aMatrix )
296 : : {
297 : : // retrieve bounds before and after transformation change.
298 [ # # ]: 0 : const ::basegfx::B2DRectangle& rPrevBounds( getUpdateArea() );
299 : :
300 [ # # ]: 0 : maTransform = aMatrix;
301 : :
302 [ # # ][ # # ]: 0 : if( !updateClipState( rSprite ) &&
[ # # ][ # # ]
303 : : mbActive )
304 : : {
305 : 0 : mpSpriteCanvas->updateSprite( rSprite,
306 : : maPosition,
307 [ # # ]: 0 : rPrevBounds );
308 : 0 : mpSpriteCanvas->updateSprite( rSprite,
309 : : maPosition,
310 [ # # ][ # # ]: 0 : getUpdateArea() );
311 : : }
312 : :
313 : 0 : mbTransformDirty = true;
314 [ # # ]: 0 : }
315 : 0 : }
316 : :
317 : 0 : void CanvasCustomSpriteHelper::clip( const Sprite::Reference& rSprite,
318 : : const uno::Reference< rendering::XPolyPolygon2D >& xClip )
319 : : {
320 : : // NULL xClip explicitly allowed here (to clear clipping)
321 : :
322 : : // retrieve bounds before and after clip change.
323 : 0 : const ::basegfx::B2DRectangle& rPrevBounds( getUpdateArea() );
324 : :
325 : 0 : mxClipPoly = xClip;
326 : :
327 [ # # ][ # # ]: 0 : if( !updateClipState( rSprite ) &&
[ # # ]
328 : : mbActive )
329 : : {
330 : 0 : mpSpriteCanvas->updateSprite( rSprite,
331 : : maPosition,
332 : 0 : rPrevBounds );
333 : 0 : mpSpriteCanvas->updateSprite( rSprite,
334 : : maPosition,
335 [ # # ]: 0 : getUpdateArea() );
336 : : }
337 : :
338 : 0 : mbClipDirty = true;
339 : 0 : }
340 : :
341 : 0 : void CanvasCustomSpriteHelper::setPriority( const Sprite::Reference& rSprite,
342 : : double nPriority )
343 : : {
344 [ # # ]: 0 : if( !mpSpriteCanvas.get() )
345 : 0 : return; // we're disposed
346 : :
347 [ # # ]: 0 : if( nPriority != mfPriority )
348 : : {
349 : 0 : mfPriority = nPriority;
350 : :
351 [ # # ]: 0 : if( mbActive )
352 : : {
353 : 0 : mpSpriteCanvas->updateSprite( rSprite,
354 : : maPosition,
355 [ # # ]: 0 : getUpdateArea() );
356 : : }
357 : :
358 : 0 : mbPrioDirty = true;
359 : : }
360 : : }
361 : :
362 : 0 : void CanvasCustomSpriteHelper::show( const Sprite::Reference& rSprite )
363 : : {
364 [ # # ]: 0 : if( !mpSpriteCanvas.get() )
365 : 0 : return; // we're disposed
366 : :
367 [ # # ]: 0 : if( !mbActive )
368 : : {
369 : 0 : mpSpriteCanvas->showSprite( rSprite );
370 : 0 : mbActive = true;
371 : :
372 : : // TODO(P1): if clip is the NULL clip (nothing visible),
373 : : // also save us the update call.
374 : :
375 [ # # ]: 0 : if( mfAlpha != 0.0 )
376 : : {
377 : 0 : mpSpriteCanvas->updateSprite( rSprite,
378 : : maPosition,
379 [ # # ]: 0 : getUpdateArea() );
380 : : }
381 : :
382 : 0 : mbVisibilityDirty = true;
383 : : }
384 : : }
385 : :
386 : 0 : void CanvasCustomSpriteHelper::hide( const Sprite::Reference& rSprite )
387 : : {
388 [ # # ]: 0 : if( !mpSpriteCanvas.get() )
389 : 0 : return; // we're disposed
390 : :
391 [ # # ]: 0 : if( mbActive )
392 : : {
393 : 0 : mpSpriteCanvas->hideSprite( rSprite );
394 : 0 : mbActive = false;
395 : :
396 : : // TODO(P1): if clip is the NULL clip (nothing visible),
397 : : // also save us the update call.
398 : :
399 [ # # ]: 0 : if( mfAlpha != 0.0 )
400 : : {
401 : 0 : mpSpriteCanvas->updateSprite( rSprite,
402 : : maPosition,
403 [ # # ]: 0 : getUpdateArea() );
404 : : }
405 : :
406 : 0 : mbVisibilityDirty = true;
407 : : }
408 : : }
409 : :
410 : : // Sprite interface
411 : 0 : bool CanvasCustomSpriteHelper::isAreaUpdateOpaque( const ::basegfx::B2DRange& rUpdateArea ) const
412 : : {
413 [ # # ]: 0 : if( !mbIsCurrClipRectangle ||
[ # # # # ]
[ # # ]
414 : 0 : !mbIsContentFullyOpaque ||
415 : 0 : !::rtl::math::approxEqual(mfAlpha, 1.0) )
416 : : {
417 : : // sprite either transparent, or clip rect does not
418 : : // represent exact bounds -> update might not be fully
419 : : // opaque
420 : 0 : return false;
421 : : }
422 : : else
423 : : {
424 : : // make sure sprite rect fully covers update area -
425 : : // although the update area originates from the sprite,
426 : : // it's by no means guaranteed that it's limited to this
427 : : // sprite's update area - after all, other sprites might
428 : : // have been merged, or this sprite is moving.
429 [ # # ]: 0 : return getUpdateArea().isInside( rUpdateArea );
430 : : }
431 : : }
432 : :
433 : 0 : ::basegfx::B2DPoint CanvasCustomSpriteHelper::getPosPixel() const
434 : : {
435 : 0 : return maPosition;
436 : : }
437 : :
438 : 0 : ::basegfx::B2DVector CanvasCustomSpriteHelper::getSizePixel() const
439 : : {
440 : 0 : return maSize;
441 : : }
442 : :
443 : 0 : ::basegfx::B2DRange CanvasCustomSpriteHelper::getUpdateArea( const ::basegfx::B2DRange& rBounds ) const
444 : : {
445 : : // Internal! Only call with locked object mutex!
446 [ # # ]: 0 : ::basegfx::B2DHomMatrix aTransform( maTransform );
447 : : aTransform.translate( maPosition.getX(),
448 [ # # ]: 0 : maPosition.getY() );
449 : :
450 : : // transform bounds at origin, as the sprite transformation is
451 : : // formulated that way
452 [ # # ]: 0 : ::basegfx::B2DRectangle aTransformedBounds;
453 : : return ::canvas::tools::calcTransformedRectBounds( aTransformedBounds,
454 : : rBounds,
455 [ # # ][ # # ]: 0 : aTransform );
456 : : }
457 : :
458 : 0 : ::basegfx::B2DRange CanvasCustomSpriteHelper::getUpdateArea() const
459 : : {
460 : : // Internal! Only call with locked object mutex!
461 : :
462 : : // return effective sprite rect, i.e. take active clip into
463 : : // account
464 [ # # ]: 0 : if( maCurrClipBounds.isEmpty() )
465 : : return getUpdateArea( ::basegfx::B2DRectangle( 0.0, 0.0,
466 : : maSize.getX(),
467 [ # # ]: 0 : maSize.getY() ) );
468 : : else
469 : : return ::basegfx::B2DRectangle(
470 : : maPosition + maCurrClipBounds.getMinimum(),
471 [ # # ][ # # ]: 0 : maPosition + maCurrClipBounds.getMaximum() );
472 : : }
473 : :
474 : 0 : double CanvasCustomSpriteHelper::getPriority() const
475 : : {
476 : 0 : return mfPriority;
477 : : }
478 : :
479 : 0 : ::basegfx::B2DRange CanvasCustomSpriteHelper::getFullSpriteRect() const
480 : : {
481 : : // Internal! Only call with locked object mutex!
482 : : return getUpdateArea( ::basegfx::B2DRectangle( 0.0, 0.0,
483 : : maSize.getX(),
484 [ # # ]: 0 : maSize.getY() ) );
485 : : }
486 : : }
487 : :
488 : : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|