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
|
||||
- `-emrange <em range>` – sets the distance field range in em's
|
||||
- `-pxrange <pixel range>` (default = 2) – sets the distance field range in output pixels
|
||||
- `-aemrange` / `-apxrange <outermost distance> <innermost distance>` – sets the distance field range asymmetrically by specifying the minimum and maximum representable signed distances (outside distances are negative!)
|
||||
- `-pxalign <off / on / horizontal / vertical>` (default = vertical) – enables or disables alignment of glyph's origin point with the pixel grid
|
||||
- `-empadding` / `-pxpadding <width>` – sets additional padding within each glyph's box (in em's / pixels)
|
||||
- `-outerempadding` / `-outerpxpadding <width>` – sets additional padding around each glyph's box
|
||||
- `-aempadding` / `-apxpadding` / `-aouterempadding` / `-aouterpxpadding <left> <bottom> <right> <top>` – sets additional padding (see above) asymmetrically with a separate width value for each side
|
||||
|
||||
### Distance field generator settings
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
void GlyphGeometry::wrapBox(double scale, double range, double miterLimit, bool pxAlignOrigin) {
|
||||
wrapBox(scale, range, miterLimit, pxAlignOrigin, pxAlignOrigin);
|
||||
}
|
||||
|
||||
void GlyphGeometry::wrapBox(double scale, double range, double miterLimit, bool pxAlignOriginX, bool pxAlignOriginY) {
|
||||
scale *= geometryScale;
|
||||
range /= geometryScale;
|
||||
void GlyphGeometry::wrapBox(const GlyphAttributes &glyphAttributes) {
|
||||
double scale = glyphAttributes.scale*geometryScale;
|
||||
msdfgen::Range range = glyphAttributes.range/geometryScale;
|
||||
Padding fullPadding = (glyphAttributes.innerPadding+glyphAttributes.outerPadding)/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);
|
||||
if (pxAlignOriginX) {
|
||||
l += range.lower, b += range.lower;
|
||||
r -= range.lower, t -= range.lower;
|
||||
if (glyphAttributes.miterLimit > 0)
|
||||
shape.boundMiters(l, b, r, t, -range.lower, glyphAttributes.miterLimit, 1);
|
||||
l -= fullPadding.l, b -= fullPadding.b;
|
||||
r += fullPadding.r, t += fullPadding.t;
|
||||
if (glyphAttributes.pxAlignOriginX) {
|
||||
int sl = (int) floor(scale*l-.5);
|
||||
int sr = (int) ceil(scale*r+.5);
|
||||
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.translate.x = -l+.5*(box.rect.w-w)/scale;
|
||||
}
|
||||
if (pxAlignOriginY) {
|
||||
if (glyphAttributes.pxAlignOriginY) {
|
||||
int sb = (int) floor(scale*b-.5);
|
||||
int st = (int) ceil(scale*t+.5);
|
||||
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.translate.y = -b+.5*(box.rect.h-h)/scale;
|
||||
}
|
||||
box.outerPadding = glyphAttributes.scale*glyphAttributes.outerPadding;
|
||||
} else {
|
||||
box.rect.w = 0, box.rect.h = 0;
|
||||
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) {
|
||||
frameBox(scale, range, miterLimit, width, height, fixedX, fixedY, pxAlignOrigin, pxAlignOrigin);
|
||||
void GlyphGeometry::wrapBox(double scale, double range, double miterLimit, bool 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) {
|
||||
scale *= geometryScale;
|
||||
range /= geometryScale;
|
||||
void GlyphGeometry::wrapBox(double scale, double range, double miterLimit, bool pxAlignOriginX, bool pxAlignOriginY) {
|
||||
GlyphAttributes attribs = { };
|
||||
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.scale = scale;
|
||||
box.rect.w = width;
|
||||
|
|
@ -108,13 +125,15 @@ void GlyphGeometry::frameBox(double scale, double range, double miterLimit, int
|
|||
box.translate.y = *fixedY/geometryScale;
|
||||
} else {
|
||||
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);
|
||||
l += range.lower, b += range.lower;
|
||||
r -= range.lower, t -= range.lower;
|
||||
if (glyphAttributes.miterLimit > 0)
|
||||
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)
|
||||
box.translate.x = *fixedX/geometryScale;
|
||||
else if (pxAlignOriginX) {
|
||||
else if (glyphAttributes.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;
|
||||
|
|
@ -124,7 +143,7 @@ void GlyphGeometry::frameBox(double scale, double range, double miterLimit, int
|
|||
}
|
||||
if (fixedY)
|
||||
box.translate.y = *fixedY/geometryScale;
|
||||
else if (pxAlignOriginY) {
|
||||
else if (glyphAttributes.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;
|
||||
|
|
@ -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.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) {
|
||||
|
|
@ -194,7 +234,7 @@ void GlyphGeometry::getBoxSize(int &w, int &h) const {
|
|||
w = box.rect.w, h = box.rect.h;
|
||||
}
|
||||
|
||||
double GlyphGeometry::getBoxRange() const {
|
||||
msdfgen::Range GlyphGeometry::getBoxRange() const {
|
||||
return box.range;
|
||||
}
|
||||
|
||||
|
|
@ -213,20 +253,20 @@ msdfgen::Vector2 GlyphGeometry::getBoxTranslate() const {
|
|||
void GlyphGeometry::getQuadPlaneBounds(double &l, double &b, double &r, double &t) const {
|
||||
if (box.rect.w > 0 && box.rect.h > 0) {
|
||||
double invBoxScale = 1/box.scale;
|
||||
l = geometryScale*(-box.translate.x+.5*invBoxScale);
|
||||
b = geometryScale*(-box.translate.y+.5*invBoxScale);
|
||||
r = geometryScale*(-box.translate.x+(box.rect.w-.5)*invBoxScale);
|
||||
t = geometryScale*(-box.translate.y+(box.rect.h-.5)*invBoxScale);
|
||||
l = geometryScale*(-box.translate.x+(box.outerPadding.l+.5)*invBoxScale);
|
||||
b = geometryScale*(-box.translate.y+(box.outerPadding.b+.5)*invBoxScale);
|
||||
r = geometryScale*(-box.translate.x+(-box.outerPadding.r+box.rect.w-.5)*invBoxScale);
|
||||
t = geometryScale*(-box.translate.y+(-box.outerPadding.t+box.rect.h-.5)*invBoxScale);
|
||||
} else
|
||||
l = 0, b = 0, r = 0, t = 0;
|
||||
}
|
||||
|
||||
void GlyphGeometry::getQuadAtlasBounds(double &l, double &b, double &r, double &t) const {
|
||||
if (box.rect.w > 0 && box.rect.h > 0) {
|
||||
l = box.rect.x+.5;
|
||||
b = box.rect.y+.5;
|
||||
r = box.rect.x+box.rect.w-.5;
|
||||
t = box.rect.y+box.rect.h-.5;
|
||||
l = box.rect.x+box.outerPadding.l+.5;
|
||||
b = box.rect.y+box.outerPadding.b+.5;
|
||||
r = box.rect.x-box.outerPadding.r+box.rect.w-.5;
|
||||
t = box.rect.y-box.outerPadding.t+box.rect.h-.5;
|
||||
} else
|
||||
l = 0, b = 0, r = 0, t = 0;
|
||||
}
|
||||
|
|
@ -244,4 +284,8 @@ GlyphGeometry::operator GlyphBox() const {
|
|||
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 "types.h"
|
||||
#include "Rectangle.h"
|
||||
#include "Padding.h"
|
||||
#include "GlyphBox.h"
|
||||
|
||||
namespace msdf_atlas {
|
||||
|
|
@ -13,6 +14,14 @@ namespace msdf_atlas {
|
|||
class GlyphGeometry {
|
||||
|
||||
public:
|
||||
struct GlyphAttributes {
|
||||
double scale;
|
||||
msdfgen::Range range;
|
||||
Padding innerPadding, outerPadding;
|
||||
double miterLimit;
|
||||
bool pxAlignOriginX, pxAlignOriginY;
|
||||
};
|
||||
|
||||
GlyphGeometry();
|
||||
/// Loads glyph geometry from font
|
||||
bool load(msdfgen::FontHandle *font, double geometryScale, msdfgen::GlyphIndex index, bool preprocessGeometry = true);
|
||||
|
|
@ -20,9 +29,11 @@ public:
|
|||
/// Applies edge coloring to glyph shape
|
||||
void edgeColoring(void (*fn)(msdfgen::Shape &, double, unsigned long long), double angleThreshold, unsigned long long seed);
|
||||
/// Computes the dimensions of the glyph's box as well as the transformation for the generator function
|
||||
void wrapBox(const GlyphAttributes &glyphAttributes);
|
||||
void wrapBox(double scale, double range, double miterLimit, bool pxAlignOrigin = false);
|
||||
void wrapBox(double scale, double range, double miterLimit, bool pxAlignOriginX, bool pxAlignOriginY);
|
||||
/// 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 pxAlignOriginX, bool pxAlignOriginY);
|
||||
/// 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
|
||||
void getBoxSize(int &w, int &h) const;
|
||||
/// 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
|
||||
msdfgen::Projection getBoxProjection() const;
|
||||
/// Returns the scale needed to generate the glyph's bitmap
|
||||
|
|
@ -77,11 +88,14 @@ private:
|
|||
double advance;
|
||||
struct {
|
||||
Rectangle rect;
|
||||
double range;
|
||||
msdfgen::Range range;
|
||||
double scale;
|
||||
msdfgen::Vector2 translate;
|
||||
Padding outerPadding;
|
||||
} box;
|
||||
|
||||
};
|
||||
|
||||
msdfgen::Range operator+(msdfgen::Range a, msdfgen::Range b);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -107,20 +107,20 @@ GridAtlasPacker::GridAtlasPacker() :
|
|||
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;
|
||||
msdfgen::Shape::Bounds maxBounds = { +LARGE_VALUE, +LARGE_VALUE, -LARGE_VALUE, -LARGE_VALUE };
|
||||
for (GlyphGeometry *glyph = glyphs, *end = glyphs+count; glyph < end; ++glyph) {
|
||||
if (!glyph->isWhitespace()) {
|
||||
double geometryScale = glyph->getGeometryScale();
|
||||
double shapeRange = range/geometryScale;
|
||||
double shapeOuterRange = outerRange/geometryScale;
|
||||
geometryScale *= scale;
|
||||
const msdfgen::Shape::Bounds &shapeBounds = glyph->getShapeBounds();
|
||||
double l = shapeBounds.l, b = shapeBounds.b, r = shapeBounds.r, t = shapeBounds.t;
|
||||
l -= .5*shapeRange, b -= .5*shapeRange;
|
||||
r += .5*shapeRange, t += .5*shapeRange;
|
||||
l -= shapeOuterRange, b -= shapeOuterRange;
|
||||
r += shapeOuterRange, t += shapeOuterRange;
|
||||
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;
|
||||
r *= geometryScale, t *= geometryScale;
|
||||
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)
|
||||
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 (hFixed)
|
||||
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 -= spacing, cellHeight -= spacing;
|
||||
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;
|
||||
if (TRY_FIT(1)) {
|
||||
while (maxScale < 1e+32 && ((maxScale = 2*minScale), TRY_FIT(maxScale)))
|
||||
|
|
@ -220,7 +224,7 @@ int GridAtlasPacker::pack(GlyphGeometry *glyphs, int count) {
|
|||
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;
|
||||
|
||||
msdfgen::Shape::Bounds maxBounds = { };
|
||||
|
|
@ -229,14 +233,14 @@ int GridAtlasPacker::pack(GlyphGeometry *glyphs, int count) {
|
|||
if (scale <= 0) {
|
||||
|
||||
// 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) {
|
||||
scale = scaleToFit(glyphs, count, cellWidth, cellHeight, maxBounds, maxWidth, maxHeight);
|
||||
if (scale < minScale) {
|
||||
scale = minScale;
|
||||
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) {
|
||||
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;
|
||||
cellHeight = (int) ceil(maxHeight)+spacing+1;
|
||||
raiseToConstraint(cellWidth, cellHeight, cellDimensionsConstraint);
|
||||
scale = scaleToFit(glyphs, count, cellWidth, cellHeight, maxBounds, maxWidth, maxHeight);
|
||||
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) {
|
||||
|
|
@ -316,7 +320,12 @@ int GridAtlasPacker::pack(GlyphGeometry *glyphs, int count) {
|
|||
|
||||
} 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;
|
||||
if (pxAlignOriginX && !hFixed) {
|
||||
maxWidth -= 1; // Added by getMaxBounds
|
||||
|
|
@ -327,11 +336,13 @@ int GridAtlasPacker::pack(GlyphGeometry *glyphs, int count) {
|
|||
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;
|
||||
if (cellWidth > 0)
|
||||
hScale = (cellWidth-hSlack-spacing-1-pxRange)/maxWidth;
|
||||
hScale = (cellWidth-hSlack-spacing-extraPxWidth-1)/maxWidth;
|
||||
if (cellHeight > 0)
|
||||
vScale = (cellHeight-vSlack-spacing-1-pxRange)/maxHeight;
|
||||
vScale = (cellHeight-vSlack-spacing-extraPxHeight-1)/maxHeight;
|
||||
if (hScale || vScale) {
|
||||
if (hScale && vScale)
|
||||
scale = std::min(hScale, vScale);
|
||||
|
|
@ -353,8 +364,8 @@ int GridAtlasPacker::pack(GlyphGeometry *glyphs, int count) {
|
|||
int tHeight = (height+spacing)/rows;
|
||||
lowerToConstraint(tWidth, tHeight, cellDimensionsConstraint);
|
||||
if (tWidth > 0 && tHeight > 0) {
|
||||
hScale = (tWidth-hSlack-spacing-1-pxRange)/maxWidth;
|
||||
vScale = (tHeight-vSlack-spacing-1-pxRange)/maxHeight;
|
||||
hScale = (tWidth-hSlack-spacing-extraPxWidth-1)/maxWidth;
|
||||
vScale = (tHeight-vSlack-spacing-extraPxHeight-1)/maxHeight;
|
||||
double curScale = std::min(hScale, vScale);
|
||||
if (curScale > scale) {
|
||||
scale = curScale;
|
||||
|
|
@ -384,16 +395,16 @@ int GridAtlasPacker::pack(GlyphGeometry *glyphs, int count) {
|
|||
}
|
||||
|
||||
if (scale <= 0) {
|
||||
cellWidth = (int) ceil(minScale*maxWidth+pxRange)+hSlack+spacing+1;
|
||||
cellHeight = (int) ceil(minScale*maxHeight+pxRange)+vSlack+spacing+1;
|
||||
cellWidth = (int) ceil(minScale*maxWidth+extraPxWidth)+hSlack+spacing+1;
|
||||
cellHeight = (int) ceil(minScale*maxHeight+extraPxHeight)+vSlack+spacing+1;
|
||||
raiseToConstraint(cellWidth, cellHeight, cellDimensionsConstraint);
|
||||
hScale = (cellWidth-hSlack-spacing-1-pxRange)/maxWidth;
|
||||
vScale = (cellHeight-vSlack-spacing-1-pxRange)/maxHeight;
|
||||
hScale = (cellWidth-hSlack-spacing-extraPxWidth-1)/maxWidth;
|
||||
vScale = (cellHeight-vSlack-spacing-extraPxHeight-1)/maxHeight;
|
||||
scale = std::min(hScale, vScale);
|
||||
}
|
||||
|
||||
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);
|
||||
if (optimalCellHeight < cellHeight && optimalCellWidth <= cellWidth) {
|
||||
cellWidth = optimalCellWidth;
|
||||
|
|
@ -403,11 +414,15 @@ int GridAtlasPacker::pack(GlyphGeometry *glyphs, int count) {
|
|||
maxBounds.l *= scale, maxBounds.b *= scale;
|
||||
maxBounds.r *= scale, maxBounds.t *= 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 {
|
||||
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 optimalCellHeight = (int) ceil(maxHeight)+spacing+1;
|
||||
if (cellWidth < 0 || cellHeight < 0) {
|
||||
|
|
@ -481,10 +496,18 @@ int GridAtlasPacker::pack(GlyphGeometry *glyphs, int count) {
|
|||
if (rows*cellHeight > height)
|
||||
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;
|
||||
for (GlyphGeometry *glyph = glyphs, *end = glyphs+count; glyph < end; ++glyph) {
|
||||
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);
|
||||
if (++col >= columns) {
|
||||
if (++row >= rows) {
|
||||
|
|
@ -554,11 +577,11 @@ void GridAtlasPacker::setMinimumScale(double minScale) {
|
|||
this->minScale = minScale;
|
||||
}
|
||||
|
||||
void GridAtlasPacker::setUnitRange(double unitRange) {
|
||||
void GridAtlasPacker::setUnitRange(msdfgen::Range unitRange) {
|
||||
this->unitRange = unitRange;
|
||||
}
|
||||
|
||||
void GridAtlasPacker::setPixelRange(double pxRange) {
|
||||
void GridAtlasPacker::setPixelRange(msdfgen::Range pxRange) {
|
||||
this->pxRange = pxRange;
|
||||
}
|
||||
|
||||
|
|
@ -574,6 +597,22 @@ void GridAtlasPacker::setOriginPixelAlignment(bool alignX, bool 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 {
|
||||
width = this->width, height = this->height;
|
||||
}
|
||||
|
|
@ -594,7 +633,7 @@ double GridAtlasPacker::getScale() const {
|
|||
return scale;
|
||||
}
|
||||
|
||||
double GridAtlasPacker::getPixelRange() const {
|
||||
msdfgen::Range GridAtlasPacker::getPixelRange() const {
|
||||
return pxRange+scale*unitRange;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "Padding.h"
|
||||
#include "GlyphGeometry.h"
|
||||
|
||||
namespace msdf_atlas {
|
||||
|
|
@ -40,14 +41,22 @@ public:
|
|||
/// Sets the minimum glyph scale
|
||||
void setMinimumScale(double minScale);
|
||||
/// 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
|
||||
void setPixelRange(double pxRange);
|
||||
void setPixelRange(msdfgen::Range pxRange);
|
||||
/// Sets the miter limit for bounds computation
|
||||
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);
|
||||
/// 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
|
||||
void getDimensions(int &width, int &height) const;
|
||||
|
|
@ -60,7 +69,7 @@ public:
|
|||
/// Returns the final glyph scale
|
||||
double getScale() const;
|
||||
/// 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
|
||||
void getFixedOrigin(double &x, double &y);
|
||||
/// 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 minScale;
|
||||
double fixedX, fixedY;
|
||||
double unitRange;
|
||||
double pxRange;
|
||||
msdfgen::Range unitRange;
|
||||
msdfgen::Range pxRange;
|
||||
double miterLimit;
|
||||
bool pxAlignOriginX, pxAlignOriginY;
|
||||
Padding innerUnitPadding, outerUnitPadding;
|
||||
Padding innerPxPadding, outerPxPadding;
|
||||
double scaleMaximizationTolerance;
|
||||
double alignedColumnsBias;
|
||||
bool cutoff;
|
||||
|
|
@ -89,7 +100,7 @@ private:
|
|||
static void raiseToConstraint(int &width, int &height, DimensionsConstraint constraint);
|
||||
|
||||
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;
|
||||
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
double range = unitRange+pxRange/scale;
|
||||
// Wrap glyphs into boxes
|
||||
std::vector<Rectangle> rectangles;
|
||||
std::vector<GlyphGeometry *> rectangleGlyphs;
|
||||
rectangles.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) {
|
||||
if (!glyph->isWhitespace()) {
|
||||
Rectangle rect = { };
|
||||
glyph->wrapBox(scale, range, miterLimit, pxAlignOriginX, pxAlignOriginY);
|
||||
glyph->wrapBox(attribs);
|
||||
glyph->getBoxSize(rect.w, rect.h);
|
||||
if (rect.w > 0 && rect.h > 0) {
|
||||
rectangles.push_back(rect);
|
||||
|
|
@ -143,11 +150,11 @@ void TightAtlasPacker::setMinimumScale(double minScale) {
|
|||
this->minScale = minScale;
|
||||
}
|
||||
|
||||
void TightAtlasPacker::setUnitRange(double unitRange) {
|
||||
void TightAtlasPacker::setUnitRange(msdfgen::Range unitRange) {
|
||||
this->unitRange = unitRange;
|
||||
}
|
||||
|
||||
void TightAtlasPacker::setPixelRange(double pxRange) {
|
||||
void TightAtlasPacker::setPixelRange(msdfgen::Range pxRange) {
|
||||
this->pxRange = pxRange;
|
||||
}
|
||||
|
||||
|
|
@ -163,6 +170,22 @@ void TightAtlasPacker::setOriginPixelAlignment(bool alignX, bool 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 {
|
||||
width = this->width, height = this->height;
|
||||
}
|
||||
|
|
@ -171,7 +194,7 @@ double TightAtlasPacker::getScale() const {
|
|||
return scale;
|
||||
}
|
||||
|
||||
double TightAtlasPacker::getPixelRange() const {
|
||||
msdfgen::Range TightAtlasPacker::getPixelRange() const {
|
||||
return pxRange+scale*unitRange;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
#include "Padding.h"
|
||||
#include "GlyphGeometry.h"
|
||||
|
||||
namespace msdf_atlas {
|
||||
|
|
@ -31,21 +32,29 @@ public:
|
|||
/// Sets the minimum glyph scale
|
||||
void setMinimumScale(double minScale);
|
||||
/// 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
|
||||
void setPixelRange(double pxRange);
|
||||
void setPixelRange(msdfgen::Range pxRange);
|
||||
/// Sets the miter limit for bounds computation
|
||||
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);
|
||||
/// 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
|
||||
void getDimensions(int &width, int &height) const;
|
||||
/// Returns the final glyph scale
|
||||
double getScale() const;
|
||||
/// Returns the final combined pixel range (including converted unit range)
|
||||
double getPixelRange() const;
|
||||
msdfgen::Range getPixelRange() const;
|
||||
|
||||
private:
|
||||
int width, height;
|
||||
|
|
@ -53,10 +62,12 @@ private:
|
|||
DimensionsConstraint dimensionsConstraint;
|
||||
double scale;
|
||||
double minScale;
|
||||
double unitRange;
|
||||
double pxRange;
|
||||
msdfgen::Range unitRange;
|
||||
msdfgen::Range pxRange;
|
||||
double miterLimit;
|
||||
bool pxAlignOriginX, pxAlignOriginY;
|
||||
Padding innerUnitPadding, outerUnitPadding;
|
||||
Padding innerPxPadding, outerPxPadding;
|
||||
double scaleMaximizationTolerance;
|
||||
|
||||
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.imageType = convertImageType(properties.imageType);
|
||||
fontVariant.metrics.fontSize = REAL(properties.fontSize*fontMetrics.emSize);
|
||||
if (properties.imageType != ImageType::HARD_MASK)
|
||||
fontVariant.metrics.distanceRange = REAL(properties.pxRange);
|
||||
if (properties.imageType != ImageType::HARD_MASK) {
|
||||
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.ascender = REAL(fontMetrics.ascenderY);
|
||||
fontVariant.metrics.descender = REAL(fontMetrics.descenderY);
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ namespace msdf_atlas {
|
|||
|
||||
struct ArteryFontExportProperties {
|
||||
double fontSize;
|
||||
double pxRange;
|
||||
msdfgen::Range pxRange;
|
||||
ImageType imageType;
|
||||
ImageFormat imageFormat;
|
||||
YDirection yDirection;
|
||||
|
|
|
|||
|
|
@ -67,8 +67,10 @@ bool exportJSON(const FontGeometry *fonts, int fontCount, ImageType imageType, c
|
|||
// Atlas properties
|
||||
fputs("\"atlas\":{", f); {
|
||||
fprintf(f, "\"type\":\"%s\",", imageTypeString(imageType));
|
||||
if (imageType == ImageType::SDF || imageType == ImageType::PSDF || imageType == ImageType::MSDF || imageType == ImageType::MTSDF)
|
||||
fprintf(f, "\"distanceRange\":%.17g,", metrics.distanceRange);
|
||||
if (imageType == ImageType::SDF || imageType == ImageType::PSDF || imageType == ImageType::MSDF || imageType == ImageType::MTSDF) {
|
||||
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, "\"width\":%d,", metrics.width);
|
||||
fprintf(f, "\"height\":%d,", metrics.height);
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ struct JsonAtlasMetrics {
|
|||
const double *originX, *originY;
|
||||
int spacing;
|
||||
};
|
||||
double distanceRange;
|
||||
msdfgen::Range distanceRange;
|
||||
double size;
|
||||
int width, height;
|
||||
YDirection yDirection;
|
||||
|
|
|
|||
|
|
@ -128,14 +128,28 @@ GLYPH CONFIGURATION
|
|||
Specifies the size of the glyphs in the atlas bitmap in pixels per em.
|
||||
-minsize <em size>
|
||||
Specifies the minimum size. The largest possible size that fits the same atlas dimensions will be used.
|
||||
-emrange <em range>
|
||||
Specifies the SDF distance range in em's.
|
||||
-pxrange <pixel range>
|
||||
Specifies the SDF distance range in output pixels. The default value is 2.
|
||||
-emrange <em range width>
|
||||
Specifies the width of the representable SDF distance range in ems.
|
||||
-pxrange <pixel range width>
|
||||
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>
|
||||
Specifies whether each glyph's origin point should be aligned with the pixel grid.
|
||||
-nokerning
|
||||
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
|
||||
-angle <angle>
|
||||
|
|
@ -264,6 +278,13 @@ static msdfgen::FontHandle *loadVarFont(msdfgen::FreetypeHandle *library, const
|
|||
}
|
||||
#endif
|
||||
|
||||
enum class Units {
|
||||
/// Value is specified in ems
|
||||
EMS,
|
||||
/// Value is specified in pixels
|
||||
PIXELS
|
||||
};
|
||||
|
||||
struct FontInput {
|
||||
const char *fontFilename;
|
||||
bool variableFont;
|
||||
|
|
@ -279,7 +300,7 @@ struct Configuration {
|
|||
YDirection yDirection;
|
||||
int width, height;
|
||||
double emSize;
|
||||
double pxRange;
|
||||
msdfgen::Range pxRange;
|
||||
double angleThreshold;
|
||||
double miterLimit;
|
||||
bool pxAlignOriginX, pxAlignOriginY;
|
||||
|
|
@ -370,13 +391,12 @@ int main(int argc, const char *const *argv) {
|
|||
config.generatorAttributes.config.overlapSupport = !config.preprocessGeometry;
|
||||
config.generatorAttributes.scanlinePass = !config.preprocessGeometry;
|
||||
double minEmSize = 0;
|
||||
enum {
|
||||
/// Range specified in ems
|
||||
RANGE_EM,
|
||||
/// Range specified in output pixels
|
||||
RANGE_PIXEL,
|
||||
} rangeMode = RANGE_PIXEL;
|
||||
double rangeValue = 0;
|
||||
Units rangeUnits = Units::PIXELS;
|
||||
msdfgen::Range rangeValue = 0;
|
||||
Padding innerPadding;
|
||||
Padding outerPadding;
|
||||
Units innerPaddingUnits = Units::EMS;
|
||||
Units outerPaddingUnits = Units::EMS;
|
||||
PackingStyle packingStyle = PackingStyle::TIGHT;
|
||||
DimensionsConstraint atlasSizeConstraint = DimensionsConstraint::NONE;
|
||||
DimensionsConstraint cellSizeConstraint = DimensionsConstraint::NONE;
|
||||
|
|
@ -577,20 +597,40 @@ int main(int argc, const char *const *argv) {
|
|||
}
|
||||
ARG_CASE("-emrange", 1) {
|
||||
double r;
|
||||
if (!(parseDouble(r, argv[argPos++]) && r >= 0))
|
||||
ABORT("Invalid range argument. Use -emrange <em range> with a positive real number.");
|
||||
rangeMode = RANGE_EM;
|
||||
if (!(parseDouble(r, argv[argPos++]) && r != 0))
|
||||
ABORT("Invalid range argument. Use -emrange <em range> with a non-zero real number.");
|
||||
rangeUnits = Units::EMS;
|
||||
rangeValue = r;
|
||||
continue;
|
||||
}
|
||||
ARG_CASE("-pxrange", 1) {
|
||||
double r;
|
||||
if (!(parseDouble(r, argv[argPos++]) && r >= 0))
|
||||
ABORT("Invalid range argument. Use -pxrange <pixel range> with a positive real number.");
|
||||
rangeMode = RANGE_PIXEL;
|
||||
if (!(parseDouble(r, argv[argPos++]) && r != 0))
|
||||
ABORT("Invalid range argument. Use -pxrange <pixel range> with a non-zero real number.");
|
||||
rangeUnits = Units::PIXELS;
|
||||
rangeValue = r;
|
||||
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) {
|
||||
if (ARG_IS("off") || ARG_PREFIX("disable") || ARG_IS("0") || ARG_IS("false") || ARG_PREFIX("n"))
|
||||
config.pxAlignOriginX = false, config.pxAlignOriginY = false;
|
||||
|
|
@ -605,6 +645,70 @@ int main(int argc, const char *const *argv) {
|
|||
++argPos;
|
||||
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) {
|
||||
double at;
|
||||
if (!parseAngle(at, argv[argPos++]))
|
||||
|
|
@ -841,10 +945,10 @@ int main(int argc, const char *const *argv) {
|
|||
minEmSize = DEFAULT_SIZE;
|
||||
}
|
||||
if (config.imageType == ImageType::HARD_MASK || config.imageType == ImageType::SOFT_MASK) {
|
||||
rangeMode = RANGE_PIXEL;
|
||||
rangeUnits = Units::PIXELS;
|
||||
rangeValue = 1;
|
||||
} else if (rangeValue <= 0) {
|
||||
rangeMode = RANGE_PIXEL;
|
||||
} else if (rangeValue.lower == rangeValue.upper) {
|
||||
rangeUnits = Units::PIXELS;
|
||||
rangeValue = DEFAULT_PIXEL_RANGE;
|
||||
}
|
||||
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
|
||||
{
|
||||
double unitRange = 0, pxRange = 0;
|
||||
switch (rangeMode) {
|
||||
case RANGE_EM:
|
||||
unitRange = rangeValue;
|
||||
msdfgen::Range emRange = 0, pxRange = 0;
|
||||
switch (rangeUnits) {
|
||||
case Units::EMS:
|
||||
emRange = rangeValue;
|
||||
break;
|
||||
case RANGE_PIXEL:
|
||||
case Units::PIXELS:
|
||||
pxRange = rangeValue;
|
||||
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 fixedScale = config.emSize > 0;
|
||||
switch (packingStyle) {
|
||||
|
|
@ -1077,9 +1199,13 @@ int main(int argc, const char *const *argv) {
|
|||
else
|
||||
atlasPacker.setMinimumScale(minEmSize);
|
||||
atlasPacker.setPixelRange(pxRange);
|
||||
atlasPacker.setUnitRange(unitRange);
|
||||
atlasPacker.setUnitRange(emRange);
|
||||
atlasPacker.setMiterLimit(config.miterLimit);
|
||||
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 (remaining < 0) {
|
||||
ABORT("Failed to pack glyphs into atlas.");
|
||||
|
|
@ -1119,9 +1245,13 @@ int main(int argc, const char *const *argv) {
|
|||
else
|
||||
atlasPacker.setMinimumScale(minEmSize);
|
||||
atlasPacker.setPixelRange(pxRange);
|
||||
atlasPacker.setUnitRange(unitRange);
|
||||
atlasPacker.setUnitRange(emRange);
|
||||
atlasPacker.setMiterLimit(config.miterLimit);
|
||||
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 (remaining < 0) {
|
||||
ABORT("Failed to pack glyphs into atlas.");
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
#include "types.h"
|
||||
#include "utf8.h"
|
||||
#include "Rectangle.h"
|
||||
#include "Padding.h"
|
||||
#include "Charset.h"
|
||||
#include "GlyphBox.h"
|
||||
#include "GlyphGeometry.h"
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
namespace msdf_atlas {
|
||||
|
||||
static const char *const shadronFillGlyphMask = R"(
|
||||
template <ATLAS, RANGE, COLOR>
|
||||
template <ATLAS, RANGE, ZERO_DIST, COLOR>
|
||||
glsl vec4 fillGlyph(vec2 texCoord) {
|
||||
float fill = texture((ATLAS), texCoord).r;
|
||||
return vec4(vec3(COLOR), fill);
|
||||
|
|
@ -15,10 +15,10 @@ glsl vec4 fillGlyph(vec2 texCoord) {
|
|||
)";
|
||||
|
||||
static const char *const shadronFillGlyphSdf = R"(
|
||||
template <ATLAS, RANGE, COLOR>
|
||||
template <ATLAS, RANGE, ZERO_DIST, COLOR>
|
||||
glsl vec4 fillGlyph(vec2 texCoord) {
|
||||
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);
|
||||
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);
|
||||
}
|
||||
%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), \
|
||||
fragment_data(vec2), \
|
||||
vertex(projectVertex<TEXT_SIZE>, triangles, VERTEX_LIST), \
|
||||
fragment(fillGlyph<ATLAS, RANGE, COLOR>), \
|
||||
fragment(fillGlyph<ATLAS, RANGE, ZERO_DIST, COLOR>), \
|
||||
depth(false), \
|
||||
blend(transparency), \
|
||||
background(vec4(vec3(COLOR), 0.0)), \
|
||||
|
|
@ -94,7 +94,7 @@ static std::string escapeString(const std::string &str) {
|
|||
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)
|
||||
return false;
|
||||
double texelWidth = 1./atlasWidth;
|
||||
|
|
@ -109,7 +109,9 @@ bool generateShadronPreview(const FontGeometry *fonts, int fontCount, ImageType
|
|||
else
|
||||
fprintf(file, "image Atlas = file()");
|
||||
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();
|
||||
for (int i = 1; i < fontCount; ++i) {
|
||||
|
|
@ -163,7 +165,7 @@ bool generateShadronPreview(const FontGeometry *fonts, int fontCount, ImageType
|
|||
fputs("};\n", file);
|
||||
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);
|
||||
fclose(file);
|
||||
return anyGlyphs;
|
||||
|
|
|
|||
|
|
@ -9,6 +9,6 @@
|
|||
namespace msdf_atlas {
|
||||
|
||||
/// Generates a Shadron script that displays a string using the generated atlas
|
||||
bool generateShadronPreview(const FontGeometry *fonts, int fontCount, ImageType atlasType, int atlasWidth, int atlasHeight, double pxRange, const unicode_t *text, const char *imageFilename, bool fullRange, const char *outputFilename);
|
||||
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