Compare commits

...

3 Commits
v1.3 ... master

Author SHA1 Message Date
Chlumsky 30b6f4fd1a Image format information #128, warning fix #129 2025-05-31 13:17:46 +02:00
Chlumsky f7eb7efdad Readme update 2025-01-27 12:18:50 +01:00
Chlumsky 7e8d34645a FontGeometry constructor fix 2024-11-24 09:39:37 +01:00
13 changed files with 106 additions and 54 deletions

View File

@ -1,6 +1,6 @@
MIT License
Copyright (c) 2020 - 2024 Viktor Chlumsky
Copyright (c) 2020 - 2025 Viktor Chlumsky
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@ -34,7 +34,7 @@ Notes:
This project can be used either as a library or as a standalone console program.
Examples of how to use it as a library are available at the [bottom of the page](#library-usage-examples).
To start using the program immediately, there is a Windows binary available for download in the ["Releases" section](https://github.com/Chlumsky/msdf-atlas-gen/releases).
To start using the program right away, you may download a Windows binary in the ["Releases" section](https://github.com/Chlumsky/msdf-atlas-gen/releases).
To build the project from source, you may use the included [CMake script](CMakeLists.txt).
In its default configuration, it requires [vcpkg](https://vcpkg.io/) as the provider for third-party library dependencies.
If you set the environment variable `VCPKG_ROOT` to the vcpkg directory, the CMake configuration will take care of fetching all required packages from vcpkg.
@ -79,16 +79,22 @@ If no character set or glyph set is provided, and `-allglyphs` is not used, the
- `png` – a compressed PNG image
- `bmp` – an uncompressed BMP image
- `tiff` – an uncompressed floating-point TIFF image
- `rgba` – an uncompressed [RGBA](https://github.com/bzotto/rgba_bitmap) file
- `fl32` – an uncompressed floating-point FL32 file
- `text` – a sequence of pixel values in plain text
- `textfloat` – a sequence of floating-point pixel values in plain text
- `bin` – a sequence of pixel values encoded as raw bytes of data
- `binfloat` – a sequence of pixel values encoded as raw 32-bit floating-point values
- `binfloat` – a sequence of pixel values encoded as raw 32-bit floating-point values (little endian, `binfloatbe` for big endian)
If format is not specified, it may be deduced from the extension of the `-imageout` argument or other clues.
Please note that all color values must be interpreted as if they were linear (not sRGB) like the alpha channel, even if the image format implies otherwise.
### Atlas dimensions
`-dimensions <width> <height>` &ndash; sets fixed atlas dimensions
Alternativelly, the minimum possible dimensions may be selected automatically if a dimensions constraint is set instead:
Alternatively, the minimum possible dimensions may be selected automatically if a dimensions constraint is set instead:
- `-pots` &ndash; a power-of-two square
- `-potr` &ndash; a power-of-two square or rectangle (typically 2:1 aspect ratio)
@ -112,8 +118,23 @@ In that case, these additional options are available to customize the layout:
Any non-empty subset of the following may be specified:
- `-imageout <filename.*>` &ndash; saves the atlas bitmap as a plain image file. Format matches `-format`
- `-json <filename.json>` &ndash; writes the atlas's layout data as well as other metrics into a structured JSON file
- `-csv <filename.csv>` &ndash; writes the glyph layout data into a simple CSV file
- `-json <filename.json>` &ndash; writes the atlas's layout data as well as other metrics into a structured JSON file <details><summary>JSON fields</summary>
- `atlas` section includes the settings used to generate the atlas, including its type and dimensions. The `size` field represents the font size in pixels per em.
- If there are multiple input fonts (`-and` parameter), the remaining data are grouped into `variants`, each representing an input font.
- `metrics` section contains useful font metric values retrieved from the font. All values are in em's.
- `glyphs` is an array of individual glyphs identified by Unicode character index (`unicode`) or glyph index (`index`), depending on whether character set or glyph set mode is used.
- `advance` is the horizontal advance in em's.
- `planeBounds` represents the glyph quad's bounds in em's relative to the baseline and horizontal cursor position.
- `atlasBounds` represents the glyph's bounds in the atlas in pixels.
- If available, `kerning` lists all kerning pairs and their advance adjustment (which needs to be added to the base advance of the first glyph in the pair).
</details>
- `-csv <filename.csv>` &ndash; writes the glyph layout data into a simple CSV file <details><summary>CSV columns</summary>
- If there are multiple input fonts (`-and` parameter), the first column is the font index, otherwise it is skipped.
- Character Unicode value or glyph index, depending on whether character set or glyph set mode is used.
- Horizontal advance in em's.
- The next 4 columns are the glyph quad's bounds in em's relative to the baseline and cursor. Depending on the `-yorigin` setting, this is either *left, bottom, right, top* (bottom-up Y) or *left, top, right, bottom* (top-down Y).
- The last 4 columns the the glyph's bounds in the atlas in pixels. Depending on the `-yorigin` setting, this is either *left, bottom, right, top* (bottom-up Y) or *left, top, right, bottom* (top-down Y).
</details>
- `-arfont <filename.arfont>` &ndash; saves the atlas and its layout data as an [Artery Font](https://github.com/Chlumsky/artery-font-format) file
- `-shadronpreview <filename.shadron> <sample text>` &ndash; generates a [Shadron script](https://www.arteryengine.com/shadron/) that uses the generated atlas to draw a sample text as a preview
@ -140,6 +161,7 @@ Any non-empty subset of the following may be specified:
- `-scanline` &ndash; performs an additional scanline pass to fix the signs of the distances
- `-seed <N>` &ndash; sets the initial seed for the edge coloring heuristic
- `-threads <N>` &ndash; sets the number of threads for the parallel computation (0 = auto)
- `-yorigin <bottom / top>` &ndash; specifies the direction of the Y-axis in output coordinates. The default is bottom-up.
Use `-help` for an exhaustive list of options.
@ -215,7 +237,7 @@ bool generateAtlas(const char *fontFilename) {
ImmediateAtlasGenerator<
float, // pixel type of buffer for individual glyphs depends on generator function
3, // number of atlas color channels
&msdfGenerator, // function to generate bitmaps for individual glyphs
msdfGenerator, // function to generate bitmaps for individual glyphs
BitmapAtlasStorage<byte, 3> // class that stores the atlas bitmap
// For example, a custom atlas storage class that stores it in VRAM can be used.
> generator(width, height);
@ -227,7 +249,7 @@ bool generateAtlas(const char *fontFilename) {
generator.generate(glyphs.data(), glyphs.size());
// The atlas bitmap can now be retrieved via atlasStorage as a BitmapConstRef.
// The glyphs array (or fontGeometry) contains positioning data for typesetting text.
success = myProject::submitAtlasBitmapAndLayout(generator.atlasStorage(), glyphs);
success = my_project::submitAtlasBitmapAndLayout(generator.atlasStorage(), glyphs);
// Cleanup
msdfgen::destroyFont(font);
}
@ -248,7 +270,7 @@ Acquiring the `GlyphGeometry` objects can be adapted from the previous example.
using namespace msdf_atlas;
using MyDynamicAtlas = DynamicAtlas<ImmediateAtlasGenerator<float, 3, &msdfGenerator, BitmapAtlasStorage<byte, 3>>>;
using MyDynamicAtlas = DynamicAtlas<ImmediateAtlasGenerator<float, 3, msdfGenerator, BitmapAtlasStorage<byte, 3>>>;
const double pixelRange = 2.0;
const double glyphScale = 32.0;

@ -1 +1 @@
Subproject commit 888674220216d1d326c6f29cf89165b545279c1f
Subproject commit af79386abe0857fe1c30be97eec760dbd84022c5

View File

@ -1,24 +1,16 @@
#include "DynamicAtlas.h"
namespace msdf_atlas {
#include "utils.hpp"
static int ceilPOT(int x) {
if (x > 0) {
int y = 1;
while (y < x)
y <<= 1;
return y;
}
return 0;
}
namespace msdf_atlas {
template <class AtlasGenerator>
DynamicAtlas<AtlasGenerator>::DynamicAtlas() : side(0), spacing(0), glyphCount(0), totalArea(0) { }
template <class AtlasGenerator>
template <typename... ARGS>
DynamicAtlas<AtlasGenerator>::DynamicAtlas(int minSide, ARGS... args) : side(ceilPOT(minSide)), spacing(0), glyphCount(0), totalArea(0), packer(side+spacing, side+spacing), generator(side, side, args...) { }
DynamicAtlas<AtlasGenerator>::DynamicAtlas(int minSide, ARGS... args) : side(minSide > 0 ? ceilToPOT(minSide) : 0), spacing(0), glyphCount(0), totalArea(0), packer(side+spacing, side+spacing), generator(side, side, args...) { }
template <class AtlasGenerator>
DynamicAtlas<AtlasGenerator>::DynamicAtlas(AtlasGenerator &&generator) : side(0), spacing(0), glyphCount(0), totalArea(0), generator((AtlasGenerator &&) generator) { }

View File

@ -25,9 +25,34 @@ const GlyphGeometry *FontGeometry::GlyphRange::end() const {
return glyphs->data()+rangeEnd;
}
FontGeometry::FontGeometry() : geometryScale(1), metrics(), preferredIdentifierType(GlyphIdentifierType::UNICODE_CODEPOINT), glyphs(&ownGlyphs), rangeStart(glyphs->size()), rangeEnd(glyphs->size()) { }
FontGeometry::FontGeometry() : geometryScale(1), metrics(), preferredIdentifierType(GlyphIdentifierType::UNICODE_CODEPOINT), glyphs(&ownGlyphs), rangeStart(0), rangeEnd(0) { }
FontGeometry::FontGeometry(std::vector<GlyphGeometry> *glyphStorage) : geometryScale(1), metrics(), preferredIdentifierType(GlyphIdentifierType::UNICODE_CODEPOINT), glyphs(glyphStorage), rangeStart(glyphs->size()), rangeEnd(glyphs->size()) { }
FontGeometry::FontGeometry(std::vector<GlyphGeometry> *glyphStorage) : geometryScale(1), metrics(), preferredIdentifierType(GlyphIdentifierType::UNICODE_CODEPOINT) {
glyphs = glyphStorage ? glyphStorage : &ownGlyphs;
rangeStart = glyphs->size();
rangeEnd = glyphs->size();
}
FontGeometry::FontGeometry(FontGeometry &&orig) : geometryScale(orig.geometryScale), metrics(orig.metrics), preferredIdentifierType(orig.preferredIdentifierType), glyphs(orig.glyphs), rangeStart(orig.rangeStart), rangeEnd(orig.rangeEnd), glyphsByIndex((std::map<int, size_t> &&) orig.glyphsByIndex), glyphsByCodepoint((std::map<unicode_t, size_t> &&) orig.glyphsByCodepoint), kerning((std::map<std::pair<int, int>, double> &&) orig.kerning), ownGlyphs((std::vector<GlyphGeometry> &&) orig.ownGlyphs), name((std::string &&) orig.name) {
if (glyphs == &orig.ownGlyphs)
glyphs = &ownGlyphs;
}
FontGeometry &FontGeometry::operator=(FontGeometry &&orig) {
if (this != &orig) {
geometryScale = orig.geometryScale;
metrics = orig.metrics;
glyphs = orig.glyphs == &orig.ownGlyphs ? &ownGlyphs : orig.glyphs;
rangeStart = orig.rangeStart;
rangeEnd = orig.rangeEnd;
glyphsByIndex = (std::map<int, size_t> &&) orig.glyphsByIndex;
glyphsByCodepoint = (std::map<unicode_t, size_t> &&) orig.glyphsByCodepoint;
kerning = (std::map<std::pair<int, int>, double> &&) orig.kerning;
ownGlyphs = (std::vector<GlyphGeometry> &&) orig.ownGlyphs;
name = (std::string &&) orig.name;
}
return *this;
}
int FontGeometry::loadGlyphRange(msdfgen::FontHandle *font, double fontScale, unsigned rangeStart, unsigned rangeEnd, bool preprocessGeometry, bool enableKerning) {
if (!(glyphs->size() == this->rangeEnd && loadMetrics(font, fontScale)))

View File

@ -32,6 +32,8 @@ public:
FontGeometry();
explicit FontGeometry(std::vector<GlyphGeometry> *glyphStorage);
FontGeometry(FontGeometry &&orig);
FontGeometry &operator=(FontGeometry &&orig);
/// Loads the consecutive range of glyphs between rangeStart (inclusive) and rangeEnd (exclusive), returns the number of successfully loaded glyphs
int loadGlyphRange(msdfgen::FontHandle *font, double fontScale, unsigned rangeStart, unsigned rangeEnd, bool preprocessGeometry = true, bool enableKerning = true);
@ -81,6 +83,9 @@ private:
std::vector<GlyphGeometry> ownGlyphs;
std::string name;
FontGeometry(const FontGeometry &);
FontGeometry &operator=(const FontGeometry &);
};
}

View File

@ -2,23 +2,10 @@
#include "GridAtlasPacker.h"
#include <algorithm>
#include "utils.hpp"
namespace msdf_atlas {
static int floorPOT(int x) {
int y = 1;
while (x >= y && y<<1)
y <<= 1;
return y>>1;
}
static int ceilPOT(int x) {
int y = 1;
while (x > y && y<<1)
y <<= 1;
return y;
}
static bool squareConstraint(DimensionsConstraint constraint) {
switch (constraint) {
case DimensionsConstraint::SQUARE:
@ -51,9 +38,9 @@ void GridAtlasPacker::lowerToConstraint(int &width, int &height, DimensionsConst
case DimensionsConstraint::POWER_OF_TWO_RECTANGLE:
case DimensionsConstraint::POWER_OF_TWO_SQUARE:
if (width > 0)
width = floorPOT(width);
width = floorToPOT(width);
if (height > 0)
height = floorPOT(height);
height = floorToPOT(height);
break;
}
}
@ -76,9 +63,9 @@ void GridAtlasPacker::raiseToConstraint(int &width, int &height, DimensionsConst
case DimensionsConstraint::POWER_OF_TWO_RECTANGLE:
case DimensionsConstraint::POWER_OF_TWO_SQUARE:
if (width > 0)
width = ceilPOT(width);
width = ceilToPOT(width);
if (height > 0)
height = ceilPOT(height);
height = ceilToPOT(height);
break;
}
}

View File

@ -8,7 +8,6 @@ namespace msdf_atlas {
/*
* Copies a rectangular section from source bitmap to destination bitmap.
* Width and height are not checked and must not exceed bitmap bounds!
*/
void blit(const msdfgen::BitmapRef<byte, 1> &dst, const msdfgen::BitmapConstRef<byte, 1> &src, int dx, int dy, int sx, int sy, int w, int h);

View File

@ -19,7 +19,7 @@ template <int N>
bool saveImageText(const msdfgen::BitmapConstRef<float, N> &bitmap, const char *filename, YDirection outputYDirection);
template <int N>
bool saveImage(const msdfgen::BitmapConstRef<byte, N> &bitmap, ImageFormat format, const char *filename, YDirection outputYDirection) {
bool saveImage(const msdfgen::BitmapConstRef<byte, N> &bitmap, ImageFormat format, const char *filename, YDirection outputYDirection = YDirection::BOTTOM_UP) {
switch (format) {
#ifndef MSDFGEN_DISABLE_PNG
case ImageFormat::PNG:
@ -48,7 +48,7 @@ bool saveImage(const msdfgen::BitmapConstRef<byte, N> &bitmap, ImageFormat forma
}
template <int N>
bool saveImage(const msdfgen::BitmapConstRef<float, N> &bitmap, ImageFormat format, const char *filename, YDirection outputYDirection) {
bool saveImage(const msdfgen::BitmapConstRef<float, N> &bitmap, ImageFormat format, const char *filename, YDirection outputYDirection = YDirection::BOTTOM_UP) {
switch (format) {
#ifndef MSDFGEN_DISABLE_PNG
case ImageFormat::PNG:

View File

@ -2,7 +2,7 @@
/*
* MULTI-CHANNEL SIGNED DISTANCE FIELD ATLAS GENERATOR - standalone console program
* --------------------------------------------------------------------------------
* A utility by Viktor Chlumsky, (c) 2020 - 2024
* A utility by Viktor Chlumsky, (c) 2020 - 2025
*/
#ifdef MSDF_ATLAS_STANDALONE
@ -451,27 +451,27 @@ int main(int argc, const char *const *argv) {
#endif
if (ARG_IS("bmp"))
config.imageFormat = ImageFormat::BMP;
else if (ARG_IS("tiff"))
else if (ARG_IS("tiff") || ARG_IS("tif"))
config.imageFormat = ImageFormat::TIFF;
else if (ARG_IS("rgba"))
config.imageFormat = ImageFormat::RGBA;
else if (ARG_IS("fl32"))
config.imageFormat = ImageFormat::FL32;
else if (ARG_IS("text"))
else if (ARG_IS("text") || ARG_IS("txt"))
config.imageFormat = ImageFormat::TEXT;
else if (ARG_IS("textfloat"))
else if (ARG_IS("textfloat") || ARG_IS("txtfloat"))
config.imageFormat = ImageFormat::TEXT_FLOAT;
else if (ARG_IS("bin"))
else if (ARG_IS("bin") || ARG_IS("binary"))
config.imageFormat = ImageFormat::BINARY;
else if (ARG_IS("binfloat"))
else if (ARG_IS("binfloat") || ARG_IS("binfloatle"))
config.imageFormat = ImageFormat::BINARY_FLOAT;
else if (ARG_IS("binfloatbe"))
config.imageFormat = ImageFormat::BINARY_FLOAT_BE;
else {
#ifndef MSDFGEN_DISABLE_PNG
ABORT("Invalid image format. Valid formats are: png, bmp, tiff, rgba, fl32, text, textfloat, bin, binfloat");
ABORT("Invalid image format. Valid formats are: png, bmp, tiff, rgba, fl32, text, textfloat, bin, binfloat, binfloatbe");
#else
ABORT("Invalid image format. Valid formats are: bmp, tiff, rgba, fl32, text, textfloat, bin, binfloat");
ABORT("Invalid image format. Valid formats are: bmp, tiff, rgba, fl32, text, textfloat, bin, binfloat, binfloatbe");
#endif
}
imageFormatName = arg;

View File

@ -4,7 +4,7 @@
/*
* MULTI-CHANNEL SIGNED DISTANCE FIELD ATLAS GENERATOR
* ---------------------------------------------------
* A utility by Viktor Chlumsky, (c) 2020 - 2024
* A utility by Viktor Chlumsky, (c) 2020 - 2025
* Generates compact bitmap font atlases using MSDFgen
*/

22
msdf-atlas-gen/utils.hpp Normal file
View File

@ -0,0 +1,22 @@
#pragma once
namespace msdf_atlas {
/// Floor positive integer to nearest lower or equal power of two
inline int floorToPOT(int x) {
int y = 1;
while (x >= y && y<<1)
y <<= 1;
return y>>1;
}
/// Ceil positive integer to nearest higher or equal power of two
inline int ceilToPOT(int x) {
int y = 1;
while (x > y && y<<1)
y <<= 1;
return y;
}
}

@ -1 +1 @@
Subproject commit 85e8b3d47b3d1a42e4a5ebda0a24fb1cc2e669e0
Subproject commit 03889564a50452fa2e0b0a60973b5057001b391b