mirror of https://github.com/Chlumsky/msdfgen.git
Added font metrics API, fixed glyph import edge case, fixed 8-bit PNG orientation
This commit is contained in:
parent
b1c9f2825d
commit
b9d6f0bb72
|
|
@ -58,7 +58,7 @@ The input can be specified as one of:
|
||||||
The complete list of available options can be printed with **-help**.
|
The complete list of available options can be printed with **-help**.
|
||||||
Some of the important ones are:
|
Some of the important ones are:
|
||||||
- **-o \<filename\>** – specifies the output file name. The desired format will be deduced from the extension
|
- **-o \<filename\>** – 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\>** – specifies the dimensions of the output distance field (in pixels).
|
- **-size \<width\> \<height\>** – specifies the dimensions of the output distance field (in pixels).
|
||||||
- **-range \<range\>**, **-pxrange \<range\>** – specifies the width of the range around the shape
|
- **-range \<range\>**, **-pxrange \<range\>** – 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.
|
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
|
`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.
|
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
|
- 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`
|
- 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.
|
on the distance field beforehand to simulate the standard 8 bits/channel image format.
|
||||||
|
|
||||||
|
|
@ -121,7 +121,7 @@ int main() {
|
||||||
// max. angle
|
// max. angle
|
||||||
edgeColoringSimple(shape, 3.0);
|
edgeColoringSimple(shape, 3.0);
|
||||||
// image width, height
|
// image width, height
|
||||||
Bitmap<FloatRGB> msdf(32, 32);
|
Bitmap<float, 3> msdf(32, 32);
|
||||||
// range, scale, translation
|
// range, scale, translation
|
||||||
generateMSDF(msdf, shape, 4.0, 1.0, Vector2(4.0, 4.0));
|
generateMSDF(msdf, shape, 4.0, 1.0, Vector2(4.0, 4.0));
|
||||||
savePng(msdf, "output.png");
|
savePng(msdf, "output.png");
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@
|
||||||
namespace msdfgen {
|
namespace msdfgen {
|
||||||
|
|
||||||
#define REQUIRE(cond) { if (!(cond)) return false; }
|
#define REQUIRE(cond) { if (!(cond)) return false; }
|
||||||
|
#define F26DOT6_TO_DOUBLE(x) (1/64.*double(x))
|
||||||
|
|
||||||
class FreetypeHandle {
|
class FreetypeHandle {
|
||||||
friend FreetypeHandle * initializeFreetype();
|
friend FreetypeHandle * initializeFreetype();
|
||||||
|
|
@ -27,10 +28,10 @@ class FreetypeHandle {
|
||||||
class FontHandle {
|
class FontHandle {
|
||||||
friend FontHandle * loadFont(FreetypeHandle *library, const char *filename);
|
friend FontHandle * loadFont(FreetypeHandle *library, const char *filename);
|
||||||
friend void destroyFont(FontHandle *font);
|
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 getFontWhitespaceWidth(double &spaceAdvance, double &tabAdvance, FontHandle *font);
|
||||||
friend bool loadGlyph(Shape &output, FontHandle *font, int unicode, double *advance);
|
friend bool loadGlyph(Shape &output, FontHandle *font, unicode_t unicode, double *advance);
|
||||||
friend bool getKerning(double &output, FontHandle *font, int unicode1, int unicode2);
|
friend bool getKerning(double &output, FontHandle *font, unicode_t unicode1, unicode_t unicode2);
|
||||||
|
|
||||||
FT_Face face;
|
FT_Face face;
|
||||||
|
|
||||||
|
|
@ -43,20 +44,24 @@ struct FtContext {
|
||||||
};
|
};
|
||||||
|
|
||||||
static Point2 ftPoint2(const FT_Vector &vector) {
|
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) {
|
static int ftMoveTo(const FT_Vector *to, void *user) {
|
||||||
FtContext *context = reinterpret_cast<FtContext *>(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);
|
context->position = ftPoint2(*to);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ftLineTo(const FT_Vector *to, void *user) {
|
static int ftLineTo(const FT_Vector *to, void *user) {
|
||||||
FtContext *context = reinterpret_cast<FtContext *>(user);
|
FtContext *context = reinterpret_cast<FtContext *>(user);
|
||||||
context->contour->addEdge(new LinearSegment(context->position, ftPoint2(*to)));
|
Point2 endpoint = ftPoint2(*to);
|
||||||
context->position = ftPoint2(*to);
|
if (endpoint != context->position) {
|
||||||
|
context->contour->addEdge(new LinearSegment(context->position, endpoint));
|
||||||
|
context->position = endpoint;
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -106,8 +111,13 @@ void destroyFont(FontHandle *font) {
|
||||||
delete font;
|
delete font;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool getFontScale(double &output, FontHandle *font) {
|
bool getFontMetrics(FontMetrics &metrics, FontHandle *font) {
|
||||||
output = font->face->units_per_EM/64.;
|
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;
|
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);
|
FT_Error error = FT_Load_Char(font->face, ' ', FT_LOAD_NO_SCALE);
|
||||||
if (error)
|
if (error)
|
||||||
return false;
|
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);
|
error = FT_Load_Char(font->face, '\t', FT_LOAD_NO_SCALE);
|
||||||
if (error)
|
if (error)
|
||||||
return false;
|
return false;
|
||||||
tabAdvance = font->face->glyph->advance.x/64.;
|
tabAdvance = F26DOT6_TO_DOUBLE(font->face->glyph->advance.x);
|
||||||
return true;
|
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)
|
if (!font)
|
||||||
return false;
|
return false;
|
||||||
FT_Error error = FT_Load_Char(font->face, unicode, FT_LOAD_NO_SCALE);
|
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.contours.clear();
|
||||||
output.inverseYAxis = false;
|
output.inverseYAxis = false;
|
||||||
if (advance)
|
if (advance)
|
||||||
*advance = font->face->glyph->advance.x/64.;
|
*advance = F26DOT6_TO_DOUBLE(font->face->glyph->advance.x);
|
||||||
|
|
||||||
FtContext context = { };
|
FtContext context = { };
|
||||||
context.shape = &output;
|
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);
|
error = FT_Outline_Decompose(&font->face->glyph->outline, &ftFunctions, &context);
|
||||||
if (error)
|
if (error)
|
||||||
return false;
|
return false;
|
||||||
|
if (!output.contours.empty() && output.contours.back().edges.empty())
|
||||||
|
output.contours.pop_back();
|
||||||
return true;
|
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;
|
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)) {
|
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;
|
output = 0;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
output = kerning.x/64.;
|
output = F26DOT6_TO_DOUBLE(kerning.x);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,24 +6,38 @@
|
||||||
|
|
||||||
namespace msdfgen {
|
namespace msdfgen {
|
||||||
|
|
||||||
|
typedef unsigned unicode_t;
|
||||||
|
|
||||||
class FreetypeHandle;
|
class FreetypeHandle;
|
||||||
class FontHandle;
|
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();
|
FreetypeHandle * initializeFreetype();
|
||||||
/// Deinitializes the FreeType library
|
/// Deinitializes the FreeType library.
|
||||||
void deinitializeFreetype(FreetypeHandle *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);
|
FontHandle * loadFont(FreetypeHandle *library, const char *filename);
|
||||||
/// Unloads a font file
|
/// Unloads a font file.
|
||||||
void destroyFont(FontHandle *font);
|
void destroyFont(FontHandle *font);
|
||||||
/// Returns the size of one EM in the font's coordinate system
|
/// Outputs the metrics of a font file.
|
||||||
bool getFontScale(double &output, FontHandle *font);
|
bool getFontMetrics(FontMetrics &metrics, FontHandle *font);
|
||||||
/// Returns the width of space and tab
|
/// Outputs the width of the space and tab characters.
|
||||||
bool getFontWhitespaceWidth(double &spaceAdvance, double &tabAdvance, FontHandle *font);
|
bool getFontWhitespaceWidth(double &spaceAdvance, double &tabAdvance, FontHandle *font);
|
||||||
/// Loads the shape prototype of a glyph from font file
|
/// Loads the geometry of a glyph from a font file.
|
||||||
bool loadGlyph(Shape &output, FontHandle *font, int unicode, double *advance = NULL);
|
bool loadGlyph(Shape &output, FontHandle *font, unicode_t unicode, double *advance = NULL);
|
||||||
/// Returns the kerning distance adjustment between two specific glyphs.
|
/// Outputs the kerning distance adjustment between two specific glyphs.
|
||||||
bool getKerning(double &output, FontHandle *font, int unicode1, int unicode2);
|
bool getKerning(double &output, FontHandle *font, unicode_t unicode1, unicode_t unicode2);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,24 @@
|
||||||
|
|
||||||
#include "save-png.h"
|
#include "save-png.h"
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
#include <lodepng.h>
|
#include <lodepng.h>
|
||||||
#include "../core/pixel-conversion.hpp"
|
#include "../core/pixel-conversion.hpp"
|
||||||
|
|
||||||
namespace msdfgen {
|
namespace msdfgen {
|
||||||
|
|
||||||
bool savePng(const BitmapConstRef<byte, 1> &bitmap, const char *filename) {
|
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) {
|
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) {
|
bool savePng(const BitmapConstRef<float, 1> &bitmap, const char *filename) {
|
||||||
|
|
|
||||||
4
main.cpp
4
main.cpp
|
|
@ -65,7 +65,7 @@ static bool parseDouble(double &value, const char *arg) {
|
||||||
return sscanf(arg, "%lf%c", &value, &c) == 1;
|
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;
|
unsigned uuc;
|
||||||
if (parseUnsigned(uuc, arg)) {
|
if (parseUnsigned(uuc, arg)) {
|
||||||
unicode = uuc;
|
unicode = uuc;
|
||||||
|
|
@ -368,7 +368,7 @@ int main(int argc, const char * const *argv) {
|
||||||
const char *testRender = NULL;
|
const char *testRender = NULL;
|
||||||
const char *testRenderMulti = NULL;
|
const char *testRenderMulti = NULL;
|
||||||
bool outputSpecified = false;
|
bool outputSpecified = false;
|
||||||
int unicode = 0;
|
unicode_t unicode = 0;
|
||||||
int svgPathIndex = 0;
|
int svgPathIndex = 0;
|
||||||
|
|
||||||
int width = 64, height = 64;
|
int width = 64, height = 64;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue