Updated to MSDFgen 1.9

This commit is contained in:
Chlumsky 2021-05-29 11:56:55 +02:00
parent 67510959f1
commit d238eecc5b
8 changed files with 127 additions and 28 deletions

View File

@ -31,9 +31,8 @@ public:
/// Configuration of signed distance field generator
struct GeneratorAttributes {
bool overlapSupport = false;
msdfgen::MSDFGeneratorConfig config;
bool scanlinePass = false;
double errorCorrectionThreshold = MSDFGEN_DEFAULT_ERROR_CORRECTION_THRESHOLD;
};
/// A function that generates the bitmap for a single glyph

View File

@ -48,7 +48,7 @@ BitmapAtlasStorage<T, N>::operator msdfgen::BitmapRef<T, N>() {
template <typename T, int N>
BitmapAtlasStorage<T, N>::operator msdfgen::Bitmap<T, N>() && {
return (msdfgen::Bitmap<T, N>() &&) bitmap;
return (msdfgen::Bitmap<T, N> &&) bitmap;
}
template <typename T, int N>

View File

@ -121,6 +121,10 @@ double GlyphGeometry::getBoxRange() const {
return box.range;
}
msdfgen::Projection GlyphGeometry::getBoxProjection() const {
return msdfgen::Projection(msdfgen::Vector2(box.scale), box.translate);
}
double GlyphGeometry::getBoxScale() const {
return box.scale;
}

View File

@ -40,6 +40,8 @@ public:
void getBoxSize(int &w, int &h) const;
/// Returns the range needed to generate the glyph's SDF
double getBoxRange() const;
/// Returns the projection needed to generate the glyph's bitmap
msdfgen::Projection getBoxProjection() const;
/// Returns the scale needed to generate the glyph's bitmap
double getBoxScale() const;
/// Returns the translation vector needed to generate the glyph's bitmap

View File

@ -69,10 +69,10 @@ double TightAtlasPacker::packAndScale(GlyphGeometry *glyphs, int count, int widt
#define TRY_PACK(scale) (lastResult = !tryPack(glyphs, count, DimensionsConstraint(), width, height, padding, (scale), unitRange+pxRange/(scale), miterLimit))
double minScale = 1, maxScale = 1;
if (TRY_PACK(1)) {
while (maxScale < 1e+32 && TRY_PACK(maxScale = 2*minScale))
while (maxScale < 1e+32 && ((maxScale = 2*minScale), TRY_PACK(maxScale)))
minScale = maxScale;
} else {
while (minScale > 1e-32 && !TRY_PACK(minScale = .5*maxScale))
while (minScale > 1e-32 && ((minScale = .5*maxScale), !TRY_PACK(minScale)))
maxScale = minScale;
}
if (minScale == maxScale)

View File

@ -8,32 +8,44 @@ void scanlineGenerator(const msdfgen::BitmapRef<float, 1> &output, const GlyphGe
}
void sdfGenerator(const msdfgen::BitmapRef<float, 1> &output, const GlyphGeometry &glyph, const GeneratorAttributes &attribs) {
msdfgen::generateSDF(output, glyph.getShape(), glyph.getBoxRange(), glyph.getBoxScale(), glyph.getBoxTranslate(), attribs.overlapSupport);
msdfgen::generateSDF(output, glyph.getShape(), glyph.getBoxProjection(), glyph.getBoxRange(), attribs.config);
if (attribs.scanlinePass)
msdfgen::distanceSignCorrection(output, glyph.getShape(), glyph.getBoxScale(), glyph.getBoxTranslate(), MSDF_ATLAS_GLYPH_FILL_RULE);
msdfgen::distanceSignCorrection(output, glyph.getShape(), glyph.getBoxProjection(), MSDF_ATLAS_GLYPH_FILL_RULE);
}
void psdfGenerator(const msdfgen::BitmapRef<float, 1> &output, const GlyphGeometry &glyph, const GeneratorAttributes &attribs) {
msdfgen::generatePseudoSDF(output, glyph.getShape(), glyph.getBoxRange(), glyph.getBoxScale(), glyph.getBoxTranslate(), attribs.overlapSupport);
msdfgen::generatePseudoSDF(output, glyph.getShape(), glyph.getBoxProjection(), glyph.getBoxRange(), attribs.config);
if (attribs.scanlinePass)
msdfgen::distanceSignCorrection(output, glyph.getShape(), glyph.getBoxScale(), glyph.getBoxTranslate(), MSDF_ATLAS_GLYPH_FILL_RULE);
msdfgen::distanceSignCorrection(output, glyph.getShape(), glyph.getBoxProjection(), MSDF_ATLAS_GLYPH_FILL_RULE);
}
void msdfGenerator(const msdfgen::BitmapRef<float, 3> &output, const GlyphGeometry &glyph, const GeneratorAttributes &attribs) {
msdfgen::generateMSDF(output, glyph.getShape(), glyph.getBoxRange(), glyph.getBoxScale(), glyph.getBoxTranslate(), attribs.errorCorrectionThreshold, attribs.overlapSupport);
msdfgen::MSDFGeneratorConfig config = attribs.config;
if (attribs.scanlinePass)
config.errorCorrection.mode = msdfgen::ErrorCorrectionConfig::DISABLED;
msdfgen::generateMSDF(output, glyph.getShape(), glyph.getBoxProjection(), glyph.getBoxRange(), config);
if (attribs.scanlinePass) {
msdfgen::distanceSignCorrection(output, glyph.getShape(), glyph.getBoxScale(), glyph.getBoxTranslate(), MSDF_ATLAS_GLYPH_FILL_RULE);
if (attribs.errorCorrectionThreshold > 0)
msdfgen::msdfErrorCorrection(output, attribs.errorCorrectionThreshold/(glyph.getBoxScale()*glyph.getBoxRange()));
msdfgen::distanceSignCorrection(output, glyph.getShape(), glyph.getBoxProjection(), MSDF_ATLAS_GLYPH_FILL_RULE);
if (attribs.config.errorCorrection.mode != msdfgen::ErrorCorrectionConfig::DISABLED) {
config.errorCorrection.mode = attribs.config.errorCorrection.mode;
config.errorCorrection.distanceCheckMode = msdfgen::ErrorCorrectionConfig::DO_NOT_CHECK_DISTANCE;
msdfgen::msdfErrorCorrection(output, glyph.getShape(), glyph.getBoxProjection(), glyph.getBoxRange(), config);
}
}
}
void mtsdfGenerator(const msdfgen::BitmapRef<float, 4> &output, const GlyphGeometry &glyph, const GeneratorAttributes &attribs) {
msdfgen::generateMTSDF(output, glyph.getShape(), glyph.getBoxRange(), glyph.getBoxScale(), glyph.getBoxTranslate(), attribs.errorCorrectionThreshold, attribs.overlapSupport);
msdfgen::MSDFGeneratorConfig config = attribs.config;
if (attribs.scanlinePass)
config.errorCorrection.mode = msdfgen::ErrorCorrectionConfig::DISABLED;
msdfgen::generateMTSDF(output, glyph.getShape(), glyph.getBoxProjection(), glyph.getBoxRange(), config);
if (attribs.scanlinePass) {
msdfgen::distanceSignCorrection(output, glyph.getShape(), glyph.getBoxScale(), glyph.getBoxTranslate(), MSDF_ATLAS_GLYPH_FILL_RULE);
if (attribs.errorCorrectionThreshold > 0)
msdfgen::msdfErrorCorrection(output, attribs.errorCorrectionThreshold/(glyph.getBoxScale()*glyph.getBoxRange()));
msdfgen::distanceSignCorrection(output, glyph.getShape(), glyph.getBoxProjection(), MSDF_ATLAS_GLYPH_FILL_RULE);
if (attribs.config.errorCorrection.mode != msdfgen::ErrorCorrectionConfig::DISABLED) {
config.errorCorrection.mode = attribs.config.errorCorrection.mode;
config.errorCorrection.distanceCheckMode = msdfgen::ErrorCorrectionConfig::DO_NOT_CHECK_DISTANCE;
msdfgen::msdfErrorCorrection(output, glyph.getShape(), glyph.getBoxProjection(), glyph.getBoxRange(), config);
}
}
}

View File

@ -94,8 +94,12 @@ GLYPH CONFIGURATION
DISTANCE FIELD GENERATOR SETTINGS
-angle <angle>
Specifies the minimum angle between adjacent edges to be considered a corner. Append D for degrees. (msdf / mtsdf only)
-errorcorrection <threshold>
Changes the threshold used to detect and correct potential artifacts. 0 disables error correction. (msdf / mtsdf only)
-errorcorrection <mode>
Changes the MSDF/MTSDF error correction mode. Use -errorcorrection help for a list of valid modes.
-errordeviationratio <ratio>
Sets the minimum ratio between the actual and maximum expected distance delta to be considered an error.
-errorimproveratio <ratio>
Sets the minimum ratio between the pre-correction distance error and the post-correction distance error.
-miterlimit <value>
Sets the miter limit that limits the extension of each glyph's bounding box due to very sharp corners. (psdf / msdf / mtsdf only))"
#ifdef MSDFGEN_USE_SKIA
@ -120,6 +124,28 @@ R"(
Sets the number of threads for the parallel computation. (0 = auto)
)";
static const char *errorCorrectionHelpText = R"(
ERROR CORRECTION MODES
auto-fast
Detects inversion artifacts and distance errors that do not affect edges by range testing.
auto-full
Detects inversion artifacts and distance errors that do not affect edges by exact distance evaluation.
auto-mixed (default)
Detects inversions by distance evaluation and distance errors that do not affect edges by range testing.
disabled
Disables error correction.
distance-fast
Detects distance errors by range testing. Does not care if edges and corners are affected.
distance-full
Detects distance errors by exact distance evaluation. Does not care if edges and corners are affected, slow.
edge-fast
Detects inversion artifacts only by range testing.
edge-full
Detects inversion artifacts only by exact distance evaluation.
help
Displays this help.
)";
static char toupper(char c) {
return c >= 'a' && c <= 'z' ? c-'a'+'A' : c;
}
@ -247,9 +273,8 @@ int main(int argc, const char * const *argv) {
false
#endif
);
config.generatorAttributes.overlapSupport = !config.preprocessGeometry;
config.generatorAttributes.config.overlapSupport = !config.preprocessGeometry;
config.generatorAttributes.scanlinePass = !config.preprocessGeometry;
config.generatorAttributes.errorCorrectionThreshold = MSDFGEN_DEFAULT_ERROR_CORRECTION_THRESHOLD;
double minEmSize = 0;
enum {
/// Range specified in EMs
@ -266,6 +291,7 @@ int main(int argc, const char * const *argv) {
// Parse command line
int argPos = 1;
bool suggestHelp = false;
bool explicitErrorCorrectionMode = false;
while (argPos < argc) {
const char *arg = argv[argPos];
#define ARG_CASE(s, p) if (!strcmp(arg, s) && argPos+(p) < argc)
@ -471,10 +497,53 @@ int main(int argc, const char * const *argv) {
continue;
}
ARG_CASE("-errorcorrection", 1) {
double ect;
if (!parseDouble(ect, argv[argPos+1]) || ect < 0)
ABORT("Invalid error correction threshold. Use -errorcorrection <threshold> with a real number larger or equal to 1.");
config.generatorAttributes.errorCorrectionThreshold = ect;
msdfgen::ErrorCorrectionConfig &ec = config.generatorAttributes.config.errorCorrection;
if (!strcmp(argv[argPos+1], "disabled") || !strcmp(argv[argPos+1], "0") || !strcmp(argv[argPos+1], "none")) {
ec.mode = msdfgen::ErrorCorrectionConfig::DISABLED;
ec.distanceCheckMode = msdfgen::ErrorCorrectionConfig::DO_NOT_CHECK_DISTANCE;
} else if (!strcmp(argv[argPos+1], "default") || !strcmp(argv[argPos+1], "auto") || !strcmp(argv[argPos+1], "auto-mixed") || !strcmp(argv[argPos+1], "mixed")) {
ec.mode = msdfgen::ErrorCorrectionConfig::EDGE_PRIORITY;
ec.distanceCheckMode = msdfgen::ErrorCorrectionConfig::CHECK_DISTANCE_AT_EDGE;
} else if (!strcmp(argv[argPos+1], "auto-fast") || !strcmp(argv[argPos+1], "fast")) {
ec.mode = msdfgen::ErrorCorrectionConfig::EDGE_PRIORITY;
ec.distanceCheckMode = msdfgen::ErrorCorrectionConfig::DO_NOT_CHECK_DISTANCE;
} else if (!strcmp(argv[argPos+1], "auto-full") || !strcmp(argv[argPos+1], "full")) {
ec.mode = msdfgen::ErrorCorrectionConfig::EDGE_PRIORITY;
ec.distanceCheckMode = msdfgen::ErrorCorrectionConfig::ALWAYS_CHECK_DISTANCE;
} else if (!strcmp(argv[argPos+1], "distance") || !strcmp(argv[argPos+1], "distance-fast") || !strcmp(argv[argPos+1], "indiscriminate") || !strcmp(argv[argPos+1], "indiscriminate-fast")) {
ec.mode = msdfgen::ErrorCorrectionConfig::INDISCRIMINATE;
ec.distanceCheckMode = msdfgen::ErrorCorrectionConfig::DO_NOT_CHECK_DISTANCE;
} else if (!strcmp(argv[argPos+1], "distance-full") || !strcmp(argv[argPos+1], "indiscriminate-full")) {
ec.mode = msdfgen::ErrorCorrectionConfig::INDISCRIMINATE;
ec.distanceCheckMode = msdfgen::ErrorCorrectionConfig::ALWAYS_CHECK_DISTANCE;
} else if (!strcmp(argv[argPos+1], "edge-fast")) {
ec.mode = msdfgen::ErrorCorrectionConfig::EDGE_ONLY;
ec.distanceCheckMode = msdfgen::ErrorCorrectionConfig::DO_NOT_CHECK_DISTANCE;
} else if (!strcmp(argv[argPos+1], "edge") || !strcmp(argv[argPos+1], "edge-full")) {
ec.mode = msdfgen::ErrorCorrectionConfig::EDGE_ONLY;
ec.distanceCheckMode = msdfgen::ErrorCorrectionConfig::ALWAYS_CHECK_DISTANCE;
} else if (!strcmp(argv[argPos+1], "help")) {
puts(errorCorrectionHelpText);
return 0;
} else
ABORT("Unknown error correction mode. Use -errorcorrection help for more information.");
explicitErrorCorrectionMode = true;
argPos += 2;
continue;
}
ARG_CASE("-errordeviationratio", 1) {
double edr;
if (!(parseDouble(edr, argv[argPos+1]) && edr > 0))
ABORT("Invalid error deviation ratio. Use -errordeviationratio <ratio> with a positive real number.");
config.generatorAttributes.config.errorCorrection.minDeviationRatio = edr;
argPos += 2;
continue;
}
ARG_CASE("-errorimproveratio", 1) {
double eir;
if (!(parseDouble(eir, argv[argPos+1]) && eir > 0))
ABORT("Invalid error improvement ratio. Use -errorimproveratio <ratio> with a positive real number.");
config.generatorAttributes.config.errorCorrection.minImproveRatio = eir;
argPos += 2;
continue;
}
@ -507,12 +576,12 @@ int main(int argc, const char * const *argv) {
continue;
}
ARG_CASE("-nooverlap", 0) {
config.generatorAttributes.overlapSupport = false;
config.generatorAttributes.config.overlapSupport = false;
++argPos;
continue;
}
ARG_CASE("-overlap", 0) {
config.generatorAttributes.overlapSupport = true;
config.generatorAttributes.config.overlapSupport = true;
++argPos;
continue;
}
@ -607,6 +676,19 @@ int main(int argc, const char * const *argv) {
config.kerning = false;
if (config.threadCount <= 0)
config.threadCount = std::max((int) std::thread::hardware_concurrency(), 1);
if (config.generatorAttributes.scanlinePass) {
if (explicitErrorCorrectionMode && config.generatorAttributes.config.errorCorrection.distanceCheckMode != msdfgen::ErrorCorrectionConfig::DO_NOT_CHECK_DISTANCE) {
const char *fallbackModeName = "unknown";
switch (config.generatorAttributes.config.errorCorrection.mode) {
case msdfgen::ErrorCorrectionConfig::DISABLED: fallbackModeName = "disabled"; break;
case msdfgen::ErrorCorrectionConfig::INDISCRIMINATE: fallbackModeName = "distance-fast"; break;
case msdfgen::ErrorCorrectionConfig::EDGE_PRIORITY: fallbackModeName = "auto-fast"; break;
case msdfgen::ErrorCorrectionConfig::EDGE_ONLY: fallbackModeName = "edge-fast"; break;
}
printf("Selected error correction mode not compatible with scanline mode, falling back to %s.\n", fallbackModeName);
}
config.generatorAttributes.config.errorCorrection.distanceCheckMode = msdfgen::ErrorCorrectionConfig::DO_NOT_CHECK_DISTANCE;
}
// Finalize image format
ImageFormat imageExtension = ImageFormat::UNSPECIFIED;

@ -1 +1 @@
Subproject commit ae7fc5e7a53828949f3bd15006c96ef1a97b5105
Subproject commit 0f9c93e6868571dd2223886ecb3933fd44e7fbe8