Line data Source code
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 :
26 : #include <canvas/debug.hxx>
27 : #include <canvas/verbosetrace.hxx>
28 : #include <canvas/canvastools.hxx>
29 :
30 : #include <com/sun/star/rendering/XBitmap.hpp>
31 : #include <com/sun/star/rendering/XCanvas.hpp>
32 :
33 : #include <rtl/math.hxx>
34 :
35 : #include <vcl/metaact.hxx>
36 : #include <vcl/bitmapex.hxx>
37 : #include <vcl/canvastools.hxx>
38 : #include <vcl/svapp.hxx>
39 : #include <vcl/outdev.hxx>
40 : #include <vcl/virdev.hxx>
41 : #include <vcl/gdimtf.hxx>
42 : #include <vcl/gradient.hxx>
43 :
44 : #include <basegfx/range/b2drange.hxx>
45 : #include <basegfx/point/b2dpoint.hxx>
46 : #include <basegfx/vector/b2dsize.hxx>
47 : #include <basegfx/numeric/ftools.hxx>
48 : #include <basegfx/matrix/b2dhommatrix.hxx>
49 : #include <basegfx/tuple/b2dtuple.hxx>
50 : #include <basegfx/tools/canvastools.hxx>
51 :
52 : #include <boost/utility.hpp>
53 :
54 : #include "transparencygroupaction.hxx"
55 : #include "outdevstate.hxx"
56 : #include "mtftools.hxx"
57 : #include "cppcanvas/vclfactory.hxx"
58 :
59 :
60 : using namespace ::com::sun::star;
61 :
62 : namespace cppcanvas
63 : {
64 : namespace internal
65 : {
66 : // free support functions
67 : // ======================
68 : namespace
69 : {
70 0 : class TransparencyGroupAction : public Action, private ::boost::noncopyable
71 : {
72 : public:
73 : /** Create new transparency group action.
74 :
75 : @param rGroupMtf
76 : Metafile that groups all actions to be rendered
77 : transparent.
78 :
79 : @param rAlphaGradient
80 : VCL gradient, to be rendered into the action's alpha
81 : channel.
82 :
83 : @param rParms
84 : Render parameters
85 :
86 : @param rDstPoint
87 : Left, top edge of destination, in current state
88 : coordinate system
89 :
90 : @param rDstSize
91 : Size of the transparency group object, in current
92 : state coordinate system.
93 : */
94 : TransparencyGroupAction( MtfAutoPtr&& rGroupMtf,
95 : GradientAutoPtr&& rAlphaGradient,
96 : const Renderer::Parameters& rParms,
97 : const ::basegfx::B2DPoint& rDstPoint,
98 : const ::basegfx::B2DVector& rDstSize,
99 : const CanvasSharedPtr& rCanvas,
100 : const OutDevState& rState );
101 :
102 : virtual bool render( const ::basegfx::B2DHomMatrix& rTransformation ) const SAL_OVERRIDE;
103 : virtual bool renderSubset( const ::basegfx::B2DHomMatrix& rTransformation,
104 : const Subset& rSubset ) const SAL_OVERRIDE;
105 :
106 : virtual ::basegfx::B2DRange getBounds( const ::basegfx::B2DHomMatrix& rTransformation ) const SAL_OVERRIDE;
107 : virtual ::basegfx::B2DRange getBounds( const ::basegfx::B2DHomMatrix& rTransformation,
108 : const Subset& rSubset ) const SAL_OVERRIDE;
109 :
110 : virtual sal_Int32 getActionCount() const SAL_OVERRIDE;
111 :
112 : private:
113 : MtfAutoPtr mpGroupMtf;
114 : GradientAutoPtr mpAlphaGradient;
115 :
116 : const Renderer::Parameters maParms;
117 :
118 : const ::basegfx::B2DSize maDstSize;
119 :
120 : mutable uno::Reference< rendering::XBitmap > mxBufferBitmap; // contains last rendered version
121 : mutable ::basegfx::B2DHomMatrix maLastTransformation; // contains last active transformation
122 : mutable Subset maLastSubset; // contains last effective subset
123 :
124 : // transformation for
125 : // mxBufferBitmap content
126 : CanvasSharedPtr mpCanvas;
127 : rendering::RenderState maState;
128 : const double mnAlpha;
129 : };
130 :
131 :
132 : /** Setup transformation such that the next render call is
133 : moved rPoint away, and scaled according to the ratio
134 : given by src and dst size.
135 : */
136 0 : void implSetupTransform( rendering::RenderState& rRenderState,
137 : const ::basegfx::B2DPoint& rDstPoint )
138 : {
139 0 : ::basegfx::B2DHomMatrix aLocalTransformation;
140 :
141 : aLocalTransformation.translate( rDstPoint.getX(),
142 0 : rDstPoint.getY() );
143 : ::canvas::tools::appendToRenderState( rRenderState,
144 0 : aLocalTransformation );
145 0 : }
146 :
147 0 : TransparencyGroupAction::TransparencyGroupAction( MtfAutoPtr&& rGroupMtf,
148 : GradientAutoPtr&& rAlphaGradient,
149 : const Renderer::Parameters& rParms,
150 : const ::basegfx::B2DPoint& rDstPoint,
151 : const ::basegfx::B2DVector& rDstSize,
152 : const CanvasSharedPtr& rCanvas,
153 : const OutDevState& rState ) :
154 0 : mpGroupMtf( std::move(rGroupMtf) ),
155 0 : mpAlphaGradient( std::move(rAlphaGradient) ),
156 : maParms( rParms ),
157 : maDstSize( rDstSize ),
158 : mxBufferBitmap(),
159 : maLastTransformation(),
160 : mpCanvas( rCanvas ),
161 : maState(),
162 0 : mnAlpha( 1.0 )
163 : {
164 0 : tools::initRenderState(maState,rState);
165 0 : implSetupTransform( maState, rDstPoint );
166 :
167 : // correct clip (which is relative to original transform)
168 : tools::modifyClip( maState,
169 : rState,
170 : rCanvas,
171 : rDstPoint,
172 : NULL,
173 0 : NULL );
174 :
175 0 : maLastSubset.mnSubsetBegin = 0;
176 0 : maLastSubset.mnSubsetEnd = -1;
177 0 : }
178 :
179 : // TODO(P3): The whole float transparency handling is a mess,
180 : // this should be refactored. What's more, the old idea of
181 : // having only internal 'metaactions', and not the original
182 : // GDIMetaFile now looks a lot less attractive. Try to move
183 : // into the direction of having a direct GDIMetaFile2XCanvas
184 : // renderer, and maybe a separate metafile XCanvas
185 : // implementation.
186 0 : bool TransparencyGroupAction::renderSubset( const ::basegfx::B2DHomMatrix& rTransformation,
187 : const Subset& rSubset ) const
188 : {
189 : SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::TransparencyGroupAction::renderSubset()" );
190 : SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::TransparencyGroupAction: 0x" << std::hex << this );
191 :
192 : // determine overall transformation matrix (render, view,
193 : // and passed transformation)
194 0 : ::basegfx::B2DHomMatrix aTransform;
195 0 : ::canvas::tools::getRenderStateTransform( aTransform, maState );
196 0 : aTransform = rTransformation * aTransform;
197 :
198 0 : ::basegfx::B2DHomMatrix aTotalTransform;
199 0 : ::canvas::tools::getViewStateTransform( aTotalTransform, mpCanvas->getViewState() );
200 0 : aTotalTransform = aTotalTransform * aTransform;
201 :
202 : // since pure translational changes to the transformation
203 : // does not matter, remove them before comparing
204 0 : aTotalTransform.set( 0, 2, 0.0 );
205 0 : aTotalTransform.set( 1, 2, 0.0 );
206 :
207 : // determine total scaling factor of the
208 : // transformation matrix - need to make the bitmap
209 : // large enough
210 0 : ::basegfx::B2DTuple aScale;
211 0 : ::basegfx::B2DTuple aTranslate;
212 : double nRotate;
213 : double nShearX;
214 0 : if( !aTotalTransform.decompose( aScale,
215 : aTranslate,
216 : nRotate,
217 0 : nShearX ) )
218 : {
219 : SAL_WARN( "cppcanvas.emf", "TransparencyGroupAction::renderSubset(): non-decomposable transformation" );
220 0 : return false;
221 : }
222 :
223 : // if there's no buffer bitmap, or as soon as the
224 : // total transformation changes, we've got to
225 : // re-render the bitmap
226 0 : if( !mxBufferBitmap.is() ||
227 0 : aTotalTransform != maLastTransformation ||
228 0 : rSubset.mnSubsetBegin != maLastSubset.mnSubsetBegin ||
229 0 : rSubset.mnSubsetEnd != maLastSubset.mnSubsetEnd )
230 : {
231 : DBG_TESTSOLARMUTEX();
232 :
233 : // output size of metafile
234 0 : ::Size aOutputSizePixel( ::basegfx::fround( aScale.getX() * maDstSize.getX() ),
235 0 : ::basegfx::fround( aScale.getY() * maDstSize.getY() ) );
236 :
237 : // pixel size of cache bitmap: round up to nearest int
238 0 : ::Size aBitmapSizePixel( static_cast<sal_Int32>( aScale.getX() * maDstSize.getX() )+1,
239 0 : static_cast<sal_Int32>( aScale.getY() * maDstSize.getY() )+1 );
240 :
241 0 : ::Point aEmptyPoint;
242 :
243 : // render our content into an appropriately sized
244 : // VirtualDevice with alpha channel
245 : ScopedVclPtrInstance<VirtualDevice> aVDev(
246 0 : *::Application::GetDefaultDevice(), 0, 0 );
247 0 : aVDev->SetOutputSizePixel( aBitmapSizePixel );
248 0 : aVDev->SetMapMode();
249 :
250 0 : if( rSubset.mnSubsetBegin != 0 ||
251 0 : rSubset.mnSubsetEnd != -1 )
252 : {
253 : // true subset - extract referenced
254 : // metaactions from mpGroupMtf
255 0 : GDIMetaFile aMtf;
256 : MetaAction* pCurrAct;
257 : int nCurrActionIndex;
258 :
259 : // extract subset actions
260 0 : for( nCurrActionIndex=0,
261 0 : pCurrAct=mpGroupMtf->FirstAction();
262 : pCurrAct;
263 0 : ++nCurrActionIndex, pCurrAct = mpGroupMtf->NextAction() )
264 : {
265 0 : switch( pCurrAct->GetType() )
266 : {
267 : case MetaActionType::PUSH:
268 : case MetaActionType::POP:
269 : case MetaActionType::CLIPREGION:
270 : case MetaActionType::ISECTRECTCLIPREGION:
271 : case MetaActionType::ISECTREGIONCLIPREGION:
272 : case MetaActionType::MOVECLIPREGION:
273 : case MetaActionType::LINECOLOR:
274 : case MetaActionType::FILLCOLOR:
275 : case MetaActionType::TEXTCOLOR:
276 : case MetaActionType::TEXTFILLCOLOR:
277 : case MetaActionType::TEXTLINECOLOR:
278 : case MetaActionType::TEXTALIGN:
279 : case MetaActionType::FONT:
280 : case MetaActionType::RASTEROP:
281 : case MetaActionType::REFPOINT:
282 : case MetaActionType::LAYOUTMODE:
283 : // state-changing action - copy as-is
284 0 : aMtf.AddAction( pCurrAct->Clone() );
285 0 : break;
286 :
287 : case MetaActionType::GRADIENT:
288 : case MetaActionType::HATCH:
289 : case MetaActionType::EPS:
290 : case MetaActionType::COMMENT:
291 : case MetaActionType::POINT:
292 : case MetaActionType::PIXEL:
293 : case MetaActionType::LINE:
294 : case MetaActionType::RECT:
295 : case MetaActionType::ROUNDRECT:
296 : case MetaActionType::ELLIPSE:
297 : case MetaActionType::ARC:
298 : case MetaActionType::PIE:
299 : case MetaActionType::CHORD:
300 : case MetaActionType::POLYLINE:
301 : case MetaActionType::POLYGON:
302 : case MetaActionType::POLYPOLYGON:
303 : case MetaActionType::BMP:
304 : case MetaActionType::BMPSCALE:
305 : case MetaActionType::BMPSCALEPART:
306 : case MetaActionType::BMPEX:
307 : case MetaActionType::BMPEXSCALE:
308 : case MetaActionType::BMPEXSCALEPART:
309 : case MetaActionType::MASK:
310 : case MetaActionType::MASKSCALE:
311 : case MetaActionType::MASKSCALEPART:
312 : case MetaActionType::GRADIENTEX:
313 : case MetaActionType::WALLPAPER:
314 : case MetaActionType::Transparent:
315 : case MetaActionType::FLOATTRANSPARENT:
316 : case MetaActionType::TEXT:
317 : case MetaActionType::TEXTARRAY:
318 : case MetaActionType::TEXTLINE:
319 : case MetaActionType::TEXTRECT:
320 : case MetaActionType::STRETCHTEXT:
321 : // output-generating action - only
322 : // copy, if we're within the
323 : // requested subset
324 0 : if( rSubset.mnSubsetBegin <= nCurrActionIndex &&
325 0 : rSubset.mnSubsetEnd > nCurrActionIndex )
326 : {
327 0 : aMtf.AddAction( pCurrAct->Clone() );
328 : }
329 0 : break;
330 :
331 : default:
332 : SAL_WARN( "cppcanvas.emf", "Unknown meta action type encountered" );
333 0 : break;
334 : }
335 : }
336 :
337 0 : aVDev->DrawTransparent( aMtf,
338 : aEmptyPoint,
339 : aOutputSizePixel,
340 0 : *mpAlphaGradient );
341 : }
342 : else
343 : {
344 : // no subsetting - render whole mtf
345 0 : aVDev->DrawTransparent( *mpGroupMtf,
346 : aEmptyPoint,
347 : aOutputSizePixel,
348 0 : *mpAlphaGradient );
349 : }
350 :
351 :
352 : // update buffered bitmap and transformation
353 : BitmapSharedPtr aBmp( VCLFactory::createBitmap(
354 : mpCanvas,
355 0 : aVDev->GetBitmapEx(
356 : aEmptyPoint,
357 0 : aBitmapSizePixel ) ) );
358 0 : mxBufferBitmap = aBmp->getUNOBitmap();
359 0 : maLastTransformation = aTotalTransform;
360 0 : maLastSubset = rSubset;
361 : }
362 :
363 : // determine target transformation (we can't simply pass
364 : // aTotalTransform as assembled above, since we must take
365 : // the canvas' view state as is, it might contain clipping
366 : // (which, in turn, is relative to the view
367 : // transformation))
368 :
369 : // given that aTotalTransform is the identity
370 : // transformation, we could simply render our bitmap
371 : // as-is. Now, since the mxBufferBitmap content already
372 : // accounts for scale changes in the overall
373 : // transformation, we must factor this out
374 : // before. Generally, the transformation matrix should be
375 : // structured like this:
376 : // Translation*Rotation*Shear*Scale. Thus, to neutralize
377 : // the contained scaling, we've got to right-multiply with
378 : // the inverse.
379 0 : ::basegfx::B2DHomMatrix aScaleCorrection;
380 0 : aScaleCorrection.scale( 1/aScale.getX(), 1/aScale.getY() );
381 0 : aTransform = aTransform * aScaleCorrection;
382 :
383 0 : rendering::RenderState aLocalState( maState );
384 0 : ::canvas::tools::setRenderStateTransform(aLocalState, aTransform);
385 :
386 : #if OSL_DEBUG_LEVEL > 2
387 : aLocalState.Clip.clear();
388 : aLocalState.DeviceColor =
389 : vcl::unotools::colorToDoubleSequence(
390 : ::Color( 0x80FF0000 ),
391 : mpCanvas->getUNOCanvas()->getDevice()->getDeviceColorSpace() );
392 :
393 : if( maState.Clip.is() )
394 : mpCanvas->getUNOCanvas()->fillPolyPolygon( maState.Clip,
395 : mpCanvas->getViewState(),
396 : aLocalState );
397 :
398 : aLocalState.DeviceColor = maState.DeviceColor;
399 : #endif
400 :
401 0 : if( ::rtl::math::approxEqual(mnAlpha, 1.0) )
402 : {
403 : // no further alpha changes necessary -> draw directly
404 0 : mpCanvas->getUNOCanvas()->drawBitmap( mxBufferBitmap,
405 0 : mpCanvas->getViewState(),
406 0 : aLocalState );
407 : }
408 : else
409 : {
410 : // add alpha modulation value to DeviceColor
411 0 : uno::Sequence<rendering::ARGBColor> aCols(1);
412 0 : aCols[0] = rendering::ARGBColor( mnAlpha, 1.0, 1.0, 1.0);
413 0 : aLocalState.DeviceColor =
414 0 : mpCanvas->getUNOCanvas()->getDevice()->getDeviceColorSpace()->convertFromARGB(
415 0 : aCols);
416 :
417 0 : mpCanvas->getUNOCanvas()->drawBitmapModulated( mxBufferBitmap,
418 0 : mpCanvas->getViewState(),
419 0 : aLocalState );
420 : }
421 :
422 0 : return true;
423 : }
424 :
425 : // TODO(P3): The whole float transparency handling is a mess,
426 : // this should be refactored. What's more, the old idea of
427 : // having only internal 'metaactions', and not the original
428 : // GDIMetaFile now looks a lot less attractive. Try to move
429 : // into the direction of having a direct GDIMetaFile2XCanvas
430 : // renderer, and maybe a separate metafile XCanvas
431 : // implementation.
432 0 : bool TransparencyGroupAction::render( const ::basegfx::B2DHomMatrix& rTransformation ) const
433 : {
434 : Subset aSubset;
435 :
436 0 : aSubset.mnSubsetBegin = 0;
437 0 : aSubset.mnSubsetEnd = -1;
438 :
439 0 : return renderSubset( rTransformation, aSubset );
440 : }
441 :
442 0 : ::basegfx::B2DRange TransparencyGroupAction::getBounds( const ::basegfx::B2DHomMatrix& rTransformation ) const
443 : {
444 0 : rendering::RenderState aLocalState( maState );
445 0 : ::canvas::tools::prependToRenderState(aLocalState, rTransformation);
446 :
447 : return tools::calcDevicePixelBounds(
448 : ::basegfx::B2DRange( 0,0,
449 : maDstSize.getX(),
450 : maDstSize.getY() ),
451 0 : mpCanvas->getViewState(),
452 0 : aLocalState );
453 : }
454 :
455 0 : ::basegfx::B2DRange TransparencyGroupAction::getBounds( const ::basegfx::B2DHomMatrix& rTransformation,
456 : const Subset& rSubset ) const
457 : {
458 : // TODO(F3): Currently, the bounds for
459 : // TransparencyGroupAction subsets equal those of the
460 : // full set, although this action is able to render
461 : // true subsets.
462 :
463 : // polygon only contains a single action, empty bounds
464 : // if subset requests different range
465 0 : if( rSubset.mnSubsetBegin != 0 ||
466 0 : rSubset.mnSubsetEnd != 1 )
467 0 : return ::basegfx::B2DRange();
468 :
469 0 : return getBounds( rTransformation );
470 : }
471 :
472 0 : sal_Int32 TransparencyGroupAction::getActionCount() const
473 : {
474 0 : return mpGroupMtf.get() ? mpGroupMtf->GetActionSize() : 0;
475 : }
476 :
477 : }
478 :
479 0 : ActionSharedPtr TransparencyGroupActionFactory::createTransparencyGroupAction( MtfAutoPtr&& rGroupMtf,
480 : GradientAutoPtr&& rAlphaGradient,
481 : const Renderer::Parameters& rParms,
482 : const ::basegfx::B2DPoint& rDstPoint,
483 : const ::basegfx::B2DVector& rDstSize,
484 : const CanvasSharedPtr& rCanvas,
485 : const OutDevState& rState )
486 : {
487 : return ActionSharedPtr( new TransparencyGroupAction(std::move(rGroupMtf),
488 : std::move(rAlphaGradient),
489 : rParms,
490 : rDstPoint,
491 : rDstSize,
492 : rCanvas,
493 0 : rState ) );
494 : }
495 :
496 : }
497 : }
498 :
499 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|