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/canvastools.hxx>
28 #include <com/sun/star/rendering/XBitmap.hpp>
29 #include <com/sun/star/rendering/XCanvas.hpp>
31 #include <rtl/math.hxx>
33 #include <vcl/metaact.hxx>
34 #include <vcl/bitmapex.hxx>
35 #include <vcl/canvastools.hxx>
36 #include <vcl/svapp.hxx>
37 #include <vcl/outdev.hxx>
38 #include <vcl/virdev.hxx>
39 #include <vcl/gdimtf.hxx>
40 #include <vcl/gradient.hxx>
42 #include <basegfx/range/b2drange.hxx>
43 #include <basegfx/point/b2dpoint.hxx>
44 #include <basegfx/vector/b2dsize.hxx>
45 #include <basegfx/numeric/ftools.hxx>
46 #include <basegfx/matrix/b2dhommatrix.hxx>
47 #include <basegfx/tuple/b2dtuple.hxx>
48 #include <basegfx/tools/canvastools.hxx>
49 #include <basegfx/matrix/b2dhommatrixtools.hxx>
51 #include "transparencygroupaction.hxx"
52 #include "outdevstate.hxx"
53 #include "mtftools.hxx"
54 #include "cppcanvas/vclfactory.hxx"
57 using namespace ::com::sun::star
;
63 // free support functions
64 // ======================
67 class TransparencyGroupAction
: public Action
70 /** Create new transparency group action.
73 Metafile that groups all actions to be rendered
77 VCL gradient, to be rendered into the action's alpha
81 Left, top edge of destination, in current state
85 Size of the transparency group object, in current
86 state coordinate system.
88 TransparencyGroupAction( MtfAutoPtr
&& rGroupMtf
,
89 GradientAutoPtr
&& rAlphaGradient
,
90 const ::basegfx::B2DPoint
& rDstPoint
,
91 const ::basegfx::B2DVector
& rDstSize
,
92 const CanvasSharedPtr
& rCanvas
,
93 const OutDevState
& rState
);
95 TransparencyGroupAction(const TransparencyGroupAction
&) = delete;
96 const TransparencyGroupAction
& operator=(const TransparencyGroupAction
&) = delete;
98 virtual bool render( const ::basegfx::B2DHomMatrix
& rTransformation
) const override
;
99 virtual bool renderSubset( const ::basegfx::B2DHomMatrix
& rTransformation
,
100 const Subset
& rSubset
) const override
;
102 virtual ::basegfx::B2DRange
getBounds( const ::basegfx::B2DHomMatrix
& rTransformation
) const override
;
103 virtual ::basegfx::B2DRange
getBounds( const ::basegfx::B2DHomMatrix
& rTransformation
,
104 const Subset
& rSubset
) const override
;
106 virtual sal_Int32
getActionCount() const override
;
109 MtfAutoPtr mpGroupMtf
;
110 GradientAutoPtr mpAlphaGradient
;
112 const ::basegfx::B2DSize maDstSize
;
114 mutable uno::Reference
< rendering::XBitmap
> mxBufferBitmap
; // contains last rendered version
115 mutable ::basegfx::B2DHomMatrix maLastTransformation
; // contains last active transformation
116 mutable Subset maLastSubset
; // contains last effective subset
118 // transformation for
119 // mxBufferBitmap content
120 CanvasSharedPtr mpCanvas
;
121 rendering::RenderState maState
;
122 const double mnAlpha
;
126 /** Setup transformation such that the next render call is
127 moved rPoint away, and scaled according to the ratio
128 given by src and dst size.
130 void implSetupTransform( rendering::RenderState
& rRenderState
,
131 const ::basegfx::B2DPoint
& rDstPoint
)
133 ::basegfx::B2DHomMatrix aLocalTransformation
;
135 aLocalTransformation
.translate( rDstPoint
.getX(),
137 ::canvas::tools::appendToRenderState( rRenderState
,
138 aLocalTransformation
);
141 TransparencyGroupAction::TransparencyGroupAction( MtfAutoPtr
&& rGroupMtf
,
142 GradientAutoPtr
&& rAlphaGradient
,
143 const ::basegfx::B2DPoint
& rDstPoint
,
144 const ::basegfx::B2DVector
& rDstSize
,
145 const CanvasSharedPtr
& rCanvas
,
146 const OutDevState
& rState
) :
147 mpGroupMtf( std::move(rGroupMtf
) ),
148 mpAlphaGradient( std::move(rAlphaGradient
) ),
149 maDstSize( rDstSize
),
151 maLastTransformation(),
156 tools::initRenderState(maState
,rState
);
157 implSetupTransform( maState
, rDstPoint
);
159 // correct clip (which is relative to original transform)
160 tools::modifyClip( maState
,
167 maLastSubset
.mnSubsetBegin
= 0;
168 maLastSubset
.mnSubsetEnd
= -1;
171 // TODO(P3): The whole float transparency handling is a mess,
172 // this should be refactored. What's more, the old idea of
173 // having only internal 'metaactions', and not the original
174 // GDIMetaFile now looks a lot less attractive. Try to move
175 // into the direction of having a direct GDIMetaFile2XCanvas
176 // renderer, and maybe a separate metafile XCanvas
178 bool TransparencyGroupAction::renderSubset( const ::basegfx::B2DHomMatrix
& rTransformation
,
179 const Subset
& rSubset
) const
181 SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::TransparencyGroupAction::renderSubset()" );
182 SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::TransparencyGroupAction: 0x" << std::hex
<< this );
184 // determine overall transformation matrix (render, view,
185 // and passed transformation)
186 ::basegfx::B2DHomMatrix aTransform
;
187 ::canvas::tools::getRenderStateTransform( aTransform
, maState
);
188 aTransform
= rTransformation
* aTransform
;
190 ::basegfx::B2DHomMatrix aTotalTransform
;
191 ::canvas::tools::getViewStateTransform( aTotalTransform
, mpCanvas
->getViewState() );
192 aTotalTransform
= aTotalTransform
* aTransform
;
194 // since pure translational changes to the transformation
195 // does not matter, remove them before comparing
196 aTotalTransform
.set( 0, 2, 0.0 );
197 aTotalTransform
.set( 1, 2, 0.0 );
199 // determine total scaling factor of the
200 // transformation matrix - need to make the bitmap
202 ::basegfx::B2DTuple aScale
;
203 ::basegfx::B2DTuple aTranslate
;
206 if( !aTotalTransform
.decompose( aScale
,
211 SAL_WARN( "cppcanvas.emf", "TransparencyGroupAction::renderSubset(): non-decomposable transformation" );
215 // if there's no buffer bitmap, or as soon as the
216 // total transformation changes, we've got to
217 // re-render the bitmap
218 if( !mxBufferBitmap
.is() ||
219 aTotalTransform
!= maLastTransformation
||
220 rSubset
.mnSubsetBegin
!= maLastSubset
.mnSubsetBegin
||
221 rSubset
.mnSubsetEnd
!= maLastSubset
.mnSubsetEnd
)
223 DBG_TESTSOLARMUTEX();
225 // output size of metafile
226 ::Size
aOutputSizePixel( ::basegfx::fround( aScale
.getX() * maDstSize
.getX() ),
227 ::basegfx::fround( aScale
.getY() * maDstSize
.getY() ) );
229 // pixel size of cache bitmap: round up to nearest int
230 ::Size
aBitmapSizePixel( static_cast<sal_Int32
>( aScale
.getX() * maDstSize
.getX() )+1,
231 static_cast<sal_Int32
>( aScale
.getY() * maDstSize
.getY() )+1 );
235 // render our content into an appropriately sized
236 // VirtualDevice with alpha channel
237 ScopedVclPtrInstance
<VirtualDevice
> aVDev(
238 *::Application::GetDefaultDevice(), DeviceFormat::DEFAULT
, DeviceFormat::DEFAULT
);
239 aVDev
->SetOutputSizePixel( aBitmapSizePixel
);
242 if( rSubset
.mnSubsetBegin
!= 0 ||
243 rSubset
.mnSubsetEnd
!= -1 )
245 // true subset - extract referenced
246 // metaactions from mpGroupMtf
248 MetaAction
* pCurrAct
;
249 int nCurrActionIndex
;
251 // extract subset actions
252 for( nCurrActionIndex
=0,
253 pCurrAct
=mpGroupMtf
->FirstAction();
255 ++nCurrActionIndex
, pCurrAct
= mpGroupMtf
->NextAction() )
257 switch( pCurrAct
->GetType() )
259 case MetaActionType::PUSH
:
260 case MetaActionType::POP
:
261 case MetaActionType::CLIPREGION
:
262 case MetaActionType::ISECTRECTCLIPREGION
:
263 case MetaActionType::ISECTREGIONCLIPREGION
:
264 case MetaActionType::MOVECLIPREGION
:
265 case MetaActionType::LINECOLOR
:
266 case MetaActionType::FILLCOLOR
:
267 case MetaActionType::TEXTCOLOR
:
268 case MetaActionType::TEXTFILLCOLOR
:
269 case MetaActionType::TEXTLINECOLOR
:
270 case MetaActionType::TEXTALIGN
:
271 case MetaActionType::FONT
:
272 case MetaActionType::RASTEROP
:
273 case MetaActionType::REFPOINT
:
274 case MetaActionType::LAYOUTMODE
:
275 // state-changing action - copy as-is
276 aMtf
.AddAction( pCurrAct
->Clone() );
279 case MetaActionType::GRADIENT
:
280 case MetaActionType::HATCH
:
281 case MetaActionType::EPS
:
282 case MetaActionType::COMMENT
:
283 case MetaActionType::POINT
:
284 case MetaActionType::PIXEL
:
285 case MetaActionType::LINE
:
286 case MetaActionType::RECT
:
287 case MetaActionType::ROUNDRECT
:
288 case MetaActionType::ELLIPSE
:
289 case MetaActionType::ARC
:
290 case MetaActionType::PIE
:
291 case MetaActionType::CHORD
:
292 case MetaActionType::POLYLINE
:
293 case MetaActionType::POLYGON
:
294 case MetaActionType::POLYPOLYGON
:
295 case MetaActionType::BMP
:
296 case MetaActionType::BMPSCALE
:
297 case MetaActionType::BMPSCALEPART
:
298 case MetaActionType::BMPEX
:
299 case MetaActionType::BMPEXSCALE
:
300 case MetaActionType::BMPEXSCALEPART
:
301 case MetaActionType::MASK
:
302 case MetaActionType::MASKSCALE
:
303 case MetaActionType::MASKSCALEPART
:
304 case MetaActionType::GRADIENTEX
:
305 case MetaActionType::WALLPAPER
:
306 case MetaActionType::Transparent
:
307 case MetaActionType::FLOATTRANSPARENT
:
308 case MetaActionType::TEXT
:
309 case MetaActionType::TEXTARRAY
:
310 case MetaActionType::TEXTLINE
:
311 case MetaActionType::TEXTRECT
:
312 case MetaActionType::STRETCHTEXT
:
313 // output-generating action - only
314 // copy, if we're within the
316 if( rSubset
.mnSubsetBegin
<= nCurrActionIndex
&&
317 rSubset
.mnSubsetEnd
> nCurrActionIndex
)
319 aMtf
.AddAction( pCurrAct
->Clone() );
324 SAL_WARN( "cppcanvas.emf", "Unknown meta action type encountered" );
329 aVDev
->DrawTransparent( aMtf
,
336 // no subsetting - render whole mtf
337 aVDev
->DrawTransparent( *mpGroupMtf
,
344 // update buffered bitmap and transformation
345 BitmapSharedPtr
aBmp( VCLFactory::createBitmap(
349 aBitmapSizePixel
) ) );
350 mxBufferBitmap
= aBmp
->getUNOBitmap();
351 maLastTransformation
= aTotalTransform
;
352 maLastSubset
= rSubset
;
355 // determine target transformation (we can't simply pass
356 // aTotalTransform as assembled above, since we must take
357 // the canvas' view state as is, it might contain clipping
358 // (which, in turn, is relative to the view
361 // given that aTotalTransform is the identity
362 // transformation, we could simply render our bitmap
363 // as-is. Now, since the mxBufferBitmap content already
364 // accounts for scale changes in the overall
365 // transformation, we must factor this out
366 // before. Generally, the transformation matrix should be
367 // structured like this:
368 // Translation*Rotation*Shear*Scale. Thus, to neutralize
369 // the contained scaling, we've got to right-multiply with
371 ::basegfx::B2DHomMatrix aScaleCorrection
;
372 aScaleCorrection
.scale( 1/aScale
.getX(), 1/aScale
.getY() );
373 aTransform
= aTransform
* aScaleCorrection
;
375 rendering::RenderState
aLocalState( maState
);
376 ::canvas::tools::setRenderStateTransform(aLocalState
, aTransform
);
378 if(aLocalState
.Clip
.is())
381 // Adjust renderstate clip to modified scale from above
382 ::basegfx::B2DPolyPolygon aClip
= ::basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D(aLocalState
.Clip
);
383 aClip
.transform(basegfx::tools::createScaleB2DHomMatrix(aScale
));
384 aLocalState
.Clip
= ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(mpCanvas
->getUNOCanvas()->getDevice(), aClip
);
387 #if OSL_DEBUG_LEVEL > 2
388 aLocalState
.Clip
.clear();
389 aLocalState
.DeviceColor
=
390 vcl::unotools::colorToDoubleSequence(
391 ::Color( 0x80FF0000 ),
392 mpCanvas
->getUNOCanvas()->getDevice()->getDeviceColorSpace() );
394 if( maState
.Clip
.is() )
395 mpCanvas
->getUNOCanvas()->fillPolyPolygon( maState
.Clip
,
396 mpCanvas
->getViewState(),
399 aLocalState
.DeviceColor
= maState
.DeviceColor
;
402 if( ::rtl::math::approxEqual(mnAlpha
, 1.0) )
404 // no further alpha changes necessary -> draw directly
405 mpCanvas
->getUNOCanvas()->drawBitmap( mxBufferBitmap
,
406 mpCanvas
->getViewState(),
411 // add alpha modulation value to DeviceColor
412 uno::Sequence
<rendering::ARGBColor
> aCols(1);
413 aCols
[0] = rendering::ARGBColor( mnAlpha
, 1.0, 1.0, 1.0);
414 aLocalState
.DeviceColor
=
415 mpCanvas
->getUNOCanvas()->getDevice()->getDeviceColorSpace()->convertFromARGB(
418 mpCanvas
->getUNOCanvas()->drawBitmapModulated( mxBufferBitmap
,
419 mpCanvas
->getViewState(),
426 // TODO(P3): The whole float transparency handling is a mess,
427 // this should be refactored. What's more, the old idea of
428 // having only internal 'metaactions', and not the original
429 // GDIMetaFile now looks a lot less attractive. Try to move
430 // into the direction of having a direct GDIMetaFile2XCanvas
431 // renderer, and maybe a separate metafile XCanvas
433 bool TransparencyGroupAction::render( const ::basegfx::B2DHomMatrix
& rTransformation
) const
437 aSubset
.mnSubsetBegin
= 0;
438 aSubset
.mnSubsetEnd
= -1;
440 return renderSubset( rTransformation
, aSubset
);
443 ::basegfx::B2DRange
TransparencyGroupAction::getBounds( const ::basegfx::B2DHomMatrix
& rTransformation
) const
445 rendering::RenderState
aLocalState( maState
);
446 ::canvas::tools::prependToRenderState(aLocalState
, rTransformation
);
448 return tools::calcDevicePixelBounds(
449 ::basegfx::B2DRange( 0,0,
452 mpCanvas
->getViewState(),
456 ::basegfx::B2DRange
TransparencyGroupAction::getBounds( const ::basegfx::B2DHomMatrix
& rTransformation
,
457 const Subset
& rSubset
) const
459 // TODO(F3): Currently, the bounds for
460 // TransparencyGroupAction subsets equal those of the
461 // full set, although this action is able to render
464 // polygon only contains a single action, empty bounds
465 // if subset requests different range
466 if( rSubset
.mnSubsetBegin
!= 0 ||
467 rSubset
.mnSubsetEnd
!= 1 )
468 return ::basegfx::B2DRange();
470 return getBounds( rTransformation
);
473 sal_Int32
TransparencyGroupAction::getActionCount() const
475 return mpGroupMtf
.get() ? mpGroupMtf
->GetActionSize() : 0;
480 ActionSharedPtr
TransparencyGroupActionFactory::createTransparencyGroupAction( MtfAutoPtr
&& rGroupMtf
,
481 GradientAutoPtr
&& rAlphaGradient
,
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
),
498 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */