diff --git a/README.md b/README.md index 1f7ab8b..66800d7 100644 --- a/README.md +++ b/README.md @@ -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 \** – 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 \ \** – specifies the dimensions of the output distance field (in pixels). - **-range \**, **-pxrange \** – 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 msdf(32, 32); + Bitmap msdf(32, 32); // range, scale, translation generateMSDF(msdf, shape, 4.0, 1.0, Vector2(4.0, 4.0)); savePng(msdf, "output.png"); diff --git a/ext/import-font.cpp b/ext/import-font.cpp index de0f81c..f4afb11 100644 --- a/ext/import-font.cpp +++ b/ext/import-font.cpp @@ -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(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(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; } diff --git a/ext/import-font.h b/ext/import-font.h index 32ff42a..4edf4f0 100644 --- a/ext/import-font.h +++ b/ext/import-font.h @@ -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); } diff --git a/ext/save-png.cpp b/ext/save-png.cpp index 6eb2f55..b8a1759 100644 --- a/ext/save-png.cpp +++ b/ext/save-png.cpp @@ -1,17 +1,24 @@ #include "save-png.h" +#include #include #include "../core/pixel-conversion.hpp" namespace msdfgen { bool savePng(const BitmapConstRef &bitmap, const char *filename) { - return !lodepng::encode(filename, bitmap.pixels, bitmap.width, bitmap.height, LCT_GREY); + std::vector 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 &bitmap, const char *filename) { - return !lodepng::encode(filename, bitmap.pixels, bitmap.width, bitmap.height, LCT_RGB); + std::vector 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 &bitmap, const char *filename) { diff --git a/main.cpp b/main.cpp index 432ed45..24ebb9e 100644 --- a/main.cpp +++ b/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;