diff options
Diffstat (limited to 'drawinglayer/source/processor2d/vclprocessor2d.cxx')
-rw-r--r-- | drawinglayer/source/processor2d/vclprocessor2d.cxx | 871 |
1 files changed, 493 insertions, 378 deletions
diff --git a/drawinglayer/source/processor2d/vclprocessor2d.cxx b/drawinglayer/source/processor2d/vclprocessor2d.cxx index 5a0a85f911ef..4d089830a9db 100644 --- a/drawinglayer/source/processor2d/vclprocessor2d.cxx +++ b/drawinglayer/source/processor2d/vclprocessor2d.cxx @@ -22,31 +22,28 @@ #include "getdigitlanguage.hxx" #include "vclhelperbufferdevice.hxx" #include <cmath> -#include <comphelper/string.hxx> +#include <comphelper/lok.hxx> #include <tools/debug.hxx> +#include <tools/fract.hxx> +#include <utility> +#include <vcl/glyphitemcache.hxx> #include <vcl/graph.hxx> +#include <vcl/kernarray.hxx> #include <vcl/outdev.hxx> -#include <rtl/ustrbuf.hxx> #include <sal/log.hxx> -#include <toolkit/helper/vclunohelper.hxx> #include <basegfx/polygon/b2dpolygontools.hxx> #include <basegfx/polygon/b2dpolypolygontools.hxx> #include <basegfx/polygon/b2dpolygonclipper.hxx> #include <basegfx/color/bcolor.hxx> #include <basegfx/matrix/b2dhommatrixtools.hxx> +#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx> #include <drawinglayer/primitive2d/textprimitive2d.hxx> #include <drawinglayer/primitive2d/textdecoratedprimitive2d.hxx> -#include <drawinglayer/primitive2d/polygonprimitive2d.hxx> #include <drawinglayer/primitive2d/bitmapprimitive2d.hxx> #include <drawinglayer/primitive2d/fillgraphicprimitive2d.hxx> -#include <drawinglayer/primitive2d/PolyPolygonHairlinePrimitive2D.hxx> -#include <drawinglayer/primitive2d/PolyPolygonMarkerPrimitive2D.hxx> -#include <drawinglayer/primitive2d/PolyPolygonStrokePrimitive2D.hxx> -#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx> -#include <drawinglayer/primitive2d/PolyPolygonGradientPrimitive2D.hxx> -#include <drawinglayer/primitive2d/PolyPolygonHatchPrimitive2D.hxx> +#include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx> +#include <drawinglayer/primitive2d/PolygonStrokePrimitive2D.hxx> #include <drawinglayer/primitive2d/PolyPolygonGraphicPrimitive2D.hxx> -#include <drawinglayer/primitive2d/PolyPolygonSelectionPrimitive2D.hxx> #include <drawinglayer/primitive2d/maskprimitive2d.hxx> #include <drawinglayer/primitive2d/modifiedcolorprimitive2d.hxx> #include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx> @@ -56,8 +53,6 @@ #include <drawinglayer/primitive2d/pagepreviewprimitive2d.hxx> #include <drawinglayer/primitive2d/textenumsprimitive2d.hxx> #include <drawinglayer/primitive2d/svggradientprimitive2d.hxx> -// for support of Title/Description in all apps when embedding pictures -#include <drawinglayer/primitive2d/objectinfoprimitive2d.hxx> // control support #include <drawinglayer/primitive2d/textlayoutdevice.hxx> @@ -94,6 +89,29 @@ sal_uInt32 calculateStepsForSvgGradient(const basegfx::BColor& rColorA, } } +namespace +{ +/** helper to convert a MapMode to a transformation */ +basegfx::B2DHomMatrix getTransformFromMapMode(const MapMode& rMapMode) +{ + basegfx::B2DHomMatrix aMapping; + const Fraction aNoScale(1, 1); + const Point& rOrigin(rMapMode.GetOrigin()); + + if (0 != rOrigin.X() || 0 != rOrigin.Y()) + { + aMapping.translate(rOrigin.X(), rOrigin.Y()); + } + + if (rMapMode.GetScaleX() != aNoScale || rMapMode.GetScaleY() != aNoScale) + { + aMapping.scale(double(rMapMode.GetScaleX()), double(rMapMode.GetScaleY())); + } + + return aMapping; +} +} + namespace drawinglayer::processor2d { // rendering support @@ -111,45 +129,64 @@ void VclProcessor2D::RenderTextSimpleOrDecoratedPortionPrimitive2D( basegfx::B2DVector aFontScaling, aTranslate; double fRotate, fShearX; aLocalTransform.decompose(aFontScaling, aTranslate, fRotate, fShearX); + bool bPrimitiveAccepted(false); // tdf#95581: Assume tiny shears are rounding artefacts or whatever and can be ignored, // especially if the effect is less than a pixel. if (std::abs(aFontScaling.getY() * fShearX) < 1) { - if (basegfx::fTools::less(aFontScaling.getX(), 0.0) - && basegfx::fTools::less(aFontScaling.getY(), 0.0)) + if (aFontScaling.getX() < 0.0 && aFontScaling.getY() < 0.0) { // handle special case: If scale is negative in (x,y) (3rd quadrant), it can // be expressed as rotation by PI. Use this since the Font rendering will not // apply the negative scales in any form aFontScaling = basegfx::absolute(aFontScaling); - fRotate += F_PI; + fRotate += M_PI; } - if (basegfx::fTools::more(aFontScaling.getX(), 0.0) - && basegfx::fTools::more(aFontScaling.getY(), 0.0)) + if (aFontScaling.getX() > 0.0 && aFontScaling.getY() > 0.0) { - // Get the VCL font (use FontHeight as FontWidth) - vcl::Font aFont(primitive2d::getVclFontFromFontAttribute( - rTextCandidate.getFontAttribute(), aFontScaling.getX(), aFontScaling.getY(), - fRotate, rTextCandidate.getLocale())); + double fIgnoreRotate, fIgnoreShearX; - // set FillColor Attribute - const Color aFillColor(rTextCandidate.getTextFillColor()); - if (aFillColor != COL_TRANSPARENT) + basegfx::B2DVector aFontSize, aTextTranslate; + rTextCandidate.getTextTransform().decompose(aFontSize, aTextTranslate, fIgnoreRotate, + fIgnoreShearX); + + // tdf#153092 Ideally we don't have to scale the font and dxarray, but we might have + // to nevertheless if dealing with non integer sizes + const bool bScaleFont(aFontSize.getY() != std::round(aFontSize.getY()) + || comphelper::LibreOfficeKit::isActive()); + vcl::Font aFont; + + // Get the VCL font + if (!bScaleFont) + { + aFont = primitive2d::getVclFontFromFontAttribute( + rTextCandidate.getFontAttribute(), aFontSize.getX(), aFontSize.getY(), fRotate, + rTextCandidate.getLocale()); + } + else { - aFont.SetFillColor(aFillColor); - aFont.SetTransparent(false); + aFont = primitive2d::getVclFontFromFontAttribute( + rTextCandidate.getFontAttribute(), aFontScaling.getX(), aFontScaling.getY(), + fRotate, rTextCandidate.getLocale()); } // Don't draw fonts without height - if (aFont.GetFontHeight() <= 0) + Size aResultFontSize = aFont.GetFontSize(); + if (aResultFontSize.Height() <= 0) return; + // set FillColor Attribute + const Color aFillColor(rTextCandidate.getTextFillColor()); + aFont.SetTransparent(aFillColor.IsTransparent()); + aFont.SetFillColor(aFillColor); + // handle additional font attributes - const primitive2d::TextDecoratedPortionPrimitive2D* pTCPP - = dynamic_cast<const primitive2d::TextDecoratedPortionPrimitive2D*>( + const primitive2d::TextDecoratedPortionPrimitive2D* pTCPP = nullptr; + if (rTextCandidate.getPrimitive2DID() == PRIMITIVE2D_ID_TEXTDECORATEDPORTIONPRIMITIVE2D) + pTCPP = static_cast<const primitive2d::TextDecoratedPortionPrimitive2D*>( &rTextCandidate); if (pTCPP != nullptr) @@ -251,83 +288,187 @@ void VclProcessor2D::RenderTextSimpleOrDecoratedPortionPrimitive2D( aFont.SetShadow(true); } - // create transformed integer DXArray in view coordinate system - std::vector<long> aTransformedDXArray; + // create integer DXArray + KernArray aDXArray; if (!rTextCandidate.getDXArray().empty()) { - aTransformedDXArray.reserve(rTextCandidate.getDXArray().size()); - const basegfx::B2DVector aPixelVector(maCurrentTransformation - * basegfx::B2DVector(1.0, 0.0)); - const double fPixelVectorFactor(aPixelVector.getLength()); - - for (auto const& elem : rTextCandidate.getDXArray()) + double fPixelVectorFactor(1.0); + if (bScaleFont) { - aTransformedDXArray.push_back(basegfx::fround(elem * fPixelVectorFactor)); + const basegfx::B2DVector aPixelVector(maCurrentTransformation + * basegfx::B2DVector(1.0, 0.0)); + fPixelVectorFactor = aPixelVector.getLength(); } + + aDXArray.reserve(rTextCandidate.getDXArray().size()); + for (auto const& elem : rTextCandidate.getDXArray()) + aDXArray.push_back(elem * fPixelVectorFactor); } // set parameters and paint text snippet const basegfx::BColor aRGBFontColor( maBColorModifierStack.getModifiedColor(rTextCandidate.getFontColor())); - const basegfx::B2DPoint aPoint(aLocalTransform * basegfx::B2DPoint(0.0, 0.0)); - const Point aStartPoint(basegfx::fround(aPoint.getX()), basegfx::fround(aPoint.getY())); - const ComplexTextLayoutFlags nOldLayoutMode(mpOutputDevice->GetLayoutMode()); + + // Store previous complex text layout state, to be restored after drawing + const vcl::text::ComplexTextLayoutFlags nOldLayoutMode(mpOutputDevice->GetLayoutMode()); if (rTextCandidate.getFontAttribute().getRTL()) { - ComplexTextLayoutFlags nRTLLayoutMode(nOldLayoutMode - & ~ComplexTextLayoutFlags::BiDiStrong); - nRTLLayoutMode - |= ComplexTextLayoutFlags::BiDiRtl | ComplexTextLayoutFlags::TextOriginLeft; + vcl::text::ComplexTextLayoutFlags nRTLLayoutMode( + nOldLayoutMode & ~vcl::text::ComplexTextLayoutFlags::BiDiStrong); + nRTLLayoutMode |= vcl::text::ComplexTextLayoutFlags::BiDiRtl + | vcl::text::ComplexTextLayoutFlags::TextOriginLeft; mpOutputDevice->SetLayoutMode(nRTLLayoutMode); } + else + { + // tdf#101686: This is LTR text, but the output device may have RTL state. + vcl::text::ComplexTextLayoutFlags nLTRLayoutMode(nOldLayoutMode); + nLTRLayoutMode = nLTRLayoutMode & ~vcl::text::ComplexTextLayoutFlags::BiDiRtl; + nLTRLayoutMode = nLTRLayoutMode & ~vcl::text::ComplexTextLayoutFlags::BiDiStrong; + mpOutputDevice->SetLayoutMode(nLTRLayoutMode); + } - mpOutputDevice->SetFont(aFont); - mpOutputDevice->SetTextColor(Color(aRGBFontColor)); + Point aStartPoint; + bool bChangeMapMode(false); + if (!bScaleFont) + { + basegfx::B2DHomMatrix aCombinedTransform( + getTransformFromMapMode(mpOutputDevice->GetMapMode()) + * maCurrentTransformation); + + basegfx::B2DVector aCurrentScaling, aCurrentTranslate; + double fCurrentRotate; + aCombinedTransform.decompose(aCurrentScaling, aCurrentTranslate, fCurrentRotate, + fIgnoreShearX); + + const Point aOrigin( + basegfx::fround<tools::Long>(aCurrentTranslate.getX() / aCurrentScaling.getX()), + basegfx::fround<tools::Long>(aCurrentTranslate.getY() + / aCurrentScaling.getY())); + + Fraction aScaleX(aCurrentScaling.getX()); + if (!aScaleX.IsValid()) + { + SAL_WARN("drawinglayer", "invalid X Scale"); + return; + } - OUString aText(rTextCandidate.getText()); - sal_Int32 nPos = rTextCandidate.getTextPosition(); - sal_Int32 nLen = rTextCandidate.getTextLength(); + Fraction aScaleY(aCurrentScaling.getY()); + if (!aScaleY.IsValid()) + { + SAL_WARN("drawinglayer", "invalid Y Scale"); + return; + } + + MapMode aMapMode(mpOutputDevice->GetMapMode().GetMapUnit(), aOrigin, aScaleX, + aScaleY); - long* pDXArray = !aTransformedDXArray.empty() ? aTransformedDXArray.data() : nullptr; + if (fCurrentRotate) + aTextTranslate *= basegfx::utils::createRotateB2DHomMatrix(fCurrentRotate); + aStartPoint = Point(basegfx::fround<tools::Long>(aTextTranslate.getX()), + basegfx::fround<tools::Long>(aTextTranslate.getY())); - if (rTextCandidate.isFilled()) + bChangeMapMode = aMapMode != mpOutputDevice->GetMapMode(); + if (bChangeMapMode) + { + mpOutputDevice->Push(vcl::PushFlags::MAPMODE); + mpOutputDevice->SetRelativeMapMode(aMapMode); + } + } + else { - basegfx::B2DVector aOldFontScaling, aOldTranslate; - double fOldRotate, fOldShearX; - rTextCandidate.getTextTransform().decompose(aOldFontScaling, aOldTranslate, - fOldRotate, fOldShearX); - - long nWidthToFill = static_cast<long>( - rTextCandidate.getWidthToFill() * aFontScaling.getX() / aOldFontScaling.getX()); - - long nWidth - = mpOutputDevice->GetTextArray(rTextCandidate.getText(), pDXArray, 0, 1); - long nChars = 2; - if (nWidth) - nChars = nWidthToFill / nWidth; - - OUStringBuffer aFilled; - comphelper::string::padToLength(aFilled, nChars, aText[0]); - aText = aFilled.makeStringAndClear(); - nPos = 0; - nLen = nChars; + const basegfx::B2DPoint aPoint(aLocalTransform * basegfx::B2DPoint(0.0, 0.0)); + double aPointX = aPoint.getX(), aPointY = aPoint.getY(); + + if (!comphelper::LibreOfficeKit::isActive()) + { + // aFont has an integer size; we must scale a bit for precision + double nFontScalingFixY = aFontScaling.getY() / aResultFontSize.Height(); + double nFontScalingFixX + = aFontScaling.getX() + / (aResultFontSize.Width() ? aResultFontSize.Width() + : aResultFontSize.Height()); + +#ifdef _WIN32 + if (aResultFontSize.Width() + && aResultFontSize.Width() != aResultFontSize.Height()) + { + // See getVclFontFromFontAttribute in drawinglayer/source/primitive2d/textlayoutdevice.cxx + vcl::Font aUnscaledTest(aFont); + aUnscaledTest.SetFontSize({ 0, aResultFontSize.Height() }); + const FontMetric aUnscaledFontMetric( + Application::GetDefaultDevice()->GetFontMetric(aUnscaledTest)); + if (aUnscaledFontMetric.GetAverageFontWidth() > 0) + { + double nExistingXScale = static_cast<double>(aResultFontSize.Width()) + / aUnscaledFontMetric.GetAverageFontWidth(); + nFontScalingFixX + = aFontScaling.getX() / aFontScaling.getY() / nExistingXScale; + } + } +#endif + + if (!rtl_math_approxEqual(nFontScalingFixY, 1.0) + || !rtl_math_approxEqual(nFontScalingFixX, 1.0)) + { + MapMode aMapMode = mpOutputDevice->GetMapMode(); + aMapMode.SetScaleX(aMapMode.GetScaleX() * nFontScalingFixX); + aMapMode.SetScaleY(aMapMode.GetScaleY() * nFontScalingFixY); + + const bool bValidScaling + = aMapMode.GetScaleX().IsValid() && aMapMode.GetScaleY().IsValid(); + if (!bValidScaling) + SAL_WARN("drawinglayer", "skipping invalid scaling"); + else + { + assert(nFontScalingFixX != 0 && nFontScalingFixY != 0 + && "or bValidScaling would be false"); + + Point origin = aMapMode.GetOrigin(); + + mpOutputDevice->Push(vcl::PushFlags::MAPMODE); + mpOutputDevice->SetRelativeMapMode(aMapMode); + bChangeMapMode = true; + + aPointX = (aPointX + origin.X()) / nFontScalingFixX - origin.X(); + aPointY = (aPointY + origin.Y()) / nFontScalingFixY - origin.Y(); + } + } + } + + aStartPoint = Point(basegfx::fround<tools::Long>(aPointX), + basegfx::fround<tools::Long>(aPointY)); } - if (!aTransformedDXArray.empty()) + // tdf#152990 set the font after the MapMode is (potentially) set so canvas uses the desired + // font size + mpOutputDevice->SetFont(aFont); + mpOutputDevice->SetTextColor(Color(aRGBFontColor)); + + if (!aDXArray.empty()) { - mpOutputDevice->DrawTextArray(aStartPoint, aText, pDXArray, nPos, nLen); + const SalLayoutGlyphs* pGlyphs = SalLayoutGlyphsCache::self()->GetLayoutGlyphs( + mpOutputDevice, rTextCandidate.getText(), rTextCandidate.getTextPosition(), + rTextCandidate.getTextLength()); + mpOutputDevice->DrawTextArray( + aStartPoint, rTextCandidate.getText(), aDXArray, + rTextCandidate.getKashidaArray(), rTextCandidate.getTextPosition(), + rTextCandidate.getTextLength(), SalLayoutFlags::NONE, pGlyphs); } else { - mpOutputDevice->DrawText(aStartPoint, aText, nPos, nLen); + mpOutputDevice->DrawText(aStartPoint, rTextCandidate.getText(), + rTextCandidate.getTextPosition(), + rTextCandidate.getTextLength()); } - if (rTextCandidate.getFontAttribute().getRTL()) - { - mpOutputDevice->SetLayoutMode(nOldLayoutMode); - } + // Restore previous layout mode + mpOutputDevice->SetLayoutMode(nOldLayoutMode); + + if (bChangeMapMode) + mpOutputDevice->Pop(); bPrimitiveAccepted = true; } @@ -352,8 +493,7 @@ void VclProcessor2D::RenderPolygonHairlinePrimitive2D( basegfx::B2DPolygon aLocalPolygon(rPolygonCandidate.getB2DPolygon()); aLocalPolygon.transform(maCurrentTransformation); - if (bPixelBased && getOptionsDrawinglayer().IsAntiAliasing() - && getOptionsDrawinglayer().IsSnapHorVerLinesToDiscrete()) + if (bPixelBased && getViewInformation2D().getPixelSnapHairline()) { // #i98289# // when a Hairline is painted and AntiAliasing is on the option SnapHorVerLinesToDiscrete @@ -366,18 +506,18 @@ void VclProcessor2D::RenderPolygonHairlinePrimitive2D( mpOutputDevice->DrawPolyLine(aLocalPolygon, 0.0); } -// direct draw of transformed BitmapEx primitive +// direct draw of transformed Bitmap primitive void VclProcessor2D::RenderBitmapPrimitive2D(const primitive2d::BitmapPrimitive2D& rBitmapCandidate) { - BitmapEx aBitmapEx(VCLUnoHelper::GetBitmap(rBitmapCandidate.getXBitmap())); + Bitmap aBitmap(rBitmapCandidate.getBitmap()); const basegfx::B2DHomMatrix aLocalTransform(maCurrentTransformation * rBitmapCandidate.getTransform()); if (maBColorModifierStack.count()) { - aBitmapEx = aBitmapEx.ModifyBitmapEx(maBColorModifierStack); + aBitmap = aBitmap.Modify(maBColorModifierStack); - if (aBitmapEx.IsEmpty()) + if (aBitmap.IsEmpty()) { // color gets completely replaced, get it const basegfx::BColor aModifiedColor( @@ -397,249 +537,248 @@ void VclProcessor2D::RenderBitmapPrimitive2D(const primitive2d::BitmapPrimitive2 // the own transformer is used (see OutputDevice::DrawTransformedBitmapEx). // draw using OutputDevice'sDrawTransformedBitmapEx - mpOutputDevice->DrawTransformedBitmapEx(aLocalTransform, aBitmapEx); + mpOutputDevice->DrawTransformedBitmapEx(aLocalTransform, aBitmap); } void VclProcessor2D::RenderFillGraphicPrimitive2D( const primitive2d::FillGraphicPrimitive2D& rFillBitmapCandidate) { + if (rFillBitmapCandidate.getTransparency() < 0.0 + || rFillBitmapCandidate.getTransparency() > 1.0) + { + // invalid transparence, done + return; + } + + bool bPrimitiveAccepted = RenderFillGraphicPrimitive2DImpl(rFillBitmapCandidate); + + if (!bPrimitiveAccepted) + { + // do not accept, use decomposition + process(rFillBitmapCandidate); + } +} + +bool VclProcessor2D::RenderFillGraphicPrimitive2DImpl( + const primitive2d::FillGraphicPrimitive2D& rFillBitmapCandidate) +{ const attribute::FillGraphicAttribute& rFillGraphicAttribute( rFillBitmapCandidate.getFillGraphic()); - bool bPrimitiveAccepted(false); // #121194# when tiling is used and content is bitmap-based, do direct tiling in the // renderer on pixel base to ensure tight fitting. Do not do this when // the fill is rotated or sheared. - if (rFillGraphicAttribute.getTiling()) - { - // content is bitmap(ex) - // - // for Vector Graphic Data (SVG, EMF+) support, force decomposition when present. This will lead to use - // the primitive representation of the vector data directly. - // - // when graphic is animated, force decomposition to use the correct graphic, else - // fill style will not be animated - if (GraphicType::Bitmap == rFillGraphicAttribute.getGraphic().GetType() - && !rFillGraphicAttribute.getGraphic().getVectorGraphicData() - && !rFillGraphicAttribute.getGraphic().IsAnimated()) - { - // decompose matrix to check for shear, rotate and mirroring - basegfx::B2DHomMatrix aLocalTransform(maCurrentTransformation - * rFillBitmapCandidate.getTransformation()); - basegfx::B2DVector aScale, aTranslate; - double fRotate, fShearX; - aLocalTransform.decompose(aScale, aTranslate, fRotate, fShearX); - - // when nopt rotated/sheared - if (basegfx::fTools::equalZero(fRotate) && basegfx::fTools::equalZero(fShearX)) - { - // no shear or rotate, draw direct in pixel coordinates - bPrimitiveAccepted = true; + if (!rFillGraphicAttribute.getTiling()) + return false; + + // content is bitmap(ex) + // + // for Vector Graphic Data (SVG, EMF+) support, force decomposition when present. This will lead to use + // the primitive representation of the vector data directly. + // + // when graphic is animated, force decomposition to use the correct graphic, else + // fill style will not be animated + if (GraphicType::Bitmap != rFillGraphicAttribute.getGraphic().GetType() + || rFillGraphicAttribute.getGraphic().getVectorGraphicData() + || rFillGraphicAttribute.getGraphic().IsAnimated()) + return false; + + // decompose matrix to check for shear, rotate and mirroring + basegfx::B2DHomMatrix aLocalTransform(maCurrentTransformation + * rFillBitmapCandidate.getTransformation()); + basegfx::B2DVector aScale, aTranslate; + double fRotate, fShearX; + aLocalTransform.decompose(aScale, aTranslate, fRotate, fShearX); - // transform object range to device coordinates (pixels). Use - // the device transformation for better accuracy - basegfx::B2DRange aObjectRange(aTranslate, aTranslate + aScale); - aObjectRange.transform(mpOutputDevice->GetViewTransformation()); + // when nopt rotated/sheared + if (!basegfx::fTools::equalZero(fRotate) || !basegfx::fTools::equalZero(fShearX)) + return false; - // extract discrete size of object - const sal_Int32 nOWidth(basegfx::fround(aObjectRange.getWidth())); - const sal_Int32 nOHeight(basegfx::fround(aObjectRange.getHeight())); + // no shear or rotate, draw direct in pixel coordinates - // only do something when object has a size in discrete units - if (nOWidth > 0 && nOHeight > 0) - { - // transform graphic range to device coordinates (pixels). Use - // the device transformation for better accuracy - basegfx::B2DRange aGraphicRange(rFillGraphicAttribute.getGraphicRange()); - aGraphicRange.transform(mpOutputDevice->GetViewTransformation() - * aLocalTransform); - - // extract discrete size of graphic - // caution: when getting to zero, nothing would be painted; thus, do not allow this - const sal_Int32 nBWidth( - std::max(sal_Int32(1), basegfx::fround(aGraphicRange.getWidth()))); - const sal_Int32 nBHeight( - std::max(sal_Int32(1), basegfx::fround(aGraphicRange.getHeight()))); - - // only do something when bitmap fill has a size in discrete units - if (nBWidth > 0 && nBHeight > 0) - { - // nBWidth, nBHeight is the pixel size of the needed bitmap. To not need to scale it - // in vcl many times, create a size-optimized version - const Size aNeededBitmapSizePixel(nBWidth, nBHeight); - BitmapEx aBitmapEx(rFillGraphicAttribute.getGraphic().GetBitmapEx()); - const bool bPreScaled(nBWidth * nBHeight < (250 * 250)); - - // ... but only up to a maximum size, else it gets too expensive - if (bPreScaled) - { - // if color depth is below 24bit, expand before scaling for better quality. - // This is even needed for low colors, else the scale will produce - // a bitmap in gray or Black/White (!) - if (aBitmapEx.GetBitCount() < 24) - { - aBitmapEx.Convert(BmpConversion::N24Bit); - } + // transform object range to device coordinates (pixels). Use + // the device transformation for better accuracy + basegfx::B2DRange aObjectRange(aTranslate, aTranslate + aScale); + aObjectRange.transform(mpOutputDevice->GetViewTransformation()); - aBitmapEx.Scale(aNeededBitmapSizePixel, BmpScaleFlag::Interpolate); - } + // extract discrete size of object + const sal_Int32 nOWidth(basegfx::fround(aObjectRange.getWidth())); + const sal_Int32 nOHeight(basegfx::fround(aObjectRange.getHeight())); - bool bPainted(false); + // only do something when object has a size in discrete units + if (nOWidth <= 0 || nOHeight <= 0) + return true; - if (maBColorModifierStack.count()) - { - // when color modifier, apply to bitmap - aBitmapEx = aBitmapEx.ModifyBitmapEx(maBColorModifierStack); + // transform graphic range to device coordinates (pixels). Use + // the device transformation for better accuracy + basegfx::B2DRange aGraphicRange(rFillGraphicAttribute.getGraphicRange()); + aGraphicRange.transform(mpOutputDevice->GetViewTransformation() * aLocalTransform); - // impModifyBitmapEx uses empty bitmap as sign to return that - // the content will be completely replaced to mono color, use shortcut - if (aBitmapEx.IsEmpty()) - { - // color gets completely replaced, get it - const basegfx::BColor aModifiedColor( - maBColorModifierStack.getModifiedColor(basegfx::BColor())); - basegfx::B2DPolygon aPolygon(basegfx::utils::createUnitPolygon()); - aPolygon.transform(aLocalTransform); + // extract discrete size of graphic + // caution: when getting to zero, nothing would be painted; thus, do not allow this + const sal_Int32 nBWidth(std::max(sal_Int32(1), basegfx::fround(aGraphicRange.getWidth()))); + const sal_Int32 nBHeight(std::max(sal_Int32(1), basegfx::fround(aGraphicRange.getHeight()))); - mpOutputDevice->SetFillColor(Color(aModifiedColor)); - mpOutputDevice->SetLineColor(); - mpOutputDevice->DrawPolygon(aPolygon); + // nBWidth, nBHeight is the pixel size of the needed bitmap. To not need to scale it + // in vcl many times, create a size-optimized version + const Size aNeededBitmapSizePixel(nBWidth, nBHeight); + Bitmap aBitmap(rFillGraphicAttribute.getGraphic().GetBitmap()); + const bool bPreScaled(nBWidth * nBHeight < (250 * 250)); - bPainted = true; - } - } + // ... but only up to a maximum size, else it gets too expensive + if (bPreScaled) + { + // if color depth is below 24bit, expand before scaling for better quality. + // This is even needed for low colors, else the scale will produce + // a bitmap in gray or Black/White (!) + if (isPalettePixelFormat(aBitmap.getPixelFormat())) + { + aBitmap.Convert(BmpConversion::N24Bit); + } - if (!bPainted) - { - sal_Int32 nBLeft(basegfx::fround(aGraphicRange.getMinX())); - sal_Int32 nBTop(basegfx::fround(aGraphicRange.getMinY())); - const sal_Int32 nOLeft(basegfx::fround(aObjectRange.getMinX())); - const sal_Int32 nOTop(basegfx::fround(aObjectRange.getMinY())); - sal_Int32 nPosX(0); - sal_Int32 nPosY(0); - - if (nBLeft > nOLeft) - { - const sal_Int32 nDiff((nBLeft / nBWidth) + 1); + aBitmap.Scale(aNeededBitmapSizePixel, BmpScaleFlag::Interpolate); + } - nPosX -= nDiff; - nBLeft -= nDiff * nBWidth; - } + if (rFillBitmapCandidate.hasTransparency()) + aBitmap.BlendAlpha( + static_cast<sal_uInt8>(255 - (rFillBitmapCandidate.getTransparency() * 255))); - if (nBLeft + nBWidth <= nOLeft) - { - const sal_Int32 nDiff(-nBLeft / nBWidth); + if (maBColorModifierStack.count()) + { + // when color modifier, apply to bitmap + aBitmap = aBitmap.Modify(maBColorModifierStack); - nPosX += nDiff; - nBLeft += nDiff * nBWidth; - } + // ModifyBitmapEx uses empty bitmap as sign to return that + // the content will be completely replaced to mono color, use shortcut + if (aBitmap.IsEmpty()) + { + // color gets completely replaced, get it + const basegfx::BColor aModifiedColor( + maBColorModifierStack.getModifiedColor(basegfx::BColor())); + basegfx::B2DPolygon aPolygon(basegfx::utils::createUnitPolygon()); + aPolygon.transform(aLocalTransform); - if (nBTop > nOTop) - { - const sal_Int32 nDiff((nBTop / nBHeight) + 1); + mpOutputDevice->SetFillColor(Color(aModifiedColor)); + mpOutputDevice->SetLineColor(); + mpOutputDevice->DrawPolygon(aPolygon); - nPosY -= nDiff; - nBTop -= nDiff * nBHeight; - } + return true; + } + } - if (nBTop + nBHeight <= nOTop) - { - const sal_Int32 nDiff(-nBTop / nBHeight); + sal_Int32 nBLeft(basegfx::fround(aGraphicRange.getMinX())); + sal_Int32 nBTop(basegfx::fround(aGraphicRange.getMinY())); + const sal_Int32 nOLeft(basegfx::fround(aObjectRange.getMinX())); + const sal_Int32 nOTop(basegfx::fround(aObjectRange.getMinY())); + sal_Int32 nPosX(0); + sal_Int32 nPosY(0); - nPosY += nDiff; - nBTop += nDiff * nBHeight; - } + if (nBLeft > nOLeft) + { + const sal_Int32 nDiff((nBLeft / nBWidth) + 1); - // prepare OutDev - const Point aEmptyPoint(0, 0); - const ::tools::Rectangle aVisiblePixel( - aEmptyPoint, mpOutputDevice->GetOutputSizePixel()); - const bool bWasEnabled(mpOutputDevice->IsMapModeEnabled()); - mpOutputDevice->EnableMapMode(false); + nPosX -= nDiff; + nBLeft -= nDiff * nBWidth; + } - // check if offset is used - const sal_Int32 nOffsetX( - basegfx::fround(rFillGraphicAttribute.getOffsetX() * nBWidth)); + if (nBLeft + nBWidth <= nOLeft) + { + const sal_Int32 nDiff(-nBLeft / nBWidth); - if (nOffsetX) - { - // offset in X, so iterate over Y first and draw lines - for (sal_Int32 nYPos(nBTop); nYPos < nOTop + nOHeight; - nYPos += nBHeight, nPosY++) - { - for (sal_Int32 nXPos((nPosY % 2) ? nBLeft - nBWidth + nOffsetX - : nBLeft); - nXPos < nOLeft + nOWidth; nXPos += nBWidth) - { - const ::tools::Rectangle aOutRectPixel( - Point(nXPos, nYPos), aNeededBitmapSizePixel); - - if (aOutRectPixel.IsOver(aVisiblePixel)) - { - if (bPreScaled) - { - mpOutputDevice->DrawBitmapEx( - aOutRectPixel.TopLeft(), aBitmapEx); - } - else - { - mpOutputDevice->DrawBitmapEx( - aOutRectPixel.TopLeft(), aNeededBitmapSizePixel, - aBitmapEx); - } - } - } - } - } - else - { - // check if offset is used - const sal_Int32 nOffsetY( - basegfx::fround(rFillGraphicAttribute.getOffsetY() * nBHeight)); + nPosX += nDiff; + nBLeft += nDiff * nBWidth; + } - // possible offset in Y, so iterate over X first and draw columns - for (sal_Int32 nXPos(nBLeft); nXPos < nOLeft + nOWidth; - nXPos += nBWidth, nPosX++) - { - for (sal_Int32 nYPos((nPosX % 2) ? nBTop - nBHeight + nOffsetY - : nBTop); - nYPos < nOTop + nOHeight; nYPos += nBHeight) - { - const ::tools::Rectangle aOutRectPixel( - Point(nXPos, nYPos), aNeededBitmapSizePixel); - - if (aOutRectPixel.IsOver(aVisiblePixel)) - { - if (bPreScaled) - { - mpOutputDevice->DrawBitmapEx( - aOutRectPixel.TopLeft(), aBitmapEx); - } - else - { - mpOutputDevice->DrawBitmapEx( - aOutRectPixel.TopLeft(), aNeededBitmapSizePixel, - aBitmapEx); - } - } - } - } - } + if (nBTop > nOTop) + { + const sal_Int32 nDiff((nBTop / nBHeight) + 1); - // restore OutDev - mpOutputDevice->EnableMapMode(bWasEnabled); - } + nPosY -= nDiff; + nBTop -= nDiff * nBHeight; + } + + if (nBTop + nBHeight <= nOTop) + { + const sal_Int32 nDiff(-nBTop / nBHeight); + + nPosY += nDiff; + nBTop += nDiff * nBHeight; + } + + // prepare OutDev + const Point aEmptyPoint(0, 0); + // the visible rect, in pixels + const ::tools::Rectangle aVisiblePixel(aEmptyPoint, mpOutputDevice->GetOutputSizePixel()); + const bool bWasEnabled(mpOutputDevice->IsMapModeEnabled()); + mpOutputDevice->EnableMapMode(false); + + // check if offset is used + const sal_Int32 nOffsetX(basegfx::fround(rFillGraphicAttribute.getOffsetX() * nBWidth)); + const sal_Int32 nOffsetY(basegfx::fround(rFillGraphicAttribute.getOffsetY() * nBHeight)); + + // if the tile is a single pixel big, just flood fill with that pixel color + if (nOffsetX == 0 && nOffsetY == 0 && aNeededBitmapSizePixel.getWidth() == 1 + && aNeededBitmapSizePixel.getHeight() == 1) + { + Color col = aBitmap.GetPixelColor(0, 0); + mpOutputDevice->SetLineColor(col); + mpOutputDevice->SetFillColor(col); + mpOutputDevice->DrawRect(aVisiblePixel); + } + else if (nOffsetX) + { + // offset in X, so iterate over Y first and draw lines + for (sal_Int32 nYPos(nBTop); nYPos < nOTop + nOHeight; nYPos += nBHeight, nPosY++) + { + for (sal_Int32 nXPos((nPosY % 2) ? nBLeft - nBWidth + nOffsetX : nBLeft); + nXPos < nOLeft + nOWidth; nXPos += nBWidth) + { + const ::tools::Rectangle aOutRectPixel(Point(nXPos, nYPos), aNeededBitmapSizePixel); + + if (aOutRectPixel.Overlaps(aVisiblePixel)) + { + if (bPreScaled) + { + mpOutputDevice->DrawBitmapEx(aOutRectPixel.TopLeft(), aBitmap); + } + else + { + mpOutputDevice->DrawBitmapEx(aOutRectPixel.TopLeft(), + aNeededBitmapSizePixel, aBitmap); } } } } } - - if (!bPrimitiveAccepted) + else // nOffsetY is used { - // do not accept, use decomposition - process(rFillBitmapCandidate); + // possible offset in Y, so iterate over X first and draw columns + for (sal_Int32 nXPos(nBLeft); nXPos < nOLeft + nOWidth; nXPos += nBWidth, nPosX++) + { + for (sal_Int32 nYPos((nPosX % 2) ? nBTop - nBHeight + nOffsetY : nBTop); + nYPos < nOTop + nOHeight; nYPos += nBHeight) + { + const ::tools::Rectangle aOutRectPixel(Point(nXPos, nYPos), aNeededBitmapSizePixel); + + if (aOutRectPixel.Overlaps(aVisiblePixel)) + { + if (bPreScaled) + { + mpOutputDevice->DrawBitmapEx(aOutRectPixel.TopLeft(), aBitmap); + } + else + { + mpOutputDevice->DrawBitmapEx(aOutRectPixel.TopLeft(), + aNeededBitmapSizePixel, aBitmap); + } + } + } + } } + + // restore OutDev + mpOutputDevice->EnableMapMode(bWasEnabled); + return true; } // direct draw of Graphic @@ -672,7 +811,8 @@ void VclProcessor2D::RenderPolyPolygonGraphicPrimitive2D( case GraphicType::Bitmap: { if (!rFillGraphicAttribute.getGraphic().IsTransparent() - && !rFillGraphicAttribute.getGraphic().IsAlpha()) + && !rFillGraphicAttribute.getGraphic().IsAlpha() + && !rPolygonCandidate.hasTransparency()) { // bitmap is not transparent and has no alpha const sal_uInt32 nBColorModifierStackCount(maBColorModifierStack.count()); @@ -757,7 +897,7 @@ void VclProcessor2D::RenderPolyPolygonGraphicPrimitive2D( } } -// mask group. Force output to VDev and create mask from given mask +// mask group void VclProcessor2D::RenderMaskPrimitive2DPixel(const primitive2d::MaskPrimitive2D& rMaskCandidate) { if (rMaskCandidate.getChildren().empty()) @@ -769,6 +909,16 @@ void VclProcessor2D::RenderMaskPrimitive2DPixel(const primitive2d::MaskPrimitive return; aMask.transform(maCurrentTransformation); + + // Unless smooth edges are needed, simply use clipping. + if (basegfx::utils::isRectangle(aMask) || !getViewInformation2D().getUseAntiAliasing()) + { + auto popIt = mpOutputDevice->ScopedPush(vcl::PushFlags::CLIPREGION); + mpOutputDevice->IntersectClipRegion(vcl::Region(aMask)); + process(rMaskCandidate.getChildren()); + return; + } + const basegfx::B2DRange aRange(basegfx::utils::getRange(aMask)); impBufferDevice aBufferDevice(*mpOutputDevice, aRange); @@ -785,28 +935,11 @@ void VclProcessor2D::RenderMaskPrimitive2DPixel(const primitive2d::MaskPrimitive // back to old OutDev mpOutputDevice = pLastOutputDevice; - // if the mask fills the whole area we can skip - // creating a transparent vd and filling it. - if (!basegfx::utils::isRectangle(aMask)) - { - // draw mask - if (getOptionsDrawinglayer().IsAntiAliasing()) - { - // with AA, use 8bit AlphaMask to get nice borders - VirtualDevice& rTransparence = aBufferDevice.getTransparence(); - rTransparence.SetLineColor(); - rTransparence.SetFillColor(COL_BLACK); - rTransparence.DrawPolyPolygon(aMask); - } - else - { - // No AA, use 1bit mask - VirtualDevice& rMask = aBufferDevice.getMask(); - rMask.SetLineColor(); - rMask.SetFillColor(COL_BLACK); - rMask.DrawPolyPolygon(aMask); - } - } + // draw mask + VirtualDevice& rMask = aBufferDevice.getTransparence(); + rMask.SetLineColor(); + rMask.SetFillColor(COL_BLACK); + rMask.DrawPolyPolygon(aMask); // dump buffer to outdev aBufferDevice.paint(); @@ -841,14 +974,13 @@ void VclProcessor2D::RenderUnifiedTransparencePrimitive2D( // transparence is in visible range basegfx::B2DRange aRange(rTransCandidate.getChildren().getB2DRange(getViewInformation2D())); aRange.transform(maCurrentTransformation); - impBufferDevice aBufferDevice(*mpOutputDevice, aRange, true); + impBufferDevice aBufferDevice(*mpOutputDevice, aRange); if (aBufferDevice.isVisible()) { // remember last OutDev and set to content OutputDevice* pLastOutputDevice = mpOutputDevice; mpOutputDevice = &aBufferDevice.getContent(); - mpOutputDevice->Erase(); // paint content to it process(rTransCandidate.getChildren()); @@ -871,7 +1003,7 @@ void VclProcessor2D::RenderTransparencePrimitive2D( basegfx::B2DRange aRange(rTransCandidate.getChildren().getB2DRange(getViewInformation2D())); aRange.transform(maCurrentTransformation); - impBufferDevice aBufferDevice(*mpOutputDevice, aRange, true); + impBufferDevice aBufferDevice(*mpOutputDevice, aRange); if (!aBufferDevice.isVisible()) return; @@ -879,7 +1011,6 @@ void VclProcessor2D::RenderTransparencePrimitive2D( // remember last OutDev and set to content OutputDevice* pLastOutputDevice = mpOutputDevice; mpOutputDevice = &aBufferDevice.getContent(); - mpOutputDevice->Erase(); // paint content to it process(rTransCandidate.getChildren()); @@ -891,13 +1022,11 @@ void VclProcessor2D::RenderTransparencePrimitive2D( basegfx::BColorModifierStack aLastBColorModifierStack(maBColorModifierStack); maBColorModifierStack = basegfx::BColorModifierStack(); - mpOutputDevice->Erase(); - // paint mask to it (always with transparence intensities, evtl. with AA) process(rTransCandidate.getTransparence()); // back to old color stack - maBColorModifierStack = aLastBColorModifierStack; + maBColorModifierStack = std::move(aLastBColorModifierStack); // back to old OutDev mpOutputDevice = pLastOutputDevice; @@ -917,19 +1046,17 @@ void VclProcessor2D::RenderTransformPrimitive2D( // create new transformations for CurrentTransformation // and for local ViewInformation2D maCurrentTransformation = maCurrentTransformation * rTransformCandidate.getTransformation(); - const geometry::ViewInformation2D aViewInformation2D( - getViewInformation2D().getObjectTransformation() * rTransformCandidate.getTransformation(), - getViewInformation2D().getViewTransformation(), getViewInformation2D().getViewport(), - getViewInformation2D().getVisualizedPage(), getViewInformation2D().getViewTime(), - getViewInformation2D().getExtendedInformationSequence()); - updateViewInformation(aViewInformation2D); + geometry::ViewInformation2D aViewInformation2D(getViewInformation2D()); + aViewInformation2D.setObjectTransformation(getViewInformation2D().getObjectTransformation() + * rTransformCandidate.getTransformation()); + setViewInformation2D(aViewInformation2D); // process content process(rTransformCandidate.getChildren()); // restore transformations maCurrentTransformation = aLastCurrentTransformation; - updateViewInformation(aLastViewInformation2D); + setViewInformation2D(aLastViewInformation2D); } // new XDrawPage for ViewInformation2D @@ -940,18 +1067,15 @@ void VclProcessor2D::RenderPagePreviewPrimitive2D( 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); + geometry::ViewInformation2D aViewInformation2D(getViewInformation2D()); + aViewInformation2D.setVisualizedPage(rPagePreviewCandidate.getXDrawPage()); + setViewInformation2D(aViewInformation2D); // process decomposed content process(rPagePreviewCandidate); // restore transformations - updateViewInformation(aLastViewInformation2D); + setViewInformation2D(aLastViewInformation2D); } // marker @@ -962,11 +1086,11 @@ void VclProcessor2D::RenderMarkerArrayPrimitive2D( const std::vector<basegfx::B2DPoint>& rPositions = rMarkArrayCandidate.getPositions(); const sal_uInt32 nCount(rPositions.size()); - if (!(nCount && !rMarkArrayCandidate.getMarker().IsEmpty())) + if (!nCount || rMarkArrayCandidate.getMarker().IsEmpty()) return; // get pixel size - const BitmapEx& rMarker(rMarkArrayCandidate.getMarker()); + const Bitmap& rMarker(rMarkArrayCandidate.getMarker()); const Size aBitmapSize(rMarker.GetSizePixel()); if (!(aBitmapSize.Width() && aBitmapSize.Height())) @@ -990,8 +1114,8 @@ void VclProcessor2D::RenderMarkerArrayPrimitive2D( { const basegfx::B2DPoint aDiscreteTopLeft((maCurrentTransformation * pos) - aDiscreteHalfSize); - const Point aDiscretePoint(basegfx::fround(aDiscreteTopLeft.getX()), - basegfx::fround(aDiscreteTopLeft.getY())); + const Point aDiscretePoint(basegfx::fround<tools::Long>(aDiscreteTopLeft.getX()), + basegfx::fround<tools::Long>(aDiscreteTopLeft.getY())); mpOutputDevice->DrawBitmapEx(aDiscretePoint + aOrigin, rMarker); } @@ -1011,8 +1135,8 @@ void VclProcessor2D::RenderPointArrayPrimitive2D( for (auto const& pos : rPositions) { const basegfx::B2DPoint aViewPosition(maCurrentTransformation * pos); - const Point aPos(basegfx::fround(aViewPosition.getX()), - basegfx::fround(aViewPosition.getY())); + const Point aPos(basegfx::fround<tools::Long>(aViewPosition.getX()), + basegfx::fround<tools::Long>(aViewPosition.getY())); mpOutputDevice->DrawPixel(aPos, aVCLColor); } @@ -1027,7 +1151,7 @@ void VclProcessor2D::RenderPolygonStrokePrimitive2D( const double fLineWidth(rLineAttribute.getWidth()); bool bDone(false); - if (basegfx::fTools::more(fLineWidth, 0.0)) + if (fLineWidth > 0.0) { const basegfx::B2DVector aDiscreteUnit(maCurrentTransformation * basegfx::B2DVector(fLineWidth, 0.0)); @@ -1058,7 +1182,7 @@ void VclProcessor2D::RenderPolygonStrokePrimitive2D( if (nCount) { - const bool bAntiAliased(getOptionsDrawinglayer().IsAntiAliasing()); + const bool bAntiAliased(getViewInformation2D().getUseAntiAliasing()); aHairlinePolyPolygon.transform(maCurrentTransformation); if (bAntiAliased) @@ -1271,26 +1395,12 @@ void VclProcessor2D::RenderEpsPrimitive2D(const primitive2d::EpsPrimitive2D& rEp } } -void VclProcessor2D::RenderObjectInfoPrimitive2D( - const primitive2d::ObjectInfoPrimitive2D& rObjectInfoPrimitive2D) -{ - // remember current ObjectInfoPrimitive2D and set new current one (build a stack - push) - const primitive2d::ObjectInfoPrimitive2D* pLast(getObjectInfoPrimitive2D()); - mpObjectInfoPrimitive2D = &rObjectInfoPrimitive2D; - - // process content - process(rObjectInfoPrimitive2D.getChildren()); - - // restore current ObjectInfoPrimitive2D (pop) - mpObjectInfoPrimitive2D = pLast; -} - void VclProcessor2D::RenderSvgLinearAtomPrimitive2D( const primitive2d::SvgLinearAtomPrimitive2D& rCandidate) { const double fDelta(rCandidate.getOffsetB() - rCandidate.getOffsetA()); - if (!basegfx::fTools::more(fDelta, 0.0)) + if (fDelta <= 0.0) return; const basegfx::BColor aColorA(maBColorModifierStack.getModifiedColor(rCandidate.getColorA())); @@ -1300,7 +1410,7 @@ void VclProcessor2D::RenderSvgLinearAtomPrimitive2D( const basegfx::B2DVector aDiscreteVector( getViewInformation2D().getInverseObjectToViewTransformation() * basegfx::B2DVector(1.0, 1.0)); - const double fDiscreteUnit(aDiscreteVector.getLength() * (1.0 / 1.414213562373)); + const double fDiscreteUnit(aDiscreteVector.getLength() * (1.0 / M_SQRT2)); // use color distance and discrete lengths to calculate step count const sal_uInt32 nSteps(calculateStepsForSvgGradient(aColorA, aColorB, fDelta, fDiscreteUnit)); @@ -1334,7 +1444,7 @@ void VclProcessor2D::RenderSvgRadialAtomPrimitive2D( { const double fDeltaScale(rCandidate.getScaleB() - rCandidate.getScaleA()); - if (!basegfx::fTools::more(fDeltaScale, 0.0)) + if (fDeltaScale <= 0.0) return; const basegfx::BColor aColorA(maBColorModifierStack.getModifiedColor(rCandidate.getColorA())); @@ -1344,7 +1454,7 @@ void VclProcessor2D::RenderSvgRadialAtomPrimitive2D( const basegfx::B2DVector aDiscreteVector( getViewInformation2D().getInverseObjectToViewTransformation() * basegfx::B2DVector(1.0, 1.0)); - const double fDiscreteUnit(aDiscreteVector.getLength() * (1.0 / 1.414213562373)); + const double fDiscreteUnit(aDiscreteVector.getLength() * (1.0 / M_SQRT2)); // use color distance and discrete lengths to calculate step count const sal_uInt32 nSteps( @@ -1437,8 +1547,7 @@ void VclProcessor2D::adaptTextToFillDrawMode() const { const DrawModeFlags nOriginalDrawMode(mpOutputDevice->GetDrawMode()); if (!(nOriginalDrawMode - & (DrawModeFlags::BlackText | DrawModeFlags::GrayText | DrawModeFlags::WhiteText - | DrawModeFlags::SettingsText))) + & (DrawModeFlags::BlackText | DrawModeFlags::GrayText | DrawModeFlags::SettingsText))) return; DrawModeFlags nAdaptedDrawMode(nOriginalDrawMode); @@ -1461,15 +1570,6 @@ void VclProcessor2D::adaptTextToFillDrawMode() const nAdaptedDrawMode &= ~DrawModeFlags::GrayFill; } - if (nOriginalDrawMode & DrawModeFlags::WhiteText) - { - nAdaptedDrawMode |= DrawModeFlags::WhiteFill; - } - else - { - nAdaptedDrawMode &= ~DrawModeFlags::WhiteFill; - } - if (nOriginalDrawMode & DrawModeFlags::SettingsText) { nAdaptedDrawMode |= DrawModeFlags::SettingsFill; @@ -1482,25 +1582,40 @@ void VclProcessor2D::adaptTextToFillDrawMode() const mpOutputDevice->SetDrawMode(nAdaptedDrawMode); } -// process support +void VclProcessor2D::onViewInformation2DChanged() +{ + // apply AntiAlias information to target device + if (getViewInformation2D().getUseAntiAliasing()) + mpOutputDevice->SetAntialiasing(mpOutputDevice->GetAntialiasing() + | AntialiasingFlags::Enable); + else + mpOutputDevice->SetAntialiasing(mpOutputDevice->GetAntialiasing() + & ~AntialiasingFlags::Enable); + + // apply DrawModeFlags to target device + if (getViewInformation2D().getDrawModeFlags() != mpOutputDevice->GetDrawMode()) + mpOutputDevice->SetDrawMode(getViewInformation2D().getDrawModeFlags()); +} +// process support VclProcessor2D::VclProcessor2D(const geometry::ViewInformation2D& rViewInformation, - OutputDevice& rOutDev, - const basegfx::BColorModifierStack& rInitStack) + OutputDevice& rOutDev) : BaseProcessor2D(rViewInformation) , mpOutputDevice(&rOutDev) - , maBColorModifierStack(rInitStack) - , maCurrentTransformation() - , maDrawinglayerOpt() + , maBColorModifierStack() , mnPolygonStrokePrimitive2D(0) - , mpObjectInfoPrimitive2D(nullptr) + , mnOriginalAA(rOutDev.GetAntialiasing()) { // set digit language, derived from SvtCTLOptions to have the correct // number display for arabic/hindi numerals rOutDev.SetDigitLanguage(drawinglayer::detail::getDigitLanguage()); + + // NOTE: to save/restore original AntiAliasing mode we need + // to use mnOriginalAA here - OutputDevice::Push/Pop does not + // offer that } -VclProcessor2D::~VclProcessor2D() {} +VclProcessor2D::~VclProcessor2D() { mpOutputDevice->SetAntialiasing(mnOriginalAA); } } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |