Origin pixel alignment per axis, CLI setting
This commit is contained in:
parent
3e73a31618
commit
ae441c9989
|
|
@ -109,6 +109,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
|
||||||
|
|
||||||
### Distance field generator settings
|
### Distance field generator settings
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -51,32 +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) {
|
void GlyphGeometry::wrapBox(double scale, double range, double miterLimit, bool alignOrigin) {
|
||||||
scale *= geometryScale;
|
wrapBox(scale, range, miterLimit, alignOrigin, alignOrigin);
|
||||||
range /= geometryScale;
|
|
||||||
box.range = range;
|
|
||||||
box.scale = scale;
|
|
||||||
if (bounds.l < bounds.r && bounds.b < bounds.t) {
|
|
||||||
double l = bounds.l, b = bounds.b, r = bounds.r, t = bounds.t;
|
|
||||||
l -= .5*range, b -= .5*range;
|
|
||||||
r += .5*range, t += .5*range;
|
|
||||||
if (miterLimit > 0)
|
|
||||||
shape.boundMiters(l, b, r, t, .5*range, miterLimit, 1);
|
|
||||||
int sl = (int) floor(scale*l-.5);
|
|
||||||
int sb = (int) floor(scale*b-.5);
|
|
||||||
int sr = (int) ceil(scale*r+.5);
|
|
||||||
int st = (int) ceil(scale*t+.5);
|
|
||||||
box.rect.w = sr-sl;
|
|
||||||
box.rect.h = st-sb;
|
|
||||||
box.translate.x = -sl/scale;
|
|
||||||
box.translate.y = -sb/scale;
|
|
||||||
} else {
|
|
||||||
box.rect.w = 0, box.rect.h = 0;
|
|
||||||
box.translate = msdfgen::Vector2();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GlyphGeometry::wrapBoxUnaligned(double scale, double range, double miterLimit) {
|
void GlyphGeometry::wrapBox(double scale, double range, double miterLimit, bool alignOriginX, bool alignOriginY) {
|
||||||
scale *= geometryScale;
|
scale *= geometryScale;
|
||||||
range /= geometryScale;
|
range /= geometryScale;
|
||||||
box.range = range;
|
box.range = range;
|
||||||
|
|
@ -87,12 +66,26 @@ void GlyphGeometry::wrapBoxUnaligned(double scale, double range, double miterLim
|
||||||
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);
|
||||||
double w = scale*(r-l);
|
if (alignOriginX) {
|
||||||
double h = scale*(t-b);
|
int sl = (int) floor(scale*l-.5);
|
||||||
box.rect.w = (int) ceil(w)+1;
|
int sr = (int) ceil(scale*r+.5);
|
||||||
box.rect.h = (int) ceil(h)+1;
|
box.rect.w = sr-sl;
|
||||||
box.translate.x = -l+.5*(box.rect.w-w)/scale;
|
box.translate.x = -sl/scale;
|
||||||
box.translate.y = -b+.5*(box.rect.h-h)/scale;
|
} else {
|
||||||
|
double w = scale*(r-l);
|
||||||
|
box.rect.w = (int) ceil(w)+1;
|
||||||
|
box.translate.x = -l+.5*(box.rect.w-w)/scale;
|
||||||
|
}
|
||||||
|
if (alignOriginY) {
|
||||||
|
int sb = (int) floor(scale*b-.5);
|
||||||
|
int st = (int) ceil(scale*t+.5);
|
||||||
|
box.rect.h = st-sb;
|
||||||
|
box.translate.y = -sb/scale;
|
||||||
|
} else {
|
||||||
|
double h = scale*(t-b);
|
||||||
|
box.rect.h = (int) ceil(h)+1;
|
||||||
|
box.translate.y = -b+.5*(box.rect.h-h)/scale;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
box.rect.w = 0, box.rect.h = 0;
|
box.rect.w = 0, box.rect.h = 0;
|
||||||
box.translate = msdfgen::Vector2();
|
box.translate = msdfgen::Vector2();
|
||||||
|
|
|
||||||
|
|
@ -20,9 +20,8 @@ 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);
|
void wrapBox(double scale, double range, double miterLimit, bool alignOrigin = false);
|
||||||
/// Computes the dimensions of the glyph's box and the transformation for the generator function, allowing origin to be located at a non-integer coordinate
|
void wrapBox(double scale, double range, double miterLimit, bool alignOriginX, bool alignOriginY);
|
||||||
void wrapBoxUnaligned(double scale, double range, double miterLimit);
|
|
||||||
/// 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
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ TightAtlasPacker::TightAtlasPacker() :
|
||||||
unitRange(0),
|
unitRange(0),
|
||||||
pxRange(0),
|
pxRange(0),
|
||||||
miterLimit(0),
|
miterLimit(0),
|
||||||
|
alignOriginX(false), alignOriginY(false),
|
||||||
scaleMaximizationTolerance(.001)
|
scaleMaximizationTolerance(.001)
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
|
|
@ -30,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);
|
glyph->wrapBox(scale, range, miterLimit, alignOriginX, alignOriginY);
|
||||||
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);
|
||||||
|
|
@ -155,6 +156,14 @@ void TightAtlasPacker::setMiterLimit(double miterLimit) {
|
||||||
this->miterLimit = miterLimit;
|
this->miterLimit = miterLimit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TightAtlasPacker::setOriginAlignment(bool align) {
|
||||||
|
alignOriginX = align, alignOriginY = align;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TightAtlasPacker::setOriginAlignment(bool alignX, bool alignY) {
|
||||||
|
alignOriginX = alignX, alignOriginY = alignY;
|
||||||
|
}
|
||||||
|
|
||||||
void TightAtlasPacker::getDimensions(int &width, int &height) const {
|
void TightAtlasPacker::getDimensions(int &width, int &height) const {
|
||||||
width = this->width, height = this->height;
|
width = this->width, height = this->height;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,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 setOriginAlignment(bool align);
|
||||||
|
void setOriginAlignment(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;
|
||||||
|
|
@ -61,6 +64,7 @@ private:
|
||||||
double unitRange;
|
double unitRange;
|
||||||
double pxRange;
|
double pxRange;
|
||||||
double miterLimit;
|
double miterLimit;
|
||||||
|
bool alignOriginX, alignOriginY;
|
||||||
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;
|
||||||
|
|
|
||||||
|
|
@ -111,6 +111,8 @@ 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>
|
||||||
|
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.
|
||||||
|
|
||||||
|
|
@ -250,6 +252,7 @@ struct Configuration {
|
||||||
double pxRange;
|
double pxRange;
|
||||||
double angleThreshold;
|
double angleThreshold;
|
||||||
double miterLimit;
|
double miterLimit;
|
||||||
|
bool alignOriginX, alignOriginY;
|
||||||
void (*edgeColoring)(msdfgen::Shape &, double, unsigned long long);
|
void (*edgeColoring)(msdfgen::Shape &, double, unsigned long long);
|
||||||
bool expensiveColoring;
|
bool expensiveColoring;
|
||||||
unsigned long long coloringSeed;
|
unsigned long long coloringSeed;
|
||||||
|
|
@ -340,6 +343,7 @@ int main(int argc, const char * const *argv) {
|
||||||
TightAtlasPacker::DimensionsConstraint atlasSizeConstraint = TightAtlasPacker::DimensionsConstraint::MULTIPLE_OF_FOUR_SQUARE;
|
TightAtlasPacker::DimensionsConstraint atlasSizeConstraint = TightAtlasPacker::DimensionsConstraint::MULTIPLE_OF_FOUR_SQUARE;
|
||||||
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.threadCount = 0;
|
config.threadCount = 0;
|
||||||
|
|
||||||
// Parse command line
|
// Parse command line
|
||||||
|
|
@ -561,6 +565,20 @@ int main(int argc, const char * const *argv) {
|
||||||
++argPos;
|
++argPos;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
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')
|
||||||
|
config.alignOriginX = false, config.alignOriginY = false;
|
||||||
|
else if (!strcmp(argv[argPos+1], "on") || !memcmp(argv[argPos+1], "enable", 6) || !strcmp(argv[argPos+1], "1") || argv[argPos+1][0] == 'y')
|
||||||
|
config.alignOriginX = true, config.alignOriginY = true;
|
||||||
|
else if (argv[argPos+1][0] == 'h')
|
||||||
|
config.alignOriginX = true, config.alignOriginY = false;
|
||||||
|
else if (argv[argPos+1][0] == 'v' || !strcmp(argv[argPos+1], "baseline") || !strcmp(argv[argPos+1], "default"))
|
||||||
|
config.alignOriginX = false, config.alignOriginY = true;
|
||||||
|
else
|
||||||
|
ABORT("Unknown -pxalign setting. Use on, off, horizontal, or vertical.");
|
||||||
|
argPos += 2;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
ARG_CASE("-angle", 1) {
|
ARG_CASE("-angle", 1) {
|
||||||
double at;
|
double at;
|
||||||
if (!parseAngle(at, argv[argPos+1]))
|
if (!parseAngle(at, argv[argPos+1]))
|
||||||
|
|
@ -571,7 +589,7 @@ int main(int argc, const char * const *argv) {
|
||||||
}
|
}
|
||||||
ARG_CASE("-errorcorrection", 1) {
|
ARG_CASE("-errorcorrection", 1) {
|
||||||
msdfgen::ErrorCorrectionConfig &ec = config.generatorAttributes.config.errorCorrection;
|
msdfgen::ErrorCorrectionConfig &ec = config.generatorAttributes.config.errorCorrection;
|
||||||
if (!strcmp(argv[argPos+1], "disabled") || !strcmp(argv[argPos+1], "0") || !strcmp(argv[argPos+1], "none")) {
|
if (!memcmp(argv[argPos+1], "disable", 7) || !strcmp(argv[argPos+1], "0") || !strcmp(argv[argPos+1], "none")) {
|
||||||
ec.mode = msdfgen::ErrorCorrectionConfig::DISABLED;
|
ec.mode = msdfgen::ErrorCorrectionConfig::DISABLED;
|
||||||
ec.distanceCheckMode = msdfgen::ErrorCorrectionConfig::DO_NOT_CHECK_DISTANCE;
|
ec.distanceCheckMode = msdfgen::ErrorCorrectionConfig::DO_NOT_CHECK_DISTANCE;
|
||||||
} else if (!strcmp(argv[argPos+1], "default") || !strcmp(argv[argPos+1], "auto") || !strcmp(argv[argPos+1], "auto-mixed") || !strcmp(argv[argPos+1], "mixed")) {
|
} else if (!strcmp(argv[argPos+1], "default") || !strcmp(argv[argPos+1], "auto") || !strcmp(argv[argPos+1], "auto-mixed") || !strcmp(argv[argPos+1], "mixed")) {
|
||||||
|
|
@ -714,7 +732,8 @@ int main(int argc, const char * const *argv) {
|
||||||
".exe"
|
".exe"
|
||||||
#endif
|
#endif
|
||||||
" -font <filename.ttf/otf> -charset <charset> <output specification> <options>\n"
|
" -font <filename.ttf/otf> -charset <charset> <output specification> <options>\n"
|
||||||
"Use -help for more information.\n", stderr
|
"Use -help for more information.\n",
|
||||||
|
stderr
|
||||||
);
|
);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
@ -789,8 +808,12 @@ int main(int argc, const char * const *argv) {
|
||||||
config.imageFormat = ImageFormat::PNG;
|
config.imageFormat = ImageFormat::PNG;
|
||||||
imageFormatName = "png";
|
imageFormatName = "png";
|
||||||
// If image format is not specified and -imageout is the only image output, infer format from its extension
|
// If image format is not specified and -imageout is the only image output, infer format from its extension
|
||||||
if (imageExtension != ImageFormat::UNSPECIFIED && !config.arteryFontFilename)
|
if (!config.arteryFontFilename) {
|
||||||
config.imageFormat = imageExtension;
|
if (imageExtension != ImageFormat::UNSPECIFIED)
|
||||||
|
config.imageFormat = imageExtension;
|
||||||
|
else
|
||||||
|
fputs("Warning: Could not infer image format from file extension, PNG will be used.\n", stderr);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (config.imageType == ImageType::MTSDF && config.imageFormat == ImageFormat::BMP)
|
if (config.imageType == ImageType::MTSDF && config.imageFormat == ImageFormat::BMP)
|
||||||
ABORT("Atlas type not compatible with image format. MTSDF requires a format with alpha channel.");
|
ABORT("Atlas type not compatible with image format. MTSDF requires a format with alpha channel.");
|
||||||
|
|
@ -966,6 +989,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);
|
||||||
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.");
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue