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