Added font metrics API, fixed glyph import edge case, fixed 8-bit PNG orientation

This commit is contained in:
Viktor Chlumský 2020-01-12 11:40:49 +01:00
parent b1c9f2825d
commit b9d6f0bb72
5 changed files with 66 additions and 33 deletions

View File

@ -58,7 +58,7 @@ The input can be specified as one of:
The complete list of available options can be printed with **-help**.
Some of the important ones are:
- **-o \<filename\>** &ndash; specifies the output file name. The desired format will be deduced from the extension
(png, bmp, txt, bin). Otherwise, use -format.
(png, bmp, tif, txt, bin). Otherwise, use -format.
- **-size \<width\> \<height\>** &ndash; specifies the dimensions of the output distance field (in pixels).
- **-range \<range\>**, **-pxrange \<range\>** &ndash; specifies the width of the range around the shape
between the minimum and maximum representable signed distance in shape units or distance field pixels, respectivelly.
@ -99,7 +99,7 @@ in order to generate a distance field. Please note that all classes and function
`color` member. Keep in mind that at least two color channels must be turned on in each edge, and iff two edges meet
at a sharp corner, they must only have one channel in common.
- Call `generateSDF`, `generatePseudoSDF`, or `generateMSDF` to generate a distance field into a floating point
`Bitmap` object. This can then be worked with further or saved to a file using `saveBmp` or `savePng`.
`Bitmap` object. This can then be worked with further or saved to a file using `saveBmp`, `savePng`, or `saveTiff`.
- You may also render an image from the distance field using `renderSDF`. Consider calling `simulate8bit`
on the distance field beforehand to simulate the standard 8 bits/channel image format.
@ -121,7 +121,7 @@ int main() {
// max. angle
edgeColoringSimple(shape, 3.0);
// image width, height
Bitmap<FloatRGB> msdf(32, 32);
Bitmap<float, 3> msdf(32, 32);
// range, scale, translation
generateMSDF(msdf, shape, 4.0, 1.0, Vector2(4.0, 4.0));
savePng(msdf, "output.png");

View File

@ -14,6 +14,7 @@
namespace msdfgen {
#define REQUIRE(cond) { if (!(cond)) return false; }
#define F26DOT6_TO_DOUBLE(x) (1/64.*double(x))
class FreetypeHandle {
friend FreetypeHandle * initializeFreetype();
@ -27,10 +28,10 @@ class FreetypeHandle {
class FontHandle {
friend FontHandle * loadFont(FreetypeHandle *library, const char *filename);
friend void destroyFont(FontHandle *font);
friend bool getFontScale(double &output, FontHandle *font);
friend bool getFontMetrics(FontMetrics &metrics, FontHandle *font);
friend bool getFontWhitespaceWidth(double &spaceAdvance, double &tabAdvance, FontHandle *font);
friend bool loadGlyph(Shape &output, FontHandle *font, int unicode, double *advance);
friend bool getKerning(double &output, FontHandle *font, int unicode1, int unicode2);
friend bool loadGlyph(Shape &output, FontHandle *font, unicode_t unicode, double *advance);
friend bool getKerning(double &output, FontHandle *font, unicode_t unicode1, unicode_t unicode2);
FT_Face face;
@ -43,20 +44,24 @@ struct FtContext {
};
static Point2 ftPoint2(const FT_Vector &vector) {
return Point2(vector.x/64., vector.y/64.);
return Point2(F26DOT6_TO_DOUBLE(vector.x), F26DOT6_TO_DOUBLE(vector.y));
}
static int ftMoveTo(const FT_Vector *to, void *user) {
FtContext *context = reinterpret_cast<FtContext *>(user);
context->contour = &context->shape->addContour();
if (!(context->contour && context->contour->edges.empty()))
context->contour = &context->shape->addContour();
context->position = ftPoint2(*to);
return 0;
}
static int ftLineTo(const FT_Vector *to, void *user) {
FtContext *context = reinterpret_cast<FtContext *>(user);
context->contour->addEdge(new LinearSegment(context->position, ftPoint2(*to)));
context->position = ftPoint2(*to);
Point2 endpoint = ftPoint2(*to);
if (endpoint != context->position) {
context->contour->addEdge(new LinearSegment(context->position, endpoint));
context->position = endpoint;
}
return 0;
}
@ -106,8 +111,13 @@ void destroyFont(FontHandle *font) {
delete font;
}
bool getFontScale(double &output, FontHandle *font) {
output = font->face->units_per_EM/64.;
bool getFontMetrics(FontMetrics &metrics, FontHandle *font) {
metrics.emSize = F26DOT6_TO_DOUBLE(font->face->units_per_EM);
metrics.ascenderY = F26DOT6_TO_DOUBLE(font->face->ascender);
metrics.descenderY = F26DOT6_TO_DOUBLE(font->face->descender);
metrics.lineHeight = F26DOT6_TO_DOUBLE(font->face->height);
metrics.underlineY = F26DOT6_TO_DOUBLE(font->face->underline_position);
metrics.underlineThickness = F26DOT6_TO_DOUBLE(font->face->underline_thickness);
return true;
}
@ -115,15 +125,15 @@ bool getFontWhitespaceWidth(double &spaceAdvance, double &tabAdvance, FontHandle
FT_Error error = FT_Load_Char(font->face, ' ', FT_LOAD_NO_SCALE);
if (error)
return false;
spaceAdvance = font->face->glyph->advance.x/64.;
spaceAdvance = F26DOT6_TO_DOUBLE(font->face->glyph->advance.x);
error = FT_Load_Char(font->face, '\t', FT_LOAD_NO_SCALE);
if (error)
return false;
tabAdvance = font->face->glyph->advance.x/64.;
tabAdvance = F26DOT6_TO_DOUBLE(font->face->glyph->advance.x);
return true;
}
bool loadGlyph(Shape &output, FontHandle *font, int unicode, double *advance) {
bool loadGlyph(Shape &output, FontHandle *font, unicode_t unicode, double *advance) {
if (!font)
return false;
FT_Error error = FT_Load_Char(font->face, unicode, FT_LOAD_NO_SCALE);
@ -132,7 +142,7 @@ bool loadGlyph(Shape &output, FontHandle *font, int unicode, double *advance) {
output.contours.clear();
output.inverseYAxis = false;
if (advance)
*advance = font->face->glyph->advance.x/64.;
*advance = F26DOT6_TO_DOUBLE(font->face->glyph->advance.x);
FtContext context = { };
context.shape = &output;
@ -146,16 +156,18 @@ bool loadGlyph(Shape &output, FontHandle *font, int unicode, double *advance) {
error = FT_Outline_Decompose(&font->face->glyph->outline, &ftFunctions, &context);
if (error)
return false;
if (!output.contours.empty() && output.contours.back().edges.empty())
output.contours.pop_back();
return true;
}
bool getKerning(double &output, FontHandle *font, int unicode1, int unicode2) {
bool getKerning(double &output, FontHandle *font, unicode_t unicode1, unicode_t unicode2) {
FT_Vector kerning;
if (FT_Get_Kerning(font->face, FT_Get_Char_Index(font->face, unicode1), FT_Get_Char_Index(font->face, unicode2), FT_KERNING_UNSCALED, &kerning)) {
output = 0;
return false;
}
output = kerning.x/64.;
output = F26DOT6_TO_DOUBLE(kerning.x);
return true;
}

View File

@ -6,24 +6,38 @@
namespace msdfgen {
typedef unsigned unicode_t;
class FreetypeHandle;
class FontHandle;
/// Initializes the FreeType library
/// Global metrics of a typeface (in font units).
struct FontMetrics {
/// The size of one EM.
double emSize;
/// The vertical position of the ascender and descender relative to the baseline.
double ascenderY, descenderY;
/// The vertical difference between consecutive baselines.
double lineHeight;
/// The vertical position and thickness of the underline.
double underlineY, underlineThickness;
};
/// Initializes the FreeType library.
FreetypeHandle * initializeFreetype();
/// Deinitializes the FreeType library
/// Deinitializes the FreeType library.
void deinitializeFreetype(FreetypeHandle *library);
/// Loads a font file and returns its handle
/// Loads a font file and returns its handle.
FontHandle * loadFont(FreetypeHandle *library, const char *filename);
/// Unloads a font file
/// Unloads a font file.
void destroyFont(FontHandle *font);
/// Returns the size of one EM in the font's coordinate system
bool getFontScale(double &output, FontHandle *font);
/// Returns the width of space and tab
/// Outputs the metrics of a font file.
bool getFontMetrics(FontMetrics &metrics, FontHandle *font);
/// Outputs the width of the space and tab characters.
bool getFontWhitespaceWidth(double &spaceAdvance, double &tabAdvance, FontHandle *font);
/// Loads the shape prototype of a glyph from font file
bool loadGlyph(Shape &output, FontHandle *font, int unicode, double *advance = NULL);
/// Returns the kerning distance adjustment between two specific glyphs.
bool getKerning(double &output, FontHandle *font, int unicode1, int unicode2);
/// Loads the geometry of a glyph from a font file.
bool loadGlyph(Shape &output, FontHandle *font, unicode_t unicode, double *advance = NULL);
/// Outputs the kerning distance adjustment between two specific glyphs.
bool getKerning(double &output, FontHandle *font, unicode_t unicode1, unicode_t unicode2);
}

View File

@ -1,17 +1,24 @@
#include "save-png.h"
#include <cstring>
#include <lodepng.h>
#include "../core/pixel-conversion.hpp"
namespace msdfgen {
bool savePng(const BitmapConstRef<byte, 1> &bitmap, const char *filename) {
return !lodepng::encode(filename, bitmap.pixels, bitmap.width, bitmap.height, LCT_GREY);
std::vector<byte> pixels(bitmap.width*bitmap.height);
for (int y = 0; y < bitmap.height; ++y)
memcpy(&pixels[bitmap.width*y], bitmap(0, bitmap.height-y-1), bitmap.width);
return !lodepng::encode(filename, pixels, bitmap.width, bitmap.height, LCT_GREY);
}
bool savePng(const BitmapConstRef<byte, 3> &bitmap, const char *filename) {
return !lodepng::encode(filename, bitmap.pixels, bitmap.width, bitmap.height, LCT_RGB);
std::vector<byte> pixels(3*bitmap.width*bitmap.height);
for (int y = 0; y < bitmap.height; ++y)
memcpy(&pixels[3*bitmap.width*y], bitmap(0, bitmap.height-y-1), 3*bitmap.width);
return !lodepng::encode(filename, pixels, bitmap.width, bitmap.height, LCT_RGB);
}
bool savePng(const BitmapConstRef<float, 1> &bitmap, const char *filename) {

View File

@ -65,7 +65,7 @@ static bool parseDouble(double &value, const char *arg) {
return sscanf(arg, "%lf%c", &value, &c) == 1;
}
static bool parseUnicode(int &unicode, const char *arg) {
static bool parseUnicode(unicode_t &unicode, const char *arg) {
unsigned uuc;
if (parseUnsigned(uuc, arg)) {
unicode = uuc;
@ -368,7 +368,7 @@ int main(int argc, const char * const *argv) {
const char *testRender = NULL;
const char *testRenderMulti = NULL;
bool outputSpecified = false;
int unicode = 0;
unicode_t unicode = 0;
int svgPathIndex = 0;
int width = 64, height = 64;