Version 5.2.6.1, tag libreoffice-5.2.6.1
[LibreOffice.git] / cppcanvas / source / mtfrenderer / transparencygroupaction.cxx
blob474d22b7dfaeb7a61a968cf9af9cf69de7e38273
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>
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;
59 namespace cppcanvas
61 namespace internal
63 // free support functions
64 // ======================
65 namespace
67 class TransparencyGroupAction : public Action
69 public:
70 /** Create new transparency group action.
72 @param rGroupMtf
73 Metafile that groups all actions to be rendered
74 transparent.
76 @param rAlphaGradient
77 VCL gradient, to be rendered into the action's alpha
78 channel.
80 @param rDstPoint
81 Left, top edge of destination, in current state
82 coordinate system
84 @param rDstSize
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;
108 private:
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(),
136 rDstPoint.getY() );
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 ),
150 mxBufferBitmap(),
151 maLastTransformation(),
152 mpCanvas( rCanvas ),
153 maState(),
154 mnAlpha( 1.0 )
156 tools::initRenderState(maState,rState);
157 implSetupTransform( maState, rDstPoint );
159 // correct clip (which is relative to original transform)
160 tools::modifyClip( maState,
161 rState,
162 rCanvas,
163 rDstPoint,
164 nullptr,
165 nullptr );
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
177 // implementation.
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
201 // large enough
202 ::basegfx::B2DTuple aScale;
203 ::basegfx::B2DTuple aTranslate;
204 double nRotate;
205 double nShearX;
206 if( !aTotalTransform.decompose( aScale,
207 aTranslate,
208 nRotate,
209 nShearX ) )
211 SAL_WARN( "cppcanvas.emf", "TransparencyGroupAction::renderSubset(): non-decomposable transformation" );
212 return false;
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 );
233 ::Point aEmptyPoint;
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 );
240 aVDev->SetMapMode();
242 if( rSubset.mnSubsetBegin != 0 ||
243 rSubset.mnSubsetEnd != -1 )
245 // true subset - extract referenced
246 // metaactions from mpGroupMtf
247 GDIMetaFile aMtf;
248 MetaAction* pCurrAct;
249 int nCurrActionIndex;
251 // extract subset actions
252 for( nCurrActionIndex=0,
253 pCurrAct=mpGroupMtf->FirstAction();
254 pCurrAct;
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() );
277 break;
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
315 // requested subset
316 if( rSubset.mnSubsetBegin <= nCurrActionIndex &&
317 rSubset.mnSubsetEnd > nCurrActionIndex )
319 aMtf.AddAction( pCurrAct->Clone() );
321 break;
323 default:
324 SAL_WARN( "cppcanvas.emf", "Unknown meta action type encountered" );
325 break;
329 aVDev->DrawTransparent( aMtf,
330 aEmptyPoint,
331 aOutputSizePixel,
332 *mpAlphaGradient );
334 else
336 // no subsetting - render whole mtf
337 aVDev->DrawTransparent( *mpGroupMtf,
338 aEmptyPoint,
339 aOutputSizePixel,
340 *mpAlphaGradient );
344 // update buffered bitmap and transformation
345 BitmapSharedPtr aBmp( VCLFactory::createBitmap(
346 mpCanvas,
347 aVDev->GetBitmapEx(
348 aEmptyPoint,
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
359 // transformation))
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
370 // the inverse.
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())
380 // tdf#95709
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(),
397 aLocalState );
399 aLocalState.DeviceColor = maState.DeviceColor;
400 #endif
402 if( ::rtl::math::approxEqual(mnAlpha, 1.0) )
404 // no further alpha changes necessary -> draw directly
405 mpCanvas->getUNOCanvas()->drawBitmap( mxBufferBitmap,
406 mpCanvas->getViewState(),
407 aLocalState );
409 else
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(
416 aCols);
418 mpCanvas->getUNOCanvas()->drawBitmapModulated( mxBufferBitmap,
419 mpCanvas->getViewState(),
420 aLocalState );
423 return true;
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
432 // implementation.
433 bool TransparencyGroupAction::render( const ::basegfx::B2DHomMatrix& rTransformation ) const
435 Subset aSubset;
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,
450 maDstSize.getX(),
451 maDstSize.getY() ),
452 mpCanvas->getViewState(),
453 aLocalState );
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
462 // true subsets.
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),
489 rDstPoint,
490 rDstSize,
491 rCanvas,
492 rState ) );
498 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */