summaryrefslogtreecommitdiff
path: root/basegfx
diff options
context:
space:
mode:
authorArmin Le Grand (Collabora) <Armin.Le.Grand@me.com>2020-02-14 12:32:42 +0100
committerArmin Le Grand <Armin.Le.Grand@me.com>2020-02-14 16:44:26 +0100
commit0dc4fddb9c76a3f4682eca4059b42a079e74e735 (patch)
treed75c9e40979d9cc865b90468aea8d6763a2e0253 /basegfx
parent75500a4161c86bba644d212f2f2eef78dfeb9dea (diff)
tdf#130655 added callback interface to ::applyLineDashing
This version of the tooling method allows to avoid collecting line snippets in a return value PolyPolygon. Instead, offer lambda functions to get callbacks for created snippets. The original method using a B2DPolyPolygon return value is adapted to already use this, so serves as example of usage and ensures that only one identical algorithm is used. Change-Id: Ie306968a895ad280fc2425fb40b3244769216ba0 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/88684 Tested-by: Jenkins Reviewed-by: Armin Le Grand <Armin.Le.Grand@me.com>
Diffstat (limited to 'basegfx')
-rw-r--r--basegfx/source/polygon/b2dpolygontools.cxx419
1 files changed, 240 insertions, 179 deletions
diff --git a/basegfx/source/polygon/b2dpolygontools.cxx b/basegfx/source/polygon/b2dpolygontools.cxx
index cf2de34859cd..68d1120bc2cb 100644
--- a/basegfx/source/polygon/b2dpolygontools.cxx
+++ b/basegfx/source/polygon/b2dpolygontools.cxx
@@ -1110,7 +1110,102 @@ namespace basegfx::utils
return false;
}
- void applyLineDashing(const B2DPolygon& rCandidate, const std::vector<double>& rDotDashArray, B2DPolyPolygon* pLineTarget, B2DPolyPolygon* pGapTarget, double fDotDashLength)
+ void applyLineDashing(
+ const B2DPolygon& rCandidate,
+ const std::vector<double>& rDotDashArray,
+ B2DPolyPolygon* pLineTarget,
+ B2DPolyPolygon* pGapTarget,
+ double fDotDashLength)
+ {
+ // clear targets in any case
+ if(pLineTarget)
+ {
+ pLineTarget->clear();
+ }
+
+ if(pGapTarget)
+ {
+ pGapTarget->clear();
+ }
+
+ // provide callbacks as lambdas
+ auto aLineCallback(
+ nullptr == pLineTarget
+ ? std::function<void(const basegfx::B2DPolygon&)>()
+ : [&pLineTarget](const basegfx::B2DPolygon& rSnippet){ pLineTarget->append(rSnippet); });
+ auto aGapCallback(
+ nullptr == pGapTarget
+ ? std::function<void(const basegfx::B2DPolygon&)>()
+ : [&pGapTarget](const basegfx::B2DPolygon& rSnippet){ pGapTarget->append(rSnippet); });
+
+ // call version that uses callbacks
+ applyLineDashing(
+ rCandidate,
+ rDotDashArray,
+ aLineCallback,
+ aGapCallback,
+ fDotDashLength);
+ }
+
+ static void implHandleSnippet(
+ const B2DPolygon& rSnippet,
+ std::function<void(const basegfx::B2DPolygon& rSnippet)>& rTargetCallback,
+ B2DPolygon& rFirst,
+ B2DPolygon& rLast)
+ {
+ if(rSnippet.isClosed())
+ {
+ if(!rFirst.count())
+ {
+ rFirst = rSnippet;
+ }
+ else
+ {
+ if(rLast.count())
+ {
+ rTargetCallback(rLast);
+ }
+
+ rLast = rSnippet;
+ }
+ }
+ else
+ {
+ rTargetCallback(rSnippet);
+ }
+ }
+
+ static void implHandleFirstLast(
+ std::function<void(const basegfx::B2DPolygon& rSnippet)>& rTargetCallback,
+ B2DPolygon& rFirst,
+ B2DPolygon& rLast)
+ {
+ if(rFirst.count() && rLast.count()
+ && rFirst.getB2DPoint(0).equal(rLast.getB2DPoint(rLast.count() - 1)))
+ {
+ // start of first and end of last are the same -> merge them
+ rLast.append(rFirst);
+ rLast.removeDoublePoints();
+ rFirst.clear();
+ }
+
+ if(rLast.count())
+ {
+ rTargetCallback(rLast);
+ }
+
+ if(rFirst.count())
+ {
+ rTargetCallback(rFirst);
+ }
+ }
+
+ void applyLineDashing(
+ const B2DPolygon& rCandidate,
+ const std::vector<double>& rDotDashArray,
+ std::function<void(const basegfx::B2DPolygon& rSnippet)> aLineTargetCallback,
+ std::function<void(const basegfx::B2DPolygon& rSnippet)> aGapTargetCallback,
+ double fDotDashLength)
{
const sal_uInt32 nPointCount(rCandidate.count());
const sal_uInt32 nDotDashCount(rDotDashArray.size());
@@ -1120,244 +1215,210 @@ namespace basegfx::utils
fDotDashLength = std::accumulate(rDotDashArray.begin(), rDotDashArray.end(), 0.0);
}
- if(fTools::more(fDotDashLength, 0.0) && (pLineTarget || pGapTarget) && nPointCount)
+ if(fTools::lessOrEqual(fDotDashLength, 0.0) || (!aLineTargetCallback && !aGapTargetCallback) || !nPointCount)
{
- // clear targets
- if(pLineTarget)
+ // parameters make no sense, just add source to targets
+ if(aLineTargetCallback)
{
- pLineTarget->clear();
+ aLineTargetCallback(rCandidate);
}
- if(pGapTarget)
+ if(aGapTargetCallback)
{
- pGapTarget->clear();
+ aGapTargetCallback(rCandidate);
}
- // prepare current edge's start
- B2DCubicBezier aCurrentEdge;
- const sal_uInt32 nEdgeCount(rCandidate.isClosed() ? nPointCount : nPointCount - 1);
- aCurrentEdge.setStartPoint(rCandidate.getB2DPoint(0));
+ return;
+ }
- // prepare DotDashArray iteration and the line/gap switching bool
- sal_uInt32 nDotDashIndex(0);
- bool bIsLine(true);
- double fDotDashMovingLength(rDotDashArray[0]);
- B2DPolygon aSnippet;
+ // prepare current edge's start
+ B2DCubicBezier aCurrentEdge;
+ const bool bIsClosed(rCandidate.isClosed());
+ const sal_uInt32 nEdgeCount(bIsClosed ? nPointCount : nPointCount - 1);
+ aCurrentEdge.setStartPoint(rCandidate.getB2DPoint(0));
- // iterate over all edges
- for(sal_uInt32 a(0); a < nEdgeCount; a++)
- {
- // update current edge (fill in C1, C2 and end point)
- double fLastDotDashMovingLength(0.0);
- const sal_uInt32 nNextIndex((a + 1) % nPointCount);
- aCurrentEdge.setControlPointA(rCandidate.getNextControlPoint(a));
- aCurrentEdge.setControlPointB(rCandidate.getPrevControlPoint(nNextIndex));
- aCurrentEdge.setEndPoint(rCandidate.getB2DPoint(nNextIndex));
+ // prepare DotDashArray iteration and the line/gap switching bool
+ sal_uInt32 nDotDashIndex(0);
+ bool bIsLine(true);
+ double fDotDashMovingLength(rDotDashArray[0]);
+ B2DPolygon aSnippet;
- // check if we have a trivial bezier segment -> possible fallback to edge
- aCurrentEdge.testAndSolveTrivialBezier();
+ // remember 1st and last snippets to try to merge after execution
+ // is complete and hand to callback
+ B2DPolygon aFirstLine, aLastLine;
+ B2DPolygon aFirstGap, aLastGap;
- if(aCurrentEdge.isBezier())
- {
- // bezier segment
- const B2DCubicBezierHelper aCubicBezierHelper(aCurrentEdge);
- const double fEdgeLength(aCubicBezierHelper.getLength());
+ // iterate over all edges
+ for(sal_uInt32 a(0); a < nEdgeCount; a++)
+ {
+ // update current edge (fill in C1, C2 and end point)
+ double fLastDotDashMovingLength(0.0);
+ const sal_uInt32 nNextIndex((a + 1) % nPointCount);
+ aCurrentEdge.setControlPointA(rCandidate.getNextControlPoint(a));
+ aCurrentEdge.setControlPointB(rCandidate.getPrevControlPoint(nNextIndex));
+ aCurrentEdge.setEndPoint(rCandidate.getB2DPoint(nNextIndex));
- if(!fTools::equalZero(fEdgeLength))
+ // check if we have a trivial bezier segment -> possible fallback to edge
+ aCurrentEdge.testAndSolveTrivialBezier();
+
+ if(aCurrentEdge.isBezier())
+ {
+ // bezier segment
+ const B2DCubicBezierHelper aCubicBezierHelper(aCurrentEdge);
+ const double fEdgeLength(aCubicBezierHelper.getLength());
+
+ if(!fTools::equalZero(fEdgeLength))
+ {
+ while(fTools::less(fDotDashMovingLength, fEdgeLength))
{
- while(fTools::less(fDotDashMovingLength, fEdgeLength))
+ // new split is inside edge, create and append snippet [fLastDotDashMovingLength, fDotDashMovingLength]
+ const bool bHandleLine(bIsLine && aLineTargetCallback);
+ const bool bHandleGap(!bIsLine && aGapTargetCallback);
+
+ if(bHandleLine || bHandleGap)
{
- // new split is inside edge, create and append snippet [fLastDotDashMovingLength, fDotDashMovingLength]
- const bool bHandleLine(bIsLine && pLineTarget);
- const bool bHandleGap(!bIsLine && pGapTarget);
+ const double fBezierSplitStart(aCubicBezierHelper.distanceToRelative(fLastDotDashMovingLength));
+ const double fBezierSplitEnd(aCubicBezierHelper.distanceToRelative(fDotDashMovingLength));
+ B2DCubicBezier aBezierSnippet(aCurrentEdge.snippet(fBezierSplitStart, fBezierSplitEnd));
- if(bHandleLine || bHandleGap)
+ if(!aSnippet.count())
{
- const double fBezierSplitStart(aCubicBezierHelper.distanceToRelative(fLastDotDashMovingLength));
- const double fBezierSplitEnd(aCubicBezierHelper.distanceToRelative(fDotDashMovingLength));
- B2DCubicBezier aBezierSnippet(aCurrentEdge.snippet(fBezierSplitStart, fBezierSplitEnd));
-
- if(!aSnippet.count())
- {
- aSnippet.append(aBezierSnippet.getStartPoint());
- }
+ aSnippet.append(aBezierSnippet.getStartPoint());
+ }
- aSnippet.appendBezierSegment(aBezierSnippet.getControlPointA(), aBezierSnippet.getControlPointB(), aBezierSnippet.getEndPoint());
+ aSnippet.appendBezierSegment(aBezierSnippet.getControlPointA(), aBezierSnippet.getControlPointB(), aBezierSnippet.getEndPoint());
- if(bHandleLine)
- {
- pLineTarget->append(aSnippet);
- }
- else
- {
- pGapTarget->append(aSnippet);
- }
+ if(bHandleLine)
+ {
+ implHandleSnippet(aSnippet, aLineTargetCallback, aFirstLine, aLastLine);
+ }
- aSnippet.clear();
+ if(bHandleGap)
+ {
+ implHandleSnippet(aSnippet, aGapTargetCallback, aFirstGap, aLastGap);
}
- // prepare next DotDashArray step and flip line/gap flag
- fLastDotDashMovingLength = fDotDashMovingLength;
- fDotDashMovingLength += rDotDashArray[(++nDotDashIndex) % nDotDashCount];
- bIsLine = !bIsLine;
+ aSnippet.clear();
}
- // append closing snippet [fLastDotDashMovingLength, fEdgeLength]
- const bool bHandleLine(bIsLine && pLineTarget);
- const bool bHandleGap(!bIsLine && pGapTarget);
+ // prepare next DotDashArray step and flip line/gap flag
+ fLastDotDashMovingLength = fDotDashMovingLength;
+ fDotDashMovingLength += rDotDashArray[(++nDotDashIndex) % nDotDashCount];
+ bIsLine = !bIsLine;
+ }
- if(bHandleLine || bHandleGap)
- {
- B2DCubicBezier aRight;
- const double fBezierSplit(aCubicBezierHelper.distanceToRelative(fLastDotDashMovingLength));
+ // append closing snippet [fLastDotDashMovingLength, fEdgeLength]
+ const bool bHandleLine(bIsLine && aLineTargetCallback);
+ const bool bHandleGap(!bIsLine && aGapTargetCallback);
- aCurrentEdge.split(fBezierSplit, nullptr, &aRight);
+ if(bHandleLine || bHandleGap)
+ {
+ B2DCubicBezier aRight;
+ const double fBezierSplit(aCubicBezierHelper.distanceToRelative(fLastDotDashMovingLength));
- if(!aSnippet.count())
- {
- aSnippet.append(aRight.getStartPoint());
- }
+ aCurrentEdge.split(fBezierSplit, nullptr, &aRight);
- aSnippet.appendBezierSegment(aRight.getControlPointA(), aRight.getControlPointB(), aRight.getEndPoint());
+ if(!aSnippet.count())
+ {
+ aSnippet.append(aRight.getStartPoint());
}
- // prepare move to next edge
- fDotDashMovingLength -= fEdgeLength;
+ aSnippet.appendBezierSegment(aRight.getControlPointA(), aRight.getControlPointB(), aRight.getEndPoint());
}
+
+ // prepare move to next edge
+ fDotDashMovingLength -= fEdgeLength;
}
- else
- {
- // simple edge
- const double fEdgeLength(aCurrentEdge.getEdgeLength());
+ }
+ else
+ {
+ // simple edge
+ const double fEdgeLength(aCurrentEdge.getEdgeLength());
- if(!fTools::equalZero(fEdgeLength))
+ if(!fTools::equalZero(fEdgeLength))
+ {
+ while(fTools::less(fDotDashMovingLength, fEdgeLength))
{
- while(fTools::less(fDotDashMovingLength, fEdgeLength))
- {
- // new split is inside edge, create and append snippet [fLastDotDashMovingLength, fDotDashMovingLength]
- const bool bHandleLine(bIsLine && pLineTarget);
- const bool bHandleGap(!bIsLine && pGapTarget);
+ // new split is inside edge, create and append snippet [fLastDotDashMovingLength, fDotDashMovingLength]
+ const bool bHandleLine(bIsLine && aLineTargetCallback);
+ const bool bHandleGap(!bIsLine && aGapTargetCallback);
- if(bHandleLine || bHandleGap)
+ if(bHandleLine || bHandleGap)
+ {
+ if(!aSnippet.count())
{
- if(!aSnippet.count())
- {
- aSnippet.append(interpolate(aCurrentEdge.getStartPoint(), aCurrentEdge.getEndPoint(), fLastDotDashMovingLength / fEdgeLength));
- }
+ aSnippet.append(interpolate(aCurrentEdge.getStartPoint(), aCurrentEdge.getEndPoint(), fLastDotDashMovingLength / fEdgeLength));
+ }
- aSnippet.append(interpolate(aCurrentEdge.getStartPoint(), aCurrentEdge.getEndPoint(), fDotDashMovingLength / fEdgeLength));
+ aSnippet.append(interpolate(aCurrentEdge.getStartPoint(), aCurrentEdge.getEndPoint(), fDotDashMovingLength / fEdgeLength));
- if(bHandleLine)
- {
- pLineTarget->append(aSnippet);
- }
- else
- {
- pGapTarget->append(aSnippet);
- }
+ if(bHandleLine)
+ {
+ implHandleSnippet(aSnippet, aLineTargetCallback, aFirstLine, aLastLine);
+ }
- aSnippet.clear();
+ if(bHandleGap)
+ {
+ implHandleSnippet(aSnippet, aGapTargetCallback, aFirstGap, aLastGap);
}
- // prepare next DotDashArray step and flip line/gap flag
- fLastDotDashMovingLength = fDotDashMovingLength;
- fDotDashMovingLength += rDotDashArray[(++nDotDashIndex) % nDotDashCount];
- bIsLine = !bIsLine;
+ aSnippet.clear();
}
- // append snippet [fLastDotDashMovingLength, fEdgeLength]
- const bool bHandleLine(bIsLine && pLineTarget);
- const bool bHandleGap(!bIsLine && pGapTarget);
+ // prepare next DotDashArray step and flip line/gap flag
+ fLastDotDashMovingLength = fDotDashMovingLength;
+ fDotDashMovingLength += rDotDashArray[(++nDotDashIndex) % nDotDashCount];
+ bIsLine = !bIsLine;
+ }
- if(bHandleLine || bHandleGap)
- {
- if(!aSnippet.count())
- {
- aSnippet.append(interpolate(aCurrentEdge.getStartPoint(), aCurrentEdge.getEndPoint(), fLastDotDashMovingLength / fEdgeLength));
- }
+ // append snippet [fLastDotDashMovingLength, fEdgeLength]
+ const bool bHandleLine(bIsLine && aLineTargetCallback);
+ const bool bHandleGap(!bIsLine && aGapTargetCallback);
- aSnippet.append(aCurrentEdge.getEndPoint());
+ if(bHandleLine || bHandleGap)
+ {
+ if(!aSnippet.count())
+ {
+ aSnippet.append(interpolate(aCurrentEdge.getStartPoint(), aCurrentEdge.getEndPoint(), fLastDotDashMovingLength / fEdgeLength));
}
- // prepare move to next edge
- fDotDashMovingLength -= fEdgeLength;
+ aSnippet.append(aCurrentEdge.getEndPoint());
}
- }
- // prepare next edge step (end point gets new start point)
- aCurrentEdge.setStartPoint(aCurrentEdge.getEndPoint());
- }
-
- // append last intermediate results (if exists)
- if(aSnippet.count())
- {
- if(bIsLine && pLineTarget)
- {
- pLineTarget->append(aSnippet);
- }
- else if(!bIsLine && pGapTarget)
- {
- pGapTarget->append(aSnippet);
+ // prepare move to next edge
+ fDotDashMovingLength -= fEdgeLength;
}
}
- // check if start and end polygon may be merged
- if(pLineTarget)
- {
- const sal_uInt32 nCount(pLineTarget->count());
+ // prepare next edge step (end point gets new start point)
+ aCurrentEdge.setStartPoint(aCurrentEdge.getEndPoint());
+ }
- if(nCount > 1)
- {
- // these polygons were created above, there exists none with less than two points,
- // thus direct point access below is allowed
- const B2DPolygon aFirst(pLineTarget->getB2DPolygon(0));
- B2DPolygon aLast(pLineTarget->getB2DPolygon(nCount - 1));
+ // append last intermediate results (if exists)
+ if(aSnippet.count())
+ {
+ const bool bHandleLine(bIsLine && aLineTargetCallback);
+ const bool bHandleGap(!bIsLine && aGapTargetCallback);
- if(aFirst.getB2DPoint(0).equal(aLast.getB2DPoint(aLast.count() - 1)))
- {
- // start of first and end of last are the same -> merge them
- aLast.append(aFirst);
- aLast.removeDoublePoints();
- pLineTarget->setB2DPolygon(0, aLast);
- pLineTarget->remove(nCount - 1);
- }
- }
+ if(bHandleLine)
+ {
+ implHandleSnippet(aSnippet, aLineTargetCallback, aFirstLine, aLastLine);
}
- if(pGapTarget)
+ if(bHandleGap)
{
- const sal_uInt32 nCount(pGapTarget->count());
-
- if(nCount > 1)
- {
- // these polygons were created above, there exists none with less than two points,
- // thus direct point access below is allowed
- const B2DPolygon aFirst(pGapTarget->getB2DPolygon(0));
- B2DPolygon aLast(pGapTarget->getB2DPolygon(nCount - 1));
-
- if(aFirst.getB2DPoint(0).equal(aLast.getB2DPoint(aLast.count() - 1)))
- {
- // start of first and end of last are the same -> merge them
- aLast.append(aFirst);
- aLast.removeDoublePoints();
- pGapTarget->setB2DPolygon(0, aLast);
- pGapTarget->remove(nCount - 1);
- }
- }
+ implHandleSnippet(aSnippet, aGapTargetCallback, aFirstGap, aLastGap);
}
}
- else
+
+ if(bIsClosed && aLineTargetCallback)
{
- // parameters make no sense, just add source to targets
- if(pLineTarget)
- {
- pLineTarget->append(rCandidate);
- }
+ implHandleFirstLast(aLineTargetCallback, aFirstLine, aLastLine);
+ }
- if(pGapTarget)
- {
- pGapTarget->append(rCandidate);
- }
+ if(bIsClosed && aGapTargetCallback)
+ {
+ implHandleFirstLast(aGapTargetCallback, aFirstGap, aLastGap);
}
}