mirror of https://github.com/Chlumsky/msdfgen.git
Added MTSDF mode
This commit is contained in:
parent
b9d6f0bb72
commit
e93c8d988c
|
|
@ -43,9 +43,10 @@ msdfgen.exe <mode> <input> <options>
|
||||||
where only the input specification is required.
|
where only the input specification is required.
|
||||||
|
|
||||||
Mode can be one of:
|
Mode can be one of:
|
||||||
- **sdf** – generates a conventional monochrome signed distance field.
|
- **sdf** – generates a conventional monochrome (true) signed distance field.
|
||||||
- **psdf** – generates a monochrome signed pseudo-distance field.
|
- **psdf** – generates a monochrome signed pseudo-distance field.
|
||||||
- **msdf** (default) – generates a multi-channel signed distance field using my new method.
|
- **msdf** (default) – generates a multi-channel signed distance field using my new method.
|
||||||
|
- **mtsdf** – generates a combined multi-channel and true signed distance field in the alpha channel.
|
||||||
|
|
||||||
The input can be specified as one of:
|
The input can be specified as one of:
|
||||||
- **-font \<filename.ttf\> \<character code\>** – to load a glyph from a font file.
|
- **-font \<filename.ttf\> \<character code\>** – to load a glyph from a font file.
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,7 @@ typename SimpleContourCombiner<EdgeSelector>::DistanceType SimpleContourCombiner
|
||||||
template class SimpleContourCombiner<TrueDistanceSelector>;
|
template class SimpleContourCombiner<TrueDistanceSelector>;
|
||||||
template class SimpleContourCombiner<PseudoDistanceSelector>;
|
template class SimpleContourCombiner<PseudoDistanceSelector>;
|
||||||
template class SimpleContourCombiner<MultiDistanceSelector>;
|
template class SimpleContourCombiner<MultiDistanceSelector>;
|
||||||
|
template class SimpleContourCombiner<MultiAndTrueDistanceSelector>;
|
||||||
|
|
||||||
template <class EdgeSelector>
|
template <class EdgeSelector>
|
||||||
OverlappingContourCombiner<EdgeSelector>::OverlappingContourCombiner(const Shape &shape) {
|
OverlappingContourCombiner<EdgeSelector>::OverlappingContourCombiner(const Shape &shape) {
|
||||||
|
|
@ -118,5 +119,6 @@ typename OverlappingContourCombiner<EdgeSelector>::DistanceType OverlappingConto
|
||||||
template class OverlappingContourCombiner<TrueDistanceSelector>;
|
template class OverlappingContourCombiner<TrueDistanceSelector>;
|
||||||
template class OverlappingContourCombiner<PseudoDistanceSelector>;
|
template class OverlappingContourCombiner<PseudoDistanceSelector>;
|
||||||
template class OverlappingContourCombiner<MultiDistanceSelector>;
|
template class OverlappingContourCombiner<MultiDistanceSelector>;
|
||||||
|
template class OverlappingContourCombiner<MultiAndTrueDistanceSelector>;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -76,6 +76,10 @@ double PseudoDistanceSelectorBase::computeDistance(const Point2 &p) const {
|
||||||
return minDistance;
|
return minDistance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SignedDistance PseudoDistanceSelectorBase::trueDistance() const {
|
||||||
|
return minTrueDistance;
|
||||||
|
}
|
||||||
|
|
||||||
PseudoDistanceSelector::PseudoDistanceSelector(const Point2 &p) : p(p) { }
|
PseudoDistanceSelector::PseudoDistanceSelector(const Point2 &p) : p(p) { }
|
||||||
|
|
||||||
void PseudoDistanceSelector::addEdge(const EdgeSegment *prevEdge, const EdgeSegment *edge, const EdgeSegment *nextEdge) {
|
void PseudoDistanceSelector::addEdge(const EdgeSegment *prevEdge, const EdgeSegment *edge, const EdgeSegment *nextEdge) {
|
||||||
|
|
@ -128,4 +132,25 @@ MultiDistanceSelector::DistanceType MultiDistanceSelector::distance() const {
|
||||||
return multiDistance;
|
return multiDistance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SignedDistance MultiDistanceSelector::trueDistance() const {
|
||||||
|
SignedDistance distance = r.trueDistance();
|
||||||
|
if (g.trueDistance() < distance)
|
||||||
|
distance = g.trueDistance();
|
||||||
|
if (b.trueDistance() < distance)
|
||||||
|
distance = b.trueDistance();
|
||||||
|
return distance;
|
||||||
|
}
|
||||||
|
|
||||||
|
MultiAndTrueDistanceSelector::MultiAndTrueDistanceSelector(const Point2 &p) : MultiDistanceSelector(p) { }
|
||||||
|
|
||||||
|
MultiAndTrueDistanceSelector::DistanceType MultiAndTrueDistanceSelector::distance() const {
|
||||||
|
MultiDistance multiDistance = MultiDistanceSelector::distance();
|
||||||
|
MultiAndTrueDistance mtd;
|
||||||
|
mtd.r = multiDistance.r;
|
||||||
|
mtd.g = multiDistance.g;
|
||||||
|
mtd.b = multiDistance.b;
|
||||||
|
mtd.a = trueDistance().distance;
|
||||||
|
return mtd;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,9 @@ namespace msdfgen {
|
||||||
struct MultiDistance {
|
struct MultiDistance {
|
||||||
double r, g, b;
|
double r, g, b;
|
||||||
};
|
};
|
||||||
|
struct MultiAndTrueDistance : MultiDistance {
|
||||||
|
double a;
|
||||||
|
};
|
||||||
|
|
||||||
/// Selects the nearest edge by its true distance.
|
/// Selects the nearest edge by its true distance.
|
||||||
class TrueDistanceSelector {
|
class TrueDistanceSelector {
|
||||||
|
|
@ -38,6 +41,7 @@ public:
|
||||||
void addEdgePseudoDistance(const SignedDistance &distance);
|
void addEdgePseudoDistance(const SignedDistance &distance);
|
||||||
void merge(const PseudoDistanceSelectorBase &other);
|
void merge(const PseudoDistanceSelectorBase &other);
|
||||||
double computeDistance(const Point2 &p) const;
|
double computeDistance(const Point2 &p) const;
|
||||||
|
SignedDistance trueDistance() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
SignedDistance minTrueDistance;
|
SignedDistance minTrueDistance;
|
||||||
|
|
@ -73,6 +77,7 @@ public:
|
||||||
void addEdge(const EdgeSegment *prevEdge, const EdgeSegment *edge, const EdgeSegment *nextEdge);
|
void addEdge(const EdgeSegment *prevEdge, const EdgeSegment *edge, const EdgeSegment *nextEdge);
|
||||||
void merge(const MultiDistanceSelector &other);
|
void merge(const MultiDistanceSelector &other);
|
||||||
DistanceType distance() const;
|
DistanceType distance() const;
|
||||||
|
SignedDistance trueDistance() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Point2 p;
|
Point2 p;
|
||||||
|
|
@ -80,4 +85,15 @@ private:
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Selects the nearest edge for each of the three color channels by its pseudo-distance and by true distance for the alpha channel.
|
||||||
|
class MultiAndTrueDistanceSelector : public MultiDistanceSelector {
|
||||||
|
|
||||||
|
public:
|
||||||
|
typedef MultiAndTrueDistance DistanceType;
|
||||||
|
|
||||||
|
explicit MultiAndTrueDistanceSelector(const Point2 &p = Point2());
|
||||||
|
DistanceType distance() const;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,8 @@ void scanlineSDF(Scanline &line, const BitmapConstRef<float, 1> &sdf, const Vect
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void scanlineSDF(Scanline &line, const BitmapConstRef<float, 3> &sdf, const Vector2 &scale, const Vector2 &translate, bool inverseYAxis, double y) {
|
template <int N>
|
||||||
|
void scanlineMSDF(Scanline &line, const BitmapConstRef<float, N> &sdf, const Vector2 &scale, const Vector2 &translate, bool inverseYAxis, double y) {
|
||||||
if (!(sdf.width > 0 && sdf.height > 0))
|
if (!(sdf.width > 0 && sdf.height > 0))
|
||||||
return line.setIntersections(std::vector<Scanline::Intersection>());
|
return line.setIntersections(std::vector<Scanline::Intersection>());
|
||||||
double pixelY = clamp(scale.x*(y+translate.y)-.5, double(sdf.height-1));
|
double pixelY = clamp(scale.x*(y+translate.y)-.5, double(sdf.height-1));
|
||||||
|
|
@ -123,46 +124,43 @@ void scanlineSDF(Scanline &line, const BitmapConstRef<float, 3> &sdf, const Vect
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void scanlineSDF(Scanline &line, const BitmapConstRef<float, 3> &sdf, const Vector2 &scale, const Vector2 &translate, bool inverseYAxis, double y) {
|
||||||
|
scanlineMSDF(line, sdf, scale, translate, inverseYAxis, y);
|
||||||
|
}
|
||||||
|
void scanlineSDF(Scanline &line, const BitmapConstRef<float, 4> &sdf, const Vector2 &scale, const Vector2 &translate, bool inverseYAxis, double y) {
|
||||||
|
scanlineMSDF(line, sdf, scale, translate, inverseYAxis, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <int N>
|
||||||
|
double estimateSDFErrorInner(const BitmapConstRef<float, N> &sdf, const Shape &shape, const Vector2 &scale, const Vector2 &translate, int scanlinesPerRow, FillRule fillRule) {
|
||||||
|
if (sdf.width <= 1 || sdf.height <= 1 || scanlinesPerRow < 1)
|
||||||
|
return 0;
|
||||||
|
double subRowSize = 1./scanlinesPerRow;
|
||||||
|
double xFrom = .5/scale.x-translate.x;
|
||||||
|
double xTo = (sdf.width-.5)/scale.x-translate.x;
|
||||||
|
double overlapFactor = 1/(xTo-xFrom);
|
||||||
|
double error = 0;
|
||||||
|
Scanline refScanline, sdfScanline;
|
||||||
|
for (int row = 0; row < sdf.height-1; ++row) {
|
||||||
|
for (int subRow = 0; subRow < scanlinesPerRow; ++subRow) {
|
||||||
|
double bt = (subRow+.5)*subRowSize;
|
||||||
|
double y = (row+bt+.5)/scale.y-translate.y;
|
||||||
|
shape.scanline(refScanline, y);
|
||||||
|
scanlineSDF(sdfScanline, sdf, scale, translate, shape.inverseYAxis, y);
|
||||||
|
error += 1-overlapFactor*Scanline::overlap(refScanline, sdfScanline, xFrom, xTo, fillRule);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return error/((sdf.height-1)*scanlinesPerRow);
|
||||||
|
}
|
||||||
|
|
||||||
double estimateSDFError(const BitmapConstRef<float, 1> &sdf, const Shape &shape, const Vector2 &scale, const Vector2 &translate, int scanlinesPerRow, FillRule fillRule) {
|
double estimateSDFError(const BitmapConstRef<float, 1> &sdf, const Shape &shape, const Vector2 &scale, const Vector2 &translate, int scanlinesPerRow, FillRule fillRule) {
|
||||||
if (sdf.width <= 1 || sdf.height <= 1 || scanlinesPerRow < 1)
|
return estimateSDFErrorInner(sdf, shape, scale, translate, scanlinesPerRow, fillRule);
|
||||||
return 0;
|
|
||||||
double subRowSize = 1./scanlinesPerRow;
|
|
||||||
double xFrom = .5/scale.x-translate.x;
|
|
||||||
double xTo = (sdf.width-.5)/scale.x-translate.x;
|
|
||||||
double overlapFactor = 1/(xTo-xFrom);
|
|
||||||
double error = 0;
|
|
||||||
Scanline refScanline, sdfScanline;
|
|
||||||
for (int row = 0; row < sdf.height-1; ++row) {
|
|
||||||
for (int subRow = 0; subRow < scanlinesPerRow; ++subRow) {
|
|
||||||
double bt = (subRow+.5)*subRowSize;
|
|
||||||
double y = (row+bt+.5)/scale.y-translate.y;
|
|
||||||
shape.scanline(refScanline, y);
|
|
||||||
scanlineSDF(sdfScanline, sdf, scale, translate, shape.inverseYAxis, y);
|
|
||||||
error += 1-overlapFactor*Scanline::overlap(refScanline, sdfScanline, xFrom, xTo, fillRule);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return error/((sdf.height-1)*scanlinesPerRow);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
double estimateSDFError(const BitmapConstRef<float, 3> &sdf, const Shape &shape, const Vector2 &scale, const Vector2 &translate, int scanlinesPerRow, FillRule fillRule) {
|
double estimateSDFError(const BitmapConstRef<float, 3> &sdf, const Shape &shape, const Vector2 &scale, const Vector2 &translate, int scanlinesPerRow, FillRule fillRule) {
|
||||||
if (sdf.width <= 1 || sdf.height <= 1 || scanlinesPerRow < 1)
|
return estimateSDFErrorInner(sdf, shape, scale, translate, scanlinesPerRow, fillRule);
|
||||||
return 0;
|
}
|
||||||
double subRowSize = 1./scanlinesPerRow;
|
double estimateSDFError(const BitmapConstRef<float, 4> &sdf, const Shape &shape, const Vector2 &scale, const Vector2 &translate, int scanlinesPerRow, FillRule fillRule) {
|
||||||
double xFrom = .5/scale.x-translate.x;
|
return estimateSDFErrorInner(sdf, shape, scale, translate, scanlinesPerRow, fillRule);
|
||||||
double xTo = (sdf.width-.5)/scale.x-translate.x;
|
|
||||||
double overlapFactor = 1/(xTo-xFrom);
|
|
||||||
double error = 0;
|
|
||||||
Scanline refScanline, sdfScanline;
|
|
||||||
for (int row = 0; row < sdf.height-1; ++row) {
|
|
||||||
for (int subRow = 0; subRow < scanlinesPerRow; ++subRow) {
|
|
||||||
double bt = (subRow+.5)*subRowSize;
|
|
||||||
double y = (row+bt+.5)/scale.y-translate.y;
|
|
||||||
shape.scanline(refScanline, y);
|
|
||||||
scanlineSDF(sdfScanline, sdf, scale, translate, shape.inverseYAxis, y);
|
|
||||||
error += 1-overlapFactor*Scanline::overlap(refScanline, sdfScanline, xFrom, xTo, fillRule);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return error/((sdf.height-1)*scanlinesPerRow);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,9 +11,11 @@ namespace msdfgen {
|
||||||
/// Analytically constructs a scanline at y evaluating fill by linear interpolation of the SDF.
|
/// Analytically constructs a scanline at y evaluating fill by linear interpolation of the SDF.
|
||||||
void scanlineSDF(Scanline &line, const BitmapConstRef<float, 1> &sdf, const Vector2 &scale, const Vector2 &translate, bool inverseYAxis, double y);
|
void scanlineSDF(Scanline &line, const BitmapConstRef<float, 1> &sdf, const Vector2 &scale, const Vector2 &translate, bool inverseYAxis, double y);
|
||||||
void scanlineSDF(Scanline &line, const BitmapConstRef<float, 3> &sdf, const Vector2 &scale, const Vector2 &translate, bool inverseYAxis, double y);
|
void scanlineSDF(Scanline &line, const BitmapConstRef<float, 3> &sdf, const Vector2 &scale, const Vector2 &translate, bool inverseYAxis, double y);
|
||||||
|
void scanlineSDF(Scanline &line, const BitmapConstRef<float, 4> &sdf, const Vector2 &scale, const Vector2 &translate, bool inverseYAxis, double y);
|
||||||
|
|
||||||
/// Estimates the portion of the area that will be filled incorrectly when rendering using the SDF.
|
/// Estimates the portion of the area that will be filled incorrectly when rendering using the SDF.
|
||||||
double estimateSDFError(const BitmapConstRef<float, 1> &sdf, const Shape &shape, const Vector2 &scale, const Vector2 &translate, int scanlinesPerRow, FillRule fillRule = FILL_NONZERO);
|
double estimateSDFError(const BitmapConstRef<float, 1> &sdf, const Shape &shape, const Vector2 &scale, const Vector2 &translate, int scanlinesPerRow, FillRule fillRule = FILL_NONZERO);
|
||||||
double estimateSDFError(const BitmapConstRef<float, 3> &sdf, const Shape &shape, const Vector2 &scale, const Vector2 &translate, int scanlinesPerRow, FillRule fillRule = FILL_NONZERO);
|
double estimateSDFError(const BitmapConstRef<float, 3> &sdf, const Shape &shape, const Vector2 &scale, const Vector2 &translate, int scanlinesPerRow, FillRule fillRule = FILL_NONZERO);
|
||||||
|
double estimateSDFError(const BitmapConstRef<float, 4> &sdf, const Shape &shape, const Vector2 &scale, const Vector2 &translate, int scanlinesPerRow, FillRule fillRule = FILL_NONZERO);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,18 @@ public:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
class DistancePixelConversion<MultiAndTrueDistance> {
|
||||||
|
public:
|
||||||
|
typedef BitmapRef<float, 4> BitmapRefType;
|
||||||
|
inline static void convert(float *pixels, const MultiAndTrueDistance &distance, double range) {
|
||||||
|
pixels[0] = float(distance.r/range+.5);
|
||||||
|
pixels[1] = float(distance.g/range+.5);
|
||||||
|
pixels[2] = float(distance.b/range+.5);
|
||||||
|
pixels[3] = float(distance.a/range+.5);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
template <class ContourCombiner>
|
template <class ContourCombiner>
|
||||||
void generateDistanceField(const typename DistancePixelConversion<typename ContourCombiner::DistanceType>::BitmapRefType &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate) {
|
void generateDistanceField(const typename DistancePixelConversion<typename ContourCombiner::DistanceType>::BitmapRefType &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate) {
|
||||||
#ifdef MSDFGEN_USE_OPENMP
|
#ifdef MSDFGEN_USE_OPENMP
|
||||||
|
|
@ -96,6 +108,15 @@ void generateMSDF(const BitmapRef<float, 3> &output, const Shape &shape, double
|
||||||
msdfErrorCorrection(output, edgeThreshold/(scale*range));
|
msdfErrorCorrection(output, edgeThreshold/(scale*range));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void generateMTSDF(const BitmapRef<float, 4> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, double edgeThreshold, bool overlapSupport) {
|
||||||
|
if (overlapSupport)
|
||||||
|
generateDistanceField<OverlappingContourCombiner<MultiAndTrueDistanceSelector> >(output, shape, range, scale, translate);
|
||||||
|
else
|
||||||
|
generateDistanceField<SimpleContourCombiner<MultiAndTrueDistanceSelector> >(output, shape, range, scale, translate);
|
||||||
|
if (edgeThreshold > 0)
|
||||||
|
msdfErrorCorrection(output, edgeThreshold/(scale*range));
|
||||||
|
}
|
||||||
|
|
||||||
inline static bool detectClash(const float *a, const float *b, double threshold) {
|
inline static bool detectClash(const float *a, const float *b, double threshold) {
|
||||||
// Sort channels so that pairs (a0, b0), (a1, b1), (a2, b2) go from biggest to smallest absolute difference
|
// Sort channels so that pairs (a0, b0), (a1, b1), (a2, b2) go from biggest to smallest absolute difference
|
||||||
float a0 = a[0], a1 = a[1], a2 = a[2];
|
float a0 = a[0], a1 = a[1], a2 = a[2];
|
||||||
|
|
@ -118,7 +139,8 @@ inline static bool detectClash(const float *a, const float *b, double threshold)
|
||||||
fabsf(a2-.5f) >= fabsf(b2-.5f); // Out of the pair, only flag the pixel farther from a shape edge
|
fabsf(a2-.5f) >= fabsf(b2-.5f); // Out of the pair, only flag the pixel farther from a shape edge
|
||||||
}
|
}
|
||||||
|
|
||||||
void msdfErrorCorrection(const BitmapRef<float, 3> &output, const Vector2 &threshold) {
|
template <int N>
|
||||||
|
void msdfErrorCorrectionInner(const BitmapRef<float, N> &output, const Vector2 &threshold) {
|
||||||
std::vector<std::pair<int, int> > clashes;
|
std::vector<std::pair<int, int> > clashes;
|
||||||
int w = output.width, h = output.height;
|
int w = output.width, h = output.height;
|
||||||
for (int y = 0; y < h; ++y)
|
for (int y = 0; y < h; ++y)
|
||||||
|
|
@ -156,6 +178,13 @@ void msdfErrorCorrection(const BitmapRef<float, 3> &output, const Vector2 &thres
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void msdfErrorCorrection(const BitmapRef<float, 3> &output, const Vector2 &threshold) {
|
||||||
|
msdfErrorCorrectionInner(output, threshold);
|
||||||
|
}
|
||||||
|
void msdfErrorCorrection(const BitmapRef<float, 4> &output, const Vector2 &threshold) {
|
||||||
|
msdfErrorCorrectionInner(output, threshold);
|
||||||
|
}
|
||||||
|
|
||||||
// Legacy version
|
// Legacy version
|
||||||
|
|
||||||
void generateSDF_legacy(const BitmapRef<float, 1> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate) {
|
void generateSDF_legacy(const BitmapRef<float, 1> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate) {
|
||||||
|
|
@ -261,4 +290,62 @@ void generateMSDF_legacy(const BitmapRef<float, 3> &output, const Shape &shape,
|
||||||
msdfErrorCorrection(output, edgeThreshold/(scale*range));
|
msdfErrorCorrection(output, edgeThreshold/(scale*range));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void generateMTSDF_legacy(const BitmapRef<float, 4> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, double edgeThreshold) {
|
||||||
|
#ifdef MSDFGEN_USE_OPENMP
|
||||||
|
#pragma omp parallel for
|
||||||
|
#endif
|
||||||
|
for (int y = 0; y < output.height; ++y) {
|
||||||
|
int row = shape.inverseYAxis ? output.height-y-1 : y;
|
||||||
|
for (int x = 0; x < output.width; ++x) {
|
||||||
|
Point2 p = Vector2(x+.5, y+.5)/scale-translate;
|
||||||
|
|
||||||
|
SignedDistance minDistance;
|
||||||
|
struct {
|
||||||
|
SignedDistance minDistance;
|
||||||
|
const EdgeHolder *nearEdge;
|
||||||
|
double nearParam;
|
||||||
|
} r, g, b;
|
||||||
|
r.nearEdge = g.nearEdge = b.nearEdge = NULL;
|
||||||
|
r.nearParam = g.nearParam = b.nearParam = 0;
|
||||||
|
|
||||||
|
for (std::vector<Contour>::const_iterator contour = shape.contours.begin(); contour != shape.contours.end(); ++contour)
|
||||||
|
for (std::vector<EdgeHolder>::const_iterator edge = contour->edges.begin(); edge != contour->edges.end(); ++edge) {
|
||||||
|
double param;
|
||||||
|
SignedDistance distance = (*edge)->signedDistance(p, param);
|
||||||
|
if (distance < minDistance)
|
||||||
|
minDistance = distance;
|
||||||
|
if ((*edge)->color&RED && distance < r.minDistance) {
|
||||||
|
r.minDistance = distance;
|
||||||
|
r.nearEdge = &*edge;
|
||||||
|
r.nearParam = param;
|
||||||
|
}
|
||||||
|
if ((*edge)->color&GREEN && distance < g.minDistance) {
|
||||||
|
g.minDistance = distance;
|
||||||
|
g.nearEdge = &*edge;
|
||||||
|
g.nearParam = param;
|
||||||
|
}
|
||||||
|
if ((*edge)->color&BLUE && distance < b.minDistance) {
|
||||||
|
b.minDistance = distance;
|
||||||
|
b.nearEdge = &*edge;
|
||||||
|
b.nearParam = param;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (r.nearEdge)
|
||||||
|
(*r.nearEdge)->distanceToPseudoDistance(r.minDistance, p, r.nearParam);
|
||||||
|
if (g.nearEdge)
|
||||||
|
(*g.nearEdge)->distanceToPseudoDistance(g.minDistance, p, g.nearParam);
|
||||||
|
if (b.nearEdge)
|
||||||
|
(*b.nearEdge)->distanceToPseudoDistance(b.minDistance, p, b.nearParam);
|
||||||
|
output(x, row)[0] = float(r.minDistance.distance/range+.5);
|
||||||
|
output(x, row)[1] = float(g.minDistance.distance/range+.5);
|
||||||
|
output(x, row)[2] = float(b.minDistance.distance/range+.5);
|
||||||
|
output(x, row)[3] = float(minDistance.distance/range+.5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (edgeThreshold > 0)
|
||||||
|
msdfErrorCorrection(output, edgeThreshold/(scale*range));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,8 @@ void distanceSignCorrection(const BitmapRef<float, 1> &sdf, const Shape &shape,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void distanceSignCorrection(const BitmapRef<float, 3> &sdf, const Shape &shape, const Vector2 &scale, const Vector2 &translate, FillRule fillRule) {
|
template <int N>
|
||||||
|
static void multiDistanceSignCorrection(const BitmapRef<float, N> &sdf, const Shape &shape, const Vector2 &scale, const Vector2 &translate, FillRule fillRule) {
|
||||||
int w = sdf.width, h = sdf.height;
|
int w = sdf.width, h = sdf.height;
|
||||||
if (!(w*h))
|
if (!(w*h))
|
||||||
return;
|
return;
|
||||||
|
|
@ -66,6 +67,8 @@ void distanceSignCorrection(const BitmapRef<float, 3> &sdf, const Shape &shape,
|
||||||
*match = -1;
|
*match = -1;
|
||||||
} else
|
} else
|
||||||
*match = 1;
|
*match = 1;
|
||||||
|
if (N >= 4 && (msd[3] > .5f) != fill)
|
||||||
|
msd[3] = 1.f-msd[3];
|
||||||
++match;
|
++match;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -94,4 +97,12 @@ void distanceSignCorrection(const BitmapRef<float, 3> &sdf, const Shape &shape,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void distanceSignCorrection(const BitmapRef<float, 3> &sdf, const Shape &shape, const Vector2 &scale, const Vector2 &translate, FillRule fillRule) {
|
||||||
|
multiDistanceSignCorrection(sdf, shape, scale, translate, fillRule);
|
||||||
|
}
|
||||||
|
|
||||||
|
void distanceSignCorrection(const BitmapRef<float, 4> &sdf, const Shape &shape, const Vector2 &scale, const Vector2 &translate, FillRule fillRule) {
|
||||||
|
multiDistanceSignCorrection(sdf, shape, scale, translate, fillRule);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,5 +13,6 @@ void rasterize(const BitmapRef<float, 1> &output, const Shape &shape, const Vect
|
||||||
/// Fixes the sign of the input signed distance field, so that it matches the shape's rasterized fill.
|
/// Fixes the sign of the input signed distance field, so that it matches the shape's rasterized fill.
|
||||||
void distanceSignCorrection(const BitmapRef<float, 1> &sdf, const Shape &shape, const Vector2 &scale, const Vector2 &translate, FillRule fillRule = FILL_NONZERO);
|
void distanceSignCorrection(const BitmapRef<float, 1> &sdf, const Shape &shape, const Vector2 &scale, const Vector2 &translate, FillRule fillRule = FILL_NONZERO);
|
||||||
void distanceSignCorrection(const BitmapRef<float, 3> &sdf, const Shape &shape, const Vector2 &scale, const Vector2 &translate, FillRule fillRule = FILL_NONZERO);
|
void distanceSignCorrection(const BitmapRef<float, 3> &sdf, const Shape &shape, const Vector2 &scale, const Vector2 &translate, FillRule fillRule = FILL_NONZERO);
|
||||||
|
void distanceSignCorrection(const BitmapRef<float, 4> &sdf, const Shape &shape, const Vector2 &scale, const Vector2 &translate, FillRule fillRule = FILL_NONZERO);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -73,6 +73,29 @@ void renderSDF(const BitmapRef<float, 3> &output, const BitmapConstRef<float, 3>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void renderSDF(const BitmapRef<float, 1> &output, const BitmapConstRef<float, 4> &sdf, double pxRange) {
|
||||||
|
pxRange *= (double) (output.width+output.height)/(sdf.width+sdf.height);
|
||||||
|
for (int y = 0; y < output.height; ++y)
|
||||||
|
for (int x = 0; x < output.width; ++x) {
|
||||||
|
float sd[4];
|
||||||
|
sample(sd, sdf, Point2((x+.5)/output.width, (y+.5)/output.height));
|
||||||
|
*output(x, y) = distVal(median(sd[0], sd[1], sd[2]), pxRange);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void renderSDF(const BitmapRef<float, 4> &output, const BitmapConstRef<float, 4> &sdf, double pxRange) {
|
||||||
|
pxRange *= (double) (output.width+output.height)/(sdf.width+sdf.height);
|
||||||
|
for (int y = 0; y < output.height; ++y)
|
||||||
|
for (int x = 0; x < output.width; ++x) {
|
||||||
|
float sd[4];
|
||||||
|
sample(sd, sdf, Point2((x+.5)/output.width, (y+.5)/output.height));
|
||||||
|
output(x, y)[0] = distVal(sd[0], pxRange);
|
||||||
|
output(x, y)[1] = distVal(sd[1], pxRange);
|
||||||
|
output(x, y)[2] = distVal(sd[2], pxRange);
|
||||||
|
output(x, y)[3] = distVal(sd[3], pxRange);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void simulate8bit(const BitmapRef<float, 1> &bitmap) {
|
void simulate8bit(const BitmapRef<float, 1> &bitmap) {
|
||||||
const float *end = bitmap.pixels+1*bitmap.width*bitmap.height;
|
const float *end = bitmap.pixels+1*bitmap.width*bitmap.height;
|
||||||
for (float *p = bitmap.pixels; p < end; ++p)
|
for (float *p = bitmap.pixels; p < end; ++p)
|
||||||
|
|
@ -85,4 +108,10 @@ void simulate8bit(const BitmapRef<float, 3> &bitmap) {
|
||||||
*p = pixelByteToFloat(pixelFloatToByte(*p));
|
*p = pixelByteToFloat(pixelFloatToByte(*p));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void simulate8bit(const BitmapRef<float, 4> &bitmap) {
|
||||||
|
const float *end = bitmap.pixels+4*bitmap.width*bitmap.height;
|
||||||
|
for (float *p = bitmap.pixels; p < end; ++p)
|
||||||
|
*p = pixelByteToFloat(pixelFloatToByte(*p));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,9 +11,12 @@ void renderSDF(const BitmapRef<float, 1> &output, const BitmapConstRef<float, 1>
|
||||||
void renderSDF(const BitmapRef<float, 3> &output, const BitmapConstRef<float, 1> &sdf, double pxRange = 0);
|
void renderSDF(const BitmapRef<float, 3> &output, const BitmapConstRef<float, 1> &sdf, double pxRange = 0);
|
||||||
void renderSDF(const BitmapRef<float, 1> &output, const BitmapConstRef<float, 3> &sdf, double pxRange = 0);
|
void renderSDF(const BitmapRef<float, 1> &output, const BitmapConstRef<float, 3> &sdf, double pxRange = 0);
|
||||||
void renderSDF(const BitmapRef<float, 3> &output, const BitmapConstRef<float, 3> &sdf, double pxRange = 0);
|
void renderSDF(const BitmapRef<float, 3> &output, const BitmapConstRef<float, 3> &sdf, double pxRange = 0);
|
||||||
|
void renderSDF(const BitmapRef<float, 1> &output, const BitmapConstRef<float, 4> &sdf, double pxRange = 0);
|
||||||
|
void renderSDF(const BitmapRef<float, 4> &output, const BitmapConstRef<float, 4> &sdf, double pxRange = 0);
|
||||||
|
|
||||||
/// Snaps the values of the floating-point bitmaps into one of the 256 values representable in a standard 8-bit bitmap.
|
/// Snaps the values of the floating-point bitmaps into one of the 256 values representable in a standard 8-bit bitmap.
|
||||||
void simulate8bit(const BitmapRef<float, 1> &bitmap);
|
void simulate8bit(const BitmapRef<float, 1> &bitmap);
|
||||||
void simulate8bit(const BitmapRef<float, 3> &bitmap);
|
void simulate8bit(const BitmapRef<float, 3> &bitmap);
|
||||||
|
void simulate8bit(const BitmapRef<float, 4> &bitmap);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -108,6 +108,11 @@ bool saveBmp(const BitmapConstRef<byte, 3> &bitmap, const char *filename) {
|
||||||
return !fclose(file);
|
return !fclose(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool saveBmp(const BitmapConstRef<byte, 4> &bitmap, const char *filename) {
|
||||||
|
// RGBA not supported by the BMP format
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool saveBmp(const BitmapConstRef<float, 1> &bitmap, const char *filename) {
|
bool saveBmp(const BitmapConstRef<float, 1> &bitmap, const char *filename) {
|
||||||
FILE *file = fopen(filename, "wb");
|
FILE *file = fopen(filename, "wb");
|
||||||
if (!file)
|
if (!file)
|
||||||
|
|
@ -156,4 +161,9 @@ bool saveBmp(const BitmapConstRef<float, 3> &bitmap, const char *filename) {
|
||||||
return !fclose(file);
|
return !fclose(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool saveBmp(const BitmapConstRef<float, 4> &bitmap, const char *filename) {
|
||||||
|
// RGBA not supported by the BMP format
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,9 @@ namespace msdfgen {
|
||||||
/// Saves the bitmap as a BMP file.
|
/// Saves the bitmap as a BMP file.
|
||||||
bool saveBmp(const BitmapConstRef<byte, 1> &bitmap, const char *filename);
|
bool saveBmp(const BitmapConstRef<byte, 1> &bitmap, const char *filename);
|
||||||
bool saveBmp(const BitmapConstRef<byte, 3> &bitmap, const char *filename);
|
bool saveBmp(const BitmapConstRef<byte, 3> &bitmap, const char *filename);
|
||||||
|
bool saveBmp(const BitmapConstRef<byte, 4> &bitmap, const char *filename);
|
||||||
bool saveBmp(const BitmapConstRef<float, 1> &bitmap, const char *filename);
|
bool saveBmp(const BitmapConstRef<float, 1> &bitmap, const char *filename);
|
||||||
bool saveBmp(const BitmapConstRef<float, 3> &bitmap, const char *filename);
|
bool saveBmp(const BitmapConstRef<float, 3> &bitmap, const char *filename);
|
||||||
|
bool saveBmp(const BitmapConstRef<float, 4> &bitmap, const char *filename);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,11 @@ template <typename T>
|
||||||
static bool writeValue(FILE *file, T value) {
|
static bool writeValue(FILE *file, T value) {
|
||||||
return fwrite(&value, sizeof(T), 1, file) == 1;
|
return fwrite(&value, sizeof(T), 1, file) == 1;
|
||||||
}
|
}
|
||||||
|
template <typename T>
|
||||||
|
static void writeValueRepeated(FILE *file, T value, int times) {
|
||||||
|
for (int i = 0; i < times; ++i)
|
||||||
|
writeValue(file, value);
|
||||||
|
}
|
||||||
|
|
||||||
static bool writeTiffHeader(FILE *file, int width, int height, int channels) {
|
static bool writeTiffHeader(FILE *file, int width, int height, int channels) {
|
||||||
#ifdef __BIG_ENDIAN__
|
#ifdef __BIG_ENDIAN__
|
||||||
|
|
@ -47,8 +52,8 @@ static bool writeTiffHeader(FILE *file, int width, int height, int channels) {
|
||||||
writeValue<uint16_t>(file, 0x0102u);
|
writeValue<uint16_t>(file, 0x0102u);
|
||||||
writeValue<uint16_t>(file, 0x0003u);
|
writeValue<uint16_t>(file, 0x0003u);
|
||||||
writeValue<uint32_t>(file, channels);
|
writeValue<uint32_t>(file, channels);
|
||||||
if (channels == 3)
|
if (channels > 1)
|
||||||
writeValue<uint32_t>(file, 0x00c2u); // Offset of 32, 32, 32
|
writeValue<uint32_t>(file, 0x00c2u); // Offset of 32, 32, ...
|
||||||
else {
|
else {
|
||||||
writeValue<uint16_t>(file, 32);
|
writeValue<uint16_t>(file, 32);
|
||||||
writeValue<uint16_t>(file, 0);
|
writeValue<uint16_t>(file, 0);
|
||||||
|
|
@ -63,13 +68,13 @@ static bool writeTiffHeader(FILE *file, int width, int height, int channels) {
|
||||||
writeValue<uint16_t>(file, 0x0106u);
|
writeValue<uint16_t>(file, 0x0106u);
|
||||||
writeValue<uint16_t>(file, 0x0003u);
|
writeValue<uint16_t>(file, 0x0003u);
|
||||||
writeValue<uint32_t>(file, 1);
|
writeValue<uint32_t>(file, 1);
|
||||||
writeValue<uint16_t>(file, channels == 3 ? 2 : 1);
|
writeValue<uint16_t>(file, channels >= 3 ? 2 : 1);
|
||||||
writeValue<uint16_t>(file, 0);
|
writeValue<uint16_t>(file, 0);
|
||||||
// StripOffsets
|
// StripOffsets
|
||||||
writeValue<uint16_t>(file, 0x0111u);
|
writeValue<uint16_t>(file, 0x0111u);
|
||||||
writeValue<uint16_t>(file, 0x0004u);
|
writeValue<uint16_t>(file, 0x0004u);
|
||||||
writeValue<uint32_t>(file, 1);
|
writeValue<uint32_t>(file, 1);
|
||||||
writeValue<uint32_t>(file, channels == 3 ? 0x00f6u : 0x00d2u); // Offset of pixel data
|
writeValue<uint32_t>(file, 0x00d2u+(channels > 1)*channels*12); // Offset of pixel data
|
||||||
// SamplesPerPixel
|
// SamplesPerPixel
|
||||||
writeValue<uint16_t>(file, 0x0115u);
|
writeValue<uint16_t>(file, 0x0115u);
|
||||||
writeValue<uint16_t>(file, 0x0003u);
|
writeValue<uint16_t>(file, 0x0003u);
|
||||||
|
|
@ -90,12 +95,12 @@ static bool writeTiffHeader(FILE *file, int width, int height, int channels) {
|
||||||
writeValue<uint16_t>(file, 0x011au);
|
writeValue<uint16_t>(file, 0x011au);
|
||||||
writeValue<uint16_t>(file, 0x0005u);
|
writeValue<uint16_t>(file, 0x0005u);
|
||||||
writeValue<uint32_t>(file, 1);
|
writeValue<uint32_t>(file, 1);
|
||||||
writeValue<uint32_t>(file, channels == 3 ? 0x00c8u : 0x00c2u); // Offset of 300, 1
|
writeValue<uint32_t>(file, 0x00c2u+(channels > 1)*channels*2); // Offset of 300, 1
|
||||||
// YResolution
|
// YResolution
|
||||||
writeValue<uint16_t>(file, 0x011bu);
|
writeValue<uint16_t>(file, 0x011bu);
|
||||||
writeValue<uint16_t>(file, 0x0005u);
|
writeValue<uint16_t>(file, 0x0005u);
|
||||||
writeValue<uint32_t>(file, 1);
|
writeValue<uint32_t>(file, 1);
|
||||||
writeValue<uint32_t>(file, channels == 3 ? 0x00d0u : 0x00cau); // Offset of 300, 1
|
writeValue<uint32_t>(file, 0x00cau+(channels > 1)*channels*2); // Offset of 300, 1
|
||||||
// ResolutionUnit
|
// ResolutionUnit
|
||||||
writeValue<uint16_t>(file, 0x0128u);
|
writeValue<uint16_t>(file, 0x0128u);
|
||||||
writeValue<uint16_t>(file, 0x0003u);
|
writeValue<uint16_t>(file, 0x0003u);
|
||||||
|
|
@ -106,8 +111,8 @@ static bool writeTiffHeader(FILE *file, int width, int height, int channels) {
|
||||||
writeValue<uint16_t>(file, 0x0153u);
|
writeValue<uint16_t>(file, 0x0153u);
|
||||||
writeValue<uint16_t>(file, 0x0003u);
|
writeValue<uint16_t>(file, 0x0003u);
|
||||||
writeValue<uint32_t>(file, channels);
|
writeValue<uint32_t>(file, channels);
|
||||||
if (channels == 3)
|
if (channels > 1)
|
||||||
writeValue<uint32_t>(file, 0x00d8u); // Offset of 3, 3, 3
|
writeValue<uint32_t>(file, 0x00d2u+channels*2); // Offset of 3, 3, ...
|
||||||
else {
|
else {
|
||||||
writeValue<uint16_t>(file, 3);
|
writeValue<uint16_t>(file, 3);
|
||||||
writeValue<uint16_t>(file, 0);
|
writeValue<uint16_t>(file, 0);
|
||||||
|
|
@ -116,46 +121,38 @@ static bool writeTiffHeader(FILE *file, int width, int height, int channels) {
|
||||||
writeValue<uint16_t>(file, 0x0154u);
|
writeValue<uint16_t>(file, 0x0154u);
|
||||||
writeValue<uint16_t>(file, 0x000bu);
|
writeValue<uint16_t>(file, 0x000bu);
|
||||||
writeValue<uint32_t>(file, channels);
|
writeValue<uint32_t>(file, channels);
|
||||||
if (channels == 3)
|
if (channels > 1)
|
||||||
writeValue<uint32_t>(file, 0x00deu); // Offset of 0.f, 0.f, 0.f
|
writeValue<uint32_t>(file, 0x00d2u+channels*4); // Offset of 0.f, 0.f, ...
|
||||||
else
|
else
|
||||||
writeValue<float>(file, 0.f);
|
writeValue<float>(file, 0.f);
|
||||||
// SMaxSampleValue
|
// SMaxSampleValue
|
||||||
writeValue<uint16_t>(file, 0x0155u);
|
writeValue<uint16_t>(file, 0x0155u);
|
||||||
writeValue<uint16_t>(file, 0x000bu);
|
writeValue<uint16_t>(file, 0x000bu);
|
||||||
writeValue<uint32_t>(file, channels);
|
writeValue<uint32_t>(file, channels);
|
||||||
if (channels == 3)
|
if (channels > 1)
|
||||||
writeValue<uint32_t>(file, 0x00eau); // Offset of 1.f, 1.f, 1.f
|
writeValue<uint32_t>(file, 0x00d2u+channels*8); // Offset of 1.f, 1.f, ...
|
||||||
else
|
else
|
||||||
writeValue<float>(file, 1.f);
|
writeValue<float>(file, 1.f);
|
||||||
// Offset = 0x00be
|
// Offset = 0x00be
|
||||||
|
|
||||||
writeValue<uint32_t>(file, 0);
|
writeValue<uint32_t>(file, 0);
|
||||||
|
|
||||||
if (channels == 3) {
|
if (channels > 1) {
|
||||||
// 0x00c2 BitsPerSample data
|
// 0x00c2 BitsPerSample data
|
||||||
writeValue<uint16_t>(file, 32);
|
writeValueRepeated<uint16_t>(file, 32, channels);
|
||||||
writeValue<uint16_t>(file, 32);
|
// 0x00c2 + 2*N XResolution data
|
||||||
writeValue<uint16_t>(file, 32);
|
|
||||||
// 0x00c8 XResolution data
|
|
||||||
writeValue<uint32_t>(file, 300);
|
writeValue<uint32_t>(file, 300);
|
||||||
writeValue<uint32_t>(file, 1);
|
writeValue<uint32_t>(file, 1);
|
||||||
// 0x00d0 YResolution data
|
// 0x00ca + 2*N YResolution data
|
||||||
writeValue<uint32_t>(file, 300);
|
writeValue<uint32_t>(file, 300);
|
||||||
writeValue<uint32_t>(file, 1);
|
writeValue<uint32_t>(file, 1);
|
||||||
// 0x00d8 SampleFormat data
|
// 0x00d2 + 2*N SampleFormat data
|
||||||
writeValue<uint16_t>(file, 3);
|
writeValueRepeated<uint16_t>(file, 3, channels);
|
||||||
writeValue<uint16_t>(file, 3);
|
// 0x00d2 + 4*N SMinSampleValue data
|
||||||
writeValue<uint16_t>(file, 3);
|
writeValueRepeated<float>(file, 0.f, channels);
|
||||||
// 0x00de SMinSampleValue data
|
// 0x00d2 + 8*N SMaxSampleValue data
|
||||||
writeValue<float>(file, 0.f);
|
writeValueRepeated<float>(file, 1.f, channels);
|
||||||
writeValue<float>(file, 0.f);
|
// Offset = 0x00d2 + 12*N
|
||||||
writeValue<float>(file, 0.f);
|
|
||||||
// 0x00ea SMaxSampleValue data
|
|
||||||
writeValue<float>(file, 1.f);
|
|
||||||
writeValue<float>(file, 1.f);
|
|
||||||
writeValue<float>(file, 1.f);
|
|
||||||
// Offset = 0x00f6
|
|
||||||
} else {
|
} else {
|
||||||
// 0x00c2 XResolution data
|
// 0x00c2 XResolution data
|
||||||
writeValue<uint32_t>(file, 300);
|
writeValue<uint32_t>(file, 300);
|
||||||
|
|
@ -169,24 +166,25 @@ static bool writeTiffHeader(FILE *file, int width, int height, int channels) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <int N>
|
||||||
|
bool saveTiffFloat(const BitmapConstRef<float, N> &bitmap, const char *filename) {
|
||||||
|
FILE *file = fopen(filename, "wb");
|
||||||
|
if (!file)
|
||||||
|
return false;
|
||||||
|
writeTiffHeader(file, bitmap.width, bitmap.height, N);
|
||||||
|
for (int y = bitmap.height-1; y >= 0; --y)
|
||||||
|
fwrite(bitmap(0, y), sizeof(float), N*bitmap.width, file);
|
||||||
|
return !fclose(file);
|
||||||
|
}
|
||||||
|
|
||||||
bool saveTiff(const BitmapConstRef<float, 1> &bitmap, const char *filename) {
|
bool saveTiff(const BitmapConstRef<float, 1> &bitmap, const char *filename) {
|
||||||
FILE *file = fopen(filename, "wb");
|
return saveTiffFloat(bitmap, filename);
|
||||||
if (!file)
|
|
||||||
return false;
|
|
||||||
writeTiffHeader(file, bitmap.width, bitmap.height, 1);
|
|
||||||
for (int y = bitmap.height-1; y >= 0; --y)
|
|
||||||
fwrite(bitmap(0, y), sizeof(float), bitmap.width, file);
|
|
||||||
return !fclose(file);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool saveTiff(const BitmapConstRef<float, 3> &bitmap, const char *filename) {
|
bool saveTiff(const BitmapConstRef<float, 3> &bitmap, const char *filename) {
|
||||||
FILE *file = fopen(filename, "wb");
|
return saveTiffFloat(bitmap, filename);
|
||||||
if (!file)
|
}
|
||||||
return false;
|
bool saveTiff(const BitmapConstRef<float, 4> &bitmap, const char *filename) {
|
||||||
writeTiffHeader(file, bitmap.width, bitmap.height, 3);
|
return saveTiffFloat(bitmap, filename);
|
||||||
for (int y = bitmap.height-1; y >= 0; --y)
|
|
||||||
fwrite(bitmap(0, y), sizeof(float), 3*bitmap.width, file);
|
|
||||||
return !fclose(file);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,5 +8,6 @@ namespace msdfgen {
|
||||||
/// Saves the bitmap as an uncompressed floating-point TIFF file.
|
/// Saves the bitmap as an uncompressed floating-point TIFF file.
|
||||||
bool saveTiff(const BitmapConstRef<float, 1> &bitmap, const char *filename);
|
bool saveTiff(const BitmapConstRef<float, 1> &bitmap, const char *filename);
|
||||||
bool saveTiff(const BitmapConstRef<float, 3> &bitmap, const char *filename);
|
bool saveTiff(const BitmapConstRef<float, 3> &bitmap, const char *filename);
|
||||||
|
bool saveTiff(const BitmapConstRef<float, 4> &bitmap, const char *filename);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,13 @@ bool savePng(const BitmapConstRef<byte, 3> &bitmap, const char *filename) {
|
||||||
return !lodepng::encode(filename, pixels, bitmap.width, bitmap.height, LCT_RGB);
|
return !lodepng::encode(filename, pixels, bitmap.width, bitmap.height, LCT_RGB);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool savePng(const BitmapConstRef<byte, 4> &bitmap, const char *filename) {
|
||||||
|
std::vector<byte> pixels(4*bitmap.width*bitmap.height);
|
||||||
|
for (int y = 0; y < bitmap.height; ++y)
|
||||||
|
memcpy(&pixels[4*bitmap.width*y], bitmap(0, bitmap.height-y-1), 4*bitmap.width);
|
||||||
|
return !lodepng::encode(filename, pixels, bitmap.width, bitmap.height, LCT_RGBA);
|
||||||
|
}
|
||||||
|
|
||||||
bool savePng(const BitmapConstRef<float, 1> &bitmap, const char *filename) {
|
bool savePng(const BitmapConstRef<float, 1> &bitmap, const char *filename) {
|
||||||
std::vector<byte> pixels(bitmap.width*bitmap.height);
|
std::vector<byte> pixels(bitmap.width*bitmap.height);
|
||||||
std::vector<byte>::iterator it = pixels.begin();
|
std::vector<byte>::iterator it = pixels.begin();
|
||||||
|
|
@ -42,4 +49,17 @@ bool savePng(const BitmapConstRef<float, 3> &bitmap, const char *filename) {
|
||||||
return !lodepng::encode(filename, pixels, bitmap.width, bitmap.height, LCT_RGB);
|
return !lodepng::encode(filename, pixels, bitmap.width, bitmap.height, LCT_RGB);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool savePng(const BitmapConstRef<float, 4> &bitmap, const char *filename) {
|
||||||
|
std::vector<byte> pixels(4*bitmap.width*bitmap.height);
|
||||||
|
std::vector<byte>::iterator it = pixels.begin();
|
||||||
|
for (int y = bitmap.height-1; y >= 0; --y)
|
||||||
|
for (int x = 0; x < bitmap.width; ++x) {
|
||||||
|
*it++ = pixelFloatToByte(bitmap(x, y)[0]);
|
||||||
|
*it++ = pixelFloatToByte(bitmap(x, y)[1]);
|
||||||
|
*it++ = pixelFloatToByte(bitmap(x, y)[2]);
|
||||||
|
*it++ = pixelFloatToByte(bitmap(x, y)[3]);
|
||||||
|
}
|
||||||
|
return !lodepng::encode(filename, pixels, bitmap.width, bitmap.height, LCT_RGBA);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,9 @@ namespace msdfgen {
|
||||||
/// Saves the bitmap as a PNG file.
|
/// Saves the bitmap as a PNG file.
|
||||||
bool savePng(const BitmapConstRef<byte, 1> &bitmap, const char *filename);
|
bool savePng(const BitmapConstRef<byte, 1> &bitmap, const char *filename);
|
||||||
bool savePng(const BitmapConstRef<byte, 3> &bitmap, const char *filename);
|
bool savePng(const BitmapConstRef<byte, 3> &bitmap, const char *filename);
|
||||||
|
bool savePng(const BitmapConstRef<byte, 4> &bitmap, const char *filename);
|
||||||
bool savePng(const BitmapConstRef<float, 1> &bitmap, const char *filename);
|
bool savePng(const BitmapConstRef<float, 1> &bitmap, const char *filename);
|
||||||
bool savePng(const BitmapConstRef<float, 3> &bitmap, const char *filename);
|
bool savePng(const BitmapConstRef<float, 3> &bitmap, const char *filename);
|
||||||
|
bool savePng(const BitmapConstRef<float, 4> &bitmap, const char *filename);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
53
main.cpp
53
main.cpp
|
|
@ -265,9 +265,10 @@ static const char *helpText =
|
||||||
" <mode> <input specification> <options>\n"
|
" <mode> <input specification> <options>\n"
|
||||||
"\n"
|
"\n"
|
||||||
"MODES\n"
|
"MODES\n"
|
||||||
" sdf - Generate conventional monochrome signed distance field.\n"
|
" sdf - Generate conventional monochrome (true) signed distance field.\n"
|
||||||
" psdf - Generate monochrome signed pseudo-distance field.\n"
|
" psdf - Generate monochrome signed pseudo-distance field.\n"
|
||||||
" msdf - Generate multi-channel signed distance field. This is used by default if no mode is specified.\n"
|
" msdf - Generate multi-channel signed distance field. This is used by default if no mode is specified.\n"
|
||||||
|
" mtsdf - Generate combined multi-channel and true signed distance field in the alpha channel.\n"
|
||||||
" metrics - Report shape metrics only.\n"
|
" metrics - Report shape metrics only.\n"
|
||||||
"\n"
|
"\n"
|
||||||
"INPUT SPECIFICATION\n"
|
"INPUT SPECIFICATION\n"
|
||||||
|
|
@ -355,6 +356,7 @@ int main(int argc, const char * const *argv) {
|
||||||
SINGLE,
|
SINGLE,
|
||||||
PSEUDO,
|
PSEUDO,
|
||||||
MULTI,
|
MULTI,
|
||||||
|
MULTI_AND_TRUE,
|
||||||
METRICS
|
METRICS
|
||||||
} mode = MULTI;
|
} mode = MULTI;
|
||||||
bool legacyMode = false;
|
bool legacyMode = false;
|
||||||
|
|
@ -385,7 +387,7 @@ int main(int argc, const char * const *argv) {
|
||||||
Vector2 scale = 1;
|
Vector2 scale = 1;
|
||||||
bool scaleSpecified = false;
|
bool scaleSpecified = false;
|
||||||
double angleThreshold = 3;
|
double angleThreshold = 3;
|
||||||
double edgeThreshold = 1.001;
|
double edgeThreshold = MSDFGEN_DEFAULT_ERROR_CORRECTION_THRESHOLD;
|
||||||
bool defEdgeAssignment = true;
|
bool defEdgeAssignment = true;
|
||||||
const char *edgeAssignment = NULL;
|
const char *edgeAssignment = NULL;
|
||||||
bool yFlip = false;
|
bool yFlip = false;
|
||||||
|
|
@ -410,6 +412,7 @@ int main(int argc, const char * const *argv) {
|
||||||
ARG_MODE("sdf", SINGLE)
|
ARG_MODE("sdf", SINGLE)
|
||||||
ARG_MODE("psdf", PSEUDO)
|
ARG_MODE("psdf", PSEUDO)
|
||||||
ARG_MODE("msdf", MULTI)
|
ARG_MODE("msdf", MULTI)
|
||||||
|
ARG_MODE("mtsdf", MULTI_AND_TRUE)
|
||||||
ARG_MODE("metrics", METRICS)
|
ARG_MODE("metrics", METRICS)
|
||||||
|
|
||||||
ARG_CASE("-svg", 1) {
|
ARG_CASE("-svg", 1) {
|
||||||
|
|
@ -658,6 +661,8 @@ int main(int argc, const char * const *argv) {
|
||||||
double glyphAdvance = 0;
|
double glyphAdvance = 0;
|
||||||
if (!inputType || !input)
|
if (!inputType || !input)
|
||||||
ABORT("No input specified! Use either -svg <file.svg> or -font <file.ttf/otf> <character code>, or see -help.");
|
ABORT("No input specified! Use either -svg <file.svg> or -font <file.ttf/otf> <character code>, or see -help.");
|
||||||
|
if (mode == MULTI_AND_TRUE && (format == BMP || format == AUTO && output && cmpExtension(output, ".bmp")))
|
||||||
|
ABORT("Incompatible image format. A BMP file cannot contain alpha channel, which is required in mtsdf mode.");
|
||||||
Shape shape;
|
Shape shape;
|
||||||
switch (inputType) {
|
switch (inputType) {
|
||||||
case SVG: {
|
case SVG: {
|
||||||
|
|
@ -782,6 +787,7 @@ int main(int argc, const char * const *argv) {
|
||||||
// Compute output
|
// Compute output
|
||||||
Bitmap<float, 1> sdf;
|
Bitmap<float, 1> sdf;
|
||||||
Bitmap<float, 3> msdf;
|
Bitmap<float, 3> msdf;
|
||||||
|
Bitmap<float, 4> mtsdf;
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
case SINGLE: {
|
case SINGLE: {
|
||||||
sdf = Bitmap<float, 1>(width, height);
|
sdf = Bitmap<float, 1>(width, height);
|
||||||
|
|
@ -811,6 +817,18 @@ int main(int argc, const char * const *argv) {
|
||||||
generateMSDF(msdf, shape, range, scale, translate, scanlinePass ? 0 : edgeThreshold, overlapSupport);
|
generateMSDF(msdf, shape, range, scale, translate, scanlinePass ? 0 : edgeThreshold, overlapSupport);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case MULTI_AND_TRUE: {
|
||||||
|
if (!skipColoring)
|
||||||
|
edgeColoringSimple(shape, angleThreshold, coloringSeed);
|
||||||
|
if (edgeAssignment)
|
||||||
|
parseColoring(shape, edgeAssignment);
|
||||||
|
mtsdf = Bitmap<float, 4>(width, height);
|
||||||
|
if (legacyMode)
|
||||||
|
generateMTSDF_legacy(mtsdf, shape, range, scale, translate, scanlinePass ? 0 : edgeThreshold);
|
||||||
|
else
|
||||||
|
generateMTSDF(mtsdf, shape, range, scale, translate, scanlinePass ? 0 : edgeThreshold, overlapSupport);
|
||||||
|
break;
|
||||||
|
}
|
||||||
default:;
|
default:;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -836,6 +854,9 @@ int main(int argc, const char * const *argv) {
|
||||||
case MULTI:
|
case MULTI:
|
||||||
invertColor<3>(msdf);
|
invertColor<3>(msdf);
|
||||||
break;
|
break;
|
||||||
|
case MULTI_AND_TRUE:
|
||||||
|
invertColor<4>(mtsdf);
|
||||||
|
break;
|
||||||
default:;
|
default:;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -850,6 +871,11 @@ int main(int argc, const char * const *argv) {
|
||||||
if (edgeThreshold > 0)
|
if (edgeThreshold > 0)
|
||||||
msdfErrorCorrection(msdf, edgeThreshold/(scale*range));
|
msdfErrorCorrection(msdf, edgeThreshold/(scale*range));
|
||||||
break;
|
break;
|
||||||
|
case MULTI_AND_TRUE:
|
||||||
|
distanceSignCorrection(mtsdf, shape, scale, translate, fillRule);
|
||||||
|
if (edgeThreshold > 0)
|
||||||
|
msdfErrorCorrection(mtsdf, edgeThreshold/(scale*range));
|
||||||
|
break;
|
||||||
default:;
|
default:;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -912,6 +938,29 @@ int main(int argc, const char * const *argv) {
|
||||||
ABORT("Failed to write test render file.");
|
ABORT("Failed to write test render file.");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case MULTI_AND_TRUE:
|
||||||
|
error = writeOutput<4>(mtsdf, output, format);
|
||||||
|
if (error)
|
||||||
|
ABORT(error);
|
||||||
|
if (is8bitFormat(format) && (testRenderMulti || testRender || estimateError))
|
||||||
|
simulate8bit(mtsdf);
|
||||||
|
if (estimateError) {
|
||||||
|
double sdfError = estimateSDFError(mtsdf, shape, scale, translate, SDF_ERROR_ESTIMATE_PRECISION, fillRule);
|
||||||
|
printf("SDF error ~ %e\n", sdfError);
|
||||||
|
}
|
||||||
|
if (testRenderMulti) {
|
||||||
|
Bitmap<float, 4> render(testWidthM, testHeightM);
|
||||||
|
renderSDF(render, mtsdf, avgScale*range);
|
||||||
|
if (!savePng(render, testRenderMulti))
|
||||||
|
puts("Failed to write test render file.");
|
||||||
|
}
|
||||||
|
if (testRender) {
|
||||||
|
Bitmap<float, 1> render(testWidth, testHeight);
|
||||||
|
renderSDF(render, mtsdf, avgScale*range);
|
||||||
|
if (!savePng(render, testRender))
|
||||||
|
ABORT("Failed to write test render file.");
|
||||||
|
}
|
||||||
|
break;
|
||||||
default:;
|
default:;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
10
msdfgen.h
10
msdfgen.h
|
|
@ -31,6 +31,7 @@
|
||||||
#include "core/shape-description.h"
|
#include "core/shape-description.h"
|
||||||
|
|
||||||
#define MSDFGEN_VERSION "1.6"
|
#define MSDFGEN_VERSION "1.6"
|
||||||
|
#define MSDFGEN_DEFAULT_ERROR_CORRECTION_THRESHOLD 1.001
|
||||||
|
|
||||||
namespace msdfgen {
|
namespace msdfgen {
|
||||||
|
|
||||||
|
|
@ -41,14 +42,19 @@ void generateSDF(const BitmapRef<float, 1> &output, const Shape &shape, double r
|
||||||
void generatePseudoSDF(const BitmapRef<float, 1> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, bool overlapSupport = true);
|
void generatePseudoSDF(const BitmapRef<float, 1> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, bool overlapSupport = true);
|
||||||
|
|
||||||
/// Generates a multi-channel signed distance field. Edge colors must be assigned first! (See edgeColoringSimple)
|
/// Generates a multi-channel signed distance field. Edge colors must be assigned first! (See edgeColoringSimple)
|
||||||
void generateMSDF(const BitmapRef<float, 3> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, double edgeThreshold = 1.001, bool overlapSupport = true);
|
void generateMSDF(const BitmapRef<float, 3> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, double edgeThreshold = MSDFGEN_DEFAULT_ERROR_CORRECTION_THRESHOLD, bool overlapSupport = true);
|
||||||
|
|
||||||
|
/// Generates a multi-channel signed distance field with true distance in the alpha channel. Edge colors must be assigned first.
|
||||||
|
void generateMTSDF(const BitmapRef<float, 4> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, double edgeThreshold = MSDFGEN_DEFAULT_ERROR_CORRECTION_THRESHOLD, bool overlapSupport = true);
|
||||||
|
|
||||||
/// Resolves multi-channel signed distance field values that may cause interpolation artifacts. (Already called by generateMSDF)
|
/// Resolves multi-channel signed distance field values that may cause interpolation artifacts. (Already called by generateMSDF)
|
||||||
void msdfErrorCorrection(const BitmapRef<float, 3> &output, const Vector2 &threshold);
|
void msdfErrorCorrection(const BitmapRef<float, 3> &output, const Vector2 &threshold);
|
||||||
|
void msdfErrorCorrection(const BitmapRef<float, 4> &output, const Vector2 &threshold);
|
||||||
|
|
||||||
// Original simpler versions of the previous functions, which work well under normal circumstances, but cannot deal with overlapping contours.
|
// Original simpler versions of the previous functions, which work well under normal circumstances, but cannot deal with overlapping contours.
|
||||||
void generateSDF_legacy(const BitmapRef<float, 1> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate);
|
void generateSDF_legacy(const BitmapRef<float, 1> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate);
|
||||||
void generatePseudoSDF_legacy(const BitmapRef<float, 1> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate);
|
void generatePseudoSDF_legacy(const BitmapRef<float, 1> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate);
|
||||||
void generateMSDF_legacy(const BitmapRef<float, 3> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, double edgeThreshold = 1.001);
|
void generateMSDF_legacy(const BitmapRef<float, 3> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, double edgeThreshold = MSDFGEN_DEFAULT_ERROR_CORRECTION_THRESHOLD);
|
||||||
|
void generateMTSDF_legacy(const BitmapRef<float, 4> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, double edgeThreshold = MSDFGEN_DEFAULT_ERROR_CORRECTION_THRESHOLD);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue