mirror of https://github.com/Chlumsky/msdfgen.git
Core algorithm performance optimization
This commit is contained in:
parent
5b5272e7a8
commit
8ebb37da4e
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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)) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue