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.
|
||||
|
||||
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.
|
||||
- **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:
|
||||
- **-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<PseudoDistanceSelector>;
|
||||
template class SimpleContourCombiner<MultiDistanceSelector>;
|
||||
template class SimpleContourCombiner<MultiAndTrueDistanceSelector>;
|
||||
|
||||
template <class EdgeSelector>
|
||||
OverlappingContourCombiner<EdgeSelector>::OverlappingContourCombiner(const Shape &shape) {
|
||||
|
|
@ -118,5 +119,6 @@ typename OverlappingContourCombiner<EdgeSelector>::DistanceType OverlappingConto
|
|||
template class OverlappingContourCombiner<TrueDistanceSelector>;
|
||||
template class OverlappingContourCombiner<PseudoDistanceSelector>;
|
||||
template class OverlappingContourCombiner<MultiDistanceSelector>;
|
||||
template class OverlappingContourCombiner<MultiAndTrueDistanceSelector>;
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -76,6 +76,10 @@ double PseudoDistanceSelectorBase::computeDistance(const Point2 &p) const {
|
|||
return minDistance;
|
||||
}
|
||||
|
||||
SignedDistance PseudoDistanceSelectorBase::trueDistance() const {
|
||||
return minTrueDistance;
|
||||
}
|
||||
|
||||
PseudoDistanceSelector::PseudoDistanceSelector(const Point2 &p) : p(p) { }
|
||||
|
||||
void PseudoDistanceSelector::addEdge(const EdgeSegment *prevEdge, const EdgeSegment *edge, const EdgeSegment *nextEdge) {
|
||||
|
|
@ -128,4 +132,25 @@ MultiDistanceSelector::DistanceType MultiDistanceSelector::distance() const {
|
|||
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 {
|
||||
double r, g, b;
|
||||
};
|
||||
struct MultiAndTrueDistance : MultiDistance {
|
||||
double a;
|
||||
};
|
||||
|
||||
/// Selects the nearest edge by its true distance.
|
||||
class TrueDistanceSelector {
|
||||
|
|
@ -38,6 +41,7 @@ public:
|
|||
void addEdgePseudoDistance(const SignedDistance &distance);
|
||||
void merge(const PseudoDistanceSelectorBase &other);
|
||||
double computeDistance(const Point2 &p) const;
|
||||
SignedDistance trueDistance() const;
|
||||
|
||||
private:
|
||||
SignedDistance minTrueDistance;
|
||||
|
|
@ -73,6 +77,7 @@ public:
|
|||
void addEdge(const EdgeSegment *prevEdge, const EdgeSegment *edge, const EdgeSegment *nextEdge);
|
||||
void merge(const MultiDistanceSelector &other);
|
||||
DistanceType distance() const;
|
||||
SignedDistance trueDistance() const;
|
||||
|
||||
private:
|
||||
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
|
||||
}
|
||||
|
||||
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))
|
||||
return line.setIntersections(std::vector<Scanline::Intersection>());
|
||||
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
|
||||
}
|
||||
|
||||
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) {
|
||||
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);
|
||||
return estimateSDFErrorInner(sdf, shape, scale, translate, scanlinesPerRow, 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 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);
|
||||
return estimateSDFErrorInner(sdf, shape, scale, translate, scanlinesPerRow, fillRule);
|
||||
}
|
||||
double estimateSDFError(const BitmapConstRef<float, 4> &sdf, const Shape &shape, const Vector2 &scale, const Vector2 &translate, int scanlinesPerRow, FillRule fillRule) {
|
||||
return estimateSDFErrorInner(sdf, shape, scale, translate, scanlinesPerRow, fillRule);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,9 +11,11 @@ namespace msdfgen {
|
|||
/// 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, 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.
|
||||
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, 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>
|
||||
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
|
||||
|
|
@ -96,6 +108,15 @@ void generateMSDF(const BitmapRef<float, 3> &output, const Shape &shape, double
|
|||
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) {
|
||||
// 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];
|
||||
|
|
@ -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
|
||||
}
|
||||
|
||||
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;
|
||||
int w = output.width, h = output.height;
|
||||
for (int y = 0; y < h; ++y)
|
||||
|
|
@ -156,6 +178,13 @@ void msdfErrorCorrection(const BitmapRef<float, 3> &output, const Vector2 &thres
|
|||
#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
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
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;
|
||||
if (!(w*h))
|
||||
return;
|
||||
|
|
@ -66,6 +67,8 @@ void distanceSignCorrection(const BitmapRef<float, 3> &sdf, const Shape &shape,
|
|||
*match = -1;
|
||||
} else
|
||||
*match = 1;
|
||||
if (N >= 4 && (msd[3] > .5f) != fill)
|
||||
msd[3] = 1.f-msd[3];
|
||||
++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.
|
||||
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, 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) {
|
||||
const float *end = bitmap.pixels+1*bitmap.width*bitmap.height;
|
||||
for (float *p = bitmap.pixels; p < end; ++p)
|
||||
|
|
@ -85,4 +108,10 @@ void simulate8bit(const BitmapRef<float, 3> &bitmap) {
|
|||
*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, 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, 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.
|
||||
void simulate8bit(const BitmapRef<float, 1> &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);
|
||||
}
|
||||
|
||||
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) {
|
||||
FILE *file = fopen(filename, "wb");
|
||||
if (!file)
|
||||
|
|
@ -156,4 +161,9 @@ bool saveBmp(const BitmapConstRef<float, 3> &bitmap, const char *filename) {
|
|||
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.
|
||||
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, 4> &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, 4> &bitmap, const char *filename);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,6 +20,11 @@ template <typename T>
|
|||
static bool writeValue(FILE *file, T value) {
|
||||
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) {
|
||||
#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, 0x0003u);
|
||||
writeValue<uint32_t>(file, channels);
|
||||
if (channels == 3)
|
||||
writeValue<uint32_t>(file, 0x00c2u); // Offset of 32, 32, 32
|
||||
if (channels > 1)
|
||||
writeValue<uint32_t>(file, 0x00c2u); // Offset of 32, 32, ...
|
||||
else {
|
||||
writeValue<uint16_t>(file, 32);
|
||||
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, 0x0003u);
|
||||
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);
|
||||
// StripOffsets
|
||||
writeValue<uint16_t>(file, 0x0111u);
|
||||
writeValue<uint16_t>(file, 0x0004u);
|
||||
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
|
||||
writeValue<uint16_t>(file, 0x0115u);
|
||||
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, 0x0005u);
|
||||
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
|
||||
writeValue<uint16_t>(file, 0x011bu);
|
||||
writeValue<uint16_t>(file, 0x0005u);
|
||||
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
|
||||
writeValue<uint16_t>(file, 0x0128u);
|
||||
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, 0x0003u);
|
||||
writeValue<uint32_t>(file, channels);
|
||||
if (channels == 3)
|
||||
writeValue<uint32_t>(file, 0x00d8u); // Offset of 3, 3, 3
|
||||
if (channels > 1)
|
||||
writeValue<uint32_t>(file, 0x00d2u+channels*2); // Offset of 3, 3, ...
|
||||
else {
|
||||
writeValue<uint16_t>(file, 3);
|
||||
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, 0x000bu);
|
||||
writeValue<uint32_t>(file, channels);
|
||||
if (channels == 3)
|
||||
writeValue<uint32_t>(file, 0x00deu); // Offset of 0.f, 0.f, 0.f
|
||||
if (channels > 1)
|
||||
writeValue<uint32_t>(file, 0x00d2u+channels*4); // Offset of 0.f, 0.f, ...
|
||||
else
|
||||
writeValue<float>(file, 0.f);
|
||||
// SMaxSampleValue
|
||||
writeValue<uint16_t>(file, 0x0155u);
|
||||
writeValue<uint16_t>(file, 0x000bu);
|
||||
writeValue<uint32_t>(file, channels);
|
||||
if (channels == 3)
|
||||
writeValue<uint32_t>(file, 0x00eau); // Offset of 1.f, 1.f, 1.f
|
||||
if (channels > 1)
|
||||
writeValue<uint32_t>(file, 0x00d2u+channels*8); // Offset of 1.f, 1.f, ...
|
||||
else
|
||||
writeValue<float>(file, 1.f);
|
||||
// Offset = 0x00be
|
||||
|
||||
writeValue<uint32_t>(file, 0);
|
||||
|
||||
if (channels == 3) {
|
||||
if (channels > 1) {
|
||||
// 0x00c2 BitsPerSample data
|
||||
writeValue<uint16_t>(file, 32);
|
||||
writeValue<uint16_t>(file, 32);
|
||||
writeValue<uint16_t>(file, 32);
|
||||
// 0x00c8 XResolution data
|
||||
writeValueRepeated<uint16_t>(file, 32, channels);
|
||||
// 0x00c2 + 2*N XResolution data
|
||||
writeValue<uint32_t>(file, 300);
|
||||
writeValue<uint32_t>(file, 1);
|
||||
// 0x00d0 YResolution data
|
||||
// 0x00ca + 2*N YResolution data
|
||||
writeValue<uint32_t>(file, 300);
|
||||
writeValue<uint32_t>(file, 1);
|
||||
// 0x00d8 SampleFormat data
|
||||
writeValue<uint16_t>(file, 3);
|
||||
writeValue<uint16_t>(file, 3);
|
||||
writeValue<uint16_t>(file, 3);
|
||||
// 0x00de SMinSampleValue data
|
||||
writeValue<float>(file, 0.f);
|
||||
writeValue<float>(file, 0.f);
|
||||
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
|
||||
// 0x00d2 + 2*N SampleFormat data
|
||||
writeValueRepeated<uint16_t>(file, 3, channels);
|
||||
// 0x00d2 + 4*N SMinSampleValue data
|
||||
writeValueRepeated<float>(file, 0.f, channels);
|
||||
// 0x00d2 + 8*N SMaxSampleValue data
|
||||
writeValueRepeated<float>(file, 1.f, channels);
|
||||
// Offset = 0x00d2 + 12*N
|
||||
} else {
|
||||
// 0x00c2 XResolution data
|
||||
writeValue<uint32_t>(file, 300);
|
||||
|
|
@ -169,24 +166,25 @@ static bool writeTiffHeader(FILE *file, int width, int height, int channels) {
|
|||
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) {
|
||||
FILE *file = fopen(filename, "wb");
|
||||
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);
|
||||
return saveTiffFloat(bitmap, filename);
|
||||
}
|
||||
|
||||
bool saveTiff(const BitmapConstRef<float, 3> &bitmap, const char *filename) {
|
||||
FILE *file = fopen(filename, "wb");
|
||||
if (!file)
|
||||
return false;
|
||||
writeTiffHeader(file, bitmap.width, bitmap.height, 3);
|
||||
for (int y = bitmap.height-1; y >= 0; --y)
|
||||
fwrite(bitmap(0, y), sizeof(float), 3*bitmap.width, file);
|
||||
return !fclose(file);
|
||||
return saveTiffFloat(bitmap, filename);
|
||||
}
|
||||
bool saveTiff(const BitmapConstRef<float, 4> &bitmap, const char *filename) {
|
||||
return saveTiffFloat(bitmap, filename);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,5 +8,6 @@ namespace msdfgen {
|
|||
/// 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, 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);
|
||||
}
|
||||
|
||||
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) {
|
||||
std::vector<byte> pixels(bitmap.width*bitmap.height);
|
||||
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);
|
||||
}
|
||||
|
||||
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.
|
||||
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, 4> &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, 4> &bitmap, const char *filename);
|
||||
|
||||
}
|
||||
|
|
|
|||
53
main.cpp
53
main.cpp
|
|
@ -265,9 +265,10 @@ static const char *helpText =
|
|||
" <mode> <input specification> <options>\n"
|
||||
"\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"
|
||||
" 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"
|
||||
"\n"
|
||||
"INPUT SPECIFICATION\n"
|
||||
|
|
@ -355,6 +356,7 @@ int main(int argc, const char * const *argv) {
|
|||
SINGLE,
|
||||
PSEUDO,
|
||||
MULTI,
|
||||
MULTI_AND_TRUE,
|
||||
METRICS
|
||||
} mode = MULTI;
|
||||
bool legacyMode = false;
|
||||
|
|
@ -385,7 +387,7 @@ int main(int argc, const char * const *argv) {
|
|||
Vector2 scale = 1;
|
||||
bool scaleSpecified = false;
|
||||
double angleThreshold = 3;
|
||||
double edgeThreshold = 1.001;
|
||||
double edgeThreshold = MSDFGEN_DEFAULT_ERROR_CORRECTION_THRESHOLD;
|
||||
bool defEdgeAssignment = true;
|
||||
const char *edgeAssignment = NULL;
|
||||
bool yFlip = false;
|
||||
|
|
@ -410,6 +412,7 @@ int main(int argc, const char * const *argv) {
|
|||
ARG_MODE("sdf", SINGLE)
|
||||
ARG_MODE("psdf", PSEUDO)
|
||||
ARG_MODE("msdf", MULTI)
|
||||
ARG_MODE("mtsdf", MULTI_AND_TRUE)
|
||||
ARG_MODE("metrics", METRICS)
|
||||
|
||||
ARG_CASE("-svg", 1) {
|
||||
|
|
@ -658,6 +661,8 @@ int main(int argc, const char * const *argv) {
|
|||
double glyphAdvance = 0;
|
||||
if (!inputType || !input)
|
||||
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;
|
||||
switch (inputType) {
|
||||
case SVG: {
|
||||
|
|
@ -782,6 +787,7 @@ int main(int argc, const char * const *argv) {
|
|||
// Compute output
|
||||
Bitmap<float, 1> sdf;
|
||||
Bitmap<float, 3> msdf;
|
||||
Bitmap<float, 4> mtsdf;
|
||||
switch (mode) {
|
||||
case SINGLE: {
|
||||
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);
|
||||
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:;
|
||||
}
|
||||
|
||||
|
|
@ -836,6 +854,9 @@ int main(int argc, const char * const *argv) {
|
|||
case MULTI:
|
||||
invertColor<3>(msdf);
|
||||
break;
|
||||
case MULTI_AND_TRUE:
|
||||
invertColor<4>(mtsdf);
|
||||
break;
|
||||
default:;
|
||||
}
|
||||
}
|
||||
|
|
@ -850,6 +871,11 @@ int main(int argc, const char * const *argv) {
|
|||
if (edgeThreshold > 0)
|
||||
msdfErrorCorrection(msdf, edgeThreshold/(scale*range));
|
||||
break;
|
||||
case MULTI_AND_TRUE:
|
||||
distanceSignCorrection(mtsdf, shape, scale, translate, fillRule);
|
||||
if (edgeThreshold > 0)
|
||||
msdfErrorCorrection(mtsdf, edgeThreshold/(scale*range));
|
||||
break;
|
||||
default:;
|
||||
}
|
||||
}
|
||||
|
|
@ -912,6 +938,29 @@ int main(int argc, const char * const *argv) {
|
|||
ABORT("Failed to write test render file.");
|
||||
}
|
||||
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:;
|
||||
}
|
||||
|
||||
|
|
|
|||
10
msdfgen.h
10
msdfgen.h
|
|
@ -31,6 +31,7 @@
|
|||
#include "core/shape-description.h"
|
||||
|
||||
#define MSDFGEN_VERSION "1.6"
|
||||
#define MSDFGEN_DEFAULT_ERROR_CORRECTION_THRESHOLD 1.001
|
||||
|
||||
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);
|
||||
|
||||
/// 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)
|
||||
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.
|
||||
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 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