瀏覽代碼

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

Viktor Chlumský 5 年之前
父節點
當前提交
b9d6f0bb72
共有 5 個文件被更改,包括 66 次插入33 次删除
  1. 3 3
      README.md
  2. 27 15
      ext/import-font.cpp
  3. 25 11
      ext/import-font.h
  4. 9 2
      ext/save-png.cpp
  5. 2 2
      main.cpp

+ 3 - 3
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**.
 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\>** &ndash; specifies the output file name. The desired format will be deduced from the extension
  - **-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).
  - **-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
  - **-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.
    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");

+ 27 - 15
ext/import-font.cpp

@@ -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 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;
     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)));
-    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;
     return 0;
 }
 }
 
 
@@ -106,8 +111,13 @@ void destroyFont(FontHandle *font) {
     delete 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;
     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;
 }
 }
 
 

+ 25 - 11
ext/import-font.h

@@ -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
-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);
 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);
 
 
 }
 }

+ 9 - 2
ext/save-png.cpp

@@ -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) {

+ 2 - 2
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;