From 60789b8cf3695b4d1e08ace0e3116c49b8eb92d3 Mon Sep 17 00:00:00 2001 From: Chlumsky Date: Sun, 18 Oct 2020 14:00:24 +0200 Subject: [PATCH] Loading glyphs by glyph index --- CHANGELOG.md | 18 +++++++ README.md | 23 +++++--- msdf-atlas-gen/Charset.h | 2 +- msdf-atlas-gen/GlyphBox.h | 4 +- msdf-atlas-gen/GlyphGeometry.cpp | 40 ++++++++++++-- msdf-atlas-gen/GlyphGeometry.h | 10 +++- msdf-atlas-gen/artery-font-export.cpp | 46 +++++++++------- msdf-atlas-gen/artery-font-export.h | 10 +++- msdf-atlas-gen/charset-parser.cpp | 8 +-- msdf-atlas-gen/csv-export.cpp | 4 +- msdf-atlas-gen/csv-export.h | 5 +- msdf-atlas-gen/json-export.cpp | 11 +++- msdf-atlas-gen/json-export.h | 2 +- msdf-atlas-gen/main.cpp | 75 +++++++++++++++++++++------ msdf-atlas-gen/types.h | 6 +++ 15 files changed, 201 insertions(+), 63 deletions(-) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..035d97a --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,18 @@ + +## Version 1.1 (2020-10-18) + +- Updated to MSDFgen 1.8. +- Glyph geometry is now preprocessed by Skia to resolve irregularities which were previously unsupported and caused artifacts. + - The scanline pass and overlapping contour mode is made obsolete by this step and has been disabled by default. The preprocess step can be disabled by the new `-nopreprocess` switch and the former enabled by `-scanline` and `-overlap` respectively. + - The project can be built without the Skia library, forgoing the geometry preprocessing feature. This is controlled by the macro definition `MSDFGEN_USE_SKIA`. +- Glyphs can now also be loaded by glyph index rather than Unicode values. In the standalone version, a set of glyphs can be passed by `-glyphset` in place of `-charset`. +- Glyphs not present in the font should now be correctly skipped instead of producing a placeholder symbol. +- Added `-threads` argument to set the number of concurrent threads used during distance field generation. + +### Version 1.0.1 (2020-03-09) + +- Updated to MSDFgen 1.7.1. + +## Version 1.0 (2020-03-08) + +- Initial release. diff --git a/README.md b/README.md index 496ae34..26a2fb0 100644 --- a/README.md +++ b/README.md @@ -3,19 +3,17 @@ This is a utility for generating compact font atlases using [MSDFgen](https://github.com/Chlumsky/msdfgen). -It can read TTF and OTF font files, select a subset of characters, generate distance fields or bitmaps for each, and tightly pack them into an atlas bitmap, which can be e.g. used as a texture in video games. The atlas can be exported as an image file or an [Artery Font](https://github.com/Chlumsky/artery-font-format) file, and its layout written into a CSV or structured JSON file. +The atlas generator loads a subset of glyphs from a TTF or OTF font file, generates a distance field for each of them, and tightly packs them into an atlas bitmap (example below). The finished atlas and/or its layout metadata can be exported as an [Artery Font](https://github.com/Chlumsky/artery-font-format) file, a plain image file, a CSV sheet or a structured JSON file. ![Atlas example](https://user-images.githubusercontent.com/18639794/76163889-811f2e80-614a-11ea-9b28-1eed54dbb899.png) -## Getting started +A font atlas is typically stored in texture memory and used to draw text in real-time rendering contexts such as video games. -This project can be used either as a library or as a console program. -To start using the program immediately, there is a Windows binary available for download in the ["Releases" section](https://github.com/Chlumsky/msdf-atlas-gen/releases). -To build the project, you may use the included [Visual Studio solution](msdf-atlas-gen.sln) or the [Unix Makefile](Makefile). +- See what's new in the [changelog](CHANGELOG.md). ## Atlas types -The utility can generate the atlas bitmap in the following six ways: +The atlas generator can generate the following six types of atlases. | |Hard mask|Soft mask|SDF|PSDF|MSDF|MTSDF| |-|-|-|-|-|-|-| @@ -32,12 +30,21 @@ Notes: - *Soft effects* refers to the support of effects that use true distance, such as glows, rounded borders, or simplified shadows. - *Hard effects* refers to the support of effects that use pseudo-distance, such as mitered borders or thickness adjustment. +## Getting started + +This project can be used either as a library or as a standalone console program. +To start using the program immediately, there is a Windows binary available for download in the ["Releases" section](https://github.com/Chlumsky/msdf-atlas-gen/releases). +To build the project, you may use the included [Visual Studio solution](msdf-atlas-gen.sln) or the [Unix Makefile](Makefile). + ## Command line arguments +Use the following command line arguments for the standalone version of the atlas generator. + ### Input - `-font ` – sets the input font file. - `-charset ` – sets the character set. The ASCII charset will be used if not specified. See [the syntax specification](#character-set-specification-syntax) of `charset.txt`. +- `-glyphset ` – sets the set of input glyphs using their indices within the font file. See [the syntax specification](#glyph-set-specification). ### Bitmap atlas type @@ -123,3 +130,7 @@ Additionally, the include directive can be used to include other charset files a It must be written on a separate line: `@include "base-charset.txt"` + +### Glyph set specification + +The syntax of the glyph set specification is mostly the same as that of a character set, but only numeric values (decimal and hexadecimal) are allowed. diff --git a/msdf-atlas-gen/Charset.h b/msdf-atlas-gen/Charset.h index 6b107b3..d0cc719 100644 --- a/msdf-atlas-gen/Charset.h +++ b/msdf-atlas-gen/Charset.h @@ -25,7 +25,7 @@ public: std::set::const_iterator end() const; /// Load character set from a text file with the correct syntax - bool load(const char *filename); + bool load(const char *filename, bool disableCharLiterals = false); private: std::set codepoints; diff --git a/msdf-atlas-gen/GlyphBox.h b/msdf-atlas-gen/GlyphBox.h index a6d1c6a..83b8842 100644 --- a/msdf-atlas-gen/GlyphBox.h +++ b/msdf-atlas-gen/GlyphBox.h @@ -1,13 +1,11 @@ #pragma once -#include "types.h" - namespace msdf_atlas { /// The glyph box - its bounds in plane and atlas struct GlyphBox { - unicode_t codepoint; + int index; double advance; struct { double l, b, r, t; diff --git a/msdf-atlas-gen/GlyphGeometry.cpp b/msdf-atlas-gen/GlyphGeometry.cpp index 39a70f2..0ac3817 100644 --- a/msdf-atlas-gen/GlyphGeometry.cpp +++ b/msdf-atlas-gen/GlyphGeometry.cpp @@ -6,11 +6,12 @@ namespace msdf_atlas { -GlyphGeometry::GlyphGeometry() : codepoint(), bounds(), advance(), box() { } +GlyphGeometry::GlyphGeometry() : index(), codepoint(), bounds(), advance(), box() { } -bool GlyphGeometry::load(msdfgen::FontHandle *font, unicode_t codepoint, bool preprocessGeometry) { - if (font && msdfgen::loadGlyph(shape, font, codepoint, &advance) && shape.validate()) { - this->codepoint = codepoint; +bool GlyphGeometry::load(msdfgen::FontHandle *font, msdfgen::GlyphIndex index, bool preprocessGeometry) { + if (font && msdfgen::loadGlyph(shape, font, index, &advance) && shape.validate()) { + this->index = index.getIndex(); + codepoint = 0; #ifdef MSDFGEN_USE_SKIA if (preprocessGeometry) msdfgen::resolveShapeGeometry(shape); @@ -33,6 +34,17 @@ bool GlyphGeometry::load(msdfgen::FontHandle *font, unicode_t codepoint, bool pr return false; } +bool GlyphGeometry::load(msdfgen::FontHandle *font, unicode_t codepoint, bool preprocessGeometry) { + msdfgen::GlyphIndex index; + if (msdfgen::getGlyphIndex(index, font, codepoint)) { + if (load(font, index, preprocessGeometry)) { + this->codepoint = codepoint; + return true; + } + } + return false; +} + void GlyphGeometry::edgeColoring(double angleThreshold, unsigned long long seed) { msdfgen::edgeColoringInkTrap(shape, angleThreshold, seed); } @@ -62,10 +74,28 @@ void GlyphGeometry::placeBox(int x, int y) { box.rect.x = x, box.rect.y = y; } +int GlyphGeometry::getIndex() const { + return index; +} + +msdfgen::GlyphIndex GlyphGeometry::getGlyphIndex() const { + return msdfgen::GlyphIndex(index); +} + unicode_t GlyphGeometry::getCodepoint() const { return codepoint; } +int GlyphGeometry::getIdentifier(GlyphIdentifierType type) const { + switch (type) { + case GlyphIdentifierType::GLYPH_INDEX: + return index; + case GlyphIdentifierType::UNICODE_CODEPOINT: + return (int) codepoint; + } + return 0; +} + const msdfgen::Shape & GlyphGeometry::getShape() const { return shape; } @@ -121,7 +151,7 @@ bool GlyphGeometry::isWhitespace() const { GlyphGeometry::operator GlyphBox() const { GlyphBox box; - box.codepoint = codepoint; + box.index = index; box.advance = advance; getQuadPlaneBounds(box.bounds.l, box.bounds.b, box.bounds.r, box.bounds.t); box.rect.x = this->box.rect.x, box.rect.y = this->box.rect.y, box.rect.w = this->box.rect.w, box.rect.h = this->box.rect.h; diff --git a/msdf-atlas-gen/GlyphGeometry.h b/msdf-atlas-gen/GlyphGeometry.h index eea6dd5..556e114 100644 --- a/msdf-atlas-gen/GlyphGeometry.h +++ b/msdf-atlas-gen/GlyphGeometry.h @@ -14,6 +14,7 @@ class GlyphGeometry { public: GlyphGeometry(); /// Loads glyph geometry from font + bool load(msdfgen::FontHandle *font, msdfgen::GlyphIndex index, bool preprocessGeometry = true); bool load(msdfgen::FontHandle *font, unicode_t codepoint, bool preprocessGeometry = true); /// Applies edge coloring to glyph shape void edgeColoring(double angleThreshold, unsigned long long seed); @@ -21,8 +22,14 @@ public: void wrapBox(double scale, double range, double miterLimit); /// Sets the glyph's box's position in the atlas void placeBox(int x, int y); - /// Returns the glyph's Unicode index + /// Returns the glyph's index within the font + int getIndex() const; + /// Returns the glyph's index as a msdfgen::GlyphIndex + msdfgen::GlyphIndex getGlyphIndex() const; + /// Returns the Unicode codepoint represented by the glyph or 0 if unknown unicode_t getCodepoint() const; + /// Returns the glyph's identifier specified by the supplied identifier type + int getIdentifier(GlyphIdentifierType type) const; /// Returns the glyph's shape const msdfgen::Shape & getShape() const; /// Returns the glyph's advance @@ -47,6 +54,7 @@ public: operator GlyphBox() const; private: + int index; unicode_t codepoint; msdfgen::Shape shape; msdfgen::Shape::Bounds bounds; diff --git a/msdf-atlas-gen/artery-font-export.cpp b/msdf-atlas-gen/artery-font-export.cpp index 6e151ef..75a9e3c 100644 --- a/msdf-atlas-gen/artery-font-export.cpp +++ b/msdf-atlas-gen/artery-font-export.cpp @@ -24,6 +24,16 @@ static artery_font::ImageType convertImageType(ImageType imageType) { return artery_font::IMAGE_NONE; } +static artery_font::CodepointType convertCodepointType(GlyphIdentifierType glyphIdentifierType) { + switch (glyphIdentifierType) { + case GlyphIdentifierType::GLYPH_INDEX: + return artery_font::CP_INDEXED; + case GlyphIdentifierType::UNICODE_CODEPOINT: + return artery_font::CP_UNICODE; + } + return artery_font::CP_UNSPECIFIED; +} + template static bool encodeTiff(std::vector &output, const msdfgen::BitmapConstRef &atlas) { // TODO @@ -43,7 +53,7 @@ artery_font::PixelFormat getPixelFormat() { } template -bool exportArteryFont(msdfgen::FontHandle *font, const GlyphGeometry *glyphs, int glyphCount, double fontSize, double pxRange, const msdfgen::BitmapConstRef &atlas, ImageType imageType, ImageFormat imageFormat, const char *filename) { +bool exportArteryFont(msdfgen::FontHandle *font, const GlyphGeometry *glyphs, int glyphCount, const msdfgen::BitmapConstRef &atlas, const char *filename, const ArteryFontExportProperties &properties) { artery_font::StdArteryFont arfont = { }; arfont.metadataFormat = artery_font::METADATA_NONE; @@ -53,11 +63,11 @@ bool exportArteryFont(msdfgen::FontHandle *font, const GlyphGeometry *glyphs, in return false; double fsScale = 1/fontMetrics.emSize; artery_font::StdFontVariant fontVariant = { }; - fontVariant.codepointType = artery_font::CP_UNICODE; - fontVariant.imageType = convertImageType(imageType); - fontVariant.metrics.fontSize = REAL(fontSize); - if (imageType != ImageType::HARD_MASK) - fontVariant.metrics.distanceRange = REAL(pxRange); + fontVariant.codepointType = convertCodepointType(properties.glyphIdentifierType); + fontVariant.imageType = convertImageType(properties.imageType); + fontVariant.metrics.fontSize = REAL(properties.fontSize); + if (properties.imageType != ImageType::HARD_MASK) + fontVariant.metrics.distanceRange = REAL(properties.pxRange); fontVariant.metrics.emSize = REAL(fsScale*fontMetrics.emSize); fontVariant.metrics.ascender = REAL(fsScale*fontMetrics.ascenderY); fontVariant.metrics.descender = REAL(fsScale*fontMetrics.descenderY); @@ -67,7 +77,7 @@ bool exportArteryFont(msdfgen::FontHandle *font, const GlyphGeometry *glyphs, in fontVariant.glyphs = artery_font::StdList >(glyphCount); for (int i = 0; i < glyphCount; ++i) { artery_font::Glyph &glyph = fontVariant.glyphs[i]; - glyph.codepoint = glyphs[i].getCodepoint(); + glyph.codepoint = glyphs[i].getIdentifier(properties.glyphIdentifierType); glyph.image = 0; double l, b, r, t; glyphs[i].getQuadPlaneBounds(l, b, r, t); @@ -84,10 +94,10 @@ bool exportArteryFont(msdfgen::FontHandle *font, const GlyphGeometry *glyphs, in glyph.advance.v = REAL(0); for (int j = 0; j < glyphCount; ++j) { double kerning; - if (msdfgen::getKerning(kerning, font, glyphs[i].getCodepoint(), glyphs[j].getCodepoint()) && kerning) { + if (msdfgen::getKerning(kerning, font, glyphs[i].getGlyphIndex(), glyphs[j].getGlyphIndex()) && kerning) { artery_font::KernPair kernPair = { }; - kernPair.codepoint1 = glyphs[i].getCodepoint(); - kernPair.codepoint2 = glyphs[j].getCodepoint(); + kernPair.codepoint1 = glyphs[i].getIdentifier(properties.glyphIdentifierType); + kernPair.codepoint2 = glyphs[j].getIdentifier(properties.glyphIdentifierType); kernPair.advance.h = REAL(fsScale*kerning); fontVariant.kernPairs.vector.push_back((artery_font::KernPair &&) kernPair); } @@ -101,8 +111,8 @@ bool exportArteryFont(msdfgen::FontHandle *font, const GlyphGeometry *glyphs, in image.width = atlas.width; image.height = atlas.height; image.channels = N; - image.imageType = convertImageType(imageType); - switch (imageFormat) { + image.imageType = convertImageType(properties.imageType); + switch (properties.imageFormat) { case ImageFormat::PNG: image.encoding = artery_font::IMAGE_PNG; image.pixelFormat = artery_font::PIXEL_UNSIGNED8; @@ -139,11 +149,11 @@ bool exportArteryFont(msdfgen::FontHandle *font, const GlyphGeometry *glyphs, in return artery_font::writeFile(arfont, filename); } -template bool exportArteryFont(msdfgen::FontHandle *font, const GlyphGeometry *glyphs, int glyphCount, double fontSize, double pxRange, const msdfgen::BitmapConstRef &atlas, ImageType imageType, ImageFormat imageFormat, const char *filename); -template bool exportArteryFont(msdfgen::FontHandle *font, const GlyphGeometry *glyphs, int glyphCount, double fontSize, double pxRange, const msdfgen::BitmapConstRef &atlas, ImageType imageType, ImageFormat imageFormat, const char *filename); -template bool exportArteryFont(msdfgen::FontHandle *font, const GlyphGeometry *glyphs, int glyphCount, double fontSize, double pxRange, const msdfgen::BitmapConstRef &atlas, ImageType imageType, ImageFormat imageFormat, const char *filename); -template bool exportArteryFont(msdfgen::FontHandle *font, const GlyphGeometry *glyphs, int glyphCount, double fontSize, double pxRange, const msdfgen::BitmapConstRef &atlas, ImageType imageType, ImageFormat imageFormat, const char *filename); -template bool exportArteryFont(msdfgen::FontHandle *font, const GlyphGeometry *glyphs, int glyphCount, double fontSize, double pxRange, const msdfgen::BitmapConstRef &atlas, ImageType imageType, ImageFormat imageFormat, const char *filename); -template bool exportArteryFont(msdfgen::FontHandle *font, const GlyphGeometry *glyphs, int glyphCount, double fontSize, double pxRange, const msdfgen::BitmapConstRef &atlas, ImageType imageType, ImageFormat imageFormat, const char *filename); +template bool exportArteryFont(msdfgen::FontHandle *font, const GlyphGeometry *glyphs, int glyphCount, const msdfgen::BitmapConstRef &atlas, const char *filename, const ArteryFontExportProperties &properties); +template bool exportArteryFont(msdfgen::FontHandle *font, const GlyphGeometry *glyphs, int glyphCount, const msdfgen::BitmapConstRef &atlas, const char *filename, const ArteryFontExportProperties &properties); +template bool exportArteryFont(msdfgen::FontHandle *font, const GlyphGeometry *glyphs, int glyphCount, const msdfgen::BitmapConstRef &atlas, const char *filename, const ArteryFontExportProperties &properties); +template bool exportArteryFont(msdfgen::FontHandle *font, const GlyphGeometry *glyphs, int glyphCount, const msdfgen::BitmapConstRef &atlas, const char *filename, const ArteryFontExportProperties &properties); +template bool exportArteryFont(msdfgen::FontHandle *font, const GlyphGeometry *glyphs, int glyphCount, const msdfgen::BitmapConstRef &atlas, const char *filename, const ArteryFontExportProperties &properties); +template bool exportArteryFont(msdfgen::FontHandle *font, const GlyphGeometry *glyphs, int glyphCount, const msdfgen::BitmapConstRef &atlas, const char *filename, const ArteryFontExportProperties &properties); } diff --git a/msdf-atlas-gen/artery-font-export.h b/msdf-atlas-gen/artery-font-export.h index fe2abdc..4d907a7 100644 --- a/msdf-atlas-gen/artery-font-export.h +++ b/msdf-atlas-gen/artery-font-export.h @@ -8,8 +8,16 @@ namespace msdf_atlas { +struct ArteryFontExportProperties { + GlyphIdentifierType glyphIdentifierType; + double fontSize; + double pxRange; + ImageType imageType; + ImageFormat imageFormat; +}; + /// Encodes the atlas bitmap and its layout into an Artery Atlas Font file template -bool exportArteryFont(msdfgen::FontHandle *font, const GlyphGeometry *glyphs, int glyphCount, double fontSize, double pxRange, const msdfgen::BitmapConstRef &atlas, ImageType imageType, ImageFormat imageFormat, const char *filename); +bool exportArteryFont(msdfgen::FontHandle *font, const GlyphGeometry *glyphs, int glyphCount, const msdfgen::BitmapConstRef &atlas, const char *filename, const ArteryFontExportProperties &properties); } diff --git a/msdf-atlas-gen/charset-parser.cpp b/msdf-atlas-gen/charset-parser.cpp index a272f5a..23c68a3 100644 --- a/msdf-atlas-gen/charset-parser.cpp +++ b/msdf-atlas-gen/charset-parser.cpp @@ -96,7 +96,7 @@ static std::string combinePath(const char *basePath, const char *relPath) { return std::string(basePath, lastSlash+1)+relPath; } -bool Charset::load(const char *filename) { +bool Charset::load(const char *filename, bool disableCharLiterals) { if (FILE *f = fopen(filename, "rb")) { @@ -125,7 +125,7 @@ bool Charset::load(const char *filename) { goto FAIL; switch (state) { case CLEAR: - if (cp > 0) + if (cp >= 0) add((unicode_t) cp); state = TIGHT; break; @@ -144,7 +144,7 @@ bool Charset::load(const char *filename) { buffer.clear(); continue; // next character already read case '\'': // single UTF-8 character - if (!(state == CLEAR || state == RANGE_BRACKET || state == RANGE_SEPARATOR)) + if (!(state == CLEAR || state == RANGE_BRACKET || state == RANGE_SEPARATOR) || disableCharLiterals) goto FAIL; if (!readString(buffer, f, '\'')) goto FAIL; @@ -173,7 +173,7 @@ bool Charset::load(const char *filename) { buffer.clear(); break; case '"': // string of UTF-8 characters - if (state != CLEAR) + if (state != CLEAR || disableCharLiterals) goto FAIL; if (!readString(buffer, f, '"')) goto FAIL; diff --git a/msdf-atlas-gen/csv-export.cpp b/msdf-atlas-gen/csv-export.cpp index e48fbe1..6dc5a91 100644 --- a/msdf-atlas-gen/csv-export.cpp +++ b/msdf-atlas-gen/csv-export.cpp @@ -5,7 +5,7 @@ namespace msdf_atlas { -bool exportCSV(const GlyphGeometry *glyphs, int glyphCount, double emSize, const char *filename) { +bool exportCSV(const GlyphGeometry *glyphs, int glyphCount, GlyphIdentifierType glyphIdentifierType, double emSize, const char *filename) { FILE *f = fopen(filename, "w"); if (!f) return false; @@ -13,7 +13,7 @@ bool exportCSV(const GlyphGeometry *glyphs, int glyphCount, double emSize, const double fsScale = 1/emSize; for (int i = 0; i < glyphCount; ++i) { double l, b, r, t; - fprintf(f, "%u,%.17g,", glyphs[i].getCodepoint(), fsScale*glyphs[i].getAdvance()); + fprintf(f, "%d,%.17g,", glyphs[i].getIdentifier(glyphIdentifierType), fsScale*glyphs[i].getAdvance()); glyphs[i].getQuadPlaneBounds(l, b, r, t); fprintf(f, "%.17g,%.17g,%.17g,%.17g,", fsScale*l, fsScale*b, fsScale*r, fsScale*t); glyphs[i].getQuadAtlasBounds(l, b, r, t); diff --git a/msdf-atlas-gen/csv-export.h b/msdf-atlas-gen/csv-export.h index 492c50f..4ea8dfc 100644 --- a/msdf-atlas-gen/csv-export.h +++ b/msdf-atlas-gen/csv-export.h @@ -1,14 +1,15 @@ #pragma once +#include "types.h" #include "GlyphGeometry.h" namespace msdf_atlas { /** * Writes the positioning data and atlas layout of the glyphs into a CSV file - * The columns are: Unicode index, horizontal advance, plane bounds (l, b, r, t), atlas bounds (l, b, r, t) + * The columns are: glyph identifier (index or Unicode), horizontal advance, plane bounds (l, b, r, t), atlas bounds (l, b, r, t) */ -bool exportCSV(const GlyphGeometry *glyphs, int glyphCount, double emSize, const char *filename); +bool exportCSV(const GlyphGeometry *glyphs, int glyphCount, GlyphIdentifierType glyphIdentifierType, double emSize, const char *filename); } diff --git a/msdf-atlas-gen/json-export.cpp b/msdf-atlas-gen/json-export.cpp index ec6c332..a7048ee 100644 --- a/msdf-atlas-gen/json-export.cpp +++ b/msdf-atlas-gen/json-export.cpp @@ -21,7 +21,7 @@ static const char * imageTypeString(ImageType type) { return nullptr; } -bool exportJSON(msdfgen::FontHandle *font, const GlyphGeometry *glyphs, int glyphCount, double fontSize, double pxRange, int atlasWidth, int atlasHeight, ImageType imageType, const char *filename) { +bool exportJSON(msdfgen::FontHandle *font, const GlyphGeometry *glyphs, int glyphCount, GlyphIdentifierType glyphIdentifierType, double fontSize, double pxRange, int atlasWidth, int atlasHeight, ImageType imageType, const char *filename) { msdfgen::FontMetrics fontMetrics; if (!msdfgen::getFontMetrics(fontMetrics, font)) return false; @@ -56,7 +56,14 @@ bool exportJSON(msdfgen::FontHandle *font, const GlyphGeometry *glyphs, int glyp fputs("\"glyphs\":[", f); for (int i = 0; i < glyphCount; ++i) { fputs(i == 0 ? "{" : ",{", f); - fprintf(f, "\"unicode\":%u,", glyphs[i].getCodepoint()); + switch (glyphIdentifierType) { + case GlyphIdentifierType::GLYPH_INDEX: + fprintf(f, "\"index\":%d,", glyphs[i].getIndex()); + break; + case GlyphIdentifierType::UNICODE_CODEPOINT: + fprintf(f, "\"unicode\":%u,", glyphs[i].getCodepoint()); + break; + } fprintf(f, "\"advance\":%.17g", fsScale*glyphs[i].getAdvance()); double l, b, r, t; glyphs[i].getQuadPlaneBounds(l, b, r, t); diff --git a/msdf-atlas-gen/json-export.h b/msdf-atlas-gen/json-export.h index 9299274..7256e16 100644 --- a/msdf-atlas-gen/json-export.h +++ b/msdf-atlas-gen/json-export.h @@ -9,6 +9,6 @@ namespace msdf_atlas { /// Writes the font and glyph metrics and atlas layout data into a comprehensive JSON file -bool exportJSON(msdfgen::FontHandle *font, const GlyphGeometry *glyphs, int glyphCount, double fontSize, double pxRange, int atlasWidth, int atlasHeight, ImageType imageType, const char *filename); +bool exportJSON(msdfgen::FontHandle *font, const GlyphGeometry *glyphs, int glyphCount, GlyphIdentifierType glyphIdentifierType, double fontSize, double pxRange, int atlasWidth, int atlasHeight, ImageType imageType, const char *filename); } diff --git a/msdf-atlas-gen/main.cpp b/msdf-atlas-gen/main.cpp index 65c0ae9..238175f 100644 --- a/msdf-atlas-gen/main.cpp +++ b/msdf-atlas-gen/main.cpp @@ -44,6 +44,8 @@ INPUT SPECIFICATION Specifies the input TrueType / OpenType font file. This is required. -charset Specifies the input character set. Refer to the documentation for format of charset specification. Defaults to ASCII. + -glyphset + Specifies the set of input glyphs as glyph indices within the font file. ATLAS CONFIGURATION -type @@ -145,7 +147,19 @@ static bool cmpExtension(const char *path, const char *ext) { return true; } -static void loadGlyphs(std::vector &glyphs, msdfgen::FontHandle *font, const Charset &charset, bool preprocessGeometry) { +static void loadGlyphsByIndex(std::vector &glyphs, msdfgen::FontHandle *font, const Charset &charset, bool preprocessGeometry) { + glyphs.clear(); + glyphs.reserve(charset.size()); + for (unicode_t cp : charset) { + GlyphGeometry glyph; + if (glyph.load(font, msdfgen::GlyphIndex(cp), preprocessGeometry)) + glyphs.push_back((GlyphGeometry &&) glyph); + else + printf("Glyph # 0x%X missing\n", cp); + } +} + +static void loadGlyphsByUnicode(std::vector &glyphs, msdfgen::FontHandle *font, const Charset &charset, bool preprocessGeometry) { glyphs.clear(); glyphs.reserve(charset.size()); for (unicode_t cp : charset) { @@ -158,6 +172,7 @@ static void loadGlyphs(std::vector &glyphs, msdfgen::FontHandle * } struct Configuration { + GlyphIdentifierType glyphIdentifierType; ImageType imageType; ImageFormat imageFormat; int width, height; @@ -197,7 +212,13 @@ static bool makeAtlas(const std::vector &glyphs, msdfgen::FontHan } if (config.arteryFontFilename) { - if (exportArteryFont(font, glyphs.data(), glyphs.size(), config.emSize, config.pxRange, bitmap, config.imageType, config.imageFormat, config.arteryFontFilename)) + ArteryFontExportProperties arfontProps; + arfontProps.glyphIdentifierType = config.glyphIdentifierType; + arfontProps.fontSize = config.emSize; + arfontProps.pxRange = config.pxRange; + arfontProps.imageType = config.imageType; + arfontProps.imageFormat = config.imageFormat; + if (exportArteryFont(font, glyphs.data(), glyphs.size(), bitmap, config.arteryFontFilename, arfontProps)) puts("Artery Font file generated."); else { success = false; @@ -215,6 +236,7 @@ int main(int argc, const char * const *argv) { Configuration config = { }; const char *fontFilename = nullptr; const char *charsetFilename = nullptr; + config.glyphIdentifierType = GlyphIdentifierType::UNICODE_CODEPOINT; config.imageType = ImageType::MSDF; config.imageFormat = ImageFormat::UNSPECIFIED; const char *imageFormatName = nullptr; @@ -299,6 +321,13 @@ int main(int argc, const char * const *argv) { } ARG_CASE("-charset", 1) { charsetFilename = argv[++argPos]; + config.glyphIdentifierType = GlyphIdentifierType::UNICODE_CODEPOINT; + ++argPos; + continue; + } + ARG_CASE("-glyphset", 1) { + charsetFilename = argv[++argPos]; + config.glyphIdentifierType = GlyphIdentifierType::GLYPH_INDEX; ++argPos; continue; } @@ -593,17 +622,24 @@ int main(int argc, const char * const *argv) { // Load character set Charset charset; if (charsetFilename) { - if (!charset.load(charsetFilename)) - ABORT("Failed to load character set specification."); + if (!charset.load(charsetFilename, config.glyphIdentifierType != GlyphIdentifierType::UNICODE_CODEPOINT)) + ABORT(config.glyphIdentifierType == GlyphIdentifierType::GLYPH_INDEX ? "Failed to load glyph set specification." : "Failed to load character set specification."); } else charset = Charset::ASCII; - if (charset.empty()) - ABORT("No character set loaded."); // Load glyphs std::vector glyphs; - loadGlyphs(glyphs, font, charset, config.preprocessGeometry); - printf("Loaded geometry of %d out of %d characters.\n", (int) glyphs.size(), (int) charset.size()); + switch (config.glyphIdentifierType) { + case GlyphIdentifierType::GLYPH_INDEX: + loadGlyphsByIndex(glyphs, font, charset, config.preprocessGeometry); + break; + case GlyphIdentifierType::UNICODE_CODEPOINT: + loadGlyphsByUnicode(glyphs, font, charset, config.preprocessGeometry); + break; + } + if (glyphs.empty()) + ABORT("No glyphs loaded."); + printf("Loaded geometry of %d out of %d %s.\n", (int) glyphs.size(), (int) charset.size(), config.glyphIdentifierType == GlyphIdentifierType::GLYPH_INDEX ? "glyphs" : "characters"); // Determine final atlas dimensions, scale and range, pack glyphs { @@ -709,7 +745,7 @@ int main(int argc, const char * const *argv) { } if (config.csvFilename) { - if (exportCSV(glyphs.data(), glyphs.size(), fontMetrics.emSize, config.csvFilename)) + if (exportCSV(glyphs.data(), glyphs.size(), config.glyphIdentifierType, fontMetrics.emSize, config.csvFilename)) puts("Glyph layout written into CSV file."); else { result = 1; @@ -717,7 +753,7 @@ int main(int argc, const char * const *argv) { } } if (config.jsonFilename) { - if (exportJSON(font, glyphs.data(), glyphs.size(), config.emSize, config.pxRange, config.width, config.height, config.imageType, config.jsonFilename)) + if (exportJSON(font, glyphs.data(), glyphs.size(), config.glyphIdentifierType, config.emSize, config.pxRange, config.width, config.height, config.imageType, config.jsonFilename)) puts("Glyph layout and metadata written into JSON file."); else { result = 1; @@ -726,14 +762,19 @@ int main(int argc, const char * const *argv) { } if (config.shadronPreviewFilename && config.shadronPreviewText) { - std::vector previewText; - utf8Decode(previewText, config.shadronPreviewText); - previewText.push_back(0); - if (generateShadronPreview(font, glyphs.data(), glyphs.size(), config.imageType, config.width, config.height, config.pxRange, previewText.data(), config.imageFilename, config.shadronPreviewFilename)) - puts("Shadron preview script generated."); - else { + if (config.glyphIdentifierType == GlyphIdentifierType::UNICODE_CODEPOINT) { + std::vector previewText; + utf8Decode(previewText, config.shadronPreviewText); + previewText.push_back(0); + if (generateShadronPreview(font, glyphs.data(), glyphs.size(), config.imageType, config.width, config.height, config.pxRange, previewText.data(), config.imageFilename, config.shadronPreviewFilename)) + puts("Shadron preview script generated."); + else { + result = 1; + puts("Failed to generate Shadron preview file."); + } + } else { result = 1; - puts("Failed to generate Shadron preview file."); + puts("Shadron preview not supported in -glyphset mode."); } } diff --git a/msdf-atlas-gen/types.h b/msdf-atlas-gen/types.h index 6f45c95..613cb7f 100644 --- a/msdf-atlas-gen/types.h +++ b/msdf-atlas-gen/types.h @@ -37,4 +37,10 @@ enum class ImageFormat { BINARY_FLOAT_BE }; +/// Glyph identification +enum class GlyphIdentifierType { + GLYPH_INDEX, + UNICODE_CODEPOINT +}; + }