Giter Club home page Giter Club logo

msdf-atlas-gen's Introduction

Multi-channel signed distance field atlas generator

This is a utility for generating compact font atlases using MSDFgen.

The atlas generator loads a subset of glyphs from a TTF or OTF font file, generates a distance field for each of them, and tightly packs them into an atlas bitmap (example below). The finished atlas and/or its layout metadata can be exported as an Artery Font file, a plain image file, a CSV sheet or a structured JSON file.

Atlas example

A font atlas is typically stored in texture memory and used to draw text in real-time rendering contexts such as video games.

Atlas types

The atlas generator can generate the following six types of atlases.

Hard mask Soft mask SDF PSDF MSDF MTSDF
Hard mask Soft mask SDF PSDF MSDF MTSDF
Channels: 1 (1-bit) 1 1 1 3 4
Anti-aliasing: - Yes Yes Yes Yes Yes
Scalability: - - Yes Yes Yes Yes
Sharp corners: - - - - Yes Yes
Soft effects: - - Yes - - Yes
Hard effects: - - - Yes Yes Yes

Notes:

  • Sharp corners refers to preservation of corner sharpness when upscaled.
  • Soft effects refers to the support of effects that use true distance, such as glows, rounded outlines, or simplified shadows.
  • Hard effects refers to the support of effects that use perpendicular distance, such as mitered outlines or thickness adjustment.

Getting started

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. To start using the program immediately, there is a Windows binary available for download in the "Releases" section. To build the project from source, you may use the included CMake script. In its default configuration, it requires vcpkg 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.

Command line arguments

Use the following command line arguments for the standalone version of the atlas generator.

Input

  • -font <fontfile.ttf/otf> (required) – sets the input font file.
    • Alternatively, use -varfont <fontfile.ttf/otf?var0=value0&var1=value1> to configure a variable font.
  • -charset <charset.txt> – sets the character set. See the syntax specification of charset.txt.
  • -glyphset <glyphset.txt> – sets the set of input glyphs using their indices within the font file. See the syntax specification.
  • -chars / -glyphs <set string> sets the above character / glyph set in-line. See the syntax specification.
  • -allglyphs – sets the set of input glyphs to all glyphs present within the font file.
  • -fontscale <scale> – applies a scaling transformation to the font's glyphs. Mainly to be used to generate multiple sizes in a single atlas, otherwise use -size.
  • -fontname <name> – sets a name for the font that will be stored in certain output files as metadata.
  • -and – separates multiple inputs to be combined into a single atlas.

If no character set or glyph set is provided, and -allglyphs is not used, the ASCII charset will be used.

Bitmap atlas type

-type <type> – see Atlas types

<type> can be one of:

  • hardmask – a non-anti-aliased binary image
  • softmask – an anti-aliased image
  • sdf – a true signed distance field (SDF)
  • psdf – a signed perpendicular distance field (PSDF)
  • msdf (default) – a multi-channel signed distance field (MSDF)
  • mtsdf – a combination of MSDF and true SDF in the alpha channel

Atlas image format

-format <format>

<format> can be one of:

  • png – a compressed PNG image
  • bmp – an uncompressed BMP image
  • tiff – an uncompressed floating-point TIFF image
  • 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

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:

  • -pots – a power-of-two square
  • -potr – a power-of-two square or rectangle (typically 2:1 aspect ratio)
  • -square – any square dimensions
  • -square2 – square with even side length
  • -square4 (default) – square with side length divisible by four

Uniform grid atlas

By default, glyphs in the atlas have different dimensions and are bin-packed in an irregular fashion to maximize use of space. With the -uniformgrid switch, you can instead force all glyphs to have identical dimensions and be laid out in a grid. In that case, these additional options are available to customize the layout:

  • -uniformcols <N> – sets the number of columns
  • -uniformcell <width> <height> – sets the dimensions of the grid's cells
  • -uniformcellconstraint <none / pots / potr / square / square2 / square4> – sets constraint for cell dimensions (see explanation of options above)
  • -uniformorigin <off / on / horizontal / vertical> – sets whether the glyph's origin point should be fixed at the same position in each cell

Outputs

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
  • -arfont <filename.arfont> – saves the atlas and its layout data as an Artery Font file
  • -shadronpreview <filename.shadron> <sample text> – generates a Shadron script that uses the generated atlas to draw a sample text as a preview

Glyph configuration

  • -size <em size> – sets the size of the glyphs in the atlas in pixels per em
  • -minsize <em size> – sets the minimum size. The largest possible size that fits the same atlas dimensions will be used
  • -emrange <em range> – sets the distance field range in em's
  • -pxrange <pixel range> (default = 2) – sets the distance field range in output pixels
  • -aemrange / -apxrange <outermost distance> <innermost distance> – sets the distance field range asymmetrically by specifying the minimum and maximum representable signed distances (outside distances are negative!)
  • -pxalign <off / on / horizontal / vertical> (default = vertical) – enables or disables alignment of glyph's origin point with the pixel grid
  • -empadding / -pxpadding <width> – sets additional padding within each glyph's box (in em's / pixels)
  • -outerempadding / -outerpxpadding <width> – sets additional padding around each glyph's box
  • -aempadding / -apxpadding / -aouterempadding / -aouterpxpadding <left> <bottom> <right> <top> – sets additional padding (see above) asymmetrically with a separate width value for each side

Distance field generator settings

  • -angle <angle> – sets the minimum angle between adjacent edges to be considered a corner. Append D for degrees (msdf / mtsdf only)
  • -coloringstrategy <simple / inktrap / distance> – selects the edge coloring heuristic (msdf / mtsdf only)
  • -errorcorrection <mode> – selects the error correction algorithm. Use help as mode for more information (msdf / mtsdf only)
  • -miterlimit <value> – sets the miter limit that limits the extension of each glyph's bounding box due to very sharp corners (psdf / msdf / mtsdf only)
  • -overlap – switches to distance field generator with support for overlapping contours
  • -nopreprocess – disables path preprocessing which resolves self-intersections and overlapping contours
  • -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)

Use -help for an exhaustive list of options.

Character set specification syntax

The character set file is a text file with UTF-8 or ASCII encoding. The characters can be denoted in the following ways:

  • Single character: 'A' (UTF-8 encoded), 65 (decimal Unicode), 0x41 (hexadecimal Unicode)
  • Range of characters: ['A', 'Z'], [65, 90], [0x41, 0x5a]
  • String of characters: "ABCDEFGHIJKLMNOPQRSTUVWXYZ" (UTF-8 encoded)

The entries should be separated by commas or whitespace. In between quotation marks, backslash is used as the escape character (e.g. '\'', '\\', "!\"#"). The order in which characters appear is not taken into consideration.

Additionally, the include directive can be used to include other charset files and combine character sets in a hierarchical way. It must be written on a separate line:

@include "base-charset.txt"

Glyph set specification

The syntax of the glyph set specification is mostly the same as that of a character set, but only numeric values (decimal and hexadecimal) are allowed.

Library usage examples

Here are commented snippets of code that demonstrate how the project can be used as a library.

Generating whole atlas at once

#include <msdf-atlas-gen/msdf-atlas-gen.h>

using namespace msdf_atlas;

bool generateAtlas(const char *fontFilename) {
    bool success = false;
    // Initialize instance of FreeType library
    if (msdfgen::FreetypeHandle *ft = msdfgen::initializeFreetype()) {
        // Load font file
        if (msdfgen::FontHandle *font = msdfgen::loadFont(ft, fontFilename)) {
            // Storage for glyph geometry and their coordinates in the atlas
            std::vector<GlyphGeometry> glyphs;
            // FontGeometry is a helper class that loads a set of glyphs from a single font.
            // It can also be used to get additional font metrics, kerning information, etc.
            FontGeometry fontGeometry(&glyphs);
            // Load a set of character glyphs:
            // The second argument can be ignored unless you mix different font sizes in one atlas.
            // In the last argument, you can specify a charset other than ASCII.
            // To load specific glyph indices, use loadGlyphs instead.
            fontGeometry.loadCharset(font, 1.0, Charset::ASCII);
            // Apply MSDF edge coloring. See edge-coloring.h for other coloring strategies.
            const double maxCornerAngle = 3.0;
            for (GlyphGeometry &glyph : glyphs)
                glyph.edgeColoring(&msdfgen::edgeColoringInkTrap, maxCornerAngle, 0);
            // TightAtlasPacker class computes the layout of the atlas.
            TightAtlasPacker packer;
            // Set atlas parameters:
            // setDimensions or setDimensionsConstraint to find the best value
            packer.setDimensionsConstraint(TightAtlasPacker::DimensionsConstraint::SQUARE);
            // setScale for a fixed size or setMinimumScale to use the largest that fits
            packer.setMinimumScale(24.0);
            // setPixelRange or setUnitRange
            packer.setPixelRange(2.0);
            packer.setMiterLimit(1.0);
            // Compute atlas layout - pack glyphs
            packer.pack(glyphs.data(), glyphs.size());
            // Get final atlas dimensions
            int width = 0, height = 0;
            packer.getDimensions(width, height);
            // The ImmediateAtlasGenerator class facilitates the generation of the atlas bitmap.
            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
                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);
            // GeneratorAttributes can be modified to change the generator's default settings.
            GeneratorAttributes attributes;
            generator.setAttributes(attributes);
            generator.setThreadCount(4);
            // Generate atlas bitmap
            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);
            // Cleanup
            msdfgen::destroyFont(font);
        }
        msdfgen::deinitializeFreetype(ft);
    }
    return success;
}

Dynamic atlas

The DynamicAtlas class allows you to add glyphs to the atlas "on-the-fly" as they are needed. In this example, the ImmediateAtlasGenerator is used as the underlying atlas generator, which however isn't ideal for this purpose because it may launch new threads every time. In practice, you would typically define your own atlas generator class that properly handles your specific performance and synchronization requirements.

Acquiring the GlyphGeometry objects can be adapted from the previous example.

#include <msdf-atlas-gen/msdf-atlas-gen.h>

using namespace msdf_atlas;

using MyDynamicAtlas = DynamicAtlas<ImmediateAtlasGenerator<float, 3, &msdfGenerator, BitmapAtlasStorage<byte, 3>>>;

const double pixelRange = 2.0;
const double glyphScale = 32.0;
const double miterLimit = 1.0;
const double maxCornerAngle = 3.0;

MyDynamicAtlas atlas;

void addGlyphsToAtlas(GlyphGeometry *glyphs, int count) {
    for (int i = 0; i < count; ++i) {
        // Apply MSDF edge coloring. See edge-coloring.h for other coloring strategies.
        glyphs[i].edgeColoring(&msdfgen::edgeColoringInkTrap, maxCornerAngle, 0);
        // Finalize glyph box size based on the parameters
        glyphs[i].wrapBox(glyphScale, pixelRange/glyphScale, miterLimit);
    }
    // Add glyphs to atlas - invokes the underlying atlas generator
    // Adding multiple glyphs at once may improve packing efficiency.
    MyDynamicAtlas::ChangeFlags change = atlas.add(glyphs, count);
    if (change&MyDynamicAtlas::RESIZED) {
        // Atlas has been enlarged - can be handled here or directly in custom generator class
    }
    // Glyph positioning data is now stored in glyphs.
}

The atlas storage (and its bitmap) can be accessed as dynamicAtlas.atlasGenerator().atlasStorage().

msdf-atlas-gen's People

Contributors

chlumsky avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

msdf-atlas-gen's Issues

Documentation for json, csv output

Is there anywhere I can go to help make sense of the output layout data?

for example, I have this output regarding the character 'A':

    {"unicode":65,"advance":0.63300000000000001,
      "planeBounds":{"left":0.0023802681992336704,"bottom":-0.032253256704980887,"right":0.63011973180076619,"top":0.77125325670498079},
      "atlasBounds":{"left":0.5,"bottom":43.5,"right":25.5,"top":75.5}
    },

unicode is self explanatory,
advance is, I assume, the proportional-to-font-size distance to advance the cursor for the next character
planeBounds I assume are the bounds of the glyph on a [-1,1] plane (for both X and Y)? However, if that were the case, looking at my output image I would expect the bounds for 'A' to be something along the lines of "left":-1,"bottom":-0.7,"right":0.1,"top":-0.5, which it isn't even close to...
atlasBounds I assume would have been similar to planeBounds, but in pixel coordinates?

Any help and I'd happily contribute a blurb to your README!

Thanks for your work!

does this build for linux?

I've been trying to get this to build for ubuntu 20.04 and am failing.

I've

git clone [email protected]:Chlumsky/msdf-atlas-gen
cd msdf-atlas-gen
git submodule init
git submodule update
sudo vcpkg install
mkdir build
cd build
cmake ..

and hit a bunch of errors. I've tried to chase down any missing needed libraries and sudo apt-get install'd them, I've installed vcpkg on my system and set /opt/vcpkg as VCPKG_ROOT in my .bashrc, I've run vcpkg install in the msdf-atlas-gen directory: what am I supposed to be doing that I'm not?

My latest error is that it can't find tinyxml2, even though I see it in the vcpkg_installed directory, and I've sudo apt-get install libtinyxml2-dev'd.

Missing 5 codepoints: 0x30, 0x31, 0x32, 0x33, 0x39

I am trying to generate a sdf using a icon font created in iconmoon.
I get the above message and it does not generate the atlas.

The command I used was
msdf-atlas-gen -type sdf -font .\icons.ttf -json icons_font.json -format png -imageout icons_font.png -charset charset.txt -dimensions 256 256

and the charset.txt uses the text you would use in the css to display the icon.
"\e900","\e901","\e902","\e903"

Any assistance in helping me generate the sdf from this icon font would be greatly appreciated.

Feature Request: Support for multiple fonts in a single atlas

Hello,

I would be interested in having multiple fonts in a single atlas texture. I can probably contribute towards that, but before starting, I would like to know if there are already plans or even maybe work in that direction?

Thank you, kind regards.

Cannot build on m1 macbook, 'tf2build.h' not found

On an m1 macbook, I cloned the repository recursively, and ran make

The following error is printed

msdfgen/ext/import-font.cpp:6:10: fatal error: 'ft2build.h' file not found
#include <ft2build.h>
         ^~~~~~~~~~~~
1 error generated.

Using the cmake GUI, it reports to me:

Could NOT find Freetype (missing: FREETYPE_LIBRARY FREETYPE_INCLUDE_DIRS)

I have tried installing freetype via brew install freetype, and modifying the Makefile to point freetype like so:

-I /opt/homebrew/include/freetype2

This instead produces the error:

ld: library not found for -lfreetype
clang: error: linker command failed with exit code 1 (use -v to see invocation)

I believe macOS should already include the freetype library anyway.

I have successfully built this library on other macOS machines, so I'm not sure if I'm doing something wrong or there is something unique about my setup that is causing this issue.

Question:The padding parameter.

If I understand correctly, the current padding parameter is only between glyph and glyph. However, it doesn't seem to be helpful for applying some special effects. The correct way should be to apply padding when generating glyph's sdf, which means that the padding area should also contain sdf data.

Border /Edge bleeding issue

I have created a MSDF
msdf-atlas-gen-master bastianzuhlke$ ./bin/msdf-atlas-gen -pxrange 0 -font OpenSans-Regular-V2.ttf -charset charset.txt -size 36 -format bin -imageout OpenSans-Regular-V2.raw -json OpenSans-Regular-V2.json -pots -type msdf
When I render the word Lorem I get a dark grey line left from the "L". I can see that in the atlas left to the "L" is the "M".
So I suppose that the shader is fetching texels belonging to the M.

"L"
"atlasBounds": {
"left": 73.5,
"bottom": 95.5,
"right": 90.5,
"top": 123.5
}

"M"
"atlasBounds": {
"left": 44.5,
"bottom": 95.5,
"right": 72.5,
"top": 123.5
}

No MipMap is used.
TEXTURE_MAG_FILTER -> Linear
TEXTURE_MIN_FILTER -> Linear

The atlas is 256x256. To calculate the uvs I am diving the left / bottom / right / top with 256.
In general such border lines appear here and then.

If i am using an atlas generated by msdf-bmfont-xml which offers texturePadding I do not have these artefacts. Unfortunately msdf-bmfont-xml has some flaws why I can not use it.

Any idea to avoid this issue ?

Bottom-alligned text for a top-left aligned display

Hi! I am using this amazing program to generate font atlases for my game.

However, I've been struggling to get the text properly aligned.

The system expects to get 2 coordinates, the top left and bottom right positions, with the top left being the closest one to [0, 0] and the bottom right one being the one closest to [screenWidth, screenHeight]. In other words, the screen uses a Y-down coordinate system.

Do you have any tips and/or tricks on how to get it working?

So far I've mangaged to cook up the following with the hope of at least getting something working, however it doesn't feel like the base line is straight, and certain characters are heavily displaced(e.g comma)

FontCharacter meta = Meta.Characters[glyph];


Vector2 uvStart = new Vector2(meta.AtlasBounds.Left, meta.AtlasBounds.Top) / new Vector2(Meta.AtlasMetrics.Width, Meta.AtlasMetrics.Height);
Vector2 uvEnd = new Vector2(meta.AtlasBounds.Right, meta.AtlasBounds.Bottom) / new Vector2(Meta.AtlasMetrics.Width, Meta.AtlasMetrics.Height);
            
			
Vector2 screenStart = new Vector2(meta.PlaneBounds.Left, Meta.Metrics.Ascender + meta.PlaneBounds.Bottom) * size;
Vector2 screenEnd = new Vector2(meta.PlaneBounds.Right, Meta.Metrics.Ascender + meta.PlaneBounds.Top) * size;

drawCallback(uvStart, uvEnd, screenStart, screenEnd, offset);

offset.X += meta.Advance * size;

Generated SDF looks too sharp?

When I run this command:

$ .\msdf-atlas-gen.exe -font Dangrek-Regular.ttf -size 128 -type sdf -imageout dangrek.png -json dangrek.json

Loaded geometry of 95 out of 95 glyphs.
Atlas dimensions: 668 x 668
Atlas image file saved.
Glyph layout and metadata written into JSON file.

I get this output image:
dangrek

Looks to sharp to me compared to the images you see in the documentation:

A

When I change the output type to msdf, this is what I get:

dangrek

So not sure if I'm doing something wrong, or that's just the way it's supposed to be.

No Kerning from FreeType

Hi Viktor, awesome work on these libs and the idea behind them!

I've been working with it over the last couple days and can't seem to get any kerning results, the list in e.g. a converted json output is always empty. I dug in and it seems the FT_Get_Kerning() call in msdfgen always yields zero kerning. I tried updating FT to their latest version as well, to no success. Any clues why this is the case? Wasn't able to root-cause it.

Cheers and all the best!

License / Can I distribute this?

Hey, I've been using MSDF for a while now and really like it, thanks for sharing your great projects with the world!

I'm working on a UI package for Unity and since I'm unhappy with Unity's fonts I wanted to ship with MSDF instead. Are there any restrictions that prevent me from distributing my package that bundles msdf and the generator within it to customers?

Export all glyphs instead of user-defined subset

The atlas generator loads a subset of glyphs from a TTF or OTF font file

that's nice, but I'm not picky and just want access to all glyphs. is there a way to do this?

already tried charset.txt with [0, 1114111] in it but the resulting image looked completely broken

Feature request: parameterize min glyph spacing

i'm using mtsdf and drawing outlines around my text, but the outlines from adjacent characters is bleeding in very slightly around the borders of some of my characters. if there were a way to set the glyphs to not be packed so tight (ie "leave an additional 2px border around each character") I would expect that could solve it!

Questions about how small can I really get away with

Hey, I'm a massive fan of your work.

I am building a web map engine and I originally used a loop + blinn approach where I prep the vertices with webworkers and create SDFs via the GPU, but the complexity was high, I wasn't supporting all glyph types well, and the cost on the webworkers was too much considering I needed to prep lots of other data for the GPU.

Long story short I switched to MSDF, created a NodeJS wrapper of your work and manage the glyphs in my own way to create some stunning results:

Screen Shot 2021-07-07 at 05 44 24

Screen Shot 2021-07-07 at 05 45 24

Comparatively, a high enough resolution MSDF has really sharp edges and asian glyphs look absolutely gorgeous. I like to use roughly 22px size per glyph with 6px range for the best results (I like large ranges for nice large stroke effects around the glyph if necessary).

Here's what 16px with 4px range looks like (all Roboto medium+regular fonts)
artifacts

I noticed the lower I go, obvious artifacts start showing up.

Just curious if there is anything I can do to get the glyphs smaller without sacrificing the quality of the glyphs? If you zoom in on this last picture you will notice it starts getting really confused on where the edges really exist.

Since the browser fetches chunks of glyphs at a time in this scenario, obviously the smaller the better.
Any advice would be appreciated.

EDIT - I just noticed you did some work in May, do you think this could have a substantial effect on the results?

Feature request: Add padding parameter

Subj, usually texture packer apps accept padding param between symbols so shader is easier to code, simple clamp() is not enough if I draw want to draw the letter with spacing, for example when i want to scale it

Thanks for awesome lib btw!

Error while building with Makefile

Hello! I'm interested in using this library for a project, and so I decided I'd clone and test it out to get a feel for its output etc., but I ran into an error; I ran the following commands (approximately):

$ git clone --recurse-submodules -j8 https://github.com/Chlumsky/msdf-atlas-gen/
$ cd msdf-atlas-gen
$ make

Here's the output:

❯ make
mkdir -p bin
g++ -I /usr/local/include/freetype2 -I /usr/include/freetype2 -I artery-font-format -I msdfgen/include -I msdfgen -D MSDFGEN_USE_CPP11 -D MSDF_ATLAS_STANDALONE -std=c++11 -pthread -O2 -o bin/msdf-atlas-gen msdfgen/core/*.cpp msdfgen/lib/*.cpp msdfgen/ext/*.cpp msdf-atlas-gen/*.cpp -lfreetype
msdf-atlas-gen/TightAtlasPacker.cpp:72:54: warning: multiple unsequenced modifications to 'maxScale' [-Wunsequenced]
        while (maxScale < 1e+32 && TRY_PACK(maxScale = 2*minScale))
                                                     ^
msdf-atlas-gen/TightAtlasPacker.cpp:69:116: note: expanded from macro 'TRY_PACK'
    #define TRY_PACK(scale) (lastResult = !tryPack(glyphs, count, DimensionsConstraint(), width, height, padding, (scale), unitRange+pxRange/(scale), miterLimit))
                                                                                                                   ^                          ~~~~~
msdf-atlas-gen/TightAtlasPacker.cpp:75:55: warning: multiple unsequenced modifications to 'minScale' [-Wunsequenced]
        while (minScale > 1e-32 && !TRY_PACK(minScale = .5*maxScale))
                                                      ^
msdf-atlas-gen/TightAtlasPacker.cpp:69:116: note: expanded from macro 'TRY_PACK'
    #define TRY_PACK(scale) (lastResult = !tryPack(glyphs, count, DimensionsConstraint(), width, height, padding, (scale), unitRange+pxRange/(scale), miterLimit))
                                                                                                                   ^                          ~~~~~
2 warnings generated.
In file included from msdf-atlas-gen/main.cpp:20:
In file included from msdf-atlas-gen/msdf-atlas-gen.h:29:
In file included from msdf-atlas-gen/BitmapAtlasStorage.h:33:
msdf-atlas-gen/BitmapAtlasStorage.hpp:51:37: error: non-member function cannot have '&&' qualifier
    return (msdfgen::Bitmap<T, N>() &&) bitmap;
                                    ^~
1 error generated.
make: *** [all] Error 1

Any idea what's causing that error? Here's the output of g++ --version in case it's helpful:

❯ g++ --version
Configured with: --prefix=/Applications/Xcode.app/Contents/Developer/usr --with-gxx-include-dir=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/c++/4.2.1
Apple clang version 12.0.5 (clang-1205.0.22.9)
Target: arm64-apple-darwin20.3.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin

As you can see I'm using Apple's g++ binary on an Apple M1, but I don't know if that's the issue or if it's something else?

Thanks!

SVG support

Would appreciate support for SVG file shapes. This is supported in msdfgen, so why not here too?

Half-pixel padding on atlasBounds

I've noticed that all the fonts I've tested with so far have a value of 0.5 added to each entry in the atlasBounds structure, for example on this 1-character 16px atlas I used to test with:

"atlasBounds": { "left": 0.5, "bottom": 1.5, "right": 14.5, "top": 15.5 }

Is it safe to just truncate these values? In all your example code and others example code, the 0.5 is just ignored, so I am curious as to why it's added in the first place?

EDIT: Ah is it to force the GPU to chose the correct pixel when calculating UVs?

Great tool by the way, and really excellent paper as well.

What are planeBounds and advance

Hello, Chlumsky, I am trying to render such letters as 'g', comma, or apostrophe right. How can I use the info about planeBounds and advance to do it? I've looked at freetype but it has different notations

iOS, Android support

Hello there,

Can we integrate msdf-atlas-gen on the mobile apps and generate msdf type atlas on mobile for drawing text in real-time?

Thank You for Your hard work.

Sanitizer errors

Hello,

I built the program with sanitizers enabled (-fsanitize=address,undefined). Running the program with charset file containing cyrillic letters

['А', 'Я']

trips ubsan

; ./msdf-atlas-gen -imageout Cantarell-Regular.png -json Cantarell-Regular.json -font Cantarell-Regular.ttf -charset a.txt 
Neither atlas size nor glyph size selected, using default...
msdf-atlas-gen/utf8.cpp:22:32: runtime error: left shift of negative value -48
Failed to load character set specification.

which corresponds to

for (block = 0; (*c<<block)&0x40 && block < 4; ++block);

Casting to unsigned char before shifting appears to fix the issue.

Correct way to parse and draw glyph data

Hi,
I have exported the font data(CSV format) and texture into external files and using the code below to build vertex buffers but was not able to calculate offset_x and offset_y. Here is the code, please let me know if anyone finds any other mistake in the code.

typedef struct Glyph
{
unsigned int code;
float advance;
float pleft; // plane left
float pright; // plane right
float ptop; // plane top
float pbottom; //plane left
float aleft; // atlasleft
float aright; // atlasright
float atop; //atlastop
float  abottom; //atlasbottom
float kerning; // Not supported right now
} Glyph;

// font->gdata is std::vector<Glyph> where are the glyph info is stored

void add_text_msdf(FTGL *font, VertexBuffer *vb, const char* text, float x, float y)
{
   // Number of glyphs in text
    size_t num_glyph = 0;
   // Position to draw
    int xpos = x;
    int ypos = y;
      
    while (*text) { 
	    if (*text >= 32 && *text < 128) {
            // Calculate kerning if supported
            /*
            float kerning = 0.0f;
            if( num_glyph > 0) {
                kerning = font->gdata[*text-32-1].kerning - font->gdata[*text-32].kerning;
            }
            xpos += kerning;
            */

            // Width and Height of Glyph;
            float width = font->gdata[*text-32].aright - font->gdata[*text-32].aleft;
            float height = font->gdata[*text-32].abottom - font->gdata[*text-32].atop; 
	        
            // Quad Coordinates
            float x0  = xpos + ?; // How to calculate offset_x
            float y0  = ypos + ?; // How to calculate offset_y
            float x1  = x0 + width ;
            float y1  = y0 - height;
            
            // UV Coordinates
            float s0 = font->gdata[*text-32].aleft / 512.0; // atlas width
            float t0 = font->gdata[*text-32].atop / 512.0; // atlas height
            float s1 = width / 512.0; // atlas width
            float t1 = height / 512.0; // atlas height
            
            GLuint ioff = num_glyph * 4;
        
            GLuint indices[] = {0+ioff, 1+ioff, 3+ioff, // first triangle
                                1+ioff, 2+ioff, 3+ioff}; // Second triangle
                            
            GLfloat vertices[] = {
                x0, y0, 0,  s0, t0,  // top left
                x0, y1, 0,  s0, t1, // top right
                x1, y1, 0,  s1, t1, // bottom right
                x1, y0, 0,  s1, t0 }; // bottom left
            
            vb->push_back(vertices, 20, indices, 6);
            xpos += font->gdata[*text-32].advance * 20.0; // Multiply by font size, am I correct?
            num_glyph++;
	    }
    ++text;
	}
}
//

Feature request: extra margin between glyphs

Hi,

I'm using your wonderful tool to create my fonts, but since the margin between the glyphs is only 1, some glyphs at some font sizes are getting artifacts from the other glyphs. Can you please add a parameter to define an extra margin around the glyphs?

Kind regards

guich

Feature Request: overflow atlas into new atlases

Hello there,

I would like to suggest a new feature to atlas generation.

It seems to be pretty common to pack a font across several textures whenever the glyphs can't fit into the desired texture size.

This is especially useful in contexts like mobile gaming and huge character sets where glyphs may overflow a single 2048x2048 atlas.

Cheers! Thanks for the relevant work in distance fields generation!

Rework charset parser

Currently, the charset parser is implemented in Charset::load and can only work with a FILE * handle. It would be much better if it was possible to parse the data from other sources, such as a string in memory. The function could be reimplemented in the following way to allow for various implementations of reading input and outputting the parsed values.

template <int (*READ)(void *, int, void *), void (*ADD)(unsigned, void *)>
bool parseCharset(void *userData, bool disableCharLiterals = false);

API Clarifications

A few things came up for me when trying to use this package and I was hoping someone could lend a hand. I'm using the JSON output and am confused about the planeBounds and atlasBounds fields. I think atlas bounds are the pixel space coordinates in the texture for a given glyph and I can calculate uvs using atlasBounds divided by atlas dimensions. Is this correct?

Secondly I have no idea what planeBounds is for, I was looking for a description in the docs but didn't see anything. Could you give me a quick explaination?

Lastly, I'm trying to figure out the relationship between emRange, pixelRange, and size in ems. What I'd like to do is generate a font at a given point size but after tinkering a while I didn't figure out how to do this, could you please point me in the right direction?

Thanks! By the way I'm really impressed with the atlas generation performance, Unity's TextMeshPro takes up to 30 seconds to build an atlas but msdf-atlas-gen is around 100ms for the same font.

Feature Request: CSV + JSON format

I like CSV for keeping the glyph data as it's much more space-efficient than json, however this means the font data (atlas and metrics fields) gets discarded. It'd be nice to be able to output both a CSV with the glyph data, and a JSON file with the metrics. Or maybe two csv files?

Msdfgen includes don't contain `msdfgen` folder

Hi,

I noticed that msdfgen includes, such as this one are like <msdfgen.h> and not <msdfgen/msdfgen.h>.

If I am not mistaken, the msdfgen cmake does nto provide access to this kind of include (inferred from this line but I am no really good with cmake so I might have missed something). It seems to work here because of how git submodules work and the manual -I msdfgen in the makefile.

Would you be interested in a PR to change that ? I actually need it to integrate this repo to conan package system, but it can be done externally so this modification is not necessary on my side, but I figured you might want to have it here too.

Padding

Make is so you can specify padding between glyphs in the atlas

Pull request that adds this feature:
#80

set inverseYAxis of Shape

When generating the atlas with some fonts, I need to set inverseYAxis to true, but I cannot find a way to do this without removing const from GlyphGeometry's getShape() function. My intuition tells me, that there is a good reason for const here, and it shouldn't just be removed. Another option would be to const_cast the shape. This also feels weird, but has the advantage that nothing would have to be changed in this repository.

To clarify, here how I do it:

[...]
            // Storage for glyph geometry and their coordinates in the atlas
            std::vector <msdf_atlas::GlyphGeometry> glyphs;
[...]
            for(msdf_atlas::GlyphGeometry & glyph : glyphs){
                glyph.getShape().inverseYAxis = true; // only works if we remove const from GlyphGeometry::getShape()
                
                // or we do a const_cast
                msdfgen::Shape & shape = const_cast <msdfgen::Shape &>(glyph.getShape());
                shape.inverseYAxis = true;

                glyph.edgeColoring(&msdfgen::edgeColoringInkTrap,
                                   3.0, // max corner angle
                                   0); // seed
            }
[...]

Is there a better way? If not, what about either an additional non-const function for getShape, or an additional inverseYAxis parameter when loading the glyphs in FontGeometry::loadCharset and GlyphGeometry::load?

I assume I am not the only one running into this, but if you think const_casting is fine, feel free to simply close this issue. Otherwise I'm happy to send you a PR, or, if there is a better way to do this I would greatly appreciate a pointer in the right direction.

Regression/difference between output from latest version and 1.2

Same font, same command line, but now the font draws bigger in my application and also seems misaligned on the vertical axis.

Top image is how it was, bottom image is how it is.

Command line is:

msdf-atlas-gen.exe -font FontAwesome.otf -fontname FontAwesome -and -font Aileron-Regular.otf -fontname Aileron-Regular -and -font Aileron-SemiBold.otf -fontname Aileron-SemiBold -charset CharSet.txt -yorigin top -minsize 64 -pxrange 16 -nokerning -imageout FontAtlas.png -json FontAtlas.json > FontAtlas.log 2>&1

Is it expected to be this different between versions?

Screenshot 2023-11-09 at 12 15 17 Screenshot 2023-11-09 at 12 15 49

How do I output all glyphs, but with the "unicode" field included in the JSON where appropriate?

Loving the whole system by the way, been using this for years.

I have been using a CharSet.txt consisting of basically 0x0000 -> 0x10ffff.

I realised though that this misses out glyph index 0, which is ".notdef".

So I thought, ok, I should generate using GlyphSet.txt. However, although this seems to work, in that it outputs the glyphs in the range, I noticed the entries in the JSON now don't have the "unicode" field.

So how can I get it to output all glyphs in a font (surely a common requirement?) but include their unicodes, or say, zero, or some undefined value if they don't have a unicode value (such as .notdef amongst some others)?

Packaging in conan

Thanks for this tool.

Would you be interested in packaging it for conan ? I'm using conan as my package manager for C++, and it would be nice to have access to msdf-atlas-gen there, for easy integration in build systems. Also conan-center provides prebuilt binaries of packages for a lot of different configurations which is handy.
msdfgen is already packaged for example.

I think this might need a small rework of the build script, for example a move to CMake.

Pack glyph atlas into multiple textures as texture 2D array

Hi!
Sorry for bad English skill.

I want to pack glyph atlas into multiple textures as texture 2D array.
So I try to add a new member "int textureIndex" into class "GlyphGeometry", and I also try to add the parameter "textureIndex" to "TightAtlasPacker::pack()".
I desire it finally output the attribute "textureIndex" of glyphs into JSON file and load it to render text using texture 2D array.

But "config.emSize" and "config.pxRange" seem to have depended on value of "atlasPacker.getScale()" and "atlasPacker.getPixelRange()" in "main.cpp".
I don't know what should I do if it has multiple value of "atlasPacker"(that means we have multiple value of "atlasPacker.getScale()" ) after I added a loop to generate multiple bitmaps.
And I am not sure I am on the right track or not.

Does anyone can provide me some advice?

Thanks.

Feature Request: Support Y origin top

I'm trying to integrate this awesome tool into the game engine I'm making. I found that in the output JSON file, there is an attribute of "atlas" listed as "yOrigin": "bottom", and it seems like this is hardcoded in the original C++ code. It would be better if the tool could receive the Y origin direction as a command-line parameter since the graphics API I'm working with has the top side of the texture as the Y origin and this gives users enough flexibility.

json export does not respect charset

Hi,

When exporting to json all the characters are included even if charset file supplied. I don't think this should be the case.

Thanks for this tool, its great!

James

License?

Currently this project does not have a license, which means that it cannot be redistributed in any way. Can you please include a license file with the project to make the terms explicit?

I expect that this is a simple omission, since msdfgen itself is already licensed under MIT.

Pixel-perfect positioning of hardmask & softmask atlas

In hardmask and softmask atlas -types, there are no special considerations to ensure that the glyph quads can be placed exactly onto the pixel grid. Instead, the plane bounds dictate that each glyph can be shifted by a different fractional pixel amount. This should be resolved.

The object or library file ... was created by a different version of the compiler than objects like ... error.

Hi again, I'm sure this isn't directly related because of your library, but I noticed it only happens when trying to use it in Release build. So I compiled the game from source and included it in my project. I can run the example on Debug and it compiles completely fine and runs exactly how I expected, which is great. My issue comes in with compiling to Release (yes I am using the Release Library compiled files from both msdf-atlas-gen and msdfgen). I get this weird error:

image

I have two projects that you can see in this screenshot, I have cosmic-engine-engine which is the backend engine which does use msdf-atlas-gen, and I have cosmic-engine-editor, which includes both the libraries and the engine. I can compile and run Debug but I can't with Release (because of this error). Now I know it's something having to do with the Release build of the library as if I were to comment out all the code relating to msdf-atlas-gen/msdfgen, both can compile and run, but with the example code, only Debug can run, Release has compile errors. I didn't change anything about the solution for msdf-atlas-gen except I believe I did change the runtime library used so that my engine could even use the libraries. I'll keep looking into it, maybe I have some mismatch going on, and I did remove the one preprocessor definition that you explained in my last issue.

I'll hopefully be able to solve this before you even reply, maybe I just accidentally changed something, but so far everything seems to be unchanged from the source code except for the runtime library, which again, needed to be changed for the sake of being able to link it.

And no, I only have vs2022 installed with it's own compiler, and I opened up your code via the same vs2022 version, as well as set the toolset and c++ version to the same of my engine/editor.

Edit*: Doesn't happen on the engine project version on Release build, however the engine project is just a static library and I've been told some linking errors may not occur in this type of build process.

Empty kerning array in json

The json contains and empty array for kerning.

Command: ./msdf-atlas-gen -font Montserrat-SemiBold.ttf -json Montserrat.json -imageout Montserrat.json.png -type mtsdf
(The double extension on -imageout is just how the engine handles fonts)

Font used: Montserrat SemiBold (ttf)
Platform: Fedora Linux x86_64

where is the width and height information at?

first of all, i want to thank you for making this great technique for free! love it so far!

thanks man!

anyway, im using json as an output :

{ "atlas": { "type": "msdf", "distanceRange": 2, "size": 48, "width": 64, "height": 64, "yOrigin": "bottom" }, "name": "raleway_regular", "metrics": { "emSize": 1, "lineHeight": 1.1739999999999999, "ascender": 0.94000000000000006, "descender": -0.23400000000000001, "underlineY": -0.10000000000000001, "underlineThickness": 0.050000000000000003 }, "glyphs": [ { "unicode": 65, "advance": 0.67900000000000005, "planeBounds": { "left": -0.013666666666666617, "bottom": -0.030416666666666609, "right": 0.69466666666666677, "top": 0.74041666666666672 }, "atlasBounds": { "left": 0.5, "bottom": 26.5, "right": 34.5, "top": 63.5 } }, { "unicode": 66, "advance": 0.66800000000000004, "planeBounds": { "left": 0.064833333333333368, "bottom": -0.030416666666666609, "right": 0.64816666666666678, "top": 0.74041666666666672 }, "atlasBounds": { "left": 35.5, "bottom": 26.5, "right": 63.5, "top": 63.5 } } ], "kerning": [ { "unicode1": 66, "unicode2": 65, "advance": -0.014 } ] }

i couldnt find the width and height information for each of Glyphs?
also i tried to parse the information using freetype in my project.
im using 48 as pixel size. i got this results

1

why does it produce difference results? what am i doing wrong?
im using this command line

msdf-atlas-gen.exe -font raleway_regular.ttf -fontname raleway_regular -type msdf -format png -size 48 -pxrange 2 -charset charset.txt -imageout test.png -json font.json -csv font.csv

Compiling error.

I'm trying to link this to my project, and I seem to only have one compiling error at the moment, and it's '&' requires I-value at ImmediateAtlasGenerator.hpp at line 39 on the most recent source code download.

Is this simply a linking error? Just as an FYI, I use C++ 20 on the msvc build compiler.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.