1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_cppcanvas.hxx"
30 
31 #include <rtl/logfile.hxx>
32 
33 #include <com/sun/star/rendering/XCanvas.hpp>
34 #include <com/sun/star/rendering/TexturingMode.hpp>
35 
36 #include <tools/gen.hxx>
37 #include <vcl/canvastools.hxx>
38 
39 #include <basegfx/range/b2drectangle.hxx>
40 #include <basegfx/tools/canvastools.hxx>
41 #include <basegfx/polygon/b2dpolypolygon.hxx>
42 #include <basegfx/polygon/b2dpolypolygontools.hxx>
43 #include <basegfx/matrix/b2dhommatrix.hxx>
44 #include <canvas/canvastools.hxx>
45 
46 #include <boost/utility.hpp>
47 
48 #include "cachedprimitivebase.hxx"
49 #include "polypolyaction.hxx"
50 #include "outdevstate.hxx"
51 #include "mtftools.hxx"
52 
53 
54 using namespace ::com::sun::star;
55 
56 namespace cppcanvas
57 {
58     namespace internal
59     {
60         namespace
61         {
62             class PolyPolyAction : public CachedPrimitiveBase
63             {
64             public:
65                 PolyPolyAction( const ::basegfx::B2DPolyPolygon&,
66                                 const CanvasSharedPtr&,
67                                 const OutDevState&,
68                                 bool bFill,
69                                 bool bStroke );
70                 PolyPolyAction( const ::basegfx::B2DPolyPolygon&,
71                                 const CanvasSharedPtr&,
72                                 const OutDevState&,
73                                 bool bFill,
74                                 bool bStroke,
75                                 int nTransparency );
76 
77                 virtual bool render( const ::basegfx::B2DHomMatrix& rTransformation,
78                                      const Subset&					rSubset ) const;
79 
80                 virtual ::basegfx::B2DRange getBounds( const ::basegfx::B2DHomMatrix& rTransformation ) const;
81                 virtual ::basegfx::B2DRange getBounds( const ::basegfx::B2DHomMatrix&	rTransformation,
82                                                        const Subset&					rSubset ) const;
83 
84                 virtual sal_Int32 getActionCount() const;
85 
86             private:
87                 using Action::render;
88                 virtual bool render( uno::Reference< rendering::XCachedPrimitive >& rCachedPrimitive,
89                                      const ::basegfx::B2DHomMatrix&                 rTransformation ) const;
90 
91                 const uno::Reference< rendering::XPolyPolygon2D > 	mxPolyPoly;
92                 const ::basegfx::B2DRange							maBounds;
93                 const CanvasSharedPtr								mpCanvas;
94 
95             	// stroke color is now implicit: the maState.DeviceColor member
96                 rendering::RenderState								maState;
97 
98                 uno::Sequence< double >								maFillColor;
99             };
100 
101             PolyPolyAction::PolyPolyAction( const ::basegfx::B2DPolyPolygon&	rPolyPoly,
102                                             const CanvasSharedPtr&              rCanvas,
103                                             const OutDevState&                  rState,
104                                             bool                                bFill,
105                                             bool                                bStroke ) :
106                 CachedPrimitiveBase( rCanvas, false ),
107                 mxPolyPoly( ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon( rCanvas->getUNOCanvas()->getDevice(), rPolyPoly) ),
108                 maBounds( ::basegfx::tools::getRange(rPolyPoly) ),
109                 mpCanvas( rCanvas ),
110                 maState(),
111                 maFillColor()
112             {
113                 tools::initRenderState(maState,rState);
114 
115                 if( bFill )
116                     maFillColor = rState.fillColor;
117 
118                 if( bStroke )
119                     maState.DeviceColor = rState.lineColor;
120             }
121 
122             PolyPolyAction::PolyPolyAction( const ::basegfx::B2DPolyPolygon&	rPolyPoly,
123                                             const CanvasSharedPtr&              rCanvas,
124                                             const OutDevState&                  rState,
125                                             bool                                bFill,
126                                             bool                                bStroke,
127                                             int                                 nTransparency ) :
128                 CachedPrimitiveBase( rCanvas, false ),
129                 mxPolyPoly( ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon( rCanvas->getUNOCanvas()->getDevice(), rPolyPoly) ),
130                 maBounds( ::basegfx::tools::getRange(rPolyPoly) ),
131                 mpCanvas( rCanvas ),
132                 maState(),
133                 maFillColor()
134             {
135                 tools::initRenderState(maState,rState);
136 
137                 if( bFill )
138                 {
139                     maFillColor = rState.fillColor;
140 
141                     if( maFillColor.getLength() < 4 )
142                         maFillColor.realloc( 4 );
143 
144                     // TODO(F1): Color management
145                     // adapt fill color transparency
146                     maFillColor[3] = 1.0 - nTransparency / 100.0;
147                 }
148 
149                 if( bStroke )
150                 {
151                     maState.DeviceColor = rState.lineColor;
152 
153                     if( maState.DeviceColor.getLength() < 4 )
154                         maState.DeviceColor.realloc( 4 );
155 
156                     // TODO(F1): Color management
157                     // adapt fill color transparency
158                     maState.DeviceColor[3] = 1.0 - nTransparency / 100.0;
159                 }
160             }
161 
162             bool PolyPolyAction::render( uno::Reference< rendering::XCachedPrimitive >& rCachedPrimitive,
163                                          const ::basegfx::B2DHomMatrix&                 rTransformation ) const
164             {
165                 RTL_LOGFILE_CONTEXT( aLog, "::cppcanvas::internal::PolyPolyAction::render()" );
166                 RTL_LOGFILE_CONTEXT_TRACE1( aLog, "::cppcanvas::internal::PolyPolyAction: 0x%X", this );
167 
168                 rendering::RenderState aLocalState( maState );
169                 ::canvas::tools::prependToRenderState(aLocalState, rTransformation);
170 
171 #ifdef SPECIAL_DEBUG
172                 aLocalState.Clip.clear();
173                 aLocalState.DeviceColor =
174                     ::vcl::unotools::colorToDoubleSequence( mpCanvas->getUNOCanvas()->getDevice(),
175                                                             ::Color( 0x80FF0000 ) );
176 
177                 if( maState.Clip.is() )
178                     mpCanvas->getUNOCanvas()->fillPolyPolygon( maState.Clip,
179                                                                mpCanvas->getViewState(),
180                                                                aLocalState );
181 
182                 aLocalState.DeviceColor = maState.DeviceColor;
183 #endif
184 
185                 if( maFillColor.getLength() )
186                 {
187                     // TODO(E3): Use DBO's finalizer here,
188                     // fillPolyPolygon() might throw
189                     const uno::Sequence< double > aTmpColor( aLocalState.DeviceColor );
190                     aLocalState.DeviceColor = maFillColor;
191 
192                     rCachedPrimitive = mpCanvas->getUNOCanvas()->fillPolyPolygon( mxPolyPoly,
193                                                                                   mpCanvas->getViewState(),
194                                                                                   aLocalState );
195 
196                     aLocalState.DeviceColor = aTmpColor;
197                 }
198 
199                 if( aLocalState.DeviceColor.getLength() )
200                 {
201                     rCachedPrimitive = mpCanvas->getUNOCanvas()->drawPolyPolygon( mxPolyPoly,
202                                                                                   mpCanvas->getViewState(),
203                                                                                   aLocalState );
204                 }
205 
206                 return true;
207             }
208 
209             bool PolyPolyAction::render( const ::basegfx::B2DHomMatrix&	rTransformation,
210                                          const Subset&					rSubset ) const
211             {
212                 // TODO(F1): Split up poly-polygon into polygons, or even
213                 // line segments, when subsets are requested.
214 
215                 // polygon only contains a single action, fail if subset
216                 // requests different range
217                 if( rSubset.mnSubsetBegin != 0 ||
218                     rSubset.mnSubsetEnd != 1 )
219                     return false;
220 
221                 return CachedPrimitiveBase::render( rTransformation );
222             }
223 
224             ::basegfx::B2DRange PolyPolyAction::getBounds( const ::basegfx::B2DHomMatrix&	rTransformation ) const
225             {
226                 rendering::RenderState aLocalState( maState );
227                 ::canvas::tools::prependToRenderState(aLocalState, rTransformation);
228 
229                 return tools::calcDevicePixelBounds(
230                     maBounds,
231                     mpCanvas->getViewState(),
232                     aLocalState );
233             }
234 
235             ::basegfx::B2DRange PolyPolyAction::getBounds( const ::basegfx::B2DHomMatrix&	rTransformation,
236                                                            const Subset&					rSubset ) const
237             {
238                 // TODO(F1): Split up poly-polygon into polygons, or even
239                 // line segments, when subsets are requested.
240 
241                 // polygon only contains a single action, empty bounds
242                 // if subset requests different range
243                 if( rSubset.mnSubsetBegin != 0 ||
244                     rSubset.mnSubsetEnd != 1 )
245                     return ::basegfx::B2DRange();
246 
247                 return getBounds( rTransformation );
248             }
249 
250             sal_Int32 PolyPolyAction::getActionCount() const
251             {
252                 // TODO(F1): Split up poly-polygon into polygons, or even
253                 // line segments, when subsets are requested.
254                 return 1;
255             }
256 
257 
258             // -------------------------------------------------------------------------------
259 
260             class TexturedPolyPolyAction : public CachedPrimitiveBase
261             {
262             public:
263                 TexturedPolyPolyAction( const ::basegfx::B2DPolyPolygon& rPoly,
264                                         const CanvasSharedPtr&		     rCanvas,
265                                         const OutDevState&			     rState,
266                                         const rendering::Texture& 	     rTexture );
267 
268                 virtual bool render( const ::basegfx::B2DHomMatrix& rTransformation,
269                                      const Subset&					rSubset ) const;
270 
271                 virtual ::basegfx::B2DRange getBounds( const ::basegfx::B2DHomMatrix& rTransformation ) const;
272                 virtual ::basegfx::B2DRange getBounds( const ::basegfx::B2DHomMatrix&	rTransformation,
273                                                        const Subset&					rSubset ) const;
274 
275                 virtual sal_Int32 getActionCount() const;
276 
277             private:
278                 using Action::render;
279                 virtual bool render( uno::Reference< rendering::XCachedPrimitive >& rCachedPrimitive,
280                                      const ::basegfx::B2DHomMatrix&                 rTransformation ) const;
281 
282                 const uno::Reference< rendering::XPolyPolygon2D >	mxPolyPoly;
283                 const ::basegfx::B2DRectangle						maBounds;
284                 const CanvasSharedPtr								mpCanvas;
285 
286             	// stroke color is now implicit: the maState.DeviceColor member
287                 rendering::RenderState								maState;
288                 const rendering::Texture							maTexture;
289             };
290 
291             TexturedPolyPolyAction::TexturedPolyPolyAction( const ::basegfx::B2DPolyPolygon& rPolyPoly,
292                                                             const CanvasSharedPtr&		     rCanvas,
293                                                             const OutDevState& 			     rState,
294                                                             const rendering::Texture& 	     rTexture ) :
295                 CachedPrimitiveBase( rCanvas, true ),
296                 mxPolyPoly( ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon( rCanvas->getUNOCanvas()->getDevice(), rPolyPoly) ),
297                 maBounds( ::basegfx::tools::getRange(rPolyPoly) ),
298                 mpCanvas( rCanvas ),
299                 maState(),
300                 maTexture( rTexture )
301             {
302                 tools::initRenderState(maState,rState);
303             }
304 
305             bool TexturedPolyPolyAction::render( uno::Reference< rendering::XCachedPrimitive >& rCachedPrimitive,
306                                                  const ::basegfx::B2DHomMatrix&                 rTransformation ) const
307             {
308                 RTL_LOGFILE_CONTEXT( aLog, "::cppcanvas::internal::PolyPolyAction::render()" );
309                 RTL_LOGFILE_CONTEXT_TRACE1( aLog, "::cppcanvas::internal::PolyPolyAction: 0x%X", this );
310 
311                 rendering::RenderState aLocalState( maState );
312                 ::canvas::tools::prependToRenderState(aLocalState, rTransformation);
313 
314                 uno::Sequence< rendering::Texture > aSeq(1);
315                 aSeq[0] = maTexture;
316 
317                 rCachedPrimitive = mpCanvas->getUNOCanvas()->fillTexturedPolyPolygon( mxPolyPoly,
318                                                                                       mpCanvas->getViewState(),
319                                                                                       aLocalState,
320                                                                                       aSeq );
321                 return true;
322             }
323 
324             bool TexturedPolyPolyAction::render( const ::basegfx::B2DHomMatrix&	rTransformation,
325                                                  const Subset&					rSubset ) const
326             {
327                 // TODO(F1): Split up poly-polygon into polygons, or even
328                 // line segments, when subsets are requested.
329 
330                 // polygon only contains a single action, fail if subset
331                 // requests different range
332                 if( rSubset.mnSubsetBegin != 0 ||
333                     rSubset.mnSubsetEnd != 1 )
334                     return false;
335 
336                 return CachedPrimitiveBase::render( rTransformation );
337             }
338 
339             ::basegfx::B2DRange TexturedPolyPolyAction::getBounds( const ::basegfx::B2DHomMatrix&	rTransformation ) const
340             {
341                 rendering::RenderState aLocalState( maState );
342                 ::canvas::tools::prependToRenderState(aLocalState, rTransformation);
343 
344                 return tools::calcDevicePixelBounds(
345                     maBounds,
346                     mpCanvas->getViewState(),
347                     aLocalState );
348             }
349 
350             ::basegfx::B2DRange TexturedPolyPolyAction::getBounds( const ::basegfx::B2DHomMatrix&	rTransformation,
351                                                                    const Subset&					rSubset ) const
352             {
353                 // TODO(F1): Split up poly-polygon into polygons, or even
354                 // line segments, when subsets are requested.
355 
356                 // polygon only contains a single action, empty bounds
357                 // if subset requests different range
358                 if( rSubset.mnSubsetBegin != 0 ||
359                     rSubset.mnSubsetEnd != 1 )
360                     return ::basegfx::B2DRange();
361 
362                 return getBounds( rTransformation );
363             }
364 
365             sal_Int32 TexturedPolyPolyAction::getActionCount() const
366             {
367                 // TODO(F1): Split up poly-polygon into polygons, or even
368                 // line segments, when subsets are requested.
369                 return 1;
370             }
371 
372             // -------------------------------------------------------------------------------
373 
374             class StrokedPolyPolyAction : public CachedPrimitiveBase
375             {
376             public:
377                 StrokedPolyPolyAction( const ::basegfx::B2DPolyPolygon&     rPoly,
378                                        const CanvasSharedPtr&				rCanvas,
379                                        const OutDevState&					rState,
380                                        const rendering::StrokeAttributes&	rStrokeAttributes );
381 
382                 virtual bool render( const ::basegfx::B2DHomMatrix& rTransformation,
383                                      const Subset&					rSubset ) const;
384 
385                 virtual ::basegfx::B2DRange getBounds( const ::basegfx::B2DHomMatrix& rTransformation ) const;
386                 virtual ::basegfx::B2DRange getBounds( const ::basegfx::B2DHomMatrix&	rTransformation,
387                                                        const Subset&					rSubset ) const;
388 
389                 virtual sal_Int32 getActionCount() const;
390 
391             private:
392                 using Action::render;
393                 virtual bool render( uno::Reference< rendering::XCachedPrimitive >& rCachedPrimitive,
394                                      const ::basegfx::B2DHomMatrix&                 rTransformation ) const;
395 
396                 const uno::Reference< rendering::XPolyPolygon2D >	mxPolyPoly;
397                 const ::basegfx::B2DRectangle						maBounds;
398                 const CanvasSharedPtr								mpCanvas;
399                 rendering::RenderState								maState;
400                 const rendering::StrokeAttributes					maStrokeAttributes;
401             };
402 
403             StrokedPolyPolyAction::StrokedPolyPolyAction( const ::basegfx::B2DPolyPolygon&      rPolyPoly,
404                                                           const CanvasSharedPtr&				rCanvas,
405                                                           const OutDevState& 					rState,
406                                                           const rendering::StrokeAttributes&	rStrokeAttributes ) :
407                 CachedPrimitiveBase( rCanvas, false ),
408                 mxPolyPoly( ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon( rCanvas->getUNOCanvas()->getDevice(), rPolyPoly) ),
409                 maBounds( ::basegfx::tools::getRange(rPolyPoly) ),
410                 mpCanvas( rCanvas ),
411                 maState(),
412                 maStrokeAttributes( rStrokeAttributes )
413             {
414                 tools::initRenderState(maState,rState);
415                 maState.DeviceColor = rState.lineColor;
416             }
417 
418             bool StrokedPolyPolyAction::render( uno::Reference< rendering::XCachedPrimitive >& rCachedPrimitive,
419                                                 const ::basegfx::B2DHomMatrix&                 rTransformation ) const
420             {
421                 RTL_LOGFILE_CONTEXT( aLog, "::cppcanvas::internal::PolyPolyAction::render()" );
422                 RTL_LOGFILE_CONTEXT_TRACE1( aLog, "::cppcanvas::internal::PolyPolyAction: 0x%X", this );
423 
424                 rendering::RenderState aLocalState( maState );
425                 ::canvas::tools::prependToRenderState(aLocalState, rTransformation);
426 
427                 rCachedPrimitive = mpCanvas->getUNOCanvas()->strokePolyPolygon( mxPolyPoly,
428                                                                                 mpCanvas->getViewState(),
429                                                                                 aLocalState,
430                                                                                 maStrokeAttributes );
431                 return true;
432             }
433 
434             bool StrokedPolyPolyAction::render( const ::basegfx::B2DHomMatrix&	rTransformation,
435                                                 const Subset&					rSubset ) const
436             {
437                 // TODO(F1): Split up poly-polygon into polygons, or even
438                 // line segments, when subsets are requested.
439 
440                 // polygon only contains a single action, fail if subset
441                 // requests different range
442                 if( rSubset.mnSubsetBegin != 0 ||
443                     rSubset.mnSubsetEnd != 1 )
444                     return false;
445 
446                 return CachedPrimitiveBase::render( rTransformation );
447             }
448 
449             ::basegfx::B2DRange StrokedPolyPolyAction::getBounds( const ::basegfx::B2DHomMatrix&	rTransformation ) const
450             {
451                 rendering::RenderState aLocalState( maState );
452                 ::canvas::tools::prependToRenderState(aLocalState, rTransformation);
453 
454                 return tools::calcDevicePixelBounds(
455                     maBounds,
456                     mpCanvas->getViewState(),
457                     aLocalState );
458             }
459 
460             ::basegfx::B2DRange StrokedPolyPolyAction::getBounds( const ::basegfx::B2DHomMatrix&	rTransformation,
461                                                                   const Subset&					rSubset ) const
462             {
463                 // TODO(F1): Split up poly-polygon into polygons, or even
464                 // line segments, when subsets are requested.
465 
466                 // polygon only contains a single action, empty bounds
467                 // if subset requests different range
468                 if( rSubset.mnSubsetBegin != 0 ||
469                     rSubset.mnSubsetEnd != 1 )
470                     return ::basegfx::B2DRange();
471 
472                 return getBounds( rTransformation );
473             }
474 
475             sal_Int32 StrokedPolyPolyAction::getActionCount() const
476             {
477                 // TODO(F1): Split up poly-polygon into polygons, or even
478                 // line segments, when subsets are requested.
479                 return 1;
480             }
481         }
482 
483         ActionSharedPtr PolyPolyActionFactory::createPolyPolyAction( const ::basegfx::B2DPolyPolygon& rPoly,
484                                                                      const CanvasSharedPtr&           rCanvas,
485                                                                      const OutDevState&               rState	)
486         {
487             OSL_ENSURE( rState.isLineColorSet || rState.isFillColorSet,
488                         "PolyPolyActionFactory::createPolyPolyAction() with empty line and fill color" );
489             return ActionSharedPtr( new PolyPolyAction( rPoly, rCanvas, rState,
490                                                         rState.isFillColorSet,
491                                                         rState.isLineColorSet ) );
492         }
493 
494         ActionSharedPtr PolyPolyActionFactory::createPolyPolyAction( const ::basegfx::B2DPolyPolygon&   rPoly,
495                                                                      const CanvasSharedPtr&             rCanvas,
496                                                                      const OutDevState&                 rState,
497                                                                      const rendering::Texture&          rTexture )
498         {
499             return ActionSharedPtr( new TexturedPolyPolyAction( rPoly, rCanvas, rState, rTexture ) );
500         }
501 
502         ActionSharedPtr PolyPolyActionFactory::createLinePolyPolyAction( const ::basegfx::B2DPolyPolygon& rPoly,
503                                                                          const CanvasSharedPtr&           rCanvas,
504                                                                          const OutDevState&               rState )
505         {
506             OSL_ENSURE( rState.isLineColorSet,
507                         "PolyPolyActionFactory::createLinePolyPolyAction() called with empty line color" );
508 
509             return ActionSharedPtr( new PolyPolyAction( rPoly, rCanvas, rState,
510                                                         false,
511                                                         rState.isLineColorSet ) );
512         }
513 
514         ActionSharedPtr PolyPolyActionFactory::createPolyPolyAction( const ::basegfx::B2DPolyPolygon&   rPoly,
515                                                                      const CanvasSharedPtr&				rCanvas,
516                                                                      const OutDevState&					rState,
517                                                                      const rendering::StrokeAttributes& rStrokeAttributes )
518         {
519             OSL_ENSURE( rState.isLineColorSet,
520                         "PolyPolyActionFactory::createPolyPolyAction() for strokes called with empty line color" );
521             return ActionSharedPtr( new StrokedPolyPolyAction( rPoly, rCanvas, rState, rStrokeAttributes ) );
522         }
523 
524         ActionSharedPtr PolyPolyActionFactory::createPolyPolyAction( const ::basegfx::B2DPolyPolygon& rPoly,
525                                                                      const CanvasSharedPtr&           rCanvas,
526                                                                      const OutDevState&               rState,
527                                                                      int                              nTransparency 	)
528         {
529             OSL_ENSURE( rState.isLineColorSet || rState.isFillColorSet,
530                         "PolyPolyActionFactory::createPolyPolyAction() with empty line and fill color" );
531             return ActionSharedPtr( new PolyPolyAction( rPoly, rCanvas, rState,
532                                                         rState.isFillColorSet,
533                                                         rState.isLineColorSet,
534                                                         nTransparency ) );
535         }
536 
537     }
538 }
539