Support for multiple fonts in one atlas

This commit is contained in:
Chlumsky 2021-03-13 01:21:44 +01:00 committed by Viktor Chlumský
parent e44853e47e
commit 690ae5fb16
17 changed files with 643 additions and 240 deletions

View File

@ -45,6 +45,8 @@ Use the following command line arguments for the standalone version of the atlas
- `-font <fontfile.ttf/otf>` &ndash; sets the input font file. - `-font <fontfile.ttf/otf>` &ndash; sets the input font file.
- `-charset <charset.txt>` &ndash; sets the character set. The ASCII charset will be used if not specified. See [the syntax specification](#character-set-specification-syntax) of `charset.txt`. - `-charset <charset.txt>` &ndash; 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 <glyphset.txt>` &ndash; sets the set of input glyphs using their indices within the font file. See [the syntax specification](#glyph-set-specification). - `-glyphset <glyphset.txt>` &ndash; sets the set of input glyphs using their indices within the font file. See [the syntax specification](#glyph-set-specification).
- `-fontscale <scale>` &ndash; applies a scaling transformation to the font's glyphs. Mainly to be used to generate multiple sizes in a single atlas, otherwise use [`-size`](#glyph-configuration).
- `-and` &ndash; separates multiple inputs to be combined into a single atlas.
### Bitmap atlas type ### Bitmap atlas type

View File

@ -314,6 +314,7 @@
<ClCompile Include="msdf-atlas-gen\charset-parser.cpp" /> <ClCompile Include="msdf-atlas-gen\charset-parser.cpp" />
<ClCompile Include="msdf-atlas-gen\Charset.cpp" /> <ClCompile Include="msdf-atlas-gen\Charset.cpp" />
<ClCompile Include="msdf-atlas-gen\csv-export.cpp" /> <ClCompile Include="msdf-atlas-gen\csv-export.cpp" />
<ClCompile Include="msdf-atlas-gen\FontGeometry.cpp" />
<ClCompile Include="msdf-atlas-gen\glyph-generators.cpp" /> <ClCompile Include="msdf-atlas-gen\glyph-generators.cpp" />
<ClCompile Include="msdf-atlas-gen\GlyphGeometry.cpp" /> <ClCompile Include="msdf-atlas-gen\GlyphGeometry.cpp" />
<ClCompile Include="msdf-atlas-gen\image-encode.cpp" /> <ClCompile Include="msdf-atlas-gen\image-encode.cpp" />
@ -337,6 +338,7 @@
<ClInclude Include="msdf-atlas-gen\csv-export.h" /> <ClInclude Include="msdf-atlas-gen\csv-export.h" />
<ClInclude Include="msdf-atlas-gen\DynamicAtlas.h" /> <ClInclude Include="msdf-atlas-gen\DynamicAtlas.h" />
<ClInclude Include="msdf-atlas-gen\DynamicAtlas.hpp" /> <ClInclude Include="msdf-atlas-gen\DynamicAtlas.hpp" />
<ClInclude Include="msdf-atlas-gen\FontGeometry.h" />
<ClInclude Include="msdf-atlas-gen\glyph-generators.h" /> <ClInclude Include="msdf-atlas-gen\glyph-generators.h" />
<ClInclude Include="msdf-atlas-gen\image-encode.h" /> <ClInclude Include="msdf-atlas-gen\image-encode.h" />
<ClInclude Include="msdf-atlas-gen\Charset.h" /> <ClInclude Include="msdf-atlas-gen\Charset.h" />

View File

@ -66,6 +66,9 @@
<ClCompile Include="msdf-atlas-gen\Workload.cpp"> <ClCompile Include="msdf-atlas-gen\Workload.cpp">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="msdf-atlas-gen\FontGeometry.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="resource.h"> <ClInclude Include="resource.h">
@ -164,6 +167,9 @@
<ClInclude Include="msdf-atlas-gen\Workload.h"> <ClInclude Include="msdf-atlas-gen\Workload.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="msdf-atlas-gen\FontGeometry.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ResourceCompile Include="msdf-atlas-gen.rc"> <ResourceCompile Include="msdf-atlas-gen.rc">

View File

@ -0,0 +1,172 @@
#include "FontGeometry.h"
namespace msdf_atlas {
FontGeometry::GlyphRange::GlyphRange() : glyphs(), rangeStart(), rangeEnd() { }
FontGeometry::GlyphRange::GlyphRange(const std::vector<GlyphGeometry> *glyphs, size_t rangeStart, size_t rangeEnd) : glyphs(glyphs), rangeStart(rangeStart), rangeEnd(rangeEnd) { }
size_t FontGeometry::GlyphRange::size() const {
return glyphs->size();
}
bool FontGeometry::GlyphRange::empty() const {
return glyphs->empty();
}
const GlyphGeometry * FontGeometry::GlyphRange::begin() const {
return glyphs->data()+rangeStart;
}
const GlyphGeometry * FontGeometry::GlyphRange::end() const {
return glyphs->data()+rangeEnd;
}
FontGeometry::FontGeometry() : geometryScale(1), metrics(), preferredIdentifierType(GlyphIdentifierType::UNICODE_CODEPOINT), glyphs(&ownGlyphs), rangeStart(glyphs->size()), rangeEnd(glyphs->size()) { }
FontGeometry::FontGeometry(std::vector<GlyphGeometry> *glyphStorage) : geometryScale(1), metrics(), preferredIdentifierType(GlyphIdentifierType::UNICODE_CODEPOINT), glyphs(glyphStorage), rangeStart(glyphs->size()), rangeEnd(glyphs->size()) { }
int FontGeometry::loadGlyphset(msdfgen::FontHandle *font, double fontScale, const Charset &glyphset, bool preprocessGeometry, bool enableKerning) {
if (!(glyphs->size() == rangeEnd && loadMetrics(font, fontScale)))
return -1;
glyphs->reserve(glyphs->size()+glyphset.size());
int loaded = 0;
for (unicode_t index : glyphset) {
GlyphGeometry glyph;
if (glyph.load(font, geometryScale, msdfgen::GlyphIndex(index), preprocessGeometry)) {
addGlyph((GlyphGeometry &&) glyph);
++loaded;
}
}
if (enableKerning)
loadKerning(font);
preferredIdentifierType = GlyphIdentifierType::GLYPH_INDEX;
return loaded;
}
int FontGeometry::loadCharset(msdfgen::FontHandle *font, double fontScale, const Charset &charset, bool preprocessGeometry, bool enableKerning) {
if (!(glyphs->size() == rangeEnd && loadMetrics(font, fontScale)))
return -1;
glyphs->reserve(glyphs->size()+charset.size());
int loaded = 0;
for (unicode_t cp : charset) {
GlyphGeometry glyph;
if (glyph.load(font, geometryScale, cp, preprocessGeometry)) {
addGlyph((GlyphGeometry &&) glyph);
++loaded;
}
}
if (enableKerning)
loadKerning(font);
preferredIdentifierType = GlyphIdentifierType::UNICODE_CODEPOINT;
return loaded;
}
bool FontGeometry::loadMetrics(msdfgen::FontHandle *font, double fontScale) {
if (!msdfgen::getFontMetrics(metrics, font))
return false;
if (metrics.emSize <= 0)
metrics.emSize = MSDF_ATLAS_DEFAULT_EM_SIZE;
geometryScale = fontScale/metrics.emSize;
metrics.emSize *= geometryScale;
metrics.ascenderY *= geometryScale;
metrics.descenderY *= geometryScale;
metrics.lineHeight *= geometryScale;
metrics.underlineY *= geometryScale;
metrics.underlineThickness *= geometryScale;
return true;
}
bool FontGeometry::addGlyph(const GlyphGeometry &glyph) {
if (glyphs->size() != rangeEnd)
return false;
glyphsByIndex.insert(std::make_pair(glyph.getIndex(), rangeEnd));
if (glyph.getCodepoint())
glyphsByCodepoint.insert(std::make_pair(glyph.getCodepoint(), rangeEnd));
glyphs->push_back(glyph);
++rangeEnd;
return true;
}
bool FontGeometry::addGlyph(GlyphGeometry &&glyph) {
if (glyphs->size() != rangeEnd)
return false;
glyphsByIndex.insert(std::make_pair(glyph.getIndex(), rangeEnd));
if (glyph.getCodepoint())
glyphsByCodepoint.insert(std::make_pair(glyph.getCodepoint(), rangeEnd));
glyphs->push_back((GlyphGeometry &&) glyph);
++rangeEnd;
return true;
}
int FontGeometry::loadKerning(msdfgen::FontHandle *font) {
int loaded = 0;
for (size_t i = rangeStart; i < rangeEnd; ++i)
for (size_t j = rangeStart; j < rangeEnd; ++j) {
double advance;
if (msdfgen::getKerning(advance, font, (*glyphs)[i].getGlyphIndex(), (*glyphs)[j].getGlyphIndex()) && advance) {
kerning[std::make_pair<int, int>((*glyphs)[i].getIndex(), (*glyphs)[j].getIndex())] = geometryScale*advance;
++loaded;
}
}
return loaded;
}
double FontGeometry::getGeometryScale() const {
return geometryScale;
}
const msdfgen::FontMetrics & FontGeometry::getMetrics() const {
return metrics;
}
GlyphIdentifierType FontGeometry::getPreferredIdentifierType() const {
return preferredIdentifierType;
}
FontGeometry::GlyphRange FontGeometry::getGlyphs() const {
return GlyphRange(glyphs, rangeStart, rangeEnd);
}
const GlyphGeometry * FontGeometry::getGlyph(msdfgen::GlyphIndex index) const {
std::map<int, size_t>::const_iterator it = glyphsByIndex.find(index.getIndex());
if (it != glyphsByIndex.end())
return &(*glyphs)[it->second];
return nullptr;
}
const GlyphGeometry * FontGeometry::getGlyph(unicode_t codepoint) const {
std::map<unicode_t, size_t>::const_iterator it = glyphsByCodepoint.find(codepoint);
if (it != glyphsByCodepoint.end())
return &(*glyphs)[it->second];
return nullptr;
}
bool FontGeometry::getAdvance(double &advance, msdfgen::GlyphIndex index1, msdfgen::GlyphIndex index2) const {
const GlyphGeometry *glyph1 = getGlyph(index1);
if (!glyph1)
return false;
advance = glyph1->getAdvance();
std::map<std::pair<int, int>, double>::const_iterator it = kerning.find(std::make_pair<int, int>(index1.getIndex(), index2.getIndex()));
if (it != kerning.end())
advance += it->second;
return true;
}
bool FontGeometry::getAdvance(double &advance, unicode_t codepoint1, unicode_t codepoint2) const {
const GlyphGeometry *glyph1, *glyph2;
if (!((glyph1 = getGlyph(codepoint1)) && (glyph2 = getGlyph(codepoint2))))
return false;
advance = glyph1->getAdvance();
std::map<std::pair<int, int>, double>::const_iterator it = kerning.find(std::make_pair<int, int>(glyph1->getIndex(), glyph2->getIndex()));
if (it != kerning.end())
advance += it->second;
return true;
}
const std::map<std::pair<int, int>, double> & FontGeometry::getKerning() const {
return kerning;
}
}

View File

@ -0,0 +1,80 @@
#pragma once
#include <utility>
#include <vector>
#include <map>
#include <msdfgen.h>
#include <msdfgen-ext.h>
#include "types.h"
#include "GlyphGeometry.h"
#include "Charset.h"
#define MSDF_ATLAS_DEFAULT_EM_SIZE 32.0
namespace msdf_atlas {
/// Represents the geometry of all glyphs of a given font or font variant
class FontGeometry {
public:
class GlyphRange {
public:
GlyphRange();
GlyphRange(const std::vector<GlyphGeometry> *glyphs, size_t rangeStart, size_t rangeEnd);
size_t size() const;
bool empty() const;
const GlyphGeometry * begin() const;
const GlyphGeometry * end() const;
private:
const std::vector<GlyphGeometry> *glyphs;
size_t rangeStart, rangeEnd;
};
FontGeometry();
explicit FontGeometry(std::vector<GlyphGeometry> *glyphStorage);
/// Loads all glyphs in a glyphset (Charset elements are glyph indices), returns the number of successfully loaded glyphs
int loadGlyphset(msdfgen::FontHandle *font, double fontScale, const Charset &glyphset, bool preprocessGeometry = true, bool enableKerning = true);
/// Loads all glyphs in a charset (Charset elements are Unicode codepoints), returns the number of successfully loaded glyphs
int loadCharset(msdfgen::FontHandle *font, double fontScale, const Charset &charset, bool preprocessGeometry = true, bool enableKerning = true);
/// Only loads font metrics and geometry scale from font
bool loadMetrics(msdfgen::FontHandle *font, double fontScale);
/// Adds a loaded glyph
bool addGlyph(const GlyphGeometry &glyph);
bool addGlyph(GlyphGeometry &&glyph);
/// Loads kerning pairs for all glyphs that are currently present, returns the number of loaded kerning pairs
int loadKerning(msdfgen::FontHandle *font);
/// Returns the geometry scale to be used when loading glyphs
double getGeometryScale() const;
/// Returns the processed font metrics
const msdfgen::FontMetrics & getMetrics() const;
/// Returns the type of identifier that was used to load glyphs
GlyphIdentifierType getPreferredIdentifierType() const;
/// Returns the list of all glyphs
GlyphRange getGlyphs() const;
/// Finds a glyph by glyph index or Unicode codepoint, returns null if not found
const GlyphGeometry * getGlyph(msdfgen::GlyphIndex index) const;
const GlyphGeometry * getGlyph(unicode_t codepoint) const;
/// Outputs the advance between two glyphs with kerning taken into consideration, returns false on failure
bool getAdvance(double &advance, msdfgen::GlyphIndex index1, msdfgen::GlyphIndex index2) const;
bool getAdvance(double &advance, unicode_t codepoint1, unicode_t codepoint2) const;
/// Returns the complete mapping of kerning pairs (by glyph indices) and their respective advance values
const std::map<std::pair<int, int>, double> & getKerning() const;
private:
double geometryScale;
msdfgen::FontMetrics metrics;
GlyphIdentifierType preferredIdentifierType;
std::vector<GlyphGeometry> *glyphs;
size_t rangeStart, rangeEnd;
std::map<int, size_t> glyphsByIndex;
std::map<unicode_t, size_t> glyphsByCodepoint;
std::map<std::pair<int, int>, double> kerning;
std::vector<GlyphGeometry> ownGlyphs;
};
}

View File

@ -6,12 +6,14 @@
namespace msdf_atlas { namespace msdf_atlas {
GlyphGeometry::GlyphGeometry() : index(), codepoint(), bounds(), advance(), box() { } GlyphGeometry::GlyphGeometry() : index(), codepoint(), geometryScale(), bounds(), advance(), box() { }
bool GlyphGeometry::load(msdfgen::FontHandle *font, msdfgen::GlyphIndex index, bool preprocessGeometry) { bool GlyphGeometry::load(msdfgen::FontHandle *font, double geometryScale, msdfgen::GlyphIndex index, bool preprocessGeometry) {
if (font && msdfgen::loadGlyph(shape, font, index, &advance) && shape.validate()) { if (font && msdfgen::loadGlyph(shape, font, index, &advance) && shape.validate()) {
this->index = index.getIndex(); this->index = index.getIndex();
this->geometryScale = geometryScale;
codepoint = 0; codepoint = 0;
advance *= geometryScale;
#ifdef MSDFGEN_USE_SKIA #ifdef MSDFGEN_USE_SKIA
if (preprocessGeometry) if (preprocessGeometry)
msdfgen::resolveShapeGeometry(shape); msdfgen::resolveShapeGeometry(shape);
@ -34,10 +36,10 @@ bool GlyphGeometry::load(msdfgen::FontHandle *font, msdfgen::GlyphIndex index, b
return false; return false;
} }
bool GlyphGeometry::load(msdfgen::FontHandle *font, unicode_t codepoint, bool preprocessGeometry) { bool GlyphGeometry::load(msdfgen::FontHandle *font, double geometryScale, unicode_t codepoint, bool preprocessGeometry) {
msdfgen::GlyphIndex index; msdfgen::GlyphIndex index;
if (msdfgen::getGlyphIndex(index, font, codepoint)) { if (msdfgen::getGlyphIndex(index, font, codepoint)) {
if (load(font, index, preprocessGeometry)) { if (load(font, geometryScale, index, preprocessGeometry)) {
this->codepoint = codepoint; this->codepoint = codepoint;
return true; return true;
} }
@ -50,6 +52,8 @@ void GlyphGeometry::edgeColoring(double angleThreshold, unsigned long long seed)
} }
void GlyphGeometry::wrapBox(double scale, double range, double miterLimit) { void GlyphGeometry::wrapBox(double scale, double range, double miterLimit) {
scale *= geometryScale;
range /= geometryScale;
box.range = range; box.range = range;
box.scale = scale; box.scale = scale;
if (bounds.l < bounds.r && bounds.b < bounds.t) { if (bounds.l < bounds.r && bounds.b < bounds.t) {
@ -127,10 +131,11 @@ msdfgen::Vector2 GlyphGeometry::getBoxTranslate() const {
void GlyphGeometry::getQuadPlaneBounds(double &l, double &b, double &r, double &t) const { void GlyphGeometry::getQuadPlaneBounds(double &l, double &b, double &r, double &t) const {
if (box.rect.w > 0 && box.rect.h > 0) { if (box.rect.w > 0 && box.rect.h > 0) {
l = -box.translate.x+.5/box.scale; double invBoxScale = 1/box.scale;
b = -box.translate.y+.5/box.scale; l = geometryScale*(-box.translate.x+.5*invBoxScale);
r = -box.translate.x+(box.rect.w-.5)/box.scale; b = geometryScale*(-box.translate.y+.5*invBoxScale);
t = -box.translate.y+(box.rect.h-.5)/box.scale; r = geometryScale*(-box.translate.x+(box.rect.w-.5)*invBoxScale);
t = geometryScale*(-box.translate.y+(box.rect.h-.5)*invBoxScale);
} else } else
l = 0, b = 0, r = 0, t = 0; l = 0, b = 0, r = 0, t = 0;
} }

View File

@ -8,14 +8,14 @@
namespace msdf_atlas { namespace msdf_atlas {
/// Represent's the shape geometry of a single glyph as well as its configuration /// Represents the shape geometry of a single glyph as well as its configuration
class GlyphGeometry { class GlyphGeometry {
public: public:
GlyphGeometry(); GlyphGeometry();
/// Loads glyph geometry from font /// Loads glyph geometry from font
bool load(msdfgen::FontHandle *font, msdfgen::GlyphIndex index, bool preprocessGeometry = true); bool load(msdfgen::FontHandle *font, double geometryScale, msdfgen::GlyphIndex index, bool preprocessGeometry = true);
bool load(msdfgen::FontHandle *font, unicode_t codepoint, bool preprocessGeometry = true); bool load(msdfgen::FontHandle *font, double geometryScale, unicode_t codepoint, bool preprocessGeometry = true);
/// Applies edge coloring to glyph shape /// Applies edge coloring to glyph shape
void edgeColoring(double angleThreshold, unsigned long long seed); void edgeColoring(double angleThreshold, unsigned long long seed);
/// Computes the dimensions of the glyph's box as well as the transformation for the generator function /// Computes the dimensions of the glyph's box as well as the transformation for the generator function
@ -56,6 +56,7 @@ public:
private: private:
int index; int index;
unicode_t codepoint; unicode_t codepoint;
double geometryScale;
msdfgen::Shape shape; msdfgen::Shape shape;
msdfgen::Shape::Bounds bounds; msdfgen::Shape::Bounds bounds;
double advance; double advance;

View File

@ -3,6 +3,7 @@
#include <artery-font/std-artery-font.h> #include <artery-font/std-artery-font.h>
#include <artery-font/stdio-serialization.h> #include <artery-font/stdio-serialization.h>
#include "GlyphGeometry.h"
#include "image-encode.h" #include "image-encode.h"
namespace msdf_atlas { namespace msdf_atlas {
@ -53,55 +54,69 @@ artery_font::PixelFormat getPixelFormat<float>() {
} }
template <typename REAL, typename T, int N> template <typename REAL, typename T, int N>
bool exportArteryFont(msdfgen::FontHandle *font, const GlyphGeometry *glyphs, int glyphCount, const msdfgen::BitmapConstRef<T, N> &atlas, const char *filename, const ArteryFontExportProperties &properties) { bool exportArteryFont(const FontGeometry *fonts, int fontCount, const msdfgen::BitmapConstRef<T, N> &atlas, const char *filename, const ArteryFontExportProperties &properties) {
artery_font::StdArteryFont<REAL> arfont = { }; artery_font::StdArteryFont<REAL> arfont = { };
arfont.metadataFormat = artery_font::METADATA_NONE; arfont.metadataFormat = artery_font::METADATA_NONE;
if (glyphCount > 0) { for (int i = 0; i < fontCount; ++i) {
msdfgen::FontMetrics fontMetrics; const FontGeometry &font = fonts[i];
if (!msdfgen::getFontMetrics(fontMetrics, font)) GlyphIdentifierType identifierType = font.getPreferredIdentifierType();
return false; const msdfgen::FontMetrics &fontMetrics = font.getMetrics();
double fsScale = 1/fontMetrics.emSize;
artery_font::StdFontVariant<REAL> fontVariant = { }; artery_font::StdFontVariant<REAL> fontVariant = { };
fontVariant.codepointType = convertCodepointType(properties.glyphIdentifierType); fontVariant.codepointType = convertCodepointType(identifierType);
fontVariant.imageType = convertImageType(properties.imageType); fontVariant.imageType = convertImageType(properties.imageType);
fontVariant.metrics.fontSize = REAL(properties.fontSize); fontVariant.metrics.fontSize = REAL(properties.fontSize*fontMetrics.emSize);
if (properties.imageType != ImageType::HARD_MASK) if (properties.imageType != ImageType::HARD_MASK)
fontVariant.metrics.distanceRange = REAL(properties.pxRange); fontVariant.metrics.distanceRange = REAL(properties.pxRange);
fontVariant.metrics.emSize = REAL(fsScale*fontMetrics.emSize); fontVariant.metrics.emSize = REAL(fontMetrics.emSize);
fontVariant.metrics.ascender = REAL(fsScale*fontMetrics.ascenderY); fontVariant.metrics.ascender = REAL(fontMetrics.ascenderY);
fontVariant.metrics.descender = REAL(fsScale*fontMetrics.descenderY); fontVariant.metrics.descender = REAL(fontMetrics.descenderY);
fontVariant.metrics.lineHeight = REAL(fsScale*fontMetrics.lineHeight); fontVariant.metrics.lineHeight = REAL(fontMetrics.lineHeight);
fontVariant.metrics.underlineY = REAL(fsScale*fontMetrics.underlineY); fontVariant.metrics.underlineY = REAL(fontMetrics.underlineY);
fontVariant.metrics.underlineThickness = REAL(fsScale*fontMetrics.underlineThickness); fontVariant.metrics.underlineThickness = REAL(fontMetrics.underlineThickness);
fontVariant.glyphs = artery_font::StdList<artery_font::Glyph<REAL> >(glyphCount); fontVariant.glyphs = artery_font::StdList<artery_font::Glyph<REAL> >(font.getGlyphs().size());
for (int i = 0; i < glyphCount; ++i) { int j = 0;
artery_font::Glyph<REAL> &glyph = fontVariant.glyphs[i]; for (const GlyphGeometry &glyphGeom : font.getGlyphs()) {
glyph.codepoint = glyphs[i].getIdentifier(properties.glyphIdentifierType); artery_font::Glyph<REAL> &glyph = fontVariant.glyphs[j++];
glyph.codepoint = glyphGeom.getIdentifier(identifierType);
glyph.image = 0; glyph.image = 0;
double l, b, r, t; double l, b, r, t;
glyphs[i].getQuadPlaneBounds(l, b, r, t); glyphGeom.getQuadPlaneBounds(l, b, r, t);
glyph.planeBounds.l = REAL(fsScale*l); glyph.planeBounds.l = REAL(l);
glyph.planeBounds.b = REAL(fsScale*b); glyph.planeBounds.b = REAL(b);
glyph.planeBounds.r = REAL(fsScale*r); glyph.planeBounds.r = REAL(r);
glyph.planeBounds.t = REAL(fsScale*t); glyph.planeBounds.t = REAL(t);
glyphs[i].getQuadAtlasBounds(l, b, r, t); glyphGeom.getQuadAtlasBounds(l, b, r, t);
glyph.imageBounds.l = REAL(l); glyph.imageBounds.l = REAL(l);
glyph.imageBounds.b = REAL(b); glyph.imageBounds.b = REAL(b);
glyph.imageBounds.r = REAL(r); glyph.imageBounds.r = REAL(r);
glyph.imageBounds.t = REAL(t); glyph.imageBounds.t = REAL(t);
glyph.advance.h = REAL(fsScale*glyphs[i].getAdvance()); glyph.advance.h = REAL(glyphGeom.getAdvance());
glyph.advance.v = REAL(0); glyph.advance.v = REAL(0);
for (int j = 0; j < glyphCount; ++j) { }
double kerning; switch (identifierType) {
if (msdfgen::getKerning(kerning, font, glyphs[i].getGlyphIndex(), glyphs[j].getGlyphIndex()) && kerning) { case GlyphIdentifierType::GLYPH_INDEX:
for (const std::pair<std::pair<int, int>, double> &elem : font.getKerning()) {
artery_font::KernPair<REAL> kernPair = { }; artery_font::KernPair<REAL> kernPair = { };
kernPair.codepoint1 = glyphs[i].getIdentifier(properties.glyphIdentifierType); kernPair.codepoint1 = elem.first.first;
kernPair.codepoint2 = glyphs[j].getIdentifier(properties.glyphIdentifierType); kernPair.codepoint2 = elem.first.second;
kernPair.advance.h = REAL(fsScale*kerning); kernPair.advance.h = REAL(elem.second);
fontVariant.kernPairs.vector.push_back((artery_font::KernPair<REAL> &&) kernPair); fontVariant.kernPairs.vector.push_back((artery_font::KernPair<REAL> &&) kernPair);
} }
} break;
case GlyphIdentifierType::UNICODE_CODEPOINT:
for (const std::pair<std::pair<int, int>, double> &elem : font.getKerning()) {
const GlyphGeometry *glyph1 = font.getGlyph(msdfgen::GlyphIndex(elem.first.first));
const GlyphGeometry *glyph2 = font.getGlyph(msdfgen::GlyphIndex(elem.first.second));
if (glyph1 && glyph2 && glyph1->getCodepoint() && glyph2->getCodepoint()) {
artery_font::KernPair<REAL> kernPair = { };
kernPair.codepoint1 = glyph1->getCodepoint();
kernPair.codepoint2 = glyph2->getCodepoint();
kernPair.advance.h = REAL(elem.second);
fontVariant.kernPairs.vector.push_back((artery_font::KernPair<REAL> &&) kernPair);
}
}
break;
} }
arfont.variants.vector.push_back((artery_font::StdFontVariant<REAL> &&) fontVariant); arfont.variants.vector.push_back((artery_font::StdFontVariant<REAL> &&) fontVariant);
} }
@ -149,11 +164,11 @@ bool exportArteryFont(msdfgen::FontHandle *font, const GlyphGeometry *glyphs, in
return artery_font::writeFile(arfont, filename); return artery_font::writeFile(arfont, filename);
} }
template bool exportArteryFont<float>(msdfgen::FontHandle *font, const GlyphGeometry *glyphs, int glyphCount, const msdfgen::BitmapConstRef<byte, 1> &atlas, const char *filename, const ArteryFontExportProperties &properties); template bool exportArteryFont<float>(const FontGeometry *fonts, int fontCount, const msdfgen::BitmapConstRef<byte, 1> &atlas, const char *filename, const ArteryFontExportProperties &properties);
template bool exportArteryFont<float>(msdfgen::FontHandle *font, const GlyphGeometry *glyphs, int glyphCount, const msdfgen::BitmapConstRef<byte, 3> &atlas, const char *filename, const ArteryFontExportProperties &properties); template bool exportArteryFont<float>(const FontGeometry *fonts, int fontCount, const msdfgen::BitmapConstRef<byte, 3> &atlas, const char *filename, const ArteryFontExportProperties &properties);
template bool exportArteryFont<float>(msdfgen::FontHandle *font, const GlyphGeometry *glyphs, int glyphCount, const msdfgen::BitmapConstRef<byte, 4> &atlas, const char *filename, const ArteryFontExportProperties &properties); template bool exportArteryFont<float>(const FontGeometry *fonts, int fontCount, const msdfgen::BitmapConstRef<byte, 4> &atlas, const char *filename, const ArteryFontExportProperties &properties);
template bool exportArteryFont<float>(msdfgen::FontHandle *font, const GlyphGeometry *glyphs, int glyphCount, const msdfgen::BitmapConstRef<float, 1> &atlas, const char *filename, const ArteryFontExportProperties &properties); template bool exportArteryFont<float>(const FontGeometry *fonts, int fontCount, const msdfgen::BitmapConstRef<float, 1> &atlas, const char *filename, const ArteryFontExportProperties &properties);
template bool exportArteryFont<float>(msdfgen::FontHandle *font, const GlyphGeometry *glyphs, int glyphCount, const msdfgen::BitmapConstRef<float, 3> &atlas, const char *filename, const ArteryFontExportProperties &properties); template bool exportArteryFont<float>(const FontGeometry *fonts, int fontCount, const msdfgen::BitmapConstRef<float, 3> &atlas, const char *filename, const ArteryFontExportProperties &properties);
template bool exportArteryFont<float>(msdfgen::FontHandle *font, const GlyphGeometry *glyphs, int glyphCount, const msdfgen::BitmapConstRef<float, 4> &atlas, const char *filename, const ArteryFontExportProperties &properties); template bool exportArteryFont<float>(const FontGeometry *fonts, int fontCount, const msdfgen::BitmapConstRef<float, 4> &atlas, const char *filename, const ArteryFontExportProperties &properties);
} }

View File

@ -4,12 +4,11 @@
#include <msdfgen.h> #include <msdfgen.h>
#include <msdfgen-ext.h> #include <msdfgen-ext.h>
#include "types.h" #include "types.h"
#include "GlyphGeometry.h" #include "FontGeometry.h"
namespace msdf_atlas { namespace msdf_atlas {
struct ArteryFontExportProperties { struct ArteryFontExportProperties {
GlyphIdentifierType glyphIdentifierType;
double fontSize; double fontSize;
double pxRange; double pxRange;
ImageType imageType; ImageType imageType;
@ -18,6 +17,6 @@ struct ArteryFontExportProperties {
/// Encodes the atlas bitmap and its layout into an Artery Atlas Font file /// Encodes the atlas bitmap and its layout into an Artery Atlas Font file
template <typename REAL, typename T, int N> template <typename REAL, typename T, int N>
bool exportArteryFont(msdfgen::FontHandle *font, const GlyphGeometry *glyphs, int glyphCount, const msdfgen::BitmapConstRef<T, N> &atlas, const char *filename, const ArteryFontExportProperties &properties); bool exportArteryFont(const FontGeometry *fonts, int fontCount, const msdfgen::BitmapConstRef<T, N> &atlas, const char *filename, const ArteryFontExportProperties &properties);
} }

View File

@ -2,22 +2,26 @@
#include "csv-export.h" #include "csv-export.h"
#include <cstdio> #include <cstdio>
#include "GlyphGeometry.h"
namespace msdf_atlas { namespace msdf_atlas {
bool exportCSV(const GlyphGeometry *glyphs, int glyphCount, GlyphIdentifierType glyphIdentifierType, double emSize, const char *filename) { bool exportCSV(const FontGeometry *fonts, int fontCount, const char *filename) {
FILE *f = fopen(filename, "w"); FILE *f = fopen(filename, "w");
if (!f) if (!f)
return false; return false;
double fsScale = 1/emSize; for (int i = 0; i < fontCount; ++i) {
for (int i = 0; i < glyphCount; ++i) { for (const GlyphGeometry &glyph : fonts[i].getGlyphs()) {
double l, b, r, t; double l, b, r, t;
fprintf(f, "%d,%.17g,", glyphs[i].getIdentifier(glyphIdentifierType), fsScale*glyphs[i].getAdvance()); if (fontCount > 1)
glyphs[i].getQuadPlaneBounds(l, b, r, t); fprintf(f, "%d,", i);
fprintf(f, "%.17g,%.17g,%.17g,%.17g,", fsScale*l, fsScale*b, fsScale*r, fsScale*t); fprintf(f, "%d,%.17g,", glyph.getIdentifier(fonts[i].getPreferredIdentifierType()), glyph.getAdvance());
glyphs[i].getQuadAtlasBounds(l, b, r, t); glyph.getQuadPlaneBounds(l, b, r, t);
fprintf(f, "%.17g,%.17g,%.17g,%.17g\n", l, b, r, t); fprintf(f, "%.17g,%.17g,%.17g,%.17g,", l, b, r, t);
glyph.getQuadAtlasBounds(l, b, r, t);
fprintf(f, "%.17g,%.17g,%.17g,%.17g\n", l, b, r, t);
}
} }
fclose(f); fclose(f);

View File

@ -1,15 +1,14 @@
#pragma once #pragma once
#include "types.h" #include "FontGeometry.h"
#include "GlyphGeometry.h"
namespace msdf_atlas { namespace msdf_atlas {
/** /**
* Writes the positioning data and atlas layout of the glyphs into a CSV file * Writes the positioning data and atlas layout of the glyphs into a CSV file
* The columns are: glyph identifier (index or Unicode), horizontal advance, plane bounds (l, b, r, t), atlas bounds (l, b, r, t) * The columns are: font variant index (if fontCount > 1), 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, GlyphIdentifierType glyphIdentifierType, double emSize, const char *filename); bool exportCSV(const FontGeometry *fonts, int fontCount, const char *filename);
} }

View File

@ -1,6 +1,8 @@
#include "json-export.h" #include "json-export.h"
#include "GlyphGeometry.h"
namespace msdf_atlas { namespace msdf_atlas {
static const char * imageTypeString(ImageType type) { static const char * imageTypeString(ImageType type) {
@ -21,12 +23,7 @@ static const char * imageTypeString(ImageType type) {
return nullptr; return nullptr;
} }
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) { bool exportJSON(const FontGeometry *fonts, int fontCount, double fontSize, double pxRange, int atlasWidth, int atlasHeight, ImageType imageType, const char *filename, bool kerning) {
msdfgen::FontMetrics fontMetrics;
if (!msdfgen::getFontMetrics(fontMetrics, font))
return false;
double fsScale = 1/fontMetrics.emSize;
FILE *f = fopen(filename, "w"); FILE *f = fopen(filename, "w");
if (!f) if (!f)
return false; return false;
@ -43,56 +40,86 @@ bool exportJSON(msdfgen::FontHandle *font, const GlyphGeometry *glyphs, int glyp
fputs("\"yOrigin\":\"bottom\"", f); fputs("\"yOrigin\":\"bottom\"", f);
} fputs("},", f); } fputs("},", f);
// Font metrics if (fontCount > 1)
fputs("\"metrics\":{", f); { fputs("\"variants\":[", f);
fprintf(f, "\"lineHeight\":%.17g,", fsScale*fontMetrics.lineHeight); for (int i = 0; i < fontCount; ++i) {
fprintf(f, "\"ascender\":%.17g,", fsScale*fontMetrics.ascenderY); const FontGeometry &font = fonts[i];
fprintf(f, "\"descender\":%.17g,", fsScale*fontMetrics.descenderY); if (fontCount > 1)
fprintf(f, "\"underlineY\":%.17g,", fsScale*fontMetrics.underlineY); fputs(i == 0 ? "{" : ",{", f);
fprintf(f, "\"underlineThickness\":%.17g", fsScale*fontMetrics.underlineThickness);
} fputs("},", f);
// Glyph mapping // Font metrics
fputs("\"glyphs\":[", f); fputs("\"metrics\":{", f); {
for (int i = 0; i < glyphCount; ++i) { const msdfgen::FontMetrics &metrics = font.getMetrics();
fputs(i == 0 ? "{" : ",{", f); fprintf(f, "\"emSize\":%.17g,", metrics.emSize);
switch (glyphIdentifierType) { fprintf(f, "\"lineHeight\":%.17g,", metrics.lineHeight);
case GlyphIdentifierType::GLYPH_INDEX: fprintf(f, "\"ascender\":%.17g,", metrics.ascenderY);
fprintf(f, "\"index\":%d,", glyphs[i].getIndex()); fprintf(f, "\"descender\":%.17g,", metrics.descenderY);
break; fprintf(f, "\"underlineY\":%.17g,", metrics.underlineY);
case GlyphIdentifierType::UNICODE_CODEPOINT: fprintf(f, "\"underlineThickness\":%.17g", metrics.underlineThickness);
fprintf(f, "\"unicode\":%u,", glyphs[i].getCodepoint()); } fputs("},", f);
break;
}
fprintf(f, "\"advance\":%.17g", fsScale*glyphs[i].getAdvance());
double l, b, r, t;
glyphs[i].getQuadPlaneBounds(l, b, r, t);
if (l || b || r || t)
fprintf(f, ",\"planeBounds\":{\"left\":%.17g,\"bottom\":%.17g,\"right\":%.17g,\"top\":%.17g}", fsScale*l, fsScale*b, fsScale*r, fsScale*t);
glyphs[i].getQuadAtlasBounds(l, b, r, t);
if (l || b || r || t)
fprintf(f, ",\"atlasBounds\":{\"left\":%.17g,\"bottom\":%.17g,\"right\":%.17g,\"top\":%.17g}", l, b, r, t);
fputs("}", f);
}
fputs("],", f);
// Kerning pairs // Glyph mapping
fputs("\"kerning\":[", f); fputs("\"glyphs\":[", f);
bool firstPair = true; bool firstGlyph = true;
for (int i = 0; i < glyphCount; ++i) { for (const GlyphGeometry &glyph : font.getGlyphs()) {
for (int j = 0; j < glyphCount; ++j) { fputs(firstGlyph ? "{" : ",{", f);
double kerning; switch (font.getPreferredIdentifierType()) {
if (msdfgen::getKerning(kerning, font, glyphs[i].getCodepoint(), glyphs[j].getCodepoint()) && kerning) { case GlyphIdentifierType::GLYPH_INDEX:
fputs(firstPair ? "{" : ",{", f); fprintf(f, "\"index\":%d,", glyph.getIndex());
fprintf(f, "\"unicode1\":%u,", glyphs[i].getCodepoint()); break;
fprintf(f, "\"unicode2\":%u,", glyphs[j].getCodepoint()); case GlyphIdentifierType::UNICODE_CODEPOINT:
fprintf(f, "\"advance\":%.17g", fsScale*kerning); fprintf(f, "\"unicode\":%u,", glyph.getCodepoint());
fputs("}", f); break;
firstPair = false;
} }
fprintf(f, "\"advance\":%.17g", glyph.getAdvance());
double l, b, r, t;
glyph.getQuadPlaneBounds(l, b, r, t);
if (l || b || r || t)
fprintf(f, ",\"planeBounds\":{\"left\":%.17g,\"bottom\":%.17g,\"right\":%.17g,\"top\":%.17g}", l, b, r, t);
glyph.getQuadAtlasBounds(l, b, r, t);
if (l || b || r || t)
fprintf(f, ",\"atlasBounds\":{\"left\":%.17g,\"bottom\":%.17g,\"right\":%.17g,\"top\":%.17g}", l, b, r, t);
fputs("}", f);
firstGlyph = false;
} fputs("]", f);
// Kerning pairs
if (kerning) {
fputs(",\"kerning\":[", f);
bool firstPair = true;
switch (font.getPreferredIdentifierType()) {
case GlyphIdentifierType::GLYPH_INDEX:
for (const std::pair<std::pair<int, int>, double> &kernPair : font.getKerning()) {
fputs(firstPair ? "{" : ",{", f);
fprintf(f, "\"index1\":%d,", kernPair.first.first);
fprintf(f, "\"index2\":%d,", kernPair.first.second);
fprintf(f, "\"advance\":%.17g", kernPair.second);
fputs("}", f);
firstPair = false;
}
break;
case GlyphIdentifierType::UNICODE_CODEPOINT:
for (const std::pair<std::pair<int, int>, double> &kernPair : font.getKerning()) {
const GlyphGeometry *glyph1 = font.getGlyph(msdfgen::GlyphIndex(kernPair.first.first));
const GlyphGeometry *glyph2 = font.getGlyph(msdfgen::GlyphIndex(kernPair.first.second));
if (glyph1 && glyph2 && glyph1->getCodepoint() && glyph2->getCodepoint()) {
fputs(firstPair ? "{" : ",{", f);
fprintf(f, "\"unicode1\":%u,", glyph1->getCodepoint());
fprintf(f, "\"unicode2\":%u,", glyph2->getCodepoint());
fprintf(f, "\"advance\":%.17g", kernPair.second);
fputs("}", f);
firstPair = false;
}
}
break;
} fputs("]", f);
} }
if (fontCount > 1)
fputs("}", f);
} }
fputs("]", f); if (fontCount > 1)
fputs("]", f);
fputs("}\n", f); fputs("}\n", f);
fclose(f); fclose(f);

View File

@ -4,11 +4,11 @@
#include <msdfgen.h> #include <msdfgen.h>
#include <msdfgen-ext.h> #include <msdfgen-ext.h>
#include "types.h" #include "types.h"
#include "GlyphGeometry.h" #include "FontGeometry.h"
namespace msdf_atlas { namespace msdf_atlas {
/// Writes the font and glyph metrics and atlas layout data into a comprehensive JSON file /// 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, GlyphIdentifierType glyphIdentifierType, double fontSize, double pxRange, int atlasWidth, int atlasHeight, ImageType imageType, const char *filename); bool exportJSON(const FontGeometry *fonts, int fontCount, double fontSize, double pxRange, int atlasWidth, int atlasHeight, ImageType imageType, const char *filename, bool kerning);
} }

View File

@ -11,7 +11,9 @@
#define _USE_MATH_DEFINES #define _USE_MATH_DEFINES
#include <cstdio> #include <cstdio>
#include <cmath> #include <cmath>
#include <cstring>
#include <cassert> #include <cassert>
#include <vector>
#include <algorithm> #include <algorithm>
#include <thread> #include <thread>
@ -21,7 +23,6 @@ using namespace msdf_atlas;
#define DEFAULT_ANGLE_THRESHOLD 3.0 #define DEFAULT_ANGLE_THRESHOLD 3.0
#define DEFAULT_MITER_LIMIT 1.0 #define DEFAULT_MITER_LIMIT 1.0
#define DEFAULT_EM_SIZE 32.0
#define DEFAULT_PIXEL_RANGE 2.0 #define DEFAULT_PIXEL_RANGE 2.0
#define SDF_ERROR_ESTIMATE_PRECISION 19 #define SDF_ERROR_ESTIMATE_PRECISION 19
#define GLYPH_FILL_RULE msdfgen::FILL_NONZERO #define GLYPH_FILL_RULE msdfgen::FILL_NONZERO
@ -46,6 +47,10 @@ INPUT SPECIFICATION
Specifies the input character set. Refer to the documentation for format of charset specification. Defaults to ASCII. Specifies the input character set. Refer to the documentation for format of charset specification. Defaults to ASCII.
-glyphset <filename> -glyphset <filename>
Specifies the set of input glyphs as glyph indices within the font file. Specifies the set of input glyphs as glyph indices within the font file.
-fontscale <scale>
Specifies the scale to be applied to the glyph geometry of the font.
-and
Separates multiple inputs to be combined into a single atlas.
ATLAS CONFIGURATION ATLAS CONFIGURATION
-type <hardmask / softmask / sdf / psdf / msdf / mtsdf> -type <hardmask / softmask / sdf / psdf / msdf / mtsdf>
@ -79,6 +84,8 @@ GLYPH CONFIGURATION
Specifies the SDF distance range in EM's. Specifies the SDF distance range in EM's.
-pxrange <pixel range> -pxrange <pixel range>
Specifies the SDF distance range in output pixels. The default value is 2. Specifies the SDF distance range in output pixels. The default value is 2.
-nokerning
Disables inclusion of kerning pair table in output files.
DISTANCE FIELD GENERATOR SETTINGS DISTANCE FIELD GENERATOR SETTINGS
-angle <angle> -angle <angle>
@ -147,32 +154,14 @@ static bool cmpExtension(const char *path, const char *ext) {
return true; return true;
} }
static void loadGlyphsByIndex(std::vector<GlyphGeometry> &glyphs, msdfgen::FontHandle *font, const Charset &charset, bool preprocessGeometry) { struct FontInput {
glyphs.clear(); const char *fontFilename;
glyphs.reserve(charset.size()); GlyphIdentifierType glyphIdentifierType;
for (unicode_t cp : charset) { const char *charsetFilename;
GlyphGeometry glyph; double fontScale;
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<GlyphGeometry> &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, cp, preprocessGeometry))
glyphs.push_back((GlyphGeometry &&) glyph);
else
printf("Glyph for codepoint 0x%X missing\n", cp);
}
}
struct Configuration { struct Configuration {
GlyphIdentifierType glyphIdentifierType;
ImageType imageType; ImageType imageType;
ImageFormat imageFormat; ImageFormat imageFormat;
int width, height; int width, height;
@ -183,6 +172,7 @@ struct Configuration {
unsigned long long coloringSeed; unsigned long long coloringSeed;
GeneratorAttributes generatorAttributes; GeneratorAttributes generatorAttributes;
bool preprocessGeometry; bool preprocessGeometry;
bool kerning;
int threadCount; int threadCount;
const char *arteryFontFilename; const char *arteryFontFilename;
const char *imageFilename; const char *imageFilename;
@ -193,7 +183,7 @@ struct Configuration {
}; };
template <typename T, typename S, int N, GeneratorFunction<S, N> GEN_FN> template <typename T, typename S, int N, GeneratorFunction<S, N> GEN_FN>
static bool makeAtlas(const std::vector<GlyphGeometry> &glyphs, msdfgen::FontHandle *font, const Configuration &config) { static bool makeAtlas(const std::vector<GlyphGeometry> &glyphs, const std::vector<FontGeometry> &fonts, const Configuration &config) {
ImmediateAtlasGenerator<S, N, GEN_FN, BitmapAtlasStorage<T, N> > generator(config.width, config.height); ImmediateAtlasGenerator<S, N, GEN_FN, BitmapAtlasStorage<T, N> > generator(config.width, config.height);
generator.setAttributes(config.generatorAttributes); generator.setAttributes(config.generatorAttributes);
generator.setThreadCount(config.threadCount); generator.setThreadCount(config.threadCount);
@ -213,12 +203,11 @@ static bool makeAtlas(const std::vector<GlyphGeometry> &glyphs, msdfgen::FontHan
if (config.arteryFontFilename) { if (config.arteryFontFilename) {
ArteryFontExportProperties arfontProps; ArteryFontExportProperties arfontProps;
arfontProps.glyphIdentifierType = config.glyphIdentifierType;
arfontProps.fontSize = config.emSize; arfontProps.fontSize = config.emSize;
arfontProps.pxRange = config.pxRange; arfontProps.pxRange = config.pxRange;
arfontProps.imageType = config.imageType; arfontProps.imageType = config.imageType;
arfontProps.imageFormat = config.imageFormat; arfontProps.imageFormat = config.imageFormat;
if (exportArteryFont<float>(font, glyphs.data(), glyphs.size(), bitmap, config.arteryFontFilename, arfontProps)) if (exportArteryFont<float>(fonts.data(), fonts.size(), bitmap, config.arteryFontFilename, arfontProps))
puts("Artery Font file generated."); puts("Artery Font file generated.");
else { else {
success = false; success = false;
@ -233,10 +222,12 @@ int main(int argc, const char * const *argv) {
#define ABORT(msg) { puts(msg); return 1; } #define ABORT(msg) { puts(msg); return 1; }
int result = 0; int result = 0;
std::vector<FontInput> fontInputs;
FontInput fontInput = { };
Configuration config = { }; Configuration config = { };
const char *fontFilename = nullptr; fontInput.glyphIdentifierType = GlyphIdentifierType::UNICODE_CODEPOINT;
const char *charsetFilename = nullptr; fontInput.fontScale = -1;
config.glyphIdentifierType = GlyphIdentifierType::UNICODE_CODEPOINT; config.kerning = true;
config.imageType = ImageType::MSDF; config.imageType = ImageType::MSDF;
config.imageFormat = ImageFormat::UNSPECIFIED; config.imageFormat = ImageFormat::UNSPECIFIED;
const char *imageFormatName = nullptr; const char *imageFormatName = nullptr;
@ -315,19 +306,36 @@ int main(int argc, const char * const *argv) {
continue; continue;
} }
ARG_CASE("-font", 1) { ARG_CASE("-font", 1) {
fontFilename = argv[++argPos]; fontInput.fontFilename = argv[++argPos];
++argPos; ++argPos;
continue; continue;
} }
ARG_CASE("-charset", 1) { ARG_CASE("-charset", 1) {
charsetFilename = argv[++argPos]; fontInput.charsetFilename = argv[++argPos];
config.glyphIdentifierType = GlyphIdentifierType::UNICODE_CODEPOINT; fontInput.glyphIdentifierType = GlyphIdentifierType::UNICODE_CODEPOINT;
++argPos; ++argPos;
continue; continue;
} }
ARG_CASE("-glyphset", 1) { ARG_CASE("-glyphset", 1) {
charsetFilename = argv[++argPos]; fontInput.charsetFilename = argv[++argPos];
config.glyphIdentifierType = GlyphIdentifierType::GLYPH_INDEX; fontInput.glyphIdentifierType = GlyphIdentifierType::GLYPH_INDEX;
++argPos;
continue;
}
ARG_CASE("-fontscale", 1) {
double fs;
if (!(parseDouble(fs, argv[++argPos]) && fs > 0))
ABORT("Invalid font scale argument. Use -fontscale <font scale> with a positive real number.");
fontInput.fontScale = fs;
++argPos;
continue;
}
ARG_CASE("-and", 0) {
if (!fontInput.fontFilename && !fontInput.charsetFilename && fontInput.fontScale < 0)
ABORT("No font, character set, or font scale specified before -and separator.");
if (!fontInputs.empty() && !memcmp(&fontInputs.back(), &fontInput, sizeof(FontInput)))
ABORT("No changes between subsequent inputs. A different font, character set, or font scale must be set inbetween -and separators.");
fontInputs.push_back(fontInput);
++argPos; ++argPos;
continue; continue;
} }
@ -453,34 +461,44 @@ int main(int argc, const char * const *argv) {
++argPos; ++argPos;
continue; continue;
} }
ARG_CASE("-nokerning", 0) {
config.kerning = false;
++argPos;
continue;
}
ARG_CASE("-kerning", 0) {
config.kerning = true;
++argPos;
continue;
}
ARG_CASE("-nopreprocess", 0) { ARG_CASE("-nopreprocess", 0) {
config.preprocessGeometry = false; config.preprocessGeometry = false;
argPos += 1; ++argPos;
continue; continue;
} }
ARG_CASE("-preprocess", 0) { ARG_CASE("-preprocess", 0) {
config.preprocessGeometry = true; config.preprocessGeometry = true;
argPos += 1; ++argPos;
continue; continue;
} }
ARG_CASE("-nooverlap", 0) { ARG_CASE("-nooverlap", 0) {
config.generatorAttributes.overlapSupport = false; config.generatorAttributes.overlapSupport = false;
argPos += 1; ++argPos;
continue; continue;
} }
ARG_CASE("-overlap", 0) { ARG_CASE("-overlap", 0) {
config.generatorAttributes.overlapSupport = true; config.generatorAttributes.overlapSupport = true;
argPos += 1; ++argPos;
continue; continue;
} }
ARG_CASE("-noscanline", 0) { ARG_CASE("-noscanline", 0) {
config.generatorAttributes.scanlinePass = false; config.generatorAttributes.scanlinePass = false;
argPos += 1; ++argPos;
continue; continue;
} }
ARG_CASE("-scanline", 0) { ARG_CASE("-scanline", 0) {
config.generatorAttributes.scanlinePass = true; config.generatorAttributes.scanlinePass = true;
argPos += 1; ++argPos;
continue; continue;
} }
ARG_CASE("-seed", 1) { ARG_CASE("-seed", 1) {
@ -520,7 +538,7 @@ int main(int argc, const char * const *argv) {
); );
return 0; return 0;
} }
if (!fontFilename) if (!fontInput.fontFilename)
ABORT("No font specified."); ABORT("No font specified.");
if (!(config.arteryFontFilename || config.imageFilename || config.jsonFilename || config.csvFilename || config.shadronPreviewFilename)) { if (!(config.arteryFontFilename || config.imageFilename || config.jsonFilename || config.csvFilename || config.shadronPreviewFilename)) {
puts("No output specified."); puts("No output specified.");
@ -528,6 +546,22 @@ int main(int argc, const char * const *argv) {
} }
bool layoutOnly = !(config.arteryFontFilename || config.imageFilename); bool layoutOnly = !(config.arteryFontFilename || config.imageFilename);
// Finalize font inputs
const FontInput *nextFontInput = &fontInput;
for (std::vector<FontInput>::reverse_iterator it = fontInputs.rbegin(); it != fontInputs.rend(); ++it) {
if (!it->fontFilename && nextFontInput->fontFilename)
it->fontFilename = nextFontInput->fontFilename;
if (!it->charsetFilename && nextFontInput->charsetFilename) {
it->charsetFilename = nextFontInput->charsetFilename;
it->glyphIdentifierType = nextFontInput->glyphIdentifierType;
}
if (it->fontScale < 0 && nextFontInput->fontScale >= 0)
it->fontScale = nextFontInput->fontScale;
nextFontInput = &*it;
}
if (fontInputs.empty() || memcmp(&fontInputs.back(), &fontInput, sizeof(FontInput)))
fontInputs.push_back(fontInput);
// Fix up configuration based on related values // Fix up configuration based on related values
if (!(config.imageType == ImageType::PSDF || config.imageType == ImageType::MSDF || config.imageType == ImageType::MTSDF)) if (!(config.imageType == ImageType::PSDF || config.imageType == ImageType::MSDF || config.imageType == ImageType::MTSDF))
config.miterLimit = 0; config.miterLimit = 0;
@ -535,7 +569,7 @@ int main(int argc, const char * const *argv) {
minEmSize = config.emSize; minEmSize = config.emSize;
if (!(fixedWidth > 0 && fixedHeight > 0) && !(minEmSize > 0)) { if (!(fixedWidth > 0 && fixedHeight > 0) && !(minEmSize > 0)) {
puts("Neither atlas size nor glyph size selected, using default..."); puts("Neither atlas size nor glyph size selected, using default...");
minEmSize = DEFAULT_EM_SIZE; minEmSize = MSDF_ATLAS_DEFAULT_EM_SIZE;
} }
if (!(config.imageType == ImageType::SDF || config.imageType == ImageType::PSDF || config.imageType == ImageType::MSDF || config.imageType == ImageType::MTSDF)) { if (!(config.imageType == ImageType::SDF || config.imageType == ImageType::PSDF || config.imageType == ImageType::MSDF || config.imageType == ImageType::MTSDF)) {
rangeMode = RANGE_PIXEL; rangeMode = RANGE_PIXEL;
@ -544,6 +578,8 @@ int main(int argc, const char * const *argv) {
rangeMode = RANGE_PIXEL; rangeMode = RANGE_PIXEL;
rangeValue = DEFAULT_PIXEL_RANGE; rangeValue = DEFAULT_PIXEL_RANGE;
} }
if (config.kerning && !(config.arteryFontFilename || config.jsonFilename || config.shadronPreviewFilename))
config.kerning = false;
if (config.threadCount <= 0) if (config.threadCount <= 0)
config.threadCount = std::max((int) std::thread::hardware_concurrency(), 1); config.threadCount = std::max((int) std::thread::hardware_concurrency(), 1);
@ -598,61 +634,108 @@ int main(int argc, const char * const *argv) {
config.imageFormat == ImageFormat::BINARY_FLOAT_BE config.imageFormat == ImageFormat::BINARY_FLOAT_BE
); );
// Load font // Load fonts
class FontHolder {
msdfgen::FreetypeHandle *ft;
msdfgen::FontHandle *font;
public:
explicit FontHolder(const char *fontFilename) : ft(nullptr), font(nullptr) {
if ((ft = msdfgen::initializeFreetype()))
font = msdfgen::loadFont(ft, fontFilename);
}
~FontHolder() {
if (ft) {
if (font)
msdfgen::destroyFont(font);
msdfgen::deinitializeFreetype(ft);
}
}
operator msdfgen::FontHandle *() const {
return font;
}
} font(fontFilename);
if (!font)
ABORT("Failed to load specified font file.");
msdfgen::FontMetrics fontMetrics = { };
msdfgen::getFontMetrics(fontMetrics, font);
if (fontMetrics.emSize <= 0)
fontMetrics.emSize = DEFAULT_EM_SIZE;
// Load character set
Charset charset;
if (charsetFilename) {
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;
// Load glyphs
std::vector<GlyphGeometry> glyphs; std::vector<GlyphGeometry> glyphs;
switch (config.glyphIdentifierType) { std::vector<FontGeometry> fonts;
case GlyphIdentifierType::GLYPH_INDEX: bool anyCodepointsAvailable = false;
loadGlyphsByIndex(glyphs, font, charset, config.preprocessGeometry); {
break; class FontHolder {
case GlyphIdentifierType::UNICODE_CODEPOINT: msdfgen::FreetypeHandle *ft;
loadGlyphsByUnicode(glyphs, font, charset, config.preprocessGeometry); msdfgen::FontHandle *font;
break; const char *fontFilename;
public:
FontHolder() : ft(msdfgen::initializeFreetype()), font(nullptr), fontFilename(nullptr) { }
~FontHolder() {
if (ft) {
if (font)
msdfgen::destroyFont(font);
msdfgen::deinitializeFreetype(ft);
}
}
bool load(const char *fontFilename) {
if (ft && fontFilename) {
if (this->fontFilename && !strcmp(this->fontFilename, fontFilename))
return true;
if (font)
msdfgen::destroyFont(font);
if ((font = msdfgen::loadFont(ft, fontFilename))) {
this->fontFilename = fontFilename;
return true;
}
this->fontFilename = nullptr;
}
return false;
}
operator msdfgen::FontHandle *() const {
return font;
}
} font;
for (FontInput &fontInput : fontInputs) {
if (!font.load(fontInput.fontFilename))
ABORT("Failed to load specified font file.");
if (fontInput.fontScale <= 0)
fontInput.fontScale = 1;
// Load character set
Charset charset;
if (fontInput.charsetFilename) {
if (!charset.load(fontInput.charsetFilename, fontInput.glyphIdentifierType != GlyphIdentifierType::UNICODE_CODEPOINT))
ABORT(fontInput.glyphIdentifierType == GlyphIdentifierType::GLYPH_INDEX ? "Failed to load glyph set specification." : "Failed to load character set specification.");
} else {
charset = Charset::ASCII;
fontInput.glyphIdentifierType = GlyphIdentifierType::UNICODE_CODEPOINT;
}
// Load glyphs
FontGeometry fontGeometry(&glyphs);
int glyphsLoaded = -1;
switch (fontInput.glyphIdentifierType) {
case GlyphIdentifierType::GLYPH_INDEX:
glyphsLoaded = fontGeometry.loadGlyphset(font, fontInput.fontScale, charset, config.preprocessGeometry, config.kerning);
break;
case GlyphIdentifierType::UNICODE_CODEPOINT:
glyphsLoaded = fontGeometry.loadCharset(font, fontInput.fontScale, charset, config.preprocessGeometry, config.kerning);
anyCodepointsAvailable |= glyphsLoaded > 0;
break;
}
if (glyphsLoaded < 0)
ABORT("Failed to load glyphs from font.");
printf("Loaded geometry of %d out of %d glyphs", glyphsLoaded, (int) charset.size());
if (fontInputs.size() > 1)
printf(" from font \"%s\"", fontInput.fontFilename);
printf(".\n");
// List missing glyphs
if (glyphsLoaded < (int) charset.size()) {
printf("Missing %d %s", (int) charset.size()-glyphsLoaded, fontInput.glyphIdentifierType == GlyphIdentifierType::UNICODE_CODEPOINT ? "codepoints" : "glyphs");
bool first = true;
switch (fontInput.glyphIdentifierType) {
case GlyphIdentifierType::GLYPH_INDEX:
for (unicode_t cp : charset)
if (!fontGeometry.getGlyph(msdfgen::GlyphIndex(cp)))
printf("%c 0x%02X", first ? ((first = false), ':') : ',', cp);
break;
case GlyphIdentifierType::UNICODE_CODEPOINT:
for (unicode_t cp : charset)
if (!fontGeometry.getGlyph(cp))
printf("%c 0x%02X", first ? ((first = false), ':') : ',', cp);
break;
}
printf("\n");
}
fonts.push_back((FontGeometry &&) fontGeometry);
}
} }
if (glyphs.empty()) if (glyphs.empty())
ABORT("No glyphs loaded."); 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 // Determine final atlas dimensions, scale and range, pack glyphs
{ {
double unitRange = 0, pxRange = 0; double unitRange = 0, pxRange = 0;
switch (rangeMode) { switch (rangeMode) {
case RANGE_EM: case RANGE_EM:
unitRange = rangeValue*fontMetrics.emSize; unitRange = rangeValue;
break; break;
case RANGE_PIXEL: case RANGE_PIXEL:
pxRange = rangeValue; pxRange = rangeValue;
@ -668,9 +751,9 @@ int main(int argc, const char * const *argv) {
atlasPacker.setPadding(config.imageType == ImageType::MSDF || config.imageType == ImageType::MTSDF ? 0 : -1); atlasPacker.setPadding(config.imageType == ImageType::MSDF || config.imageType == ImageType::MTSDF ? 0 : -1);
// TODO: In this case (if padding is -1), the border pixels of each glyph are black, but still computed. For floating-point output, this may play a role. // TODO: In this case (if padding is -1), the border pixels of each glyph are black, but still computed. For floating-point output, this may play a role.
if (fixedScale) if (fixedScale)
atlasPacker.setScale(config.emSize/fontMetrics.emSize); atlasPacker.setScale(config.emSize);
else else
atlasPacker.setMinimumScale(minEmSize/fontMetrics.emSize); atlasPacker.setMinimumScale(minEmSize);
atlasPacker.setPixelRange(pxRange); atlasPacker.setPixelRange(pxRange);
atlasPacker.setUnitRange(unitRange); atlasPacker.setUnitRange(unitRange);
atlasPacker.setMiterLimit(config.miterLimit); atlasPacker.setMiterLimit(config.miterLimit);
@ -685,7 +768,7 @@ int main(int argc, const char * const *argv) {
atlasPacker.getDimensions(config.width, config.height); atlasPacker.getDimensions(config.width, config.height);
if (!(config.width > 0 && config.height > 0)) if (!(config.width > 0 && config.height > 0))
ABORT("Unable to determine atlas size."); ABORT("Unable to determine atlas size.");
config.emSize = atlasPacker.getScale()*fontMetrics.emSize; config.emSize = atlasPacker.getScale();
config.pxRange = atlasPacker.getPixelRange(); config.pxRange = atlasPacker.getPixelRange();
if (!fixedScale) if (!fixedScale)
printf("Glyph size: %.9g pixels/EM\n", config.emSize); printf("Glyph size: %.9g pixels/EM\n", config.emSize);
@ -709,34 +792,34 @@ int main(int argc, const char * const *argv) {
switch (config.imageType) { switch (config.imageType) {
case ImageType::HARD_MASK: case ImageType::HARD_MASK:
if (floatingPointFormat) if (floatingPointFormat)
success = makeAtlas<float, float, 1, scanlineGenerator>(glyphs, font, config); success = makeAtlas<float, float, 1, scanlineGenerator>(glyphs, fonts, config);
else else
success = makeAtlas<byte, float, 1, scanlineGenerator>(glyphs, font, config); success = makeAtlas<byte, float, 1, scanlineGenerator>(glyphs, fonts, config);
break; break;
case ImageType::SOFT_MASK: case ImageType::SOFT_MASK:
case ImageType::SDF: case ImageType::SDF:
if (floatingPointFormat) if (floatingPointFormat)
success = makeAtlas<float, float, 1, sdfGenerator>(glyphs, font, config); success = makeAtlas<float, float, 1, sdfGenerator>(glyphs, fonts, config);
else else
success = makeAtlas<byte, float, 1, sdfGenerator>(glyphs, font, config); success = makeAtlas<byte, float, 1, sdfGenerator>(glyphs, fonts, config);
break; break;
case ImageType::PSDF: case ImageType::PSDF:
if (floatingPointFormat) if (floatingPointFormat)
success = makeAtlas<float, float, 1, psdfGenerator>(glyphs, font, config); success = makeAtlas<float, float, 1, psdfGenerator>(glyphs, fonts, config);
else else
success = makeAtlas<byte, float, 1, psdfGenerator>(glyphs, font, config); success = makeAtlas<byte, float, 1, psdfGenerator>(glyphs, fonts, config);
break; break;
case ImageType::MSDF: case ImageType::MSDF:
if (floatingPointFormat) if (floatingPointFormat)
success = makeAtlas<float, float, 3, msdfGenerator>(glyphs, font, config); success = makeAtlas<float, float, 3, msdfGenerator>(glyphs, fonts, config);
else else
success = makeAtlas<byte, float, 3, msdfGenerator>(glyphs, font, config); success = makeAtlas<byte, float, 3, msdfGenerator>(glyphs, fonts, config);
break; break;
case ImageType::MTSDF: case ImageType::MTSDF:
if (floatingPointFormat) if (floatingPointFormat)
success = makeAtlas<float, float, 4, mtsdfGenerator>(glyphs, font, config); success = makeAtlas<float, float, 4, mtsdfGenerator>(glyphs, fonts, config);
else else
success = makeAtlas<byte, float, 4, mtsdfGenerator>(glyphs, font, config); success = makeAtlas<byte, float, 4, mtsdfGenerator>(glyphs, fonts, config);
break; break;
} }
if (!success) if (!success)
@ -744,7 +827,7 @@ int main(int argc, const char * const *argv) {
} }
if (config.csvFilename) { if (config.csvFilename) {
if (exportCSV(glyphs.data(), glyphs.size(), config.glyphIdentifierType, fontMetrics.emSize, config.csvFilename)) if (exportCSV(fonts.data(), fonts.size(), config.csvFilename))
puts("Glyph layout written into CSV file."); puts("Glyph layout written into CSV file.");
else { else {
result = 1; result = 1;
@ -752,7 +835,7 @@ int main(int argc, const char * const *argv) {
} }
} }
if (config.jsonFilename) { if (config.jsonFilename) {
if (exportJSON(font, glyphs.data(), glyphs.size(), config.glyphIdentifierType, config.emSize, config.pxRange, config.width, config.height, config.imageType, config.jsonFilename)) if (exportJSON(fonts.data(), fonts.size(), config.emSize, config.pxRange, config.width, config.height, config.imageType, config.jsonFilename, config.kerning))
puts("Glyph layout and metadata written into JSON file."); puts("Glyph layout and metadata written into JSON file.");
else { else {
result = 1; result = 1;
@ -761,11 +844,11 @@ int main(int argc, const char * const *argv) {
} }
if (config.shadronPreviewFilename && config.shadronPreviewText) { if (config.shadronPreviewFilename && config.shadronPreviewText) {
if (config.glyphIdentifierType == GlyphIdentifierType::UNICODE_CODEPOINT) { if (anyCodepointsAvailable) {
std::vector<unicode_t> previewText; std::vector<unicode_t> previewText;
utf8Decode(previewText, config.shadronPreviewText); utf8Decode(previewText, config.shadronPreviewText);
previewText.push_back(0); previewText.push_back(0);
if (generateShadronPreview(font, glyphs.data(), glyphs.size(), config.imageType, config.width, config.height, config.pxRange, previewText.data(), config.imageFilename, floatingPointFormat, config.shadronPreviewFilename)) if (generateShadronPreview(fonts.data(), fonts.size(), config.imageType, config.width, config.height, config.pxRange, previewText.data(), config.imageFilename, floatingPointFormat, config.shadronPreviewFilename))
puts("Shadron preview script generated."); puts("Shadron preview script generated.");
else { else {
result = 1; result = 1;

View File

@ -19,6 +19,7 @@
#include "Charset.h" #include "Charset.h"
#include "GlyphBox.h" #include "GlyphBox.h"
#include "GlyphGeometry.h" #include "GlyphGeometry.h"
#include "FontGeometry.h"
#include "RectanglePacker.h" #include "RectanglePacker.h"
#include "rectangle-packing.h" #include "rectangle-packing.h"
#include "Workload.h" #include "Workload.h"

View File

@ -77,9 +77,12 @@ static std::string relativizePath(const char *base, const char *target) {
return output; return output;
} }
bool generateShadronPreview(msdfgen::FontHandle *font, const GlyphGeometry *glyphs, int glyphCount, ImageType atlasType, int atlasWidth, int atlasHeight, double pxRange, const unicode_t *text, const char *imageFilename, bool fullRange, const char *outputFilename) { bool generateShadronPreview(const FontGeometry *fonts, int fontCount, ImageType atlasType, int atlasWidth, int atlasHeight, double pxRange, const unicode_t *text, const char *imageFilename, bool fullRange, const char *outputFilename) {
if (fontCount <= 0)
return false;
double texelWidth = 1./atlasWidth; double texelWidth = 1./atlasWidth;
double texelHeight = 1./atlasHeight; double texelHeight = 1./atlasHeight;
bool anyGlyphs = false;
FILE *file = fopen(outputFilename, "w"); FILE *file = fopen(outputFilename, "w");
if (!file) if (!file)
return false; return false;
@ -91,9 +94,12 @@ bool generateShadronPreview(msdfgen::FontHandle *font, const GlyphGeometry *glyp
fprintf(file, " : %sfilter(%s), map(repeat);\n", fullRange ? "full_range(true), " : "", atlasType == ImageType::HARD_MASK ? "nearest" : "linear"); fprintf(file, " : %sfilter(%s), map(repeat);\n", fullRange ? "full_range(true), " : "", atlasType == ImageType::HARD_MASK ? "nearest" : "linear");
fprintf(file, "const vec2 txRange = vec2(%.9g, %.9g);\n\n", pxRange*texelWidth, pxRange*texelHeight); fprintf(file, "const vec2 txRange = vec2(%.9g, %.9g);\n\n", pxRange*texelWidth, pxRange*texelHeight);
{ {
msdfgen::FontMetrics fontMetrics; msdfgen::FontMetrics fontMetrics = fonts->getMetrics();
if (!msdfgen::getFontMetrics(fontMetrics, font)) for (int i = 1; i < fontCount; ++i) {
return false; fontMetrics.lineHeight = std::max(fontMetrics.lineHeight, fonts[i].getMetrics().lineHeight);
fontMetrics.ascenderY = std::max(fontMetrics.ascenderY, fonts[i].getMetrics().ascenderY);
fontMetrics.descenderY = std::min(fontMetrics.descenderY, fonts[i].getMetrics().descenderY);
}
double fsScale = 1/(fontMetrics.ascenderY-fontMetrics.descenderY); double fsScale = 1/(fontMetrics.ascenderY-fontMetrics.descenderY);
fputs("vertex_list GlyphVertex textQuadVertices = {\n", file); fputs("vertex_list GlyphVertex textQuadVertices = {\n", file);
double x = 0, y = -fsScale*fontMetrics.ascenderY; double x = 0, y = -fsScale*fontMetrics.ascenderY;
@ -107,13 +113,14 @@ bool generateShadronPreview(msdfgen::FontHandle *font, const GlyphGeometry *glyp
y -= fsScale*fontMetrics.lineHeight; y -= fsScale*fontMetrics.lineHeight;
continue; continue;
} }
for (int i = 0; i < glyphCount; ++i) { for (int i = 0; i < fontCount; ++i) {
if (glyphs[i].getCodepoint() == *cp) { const GlyphGeometry *glyph = fonts[i].getGlyph(*cp);
if (!glyphs[i].isWhitespace()) { if (glyph) {
if (!glyph->isWhitespace()) {
double pl, pb, pr, pt; double pl, pb, pr, pt;
double il, ib, ir, it; double il, ib, ir, it;
glyphs[i].getQuadPlaneBounds(pl, pb, pr, pt); glyph->getQuadPlaneBounds(pl, pb, pr, pt);
glyphs[i].getQuadAtlasBounds(il, ib, ir, it); glyph->getQuadAtlasBounds(il, ib, ir, it);
pl *= fsScale, pb *= fsScale, pr *= fsScale, pt *= fsScale; pl *= fsScale, pb *= fsScale, pr *= fsScale, pt *= fsScale;
pl += x, pb += y, pr += x, pt += y; pl += x, pb += y, pr += x, pt += y;
il *= texelWidth, ib *= texelHeight, ir *= texelWidth, it *= texelHeight; il *= texelWidth, ib *= texelHeight, ir *= texelWidth, it *= texelHeight;
@ -126,10 +133,10 @@ bool generateShadronPreview(msdfgen::FontHandle *font, const GlyphGeometry *glyp
pr, pb, ir, ib pr, pb, ir, ib
); );
} }
x += fsScale*glyphs[i].getAdvance(); double advance = glyph->getAdvance();
double kerning; fonts[i].getAdvance(advance, cp[0], cp[1]);
if (msdfgen::getKerning(kerning, font, cp[0], cp[1])) x += fsScale*advance;
x += fsScale*kerning; anyGlyphs = true;
break; break;
} }
} }
@ -142,7 +149,7 @@ bool generateShadronPreview(msdfgen::FontHandle *font, const GlyphGeometry *glyp
fputs("PREVIEW_IMAGE(Preview, Atlas, txRange, vec3(1.0), textQuadVertices, textSize, ivec2(1200, 400));\n", file); fputs("PREVIEW_IMAGE(Preview, Atlas, txRange, vec3(1.0), textQuadVertices, textSize, ivec2(1200, 400));\n", file);
fputs("export png(Preview, \"preview.png\");\n", file); fputs("export png(Preview, \"preview.png\");\n", file);
fclose(file); fclose(file);
return true; return anyGlyphs;
} }
} }

View File

@ -4,11 +4,11 @@
#include <msdfgen.h> #include <msdfgen.h>
#include <msdfgen-ext.h> #include <msdfgen-ext.h>
#include "types.h" #include "types.h"
#include "GlyphGeometry.h" #include "FontGeometry.h"
namespace msdf_atlas { namespace msdf_atlas {
/// Generates a Shadron script that displays a string using the generated atlas /// Generates a Shadron script that displays a string using the generated atlas
bool generateShadronPreview(msdfgen::FontHandle *font, const GlyphGeometry *glyphs, int glyphCount, ImageType atlasType, int atlasWidth, int atlasHeight, double pxRange, const unicode_t *text, const char *imageFilename, bool fullRange, const char *outputFilename); bool generateShadronPreview(const FontGeometry *fonts, int fontCount, ImageType atlasType, int atlasWidth, int atlasHeight, double pxRange, const unicode_t *text, const char *imageFilename, bool fullRange, const char *outputFilename);
} }