Compare commits
3 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
30b6f4fd1a | |
|
|
f7eb7efdad | |
|
|
7e8d34645a |
|
|
@ -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
|
||||
|
|
|
|||
38
README.md
38
README.md
|
|
@ -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>` – 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` – a power-of-two square
|
||||
- `-potr` – 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.*>` – saves the atlas bitmap as a plain image file. Format matches `-format`
|
||||
- `-json <filename.json>` – writes the atlas's layout data as well as other metrics into a structured JSON file
|
||||
- `-csv <filename.csv>` – writes the glyph layout data into a simple CSV file
|
||||
- `-json <filename.json>` – 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>` – 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>` – saves the atlas and its layout data as an [Artery Font](https://github.com/Chlumsky/artery-font-format) file
|
||||
- `-shadronpreview <filename.shadron> <sample text>` – 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` – performs an additional scanline pass to fix the signs of the distances
|
||||
- `-seed <N>` – sets the initial seed for the edge coloring heuristic
|
||||
- `-threads <N>` – sets the number of threads for the parallel computation (0 = auto)
|
||||
- `-yorigin <bottom / top>` – 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
|
||||
|
|
@ -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) { }
|
||||
|
|
|
|||
|
|
@ -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)))
|
||||
|
|
|
|||
|
|
@ -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 &);
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
*/
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
2
msdfgen
2
msdfgen
|
|
@ -1 +1 @@
|
|||
Subproject commit 85e8b3d47b3d1a42e4a5ebda0a24fb1cc2e669e0
|
||||
Subproject commit 03889564a50452fa2e0b0a60973b5057001b391b
|
||||
Loading…
Reference in New Issue