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 <tools/gen.hxx>
32 
33 #include <canvas/debug.hxx>
34 #include <canvas/verbosetrace.hxx>
35 #include <canvas/canvastools.hxx>
36 
37 #include <rtl/logfile.hxx>
38 
39 #include <com/sun/star/rendering/XBitmap.hpp>
40 #include <com/sun/star/rendering/XCanvas.hpp>
41 
42 #include <rtl/math.hxx>
43 
44 #include <vcl/metaact.hxx>
45 #include <vcl/bitmapex.hxx>
46 #include <vcl/canvastools.hxx>
47 #include <vcl/svapp.hxx>
48 #include <vcl/outdev.hxx>
49 #include <vcl/virdev.hxx>
50 #include <vcl/virdev.hxx>
51 #include <vcl/gdimtf.hxx>
52 #include <vcl/gradient.hxx>
53 
54 #include <canvas/canvastools.hxx>
55 
56 #include <basegfx/range/b2drange.hxx>
57 #include <basegfx/point/b2dpoint.hxx>
58 #include <basegfx/vector/b2dsize.hxx>
59 #include <basegfx/numeric/ftools.hxx>
60 #include <basegfx/matrix/b2dhommatrix.hxx>
61 #include <basegfx/tuple/b2dtuple.hxx>
62 #include <basegfx/tools/canvastools.hxx>
63 
64 #include <boost/utility.hpp>
65 
66 #include "transparencygroupaction.hxx"
67 #include "outdevstate.hxx"
68 #include "mtftools.hxx"
69 #include "cppcanvas/vclfactory.hxx"
70 
71 
72 using namespace ::com::sun::star;
73 
74 namespace cppcanvas
75 {
76     namespace internal
77     {
78 		// free support functions
79 		// ======================
80         namespace
81         {
82             class TransparencyGroupAction : public Action, private ::boost::noncopyable
83             {
84             public:
85                 /** Create new transparency group action.
86 
87         	    	@param rGroupMtf
88                     Metafile that groups all actions to be rendered
89                     transparent
90 
91                     @param rParms
92                     Render parameters
93 
94                     @param rDstPoint
95                     Left, top edge of destination, in current state
96                     coordinate system
97 
98                     @param rDstSize
99                     Size of the transparency group object, in current
100                     state coordinate system.
101 
102                     @param nAlpha
103                     Alpha value, must be in the range [0,1]
104                 */
105                 TransparencyGroupAction( MtfAutoPtr&					rGroupMtf,
106                                          const Renderer::Parameters& 	rParms,
107                                          const ::basegfx::B2DPoint& 	rDstPoint,
108                                          const ::basegfx::B2DVector& 	rDstSize,
109                                          double 						nAlpha,
110                                          const CanvasSharedPtr&			rCanvas,
111                                          const OutDevState& 			rState );
112 
113                 /** Create new transparency group action.
114 
115 	            	@param rGroupMtf
116                     Metafile that groups all actions to be rendered
117                     transparent.
118 
119                     @param rAlphaGradient
120                     VCL gradient, to be rendered into the action's alpha
121                     channel.
122 
123                     @param rParms
124                     Render parameters
125 
126                     @param rDstPoint
127                     Left, top edge of destination, in current state
128                     coordinate system
129 
130                     @param rDstSize
131                     Size of the transparency group object, in current
132                     state coordinate system.
133                 */
134                 TransparencyGroupAction( MtfAutoPtr&					rGroupMtf,
135                                          GradientAutoPtr&				rAlphaGradient,
136                                          const Renderer::Parameters& 	rParms,
137                                          const ::basegfx::B2DPoint& 	rDstPoint,
138                                          const ::basegfx::B2DVector& 	rDstSize,
139                                          const CanvasSharedPtr&			rCanvas,
140                                          const OutDevState& 			rState );
141 
142                 virtual bool render( const ::basegfx::B2DHomMatrix& rTransformation ) const;
143                 virtual bool render( const ::basegfx::B2DHomMatrix& rTransformation,
144                                      const Subset&					rSubset ) const;
145 
146                 virtual ::basegfx::B2DRange getBounds( const ::basegfx::B2DHomMatrix& rTransformation ) const;
147                 virtual ::basegfx::B2DRange getBounds( const ::basegfx::B2DHomMatrix&	rTransformation,
148                                                        const Subset&					rSubset ) const;
149 
150                 virtual sal_Int32 getActionCount() const;
151 
152             private:
153                 MtfAutoPtr											mpGroupMtf;
154                 GradientAutoPtr										mpAlphaGradient;
155 
156                 const Renderer::Parameters 							maParms;
157 
158                 const ::basegfx::B2DSize 							maDstSize;
159 
160                 mutable uno::Reference< rendering::XBitmap > 		mxBufferBitmap; // contains last rendered version
161                 mutable ::basegfx::B2DHomMatrix						maLastTransformation; // contains last active transformation
162                 mutable Subset										maLastSubset; // contains last effective subset
163 
164                 // transformation for
165                 // mxBufferBitmap content
166                 CanvasSharedPtr										mpCanvas;
167                 rendering::RenderState								maState;
168                 const double										mnAlpha;
169             };
170 
171 
172             /** Setup transformation such that the next render call is
173                 moved rPoint away, and scaled according to the ratio
174                 given by src and dst size.
175             */
176             void implSetupTransform( rendering::RenderState& 	rRenderState,
177                                      const ::basegfx::B2DPoint&	rDstPoint	)
178             {
179                 ::basegfx::B2DHomMatrix	aLocalTransformation;
180 
181                 aLocalTransformation.translate( rDstPoint.getX(),
182                                                 rDstPoint.getY() );
183                 ::canvas::tools::appendToRenderState( rRenderState,
184                                                       aLocalTransformation );
185             }
186 
187             TransparencyGroupAction::TransparencyGroupAction( MtfAutoPtr&					rGroupMtf,
188                                                               const Renderer::Parameters& 	rParms,
189                                                               const ::basegfx::B2DPoint& 	rDstPoint,
190                                                               const ::basegfx::B2DVector& 	rDstSize,
191                                                               double 						nAlpha,
192                                                               const CanvasSharedPtr&		rCanvas,
193                                                               const OutDevState& 			rState ) :
194                 mpGroupMtf( rGroupMtf ),
195                 mpAlphaGradient(),
196                 maParms( rParms ),
197                 maDstSize( rDstSize ),
198                 mxBufferBitmap(),
199                 maLastTransformation(),
200                 mpCanvas( rCanvas ),
201                 maState(),
202                 mnAlpha( nAlpha )
203             {
204                 tools::initRenderState(maState,rState);
205                 implSetupTransform( maState, rDstPoint );
206 
207                 // correct clip (which is relative to original transform)
208                 tools::modifyClip( maState,
209                                    rState,
210                                    rCanvas,
211                                    rDstPoint,
212                                    NULL,
213                                    NULL );
214 
215                 maLastSubset.mnSubsetBegin = 0;
216                 maLastSubset.mnSubsetEnd = -1;
217             }
218 
219             TransparencyGroupAction::TransparencyGroupAction( MtfAutoPtr&					rGroupMtf,
220                                                               GradientAutoPtr&				rAlphaGradient,
221                                                               const Renderer::Parameters& 	rParms,
222                                                               const ::basegfx::B2DPoint& 	rDstPoint,
223                                                               const ::basegfx::B2DVector& 	rDstSize,
224                                                               const CanvasSharedPtr&		rCanvas,
225                                                               const OutDevState& 			rState ) :
226                 mpGroupMtf( rGroupMtf ),
227                 mpAlphaGradient( rAlphaGradient ),
228                 maParms( rParms ),
229                 maDstSize( rDstSize ),
230                 mxBufferBitmap(),
231                 maLastTransformation(),
232                 mpCanvas( rCanvas ),
233                 maState(),
234                 mnAlpha( 1.0 )
235             {
236                 tools::initRenderState(maState,rState);
237                 implSetupTransform( maState, rDstPoint );
238 
239                 // correct clip (which is relative to original transform)
240                 tools::modifyClip( maState,
241                                    rState,
242                                    rCanvas,
243                                    rDstPoint,
244                                    NULL,
245                                    NULL );
246 
247                 maLastSubset.mnSubsetBegin = 0;
248                 maLastSubset.mnSubsetEnd = -1;
249             }
250 
251             // TODO(P3): The whole float transparency handling is a mess,
252             // this should be refactored. What's more, the old idea of
253             // having only internal 'metaactions', and not the original
254             // GDIMetaFile now looks a lot less attractive. Try to move
255             // into the direction of having a direct GDIMetaFile2XCanvas
256             // renderer, and maybe a separate metafile XCanvas
257             // implementation.
258             bool TransparencyGroupAction::render( const ::basegfx::B2DHomMatrix&	rTransformation,
259                                                   const Subset&						rSubset ) const
260             {
261                 RTL_LOGFILE_CONTEXT( aLog, "::cppcanvas::internal::TransparencyGroupAction::render()" );
262                 RTL_LOGFILE_CONTEXT_TRACE1( aLog, "::cppcanvas::internal::TransparencyGroupAction: 0x%X", this );
263 
264                 // determine overall transformation matrix (render, view,
265                 // and passed transformation)
266                 ::basegfx::B2DHomMatrix aTransform;
267                 ::canvas::tools::getRenderStateTransform( aTransform, maState );
268                 aTransform = rTransformation * aTransform;
269 
270                 ::basegfx::B2DHomMatrix aTotalTransform;
271                 ::canvas::tools::getViewStateTransform( aTotalTransform, mpCanvas->getViewState() );
272                 aTotalTransform = aTotalTransform * aTransform;
273 
274                 // since pure translational changes to the transformation
275                 // does not matter, remove them before comparing
276                 aTotalTransform.set( 0, 2, 0.0 );
277                 aTotalTransform.set( 1, 2, 0.0 );
278 
279                 // if there's no buffer bitmap, or as soon as the
280                 // total transformation changes, we've got to
281                 // re-render the bitmap
282                 if( !mxBufferBitmap.is() ||
283                     aTotalTransform != maLastTransformation ||
284                     rSubset.mnSubsetBegin != maLastSubset.mnSubsetBegin ||
285                     rSubset.mnSubsetEnd != maLastSubset.mnSubsetEnd )
286                 {
287                     DBG_TESTSOLARMUTEX();
288 
289                     // determine total scaling factor of the
290                     // transformation matrix - need to make the bitmap
291                     // large enough
292                     ::basegfx::B2DTuple aScale;
293                     ::basegfx::B2DTuple aTranslate;
294                     double				nRotate;
295                     double				nShearX;
296                     if( !aTotalTransform.decompose( aScale,
297                                                     aTranslate,
298                                                     nRotate,
299                                                     nShearX ) )
300                     {
301                         OSL_ENSURE( false,
302                                     "TransparencyGroupAction::render(): non-decomposable transformation" );
303                         return false;
304                     }
305 
306                     // output size of metafile
307                     ::Size aOutputSizePixel( ::basegfx::fround( aScale.getX() * maDstSize.getX() ),
308                                              ::basegfx::fround( aScale.getY() * maDstSize.getY() ) );
309 
310                     // pixel size of cache bitmap: round up to nearest int
311                     ::Size aBitmapSizePixel( static_cast<sal_Int32>( aScale.getX() * maDstSize.getX() )+1,
312                                              static_cast<sal_Int32>( aScale.getY() * maDstSize.getY() )+1 );
313 
314                     ::Point aEmptyPoint;
315 
316                     // render our content into an appropriately sized
317                     // VirtualDevice with alpha channel
318                     VirtualDevice aVDev(
319                         *::Application::GetDefaultDevice(), 0, 0 );
320                     aVDev.SetOutputSizePixel( aBitmapSizePixel );
321                     aVDev.SetMapMode();
322 
323                     if( rSubset.mnSubsetBegin != 0 ||
324                         rSubset.mnSubsetEnd != -1 )
325                     {
326                         // true subset - extract referenced
327                         // metaactions from mpGroupMtf
328                         GDIMetaFile aMtf;
329                         MetaAction* pCurrAct;
330                         int 		nCurrActionIndex;
331 
332                         // extract subset actions
333                         for( nCurrActionIndex=0,
334                                  pCurrAct=mpGroupMtf->FirstAction();
335                              pCurrAct;
336                              ++nCurrActionIndex, pCurrAct = mpGroupMtf->NextAction() )
337                         {
338                             switch( pCurrAct->GetType() )
339                             {
340                                 case META_PUSH_ACTION:
341                                 case META_POP_ACTION:
342                                 case META_CLIPREGION_ACTION:
343                                 case META_ISECTRECTCLIPREGION_ACTION:
344                                 case META_ISECTREGIONCLIPREGION_ACTION:
345                                 case META_MOVECLIPREGION_ACTION:
346                                 case META_LINECOLOR_ACTION:
347                                 case META_FILLCOLOR_ACTION:
348                                 case META_TEXTCOLOR_ACTION:
349                                 case META_TEXTFILLCOLOR_ACTION:
350                                 case META_TEXTLINECOLOR_ACTION:
351                                 case META_TEXTALIGN_ACTION:
352                                 case META_FONT_ACTION:
353                                 case META_RASTEROP_ACTION:
354                                 case META_REFPOINT_ACTION:
355                                 case META_LAYOUTMODE_ACTION:
356                                     // state-changing action - copy as-is
357                                     aMtf.AddAction( pCurrAct->Clone() );
358                                     break;
359 
360                                 case META_GRADIENT_ACTION:
361                                 case META_HATCH_ACTION:
362                                 case META_EPS_ACTION:
363                                 case META_COMMENT_ACTION:
364                                 case META_POINT_ACTION:
365                                 case META_PIXEL_ACTION:
366                                 case META_LINE_ACTION:
367                                 case META_RECT_ACTION:
368                                 case META_ROUNDRECT_ACTION:
369                                 case META_ELLIPSE_ACTION:
370                                 case META_ARC_ACTION:
371                                 case META_PIE_ACTION:
372                                 case META_CHORD_ACTION:
373                                 case META_POLYLINE_ACTION:
374                                 case META_POLYGON_ACTION:
375                                 case META_POLYPOLYGON_ACTION:
376                                 case META_BMP_ACTION:
377                                 case META_BMPSCALE_ACTION:
378                                 case META_BMPSCALEPART_ACTION:
379                                 case META_BMPEX_ACTION:
380                                 case META_BMPEXSCALE_ACTION:
381                                 case META_BMPEXSCALEPART_ACTION:
382                                 case META_MASK_ACTION:
383                                 case META_MASKSCALE_ACTION:
384                                 case META_MASKSCALEPART_ACTION:
385                                 case META_GRADIENTEX_ACTION:
386                                 case META_WALLPAPER_ACTION:
387                                 case META_TRANSPARENT_ACTION:
388                                 case META_FLOATTRANSPARENT_ACTION:
389                                 case META_TEXT_ACTION:
390                                 case META_TEXTARRAY_ACTION:
391                                 case META_TEXTLINE_ACTION:
392                                 case META_TEXTRECT_ACTION:
393                                 case META_STRETCHTEXT_ACTION:
394                                 case META_RENDERGRAPHIC_ACTION:
395                                     // output-generating action - only
396                                     // copy, if we're within the
397                                     // requested subset
398                                     if( rSubset.mnSubsetBegin <= nCurrActionIndex &&
399                                         rSubset.mnSubsetEnd > nCurrActionIndex )
400                                     {
401                                         aMtf.AddAction( pCurrAct->Clone() );
402                                     }
403                                     break;
404 
405                                 default:
406                                     OSL_ENSURE( false,
407                                                 "Unknown meta action type encountered" );
408                                     break;
409                             }
410                         }
411 
412                         aVDev.DrawTransparent( aMtf,
413                                                aEmptyPoint,
414                                                aOutputSizePixel,
415                                                *mpAlphaGradient );
416                     }
417                     else
418                     {
419                         // no subsetting - render whole mtf
420                         aVDev.DrawTransparent( *mpGroupMtf,
421                                                aEmptyPoint,
422                                                aOutputSizePixel,
423                                                *mpAlphaGradient );
424                     }
425 
426 
427                     // update buffered bitmap and transformation
428                     BitmapSharedPtr aBmp( VCLFactory::getInstance().createBitmap(
429                                               mpCanvas,
430                                               aVDev.GetBitmapEx(
431                                                   aEmptyPoint,
432                                                   aBitmapSizePixel ) ) );
433                     mxBufferBitmap = aBmp->getUNOBitmap();
434                     maLastTransformation = aTotalTransform;
435                     maLastSubset = rSubset;
436                 }
437 
438                 // determine target transformation (we can't simply pass
439                 // aTotalTransform as assembled above, since we must take
440                 // the canvas' view state as is, it might contain clipping
441                 // (which, in turn, is relative to the view
442                 // transformation))
443 
444                 // given that aTotalTransform is the identity
445                 // transformation, we could simply render our bitmap
446                 // as-is. Now, since the mxBufferBitmap content already
447                 // accounts for scale changes in the overall
448                 // transformation, we must factor this out
449                 // before. Generally, the transformation matrix should be
450                 // structured like this:
451                 // Translation*Rotation*Shear*Scale. Thus, to neutralize
452                 // the contained scaling, we've got to right-multiply with
453                 // the inverse.
454                 ::basegfx::B2ISize aBmpSize(
455                     ::basegfx::unotools::b2ISizeFromIntegerSize2D( mxBufferBitmap->getSize() ) );
456 
457                 ::basegfx::B2DHomMatrix aScaleCorrection;
458                 aScaleCorrection.scale( (double)maDstSize.getX() / aBmpSize.getX(),
459                                         (double)maDstSize.getY() / aBmpSize.getY() );
460                 aTransform = aTransform * aScaleCorrection;
461 
462                 rendering::RenderState aLocalState( maState );
463                 ::canvas::tools::setRenderStateTransform(aLocalState, aTransform);
464 
465 #ifdef SPECIAL_DEBUG
466                 aLocalState.Clip.clear();
467                 aLocalState.DeviceColor =
468                     ::vcl::unotools::colorToDoubleSequence(
469                         ::Color( 0x80FF0000 ),
470                         mpCanvas->getUNOCanvas()->getDevice()->getDeviceColorSpace() );
471 
472                 if( maState.Clip.is() )
473                     mpCanvas->getUNOCanvas()->fillPolyPolygon( maState.Clip,
474                                                                mpCanvas->getViewState(),
475                                                                aLocalState );
476 
477                 aLocalState.DeviceColor = maState.DeviceColor;
478 #endif
479 
480                 if( ::rtl::math::approxEqual(mnAlpha, 1.0) )
481                 {
482                     // no further alpha changes necessary -> draw directly
483                     mpCanvas->getUNOCanvas()->drawBitmap( mxBufferBitmap,
484                                                           mpCanvas->getViewState(),
485                                                           aLocalState );
486                 }
487                 else
488                 {
489                     // add alpha modulation value to DeviceColor
490                     uno::Sequence<rendering::ARGBColor> aCols(1);
491                     aCols[0] = rendering::ARGBColor( mnAlpha, 1.0, 1.0, 1.0);
492                     aLocalState.DeviceColor =
493                         mpCanvas->getUNOCanvas()->getDevice()->getDeviceColorSpace()->convertFromARGB(
494                             aCols);
495 
496                     mpCanvas->getUNOCanvas()->drawBitmapModulated( mxBufferBitmap,
497                                                                    mpCanvas->getViewState(),
498                                                                    aLocalState );
499                 }
500 
501                 return true;
502             }
503 
504             // TODO(P3): The whole float transparency handling is a mess,
505             // this should be refactored. What's more, the old idea of
506             // having only internal 'metaactions', and not the original
507             // GDIMetaFile now looks a lot less attractive. Try to move
508             // into the direction of having a direct GDIMetaFile2XCanvas
509             // renderer, and maybe a separate metafile XCanvas
510             // implementation.
511             bool TransparencyGroupAction::render( const ::basegfx::B2DHomMatrix& rTransformation ) const
512             {
513                 Subset aSubset;
514 
515                 aSubset.mnSubsetBegin = 0;
516                 aSubset.mnSubsetEnd   = -1;
517 
518                 return render( rTransformation, aSubset );
519             }
520 
521             ::basegfx::B2DRange TransparencyGroupAction::getBounds( const ::basegfx::B2DHomMatrix&	rTransformation ) const
522             {
523                 rendering::RenderState aLocalState( maState );
524                 ::canvas::tools::prependToRenderState(aLocalState, rTransformation);
525 
526                 return tools::calcDevicePixelBounds(
527                     ::basegfx::B2DRange( 0,0,
528                                          maDstSize.getX(),
529                                          maDstSize.getY() ),
530                     mpCanvas->getViewState(),
531                     aLocalState );
532             }
533 
534             ::basegfx::B2DRange TransparencyGroupAction::getBounds( const ::basegfx::B2DHomMatrix&	rTransformation,
535                                                                     const Subset&					rSubset ) const
536             {
537                 // TODO(F3): Currently, the bounds for
538                 // TransparencyGroupAction subsets equal those of the
539                 // full set, although this action is able to render
540                 // true subsets.
541 
542                 // polygon only contains a single action, empty bounds
543                 // if subset requests different range
544                 if( rSubset.mnSubsetBegin != 0 ||
545                     rSubset.mnSubsetEnd != 1 )
546                     return ::basegfx::B2DRange();
547 
548                 return getBounds( rTransformation );
549             }
550 
551             sal_Int32 TransparencyGroupAction::getActionCount() const
552             {
553                 return mpGroupMtf.get() ? mpGroupMtf->GetActionCount() : 0;
554             }
555 
556         }
557 
558         ActionSharedPtr TransparencyGroupActionFactory::createTransparencyGroupAction( MtfAutoPtr&					rGroupMtf,
559                                                                                        const Renderer::Parameters&	rParms,
560                                                                                        const ::basegfx::B2DPoint& 	rDstPoint,
561                                                                                        const ::basegfx::B2DVector& 	rDstSize,
562                                                                                        double 						nAlpha,
563                                                                                        const CanvasSharedPtr&		rCanvas,
564                                                                                        const OutDevState& 			rState )
565         {
566             return ActionSharedPtr( new TransparencyGroupAction(rGroupMtf,
567                                                                 rParms,
568                                                                 rDstPoint,
569                                                                 rDstSize,
570                                                                 nAlpha,
571                                                                 rCanvas,
572                                                                 rState ) );
573         }
574 
575         ActionSharedPtr TransparencyGroupActionFactory::createTransparencyGroupAction( MtfAutoPtr&					rGroupMtf,
576                                                                                        GradientAutoPtr&				rAlphaGradient,
577                                                                                        const Renderer::Parameters&	rParms,
578                                                                                        const ::basegfx::B2DPoint& 	rDstPoint,
579                                                                                        const ::basegfx::B2DVector&  rDstSize,
580                                                                                        const CanvasSharedPtr&		rCanvas,
581                                                                                        const OutDevState& 			rState )
582         {
583             return ActionSharedPtr( new TransparencyGroupAction(rGroupMtf,
584                                                                 rAlphaGradient,
585                                                                 rParms,
586                                                                 rDstPoint,
587                                                                 rDstSize,
588                                                                 rCanvas,
589                                                                 rState ) );
590         }
591 
592     }
593 }
594