/************************************************************************* * * OpenOffice.org - a multi-platform office productivity suite * * $RCSfile: canvasprocessor.cxx,v $ * * $Revision: 1.8 $ * * last change: $Author: aw $ $Date: 2008-07-21 17:41:18 $ * * The Contents of this file are made available subject to * the terms of GNU Lesser General Public License Version 2.1. * * * GNU Lesser General Public License Version 2.1 * ============================================= * Copyright 2005 by Sun Microsystems, Inc. * 901 San Antonio Road, Palo Alto, CA 94303, USA * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License version 2.1, as published by the Free Software Foundation. * * This library 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 for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, * MA 02111-1307 USA * ************************************************************************/ // MARKER(update_precomp.py): autogen include statement, do not remove #include "precompiled_drawinglayer.hxx" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include ////////////////////////////////////////////////////////////////////////////// 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(alpha) + x/2 cos(alpha) // // (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::createPolygonFromRect( basegfx::B2DRange(0.0, 0.0, 1.0, 1.0)); } // 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.getFontAttributes() ); 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 xFont( mxCanvas->createFont( aFontRequest, uno::Sequence< beans::PropertyValue >(), aFontMatrix )); if( !xFont.is() ) return; uno::Reference 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(&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( 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 alpha // channel. Note that the OutDev implementation has a // shortcoming, in that nested transparency groups // don't work - alpha is not combined properly. // process(rTransCandidate.getTransparenceList()); // back to old OutDev and worldToView mxCanvas = xLastCanvas; setWorldToView(aLastWorldToView); // DUMMY: add alpha 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& 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 { ////////////////////////////////////////////////////////////////////////////// // internal processing support void canvasProcessor2D::processBasePrimitive2D(const primitive2d::BasePrimitive2D& rCandidate) { switch(rCandidate.getPrimitiveID()) { 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::setIdentityAffineMatrix2D(maRenderState.AffineTransform); 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::setIdentityAffineMatrix2D(maRenderState.AffineTransform); 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. Special handling for MetaFiles. const primitive2d::MaskPrimitive2D& rMaskCandidate = static_cast< const primitive2d::MaskPrimitive2D& >(rCandidate); if(rMaskCandidate.getChildren().hasElements()) { basegfx::B2DPolyPolygon aMask(rMaskCandidate.getMask()); if(aMask.count()) { // transform new mask polygon to World coordinates for processing. All masks // are processed in World 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(); } // recursively paint content process(rMaskCandidate.getChildren()); // 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(); } } else { // no mask, no clipping. recursively paint content process(rMaskCandidate.getChildren()); } } 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::setViewStateTransform(maViewState, getViewInformation2D().getObjectToViewTransformation()); // proccess content process(rTransformCandidate.getChildren()); // restore transformations updateViewInformation(aLastViewInformation2D); // restore at canvas canvas::tools::setViewStateTransform(maViewState, getViewInformation2D().getObjectToViewTransformation()); 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 const primitive2d::MetafilePrimitive2D& rMetaCandidate = static_cast< const primitive2d::MetafilePrimitive2D& >(rCandidate); 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(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(); } 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::setIdentityAffineMatrix2D(maRenderState.AffineTransform); 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 const primitive2d::TextSimplePortionPrimitive2D& rTextCandidate = static_cast< const primitive2d::TextSimplePortionPrimitive2D& >(rCandidate); if(rTextCandidate.getTextLength()) { const primitive2d::FontAttributes& rFontAttrs(rTextCandidate.getFontAttributes()); rendering::FontRequest aFontRequest; aFontRequest.FontDescription.FamilyName = rFontAttrs.getFamilyName(); aFontRequest.FontDescription.StyleName = rFontAttrs.getStyleName(); aFontRequest.FontDescription.IsSymbolFont = rFontAttrs.getSymbol() ? util::TriState_YES : util::TriState_NO; aFontRequest.FontDescription.IsVertical = rFontAttrs.getVertical() ? util::TriState_YES : util::TriState_NO; // TODO(F2): improve vclenum->panose conversion aFontRequest.FontDescription.FontDescription.Weight = static_cast< sal_uInt8 >(rFontAttrs.getWeight()); aFontRequest.FontDescription.FontDescription.Letterform = rFontAttrs.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 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 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, rTextCandidate.getTextTransform()); // paint mxCanvas->drawTextLayout(xLayout, maViewState, maRenderState); } } } break; } case PRIMITIVE2D_ID_BITMAPPRIMITIVE2D : { // Bitmap primitive const primitive2d::BitmapPrimitive2D& rBitmapCandidate = static_cast< const primitive2d::BitmapPrimitive2D& >(rCandidate); if(rBitmapCandidate.getBitmapEx().IsEmpty()) { // replace with color filled polygon const basegfx::BColor aModifiedColor(maBColorModifierStack.getModifiedColor(basegfx::BColor())); const basegfx::B2DPolygon aPolygon(basegfx::tools::createPolygonFromRect(basegfx::B2DRange(0.0, 0.0, 1.0, 1.0))); maRenderState.DeviceColor = aModifiedColor.colorToDoubleSequence(mxCanvas->getDevice()); canvas::tools::setRenderStateTransform(maRenderState, rBitmapCandidate.getTransform()); mxCanvas->fillPolyPolygon(basegfx::unotools::xPolyPolygonFromB2DPolyPolygon( mxCanvas->getDevice(), basegfx::B2DPolyPolygon(aPolygon)), maViewState, maRenderState); } else { // apply possible color modification to BitmapEx BitmapEx aBitmapEx(impModifyBitmapEx(maBColorModifierStack, rBitmapCandidate.getBitmapEx())); // adapt object's transformation to the correct scale basegfx::B2DVector aScale, aTranslate; double fRotate, fShearX; basegfx::B2DHomMatrix aNewMatrix; const Size aSizePixel(aBitmapEx.GetSizePixel()); const double fBitmapWidth(aSizePixel.Width() == 0 ? 1.0 : aSizePixel.Width()); const double fBitmapHeight(aSizePixel.Height() == 0 ? 1.0 : aSizePixel.Height()); rBitmapCandidate.getTransform().decompose(aScale, aTranslate, fRotate, fShearX); aNewMatrix.scale(aScale.getX() / fBitmapWidth, aScale.getY() / fBitmapHeight); aNewMatrix.shearX(fShearX); aNewMatrix.rotate(fRotate); aNewMatrix.translate(aTranslate.getX(), aTranslate.getY()); canvas::tools::setRenderStateTransform(maRenderState, aNewMatrix); mxCanvas->drawBitmap( vcl::unotools::xBitmapFromBitmapEx(mxCanvas->getDevice(), aBitmapEx), maViewState, maRenderState); } break; } case PRIMITIVE2D_ID_ALPHAPRIMITIVE2D : { // Alpha primitive const primitive2d::AlphaPrimitive2D& rAlphaCandidate = static_cast< const primitive2d::AlphaPrimitive2D& >(rCandidate); const primitive2d::Primitive2DSequence& rChildren = rAlphaCandidate.getChildren(); if(rChildren.hasElements()) { // get logic and discrete range of transparent part basegfx::B2DRange aLogicRange(primitive2d::getB2DRangeFromPrimitive2DSequence(rChildren, getViewInformation2D())); 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()))); // determine integer bitmap size const com::sun::star::geometry::IntegerSize2D aIntegerSize2D( basegfx::fround(aDiscreteRange.getWidth()), basegfx::fround(aDiscreteRange.getHeight())); // create XBitmap for rendering child context uno::Reference< rendering::XCanvas > xChildBitmap( mxCanvas->getDevice()->createCompatibleBitmap(aIntegerSize2D), uno::UNO_QUERY_THROW); if(xChildBitmap.is()) { // create and set ViewInformation2D for Sub-Canvas const geometry::ViewInformation2D aLastViewInformation2D(getViewInformation2D()); // prepare discrete offset for XBitmap basegfx::B2DHomMatrix aDiscreteOffset; aDiscreteOffset.translate(-aDiscreteRange.getMinX(), -aDiscreteRange.getMinY()); // 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); canvas::tools::setViewStateTransform(maViewState, getViewInformation2D().getObjectToViewTransformation()); // remember last canvas uno::Reference< rendering::XCanvas > xLastCanvas(mxCanvas); // set child bitmap as target and paint child content mxCanvas = xChildBitmap; process(rChildren); // check if alpha is simple alpha (just one PolyPolygonColor primitive) bool bIsSimpleTransparence(false); const primitive2d::PolyPolygonColorPrimitive2D* pPoPoColor = 0; uno::Reference< rendering::XCanvas > xAlphaBitmap; const primitive2d::Primitive2DSequence& rAlpha = rAlphaCandidate.getAlpha(); if(rAlpha.hasElements() && 1 == rAlpha.getLength()) { const primitive2d::Primitive2DReference xReference(rAlpha[0]); pPoPoColor = dynamic_cast< const primitive2d::PolyPolygonColorPrimitive2D* >(xReference.get()); if(pPoPoColor && PRIMITIVE2D_ID_POLYPOLYGONCOLORPRIMITIVE2D == pPoPoColor->getPrimitiveID()) { bIsSimpleTransparence = true; } } if(!bIsSimpleTransparence) { // create XBitmap for rendering alpha context xAlphaBitmap = uno::Reference< rendering::XCanvas >( mxCanvas->getDevice()->createCompatibleBitmap(aIntegerSize2D), uno::UNO_QUERY_THROW); if(xAlphaBitmap.is()) { // set alpha bitmap as target and paint child content mxCanvas = xAlphaBitmap; process(rAlpha); } } // restore old canvas and ViewInformation mxCanvas = xLastCanvas; updateViewInformation(aLastViewInformation2D); canvas::tools::setViewStateTransform(maViewState, getViewInformation2D().getObjectToViewTransformation()); // prepare bitmap transformation for local painting const double fBitmapWidth(std::max(1.0, aDiscreteRange.getWidth())); const double fBitmapHeight(std::max(1.0, aDiscreteRange.getHeight())); basegfx::B2DHomMatrix aBitmapTransform; // transform back logic range from discrete range to work on same bounds and // to have the correct logic width/Height for bitmap render state due to discrete // roundings (pixel alignments) aLogicRange = aDiscreteRange; aLogicRange.transform(getViewInformation2D().getInverseObjectToViewTransformation()); // LogicRange Width/Height is correct aBitmapTransform.scale(aLogicRange.getWidth() / fBitmapWidth, aLogicRange.getHeight() / fBitmapHeight); aBitmapTransform.translate(aLogicRange.getMinX(), aLogicRange.getMinY()); canvas::tools::setRenderStateTransform(maRenderState, aBitmapTransform); if(bIsSimpleTransparence) { // it's a simple transparence, draw modulated content XBitmap. The alpha // is contained in the fill color of pPoPoColor, all color channels are // the same to build a gray value const basegfx::BColor& rAlphaFill = pPoPoColor->getBColor(); // add alpha modulation value to DeviceColor uno::Sequence< double > aColor(4); aColor[0] = 1.0; aColor[1] = 1.0; aColor[2] = 1.0; aColor[3] = rAlphaFill.getRed(); maRenderState.DeviceColor = aColor; // draw child bitmap mxCanvas->drawBitmapModulated(uno::Reference< rendering::XBitmap >(xChildBitmap, uno::UNO_QUERY_THROW), maViewState, maRenderState); } else { // It's a alpha with various geometry, need to combine child and alpha bitmap to a single // BitmapEx. I konow no other way here than to get the VCL Bitmaps and using VCL functionality // for this combination. This is very slow and needs to be enchanced ASAP. const uno::Reference< rendering::XIntegerReadOnlyBitmap > xReadOnlyChild(xChildBitmap, uno::UNO_QUERY_THROW); const uno::Reference< rendering::XIntegerReadOnlyBitmap > xReadOnlyAlpha(xAlphaBitmap, uno::UNO_QUERY_THROW); const Bitmap aChildBitmap(vcl::unotools::bitmapExFromXBitmap(xReadOnlyChild).GetBitmap()); const AlphaMask aAlphaBitmap(vcl::unotools::bitmapExFromXBitmap(xReadOnlyAlpha).GetBitmap()); // draw bitmap mxCanvas->drawBitmap(vcl::unotools::xBitmapFromBitmapEx(mxCanvas->getDevice(), BitmapEx(aChildBitmap, aAlphaBitmap)), maViewState, maRenderState); } } } break; } default : { // process recursively process(rCandidate.get2DDecomposition(getViewInformation2D())); break; } } } ////////////////////////////////////////////////////////////////////////////// // process support canvasProcessor2D::canvasProcessor2D( const geometry::ViewInformation2D& rViewInformation, const uno::Reference< rendering::XCanvas >& rCanvas) : BaseProcessor2D(rViewInformation), mxCanvas( rCanvas ), maViewState(), maRenderState(), maBColorModifierStack(), maDrawinglayerOpt(), maClipPolyPolygon(), meLang(LANGUAGE_SYSTEM) { const SvtCTLOptions aSvtCTLOptions; canvas::tools::initViewState(maViewState); canvas::tools::initRenderState(maRenderState); canvas::tools::setViewStateTransform(maViewState, getViewInformation2D().getObjectToViewTransformation()); if(SvtCTLOptions::NUMERALS_HINDI == aSvtCTLOptions.GetCTLTextNumerals()) { meLang = LANGUAGE_ARABIC; } else if(SvtCTLOptions::NUMERALS_ARABIC == aSvtCTLOptions.GetCTLTextNumerals()) { meLang = LANGUAGE_ENGLISH; } else { meLang = (LanguageType)Application::GetSettings().GetLanguage(); } } canvasProcessor2D::~canvasProcessor2D() { } } // end of namespace processor2d } // end of namespace drawinglayer ////////////////////////////////////////////////////////////////////////////// // eof