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