From f65cbae8947c0462b35c8d3c3d5124e431b100b6 Mon Sep 17 00:00:00 2001 From: Armin Le Grand Date: Tue, 4 Jul 2017 16:28:58 +0200 Subject: borderline: correct problems with border display Borderline display with direct paint and with primitive direct paint has quite some errors in the current state. Started to unify usages, check deeper with creation/usage. borderline: deep changes to BorderLine Found basic error in determining the offset values for BorderLinePrimitive creation, these were not centered on the lines. Corrected that. This makes it possible to remove the formally used clipping which seems to have been used to correct that. Also allows to go back to a 'normal' decomposition that creates line primitives as expected. That again can then be painted quite normally. Also added view-dependent case to the decomposition to guarantee a gap of one discrete unit (pixel). Removed the direct painter, too. Checked and corrected stroking. borderline: Adapted previews to primitives Added code to use the primitive representation in all dialogs and apps using tables. The edit views use these mostly, so the preview should do that, too. Currently missing is a good visualization of diagonals, but this is also true for edit views. Checked all apps and table usages to not get worse borderline: correct line dash visualization When a dashed line is used, a factor of 10.0 was applied in the original coded, added that. Also the orientation of vertical borders was inverted since it was simpler to exchange Start/End, but this also mirrors the line dash visualisation, corrected that Change-Id: I4c1b380a76cb37389fab1259a53fb7cc9da982d1 e95e246d5563360617a2a2213e4d5ec7d0e736b9 62369b4de58fb0264aeb710ec6983ceddca5701d 77418cc6c84ebb0632f8c3448976e82ce612d6b6 b4eb28dc86ce05eb89b26517167305b994158ef8 borderline: adapt cppunittest and clang --- drawinglayer/Library_drawinglayer.mk | 1 - drawinglayer/qa/unit/border.cxx | 47 +-- .../source/primitive2d/borderlineprimitive2d.cxx | 389 ++++++++------------ .../primitive2d/clippedborderlineprimitive2d.cxx | 64 ---- .../source/processor2d/vclpixelprocessor2d.cxx | 395 +-------------------- .../source/processor2d/vclpixelprocessor2d.hxx | 1 - 6 files changed, 180 insertions(+), 717 deletions(-) delete mode 100644 drawinglayer/source/primitive2d/clippedborderlineprimitive2d.cxx (limited to 'drawinglayer') diff --git a/drawinglayer/Library_drawinglayer.mk b/drawinglayer/Library_drawinglayer.mk index 8c431c8ec08f..32daccd9d43e 100644 --- a/drawinglayer/Library_drawinglayer.mk +++ b/drawinglayer/Library_drawinglayer.mk @@ -67,7 +67,6 @@ $(eval $(call gb_Library_add_exception_objects,drawinglayer,\ drawinglayer/source/primitive2d/baseprimitive2d \ drawinglayer/source/primitive2d/bitmapprimitive2d \ drawinglayer/source/primitive2d/borderlineprimitive2d \ - drawinglayer/source/primitive2d/clippedborderlineprimitive2d \ drawinglayer/source/primitive2d/controlprimitive2d \ drawinglayer/source/primitive2d/cropprimitive2d \ drawinglayer/source/primitive2d/discretebitmapprimitive2d \ diff --git a/drawinglayer/qa/unit/border.cxx b/drawinglayer/qa/unit/border.cxx index 25264c46ee51..ce99965b13f9 100644 --- a/drawinglayer/qa/unit/border.cxx +++ b/drawinglayer/qa/unit/border.cxx @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -70,17 +71,15 @@ void DrawinglayerBorderTest::testDoubleDecompositionSolid() // Make sure it results in two borders as it's a double one. CPPUNIT_ASSERT_EQUAL(static_cast(2), aContainer.size()); - // Get the inside line. - auto pInside = dynamic_cast(aContainer[0].get()); + // Get the inside line, now a PolygonStrokePrimitive2D + auto pInside = dynamic_cast(aContainer[0].get()); CPPUNIT_ASSERT(pInside); // Make sure the inside line's height is fLeftWidth. - const basegfx::B2DPolyPolygon& rPolyPolygon = pInside->getB2DPolyPolygon(); - CPPUNIT_ASSERT_EQUAL(static_cast(1), rPolyPolygon.count()); - const basegfx::B2DPolygon& rPolygon = rPolyPolygon.getB2DPolygon(0); - const basegfx::B2DRange& rRange = rPolygon.getB2DRange(); + const double fLineWidthFromDecompose = pInside->getLineAttribute().getWidth(); + // This was 2.47, i.e. the width of the inner line was 1 unit (in the bugdoc's case: 1 pixel) wider than expected. - CPPUNIT_ASSERT_DOUBLES_EQUAL(fLeftWidth, rRange.getHeight(), basegfx::fTools::getSmallValue()); + CPPUNIT_ASSERT_DOUBLES_EQUAL(fLeftWidth, fLineWidthFromDecompose, basegfx::fTools::getSmallValue()); } void DrawinglayerBorderTest::testDoublePixelProcessing() @@ -100,7 +99,7 @@ void DrawinglayerBorderTest::testDoublePixelProcessing() basegfx::B2DPoint aEnd(100, 20); double const fLeftWidth = 1.47; double const fDistance = 1.47; - double fRightWidth = 1.47; + double const fRightWidth = 1.47; double const fExtendLeftStart = 0; double const fExtendLeftEnd = 0; double const fExtendRightStart = 0; @@ -117,31 +116,33 @@ void DrawinglayerBorderTest::testDoublePixelProcessing() // Process the primitives. pProcessor->process(aPrimitives); - // Now assert the height of the outer (second) border polygon. + // Double line now gets decomposed in Metafile to painting four lines + // with width == 0 in a cross pattern due to real line width being between + // 1.0 and 2.0. Count created lines aMetaFile.Stop(); aMetaFile.WindStart(); - bool bFirst = true; - sal_Int32 nHeight = 0; + sal_uInt32 nPolyLineActionCount = 0; + for (std::size_t nAction = 0; nAction < aMetaFile.GetActionSize(); ++nAction) { MetaAction* pAction = aMetaFile.GetAction(nAction); - if (pAction->GetType() == MetaActionType::POLYPOLYGON) + + if (MetaActionType::POLYLINE == pAction->GetType()) { - if (bFirst) + auto pMPLAction = static_cast(pAction); + + if (0 == pMPLAction->GetLineInfo().GetWidth() && LineStyle::Solid == pMPLAction->GetLineInfo().GetStyle()) { - bFirst = false; - continue; + nPolyLineActionCount++; } - - auto pMPPAction = static_cast(pAction); - const tools::PolyPolygon& rPolyPolygon = pMPPAction->GetPolyPolygon(); - nHeight = rPolyPolygon.GetBoundRect().getHeight(); } } - sal_Int32 nExpectedHeight = std::round(fRightWidth); - // This was 2, and should be 1: if the logical requested width is 1.47, - // then that must be 1 px on the screen, not 2. - CPPUNIT_ASSERT_EQUAL(nExpectedHeight, nHeight); + + // Check if all eight (2x four) simple lines with width == 0 and + // solid were created + const sal_uInt32 nExpectedNumPolyLineActions = 8; + + CPPUNIT_ASSERT_EQUAL(nExpectedNumPolyLineActions, nPolyLineActionCount); } CPPUNIT_TEST_SUITE_REGISTRATION(DrawinglayerBorderTest); diff --git a/drawinglayer/source/primitive2d/borderlineprimitive2d.cxx b/drawinglayer/source/primitive2d/borderlineprimitive2d.cxx index c6ea4a5c41f7..9735cc1aa44d 100644 --- a/drawinglayer/source/primitive2d/borderlineprimitive2d.cxx +++ b/drawinglayer/source/primitive2d/borderlineprimitive2d.cxx @@ -42,282 +42,138 @@ T round(T x) } #endif -namespace drawinglayer { - -namespace { - -void moveLine(basegfx::B2DPolygon& rPoly, double fGap, const basegfx::B2DVector& rVector) -{ - if (basegfx::fTools::equalZero(rVector.getX())) - { - basegfx::B2DHomMatrix aMat(1, 0, fGap, 0, 1, 0); - rPoly.transform(aMat); - } - else if (basegfx::fTools::equalZero(rVector.getY())) - { - basegfx::B2DHomMatrix aMat(1, 0, 0, 0, 1, fGap); - rPoly.transform(aMat); - } -} - -primitive2d::Primitive2DReference makeHairLinePrimitive( - const basegfx::B2DPoint& rStart, const basegfx::B2DPoint& rEnd, const basegfx::B2DVector& rVector, - const basegfx::BColor& rColor, double fGap) -{ - basegfx::B2DPolygon aPolygon; - aPolygon.append(rStart); - aPolygon.append(rEnd); - moveLine(aPolygon, fGap, rVector); - - return primitive2d::Primitive2DReference(new primitive2d::PolygonHairlinePrimitive2D(aPolygon, rColor)); -} - -primitive2d::Primitive2DReference makeSolidLinePrimitive( - const basegfx::B2DPolyPolygon& rClipRegion, const basegfx::B2DPoint& rStart, const basegfx::B2DPoint& rEnd, - const basegfx::B2DVector& rVector, const basegfx::BColor& rColor, double fLineWidth, double fGap) +namespace drawinglayer { - const basegfx::B2DVector aPerpendicular = basegfx::getPerpendicular(rVector); - const basegfx::B2DVector aLineWidthOffset = (fLineWidth * 0.5) * aPerpendicular; - - basegfx::B2DPolygon aPolygon; - aPolygon.append(rStart + aLineWidthOffset); - aPolygon.append(rEnd + aLineWidthOffset); - aPolygon.append(rEnd - aLineWidthOffset); - aPolygon.append(rStart - aLineWidthOffset); - aPolygon.setClosed(true); - - moveLine(aPolygon, fGap, rVector); - - basegfx::B2DPolyPolygon aClipped = - basegfx::tools::clipPolygonOnPolyPolygon(aPolygon, rClipRegion, true, false); - - if (aClipped.count()) - aPolygon = aClipped.getB2DPolygon(0); - - return primitive2d::Primitive2DReference( - new primitive2d::PolyPolygonColorPrimitive2D(basegfx::B2DPolyPolygon(aPolygon), rColor)); -} - -} - - // fdo#49438: heuristic pseudo hack - static bool lcl_UseHairline(double const fW, - basegfx::B2DPoint const& rStart, basegfx::B2DPoint const& rEnd, - geometry::ViewInformation2D const& rViewInformation) - { - basegfx::B2DTuple scale; - basegfx::B2DTuple translation; - double fRotation; - double fShear; - rViewInformation.getObjectToViewTransformation().decompose( - scale, translation, fRotation, fShear); - double const fScale( - (rEnd.getX() - rStart.getX() > rEnd.getY() - rStart.getY()) - ? scale.getY() : scale.getX()); - return (fW * fScale < 0.51); - } - - static double lcl_GetCorrectedWidth(double const fW, - basegfx::B2DPoint const& rStart, basegfx::B2DPoint const& rEnd, - geometry::ViewInformation2D const& rViewInformation) - { - return (lcl_UseHairline(fW, rStart, rEnd, rViewInformation)) ? 0.0 : fW; - } - namespace primitive2d { - double BorderLinePrimitive2D::getWidth( - geometry::ViewInformation2D const& rViewInformation) const - { - return lcl_GetCorrectedWidth(mfLeftWidth, getStart(), getEnd(), - rViewInformation) - + lcl_GetCorrectedWidth(mfDistance, getStart(), getEnd(), - rViewInformation) - + lcl_GetCorrectedWidth(mfRightWidth, getStart(), getEnd(), - rViewInformation); - } - - basegfx::B2DPolyPolygon BorderLinePrimitive2D::getClipPolygon( - geometry::ViewInformation2D const& rViewInformation) const + // helper to add a centered, maybe stroked line primitive to rContainer + void addPolygonStrokePrimitive2D( + Primitive2DContainer& rContainer, + const basegfx::B2DPoint& rStart, + const basegfx::B2DPoint& rEnd, + const basegfx::BColor& rColor, + double fWidth, + SvxBorderLineStyle aStyle, + double fPatternScale) { - basegfx::B2DPolygon clipPolygon; - - // Get the vectors - basegfx::B2DVector aVector( getEnd() - getStart() ); - aVector.normalize(); - const basegfx::B2DVector aPerpendicular(basegfx::getPerpendicular(aVector)); - - // Get the points - const double fWidth(getWidth(rViewInformation)); - const basegfx::B2DVector aLeftOff( - aPerpendicular * (-0.5 * std::max(fWidth, 1.0))); - const basegfx::B2DVector aRightOff( - aPerpendicular * (0.5 * std::max(fWidth, 1.0))); + basegfx::B2DPolygon aPolygon; - const basegfx::B2DVector aSLVector( aLeftOff - ( getExtendLeftStart() * aVector ) ); - clipPolygon.append( basegfx::B2DPoint( getStart() + aSLVector * 2.0 ) ); + aPolygon.append(rStart); + aPolygon.append(rEnd); - clipPolygon.append( getStart( ) ); + const attribute::LineAttribute aLineAttribute(rColor, fWidth); + static double fPatScFact(10.0); // 10.0 multiply, see old code + const std::vector aDashing(svtools::GetLineDashing(aStyle, fPatternScale * fPatScFact)); - const basegfx::B2DVector aSRVector( aRightOff - ( getExtendRightStart() * aVector ) ); - clipPolygon.append( basegfx::B2DPoint( getStart() + aSRVector * 2.0 ) ); - - const basegfx::B2DVector aERVector( aRightOff + ( getExtendRightEnd() * aVector ) ); - clipPolygon.append( basegfx::B2DPoint( getEnd() + aERVector * 2.0 ) ); - - clipPolygon.append( getEnd( ) ); - - const basegfx::B2DVector aELVector( aLeftOff + ( getExtendLeftEnd() * aVector ) ); - clipPolygon.append( basegfx::B2DPoint( getEnd() + aELVector * 2.0 ) ); - - clipPolygon.setClosed( true ); - - return basegfx::B2DPolyPolygon( clipPolygon ); - } + if (aDashing.empty()) + { + rContainer.push_back( + new PolygonStrokePrimitive2D( + aPolygon, + aLineAttribute)); + } + else + { + const attribute::StrokeAttribute aStrokeAttribute(aDashing); - void BorderLinePrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& rViewInformation) const - { - createDecomposition(rContainer, rViewInformation, false); + rContainer.push_back( + new PolygonStrokePrimitive2D( + aPolygon, + aLineAttribute, + aStrokeAttribute)); + } } - void BorderLinePrimitive2D::createDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& rViewInformation, bool bPixelCorrection) const + void BorderLinePrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const { - if(!getStart().equal(getEnd()) && ( isInsideUsed() || isOutsideUsed() ) ) + if (!getStart().equal(getEnd()) && (isInsideUsed() || isOutsideUsed())) { // get data and vectors basegfx::B2DVector aVector(getEnd() - getStart()); aVector.normalize(); const basegfx::B2DVector aPerpendicular(basegfx::getPerpendicular(aVector)); - const basegfx::B2DPolyPolygon& aClipRegion = - getClipPolygon(rViewInformation); - - if(isOutsideUsed() && isInsideUsed()) + if (isOutsideUsed() && isInsideUsed()) { - const double fExt = getWidth(rViewInformation); // Extend a lot: it'll be clipped later. - const basegfx::B2DPoint aTmpStart(getStart() - (fExt * aVector)); - const basegfx::B2DPoint aTmpEnd(getEnd() + (fExt * aVector)); - - double fLeftWidth = getLeftWidth(); - bool bLeftHairline = lcl_UseHairline(fLeftWidth, getStart(), getEnd(), rViewInformation); - if (bLeftHairline) - fLeftWidth = 0.0; - - double fRightWidth = getRightWidth(); - bool bRightHairline = lcl_UseHairline(fRightWidth, getStart(), getEnd(), rViewInformation); - if (bRightHairline) - fRightWidth = 0.0; - - // "inside" line - - if (bLeftHairline) - rContainer.push_back(makeHairLinePrimitive( - getStart(), getEnd(), aVector, getRGBColorLeft(), 0.0)); - else + // double line with gap. Use mfDiscreteDistance (see get2DDecomposition) as distance. + // That value is prepared to be at least one pixel (discrete unit) so that the + // decomposition is view-dependent in this cases + if (isInsideUsed()) { - double fWidth = bPixelCorrection ? std::round(fLeftWidth) : fLeftWidth; - rContainer.push_back(makeSolidLinePrimitive( - aClipRegion, aTmpStart, aTmpEnd, aVector, getRGBColorLeft(), fWidth, -fLeftWidth/2.0)); + // inside line (left). Create stroke primitive centered on line width + const double fDeltaY((mfDiscreteDistance + getLeftWidth()) * 0.5); + const basegfx::B2DVector aDeltaY(aPerpendicular * fDeltaY); + const basegfx::B2DPoint aStart(getStart() - (aVector * getExtendLeftStart()) - aDeltaY); + const basegfx::B2DPoint aEnd(getEnd() + (aVector * getExtendLeftEnd()) - aDeltaY); + + addPolygonStrokePrimitive2D( + rContainer, + aStart, + aEnd, + getRGBColorLeft(), + getLeftWidth(), + getStyle(), + getPatternScale()); } - // "outside" line - - if (bRightHairline) - rContainer.push_back(makeHairLinePrimitive( - getStart(), getEnd(), aVector, getRGBColorRight(), fLeftWidth+mfDistance)); - else + if (hasGapColor() && isDistanceUsed()) { - double fWidth = bPixelCorrection ? std::round(fRightWidth) : fRightWidth; - rContainer.push_back(makeSolidLinePrimitive( - aClipRegion, aTmpStart, aTmpEnd, aVector, getRGBColorRight(), fWidth, mfDistance+fRightWidth/2.0)); + // gap (if visible, found no practicval usage). + // Create stroke primitive on vector with given color + addPolygonStrokePrimitive2D( + rContainer, + getStart(), + getEnd(), + getRGBColorGap(), + mfDiscreteDistance, + getStyle(), + getPatternScale()); } - } - else - { - // single line, create geometry - basegfx::B2DPolygon aPolygon; - const double fExt = getWidth(rViewInformation); // Extend a lot: it'll be clipped after - const basegfx::B2DPoint aTmpStart(getStart() - (fExt * aVector)); - const basegfx::B2DPoint aTmpEnd(getEnd() + (fExt * aVector)); - // Get which is the line to show - bool bIsSolidline = mnStyle == SvxBorderLineStyle::SOLID; - double nWidth = getLeftWidth(); - basegfx::BColor aColor = getRGBColorLeft(); - if ( basegfx::fTools::equal( 0.0, mfLeftWidth ) ) + if (isOutsideUsed()) { - nWidth = getRightWidth(); - aColor = getRGBColorRight(); + // outside line (right). Create stroke primitive centered on line width + const double fDeltaY((mfDiscreteDistance + getRightWidth()) * 0.5); + const basegfx::B2DVector aDeltaY(aPerpendicular * fDeltaY); + const basegfx::B2DPoint aStart(getStart() - (aVector * getExtendRightStart()) + aDeltaY); + const basegfx::B2DPoint aEnd(getEnd() + (aVector * getExtendRightEnd()) + aDeltaY); + + addPolygonStrokePrimitive2D( + rContainer, + aStart, + aEnd, + getRGBColorRight(), + getRightWidth(), + getStyle(), + getPatternScale()); } - bool const bIsHairline = lcl_UseHairline( - nWidth, getStart(), getEnd(), rViewInformation); - nWidth = lcl_GetCorrectedWidth(nWidth, - getStart(), getEnd(), rViewInformation); - - if(bIsHairline && bIsSolidline) - { - // create hairline primitive - aPolygon.append( getStart() ); - aPolygon.append( getEnd() ); - - rContainer.push_back(new PolygonHairlinePrimitive2D( - aPolygon, - aColor)); - } - else - { - // create filled polygon primitive - const basegfx::B2DVector aLineWidthOffset(((nWidth + 1) * 0.5) * aPerpendicular); - - aPolygon.append( aTmpStart ); - aPolygon.append( aTmpEnd ); - - basegfx::B2DPolyPolygon aDashed = - svtools::ApplyLineDashing(aPolygon, getStyle(), mfPatternScale*10.0); - - for (sal_uInt32 i = 0; i < aDashed.count(); i++ ) - { - basegfx::B2DPolygon aDash = aDashed.getB2DPolygon( i ); - basegfx::B2DPoint aDashStart = aDash.getB2DPoint( 0 ); - basegfx::B2DPoint aDashEnd = aDash.getB2DPoint( aDash.count() - 1 ); - - basegfx::B2DPolygon aDashPolygon; - aDashPolygon.append( aDashStart + aLineWidthOffset ); - aDashPolygon.append( aDashEnd + aLineWidthOffset ); - aDashPolygon.append( aDashEnd - aLineWidthOffset ); - aDashPolygon.append( aDashStart - aLineWidthOffset ); - aDashPolygon.setClosed( true ); - - basegfx::B2DPolyPolygon aClipped = basegfx::tools::clipPolygonOnPolyPolygon( - aDashPolygon, aClipRegion, true, false ); + } + else if(isInsideUsed()) + { + // single line, only inside values used, no vertical offsets + addPolygonStrokePrimitive2D( + rContainer, + getStart(), + getEnd(), + getRGBColorLeft(), + getLeftWidth(), + getStyle(), + getPatternScale()); + } + } + } - if ( aClipped.count() ) - aDashed.setB2DPolygon( i, aClipped.getB2DPolygon( 0 ) ); - } + bool BorderLinePrimitive2D::isHorizontalOrVertical(const geometry::ViewInformation2D& rViewInformation) const + { + if (!getStart().equal(getEnd())) + { + const basegfx::B2DHomMatrix& rOTVT = rViewInformation.getObjectToViewTransformation(); + const basegfx::B2DVector aVector(rOTVT * getEnd() - rOTVT * getStart()); - sal_uInt32 n = aDashed.count(); - for (sal_uInt32 i = 0; i < n; ++i) - { - basegfx::B2DPolygon aDash = aDashed.getB2DPolygon(i); - if (bIsHairline) - { - // Convert a rectangular polygon into a line. - basegfx::B2DPolygon aDash2; - basegfx::B2DRange aRange = aDash.getB2DRange(); - aDash2.append(basegfx::B2DPoint(aRange.getMinX(), aRange.getMinY())); - aDash2.append(basegfx::B2DPoint(aRange.getMaxX(), aRange.getMinY())); - rContainer.push_back( - new PolygonHairlinePrimitive2D(aDash2, aColor)); - } - else - { - rContainer.push_back( - new PolyPolygonColorPrimitive2D(basegfx::B2DPolyPolygon(aDash), aColor)); - } - } - } - } + return basegfx::fTools::equalZero(aVector.getX()) || basegfx::fTools::equalZero(aVector.getY()); } + + return false; } BorderLinePrimitive2D::BorderLinePrimitive2D( @@ -351,7 +207,8 @@ primitive2d::Primitive2DReference makeSolidLinePrimitive( maRGBColorGap(rRGBColorGap), mbHasGapColor(bHasGapColor), mnStyle(nStyle), - mfPatternScale(fPatternScale) + mfPatternScale(fPatternScale), + mfDiscreteDistance(0.0) { } @@ -381,6 +238,42 @@ primitive2d::Primitive2DReference makeSolidLinePrimitive( return false; } + void BorderLinePrimitive2D::get2DDecomposition(Primitive2DDecompositionVisitor& rVisitor, const geometry::ViewInformation2D& rViewInformation) const + { + ::osl::MutexGuard aGuard(m_aMutex); + + if (!getStart().equal(getEnd()) && isOutsideUsed() && isInsideUsed()) + { + // Double line with gap. In this case, we want to be view-dependent. + // Get the current DiscreteUnit, look at X and Y and use the maximum + const basegfx::B2DVector aDiscreteVector(rViewInformation.getInverseObjectToViewTransformation() * basegfx::B2DVector(1.0, 1.0)); + const double fDiscreteUnit(std::min(fabs(aDiscreteVector.getX()), fabs(aDiscreteVector.getY()))); + + // When discrete unit is bigger than distance (distance is less than one pixel), + // force distance to one pixel. Or expressed different, do not let the distance + // get smaller than one pixel. This is done for screen rendering and compatibility. + // This can also be done using DiscreteMetricDependentPrimitive2D as base class + // for this class, but specialization is better here for later buffering (only + // do this when 'double line with gap') + const double fNewDiscreteDistance(std::max(fDiscreteUnit, getDistance())); + + if (!rtl::math::approxEqual(fNewDiscreteDistance, mfDiscreteDistance)) + { + if (!getBuffered2DDecomposition().empty()) + { + // conditions of last local decomposition have changed, delete + const_cast< BorderLinePrimitive2D* >(this)->setBuffered2DDecomposition(Primitive2DContainer()); + } + + // remember value for usage in create2DDecomposition + const_cast< BorderLinePrimitive2D* >(this)->mfDiscreteDistance = fNewDiscreteDistance; + } + } + + // call base implementation + BufferedDecompositionPrimitive2D::get2DDecomposition(rVisitor, rViewInformation); + } + // provide unique ID ImplPrimitive2DIDBlock(BorderLinePrimitive2D, PRIMITIVE2D_ID_BORDERLINEPRIMITIVE2D) diff --git a/drawinglayer/source/primitive2d/clippedborderlineprimitive2d.cxx b/drawinglayer/source/primitive2d/clippedborderlineprimitive2d.cxx deleted file mode 100644 index ca44a2838909..000000000000 --- a/drawinglayer/source/primitive2d/clippedborderlineprimitive2d.cxx +++ /dev/null @@ -1,64 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* - * This file is part of the LibreOffice project. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ - -#include -#include - -namespace drawinglayer -{ - namespace primitive2d - { - basegfx::B2DPolyPolygon ClippedBorderLinePrimitive2D::getClipPolygon( - SAL_UNUSED_PARAMETER geometry::ViewInformation2D const&) const - { - basegfx::B2DPolyPolygon aPolyPolygon; - aPolyPolygon.append( maIntersection ); - return aPolyPolygon; - } - - ClippedBorderLinePrimitive2D::ClippedBorderLinePrimitive2D( - const basegfx::B2DPoint& rStart, - const basegfx::B2DPoint& rEnd, - double fLeftWidth, - double fDistance, - double fRightWidth, - const basegfx::B2DPolygon& rIntersection, - const basegfx::BColor& rRGBColorRight, - const basegfx::BColor& rRGBColorLeft, - const basegfx::BColor& rRGBColorGap, - bool bHasGapColor, - SvxBorderLineStyle nStyle, - double fPatternScale) - : BorderLinePrimitive2D( rStart, rEnd, fLeftWidth,fDistance, fRightWidth, - 0.0, 0.0, 0.0, 0.0, rRGBColorRight, rRGBColorLeft, - rRGBColorGap, bHasGapColor, nStyle, fPatternScale), - maIntersection( rIntersection ) - { - } - - bool ClippedBorderLinePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const - { - if(BorderLinePrimitive2D::operator==(rPrimitive)) - { - const ClippedBorderLinePrimitive2D& rCompare = static_cast(rPrimitive); - - return maIntersection == rCompare.maIntersection; - } - - return false; - } - - // provide unique ID - ImplPrimitive2DIDBlock(ClippedBorderLinePrimitive2D, PRIMITIVE2D_ID_CLIPPEDBORDERLINEPRIMITIVE2D) - - - } // namespace primitive2d -} // namespace drawinglayer - -/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/drawinglayer/source/processor2d/vclpixelprocessor2d.cxx b/drawinglayer/source/processor2d/vclpixelprocessor2d.cxx index 4b5e45c7ef04..a93f85df4595 100644 --- a/drawinglayer/source/processor2d/vclpixelprocessor2d.cxx +++ b/drawinglayer/source/processor2d/vclpixelprocessor2d.cxx @@ -70,34 +70,6 @@ T round(T x) using namespace com::sun::star; -namespace { - -basegfx::B2DPolygon makeRectPolygon( double fX, double fY, double fW, double fH ) -{ - basegfx::B2DPolygon aPoly; - aPoly.append(basegfx::B2DPoint(fX, fY)); - aPoly.append(basegfx::B2DPoint(fX+fW, fY)); - aPoly.append(basegfx::B2DPoint(fX+fW, fY+fH)); - aPoly.append(basegfx::B2DPoint(fX, fY+fH)); - aPoly.setClosed(true); - return aPoly; -} - -void drawHairLine( - OutputDevice* pOutDev, double fX1, double fY1, double fX2, double fY2, - const basegfx::BColor& rColor ) -{ - basegfx::B2DPolygon aTarget; - aTarget.append(basegfx::B2DPoint(fX1, fY1)); - aTarget.append(basegfx::B2DPoint(fX2, fY2)); - - pOutDev->SetFillColor(); - pOutDev->SetLineColor(Color(rColor)); - pOutDev->DrawPolyLine(aTarget); -} - -} - namespace drawinglayer { namespace processor2d @@ -306,341 +278,6 @@ namespace drawinglayer return bTryWorked; } - bool VclPixelProcessor2D::tryDrawBorderLinePrimitive2DDirect( - const drawinglayer::primitive2d::BorderLinePrimitive2D& rSource) - { - const basegfx::B2DPoint& rS = rSource.getStart(); - const basegfx::B2DPoint& rE = rSource.getEnd(); - - double fX1 = rS.getX(); - double fY1 = rS.getY(); - double fX2 = rE.getX(); - double fY2 = rE.getY(); - - bool bHorizontal = false; - if (fX1 == fX2) - { - // Vertical line. - } - else if (fY1 == fY2) - { - // Horizontal line. - bHorizontal = true; - } - else - // Neither. Bail out. - return false; - - switch (rSource.getStyle()) - { - case SvxBorderLineStyle::SOLID: - case SvxBorderLineStyle::DOUBLE_THIN: - { - const basegfx::BColor aLineColor = - maBColorModifierStack.getModifiedColor(rSource.getRGBColorLeft()); - double nThick = rtl::math::round(rSource.getLeftWidth()); - - bool bDouble = rSource.getStyle() == SvxBorderLineStyle::DOUBLE_THIN; - - basegfx::B2DPolygon aTarget; - - if (bHorizontal) - { - // Horizontal line. Draw it as a rectangle. - - aTarget = makeRectPolygon(fX1, fY1, fX2-fX1, nThick); - aTarget.transform(maCurrentTransformation); - - basegfx::B2DRange aRange = aTarget.getB2DRange(); - double fH = aRange.getHeight(); - - if (bDouble) - { - // Double line - drawHairLine( - mpOutputDevice, aRange.getMinX(), aRange.getMinY()-1.0, aRange.getMaxX(), aRange.getMinY()-1.0, - aLineColor); - - drawHairLine( - mpOutputDevice, aRange.getMinX(), aRange.getMinY()+1.0, aRange.getMaxX(), aRange.getMinY()+1.0, - aLineColor); - - return true; - } - - if (fH <= 1.0) - { - // Draw it as a line. - drawHairLine( - mpOutputDevice, aRange.getMinX(), aRange.getMinY(), aRange.getMaxX(), aRange.getMinY(), - aLineColor); - - return true; - } - - double fOffset = rtl::math::round(fH/2.0, 0, rtl_math_RoundingMode_Down); - if (fOffset != 0.0) - { - // Move it up a bit to align it vertically centered. - basegfx::B2DHomMatrix aMat; - aMat.set(1, 2, -fOffset); - aTarget.transform(aMat); - } - } - else - { - // Vertical line. Draw it as a rectangle. - - aTarget = makeRectPolygon(fX1, fY1, nThick, fY2-fY1); - aTarget.transform(maCurrentTransformation); - - basegfx::B2DRange aRange = aTarget.getB2DRange(); - double fW = aRange.getWidth(); - - if (bDouble) - { - // Draw it as a line. - drawHairLine( - mpOutputDevice, aRange.getMinX()-1.0, aRange.getMinY(), aRange.getMinX()-1.0, aRange.getMaxY(), - aLineColor); - - drawHairLine( - mpOutputDevice, aRange.getMinX()+1.0, aRange.getMinY(), aRange.getMinX()+1.0, aRange.getMaxY(), - aLineColor); - - return true; - } - - if (fW <= 1.0) - { - // Draw it as a line. - drawHairLine( - mpOutputDevice, aRange.getMinX(), aRange.getMinY(), aRange.getMinX(), aRange.getMaxY(), - aLineColor); - - return true; - } - - double fOffset = rtl::math::round(fW/2.0, 0, rtl_math_RoundingMode_Down); - if (fOffset != 0.0) - { - // Move it to the left a bit to center it horizontally. - basegfx::B2DHomMatrix aMat; - aMat.set(0, 2, -fOffset); - aTarget.transform(aMat); - } - } - - mpOutputDevice->SetFillColor(Color(aLineColor)); - mpOutputDevice->SetLineColor(); - mpOutputDevice->DrawPolygon(aTarget); - return true; - } - break; - case SvxBorderLineStyle::DOTTED: - case SvxBorderLineStyle::DASHED: - case SvxBorderLineStyle::DASH_DOT: - case SvxBorderLineStyle::DASH_DOT_DOT: - case SvxBorderLineStyle::FINE_DASHED: - { - std::vector aPattern = - svtools::GetLineDashing(rSource.getStyle(), rSource.getPatternScale()*10.0); - - if (aPattern.empty()) - // Failed to get pattern values. - return false; - - double nThick = rtl::math::round(rSource.getLeftWidth()); - - const basegfx::BColor aLineColor = - maBColorModifierStack.getModifiedColor(rSource.getRGBColorLeft()); - - // Transform the current line range before using it for rendering. - basegfx::B2DRange aRange(fX1, fY1, fX2, fY2); - aRange.transform(maCurrentTransformation); - fX1 = aRange.getMinX(); - fX2 = aRange.getMaxX(); - fY1 = aRange.getMinY(); - fY2 = aRange.getMaxY(); - - basegfx::B2DPolyPolygon aTarget; - - if (bHorizontal) - { - // Horizontal line. - - if (basegfx::fTools::equalZero(nThick)) - { - // Dash line segment too small to draw. Substitute it with a solid line. - drawHairLine(mpOutputDevice, fX1, fY1, fX2, fY1, aLineColor); - return true; - } - - // Create a dash unit polygon set. - basegfx::B2DPolyPolygon aDashes; - std::vector::const_iterator it = aPattern.begin(), itEnd = aPattern.end(); - for (; it != itEnd; ++it) - aDashes.append(makeRectPolygon(0, 0, *it, nThick)); - - aDashes.transform(maCurrentTransformation); - rtl::math::setNan(&nThick); - - // Pixelize the dash unit. We use the same height for - // all dash polygons. - basegfx::B2DPolyPolygon aDashesPix; - - for (sal_uInt32 i = 0, n = aDashes.count(); i < n; ++i) - { - basegfx::B2DPolygon aPoly = aDashes.getB2DPolygon(i); - aRange = aPoly.getB2DRange(); - double fW = rtl::math::round(aRange.getWidth()); - if (basegfx::fTools::equalZero(fW)) - { - // Dash line segment too small to draw. Substitute it with a solid line. - drawHairLine(mpOutputDevice, fX1, fY1, fX2, fY1, aLineColor); - return true; - } - - if (rtl::math::isNan(nThick)) - nThick = rtl::math::round(aRange.getHeight()); - - aDashesPix.append(makeRectPolygon(0, 0, fW, nThick)); - } - - // Make all dash polygons and render them. - double fX = fX1; - bool bLine = true; - sal_uInt32 i = 0, n = aDashesPix.count(); - while (fX <= fX2) - { - basegfx::B2DPolygon aPoly = aDashesPix.getB2DPolygon(i); - aRange = aPoly.getB2DRange(); - if (bLine) - { - double fBlockW = aRange.getWidth(); - if (fX + fBlockW > fX2) - // Clip the right end in case it spills over the range. - fBlockW = fX2 - fX + 1; - - double fH = aRange.getHeight(); - if (basegfx::fTools::equalZero(fH)) - fH = 1.0; - - aTarget.append(makeRectPolygon(fX, fY1, fBlockW, fH)); - } - - bLine = !bLine; // line and blank alternate. - fX += aRange.getWidth(); - - ++i; - if (i >= n) - i = 0; - } - - double fOffset = rtl::math::round(nThick/2.0, 0, rtl_math_RoundingMode_Down); - if (fOffset != 0.0) - { - // Move it up a bit to align it vertically centered. - basegfx::B2DHomMatrix aMat; - aMat.set(1, 2, -fOffset); - aTarget.transform(aMat); - } - } - else - { - // Vertical line. - - if (basegfx::fTools::equalZero(nThick)) - { - // Dash line segment too small to draw. Substitute it with a solid line. - drawHairLine(mpOutputDevice, fX1, fY1, fX1, fY2, aLineColor); - return true; - } - - // Create a dash unit polygon set. - basegfx::B2DPolyPolygon aDashes; - std::vector::const_iterator it = aPattern.begin(), itEnd = aPattern.end(); - for (; it != itEnd; ++it) - aDashes.append(makeRectPolygon(0, 0, nThick, *it)); - - aDashes.transform(maCurrentTransformation); - rtl::math::setNan(&nThick); - - // Pixelize the dash unit. We use the same width for - // all dash polygons. - basegfx::B2DPolyPolygon aDashesPix; - - for (sal_uInt32 i = 0, n = aDashes.count(); i < n; ++i) - { - basegfx::B2DPolygon aPoly = aDashes.getB2DPolygon(i); - aRange = aPoly.getB2DRange(); - double fH = rtl::math::round(aRange.getHeight()); - if (basegfx::fTools::equalZero(fH)) - { - // Dash line segment too small to draw. Substitute it with a solid line. - drawHairLine(mpOutputDevice, fX1, fY1, fX1, fY2, aLineColor); - return true; - } - - if (rtl::math::isNan(nThick)) - nThick = rtl::math::round(aRange.getWidth()); - - aDashesPix.append(makeRectPolygon(0, 0, nThick, fH)); - } - - // Make all dash polygons and render them. - double fY = fY1; - bool bLine = true; - sal_uInt32 i = 0, n = aDashesPix.count(); - while (fY <= fY2) - { - basegfx::B2DPolygon aPoly = aDashesPix.getB2DPolygon(i); - aRange = aPoly.getB2DRange(); - if (bLine) - { - double fBlockH = aRange.getHeight(); - if (fY + fBlockH > fY2) - // Clip the bottom end in case it spills over the range. - fBlockH = fY2 - fY + 1; - - double fW = aRange.getWidth(); - if (basegfx::fTools::equalZero(fW)) - fW = 1.0; - - aTarget.append(makeRectPolygon(fX1, fY, fW, fBlockH)); - } - - bLine = !bLine; // line and blank alternate. - fY += aRange.getHeight(); - - ++i; - if (i >= n) - i = 0; - } - - double fOffset = rtl::math::round(nThick/2.0, 0, rtl_math_RoundingMode_Down); - if (fOffset != 0.0) - { - // Move it to the left a bit to center it horizontally. - basegfx::B2DHomMatrix aMat; - aMat.set(0, 2, -fOffset); - aTarget.transform(aMat); - } - } - - mpOutputDevice->SetFillColor(Color(aLineColor)); - mpOutputDevice->SetLineColor(); - mpOutputDevice->DrawPolyPolygon(aTarget); - - return true; - } - break; - default: - ; - } - return false; - } - void VclPixelProcessor2D::processBasePrimitive2D(const primitive2d::BasePrimitive2D& rCandidate) { switch(rCandidate.getPrimitive2DID()) @@ -1253,28 +890,26 @@ namespace drawinglayer } case PRIMITIVE2D_ID_BORDERLINEPRIMITIVE2D: { - // process recursively, but turn off anti-aliasing. Border - // lines are always rectangular, and look horrible when - // the anti-aliasing is enabled. - AntialiasingFlags nAntiAliasing = mpOutputDevice->GetAntialiasing(); - mpOutputDevice->SetAntialiasing(nAntiAliasing & ~AntialiasingFlags::EnableB2dDraw); - + // process recursively, but switch off AntiAliasing for + // horizontal/vertical lines (*not* diagonal lines). + // Checked using AntialiasingFlags::PixelSnapHairline instead, + // but with AntiAliasing on the display really is too 'ghosty' when + // using fine stroking. Correct, but 'ghosty'. const drawinglayer::primitive2d::BorderLinePrimitive2D& rBorder = static_cast(rCandidate); - if (!tryDrawBorderLinePrimitive2DDirect(rBorder)) + if (rBorder.isHorizontalOrVertical(getViewInformation2D())) { - if (rBorder.getStyle() == SvxBorderLineStyle::DOUBLE) - { - primitive2d::Primitive2DContainer aContainer; - rBorder.createDecomposition(aContainer, getViewInformation2D(), true); - process(aContainer); - } - else - process(rCandidate); - } + AntialiasingFlags nAntiAliasing = mpOutputDevice->GetAntialiasing(); + mpOutputDevice->SetAntialiasing(nAntiAliasing & ~AntialiasingFlags::EnableB2dDraw); - mpOutputDevice->SetAntialiasing(nAntiAliasing); + process(rCandidate); + mpOutputDevice->SetAntialiasing(nAntiAliasing); + } + else + { + process(rCandidate); + } break; } default : diff --git a/drawinglayer/source/processor2d/vclpixelprocessor2d.hxx b/drawinglayer/source/processor2d/vclpixelprocessor2d.hxx index 0a8e97d748b8..19c0282ffc5a 100644 --- a/drawinglayer/source/processor2d/vclpixelprocessor2d.hxx +++ b/drawinglayer/source/processor2d/vclpixelprocessor2d.hxx @@ -64,7 +64,6 @@ namespace drawinglayer bool tryDrawPolyPolygonColorPrimitive2DDirect(const drawinglayer::primitive2d::PolyPolygonColorPrimitive2D& rSource, double fTransparency); bool tryDrawPolygonHairlinePrimitive2DDirect(const drawinglayer::primitive2d::PolygonHairlinePrimitive2D& rSource, double fTransparency); bool tryDrawPolygonStrokePrimitive2DDirect(const drawinglayer::primitive2d::PolygonStrokePrimitive2D& rSource, double fTransparency); - bool tryDrawBorderLinePrimitive2DDirect(const drawinglayer::primitive2d::BorderLinePrimitive2D& rSource); public: /// constructor/destructor -- cgit v1.2.3