소스 검색

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**.
 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");

+ 27 - 15
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<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;
 }
 

+ 25 - 11
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);
 
 }

+ 9 - 2
ext/save-png.cpp

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

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