1
0
Fork 0

Merge pull request #113965 from akien-mga/msdfgen-1.13

msdfgen: Update to 1.13
This commit is contained in:
Thaddeus Crews 2025-12-15 08:01:02 -06:00
commit 4d0ddafa21
No known key found for this signature in database
GPG Key ID: 8C6E5FEB5FC03CCC
43 changed files with 807 additions and 1041 deletions

View File

@ -23,6 +23,7 @@ if env["builtin_msdfgen"]:
"core/Scanline.cpp",
"core/Shape.cpp",
"core/contour-combiners.cpp",
"core/convergent-curve-ordering.cpp",
"core/edge-coloring.cpp",
"core/edge-segments.cpp",
"core/edge-selectors.cpp",

View File

@ -130,6 +130,7 @@ if env["msdfgen_enabled"] and env["freetype_enabled"]:
"core/Scanline.cpp",
"core/Shape.cpp",
"core/contour-combiners.cpp",
"core/convergent-curve-ordering.cpp",
"core/edge-coloring.cpp",
"core/edge-segments.cpp",
"core/edge-selectors.cpp",

View File

@ -125,6 +125,7 @@ if env["msdfgen_enabled"] and env["freetype_enabled"]:
"core/Scanline.cpp",
"core/Shape.cpp",
"core/contour-combiners.cpp",
"core/convergent-curve-ordering.cpp",
"core/edge-coloring.cpp",
"core/edge-segments.cpp",
"core/edge-selectors.cpp",

View File

@ -850,15 +850,19 @@ Collection of single-file libraries used in Godot components.
## msdfgen
- Upstream: https://github.com/Chlumsky/msdfgen
- Version: 1.12.1 (6574da1310df433c97ca0fddcab7e463c31e58f8, 2025)
- Version: 1.13 (1874bcf7d9624ccc85b4bc9a85d78116f690f35b, 2025)
- License: MIT
Files extracted from the upstream source:
- `msdfgen.h`
- Files in `core/` folder
- Files in `core/` folder, minus `export-svg.*` and `save-*.*` files
- `LICENSE.txt`
Patches:
- `0001-remove-unused-save-features.patch` ([GH-113965](https://github.com/godotengine/godot/issues/113965))
## openxr

View File

@ -1,6 +1,7 @@
#pragma once
#include "YAxisOrientation.h"
#include "BitmapRef.hpp"
namespace msdfgen {
@ -11,14 +12,16 @@ class Bitmap {
public:
Bitmap();
Bitmap(int width, int height);
Bitmap(const BitmapConstRef<T, N> &orig);
Bitmap(int width, int height, YAxisOrientation yOrientation = MSDFGEN_Y_AXIS_DEFAULT_ORIENTATION);
explicit Bitmap(const BitmapConstRef<T, N> &orig);
explicit Bitmap(const BitmapConstSection<T, N> &orig);
Bitmap(const Bitmap<T, N> &orig);
#ifdef MSDFGEN_USE_CPP11
Bitmap(Bitmap<T, N> &&orig);
#endif
~Bitmap();
Bitmap<T, N> &operator=(const BitmapConstRef<T, N> &orig);
Bitmap<T, N> &operator=(const BitmapConstSection<T, N> &orig);
Bitmap<T, N> &operator=(const Bitmap<T, N> &orig);
#ifdef MSDFGEN_USE_CPP11
Bitmap<T, N> &operator=(Bitmap<T, N> &&orig);
@ -38,10 +41,17 @@ public:
#endif
operator BitmapRef<T, N>();
operator BitmapConstRef<T, N>() const;
operator BitmapSection<T, N>();
operator BitmapConstSection<T, N>() const;
/// Returns a reference to a rectangular section of the bitmap specified by bounds (excluding xMax, yMax).
BitmapSection<T, N> getSection(int xMin, int yMin, int xMax, int yMax);
/// Returns a constant reference to a rectangular section of the bitmap specified by bounds (excluding xMax, yMax).
BitmapConstSection<T, N> getConstSection(int xMin, int yMin, int xMax, int yMax) const;
private:
T *pixels;
int w, h;
YAxisOrientation yOrientation;
};

View File

@ -7,28 +7,41 @@
namespace msdfgen {
template <typename T, int N>
Bitmap<T, N>::Bitmap() : pixels(NULL), w(0), h(0) { }
Bitmap<T, N>::Bitmap() : pixels(NULL), w(0), h(0), yOrientation(MSDFGEN_Y_AXIS_DEFAULT_ORIENTATION) { }
template <typename T, int N>
Bitmap<T, N>::Bitmap(int width, int height) : w(width), h(height) {
Bitmap<T, N>::Bitmap(int width, int height, YAxisOrientation yOrientation) : w(width), h(height), yOrientation(yOrientation) {
pixels = new T[N*w*h];
}
template <typename T, int N>
Bitmap<T, N>::Bitmap(const BitmapConstRef<T, N> &orig) : w(orig.width), h(orig.height) {
Bitmap<T, N>::Bitmap(const BitmapConstRef<T, N> &orig) : w(orig.width), h(orig.height), yOrientation(orig.yOrientation) {
pixels = new T[N*w*h];
memcpy(pixels, orig.pixels, sizeof(T)*N*w*h);
}
template <typename T, int N>
Bitmap<T, N>::Bitmap(const Bitmap<T, N> &orig) : w(orig.w), h(orig.h) {
Bitmap<T, N>::Bitmap(const BitmapConstSection<T, N> &orig) : w(orig.width), h(orig.height), yOrientation(orig.yOrientation) {
pixels = new T[N*w*h];
T *dst = pixels;
const T *src = orig.pixels;
int rowLength = N*w;
for (int y = 0; y < h; ++y) {
memcpy(dst, src, sizeof(T)*rowLength);
dst += rowLength;
src += orig.rowStride;
}
}
template <typename T, int N>
Bitmap<T, N>::Bitmap(const Bitmap<T, N> &orig) : w(orig.w), h(orig.h), yOrientation(orig.yOrientation) {
pixels = new T[N*w*h];
memcpy(pixels, orig.pixels, sizeof(T)*N*w*h);
}
#ifdef MSDFGEN_USE_CPP11
template <typename T, int N>
Bitmap<T, N>::Bitmap(Bitmap<T, N> &&orig) : pixels(orig.pixels), w(orig.w), h(orig.h) {
Bitmap<T, N>::Bitmap(Bitmap<T, N> &&orig) : pixels(orig.pixels), w(orig.w), h(orig.h), yOrientation(orig.yOrientation) {
orig.pixels = NULL;
orig.w = 0, orig.h = 0;
}
@ -36,25 +49,46 @@ Bitmap<T, N>::Bitmap(Bitmap<T, N> &&orig) : pixels(orig.pixels), w(orig.w), h(or
template <typename T, int N>
Bitmap<T, N>::~Bitmap() {
delete [] pixels;
delete[] pixels;
}
template <typename T, int N>
Bitmap<T, N> &Bitmap<T, N>::operator=(const BitmapConstRef<T, N> &orig) {
if (pixels != orig.pixels) {
delete [] pixels;
delete[] pixels;
w = orig.width, h = orig.height;
yOrientation = orig.yOrientation;
pixels = new T[N*w*h];
memcpy(pixels, orig.pixels, sizeof(T)*N*w*h);
}
return *this;
}
template <typename T, int N>
Bitmap<T, N> &Bitmap<T, N>::operator=(const BitmapConstSection<T, N> &orig) {
if (orig.pixels && orig.pixels >= pixels && orig.pixels < pixels+N*w*h)
return *this = Bitmap<T, N>(orig);
delete[] pixels;
w = orig.width, h = orig.height;
yOrientation = orig.yOrientation;
pixels = new T[N*w*h];
T *dst = pixels;
const T *src = orig.pixels;
int rowLength = N*w;
for (int y = 0; y < h; ++y) {
memcpy(dst, src, sizeof(T)*rowLength);
dst += rowLength;
src += orig.rowStride;
}
return *this;
}
template <typename T, int N>
Bitmap<T, N> &Bitmap<T, N>::operator=(const Bitmap<T, N> &orig) {
if (this != &orig) {
delete [] pixels;
delete[] pixels;
w = orig.w, h = orig.h;
yOrientation = orig.yOrientation;
pixels = new T[N*w*h];
memcpy(pixels, orig.pixels, sizeof(T)*N*w*h);
}
@ -65,9 +99,10 @@ Bitmap<T, N> &Bitmap<T, N>::operator=(const Bitmap<T, N> &orig) {
template <typename T, int N>
Bitmap<T, N> &Bitmap<T, N>::operator=(Bitmap<T, N> &&orig) {
if (this != &orig) {
delete [] pixels;
delete[] pixels;
pixels = orig.pixels;
w = orig.w, h = orig.h;
yOrientation = orig.yOrientation;
orig.pixels = NULL;
}
return *this;
@ -106,12 +141,32 @@ Bitmap<T, N>::operator const T *() const {
template <typename T, int N>
Bitmap<T, N>::operator BitmapRef<T, N>() {
return BitmapRef<T, N>(pixels, w, h);
return BitmapRef<T, N>(pixels, w, h, yOrientation);
}
template <typename T, int N>
Bitmap<T, N>::operator BitmapConstRef<T, N>() const {
return BitmapConstRef<T, N>(pixels, w, h);
return BitmapConstRef<T, N>(pixels, w, h, yOrientation);
}
template <typename T, int N>
Bitmap<T, N>::operator BitmapSection<T, N>() {
return BitmapSection<T, N>(pixels, w, h, yOrientation);
}
template <typename T, int N>
Bitmap<T, N>::operator BitmapConstSection<T, N>() const {
return BitmapConstSection<T, N>(pixels, w, h, yOrientation);
}
template <typename T, int N>
BitmapSection<T, N> Bitmap<T, N>::getSection(int xMin, int yMin, int xMax, int yMax) {
return BitmapSection<T, N>(pixels+N*(w*yMin+xMin), xMax-xMin, yMax-yMin, N*w, yOrientation);
}
template <typename T, int N>
BitmapConstSection<T, N> Bitmap<T, N>::getConstSection(int xMin, int yMin, int xMax, int yMax) const {
return BitmapConstSection<T, N>(pixels+N*(w*yMin+xMin), xMax-xMin, yMax-yMin, N*w, yOrientation);
}
}

View File

@ -1,41 +1,154 @@
#pragma once
#include "base.h"
#include "YAxisOrientation.h"
namespace msdfgen {
/// Reference to a 2D image bitmap or a buffer acting as one. Pixel storage not owned or managed by the object.
template <typename T, int N = 1>
struct BitmapRef;
/// Constant reference to a 2D image bitmap or a buffer acting as one. Pixel storage not owned or managed by the object.
template <typename T, int N = 1>
struct BitmapConstRef;
/// Reference to a 2D image bitmap with non-contiguous rows of pixels. Pixel storage not owned or managed by the object. Can represent e.g. a section of a larger bitmap, bitmap with padded rows, or vertically flipped bitmap (rowStride can be negative).
template <typename T, int N = 1>
struct BitmapSection;
/// Constant reference to a 2D image bitmap with non-contiguous rows of pixels. Pixel storage not owned or managed by the object. Can represent e.g. a section of a larger bitmap, bitmap with padded rows, or vertically flipped bitmap (rowStride can be negative).
template <typename T, int N = 1>
struct BitmapConstSection;
template <typename T, int N>
struct BitmapRef {
T *pixels;
int width, height;
YAxisOrientation yOrientation;
inline BitmapRef() : pixels(NULL), width(0), height(0) { }
inline BitmapRef(T *pixels, int width, int height) : pixels(pixels), width(width), height(height) { }
inline BitmapRef() : pixels(NULL), width(0), height(0), yOrientation(MSDFGEN_Y_AXIS_DEFAULT_ORIENTATION) { }
inline BitmapRef(T *pixels, int width, int height, YAxisOrientation yOrientation = MSDFGEN_Y_AXIS_DEFAULT_ORIENTATION) : pixels(pixels), width(width), height(height), yOrientation(yOrientation) { }
inline T *operator()(int x, int y) const {
return pixels+N*(width*y+x);
}
/// Returns a reference to a rectangular section of the bitmap specified by bounds (excluding xMax, yMax).
inline BitmapSection<T, N> getSection(int xMin, int yMin, int xMax, int yMax) const {
return BitmapSection<T, N>(pixels+N*(width*yMin+xMin), xMax-xMin, yMax-yMin, N*width, yOrientation);
}
/// Returns a constant reference to a rectangular section of the bitmap specified by bounds (excluding xMax, yMax).
inline BitmapConstSection<T, N> getConstSection(int xMin, int yMin, int xMax, int yMax) const {
return BitmapConstSection<T, N>(pixels+N*(width*yMin+xMin), xMax-xMin, yMax-yMin, N*width, yOrientation);
}
};
/// Constant reference to a 2D image bitmap or a buffer acting as one. Pixel storage not owned or managed by the object.
template <typename T, int N = 1>
template <typename T, int N>
struct BitmapConstRef {
const T *pixels;
int width, height;
YAxisOrientation yOrientation;
inline BitmapConstRef() : pixels(NULL), width(0), height(0) { }
inline BitmapConstRef(const T *pixels, int width, int height) : pixels(pixels), width(width), height(height) { }
inline BitmapConstRef(const BitmapRef<T, N> &orig) : pixels(orig.pixels), width(orig.width), height(orig.height) { }
inline BitmapConstRef() : pixels(NULL), width(0), height(0), yOrientation(MSDFGEN_Y_AXIS_DEFAULT_ORIENTATION) { }
inline BitmapConstRef(const T *pixels, int width, int height, YAxisOrientation yOrientation = MSDFGEN_Y_AXIS_DEFAULT_ORIENTATION) : pixels(pixels), width(width), height(height), yOrientation(yOrientation) { }
inline BitmapConstRef(const BitmapRef<T, N> &orig) : pixels(orig.pixels), width(orig.width), height(orig.height), yOrientation(orig.yOrientation) { }
inline const T *operator()(int x, int y) const {
return pixels+N*(width*y+x);
}
/// Returns a constant reference to a rectangular section of the bitmap specified by bounds (excluding xMax, yMax).
inline BitmapConstSection<T, N> getSection(int xMin, int yMin, int xMax, int yMax) const {
return BitmapConstSection<T, N>(pixels+N*(width*yMin+xMin), xMax-xMin, yMax-yMin, N*width, yOrientation);
}
/// Returns a constant reference to a rectangular section of the bitmap specified by bounds (excluding xMax, yMax).
inline BitmapConstSection<T, N> getConstSection(int xMin, int yMin, int xMax, int yMax) const {
return getSection(xMin, yMin, xMax, yMax);
}
};
template <typename T, int N>
struct BitmapSection {
T *pixels;
int width, height;
/// Specifies the difference between the beginnings of adjacent pixel rows as the number of T elements, can be negative.
int rowStride;
YAxisOrientation yOrientation;
inline BitmapSection() : pixels(NULL), width(0), height(0), rowStride(0), yOrientation(MSDFGEN_Y_AXIS_DEFAULT_ORIENTATION) { }
inline BitmapSection(T *pixels, int width, int height, YAxisOrientation yOrientation = MSDFGEN_Y_AXIS_DEFAULT_ORIENTATION) : pixels(pixels), width(width), height(height), rowStride(N*width), yOrientation(yOrientation) { }
inline BitmapSection(T *pixels, int width, int height, int rowStride, YAxisOrientation yOrientation = MSDFGEN_Y_AXIS_DEFAULT_ORIENTATION) : pixels(pixels), width(width), height(height), rowStride(rowStride), yOrientation(yOrientation) { }
inline BitmapSection(const BitmapRef<T, N> &orig) : pixels(orig.pixels), width(orig.width), height(orig.height), rowStride(N*orig.width), yOrientation(orig.yOrientation) { }
inline T *operator()(int x, int y) const {
return pixels+rowStride*y+N*x;
}
/// Returns a reference to a rectangular subsection of the bitmap specified by bounds (excluding xMax, yMax).
inline BitmapSection<T, N> getSection(int xMin, int yMin, int xMax, int yMax) const {
return BitmapSection<T, N>(pixels+rowStride*yMin+N*xMin, xMax-xMin, yMax-yMin, rowStride, yOrientation);
}
/// Returns a constant reference to a rectangular subsection of the bitmap specified by bounds (excluding xMax, yMax).
inline BitmapConstSection<T, N> getConstSection(int xMin, int yMin, int xMax, int yMax) const {
return BitmapConstSection<T, N>(pixels+rowStride*yMin+N*xMin, xMax-xMin, yMax-yMin, rowStride, yOrientation);
}
/// Makes sure that the section's Y-axis orientation matches the argument by potentially reordering its rows.
inline void reorient(YAxisOrientation newYAxisOrientation) {
if (yOrientation != newYAxisOrientation) {
pixels += rowStride*(height-1);
rowStride = -rowStride;
yOrientation = newYAxisOrientation;
}
}
};
template <typename T, int N>
struct BitmapConstSection {
const T *pixels;
int width, height;
/// Specifies the difference between the beginnings of adjacent pixel rows as the number of T elements, can be negative.
int rowStride;
YAxisOrientation yOrientation;
inline BitmapConstSection() : pixels(NULL), width(0), height(0), rowStride(0), yOrientation(MSDFGEN_Y_AXIS_DEFAULT_ORIENTATION) { }
inline BitmapConstSection(const T *pixels, int width, int height, YAxisOrientation yOrientation = MSDFGEN_Y_AXIS_DEFAULT_ORIENTATION) : pixels(pixels), width(width), height(height), rowStride(N*width), yOrientation(yOrientation) { }
inline BitmapConstSection(const T *pixels, int width, int height, int rowStride, YAxisOrientation yOrientation = MSDFGEN_Y_AXIS_DEFAULT_ORIENTATION) : pixels(pixels), width(width), height(height), rowStride(rowStride), yOrientation(yOrientation) { }
inline BitmapConstSection(const BitmapRef<T, N> &orig) : pixels(orig.pixels), width(orig.width), height(orig.height), rowStride(N*orig.width), yOrientation(orig.yOrientation) { }
inline BitmapConstSection(const BitmapConstRef<T, N> &orig) : pixels(orig.pixels), width(orig.width), height(orig.height), rowStride(N*orig.width), yOrientation(orig.yOrientation) { }
inline BitmapConstSection(const BitmapSection<T, N> &orig) : pixels(orig.pixels), width(orig.width), height(orig.height), rowStride(orig.rowStride), yOrientation(orig.yOrientation) { }
inline const T *operator()(int x, int y) const {
return pixels+rowStride*y+N*x;
}
/// Returns a constant reference to a rectangular subsection of the bitmap specified by bounds (excluding xMax, yMax).
inline BitmapConstSection<T, N> getSection(int xMin, int yMin, int xMax, int yMax) const {
return BitmapConstSection<T, N>(pixels+rowStride*yMin+N*xMin, xMax-xMin, yMax-yMin, rowStride, yOrientation);
}
/// Returns a constant reference to a rectangular subsection of the bitmap specified by bounds (excluding xMax, yMax).
inline BitmapConstSection<T, N> getConstSection(int xMin, int yMin, int xMax, int yMax) const {
return getSection(xMin, yMin, xMax, yMax);
}
/// Makes sure that the section's Y-axis orientation matches the argument by potentially reordering its rows.
inline void reorient(YAxisOrientation newYAxisOrientation) {
if (yOrientation != newYAxisOrientation) {
pixels += rowStride*(height-1);
rowStride = -rowStride;
yOrientation = newYAxisOrientation;
}
}
};
}

View File

@ -24,19 +24,19 @@ EdgeHolder &Contour::addEdge() {
return edges.back();
}
static void boundPoint(double &l, double &b, double &r, double &t, Point2 p) {
if (p.x < l) l = p.x;
if (p.y < b) b = p.y;
if (p.x > r) r = p.x;
if (p.y > t) t = p.y;
static void boundPoint(double &xMin, double &yMin, double &xMax, double &yMax, Point2 p) {
if (p.x < xMin) xMin = p.x;
if (p.y < yMin) yMin = p.y;
if (p.x > xMax) xMax = p.x;
if (p.y > yMax) yMax = p.y;
}
void Contour::bound(double &l, double &b, double &r, double &t) const {
void Contour::bound(double &xMin, double &yMin, double &xMax, double &yMax) const {
for (std::vector<EdgeHolder>::const_iterator edge = edges.begin(); edge != edges.end(); ++edge)
(*edge)->bound(l, b, r, t);
(*edge)->bound(xMin, yMin, xMax, yMax);
}
void Contour::boundMiters(double &l, double &b, double &r, double &t, double border, double miterLimit, int polarity) const {
void Contour::boundMiters(double &xMin, double &yMin, double &xMax, double &yMax, double border, double miterLimit, int polarity) const {
if (edges.empty())
return;
Vector2 prevDir = edges.back()->direction(1).normalize(true);
@ -48,7 +48,7 @@ void Contour::boundMiters(double &l, double &b, double &r, double &t, double bor
if (q > 0)
miterLength = min(1/sqrt(q), miterLimit);
Point2 miter = (*edge)->point(0)+border*miterLength*(prevDir+dir).normalize(true);
boundPoint(l, b, r, t, miter);
boundPoint(xMin, yMin, xMax, yMax, miter);
}
prevDir = (*edge)->direction(1).normalize(true);
}

View File

@ -21,9 +21,9 @@ public:
/// Creates a new edge in the contour and returns its reference.
EdgeHolder &addEdge();
/// Adjusts the bounding box to fit the contour.
void bound(double &l, double &b, double &r, double &t) const;
void bound(double &xMin, double &yMin, double &xMax, double &yMax) const;
/// Adjusts the bounding box to fit the contour border's mitered corners.
void boundMiters(double &l, double &b, double &r, double &t, double border, double miterLimit, int polarity) const;
void boundMiters(double &xMin, double &yMin, double &xMax, double &yMax, double border, double miterLimit, int polarity) const;
/// Computes the winding of the contour. Returns 1 if positive, -1 if negative.
int winding() const;
/// Reverses the sequence of edges on the contour.

View File

@ -87,17 +87,15 @@ public:
Point2 shapeCoord, sdfCoord;
const float *msd;
bool protectedFlag;
inline ShapeDistanceChecker(const BitmapConstRef<float, N> &sdf, const Shape &shape, const Projection &projection, DistanceMapping distanceMapping, double minImproveRatio) : distanceFinder(shape), sdf(sdf), distanceMapping(distanceMapping), minImproveRatio(minImproveRatio) {
inline ShapeDistanceChecker(const BitmapConstSection<float, N> &sdf, const Shape &shape, const Projection &projection, DistanceMapping distanceMapping, double minImproveRatio) : distanceFinder(shape), sdf(sdf), distanceMapping(distanceMapping), minImproveRatio(minImproveRatio) {
texelSize = projection.unprojectVector(Vector2(1));
if (shape.inverseYAxis)
texelSize.y = -texelSize.y;
}
inline ArtifactClassifier classifier(const Vector2 &direction, double span) {
return ArtifactClassifier(this, direction, span);
}
private:
ShapeDistanceFinder<ContourCombiner<PerpendicularDistanceSelector> > distanceFinder;
BitmapConstRef<float, N> sdf;
BitmapConstSection<float, N> sdf;
DistanceMapping distanceMapping;
Vector2 texelSize;
double minImproveRatio;
@ -105,10 +103,11 @@ private:
MSDFErrorCorrection::MSDFErrorCorrection() { }
MSDFErrorCorrection::MSDFErrorCorrection(const BitmapRef<byte, 1> &stencil, const SDFTransformation &transformation) : stencil(stencil), transformation(transformation) {
MSDFErrorCorrection::MSDFErrorCorrection(const BitmapSection<byte, 1> &stencil, const SDFTransformation &transformation) : stencil(stencil), transformation(transformation) {
minDeviationRatio = ErrorCorrectionConfig::defaultMinDeviationRatio;
minImproveRatio = ErrorCorrectionConfig::defaultMinImproveRatio;
memset(stencil.pixels, 0, sizeof(byte)*stencil.width*stencil.height);
for (int y = 0; y < stencil.height; ++y)
memset(stencil(0, y), 0, sizeof(byte)*stencil.width);
}
void MSDFErrorCorrection::setMinDeviationRatio(double minDeviationRatio) {
@ -120,6 +119,7 @@ void MSDFErrorCorrection::setMinImproveRatio(double minImproveRatio) {
}
void MSDFErrorCorrection::protectCorners(const Shape &shape) {
stencil.reorient(shape.getYAxisOrientation());
for (std::vector<Contour>::const_iterator contour = shape.contours.begin(); contour != shape.contours.end(); ++contour)
if (!contour->edges.empty()) {
const EdgeSegment *prevEdge = contour->edges.back();
@ -131,8 +131,6 @@ void MSDFErrorCorrection::protectCorners(const Shape &shape) {
Point2 p = transformation.project((*edge)->point(0));
int l = (int) floor(p.x-.5);
int b = (int) floor(p.y-.5);
if (shape.inverseYAxis)
b = stencil.height-b-2;
int r = l+1;
int t = b+1;
// Check that the positions are within bounds.
@ -189,8 +187,9 @@ static void protectExtremeChannels(byte *stencil, const float *msd, float m, int
}
template <int N>
void MSDFErrorCorrection::protectEdges(const BitmapConstRef<float, N> &sdf) {
void MSDFErrorCorrection::protectEdges(const BitmapConstSection<float, N> &sdf) {
float radius;
stencil.reorient(sdf.yOrientation);
// Horizontal texel pairs
radius = float(PROTECTION_RADIUS_TOLERANCE*transformation.unprojectVector(Vector2(transformation.distanceMapping(DistanceMapping::Delta(1)), 0)).length());
for (int y = 0; y < sdf.height; ++y) {
@ -251,9 +250,11 @@ void MSDFErrorCorrection::protectEdges(const BitmapConstRef<float, N> &sdf) {
}
void MSDFErrorCorrection::protectAll() {
byte *end = stencil.pixels+stencil.width*stencil.height;
for (byte *mask = stencil.pixels; mask < end; ++mask)
*mask |= (byte) PROTECTED;
for (int y = 0; y < stencil.height; ++y) {
byte *mask = stencil(0, y);
for (int x = 0; x < stencil.width; ++x)
*mask++ |= (byte) PROTECTED;
}
}
/// Returns the median of the linear interpolation of texels a, b at t.
@ -273,16 +274,6 @@ static float interpolatedMedian(const float *a, const float *l, const float *q,
));
}
/// Determines if the interpolated median xm is an artifact.
static bool isArtifact(bool isProtected, double axSpan, double bxSpan, float am, float bm, float xm) {
return (
// For protected texels, only report an artifact if it would cause fill inversion (change between positive and negative distance).
(!isProtected || (am > .5f && bm > .5f && xm <= .5f) || (am < .5f && bm < .5f && xm >= .5f)) &&
// This is an artifact if the interpolated median is outside the range of possible values based on its distance from a, b.
!(xm >= am-axSpan && xm <= am+axSpan && xm >= bm-bxSpan && xm <= bm+bxSpan)
);
}
/// Checks if a linear interpolation artifact will occur at a point where two specific color channels are equal - such points have extreme median values.
template <class ArtifactClassifier>
static bool hasLinearArtifactInner(const ArtifactClassifier &artifactClassifier, float am, float bm, const float *a, const float *b, float dA, float dB) {
@ -390,7 +381,8 @@ static bool hasDiagonalArtifact(const ArtifactClassifier &artifactClassifier, fl
}
template <int N>
void MSDFErrorCorrection::findErrors(const BitmapConstRef<float, N> &sdf) {
void MSDFErrorCorrection::findErrors(const BitmapConstSection<float, N> &sdf) {
stencil.reorient(sdf.yOrientation);
// Compute the expected deltas between values of horizontally, vertically, and diagonally adjacent texels.
double hSpan = minDeviationRatio*transformation.unprojectVector(Vector2(transformation.distanceMapping(DistanceMapping::Delta(1)), 0)).length();
double vSpan = minDeviationRatio*transformation.unprojectVector(Vector2(0, transformation.distanceMapping(DistanceMapping::Delta(1)))).length();
@ -418,7 +410,9 @@ void MSDFErrorCorrection::findErrors(const BitmapConstRef<float, N> &sdf) {
}
template <template <typename> class ContourCombiner, int N>
void MSDFErrorCorrection::findErrors(const BitmapConstRef<float, N> &sdf, const Shape &shape) {
void MSDFErrorCorrection::findErrors(BitmapConstSection<float, N> sdf, const Shape &shape) {
sdf.reorient(shape.getYAxisOrientation());
stencil.reorient(sdf.yOrientation);
// Compute the expected deltas between values of horizontally, vertically, and diagonally adjacent texels.
double hSpan = minDeviationRatio*transformation.unprojectVector(Vector2(transformation.distanceMapping(DistanceMapping::Delta(1)), 0)).length();
double vSpan = minDeviationRatio*transformation.unprojectVector(Vector2(0, transformation.distanceMapping(DistanceMapping::Delta(1)))).length();
@ -428,69 +422,75 @@ void MSDFErrorCorrection::findErrors(const BitmapConstRef<float, N> &sdf, const
#endif
{
ShapeDistanceChecker<ContourCombiner, N> shapeDistanceChecker(sdf, shape, transformation, transformation.distanceMapping, minImproveRatio);
bool rightToLeft = false;
int xDirection = 1;
// Inspect all texels.
#ifdef MSDFGEN_USE_OPENMP
#pragma omp for
#endif
for (int y = 0; y < sdf.height; ++y) {
int row = shape.inverseYAxis ? sdf.height-y-1 : y;
for (int col = 0; col < sdf.width; ++col) {
int x = rightToLeft ? sdf.width-col-1 : col;
if ((*stencil(x, row)&ERROR))
int x = xDirection < 0 ? sdf.width-1 : 0;
for (int col = 0; col < sdf.width; ++col, x += xDirection) {
if ((*stencil(x, y)&ERROR))
continue;
const float *c = sdf(x, row);
const float *c = sdf(x, y);
shapeDistanceChecker.shapeCoord = transformation.unproject(Point2(x+.5, y+.5));
shapeDistanceChecker.sdfCoord = Point2(x+.5, row+.5);
shapeDistanceChecker.sdfCoord = Point2(x+.5, y+.5);
shapeDistanceChecker.msd = c;
shapeDistanceChecker.protectedFlag = (*stencil(x, row)&PROTECTED) != 0;
shapeDistanceChecker.protectedFlag = (*stencil(x, y)&PROTECTED) != 0;
float cm = median(c[0], c[1], c[2]);
const float *l = NULL, *b = NULL, *r = NULL, *t = NULL;
// Mark current texel c with the error flag if an artifact occurs when it's interpolated with any of its 8 neighbors.
*stencil(x, row) |= (byte) (ERROR*(
(x > 0 && ((l = sdf(x-1, row)), hasLinearArtifact(shapeDistanceChecker.classifier(Vector2(-1, 0), hSpan), cm, c, l))) ||
(row > 0 && ((b = sdf(x, row-1)), hasLinearArtifact(shapeDistanceChecker.classifier(Vector2(0, -1), vSpan), cm, c, b))) ||
(x < sdf.width-1 && ((r = sdf(x+1, row)), hasLinearArtifact(shapeDistanceChecker.classifier(Vector2(+1, 0), hSpan), cm, c, r))) ||
(row < sdf.height-1 && ((t = sdf(x, row+1)), hasLinearArtifact(shapeDistanceChecker.classifier(Vector2(0, +1), vSpan), cm, c, t))) ||
(x > 0 && row > 0 && hasDiagonalArtifact(shapeDistanceChecker.classifier(Vector2(-1, -1), dSpan), cm, c, l, b, sdf(x-1, row-1))) ||
(x < sdf.width-1 && row > 0 && hasDiagonalArtifact(shapeDistanceChecker.classifier(Vector2(+1, -1), dSpan), cm, c, r, b, sdf(x+1, row-1))) ||
(x > 0 && row < sdf.height-1 && hasDiagonalArtifact(shapeDistanceChecker.classifier(Vector2(-1, +1), dSpan), cm, c, l, t, sdf(x-1, row+1))) ||
(x < sdf.width-1 && row < sdf.height-1 && hasDiagonalArtifact(shapeDistanceChecker.classifier(Vector2(+1, +1), dSpan), cm, c, r, t, sdf(x+1, row+1)))
*stencil(x, y) |= (byte) (ERROR*(
(x > 0 && ((l = sdf(x-1, y)), hasLinearArtifact(shapeDistanceChecker.classifier(Vector2(-1, 0), hSpan), cm, c, l))) ||
(y > 0 && ((b = sdf(x, y-1)), hasLinearArtifact(shapeDistanceChecker.classifier(Vector2(0, -1), vSpan), cm, c, b))) ||
(x < sdf.width-1 && ((r = sdf(x+1, y)), hasLinearArtifact(shapeDistanceChecker.classifier(Vector2(+1, 0), hSpan), cm, c, r))) ||
(y < sdf.height-1 && ((t = sdf(x, y+1)), hasLinearArtifact(shapeDistanceChecker.classifier(Vector2(0, +1), vSpan), cm, c, t))) ||
(x > 0 && y > 0 && hasDiagonalArtifact(shapeDistanceChecker.classifier(Vector2(-1, -1), dSpan), cm, c, l, b, sdf(x-1, y-1))) ||
(x < sdf.width-1 && y > 0 && hasDiagonalArtifact(shapeDistanceChecker.classifier(Vector2(+1, -1), dSpan), cm, c, r, b, sdf(x+1, y-1))) ||
(x > 0 && y < sdf.height-1 && hasDiagonalArtifact(shapeDistanceChecker.classifier(Vector2(-1, +1), dSpan), cm, c, l, t, sdf(x-1, y+1))) ||
(x < sdf.width-1 && y < sdf.height-1 && hasDiagonalArtifact(shapeDistanceChecker.classifier(Vector2(+1, +1), dSpan), cm, c, r, t, sdf(x+1, y+1)))
));
}
xDirection = -xDirection;
}
}
}
template <int N>
void MSDFErrorCorrection::apply(const BitmapRef<float, N> &sdf) const {
int texelCount = sdf.width*sdf.height;
const byte *mask = stencil.pixels;
float *texel = sdf.pixels;
for (int i = 0; i < texelCount; ++i) {
if (*mask&ERROR) {
// Set all color channels to the median.
float m = median(texel[0], texel[1], texel[2]);
texel[0] = m, texel[1] = m, texel[2] = m;
void MSDFErrorCorrection::apply(BitmapSection<float, N> sdf) const {
sdf.reorient(stencil.yOrientation);
const byte *stencilRow = stencil.pixels;
float *rowStart = sdf.pixels;
for (int y = 0; y < sdf.height; ++y) {
const byte *mask = stencilRow;
float *pixel = rowStart;
for (int x = 0; x < sdf.width; ++x) {
if (*mask&ERROR) {
// Set all color channels to the median.
float m = median(pixel[0], pixel[1], pixel[2]);
pixel[0] = m, pixel[1] = m, pixel[2] = m;
}
++mask;
pixel += N;
}
++mask;
texel += N;
stencilRow += stencil.rowStride;
rowStart += sdf.rowStride;
}
}
BitmapConstRef<byte, 1> MSDFErrorCorrection::getStencil() const {
BitmapConstSection<byte, 1> MSDFErrorCorrection::getStencil() const {
return stencil;
}
template void MSDFErrorCorrection::protectEdges(const BitmapConstRef<float, 3> &sdf);
template void MSDFErrorCorrection::protectEdges(const BitmapConstRef<float, 4> &sdf);
template void MSDFErrorCorrection::findErrors(const BitmapConstRef<float, 3> &sdf);
template void MSDFErrorCorrection::findErrors(const BitmapConstRef<float, 4> &sdf);
template void MSDFErrorCorrection::findErrors<SimpleContourCombiner>(const BitmapConstRef<float, 3> &sdf, const Shape &shape);
template void MSDFErrorCorrection::findErrors<SimpleContourCombiner>(const BitmapConstRef<float, 4> &sdf, const Shape &shape);
template void MSDFErrorCorrection::findErrors<OverlappingContourCombiner>(const BitmapConstRef<float, 3> &sdf, const Shape &shape);
template void MSDFErrorCorrection::findErrors<OverlappingContourCombiner>(const BitmapConstRef<float, 4> &sdf, const Shape &shape);
template void MSDFErrorCorrection::apply(const BitmapRef<float, 3> &sdf) const;
template void MSDFErrorCorrection::apply(const BitmapRef<float, 4> &sdf) const;
template void MSDFErrorCorrection::protectEdges(const BitmapConstSection<float, 3> &sdf);
template void MSDFErrorCorrection::protectEdges(const BitmapConstSection<float, 4> &sdf);
template void MSDFErrorCorrection::findErrors(const BitmapConstSection<float, 3> &sdf);
template void MSDFErrorCorrection::findErrors(const BitmapConstSection<float, 4> &sdf);
template void MSDFErrorCorrection::findErrors<SimpleContourCombiner>(BitmapConstSection<float, 3> sdf, const Shape &shape);
template void MSDFErrorCorrection::findErrors<SimpleContourCombiner>(BitmapConstSection<float, 4> sdf, const Shape &shape);
template void MSDFErrorCorrection::findErrors<OverlappingContourCombiner>(BitmapConstSection<float, 3> sdf, const Shape &shape);
template void MSDFErrorCorrection::findErrors<OverlappingContourCombiner>(BitmapConstSection<float, 4> sdf, const Shape &shape);
template void MSDFErrorCorrection::apply(BitmapSection<float, 3> sdf) const;
template void MSDFErrorCorrection::apply(BitmapSection<float, 4> sdf) const;
}

View File

@ -20,7 +20,7 @@ public:
};
MSDFErrorCorrection();
explicit MSDFErrorCorrection(const BitmapRef<byte, 1> &stencil, const SDFTransformation &transformation);
explicit MSDFErrorCorrection(const BitmapSection<byte, 1> &stencil, const SDFTransformation &transformation);
/// Sets the minimum ratio between the actual and maximum expected distance delta to be considered an error.
void setMinDeviationRatio(double minDeviationRatio);
/// Sets the minimum ratio between the pre-correction distance error and the post-correction distance error.
@ -29,23 +29,23 @@ public:
void protectCorners(const Shape &shape);
/// Flags all texels that contribute to edges as protected.
template <int N>
void protectEdges(const BitmapConstRef<float, N> &sdf);
void protectEdges(const BitmapConstSection<float, N> &sdf);
/// Flags all texels as protected.
void protectAll();
/// Flags texels that are expected to cause interpolation artifacts based on analysis of the SDF only.
template <int N>
void findErrors(const BitmapConstRef<float, N> &sdf);
void findErrors(const BitmapConstSection<float, N> &sdf);
/// Flags texels that are expected to cause interpolation artifacts based on analysis of the SDF and comparison with the exact shape distance.
template <template <typename> class ContourCombiner, int N>
void findErrors(const BitmapConstRef<float, N> &sdf, const Shape &shape);
void findErrors(BitmapConstSection<float, N> sdf, const Shape &shape);
/// Modifies the MSDF so that all texels with the error flag are converted to single-channel.
template <int N>
void apply(const BitmapRef<float, N> &sdf) const;
void apply(BitmapSection<float, N> sdf) const;
/// Returns the stencil in its current state (see Flags).
BitmapConstRef<byte, 1> getStencil() const;
BitmapConstSection<byte, 1> getStencil() const;
private:
BitmapRef<byte, 1> stencil;
BitmapSection<byte, 1> stencil;
SDFTransformation transformation;
double minDeviationRatio;
double minImproveRatio;

View File

@ -3,6 +3,7 @@
#include <cstdlib>
#include "arithmetics.hpp"
#include "convergent-curve-ordering.h"
#define DECONVERGE_OVERSHOOT 1.11111111111111111 // moves control points slightly more than necessary to account for floating-point errors
@ -70,7 +71,7 @@ void Shape::normalize() {
contour->edges.push_back(EdgeHolder(parts[0]));
contour->edges.push_back(EdgeHolder(parts[1]));
contour->edges.push_back(EdgeHolder(parts[2]));
} else {
} else if (!contour->edges.empty()) {
// Push apart convergent edge segments
EdgeHolder *prevEdge = &contour->edges.back();
for (std::vector<EdgeHolder>::iterator edge = contour->edges.begin(); edge != contour->edges.end(); ++edge) {
@ -79,8 +80,7 @@ void Shape::normalize() {
if (dotProduct(prevDir, curDir) < MSDFGEN_CORNER_DOT_EPSILON-1) {
double factor = DECONVERGE_OVERSHOOT*sqrt(1-(MSDFGEN_CORNER_DOT_EPSILON-1)*(MSDFGEN_CORNER_DOT_EPSILON-1))/(MSDFGEN_CORNER_DOT_EPSILON-1);
Vector2 axis = factor*(curDir-prevDir).normalize();
// Determine curve ordering using third-order derivative (t = 0) of crossProduct((*prevEdge)->point(1-t)-p0, (*edge)->point(t)-p0) where p0 is the corner (*edge)->point(0)
if (crossProduct((*prevEdge)->directionChange(1), (*edge)->direction(0))+crossProduct((*edge)->directionChange(0), (*prevEdge)->direction(1)) < 0)
if (convergentCurveOrdering(*prevEdge, *edge) < 0)
axis = -axis;
deconvergeEdge(*prevEdge, 1, axis.getOrthogonal(true));
deconvergeEdge(*edge, 0, axis.getOrthogonal(false));
@ -91,14 +91,14 @@ void Shape::normalize() {
}
}
void Shape::bound(double &l, double &b, double &r, double &t) const {
void Shape::bound(double &xMin, double &yMin, double &xMax, double &yMax) const {
for (std::vector<Contour>::const_iterator contour = contours.begin(); contour != contours.end(); ++contour)
contour->bound(l, b, r, t);
contour->bound(xMin, yMin, xMax, yMax);
}
void Shape::boundMiters(double &l, double &b, double &r, double &t, double border, double miterLimit, int polarity) const {
void Shape::boundMiters(double &xMin, double &yMin, double &xMax, double &yMax, double border, double miterLimit, int polarity) const {
for (std::vector<Contour>::const_iterator contour = contours.begin(); contour != contours.end(); ++contour)
contour->boundMiters(l, b, r, t, border, miterLimit, polarity);
contour->boundMiters(xMin, yMin, xMax, yMax, border, miterLimit, polarity);
}
Shape::Bounds Shape::getBounds(double border, double miterLimit, int polarity) const {
@ -197,4 +197,12 @@ void Shape::orientContours() {
contours[i].reverse();
}
YAxisOrientation Shape::getYAxisOrientation() const {
return inverseYAxis ? MSDFGEN_Y_AXIS_NONDEFAULT_ORIENTATION : MSDFGEN_Y_AXIS_DEFAULT_ORIENTATION;
}
void Shape::setYAxisOrientation(YAxisOrientation yAxisOrientation) {
inverseYAxis = yAxisOrientation != MSDFGEN_Y_AXIS_DEFAULT_ORIENTATION;
}
}

View File

@ -3,6 +3,7 @@
#include <vector>
#include "Contour.h"
#include "YAxisOrientation.h"
#include "Scanline.h"
namespace msdfgen {
@ -15,12 +16,14 @@ class Shape {
public:
struct Bounds {
// NOTE: b is actually the lower Y-coordinate and t the higher Y-coordinate. For Y_DOWNWARD orientation, b is actually the top and t the bottom. May be renamed in a future version.
double l, b, r, t;
};
/// The list of contours the shape consists of.
std::vector<Contour> contours;
/// Specifies whether the shape uses bottom-to-top (false) or top-to-bottom (true) Y coordinates.
/// DEPRECATED - use getYAxisOrientation / setYAxisOrientation instead.
bool inverseYAxis;
Shape();
@ -36,9 +39,9 @@ public:
/// Performs basic checks to determine if the object represents a valid shape.
bool validate() const;
/// Adjusts the bounding box to fit the shape.
void bound(double &l, double &b, double &r, double &t) const;
void bound(double &xMin, double &yMin, double &xMax, double &yMax) const;
/// Adjusts the bounding box to fit the shape border's mitered corners.
void boundMiters(double &l, double &b, double &r, double &t, double border, double miterLimit, int polarity) const;
void boundMiters(double &xMin, double &yMin, double &xMax, double &yMax, double border, double miterLimit, int polarity) const;
/// Computes the minimum bounding box that fits the shape, optionally with a (mitered) border.
Bounds getBounds(double border = 0, double miterLimit = 0, int polarity = 0) const;
/// Outputs the scanline that intersects the shape at y.
@ -47,6 +50,10 @@ public:
int edgeCount() const;
/// Assumes its contours are unoriented (even-odd fill rule). Attempts to orient them to conform to the non-zero winding rule.
void orientContours();
/// Returns the orientation of the axis of the shape's Y coordinates.
YAxisOrientation getYAxisOrientation() const;
/// Sets the orientation of the axis of the shape's Y coordinates.
void setYAxisOrientation(YAxisOrientation yAxisOrientation);
};

View File

@ -0,0 +1,17 @@
#pragma once
#include "base.h"
namespace msdfgen {
/// Specifies whether the Y component of the coordinate system increases in the upward or downward direction.
enum YAxisOrientation {
Y_UPWARD,
Y_DOWNWARD
};
}
#define MSDFGEN_Y_AXIS_DEFAULT_ORIENTATION msdfgen::Y_UPWARD
#define MSDFGEN_Y_AXIS_NONDEFAULT_ORIENTATION (MSDFGEN_Y_AXIS_DEFAULT_ORIENTATION == msdfgen::Y_DOWNWARD ? msdfgen::Y_UPWARD : msdfgen::Y_DOWNWARD)

View File

@ -8,7 +8,9 @@
namespace msdfgen {
template <typename T, int N>
static void interpolate(T *output, const BitmapConstRef<T, N> &bitmap, Point2 pos) {
inline void interpolate(T *output, const BitmapConstSection<T, N> &bitmap, Point2 pos) {
pos.x = clamp(pos.x, double(bitmap.width));
pos.y = clamp(pos.y, double(bitmap.height));
pos -= .5;
int l = (int) floor(pos.x);
int b = (int) floor(pos.y);

View File

@ -32,7 +32,7 @@ static double resolveDistance(const MultiDistance &distance) {
}
template <class EdgeSelector>
SimpleContourCombiner<EdgeSelector>::SimpleContourCombiner(const Shape &shape) { }
SimpleContourCombiner<EdgeSelector>::SimpleContourCombiner(const Shape &) { }
template <class EdgeSelector>
void SimpleContourCombiner<EdgeSelector>::reset(const Point2 &p) {

View File

@ -0,0 +1,140 @@
#include "convergent-curve-ordering.h"
#include "arithmetics.hpp"
#include "Vector2.hpp"
/*
* For non-degenerate curves A(t), B(t) (ones where all control points are distinct) both originating at P = A(0) = B(0) = *corner,
* we are computing the limit of
*
* sign(crossProduct( A(t / |A'(0)|) - P, B(t / |B'(0)|) - P ))
*
* for t -> 0 from 1. Of note is that the curves' parameter has to be normed by the first derivative at P,
* which ensures that the limit approaches P at the same rate along both curves - omitting this was the main error of earlier versions of deconverge.
*
* For degenerate cubic curves (ones where the first control point equals the origin point), the denominator |A'(0)| is zero,
* so to address that, we approach with the square root of t and use the derivative of A(sqrt(t)), which at t = 0 equals A''(0)/2
* Therefore, in these cases, we replace one factor of the cross product with A(sqrt(2*t / |A''(0)|)) - P
*
* The cross product results in a polynomial (in respect to t or t^2 in the degenerate case),
* the limit of sign of which at zero can be determined by the lowest order non-zero derivative,
* which equals to the sign of the first non-zero polynomial coefficient in the order of increasing exponents.
*
* The polynomial's constant and linear terms are zero, so the first derivative is definitely zero as well.
* The second derivative is assumed to be zero (or near zero) due to the curves being convergent - this is an input requirement
* (otherwise the correct result is the sign of the cross product of their directions at t = 0).
* Therefore, we skip the first and second derivatives.
*/
namespace msdfgen {
static void simplifyDegenerateCurve(Point2 *controlPoints, int &order) {
if (order == 3 && (controlPoints[1] == controlPoints[0] || controlPoints[1] == controlPoints[3]) && (controlPoints[2] == controlPoints[0] || controlPoints[2] == controlPoints[3])) {
controlPoints[1] = controlPoints[3];
order = 1;
}
if (order == 2 && (controlPoints[1] == controlPoints[0] || controlPoints[1] == controlPoints[2])) {
controlPoints[1] = controlPoints[2];
order = 1;
}
if (order == 1 && controlPoints[0] == controlPoints[1])
order = 0;
}
int convergentCurveOrdering(const Point2 *corner, int controlPointsBefore, int controlPointsAfter) {
if (!(controlPointsBefore > 0 && controlPointsAfter > 0))
return 0;
Vector2 a1, a2, a3, b1, b2, b3;
a1 = *(corner-1)-*corner;
b1 = *(corner+1)-*corner;
if (controlPointsBefore >= 2)
a2 = *(corner-2)-*(corner-1)-a1;
if (controlPointsAfter >= 2)
b2 = *(corner+2)-*(corner+1)-b1;
if (controlPointsBefore >= 3) {
a3 = *(corner-3)-*(corner-2)-(*(corner-2)-*(corner-1))-a2;
a2 *= 3;
}
if (controlPointsAfter >= 3) {
b3 = *(corner+3)-*(corner+2)-(*(corner+2)-*(corner+1))-b2;
b2 *= 3;
}
a1 *= controlPointsBefore;
b1 *= controlPointsAfter;
// Non-degenerate case
if (a1 && b1) {
double as = a1.length();
double bs = b1.length();
// Third derivative
if (double d = as*crossProduct(a1, b2) + bs*crossProduct(a2, b1))
return sign(d);
// Fourth derivative
if (double d = as*as*crossProduct(a1, b3) + as*bs*crossProduct(a2, b2) + bs*bs*crossProduct(a3, b1))
return sign(d);
// Fifth derivative
if (double d = as*crossProduct(a2, b3) + bs*crossProduct(a3, b2))
return sign(d);
// Sixth derivative
return sign(crossProduct(a3, b3));
}
// Degenerate curve after corner (control point after corner equals corner)
int s = 1;
if (a1) { // !b1
// Swap aN <-> bN and handle in if (b1)
b1 = a1;
a1 = b2, b2 = a2, a2 = a1;
a1 = b3, b3 = a3, a3 = a1;
s = -1; // make sure to also flip output
}
// Degenerate curve before corner (control point before corner equals corner)
if (b1) { // !a1
// Two-and-a-half-th derivative
if (double d = crossProduct(a3, b1))
return s*sign(d);
// Third derivative
if (double d = crossProduct(a2, b2))
return s*sign(d);
// Three-and-a-half-th derivative
if (double d = crossProduct(a3, b2))
return s*sign(d);
// Fourth derivative
if (double d = crossProduct(a2, b3))
return s*sign(d);
// Four-and-a-half-th derivative
return s*sign(crossProduct(a3, b3));
}
// Degenerate curves on both sides of the corner (control point before and after corner equals corner)
{ // !a1 && !b1
// Two-and-a-half-th derivative
if (double d = sqrt(a2.length())*crossProduct(a2, b3) + sqrt(b2.length())*crossProduct(a3, b2))
return sign(d);
// Third derivative
return sign(crossProduct(a3, b3));
}
}
int convergentCurveOrdering(const EdgeSegment *a, const EdgeSegment *b) {
Point2 controlPoints[12];
Point2 *corner = controlPoints+4;
Point2 *aCpTmp = controlPoints+8;
int aOrder = int(a->type());
int bOrder = int(b->type());
if (!(aOrder >= 1 && aOrder <= 3 && bOrder >= 1 && bOrder <= 3)) {
// Not implemented - only linear, quadratic, and cubic curves supported
return 0;
}
for (int i = 0; i <= aOrder; ++i)
aCpTmp[i] = a->controlPoints()[i];
for (int i = 0; i <= bOrder; ++i)
corner[i] = b->controlPoints()[i];
if (aCpTmp[aOrder] != *corner)
return 0;
simplifyDegenerateCurve(aCpTmp, aOrder);
simplifyDegenerateCurve(corner, bOrder);
for (int i = 0; i < aOrder; ++i)
corner[i-aOrder] = aCpTmp[i];
return convergentCurveOrdering(corner, aOrder, bOrder);
}
}

View File

@ -0,0 +1,11 @@
#pragma once
#include "edge-segments.h"
namespace msdfgen {
/// For curves a, b converging at P = a->point(1) = b->point(0) with the same (opposite) direction, determines the relative ordering in which they exit P (i.e. whether a is to the left or right of b at the smallest positive radius around P)
int convergentCurveOrdering(const EdgeSegment *a, const EdgeSegment *b);
}

View File

@ -199,9 +199,9 @@ SignedDistance QuadraticSegment::signedDistance(Point2 origin, double &param) co
double minDistance = nonZeroSign(crossProduct(epDir, qa))*qa.length(); // distance from A
param = -dotProduct(qa, epDir)/dotProduct(epDir, epDir);
{
epDir = direction(1);
double distance = (p[2]-origin).length(); // distance from B
if (distance < fabs(minDistance)) {
epDir = direction(1);
minDistance = nonZeroSign(crossProduct(epDir, p[2]-origin))*distance;
param = dotProduct(origin-p[1], epDir)/dotProduct(epDir, epDir);
}
@ -235,25 +235,31 @@ SignedDistance CubicSegment::signedDistance(Point2 origin, double &param) const
double minDistance = nonZeroSign(crossProduct(epDir, qa))*qa.length(); // distance from A
param = -dotProduct(qa, epDir)/dotProduct(epDir, epDir);
{
epDir = direction(1);
double distance = (p[3]-origin).length(); // distance from B
if (distance < fabs(minDistance)) {
epDir = direction(1);
minDistance = nonZeroSign(crossProduct(epDir, p[3]-origin))*distance;
param = dotProduct(epDir-(p[3]-origin), epDir)/dotProduct(epDir, epDir);
}
}
// Iterative minimum distance search
for (int i = 0; i <= MSDFGEN_CUBIC_SEARCH_STARTS; ++i) {
double t = (double) i/MSDFGEN_CUBIC_SEARCH_STARTS;
double t = 1./MSDFGEN_CUBIC_SEARCH_STARTS*i;
Vector2 qe = qa+3*t*ab+3*t*t*br+t*t*t*as;
for (int step = 0; step < MSDFGEN_CUBIC_SEARCH_STEPS; ++step) {
// Improve t
Vector2 d1 = 3*ab+6*t*br+3*t*t*as;
Vector2 d2 = 6*br+6*t*as;
t -= dotProduct(qe, d1)/(dotProduct(d1, d1)+dotProduct(qe, d2));
if (t <= 0 || t >= 1)
break;
qe = qa+3*t*ab+3*t*t*br+t*t*t*as;
Vector2 d1 = 3*ab+6*t*br+3*t*t*as;
Vector2 d2 = 6*br+6*t*as;
double improvedT = t-dotProduct(qe, d1)/(dotProduct(d1, d1)+dotProduct(qe, d2));
if (improvedT > 0 && improvedT < 1) {
int remainingSteps = MSDFGEN_CUBIC_SEARCH_STEPS;
do {
t = improvedT;
qe = qa+3*t*ab+3*t*t*br+t*t*t*as;
d1 = 3*ab+6*t*br+3*t*t*as;
if (!--remainingSteps)
break;
d2 = 6*br+6*t*as;
improvedT = t-dotProduct(qe, d1)/(dotProduct(d1, d1)+dotProduct(qe, d2));
} while (improvedT > 0 && improvedT < 1);
double distance = qe.length();
if (distance < fabs(minDistance)) {
minDistance = nonZeroSign(crossProduct(d1, qe))*distance;
@ -396,37 +402,37 @@ int CubicSegment::scanlineIntersections(double x[3], int dy[3], double y) const
return total;
}
static void pointBounds(Point2 p, double &l, double &b, double &r, double &t) {
if (p.x < l) l = p.x;
if (p.y < b) b = p.y;
if (p.x > r) r = p.x;
if (p.y > t) t = p.y;
static void pointBounds(Point2 p, double &xMin, double &yMin, double &xMax, double &yMax) {
if (p.x < xMin) xMin = p.x;
if (p.y < yMin) yMin = p.y;
if (p.x > xMax) xMax = p.x;
if (p.y > yMax) yMax = p.y;
}
void LinearSegment::bound(double &l, double &b, double &r, double &t) const {
pointBounds(p[0], l, b, r, t);
pointBounds(p[1], l, b, r, t);
void LinearSegment::bound(double &xMin, double &yMin, double &xMax, double &yMax) const {
pointBounds(p[0], xMin, yMin, xMax, yMax);
pointBounds(p[1], xMin, yMin, xMax, yMax);
}
void QuadraticSegment::bound(double &l, double &b, double &r, double &t) const {
pointBounds(p[0], l, b, r, t);
pointBounds(p[2], l, b, r, t);
void QuadraticSegment::bound(double &xMin, double &yMin, double &xMax, double &yMax) const {
pointBounds(p[0], xMin, yMin, xMax, yMax);
pointBounds(p[2], xMin, yMin, xMax, yMax);
Vector2 bot = (p[1]-p[0])-(p[2]-p[1]);
if (bot.x) {
double param = (p[1].x-p[0].x)/bot.x;
if (param > 0 && param < 1)
pointBounds(point(param), l, b, r, t);
pointBounds(point(param), xMin, yMin, xMax, yMax);
}
if (bot.y) {
double param = (p[1].y-p[0].y)/bot.y;
if (param > 0 && param < 1)
pointBounds(point(param), l, b, r, t);
pointBounds(point(param), xMin, yMin, xMax, yMax);
}
}
void CubicSegment::bound(double &l, double &b, double &r, double &t) const {
pointBounds(p[0], l, b, r, t);
pointBounds(p[3], l, b, r, t);
void CubicSegment::bound(double &xMin, double &yMin, double &xMax, double &yMax) const {
pointBounds(p[0], xMin, yMin, xMax, yMax);
pointBounds(p[3], xMin, yMin, xMax, yMax);
Vector2 a0 = p[1]-p[0];
Vector2 a1 = 2*(p[2]-p[1]-a0);
Vector2 a2 = p[3]-3*p[2]+3*p[1]-p[0];
@ -435,11 +441,11 @@ void CubicSegment::bound(double &l, double &b, double &r, double &t) const {
solutions = solveQuadratic(params, a2.x, a1.x, a0.x);
for (int i = 0; i < solutions; ++i)
if (params[i] > 0 && params[i] < 1)
pointBounds(point(params[i]), l, b, r, t);
pointBounds(point(params[i]), xMin, yMin, xMax, yMax);
solutions = solveQuadratic(params, a2.y, a1.y, a0.y);
for (int i = 0; i < solutions; ++i)
if (params[i] > 0 && params[i] < 1)
pointBounds(point(params[i]), l, b, r, t);
pointBounds(point(params[i]), xMin, yMin, xMax, yMax);
}
void LinearSegment::reverse() {

View File

@ -42,7 +42,7 @@ public:
/// Outputs a list of (at most three) intersections (their X coordinates) with an infinite horizontal scanline at y and returns how many there are.
virtual int scanlineIntersections(double x[3], int dy[3], double y) const = 0;
/// Adjusts the bounding box to fit the edge segment.
virtual void bound(double &l, double &b, double &r, double &t) const = 0;
virtual void bound(double &xMin, double &yMin, double &xMax, double &yMax) const = 0;
/// Reverses the edge (swaps its start point and end point).
virtual void reverse() = 0;
@ -75,7 +75,7 @@ public:
double length() const;
SignedDistance signedDistance(Point2 origin, double &param) const;
int scanlineIntersections(double x[3], int dy[3], double y) const;
void bound(double &l, double &b, double &r, double &t) const;
void bound(double &xMin, double &yMin, double &xMax, double &yMax) const;
void reverse();
void moveStartPoint(Point2 to);
@ -104,7 +104,7 @@ public:
double length() const;
SignedDistance signedDistance(Point2 origin, double &param) const;
int scanlineIntersections(double x[3], int dy[3], double y) const;
void bound(double &l, double &b, double &r, double &t) const;
void bound(double &xMin, double &yMin, double &xMax, double &yMax) const;
void reverse();
void moveStartPoint(Point2 to);
@ -134,7 +134,7 @@ public:
Vector2 directionChange(double param) const;
SignedDistance signedDistance(Point2 origin, double &param) const;
int scanlineIntersections(double x[3], int dy[3], double y) const;
void bound(double &l, double &b, double &r, double &t) const;
void bound(double &xMin, double &yMin, double &xMax, double &yMax) const;
void reverse();
void moveStartPoint(Point2 to);

View File

@ -11,6 +11,7 @@ TrueDistanceSelector::EdgeCache::EdgeCache() : absDistance(0) { }
void TrueDistanceSelector::reset(const Point2 &p) {
double delta = DISTANCE_DELTA_FACTOR*(p-this->p).length();
// Since minDistance.distance is initialized to -DBL_MAX, at first glance this seems like it could make it underflow to -infinity, but in practice delta would have to be extremely high for this to happen (above 9e291)
minDistance.distance += nonZeroSign(minDistance.distance)*delta;
this->p = p;
}
@ -60,7 +61,7 @@ void PerpendicularDistanceSelectorBase::reset(double delta) {
nearEdgeParam = 0;
}
bool PerpendicularDistanceSelectorBase::isEdgeRelevant(const EdgeCache &cache, const EdgeSegment *edge, const Point2 &p) const {
bool PerpendicularDistanceSelectorBase::isEdgeRelevant(const EdgeCache &cache, const EdgeSegment *, const Point2 &p) const {
double delta = DISTANCE_DELTA_FACTOR*(p-cache.point).length();
return (
cache.absDistance-delta <= fabs(minTrueDistance.distance) ||

View File

@ -1,79 +0,0 @@
#include "export-svg.h"
#include <cstdio>
#include "edge-segments.h"
namespace msdfgen {
static void writeSvgCoord(FILE *f, Point2 coord) {
fprintf(f, "%.17g %.17g", coord.x, coord.y);
}
static void writeSvgPathDef(FILE *f, const Shape &shape) {
bool beginning = true;
for (const Contour &c : shape.contours) {
if (c.edges.empty())
continue;
if (beginning)
beginning = false;
else
fputc(' ', f);
fputs("M ", f);
writeSvgCoord(f, c.edges[0]->controlPoints()[0]);
for (const EdgeHolder &e : c.edges) {
const Point2 *cp = e->controlPoints();
switch (e->type()) {
case (int) LinearSegment::EDGE_TYPE:
fputs(" L ", f);
writeSvgCoord(f, cp[1]);
break;
case (int) QuadraticSegment::EDGE_TYPE:
fputs(" Q ", f);
writeSvgCoord(f, cp[1]);
fputc(' ', f);
writeSvgCoord(f, cp[2]);
break;
case (int) CubicSegment::EDGE_TYPE:
fputs(" C ", f);
writeSvgCoord(f, cp[1]);
fputc(' ', f);
writeSvgCoord(f, cp[2]);
fputc(' ', f);
writeSvgCoord(f, cp[3]);
break;
}
}
fputs(" Z", f);
}
}
bool saveSvgShape(const Shape &shape, const char *filename) {
if (FILE *f = fopen(filename, "w")) {
fputs("<svg xmlns=\"http://www.w3.org/2000/svg\"><path", f);
if (!shape.inverseYAxis)
fputs(" transform=\"scale(1 -1)\"", f);
fputs(" d=\"", f);
writeSvgPathDef(f, shape);
fputs("\"/></svg>\n", f);
fclose(f);
return true;
}
return false;
}
bool saveSvgShape(const Shape &shape, const Shape::Bounds &bounds, const char *filename) {
if (FILE *f = fopen(filename, "w")) {
fprintf(f, "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"%.17g %.17g %.17g %.17g\"><path", bounds.l, bounds.b, bounds.r-bounds.l, bounds.t-bounds.b);
if (!shape.inverseYAxis)
fprintf(f, " transform=\"translate(0 %.17g) scale(1 -1)\"", bounds.b+bounds.t);
fputs(" d=\"", f);
writeSvgPathDef(f, shape);
fputs("\"/></svg>\n", f);
fclose(f);
return true;
}
return false;
}
}

View File

@ -1,11 +0,0 @@
#pragma once
#include "Shape.h"
namespace msdfgen {
bool saveSvgShape(const Shape &shape, const char *filename);
bool saveSvgShape(const Shape &shape, const Shape::Bounds &bounds, const char *filename);
}

View File

@ -10,15 +10,14 @@
namespace msdfgen {
template <int N>
static void msdfErrorCorrectionInner(const BitmapRef<float, N> &sdf, const Shape &shape, const SDFTransformation &transformation, const MSDFGeneratorConfig &config) {
static void msdfErrorCorrectionInner(const BitmapSection<float, N> &sdf, const Shape &shape, const SDFTransformation &transformation, const MSDFGeneratorConfig &config) {
if (config.errorCorrection.mode == ErrorCorrectionConfig::DISABLED)
return;
Bitmap<byte, 1> stencilBuffer;
if (!config.errorCorrection.buffer)
stencilBuffer = Bitmap<byte, 1>(sdf.width, sdf.height);
BitmapRef<byte, 1> stencil;
BitmapSection<byte, 1> stencil(NULL, sdf.width, sdf.height);
stencil.pixels = config.errorCorrection.buffer ? config.errorCorrection.buffer : (byte *) stencilBuffer;
stencil.width = sdf.width, stencil.height = sdf.height;
MSDFErrorCorrection ec(stencil, transformation);
ec.setMinDeviationRatio(config.errorCorrection.minDeviationRatio);
ec.setMinImproveRatio(config.errorCorrection.minImproveRatio);
@ -49,7 +48,7 @@ static void msdfErrorCorrectionInner(const BitmapRef<float, N> &sdf, const Shape
}
template <int N>
static void msdfErrorCorrectionShapeless(const BitmapRef<float, N> &sdf, const SDFTransformation &transformation, double minDeviationRatio, bool protectAll) {
static void msdfErrorCorrectionShapeless(const BitmapSection<float, N> &sdf, const SDFTransformation &transformation, double minDeviationRatio, bool protectAll) {
Bitmap<byte, 1> stencilBuffer(sdf.width, sdf.height);
MSDFErrorCorrection ec(stencilBuffer, transformation);
ec.setMinDeviationRatio(minDeviationRatio);
@ -59,54 +58,54 @@ static void msdfErrorCorrectionShapeless(const BitmapRef<float, N> &sdf, const S
ec.apply(sdf);
}
void msdfErrorCorrection(const BitmapRef<float, 3> &sdf, const Shape &shape, const SDFTransformation &transformation, const MSDFGeneratorConfig &config) {
void msdfErrorCorrection(const BitmapSection<float, 3> &sdf, const Shape &shape, const SDFTransformation &transformation, const MSDFGeneratorConfig &config) {
msdfErrorCorrectionInner(sdf, shape, transformation, config);
}
void msdfErrorCorrection(const BitmapRef<float, 4> &sdf, const Shape &shape, const SDFTransformation &transformation, const MSDFGeneratorConfig &config) {
void msdfErrorCorrection(const BitmapSection<float, 4> &sdf, const Shape &shape, const SDFTransformation &transformation, const MSDFGeneratorConfig &config) {
msdfErrorCorrectionInner(sdf, shape, transformation, config);
}
void msdfErrorCorrection(const BitmapRef<float, 3> &sdf, const Shape &shape, const Projection &projection, Range range, const MSDFGeneratorConfig &config) {
void msdfErrorCorrection(const BitmapSection<float, 3> &sdf, const Shape &shape, const Projection &projection, Range range, const MSDFGeneratorConfig &config) {
msdfErrorCorrectionInner(sdf, shape, SDFTransformation(projection, range), config);
}
void msdfErrorCorrection(const BitmapRef<float, 4> &sdf, const Shape &shape, const Projection &projection, Range range, const MSDFGeneratorConfig &config) {
void msdfErrorCorrection(const BitmapSection<float, 4> &sdf, const Shape &shape, const Projection &projection, Range range, const MSDFGeneratorConfig &config) {
msdfErrorCorrectionInner(sdf, shape, SDFTransformation(projection, range), config);
}
void msdfFastDistanceErrorCorrection(const BitmapRef<float, 3> &sdf, const SDFTransformation &transformation, double minDeviationRatio) {
void msdfFastDistanceErrorCorrection(const BitmapSection<float, 3> &sdf, const SDFTransformation &transformation, double minDeviationRatio) {
msdfErrorCorrectionShapeless(sdf, transformation, minDeviationRatio, false);
}
void msdfFastDistanceErrorCorrection(const BitmapRef<float, 4> &sdf, const SDFTransformation &transformation, double minDeviationRatio) {
void msdfFastDistanceErrorCorrection(const BitmapSection<float, 4> &sdf, const SDFTransformation &transformation, double minDeviationRatio) {
msdfErrorCorrectionShapeless(sdf, transformation, minDeviationRatio, false);
}
void msdfFastDistanceErrorCorrection(const BitmapRef<float, 3> &sdf, const Projection &projection, Range range, double minDeviationRatio) {
void msdfFastDistanceErrorCorrection(const BitmapSection<float, 3> &sdf, const Projection &projection, Range range, double minDeviationRatio) {
msdfErrorCorrectionShapeless(sdf, SDFTransformation(projection, range), minDeviationRatio, false);
}
void msdfFastDistanceErrorCorrection(const BitmapRef<float, 4> &sdf, const Projection &projection, Range range, double minDeviationRatio) {
void msdfFastDistanceErrorCorrection(const BitmapSection<float, 4> &sdf, const Projection &projection, Range range, double minDeviationRatio) {
msdfErrorCorrectionShapeless(sdf, SDFTransformation(projection, range), minDeviationRatio, false);
}
void msdfFastDistanceErrorCorrection(const BitmapRef<float, 3> &sdf, Range pxRange, double minDeviationRatio) {
void msdfFastDistanceErrorCorrection(const BitmapSection<float, 3> &sdf, Range pxRange, double minDeviationRatio) {
msdfErrorCorrectionShapeless(sdf, SDFTransformation(Projection(), pxRange), minDeviationRatio, false);
}
void msdfFastDistanceErrorCorrection(const BitmapRef<float, 4> &sdf, Range pxRange, double minDeviationRatio) {
void msdfFastDistanceErrorCorrection(const BitmapSection<float, 4> &sdf, Range pxRange, double minDeviationRatio) {
msdfErrorCorrectionShapeless(sdf, SDFTransformation(Projection(), pxRange), minDeviationRatio, false);
}
void msdfFastEdgeErrorCorrection(const BitmapRef<float, 3> &sdf, const SDFTransformation &transformation, double minDeviationRatio) {
void msdfFastEdgeErrorCorrection(const BitmapSection<float, 3> &sdf, const SDFTransformation &transformation, double minDeviationRatio) {
msdfErrorCorrectionShapeless(sdf, transformation, minDeviationRatio, true);
}
void msdfFastEdgeErrorCorrection(const BitmapRef<float, 4> &sdf, const SDFTransformation &transformation, double minDeviationRatio) {
void msdfFastEdgeErrorCorrection(const BitmapSection<float, 4> &sdf, const SDFTransformation &transformation, double minDeviationRatio) {
msdfErrorCorrectionShapeless(sdf, transformation, minDeviationRatio, true);
}
void msdfFastEdgeErrorCorrection(const BitmapRef<float, 3> &sdf, const Projection &projection, Range range, double minDeviationRatio) {
void msdfFastEdgeErrorCorrection(const BitmapSection<float, 3> &sdf, const Projection &projection, Range range, double minDeviationRatio) {
msdfErrorCorrectionShapeless(sdf, SDFTransformation(projection, range), minDeviationRatio, true);
}
void msdfFastEdgeErrorCorrection(const BitmapRef<float, 4> &sdf, const Projection &projection, Range range, double minDeviationRatio) {
void msdfFastEdgeErrorCorrection(const BitmapSection<float, 4> &sdf, const Projection &projection, Range range, double minDeviationRatio) {
msdfErrorCorrectionShapeless(sdf, SDFTransformation(projection, range), minDeviationRatio, true);
}
void msdfFastEdgeErrorCorrection(const BitmapRef<float, 3> &sdf, Range pxRange, double minDeviationRatio) {
void msdfFastEdgeErrorCorrection(const BitmapSection<float, 3> &sdf, Range pxRange, double minDeviationRatio) {
msdfErrorCorrectionShapeless(sdf, SDFTransformation(Projection(), pxRange), minDeviationRatio, true);
}
void msdfFastEdgeErrorCorrection(const BitmapRef<float, 4> &sdf, Range pxRange, double minDeviationRatio) {
void msdfFastEdgeErrorCorrection(const BitmapSection<float, 4> &sdf, Range pxRange, double minDeviationRatio) {
msdfErrorCorrectionShapeless(sdf, SDFTransformation(Projection(), pxRange), minDeviationRatio, true);
}
@ -136,7 +135,7 @@ inline static bool detectClash(const float *a, const float *b, double threshold)
}
template <int N>
static void msdfErrorCorrectionInner_legacy(const BitmapRef<float, N> &output, const Vector2 &threshold) {
static void msdfErrorCorrectionInner_legacy(const BitmapSection<float, N> &output, const Vector2 &threshold) {
std::vector<std::pair<int, int> > clashes;
int w = output.width, h = output.height;
for (int y = 0; y < h; ++y)
@ -174,10 +173,10 @@ static void msdfErrorCorrectionInner_legacy(const BitmapRef<float, N> &output, c
#endif
}
void msdfErrorCorrection_legacy(const BitmapRef<float, 3> &output, const Vector2 &threshold) {
void msdfErrorCorrection_legacy(const BitmapSection<float, 3> &output, const Vector2 &threshold) {
msdfErrorCorrectionInner_legacy(output, threshold);
}
void msdfErrorCorrection_legacy(const BitmapRef<float, 4> &output, const Vector2 &threshold) {
void msdfErrorCorrection_legacy(const BitmapSection<float, 4> &output, const Vector2 &threshold) {
msdfErrorCorrectionInner_legacy(output, threshold);
}

View File

@ -12,29 +12,29 @@
namespace msdfgen {
/// Predicts potential artifacts caused by the interpolation of the MSDF and corrects them by converting nearby texels to single-channel.
void msdfErrorCorrection(const BitmapRef<float, 3> &sdf, const Shape &shape, const SDFTransformation &transformation, const MSDFGeneratorConfig &config = MSDFGeneratorConfig());
void msdfErrorCorrection(const BitmapRef<float, 4> &sdf, const Shape &shape, const SDFTransformation &transformation, const MSDFGeneratorConfig &config = MSDFGeneratorConfig());
void msdfErrorCorrection(const BitmapRef<float, 3> &sdf, const Shape &shape, const Projection &projection, Range range, const MSDFGeneratorConfig &config = MSDFGeneratorConfig());
void msdfErrorCorrection(const BitmapRef<float, 4> &sdf, const Shape &shape, const Projection &projection, Range range, const MSDFGeneratorConfig &config = MSDFGeneratorConfig());
void msdfErrorCorrection(const BitmapSection<float, 3> &sdf, const Shape &shape, const SDFTransformation &transformation, const MSDFGeneratorConfig &config = MSDFGeneratorConfig());
void msdfErrorCorrection(const BitmapSection<float, 4> &sdf, const Shape &shape, const SDFTransformation &transformation, const MSDFGeneratorConfig &config = MSDFGeneratorConfig());
void msdfErrorCorrection(const BitmapSection<float, 3> &sdf, const Shape &shape, const Projection &projection, Range range, const MSDFGeneratorConfig &config = MSDFGeneratorConfig());
void msdfErrorCorrection(const BitmapSection<float, 4> &sdf, const Shape &shape, const Projection &projection, Range range, const MSDFGeneratorConfig &config = MSDFGeneratorConfig());
/// Applies the simplified error correction to all discontiunous distances (INDISCRIMINATE mode). Does not need shape or translation.
void msdfFastDistanceErrorCorrection(const BitmapRef<float, 3> &sdf, const SDFTransformation &transformation, double minDeviationRatio = ErrorCorrectionConfig::defaultMinDeviationRatio);
void msdfFastDistanceErrorCorrection(const BitmapRef<float, 4> &sdf, const SDFTransformation &transformation, double minDeviationRatio = ErrorCorrectionConfig::defaultMinDeviationRatio);
void msdfFastDistanceErrorCorrection(const BitmapRef<float, 3> &sdf, const Projection &projection, Range range, double minDeviationRatio = ErrorCorrectionConfig::defaultMinDeviationRatio);
void msdfFastDistanceErrorCorrection(const BitmapRef<float, 4> &sdf, const Projection &projection, Range range, double minDeviationRatio = ErrorCorrectionConfig::defaultMinDeviationRatio);
void msdfFastDistanceErrorCorrection(const BitmapRef<float, 3> &sdf, Range pxRange, double minDeviationRatio = ErrorCorrectionConfig::defaultMinDeviationRatio);
void msdfFastDistanceErrorCorrection(const BitmapRef<float, 4> &sdf, Range pxRange, double minDeviationRatio = ErrorCorrectionConfig::defaultMinDeviationRatio);
void msdfFastDistanceErrorCorrection(const BitmapSection<float, 3> &sdf, const SDFTransformation &transformation, double minDeviationRatio = ErrorCorrectionConfig::defaultMinDeviationRatio);
void msdfFastDistanceErrorCorrection(const BitmapSection<float, 4> &sdf, const SDFTransformation &transformation, double minDeviationRatio = ErrorCorrectionConfig::defaultMinDeviationRatio);
void msdfFastDistanceErrorCorrection(const BitmapSection<float, 3> &sdf, const Projection &projection, Range range, double minDeviationRatio = ErrorCorrectionConfig::defaultMinDeviationRatio);
void msdfFastDistanceErrorCorrection(const BitmapSection<float, 4> &sdf, const Projection &projection, Range range, double minDeviationRatio = ErrorCorrectionConfig::defaultMinDeviationRatio);
void msdfFastDistanceErrorCorrection(const BitmapSection<float, 3> &sdf, Range pxRange, double minDeviationRatio = ErrorCorrectionConfig::defaultMinDeviationRatio);
void msdfFastDistanceErrorCorrection(const BitmapSection<float, 4> &sdf, Range pxRange, double minDeviationRatio = ErrorCorrectionConfig::defaultMinDeviationRatio);
/// Applies the simplified error correction to edges only (EDGE_ONLY mode). Does not need shape or translation.
void msdfFastEdgeErrorCorrection(const BitmapRef<float, 3> &sdf, const SDFTransformation &transformation, double minDeviationRatio = ErrorCorrectionConfig::defaultMinDeviationRatio);
void msdfFastEdgeErrorCorrection(const BitmapRef<float, 4> &sdf, const SDFTransformation &transformation, double minDeviationRatio = ErrorCorrectionConfig::defaultMinDeviationRatio);
void msdfFastEdgeErrorCorrection(const BitmapRef<float, 3> &sdf, const Projection &projection, Range range, double minDeviationRatio = ErrorCorrectionConfig::defaultMinDeviationRatio);
void msdfFastEdgeErrorCorrection(const BitmapRef<float, 4> &sdf, const Projection &projection, Range range, double minDeviationRatio = ErrorCorrectionConfig::defaultMinDeviationRatio);
void msdfFastEdgeErrorCorrection(const BitmapRef<float, 3> &sdf, Range pxRange, double minDeviationRatio = ErrorCorrectionConfig::defaultMinDeviationRatio);
void msdfFastEdgeErrorCorrection(const BitmapRef<float, 4> &sdf, Range pxRange, double minDeviationRatio = ErrorCorrectionConfig::defaultMinDeviationRatio);
void msdfFastEdgeErrorCorrection(const BitmapSection<float, 3> &sdf, const SDFTransformation &transformation, double minDeviationRatio = ErrorCorrectionConfig::defaultMinDeviationRatio);
void msdfFastEdgeErrorCorrection(const BitmapSection<float, 4> &sdf, const SDFTransformation &transformation, double minDeviationRatio = ErrorCorrectionConfig::defaultMinDeviationRatio);
void msdfFastEdgeErrorCorrection(const BitmapSection<float, 3> &sdf, const Projection &projection, Range range, double minDeviationRatio = ErrorCorrectionConfig::defaultMinDeviationRatio);
void msdfFastEdgeErrorCorrection(const BitmapSection<float, 4> &sdf, const Projection &projection, Range range, double minDeviationRatio = ErrorCorrectionConfig::defaultMinDeviationRatio);
void msdfFastEdgeErrorCorrection(const BitmapSection<float, 3> &sdf, Range pxRange, double minDeviationRatio = ErrorCorrectionConfig::defaultMinDeviationRatio);
void msdfFastEdgeErrorCorrection(const BitmapSection<float, 4> &sdf, Range pxRange, double minDeviationRatio = ErrorCorrectionConfig::defaultMinDeviationRatio);
/// The original version of the error correction algorithm.
void msdfErrorCorrection_legacy(const BitmapRef<float, 3> &output, const Vector2 &threshold);
void msdfErrorCorrection_legacy(const BitmapRef<float, 4> &output, const Vector2 &threshold);
void msdfErrorCorrection_legacy(const BitmapSection<float, 3> &output, const Vector2 &threshold);
void msdfErrorCorrection_legacy(const BitmapSection<float, 4> &output, const Vector2 &threshold);
}

View File

@ -15,7 +15,7 @@ template <>
class DistancePixelConversion<double> {
DistanceMapping mapping;
public:
typedef BitmapRef<float, 1> BitmapRefType;
typedef BitmapSection<float, 1> BitmapSectionType;
inline explicit DistancePixelConversion(DistanceMapping mapping) : mapping(mapping) { }
inline void operator()(float *pixels, double distance) const {
*pixels = float(mapping(distance));
@ -26,7 +26,7 @@ template <>
class DistancePixelConversion<MultiDistance> {
DistanceMapping mapping;
public:
typedef BitmapRef<float, 3> BitmapRefType;
typedef BitmapSection<float, 3> BitmapSectionType;
inline explicit DistancePixelConversion(DistanceMapping mapping) : mapping(mapping) { }
inline void operator()(float *pixels, const MultiDistance &distance) const {
pixels[0] = float(mapping(distance.r));
@ -39,7 +39,7 @@ template <>
class DistancePixelConversion<MultiAndTrueDistance> {
DistanceMapping mapping;
public:
typedef BitmapRef<float, 4> BitmapRefType;
typedef BitmapSection<float, 4> BitmapSectionType;
inline explicit DistancePixelConversion(DistanceMapping mapping) : mapping(mapping) { }
inline void operator()(float *pixels, const MultiAndTrueDistance &distance) const {
pixels[0] = float(mapping(distance.r));
@ -50,45 +50,46 @@ public:
};
template <class ContourCombiner>
void generateDistanceField(const typename DistancePixelConversion<typename ContourCombiner::DistanceType>::BitmapRefType &output, const Shape &shape, const SDFTransformation &transformation) {
void generateDistanceField(typename DistancePixelConversion<typename ContourCombiner::DistanceType>::BitmapSectionType output, const Shape &shape, const SDFTransformation &transformation) {
DistancePixelConversion<typename ContourCombiner::DistanceType> distancePixelConversion(transformation.distanceMapping);
output.reorient(shape.getYAxisOrientation());
#ifdef MSDFGEN_USE_OPENMP
#pragma omp parallel
#endif
{
ShapeDistanceFinder<ContourCombiner> distanceFinder(shape);
bool rightToLeft = false;
int xDirection = 1;
#ifdef MSDFGEN_USE_OPENMP
#pragma omp for
#endif
for (int y = 0; y < output.height; ++y) {
int row = shape.inverseYAxis ? output.height-y-1 : y;
int x = xDirection < 0 ? output.width-1 : 0;
for (int col = 0; col < output.width; ++col) {
int x = rightToLeft ? output.width-col-1 : col;
Point2 p = transformation.unproject(Point2(x+.5, y+.5));
typename ContourCombiner::DistanceType distance = distanceFinder.distance(p);
distancePixelConversion(output(x, row), distance);
distancePixelConversion(output(x, y), distance);
x += xDirection;
}
rightToLeft = !rightToLeft;
xDirection = -xDirection;
}
}
}
void generateSDF(const BitmapRef<float, 1> &output, const Shape &shape, const SDFTransformation &transformation, const GeneratorConfig &config) {
void generateSDF(const BitmapSection<float, 1> &output, const Shape &shape, const SDFTransformation &transformation, const GeneratorConfig &config) {
if (config.overlapSupport)
generateDistanceField<OverlappingContourCombiner<TrueDistanceSelector> >(output, shape, transformation);
else
generateDistanceField<SimpleContourCombiner<TrueDistanceSelector> >(output, shape, transformation);
}
void generatePSDF(const BitmapRef<float, 1> &output, const Shape &shape, const SDFTransformation &transformation, const GeneratorConfig &config) {
void generatePSDF(const BitmapSection<float, 1> &output, const Shape &shape, const SDFTransformation &transformation, const GeneratorConfig &config) {
if (config.overlapSupport)
generateDistanceField<OverlappingContourCombiner<PerpendicularDistanceSelector> >(output, shape, transformation);
else
generateDistanceField<SimpleContourCombiner<PerpendicularDistanceSelector> >(output, shape, transformation);
}
void generateMSDF(const BitmapRef<float, 3> &output, const Shape &shape, const SDFTransformation &transformation, const MSDFGeneratorConfig &config) {
void generateMSDF(const BitmapSection<float, 3> &output, const Shape &shape, const SDFTransformation &transformation, const MSDFGeneratorConfig &config) {
if (config.overlapSupport)
generateDistanceField<OverlappingContourCombiner<MultiDistanceSelector> >(output, shape, transformation);
else
@ -96,7 +97,7 @@ void generateMSDF(const BitmapRef<float, 3> &output, const Shape &shape, const S
msdfErrorCorrection(output, shape, transformation, config);
}
void generateMTSDF(const BitmapRef<float, 4> &output, const Shape &shape, const SDFTransformation &transformation, const MSDFGeneratorConfig &config) {
void generateMTSDF(const BitmapSection<float, 4> &output, const Shape &shape, const SDFTransformation &transformation, const MSDFGeneratorConfig &config) {
if (config.overlapSupport)
generateDistanceField<OverlappingContourCombiner<MultiAndTrueDistanceSelector> >(output, shape, transformation);
else
@ -104,21 +105,21 @@ void generateMTSDF(const BitmapRef<float, 4> &output, const Shape &shape, const
msdfErrorCorrection(output, shape, transformation, config);
}
void generateSDF(const BitmapRef<float, 1> &output, const Shape &shape, const Projection &projection, Range range, const GeneratorConfig &config) {
void generateSDF(const BitmapSection<float, 1> &output, const Shape &shape, const Projection &projection, Range range, const GeneratorConfig &config) {
if (config.overlapSupport)
generateDistanceField<OverlappingContourCombiner<TrueDistanceSelector> >(output, shape, SDFTransformation(projection, range));
else
generateDistanceField<SimpleContourCombiner<TrueDistanceSelector> >(output, shape, SDFTransformation(projection, range));
}
void generatePSDF(const BitmapRef<float, 1> &output, const Shape &shape, const Projection &projection, Range range, const GeneratorConfig &config) {
void generatePSDF(const BitmapSection<float, 1> &output, const Shape &shape, const Projection &projection, Range range, const GeneratorConfig &config) {
if (config.overlapSupport)
generateDistanceField<OverlappingContourCombiner<PerpendicularDistanceSelector> >(output, shape, SDFTransformation(projection, range));
else
generateDistanceField<SimpleContourCombiner<PerpendicularDistanceSelector> >(output, shape, SDFTransformation(projection, range));
}
void generateMSDF(const BitmapRef<float, 3> &output, const Shape &shape, const Projection &projection, Range range, const MSDFGeneratorConfig &config) {
void generateMSDF(const BitmapSection<float, 3> &output, const Shape &shape, const Projection &projection, Range range, const MSDFGeneratorConfig &config) {
if (config.overlapSupport)
generateDistanceField<OverlappingContourCombiner<MultiDistanceSelector> >(output, shape, SDFTransformation(projection, range));
else
@ -126,7 +127,7 @@ void generateMSDF(const BitmapRef<float, 3> &output, const Shape &shape, const P
msdfErrorCorrection(output, shape, SDFTransformation(projection, range), config);
}
void generateMTSDF(const BitmapRef<float, 4> &output, const Shape &shape, const Projection &projection, Range range, const MSDFGeneratorConfig &config) {
void generateMTSDF(const BitmapSection<float, 4> &output, const Shape &shape, const Projection &projection, Range range, const MSDFGeneratorConfig &config) {
if (config.overlapSupport)
generateDistanceField<OverlappingContourCombiner<MultiAndTrueDistanceSelector> >(output, shape, SDFTransformation(projection, range));
else
@ -136,39 +137,39 @@ void generateMTSDF(const BitmapRef<float, 4> &output, const Shape &shape, const
// Legacy API
void generatePseudoSDF(const BitmapRef<float, 1> &output, const Shape &shape, const Projection &projection, Range range, const GeneratorConfig &config) {
void generatePseudoSDF(const BitmapSection<float, 1> &output, const Shape &shape, const Projection &projection, Range range, const GeneratorConfig &config) {
generatePSDF(output, shape, SDFTransformation(projection, range), config);
}
void generateSDF(const BitmapRef<float, 1> &output, const Shape &shape, Range range, const Vector2 &scale, const Vector2 &translate, bool overlapSupport) {
void generateSDF(const BitmapSection<float, 1> &output, const Shape &shape, Range range, const Vector2 &scale, const Vector2 &translate, bool overlapSupport) {
generateSDF(output, shape, Projection(scale, translate), range, GeneratorConfig(overlapSupport));
}
void generatePSDF(const BitmapRef<float, 1> &output, const Shape &shape, Range range, const Vector2 &scale, const Vector2 &translate, bool overlapSupport) {
void generatePSDF(const BitmapSection<float, 1> &output, const Shape &shape, Range range, const Vector2 &scale, const Vector2 &translate, bool overlapSupport) {
generatePSDF(output, shape, Projection(scale, translate), range, GeneratorConfig(overlapSupport));
}
void generatePseudoSDF(const BitmapRef<float, 1> &output, const Shape &shape, Range range, const Vector2 &scale, const Vector2 &translate, bool overlapSupport) {
void generatePseudoSDF(const BitmapSection<float, 1> &output, const Shape &shape, Range range, const Vector2 &scale, const Vector2 &translate, bool overlapSupport) {
generatePSDF(output, shape, Projection(scale, translate), range, GeneratorConfig(overlapSupport));
}
void generateMSDF(const BitmapRef<float, 3> &output, const Shape &shape, Range range, const Vector2 &scale, const Vector2 &translate, const ErrorCorrectionConfig &errorCorrectionConfig, bool overlapSupport) {
void generateMSDF(const BitmapSection<float, 3> &output, const Shape &shape, Range range, const Vector2 &scale, const Vector2 &translate, const ErrorCorrectionConfig &errorCorrectionConfig, bool overlapSupport) {
generateMSDF(output, shape, Projection(scale, translate), range, MSDFGeneratorConfig(overlapSupport, errorCorrectionConfig));
}
void generateMTSDF(const BitmapRef<float, 4> &output, const Shape &shape, Range range, const Vector2 &scale, const Vector2 &translate, const ErrorCorrectionConfig &errorCorrectionConfig, bool overlapSupport) {
void generateMTSDF(const BitmapSection<float, 4> &output, const Shape &shape, Range range, const Vector2 &scale, const Vector2 &translate, const ErrorCorrectionConfig &errorCorrectionConfig, bool overlapSupport) {
generateMTSDF(output, shape, Projection(scale, translate), range, MSDFGeneratorConfig(overlapSupport, errorCorrectionConfig));
}
// Legacy version
void generateSDF_legacy(const BitmapRef<float, 1> &output, const Shape &shape, Range range, const Vector2 &scale, const Vector2 &translate) {
void generateSDF_legacy(BitmapSection<float, 1> output, const Shape &shape, Range range, const Vector2 &scale, const Vector2 &translate) {
DistanceMapping distanceMapping(range);
output.reorient(shape.getYAxisOrientation());
#ifdef MSDFGEN_USE_OPENMP
#pragma omp parallel for
#endif
for (int y = 0; y < output.height; ++y) {
int row = shape.inverseYAxis ? output.height-y-1 : y;
for (int x = 0; x < output.width; ++x) {
double dummy;
Point2 p = Vector2(x+.5, y+.5)/scale-translate;
@ -179,18 +180,18 @@ void generateSDF_legacy(const BitmapRef<float, 1> &output, const Shape &shape, R
if (distance < minDistance)
minDistance = distance;
}
*output(x, row) = float(distanceMapping(minDistance.distance));
*output(x, y) = float(distanceMapping(minDistance.distance));
}
}
}
void generatePSDF_legacy(const BitmapRef<float, 1> &output, const Shape &shape, Range range, const Vector2 &scale, const Vector2 &translate) {
void generatePSDF_legacy(BitmapSection<float, 1> output, const Shape &shape, Range range, const Vector2 &scale, const Vector2 &translate) {
DistanceMapping distanceMapping(range);
output.reorient(shape.getYAxisOrientation());
#ifdef MSDFGEN_USE_OPENMP
#pragma omp parallel for
#endif
for (int y = 0; y < output.height; ++y) {
int row = shape.inverseYAxis ? output.height-y-1 : y;
for (int x = 0; x < output.width; ++x) {
Point2 p = Vector2(x+.5, y+.5)/scale-translate;
SignedDistance minDistance;
@ -208,22 +209,22 @@ void generatePSDF_legacy(const BitmapRef<float, 1> &output, const Shape &shape,
}
if (nearEdge)
(*nearEdge)->distanceToPerpendicularDistance(minDistance, p, nearParam);
*output(x, row) = float(distanceMapping(minDistance.distance));
*output(x, y) = float(distanceMapping(minDistance.distance));
}
}
}
void generatePseudoSDF_legacy(const BitmapRef<float, 1> &output, const Shape &shape, Range range, const Vector2 &scale, const Vector2 &translate) {
void generatePseudoSDF_legacy(BitmapSection<float, 1> output, const Shape &shape, Range range, const Vector2 &scale, const Vector2 &translate) {
generatePSDF_legacy(output, shape, range, scale, translate);
}
void generateMSDF_legacy(const BitmapRef<float, 3> &output, const Shape &shape, Range range, const Vector2 &scale, const Vector2 &translate, ErrorCorrectionConfig errorCorrectionConfig) {
void generateMSDF_legacy(BitmapSection<float, 3> output, const Shape &shape, Range range, const Vector2 &scale, const Vector2 &translate, ErrorCorrectionConfig errorCorrectionConfig) {
DistanceMapping distanceMapping(range);
output.reorient(shape.getYAxisOrientation());
#ifdef MSDFGEN_USE_OPENMP
#pragma omp parallel for
#endif
for (int y = 0; y < output.height; ++y) {
int row = shape.inverseYAxis ? output.height-y-1 : y;
for (int x = 0; x < output.width; ++x) {
Point2 p = Vector2(x+.5, y+.5)/scale-translate;
@ -262,9 +263,9 @@ void generateMSDF_legacy(const BitmapRef<float, 3> &output, const Shape &shape,
(*g.nearEdge)->distanceToPerpendicularDistance(g.minDistance, p, g.nearParam);
if (b.nearEdge)
(*b.nearEdge)->distanceToPerpendicularDistance(b.minDistance, p, b.nearParam);
output(x, row)[0] = float(distanceMapping(r.minDistance.distance));
output(x, row)[1] = float(distanceMapping(g.minDistance.distance));
output(x, row)[2] = float(distanceMapping(b.minDistance.distance));
output(x, y)[0] = float(distanceMapping(r.minDistance.distance));
output(x, y)[1] = float(distanceMapping(g.minDistance.distance));
output(x, y)[2] = float(distanceMapping(b.minDistance.distance));
}
}
@ -272,13 +273,13 @@ void generateMSDF_legacy(const BitmapRef<float, 3> &output, const Shape &shape,
msdfErrorCorrection(output, shape, Projection(scale, translate), range, MSDFGeneratorConfig(false, errorCorrectionConfig));
}
void generateMTSDF_legacy(const BitmapRef<float, 4> &output, const Shape &shape, Range range, const Vector2 &scale, const Vector2 &translate, ErrorCorrectionConfig errorCorrectionConfig) {
void generateMTSDF_legacy(BitmapSection<float, 4> output, const Shape &shape, Range range, const Vector2 &scale, const Vector2 &translate, ErrorCorrectionConfig errorCorrectionConfig) {
DistanceMapping distanceMapping(range);
output.reorient(shape.getYAxisOrientation());
#ifdef MSDFGEN_USE_OPENMP
#pragma omp parallel for
#endif
for (int y = 0; y < output.height; ++y) {
int row = shape.inverseYAxis ? output.height-y-1 : y;
for (int x = 0; x < output.width; ++x) {
Point2 p = Vector2(x+.5, y+.5)/scale-translate;
@ -320,10 +321,10 @@ void generateMTSDF_legacy(const BitmapRef<float, 4> &output, const Shape &shape,
(*g.nearEdge)->distanceToPerpendicularDistance(g.minDistance, p, g.nearParam);
if (b.nearEdge)
(*b.nearEdge)->distanceToPerpendicularDistance(b.minDistance, p, b.nearParam);
output(x, row)[0] = float(distanceMapping(r.minDistance.distance));
output(x, row)[1] = float(distanceMapping(g.minDistance.distance));
output(x, row)[2] = float(distanceMapping(b.minDistance.distance));
output(x, row)[3] = float(distanceMapping(minDistance.distance));
output(x, y)[0] = float(distanceMapping(r.minDistance.distance));
output(x, y)[1] = float(distanceMapping(g.minDistance.distance));
output(x, y)[2] = float(distanceMapping(b.minDistance.distance));
output(x, y)[3] = float(distanceMapping(minDistance.distance));
}
}

View File

@ -6,58 +6,60 @@
namespace msdfgen {
void rasterize(const BitmapRef<float, 1> &output, const Shape &shape, const Projection &projection, FillRule fillRule) {
void rasterize(BitmapSection<float, 1> output, const Shape &shape, const Projection &projection, FillRule fillRule) {
output.reorient(shape.getYAxisOrientation());
Scanline scanline;
for (int y = 0; y < output.height; ++y) {
int row = shape.inverseYAxis ? output.height-y-1 : y;
shape.scanline(scanline, projection.unprojectY(y+.5));
for (int x = 0; x < output.width; ++x)
*output(x, row) = (float) scanline.filled(projection.unprojectX(x+.5), fillRule);
*output(x, y) = (float) scanline.filled(projection.unprojectX(x+.5), fillRule);
}
}
void distanceSignCorrection(const BitmapRef<float, 1> &sdf, const Shape &shape, const Projection &projection, FillRule fillRule) {
void distanceSignCorrection(BitmapSection<float, 1> sdf, const Shape &shape, const Projection &projection, float sdfZeroValue, FillRule fillRule) {
sdf.reorient(shape.getYAxisOrientation());
float doubleSdfZeroValue = sdfZeroValue+sdfZeroValue;
Scanline scanline;
for (int y = 0; y < sdf.height; ++y) {
int row = shape.inverseYAxis ? sdf.height-y-1 : y;
shape.scanline(scanline, projection.unprojectY(y+.5));
for (int x = 0; x < sdf.width; ++x) {
bool fill = scanline.filled(projection.unprojectX(x+.5), fillRule);
float &sd = *sdf(x, row);
if ((sd > .5f) != fill)
sd = 1.f-sd;
float &sd = *sdf(x, y);
if ((sd > sdfZeroValue) != fill)
sd = doubleSdfZeroValue-sd;
}
}
}
template <int N>
static void multiDistanceSignCorrection(const BitmapRef<float, N> &sdf, const Shape &shape, const Projection &projection, FillRule fillRule) {
static void multiDistanceSignCorrection(BitmapSection<float, N> sdf, const Shape &shape, const Projection &projection, float sdfZeroValue, FillRule fillRule) {
int w = sdf.width, h = sdf.height;
if (!(w && h))
return;
sdf.reorient(shape.getYAxisOrientation());
float doubleSdfZeroValue = sdfZeroValue+sdfZeroValue;
Scanline scanline;
bool ambiguous = false;
std::vector<char> matchMap;
matchMap.resize(w*h);
char *match = &matchMap[0];
for (int y = 0; y < h; ++y) {
int row = shape.inverseYAxis ? h-y-1 : y;
shape.scanline(scanline, projection.unprojectY(y+.5));
for (int x = 0; x < w; ++x) {
bool fill = scanline.filled(projection.unprojectX(x+.5), fillRule);
float *msd = sdf(x, row);
float *msd = sdf(x, y);
float sd = median(msd[0], msd[1], msd[2]);
if (sd == .5f)
if (sd == sdfZeroValue)
ambiguous = true;
else if ((sd > .5f) != fill) {
msd[0] = 1.f-msd[0];
msd[1] = 1.f-msd[1];
msd[2] = 1.f-msd[2];
else if ((sd > sdfZeroValue) != fill) {
msd[0] = doubleSdfZeroValue-msd[0];
msd[1] = doubleSdfZeroValue-msd[1];
msd[2] = doubleSdfZeroValue-msd[2];
*match = -1;
} else
*match = 1;
if (N >= 4 && (msd[3] > .5f) != fill)
msd[3] = 1.f-msd[3];
if (N >= 4 && (msd[3] > sdfZeroValue) != fill)
msd[3] = doubleSdfZeroValue-msd[3];
++match;
}
}
@ -65,7 +67,6 @@ static void multiDistanceSignCorrection(const BitmapRef<float, N> &sdf, const Sh
if (ambiguous) {
match = &matchMap[0];
for (int y = 0; y < h; ++y) {
int row = shape.inverseYAxis ? h-y-1 : y;
for (int x = 0; x < w; ++x) {
if (!*match) {
int neighborMatch = 0;
@ -74,10 +75,10 @@ static void multiDistanceSignCorrection(const BitmapRef<float, N> &sdf, const Sh
if (y > 0) neighborMatch += *(match-w);
if (y < h-1) neighborMatch += *(match+w);
if (neighborMatch < 0) {
float *msd = sdf(x, row);
msd[0] = 1.f-msd[0];
msd[1] = 1.f-msd[1];
msd[2] = 1.f-msd[2];
float *msd = sdf(x, y);
msd[0] = doubleSdfZeroValue-msd[0];
msd[1] = doubleSdfZeroValue-msd[1];
msd[2] = doubleSdfZeroValue-msd[2];
}
}
++match;
@ -86,29 +87,41 @@ static void multiDistanceSignCorrection(const BitmapRef<float, N> &sdf, const Sh
}
}
void distanceSignCorrection(const BitmapRef<float, 3> &sdf, const Shape &shape, const Projection &projection, FillRule fillRule) {
multiDistanceSignCorrection(sdf, shape, projection, fillRule);
void distanceSignCorrection(BitmapSection<float, 3> sdf, const Shape &shape, const Projection &projection, float sdfZeroValue, FillRule fillRule) {
multiDistanceSignCorrection(sdf, shape, projection, sdfZeroValue, fillRule);
}
void distanceSignCorrection(const BitmapRef<float, 4> &sdf, const Shape &shape, const Projection &projection, FillRule fillRule) {
multiDistanceSignCorrection(sdf, shape, projection, fillRule);
void distanceSignCorrection(BitmapSection<float, 4> sdf, const Shape &shape, const Projection &projection, float sdfZeroValue, FillRule fillRule) {
multiDistanceSignCorrection(sdf, shape, projection, sdfZeroValue, fillRule);
}
// Legacy API
void rasterize(const BitmapRef<float, 1> &output, const Shape &shape, const Vector2 &scale, const Vector2 &translate, FillRule fillRule) {
void rasterize(const BitmapSection<float, 1> &output, const Shape &shape, const Vector2 &scale, const Vector2 &translate, FillRule fillRule) {
rasterize(output, shape, Projection(scale, translate), fillRule);
}
void distanceSignCorrection(const BitmapRef<float, 1> &sdf, const Shape &shape, const Vector2 &scale, const Vector2 &translate, FillRule fillRule) {
void distanceSignCorrection(BitmapSection<float, 1> sdf, const Shape &shape, const Projection &projection, FillRule fillRule) {
distanceSignCorrection(sdf, shape, projection, .5f, fillRule);
}
void distanceSignCorrection(BitmapSection<float, 3> sdf, const Shape &shape, const Projection &projection, FillRule fillRule) {
distanceSignCorrection(sdf, shape, projection, .5f, fillRule);
}
void distanceSignCorrection(BitmapSection<float, 4> sdf, const Shape &shape, const Projection &projection, FillRule fillRule) {
distanceSignCorrection(sdf, shape, projection, .5f, fillRule);
}
void distanceSignCorrection(const BitmapSection<float, 1> &sdf, const Shape &shape, const Vector2 &scale, const Vector2 &translate, FillRule fillRule) {
distanceSignCorrection(sdf, shape, Projection(scale, translate), fillRule);
}
void distanceSignCorrection(const BitmapRef<float, 3> &sdf, const Shape &shape, const Vector2 &scale, const Vector2 &translate, FillRule fillRule) {
void distanceSignCorrection(const BitmapSection<float, 3> &sdf, const Shape &shape, const Vector2 &scale, const Vector2 &translate, FillRule fillRule) {
distanceSignCorrection(sdf, shape, Projection(scale, translate), fillRule);
}
void distanceSignCorrection(const BitmapRef<float, 4> &sdf, const Shape &shape, const Vector2 &scale, const Vector2 &translate, FillRule fillRule) {
void distanceSignCorrection(const BitmapSection<float, 4> &sdf, const Shape &shape, const Vector2 &scale, const Vector2 &translate, FillRule fillRule) {
distanceSignCorrection(sdf, shape, Projection(scale, translate), fillRule);
}

View File

@ -10,16 +10,19 @@
namespace msdfgen {
/// Rasterizes the shape into a monochrome bitmap.
void rasterize(const BitmapRef<float, 1> &output, const Shape &shape, const Projection &projection, FillRule fillRule = FILL_NONZERO);
void rasterize(BitmapSection<float, 1> output, const Shape &shape, const Projection &projection, FillRule fillRule = FILL_NONZERO);
/// Fixes the sign of the input signed distance field, so that it matches the shape's rasterized fill.
void distanceSignCorrection(const BitmapRef<float, 1> &sdf, const Shape &shape, const Projection &projection, FillRule fillRule = FILL_NONZERO);
void distanceSignCorrection(const BitmapRef<float, 3> &sdf, const Shape &shape, const Projection &projection, FillRule fillRule = FILL_NONZERO);
void distanceSignCorrection(const BitmapRef<float, 4> &sdf, const Shape &shape, const Projection &projection, FillRule fillRule = FILL_NONZERO);
void distanceSignCorrection(BitmapSection<float, 1> sdf, const Shape &shape, const Projection &projection, float sdfZeroValue = .5f, FillRule fillRule = FILL_NONZERO);
void distanceSignCorrection(BitmapSection<float, 3> sdf, const Shape &shape, const Projection &projection, float sdfZeroValue = .5f, FillRule fillRule = FILL_NONZERO);
void distanceSignCorrection(BitmapSection<float, 4> sdf, const Shape &shape, const Projection &projection, float sdfZeroValue = .5f, FillRule fillRule = FILL_NONZERO);
// Old version of the function API's kept for backwards compatibility
void rasterize(const BitmapRef<float, 1> &output, const Shape &shape, const Vector2 &scale, const Vector2 &translate, FillRule fillRule = FILL_NONZERO);
void distanceSignCorrection(const BitmapRef<float, 1> &sdf, const Shape &shape, const Vector2 &scale, const Vector2 &translate, FillRule fillRule = FILL_NONZERO);
void distanceSignCorrection(const BitmapRef<float, 3> &sdf, const Shape &shape, const Vector2 &scale, const Vector2 &translate, FillRule fillRule = FILL_NONZERO);
void distanceSignCorrection(const BitmapRef<float, 4> &sdf, const Shape &shape, const Vector2 &scale, const Vector2 &translate, FillRule fillRule = FILL_NONZERO);
// Old versions of the function API's kept for backwards compatibility
void rasterize(const BitmapSection<float, 1> &output, const Shape &shape, const Vector2 &scale, const Vector2 &translate, FillRule fillRule = FILL_NONZERO);
void distanceSignCorrection(BitmapSection<float, 1> sdf, const Shape &shape, const Projection &projection, FillRule fillRule);
void distanceSignCorrection(BitmapSection<float, 3> sdf, const Shape &shape, const Projection &projection, FillRule fillRule);
void distanceSignCorrection(BitmapSection<float, 4> sdf, const Shape &shape, const Projection &projection, FillRule fillRule);
void distanceSignCorrection(const BitmapSection<float, 1> &sdf, const Shape &shape, const Vector2 &scale, const Vector2 &translate, FillRule fillRule = FILL_NONZERO);
void distanceSignCorrection(const BitmapSection<float, 3> &sdf, const Shape &shape, const Vector2 &scale, const Vector2 &translate, FillRule fillRule = FILL_NONZERO);
void distanceSignCorrection(const BitmapSection<float, 4> &sdf, const Shape &shape, const Vector2 &scale, const Vector2 &translate, FillRule fillRule = FILL_NONZERO);
}

View File

@ -12,7 +12,7 @@ static float distVal(float dist, DistanceMapping mapping) {
return (float) clamp(mapping(dist)+.5);
}
void renderSDF(const BitmapRef<float, 1> &output, const BitmapConstRef<float, 1> &sdf, Range sdfPxRange, float sdThreshold) {
void renderSDF(const BitmapSection<float, 1> &output, const BitmapConstSection<float, 1> &sdf, Range sdfPxRange, float sdThreshold) {
Vector2 scale((double) sdf.width/output.width, (double) sdf.height/output.height);
if (sdfPxRange.lower == sdfPxRange.upper) {
for (int y = 0; y < output.height; ++y) {
@ -37,7 +37,7 @@ void renderSDF(const BitmapRef<float, 1> &output, const BitmapConstRef<float, 1>
}
void renderSDF(const BitmapRef<float, 3> &output, const BitmapConstRef<float, 1> &sdf, Range sdfPxRange, float sdThreshold) {
void renderSDF(const BitmapSection<float, 3> &output, const BitmapConstSection<float, 1> &sdf, Range sdfPxRange, float sdThreshold) {
Vector2 scale((double) sdf.width/output.width, (double) sdf.height/output.height);
if (sdfPxRange.lower == sdfPxRange.upper) {
for (int y = 0; y < output.height; ++y) {
@ -67,7 +67,7 @@ void renderSDF(const BitmapRef<float, 3> &output, const BitmapConstRef<float, 1>
}
}
void renderSDF(const BitmapRef<float, 1> &output, const BitmapConstRef<float, 3> &sdf, Range sdfPxRange, float sdThreshold) {
void renderSDF(const BitmapSection<float, 1> &output, const BitmapConstSection<float, 3> &sdf, Range sdfPxRange, float sdThreshold) {
Vector2 scale((double) sdf.width/output.width, (double) sdf.height/output.height);
if (sdfPxRange.lower == sdfPxRange.upper) {
for (int y = 0; y < output.height; ++y) {
@ -91,7 +91,7 @@ void renderSDF(const BitmapRef<float, 1> &output, const BitmapConstRef<float, 3>
}
}
void renderSDF(const BitmapRef<float, 3> &output, const BitmapConstRef<float, 3> &sdf, Range sdfPxRange, float sdThreshold) {
void renderSDF(const BitmapSection<float, 3> &output, const BitmapConstSection<float, 3> &sdf, Range sdfPxRange, float sdThreshold) {
Vector2 scale((double) sdf.width/output.width, (double) sdf.height/output.height);
if (sdfPxRange.lower == sdfPxRange.upper) {
for (int y = 0; y < output.height; ++y) {
@ -119,7 +119,7 @@ void renderSDF(const BitmapRef<float, 3> &output, const BitmapConstRef<float, 3>
}
}
void renderSDF(const BitmapRef<float, 1> &output, const BitmapConstRef<float, 4> &sdf, Range sdfPxRange, float sdThreshold) {
void renderSDF(const BitmapSection<float, 1> &output, const BitmapConstSection<float, 4> &sdf, Range sdfPxRange, float sdThreshold) {
Vector2 scale((double) sdf.width/output.width, (double) sdf.height/output.height);
if (sdfPxRange.lower == sdfPxRange.upper) {
for (int y = 0; y < output.height; ++y) {
@ -143,7 +143,7 @@ void renderSDF(const BitmapRef<float, 1> &output, const BitmapConstRef<float, 4>
}
}
void renderSDF(const BitmapRef<float, 4> &output, const BitmapConstRef<float, 4> &sdf, Range sdfPxRange, float sdThreshold) {
void renderSDF(const BitmapSection<float, 4> &output, const BitmapConstSection<float, 4> &sdf, Range sdfPxRange, float sdThreshold) {
Vector2 scale((double) sdf.width/output.width, (double) sdf.height/output.height);
if (sdfPxRange.lower == sdfPxRange.upper) {
for (int y = 0; y < output.height; ++y) {
@ -173,19 +173,19 @@ void renderSDF(const BitmapRef<float, 4> &output, const BitmapConstRef<float, 4>
}
}
void simulate8bit(const BitmapRef<float, 1> &bitmap) {
void simulate8bit(const BitmapSection<float, 1> &bitmap) {
const float *end = bitmap.pixels+1*bitmap.width*bitmap.height;
for (float *p = bitmap.pixels; p < end; ++p)
*p = pixelByteToFloat(pixelFloatToByte(*p));
}
void simulate8bit(const BitmapRef<float, 3> &bitmap) {
void simulate8bit(const BitmapSection<float, 3> &bitmap) {
const float *end = bitmap.pixels+3*bitmap.width*bitmap.height;
for (float *p = bitmap.pixels; p < end; ++p)
*p = pixelByteToFloat(pixelFloatToByte(*p));
}
void simulate8bit(const BitmapRef<float, 4> &bitmap) {
void simulate8bit(const BitmapSection<float, 4> &bitmap) {
const float *end = bitmap.pixels+4*bitmap.width*bitmap.height;
for (float *p = bitmap.pixels; p < end; ++p)
*p = pixelByteToFloat(pixelFloatToByte(*p));

View File

@ -8,16 +8,16 @@
namespace msdfgen {
/// Reconstructs the shape's appearance into output from the distance field sdf.
void renderSDF(const BitmapRef<float, 1> &output, const BitmapConstRef<float, 1> &sdf, Range sdfPxRange = 0, float sdThreshold = .5f);
void renderSDF(const BitmapRef<float, 3> &output, const BitmapConstRef<float, 1> &sdf, Range sdfPxRange = 0, float sdThreshold = .5f);
void renderSDF(const BitmapRef<float, 1> &output, const BitmapConstRef<float, 3> &sdf, Range sdfPxRange = 0, float sdThreshold = .5f);
void renderSDF(const BitmapRef<float, 3> &output, const BitmapConstRef<float, 3> &sdf, Range sdfPxRange = 0, float sdThreshold = .5f);
void renderSDF(const BitmapRef<float, 1> &output, const BitmapConstRef<float, 4> &sdf, Range sdfPxRange = 0, float sdThreshold = .5f);
void renderSDF(const BitmapRef<float, 4> &output, const BitmapConstRef<float, 4> &sdf, Range sdfPxRange = 0, float sdThreshold = .5f);
void renderSDF(const BitmapSection<float, 1> &output, const BitmapConstSection<float, 1> &sdf, Range sdfPxRange = 0, float sdThreshold = .5f);
void renderSDF(const BitmapSection<float, 3> &output, const BitmapConstSection<float, 1> &sdf, Range sdfPxRange = 0, float sdThreshold = .5f);
void renderSDF(const BitmapSection<float, 1> &output, const BitmapConstSection<float, 3> &sdf, Range sdfPxRange = 0, float sdThreshold = .5f);
void renderSDF(const BitmapSection<float, 3> &output, const BitmapConstSection<float, 3> &sdf, Range sdfPxRange = 0, float sdThreshold = .5f);
void renderSDF(const BitmapSection<float, 1> &output, const BitmapConstSection<float, 4> &sdf, Range sdfPxRange = 0, float sdThreshold = .5f);
void renderSDF(const BitmapSection<float, 4> &output, const BitmapConstSection<float, 4> &sdf, Range sdfPxRange = 0, float sdThreshold = .5f);
/// Snaps the values of the floating-point bitmaps into one of the 256 values representable in a standard 8-bit bitmap.
void simulate8bit(const BitmapRef<float, 1> &bitmap);
void simulate8bit(const BitmapRef<float, 3> &bitmap);
void simulate8bit(const BitmapRef<float, 4> &bitmap);
void simulate8bit(const BitmapSection<float, 1> &bitmap);
void simulate8bit(const BitmapSection<float, 3> &bitmap);
void simulate8bit(const BitmapSection<float, 4> &bitmap);
}

View File

@ -1,173 +0,0 @@
#ifndef _CRT_SECURE_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS
#endif
#include "save-bmp.h"
#include <cstdio>
#ifdef MSDFGEN_USE_CPP11
#include <cstdint>
#else
namespace msdfgen {
typedef int int32_t;
typedef unsigned uint32_t;
typedef unsigned short uint16_t;
typedef unsigned char uint8_t;
}
#endif
#include "pixel-conversion.hpp"
namespace msdfgen {
template <typename T>
static bool writeValue(FILE *file, T value) {
#ifdef __BIG_ENDIAN__
T reverse = 0;
for (int i = 0; i < sizeof(T); ++i) {
reverse <<= 8;
reverse |= value&T(0xff);
value >>= 8;
}
return fwrite(&reverse, sizeof(T), 1, file) == 1;
#else
return fwrite(&value, sizeof(T), 1, file) == 1;
#endif
}
static bool writeBmpHeader(FILE *file, int width, int height, int &paddedWidth) {
paddedWidth = (3*width+3)&~3;
const uint32_t bitmapStart = 54;
const uint32_t bitmapSize = paddedWidth*height;
const uint32_t fileSize = bitmapStart+bitmapSize;
writeValue<uint16_t>(file, 0x4d42u);
writeValue<uint32_t>(file, fileSize);
writeValue<uint16_t>(file, 0);
writeValue<uint16_t>(file, 0);
writeValue<uint32_t>(file, bitmapStart);
writeValue<uint32_t>(file, 40);
writeValue<int32_t>(file, width);
writeValue<int32_t>(file, height);
writeValue<uint16_t>(file, 1);
writeValue<uint16_t>(file, 24);
writeValue<uint32_t>(file, 0);
writeValue<uint32_t>(file, bitmapSize);
writeValue<uint32_t>(file, 2835);
writeValue<uint32_t>(file, 2835);
writeValue<uint32_t>(file, 0);
writeValue<uint32_t>(file, 0);
return true;
}
bool saveBmp(const BitmapConstRef<byte, 1> &bitmap, const char *filename) {
FILE *file = fopen(filename, "wb");
if (!file)
return false;
int paddedWidth;
writeBmpHeader(file, bitmap.width, bitmap.height, paddedWidth);
const uint8_t padding[4] = { };
int padLength = paddedWidth-3*bitmap.width;
for (int y = 0; y < bitmap.height; ++y) {
for (int x = 0; x < bitmap.width; ++x) {
uint8_t px = (uint8_t) *bitmap(x, y);
fwrite(&px, sizeof(uint8_t), 1, file);
fwrite(&px, sizeof(uint8_t), 1, file);
fwrite(&px, sizeof(uint8_t), 1, file);
}
fwrite(padding, 1, padLength, file);
}
return !fclose(file);
}
bool saveBmp(const BitmapConstRef<byte, 3> &bitmap, const char *filename) {
FILE *file = fopen(filename, "wb");
if (!file)
return false;
int paddedWidth;
writeBmpHeader(file, bitmap.width, bitmap.height, paddedWidth);
const uint8_t padding[4] = { };
int padLength = paddedWidth-3*bitmap.width;
for (int y = 0; y < bitmap.height; ++y) {
for (int x = 0; x < bitmap.width; ++x) {
uint8_t bgr[3] = {
(uint8_t) bitmap(x, y)[2],
(uint8_t) bitmap(x, y)[1],
(uint8_t) bitmap(x, y)[0]
};
fwrite(bgr, sizeof(uint8_t), 3, file);
}
fwrite(padding, 1, padLength, file);
}
return !fclose(file);
}
bool saveBmp(const BitmapConstRef<byte, 4> &bitmap, const char *filename) {
// RGBA not supported by the BMP format
return false;
}
bool saveBmp(const BitmapConstRef<float, 1> &bitmap, const char *filename) {
FILE *file = fopen(filename, "wb");
if (!file)
return false;
int paddedWidth;
writeBmpHeader(file, bitmap.width, bitmap.height, paddedWidth);
const uint8_t padding[4] = { };
int padLength = paddedWidth-3*bitmap.width;
for (int y = 0; y < bitmap.height; ++y) {
for (int x = 0; x < bitmap.width; ++x) {
uint8_t px = (uint8_t) pixelFloatToByte(*bitmap(x, y));
fwrite(&px, sizeof(uint8_t), 1, file);
fwrite(&px, sizeof(uint8_t), 1, file);
fwrite(&px, sizeof(uint8_t), 1, file);
}
fwrite(padding, 1, padLength, file);
}
return !fclose(file);
}
bool saveBmp(const BitmapConstRef<float, 3> &bitmap, const char *filename) {
FILE *file = fopen(filename, "wb");
if (!file)
return false;
int paddedWidth;
writeBmpHeader(file, bitmap.width, bitmap.height, paddedWidth);
const uint8_t padding[4] = { };
int padLength = paddedWidth-3*bitmap.width;
for (int y = 0; y < bitmap.height; ++y) {
for (int x = 0; x < bitmap.width; ++x) {
uint8_t bgr[3] = {
(uint8_t) pixelFloatToByte(bitmap(x, y)[2]),
(uint8_t) pixelFloatToByte(bitmap(x, y)[1]),
(uint8_t) pixelFloatToByte(bitmap(x, y)[0])
};
fwrite(bgr, sizeof(uint8_t), 3, file);
}
fwrite(padding, 1, padLength, file);
}
return !fclose(file);
}
bool saveBmp(const BitmapConstRef<float, 4> &bitmap, const char *filename) {
// RGBA not supported by the BMP format
return false;
}
}

View File

@ -1,16 +0,0 @@
#pragma once
#include "BitmapRef.hpp"
namespace msdfgen {
/// Saves the bitmap as a BMP file.
bool saveBmp(const BitmapConstRef<byte, 1> &bitmap, const char *filename);
bool saveBmp(const BitmapConstRef<byte, 3> &bitmap, const char *filename);
bool saveBmp(const BitmapConstRef<byte, 4> &bitmap, const char *filename);
bool saveBmp(const BitmapConstRef<float, 1> &bitmap, const char *filename);
bool saveBmp(const BitmapConstRef<float, 3> &bitmap, const char *filename);
bool saveBmp(const BitmapConstRef<float, 4> &bitmap, const char *filename);
}

View File

@ -1,39 +0,0 @@
#include "save-fl32.h"
#include <cstdio>
namespace msdfgen {
// Requires byte reversal for floats on big-endian platform
#ifndef __BIG_ENDIAN__
template <int N>
bool saveFl32(const BitmapConstRef<float, N> &bitmap, const char *filename) {
if (FILE *f = fopen(filename, "wb")) {
byte header[16] = { byte('F'), byte('L'), byte('3'), byte('2') };
header[4] = byte(bitmap.height);
header[5] = byte(bitmap.height>>8);
header[6] = byte(bitmap.height>>16);
header[7] = byte(bitmap.height>>24);
header[8] = byte(bitmap.width);
header[9] = byte(bitmap.width>>8);
header[10] = byte(bitmap.width>>16);
header[11] = byte(bitmap.width>>24);
header[12] = byte(N);
fwrite(header, 1, 16, f);
fwrite(bitmap.pixels, sizeof(float), N*bitmap.width*bitmap.height, f);
fclose(f);
return true;
}
return false;
}
template bool saveFl32(const BitmapConstRef<float, 1> &bitmap, const char *filename);
template bool saveFl32(const BitmapConstRef<float, 2> &bitmap, const char *filename);
template bool saveFl32(const BitmapConstRef<float, 3> &bitmap, const char *filename);
template bool saveFl32(const BitmapConstRef<float, 4> &bitmap, const char *filename);
#endif
}

View File

@ -1,12 +0,0 @@
#pragma once
#include "BitmapRef.hpp"
namespace msdfgen {
/// Saves the bitmap as an uncompressed floating-point FL32 file, which can be decoded trivially.
template <int N>
bool saveFl32(const BitmapConstRef<float, N> &bitmap, const char *filename);
}

View File

@ -1,133 +0,0 @@
#include "save-rgba.h"
#include <cstdio>
#include "pixel-conversion.hpp"
namespace msdfgen {
class RgbaFileOutput {
FILE *file;
public:
RgbaFileOutput(const char *filename, unsigned width, unsigned height) {
if ((file = fopen(filename, "wb"))) {
byte header[12] = { byte('R'), byte('G'), byte('B'), byte('A') };
header[4] = byte(width>>24);
header[5] = byte(width>>16);
header[6] = byte(width>>8);
header[7] = byte(width);
header[8] = byte(height>>24);
header[9] = byte(height>>16);
header[10] = byte(height>>8);
header[11] = byte(height);
fwrite(header, 1, 12, file);
}
}
~RgbaFileOutput() {
if (file)
fclose(file);
}
void writePixel(const byte rgba[4]) {
fwrite(rgba, 1, 4, file);
}
operator FILE *() {
return file;
}
};
bool saveRgba(const BitmapConstRef<byte, 1> &bitmap, const char *filename) {
RgbaFileOutput output(filename, bitmap.width, bitmap.height);
if (output) {
byte rgba[4] = { byte(0), byte(0), byte(0), byte(0xff) };
for (int y = bitmap.height; y--;) {
for (const byte *p = bitmap(0, y), *end = p+bitmap.width; p < end; ++p) {
rgba[0] = rgba[1] = rgba[2] = *p;
output.writePixel(rgba);
}
}
return true;
}
return false;
}
bool saveRgba(const BitmapConstRef<byte, 3> &bitmap, const char *filename) {
RgbaFileOutput output(filename, bitmap.width, bitmap.height);
if (output) {
byte rgba[4] = { byte(0), byte(0), byte(0), byte(0xff) };
for (int y = bitmap.height; y--;) {
for (const byte *p = bitmap(0, y), *end = p+3*bitmap.width; p < end; p += 3) {
rgba[0] = p[0], rgba[1] = p[1], rgba[2] = p[2];
output.writePixel(rgba);
}
}
return true;
}
return false;
}
bool saveRgba(const BitmapConstRef<byte, 4> &bitmap, const char *filename) {
RgbaFileOutput output(filename, bitmap.width, bitmap.height);
if (output) {
for (int y = bitmap.height; y--;)
fwrite(bitmap(0, y), 1, 4*bitmap.width, output);
return true;
}
return false;
}
bool saveRgba(const BitmapConstRef<float, 1> &bitmap, const char *filename) {
RgbaFileOutput output(filename, bitmap.width, bitmap.height);
if (output) {
byte rgba[4] = { byte(0), byte(0), byte(0), byte(0xff) };
for (int y = bitmap.height; y--;) {
for (const float *p = bitmap(0, y), *end = p+bitmap.width; p < end; ++p) {
rgba[0] = rgba[1] = rgba[2] = pixelFloatToByte(*p);
output.writePixel(rgba);
}
}
return true;
}
return false;
}
bool saveRgba(const BitmapConstRef<float, 3> &bitmap, const char *filename) {
RgbaFileOutput output(filename, bitmap.width, bitmap.height);
if (output) {
byte rgba[4] = { byte(0), byte(0), byte(0), byte(0xff) };
for (int y = bitmap.height; y--;) {
for (const float *p = bitmap(0, y), *end = p+3*bitmap.width; p < end; p += 3) {
rgba[0] = pixelFloatToByte(p[0]);
rgba[1] = pixelFloatToByte(p[1]);
rgba[2] = pixelFloatToByte(p[2]);
output.writePixel(rgba);
}
}
return true;
}
return false;
}
bool saveRgba(const BitmapConstRef<float, 4> &bitmap, const char *filename) {
RgbaFileOutput output(filename, bitmap.width, bitmap.height);
if (output) {
byte rgba[4];
for (int y = bitmap.height; y--;) {
for (const float *p = bitmap(0, y), *end = p+4*bitmap.width; p < end; p += 4) {
rgba[0] = pixelFloatToByte(p[0]);
rgba[1] = pixelFloatToByte(p[1]);
rgba[2] = pixelFloatToByte(p[2]);
rgba[3] = pixelFloatToByte(p[3]);
output.writePixel(rgba);
}
}
return true;
}
return false;
}
}

View File

@ -1,16 +0,0 @@
#pragma once
#include "BitmapRef.hpp"
namespace msdfgen {
/// Saves the bitmap as a simple RGBA file, which can be decoded trivially.
bool saveRgba(const BitmapConstRef<byte, 1> &bitmap, const char *filename);
bool saveRgba(const BitmapConstRef<byte, 3> &bitmap, const char *filename);
bool saveRgba(const BitmapConstRef<byte, 4> &bitmap, const char *filename);
bool saveRgba(const BitmapConstRef<float, 1> &bitmap, const char *filename);
bool saveRgba(const BitmapConstRef<float, 3> &bitmap, const char *filename);
bool saveRgba(const BitmapConstRef<float, 4> &bitmap, const char *filename);
}

View File

@ -1,194 +0,0 @@
#ifndef _CRT_SECURE_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS
#endif
#include "save-tiff.h"
#include <cstdio>
#ifdef MSDFGEN_USE_CPP11
#include <cstdint>
#else
namespace msdfgen {
typedef int int32_t;
typedef unsigned uint32_t;
typedef unsigned short uint16_t;
typedef unsigned char uint8_t;
}
#endif
namespace msdfgen {
template <typename T>
static bool writeValue(FILE *file, T value) {
return fwrite(&value, sizeof(T), 1, file) == 1;
}
template <typename T>
static void writeValueRepeated(FILE *file, T value, int times) {
for (int i = 0; i < times; ++i)
writeValue(file, value);
}
static bool writeTiffHeader(FILE *file, int width, int height, int channels) {
#ifdef __BIG_ENDIAN__
writeValue<uint16_t>(file, 0x4d4du);
#else
writeValue<uint16_t>(file, 0x4949u);
#endif
writeValue<uint16_t>(file, 42);
writeValue<uint32_t>(file, 0x0008u); // Offset of first IFD
// Offset = 0x0008
writeValue<uint16_t>(file, 15); // Number of IFD entries
// ImageWidth
writeValue<uint16_t>(file, 0x0100u);
writeValue<uint16_t>(file, 0x0004u);
writeValue<uint32_t>(file, 1);
writeValue<int32_t>(file, width);
// ImageLength
writeValue<uint16_t>(file, 0x0101u);
writeValue<uint16_t>(file, 0x0004u);
writeValue<uint32_t>(file, 1);
writeValue<int32_t>(file, height);
// BitsPerSample
writeValue<uint16_t>(file, 0x0102u);
writeValue<uint16_t>(file, 0x0003u);
writeValue<uint32_t>(file, channels);
if (channels > 1)
writeValue<uint32_t>(file, 0x00c2u); // Offset of 32, 32, ...
else {
writeValue<uint16_t>(file, 32);
writeValue<uint16_t>(file, 0);
}
// Compression
writeValue<uint16_t>(file, 0x0103u);
writeValue<uint16_t>(file, 0x0003u);
writeValue<uint32_t>(file, 1);
writeValue<uint16_t>(file, 1);
writeValue<uint16_t>(file, 0);
// PhotometricInterpretation
writeValue<uint16_t>(file, 0x0106u);
writeValue<uint16_t>(file, 0x0003u);
writeValue<uint32_t>(file, 1);
writeValue<uint16_t>(file, channels >= 3 ? 2 : 1);
writeValue<uint16_t>(file, 0);
// StripOffsets
writeValue<uint16_t>(file, 0x0111u);
writeValue<uint16_t>(file, 0x0004u);
writeValue<uint32_t>(file, 1);
writeValue<uint32_t>(file, 0x00d2u+(channels > 1)*channels*12); // Offset of pixel data
// SamplesPerPixel
writeValue<uint16_t>(file, 0x0115u);
writeValue<uint16_t>(file, 0x0003u);
writeValue<uint32_t>(file, 1);
writeValue<uint16_t>(file, channels);
writeValue<uint16_t>(file, 0);
// RowsPerStrip
writeValue<uint16_t>(file, 0x0116u);
writeValue<uint16_t>(file, 0x0004u);
writeValue<uint32_t>(file, 1);
writeValue<int32_t>(file, height);
// StripByteCounts
writeValue<uint16_t>(file, 0x0117u);
writeValue<uint16_t>(file, 0x0004u);
writeValue<uint32_t>(file, 1);
writeValue<int32_t>(file, sizeof(float)*channels*width*height);
// XResolution
writeValue<uint16_t>(file, 0x011au);
writeValue<uint16_t>(file, 0x0005u);
writeValue<uint32_t>(file, 1);
writeValue<uint32_t>(file, 0x00c2u+(channels > 1)*channels*2); // Offset of 300, 1
// YResolution
writeValue<uint16_t>(file, 0x011bu);
writeValue<uint16_t>(file, 0x0005u);
writeValue<uint32_t>(file, 1);
writeValue<uint32_t>(file, 0x00cau+(channels > 1)*channels*2); // Offset of 300, 1
// ResolutionUnit
writeValue<uint16_t>(file, 0x0128u);
writeValue<uint16_t>(file, 0x0003u);
writeValue<uint32_t>(file, 1);
writeValue<uint16_t>(file, 2);
writeValue<uint16_t>(file, 0);
// SampleFormat
writeValue<uint16_t>(file, 0x0153u);
writeValue<uint16_t>(file, 0x0003u);
writeValue<uint32_t>(file, channels);
if (channels > 1)
writeValue<uint32_t>(file, 0x00d2u+channels*2); // Offset of 3, 3, ...
else {
writeValue<uint16_t>(file, 3);
writeValue<uint16_t>(file, 0);
}
// SMinSampleValue
writeValue<uint16_t>(file, 0x0154u);
writeValue<uint16_t>(file, 0x000bu);
writeValue<uint32_t>(file, channels);
if (channels > 1)
writeValue<uint32_t>(file, 0x00d2u+channels*4); // Offset of 0.f, 0.f, ...
else
writeValue<float>(file, 0.f);
// SMaxSampleValue
writeValue<uint16_t>(file, 0x0155u);
writeValue<uint16_t>(file, 0x000bu);
writeValue<uint32_t>(file, channels);
if (channels > 1)
writeValue<uint32_t>(file, 0x00d2u+channels*8); // Offset of 1.f, 1.f, ...
else
writeValue<float>(file, 1.f);
// Offset = 0x00be
writeValue<uint32_t>(file, 0);
if (channels > 1) {
// 0x00c2 BitsPerSample data
writeValueRepeated<uint16_t>(file, 32, channels);
// 0x00c2 + 2*N XResolution data
writeValue<uint32_t>(file, 300);
writeValue<uint32_t>(file, 1);
// 0x00ca + 2*N YResolution data
writeValue<uint32_t>(file, 300);
writeValue<uint32_t>(file, 1);
// 0x00d2 + 2*N SampleFormat data
writeValueRepeated<uint16_t>(file, 3, channels);
// 0x00d2 + 4*N SMinSampleValue data
writeValueRepeated<float>(file, 0.f, channels);
// 0x00d2 + 8*N SMaxSampleValue data
writeValueRepeated<float>(file, 1.f, channels);
// Offset = 0x00d2 + 12*N
} else {
// 0x00c2 XResolution data
writeValue<uint32_t>(file, 300);
writeValue<uint32_t>(file, 1);
// 0x00ca YResolution data
writeValue<uint32_t>(file, 300);
writeValue<uint32_t>(file, 1);
// Offset = 0x00d2
}
return true;
}
template <int N>
bool saveTiffFloat(const BitmapConstRef<float, N> &bitmap, const char *filename) {
FILE *file = fopen(filename, "wb");
if (!file)
return false;
writeTiffHeader(file, bitmap.width, bitmap.height, N);
for (int y = bitmap.height-1; y >= 0; --y)
fwrite(bitmap(0, y), sizeof(float), N*bitmap.width, file);
return !fclose(file);
}
bool saveTiff(const BitmapConstRef<float, 1> &bitmap, const char *filename) {
return saveTiffFloat(bitmap, filename);
}
bool saveTiff(const BitmapConstRef<float, 3> &bitmap, const char *filename) {
return saveTiffFloat(bitmap, filename);
}
bool saveTiff(const BitmapConstRef<float, 4> &bitmap, const char *filename) {
return saveTiffFloat(bitmap, filename);
}
}

View File

@ -1,13 +0,0 @@
#pragma once
#include "BitmapRef.hpp"
namespace msdfgen {
/// Saves the bitmap as an uncompressed floating-point TIFF file.
bool saveTiff(const BitmapConstRef<float, 1> &bitmap, const char *filename);
bool saveTiff(const BitmapConstRef<float, 3> &bitmap, const char *filename);
bool saveTiff(const BitmapConstRef<float, 4> &bitmap, const char *filename);
}

View File

@ -6,11 +6,11 @@
namespace msdfgen {
void scanlineSDF(Scanline &line, const BitmapConstRef<float, 1> &sdf, const Projection &projection, double y, bool inverseYAxis) {
void scanlineSDF(Scanline &line, const BitmapConstSection<float, 1> &sdf, const Projection &projection, double y, YAxisOrientation yAxisOrientation) {
if (!(sdf.width > 0 && sdf.height > 0))
return line.setIntersections(std::vector<Scanline::Intersection>());
double pixelY = clamp(projection.projectY(y)-.5, double(sdf.height-1));
if (inverseYAxis)
if (yAxisOrientation == Y_DOWNWARD)
pixelY = sdf.height-1-pixelY;
int b = (int) floor(pixelY);
int t = b+1;
@ -46,11 +46,11 @@ void scanlineSDF(Scanline &line, const BitmapConstRef<float, 1> &sdf, const Proj
}
template <int N>
void scanlineMSDF(Scanline &line, const BitmapConstRef<float, N> &sdf, const Projection &projection, double y, bool inverseYAxis) {
void scanlineMSDF(Scanline &line, const BitmapConstSection<float, N> &sdf, const Projection &projection, double y, YAxisOrientation yAxisOrientation) {
if (!(sdf.width > 0 && sdf.height > 0))
return line.setIntersections(std::vector<Scanline::Intersection>());
double pixelY = clamp(projection.projectY(y)-.5, double(sdf.height-1));
if (inverseYAxis)
if (yAxisOrientation == Y_DOWNWARD)
pixelY = sdf.height-1-pixelY;
int b = (int) floor(pixelY);
int t = b+1;
@ -124,15 +124,15 @@ void scanlineMSDF(Scanline &line, const BitmapConstRef<float, N> &sdf, const Pro
#endif
}
void scanlineSDF(Scanline &line, const BitmapConstRef<float, 3> &sdf, const Projection &projection, double y, bool inverseYAxis) {
scanlineMSDF(line, sdf, projection, y, inverseYAxis);
void scanlineSDF(Scanline &line, const BitmapConstSection<float, 3> &sdf, const Projection &projection, double y, YAxisOrientation yAxisOrientation) {
scanlineMSDF(line, sdf, projection, y, yAxisOrientation);
}
void scanlineSDF(Scanline &line, const BitmapConstRef<float, 4> &sdf, const Projection &projection, double y, bool inverseYAxis) {
scanlineMSDF(line, sdf, projection, y, inverseYAxis);
void scanlineSDF(Scanline &line, const BitmapConstSection<float, 4> &sdf, const Projection &projection, double y, YAxisOrientation yAxisOrientation) {
scanlineMSDF(line, sdf, projection, y, yAxisOrientation);
}
template <int N>
double estimateSDFErrorInner(const BitmapConstRef<float, N> &sdf, const Shape &shape, const Projection &projection, int scanlinesPerRow, FillRule fillRule) {
double estimateSDFErrorInner(const BitmapConstSection<float, N> &sdf, const Shape &shape, const Projection &projection, int scanlinesPerRow, FillRule fillRule) {
if (sdf.width <= 1 || sdf.height <= 1 || scanlinesPerRow < 1)
return 0;
double subRowSize = 1./scanlinesPerRow;
@ -146,46 +146,58 @@ double estimateSDFErrorInner(const BitmapConstRef<float, N> &sdf, const Shape &s
double bt = (subRow+.5)*subRowSize;
double y = projection.unprojectY(row+bt+.5);
shape.scanline(refScanline, y);
scanlineSDF(sdfScanline, sdf, projection, y, shape.inverseYAxis);
scanlineSDF(sdfScanline, sdf, projection, y, shape.getYAxisOrientation());
error += 1-overlapFactor*Scanline::overlap(refScanline, sdfScanline, xFrom, xTo, fillRule);
}
}
return error/((sdf.height-1)*scanlinesPerRow);
}
double estimateSDFError(const BitmapConstRef<float, 1> &sdf, const Shape &shape, const Projection &projection, int scanlinesPerRow, FillRule fillRule) {
double estimateSDFError(const BitmapConstSection<float, 1> &sdf, const Shape &shape, const Projection &projection, int scanlinesPerRow, FillRule fillRule) {
return estimateSDFErrorInner(sdf, shape, projection, scanlinesPerRow, fillRule);
}
double estimateSDFError(const BitmapConstRef<float, 3> &sdf, const Shape &shape, const Projection &projection, int scanlinesPerRow, FillRule fillRule) {
double estimateSDFError(const BitmapConstSection<float, 3> &sdf, const Shape &shape, const Projection &projection, int scanlinesPerRow, FillRule fillRule) {
return estimateSDFErrorInner(sdf, shape, projection, scanlinesPerRow, fillRule);
}
double estimateSDFError(const BitmapConstRef<float, 4> &sdf, const Shape &shape, const Projection &projection, int scanlinesPerRow, FillRule fillRule) {
double estimateSDFError(const BitmapConstSection<float, 4> &sdf, const Shape &shape, const Projection &projection, int scanlinesPerRow, FillRule fillRule) {
return estimateSDFErrorInner(sdf, shape, projection, scanlinesPerRow, fillRule);
}
// Legacy API
void scanlineSDF(Scanline &line, const BitmapConstRef<float, 1> &sdf, const Vector2 &scale, const Vector2 &translate, bool inverseYAxis, double y) {
void scanlineSDF(Scanline &line, const BitmapConstSection<float, 1> &sdf, const Projection &projection, double y, bool inverseYAxis) {
scanlineSDF(line, sdf, projection, y, inverseYAxis ? MSDFGEN_Y_AXIS_NONDEFAULT_ORIENTATION : MSDFGEN_Y_AXIS_DEFAULT_ORIENTATION);
}
void scanlineSDF(Scanline &line, const BitmapConstSection<float, 3> &sdf, const Projection &projection, double y, bool inverseYAxis) {
scanlineSDF(line, sdf, projection, y, inverseYAxis ? MSDFGEN_Y_AXIS_NONDEFAULT_ORIENTATION : MSDFGEN_Y_AXIS_DEFAULT_ORIENTATION);
}
void scanlineSDF(Scanline &line, const BitmapConstSection<float, 4> &sdf, const Projection &projection, double y, bool inverseYAxis) {
scanlineSDF(line, sdf, projection, y, inverseYAxis ? MSDFGEN_Y_AXIS_NONDEFAULT_ORIENTATION : MSDFGEN_Y_AXIS_DEFAULT_ORIENTATION);
}
void scanlineSDF(Scanline &line, const BitmapConstSection<float, 1> &sdf, const Vector2 &scale, const Vector2 &translate, bool inverseYAxis, double y) {
scanlineSDF(line, sdf, Projection(scale, translate), y, inverseYAxis);
}
void scanlineSDF(Scanline &line, const BitmapConstRef<float, 3> &sdf, const Vector2 &scale, const Vector2 &translate, bool inverseYAxis, double y) {
void scanlineSDF(Scanline &line, const BitmapConstSection<float, 3> &sdf, const Vector2 &scale, const Vector2 &translate, bool inverseYAxis, double y) {
scanlineSDF(line, sdf, Projection(scale, translate), y, inverseYAxis);
}
void scanlineSDF(Scanline &line, const BitmapConstRef<float, 4> &sdf, const Vector2 &scale, const Vector2 &translate, bool inverseYAxis, double y) {
void scanlineSDF(Scanline &line, const BitmapConstSection<float, 4> &sdf, const Vector2 &scale, const Vector2 &translate, bool inverseYAxis, double y) {
scanlineSDF(line, sdf, Projection(scale, translate), y, inverseYAxis);
}
double estimateSDFError(const BitmapConstRef<float, 1> &sdf, const Shape &shape, const Vector2 &scale, const Vector2 &translate, int scanlinesPerRow, FillRule fillRule) {
double estimateSDFError(const BitmapConstSection<float, 1> &sdf, const Shape &shape, const Vector2 &scale, const Vector2 &translate, int scanlinesPerRow, FillRule fillRule) {
return estimateSDFError(sdf, shape, Projection(scale, translate), scanlinesPerRow, fillRule);
}
double estimateSDFError(const BitmapConstRef<float, 3> &sdf, const Shape &shape, const Vector2 &scale, const Vector2 &translate, int scanlinesPerRow, FillRule fillRule) {
double estimateSDFError(const BitmapConstSection<float, 3> &sdf, const Shape &shape, const Vector2 &scale, const Vector2 &translate, int scanlinesPerRow, FillRule fillRule) {
return estimateSDFError(sdf, shape, Projection(scale, translate), scanlinesPerRow, fillRule);
}
double estimateSDFError(const BitmapConstRef<float, 4> &sdf, const Shape &shape, const Vector2 &scale, const Vector2 &translate, int scanlinesPerRow, FillRule fillRule) {
double estimateSDFError(const BitmapConstSection<float, 4> &sdf, const Shape &shape, const Vector2 &scale, const Vector2 &translate, int scanlinesPerRow, FillRule fillRule) {
return estimateSDFError(sdf, shape, Projection(scale, translate), scanlinesPerRow, fillRule);
}

View File

@ -10,21 +10,24 @@
namespace msdfgen {
/// Analytically constructs a scanline at y evaluating fill by linear interpolation of the SDF.
void scanlineSDF(Scanline &line, const BitmapConstRef<float, 1> &sdf, const Projection &projection, double y, bool inverseYAxis = false);
void scanlineSDF(Scanline &line, const BitmapConstRef<float, 3> &sdf, const Projection &projection, double y, bool inverseYAxis = false);
void scanlineSDF(Scanline &line, const BitmapConstRef<float, 4> &sdf, const Projection &projection, double y, bool inverseYAxis = false);
void scanlineSDF(Scanline &line, const BitmapConstSection<float, 1> &sdf, const Projection &projection, double y, YAxisOrientation yAxisOrientation = MSDFGEN_Y_AXIS_DEFAULT_ORIENTATION);
void scanlineSDF(Scanline &line, const BitmapConstSection<float, 3> &sdf, const Projection &projection, double y, YAxisOrientation yAxisOrientation = MSDFGEN_Y_AXIS_DEFAULT_ORIENTATION);
void scanlineSDF(Scanline &line, const BitmapConstSection<float, 4> &sdf, const Projection &projection, double y, YAxisOrientation yAxisOrientation = MSDFGEN_Y_AXIS_DEFAULT_ORIENTATION);
/// Estimates the portion of the area that will be filled incorrectly when rendering using the SDF.
double estimateSDFError(const BitmapConstRef<float, 1> &sdf, const Shape &shape, const Projection &projection, int scanlinesPerRow, FillRule fillRule = FILL_NONZERO);
double estimateSDFError(const BitmapConstRef<float, 3> &sdf, const Shape &shape, const Projection &projection, int scanlinesPerRow, FillRule fillRule = FILL_NONZERO);
double estimateSDFError(const BitmapConstRef<float, 4> &sdf, const Shape &shape, const Projection &projection, int scanlinesPerRow, FillRule fillRule = FILL_NONZERO);
double estimateSDFError(const BitmapConstSection<float, 1> &sdf, const Shape &shape, const Projection &projection, int scanlinesPerRow, FillRule fillRule = FILL_NONZERO);
double estimateSDFError(const BitmapConstSection<float, 3> &sdf, const Shape &shape, const Projection &projection, int scanlinesPerRow, FillRule fillRule = FILL_NONZERO);
double estimateSDFError(const BitmapConstSection<float, 4> &sdf, const Shape &shape, const Projection &projection, int scanlinesPerRow, FillRule fillRule = FILL_NONZERO);
// Old version of the function API's kept for backwards compatibility
void scanlineSDF(Scanline &line, const BitmapConstRef<float, 1> &sdf, const Vector2 &scale, const Vector2 &translate, bool inverseYAxis, double y);
void scanlineSDF(Scanline &line, const BitmapConstRef<float, 3> &sdf, const Vector2 &scale, const Vector2 &translate, bool inverseYAxis, double y);
void scanlineSDF(Scanline &line, const BitmapConstRef<float, 4> &sdf, const Vector2 &scale, const Vector2 &translate, bool inverseYAxis, double y);
double estimateSDFError(const BitmapConstRef<float, 1> &sdf, const Shape &shape, const Vector2 &scale, const Vector2 &translate, int scanlinesPerRow, FillRule fillRule = FILL_NONZERO);
double estimateSDFError(const BitmapConstRef<float, 3> &sdf, const Shape &shape, const Vector2 &scale, const Vector2 &translate, int scanlinesPerRow, FillRule fillRule = FILL_NONZERO);
double estimateSDFError(const BitmapConstRef<float, 4> &sdf, const Shape &shape, const Vector2 &scale, const Vector2 &translate, int scanlinesPerRow, FillRule fillRule = FILL_NONZERO);
void scanlineSDF(Scanline &line, const BitmapConstSection<float, 1> &sdf, const Projection &projection, double y, bool inverseYAxis);
void scanlineSDF(Scanline &line, const BitmapConstSection<float, 3> &sdf, const Projection &projection, double y, bool inverseYAxis);
void scanlineSDF(Scanline &line, const BitmapConstSection<float, 4> &sdf, const Projection &projection, double y, bool inverseYAxis);
void scanlineSDF(Scanline &line, const BitmapConstSection<float, 1> &sdf, const Vector2 &scale, const Vector2 &translate, bool inverseYAxis, double y);
void scanlineSDF(Scanline &line, const BitmapConstSection<float, 3> &sdf, const Vector2 &scale, const Vector2 &translate, bool inverseYAxis, double y);
void scanlineSDF(Scanline &line, const BitmapConstSection<float, 4> &sdf, const Vector2 &scale, const Vector2 &translate, bool inverseYAxis, double y);
double estimateSDFError(const BitmapConstSection<float, 1> &sdf, const Shape &shape, const Vector2 &scale, const Vector2 &translate, int scanlinesPerRow, FillRule fillRule = FILL_NONZERO);
double estimateSDFError(const BitmapConstSection<float, 3> &sdf, const Shape &shape, const Vector2 &scale, const Vector2 &translate, int scanlinesPerRow, FillRule fillRule = FILL_NONZERO);
double estimateSDFError(const BitmapConstSection<float, 4> &sdf, const Shape &shape, const Vector2 &scale, const Vector2 &translate, int scanlinesPerRow, FillRule fillRule = FILL_NONZERO);
}

View File

@ -51,6 +51,17 @@ int readCoordS(const char **input, Point2 &coord) {
return 2;
}
bool matchStringS(const char **input, const char *str) {
const char *cur = *input;
while (*cur && *str && *cur == *str)
++cur, ++str;
if (!*str) {
*input = cur;
return true;
}
return false;
}
static bool writeCoord(FILE *output, Point2 coord) {
fprintf(output, "%.12g, %.12g", coord.x, coord.y);
return true;
@ -176,7 +187,7 @@ static bool readContour(T *input, Contour &output, const Point2 *first, int term
bool readShapeDescription(FILE *input, Shape &output, bool *colorsSpecified) {
bool locColorsSpec = false;
output.contours.clear();
output.inverseYAxis = false;
output.setYAxisOrientation(MSDFGEN_Y_AXIS_DEFAULT_ORIENTATION);
Point2 p;
int result = readCoordF(input, p);
if (result == 2) {
@ -187,9 +198,21 @@ bool readShapeDescription(FILE *input, Shape &output, bool *colorsSpecified) {
int c = readCharF(input);
if (c == '@') {
char after = '\0';
if (fscanf(input, "invert-y%c", &after) != 1)
if (fscanf(input, "y-%c", &after) == 1 && (after == 'u' || after == 'd')) {
switch (after) {
case 'u':
output.setYAxisOrientation(Y_UPWARD);
break;
case 'd':
output.setYAxisOrientation(Y_DOWNWARD);
break;
}
if (fscanf(input, after == 'u' ? "p%c" : "own%c", &after) != 1)
return feof(input) != 0;
} else if (fscanf(input, "invert-y%c", &after) == 1)
output.inverseYAxis = true;
else
return feof(input) != 0;
output.inverseYAxis = true;
c = after;
if (c == ' ' || c == '\t' || c == '\r' || c == '\n')
c = readCharF(input);
@ -206,7 +229,7 @@ bool readShapeDescription(FILE *input, Shape &output, bool *colorsSpecified) {
bool readShapeDescription(const char *input, Shape &output, bool *colorsSpecified) {
bool locColorsSpec = false;
output.contours.clear();
output.inverseYAxis = false;
output.setYAxisOrientation(MSDFGEN_Y_AXIS_DEFAULT_ORIENTATION);
Point2 p;
int result = readCoordS(&input, p);
if (result == 2) {
@ -216,11 +239,14 @@ bool readShapeDescription(const char *input, Shape &output, bool *colorsSpecifie
else {
int c = readCharS(&input);
if (c == '@') {
for (int i = 0; i < (int) sizeof("invert-y")-1; ++i)
if (input[i] != "invert-y"[i])
return false;
output.inverseYAxis = true;
input += sizeof("invert-y")-1;
if (matchStringS(&input, "y-down"))
output.setYAxisOrientation(Y_DOWNWARD);
else if (matchStringS(&input, "y-up"))
output.setYAxisOrientation(Y_UPWARD);
else if (matchStringS(&input, "invert-y"))
output.inverseYAxis = true;
else
return false;
c = readCharS(&input);
}
for (; c == '{'; c = readCharS(&input))
@ -244,8 +270,14 @@ bool writeShapeDescription(FILE *output, const Shape &shape) {
if (!shape.validate())
return false;
bool writeColors = isColored(shape);
if (shape.inverseYAxis)
fprintf(output, "@invert-y\n");
switch (shape.getYAxisOrientation()) {
case Y_UPWARD:
fprintf(output, "@y-up\n");
break;
case Y_DOWNWARD:
fprintf(output, "@y-down\n");
break;
}
for (std::vector<Contour>::const_iterator contour = shape.contours.begin(); contour != shape.contours.end(); ++contour) {
fprintf(output, "{\n");
if (!contour->edges.empty()) {

View File

@ -34,45 +34,40 @@
#include "core/render-sdf.h"
#include "core/rasterization.h"
#include "core/sdf-error-estimation.h"
#include "core/save-bmp.h"
#include "core/save-tiff.h"
#include "core/save-rgba.h"
#include "core/save-fl32.h"
#include "core/shape-description.h"
#include "core/export-svg.h"
namespace msdfgen {
/// Generates a conventional single-channel signed distance field.
void generateSDF(const BitmapRef<float, 1> &output, const Shape &shape, const SDFTransformation &transformation, const GeneratorConfig &config = GeneratorConfig());
void generateSDF(const BitmapSection<float, 1> &output, const Shape &shape, const SDFTransformation &transformation, const GeneratorConfig &config = GeneratorConfig());
/// Generates a single-channel signed perpendicular distance field.
void generatePSDF(const BitmapRef<float, 1> &output, const Shape &shape, const SDFTransformation &transformation, const GeneratorConfig &config = GeneratorConfig());
void generatePSDF(const BitmapSection<float, 1> &output, const Shape &shape, const SDFTransformation &transformation, const GeneratorConfig &config = GeneratorConfig());
/// Generates a multi-channel signed distance field. Edge colors must be assigned first! (See edgeColoringSimple)
void generateMSDF(const BitmapRef<float, 3> &output, const Shape &shape, const SDFTransformation &transformation, const MSDFGeneratorConfig &config = MSDFGeneratorConfig());
void generateMSDF(const BitmapSection<float, 3> &output, const Shape &shape, const SDFTransformation &transformation, const MSDFGeneratorConfig &config = MSDFGeneratorConfig());
/// Generates a multi-channel signed distance field with true distance in the alpha channel. Edge colors must be assigned first.
void generateMTSDF(const BitmapRef<float, 4> &output, const Shape &shape, const SDFTransformation &transformation, const MSDFGeneratorConfig &config = MSDFGeneratorConfig());
void generateMTSDF(const BitmapSection<float, 4> &output, const Shape &shape, const SDFTransformation &transformation, const MSDFGeneratorConfig &config = MSDFGeneratorConfig());
// Old version of the function API's kept for backwards compatibility
void generateSDF(const BitmapRef<float, 1> &output, const Shape &shape, const Projection &projection, Range range, const GeneratorConfig &config = GeneratorConfig());
void generatePSDF(const BitmapRef<float, 1> &output, const Shape &shape, const Projection &projection, Range range, const GeneratorConfig &config = GeneratorConfig());
void generatePseudoSDF(const BitmapRef<float, 1> &output, const Shape &shape, const Projection &projection, Range range, const GeneratorConfig &config = GeneratorConfig());
void generateMSDF(const BitmapRef<float, 3> &output, const Shape &shape, const Projection &projection, Range range, const MSDFGeneratorConfig &config = MSDFGeneratorConfig());
void generateMTSDF(const BitmapRef<float, 4> &output, const Shape &shape, const Projection &projection, Range range, const MSDFGeneratorConfig &config = MSDFGeneratorConfig());
void generateSDF(const BitmapSection<float, 1> &output, const Shape &shape, const Projection &projection, Range range, const GeneratorConfig &config = GeneratorConfig());
void generatePSDF(const BitmapSection<float, 1> &output, const Shape &shape, const Projection &projection, Range range, const GeneratorConfig &config = GeneratorConfig());
void generatePseudoSDF(const BitmapSection<float, 1> &output, const Shape &shape, const Projection &projection, Range range, const GeneratorConfig &config = GeneratorConfig());
void generateMSDF(const BitmapSection<float, 3> &output, const Shape &shape, const Projection &projection, Range range, const MSDFGeneratorConfig &config = MSDFGeneratorConfig());
void generateMTSDF(const BitmapSection<float, 4> &output, const Shape &shape, const Projection &projection, Range range, const MSDFGeneratorConfig &config = MSDFGeneratorConfig());
void generateSDF(const BitmapRef<float, 1> &output, const Shape &shape, Range range, const Vector2 &scale, const Vector2 &translate, bool overlapSupport = true);
void generatePSDF(const BitmapRef<float, 1> &output, const Shape &shape, Range range, const Vector2 &scale, const Vector2 &translate, bool overlapSupport = true);
void generatePseudoSDF(const BitmapRef<float, 1> &output, const Shape &shape, Range range, const Vector2 &scale, const Vector2 &translate, bool overlapSupport = true);
void generateMSDF(const BitmapRef<float, 3> &output, const Shape &shape, Range range, const Vector2 &scale, const Vector2 &translate, const ErrorCorrectionConfig &errorCorrectionConfig = ErrorCorrectionConfig(), bool overlapSupport = true);
void generateMTSDF(const BitmapRef<float, 4> &output, const Shape &shape, Range range, const Vector2 &scale, const Vector2 &translate, const ErrorCorrectionConfig &errorCorrectionConfig = ErrorCorrectionConfig(), bool overlapSupport = true);
void generateSDF(const BitmapSection<float, 1> &output, const Shape &shape, Range range, const Vector2 &scale, const Vector2 &translate, bool overlapSupport = true);
void generatePSDF(const BitmapSection<float, 1> &output, const Shape &shape, Range range, const Vector2 &scale, const Vector2 &translate, bool overlapSupport = true);
void generatePseudoSDF(const BitmapSection<float, 1> &output, const Shape &shape, Range range, const Vector2 &scale, const Vector2 &translate, bool overlapSupport = true);
void generateMSDF(const BitmapSection<float, 3> &output, const Shape &shape, Range range, const Vector2 &scale, const Vector2 &translate, const ErrorCorrectionConfig &errorCorrectionConfig = ErrorCorrectionConfig(), bool overlapSupport = true);
void generateMTSDF(const BitmapSection<float, 4> &output, const Shape &shape, Range range, const Vector2 &scale, const Vector2 &translate, const ErrorCorrectionConfig &errorCorrectionConfig = ErrorCorrectionConfig(), bool overlapSupport = true);
// Original simpler versions of the previous functions, which work well under normal circumstances, but cannot deal with overlapping contours.
void generateSDF_legacy(const BitmapRef<float, 1> &output, const Shape &shape, Range range, const Vector2 &scale, const Vector2 &translate);
void generatePSDF_legacy(const BitmapRef<float, 1> &output, const Shape &shape, Range range, const Vector2 &scale, const Vector2 &translate);
void generatePseudoSDF_legacy(const BitmapRef<float, 1> &output, const Shape &shape, Range range, const Vector2 &scale, const Vector2 &translate);
void generateMSDF_legacy(const BitmapRef<float, 3> &output, const Shape &shape, Range range, const Vector2 &scale, const Vector2 &translate, ErrorCorrectionConfig errorCorrectionConfig = ErrorCorrectionConfig());
void generateMTSDF_legacy(const BitmapRef<float, 4> &output, const Shape &shape, Range range, const Vector2 &scale, const Vector2 &translate, ErrorCorrectionConfig errorCorrectionConfig = ErrorCorrectionConfig());
void generateSDF_legacy(BitmapSection<float, 1> output, const Shape &shape, Range range, const Vector2 &scale, const Vector2 &translate);
void generatePSDF_legacy(BitmapSection<float, 1> output, const Shape &shape, Range range, const Vector2 &scale, const Vector2 &translate);
void generatePseudoSDF_legacy(BitmapSection<float, 1> output, const Shape &shape, Range range, const Vector2 &scale, const Vector2 &translate);
void generateMSDF_legacy(BitmapSection<float, 3> output, const Shape &shape, Range range, const Vector2 &scale, const Vector2 &translate, ErrorCorrectionConfig errorCorrectionConfig = ErrorCorrectionConfig());
void generateMTSDF_legacy(BitmapSection<float, 4> output, const Shape &shape, Range range, const Vector2 &scale, const Vector2 &translate, ErrorCorrectionConfig errorCorrectionConfig = ErrorCorrectionConfig());
}

View File

@ -0,0 +1,17 @@
diff --git a/thirdparty/msdfgen/msdfgen.h b/thirdparty/msdfgen/msdfgen.h
index bf0ae2badc..d2c9a2fc8f 100644
--- a/thirdparty/msdfgen/msdfgen.h
+++ b/thirdparty/msdfgen/msdfgen.h
@@ -34,12 +34,7 @@
#include "core/render-sdf.h"
#include "core/rasterization.h"
#include "core/sdf-error-estimation.h"
-#include "core/save-bmp.h"
-#include "core/save-tiff.h"
-#include "core/save-rgba.h"
-#include "core/save-fl32.h"
#include "core/shape-description.h"
-#include "core/export-svg.h"
namespace msdfgen {