[Libreoffice-commits] core.git: Branch 'distro/collabora/cp-6.4' - basegfx/source include/basegfx
Armin Le Grand (Collabora) (via logerrit)
logerrit at kemper.freedesktop.org
Thu May 14 07:07:17 UTC 2020
basegfx/source/polygon/b2dpolygontools.cxx | 419 ++++++++++++++++------------
include/basegfx/polygon/b2dpolygontools.hxx | 24 +
2 files changed, 263 insertions(+), 180 deletions(-)
New commits:
commit d892e03f7dcbb7b8a6cc6a78dd758bab95ddded1
Author: Armin Le Grand (Collabora) <Armin.Le.Grand at me.com>
AuthorDate: Fri Feb 14 12:32:42 2020 +0100
Commit: Miklos Vajna <vmiklos at collabora.com>
CommitDate: Thu May 14 09:06:32 2020 +0200
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.
(cherry picked from commit 0dc4fddb9c76a3f4682eca4059b42a079e74e735)
[ Fixes the cui/ build. ]
Change-Id: Ie306968a895ad280fc2425fb40b3244769216ba0
diff --git a/basegfx/source/polygon/b2dpolygontools.cxx b/basegfx/source/polygon/b2dpolygontools.cxx
index cbc18ee74203..245ed957fac3 100644
--- a/basegfx/source/polygon/b2dpolygontools.cxx
+++ b/basegfx/source/polygon/b2dpolygontools.cxx
@@ -1113,7 +1113,102 @@ namespace basegfx
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());
@@ -1123,244 +1218,210 @@ namespace basegfx
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);
}
}
diff --git a/include/basegfx/polygon/b2dpolygontools.hxx b/include/basegfx/polygon/b2dpolygontools.hxx
index 565f8013ffe9..2ec15714088b 100644
--- a/include/basegfx/polygon/b2dpolygontools.hxx
+++ b/include/basegfx/polygon/b2dpolygontools.hxx
@@ -20,6 +20,9 @@
#ifndef INCLUDED_BASEGFX_POLYGON_B2DPOLYGONTOOLS_HXX
#define INCLUDED_BASEGFX_POLYGON_B2DPOLYGONTOOLS_HXX
+#include <vector>
+#include <functional>
+
#include <basegfx/point/b2dpoint.hxx>
#include <basegfx/vector/b2dvector.hxx>
#include <basegfx/range/b2drectangle.hxx>
@@ -27,7 +30,6 @@
#include <basegfx/polygon/b2dpolygontriangulator.hxx>
#include <com/sun/star/drawing/PointSequence.hpp>
#include <com/sun/star/drawing/FlagSequence.hpp>
-#include <vector>
#include <basegfx/basegfxdllapi.h>
#include <o3tl/typed_flags_set.hxx>
@@ -188,7 +190,27 @@ namespace basegfx
@param fFullDashDotLen
The summed-up length of the rDotDashArray. If zero, it will
be calculated internally.
+
+ There is now a 2nd version that allows to provide callback
+ functions that get called when a snippet of a line/gap is
+ produced and needs to be added. This allows to use it like
+ a 'pipeline'. When using this (e.g. the 1st version uses
+ this internally to guarantee the same algorithm is used)
+ it is not needed to accumulate a potentially huge number
+ of polygons in the result-polyPolygons, but e.g. consume
+ them directly in the caller. Example is renderinmg a
+ dashed line but without creating the potentially huge amount
+ of polygons.
+ The 2nd version will also merge first/last line/gap snippets
+ if the input polygon is closed and the start/end-points match
+ accordingly - at the cost that this will be delivered last.
*/
+ BASEGFX_DLLPUBLIC 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 = std::function<void(const basegfx::B2DPolygon&)>(),
+ double fDotDashLength = 0.0);
BASEGFX_DLLPUBLIC void applyLineDashing(
const B2DPolygon& rCandidate,
const ::std::vector<double>& rDotDashArray,
More information about the Libreoffice-commits
mailing list