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::__anon16bb2ce10111::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::__anon16bb2ce10111::SpriteEntry94     SpriteEntry( const cppcanvas::CustomSpriteSharedPtr& rSprite,
95                  double                                  nPrio ) :
96         mpSprite( rSprite ),
97         mnPriority( nPrio )
98     {
99     }
100 
operator <slideshow::internal::__anon16bb2ce10111::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