Branch libreoffice-5-0-4
[LibreOffice.git] / canvas / source / tools / canvascustomspritehelper.cxx
blob92035174da5f4714113b6f373a2d8264b8abc7dd
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
21 #include <canvas/debug.hxx>
22 #include <tools/diagnose_ex.h>
23 #include <canvas/verbosetrace.hxx>
24 #include <canvas/canvastools.hxx>
26 #include <rtl/math.hxx>
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>
36 #include <canvas/base/canvascustomspritehelper.hxx>
38 using namespace ::com::sun::star;
41 namespace canvas
43 bool CanvasCustomSpriteHelper::updateClipState( const Sprite::Reference& rSprite )
45 if( !mxClipPoly.is() )
47 // empty clip polygon -> everything is visible now
48 maCurrClipBounds.reset();
49 mbIsCurrClipRectangle = true;
51 else
53 const sal_Int32 nNumClipPolygons( mxClipPoly->getNumberOfPolygons() );
55 // clip is not empty - determine actual update area
56 ::basegfx::B2DPolyPolygon aClipPath(
57 polyPolygonFromXPolyPolygon2D( mxClipPoly ) );
59 // apply sprite transformation also to clip!
60 aClipPath.transform( maTransform );
62 // clip which is about to be set, expressed as a
63 // b2drectangle
64 const ::basegfx::B2DRectangle& rClipBounds(
65 ::basegfx::tools::getRange( aClipPath ) );
67 const ::basegfx::B2DRectangle aBounds( 0.0, 0.0,
68 maSize.getX(),
69 maSize.getY() );
71 // rectangular area which is actually covered by the sprite.
72 // coordinates are relative to the sprite origin.
73 ::basegfx::B2DRectangle aSpriteRectPixel;
74 ::canvas::tools::calcTransformedRectBounds( aSpriteRectPixel,
75 aBounds,
76 maTransform );
78 // aClipBoundsA = new clip bound rect, intersected
79 // with sprite area
80 ::basegfx::B2DRectangle aClipBoundsA(rClipBounds);
81 aClipBoundsA.intersect( aSpriteRectPixel );
83 if( nNumClipPolygons != 1 )
85 // clip cannot be a single rectangle -> cannot
86 // optimize update
87 mbIsCurrClipRectangle = false;
88 maCurrClipBounds = aClipBoundsA;
90 else
92 // new clip could be a single rectangle - check
93 // that now:
94 const bool bNewClipIsRect(
95 ::basegfx::tools::isRectangle( aClipPath.getB2DPolygon(0) ) );
97 // both new and old clip are truly rectangles
98 // - can now take the optimized path
99 const bool bUseOptimizedUpdate( bNewClipIsRect &&
100 mbIsCurrClipRectangle );
102 const ::basegfx::B2DRectangle aOldBounds( maCurrClipBounds );
104 // store new current clip type
105 maCurrClipBounds = aClipBoundsA;
106 mbIsCurrClipRectangle = bNewClipIsRect;
108 if( mbActive &&
109 bUseOptimizedUpdate )
111 // aClipBoundsB = maCurrClipBounds, i.e. last
112 // clip, intersected with sprite area
113 typedef ::std::vector< ::basegfx::B2DRectangle > VectorOfRects;
114 VectorOfRects aClipDifferences;
116 // get all rectangles covered by exactly one
117 // of the polygons (aka XOR)
118 ::basegfx::computeSetDifference(aClipDifferences,
119 aClipBoundsA,
120 aOldBounds);
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 VectorOfRects::const_iterator aCurr( aClipDifferences.begin() );
130 const VectorOfRects::const_iterator aEnd( aClipDifferences.end() );
131 while( aCurr != aEnd )
133 mpSpriteCanvas->updateSprite(
134 rSprite,
135 maPosition,
136 ::basegfx::B2DRectangle(
137 maPosition + aCurr->getMinimum(),
138 maPosition + aCurr->getMaximum() ) );
139 ++aCurr;
142 // update calls all done
143 return true;
148 // caller needs to perform update calls
149 return false;
152 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 mbVisibilityDirty( true )
173 void CanvasCustomSpriteHelper::init( const geometry::RealSize2D& rSpriteSize,
174 const SpriteSurface::Reference& rOwningSpriteCanvas )
176 ENSURE_OR_THROW( rOwningSpriteCanvas.get(),
177 "CanvasCustomSpriteHelper::init(): Invalid owning sprite canvas" );
179 mpSpriteCanvas = rOwningSpriteCanvas;
180 maSize.setX( ::std::max( 1.0,
181 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 ceil( rSpriteSize.Height ) ) );
188 void CanvasCustomSpriteHelper::disposing()
190 mpSpriteCanvas.clear();
193 void CanvasCustomSpriteHelper::clearingContent( const Sprite::Reference& /*rSprite*/ )
195 // about to clear content to fully transparent
196 mbIsContentFullyOpaque = false;
199 void CanvasCustomSpriteHelper::checkDrawBitmap( const Sprite::Reference& rSprite,
200 const uno::Reference< rendering::XBitmap >& xBitmap,
201 const rendering::ViewState& viewState,
202 const rendering::RenderState& renderState )
204 // check whether bitmap is non-alpha, and whether its
205 // transformed size covers the whole sprite.
206 if( !xBitmap->hasAlpha() )
208 const geometry::IntegerSize2D& rInputSize(
209 xBitmap->getSize() );
210 const ::basegfx::B2DSize& rOurSize(
211 rSprite->getSizePixel() );
213 ::basegfx::B2DHomMatrix aTransform;
214 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 renderState) ) )
225 // bitmap is opaque and will fully cover the sprite,
226 // set flag appropriately
227 mbIsContentFullyOpaque = true;
232 void CanvasCustomSpriteHelper::setAlpha( const Sprite::Reference& rSprite,
233 double alpha )
235 if( !mpSpriteCanvas.get() )
236 return; // we're disposed
238 if( alpha != mfAlpha )
240 mfAlpha = alpha;
242 if( mbActive )
244 mpSpriteCanvas->updateSprite( rSprite,
245 maPosition,
246 getUpdateArea() );
249 mbAlphaDirty = true;
253 void CanvasCustomSpriteHelper::move( const Sprite::Reference& rSprite,
254 const geometry::RealPoint2D& aNewPos,
255 const rendering::ViewState& viewState,
256 const rendering::RenderState& renderState )
258 if( !mpSpriteCanvas.get() )
259 return; // we're disposed
261 ::basegfx::B2DHomMatrix aTransform;
262 ::canvas::tools::mergeViewAndRenderTransform(aTransform,
263 viewState,
264 renderState);
266 // convert position to device pixel
267 ::basegfx::B2DPoint aPoint(
268 ::basegfx::unotools::b2DPointFromRealPoint2D(aNewPos) );
269 aPoint *= aTransform;
271 if( aPoint != maPosition )
273 const ::basegfx::B2DRectangle& rBounds( getFullSpriteRect() );
275 if( mbActive )
277 mpSpriteCanvas->moveSprite( rSprite,
278 rBounds.getMinimum(),
279 rBounds.getMinimum() - maPosition + aPoint,
280 rBounds.getRange() );
283 maPosition = aPoint;
284 mbPositionDirty = true;
288 void CanvasCustomSpriteHelper::transform( const Sprite::Reference& rSprite,
289 const geometry::AffineMatrix2D& aTransformation )
291 ::basegfx::B2DHomMatrix aMatrix;
292 ::basegfx::unotools::homMatrixFromAffineMatrix(aMatrix,
293 aTransformation);
295 if( maTransform != aMatrix )
297 // retrieve bounds before and after transformation change.
298 const ::basegfx::B2DRectangle& rPrevBounds( getUpdateArea() );
300 maTransform = aMatrix;
302 if( !updateClipState( rSprite ) &&
303 mbActive )
305 mpSpriteCanvas->updateSprite( rSprite,
306 maPosition,
307 rPrevBounds );
308 mpSpriteCanvas->updateSprite( rSprite,
309 maPosition,
310 getUpdateArea() );
313 mbTransformDirty = true;
317 void CanvasCustomSpriteHelper::clip( const Sprite::Reference& rSprite,
318 const uno::Reference< rendering::XPolyPolygon2D >& xClip )
320 // NULL xClip explicitly allowed here (to clear clipping)
322 // retrieve bounds before and after clip change.
323 const ::basegfx::B2DRectangle& rPrevBounds( getUpdateArea() );
325 mxClipPoly = xClip;
327 if( !updateClipState( rSprite ) &&
328 mbActive )
330 mpSpriteCanvas->updateSprite( rSprite,
331 maPosition,
332 rPrevBounds );
333 mpSpriteCanvas->updateSprite( rSprite,
334 maPosition,
335 getUpdateArea() );
338 mbClipDirty = true;
341 void CanvasCustomSpriteHelper::setPriority( const Sprite::Reference& rSprite,
342 double nPriority )
344 if( !mpSpriteCanvas.get() )
345 return; // we're disposed
347 if( nPriority != mfPriority )
349 mfPriority = nPriority;
351 if( mbActive )
353 mpSpriteCanvas->updateSprite( rSprite,
354 maPosition,
355 getUpdateArea() );
358 mbPrioDirty = true;
362 void CanvasCustomSpriteHelper::show( const Sprite::Reference& rSprite )
364 if( !mpSpriteCanvas.get() )
365 return; // we're disposed
367 if( !mbActive )
369 mpSpriteCanvas->showSprite( rSprite );
370 mbActive = true;
372 // TODO(P1): if clip is the NULL clip (nothing visible),
373 // also save us the update call.
375 if( mfAlpha != 0.0 )
377 mpSpriteCanvas->updateSprite( rSprite,
378 maPosition,
379 getUpdateArea() );
382 mbVisibilityDirty = true;
386 void CanvasCustomSpriteHelper::hide( const Sprite::Reference& rSprite )
388 if( !mpSpriteCanvas.get() )
389 return; // we're disposed
391 if( mbActive )
393 mpSpriteCanvas->hideSprite( rSprite );
394 mbActive = false;
396 // TODO(P1): if clip is the NULL clip (nothing visible),
397 // also save us the update call.
399 if( mfAlpha != 0.0 )
401 mpSpriteCanvas->updateSprite( rSprite,
402 maPosition,
403 getUpdateArea() );
406 mbVisibilityDirty = true;
410 // Sprite interface
411 bool CanvasCustomSpriteHelper::isAreaUpdateOpaque( const ::basegfx::B2DRange& rUpdateArea ) const
413 if( !mbIsCurrClipRectangle ||
414 !mbIsContentFullyOpaque ||
415 !::rtl::math::approxEqual(mfAlpha, 1.0) )
417 // sprite either transparent, or clip rect does not
418 // represent exact bounds -> update might not be fully
419 // opaque
420 return false;
422 else
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 return getUpdateArea().isInside( rUpdateArea );
435 ::basegfx::B2DRange CanvasCustomSpriteHelper::getUpdateArea( const ::basegfx::B2DRange& rBounds ) const
437 // Internal! Only call with locked object mutex!
438 ::basegfx::B2DHomMatrix aTransform( maTransform );
439 aTransform.translate( maPosition.getX(),
440 maPosition.getY() );
442 // transform bounds at origin, as the sprite transformation is
443 // formulated that way
444 ::basegfx::B2DRectangle aTransformedBounds;
445 return ::canvas::tools::calcTransformedRectBounds( aTransformedBounds,
446 rBounds,
447 aTransform );
450 ::basegfx::B2DRange CanvasCustomSpriteHelper::getUpdateArea() const
452 // Internal! Only call with locked object mutex!
454 // return effective sprite rect, i.e. take active clip into
455 // account
456 if( maCurrClipBounds.isEmpty() )
457 return getUpdateArea( ::basegfx::B2DRectangle( 0.0, 0.0,
458 maSize.getX(),
459 maSize.getY() ) );
460 else
461 return ::basegfx::B2DRectangle(
462 maPosition + maCurrClipBounds.getMinimum(),
463 maPosition + maCurrClipBounds.getMaximum() );
467 ::basegfx::B2DRange CanvasCustomSpriteHelper::getFullSpriteRect() const
469 // Internal! Only call with locked object mutex!
470 return getUpdateArea( ::basegfx::B2DRectangle( 0.0, 0.0,
471 maSize.getX(),
472 maSize.getY() ) );
476 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */