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 
36 #include <canvas/canvastools.hxx>
37 
38 using namespace ::com::sun::star;
39 
40 
41 namespace canvas
42 {
44  {
45  if( !mxClipPoly.is() )
46  {
47  // empty clip polygon -> everything is visible now
49  mbIsCurrClipRectangle = true;
50  }
51  else
52  {
53  const sal_Int32 nNumClipPolygons( mxClipPoly->getNumberOfPolygons() );
54 
55  // clip is not empty - determine actual update area
56  ::basegfx::B2DPolyPolygon aClipPath(
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,
76  maTransform );
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
87  mbIsCurrClipRectangle = false;
88  maCurrClipBounds = aClipBoundsA;
89  }
90  else
91  {
92  // new clip could be a single rectangle - check
93  // that now:
94  const bool bNewClipIsRect(
95  ::basegfx::utils::isRectangle( aClipPath.getB2DPolygon(0) ) );
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,
132  maPosition,
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  mpSpriteCanvas(),
150  maCurrClipBounds(),
151  maPosition(),
152  maSize(),
153  maTransform(),
154  mxClipPoly(),
155  mfPriority(0.0),
156  mfAlpha(0.0),
157  mbActive(false),
158  mbIsCurrClipRectangle(true),
159  mbIsContentFullyOpaque( false ),
160  mbTransformDirty( true )
161  {
162  }
163 
164  void CanvasCustomSpriteHelper::init( const geometry::RealSize2D& rSpriteSize,
165  const SpriteSurface::Reference& rOwningSpriteCanvas )
166  {
167  ENSURE_OR_THROW( rOwningSpriteCanvas,
168  "CanvasCustomSpriteHelper::init(): Invalid owning sprite canvas" );
169 
170  mpSpriteCanvas = rOwningSpriteCanvas;
171  maSize.setX( std::max( 1.0,
172  ceil( rSpriteSize.Width ) ) ); // round up to nearest int,
173  // enforce sprite to have at
174  // least (1,1) pixel size
175  maSize.setY( std::max( 1.0,
176  ceil( rSpriteSize.Height ) ) );
177  }
178 
180  {
181  mpSpriteCanvas.clear();
182  }
183 
185  {
186  // about to clear content to fully transparent
187  mbIsContentFullyOpaque = false;
188  }
189 
191  const uno::Reference< rendering::XBitmap >& xBitmap,
192  const rendering::ViewState& viewState,
193  const rendering::RenderState& renderState )
194  {
195  // check whether bitmap is non-alpha, and whether its
196  // transformed size covers the whole sprite.
197  if( xBitmap->hasAlpha() )
198  return;
199 
200  const geometry::IntegerSize2D& rInputSize(
201  xBitmap->getSize() );
202  const ::basegfx::B2DSize& rOurSize(
203  rSprite->getSizePixel() );
204 
205  ::basegfx::B2DHomMatrix aTransform;
206  if( tools::isInside(
207  ::basegfx::B2DRectangle( 0.0,0.0,
208  rOurSize.getX(),
209  rOurSize.getY() ),
210  ::basegfx::B2DRectangle( 0.0,0.0,
211  rInputSize.Width,
212  rInputSize.Height ),
214  viewState,
215  renderState) ) )
216  {
217  // bitmap is opaque and will fully cover the sprite,
218  // set flag appropriately
219  mbIsContentFullyOpaque = true;
220  }
221  }
222 
224  double alpha )
225  {
226  if( !mpSpriteCanvas )
227  return; // we're disposed
228 
229  if( alpha != mfAlpha )
230  {
231  mfAlpha = alpha;
232 
233  if( mbActive )
234  {
235  mpSpriteCanvas->updateSprite( rSprite,
236  maPosition,
237  getUpdateArea() );
238  }
239  }
240  }
241 
243  const geometry::RealPoint2D& aNewPos,
244  const rendering::ViewState& viewState,
245  const rendering::RenderState& renderState )
246  {
247  if( !mpSpriteCanvas )
248  return; // we're disposed
249 
250  ::basegfx::B2DHomMatrix aTransform;
252  viewState,
253  renderState);
254 
255  // convert position to device pixel
256  ::basegfx::B2DPoint aPoint(
258  aPoint *= aTransform;
259 
260  if( aPoint == maPosition )
261  return;
262 
263  const ::basegfx::B2DRectangle& rBounds
264  = getUpdateArea( ::basegfx::B2DRectangle( 0.0, 0.0,
265  maSize.getX(),
266  maSize.getY() ) );
267 
268  if( mbActive )
269  {
270  mpSpriteCanvas->moveSprite( rSprite,
271  rBounds.getMinimum(),
272  rBounds.getMinimum() - maPosition + aPoint,
273  rBounds.getRange() );
274  }
275 
276  maPosition = aPoint;
277  }
278 
280  const geometry::AffineMatrix2D& aTransformation )
281  {
282  ::basegfx::B2DHomMatrix aMatrix;
283  ::basegfx::unotools::homMatrixFromAffineMatrix(aMatrix,
284  aTransformation);
285 
286  if( maTransform == aMatrix )
287  return;
288 
289  // retrieve bounds before and after transformation change.
290  const ::basegfx::B2DRectangle& rPrevBounds( getUpdateArea() );
291 
292  maTransform = aMatrix;
293 
294  if( !updateClipState( rSprite ) &&
295  mbActive )
296  {
297  mpSpriteCanvas->updateSprite( rSprite,
298  maPosition,
299  rPrevBounds );
300  mpSpriteCanvas->updateSprite( rSprite,
301  maPosition,
302  getUpdateArea() );
303  }
304 
305  mbTransformDirty = true;
306  }
307 
309  const uno::Reference< rendering::XPolyPolygon2D >& xClip )
310  {
311  // NULL xClip explicitly allowed here (to clear clipping)
312 
313  // retrieve bounds before and after clip change.
314  const ::basegfx::B2DRectangle& rPrevBounds( getUpdateArea() );
315 
316  mxClipPoly = xClip;
317 
318  if( !updateClipState( rSprite ) &&
319  mbActive )
320  {
321  mpSpriteCanvas->updateSprite( rSprite,
322  maPosition,
323  rPrevBounds );
324  mpSpriteCanvas->updateSprite( rSprite,
325  maPosition,
326  getUpdateArea() );
327  }
328  }
329 
331  double nPriority )
332  {
333  if( !mpSpriteCanvas )
334  return; // we're disposed
335 
336  if( nPriority != mfPriority )
337  {
338  mfPriority = nPriority;
339 
340  if( mbActive )
341  {
342  mpSpriteCanvas->updateSprite( rSprite,
343  maPosition,
344  getUpdateArea() );
345  }
346  }
347  }
348 
350  {
351  if( !mpSpriteCanvas )
352  return; // we're disposed
353 
354  if( mbActive )
355  return;
356 
357  mpSpriteCanvas->showSprite( rSprite );
358  mbActive = true;
359 
360  // TODO(P1): if clip is the NULL clip (nothing visible),
361  // also save us the update call.
362 
363  if( mfAlpha != 0.0 )
364  {
365  mpSpriteCanvas->updateSprite( rSprite,
366  maPosition,
367  getUpdateArea() );
368  }
369  }
370 
372  {
373  if( !mpSpriteCanvas )
374  return; // we're disposed
375 
376  if( !mbActive )
377  return;
378 
379  mpSpriteCanvas->hideSprite( rSprite );
380  mbActive = false;
381 
382  // TODO(P1): if clip is the NULL clip (nothing visible),
383  // also save us the update call.
384 
385  if( mfAlpha != 0.0 )
386  {
387  mpSpriteCanvas->updateSprite( rSprite,
388  maPosition,
389  getUpdateArea() );
390  }
391  }
392 
393  bool CanvasCustomSpriteHelper::isAreaUpdateOpaque( const ::basegfx::B2DRange& rUpdateArea ) const
394  {
395  if( !mbIsCurrClipRectangle ||
397  !::rtl::math::approxEqual(mfAlpha, 1.0) )
398  {
399  // sprite either transparent, or clip rect does not
400  // represent exact bounds -> update might not be fully
401  // opaque
402  return false;
403  }
404  else
405  {
406  // make sure sprite rect fully covers update area -
407  // although the update area originates from the sprite,
408  // it's by no means guaranteed that it's limited to this
409  // sprite's update area - after all, other sprites might
410  // have been merged, or this sprite is moving.
411  return getUpdateArea().isInside( rUpdateArea );
412  }
413  }
414 
415  ::basegfx::B2DRange CanvasCustomSpriteHelper::getUpdateArea( const ::basegfx::B2DRange& rBounds ) const
416  {
417  // Internal! Only call with locked object mutex!
418  ::basegfx::B2DHomMatrix aTransform( maTransform );
419  aTransform.translate( maPosition.getX(),
420  maPosition.getY() );
421 
422  // transform bounds at origin, as the sprite transformation is
423  // formulated that way
424  ::basegfx::B2DRectangle aTransformedBounds;
426  rBounds,
427  aTransform );
428  }
429 
431  {
432  // Internal! Only call with locked object mutex!
433 
434  // return effective sprite rect, i.e. take active clip into
435  // account
436  if( maCurrClipBounds.isEmpty() )
437  return getUpdateArea( ::basegfx::B2DRectangle( 0.0, 0.0,
438  maSize.getX(),
439  maSize.getY() ) );
440  else
441  return ::basegfx::B2DRectangle(
444  }
445 }
446 
447 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
void setX(double fX)
SpriteSurface::Reference mpSpriteCanvas
Owning sprite canvas.
::basegfx::B2DHomMatrix & mergeViewAndRenderTransform(::basegfx::B2DHomMatrix &combinedTransform, const rendering::ViewState &viewState, const rendering::RenderState &renderState)
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)
void clearingContent(const Sprite::Reference &rSprite)
need to call this method for XCanvas::clear(), for opacity tracking
::basegfx::B2DRange maCurrClipBounds
Currently active clip area.
bool isAreaUpdateOpaque(const ::basegfx::B2DRange &rUpdateArea) const
const cppcanvas::SpriteCanvasSharedPtr mpSpriteCanvas
double getX() const
#define max(a, b)
Definition: dx_winstuff.hxx:45
::basegfx::B2DRange & calcTransformedRectBounds(::basegfx::B2DRange &outRect, const ::basegfx::B2DRange &inRect, const ::basegfx::B2DHomMatrix &transformation)
Calc the bounding rectangle of a transformed rectangle.
double getY() const
geometry::RealPoint2D maPosition
B2DPoint getMaximum() const
bool isInside(const B2DTuple &rTuple) const
bool isInside(const ::basegfx::B2DRange &rContainedRect, const ::basegfx::B2DRange &rTransformRect, const ::basegfx::B2DHomMatrix &rTransformation)
Check whether a given rectangle is within another transformed rectangle.
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
bool mbActive
void show(const Sprite::Reference &rSprite)
basegfx::B2DHomMatrix maTransform
bool isEmpty() const
bool mbIsCurrClipRectangle
If true, denotes that the current sprite clip is a true rectangle, i.e.
void transform(const Sprite::Reference &rSprite, const css::geometry::AffineMatrix2D &aTransformation)
void intersect(const B2DRange &rRange)
B2DRange getRange(const B2DPolygon &rCandidate)
void hide(const Sprite::Reference &rSprite)
bool mbTransformDirty
True, iff maTransform has changed.
#define ENSURE_OR_THROW(c, m)
css::uno::Reference< css::rendering::XPolyPolygon2D > mxClipPoly
void setY(double fY)
::basegfx::B2DRange getUpdateArea() const
void disposing()
Object is being disposed, release all internal references.
B2DPoint getMinimum() const
void clip(const Sprite::Reference &rSprite, const css::uno::Reference< css::rendering::XPolyPolygon2D > &aClip)
void translate(double fX, double fY)
void setPriority(const Sprite::Reference &rSprite, double nPriority)
void init(const css::geometry::RealSize2D &rSpriteSize, const SpriteSurface::Reference &rOwningSpriteCanvas)
Init helper.
::basegfx::B2DPoint b2DPointFromRealPoint2D(const geometry::RealPoint2D &rPoint)
bool isRectangle(const B2DPolygon &rPoly)
::basegfx::B2IVector maSize
Definition: dx_9rm.cxx:116
virtual ::basegfx::B2DPolyPolygon polyPolygonFromXPolyPolygon2D(css::uno::Reference< css::rendering::XPolyPolygon2D > &xPoly) const =0
Called to convert an API polygon to a basegfx polygon.
bool updateClipState(const Sprite::Reference &rSprite)
Update clip information from current state.