Line data Source code
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 :
21 : #include <canvas/debug.hxx>
22 : #include <tools/diagnose_ex.h>
23 : #include <canvas/verbosetrace.hxx>
24 : #include <canvas/canvastools.hxx>
25 :
26 : #include <rtl/math.hxx>
27 :
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>
35 :
36 : #include <canvas/base/canvascustomspritehelper.hxx>
37 :
38 : using namespace ::com::sun::star;
39 :
40 :
41 : namespace canvas
42 : {
43 0 : bool CanvasCustomSpriteHelper::updateClipState( const Sprite::Reference& rSprite )
44 : {
45 0 : if( !mxClipPoly.is() )
46 : {
47 : // empty clip polygon -> everything is visible now
48 0 : maCurrClipBounds.reset();
49 0 : mbIsCurrClipRectangle = true;
50 : }
51 : else
52 : {
53 0 : const sal_Int32 nNumClipPolygons( mxClipPoly->getNumberOfPolygons() );
54 :
55 : // clip is not empty - determine actual update area
56 : ::basegfx::B2DPolyPolygon aClipPath(
57 0 : polyPolygonFromXPolyPolygon2D( mxClipPoly ) );
58 :
59 : // apply sprite transformation also to clip!
60 0 : aClipPath.transform( maTransform );
61 :
62 : // clip which is about to be set, expressed as a
63 : // b2drectangle
64 : const ::basegfx::B2DRectangle& rClipBounds(
65 0 : ::basegfx::tools::getRange( aClipPath ) );
66 :
67 : const ::basegfx::B2DRectangle aBounds( 0.0, 0.0,
68 : maSize.getX(),
69 0 : maSize.getY() );
70 :
71 : // rectangular area which is actually covered by the sprite.
72 : // coordinates are relative to the sprite origin.
73 0 : ::basegfx::B2DRectangle aSpriteRectPixel;
74 : ::canvas::tools::calcTransformedRectBounds( aSpriteRectPixel,
75 : aBounds,
76 0 : maTransform );
77 :
78 : // aClipBoundsA = new clip bound rect, intersected
79 : // with sprite area
80 0 : ::basegfx::B2DRectangle aClipBoundsA(rClipBounds);
81 0 : aClipBoundsA.intersect( aSpriteRectPixel );
82 :
83 0 : if( nNumClipPolygons != 1 )
84 : {
85 : // clip cannot be a single rectangle -> cannot
86 : // optimize update
87 0 : mbIsCurrClipRectangle = false;
88 0 : maCurrClipBounds = aClipBoundsA;
89 : }
90 : else
91 : {
92 : // new clip could be a single rectangle - check
93 : // that now:
94 : const bool bNewClipIsRect(
95 0 : ::basegfx::tools::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 &&
100 0 : mbIsCurrClipRectangle );
101 :
102 0 : const ::basegfx::B2DRectangle aOldBounds( maCurrClipBounds );
103 :
104 : // store new current clip type
105 0 : maCurrClipBounds = aClipBoundsA;
106 0 : mbIsCurrClipRectangle = bNewClipIsRect;
107 :
108 0 : if( mbActive &&
109 : bUseOptimizedUpdate )
110 : {
111 : // aClipBoundsB = maCurrClipBounds, i.e. last
112 : // clip, intersected with sprite area
113 : typedef ::std::vector< ::basegfx::B2DRectangle > VectorOfRects;
114 0 : VectorOfRects aClipDifferences;
115 :
116 : // get all rectangles covered by exactly one
117 : // of the polygons (aka XOR)
118 : ::basegfx::computeSetDifference(aClipDifferences,
119 : aClipBoundsA,
120 0 : aOldBounds);
121 :
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
128 : // spritecanvas.
129 0 : VectorOfRects::const_iterator aCurr( aClipDifferences.begin() );
130 0 : const VectorOfRects::const_iterator aEnd( aClipDifferences.end() );
131 0 : while( aCurr != aEnd )
132 : {
133 0 : mpSpriteCanvas->updateSprite(
134 : rSprite,
135 : maPosition,
136 : ::basegfx::B2DRectangle(
137 : maPosition + aCurr->getMinimum(),
138 0 : maPosition + aCurr->getMaximum() ) );
139 0 : ++aCurr;
140 : }
141 :
142 : // update calls all done
143 0 : return true;
144 : }
145 0 : }
146 : }
147 :
148 : // caller needs to perform update calls
149 0 : return false;
150 : }
151 :
152 0 : CanvasCustomSpriteHelper::CanvasCustomSpriteHelper() :
153 : mpSpriteCanvas(),
154 : maCurrClipBounds(),
155 : maPosition(),
156 : maSize(),
157 : maTransform(),
158 : mxClipPoly(),
159 : mfPriority(0.0),
160 : mfAlpha(0.0),
161 : mbActive(false),
162 : mbIsCurrClipRectangle(true),
163 : mbIsContentFullyOpaque( false ),
164 : mbAlphaDirty( true ),
165 : mbPositionDirty( true ),
166 : mbTransformDirty( true ),
167 : mbClipDirty( true ),
168 : mbPrioDirty( true ),
169 0 : mbVisibilityDirty( true )
170 : {
171 0 : }
172 :
173 0 : void CanvasCustomSpriteHelper::init( const geometry::RealSize2D& rSpriteSize,
174 : const SpriteSurface::Reference& rOwningSpriteCanvas )
175 : {
176 0 : ENSURE_OR_THROW( rOwningSpriteCanvas.get(),
177 : "CanvasCustomSpriteHelper::init(): Invalid owning sprite canvas" );
178 :
179 0 : mpSpriteCanvas = rOwningSpriteCanvas;
180 : maSize.setX( ::std::max( 1.0,
181 0 : 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 0 : ceil( rSpriteSize.Height ) ) );
186 0 : }
187 :
188 0 : void CanvasCustomSpriteHelper::disposing()
189 : {
190 0 : mpSpriteCanvas.clear();
191 0 : }
192 :
193 0 : void CanvasCustomSpriteHelper::clearingContent( const Sprite::Reference& /*rSprite*/ )
194 : {
195 : // about to clear content to fully transparent
196 0 : mbIsContentFullyOpaque = false;
197 0 : }
198 :
199 0 : void CanvasCustomSpriteHelper::checkDrawBitmap( const Sprite::Reference& rSprite,
200 : const uno::Reference< rendering::XBitmap >& xBitmap,
201 : const rendering::ViewState& viewState,
202 : const rendering::RenderState& renderState )
203 : {
204 : // check whether bitmap is non-alpha, and whether its
205 : // transformed size covers the whole sprite.
206 0 : if( !xBitmap->hasAlpha() )
207 : {
208 : const geometry::IntegerSize2D& rInputSize(
209 0 : xBitmap->getSize() );
210 : const ::basegfx::B2DSize& rOurSize(
211 0 : rSprite->getSizePixel() );
212 :
213 0 : ::basegfx::B2DHomMatrix aTransform;
214 0 : if( tools::isInside(
215 : ::basegfx::B2DRectangle( 0.0,0.0,
216 : rOurSize.getX(),
217 : rOurSize.getY() ),
218 : ::basegfx::B2DRectangle( 0.0,0.0,
219 : rInputSize.Width,
220 : rInputSize.Height ),
221 : ::canvas::tools::mergeViewAndRenderTransform(aTransform,
222 : viewState,
223 0 : renderState) ) )
224 : {
225 : // bitmap is opaque and will fully cover the sprite,
226 : // set flag appropriately
227 0 : mbIsContentFullyOpaque = true;
228 0 : }
229 : }
230 0 : }
231 :
232 0 : void CanvasCustomSpriteHelper::setAlpha( const Sprite::Reference& rSprite,
233 : double alpha )
234 : {
235 0 : if( !mpSpriteCanvas.get() )
236 0 : return; // we're disposed
237 :
238 0 : if( alpha != mfAlpha )
239 : {
240 0 : mfAlpha = alpha;
241 :
242 0 : if( mbActive )
243 : {
244 0 : mpSpriteCanvas->updateSprite( rSprite,
245 : maPosition,
246 0 : getUpdateArea() );
247 : }
248 :
249 0 : mbAlphaDirty = true;
250 : }
251 : }
252 :
253 0 : void CanvasCustomSpriteHelper::move( const Sprite::Reference& rSprite,
254 : const geometry::RealPoint2D& aNewPos,
255 : const rendering::ViewState& viewState,
256 : const rendering::RenderState& renderState )
257 : {
258 0 : if( !mpSpriteCanvas.get() )
259 0 : return; // we're disposed
260 :
261 0 : ::basegfx::B2DHomMatrix aTransform;
262 : ::canvas::tools::mergeViewAndRenderTransform(aTransform,
263 : viewState,
264 0 : renderState);
265 :
266 : // convert position to device pixel
267 : ::basegfx::B2DPoint aPoint(
268 0 : ::basegfx::unotools::b2DPointFromRealPoint2D(aNewPos) );
269 0 : aPoint *= aTransform;
270 :
271 0 : if( aPoint != maPosition )
272 : {
273 0 : const ::basegfx::B2DRectangle& rBounds( getFullSpriteRect() );
274 :
275 0 : if( mbActive )
276 : {
277 0 : mpSpriteCanvas->moveSprite( rSprite,
278 : rBounds.getMinimum(),
279 : rBounds.getMinimum() - maPosition + aPoint,
280 0 : rBounds.getRange() );
281 : }
282 :
283 0 : maPosition = aPoint;
284 0 : mbPositionDirty = true;
285 0 : }
286 : }
287 :
288 0 : void CanvasCustomSpriteHelper::transform( const Sprite::Reference& rSprite,
289 : const geometry::AffineMatrix2D& aTransformation )
290 : {
291 0 : ::basegfx::B2DHomMatrix aMatrix;
292 : ::basegfx::unotools::homMatrixFromAffineMatrix(aMatrix,
293 0 : aTransformation);
294 :
295 0 : if( maTransform != aMatrix )
296 : {
297 : // retrieve bounds before and after transformation change.
298 0 : const ::basegfx::B2DRectangle& rPrevBounds( getUpdateArea() );
299 :
300 0 : maTransform = aMatrix;
301 :
302 0 : if( !updateClipState( rSprite ) &&
303 : mbActive )
304 : {
305 0 : mpSpriteCanvas->updateSprite( rSprite,
306 : maPosition,
307 0 : rPrevBounds );
308 0 : mpSpriteCanvas->updateSprite( rSprite,
309 : maPosition,
310 0 : getUpdateArea() );
311 : }
312 :
313 0 : mbTransformDirty = true;
314 0 : }
315 0 : }
316 :
317 0 : void CanvasCustomSpriteHelper::clip( const Sprite::Reference& rSprite,
318 : const uno::Reference< rendering::XPolyPolygon2D >& xClip )
319 : {
320 : // NULL xClip explicitly allowed here (to clear clipping)
321 :
322 : // retrieve bounds before and after clip change.
323 0 : const ::basegfx::B2DRectangle& rPrevBounds( getUpdateArea() );
324 :
325 0 : mxClipPoly = xClip;
326 :
327 0 : if( !updateClipState( rSprite ) &&
328 : mbActive )
329 : {
330 0 : mpSpriteCanvas->updateSprite( rSprite,
331 : maPosition,
332 0 : rPrevBounds );
333 0 : mpSpriteCanvas->updateSprite( rSprite,
334 : maPosition,
335 0 : getUpdateArea() );
336 : }
337 :
338 0 : mbClipDirty = true;
339 0 : }
340 :
341 0 : void CanvasCustomSpriteHelper::setPriority( const Sprite::Reference& rSprite,
342 : double nPriority )
343 : {
344 0 : if( !mpSpriteCanvas.get() )
345 0 : return; // we're disposed
346 :
347 0 : if( nPriority != mfPriority )
348 : {
349 0 : mfPriority = nPriority;
350 :
351 0 : if( mbActive )
352 : {
353 0 : mpSpriteCanvas->updateSprite( rSprite,
354 : maPosition,
355 0 : getUpdateArea() );
356 : }
357 :
358 0 : mbPrioDirty = true;
359 : }
360 : }
361 :
362 0 : void CanvasCustomSpriteHelper::show( const Sprite::Reference& rSprite )
363 : {
364 0 : if( !mpSpriteCanvas.get() )
365 0 : return; // we're disposed
366 :
367 0 : if( !mbActive )
368 : {
369 0 : mpSpriteCanvas->showSprite( rSprite );
370 0 : mbActive = true;
371 :
372 : // TODO(P1): if clip is the NULL clip (nothing visible),
373 : // also save us the update call.
374 :
375 0 : if( mfAlpha != 0.0 )
376 : {
377 0 : mpSpriteCanvas->updateSprite( rSprite,
378 : maPosition,
379 0 : getUpdateArea() );
380 : }
381 :
382 0 : mbVisibilityDirty = true;
383 : }
384 : }
385 :
386 0 : void CanvasCustomSpriteHelper::hide( const Sprite::Reference& rSprite )
387 : {
388 0 : if( !mpSpriteCanvas.get() )
389 0 : return; // we're disposed
390 :
391 0 : if( mbActive )
392 : {
393 0 : mpSpriteCanvas->hideSprite( rSprite );
394 0 : mbActive = false;
395 :
396 : // TODO(P1): if clip is the NULL clip (nothing visible),
397 : // also save us the update call.
398 :
399 0 : if( mfAlpha != 0.0 )
400 : {
401 0 : mpSpriteCanvas->updateSprite( rSprite,
402 : maPosition,
403 0 : getUpdateArea() );
404 : }
405 :
406 0 : mbVisibilityDirty = true;
407 : }
408 : }
409 :
410 : // Sprite interface
411 0 : bool CanvasCustomSpriteHelper::isAreaUpdateOpaque( const ::basegfx::B2DRange& rUpdateArea ) const
412 : {
413 0 : if( !mbIsCurrClipRectangle ||
414 0 : !mbIsContentFullyOpaque ||
415 0 : !::rtl::math::approxEqual(mfAlpha, 1.0) )
416 : {
417 : // sprite either transparent, or clip rect does not
418 : // represent exact bounds -> update might not be fully
419 : // opaque
420 0 : return false;
421 : }
422 : else
423 : {
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 0 : return getUpdateArea().isInside( rUpdateArea );
430 : }
431 : }
432 :
433 0 : ::basegfx::B2DPoint CanvasCustomSpriteHelper::getPosPixel() const
434 : {
435 0 : return maPosition;
436 : }
437 :
438 0 : ::basegfx::B2DVector CanvasCustomSpriteHelper::getSizePixel() const
439 : {
440 0 : return maSize;
441 : }
442 :
443 0 : ::basegfx::B2DRange CanvasCustomSpriteHelper::getUpdateArea( const ::basegfx::B2DRange& rBounds ) const
444 : {
445 : // Internal! Only call with locked object mutex!
446 0 : ::basegfx::B2DHomMatrix aTransform( maTransform );
447 : aTransform.translate( maPosition.getX(),
448 0 : maPosition.getY() );
449 :
450 : // transform bounds at origin, as the sprite transformation is
451 : // formulated that way
452 0 : ::basegfx::B2DRectangle aTransformedBounds;
453 : return ::canvas::tools::calcTransformedRectBounds( aTransformedBounds,
454 : rBounds,
455 0 : aTransform );
456 : }
457 :
458 0 : ::basegfx::B2DRange CanvasCustomSpriteHelper::getUpdateArea() const
459 : {
460 : // Internal! Only call with locked object mutex!
461 :
462 : // return effective sprite rect, i.e. take active clip into
463 : // account
464 0 : if( maCurrClipBounds.isEmpty() )
465 : return getUpdateArea( ::basegfx::B2DRectangle( 0.0, 0.0,
466 : maSize.getX(),
467 0 : maSize.getY() ) );
468 : else
469 : return ::basegfx::B2DRectangle(
470 : maPosition + maCurrClipBounds.getMinimum(),
471 0 : maPosition + maCurrClipBounds.getMaximum() );
472 : }
473 :
474 0 : double CanvasCustomSpriteHelper::getPriority() const
475 : {
476 0 : return mfPriority;
477 : }
478 :
479 0 : ::basegfx::B2DRange CanvasCustomSpriteHelper::getFullSpriteRect() const
480 : {
481 : // Internal! Only call with locked object mutex!
482 : return getUpdateArea( ::basegfx::B2DRectangle( 0.0, 0.0,
483 : maSize.getX(),
484 0 : maSize.getY() ) );
485 : }
486 : }
487 :
488 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|