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 .
20 #include <sal/config.h>
22 #include <com/sun/star/rendering/XPolyPolygon2D.hpp>
23 #include <com/sun/star/geometry/RealSize2D.hpp>
24 #include <com/sun/star/rendering/XBitmap.hpp>
25 #include <com/sun/star/geometry/IntegerSize2D.hpp>
26 #include <basegfx/matrix/b2dhommatrix.hxx>
27 #include <basegfx/point/b2dpoint.hxx>
28 #include <basegfx/polygon/b2dpolygontools.hxx>
29 #include <basegfx/polygon/b2dpolypolygontools.hxx>
30 #include <basegfx/utils/canvastools.hxx>
31 #include <basegfx/vector/b2dsize.hxx>
32 #include <rtl/math.hxx>
33 #include <tools/diagnose_ex.h>
35 #include <base/canvascustomspritehelper.hxx>
36 #include <canvas/canvastools.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::utils::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::utils::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 std::vector
< ::basegfx::B2DRectangle
> aClipDifferences
;
115 // get all rectangles covered by exactly one
116 // of the polygons (aka XOR)
117 ::basegfx::computeSetDifference(aClipDifferences
,
121 // aClipDifferences now contains the final
122 // update areas, coordinates are still relative
123 // to the sprite origin. before submitting
124 // this area to 'updateSprite()' we need to
125 // translate this area to the final position,
126 // coordinates need to be relative to the
128 for( const auto& rClipDiff
: aClipDifferences
)
130 mpSpriteCanvas
->updateSprite(
133 ::basegfx::B2DRectangle(
134 maPosition
+ rClipDiff
.getMinimum(),
135 maPosition
+ rClipDiff
.getMaximum() ) );
138 // update calls all done
144 // caller needs to perform update calls
148 CanvasCustomSpriteHelper::CanvasCustomSpriteHelper() :
152 mbIsCurrClipRectangle(true),
153 mbIsContentFullyOpaque( false ),
154 mbTransformDirty( true )
158 void CanvasCustomSpriteHelper::init( const geometry::RealSize2D
& rSpriteSize
,
159 const SpriteSurface::Reference
& rOwningSpriteCanvas
)
161 ENSURE_OR_THROW( rOwningSpriteCanvas
,
162 "CanvasCustomSpriteHelper::init(): Invalid owning sprite canvas" );
164 mpSpriteCanvas
= rOwningSpriteCanvas
;
165 maSize
.setX( std::max( 1.0,
166 ceil( rSpriteSize
.Width
) ) ); // round up to nearest int,
167 // enforce sprite to have at
168 // least (1,1) pixel size
169 maSize
.setY( std::max( 1.0,
170 ceil( rSpriteSize
.Height
) ) );
173 void CanvasCustomSpriteHelper::disposing()
175 mpSpriteCanvas
.clear();
178 void CanvasCustomSpriteHelper::clearingContent( const Sprite::Reference
& /*rSprite*/ )
180 // about to clear content to fully transparent
181 mbIsContentFullyOpaque
= false;
184 void CanvasCustomSpriteHelper::checkDrawBitmap( const Sprite::Reference
& rSprite
,
185 const uno::Reference
< rendering::XBitmap
>& xBitmap
,
186 const rendering::ViewState
& viewState
,
187 const rendering::RenderState
& renderState
)
189 // check whether bitmap is non-alpha, and whether its
190 // transformed size covers the whole sprite.
191 if( xBitmap
->hasAlpha() )
194 const geometry::IntegerSize2D
& rInputSize(
195 xBitmap
->getSize() );
196 const ::basegfx::B2DSize
& rOurSize(
197 rSprite
->getSizePixel() );
199 ::basegfx::B2DHomMatrix aTransform
;
201 ::basegfx::B2DRectangle( 0.0,0.0,
204 ::basegfx::B2DRectangle( 0.0,0.0,
207 ::canvas::tools::mergeViewAndRenderTransform(aTransform
,
211 // bitmap is opaque and will fully cover the sprite,
212 // set flag appropriately
213 mbIsContentFullyOpaque
= true;
217 void CanvasCustomSpriteHelper::setAlpha( const Sprite::Reference
& rSprite
,
220 if( !mpSpriteCanvas
)
221 return; // we're disposed
223 if( alpha
!= mfAlpha
)
229 mpSpriteCanvas
->updateSprite( rSprite
,
236 void CanvasCustomSpriteHelper::move( const Sprite::Reference
& rSprite
,
237 const geometry::RealPoint2D
& aNewPos
,
238 const rendering::ViewState
& viewState
,
239 const rendering::RenderState
& renderState
)
241 if( !mpSpriteCanvas
)
242 return; // we're disposed
244 ::basegfx::B2DHomMatrix aTransform
;
245 ::canvas::tools::mergeViewAndRenderTransform(aTransform
,
249 // convert position to device pixel
250 ::basegfx::B2DPoint
aPoint(
251 ::basegfx::unotools::b2DPointFromRealPoint2D(aNewPos
) );
252 aPoint
*= aTransform
;
254 if( aPoint
== maPosition
)
257 const ::basegfx::B2DRectangle
& rBounds
258 = getUpdateArea( ::basegfx::B2DRectangle( 0.0, 0.0,
264 mpSpriteCanvas
->moveSprite( rSprite
,
265 rBounds
.getMinimum(),
266 rBounds
.getMinimum() - maPosition
+ aPoint
,
267 rBounds
.getRange() );
273 void CanvasCustomSpriteHelper::transform( const Sprite::Reference
& rSprite
,
274 const geometry::AffineMatrix2D
& aTransformation
)
276 ::basegfx::B2DHomMatrix aMatrix
;
277 ::basegfx::unotools::homMatrixFromAffineMatrix(aMatrix
,
280 if( maTransform
== aMatrix
)
283 // retrieve bounds before and after transformation change.
284 const ::basegfx::B2DRectangle
& rPrevBounds( getUpdateArea() );
286 maTransform
= aMatrix
;
288 if( !updateClipState( rSprite
) &&
291 mpSpriteCanvas
->updateSprite( rSprite
,
294 mpSpriteCanvas
->updateSprite( rSprite
,
299 mbTransformDirty
= true;
302 void CanvasCustomSpriteHelper::clip( const Sprite::Reference
& rSprite
,
303 const uno::Reference
< rendering::XPolyPolygon2D
>& xClip
)
305 // NULL xClip explicitly allowed here (to clear clipping)
307 // retrieve bounds before and after clip change.
308 const ::basegfx::B2DRectangle
& rPrevBounds( getUpdateArea() );
312 if( !updateClipState( rSprite
) &&
315 mpSpriteCanvas
->updateSprite( rSprite
,
318 mpSpriteCanvas
->updateSprite( rSprite
,
324 void CanvasCustomSpriteHelper::setPriority( const Sprite::Reference
& rSprite
,
327 if( !mpSpriteCanvas
)
328 return; // we're disposed
330 if( nPriority
!= mfPriority
)
332 mfPriority
= nPriority
;
336 mpSpriteCanvas
->updateSprite( rSprite
,
343 void CanvasCustomSpriteHelper::show( const Sprite::Reference
& rSprite
)
345 if( !mpSpriteCanvas
)
346 return; // we're disposed
351 mpSpriteCanvas
->showSprite( rSprite
);
354 // TODO(P1): if clip is the NULL clip (nothing visible),
355 // also save us the update call.
359 mpSpriteCanvas
->updateSprite( rSprite
,
365 void CanvasCustomSpriteHelper::hide( const Sprite::Reference
& rSprite
)
367 if( !mpSpriteCanvas
)
368 return; // we're disposed
373 mpSpriteCanvas
->hideSprite( rSprite
);
376 // TODO(P1): if clip is the NULL clip (nothing visible),
377 // also save us the update call.
381 mpSpriteCanvas
->updateSprite( rSprite
,
387 bool CanvasCustomSpriteHelper::isAreaUpdateOpaque( const ::basegfx::B2DRange
& rUpdateArea
) const
389 if( !mbIsCurrClipRectangle
||
390 !mbIsContentFullyOpaque
||
391 !::rtl::math::approxEqual(mfAlpha
, 1.0) )
393 // sprite either transparent, or clip rect does not
394 // represent exact bounds -> update might not be fully
400 // make sure sprite rect fully covers update area -
401 // although the update area originates from the sprite,
402 // it's by no means guaranteed that it's limited to this
403 // sprite's update area - after all, other sprites might
404 // have been merged, or this sprite is moving.
405 return getUpdateArea().isInside( rUpdateArea
);
409 ::basegfx::B2DRange
CanvasCustomSpriteHelper::getUpdateArea( const ::basegfx::B2DRange
& rBounds
) const
411 // Internal! Only call with locked object mutex!
412 ::basegfx::B2DHomMatrix
aTransform( maTransform
);
413 aTransform
.translate( maPosition
.getX(),
416 // transform bounds at origin, as the sprite transformation is
417 // formulated that way
418 ::basegfx::B2DRectangle aTransformedBounds
;
419 return ::canvas::tools::calcTransformedRectBounds( aTransformedBounds
,
424 ::basegfx::B2DRange
CanvasCustomSpriteHelper::getUpdateArea() const
426 // Internal! Only call with locked object mutex!
428 // return effective sprite rect, i.e. take active clip into
430 if( maCurrClipBounds
.isEmpty() )
431 return getUpdateArea( ::basegfx::B2DRectangle( 0.0, 0.0,
435 return ::basegfx::B2DRectangle(
436 maPosition
+ maCurrClipBounds
.getMinimum(),
437 maPosition
+ maCurrClipBounds
.getMaximum() );
441 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */