Padding
This commit is contained in:
parent
b67fb967c3
commit
2588a2aae3
|
|
@ -120,7 +120,11 @@ 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
|
||||||
|
- `-aemrange` / `-apxrange <outermost distance> <innermost distance>` – sets the distance field range asymmetrically by specifying the minimum and maximum representable signed distances (outside distances are negative!)
|
||||||
- `-pxalign <off / on / horizontal / vertical>` (default = vertical) – enables or disables alignment of glyph's origin point with the pixel grid
|
- `-pxalign <off / on / horizontal / vertical>` (default = vertical) – enables or disables alignment of glyph's origin point with the pixel grid
|
||||||
|
- `-empadding` / `-pxpadding <width>` – sets additional padding within each glyph's box (in em's / pixels)
|
||||||
|
- `-outerempadding` / `-outerpxpadding <width>` – sets additional padding around each glyph's box
|
||||||
|
- `-aempadding` / `-apxpadding` / `-aouterempadding` / `-aouterpxpadding <left> <bottom> <right> <top>` – sets additional padding (see above) asymmetrically with a separate width value for each side
|
||||||
|
|
||||||
### Distance field generator settings
|
### Distance field generator settings
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
Subproject commit 34134bde3cea35a93c2ae5703fa8d3d463793400
|
Subproject commit 888674220216d1d326c6f29cf89165b545279c1f
|
||||||
|
|
@ -51,22 +51,21 @@ 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 pxAlignOrigin) {
|
void GlyphGeometry::wrapBox(const GlyphAttributes &glyphAttributes) {
|
||||||
wrapBox(scale, range, miterLimit, pxAlignOrigin, pxAlignOrigin);
|
double scale = glyphAttributes.scale*geometryScale;
|
||||||
}
|
msdfgen::Range range = glyphAttributes.range/geometryScale;
|
||||||
|
Padding fullPadding = (glyphAttributes.innerPadding+glyphAttributes.outerPadding)/geometryScale;
|
||||||
void GlyphGeometry::wrapBox(double scale, double range, double miterLimit, bool pxAlignOriginX, bool pxAlignOriginY) {
|
|
||||||
scale *= geometryScale;
|
|
||||||
range /= geometryScale;
|
|
||||||
box.range = range;
|
box.range = range;
|
||||||
box.scale = scale;
|
box.scale = scale;
|
||||||
if (bounds.l < bounds.r && bounds.b < bounds.t) {
|
if (bounds.l < bounds.r && bounds.b < bounds.t) {
|
||||||
double l = bounds.l, b = bounds.b, r = bounds.r, t = bounds.t;
|
double l = bounds.l, b = bounds.b, r = bounds.r, t = bounds.t;
|
||||||
l -= .5*range, b -= .5*range;
|
l += range.lower, b += range.lower;
|
||||||
r += .5*range, t += .5*range;
|
r -= range.lower, t -= range.lower;
|
||||||
if (miterLimit > 0)
|
if (glyphAttributes.miterLimit > 0)
|
||||||
shape.boundMiters(l, b, r, t, .5*range, miterLimit, 1);
|
shape.boundMiters(l, b, r, t, -range.lower, glyphAttributes.miterLimit, 1);
|
||||||
if (pxAlignOriginX) {
|
l -= fullPadding.l, b -= fullPadding.b;
|
||||||
|
r += fullPadding.r, t += fullPadding.t;
|
||||||
|
if (glyphAttributes.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 +75,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 (pxAlignOriginY) {
|
if (glyphAttributes.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;
|
||||||
|
|
@ -86,19 +85,37 @@ void GlyphGeometry::wrapBox(double scale, double range, double miterLimit, bool
|
||||||
box.rect.h = (int) ceil(h)+1;
|
box.rect.h = (int) ceil(h)+1;
|
||||||
box.translate.y = -b+.5*(box.rect.h-h)/scale;
|
box.translate.y = -b+.5*(box.rect.h-h)/scale;
|
||||||
}
|
}
|
||||||
|
box.outerPadding = glyphAttributes.scale*glyphAttributes.outerPadding;
|
||||||
} 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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GlyphGeometry::frameBox(double scale, double range, double miterLimit, int width, int height, const double *fixedX, const double *fixedY, bool pxAlignOrigin) {
|
void GlyphGeometry::wrapBox(double scale, double range, double miterLimit, bool pxAlignOrigin) {
|
||||||
frameBox(scale, range, miterLimit, width, height, fixedX, fixedY, pxAlignOrigin, pxAlignOrigin);
|
GlyphAttributes attribs = { };
|
||||||
|
attribs.scale = scale;
|
||||||
|
attribs.range = range;
|
||||||
|
attribs.miterLimit = miterLimit;
|
||||||
|
attribs.pxAlignOriginX = pxAlignOrigin;
|
||||||
|
attribs.pxAlignOriginY = pxAlignOrigin;
|
||||||
|
wrapBox(attribs);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GlyphGeometry::frameBox(double scale, double range, double miterLimit, int width, int height, const double *fixedX, const double *fixedY, bool pxAlignOriginX, bool pxAlignOriginY) {
|
void GlyphGeometry::wrapBox(double scale, double range, double miterLimit, bool pxAlignOriginX, bool pxAlignOriginY) {
|
||||||
scale *= geometryScale;
|
GlyphAttributes attribs = { };
|
||||||
range /= geometryScale;
|
attribs.scale = scale;
|
||||||
|
attribs.range = range;
|
||||||
|
attribs.miterLimit = miterLimit;
|
||||||
|
attribs.pxAlignOriginX = pxAlignOriginX;
|
||||||
|
attribs.pxAlignOriginY = pxAlignOriginY;
|
||||||
|
wrapBox(attribs);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GlyphGeometry::frameBox(const GlyphAttributes &glyphAttributes, int width, int height, const double *fixedX, const double *fixedY) {
|
||||||
|
double scale = glyphAttributes.scale*geometryScale;
|
||||||
|
msdfgen::Range range = glyphAttributes.range/geometryScale;
|
||||||
|
Padding fullPadding = (glyphAttributes.innerPadding+glyphAttributes.outerPadding)/geometryScale;
|
||||||
box.range = range;
|
box.range = range;
|
||||||
box.scale = scale;
|
box.scale = scale;
|
||||||
box.rect.w = width;
|
box.rect.w = width;
|
||||||
|
|
@ -108,13 +125,15 @@ void GlyphGeometry::frameBox(double scale, double range, double miterLimit, int
|
||||||
box.translate.y = *fixedY/geometryScale;
|
box.translate.y = *fixedY/geometryScale;
|
||||||
} else {
|
} else {
|
||||||
double l = bounds.l, b = bounds.b, r = bounds.r, t = bounds.t;
|
double l = bounds.l, b = bounds.b, r = bounds.r, t = bounds.t;
|
||||||
l -= .5*range, b -= .5*range;
|
l += range.lower, b += range.lower;
|
||||||
r += .5*range, t += .5*range;
|
r -= range.lower, t -= range.lower;
|
||||||
if (miterLimit > 0)
|
if (glyphAttributes.miterLimit > 0)
|
||||||
shape.boundMiters(l, b, r, t, .5*range, miterLimit, 1);
|
shape.boundMiters(l, b, r, t, -range.lower, glyphAttributes.miterLimit, 1);
|
||||||
|
l -= fullPadding.l, b -= fullPadding.b;
|
||||||
|
r += fullPadding.r, t += fullPadding.t;
|
||||||
if (fixedX)
|
if (fixedX)
|
||||||
box.translate.x = *fixedX/geometryScale;
|
box.translate.x = *fixedX/geometryScale;
|
||||||
else if (pxAlignOriginX) {
|
else if (glyphAttributes.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.translate.x = (-sl+(box.rect.w-(sr-sl))/2)/scale;
|
box.translate.x = (-sl+(box.rect.w-(sr-sl))/2)/scale;
|
||||||
|
|
@ -124,7 +143,7 @@ void GlyphGeometry::frameBox(double scale, double range, double miterLimit, int
|
||||||
}
|
}
|
||||||
if (fixedY)
|
if (fixedY)
|
||||||
box.translate.y = *fixedY/geometryScale;
|
box.translate.y = *fixedY/geometryScale;
|
||||||
else if (pxAlignOriginY) {
|
else if (glyphAttributes.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.translate.y = (-sb+(box.rect.h-(st-sb))/2)/scale;
|
box.translate.y = (-sb+(box.rect.h-(st-sb))/2)/scale;
|
||||||
|
|
@ -133,6 +152,27 @@ void GlyphGeometry::frameBox(double scale, double range, double miterLimit, int
|
||||||
box.translate.y = -b+.5*(box.rect.h-h)/scale;
|
box.translate.y = -b+.5*(box.rect.h-h)/scale;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
box.outerPadding = glyphAttributes.scale*glyphAttributes.outerPadding;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GlyphGeometry::frameBox(double scale, double range, double miterLimit, int width, int height, const double *fixedX, const double *fixedY, bool pxAlignOrigin) {
|
||||||
|
GlyphAttributes attribs = { };
|
||||||
|
attribs.scale = scale;
|
||||||
|
attribs.range = range;
|
||||||
|
attribs.miterLimit = miterLimit;
|
||||||
|
attribs.pxAlignOriginX = pxAlignOrigin;
|
||||||
|
attribs.pxAlignOriginY = pxAlignOrigin;
|
||||||
|
frameBox(attribs, width, height, fixedX, fixedY);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GlyphGeometry::frameBox(double scale, double range, double miterLimit, int width, int height, const double *fixedX, const double *fixedY, bool pxAlignOriginX, bool pxAlignOriginY) {
|
||||||
|
GlyphAttributes attribs = { };
|
||||||
|
attribs.scale = scale;
|
||||||
|
attribs.range = range;
|
||||||
|
attribs.miterLimit = miterLimit;
|
||||||
|
attribs.pxAlignOriginX = pxAlignOriginX;
|
||||||
|
attribs.pxAlignOriginY = pxAlignOriginY;
|
||||||
|
frameBox(attribs, width, height, fixedX, fixedY);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GlyphGeometry::placeBox(int x, int y) {
|
void GlyphGeometry::placeBox(int x, int y) {
|
||||||
|
|
@ -194,7 +234,7 @@ void GlyphGeometry::getBoxSize(int &w, int &h) const {
|
||||||
w = box.rect.w, h = box.rect.h;
|
w = box.rect.w, h = box.rect.h;
|
||||||
}
|
}
|
||||||
|
|
||||||
double GlyphGeometry::getBoxRange() const {
|
msdfgen::Range GlyphGeometry::getBoxRange() const {
|
||||||
return box.range;
|
return box.range;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -213,20 +253,20 @@ msdfgen::Vector2 GlyphGeometry::getBoxTranslate() const {
|
||||||
void GlyphGeometry::getQuadPlaneBounds(double &l, double &b, double &r, double &t) const {
|
void GlyphGeometry::getQuadPlaneBounds(double &l, double &b, double &r, double &t) const {
|
||||||
if (box.rect.w > 0 && box.rect.h > 0) {
|
if (box.rect.w > 0 && box.rect.h > 0) {
|
||||||
double invBoxScale = 1/box.scale;
|
double invBoxScale = 1/box.scale;
|
||||||
l = geometryScale*(-box.translate.x+.5*invBoxScale);
|
l = geometryScale*(-box.translate.x+(box.outerPadding.l+.5)*invBoxScale);
|
||||||
b = geometryScale*(-box.translate.y+.5*invBoxScale);
|
b = geometryScale*(-box.translate.y+(box.outerPadding.b+.5)*invBoxScale);
|
||||||
r = geometryScale*(-box.translate.x+(box.rect.w-.5)*invBoxScale);
|
r = geometryScale*(-box.translate.x+(-box.outerPadding.r+box.rect.w-.5)*invBoxScale);
|
||||||
t = geometryScale*(-box.translate.y+(box.rect.h-.5)*invBoxScale);
|
t = geometryScale*(-box.translate.y+(-box.outerPadding.t+box.rect.h-.5)*invBoxScale);
|
||||||
} else
|
} else
|
||||||
l = 0, b = 0, r = 0, t = 0;
|
l = 0, b = 0, r = 0, t = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GlyphGeometry::getQuadAtlasBounds(double &l, double &b, double &r, double &t) const {
|
void GlyphGeometry::getQuadAtlasBounds(double &l, double &b, double &r, double &t) const {
|
||||||
if (box.rect.w > 0 && box.rect.h > 0) {
|
if (box.rect.w > 0 && box.rect.h > 0) {
|
||||||
l = box.rect.x+.5;
|
l = box.rect.x+box.outerPadding.l+.5;
|
||||||
b = box.rect.y+.5;
|
b = box.rect.y+box.outerPadding.b+.5;
|
||||||
r = box.rect.x+box.rect.w-.5;
|
r = box.rect.x-box.outerPadding.r+box.rect.w-.5;
|
||||||
t = box.rect.y+box.rect.h-.5;
|
t = box.rect.y-box.outerPadding.t+box.rect.h-.5;
|
||||||
} else
|
} else
|
||||||
l = 0, b = 0, r = 0, t = 0;
|
l = 0, b = 0, r = 0, t = 0;
|
||||||
}
|
}
|
||||||
|
|
@ -244,4 +284,8 @@ GlyphGeometry::operator GlyphBox() const {
|
||||||
return box;
|
return box;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
msdfgen::Range operator+(msdfgen::Range a, msdfgen::Range b) {
|
||||||
|
return msdfgen::Range(a.lower+b.lower, a.upper+b.upper);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@
|
||||||
#include <msdfgen-ext.h>
|
#include <msdfgen-ext.h>
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
#include "Rectangle.h"
|
#include "Rectangle.h"
|
||||||
|
#include "Padding.h"
|
||||||
#include "GlyphBox.h"
|
#include "GlyphBox.h"
|
||||||
|
|
||||||
namespace msdf_atlas {
|
namespace msdf_atlas {
|
||||||
|
|
@ -13,6 +14,14 @@ namespace msdf_atlas {
|
||||||
class GlyphGeometry {
|
class GlyphGeometry {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
struct GlyphAttributes {
|
||||||
|
double scale;
|
||||||
|
msdfgen::Range range;
|
||||||
|
Padding innerPadding, outerPadding;
|
||||||
|
double miterLimit;
|
||||||
|
bool pxAlignOriginX, pxAlignOriginY;
|
||||||
|
};
|
||||||
|
|
||||||
GlyphGeometry();
|
GlyphGeometry();
|
||||||
/// Loads glyph geometry from font
|
/// 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, msdfgen::GlyphIndex index, bool preprocessGeometry = true);
|
||||||
|
|
@ -20,9 +29,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(const GlyphAttributes &glyphAttributes);
|
||||||
void wrapBox(double scale, double range, double miterLimit, bool pxAlignOrigin = false);
|
void wrapBox(double scale, double range, double miterLimit, bool pxAlignOrigin = false);
|
||||||
void wrapBox(double scale, double range, double miterLimit, bool pxAlignOriginX, bool pxAlignOriginY);
|
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(const GlyphAttributes &glyphAttributes, 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 pxAlignOrigin = false);
|
||||||
void frameBox(double scale, double range, double miterLimit, int width, int height, const double *fixedX, const double *fixedY, bool pxAlignOriginX, bool pxAlignOriginY);
|
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
|
||||||
|
|
@ -52,7 +63,7 @@ public:
|
||||||
/// Outputs the dimensions of the glyph's box in the atlas
|
/// Outputs the dimensions of the glyph's box in the atlas
|
||||||
void getBoxSize(int &w, int &h) const;
|
void getBoxSize(int &w, int &h) const;
|
||||||
/// Returns the range needed to generate the glyph's SDF
|
/// Returns the range needed to generate the glyph's SDF
|
||||||
double getBoxRange() const;
|
msdfgen::Range getBoxRange() const;
|
||||||
/// Returns the projection needed to generate the glyph's bitmap
|
/// Returns the projection needed to generate the glyph's bitmap
|
||||||
msdfgen::Projection getBoxProjection() const;
|
msdfgen::Projection getBoxProjection() const;
|
||||||
/// Returns the scale needed to generate the glyph's bitmap
|
/// Returns the scale needed to generate the glyph's bitmap
|
||||||
|
|
@ -77,11 +88,14 @@ private:
|
||||||
double advance;
|
double advance;
|
||||||
struct {
|
struct {
|
||||||
Rectangle rect;
|
Rectangle rect;
|
||||||
double range;
|
msdfgen::Range range;
|
||||||
double scale;
|
double scale;
|
||||||
msdfgen::Vector2 translate;
|
msdfgen::Vector2 translate;
|
||||||
|
Padding outerPadding;
|
||||||
} box;
|
} box;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
msdfgen::Range operator+(msdfgen::Range a, msdfgen::Range b);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -107,20 +107,20 @@ GridAtlasPacker::GridAtlasPacker() :
|
||||||
cutoff(false)
|
cutoff(false)
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
msdfgen::Shape::Bounds GridAtlasPacker::getMaxBounds(double &maxWidth, double &maxHeight, GlyphGeometry *glyphs, int count, double scale, double range) const {
|
msdfgen::Shape::Bounds GridAtlasPacker::getMaxBounds(double &maxWidth, double &maxHeight, GlyphGeometry *glyphs, int count, double scale, double outerRange) const {
|
||||||
static const double LARGE_VALUE = 1e240;
|
static const double LARGE_VALUE = 1e240;
|
||||||
msdfgen::Shape::Bounds maxBounds = { +LARGE_VALUE, +LARGE_VALUE, -LARGE_VALUE, -LARGE_VALUE };
|
msdfgen::Shape::Bounds maxBounds = { +LARGE_VALUE, +LARGE_VALUE, -LARGE_VALUE, -LARGE_VALUE };
|
||||||
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()) {
|
||||||
double geometryScale = glyph->getGeometryScale();
|
double geometryScale = glyph->getGeometryScale();
|
||||||
double shapeRange = range/geometryScale;
|
double shapeOuterRange = outerRange/geometryScale;
|
||||||
geometryScale *= scale;
|
geometryScale *= scale;
|
||||||
const msdfgen::Shape::Bounds &shapeBounds = glyph->getShapeBounds();
|
const msdfgen::Shape::Bounds &shapeBounds = glyph->getShapeBounds();
|
||||||
double l = shapeBounds.l, b = shapeBounds.b, r = shapeBounds.r, t = shapeBounds.t;
|
double l = shapeBounds.l, b = shapeBounds.b, r = shapeBounds.r, t = shapeBounds.t;
|
||||||
l -= .5*shapeRange, b -= .5*shapeRange;
|
l -= shapeOuterRange, b -= shapeOuterRange;
|
||||||
r += .5*shapeRange, t += .5*shapeRange;
|
r += shapeOuterRange, t += shapeOuterRange;
|
||||||
if (miterLimit > 0)
|
if (miterLimit > 0)
|
||||||
glyph->getShape().boundMiters(l, b, r, t, .5*shapeRange, miterLimit, 1);
|
glyph->getShape().boundMiters(l, b, r, t, shapeOuterRange, miterLimit, 1);
|
||||||
l *= geometryScale, b *= geometryScale;
|
l *= geometryScale, b *= geometryScale;
|
||||||
r *= geometryScale, t *= geometryScale;
|
r *= geometryScale, t *= geometryScale;
|
||||||
maxBounds.l = std::min(maxBounds.l, l);
|
maxBounds.l = std::min(maxBounds.l, l);
|
||||||
|
|
@ -133,6 +133,10 @@ 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();
|
||||||
|
Padding fullPadding = scale*(innerUnitPadding+outerUnitPadding)+innerPxPadding+outerPxPadding;
|
||||||
|
pad(maxBounds, fullPadding);
|
||||||
|
maxWidth += fullPadding.l+fullPadding.r;
|
||||||
|
maxHeight += fullPadding.b+fullPadding.t;
|
||||||
// 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 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;
|
||||||
|
|
@ -154,7 +158,7 @@ double GridAtlasPacker::scaleToFit(GlyphGeometry *glyphs, int count, int cellWid
|
||||||
--cellWidth, --cellHeight; // Implicit half-pixel padding from each side to make sure that no representable values are beyond outermost pixel centers
|
--cellWidth, --cellHeight; // Implicit half-pixel padding from each side to make sure that no representable values are beyond outermost pixel centers
|
||||||
cellWidth -= spacing, cellHeight -= spacing;
|
cellWidth -= spacing, cellHeight -= spacing;
|
||||||
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.lower+pxRange.lower/(scale))), lastResult = maxWidth <= cellWidth && maxHeight <= cellHeight)
|
||||||
double minScale = 1, maxScale = 1;
|
double minScale = 1, maxScale = 1;
|
||||||
if (TRY_FIT(1)) {
|
if (TRY_FIT(1)) {
|
||||||
while (maxScale < 1e+32 && ((maxScale = 2*minScale), TRY_FIT(maxScale)))
|
while (maxScale < 1e+32 && ((maxScale = 2*minScale), TRY_FIT(maxScale)))
|
||||||
|
|
@ -220,7 +224,7 @@ int GridAtlasPacker::pack(GlyphGeometry *glyphs, int count) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((cellWidth > 0 && cellWidth-spacing-1 <= pxRange) || (cellHeight > 0 && cellHeight-spacing-1 <= pxRange)) // cells definitely too small
|
if ((cellWidth > 0 && cellWidth-spacing-1 <= -2*pxRange.lower) || (cellHeight > 0 && cellHeight-spacing-1 <= -2*pxRange.lower)) // cells definitely too small
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
msdfgen::Shape::Bounds maxBounds = { };
|
msdfgen::Shape::Bounds maxBounds = { };
|
||||||
|
|
@ -229,14 +233,14 @@ int GridAtlasPacker::pack(GlyphGeometry *glyphs, int count) {
|
||||||
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.lower != pxRange.upper && miterLimit > 0) {
|
||||||
|
|
||||||
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);
|
||||||
if (scale < minScale) {
|
if (scale < minScale) {
|
||||||
scale = minScale;
|
scale = minScale;
|
||||||
cutoff = true;
|
cutoff = true;
|
||||||
maxBounds = getMaxBounds(maxWidth, maxHeight, glyphs, count, scale, unitRange+pxRange/scale);
|
maxBounds = getMaxBounds(maxWidth, maxHeight, glyphs, count, scale, -(unitRange.lower+pxRange.lower/scale));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -296,13 +300,13 @@ int GridAtlasPacker::pack(GlyphGeometry *glyphs, int count) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (scale <= 0) {
|
if (scale <= 0) {
|
||||||
maxBounds = getMaxBounds(maxWidth, maxHeight, glyphs, count, minScale, unitRange+pxRange/minScale);
|
maxBounds = getMaxBounds(maxWidth, maxHeight, glyphs, count, minScale, -(unitRange.lower+pxRange.lower/minScale));
|
||||||
cellWidth = (int) ceil(maxWidth)+spacing+1;
|
cellWidth = (int) ceil(maxWidth)+spacing+1;
|
||||||
cellHeight = (int) ceil(maxHeight)+spacing+1;
|
cellHeight = (int) ceil(maxHeight)+spacing+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)
|
||||||
maxBounds = getMaxBounds(maxWidth, maxHeight, glyphs, count, scale = minScale, unitRange+pxRange/minScale);
|
maxBounds = getMaxBounds(maxWidth, maxHeight, glyphs, count, scale = minScale, -(unitRange.lower+pxRange.lower/minScale));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (initial.rows < 0 && initial.cellHeight < 0) {
|
if (initial.rows < 0 && initial.cellHeight < 0) {
|
||||||
|
|
@ -316,7 +320,12 @@ int GridAtlasPacker::pack(GlyphGeometry *glyphs, int count) {
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
maxBounds = getMaxBounds(maxWidth, maxHeight, glyphs, count, 1, unitRange);
|
Padding pxPadding = innerPxPadding+outerPxPadding;
|
||||||
|
maxBounds = getMaxBounds(maxWidth, maxHeight, glyphs, count, 1, -unitRange.lower);
|
||||||
|
// Undo pxPadding added by getMaxBounds before pixel scale is known
|
||||||
|
pad(maxBounds, -pxPadding);
|
||||||
|
maxWidth -= pxPadding.l+pxPadding.r;
|
||||||
|
maxHeight -= pxPadding.b+pxPadding.t;
|
||||||
int hSlack = 0, vSlack = 0;
|
int hSlack = 0, vSlack = 0;
|
||||||
if (pxAlignOriginX && !hFixed) {
|
if (pxAlignOriginX && !hFixed) {
|
||||||
maxWidth -= 1; // Added by getMaxBounds
|
maxWidth -= 1; // Added by getMaxBounds
|
||||||
|
|
@ -327,11 +336,13 @@ int GridAtlasPacker::pack(GlyphGeometry *glyphs, int count) {
|
||||||
vSlack = 1;
|
vSlack = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
double extraPxWidth = -2*pxRange.lower+pxPadding.l+pxPadding.r;
|
||||||
|
double extraPxHeight = -2*pxRange.lower+pxPadding.b+pxPadding.t;
|
||||||
double hScale = 0, vScale = 0;
|
double hScale = 0, vScale = 0;
|
||||||
if (cellWidth > 0)
|
if (cellWidth > 0)
|
||||||
hScale = (cellWidth-hSlack-spacing-1-pxRange)/maxWidth;
|
hScale = (cellWidth-hSlack-spacing-extraPxWidth-1)/maxWidth;
|
||||||
if (cellHeight > 0)
|
if (cellHeight > 0)
|
||||||
vScale = (cellHeight-vSlack-spacing-1-pxRange)/maxHeight;
|
vScale = (cellHeight-vSlack-spacing-extraPxHeight-1)/maxHeight;
|
||||||
if (hScale || vScale) {
|
if (hScale || vScale) {
|
||||||
if (hScale && vScale)
|
if (hScale && vScale)
|
||||||
scale = std::min(hScale, vScale);
|
scale = std::min(hScale, vScale);
|
||||||
|
|
@ -353,8 +364,8 @@ int GridAtlasPacker::pack(GlyphGeometry *glyphs, int count) {
|
||||||
int tHeight = (height+spacing)/rows;
|
int tHeight = (height+spacing)/rows;
|
||||||
lowerToConstraint(tWidth, tHeight, cellDimensionsConstraint);
|
lowerToConstraint(tWidth, tHeight, cellDimensionsConstraint);
|
||||||
if (tWidth > 0 && tHeight > 0) {
|
if (tWidth > 0 && tHeight > 0) {
|
||||||
hScale = (tWidth-hSlack-spacing-1-pxRange)/maxWidth;
|
hScale = (tWidth-hSlack-spacing-extraPxWidth-1)/maxWidth;
|
||||||
vScale = (tHeight-vSlack-spacing-1-pxRange)/maxHeight;
|
vScale = (tHeight-vSlack-spacing-extraPxHeight-1)/maxHeight;
|
||||||
double curScale = std::min(hScale, vScale);
|
double curScale = std::min(hScale, vScale);
|
||||||
if (curScale > scale) {
|
if (curScale > scale) {
|
||||||
scale = curScale;
|
scale = curScale;
|
||||||
|
|
@ -384,16 +395,16 @@ int GridAtlasPacker::pack(GlyphGeometry *glyphs, int count) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (scale <= 0) {
|
if (scale <= 0) {
|
||||||
cellWidth = (int) ceil(minScale*maxWidth+pxRange)+hSlack+spacing+1;
|
cellWidth = (int) ceil(minScale*maxWidth+extraPxWidth)+hSlack+spacing+1;
|
||||||
cellHeight = (int) ceil(minScale*maxHeight+pxRange)+vSlack+spacing+1;
|
cellHeight = (int) ceil(minScale*maxHeight+extraPxHeight)+vSlack+spacing+1;
|
||||||
raiseToConstraint(cellWidth, cellHeight, cellDimensionsConstraint);
|
raiseToConstraint(cellWidth, cellHeight, cellDimensionsConstraint);
|
||||||
hScale = (cellWidth-hSlack-spacing-1-pxRange)/maxWidth;
|
hScale = (cellWidth-hSlack-spacing-extraPxWidth-1)/maxWidth;
|
||||||
vScale = (cellHeight-vSlack-spacing-1-pxRange)/maxHeight;
|
vScale = (cellHeight-vSlack-spacing-extraPxHeight-1)/maxHeight;
|
||||||
scale = std::min(hScale, vScale);
|
scale = std::min(hScale, vScale);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (initial.rows < 0 && initial.cellHeight < 0) {
|
if (initial.rows < 0 && initial.cellHeight < 0) {
|
||||||
int optimalCellWidth = cellWidth, optimalCellHeight = (int) ceil(scale*maxHeight+pxRange)+vSlack+spacing+1;
|
int optimalCellWidth = cellWidth, optimalCellHeight = (int) ceil(scale*maxHeight+extraPxHeight)+vSlack+spacing+1;
|
||||||
raiseToConstraint(optimalCellWidth, optimalCellHeight, cellDimensionsConstraint);
|
raiseToConstraint(optimalCellWidth, optimalCellHeight, cellDimensionsConstraint);
|
||||||
if (optimalCellHeight < cellHeight && optimalCellWidth <= cellWidth) {
|
if (optimalCellHeight < cellHeight && optimalCellWidth <= cellWidth) {
|
||||||
cellWidth = optimalCellWidth;
|
cellWidth = optimalCellWidth;
|
||||||
|
|
@ -403,11 +414,15 @@ int GridAtlasPacker::pack(GlyphGeometry *glyphs, int count) {
|
||||||
maxBounds.l *= scale, maxBounds.b *= scale;
|
maxBounds.l *= scale, maxBounds.b *= scale;
|
||||||
maxBounds.r *= scale, maxBounds.t *= scale;
|
maxBounds.r *= scale, maxBounds.t *= scale;
|
||||||
maxWidth *= scale, maxHeight *= scale;
|
maxWidth *= scale, maxHeight *= scale;
|
||||||
|
// Redo addition of pxPadding once scale is known
|
||||||
|
pad(maxBounds, pxPadding);
|
||||||
|
maxWidth += pxPadding.l+pxPadding.r;
|
||||||
|
maxHeight += pxPadding.b+pxPadding.t;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
maxBounds = getMaxBounds(maxWidth, maxHeight, glyphs, count, scale, unitRange+pxRange/scale);
|
maxBounds = getMaxBounds(maxWidth, maxHeight, glyphs, count, scale, -(unitRange.lower+pxRange.lower/scale));
|
||||||
int optimalCellWidth = (int) ceil(maxWidth)+spacing+1;
|
int optimalCellWidth = (int) ceil(maxWidth)+spacing+1;
|
||||||
int optimalCellHeight = (int) ceil(maxHeight)+spacing+1;
|
int optimalCellHeight = (int) ceil(maxHeight)+spacing+1;
|
||||||
if (cellWidth < 0 || cellHeight < 0) {
|
if (cellWidth < 0 || cellHeight < 0) {
|
||||||
|
|
@ -481,10 +496,18 @@ int GridAtlasPacker::pack(GlyphGeometry *glyphs, int count) {
|
||||||
if (rows*cellHeight > height)
|
if (rows*cellHeight > height)
|
||||||
rows = height/cellHeight;
|
rows = height/cellHeight;
|
||||||
|
|
||||||
|
GlyphGeometry::GlyphAttributes attribs = { };
|
||||||
|
attribs.scale = scale;
|
||||||
|
attribs.range = unitRange+pxRange/scale;
|
||||||
|
attribs.innerPadding = innerUnitPadding+1/scale*innerPxPadding;
|
||||||
|
attribs.outerPadding = outerUnitPadding+1/scale*outerPxPadding;
|
||||||
|
attribs.miterLimit = miterLimit;
|
||||||
|
attribs.pxAlignOriginX = pxAlignOriginX;
|
||||||
|
attribs.pxAlignOriginY = pxAlignOriginY;
|
||||||
int col = 0, row = 0;
|
int col = 0, row = 0;
|
||||||
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()) {
|
||||||
glyph->frameBox(scale, unitRange+pxRange/scale, miterLimit, cellWidth-spacing, cellHeight-spacing, hFixed ? &fixedX : nullptr, vFixed ? &fixedY : nullptr, pxAlignOriginX, pxAlignOriginY);
|
glyph->frameBox(attribs, cellWidth-spacing, cellHeight-spacing, hFixed ? &fixedX : nullptr, vFixed ? &fixedY : nullptr);
|
||||||
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) {
|
||||||
|
|
@ -554,11 +577,11 @@ void GridAtlasPacker::setMinimumScale(double minScale) {
|
||||||
this->minScale = minScale;
|
this->minScale = minScale;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GridAtlasPacker::setUnitRange(double unitRange) {
|
void GridAtlasPacker::setUnitRange(msdfgen::Range unitRange) {
|
||||||
this->unitRange = unitRange;
|
this->unitRange = unitRange;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GridAtlasPacker::setPixelRange(double pxRange) {
|
void GridAtlasPacker::setPixelRange(msdfgen::Range pxRange) {
|
||||||
this->pxRange = pxRange;
|
this->pxRange = pxRange;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -574,6 +597,22 @@ void GridAtlasPacker::setOriginPixelAlignment(bool alignX, bool alignY) {
|
||||||
pxAlignOriginX = alignX, pxAlignOriginY = alignY;
|
pxAlignOriginX = alignX, pxAlignOriginY = alignY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GridAtlasPacker::setInnerUnitPadding(const Padding &padding) {
|
||||||
|
innerUnitPadding = padding;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GridAtlasPacker::setOuterUnitPadding(const Padding &padding) {
|
||||||
|
outerUnitPadding = padding;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GridAtlasPacker::setInnerPixelPadding(const Padding &padding) {
|
||||||
|
innerPxPadding = padding;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GridAtlasPacker::setOuterPixelPadding(const Padding &padding) {
|
||||||
|
outerPxPadding = padding;
|
||||||
|
}
|
||||||
|
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
@ -594,7 +633,7 @@ double GridAtlasPacker::getScale() const {
|
||||||
return scale;
|
return scale;
|
||||||
}
|
}
|
||||||
|
|
||||||
double GridAtlasPacker::getPixelRange() const {
|
msdfgen::Range GridAtlasPacker::getPixelRange() const {
|
||||||
return pxRange+scale*unitRange;
|
return pxRange+scale*unitRange;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "Padding.h"
|
||||||
#include "GlyphGeometry.h"
|
#include "GlyphGeometry.h"
|
||||||
|
|
||||||
namespace msdf_atlas {
|
namespace msdf_atlas {
|
||||||
|
|
@ -40,14 +41,22 @@ public:
|
||||||
/// Sets the minimum glyph scale
|
/// Sets the minimum glyph scale
|
||||||
void setMinimumScale(double minScale);
|
void setMinimumScale(double minScale);
|
||||||
/// Sets the unit component of the total distance range
|
/// Sets the unit component of the total distance range
|
||||||
void setUnitRange(double unitRange);
|
void setUnitRange(msdfgen::Range unitRange);
|
||||||
/// Sets the pixel component of the total distance range
|
/// Sets the pixel component of the total distance range
|
||||||
void setPixelRange(double pxRange);
|
void setPixelRange(msdfgen::Range 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
|
/// Sets whether each glyph's origin point should stay aligned with the pixel grid
|
||||||
void setOriginPixelAlignment(bool align);
|
void setOriginPixelAlignment(bool align);
|
||||||
void setOriginPixelAlignment(bool alignX, bool alignY);
|
void setOriginPixelAlignment(bool alignX, bool alignY);
|
||||||
|
/// Sets the unit component of width of additional padding that is part of each glyph quad
|
||||||
|
void setInnerUnitPadding(const Padding &padding);
|
||||||
|
/// Sets the unit component of width of additional padding around each glyph quad
|
||||||
|
void setOuterUnitPadding(const Padding &padding);
|
||||||
|
/// Sets the pixel component of width of additional padding that is part of each glyph quad
|
||||||
|
void setInnerPixelPadding(const Padding &padding);
|
||||||
|
/// Sets the pixel component of width of additional padding around each glyph quad
|
||||||
|
void setOuterPixelPadding(const Padding &padding);
|
||||||
|
|
||||||
/// 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;
|
||||||
|
|
@ -60,7 +69,7 @@ public:
|
||||||
/// Returns the final glyph scale
|
/// Returns the final glyph scale
|
||||||
double getScale() const;
|
double getScale() const;
|
||||||
/// Returns the final combined pixel range (including converted unit range)
|
/// Returns the final combined pixel range (including converted unit range)
|
||||||
double getPixelRange() const;
|
msdfgen::Range getPixelRange() const;
|
||||||
/// Outputs the position of the origin within each cell, each value is only valid if the origin is fixed in the respective dimension
|
/// Outputs the position of the origin within each cell, each value is only valid if the origin is fixed in the respective dimension
|
||||||
void getFixedOrigin(double &x, double &y);
|
void getFixedOrigin(double &x, double &y);
|
||||||
/// Returns true if the explicitly constrained cell dimensions aren't large enough to fit each glyph fully
|
/// Returns true if the explicitly constrained cell dimensions aren't large enough to fit each glyph fully
|
||||||
|
|
@ -77,10 +86,12 @@ private:
|
||||||
double scale;
|
double scale;
|
||||||
double minScale;
|
double minScale;
|
||||||
double fixedX, fixedY;
|
double fixedX, fixedY;
|
||||||
double unitRange;
|
msdfgen::Range unitRange;
|
||||||
double pxRange;
|
msdfgen::Range pxRange;
|
||||||
double miterLimit;
|
double miterLimit;
|
||||||
bool pxAlignOriginX, pxAlignOriginY;
|
bool pxAlignOriginX, pxAlignOriginY;
|
||||||
|
Padding innerUnitPadding, outerUnitPadding;
|
||||||
|
Padding innerPxPadding, outerPxPadding;
|
||||||
double scaleMaximizationTolerance;
|
double scaleMaximizationTolerance;
|
||||||
double alignedColumnsBias;
|
double alignedColumnsBias;
|
||||||
bool cutoff;
|
bool cutoff;
|
||||||
|
|
@ -89,7 +100,7 @@ private:
|
||||||
static void raiseToConstraint(int &width, int &height, DimensionsConstraint constraint);
|
static void raiseToConstraint(int &width, int &height, DimensionsConstraint constraint);
|
||||||
|
|
||||||
double dimensionsRating(int width, int height, bool aligned) const;
|
double dimensionsRating(int width, int height, bool aligned) const;
|
||||||
msdfgen::Shape::Bounds getMaxBounds(double &maxWidth, double &maxHeight, GlyphGeometry *glyphs, int count, double scale, double range) const;
|
msdfgen::Shape::Bounds getMaxBounds(double &maxWidth, double &maxHeight, GlyphGeometry *glyphs, int count, double scale, double outerRange) const;
|
||||||
double scaleToFit(GlyphGeometry *glyphs, int count, int cellWidth, int cellHeight, msdfgen::Shape::Bounds &maxBounds, double &maxWidth, double &maxHeight) const;
|
double scaleToFit(GlyphGeometry *glyphs, int count, int cellWidth, int cellHeight, msdfgen::Shape::Bounds &maxBounds, double &maxWidth, double &maxHeight) const;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
|
||||||
|
#include "Padding.h"
|
||||||
|
|
||||||
|
namespace msdf_atlas {
|
||||||
|
|
||||||
|
void pad(msdfgen::Shape::Bounds &bounds, const Padding &padding) {
|
||||||
|
bounds.l -= padding.l;
|
||||||
|
bounds.b -= padding.b;
|
||||||
|
bounds.r += padding.r;
|
||||||
|
bounds.t += padding.t;
|
||||||
|
}
|
||||||
|
|
||||||
|
Padding operator-(const Padding &padding) {
|
||||||
|
return Padding(-padding.l, -padding.b, -padding.r, -padding.t);
|
||||||
|
}
|
||||||
|
|
||||||
|
Padding operator+(const Padding &a, const Padding &b) {
|
||||||
|
return Padding(a.l+b.l, a.b+b.b, a.r+b.r, a.t+b.t);
|
||||||
|
}
|
||||||
|
|
||||||
|
Padding operator-(const Padding &a, const Padding &b) {
|
||||||
|
return Padding(a.l-b.l, a.b-b.b, a.r-b.r, a.t-b.t);
|
||||||
|
}
|
||||||
|
|
||||||
|
Padding operator*(double factor, const Padding &padding) {
|
||||||
|
return Padding(factor*padding.l, factor*padding.b, factor*padding.r, factor*padding.t);
|
||||||
|
}
|
||||||
|
|
||||||
|
Padding operator*(const Padding &padding, double factor) {
|
||||||
|
return Padding(padding.l*factor, padding.b*factor, padding.r*factor, padding.t*factor);
|
||||||
|
}
|
||||||
|
|
||||||
|
Padding operator/(const Padding &padding, double divisor) {
|
||||||
|
return Padding(padding.l/divisor, padding.b/divisor, padding.r/divisor, padding.t/divisor);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <msdfgen.h>
|
||||||
|
|
||||||
|
namespace msdf_atlas {
|
||||||
|
|
||||||
|
struct Padding {
|
||||||
|
double l, b, r, t;
|
||||||
|
|
||||||
|
inline Padding(double uniformPadding = 0) : l(uniformPadding), b(uniformPadding), r(uniformPadding), t(uniformPadding) { }
|
||||||
|
inline Padding(double l, double b, double r, double t) : l(l), b(b), r(r), t(t) { }
|
||||||
|
};
|
||||||
|
|
||||||
|
void pad(msdfgen::Shape::Bounds &bounds, const Padding &padding);
|
||||||
|
|
||||||
|
Padding operator-(const Padding &padding);
|
||||||
|
Padding operator+(const Padding &a, const Padding &b);
|
||||||
|
Padding operator-(const Padding &a, const Padding &b);
|
||||||
|
Padding operator*(double factor, const Padding &padding);
|
||||||
|
Padding operator*(const Padding &padding, double factor);
|
||||||
|
Padding operator/(const Padding &padding, double divisor);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -22,16 +22,23 @@ TightAtlasPacker::TightAtlasPacker() :
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
int TightAtlasPacker::tryPack(GlyphGeometry *glyphs, int count, DimensionsConstraint dimensionsConstraint, int &width, int &height, double scale) const {
|
int TightAtlasPacker::tryPack(GlyphGeometry *glyphs, int count, DimensionsConstraint dimensionsConstraint, int &width, int &height, double scale) const {
|
||||||
double range = unitRange+pxRange/scale;
|
|
||||||
// Wrap glyphs into boxes
|
// Wrap glyphs into boxes
|
||||||
std::vector<Rectangle> rectangles;
|
std::vector<Rectangle> rectangles;
|
||||||
std::vector<GlyphGeometry *> rectangleGlyphs;
|
std::vector<GlyphGeometry *> rectangleGlyphs;
|
||||||
rectangles.reserve(count);
|
rectangles.reserve(count);
|
||||||
rectangleGlyphs.reserve(count);
|
rectangleGlyphs.reserve(count);
|
||||||
|
GlyphGeometry::GlyphAttributes attribs = { };
|
||||||
|
attribs.scale = scale;
|
||||||
|
attribs.range = unitRange+pxRange/scale;
|
||||||
|
attribs.innerPadding = innerUnitPadding+innerPxPadding/scale;
|
||||||
|
attribs.outerPadding = outerUnitPadding+outerPxPadding/scale;
|
||||||
|
attribs.miterLimit = miterLimit;
|
||||||
|
attribs.pxAlignOriginX = pxAlignOriginX;
|
||||||
|
attribs.pxAlignOriginY = pxAlignOriginY;
|
||||||
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, pxAlignOriginX, pxAlignOriginY);
|
glyph->wrapBox(attribs);
|
||||||
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);
|
||||||
|
|
@ -143,11 +150,11 @@ void TightAtlasPacker::setMinimumScale(double minScale) {
|
||||||
this->minScale = minScale;
|
this->minScale = minScale;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TightAtlasPacker::setUnitRange(double unitRange) {
|
void TightAtlasPacker::setUnitRange(msdfgen::Range unitRange) {
|
||||||
this->unitRange = unitRange;
|
this->unitRange = unitRange;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TightAtlasPacker::setPixelRange(double pxRange) {
|
void TightAtlasPacker::setPixelRange(msdfgen::Range pxRange) {
|
||||||
this->pxRange = pxRange;
|
this->pxRange = pxRange;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -163,6 +170,22 @@ void TightAtlasPacker::setOriginPixelAlignment(bool alignX, bool alignY) {
|
||||||
pxAlignOriginX = alignX, pxAlignOriginY = alignY;
|
pxAlignOriginX = alignX, pxAlignOriginY = alignY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TightAtlasPacker::setInnerUnitPadding(const Padding &padding) {
|
||||||
|
innerUnitPadding = padding;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TightAtlasPacker::setOuterUnitPadding(const Padding &padding) {
|
||||||
|
outerUnitPadding = padding;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TightAtlasPacker::setInnerPixelPadding(const Padding &padding) {
|
||||||
|
innerPxPadding = padding;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TightAtlasPacker::setOuterPixelPadding(const Padding &padding) {
|
||||||
|
outerPxPadding = padding;
|
||||||
|
}
|
||||||
|
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
@ -171,7 +194,7 @@ double TightAtlasPacker::getScale() const {
|
||||||
return scale;
|
return scale;
|
||||||
}
|
}
|
||||||
|
|
||||||
double TightAtlasPacker::getPixelRange() const {
|
msdfgen::Range TightAtlasPacker::getPixelRange() const {
|
||||||
return pxRange+scale*unitRange;
|
return pxRange+scale*unitRange;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
|
#include "Padding.h"
|
||||||
#include "GlyphGeometry.h"
|
#include "GlyphGeometry.h"
|
||||||
|
|
||||||
namespace msdf_atlas {
|
namespace msdf_atlas {
|
||||||
|
|
@ -31,21 +32,29 @@ public:
|
||||||
/// Sets the minimum glyph scale
|
/// Sets the minimum glyph scale
|
||||||
void setMinimumScale(double minScale);
|
void setMinimumScale(double minScale);
|
||||||
/// Sets the unit component of the total distance range
|
/// Sets the unit component of the total distance range
|
||||||
void setUnitRange(double unitRange);
|
void setUnitRange(msdfgen::Range unitRange);
|
||||||
/// Sets the pixel component of the total distance range
|
/// Sets the pixel component of the total distance range
|
||||||
void setPixelRange(double pxRange);
|
void setPixelRange(msdfgen::Range 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
|
/// Sets whether each glyph's origin point should stay aligned with the pixel grid
|
||||||
void setOriginPixelAlignment(bool align);
|
void setOriginPixelAlignment(bool align);
|
||||||
void setOriginPixelAlignment(bool alignX, bool alignY);
|
void setOriginPixelAlignment(bool alignX, bool alignY);
|
||||||
|
/// Sets the unit component of width of additional padding that is part of each glyph quad
|
||||||
|
void setInnerUnitPadding(const Padding &padding);
|
||||||
|
/// Sets the unit component of width of additional padding around each glyph quad
|
||||||
|
void setOuterUnitPadding(const Padding &padding);
|
||||||
|
/// Sets the pixel component of width of additional padding that is part of each glyph quad
|
||||||
|
void setInnerPixelPadding(const Padding &padding);
|
||||||
|
/// Sets the pixel component of width of additional padding around each glyph quad
|
||||||
|
void setOuterPixelPadding(const Padding &padding);
|
||||||
|
|
||||||
/// 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;
|
||||||
/// Returns the final glyph scale
|
/// Returns the final glyph scale
|
||||||
double getScale() const;
|
double getScale() const;
|
||||||
/// Returns the final combined pixel range (including converted unit range)
|
/// Returns the final combined pixel range (including converted unit range)
|
||||||
double getPixelRange() const;
|
msdfgen::Range getPixelRange() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int width, height;
|
int width, height;
|
||||||
|
|
@ -53,10 +62,12 @@ private:
|
||||||
DimensionsConstraint dimensionsConstraint;
|
DimensionsConstraint dimensionsConstraint;
|
||||||
double scale;
|
double scale;
|
||||||
double minScale;
|
double minScale;
|
||||||
double unitRange;
|
msdfgen::Range unitRange;
|
||||||
double pxRange;
|
msdfgen::Range pxRange;
|
||||||
double miterLimit;
|
double miterLimit;
|
||||||
bool pxAlignOriginX, pxAlignOriginY;
|
bool pxAlignOriginX, pxAlignOriginY;
|
||||||
|
Padding innerUnitPadding, outerUnitPadding;
|
||||||
|
Padding innerPxPadding, outerPxPadding;
|
||||||
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;
|
||||||
|
|
|
||||||
|
|
@ -69,8 +69,10 @@ bool exportArteryFont(const FontGeometry *fonts, int fontCount, const msdfgen::B
|
||||||
fontVariant.codepointType = convertCodepointType(identifierType);
|
fontVariant.codepointType = convertCodepointType(identifierType);
|
||||||
fontVariant.imageType = convertImageType(properties.imageType);
|
fontVariant.imageType = convertImageType(properties.imageType);
|
||||||
fontVariant.metrics.fontSize = REAL(properties.fontSize*fontMetrics.emSize);
|
fontVariant.metrics.fontSize = REAL(properties.fontSize*fontMetrics.emSize);
|
||||||
if (properties.imageType != ImageType::HARD_MASK)
|
if (properties.imageType != ImageType::HARD_MASK) {
|
||||||
fontVariant.metrics.distanceRange = REAL(properties.pxRange);
|
fontVariant.metrics.distanceRange = REAL(properties.pxRange.upper-properties.pxRange.lower);
|
||||||
|
fontVariant.metrics.distanceRangeMiddle = REAL(.5*(properties.pxRange.lower+properties.pxRange.upper));
|
||||||
|
}
|
||||||
fontVariant.metrics.emSize = REAL(fontMetrics.emSize);
|
fontVariant.metrics.emSize = REAL(fontMetrics.emSize);
|
||||||
fontVariant.metrics.ascender = REAL(fontMetrics.ascenderY);
|
fontVariant.metrics.ascender = REAL(fontMetrics.ascenderY);
|
||||||
fontVariant.metrics.descender = REAL(fontMetrics.descenderY);
|
fontVariant.metrics.descender = REAL(fontMetrics.descenderY);
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ namespace msdf_atlas {
|
||||||
|
|
||||||
struct ArteryFontExportProperties {
|
struct ArteryFontExportProperties {
|
||||||
double fontSize;
|
double fontSize;
|
||||||
double pxRange;
|
msdfgen::Range pxRange;
|
||||||
ImageType imageType;
|
ImageType imageType;
|
||||||
ImageFormat imageFormat;
|
ImageFormat imageFormat;
|
||||||
YDirection yDirection;
|
YDirection yDirection;
|
||||||
|
|
|
||||||
|
|
@ -67,8 +67,10 @@ bool exportJSON(const FontGeometry *fonts, int fontCount, ImageType imageType, c
|
||||||
// Atlas properties
|
// Atlas properties
|
||||||
fputs("\"atlas\":{", f); {
|
fputs("\"atlas\":{", f); {
|
||||||
fprintf(f, "\"type\":\"%s\",", imageTypeString(imageType));
|
fprintf(f, "\"type\":\"%s\",", imageTypeString(imageType));
|
||||||
if (imageType == ImageType::SDF || imageType == ImageType::PSDF || imageType == ImageType::MSDF || imageType == ImageType::MTSDF)
|
if (imageType == ImageType::SDF || imageType == ImageType::PSDF || imageType == ImageType::MSDF || imageType == ImageType::MTSDF) {
|
||||||
fprintf(f, "\"distanceRange\":%.17g,", metrics.distanceRange);
|
fprintf(f, "\"distanceRange\":%.17g,", metrics.distanceRange.upper-metrics.distanceRange.lower);
|
||||||
|
fprintf(f, "\"distanceRangeMiddle\":%.17g,", .5*(metrics.distanceRange.lower+metrics.distanceRange.upper));
|
||||||
|
}
|
||||||
fprintf(f, "\"size\":%.17g,", metrics.size);
|
fprintf(f, "\"size\":%.17g,", metrics.size);
|
||||||
fprintf(f, "\"width\":%d,", metrics.width);
|
fprintf(f, "\"width\":%d,", metrics.width);
|
||||||
fprintf(f, "\"height\":%d,", metrics.height);
|
fprintf(f, "\"height\":%d,", metrics.height);
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ struct JsonAtlasMetrics {
|
||||||
const double *originX, *originY;
|
const double *originX, *originY;
|
||||||
int spacing;
|
int spacing;
|
||||||
};
|
};
|
||||||
double distanceRange;
|
msdfgen::Range distanceRange;
|
||||||
double size;
|
double size;
|
||||||
int width, height;
|
int width, height;
|
||||||
YDirection yDirection;
|
YDirection yDirection;
|
||||||
|
|
|
||||||
|
|
@ -128,14 +128,28 @@ GLYPH CONFIGURATION
|
||||||
Specifies the size of the glyphs in the atlas bitmap in pixels per em.
|
Specifies the size of the glyphs in the atlas bitmap in pixels per em.
|
||||||
-minsize <em size>
|
-minsize <em size>
|
||||||
Specifies the minimum size. The largest possible size that fits the same atlas dimensions will be used.
|
Specifies the minimum size. The largest possible size that fits the same atlas dimensions will be used.
|
||||||
-emrange <em range>
|
-emrange <em range width>
|
||||||
Specifies the SDF distance range in em's.
|
Specifies the width of the representable SDF distance range in ems.
|
||||||
-pxrange <pixel range>
|
-pxrange <pixel range width>
|
||||||
Specifies the SDF distance range in output pixels. The default value is 2.
|
Specifies the width of the SDF distance range in output pixels. The default value is 2.
|
||||||
|
-aemrange <outermost distance> <innermost distance>
|
||||||
|
Specifies the outermost (negative) and innermost representable distance in ems.
|
||||||
|
-apxrange <outermost distance> <innermost distance>
|
||||||
|
Specifies the outermost (negative) and innermost representable distance in pixels.
|
||||||
-pxalign <off / on / 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.
|
||||||
|
To specify additional inner / outer padding for each glyph in ems / pixels:
|
||||||
|
-empadding <width>
|
||||||
|
-pxpadding <width>
|
||||||
|
-outerempadding <width>
|
||||||
|
-outerpxpadding <width>
|
||||||
|
Or asymmetrical padding with a separate value for each side:
|
||||||
|
-aempadding <left> <bottom> <right> <top>
|
||||||
|
-apxpadding <left> <bottom> <right> <top>
|
||||||
|
-aouterempadding <left> <bottom> <right> <top>
|
||||||
|
-aouterpxpadding <left> <bottom> <right> <top>
|
||||||
|
|
||||||
DISTANCE FIELD GENERATOR SETTINGS
|
DISTANCE FIELD GENERATOR SETTINGS
|
||||||
-angle <angle>
|
-angle <angle>
|
||||||
|
|
@ -264,6 +278,13 @@ static msdfgen::FontHandle *loadVarFont(msdfgen::FreetypeHandle *library, const
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
enum class Units {
|
||||||
|
/// Value is specified in ems
|
||||||
|
EMS,
|
||||||
|
/// Value is specified in pixels
|
||||||
|
PIXELS
|
||||||
|
};
|
||||||
|
|
||||||
struct FontInput {
|
struct FontInput {
|
||||||
const char *fontFilename;
|
const char *fontFilename;
|
||||||
bool variableFont;
|
bool variableFont;
|
||||||
|
|
@ -279,7 +300,7 @@ struct Configuration {
|
||||||
YDirection yDirection;
|
YDirection yDirection;
|
||||||
int width, height;
|
int width, height;
|
||||||
double emSize;
|
double emSize;
|
||||||
double pxRange;
|
msdfgen::Range pxRange;
|
||||||
double angleThreshold;
|
double angleThreshold;
|
||||||
double miterLimit;
|
double miterLimit;
|
||||||
bool pxAlignOriginX, pxAlignOriginY;
|
bool pxAlignOriginX, pxAlignOriginY;
|
||||||
|
|
@ -370,13 +391,12 @@ int main(int argc, const char *const *argv) {
|
||||||
config.generatorAttributes.config.overlapSupport = !config.preprocessGeometry;
|
config.generatorAttributes.config.overlapSupport = !config.preprocessGeometry;
|
||||||
config.generatorAttributes.scanlinePass = !config.preprocessGeometry;
|
config.generatorAttributes.scanlinePass = !config.preprocessGeometry;
|
||||||
double minEmSize = 0;
|
double minEmSize = 0;
|
||||||
enum {
|
Units rangeUnits = Units::PIXELS;
|
||||||
/// Range specified in ems
|
msdfgen::Range rangeValue = 0;
|
||||||
RANGE_EM,
|
Padding innerPadding;
|
||||||
/// Range specified in output pixels
|
Padding outerPadding;
|
||||||
RANGE_PIXEL,
|
Units innerPaddingUnits = Units::EMS;
|
||||||
} rangeMode = RANGE_PIXEL;
|
Units outerPaddingUnits = Units::EMS;
|
||||||
double rangeValue = 0;
|
|
||||||
PackingStyle packingStyle = PackingStyle::TIGHT;
|
PackingStyle packingStyle = PackingStyle::TIGHT;
|
||||||
DimensionsConstraint atlasSizeConstraint = DimensionsConstraint::NONE;
|
DimensionsConstraint atlasSizeConstraint = DimensionsConstraint::NONE;
|
||||||
DimensionsConstraint cellSizeConstraint = DimensionsConstraint::NONE;
|
DimensionsConstraint cellSizeConstraint = DimensionsConstraint::NONE;
|
||||||
|
|
@ -577,20 +597,40 @@ int main(int argc, const char *const *argv) {
|
||||||
}
|
}
|
||||||
ARG_CASE("-emrange", 1) {
|
ARG_CASE("-emrange", 1) {
|
||||||
double r;
|
double r;
|
||||||
if (!(parseDouble(r, argv[argPos++]) && r >= 0))
|
if (!(parseDouble(r, argv[argPos++]) && r != 0))
|
||||||
ABORT("Invalid range argument. Use -emrange <em range> with a positive real number.");
|
ABORT("Invalid range argument. Use -emrange <em range> with a non-zero real number.");
|
||||||
rangeMode = RANGE_EM;
|
rangeUnits = Units::EMS;
|
||||||
rangeValue = r;
|
rangeValue = r;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
ARG_CASE("-pxrange", 1) {
|
ARG_CASE("-pxrange", 1) {
|
||||||
double r;
|
double r;
|
||||||
if (!(parseDouble(r, argv[argPos++]) && r >= 0))
|
if (!(parseDouble(r, argv[argPos++]) && r != 0))
|
||||||
ABORT("Invalid range argument. Use -pxrange <pixel range> with a positive real number.");
|
ABORT("Invalid range argument. Use -pxrange <pixel range> with a non-zero real number.");
|
||||||
rangeMode = RANGE_PIXEL;
|
rangeUnits = Units::PIXELS;
|
||||||
rangeValue = r;
|
rangeValue = r;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
ARG_CASE("-aemrange", 2) {
|
||||||
|
double r0, r1;
|
||||||
|
if (!(parseDouble(r0, argv[argPos++]) && parseDouble(r1, argv[argPos++])))
|
||||||
|
ABORT("Invalid range arguments. Use -aemrange <minimum> <maximum> with two real numbers.");
|
||||||
|
if (r0 == r1)
|
||||||
|
ABORT("Range must be non-empty.");
|
||||||
|
rangeUnits = Units::EMS;
|
||||||
|
rangeValue = msdfgen::Range(r0, r1);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
ARG_CASE("-apxrange", 2) {
|
||||||
|
double r0, r1;
|
||||||
|
if (!(parseDouble(r0, argv[argPos++]) && parseDouble(r1, argv[argPos++])))
|
||||||
|
ABORT("Invalid range arguments. Use -apxrange <minimum> <maximum> with two real numbers.");
|
||||||
|
if (r0 == r1)
|
||||||
|
ABORT("Range must be non-empty.");
|
||||||
|
rangeUnits = Units::PIXELS;
|
||||||
|
rangeValue = msdfgen::Range(r0, r1);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
ARG_CASE("-pxalign", 1) {
|
ARG_CASE("-pxalign", 1) {
|
||||||
if (ARG_IS("off") || ARG_PREFIX("disable") || ARG_IS("0") || ARG_IS("false") || ARG_PREFIX("n"))
|
if (ARG_IS("off") || ARG_PREFIX("disable") || ARG_IS("0") || ARG_IS("false") || ARG_PREFIX("n"))
|
||||||
config.pxAlignOriginX = false, config.pxAlignOriginY = false;
|
config.pxAlignOriginX = false, config.pxAlignOriginY = false;
|
||||||
|
|
@ -605,6 +645,70 @@ int main(int argc, const char *const *argv) {
|
||||||
++argPos;
|
++argPos;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
ARG_CASE("-empadding", 1) {
|
||||||
|
double p;
|
||||||
|
if (!parseDouble(p, argv[argPos++]))
|
||||||
|
ABORT("Invalid padding argument. Use -empadding <padding> with a real number.");
|
||||||
|
innerPaddingUnits = Units::EMS;
|
||||||
|
innerPadding = Padding(p);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
ARG_CASE("-pxpadding", 1) {
|
||||||
|
double p;
|
||||||
|
if (!parseDouble(p, argv[argPos++]))
|
||||||
|
ABORT("Invalid padding argument. Use -pxpadding <padding> with a real number.");
|
||||||
|
innerPaddingUnits = Units::PIXELS;
|
||||||
|
innerPadding = Padding(p);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
ARG_CASE("-outerempadding", 1) {
|
||||||
|
double p;
|
||||||
|
if (!parseDouble(p, argv[argPos++]))
|
||||||
|
ABORT("Invalid padding argument. Use -outerempadding <padding> with a real number.");
|
||||||
|
outerPaddingUnits = Units::EMS;
|
||||||
|
outerPadding = Padding(p);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
ARG_CASE("-outerpxpadding", 1) {
|
||||||
|
double p;
|
||||||
|
if (!parseDouble(p, argv[argPos++]))
|
||||||
|
ABORT("Invalid padding argument. Use -outerpxpadding <padding> with a real number.");
|
||||||
|
outerPaddingUnits = Units::PIXELS;
|
||||||
|
outerPadding = Padding(p);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
ARG_CASE("-aempadding", 4) {
|
||||||
|
double l, b, r, t;
|
||||||
|
if (!(parseDouble(l, argv[argPos++]) && parseDouble(b, argv[argPos++]) && parseDouble(r, argv[argPos++]) && parseDouble(t, argv[argPos++])))
|
||||||
|
ABORT("Invalid padding arguments. Use -aempadding <left> <bottom> <right> <top> with 4 real numbers.");
|
||||||
|
innerPaddingUnits = Units::EMS;
|
||||||
|
innerPadding.l = l, innerPadding.b = b, innerPadding.r = r, innerPadding.t = t;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
ARG_CASE("-apxpadding", 4) {
|
||||||
|
double l, b, r, t;
|
||||||
|
if (!(parseDouble(l, argv[argPos++]) && parseDouble(b, argv[argPos++]) && parseDouble(r, argv[argPos++]) && parseDouble(t, argv[argPos++])))
|
||||||
|
ABORT("Invalid padding arguments. Use -apxpadding <left> <bottom> <right> <top> with 4 real numbers.");
|
||||||
|
innerPaddingUnits = Units::PIXELS;
|
||||||
|
innerPadding.l = l, innerPadding.b = b, innerPadding.r = r, innerPadding.t = t;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
ARG_CASE("-aouterempadding", 4) {
|
||||||
|
double l, b, r, t;
|
||||||
|
if (!(parseDouble(l, argv[argPos++]) && parseDouble(b, argv[argPos++]) && parseDouble(r, argv[argPos++]) && parseDouble(t, argv[argPos++])))
|
||||||
|
ABORT("Invalid padding arguments. Use -aouterempadding <left> <bottom> <right> <top> with 4 real numbers.");
|
||||||
|
outerPaddingUnits = Units::EMS;
|
||||||
|
outerPadding.l = l, outerPadding.b = b, outerPadding.r = r, outerPadding.t = t;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
ARG_CASE("-aouterpxpadding", 4) {
|
||||||
|
double l, b, r, t;
|
||||||
|
if (!(parseDouble(l, argv[argPos++]) && parseDouble(b, argv[argPos++]) && parseDouble(r, argv[argPos++]) && parseDouble(t, argv[argPos++])))
|
||||||
|
ABORT("Invalid padding arguments. Use -aouterpxpadding <left> <bottom> <right> <top> with 4 real numbers.");
|
||||||
|
outerPaddingUnits = Units::PIXELS;
|
||||||
|
outerPadding.l = l, outerPadding.b = b, outerPadding.r = r, outerPadding.t = t;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
ARG_CASE("-angle", 1) {
|
ARG_CASE("-angle", 1) {
|
||||||
double at;
|
double at;
|
||||||
if (!parseAngle(at, argv[argPos++]))
|
if (!parseAngle(at, argv[argPos++]))
|
||||||
|
|
@ -841,10 +945,10 @@ int main(int argc, const char *const *argv) {
|
||||||
minEmSize = DEFAULT_SIZE;
|
minEmSize = DEFAULT_SIZE;
|
||||||
}
|
}
|
||||||
if (config.imageType == ImageType::HARD_MASK || config.imageType == ImageType::SOFT_MASK) {
|
if (config.imageType == ImageType::HARD_MASK || config.imageType == ImageType::SOFT_MASK) {
|
||||||
rangeMode = RANGE_PIXEL;
|
rangeUnits = Units::PIXELS;
|
||||||
rangeValue = 1;
|
rangeValue = 1;
|
||||||
} else if (rangeValue <= 0) {
|
} else if (rangeValue.lower == rangeValue.upper) {
|
||||||
rangeMode = RANGE_PIXEL;
|
rangeUnits = Units::PIXELS;
|
||||||
rangeValue = DEFAULT_PIXEL_RANGE;
|
rangeValue = DEFAULT_PIXEL_RANGE;
|
||||||
}
|
}
|
||||||
if (config.kerning && !(config.arteryFontFilename || config.jsonFilename || config.shadronPreviewFilename))
|
if (config.kerning && !(config.arteryFontFilename || config.jsonFilename || config.shadronPreviewFilename))
|
||||||
|
|
@ -1052,15 +1156,33 @@ int main(int argc, const char *const *argv) {
|
||||||
|
|
||||||
// Determine final atlas dimensions, scale and range, pack glyphs
|
// Determine final atlas dimensions, scale and range, pack glyphs
|
||||||
{
|
{
|
||||||
double unitRange = 0, pxRange = 0;
|
msdfgen::Range emRange = 0, pxRange = 0;
|
||||||
switch (rangeMode) {
|
switch (rangeUnits) {
|
||||||
case RANGE_EM:
|
case Units::EMS:
|
||||||
unitRange = rangeValue;
|
emRange = rangeValue;
|
||||||
break;
|
break;
|
||||||
case RANGE_PIXEL:
|
case Units::PIXELS:
|
||||||
pxRange = rangeValue;
|
pxRange = rangeValue;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
Padding innerEmPadding, outerEmPadding;
|
||||||
|
Padding innerPxPadding, outerPxPadding;
|
||||||
|
switch (innerPaddingUnits) {
|
||||||
|
case Units::EMS:
|
||||||
|
innerEmPadding = innerPadding;
|
||||||
|
break;
|
||||||
|
case Units::PIXELS:
|
||||||
|
innerPxPadding = innerPadding;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
switch (outerPaddingUnits) {
|
||||||
|
case Units::EMS:
|
||||||
|
outerEmPadding = outerPadding;
|
||||||
|
break;
|
||||||
|
case Units::PIXELS:
|
||||||
|
outerPxPadding = outerPadding;
|
||||||
|
break;
|
||||||
|
}
|
||||||
bool fixedDimensions = fixedWidth >= 0 && fixedHeight >= 0;
|
bool fixedDimensions = fixedWidth >= 0 && fixedHeight >= 0;
|
||||||
bool fixedScale = config.emSize > 0;
|
bool fixedScale = config.emSize > 0;
|
||||||
switch (packingStyle) {
|
switch (packingStyle) {
|
||||||
|
|
@ -1077,9 +1199,13 @@ int main(int argc, const char *const *argv) {
|
||||||
else
|
else
|
||||||
atlasPacker.setMinimumScale(minEmSize);
|
atlasPacker.setMinimumScale(minEmSize);
|
||||||
atlasPacker.setPixelRange(pxRange);
|
atlasPacker.setPixelRange(pxRange);
|
||||||
atlasPacker.setUnitRange(unitRange);
|
atlasPacker.setUnitRange(emRange);
|
||||||
atlasPacker.setMiterLimit(config.miterLimit);
|
atlasPacker.setMiterLimit(config.miterLimit);
|
||||||
atlasPacker.setOriginPixelAlignment(config.pxAlignOriginX, config.pxAlignOriginY);
|
atlasPacker.setOriginPixelAlignment(config.pxAlignOriginX, config.pxAlignOriginY);
|
||||||
|
atlasPacker.setInnerUnitPadding(innerEmPadding);
|
||||||
|
atlasPacker.setOuterUnitPadding(outerEmPadding);
|
||||||
|
atlasPacker.setInnerPixelPadding(innerPxPadding);
|
||||||
|
atlasPacker.setOuterPixelPadding(outerPxPadding);
|
||||||
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.");
|
||||||
|
|
@ -1119,9 +1245,13 @@ int main(int argc, const char *const *argv) {
|
||||||
else
|
else
|
||||||
atlasPacker.setMinimumScale(minEmSize);
|
atlasPacker.setMinimumScale(minEmSize);
|
||||||
atlasPacker.setPixelRange(pxRange);
|
atlasPacker.setPixelRange(pxRange);
|
||||||
atlasPacker.setUnitRange(unitRange);
|
atlasPacker.setUnitRange(emRange);
|
||||||
atlasPacker.setMiterLimit(config.miterLimit);
|
atlasPacker.setMiterLimit(config.miterLimit);
|
||||||
atlasPacker.setOriginPixelAlignment(config.pxAlignOriginX, config.pxAlignOriginY);
|
atlasPacker.setOriginPixelAlignment(config.pxAlignOriginX, config.pxAlignOriginY);
|
||||||
|
atlasPacker.setInnerUnitPadding(innerEmPadding);
|
||||||
|
atlasPacker.setOuterUnitPadding(outerEmPadding);
|
||||||
|
atlasPacker.setInnerPixelPadding(innerPxPadding);
|
||||||
|
atlasPacker.setOuterPixelPadding(outerPxPadding);
|
||||||
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.");
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
#include "utf8.h"
|
#include "utf8.h"
|
||||||
#include "Rectangle.h"
|
#include "Rectangle.h"
|
||||||
|
#include "Padding.h"
|
||||||
#include "Charset.h"
|
#include "Charset.h"
|
||||||
#include "GlyphBox.h"
|
#include "GlyphBox.h"
|
||||||
#include "GlyphGeometry.h"
|
#include "GlyphGeometry.h"
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
namespace msdf_atlas {
|
namespace msdf_atlas {
|
||||||
|
|
||||||
static const char *const shadronFillGlyphMask = R"(
|
static const char *const shadronFillGlyphMask = R"(
|
||||||
template <ATLAS, RANGE, COLOR>
|
template <ATLAS, RANGE, ZERO_DIST, COLOR>
|
||||||
glsl vec4 fillGlyph(vec2 texCoord) {
|
glsl vec4 fillGlyph(vec2 texCoord) {
|
||||||
float fill = texture((ATLAS), texCoord).r;
|
float fill = texture((ATLAS), texCoord).r;
|
||||||
return vec4(vec3(COLOR), fill);
|
return vec4(vec3(COLOR), fill);
|
||||||
|
|
@ -15,10 +15,10 @@ glsl vec4 fillGlyph(vec2 texCoord) {
|
||||||
)";
|
)";
|
||||||
|
|
||||||
static const char *const shadronFillGlyphSdf = R"(
|
static const char *const shadronFillGlyphSdf = R"(
|
||||||
template <ATLAS, RANGE, COLOR>
|
template <ATLAS, RANGE, ZERO_DIST, COLOR>
|
||||||
glsl vec4 fillGlyph(vec2 texCoord) {
|
glsl vec4 fillGlyph(vec2 texCoord) {
|
||||||
vec3 s = texture((ATLAS), texCoord).rgb;
|
vec3 s = texture((ATLAS), texCoord).rgb;
|
||||||
float sd = dot(vec2(RANGE), 0.5/fwidth(texCoord))*(median(s.r, s.g, s.b)-0.5);
|
float sd = dot(vec2(RANGE), 0.5/fwidth(texCoord))*(median(s.r, s.g, s.b)-ZERO_DIST);
|
||||||
float fill = clamp(sd+0.5, 0.0, 1.0);
|
float fill = clamp(sd+0.5, 0.0, 1.0);
|
||||||
return vec4(vec3(COLOR), fill);
|
return vec4(vec3(COLOR), fill);
|
||||||
}
|
}
|
||||||
|
|
@ -43,11 +43,11 @@ glsl vec4 projectVertex(out vec2 texCoord, in GlyphVertex vertex) {
|
||||||
return vec4(coord, 0.0, 1.0);
|
return vec4(coord, 0.0, 1.0);
|
||||||
}
|
}
|
||||||
%s
|
%s
|
||||||
#define PREVIEW_IMAGE(NAME, ATLAS, RANGE, COLOR, VERTEX_LIST, TEXT_SIZE, DIMENSIONS) model image NAME : \
|
#define PREVIEW_IMAGE(NAME, ATLAS, RANGE, ZERO_DIST, COLOR, VERTEX_LIST, TEXT_SIZE, DIMENSIONS) model image NAME : \
|
||||||
vertex_data(GlyphVertex), \
|
vertex_data(GlyphVertex), \
|
||||||
fragment_data(vec2), \
|
fragment_data(vec2), \
|
||||||
vertex(projectVertex<TEXT_SIZE>, triangles, VERTEX_LIST), \
|
vertex(projectVertex<TEXT_SIZE>, triangles, VERTEX_LIST), \
|
||||||
fragment(fillGlyph<ATLAS, RANGE, COLOR>), \
|
fragment(fillGlyph<ATLAS, RANGE, ZERO_DIST, COLOR>), \
|
||||||
depth(false), \
|
depth(false), \
|
||||||
blend(transparency), \
|
blend(transparency), \
|
||||||
background(vec4(vec3(COLOR), 0.0)), \
|
background(vec4(vec3(COLOR), 0.0)), \
|
||||||
|
|
@ -94,7 +94,7 @@ static std::string escapeString(const std::string &str) {
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
bool generateShadronPreview(const FontGeometry *fonts, int fontCount, ImageType atlasType, int atlasWidth, int atlasHeight, msdfgen::Range pxRange, const unicode_t *text, const char *imageFilename, bool fullRange, const char *outputFilename) {
|
||||||
if (fontCount <= 0)
|
if (fontCount <= 0)
|
||||||
return false;
|
return false;
|
||||||
double texelWidth = 1./atlasWidth;
|
double texelWidth = 1./atlasWidth;
|
||||||
|
|
@ -109,7 +109,9 @@ bool generateShadronPreview(const FontGeometry *fonts, int fontCount, ImageType
|
||||||
else
|
else
|
||||||
fprintf(file, "image Atlas = file()");
|
fprintf(file, "image Atlas = file()");
|
||||||
fprintf(file, " : %sfilter(%s), map(repeat);\n", fullRange ? "full_range(true), " : "", atlasType == ImageType::HARD_MASK ? "nearest" : "linear");
|
fprintf(file, " : %sfilter(%s), map(repeat);\n", fullRange ? "full_range(true), " : "", atlasType == ImageType::HARD_MASK ? "nearest" : "linear");
|
||||||
fprintf(file, "const vec2 txRange = vec2(%.9g, %.9g);\n\n", pxRange*texelWidth, pxRange*texelHeight);
|
double pxRangeWidth = pxRange.upper-pxRange.lower;
|
||||||
|
fprintf(file, "const vec2 txRange = vec2(%.9g, %.9g);\n", pxRangeWidth*texelWidth, pxRangeWidth*texelHeight);
|
||||||
|
fprintf(file, "const float zeroDistanceValue = %.9g;\n\n", -pxRange.lower/(pxRange.upper-pxRange.lower));
|
||||||
{
|
{
|
||||||
msdfgen::FontMetrics fontMetrics = fonts->getMetrics();
|
msdfgen::FontMetrics fontMetrics = fonts->getMetrics();
|
||||||
for (int i = 1; i < fontCount; ++i) {
|
for (int i = 1; i < fontCount; ++i) {
|
||||||
|
|
@ -163,7 +165,7 @@ bool generateShadronPreview(const FontGeometry *fonts, int fontCount, ImageType
|
||||||
fputs("};\n", file);
|
fputs("};\n", file);
|
||||||
fprintf(file, "const vec2 textSize = vec2(%.9g, %.9g);\n\n", textWidth, -y);
|
fprintf(file, "const vec2 textSize = vec2(%.9g, %.9g);\n\n", textWidth, -y);
|
||||||
}
|
}
|
||||||
fputs("PREVIEW_IMAGE(Preview, Atlas, txRange, vec3(1.0), textQuadVertices, textSize, ivec2(1200, 400));\n", file);
|
fputs("PREVIEW_IMAGE(Preview, Atlas, txRange, zeroDistanceValue, vec3(1.0), textQuadVertices, textSize, ivec2(1200, 400));\n", file);
|
||||||
fputs("export png(Preview, \"preview.png\");\n", file);
|
fputs("export png(Preview, \"preview.png\");\n", file);
|
||||||
fclose(file);
|
fclose(file);
|
||||||
return anyGlyphs;
|
return anyGlyphs;
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,6 @@
|
||||||
namespace msdf_atlas {
|
namespace msdf_atlas {
|
||||||
|
|
||||||
/// Generates a Shadron script that displays a string using the generated 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);
|
bool generateShadronPreview(const FontGeometry *fonts, int fontCount, ImageType atlasType, int atlasWidth, int atlasHeight, msdfgen::Range pxRange, const unicode_t *text, const char *imageFilename, bool fullRange, const char *outputFilename);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
2
msdfgen
2
msdfgen
|
|
@ -1 +1 @@
|
||||||
Subproject commit c7a724c17366db009a43514b90329519d792b51b
|
Subproject commit 5dc5f6260b85064c7972808daa8f544d76a73c17
|
||||||
Loading…
Reference in New Issue