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 : #include <canvas/debug.hxx>
21 : #include <tools/diagnose_ex.h>
22 : #include <canvas/canvastools.hxx>
23 :
24 : #include "eventqueue.hxx"
25 : #include "eventmultiplexer.hxx"
26 : #include "slideview.hxx"
27 : #include "delayevent.hxx"
28 : #include "unoview.hxx"
29 :
30 : #include <cppuhelper/basemutex.hxx>
31 : #include <cppuhelper/compbase2.hxx>
32 : #include <cppuhelper/implementationentry.hxx>
33 : #include <cppuhelper/interfacecontainer.h>
34 : #include <comphelper/make_shared_from_uno.hxx>
35 :
36 : #include <cppcanvas/spritecanvas.hxx>
37 : #include <cppcanvas/customsprite.hxx>
38 : #include <cppcanvas/vclfactory.hxx>
39 : #include <cppcanvas/basegfxfactory.hxx>
40 :
41 : #include <tools/debug.hxx>
42 :
43 : #include <basegfx/range/b1drange.hxx>
44 : #include <basegfx/range/b2drange.hxx>
45 : #include <basegfx/range/b2irange.hxx>
46 : #include <basegfx/point/b2dpoint.hxx>
47 : #include <basegfx/polygon/b2dpolygon.hxx>
48 : #include <basegfx/matrix/b2dhommatrix.hxx>
49 : #include <basegfx/polygon/b2dpolygontools.hxx>
50 : #include <basegfx/polygon/b2dpolypolygontools.hxx>
51 : #include <basegfx/tools/canvastools.hxx>
52 : #include <basegfx/polygon/b2dpolygonclipper.hxx>
53 : #include <basegfx/polygon/b2dpolypolygoncutter.hxx>
54 :
55 : #include <com/sun/star/presentation/XSlideShow.hpp>
56 :
57 : #include <boost/noncopyable.hpp>
58 : #include <boost/bind.hpp>
59 : #include <boost/weak_ptr.hpp>
60 :
61 : #include <vector>
62 : #include <iterator>
63 : #include <algorithm>
64 :
65 : using namespace com::sun::star;
66 :
67 : namespace slideshow {
68 : namespace internal {
69 :
70 : namespace {
71 :
72 : /** Sprite entry, to store sprite plus priority
73 :
74 : The operator<() defines a strict weak ordering of sprites, sort
75 : key is the sprite priority.
76 : */
77 0 : struct SpriteEntry
78 : {
79 0 : SpriteEntry( const cppcanvas::CustomSpriteSharedPtr& rSprite,
80 : double nPrio ) :
81 : mpSprite( rSprite ),
82 0 : mnPriority( nPrio )
83 : {
84 0 : }
85 :
86 0 : bool operator<(const SpriteEntry& rRHS) const
87 : {
88 0 : return mnPriority < rRHS.mnPriority;
89 : }
90 :
91 : boost::weak_ptr< cppcanvas::CustomSprite > mpSprite;
92 : double mnPriority;
93 : };
94 :
95 : typedef std::vector< SpriteEntry > SpriteVector;
96 :
97 :
98 : /** Create a clip polygon for slide views
99 :
100 : @param rClip
101 : Clip to set (can be empty)
102 :
103 : @param rCanvas
104 : Canvas to create the clip polygon for
105 :
106 : @param rUserSize
107 : The size of the view. Note that the returned clip will
108 : <em>always</em> clip to at least the rect defined herein.
109 :
110 : @return the view clip polygon, in view coordinates, which is
111 : guaranteed to at least clip to the view size.
112 : */
113 0 : basegfx::B2DPolyPolygon createClipPolygon( const basegfx::B2DPolyPolygon& rClip,
114 : const cppcanvas::CanvasSharedPtr& /*rCanvas*/,
115 : const basegfx::B2DSize& rUserSize )
116 : {
117 : // setup canvas clipping
118 : // =====================
119 :
120 : // AW: Simplified
121 0 : const basegfx::B2DRange aClipRange(0, 0, rUserSize.getX(), rUserSize.getY());
122 :
123 0 : if(rClip.count())
124 : {
125 0 : return basegfx::tools::clipPolyPolygonOnRange(rClip, aClipRange, true, false);
126 : }
127 : else
128 : {
129 0 : return basegfx::B2DPolyPolygon(basegfx::tools::createPolygonFromRect(aClipRange));
130 : }
131 : }
132 :
133 : /** Prepare given clip polygon to be stored as the current clip
134 :
135 : Note that this is separate from createClipPolygon(), to allow
136 : SlideView implementations to store this intermediate result
137 : (createClipPolygon() has to be called every time the view size
138 : changes)
139 : */
140 0 : basegfx::B2DPolyPolygon prepareClip( const basegfx::B2DPolyPolygon& rClip )
141 : {
142 0 : basegfx::B2DPolyPolygon aClip( rClip );
143 :
144 : // TODO(P2): unnecessary, once XCanvas is correctly handling this
145 : // AW: Should be no longer necessary; tools are now bezier-safe
146 0 : if( aClip.areControlPointsUsed() )
147 0 : aClip = basegfx::tools::adaptiveSubdivideByAngle( aClip );
148 :
149 : // normalize polygon, preparation for clipping
150 : // in updateCanvas()
151 0 : aClip = basegfx::tools::correctOrientations(aClip);
152 0 : aClip = basegfx::tools::solveCrossovers(aClip);
153 0 : aClip = basegfx::tools::stripNeutralPolygons(aClip);
154 0 : aClip = basegfx::tools::stripDispensablePolygons(aClip, false);
155 :
156 0 : return aClip;
157 : }
158 :
159 :
160 0 : void clearRect( ::cppcanvas::CanvasSharedPtr const& pCanvas,
161 : basegfx::B2IRange const& rArea )
162 : {
163 : // convert clip polygon to device coordinate system
164 0 : ::basegfx::B2DPolyPolygon const* pClipPoly( pCanvas->getClip() );
165 0 : if( pClipPoly )
166 : {
167 0 : ::basegfx::B2DPolyPolygon aClipPoly( *pClipPoly );
168 0 : aClipPoly.transform( pCanvas->getTransformation() );
169 0 : pCanvas->setClip( aClipPoly );
170 : }
171 :
172 : // set transformation to identitiy (->device pixel)
173 0 : pCanvas->setTransformation( ::basegfx::B2DHomMatrix() );
174 :
175 : // #i42440# Fill the _full_ background in
176 : // black. Since we had to extend the bitmap by one
177 : // pixel, and the bitmap is initialized white,
178 : // depending on the slide content a one pixel wide
179 : // line will show to the bottom and the right.
180 : const ::basegfx::B2DPolygon aPoly(
181 : ::basegfx::tools::createPolygonFromRect(
182 0 : basegfx::B2DRange(rArea)));
183 :
184 : ::cppcanvas::PolyPolygonSharedPtr pPolyPoly(
185 0 : ::cppcanvas::BaseGfxFactory::getInstance().createPolyPolygon( pCanvas,
186 0 : aPoly ) );
187 :
188 0 : if( pPolyPoly )
189 : {
190 0 : pPolyPoly->setCompositeOp( cppcanvas::CanvasGraphic::SOURCE );
191 0 : pPolyPoly->setRGBAFillColor( 0xFFFFFF00U );
192 0 : pPolyPoly->draw();
193 0 : }
194 :
195 : #if OSL_DEBUG_LEVEL >= 2 && defined(DBG_UTIL)
196 : ::cppcanvas::CanvasSharedPtr pCliplessCanvas( pCanvas->clone() );
197 : pCliplessCanvas->setClip();
198 :
199 : if( pCanvas->getClip() )
200 : {
201 : ::cppcanvas::PolyPolygonSharedPtr pPolyPoly2(
202 : ::cppcanvas::BaseGfxFactory::getInstance().createPolyPolygon( pCliplessCanvas,
203 : aPoly ));
204 : if( pPolyPoly2 )
205 : {
206 : pPolyPoly2->setRGBALineColor( 0x008000FFU );
207 : pPolyPoly2->draw();
208 : }
209 : }
210 : #endif
211 0 : }
212 :
213 : /** Get bounds in pixel
214 :
215 : @param rLayerBounds
216 : Bound rect, in user space coordinates
217 :
218 : @param rTransformation
219 : User space to device pixel transformation
220 :
221 : @return the layer bounds in pixel, extended by one pixel to the
222 : right and bottom
223 : */
224 0 : basegfx::B2IRange getLayerBoundsPixel( basegfx::B2DRange const& rLayerBounds,
225 : basegfx::B2DHomMatrix const& rTransformation )
226 : {
227 0 : ::basegfx::B2DRange aTmpRect;
228 : ::canvas::tools::calcTransformedRectBounds( aTmpRect,
229 : rLayerBounds,
230 0 : rTransformation );
231 :
232 0 : if( aTmpRect.isEmpty() )
233 0 : return ::basegfx::B2IRange();
234 :
235 : // #i42440# Returned layer size is one pixel too small, as
236 : // rendering happens one pixel to the right and below the
237 : // actual bound rect.
238 : return ::basegfx::B2IRange( ::basegfx::fround(aTmpRect.getMinX()),
239 : ::basegfx::fround(aTmpRect.getMinY()),
240 0 : ::basegfx::fround(aTmpRect.getMaxX()) + 1,
241 0 : ::basegfx::fround(aTmpRect.getMaxY()) + 1 );
242 : }
243 :
244 :
245 :
246 :
247 : /** Container class for sprites issued by a ViewLayer
248 :
249 : This class handles the sprite prioritization issues, that are
250 : needed for layer sprites (e.g. the need to re-prioritize sprites
251 : when the layer changes prio).
252 : */
253 0 : class LayerSpriteContainer
254 : {
255 : /** Max fill level of maSprites, before we try to prune it from
256 : deceased sprites
257 : */
258 : enum{ SPRITE_ULLAGE=256 };
259 :
260 : /** All sprites that have been issued by this container (pruned
261 : from time to time, for invalid references). This vector is
262 : kept sorted with increasing sprite priority.
263 : */
264 : SpriteVector maSprites;
265 :
266 : /// Priority of this layer, relative to other view layers
267 : basegfx::B1DRange maLayerPrioRange;
268 :
269 0 : double getSpritePriority( std::size_t nSpriteNum ) const
270 : {
271 : // divide the available layer range equally between all
272 : // sprites, assign upper bound of individual sprite range as
273 : // sprite prio (the layer itself gets assigned the lower bound
274 : // of sprite 0's individual range):
275 :
276 : // | layer 0 | layer 1 | ...
277 : // | sprite 0 | sprite 1 | sprite 0 | sprite 1 | ...
278 0 : return maLayerPrioRange.getMinimum() + maLayerPrioRange.getRange()*(nSpriteNum+1)/(maSprites.size()+1);
279 : }
280 :
281 : /** Rescan sprite vector, and remove deceased sprites (and reset
282 : sprite prio)
283 :
284 : @param aBegin
285 : Iterator to the first entry to rescan
286 : */
287 0 : void updateSprites()
288 : {
289 0 : SpriteVector aValidSprites;
290 :
291 : // check all sprites for validity and set new priority
292 0 : SpriteVector::iterator aCurrSprite( maSprites.begin() );
293 0 : const SpriteVector::iterator aEnd( maSprites.end() );
294 0 : while( aCurrSprite != aEnd )
295 : {
296 0 : cppcanvas::CustomSpriteSharedPtr pCurrSprite( aCurrSprite->mpSprite.lock() );
297 :
298 0 : if( pCurrSprite )
299 : {
300 : // only copy still valid sprites over to the refreshed
301 : // sprite vector.
302 0 : aValidSprites.push_back( *aCurrSprite );
303 :
304 0 : pCurrSprite->setPriority(
305 0 : getSpritePriority( aValidSprites.size()-1 ));
306 : }
307 :
308 0 : ++aCurrSprite;
309 0 : }
310 :
311 : // replace sprite list with pruned one
312 0 : maSprites.swap( aValidSprites );
313 0 : }
314 :
315 : public:
316 0 : LayerSpriteContainer() :
317 : maSprites(),
318 0 : maLayerPrioRange()
319 : {
320 0 : }
321 :
322 0 : basegfx::B1DRange getLayerPriority() const
323 : {
324 0 : return maLayerPrioRange;
325 : }
326 :
327 0 : void setLayerPriority( const basegfx::B1DRange& rRange )
328 : {
329 0 : if( rRange != maLayerPrioRange )
330 : {
331 0 : maLayerPrioRange = rRange;
332 :
333 : // prune and recalc sprite prios
334 0 : updateSprites();
335 : }
336 0 : }
337 :
338 0 : void addSprite( const cppcanvas::CustomSpriteSharedPtr& pSprite,
339 : double nPriority )
340 : {
341 0 : if( !pSprite )
342 0 : return;
343 :
344 0 : SpriteEntry aEntry( pSprite,nPriority );
345 :
346 : // insert new sprite, such that vector stays sorted
347 : SpriteVector::iterator aInsertPos(
348 : maSprites.insert(
349 : std::lower_bound( maSprites.begin(),
350 : maSprites.end(),
351 : aEntry ),
352 0 : aEntry ));
353 :
354 0 : const std::size_t nNumSprites( maSprites.size() );
355 0 : if( nNumSprites > SPRITE_ULLAGE ||
356 0 : maSprites.end() - aInsertPos > 1 )
357 : {
358 : // updateSprites() also updates all sprite prios
359 0 : updateSprites();
360 : }
361 : else
362 : {
363 : // added sprite to the end, and not too many sprites in
364 : // vector - perform optimized update (only need to set
365 : // prio). This basically caters for the common case of
366 : // iterated character animations, which generate lots of
367 : // sprites, all added to the end.
368 0 : pSprite->setPriority(
369 0 : getSpritePriority( nNumSprites-1 ));
370 0 : }
371 : }
372 :
373 0 : void clear()
374 : {
375 0 : maSprites.clear();
376 0 : }
377 : };
378 :
379 :
380 :
381 :
382 :
383 : /** This class provides layers for a slide view
384 :
385 : Layers are used to render animations with the correct z order -
386 : because sprites are always in front of the static canvas
387 : background, shapes that must appear <em<before</em> an animation
388 : must also be displayed as a sprite.
389 :
390 : Each layer has a priority assigned to it (valid range [0,1]), which
391 : also affects all sprites created for this specific layer - i.e. if
392 : the layer priority changes, the sprites change z order together
393 : with their parent.
394 : */
395 0 : class SlideViewLayer : public ViewLayer,
396 : private boost::noncopyable
397 : {
398 : /// Smart container for all sprites issued by this layer
399 : mutable LayerSpriteContainer maSpriteContainer;
400 :
401 : /// Bounds of this layer in user space coordinates
402 : basegfx::B2DRange maLayerBounds;
403 :
404 : /// Bounds of this layer in device pixel
405 : mutable basegfx::B2IRange maLayerBoundsPixel;
406 :
407 : /// Current clip polygon in user coordinates
408 : basegfx::B2DPolyPolygon maClip;
409 :
410 : /// Current size of the view in user coordinates
411 : basegfx::B2DSize maUserSize;
412 :
413 : /// Current overall view transformation
414 : basegfx::B2DHomMatrix maTransformation;
415 :
416 : /// 'parent' canvas, this viewlayer is associated with
417 : const cppcanvas::SpriteCanvasSharedPtr mpSpriteCanvas;
418 :
419 : /** output surface (necessarily a sprite, won't otherwise be able
420 : to display anything <em>before</em> other sprites)
421 : */
422 : mutable cppcanvas::CustomSpriteSharedPtr mpSprite;
423 :
424 : /// actual output canvas retrieved from a sprite
425 : mutable cppcanvas::CanvasSharedPtr mpOutputCanvas;
426 :
427 : /// ptr back to owning view. needed for isOnView() method
428 : View const* const mpParentView;
429 :
430 : public:
431 : /** Create a new layer
432 :
433 : @param pCanvas
434 : Sprite canvas to create the layer on
435 :
436 : @param rTransform
437 : Initial overall canvas transformation
438 :
439 : @param rLayerBounds
440 : Initial layer bounds, in view coordinate system
441 : */
442 0 : SlideViewLayer( const cppcanvas::SpriteCanvasSharedPtr& pCanvas,
443 : const basegfx::B2DHomMatrix& rTransform,
444 : const basegfx::B2DRange& rLayerBounds,
445 : const basegfx::B2DSize& rUserSize,
446 : View const* const pParentView) :
447 : maSpriteContainer(),
448 : maLayerBounds(rLayerBounds),
449 : maLayerBoundsPixel(),
450 : maClip(),
451 : maUserSize(rUserSize),
452 : maTransformation(rTransform),
453 : mpSpriteCanvas(pCanvas),
454 : mpSprite(),
455 : mpOutputCanvas(),
456 0 : mpParentView(pParentView)
457 : {
458 0 : }
459 :
460 0 : void updateView( const basegfx::B2DHomMatrix& rMatrix,
461 : const basegfx::B2DSize& rUserSize )
462 : {
463 0 : maTransformation = rMatrix;
464 0 : maUserSize = rUserSize;
465 :
466 : // limit layer bounds to visible screen
467 : maLayerBounds.intersect( basegfx::B2DRange(0.0,
468 : 0.0,
469 : maUserSize.getX(),
470 0 : maUserSize.getY()) );
471 :
472 : basegfx::B2IRange const& rNewLayerPixel(
473 : getLayerBoundsPixel(maLayerBounds,
474 0 : maTransformation) );
475 0 : if( rNewLayerPixel != maLayerBoundsPixel )
476 : {
477 : // re-gen sprite with new size
478 0 : mpOutputCanvas.reset();
479 0 : mpSprite.reset();
480 : }
481 0 : }
482 :
483 0 : virtual ::com::sun::star::geometry::IntegerSize2D getTranslationOffset() const SAL_OVERRIDE
484 : {
485 0 : basegfx::B2DRectangle aTmpRect;
486 : canvas::tools::calcTransformedRectBounds( aTmpRect,
487 : maLayerBounds,
488 0 : maTransformation );
489 0 : geometry::IntegerSize2D offset(0, 0);
490 :
491 : // Add translation according to the origin of aTmpRect. Ignore the
492 : // translation when aTmpRect was not properly initialized.
493 0 : if ( ! aTmpRect.isEmpty())
494 : {
495 0 : offset.Width = basegfx::fround(aTmpRect.getMinX());
496 0 : offset.Height = basegfx::fround(aTmpRect.getMinY());
497 : }
498 0 : return offset;
499 : }
500 :
501 : private:
502 : // ViewLayer interface
503 :
504 :
505 0 : virtual cppcanvas::CustomSpriteSharedPtr createSprite(
506 : const ::basegfx::B2DSize& rSpriteSizePixel,
507 : double nPriority ) const SAL_OVERRIDE
508 : {
509 : cppcanvas::CustomSpriteSharedPtr pSprite(
510 0 : mpSpriteCanvas->createCustomSprite( rSpriteSizePixel ) );
511 :
512 : maSpriteContainer.addSprite( pSprite,
513 0 : nPriority );
514 :
515 0 : return pSprite;
516 : }
517 :
518 0 : virtual void setPriority( const basegfx::B1DRange& rRange ) SAL_OVERRIDE
519 : {
520 : OSL_ENSURE( !rRange.isEmpty() &&
521 : rRange.getMinimum() >= 1.0,
522 : "SlideViewLayer::setPriority(): prio MUST be larger than 1.0 (because "
523 : "the background layer already lies there)" );
524 :
525 0 : maSpriteContainer.setLayerPriority( rRange );
526 :
527 0 : if( mpSprite )
528 0 : mpSprite->setPriority( rRange.getMinimum() );
529 0 : }
530 :
531 0 : virtual basegfx::B2DHomMatrix getTransformation() const SAL_OVERRIDE
532 : {
533 : // Offset given transformation by left, top border of given
534 : // range (after transformation through given transformation)
535 0 : basegfx::B2DRectangle aTmpRect;
536 : canvas::tools::calcTransformedRectBounds( aTmpRect,
537 : maLayerBounds,
538 0 : maTransformation );
539 :
540 0 : basegfx::B2DHomMatrix aMatrix( maTransformation );
541 :
542 : // Add translation according to the origin of aTmpRect. Ignore the
543 : // translation when aTmpRect was not properly initialized.
544 0 : if ( ! aTmpRect.isEmpty())
545 : {
546 0 : aMatrix.translate( -basegfx::fround(aTmpRect.getMinX()),
547 0 : -basegfx::fround(aTmpRect.getMinY()) );
548 : }
549 :
550 0 : return aMatrix;
551 : }
552 :
553 0 : virtual basegfx::B2DHomMatrix getSpriteTransformation() const SAL_OVERRIDE
554 : {
555 0 : return maTransformation;
556 : }
557 :
558 0 : virtual void clear() const SAL_OVERRIDE
559 : {
560 : // grab canvas - that also lazy-initializes maLayerBoundsPixel
561 0 : cppcanvas::CanvasSharedPtr pCanvas=getCanvas()->clone();
562 :
563 : // clear whole canvas
564 0 : const basegfx::B2I64Tuple& rSpriteSize(maLayerBoundsPixel.getRange());
565 : clearRect(pCanvas,
566 0 : basegfx::B2IRange(0,0,rSpriteSize.getX(),rSpriteSize.getY()));
567 0 : }
568 :
569 0 : virtual void clearAll() const SAL_OVERRIDE
570 : {
571 : // grab canvas - that also lazy-initializes maLayerBoundsPixel
572 0 : ::cppcanvas::CanvasSharedPtr pCanvas( getCanvas()->clone() );
573 :
574 : // clear layer clip, to clear whole area
575 0 : pCanvas->setClip();
576 :
577 : // clear whole canvas
578 0 : const basegfx::B2I64Tuple& rSpriteSize(maLayerBoundsPixel.getRange());
579 : clearRect(pCanvas,
580 0 : basegfx::B2IRange(0,0,rSpriteSize.getX(),rSpriteSize.getY()));
581 0 : }
582 :
583 0 : virtual bool isOnView(boost::shared_ptr<View> const& rView) const SAL_OVERRIDE
584 : {
585 0 : return rView.get() == mpParentView;
586 : }
587 :
588 0 : virtual cppcanvas::CanvasSharedPtr getCanvas() const SAL_OVERRIDE
589 : {
590 0 : if( !mpOutputCanvas )
591 : {
592 0 : if( !mpSprite )
593 : {
594 : maLayerBoundsPixel = getLayerBoundsPixel(maLayerBounds,
595 0 : maTransformation);
596 :
597 : // HACK: ensure at least 1x1 pixel size. clients might
598 : // need an actual canvas (e.g. for bound rect
599 : // calculations) without rendering anything. Better
600 : // solution: introduce something like a reference
601 : // canvas for ViewLayers, which is always available.
602 0 : if( maLayerBoundsPixel.isEmpty() )
603 0 : maLayerBoundsPixel = basegfx::B2IRange(0,0,1,1);
604 :
605 0 : const basegfx::B2I64Tuple& rSpriteSize(maLayerBoundsPixel.getRange());
606 0 : mpSprite = mpSpriteCanvas->createCustomSprite(
607 0 : basegfx::B2DVector(sal::static_int_cast<sal_Int32>(rSpriteSize.getX()),
608 0 : sal::static_int_cast<sal_Int32>(rSpriteSize.getY())) );
609 :
610 0 : mpSprite->setPriority(
611 0 : maSpriteContainer.getLayerPriority().getMinimum() );
612 :
613 : #if OSL_DEBUG_LEVEL >= 2 && defined(DBG_UTIL)
614 : mpSprite->movePixel(
615 : basegfx::B2DPoint(maLayerBoundsPixel.getMinimum()) +
616 : basegfx::B2DPoint(10,10) );
617 :
618 : mpSprite->setAlpha(0.5);
619 : #else
620 0 : mpSprite->movePixel(
621 0 : basegfx::B2DPoint(maLayerBoundsPixel.getMinimum()) );
622 :
623 0 : mpSprite->setAlpha(1.0);
624 : #endif
625 0 : mpSprite->show();
626 : }
627 :
628 0 : ENSURE_OR_THROW( mpSprite,
629 : "SlideViewLayer::getCanvas(): no layer sprite" );
630 :
631 0 : mpOutputCanvas = mpSprite->getContentCanvas();
632 :
633 0 : ENSURE_OR_THROW( mpOutputCanvas,
634 : "SlideViewLayer::getCanvas(): sprite doesn't yield a canvas" );
635 :
636 : // new canvas retrieved - setup transformation and clip
637 0 : mpOutputCanvas->setTransformation( getTransformation() );
638 0 : mpOutputCanvas->setClip(
639 : createClipPolygon( maClip,
640 : mpOutputCanvas,
641 0 : maUserSize ));
642 : }
643 :
644 0 : return mpOutputCanvas;
645 : }
646 :
647 0 : virtual void setClip( const basegfx::B2DPolyPolygon& rClip ) SAL_OVERRIDE
648 : {
649 0 : basegfx::B2DPolyPolygon aNewClip = prepareClip( rClip );
650 :
651 0 : if( aNewClip != maClip )
652 : {
653 0 : maClip = aNewClip;
654 :
655 0 : if(mpOutputCanvas )
656 0 : mpOutputCanvas->setClip(
657 : createClipPolygon( maClip,
658 : mpOutputCanvas,
659 0 : maUserSize ));
660 0 : }
661 0 : }
662 :
663 0 : virtual bool resize( const ::basegfx::B2DRange& rArea ) SAL_OVERRIDE
664 : {
665 0 : const bool bRet( maLayerBounds != rArea );
666 0 : maLayerBounds = rArea;
667 : updateView( maTransformation,
668 0 : maUserSize );
669 :
670 0 : return bRet;
671 : }
672 : };
673 :
674 :
675 :
676 :
677 : typedef cppu::WeakComponentImplHelper2<
678 : ::com::sun::star::util::XModifyListener,
679 : ::com::sun::star::awt::XPaintListener> SlideViewBase;
680 :
681 : /** SlideView class
682 :
683 : This class implements the View interface, encapsulating
684 : <em>one</em> view a slideshow is displayed on.
685 : */
686 0 : class SlideView : private cppu::BaseMutex,
687 : public SlideViewBase,
688 : public UnoView
689 : {
690 : public:
691 : SlideView( const uno::Reference<presentation::XSlideShowView>& xView,
692 : EventQueue& rEventQueue,
693 : EventMultiplexer& rEventMultiplexer );
694 : void updateCanvas();
695 :
696 : private:
697 : // View:
698 : virtual ViewLayerSharedPtr createViewLayer( const basegfx::B2DRange& rLayerBounds ) const SAL_OVERRIDE;
699 : virtual bool updateScreen() const SAL_OVERRIDE;
700 : virtual bool paintScreen() const SAL_OVERRIDE;
701 : virtual void setViewSize( const ::basegfx::B2DSize& ) SAL_OVERRIDE;
702 : virtual void setCursorShape( sal_Int16 nPointerShape ) SAL_OVERRIDE;
703 :
704 : // ViewLayer interface
705 : virtual bool isOnView(boost::shared_ptr<View> const& rView) const SAL_OVERRIDE;
706 : virtual void clear() const SAL_OVERRIDE;
707 : virtual void clearAll() const SAL_OVERRIDE;
708 : virtual cppcanvas::CanvasSharedPtr getCanvas() const SAL_OVERRIDE;
709 : virtual cppcanvas::CustomSpriteSharedPtr createSprite( const ::basegfx::B2DSize& rSpriteSizePixel,
710 : double nPriority ) const SAL_OVERRIDE;
711 : virtual void setPriority( const basegfx::B1DRange& rRange ) SAL_OVERRIDE;
712 : virtual geometry::IntegerSize2D getTranslationOffset() const SAL_OVERRIDE;
713 : virtual ::basegfx::B2DHomMatrix getTransformation() const SAL_OVERRIDE;
714 : virtual basegfx::B2DHomMatrix getSpriteTransformation() const SAL_OVERRIDE;
715 : virtual void setClip( const ::basegfx::B2DPolyPolygon& rClip ) SAL_OVERRIDE;
716 : virtual bool resize( const ::basegfx::B2DRange& rArea ) SAL_OVERRIDE;
717 :
718 : // UnoView:
719 : virtual void _dispose() SAL_OVERRIDE;
720 : virtual uno::Reference<presentation::XSlideShowView> getUnoView()const SAL_OVERRIDE;
721 : virtual void setIsSoundEnabled (const bool bValue) SAL_OVERRIDE;
722 : virtual bool isSoundEnabled (void) const SAL_OVERRIDE;
723 :
724 : // XEventListener:
725 : virtual void SAL_CALL disposing( lang::EventObject const& evt )
726 : throw (uno::RuntimeException, std::exception) SAL_OVERRIDE;
727 : // XModifyListener:
728 : virtual void SAL_CALL modified( const lang::EventObject& aEvent )
729 : throw (uno::RuntimeException, std::exception) SAL_OVERRIDE;
730 : // XPaintListener:
731 : virtual void SAL_CALL windowPaint( const awt::PaintEvent& e )
732 : throw (uno::RuntimeException, std::exception) SAL_OVERRIDE;
733 :
734 : // WeakComponentImplHelperBase:
735 : virtual void SAL_CALL disposing() SAL_OVERRIDE;
736 :
737 : void updateClip();
738 :
739 : private:
740 : typedef std::vector< boost::weak_ptr<SlideViewLayer> > ViewLayerVector;
741 :
742 : /// Prune viewlayers from deceased ones, optionally update them
743 : void pruneLayers( bool bWithViewLayerUpdate=false ) const;
744 :
745 : /** Max fill level of maViewLayers, before we try to prune it from
746 : deceased layers
747 : */
748 : enum{ LAYER_ULLAGE=8 };
749 :
750 : uno::Reference<presentation::XSlideShowView> mxView;
751 : cppcanvas::SpriteCanvasSharedPtr mpCanvas;
752 :
753 : EventMultiplexer& mrEventMultiplexer;
754 : EventQueue& mrEventQueue;
755 :
756 : mutable LayerSpriteContainer maSprites;
757 : mutable ViewLayerVector maViewLayers;
758 :
759 : basegfx::B2DPolyPolygon maClip;
760 :
761 : basegfx::B2DHomMatrix maViewTransform;
762 : basegfx::B2DSize maUserSize;
763 : bool mbIsSoundEnabled;
764 : };
765 :
766 :
767 0 : SlideView::SlideView( const uno::Reference<presentation::XSlideShowView>& xView,
768 : EventQueue& rEventQueue,
769 : EventMultiplexer& rEventMultiplexer ) :
770 : SlideViewBase( m_aMutex ),
771 : mxView( xView ),
772 : mpCanvas(),
773 : mrEventMultiplexer( rEventMultiplexer ),
774 : mrEventQueue( rEventQueue ),
775 : maSprites(),
776 : maViewLayers(),
777 : maClip(),
778 : maViewTransform(),
779 : maUserSize( 1.0, 1.0 ), // default size: one-by-one rectangle
780 0 : mbIsSoundEnabled(true)
781 : {
782 : // take care not constructing any UNO references to this _inside_
783 : // ctor, shift that code to createSlideView()!
784 0 : ENSURE_OR_THROW( mxView.is(),
785 : "SlideView::SlideView(): Invalid view" );
786 :
787 0 : mpCanvas = cppcanvas::VCLFactory::getInstance().createSpriteCanvas(
788 0 : xView->getCanvas() );
789 0 : ENSURE_OR_THROW( mpCanvas,
790 : "Could not create cppcanvas" );
791 :
792 : geometry::AffineMatrix2D aViewTransform(
793 0 : xView->getTransformation() );
794 :
795 0 : if( basegfx::fTools::equalZero(
796 : basegfx::B2DVector(aViewTransform.m00,
797 0 : aViewTransform.m10).getLength()) ||
798 : basegfx::fTools::equalZero(
799 : basegfx::B2DVector(aViewTransform.m01,
800 0 : aViewTransform.m11).getLength()) )
801 : {
802 : OSL_FAIL( "SlideView::SlideView(): Singular matrix!" );
803 :
804 0 : canvas::tools::setIdentityAffineMatrix2D(aViewTransform);
805 : }
806 :
807 : basegfx::unotools::homMatrixFromAffineMatrix(
808 0 : maViewTransform, aViewTransform );
809 :
810 : // once and forever: set fixed prio to this 'layer' (we're always
811 : // the background layer)
812 0 : maSprites.setLayerPriority( basegfx::B1DRange(0.0,1.0) );
813 0 : }
814 :
815 0 : void SlideView::disposing()
816 : {
817 0 : osl::MutexGuard aGuard( m_aMutex );
818 :
819 0 : maViewLayers.clear();
820 0 : maSprites.clear();
821 0 : mpCanvas.reset();
822 :
823 : // additionally, also de-register from XSlideShowView
824 0 : if (mxView.is())
825 : {
826 0 : mxView->removeTransformationChangedListener( this );
827 0 : mxView->removePaintListener( this );
828 0 : mxView.clear();
829 0 : }
830 0 : }
831 :
832 0 : ViewLayerSharedPtr SlideView::createViewLayer( const basegfx::B2DRange& rLayerBounds ) const
833 : {
834 0 : osl::MutexGuard aGuard( m_aMutex );
835 :
836 0 : ENSURE_OR_THROW( mpCanvas,
837 : "SlideView::createViewLayer(): Disposed" );
838 :
839 0 : const std::size_t nNumLayers( maViewLayers.size() );
840 :
841 : // avoid filling up layer vector with lots of deceased layer weak
842 : // ptrs
843 0 : if( nNumLayers > LAYER_ULLAGE )
844 0 : pruneLayers();
845 :
846 : boost::shared_ptr<SlideViewLayer> pViewLayer( new SlideViewLayer(mpCanvas,
847 0 : getTransformation(),
848 : rLayerBounds,
849 : maUserSize,
850 0 : this) );
851 0 : maViewLayers.push_back( pViewLayer );
852 :
853 0 : return pViewLayer;
854 : }
855 :
856 0 : bool SlideView::updateScreen() const
857 : {
858 0 : osl::MutexGuard aGuard( m_aMutex );
859 :
860 0 : ENSURE_OR_RETURN_FALSE( mpCanvas.get(),
861 : "SlideView::updateScreen(): Disposed" );
862 :
863 0 : return mpCanvas->updateScreen( false );
864 : }
865 :
866 0 : bool SlideView::paintScreen() const
867 : {
868 0 : osl::MutexGuard aGuard( m_aMutex );
869 :
870 0 : ENSURE_OR_RETURN_FALSE( mpCanvas.get(),
871 : "SlideView::paintScreen(): Disposed" );
872 :
873 0 : return mpCanvas->updateScreen( true );
874 : }
875 :
876 0 : void SlideView::clear() const
877 : {
878 0 : osl::MutexGuard aGuard( m_aMutex );
879 :
880 : OSL_ENSURE( mxView.is() && mpCanvas,
881 : "SlideView::clear(): Disposed" );
882 0 : if( !mxView.is() || !mpCanvas )
883 0 : return;
884 :
885 : // keep layer clip
886 0 : clearRect(getCanvas()->clone(),
887 : getLayerBoundsPixel(
888 : basegfx::B2DRange(0,0,
889 : maUserSize.getX(),
890 : maUserSize.getY()),
891 0 : getTransformation()));
892 : }
893 :
894 0 : void SlideView::clearAll() const
895 : {
896 0 : osl::MutexGuard aGuard( m_aMutex );
897 :
898 : OSL_ENSURE( mxView.is() && mpCanvas,
899 : "SlideView::clear(): Disposed" );
900 0 : if( !mxView.is() || !mpCanvas )
901 0 : return;
902 :
903 0 : mpCanvas->clear(); // this is unnecessary, strictly speaking. but
904 : // it makes the SlideView behave exactly like a
905 : // sprite-based SlideViewLayer, because those
906 : // are created from scratch after a resize
907 :
908 : // clear whole view
909 0 : mxView->clear();
910 : }
911 :
912 0 : void SlideView::setViewSize( const basegfx::B2DSize& rSize )
913 : {
914 0 : osl::MutexGuard aGuard( m_aMutex );
915 :
916 0 : maUserSize = rSize;
917 0 : updateCanvas();
918 0 : }
919 :
920 0 : void SlideView::setCursorShape( sal_Int16 nPointerShape )
921 : {
922 0 : osl::MutexGuard const guard( m_aMutex );
923 :
924 0 : if (mxView.is())
925 0 : mxView->setMouseCursor( nPointerShape );
926 0 : }
927 :
928 0 : bool SlideView::isOnView(boost::shared_ptr<View> const& rView) const
929 : {
930 0 : return rView.get() == this;
931 : }
932 :
933 0 : cppcanvas::CanvasSharedPtr SlideView::getCanvas() const
934 : {
935 0 : osl::MutexGuard aGuard( m_aMutex );
936 :
937 0 : ENSURE_OR_THROW( mpCanvas,
938 : "SlideView::getCanvas(): Disposed" );
939 :
940 0 : return mpCanvas;
941 : }
942 :
943 0 : cppcanvas::CustomSpriteSharedPtr SlideView::createSprite(
944 : const basegfx::B2DSize& rSpriteSizePixel,
945 : double nPriority ) const
946 : {
947 0 : osl::MutexGuard aGuard( m_aMutex );
948 :
949 0 : ENSURE_OR_THROW( mpCanvas, "SlideView::createSprite(): Disposed" );
950 :
951 : cppcanvas::CustomSpriteSharedPtr pSprite(
952 0 : mpCanvas->createCustomSprite( rSpriteSizePixel ) );
953 :
954 : maSprites.addSprite( pSprite,
955 0 : nPriority );
956 :
957 0 : return pSprite;
958 : }
959 :
960 0 : void SlideView::setPriority( const basegfx::B1DRange& /*rRange*/ )
961 : {
962 0 : osl::MutexGuard aGuard( m_aMutex );
963 :
964 : OSL_FAIL( "SlideView::setPriority() is a NOOP for slide view - "
965 0 : "content will always be shown in the background" );
966 0 : }
967 :
968 0 : basegfx::B2DHomMatrix SlideView::getTransformation() const
969 : {
970 0 : osl::MutexGuard aGuard( m_aMutex );
971 :
972 0 : basegfx::B2DHomMatrix aMatrix;
973 0 : aMatrix.scale( 1.0/maUserSize.getX(), 1.0/maUserSize.getY() );
974 :
975 0 : return maViewTransform * aMatrix;
976 : }
977 :
978 0 : geometry::IntegerSize2D SlideView::getTranslationOffset() const
979 : {
980 0 : return mxView->getTranslationOffset();
981 : }
982 :
983 0 : basegfx::B2DHomMatrix SlideView::getSpriteTransformation() const
984 : {
985 0 : return getTransformation();
986 : }
987 :
988 0 : void SlideView::setClip( const basegfx::B2DPolyPolygon& rClip )
989 : {
990 0 : osl::MutexGuard aGuard( m_aMutex );
991 :
992 0 : basegfx::B2DPolyPolygon aNewClip = prepareClip( rClip );
993 :
994 0 : if( aNewClip != maClip )
995 : {
996 0 : maClip = aNewClip;
997 :
998 0 : updateClip();
999 0 : }
1000 0 : }
1001 :
1002 0 : bool SlideView::resize( const ::basegfx::B2DRange& /*rArea*/ )
1003 : {
1004 0 : osl::MutexGuard aGuard( m_aMutex );
1005 :
1006 : OSL_FAIL( "SlideView::resize(): ignored for the View, can't change size "
1007 : "effectively, anyway" );
1008 :
1009 0 : return false;
1010 : }
1011 :
1012 0 : uno::Reference<presentation::XSlideShowView> SlideView::getUnoView() const
1013 : {
1014 0 : osl::MutexGuard aGuard( m_aMutex );
1015 0 : return mxView;
1016 : }
1017 :
1018 0 : void SlideView::setIsSoundEnabled (const bool bValue)
1019 : {
1020 0 : mbIsSoundEnabled = bValue;
1021 0 : }
1022 :
1023 0 : bool SlideView::isSoundEnabled (void) const
1024 : {
1025 0 : return mbIsSoundEnabled;
1026 : }
1027 :
1028 0 : void SlideView::_dispose()
1029 : {
1030 0 : dispose();
1031 0 : }
1032 :
1033 : // XEventListener
1034 0 : void SlideView::disposing( lang::EventObject const& evt )
1035 : throw (uno::RuntimeException, std::exception)
1036 : {
1037 : (void)evt;
1038 :
1039 : // no deregistration necessary anymore, XView has left:
1040 0 : osl::MutexGuard const guard( m_aMutex );
1041 :
1042 0 : if (mxView.is())
1043 : {
1044 : OSL_ASSERT( evt.Source == mxView );
1045 0 : mxView.clear();
1046 : }
1047 :
1048 0 : dispose();
1049 0 : }
1050 :
1051 : // XModifyListener
1052 0 : void SlideView::modified( const lang::EventObject& /*aEvent*/ )
1053 : throw (uno::RuntimeException, std::exception)
1054 : {
1055 0 : osl::MutexGuard const guard( m_aMutex );
1056 :
1057 : OSL_ENSURE( mxView.is(), "SlideView::modified(): "
1058 : "Disposed, but event received from XSlideShowView?!");
1059 :
1060 0 : if( !mxView.is() )
1061 0 : return;
1062 :
1063 : geometry::AffineMatrix2D aViewTransform(
1064 0 : mxView->getTransformation() );
1065 :
1066 0 : if( basegfx::fTools::equalZero(
1067 : basegfx::B2DVector(aViewTransform.m00,
1068 0 : aViewTransform.m10).getLength()) ||
1069 : basegfx::fTools::equalZero(
1070 : basegfx::B2DVector(aViewTransform.m01,
1071 0 : aViewTransform.m11).getLength()) )
1072 : {
1073 : OSL_FAIL( "SlideView::modified(): Singular matrix!" );
1074 :
1075 0 : canvas::tools::setIdentityAffineMatrix2D(aViewTransform);
1076 : }
1077 :
1078 : // view transformation really changed?
1079 0 : basegfx::B2DHomMatrix aNewTransform;
1080 : basegfx::unotools::homMatrixFromAffineMatrix(
1081 : aNewTransform,
1082 0 : aViewTransform );
1083 :
1084 0 : if( aNewTransform == maViewTransform )
1085 0 : return; // No change, nothing to do
1086 :
1087 0 : maViewTransform = aNewTransform;
1088 :
1089 0 : updateCanvas();
1090 :
1091 : // notify view change. Don't call EventMultiplexer directly, this
1092 : // might not be the main thread!
1093 : mrEventQueue.addEvent(
1094 : makeEvent( boost::bind( (bool (EventMultiplexer::*)(
1095 : const uno::Reference<presentation::XSlideShowView>&))
1096 : &EventMultiplexer::notifyViewChanged,
1097 : boost::ref(mrEventMultiplexer), mxView ),
1098 0 : "EventMultiplexer::notifyViewChanged"));
1099 : }
1100 :
1101 : // XPaintListener
1102 0 : void SlideView::windowPaint( const awt::PaintEvent& /*e*/ )
1103 : throw (uno::RuntimeException, std::exception)
1104 : {
1105 0 : osl::MutexGuard aGuard( m_aMutex );
1106 :
1107 : OSL_ENSURE( mxView.is() && mpCanvas, "Disposed, but event received?!" );
1108 :
1109 : // notify view clobbering. Don't call EventMultiplexer directly,
1110 : // this might not be the main thread!
1111 : mrEventQueue.addEvent(
1112 : makeEvent( boost::bind( &EventMultiplexer::notifyViewClobbered,
1113 : boost::ref(mrEventMultiplexer), mxView ),
1114 0 : "EventMultiplexer::notifyViewClobbered") );
1115 0 : }
1116 :
1117 0 : void SlideView::updateCanvas()
1118 : {
1119 : OSL_ENSURE( mpCanvas,
1120 : "SlideView::updateCanvasTransform(): Disposed" );
1121 :
1122 0 : if( !mpCanvas || !mxView.is())
1123 0 : return;
1124 :
1125 0 : clearAll();
1126 0 : mpCanvas->setTransformation( getTransformation() );
1127 0 : mpCanvas->setClip(
1128 : createClipPolygon( maClip,
1129 : mpCanvas,
1130 0 : maUserSize ));
1131 :
1132 : // forward update to viewlayers
1133 0 : pruneLayers( true );
1134 : }
1135 :
1136 0 : void SlideView::updateClip()
1137 : {
1138 : OSL_ENSURE( mpCanvas,
1139 : "SlideView::updateClip(): Disposed" );
1140 :
1141 0 : if( !mpCanvas )
1142 0 : return;
1143 :
1144 0 : mpCanvas->setClip(
1145 : createClipPolygon( maClip,
1146 : mpCanvas,
1147 0 : maUserSize ));
1148 :
1149 0 : pruneLayers( false );
1150 : }
1151 :
1152 0 : void SlideView::pruneLayers( bool bWithViewLayerUpdate ) const
1153 : {
1154 0 : ViewLayerVector aValidLayers;
1155 :
1156 : const basegfx::B2DHomMatrix& rCurrTransform(
1157 0 : getTransformation() );
1158 :
1159 : // check all layers for validity, and retain only the live ones
1160 0 : ViewLayerVector::const_iterator aCurr( maViewLayers.begin() );
1161 0 : const ViewLayerVector::const_iterator aEnd( maViewLayers.end() );
1162 0 : while( aCurr != aEnd )
1163 : {
1164 0 : boost::shared_ptr< SlideViewLayer > pCurrLayer( aCurr->lock() );
1165 :
1166 0 : if( pCurrLayer )
1167 : {
1168 0 : aValidLayers.push_back( pCurrLayer );
1169 :
1170 0 : if( bWithViewLayerUpdate )
1171 : pCurrLayer->updateView( rCurrTransform,
1172 0 : maUserSize );
1173 : }
1174 :
1175 0 : ++aCurr;
1176 0 : }
1177 :
1178 : // replace layer list with pruned one
1179 0 : maViewLayers.swap( aValidLayers );
1180 0 : }
1181 :
1182 : } // anonymous namespace
1183 :
1184 0 : UnoViewSharedPtr createSlideView( uno::Reference< presentation::XSlideShowView> const& xView,
1185 : EventQueue& rEventQueue,
1186 : EventMultiplexer& rEventMultiplexer )
1187 : {
1188 : boost::shared_ptr<SlideView> const that(
1189 : comphelper::make_shared_from_UNO(
1190 : new SlideView(xView,
1191 : rEventQueue,
1192 0 : rEventMultiplexer)));
1193 :
1194 : // register listeners with XSlideShowView
1195 0 : xView->addTransformationChangedListener( that.get() );
1196 0 : xView->addPaintListener( that.get() );
1197 :
1198 : // set new transformation
1199 0 : that->updateCanvas();
1200 :
1201 0 : return that;
1202 : }
1203 :
1204 : } // namespace internal
1205 0 : } // namespace slideshow
1206 :
1207 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|