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>
33#include <tools/diagnose_ex.h>
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(
195 xBitmap->getSize() );
196 const ::basegfx::B2DSize& rOurSize(
197 rSprite->getSizePixel() );
198
199 ::basegfx::B2DHomMatrix aTransform;
200 if( tools::isInside(
202 rOurSize.getX(),
203 rOurSize.getY() ),
205 rInputSize.Width,
206 rInputSize.Height ),
208 viewState,
209 renderState) ) )
210 {
211 // bitmap is opaque and will fully cover the sprite,
212 // set flag appropriately
214 }
215 }
216
218 double alpha )
219 {
220 if( !mpSpriteCanvas )
221 return; // we're disposed
222
223 if( alpha != mfAlpha )
224 {
225 mfAlpha = alpha;
226
227 if( mbActive )
228 {
229 mpSpriteCanvas->updateSprite( rSprite,
231 getUpdateArea() );
232 }
233 }
234 }
235
237 const geometry::RealPoint2D& aNewPos,
238 const rendering::ViewState& viewState,
239 const rendering::RenderState& renderState )
240 {
241 if( !mpSpriteCanvas )
242 return; // we're disposed
243
244 ::basegfx::B2DHomMatrix aTransform;
246 viewState,
247 renderState);
248
249 // convert position to device pixel
250 ::basegfx::B2DPoint aPoint(
251 ::basegfx::unotools::b2DPointFromRealPoint2D(aNewPos) );
252 aPoint *= aTransform;
253
254 if( aPoint == maPosition )
255 return;
256
257 const ::basegfx::B2DRectangle& rBounds
259 maSize.getX(),
260 maSize.getY() ) );
261
262 if( mbActive )
263 {
264 mpSpriteCanvas->moveSprite( rSprite,
265 rBounds.getMinimum(),
266 rBounds.getMinimum() - maPosition + aPoint,
267 rBounds.getRange() );
268 }
269
270 maPosition = aPoint;
271 }
272
274 const geometry::AffineMatrix2D& aTransformation )
275 {
277 ::basegfx::unotools::homMatrixFromAffineMatrix(aMatrix,
278 aTransformation);
279
280 if( maTransform == aMatrix )
281 return;
282
283 // retrieve bounds before and after transformation change.
284 const ::basegfx::B2DRectangle& rPrevBounds( getUpdateArea() );
285
286 maTransform = aMatrix;
287
288 if( !updateClipState( rSprite ) &&
289 mbActive )
290 {
291 mpSpriteCanvas->updateSprite( rSprite,
293 rPrevBounds );
294 mpSpriteCanvas->updateSprite( rSprite,
296 getUpdateArea() );
297 }
298
299 mbTransformDirty = true;
300 }
301
303 const uno::Reference< rendering::XPolyPolygon2D >& xClip )
304 {
305 // NULL xClip explicitly allowed here (to clear clipping)
306
307 // retrieve bounds before and after clip change.
308 const ::basegfx::B2DRectangle& rPrevBounds( getUpdateArea() );
309
310 mxClipPoly = xClip;
311
312 if( !updateClipState( rSprite ) &&
313 mbActive )
314 {
315 mpSpriteCanvas->updateSprite( rSprite,
317 rPrevBounds );
318 mpSpriteCanvas->updateSprite( rSprite,
320 getUpdateArea() );
321 }
322 }
323
325 double nPriority )
326 {
327 if( !mpSpriteCanvas )
328 return; // we're disposed
329
330 if( nPriority != mfPriority )
331 {
332 mfPriority = nPriority;
333
334 if( mbActive )
335 {
336 mpSpriteCanvas->updateSprite( rSprite,
338 getUpdateArea() );
339 }
340 }
341 }
342
344 {
345 if( !mpSpriteCanvas )
346 return; // we're disposed
347
348 if( mbActive )
349 return;
350
351 mpSpriteCanvas->showSprite( rSprite );
352 mbActive = true;
353
354 // TODO(P1): if clip is the NULL clip (nothing visible),
355 // also save us the update call.
356
357 if( mfAlpha != 0.0 )
358 {
359 mpSpriteCanvas->updateSprite( rSprite,
361 getUpdateArea() );
362 }
363 }
364
366 {
367 if( !mpSpriteCanvas )
368 return; // we're disposed
369
370 if( !mbActive )
371 return;
372
373 mpSpriteCanvas->hideSprite( rSprite );
374 mbActive = false;
375
376 // TODO(P1): if clip is the NULL clip (nothing visible),
377 // also save us the update call.
378
379 if( mfAlpha != 0.0 )
380 {
381 mpSpriteCanvas->updateSprite( rSprite,
383 getUpdateArea() );
384 }
385 }
386
387 bool CanvasCustomSpriteHelper::isAreaUpdateOpaque( const ::basegfx::B2DRange& rUpdateArea ) const
388 {
391 !::rtl::math::approxEqual(mfAlpha, 1.0) )
392 {
393 // sprite either transparent, or clip rect does not
394 // represent exact bounds -> update might not be fully
395 // opaque
396 return false;
397 }
398 else
399 {
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 );
406 }
407 }
408
409 ::basegfx::B2DRange CanvasCustomSpriteHelper::getUpdateArea( const ::basegfx::B2DRange& rBounds ) const
410 {
411 // Internal! Only call with locked object mutex!
413 aTransform.translate( maPosition.getX(),
414 maPosition.getY() );
415
416 // transform bounds at origin, as the sprite transformation is
417 // formulated that way
418 ::basegfx::B2DRectangle aTransformedBounds;
420 rBounds,
421 aTransform );
422 }
423
425 {
426 // Internal! Only call with locked object mutex!
427
428 // return effective sprite rect, i.e. take active clip into
429 // account
431 return getUpdateArea( ::basegfx::B2DRectangle( 0.0, 0.0,
432 maSize.getX(),
433 maSize.getY() ) );
434 else
435 return ::basegfx::B2DRectangle(
438 }
439}
440
441/* 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 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.
bool isRectangle(const ::tools::PolyPolygon &rPolyPoly)
Predicate, to determine whether polygon is actually an axis-aligned rectangle.
Definition: impltools.cxx:138
bool mbActive