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