1
0
mirror of https://github.com/godotengine/godot.git synced 2025-12-31 18:41:20 +00:00

msdfgen: Update to 1.13

Remove unused `export-svg` and `save-*` files.
This commit is contained in:
Rémi Verschelde
2025-12-12 22:39:53 +01:00
parent 08e6cd181f
commit 76dda1f2c8
43 changed files with 807 additions and 1041 deletions

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,6 +1,7 @@
#pragma once #pragma once
#include "YAxisOrientation.h"
#include "BitmapRef.hpp" #include "BitmapRef.hpp"
namespace msdfgen { namespace msdfgen {
@@ -11,14 +12,16 @@ class Bitmap {
public: public:
Bitmap(); Bitmap();
Bitmap(int width, int height); Bitmap(int width, int height, YAxisOrientation yOrientation = MSDFGEN_Y_AXIS_DEFAULT_ORIENTATION);
Bitmap(const BitmapConstRef<T, N> &orig); explicit Bitmap(const BitmapConstRef<T, N> &orig);
explicit Bitmap(const BitmapConstSection<T, N> &orig);
Bitmap(const Bitmap<T, N> &orig); Bitmap(const Bitmap<T, N> &orig);
#ifdef MSDFGEN_USE_CPP11 #ifdef MSDFGEN_USE_CPP11
Bitmap(Bitmap<T, N> &&orig); Bitmap(Bitmap<T, N> &&orig);
#endif #endif
~Bitmap(); ~Bitmap();
Bitmap<T, N> &operator=(const BitmapConstRef<T, N> &orig); 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); Bitmap<T, N> &operator=(const Bitmap<T, N> &orig);
#ifdef MSDFGEN_USE_CPP11 #ifdef MSDFGEN_USE_CPP11
Bitmap<T, N> &operator=(Bitmap<T, N> &&orig); Bitmap<T, N> &operator=(Bitmap<T, N> &&orig);
@@ -38,10 +41,17 @@ public:
#endif #endif
operator BitmapRef<T, N>(); operator BitmapRef<T, N>();
operator BitmapConstRef<T, N>() const; 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: private:
T *pixels; T *pixels;
int w, h; int w, h;
YAxisOrientation yOrientation;
}; };

View File

@@ -7,28 +7,41 @@
namespace msdfgen { namespace msdfgen {
template <typename T, int N> 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> 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]; pixels = new T[N*w*h];
} }
template <typename T, int N> 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]; pixels = new T[N*w*h];
memcpy(pixels, orig.pixels, sizeof(T)*N*w*h); memcpy(pixels, orig.pixels, sizeof(T)*N*w*h);
} }
template <typename T, int N> 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]; pixels = new T[N*w*h];
memcpy(pixels, orig.pixels, sizeof(T)*N*w*h); memcpy(pixels, orig.pixels, sizeof(T)*N*w*h);
} }
#ifdef MSDFGEN_USE_CPP11 #ifdef MSDFGEN_USE_CPP11
template <typename T, int N> 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.pixels = NULL;
orig.w = 0, orig.h = 0; 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> template <typename T, int N>
Bitmap<T, N>::~Bitmap() { Bitmap<T, N>::~Bitmap() {
delete [] pixels; delete[] pixels;
} }
template <typename T, int N> template <typename T, int N>
Bitmap<T, N> &Bitmap<T, N>::operator=(const BitmapConstRef<T, N> &orig) { Bitmap<T, N> &Bitmap<T, N>::operator=(const BitmapConstRef<T, N> &orig) {
if (pixels != orig.pixels) { if (pixels != orig.pixels) {
delete [] pixels; delete[] pixels;
w = orig.width, h = orig.height; w = orig.width, h = orig.height;
yOrientation = orig.yOrientation;
pixels = new T[N*w*h]; pixels = new T[N*w*h];
memcpy(pixels, orig.pixels, sizeof(T)*N*w*h); memcpy(pixels, orig.pixels, sizeof(T)*N*w*h);
} }
return *this; 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> template <typename T, int N>
Bitmap<T, N> &Bitmap<T, N>::operator=(const Bitmap<T, N> &orig) { Bitmap<T, N> &Bitmap<T, N>::operator=(const Bitmap<T, N> &orig) {
if (this != &orig) { if (this != &orig) {
delete [] pixels; delete[] pixels;
w = orig.w, h = orig.h; w = orig.w, h = orig.h;
yOrientation = orig.yOrientation;
pixels = new T[N*w*h]; pixels = new T[N*w*h];
memcpy(pixels, orig.pixels, sizeof(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> template <typename T, int N>
Bitmap<T, N> &Bitmap<T, N>::operator=(Bitmap<T, N> &&orig) { Bitmap<T, N> &Bitmap<T, N>::operator=(Bitmap<T, N> &&orig) {
if (this != &orig) { if (this != &orig) {
delete [] pixels; delete[] pixels;
pixels = orig.pixels; pixels = orig.pixels;
w = orig.w, h = orig.h; w = orig.w, h = orig.h;
yOrientation = orig.yOrientation;
orig.pixels = NULL; orig.pixels = NULL;
} }
return *this; return *this;
@@ -106,12 +141,32 @@ Bitmap<T, N>::operator const T *() const {
template <typename T, int N> template <typename T, int N>
Bitmap<T, N>::operator BitmapRef<T, 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> template <typename T, int N>
Bitmap<T, N>::operator BitmapConstRef<T, N>() const { 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 #pragma once
#include "base.h" #include "YAxisOrientation.h"
namespace msdfgen { namespace msdfgen {
/// Reference to a 2D image bitmap or a buffer acting as one. Pixel storage not owned or managed by the object. /// 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 = 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 { struct BitmapRef {
T *pixels; T *pixels;
int width, height; int width, height;
YAxisOrientation yOrientation;
inline BitmapRef() : pixels(NULL), width(0), height(0) { } inline BitmapRef() : pixels(NULL), width(0), height(0), yOrientation(MSDFGEN_Y_AXIS_DEFAULT_ORIENTATION) { }
inline BitmapRef(T *pixels, int width, int height) : pixels(pixels), width(width), height(height) { } 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 { inline T *operator()(int x, int y) const {
return pixels+N*(width*y+x); 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>
template <typename T, int N = 1>
struct BitmapConstRef { struct BitmapConstRef {
const T *pixels; const T *pixels;
int width, height; int width, height;
YAxisOrientation yOrientation;
inline BitmapConstRef() : pixels(NULL), width(0), height(0) { } inline BitmapConstRef() : pixels(NULL), width(0), height(0), yOrientation(MSDFGEN_Y_AXIS_DEFAULT_ORIENTATION) { }
inline BitmapConstRef(const T *pixels, int width, int height) : pixels(pixels), width(width), height(height) { } 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) { } 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 { inline const T *operator()(int x, int y) const {
return pixels+N*(width*y+x); 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(); return edges.back();
} }
static void boundPoint(double &l, double &b, double &r, double &t, Point2 p) { static void boundPoint(double &xMin, double &yMin, double &xMax, double &yMax, Point2 p) {
if (p.x < l) l = p.x; if (p.x < xMin) xMin = p.x;
if (p.y < b) b = p.y; if (p.y < yMin) yMin = p.y;
if (p.x > r) r = p.x; if (p.x > xMax) xMax = p.x;
if (p.y > t) t = p.y; 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) 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()) if (edges.empty())
return; return;
Vector2 prevDir = edges.back()->direction(1).normalize(true); 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) if (q > 0)
miterLength = min(1/sqrt(q), miterLimit); miterLength = min(1/sqrt(q), miterLimit);
Point2 miter = (*edge)->point(0)+border*miterLength*(prevDir+dir).normalize(true); 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); prevDir = (*edge)->direction(1).normalize(true);
} }

View File

@@ -21,9 +21,9 @@ public:
/// Creates a new edge in the contour and returns its reference. /// Creates a new edge in the contour and returns its reference.
EdgeHolder &addEdge(); EdgeHolder &addEdge();
/// Adjusts the bounding box to fit the contour. /// 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. /// 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. /// Computes the winding of the contour. Returns 1 if positive, -1 if negative.
int winding() const; int winding() const;
/// Reverses the sequence of edges on the contour. /// Reverses the sequence of edges on the contour.

View File

@@ -87,17 +87,15 @@ public:
Point2 shapeCoord, sdfCoord; Point2 shapeCoord, sdfCoord;
const float *msd; const float *msd;
bool protectedFlag; 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)); texelSize = projection.unprojectVector(Vector2(1));
if (shape.inverseYAxis)
texelSize.y = -texelSize.y;
} }
inline ArtifactClassifier classifier(const Vector2 &direction, double span) { inline ArtifactClassifier classifier(const Vector2 &direction, double span) {
return ArtifactClassifier(this, direction, span); return ArtifactClassifier(this, direction, span);
} }
private: private:
ShapeDistanceFinder<ContourCombiner<PerpendicularDistanceSelector> > distanceFinder; ShapeDistanceFinder<ContourCombiner<PerpendicularDistanceSelector> > distanceFinder;
BitmapConstRef<float, N> sdf; BitmapConstSection<float, N> sdf;
DistanceMapping distanceMapping; DistanceMapping distanceMapping;
Vector2 texelSize; Vector2 texelSize;
double minImproveRatio; double minImproveRatio;
@@ -105,10 +103,11 @@ private:
MSDFErrorCorrection::MSDFErrorCorrection() { } 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; minDeviationRatio = ErrorCorrectionConfig::defaultMinDeviationRatio;
minImproveRatio = ErrorCorrectionConfig::defaultMinImproveRatio; 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) { void MSDFErrorCorrection::setMinDeviationRatio(double minDeviationRatio) {
@@ -120,6 +119,7 @@ void MSDFErrorCorrection::setMinImproveRatio(double minImproveRatio) {
} }
void MSDFErrorCorrection::protectCorners(const Shape &shape) { 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) for (std::vector<Contour>::const_iterator contour = shape.contours.begin(); contour != shape.contours.end(); ++contour)
if (!contour->edges.empty()) { if (!contour->edges.empty()) {
const EdgeSegment *prevEdge = contour->edges.back(); const EdgeSegment *prevEdge = contour->edges.back();
@@ -131,8 +131,6 @@ void MSDFErrorCorrection::protectCorners(const Shape &shape) {
Point2 p = transformation.project((*edge)->point(0)); Point2 p = transformation.project((*edge)->point(0));
int l = (int) floor(p.x-.5); int l = (int) floor(p.x-.5);
int b = (int) floor(p.y-.5); int b = (int) floor(p.y-.5);
if (shape.inverseYAxis)
b = stencil.height-b-2;
int r = l+1; int r = l+1;
int t = b+1; int t = b+1;
// Check that the positions are within bounds. // 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> template <int N>
void MSDFErrorCorrection::protectEdges(const BitmapConstRef<float, N> &sdf) { void MSDFErrorCorrection::protectEdges(const BitmapConstSection<float, N> &sdf) {
float radius; float radius;
stencil.reorient(sdf.yOrientation);
// Horizontal texel pairs // Horizontal texel pairs
radius = float(PROTECTION_RADIUS_TOLERANCE*transformation.unprojectVector(Vector2(transformation.distanceMapping(DistanceMapping::Delta(1)), 0)).length()); radius = float(PROTECTION_RADIUS_TOLERANCE*transformation.unprojectVector(Vector2(transformation.distanceMapping(DistanceMapping::Delta(1)), 0)).length());
for (int y = 0; y < sdf.height; ++y) { for (int y = 0; y < sdf.height; ++y) {
@@ -251,9 +250,11 @@ void MSDFErrorCorrection::protectEdges(const BitmapConstRef<float, N> &sdf) {
} }
void MSDFErrorCorrection::protectAll() { void MSDFErrorCorrection::protectAll() {
byte *end = stencil.pixels+stencil.width*stencil.height; for (int y = 0; y < stencil.height; ++y) {
for (byte *mask = stencil.pixels; mask < end; ++mask) byte *mask = stencil(0, y);
*mask |= (byte) PROTECTED; for (int x = 0; x < stencil.width; ++x)
*mask++ |= (byte) PROTECTED;
}
} }
/// Returns the median of the linear interpolation of texels a, b at t. /// 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. /// 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> template <class ArtifactClassifier>
static bool hasLinearArtifactInner(const ArtifactClassifier &artifactClassifier, float am, float bm, const float *a, const float *b, float dA, float dB) { 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> 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. // 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 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(); 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> 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. // 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 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(); 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 #endif
{ {
ShapeDistanceChecker<ContourCombiner, N> shapeDistanceChecker(sdf, shape, transformation, transformation.distanceMapping, minImproveRatio); ShapeDistanceChecker<ContourCombiner, N> shapeDistanceChecker(sdf, shape, transformation, transformation.distanceMapping, minImproveRatio);
bool rightToLeft = false; int xDirection = 1;
// Inspect all texels. // Inspect all texels.
#ifdef MSDFGEN_USE_OPENMP #ifdef MSDFGEN_USE_OPENMP
#pragma omp for #pragma omp for
#endif #endif
for (int y = 0; y < sdf.height; ++y) { for (int y = 0; y < sdf.height; ++y) {
int row = shape.inverseYAxis ? sdf.height-y-1 : y; int x = xDirection < 0 ? sdf.width-1 : 0;
for (int col = 0; col < sdf.width; ++col) { for (int col = 0; col < sdf.width; ++col, x += xDirection) {
int x = rightToLeft ? sdf.width-col-1 : col; if ((*stencil(x, y)&ERROR))
if ((*stencil(x, row)&ERROR))
continue; continue;
const float *c = sdf(x, row); const float *c = sdf(x, y);
shapeDistanceChecker.shapeCoord = transformation.unproject(Point2(x+.5, y+.5)); 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.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]); float cm = median(c[0], c[1], c[2]);
const float *l = NULL, *b = NULL, *r = NULL, *t = NULL; 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. // 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*( *stencil(x, y) |= (byte) (ERROR*(
(x > 0 && ((l = sdf(x-1, row)), hasLinearArtifact(shapeDistanceChecker.classifier(Vector2(-1, 0), hSpan), cm, c, l))) || (x > 0 && ((l = sdf(x-1, y)), 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))) || (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, row)), hasLinearArtifact(shapeDistanceChecker.classifier(Vector2(+1, 0), hSpan), cm, c, r))) || (x < sdf.width-1 && ((r = sdf(x+1, y)), 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))) || (y < sdf.height-1 && ((t = sdf(x, y+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 > 0 && y > 0 && hasDiagonalArtifact(shapeDistanceChecker.classifier(Vector2(-1, -1), dSpan), cm, c, l, b, sdf(x-1, y-1))) ||
(x < sdf.width-1 && row > 0 && hasDiagonalArtifact(shapeDistanceChecker.classifier(Vector2(+1, -1), dSpan), cm, c, r, b, sdf(x+1, row-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 && row < sdf.height-1 && hasDiagonalArtifact(shapeDistanceChecker.classifier(Vector2(-1, +1), dSpan), cm, c, l, t, sdf(x-1, row+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 && row < sdf.height-1 && hasDiagonalArtifact(shapeDistanceChecker.classifier(Vector2(+1, +1), dSpan), cm, c, r, t, sdf(x+1, row+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> template <int N>
void MSDFErrorCorrection::apply(const BitmapRef<float, N> &sdf) const { void MSDFErrorCorrection::apply(BitmapSection<float, N> sdf) const {
int texelCount = sdf.width*sdf.height; sdf.reorient(stencil.yOrientation);
const byte *mask = stencil.pixels; const byte *stencilRow = stencil.pixels;
float *texel = sdf.pixels; float *rowStart = sdf.pixels;
for (int i = 0; i < texelCount; ++i) { for (int y = 0; y < sdf.height; ++y) {
if (*mask&ERROR) { const byte *mask = stencilRow;
// Set all color channels to the median. float *pixel = rowStart;
float m = median(texel[0], texel[1], texel[2]); for (int x = 0; x < sdf.width; ++x) {
texel[0] = m, texel[1] = m, texel[2] = m; 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; stencilRow += stencil.rowStride;
texel += N; rowStart += sdf.rowStride;
} }
} }
BitmapConstRef<byte, 1> MSDFErrorCorrection::getStencil() const { BitmapConstSection<byte, 1> MSDFErrorCorrection::getStencil() const {
return stencil; return stencil;
} }
template void MSDFErrorCorrection::protectEdges(const BitmapConstRef<float, 3> &sdf); template void MSDFErrorCorrection::protectEdges(const BitmapConstSection<float, 3> &sdf);
template void MSDFErrorCorrection::protectEdges(const BitmapConstRef<float, 4> &sdf); template void MSDFErrorCorrection::protectEdges(const BitmapConstSection<float, 4> &sdf);
template void MSDFErrorCorrection::findErrors(const BitmapConstRef<float, 3> &sdf); template void MSDFErrorCorrection::findErrors(const BitmapConstSection<float, 3> &sdf);
template void MSDFErrorCorrection::findErrors(const BitmapConstRef<float, 4> &sdf); template void MSDFErrorCorrection::findErrors(const BitmapConstSection<float, 4> &sdf);
template void MSDFErrorCorrection::findErrors<SimpleContourCombiner>(const BitmapConstRef<float, 3> &sdf, const Shape &shape); template void MSDFErrorCorrection::findErrors<SimpleContourCombiner>(BitmapConstSection<float, 3> sdf, const Shape &shape);
template void MSDFErrorCorrection::findErrors<SimpleContourCombiner>(const BitmapConstRef<float, 4> &sdf, const Shape &shape); template void MSDFErrorCorrection::findErrors<SimpleContourCombiner>(BitmapConstSection<float, 4> sdf, const Shape &shape);
template void MSDFErrorCorrection::findErrors<OverlappingContourCombiner>(const BitmapConstRef<float, 3> &sdf, const Shape &shape); template void MSDFErrorCorrection::findErrors<OverlappingContourCombiner>(BitmapConstSection<float, 3> sdf, const Shape &shape);
template void MSDFErrorCorrection::findErrors<OverlappingContourCombiner>(const BitmapConstRef<float, 4> &sdf, const Shape &shape); template void MSDFErrorCorrection::findErrors<OverlappingContourCombiner>(BitmapConstSection<float, 4> sdf, const Shape &shape);
template void MSDFErrorCorrection::apply(const BitmapRef<float, 3> &sdf) const; template void MSDFErrorCorrection::apply(BitmapSection<float, 3> sdf) const;
template void MSDFErrorCorrection::apply(const BitmapRef<float, 4> &sdf) const; template void MSDFErrorCorrection::apply(BitmapSection<float, 4> sdf) const;
} }

View File

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

View File

@@ -3,6 +3,7 @@
#include <cstdlib> #include <cstdlib>
#include "arithmetics.hpp" #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 #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[0]));
contour->edges.push_back(EdgeHolder(parts[1])); contour->edges.push_back(EdgeHolder(parts[1]));
contour->edges.push_back(EdgeHolder(parts[2])); contour->edges.push_back(EdgeHolder(parts[2]));
} else { } else if (!contour->edges.empty()) {
// Push apart convergent edge segments // Push apart convergent edge segments
EdgeHolder *prevEdge = &contour->edges.back(); EdgeHolder *prevEdge = &contour->edges.back();
for (std::vector<EdgeHolder>::iterator edge = contour->edges.begin(); edge != contour->edges.end(); ++edge) { 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) { 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); 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(); 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 (convergentCurveOrdering(*prevEdge, *edge) < 0)
if (crossProduct((*prevEdge)->directionChange(1), (*edge)->direction(0))+crossProduct((*edge)->directionChange(0), (*prevEdge)->direction(1)) < 0)
axis = -axis; axis = -axis;
deconvergeEdge(*prevEdge, 1, axis.getOrthogonal(true)); deconvergeEdge(*prevEdge, 1, axis.getOrthogonal(true));
deconvergeEdge(*edge, 0, axis.getOrthogonal(false)); 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) 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) 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 { Shape::Bounds Shape::getBounds(double border, double miterLimit, int polarity) const {
@@ -197,4 +197,12 @@ void Shape::orientContours() {
contours[i].reverse(); 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 <vector>
#include "Contour.h" #include "Contour.h"
#include "YAxisOrientation.h"
#include "Scanline.h" #include "Scanline.h"
namespace msdfgen { namespace msdfgen {
@@ -15,12 +16,14 @@ class Shape {
public: public:
struct Bounds { 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; double l, b, r, t;
}; };
/// The list of contours the shape consists of. /// The list of contours the shape consists of.
std::vector<Contour> contours; std::vector<Contour> contours;
/// Specifies whether the shape uses bottom-to-top (false) or top-to-bottom (true) Y coordinates. /// Specifies whether the shape uses bottom-to-top (false) or top-to-bottom (true) Y coordinates.
/// DEPRECATED - use getYAxisOrientation / setYAxisOrientation instead.
bool inverseYAxis; bool inverseYAxis;
Shape(); Shape();
@@ -36,9 +39,9 @@ public:
/// Performs basic checks to determine if the object represents a valid shape. /// Performs basic checks to determine if the object represents a valid shape.
bool validate() const; bool validate() const;
/// Adjusts the bounding box to fit the shape. /// 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. /// 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. /// 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; Bounds getBounds(double border = 0, double miterLimit = 0, int polarity = 0) const;
/// Outputs the scanline that intersects the shape at y. /// Outputs the scanline that intersects the shape at y.
@@ -47,6 +50,10 @@ public:
int edgeCount() const; int edgeCount() const;
/// Assumes its contours are unoriented (even-odd fill rule). Attempts to orient them to conform to the non-zero winding rule. /// Assumes its contours are unoriented (even-odd fill rule). Attempts to orient them to conform to the non-zero winding rule.
void orientContours(); 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 { namespace msdfgen {
template <typename T, int N> 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; pos -= .5;
int l = (int) floor(pos.x); int l = (int) floor(pos.x);
int b = (int) floor(pos.y); int b = (int) floor(pos.y);

View File

@@ -32,7 +32,7 @@ static double resolveDistance(const MultiDistance &distance) {
} }
template <class EdgeSelector> template <class EdgeSelector>
SimpleContourCombiner<EdgeSelector>::SimpleContourCombiner(const Shape &shape) { } SimpleContourCombiner<EdgeSelector>::SimpleContourCombiner(const Shape &) { }
template <class EdgeSelector> template <class EdgeSelector>
void SimpleContourCombiner<EdgeSelector>::reset(const Point2 &p) { 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 double minDistance = nonZeroSign(crossProduct(epDir, qa))*qa.length(); // distance from A
param = -dotProduct(qa, epDir)/dotProduct(epDir, epDir); param = -dotProduct(qa, epDir)/dotProduct(epDir, epDir);
{ {
epDir = direction(1);
double distance = (p[2]-origin).length(); // distance from B double distance = (p[2]-origin).length(); // distance from B
if (distance < fabs(minDistance)) { if (distance < fabs(minDistance)) {
epDir = direction(1);
minDistance = nonZeroSign(crossProduct(epDir, p[2]-origin))*distance; minDistance = nonZeroSign(crossProduct(epDir, p[2]-origin))*distance;
param = dotProduct(origin-p[1], epDir)/dotProduct(epDir, epDir); 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 double minDistance = nonZeroSign(crossProduct(epDir, qa))*qa.length(); // distance from A
param = -dotProduct(qa, epDir)/dotProduct(epDir, epDir); param = -dotProduct(qa, epDir)/dotProduct(epDir, epDir);
{ {
epDir = direction(1);
double distance = (p[3]-origin).length(); // distance from B double distance = (p[3]-origin).length(); // distance from B
if (distance < fabs(minDistance)) { if (distance < fabs(minDistance)) {
epDir = direction(1);
minDistance = nonZeroSign(crossProduct(epDir, p[3]-origin))*distance; minDistance = nonZeroSign(crossProduct(epDir, p[3]-origin))*distance;
param = dotProduct(epDir-(p[3]-origin), epDir)/dotProduct(epDir, epDir); param = dotProduct(epDir-(p[3]-origin), epDir)/dotProduct(epDir, epDir);
} }
} }
// Iterative minimum distance search // Iterative minimum distance search
for (int i = 0; i <= MSDFGEN_CUBIC_SEARCH_STARTS; ++i) { 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; Vector2 qe = qa+3*t*ab+3*t*t*br+t*t*t*as;
for (int step = 0; step < MSDFGEN_CUBIC_SEARCH_STEPS; ++step) { Vector2 d1 = 3*ab+6*t*br+3*t*t*as;
// Improve t Vector2 d2 = 6*br+6*t*as;
Vector2 d1 = 3*ab+6*t*br+3*t*t*as; double improvedT = t-dotProduct(qe, d1)/(dotProduct(d1, d1)+dotProduct(qe, d2));
Vector2 d2 = 6*br+6*t*as; if (improvedT > 0 && improvedT < 1) {
t -= dotProduct(qe, d1)/(dotProduct(d1, d1)+dotProduct(qe, d2)); int remainingSteps = MSDFGEN_CUBIC_SEARCH_STEPS;
if (t <= 0 || t >= 1) do {
break; t = improvedT;
qe = qa+3*t*ab+3*t*t*br+t*t*t*as; 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(); double distance = qe.length();
if (distance < fabs(minDistance)) { if (distance < fabs(minDistance)) {
minDistance = nonZeroSign(crossProduct(d1, qe))*distance; minDistance = nonZeroSign(crossProduct(d1, qe))*distance;
@@ -396,37 +402,37 @@ int CubicSegment::scanlineIntersections(double x[3], int dy[3], double y) const
return total; return total;
} }
static void pointBounds(Point2 p, double &l, double &b, double &r, double &t) { static void pointBounds(Point2 p, double &xMin, double &yMin, double &xMax, double &yMax) {
if (p.x < l) l = p.x; if (p.x < xMin) xMin = p.x;
if (p.y < b) b = p.y; if (p.y < yMin) yMin = p.y;
if (p.x > r) r = p.x; if (p.x > xMax) xMax = p.x;
if (p.y > t) t = p.y; if (p.y > yMax) yMax = p.y;
} }
void LinearSegment::bound(double &l, double &b, double &r, double &t) const { void LinearSegment::bound(double &xMin, double &yMin, double &xMax, double &yMax) const {
pointBounds(p[0], l, b, r, t); pointBounds(p[0], xMin, yMin, xMax, yMax);
pointBounds(p[1], l, b, r, t); pointBounds(p[1], xMin, yMin, xMax, yMax);
} }
void QuadraticSegment::bound(double &l, double &b, double &r, double &t) const { void QuadraticSegment::bound(double &xMin, double &yMin, double &xMax, double &yMax) const {
pointBounds(p[0], l, b, r, t); pointBounds(p[0], xMin, yMin, xMax, yMax);
pointBounds(p[2], l, b, r, t); pointBounds(p[2], xMin, yMin, xMax, yMax);
Vector2 bot = (p[1]-p[0])-(p[2]-p[1]); Vector2 bot = (p[1]-p[0])-(p[2]-p[1]);
if (bot.x) { if (bot.x) {
double param = (p[1].x-p[0].x)/bot.x; double param = (p[1].x-p[0].x)/bot.x;
if (param > 0 && param < 1) if (param > 0 && param < 1)
pointBounds(point(param), l, b, r, t); pointBounds(point(param), xMin, yMin, xMax, yMax);
} }
if (bot.y) { if (bot.y) {
double param = (p[1].y-p[0].y)/bot.y; double param = (p[1].y-p[0].y)/bot.y;
if (param > 0 && param < 1) 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 { void CubicSegment::bound(double &xMin, double &yMin, double &xMax, double &yMax) const {
pointBounds(p[0], l, b, r, t); pointBounds(p[0], xMin, yMin, xMax, yMax);
pointBounds(p[3], l, b, r, t); pointBounds(p[3], xMin, yMin, xMax, yMax);
Vector2 a0 = p[1]-p[0]; Vector2 a0 = p[1]-p[0];
Vector2 a1 = 2*(p[2]-p[1]-a0); Vector2 a1 = 2*(p[2]-p[1]-a0);
Vector2 a2 = p[3]-3*p[2]+3*p[1]-p[0]; 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); solutions = solveQuadratic(params, a2.x, a1.x, a0.x);
for (int i = 0; i < solutions; ++i) for (int i = 0; i < solutions; ++i)
if (params[i] > 0 && params[i] < 1) 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); solutions = solveQuadratic(params, a2.y, a1.y, a0.y);
for (int i = 0; i < solutions; ++i) for (int i = 0; i < solutions; ++i)
if (params[i] > 0 && params[i] < 1) 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() { 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. /// 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; virtual int scanlineIntersections(double x[3], int dy[3], double y) const = 0;
/// Adjusts the bounding box to fit the edge segment. /// 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). /// Reverses the edge (swaps its start point and end point).
virtual void reverse() = 0; virtual void reverse() = 0;
@@ -75,7 +75,7 @@ public:
double length() const; double length() const;
SignedDistance signedDistance(Point2 origin, double &param) const; SignedDistance signedDistance(Point2 origin, double &param) const;
int scanlineIntersections(double x[3], int dy[3], double y) 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 reverse();
void moveStartPoint(Point2 to); void moveStartPoint(Point2 to);
@@ -104,7 +104,7 @@ public:
double length() const; double length() const;
SignedDistance signedDistance(Point2 origin, double &param) const; SignedDistance signedDistance(Point2 origin, double &param) const;
int scanlineIntersections(double x[3], int dy[3], double y) 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 reverse();
void moveStartPoint(Point2 to); void moveStartPoint(Point2 to);
@@ -134,7 +134,7 @@ public:
Vector2 directionChange(double param) const; Vector2 directionChange(double param) const;
SignedDistance signedDistance(Point2 origin, double &param) const; SignedDistance signedDistance(Point2 origin, double &param) const;
int scanlineIntersections(double x[3], int dy[3], double y) 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 reverse();
void moveStartPoint(Point2 to); void moveStartPoint(Point2 to);

View File

@@ -11,6 +11,7 @@ TrueDistanceSelector::EdgeCache::EdgeCache() : absDistance(0) { }
void TrueDistanceSelector::reset(const Point2 &p) { void TrueDistanceSelector::reset(const Point2 &p) {
double delta = DISTANCE_DELTA_FACTOR*(p-this->p).length(); 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; minDistance.distance += nonZeroSign(minDistance.distance)*delta;
this->p = p; this->p = p;
} }
@@ -60,7 +61,7 @@ void PerpendicularDistanceSelectorBase::reset(double delta) {
nearEdgeParam = 0; 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(); double delta = DISTANCE_DELTA_FACTOR*(p-cache.point).length();
return ( return (
cache.absDistance-delta <= fabs(minTrueDistance.distance) || 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 { namespace msdfgen {
template <int N> 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) if (config.errorCorrection.mode == ErrorCorrectionConfig::DISABLED)
return; return;
Bitmap<byte, 1> stencilBuffer; Bitmap<byte, 1> stencilBuffer;
if (!config.errorCorrection.buffer) if (!config.errorCorrection.buffer)
stencilBuffer = Bitmap<byte, 1>(sdf.width, sdf.height); 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.pixels = config.errorCorrection.buffer ? config.errorCorrection.buffer : (byte *) stencilBuffer;
stencil.width = sdf.width, stencil.height = sdf.height;
MSDFErrorCorrection ec(stencil, transformation); MSDFErrorCorrection ec(stencil, transformation);
ec.setMinDeviationRatio(config.errorCorrection.minDeviationRatio); ec.setMinDeviationRatio(config.errorCorrection.minDeviationRatio);
ec.setMinImproveRatio(config.errorCorrection.minImproveRatio); ec.setMinImproveRatio(config.errorCorrection.minImproveRatio);
@@ -49,7 +48,7 @@ static void msdfErrorCorrectionInner(const BitmapRef<float, N> &sdf, const Shape
} }
template <int N> 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); Bitmap<byte, 1> stencilBuffer(sdf.width, sdf.height);
MSDFErrorCorrection ec(stencilBuffer, transformation); MSDFErrorCorrection ec(stencilBuffer, transformation);
ec.setMinDeviationRatio(minDeviationRatio); ec.setMinDeviationRatio(minDeviationRatio);
@@ -59,54 +58,54 @@ static void msdfErrorCorrectionShapeless(const BitmapRef<float, N> &sdf, const S
ec.apply(sdf); 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); 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); 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); 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); 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); 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); 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); 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); 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); 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); 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); 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); 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); 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); 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); 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); 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> 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; std::vector<std::pair<int, int> > clashes;
int w = output.width, h = output.height; int w = output.width, h = output.height;
for (int y = 0; y < h; ++y) for (int y = 0; y < h; ++y)
@@ -174,10 +173,10 @@ static void msdfErrorCorrectionInner_legacy(const BitmapRef<float, N> &output, c
#endif #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); 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); msdfErrorCorrectionInner_legacy(output, threshold);
} }

View File

@@ -12,29 +12,29 @@
namespace msdfgen { namespace msdfgen {
/// Predicts potential artifacts caused by the interpolation of the MSDF and corrects them by converting nearby texels to single-channel. /// 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 BitmapSection<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 BitmapSection<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 BitmapSection<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, 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. /// 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 BitmapSection<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 BitmapSection<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 BitmapSection<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 BitmapSection<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 BitmapSection<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, 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. /// 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 BitmapSection<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 BitmapSection<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 BitmapSection<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 BitmapSection<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 BitmapSection<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, 4> &sdf, Range pxRange, double minDeviationRatio = ErrorCorrectionConfig::defaultMinDeviationRatio);
/// The original version of the error correction algorithm. /// The original version of the error correction algorithm.
void msdfErrorCorrection_legacy(const BitmapRef<float, 3> &output, const Vector2 &threshold); void msdfErrorCorrection_legacy(const BitmapSection<float, 3> &output, const Vector2 &threshold);
void msdfErrorCorrection_legacy(const BitmapRef<float, 4> &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> { class DistancePixelConversion<double> {
DistanceMapping mapping; DistanceMapping mapping;
public: public:
typedef BitmapRef<float, 1> BitmapRefType; typedef BitmapSection<float, 1> BitmapSectionType;
inline explicit DistancePixelConversion(DistanceMapping mapping) : mapping(mapping) { } inline explicit DistancePixelConversion(DistanceMapping mapping) : mapping(mapping) { }
inline void operator()(float *pixels, double distance) const { inline void operator()(float *pixels, double distance) const {
*pixels = float(mapping(distance)); *pixels = float(mapping(distance));
@@ -26,7 +26,7 @@ template <>
class DistancePixelConversion<MultiDistance> { class DistancePixelConversion<MultiDistance> {
DistanceMapping mapping; DistanceMapping mapping;
public: public:
typedef BitmapRef<float, 3> BitmapRefType; typedef BitmapSection<float, 3> BitmapSectionType;
inline explicit DistancePixelConversion(DistanceMapping mapping) : mapping(mapping) { } inline explicit DistancePixelConversion(DistanceMapping mapping) : mapping(mapping) { }
inline void operator()(float *pixels, const MultiDistance &distance) const { inline void operator()(float *pixels, const MultiDistance &distance) const {
pixels[0] = float(mapping(distance.r)); pixels[0] = float(mapping(distance.r));
@@ -39,7 +39,7 @@ template <>
class DistancePixelConversion<MultiAndTrueDistance> { class DistancePixelConversion<MultiAndTrueDistance> {
DistanceMapping mapping; DistanceMapping mapping;
public: public:
typedef BitmapRef<float, 4> BitmapRefType; typedef BitmapSection<float, 4> BitmapSectionType;
inline explicit DistancePixelConversion(DistanceMapping mapping) : mapping(mapping) { } inline explicit DistancePixelConversion(DistanceMapping mapping) : mapping(mapping) { }
inline void operator()(float *pixels, const MultiAndTrueDistance &distance) const { inline void operator()(float *pixels, const MultiAndTrueDistance &distance) const {
pixels[0] = float(mapping(distance.r)); pixels[0] = float(mapping(distance.r));
@@ -50,45 +50,46 @@ public:
}; };
template <class ContourCombiner> 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); DistancePixelConversion<typename ContourCombiner::DistanceType> distancePixelConversion(transformation.distanceMapping);
output.reorient(shape.getYAxisOrientation());
#ifdef MSDFGEN_USE_OPENMP #ifdef MSDFGEN_USE_OPENMP
#pragma omp parallel #pragma omp parallel
#endif #endif
{ {
ShapeDistanceFinder<ContourCombiner> distanceFinder(shape); ShapeDistanceFinder<ContourCombiner> distanceFinder(shape);
bool rightToLeft = false; int xDirection = 1;
#ifdef MSDFGEN_USE_OPENMP #ifdef MSDFGEN_USE_OPENMP
#pragma omp for #pragma omp for
#endif #endif
for (int y = 0; y < output.height; ++y) { 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) { 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)); Point2 p = transformation.unproject(Point2(x+.5, y+.5));
typename ContourCombiner::DistanceType distance = distanceFinder.distance(p); 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) if (config.overlapSupport)
generateDistanceField<OverlappingContourCombiner<TrueDistanceSelector> >(output, shape, transformation); generateDistanceField<OverlappingContourCombiner<TrueDistanceSelector> >(output, shape, transformation);
else else
generateDistanceField<SimpleContourCombiner<TrueDistanceSelector> >(output, shape, transformation); 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) if (config.overlapSupport)
generateDistanceField<OverlappingContourCombiner<PerpendicularDistanceSelector> >(output, shape, transformation); generateDistanceField<OverlappingContourCombiner<PerpendicularDistanceSelector> >(output, shape, transformation);
else else
generateDistanceField<SimpleContourCombiner<PerpendicularDistanceSelector> >(output, shape, transformation); 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) if (config.overlapSupport)
generateDistanceField<OverlappingContourCombiner<MultiDistanceSelector> >(output, shape, transformation); generateDistanceField<OverlappingContourCombiner<MultiDistanceSelector> >(output, shape, transformation);
else else
@@ -96,7 +97,7 @@ void generateMSDF(const BitmapRef<float, 3> &output, const Shape &shape, const S
msdfErrorCorrection(output, shape, transformation, config); 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) if (config.overlapSupport)
generateDistanceField<OverlappingContourCombiner<MultiAndTrueDistanceSelector> >(output, shape, transformation); generateDistanceField<OverlappingContourCombiner<MultiAndTrueDistanceSelector> >(output, shape, transformation);
else else
@@ -104,21 +105,21 @@ void generateMTSDF(const BitmapRef<float, 4> &output, const Shape &shape, const
msdfErrorCorrection(output, shape, transformation, config); 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) if (config.overlapSupport)
generateDistanceField<OverlappingContourCombiner<TrueDistanceSelector> >(output, shape, SDFTransformation(projection, range)); generateDistanceField<OverlappingContourCombiner<TrueDistanceSelector> >(output, shape, SDFTransformation(projection, range));
else else
generateDistanceField<SimpleContourCombiner<TrueDistanceSelector> >(output, shape, SDFTransformation(projection, range)); 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) if (config.overlapSupport)
generateDistanceField<OverlappingContourCombiner<PerpendicularDistanceSelector> >(output, shape, SDFTransformation(projection, range)); generateDistanceField<OverlappingContourCombiner<PerpendicularDistanceSelector> >(output, shape, SDFTransformation(projection, range));
else else
generateDistanceField<SimpleContourCombiner<PerpendicularDistanceSelector> >(output, shape, SDFTransformation(projection, range)); 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) if (config.overlapSupport)
generateDistanceField<OverlappingContourCombiner<MultiDistanceSelector> >(output, shape, SDFTransformation(projection, range)); generateDistanceField<OverlappingContourCombiner<MultiDistanceSelector> >(output, shape, SDFTransformation(projection, range));
else else
@@ -126,7 +127,7 @@ void generateMSDF(const BitmapRef<float, 3> &output, const Shape &shape, const P
msdfErrorCorrection(output, shape, SDFTransformation(projection, range), config); 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) if (config.overlapSupport)
generateDistanceField<OverlappingContourCombiner<MultiAndTrueDistanceSelector> >(output, shape, SDFTransformation(projection, range)); generateDistanceField<OverlappingContourCombiner<MultiAndTrueDistanceSelector> >(output, shape, SDFTransformation(projection, range));
else else
@@ -136,39 +137,39 @@ void generateMTSDF(const BitmapRef<float, 4> &output, const Shape &shape, const
// Legacy API // 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); 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)); 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)); 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)); 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)); 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)); generateMTSDF(output, shape, Projection(scale, translate), range, MSDFGeneratorConfig(overlapSupport, errorCorrectionConfig));
} }
// Legacy version // 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); DistanceMapping distanceMapping(range);
output.reorient(shape.getYAxisOrientation());
#ifdef MSDFGEN_USE_OPENMP #ifdef MSDFGEN_USE_OPENMP
#pragma omp parallel for #pragma omp parallel for
#endif #endif
for (int y = 0; y < output.height; ++y) { 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) { for (int x = 0; x < output.width; ++x) {
double dummy; double dummy;
Point2 p = Vector2(x+.5, y+.5)/scale-translate; 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) if (distance < minDistance)
minDistance = distance; 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); DistanceMapping distanceMapping(range);
output.reorient(shape.getYAxisOrientation());
#ifdef MSDFGEN_USE_OPENMP #ifdef MSDFGEN_USE_OPENMP
#pragma omp parallel for #pragma omp parallel for
#endif #endif
for (int y = 0; y < output.height; ++y) { 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) { for (int x = 0; x < output.width; ++x) {
Point2 p = Vector2(x+.5, y+.5)/scale-translate; Point2 p = Vector2(x+.5, y+.5)/scale-translate;
SignedDistance minDistance; SignedDistance minDistance;
@@ -208,22 +209,22 @@ void generatePSDF_legacy(const BitmapRef<float, 1> &output, const Shape &shape,
} }
if (nearEdge) if (nearEdge)
(*nearEdge)->distanceToPerpendicularDistance(minDistance, p, nearParam); (*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); 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); DistanceMapping distanceMapping(range);
output.reorient(shape.getYAxisOrientation());
#ifdef MSDFGEN_USE_OPENMP #ifdef MSDFGEN_USE_OPENMP
#pragma omp parallel for #pragma omp parallel for
#endif #endif
for (int y = 0; y < output.height; ++y) { 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) { for (int x = 0; x < output.width; ++x) {
Point2 p = Vector2(x+.5, y+.5)/scale-translate; 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); (*g.nearEdge)->distanceToPerpendicularDistance(g.minDistance, p, g.nearParam);
if (b.nearEdge) if (b.nearEdge)
(*b.nearEdge)->distanceToPerpendicularDistance(b.minDistance, p, b.nearParam); (*b.nearEdge)->distanceToPerpendicularDistance(b.minDistance, p, b.nearParam);
output(x, row)[0] = float(distanceMapping(r.minDistance.distance)); output(x, y)[0] = float(distanceMapping(r.minDistance.distance));
output(x, row)[1] = float(distanceMapping(g.minDistance.distance)); output(x, y)[1] = float(distanceMapping(g.minDistance.distance));
output(x, row)[2] = float(distanceMapping(b.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)); 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); DistanceMapping distanceMapping(range);
output.reorient(shape.getYAxisOrientation());
#ifdef MSDFGEN_USE_OPENMP #ifdef MSDFGEN_USE_OPENMP
#pragma omp parallel for #pragma omp parallel for
#endif #endif
for (int y = 0; y < output.height; ++y) { 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) { for (int x = 0; x < output.width; ++x) {
Point2 p = Vector2(x+.5, y+.5)/scale-translate; 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); (*g.nearEdge)->distanceToPerpendicularDistance(g.minDistance, p, g.nearParam);
if (b.nearEdge) if (b.nearEdge)
(*b.nearEdge)->distanceToPerpendicularDistance(b.minDistance, p, b.nearParam); (*b.nearEdge)->distanceToPerpendicularDistance(b.minDistance, p, b.nearParam);
output(x, row)[0] = float(distanceMapping(r.minDistance.distance)); output(x, y)[0] = float(distanceMapping(r.minDistance.distance));
output(x, row)[1] = float(distanceMapping(g.minDistance.distance)); output(x, y)[1] = float(distanceMapping(g.minDistance.distance));
output(x, row)[2] = float(distanceMapping(b.minDistance.distance)); output(x, y)[2] = float(distanceMapping(b.minDistance.distance));
output(x, row)[3] = float(distanceMapping(minDistance.distance)); output(x, y)[3] = float(distanceMapping(minDistance.distance));
} }
} }

View File

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

View File

@@ -10,16 +10,19 @@
namespace msdfgen { namespace msdfgen {
/// Rasterizes the shape into a monochrome bitmap. /// 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. /// 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(BitmapSection<float, 1> sdf, const Shape &shape, const Projection &projection, float sdfZeroValue = .5f, FillRule fillRule = FILL_NONZERO);
void distanceSignCorrection(const BitmapRef<float, 3> &sdf, const Shape &shape, const Projection &projection, 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(const BitmapRef<float, 4> &sdf, const Shape &shape, const Projection &projection, 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 // Old versions 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 rasterize(const BitmapSection<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(BitmapSection<float, 1> sdf, const Shape &shape, const Projection &projection, FillRule fillRule);
void distanceSignCorrection(const BitmapRef<float, 3> &sdf, const Shape &shape, const Vector2 &scale, const Vector2 &translate, FillRule fillRule = FILL_NONZERO); void distanceSignCorrection(BitmapSection<float, 3> sdf, const Shape &shape, const Projection &projection, FillRule fillRule);
void distanceSignCorrection(const BitmapRef<float, 4> &sdf, const Shape &shape, const Vector2 &scale, const Vector2 &translate, FillRule fillRule = FILL_NONZERO); 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); 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); Vector2 scale((double) sdf.width/output.width, (double) sdf.height/output.height);
if (sdfPxRange.lower == sdfPxRange.upper) { if (sdfPxRange.lower == sdfPxRange.upper) {
for (int y = 0; y < output.height; ++y) { 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); Vector2 scale((double) sdf.width/output.width, (double) sdf.height/output.height);
if (sdfPxRange.lower == sdfPxRange.upper) { if (sdfPxRange.lower == sdfPxRange.upper) {
for (int y = 0; y < output.height; ++y) { 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); Vector2 scale((double) sdf.width/output.width, (double) sdf.height/output.height);
if (sdfPxRange.lower == sdfPxRange.upper) { if (sdfPxRange.lower == sdfPxRange.upper) {
for (int y = 0; y < output.height; ++y) { 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); Vector2 scale((double) sdf.width/output.width, (double) sdf.height/output.height);
if (sdfPxRange.lower == sdfPxRange.upper) { if (sdfPxRange.lower == sdfPxRange.upper) {
for (int y = 0; y < output.height; ++y) { 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); Vector2 scale((double) sdf.width/output.width, (double) sdf.height/output.height);
if (sdfPxRange.lower == sdfPxRange.upper) { if (sdfPxRange.lower == sdfPxRange.upper) {
for (int y = 0; y < output.height; ++y) { 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); Vector2 scale((double) sdf.width/output.width, (double) sdf.height/output.height);
if (sdfPxRange.lower == sdfPxRange.upper) { if (sdfPxRange.lower == sdfPxRange.upper) {
for (int y = 0; y < output.height; ++y) { 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; const float *end = bitmap.pixels+1*bitmap.width*bitmap.height;
for (float *p = bitmap.pixels; p < end; ++p) for (float *p = bitmap.pixels; p < end; ++p)
*p = pixelByteToFloat(pixelFloatToByte(*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; const float *end = bitmap.pixels+3*bitmap.width*bitmap.height;
for (float *p = bitmap.pixels; p < end; ++p) for (float *p = bitmap.pixels; p < end; ++p)
*p = pixelByteToFloat(pixelFloatToByte(*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; const float *end = bitmap.pixels+4*bitmap.width*bitmap.height;
for (float *p = bitmap.pixels; p < end; ++p) for (float *p = bitmap.pixels; p < end; ++p)
*p = pixelByteToFloat(pixelFloatToByte(*p)); *p = pixelByteToFloat(pixelFloatToByte(*p));

View File

@@ -8,16 +8,16 @@
namespace msdfgen { namespace msdfgen {
/// Reconstructs the shape's appearance into output from the distance field sdf. /// 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 BitmapSection<float, 1> &output, const BitmapConstSection<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 BitmapSection<float, 3> &output, const BitmapConstSection<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 BitmapSection<float, 1> &output, const BitmapConstSection<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 BitmapSection<float, 3> &output, const BitmapConstSection<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 BitmapSection<float, 1> &output, const BitmapConstSection<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, 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. /// 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 BitmapSection<float, 1> &bitmap);
void simulate8bit(const BitmapRef<float, 3> &bitmap); void simulate8bit(const BitmapSection<float, 3> &bitmap);
void simulate8bit(const BitmapRef<float, 4> &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 { 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)) if (!(sdf.width > 0 && sdf.height > 0))
return line.setIntersections(std::vector<Scanline::Intersection>()); return line.setIntersections(std::vector<Scanline::Intersection>());
double pixelY = clamp(projection.projectY(y)-.5, double(sdf.height-1)); double pixelY = clamp(projection.projectY(y)-.5, double(sdf.height-1));
if (inverseYAxis) if (yAxisOrientation == Y_DOWNWARD)
pixelY = sdf.height-1-pixelY; pixelY = sdf.height-1-pixelY;
int b = (int) floor(pixelY); int b = (int) floor(pixelY);
int t = b+1; int t = b+1;
@@ -46,11 +46,11 @@ void scanlineSDF(Scanline &line, const BitmapConstRef<float, 1> &sdf, const Proj
} }
template <int N> 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)) if (!(sdf.width > 0 && sdf.height > 0))
return line.setIntersections(std::vector<Scanline::Intersection>()); return line.setIntersections(std::vector<Scanline::Intersection>());
double pixelY = clamp(projection.projectY(y)-.5, double(sdf.height-1)); double pixelY = clamp(projection.projectY(y)-.5, double(sdf.height-1));
if (inverseYAxis) if (yAxisOrientation == Y_DOWNWARD)
pixelY = sdf.height-1-pixelY; pixelY = sdf.height-1-pixelY;
int b = (int) floor(pixelY); int b = (int) floor(pixelY);
int t = b+1; int t = b+1;
@@ -124,15 +124,15 @@ void scanlineMSDF(Scanline &line, const BitmapConstRef<float, N> &sdf, const Pro
#endif #endif
} }
void scanlineSDF(Scanline &line, const BitmapConstRef<float, 3> &sdf, const Projection &projection, double y, bool inverseYAxis) { void scanlineSDF(Scanline &line, const BitmapConstSection<float, 3> &sdf, const Projection &projection, double y, YAxisOrientation yAxisOrientation) {
scanlineMSDF(line, sdf, projection, y, inverseYAxis); scanlineMSDF(line, sdf, projection, y, yAxisOrientation);
} }
void scanlineSDF(Scanline &line, const BitmapConstRef<float, 4> &sdf, const Projection &projection, double y, bool inverseYAxis) { void scanlineSDF(Scanline &line, const BitmapConstSection<float, 4> &sdf, const Projection &projection, double y, YAxisOrientation yAxisOrientation) {
scanlineMSDF(line, sdf, projection, y, inverseYAxis); scanlineMSDF(line, sdf, projection, y, yAxisOrientation);
} }
template <int N> 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) if (sdf.width <= 1 || sdf.height <= 1 || scanlinesPerRow < 1)
return 0; return 0;
double subRowSize = 1./scanlinesPerRow; double subRowSize = 1./scanlinesPerRow;
@@ -146,46 +146,58 @@ double estimateSDFErrorInner(const BitmapConstRef<float, N> &sdf, const Shape &s
double bt = (subRow+.5)*subRowSize; double bt = (subRow+.5)*subRowSize;
double y = projection.unprojectY(row+bt+.5); double y = projection.unprojectY(row+bt+.5);
shape.scanline(refScanline, y); 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); error += 1-overlapFactor*Scanline::overlap(refScanline, sdfScanline, xFrom, xTo, fillRule);
} }
} }
return error/((sdf.height-1)*scanlinesPerRow); 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); 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); 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); return estimateSDFErrorInner(sdf, shape, projection, scanlinesPerRow, fillRule);
} }
// Legacy API // 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); 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); 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); 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); 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); 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); return estimateSDFError(sdf, shape, Projection(scale, translate), scanlinesPerRow, fillRule);
} }

View File

@@ -10,21 +10,24 @@
namespace msdfgen { namespace msdfgen {
/// Analytically constructs a scanline at y evaluating fill by linear interpolation of the SDF. /// 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 BitmapConstSection<float, 1> &sdf, const Projection &projection, double y, YAxisOrientation yAxisOrientation = MSDFGEN_Y_AXIS_DEFAULT_ORIENTATION);
void scanlineSDF(Scanline &line, const BitmapConstRef<float, 3> &sdf, const Projection &projection, double y, bool inverseYAxis = false); 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 BitmapConstRef<float, 4> &sdf, const Projection &projection, double y, bool inverseYAxis = false); 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. /// 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 BitmapConstSection<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 BitmapConstSection<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, 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 // 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 BitmapConstSection<float, 1> &sdf, const Projection &projection, double y, bool 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 Projection &projection, double y, bool 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 Projection &projection, double y, bool inverseYAxis);
double estimateSDFError(const BitmapConstRef<float, 1> &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 Vector2 &scale, const Vector2 &translate, bool inverseYAxis, double y);
double estimateSDFError(const BitmapConstRef<float, 3> &sdf, const Shape &shape, const Vector2 &scale, const Vector2 &translate, int scanlinesPerRow, FillRule fillRule = FILL_NONZERO); void scanlineSDF(Scanline &line, const BitmapConstSection<float, 3> &sdf, const Vector2 &scale, const Vector2 &translate, bool inverseYAxis, double y);
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, 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; 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) { static bool writeCoord(FILE *output, Point2 coord) {
fprintf(output, "%.12g, %.12g", coord.x, coord.y); fprintf(output, "%.12g, %.12g", coord.x, coord.y);
return true; 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 readShapeDescription(FILE *input, Shape &output, bool *colorsSpecified) {
bool locColorsSpec = false; bool locColorsSpec = false;
output.contours.clear(); output.contours.clear();
output.inverseYAxis = false; output.setYAxisOrientation(MSDFGEN_Y_AXIS_DEFAULT_ORIENTATION);
Point2 p; Point2 p;
int result = readCoordF(input, p); int result = readCoordF(input, p);
if (result == 2) { if (result == 2) {
@@ -187,9 +198,21 @@ bool readShapeDescription(FILE *input, Shape &output, bool *colorsSpecified) {
int c = readCharF(input); int c = readCharF(input);
if (c == '@') { if (c == '@') {
char after = '\0'; 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; return feof(input) != 0;
output.inverseYAxis = true;
c = after; c = after;
if (c == ' ' || c == '\t' || c == '\r' || c == '\n') if (c == ' ' || c == '\t' || c == '\r' || c == '\n')
c = readCharF(input); 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 readShapeDescription(const char *input, Shape &output, bool *colorsSpecified) {
bool locColorsSpec = false; bool locColorsSpec = false;
output.contours.clear(); output.contours.clear();
output.inverseYAxis = false; output.setYAxisOrientation(MSDFGEN_Y_AXIS_DEFAULT_ORIENTATION);
Point2 p; Point2 p;
int result = readCoordS(&input, p); int result = readCoordS(&input, p);
if (result == 2) { if (result == 2) {
@@ -216,11 +239,14 @@ bool readShapeDescription(const char *input, Shape &output, bool *colorsSpecifie
else { else {
int c = readCharS(&input); int c = readCharS(&input);
if (c == '@') { if (c == '@') {
for (int i = 0; i < (int) sizeof("invert-y")-1; ++i) if (matchStringS(&input, "y-down"))
if (input[i] != "invert-y"[i]) output.setYAxisOrientation(Y_DOWNWARD);
return false; else if (matchStringS(&input, "y-up"))
output.inverseYAxis = true; output.setYAxisOrientation(Y_UPWARD);
input += sizeof("invert-y")-1; else if (matchStringS(&input, "invert-y"))
output.inverseYAxis = true;
else
return false;
c = readCharS(&input); c = readCharS(&input);
} }
for (; c == '{'; c = readCharS(&input)) for (; c == '{'; c = readCharS(&input))
@@ -244,8 +270,14 @@ bool writeShapeDescription(FILE *output, const Shape &shape) {
if (!shape.validate()) if (!shape.validate())
return false; return false;
bool writeColors = isColored(shape); bool writeColors = isColored(shape);
if (shape.inverseYAxis) switch (shape.getYAxisOrientation()) {
fprintf(output, "@invert-y\n"); 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) { for (std::vector<Contour>::const_iterator contour = shape.contours.begin(); contour != shape.contours.end(); ++contour) {
fprintf(output, "{\n"); fprintf(output, "{\n");
if (!contour->edges.empty()) { if (!contour->edges.empty()) {

View File

@@ -34,45 +34,40 @@
#include "core/render-sdf.h" #include "core/render-sdf.h"
#include "core/rasterization.h" #include "core/rasterization.h"
#include "core/sdf-error-estimation.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/shape-description.h"
#include "core/export-svg.h"
namespace msdfgen { namespace msdfgen {
/// Generates a conventional single-channel signed distance field. /// 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. /// 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) /// 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. /// 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 // 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 generateSDF(const BitmapSection<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 generatePSDF(const BitmapSection<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 generatePseudoSDF(const BitmapSection<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 generateMSDF(const BitmapSection<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 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 generateSDF(const BitmapSection<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 generatePSDF(const BitmapSection<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 generatePseudoSDF(const BitmapSection<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 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 BitmapRef<float, 4> &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. // 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 generateSDF_legacy(BitmapSection<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 generatePSDF_legacy(BitmapSection<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 generatePseudoSDF_legacy(BitmapSection<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 generateMSDF_legacy(BitmapSection<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 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 {