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**.
|
||||
Some of the important ones are:
|
||||
- **-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).
|
||||
- **-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.
|
||||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
|
|
|
|||
Loading…
Reference in New Issue