diff options
Diffstat (limited to 'drawinglayer/source/processor2d/vclprocessor2d.cxx')
-rw-r--r-- | drawinglayer/source/processor2d/vclprocessor2d.cxx | 662 |
1 files changed, 376 insertions, 286 deletions
diff --git a/drawinglayer/source/processor2d/vclprocessor2d.cxx b/drawinglayer/source/processor2d/vclprocessor2d.cxx index 9ef220c32cfb..0c5f70bb530b 100644 --- a/drawinglayer/source/processor2d/vclprocessor2d.cxx +++ b/drawinglayer/source/processor2d/vclprocessor2d.cxx @@ -23,9 +23,14 @@ #include "vclhelperbufferdevice.hxx" #include <cmath> #include <comphelper/string.hxx> +#include <comphelper/lok.hxx> #include <svtools/optionsdrawinglayer.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> @@ -90,6 +95,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 @@ -107,14 +135,14 @@ 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 @@ -123,25 +151,43 @@ void VclProcessor2D::RenderTextSimpleOrDecoratedPortionPrimitive2D( 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; + + 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 = 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()); - if (aFillColor != COL_TRANSPARENT) - { - aFont.SetFillColor(aFillColor); - aFont.SetTransparent(false); - } + aFont.SetTransparent(aFillColor.IsTransparent()); + aFont.SetFillColor(aFillColor); // handle additional font attributes const primitive2d::TextDecoratedPortionPrimitive2D* pTCPP = nullptr; @@ -248,27 +294,27 @@ void VclProcessor2D::RenderTextSimpleOrDecoratedPortionPrimitive2D( aFont.SetShadow(true); } - // create transformed integer DXArray in view coordinate system - std::vector<sal_Int32> 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(basegfx::fround(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 vcl::text::ComplexTextLayoutFlags nOldLayoutMode(mpOutputDevice->GetLayoutMode()); if (rTextCandidate.getFontAttribute().getRTL()) @@ -280,47 +326,127 @@ void VclProcessor2D::RenderTextSimpleOrDecoratedPortionPrimitive2D( mpOutputDevice->SetLayoutMode(nRTLLayoutMode); } - mpOutputDevice->SetFont(aFont); mpOutputDevice->SetTextColor(Color(aRGBFontColor)); OUString aText(rTextCandidate.getText()); sal_Int32 nPos = rTextCandidate.getTextPosition(); sal_Int32 nLen = rTextCandidate.getTextLength(); + // this contraption is used in editeng, with format paragraph used to + // set a tab with a tab-fill character if (rTextCandidate.isFilled()) { - basegfx::B2DVector aOldFontScaling, aOldTranslate; - double fOldRotate, fOldShearX; - rTextCandidate.getTextTransform().decompose(aOldFontScaling, aOldTranslate, - fOldRotate, fOldShearX); - - tools::Long nWidthToFill = static_cast<tools::Long>( - rTextCandidate.getWidthToFill() * aFontScaling.getX() / aOldFontScaling.getX()); + tools::Long nWidthToFill = rTextCandidate.getWidthToFill(); - tools::Long nWidth = mpOutputDevice->GetTextArray(rTextCandidate.getText(), - &aTransformedDXArray, 0, 1); - tools::Long nChars = 2; + tools::Long nWidth = basegfx::fround<tools::Long>( + mpOutputDevice->GetTextArray(rTextCandidate.getText(), &aDXArray, 0, 1)); + sal_Int32 nChars = 2; if (nWidth) nChars = nWidthToFill / nWidth; - OUStringBuffer aFilled; + OUStringBuffer aFilled(nChars); comphelper::string::padToLength(aFilled, nChars, aText[0]); aText = aFilled.makeStringAndClear(); nPos = 0; nLen = nChars; - if (!aTransformedDXArray.empty()) + if (!aDXArray.empty()) { - sal_Int32 nDX = aTransformedDXArray[0]; - aTransformedDXArray.resize(nLen); + sal_Int32 nDX = aDXArray[0]; + aDXArray.resize(nLen); for (sal_Int32 i = 1; i < nLen; ++i) - aTransformedDXArray[i] = aTransformedDXArray[i - 1] + nDX; + aDXArray.set(i, aDXArray[i - 1] + nDX); } } - if (!aTransformedDXArray.empty()) + Point aStartPoint; + bool bChangeMapMode(false); + if (!bScaleFont) { - mpOutputDevice->DrawTextArray(aStartPoint, aText, aTransformedDXArray, nPos, nLen); + 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; + } + + Fraction aScaleY(aCurrentScaling.getY()); + if (!aScaleY.IsValid()) + { + SAL_WARN("drawinglayer", "invalid Y Scale"); + return; + } + + MapMode aMapMode(mpOutputDevice->GetMapMode().GetMapUnit(), aOrigin, aScaleX, + aScaleY); + + if (fCurrentRotate) + aTextTranslate *= basegfx::utils::createRotateB2DHomMatrix(fCurrentRotate); + aStartPoint = Point(basegfx::fround<tools::Long>(aTextTranslate.getX()), + basegfx::fround<tools::Long>(aTextTranslate.getY())); + + bChangeMapMode = aMapMode != mpOutputDevice->GetMapMode(); + if (bChangeMapMode) + { + mpOutputDevice->Push(vcl::PushFlags::MAPMODE); + mpOutputDevice->SetRelativeMapMode(aMapMode); + } + } + else + { + const basegfx::B2DPoint aPoint(aLocalTransform * basegfx::B2DPoint(0.0, 0.0)); + double aPointX = aPoint.getX(), aPointY = aPoint.getY(); + + // 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()); + + 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); + + mpOutputDevice->Push(vcl::PushFlags::MAPMODE); + mpOutputDevice->SetRelativeMapMode(aMapMode); + bChangeMapMode = true; + + aPointX /= nFontScalingFixX; + aPointY /= nFontScalingFixY; + } + + aStartPoint = Point(basegfx::fround<tools::Long>(aPointX), + basegfx::fround<tools::Long>(aPointY)); + } + + // tdf#152990 set the font after the MapMode is (potentially) set so canvas uses the desired + // font size + mpOutputDevice->SetFont(aFont); + + if (!aDXArray.empty()) + { + const SalLayoutGlyphs* pGlyphs = SalLayoutGlyphsCache::self()->GetLayoutGlyphs( + mpOutputDevice, aText, nPos, nLen); + mpOutputDevice->DrawTextArray(aStartPoint, aText, aDXArray, + rTextCandidate.getKashidaArray(), nPos, nLen, + SalLayoutFlags::NONE, pGlyphs); } else { @@ -332,6 +458,9 @@ void VclProcessor2D::RenderTextSimpleOrDecoratedPortionPrimitive2D( mpOutputDevice->SetLayoutMode(nOldLayoutMode); } + if (bChangeMapMode) + mpOutputDevice->Pop(); + bPrimitiveAccepted = true; } } @@ -355,8 +484,7 @@ void VclProcessor2D::RenderPolygonHairlinePrimitive2D( basegfx::B2DPolygon aLocalPolygon(rPolygonCandidate.getB2DPolygon()); aLocalPolygon.transform(maCurrentTransformation); - if (bPixelBased && SvtOptionsDrawinglayer::IsAntiAliasing() - && SvtOptionsDrawinglayer::IsSnapHorVerLinesToDiscrete()) + if (bPixelBased && getViewInformation2D().getPixelSnapHairline()) { // #i98289# // when a Hairline is painted and AntiAliasing is on the option SnapHorVerLinesToDiscrete @@ -372,7 +500,7 @@ void VclProcessor2D::RenderPolygonHairlinePrimitive2D( // direct draw of transformed BitmapEx primitive void VclProcessor2D::RenderBitmapPrimitive2D(const primitive2d::BitmapPrimitive2D& rBitmapCandidate) { - BitmapEx aBitmapEx(VCLUnoHelper::GetBitmap(rBitmapCandidate.getXBitmap())); + BitmapEx aBitmapEx(rBitmapCandidate.getBitmap()); const basegfx::B2DHomMatrix aLocalTransform(maCurrentTransformation * rBitmapCandidate.getTransform()); @@ -406,243 +534,224 @@ void VclProcessor2D::RenderBitmapPrimitive2D(const primitive2d::BitmapPrimitive2 void VclProcessor2D::RenderFillGraphicPrimitive2D( const primitive2d::FillGraphicPrimitive2D& rFillBitmapCandidate) { + 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 (isPalettePixelFormat(aBitmapEx.getPixelFormat())) - { - 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); + BitmapEx aBitmapEx(rFillGraphicAttribute.getGraphic().GetBitmapEx()); + 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(aBitmapEx.getPixelFormat())) + { + aBitmapEx.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); + aBitmapEx.Scale(aNeededBitmapSizePixel, BmpScaleFlag::Interpolate); + } - nPosX -= nDiff; - nBLeft -= nDiff * nBWidth; - } + if (maBColorModifierStack.count()) + { + // when color modifier, apply to bitmap + aBitmapEx = aBitmapEx.ModifyBitmapEx(maBColorModifierStack); - if (nBLeft + nBWidth <= nOLeft) - { - const sal_Int32 nDiff(-nBLeft / nBWidth); + // ModifyBitmapEx 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); - nPosX += nDiff; - nBLeft += nDiff * nBWidth; - } + mpOutputDevice->SetFillColor(Color(aModifiedColor)); + mpOutputDevice->SetLineColor(); + mpOutputDevice->DrawPolygon(aPolygon); - if (nBTop > nOTop) - { - const sal_Int32 nDiff((nBTop / nBHeight) + 1); + return true; + } + } - nPosY -= nDiff; - nBTop -= nDiff * 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); - if (nBTop + nBHeight <= nOTop) - { - const sal_Int32 nDiff(-nBTop / nBHeight); + if (nBLeft > nOLeft) + { + const sal_Int32 nDiff((nBLeft / nBWidth) + 1); - nPosY += nDiff; - nBTop += nDiff * nBHeight; - } + nPosX -= nDiff; + nBLeft -= nDiff * nBWidth; + } - // prepare OutDev - const Point aEmptyPoint(0, 0); - const ::tools::Rectangle aVisiblePixel( - aEmptyPoint, mpOutputDevice->GetOutputSizePixel()); - const bool bWasEnabled(mpOutputDevice->IsMapModeEnabled()); - mpOutputDevice->EnableMapMode(false); + if (nBLeft + nBWidth <= nOLeft) + { + const sal_Int32 nDiff(-nBLeft / nBWidth); - // check if offset is used - const sal_Int32 nOffsetX( - basegfx::fround(rFillGraphicAttribute.getOffsetX() * nBWidth)); + nPosX += nDiff; + nBLeft += nDiff * 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.Overlaps(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)); + if (nBTop > nOTop) + { + const sal_Int32 nDiff((nBTop / nBHeight) + 1); - // 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(), aBitmapEx); - } - else - { - mpOutputDevice->DrawBitmapEx( - aOutRectPixel.TopLeft(), aNeededBitmapSizePixel, - aBitmapEx); - } - } - } - } - } + nPosY -= nDiff; + nBTop -= nDiff * nBHeight; + } - // restore OutDev - mpOutputDevice->EnableMapMode(bWasEnabled); - } + 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)); + + 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(), aBitmapEx); + } + else + { + mpOutputDevice->DrawBitmapEx(aOutRectPixel.TopLeft(), + aNeededBitmapSizePixel, aBitmapEx); } } } } } - - if (!bPrimitiveAccepted) + else { - // do not accept, use decomposition - process(rFillBitmapCandidate); + // check if offset is used + const sal_Int32 nOffsetY(basegfx::fround(rFillGraphicAttribute.getOffsetY() * nBHeight)); + + // 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(), aBitmapEx); + } + else + { + mpOutputDevice->DrawBitmapEx(aOutRectPixel.TopLeft(), + aNeededBitmapSizePixel, aBitmapEx); + } + } + } + } } + + // restore OutDev + mpOutputDevice->EnableMapMode(bWasEnabled); + return true; } // direct draw of Graphic @@ -774,7 +883,7 @@ void VclProcessor2D::RenderMaskPrimitive2DPixel(const primitive2d::MaskPrimitive aMask.transform(maCurrentTransformation); // Unless smooth edges are needed, simply use clipping. - if (basegfx::utils::isRectangle(aMask) || !SvtOptionsDrawinglayer::IsAntiAliasing()) + if (basegfx::utils::isRectangle(aMask) || !getViewInformation2D().getUseAntiAliasing()) { mpOutputDevice->Push(vcl::PushFlags::CLIPREGION); mpOutputDevice->IntersectClipRegion(vcl::Region(aMask)); @@ -890,7 +999,7 @@ void VclProcessor2D::RenderTransparencePrimitive2D( process(rTransCandidate.getTransparence()); // back to old color stack - maBColorModifierStack = aLastBColorModifierStack; + maBColorModifierStack = std::move(aLastBColorModifierStack); // back to old OutDev mpOutputDevice = pLastOutputDevice; @@ -910,10 +1019,9 @@ 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()); + geometry::ViewInformation2D aViewInformation2D(getViewInformation2D()); + aViewInformation2D.setObjectTransformation(getViewInformation2D().getObjectTransformation() + * rTransformCandidate.getTransformation()); updateViewInformation(aViewInformation2D); // process content @@ -932,10 +1040,8 @@ 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()); + geometry::ViewInformation2D aViewInformation2D(getViewInformation2D()); + aViewInformation2D.setVisualizedPage(rPagePreviewCandidate.getXDrawPage()); updateViewInformation(aViewInformation2D); // process decomposed content @@ -981,8 +1087,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); } @@ -1002,8 +1108,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); } @@ -1018,7 +1124,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)); @@ -1049,7 +1155,7 @@ void VclProcessor2D::RenderPolygonStrokePrimitive2D( if (nCount) { - const bool bAntiAliased(SvtOptionsDrawinglayer::IsAntiAliasing()); + const bool bAntiAliased(getViewInformation2D().getUseAntiAliasing()); aHairlinePolyPolygon.transform(maCurrentTransformation); if (bAntiAliased) @@ -1262,26 +1368,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())); @@ -1291,7 +1383,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)); @@ -1325,7 +1417,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())); @@ -1335,7 +1427,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( @@ -1476,13 +1568,11 @@ void VclProcessor2D::adaptTextToFillDrawMode() const // process support VclProcessor2D::VclProcessor2D(const geometry::ViewInformation2D& rViewInformation, - OutputDevice& rOutDev, - const basegfx::BColorModifierStack& rInitStack) + OutputDevice& rOutDev, basegfx::BColorModifierStack aInitStack) : BaseProcessor2D(rViewInformation) , mpOutputDevice(&rOutDev) - , maBColorModifierStack(rInitStack) + , maBColorModifierStack(std::move(aInitStack)) , mnPolygonStrokePrimitive2D(0) - , mpObjectInfoPrimitive2D(nullptr) { // set digit language, derived from SvtCTLOptions to have the correct // number display for arabic/hindi numerals |