diff --git a/README.md b/README.md index 49c93db..717698f 100644 --- a/README.md +++ b/README.md @@ -128,10 +128,11 @@ int main() { shape.normalize(); // max. angle edgeColoringSimple(shape, 3.0); - // image width, height + // output width, height Bitmap msdf(32, 32); - // range, scale, translation - generateMSDF(msdf, shape, 4.0, 1.0, Vector2(4.0, 4.0)); + // scale, translation + SDFTransformation t(Projection(1.0, Vector2(4.0, 4.0)), Range(4.0)); + generateMSDF(msdf, shape, t); savePng(msdf, "output.png"); } destroyFont(font); diff --git a/core/DistanceMapping.cpp b/core/DistanceMapping.cpp new file mode 100644 index 0000000..526e129 --- /dev/null +++ b/core/DistanceMapping.cpp @@ -0,0 +1,27 @@ + +#include "DistanceMapping.h" + +namespace msdfgen { + +DistanceMapping DistanceMapping::inverse(Range range) { + double rangeWidth = range.upper-range.lower; + return DistanceMapping(rangeWidth, range.lower/(rangeWidth ? rangeWidth : 1)); +} + +DistanceMapping::DistanceMapping() : scale(1), translate(0) { } + +DistanceMapping::DistanceMapping(Range range) : scale(1/(range.upper-range.lower)), translate(-range.lower) { } + +double DistanceMapping::operator()(double d) const { + return scale*(d+translate); +} + +double DistanceMapping::operator()(Delta d) const { + return scale*d.value; +} + +DistanceMapping DistanceMapping::inverse() const { + return DistanceMapping(1/scale, -scale*translate); +} + +} diff --git a/core/DistanceMapping.h b/core/DistanceMapping.h new file mode 100644 index 0000000..fadbefa --- /dev/null +++ b/core/DistanceMapping.h @@ -0,0 +1,36 @@ + +#pragma once + +#include "Range.hpp" + +namespace msdfgen { + +/// Linear transformation of signed distance values. +class DistanceMapping { + +public: + /// Explicitly designates value as distance delta rather than an absolute distance. + class Delta { + public: + double value; + inline explicit Delta(double distanceDelta) : value(distanceDelta) { } + inline operator double() const { return value; } + }; + + static DistanceMapping inverse(Range range); + + DistanceMapping(); + DistanceMapping(Range range); + double operator()(double d) const; + double operator()(Delta d) const; + DistanceMapping inverse() const; + +private: + double scale; + double translate; + + inline DistanceMapping(double scale, double translate) : scale(scale), translate(translate) { } + +}; + +} diff --git a/core/MSDFErrorCorrection.cpp b/core/MSDFErrorCorrection.cpp index 94b6845..7639dc6 100644 --- a/core/MSDFErrorCorrection.cpp +++ b/core/MSDFErrorCorrection.cpp @@ -74,7 +74,7 @@ public: // Compute the evaluated distance (interpolated median) before and after error correction, as well as the exact shape distance. float oldPSD = median(oldMSD[0], oldMSD[1], oldMSD[2]); float newPSD = median(newMSD[0], newMSD[1], newMSD[2]); - float refPSD = float(parent->invRange*parent->distanceFinder.distance(parent->shapeCoord+tVector*parent->texelSize)+.5); + float refPSD = float(parent->distanceMapping(parent->distanceFinder.distance(parent->shapeCoord+tVector*parent->texelSize))); // Compare the differences of the exact distance and the before and after distances. return parent->minImproveRatio*fabsf(newPSD-refPSD) < double(fabsf(oldPSD-refPSD)); } @@ -87,7 +87,7 @@ public: Point2 shapeCoord, sdfCoord; const float *msd; bool protectedFlag; - inline ShapeDistanceChecker(const BitmapConstRef &sdf, const Shape &shape, const Projection &projection, double invRange, double minImproveRatio) : distanceFinder(shape), sdf(sdf), invRange(invRange), minImproveRatio(minImproveRatio) { + inline ShapeDistanceChecker(const BitmapConstRef &sdf, const Shape &shape, const Projection &projection, DistanceMapping distanceMapping, double minImproveRatio) : distanceFinder(shape), sdf(sdf), distanceMapping(distanceMapping), minImproveRatio(minImproveRatio) { texelSize = projection.unprojectVector(Vector2(1)); } inline ArtifactClassifier classifier(const Vector2 &direction, double span) { @@ -96,15 +96,14 @@ public: private: ShapeDistanceFinder > distanceFinder; BitmapConstRef sdf; - double invRange; + DistanceMapping distanceMapping; Vector2 texelSize; double minImproveRatio; }; MSDFErrorCorrection::MSDFErrorCorrection() { } -MSDFErrorCorrection::MSDFErrorCorrection(const BitmapRef &stencil, const Projection &projection, double range) : stencil(stencil), projection(projection) { - invRange = 1/range; +MSDFErrorCorrection::MSDFErrorCorrection(const BitmapRef &stencil, const SDFTransformation &transformation) : stencil(stencil), transformation(transformation) { minDeviationRatio = ErrorCorrectionConfig::defaultMinDeviationRatio; minImproveRatio = ErrorCorrectionConfig::defaultMinImproveRatio; memset(stencil.pixels, 0, sizeof(byte)*stencil.width*stencil.height); @@ -127,7 +126,7 @@ void MSDFErrorCorrection::protectCorners(const Shape &shape) { // If the color changes from prevEdge to edge, this is a corner. if (!(commonColor&(commonColor-1))) { // Find the four texels that envelop the corner and mark them as protected. - Point2 p = projection.project((*edge)->point(0)); + Point2 p = transformation.project((*edge)->point(0)); if (shape.inverseYAxis) p.y = stencil.height-p.y; int l = (int) floor(p.x-.5); @@ -191,7 +190,7 @@ template void MSDFErrorCorrection::protectEdges(const BitmapConstRef &sdf) { float radius; // Horizontal texel pairs - radius = float(PROTECTION_RADIUS_TOLERANCE*projection.unprojectVector(Vector2(invRange, 0)).length()); + radius = float(PROTECTION_RADIUS_TOLERANCE*transformation.unprojectVector(Vector2(transformation.distanceMapping(DistanceMapping::Delta(1)), 0)).length()); for (int y = 0; y < sdf.height; ++y) { const float *left = sdf(0, y); const float *right = sdf(1, y); @@ -207,7 +206,7 @@ void MSDFErrorCorrection::protectEdges(const BitmapConstRef &sdf) { } } // Vertical texel pairs - radius = float(PROTECTION_RADIUS_TOLERANCE*projection.unprojectVector(Vector2(0, invRange)).length()); + radius = float(PROTECTION_RADIUS_TOLERANCE*transformation.unprojectVector(Vector2(0, transformation.distanceMapping(DistanceMapping::Delta(1)))).length()); for (int y = 0; y < sdf.height-1; ++y) { const float *bottom = sdf(0, y); const float *top = sdf(0, y+1); @@ -223,7 +222,7 @@ void MSDFErrorCorrection::protectEdges(const BitmapConstRef &sdf) { } } // Diagonal texel pairs - radius = float(PROTECTION_RADIUS_TOLERANCE*projection.unprojectVector(Vector2(invRange)).length()); + radius = float(PROTECTION_RADIUS_TOLERANCE*transformation.unprojectVector(Vector2(transformation.distanceMapping(DistanceMapping::Delta(1)))).length()); for (int y = 0; y < sdf.height-1; ++y) { const float *lb = sdf(0, y); const float *rb = sdf(1, y); @@ -391,9 +390,9 @@ static bool hasDiagonalArtifact(const ArtifactClassifier &artifactClassifier, fl template void MSDFErrorCorrection::findErrors(const BitmapConstRef &sdf) { // Compute the expected deltas between values of horizontally, vertically, and diagonally adjacent texels. - double hSpan = minDeviationRatio*projection.unprojectVector(Vector2(invRange, 0)).length(); - double vSpan = minDeviationRatio*projection.unprojectVector(Vector2(0, invRange)).length(); - double dSpan = minDeviationRatio*projection.unprojectVector(Vector2(invRange)).length(); + double hSpan = minDeviationRatio*transformation.unprojectVector(Vector2(transformation.distanceMapping(DistanceMapping::Delta(1)), 0)).length(); + double vSpan = minDeviationRatio*transformation.unprojectVector(Vector2(0, transformation.distanceMapping(DistanceMapping::Delta(1)))).length(); + double dSpan = minDeviationRatio*transformation.unprojectVector(Vector2(transformation.distanceMapping(DistanceMapping::Delta(1)))).length(); // Inspect all texels. for (int y = 0; y < sdf.height; ++y) { for (int x = 0; x < sdf.width; ++x) { @@ -419,14 +418,14 @@ void MSDFErrorCorrection::findErrors(const BitmapConstRef &sdf) { template