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