LibreOffice Module canvas (master) 1
canvascustomspritehelper.cxx
Go to the documentation of this file.
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#include <sal/config.h>
21
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>
32#include <rtl/math.hxx>
34
37
38using namespace ::com::sun::star;
39
40
41namespace canvas
42{
44 {
45 if( !mxClipPoly.is() )
46 {
47 // empty clip polygon -> everything is visible now
50 }
51 else
52 {
53 const sal_Int32 nNumClipPolygons( mxClipPoly->getNumberOfPolygons() );
54
55 // clip is not empty - determine actual update area
58
59 // apply sprite transformation also to clip!
60 aClipPath.transform( maTransform );
61
62 // clip which is about to be set, expressed as a
63 // b2drectangle
64 const ::basegfx::B2DRectangle& rClipBounds(
65 ::basegfx::utils::getRange( aClipPath ) );
66
67 const ::basegfx::B2DRectangle aBounds( 0.0, 0.0,
68 maSize.getX(),
69 maSize.getY() );
70
71 // rectangular area which is actually covered by the sprite.
72 // coordinates are relative to the sprite origin.
73 ::basegfx::B2DRectangle aSpriteRectPixel;
75 aBounds,
77
78 // aClipBoundsA = new clip bound rect, intersected
79 // with sprite area
80 ::basegfx::B2DRectangle aClipBoundsA(rClipBounds);
81 aClipBoundsA.intersect( aSpriteRectPixel );
82
83 if( nNumClipPolygons != 1 )
84 {
85 // clip cannot be a single rectangle -> cannot
86 // optimize update
88 maCurrClipBounds = aClipBoundsA;
89 }
90 else
91 {
92 // new clip could be a single rectangle - check
93 // that now:
94 const bool bNewClipIsRect(
96
97 // both new and old clip are truly rectangles
98 // - can now take the optimized path
99 const bool bUseOptimizedUpdate( bNewClipIsRect &&
101
102 const ::basegfx::B2DRectangle aOldBounds( maCurrClipBounds );
103
104 // store new current clip type
105 maCurrClipBounds = aClipBoundsA;
106 mbIsCurrClipRectangle = bNewClipIsRect;
107
108 if( mbActive &&
109 bUseOptimizedUpdate )
110 {
111 // aClipBoundsB = maCurrClipBounds, i.e. last
112 // clip, intersected with sprite area
113 std::vector< ::basegfx::B2DRectangle > aClipDifferences;
114
115 // get all rectangles covered by exactly one
116 // of the polygons (aka XOR)
117 ::basegfx::computeSetDifference(aClipDifferences,
118 aClipBoundsA,
119 aOldBounds);
120
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
127 // spritecanvas.
128 for( const auto& rClipDiff : aClipDifferences )
129 {
130 mpSpriteCanvas->updateSprite(
131 rSprite,
134 maPosition + rClipDiff.getMinimum(),
135 maPosition + rClipDiff.getMaximum() ) );
136 }
137
138 // update calls all done
139 return true;
140 }
141 }
142 }
143
144 // caller needs to perform update calls
145 return false;
146 }
147
149 mfPriority(0.0),
150 mfAlpha(0.0),
151 mbActive(false),
152 mbIsCurrClipRectangle(true),
153 mbIsContentFullyOpaque( false ),
154 mbTransformDirty( true )
155 {
156 }
157
158 void CanvasCustomSpriteHelper::init( const geometry::RealSize2D& rSpriteSize,
159 const SpriteSurface::Reference& rOwningSpriteCanvas )
160 {
161 ENSURE_OR_THROW( rOwningSpriteCanvas,
162 "CanvasCustomSpriteHelper::init(): Invalid owning sprite canvas" );
163
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 ) ) );
171 }
172
174 {
175 mpSpriteCanvas.clear();
176 }
177
179 {
180 // about to clear content to fully transparent
182 }
183
185 const uno::Reference< rendering::XBitmap >& xBitmap,
186 const rendering::ViewState& viewState,
187 const rendering::RenderState& renderState )
188 {
189 // check whether bitmap is non-alpha, and whether its
190 // transformed size covers the whole sprite.
191 if( xBitmap->hasAlpha() )
192 return;
193
194 const geometry::IntegerSize2D& rInputSize(xBitmap->getSize());
195 basegfx::B2DSize rOurSize(rSprite->getSizePixel().getX(), rSprite->getSizePixel().getY());
196
197 ::basegfx::B2DHomMatrix aTransform;
198 if( tools::isInside(
200 rOurSize.getWidth(),
201 rOurSize.getHeight() ),
203 rInputSize.Width,
204 rInputSize.Height ),
206 viewState,
207 renderState) ) )
208 {
209 // bitmap is opaque and will fully cover the sprite,
210 // set flag appropriately
212 }
213 }
214
216 double alpha )
217 {
218 if( !mpSpriteCanvas )
219 return; // we're disposed
220
221 if( alpha != mfAlpha )
222 {
223 mfAlpha = alpha;
224
225 if( mbActive )
226 {
227 mpSpriteCanvas->updateSprite( rSprite,
229 getUpdateArea() );
230 }
231 }
232 }
233
235 const geometry::RealPoint2D& aNewPos,
236 const rendering::ViewState& viewState,
237 const rendering::RenderState& renderState )
238 {
239 if( !mpSpriteCanvas )
240 return; // we're disposed
241
242 ::basegfx::B2DHomMatrix aTransform;
244 viewState,
245 renderState);
246
247 // convert position to device pixel
248 ::basegfx::B2DPoint aPoint(
249 ::basegfx::unotools::b2DPointFromRealPoint2D(aNewPos) );
250 aPoint *= aTransform;
251
252 if( aPoint == maPosition )
253 return;
254
255 const ::basegfx::B2DRectangle& rBounds
257 maSize.getX(),
258 maSize.getY() ) );
259
260 if( mbActive )
261 {
262 mpSpriteCanvas->moveSprite( rSprite,
263 rBounds.getMinimum(),
264 rBounds.getMinimum() - maPosition + aPoint,
265 rBounds.getRange() );
266 }
267
268 maPosition = aPoint;
269 }
270
272 const geometry::AffineMatrix2D& aTransformation )
273 {
275 ::basegfx::unotools::homMatrixFromAffineMatrix(aMatrix,
276 aTransformation);
277
278 if( maTransform == aMatrix )
279 return;
280
281 // retrieve bounds before and after transformation change.
282 const ::basegfx::B2DRectangle& rPrevBounds( getUpdateArea() );
283
284 maTransform = aMatrix;
285
286 if( !updateClipState( rSprite ) &&
287 mbActive )
288 {
289 mpSpriteCanvas->updateSprite( rSprite,
291 rPrevBounds );
292 mpSpriteCanvas->updateSprite( rSprite,
294 getUpdateArea() );
295 }
296
297 mbTransformDirty = true;
298 }
299
301 const uno::Reference< rendering::XPolyPolygon2D >& xClip )
302 {
303 // NULL xClip explicitly allowed here (to clear clipping)
304
305 // retrieve bounds before and after clip change.
306 const ::basegfx::B2DRectangle& rPrevBounds( getUpdateArea() );
307
308 mxClipPoly = xClip;
309
310 if( !updateClipState( rSprite ) &&
311 mbActive )
312 {
313 mpSpriteCanvas->updateSprite( rSprite,
315 rPrevBounds );
316 mpSpriteCanvas->updateSprite( rSprite,
318 getUpdateArea() );
319 }
320 }
321
323 double nPriority )
324 {
325 if( !mpSpriteCanvas )
326 return; // we're disposed
327
328 if( nPriority != mfPriority )
329 {
330 mfPriority = nPriority;
331
332 if( mbActive )
333 {
334 mpSpriteCanvas->updateSprite( rSprite,
336 getUpdateArea() );
337 }
338 }
339 }
340
342 {
343 if( !mpSpriteCanvas )
344 return; // we're disposed
345
346 if( mbActive )
347 return;
348
349 mpSpriteCanvas->showSprite( rSprite );
350 mbActive = true;
351
352 // TODO(P1): if clip is the NULL clip (nothing visible),
353 // also save us the update call.
354
355 if( mfAlpha != 0.0 )
356 {
357 mpSpriteCanvas->updateSprite( rSprite,
359 getUpdateArea() );
360 }
361 }
362
364 {
365 if( !mpSpriteCanvas )
366 return; // we're disposed
367
368 if( !mbActive )
369 return;
370
371 mpSpriteCanvas->hideSprite( rSprite );
372 mbActive = false;
373
374 // TODO(P1): if clip is the NULL clip (nothing visible),
375 // also save us the update call.
376
377 if( mfAlpha != 0.0 )
378 {
379 mpSpriteCanvas->updateSprite( rSprite,
381 getUpdateArea() );
382 }
383 }
384
385 bool CanvasCustomSpriteHelper::isAreaUpdateOpaque( const ::basegfx::B2DRange& rUpdateArea ) const
386 {
389 !::rtl::math::approxEqual(mfAlpha, 1.0) )
390 {
391 // sprite either transparent, or clip rect does not
392 // represent exact bounds -> update might not be fully
393 // opaque
394 return false;
395 }
396 else
397 {
398 // make sure sprite rect fully covers update area -
399 // although the update area originates from the sprite,
400 // it's by no means guaranteed that it's limited to this
401 // sprite's update area - after all, other sprites might
402 // have been merged, or this sprite is moving.
403 return getUpdateArea().isInside( rUpdateArea );
404 }
405 }
406
407 ::basegfx::B2DRange CanvasCustomSpriteHelper::getUpdateArea( const ::basegfx::B2DRange& rBounds ) const
408 {
409 // Internal! Only call with locked object mutex!
411 aTransform.translate( maPosition.getX(),
412 maPosition.getY() );
413
414 // transform bounds at origin, as the sprite transformation is
415 // formulated that way
416 ::basegfx::B2DRectangle aTransformedBounds;
418 rBounds,
419 aTransform );
420 }
421
423 {
424 // Internal! Only call with locked object mutex!
425
426 // return effective sprite rect, i.e. take active clip into
427 // account
429 return getUpdateArea( ::basegfx::B2DRectangle( 0.0, 0.0,
430 maSize.getX(),
431 maSize.getY() ) );
432 else
433 return ::basegfx::B2DRectangle(
436 }
437}
438
439/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
void translate(double fX, double fY)
B2DPolygon const & getB2DPolygon(sal_uInt32 nIndex) const
void transform(const basegfx::B2DHomMatrix &rMatrix)
B2DPoint getMaximum() const
B2DPoint getMinimum() const
void intersect(const Range2D &rRange)
bool isInside(const Tuple2D< TYPE > &rTuple) const
bool isEmpty() const
TYPE getWidth() const
TYPE getHeight() const
TYPE getX() const
void setY(TYPE fY)
TYPE getY() const
void setX(TYPE fX)
virtual ::basegfx::B2DPolyPolygon polyPolygonFromXPolyPolygon2D(css::uno::Reference< css::rendering::XPolyPolygon2D > &xPoly) const =0
Called to convert an API polygon to a basegfx polygon.
void clip(const Sprite::Reference &rSprite, const css::uno::Reference< css::rendering::XPolyPolygon2D > &aClip)
void init(const css::geometry::RealSize2D &rSpriteSize, const SpriteSurface::Reference &rOwningSpriteCanvas)
Init helper.
::basegfx::B2DRange getUpdateArea() const
bool isAreaUpdateOpaque(const ::basegfx::B2DRange &rUpdateArea) const
SpriteSurface::Reference mpSpriteCanvas
Owning sprite canvas.
void transform(const Sprite::Reference &rSprite, const css::geometry::AffineMatrix2D &aTransformation)
void hide(const Sprite::Reference &rSprite)
bool mbTransformDirty
True, iff maTransform has changed.
void show(const Sprite::Reference &rSprite)
void setAlpha(const Sprite::Reference &rSprite, double alpha)
void move(const Sprite::Reference &rSprite, const css::geometry::RealPoint2D &aNewPos, const css::rendering::ViewState &viewState, const css::rendering::RenderState &renderState)
bool updateClipState(const Sprite::Reference &rSprite)
Update clip information from current state.
bool mbIsCurrClipRectangle
If true, denotes that the current sprite clip is a true rectangle, i.e.
void checkDrawBitmap(const Sprite::Reference &rSprite, const css::uno::Reference< css::rendering::XBitmap > &xBitmap, const css::rendering::ViewState &viewState, const css::rendering::RenderState &renderState)
need to call this method for XCanvas::drawBitmap(), for opacity tracking
css::uno::Reference< css::rendering::XPolyPolygon2D > mxClipPoly
void disposing()
Object is being disposed, release all internal references.
::basegfx::B2DRange maCurrClipBounds
Currently active clip area.
void setPriority(const Sprite::Reference &rSprite, double nPriority)
void clearingContent(const Sprite::Reference &rSprite)
need to call this method for XCanvas::clear(), for opacity tracking
#define ENSURE_OR_THROW(c, m)
#define max(a, b)
Definition: dx_winstuff.hxx:43
::basegfx::B2DHomMatrix & mergeViewAndRenderTransform(::basegfx::B2DHomMatrix &combinedTransform, const rendering::ViewState &viewState, const rendering::RenderState &renderState)
bool isInside(const ::basegfx::B2DRange &rContainedRect, const ::basegfx::B2DRange &rTransformRect, const ::basegfx::B2DHomMatrix &rTransformation)
Check whether a given rectangle is within another transformed rectangle.
::basegfx::B2DRange & calcTransformedRectBounds(::basegfx::B2DRange &outRect, const ::basegfx::B2DRange &inRect, const ::basegfx::B2DHomMatrix &transformation)
Calc the bounding rectangle of a transformed rectangle.
constexpr double alpha[nDetails]
bool isRectangle(const ::tools::PolyPolygon &rPolyPoly)
Predicate, to determine whether polygon is actually an axis-aligned rectangle.
Definition: impltools.cxx:165
bool mbActive