Add Text rendering

- Add FreeType as dependency
- Add MsdfGen as dependency
- Add MsdfAtlasGen as dependency
This commit is contained in:
Thraix
2023-05-08 22:12:19 +02:00
parent ad69293faa
commit 4e466a1fce
190 changed files with 44693 additions and 46 deletions
@@ -0,0 +1,42 @@
#pragma once
#include <msdfgen.h>
#include "Remap.h"
#include "GlyphGeometry.h"
namespace msdf_atlas {
namespace {
/** Prototype of an atlas generator class.
* An atlas generator maintains the atlas bitmap (AtlasStorage) and its layout and facilitates
* generation of bitmap representation of glyphs. The layout of the atlas is given by the caller.
*/
class AtlasGenerator {
public:
AtlasGenerator();
AtlasGenerator(int width, int height);
/// Generates bitmap representation for the supplied array of glyphs
void generate(const GlyphGeometry *glyphs, int count);
/// Resizes the atlas and rearranges the generated pixels according to the remapping array
void rearrange(int width, int height, const Remap *remapping, int count);
/// Resizes the atlas and keeps the generated pixels in place
void resize(int width, int height);
};
}
/// Configuration of signed distance field generator
struct GeneratorAttributes {
msdfgen::MSDFGeneratorConfig config;
bool scanlinePass = false;
};
/// A function that generates the bitmap for a single glyph
template <typename T, int N>
using GeneratorFunction = void (*)(const msdfgen::BitmapRef<T, N> &, const GlyphGeometry &, const GeneratorAttributes &);
}
@@ -0,0 +1,37 @@
#pragma once
#include <msdfgen.h>
#include "Remap.h"
namespace msdf_atlas {
namespace {
/** Prototype of an atlas storage class.
* An atlas storage physically holds the pixels of the atlas
* and allows to read and write subsections represented as bitmaps.
* Can be implemented using a simple bitmap (BitmapAtlasStorage),
* as texture memory, or any other way.
*/
class AtlasStorage {
public:
AtlasStorage();
AtlasStorage(int width, int height);
/// Creates a copy with different dimensions
AtlasStorage(const AtlasStorage &orig, int width, int height);
/// Creates a copy with different dimensions and rearranges the pixels according to the remapping array
AtlasStorage(const AtlasStorage &orig, int width, int height, const Remap *remapping, int count);
/// Stores a subsection at x, y into the atlas storage. May be implemented for only some T, N
template <typename T, int N>
void put(int x, int y, const msdfgen::BitmapConstRef<T, N> &subBitmap);
/// Retrieves a subsection at x, y from the atlas storage. May be implemented for only some T, N
template <typename T, int N>
void get(int x, int y, const msdfgen::BitmapRef<T, N> &subBitmap) const;
};
}
}
@@ -0,0 +1,33 @@
#pragma once
#include "AtlasStorage.h"
namespace msdf_atlas {
/// An implementation of AtlasStorage represented by a bitmap in memory (msdfgen::Bitmap)
template <typename T, int N>
class BitmapAtlasStorage {
public:
BitmapAtlasStorage();
BitmapAtlasStorage(int width, int height);
explicit BitmapAtlasStorage(const msdfgen::BitmapConstRef<T, N> &bitmap);
explicit BitmapAtlasStorage(msdfgen::Bitmap<T, N> &&bitmap);
BitmapAtlasStorage(const BitmapAtlasStorage<T, N> &orig, int width, int height);
BitmapAtlasStorage(const BitmapAtlasStorage<T, N> &orig, int width, int height, const Remap *remapping, int count);
operator msdfgen::BitmapConstRef<T, N>() const;
operator msdfgen::BitmapRef<T, N>();
operator msdfgen::Bitmap<T, N>() &&;
template <typename S>
void put(int x, int y, const msdfgen::BitmapConstRef<S, N> &subBitmap);
void get(int x, int y, const msdfgen::BitmapRef<T, N> &subBitmap) const;
private:
msdfgen::Bitmap<T, N> bitmap;
};
}
#include "BitmapAtlasStorage.hpp"
@@ -0,0 +1,65 @@
#include "BitmapAtlasStorage.h"
#include <cstring>
#include <algorithm>
#include "bitmap-blit.h"
namespace msdf_atlas {
template <typename T, int N>
BitmapAtlasStorage<T, N>::BitmapAtlasStorage() { }
template <typename T, int N>
BitmapAtlasStorage<T, N>::BitmapAtlasStorage(int width, int height) : bitmap(width, height) {
memset((T *) bitmap, 0, sizeof(T)*N*width*height);
}
template <typename T, int N>
BitmapAtlasStorage<T, N>::BitmapAtlasStorage(const msdfgen::BitmapConstRef<T, N> &bitmap) : bitmap(bitmap) { }
template <typename T, int N>
BitmapAtlasStorage<T, N>::BitmapAtlasStorage(msdfgen::Bitmap<T, N> &&bitmap) : bitmap((msdfgen::Bitmap<T, N> &&) bitmap) { }
template <typename T, int N>
BitmapAtlasStorage<T, N>::BitmapAtlasStorage(const BitmapAtlasStorage<T, N> &orig, int width, int height) : bitmap(width, height) {
memset((T *) bitmap, 0, sizeof(T)*N*width*height);
blit(bitmap, orig.bitmap, 0, 0, 0, 0, std::min(width, orig.bitmap.width()), std::min(height, orig.bitmap.height()));
}
template <typename T, int N>
BitmapAtlasStorage<T, N>::BitmapAtlasStorage(const BitmapAtlasStorage<T, N> &orig, int width, int height, const Remap *remapping, int count) : bitmap(width, height) {
memset((T *) bitmap, 0, sizeof(T)*N*width*height);
for (int i = 0; i < count; ++i) {
const Remap &remap = remapping[i];
blit(bitmap, orig.bitmap, remap.target.x, remap.target.y, remap.source.x, remap.source.y, remap.width, remap.height);
}
}
template <typename T, int N>
BitmapAtlasStorage<T, N>::operator msdfgen::BitmapConstRef<T, N>() const {
return bitmap;
}
template <typename T, int N>
BitmapAtlasStorage<T, N>::operator msdfgen::BitmapRef<T, N>() {
return bitmap;
}
template <typename T, int N>
BitmapAtlasStorage<T, N>::operator msdfgen::Bitmap<T, N>() && {
return (msdfgen::Bitmap<T, N> &&) bitmap;
}
template <typename T, int N>
template <typename S>
void BitmapAtlasStorage<T, N>::put(int x, int y, const msdfgen::BitmapConstRef<S, N> &subBitmap) {
blit(bitmap, subBitmap, x, y, 0, 0, subBitmap.width, subBitmap.height);
}
template <typename T, int N>
void BitmapAtlasStorage<T, N>::get(int x, int y, const msdfgen::BitmapRef<T, N> &subBitmap) const {
blit(subBitmap, bitmap, 0, 0, x, y, subBitmap.width, subBitmap.height);
}
}
@@ -0,0 +1,35 @@
#pragma once
#include <cstdlib>
#include <set>
#include "types.h"
namespace msdf_atlas {
/// Represents a set of Unicode codepoints (characters)
class Charset {
public:
/// The set of the 95 printable ASCII characters
static MSDF_ATLAS_PUBLIC const Charset ASCII;
/// Adds a codepoint
void add(unicode_t cp);
/// Removes a codepoint
void remove(unicode_t cp);
size_t size() const;
bool empty() const;
std::set<unicode_t>::const_iterator begin() const;
std::set<unicode_t>::const_iterator end() const;
/// Load character set from a text file with the correct syntax
bool load(const char *filename, bool disableCharLiterals = false);
private:
std::set<unicode_t> codepoints;
};
}
@@ -0,0 +1,49 @@
#pragma once
#include <vector>
#include "RectanglePacker.h"
#include "AtlasGenerator.h"
namespace msdf_atlas {
/**
* This class can be used to produce a dynamic atlas to which more glyphs are added over time.
* It takes care of laying out and enlarging the atlas as necessary and delegates the actual work
* to the specified AtlasGenerator, which may e.g. do the work asynchronously.
*/
template <class AtlasGenerator>
class DynamicAtlas {
public:
enum ChangeFlag {
NO_CHANGE = 0x00,
RESIZED = 0x01,
REARRANGED = 0x02
};
typedef int ChangeFlags;
DynamicAtlas();
/// Creates with a configured generator. The generator must not contain any prior glyphs!
explicit DynamicAtlas(AtlasGenerator &&generator);
/// Adds a batch of glyphs. Adding more than one glyph at a time may improve packing efficiency
ChangeFlags add(GlyphGeometry *glyphs, int count, bool allowRearrange = false);
/// Allows access to generator. Do not add glyphs to the generator directly!
AtlasGenerator & atlasGenerator();
const AtlasGenerator & atlasGenerator() const;
private:
AtlasGenerator generator;
RectanglePacker packer;
int glyphCount;
int side;
std::vector<Rectangle> rectangles;
std::vector<Remap> remapBuffer;
int totalArea;
int padding;
};
}
#include "DynamicAtlas.hpp"
@@ -0,0 +1,78 @@
#include "DynamicAtlas.h"
namespace msdf_atlas {
template <class AtlasGenerator>
DynamicAtlas<AtlasGenerator>::DynamicAtlas() : glyphCount(0), side(0), totalArea(0), padding(0) { }
template <class AtlasGenerator>
DynamicAtlas<AtlasGenerator>::DynamicAtlas(AtlasGenerator &&generator) : generator((AtlasGenerator &&) generator), glyphCount(0), side(0), totalArea(0), padding(0) { }
template <class AtlasGenerator>
typename DynamicAtlas<AtlasGenerator>::ChangeFlags DynamicAtlas<AtlasGenerator>::add(GlyphGeometry *glyphs, int count, bool allowRearrange) {
ChangeFlags changeFlags = 0;
int start = rectangles.size();
for (int i = 0; i < count; ++i) {
if (!glyphs[i].isWhitespace()) {
int w, h;
glyphs[i].getBoxSize(w, h);
Rectangle rect = { 0, 0, w+padding, h+padding };
rectangles.push_back(rect);
Remap remapEntry = { };
remapEntry.index = glyphCount+i;
remapEntry.width = w;
remapEntry.height = h;
remapBuffer.push_back(remapEntry);
totalArea += (w+padding)*(h+padding);
}
}
if ((int) rectangles.size() > start) {
int packerStart = start;
int remaining;
while ((remaining = packer.pack(rectangles.data()+packerStart, rectangles.size()-packerStart)) > 0) {
side = (side+!side)<<1;
while (side*side < totalArea)
side <<= 1;
if (allowRearrange) {
packer = RectanglePacker(side+padding, side+padding);
packerStart = 0;
} else {
packer.expand(side+padding, side+padding);
packerStart = rectangles.size()-remaining;
}
changeFlags |= RESIZED;
}
if (packerStart < start) {
for (int i = packerStart; i < start; ++i) {
Remap &remap = remapBuffer[i];
remap.source = remap.target;
remap.target.x = rectangles[i].x;
remap.target.y = rectangles[i].y;
}
generator.rearrange(side, side, remapBuffer.data(), start);
changeFlags |= REARRANGED;
} else if (changeFlags&RESIZED)
generator.resize(side, side);
for (int i = start; i < (int) rectangles.size(); ++i) {
remapBuffer[i].target.x = rectangles[i].x;
remapBuffer[i].target.y = rectangles[i].y;
glyphs[remapBuffer[i].index-glyphCount].placeBox(rectangles[i].x, rectangles[i].y);
}
}
generator.generate(glyphs, count);
glyphCount += count;
return changeFlags;
}
template <class AtlasGenerator>
AtlasGenerator & DynamicAtlas<AtlasGenerator>::atlasGenerator() {
return generator;
}
template <class AtlasGenerator>
const AtlasGenerator & DynamicAtlas<AtlasGenerator>::atlasGenerator() const {
return generator;
}
}
@@ -0,0 +1,86 @@
#pragma once
#include <utility>
#include <vector>
#include <string>
#include <map>
#include <msdfgen.h>
#include <msdfgen-ext.h>
#include "types.h"
#include "GlyphGeometry.h"
#include "Charset.h"
#define MSDF_ATLAS_DEFAULT_EM_SIZE 32.0
namespace msdf_atlas {
/// Represents the geometry of all glyphs of a given font or font variant
class FontGeometry {
public:
class GlyphRange {
public:
GlyphRange();
GlyphRange(const std::vector<GlyphGeometry> *glyphs, size_t rangeStart, size_t rangeEnd);
size_t size() const;
bool empty() const;
const GlyphGeometry * begin() const;
const GlyphGeometry * end() const;
private:
const std::vector<GlyphGeometry> *glyphs;
size_t rangeStart, rangeEnd;
};
FontGeometry();
explicit FontGeometry(std::vector<GlyphGeometry> *glyphStorage);
/// Loads all glyphs in a glyphset (Charset elements are glyph indices), returns the number of successfully loaded glyphs
int loadGlyphset(msdfgen::FontHandle *font, double fontScale, const Charset &glyphset, bool preprocessGeometry = true, bool enableKerning = true);
/// Loads all glyphs in a charset (Charset elements are Unicode codepoints), returns the number of successfully loaded glyphs
int loadCharset(msdfgen::FontHandle *font, double fontScale, const Charset &charset, bool preprocessGeometry = true, bool enableKerning = true);
/// Only loads font metrics and geometry scale from font
bool loadMetrics(msdfgen::FontHandle *font, double fontScale);
/// Adds a loaded glyph
bool addGlyph(const GlyphGeometry &glyph);
bool addGlyph(GlyphGeometry &&glyph);
/// Loads kerning pairs for all glyphs that are currently present, returns the number of loaded kerning pairs
int loadKerning(msdfgen::FontHandle *font);
/// Sets a name to be associated with the font
void setName(const char *name);
/// Returns the geometry scale to be used when loading glyphs
double getGeometryScale() const;
/// Returns the processed font metrics
const msdfgen::FontMetrics & getMetrics() const;
/// Returns the type of identifier that was used to load glyphs
GlyphIdentifierType getPreferredIdentifierType() const;
/// Returns the list of all glyphs
GlyphRange getGlyphs() const;
/// Finds a glyph by glyph index or Unicode codepoint, returns null if not found
const GlyphGeometry * getGlyph(msdfgen::GlyphIndex index) const;
const GlyphGeometry * getGlyph(unicode_t codepoint) const;
/// Outputs the advance between two glyphs with kerning taken into consideration, returns false on failure
bool getAdvance(double &advance, msdfgen::GlyphIndex index1, msdfgen::GlyphIndex index2) const;
bool getAdvance(double &advance, unicode_t codepoint1, unicode_t codepoint2) const;
/// Returns the complete mapping of kerning pairs (by glyph indices) and their respective advance values
const std::map<std::pair<int, int>, double> & getKerning() const;
/// Returns the name associated with the font or null if not set
const char * getName() const;
private:
double geometryScale;
msdfgen::FontMetrics metrics;
GlyphIdentifierType preferredIdentifierType;
std::vector<GlyphGeometry> *glyphs;
size_t rangeStart, rangeEnd;
std::map<int, size_t> glyphsByIndex;
std::map<unicode_t, size_t> glyphsByCodepoint;
std::map<std::pair<int, int>, double> kerning;
std::vector<GlyphGeometry> ownGlyphs;
std::string name;
};
}
@@ -0,0 +1,19 @@
#pragma once
#include "Rectangle.h"
namespace msdf_atlas {
/// The glyph box - its bounds in plane and atlas
struct GlyphBox {
int index;
double advance;
struct {
double l, b, r, t;
} bounds;
Rectangle rect;
};
}
@@ -0,0 +1,79 @@
#pragma once
#include <msdfgen.h>
#include <msdfgen-ext.h>
#include "types.h"
#include "Rectangle.h"
#include "GlyphBox.h"
namespace msdf_atlas {
/// Represents the shape geometry of a single glyph as well as its configuration
class GlyphGeometry {
public:
GlyphGeometry();
/// Loads glyph geometry from font
bool load(msdfgen::FontHandle *font, double geometryScale, msdfgen::GlyphIndex index, bool preprocessGeometry = true);
bool load(msdfgen::FontHandle *font, double geometryScale, unicode_t codepoint, bool preprocessGeometry = true);
/// Applies edge coloring to glyph shape
void edgeColoring(void (*fn)(msdfgen::Shape &, double, unsigned long long), double angleThreshold, unsigned long long seed);
/// Computes the dimensions of the glyph's box as well as the transformation for the generator function
void wrapBox(double scale, double range, double miterLimit);
/// Sets the glyph's box's position in the atlas
void placeBox(int x, int y);
/// Sets the glyph's box's rectangle in the atlas
void setBoxRect(const Rectangle &rect);
/// Returns the glyph's index within the font
int getIndex() const;
/// Returns the glyph's index as a msdfgen::GlyphIndex
msdfgen::GlyphIndex getGlyphIndex() const;
/// Returns the Unicode codepoint represented by the glyph or 0 if unknown
unicode_t getCodepoint() const;
/// Returns the glyph's identifier specified by the supplied identifier type
int getIdentifier(GlyphIdentifierType type) const;
/// Returns the glyph's shape
const msdfgen::Shape & getShape() const;
/// Returns the glyph's advance
double getAdvance() const;
/// Returns the glyph's box in the atlas
Rectangle getBoxRect() const;
/// Outputs the position and dimensions of the glyph's box in the atlas
void getBoxRect(int &x, int &y, int &w, int &h) const;
/// Outputs the dimensions of the glyph's box in the atlas
void getBoxSize(int &w, int &h) const;
/// Returns the range needed to generate the glyph's SDF
double getBoxRange() const;
/// Returns the projection needed to generate the glyph's bitmap
msdfgen::Projection getBoxProjection() const;
/// Returns the scale needed to generate the glyph's bitmap
double getBoxScale() const;
/// Returns the translation vector needed to generate the glyph's bitmap
msdfgen::Vector2 getBoxTranslate() const;
/// Outputs the bounding box of the glyph as it should be placed on the baseline
void getQuadPlaneBounds(double &l, double &b, double &r, double &t) const;
/// Outputs the bounding box of the glyph in the atlas
void getQuadAtlasBounds(double &l, double &b, double &r, double &t) const;
/// Returns true if the glyph is a whitespace and has no geometry
bool isWhitespace() const;
/// Simplifies to GlyphBox
operator GlyphBox() const;
private:
int index;
unicode_t codepoint;
double geometryScale;
msdfgen::Shape shape;
msdfgen::Shape::Bounds bounds;
double advance;
struct {
Rectangle rect;
double range;
double scale;
msdfgen::Vector2 translate;
} box;
};
}
@@ -0,0 +1,47 @@
#pragma once
#include <vector>
#include "GlyphBox.h"
#include "Workload.h"
#include "AtlasGenerator.h"
namespace msdf_atlas {
/**
* An implementation of AtlasGenerator that uses the specified generator function
* and AtlasStorage class and generates glyph bitmaps immediately
* (does not return until all submitted work is finished),
* but may use multiple threads (setThreadCount).
*/
template <typename T, int N, GeneratorFunction<T, N> GEN_FN, class AtlasStorage>
class ImmediateAtlasGenerator {
public:
ImmediateAtlasGenerator();
ImmediateAtlasGenerator(int width, int height);
void generate(const GlyphGeometry *glyphs, int count);
void rearrange(int width, int height, const Remap *remapping, int count);
void resize(int width, int height);
/// Sets attributes for the generator function
void setAttributes(const GeneratorAttributes &attributes);
/// Sets the number of threads to be run by generate
void setThreadCount(int threadCount);
/// Allows access to the underlying AtlasStorage
const AtlasStorage & atlasStorage() const;
/// Returns the layout of the contained glyphs as a list of GlyphBoxes
const std::vector<GlyphBox> & getLayout() const;
private:
AtlasStorage storage;
std::vector<GlyphBox> layout;
std::vector<T> glyphBuffer;
std::vector<byte> errorCorrectionBuffer;
GeneratorAttributes attributes;
int threadCount;
};
}
#include "ImmediateAtlasGenerator.hpp"
@@ -0,0 +1,82 @@
#include "ImmediateAtlasGenerator.h"
#include <algorithm>
namespace msdf_atlas {
template <typename T, int N, GeneratorFunction<T, N> GEN_FN, class AtlasStorage>
ImmediateAtlasGenerator<T, N, GEN_FN, AtlasStorage>::ImmediateAtlasGenerator() : threadCount(1) { }
template <typename T, int N, GeneratorFunction<T, N> GEN_FN, class AtlasStorage>
ImmediateAtlasGenerator<T, N, GEN_FN, AtlasStorage>::ImmediateAtlasGenerator(int width, int height) : storage(width, height), threadCount(1) { }
template <typename T, int N, GeneratorFunction<T, N> GEN_FN, class AtlasStorage>
void ImmediateAtlasGenerator<T, N, GEN_FN, AtlasStorage>::generate(const GlyphGeometry *glyphs, int count) {
int maxBoxArea = 0;
for (int i = 0; i < count; ++i) {
GlyphBox box = glyphs[i];
maxBoxArea = std::max(maxBoxArea, box.rect.w*box.rect.h);
layout.push_back((GlyphBox &&) box);
}
int threadBufferSize = N*maxBoxArea;
if (threadCount*threadBufferSize > (int) glyphBuffer.size())
glyphBuffer.resize(threadCount*threadBufferSize);
if (threadCount*maxBoxArea > (int) errorCorrectionBuffer.size())
errorCorrectionBuffer.resize(threadCount*maxBoxArea);
std::vector<GeneratorAttributes> threadAttributes(threadCount);
for (int i = 0; i < threadCount; ++i) {
threadAttributes[i] = attributes;
threadAttributes[i].config.errorCorrection.buffer = errorCorrectionBuffer.data()+i*maxBoxArea;
}
Workload([this, glyphs, &threadAttributes, threadBufferSize](int i, int threadNo) -> bool {
const GlyphGeometry &glyph = glyphs[i];
if (!glyph.isWhitespace()) {
int l, b, w, h;
glyph.getBoxRect(l, b, w, h);
msdfgen::BitmapRef<T, N> glyphBitmap(glyphBuffer.data()+threadNo*threadBufferSize, w, h);
GEN_FN(glyphBitmap, glyph, threadAttributes[threadNo]);
storage.put(l, b, msdfgen::BitmapConstRef<T, N>(glyphBitmap));
}
return true;
}, count).finish(threadCount);
}
template <typename T, int N, GeneratorFunction<T, N> GEN_FN, class AtlasStorage>
void ImmediateAtlasGenerator<T, N, GEN_FN, AtlasStorage>::rearrange(int width, int height, const Remap *remapping, int count) {
for (int i = 0; i < count; ++i) {
layout[remapping[i].index].rect.x = remapping[i].target.x;
layout[remapping[i].index].rect.y = remapping[i].target.y;
}
AtlasStorage newStorage((AtlasStorage &&) storage, width, height, remapping, count);
storage = (AtlasStorage &&) newStorage;
}
template <typename T, int N, GeneratorFunction<T, N> GEN_FN, class AtlasStorage>
void ImmediateAtlasGenerator<T, N, GEN_FN, AtlasStorage>::resize(int width, int height) {
AtlasStorage newStorage((AtlasStorage &&) storage, width, height);
storage = (AtlasStorage &&) newStorage;
}
template <typename T, int N, GeneratorFunction<T, N> GEN_FN, class AtlasStorage>
void ImmediateAtlasGenerator<T, N, GEN_FN, AtlasStorage>::setAttributes(const GeneratorAttributes &attributes) {
this->attributes = attributes;
}
template <typename T, int N, GeneratorFunction<T, N> GEN_FN, class AtlasStorage>
void ImmediateAtlasGenerator<T, N, GEN_FN, AtlasStorage>::setThreadCount(int threadCount) {
this->threadCount = threadCount;
}
template <typename T, int N, GeneratorFunction<T, N> GEN_FN, class AtlasStorage>
const AtlasStorage & ImmediateAtlasGenerator<T, N, GEN_FN, AtlasStorage>::atlasStorage() const {
return storage;
}
template <typename T, int N, GeneratorFunction<T, N> GEN_FN, class AtlasStorage>
const std::vector<GlyphBox> & ImmediateAtlasGenerator<T, N, GEN_FN, AtlasStorage>::getLayout() const {
return layout;
}
}
@@ -0,0 +1,14 @@
#pragma once
namespace msdf_atlas {
struct Rectangle {
int x, y, w, h;
};
struct OrientedRectangle : Rectangle {
bool rotated;
};
}
@@ -0,0 +1,30 @@
#pragma once
#include <vector>
#include "Rectangle.h"
namespace msdf_atlas {
/// Guillotine 2D single bin packer
class RectanglePacker {
public:
RectanglePacker();
RectanglePacker(int width, int height);
/// Expands the packing area - both width and height must be greater or equal to the previous value
void expand(int width, int height);
/// Packs the rectangle array, returns how many didn't fit (0 on success)
int pack(Rectangle *rectangles, int count);
int pack(OrientedRectangle *rectangles, int count);
private:
std::vector<Rectangle> spaces;
static int rateFit(int w, int h, int sw, int sh);
void splitSpace(int index, int w, int h);
};
}
@@ -0,0 +1,15 @@
#pragma once
namespace msdf_atlas {
/// Represents the repositioning of a subsection of the atlas
struct Remap {
int index;
struct {
int x, y;
} source, target;
int width, height;
};
}
@@ -0,0 +1,71 @@
#pragma once
#include "GlyphGeometry.h"
namespace msdf_atlas {
/**
* This class computes the layout of a static atlas and may optionally
* also find the minimum required dimensions and/or the maximum glyph scale
*/
class TightAtlasPacker {
public:
/// Constraints for the atlas's dimensions - see size selectors for more info
enum class DimensionsConstraint {
POWER_OF_TWO_SQUARE,
POWER_OF_TWO_RECTANGLE,
MULTIPLE_OF_FOUR_SQUARE,
EVEN_SQUARE,
SQUARE
};
TightAtlasPacker();
/// Computes the layout for the array of glyphs. Returns 0 on success
int pack(GlyphGeometry *glyphs, int count);
/// Sets the atlas's dimensions to be fixed
void setDimensions(int width, int height);
/// Sets the atlas's dimensions to be determined during pack
void unsetDimensions();
/// Sets the constraint to be used when determining dimensions
void setDimensionsConstraint(DimensionsConstraint dimensionsConstraint);
/// Sets the padding between glyph boxes
void setPadding(int padding);
/// Sets fixed glyph scale
void setScale(double scale);
/// Sets the minimum glyph scale
void setMinimumScale(double minScale);
/// Sets the unit component of the total distance range
void setUnitRange(double unitRange);
/// Sets the pixel component of the total distance range
void setPixelRange(double pxRange);
/// Sets the miter limit for bounds computation
void setMiterLimit(double miterLimit);
/// Outputs the atlas's final dimensions
void getDimensions(int &width, int &height) const;
/// Returns the final glyph scale
double getScale() const;
/// Returns the final combined pixel range (including converted unit range)
double getPixelRange() const;
private:
int width, height;
int padding;
DimensionsConstraint dimensionsConstraint;
double scale;
double minScale;
double unitRange;
double pxRange;
double miterLimit;
double scaleMaximizationTolerance;
static int tryPack(GlyphGeometry *glyphs, int count, DimensionsConstraint dimensionsConstraint, int &width, int &height, int padding, double scale, double range, double miterLimit);
static double packAndScale(GlyphGeometry *glyphs, int count, int width, int height, int padding, double unitRange, double pxRange, double miterLimit, double tolerance);
};
}
@@ -0,0 +1,32 @@
#pragma once
#include <functional>
namespace msdf_atlas {
/**
* This function allows to split a workload into multiple threads.
* The worker function:
* bool FN(int chunk, int threadNo);
* should process the given chunk (out of chunks) and return true.
* If false is returned, the process is interrupted.
*/
class Workload {
public:
Workload();
Workload(const std::function<bool(int, int)> &workerFunction, int chunks);
/// Runs the process and returns true if all chunks have been processed
bool finish(int threadCount);
private:
std::function<bool(int, int)> workerFunction;
int chunks;
bool finishSequential();
bool finishParallel(int threadCount);
};
}
@@ -0,0 +1,27 @@
#pragma once
#ifndef MSDF_ATLAS_NO_ARTERY_FONT
#include <msdfgen.h>
#include <msdfgen-ext.h>
#include "types.h"
#include "FontGeometry.h"
namespace msdf_atlas {
struct ArteryFontExportProperties {
double fontSize;
double pxRange;
ImageType imageType;
ImageFormat imageFormat;
YDirection yDirection;
};
/// Encodes the atlas bitmap and its layout into an Artery Atlas Font file
template <typename REAL, typename T, int N>
bool exportArteryFont(const FontGeometry *fonts, int fontCount, const msdfgen::BitmapConstRef<T, N> &atlas, const char *filename, const ArteryFontExportProperties &properties);
}
#endif
@@ -0,0 +1,26 @@
#pragma once
#include <msdfgen.h>
#include "types.h"
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);
void blit(const msdfgen::BitmapRef<byte, 3> &dst, const msdfgen::BitmapConstRef<byte, 3> &src, int dx, int dy, int sx, int sy, int w, int h);
void blit(const msdfgen::BitmapRef<byte, 4> &dst, const msdfgen::BitmapConstRef<byte, 4> &src, int dx, int dy, int sx, int sy, int w, int h);
void blit(const msdfgen::BitmapRef<float, 1> &dst, const msdfgen::BitmapConstRef<float, 1> &src, int dx, int dy, int sx, int sy, int w, int h);
void blit(const msdfgen::BitmapRef<float, 3> &dst, const msdfgen::BitmapConstRef<float, 3> &src, int dx, int dy, int sx, int sy, int w, int h);
void blit(const msdfgen::BitmapRef<float, 4> &dst, const msdfgen::BitmapConstRef<float, 4> &src, int dx, int dy, int sx, int sy, int w, int h);
void blit(const msdfgen::BitmapRef<byte, 1> &dst, const msdfgen::BitmapConstRef<float, 1> &src, int dx, int dy, int sx, int sy, int w, int h);
void blit(const msdfgen::BitmapRef<byte, 3> &dst, const msdfgen::BitmapConstRef<float, 3> &src, int dx, int dy, int sx, int sy, int w, int h);
void blit(const msdfgen::BitmapRef<byte, 4> &dst, const msdfgen::BitmapConstRef<float, 4> &src, int dx, int dy, int sx, int sy, int w, int h);
}
@@ -0,0 +1,14 @@
#pragma once
#include "FontGeometry.h"
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, int atlasWidth, int atlasHeight, YDirection yDirection, const char *filename);
}
@@ -0,0 +1,25 @@
#pragma once
#include <msdfgen.h>
#include "GlyphGeometry.h"
#include "AtlasGenerator.h"
#define MSDF_ATLAS_GLYPH_FILL_RULE msdfgen::FILL_NONZERO
namespace msdf_atlas {
// Glyph bitmap generator functions
/// Generates non-anti-aliased binary image of the glyph using scanline rasterization
void scanlineGenerator(const msdfgen::BitmapRef<float, 1> &output, const GlyphGeometry &glyph, const GeneratorAttributes &attribs);
/// Generates a true signed distance field of the glyph
void sdfGenerator(const msdfgen::BitmapRef<float, 1> &output, const GlyphGeometry &glyph, const GeneratorAttributes &attribs);
/// Generates a signed pseudo-distance field of the glyph
void psdfGenerator(const msdfgen::BitmapRef<float, 1> &output, const GlyphGeometry &glyph, const GeneratorAttributes &attribs);
/// Generates a multi-channel signed distance field of the glyph
void msdfGenerator(const msdfgen::BitmapRef<float, 3> &output, const GlyphGeometry &glyph, const GeneratorAttributes &attribs);
/// Generates a multi-channel and alpha-encoded true signed distance field of the glyph
void mtsdfGenerator(const msdfgen::BitmapRef<float, 4> &output, const GlyphGeometry &glyph, const GeneratorAttributes &attribs);
}
@@ -0,0 +1,20 @@
#pragma once
#include <vector>
#include <msdfgen.h>
#include "types.h"
namespace msdf_atlas {
// Functions to encode an image as a sequence of bytes in memory
// Only PNG format available currently
bool encodePng(std::vector<byte> &output, const msdfgen::BitmapConstRef<msdfgen::byte, 1> &bitmap);
bool encodePng(std::vector<byte> &output, const msdfgen::BitmapConstRef<msdfgen::byte, 3> &bitmap);
bool encodePng(std::vector<byte> &output, const msdfgen::BitmapConstRef<msdfgen::byte, 4> &bitmap);
bool encodePng(std::vector<byte> &output, const msdfgen::BitmapConstRef<float, 1> &bitmap);
bool encodePng(std::vector<byte> &output, const msdfgen::BitmapConstRef<float, 3> &bitmap);
bool encodePng(std::vector<byte> &output, const msdfgen::BitmapConstRef<float, 4> &bitmap);
}
@@ -0,0 +1,14 @@
#pragma once
#include <msdfgen.h>
#include <msdfgen-ext.h>
#include "types.h"
#include "FontGeometry.h"
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, YDirection yDirection, const char *filename, bool kerning);
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,37 @@
#pragma once
/*
* MULTI-CHANNEL SIGNED DISTANCE FIELD ATLAS GENERATOR
* ---------------------------------------------------
* A utility by Viktor Chlumsky, (c) 2020 - 2023
* Generates compact bitmap font atlases using MSDFgen
*/
#include <msdfgen.h>
#include <msdfgen-ext.h>
#include "types.h"
#include "utf8.h"
#include "Rectangle.h"
#include "Charset.h"
#include "GlyphBox.h"
#include "GlyphGeometry.h"
#include "FontGeometry.h"
#include "RectanglePacker.h"
#include "rectangle-packing.h"
#include "Workload.h"
#include "size-selectors.h"
#include "bitmap-blit.h"
#include "AtlasStorage.h"
#include "BitmapAtlasStorage.h"
#include "TightAtlasPacker.h"
#include "AtlasGenerator.h"
#include "ImmediateAtlasGenerator.h"
#include "DynamicAtlas.h"
#include "glyph-generators.h"
#include "image-encode.h"
#include "artery-font-export.h"
#include "csv-export.h"
#include "json-export.h"
#include "shadron-preview-generator.h"
@@ -0,0 +1,19 @@
#pragma once
#include <utility>
#include "Rectangle.h"
namespace msdf_atlas {
/// Packs the rectangle array into an atlas with fixed dimensions, returns how many didn't fit (0 on success)
template <typename RectangleType>
int packRectangles(RectangleType *rectangles, int count, int width, int height, int padding = 0);
/// Packs the rectangle array into an atlas of unknown size, returns the minimum required dimensions constrained by SizeSelector
template <class SizeSelector, typename RectangleType>
std::pair<int, int> packRectangles(RectangleType *rectangles, int count, int padding = 0);
}
#include "rectangle-packing.hpp"
@@ -0,0 +1,61 @@
#include "rectangle-packing.h"
#include <vector>
#include "RectanglePacker.h"
namespace msdf_atlas {
static void copyRectanglePlacement(Rectangle &dst, const Rectangle &src) {
dst.x = src.x;
dst.y = src.y;
}
static void copyRectanglePlacement(OrientedRectangle &dst, const OrientedRectangle &src) {
dst.x = src.x;
dst.y = src.y;
dst.rotated = src.rotated;
}
template <typename RectangleType>
int packRectangles(RectangleType *rectangles, int count, int width, int height, int padding) {
if (padding)
for (int i = 0; i < count; ++i) {
rectangles[i].w += padding;
rectangles[i].h += padding;
}
int result = RectanglePacker(width+padding, height+padding).pack(rectangles, count);
if (padding)
for (int i = 0; i < count; ++i) {
rectangles[i].w -= padding;
rectangles[i].h -= padding;
}
return result;
}
template <class SizeSelector, typename RectangleType>
std::pair<int, int> packRectangles(RectangleType *rectangles, int count, int padding) {
std::vector<RectangleType> rectanglesCopy(count);
int totalArea = 0;
for (int i = 0; i < count; ++i) {
rectanglesCopy[i].w = rectangles[i].w+padding;
rectanglesCopy[i].h = rectangles[i].h+padding;
totalArea += rectangles[i].w*rectangles[i].h;
}
std::pair<int, int> dimensions;
SizeSelector sizeSelector(totalArea);
int width, height;
while (sizeSelector(width, height)) {
if (!RectanglePacker(width+padding, height+padding).pack(rectanglesCopy.data(), count)) {
dimensions.first = width;
dimensions.second = height;
for (int i = 0; i < count; ++i)
copyRectanglePlacement(rectangles[i], rectanglesCopy[i]);
--sizeSelector;
} else
++sizeSelector;
}
return dimensions;
}
}
@@ -0,0 +1,14 @@
#pragma once
#include <msdfgen.h>
#include <msdfgen-ext.h>
#include "types.h"
#include "FontGeometry.h"
namespace msdf_atlas {
/// Generates a Shadron script that displays a string using the generated atlas
bool generateShadronPreview(const FontGeometry *fonts, int fontCount, ImageType atlasType, int atlasWidth, int atlasHeight, double pxRange, const unicode_t *text, const char *imageFilename, bool fullRange, const char *outputFilename);
}
@@ -0,0 +1,54 @@
#pragma once
namespace msdf_atlas {
// The size selector classes are used to select the minimum dimensions of the atlas fitting a given constraint.
/// Selects square dimensions which are also a multiple of MULTIPLE
template <int MULTIPLE = 1>
class SquareSizeSelector {
public:
explicit SquareSizeSelector(int minArea = 0);
bool operator()(int &width, int &height) const;
SquareSizeSelector<MULTIPLE> & operator++();
SquareSizeSelector<MULTIPLE> & operator--();
private:
int lowerBound, upperBound;
int current;
void updateCurrent();
};
/// Selects square power-of-two dimensions
class SquarePowerOfTwoSizeSelector {
public:
explicit SquarePowerOfTwoSizeSelector(int minArea = 0);
bool operator()(int &width, int &height) const;
SquarePowerOfTwoSizeSelector & operator++();
SquarePowerOfTwoSizeSelector & operator--();
private:
int side;
};
/// Selects square or rectangular (2:1) power-of-two dimensions
class PowerOfTwoSizeSelector {
public:
explicit PowerOfTwoSizeSelector(int minArea = 0);
bool operator()(int &width, int &height) const;
PowerOfTwoSizeSelector & operator++();
PowerOfTwoSizeSelector & operator--();
private:
int w, h;
};
}
@@ -0,0 +1,52 @@
#pragma once
#include <cstdint>
namespace msdf_atlas {
typedef unsigned char byte;
typedef uint32_t unicode_t;
/// Type of atlas image contents
enum class ImageType {
/// Rendered glyphs without anti-aliasing (two colors only)
HARD_MASK,
/// Rendered glyphs with anti-aliasing
SOFT_MASK,
/// Signed (true) distance field
SDF,
/// Signed pseudo-distance field
PSDF,
/// Multi-channel signed distance field
MSDF,
/// Multi-channel & true signed distance field
MTSDF
};
/// Atlas image encoding
enum class ImageFormat {
UNSPECIFIED,
PNG,
BMP,
TIFF,
TEXT,
TEXT_FLOAT,
BINARY,
BINARY_FLOAT,
BINARY_FLOAT_BE
};
/// Glyph identification
enum class GlyphIdentifierType {
GLYPH_INDEX,
UNICODE_CODEPOINT
};
/// Direction of the Y-axis
enum class YDirection {
BOTTOM_UP,
TOP_DOWN
};
}
@@ -0,0 +1,12 @@
#pragma once
#include <vector>
#include "types.h"
namespace msdf_atlas {
/// Decodes the UTF-8 string into an array of Unicode codepoints
void utf8Decode(std::vector<unicode_t> &codepoints, const char *utf8String);
}