From d238eecc5b1b1272d25ce66ff3963b2ad65b100b Mon Sep 17 00:00:00 2001 From: Chlumsky Date: Sat, 29 May 2021 11:56:55 +0200 Subject: [PATCH] Updated to MSDFgen 1.9 --- msdf-atlas-gen/AtlasGenerator.h | 3 +- msdf-atlas-gen/BitmapAtlasStorage.hpp | 2 +- msdf-atlas-gen/GlyphGeometry.cpp | 4 + msdf-atlas-gen/GlyphGeometry.h | 2 + msdf-atlas-gen/TightAtlasPacker.cpp | 4 +- msdf-atlas-gen/glyph-generators.cpp | 36 ++++++--- msdf-atlas-gen/main.cpp | 102 +++++++++++++++++++++++--- msdfgen | 2 +- 8 files changed, 127 insertions(+), 28 deletions(-) diff --git a/msdf-atlas-gen/AtlasGenerator.h b/msdf-atlas-gen/AtlasGenerator.h index 8a587c8..75a17a7 100644 --- a/msdf-atlas-gen/AtlasGenerator.h +++ b/msdf-atlas-gen/AtlasGenerator.h @@ -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 diff --git a/msdf-atlas-gen/BitmapAtlasStorage.hpp b/msdf-atlas-gen/BitmapAtlasStorage.hpp index 602a352..fbfdc9b 100644 --- a/msdf-atlas-gen/BitmapAtlasStorage.hpp +++ b/msdf-atlas-gen/BitmapAtlasStorage.hpp @@ -48,7 +48,7 @@ BitmapAtlasStorage::operator msdfgen::BitmapRef() { template BitmapAtlasStorage::operator msdfgen::Bitmap() && { - return (msdfgen::Bitmap() &&) bitmap; + return (msdfgen::Bitmap &&) bitmap; } template diff --git a/msdf-atlas-gen/GlyphGeometry.cpp b/msdf-atlas-gen/GlyphGeometry.cpp index 11b6c04..8a9c441 100644 --- a/msdf-atlas-gen/GlyphGeometry.cpp +++ b/msdf-atlas-gen/GlyphGeometry.cpp @@ -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; } diff --git a/msdf-atlas-gen/GlyphGeometry.h b/msdf-atlas-gen/GlyphGeometry.h index 44724ef..7127bbc 100644 --- a/msdf-atlas-gen/GlyphGeometry.h +++ b/msdf-atlas-gen/GlyphGeometry.h @@ -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 diff --git a/msdf-atlas-gen/TightAtlasPacker.cpp b/msdf-atlas-gen/TightAtlasPacker.cpp index fe59411..5c623ea 100644 --- a/msdf-atlas-gen/TightAtlasPacker.cpp +++ b/msdf-atlas-gen/TightAtlasPacker.cpp @@ -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) diff --git a/msdf-atlas-gen/glyph-generators.cpp b/msdf-atlas-gen/glyph-generators.cpp index a4ea4f7..af4a325 100644 --- a/msdf-atlas-gen/glyph-generators.cpp +++ b/msdf-atlas-gen/glyph-generators.cpp @@ -8,32 +8,44 @@ void scanlineGenerator(const msdfgen::BitmapRef &output, const GlyphGe } void sdfGenerator(const msdfgen::BitmapRef &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 &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 &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 &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); + } } } diff --git a/msdf-atlas-gen/main.cpp b/msdf-atlas-gen/main.cpp index f22ac03..74603b9 100644 --- a/msdf-atlas-gen/main.cpp +++ b/msdf-atlas-gen/main.cpp @@ -94,8 +94,12 @@ GLYPH CONFIGURATION DISTANCE FIELD GENERATOR SETTINGS -angle Specifies the minimum angle between adjacent edges to be considered a corner. Append D for degrees. (msdf / mtsdf only) - -errorcorrection - Changes the threshold used to detect and correct potential artifacts. 0 disables error correction. (msdf / mtsdf only) + -errorcorrection + Changes the MSDF/MTSDF error correction mode. Use -errorcorrection help for a list of valid modes. + -errordeviationratio + Sets the minimum ratio between the actual and maximum expected distance delta to be considered an error. + -errorimproveratio + Sets the minimum ratio between the pre-correction distance error and the post-correction distance error. -miterlimit 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 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 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 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; diff --git a/msdfgen b/msdfgen index ae7fc5e..0f9c93e 160000 --- a/msdfgen +++ b/msdfgen @@ -1 +1 @@ -Subproject commit ae7fc5e7a53828949f3bd15006c96ef1a97b5105 +Subproject commit 0f9c93e6868571dd2223886ecb3933fd44e7fbe8