Bump version to 21.06.18.1
[LibreOffice.git] / cppcanvas / source / mtfrenderer / transparencygroupaction.cxx
blob55cc47a97732a7fbf7792f37fbb2d316aa7c8712
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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>
22 #include <utility>
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>
55 #endif
57 using namespace ::com::sun::star;
59 namespace cppcanvas::internal
61 // free support functions
62 // ======================
63 namespace
65 class TransparencyGroupAction : public Action
67 public:
68 /** Create new transparency group action.
70 @param rGroupMtf
71 Metafile that groups all actions to be rendered
72 transparent.
74 @param rAlphaGradient
75 VCL gradient, to be rendered into the action's alpha
76 channel.
78 @param rDstPoint
79 Left, top edge of destination, in current state
80 coordinate system
82 @param rDstSize
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;
106 private:
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(),
133 rDstPoint.getY() );
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 ),
147 mxBufferBitmap(),
148 maLastTransformation(),
149 mpCanvas( rCanvas ),
150 maState()
152 tools::initRenderState(maState,rState);
153 implSetupTransform( maState, rDstPoint );
155 // correct clip (which is relative to original transform)
156 tools::modifyClip( maState,
157 rState,
158 rCanvas,
159 rDstPoint,
160 nullptr,
161 nullptr );
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
173 // implementation.
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
197 // large enough
198 ::basegfx::B2DTuple aScale;
199 ::basegfx::B2DTuple aTranslate;
200 double nRotate;
201 double nShearX;
202 if( !aTotalTransform.decompose( aScale,
203 aTranslate,
204 nRotate,
205 nShearX ) )
207 SAL_WARN( "cppcanvas.emf", "TransparencyGroupAction::renderSubset(): non-decomposable transformation" );
208 return false;
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 );
229 ::Point aEmptyPoint;
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 );
236 aVDev->SetMapMode();
238 if( rSubset.mnSubsetBegin != 0 ||
239 rSubset.mnSubsetEnd != -1 )
241 // true subset - extract referenced
242 // metaactions from mpGroupMtf
243 GDIMetaFile aMtf;
244 MetaAction* pCurrAct;
245 int nCurrActionIndex;
247 // extract subset actions
248 for( nCurrActionIndex=0,
249 pCurrAct=mpGroupMtf->FirstAction();
250 pCurrAct;
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() );
273 break;
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
311 // requested subset
312 if( rSubset.mnSubsetBegin <= nCurrActionIndex &&
313 rSubset.mnSubsetEnd > nCurrActionIndex )
315 aMtf.AddAction( pCurrAct->Clone() );
317 break;
319 default:
320 SAL_WARN( "cppcanvas.emf", "Unknown meta action type encountered" );
321 break;
325 aVDev->DrawTransparent( aMtf,
326 aEmptyPoint,
327 aOutputSizePixel,
328 *mpAlphaGradient );
330 else
332 // no subsetting - render whole mtf
333 aVDev->DrawTransparent( *mpGroupMtf,
334 aEmptyPoint,
335 aOutputSizePixel,
336 *mpAlphaGradient );
340 // update buffered bitmap and transformation
341 BitmapSharedPtr aBmp( VCLFactory::createBitmap(
342 mpCanvas,
343 aVDev->GetBitmapEx(
344 aEmptyPoint,
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
355 // transformation))
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
366 // the inverse.
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())
376 // tdf#95709
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(),
393 aLocalState );
395 aLocalState.DeviceColor = maState.DeviceColor;
396 #endif
398 // no further alpha changes necessary -> draw directly
399 mpCanvas->getUNOCanvas()->drawBitmap( mxBufferBitmap,
400 mpCanvas->getViewState(),
401 aLocalState );
402 return true;
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
411 // implementation.
412 bool TransparencyGroupAction::render( const ::basegfx::B2DHomMatrix& rTransformation ) const
414 Subset aSubset;
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,
429 maDstSize.getX(),
430 maDstSize.getY() ),
431 mpCanvas->getViewState(),
432 aLocalState );
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
441 // true subsets.
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),
468 rDstPoint,
469 rDstSize,
470 rCanvas,
471 rState );
476 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */