summaryrefslogtreecommitdiff
path: root/drawinglayer/source/processor2d/canvasprocessor.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'drawinglayer/source/processor2d/canvasprocessor.cxx')
-rw-r--r--drawinglayer/source/processor2d/canvasprocessor.cxx2218
1 files changed, 2218 insertions, 0 deletions
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