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_basegfx.hxx"
26 
27 #include <com/sun/star/geometry/AffineMatrix2D.hpp>
28 #include <com/sun/star/rendering/RenderState.hpp>
29 #include <com/sun/star/rendering/ViewState.hpp>
30 #include <com/sun/star/rendering/XCanvas.hpp>
31 #include <com/sun/star/rendering/CompositeOperation.hpp>
32 
33 #include <basegfx/matrix/b2dhommatrix.hxx>
34 #include <basegfx/range/b2drange.hxx>
35 #include <basegfx/range/b2drectangle.hxx>
36 #include <basegfx/point/b2dpoint.hxx>
37 #include <basegfx/tools/canvastools.hxx>
38 #include <basegfx/polygon/b2dpolygon.hxx>
39 #include <basegfx/polygon/b2dpolypolygontools.hxx>
40 #include <basegfx/tools/unopolypolygon.hxx>
41 #include <basegfx/matrix/b2dhommatrixtools.hxx>
42 
43 
44 using namespace ::com::sun::star;
45 
46 namespace basegfx
47 {
48 namespace unotools
49 {
50     UnoPolyPolygon::UnoPolyPolygon( const B2DPolyPolygon& rPolyPoly ) :
51         UnoPolyPolygonBase( m_aMutex ),
52         maPolyPoly( rPolyPoly ),
53         meFillRule( rendering::FillRule_EVEN_ODD )
54     {
55         // or else races will haunt us.
56         maPolyPoly.makeUnique();
57     }
58 
59     void SAL_CALL UnoPolyPolygon::addPolyPolygon(
60         const geometry::RealPoint2D& 						position,
61         const uno::Reference< rendering::XPolyPolygon2D >&	polyPolygon ) throw (lang::IllegalArgumentException,uno::RuntimeException)
62     {
63         osl::MutexGuard const guard( m_aMutex );
64         modifying();
65 
66         // TODO(F1): Correctly fulfill the UNO API
67         // specification. This will probably result in a vector of
68         // poly-polygons to be stored in this object.
69 
70         const sal_Int32 nPolys( polyPolygon->getNumberOfPolygons() );
71 
72         if( !polyPolygon.is() || !nPolys )
73         {
74             // invalid or empty polygon - nothing to do.
75             return;
76         }
77 
78         B2DPolyPolygon        aSrcPoly;
79         const UnoPolyPolygon* pSrc( dynamic_cast< UnoPolyPolygon* >(polyPolygon.get()) );
80 
81         // try to extract polygon data from interface. First,
82         // check whether it's the same implementation object,
83         // which we can tunnel then.
84         if( pSrc )
85         {
86             aSrcPoly = pSrc->getPolyPolygon();
87         }
88         else
89         {
90             // not a known implementation object - try data source
91             // interfaces
92             uno::Reference< rendering::XBezierPolyPolygon2D > xBezierPoly(
93                 polyPolygon,
94                 uno::UNO_QUERY );
95 
96             if( xBezierPoly.is() )
97             {
98                 aSrcPoly = unotools::polyPolygonFromBezier2DSequenceSequence(
99                     xBezierPoly->getBezierSegments( 0,
100                                                     nPolys,
101                                                     0,
102                                                     -1 ) );
103             }
104             else
105             {
106                 uno::Reference< rendering::XLinePolyPolygon2D > xLinePoly(
107                     polyPolygon,
108                     uno::UNO_QUERY );
109 
110                 // no implementation class and no data provider
111                 // found - contract violation.
112                 if( !xLinePoly.is() )
113                     throw lang::IllegalArgumentException(
114                         ::rtl::OUString(
115                             RTL_CONSTASCII_USTRINGPARAM(
116                                 "UnoPolyPolygon::addPolyPolygon(): Invalid input "
117                                 "poly-polygon, cannot retrieve vertex data")),
118                         static_cast<cppu::OWeakObject*>(this), 1);
119 
120                 aSrcPoly = unotools::polyPolygonFromPoint2DSequenceSequence(
121                     xLinePoly->getPoints( 0,
122                                           nPolys,
123                                           0,
124                                           -1 ) );
125             }
126         }
127 
128         const B2DRange	aBounds( tools::getRange( aSrcPoly ) );
129         const B2DVector 	aOffset( unotools::b2DPointFromRealPoint2D( position ) -
130                                              aBounds.getMinimum() );
131 
132         if( !aOffset.equalZero() )
133         {
134             const B2DHomMatrix aTranslate(tools::createTranslateB2DHomMatrix(aOffset));
135             aSrcPoly.transform( aTranslate );
136         }
137 
138         maPolyPoly.append( aSrcPoly );
139     }
140 
141     sal_Int32 SAL_CALL UnoPolyPolygon::getNumberOfPolygons() throw (uno::RuntimeException)
142     {
143         osl::MutexGuard const guard( m_aMutex );
144         return maPolyPoly.count();
145     }
146 
147     sal_Int32 SAL_CALL UnoPolyPolygon::getNumberOfPolygonPoints(
148         sal_Int32 polygon ) throw (lang::IndexOutOfBoundsException,uno::RuntimeException)
149     {
150         osl::MutexGuard const guard( m_aMutex );
151         checkIndex( polygon );
152 
153         return maPolyPoly.getB2DPolygon(polygon).count();
154     }
155 
156     rendering::FillRule SAL_CALL UnoPolyPolygon::getFillRule() throw (uno::RuntimeException)
157     {
158         osl::MutexGuard const guard( m_aMutex );
159         return meFillRule;
160     }
161 
162     void SAL_CALL UnoPolyPolygon::setFillRule(
163         rendering::FillRule fillRule ) throw (uno::RuntimeException)
164     {
165         osl::MutexGuard const guard( m_aMutex );
166         modifying();
167 
168         meFillRule = fillRule;
169     }
170 
171     sal_Bool SAL_CALL UnoPolyPolygon::isClosed(
172         sal_Int32 index ) throw (lang::IndexOutOfBoundsException,uno::RuntimeException)
173     {
174         osl::MutexGuard const guard( m_aMutex );
175         checkIndex( index );
176 
177         return maPolyPoly.getB2DPolygon(index).isClosed();
178     }
179 
180     void SAL_CALL UnoPolyPolygon::setClosed(
181         sal_Int32 index,
182         sal_Bool closedState ) throw (lang::IndexOutOfBoundsException,uno::RuntimeException)
183     {
184         osl::MutexGuard const guard( m_aMutex );
185         modifying();
186 
187         if( index == -1L )
188         {
189             // set all
190             maPolyPoly.setClosed( closedState );
191         }
192         else
193         {
194             checkIndex( index );
195 
196             // fetch referenced polygon, change state
197             B2DPolygon aTmp( maPolyPoly.getB2DPolygon(index) );
198             aTmp.setClosed( closedState );
199 
200             // set back to container
201             maPolyPoly.setB2DPolygon( index, aTmp );
202         }
203     }
204 
205     uno::Sequence< uno::Sequence< geometry::RealPoint2D > > SAL_CALL UnoPolyPolygon::getPoints(
206         sal_Int32 nPolygonIndex,
207         sal_Int32 nNumberOfPolygons,
208         sal_Int32 nPointIndex,
209         sal_Int32 nNumberOfPoints ) throw (lang::IndexOutOfBoundsException,uno::RuntimeException)
210     {
211         osl::MutexGuard const guard( m_aMutex );
212 
213         return unotools::pointSequenceSequenceFromB2DPolyPolygon(
214             getSubsetPolyPolygon( nPolygonIndex,
215                                   nNumberOfPolygons,
216                                   nPointIndex,
217                                   nNumberOfPoints ) );
218     }
219 
220     void SAL_CALL UnoPolyPolygon::setPoints(
221         const uno::Sequence< uno::Sequence< geometry::RealPoint2D > >& points,
222         sal_Int32 nPolygonIndex ) throw (lang::IndexOutOfBoundsException,uno::RuntimeException)
223     {
224         osl::MutexGuard const guard( m_aMutex );
225         modifying();
226 
227         const B2DPolyPolygon& rNewPolyPoly(
228             unotools::polyPolygonFromPoint2DSequenceSequence( points ) );
229 
230         if( nPolygonIndex == -1 )
231         {
232             maPolyPoly = rNewPolyPoly;
233         }
234         else
235         {
236             checkIndex( nPolygonIndex );
237 
238             maPolyPoly.insert( nPolygonIndex, rNewPolyPoly );
239         }
240     }
241 
242     geometry::RealPoint2D SAL_CALL UnoPolyPolygon::getPoint(
243         sal_Int32 nPolygonIndex,
244         sal_Int32 nPointIndex ) throw (lang::IndexOutOfBoundsException,uno::RuntimeException)
245     {
246         osl::MutexGuard const guard( m_aMutex );
247         checkIndex( nPolygonIndex );
248 
249         const B2DPolygon& rPoly( maPolyPoly.getB2DPolygon( nPolygonIndex ) );
250 
251         if( nPointIndex < 0 || nPointIndex >= static_cast<sal_Int32>(rPoly.count()) )
252             throw lang::IndexOutOfBoundsException();
253 
254         return unotools::point2DFromB2DPoint( rPoly.getB2DPoint( nPointIndex ) );
255     }
256 
257     void SAL_CALL UnoPolyPolygon::setPoint(
258         const geometry::RealPoint2D& point,
259         sal_Int32 nPolygonIndex,
260         sal_Int32 nPointIndex ) throw (lang::IndexOutOfBoundsException,uno::RuntimeException)
261     {
262         osl::MutexGuard const guard( m_aMutex );
263         checkIndex( nPolygonIndex );
264         modifying();
265 
266         B2DPolygon aPoly( maPolyPoly.getB2DPolygon( nPolygonIndex ) );
267 
268         if( nPointIndex < 0 || nPointIndex >= static_cast<sal_Int32>(aPoly.count()) )
269             throw lang::IndexOutOfBoundsException();
270 
271         aPoly.setB2DPoint( nPointIndex,
272                            unotools::b2DPointFromRealPoint2D( point ) );
273         maPolyPoly.setB2DPolygon( nPolygonIndex, aPoly );
274     }
275 
276     uno::Sequence< uno::Sequence< geometry::RealBezierSegment2D > > SAL_CALL UnoPolyPolygon::getBezierSegments(
277         sal_Int32 nPolygonIndex,
278         sal_Int32 nNumberOfPolygons,
279         sal_Int32 nPointIndex,
280         sal_Int32 nNumberOfPoints ) throw (lang::IndexOutOfBoundsException, uno::RuntimeException)
281     {
282         osl::MutexGuard const guard( m_aMutex );
283         return unotools::bezierSequenceSequenceFromB2DPolyPolygon(
284             getSubsetPolyPolygon( nPolygonIndex,
285                                   nNumberOfPolygons,
286                                   nPointIndex,
287                                   nNumberOfPoints ) );
288     }
289 
290     void SAL_CALL UnoPolyPolygon::setBezierSegments(
291         const uno::Sequence< uno::Sequence< geometry::RealBezierSegment2D > >&	points,
292         sal_Int32 																nPolygonIndex ) throw (lang::IndexOutOfBoundsException,
293                                                                                                        uno::RuntimeException)
294     {
295         osl::MutexGuard const guard( m_aMutex );
296         modifying();
297         const B2DPolyPolygon& rNewPolyPoly(
298             unotools::polyPolygonFromBezier2DSequenceSequence( points ) );
299 
300         if( nPolygonIndex == -1 )
301         {
302             maPolyPoly = rNewPolyPoly;
303         }
304         else
305         {
306             checkIndex( nPolygonIndex );
307 
308             maPolyPoly.insert( nPolygonIndex, rNewPolyPoly );
309         }
310     }
311 
312     geometry::RealBezierSegment2D SAL_CALL UnoPolyPolygon::getBezierSegment( sal_Int32 nPolygonIndex,
313                                                                              sal_Int32 nPointIndex ) throw (lang::IndexOutOfBoundsException,
314                                                                                                             uno::RuntimeException)
315     {
316         osl::MutexGuard const guard( m_aMutex );
317         checkIndex( nPolygonIndex );
318 
319         const B2DPolygon& rPoly( maPolyPoly.getB2DPolygon( nPolygonIndex ) );
320 		const sal_uInt32  nPointCount(rPoly.count());
321 
322         if( nPointIndex < 0 || nPointIndex >= static_cast<sal_Int32>(nPointCount) )
323             throw lang::IndexOutOfBoundsException();
324 
325         const B2DPoint& rPt( rPoly.getB2DPoint( nPointIndex ) );
326         const B2DPoint& rCtrl0( rPoly.getNextControlPoint(nPointIndex) );
327         const B2DPoint& rCtrl1( rPoly.getPrevControlPoint((nPointIndex + 1) % nPointCount) );
328 
329         return geometry::RealBezierSegment2D( rPt.getX(),
330                                               rPt.getY(),
331                                               rCtrl0.getX(),
332                                               rCtrl0.getY(),
333                                               rCtrl1.getX(),
334                                               rCtrl1.getY() );
335     }
336 
337     void SAL_CALL UnoPolyPolygon::setBezierSegment( const geometry::RealBezierSegment2D& segment,
338                                                          sal_Int32 						 nPolygonIndex,
339                                                          sal_Int32						 nPointIndex ) throw (lang::IndexOutOfBoundsException,
340                                                                                                               uno::RuntimeException)
341     {
342         osl::MutexGuard const guard( m_aMutex );
343         checkIndex( nPolygonIndex );
344         modifying();
345 
346         B2DPolygon aPoly( maPolyPoly.getB2DPolygon( nPolygonIndex ) );
347 		const sal_uInt32 nPointCount(aPoly.count());
348 
349         if( nPointIndex < 0 || nPointIndex >= static_cast<sal_Int32>(nPointCount) )
350             throw lang::IndexOutOfBoundsException();
351 
352         aPoly.setB2DPoint( nPointIndex,
353                            B2DPoint( segment.Px,
354                                      segment.Py ) );
355         aPoly.setNextControlPoint(nPointIndex,
356                                   B2DPoint(segment.C1x, segment.C1y));
357         aPoly.setPrevControlPoint((nPointIndex + 1) % nPointCount,
358                                   B2DPoint(segment.C2x, segment.C2y));
359 
360         maPolyPoly.setB2DPolygon( nPolygonIndex, aPoly );
361     }
362 
363     B2DPolyPolygon UnoPolyPolygon::getSubsetPolyPolygon(
364         sal_Int32 nPolygonIndex,
365         sal_Int32 nNumberOfPolygons,
366         sal_Int32 nPointIndex,
367         sal_Int32 nNumberOfPoints ) const
368     {
369         osl::MutexGuard const guard( m_aMutex );
370         checkIndex( nPolygonIndex );
371 
372         const sal_Int32 nPolyCount( maPolyPoly.count() );
373 
374         // check for "full polygon" case
375         if( !nPolygonIndex &&
376             !nPointIndex &&
377             nNumberOfPolygons == nPolyCount &&
378             nNumberOfPoints == -1 )
379         {
380             return maPolyPoly;
381         }
382 
383         B2DPolyPolygon aSubsetPoly;
384 
385         // create temporary polygon (as an extract from maPoly,
386         // which contains the requested subset)
387         for( sal_Int32 i=nPolygonIndex; i<nNumberOfPolygons; ++i )
388         {
389             checkIndex(i);
390 
391             const B2DPolygon& rCurrPoly( maPolyPoly.getB2DPolygon(i) );
392 
393             sal_Int32 nFirstPoint(0);
394             sal_Int32 nLastPoint(nPolyCount-1);
395 
396             if( nPointIndex && i==nPolygonIndex )
397             {
398                 // very first polygon - respect nPointIndex, if
399                 // not zero
400 
401                 // empty polygon - impossible to specify _any_
402                 // legal value except 0 here!
403                 if( !nPolyCount && nPointIndex )
404                     throw lang::IndexOutOfBoundsException();
405 
406                 nFirstPoint = nPointIndex;
407             }
408 
409             if( i==nNumberOfPolygons-1 && nNumberOfPoints != -1 )
410             {
411                 // very last polygon - respect nNumberOfPoints
412 
413                 // empty polygon - impossible to specify _any_
414                 // legal value except -1 here!
415                 if( !nPolyCount )
416                     throw lang::IndexOutOfBoundsException();
417 
418                 nLastPoint = nFirstPoint+nNumberOfPoints;
419             }
420 
421             if( !nPolyCount )
422             {
423                 // empty polygon - index checks already performed
424                 // above, now simply append empty polygon
425                 aSubsetPoly.append( rCurrPoly );
426             }
427             else
428             {
429                 if( nFirstPoint < 0 || nFirstPoint >= nPolyCount )
430                     throw lang::IndexOutOfBoundsException();
431 
432                 if( nLastPoint < 0 || nLastPoint >= nPolyCount )
433                     throw lang::IndexOutOfBoundsException();
434 
435                 B2DPolygon aTmp;
436                 for( sal_Int32 j=nFirstPoint; j<nLastPoint; ++j )
437                     aTmp.append( rCurrPoly.getB2DPoint(j) );
438 
439                 aSubsetPoly.append( aTmp );
440             }
441         }
442 
443         return aSubsetPoly;
444     }
445 
446     B2DPolyPolygon UnoPolyPolygon::getPolyPolygonUnsafe() const
447     {
448         return maPolyPoly;
449     }
450 
451 #define IMPLEMENTATION_NAME "gfx::internal::UnoPolyPolygon"
452 #define SERVICE_NAME "com.sun.star.rendering.PolyPolygon2D"
453     ::rtl::OUString SAL_CALL UnoPolyPolygon::getImplementationName() throw( uno::RuntimeException )
454     {
455         return ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( IMPLEMENTATION_NAME ) );
456     }
457 
458     sal_Bool SAL_CALL UnoPolyPolygon::supportsService( const ::rtl::OUString& ServiceName ) throw( uno::RuntimeException )
459     {
460         return ServiceName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM ( SERVICE_NAME ) );
461     }
462 
463     uno::Sequence< ::rtl::OUString > SAL_CALL UnoPolyPolygon::getSupportedServiceNames()  throw( uno::RuntimeException )
464     {
465         uno::Sequence< ::rtl::OUString > aRet(1);
466         aRet[0] = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM ( SERVICE_NAME ) );
467 
468         return aRet;
469     }
470 
471     B2DPolyPolygon UnoPolyPolygon::getPolyPolygon() const
472     {
473         osl::MutexGuard const guard( m_aMutex );
474 
475         // detach result from us
476         B2DPolyPolygon aRet( maPolyPoly );
477         aRet.makeUnique();
478         return aRet;
479     }
480 
481 }
482 }
483