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