From 67510959f13b301a077c06940da6d57eb6414410 Mon Sep 17 00:00:00 2001 From: Chlumsky Date: Sat, 1 May 2021 22:05:28 +0200 Subject: [PATCH] Added -yorigin setting --- msdf-atlas-gen/artery-font-export.cpp | 17 +++- msdf-atlas-gen/artery-font-export.h | 1 + msdf-atlas-gen/csv-export.cpp | 20 +++- msdf-atlas-gen/csv-export.h | 2 +- msdf-atlas-gen/image-save.h | 2 +- msdf-atlas-gen/image-save.hpp | 133 +++++++++++++++----------- msdf-atlas-gen/json-export.cpp | 35 +++++-- msdf-atlas-gen/json-export.h | 2 +- msdf-atlas-gen/main.cpp | 24 ++++- msdf-atlas-gen/types.h | 6 ++ 10 files changed, 165 insertions(+), 77 deletions(-) diff --git a/msdf-atlas-gen/artery-font-export.cpp b/msdf-atlas-gen/artery-font-export.cpp index fc8a61a..e7d96b5 100644 --- a/msdf-atlas-gen/artery-font-export.cpp +++ b/msdf-atlas-gen/artery-font-export.cpp @@ -154,9 +154,22 @@ bool exportArteryFont(const FontGeometry *fonts, int fontCount, const msdfgen::B return false; image.encoding = artery_font::IMAGE_RAW_BINARY; image.rawBinaryFormat.rowLength = N*sizeof(T)*atlas.width; - image.rawBinaryFormat.orientation = artery_font::ORIENTATION_BOTTOM_UP; image.data = artery_font::StdByteArray(N*sizeof(T)*atlas.width*atlas.height); - memcpy((byte *) image.data, atlas.pixels, N*sizeof(T)*atlas.width*atlas.height); + switch (properties.yDirection) { + case YDirection::BOTTOM_UP: + image.rawBinaryFormat.orientation = artery_font::ORIENTATION_BOTTOM_UP; + memcpy((byte *) image.data, atlas.pixels, N*sizeof(T)*atlas.width*atlas.height); + break; + case YDirection::TOP_DOWN: { + image.rawBinaryFormat.orientation = artery_font::ORIENTATION_TOP_DOWN; + byte *imageData = (byte *) image.data; + for (int y = atlas.height-1; y >= 0; --y) { + memcpy(imageData, atlas.pixels+N*atlas.width*y, N*sizeof(T)*atlas.width); + imageData += N*sizeof(T)*atlas.width; + } + break; + } + } break; default: return false; diff --git a/msdf-atlas-gen/artery-font-export.h b/msdf-atlas-gen/artery-font-export.h index 74a2b22..593fe5c 100644 --- a/msdf-atlas-gen/artery-font-export.h +++ b/msdf-atlas-gen/artery-font-export.h @@ -13,6 +13,7 @@ struct ArteryFontExportProperties { double pxRange; ImageType imageType; ImageFormat imageFormat; + YDirection yDirection; }; /// Encodes the atlas bitmap and its layout into an Artery Atlas Font file diff --git a/msdf-atlas-gen/csv-export.cpp b/msdf-atlas-gen/csv-export.cpp index 28e46ff..a85f9bc 100644 --- a/msdf-atlas-gen/csv-export.cpp +++ b/msdf-atlas-gen/csv-export.cpp @@ -6,7 +6,7 @@ namespace msdf_atlas { -bool exportCSV(const FontGeometry *fonts, int fontCount, const char *filename) { +bool exportCSV(const FontGeometry *fonts, int fontCount, int atlasWidth, int atlasHeight, YDirection yDirection, const char *filename) { FILE *f = fopen(filename, "w"); if (!f) return false; @@ -18,9 +18,23 @@ bool exportCSV(const FontGeometry *fonts, int fontCount, const char *filename) { fprintf(f, "%d,", i); fprintf(f, "%d,%.17g,", glyph.getIdentifier(fonts[i].getPreferredIdentifierType()), glyph.getAdvance()); glyph.getQuadPlaneBounds(l, b, r, t); - fprintf(f, "%.17g,%.17g,%.17g,%.17g,", l, b, r, t); + switch (yDirection) { + case YDirection::BOTTOM_UP: + fprintf(f, "%.17g,%.17g,%.17g,%.17g,", l, b, r, t); + break; + case YDirection::TOP_DOWN: + fprintf(f, "%.17g,%.17g,%.17g,%.17g,", l, -t, r, -b); + break; + } glyph.getQuadAtlasBounds(l, b, r, t); - fprintf(f, "%.17g,%.17g,%.17g,%.17g\n", l, b, r, t); + switch (yDirection) { + case YDirection::BOTTOM_UP: + fprintf(f, "%.17g,%.17g,%.17g,%.17g\n", l, b, r, t); + break; + case YDirection::TOP_DOWN: + fprintf(f, "%.17g,%.17g,%.17g,%.17g\n", l, atlasHeight-t, r, atlasHeight-b); + break; + } } } diff --git a/msdf-atlas-gen/csv-export.h b/msdf-atlas-gen/csv-export.h index 49184e7..56d5c4c 100644 --- a/msdf-atlas-gen/csv-export.h +++ b/msdf-atlas-gen/csv-export.h @@ -9,6 +9,6 @@ namespace msdf_atlas { * Writes the positioning data and atlas layout of the glyphs into a CSV file * The columns are: font variant index (if fontCount > 1), glyph identifier (index or Unicode), horizontal advance, plane bounds (l, b, r, t), atlas bounds (l, b, r, t) */ -bool exportCSV(const FontGeometry *fonts, int fontCount, const char *filename); +bool exportCSV(const FontGeometry *fonts, int fontCount, int atlasWidth, int atlasHeight, YDirection yDirection, const char *filename); } diff --git a/msdf-atlas-gen/image-save.h b/msdf-atlas-gen/image-save.h index 0e73fa8..0a0b37e 100644 --- a/msdf-atlas-gen/image-save.h +++ b/msdf-atlas-gen/image-save.h @@ -8,7 +8,7 @@ namespace msdf_atlas { /// Saves the bitmap as an image file with the specified format template -bool saveImage(const msdfgen::BitmapConstRef &bitmap, ImageFormat format, const char *filename); +bool saveImage(const msdfgen::BitmapConstRef &bitmap, ImageFormat format, const char *filename, YDirection outputYDirection = YDirection::BOTTOM_UP); } diff --git a/msdf-atlas-gen/image-save.hpp b/msdf-atlas-gen/image-save.hpp index cd3dc7e..81b3dc6 100644 --- a/msdf-atlas-gen/image-save.hpp +++ b/msdf-atlas-gen/image-save.hpp @@ -7,19 +7,19 @@ namespace msdf_atlas { template -bool saveImageBinary(const msdfgen::BitmapConstRef &bitmap, const char *filename); +bool saveImageBinary(const msdfgen::BitmapConstRef &bitmap, const char *filename, YDirection outputYDirection); template -bool saveImageBinaryLE(const msdfgen::BitmapConstRef &bitmap, const char *filename); +bool saveImageBinaryLE(const msdfgen::BitmapConstRef &bitmap, const char *filename, YDirection outputYDirection); template -bool saveImageBinaryBE(const msdfgen::BitmapConstRef &bitmap, const char *filename); +bool saveImageBinaryBE(const msdfgen::BitmapConstRef &bitmap, const char *filename, YDirection outputYDirection); template -bool saveImageText(const msdfgen::BitmapConstRef &bitmap, const char *filename); +bool saveImageText(const msdfgen::BitmapConstRef &bitmap, const char *filename, YDirection outputYDirection); template -bool saveImageText(const msdfgen::BitmapConstRef &bitmap, const char *filename); +bool saveImageText(const msdfgen::BitmapConstRef &bitmap, const char *filename, YDirection outputYDirection); template -bool saveImage(const msdfgen::BitmapConstRef &bitmap, ImageFormat format, const char *filename) { +bool saveImage(const msdfgen::BitmapConstRef &bitmap, ImageFormat format, const char *filename, YDirection outputYDirection) { switch (format) { case ImageFormat::PNG: return msdfgen::savePng(bitmap, filename); @@ -28,11 +28,11 @@ bool saveImage(const msdfgen::BitmapConstRef &bitmap, ImageFormat forma case ImageFormat::TIFF: return false; case ImageFormat::TEXT: - return saveImageText(bitmap, filename); + return saveImageText(bitmap, filename, outputYDirection); case ImageFormat::TEXT_FLOAT: return false; case ImageFormat::BINARY: - return saveImageBinary(bitmap, filename); + return saveImageBinary(bitmap, filename, outputYDirection); case ImageFormat::BINARY_FLOAT: case ImageFormat::BINARY_FLOAT_BE: return false; @@ -42,7 +42,7 @@ bool saveImage(const msdfgen::BitmapConstRef &bitmap, ImageFormat forma } template -bool saveImage(const msdfgen::BitmapConstRef &bitmap, ImageFormat format, const char *filename) { +bool saveImage(const msdfgen::BitmapConstRef &bitmap, ImageFormat format, const char *filename, YDirection outputYDirection) { switch (format) { case ImageFormat::PNG: return msdfgen::savePng(bitmap, filename); @@ -53,63 +53,84 @@ bool saveImage(const msdfgen::BitmapConstRef &bitmap, ImageFormat form case ImageFormat::TEXT: return false; case ImageFormat::TEXT_FLOAT: - return saveImageText(bitmap, filename); + return saveImageText(bitmap, filename, outputYDirection); case ImageFormat::BINARY: return false; case ImageFormat::BINARY_FLOAT: - return saveImageBinaryLE(bitmap, filename); + return saveImageBinaryLE(bitmap, filename, outputYDirection); case ImageFormat::BINARY_FLOAT_BE: - return saveImageBinaryBE(bitmap, filename); + return saveImageBinaryBE(bitmap, filename, outputYDirection); default:; } return false; } template -bool saveImageBinary(const msdfgen::BitmapConstRef &bitmap, const char *filename) { +bool saveImageBinary(const msdfgen::BitmapConstRef &bitmap, const char *filename, YDirection outputYDirection) { bool success = false; if (FILE *f = fopen(filename, "wb")) { - success = fwrite(bitmap.pixels, 1, N*bitmap.width*bitmap.height, f) == N*bitmap.width*bitmap.height; - fclose(f); - } - return success; -} - -template -bool - #ifdef __BIG_ENDIAN__ - saveImageBinaryBE - #else - saveImageBinaryLE - #endif - (const msdfgen::BitmapConstRef &bitmap, const char *filename) { - bool success = false; - if (FILE *f = fopen(filename, "wb")) { - success = fwrite(bitmap.pixels, sizeof(float), N*bitmap.width*bitmap.height, f) == N*bitmap.width*bitmap.height; - fclose(f); - } - return success; -} - -template -bool - #ifdef __BIG_ENDIAN__ - saveImageBinaryLE - #else - saveImageBinaryBE - #endif - (const msdfgen::BitmapConstRef &bitmap, const char *filename) { - bool success = false; - if (FILE *f = fopen(filename, "wb")) { - const float *p = bitmap.pixels; - int count = N*bitmap.width*bitmap.height; int written = 0; - for (int i = 0; i < count; ++i) { - const unsigned char *b = reinterpret_cast(p++); - for (int i = sizeof(float)-1; i >= 0; --i) - written += fwrite(b+i, 1, 1, f); + switch (outputYDirection) { + case YDirection::BOTTOM_UP: + written = fwrite(bitmap.pixels, 1, N*bitmap.width*bitmap.height, f); + break; + case YDirection::TOP_DOWN: + for (int y = bitmap.height-1; y >= 0; --y) + written += fwrite(bitmap.pixels+N*bitmap.width*y, 1, N*bitmap.width, f); + break; } - success = written == sizeof(float)*count; + success = written == N*bitmap.width*bitmap.height; + fclose(f); + } + return success; +} + +template +bool + #ifdef __BIG_ENDIAN__ + saveImageBinaryBE + #else + saveImageBinaryLE + #endif + (const msdfgen::BitmapConstRef &bitmap, const char *filename, YDirection outputYDirection) { + bool success = false; + if (FILE *f = fopen(filename, "wb")) { + int written = 0; + switch (outputYDirection) { + case YDirection::BOTTOM_UP: + written = fwrite(bitmap.pixels, sizeof(float), N*bitmap.width*bitmap.height, f); + break; + case YDirection::TOP_DOWN: + for (int y = bitmap.height-1; y >= 0; --y) + written += fwrite(bitmap.pixels+N*bitmap.width*y, sizeof(float), N*bitmap.width, f); + break; + } + success = written == N*bitmap.width*bitmap.height; + fclose(f); + } + return success; +} + +template +bool + #ifdef __BIG_ENDIAN__ + saveImageBinaryLE + #else + saveImageBinaryBE + #endif + (const msdfgen::BitmapConstRef &bitmap, const char *filename, YDirection outputYDirection) { + bool success = false; + if (FILE *f = fopen(filename, "wb")) { + int written = 0; + for (int y = 0; y < bitmap.height; ++y) { + const float *p = bitmap.pixels+N*bitmap.width*(outputYDirection == YDirection::TOP_DOWN ? bitmap.height-y-1 : y); + for (int x = 0; x < bitmap.width; ++x) { + const unsigned char *b = reinterpret_cast(p++); + for (int i = sizeof(float)-1; i >= 0; --i) + written += fwrite(b+i, 1, 1, f); + } + } + success = written == sizeof(float)*N*bitmap.width*bitmap.height; fclose(f); } return success; @@ -117,11 +138,11 @@ bool template -bool saveImageText(const msdfgen::BitmapConstRef &bitmap, const char *filename) { +bool saveImageText(const msdfgen::BitmapConstRef &bitmap, const char *filename, YDirection outputYDirection) { bool success = false; if (FILE *f = fopen(filename, "wb")) { - const byte *p = bitmap.pixels; for (int y = 0; y < bitmap.height; ++y) { + const byte *p = bitmap.pixels+N*bitmap.width*(outputYDirection == YDirection::TOP_DOWN ? bitmap.height-y-1 : y); for (int x = 0; x < N*bitmap.width; ++x) { fprintf(f, x ? " %02X" : "%02X", (unsigned) *p++); } @@ -133,11 +154,11 @@ bool saveImageText(const msdfgen::BitmapConstRef &bitmap, const char *f } template -bool saveImageText(const msdfgen::BitmapConstRef &bitmap, const char *filename) { +bool saveImageText(const msdfgen::BitmapConstRef &bitmap, const char *filename, YDirection outputYDirection) { bool success = false; if (FILE *f = fopen(filename, "wb")) { - const float *p = bitmap.pixels; for (int y = 0; y < bitmap.height; ++y) { + const float *p = bitmap.pixels+N*bitmap.width*(outputYDirection == YDirection::TOP_DOWN ? bitmap.height-y-1 : y); for (int x = 0; x < N*bitmap.width; ++x) { fprintf(f, x ? " %g" : "%g", *p++); } diff --git a/msdf-atlas-gen/json-export.cpp b/msdf-atlas-gen/json-export.cpp index 2483bcf..2bf24dc 100644 --- a/msdf-atlas-gen/json-export.cpp +++ b/msdf-atlas-gen/json-export.cpp @@ -58,7 +58,7 @@ static const char * imageTypeString(ImageType type) { return nullptr; } -bool exportJSON(const FontGeometry *fonts, int fontCount, double fontSize, double pxRange, int atlasWidth, int atlasHeight, ImageType imageType, const char *filename, bool kerning) { +bool exportJSON(const FontGeometry *fonts, int fontCount, double fontSize, double pxRange, int atlasWidth, int atlasHeight, ImageType imageType, YDirection yDirection, const char *filename, bool kerning) { FILE *f = fopen(filename, "w"); if (!f) return false; @@ -72,7 +72,7 @@ bool exportJSON(const FontGeometry *fonts, int fontCount, double fontSize, doubl fprintf(f, "\"size\":%.17g,", fontSize); fprintf(f, "\"width\":%d,", atlasWidth); fprintf(f, "\"height\":%d,", atlasHeight); - fputs("\"yOrigin\":\"bottom\"", f); + fprintf(f, "\"yOrigin\":\"%s\"", yDirection == YDirection::TOP_DOWN ? "top" : "bottom"); } fputs("},", f); if (fontCount > 1) @@ -89,12 +89,13 @@ bool exportJSON(const FontGeometry *fonts, int fontCount, double fontSize, doubl // Font metrics fputs("\"metrics\":{", f); { + double yFactor = yDirection == YDirection::TOP_DOWN ? -1 : 1; const msdfgen::FontMetrics &metrics = font.getMetrics(); fprintf(f, "\"emSize\":%.17g,", metrics.emSize); fprintf(f, "\"lineHeight\":%.17g,", metrics.lineHeight); - fprintf(f, "\"ascender\":%.17g,", metrics.ascenderY); - fprintf(f, "\"descender\":%.17g,", metrics.descenderY); - fprintf(f, "\"underlineY\":%.17g,", metrics.underlineY); + fprintf(f, "\"ascender\":%.17g,", yFactor*metrics.ascenderY); + fprintf(f, "\"descender\":%.17g,", yFactor*metrics.descenderY); + fprintf(f, "\"underlineY\":%.17g,", yFactor*metrics.underlineY); fprintf(f, "\"underlineThickness\":%.17g", metrics.underlineThickness); } fputs("},", f); @@ -114,11 +115,27 @@ bool exportJSON(const FontGeometry *fonts, int fontCount, double fontSize, doubl fprintf(f, "\"advance\":%.17g", glyph.getAdvance()); double l, b, r, t; glyph.getQuadPlaneBounds(l, b, r, t); - if (l || b || r || t) - fprintf(f, ",\"planeBounds\":{\"left\":%.17g,\"bottom\":%.17g,\"right\":%.17g,\"top\":%.17g}", l, b, r, t); + if (l || b || r || t) { + switch (yDirection) { + case YDirection::BOTTOM_UP: + fprintf(f, ",\"planeBounds\":{\"left\":%.17g,\"bottom\":%.17g,\"right\":%.17g,\"top\":%.17g}", l, b, r, t); + break; + case YDirection::TOP_DOWN: + fprintf(f, ",\"planeBounds\":{\"left\":%.17g,\"top\":%.17g,\"right\":%.17g,\"bottom\":%.17g}", l, -t, r, -b); + break; + } + } glyph.getQuadAtlasBounds(l, b, r, t); - if (l || b || r || t) - fprintf(f, ",\"atlasBounds\":{\"left\":%.17g,\"bottom\":%.17g,\"right\":%.17g,\"top\":%.17g}", l, b, r, t); + if (l || b || r || t) { + switch (yDirection) { + case YDirection::BOTTOM_UP: + fprintf(f, ",\"atlasBounds\":{\"left\":%.17g,\"bottom\":%.17g,\"right\":%.17g,\"top\":%.17g}", l, b, r, t); + break; + case YDirection::TOP_DOWN: + fprintf(f, ",\"atlasBounds\":{\"left\":%.17g,\"top\":%.17g,\"right\":%.17g,\"bottom\":%.17g}", l, atlasHeight-t, r, atlasHeight-b); + break; + } + } fputs("}", f); firstGlyph = false; } fputs("]", f); diff --git a/msdf-atlas-gen/json-export.h b/msdf-atlas-gen/json-export.h index e068ac7..fe7bc04 100644 --- a/msdf-atlas-gen/json-export.h +++ b/msdf-atlas-gen/json-export.h @@ -9,6 +9,6 @@ namespace msdf_atlas { /// Writes the font and glyph metrics and atlas layout data into a comprehensive JSON file -bool exportJSON(const FontGeometry *fonts, int fontCount, double fontSize, double pxRange, int atlasWidth, int atlasHeight, ImageType imageType, const char *filename, bool kerning); +bool exportJSON(const FontGeometry *fonts, int fontCount, double fontSize, double pxRange, int atlasWidth, int atlasHeight, ImageType imageType, YDirection yDirection, const char *filename, bool kerning); } diff --git a/msdf-atlas-gen/main.cpp b/msdf-atlas-gen/main.cpp index 6967117..f22ac03 100644 --- a/msdf-atlas-gen/main.cpp +++ b/msdf-atlas-gen/main.cpp @@ -64,6 +64,8 @@ ATLAS CONFIGURATION -pots / -potr / -square / -square2 / -square4 Picks the minimum atlas dimensions that fit all glyphs and satisfy the selected constraint: power of two square / ... rectangle / any square / square with side divisible by 2 / ... 4 + -yorigin + Determines whether the Y-axis is oriented upwards (bottom origin, default) or downwards (top origin). OUTPUT SPECIFICATION - one or more can be specified -imageout @@ -167,6 +169,7 @@ struct FontInput { struct Configuration { ImageType imageType; ImageFormat imageFormat; + YDirection yDirection; int width, height; double emSize; double pxRange; @@ -196,7 +199,7 @@ static bool makeAtlas(const std::vector &glyphs, const std::vecto bool success = true; if (config.imageFilename) { - if (saveImage(bitmap, config.imageFormat, config.imageFilename)) + if (saveImage(bitmap, config.imageFormat, config.imageFilename, config.yDirection)) puts("Atlas image file saved."); else { success = false; @@ -210,6 +213,7 @@ static bool makeAtlas(const std::vector &glyphs, const std::vecto arfontProps.pxRange = config.pxRange; arfontProps.imageType = config.imageType; arfontProps.imageFormat = config.imageFormat; + arfontProps.yDirection = config.yDirection; if (exportArteryFont(fonts.data(), fonts.size(), bitmap, config.arteryFontFilename, arfontProps)) puts("Artery Font file generated."); else { @@ -230,9 +234,10 @@ int main(int argc, const char * const *argv) { Configuration config = { }; fontInput.glyphIdentifierType = GlyphIdentifierType::UNICODE_CODEPOINT; fontInput.fontScale = -1; - config.kerning = true; config.imageType = ImageType::MSDF; config.imageFormat = ImageFormat::UNSPECIFIED; + config.yDirection = YDirection::BOTTOM_UP; + config.kerning = true; const char *imageFormatName = nullptr; int fixedWidth = -1, fixedHeight = -1; config.preprocessGeometry = ( @@ -412,6 +417,17 @@ int main(int argc, const char * const *argv) { ++argPos; continue; } + ARG_CASE("-yorigin", 1) { + arg = argv[++argPos]; + if (!strcmp(arg, "bottom")) + config.yDirection = YDirection::BOTTOM_UP; + else if (!strcmp(arg, "top")) + config.yDirection = YDirection::TOP_DOWN; + else + ABORT("Invalid Y-axis origin. Use bottom or top."); + ++argPos; + continue; + } ARG_CASE("-size", 1) { double s; if (!(parseDouble(s, argv[++argPos]) && s > 0)) @@ -839,7 +855,7 @@ int main(int argc, const char * const *argv) { } if (config.csvFilename) { - if (exportCSV(fonts.data(), fonts.size(), config.csvFilename)) + if (exportCSV(fonts.data(), fonts.size(), config.width, config.height, config.yDirection, config.csvFilename)) puts("Glyph layout written into CSV file."); else { result = 1; @@ -847,7 +863,7 @@ int main(int argc, const char * const *argv) { } } if (config.jsonFilename) { - if (exportJSON(fonts.data(), fonts.size(), config.emSize, config.pxRange, config.width, config.height, config.imageType, config.jsonFilename, config.kerning)) + if (exportJSON(fonts.data(), fonts.size(), config.emSize, config.pxRange, config.width, config.height, config.imageType, config.yDirection, config.jsonFilename, config.kerning)) puts("Glyph layout and metadata written into JSON file."); else { result = 1; diff --git a/msdf-atlas-gen/types.h b/msdf-atlas-gen/types.h index 613cb7f..27311e7 100644 --- a/msdf-atlas-gen/types.h +++ b/msdf-atlas-gen/types.h @@ -43,4 +43,10 @@ enum class GlyphIdentifierType { UNICODE_CODEPOINT }; +/// Direction of the Y-axis +enum class YDirection { + BOTTOM_UP, + TOP_DOWN +}; + }