1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: transparencygroupaction.cxx,v $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_cppcanvas.hxx"
34 #include <tools/gen.hxx>
36 #include <canvas/debug.hxx>
37 #include <canvas/verbosetrace.hxx>
38 #include <canvas/canvastools.hxx>
40 #include <rtl/logfile.hxx>
42 #include <com/sun/star/rendering/XBitmap.hpp>
43 #include <com/sun/star/rendering/XCanvas.hpp>
45 #include <rtl/math.hxx>
47 #include <vcl/metaact.hxx>
48 #include <vcl/bitmapex.hxx>
49 #include <vcl/canvastools.hxx>
50 #include <vcl/svapp.hxx>
51 #include <vcl/outdev.hxx>
52 #include <vcl/virdev.hxx>
53 #include <vcl/virdev.hxx>
54 #include <vcl/gdimtf.hxx>
55 #include <vcl/gradient.hxx>
57 #include <canvas/canvastools.hxx>
59 #include <basegfx/range/b2drange.hxx>
60 #include <basegfx/point/b2dpoint.hxx>
61 #include <basegfx/vector/b2dsize.hxx>
62 #include <basegfx/numeric/ftools.hxx>
63 #include <basegfx/matrix/b2dhommatrix.hxx>
64 #include <basegfx/tuple/b2dtuple.hxx>
65 #include <basegfx/tools/canvastools.hxx>
67 #include <boost/utility.hpp>
69 #include "transparencygroupaction.hxx"
70 #include "outdevstate.hxx"
71 #include "mtftools.hxx"
72 #include "cppcanvas/vclfactory.hxx"
75 using namespace ::com::sun::star
;
81 // free support functions
82 // ======================
85 class TransparencyGroupAction
: public Action
, private ::boost::noncopyable
88 /** Create new transparency group action.
91 Metafile that groups all actions to be rendered
98 Left, top edge of destination, in current state
102 Size of the transparency group object, in current
103 state coordinate system.
106 Alpha value, must be in the range [0,1]
108 TransparencyGroupAction( MtfAutoPtr
& rGroupMtf
,
109 const Renderer::Parameters
& rParms
,
110 const ::basegfx::B2DPoint
& rDstPoint
,
111 const ::basegfx::B2DVector
& rDstSize
,
113 const CanvasSharedPtr
& rCanvas
,
114 const OutDevState
& rState
);
116 /** Create new transparency group action.
119 Metafile that groups all actions to be rendered
122 @param rAlphaGradient
123 VCL gradient, to be rendered into the action's alpha
130 Left, top edge of destination, in current state
134 Size of the transparency group object, in current
135 state coordinate system.
137 TransparencyGroupAction( MtfAutoPtr
& rGroupMtf
,
138 GradientAutoPtr
& rAlphaGradient
,
139 const Renderer::Parameters
& rParms
,
140 const ::basegfx::B2DPoint
& rDstPoint
,
141 const ::basegfx::B2DVector
& rDstSize
,
142 const CanvasSharedPtr
& rCanvas
,
143 const OutDevState
& rState
);
145 virtual bool render( const ::basegfx::B2DHomMatrix
& rTransformation
) const;
146 virtual bool render( const ::basegfx::B2DHomMatrix
& rTransformation
,
147 const Subset
& rSubset
) const;
149 virtual ::basegfx::B2DRange
getBounds( const ::basegfx::B2DHomMatrix
& rTransformation
) const;
150 virtual ::basegfx::B2DRange
getBounds( const ::basegfx::B2DHomMatrix
& rTransformation
,
151 const Subset
& rSubset
) const;
153 virtual sal_Int32
getActionCount() const;
156 MtfAutoPtr mpGroupMtf
;
157 GradientAutoPtr mpAlphaGradient
;
159 const Renderer::Parameters maParms
;
161 const ::basegfx::B2DSize maDstSize
;
163 mutable uno::Reference
< rendering::XBitmap
> mxBufferBitmap
; // contains last rendered version
164 mutable ::basegfx::B2DHomMatrix maLastTransformation
; // contains last active transformation
165 mutable Subset maLastSubset
; // contains last effective subset
167 // transformation for
168 // mxBufferBitmap content
169 CanvasSharedPtr mpCanvas
;
170 rendering::RenderState maState
;
171 const double mnAlpha
;
175 /** Setup transformation such that the next render call is
176 moved rPoint away, and scaled according to the ratio
177 given by src and dst size.
179 void implSetupTransform( rendering::RenderState
& rRenderState
,
180 const ::basegfx::B2DPoint
& rDstPoint
)
182 ::basegfx::B2DHomMatrix aLocalTransformation
;
184 aLocalTransformation
.translate( rDstPoint
.getX(),
186 ::canvas::tools::appendToRenderState( rRenderState
,
187 aLocalTransformation
);
190 TransparencyGroupAction::TransparencyGroupAction( MtfAutoPtr
& rGroupMtf
,
191 const Renderer::Parameters
& rParms
,
192 const ::basegfx::B2DPoint
& rDstPoint
,
193 const ::basegfx::B2DVector
& rDstSize
,
195 const CanvasSharedPtr
& rCanvas
,
196 const OutDevState
& rState
) :
197 mpGroupMtf( rGroupMtf
),
200 maDstSize( rDstSize
),
202 maLastTransformation(),
207 tools::initRenderState(maState
,rState
);
208 implSetupTransform( maState
, rDstPoint
);
210 // correct clip (which is relative to original transform)
211 tools::modifyClip( maState
,
218 maLastSubset
.mnSubsetBegin
= 0;
219 maLastSubset
.mnSubsetEnd
= -1;
222 TransparencyGroupAction::TransparencyGroupAction( MtfAutoPtr
& rGroupMtf
,
223 GradientAutoPtr
& rAlphaGradient
,
224 const Renderer::Parameters
& rParms
,
225 const ::basegfx::B2DPoint
& rDstPoint
,
226 const ::basegfx::B2DVector
& rDstSize
,
227 const CanvasSharedPtr
& rCanvas
,
228 const OutDevState
& rState
) :
229 mpGroupMtf( rGroupMtf
),
230 mpAlphaGradient( rAlphaGradient
),
232 maDstSize( rDstSize
),
234 maLastTransformation(),
239 tools::initRenderState(maState
,rState
);
240 implSetupTransform( maState
, rDstPoint
);
242 // correct clip (which is relative to original transform)
243 tools::modifyClip( maState
,
250 maLastSubset
.mnSubsetBegin
= 0;
251 maLastSubset
.mnSubsetEnd
= -1;
254 // TODO(P3): The whole float transparency handling is a mess,
255 // this should be refactored. What's more, the old idea of
256 // having only internal 'metaactions', and not the original
257 // GDIMetaFile now looks a lot less attractive. Try to move
258 // into the direction of having a direct GDIMetaFile2XCanvas
259 // renderer, and maybe a separate metafile XCanvas
261 bool TransparencyGroupAction::render( const ::basegfx::B2DHomMatrix
& rTransformation
,
262 const Subset
& rSubset
) const
264 RTL_LOGFILE_CONTEXT( aLog
, "::cppcanvas::internal::TransparencyGroupAction::render()" );
265 RTL_LOGFILE_CONTEXT_TRACE1( aLog
, "::cppcanvas::internal::TransparencyGroupAction: 0x%X", this );
267 // determine overall transformation matrix (render, view,
268 // and passed transformation)
269 ::basegfx::B2DHomMatrix aTransform
;
270 ::canvas::tools::getRenderStateTransform( aTransform
, maState
);
271 aTransform
= rTransformation
* aTransform
;
273 ::basegfx::B2DHomMatrix aTotalTransform
;
274 ::canvas::tools::getViewStateTransform( aTotalTransform
, mpCanvas
->getViewState() );
275 aTotalTransform
= aTotalTransform
* aTransform
;
277 // since pure translational changes to the transformation
278 // does not matter, remove them before comparing
279 aTotalTransform
.set( 0, 2, 0.0 );
280 aTotalTransform
.set( 1, 2, 0.0 );
282 // if there's no buffer bitmap, or as soon as the
283 // total transformation changes, we've got to
284 // re-render the bitmap
285 if( !mxBufferBitmap
.is() ||
286 aTotalTransform
!= maLastTransformation
||
287 rSubset
.mnSubsetBegin
!= maLastSubset
.mnSubsetBegin
||
288 rSubset
.mnSubsetEnd
!= maLastSubset
.mnSubsetEnd
)
290 DBG_TESTSOLARMUTEX();
292 // determine total scaling factor of the
293 // transformation matrix - need to make the bitmap
295 ::basegfx::B2DTuple aScale
;
296 ::basegfx::B2DTuple aTranslate
;
299 if( !aTotalTransform
.decompose( aScale
,
305 "TransparencyGroupAction::render(): non-decomposable transformation" );
309 // output size of metafile
310 ::Size
aOutputSizePixel( ::basegfx::fround( aScale
.getX() * maDstSize
.getX() ),
311 ::basegfx::fround( aScale
.getY() * maDstSize
.getY() ) );
313 // pixel size of cache bitmap: round up to nearest int
314 ::Size
aBitmapSizePixel( static_cast<sal_Int32
>( aScale
.getX() * maDstSize
.getX() )+1,
315 static_cast<sal_Int32
>( aScale
.getY() * maDstSize
.getY() )+1 );
319 // render our content into an appropriately sized
320 // VirtualDevice with alpha channel
322 *::Application::GetDefaultDevice(), 0, 0 );
323 aVDev
.SetOutputSizePixel( aBitmapSizePixel
);
326 if( rSubset
.mnSubsetBegin
!= 0 ||
327 rSubset
.mnSubsetEnd
!= -1 )
329 // true subset - extract referenced
330 // metaactions from mpGroupMtf
332 MetaAction
* pCurrAct
;
333 int nCurrActionIndex
;
335 // extract subset actions
336 for( nCurrActionIndex
=0,
337 pCurrAct
=mpGroupMtf
->FirstAction();
339 ++nCurrActionIndex
, pCurrAct
= mpGroupMtf
->NextAction() )
341 switch( pCurrAct
->GetType() )
343 case META_PUSH_ACTION
:
344 case META_POP_ACTION
:
345 case META_CLIPREGION_ACTION
:
346 case META_ISECTRECTCLIPREGION_ACTION
:
347 case META_ISECTREGIONCLIPREGION_ACTION
:
348 case META_MOVECLIPREGION_ACTION
:
349 case META_LINECOLOR_ACTION
:
350 case META_FILLCOLOR_ACTION
:
351 case META_TEXTCOLOR_ACTION
:
352 case META_TEXTFILLCOLOR_ACTION
:
353 case META_TEXTLINECOLOR_ACTION
:
354 case META_TEXTALIGN_ACTION
:
355 case META_FONT_ACTION
:
356 case META_RASTEROP_ACTION
:
357 case META_REFPOINT_ACTION
:
358 case META_LAYOUTMODE_ACTION
:
359 // state-changing action - copy as-is
360 aMtf
.AddAction( pCurrAct
->Clone() );
363 case META_GRADIENT_ACTION
:
364 case META_HATCH_ACTION
:
365 case META_EPS_ACTION
:
366 case META_COMMENT_ACTION
:
367 case META_POINT_ACTION
:
368 case META_PIXEL_ACTION
:
369 case META_LINE_ACTION
:
370 case META_RECT_ACTION
:
371 case META_ROUNDRECT_ACTION
:
372 case META_ELLIPSE_ACTION
:
373 case META_ARC_ACTION
:
374 case META_PIE_ACTION
:
375 case META_CHORD_ACTION
:
376 case META_POLYLINE_ACTION
:
377 case META_POLYGON_ACTION
:
378 case META_POLYPOLYGON_ACTION
:
379 case META_BMP_ACTION
:
380 case META_BMPSCALE_ACTION
:
381 case META_BMPSCALEPART_ACTION
:
382 case META_BMPEX_ACTION
:
383 case META_BMPEXSCALE_ACTION
:
384 case META_BMPEXSCALEPART_ACTION
:
385 case META_MASK_ACTION
:
386 case META_MASKSCALE_ACTION
:
387 case META_MASKSCALEPART_ACTION
:
388 case META_GRADIENTEX_ACTION
:
389 case META_WALLPAPER_ACTION
:
390 case META_TRANSPARENT_ACTION
:
391 case META_FLOATTRANSPARENT_ACTION
:
392 case META_TEXT_ACTION
:
393 case META_TEXTARRAY_ACTION
:
394 case META_TEXTLINE_ACTION
:
395 case META_TEXTRECT_ACTION
:
396 case META_STRETCHTEXT_ACTION
:
397 // output-generating action - only
398 // copy, if we're within the
400 if( rSubset
.mnSubsetBegin
<= nCurrActionIndex
&&
401 rSubset
.mnSubsetEnd
> nCurrActionIndex
)
403 aMtf
.AddAction( pCurrAct
->Clone() );
409 "Unknown meta action type encountered" );
414 aVDev
.DrawTransparent( aMtf
,
421 // no subsetting - render whole mtf
422 aVDev
.DrawTransparent( *mpGroupMtf
,
429 // update buffered bitmap and transformation
430 BitmapSharedPtr
aBmp( VCLFactory::getInstance().createBitmap(
434 aBitmapSizePixel
) ) );
435 mxBufferBitmap
= aBmp
->getUNOBitmap();
436 maLastTransformation
= aTotalTransform
;
437 maLastSubset
= rSubset
;
440 // determine target transformation (we can't simply pass
441 // aTotalTransform as assembled above, since we must take
442 // the canvas' view state as is, it might contain clipping
443 // (which, in turn, is relative to the view
446 // given that aTotalTransform is the identity
447 // transformation, we could simply render our bitmap
448 // as-is. Now, since the mxBufferBitmap content already
449 // accounts for scale changes in the overall
450 // transformation, we must factor this out
451 // before. Generally, the transformation matrix should be
452 // structured like this:
453 // Translation*Rotation*Shear*Scale. Thus, to neutralize
454 // the contained scaling, we've got to right-multiply with
456 ::basegfx::B2ISize
aBmpSize(
457 ::basegfx::unotools::b2ISizeFromIntegerSize2D( mxBufferBitmap
->getSize() ) );
459 ::basegfx::B2DHomMatrix aScaleCorrection
;
460 aScaleCorrection
.scale( (double)maDstSize
.getX() / aBmpSize
.getX(),
461 (double)maDstSize
.getY() / aBmpSize
.getY() );
462 aTransform
= aTransform
* aScaleCorrection
;
464 rendering::RenderState
aLocalState( maState
);
465 ::canvas::tools::setRenderStateTransform(aLocalState
, aTransform
);
468 aLocalState
.Clip
.clear();
469 aLocalState
.DeviceColor
=
470 ::vcl::unotools::colorToDoubleSequence(
471 ::Color( 0x80FF0000 ),
472 mpCanvas
->getUNOCanvas()->getDevice()->getDeviceColorSpace() );
474 if( maState
.Clip
.is() )
475 mpCanvas
->getUNOCanvas()->fillPolyPolygon( maState
.Clip
,
476 mpCanvas
->getViewState(),
479 aLocalState
.DeviceColor
= maState
.DeviceColor
;
482 if( ::rtl::math::approxEqual(mnAlpha
, 1.0) )
484 // no further alpha changes necessary -> draw directly
485 mpCanvas
->getUNOCanvas()->drawBitmap( mxBufferBitmap
,
486 mpCanvas
->getViewState(),
491 // add alpha modulation value to DeviceColor
492 uno::Sequence
<rendering::ARGBColor
> aCols(1);
493 aCols
[0] = rendering::ARGBColor( mnAlpha
, 1.0, 1.0, 1.0);
494 aLocalState
.DeviceColor
=
495 mpCanvas
->getUNOCanvas()->getDevice()->getDeviceColorSpace()->convertFromARGB(
498 mpCanvas
->getUNOCanvas()->drawBitmapModulated( mxBufferBitmap
,
499 mpCanvas
->getViewState(),
506 // TODO(P3): The whole float transparency handling is a mess,
507 // this should be refactored. What's more, the old idea of
508 // having only internal 'metaactions', and not the original
509 // GDIMetaFile now looks a lot less attractive. Try to move
510 // into the direction of having a direct GDIMetaFile2XCanvas
511 // renderer, and maybe a separate metafile XCanvas
513 bool TransparencyGroupAction::render( const ::basegfx::B2DHomMatrix
& rTransformation
) const
517 aSubset
.mnSubsetBegin
= 0;
518 aSubset
.mnSubsetEnd
= -1;
520 return render( rTransformation
, aSubset
);
523 ::basegfx::B2DRange
TransparencyGroupAction::getBounds( const ::basegfx::B2DHomMatrix
& rTransformation
) const
525 rendering::RenderState
aLocalState( maState
);
526 ::canvas::tools::prependToRenderState(aLocalState
, rTransformation
);
528 return tools::calcDevicePixelBounds(
529 ::basegfx::B2DRange( 0,0,
532 mpCanvas
->getViewState(),
536 ::basegfx::B2DRange
TransparencyGroupAction::getBounds( const ::basegfx::B2DHomMatrix
& rTransformation
,
537 const Subset
& rSubset
) const
539 // TODO(F3): Currently, the bounds for
540 // TransparencyGroupAction subsets equal those of the
541 // full set, although this action is able to render
544 // polygon only contains a single action, empty bounds
545 // if subset requests different range
546 if( rSubset
.mnSubsetBegin
!= 0 ||
547 rSubset
.mnSubsetEnd
!= 1 )
548 return ::basegfx::B2DRange();
550 return getBounds( rTransformation
);
553 sal_Int32
TransparencyGroupAction::getActionCount() const
555 return mpGroupMtf
.get() ? mpGroupMtf
->GetActionCount() : 0;
560 ActionSharedPtr
TransparencyGroupActionFactory::createTransparencyGroupAction( MtfAutoPtr
& rGroupMtf
,
561 const Renderer::Parameters
& rParms
,
562 const ::basegfx::B2DPoint
& rDstPoint
,
563 const ::basegfx::B2DVector
& rDstSize
,
565 const CanvasSharedPtr
& rCanvas
,
566 const OutDevState
& rState
)
568 return ActionSharedPtr( new TransparencyGroupAction(rGroupMtf
,
577 ActionSharedPtr
TransparencyGroupActionFactory::createTransparencyGroupAction( MtfAutoPtr
& rGroupMtf
,
578 GradientAutoPtr
& rAlphaGradient
,
579 const Renderer::Parameters
& rParms
,
580 const ::basegfx::B2DPoint
& rDstPoint
,
581 const ::basegfx::B2DVector
& rDstSize
,
582 const CanvasSharedPtr
& rCanvas
,
583 const OutDevState
& rState
)
585 return ActionSharedPtr( new TransparencyGroupAction(rGroupMtf
,