1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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
;
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;
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
64 const ::basegfx::B2DRectangle
& rClipBounds(
65 ::basegfx::tools::getRange( aClipPath
) );
67 const ::basegfx::B2DRectangle
aBounds( 0.0, 0.0,
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
,
78 // aClipBoundsA = new clip bound rect, intersected
80 ::basegfx::B2DRectangle
aClipBoundsA(rClipBounds
);
81 aClipBoundsA
.intersect( aSpriteRectPixel
);
83 if( nNumClipPolygons
!= 1 )
85 // clip cannot be a single rectangle -> cannot
87 mbIsCurrClipRectangle
= false;
88 maCurrClipBounds
= aClipBoundsA
;
92 // new clip could be a single rectangle - check
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
;
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
,
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
129 VectorOfRects::const_iterator
aCurr( aClipDifferences
.begin() );
130 const VectorOfRects::const_iterator
aEnd( aClipDifferences
.end() );
131 while( aCurr
!= aEnd
)
133 mpSpriteCanvas
->updateSprite(
136 ::basegfx::B2DRectangle(
137 maPosition
+ aCurr
->getMinimum(),
138 maPosition
+ aCurr
->getMaximum() ) );
142 // update calls all done
148 // caller needs to perform update calls
152 CanvasCustomSpriteHelper::CanvasCustomSpriteHelper() :
162 mbIsCurrClipRectangle(true),
163 mbIsContentFullyOpaque( false ),
164 mbAlphaDirty( true ),
165 mbPositionDirty( true ),
166 mbTransformDirty( 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
;
215 ::basegfx::B2DRectangle( 0.0,0.0,
218 ::basegfx::B2DRectangle( 0.0,0.0,
221 ::canvas::tools::mergeViewAndRenderTransform(aTransform
,
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
,
235 if( !mpSpriteCanvas
.get() )
236 return; // we're disposed
238 if( alpha
!= mfAlpha
)
244 mpSpriteCanvas
->updateSprite( rSprite
,
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
,
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() );
277 mpSpriteCanvas
->moveSprite( rSprite
,
278 rBounds
.getMinimum(),
279 rBounds
.getMinimum() - maPosition
+ aPoint
,
280 rBounds
.getRange() );
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
,
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
) &&
305 mpSpriteCanvas
->updateSprite( rSprite
,
308 mpSpriteCanvas
->updateSprite( rSprite
,
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() );
327 if( !updateClipState( rSprite
) &&
330 mpSpriteCanvas
->updateSprite( rSprite
,
333 mpSpriteCanvas
->updateSprite( rSprite
,
341 void CanvasCustomSpriteHelper::setPriority( const Sprite::Reference
& rSprite
,
344 if( !mpSpriteCanvas
.get() )
345 return; // we're disposed
347 if( nPriority
!= mfPriority
)
349 mfPriority
= nPriority
;
353 mpSpriteCanvas
->updateSprite( rSprite
,
362 void CanvasCustomSpriteHelper::show( const Sprite::Reference
& rSprite
)
364 if( !mpSpriteCanvas
.get() )
365 return; // we're disposed
369 mpSpriteCanvas
->showSprite( rSprite
);
372 // TODO(P1): if clip is the NULL clip (nothing visible),
373 // also save us the update call.
377 mpSpriteCanvas
->updateSprite( rSprite
,
382 mbVisibilityDirty
= true;
386 void CanvasCustomSpriteHelper::hide( const Sprite::Reference
& rSprite
)
388 if( !mpSpriteCanvas
.get() )
389 return; // we're disposed
393 mpSpriteCanvas
->hideSprite( rSprite
);
396 // TODO(P1): if clip is the NULL clip (nothing visible),
397 // also save us the update call.
401 mpSpriteCanvas
->updateSprite( rSprite
,
406 mbVisibilityDirty
= true;
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
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(),
442 // transform bounds at origin, as the sprite transformation is
443 // formulated that way
444 ::basegfx::B2DRectangle aTransformedBounds
;
445 return ::canvas::tools::calcTransformedRectBounds( aTransformedBounds
,
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
456 if( maCurrClipBounds
.isEmpty() )
457 return getUpdateArea( ::basegfx::B2DRectangle( 0.0, 0.0,
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,
476 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */