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