| /************************************************************** |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the 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/PanoseProportion.hpp> |
| #include <com/sun/star/rendering/CompositeOperation.hpp> |
| #include <com/sun/star/rendering/StrokeAttributes.hpp> |
| #include <com/sun/star/rendering/PathJoinType.hpp> |
| #include <com/sun/star/rendering/PathCapType.hpp> |
| #include <drawinglayer/primitive2d/fillbitmapprimitive2d.hxx> |
| #include <com/sun/star/rendering/TexturingMode.hpp> |
| #include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx> |
| #include <vclhelperbufferdevice.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.Proportion = |
| rFontAttr.getMonospaced() |
| ? rendering::PanoseProportion::MONO_SPACED |
| : rendering::PanoseProportion::ANYTHING; |
| 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; |
| } |
| |
| switch(rLineAttribute.getLineCap()) |
| { |
| case com::sun::star::drawing::LineCap_ROUND: |
| aStrokeAttribute.StartCapType = rendering::PathCapType::ROUND; |
| aStrokeAttribute.EndCapType = rendering::PathCapType::ROUND; |
| break; |
| case com::sun::star::drawing::LineCap_SQUARE: |
| aStrokeAttribute.StartCapType = rendering::PathCapType::SQUARE; |
| aStrokeAttribute.EndCapType = rendering::PathCapType::SQUARE; |
| break; |
| default: // com::sun::star::drawing::LineCap_BUTT |
| aStrokeAttribute.StartCapType = rendering::PathCapType::BUTT; |
| aStrokeAttribute.EndCapType = rendering::PathCapType::BUTT; |
| 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_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), |
| 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 |