diff options
Diffstat (limited to 'drawinglayer/source/processor2d')
22 files changed, 9500 insertions, 0 deletions
diff --git a/drawinglayer/source/processor2d/baseprocessor2d.cxx b/drawinglayer/source/processor2d/baseprocessor2d.cxx new file mode 100644 index 000000000000..ffa58ada0d03 --- /dev/null +++ b/drawinglayer/source/processor2d/baseprocessor2d.cxx @@ -0,0 +1,91 @@ +/************************************************************************* + * + * 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_drawinglayer.hxx" + +#include <drawinglayer/processor2d/baseprocessor2d.hxx> + +////////////////////////////////////////////////////////////////////////////// + +using namespace com::sun::star; + +////////////////////////////////////////////////////////////////////////////// + +namespace drawinglayer +{ + namespace processor2d + { + void BaseProcessor2D::processBasePrimitive2D(const primitive2d::BasePrimitive2D& /*rCandidate*/) + { + } + + BaseProcessor2D::BaseProcessor2D(const geometry::ViewInformation2D& rViewInformation) + : maViewInformation2D(rViewInformation) + { + } + + BaseProcessor2D::~BaseProcessor2D() + { + } + + void BaseProcessor2D::process(const primitive2d::Primitive2DSequence& rSource) + { + if(rSource.hasElements()) + { + const sal_Int32 nCount(rSource.getLength()); + + for(sal_Int32 a(0L); a < nCount; a++) + { + // get reference + const primitive2d::Primitive2DReference xReference(rSource[a]); + + if(xReference.is()) + { + // try to cast to BasePrimitive2D implementation + const primitive2d::BasePrimitive2D* pBasePrimitive = dynamic_cast< const primitive2d::BasePrimitive2D* >(xReference.get()); + + if(pBasePrimitive) + { + // it is a BasePrimitive2D implementation, use local processor + processBasePrimitive2D(*pBasePrimitive); + } + else + { + // unknown implementation, use UNO API call instead and process recursively + const uno::Sequence< beans::PropertyValue >& rViewParameters(getViewInformation2D().getViewInformationSequence()); + process(xReference->getDecomposition(rViewParameters)); + } + } + } + } + } + } // end of namespace processor2d +} // end of namespace drawinglayer + +////////////////////////////////////////////////////////////////////////////// +// eof diff --git a/drawinglayer/source/processor2d/canvasprocessor.cxx b/drawinglayer/source/processor2d/canvasprocessor.cxx new file mode 100644 index 000000000000..e20f1a417dcc --- /dev/null +++ b/drawinglayer/source/processor2d/canvasprocessor.cxx @@ -0,0 +1,2218 @@ +/************************************************************************* + * + * 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_drawinglayer.hxx" + +#include <drawinglayer/processor2d/canvasprocessor.hxx> +#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx> +#include <drawinglayer/primitive2d/polygonprimitive2d.hxx> +#include <com/sun/star/rendering/XCanvas.hpp> +#include <vcl/canvastools.hxx> +#include <basegfx/tools/canvastools.hxx> +#include <drawinglayer/primitive2d/polypolygonprimitive2d.hxx> +#include <drawinglayer/primitive2d/modifiedcolorprimitive2d.hxx> +#include <drawinglayer/primitive2d/transformprimitive2d.hxx> +#include <canvas/canvastools.hxx> +#include <svl/ctloptions.hxx> +#include <vcl/svapp.hxx> +#include <drawinglayer/primitive2d/maskprimitive2d.hxx> +#include <basegfx/polygon/b2dpolygonclipper.hxx> +#include <drawinglayer/primitive2d/pagepreviewprimitive2d.hxx> +#include <drawinglayer/primitive2d/metafileprimitive2d.hxx> +#include <cppcanvas/basegfxfactory.hxx> +#include <com/sun/star/rendering/XBitmapCanvas.hpp> +#include <cppcanvas/vclfactory.hxx> +#include <drawinglayer/primitive2d/pointarrayprimitive2d.hxx> +#include <drawinglayer/primitive2d/textprimitive2d.hxx> +#include <com/sun/star/rendering/TextDirection.hpp> +#include <vclhelperbitmaptransform.hxx> +#include <drawinglayer/primitive2d/bitmapprimitive2d.hxx> +#include <basegfx/polygon/b2dpolygontools.hxx> +#include <drawinglayer/primitive2d/transparenceprimitive2d.hxx> +#include <basegfx/tuple/b2i64tuple.hxx> +#include <basegfx/range/b2irange.hxx> +#include <com/sun/star/rendering/XIntegerReadOnlyBitmap.hpp> +#include <com/sun/star/rendering/CompositeOperation.hpp> +#include <com/sun/star/rendering/StrokeAttributes.hpp> +#include <com/sun/star/rendering/PathJoinType.hpp> +#include <drawinglayer/primitive2d/fillbitmapprimitive2d.hxx> +#include <com/sun/star/rendering/TexturingMode.hpp> +#include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx> +#include <vclhelperbufferdevice.hxx> +#include <drawinglayer/primitive2d/chartprimitive2d.hxx> +#include <helperchartrenderer.hxx> +#include <drawinglayer/primitive2d/wrongspellprimitive2d.hxx> +#include <helperwrongspellrenderer.hxx> +#include <basegfx/matrix/b2dhommatrixtools.hxx> + +////////////////////////////////////////////////////////////////////////////// + +using namespace com::sun::star; + +////////////////////////////////////////////////////////////////////////////// +// AW: Adding the canvas example from THB here to extract stuff later +/* + // TODO(Q3): share impCreateEmptyBitmapWithPattern() and other + // helper methods with vclprocessor.cxx + Bitmap impCreateEmptyBitmapWithPattern(Bitmap aSource, const Size& aTargetSizePixel) + { + Bitmap aRetval; + BitmapReadAccess* pReadAccess = aSource.AcquireReadAccess(); + + if(pReadAccess) + { + if(aSource.GetBitCount() <= 8) + { + BitmapPalette aPalette(pReadAccess->GetPalette()); + aRetval = Bitmap(aTargetSizePixel, aSource.GetBitCount(), &aPalette); + } + else + { + aRetval = Bitmap(aTargetSizePixel, aSource.GetBitCount()); + } + + delete pReadAccess; + } + + return aRetval; + } + + Bitmap impModifyBitmap(const basegfx::BColorModifier& rModifier, const Bitmap& rSource) + { + Bitmap aRetval(rSource); + + switch(rModifier.getMode()) + { + case basegfx::BCOLORMODIFYMODE_REPLACE : + { + aRetval = impCreateEmptyBitmapWithPattern(aRetval, Size(1L, 1L)); + aRetval.Erase(Color(rModifier.getBColor())); + break; + } + + default : // BCOLORMODIFYMODE_INTERPOLATE, BCOLORMODIFYMODE_GRAY, BCOLORMODIFYMODE_BLACKANDWHITE + { + BitmapWriteAccess* pContent = aRetval.AcquireWriteAccess(); + + if(pContent) + { + for(sal_uInt32 y(0L); y < (sal_uInt32)pContent->Height(); y++) + { + for(sal_uInt32 x(0L); x < (sal_uInt32)pContent->Width(); x++) + { + const Color aColor = pContent->GetPixel(y, x); + const basegfx::BColor aBColor(rModifier.getModifiedColor(aColor.getBColor())); + pContent->SetPixel(y, x, BitmapColor(Color(aBColor))); + } + } + + delete pContent; + } + + break; + } + } + + return aRetval; + } + + Bitmap impModifyBitmap(const basegfx::BColorModifierStack& rBColorModifierStack, const Bitmap& rSource) + { + Bitmap aRetval(rSource); + + for(sal_uInt32 a(rBColorModifierStack.count()); a; ) + { + const basegfx::BColorModifier& rModifier = rBColorModifierStack.getBColorModifier(--a); + aRetval = impModifyBitmap(rModifier, aRetval); + } + + return aRetval; + } + + sal_uInt32 impCalcGradientSteps(sal_uInt32 nSteps, const basegfx::B2DRange& rRange, sal_uInt32 nMaxDist) + { + if(nSteps == 0L) + nSteps = (sal_uInt32)(rRange.getWidth() + rRange.getHeight()) / 8; + + if(nSteps < 2L) + { + nSteps = 2L; + } + + if(nSteps > nMaxDist) + { + nSteps = nMaxDist; + } + + return nSteps; + } + + void canvasProcessor::impDrawGradientSimple( + const basegfx::B2DPolyPolygon& rTargetForm, + const ::std::vector< basegfx::B2DHomMatrix >& rMatrices, + const ::std::vector< basegfx::BColor >& rColors, + const basegfx::B2DPolygon& rUnitPolygon) + { + uno::Reference< rendering::XPolyPolygon2D > xPoly( + basegfx::unotools::xPolyPolygonFromB2DPolygon( + mxCanvas->getDevice(), + rUnitPolygon)); + uno::Reference< rendering::XPolyPolygon2D > xTargetPoly( + basegfx::unotools::xPolyPolygonFromB2DPolyPolygon( + mxCanvas->getDevice(), + rTargetForm)); + + for(sal_uInt32 a(0L); a < rColors.size(); a++) + { + // set correct color + const basegfx::BColor aFillColor(rColors[a]); + + maRenderState.DeviceColor = basegfx::unotools::colorToDoubleSequence( + mxCanvas->getDevice(), + aFillColor); + + if(a) + { + if(a - 1L < rMatrices.size()) + { + canvas::tools::setRenderStateTransform( maRenderState, + rMatrices[a - 1L] ); + mxCanvas->fillPolyPolygon(xPoly,maViewState,maRenderState); + } + } + else + { + canvas::tools::setRenderStateTransform( maRenderState, + basegfx::B2DHomMatrix() ); + mxCanvas->fillPolyPolygon(xTargetPoly,maViewState,maRenderState); + } + } + } + + void canvasProcessor::impDrawGradientComplex( + const basegfx::B2DPolyPolygon& rTargetForm, + const ::std::vector< basegfx::B2DHomMatrix >& rMatrices, + const ::std::vector< basegfx::BColor >& rColors, + const basegfx::B2DPolygon& rUnitPolygon) + { + uno::Reference< rendering::XPolyPolygon2D > xPoly( + basegfx::unotools::xPolyPolygonFromB2DPolygon( + mxCanvas->getDevice(), + rUnitPolygon)); + uno::Reference< rendering::XPolyPolygon2D > xTargetPoly( + basegfx::unotools::xPolyPolygonFromB2DPolyPolygon( + mxCanvas->getDevice(), + rTargetForm)); + + maRenderState.Clip = xTargetPoly; + + // draw gradient PolyPolygons + for(std::size_t a = 0L; a < rMatrices.size(); a++) + { + // set correct color + if(rColors.size() > a) + { + const basegfx::BColor aFillColor(rColors[a]); + + maRenderState.DeviceColor = basegfx::unotools::colorToDoubleSequence( + mxCanvas->getDevice(), + aFillColor); + } + + canvas::tools::setRenderStateTransform( maRenderState, + rMatrices[a] ); + + if(a) + mxCanvas->fillPolyPolygon(xPoly,maViewState,maRenderState); + else + mxCanvas->fillPolyPolygon(xTargetPoly,maViewState,maRenderState); + } + + maRenderState.Clip.clear(); + } + + void canvasProcessor::impDrawGradient( + const basegfx::B2DPolyPolygon& rTargetForm, + ::drawinglayer::primitive::GradientStyle eGradientStyle, + sal_uInt32 nSteps, + const basegfx::BColor& rStart, + const basegfx::BColor& rEnd, + double fBorder, double fAngle, double fOffsetX, double fOffsetY, bool bSimple) + { + fprintf(stderr,"impDrawGradient\n"); + + basegfx::B2DPolyPolygon aTmp(rTargetForm); + aTmp.transform( maWorldToView ); + const basegfx::B2DRange aOutlineRangePixel(basegfx::tools::getRange(aTmp)); + const basegfx::B2DRange aOutlineRange(basegfx::tools::getRange(rTargetForm)); + + fprintf(stderr,"impDrawGradient: #%d\n",nSteps); + + if( // step count is infinite, can use native canvas + // gradients here + nSteps == 0 || + // step count is sufficiently high, such that no + // discernible difference should be visible. + nSteps > 64 ) + { + uno::Reference< rendering::XParametricPolyPolygon2DFactory > xFactory( + mxCanvas->getDevice()->getParametricPolyPolygonFactory() ); + + if( xFactory.is() ) + { + fprintf(stderr,"native gradient #1\n"); + + basegfx::B2DHomMatrix aTextureTransformation; + rendering::Texture aTexture; + + aTexture.RepeatModeX = rendering::TexturingMode::CLAMP; + aTexture.RepeatModeY = rendering::TexturingMode::CLAMP; + aTexture.Alpha = 1.0; + + + // setup start/end color values + // ---------------------------- + + const uno::Sequence< double > aStartColor( + basegfx::unotools::colorToDoubleSequence( mxCanvas->getDevice(), + rStart )); + const uno::Sequence< double > aEndColor( + basegfx::unotools::colorToDoubleSequence( mxCanvas->getDevice(), + rEnd )); + + // Setup texture transformation + // ---------------------------- + + const basegfx::B2DRange& rBounds( + basegfx::tools::getRange( rTargetForm )); + + // setup rotation angle. VCL rotates + // counter-clockwise, while canvas transformation + // rotates clockwise + //fAngle = -fAngle; + + switch(eGradientStyle) + { + case ::drawinglayer::primitive::GRADIENTSTYLE_LINEAR: + // FALLTHROUGH intended + case ::drawinglayer::primitive::GRADIENTSTYLE_AXIAL: + { + // standard orientation for VCL linear + // gradient is vertical, thus, rotate 90 + // degrees + fAngle += M_PI/2.0; + + // shrink texture, to account for border + // (only in x direction, linear gradient + // is constant in y direction, anyway) + aTextureTransformation.scale( + basegfx::pruneScaleValue(1.0 - fBorder), + 1.0 ); + + double fBorderX(0.0); + + // determine type of gradient (and necessary + // transformation matrix, should it be emulated by a + // generic gradient) + switch(eGradientStyle) + { + case ::drawinglayer::primitive::GRADIENTSTYLE_LINEAR: + // linear gradients don't respect + // offsets (they are implicitely + // assumed to be 50%). linear + // gradients don't have border on + // both sides, only on the + // startColor side. Gradient is + // invariant in y direction: leave + // y offset alone. + fBorderX = fBorder; + aTexture.Gradient = xFactory->createLinearHorizontalGradient( aStartColor, + aEndColor ); + break; + + case ::drawinglayer::primitive::GRADIENTSTYLE_AXIAL: + // axial gradients have border on + // both sides. Gradient is + // invariant in y direction: leave + // y offset alone. + fBorderX = fBorder * .5; + aTexture.Gradient = xFactory->createAxialHorizontalGradient( aStartColor, + aEndColor ); + break; + } + + // apply border offset values + aTextureTransformation.translate( fBorderX, + 0.0 ); + + // rotate texture according to gradient rotation + aTextureTransformation.translate( -0.5, -0.5 ); + aTextureTransformation.rotate( fAngle ); + + // to let the first strip of a rotated + // gradient start at the _edge_ of the + // bound rect (and not, due to rotation, + // slightly inside), slightly enlarge the + // gradient: + // + // y/2 sin(transparence) + x/2 cos(transparence) + // + // (values to change are not actual + // gradient scales, but original bound + // rect dimensions. Since we still want + // the border setting to apply after that, + // we multiply with that as above for + // nScaleX) + const double nScale( + basegfx::pruneScaleValue( + fabs( rBounds.getHeight()*sin(fAngle) ) + + fabs( rBounds.getWidth()*cos(fAngle) ))); + + aTextureTransformation.scale( nScale, nScale ); + + // translate back origin to center of + // primitive + aTextureTransformation.translate( 0.5*rBounds.getWidth(), + 0.5*rBounds.getHeight() ); + break; + } + + case ::drawinglayer::primitive::GRADIENTSTYLE_RADIAL: + // FALLTHROUGH intended + case ::drawinglayer::primitive::GRADIENTSTYLE_ELLIPTICAL: + // FALLTHROUGH intended + case ::drawinglayer::primitive::GRADIENTSTYLE_SQUARE: + // FALLTHROUGH intended + case ::drawinglayer::primitive::GRADIENTSTYLE_RECT: + { + fprintf(stderr,"native gradient #2\n"); + + // determine scale factors for the gradient (must + // be scaled up from [0,1]x[0,1] rect to object + // bounds). Will potentially changed in switch + // statement below. + // Respect border value, while doing so, the VCL + // gradient's border will effectively shrink the + // resulting gradient. + double nScaleX( rBounds.getWidth() * (1.0 - fBorder) ); + double nScaleY( rBounds.getHeight()* (1.0 - fBorder) ); + + // determine offset values. Since the + // border is divided half-by-half to both + // sides of the gradient, divide + // translation offset by an additional + // factor of 2. Also respect offset here, + // but since VCL gradients have their + // center at [0,0] for zero offset, but + // canvas gradients have their top, left + // edge aligned with the primitive, and + // offset of 50% effectively must yield + // zero shift. Both values will + // potentially be adapted in switch + // statement below. + double nOffsetX( rBounds.getWidth() * + (2.0 * fOffsetX - 1.0 + fBorder)*.5 ); + double nOffsetY( rBounds.getHeight() * + (2.0 * fOffsetY - 1.0 + fBorder)*.5 ); + + // determine type of gradient (and necessary + // transformation matrix, should it be emulated by a + // generic gradient) + switch(eGradientStyle) + { + case ::drawinglayer::primitive::GRADIENTSTYLE_RADIAL: + { + // create isotrophic scaling + if( nScaleX > nScaleY ) + { + nOffsetY -= (nScaleX - nScaleY) * 0.5; + nScaleY = nScaleX; + } + else + { + nOffsetX -= (nScaleY - nScaleX) * 0.5; + nScaleX = nScaleY; + } + + // enlarge gradient to match bound rect diagonal + aTextureTransformation.translate( -0.5, -0.5 ); + const double nScale( hypot(rBounds.getWidth(), + rBounds.getHeight()) / nScaleX ); + aTextureTransformation.scale( nScale, nScale ); + aTextureTransformation.translate( 0.5, 0.5 ); + + aTexture.Gradient = xFactory->createEllipticalGradient( + aEndColor, + aStartColor, + cssgeom::RealRectangle2D(0.0,0.0, + 1.0,1.0) ); + } + break; + + case ::drawinglayer::primitive::GRADIENTSTYLE_ELLIPTICAL: + { + // enlarge gradient slightly + aTextureTransformation.translate( -0.5, -0.5 ); + const double nSqrt2( sqrt(2.0) ); + aTextureTransformation.scale( nSqrt2,nSqrt2 ); + aTextureTransformation.translate( 0.5, 0.5 ); + + aTexture.Gradient = xFactory->createEllipticalGradient( + aEndColor, + aStartColor, + cssgeom::RealRectangle2D( rBounds.getMinX(), + rBounds.getMinY(), + rBounds.getMaxX(), + rBounds.getMaxY() )); + } + break; + + case ::drawinglayer::primitive::GRADIENTSTYLE_SQUARE: + { + // create isotrophic scaling + if( nScaleX > nScaleY ) + { + nOffsetY -= (nScaleX - nScaleY) * 0.5; + nScaleY = nScaleX; + } + else + { + nOffsetX -= (nScaleY - nScaleX) * 0.5; + nScaleX = nScaleY; + } + + aTexture.Gradient = xFactory->createRectangularGradient( + aEndColor, + aStartColor, + cssgeom::RealRectangle2D(0.0,0.0, + 1.0,1.0)); + } + break; + + case ::drawinglayer::primitive::GRADIENTSTYLE_RECT: + { + aTexture.Gradient = xFactory->createRectangularGradient( + aEndColor, + aStartColor, + cssgeom::RealRectangle2D( rBounds.getMinX(), + rBounds.getMinY(), + rBounds.getMaxX(), + rBounds.getMaxY() )); + } + break; + } + + nScaleX = basegfx::pruneScaleValue( nScaleX ); + nScaleY = basegfx::pruneScaleValue( nScaleY ); + + aTextureTransformation.scale( nScaleX, nScaleY ); + + // rotate texture according to gradient rotation + aTextureTransformation.translate( -0.5*nScaleX, -0.5*nScaleY ); + aTextureTransformation.rotate( fAngle ); + aTextureTransformation.translate( 0.5*nScaleX, 0.5*nScaleY ); + + aTextureTransformation.translate( nOffsetX, nOffsetY ); + } + break; + + default: + OSL_ENSURE( false, + "canvasProcessor::impDrawGradient(): Unexpected gradient type" ); + break; + } + + // As the texture coordinate space is relative to + // the polygon coordinate space (NOT to the + // polygon itself), move gradient to the start of + // the actual polygon. If we skip this, the + // gradient will always display at the origin, and + // not within the polygon bound (which might be + // miles away from the origin). + aTextureTransformation.translate( rBounds.getMinX(), + rBounds.getMinY() ); + + basegfx::unotools::affineMatrixFromHomMatrix( aTexture.AffineTransform, + aTextureTransformation ); + uno::Sequence< rendering::Texture > aSeq(1); + aSeq[0] = aTexture; + + mxCanvas->fillTexturedPolyPolygon( + basegfx::unotools::xPolyPolygonFromB2DPolyPolygon( + mxCanvas->getDevice(), + rTargetForm), + maViewState, + maRenderState, + aSeq ); + + // done, using native gradients + return; + } + } + else + { + // make sure steps is not too high/low + nSteps = impCalcGradientSteps(nSteps, + aOutlineRangePixel, + sal_uInt32((rStart.getMaximumDistance(rEnd) * 127.5) + 0.5)); + + + ::std::vector< basegfx::B2DHomMatrix > aMatrices; + ::std::vector< basegfx::BColor > aColors; + basegfx::B2DPolygon aUnitPolygon; + + if( drawinglayer::primitive::GRADIENTSTYLE_RADIAL == eGradientStyle || + drawinglayer::primitive::GRADIENTSTYLE_ELLIPTICAL == eGradientStyle) + { + const basegfx::B2DPoint aCircleCenter(0.5, 0.5); + aUnitPolygon = basegfx::tools::createPolygonFromEllipse(aCircleCenter, 0.5, 0.5); + aUnitPolygon = basegfx::tools::adaptiveSubdivideByAngle(aUnitPolygon); + } + else + { + aUnitPolygon = basegfx::tools::createUnitPolygon(); + } + + // create geometries + switch(eGradientStyle) + { + case ::drawinglayer::primitive::GRADIENTSTYLE_LINEAR: + { + ::drawinglayer::primitive::geoTexSvxGradientLinear aGradient(aOutlineRange, rStart, rEnd, nSteps, fBorder, fAngle); + aGradient.appendTransformations(aMatrices); + aGradient.appendColors(aColors); + break; + } + case ::drawinglayer::primitive::GRADIENTSTYLE_AXIAL: + { + ::drawinglayer::primitive::geoTexSvxGradientAxial aGradient(aOutlineRange, rStart, rEnd, nSteps, fBorder, fAngle); + aGradient.appendTransformations(aMatrices); + aGradient.appendColors(aColors); + break; + } + case ::drawinglayer::primitive::GRADIENTSTYLE_RADIAL: + { + ::drawinglayer::primitive::geoTexSvxGradientRadial aGradient(aOutlineRange, rStart, rEnd, nSteps, fBorder, fOffsetX, fOffsetY); + aGradient.appendTransformations(aMatrices); + aGradient.appendColors(aColors); + break; + } + case ::drawinglayer::primitive::GRADIENTSTYLE_ELLIPTICAL: + { + ::drawinglayer::primitive::geoTexSvxGradientElliptical aGradient(aOutlineRange, rStart, rEnd, nSteps, fBorder, fOffsetX, fOffsetX, fAngle); + aGradient.appendTransformations(aMatrices); + aGradient.appendColors(aColors); + break; + } + case ::drawinglayer::primitive::GRADIENTSTYLE_SQUARE: + { + ::drawinglayer::primitive::geoTexSvxGradientSquare aGradient(aOutlineRange, rStart, rEnd, nSteps, fBorder, fOffsetX, fOffsetX, fAngle); + aGradient.appendTransformations(aMatrices); + aGradient.appendColors(aColors); + break; + } + case ::drawinglayer::primitive::GRADIENTSTYLE_RECT: + { + ::drawinglayer::primitive::geoTexSvxGradientRect aGradient(aOutlineRange, rStart, rEnd, nSteps, fBorder, fOffsetX, fOffsetX, fAngle); + aGradient.appendTransformations(aMatrices); + aGradient.appendColors(aColors); + break; + } + } + + // paint them with mask using the XOR method + if(aMatrices.size()) + { + if(bSimple) + { + impDrawGradientSimple(rTargetForm, aMatrices, aColors, aUnitPolygon); + } + else + { + impDrawGradientComplex(rTargetForm, aMatrices, aColors, aUnitPolygon); + } + } + } + } + + + ////////////////////////////////////////////////////////////////////////////// + // rendering support + + // directdraw of text simple portion + void canvasProcessor::impRender_STXP(const textSimplePortionPrimitive& rTextCandidate) + { + const fontAttributes& rFontAttrs( rTextCandidate.getFontAttribute() ); + rendering::FontRequest aFontRequest; + + aFontRequest.FontDescription.FamilyName = rFontAttrs.maFamilyName; + aFontRequest.FontDescription.StyleName = rFontAttrs.maStyleName; + aFontRequest.FontDescription.IsSymbolFont = rFontAttrs.mbSymbol ? util::TriState_YES : util::TriState_NO; + aFontRequest.FontDescription.IsVertical = rFontAttrs.mbVertical ? util::TriState_YES : util::TriState_NO; + + // TODO(F2): improve vclenum->panose conversion + aFontRequest.FontDescription.FontDescription.Weight = + rFontAttrs.mnWeight; + aFontRequest.FontDescription.FontDescription.Letterform = + rFontAttrs.mbItalic ? 9 : 0; + + // font matrix should only be used for glyph rotations etc. + css::geometry::Matrix2D aFontMatrix; + canvas::tools::setIdentityMatrix2D( aFontMatrix ); + + uno::Reference<rendering::XCanvasFont> xFont( + mxCanvas->createFont( aFontRequest, + uno::Sequence< beans::PropertyValue >(), + aFontMatrix )); + + if( !xFont.is() ) + return; + + uno::Reference<rendering::XTextLayout> xLayout( + xFont->createTextLayout( + rendering::StringContext( rTextCandidate.getText(), + 0, + rTextCandidate.getText().Len() ), + // TODO(F3): Is this sufficient? + rendering::TextDirection::WEAK_LEFT_TO_RIGHT, + 0 )); + if( !xLayout.is() ) + return; + + xLayout->applyLogicalAdvancements( + uno::Sequence<double>(&rTextCandidate.getDXArray()[0], + rTextCandidate.getDXArray().size() )); + + const basegfx::BColor aRGBColor( + maBColorModifierStack.getModifiedColor( + rTextCandidate.getFontColor())); + + maRenderState.DeviceColor = basegfx::unotools::colorToDoubleSequence( + mxCanvas->getDevice(), + aRGBColor); + + // get render parameters and paint + mxCanvas->drawTextLayout( xLayout, + maViewState, + maRenderState ); + } + + // direct draw of hairline + void canvasProcessor::impRender_POHL(const polygonHairlinePrimitive& rPolygonCandidate) + { + const basegfx::BColor aRGBColor( + maBColorModifierStack.getModifiedColor( + rPolygonCandidate.getBColor())); + + maRenderState.DeviceColor = basegfx::unotools::colorToDoubleSequence( + mxCanvas->getDevice(), + aRGBColor); + + mxCanvas->drawPolyPolygon( basegfx::unotools::xPolyPolygonFromB2DPolygon( + mxCanvas->getDevice(), + rPolygonCandidate.getB2DPolygon()), + maViewState, + maRenderState ); + } + + // direct draw of transformed BitmapEx primitive + void canvasProcessor::impRender_BMPR(const bitmapPrimitive& rBitmapCandidate) + { + BitmapEx aBitmapEx(rBitmapCandidate.getBitmapEx()); + + if(maBColorModifierStack.count()) + { + // TODO(Q3): Share common bmp modification code with + // vclprocessor.cxx + Bitmap aChangedBitmap(impModifyBitmap(maBColorModifierStack, aBitmapEx.GetBitmap())); + + if(aBitmapEx.IsTransparent()) + { + if(aBitmapEx.IsAlpha()) + aBitmapEx = BitmapEx(aChangedBitmap, aBitmapEx.GetAlpha()); + else + aBitmapEx = BitmapEx(aChangedBitmap, aBitmapEx.GetMask()); + } + else + aBitmapEx = BitmapEx(aChangedBitmap); + } + + mxCanvas->drawBitmap( + vcl::unotools::xBitmapFromBitmapEx( mxCanvas->getDevice(), + aBitmapEx ), + maViewState, + maRenderState); + } + + void canvasProcessor::impRender_PPLB(const polyPolygonBitmapPrimitive& rPolyBitmapCandidate ) + { + const fillBitmapAttribute& rFillBmpAttr( rPolyBitmapCandidate.getFillBitmap() ); + const basegfx::B2DPolyPolygon& rPoly( rPolyBitmapCandidate.getB2DPolyPolygon() ); + + // TODO(Q3): Share common bmp modification code with + // vclprocessor.cxx + Bitmap aChangedBitmap( + impModifyBitmap(maBColorModifierStack, + rFillBmpAttr.getBitmap())); + + rendering::Texture aTexture; + const basegfx::B2DVector aBmpSize( rFillBmpAttr.getSize() ); + + const basegfx::B2DRange& rBounds( + basegfx::tools::getRange( rPoly )); + + basegfx::B2DHomMatrix aScale; + aScale.scale( aBmpSize.getX() * rBounds.getWidth(), + aBmpSize.getY() * rBounds.getHeight() ); + + basegfx::unotools::affineMatrixFromHomMatrix( + aTexture.AffineTransform, + aScale ); + + aTexture.Alpha = 1.0; + aTexture.Bitmap = + ::vcl::unotools::xBitmapFromBitmapEx( + mxCanvas->getDevice(), + aChangedBitmap ); + aTexture.RepeatModeX = rendering::TexturingMode::REPEAT; + aTexture.RepeatModeY = rendering::TexturingMode::REPEAT; + + uno::Sequence< rendering::Texture > aSeq(1); + aSeq[0] = aTexture; + + mxCanvas->fillTexturedPolyPolygon( + basegfx::unotools::xPolyPolygonFromB2DPolyPolygon( + mxCanvas->getDevice(), + rPoly), + maViewState, + maRenderState, + aSeq ); + } + + // direct draw of gradient + void canvasProcessor::impRender_PPLG(const polyPolygonGradientPrimitive& rPolygonCandidate) + { + const fillGradientAttribute& rGradient(rPolygonCandidate.getFillGradient()); + basegfx::BColor aStartColor(maBColorModifierStack.getModifiedColor(rGradient.getStartColor())); + basegfx::BColor aEndColor(maBColorModifierStack.getModifiedColor(rGradient.getEndColor())); + basegfx::B2DPolyPolygon aLocalPolyPolygon(rPolygonCandidate.getB2DPolyPolygon()); + + if(aStartColor == aEndColor) + { + // no gradient at all, draw as polygon + + maRenderState.DeviceColor = basegfx::unotools::colorToDoubleSequence( + mxCanvas->getDevice(), + aStartColor); + + mxCanvas->drawPolyPolygon( basegfx::unotools::xPolyPolygonFromB2DPolyPolygon( + mxCanvas->getDevice(), + aLocalPolyPolygon), + maViewState, + maRenderState ); + } + else + { + // TODO(F3): if rGradient.getSteps() > 0, render + // gradient manually! + impDrawGradient( + aLocalPolyPolygon, rGradient.getStyle(), rGradient.getSteps(), + aStartColor, aEndColor, rGradient.getBorder(), + -rGradient.getAngle(), rGradient.getOffsetX(), rGradient.getOffsetY(), false); + } + } + + // direct draw of PolyPolygon with color + void canvasProcessor::impRender_PPLC(const polyPolygonColorPrimitive& rPolygonCandidate) + { + const basegfx::BColor aRGBColor( + maBColorModifierStack.getModifiedColor( + rPolygonCandidate.getBColor())); + + maRenderState.DeviceColor = basegfx::unotools::colorToDoubleSequence( + mxCanvas->getDevice(), + aRGBColor); + + mxCanvas->fillPolyPolygon( basegfx::unotools::xPolyPolygonFromB2DPolyPolygon( + mxCanvas->getDevice(), + rPolygonCandidate.getB2DPolyPolygon()), + maViewState, + maRenderState ); + } + + // direct draw of MetaFile + void canvasProcessor::impRender_META(const metafilePrimitive& rMetaCandidate) + { + // get metafile (copy it) + GDIMetaFile aMetaFile; + + // TODO(Q3): Share common metafile modification code with + // vclprocessor.cxx + if(maBColorModifierStack.count()) + { + const basegfx::BColor aRGBBaseColor(0, 0, 0); + const basegfx::BColor aRGBColor(maBColorModifierStack.getModifiedColor(aRGBBaseColor)); + aMetaFile = rMetaCandidate.getMetaFile().GetMonochromeMtf(Color(aRGBColor)); + } + else + { + aMetaFile = rMetaCandidate.getMetaFile(); + } + + cppcanvas::BitmapCanvasSharedPtr pCanvas( + cppcanvas::VCLFactory::getInstance().createCanvas( + uno::Reference<rendering::XBitmapCanvas>( + mxCanvas, + uno::UNO_QUERY_THROW) )); + cppcanvas::RendererSharedPtr pMtfRenderer( + cppcanvas::VCLFactory::getInstance().createRenderer( + pCanvas, + aMetaFile, + cppcanvas::Renderer::Parameters() )); + if( pMtfRenderer ) + { + pCanvas->setTransformation(maWorldToView); + pMtfRenderer->setTransformation(rMetaCandidate.getTransform()); + pMtfRenderer->draw(); + } + } + + // mask group. Set mask polygon as clip + void canvasProcessor::impRender_MASK(const maskPrimitive& rMaskCandidate) + { + const primitiveVector& rSubList = rMaskCandidate.getPrimitiveVector(); + + if(!rSubList.empty()) + { + // TODO(F3): cannot use state-global renderstate, when recursing! + maRenderState.Clip = + basegfx::unotools::xPolyPolygonFromB2DPolyPolygon( + mxCanvas->getDevice(), + rMaskCandidate.getMask()); + + // paint to it + process(rSubList); + + maRenderState.Clip.clear(); + } + } + + // modified color group. Force output to unified color. + void canvasProcessor::impRender_MCOL(const modifiedColorPrimitive& rModifiedCandidate) + { + const primitiveVector& rSubList = rModifiedCandidate.getPrimitiveVector(); + + if(!rSubList.empty()) + { + maBColorModifierStack.push(rModifiedCandidate.getColorModifier()); + process(rModifiedCandidate.getPrimitiveVector()); + maBColorModifierStack.pop(); + } + } + + // sub-transparence group. Draw to bitmap device first. + void canvasProcessor::impRender_TRPR(const transparencePrimitive& rTransCandidate) + { + const primitiveVector& rSubList = rTransCandidate.getPrimitiveVector(); + + if(!rSubList.empty()) + { + basegfx::B2DRange aRange( + get2DRangeFromVector(rSubList, + getViewInformation())); + aRange.transform(maWorldToView); + const basegfx::B2I64Tuple& rSize( + canvas::tools::spritePixelAreaFromB2DRange(aRange).getRange()); + uno::Reference< rendering::XCanvas > xBitmap( + mxCanvas->getDevice()->createCompatibleAlphaBitmap( + css::geometry::IntegerSize2D(rSize.getX(), + rSize.getY())), + uno::UNO_QUERY_THROW); + + // remember last worldToView and add pixel offset + basegfx::B2DHomMatrix aLastWorldToView(maWorldToView); + basegfx::B2DHomMatrix aPixelOffset; + aPixelOffset.translate(aRange.getMinX(), + aRange.getMinY()); + setWorldToView(aPixelOffset * maWorldToView); + + // remember last canvas, set bitmap as target + uno::Reference< rendering::XCanvas > xLastCanvas( mxCanvas ); + mxCanvas = xBitmap; + + // paint content to it + process(rSubList); + + // TODO(F3): render transparent list to transparence + // channel. Note that the OutDev implementation has a + // shortcoming, in that nested transparency groups + // don't work - transparence is not combined properly. + + // process(rTransCandidate.getTransparenceList()); + + // back to old OutDev and worldToView + mxCanvas = xLastCanvas; + setWorldToView(aLastWorldToView); + + // DUMMY: add transparence modulation value to DeviceColor + // TODO(F3): color management + canvas::tools::setDeviceColor( maRenderState, + 1.0, 1.0, 1.0, 0.5 ); + // finally, draw bitmap + mxCanvas->drawBitmapModulated( + uno::Reference< rendering::XBitmap >( + xBitmap, + uno::UNO_QUERY_THROW), + maViewState, + maRenderState ); + } + } + + // transform group. + void canvasProcessor::impRender_TRN2(const transformPrimitive& rTransformCandidate) + { + // remember current transformation + basegfx::B2DHomMatrix aLastWorldToView(maWorldToView); + + // create new transformations + setWorldToView(maWorldToView * rTransformCandidate.getTransformation()); + + // let break down + process(rTransformCandidate.getPrimitiveVector()); + + // restore transformations + setWorldToView(aLastWorldToView); + } + + // marker + void canvasProcessor::impRender_MARK(const markerPrimitive& rMarkCandidate) + { + const basegfx::BColor aRGBColor(maBColorModifierStack.getModifiedColor(rMarkCandidate.getRGBColor())); + + canvas::tools::initRenderState(maMarkerRenderState); + maMarkerRenderState.DeviceColor = basegfx::unotools::colorToDoubleSequence( + mxCanvas->getDevice(), + aRGBColor); + + // Markers are special objects - their position is + // determined by the view transformation, but their size + // is always the same + const basegfx::B2DPoint aViewPos(maWorldToView * rMarkCandidate.getPosition()); + + uno::Reference< rendering::XPolyPolygon2D > xMarkerPoly; + uno::Reference< rendering::XPolyPolygon2D > xHighlightMarkerPoly; + switch(rMarkCandidate.getStyle()) + { + default: + case MARKERSTYLE_POINT: + mxCanvas->drawPoint( basegfx::unotools::point2DFromB2DPoint(aViewPos), + maMarkerViewState, + maMarkerRenderState ); + return; + + case MARKERSTYLE_CROSS: + if( !mxCrossMarkerPoly.is() ) + { + basegfx::B2DPolyPolygon aPoly; + basegfx::tools::importFromSvgD( + aPoly, + rtl::OUString::createFromAscii( + "m-1 0 h2 m0 -1 v2" )); + mxCrossMarkerPoly = + basegfx::unotools::xPolyPolygonFromB2DPolyPolygon( + mxCanvas->getDevice(), + aPoly ); + } + xMarkerPoly = mxCrossMarkerPoly; + break; + + case MARKERSTYLE_GLUEPOINT : + if( !mxGluePointPoly.is() ) + { + basegfx::B2DPolyPolygon aPoly; + basegfx::tools::importFromSvgD( + aPoly, + rtl::OUString::createFromAscii( + "m-2 -3 l5 5 m-3 -2 l5 5 m-3 2 l5 -5 m-2 3 l5 -5" )); + mxGluePointPoly = + basegfx::unotools::xPolyPolygonFromB2DPolyPolygon( + mxCanvas->getDevice(), + aPoly ); + } + if( !mxGluePointHighlightPoly.is() ) + { + basegfx::B2DPolyPolygon aPoly; + basegfx::tools::importFromSvgD( + aPoly, + rtl::OUString::createFromAscii( + "m-2 -2 l4 4 m-2 2 l4 -4" )); + mxGluePointHighlightPoly = + basegfx::unotools::xPolyPolygonFromB2DPolyPolygon( + mxCanvas->getDevice(), + aPoly ); + } + xMarkerPoly = mxGluePointPoly; + xHighlightMarkerPoly = mxGluePointHighlightPoly; + break; + } + + basegfx::B2DRange aRange; + rMarkCandidate.getRealtiveViewRange(aRange); + const basegfx::B2DPoint aCenter(aRange.getCenter()); + + basegfx::B2DHomMatrix aTranslate; + aTranslate.translate(aViewPos.getX()+aCenter.getX(), + aViewPos.getY()+aCenter.getY()); + + canvas::tools::setRenderStateTransform( maMarkerRenderState, + aTranslate ); + + + mxCanvas->drawPolyPolygon( xMarkerPoly, + maMarkerViewState, + maMarkerRenderState ); + if( xHighlightMarkerPoly.is() ) + { + // TODO(F3): color management + canvas::tools::setDeviceColor(maMarkerRenderState, + 0.0, 0.0, 1.0, 1.0); + mxCanvas->drawPolyPolygon( xMarkerPoly, + maMarkerViewState, + maMarkerRenderState ); + } + } + + void canvasProcessor::setWorldToView(const basegfx::B2DHomMatrix& rMat) + { + maWorldToView = rMat; + canvas::tools::setViewStateTransform(maViewState, + maWorldToView); + } + + ////////////////////////////////////////////////////////////////////////////// + // internal processing support + + void canvasProcessor::process(const primitiveVector& rSource) + { + primitiveVector::const_iterator aCurr = rSource.begin(); + const primitiveVector::const_iterator aEnd = rSource.end(); + while( aCurr != aEnd ) + { + const referencedPrimitive& rCandidate = *aCurr; + + switch(rCandidate.getID()) + { + case CreatePrimitiveID('S', 'T', 'X', 'P'): + { + // directdraw of text simple portion + impRender_STXP(static_cast< const textSimplePortionPrimitive& >(rCandidate.getBasePrimitive())); + break; + } + + case CreatePrimitiveID('P', 'O', 'H', 'L'): + { + // direct draw of hairline + impRender_POHL(static_cast< const polygonHairlinePrimitive& >(rCandidate.getBasePrimitive())); + break; + } + + case CreatePrimitiveID('B', 'M', 'P', 'R'): + { + // direct draw of transformed BitmapEx primitive + impRender_BMPR(static_cast< const bitmapPrimitive& >(rCandidate.getBasePrimitive())); + break; + } + + case CreatePrimitiveID('F', 'B', 'M', 'P'): + { + OSL_ENSURE(false,"fillBitmapPrimitive not yet implemented"); + break; + } + + case CreatePrimitiveID('P', 'P', 'L', 'B'): + { + // direct draw of polygon with bitmap fill + impRender_PPLB(static_cast< const polyPolygonBitmapPrimitive& >(rCandidate.getBasePrimitive())); + break; + } + + case CreatePrimitiveID('P', 'P', 'L', 'G'): + { + // direct draw of gradient + impRender_PPLG(static_cast< const polyPolygonGradientPrimitive& >(rCandidate.getBasePrimitive())); + break; + } + + case CreatePrimitiveID('P', 'P', 'L', 'C'): + { + // direct draw of PolyPolygon with color + impRender_PPLC(static_cast< const polyPolygonColorPrimitive& >(rCandidate.getBasePrimitive())); + break; + } + + case CreatePrimitiveID('M', 'E', 'T', 'A'): + { + // direct draw of MetaFile + impRender_META(static_cast< const metafilePrimitive& >(rCandidate.getBasePrimitive())); + break; + } + + case CreatePrimitiveID('M', 'A', 'S', 'K'): + { + // mask group. Force output to VDev and create mask from given mask + impRender_MASK(static_cast< const maskPrimitive& >(rCandidate.getBasePrimitive())); + break; + } + + case CreatePrimitiveID('M', 'C', 'O', 'L'): + { + // modified color group. Force output to unified color. + impRender_MCOL(static_cast< const modifiedColorPrimitive& >(rCandidate.getBasePrimitive())); + break; + } + + case CreatePrimitiveID('T', 'R', 'P', 'R'): + { + // sub-transparence group. Draw to VDev first. + impRender_TRPR(static_cast< const transparencePrimitive& >(rCandidate.getBasePrimitive())); + break; + } + + case CreatePrimitiveID('T', 'R', 'N', '2'): + { + // transform group. + impRender_TRN2(static_cast< const transformPrimitive& >(rCandidate.getBasePrimitive())); + break; + } + + case CreatePrimitiveID('M', 'A', 'R', 'K'): + { + // marker + impRender_MARK(static_cast< const markerPrimitive& >(rCandidate.getBasePrimitive())); + break; + } + + case CreatePrimitiveID('A', 'N', 'S', 'W'): + case CreatePrimitiveID('A', 'N', 'B', 'L'): + case CreatePrimitiveID('A', 'N', 'I', 'N'): + { + // check timing, but do not accept + const animatedSwitchPrimitive& rAnimatedCandidate(static_cast< const animatedSwitchPrimitive& >(rCandidate.getBasePrimitive())); + const ::drawinglayer::animation::animationEntryList& rAnimationList = rAnimatedCandidate.getAnimationList(); + const double fNewTime(rAnimationList.getNextEventTime(getViewInformation().getViewTime())); + + // let break down + process(rAnimatedCandidate.getDecomposition(getViewInformation())); + + break; + } + + default: + { + // let break down + process(rCandidate.getBasePrimitive().getDecomposition(getViewInformation())); + } + } + + ++aCurr; + } + } + + canvasProcessor::canvasProcessor( const ::drawinglayer::geometry::viewInformation& rViewInformation, + const uno::Reference<rendering::XCanvas>& rCanvas ) : + processor(rViewInformation), + mxCanvas( rCanvas ), + mxCrossMarkerPoly(), + mxGluePointPoly(), + mxGluePointHighlightPoly(), + maBColorModifierStack(), + maWorldToView(), + maViewState(), + maRenderState(), + maMarkerViewState(), + maMarkerRenderState() + { + canvas::tools::initViewState(maViewState); + canvas::tools::initRenderState(maRenderState); + canvas::tools::initViewState(maMarkerViewState); + canvas::tools::initRenderState(maMarkerRenderState); + + maWorldToView = maViewInformation.getViewTransformation(); + + canvas::tools::setViewStateTransform(maViewState, + maWorldToView); + } + + canvasProcessor::~canvasProcessor() + {} +*/ +////////////////////////////////////////////////////////////////////////////// + +namespace drawinglayer +{ + namespace processor2d + { + ////////////////////////////////////////////////////////////////////////////// + // single primitive renderers + + void canvasProcessor2D::impRenderMaskPrimitive2D(const primitive2d::MaskPrimitive2D& rMaskCandidate) + { + const primitive2d::Primitive2DSequence& rChildren = rMaskCandidate.getChildren(); + static bool bUseMaskBitmapMethod(true); + + if(rChildren.hasElements()) + { + basegfx::B2DPolyPolygon aMask(rMaskCandidate.getMask()); + + if(!aMask.count()) + { + // no mask, no clipping. recursively paint content + process(rChildren); + } + else + { + // there are principally two methods for implementing the mask primitive. One + // is to set a clip polygon at the canvas, the other is to create and use a + // transparence-using XBitmap for content and draw the mask as transparence. Both have their + // advantages and disadvantages, so here are both with a bool allowing simple + // change + if(bUseMaskBitmapMethod) + { + // get logic range of transparent part, clip with ViewRange + basegfx::B2DRange aLogicRange(aMask.getB2DRange()); + + if(!getViewInformation2D().getViewport().isEmpty()) + { + aLogicRange.intersect(getViewInformation2D().getViewport()); + } + + if(!aLogicRange.isEmpty()) + { + // get discrete range of transparent part + basegfx::B2DRange aDiscreteRange(aLogicRange); + aDiscreteRange.transform(getViewInformation2D().getObjectToViewTransformation()); + + // expand to next covering discrete values (pixel bounds) + aDiscreteRange.expand(basegfx::B2DTuple(floor(aDiscreteRange.getMinX()), floor(aDiscreteRange.getMinY()))); + aDiscreteRange.expand(basegfx::B2DTuple(ceil(aDiscreteRange.getMaxX()), ceil(aDiscreteRange.getMaxY()))); + + // use VCL-based buffer device + impBufferDevice aBufferDevice(*mpOutputDevice, aDiscreteRange, false); + + if(aBufferDevice.isVisible()) + { + // remember current OutDev, Canvas and ViewInformation + OutputDevice* pLastOutputDevice = mpOutputDevice; + uno::Reference< rendering::XCanvas > xLastCanvas(mxCanvas); + const geometry::ViewInformation2D aLastViewInformation2D(getViewInformation2D()); + + // prepare discrete offset for XBitmap, do not forget that the buffer bitmap + // may be truncated to discrete visible pixels + const basegfx::B2DHomMatrix aDiscreteOffset(basegfx::tools::createTranslateB2DHomMatrix( + aDiscreteRange.getMinX() > 0.0 ? -aDiscreteRange.getMinX() : 0.0, + aDiscreteRange.getMinY() > 0.0 ? -aDiscreteRange.getMinY() : 0.0)); + + // create new local ViewInformation2D with new transformation + const geometry::ViewInformation2D aViewInformation2D( + getViewInformation2D().getObjectTransformation(), + aDiscreteOffset * getViewInformation2D().getViewTransformation(), + getViewInformation2D().getViewport(), + getViewInformation2D().getVisualizedPage(), + getViewInformation2D().getViewTime(), + getViewInformation2D().getExtendedInformationSequence()); + updateViewInformation(aViewInformation2D); + + // set OutDev and Canvas to content target + mpOutputDevice = &aBufferDevice.getContent(); + mxCanvas = mpOutputDevice->GetCanvas(); + canvas::tools::setViewStateTransform(maViewState, getViewInformation2D().getViewTransformation()); + + // if ViewState transform is changed, the clipping polygon needs to be adapted, too + const basegfx::B2DPolyPolygon aOldClipPolyPolygon(maClipPolyPolygon); + + if(maClipPolyPolygon.count()) + { + maClipPolyPolygon.transform(aDiscreteOffset); + maViewState.Clip = basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(mxCanvas->getDevice(), maClipPolyPolygon); + } + + // paint content + process(rChildren); + + // draw mask + const basegfx::BColor aBlack(0.0, 0.0, 0.0); + maRenderState.DeviceColor = aBlack.colorToDoubleSequence(mxCanvas->getDevice()); + + if(getOptionsDrawinglayer().IsAntiAliasing()) + { + // with AA, use 8bit AlphaMask to get nice borders + VirtualDevice& rTransparence = aBufferDevice.getTransparence(); + rTransparence.GetCanvas()->fillPolyPolygon( + basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(mxCanvas->getDevice(), aMask), + maViewState, maRenderState); + } + else + { + // No AA, use 1bit mask + VirtualDevice& rMask = aBufferDevice.getMask(); + rMask.GetCanvas()->fillPolyPolygon( + basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(mxCanvas->getDevice(), aMask), + maViewState, maRenderState); + } + + // back to old color stack, OutDev, Canvas and ViewTransform + mpOutputDevice = pLastOutputDevice; + mxCanvas = xLastCanvas; + updateViewInformation(aLastViewInformation2D); + canvas::tools::setViewStateTransform(maViewState, getViewInformation2D().getViewTransformation()); + + // restore clipping polygon + maClipPolyPolygon = aOldClipPolyPolygon; + + if(maClipPolyPolygon.count()) + { + maViewState.Clip = basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(mxCanvas->getDevice(), maClipPolyPolygon); + } + + // dump buffer to outdev + aBufferDevice.paint(); + } + } + } + else + { + // transform new mask polygon to view coordinates for processing. All masks + // are processed in view coordinates and clipped against each other evtl. to + // create multi-clips + aMask.transform(getViewInformation2D().getObjectTransformation()); + + // remember last current clip polygon + const basegfx::B2DPolyPolygon aLastClipPolyPolygon(maClipPolyPolygon); + + if(maClipPolyPolygon.count()) + { + // there is already a clip polygon set; build clipped union of + // current mask polygon and new one + maClipPolyPolygon = basegfx::tools::clipPolyPolygonOnPolyPolygon(aMask, maClipPolyPolygon, false, false); + } + else + { + // use mask directly + maClipPolyPolygon = aMask; + } + + // set at ViewState + if(maClipPolyPolygon.count()) + { + // set new as clip polygon + maViewState.Clip = basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(mxCanvas->getDevice(), maClipPolyPolygon); + } + else + { + // empty, reset + maViewState.Clip.clear(); + } + + // paint content + process(rChildren); + + // restore local current to rescued clip polygon + maClipPolyPolygon = aLastClipPolyPolygon; + + // set at ViewState + if(maClipPolyPolygon.count()) + { + // set new as clip polygon + maViewState.Clip = basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(mxCanvas->getDevice(), maClipPolyPolygon); + } + else + { + // empty, reset + maViewState.Clip.clear(); + } + } + } + } + } + + void canvasProcessor2D::impRenderMetafilePrimitive2D(const primitive2d::MetafilePrimitive2D& rMetaCandidate) + { + GDIMetaFile aMetaFile; + + if(maBColorModifierStack.count()) + { + const basegfx::BColor aRGBBaseColor(0, 0, 0); + const basegfx::BColor aRGBColor(maBColorModifierStack.getModifiedColor(aRGBBaseColor)); + aMetaFile = rMetaCandidate.getMetaFile().GetMonochromeMtf(Color(aRGBColor)); + } + else + { + aMetaFile = rMetaCandidate.getMetaFile(); + } + + cppcanvas::BitmapCanvasSharedPtr pCanvas(cppcanvas::VCLFactory::getInstance().createCanvas( + uno::Reference<rendering::XBitmapCanvas>(mxCanvas, uno::UNO_QUERY_THROW))); + cppcanvas::RendererSharedPtr pMtfRenderer(cppcanvas::VCLFactory::getInstance().createRenderer( + pCanvas, aMetaFile, cppcanvas::Renderer::Parameters())); + + if(pMtfRenderer) + { + pCanvas->setTransformation(getViewInformation2D().getObjectToViewTransformation()); + pMtfRenderer->setTransformation(rMetaCandidate.getTransform()); + pMtfRenderer->draw(); + } + } + + void canvasProcessor2D::impRenderTextSimplePortionPrimitive2D(const primitive2d::TextSimplePortionPrimitive2D& rTextCandidate) + { + if(rTextCandidate.getTextLength()) + { + double fShearX(0.0); + { + const basegfx::B2DHomMatrix aLocalTransform(getViewInformation2D().getObjectToViewTransformation() * rTextCandidate.getTextTransform()); + basegfx::B2DVector aScale, aTranslate; + double fRotate; + aLocalTransform.decompose(aScale, aTranslate, fRotate, fShearX); + } + + if(!basegfx::fTools::equalZero(fShearX)) + { + // text is sheared. As long as the canvas renderers do not support this, + // use the decomposed primitive + process(rTextCandidate.get2DDecomposition(getViewInformation2D())); + } + else + { + const attribute::FontAttribute& rFontAttr(rTextCandidate.getFontAttribute()); + rendering::FontRequest aFontRequest; + + aFontRequest.FontDescription.FamilyName = rFontAttr.getFamilyName(); + aFontRequest.FontDescription.StyleName = rFontAttr.getStyleName(); + aFontRequest.FontDescription.IsSymbolFont = rFontAttr.getSymbol() ? util::TriState_YES : util::TriState_NO; + aFontRequest.FontDescription.IsVertical = rFontAttr.getVertical() ? util::TriState_YES : util::TriState_NO; + // TODO(F2): improve vclenum->panose conversion + aFontRequest.FontDescription.FontDescription.Weight = static_cast< sal_uInt8 >(rFontAttr.getWeight()); + aFontRequest.FontDescription.FontDescription.Letterform = rFontAttr.getItalic() ? 9 : 0; + + // init CellSize to 1.0, else a default font height will be used + aFontRequest.CellSize = 1.0; + aFontRequest.Locale = rTextCandidate.getLocale(); + + // font matrix should only be used for glyph rotations etc. + com::sun::star::geometry::Matrix2D aFontMatrix; + canvas::tools::setIdentityMatrix2D(aFontMatrix); + + uno::Reference<rendering::XCanvasFont> xFont(mxCanvas->createFont( + aFontRequest, uno::Sequence< beans::PropertyValue >(), aFontMatrix)); + + if(xFont.is()) + { + // got a font, now try to get a TextLayout + const rendering::StringContext aStringContext( + rTextCandidate.getText(), rTextCandidate.getTextPosition(), rTextCandidate.getTextLength()); + uno::Reference<rendering::XTextLayout> xLayout(xFont->createTextLayout( + aStringContext, com::sun::star::rendering::TextDirection::WEAK_LEFT_TO_RIGHT, 0)); + + if(xLayout.is()) + { + // got a text layout, apply DXArray if given + const ::std::vector< double >& rDXArray = rTextCandidate.getDXArray(); + const sal_uInt32 nDXCount(rDXArray.size()); + + if(nDXCount) + { + // DXArray does not need to be adapted to getTextPosition/getTextLength, + // it is already provided correctly + const uno::Sequence< double > aDXSequence(&rDXArray[0], nDXCount); + xLayout->applyLogicalAdvancements(aDXSequence); + } + + // set text color + const basegfx::BColor aRGBColor(maBColorModifierStack.getModifiedColor(rTextCandidate.getFontColor())); + maRenderState.DeviceColor = aRGBColor.colorToDoubleSequence(mxCanvas->getDevice()); + + // set text transformation + canvas::tools::setRenderStateTransform(maRenderState, + getViewInformation2D().getObjectTransformation() * rTextCandidate.getTextTransform()); + + // paint + mxCanvas->drawTextLayout(xLayout, maViewState, maRenderState); + } + } + } + } + } + + void canvasProcessor2D::impRenderBitmapPrimitive2D(const primitive2d::BitmapPrimitive2D& rBitmapCandidate) + { + // apply possible color modification to BitmapEx + BitmapEx aModifiedBitmapEx(impModifyBitmapEx(maBColorModifierStack, rBitmapCandidate.getBitmapEx())); + + if(aModifiedBitmapEx.IsEmpty()) + { + // replace with color filled polygon + const basegfx::BColor aModifiedColor(maBColorModifierStack.getModifiedColor(basegfx::BColor())); + const basegfx::B2DPolygon aPolygon(basegfx::tools::createUnitPolygon()); + + maRenderState.DeviceColor = aModifiedColor.colorToDoubleSequence(mxCanvas->getDevice()); + canvas::tools::setRenderStateTransform(maRenderState, + getViewInformation2D().getObjectTransformation() * rBitmapCandidate.getTransform()); + + mxCanvas->fillPolyPolygon(basegfx::unotools::xPolyPolygonFromB2DPolyPolygon( + mxCanvas->getDevice(), basegfx::B2DPolyPolygon(aPolygon)), maViewState, maRenderState); + } + else + { + // adapt object's transformation to the correct scale + basegfx::B2DVector aScale, aTranslate; + double fRotate, fShearX; + const Size aSizePixel(aModifiedBitmapEx.GetSizePixel()); + + if(0 != aSizePixel.Width() && 0 != aSizePixel.Height()) + { + rBitmapCandidate.getTransform().decompose(aScale, aTranslate, fRotate, fShearX); + const basegfx::B2DHomMatrix aNewMatrix(basegfx::tools::createScaleShearXRotateTranslateB2DHomMatrix( + aScale.getX() / aSizePixel.Width(), aScale.getY() / aSizePixel.Height(), + fShearX, fRotate, aTranslate.getX(), aTranslate.getY())); + + canvas::tools::setRenderStateTransform(maRenderState, + getViewInformation2D().getObjectTransformation() * aNewMatrix); + + mxCanvas->drawBitmap( + vcl::unotools::xBitmapFromBitmapEx(mxCanvas->getDevice(), aModifiedBitmapEx), + maViewState, maRenderState); + } + } + } + + void canvasProcessor2D::impRenderTransparencePrimitive2D(const primitive2d::TransparencePrimitive2D& rTransparenceCandidate) + { + const primitive2d::Primitive2DSequence& rChildren = rTransparenceCandidate.getChildren(); + const primitive2d::Primitive2DSequence& rTransparence = rTransparenceCandidate.getTransparence(); + + if(rChildren.hasElements() && rTransparence.hasElements()) + { + // get logic range of transparent part and clip with ViewRange + basegfx::B2DRange aLogicRange(primitive2d::getB2DRangeFromPrimitive2DSequence(rChildren, getViewInformation2D())); + + if(!getViewInformation2D().getViewport().isEmpty()) + { + aLogicRange.intersect(getViewInformation2D().getViewport()); + } + + if(!aLogicRange.isEmpty()) + { + // get discrete range of transparent part + basegfx::B2DRange aDiscreteRange(aLogicRange); + aDiscreteRange.transform(getViewInformation2D().getObjectToViewTransformation()); + + // expand to next covering discrete values (pixel bounds) + aDiscreteRange.expand(basegfx::B2DTuple(floor(aDiscreteRange.getMinX()), floor(aDiscreteRange.getMinY()))); + aDiscreteRange.expand(basegfx::B2DTuple(ceil(aDiscreteRange.getMaxX()), ceil(aDiscreteRange.getMaxY()))); + + // use VCL-based buffer device + impBufferDevice aBufferDevice(*mpOutputDevice, aDiscreteRange, false); + + if(aBufferDevice.isVisible()) + { + // remember current OutDev, Canvas and ViewInformation + OutputDevice* pLastOutputDevice = mpOutputDevice; + uno::Reference< rendering::XCanvas > xLastCanvas(mxCanvas); + const geometry::ViewInformation2D aLastViewInformation2D(getViewInformation2D()); + + // prepare discrete offset for XBitmap, do not forget that the buffer bitmap + // may be truncated to discrete visible pixels + const basegfx::B2DHomMatrix aDiscreteOffset(basegfx::tools::createTranslateB2DHomMatrix( + aDiscreteRange.getMinX() > 0.0 ? -aDiscreteRange.getMinX() : 0.0, + aDiscreteRange.getMinY() > 0.0 ? -aDiscreteRange.getMinY() : 0.0)); + + // create new local ViewInformation2D with new transformation + const geometry::ViewInformation2D aViewInformation2D( + getViewInformation2D().getObjectTransformation(), + aDiscreteOffset * getViewInformation2D().getViewTransformation(), + getViewInformation2D().getViewport(), + getViewInformation2D().getVisualizedPage(), + getViewInformation2D().getViewTime(), + getViewInformation2D().getExtendedInformationSequence()); + updateViewInformation(aViewInformation2D); + + // set OutDev and Canvas to content target + mpOutputDevice = &aBufferDevice.getContent(); + mxCanvas = mpOutputDevice->GetCanvas(); + canvas::tools::setViewStateTransform(maViewState, getViewInformation2D().getViewTransformation()); + + // if ViewState transform is changed, the clipping polygon needs to be adapted, too + const basegfx::B2DPolyPolygon aOldClipPolyPolygon(maClipPolyPolygon); + + if(maClipPolyPolygon.count()) + { + maClipPolyPolygon.transform(aDiscreteOffset); + maViewState.Clip = basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(mxCanvas->getDevice(), maClipPolyPolygon); + } + + // paint content + process(rChildren); + + // set to mask + mpOutputDevice = &aBufferDevice.getTransparence(); + mxCanvas = mpOutputDevice->GetCanvas(); + canvas::tools::setViewStateTransform(maViewState, getViewInformation2D().getViewTransformation()); + + // when painting transparence masks, reset the color stack + basegfx::BColorModifierStack aLastBColorModifierStack(maBColorModifierStack); + maBColorModifierStack = basegfx::BColorModifierStack(); + + // paint mask to it (always with transparence intensities, evtl. with AA) + process(rTransparence); + + // back to old color stack, OutDev, Canvas and ViewTransform + maBColorModifierStack = aLastBColorModifierStack; + mpOutputDevice = pLastOutputDevice; + mxCanvas = xLastCanvas; + updateViewInformation(aLastViewInformation2D); + canvas::tools::setViewStateTransform(maViewState, getViewInformation2D().getViewTransformation()); + + // restore clipping polygon + maClipPolyPolygon = aOldClipPolyPolygon; + + if(maClipPolyPolygon.count()) + { + maViewState.Clip = basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(mxCanvas->getDevice(), maClipPolyPolygon); + } + + // dump buffer to outdev + aBufferDevice.paint(); + } + } + } + } + + void canvasProcessor2D::impRenderPolygonStrokePrimitive2D(const primitive2d::PolygonStrokePrimitive2D& rPolygonStrokePrimitive) + { + // support direct fat line geometry. This moves the decomposition to the canvas. + // As long as our canvases are used (which also use basegfx tooling) this makes + // no difference, but potentially canvases may better support this + static bool bSupportFatLineDirectly(true); + bool bOutputDone(false); + + if(bSupportFatLineDirectly) + { + const attribute::LineAttribute& rLineAttribute = rPolygonStrokePrimitive.getLineAttribute(); + const attribute::StrokeAttribute& rStrokeAttribute = rPolygonStrokePrimitive.getStrokeAttribute(); + + if(0.0 < rLineAttribute.getWidth() || 0 != rStrokeAttribute.getDotDashArray().size()) + { + rendering::StrokeAttributes aStrokeAttribute; + + aStrokeAttribute.StrokeWidth = rLineAttribute.getWidth(); + aStrokeAttribute.MiterLimit = 15.0; // degrees; maybe here (15.0 * F_PI180) is needed, not clear in the documentation + const ::std::vector< double >& rDotDashArray = rStrokeAttribute.getDotDashArray(); + + if(rDotDashArray.size()) + { + aStrokeAttribute.DashArray = uno::Sequence< double >(&rDotDashArray[0], rDotDashArray.size()); + } + + switch(rLineAttribute.getLineJoin()) + { + default: // B2DLINEJOIN_NONE, B2DLINEJOIN_MIDDLE + aStrokeAttribute.JoinType = rendering::PathJoinType::NONE; + break; + case basegfx::B2DLINEJOIN_BEVEL: + aStrokeAttribute.JoinType = rendering::PathJoinType::BEVEL; + break; + case basegfx::B2DLINEJOIN_MITER: + aStrokeAttribute.JoinType = rendering::PathJoinType::MITER; + break; + case basegfx::B2DLINEJOIN_ROUND: + aStrokeAttribute.JoinType = rendering::PathJoinType::ROUND; + break; + } + + const basegfx::BColor aHairlineColor(maBColorModifierStack.getModifiedColor(rLineAttribute.getColor())); + maRenderState.DeviceColor = aHairlineColor.colorToDoubleSequence(mxCanvas->getDevice()); + canvas::tools::setRenderStateTransform(maRenderState, getViewInformation2D().getObjectTransformation()); + + mxCanvas->strokePolyPolygon( + basegfx::unotools::xPolyPolygonFromB2DPolygon(mxCanvas->getDevice(), rPolygonStrokePrimitive.getB2DPolygon()), + maViewState, maRenderState, aStrokeAttribute); + + bOutputDone = true; + } + } + + if(!bOutputDone) + { + // process decomposition + process(rPolygonStrokePrimitive.get2DDecomposition(getViewInformation2D())); + } + } + + void canvasProcessor2D::impRenderFillBitmapPrimitive2D(const primitive2d::FillBitmapPrimitive2D& rFillBitmapPrimitive2D) + { + // support tiled fills directly when tiling is on + static bool bSupportFillBitmapDirectly(true); + bool bOutputDone(false); + + if(bSupportFillBitmapDirectly) + { + const attribute::FillBitmapAttribute& rFillBitmapAttribute = rFillBitmapPrimitive2D.getFillBitmap(); + + if(rFillBitmapAttribute.getTiling()) + { + // apply possible color modification to Bitmap + const BitmapEx aChangedBitmapEx(impModifyBitmapEx(maBColorModifierStack, rFillBitmapAttribute.getBitmapEx())); + + if(aChangedBitmapEx.IsEmpty()) + { + // replace with color filled polygon + const basegfx::BColor aModifiedColor(maBColorModifierStack.getModifiedColor(basegfx::BColor())); + const basegfx::B2DPolygon aPolygon(basegfx::tools::createUnitPolygon()); + + maRenderState.DeviceColor = aModifiedColor.colorToDoubleSequence(mxCanvas->getDevice()); + canvas::tools::setRenderStateTransform(maRenderState, + getViewInformation2D().getObjectTransformation() * rFillBitmapPrimitive2D.getTransformation()); + + mxCanvas->fillPolyPolygon(basegfx::unotools::xPolyPolygonFromB2DPolyPolygon( + mxCanvas->getDevice(), basegfx::B2DPolyPolygon(aPolygon)), maViewState, maRenderState); + } + else + { + const Size aSizePixel(aChangedBitmapEx.GetSizePixel()); + + if(0 != aSizePixel.Width() && 0 != aSizePixel.Height()) + { + // create texture matrix from texture to object (where object is unit square here), + // so use values directly + const basegfx::B2DHomMatrix aTextureMatrix(basegfx::tools::createScaleTranslateB2DHomMatrix( + rFillBitmapAttribute.getSize().getX(), rFillBitmapAttribute.getSize().getY(), + rFillBitmapAttribute.getTopLeft().getX(), rFillBitmapAttribute.getTopLeft().getY())); + + // create and fill texture + rendering::Texture aTexture; + + basegfx::unotools::affineMatrixFromHomMatrix(aTexture.AffineTransform, aTextureMatrix); + aTexture.Alpha = 1.0; + aTexture.Bitmap = vcl::unotools::xBitmapFromBitmapEx(mxCanvas->getDevice(), aChangedBitmapEx); + aTexture.RepeatModeX = rendering::TexturingMode::REPEAT; + aTexture.RepeatModeY = rendering::TexturingMode::REPEAT; + + // canvas needs a polygon to fill, create unit rectangle polygon + const basegfx::B2DPolygon aOutlineRectangle(basegfx::tools::createUnitPolygon()); + + // set primitive's transformation as render state transform + canvas::tools::setRenderStateTransform(maRenderState, + getViewInformation2D().getObjectTransformation() * rFillBitmapPrimitive2D.getTransformation()); + + // put texture into a uno sequence for handover + uno::Sequence< rendering::Texture > aSeq(1); + aSeq[0] = aTexture; + + // draw textured rectangle + mxCanvas->fillTexturedPolyPolygon( + basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(mxCanvas->getDevice(), basegfx::B2DPolyPolygon(aOutlineRectangle)), + maViewState, maRenderState, aSeq); + } + } + + bOutputDone = true; + } + } + + if(!bOutputDone) + { + // process decomposition + process(rFillBitmapPrimitive2D.get2DDecomposition(getViewInformation2D())); + } + } + + void canvasProcessor2D::impRenderUnifiedTransparencePrimitive2D(const primitive2d::UnifiedTransparencePrimitive2D& rUniTransparenceCandidate) + { + if(0.0 == rUniTransparenceCandidate.getTransparence()) + { + // not transparent at all, directly use content + process(rUniTransparenceCandidate.getChildren()); + } + else if(rUniTransparenceCandidate.getTransparence() > 0.0 && rUniTransparenceCandidate.getTransparence() < 1.0) + { + const primitive2d::Primitive2DSequence rChildren = rUniTransparenceCandidate.getChildren(); + + if(rChildren.hasElements()) + { + bool bOutputDone(false); + + // Detect if a single PolyPolygonColorPrimitive2D is contained; in that case, + // use the fillPolyPolygon method with correctly set transparence. This is a often used + // case, so detectiong it is valuable + if(1 == rChildren.getLength()) + { + const primitive2d::Primitive2DReference xReference(rChildren[0]); + const primitive2d::PolyPolygonColorPrimitive2D* pPoPoColor = dynamic_cast< const primitive2d::PolyPolygonColorPrimitive2D* >(xReference.get()); + + if(pPoPoColor && PRIMITIVE2D_ID_POLYPOLYGONCOLORPRIMITIVE2D == pPoPoColor->getPrimitive2DID()) + { + // direct draw of PolyPolygon with color and transparence + const basegfx::BColor aPolygonColor(maBColorModifierStack.getModifiedColor(pPoPoColor->getBColor())); + + // add transparence modulation value to DeviceColor + uno::Sequence< double > aColor(4); + + aColor[0] = aPolygonColor.getRed(); + aColor[1] = aPolygonColor.getGreen(); + aColor[2] = aPolygonColor.getBlue(); + aColor[3] = 1.0 - rUniTransparenceCandidate.getTransparence(); + maRenderState.DeviceColor = aColor; + + canvas::tools::setRenderStateTransform(maRenderState, getViewInformation2D().getObjectTransformation()); + mxCanvas->fillPolyPolygon( + basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(mxCanvas->getDevice(), pPoPoColor->getB2DPolyPolygon()), + maViewState, maRenderState); + bOutputDone = true; + } + } + + if(!bOutputDone) + { + // process decomposition. This will be decomposed to an TransparencePrimitive2D + // with the same child context and a single polygon for transparent context. This could be + // directly handled here with known VCL-buffer technology, but would only + // make a small difference compared to directly rendering the TransparencePrimitive2D + // using impRenderTransparencePrimitive2D above. + process(rUniTransparenceCandidate.get2DDecomposition(getViewInformation2D())); + } + } + } + } + + ////////////////////////////////////////////////////////////////////////////// + // internal processing support + + void canvasProcessor2D::processBasePrimitive2D(const primitive2d::BasePrimitive2D& rCandidate) + { + switch(rCandidate.getPrimitive2DID()) + { + case PRIMITIVE2D_ID_POLYGONHAIRLINEPRIMITIVE2D : + { + // direct draw of hairline + const primitive2d::PolygonHairlinePrimitive2D& rPolygonCandidate = static_cast< const primitive2d::PolygonHairlinePrimitive2D& >(rCandidate); + const basegfx::BColor aHairlineColor(maBColorModifierStack.getModifiedColor(rPolygonCandidate.getBColor())); + + maRenderState.DeviceColor = aHairlineColor.colorToDoubleSequence(mxCanvas->getDevice()); + canvas::tools::setRenderStateTransform(maRenderState, getViewInformation2D().getObjectTransformation()); + mxCanvas->drawPolyPolygon( + basegfx::unotools::xPolyPolygonFromB2DPolygon(mxCanvas->getDevice(), rPolygonCandidate.getB2DPolygon()), + maViewState, maRenderState); + + break; + } + case PRIMITIVE2D_ID_POLYPOLYGONCOLORPRIMITIVE2D : + { + // direct draw of PolyPolygon with color + const primitive2d::PolyPolygonColorPrimitive2D& rPolygonCandidate = static_cast< const primitive2d::PolyPolygonColorPrimitive2D& >(rCandidate); + const basegfx::BColor aPolygonColor(maBColorModifierStack.getModifiedColor(rPolygonCandidate.getBColor())); + + maRenderState.DeviceColor = aPolygonColor.colorToDoubleSequence(mxCanvas->getDevice()); + canvas::tools::setRenderStateTransform(maRenderState, getViewInformation2D().getObjectTransformation()); + mxCanvas->fillPolyPolygon( + basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(mxCanvas->getDevice(), rPolygonCandidate.getB2DPolyPolygon()), + maViewState, maRenderState); + + break; + } + case PRIMITIVE2D_ID_MODIFIEDCOLORPRIMITIVE2D : + { + // modified color group. Force output to unified color. + const primitive2d::ModifiedColorPrimitive2D& rModifiedCandidate = static_cast< const primitive2d::ModifiedColorPrimitive2D& >(rCandidate); + + if(rModifiedCandidate.getChildren().hasElements()) + { + maBColorModifierStack.push(rModifiedCandidate.getColorModifier()); + process(rModifiedCandidate.getChildren()); + maBColorModifierStack.pop(); + } + + break; + } + case PRIMITIVE2D_ID_MASKPRIMITIVE2D : + { + // mask group + impRenderMaskPrimitive2D(static_cast< const primitive2d::MaskPrimitive2D& >(rCandidate)); + + break; + } + case PRIMITIVE2D_ID_TRANSFORMPRIMITIVE2D : + { + // transform group. Remember current ViewInformation2D + const primitive2d::TransformPrimitive2D& rTransformCandidate = static_cast< const primitive2d::TransformPrimitive2D& >(rCandidate); + const geometry::ViewInformation2D aLastViewInformation2D(getViewInformation2D()); + + // create new local ViewInformation2D with new transformation + const geometry::ViewInformation2D aViewInformation2D( + getViewInformation2D().getObjectTransformation() * rTransformCandidate.getTransformation(), + getViewInformation2D().getViewTransformation(), + getViewInformation2D().getViewport(), + getViewInformation2D().getVisualizedPage(), + getViewInformation2D().getViewTime(), + getViewInformation2D().getExtendedInformationSequence()); + updateViewInformation(aViewInformation2D); + + // set at canvas + canvas::tools::setRenderStateTransform(maRenderState, getViewInformation2D().getObjectTransformation()); + + // proccess content + process(rTransformCandidate.getChildren()); + + // restore transformations + updateViewInformation(aLastViewInformation2D); + + // restore at canvas + canvas::tools::setRenderStateTransform(maRenderState, getViewInformation2D().getObjectTransformation()); + + break; + } + case PRIMITIVE2D_ID_PAGEPREVIEWPRIMITIVE2D : + { + // new XDrawPage for ViewInformation2D + const primitive2d::PagePreviewPrimitive2D& rPagePreviewCandidate = static_cast< const primitive2d::PagePreviewPrimitive2D& >(rCandidate); + + // remember current transformation and ViewInformation + const geometry::ViewInformation2D aLastViewInformation2D(getViewInformation2D()); + + // create new local ViewInformation2D + const geometry::ViewInformation2D aViewInformation2D( + getViewInformation2D().getObjectTransformation(), + getViewInformation2D().getViewTransformation(), + getViewInformation2D().getViewport(), + rPagePreviewCandidate.getXDrawPage(), + getViewInformation2D().getViewTime(), + getViewInformation2D().getExtendedInformationSequence()); + updateViewInformation(aViewInformation2D); + + // proccess decomposed content + process(rPagePreviewCandidate.get2DDecomposition(getViewInformation2D())); + + // restore transformations + updateViewInformation(aLastViewInformation2D); + break; + } + case PRIMITIVE2D_ID_METAFILEPRIMITIVE2D : + { + // MetaFile primitive + impRenderMetafilePrimitive2D(static_cast< const primitive2d::MetafilePrimitive2D& >(rCandidate)); + + break; + } + case PRIMITIVE2D_ID_POINTARRAYPRIMITIVE2D : + { + // PointArray primitive + const primitive2d::PointArrayPrimitive2D& rPointArrayCandidate = static_cast< const primitive2d::PointArrayPrimitive2D& >(rCandidate); + + // set point color + const basegfx::BColor aRGBColor(maBColorModifierStack.getModifiedColor(rPointArrayCandidate.getRGBColor())); + maRenderState.DeviceColor = aRGBColor.colorToDoubleSequence(mxCanvas->getDevice()); + canvas::tools::setRenderStateTransform(maRenderState, getViewInformation2D().getObjectTransformation()); + + const std::vector< basegfx::B2DPoint >& rPointVector = rPointArrayCandidate.getPositions(); + const sal_uInt32 nPointCount(rPointVector.size()); + + for(sal_uInt32 a(0); a < nPointCount; a++) + { + const basegfx::B2DPoint& rPoint = rPointVector[a]; + mxCanvas->drawPoint(basegfx::unotools::point2DFromB2DPoint(rPoint), maViewState, maRenderState); + } + + break; + } + case PRIMITIVE2D_ID_TEXTSIMPLEPORTIONPRIMITIVE2D : + { + // TextSimplePortion primitive + impRenderTextSimplePortionPrimitive2D(static_cast< const primitive2d::TextSimplePortionPrimitive2D& >(rCandidate)); + + break; + } + case PRIMITIVE2D_ID_BITMAPPRIMITIVE2D : + { + // Bitmap primitive + impRenderBitmapPrimitive2D(static_cast< const primitive2d::BitmapPrimitive2D& >(rCandidate)); + + break; + } + case PRIMITIVE2D_ID_TRANSPARENCEPRIMITIVE2D : + { + // Transparence primitive + impRenderTransparencePrimitive2D(static_cast< const primitive2d::TransparencePrimitive2D& >(rCandidate)); + + break; + } + case PRIMITIVE2D_ID_POLYGONSTROKEPRIMITIVE2D: + { + // PolygonStrokePrimitive + impRenderPolygonStrokePrimitive2D(static_cast< const primitive2d::PolygonStrokePrimitive2D& >(rCandidate)); + + break; + } + case PRIMITIVE2D_ID_FILLBITMAPPRIMITIVE2D : + { + // FillBitmapPrimitive2D + impRenderFillBitmapPrimitive2D(static_cast< const primitive2d::FillBitmapPrimitive2D& >(rCandidate)); + + break; + } + case PRIMITIVE2D_ID_UNIFIEDTRANSPARENCEPRIMITIVE2D : + { + // UnifiedTransparencePrimitive2D + impRenderUnifiedTransparencePrimitive2D(static_cast< const primitive2d::UnifiedTransparencePrimitive2D& >(rCandidate)); + + break; + } + case PRIMITIVE2D_ID_CHARTPRIMITIVE2D : + { + // chart primitive in canvas renderer; restore original DrawMode during call + // since the evtl. used ChartPrettyPainter will use the MapMode + const primitive2d::ChartPrimitive2D& rChartPrimitive = static_cast< const primitive2d::ChartPrimitive2D& >(rCandidate); + mpOutputDevice->Push(PUSH_MAPMODE); + mpOutputDevice->SetMapMode(maOriginalMapMode); + + if(!renderChartPrimitive2D( + rChartPrimitive, + *mpOutputDevice, + getViewInformation2D())) + { + // fallback to decomposition (MetaFile) + process(rChartPrimitive.get2DDecomposition(getViewInformation2D())); + } + + mpOutputDevice->Pop(); + break; + } + case PRIMITIVE2D_ID_WRONGSPELLPRIMITIVE2D : + { + // wrong spell primitive. Handled directly here using VCL since VCL has a nice and + // very direct waveline painting which is needed for this. If VCL is to be avoided, + // this can be removed anytime and the decomposition may be used + const primitive2d::WrongSpellPrimitive2D& rWrongSpellPrimitive = static_cast< const primitive2d::WrongSpellPrimitive2D& >(rCandidate); + + if(!renderWrongSpellPrimitive2D( + rWrongSpellPrimitive, + *mpOutputDevice, + getViewInformation2D().getObjectToViewTransformation(), + maBColorModifierStack)) + { + // fallback to decomposition (MetaFile) + process(rWrongSpellPrimitive.get2DDecomposition(getViewInformation2D())); + } + + break; + } + + // nice to have: + // + // case PRIMITIVE2D_ID_CONTROLPRIMITIVE2D : + // - support FormControls more direct eventually, not sure if this is needed + // with the canvas renderer. The decomposition provides a bitmap representation + // of the control which will work + // + + default : + { + // process recursively + process(rCandidate.get2DDecomposition(getViewInformation2D())); + + break; + } + } + } + + ////////////////////////////////////////////////////////////////////////////// + // process support + + canvasProcessor2D::canvasProcessor2D( + const geometry::ViewInformation2D& rViewInformation, + OutputDevice& rOutDev) + : BaseProcessor2D(rViewInformation), + maOriginalMapMode(rOutDev.GetMapMode()), + mpOutputDevice(&rOutDev), + mxCanvas(rOutDev.GetCanvas()), + maViewState(), + maRenderState(), + maBColorModifierStack(), + maDrawinglayerOpt(), + maClipPolyPolygon(), + meLang(LANGUAGE_SYSTEM) + { + const SvtCTLOptions aSvtCTLOptions; + + canvas::tools::initViewState(maViewState); + canvas::tools::initRenderState(maRenderState); + canvas::tools::setViewStateTransform(maViewState, getViewInformation2D().getViewTransformation()); + + // set digit language, derived from SvtCTLOptions to have the correct + // number display for arabic/hindi numerals + if(SvtCTLOptions::NUMERALS_HINDI == aSvtCTLOptions.GetCTLTextNumerals()) + { + meLang = LANGUAGE_ARABIC_SAUDI_ARABIA; + } + else if(SvtCTLOptions::NUMERALS_ARABIC == aSvtCTLOptions.GetCTLTextNumerals()) + { + meLang = LANGUAGE_ENGLISH; + } + else + { + meLang = (LanguageType)Application::GetSettings().GetLanguage(); + } + + rOutDev.SetDigitLanguage(meLang); + + // prepare output directly to pixels + mpOutputDevice->Push(PUSH_MAPMODE); + mpOutputDevice->SetMapMode(); + + // react on AntiAliasing settings + if(getOptionsDrawinglayer().IsAntiAliasing()) + { + mpOutputDevice->SetAntialiasing(mpOutputDevice->GetAntialiasing() | ANTIALIASING_ENABLE_B2DDRAW); + } + else + { + mpOutputDevice->SetAntialiasing(mpOutputDevice->GetAntialiasing() & ~ANTIALIASING_ENABLE_B2DDRAW); + } + } + + canvasProcessor2D::~canvasProcessor2D() + { + // restore MapMode + mpOutputDevice->Pop(); + + // restore AntiAliasing + mpOutputDevice->SetAntialiasing(mpOutputDevice->GetAntialiasing() & ~ANTIALIASING_ENABLE_B2DDRAW); + } + } // end of namespace processor2d +} // end of namespace drawinglayer + +////////////////////////////////////////////////////////////////////////////// +// eof diff --git a/drawinglayer/source/processor2d/contourextractor2d.cxx b/drawinglayer/source/processor2d/contourextractor2d.cxx new file mode 100644 index 000000000000..d87b13551e1a --- /dev/null +++ b/drawinglayer/source/processor2d/contourextractor2d.cxx @@ -0,0 +1,203 @@ +/************************************************************************* + * + * 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_drawinglayer.hxx" + +#include <drawinglayer/processor2d/contourextractor2d.hxx> +#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx> +#include <drawinglayer/primitive2d/polygonprimitive2d.hxx> +#include <drawinglayer/primitive2d/polypolygonprimitive2d.hxx> +#include <drawinglayer/primitive2d/bitmapprimitive2d.hxx> +#include <basegfx/polygon/b2dpolygontools.hxx> +#include <drawinglayer/primitive2d/metafileprimitive2d.hxx> +#include <drawinglayer/primitive2d/transparenceprimitive2d.hxx> +#include <drawinglayer/primitive2d/maskprimitive2d.hxx> +#include <drawinglayer/primitive2d/transformprimitive2d.hxx> +#include <drawinglayer/primitive2d/sceneprimitive2d.hxx> + +////////////////////////////////////////////////////////////////////////////// + +using namespace com::sun::star; + +////////////////////////////////////////////////////////////////////////////// + +namespace drawinglayer +{ + namespace processor2d + { + ContourExtractor2D::ContourExtractor2D(const geometry::ViewInformation2D& rViewInformation) + : BaseProcessor2D(rViewInformation), + maExtractedContour() + { + } + + ContourExtractor2D::~ContourExtractor2D() + { + } + + void ContourExtractor2D::processBasePrimitive2D(const primitive2d::BasePrimitive2D& rCandidate) + { + switch(rCandidate.getPrimitive2DID()) + { + case PRIMITIVE2D_ID_POLYGONHAIRLINEPRIMITIVE2D : + { + // extract hairline in world coordinates + const primitive2d::PolygonHairlinePrimitive2D& rPolygonCandidate(static_cast< const primitive2d::PolygonHairlinePrimitive2D& >(rCandidate)); + basegfx::B2DPolygon aLocalPolygon(rPolygonCandidate.getB2DPolygon()); + aLocalPolygon.transform(getViewInformation2D().getObjectTransformation()); + + if(aLocalPolygon.isClosed()) + { + // line polygons need to be represented as open polygons to differentiate them + // from filled polygons + basegfx::tools::openWithGeometryChange(aLocalPolygon); + } + + maExtractedContour.push_back(basegfx::B2DPolyPolygon(aLocalPolygon)); + break; + } + case PRIMITIVE2D_ID_POLYPOLYGONCOLORPRIMITIVE2D : + { + // extract fill in world coordinates + const primitive2d::PolyPolygonColorPrimitive2D& rPolygonCandidate(static_cast< const primitive2d::PolyPolygonColorPrimitive2D& >(rCandidate)); + basegfx::B2DPolyPolygon aLocalPolyPolygon(rPolygonCandidate.getB2DPolyPolygon()); + aLocalPolyPolygon.transform(getViewInformation2D().getObjectTransformation()); + maExtractedContour.push_back(aLocalPolyPolygon); + break; + } + case PRIMITIVE2D_ID_BITMAPPRIMITIVE2D : + { + // extract BoundRect from bitmaps in world coordinates + const primitive2d::BitmapPrimitive2D& rBitmapCandidate(static_cast< const primitive2d::BitmapPrimitive2D& >(rCandidate)); + basegfx::B2DHomMatrix aLocalTransform(getViewInformation2D().getObjectTransformation() * rBitmapCandidate.getTransform()); + basegfx::B2DPolygon aPolygon(basegfx::tools::createUnitPolygon()); + aPolygon.transform(aLocalTransform); + maExtractedContour.push_back(basegfx::B2DPolyPolygon(aPolygon)); + break; + } + case PRIMITIVE2D_ID_METAFILEPRIMITIVE2D : + { + // extract BoundRect from MetaFiles in world coordinates + const primitive2d::MetafilePrimitive2D& rMetaCandidate(static_cast< const primitive2d::MetafilePrimitive2D& >(rCandidate)); + basegfx::B2DHomMatrix aLocalTransform(getViewInformation2D().getObjectTransformation() * rMetaCandidate.getTransform()); + basegfx::B2DPolygon aPolygon(basegfx::tools::createUnitPolygon()); + aPolygon.transform(aLocalTransform); + maExtractedContour.push_back(basegfx::B2DPolyPolygon(aPolygon)); + break; + } + case PRIMITIVE2D_ID_TRANSPARENCEPRIMITIVE2D : + { + // sub-transparence group. Look at children + const primitive2d::TransparencePrimitive2D& rTransCandidate(static_cast< const primitive2d::TransparencePrimitive2D& >(rCandidate)); + process(rTransCandidate.getChildren()); + break; + } + case PRIMITIVE2D_ID_MASKPRIMITIVE2D : + { + // extract mask in world coordinates, ignore content + const primitive2d::MaskPrimitive2D& rMaskCandidate(static_cast< const primitive2d::MaskPrimitive2D& >(rCandidate)); + basegfx::B2DPolyPolygon aMask(rMaskCandidate.getMask()); + aMask.transform(getViewInformation2D().getObjectTransformation()); + maExtractedContour.push_back(basegfx::B2DPolyPolygon(aMask)); + break; + } + case PRIMITIVE2D_ID_TRANSFORMPRIMITIVE2D : + { + // remember current ViewInformation2D + const primitive2d::TransformPrimitive2D& rTransformCandidate(static_cast< const primitive2d::TransformPrimitive2D& >(rCandidate)); + const geometry::ViewInformation2D aLastViewInformation2D(getViewInformation2D()); + + // create new local ViewInformation2D + const geometry::ViewInformation2D aViewInformation2D( + getViewInformation2D().getObjectTransformation() * rTransformCandidate.getTransformation(), + getViewInformation2D().getViewTransformation(), + getViewInformation2D().getViewport(), + getViewInformation2D().getVisualizedPage(), + getViewInformation2D().getViewTime(), + getViewInformation2D().getExtendedInformationSequence()); + updateViewInformation(aViewInformation2D); + + // proccess content + process(rTransformCandidate.getChildren()); + + // restore transformations + updateViewInformation(aLastViewInformation2D); + + break; + } + case PRIMITIVE2D_ID_SCENEPRIMITIVE2D : + { + // 2D Scene primitive containing 3D stuff; extract 2D contour in world coordinates + const primitive2d::ScenePrimitive2D& rScenePrimitive2DCandidate(static_cast< const primitive2d::ScenePrimitive2D& >(rCandidate)); + const primitive2d::Primitive2DSequence xExtracted2DSceneGeometry(rScenePrimitive2DCandidate.getGeometry2D()); + const primitive2d::Primitive2DSequence xExtracted2DSceneShadow(rScenePrimitive2DCandidate.getShadow2D(getViewInformation2D())); + + // proccess content + if(xExtracted2DSceneGeometry.hasElements()) + { + process(xExtracted2DSceneGeometry); + } + + // proccess content + if(xExtracted2DSceneShadow.hasElements()) + { + process(xExtracted2DSceneShadow); + } + + break; + } + case PRIMITIVE2D_ID_WRONGSPELLPRIMITIVE2D : + case PRIMITIVE2D_ID_MARKERARRAYPRIMITIVE2D : + case PRIMITIVE2D_ID_POINTARRAYPRIMITIVE2D : + { + // ignorable primitives + break; + } + case PRIMITIVE2D_ID_TEXTSIMPLEPORTIONPRIMITIVE2D : + case PRIMITIVE2D_ID_TEXTDECORATEDPORTIONPRIMITIVE2D : + { + // primitives who's BoundRect will be added in world coordinates + basegfx::B2DRange aRange(rCandidate.getB2DRange(getViewInformation2D())); + aRange.transform(getViewInformation2D().getObjectTransformation()); + maExtractedContour.push_back(basegfx::B2DPolyPolygon(basegfx::tools::createPolygonFromRect(aRange))); + break; + } + default : + { + // process recursively + process(rCandidate.get2DDecomposition(getViewInformation2D())); + break; + } + } + } + + } // end of namespace processor2d +} // end of namespace drawinglayer + +////////////////////////////////////////////////////////////////////////////// +// eof diff --git a/drawinglayer/source/processor2d/helperchartrenderer.cxx b/drawinglayer/source/processor2d/helperchartrenderer.cxx new file mode 100644 index 000000000000..43af1f9f2f85 --- /dev/null +++ b/drawinglayer/source/processor2d/helperchartrenderer.cxx @@ -0,0 +1,155 @@ +/************************************************************************* + * + * 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_drawinglayer.hxx" + +#include <helperchartrenderer.hxx> +#include <drawinglayer/primitive2d/chartprimitive2d.hxx> +#include <svtools/chartprettypainter.hxx> +#include <com/sun/star/lang/XUnoTunnel.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <drawinglayer/geometry/viewinformation2d.hxx> + +////////////////////////////////////////////////////////////////////////////// + +using namespace com::sun::star; + +////////////////////////////////////////////////////////////////////////////// + +namespace drawinglayer +{ + bool renderChartPrimitive2D( + const primitive2d::ChartPrimitive2D& rChartCandidate, + OutputDevice& rOutputDevice, + const geometry::ViewInformation2D& rViewInformation2D) + { + bool bChartRendered(false); + + // code from chart PrettyPrinter + try + { + uno::Reference< lang::XMultiServiceFactory > xFact( rChartCandidate.getChartModel(), uno::UNO_QUERY ); + OSL_ENSURE( xFact.is(), "Chart cannot be painted pretty!\n" ); + + if( xFact.is() ) + { + uno::Reference< lang::XUnoTunnel > xChartRenderer( xFact->createInstance( + ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.comp.chart2.ChartRenderer" ) ) ), uno::UNO_QUERY ); + OSL_ENSURE( xChartRenderer.is(), "Chart cannot be painted pretty!\n" ); + + if( xChartRenderer.is() ) + { + ChartPrettyPainter* pPrettyPainter = reinterpret_cast<ChartPrettyPainter*>( + xChartRenderer->getSomething( ChartPrettyPainter::getUnoTunnelId() )); + + if( pPrettyPainter ) + { + // create logic object range; do NOT use ObjectTransformation for this + // (rViewInformation2D.getObjectTransformation()), only the logic object + // size is wanted + basegfx::B2DRange aObjectRange(0.0, 0.0, 1.0, 1.0); + aObjectRange.transform(rChartCandidate.getTransformation()); + const Rectangle aRectangle( + (sal_Int32)aObjectRange.getMinX(), (sal_Int32)aObjectRange.getMinY(), + (sal_Int32)aObjectRange.getMaxX(), (sal_Int32)aObjectRange.getMaxY()); + + // #i101811# + if(rViewInformation2D.getObjectTransformation().isIdentity()) + { + // no embedding in another transfromation, just paint with existing + // MapMode. This is just a shortcut; using the below code will also + // work; it has just a neutral ObjectTransformation + bChartRendered = pPrettyPainter->DoPaint(&rOutputDevice, aRectangle); + } + else + { + // rViewInformation2D.getObjectTransformation() is used and + // needs to be expressed in the MapMode for the PrettyPainter; + // else it would call ChartModelHelper::setPageSize(...) with the + // changed size what really will change the chart model and leads + // to re-layouts and re-formattings + const MapMode aOldMapMode(rOutputDevice.GetMapMode()); + basegfx::B2DVector aVTScale, aScale, aTranslate; + double fRotate, fShearX; + + // get basic scaling with current MapMode (aVTScale), containing + // mapping for set MapUnit (e.g. for 100th mm, the basic scale is + // not 1.0, 1.0). This is needed since this scale is included in + // the ObjectToView Transformation and needs to be removed (see + // correction below) to re-create a MapMode + rOutputDevice.SetMapMode(aOldMapMode.GetMapUnit()); + rOutputDevice.GetViewTransformation().decompose(aVTScale, aTranslate, fRotate, fShearX); + + // get complete ObjectToView Transformation scale and translate from current + // transformation chain (combined view and object transform) + rViewInformation2D.getObjectToViewTransformation().decompose(aScale, aTranslate, fRotate, fShearX); + + // assert when shear and/or rotation is used + OSL_ENSURE(basegfx::fTools::equalZero(fRotate), "Chart PrettyPrinting with unsupportable rotation (!)"); + OSL_ENSURE(basegfx::fTools::equalZero(fShearX), "Chart PrettyPrinting with unsupportable shear (!)"); + + // clean scale and translate from basic scaling (DPI, etc...) + // since this will implicitely be part of the to-be-created MapMode + const basegfx::B2DTuple aBasicCleaner( + basegfx::fTools::equalZero(aVTScale.getX()) ? 1.0 : 1.0 / aVTScale.getX(), + basegfx::fTools::equalZero(aVTScale.getY()) ? 1.0 : 1.0 / aVTScale.getY()); + aScale *= aBasicCleaner; + aTranslate *= aBasicCleaner; + + // for MapMode, take scale out of translation + const basegfx::B2DTuple aScaleRemover( + basegfx::fTools::equalZero(aScale.getX()) ? 1.0 : 1.0 / aScale.getX(), + basegfx::fTools::equalZero(aScale.getY()) ? 1.0 : 1.0 / aScale.getY()); + aTranslate *= aScaleRemover; + + // build new MapMode + const MapMode aNewMapMode(aOldMapMode.GetMapUnit(), + Point(basegfx::fround(aTranslate.getX()), basegfx::fround(aTranslate.getY())), + Fraction(aScale.getX()), Fraction(aScale.getY())); + + // use, paint, restore + rOutputDevice.SetMapMode(aNewMapMode); + bChartRendered = pPrettyPainter->DoPaint(&rOutputDevice, aRectangle); + rOutputDevice.SetMapMode(aOldMapMode); + } + } + } + } + } + catch( uno::Exception& e ) + { + (void)e; + DBG_ERROR( "Chart cannot be painted pretty!" ); + } + + return bChartRendered; + } +} // end of namespace drawinglayer + +////////////////////////////////////////////////////////////////////////////// +// eof diff --git a/drawinglayer/source/processor2d/helperchartrenderer.hxx b/drawinglayer/source/processor2d/helperchartrenderer.hxx new file mode 100644 index 000000000000..b93eeebaee4e --- /dev/null +++ b/drawinglayer/source/processor2d/helperchartrenderer.hxx @@ -0,0 +1,60 @@ +/************************************************************************* + * + * 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. + * + ************************************************************************/ + +#ifndef INCLUDED_DRAWINGLAYER_PROCESSOR2D_HELPERCHARTRENDER_HXX +#define INCLUDED_DRAWINGLAYER_PROCESSOR2D_HELPERCHARTRENDER_HXX + +#include <sal/types.h> + +////////////////////////////////////////////////////////////////////////////// +// predefines + +class OutputDevice; + +namespace drawinglayer { namespace primitive2d { class ChartPrimitive2D; }} +namespace drawinglayer { namespace geometry { class ViewInformation2D; }} + +////////////////////////////////////////////////////////////////////////////// +// support chart PrettyPrinter usage from primitives + +namespace drawinglayer +{ + // #i101811# + // Added current ViewInformation2D to take evtl. changed + // ObjectTransformation into account + bool renderChartPrimitive2D( + const primitive2d::ChartPrimitive2D& rChartCandidate, + OutputDevice& rOutputDevice, + const geometry::ViewInformation2D& rViewInformation2D); + +} // end of namespace drawinglayer + +////////////////////////////////////////////////////////////////////////////// + +#endif // INCLUDED_DRAWINGLAYER_PROCESSOR2D_HELPERCHARTRENDER_HXX + +// eof diff --git a/drawinglayer/source/processor2d/helperwrongspellrenderer.cxx b/drawinglayer/source/processor2d/helperwrongspellrenderer.cxx new file mode 100644 index 000000000000..a678a3fa12b9 --- /dev/null +++ b/drawinglayer/source/processor2d/helperwrongspellrenderer.cxx @@ -0,0 +1,96 @@ +/************************************************************************* + * + * 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_drawinglayer.hxx" + +#include <helperwrongspellrenderer.hxx> +#include <drawinglayer/primitive2d/wrongspellprimitive2d.hxx> +#include <tools/gen.hxx> +#include <vcl/outdev.hxx> +#include <basegfx/color/bcolormodifier.hxx> + +////////////////////////////////////////////////////////////////////////////// + +using namespace com::sun::star; + +////////////////////////////////////////////////////////////////////////////// + +namespace drawinglayer +{ + bool renderWrongSpellPrimitive2D( + const primitive2d::WrongSpellPrimitive2D& rWrongSpellCandidate, + OutputDevice& rOutputDevice, + const basegfx::B2DHomMatrix& rObjectToViewTransformation, + const basegfx::BColorModifierStack& rBColorModifierStack) + { + const basegfx::B2DHomMatrix aLocalTransform(rObjectToViewTransformation * rWrongSpellCandidate.getTransformation()); + const basegfx::B2DVector aFontVectorPixel(aLocalTransform * basegfx::B2DVector(0.0, 1.0)); + const sal_uInt32 nFontPixelHeight(basegfx::fround(aFontVectorPixel.getLength())); + + static const sal_uInt32 nMinimumFontHeight(5); // #define WRONG_SHOW_MIN 5 + static const sal_uInt32 nSmallFontHeight(11); // #define WRONG_SHOW_SMALL 11 + static const sal_uInt32 nMediumFontHeight(15); // #define WRONG_SHOW_MEDIUM 15 + + if(nFontPixelHeight > nMinimumFontHeight) + { + const basegfx::B2DPoint aStart(aLocalTransform * basegfx::B2DPoint(rWrongSpellCandidate.getStart(), 0.0)); + const basegfx::B2DPoint aStop(aLocalTransform * basegfx::B2DPoint(rWrongSpellCandidate.getStop(), 0.0)); + const Point aVclStart(basegfx::fround(aStart.getX()), basegfx::fround(aStart.getY())); + const Point aVclStop(basegfx::fround(aStop.getX()), basegfx::fround(aStop.getY())); + sal_uInt16 nWaveStyle(WAVE_FLAT); + + if(nFontPixelHeight > nMediumFontHeight) + { + nWaveStyle = WAVE_NORMAL; + } + else if(nFontPixelHeight > nSmallFontHeight) + { + nWaveStyle = WAVE_SMALL; + } + + // #i101075# draw it. Do not forget to use the evtl. offsetted origin of the target device, + // e.g. when used with mask/transparence buffer device + const Point aOrigin(rOutputDevice.GetMapMode().GetOrigin()); + + const basegfx::BColor aProcessedColor(rBColorModifierStack.getModifiedColor(rWrongSpellCandidate.getColor())); + const bool bMapModeEnabledState(rOutputDevice.IsMapModeEnabled()); + + rOutputDevice.EnableMapMode(false); + rOutputDevice.SetLineColor(Color(aProcessedColor)); + rOutputDevice.SetFillColor(); + rOutputDevice.DrawWaveLine(aOrigin + aVclStart, aOrigin + aVclStop, nWaveStyle); + rOutputDevice.EnableMapMode(bMapModeEnabledState); + } + + // cannot really go wrong + return true; + } +} // end of namespace drawinglayer + +////////////////////////////////////////////////////////////////////////////// +// eof diff --git a/drawinglayer/source/processor2d/helperwrongspellrenderer.hxx b/drawinglayer/source/processor2d/helperwrongspellrenderer.hxx new file mode 100644 index 000000000000..1f669775b866 --- /dev/null +++ b/drawinglayer/source/processor2d/helperwrongspellrenderer.hxx @@ -0,0 +1,65 @@ +/************************************************************************* + * + * 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. + * + ************************************************************************/ + +#ifndef INCLUDED_DRAWINGLAYER_PROCESSOR2D_HELPERWRONGSPELLRENDER_HXX +#define INCLUDED_DRAWINGLAYER_PROCESSOR2D_HELPERWRONGSPELLRENDER_HXX + +#include <sal/types.h> + +////////////////////////////////////////////////////////////////////////////// +// predefines + +class OutputDevice; + +namespace drawinglayer { namespace primitive2d { + class WrongSpellPrimitive2D; +}} + +namespace basegfx { + class B2DHomMatrix; + class BColorModifierStack; +} + +////////////////////////////////////////////////////////////////////////////// +// support WrongSpell rendreing using VCL from primitives due to VCLs nice +// and fast solution with wavelines + +namespace drawinglayer +{ + bool renderWrongSpellPrimitive2D( + const primitive2d::WrongSpellPrimitive2D& rWrongSpellCandidate, + OutputDevice& rOutputDevice, + const basegfx::B2DHomMatrix& rObjectToViewTransformation, + const basegfx::BColorModifierStack& rBColorModifierStack); + +} // end of namespace drawinglayer + +////////////////////////////////////////////////////////////////////////////// + +#endif // INCLUDED_DRAWINGLAYER_PROCESSOR2D_HELPERWRONGSPELLRENDER_HXX + +// eof diff --git a/drawinglayer/source/processor2d/hittestprocessor2d.cxx b/drawinglayer/source/processor2d/hittestprocessor2d.cxx new file mode 100644 index 000000000000..aaafa42ec44e --- /dev/null +++ b/drawinglayer/source/processor2d/hittestprocessor2d.cxx @@ -0,0 +1,605 @@ +/************************************************************************* + * + * 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_drawinglayer.hxx" + +#include <drawinglayer/processor2d/hittestprocessor2d.hxx> +#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx> +#include <drawinglayer/primitive2d/transformprimitive2d.hxx> +#include <drawinglayer/primitive2d/polygonprimitive2d.hxx> +#include <drawinglayer/primitive2d/polypolygonprimitive2d.hxx> +#include <basegfx/polygon/b2dpolygontools.hxx> +#include <basegfx/polygon/b2dpolypolygontools.hxx> +#include <drawinglayer/primitive2d/transparenceprimitive2d.hxx> +#include <drawinglayer/primitive2d/maskprimitive2d.hxx> +#include <drawinglayer/primitive2d/sceneprimitive2d.hxx> +#include <drawinglayer/primitive2d/pointarrayprimitive2d.hxx> +#include <basegfx/matrix/b3dhommatrix.hxx> +#include <drawinglayer/processor3d/cutfindprocessor3d.hxx> +#include <drawinglayer/primitive2d/hiddengeometryprimitive2d.hxx> +#include <drawinglayer/primitive2d/bitmapprimitive2d.hxx> + +////////////////////////////////////////////////////////////////////////////// + +namespace drawinglayer +{ + namespace processor2d + { + HitTestProcessor2D::HitTestProcessor2D(const geometry::ViewInformation2D& rViewInformation, + const basegfx::B2DPoint& rLogicHitPosition, + double fLogicHitTolerance, + bool bHitTextOnly) + : BaseProcessor2D(rViewInformation), + maDiscreteHitPosition(), + mfDiscreteHitTolerance(0.0), + mbHit(false), + mbHitToleranceUsed(false), + mbUseInvisiblePrimitiveContent(true), + mbHitTextOnly(bHitTextOnly) + { + // init hit tolerance + mfDiscreteHitTolerance = fLogicHitTolerance; + + if(basegfx::fTools::less(mfDiscreteHitTolerance, 0.0)) + { + // ensure input parameter for hit tolerance is >= 0.0 + mfDiscreteHitTolerance = 0.0; + } + else if(basegfx::fTools::more(mfDiscreteHitTolerance, 0.0)) + { + // generate discrete hit tolerance + mfDiscreteHitTolerance = (getViewInformation2D().getObjectToViewTransformation() + * basegfx::B2DVector(mfDiscreteHitTolerance, 0.0)).getLength(); + } + + // gererate discrete hit position + maDiscreteHitPosition = getViewInformation2D().getObjectToViewTransformation() * rLogicHitPosition; + + // check if HitTolerance is used + mbHitToleranceUsed = basegfx::fTools::more(getDiscreteHitTolerance(), 0.0); + } + + HitTestProcessor2D::~HitTestProcessor2D() + { + } + + bool HitTestProcessor2D::checkHairlineHitWithTolerance( + const basegfx::B2DPolygon& rPolygon, + double fDiscreteHitTolerance) + { + basegfx::B2DPolygon aLocalPolygon(rPolygon); + aLocalPolygon.transform(getViewInformation2D().getObjectToViewTransformation()); + + // get discrete range + basegfx::B2DRange aPolygonRange(aLocalPolygon.getB2DRange()); + + if(basegfx::fTools::more(fDiscreteHitTolerance, 0.0)) + { + aPolygonRange.grow(fDiscreteHitTolerance); + } + + // do rough range test first + if(aPolygonRange.isInside(getDiscreteHitPosition())) + { + // check if a polygon edge is hit + return basegfx::tools::isInEpsilonRange( + aLocalPolygon, + getDiscreteHitPosition(), + fDiscreteHitTolerance); + } + + return false; + } + + bool HitTestProcessor2D::checkFillHitWithTolerance( + const basegfx::B2DPolyPolygon& rPolyPolygon, + double fDiscreteHitTolerance) + { + bool bRetval(false); + basegfx::B2DPolyPolygon aLocalPolyPolygon(rPolyPolygon); + aLocalPolyPolygon.transform(getViewInformation2D().getObjectToViewTransformation()); + + // get discrete range + basegfx::B2DRange aPolygonRange(aLocalPolyPolygon.getB2DRange()); + const bool bDiscreteHitToleranceUsed(basegfx::fTools::more(fDiscreteHitTolerance, 0.0)); + + if(bDiscreteHitToleranceUsed) + { + aPolygonRange.grow(fDiscreteHitTolerance); + } + + // do rough range test first + if(aPolygonRange.isInside(getDiscreteHitPosition())) + { + // if a HitTolerance is given, check for polygon edge hit in epsilon first + if(bDiscreteHitToleranceUsed && + basegfx::tools::isInEpsilonRange( + aLocalPolyPolygon, + getDiscreteHitPosition(), + fDiscreteHitTolerance)) + { + bRetval = true; + } + + // check for hit in filled polyPolygon + if(!bRetval && basegfx::tools::isInside( + aLocalPolyPolygon, + getDiscreteHitPosition(), + true)) + { + bRetval = true; + } + } + + return bRetval; + } + + void HitTestProcessor2D::check3DHit(const primitive2d::ScenePrimitive2D& rCandidate) + { + // calculate relative point in unified 2D scene + const basegfx::B2DPoint aLogicHitPosition(getViewInformation2D().getInverseObjectToViewTransformation() * getDiscreteHitPosition()); + + // use bitmap check in ScenePrimitive2D + bool bTryFastResult(false); + + if(rCandidate.tryToCheckLastVisualisationDirectHit(aLogicHitPosition, bTryFastResult)) + { + mbHit = bTryFastResult; + } + else + { + basegfx::B2DHomMatrix aInverseSceneTransform(rCandidate.getObjectTransformation()); + aInverseSceneTransform.invert(); + const basegfx::B2DPoint aRelativePoint(aInverseSceneTransform * aLogicHitPosition); + + // check if test point is inside scene's unified area at all + if(aRelativePoint.getX() >= 0.0 && aRelativePoint.getX() <= 1.0 + && aRelativePoint.getY() >= 0.0 && aRelativePoint.getY() <= 1.0) + { + // get 3D view information + const geometry::ViewInformation3D& rObjectViewInformation3D = rCandidate.getViewInformation3D(); + + // create HitPoint Front and Back, transform to object coordinates + basegfx::B3DHomMatrix aViewToObject(rObjectViewInformation3D.getObjectToView()); + aViewToObject.invert(); + const basegfx::B3DPoint aFront(aViewToObject * basegfx::B3DPoint(aRelativePoint.getX(), aRelativePoint.getY(), 0.0)); + const basegfx::B3DPoint aBack(aViewToObject * basegfx::B3DPoint(aRelativePoint.getX(), aRelativePoint.getY(), 1.0)); + + if(!aFront.equal(aBack)) + { + const primitive3d::Primitive3DSequence& rPrimitives = rCandidate.getChildren3D(); + + if(rPrimitives.hasElements()) + { + // make BoundVolume empty and overlapping test for speedup + const basegfx::B3DRange aObjectRange( + drawinglayer::primitive3d::getB3DRangeFromPrimitive3DSequence( + rPrimitives, rObjectViewInformation3D)); + + if(!aObjectRange.isEmpty()) + { + const basegfx::B3DRange aFrontBackRange(aFront, aBack); + + if(aObjectRange.overlaps(aFrontBackRange)) + { + // bound volumes hit, geometric cut tests needed + drawinglayer::processor3d::CutFindProcessor aCutFindProcessor( + rObjectViewInformation3D, + aFront, + aBack, + true); + aCutFindProcessor.process(rPrimitives); + + mbHit = (0 != aCutFindProcessor.getCutPoints().size()); + } + } + } + } + } + + // This is needed to check hit with 3D shadows, too. HitTest is without shadow + // to keep compatible with previous versions. Keeping here as reference + // + // if(!getHit()) + // { + // // if scene has shadow, check hit with shadow, too + // const primitive2d::Primitive2DSequence xExtracted2DSceneShadow(rCandidate.getShadow2D(getViewInformation2D())); + // + // if(xExtracted2DSceneShadow.hasElements()) + // { + // // proccess extracted 2D content + // process(xExtracted2DSceneShadow); + // } + // } + + if(!getHit()) + { + // empty 3D scene; Check for border hit + basegfx::B2DPolygon aOutline(basegfx::tools::createUnitPolygon()); + aOutline.transform(rCandidate.getObjectTransformation()); + + mbHit = checkHairlineHitWithTolerance(aOutline, getDiscreteHitTolerance()); + } + + // This is what the previous version did. Keeping it here for reference + // + // // 2D Scene primitive containing 3D stuff; extract 2D contour in world coordinates + // // This may be refined later to an own 3D HitTest renderer which processes the 3D + // // geometry directly + // const primitive2d::ScenePrimitive2D& rScenePrimitive2DCandidate(static_cast< const primitive2d::ScenePrimitive2D& >(rCandidate)); + // const primitive2d::Primitive2DSequence xExtracted2DSceneGeometry(rScenePrimitive2DCandidate.getGeometry2D()); + // const primitive2d::Primitive2DSequence xExtracted2DSceneShadow(rScenePrimitive2DCandidate.getShadow2D(getViewInformation2D())); + // + // if(xExtracted2DSceneGeometry.hasElements() || xExtracted2DSceneShadow.hasElements()) + // { + // // proccess extracted 2D content + // process(xExtracted2DSceneGeometry); + // process(xExtracted2DSceneShadow); + // } + // else + // { + // // empty 3D scene; Check for border hit + // const basegfx::B2DRange aRange(rCandidate.getB2DRange(getViewInformation2D())); + // if(!aRange.isEmpty()) + // { + // const basegfx::B2DPolygon aOutline(basegfx::tools::createPolygonFromRect(aRange)); + // mbHit = checkHairlineHitWithTolerance(aOutline, getDiscreteHitTolerance()); + // } + // } + } + } + + void HitTestProcessor2D::processBasePrimitive2D(const primitive2d::BasePrimitive2D& rCandidate) + { + if(getHit()) + { + // stop processing as soon as a hit was recognized + return; + } + + switch(rCandidate.getPrimitive2DID()) + { + case PRIMITIVE2D_ID_TRANSFORMPRIMITIVE2D : + { + // remember current ViewInformation2D + const primitive2d::TransformPrimitive2D& rTransformCandidate(static_cast< const primitive2d::TransformPrimitive2D& >(rCandidate)); + const geometry::ViewInformation2D aLastViewInformation2D(getViewInformation2D()); + + // create new local ViewInformation2D containing transformation + const geometry::ViewInformation2D aViewInformation2D( + getViewInformation2D().getObjectTransformation() * rTransformCandidate.getTransformation(), + getViewInformation2D().getViewTransformation(), + getViewInformation2D().getViewport(), + getViewInformation2D().getVisualizedPage(), + getViewInformation2D().getViewTime(), + getViewInformation2D().getExtendedInformationSequence()); + updateViewInformation(aViewInformation2D); + + // proccess child content recursively + process(rTransformCandidate.getChildren()); + + // restore transformations + updateViewInformation(aLastViewInformation2D); + + break; + } + case PRIMITIVE2D_ID_POLYGONHAIRLINEPRIMITIVE2D : + { + if(!getHitTextOnly()) + { + // create hairline in discrete coordinates + const primitive2d::PolygonHairlinePrimitive2D& rPolygonCandidate(static_cast< const primitive2d::PolygonHairlinePrimitive2D& >(rCandidate)); + + // use hairline test + mbHit = checkHairlineHitWithTolerance(rPolygonCandidate.getB2DPolygon(), getDiscreteHitTolerance()); + } + + break; + } + case PRIMITIVE2D_ID_POLYGONMARKERPRIMITIVE2D : + { + if(!getHitTextOnly()) + { + // handle marker like hairline; no need to decompose in dashes + const primitive2d::PolygonMarkerPrimitive2D& rPolygonCandidate(static_cast< const primitive2d::PolygonMarkerPrimitive2D& >(rCandidate)); + + // use hairline test + mbHit = checkHairlineHitWithTolerance(rPolygonCandidate.getB2DPolygon(), getDiscreteHitTolerance()); + } + + break; + } + case PRIMITIVE2D_ID_POLYGONSTROKEPRIMITIVE2D : + { + if(!getHitTextOnly()) + { + // handle stroke evtl. directly; no need to decompose to filled polygon outlines + const primitive2d::PolygonStrokePrimitive2D& rPolygonCandidate(static_cast< const primitive2d::PolygonStrokePrimitive2D& >(rCandidate)); + const attribute::LineAttribute& rLineAttribute = rPolygonCandidate.getLineAttribute(); + + if(basegfx::fTools::more(rLineAttribute.getWidth(), 0.0)) + { + if(basegfx::B2DLINEJOIN_MITER == rLineAttribute.getLineJoin()) + { + // if line is mitered, use decomposition since mitered line + // geometry may use more space than the geometry grown by half line width + process(rCandidate.get2DDecomposition(getViewInformation2D())); + } + else + { + // for all other B2DLINEJOIN_* do a hairline HitTest with expanded tolerance + const basegfx::B2DVector aDiscreteHalfLineVector(getViewInformation2D().getObjectToViewTransformation() + * basegfx::B2DVector(rLineAttribute.getWidth() * 0.5, 0.0)); + mbHit = checkHairlineHitWithTolerance( + rPolygonCandidate.getB2DPolygon(), + getDiscreteHitTolerance() + aDiscreteHalfLineVector.getLength()); + } + } + else + { + // hairline; fallback to hairline test. Do not decompose + // since this may decompose the hairline to dashes + mbHit = checkHairlineHitWithTolerance(rPolygonCandidate.getB2DPolygon(), getDiscreteHitTolerance()); + } + } + + break; + } + case PRIMITIVE2D_ID_POLYGONWAVEPRIMITIVE2D : + { + if(!getHitTextOnly()) + { + // do not use decompose; just handle like a line with width + const primitive2d::PolygonWavePrimitive2D& rPolygonCandidate(static_cast< const primitive2d::PolygonWavePrimitive2D& >(rCandidate)); + double fLogicHitTolerance(0.0); + + // if WaveHeight, grow by it + if(basegfx::fTools::more(rPolygonCandidate.getWaveHeight(), 0.0)) + { + fLogicHitTolerance += rPolygonCandidate.getWaveHeight(); + } + + // if line width, grow by it + if(basegfx::fTools::more(rPolygonCandidate.getLineAttribute().getWidth(), 0.0)) + { + fLogicHitTolerance += rPolygonCandidate.getLineAttribute().getWidth() * 0.5; + } + + const basegfx::B2DVector aDiscreteHalfLineVector(getViewInformation2D().getObjectToViewTransformation() + * basegfx::B2DVector(fLogicHitTolerance, 0.0)); + + mbHit = checkHairlineHitWithTolerance( + rPolygonCandidate.getB2DPolygon(), + getDiscreteHitTolerance() + aDiscreteHalfLineVector.getLength()); + } + + break; + } + case PRIMITIVE2D_ID_POLYPOLYGONCOLORPRIMITIVE2D : + { + if(!getHitTextOnly()) + { + // create filled polyPolygon in discrete coordinates + const primitive2d::PolyPolygonColorPrimitive2D& rPolygonCandidate(static_cast< const primitive2d::PolyPolygonColorPrimitive2D& >(rCandidate)); + + // use fill hit test + mbHit = checkFillHitWithTolerance(rPolygonCandidate.getB2DPolyPolygon(), getDiscreteHitTolerance()); + } + + break; + } + case PRIMITIVE2D_ID_TRANSPARENCEPRIMITIVE2D : + { + // sub-transparence group + const primitive2d::TransparencePrimitive2D& rTransCandidate(static_cast< const primitive2d::TransparencePrimitive2D& >(rCandidate)); + + // Currently the transparence content is not taken into account; only + // the children are recursively checked for hit. This may be refined for + // parts where the content is completely transparent if needed. + process(rTransCandidate.getChildren()); + + break; + } + case PRIMITIVE2D_ID_MASKPRIMITIVE2D : + { + // create mask in discrete coordinates; only recursively continue + // with content when HitTest position is inside the mask + const primitive2d::MaskPrimitive2D& rMaskCandidate(static_cast< const primitive2d::MaskPrimitive2D& >(rCandidate)); + + // use fill hit test + if(checkFillHitWithTolerance(rMaskCandidate.getMask(), getDiscreteHitTolerance())) + { + // recursively HitTest children + process(rMaskCandidate.getChildren()); + } + + break; + } + case PRIMITIVE2D_ID_SCENEPRIMITIVE2D : + { + if(!getHitTextOnly()) + { + const primitive2d::ScenePrimitive2D& rScenePrimitive2D( + static_cast< const primitive2d::ScenePrimitive2D& >(rCandidate)); + check3DHit(rScenePrimitive2D); + } + + break; + } + case PRIMITIVE2D_ID_WRONGSPELLPRIMITIVE2D : + case PRIMITIVE2D_ID_MARKERARRAYPRIMITIVE2D : + case PRIMITIVE2D_ID_GRIDPRIMITIVE2D : + case PRIMITIVE2D_ID_HELPLINEPRIMITIVE2D : + { + // ignorable primitives + break; + } + case PRIMITIVE2D_ID_SHADOWPRIMITIVE2D : + { + // Ignore shadows; we do not want to have shadows hittable. + // Remove this one to make shadows hittable on demand. + break; + } + case PRIMITIVE2D_ID_TEXTSIMPLEPORTIONPRIMITIVE2D : + case PRIMITIVE2D_ID_TEXTDECORATEDPORTIONPRIMITIVE2D : + { + // for text use the BoundRect of the primitive itself + const basegfx::B2DRange aRange(rCandidate.getB2DRange(getViewInformation2D())); + + if(!aRange.isEmpty()) + { + const basegfx::B2DPolygon aOutline(basegfx::tools::createPolygonFromRect(aRange)); + mbHit = checkFillHitWithTolerance(basegfx::B2DPolyPolygon(aOutline), getDiscreteHitTolerance()); + } + + break; + } + case PRIMITIVE2D_ID_BITMAPPRIMITIVE2D : + { + if(!getHitTextOnly()) + { + // The recently added BitmapEx::GetTransparency() makes it easy to extend + // the BitmapPrimitive2D HitTest to take the contained BotmapEx and it's + // transparency into account + const basegfx::B2DRange aRange(rCandidate.getB2DRange(getViewInformation2D())); + + if(!aRange.isEmpty()) + { + const primitive2d::BitmapPrimitive2D& rBitmapCandidate(static_cast< const primitive2d::BitmapPrimitive2D& >(rCandidate)); + const BitmapEx& rBitmapEx = rBitmapCandidate.getBitmapEx(); + const Size& rSizePixel(rBitmapEx.GetSizePixel()); + + if(rSizePixel.Width() && rSizePixel.Height()) + { + basegfx::B2DHomMatrix aBackTransform( + getViewInformation2D().getObjectToViewTransformation() * + rBitmapCandidate.getTransform()); + aBackTransform.invert(); + + const basegfx::B2DPoint aRelativePoint(aBackTransform * getDiscreteHitPosition()); + const basegfx::B2DRange aUnitRange(0.0, 0.0, 1.0, 1.0); + + if(aUnitRange.isInside(aRelativePoint)) + { + const sal_Int32 nX(basegfx::fround(aRelativePoint.getX() * rSizePixel.Width())); + const sal_Int32 nY(basegfx::fround(aRelativePoint.getY() * rSizePixel.Height())); + + mbHit = (0xff != rBitmapEx.GetTransparency(nX, nY)); + } + } + else + { + // fallback to standard HitTest + const basegfx::B2DPolygon aOutline(basegfx::tools::createPolygonFromRect(aRange)); + mbHit = checkFillHitWithTolerance(basegfx::B2DPolyPolygon(aOutline), getDiscreteHitTolerance()); + } + } + } + + break; + } + case PRIMITIVE2D_ID_METAFILEPRIMITIVE2D : + case PRIMITIVE2D_ID_CONTROLPRIMITIVE2D : + case PRIMITIVE2D_ID_FILLGRADIENTPRIMITIVE2D : + case PRIMITIVE2D_ID_FILLHATCHPRIMITIVE2D : + case PRIMITIVE2D_ID_PAGEPREVIEWPRIMITIVE2D : + { + if(!getHitTextOnly()) + { + // Class of primitives for which just the BoundRect of the primitive itself + // will be used for HitTest currently. + // + // This may be refined in the future, e.g: + // - For Bitamps, the mask and/or transparence information may be used + // - For MetaFiles, the MetaFile content may be used + const basegfx::B2DRange aRange(rCandidate.getB2DRange(getViewInformation2D())); + + if(!aRange.isEmpty()) + { + const basegfx::B2DPolygon aOutline(basegfx::tools::createPolygonFromRect(aRange)); + mbHit = checkFillHitWithTolerance(basegfx::B2DPolyPolygon(aOutline), getDiscreteHitTolerance()); + } + } + + break; + } + case PRIMITIVE2D_ID_HIDDENGEOMETRYPRIMITIVE2D : + { + // HiddenGeometryPrimitive2D; the default decomposition would return an empty seqence, + // so force this primitive to process it's children directly if the switch is set + // (which is the default). Else, ignore invisible content + const primitive2d::HiddenGeometryPrimitive2D& rHiddenGeometry(static_cast< const primitive2d::HiddenGeometryPrimitive2D& >(rCandidate)); + const primitive2d::Primitive2DSequence& rChildren = rHiddenGeometry.getChildren(); + + if(rChildren.hasElements()) + { + if(getUseInvisiblePrimitiveContent()) + { + process(rChildren); + } + } + + break; + } + case PRIMITIVE2D_ID_POINTARRAYPRIMITIVE2D : + { + if(!getHitTextOnly()) + { + const primitive2d::PointArrayPrimitive2D& rPointArrayCandidate(static_cast< const primitive2d::PointArrayPrimitive2D& >(rCandidate)); + const std::vector< basegfx::B2DPoint >& rPositions = rPointArrayCandidate.getPositions(); + const sal_uInt32 nCount(rPositions.size()); + + for(sal_uInt32 a(0); !getHit() && a < nCount; a++) + { + const basegfx::B2DPoint aPosition(getViewInformation2D().getObjectToViewTransformation() * rPositions[a]); + const basegfx::B2DVector aDistance(aPosition - getDiscreteHitPosition()); + + if(aDistance.getLength() <= getDiscreteHitTolerance()) + { + mbHit = true; + } + } + } + + break; + } + default : + { + // process recursively + process(rCandidate.get2DDecomposition(getViewInformation2D())); + + break; + } + } + } + + } // end of namespace processor2d +} // end of namespace drawinglayer + +////////////////////////////////////////////////////////////////////////////// +// eof diff --git a/drawinglayer/source/processor2d/linegeometryextractor2d.cxx b/drawinglayer/source/processor2d/linegeometryextractor2d.cxx new file mode 100644 index 000000000000..be3601dd0024 --- /dev/null +++ b/drawinglayer/source/processor2d/linegeometryextractor2d.cxx @@ -0,0 +1,144 @@ +/************************************************************************* + * + * 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_drawinglayer.hxx" + +#include <drawinglayer/processor2d/linegeometryextractor2d.hxx> +#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx> +#include <drawinglayer/primitive2d/polygonprimitive2d.hxx> +#include <drawinglayer/primitive2d/polypolygonprimitive2d.hxx> +#include <drawinglayer/primitive2d/transformprimitive2d.hxx> + +////////////////////////////////////////////////////////////////////////////// + +using namespace com::sun::star; + +////////////////////////////////////////////////////////////////////////////// + +namespace drawinglayer +{ + namespace processor2d + { + LineGeometryExtractor2D::LineGeometryExtractor2D(const geometry::ViewInformation2D& rViewInformation) + : BaseProcessor2D(rViewInformation), + maExtractedHairlines(), + maExtractedLineFills(), + mbInLineGeometry(false) + { + } + + LineGeometryExtractor2D::~LineGeometryExtractor2D() + { + } + + void LineGeometryExtractor2D::processBasePrimitive2D(const primitive2d::BasePrimitive2D& rCandidate) + { + switch(rCandidate.getPrimitive2DID()) + { + case PRIMITIVE2D_ID_POLYGONSTROKEPRIMITIVE2D : + case PRIMITIVE2D_ID_POLYGONSTROKEARROWPRIMITIVE2D : + { + // enter a line geometry group (with or without LineEnds) + bool bOldState(mbInLineGeometry); + mbInLineGeometry = true; + process(rCandidate.get2DDecomposition(getViewInformation2D())); + mbInLineGeometry = bOldState; + break; + } + case PRIMITIVE2D_ID_POLYGONHAIRLINEPRIMITIVE2D : + { + if(mbInLineGeometry) + { + // extract hairline line geometry in world coordinates + const primitive2d::PolygonHairlinePrimitive2D& rPolygonCandidate(static_cast< const primitive2d::PolygonHairlinePrimitive2D& >(rCandidate)); + basegfx::B2DPolygon aLocalPolygon(rPolygonCandidate.getB2DPolygon()); + aLocalPolygon.transform(getViewInformation2D().getObjectTransformation()); + maExtractedHairlines.push_back(aLocalPolygon); + } + break; + } + case PRIMITIVE2D_ID_POLYPOLYGONCOLORPRIMITIVE2D : + { + if(mbInLineGeometry) + { + // extract filled line geometry (line with width) + const primitive2d::PolyPolygonColorPrimitive2D& rPolygonCandidate(static_cast< const primitive2d::PolyPolygonColorPrimitive2D& >(rCandidate)); + basegfx::B2DPolyPolygon aLocalPolyPolygon(rPolygonCandidate.getB2DPolyPolygon()); + aLocalPolyPolygon.transform(getViewInformation2D().getObjectTransformation()); + maExtractedLineFills.push_back(aLocalPolyPolygon); + } + break; + } + case PRIMITIVE2D_ID_TRANSFORMPRIMITIVE2D : + { + // remember current transformation and ViewInformation + const primitive2d::TransformPrimitive2D& rTransformCandidate(static_cast< const primitive2d::TransformPrimitive2D& >(rCandidate)); + const geometry::ViewInformation2D aLastViewInformation2D(getViewInformation2D()); + + // create new transformations for CurrentTransformation and for local ViewInformation2D + const geometry::ViewInformation2D aViewInformation2D( + getViewInformation2D().getObjectTransformation() * rTransformCandidate.getTransformation(), + getViewInformation2D().getViewTransformation(), + getViewInformation2D().getViewport(), + getViewInformation2D().getVisualizedPage(), + getViewInformation2D().getViewTime(), + getViewInformation2D().getExtendedInformationSequence()); + updateViewInformation(aViewInformation2D); + + // proccess content + process(rTransformCandidate.getChildren()); + + // restore transformations + updateViewInformation(aLastViewInformation2D); + + break; + } + case PRIMITIVE2D_ID_SCENEPRIMITIVE2D : + case PRIMITIVE2D_ID_WRONGSPELLPRIMITIVE2D : + case PRIMITIVE2D_ID_MARKERARRAYPRIMITIVE2D : + case PRIMITIVE2D_ID_POINTARRAYPRIMITIVE2D : + case PRIMITIVE2D_ID_BITMAPPRIMITIVE2D : + case PRIMITIVE2D_ID_METAFILEPRIMITIVE2D : + case PRIMITIVE2D_ID_MASKPRIMITIVE2D : + { + // ignorable primitives + break; + } + default : + { + // process recursively + process(rCandidate.get2DDecomposition(getViewInformation2D())); + break; + } + } + } + } // end of namespace processor2d +} // end of namespace drawinglayer + +////////////////////////////////////////////////////////////////////////////// +// eof diff --git a/drawinglayer/source/processor2d/makefile.mk b/drawinglayer/source/processor2d/makefile.mk new file mode 100644 index 000000000000..ec7b8aef647b --- /dev/null +++ b/drawinglayer/source/processor2d/makefile.mk @@ -0,0 +1,58 @@ +#************************************************************************* +# +# 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. +# +#************************************************************************* + +PRJ=..$/.. +PRJNAME=drawinglayer +TARGET=processor2d +ENABLE_EXCEPTIONS=TRUE + +# --- Settings ---------------------------------- + +.INCLUDE : settings.mk + +# --- Files ------------------------------------- + +SLOFILES= \ + $(SLO)$/baseprocessor2d.obj \ + $(SLO)$/vclhelpergradient.obj \ + $(SLO)$/vclhelperbitmaptransform.obj \ + $(SLO)$/vclhelperbitmaprender.obj \ + $(SLO)$/vclhelperbufferdevice.obj \ + $(SLO)$/vclprocessor2d.obj \ + $(SLO)$/helperchartrenderer.obj \ + $(SLO)$/helperwrongspellrenderer.obj \ + $(SLO)$/vclpixelprocessor2d.obj \ + $(SLO)$/vclmetafileprocessor2d.obj \ + $(SLO)$/contourextractor2d.obj \ + $(SLO)$/linegeometryextractor2d.obj \ + $(SLO)$/canvasprocessor.obj \ + $(SLO)$/hittestprocessor2d.obj \ + $(SLO)$/textaspolygonextractor2d.obj + +# --- Targets ---------------------------------- + +.INCLUDE : target.mk diff --git a/drawinglayer/source/processor2d/textaspolygonextractor2d.cxx b/drawinglayer/source/processor2d/textaspolygonextractor2d.cxx new file mode 100644 index 000000000000..b9ad83373923 --- /dev/null +++ b/drawinglayer/source/processor2d/textaspolygonextractor2d.cxx @@ -0,0 +1,247 @@ +/************************************************************************* + * + * 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_drawinglayer.hxx" + +#include <drawinglayer/processor2d/textaspolygonextractor2d.hxx> +#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx> +#include <drawinglayer/primitive2d/polypolygonprimitive2d.hxx> +#include <drawinglayer/primitive2d/polygonprimitive2d.hxx> +#include <drawinglayer/primitive2d/modifiedcolorprimitive2d.hxx> +#include <drawinglayer/primitive2d/transformprimitive2d.hxx> + +////////////////////////////////////////////////////////////////////////////// + +namespace drawinglayer +{ + namespace processor2d + { + void TextAsPolygonExtractor2D::processBasePrimitive2D(const primitive2d::BasePrimitive2D& rCandidate) + { + switch(rCandidate.getPrimitive2DID()) + { + case PRIMITIVE2D_ID_TEXTDECORATEDPORTIONPRIMITIVE2D : + { + // TextDecoratedPortionPrimitive2D can produce the following primitives + // when being decomposed: + // + // - TextSimplePortionPrimitive2D + // - PolygonWavePrimitive2D + // - PolygonStrokePrimitive2D + // - PolygonStrokePrimitive2D + // - PolyPolygonColorPrimitive2D + // - PolyPolygonHairlinePrimitive2D + // - PolygonHairlinePrimitive2D + // - ShadowPrimitive2D + // - ModifiedColorPrimitive2D + // - TransformPrimitive2D + // - TextEffectPrimitive2D + // - ModifiedColorPrimitive2D + // - TransformPrimitive2D + // - GroupPrimitive2D + + // encapsulate with flag and use decomposition + mnInText++; + process(rCandidate.get2DDecomposition(getViewInformation2D())); + mnInText--; + + break; + } + case PRIMITIVE2D_ID_TEXTSIMPLEPORTIONPRIMITIVE2D : + { + // TextSimplePortionPrimitive2D can produce the following primitives + // when being decomposed: + // + // - PolyPolygonColorPrimitive2D + // - TextEffectPrimitive2D + // - ModifiedColorPrimitive2D + // - TransformPrimitive2D + // - GroupPrimitive2D + + // encapsulate with flag and use decomposition + mnInText++; + process(rCandidate.get2DDecomposition(getViewInformation2D())); + mnInText--; + + break; + } + + // as can be seen from the TextSimplePortionPrimitive2D and the + // TextDecoratedPortionPrimitive2D, inside of the mnInText marks + // the following primitives can occurr containing geometry data + // from text decomposition: + // + // - PolyPolygonColorPrimitive2D + // - PolygonHairlinePrimitive2D + // - PolyPolygonHairlinePrimitive2D (for convenience) + // + case PRIMITIVE2D_ID_POLYPOLYGONCOLORPRIMITIVE2D : + { + if(mnInText) + { + const primitive2d::PolyPolygonColorPrimitive2D& rPoPoCoCandidate(static_cast< const primitive2d::PolyPolygonColorPrimitive2D& >(rCandidate)); + basegfx::B2DPolyPolygon aPolyPolygon(rPoPoCoCandidate.getB2DPolyPolygon()); + + if(aPolyPolygon.count()) + { + // transform the PolyPolygon + aPolyPolygon.transform(getViewInformation2D().getObjectToViewTransformation()); + + // get evtl. corrected color + const basegfx::BColor aColor(maBColorModifierStack.getModifiedColor(rPoPoCoCandidate.getBColor())); + + // add to result vector + maTarget.push_back(TextAsPolygonDataNode(aPolyPolygon, aColor, true)); + } + } + + break; + } + case PRIMITIVE2D_ID_POLYGONHAIRLINEPRIMITIVE2D : + { + if(mnInText) + { + const primitive2d::PolygonHairlinePrimitive2D& rPoHaCandidate(static_cast< const primitive2d::PolygonHairlinePrimitive2D& >(rCandidate)); + basegfx::B2DPolygon aPolygon(rPoHaCandidate.getB2DPolygon()); + + if(aPolygon.count()) + { + // transform the Polygon + aPolygon.transform(getViewInformation2D().getObjectToViewTransformation()); + + // get evtl. corrected color + const basegfx::BColor aColor(maBColorModifierStack.getModifiedColor(rPoHaCandidate.getBColor())); + + // add to result vector + maTarget.push_back(TextAsPolygonDataNode(basegfx::B2DPolyPolygon(aPolygon), aColor, false)); + } + } + + break; + } + case PRIMITIVE2D_ID_POLYPOLYGONHAIRLINEPRIMITIVE2D : + { + if(mnInText) + { + const primitive2d::PolyPolygonHairlinePrimitive2D& rPoPoHaCandidate(static_cast< const primitive2d::PolyPolygonHairlinePrimitive2D& >(rCandidate)); + basegfx::B2DPolyPolygon aPolyPolygon(rPoPoHaCandidate.getB2DPolyPolygon()); + + if(aPolyPolygon.count()) + { + // transform the Polygon + aPolyPolygon.transform(getViewInformation2D().getObjectToViewTransformation()); + + // get evtl. corrected color + const basegfx::BColor aColor(maBColorModifierStack.getModifiedColor(rPoPoHaCandidate.getBColor())); + + // add to result vector + maTarget.push_back(TextAsPolygonDataNode(aPolyPolygon, aColor, false)); + } + } + + break; + } + + // usage of color modification stack is needed + case PRIMITIVE2D_ID_MODIFIEDCOLORPRIMITIVE2D : + { + const primitive2d::ModifiedColorPrimitive2D& rModifiedColorCandidate(static_cast< const primitive2d::ModifiedColorPrimitive2D& >(rCandidate)); + + if(rModifiedColorCandidate.getChildren().hasElements()) + { + maBColorModifierStack.push(rModifiedColorCandidate.getColorModifier()); + process(rModifiedColorCandidate.getChildren()); + maBColorModifierStack.pop(); + } + + break; + } + + // usage of transformation stack is needed + case PRIMITIVE2D_ID_TRANSFORMPRIMITIVE2D : + { + // remember current transformation and ViewInformation + const primitive2d::TransformPrimitive2D& rTransformCandidate(static_cast< const primitive2d::TransformPrimitive2D& >(rCandidate)); + const geometry::ViewInformation2D aLastViewInformation2D(getViewInformation2D()); + + // create new transformations for CurrentTransformation and for local ViewInformation2D + const geometry::ViewInformation2D aViewInformation2D( + getViewInformation2D().getObjectTransformation() * rTransformCandidate.getTransformation(), + getViewInformation2D().getViewTransformation(), + getViewInformation2D().getViewport(), + getViewInformation2D().getVisualizedPage(), + getViewInformation2D().getViewTime(), + getViewInformation2D().getExtendedInformationSequence()); + updateViewInformation(aViewInformation2D); + + // proccess content + process(rTransformCandidate.getChildren()); + + // restore transformations + updateViewInformation(aLastViewInformation2D); + + break; + } + + // ignorable primitives + case PRIMITIVE2D_ID_SCENEPRIMITIVE2D : + case PRIMITIVE2D_ID_WRONGSPELLPRIMITIVE2D : + case PRIMITIVE2D_ID_MARKERARRAYPRIMITIVE2D : + case PRIMITIVE2D_ID_POINTARRAYPRIMITIVE2D : + case PRIMITIVE2D_ID_BITMAPPRIMITIVE2D : + case PRIMITIVE2D_ID_METAFILEPRIMITIVE2D : + case PRIMITIVE2D_ID_MASKPRIMITIVE2D : + { + break; + } + + default : + { + // process recursively + process(rCandidate.get2DDecomposition(getViewInformation2D())); + break; + } + } + } + + TextAsPolygonExtractor2D::TextAsPolygonExtractor2D(const geometry::ViewInformation2D& rViewInformation) + : BaseProcessor2D(rViewInformation), + maTarget(), + maBColorModifierStack(), + mnInText(0) + { + } + + TextAsPolygonExtractor2D::~TextAsPolygonExtractor2D() + { + } + } // end of namespace processor2d +} // end of namespace drawinglayer + +////////////////////////////////////////////////////////////////////////////// +// eof diff --git a/drawinglayer/source/processor2d/vclhelperbitmaprender.cxx b/drawinglayer/source/processor2d/vclhelperbitmaprender.cxx new file mode 100644 index 000000000000..80e34ba27701 --- /dev/null +++ b/drawinglayer/source/processor2d/vclhelperbitmaprender.cxx @@ -0,0 +1,280 @@ +/************************************************************************* + * + * 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_drawinglayer.hxx" + +#include <vclhelperbitmaprender.hxx> +#include <svtools/grfmgr.hxx> +#include <basegfx/vector/b2dvector.hxx> +#include <basegfx/matrix/b2dhommatrix.hxx> +#include <basegfx/range/b2drange.hxx> +#include <vcl/outdev.hxx> +#include <vclhelperbitmaptransform.hxx> +#include <basegfx/matrix/b2dhommatrixtools.hxx> + +////////////////////////////////////////////////////////////////////////////// +// support for different kinds of bitmap rendering using vcl + +namespace drawinglayer +{ + void RenderBitmapPrimitive2D_GraphicManager( + OutputDevice& rOutDev, + const BitmapEx& rBitmapEx, + const basegfx::B2DHomMatrix& rTransform) + { + // prepare attributes + GraphicAttr aAttributes; + + // decompose matrix to check for shear, rotate and mirroring + basegfx::B2DVector aScale, aTranslate; + double fRotate, fShearX; + rTransform.decompose(aScale, aTranslate, fRotate, fShearX); + + // mirror flags + aAttributes.SetMirrorFlags( + (basegfx::fTools::less(aScale.getX(), 0.0) ? BMP_MIRROR_HORZ : 0)| + (basegfx::fTools::less(aScale.getY(), 0.0) ? BMP_MIRROR_VERT : 0)); + + // rotation + if(!basegfx::fTools::equalZero(fRotate)) + { + double fRotation(fmod(3600.0 - (fRotate * (10.0 / F_PI180)), 3600.0)); + aAttributes.SetRotation((sal_uInt16)(fRotation)); + } + + // prepare Bitmap + basegfx::B2DRange aOutlineRange(0.0, 0.0, 1.0, 1.0); + + if(basegfx::fTools::equalZero(fRotate)) + { + aOutlineRange.transform(rTransform); + } + else + { + // if rotated, create the unrotated output rectangle for the GraphicManager paint + const basegfx::B2DHomMatrix aSimpleObjectMatrix(basegfx::tools::createScaleTranslateB2DHomMatrix( + fabs(aScale.getX()), fabs(aScale.getY()), + aTranslate.getX(), aTranslate.getY())); + + aOutlineRange.transform(aSimpleObjectMatrix); + } + + // prepare dest coor + const sal_uInt32 nDiscreteWidth(basegfx::fround(aOutlineRange.getMaxX())); + const sal_uInt32 nDiscreteHeight(basegfx::fround(aOutlineRange.getMaxY())); + const Rectangle aDestRectPixel( + basegfx::fround(aOutlineRange.getMinX()), + basegfx::fround(aOutlineRange.getMinY()), + nDiscreteWidth > 0 ? nDiscreteWidth - 1 : 0, + nDiscreteHeight > 0 ? nDiscreteHeight - 1 : 0); + + // paint it using GraphicManager + Graphic aGraphic(rBitmapEx); + GraphicObject aGraphicObject(aGraphic); + aGraphicObject.Draw(&rOutDev, aDestRectPixel.TopLeft(), aDestRectPixel.GetSize(), &aAttributes); + } + + void RenderBitmapPrimitive2D_BitmapEx( + OutputDevice& rOutDev, + const BitmapEx& rBitmapEx, + const basegfx::B2DHomMatrix& rTransform) + { + // only translate and scale, use vcl's DrawBitmapEx(). + BitmapEx aContent(rBitmapEx); + + // prepare dest coor. Necessary to expand since vcl's DrawBitmapEx draws one pix less + basegfx::B2DRange aOutlineRange(0.0, 0.0, 1.0, 1.0); + aOutlineRange.transform(rTransform); + const sal_uInt32 nDiscreteWidth(basegfx::fround(aOutlineRange.getMaxX())); + const sal_uInt32 nDiscreteHeight(basegfx::fround(aOutlineRange.getMaxY())); + const Rectangle aDestRectPixel( + basegfx::fround(aOutlineRange.getMinX()), + basegfx::fround(aOutlineRange.getMinY()), + nDiscreteWidth > 0 ? nDiscreteWidth - 1 : 0, + nDiscreteHeight > 0 ? nDiscreteHeight - 1 : 0); + + // decompose matrix to check for shear, rotate and mirroring + basegfx::B2DVector aScale, aTranslate; + double fRotate, fShearX; + rTransform.decompose(aScale, aTranslate, fRotate, fShearX); + + // Check mirroring. + sal_uInt32 nMirrorFlags(BMP_MIRROR_NONE); + + if(basegfx::fTools::less(aScale.getX(), 0.0)) + { + nMirrorFlags |= BMP_MIRROR_HORZ; + } + + if(basegfx::fTools::less(aScale.getY(), 0.0)) + { + nMirrorFlags |= BMP_MIRROR_VERT; + } + + if(BMP_MIRROR_NONE != nMirrorFlags) + { + aContent.Mirror(nMirrorFlags); + } + + // draw bitmap + rOutDev.DrawBitmapEx(aDestRectPixel.TopLeft(), aDestRectPixel.GetSize(), aContent); + } + + void RenderBitmapPrimitive2D_self( + OutputDevice& rOutDev, + const BitmapEx& rBitmapEx, + const basegfx::B2DHomMatrix& rTransform) + { + // process self with free transformation (containing shear and rotate). Get dest rect in pixels. + basegfx::B2DRange aOutlineRange(0.0, 0.0, 1.0, 1.0); + aOutlineRange.transform(rTransform); + const sal_uInt32 nDiscreteWidth(basegfx::fround(aOutlineRange.getMaxX())); + const sal_uInt32 nDiscreteHeight(basegfx::fround(aOutlineRange.getMaxY())); + const Rectangle aDestRectLogic( + basegfx::fround(aOutlineRange.getMinX()), + basegfx::fround(aOutlineRange.getMinY()), + nDiscreteWidth > 0 ? nDiscreteWidth - 1 : 0, + nDiscreteHeight > 0 ? nDiscreteHeight - 1 : 0); + const Rectangle aDestRectPixel(rOutDev.LogicToPixel(aDestRectLogic)); + + // #i96708# check if Metafile is recorded + const GDIMetaFile* pMetaFile = rOutDev.GetConnectMetaFile(); + const bool bRecordToMetaFile(pMetaFile && pMetaFile->IsRecord() && !pMetaFile->IsPause()); + + // intersect with output pixel size, but only + // when not recording to metafile + const Rectangle aOutputRectPixel(Point(), rOutDev.GetOutputSizePixel()); + Rectangle aCroppedRectPixel(bRecordToMetaFile ? aDestRectPixel : aDestRectPixel.GetIntersection(aOutputRectPixel)); + + if(!aCroppedRectPixel.IsEmpty()) + { + // as maximum for destination, orientate at aOutputRectPixel, but + // take a rotation of 45 degrees (sqrt(2)) as maximum expansion into account + const Size aSourceSizePixel(rBitmapEx.GetSizePixel()); + const double fMaximumArea( + (double)aOutputRectPixel.getWidth() * + (double)aOutputRectPixel.getHeight() * + 1.4142136); // 1.4142136 taken as sqrt(2.0) + + // test if discrete view size (pixel) maybe too big and limit it + const double fArea(aCroppedRectPixel.getWidth() * aCroppedRectPixel.getHeight()); + const bool bNeedToReduce(fArea > fMaximumArea); + double fReduceFactor(1.0); + const Size aDestSizePixel(aCroppedRectPixel.GetSize()); + + if(bNeedToReduce) + { + fReduceFactor = sqrt(fMaximumArea / fArea); + aCroppedRectPixel.setWidth(basegfx::fround(aCroppedRectPixel.getWidth() * fReduceFactor)); + aCroppedRectPixel.setHeight(basegfx::fround(aCroppedRectPixel.getHeight() * fReduceFactor)); + } + + // build transform from pixel in aDestination to pixel in rBitmapEx + // from relative in aCroppedRectPixel to relative in aDestRectPixel + // No need to take bNeedToReduce into account, TopLeft is unchanged + basegfx::B2DHomMatrix aTransform(basegfx::tools::createTranslateB2DHomMatrix( + aCroppedRectPixel.Left() - aDestRectPixel.Left(), + aCroppedRectPixel.Top() - aDestRectPixel.Top())); + + // from relative in aDestRectPixel to absolute Logic. Here it + // is essential to adapt to reduce factor (if used) + double fAdaptedDRPWidth((double)aDestRectPixel.getWidth()); + double fAdaptedDRPHeight((double)aDestRectPixel.getHeight()); + + if(bNeedToReduce) + { + fAdaptedDRPWidth *= fReduceFactor; + fAdaptedDRPHeight *= fReduceFactor; + } + + aTransform = basegfx::tools::createScaleTranslateB2DHomMatrix( + aDestRectLogic.getWidth() / fAdaptedDRPWidth, aDestRectLogic.getHeight() / fAdaptedDRPHeight, + aDestRectLogic.Left(), aDestRectLogic.Top()) + * aTransform; + + // from absolute in Logic to unified object coordinates (0.0 .. 1.0 in x and y) + basegfx::B2DHomMatrix aInvBitmapTransform(rTransform); + aInvBitmapTransform.invert(); + aTransform = aInvBitmapTransform * aTransform; + + // from unit object coordinates to rBitmapEx pixel coordintes + aTransform.scale(aSourceSizePixel.getWidth() - 1L, aSourceSizePixel.getHeight() - 1L); + + // create bitmap using source, destination and linear back-transformation + BitmapEx aDestination = impTransformBitmapEx(rBitmapEx, aCroppedRectPixel, aTransform); + + // paint + if(bNeedToReduce) + { + // paint in target size + if(bRecordToMetaFile) + { + rOutDev.DrawBitmapEx( + rOutDev.PixelToLogic(aCroppedRectPixel.TopLeft()), + rOutDev.PixelToLogic(aDestSizePixel), + aDestination); + } + else + { + const bool bWasEnabled(rOutDev.IsMapModeEnabled()); + rOutDev.EnableMapMode(false); + + rOutDev.DrawBitmapEx( + aCroppedRectPixel.TopLeft(), + aDestSizePixel, + aDestination); + + rOutDev.EnableMapMode(bWasEnabled); + } + } + else + { + if(bRecordToMetaFile) + { + rOutDev.DrawBitmapEx( + rOutDev.PixelToLogic(aCroppedRectPixel.TopLeft()), + aDestination); + } + else + { + const bool bWasEnabled(rOutDev.IsMapModeEnabled()); + rOutDev.EnableMapMode(false); + + rOutDev.DrawBitmapEx( + aCroppedRectPixel.TopLeft(), + aDestination); + + rOutDev.EnableMapMode(bWasEnabled); + } + } + } + } +} // end of namespace drawinglayer + +////////////////////////////////////////////////////////////////////////////// +// eof diff --git a/drawinglayer/source/processor2d/vclhelperbitmaprender.hxx b/drawinglayer/source/processor2d/vclhelperbitmaprender.hxx new file mode 100644 index 000000000000..915e40427aeb --- /dev/null +++ b/drawinglayer/source/processor2d/vclhelperbitmaprender.hxx @@ -0,0 +1,66 @@ +/************************************************************************* + * + * 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. + * + ************************************************************************/ + +#ifndef INCLUDED_DRAWINGLAYER_PROCESSOR2D_VCLHELPERBITMAPRENDER_HXX +#define INCLUDED_DRAWINGLAYER_PROCESSOR2D_VCLHELPERBITMAPRENDER_HXX + +#include <sal/types.h> + +////////////////////////////////////////////////////////////////////////////// +// predefines + +class OutputDevice; +class BitmapEx; +namespace basegfx { class B2DHomMatrix; } + +////////////////////////////////////////////////////////////////////////////// +// support methods for vcl direct gradient renderering + +namespace drawinglayer +{ + void RenderBitmapPrimitive2D_GraphicManager( + OutputDevice& rOutDev, + const BitmapEx& rBitmapEx, + const basegfx::B2DHomMatrix& rTransform); + + void RenderBitmapPrimitive2D_BitmapEx( + OutputDevice& rOutDev, + const BitmapEx& rBitmapEx, + const basegfx::B2DHomMatrix& rTransform); + + void RenderBitmapPrimitive2D_self( + OutputDevice& rOutDev, + const BitmapEx& rBitmapEx, + const basegfx::B2DHomMatrix& rTransform); + +} // end of namespace drawinglayer + +////////////////////////////////////////////////////////////////////////////// + +#endif // INCLUDED_DRAWINGLAYER_PROCESSOR2D_VCLHELPERBITMAPRENDER_HXX + +// eof diff --git a/drawinglayer/source/processor2d/vclhelperbitmaptransform.cxx b/drawinglayer/source/processor2d/vclhelperbitmaptransform.cxx new file mode 100644 index 000000000000..ce81fb73ed6f --- /dev/null +++ b/drawinglayer/source/processor2d/vclhelperbitmaptransform.cxx @@ -0,0 +1,431 @@ +/************************************************************************* + * + * 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_drawinglayer.hxx" + +#include <vclhelperbitmaptransform.hxx> +#include <vcl/bmpacc.hxx> +#include <basegfx/point/b2dpoint.hxx> +#include <basegfx/color/bcolormodifier.hxx> + +////////////////////////////////////////////////////////////////////////////// +// support for rendering Bitmap and BitmapEx contents + +namespace drawinglayer +{ + namespace + { + void impSmoothPoint(BitmapColor& rValue, const basegfx::B2DPoint& rSource, sal_Int32 nIntX, sal_Int32 nIntY, BitmapReadAccess& rRead) + { + double fDeltaX(rSource.getX() - nIntX); + double fDeltaY(rSource.getY() - nIntY); + sal_Int32 nIndX(0L); + sal_Int32 nIndY(0L); + + if(fDeltaX > 0.0 && nIntX + 1L < rRead.Width()) + { + nIndX++; + } + else if(fDeltaX < 0.0 && nIntX >= 1L) + { + fDeltaX = -fDeltaX; + nIndX--; + } + + if(fDeltaY > 0.0 && nIntY + 1L < rRead.Height()) + { + nIndY++; + } + else if(fDeltaY < 0.0 && nIntY >= 1L) + { + fDeltaY = -fDeltaY; + nIndY--; + } + + if(nIndX || nIndY) + { + const double fColorToReal(1.0 / 255.0); + double fR(rValue.GetRed() * fColorToReal); + double fG(rValue.GetGreen() * fColorToReal); + double fB(rValue.GetBlue() * fColorToReal); + double fRBottom(0.0), fGBottom(0.0), fBBottom(0.0); + + if(nIndX) + { + const double fMulA(fDeltaX * fColorToReal); + double fMulB(1.0 - fDeltaX); + const BitmapColor aTopPartner(rRead.GetColor(nIntY, nIntX + nIndX)); + + fR = (fR * fMulB) + (aTopPartner.GetRed() * fMulA); + fG = (fG * fMulB) + (aTopPartner.GetGreen() * fMulA); + fB = (fB * fMulB) + (aTopPartner.GetBlue() * fMulA); + + if(nIndY) + { + fMulB *= fColorToReal; + const BitmapColor aBottom(rRead.GetColor(nIntY + nIndY, nIntX)); + const BitmapColor aBottomPartner(rRead.GetColor(nIntY + nIndY, nIntX + nIndX)); + + fRBottom = (aBottom.GetRed() * fMulB) + (aBottomPartner.GetRed() * fMulA); + fGBottom = (aBottom.GetGreen() * fMulB) + (aBottomPartner.GetGreen() * fMulA); + fBBottom = (aBottom.GetBlue() * fMulB) + (aBottomPartner.GetBlue() * fMulA); + } + } + + if(nIndY) + { + if(!nIndX) + { + const BitmapColor aBottom(rRead.GetColor(nIntY + nIndY, nIntX)); + + fRBottom = aBottom.GetRed() * fColorToReal; + fGBottom = aBottom.GetGreen() * fColorToReal; + fBBottom = aBottom.GetBlue() * fColorToReal; + } + + const double fMulB(1.0 - fDeltaY); + + fR = (fR * fMulB) + (fRBottom * fDeltaY); + fG = (fG * fMulB) + (fGBottom * fDeltaY); + fB = (fB * fMulB) + (fBBottom * fDeltaY); + } + + rValue.SetRed((sal_uInt8)(fR * 255.0)); + rValue.SetGreen((sal_uInt8)(fG * 255.0)); + rValue.SetBlue((sal_uInt8)(fB * 255.0)); + } + } + + void impSmoothIndex(BitmapColor& rValue, const basegfx::B2DPoint& rSource, sal_Int32 nIntX, sal_Int32 nIntY, BitmapReadAccess& rRead) + { + double fDeltaX(rSource.getX() - nIntX); + double fDeltaY(rSource.getY() - nIntY); + sal_Int32 nIndX(0L); + sal_Int32 nIndY(0L); + + if(fDeltaX > 0.0 && nIntX + 1L < rRead.Width()) + { + nIndX++; + } + else if(fDeltaX < 0.0 && nIntX >= 1L) + { + fDeltaX = -fDeltaX; + nIndX--; + } + + if(fDeltaY > 0.0 && nIntY + 1L < rRead.Height()) + { + nIndY++; + } + else if(fDeltaY < 0.0 && nIntY >= 1L) + { + fDeltaY = -fDeltaY; + nIndY--; + } + + if(nIndX || nIndY) + { + const double fColorToReal(1.0 / 255.0); + double fVal(rValue.GetIndex() * fColorToReal); + double fValBottom(0.0); + + if(nIndX) + { + const double fMulA(fDeltaX * fColorToReal); + double fMulB(1.0 - fDeltaX); + const BitmapColor aTopPartner(rRead.GetPixel(nIntY, nIntX + nIndX)); + + fVal = (fVal * fMulB) + (aTopPartner.GetIndex() * fMulA); + + if(nIndY) + { + fMulB *= fColorToReal; + const BitmapColor aBottom(rRead.GetPixel(nIntY + nIndY, nIntX)); + const BitmapColor aBottomPartner(rRead.GetPixel(nIntY + nIndY, nIntX + nIndX)); + + fValBottom = (aBottom.GetIndex() * fMulB) + (aBottomPartner.GetIndex() * fMulA); + } + } + + if(nIndY) + { + if(!nIndX) + { + const BitmapColor aBottom(rRead.GetPixel(nIntY + nIndY, nIntX)); + + fValBottom = aBottom.GetIndex() * fColorToReal; + } + + const double fMulB(1.0 - fDeltaY); + + fVal = (fVal * fMulB) + (fValBottom * fDeltaY); + } + + rValue.SetIndex((sal_uInt8)(fVal * 255.0)); + } + } + + void impTransformBitmap(const Bitmap& rSource, Bitmap& rDestination, const basegfx::B2DHomMatrix& rTransform, bool bSmooth) + { + BitmapWriteAccess* pWrite = rDestination.AcquireWriteAccess(); + + if(pWrite) + { + const Size aContentSizePixel(rSource.GetSizePixel()); + BitmapReadAccess* pRead = (const_cast< Bitmap& >(rSource)).AcquireReadAccess(); + + if(pRead) + { + const Size aDestinationSizePixel(rDestination.GetSizePixel()); + bool bWorkWithIndex(rDestination.GetBitCount() <= 8); + BitmapColor aOutside(pRead->GetBestMatchingColor(BitmapColor(0xff, 0xff, 0xff))); + + for(sal_Int32 y(0L); y < aDestinationSizePixel.getHeight(); y++) + { + for(sal_Int32 x(0L); x < aDestinationSizePixel.getWidth(); x++) + { + const basegfx::B2DPoint aSourceCoor(rTransform * basegfx::B2DPoint(x, y)); + const sal_Int32 nIntX(basegfx::fround(aSourceCoor.getX())); + + if(nIntX >= 0L && nIntX < aContentSizePixel.getWidth()) + { + const sal_Int32 nIntY(basegfx::fround(aSourceCoor.getY())); + + if(nIntY >= 0L && nIntY < aContentSizePixel.getHeight()) + { + if(bWorkWithIndex) + { + BitmapColor aValue(pRead->GetPixel(nIntY, nIntX)); + + if(bSmooth) + { + impSmoothIndex(aValue, aSourceCoor, nIntX, nIntY, *pRead); + } + + pWrite->SetPixel(y, x, aValue); + } + else + { + BitmapColor aValue(pRead->GetColor(nIntY, nIntX)); + + if(bSmooth) + { + impSmoothPoint(aValue, aSourceCoor, nIntX, nIntY, *pRead); + } + + pWrite->SetPixel(y, x, aValue.IsIndex() ? aValue : pWrite->GetBestMatchingColor(aValue)); + } + + continue; + } + } + + // here are outside pixels. Complete mask + if(bWorkWithIndex) + { + pWrite->SetPixel(y, x, aOutside); + } + } + } + + delete pRead; + } + + delete pWrite; + } + } + + Bitmap impCreateEmptyBitmapWithPattern(const Bitmap& rSource, const Size& aTargetSizePixel) + { + Bitmap aRetval; + BitmapReadAccess* pReadAccess = (const_cast< Bitmap& >(rSource)).AcquireReadAccess(); + + if(pReadAccess) + { + if(rSource.GetBitCount() <= 8) + { + BitmapPalette aPalette(pReadAccess->GetPalette()); + aRetval = Bitmap(aTargetSizePixel, rSource.GetBitCount(), &aPalette); + } + else + { + aRetval = Bitmap(aTargetSizePixel, rSource.GetBitCount()); + } + + delete pReadAccess; + } + + return aRetval; + } + } // end of anonymous namespace +} // end of namespace drawinglayer + +namespace drawinglayer +{ + BitmapEx impTransformBitmapEx( + const BitmapEx& rSource, + const Rectangle& rCroppedRectPixel, + const basegfx::B2DHomMatrix& rTransform) + { + // force destination to 24 bit, we want to smooth output + const Size aDestinationSize(rCroppedRectPixel.GetSize()); + Bitmap aDestination(impCreateEmptyBitmapWithPattern(rSource.GetBitmap(), aDestinationSize)); + static bool bDoSmoothAtAll(true); + impTransformBitmap(rSource.GetBitmap(), aDestination, rTransform, bDoSmoothAtAll); + + // create mask + if(rSource.IsTransparent()) + { + if(rSource.IsAlpha()) + { + Bitmap aAlpha(impCreateEmptyBitmapWithPattern(rSource.GetAlpha().GetBitmap(), aDestinationSize)); + impTransformBitmap(rSource.GetAlpha().GetBitmap(), aAlpha, rTransform, bDoSmoothAtAll); + return BitmapEx(aDestination, AlphaMask(aAlpha)); + } + else + { + Bitmap aMask(impCreateEmptyBitmapWithPattern(rSource.GetMask(), aDestinationSize)); + impTransformBitmap(rSource.GetMask(), aMask, rTransform, false); + return BitmapEx(aDestination, aMask); + } + } + + return BitmapEx(aDestination); + } + + BitmapEx impModifyBitmapEx( + const basegfx::BColorModifierStack& rBColorModifierStack, + const BitmapEx& rSource) + { + Bitmap aChangedBitmap(rSource.GetBitmap()); + bool bDone(false); + + for(sal_uInt32 a(rBColorModifierStack.count()); a && !bDone; ) + { + const basegfx::BColorModifier& rModifier = rBColorModifierStack.getBColorModifier(--a); + + switch(rModifier.getMode()) + { + case basegfx::BCOLORMODIFYMODE_REPLACE : + { + // complete replace + if(rSource.IsTransparent()) + { + // clear bitmap with dest color + if(aChangedBitmap.GetBitCount() <= 8) + { + // do NOT use erase; for e.g. 8bit Bitmaps, the nearest color to the given + // erase color is determined and used -> this may be different from what is + // wanted here. Better create a new bitmap with the needed color explicitely + BitmapReadAccess* pReadAccess = aChangedBitmap.AcquireReadAccess(); + OSL_ENSURE(pReadAccess, "Got no Bitmap ReadAccess ?!?"); + + if(pReadAccess) + { + BitmapPalette aNewPalette(pReadAccess->GetPalette()); + aNewPalette[0] = BitmapColor(Color(rModifier.getBColor())); + aChangedBitmap = Bitmap( + aChangedBitmap.GetSizePixel(), + aChangedBitmap.GetBitCount(), + &aNewPalette); + delete pReadAccess; + } + } + else + { + aChangedBitmap.Erase(Color(rModifier.getBColor())); + } + } + else + { + // erase bitmap, caller will know to paint direct + aChangedBitmap.SetEmpty(); + } + + bDone = true; + break; + } + + default : // BCOLORMODIFYMODE_INTERPOLATE, BCOLORMODIFYMODE_GRAY, BCOLORMODIFYMODE_BLACKANDWHITE + { + BitmapWriteAccess* pContent = aChangedBitmap.AcquireWriteAccess(); + + if(pContent) + { + const double fConvertColor(1.0 / 255.0); + + for(sal_uInt32 y(0L); y < (sal_uInt32)pContent->Height(); y++) + { + for(sal_uInt32 x(0L); x < (sal_uInt32)pContent->Width(); x++) + { + const BitmapColor aBMCol(pContent->GetColor(y, x)); + const basegfx::BColor aBSource( + (double)aBMCol.GetRed() * fConvertColor, + (double)aBMCol.GetGreen() * fConvertColor, + (double)aBMCol.GetBlue() * fConvertColor); + const basegfx::BColor aBDest(rModifier.getModifiedColor(aBSource)); + + pContent->SetPixel(y, x, BitmapColor(Color(aBDest))); + } + } + + delete pContent; + } + + break; + } + } + } + + if(aChangedBitmap.IsEmpty()) + { + return BitmapEx(); + } + else + { + if(rSource.IsTransparent()) + { + if(rSource.IsAlpha()) + { + return BitmapEx(aChangedBitmap, rSource.GetAlpha()); + } + else + { + return BitmapEx(aChangedBitmap, rSource.GetMask()); + } + } + else + { + return BitmapEx(aChangedBitmap); + } + } + } +} // end of namespace drawinglayer + +////////////////////////////////////////////////////////////////////////////// +// eof diff --git a/drawinglayer/source/processor2d/vclhelperbitmaptransform.hxx b/drawinglayer/source/processor2d/vclhelperbitmaptransform.hxx new file mode 100644 index 000000000000..7a3df9e4fb06 --- /dev/null +++ b/drawinglayer/source/processor2d/vclhelperbitmaptransform.hxx @@ -0,0 +1,60 @@ +/************************************************************************* + * + * 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. + * + ************************************************************************/ + +#ifndef INCLUDED_DRAWINGLAYER_PROCESSOR2D_VCLHELPERBITMAPTRANSFORM_HXX +#define INCLUDED_DRAWINGLAYER_PROCESSOR2D_VCLHELPERBITMAPTRANSFORM_HXX + +#include <vcl/bitmapex.hxx> + +////////////////////////////////////////////////////////////////////////////// +// predefines + +namespace basegfx { + class B2DHomMatrix; + class BColorModifierStack; +} + +////////////////////////////////////////////////////////////////////////////// +// support methods for vcl direct gradient renderering + +namespace drawinglayer +{ + BitmapEx impTransformBitmapEx( + const BitmapEx& rSource, + const Rectangle& rCroppedRectPixel, + const basegfx::B2DHomMatrix& rTransform); + + BitmapEx impModifyBitmapEx( + const basegfx::BColorModifierStack& rBColorModifierStack, + const BitmapEx& rSource); +} // end of namespace drawinglayer + +////////////////////////////////////////////////////////////////////////////// + +#endif // INCLUDED_DRAWINGLAYER_PROCESSOR2D_VCLHELPERBITMAPTRANSFORM_HXX + +// eof diff --git a/drawinglayer/source/processor2d/vclhelperbufferdevice.cxx b/drawinglayer/source/processor2d/vclhelperbufferdevice.cxx new file mode 100644 index 000000000000..87b33b16c53d --- /dev/null +++ b/drawinglayer/source/processor2d/vclhelperbufferdevice.cxx @@ -0,0 +1,182 @@ +/************************************************************************* + * + * 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_drawinglayer.hxx" + +#include <vclhelperbufferdevice.hxx> +#include <basegfx/range/b2drange.hxx> +#include <vcl/bitmapex.hxx> +#include <basegfx/matrix/b2dhommatrix.hxx> +#include <tools/stream.hxx> + +////////////////////////////////////////////////////////////////////////////// +// support for rendering Bitmap and BitmapEx contents + +namespace drawinglayer +{ + impBufferDevice::impBufferDevice( + OutputDevice& rOutDev, + const basegfx::B2DRange& rRange, + bool bAddOffsetToMapping) + : mrOutDev(rOutDev), + maContent(rOutDev), + mpMask(0L), + mpAlpha(0L) + { + basegfx::B2DRange aRangePixel(rRange); + aRangePixel.transform(rOutDev.GetViewTransformation()); + const Rectangle aRectPixel( + (sal_Int32)floor(aRangePixel.getMinX()), (sal_Int32)floor(aRangePixel.getMinY()), + (sal_Int32)ceil(aRangePixel.getMaxX()), (sal_Int32)ceil(aRangePixel.getMaxY())); + const Point aEmptyPoint; + maDestPixel = Rectangle(aEmptyPoint, rOutDev.GetOutputSizePixel()); + maDestPixel.Intersection(aRectPixel); + + if(isVisible()) + { + maContent.SetOutputSizePixel(maDestPixel.GetSize(), false); + + // #i93485# assert when copying from window to VDev is used + OSL_ENSURE(rOutDev.GetOutDevType() != OUTDEV_WINDOW, + "impBufferDevice render helper: Copying from Window to VDev, this should be avoided (!)"); + + const bool bWasEnabledSrc(rOutDev.IsMapModeEnabled()); + rOutDev.EnableMapMode(false); + maContent.DrawOutDev(aEmptyPoint, maDestPixel.GetSize(), maDestPixel.TopLeft(), maDestPixel.GetSize(), rOutDev); + rOutDev.EnableMapMode(bWasEnabledSrc); + + MapMode aNewMapMode(rOutDev.GetMapMode()); + + if(bAddOffsetToMapping) + { + const Point aLogicTopLeft(rOutDev.PixelToLogic(maDestPixel.TopLeft())); + aNewMapMode.SetOrigin(Point(-aLogicTopLeft.X(), -aLogicTopLeft.Y())); + } + + maContent.SetMapMode(aNewMapMode); + + // copy AA flag for new target + maContent.SetAntialiasing(mrOutDev.GetAntialiasing()); + } + } + + impBufferDevice::~impBufferDevice() + { + delete mpMask; + delete mpAlpha; + } + + void impBufferDevice::paint(double fTrans) + { + const Point aEmptyPoint; + const Size aSizePixel(maContent.GetOutputSizePixel()); + const bool bWasEnabledDst(mrOutDev.IsMapModeEnabled()); + static bool bDoSaveForVisualControl(false); + + mrOutDev.EnableMapMode(false); + maContent.EnableMapMode(false); + Bitmap aContent(maContent.GetBitmap(aEmptyPoint, aSizePixel)); + + if(bDoSaveForVisualControl) + { + SvFileStream aNew((const String&)String(ByteString( "c:\\content.bmp" ), RTL_TEXTENCODING_UTF8), STREAM_WRITE|STREAM_TRUNC); + aNew << aContent; + } + + if(mpAlpha) + { + mpAlpha->EnableMapMode(false); + const AlphaMask aAlphaMask(mpAlpha->GetBitmap(aEmptyPoint, aSizePixel)); + + if(bDoSaveForVisualControl) + { + SvFileStream aNew((const String&)String(ByteString( "c:\\transparence.bmp" ), RTL_TEXTENCODING_UTF8), STREAM_WRITE|STREAM_TRUNC); + aNew << aAlphaMask.GetBitmap(); + } + + mrOutDev.DrawBitmapEx(maDestPixel.TopLeft(), BitmapEx(aContent, aAlphaMask)); + } + else if(mpMask) + { + mpMask->EnableMapMode(false); + const Bitmap aMask(mpMask->GetBitmap(aEmptyPoint, aSizePixel)); + + if(bDoSaveForVisualControl) + { + SvFileStream aNew((const String&)String(ByteString( "c:\\mask.bmp" ), RTL_TEXTENCODING_UTF8), STREAM_WRITE|STREAM_TRUNC); + aNew << aMask; + } + + mrOutDev.DrawBitmapEx(maDestPixel.TopLeft(), BitmapEx(aContent, aMask)); + } + else if(0.0 != fTrans) + { + sal_uInt8 nMaskValue((sal_uInt8)basegfx::fround(fTrans * 255.0)); + const AlphaMask aAlphaMask(aSizePixel, &nMaskValue); + mrOutDev.DrawBitmapEx(maDestPixel.TopLeft(), BitmapEx(aContent, aAlphaMask)); + } + else + { + mrOutDev.DrawBitmap(maDestPixel.TopLeft(), aContent); + } + + mrOutDev.EnableMapMode(bWasEnabledDst); + } + + VirtualDevice& impBufferDevice::getMask() + { + if(!mpMask) + { + mpMask = new VirtualDevice(mrOutDev, 1); + mpMask->SetOutputSizePixel(maDestPixel.GetSize(), true); + mpMask->SetMapMode(maContent.GetMapMode()); + + // do NOT copy AA flag for mask! + } + + return *mpMask; + } + + VirtualDevice& impBufferDevice::getTransparence() + { + if(!mpAlpha) + { + mpAlpha = new VirtualDevice(); + mpAlpha->SetOutputSizePixel(maDestPixel.GetSize(), true); + mpAlpha->SetMapMode(maContent.GetMapMode()); + + // copy AA flag for new target; masking needs to be smooth + mpAlpha->SetAntialiasing(maContent.GetAntialiasing()); + } + + return *mpAlpha; + } +} // end of namespace drawinglayer + +////////////////////////////////////////////////////////////////////////////// +// eof diff --git a/drawinglayer/source/processor2d/vclhelperbufferdevice.hxx b/drawinglayer/source/processor2d/vclhelperbufferdevice.hxx new file mode 100644 index 000000000000..857b673e36ad --- /dev/null +++ b/drawinglayer/source/processor2d/vclhelperbufferdevice.hxx @@ -0,0 +1,70 @@ +/************************************************************************* + * + * 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. + * + ************************************************************************/ + +#ifndef INCLUDED_DRAWINGLAYER_PROCESSOR2D_VCLHELPERBUFFERDEVICE_HXX +#define INCLUDED_DRAWINGLAYER_PROCESSOR2D_VCLHELPERBUFFERDEVICE_HXX + +#include <vcl/virdev.hxx> + +////////////////////////////////////////////////////////////////////////////// +// predefines + +namespace basegfx { class B2DRange; } + +////////////////////////////////////////////////////////////////////////////// +// support methods for vcl direct gradient renderering + +namespace drawinglayer +{ + class impBufferDevice + { + OutputDevice& mrOutDev; + VirtualDevice maContent; + VirtualDevice* mpMask; + VirtualDevice* mpAlpha; + Rectangle maDestPixel; + + public: + impBufferDevice( + OutputDevice& rOutDev, + const basegfx::B2DRange& rRange, + bool bAddOffsetToMapping); + ~impBufferDevice(); + + void paint(double fTrans = 0.0); + bool isVisible() const { return !maDestPixel.IsEmpty(); } + VirtualDevice& getContent() { return maContent; } + VirtualDevice& getMask(); + VirtualDevice& getTransparence(); + }; +} // end of namespace drawinglayer + +////////////////////////////////////////////////////////////////////////////// + +#endif // INCLUDED_DRAWINGLAYER_PROCESSOR2D_VCLHELPERBUFFERDEVICE_HXX + +// eof diff --git a/drawinglayer/source/processor2d/vclhelpergradient.cxx b/drawinglayer/source/processor2d/vclhelpergradient.cxx new file mode 100644 index 000000000000..6c318dbcedc9 --- /dev/null +++ b/drawinglayer/source/processor2d/vclhelpergradient.cxx @@ -0,0 +1,285 @@ +/************************************************************************* + * + * 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_drawinglayer.hxx" + +#include <vclhelpergradient.hxx> +#include <basegfx/range/b2drange.hxx> +#include <vcl/outdev.hxx> +#include <basegfx/polygon/b2dpolygon.hxx> +#include <basegfx/polygon/b2dpolypolygontools.hxx> +#include <basegfx/polygon/b2dpolygontools.hxx> +#include <drawinglayer/texture/texture.hxx> + +////////////////////////////////////////////////////////////////////////////// +// support methods for vcl direct gradient renderering + +namespace drawinglayer +{ + namespace + { + sal_uInt32 impCalcGradientSteps(OutputDevice& rOutDev, sal_uInt32 nSteps, const basegfx::B2DRange& rRange, sal_uInt32 nMaxDist) + { + if(nSteps == 0L) + { + const Size aSize(rOutDev.LogicToPixel(Size(basegfx::fround(rRange.getWidth()), basegfx::fround(rRange.getHeight())))); + nSteps = (aSize.getWidth() + aSize.getHeight()) >> 3L; + } + + if(nSteps < 2L) + { + nSteps = 2L; + } + + if(nSteps > nMaxDist) + { + nSteps = nMaxDist; + } + + return nSteps; + } + + void impDrawGradientToOutDevSimple( + OutputDevice& rOutDev, + const basegfx::B2DPolyPolygon& rTargetForm, + const ::std::vector< basegfx::B2DHomMatrix >& rMatrices, + const ::std::vector< basegfx::BColor >& rColors, + const basegfx::B2DPolygon& rUnitPolygon) + { + rOutDev.SetLineColor(); + + for(sal_uInt32 a(0L); a < rColors.size(); a++) + { + // set correct color + const basegfx::BColor aFillColor(rColors[a]); + rOutDev.SetFillColor(Color(aFillColor)); + + if(a) + { + if(a - 1L < static_cast< sal_uInt32 >(rMatrices.size())) + { + basegfx::B2DPolygon aNewPoly(rUnitPolygon); + aNewPoly.transform(rMatrices[a - 1L]); + rOutDev.DrawPolygon(aNewPoly); + } + } + else + { + rOutDev.DrawPolyPolygon(rTargetForm); + } + } + } + + void impDrawGradientToOutDevComplex( + OutputDevice& rOutDev, + const basegfx::B2DPolyPolygon& rTargetForm, + const ::std::vector< basegfx::B2DHomMatrix >& rMatrices, + const ::std::vector< basegfx::BColor >& rColors, + const basegfx::B2DPolygon& rUnitPolygon) + { + PolyPolygon aVclTargetForm(rTargetForm); + ::std::vector< Polygon > aVclPolygons; + sal_uInt32 a; + + // remember and set to XOR + rOutDev.SetLineColor(); + rOutDev.Push(PUSH_RASTEROP); + rOutDev.SetRasterOp(ROP_XOR); + + // draw gradient PolyPolygons + for(a = 0L; a < rMatrices.size(); a++) + { + // create polygon and remember + basegfx::B2DPolygon aNewPoly(rUnitPolygon); + aNewPoly.transform(rMatrices[a]); + aVclPolygons.push_back(Polygon(aNewPoly)); + + // set correct color + if(rColors.size() > a) + { + const basegfx::BColor aFillColor(rColors[a]); + rOutDev.SetFillColor(Color(aFillColor)); + } + + // create vcl PolyPolygon and draw it + if(a) + { + PolyPolygon aVclPolyPoly(aVclPolygons[a - 1L]); + aVclPolyPoly.Insert(aVclPolygons[a]); + rOutDev.DrawPolyPolygon(aVclPolyPoly); + } + else + { + PolyPolygon aVclPolyPoly(aVclTargetForm); + aVclPolyPoly.Insert(aVclPolygons[0L]); + rOutDev.DrawPolyPolygon(aVclPolyPoly); + } + } + + // draw last poly in last color + if(rColors.size()) + { + const basegfx::BColor aFillColor(rColors[rColors.size() - 1L]); + rOutDev.SetFillColor(Color(aFillColor)); + rOutDev.DrawPolygon(aVclPolygons[aVclPolygons.size() - 1L]); + } + + // draw object form in black and go back to XOR + rOutDev.SetFillColor(COL_BLACK); + rOutDev.SetRasterOp(ROP_0); + rOutDev.DrawPolyPolygon(aVclTargetForm); + rOutDev.SetRasterOp(ROP_XOR); + + // draw gradient PolyPolygons again + for(a = 0L; a < rMatrices.size(); a++) + { + // set correct color + if(rColors.size() > a) + { + const basegfx::BColor aFillColor(rColors[a]); + rOutDev.SetFillColor(Color(aFillColor)); + } + + // create vcl PolyPolygon and draw it + if(a) + { + PolyPolygon aVclPolyPoly(aVclPolygons[a - 1L]); + aVclPolyPoly.Insert(aVclPolygons[a]); + rOutDev.DrawPolyPolygon(aVclPolyPoly); + } + else + { + PolyPolygon aVclPolyPoly(aVclTargetForm); + aVclPolyPoly.Insert(aVclPolygons[0L]); + rOutDev.DrawPolyPolygon(aVclPolyPoly); + } + } + + // draw last poly in last color + if(rColors.size()) + { + const basegfx::BColor aFillColor(rColors[rColors.size() - 1L]); + rOutDev.SetFillColor(Color(aFillColor)); + rOutDev.DrawPolygon(aVclPolygons[aVclPolygons.size() - 1L]); + } + + // reset drawmode + rOutDev.Pop(); + } + } // end of anonymous namespace +} // end of namespace drawinglayer + +namespace drawinglayer +{ + void impDrawGradientToOutDev( + OutputDevice& rOutDev, + const basegfx::B2DPolyPolygon& rTargetForm, + attribute::GradientStyle eGradientStyle, + sal_uInt32 nSteps, + const basegfx::BColor& rStart, + const basegfx::BColor& rEnd, + double fBorder, double fAngle, double fOffsetX, double fOffsetY, bool bSimple) + { + const basegfx::B2DRange aOutlineRange(basegfx::tools::getRange(rTargetForm)); + ::std::vector< basegfx::B2DHomMatrix > aMatrices; + ::std::vector< basegfx::BColor > aColors; + basegfx::B2DPolygon aUnitPolygon; + + // make sure steps is not too high/low + nSteps = impCalcGradientSteps(rOutDev, nSteps, aOutlineRange, sal_uInt32((rStart.getMaximumDistance(rEnd) * 127.5) + 0.5)); + + // create geometries + switch(eGradientStyle) + { + case attribute::GRADIENTSTYLE_LINEAR: + { + texture::GeoTexSvxGradientLinear aGradient(aOutlineRange, rStart, rEnd, nSteps, fBorder, fAngle); + aGradient.appendTransformations(aMatrices); + aGradient.appendColors(aColors); + aUnitPolygon = basegfx::tools::createUnitPolygon(); + break; + } + case attribute::GRADIENTSTYLE_AXIAL: + { + texture::GeoTexSvxGradientAxial aGradient(aOutlineRange, rStart, rEnd, nSteps, fBorder, fAngle); + aGradient.appendTransformations(aMatrices); + aGradient.appendColors(aColors); + aUnitPolygon = basegfx::tools::createPolygonFromRect(basegfx::B2DRange(-1, -1, 1, 1)); + break; + } + case attribute::GRADIENTSTYLE_RADIAL: + { + texture::GeoTexSvxGradientRadial aGradient(aOutlineRange, rStart, rEnd, nSteps, fBorder, fOffsetX, fOffsetY); + aGradient.appendTransformations(aMatrices); + aGradient.appendColors(aColors); + aUnitPolygon = basegfx::tools::createPolygonFromCircle(basegfx::B2DPoint(0,0), 1); + break; + } + case attribute::GRADIENTSTYLE_ELLIPTICAL: + { + texture::GeoTexSvxGradientElliptical aGradient(aOutlineRange, rStart, rEnd, nSteps, fBorder, fOffsetX, fOffsetX, fAngle); + aGradient.appendTransformations(aMatrices); + aGradient.appendColors(aColors); + aUnitPolygon = basegfx::tools::createPolygonFromCircle(basegfx::B2DPoint(0,0), 1); + break; + } + case attribute::GRADIENTSTYLE_SQUARE: + { + texture::GeoTexSvxGradientSquare aGradient(aOutlineRange, rStart, rEnd, nSteps, fBorder, fOffsetX, fOffsetX, fAngle); + aGradient.appendTransformations(aMatrices); + aGradient.appendColors(aColors); + aUnitPolygon = basegfx::tools::createPolygonFromRect(basegfx::B2DRange(-1, -1, 1, 1)); + break; + } + case attribute::GRADIENTSTYLE_RECT: + { + texture::GeoTexSvxGradientRect aGradient(aOutlineRange, rStart, rEnd, nSteps, fBorder, fOffsetX, fOffsetX, fAngle); + aGradient.appendTransformations(aMatrices); + aGradient.appendColors(aColors); + aUnitPolygon = basegfx::tools::createPolygonFromRect(basegfx::B2DRange(-1, -1, 1, 1)); + break; + } + } + + // paint them with mask using the XOR method + if(aMatrices.size()) + { + if(bSimple) + { + impDrawGradientToOutDevSimple(rOutDev, rTargetForm, aMatrices, aColors, aUnitPolygon); + } + else + { + impDrawGradientToOutDevComplex(rOutDev, rTargetForm, aMatrices, aColors, aUnitPolygon); + } + } + } +} // end of namespace drawinglayer + +////////////////////////////////////////////////////////////////////////////// +// eof diff --git a/drawinglayer/source/processor2d/vclhelpergradient.hxx b/drawinglayer/source/processor2d/vclhelpergradient.hxx new file mode 100644 index 000000000000..d02b20d54e4e --- /dev/null +++ b/drawinglayer/source/processor2d/vclhelpergradient.hxx @@ -0,0 +1,62 @@ +/************************************************************************* + * + * 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. + * + ************************************************************************/ + +#ifndef INCLUDED_DRAWINGLAYER_PROCESSOR2D_VCLHELPERGRADIENT_HXX +#define INCLUDED_DRAWINGLAYER_PROCESSOR2D_VCLHELPERGRADIENT_HXX + +#include <sal/types.h> +#include <drawinglayer/attribute/fillgradientattribute.hxx> + +////////////////////////////////////////////////////////////////////////////// +// predefines + +class OutputDevice; +namespace basegfx { + class B2DPolyPolygon; + class BColor; +} + +////////////////////////////////////////////////////////////////////////////// +// support methods for vcl direct gradient renderering + +namespace drawinglayer +{ + void impDrawGradientToOutDev( + OutputDevice& rOutDev, + const basegfx::B2DPolyPolygon& rTargetForm, + attribute::GradientStyle eGradientStyle, + sal_uInt32 nSteps, + const basegfx::BColor& rStart, + const basegfx::BColor& rEnd, + double fBorder, double fAngle, double fOffsetX, double fOffsetY, bool bSimple); +} // end of namespace drawinglayer + +////////////////////////////////////////////////////////////////////////////// + +#endif // INCLUDED_DRAWINGLAYER_PROCESSOR2D_VCLHELPERGRADIENT_HXX + +// eof diff --git a/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx b/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx new file mode 100644 index 000000000000..0d2be85f9872 --- /dev/null +++ b/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx @@ -0,0 +1,2017 @@ +/************************************************************************* + * + * 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_drawinglayer.hxx" + +#include <drawinglayer/processor2d/vclmetafileprocessor2d.hxx> +#include <tools/gen.hxx> +#include <vcl/virdev.hxx> +#include <vcl/gdimtf.hxx> +#include <vcl/gradient.hxx> +#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx> +#include <drawinglayer/primitive2d/textprimitive2d.hxx> +#include <drawinglayer/primitive2d/polypolygonprimitive2d.hxx> +#include <drawinglayer/primitive2d/polygonprimitive2d.hxx> +#include <drawinglayer/primitive2d/bitmapprimitive2d.hxx> +#include <drawinglayer/primitive2d/metafileprimitive2d.hxx> +#include <drawinglayer/primitive2d/maskprimitive2d.hxx> +#include <basegfx/polygon/b2dpolygonclipper.hxx> +#include <basegfx/polygon/b2dpolypolygontools.hxx> +#include <drawinglayer/primitive2d/modifiedcolorprimitive2d.hxx> +#include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx> +#include <drawinglayer/primitive2d/transparenceprimitive2d.hxx> +#include <drawinglayer/primitive2d/fillgradientprimitive2d.hxx> +#include <drawinglayer/processor2d/vclpixelprocessor2d.hxx> +#include <tools/stream.hxx> +#include <drawinglayer/primitive2d/transformprimitive2d.hxx> +#include <drawinglayer/primitive2d/markerarrayprimitive2d.hxx> +#include <drawinglayer/primitive2d/pointarrayprimitive2d.hxx> +#include <vcl/graphictools.hxx> +#include <vcl/metaact.hxx> +#include <drawinglayer/primitive2d/texthierarchyprimitive2d.hxx> +#include <drawinglayer/primitive2d/textdecoratedprimitive2d.hxx> +#include <comphelper/processfactory.hxx> +#include <rtl/ustring.hxx> +#include <com/sun/star/i18n/CharacterIteratorMode.hdl> +#include <com/sun/star/i18n/WordType.hpp> +#include <drawinglayer/primitive2d/controlprimitive2d.hxx> +#include <drawinglayer/primitive2d/graphicprimitive2d.hxx> +#include <basegfx/polygon/b2dpolygontools.hxx> +#include <drawinglayer/primitive2d/pagepreviewprimitive2d.hxx> +#include <helperchartrenderer.hxx> +#include <drawinglayer/primitive2d/epsprimitive2d.hxx> +#include <basegfx/polygon/b2dlinegeometry.hxx> + +////////////////////////////////////////////////////////////////////////////// +// for PDFExtOutDevData Graphic support + +#include <vcl/graph.hxx> +#include <vcl/svapp.hxx> +#include <toolkit/helper/formpdfexport.hxx> + +////////////////////////////////////////////////////////////////////////////// +// for Control printing + +#include <com/sun/star/beans/XPropertySet.hpp> + +////////////////////////////////////////////////////////////////////////////// +// for current chart PrettyPrinting support + +#include <drawinglayer/primitive2d/chartprimitive2d.hxx> + +////////////////////////////////////////////////////////////////////////////// +// for StructureTagPrimitive support in sd's unomodel.cxx + +#include <drawinglayer/primitive2d/structuretagprimitive2d.hxx> + +////////////////////////////////////////////////////////////////////////////// + +using namespace com::sun::star; + +////////////////////////////////////////////////////////////////////////////// +// #112245# definition for maximum allowed point count due to Metafile target. +// To be on the safe side with the old tools polygon, use slightly less then +// the theoretical maximum (bad experiences with tools polygon) + +#define MAX_POLYGON_POINT_COUNT_METAFILE (0x0000fff0) + +////////////////////////////////////////////////////////////////////////////// + +namespace +{ + // #112245# helper to split line polygon in half + void splitLinePolygon( + const basegfx::B2DPolygon& rBasePolygon, + basegfx::B2DPolygon& o_aLeft, + basegfx::B2DPolygon& o_aRight) + { + const sal_uInt32 nCount(rBasePolygon.count()); + + if(nCount) + { + const sal_uInt32 nHalfCount((nCount - 1) >> 1); + + o_aLeft = basegfx::B2DPolygon(rBasePolygon, 0, nHalfCount + 1); + o_aLeft.setClosed(false); + + o_aRight = basegfx::B2DPolygon(rBasePolygon, nHalfCount, nCount - nHalfCount); + o_aRight.setClosed(false); + + if(rBasePolygon.isClosed()) + { + o_aRight.append(rBasePolygon.getB2DPoint(0)); + + if(rBasePolygon.areControlPointsUsed()) + { + o_aRight.setControlPoints( + o_aRight.count() - 1, + rBasePolygon.getPrevControlPoint(0), + rBasePolygon.getNextControlPoint(0)); + } + } + } + else + { + o_aLeft.clear(); + o_aRight.clear(); + } + } + + // #112245# helper to evtl. split filled polygons to maximum metafile point count + bool fillPolyPolygonNeededToBeSplit(basegfx::B2DPolyPolygon& rPolyPolygon) + { + bool bRetval(false); + const sal_uInt32 nPolyCount(rPolyPolygon.count()); + + if(nPolyCount) + { + basegfx::B2DPolyPolygon aSplitted; + + for(sal_uInt32 a(0); a < nPolyCount; a++) + { + const basegfx::B2DPolygon aCandidate(rPolyPolygon.getB2DPolygon(a)); + const sal_uInt32 nPointCount(aCandidate.count()); + bool bNeedToSplit(false); + + if(aCandidate.areControlPointsUsed()) + { + // compare with the maximum for bezier curved polygons + bNeedToSplit = nPointCount > ((MAX_POLYGON_POINT_COUNT_METAFILE / 3L) - 1L); + } + else + { + // compare with the maximum for simple point polygons + bNeedToSplit = nPointCount > (MAX_POLYGON_POINT_COUNT_METAFILE - 1); + } + + if(bNeedToSplit) + { + // need to split the partial polygon + const basegfx::B2DRange aRange(aCandidate.getB2DRange()); + const basegfx::B2DPoint aCenter(aRange.getCenter()); + + if(aRange.getWidth() > aRange.getHeight()) + { + // clip in left and right + const basegfx::B2DPolyPolygon aLeft( + basegfx::tools::clipPolygonOnParallelAxis( + aCandidate, + false, + true, + aCenter.getX(), + false)); + const basegfx::B2DPolyPolygon aRight( + basegfx::tools::clipPolygonOnParallelAxis( + aCandidate, + false, + false, + aCenter.getX(), + false)); + + aSplitted.append(aLeft); + aSplitted.append(aRight); + } + else + { + // clip in top and bottom + const basegfx::B2DPolyPolygon aTop( + basegfx::tools::clipPolygonOnParallelAxis( + aCandidate, + true, + true, + aCenter.getY(), + false)); + const basegfx::B2DPolyPolygon aBottom( + basegfx::tools::clipPolygonOnParallelAxis( + aCandidate, + true, + false, + aCenter.getY(), + false)); + + aSplitted.append(aTop); + aSplitted.append(aBottom); + } + } + else + { + aSplitted.append(aCandidate); + } + } + + if(aSplitted.count() != nPolyCount) + { + rPolyPolygon = aSplitted; + } + } + + return bRetval; + } +} // end of anonymous namespace + +////////////////////////////////////////////////////////////////////////////// + +namespace drawinglayer +{ + namespace processor2d + { + Rectangle VclMetafileProcessor2D::impDumpToMetaFile( + const primitive2d::Primitive2DSequence& rContent, + GDIMetaFile& o_rContentMetafile) + { + // Prepare VDev, MetaFile and connections + OutputDevice* pLastOutputDevice = mpOutputDevice; + GDIMetaFile* pLastMetafile = mpMetaFile; + basegfx::B2DRange aPrimitiveRange(primitive2d::getB2DRangeFromPrimitive2DSequence(rContent, getViewInformation2D())); + + // transform primitive range with current transformation (e.g shadow offset) + aPrimitiveRange.transform(maCurrentTransformation); + + const Rectangle aPrimitiveRectangle( + basegfx::fround(aPrimitiveRange.getMinX()), basegfx::fround(aPrimitiveRange.getMinY()), + basegfx::fround(aPrimitiveRange.getMaxX()), basegfx::fround(aPrimitiveRange.getMaxY())); + VirtualDevice aContentVDev; + MapMode aNewMapMode(pLastOutputDevice->GetMapMode()); + + mpOutputDevice = &aContentVDev; + mpMetaFile = &o_rContentMetafile; + aContentVDev.EnableOutput(false); + aContentVDev.SetMapMode(pLastOutputDevice->GetMapMode()); + o_rContentMetafile.Record(&aContentVDev); + aContentVDev.SetLineColor(pLastOutputDevice->GetLineColor()); + aContentVDev.SetFillColor(pLastOutputDevice->GetFillColor()); + aContentVDev.SetFont(pLastOutputDevice->GetFont()); + aContentVDev.SetDrawMode(pLastOutputDevice->GetDrawMode()); + aContentVDev.SetSettings(pLastOutputDevice->GetSettings()); + aContentVDev.SetRefPoint(pLastOutputDevice->GetRefPoint()); + + // dump to MetaFile + process(rContent); + + // cleanups + o_rContentMetafile.Stop(); + o_rContentMetafile.WindStart(); + aNewMapMode.SetOrigin(aPrimitiveRectangle.TopLeft()); + o_rContentMetafile.SetPrefMapMode(aNewMapMode); + o_rContentMetafile.SetPrefSize(aPrimitiveRectangle.GetSize()); + mpOutputDevice = pLastOutputDevice; + mpMetaFile = pLastMetafile; + + return aPrimitiveRectangle; + } + + void VclMetafileProcessor2D::impConvertFillGradientAttributeToVCLGradient( + Gradient& o_rVCLGradient, + const attribute::FillGradientAttribute& rFiGrAtt, + bool bIsTransparenceGradient) + { + if(bIsTransparenceGradient) + { + // it's about transparence channel intensities (black/white), do not use color modifier + o_rVCLGradient.SetStartColor(Color(rFiGrAtt.getStartColor())); + o_rVCLGradient.SetEndColor(Color(rFiGrAtt.getEndColor())); + } + else + { + // use color modifier to influence start/end color of gradient + o_rVCLGradient.SetStartColor(Color(maBColorModifierStack.getModifiedColor(rFiGrAtt.getStartColor()))); + o_rVCLGradient.SetEndColor(Color(maBColorModifierStack.getModifiedColor(rFiGrAtt.getEndColor()))); + } + + o_rVCLGradient.SetAngle(static_cast< sal_uInt16 >(rFiGrAtt.getAngle() * (1.0 / F_PI1800))); + o_rVCLGradient.SetBorder(static_cast< sal_uInt16 >(rFiGrAtt.getBorder() * 100.0)); + o_rVCLGradient.SetOfsX(static_cast< sal_uInt16 >(rFiGrAtt.getOffsetX() * 100.0)); + o_rVCLGradient.SetOfsY(static_cast< sal_uInt16 >(rFiGrAtt.getOffsetY() * 100.0)); + o_rVCLGradient.SetSteps(rFiGrAtt.getSteps()); + + // defaults for intensity; those were computed into the start/end colors already + o_rVCLGradient.SetStartIntensity(100); + o_rVCLGradient.SetEndIntensity(100); + + switch(rFiGrAtt.getStyle()) + { + default : // attribute::GRADIENTSTYLE_LINEAR : + { + o_rVCLGradient.SetStyle(GRADIENT_LINEAR); + break; + } + case attribute::GRADIENTSTYLE_AXIAL : + { + o_rVCLGradient.SetStyle(GRADIENT_AXIAL); + break; + } + case attribute::GRADIENTSTYLE_RADIAL : + { + o_rVCLGradient.SetStyle(GRADIENT_RADIAL); + break; + } + case attribute::GRADIENTSTYLE_ELLIPTICAL : + { + o_rVCLGradient.SetStyle(GRADIENT_ELLIPTICAL); + break; + } + case attribute::GRADIENTSTYLE_SQUARE : + { + o_rVCLGradient.SetStyle(GRADIENT_SQUARE); + break; + } + case attribute::GRADIENTSTYLE_RECT : + { + o_rVCLGradient.SetStyle(GRADIENT_RECT); + break; + } + } + } + + void VclMetafileProcessor2D::impStartSvtGraphicFill(SvtGraphicFill* pSvtGraphicFill) + { + if(pSvtGraphicFill && !mnSvtGraphicFillCount) + { + SvMemoryStream aMemStm; + + aMemStm << *pSvtGraphicFill; + mpMetaFile->AddAction(new MetaCommentAction("XPATHFILL_SEQ_BEGIN", 0, static_cast< const BYTE* >(aMemStm.GetData()), aMemStm.Seek(STREAM_SEEK_TO_END))); + mnSvtGraphicFillCount++; + } + } + + void VclMetafileProcessor2D::impEndSvtGraphicFill(SvtGraphicFill* pSvtGraphicFill) + { + if(pSvtGraphicFill && mnSvtGraphicFillCount) + { + mnSvtGraphicFillCount--; + mpMetaFile->AddAction(new MetaCommentAction("XPATHFILL_SEQ_END")); + delete pSvtGraphicFill; + } + } + + SvtGraphicStroke* VclMetafileProcessor2D::impTryToCreateSvtGraphicStroke( + const basegfx::B2DPolygon& rB2DPolygon, + const basegfx::BColor* pColor, + const attribute::LineAttribute* pLineAttribute, + const attribute::StrokeAttribute* pStrokeAttribute, + const attribute::LineStartEndAttribute* pStart, + const attribute::LineStartEndAttribute* pEnd) + { + SvtGraphicStroke* pRetval = 0; + + if(rB2DPolygon.count() && !mnSvtGraphicStrokeCount) + { + basegfx::BColor aStrokeColor; + basegfx::B2DPolyPolygon aStartArrow; + basegfx::B2DPolyPolygon aEndArrow; + + if(pColor) + { + aStrokeColor = *pColor; + } + else if(pLineAttribute) + { + aStrokeColor = maBColorModifierStack.getModifiedColor(pLineAttribute->getColor()); + } + + // It IS needed to record the stroke color at all in the metafile, + // SvtGraphicStroke has NO entry for stroke color(!) + mpOutputDevice->SetLineColor(Color(aStrokeColor)); + + if(!rB2DPolygon.isClosed()) + { + double fPolyLength(0.0); + + if(pStart && pStart->isActive()) + { + fPolyLength = basegfx::tools::getLength(rB2DPolygon); + + aStartArrow = basegfx::tools::createAreaGeometryForLineStartEnd( + rB2DPolygon, pStart->getB2DPolyPolygon(), true, pStart->getWidth(), + fPolyLength, pStart->isCentered() ? 0.5 : 0.0, 0); + } + + if(pEnd && pEnd->isActive()) + { + if(basegfx::fTools::equalZero(fPolyLength)) + { + fPolyLength = basegfx::tools::getLength(rB2DPolygon); + } + + aEndArrow = basegfx::tools::createAreaGeometryForLineStartEnd( + rB2DPolygon, pEnd->getB2DPolyPolygon(), false, pEnd->getWidth(), + fPolyLength, pEnd->isCentered() ? 0.5 : 0.0, 0); + } + } + + SvtGraphicStroke::JoinType eJoin(SvtGraphicStroke::joinNone); + double fLineWidth(0.0); + double fMiterLength(0.0); + SvtGraphicStroke::DashArray aDashArray; + + if(pLineAttribute) + { + // pre-fill fLineWidth + fLineWidth = pLineAttribute->getWidth(); + + // pre-fill fMiterLength + fMiterLength = fLineWidth; + + // get Join + switch(pLineAttribute->getLineJoin()) + { + default : // basegfx::B2DLINEJOIN_NONE : + { + eJoin = SvtGraphicStroke::joinNone; + break; + } + case basegfx::B2DLINEJOIN_BEVEL : + { + eJoin = SvtGraphicStroke::joinBevel; + break; + } + case basegfx::B2DLINEJOIN_MIDDLE : + case basegfx::B2DLINEJOIN_MITER : + { + eJoin = SvtGraphicStroke::joinMiter; + // ATM 15 degrees is assumed + fMiterLength /= rtl::math::sin(M_PI * (15.0 / 360.0)); + break; + } + case basegfx::B2DLINEJOIN_ROUND : + { + eJoin = SvtGraphicStroke::joinRound; + break; + } + } + } + + if(pStrokeAttribute) + { + // copy dash array + aDashArray = pStrokeAttribute->getDotDashArray(); + } + + // #i101734# apply current object transformation to created geometry. + // This is a partial fix. When a object transformation is used which + // e.g. contains a scaleX != scaleY, an unproportional scaling would + // have to be applied to the evtl. existing fat line. The current + // concept of PDF export and SvtGraphicStroke usage does simply not + // allow handling such definitions. The only clean way would be to + // add the transformation to SvtGraphicStroke and to handle it there + basegfx::B2DPolygon aB2DPolygon(rB2DPolygon); + + aB2DPolygon.transform(maCurrentTransformation); + aStartArrow.transform(maCurrentTransformation); + aEndArrow.transform(maCurrentTransformation); + + pRetval = new SvtGraphicStroke( + Polygon(aB2DPolygon), + PolyPolygon(aStartArrow), + PolyPolygon(aEndArrow), + mfCurrentUnifiedTransparence, + fLineWidth, + SvtGraphicStroke::capButt, + eJoin, + fMiterLength, + aDashArray); + } + + return pRetval; + } + + void VclMetafileProcessor2D::impStartSvtGraphicStroke(SvtGraphicStroke* pSvtGraphicStroke) + { + if(pSvtGraphicStroke && !mnSvtGraphicStrokeCount) + { + SvMemoryStream aMemStm; + + aMemStm << *pSvtGraphicStroke; + mpMetaFile->AddAction(new MetaCommentAction("XPATHSTROKE_SEQ_BEGIN", 0, static_cast< const BYTE* >(aMemStm.GetData()), aMemStm.Seek(STREAM_SEEK_TO_END))); + mnSvtGraphicStrokeCount++; + } + } + + void VclMetafileProcessor2D::impEndSvtGraphicStroke(SvtGraphicStroke* pSvtGraphicStroke) + { + if(pSvtGraphicStroke && mnSvtGraphicStrokeCount) + { + mnSvtGraphicStrokeCount--; + mpMetaFile->AddAction(new MetaCommentAction("XPATHSTROKE_SEQ_END")); + delete pSvtGraphicStroke; + } + } + + // init static break iterator + uno::Reference< ::com::sun::star::i18n::XBreakIterator > VclMetafileProcessor2D::mxBreakIterator; + + VclMetafileProcessor2D::VclMetafileProcessor2D(const geometry::ViewInformation2D& rViewInformation, OutputDevice& rOutDev) + : VclProcessor2D(rViewInformation, rOutDev), + mpMetaFile(rOutDev.GetConnectMetaFile()), + mnSvtGraphicFillCount(0), + mnSvtGraphicStrokeCount(0), + mfCurrentUnifiedTransparence(0.0), + mpPDFExtOutDevData(dynamic_cast< vcl::PDFExtOutDevData* >(rOutDev.GetExtOutDevData())) + { + OSL_ENSURE(rOutDev.GetConnectMetaFile(), "VclMetafileProcessor2D: Used on OutDev which has no MetaFile Target (!)"); + // draw to logic coordinates, do not initialize maCurrentTransformation to viewTransformation + // but only to ObjectTransformation. Do not change MapMode of destination. + maCurrentTransformation = rViewInformation.getObjectTransformation(); + } + + VclMetafileProcessor2D::~VclMetafileProcessor2D() + { + // MapMode was not changed, no restore necessary + } + + /*********************************************************************************************** + + Support of MetaCommentActions in the VclMetafileProcessor2D + Found MetaCommentActions and how they are supported: + + XGRAD_SEQ_BEGIN, XGRAD_SEQ_END: + + Used inside OutputDevice::DrawGradient to mark the start and end of a MetaGradientEx action. + It is used in various exporters/importers to have direct access to the gradient before it + is rendered by VCL (and thus fragmented to polygon color actions and others). On that base, e.g. + the Metafile to SdrObject import creates it's gradient objects. + Best (and safest) way to support it here is to use PRIMITIVE2D_ID_POLYPOLYGONGRADIENTPRIMITIVE2D, + map it back to the corresponding tools PolyPolygon and the Gradient and just call + OutputDevice::DrawGradient which creates the necessary compatible actions. + + XPATHFILL_SEQ_BEGIN, XPATHFILL_SEQ_END: + + Two producers, one is vcl/source/gdi/gdimtf.cxx, line 1273. There, it is transformed + inside GDIMetaFile::Rotate, nothing to take care of here. + The second producer is in graphics/svx/source/svdraw/impgrfll.cxx, line 374. This is used + with each incarnation of Imp_GraphicFill when a metafile is recorded, fillstyle is not + XFILL_NONE and not completely transparent. It creates a SvtGraphicFill and streams it + to the comment action. A closing end token is created in the destructor. + Usages of Imp_GraphicFill are in Do_Paint_Object-methods of SdrCircObj, SdrPathObj and + SdrRectObj. + The token users pick various actions from SvtGraphicFill, so it may need to be added for all kind + of filled objects, even simple colored polygons. It is added as extra information; the + Metafile actions between the two tokens are interpreted as output generated from those + fills. Thus, users have the choice to use the SvtGraphicFill info or the created output + actions. + Even for XFillTransparenceItem it is used, thus it may need to be supported in + UnifiedTransparencePrimitive2D, too, when interpreted as normally filled PolyPolygon. + Implemented for: + PRIMITIVE2D_ID_POLYPOLYGONBITMAPPRIMITIVE2D, + PRIMITIVE2D_ID_POLYPOLYGONHATCHPRIMITIVE2D, + PRIMITIVE2D_ID_POLYPOLYGONGRADIENTPRIMITIVE2D, + PRIMITIVE2D_ID_POLYPOLYGONCOLORPRIMITIVE2D, + and for PRIMITIVE2D_ID_UNIFIEDTRANSPARENCEPRIMITIVE2D when detected unified transparence + + XPATHSTROKE_SEQ_BEGIN, XPATHSTROKE_SEQ_END: + + Similar to pathfill, but using SvtGraphicStroke instead. It also has two producers where one + is also the GDIMetaFile::Rotate. Another user is MetaCommentAction::Move which modifies the + contained path accordingly. + The other one is SdrObject::Imp_DrawLineGeometry. It's done when MetaFile is set at OutDev and + only when geometry is a single polygon (!). I see no reason for that; in the PS exporter this + would hinder to make use of PolyPolygon strokes. I will need to add support at: + PRIMITIVE2D_ID_POLYGONHAIRLINEPRIMITIVE2D + PRIMITIVE2D_ID_POLYGONSTROKEPRIMITIVE2D + PRIMITIVE2D_ID_POLYGONSTROKEARROWPRIMITIVE2D + This can be done hierarchical, too. + Okay, base implementation done based on those three primitives. + + FIELD_SEQ_BEGIN, FIELD_SEQ_END + + Used from slideshow for URLs, created from diverse SvxField implementations inside + createBeginComment()/createEndComment(). createBeginComment() is used from editeng\impedit3.cxx + inside ImpEditEngine::Paint. + Created TextHierarchyFieldPrimitive2D and added needed infos there; it is an group primitive and wraps + text primitives (but is not limited to that). It contains the field type if special actions for the + support of FIELD_SEQ_BEGIN/END are needed; this is the case for Page and URL fields. If more is + needed, it may be supported there. + FIELD_SEQ_BEGIN;PageField + FIELD_SEQ_END + Okay, these are now completely supported by TextHierarchyFieldPrimitive2D. URL works, too. + + XTEXT + + XTEXT_EOC(i) end of character + XTEXT_EOW(i) end of word + XTEXT_EOS(i) end of sentence + + this three are with index and are created with the help of a i18n::XBreakIterator in + ImplDrawWithComments. Simplifying, moving out text painting, reworking to create some + data structure for holding those TEXT infos. + Supported directly by TextSimplePortionPrimitive2D with adding a Locale to the basic text + primitive. In the MetaFileRenderer, the creation is now done (see below). This has the advantage + that this creations do not need to be done for all paints all the time. This would be + expensive since the BreakIterator and it's usage is expensive and for each paint also the + whole character stops would need to be created. + Created only for TextDecoratedPortionPrimitive2D due to XTEXT_EOL and XTEXT_EOP (see below) + + XTEXT_EOL() end of line + XTEXT_EOP() end of paragraph + + First try with boolean marks at TextDecoratedPortionPrimitive2D did not work too well, + i decided to solve it with structure. I added the TextHierarchyPrimitives for this, + namely: + - TextHierarchyLinePrimitive2D: Encapsulates single line + - TextHierarchyParagraphPrimitive2D: Encapsulates single paragraph + - TextHierarchyBlockPrimitive2D: encapsulates object texts (only one ATM) + Those are now supported in hierarchy. This means the MetaFile renderer will support them + by using them, reculrively using their content and adding MetaFile comments as needed. + This also means that when another text layouter will be used it will be necessary to + create/support the same HierarchyPrimitives to support users. + To transport the information using this hierarchy is best suited to all future needs; + the slideshow will be able to profit from it directly when using primitives; all other + renderers not interested in the text structure will just ignore the encapsulations. + + XTEXT_PAINTSHAPE_BEGIN, XTEXT_PAINTSHAPE_END + Supported now by the TextHierarchyBlockPrimitive2D. + + EPSReplacementGraphic: + Only used in goodies\source\filter.vcl\ieps\ieps.cxx and svx\source\xml\xmlgrhlp.cxx to + hold the original EPS which was imported in the same MetaFile as first 2 entries. Only + used to export the original again (if exists). + Not necessary to support with MetaFuleRenderer. + + XTEXT_SCROLLRECT, XTEXT_PAINTRECT + Currently used to get extra MetaFile infos using GraphicExporter which again uses + SdrTextObj::GetTextScrollMetaFileAndRectangle(). ATM works with primitives since + the rectangle data is added directly by the GraphicsExporter as comment. Does not need + to be adapted at once. + When adapting later, the only user - the diashow - should directly use the provided + Anination infos in the appropriate primitives (e.g. AnimatedSwitchPrimitive2D) + + PRNSPOOL_TRANSPARENTBITMAP_BEGIN, PRNSPOOL_TRANSPARENTBITMAP_END + VCL usage when printing PL -> THB. Okay, THB confirms that it is only used as + a fix (hack) while VCL printing. It is needed to not downscale a bitmap which + was explicitely created for the printer already again to some default maximum + bitmap sizes. + Nothing to do here for the primitive renderer. + + Support for vcl::PDFExtOutDevData: + PL knows that SJ did that stuff, it's used to hold a pointer to PDFExtOutDevData at + the OutDev. When set, some extra data is written there. Trying simple PDF export and + watching if i get those infos. + Well, a PDF export does not use e.g. ImpEditEngine::Paint since the PdfFilter uses + the SdXImpressDocument::render and thus uses the VclMetafileProcessor2D. I will check + if i get a PDFExtOutDevData at the target output device. + Indeed, i get one. Checking what all may be done when that extra-device-info is there. + + All in all i have to talk to SJ. I will need to emulate some of those actions, but + i need to discuss which ones. + In the future, all those infos would be taken from the primitive sequence anyways, + thus these extensions would potentially be temporary, too. + Discussed with SJ, added the necessary support and tested it. Details follow. + + - In ImpEditEngine::Paint, paragraph infos and URL stuff is added. + Added in primitive MetaFile renderer. + Checking URL: Indeed, current version exports it, but it is missing in primitive + CWS version. Adding support. + Okay, URLs work. Checked, Done. + + - UnoControlPDFExportContact is only created when PDFExtOutDevData is used at the + target and uno control data is created in UnoControlPDFExportContact::do_PaintObject. + This may be added in primitive MetaFile renderer. + Adding support... + OOps, the necessary helper stuff is in svx/source/form/formpdxexport.cxx in namespace + svxform. Have to talk to FS if this has to be like that. Especially since + ::vcl::PDFWriter::AnyWidget is filled out, which is already part of vcl. + Wrote an eMail to FS, he is on vacation currently. I see no reason why not to move + that stuff to somewhere else, maybe tools or svtools ?!? We will see... + Moved to toolkit, so i have to link against it. I tried VCL first, but it did + not work since VCLUnoHelper::CreateFont is unresolved in VCL (!). Other then the name + may imply, it is defined in toolkit (!). Since toolkit is linked against VCL itself, + the lowest move,ment plave is toolkit. + Checked form control export, it works well. Done. + + - In goodies, in GraphicObject::Draw, when the used Graphic is linked, infos are + generated. I will need to check what happens here with primitives. + To support, use of GraphicPrimitive2D (PRIMITIVE2D_ID_GRAPHICPRIMITIVE2D) may be needed. + Added support, but feature is broken in main version, so i cannot test at all. + Writing a bug to CL (or SJ) and seeing what happens (#i80380#). + SJ took a look and we got it working. Tested VCL MetaFile Renderer based export, + as intended, the original file is exported. Works, Done. + + + + + To be done: + + - Maybe there are more places to take care of for vcl::PDFExtOutDevData! + + + + ****************************************************************************************************/ + + void VclMetafileProcessor2D::processBasePrimitive2D(const primitive2d::BasePrimitive2D& rCandidate) + { + switch(rCandidate.getPrimitive2DID()) + { + case PRIMITIVE2D_ID_WRONGSPELLPRIMITIVE2D : + { + // directdraw of wrong spell primitive + // Ignore for VclMetafileProcessor2D, this is for printing and MetaFile recording only + break; + } + case PRIMITIVE2D_ID_GRAPHICPRIMITIVE2D : + { + const primitive2d::GraphicPrimitive2D& rGraphicPrimitive = static_cast< const primitive2d::GraphicPrimitive2D& >(rCandidate); + bool bUsingPDFExtOutDevData(false); + basegfx::B2DVector aTranslate, aScale; + static bool bSuppressPDFExtOutDevDataSupport(false); + + if(mpPDFExtOutDevData && !bSuppressPDFExtOutDevDataSupport) + { + // emulate data handling from UnoControlPDFExportContact, original see + // svtools/source/graphic/grfmgr.cxx + const Graphic& rGraphic = rGraphicPrimitive.getGraphicObject().GetGraphic(); + + if(rGraphic.IsLink()) + { + const GraphicAttr& rAttr = rGraphicPrimitive.getGraphicAttr(); + + if(!rAttr.IsSpecialDrawMode() && !rAttr.IsAdjusted()) + { + const basegfx::B2DHomMatrix& rTransform = rGraphicPrimitive.getTransform(); + double fRotate, fShearX; + rTransform.decompose(aScale, aTranslate, fRotate, fShearX); + + if( basegfx::fTools::equalZero( fRotate ) && ( aScale.getX() > 0.0 ) && ( aScale.getY() > 0.0 ) ) + { + bUsingPDFExtOutDevData = true; + mpPDFExtOutDevData->BeginGroup(); + } + } + } + } + + // process recursively and add MetaFile comment + process(rGraphicPrimitive.get2DDecomposition(getViewInformation2D())); + + if(bUsingPDFExtOutDevData) + { + // emulate data handling from UnoControlPDFExportContact, original see + // svtools/source/graphic/grfmgr.cxx + const basegfx::B2DRange aCurrentRange( + aTranslate.getX(), aTranslate.getY(), + aTranslate.getX() + aScale.getX(), aTranslate.getY() + aScale.getY()); + const Rectangle aCurrentRect( + sal_Int32(floor(aCurrentRange.getMinX())), sal_Int32(floor(aCurrentRange.getMinY())), + sal_Int32(ceil(aCurrentRange.getMaxX())), sal_Int32(ceil(aCurrentRange.getMaxY()))); + const GraphicAttr& rAttr = rGraphicPrimitive.getGraphicAttr(); + Rectangle aCropRect; + + if(rAttr.IsCropped()) + { + // calculate scalings between real image size and logic object size. This + // is necessary since the crop values are relative to original bitmap size + double fFactorX(1.0); + double fFactorY(1.0); + + { + const MapMode aMapMode100thmm(MAP_100TH_MM); + const Size aBitmapSize(Application::GetDefaultDevice()->LogicToLogic( + rGraphicPrimitive.getGraphicObject().GetPrefSize(), + rGraphicPrimitive.getGraphicObject().GetPrefMapMode(), aMapMode100thmm)); + const double fDivX(aBitmapSize.Width() - rAttr.GetLeftCrop() - rAttr.GetRightCrop()); + const double fDivY(aBitmapSize.Height() - rAttr.GetTopCrop() - rAttr.GetBottomCrop()); + + if(!basegfx::fTools::equalZero(fDivX)) + { + fFactorX = aScale.getX() / fDivX; + } + + if(!basegfx::fTools::equalZero(fDivY)) + { + fFactorY = aScale.getY() / fDivY; + } + } + + // calculate crop range and rect + basegfx::B2DRange aCropRange; + aCropRange.expand(aCurrentRange.getMinimum() - basegfx::B2DPoint(rAttr.GetLeftCrop() * fFactorX, rAttr.GetTopCrop() * fFactorY)); + aCropRange.expand(aCurrentRange.getMaximum() + basegfx::B2DPoint(rAttr.GetRightCrop() * fFactorX, rAttr.GetBottomCrop() * fFactorY)); + + aCropRect = Rectangle( + sal_Int32(floor(aCropRange.getMinX())), sal_Int32(floor(aCropRange.getMinY())), + sal_Int32(ceil(aCropRange.getMaxX())), sal_Int32(ceil(aCropRange.getMaxY()))); + } + + mpPDFExtOutDevData->EndGroup(rGraphicPrimitive.getGraphicObject().GetGraphic(), + rAttr.GetTransparency(), + aCurrentRect, + aCropRect); + } + + break; + } + case PRIMITIVE2D_ID_CONTROLPRIMITIVE2D : + { + const primitive2d::ControlPrimitive2D& rControlPrimitive = static_cast< const primitive2d::ControlPrimitive2D& >(rCandidate); + const uno::Reference< awt::XControl >& rXControl(rControlPrimitive.getXControl()); + bool bIsPrintableControl(false); + + // find out if control is printable + if(rXControl.is()) + { + try + { + uno::Reference< beans::XPropertySet > xModelProperties(rXControl->getModel(), uno::UNO_QUERY); + uno::Reference< beans::XPropertySetInfo > xPropertyInfo(xModelProperties.is() + ? xModelProperties->getPropertySetInfo() + : uno::Reference< beans::XPropertySetInfo >()); + const ::rtl::OUString sPrintablePropertyName(RTL_CONSTASCII_USTRINGPARAM("Printable")); + + if(xPropertyInfo.is() && xPropertyInfo->hasPropertyByName(sPrintablePropertyName)) + { + OSL_VERIFY(xModelProperties->getPropertyValue(sPrintablePropertyName) >>= bIsPrintableControl); + } + } + catch(const uno::Exception&) + { + OSL_ENSURE(false, "VclMetafileProcessor2D: No access to printable flag of Control, caught an exception!"); + } + } + + // PDF export and printing only for printable controls + if(bIsPrintableControl) + { + const bool bPDFExport(mpPDFExtOutDevData && mpPDFExtOutDevData->GetIsExportFormFields()); + bool bDoProcessRecursively(true); + + if(bPDFExport) + { + // PDF export. Emulate data handling from UnoControlPDFExportContact + // I have now moved describePDFControl to toolkit, thus i can implement the PDF + // form control support now as follows + ::std::auto_ptr< ::vcl::PDFWriter::AnyWidget > pPDFControl; + ::toolkitform::describePDFControl(rXControl, pPDFControl); + + if(pPDFControl.get()) + { + // still need to fill in the location (is a class Rectangle) + const basegfx::B2DRange aRangeLogic(rControlPrimitive.getB2DRange(getViewInformation2D())); + const Rectangle aRectLogic( + (sal_Int32)floor(aRangeLogic.getMinX()), (sal_Int32)floor(aRangeLogic.getMinY()), + (sal_Int32)ceil(aRangeLogic.getMaxX()), (sal_Int32)ceil(aRangeLogic.getMaxY())); + pPDFControl->Location = aRectLogic; + + Size aFontSize(pPDFControl->TextFont.GetSize()); + aFontSize = mpOutputDevice->LogicToLogic(aFontSize, MapMode(MAP_POINT), mpOutputDevice->GetMapMode()); + pPDFControl->TextFont.SetSize(aFontSize); + + mpPDFExtOutDevData->BeginStructureElement(vcl::PDFWriter::Form); + mpPDFExtOutDevData->CreateControl(*pPDFControl.get()); + mpPDFExtOutDevData->EndStructureElement(); + + // no normal paint needed (see original UnoControlPDFExportContact::do_PaintObject); + // do not process recursively + bDoProcessRecursively = false; + } + else + { + // PDF export did not work, try simple output. + // Fallback to printer output by not setting bDoProcessRecursively + // to false. + } + } + + // #i93169# used flag the wrong way; true means that nothing was done yet + if(bDoProcessRecursively) + { + // printer output + try + { + // remember old graphics and create new + uno::Reference< awt::XView > xControlView(rXControl, uno::UNO_QUERY_THROW); + const uno::Reference< awt::XGraphics > xOriginalGraphics(xControlView->getGraphics()); + const uno::Reference< awt::XGraphics > xNewGraphics(mpOutputDevice->CreateUnoGraphics()); + + if(xNewGraphics.is()) + { + // link graphics and view + xControlView->setGraphics(xNewGraphics); + + // get position + const basegfx::B2DHomMatrix aObjectToDiscrete(getViewInformation2D().getObjectToViewTransformation() * rControlPrimitive.getTransform()); + const basegfx::B2DPoint aTopLeftDiscrete(aObjectToDiscrete * basegfx::B2DPoint(0.0, 0.0)); + + // draw it + xControlView->draw(basegfx::fround(aTopLeftDiscrete.getX()), basegfx::fround(aTopLeftDiscrete.getY())); + bDoProcessRecursively = false; + + // restore original graphics + xControlView->setGraphics(xOriginalGraphics); + } + } + catch( const uno::Exception& ) + { + OSL_ENSURE(false, "VclMetafileProcessor2D: Printing of Control failed, caught an exception!"); + } + } + + // process recursively if not done yet to export as decomposition (bitmap) + if(bDoProcessRecursively) + { + process(rControlPrimitive.get2DDecomposition(getViewInformation2D())); + } + } + + break; + } + case PRIMITIVE2D_ID_TEXTHIERARCHYFIELDPRIMITIVE2D : + { + // support for FIELD_SEQ_BEGIN, FIELD_SEQ_END and URL. It wraps text primitives (but is not limited to) + // thus do the MetafileAction embedding stuff but just handle recursively. + const primitive2d::TextHierarchyFieldPrimitive2D& rFieldPrimitive = static_cast< const primitive2d::TextHierarchyFieldPrimitive2D& >(rCandidate); + static const ByteString aCommentStringCommon("FIELD_SEQ_BEGIN"); + static const ByteString aCommentStringPage("FIELD_SEQ_BEGIN;PageField"); + static const ByteString aCommentStringEnd("FIELD_SEQ_END"); + + switch(rFieldPrimitive.getType()) + { + default : // case drawinglayer::primitive2d::FIELD_TYPE_COMMON : + { + mpMetaFile->AddAction(new MetaCommentAction(aCommentStringCommon)); + break; + } + case drawinglayer::primitive2d::FIELD_TYPE_PAGE : + { + mpMetaFile->AddAction(new MetaCommentAction(aCommentStringPage)); + break; + } + case drawinglayer::primitive2d::FIELD_TYPE_URL : + { + const rtl::OUString& rURL = rFieldPrimitive.getString(); + const String aOldString(rURL); + mpMetaFile->AddAction(new MetaCommentAction(aCommentStringCommon, 0, reinterpret_cast< const BYTE* >(aOldString.GetBuffer()), 2 * aOldString.Len())); + break; + } + } + + // process recursively + const primitive2d::Primitive2DSequence rContent = rFieldPrimitive.get2DDecomposition(getViewInformation2D()); + process(rContent); + + // for the end comment the type is not relevant yet, they are all the same. Just add. + mpMetaFile->AddAction(new MetaCommentAction(aCommentStringEnd)); + + if(mpPDFExtOutDevData && drawinglayer::primitive2d::FIELD_TYPE_URL == rFieldPrimitive.getType()) + { + // emulate data handling from ImpEditEngine::Paint + const basegfx::B2DRange aViewRange(primitive2d::getB2DRangeFromPrimitive2DSequence(rContent, getViewInformation2D())); + const Rectangle aRectLogic( + (sal_Int32)floor(aViewRange.getMinX()), (sal_Int32)floor(aViewRange.getMinY()), + (sal_Int32)ceil(aViewRange.getMaxX()), (sal_Int32)ceil(aViewRange.getMaxY())); + vcl::PDFExtOutDevBookmarkEntry aBookmark; + aBookmark.nLinkId = mpPDFExtOutDevData->CreateLink(aRectLogic); + aBookmark.aBookmark = rFieldPrimitive.getString(); + std::vector< vcl::PDFExtOutDevBookmarkEntry >& rBookmarks = mpPDFExtOutDevData->GetBookmarks(); + rBookmarks.push_back( aBookmark ); + } + + break; + } + case PRIMITIVE2D_ID_TEXTHIERARCHYLINEPRIMITIVE2D : + { + const primitive2d::TextHierarchyLinePrimitive2D& rLinePrimitive = static_cast< const primitive2d::TextHierarchyLinePrimitive2D& >(rCandidate); + static const ByteString aCommentString("XTEXT_EOL"); + + // process recursively and add MetaFile comment + process(rLinePrimitive.get2DDecomposition(getViewInformation2D())); + mpMetaFile->AddAction(new MetaCommentAction(aCommentString)); + + break; + } + case PRIMITIVE2D_ID_TEXTHIERARCHYBULLETPRIMITIVE2D : + { + // in Outliner::PaintBullet(), a MetafileComment for bullets is added, too. The + // "XTEXT_EOC" is used, use here, too. + const primitive2d::TextHierarchyBulletPrimitive2D& rBulletPrimitive = static_cast< const primitive2d::TextHierarchyBulletPrimitive2D& >(rCandidate); + static const ByteString aCommentString("XTEXT_EOC"); + + // process recursively and add MetaFile comment + process(rBulletPrimitive.get2DDecomposition(getViewInformation2D())); + mpMetaFile->AddAction(new MetaCommentAction(aCommentString)); + + break; + } + case PRIMITIVE2D_ID_TEXTHIERARCHYPARAGRAPHPRIMITIVE2D : + { + const primitive2d::TextHierarchyParagraphPrimitive2D& rParagraphPrimitive = static_cast< const primitive2d::TextHierarchyParagraphPrimitive2D& >(rCandidate); + static const ByteString aCommentString("XTEXT_EOP"); + + if(mpPDFExtOutDevData) + { + // emulate data handling from ImpEditEngine::Paint + mpPDFExtOutDevData->BeginStructureElement( vcl::PDFWriter::Paragraph ); + } + + // process recursively and add MetaFile comment + process(rParagraphPrimitive.get2DDecomposition(getViewInformation2D())); + mpMetaFile->AddAction(new MetaCommentAction(aCommentString)); + + if(mpPDFExtOutDevData) + { + // emulate data handling from ImpEditEngine::Paint + mpPDFExtOutDevData->EndStructureElement(); + } + + break; + } + case PRIMITIVE2D_ID_TEXTHIERARCHYBLOCKPRIMITIVE2D : + { + const primitive2d::TextHierarchyBlockPrimitive2D& rBlockPrimitive = static_cast< const primitive2d::TextHierarchyBlockPrimitive2D& >(rCandidate); + static const ByteString aCommentStringA("XTEXT_PAINTSHAPE_BEGIN"); + static const ByteString aCommentStringB("XTEXT_PAINTSHAPE_END"); + + // add MetaFile comment, process recursively and add MetaFile comment + mpMetaFile->AddAction(new MetaCommentAction(aCommentStringA)); + process(rBlockPrimitive.get2DDecomposition(getViewInformation2D())); + mpMetaFile->AddAction(new MetaCommentAction(aCommentStringB)); + + break; + } + case PRIMITIVE2D_ID_TEXTSIMPLEPORTIONPRIMITIVE2D : + case PRIMITIVE2D_ID_TEXTDECORATEDPORTIONPRIMITIVE2D : + { + // for supporting TEXT_ MetaFile actions there is more to do here; get the candidate + const primitive2d::TextSimplePortionPrimitive2D& rTextCandidate = static_cast< const primitive2d::TextSimplePortionPrimitive2D& >(rCandidate); + // const primitive2d::TextDecoratedPortionPrimitive2D* pTextDecoratedCandidate = dynamic_cast< const primitive2d::TextDecoratedPortionPrimitive2D* >(&rCandidate); + + // Adapt evtl. used special DrawMode + const sal_uInt32 nOriginalDrawMode(mpOutputDevice->GetDrawMode()); + adaptTextToFillDrawMode(); + + // directdraw of text simple portion; use default processing + RenderTextSimpleOrDecoratedPortionPrimitive2D(rTextCandidate); + + // restore DrawMode + mpOutputDevice->SetDrawMode(nOriginalDrawMode); + + // #i101169# if(pTextDecoratedCandidate) + { + // support for TEXT_ MetaFile actions only for decorated texts + if(!mxBreakIterator.is()) + { + uno::Reference< ::com::sun::star::lang::XMultiServiceFactory > xMSF(::comphelper::getProcessServiceFactory()); + mxBreakIterator.set(xMSF->createInstance(rtl::OUString::createFromAscii("com.sun.star.i18n.BreakIterator")), uno::UNO_QUERY); + } + + if(mxBreakIterator.is()) + { + const rtl::OUString& rTxt = rTextCandidate.getText(); + const sal_Int32 nTextLength(rTextCandidate.getTextLength()); // rTxt.getLength()); + + if(nTextLength) + { + const ::com::sun::star::lang::Locale& rLocale = rTextCandidate.getLocale(); + const sal_Int32 nTextPosition(rTextCandidate.getTextPosition()); + + sal_Int32 nDone; + sal_Int32 nNextCellBreak(mxBreakIterator->nextCharacters(rTxt, nTextPosition, rLocale, ::com::sun::star::i18n::CharacterIteratorMode::SKIPCELL, 0, nDone)); + ::com::sun::star::i18n::Boundary nNextWordBoundary(mxBreakIterator->getWordBoundary(rTxt, nTextPosition, rLocale, ::com::sun::star::i18n::WordType::ANY_WORD, sal_True)); + sal_Int32 nNextSentenceBreak(mxBreakIterator->endOfSentence(rTxt, nTextPosition, rLocale)); + static const ByteString aCommentStringA("XTEXT_EOC"); + static const ByteString aCommentStringB("XTEXT_EOW"); + static const ByteString aCommentStringC("XTEXT_EOS"); + + for(sal_Int32 i(nTextPosition); i < nTextPosition + nTextLength; i++) + { + // create the entries for the respective break positions + if(i == nNextCellBreak) + { + mpMetaFile->AddAction(new MetaCommentAction(aCommentStringA, i - nTextPosition)); + nNextCellBreak = mxBreakIterator->nextCharacters(rTxt, i, rLocale, ::com::sun::star::i18n::CharacterIteratorMode::SKIPCELL, 1, nDone); + } + if(i == nNextWordBoundary.endPos) + { + mpMetaFile->AddAction(new MetaCommentAction(aCommentStringB, i - nTextPosition)); + nNextWordBoundary = mxBreakIterator->getWordBoundary(rTxt, i + 1, rLocale, ::com::sun::star::i18n::WordType::ANY_WORD, sal_True); + } + if(i == nNextSentenceBreak) + { + mpMetaFile->AddAction(new MetaCommentAction(aCommentStringC, i - nTextPosition)); + nNextSentenceBreak = mxBreakIterator->endOfSentence(rTxt, i + 1, rLocale); + } + } + } + } + } + + break; + } + case PRIMITIVE2D_ID_POLYGONHAIRLINEPRIMITIVE2D : + { + const primitive2d::PolygonHairlinePrimitive2D& rHairlinePrimitive = static_cast< const primitive2d::PolygonHairlinePrimitive2D& >(rCandidate); + const basegfx::B2DPolygon& rBasePolygon = rHairlinePrimitive.getB2DPolygon(); + + if(rBasePolygon.count() > (MAX_POLYGON_POINT_COUNT_METAFILE - 1)) + { + // #i112245# Metafiles use tools Polygon and are not able to have more than 65535 points + // per polygon. If there are more, split the polygon in half and call recursively + basegfx::B2DPolygon aLeft, aRight; + splitLinePolygon(rBasePolygon, aLeft, aRight); + const primitive2d::PolygonHairlinePrimitive2D aPLeft(aLeft, rHairlinePrimitive.getBColor()); + const primitive2d::PolygonHairlinePrimitive2D aPRight(aRight, rHairlinePrimitive.getBColor()); + + processBasePrimitive2D(aPLeft); + processBasePrimitive2D(aPRight); + } + else + { + // direct draw of hairline; use default processing + // support SvtGraphicStroke MetaCommentAction + const basegfx::BColor aLineColor(maBColorModifierStack.getModifiedColor(rHairlinePrimitive.getBColor())); + SvtGraphicStroke* pSvtGraphicStroke = impTryToCreateSvtGraphicStroke( + rHairlinePrimitive.getB2DPolygon(), + &aLineColor, + 0, 0, 0, 0); + + impStartSvtGraphicStroke(pSvtGraphicStroke); + RenderPolygonHairlinePrimitive2D(static_cast< const primitive2d::PolygonHairlinePrimitive2D& >(rCandidate), false); + impEndSvtGraphicStroke(pSvtGraphicStroke); + } + break; + } + case PRIMITIVE2D_ID_POLYGONSTROKEPRIMITIVE2D : + { + const primitive2d::PolygonStrokePrimitive2D& rStrokePrimitive = static_cast< const primitive2d::PolygonStrokePrimitive2D& >(rCandidate); + const basegfx::B2DPolygon& rBasePolygon = rStrokePrimitive.getB2DPolygon(); + + if(rBasePolygon.count() > (MAX_POLYGON_POINT_COUNT_METAFILE - 1)) + { + // #i112245# Metafiles use tools Polygon and are not able to have more than 65535 points + // per polygon. If there are more, split the polygon in half and call recursively + basegfx::B2DPolygon aLeft, aRight; + splitLinePolygon(rBasePolygon, aLeft, aRight); + const primitive2d::PolygonStrokePrimitive2D aPLeft( + aLeft, rStrokePrimitive.getLineAttribute(), rStrokePrimitive.getStrokeAttribute()); + const primitive2d::PolygonStrokePrimitive2D aPRight( + aRight, rStrokePrimitive.getLineAttribute(), rStrokePrimitive.getStrokeAttribute()); + + processBasePrimitive2D(aPLeft); + processBasePrimitive2D(aPRight); + } + else + { + // support SvtGraphicStroke MetaCommentAction + SvtGraphicStroke* pSvtGraphicStroke = impTryToCreateSvtGraphicStroke( + rBasePolygon, 0, + &rStrokePrimitive.getLineAttribute(), + &rStrokePrimitive.getStrokeAttribute(), + 0, 0); + + impStartSvtGraphicStroke(pSvtGraphicStroke); + const attribute::LineAttribute& rLine = rStrokePrimitive.getLineAttribute(); + + // create MetaPolyLineActions, but without LINE_DASH + if(basegfx::fTools::more(rLine.getWidth(), 0.0)) + { + const attribute::StrokeAttribute& rStroke = rStrokePrimitive.getStrokeAttribute(); + basegfx::B2DPolyPolygon aHairLinePolyPolygon; + + if(0.0 == rStroke.getFullDotDashLen()) + { + aHairLinePolyPolygon.append(rBasePolygon); + } + else + { + basegfx::tools::applyLineDashing( + rBasePolygon, rStroke.getDotDashArray(), + &aHairLinePolyPolygon, 0, rStroke.getFullDotDashLen()); + } + + const basegfx::BColor aHairlineColor(maBColorModifierStack.getModifiedColor(rLine.getColor())); + mpOutputDevice->SetLineColor(Color(aHairlineColor)); + mpOutputDevice->SetFillColor(); + aHairLinePolyPolygon.transform(maCurrentTransformation); + + // #i113922# LineWidth needs to be transformed, too + const basegfx::B2DVector aDiscreteUnit(maCurrentTransformation * basegfx::B2DVector(rLine.getWidth(), 0.0)); + const double fDiscreteLineWidth(aDiscreteUnit.getLength()); + + LineInfo aLineInfo(LINE_SOLID, basegfx::fround(fDiscreteLineWidth)); + aLineInfo.SetLineJoin(rLine.getLineJoin()); + + for(sal_uInt32 a(0); a < aHairLinePolyPolygon.count(); a++) + { + const basegfx::B2DPolygon aCandidate(aHairLinePolyPolygon.getB2DPolygon(a)); + + if(aCandidate.count() > 1) + { + const Polygon aToolsPolygon(aCandidate); + + mpMetaFile->AddAction(new MetaPolyLineAction(aToolsPolygon, aLineInfo)); + } + } + } + else + { + process(rCandidate.get2DDecomposition(getViewInformation2D())); + } + + impEndSvtGraphicStroke(pSvtGraphicStroke); + } + + break; + } + case PRIMITIVE2D_ID_POLYGONSTROKEARROWPRIMITIVE2D : + { + const primitive2d::PolygonStrokeArrowPrimitive2D& rStrokeArrowPrimitive = static_cast< const primitive2d::PolygonStrokeArrowPrimitive2D& >(rCandidate); + const basegfx::B2DPolygon& rBasePolygon = rStrokeArrowPrimitive.getB2DPolygon(); + + if(rBasePolygon.count() > (MAX_POLYGON_POINT_COUNT_METAFILE - 1)) + { + // #i112245# Metafiles use tools Polygon and are not able to have more than 65535 points + // per polygon. If there are more, split the polygon in half and call recursively + basegfx::B2DPolygon aLeft, aRight; + splitLinePolygon(rBasePolygon, aLeft, aRight); + const attribute::LineStartEndAttribute aEmpty; + const primitive2d::PolygonStrokeArrowPrimitive2D aPLeft( + aLeft, + rStrokeArrowPrimitive.getLineAttribute(), + rStrokeArrowPrimitive.getStrokeAttribute(), + rStrokeArrowPrimitive.getStart(), + aEmpty); + const primitive2d::PolygonStrokeArrowPrimitive2D aPRight( + aRight, + rStrokeArrowPrimitive.getLineAttribute(), + rStrokeArrowPrimitive.getStrokeAttribute(), + aEmpty, + rStrokeArrowPrimitive.getEnd()); + + processBasePrimitive2D(aPLeft); + processBasePrimitive2D(aPRight); + } + else + { + // support SvtGraphicStroke MetaCommentAction + SvtGraphicStroke* pSvtGraphicStroke = impTryToCreateSvtGraphicStroke( + rBasePolygon, 0, + &rStrokeArrowPrimitive.getLineAttribute(), + &rStrokeArrowPrimitive.getStrokeAttribute(), + &rStrokeArrowPrimitive.getStart(), + &rStrokeArrowPrimitive.getEnd()); + + impStartSvtGraphicStroke(pSvtGraphicStroke); + process(rCandidate.get2DDecomposition(getViewInformation2D())); + impEndSvtGraphicStroke(pSvtGraphicStroke); + } + + break; + } + case PRIMITIVE2D_ID_BITMAPPRIMITIVE2D : + { + // direct draw of transformed BitmapEx primitive; use default processing + RenderBitmapPrimitive2D(static_cast< const primitive2d::BitmapPrimitive2D& >(rCandidate)); + break; + } + case PRIMITIVE2D_ID_POLYPOLYGONBITMAPPRIMITIVE2D : + { + // need to handle PolyPolygonBitmapPrimitive2D here to support XPATHFILL_SEQ_BEGIN/XPATHFILL_SEQ_END + const primitive2d::PolyPolygonBitmapPrimitive2D& rBitmapCandidate = static_cast< const primitive2d::PolyPolygonBitmapPrimitive2D& >(rCandidate); + basegfx::B2DPolyPolygon aLocalPolyPolygon(rBitmapCandidate.getB2DPolyPolygon()); + + if(fillPolyPolygonNeededToBeSplit(aLocalPolyPolygon)) + { + // #i112245# Metafiles use tools Polygon and are not able to have more than 65535 points + // per polygon. If there are more use the splitted polygon and call recursively + const primitive2d::PolyPolygonBitmapPrimitive2D aSplitted( + aLocalPolyPolygon, + rBitmapCandidate.getFillBitmap()); + + processBasePrimitive2D(aSplitted); + } + else + { + SvtGraphicFill* pSvtGraphicFill = 0; + + if(!mnSvtGraphicFillCount && aLocalPolyPolygon.count()) + { + aLocalPolyPolygon.transform(maCurrentTransformation); + // calculate transformation. Get real object size, all values in FillBitmapAttribute + // are relative to the unified object + const attribute::FillBitmapAttribute& rFillBitmapAttribute = rBitmapCandidate .getFillBitmap(); + const basegfx::B2DRange aOutlineRange(basegfx::tools::getRange(aLocalPolyPolygon)); + const basegfx::B2DVector aOutlineSize(aOutlineRange.getRange()); + + // get absolute values + const basegfx::B2DVector aFillBitmapSize(rFillBitmapAttribute.getSize() * aOutlineSize); + const basegfx::B2DPoint aFillBitmapTopLeft(rFillBitmapAttribute.getTopLeft() * aOutlineSize); + + // the scaling needs scale from pixel to logic coordinate system + const BitmapEx& rBitmapEx = rFillBitmapAttribute.getBitmapEx(); + Size aBmpSizePixel(rBitmapEx.GetSizePixel()); + + if(!aBmpSizePixel.Width()) + { + aBmpSizePixel.Width() = 1; + } + + if(!aBmpSizePixel.Height()) + { + aBmpSizePixel.Height() = 1; + } + + // setup transformation like in impgrfll + SvtGraphicFill::Transform aTransform; + + // scale values are divided by bitmap pixel sizes + aTransform.matrix[0] = aFillBitmapSize.getX() / aBmpSizePixel.Width(); + aTransform.matrix[4] = aFillBitmapSize.getY() / aBmpSizePixel.Height(); + + // translates are absolute + aTransform.matrix[2] = aFillBitmapTopLeft.getX(); + aTransform.matrix[5] = aFillBitmapTopLeft.getY(); + + // setup fill graphic like in impgrfll + Graphic aFillGraphic = Graphic(rBitmapEx); + aFillGraphic.SetPrefMapMode(MapMode(MAP_PIXEL)); + aFillGraphic.SetPrefSize(aBmpSizePixel); + + pSvtGraphicFill = new SvtGraphicFill( + PolyPolygon(aLocalPolyPolygon), + Color(), + 0.0, + SvtGraphicFill::fillEvenOdd, + SvtGraphicFill::fillTexture, + aTransform, + rFillBitmapAttribute.getTiling(), + SvtGraphicFill::hatchSingle, + Color(), + SvtGraphicFill::gradientLinear, + Color(), + Color(), + 0, + aFillGraphic); + } + + // Do use decomposition; encapsulate with SvtGraphicFill + impStartSvtGraphicFill(pSvtGraphicFill); + process(rCandidate.get2DDecomposition(getViewInformation2D())); + impEndSvtGraphicFill(pSvtGraphicFill); + } + + break; + } + case PRIMITIVE2D_ID_POLYPOLYGONHATCHPRIMITIVE2D : + { + // need to handle PolyPolygonHatchPrimitive2D here to support XPATHFILL_SEQ_BEGIN/XPATHFILL_SEQ_END + const primitive2d::PolyPolygonHatchPrimitive2D& rHatchCandidate = static_cast< const primitive2d::PolyPolygonHatchPrimitive2D& >(rCandidate); + basegfx::B2DPolyPolygon aLocalPolyPolygon(rHatchCandidate.getB2DPolyPolygon()); + + // #i112245# Metafiles use tools Polygon and are not able to have more than 65535 points + // per polygon. Split polygon until there are less than that + while(fillPolyPolygonNeededToBeSplit(aLocalPolyPolygon)) + ; + + SvtGraphicFill* pSvtGraphicFill = 0; + const attribute::FillHatchAttribute& rFillHatchAttribute = rHatchCandidate.getFillHatch(); + aLocalPolyPolygon.transform(maCurrentTransformation); + + if(!mnSvtGraphicFillCount && aLocalPolyPolygon.count()) + { + // re-create a VCL hatch as base data + SvtGraphicFill::HatchType eHatch(SvtGraphicFill::hatchSingle); + + switch(rFillHatchAttribute.getStyle()) + { + default: // attribute::HATCHSTYLE_SINGLE : + { + eHatch = SvtGraphicFill::hatchSingle; + break; + } + case attribute::HATCHSTYLE_DOUBLE : + { + eHatch = SvtGraphicFill::hatchDouble; + break; + } + case attribute::HATCHSTYLE_TRIPLE : + { + eHatch = SvtGraphicFill::hatchTriple; + break; + } + } + + SvtGraphicFill::Transform aTransform; + + // scale + aTransform.matrix[0] *= rFillHatchAttribute.getDistance(); + aTransform.matrix[4] *= rFillHatchAttribute.getDistance(); + + // rotate (was never correct in impgrfll anyways, use correct angle now) + aTransform.matrix[0] *= cos(rFillHatchAttribute.getAngle()); + aTransform.matrix[1] *= -sin(rFillHatchAttribute.getAngle()); + aTransform.matrix[3] *= sin(rFillHatchAttribute.getAngle()); + aTransform.matrix[4] *= cos(rFillHatchAttribute.getAngle()); + + pSvtGraphicFill = new SvtGraphicFill( + PolyPolygon(aLocalPolyPolygon), + Color(), + 0.0, + SvtGraphicFill::fillEvenOdd, + SvtGraphicFill::fillHatch, + aTransform, + false, + eHatch, + Color(rFillHatchAttribute.getColor()), + SvtGraphicFill::gradientLinear, + Color(), + Color(), + 0, + Graphic()); + } + + // Do use decomposition; encapsulate with SvtGraphicFill + impStartSvtGraphicFill(pSvtGraphicFill); + + // #i111954# do NOT use decomposition, but use direct VCL-command + // process(rCandidate.get2DDecomposition(getViewInformation2D())); + const PolyPolygon aToolsPolyPolygon(aLocalPolyPolygon); + const HatchStyle aHatchStyle( + attribute::HATCHSTYLE_SINGLE == rFillHatchAttribute.getStyle() ? HATCH_SINGLE : + attribute::HATCHSTYLE_DOUBLE == rFillHatchAttribute.getStyle() ? HATCH_DOUBLE : + HATCH_TRIPLE); + + mpOutputDevice->DrawHatch(aToolsPolyPolygon, + Hatch(aHatchStyle, + Color(rFillHatchAttribute.getColor()), + basegfx::fround(rFillHatchAttribute.getDistance()), + basegfx::fround(rFillHatchAttribute.getAngle() / F_PI1800))); + + impEndSvtGraphicFill(pSvtGraphicFill); + + break; + } + case PRIMITIVE2D_ID_POLYPOLYGONGRADIENTPRIMITIVE2D : + { + const primitive2d::PolyPolygonGradientPrimitive2D& rGradientCandidate = static_cast< const primitive2d::PolyPolygonGradientPrimitive2D& >(rCandidate); + basegfx::B2DPolyPolygon aLocalPolyPolygon(rGradientCandidate.getB2DPolyPolygon()); + + // #i112245# Metafiles use tools Polygon and are not able to have more than 65535 points + // per polygon. Split polygon until there are less than that + while(fillPolyPolygonNeededToBeSplit(aLocalPolyPolygon)) + ; + + // for support of MetaCommentActions of the form XGRAD_SEQ_BEGIN, XGRAD_SEQ_END + // it is safest to use the VCL OutputDevice::DrawGradient method which creates those. + // re-create a VCL-gradient from FillGradientPrimitive2D and the needed tools PolyPolygon + Gradient aVCLGradient; + impConvertFillGradientAttributeToVCLGradient(aVCLGradient, rGradientCandidate.getFillGradient(), false); + aLocalPolyPolygon.transform(maCurrentTransformation); + + // #i82145# ATM VCL printing of gradients using curved shapes does not work, + // i submitted the bug with the given ID to THB. When that task is fixed it is + // necessary to again remove this subdivision since it decreases possible + // printing quality (not even resolution-dependent for now). THB will tell + // me when that task is fixed in the master + const PolyPolygon aToolsPolyPolygon(basegfx::tools::adaptiveSubdivideByAngle(aLocalPolyPolygon)); + + // XPATHFILL_SEQ_BEGIN/XPATHFILL_SEQ_END support + SvtGraphicFill* pSvtGraphicFill = 0; + + if(!mnSvtGraphicFillCount && aLocalPolyPolygon.count()) + { + // setup gradient stuff like in like in impgrfll + SvtGraphicFill::GradientType eGrad(SvtGraphicFill::gradientLinear); + + switch(aVCLGradient.GetStyle()) + { + default : // GRADIENT_LINEAR: + case GRADIENT_AXIAL: + eGrad = SvtGraphicFill::gradientLinear; + break; + case GRADIENT_RADIAL: + case GRADIENT_ELLIPTICAL: + eGrad = SvtGraphicFill::gradientRadial; + break; + case GRADIENT_SQUARE: + case GRADIENT_RECT: + eGrad = SvtGraphicFill::gradientRectangular; + break; + } + + pSvtGraphicFill = new SvtGraphicFill( + aToolsPolyPolygon, + Color(), + 0.0, + SvtGraphicFill::fillEvenOdd, + SvtGraphicFill::fillGradient, + SvtGraphicFill::Transform(), + false, + SvtGraphicFill::hatchSingle, + Color(), + eGrad, + aVCLGradient.GetStartColor(), + aVCLGradient.GetEndColor(), + aVCLGradient.GetSteps(), + Graphic()); + } + + // call VCL directly; encapsulate with SvtGraphicFill + impStartSvtGraphicFill(pSvtGraphicFill); + mpOutputDevice->DrawGradient(aToolsPolyPolygon, aVCLGradient); + impEndSvtGraphicFill(pSvtGraphicFill); + + // NO usage of common own gradient randerer, not used ATM for VCL MetaFile, see text above + // RenderPolyPolygonGradientPrimitive2D(static_cast< const primitive2d::PolyPolygonGradientPrimitive2D& >(rCandidate)); + + break; + } + case PRIMITIVE2D_ID_POLYPOLYGONCOLORPRIMITIVE2D : + { + const primitive2d::PolyPolygonColorPrimitive2D& rPolygonCandidate(static_cast< const primitive2d::PolyPolygonColorPrimitive2D& >(rCandidate)); + basegfx::B2DPolyPolygon aLocalPolyPolygon(rPolygonCandidate.getB2DPolyPolygon()); + + // #i112245# Metafiles use tools Polygon and are not able to have more than 65535 points + // per polygon. Split polygon until there are less than that + while(fillPolyPolygonNeededToBeSplit(aLocalPolyPolygon)) + ; + + const basegfx::BColor aPolygonColor(maBColorModifierStack.getModifiedColor(rPolygonCandidate.getBColor())); + aLocalPolyPolygon.transform(maCurrentTransformation); + + // XPATHFILL_SEQ_BEGIN/XPATHFILL_SEQ_END support + SvtGraphicFill* pSvtGraphicFill = 0; + + if(!mnSvtGraphicFillCount && aLocalPolyPolygon.count()) + { + // setup simple color fill stuff like in impgrfll + pSvtGraphicFill = new SvtGraphicFill( + PolyPolygon(aLocalPolyPolygon), + Color(aPolygonColor), + 0.0, + SvtGraphicFill::fillEvenOdd, + SvtGraphicFill::fillSolid, + SvtGraphicFill::Transform(), + false, + SvtGraphicFill::hatchSingle, + Color(), + SvtGraphicFill::gradientLinear, + Color(), + Color(), + 0, + Graphic()); + } + + // set line and fill color + mpOutputDevice->SetFillColor(Color(aPolygonColor)); + mpOutputDevice->SetLineColor(); + + // call VCL directly; encapsulate with SvtGraphicFill + impStartSvtGraphicFill(pSvtGraphicFill); + mpOutputDevice->DrawPolyPolygon(aLocalPolyPolygon); + impEndSvtGraphicFill(pSvtGraphicFill); + + break; + } + case PRIMITIVE2D_ID_METAFILEPRIMITIVE2D : + { + // direct draw of MetaFile, use default pocessing + RenderMetafilePrimitive2D(static_cast< const primitive2d::MetafilePrimitive2D& >(rCandidate)); + break; + } + case PRIMITIVE2D_ID_MASKPRIMITIVE2D : + { + // mask group. Special handling for MetaFiles. + const primitive2d::MaskPrimitive2D& rMaskCandidate = static_cast< const primitive2d::MaskPrimitive2D& >(rCandidate); + + if(rMaskCandidate.getChildren().hasElements()) + { + basegfx::B2DPolyPolygon aMask(rMaskCandidate.getMask()); + + if(aMask.count()) + { + // prepare new mask polygon and rescue current one + aMask.transform(maCurrentTransformation); + const basegfx::B2DPolyPolygon aLastClipPolyPolygon(maClipPolyPolygon); + + if(maClipPolyPolygon.count()) + { + // there is already a clip polygon set; build clipped union of + // current mask polygon and new one + maClipPolyPolygon = basegfx::tools::clipPolyPolygonOnPolyPolygon( + aMask, + maClipPolyPolygon, + true, // #i106516# we want the inside of aMask, not the outside + false); + } + else + { + // use mask directly + maClipPolyPolygon = aMask; + } + + if(maClipPolyPolygon.count()) + { + // set VCL clip region; subdivide before conversion to tools polygon. Subdivision necessary (!) + // Removed subdivision and fixed in Region::ImplPolyPolyRegionToBandRegionFunc() in VCL where + // the ClipRegion is built from the Polygon. A AdaptiveSubdivide on the source polygon was missing there + mpOutputDevice->Push(PUSH_CLIPREGION); + //mpOutputDevice->SetClipRegion(Region(PolyPolygon(basegfx::tools::adaptiveSubdivideByAngle(maClipPolyPolygon)))); + mpOutputDevice->SetClipRegion(Region(PolyPolygon(maClipPolyPolygon))); + } + + // recursively paint content + process(rMaskCandidate.getChildren()); + + if(maClipPolyPolygon.count()) + { + // restore VCL clip region + mpOutputDevice->Pop(); + } + + // restore to rescued clip polygon + maClipPolyPolygon = aLastClipPolyPolygon; + } + else + { + // no mask, no clipping. recursively paint content + process(rMaskCandidate.getChildren()); + } + } + + break; + } + case PRIMITIVE2D_ID_MODIFIEDCOLORPRIMITIVE2D : + { + // modified color group. Force output to unified color. Use default pocessing. + RenderModifiedColorPrimitive2D(static_cast< const primitive2d::ModifiedColorPrimitive2D& >(rCandidate)); + break; + } + case PRIMITIVE2D_ID_HIDDENGEOMETRYPRIMITIVE2D : + { + // HiddenGeometryPrimitive2D; to rebuilt the old MetaFile creation, it is necessary to + // not ignore them (as it was thought), but to add a MetaFile entry for them. + basegfx::B2DRange aInvisibleRange(rCandidate.getB2DRange(getViewInformation2D())); + + if(!aInvisibleRange.isEmpty()) + { + aInvisibleRange.transform(maCurrentTransformation); + const Rectangle aRectLogic( + (sal_Int32)floor(aInvisibleRange.getMinX()), (sal_Int32)floor(aInvisibleRange.getMinY()), + (sal_Int32)ceil(aInvisibleRange.getMaxX()), (sal_Int32)ceil(aInvisibleRange.getMaxY())); + + mpOutputDevice->SetFillColor(); + mpOutputDevice->SetLineColor(); + mpOutputDevice->DrawRect(aRectLogic); + } + + break; + } + case PRIMITIVE2D_ID_UNIFIEDTRANSPARENCEPRIMITIVE2D : + { + // for metafile: Need to examine what the pure vcl version is doing here actually + // - uses DrawTransparent with metafile for content and a gradient + // - uses DrawTransparent for single PolyPoylgons directly. Can be detected by + // checking the content for single PolyPolygonColorPrimitive2D + const primitive2d::UnifiedTransparencePrimitive2D& rUniTransparenceCandidate = static_cast< const primitive2d::UnifiedTransparencePrimitive2D& >(rCandidate); + const primitive2d::Primitive2DSequence rContent = rUniTransparenceCandidate.getChildren(); + + if(rContent.hasElements()) + { + if(0.0 == rUniTransparenceCandidate.getTransparence()) + { + // not transparent at all, use content + process(rUniTransparenceCandidate.getChildren()); + } + else if(rUniTransparenceCandidate.getTransparence() > 0.0 && rUniTransparenceCandidate.getTransparence() < 1.0) + { + // try to identify a single PolyPolygonColorPrimitive2D in the + // content part of the transparence primitive + const primitive2d::PolyPolygonColorPrimitive2D* pPoPoColor = 0; + static bool bForceToMetafile(false); + + if(!bForceToMetafile && 1 == rContent.getLength()) + { + const primitive2d::Primitive2DReference xReference(rContent[0]); + pPoPoColor = dynamic_cast< const primitive2d::PolyPolygonColorPrimitive2D* >(xReference.get()); + } + + // PolyPolygonGradientPrimitive2D, PolyPolygonHatchPrimitive2D and + // PolyPolygonBitmapPrimitive2D are derived from PolyPolygonColorPrimitive2D. + // Check also for correct ID to exclude derived implementations + if(pPoPoColor && PRIMITIVE2D_ID_POLYPOLYGONCOLORPRIMITIVE2D == pPoPoColor->getPrimitive2DID()) + { + // single transparent PolyPolygon identified, use directly + const basegfx::BColor aPolygonColor(maBColorModifierStack.getModifiedColor(pPoPoColor->getBColor())); + basegfx::B2DPolyPolygon aLocalPolyPolygon(pPoPoColor->getB2DPolyPolygon()); + + // #i112245# Metafiles use tools Polygon and are not able to have more than 65535 points + // per polygon. Split polygon until there are less than that + while(fillPolyPolygonNeededToBeSplit(aLocalPolyPolygon)) + ; + + // now transform + aLocalPolyPolygon.transform(maCurrentTransformation); + + // XPATHFILL_SEQ_BEGIN/XPATHFILL_SEQ_END support + SvtGraphicFill* pSvtGraphicFill = 0; + + if(!mnSvtGraphicFillCount && aLocalPolyPolygon.count()) + { + // setup simple color with transparence fill stuff like in impgrfll + pSvtGraphicFill = new SvtGraphicFill( + PolyPolygon(aLocalPolyPolygon), + Color(aPolygonColor), + rUniTransparenceCandidate.getTransparence(), + SvtGraphicFill::fillEvenOdd, + SvtGraphicFill::fillSolid, + SvtGraphicFill::Transform(), + false, + SvtGraphicFill::hatchSingle, + Color(), + SvtGraphicFill::gradientLinear, + Color(), + Color(), + 0, + Graphic()); + } + + // set line and fill color + const sal_uInt16 nTransPercentVcl((sal_uInt16)basegfx::fround(rUniTransparenceCandidate.getTransparence() * 100.0)); + mpOutputDevice->SetFillColor(Color(aPolygonColor)); + mpOutputDevice->SetLineColor(); + + // call VCL directly; encapsulate with SvtGraphicFill + impStartSvtGraphicFill(pSvtGraphicFill); + mpOutputDevice->DrawTransparent( + PolyPolygon(aLocalPolyPolygon), + nTransPercentVcl); + impEndSvtGraphicFill(pSvtGraphicFill); + } + else + { + // svae old mfCurrentUnifiedTransparence and set new one + // so that contained SvtGraphicStroke may use the current one + const double fLastCurrentUnifiedTransparence(mfCurrentUnifiedTransparence); + // #i105377# paint the content metafile opaque as the transparency gets + // split of into the gradient below + // mfCurrentUnifiedTransparence = rUniTransparenceCandidate.getTransparence(); + mfCurrentUnifiedTransparence = 0; + + // various content, create content-metafile + GDIMetaFile aContentMetafile; + const Rectangle aPrimitiveRectangle(impDumpToMetaFile(rContent, aContentMetafile)); + + // restore mfCurrentUnifiedTransparence; it may have been used + // while processing the sub-content in impDumpToMetaFile + mfCurrentUnifiedTransparence = fLastCurrentUnifiedTransparence; + + // create uniform VCL gradient for uniform transparency + Gradient aVCLGradient; + const sal_uInt8 nTransPercentVcl((sal_uInt8)basegfx::fround(rUniTransparenceCandidate.getTransparence() * 255.0)); + const Color aTransColor(nTransPercentVcl, nTransPercentVcl, nTransPercentVcl); + + aVCLGradient.SetStyle(GRADIENT_LINEAR); + aVCLGradient.SetStartColor(aTransColor); + aVCLGradient.SetEndColor(aTransColor); + aVCLGradient.SetAngle(0); + aVCLGradient.SetBorder(0); + aVCLGradient.SetOfsX(0); + aVCLGradient.SetOfsY(0); + aVCLGradient.SetStartIntensity(100); + aVCLGradient.SetEndIntensity(100); + aVCLGradient.SetSteps(2); + + // render it to VCL + mpOutputDevice->DrawTransparent( + aContentMetafile, aPrimitiveRectangle.TopLeft(), + aPrimitiveRectangle.GetSize(), aVCLGradient); + } + } + } + + break; + } + case PRIMITIVE2D_ID_TRANSPARENCEPRIMITIVE2D : + { + // for metafile: Need to examine what the pure vcl version is doing here actually + // - uses DrawTransparent with metafile for content and a gradient + // i can detect this here with checking the gradient part for a single + // FillGradientPrimitive2D and reconstruct the gradient. + // If that detection goes wrong, i have to create an transparence-blended bitmap. Eventually + // do that in stripes, else RenderTransparencePrimitive2D may just be used + const primitive2d::TransparencePrimitive2D& rTransparenceCandidate = static_cast< const primitive2d::TransparencePrimitive2D& >(rCandidate); + const primitive2d::Primitive2DSequence rContent = rTransparenceCandidate.getChildren(); + const primitive2d::Primitive2DSequence rTransparence = rTransparenceCandidate.getTransparence(); + + if(rContent.hasElements() && rTransparence.hasElements()) + { + // try to identify a single FillGradientPrimitive2D in the + // transparence part of the primitive + const primitive2d::FillGradientPrimitive2D* pFiGradient = 0; + static bool bForceToBigTransparentVDev(false); + + if(!bForceToBigTransparentVDev && 1 == rTransparence.getLength()) + { + const primitive2d::Primitive2DReference xReference(rTransparence[0]); + pFiGradient = dynamic_cast< const primitive2d::FillGradientPrimitive2D* >(xReference.get()); + } + + // Check also for correct ID to exclude derived implementations + if(pFiGradient && PRIMITIVE2D_ID_FILLGRADIENTPRIMITIVE2D == pFiGradient->getPrimitive2DID()) + { + // various content, create content-metafile + GDIMetaFile aContentMetafile; + const Rectangle aPrimitiveRectangle(impDumpToMetaFile(rContent, aContentMetafile)); + + // re-create a VCL-gradient from FillGradientPrimitive2D + Gradient aVCLGradient; + impConvertFillGradientAttributeToVCLGradient(aVCLGradient, pFiGradient->getFillGradient(), true); + + // render it to VCL + mpOutputDevice->DrawTransparent( + aContentMetafile, aPrimitiveRectangle.TopLeft(), + aPrimitiveRectangle.GetSize(), aVCLGradient); + } + else + { + // sub-transparence group. Draw to VDev first. + // this may get refined to tiling when resolution is too big here + + // need to avoid switching off MapMode stuff here; maybe need another + // tooling class, cannot just do the same as with the pixel renderer. + // Need to experiment... + + // Okay, basic implementation finished and tested. The DPI stuff was hard + // and not easy to find out that it's needed. + // Since this will not yet happen normally (as long as noone constructs + // transparence primitives with non-trivial transparence content) i will for now not + // refine to tiling here. + + basegfx::B2DRange aViewRange(primitive2d::getB2DRangeFromPrimitive2DSequence(rContent, getViewInformation2D())); + aViewRange.transform(maCurrentTransformation); + const Rectangle aRectLogic( + (sal_Int32)floor(aViewRange.getMinX()), (sal_Int32)floor(aViewRange.getMinY()), + (sal_Int32)ceil(aViewRange.getMaxX()), (sal_Int32)ceil(aViewRange.getMaxY())); + const Rectangle aRectPixel(mpOutputDevice->LogicToPixel(aRectLogic)); + const Size aSizePixel(aRectPixel.GetSize()); + const Point aEmptyPoint; + VirtualDevice aBufferDevice; + + if(aBufferDevice.SetOutputSizePixel(aSizePixel)) + { + // create and set MapModes for target devices + MapMode aNewMapMode(mpOutputDevice->GetMapMode()); + aNewMapMode.SetOrigin(Point(-aRectLogic.Left(), -aRectLogic.Top())); + aBufferDevice.SetMapMode(aNewMapMode); + + // prepare view transformation for target renderers + // ATTENTION! Need to apply another scaling because of the potential DPI differences + // between Printer and VDev (mpOutputDevice and aBufferDevice here). + // To get the DPI, LogicToPixel from (1,1) from MAP_INCH needs to be used. + basegfx::B2DHomMatrix aViewTransform(aBufferDevice.GetViewTransformation()); + const Size aDPIOld(mpOutputDevice->LogicToPixel(Size(1, 1), MAP_INCH)); + const Size aDPINew(aBufferDevice.LogicToPixel(Size(1, 1), MAP_INCH)); + const double fDPIXChange((double)aDPIOld.getWidth() / (double)aDPINew.getWidth()); + const double fDPIYChange((double)aDPIOld.getHeight() / (double)aDPINew.getHeight()); + + if(!basegfx::fTools::equal(fDPIXChange, 1.0) || !basegfx::fTools::equal(fDPIYChange, 1.0)) + { + aViewTransform.scale(fDPIXChange, fDPIYChange); + } + + // create view information and pixel renderer. Reuse known ViewInformation + // except new transformation and range + const geometry::ViewInformation2D aViewInfo( + getViewInformation2D().getObjectTransformation(), + aViewTransform, + aViewRange, + getViewInformation2D().getVisualizedPage(), + getViewInformation2D().getViewTime(), + getViewInformation2D().getExtendedInformationSequence()); + + VclPixelProcessor2D aBufferProcessor(aViewInfo, aBufferDevice); + + // draw content using pixel renderer + aBufferProcessor.process(rContent); + const Bitmap aBmContent(aBufferDevice.GetBitmap(aEmptyPoint, aSizePixel)); + + // draw transparence using pixel renderer + aBufferDevice.Erase(); + aBufferProcessor.process(rTransparence); + const AlphaMask aBmAlpha(aBufferDevice.GetBitmap(aEmptyPoint, aSizePixel)); + +#ifdef DBG_UTIL + static bool bDoSaveForVisualControl(false); + if(bDoSaveForVisualControl) + { + SvFileStream aNew(String(ByteString( "c:\\test.bmp" ), RTL_TEXTENCODING_UTF8), STREAM_WRITE|STREAM_TRUNC); + aNew << aBmContent; + } +#endif + + // paint + mpOutputDevice->DrawBitmapEx( + aRectLogic.TopLeft(), + aRectLogic.GetSize(), + BitmapEx(aBmContent, aBmAlpha)); + } + } + } + + break; + } + case PRIMITIVE2D_ID_TRANSFORMPRIMITIVE2D : + { + // use default transform group pocessing + RenderTransformPrimitive2D(static_cast< const primitive2d::TransformPrimitive2D& >(rCandidate)); + break; + } + case PRIMITIVE2D_ID_PAGEPREVIEWPRIMITIVE2D : + { + // new XDrawPage for ViewInformation2D + RenderPagePreviewPrimitive2D(static_cast< const primitive2d::PagePreviewPrimitive2D& >(rCandidate)); + break; + } + case PRIMITIVE2D_ID_MARKERARRAYPRIMITIVE2D : + { + // use default marker array pocessing + RenderMarkerArrayPrimitive2D(static_cast< const primitive2d::MarkerArrayPrimitive2D& >(rCandidate)); + break; + } + case PRIMITIVE2D_ID_POINTARRAYPRIMITIVE2D : + { + // use default point array pocessing + RenderPointArrayPrimitive2D(static_cast< const primitive2d::PointArrayPrimitive2D& >(rCandidate)); + break; + } + case PRIMITIVE2D_ID_CHARTPRIMITIVE2D : + { + // ChartPrimitive2D + const primitive2d::ChartPrimitive2D& rChartPrimitive = static_cast< const primitive2d::ChartPrimitive2D& >(rCandidate); + + if(!renderChartPrimitive2D( + rChartPrimitive, + *mpOutputDevice, + getViewInformation2D())) + { + // fallback to decomposition (MetaFile) + process(rChartPrimitive.get2DDecomposition(getViewInformation2D())); + } + break; + } + case PRIMITIVE2D_ID_STRUCTURETAGPRIMITIVE2D : + { + // structured tag primitive + const primitive2d::StructureTagPrimitive2D& rStructureTagCandidate = static_cast< const primitive2d::StructureTagPrimitive2D& >(rCandidate); + const vcl::PDFWriter::StructElement& rTagElement(rStructureTagCandidate.getStructureElement()); + const bool bTagUsed(vcl::PDFWriter::NonStructElement != rTagElement); + + if(mpPDFExtOutDevData && bTagUsed) + { + // write start tag + mpPDFExtOutDevData->BeginStructureElement(rTagElement); + } + + // proccess childs normally + process(rStructureTagCandidate.getChildren()); + + if(mpPDFExtOutDevData && bTagUsed) + { + // write end tag + mpPDFExtOutDevData->EndStructureElement(); + } + + break; + } + case PRIMITIVE2D_ID_EPSPRIMITIVE2D : + { + RenderEpsPrimitive2D(static_cast< const primitive2d::EpsPrimitive2D& >(rCandidate)); + break; + } + default : + { + // process recursively + process(rCandidate.get2DDecomposition(getViewInformation2D())); + break; + } + } + } + } // end of namespace processor2d +} // end of namespace drawinglayer + +////////////////////////////////////////////////////////////////////////////// +// eof diff --git a/drawinglayer/source/processor2d/vclpixelprocessor2d.cxx b/drawinglayer/source/processor2d/vclpixelprocessor2d.cxx new file mode 100644 index 000000000000..6a280fcad95e --- /dev/null +++ b/drawinglayer/source/processor2d/vclpixelprocessor2d.cxx @@ -0,0 +1,613 @@ +/************************************************************************* + * + * 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_drawinglayer.hxx" + +#include <drawinglayer/processor2d/vclpixelprocessor2d.hxx> +#include <vcl/outdev.hxx> +#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx> +#include <drawinglayer/primitive2d/textprimitive2d.hxx> +#include <drawinglayer/primitive2d/polypolygonprimitive2d.hxx> +#include <drawinglayer/primitive2d/polygonprimitive2d.hxx> +#include <drawinglayer/primitive2d/bitmapprimitive2d.hxx> +#include <drawinglayer/primitive2d/fillbitmapprimitive2d.hxx> +#include <drawinglayer/primitive2d/metafileprimitive2d.hxx> +#include <drawinglayer/primitive2d/maskprimitive2d.hxx> +#include <drawinglayer/primitive2d/modifiedcolorprimitive2d.hxx> +#include <drawinglayer/primitive2d/transparenceprimitive2d.hxx> +#include <drawinglayer/primitive2d/transformprimitive2d.hxx> +#include <drawinglayer/primitive2d/markerarrayprimitive2d.hxx> +#include <drawinglayer/primitive2d/pointarrayprimitive2d.hxx> +#include <drawinglayer/primitive2d/wrongspellprimitive2d.hxx> +#include <drawinglayer/primitive2d/controlprimitive2d.hxx> +#include <com/sun/star/awt/XWindow2.hpp> +#include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx> +#include <drawinglayer/primitive2d/pagepreviewprimitive2d.hxx> +#include <drawinglayer/primitive2d/chartprimitive2d.hxx> +#include <helperchartrenderer.hxx> +#include <helperwrongspellrenderer.hxx> +#include <drawinglayer/primitive2d/fillhatchprimitive2d.hxx> +#include <basegfx/polygon/b2dpolygontools.hxx> +#include <vcl/hatch.hxx> +#include <tools/diagnose_ex.h> +#include <com/sun/star/awt/PosSize.hpp> +#include <drawinglayer/primitive2d/invertprimitive2d.hxx> +#include <cstdio> +#include <drawinglayer/primitive2d/backgroundcolorprimitive2d.hxx> +#include <basegfx/matrix/b2dhommatrixtools.hxx> +#include <drawinglayer/primitive2d/epsprimitive2d.hxx> + +#include <toolkit/helper/vclunohelper.hxx> +#include <vcl/window.hxx> + +////////////////////////////////////////////////////////////////////////////// + +using namespace com::sun::star; + +////////////////////////////////////////////////////////////////////////////// + +namespace drawinglayer +{ + namespace processor2d + { + VclPixelProcessor2D::VclPixelProcessor2D(const geometry::ViewInformation2D& rViewInformation, OutputDevice& rOutDev) + : VclProcessor2D(rViewInformation, rOutDev), + maOriginalMapMode(rOutDev.GetMapMode()) + { + // prepare maCurrentTransformation matrix with viewTransformation to target directly to pixels + maCurrentTransformation = rViewInformation.getObjectToViewTransformation(); + + // prepare output directly to pixels + mpOutputDevice->Push(PUSH_MAPMODE); + mpOutputDevice->SetMapMode(); + + // react on AntiAliasing settings + if(getOptionsDrawinglayer().IsAntiAliasing()) + { + mpOutputDevice->SetAntialiasing(mpOutputDevice->GetAntialiasing() | ANTIALIASING_ENABLE_B2DDRAW); + } + else + { + mpOutputDevice->SetAntialiasing(mpOutputDevice->GetAntialiasing() & ~ANTIALIASING_ENABLE_B2DDRAW); + } + } + + VclPixelProcessor2D::~VclPixelProcessor2D() + { + // restore MapMode + mpOutputDevice->Pop(); + + // restore AntiAliasing + mpOutputDevice->SetAntialiasing(mpOutputDevice->GetAntialiasing() & ~ANTIALIASING_ENABLE_B2DDRAW); + } + + void VclPixelProcessor2D::processBasePrimitive2D(const primitive2d::BasePrimitive2D& rCandidate) + { + switch(rCandidate.getPrimitive2DID()) + { + case PRIMITIVE2D_ID_WRONGSPELLPRIMITIVE2D : + { + // directdraw of wrong spell primitive; added test possibility to check wrong spell decompose + static bool bHandleWrongSpellDirectly(true); + + if(bHandleWrongSpellDirectly) + { + const primitive2d::WrongSpellPrimitive2D& rWrongSpellPrimitive = static_cast< const primitive2d::WrongSpellPrimitive2D& >(rCandidate); + + if(!renderWrongSpellPrimitive2D( + rWrongSpellPrimitive, + *mpOutputDevice, + maCurrentTransformation, + maBColorModifierStack)) + { + // fallback to decomposition (MetaFile) + process(rWrongSpellPrimitive.get2DDecomposition(getViewInformation2D())); + } + } + else + { + process(rCandidate.get2DDecomposition(getViewInformation2D())); + } + break; + } + case PRIMITIVE2D_ID_TEXTSIMPLEPORTIONPRIMITIVE2D : + { + // directdraw of text simple portion; added test possibility to check text decompose + static bool bForceSimpleTextDecomposition(false); + + // Adapt evtl. used special DrawMode + const sal_uInt32 nOriginalDrawMode(mpOutputDevice->GetDrawMode()); + adaptTextToFillDrawMode(); + + if(!bForceSimpleTextDecomposition && getOptionsDrawinglayer().IsRenderSimpleTextDirect()) + { + RenderTextSimpleOrDecoratedPortionPrimitive2D(static_cast< const primitive2d::TextSimplePortionPrimitive2D& >(rCandidate)); + } + else + { + process(rCandidate.get2DDecomposition(getViewInformation2D())); + } + + // restore DrawMode + mpOutputDevice->SetDrawMode(nOriginalDrawMode); + + break; + } + case PRIMITIVE2D_ID_TEXTDECORATEDPORTIONPRIMITIVE2D : + { + // directdraw of text simple portion; added test possibility to check text decompose + static bool bForceComplexTextDecomposition(false); + + // Adapt evtl. used special DrawMode + const sal_uInt32 nOriginalDrawMode(mpOutputDevice->GetDrawMode()); + adaptTextToFillDrawMode(); + + if(!bForceComplexTextDecomposition && getOptionsDrawinglayer().IsRenderDecoratedTextDirect()) + { + RenderTextSimpleOrDecoratedPortionPrimitive2D(static_cast< const primitive2d::TextSimplePortionPrimitive2D& >(rCandidate)); + } + else + { + process(rCandidate.get2DDecomposition(getViewInformation2D())); + } + + // restore DrawMode + mpOutputDevice->SetDrawMode(nOriginalDrawMode); + + break; + } + case PRIMITIVE2D_ID_POLYGONHAIRLINEPRIMITIVE2D : + { + // direct draw of hairline + RenderPolygonHairlinePrimitive2D(static_cast< const primitive2d::PolygonHairlinePrimitive2D& >(rCandidate), true); + break; + } + case PRIMITIVE2D_ID_BITMAPPRIMITIVE2D : + { + // direct draw of transformed BitmapEx primitive + RenderBitmapPrimitive2D(static_cast< const primitive2d::BitmapPrimitive2D& >(rCandidate)); + break; + } + case PRIMITIVE2D_ID_FILLBITMAPPRIMITIVE2D : + { + // direct draw of fillBitmapPrimitive + RenderFillBitmapPrimitive2D(static_cast< const primitive2d::FillBitmapPrimitive2D& >(rCandidate)); + break; + } + case PRIMITIVE2D_ID_POLYPOLYGONGRADIENTPRIMITIVE2D : + { + // direct draw of gradient + RenderPolyPolygonGradientPrimitive2D(static_cast< const primitive2d::PolyPolygonGradientPrimitive2D& >(rCandidate)); + break; + } + case PRIMITIVE2D_ID_POLYPOLYGONBITMAPPRIMITIVE2D : + { + // direct draw of bitmap + RenderPolyPolygonBitmapPrimitive2D(static_cast< const primitive2d::PolyPolygonBitmapPrimitive2D& >(rCandidate)); + break; + } + case PRIMITIVE2D_ID_POLYPOLYGONCOLORPRIMITIVE2D : + { + // direct draw of PolyPolygon with color + RenderPolyPolygonColorPrimitive2D(static_cast< const primitive2d::PolyPolygonColorPrimitive2D& >(rCandidate)); + break; + } + case PRIMITIVE2D_ID_METAFILEPRIMITIVE2D : + { + // #i98289# + const bool bForceLineSnap(getOptionsDrawinglayer().IsAntiAliasing() && getOptionsDrawinglayer().IsSnapHorVerLinesToDiscrete()); + const sal_uInt16 nOldAntiAliase(mpOutputDevice->GetAntialiasing()); + + if(bForceLineSnap) + { + mpOutputDevice->SetAntialiasing(nOldAntiAliase | ANTIALIASING_PIXELSNAPHAIRLINE); + } + + static bool bTestMetaFilePrimitiveDecomposition(true); + if(bTestMetaFilePrimitiveDecomposition) + { + // use new Metafile decomposition + process(rCandidate.get2DDecomposition(getViewInformation2D())); + } + else + { + // direct draw of MetaFile + RenderMetafilePrimitive2D(static_cast< const primitive2d::MetafilePrimitive2D& >(rCandidate)); + } + + if(bForceLineSnap) + { + mpOutputDevice->SetAntialiasing(nOldAntiAliase); + } + + break; + } + case PRIMITIVE2D_ID_MASKPRIMITIVE2D : + { + // mask group. + RenderMaskPrimitive2DPixel(static_cast< const primitive2d::MaskPrimitive2D& >(rCandidate)); + break; + } + case PRIMITIVE2D_ID_MODIFIEDCOLORPRIMITIVE2D : + { + // modified color group. Force output to unified color. + RenderModifiedColorPrimitive2D(static_cast< const primitive2d::ModifiedColorPrimitive2D& >(rCandidate)); + break; + } + case PRIMITIVE2D_ID_UNIFIEDTRANSPARENCEPRIMITIVE2D : + { + // Detect if a single PolyPolygonColorPrimitive2D is contained; in that case, + // use the faster OutputDevice::DrawTransparent method + const primitive2d::UnifiedTransparencePrimitive2D& rUniTransparenceCandidate = static_cast< const primitive2d::UnifiedTransparencePrimitive2D& >(rCandidate); + const primitive2d::Primitive2DSequence rContent = rUniTransparenceCandidate.getChildren(); + + if(rContent.hasElements()) + { + if(0.0 == rUniTransparenceCandidate.getTransparence()) + { + // not transparent at all, use content + process(rUniTransparenceCandidate.getChildren()); + } + else if(rUniTransparenceCandidate.getTransparence() > 0.0 && rUniTransparenceCandidate.getTransparence() < 1.0) + { + bool bDrawTransparentUsed(false); + + // since DEV300 m33 DrawTransparent is supported in VCL (for some targets + // natively), so i am now enabling this shortcut + static bool bAllowUsingDrawTransparent(true); + + if(bAllowUsingDrawTransparent && 1 == rContent.getLength()) + { + const primitive2d::Primitive2DReference xReference(rContent[0]); + const primitive2d::BasePrimitive2D* pBasePrimitive = dynamic_cast< const primitive2d::BasePrimitive2D* >(xReference.get()); + + if(pBasePrimitive) + { + switch(pBasePrimitive->getPrimitive2DID()) + { + case PRIMITIVE2D_ID_POLYPOLYGONCOLORPRIMITIVE2D: + { + // single transparent PolyPolygon identified, use directly + const primitive2d::PolyPolygonColorPrimitive2D* pPoPoColor = static_cast< const primitive2d::PolyPolygonColorPrimitive2D* >(pBasePrimitive); + OSL_ENSURE(pPoPoColor, "OOps, PrimitiveID and PrimitiveType do not match (!)"); + const basegfx::BColor aPolygonColor(maBColorModifierStack.getModifiedColor(pPoPoColor->getBColor())); + mpOutputDevice->SetFillColor(Color(aPolygonColor)); + mpOutputDevice->SetLineColor(); + + basegfx::B2DPolyPolygon aLocalPolyPolygon(pPoPoColor->getB2DPolyPolygon()); + aLocalPolyPolygon.transform(maCurrentTransformation); + + mpOutputDevice->DrawTransparent(aLocalPolyPolygon, rUniTransparenceCandidate.getTransparence()); + bDrawTransparentUsed = true; + break; + } + // #i# need to wait for #i101378# which is in CWS vcl112 to directly paint transparent hairlines + //case PRIMITIVE2D_ID_POLYGONHAIRLINEPRIMITIVE2D: + //{ + // // single transparent PolygonHairlinePrimitive2D identified, use directly + // const primitive2d::PolygonHairlinePrimitive2D* pPoHair = static_cast< const primitive2d::PolygonHairlinePrimitive2D* >(pBasePrimitive); + // OSL_ENSURE(pPoHair, "OOps, PrimitiveID and PrimitiveType do not match (!)"); + // break; + //} + } + } + } + + if(!bDrawTransparentUsed) + { + // unified sub-transparence. Draw to VDev first. + RenderUnifiedTransparencePrimitive2D(rUniTransparenceCandidate); + } + } + } + + break; + } + case PRIMITIVE2D_ID_TRANSPARENCEPRIMITIVE2D : + { + // sub-transparence group. Draw to VDev first. + RenderTransparencePrimitive2D(static_cast< const primitive2d::TransparencePrimitive2D& >(rCandidate)); + break; + } + case PRIMITIVE2D_ID_TRANSFORMPRIMITIVE2D : + { + // transform group. + RenderTransformPrimitive2D(static_cast< const primitive2d::TransformPrimitive2D& >(rCandidate)); + break; + } + case PRIMITIVE2D_ID_PAGEPREVIEWPRIMITIVE2D : + { + // new XDrawPage for ViewInformation2D + RenderPagePreviewPrimitive2D(static_cast< const primitive2d::PagePreviewPrimitive2D& >(rCandidate)); + break; + } + case PRIMITIVE2D_ID_MARKERARRAYPRIMITIVE2D : + { + // marker array + RenderMarkerArrayPrimitive2D(static_cast< const primitive2d::MarkerArrayPrimitive2D& >(rCandidate)); + break; + } + case PRIMITIVE2D_ID_POINTARRAYPRIMITIVE2D : + { + // point array + RenderPointArrayPrimitive2D(static_cast< const primitive2d::PointArrayPrimitive2D& >(rCandidate)); + break; + } + case PRIMITIVE2D_ID_CONTROLPRIMITIVE2D : + { + // control primitive + const primitive2d::ControlPrimitive2D& rControlPrimitive = static_cast< const primitive2d::ControlPrimitive2D& >(rCandidate); + const uno::Reference< awt::XControl >& rXControl(rControlPrimitive.getXControl()); + + try + { + // remember old graphics and create new + uno::Reference< awt::XView > xControlView(rXControl, uno::UNO_QUERY_THROW); + const uno::Reference< awt::XGraphics > xOriginalGraphics(xControlView->getGraphics()); + const uno::Reference< awt::XGraphics > xNewGraphics(mpOutputDevice->CreateUnoGraphics()); + + if(xNewGraphics.is()) + { + // link graphics and view + xControlView->setGraphics(xNewGraphics); + + // get position + const basegfx::B2DHomMatrix aObjectToPixel(maCurrentTransformation * rControlPrimitive.getTransform()); + const basegfx::B2DPoint aTopLeftPixel(aObjectToPixel * basegfx::B2DPoint(0.0, 0.0)); + + // find out if the control is already visualized as a VCL-ChildWindow. If yes, + // it does not need to be painted at all. + uno::Reference< awt::XWindow2 > xControlWindow(rXControl, uno::UNO_QUERY_THROW); + const bool bControlIsVisibleAsChildWindow(rXControl->getPeer().is() && xControlWindow->isVisible()); + + if(!bControlIsVisibleAsChildWindow) + { + // draw it. Do not forget to use the evtl. offsetted origin of the target device, + // e.g. when used with mask/transparence buffer device + const Point aOrigin(mpOutputDevice->GetMapMode().GetOrigin()); + xControlView->draw( + aOrigin.X() + basegfx::fround(aTopLeftPixel.getX()), + aOrigin.Y() + basegfx::fround(aTopLeftPixel.getY())); + } + + // restore original graphics + xControlView->setGraphics(xOriginalGraphics); + } + } + catch(const uno::Exception&) + { + DBG_UNHANDLED_EXCEPTION(); + + // process recursively and use the decomposition as Bitmap + process(rCandidate.get2DDecomposition(getViewInformation2D())); + } + + break; + } + case PRIMITIVE2D_ID_POLYGONSTROKEPRIMITIVE2D: + { + // the stroke primitive may be decomposed to filled polygons. To keep + // evtl. set DrawModes aka DRAWMODE_BLACKLINE, DRAWMODE_GRAYLINE, + // DRAWMODE_GHOSTEDLINE, DRAWMODE_WHITELINE or DRAWMODE_SETTINGSLINE + // working, these need to be copied to the corresponding fill modes + const sal_uInt32 nOriginalDrawMode(mpOutputDevice->GetDrawMode()); + adaptLineToFillDrawMode(); + + // polygon stroke primitive + static bool bSuppressFatToHairlineCorrection(false); + + if(bSuppressFatToHairlineCorrection) + { + // remeber that we enter a PolygonStrokePrimitive2D decomposition, + // used for AA thick line drawing + mnPolygonStrokePrimitive2D++; + + // with AA there is no need to handle thin lines special + process(rCandidate.get2DDecomposition(getViewInformation2D())); + + // leave PolygonStrokePrimitive2D + mnPolygonStrokePrimitive2D--; + } + else + { + // Lines with 1 and 2 pixel width without AA need special treatment since their vsiualisation + // as filled polygons is geometrically corret but looks wrong since polygon filling avoids + // the right and bottom pixels. The used method evaluates that and takes the correct action, + // including calling recursively with decomposition if line is wide enough + const primitive2d::PolygonStrokePrimitive2D& rPolygonStrokePrimitive = static_cast< const primitive2d::PolygonStrokePrimitive2D& >(rCandidate); + + RenderPolygonStrokePrimitive2D(rPolygonStrokePrimitive); + } + + // restore DrawMode + mpOutputDevice->SetDrawMode(nOriginalDrawMode); + + break; + } + case PRIMITIVE2D_ID_CHARTPRIMITIVE2D : + { + // chart primitive in pixel renderer; restore original DrawMode during call + // since the evtl. used ChartPrettyPainter will use the MapMode + const primitive2d::ChartPrimitive2D& rChartPrimitive = static_cast< const primitive2d::ChartPrimitive2D& >(rCandidate); + mpOutputDevice->Push(PUSH_MAPMODE); + mpOutputDevice->SetMapMode(maOriginalMapMode); + + if(!renderChartPrimitive2D( + rChartPrimitive, + *mpOutputDevice, + getViewInformation2D())) + { + // fallback to decomposition (MetaFile) + process(rChartPrimitive.get2DDecomposition(getViewInformation2D())); + } + + mpOutputDevice->Pop(); + break; + } + case PRIMITIVE2D_ID_FILLHATCHPRIMITIVE2D : + { + static bool bForceIgnoreHatchSmoothing(false); + + if(bForceIgnoreHatchSmoothing || getOptionsDrawinglayer().IsAntiAliasing()) + { + // if AA is used (or ignore smoothing is on), there is no need to smooth + // hatch painting, use decomposition + process(rCandidate.get2DDecomposition(getViewInformation2D())); + } + else + { + // without AA, use VCL to draw the hatch. It snaps hatch distances to the next pixel + // and forces hatch distance to be >= 3 pixels to make the hatch display look smoother. + // This is wrong in principle, but looks nicer. This could also be done here directly + // without VCL usage if needed + const primitive2d::FillHatchPrimitive2D& rFillHatchPrimitive = static_cast< const primitive2d::FillHatchPrimitive2D& >(rCandidate); + const attribute::FillHatchAttribute& rFillHatchAttributes = rFillHatchPrimitive.getFillHatch(); + + // create hatch polygon in range size and discrete coordinates + basegfx::B2DRange aHatchRange(rFillHatchPrimitive.getObjectRange()); + aHatchRange.transform(maCurrentTransformation); + const basegfx::B2DPolygon aHatchPolygon(basegfx::tools::createPolygonFromRect(aHatchRange)); + + if(rFillHatchAttributes.isFillBackground()) + { + // #i111846# background fill is active; draw fill polygon + const basegfx::BColor aPolygonColor(maBColorModifierStack.getModifiedColor(rFillHatchPrimitive.getBColor())); + + mpOutputDevice->SetFillColor(Color(aPolygonColor)); + mpOutputDevice->SetLineColor(); + mpOutputDevice->DrawPolygon(aHatchPolygon); + } + + // set hatch line color + const basegfx::BColor aHatchColor(maBColorModifierStack.getModifiedColor(rFillHatchPrimitive.getBColor())); + mpOutputDevice->SetFillColor(); + mpOutputDevice->SetLineColor(Color(aHatchColor)); + + // get hatch style + HatchStyle eHatchStyle(HATCH_SINGLE); + + switch(rFillHatchAttributes.getStyle()) + { + default : // HATCHSTYLE_SINGLE + { + break; + } + case attribute::HATCHSTYLE_DOUBLE : + { + eHatchStyle = HATCH_DOUBLE; + break; + } + case attribute::HATCHSTYLE_TRIPLE : + { + eHatchStyle = HATCH_TRIPLE; + break; + } + } + + // create hatch + const basegfx::B2DVector aDiscreteDistance(maCurrentTransformation * basegfx::B2DVector(rFillHatchAttributes.getDistance(), 0.0)); + const sal_uInt32 nDistance(basegfx::fround(aDiscreteDistance.getLength())); + const sal_uInt16 nAngle10((sal_uInt16)basegfx::fround(rFillHatchAttributes.getAngle() / F_PI1800)); + ::Hatch aVCLHatch(eHatchStyle, Color(rFillHatchAttributes.getColor()), nDistance, nAngle10); + + // draw hatch using VCL + mpOutputDevice->DrawHatch(PolyPolygon(Polygon(aHatchPolygon)), aVCLHatch); + } + break; + } + case PRIMITIVE2D_ID_BACKGROUNDCOLORPRIMITIVE2D : + { + // #i98404# Handle directly, especially when AA is active + const primitive2d::BackgroundColorPrimitive2D& rPrimitive = static_cast< const primitive2d::BackgroundColorPrimitive2D& >(rCandidate); + const sal_uInt16 nOriginalAA(mpOutputDevice->GetAntialiasing()); + + // switch AA off in all cases + mpOutputDevice->SetAntialiasing(mpOutputDevice->GetAntialiasing() & ~ANTIALIASING_ENABLE_B2DDRAW); + + // create color for fill + const basegfx::BColor aPolygonColor(maBColorModifierStack.getModifiedColor(rPrimitive.getBColor())); + mpOutputDevice->SetFillColor(Color(aPolygonColor)); + mpOutputDevice->SetLineColor(); + + // create rectangle for fill + const basegfx::B2DRange& aViewport(getViewInformation2D().getDiscreteViewport()); + const Rectangle aRectangle( + (sal_Int32)floor(aViewport.getMinX()), (sal_Int32)floor(aViewport.getMinY()), + (sal_Int32)ceil(aViewport.getMaxX()), (sal_Int32)ceil(aViewport.getMaxY())); + mpOutputDevice->DrawRect(aRectangle); + + // restore AA setting + mpOutputDevice->SetAntialiasing(nOriginalAA); + break; + } + case PRIMITIVE2D_ID_TEXTHIERARCHYEDITPRIMITIVE2D : + { + // #i97628# + // This primitive means that the content is derived from an active text edit, + // not from model data itself. Some renderers need to suppress this content, e.g. + // the pixel renderer used for displaying the edit view (like this one). It's + // not to be suppressed by the MetaFile renderers, so that the edited text is + // part of the MetaFile, e.g. needed for presentation previews. + // Action: Ignore here, do nothing. + break; + } + case PRIMITIVE2D_ID_INVERTPRIMITIVE2D : + { + // invert primitive (currently only used for HighContrast fallback for selection in SW and SC). + // Set OutDev to XOR and switch AA off (XOR does not work with AA) + mpOutputDevice->Push(); + mpOutputDevice->SetRasterOp( ROP_XOR ); + const sal_uInt16 nAntiAliasing(mpOutputDevice->GetAntialiasing()); + mpOutputDevice->SetAntialiasing(nAntiAliasing & ~ANTIALIASING_ENABLE_B2DDRAW); + + // process content recursively + process(rCandidate.get2DDecomposition(getViewInformation2D())); + + // restore OutDev + mpOutputDevice->Pop(); + mpOutputDevice->SetAntialiasing(nAntiAliasing); + break; + } + case PRIMITIVE2D_ID_EPSPRIMITIVE2D : + { + RenderEpsPrimitive2D(static_cast< const primitive2d::EpsPrimitive2D& >(rCandidate)); + break; + } + default : + { + // process recursively + process(rCandidate.get2DDecomposition(getViewInformation2D())); + break; + } + } + } + } // end of namespace processor2d +} // end of namespace drawinglayer + +////////////////////////////////////////////////////////////////////////////// +// eof diff --git a/drawinglayer/source/processor2d/vclprocessor2d.cxx b/drawinglayer/source/processor2d/vclprocessor2d.cxx new file mode 100644 index 000000000000..e49e54fb751c --- /dev/null +++ b/drawinglayer/source/processor2d/vclprocessor2d.cxx @@ -0,0 +1,1492 @@ +/************************************************************************* + * + * 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_drawinglayer.hxx" + +#include <drawinglayer/processor2d/vclprocessor2d.hxx> +#include <drawinglayer/primitive2d/textprimitive2d.hxx> +#include <drawinglayer/primitive2d/textdecoratedprimitive2d.hxx> +#include <tools/debug.hxx> +#include <vcl/outdev.hxx> +#include <drawinglayer/primitive2d/polygonprimitive2d.hxx> +#include <drawinglayer/primitive2d/bitmapprimitive2d.hxx> +#include <vclhelperbitmaptransform.hxx> +#include <basegfx/polygon/b2dpolygontools.hxx> +#include <vclhelperbitmaprender.hxx> +#include <drawinglayer/attribute/sdrfillbitmapattribute.hxx> +#include <drawinglayer/primitive2d/fillbitmapprimitive2d.hxx> +#include <drawinglayer/primitive2d/polypolygonprimitive2d.hxx> +#include <vclhelpergradient.hxx> +#include <drawinglayer/primitive2d/metafileprimitive2d.hxx> +#include <drawinglayer/primitive2d/maskprimitive2d.hxx> +#include <basegfx/polygon/b2dpolypolygontools.hxx> +#include <vclhelperbufferdevice.hxx> +#include <drawinglayer/primitive2d/modifiedcolorprimitive2d.hxx> +#include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx> +#include <drawinglayer/primitive2d/transparenceprimitive2d.hxx> +#include <drawinglayer/primitive2d/transformprimitive2d.hxx> +#include <drawinglayer/primitive2d/markerarrayprimitive2d.hxx> +#include <drawinglayer/primitive2d/pointarrayprimitive2d.hxx> +#include <drawinglayer/primitive2d/wrongspellprimitive2d.hxx> +#include <svl/ctloptions.hxx> +#include <vcl/svapp.hxx> +#include <drawinglayer/primitive2d/pagepreviewprimitive2d.hxx> +#include <tools/diagnose_ex.h> +#include <vcl/metric.hxx> +#include <drawinglayer/primitive2d/textenumsprimitive2d.hxx> +#include <drawinglayer/primitive2d/epsprimitive2d.hxx> + +////////////////////////////////////////////////////////////////////////////// +// control support + +#include <com/sun/star/awt/XWindow2.hpp> +#include <com/sun/star/awt/PosSize.hpp> +#include <com/sun/star/awt/XView.hpp> +#include <drawinglayer/primitive2d/controlprimitive2d.hxx> +#include <drawinglayer/primitive2d/textlayoutdevice.hxx> + +////////////////////////////////////////////////////////////////////////////// +// for test, can be removed again + +#include <basegfx/polygon/b2dpolygonclipper.hxx> +#include <basegfx/polygon/b2dtrapezoid.hxx> + +////////////////////////////////////////////////////////////////////////////// + +using namespace com::sun::star; + +////////////////////////////////////////////////////////////////////////////// + +namespace drawinglayer +{ + namespace processor2d + { + ////////////////////////////////////////////////////////////////////////////// + // UNO class usages + using ::com::sun::star::uno::Reference; + using ::com::sun::star::uno::UNO_QUERY; + using ::com::sun::star::uno::UNO_QUERY_THROW; + using ::com::sun::star::uno::Exception; + using ::com::sun::star::awt::XView; + using ::com::sun::star::awt::XGraphics; + using ::com::sun::star::awt::XWindow; + using ::com::sun::star::awt::PosSize::POSSIZE; + + ////////////////////////////////////////////////////////////////////////////// + // rendering support + + // directdraw of text simple portion or decorated portion primitive. When decorated, all the extra + // information is translated to VCL parameters and set at the font. + // Acceptance is restricted to no shearing and positive scaling in X and Y (no font mirroring + // for VCL) + void VclProcessor2D::RenderTextSimpleOrDecoratedPortionPrimitive2D(const primitive2d::TextSimplePortionPrimitive2D& rTextCandidate) + { + // decompose matrix to have position and size of text + basegfx::B2DHomMatrix aLocalTransform(maCurrentTransformation * rTextCandidate.getTextTransform()); + basegfx::B2DVector aFontScaling, aTranslate; + double fRotate, fShearX; + aLocalTransform.decompose(aFontScaling, aTranslate, fRotate, fShearX); + bool bPrimitiveAccepted(false); + + if(basegfx::fTools::equalZero(fShearX)) + { + if(basegfx::fTools::less(aFontScaling.getX(), 0.0) && basegfx::fTools::less(aFontScaling.getY(), 0.0)) + { + // handle special case: If scale is negative in (x,y) (3rd quadrant), it can + // be expressed as rotation by PI. Use this since the Font rendering will not + // apply the negative scales in any form + aFontScaling = basegfx::absolute(aFontScaling); + fRotate += F_PI; + } + + if(basegfx::fTools::more(aFontScaling.getX(), 0.0) && basegfx::fTools::more(aFontScaling.getY(), 0.0)) + { + // Get the VCL font (use FontHeight as FontWidth) + Font aFont(primitive2d::getVclFontFromFontAttribute( + rTextCandidate.getFontAttribute(), + aFontScaling.getX(), + aFontScaling.getY(), + fRotate, + rTextCandidate.getLocale())); + + // handle additional font attributes + const primitive2d::TextDecoratedPortionPrimitive2D* pTCPP = + dynamic_cast<const primitive2d::TextDecoratedPortionPrimitive2D*>( &rTextCandidate ); + + if( pTCPP != NULL ) + { + + // set the color of text decorations + const basegfx::BColor aTextlineColor = maBColorModifierStack.getModifiedColor(pTCPP->getTextlineColor()); + mpOutputDevice->SetTextLineColor( Color(aTextlineColor) ); + + // set Overline attribute + const FontUnderline eFontOverline(primitive2d::mapTextLineToFontUnderline( pTCPP->getFontOverline() )); + if( eFontOverline != UNDERLINE_NONE ) + { + aFont.SetOverline( eFontOverline ); + const basegfx::BColor aOverlineColor = maBColorModifierStack.getModifiedColor(pTCPP->getOverlineColor()); + mpOutputDevice->SetOverlineColor( Color(aOverlineColor) ); + if( pTCPP->getWordLineMode() ) + aFont.SetWordLineMode( true ); + } + + // set Underline attribute + const FontUnderline eFontUnderline(primitive2d::mapTextLineToFontUnderline( pTCPP->getFontUnderline() )); + if( eFontUnderline != UNDERLINE_NONE ) + { + aFont.SetUnderline( eFontUnderline ); + if( pTCPP->getWordLineMode() ) + aFont.SetWordLineMode( true ); +//TODO: ??? if( pTCPP->getUnderlineAbove() ) +// aFont.SetUnderlineAbove( true ); + } + + // set Strikeout attribute + const FontStrikeout eFontStrikeout(primitive2d::mapTextStrikeoutToFontStrikeout(pTCPP->getTextStrikeout())); + + if( eFontStrikeout != STRIKEOUT_NONE ) + aFont.SetStrikeout( eFontStrikeout ); + + // set EmphasisMark attribute + FontEmphasisMark eFontEmphasisMark = EMPHASISMARK_NONE; + switch( pTCPP->getTextEmphasisMark() ) + { + default: + DBG_WARNING1( "DrawingLayer: Unknown EmphasisMark style (%d)!", pTCPP->getTextEmphasisMark() ); + // fall through + case primitive2d::TEXT_EMPHASISMARK_NONE: eFontEmphasisMark = EMPHASISMARK_NONE; break; + case primitive2d::TEXT_EMPHASISMARK_DOT: eFontEmphasisMark = EMPHASISMARK_DOT; break; + case primitive2d::TEXT_EMPHASISMARK_CIRCLE: eFontEmphasisMark = EMPHASISMARK_CIRCLE; break; + case primitive2d::TEXT_EMPHASISMARK_DISC: eFontEmphasisMark = EMPHASISMARK_DISC; break; + case primitive2d::TEXT_EMPHASISMARK_ACCENT: eFontEmphasisMark = EMPHASISMARK_ACCENT; break; + } + + if( eFontEmphasisMark != EMPHASISMARK_NONE ) + { + DBG_ASSERT( (pTCPP->getEmphasisMarkAbove() != pTCPP->getEmphasisMarkBelow()), + "DrawingLayer: Bad EmphasisMark position!" ); + if( pTCPP->getEmphasisMarkAbove() ) + eFontEmphasisMark |= EMPHASISMARK_POS_ABOVE; + else + eFontEmphasisMark |= EMPHASISMARK_POS_BELOW; + aFont.SetEmphasisMark( eFontEmphasisMark ); + } + + // set Relief attribute + FontRelief eFontRelief = RELIEF_NONE; + switch( pTCPP->getTextRelief() ) + { + default: + DBG_WARNING1( "DrawingLayer: Unknown Relief style (%d)!", pTCPP->getTextRelief() ); + // fall through + case primitive2d::TEXT_RELIEF_NONE: eFontRelief = RELIEF_NONE; break; + case primitive2d::TEXT_RELIEF_EMBOSSED: eFontRelief = RELIEF_EMBOSSED; break; + case primitive2d::TEXT_RELIEF_ENGRAVED: eFontRelief = RELIEF_ENGRAVED; break; + } + + if( eFontRelief != RELIEF_NONE ) + aFont.SetRelief( eFontRelief ); + + // set Shadow attribute + if( pTCPP->getShadow() ) + aFont.SetShadow( true ); + } + + // create transformed integer DXArray in view coordinate system + ::std::vector< sal_Int32 > aTransformedDXArray; + + if(rTextCandidate.getDXArray().size()) + { + aTransformedDXArray.reserve(rTextCandidate.getDXArray().size()); + const basegfx::B2DVector aPixelVector(maCurrentTransformation * basegfx::B2DVector(1.0, 0.0)); + const double fPixelVectorFactor(aPixelVector.getLength()); + + for(::std::vector< double >::const_iterator aStart(rTextCandidate.getDXArray().begin()); + aStart != rTextCandidate.getDXArray().end(); aStart++) + { + aTransformedDXArray.push_back(basegfx::fround((*aStart) * fPixelVectorFactor)); + } + } + + // set parameters and paint text snippet + const basegfx::BColor aRGBFontColor(maBColorModifierStack.getModifiedColor(rTextCandidate.getFontColor())); + const basegfx::B2DPoint aPoint(aLocalTransform * basegfx::B2DPoint(0.0, 0.0)); + const Point aStartPoint(basegfx::fround(aPoint.getX()), basegfx::fround(aPoint.getY())); + const sal_uInt32 nOldLayoutMode(mpOutputDevice->GetLayoutMode()); + + if(rTextCandidate.getFontAttribute().getRTL()) + { + sal_uInt32 nRTLLayoutMode(nOldLayoutMode & ~(TEXT_LAYOUT_COMPLEX_DISABLED|TEXT_LAYOUT_BIDI_STRONG)); + nRTLLayoutMode |= TEXT_LAYOUT_BIDI_RTL|TEXT_LAYOUT_TEXTORIGIN_LEFT; + mpOutputDevice->SetLayoutMode(nRTLLayoutMode); + } + + mpOutputDevice->SetFont(aFont); + mpOutputDevice->SetTextColor(Color(aRGBFontColor)); + + if(aTransformedDXArray.size()) + { + mpOutputDevice->DrawTextArray( + aStartPoint, + rTextCandidate.getText(), + &(aTransformedDXArray[0]), + rTextCandidate.getTextPosition(), + rTextCandidate.getTextLength()); + } + else + { + mpOutputDevice->DrawText( + aStartPoint, + rTextCandidate.getText(), + rTextCandidate.getTextPosition(), + rTextCandidate.getTextLength()); + } + + if(rTextCandidate.getFontAttribute().getRTL()) + { + mpOutputDevice->SetLayoutMode(nOldLayoutMode); + } + + bPrimitiveAccepted = true; + } + } + + if(!bPrimitiveAccepted) + { + // let break down + process(rTextCandidate.get2DDecomposition(getViewInformation2D())); + } + } + + // direct draw of hairline + void VclProcessor2D::RenderPolygonHairlinePrimitive2D(const primitive2d::PolygonHairlinePrimitive2D& rPolygonCandidate, bool bPixelBased) + { + const basegfx::BColor aHairlineColor(maBColorModifierStack.getModifiedColor(rPolygonCandidate.getBColor())); + mpOutputDevice->SetLineColor(Color(aHairlineColor)); + mpOutputDevice->SetFillColor(); + + basegfx::B2DPolygon aLocalPolygon(rPolygonCandidate.getB2DPolygon()); + aLocalPolygon.transform(maCurrentTransformation); + + static bool bCheckTrapezoidDecomposition(false); + static bool bShowOutlinesThere(false); + if(bCheckTrapezoidDecomposition) + { + // clip against discrete ViewPort + const basegfx::B2DRange& rDiscreteViewport = getViewInformation2D().getDiscreteViewport(); + basegfx::B2DPolyPolygon aLocalPolyPolygon(basegfx::tools::clipPolygonOnRange( + aLocalPolygon, rDiscreteViewport, true, false)); + + if(aLocalPolyPolygon.count()) + { + // subdivide + aLocalPolyPolygon = basegfx::tools::adaptiveSubdivideByDistance( + aLocalPolyPolygon, 0.5); + + // trapezoidize + static double fLineWidth(2.0); + basegfx::B2DTrapezoidVector aB2DTrapezoidVector; + basegfx::tools::createLineTrapezoidFromB2DPolyPolygon(aB2DTrapezoidVector, aLocalPolyPolygon, fLineWidth); + + const sal_uInt32 nCount(aB2DTrapezoidVector.size()); + + if(nCount) + { + basegfx::BColor aInvPolygonColor(aHairlineColor); + aInvPolygonColor.invert(); + + for(sal_uInt32 a(0); a < nCount; a++) + { + const basegfx::B2DPolygon aTempPolygon(aB2DTrapezoidVector[a].getB2DPolygon()); + + if(bShowOutlinesThere) + { + mpOutputDevice->SetFillColor(Color(aHairlineColor)); + mpOutputDevice->SetLineColor(); + } + + mpOutputDevice->DrawPolygon(aTempPolygon); + + if(bShowOutlinesThere) + { + mpOutputDevice->SetFillColor(); + mpOutputDevice->SetLineColor(Color(aInvPolygonColor)); + mpOutputDevice->DrawPolyLine(aTempPolygon, 0.0); + } + } + } + } + } + else + { + if(bPixelBased && getOptionsDrawinglayer().IsAntiAliasing() && getOptionsDrawinglayer().IsSnapHorVerLinesToDiscrete()) + { + // #i98289# + // when a Hairline is painted and AntiAliasing is on the option SnapHorVerLinesToDiscrete + // allows to suppress AntiAliasing for pure horizontal or vertical lines. This is done since + // not-AntiAliased such lines look more pleasing to the eye (e.g. 2D chart content). This + // NEEDS to be done in discrete coordinates, so only useful for pixel based rendering. + aLocalPolygon = basegfx::tools::snapPointsOfHorizontalOrVerticalEdges(aLocalPolygon); + } + + mpOutputDevice->DrawPolyLine(aLocalPolygon, 0.0); + } + } + + // direct draw of transformed BitmapEx primitive + void VclProcessor2D::RenderBitmapPrimitive2D(const primitive2d::BitmapPrimitive2D& rBitmapCandidate) + { + // create local transform + basegfx::B2DHomMatrix aLocalTransform(maCurrentTransformation * rBitmapCandidate.getTransform()); + BitmapEx aBitmapEx(rBitmapCandidate.getBitmapEx()); + bool bPainted(false); + + if(maBColorModifierStack.count()) + { + aBitmapEx = impModifyBitmapEx(maBColorModifierStack, aBitmapEx); + + if(aBitmapEx.IsEmpty()) + { + // color gets completely replaced, get it + const basegfx::BColor aModifiedColor(maBColorModifierStack.getModifiedColor(basegfx::BColor())); + basegfx::B2DPolygon aPolygon(basegfx::tools::createUnitPolygon()); + aPolygon.transform(aLocalTransform); + + mpOutputDevice->SetFillColor(Color(aModifiedColor)); + mpOutputDevice->SetLineColor(); + mpOutputDevice->DrawPolygon(aPolygon); + + bPainted = true; + } + } + + if(!bPainted) + { + static bool bForceUseOfOwnTransformer(false); + static bool bUseGraphicManager(true); + + // decompose matrix to check for shear, rotate and mirroring + basegfx::B2DVector aScale, aTranslate; + double fRotate, fShearX; + aLocalTransform.decompose(aScale, aTranslate, fRotate, fShearX); + + if(!bForceUseOfOwnTransformer && basegfx::fTools::equalZero(fShearX)) + { + if(!bUseGraphicManager && basegfx::fTools::equalZero(fRotate)) + { + RenderBitmapPrimitive2D_BitmapEx(*mpOutputDevice, aBitmapEx, aLocalTransform); + } + else + { + RenderBitmapPrimitive2D_GraphicManager(*mpOutputDevice, aBitmapEx, aLocalTransform); + } + } + else + { + if(!aBitmapEx.IsTransparent() && (!basegfx::fTools::equalZero(fShearX) || !basegfx::fTools::equalZero(fRotate))) + { + // parts will be uncovered, extend aBitmapEx with a mask bitmap + const Bitmap aContent(aBitmapEx.GetBitmap()); + aBitmapEx = BitmapEx(aContent, Bitmap(aContent.GetSizePixel(), 1)); + } + + RenderBitmapPrimitive2D_self(*mpOutputDevice, aBitmapEx, aLocalTransform); + } + } + } + + void VclProcessor2D::RenderFillBitmapPrimitive2D(const primitive2d::FillBitmapPrimitive2D& rFillBitmapCandidate) + { + const attribute::FillBitmapAttribute& rFillBitmapAttribute(rFillBitmapCandidate.getFillBitmap()); + bool bPrimitiveAccepted(false); + + if(rFillBitmapAttribute.getTiling()) + { + // decompose matrix to check for shear, rotate and mirroring + basegfx::B2DHomMatrix aLocalTransform(maCurrentTransformation * rFillBitmapCandidate.getTransformation()); + basegfx::B2DVector aScale, aTranslate; + double fRotate, fShearX; + aLocalTransform.decompose(aScale, aTranslate, fRotate, fShearX); + + if(basegfx::fTools::equalZero(fRotate) && basegfx::fTools::equalZero(fShearX)) + { + // no shear or rotate, draw direct in pixel coordinates + bPrimitiveAccepted = true; + BitmapEx aBitmapEx(rFillBitmapAttribute.getBitmapEx()); + bool bPainted(false); + + if(maBColorModifierStack.count()) + { + aBitmapEx = impModifyBitmapEx(maBColorModifierStack, aBitmapEx); + + if(aBitmapEx.IsEmpty()) + { + // color gets completely replaced, get it + const basegfx::BColor aModifiedColor(maBColorModifierStack.getModifiedColor(basegfx::BColor())); + basegfx::B2DPolygon aPolygon(basegfx::tools::createUnitPolygon()); + aPolygon.transform(aLocalTransform); + + mpOutputDevice->SetFillColor(Color(aModifiedColor)); + mpOutputDevice->SetLineColor(); + mpOutputDevice->DrawPolygon(aPolygon); + + bPainted = true; + } + } + + if(!bPainted) + { + const basegfx::B2DPoint aObjTopLeft(aTranslate.getX(), aTranslate.getY()); + const basegfx::B2DPoint aObjBottomRight(aTranslate.getX() + aScale.getX(), aTranslate.getY() + aScale.getY()); + const Point aObjTL(mpOutputDevice->LogicToPixel(Point((sal_Int32)aObjTopLeft.getX(), (sal_Int32)aObjTopLeft.getY()))); + const Point aObjBR(mpOutputDevice->LogicToPixel(Point((sal_Int32)aObjBottomRight.getX(), (sal_Int32)aObjBottomRight.getY()))); + + const basegfx::B2DPoint aBmpTopLeft(aLocalTransform * rFillBitmapAttribute.getTopLeft()); + const basegfx::B2DPoint aBmpBottomRight(aLocalTransform * basegfx::B2DPoint(rFillBitmapAttribute.getTopLeft() + rFillBitmapAttribute.getSize())); + const Point aBmpTL(mpOutputDevice->LogicToPixel(Point((sal_Int32)aBmpTopLeft.getX(), (sal_Int32)aBmpTopLeft.getY()))); + const Point aBmpBR(mpOutputDevice->LogicToPixel(Point((sal_Int32)aBmpBottomRight.getX(), (sal_Int32)aBmpBottomRight.getY()))); + + sal_Int32 nOWidth(aObjBR.X() - aObjTL.X()); + sal_Int32 nOHeight(aObjBR.Y() - aObjTL.Y()); + + // only do something when object has a size in discrete units + if(nOWidth > 0 && nOHeight > 0) + { + sal_Int32 nBWidth(aBmpBR.X() - aBmpTL.X()); + sal_Int32 nBHeight(aBmpBR.Y() - aBmpTL.Y()); + + // only do something when bitmap fill has a size in discrete units + if(nBWidth > 0 && nBHeight > 0) + { + sal_Int32 nBLeft(aBmpTL.X()); + sal_Int32 nBTop(aBmpTL.Y()); + + if(nBLeft > aObjTL.X()) + { + nBLeft -= ((nBLeft / nBWidth) + 1L) * nBWidth; + } + + if(nBLeft + nBWidth <= aObjTL.X()) + { + nBLeft -= (nBLeft / nBWidth) * nBWidth; + } + + if(nBTop > aObjTL.Y()) + { + nBTop -= ((nBTop / nBHeight) + 1L) * nBHeight; + } + + if(nBTop + nBHeight <= aObjTL.Y()) + { + nBTop -= (nBTop / nBHeight) * nBHeight; + } + + // nBWidth, nBHeight is the pixel size of the neede bitmap. To not need to scale it + // in vcl many times, create a size-optimized version + const Size aNeededBitmapSizePixel(nBWidth, nBHeight); + + if(aNeededBitmapSizePixel != aBitmapEx.GetSizePixel()) + { + aBitmapEx.Scale(aNeededBitmapSizePixel); + } + + // prepare OutDev + const Point aEmptyPoint(0, 0); + const Rectangle aVisiblePixel(aEmptyPoint, mpOutputDevice->GetOutputSizePixel()); + const bool bWasEnabled(mpOutputDevice->IsMapModeEnabled()); + mpOutputDevice->EnableMapMode(false); + + for(sal_Int32 nXPos(nBLeft); nXPos < aObjTL.X() + nOWidth; nXPos += nBWidth) + { + for(sal_Int32 nYPos(nBTop); nYPos < aObjTL.Y() + nOHeight; nYPos += nBHeight) + { + const Rectangle aOutRectPixel(Point(nXPos, nYPos), aNeededBitmapSizePixel); + + if(aOutRectPixel.IsOver(aVisiblePixel)) + { + mpOutputDevice->DrawBitmapEx(aOutRectPixel.TopLeft(), aBitmapEx); + } + } + } + + // restore OutDev + mpOutputDevice->EnableMapMode(bWasEnabled); + } + } + } + } + } + + if(!bPrimitiveAccepted) + { + // do not accept, use decomposition + process(rFillBitmapCandidate.get2DDecomposition(getViewInformation2D())); + } + } + + // direct draw of gradient + void VclProcessor2D::RenderPolyPolygonGradientPrimitive2D(const primitive2d::PolyPolygonGradientPrimitive2D& rPolygonCandidate) + { + const attribute::FillGradientAttribute& rGradient(rPolygonCandidate.getFillGradient()); + basegfx::BColor aStartColor(maBColorModifierStack.getModifiedColor(rGradient.getStartColor())); + basegfx::BColor aEndColor(maBColorModifierStack.getModifiedColor(rGradient.getEndColor())); + basegfx::B2DPolyPolygon aLocalPolyPolygon(rPolygonCandidate.getB2DPolyPolygon()); + + if(aLocalPolyPolygon.count()) + { + aLocalPolyPolygon.transform(maCurrentTransformation); + + if(aStartColor == aEndColor) + { + // no gradient at all, draw as polygon in AA and non-AA case + mpOutputDevice->SetLineColor(); + mpOutputDevice->SetFillColor(Color(aStartColor)); + mpOutputDevice->DrawPolyPolygon(aLocalPolyPolygon); + } + else if(getOptionsDrawinglayer().IsAntiAliasing()) + { + // For AA, direct render has to be avoided since it uses XOR maskings which will not + // work with AA. Instead, the decompose which uses MaskPrimitive2D with fillings is + // used + process(rPolygonCandidate.get2DDecomposition(getViewInformation2D())); + } + else + { + impDrawGradientToOutDev( + *mpOutputDevice, aLocalPolyPolygon, rGradient.getStyle(), rGradient.getSteps(), + aStartColor, aEndColor, rGradient.getBorder(), + rGradient.getAngle(), rGradient.getOffsetX(), rGradient.getOffsetY(), false); + } + } + } + + // direct draw of bitmap + void VclProcessor2D::RenderPolyPolygonBitmapPrimitive2D(const primitive2d::PolyPolygonBitmapPrimitive2D& rPolygonCandidate) + { + bool bDone(false); + const basegfx::B2DPolyPolygon& rPolyPolygon = rPolygonCandidate.getB2DPolyPolygon(); + + if(rPolyPolygon.count()) + { + const attribute::FillBitmapAttribute& rFillBitmapAttribute = rPolygonCandidate.getFillBitmap(); + const BitmapEx& rBitmapEx = rFillBitmapAttribute.getBitmapEx(); + + if(rBitmapEx.IsEmpty()) + { + // empty bitmap, done + bDone = true; + } + else + { + // try to catch cases where the bitmap will be color-modified to a single + // color (e.g. shadow). This would NOT be optimizable with an transparence channel + // at the Bitmap which we do not have here. When this should change, this + // optimization has to be reworked accordingly. + const sal_uInt32 nBColorModifierStackCount(maBColorModifierStack.count()); + + if(nBColorModifierStackCount) + { + const basegfx::BColorModifier& rTopmostModifier = maBColorModifierStack.getBColorModifier(nBColorModifierStackCount - 1); + + if(basegfx::BCOLORMODIFYMODE_REPLACE == rTopmostModifier.getMode()) + { + // the bitmap fill is in unified color, so we can replace it with + // a single polygon fill. The form of the fill depends on tiling + if(rFillBitmapAttribute.getTiling()) + { + // with tiling, fill the whole PolyPolygon with the modifier color + basegfx::B2DPolyPolygon aLocalPolyPolygon(rPolyPolygon); + + aLocalPolyPolygon.transform(maCurrentTransformation); + mpOutputDevice->SetLineColor(); + mpOutputDevice->SetFillColor(Color(rTopmostModifier.getBColor())); + mpOutputDevice->DrawPolyPolygon(aLocalPolyPolygon); + } + else + { + // without tiling, only the area common to the bitmap tile and the + // PolyPolygon is filled. Create the bitmap tile area in object + // coordinates. For this, the object transformation needs to be created + // from the already scaled PolyPolygon. The tile area in object + // coordinates wil always be non-rotated, so it's not necessary to + // work with a polygon here + basegfx::B2DRange aTileRange(rFillBitmapAttribute.getTopLeft(), + rFillBitmapAttribute.getTopLeft() + rFillBitmapAttribute.getSize()); + const basegfx::B2DRange aPolyPolygonRange(rPolyPolygon.getB2DRange()); + basegfx::B2DHomMatrix aNewObjectTransform; + + aNewObjectTransform.set(0, 0, aPolyPolygonRange.getWidth()); + aNewObjectTransform.set(1, 1, aPolyPolygonRange.getHeight()); + aNewObjectTransform.set(0, 2, aPolyPolygonRange.getMinX()); + aNewObjectTransform.set(1, 2, aPolyPolygonRange.getMinY()); + aTileRange.transform(aNewObjectTransform); + + // now clip the object polyPolygon against the tile range + // to get the common area (OR) + basegfx::B2DPolyPolygon aTarget = basegfx::tools::clipPolyPolygonOnRange(rPolyPolygon, aTileRange, true, false); + + if(aTarget.count()) + { + aTarget.transform(maCurrentTransformation); + mpOutputDevice->SetLineColor(); + mpOutputDevice->SetFillColor(Color(rTopmostModifier.getBColor())); + mpOutputDevice->DrawPolyPolygon(aTarget); + } + } + + bDone = true; + } + } + } + } + else + { + // empty polyPolygon, done + bDone = true; + } + + if(!bDone) + { + // use default decomposition + process(rPolygonCandidate.get2DDecomposition(getViewInformation2D())); + } + } + + // direct draw of PolyPolygon with color + void VclProcessor2D::RenderPolyPolygonColorPrimitive2D(const primitive2d::PolyPolygonColorPrimitive2D& rPolygonCandidate) + { + const basegfx::BColor aPolygonColor(maBColorModifierStack.getModifiedColor(rPolygonCandidate.getBColor())); + mpOutputDevice->SetFillColor(Color(aPolygonColor)); + mpOutputDevice->SetLineColor(); + + basegfx::B2DPolyPolygon aLocalPolyPolygon(rPolygonCandidate.getB2DPolyPolygon()); + aLocalPolyPolygon.transform(maCurrentTransformation); + + static bool bCheckTrapezoidDecomposition(false); + static bool bShowOutlinesThere(false); + if(bCheckTrapezoidDecomposition) + { + // clip against discrete ViewPort + const basegfx::B2DRange& rDiscreteViewport = getViewInformation2D().getDiscreteViewport(); + aLocalPolyPolygon = basegfx::tools::clipPolyPolygonOnRange( + aLocalPolyPolygon, rDiscreteViewport, true, false); + + if(aLocalPolyPolygon.count()) + { + // subdivide + aLocalPolyPolygon = basegfx::tools::adaptiveSubdivideByDistance( + aLocalPolyPolygon, 0.5); + + // trapezoidize + basegfx::B2DTrapezoidVector aB2DTrapezoidVector; + basegfx::tools::trapezoidSubdivide(aB2DTrapezoidVector, aLocalPolyPolygon); + + const sal_uInt32 nCount(aB2DTrapezoidVector.size()); + + if(nCount) + { + basegfx::BColor aInvPolygonColor(aPolygonColor); + aInvPolygonColor.invert(); + + for(sal_uInt32 a(0); a < nCount; a++) + { + const basegfx::B2DPolygon aTempPolygon(aB2DTrapezoidVector[a].getB2DPolygon()); + + if(bShowOutlinesThere) + { + mpOutputDevice->SetFillColor(Color(aPolygonColor)); + mpOutputDevice->SetLineColor(); + } + + mpOutputDevice->DrawPolygon(aTempPolygon); + + if(bShowOutlinesThere) + { + mpOutputDevice->SetFillColor(); + mpOutputDevice->SetLineColor(Color(aInvPolygonColor)); + mpOutputDevice->DrawPolyLine(aTempPolygon, 0.0); + } + } + } + } + } + else + { + mpOutputDevice->DrawPolyPolygon(aLocalPolyPolygon); + + if(mnPolygonStrokePrimitive2D + && getOptionsDrawinglayer().IsAntiAliasing() + && (mpOutputDevice->GetAntialiasing() & ANTIALIASING_ENABLE_B2DDRAW)) + { + // when AA is on and this filled polygons are the result of stroked line geometry, + // draw the geometry once extra as lines to avoid AA 'gaps' between partial polygons + mpOutputDevice->SetFillColor(); + mpOutputDevice->SetLineColor(Color(aPolygonColor)); + const sal_uInt32 nCount(aLocalPolyPolygon.count()); + + for(sal_uInt32 a(0); a < nCount; a++) + { + mpOutputDevice->DrawPolyLine(aLocalPolyPolygon.getB2DPolygon(a), 0.0); + } + } + } + } + + // direct draw of MetaFile + void VclProcessor2D::RenderMetafilePrimitive2D(const primitive2d::MetafilePrimitive2D& rMetaCandidate) + { + // decompose matrix to check for shear, rotate and mirroring + basegfx::B2DHomMatrix aLocalTransform(maCurrentTransformation * rMetaCandidate.getTransform()); + basegfx::B2DVector aScale, aTranslate; + double fRotate, fShearX; + aLocalTransform.decompose(aScale, aTranslate, fRotate, fShearX); + + if(basegfx::fTools::less(aScale.getX(), 0.0) && basegfx::fTools::less(aScale.getY(), 0.0)) + { + // #i102175# handle special case: If scale is negative in (x,y) (3rd quadrant), it can + // be expressed as rotation by PI. This needs to be done for Metafiles since + // these can be rotated, but not really mirrored + aScale = basegfx::absolute(aScale); + fRotate += F_PI; + } + + // get BoundRect + basegfx::B2DRange aOutlineRange(rMetaCandidate.getB2DRange(getViewInformation2D())); + aOutlineRange.transform(maCurrentTransformation); + + // Due to the integer MapModes used from VCL aind inside MetaFiles errors of up to three + // pixels in size may happen. As long as there is no better way (e.g. convert the MetaFile + // to primitives) it is necessary to reduce maximum pixel size by 1 in X and Y and to use + // the inner pixel bounds accordingly (ceil resp. floor). This will also be done for logic + // units e.g. when creating a new MetaFile, but since much huger value ranges are used + // there typically will be okay for this compromize. + Rectangle aDestRectView( + // !!CAUTION!! Here, ceil and floor are exchanged BY PURPOSE, do NOT copy when + // looking for a standard conversion to rectangle (!) + (sal_Int32)ceil(aOutlineRange.getMinX()), (sal_Int32)ceil(aOutlineRange.getMinY()), + (sal_Int32)floor(aOutlineRange.getMaxX()), (sal_Int32)floor(aOutlineRange.getMaxY())); + + // get metafile (copy it) + GDIMetaFile aMetaFile; + + if(maBColorModifierStack.count()) + { + const basegfx::BColor aRGBBaseColor(0, 0, 0); + const basegfx::BColor aRGBColor(maBColorModifierStack.getModifiedColor(aRGBBaseColor)); + aMetaFile = rMetaCandidate.getMetaFile().GetMonochromeMtf(Color(aRGBColor)); + } + else + { + aMetaFile = rMetaCandidate.getMetaFile(); + } + + // rotation + if(!basegfx::fTools::equalZero(fRotate)) + { + // #i103530# + // MetaFile::Rotate has no input parameter check, so the parameter needs to be + // well-aligned to the old range [0..3600] 10th degrees with inverse orientation + sal_Int16 nRotation((sal_Int16)((fRotate / F_PI180) * -10.0)); + + while(nRotation < 0) + nRotation += 3600; + + while(nRotation >= 3600) + nRotation -= 3600; + + aMetaFile.Rotate(nRotation); + } + + // Prepare target output size + Size aDestSize(aDestRectView.GetSize()); + + if(aDestSize.getWidth() && aDestSize.getHeight()) + { + // Get preferred Metafile output size. When it's very equal to the output size, it's probably + // a rounding error somewhere, so correct it to get a 1:1 output without single pixel scalings + // of the Metafile (esp. for contaned Bitmaps, e.g 3D charts) + const Size aPrefSize(mpOutputDevice->LogicToPixel(aMetaFile.GetPrefSize(), aMetaFile.GetPrefMapMode())); + + if(aPrefSize.getWidth() && (aPrefSize.getWidth() - 1 == aDestSize.getWidth() || aPrefSize.getWidth() + 1 == aDestSize.getWidth())) + { + aDestSize.setWidth(aPrefSize.getWidth()); + } + + if(aPrefSize.getHeight() && (aPrefSize.getHeight() - 1 == aDestSize.getHeight() || aPrefSize.getHeight() + 1 == aDestSize.getHeight())) + { + aDestSize.setHeight(aPrefSize.getHeight()); + } + + // paint it + aMetaFile.WindStart(); + aMetaFile.Play(mpOutputDevice, aDestRectView.TopLeft(), aDestSize); + } + } + + // mask group. Force output to VDev and create mask from given mask + void VclProcessor2D::RenderMaskPrimitive2DPixel(const primitive2d::MaskPrimitive2D& rMaskCandidate) + { + if(rMaskCandidate.getChildren().hasElements()) + { + basegfx::B2DPolyPolygon aMask(rMaskCandidate.getMask()); + + if(aMask.count()) + { + aMask.transform(maCurrentTransformation); + const basegfx::B2DRange aRange(basegfx::tools::getRange(aMask)); + impBufferDevice aBufferDevice(*mpOutputDevice, aRange, true); + + if(aBufferDevice.isVisible()) + { + // remember last OutDev and set to content + OutputDevice* pLastOutputDevice = mpOutputDevice; + mpOutputDevice = &aBufferDevice.getContent(); + + // paint to it + process(rMaskCandidate.getChildren()); + + // back to old OutDev + mpOutputDevice = pLastOutputDevice; + + // draw mask + if(getOptionsDrawinglayer().IsAntiAliasing()) + { + // with AA, use 8bit AlphaMask to get nice borders + VirtualDevice& rTransparence = aBufferDevice.getTransparence(); + rTransparence.SetLineColor(); + rTransparence.SetFillColor(COL_BLACK); + rTransparence.DrawPolyPolygon(aMask); + + // dump buffer to outdev + aBufferDevice.paint(); + } + else + { + // No AA, use 1bit mask + VirtualDevice& rMask = aBufferDevice.getMask(); + rMask.SetLineColor(); + rMask.SetFillColor(COL_BLACK); + rMask.DrawPolyPolygon(aMask); + + // dump buffer to outdev + aBufferDevice.paint(); + } + } + } + } + } + + // modified color group. Force output to unified color. + void VclProcessor2D::RenderModifiedColorPrimitive2D(const primitive2d::ModifiedColorPrimitive2D& rModifiedCandidate) + { + if(rModifiedCandidate.getChildren().hasElements()) + { + maBColorModifierStack.push(rModifiedCandidate.getColorModifier()); + process(rModifiedCandidate.getChildren()); + maBColorModifierStack.pop(); + } + } + + // unified sub-transparence. Draw to VDev first. + void VclProcessor2D::RenderUnifiedTransparencePrimitive2D(const primitive2d::UnifiedTransparencePrimitive2D& rTransCandidate) + { + static bool bForceToDecomposition(false); + + if(rTransCandidate.getChildren().hasElements()) + { + if(bForceToDecomposition) + { + // use decomposition + process(rTransCandidate.get2DDecomposition(getViewInformation2D())); + } + else + { + if(0.0 == rTransCandidate.getTransparence()) + { + // no transparence used, so just use the content + process(rTransCandidate.getChildren()); + } + else if(rTransCandidate.getTransparence() > 0.0 && rTransCandidate.getTransparence() < 1.0) + { + // transparence is in visible range + basegfx::B2DRange aRange(primitive2d::getB2DRangeFromPrimitive2DSequence(rTransCandidate.getChildren(), getViewInformation2D())); + aRange.transform(maCurrentTransformation); + impBufferDevice aBufferDevice(*mpOutputDevice, aRange, true); + + if(aBufferDevice.isVisible()) + { + // remember last OutDev and set to content + OutputDevice* pLastOutputDevice = mpOutputDevice; + mpOutputDevice = &aBufferDevice.getContent(); + + // paint content to it + process(rTransCandidate.getChildren()); + + // back to old OutDev + mpOutputDevice = pLastOutputDevice; + + // dump buffer to outdev using given transparence + aBufferDevice.paint(rTransCandidate.getTransparence()); + } + } + } + } + } + + // sub-transparence group. Draw to VDev first. + void VclProcessor2D::RenderTransparencePrimitive2D(const primitive2d::TransparencePrimitive2D& rTransCandidate) + { + if(rTransCandidate.getChildren().hasElements()) + { + basegfx::B2DRange aRange(primitive2d::getB2DRangeFromPrimitive2DSequence(rTransCandidate.getChildren(), getViewInformation2D())); + aRange.transform(maCurrentTransformation); + impBufferDevice aBufferDevice(*mpOutputDevice, aRange, true); + + if(aBufferDevice.isVisible()) + { + // remember last OutDev and set to content + OutputDevice* pLastOutputDevice = mpOutputDevice; + mpOutputDevice = &aBufferDevice.getContent(); + + // paint content to it + process(rTransCandidate.getChildren()); + + // set to mask + mpOutputDevice = &aBufferDevice.getTransparence(); + + // when painting transparence masks, reset the color stack + basegfx::BColorModifierStack aLastBColorModifierStack(maBColorModifierStack); + maBColorModifierStack = basegfx::BColorModifierStack(); + + // paint mask to it (always with transparence intensities, evtl. with AA) + process(rTransCandidate.getTransparence()); + + // back to old color stack + maBColorModifierStack = aLastBColorModifierStack; + + // back to old OutDev + mpOutputDevice = pLastOutputDevice; + + // dump buffer to outdev + aBufferDevice.paint(); + } + } + } + + // transform group. + void VclProcessor2D::RenderTransformPrimitive2D(const primitive2d::TransformPrimitive2D& rTransformCandidate) + { + // remember current transformation and ViewInformation + const basegfx::B2DHomMatrix aLastCurrentTransformation(maCurrentTransformation); + const geometry::ViewInformation2D aLastViewInformation2D(getViewInformation2D()); + + // create new transformations for CurrentTransformation + // and for local ViewInformation2D + maCurrentTransformation = maCurrentTransformation * rTransformCandidate.getTransformation(); + const geometry::ViewInformation2D aViewInformation2D( + getViewInformation2D().getObjectTransformation() * rTransformCandidate.getTransformation(), + getViewInformation2D().getViewTransformation(), + getViewInformation2D().getViewport(), + getViewInformation2D().getVisualizedPage(), + getViewInformation2D().getViewTime(), + getViewInformation2D().getExtendedInformationSequence()); + updateViewInformation(aViewInformation2D); + + // proccess content + process(rTransformCandidate.getChildren()); + + // restore transformations + maCurrentTransformation = aLastCurrentTransformation; + updateViewInformation(aLastViewInformation2D); + } + + // new XDrawPage for ViewInformation2D + void VclProcessor2D::RenderPagePreviewPrimitive2D(const primitive2d::PagePreviewPrimitive2D& rPagePreviewCandidate) + { + // remember current transformation and ViewInformation + const geometry::ViewInformation2D aLastViewInformation2D(getViewInformation2D()); + + // create new local ViewInformation2D + const geometry::ViewInformation2D aViewInformation2D( + getViewInformation2D().getObjectTransformation(), + getViewInformation2D().getViewTransformation(), + getViewInformation2D().getViewport(), + rPagePreviewCandidate.getXDrawPage(), + getViewInformation2D().getViewTime(), + getViewInformation2D().getExtendedInformationSequence()); + updateViewInformation(aViewInformation2D); + + // proccess decomposed content + process(rPagePreviewCandidate.get2DDecomposition(getViewInformation2D())); + + // restore transformations + updateViewInformation(aLastViewInformation2D); + } + + // marker + void VclProcessor2D::RenderMarkerArrayPrimitive2D(const primitive2d::MarkerArrayPrimitive2D& rMarkArrayCandidate) + { + static bool bCheckCompleteMarkerDecompose(false); + if(bCheckCompleteMarkerDecompose) + { + process(rMarkArrayCandidate.get2DDecomposition(getViewInformation2D())); + return; + } + + // get data + const std::vector< basegfx::B2DPoint >& rPositions = rMarkArrayCandidate.getPositions(); + const sal_uInt32 nCount(rPositions.size()); + + if(nCount && !rMarkArrayCandidate.getMarker().IsEmpty()) + { + // get pixel size + const BitmapEx& rMarker(rMarkArrayCandidate.getMarker()); + const Size aBitmapSize(rMarker.GetSizePixel()); + + if(aBitmapSize.Width() && aBitmapSize.Height()) + { + // get discrete half size + const basegfx::B2DVector aDiscreteHalfSize( + (aBitmapSize.getWidth() - 1.0) * 0.5, + (aBitmapSize.getHeight() - 1.0) * 0.5); + const bool bWasEnabled(mpOutputDevice->IsMapModeEnabled()); + + // do not forget evtl. moved origin in target device MapMode when + // switching it off; it would be missing and lead to wrong positions. + // All his could be done using logic sizes and coordinates, too, but + // we want a 1:1 bitmap rendering here, so it's more safe and faster + // to work with switching off MapMode usage completely. + const Point aOrigin(mpOutputDevice->GetMapMode().GetOrigin()); + + mpOutputDevice->EnableMapMode(false); + + for(std::vector< basegfx::B2DPoint >::const_iterator aIter(rPositions.begin()); aIter != rPositions.end(); aIter++) + { + const basegfx::B2DPoint aDiscreteTopLeft((maCurrentTransformation * (*aIter)) - aDiscreteHalfSize); + const Point aDiscretePoint(basegfx::fround(aDiscreteTopLeft.getX()), basegfx::fround(aDiscreteTopLeft.getY())); + + mpOutputDevice->DrawBitmapEx(aDiscretePoint + aOrigin, rMarker); + } + + mpOutputDevice->EnableMapMode(bWasEnabled); + } + } + } + + // point + void VclProcessor2D::RenderPointArrayPrimitive2D(const primitive2d::PointArrayPrimitive2D& rPointArrayCandidate) + { + const std::vector< basegfx::B2DPoint >& rPositions = rPointArrayCandidate.getPositions(); + const basegfx::BColor aRGBColor(maBColorModifierStack.getModifiedColor(rPointArrayCandidate.getRGBColor())); + const Color aVCLColor(aRGBColor); + + for(std::vector< basegfx::B2DPoint >::const_iterator aIter(rPositions.begin()); aIter != rPositions.end(); aIter++) + { + const basegfx::B2DPoint aViewPosition(maCurrentTransformation * (*aIter)); + const Point aPos(basegfx::fround(aViewPosition.getX()), basegfx::fround(aViewPosition.getY())); + + mpOutputDevice->DrawPixel(aPos, aVCLColor); + } + } + + void VclProcessor2D::RenderPolygonStrokePrimitive2D(const primitive2d::PolygonStrokePrimitive2D& rPolygonStrokeCandidate) + { + // #i101491# method restructured to clearly use the DrawPolyLine + // calls starting from a deined line width + const attribute::LineAttribute& rLineAttribute = rPolygonStrokeCandidate.getLineAttribute(); + const double fLineWidth(rLineAttribute.getWidth()); + bool bDone(false); + + if(basegfx::fTools::more(fLineWidth, 0.0)) + { + const basegfx::B2DVector aDiscreteUnit(maCurrentTransformation * basegfx::B2DVector(fLineWidth, 0.0)); + const double fDiscreteLineWidth(aDiscreteUnit.getLength()); + const attribute::StrokeAttribute& rStrokeAttribute = rPolygonStrokeCandidate.getStrokeAttribute(); + const basegfx::BColor aHairlineColor(maBColorModifierStack.getModifiedColor(rLineAttribute.getColor())); + basegfx::B2DPolyPolygon aHairlinePolyPolygon; + + mpOutputDevice->SetLineColor(Color(aHairlineColor)); + mpOutputDevice->SetFillColor(); + + if(0.0 == rStrokeAttribute.getFullDotDashLen()) + { + // no line dashing, just copy + aHairlinePolyPolygon.append(rPolygonStrokeCandidate.getB2DPolygon()); + } + else + { + // else apply LineStyle + basegfx::tools::applyLineDashing(rPolygonStrokeCandidate.getB2DPolygon(), + rStrokeAttribute.getDotDashArray(), + &aHairlinePolyPolygon, 0, rStrokeAttribute.getFullDotDashLen()); + } + + const sal_uInt32 nCount(aHairlinePolyPolygon.count()); + + if(nCount) + { + const bool bAntiAliased(getOptionsDrawinglayer().IsAntiAliasing()); + aHairlinePolyPolygon.transform(maCurrentTransformation); + + for(sal_uInt32 a(0); a < nCount; a++) + { + basegfx::B2DPolygon aCandidate(aHairlinePolyPolygon.getB2DPolygon(a)); + + if(bAntiAliased) + { + if(basegfx::fTools::lessOrEqual(fDiscreteLineWidth, 1.0)) + { + // line in range ]0.0 .. 1.0[ + // paint as simple hairline + mpOutputDevice->DrawPolyLine(aCandidate, 0.0); + bDone = true; + } + else if(basegfx::fTools::lessOrEqual(fDiscreteLineWidth, 2.0)) + { + // line in range [1.0 .. 2.0[ + // paint as 2x2 with dynamic line distance + basegfx::B2DHomMatrix aMat; + const double fDistance(fDiscreteLineWidth - 1.0); + const double fHalfDistance(fDistance * 0.5); + + aMat.set(0, 2, -fHalfDistance); + aMat.set(1, 2, -fHalfDistance); + aCandidate.transform(aMat); + mpOutputDevice->DrawPolyLine(aCandidate, 0.0); + + aMat.set(0, 2, fDistance); + aMat.set(1, 2, 0.0); + aCandidate.transform(aMat); + mpOutputDevice->DrawPolyLine(aCandidate, 0.0); + + aMat.set(0, 2, 0.0); + aMat.set(1, 2, fDistance); + aCandidate.transform(aMat); + mpOutputDevice->DrawPolyLine(aCandidate, 0.0); + + aMat.set(0, 2, -fDistance); + aMat.set(1, 2, 0.0); + aCandidate.transform(aMat); + mpOutputDevice->DrawPolyLine(aCandidate, 0.0); + bDone = true; + } + else if(basegfx::fTools::lessOrEqual(fDiscreteLineWidth, 3.0)) + { + // line in range [2.0 .. 3.0] + // paint as cross in a 3x3 with dynamic line distance + basegfx::B2DHomMatrix aMat; + const double fDistance((fDiscreteLineWidth - 1.0) * 0.5); + + mpOutputDevice->DrawPolyLine(aCandidate, 0.0); + + aMat.set(0, 2, -fDistance); + aMat.set(1, 2, 0.0); + aCandidate.transform(aMat); + mpOutputDevice->DrawPolyLine(aCandidate, 0.0); + + aMat.set(0, 2, fDistance); + aMat.set(1, 2, -fDistance); + aCandidate.transform(aMat); + mpOutputDevice->DrawPolyLine(aCandidate, 0.0); + + aMat.set(0, 2, fDistance); + aMat.set(1, 2, fDistance); + aCandidate.transform(aMat); + mpOutputDevice->DrawPolyLine(aCandidate, 0.0); + + aMat.set(0, 2, -fDistance); + aMat.set(1, 2, fDistance); + aCandidate.transform(aMat); + mpOutputDevice->DrawPolyLine(aCandidate, 0.0); + bDone = true; + } + else + { + // #i101491# line width above 3.0 + } + } + else + { + if(basegfx::fTools::lessOrEqual(fDiscreteLineWidth, 1.5)) + { + // line width below 1.5, draw the basic hairline polygon + mpOutputDevice->DrawPolyLine(aCandidate, 0.0); + bDone = true; + } + else if(basegfx::fTools::lessOrEqual(fDiscreteLineWidth, 2.5)) + { + // line width is in range ]1.5 .. 2.5], use four hairlines + // drawn in a square + basegfx::B2DHomMatrix aMat; + mpOutputDevice->DrawPolyLine(aCandidate, 0.0); + + aMat.set(0, 2, 1.0); + aMat.set(1, 2, 0.0); + aCandidate.transform(aMat); + + mpOutputDevice->DrawPolyLine(aCandidate, 0.0); + + aMat.set(0, 2, 0.0); + aMat.set(1, 2, 1.0); + aCandidate.transform(aMat); + + mpOutputDevice->DrawPolyLine(aCandidate, 0.0); + + aMat.set(0, 2, -1.0); + aMat.set(1, 2, 0.0); + aCandidate.transform(aMat); + + mpOutputDevice->DrawPolyLine(aCandidate, 0.0); + bDone = true; + } + else + { + // #i101491# line width is above 2.5 + } + } + + if(!bDone && rPolygonStrokeCandidate.getB2DPolygon().count() > 1000) + { + // #i101491# If the polygon complexity uses more than a given amount, do + // use OuputDevice::DrawPolyLine directly; this will avoid buffering all + // decompositions in primtives (memory) and fallback to old line painting + // for very complex polygons, too + mpOutputDevice->DrawPolyLine(aCandidate, fDiscreteLineWidth, rLineAttribute.getLineJoin()); + bDone = true; + } + } + } + } + + if(!bDone) + { + // remeber that we enter a PolygonStrokePrimitive2D decomposition, + // used for AA thick line drawing + mnPolygonStrokePrimitive2D++; + + // line width is big enough for standard filled polygon visualisation or zero + process(rPolygonStrokeCandidate.get2DDecomposition(getViewInformation2D())); + + // leave PolygonStrokePrimitive2D + mnPolygonStrokePrimitive2D--; + } + } + + void VclProcessor2D::RenderEpsPrimitive2D(const primitive2d::EpsPrimitive2D& rEpsPrimitive2D) + { + // The new decomposition of Metafiles made it necessary to add an Eps + // primitive to handle embedded Eps data. On some devices, this can be + // painted directly (mac, printer). + // To be able to handle the replacement correctly, i need to handle it myself + // since DrawEPS will not be able e.g. to rotate the replacement. To be able + // to do that, i added a boolean return to OutputDevice::DrawEPS(..) + // to know when EPS was handled directly already. + basegfx::B2DRange aRange(0.0, 0.0, 1.0, 1.0); + aRange.transform(maCurrentTransformation * rEpsPrimitive2D.getEpsTransform()); + + if(!aRange.isEmpty()) + { + const Rectangle aRectangle( + (sal_Int32)floor(aRange.getMinX()), (sal_Int32)floor(aRange.getMinY()), + (sal_Int32)ceil(aRange.getMaxX()), (sal_Int32)ceil(aRange.getMaxY())); + + if(!aRectangle.IsEmpty()) + { + // try to paint EPS directly without fallback visualisation + const bool bEPSPaintedDirectly(mpOutputDevice->DrawEPS( + aRectangle.TopLeft(), + aRectangle.GetSize(), + rEpsPrimitive2D.getGfxLink(), + 0)); + + if(!bEPSPaintedDirectly) + { + // use the decomposition which will correctly handle the + // fallback visualisation using full transformation (e.g. rotation) + process(rEpsPrimitive2D.get2DDecomposition(getViewInformation2D())); + } + } + } + } + + void VclProcessor2D::adaptLineToFillDrawMode() const + { + const sal_uInt32 nOriginalDrawMode(mpOutputDevice->GetDrawMode()); + + if(nOriginalDrawMode & (DRAWMODE_BLACKLINE|DRAWMODE_GRAYLINE|DRAWMODE_GHOSTEDLINE|DRAWMODE_WHITELINE|DRAWMODE_SETTINGSLINE)) + { + sal_uInt32 nAdaptedDrawMode(nOriginalDrawMode); + + if(nOriginalDrawMode & DRAWMODE_BLACKLINE) + { + nAdaptedDrawMode |= DRAWMODE_BLACKFILL; + } + else + { + nAdaptedDrawMode &= ~DRAWMODE_BLACKFILL; + } + + if(nOriginalDrawMode & DRAWMODE_GRAYLINE) + { + nAdaptedDrawMode |= DRAWMODE_GRAYFILL; + } + else + { + nAdaptedDrawMode &= ~DRAWMODE_GRAYFILL; + } + + if(nOriginalDrawMode & DRAWMODE_GHOSTEDLINE) + { + nAdaptedDrawMode |= DRAWMODE_GHOSTEDFILL; + } + else + { + nAdaptedDrawMode &= ~DRAWMODE_GHOSTEDFILL; + } + + if(nOriginalDrawMode & DRAWMODE_WHITELINE) + { + nAdaptedDrawMode |= DRAWMODE_WHITEFILL; + } + else + { + nAdaptedDrawMode &= ~DRAWMODE_WHITEFILL; + } + + if(nOriginalDrawMode & DRAWMODE_SETTINGSLINE) + { + nAdaptedDrawMode |= DRAWMODE_SETTINGSFILL; + } + else + { + nAdaptedDrawMode &= ~DRAWMODE_SETTINGSFILL; + } + + mpOutputDevice->SetDrawMode(nAdaptedDrawMode); + } + } + + void VclProcessor2D::adaptTextToFillDrawMode() const + { + const sal_uInt32 nOriginalDrawMode(mpOutputDevice->GetDrawMode()); + if(nOriginalDrawMode & (DRAWMODE_BLACKTEXT|DRAWMODE_GRAYTEXT|DRAWMODE_GHOSTEDTEXT|DRAWMODE_WHITETEXT|DRAWMODE_SETTINGSTEXT)) + { + sal_uInt32 nAdaptedDrawMode(nOriginalDrawMode); + + if(nOriginalDrawMode & DRAWMODE_BLACKTEXT) + { + nAdaptedDrawMode |= DRAWMODE_BLACKFILL; + } + else + { + nAdaptedDrawMode &= ~DRAWMODE_BLACKFILL; + } + + if(nOriginalDrawMode & DRAWMODE_GRAYTEXT) + { + nAdaptedDrawMode |= DRAWMODE_GRAYFILL; + } + else + { + nAdaptedDrawMode &= ~DRAWMODE_GRAYFILL; + } + + if(nOriginalDrawMode & DRAWMODE_GHOSTEDTEXT) + { + nAdaptedDrawMode |= DRAWMODE_GHOSTEDFILL; + } + else + { + nAdaptedDrawMode &= ~DRAWMODE_GHOSTEDFILL; + } + + if(nOriginalDrawMode & DRAWMODE_WHITETEXT) + { + nAdaptedDrawMode |= DRAWMODE_WHITEFILL; + } + else + { + nAdaptedDrawMode &= ~DRAWMODE_WHITEFILL; + } + + if(nOriginalDrawMode & DRAWMODE_SETTINGSTEXT) + { + nAdaptedDrawMode |= DRAWMODE_SETTINGSFILL; + } + else + { + nAdaptedDrawMode &= ~DRAWMODE_SETTINGSFILL; + } + + mpOutputDevice->SetDrawMode(nAdaptedDrawMode); + } + } + + ////////////////////////////////////////////////////////////////////////////// + // process support + + VclProcessor2D::VclProcessor2D( + const geometry::ViewInformation2D& rViewInformation, + OutputDevice& rOutDev) + : BaseProcessor2D(rViewInformation), + mpOutputDevice(&rOutDev), + maBColorModifierStack(), + maCurrentTransformation(), + maDrawinglayerOpt(), + mnPolygonStrokePrimitive2D(0) + { + // set digit language, derived from SvtCTLOptions to have the correct + // number display for arabic/hindi numerals + const SvtCTLOptions aSvtCTLOptions; + LanguageType eLang(LANGUAGE_SYSTEM); + + if(SvtCTLOptions::NUMERALS_HINDI == aSvtCTLOptions.GetCTLTextNumerals()) + { + eLang = LANGUAGE_ARABIC_SAUDI_ARABIA; + } + else if(SvtCTLOptions::NUMERALS_ARABIC == aSvtCTLOptions.GetCTLTextNumerals()) + { + eLang = LANGUAGE_ENGLISH; + } + else + { + eLang = (LanguageType)Application::GetSettings().GetLanguage(); + } + + rOutDev.SetDigitLanguage(eLang); + } + + VclProcessor2D::~VclProcessor2D() + { + } + } // end of namespace processor2d +} // end of namespace drawinglayer + +////////////////////////////////////////////////////////////////////////////// +// eof |