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 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_slideshow.hxx"
26 
27 // must be first
28 #include <canvas/debug.hxx>
29 
30 #include <basegfx/range/b2drange.hxx>
31 #include <basegfx/range/b1drange.hxx>
32 #include <basegfx/range/b2dpolyrange.hxx>
33 #include <basegfx/matrix/b2dhommatrix.hxx>
34 #include <basegfx/polygon/b2dpolypolygon.hxx>
35 #include <basegfx/polygon/b2dpolypolygontools.hxx>
36 #include <basegfx/polygon/b2dpolypolygoncutter.hxx>
37 
38 #include "layer.hxx"
39 
40 #include <boost/bind.hpp>
41 
42 
43 using namespace ::com::sun::star;
44 
45 namespace slideshow
46 {
47     namespace internal
48     {
Layer(const basegfx::B2DRange & rMaxLayerBounds,Dummy)49         Layer::Layer( const basegfx::B2DRange& rMaxLayerBounds,
50                       Dummy                     ) :
51             maViewEntries(),
52             maBounds(),
53             maNewBounds(),
54             maMaxBounds( rMaxLayerBounds ),
55             mbBoundsDirty(false),
56             mbBackgroundLayer(true),
57             mbClipSet(false)
58         {
59         }
60 
Layer(const basegfx::B2DRange & rMaxLayerBounds)61         Layer::Layer( const basegfx::B2DRange& rMaxLayerBounds ) :
62             maViewEntries(),
63             maBounds(),
64             maNewBounds(),
65             maMaxBounds( rMaxLayerBounds ),
66             mbBoundsDirty(false),
67             mbBackgroundLayer(false),
68             mbClipSet(false)
69         {
70         }
71 
addView(const ViewSharedPtr & rNewView)72         ViewLayerSharedPtr Layer::addView( const ViewSharedPtr& rNewView )
73         {
74             OSL_ASSERT( rNewView );
75 
76             ViewEntryVector::iterator aIter;
77             const ViewEntryVector::iterator aEnd( maViewEntries.end() );
78             if( (aIter=std::find_if( maViewEntries.begin(),
79                                      aEnd,
80                                      boost::bind<bool>(
81                                          std::equal_to< ViewSharedPtr >(),
82                                          boost::bind( &ViewEntry::getView, _1 ),
83                                          boost::cref( rNewView )))) != aEnd )
84             {
85                 // already added - just return existing layer
86                 return aIter->mpViewLayer;
87 
88             }
89 
90             // not yet added - create new view layer
91             ViewLayerSharedPtr pNewLayer;
92             if( mbBackgroundLayer )
93                 pNewLayer = rNewView;
94             else
95                 pNewLayer = rNewView->createViewLayer(maBounds);
96 
97             // add to local list
98             maViewEntries.push_back(
99                 ViewEntry( rNewView,
100                            pNewLayer ));
101 
102             return maViewEntries.back().mpViewLayer;
103         }
104 
removeView(const ViewSharedPtr & rView)105         ViewLayerSharedPtr Layer::removeView( const ViewSharedPtr& rView )
106         {
107             OSL_ASSERT( rView );
108 
109             ViewEntryVector::iterator       aIter;
110             const ViewEntryVector::iterator aEnd( maViewEntries.end() );
111             if( (aIter=std::find_if( maViewEntries.begin(),
112                                        aEnd,
113                                        boost::bind<bool>(
114                                            std::equal_to< ViewSharedPtr >(),
115                                            boost::bind( &ViewEntry::getView, _1 ),
116                                            boost::cref( rView )))) == aEnd )
117             {
118                 // View was not added/is already removed
119                 return ViewLayerSharedPtr();
120             }
121 
122             OSL_ENSURE( std::count_if( maViewEntries.begin(),
123                                        aEnd,
124                                        boost::bind<bool>(
125                                            std::equal_to< ViewSharedPtr >(),
126                                            boost::bind( &ViewEntry::getView, _1 ),
127                                            boost::cref( rView ))) == 1,
128                         "Layer::removeView(): view added multiple times" );
129 
130             ViewLayerSharedPtr pRet( aIter->mpViewLayer );
131             maViewEntries.erase(aIter);
132 
133             return pRet;
134         }
135 
viewChanged(const ViewSharedPtr & rChangedView)136         void Layer::viewChanged( const ViewSharedPtr& rChangedView )
137         {
138             ViewEntryVector::iterator aIter;
139             const ViewEntryVector::iterator aEnd( maViewEntries.end() );
140             if( (aIter=std::find_if( maViewEntries.begin(),
141                                      aEnd,
142                                      boost::bind<bool>(
143                                          std::equal_to< ViewSharedPtr >(),
144                                          boost::bind( &ViewEntry::getView, _1 ),
145                                          boost::cref( rChangedView )))) !=
146                 aEnd )
147             {
148                 // adapt size of given ViewLayer - background layer
149                 // resizes with view.
150                 if( !mbBackgroundLayer )
151                     aIter->mpViewLayer->resize(maBounds);
152             }
153         }
154 
viewsChanged()155         void Layer::viewsChanged()
156         {
157             // adapt size of given ViewLayer - background layer
158             // resizes with view.
159             if( !mbBackgroundLayer )
160             {
161                 std::for_each( maViewEntries.begin(),
162                                maViewEntries.end(),
163                                boost::bind( &ViewLayer::resize,
164                                             boost::bind( &ViewEntry::getViewLayer,
165                                                          _1 ),
166                                             boost::cref(maBounds)));
167             }
168         }
169 
setShapeViews(ShapeSharedPtr const & rShape) const170         void Layer::setShapeViews( ShapeSharedPtr const& rShape ) const
171         {
172             rShape->clearAllViewLayers();
173 
174             std::for_each( maViewEntries.begin(),
175                            maViewEntries.end(),
176                            boost::bind(&Shape::addViewLayer,
177                                        boost::cref(rShape),
178                                        boost::bind(&ViewEntry::getViewLayer,
179                                                    _1),
180                                        false ));
181         }
182 
setPriority(const::basegfx::B1DRange & rPrioRange)183         void Layer::setPriority( const ::basegfx::B1DRange& rPrioRange )
184         {
185             if( !mbBackgroundLayer )
186             {
187                 std::for_each( maViewEntries.begin(),
188                                maViewEntries.end(),
189                                boost::bind( &ViewLayer::setPriority,
190                                             boost::bind( &ViewEntry::getViewLayer,
191                                                          _1 ),
192                                             boost::cref(rPrioRange)));
193             }
194         }
195 
addUpdateRange(::basegfx::B2DRange const & rUpdateRange)196         void Layer::addUpdateRange( ::basegfx::B2DRange const& rUpdateRange )
197         {
198             // TODO(Q1): move this to B2DMultiRange
199             if( !rUpdateRange.isEmpty() )
200                 maUpdateAreas.appendElement( rUpdateRange,
201                                              basegfx::ORIENTATION_POSITIVE );
202         }
203 
updateBounds(ShapeSharedPtr const & rShape)204         void Layer::updateBounds( ShapeSharedPtr const& rShape )
205         {
206             if( !mbBackgroundLayer )
207             {
208                 if( !mbBoundsDirty )
209                     maNewBounds.reset();
210 
211                 maNewBounds.expand( rShape->getUpdateArea() );
212             }
213 
214             mbBoundsDirty = true;
215         }
216 
commitBounds()217         bool Layer::commitBounds()
218         {
219             mbBoundsDirty = false;
220 
221             if( mbBackgroundLayer )
222                 return false;
223 
224             if( maNewBounds == maBounds )
225                 return false;
226 
227             maBounds = maNewBounds;
228             if( std::count_if( maViewEntries.begin(),
229                                maViewEntries.end(),
230                                boost::bind( &ViewLayer::resize,
231                                             boost::bind( &ViewEntry::getViewLayer,
232                                                          _1 ),
233                                             boost::cref(maBounds)) ) == 0 )
234             {
235                 return false;
236             }
237 
238             // layer content invalid, update areas have wrong
239             // coordinates/not sensible anymore.
240             clearUpdateRanges();
241 
242             return true;
243         }
244 
clearUpdateRanges()245         void Layer::clearUpdateRanges()
246         {
247             maUpdateAreas.clear();
248         }
249 
clearContent()250         void Layer::clearContent()
251         {
252             // clear content on all view layers
253             std::for_each( maViewEntries.begin(),
254                            maViewEntries.end(),
255                            boost::bind(
256                                &ViewLayer::clear,
257                                boost::bind(
258                                    &ViewEntry::getViewLayer,
259                                    _1)));
260 
261             // layer content cleared, update areas are not sensible
262             // anymore.
263             clearUpdateRanges();
264         }
265 
266         class LayerEndUpdate : private boost::noncopyable
267         {
268         public:
LayerEndUpdate(LayerSharedPtr const & rLayer)269             LayerEndUpdate( LayerSharedPtr const& rLayer ) :
270                 mpLayer( rLayer )
271             {}
272 
~LayerEndUpdate()273             ~LayerEndUpdate() { if(mpLayer) mpLayer->endUpdate(); }
274 
dismiss()275             void dismiss() { mpLayer.reset(); }
276 
277         private:
278             LayerSharedPtr mpLayer;
279         };
280 
beginUpdate()281         Layer::EndUpdater Layer::beginUpdate()
282         {
283             if( maUpdateAreas.count() )
284             {
285                 // perform proper layer update. That means, setup proper
286                 // clipping, and render each shape that intersects with
287                 // the calculated update area
288                 ::basegfx::B2DPolyPolygon aClip( maUpdateAreas.solveCrossovers() );
289                 aClip = ::basegfx::tools::stripNeutralPolygons(aClip);
290                 aClip = ::basegfx::tools::stripDispensablePolygons(aClip, false);
291 
292                 // actually, if there happen to be shapes with zero
293                 // update area in the maUpdateAreas vector, the
294                 // resulting clip polygon will be empty.
295                 if( aClip.count() )
296                 {
297                     // set clip to all view layers
298                     std::for_each( maViewEntries.begin(),
299                                    maViewEntries.end(),
300                                    boost::bind(
301                                        &ViewLayer::setClip,
302                                        boost::bind(
303                                            &ViewEntry::getViewLayer,
304                                            _1),
305                                        boost::cref(aClip)));
306 
307                     // clear update area on all view layers
308                     std::for_each( maViewEntries.begin(),
309                                    maViewEntries.end(),
310                                    boost::bind(
311                                        &ViewLayer::clear,
312                                        boost::bind(
313                                            &ViewEntry::getViewLayer,
314                                            _1)));
315 
316                     mbClipSet = true;
317                 }
318             }
319 
320             return EndUpdater(new LayerEndUpdate(shared_from_this()));
321         }
322 
endUpdate()323         void Layer::endUpdate()
324         {
325             if( mbClipSet )
326             {
327                 mbClipSet = false;
328 
329                 basegfx::B2DPolyPolygon aEmptyClip;
330                 std::for_each( maViewEntries.begin(),
331                                maViewEntries.end(),
332                                boost::bind(
333                                    &ViewLayer::setClip,
334                                    boost::bind(
335                                        &ViewEntry::getViewLayer,
336                                        _1),
337                                    boost::cref(aEmptyClip)));
338             }
339 
340             clearUpdateRanges();
341         }
342 
isInsideUpdateArea(ShapeSharedPtr const & rShape) const343         bool Layer::isInsideUpdateArea( ShapeSharedPtr const& rShape ) const
344         {
345             return maUpdateAreas.overlaps( rShape->getUpdateArea() );
346         }
347 
createBackgroundLayer(const basegfx::B2DRange & rMaxLayerBounds)348         LayerSharedPtr Layer::createBackgroundLayer( const basegfx::B2DRange& rMaxLayerBounds )
349         {
350             return LayerSharedPtr(new Layer( rMaxLayerBounds,
351                                              BackgroundLayer ));
352         }
353 
createLayer(const basegfx::B2DRange & rMaxLayerBounds)354         LayerSharedPtr Layer::createLayer( const basegfx::B2DRange& rMaxLayerBounds )
355         {
356             return LayerSharedPtr( new Layer( rMaxLayerBounds ) );
357         }
358 
359     }
360 }
361