Core algorithm performance optimization

This commit is contained in:
Viktor Chlumský 2020-03-14 19:19:17 +01:00
parent 5b5272e7a8
commit 8ebb37da4e
7 changed files with 171 additions and 68 deletions

View File

@ -91,4 +91,11 @@ void Shape::scanline(Scanline &line, double y) const {
#endif
}
int Shape::edgeCount() const {
int total = 0;
for (std::vector<Contour>::const_iterator contour = contours.begin(); contour != contours.end(); ++contour)
total += contour->edges.size();
return total;
}
}

View File

@ -40,6 +40,8 @@ public:
Bounds getBounds(double border = 0, double miterLimit = 0, int polarity = 0) const;
/// Outputs the scanline that intersects the shape at y.
void scanline(Scanline &line, double y) const;
/// Returns the total number of edge segments
int edgeCount() const;
};

View File

@ -28,12 +28,12 @@ SimpleContourCombiner<EdgeSelector>::SimpleContourCombiner(const Shape &shape) {
template <class EdgeSelector>
void SimpleContourCombiner<EdgeSelector>::reset(const Point2 &p) {
shapeEdgeSelector = EdgeSelector(p);
shapeEdgeSelector.reset(p);
}
template <class EdgeSelector>
void SimpleContourCombiner<EdgeSelector>::setContourEdgeSelection(int i, const EdgeSelector &edgeSelector) {
shapeEdgeSelector.merge(edgeSelector);
EdgeSelector & SimpleContourCombiner<EdgeSelector>::edgeSelector(int) {
return shapeEdgeSelector;
}
template <class EdgeSelector>
@ -56,24 +56,34 @@ OverlappingContourCombiner<EdgeSelector>::OverlappingContourCombiner(const Shape
template <class EdgeSelector>
void OverlappingContourCombiner<EdgeSelector>::reset(const Point2 &p) {
shapeEdgeSelector = EdgeSelector(p);
innerEdgeSelector = EdgeSelector(p);
outerEdgeSelector = EdgeSelector(p);
this->p = p;
for (std::vector<EdgeSelector>::iterator contourEdgeSelector = edgeSelectors.begin(); contourEdgeSelector != edgeSelectors.end(); ++contourEdgeSelector)
contourEdgeSelector->reset(p);
}
template <class EdgeSelector>
void OverlappingContourCombiner<EdgeSelector>::setContourEdgeSelection(int i, const EdgeSelector &edgeSelector) {
DistanceType edgeDistance = edgeSelector.distance();
edgeSelectors[i] = edgeSelector;
shapeEdgeSelector.merge(edgeSelector);
if (windings[i] > 0 && resolveDistance(edgeDistance) >= 0)
innerEdgeSelector.merge(edgeSelector);
if (windings[i] < 0 && resolveDistance(edgeDistance) <= 0)
outerEdgeSelector.merge(edgeSelector);
EdgeSelector & OverlappingContourCombiner<EdgeSelector>::edgeSelector(int i) {
return edgeSelectors[i];
}
template <class EdgeSelector>
typename OverlappingContourCombiner<EdgeSelector>::DistanceType OverlappingContourCombiner<EdgeSelector>::distance() const {
int contourCount = (int) edgeSelectors.size();
EdgeSelector shapeEdgeSelector;
EdgeSelector innerEdgeSelector;
EdgeSelector outerEdgeSelector;
shapeEdgeSelector.reset(p);
innerEdgeSelector.reset(p);
outerEdgeSelector.reset(p);
for (int i = 0; i < contourCount; ++i) {
DistanceType edgeDistance = edgeSelectors[i].distance();
shapeEdgeSelector.merge(edgeSelectors[i]);
if (windings[i] > 0 && resolveDistance(edgeDistance) >= 0)
innerEdgeSelector.merge(edgeSelectors[i]);
if (windings[i] < 0 && resolveDistance(edgeDistance) <= 0)
outerEdgeSelector.merge(edgeSelectors[i]);
}
DistanceType shapeDistance = shapeEdgeSelector.distance();
DistanceType innerDistance = innerEdgeSelector.distance();
DistanceType outerDistance = outerEdgeSelector.distance();
@ -81,7 +91,6 @@ typename OverlappingContourCombiner<EdgeSelector>::DistanceType OverlappingConto
double outerScalarDistance = resolveDistance(outerDistance);
DistanceType distance;
initDistance(distance);
int contourCount = (int) windings.size();
int winding = 0;
if (innerScalarDistance >= 0 && fabs(innerScalarDistance) <= fabs(outerScalarDistance)) {

View File

@ -16,7 +16,7 @@ public:
explicit SimpleContourCombiner(const Shape &shape);
void reset(const Point2 &p);
void setContourEdgeSelection(int i, const EdgeSelector &edgeSelector);
EdgeSelector & edgeSelector(int i);
DistanceType distance() const;
private:
@ -34,15 +34,13 @@ public:
explicit OverlappingContourCombiner(const Shape &shape);
void reset(const Point2 &p);
void setContourEdgeSelection(int i, const EdgeSelector &edgeSelector);
EdgeSelector & edgeSelector(int i);
DistanceType distance() const;
private:
Point2 p;
std::vector<int> windings;
std::vector<EdgeSelector> edgeSelectors;
EdgeSelector shapeEdgeSelector;
EdgeSelector innerEdgeSelector;
EdgeSelector outerEdgeSelector;
};

View File

@ -1,15 +1,30 @@
#include "edge-selectors.h"
#include "arithmetics.hpp"
namespace msdfgen {
TrueDistanceSelector::TrueDistanceSelector(const Point2 &p) : p(p) { }
#define DISTANCE_DELTA_FACTOR 1.001
void TrueDistanceSelector::addEdge(const EdgeSegment *prevEdge, const EdgeSegment *edge, const EdgeSegment *nextEdge) {
double dummy;
SignedDistance distance = edge->signedDistance(p, dummy);
if (distance < minDistance)
minDistance = distance;
TrueDistanceSelector::EdgeCache::EdgeCache() : absDistance(0) { }
void TrueDistanceSelector::reset(const Point2 &p) {
double delta = DISTANCE_DELTA_FACTOR*(p-this->p).length();
minDistance.distance += nonZeroSign(minDistance.distance)*delta;
this->p = p;
}
void TrueDistanceSelector::addEdge(EdgeCache &cache, const EdgeSegment *prevEdge, const EdgeSegment *edge, const EdgeSegment *nextEdge) {
double delta = DISTANCE_DELTA_FACTOR*(p-cache.point).length();
if (cache.absDistance-delta <= fabs(minDistance.distance)) {
double dummy;
SignedDistance distance = edge->signedDistance(p, dummy);
if (distance < minDistance)
minDistance = distance;
cache.point = p;
cache.absDistance = fabs(distance.distance);
}
}
void TrueDistanceSelector::merge(const TrueDistanceSelector &other) {
@ -21,24 +36,48 @@ TrueDistanceSelector::DistanceType TrueDistanceSelector::distance() const {
return minDistance.distance;
}
bool PseudoDistanceSelectorBase::pointFacingEdge(const EdgeSegment *prevEdge, const EdgeSegment *edge, const EdgeSegment *nextEdge, const Point2 &p, double param) {
PseudoDistanceSelectorBase::EdgeCache::EdgeCache() : absDistance(0), edgeDomainDistance(0), pseudoDistance(0) { }
double PseudoDistanceSelectorBase::edgeDomainDistance(const EdgeSegment *prevEdge, const EdgeSegment *edge, const EdgeSegment *nextEdge, const Point2 &p, double param) {
if (param < 0) {
Vector2 prevEdgeDir = -prevEdge->direction(1).normalize(true);
Vector2 edgeDir = edge->direction(0).normalize(true);
Vector2 pointDir = p-edge->point(0);
return dotProduct(pointDir, edgeDir) >= dotProduct(pointDir, prevEdgeDir);
return dotProduct(pointDir, (prevEdgeDir-edgeDir).normalize(true));
}
if (param > 1) {
Vector2 edgeDir = -edge->direction(1).normalize(true);
Vector2 nextEdgeDir = nextEdge->direction(0).normalize(true);
Vector2 pointDir = p-edge->point(1);
return dotProduct(pointDir, edgeDir) >= dotProduct(pointDir, nextEdgeDir);
return dotProduct(pointDir, (nextEdgeDir-edgeDir).normalize(true));
}
return true;
return 0;
}
PseudoDistanceSelectorBase::PseudoDistanceSelectorBase() : nearEdge(NULL), nearEdgeParam(0) { }
void PseudoDistanceSelectorBase::reset(double delta) {
minTrueDistance.distance += nonZeroSign(minTrueDistance.distance)*delta;
minNegativePseudoDistance.distance = -fabs(minTrueDistance.distance);
minPositivePseudoDistance.distance = fabs(minTrueDistance.distance);
nearEdge = NULL;
nearEdgeParam = 0;
}
bool PseudoDistanceSelectorBase::isEdgeRelevant(const EdgeCache &cache, const EdgeSegment *edge, const Point2 &p) const {
double delta = DISTANCE_DELTA_FACTOR*(p-cache.point).length();
return (
cache.absDistance-delta <= fabs(minTrueDistance.distance) ||
(cache.edgeDomainDistance > 0 ?
cache.edgeDomainDistance-delta <= 0 :
(cache.pseudoDistance < 0 ?
cache.pseudoDistance+delta >= minNegativePseudoDistance.distance :
cache.pseudoDistance-delta <= minPositivePseudoDistance.distance
)
)
);
}
void PseudoDistanceSelectorBase::addEdgeTrueDistance(const EdgeSegment *edge, const SignedDistance &distance, double param) {
if (distance < minTrueDistance) {
minTrueDistance = distance;
@ -80,15 +119,26 @@ SignedDistance PseudoDistanceSelectorBase::trueDistance() const {
return minTrueDistance;
}
PseudoDistanceSelector::PseudoDistanceSelector(const Point2 &p) : p(p) { }
void PseudoDistanceSelector::reset(const Point2 &p) {
double delta = DISTANCE_DELTA_FACTOR*(p-this->p).length();
PseudoDistanceSelectorBase::reset(delta);
this->p = p;
}
void PseudoDistanceSelector::addEdge(const EdgeSegment *prevEdge, const EdgeSegment *edge, const EdgeSegment *nextEdge) {
double param;
SignedDistance distance = edge->signedDistance(p, param);
addEdgeTrueDistance(edge, distance, param);
if (pointFacingEdge(prevEdge, edge, nextEdge, p, param)) {
edge->distanceToPseudoDistance(distance, p, param);
addEdgePseudoDistance(distance);
void PseudoDistanceSelector::addEdge(EdgeCache &cache, const EdgeSegment *prevEdge, const EdgeSegment *edge, const EdgeSegment *nextEdge) {
if (isEdgeRelevant(cache, edge, p)) {
double param;
SignedDistance distance = edge->signedDistance(p, param);
double edd = edgeDomainDistance(prevEdge, edge, nextEdge, p, param);
addEdgeTrueDistance(edge, distance, param);
cache.point = p;
cache.absDistance = fabs(distance.distance);
cache.edgeDomainDistance = edd;
if (edd <= 0) {
edge->distanceToPseudoDistance(distance, p, param);
addEdgePseudoDistance(distance);
cache.pseudoDistance = distance.distance;
}
}
}
@ -96,25 +146,42 @@ PseudoDistanceSelector::DistanceType PseudoDistanceSelector::distance() const {
return computeDistance(p);
}
MultiDistanceSelector::MultiDistanceSelector(const Point2 &p) : p(p) { }
void MultiDistanceSelector::reset(const Point2 &p) {
double delta = DISTANCE_DELTA_FACTOR*(p-this->p).length();
r.reset(delta);
g.reset(delta);
b.reset(delta);
this->p = p;
}
void MultiDistanceSelector::addEdge(const EdgeSegment *prevEdge, const EdgeSegment *edge, const EdgeSegment *nextEdge) {
double param;
SignedDistance distance = edge->signedDistance(p, param);
if (edge->color&RED)
r.addEdgeTrueDistance(edge, distance, param);
if (edge->color&GREEN)
g.addEdgeTrueDistance(edge, distance, param);
if (edge->color&BLUE)
b.addEdgeTrueDistance(edge, distance, param);
if (PseudoDistanceSelectorBase::pointFacingEdge(prevEdge, edge, nextEdge, p, param)) {
edge->distanceToPseudoDistance(distance, p, param);
void MultiDistanceSelector::addEdge(EdgeCache &cache, const EdgeSegment *prevEdge, const EdgeSegment *edge, const EdgeSegment *nextEdge) {
if (
(edge->color&RED && r.isEdgeRelevant(cache, edge, p)) ||
(edge->color&GREEN && g.isEdgeRelevant(cache, edge, p)) ||
(edge->color&BLUE && b.isEdgeRelevant(cache, edge, p))
) {
double param;
SignedDistance distance = edge->signedDistance(p, param);
double edd = PseudoDistanceSelectorBase::edgeDomainDistance(prevEdge, edge, nextEdge, p, param);
if (edge->color&RED)
r.addEdgePseudoDistance(distance);
r.addEdgeTrueDistance(edge, distance, param);
if (edge->color&GREEN)
g.addEdgePseudoDistance(distance);
g.addEdgeTrueDistance(edge, distance, param);
if (edge->color&BLUE)
b.addEdgePseudoDistance(distance);
b.addEdgeTrueDistance(edge, distance, param);
cache.point = p;
cache.absDistance = fabs(distance.distance);
cache.edgeDomainDistance = edd;
if (edd <= 0) {
edge->distanceToPseudoDistance(distance, p, param);
if (edge->color&RED)
r.addEdgePseudoDistance(distance);
if (edge->color&GREEN)
g.addEdgePseudoDistance(distance);
if (edge->color&BLUE)
b.addEdgePseudoDistance(distance);
cache.pseudoDistance = distance.distance;
}
}
}
@ -141,8 +208,6 @@ SignedDistance MultiDistanceSelector::trueDistance() const {
return distance;
}
MultiAndTrueDistanceSelector::MultiAndTrueDistanceSelector(const Point2 &p) : MultiDistanceSelector(p) { }
MultiAndTrueDistanceSelector::DistanceType MultiAndTrueDistanceSelector::distance() const {
MultiDistance multiDistance = MultiDistanceSelector::distance();
MultiAndTrueDistance mtd;

View File

@ -20,8 +20,15 @@ class TrueDistanceSelector {
public:
typedef double DistanceType;
explicit TrueDistanceSelector(const Point2 &p = Point2());
void addEdge(const EdgeSegment *prevEdge, const EdgeSegment *edge, const EdgeSegment *nextEdge);
struct EdgeCache {
Point2 point;
double absDistance;
EdgeCache();
};
void reset(const Point2 &p);
void addEdge(EdgeCache &cache, const EdgeSegment *prevEdge, const EdgeSegment *edge, const EdgeSegment *nextEdge);
void merge(const TrueDistanceSelector &other);
DistanceType distance() const;
@ -34,9 +41,20 @@ private:
class PseudoDistanceSelectorBase {
public:
static bool pointFacingEdge(const EdgeSegment *prevEdge, const EdgeSegment *edge, const EdgeSegment *nextEdge, const Point2 &p, double param);
struct EdgeCache {
Point2 point;
double absDistance;
double edgeDomainDistance;
double pseudoDistance;
EdgeCache();
};
static double edgeDomainDistance(const EdgeSegment *prevEdge, const EdgeSegment *edge, const EdgeSegment *nextEdge, const Point2 &p, double param);
PseudoDistanceSelectorBase();
void reset(double delta);
bool isEdgeRelevant(const EdgeCache &cache, const EdgeSegment *edge, const Point2 &p) const;
void addEdgeTrueDistance(const EdgeSegment *edge, const SignedDistance &distance, double param);
void addEdgePseudoDistance(const SignedDistance &distance);
void merge(const PseudoDistanceSelectorBase &other);
@ -58,8 +76,8 @@ class PseudoDistanceSelector : public PseudoDistanceSelectorBase {
public:
typedef double DistanceType;
explicit PseudoDistanceSelector(const Point2 &p = Point2());
void addEdge(const EdgeSegment *prevEdge, const EdgeSegment *edge, const EdgeSegment *nextEdge);
void reset(const Point2 &p);
void addEdge(EdgeCache &cache, const EdgeSegment *prevEdge, const EdgeSegment *edge, const EdgeSegment *nextEdge);
DistanceType distance() const;
private:
@ -72,9 +90,10 @@ class MultiDistanceSelector {
public:
typedef MultiDistance DistanceType;
typedef PseudoDistanceSelectorBase::EdgeCache EdgeCache;
explicit MultiDistanceSelector(const Point2 &p = Point2());
void addEdge(const EdgeSegment *prevEdge, const EdgeSegment *edge, const EdgeSegment *nextEdge);
void reset(const Point2 &p);
void addEdge(EdgeCache &cache, const EdgeSegment *prevEdge, const EdgeSegment *edge, const EdgeSegment *nextEdge);
void merge(const MultiDistanceSelector &other);
DistanceType distance() const;
SignedDistance trueDistance() const;
@ -91,7 +110,6 @@ class MultiAndTrueDistanceSelector : public MultiDistanceSelector {
public:
typedef MultiAndTrueDistance DistanceType;
explicit MultiAndTrueDistanceSelector(const Point2 &p = Point2());
DistanceType distance() const;
};

View File

@ -44,11 +44,14 @@ public:
template <class ContourCombiner>
void generateDistanceField(const typename DistancePixelConversion<typename ContourCombiner::DistanceType>::BitmapRefType &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate) {
int edgeCount = shape.edgeCount();
#ifdef MSDFGEN_USE_OPENMP
#pragma omp parallel
#endif
{
ContourCombiner contourCombiner(shape);
std::vector<typename ContourCombiner::EdgeSelectorType::EdgeCache> shapeEdgeCache(edgeCount);
bool rightToLeft = false;
Point2 p;
#ifdef MSDFGEN_USE_OPENMP
#pragma omp for
@ -56,31 +59,32 @@ void generateDistanceField(const typename DistancePixelConversion<typename Conto
for (int y = 0; y < output.height; ++y) {
int row = shape.inverseYAxis ? output.height-y-1 : y;
p.y = (y+.5)/scale.y-translate.y;
for (int x = 0; x < output.width; ++x) {
for (int col = 0; col < output.width; ++col) {
int x = rightToLeft ? output.width-col-1 : col;
p.x = (x+.5)/scale.x-translate.x;
contourCombiner.reset(p);
typename ContourCombiner::EdgeSelectorType::EdgeCache *edgeCache = &shapeEdgeCache[0];
for (std::vector<Contour>::const_iterator contour = shape.contours.begin(); contour != shape.contours.end(); ++contour) {
if (!contour->edges.empty()) {
typename ContourCombiner::EdgeSelectorType edgeSelector(p);
typename ContourCombiner::EdgeSelectorType &edgeSelector = contourCombiner.edgeSelector(int(contour-shape.contours.begin()));
const EdgeSegment *prevEdge = contour->edges.size() >= 2 ? *(contour->edges.end()-2) : *contour->edges.begin();
const EdgeSegment *curEdge = contour->edges.back();
for (std::vector<EdgeHolder>::const_iterator edge = contour->edges.begin(); edge != contour->edges.end(); ++edge) {
const EdgeSegment *nextEdge = *edge;
edgeSelector.addEdge(prevEdge, curEdge, nextEdge);
edgeSelector.addEdge(*edgeCache++, prevEdge, curEdge, nextEdge);
prevEdge = curEdge;
curEdge = nextEdge;
}
contourCombiner.setContourEdgeSelection(int(contour-shape.contours.begin()), edgeSelector);
}
}
typename ContourCombiner::DistanceType distance = contourCombiner.distance();
DistancePixelConversion<typename ContourCombiner::DistanceType>::convert(output(x, row), distance, range);
}
rightToLeft = !rightToLeft;
}
}
}