Grid atlas origin pixel alignment
This commit is contained in:
parent
37ffbd85ee
commit
0f48aaa727
|
|
@ -1,6 +1,6 @@
|
||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2020 - 2023 Viktor Chlumsky
|
Copyright (c) 2020 - 2024 Viktor Chlumsky
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|
|
||||||
|
|
@ -102,7 +102,7 @@ In that case, these additional options are available to customize the layout:
|
||||||
- `-uniformcols <N>` – sets the number of columns
|
- `-uniformcols <N>` – sets the number of columns
|
||||||
- `-uniformcell <width> <height>` – sets the dimensions of the grid's cells
|
- `-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)
|
- `-uniformcellconstraint <none / pots / potr / square / square2 / square4>` – sets constraint for cell dimensions (see explanation of options above)
|
||||||
- `-uniformorigin <yes / no / horizontal / vertical>` – sets whether the glyph's origin point should be fixed at the same position in each cell
|
- `-uniformorigin <off / on / horizontal / vertical>` – sets whether the glyph's origin point should be fixed at the same position in each cell
|
||||||
|
|
||||||
### Outputs
|
### Outputs
|
||||||
|
|
||||||
|
|
@ -120,7 +120,7 @@ Any non-empty subset of the following may be specified:
|
||||||
- `-minsize <em size>` – sets the minimum size. The largest possible size that fits the same atlas dimensions will be used
|
- `-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
|
- `-emrange <em range>` – sets the distance field range in em's
|
||||||
- `-pxrange <pixel range>` (default = 2) – sets the distance field range in output pixels
|
- `-pxrange <pixel range>` (default = 2) – sets the distance field range in output pixels
|
||||||
- `-pxalign <on / off / horizontal / vertical>` (default = vertical) – enables or disables alignment of glyph's origin point with the pixel grid
|
- `-pxalign <off / on / horizontal / vertical>` (default = vertical) – enables or disables alignment of glyph's origin point with the pixel grid
|
||||||
|
|
||||||
### Distance field generator settings
|
### Distance field generator settings
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,10 @@
|
||||||
#include <set>
|
#include <set>
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
|
|
||||||
|
#ifndef MSDF_ATLAS_PUBLIC
|
||||||
|
#define MSDF_ATLAS_PUBLIC
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace msdf_atlas {
|
namespace msdf_atlas {
|
||||||
|
|
||||||
/// Represents a set of Unicode codepoints (characters)
|
/// Represents a set of Unicode codepoints (characters)
|
||||||
|
|
|
||||||
|
|
@ -51,11 +51,11 @@ void GlyphGeometry::edgeColoring(void (*fn)(msdfgen::Shape &, double, unsigned l
|
||||||
fn(shape, angleThreshold, seed);
|
fn(shape, angleThreshold, seed);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GlyphGeometry::wrapBox(double scale, double range, double miterLimit, bool alignOrigin) {
|
void GlyphGeometry::wrapBox(double scale, double range, double miterLimit, bool pxAlignOrigin) {
|
||||||
wrapBox(scale, range, miterLimit, alignOrigin, alignOrigin);
|
wrapBox(scale, range, miterLimit, pxAlignOrigin, pxAlignOrigin);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GlyphGeometry::wrapBox(double scale, double range, double miterLimit, bool alignOriginX, bool alignOriginY) {
|
void GlyphGeometry::wrapBox(double scale, double range, double miterLimit, bool pxAlignOriginX, bool pxAlignOriginY) {
|
||||||
scale *= geometryScale;
|
scale *= geometryScale;
|
||||||
range /= geometryScale;
|
range /= geometryScale;
|
||||||
box.range = range;
|
box.range = range;
|
||||||
|
|
@ -66,7 +66,7 @@ void GlyphGeometry::wrapBox(double scale, double range, double miterLimit, bool
|
||||||
r += .5*range, t += .5*range;
|
r += .5*range, t += .5*range;
|
||||||
if (miterLimit > 0)
|
if (miterLimit > 0)
|
||||||
shape.boundMiters(l, b, r, t, .5*range, miterLimit, 1);
|
shape.boundMiters(l, b, r, t, .5*range, miterLimit, 1);
|
||||||
if (alignOriginX) {
|
if (pxAlignOriginX) {
|
||||||
int sl = (int) floor(scale*l-.5);
|
int sl = (int) floor(scale*l-.5);
|
||||||
int sr = (int) ceil(scale*r+.5);
|
int sr = (int) ceil(scale*r+.5);
|
||||||
box.rect.w = sr-sl;
|
box.rect.w = sr-sl;
|
||||||
|
|
@ -76,7 +76,7 @@ void GlyphGeometry::wrapBox(double scale, double range, double miterLimit, bool
|
||||||
box.rect.w = (int) ceil(w)+1;
|
box.rect.w = (int) ceil(w)+1;
|
||||||
box.translate.x = -l+.5*(box.rect.w-w)/scale;
|
box.translate.x = -l+.5*(box.rect.w-w)/scale;
|
||||||
}
|
}
|
||||||
if (alignOriginY) {
|
if (pxAlignOriginY) {
|
||||||
int sb = (int) floor(scale*b-.5);
|
int sb = (int) floor(scale*b-.5);
|
||||||
int st = (int) ceil(scale*t+.5);
|
int st = (int) ceil(scale*t+.5);
|
||||||
box.rect.h = st-sb;
|
box.rect.h = st-sb;
|
||||||
|
|
@ -92,7 +92,11 @@ void GlyphGeometry::wrapBox(double scale, double range, double miterLimit, bool
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GlyphGeometry::frameBox(double scale, double range, double miterLimit, int width, int height, const double *fixedX, const double *fixedY) {
|
void GlyphGeometry::frameBox(double scale, double range, double miterLimit, int width, int height, const double *fixedX, const double *fixedY, bool pxAlignOrigin) {
|
||||||
|
frameBox(scale, range, miterLimit, width, height, fixedX, fixedY, pxAlignOrigin, pxAlignOrigin);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GlyphGeometry::frameBox(double scale, double range, double miterLimit, int width, int height, const double *fixedX, const double *fixedY, bool pxAlignOriginX, bool pxAlignOriginY) {
|
||||||
scale *= geometryScale;
|
scale *= geometryScale;
|
||||||
range /= geometryScale;
|
range /= geometryScale;
|
||||||
box.range = range;
|
box.range = range;
|
||||||
|
|
@ -110,13 +114,21 @@ void GlyphGeometry::frameBox(double scale, double range, double miterLimit, int
|
||||||
shape.boundMiters(l, b, r, t, .5*range, miterLimit, 1);
|
shape.boundMiters(l, b, r, t, .5*range, miterLimit, 1);
|
||||||
if (fixedX)
|
if (fixedX)
|
||||||
box.translate.x = *fixedX/geometryScale;
|
box.translate.x = *fixedX/geometryScale;
|
||||||
else {
|
else if (pxAlignOriginX) {
|
||||||
|
int sl = (int) floor(scale*l-.5);
|
||||||
|
int sr = (int) ceil(scale*r+.5);
|
||||||
|
box.translate.x = (-sl+(box.rect.w-(sr-sl))/2)/scale;
|
||||||
|
} else {
|
||||||
double w = scale*(r-l);
|
double w = scale*(r-l);
|
||||||
box.translate.x = -l+.5*(box.rect.w-w)/scale;
|
box.translate.x = -l+.5*(box.rect.w-w)/scale;
|
||||||
}
|
}
|
||||||
if (fixedY)
|
if (fixedY)
|
||||||
box.translate.y = *fixedY/geometryScale;
|
box.translate.y = *fixedY/geometryScale;
|
||||||
else {
|
else if (pxAlignOriginY) {
|
||||||
|
int sb = (int) floor(scale*b-.5);
|
||||||
|
int st = (int) ceil(scale*t+.5);
|
||||||
|
box.translate.y = (-sb+(box.rect.h-(st-sb))/2)/scale;
|
||||||
|
} else {
|
||||||
double h = scale*(t-b);
|
double h = scale*(t-b);
|
||||||
box.translate.y = -b+.5*(box.rect.h-h)/scale;
|
box.translate.y = -b+.5*(box.rect.h-h)/scale;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,10 +20,11 @@ public:
|
||||||
/// Applies edge coloring to glyph shape
|
/// Applies edge coloring to glyph shape
|
||||||
void edgeColoring(void (*fn)(msdfgen::Shape &, double, unsigned long long), double angleThreshold, unsigned long long seed);
|
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
|
/// 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, bool alignOrigin = false);
|
void wrapBox(double scale, double range, double miterLimit, bool pxAlignOrigin = false);
|
||||||
void wrapBox(double scale, double range, double miterLimit, bool alignOriginX, bool alignOriginY);
|
void wrapBox(double scale, double range, double miterLimit, bool pxAlignOriginX, bool pxAlignOriginY);
|
||||||
/// Computes the glyph's transformation and alignment (unless specified) for given dimensions
|
/// Computes the glyph's transformation and alignment (unless specified) for given dimensions
|
||||||
void frameBox(double scale, double range, double miterLimit, int width, int height, const double *fixedX, const double *fixedY);
|
void frameBox(double scale, double range, double miterLimit, int width, int height, const double *fixedX, const double *fixedY, bool pxAlignOrigin = false);
|
||||||
|
void frameBox(double scale, double range, double miterLimit, int width, int height, const double *fixedX, const double *fixedY, bool pxAlignOriginX, bool pxAlignOriginY);
|
||||||
/// Sets the glyph's box's position in the atlas
|
/// Sets the glyph's box's position in the atlas
|
||||||
void placeBox(int x, int y);
|
void placeBox(int x, int y);
|
||||||
/// Sets the glyph's box's rectangle in the atlas
|
/// Sets the glyph's box's rectangle in the atlas
|
||||||
|
|
|
||||||
|
|
@ -101,6 +101,7 @@ GridAtlasPacker::GridAtlasPacker() :
|
||||||
unitRange(0),
|
unitRange(0),
|
||||||
pxRange(0),
|
pxRange(0),
|
||||||
miterLimit(0),
|
miterLimit(0),
|
||||||
|
pxAlignOriginX(false), pxAlignOriginY(false),
|
||||||
scaleMaximizationTolerance(.001),
|
scaleMaximizationTolerance(.001),
|
||||||
alignedColumnsBias(.125)
|
alignedColumnsBias(.125)
|
||||||
{ }
|
{ }
|
||||||
|
|
@ -131,10 +132,15 @@ msdfgen::Shape::Bounds GridAtlasPacker::getMaxBounds(double &maxWidth, double &m
|
||||||
}
|
}
|
||||||
if (maxBounds.l >= maxBounds.r || maxBounds.b >= maxBounds.t)
|
if (maxBounds.l >= maxBounds.r || maxBounds.b >= maxBounds.t)
|
||||||
maxBounds = msdfgen::Shape::Bounds();
|
maxBounds = msdfgen::Shape::Bounds();
|
||||||
|
// If origin is pixel-aligned but not fixed, one pixel has to be added to max dimensions to allow for aligning the origin by shifting by < 1 pixel
|
||||||
if (hFixed)
|
if (hFixed)
|
||||||
maxWidth = maxBounds.r-maxBounds.l;
|
maxWidth = maxBounds.r-maxBounds.l;
|
||||||
|
else if (pxAlignOriginX)
|
||||||
|
maxWidth += 1;
|
||||||
if (vFixed)
|
if (vFixed)
|
||||||
maxHeight = maxBounds.t-maxBounds.b;
|
maxHeight = maxBounds.t-maxBounds.b;
|
||||||
|
else if (pxAlignOriginY)
|
||||||
|
maxHeight += 1;
|
||||||
return maxBounds;
|
return maxBounds;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -144,6 +150,7 @@ double GridAtlasPacker::scaleToFit(GlyphGeometry *glyphs, int count, int cellWid
|
||||||
cellWidth = BIG_VALUE;
|
cellWidth = BIG_VALUE;
|
||||||
if (cellHeight <= 0)
|
if (cellHeight <= 0)
|
||||||
cellHeight = BIG_VALUE;
|
cellHeight = BIG_VALUE;
|
||||||
|
--cellWidth, --cellHeight; // Implicit half-pixel padding from each side to make sure that no representable values are beyond outermost pixel centers
|
||||||
cellWidth -= padding, cellHeight -= padding;
|
cellWidth -= padding, cellHeight -= padding;
|
||||||
bool lastResult = false;
|
bool lastResult = false;
|
||||||
#define TRY_FIT(scale) (maxWidth = 0, maxHeight = 0, maxBounds = getMaxBounds(maxWidth, maxHeight, glyphs, count, (scale), unitRange+pxRange/(scale)), lastResult = maxWidth <= cellWidth && maxHeight <= cellHeight)
|
#define TRY_FIT(scale) (maxWidth = 0, maxHeight = 0, maxBounds = getMaxBounds(maxWidth, maxHeight, glyphs, count, (scale), unitRange+pxRange/(scale)), lastResult = maxWidth <= cellWidth && maxHeight <= cellHeight)
|
||||||
|
|
@ -169,23 +176,29 @@ double GridAtlasPacker::scaleToFit(GlyphGeometry *glyphs, int count, int cellWid
|
||||||
return minScale;
|
return minScale;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ultra spaghetti
|
// TODO tame spaghetti code
|
||||||
int GridAtlasPacker::pack(GlyphGeometry *glyphs, int count) {
|
int GridAtlasPacker::pack(GlyphGeometry *glyphs, int count) {
|
||||||
|
if (!count)
|
||||||
|
return 0;
|
||||||
bool cellHeightFinal = cellHeight > 0;
|
bool cellHeightFinal = cellHeight > 0;
|
||||||
bool explicitRows = rows > 0;
|
bool explicitRows = rows > 0;
|
||||||
int cellCount = count;
|
int cellCount = 0;
|
||||||
if (!cellCount)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if (columns > 0 && rows > 0)
|
if (columns > 0 && rows > 0)
|
||||||
cellCount = columns*rows;
|
cellCount = columns*rows;
|
||||||
else if (columns > 0)
|
else {
|
||||||
rows = (cellCount+columns-1)/columns;
|
// Count non-whitespace glyphs only
|
||||||
else if (rows > 0)
|
for (const GlyphGeometry *glyph = glyphs, *end = glyphs+count; glyph < end; ++glyph) {
|
||||||
columns = (cellCount+rows-1)/rows;
|
if (!glyph->isWhitespace())
|
||||||
else if (width > 0 && cellWidth > 0) {
|
++cellCount;
|
||||||
columns = (width+padding)/cellWidth;
|
}
|
||||||
rows = (cellCount+columns-1)/columns;
|
if (columns > 0)
|
||||||
|
rows = (cellCount+columns-1)/columns;
|
||||||
|
else if (rows > 0)
|
||||||
|
columns = (cellCount+rows-1)/rows;
|
||||||
|
else if (width > 0 && cellWidth > 0) {
|
||||||
|
columns = (width+padding)/cellWidth;
|
||||||
|
rows = (cellCount+columns-1)/columns;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool dimensionsChanged = false;
|
bool dimensionsChanged = false;
|
||||||
|
|
@ -212,14 +225,16 @@ int GridAtlasPacker::pack(GlyphGeometry *glyphs, int count) {
|
||||||
if (dimensionsChanged)
|
if (dimensionsChanged)
|
||||||
lowerToConstraint(cellWidth, cellHeight, cellDimensionsConstraint);
|
lowerToConstraint(cellWidth, cellHeight, cellDimensionsConstraint);
|
||||||
|
|
||||||
if ((cellWidth > 0 && cellWidth-padding <= pxRange) || (cellHeight > 0 && cellHeight-padding <= pxRange)) // cells definitely too small
|
if ((cellWidth > 0 && cellWidth-padding-1 <= pxRange) || (cellHeight > 0 && cellHeight-padding-1 <= pxRange)) // cells definitely too small
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
|
msdfgen::Shape::Bounds maxBounds = { };
|
||||||
|
double maxWidth = 0, maxHeight = 0;
|
||||||
|
|
||||||
if (scale <= 0) {
|
if (scale <= 0) {
|
||||||
|
|
||||||
// If both pxRange and miterLimit is non-zero, miter bounds have to be computed for all potential scales
|
// If both pxRange and miterLimit is non-zero, miter bounds have to be computed for all potential scales
|
||||||
if (pxRange && miterLimit > 0) {
|
if (pxRange && miterLimit > 0) {
|
||||||
double maxWidth = 0, maxHeight = 0;
|
|
||||||
msdfgen::Shape::Bounds maxBounds;
|
|
||||||
|
|
||||||
if (cellWidth > 0 || cellHeight > 0) {
|
if (cellWidth > 0 || cellHeight > 0) {
|
||||||
scale = scaleToFit(glyphs, count, cellWidth, cellHeight, maxBounds, maxWidth, maxHeight);
|
scale = scaleToFit(glyphs, count, cellWidth, cellHeight, maxBounds, maxWidth, maxHeight);
|
||||||
|
|
@ -283,8 +298,8 @@ int GridAtlasPacker::pack(GlyphGeometry *glyphs, int count) {
|
||||||
|
|
||||||
else {
|
else {
|
||||||
maxBounds = getMaxBounds(maxWidth, maxHeight, glyphs, count, minScale, unitRange+pxRange/minScale);
|
maxBounds = getMaxBounds(maxWidth, maxHeight, glyphs, count, minScale, unitRange+pxRange/minScale);
|
||||||
cellWidth = (int) ceil(maxWidth)+padding;
|
cellWidth = (int) ceil(maxWidth)+padding+1;
|
||||||
cellHeight = (int) ceil(maxHeight)+padding;
|
cellHeight = (int) ceil(maxHeight)+padding+1;
|
||||||
raiseToConstraint(cellWidth, cellHeight, cellDimensionsConstraint);
|
raiseToConstraint(cellWidth, cellHeight, cellDimensionsConstraint);
|
||||||
scale = scaleToFit(glyphs, count, cellWidth, cellHeight, maxBounds, maxWidth, maxHeight);
|
scale = scaleToFit(glyphs, count, cellWidth, cellHeight, maxBounds, maxWidth, maxHeight);
|
||||||
if (scale < minScale)
|
if (scale < minScale)
|
||||||
|
|
@ -292,20 +307,26 @@ int GridAtlasPacker::pack(GlyphGeometry *glyphs, int count) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!explicitRows && !cellHeightFinal)
|
if (!explicitRows && !cellHeightFinal)
|
||||||
cellHeight = (int) ceil(maxHeight)+padding;
|
cellHeight = (int) ceil(maxHeight)+padding+1;
|
||||||
fixedX = (-maxBounds.l+.5*(cellWidth-padding-maxWidth))/scale;
|
|
||||||
fixedY = (-maxBounds.b+.5*(cellHeight-padding-maxHeight))/scale;
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
double maxWidth = 0, maxHeight = 0;
|
maxBounds = getMaxBounds(maxWidth, maxHeight, glyphs, count, 1, unitRange);
|
||||||
msdfgen::Shape::Bounds maxBounds = getMaxBounds(maxWidth, maxHeight, glyphs, count, 1, unitRange);
|
int hSlack = 0, vSlack = 0;
|
||||||
|
if (pxAlignOriginX && !hFixed) {
|
||||||
|
maxWidth -= 1; // Added by getMaxBounds
|
||||||
|
hSlack = 1;
|
||||||
|
}
|
||||||
|
if (pxAlignOriginY && !vFixed) {
|
||||||
|
maxHeight -= 1; // Added by getMaxBounds
|
||||||
|
vSlack = 1;
|
||||||
|
}
|
||||||
|
|
||||||
double hScale = 0, vScale = 0;
|
double hScale = 0, vScale = 0;
|
||||||
if (cellWidth > 0)
|
if (cellWidth > 0)
|
||||||
hScale = (cellWidth-padding-pxRange)/maxWidth;
|
hScale = (cellWidth-hSlack-padding-1-pxRange)/maxWidth;
|
||||||
if (cellHeight > 0)
|
if (cellHeight > 0)
|
||||||
vScale = (cellHeight-padding-pxRange)/maxHeight;
|
vScale = (cellHeight-vSlack-padding-1-pxRange)/maxHeight;
|
||||||
if (hScale || vScale) {
|
if (hScale || vScale) {
|
||||||
if (hScale && vScale)
|
if (hScale && vScale)
|
||||||
scale = std::min(hScale, vScale);
|
scale = std::min(hScale, vScale);
|
||||||
|
|
@ -318,7 +339,7 @@ int GridAtlasPacker::pack(GlyphGeometry *glyphs, int count) {
|
||||||
else if (width > 0 && height > 0) {
|
else if (width > 0 && height > 0) {
|
||||||
double bestAlignedScale = 0;
|
double bestAlignedScale = 0;
|
||||||
int bestCols = 0, bestAlignedCols = 0;
|
int bestCols = 0, bestAlignedCols = 0;
|
||||||
// TODO sqrtize
|
// TODO optimize to only test up to sqrt(cellCount) cols and rows like in the above branch (for (int q = (int) sqrt(cellCount)+1; ...)
|
||||||
for (int cols = 1; cols < width; ++cols) {
|
for (int cols = 1; cols < width; ++cols) {
|
||||||
int rows = (cellCount+cols-1)/cols;
|
int rows = (cellCount+cols-1)/cols;
|
||||||
int tWidth = (width+padding)/cols;
|
int tWidth = (width+padding)/cols;
|
||||||
|
|
@ -326,8 +347,8 @@ int GridAtlasPacker::pack(GlyphGeometry *glyphs, int count) {
|
||||||
if (!(tWidth > 0 && tHeight > 0))
|
if (!(tWidth > 0 && tHeight > 0))
|
||||||
continue;
|
continue;
|
||||||
lowerToConstraint(tWidth, tHeight, cellDimensionsConstraint);
|
lowerToConstraint(tWidth, tHeight, cellDimensionsConstraint);
|
||||||
hScale = (tWidth-padding-pxRange)/maxWidth;
|
hScale = (tWidth-hSlack-padding-pxRange)/maxWidth;
|
||||||
vScale = (tHeight-padding-pxRange)/maxHeight;
|
vScale = (tHeight-vSlack-padding-pxRange)/maxHeight;
|
||||||
double curScale = std::min(hScale, vScale);
|
double curScale = std::min(hScale, vScale);
|
||||||
if (curScale > scale) {
|
if (curScale > scale) {
|
||||||
scale = curScale;
|
scale = curScale;
|
||||||
|
|
@ -354,32 +375,47 @@ int GridAtlasPacker::pack(GlyphGeometry *glyphs, int count) {
|
||||||
}
|
}
|
||||||
|
|
||||||
else {
|
else {
|
||||||
cellWidth = (int) ceil(minScale*maxWidth+pxRange)+padding;
|
cellWidth = (int) ceil(minScale*maxWidth+pxRange)+hSlack+padding+1;
|
||||||
cellHeight = (int) ceil(minScale*maxHeight+pxRange)+padding;
|
cellHeight = (int) ceil(minScale*maxHeight+pxRange)+vSlack+padding+1;
|
||||||
raiseToConstraint(cellWidth, cellHeight, cellDimensionsConstraint);
|
raiseToConstraint(cellWidth, cellHeight, cellDimensionsConstraint);
|
||||||
hScale = (cellWidth-padding-pxRange)/maxWidth;
|
hScale = (cellWidth-hSlack-padding-1-pxRange)/maxWidth;
|
||||||
vScale = (cellHeight-padding-pxRange)/maxHeight;
|
vScale = (cellHeight-vSlack-padding-1-pxRange)/maxHeight;
|
||||||
scale = std::min(hScale, vScale);
|
scale = std::min(hScale, vScale);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!explicitRows && !cellHeightFinal)
|
if (!explicitRows && !cellHeightFinal)
|
||||||
cellHeight = (int) ceil(scale*maxHeight+pxRange)+padding;
|
cellHeight = (int) ceil(scale*maxHeight+pxRange)+vSlack+padding+1;
|
||||||
fixedX = -maxBounds.l+.5*((cellWidth-padding)/scale-maxWidth);
|
maxBounds.l *= scale, maxBounds.b *= scale;
|
||||||
fixedY = -maxBounds.b+.5*((cellHeight-padding)/scale-maxHeight);
|
maxBounds.r *= scale, maxBounds.t *= scale;
|
||||||
|
maxWidth *= scale, maxHeight *= scale;
|
||||||
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
double maxWidth = 0, maxHeight = 0;
|
|
||||||
msdfgen::Shape::Bounds maxBounds = getMaxBounds(maxWidth, maxHeight, glyphs, count, scale, unitRange+pxRange/scale);
|
|
||||||
|
|
||||||
|
} else {
|
||||||
|
maxBounds = getMaxBounds(maxWidth, maxHeight, glyphs, count, scale, unitRange+pxRange/scale);
|
||||||
if (cellWidth < 0 || cellHeight < 0) {
|
if (cellWidth < 0 || cellHeight < 0) {
|
||||||
cellWidth = (int) ceil(maxWidth)+padding;
|
cellWidth = (int) ceil(maxWidth)+padding+1;
|
||||||
cellHeight = (int) ceil(maxHeight)+padding;
|
cellHeight = (int) ceil(maxHeight)+padding+1;
|
||||||
raiseToConstraint(cellWidth, cellHeight, cellDimensionsConstraint);
|
raiseToConstraint(cellWidth, cellHeight, cellDimensionsConstraint);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fixedX = (-maxBounds.l+.5*(cellWidth-padding-maxWidth))/scale;
|
// Compute fixed origin
|
||||||
fixedY = (-maxBounds.b+.5*(cellHeight-padding-maxHeight))/scale;
|
if (hFixed) {
|
||||||
|
if (pxAlignOriginX) {
|
||||||
|
int sl = (int) floor(maxBounds.l-.5);
|
||||||
|
int sr = (int) ceil(maxBounds.r+.5);
|
||||||
|
fixedX = (-sl+(cellWidth-padding-(sr-sl))/2)/scale;
|
||||||
|
} else
|
||||||
|
fixedX = (-maxBounds.l+.5*(cellWidth-padding-maxWidth))/scale;
|
||||||
|
}
|
||||||
|
if (vFixed) {
|
||||||
|
if (pxAlignOriginY) {
|
||||||
|
int sb = (int) floor(maxBounds.b-.5);
|
||||||
|
int st = (int) ceil(maxBounds.t+.5);
|
||||||
|
fixedY = (-sb+(cellHeight-padding-(st-sb))/2)/scale;
|
||||||
|
} else
|
||||||
|
fixedY = (-maxBounds.b+.5*(cellHeight-padding-maxHeight))/scale;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (width < 0 || height < 0) {
|
if (width < 0 || height < 0) {
|
||||||
|
|
@ -420,7 +456,7 @@ int GridAtlasPacker::pack(GlyphGeometry *glyphs, int count) {
|
||||||
for (GlyphGeometry *glyph = glyphs, *end = glyphs+count; glyph < end; ++glyph) {
|
for (GlyphGeometry *glyph = glyphs, *end = glyphs+count; glyph < end; ++glyph) {
|
||||||
if (!glyph->isWhitespace()) {
|
if (!glyph->isWhitespace()) {
|
||||||
Rectangle rect = { };
|
Rectangle rect = { };
|
||||||
glyph->frameBox(scale, unitRange+pxRange/scale, miterLimit, cellWidth-padding, cellHeight-padding, hFixed ? &fixedX : nullptr, vFixed ? &fixedY : nullptr);
|
glyph->frameBox(scale, unitRange+pxRange/scale, miterLimit, cellWidth-padding, cellHeight-padding, hFixed ? &fixedX : nullptr, vFixed ? &fixedY : nullptr, pxAlignOriginX, pxAlignOriginY);
|
||||||
glyph->placeBox(col*cellWidth, height-(row+1)*cellHeight);
|
glyph->placeBox(col*cellWidth, height-(row+1)*cellHeight);
|
||||||
if (++col >= columns) {
|
if (++col >= columns) {
|
||||||
if (++row >= rows) {
|
if (++row >= rows) {
|
||||||
|
|
@ -431,9 +467,6 @@ int GridAtlasPacker::pack(GlyphGeometry *glyphs, int count) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (columns*rows < cellCount) {
|
|
||||||
// TODO return lower number
|
|
||||||
}
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -505,6 +538,14 @@ void GridAtlasPacker::setMiterLimit(double miterLimit) {
|
||||||
this->miterLimit = miterLimit;
|
this->miterLimit = miterLimit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GridAtlasPacker::setOriginPixelAlignment(bool align) {
|
||||||
|
pxAlignOriginX = align, pxAlignOriginY = align;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GridAtlasPacker::setOriginPixelAlignment(bool alignX, bool alignY) {
|
||||||
|
pxAlignOriginX = alignX, pxAlignOriginY = alignY;
|
||||||
|
}
|
||||||
|
|
||||||
void GridAtlasPacker::getDimensions(int &width, int &height) const {
|
void GridAtlasPacker::getDimensions(int &width, int &height) const {
|
||||||
width = this->width, height = this->height;
|
width = this->width, height = this->height;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,9 @@ public:
|
||||||
void setPixelRange(double pxRange);
|
void setPixelRange(double pxRange);
|
||||||
/// Sets the miter limit for bounds computation
|
/// Sets the miter limit for bounds computation
|
||||||
void setMiterLimit(double miterLimit);
|
void setMiterLimit(double miterLimit);
|
||||||
|
/// Sets whether each glyph's origin point should stay aligned with the pixel grid
|
||||||
|
void setOriginPixelAlignment(bool align);
|
||||||
|
void setOriginPixelAlignment(bool alignX, bool alignY);
|
||||||
|
|
||||||
/// Outputs the atlas's final dimensions
|
/// Outputs the atlas's final dimensions
|
||||||
void getDimensions(int &width, int &height) const;
|
void getDimensions(int &width, int &height) const;
|
||||||
|
|
@ -71,6 +74,7 @@ private:
|
||||||
double unitRange;
|
double unitRange;
|
||||||
double pxRange;
|
double pxRange;
|
||||||
double miterLimit;
|
double miterLimit;
|
||||||
|
bool pxAlignOriginX, pxAlignOriginY;
|
||||||
double scaleMaximizationTolerance;
|
double scaleMaximizationTolerance;
|
||||||
double alignedColumnsBias;
|
double alignedColumnsBias;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ TightAtlasPacker::TightAtlasPacker() :
|
||||||
unitRange(0),
|
unitRange(0),
|
||||||
pxRange(0),
|
pxRange(0),
|
||||||
miterLimit(0),
|
miterLimit(0),
|
||||||
alignOriginX(false), alignOriginY(false),
|
pxAlignOriginX(false), pxAlignOriginY(false),
|
||||||
scaleMaximizationTolerance(.001)
|
scaleMaximizationTolerance(.001)
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
|
|
@ -31,7 +31,7 @@ int TightAtlasPacker::tryPack(GlyphGeometry *glyphs, int count, DimensionsConstr
|
||||||
for (GlyphGeometry *glyph = glyphs, *end = glyphs+count; glyph < end; ++glyph) {
|
for (GlyphGeometry *glyph = glyphs, *end = glyphs+count; glyph < end; ++glyph) {
|
||||||
if (!glyph->isWhitespace()) {
|
if (!glyph->isWhitespace()) {
|
||||||
Rectangle rect = { };
|
Rectangle rect = { };
|
||||||
glyph->wrapBox(scale, range, miterLimit, alignOriginX, alignOriginY);
|
glyph->wrapBox(scale, range, miterLimit, pxAlignOriginX, pxAlignOriginY);
|
||||||
glyph->getBoxSize(rect.w, rect.h);
|
glyph->getBoxSize(rect.w, rect.h);
|
||||||
if (rect.w > 0 && rect.h > 0) {
|
if (rect.w > 0 && rect.h > 0) {
|
||||||
rectangles.push_back(rect);
|
rectangles.push_back(rect);
|
||||||
|
|
@ -157,12 +157,12 @@ void TightAtlasPacker::setMiterLimit(double miterLimit) {
|
||||||
this->miterLimit = miterLimit;
|
this->miterLimit = miterLimit;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TightAtlasPacker::setOriginAlignment(bool align) {
|
void TightAtlasPacker::setOriginPixelAlignment(bool align) {
|
||||||
alignOriginX = align, alignOriginY = align;
|
pxAlignOriginX = align, pxAlignOriginY = align;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TightAtlasPacker::setOriginAlignment(bool alignX, bool alignY) {
|
void TightAtlasPacker::setOriginPixelAlignment(bool alignX, bool alignY) {
|
||||||
alignOriginX = alignX, alignOriginY = alignY;
|
pxAlignOriginX = alignX, pxAlignOriginY = alignY;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TightAtlasPacker::getDimensions(int &width, int &height) const {
|
void TightAtlasPacker::getDimensions(int &width, int &height) const {
|
||||||
|
|
|
||||||
|
|
@ -37,8 +37,8 @@ public:
|
||||||
/// Sets the miter limit for bounds computation
|
/// Sets the miter limit for bounds computation
|
||||||
void setMiterLimit(double miterLimit);
|
void setMiterLimit(double miterLimit);
|
||||||
/// Sets whether each glyph's origin point should stay aligned with the pixel grid
|
/// Sets whether each glyph's origin point should stay aligned with the pixel grid
|
||||||
void setOriginAlignment(bool align);
|
void setOriginPixelAlignment(bool align);
|
||||||
void setOriginAlignment(bool alignX, bool alignY);
|
void setOriginPixelAlignment(bool alignX, bool alignY);
|
||||||
|
|
||||||
/// Outputs the atlas's final dimensions
|
/// Outputs the atlas's final dimensions
|
||||||
void getDimensions(int &width, int &height) const;
|
void getDimensions(int &width, int &height) const;
|
||||||
|
|
@ -56,7 +56,7 @@ private:
|
||||||
double unitRange;
|
double unitRange;
|
||||||
double pxRange;
|
double pxRange;
|
||||||
double miterLimit;
|
double miterLimit;
|
||||||
bool alignOriginX, alignOriginY;
|
bool pxAlignOriginX, pxAlignOriginY;
|
||||||
double scaleMaximizationTolerance;
|
double scaleMaximizationTolerance;
|
||||||
|
|
||||||
int tryPack(GlyphGeometry *glyphs, int count, DimensionsConstraint dimensionsConstraint, int &width, int &height, double scale) const;
|
int tryPack(GlyphGeometry *glyphs, int count, DimensionsConstraint dimensionsConstraint, int &width, int &height, double scale) const;
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
/*
|
/*
|
||||||
* MULTI-CHANNEL SIGNED DISTANCE FIELD ATLAS GENERATOR - standalone console program
|
* MULTI-CHANNEL SIGNED DISTANCE FIELD ATLAS GENERATOR - standalone console program
|
||||||
* --------------------------------------------------------------------------------
|
* --------------------------------------------------------------------------------
|
||||||
* A utility by Viktor Chlumsky, (c) 2020 - 2023
|
* A utility by Viktor Chlumsky, (c) 2020 - 2024
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifdef MSDF_ATLAS_STANDALONE
|
#ifdef MSDF_ATLAS_STANDALONE
|
||||||
|
|
@ -91,7 +91,7 @@ ATLAS CONFIGURATION
|
||||||
Sets fixed dimensions of the grid's cells.
|
Sets fixed dimensions of the grid's cells.
|
||||||
-uniformcellconstraint <none / pots / potr / square / square2 / square4>
|
-uniformcellconstraint <none / pots / potr / square / square2 / square4>
|
||||||
Constrains cell dimensions to the given rule (see -pots / ... above)
|
Constrains cell dimensions to the given rule (see -pots / ... above)
|
||||||
-uniformorigin <yes / no / horizontal / vertical>
|
-uniformorigin <off / on / horizontal / vertical>
|
||||||
Sets whether the glyph's origin point should be fixed at the same position in each cell
|
Sets whether the glyph's origin point should be fixed at the same position in each cell
|
||||||
-yorigin <bottom / top>
|
-yorigin <bottom / top>
|
||||||
Determines whether the Y-axis is oriented upwards (bottom origin, default) or downwards (top origin).
|
Determines whether the Y-axis is oriented upwards (bottom origin, default) or downwards (top origin).
|
||||||
|
|
@ -121,7 +121,7 @@ GLYPH CONFIGURATION
|
||||||
Specifies the SDF distance range in em's.
|
Specifies the SDF distance range in em's.
|
||||||
-pxrange <pixel range>
|
-pxrange <pixel range>
|
||||||
Specifies the SDF distance range in output pixels. The default value is 2.
|
Specifies the SDF distance range in output pixels. The default value is 2.
|
||||||
-pxalign <on / off / horizontal / vertical>
|
-pxalign <off / on / horizontal / vertical>
|
||||||
Specifies whether each glyph's origin point should be aligned with the pixel grid.
|
Specifies whether each glyph's origin point should be aligned with the pixel grid.
|
||||||
-nokerning
|
-nokerning
|
||||||
Disables inclusion of kerning pair table in output files.
|
Disables inclusion of kerning pair table in output files.
|
||||||
|
|
@ -262,11 +262,11 @@ struct Configuration {
|
||||||
double pxRange;
|
double pxRange;
|
||||||
double angleThreshold;
|
double angleThreshold;
|
||||||
double miterLimit;
|
double miterLimit;
|
||||||
bool alignOriginX, alignOriginY;
|
bool pxAlignOriginX, pxAlignOriginY;
|
||||||
struct {
|
struct {
|
||||||
int cellWidth, cellHeight;
|
int cellWidth, cellHeight;
|
||||||
int cols, rows;
|
int cols, rows;
|
||||||
bool hFixed, vFixed;
|
bool fixedOriginX, fixedOriginY;
|
||||||
} grid;
|
} grid;
|
||||||
void (*edgeColoring)(msdfgen::Shape &, double, unsigned long long);
|
void (*edgeColoring)(msdfgen::Shape &, double, unsigned long long);
|
||||||
bool expensiveColoring;
|
bool expensiveColoring;
|
||||||
|
|
@ -334,7 +334,7 @@ int main(int argc, const char * const *argv) {
|
||||||
config.imageType = ImageType::MSDF;
|
config.imageType = ImageType::MSDF;
|
||||||
config.imageFormat = ImageFormat::UNSPECIFIED;
|
config.imageFormat = ImageFormat::UNSPECIFIED;
|
||||||
config.yDirection = YDirection::BOTTOM_UP;
|
config.yDirection = YDirection::BOTTOM_UP;
|
||||||
config.grid.vFixed = true;
|
config.grid.fixedOriginX = false, config.grid.fixedOriginY = true;
|
||||||
config.edgeColoring = msdfgen::edgeColoringInkTrap;
|
config.edgeColoring = msdfgen::edgeColoringInkTrap;
|
||||||
config.kerning = true;
|
config.kerning = true;
|
||||||
const char *imageFormatName = nullptr;
|
const char *imageFormatName = nullptr;
|
||||||
|
|
@ -362,7 +362,7 @@ int main(int argc, const char * const *argv) {
|
||||||
DimensionsConstraint cellSizeConstraint = DimensionsConstraint::NONE;
|
DimensionsConstraint cellSizeConstraint = DimensionsConstraint::NONE;
|
||||||
config.angleThreshold = DEFAULT_ANGLE_THRESHOLD;
|
config.angleThreshold = DEFAULT_ANGLE_THRESHOLD;
|
||||||
config.miterLimit = DEFAULT_MITER_LIMIT;
|
config.miterLimit = DEFAULT_MITER_LIMIT;
|
||||||
config.alignOriginX = false, config.alignOriginY = true;
|
config.pxAlignOriginX = false, config.pxAlignOriginY = true;
|
||||||
config.threadCount = 0;
|
config.threadCount = 0;
|
||||||
|
|
||||||
// Parse command line
|
// Parse command line
|
||||||
|
|
@ -585,16 +585,16 @@ int main(int argc, const char * const *argv) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
ARG_CASE("-pxalign", 1) {
|
ARG_CASE("-pxalign", 1) {
|
||||||
if (!strcmp(argv[argPos+1], "off") || !memcmp(argv[argPos+1], "disable", 7) || !strcmp(argv[argPos+1], "0") || argv[argPos+1][0] == 'n')
|
if (!strcmp(argv[argPos+1], "off") || !memcmp(argv[argPos+1], "disable", 7) || !strcmp(argv[argPos+1], "0") || !strcmp(argv[argPos+1], "false") || argv[argPos+1][0] == 'n')
|
||||||
config.alignOriginX = false, config.alignOriginY = false;
|
config.pxAlignOriginX = false, config.pxAlignOriginY = false;
|
||||||
else if (!strcmp(argv[argPos+1], "on") || !memcmp(argv[argPos+1], "enable", 6) || !strcmp(argv[argPos+1], "1") || argv[argPos+1][0] == 'y')
|
else if (!strcmp(argv[argPos+1], "on") || !memcmp(argv[argPos+1], "enable", 6) || !strcmp(argv[argPos+1], "1") || !strcmp(argv[argPos+1], "true") || !strcmp(argv[argPos+1], "hv") || argv[argPos+1][0] == 'y')
|
||||||
config.alignOriginX = true, config.alignOriginY = true;
|
config.pxAlignOriginX = true, config.pxAlignOriginY = true;
|
||||||
else if (argv[argPos+1][0] == 'h')
|
else if (argv[argPos+1][0] == 'h')
|
||||||
config.alignOriginX = true, config.alignOriginY = false;
|
config.pxAlignOriginX = true, config.pxAlignOriginY = false;
|
||||||
else if (argv[argPos+1][0] == 'v' || !strcmp(argv[argPos+1], "baseline") || !strcmp(argv[argPos+1], "default"))
|
else if (argv[argPos+1][0] == 'v' || !strcmp(argv[argPos+1], "baseline") || !strcmp(argv[argPos+1], "default"))
|
||||||
config.alignOriginX = false, config.alignOriginY = true;
|
config.pxAlignOriginX = false, config.pxAlignOriginY = true;
|
||||||
else
|
else
|
||||||
ABORT("Unknown -pxalign setting. Use on, off, horizontal, or vertical.");
|
ABORT("Unknown -pxalign setting. Use one of: off, on, horizontal, vertical.");
|
||||||
argPos += 2;
|
argPos += 2;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
@ -650,16 +650,16 @@ int main(int argc, const char * const *argv) {
|
||||||
}
|
}
|
||||||
ARG_CASE("-uniformorigin", 1) {
|
ARG_CASE("-uniformorigin", 1) {
|
||||||
packingStyle = PackingStyle::GRID;
|
packingStyle = PackingStyle::GRID;
|
||||||
if (!strcmp(argv[argPos+1], "no") || !strcmp(argv[argPos+1], "0") || !strcmp(argv[argPos+1], "none") || !strcmp(argv[argPos+1], "false") || !strcmp(argv[argPos+1], "disabled"))
|
if (!strcmp(argv[argPos+1], "off") || !memcmp(argv[argPos+1], "disable", 7) || !strcmp(argv[argPos+1], "0") || !strcmp(argv[argPos+1], "false") || argv[argPos+1][0] == 'n')
|
||||||
config.grid.hFixed = false, config.grid.vFixed = false;
|
config.grid.fixedOriginX = false, config.grid.fixedOriginY = false;
|
||||||
else if (!strcmp(argv[argPos+1], "yes") || !strcmp(argv[argPos+1], "1") || !strcmp(argv[argPos+1], "both") || !strcmp(argv[argPos+1], "true") || !strcmp(argv[argPos+1], "enabled"))
|
else if (!strcmp(argv[argPos+1], "on") || !memcmp(argv[argPos+1], "enable", 6) || !strcmp(argv[argPos+1], "1") || !strcmp(argv[argPos+1], "true") || !strcmp(argv[argPos+1], "hv") || argv[argPos+1][0] == 'y')
|
||||||
config.grid.hFixed = true, config.grid.vFixed = true;
|
config.grid.fixedOriginX = true, config.grid.fixedOriginY = true;
|
||||||
else if (!strcmp(argv[argPos+1], "horizontal") || !strcmp(argv[argPos+1], "h"))
|
else if (argv[argPos+1][0] == 'h')
|
||||||
config.grid.hFixed = true, config.grid.vFixed = false;
|
config.grid.fixedOriginX = true, config.grid.fixedOriginY = false;
|
||||||
else if (!strcmp(argv[argPos+1], "vertical") || !strcmp(argv[argPos+1], "v") || !strcmp(argv[argPos+1], "default"))
|
else if (argv[argPos+1][0] == 'v' || !strcmp(argv[argPos+1], "baseline") || !strcmp(argv[argPos+1], "default"))
|
||||||
config.grid.hFixed = false, config.grid.vFixed = true;
|
config.grid.fixedOriginX = false, config.grid.fixedOriginY = true;
|
||||||
else
|
else
|
||||||
ABORT("Unknown option for -uniformorigin. Use one of: yes, no, horizontal, vertical.");
|
ABORT("Unknown -uniformorigin setting. Use one of: off, on, horizontal, vertical.");
|
||||||
argPos += 2;
|
argPos += 2;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
@ -889,7 +889,7 @@ int main(int argc, const char * const *argv) {
|
||||||
if (!config.arteryFontFilename) {
|
if (!config.arteryFontFilename) {
|
||||||
if (imageExtension != ImageFormat::UNSPECIFIED)
|
if (imageExtension != ImageFormat::UNSPECIFIED)
|
||||||
config.imageFormat = imageExtension;
|
config.imageFormat = imageExtension;
|
||||||
else
|
else if (config.imageFilename)
|
||||||
fputs("Warning: Could not infer image format from file extension, PNG will be used.\n", stderr);
|
fputs("Warning: Could not infer image format from file extension, PNG will be used.\n", stderr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1072,7 +1072,7 @@ int main(int argc, const char * const *argv) {
|
||||||
atlasPacker.setPixelRange(pxRange);
|
atlasPacker.setPixelRange(pxRange);
|
||||||
atlasPacker.setUnitRange(unitRange);
|
atlasPacker.setUnitRange(unitRange);
|
||||||
atlasPacker.setMiterLimit(config.miterLimit);
|
atlasPacker.setMiterLimit(config.miterLimit);
|
||||||
atlasPacker.setOriginAlignment(config.alignOriginX, config.alignOriginY);
|
atlasPacker.setOriginPixelAlignment(config.pxAlignOriginX, config.pxAlignOriginY);
|
||||||
if (int remaining = atlasPacker.pack(glyphs.data(), glyphs.size())) {
|
if (int remaining = atlasPacker.pack(glyphs.data(), glyphs.size())) {
|
||||||
if (remaining < 0) {
|
if (remaining < 0) {
|
||||||
ABORT("Failed to pack glyphs into atlas.");
|
ABORT("Failed to pack glyphs into atlas.");
|
||||||
|
|
@ -1095,7 +1095,7 @@ int main(int argc, const char * const *argv) {
|
||||||
|
|
||||||
case PackingStyle::GRID: {
|
case PackingStyle::GRID: {
|
||||||
GridAtlasPacker atlasPacker;
|
GridAtlasPacker atlasPacker;
|
||||||
atlasPacker.setFixedOrigin(config.grid.hFixed, config.grid.vFixed);
|
atlasPacker.setFixedOrigin(config.grid.fixedOriginX, config.grid.fixedOriginY);
|
||||||
if (fixedCellWidth >= 0 && fixedCellHeight >= 0)
|
if (fixedCellWidth >= 0 && fixedCellHeight >= 0)
|
||||||
atlasPacker.setCellDimensions(fixedCellWidth, fixedCellHeight);
|
atlasPacker.setCellDimensions(fixedCellWidth, fixedCellHeight);
|
||||||
else
|
else
|
||||||
|
|
@ -1114,7 +1114,7 @@ int main(int argc, const char * const *argv) {
|
||||||
atlasPacker.setPixelRange(pxRange);
|
atlasPacker.setPixelRange(pxRange);
|
||||||
atlasPacker.setUnitRange(unitRange);
|
atlasPacker.setUnitRange(unitRange);
|
||||||
atlasPacker.setMiterLimit(config.miterLimit);
|
atlasPacker.setMiterLimit(config.miterLimit);
|
||||||
//atlasPacker.setOriginAlignment(config.alignOriginX, config.alignOriginY);
|
atlasPacker.setOriginPixelAlignment(config.pxAlignOriginX, config.pxAlignOriginY);
|
||||||
if (int remaining = atlasPacker.pack(glyphs.data(), glyphs.size())) {
|
if (int remaining = atlasPacker.pack(glyphs.data(), glyphs.size())) {
|
||||||
if (remaining < 0) {
|
if (remaining < 0) {
|
||||||
ABORT("Failed to pack glyphs into atlas.");
|
ABORT("Failed to pack glyphs into atlas.");
|
||||||
|
|
@ -1133,14 +1133,14 @@ int main(int argc, const char * const *argv) {
|
||||||
config.grid.rows = atlasPacker.getRows();
|
config.grid.rows = atlasPacker.getRows();
|
||||||
if (!fixedScale)
|
if (!fixedScale)
|
||||||
printf("Glyph size: %.9g pixels/em\n", config.emSize);
|
printf("Glyph size: %.9g pixels/em\n", config.emSize);
|
||||||
if (config.grid.hFixed || config.grid.vFixed) {
|
if (config.grid.fixedOriginX || config.grid.fixedOriginY) {
|
||||||
atlasPacker.getFixedOrigin(uniformOriginX, uniformOriginY);
|
atlasPacker.getFixedOrigin(uniformOriginX, uniformOriginY);
|
||||||
printf("Grid cell origin: ");
|
printf("Grid cell origin: ");
|
||||||
if (config.grid.hFixed)
|
if (config.grid.fixedOriginX)
|
||||||
printf("X = %.9g", uniformOriginX);
|
printf("X = %.9g", uniformOriginX);
|
||||||
if (config.grid.hFixed && config.grid.vFixed)
|
if (config.grid.fixedOriginX && config.grid.fixedOriginY)
|
||||||
printf(", ");
|
printf(", ");
|
||||||
if (config.grid.vFixed) {
|
if (config.grid.fixedOriginY) {
|
||||||
switch (config.yDirection) {
|
switch (config.yDirection) {
|
||||||
case YDirection::BOTTOM_UP:
|
case YDirection::BOTTOM_UP:
|
||||||
printf("Y = %.9g", uniformOriginY);
|
printf("Y = %.9g", uniformOriginY);
|
||||||
|
|
@ -1237,9 +1237,9 @@ int main(int argc, const char * const *argv) {
|
||||||
if (packingStyle == PackingStyle::GRID) {
|
if (packingStyle == PackingStyle::GRID) {
|
||||||
gridMetrics.cellWidth = config.grid.cellWidth, gridMetrics.cellHeight = config.grid.cellHeight;
|
gridMetrics.cellWidth = config.grid.cellWidth, gridMetrics.cellHeight = config.grid.cellHeight;
|
||||||
gridMetrics.columns = config.grid.cols, gridMetrics.rows = config.grid.rows;
|
gridMetrics.columns = config.grid.cols, gridMetrics.rows = config.grid.rows;
|
||||||
if (config.grid.hFixed)
|
if (config.grid.fixedOriginX)
|
||||||
gridMetrics.originX = &uniformOriginX;
|
gridMetrics.originX = &uniformOriginX;
|
||||||
if (config.grid.vFixed)
|
if (config.grid.fixedOriginY)
|
||||||
gridMetrics.originY = &uniformOriginY;
|
gridMetrics.originY = &uniformOriginY;
|
||||||
gridMetrics.padding = padding;
|
gridMetrics.padding = padding;
|
||||||
jsonMetrics.grid = &gridMetrics;
|
jsonMetrics.grid = &gridMetrics;
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
/*
|
/*
|
||||||
* MULTI-CHANNEL SIGNED DISTANCE FIELD ATLAS GENERATOR
|
* MULTI-CHANNEL SIGNED DISTANCE FIELD ATLAS GENERATOR
|
||||||
* ---------------------------------------------------
|
* ---------------------------------------------------
|
||||||
* A utility by Viktor Chlumsky, (c) 2020 - 2023
|
* A utility by Viktor Chlumsky, (c) 2020 - 2024
|
||||||
* Generates compact bitmap font atlases using MSDFgen
|
* Generates compact bitmap font atlases using MSDFgen
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ SquareSizeSelector<MULTIPLE>::SquareSizeSelector(int minArea) : lowerBound(0), u
|
||||||
template <int MULTIPLE>
|
template <int MULTIPLE>
|
||||||
void SquareSizeSelector<MULTIPLE>::updateCurrent() {
|
void SquareSizeSelector<MULTIPLE>::updateCurrent() {
|
||||||
if (upperBound < 0)
|
if (upperBound < 0)
|
||||||
current = 5*lowerBound/4+16/MULTIPLE;
|
current = 5*lowerBound/4+16/MULTIPLE+1;
|
||||||
else
|
else
|
||||||
current = lowerBound+(upperBound-lowerBound)/2;
|
current = lowerBound+(upperBound-lowerBound)/2;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue