diff options
Diffstat (limited to 'cppcanvas/source/mtfrenderer/transparencygroupaction.cxx')
-rw-r--r-- | cppcanvas/source/mtfrenderer/transparencygroupaction.cxx | 592 |
1 files changed, 592 insertions, 0 deletions
diff --git a/cppcanvas/source/mtfrenderer/transparencygroupaction.cxx b/cppcanvas/source/mtfrenderer/transparencygroupaction.cxx new file mode 100644 index 000000000000..a8984d41e585 --- /dev/null +++ b/cppcanvas/source/mtfrenderer/transparencygroupaction.cxx @@ -0,0 +1,592 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_cppcanvas.hxx" + +#include <tools/gen.hxx> + +#include <canvas/debug.hxx> +#include <canvas/verbosetrace.hxx> +#include <canvas/canvastools.hxx> + +#include <rtl/logfile.hxx> + +#include <com/sun/star/rendering/XBitmap.hpp> +#include <com/sun/star/rendering/XCanvas.hpp> + +#include <rtl/math.hxx> + +#include <vcl/metaact.hxx> +#include <vcl/bitmapex.hxx> +#include <vcl/canvastools.hxx> +#include <vcl/svapp.hxx> +#include <vcl/outdev.hxx> +#include <vcl/virdev.hxx> +#include <vcl/virdev.hxx> +#include <vcl/gdimtf.hxx> +#include <vcl/gradient.hxx> + +#include <canvas/canvastools.hxx> + +#include <basegfx/range/b2drange.hxx> +#include <basegfx/point/b2dpoint.hxx> +#include <basegfx/vector/b2dsize.hxx> +#include <basegfx/numeric/ftools.hxx> +#include <basegfx/matrix/b2dhommatrix.hxx> +#include <basegfx/tuple/b2dtuple.hxx> +#include <basegfx/tools/canvastools.hxx> + +#include <boost/utility.hpp> + +#include "transparencygroupaction.hxx" +#include "outdevstate.hxx" +#include "mtftools.hxx" +#include "cppcanvas/vclfactory.hxx" + + +using namespace ::com::sun::star; + +namespace cppcanvas +{ + namespace internal + { + // free support functions + // ====================== + namespace + { + class TransparencyGroupAction : public Action, private ::boost::noncopyable + { + public: + /** Create new transparency group action. + + @param rGroupMtf + Metafile that groups all actions to be rendered + transparent + + @param rParms + Render parameters + + @param rDstPoint + Left, top edge of destination, in current state + coordinate system + + @param rDstSize + Size of the transparency group object, in current + state coordinate system. + + @param nAlpha + Alpha value, must be in the range [0,1] + */ + TransparencyGroupAction( MtfAutoPtr& rGroupMtf, + const Renderer::Parameters& rParms, + const ::basegfx::B2DPoint& rDstPoint, + const ::basegfx::B2DVector& rDstSize, + double nAlpha, + const CanvasSharedPtr& rCanvas, + const OutDevState& rState ); + + /** Create new transparency group action. + + @param rGroupMtf + Metafile that groups all actions to be rendered + transparent. + + @param rAlphaGradient + VCL gradient, to be rendered into the action's alpha + channel. + + @param rParms + Render parameters + + @param rDstPoint + Left, top edge of destination, in current state + coordinate system + + @param rDstSize + Size of the transparency group object, in current + state coordinate system. + */ + TransparencyGroupAction( MtfAutoPtr& rGroupMtf, + GradientAutoPtr& rAlphaGradient, + const Renderer::Parameters& rParms, + const ::basegfx::B2DPoint& rDstPoint, + const ::basegfx::B2DVector& rDstSize, + const CanvasSharedPtr& rCanvas, + const OutDevState& rState ); + + virtual bool render( const ::basegfx::B2DHomMatrix& rTransformation ) const; + virtual bool render( const ::basegfx::B2DHomMatrix& rTransformation, + const Subset& rSubset ) const; + + virtual ::basegfx::B2DRange getBounds( const ::basegfx::B2DHomMatrix& rTransformation ) const; + virtual ::basegfx::B2DRange getBounds( const ::basegfx::B2DHomMatrix& rTransformation, + const Subset& rSubset ) const; + + virtual sal_Int32 getActionCount() const; + + private: + MtfAutoPtr mpGroupMtf; + GradientAutoPtr mpAlphaGradient; + + const Renderer::Parameters maParms; + + const ::basegfx::B2DSize maDstSize; + + mutable uno::Reference< rendering::XBitmap > mxBufferBitmap; // contains last rendered version + mutable ::basegfx::B2DHomMatrix maLastTransformation; // contains last active transformation + mutable Subset maLastSubset; // contains last effective subset + + // transformation for + // mxBufferBitmap content + CanvasSharedPtr mpCanvas; + rendering::RenderState maState; + const double mnAlpha; + }; + + + /** Setup transformation such that the next render call is + moved rPoint away, and scaled according to the ratio + given by src and dst size. + */ + void implSetupTransform( rendering::RenderState& rRenderState, + const ::basegfx::B2DPoint& rDstPoint ) + { + ::basegfx::B2DHomMatrix aLocalTransformation; + + aLocalTransformation.translate( rDstPoint.getX(), + rDstPoint.getY() ); + ::canvas::tools::appendToRenderState( rRenderState, + aLocalTransformation ); + } + + TransparencyGroupAction::TransparencyGroupAction( MtfAutoPtr& rGroupMtf, + const Renderer::Parameters& rParms, + const ::basegfx::B2DPoint& rDstPoint, + const ::basegfx::B2DVector& rDstSize, + double nAlpha, + const CanvasSharedPtr& rCanvas, + const OutDevState& rState ) : + mpGroupMtf( rGroupMtf ), + mpAlphaGradient(), + maParms( rParms ), + maDstSize( rDstSize ), + mxBufferBitmap(), + maLastTransformation(), + mpCanvas( rCanvas ), + maState(), + mnAlpha( nAlpha ) + { + tools::initRenderState(maState,rState); + implSetupTransform( maState, rDstPoint ); + + // correct clip (which is relative to original transform) + tools::modifyClip( maState, + rState, + rCanvas, + rDstPoint, + NULL, + NULL ); + + maLastSubset.mnSubsetBegin = 0; + maLastSubset.mnSubsetEnd = -1; + } + + TransparencyGroupAction::TransparencyGroupAction( MtfAutoPtr& rGroupMtf, + GradientAutoPtr& rAlphaGradient, + const Renderer::Parameters& rParms, + const ::basegfx::B2DPoint& rDstPoint, + const ::basegfx::B2DVector& rDstSize, + const CanvasSharedPtr& rCanvas, + const OutDevState& rState ) : + mpGroupMtf( rGroupMtf ), + mpAlphaGradient( rAlphaGradient ), + maParms( rParms ), + maDstSize( rDstSize ), + mxBufferBitmap(), + maLastTransformation(), + mpCanvas( rCanvas ), + maState(), + mnAlpha( 1.0 ) + { + tools::initRenderState(maState,rState); + implSetupTransform( maState, rDstPoint ); + + // correct clip (which is relative to original transform) + tools::modifyClip( maState, + rState, + rCanvas, + rDstPoint, + NULL, + NULL ); + + maLastSubset.mnSubsetBegin = 0; + maLastSubset.mnSubsetEnd = -1; + } + + // TODO(P3): The whole float transparency handling is a mess, + // this should be refactored. What's more, the old idea of + // having only internal 'metaactions', and not the original + // GDIMetaFile now looks a lot less attractive. Try to move + // into the direction of having a direct GDIMetaFile2XCanvas + // renderer, and maybe a separate metafile XCanvas + // implementation. + bool TransparencyGroupAction::render( const ::basegfx::B2DHomMatrix& rTransformation, + const Subset& rSubset ) const + { + RTL_LOGFILE_CONTEXT( aLog, "::cppcanvas::internal::TransparencyGroupAction::render()" ); + RTL_LOGFILE_CONTEXT_TRACE1( aLog, "::cppcanvas::internal::TransparencyGroupAction: 0x%X", this ); + + // determine overall transformation matrix (render, view, + // and passed transformation) + ::basegfx::B2DHomMatrix aTransform; + ::canvas::tools::getRenderStateTransform( aTransform, maState ); + aTransform = rTransformation * aTransform; + + ::basegfx::B2DHomMatrix aTotalTransform; + ::canvas::tools::getViewStateTransform( aTotalTransform, mpCanvas->getViewState() ); + aTotalTransform = aTotalTransform * aTransform; + + // since pure translational changes to the transformation + // does not matter, remove them before comparing + aTotalTransform.set( 0, 2, 0.0 ); + aTotalTransform.set( 1, 2, 0.0 ); + + // if there's no buffer bitmap, or as soon as the + // total transformation changes, we've got to + // re-render the bitmap + if( !mxBufferBitmap.is() || + aTotalTransform != maLastTransformation || + rSubset.mnSubsetBegin != maLastSubset.mnSubsetBegin || + rSubset.mnSubsetEnd != maLastSubset.mnSubsetEnd ) + { + DBG_TESTSOLARMUTEX(); + + // determine total scaling factor of the + // transformation matrix - need to make the bitmap + // large enough + ::basegfx::B2DTuple aScale; + ::basegfx::B2DTuple aTranslate; + double nRotate; + double nShearX; + if( !aTotalTransform.decompose( aScale, + aTranslate, + nRotate, + nShearX ) ) + { + OSL_ENSURE( false, + "TransparencyGroupAction::render(): non-decomposable transformation" ); + return false; + } + + // output size of metafile + ::Size aOutputSizePixel( ::basegfx::fround( aScale.getX() * maDstSize.getX() ), + ::basegfx::fround( aScale.getY() * maDstSize.getY() ) ); + + // pixel size of cache bitmap: round up to nearest int + ::Size aBitmapSizePixel( static_cast<sal_Int32>( aScale.getX() * maDstSize.getX() )+1, + static_cast<sal_Int32>( aScale.getY() * maDstSize.getY() )+1 ); + + ::Point aEmptyPoint; + + // render our content into an appropriately sized + // VirtualDevice with alpha channel + VirtualDevice aVDev( + *::Application::GetDefaultDevice(), 0, 0 ); + aVDev.SetOutputSizePixel( aBitmapSizePixel ); + aVDev.SetMapMode(); + + if( rSubset.mnSubsetBegin != 0 || + rSubset.mnSubsetEnd != -1 ) + { + // true subset - extract referenced + // metaactions from mpGroupMtf + GDIMetaFile aMtf; + MetaAction* pCurrAct; + int nCurrActionIndex; + + // extract subset actions + for( nCurrActionIndex=0, + pCurrAct=mpGroupMtf->FirstAction(); + pCurrAct; + ++nCurrActionIndex, pCurrAct = mpGroupMtf->NextAction() ) + { + switch( pCurrAct->GetType() ) + { + case META_PUSH_ACTION: + case META_POP_ACTION: + case META_CLIPREGION_ACTION: + case META_ISECTRECTCLIPREGION_ACTION: + case META_ISECTREGIONCLIPREGION_ACTION: + case META_MOVECLIPREGION_ACTION: + case META_LINECOLOR_ACTION: + case META_FILLCOLOR_ACTION: + case META_TEXTCOLOR_ACTION: + case META_TEXTFILLCOLOR_ACTION: + case META_TEXTLINECOLOR_ACTION: + case META_TEXTALIGN_ACTION: + case META_FONT_ACTION: + case META_RASTEROP_ACTION: + case META_REFPOINT_ACTION: + case META_LAYOUTMODE_ACTION: + // state-changing action - copy as-is + aMtf.AddAction( pCurrAct->Clone() ); + break; + + case META_GRADIENT_ACTION: + case META_HATCH_ACTION: + case META_EPS_ACTION: + case META_COMMENT_ACTION: + case META_POINT_ACTION: + case META_PIXEL_ACTION: + case META_LINE_ACTION: + case META_RECT_ACTION: + case META_ROUNDRECT_ACTION: + case META_ELLIPSE_ACTION: + case META_ARC_ACTION: + case META_PIE_ACTION: + case META_CHORD_ACTION: + case META_POLYLINE_ACTION: + case META_POLYGON_ACTION: + case META_POLYPOLYGON_ACTION: + case META_BMP_ACTION: + case META_BMPSCALE_ACTION: + case META_BMPSCALEPART_ACTION: + case META_BMPEX_ACTION: + case META_BMPEXSCALE_ACTION: + case META_BMPEXSCALEPART_ACTION: + case META_MASK_ACTION: + case META_MASKSCALE_ACTION: + case META_MASKSCALEPART_ACTION: + case META_GRADIENTEX_ACTION: + case META_WALLPAPER_ACTION: + case META_TRANSPARENT_ACTION: + case META_FLOATTRANSPARENT_ACTION: + case META_TEXT_ACTION: + case META_TEXTARRAY_ACTION: + case META_TEXTLINE_ACTION: + case META_TEXTRECT_ACTION: + case META_STRETCHTEXT_ACTION: + // output-generating action - only + // copy, if we're within the + // requested subset + if( rSubset.mnSubsetBegin <= nCurrActionIndex && + rSubset.mnSubsetEnd > nCurrActionIndex ) + { + aMtf.AddAction( pCurrAct->Clone() ); + } + break; + + default: + OSL_ENSURE( false, + "Unknown meta action type encountered" ); + break; + } + } + + aVDev.DrawTransparent( aMtf, + aEmptyPoint, + aOutputSizePixel, + *mpAlphaGradient ); + } + else + { + // no subsetting - render whole mtf + aVDev.DrawTransparent( *mpGroupMtf, + aEmptyPoint, + aOutputSizePixel, + *mpAlphaGradient ); + } + + + // update buffered bitmap and transformation + BitmapSharedPtr aBmp( VCLFactory::getInstance().createBitmap( + mpCanvas, + aVDev.GetBitmapEx( + aEmptyPoint, + aBitmapSizePixel ) ) ); + mxBufferBitmap = aBmp->getUNOBitmap(); + maLastTransformation = aTotalTransform; + maLastSubset = rSubset; + } + + // determine target transformation (we can't simply pass + // aTotalTransform as assembled above, since we must take + // the canvas' view state as is, it might contain clipping + // (which, in turn, is relative to the view + // transformation)) + + // given that aTotalTransform is the identity + // transformation, we could simply render our bitmap + // as-is. Now, since the mxBufferBitmap content already + // accounts for scale changes in the overall + // transformation, we must factor this out + // before. Generally, the transformation matrix should be + // structured like this: + // Translation*Rotation*Shear*Scale. Thus, to neutralize + // the contained scaling, we've got to right-multiply with + // the inverse. + ::basegfx::B2ISize aBmpSize( + ::basegfx::unotools::b2ISizeFromIntegerSize2D( mxBufferBitmap->getSize() ) ); + + ::basegfx::B2DHomMatrix aScaleCorrection; + aScaleCorrection.scale( (double)maDstSize.getX() / aBmpSize.getX(), + (double)maDstSize.getY() / aBmpSize.getY() ); + aTransform = aTransform * aScaleCorrection; + + rendering::RenderState aLocalState( maState ); + ::canvas::tools::setRenderStateTransform(aLocalState, aTransform); + +#ifdef SPECIAL_DEBUG + aLocalState.Clip.clear(); + aLocalState.DeviceColor = + ::vcl::unotools::colorToDoubleSequence( + ::Color( 0x80FF0000 ), + mpCanvas->getUNOCanvas()->getDevice()->getDeviceColorSpace() ); + + if( maState.Clip.is() ) + mpCanvas->getUNOCanvas()->fillPolyPolygon( maState.Clip, + mpCanvas->getViewState(), + aLocalState ); + + aLocalState.DeviceColor = maState.DeviceColor; +#endif + + if( ::rtl::math::approxEqual(mnAlpha, 1.0) ) + { + // no further alpha changes necessary -> draw directly + mpCanvas->getUNOCanvas()->drawBitmap( mxBufferBitmap, + mpCanvas->getViewState(), + aLocalState ); + } + else + { + // add alpha modulation value to DeviceColor + uno::Sequence<rendering::ARGBColor> aCols(1); + aCols[0] = rendering::ARGBColor( mnAlpha, 1.0, 1.0, 1.0); + aLocalState.DeviceColor = + mpCanvas->getUNOCanvas()->getDevice()->getDeviceColorSpace()->convertFromARGB( + aCols); + + mpCanvas->getUNOCanvas()->drawBitmapModulated( mxBufferBitmap, + mpCanvas->getViewState(), + aLocalState ); + } + + return true; + } + + // TODO(P3): The whole float transparency handling is a mess, + // this should be refactored. What's more, the old idea of + // having only internal 'metaactions', and not the original + // GDIMetaFile now looks a lot less attractive. Try to move + // into the direction of having a direct GDIMetaFile2XCanvas + // renderer, and maybe a separate metafile XCanvas + // implementation. + bool TransparencyGroupAction::render( const ::basegfx::B2DHomMatrix& rTransformation ) const + { + Subset aSubset; + + aSubset.mnSubsetBegin = 0; + aSubset.mnSubsetEnd = -1; + + return render( rTransformation, aSubset ); + } + + ::basegfx::B2DRange TransparencyGroupAction::getBounds( const ::basegfx::B2DHomMatrix& rTransformation ) const + { + rendering::RenderState aLocalState( maState ); + ::canvas::tools::prependToRenderState(aLocalState, rTransformation); + + return tools::calcDevicePixelBounds( + ::basegfx::B2DRange( 0,0, + maDstSize.getX(), + maDstSize.getY() ), + mpCanvas->getViewState(), + aLocalState ); + } + + ::basegfx::B2DRange TransparencyGroupAction::getBounds( const ::basegfx::B2DHomMatrix& rTransformation, + const Subset& rSubset ) const + { + // TODO(F3): Currently, the bounds for + // TransparencyGroupAction subsets equal those of the + // full set, although this action is able to render + // true subsets. + + // polygon only contains a single action, empty bounds + // if subset requests different range + if( rSubset.mnSubsetBegin != 0 || + rSubset.mnSubsetEnd != 1 ) + return ::basegfx::B2DRange(); + + return getBounds( rTransformation ); + } + + sal_Int32 TransparencyGroupAction::getActionCount() const + { + return mpGroupMtf.get() ? mpGroupMtf->GetActionCount() : 0; + } + + } + + ActionSharedPtr TransparencyGroupActionFactory::createTransparencyGroupAction( MtfAutoPtr& rGroupMtf, + const Renderer::Parameters& rParms, + const ::basegfx::B2DPoint& rDstPoint, + const ::basegfx::B2DVector& rDstSize, + double nAlpha, + const CanvasSharedPtr& rCanvas, + const OutDevState& rState ) + { + return ActionSharedPtr( new TransparencyGroupAction(rGroupMtf, + rParms, + rDstPoint, + rDstSize, + nAlpha, + rCanvas, + rState ) ); + } + + ActionSharedPtr TransparencyGroupActionFactory::createTransparencyGroupAction( MtfAutoPtr& rGroupMtf, + GradientAutoPtr& rAlphaGradient, + const Renderer::Parameters& rParms, + const ::basegfx::B2DPoint& rDstPoint, + const ::basegfx::B2DVector& rDstSize, + const CanvasSharedPtr& rCanvas, + const OutDevState& rState ) + { + return ActionSharedPtr( new TransparencyGroupAction(rGroupMtf, + rAlphaGradient, + rParms, + rDstPoint, + rDstSize, + rCanvas, + rState ) ); + } + + } +} |