1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <sal/config.h>
24 #include <tools/gen.hxx>
26 #include <canvas/debug.hxx>
27 #include <canvas/verbosetrace.hxx>
28 #include <canvas/canvastools.hxx>
30 #include <com/sun/star/rendering/XBitmap.hpp>
31 #include <com/sun/star/rendering/XCanvas.hpp>
33 #include <rtl/math.hxx>
35 #include <vcl/metaact.hxx>
36 #include <vcl/bitmapex.hxx>
37 #include <vcl/canvastools.hxx>
38 #include <vcl/svapp.hxx>
39 #include <vcl/outdev.hxx>
40 #include <vcl/virdev.hxx>
41 #include <vcl/gdimtf.hxx>
42 #include <vcl/gradient.hxx>
44 #include <basegfx/range/b2drange.hxx>
45 #include <basegfx/point/b2dpoint.hxx>
46 #include <basegfx/vector/b2dsize.hxx>
47 #include <basegfx/numeric/ftools.hxx>
48 #include <basegfx/matrix/b2dhommatrix.hxx>
49 #include <basegfx/tuple/b2dtuple.hxx>
50 #include <basegfx/tools/canvastools.hxx>
52 #include <boost/utility.hpp>
54 #include "transparencygroupaction.hxx"
55 #include "outdevstate.hxx"
56 #include "mtftools.hxx"
57 #include "cppcanvas/vclfactory.hxx"
60 using namespace ::com::sun::star
;
66 // free support functions
67 // ======================
70 class TransparencyGroupAction
: public Action
, private ::boost::noncopyable
73 /** Create new transparency group action.
76 Metafile that groups all actions to be rendered
80 VCL gradient, to be rendered into the action's alpha
87 Left, top edge of destination, in current state
91 Size of the transparency group object, in current
92 state coordinate system.
94 TransparencyGroupAction( MtfAutoPtr
&& rGroupMtf
,
95 GradientAutoPtr
&& rAlphaGradient
,
96 const Renderer::Parameters
& rParms
,
97 const ::basegfx::B2DPoint
& rDstPoint
,
98 const ::basegfx::B2DVector
& rDstSize
,
99 const CanvasSharedPtr
& rCanvas
,
100 const OutDevState
& rState
);
102 virtual bool render( const ::basegfx::B2DHomMatrix
& rTransformation
) const SAL_OVERRIDE
;
103 virtual bool renderSubset( const ::basegfx::B2DHomMatrix
& rTransformation
,
104 const Subset
& rSubset
) const SAL_OVERRIDE
;
106 virtual ::basegfx::B2DRange
getBounds( const ::basegfx::B2DHomMatrix
& rTransformation
) const SAL_OVERRIDE
;
107 virtual ::basegfx::B2DRange
getBounds( const ::basegfx::B2DHomMatrix
& rTransformation
,
108 const Subset
& rSubset
) const SAL_OVERRIDE
;
110 virtual sal_Int32
getActionCount() const SAL_OVERRIDE
;
113 MtfAutoPtr mpGroupMtf
;
114 GradientAutoPtr mpAlphaGradient
;
116 const Renderer::Parameters maParms
;
118 const ::basegfx::B2DSize maDstSize
;
120 mutable uno::Reference
< rendering::XBitmap
> mxBufferBitmap
; // contains last rendered version
121 mutable ::basegfx::B2DHomMatrix maLastTransformation
; // contains last active transformation
122 mutable Subset maLastSubset
; // contains last effective subset
124 // transformation for
125 // mxBufferBitmap content
126 CanvasSharedPtr mpCanvas
;
127 rendering::RenderState maState
;
128 const double mnAlpha
;
132 /** Setup transformation such that the next render call is
133 moved rPoint away, and scaled according to the ratio
134 given by src and dst size.
136 void implSetupTransform( rendering::RenderState
& rRenderState
,
137 const ::basegfx::B2DPoint
& rDstPoint
)
139 ::basegfx::B2DHomMatrix aLocalTransformation
;
141 aLocalTransformation
.translate( rDstPoint
.getX(),
143 ::canvas::tools::appendToRenderState( rRenderState
,
144 aLocalTransformation
);
147 TransparencyGroupAction::TransparencyGroupAction( MtfAutoPtr
&& rGroupMtf
,
148 GradientAutoPtr
&& rAlphaGradient
,
149 const Renderer::Parameters
& rParms
,
150 const ::basegfx::B2DPoint
& rDstPoint
,
151 const ::basegfx::B2DVector
& rDstSize
,
152 const CanvasSharedPtr
& rCanvas
,
153 const OutDevState
& rState
) :
154 mpGroupMtf( std::move(rGroupMtf
) ),
155 mpAlphaGradient( std::move(rAlphaGradient
) ),
157 maDstSize( rDstSize
),
159 maLastTransformation(),
164 tools::initRenderState(maState
,rState
);
165 implSetupTransform( maState
, rDstPoint
);
167 // correct clip (which is relative to original transform)
168 tools::modifyClip( maState
,
175 maLastSubset
.mnSubsetBegin
= 0;
176 maLastSubset
.mnSubsetEnd
= -1;
179 // TODO(P3): The whole float transparency handling is a mess,
180 // this should be refactored. What's more, the old idea of
181 // having only internal 'metaactions', and not the original
182 // GDIMetaFile now looks a lot less attractive. Try to move
183 // into the direction of having a direct GDIMetaFile2XCanvas
184 // renderer, and maybe a separate metafile XCanvas
186 bool TransparencyGroupAction::renderSubset( const ::basegfx::B2DHomMatrix
& rTransformation
,
187 const Subset
& rSubset
) const
189 SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::TransparencyGroupAction::renderSubset()" );
190 SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::TransparencyGroupAction: 0x" << std::hex
<< this );
192 // determine overall transformation matrix (render, view,
193 // and passed transformation)
194 ::basegfx::B2DHomMatrix aTransform
;
195 ::canvas::tools::getRenderStateTransform( aTransform
, maState
);
196 aTransform
= rTransformation
* aTransform
;
198 ::basegfx::B2DHomMatrix aTotalTransform
;
199 ::canvas::tools::getViewStateTransform( aTotalTransform
, mpCanvas
->getViewState() );
200 aTotalTransform
= aTotalTransform
* aTransform
;
202 // since pure translational changes to the transformation
203 // does not matter, remove them before comparing
204 aTotalTransform
.set( 0, 2, 0.0 );
205 aTotalTransform
.set( 1, 2, 0.0 );
207 // determine total scaling factor of the
208 // transformation matrix - need to make the bitmap
210 ::basegfx::B2DTuple aScale
;
211 ::basegfx::B2DTuple aTranslate
;
214 if( !aTotalTransform
.decompose( aScale
,
219 SAL_WARN( "cppcanvas.emf", "TransparencyGroupAction::renderSubset(): non-decomposable transformation" );
223 // if there's no buffer bitmap, or as soon as the
224 // total transformation changes, we've got to
225 // re-render the bitmap
226 if( !mxBufferBitmap
.is() ||
227 aTotalTransform
!= maLastTransformation
||
228 rSubset
.mnSubsetBegin
!= maLastSubset
.mnSubsetBegin
||
229 rSubset
.mnSubsetEnd
!= maLastSubset
.mnSubsetEnd
)
231 DBG_TESTSOLARMUTEX();
233 // output size of metafile
234 ::Size
aOutputSizePixel( ::basegfx::fround( aScale
.getX() * maDstSize
.getX() ),
235 ::basegfx::fround( aScale
.getY() * maDstSize
.getY() ) );
237 // pixel size of cache bitmap: round up to nearest int
238 ::Size
aBitmapSizePixel( static_cast<sal_Int32
>( aScale
.getX() * maDstSize
.getX() )+1,
239 static_cast<sal_Int32
>( aScale
.getY() * maDstSize
.getY() )+1 );
243 // render our content into an appropriately sized
244 // VirtualDevice with alpha channel
245 ScopedVclPtrInstance
<VirtualDevice
> aVDev(
246 *::Application::GetDefaultDevice(), 0, 0 );
247 aVDev
->SetOutputSizePixel( aBitmapSizePixel
);
250 if( rSubset
.mnSubsetBegin
!= 0 ||
251 rSubset
.mnSubsetEnd
!= -1 )
253 // true subset - extract referenced
254 // metaactions from mpGroupMtf
256 MetaAction
* pCurrAct
;
257 int nCurrActionIndex
;
259 // extract subset actions
260 for( nCurrActionIndex
=0,
261 pCurrAct
=mpGroupMtf
->FirstAction();
263 ++nCurrActionIndex
, pCurrAct
= mpGroupMtf
->NextAction() )
265 switch( pCurrAct
->GetType() )
267 case MetaActionType::PUSH
:
268 case MetaActionType::POP
:
269 case MetaActionType::CLIPREGION
:
270 case MetaActionType::ISECTRECTCLIPREGION
:
271 case MetaActionType::ISECTREGIONCLIPREGION
:
272 case MetaActionType::MOVECLIPREGION
:
273 case MetaActionType::LINECOLOR
:
274 case MetaActionType::FILLCOLOR
:
275 case MetaActionType::TEXTCOLOR
:
276 case MetaActionType::TEXTFILLCOLOR
:
277 case MetaActionType::TEXTLINECOLOR
:
278 case MetaActionType::TEXTALIGN
:
279 case MetaActionType::FONT
:
280 case MetaActionType::RASTEROP
:
281 case MetaActionType::REFPOINT
:
282 case MetaActionType::LAYOUTMODE
:
283 // state-changing action - copy as-is
284 aMtf
.AddAction( pCurrAct
->Clone() );
287 case MetaActionType::GRADIENT
:
288 case MetaActionType::HATCH
:
289 case MetaActionType::EPS
:
290 case MetaActionType::COMMENT
:
291 case MetaActionType::POINT
:
292 case MetaActionType::PIXEL
:
293 case MetaActionType::LINE
:
294 case MetaActionType::RECT
:
295 case MetaActionType::ROUNDRECT
:
296 case MetaActionType::ELLIPSE
:
297 case MetaActionType::ARC
:
298 case MetaActionType::PIE
:
299 case MetaActionType::CHORD
:
300 case MetaActionType::POLYLINE
:
301 case MetaActionType::POLYGON
:
302 case MetaActionType::POLYPOLYGON
:
303 case MetaActionType::BMP
:
304 case MetaActionType::BMPSCALE
:
305 case MetaActionType::BMPSCALEPART
:
306 case MetaActionType::BMPEX
:
307 case MetaActionType::BMPEXSCALE
:
308 case MetaActionType::BMPEXSCALEPART
:
309 case MetaActionType::MASK
:
310 case MetaActionType::MASKSCALE
:
311 case MetaActionType::MASKSCALEPART
:
312 case MetaActionType::GRADIENTEX
:
313 case MetaActionType::WALLPAPER
:
314 case MetaActionType::Transparent
:
315 case MetaActionType::FLOATTRANSPARENT
:
316 case MetaActionType::TEXT
:
317 case MetaActionType::TEXTARRAY
:
318 case MetaActionType::TEXTLINE
:
319 case MetaActionType::TEXTRECT
:
320 case MetaActionType::STRETCHTEXT
:
321 // output-generating action - only
322 // copy, if we're within the
324 if( rSubset
.mnSubsetBegin
<= nCurrActionIndex
&&
325 rSubset
.mnSubsetEnd
> nCurrActionIndex
)
327 aMtf
.AddAction( pCurrAct
->Clone() );
332 SAL_WARN( "cppcanvas.emf", "Unknown meta action type encountered" );
337 aVDev
->DrawTransparent( aMtf
,
344 // no subsetting - render whole mtf
345 aVDev
->DrawTransparent( *mpGroupMtf
,
352 // update buffered bitmap and transformation
353 BitmapSharedPtr
aBmp( VCLFactory::createBitmap(
357 aBitmapSizePixel
) ) );
358 mxBufferBitmap
= aBmp
->getUNOBitmap();
359 maLastTransformation
= aTotalTransform
;
360 maLastSubset
= rSubset
;
363 // determine target transformation (we can't simply pass
364 // aTotalTransform as assembled above, since we must take
365 // the canvas' view state as is, it might contain clipping
366 // (which, in turn, is relative to the view
369 // given that aTotalTransform is the identity
370 // transformation, we could simply render our bitmap
371 // as-is. Now, since the mxBufferBitmap content already
372 // accounts for scale changes in the overall
373 // transformation, we must factor this out
374 // before. Generally, the transformation matrix should be
375 // structured like this:
376 // Translation*Rotation*Shear*Scale. Thus, to neutralize
377 // the contained scaling, we've got to right-multiply with
379 ::basegfx::B2DHomMatrix aScaleCorrection
;
380 aScaleCorrection
.scale( 1/aScale
.getX(), 1/aScale
.getY() );
381 aTransform
= aTransform
* aScaleCorrection
;
383 rendering::RenderState
aLocalState( maState
);
384 ::canvas::tools::setRenderStateTransform(aLocalState
, aTransform
);
386 #if OSL_DEBUG_LEVEL > 2
387 aLocalState
.Clip
.clear();
388 aLocalState
.DeviceColor
=
389 vcl::unotools::colorToDoubleSequence(
390 ::Color( 0x80FF0000 ),
391 mpCanvas
->getUNOCanvas()->getDevice()->getDeviceColorSpace() );
393 if( maState
.Clip
.is() )
394 mpCanvas
->getUNOCanvas()->fillPolyPolygon( maState
.Clip
,
395 mpCanvas
->getViewState(),
398 aLocalState
.DeviceColor
= maState
.DeviceColor
;
401 if( ::rtl::math::approxEqual(mnAlpha
, 1.0) )
403 // no further alpha changes necessary -> draw directly
404 mpCanvas
->getUNOCanvas()->drawBitmap( mxBufferBitmap
,
405 mpCanvas
->getViewState(),
410 // add alpha modulation value to DeviceColor
411 uno::Sequence
<rendering::ARGBColor
> aCols(1);
412 aCols
[0] = rendering::ARGBColor( mnAlpha
, 1.0, 1.0, 1.0);
413 aLocalState
.DeviceColor
=
414 mpCanvas
->getUNOCanvas()->getDevice()->getDeviceColorSpace()->convertFromARGB(
417 mpCanvas
->getUNOCanvas()->drawBitmapModulated( mxBufferBitmap
,
418 mpCanvas
->getViewState(),
425 // TODO(P3): The whole float transparency handling is a mess,
426 // this should be refactored. What's more, the old idea of
427 // having only internal 'metaactions', and not the original
428 // GDIMetaFile now looks a lot less attractive. Try to move
429 // into the direction of having a direct GDIMetaFile2XCanvas
430 // renderer, and maybe a separate metafile XCanvas
432 bool TransparencyGroupAction::render( const ::basegfx::B2DHomMatrix
& rTransformation
) const
436 aSubset
.mnSubsetBegin
= 0;
437 aSubset
.mnSubsetEnd
= -1;
439 return renderSubset( rTransformation
, aSubset
);
442 ::basegfx::B2DRange
TransparencyGroupAction::getBounds( const ::basegfx::B2DHomMatrix
& rTransformation
) const
444 rendering::RenderState
aLocalState( maState
);
445 ::canvas::tools::prependToRenderState(aLocalState
, rTransformation
);
447 return tools::calcDevicePixelBounds(
448 ::basegfx::B2DRange( 0,0,
451 mpCanvas
->getViewState(),
455 ::basegfx::B2DRange
TransparencyGroupAction::getBounds( const ::basegfx::B2DHomMatrix
& rTransformation
,
456 const Subset
& rSubset
) const
458 // TODO(F3): Currently, the bounds for
459 // TransparencyGroupAction subsets equal those of the
460 // full set, although this action is able to render
463 // polygon only contains a single action, empty bounds
464 // if subset requests different range
465 if( rSubset
.mnSubsetBegin
!= 0 ||
466 rSubset
.mnSubsetEnd
!= 1 )
467 return ::basegfx::B2DRange();
469 return getBounds( rTransformation
);
472 sal_Int32
TransparencyGroupAction::getActionCount() const
474 return mpGroupMtf
.get() ? mpGroupMtf
->GetActionSize() : 0;
479 ActionSharedPtr
TransparencyGroupActionFactory::createTransparencyGroupAction( MtfAutoPtr
&& rGroupMtf
,
480 GradientAutoPtr
&& rAlphaGradient
,
481 const Renderer::Parameters
& rParms
,
482 const ::basegfx::B2DPoint
& rDstPoint
,
483 const ::basegfx::B2DVector
& rDstSize
,
484 const CanvasSharedPtr
& rCanvas
,
485 const OutDevState
& rState
)
487 return ActionSharedPtr( new TransparencyGroupAction(std::move(rGroupMtf
),
488 std::move(rAlphaGradient
),
499 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */