| /************************************************************** |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| *************************************************************/ |
| |
| |
| |
| // MARKER(update_precomp.py): autogen include statement, do not remove |
| #include "precompiled_slideshow.hxx" |
| |
| // must be first |
| #include <canvas/debug.hxx> |
| |
| #include <basegfx/range/b2drange.hxx> |
| #include <basegfx/range/b1drange.hxx> |
| #include <basegfx/range/b2dpolyrange.hxx> |
| #include <basegfx/matrix/b2dhommatrix.hxx> |
| #include <basegfx/polygon/b2dpolypolygon.hxx> |
| #include <basegfx/polygon/b2dpolypolygontools.hxx> |
| #include <basegfx/polygon/b2dpolypolygoncutter.hxx> |
| |
| #include "layer.hxx" |
| |
| #include <boost/bind.hpp> |
| |
| |
| using namespace ::com::sun::star; |
| |
| namespace slideshow |
| { |
| namespace internal |
| { |
| Layer::Layer( const basegfx::B2DRange& rMaxLayerBounds, |
| Dummy ) : |
| maViewEntries(), |
| maBounds(), |
| maNewBounds(), |
| maMaxBounds( rMaxLayerBounds ), |
| mbBoundsDirty(false), |
| mbBackgroundLayer(true), |
| mbClipSet(false) |
| { |
| } |
| |
| Layer::Layer( const basegfx::B2DRange& rMaxLayerBounds ) : |
| maViewEntries(), |
| maBounds(), |
| maNewBounds(), |
| maMaxBounds( rMaxLayerBounds ), |
| mbBoundsDirty(false), |
| mbBackgroundLayer(false), |
| mbClipSet(false) |
| { |
| } |
| |
| ViewLayerSharedPtr Layer::addView( const ViewSharedPtr& rNewView ) |
| { |
| OSL_ASSERT( rNewView ); |
| |
| ViewEntryVector::iterator aIter; |
| const ViewEntryVector::iterator aEnd( maViewEntries.end() ); |
| if( (aIter=std::find_if( maViewEntries.begin(), |
| aEnd, |
| boost::bind<bool>( |
| std::equal_to< ViewSharedPtr >(), |
| boost::bind( &ViewEntry::getView, _1 ), |
| boost::cref( rNewView )))) != aEnd ) |
| { |
| // already added - just return existing layer |
| return aIter->mpViewLayer; |
| |
| } |
| |
| // not yet added - create new view layer |
| ViewLayerSharedPtr pNewLayer; |
| if( mbBackgroundLayer ) |
| pNewLayer = rNewView; |
| else |
| pNewLayer = rNewView->createViewLayer(maBounds); |
| |
| // add to local list |
| maViewEntries.push_back( |
| ViewEntry( rNewView, |
| pNewLayer )); |
| |
| return maViewEntries.back().mpViewLayer; |
| } |
| |
| ViewLayerSharedPtr Layer::removeView( const ViewSharedPtr& rView ) |
| { |
| OSL_ASSERT( rView ); |
| |
| ViewEntryVector::iterator aIter; |
| const ViewEntryVector::iterator aEnd( maViewEntries.end() ); |
| if( (aIter=std::find_if( maViewEntries.begin(), |
| aEnd, |
| boost::bind<bool>( |
| std::equal_to< ViewSharedPtr >(), |
| boost::bind( &ViewEntry::getView, _1 ), |
| boost::cref( rView )))) == aEnd ) |
| { |
| // View was not added/is already removed |
| return ViewLayerSharedPtr(); |
| } |
| |
| OSL_ENSURE( std::count_if( maViewEntries.begin(), |
| aEnd, |
| boost::bind<bool>( |
| std::equal_to< ViewSharedPtr >(), |
| boost::bind( &ViewEntry::getView, _1 ), |
| boost::cref( rView ))) == 1, |
| "Layer::removeView(): view added multiple times" ); |
| |
| ViewLayerSharedPtr pRet( aIter->mpViewLayer ); |
| maViewEntries.erase(aIter); |
| |
| return pRet; |
| } |
| |
| void Layer::viewChanged( const ViewSharedPtr& rChangedView ) |
| { |
| ViewEntryVector::iterator aIter; |
| const ViewEntryVector::iterator aEnd( maViewEntries.end() ); |
| if( (aIter=std::find_if( maViewEntries.begin(), |
| aEnd, |
| boost::bind<bool>( |
| std::equal_to< ViewSharedPtr >(), |
| boost::bind( &ViewEntry::getView, _1 ), |
| boost::cref( rChangedView )))) != |
| aEnd ) |
| { |
| // adapt size of given ViewLayer - background layer |
| // resizes with view. |
| if( !mbBackgroundLayer ) |
| aIter->mpViewLayer->resize(maBounds); |
| } |
| } |
| |
| void Layer::viewsChanged() |
| { |
| // adapt size of given ViewLayer - background layer |
| // resizes with view. |
| if( !mbBackgroundLayer ) |
| { |
| std::for_each( maViewEntries.begin(), |
| maViewEntries.end(), |
| boost::bind( &ViewLayer::resize, |
| boost::bind( &ViewEntry::getViewLayer, |
| _1 ), |
| boost::cref(maBounds))); |
| } |
| } |
| |
| void Layer::setShapeViews( ShapeSharedPtr const& rShape ) const |
| { |
| rShape->clearAllViewLayers(); |
| |
| std::for_each( maViewEntries.begin(), |
| maViewEntries.end(), |
| boost::bind(&Shape::addViewLayer, |
| boost::cref(rShape), |
| boost::bind(&ViewEntry::getViewLayer, |
| _1), |
| false )); |
| } |
| |
| void Layer::setPriority( const ::basegfx::B1DRange& rPrioRange ) |
| { |
| if( !mbBackgroundLayer ) |
| { |
| std::for_each( maViewEntries.begin(), |
| maViewEntries.end(), |
| boost::bind( &ViewLayer::setPriority, |
| boost::bind( &ViewEntry::getViewLayer, |
| _1 ), |
| boost::cref(rPrioRange))); |
| } |
| } |
| |
| void Layer::addUpdateRange( ::basegfx::B2DRange const& rUpdateRange ) |
| { |
| // TODO(Q1): move this to B2DMultiRange |
| if( !rUpdateRange.isEmpty() ) |
| maUpdateAreas.appendElement( rUpdateRange, |
| basegfx::ORIENTATION_POSITIVE ); |
| } |
| |
| void Layer::updateBounds( ShapeSharedPtr const& rShape ) |
| { |
| if( !mbBackgroundLayer ) |
| { |
| if( !mbBoundsDirty ) |
| maNewBounds.reset(); |
| |
| maNewBounds.expand( rShape->getUpdateArea() ); |
| } |
| |
| mbBoundsDirty = true; |
| } |
| |
| bool Layer::commitBounds() |
| { |
| mbBoundsDirty = false; |
| |
| if( mbBackgroundLayer ) |
| return false; |
| |
| if( maNewBounds == maBounds ) |
| return false; |
| |
| maBounds = maNewBounds; |
| if( std::count_if( maViewEntries.begin(), |
| maViewEntries.end(), |
| boost::bind( &ViewLayer::resize, |
| boost::bind( &ViewEntry::getViewLayer, |
| _1 ), |
| boost::cref(maBounds)) ) == 0 ) |
| { |
| return false; |
| } |
| |
| // layer content invalid, update areas have wrong |
| // coordinates/not sensible anymore. |
| clearUpdateRanges(); |
| |
| return true; |
| } |
| |
| void Layer::clearUpdateRanges() |
| { |
| maUpdateAreas.clear(); |
| } |
| |
| void Layer::clearContent() |
| { |
| // clear content on all view layers |
| std::for_each( maViewEntries.begin(), |
| maViewEntries.end(), |
| boost::bind( |
| &ViewLayer::clear, |
| boost::bind( |
| &ViewEntry::getViewLayer, |
| _1))); |
| |
| // layer content cleared, update areas are not sensible |
| // anymore. |
| clearUpdateRanges(); |
| } |
| |
| class LayerEndUpdate : private boost::noncopyable |
| { |
| public: |
| LayerEndUpdate( LayerSharedPtr const& rLayer ) : |
| mpLayer( rLayer ) |
| {} |
| |
| ~LayerEndUpdate() { if(mpLayer) mpLayer->endUpdate(); } |
| |
| void dismiss() { mpLayer.reset(); } |
| |
| private: |
| LayerSharedPtr mpLayer; |
| }; |
| |
| Layer::EndUpdater Layer::beginUpdate() |
| { |
| if( maUpdateAreas.count() ) |
| { |
| // perform proper layer update. That means, setup proper |
| // clipping, and render each shape that intersects with |
| // the calculated update area |
| ::basegfx::B2DPolyPolygon aClip( maUpdateAreas.solveCrossovers() ); |
| aClip = ::basegfx::tools::stripNeutralPolygons(aClip); |
| aClip = ::basegfx::tools::stripDispensablePolygons(aClip, false); |
| |
| // actually, if there happen to be shapes with zero |
| // update area in the maUpdateAreas vector, the |
| // resulting clip polygon will be empty. |
| if( aClip.count() ) |
| { |
| // set clip to all view layers |
| std::for_each( maViewEntries.begin(), |
| maViewEntries.end(), |
| boost::bind( |
| &ViewLayer::setClip, |
| boost::bind( |
| &ViewEntry::getViewLayer, |
| _1), |
| boost::cref(aClip))); |
| |
| // clear update area on all view layers |
| std::for_each( maViewEntries.begin(), |
| maViewEntries.end(), |
| boost::bind( |
| &ViewLayer::clear, |
| boost::bind( |
| &ViewEntry::getViewLayer, |
| _1))); |
| |
| mbClipSet = true; |
| } |
| } |
| |
| return EndUpdater(new LayerEndUpdate(shared_from_this())); |
| } |
| |
| void Layer::endUpdate() |
| { |
| if( mbClipSet ) |
| { |
| mbClipSet = false; |
| |
| basegfx::B2DPolyPolygon aEmptyClip; |
| std::for_each( maViewEntries.begin(), |
| maViewEntries.end(), |
| boost::bind( |
| &ViewLayer::setClip, |
| boost::bind( |
| &ViewEntry::getViewLayer, |
| _1), |
| boost::cref(aEmptyClip))); |
| } |
| |
| clearUpdateRanges(); |
| } |
| |
| bool Layer::isInsideUpdateArea( ShapeSharedPtr const& rShape ) const |
| { |
| return maUpdateAreas.overlaps( rShape->getUpdateArea() ); |
| } |
| |
| LayerSharedPtr Layer::createBackgroundLayer( const basegfx::B2DRange& rMaxLayerBounds ) |
| { |
| return LayerSharedPtr(new Layer( rMaxLayerBounds, |
| BackgroundLayer )); |
| } |
| |
| LayerSharedPtr Layer::createLayer( const basegfx::B2DRange& rMaxLayerBounds ) |
| { |
| return LayerSharedPtr( new Layer( rMaxLayerBounds ) ); |
| } |
| |
| } |
| } |