LibreOffice Module cppcanvas (master) 1
transparencygroupaction.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
20#include <sal/config.h>
21
22#include <utility>
23
24#include <tools/gen.hxx>
25#include <tools/debug.hxx>
26
28
29#include <com/sun/star/rendering/XBitmap.hpp>
30#include <com/sun/star/rendering/XCanvas.hpp>
31
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>
37
46#include <sal/log.hxx>
47
49#include <outdevstate.hxx>
50#include "mtftools.hxx"
52
53#if OSL_DEBUG_LEVEL > 2
54#include <vcl/canvastools.hxx>
55#endif
56
57using namespace ::com::sun::star;
58
59namespace cppcanvas::internal
60{
61 // free support functions
62 // ======================
63 namespace
64 {
65 class TransparencyGroupAction : public Action
66 {
67 public:
86 TransparencyGroupAction( std::unique_ptr< GDIMetaFile >&& rGroupMtf,
87 std::optional< Gradient >&& rAlphaGradient,
88 const ::basegfx::B2DPoint& rDstPoint,
89 const ::basegfx::B2DVector& rDstSize,
90 const CanvasSharedPtr& rCanvas,
91 const OutDevState& rState );
92
93 TransparencyGroupAction(const TransparencyGroupAction&) = delete;
94 const TransparencyGroupAction& operator=(const TransparencyGroupAction&) = delete;
95
96 virtual bool render( const ::basegfx::B2DHomMatrix& rTransformation ) const override;
97 virtual bool renderSubset( const ::basegfx::B2DHomMatrix& rTransformation,
98 const Subset& rSubset ) const override;
99
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;
103
104 virtual sal_Int32 getActionCount() const override;
105
106 private:
107 std::unique_ptr< GDIMetaFile > mpGroupMtf;
108 std::optional< Gradient > mpAlphaGradient;
109
110 const ::basegfx::B2DSize maDstSize;
111
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
115
116 // transformation for
117 // mxBufferBitmap content
119 rendering::RenderState maState;
120 };
121
122
127 void implSetupTransform( rendering::RenderState& rRenderState,
128 const ::basegfx::B2DPoint& rDstPoint )
129 {
130 ::basegfx::B2DHomMatrix aLocalTransformation;
131
132 aLocalTransformation.translate( rDstPoint.getX(),
133 rDstPoint.getY() );
134 ::canvas::tools::appendToRenderState( rRenderState,
135 aLocalTransformation );
136 }
137
138 TransparencyGroupAction::TransparencyGroupAction( std::unique_ptr< GDIMetaFile >&& rGroupMtf,
139 std::optional< Gradient >&& 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.getX(), rDstSize.getY()),
147 mpCanvas( rCanvas )
148 {
150 implSetupTransform( maState, rDstPoint );
151
152 // correct clip (which is relative to original transform)
154 rState,
155 rCanvas,
156 rDstPoint,
157 nullptr,
158 nullptr );
159
160 maLastSubset.mnSubsetBegin = 0;
161 maLastSubset.mnSubsetEnd = -1;
162 }
163
164 // TODO(P3): The whole float transparency handling is a mess,
165 // this should be refactored. What's more, the old idea of
166 // having only internal 'metaactions', and not the original
167 // GDIMetaFile now looks a lot less attractive. Try to move
168 // into the direction of having a direct GDIMetaFile2XCanvas
169 // renderer, and maybe a separate metafile XCanvas
170 // implementation.
171 bool TransparencyGroupAction::renderSubset( const ::basegfx::B2DHomMatrix& rTransformation,
172 const Subset& rSubset ) const
173 {
174 SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::TransparencyGroupAction::renderSubset()" );
175 SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::TransparencyGroupAction: 0x" << std::hex << this );
176
177 // determine overall transformation matrix (render, view,
178 // and passed transformation)
179 ::basegfx::B2DHomMatrix aTransform;
180 ::canvas::tools::getRenderStateTransform( aTransform, maState );
181 aTransform = rTransformation * aTransform;
182
183 ::basegfx::B2DHomMatrix aTotalTransform;
184 ::canvas::tools::getViewStateTransform( aTotalTransform, mpCanvas->getViewState() );
185 aTotalTransform = aTotalTransform * aTransform;
186
187 // since pure translational changes to the transformation
188 // does not matter, remove them before comparing
189 aTotalTransform.set( 0, 2, 0.0 );
190 aTotalTransform.set( 1, 2, 0.0 );
191
192 // determine total scaling factor of the
193 // transformation matrix - need to make the bitmap
194 // large enough
195 ::basegfx::B2DTuple aScale;
196 ::basegfx::B2DTuple aTranslate;
197 double nRotate;
198 double nShearX;
199 if( !aTotalTransform.decompose( aScale,
200 aTranslate,
201 nRotate,
202 nShearX ) )
203 {
204 SAL_WARN( "cppcanvas.emf", "TransparencyGroupAction::renderSubset(): non-decomposable transformation" );
205 return false;
206 }
207
208 // if there's no buffer bitmap, or as soon as the
209 // total transformation changes, we've got to
210 // re-render the bitmap
211 if( !mxBufferBitmap.is() ||
212 aTotalTransform != maLastTransformation ||
213 rSubset.mnSubsetBegin != maLastSubset.mnSubsetBegin ||
214 rSubset.mnSubsetEnd != maLastSubset.mnSubsetEnd )
215 {
217
218 // output size of metafile
219 ::Size aOutputSizePixel( ::basegfx::fround( aScale.getX() * maDstSize.getWidth() ),
220 ::basegfx::fround( aScale.getY() * maDstSize.getHeight() ) );
221
222 // pixel size of cache bitmap: round up to nearest int
223 ::Size aBitmapSizePixel( static_cast<sal_Int32>( aScale.getX() * maDstSize.getWidth() )+1,
224 static_cast<sal_Int32>( aScale.getY() * maDstSize.getHeight() )+1 );
225
226 ::Point aEmptyPoint;
227
228 // render our content into an appropriately sized
229 // VirtualDevice with alpha channel
231 *::Application::GetDefaultDevice(), DeviceFormat::WITH_ALPHA );
232 aVDev->SetOutputSizePixel( aBitmapSizePixel, true, true );
233 aVDev->SetMapMode();
234
235 if( rSubset.mnSubsetBegin != 0 ||
236 rSubset.mnSubsetEnd != -1 )
237 {
238 // true subset - extract referenced
239 // metaactions from mpGroupMtf
240 GDIMetaFile aMtf;
241 MetaAction* pCurrAct;
242 int nCurrActionIndex;
243
244 // extract subset actions
245 for( nCurrActionIndex=0,
246 pCurrAct=mpGroupMtf->FirstAction();
247 pCurrAct;
248 ++nCurrActionIndex, pCurrAct = mpGroupMtf->NextAction() )
249 {
250 switch( pCurrAct->GetType() )
251 {
252 case MetaActionType::PUSH:
253 case MetaActionType::POP:
254 case MetaActionType::CLIPREGION:
255 case MetaActionType::ISECTRECTCLIPREGION:
256 case MetaActionType::ISECTREGIONCLIPREGION:
257 case MetaActionType::MOVECLIPREGION:
258 case MetaActionType::LINECOLOR:
259 case MetaActionType::FILLCOLOR:
260 case MetaActionType::TEXTCOLOR:
261 case MetaActionType::TEXTFILLCOLOR:
262 case MetaActionType::TEXTLINECOLOR:
263 case MetaActionType::TEXTALIGN:
264 case MetaActionType::FONT:
265 case MetaActionType::RASTEROP:
266 case MetaActionType::REFPOINT:
267 case MetaActionType::LAYOUTMODE:
268 // state-changing action - copy as-is
269 aMtf.AddAction( pCurrAct->Clone() );
270 break;
271
272 case MetaActionType::GRADIENT:
273 case MetaActionType::HATCH:
274 case MetaActionType::EPS:
275 case MetaActionType::COMMENT:
276 case MetaActionType::POINT:
277 case MetaActionType::PIXEL:
278 case MetaActionType::LINE:
279 case MetaActionType::RECT:
280 case MetaActionType::ROUNDRECT:
281 case MetaActionType::ELLIPSE:
282 case MetaActionType::ARC:
283 case MetaActionType::PIE:
284 case MetaActionType::CHORD:
285 case MetaActionType::POLYLINE:
286 case MetaActionType::POLYGON:
287 case MetaActionType::POLYPOLYGON:
288 case MetaActionType::BMP:
289 case MetaActionType::BMPSCALE:
290 case MetaActionType::BMPSCALEPART:
291 case MetaActionType::BMPEX:
292 case MetaActionType::BMPEXSCALE:
293 case MetaActionType::BMPEXSCALEPART:
294 case MetaActionType::MASK:
295 case MetaActionType::MASKSCALE:
296 case MetaActionType::MASKSCALEPART:
297 case MetaActionType::GRADIENTEX:
298 case MetaActionType::WALLPAPER:
299 case MetaActionType::Transparent:
300 case MetaActionType::FLOATTRANSPARENT:
301 case MetaActionType::TEXT:
302 case MetaActionType::TEXTARRAY:
303 case MetaActionType::TEXTLINE:
304 case MetaActionType::TEXTRECT:
305 case MetaActionType::STRETCHTEXT:
306 // output-generating action - only
307 // copy, if we're within the
308 // requested subset
309 if( rSubset.mnSubsetBegin <= nCurrActionIndex &&
310 rSubset.mnSubsetEnd > nCurrActionIndex )
311 {
312 aMtf.AddAction( pCurrAct->Clone() );
313 }
314 break;
315
316 default:
317 SAL_WARN( "cppcanvas.emf", "Unknown meta action type encountered" );
318 break;
319 }
320 }
321
322 aVDev->DrawTransparent( aMtf,
323 aEmptyPoint,
324 aOutputSizePixel,
326 }
327 else
328 {
329 // no subsetting - render whole mtf
330 aVDev->DrawTransparent( *mpGroupMtf,
331 aEmptyPoint,
332 aOutputSizePixel,
334 }
335
336
337 // update buffered bitmap and transformation
338 BitmapSharedPtr aBmp( VCLFactory::createBitmap(
339 mpCanvas,
340 aVDev->GetBitmapEx(
341 aEmptyPoint,
342 aBitmapSizePixel ) ) );
343 mxBufferBitmap = aBmp->getUNOBitmap();
344 maLastTransformation = aTotalTransform;
345 maLastSubset = rSubset;
346 }
347
348 // determine target transformation (we can't simply pass
349 // aTotalTransform as assembled above, since we must take
350 // the canvas' view state as is, it might contain clipping
351 // (which, in turn, is relative to the view
352 // transformation))
353
354 // given that aTotalTransform is the identity
355 // transformation, we could simply render our bitmap
356 // as-is. Now, since the mxBufferBitmap content already
357 // accounts for scale changes in the overall
358 // transformation, we must factor this out
359 // before. Generally, the transformation matrix should be
360 // structured like this:
361 // Translation*Rotation*Shear*Scale. Thus, to neutralize
362 // the contained scaling, we've got to right-multiply with
363 // the inverse.
364 ::basegfx::B2DHomMatrix aScaleCorrection;
365 aScaleCorrection.scale( 1/aScale.getX(), 1/aScale.getY() );
366 aTransform = aTransform * aScaleCorrection;
367
368 rendering::RenderState aLocalState( maState );
369 ::canvas::tools::setRenderStateTransform(aLocalState, aTransform);
370
371 if(aLocalState.Clip.is())
372 {
373 // tdf#95709
374 // Adjust renderstate clip to modified scale from above
375 ::basegfx::B2DPolyPolygon aClip = ::basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D(aLocalState.Clip);
377 aLocalState.Clip = ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(mpCanvas->getUNOCanvas()->getDevice(), aClip);
378 }
379
380#if OSL_DEBUG_LEVEL > 2
381 aLocalState.Clip.clear();
382 aLocalState.DeviceColor =
384 ::Color( 0x80FF0000 ),
385 mpCanvas->getUNOCanvas()->getDevice()->getDeviceColorSpace() );
386
387 if( maState.Clip.is() )
388 mpCanvas->getUNOCanvas()->fillPolyPolygon( maState.Clip,
389 mpCanvas->getViewState(),
390 aLocalState );
391
392 aLocalState.DeviceColor = maState.DeviceColor;
393#endif
394
395 // no further alpha changes necessary -> draw directly
396 mpCanvas->getUNOCanvas()->drawBitmap( mxBufferBitmap,
397 mpCanvas->getViewState(),
398 aLocalState );
399 return true;
400 }
401
402 // TODO(P3): The whole float transparency handling is a mess,
403 // this should be refactored. What's more, the old idea of
404 // having only internal 'metaactions', and not the original
405 // GDIMetaFile now looks a lot less attractive. Try to move
406 // into the direction of having a direct GDIMetaFile2XCanvas
407 // renderer, and maybe a separate metafile XCanvas
408 // implementation.
409 bool TransparencyGroupAction::render( const ::basegfx::B2DHomMatrix& rTransformation ) const
410 {
411 Subset aSubset;
412
413 aSubset.mnSubsetBegin = 0;
414 aSubset.mnSubsetEnd = -1;
415
416 return renderSubset( rTransformation, aSubset );
417 }
418
419 ::basegfx::B2DRange TransparencyGroupAction::getBounds( const ::basegfx::B2DHomMatrix& rTransformation ) const
420 {
421 rendering::RenderState aLocalState( maState );
422 ::canvas::tools::prependToRenderState(aLocalState, rTransformation);
423
428 mpCanvas->getViewState(),
429 aLocalState );
430 }
431
432 ::basegfx::B2DRange TransparencyGroupAction::getBounds( const ::basegfx::B2DHomMatrix& rTransformation,
433 const Subset& rSubset ) const
434 {
435 // TODO(F3): Currently, the bounds for
436 // TransparencyGroupAction subsets equal those of the
437 // full set, although this action is able to render
438 // true subsets.
439
440 // polygon only contains a single action, empty bounds
441 // if subset requests different range
442 if( rSubset.mnSubsetBegin != 0 ||
443 rSubset.mnSubsetEnd != 1 )
444 return ::basegfx::B2DRange();
445
446 return getBounds( rTransformation );
447 }
448
449 sal_Int32 TransparencyGroupAction::getActionCount() const
450 {
451 return mpGroupMtf ? mpGroupMtf->GetActionSize() : 0;
452 }
453
454 }
455
456 std::shared_ptr<Action> TransparencyGroupActionFactory::createTransparencyGroupAction( std::unique_ptr< GDIMetaFile >&& rGroupMtf,
457 std::optional< Gradient >&& rAlphaGradient,
458 const ::basegfx::B2DPoint& rDstPoint,
459 const ::basegfx::B2DVector& rDstSize,
460 const CanvasSharedPtr& rCanvas,
461 const OutDevState& rState )
462 {
463 return std::make_shared<TransparencyGroupAction>(std::move(rGroupMtf),
464 std::move(rAlphaGradient),
465 rDstPoint,
466 rDstSize,
467 rCanvas,
468 rState );
469 }
470
471}
472
473/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
static OutputDevice * GetDefaultDevice()
void AddAction(const rtl::Reference< MetaAction > &pAction)
MetaActionType GetType() const
virtual rtl::Reference< MetaAction > Clone() const
bool decompose(B2DTuple &rScale, B2DTuple &rTranslate, double &rRotate, double &rShearX) const
void set(sal_uInt16 nRow, sal_uInt16 nColumn, double fValue)
void translate(double fX, double fY)
void scale(double fX, double fY)
void transform(const basegfx::B2DHomMatrix &rMatrix)
TYPE getWidth() const
TYPE getHeight() const
TYPE getX() const
TYPE getY() const
#define DBG_TESTSOLARMUTEX()
#define SAL_WARN(area, stream)
#define SAL_INFO(area, stream)
B2DHomMatrix createScaleB2DHomMatrix(double fScaleX, double fScaleY)
std::shared_ptr< Action > createTransparencyGroupAction(std::unique_ptr< GDIMetaFile > &&rGroupMtf, std::optional< Gradient > &&rAlphaGradient, const ::basegfx::B2DPoint &rDstPoint, const ::basegfx::B2DVector &rDstSize, const CanvasSharedPtr &rCanvas, const OutDevState &rState)
Create new transparency group action.
bool modifyClip(rendering::RenderState &o_rRenderState, const struct ::cppcanvas::internal::OutDevState &rOutdevState, const CanvasSharedPtr &rCanvas, const ::basegfx::B2DPoint &rOffset, const ::basegfx::B2DVector *pScaling, const double *pRotation)
Definition: mtftools.cxx:112
void initRenderState(rendering::RenderState &renderState, const ::cppcanvas::internal::OutDevState &outdevState)
Definition: mtftools.cxx:42
::basegfx::B2DRange calcDevicePixelBounds(const ::basegfx::B2DRange &rBounds, const rendering::ViewState &viewState, const rendering::RenderState &renderState)
Definition: mtftools.cxx:628
std::shared_ptr< Canvas > CanvasSharedPtr
std::shared_ptr< Gdiplus::Bitmap > BitmapSharedPtr
uno::Sequence< double > colorToDoubleSequence(const Color &rColor, const uno::Reference< rendering::XColorSpace > &xColorSpace)
rendering::RenderState maState
CanvasSharedPtr mpCanvas
mutable::basegfx::B2DHomMatrix maLastTransformation
const ::basegfx::B2DSize maDstSize
std::unique_ptr< GDIMetaFile > mpGroupMtf
Subset maLastSubset
uno::Reference< rendering::XBitmap > mxBufferBitmap
std::optional< Gradient > mpAlphaGradient