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>
25 #include <tools/debug.hxx>
27 #include <canvas/canvastools.hxx>
29 #include <com/sun/star/rendering/XBitmap.hpp>
30 #include <com/sun/star/rendering/XCanvas.hpp>
32 #include <vcl/metaact.hxx>
33 #include <vcl/bitmapex.hxx>
34 #include <vcl/svapp.hxx>
35 #include <vcl/virdev.hxx>
36 #include <vcl/gdimtf.hxx>
38 #include <basegfx/range/b2drange.hxx>
39 #include <basegfx/point/b2dpoint.hxx>
40 #include <basegfx/vector/b2dsize.hxx>
41 #include <basegfx/numeric/ftools.hxx>
42 #include <basegfx/matrix/b2dhommatrix.hxx>
43 #include <basegfx/tuple/b2dtuple.hxx>
44 #include <basegfx/utils/canvastools.hxx>
45 #include <basegfx/matrix/b2dhommatrixtools.hxx>
46 #include <sal/log.hxx>
48 #include "transparencygroupaction.hxx"
49 #include <outdevstate.hxx>
50 #include "mtftools.hxx"
51 #include <cppcanvas/vclfactory.hxx>
53 #if OSL_DEBUG_LEVEL > 2
54 #include <vcl/canvastools.hxx>
57 using namespace ::com::sun::star
;
59 namespace cppcanvas::internal
61 // free support functions
62 // ======================
65 class TransparencyGroupAction
: public Action
68 /** Create new transparency group action.
71 Metafile that groups all actions to be rendered
75 VCL gradient, to be rendered into the action's alpha
79 Left, top edge of destination, in current state
83 Size of the transparency group object, in current
84 state coordinate system.
86 TransparencyGroupAction( MtfAutoPtr
&& rGroupMtf
,
87 GradientAutoPtr
&& rAlphaGradient
,
88 const ::basegfx::B2DPoint
& rDstPoint
,
89 const ::basegfx::B2DVector
& rDstSize
,
90 const CanvasSharedPtr
& rCanvas
,
91 const OutDevState
& rState
);
93 TransparencyGroupAction(const TransparencyGroupAction
&) = delete;
94 const TransparencyGroupAction
& operator=(const TransparencyGroupAction
&) = delete;
96 virtual bool render( const ::basegfx::B2DHomMatrix
& rTransformation
) const override
;
97 virtual bool renderSubset( const ::basegfx::B2DHomMatrix
& rTransformation
,
98 const Subset
& rSubset
) const override
;
100 virtual ::basegfx::B2DRange
getBounds( const ::basegfx::B2DHomMatrix
& rTransformation
) const override
;
101 virtual ::basegfx::B2DRange
getBounds( const ::basegfx::B2DHomMatrix
& rTransformation
,
102 const Subset
& rSubset
) const override
;
104 virtual sal_Int32
getActionCount() const override
;
107 MtfAutoPtr mpGroupMtf
;
108 GradientAutoPtr mpAlphaGradient
;
110 const ::basegfx::B2DSize maDstSize
;
112 mutable uno::Reference
< rendering::XBitmap
> mxBufferBitmap
; // contains last rendered version
113 mutable ::basegfx::B2DHomMatrix maLastTransformation
; // contains last active transformation
114 mutable Subset maLastSubset
; // contains last effective subset
116 // transformation for
117 // mxBufferBitmap content
118 CanvasSharedPtr mpCanvas
;
119 rendering::RenderState maState
;
123 /** Setup transformation such that the next render call is
124 moved rPoint away, and scaled according to the ratio
125 given by src and dst size.
127 void implSetupTransform( rendering::RenderState
& rRenderState
,
128 const ::basegfx::B2DPoint
& rDstPoint
)
130 ::basegfx::B2DHomMatrix aLocalTransformation
;
132 aLocalTransformation
.translate( rDstPoint
.getX(),
134 ::canvas::tools::appendToRenderState( rRenderState
,
135 aLocalTransformation
);
138 TransparencyGroupAction::TransparencyGroupAction( MtfAutoPtr
&& rGroupMtf
,
139 GradientAutoPtr
&& rAlphaGradient
,
140 const ::basegfx::B2DPoint
& rDstPoint
,
141 const ::basegfx::B2DVector
& rDstSize
,
142 const CanvasSharedPtr
& rCanvas
,
143 const OutDevState
& rState
) :
144 mpGroupMtf( std::move(rGroupMtf
) ),
145 mpAlphaGradient( std::move(rAlphaGradient
) ),
146 maDstSize( rDstSize
),
148 maLastTransformation(),
152 tools::initRenderState(maState
,rState
);
153 implSetupTransform( maState
, rDstPoint
);
155 // correct clip (which is relative to original transform)
156 tools::modifyClip( maState
,
163 maLastSubset
.mnSubsetBegin
= 0;
164 maLastSubset
.mnSubsetEnd
= -1;
167 // TODO(P3): The whole float transparency handling is a mess,
168 // this should be refactored. What's more, the old idea of
169 // having only internal 'metaactions', and not the original
170 // GDIMetaFile now looks a lot less attractive. Try to move
171 // into the direction of having a direct GDIMetaFile2XCanvas
172 // renderer, and maybe a separate metafile XCanvas
174 bool TransparencyGroupAction::renderSubset( const ::basegfx::B2DHomMatrix
& rTransformation
,
175 const Subset
& rSubset
) const
177 SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::TransparencyGroupAction::renderSubset()" );
178 SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::TransparencyGroupAction: 0x" << std::hex
<< this );
180 // determine overall transformation matrix (render, view,
181 // and passed transformation)
182 ::basegfx::B2DHomMatrix aTransform
;
183 ::canvas::tools::getRenderStateTransform( aTransform
, maState
);
184 aTransform
= rTransformation
* aTransform
;
186 ::basegfx::B2DHomMatrix aTotalTransform
;
187 ::canvas::tools::getViewStateTransform( aTotalTransform
, mpCanvas
->getViewState() );
188 aTotalTransform
= aTotalTransform
* aTransform
;
190 // since pure translational changes to the transformation
191 // does not matter, remove them before comparing
192 aTotalTransform
.set( 0, 2, 0.0 );
193 aTotalTransform
.set( 1, 2, 0.0 );
195 // determine total scaling factor of the
196 // transformation matrix - need to make the bitmap
198 ::basegfx::B2DTuple aScale
;
199 ::basegfx::B2DTuple aTranslate
;
202 if( !aTotalTransform
.decompose( aScale
,
207 SAL_WARN( "cppcanvas.emf", "TransparencyGroupAction::renderSubset(): non-decomposable transformation" );
211 // if there's no buffer bitmap, or as soon as the
212 // total transformation changes, we've got to
213 // re-render the bitmap
214 if( !mxBufferBitmap
.is() ||
215 aTotalTransform
!= maLastTransformation
||
216 rSubset
.mnSubsetBegin
!= maLastSubset
.mnSubsetBegin
||
217 rSubset
.mnSubsetEnd
!= maLastSubset
.mnSubsetEnd
)
219 DBG_TESTSOLARMUTEX();
221 // output size of metafile
222 ::Size
aOutputSizePixel( ::basegfx::fround( aScale
.getX() * maDstSize
.getX() ),
223 ::basegfx::fround( aScale
.getY() * maDstSize
.getY() ) );
225 // pixel size of cache bitmap: round up to nearest int
226 ::Size
aBitmapSizePixel( static_cast<sal_Int32
>( aScale
.getX() * maDstSize
.getX() )+1,
227 static_cast<sal_Int32
>( aScale
.getY() * maDstSize
.getY() )+1 );
231 // render our content into an appropriately sized
232 // VirtualDevice with alpha channel
233 ScopedVclPtrInstance
<VirtualDevice
> aVDev(
234 *::Application::GetDefaultDevice(), DeviceFormat::DEFAULT
, DeviceFormat::DEFAULT
);
235 aVDev
->SetOutputSizePixel( aBitmapSizePixel
);
238 if( rSubset
.mnSubsetBegin
!= 0 ||
239 rSubset
.mnSubsetEnd
!= -1 )
241 // true subset - extract referenced
242 // metaactions from mpGroupMtf
244 MetaAction
* pCurrAct
;
245 int nCurrActionIndex
;
247 // extract subset actions
248 for( nCurrActionIndex
=0,
249 pCurrAct
=mpGroupMtf
->FirstAction();
251 ++nCurrActionIndex
, pCurrAct
= mpGroupMtf
->NextAction() )
253 switch( pCurrAct
->GetType() )
255 case MetaActionType::PUSH
:
256 case MetaActionType::POP
:
257 case MetaActionType::CLIPREGION
:
258 case MetaActionType::ISECTRECTCLIPREGION
:
259 case MetaActionType::ISECTREGIONCLIPREGION
:
260 case MetaActionType::MOVECLIPREGION
:
261 case MetaActionType::LINECOLOR
:
262 case MetaActionType::FILLCOLOR
:
263 case MetaActionType::TEXTCOLOR
:
264 case MetaActionType::TEXTFILLCOLOR
:
265 case MetaActionType::TEXTLINECOLOR
:
266 case MetaActionType::TEXTALIGN
:
267 case MetaActionType::FONT
:
268 case MetaActionType::RASTEROP
:
269 case MetaActionType::REFPOINT
:
270 case MetaActionType::LAYOUTMODE
:
271 // state-changing action - copy as-is
272 aMtf
.AddAction( pCurrAct
->Clone() );
275 case MetaActionType::GRADIENT
:
276 case MetaActionType::HATCH
:
277 case MetaActionType::EPS
:
278 case MetaActionType::COMMENT
:
279 case MetaActionType::POINT
:
280 case MetaActionType::PIXEL
:
281 case MetaActionType::LINE
:
282 case MetaActionType::RECT
:
283 case MetaActionType::ROUNDRECT
:
284 case MetaActionType::ELLIPSE
:
285 case MetaActionType::ARC
:
286 case MetaActionType::PIE
:
287 case MetaActionType::CHORD
:
288 case MetaActionType::POLYLINE
:
289 case MetaActionType::POLYGON
:
290 case MetaActionType::POLYPOLYGON
:
291 case MetaActionType::BMP
:
292 case MetaActionType::BMPSCALE
:
293 case MetaActionType::BMPSCALEPART
:
294 case MetaActionType::BMPEX
:
295 case MetaActionType::BMPEXSCALE
:
296 case MetaActionType::BMPEXSCALEPART
:
297 case MetaActionType::MASK
:
298 case MetaActionType::MASKSCALE
:
299 case MetaActionType::MASKSCALEPART
:
300 case MetaActionType::GRADIENTEX
:
301 case MetaActionType::WALLPAPER
:
302 case MetaActionType::Transparent
:
303 case MetaActionType::FLOATTRANSPARENT
:
304 case MetaActionType::TEXT
:
305 case MetaActionType::TEXTARRAY
:
306 case MetaActionType::TEXTLINE
:
307 case MetaActionType::TEXTRECT
:
308 case MetaActionType::STRETCHTEXT
:
309 // output-generating action - only
310 // copy, if we're within the
312 if( rSubset
.mnSubsetBegin
<= nCurrActionIndex
&&
313 rSubset
.mnSubsetEnd
> nCurrActionIndex
)
315 aMtf
.AddAction( pCurrAct
->Clone() );
320 SAL_WARN( "cppcanvas.emf", "Unknown meta action type encountered" );
325 aVDev
->DrawTransparent( aMtf
,
332 // no subsetting - render whole mtf
333 aVDev
->DrawTransparent( *mpGroupMtf
,
340 // update buffered bitmap and transformation
341 BitmapSharedPtr
aBmp( VCLFactory::createBitmap(
345 aBitmapSizePixel
) ) );
346 mxBufferBitmap
= aBmp
->getUNOBitmap();
347 maLastTransformation
= aTotalTransform
;
348 maLastSubset
= rSubset
;
351 // determine target transformation (we can't simply pass
352 // aTotalTransform as assembled above, since we must take
353 // the canvas' view state as is, it might contain clipping
354 // (which, in turn, is relative to the view
357 // given that aTotalTransform is the identity
358 // transformation, we could simply render our bitmap
359 // as-is. Now, since the mxBufferBitmap content already
360 // accounts for scale changes in the overall
361 // transformation, we must factor this out
362 // before. Generally, the transformation matrix should be
363 // structured like this:
364 // Translation*Rotation*Shear*Scale. Thus, to neutralize
365 // the contained scaling, we've got to right-multiply with
367 ::basegfx::B2DHomMatrix aScaleCorrection
;
368 aScaleCorrection
.scale( 1/aScale
.getX(), 1/aScale
.getY() );
369 aTransform
= aTransform
* aScaleCorrection
;
371 rendering::RenderState
aLocalState( maState
);
372 ::canvas::tools::setRenderStateTransform(aLocalState
, aTransform
);
374 if(aLocalState
.Clip
.is())
377 // Adjust renderstate clip to modified scale from above
378 ::basegfx::B2DPolyPolygon aClip
= ::basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D(aLocalState
.Clip
);
379 aClip
.transform(basegfx::utils::createScaleB2DHomMatrix(aScale
));
380 aLocalState
.Clip
= ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(mpCanvas
->getUNOCanvas()->getDevice(), aClip
);
383 #if OSL_DEBUG_LEVEL > 2
384 aLocalState
.Clip
.clear();
385 aLocalState
.DeviceColor
=
386 vcl::unotools::colorToDoubleSequence(
387 ::Color( 0x80FF0000 ),
388 mpCanvas
->getUNOCanvas()->getDevice()->getDeviceColorSpace() );
390 if( maState
.Clip
.is() )
391 mpCanvas
->getUNOCanvas()->fillPolyPolygon( maState
.Clip
,
392 mpCanvas
->getViewState(),
395 aLocalState
.DeviceColor
= maState
.DeviceColor
;
398 // no further alpha changes necessary -> draw directly
399 mpCanvas
->getUNOCanvas()->drawBitmap( mxBufferBitmap
,
400 mpCanvas
->getViewState(),
405 // TODO(P3): The whole float transparency handling is a mess,
406 // this should be refactored. What's more, the old idea of
407 // having only internal 'metaactions', and not the original
408 // GDIMetaFile now looks a lot less attractive. Try to move
409 // into the direction of having a direct GDIMetaFile2XCanvas
410 // renderer, and maybe a separate metafile XCanvas
412 bool TransparencyGroupAction::render( const ::basegfx::B2DHomMatrix
& rTransformation
) const
416 aSubset
.mnSubsetBegin
= 0;
417 aSubset
.mnSubsetEnd
= -1;
419 return renderSubset( rTransformation
, aSubset
);
422 ::basegfx::B2DRange
TransparencyGroupAction::getBounds( const ::basegfx::B2DHomMatrix
& rTransformation
) const
424 rendering::RenderState
aLocalState( maState
);
425 ::canvas::tools::prependToRenderState(aLocalState
, rTransformation
);
427 return tools::calcDevicePixelBounds(
428 ::basegfx::B2DRange( 0,0,
431 mpCanvas
->getViewState(),
435 ::basegfx::B2DRange
TransparencyGroupAction::getBounds( const ::basegfx::B2DHomMatrix
& rTransformation
,
436 const Subset
& rSubset
) const
438 // TODO(F3): Currently, the bounds for
439 // TransparencyGroupAction subsets equal those of the
440 // full set, although this action is able to render
443 // polygon only contains a single action, empty bounds
444 // if subset requests different range
445 if( rSubset
.mnSubsetBegin
!= 0 ||
446 rSubset
.mnSubsetEnd
!= 1 )
447 return ::basegfx::B2DRange();
449 return getBounds( rTransformation
);
452 sal_Int32
TransparencyGroupAction::getActionCount() const
454 return mpGroupMtf
? mpGroupMtf
->GetActionSize() : 0;
459 std::shared_ptr
<Action
> TransparencyGroupActionFactory::createTransparencyGroupAction( MtfAutoPtr
&& rGroupMtf
,
460 GradientAutoPtr
&& rAlphaGradient
,
461 const ::basegfx::B2DPoint
& rDstPoint
,
462 const ::basegfx::B2DVector
& rDstSize
,
463 const CanvasSharedPtr
& rCanvas
,
464 const OutDevState
& rState
)
466 return std::make_shared
<TransparencyGroupAction
>(std::move(rGroupMtf
),
467 std::move(rAlphaGradient
),
476 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */