mirror of https://github.com/Chlumsky/msdfgen.git
Bitmap refactor
This commit is contained in:
parent
fe19381bcc
commit
997e42f734
|
|
@ -1,5 +1,5 @@
|
||||||
|
|
||||||
## Version 1.6 (2019-04-06)
|
## Version 1.6 (2019-04-08)
|
||||||
|
|
||||||
- Core algorithm rewritten to split up advanced edge selection logic into modular template arguments.
|
- Core algorithm rewritten to split up advanced edge selection logic into modular template arguments.
|
||||||
- Pseudo-distance evaluation reworked to eliminate discontinuities at the midpoint between edges.
|
- Pseudo-distance evaluation reworked to eliminate discontinuities at the midpoint between edges.
|
||||||
|
|
|
||||||
|
|
@ -291,6 +291,8 @@
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="core\arithmetics.hpp" />
|
<ClInclude Include="core\arithmetics.hpp" />
|
||||||
<ClInclude Include="core\Bitmap.h" />
|
<ClInclude Include="core\Bitmap.h" />
|
||||||
|
<ClInclude Include="core\Bitmap.hpp" />
|
||||||
|
<ClInclude Include="core\BitmapRef.hpp" />
|
||||||
<ClInclude Include="core\contour-combiners.h" />
|
<ClInclude Include="core\contour-combiners.h" />
|
||||||
<ClInclude Include="core\Contour.h" />
|
<ClInclude Include="core\Contour.h" />
|
||||||
<ClInclude Include="core\edge-coloring.h" />
|
<ClInclude Include="core\edge-coloring.h" />
|
||||||
|
|
@ -299,6 +301,7 @@
|
||||||
<ClInclude Include="core\EdgeColor.h" />
|
<ClInclude Include="core\EdgeColor.h" />
|
||||||
<ClInclude Include="core\EdgeHolder.h" />
|
<ClInclude Include="core\EdgeHolder.h" />
|
||||||
<ClInclude Include="core\equation-solver.h" />
|
<ClInclude Include="core\equation-solver.h" />
|
||||||
|
<ClInclude Include="core\pixel-conversion.hpp" />
|
||||||
<ClInclude Include="core\rasterization.h" />
|
<ClInclude Include="core\rasterization.h" />
|
||||||
<ClInclude Include="core\render-sdf.h" />
|
<ClInclude Include="core\render-sdf.h" />
|
||||||
<ClInclude Include="core\save-bmp.h" />
|
<ClInclude Include="core\save-bmp.h" />
|
||||||
|
|
@ -315,7 +318,6 @@
|
||||||
<ClInclude Include="resource.h" />
|
<ClInclude Include="resource.h" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClCompile Include="core\Bitmap.cpp" />
|
|
||||||
<ClCompile Include="core\contour-combiners.cpp" />
|
<ClCompile Include="core\contour-combiners.cpp" />
|
||||||
<ClCompile Include="core\Contour.cpp" />
|
<ClCompile Include="core\Contour.cpp" />
|
||||||
<ClCompile Include="core\edge-coloring.cpp" />
|
<ClCompile Include="core\edge-coloring.cpp" />
|
||||||
|
|
|
||||||
|
|
@ -90,14 +90,20 @@
|
||||||
<ClInclude Include="core\rasterization.h">
|
<ClInclude Include="core\rasterization.h">
|
||||||
<Filter>Core</Filter>
|
<Filter>Core</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="core\BitmapRef.hpp">
|
||||||
|
<Filter>Core</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="core\Bitmap.hpp">
|
||||||
|
<Filter>Core</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="core\pixel-conversion.hpp">
|
||||||
|
<Filter>Core</Filter>
|
||||||
|
</ClInclude>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClCompile Include="main.cpp">
|
<ClCompile Include="main.cpp">
|
||||||
<Filter>Standalone</Filter>
|
<Filter>Standalone</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="core\Bitmap.cpp">
|
|
||||||
<Filter>Core</Filter>
|
|
||||||
</ClCompile>
|
|
||||||
<ClCompile Include="core\Contour.cpp">
|
<ClCompile Include="core\Contour.cpp">
|
||||||
<Filter>Core</Filter>
|
<Filter>Core</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
|
|
||||||
|
|
@ -1,77 +0,0 @@
|
||||||
|
|
||||||
#include "Bitmap.h"
|
|
||||||
|
|
||||||
#include <cstring>
|
|
||||||
|
|
||||||
namespace msdfgen {
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
Bitmap<T>::Bitmap() : content(NULL), w(0), h(0) { }
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
Bitmap<T>::Bitmap(int width, int height) : w(width), h(height) {
|
|
||||||
content = new T[w*h];
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
Bitmap<T>::Bitmap(const Bitmap<T> &orig) : w(orig.w), h(orig.h) {
|
|
||||||
content = new T[w*h];
|
|
||||||
memcpy(content, orig.content, w*h*sizeof(T));
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef MSDFGEN_USE_CPP11
|
|
||||||
template <typename T>
|
|
||||||
Bitmap<T>::Bitmap(Bitmap<T> &&orig) : content(orig.content), w(orig.w), h(orig.h) {
|
|
||||||
orig.content = NULL;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
Bitmap<T>::~Bitmap() {
|
|
||||||
delete [] content;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
Bitmap<T> & Bitmap<T>::operator=(const Bitmap<T> &orig) {
|
|
||||||
delete [] content;
|
|
||||||
w = orig.w, h = orig.h;
|
|
||||||
content = new T[w*h];
|
|
||||||
memcpy(content, orig.content, w*h*sizeof(T));
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef MSDFGEN_USE_CPP11
|
|
||||||
template <typename T>
|
|
||||||
Bitmap<T> & Bitmap<T>::operator=(Bitmap<T> &&orig) {
|
|
||||||
delete [] content;
|
|
||||||
content = orig.content;
|
|
||||||
w = orig.w, h = orig.h;
|
|
||||||
orig.content = NULL;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
int Bitmap<T>::width() const {
|
|
||||||
return w;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
int Bitmap<T>::height() const {
|
|
||||||
return h;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
T & Bitmap<T>::operator()(int x, int y) {
|
|
||||||
return content[y*w+x];
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
const T & Bitmap<T>::operator()(int x, int y) const {
|
|
||||||
return content[y*w+x];
|
|
||||||
}
|
|
||||||
|
|
||||||
template class Bitmap<float>;
|
|
||||||
template class Bitmap<FloatRGB>;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,40 +1,45 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "BitmapRef.hpp"
|
||||||
|
|
||||||
namespace msdfgen {
|
namespace msdfgen {
|
||||||
|
|
||||||
/// A floating-point RGB pixel.
|
/// A 2D image bitmap with N channels of type T. Pixel memory is managed by the class.
|
||||||
struct FloatRGB {
|
template <typename T, int N = 1>
|
||||||
float r, g, b;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// A 2D image bitmap.
|
|
||||||
template <typename T>
|
|
||||||
class Bitmap {
|
class Bitmap {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Bitmap();
|
Bitmap();
|
||||||
Bitmap(int width, int height);
|
Bitmap(int width, int height);
|
||||||
Bitmap(const Bitmap<T> &orig);
|
Bitmap(const BitmapConstRef<T, N> &orig);
|
||||||
|
Bitmap(const Bitmap<T, N> &orig);
|
||||||
#ifdef MSDFGEN_USE_CPP11
|
#ifdef MSDFGEN_USE_CPP11
|
||||||
Bitmap(Bitmap<T> &&orig);
|
Bitmap(Bitmap<T, N> &&orig);
|
||||||
#endif
|
#endif
|
||||||
~Bitmap();
|
~Bitmap();
|
||||||
Bitmap<T> & operator=(const Bitmap<T> &orig);
|
Bitmap<T, N> & operator=(const BitmapConstRef<T, N> &orig);
|
||||||
|
Bitmap<T, N> & operator=(const Bitmap<T, N> &orig);
|
||||||
#ifdef MSDFGEN_USE_CPP11
|
#ifdef MSDFGEN_USE_CPP11
|
||||||
Bitmap<T> & operator=(Bitmap<T> &&orig);
|
Bitmap<T, N> & operator=(Bitmap<T, N> &&orig);
|
||||||
#endif
|
#endif
|
||||||
/// Bitmap width in pixels.
|
/// Bitmap width in pixels.
|
||||||
int width() const;
|
int width() const;
|
||||||
/// Bitmap height in pixels.
|
/// Bitmap height in pixels.
|
||||||
int height() const;
|
int height() const;
|
||||||
T & operator()(int x, int y);
|
T * operator()(int x, int y);
|
||||||
const T & operator()(int x, int y) const;
|
const T * operator()(int x, int y) const;
|
||||||
|
explicit operator T *();
|
||||||
|
explicit operator const T *() const;
|
||||||
|
operator BitmapRef<T, N>();
|
||||||
|
operator BitmapConstRef<T, N>() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
T *content;
|
T *pixels;
|
||||||
int w, h;
|
int w, h;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#include "Bitmap.hpp"
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,117 @@
|
||||||
|
|
||||||
|
#include "Bitmap.h"
|
||||||
|
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
namespace msdfgen {
|
||||||
|
|
||||||
|
template <typename T, int N>
|
||||||
|
Bitmap<T, N>::Bitmap() : pixels(NULL), w(0), h(0) { }
|
||||||
|
|
||||||
|
template <typename T, int N>
|
||||||
|
Bitmap<T, N>::Bitmap(int width, int height) : w(width), h(height) {
|
||||||
|
pixels = new T[N*w*h];
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, int N>
|
||||||
|
Bitmap<T, N>::Bitmap(const BitmapConstRef<T, N> &orig) : w(orig.width), h(orig.height) {
|
||||||
|
pixels = new T[N*w*h];
|
||||||
|
memcpy(pixels, orig.pixels, sizeof(T)*N*w*h);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, int N>
|
||||||
|
Bitmap<T, N>::Bitmap(const Bitmap<T, N> &orig) : w(orig.w), h(orig.h) {
|
||||||
|
pixels = new T[N*w*h];
|
||||||
|
memcpy(pixels, orig.pixels, sizeof(T)*N*w*h);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef MSDFGEN_USE_CPP11
|
||||||
|
template <typename T, int N>
|
||||||
|
Bitmap<T, N>::Bitmap(Bitmap<T, N> &&orig) : pixels(orig.pixels), w(orig.w), h(orig.h) {
|
||||||
|
orig.pixels = NULL;
|
||||||
|
orig.w = 0, orig.h = 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
template <typename T, int N>
|
||||||
|
Bitmap<T, N>::~Bitmap() {
|
||||||
|
delete [] pixels;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, int N>
|
||||||
|
Bitmap<T, N> & Bitmap<T, N>::operator=(const BitmapConstRef<T, N> &orig) {
|
||||||
|
if (pixels != orig.pixels) {
|
||||||
|
delete [] pixels;
|
||||||
|
w = orig.width, h = orig.height;
|
||||||
|
pixels = new T[N*w*h];
|
||||||
|
memcpy(pixels, orig.pixels, sizeof(T)*N*w*h);
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, int N>
|
||||||
|
Bitmap<T, N> & Bitmap<T, N>::operator=(const Bitmap<T, N> &orig) {
|
||||||
|
if (this != &orig) {
|
||||||
|
delete [] pixels;
|
||||||
|
w = orig.w, h = orig.h;
|
||||||
|
pixels = new T[N*w*h];
|
||||||
|
memcpy(pixels, orig.pixels, sizeof(T)*N*w*h);
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef MSDFGEN_USE_CPP11
|
||||||
|
template <typename T, int N>
|
||||||
|
Bitmap<T, N> & Bitmap<T, N>::operator=(Bitmap<T, N> &&orig) {
|
||||||
|
if (this != &orig) {
|
||||||
|
delete [] pixels;
|
||||||
|
pixels = orig.pixels;
|
||||||
|
w = orig.w, h = orig.h;
|
||||||
|
orig.pixels = NULL;
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
template <typename T, int N>
|
||||||
|
int Bitmap<T, N>::width() const {
|
||||||
|
return w;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, int N>
|
||||||
|
int Bitmap<T, N>::height() const {
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, int N>
|
||||||
|
T * Bitmap<T, N>::operator()(int x, int y) {
|
||||||
|
return pixels+N*(w*y+x);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, int N>
|
||||||
|
const T * Bitmap<T, N>::operator()(int x, int y) const {
|
||||||
|
return pixels+N*(w*y+x);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, int N>
|
||||||
|
Bitmap<T, N>::operator T *() {
|
||||||
|
return pixels;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, int N>
|
||||||
|
Bitmap<T, N>::operator const T *() const {
|
||||||
|
return pixels;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, int N>
|
||||||
|
Bitmap<T, N>::operator BitmapRef<T, N>() {
|
||||||
|
return BitmapRef<T, N>(pixels, w, h);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, int N>
|
||||||
|
Bitmap<T, N>::operator BitmapConstRef<T, N>() const {
|
||||||
|
return BitmapConstRef<T, N>(pixels, w, h);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,43 @@
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdlib>
|
||||||
|
|
||||||
|
namespace msdfgen {
|
||||||
|
|
||||||
|
typedef unsigned char byte;
|
||||||
|
|
||||||
|
/// Reference to a 2D image bitmap or a buffer acting as one. Pixel storage not owned or managed by the object.
|
||||||
|
template <typename T, int N = 1>
|
||||||
|
struct BitmapRef {
|
||||||
|
|
||||||
|
T *pixels;
|
||||||
|
int width, height;
|
||||||
|
|
||||||
|
inline BitmapRef() : pixels(NULL), width(0), height(0) { }
|
||||||
|
inline BitmapRef(T *pixels, int width, int height) : pixels(pixels), width(width), height(height) { }
|
||||||
|
|
||||||
|
inline T * operator()(int x, int y) const {
|
||||||
|
return pixels+N*(width*y+x);
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
/// 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 {
|
||||||
|
|
||||||
|
const T *pixels;
|
||||||
|
int width, height;
|
||||||
|
|
||||||
|
inline BitmapConstRef() : pixels(NULL), width(0), height(0) { }
|
||||||
|
inline BitmapConstRef(const T *pixels, int width, int height) : pixels(pixels), width(width), height(height) { }
|
||||||
|
inline BitmapConstRef(const BitmapRef<T, N> &orig) : pixels(orig.pixels), width(orig.width), height(orig.height) { }
|
||||||
|
|
||||||
|
inline const T * operator()(int x, int y) const {
|
||||||
|
return pixels+N*(width*y+x);
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -32,7 +32,7 @@ void SimpleContourCombiner<EdgeSelector>::reset(const Point2 &p) {
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class EdgeSelector>
|
template <class EdgeSelector>
|
||||||
void SimpleContourCombiner<EdgeSelector>::setContourEdge(int i, const EdgeSelector &edgeSelector) {
|
void SimpleContourCombiner<EdgeSelector>::setContourEdgeSelection(int i, const EdgeSelector &edgeSelector) {
|
||||||
shapeEdgeSelector.merge(edgeSelector);
|
shapeEdgeSelector.merge(edgeSelector);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -61,7 +61,7 @@ void OverlappingContourCombiner<EdgeSelector>::reset(const Point2 &p) {
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class EdgeSelector>
|
template <class EdgeSelector>
|
||||||
void OverlappingContourCombiner<EdgeSelector>::setContourEdge(int i, const EdgeSelector &edgeSelector) {
|
void OverlappingContourCombiner<EdgeSelector>::setContourEdgeSelection(int i, const EdgeSelector &edgeSelector) {
|
||||||
DistanceType edgeDistance = edgeSelector.distance();
|
DistanceType edgeDistance = edgeSelector.distance();
|
||||||
edgeSelectors[i] = edgeSelector;
|
edgeSelectors[i] = edgeSelector;
|
||||||
shapeEdgeSelector.merge(edgeSelector);
|
shapeEdgeSelector.merge(edgeSelector);
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ public:
|
||||||
|
|
||||||
explicit SimpleContourCombiner(const Shape &shape);
|
explicit SimpleContourCombiner(const Shape &shape);
|
||||||
void reset(const Point2 &p);
|
void reset(const Point2 &p);
|
||||||
void setContourEdge(int i, const EdgeSelector &edgeSelector);
|
void setContourEdgeSelection(int i, const EdgeSelector &edgeSelector);
|
||||||
DistanceType distance() const;
|
DistanceType distance() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
@ -34,7 +34,7 @@ public:
|
||||||
|
|
||||||
explicit OverlappingContourCombiner(const Shape &shape);
|
explicit OverlappingContourCombiner(const Shape &shape);
|
||||||
void reset(const Point2 &p);
|
void reset(const Point2 &p);
|
||||||
void setContourEdge(int i, const EdgeSelector &edgeSelector);
|
void setContourEdgeSelection(int i, const EdgeSelector &edgeSelector);
|
||||||
DistanceType distance() const;
|
DistanceType distance() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
||||||
|
|
@ -82,7 +82,10 @@ Vector2 LinearSegment::direction(double param) const {
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector2 QuadraticSegment::direction(double param) const {
|
Vector2 QuadraticSegment::direction(double param) const {
|
||||||
return mix(p[1]-p[0], p[2]-p[1], param);
|
Vector2 tangent = mix(p[1]-p[0], p[2]-p[1], param);
|
||||||
|
if (!tangent)
|
||||||
|
return p[2]-p[0];
|
||||||
|
return tangent;
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector2 CubicSegment::direction(double param) const {
|
Vector2 CubicSegment::direction(double param) const {
|
||||||
|
|
@ -119,19 +122,21 @@ SignedDistance QuadraticSegment::signedDistance(Point2 origin, double ¶m) co
|
||||||
double t[3];
|
double t[3];
|
||||||
int solutions = solveCubic(t, a, b, c, d);
|
int solutions = solveCubic(t, a, b, c, d);
|
||||||
|
|
||||||
double minDistance = nonZeroSign(crossProduct(ab, qa))*qa.length(); // distance from A
|
Vector2 epDir = direction(0);
|
||||||
param = -dotProduct(qa, ab)/dotProduct(ab, ab);
|
double minDistance = nonZeroSign(crossProduct(epDir, qa))*qa.length(); // distance from A
|
||||||
|
param = -dotProduct(qa, epDir)/dotProduct(epDir, epDir);
|
||||||
{
|
{
|
||||||
double distance = nonZeroSign(crossProduct(p[2]-p[1], p[2]-origin))*(p[2]-origin).length(); // distance from B
|
epDir = direction(1);
|
||||||
|
double distance = nonZeroSign(crossProduct(epDir, p[2]-origin))*(p[2]-origin).length(); // distance from B
|
||||||
if (fabs(distance) < fabs(minDistance)) {
|
if (fabs(distance) < fabs(minDistance)) {
|
||||||
minDistance = distance;
|
minDistance = distance;
|
||||||
param = dotProduct(origin-p[1], p[2]-p[1])/dotProduct(p[2]-p[1], p[2]-p[1]);
|
param = dotProduct(origin-p[1], epDir)/dotProduct(epDir, epDir);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (int i = 0; i < solutions; ++i) {
|
for (int i = 0; i < solutions; ++i) {
|
||||||
if (t[i] > 0 && t[i] < 1) {
|
if (t[i] > 0 && t[i] < 1) {
|
||||||
Point2 endpoint = p[0]+2*t[i]*ab+t[i]*t[i]*br;
|
Point2 qe = p[0]+2*t[i]*ab+t[i]*t[i]*br-origin;
|
||||||
double distance = nonZeroSign(crossProduct(p[2]-p[0], endpoint-origin))*(endpoint-origin).length();
|
double distance = nonZeroSign(crossProduct(p[2]-p[0], qe))*qe.length();
|
||||||
if (fabs(distance) <= fabs(minDistance)) {
|
if (fabs(distance) <= fabs(minDistance)) {
|
||||||
minDistance = distance;
|
minDistance = distance;
|
||||||
param = t[i];
|
param = t[i];
|
||||||
|
|
@ -142,9 +147,9 @@ SignedDistance QuadraticSegment::signedDistance(Point2 origin, double ¶m) co
|
||||||
if (param >= 0 && param <= 1)
|
if (param >= 0 && param <= 1)
|
||||||
return SignedDistance(minDistance, 0);
|
return SignedDistance(minDistance, 0);
|
||||||
if (param < .5)
|
if (param < .5)
|
||||||
return SignedDistance(minDistance, fabs(dotProduct(ab.normalize(), qa.normalize())));
|
return SignedDistance(minDistance, fabs(dotProduct(direction(0).normalize(), qa.normalize())));
|
||||||
else
|
else
|
||||||
return SignedDistance(minDistance, fabs(dotProduct((p[2]-p[1]).normalize(), (p[2]-origin).normalize())));
|
return SignedDistance(minDistance, fabs(dotProduct(direction(1).normalize(), (p[2]-origin).normalize())));
|
||||||
}
|
}
|
||||||
|
|
||||||
SignedDistance CubicSegment::signedDistance(Point2 origin, double ¶m) const {
|
SignedDistance CubicSegment::signedDistance(Point2 origin, double ¶m) const {
|
||||||
|
|
@ -161,15 +166,15 @@ SignedDistance CubicSegment::signedDistance(Point2 origin, double ¶m) const
|
||||||
double distance = nonZeroSign(crossProduct(epDir, p[3]-origin))*(p[3]-origin).length(); // distance from B
|
double distance = nonZeroSign(crossProduct(epDir, p[3]-origin))*(p[3]-origin).length(); // distance from B
|
||||||
if (fabs(distance) < fabs(minDistance)) {
|
if (fabs(distance) < fabs(minDistance)) {
|
||||||
minDistance = distance;
|
minDistance = distance;
|
||||||
param = dotProduct(origin+epDir-p[3], 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 = (double) i/MSDFGEN_CUBIC_SEARCH_STARTS;
|
||||||
for (int step = 0;; ++step) {
|
for (int step = 0;; ++step) {
|
||||||
Vector2 qpt = point(t)-origin;
|
Vector2 qe = p[0]+3*t*ab+3*t*t*br+t*t*t*as-origin; // do not simplify with qa !!!
|
||||||
double distance = nonZeroSign(crossProduct(direction(t), qpt))*qpt.length();
|
double distance = nonZeroSign(crossProduct(direction(t), qe))*qe.length();
|
||||||
if (fabs(distance) < fabs(minDistance)) {
|
if (fabs(distance) < fabs(minDistance)) {
|
||||||
minDistance = distance;
|
minDistance = distance;
|
||||||
param = t;
|
param = t;
|
||||||
|
|
@ -179,7 +184,7 @@ SignedDistance CubicSegment::signedDistance(Point2 origin, double ¶m) const
|
||||||
// Improve t
|
// Improve t
|
||||||
Vector2 d1 = 3*as*t*t+6*br*t+3*ab;
|
Vector2 d1 = 3*as*t*t+6*br*t+3*ab;
|
||||||
Vector2 d2 = 6*as*t+6*br;
|
Vector2 d2 = 6*as*t+6*br;
|
||||||
t -= dotProduct(qpt, d1)/(dotProduct(d1, d1)+dotProduct(qpt, d2));
|
t -= dotProduct(qe, d1)/(dotProduct(d1, d1)+dotProduct(qe, d2));
|
||||||
if (t < 0 || t > 1)
|
if (t < 0 || t > 1)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -103,7 +103,7 @@ void MultiDistanceSelector::addEdge(const EdgeSegment *prevEdge, const EdgeSegme
|
||||||
g.addEdgeTrueDistance(edge, distance, param);
|
g.addEdgeTrueDistance(edge, distance, param);
|
||||||
if (edge->color&BLUE)
|
if (edge->color&BLUE)
|
||||||
b.addEdgeTrueDistance(edge, distance, param);
|
b.addEdgeTrueDistance(edge, distance, param);
|
||||||
if (PseudoDistanceSelector::pointFacingEdge(prevEdge, edge, nextEdge, p, param)) {
|
if (PseudoDistanceSelectorBase::pointFacingEdge(prevEdge, edge, nextEdge, p, param)) {
|
||||||
edge->distanceToPseudoDistance(distance, p, param);
|
edge->distanceToPseudoDistance(distance, p, param);
|
||||||
if (edge->color&RED)
|
if (edge->color&RED)
|
||||||
r.addEdgePseudoDistance(distance);
|
r.addEdgePseudoDistance(distance);
|
||||||
|
|
|
||||||
|
|
@ -13,29 +13,25 @@ class DistancePixelConversion;
|
||||||
template <>
|
template <>
|
||||||
class DistancePixelConversion<double> {
|
class DistancePixelConversion<double> {
|
||||||
public:
|
public:
|
||||||
typedef float PixelType;
|
typedef BitmapRef<float, 1> BitmapRefType;
|
||||||
inline static PixelType convert(double distance, double range) {
|
inline static void convert(float *pixels, double distance, double range) {
|
||||||
return PixelType(distance/range+.5);
|
*pixels = float(distance/range+.5);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
class DistancePixelConversion<MultiDistance> {
|
class DistancePixelConversion<MultiDistance> {
|
||||||
public:
|
public:
|
||||||
typedef FloatRGB PixelType;
|
typedef BitmapRef<float, 3> BitmapRefType;
|
||||||
inline static PixelType convert(const MultiDistance &distance, double range) {
|
inline static void convert(float *pixels, const MultiDistance &distance, double range) {
|
||||||
PixelType pixel;
|
pixels[0] = float(distance.r/range+.5);
|
||||||
pixel.r = float(distance.r/range+.5);
|
pixels[1] = float(distance.g/range+.5);
|
||||||
pixel.g = float(distance.g/range+.5);
|
pixels[2] = float(distance.b/range+.5);
|
||||||
pixel.b = float(distance.b/range+.5);
|
|
||||||
return pixel;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <class ContourCombiner>
|
template <class ContourCombiner>
|
||||||
void generateDistanceField(Bitmap<typename DistancePixelConversion<typename ContourCombiner::DistanceType>::PixelType> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate) {
|
void generateDistanceField(const typename DistancePixelConversion<typename ContourCombiner::DistanceType>::BitmapRefType &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate) {
|
||||||
int w = output.width(), h = output.height();
|
|
||||||
|
|
||||||
#ifdef MSDFGEN_USE_OPENMP
|
#ifdef MSDFGEN_USE_OPENMP
|
||||||
#pragma omp parallel
|
#pragma omp parallel
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -45,10 +41,10 @@ void generateDistanceField(Bitmap<typename DistancePixelConversion<typename Cont
|
||||||
#ifdef MSDFGEN_USE_OPENMP
|
#ifdef MSDFGEN_USE_OPENMP
|
||||||
#pragma omp for
|
#pragma omp for
|
||||||
#endif
|
#endif
|
||||||
for (int y = 0; y < h; ++y) {
|
for (int y = 0; y < output.height; ++y) {
|
||||||
int row = shape.inverseYAxis ? h-y-1 : y;
|
int row = shape.inverseYAxis ? output.height-y-1 : y;
|
||||||
p.y = (y+.5)/scale.y-translate.y;
|
p.y = (y+.5)/scale.y-translate.y;
|
||||||
for (int x = 0; x < w; ++x) {
|
for (int x = 0; x < output.width; ++x) {
|
||||||
p.x = (x+.5)/scale.x-translate.x;
|
p.x = (x+.5)/scale.x-translate.x;
|
||||||
|
|
||||||
contourCombiner.reset(p);
|
contourCombiner.reset(p);
|
||||||
|
|
@ -66,32 +62,32 @@ void generateDistanceField(Bitmap<typename DistancePixelConversion<typename Cont
|
||||||
curEdge = nextEdge;
|
curEdge = nextEdge;
|
||||||
}
|
}
|
||||||
|
|
||||||
contourCombiner.setContourEdge(int(contour-shape.contours.begin()), edgeSelector);
|
contourCombiner.setContourEdgeSelection(int(contour-shape.contours.begin()), edgeSelector);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
typename ContourCombiner::DistanceType distance = contourCombiner.distance();
|
typename ContourCombiner::DistanceType distance = contourCombiner.distance();
|
||||||
output(x, row) = DistancePixelConversion<typename ContourCombiner::DistanceType>::convert(distance, range);
|
DistancePixelConversion<typename ContourCombiner::DistanceType>::convert(output(x, row), distance, range);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void generateSDF(Bitmap<float> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, bool overlapSupport) {
|
void generateSDF(const BitmapRef<float, 1> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, bool overlapSupport) {
|
||||||
if (overlapSupport)
|
if (overlapSupport)
|
||||||
generateDistanceField<OverlappingContourCombiner<TrueDistanceSelector> >(output, shape, range, scale, translate);
|
generateDistanceField<OverlappingContourCombiner<TrueDistanceSelector> >(output, shape, range, scale, translate);
|
||||||
else
|
else
|
||||||
generateDistanceField<SimpleContourCombiner<TrueDistanceSelector> >(output, shape, range, scale, translate);
|
generateDistanceField<SimpleContourCombiner<TrueDistanceSelector> >(output, shape, range, scale, translate);
|
||||||
}
|
}
|
||||||
|
|
||||||
void generatePseudoSDF(Bitmap<float> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, bool overlapSupport) {
|
void generatePseudoSDF(const BitmapRef<float, 1> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, bool overlapSupport) {
|
||||||
if (overlapSupport)
|
if (overlapSupport)
|
||||||
generateDistanceField<OverlappingContourCombiner<PseudoDistanceSelector> >(output, shape, range, scale, translate);
|
generateDistanceField<OverlappingContourCombiner<PseudoDistanceSelector> >(output, shape, range, scale, translate);
|
||||||
else
|
else
|
||||||
generateDistanceField<SimpleContourCombiner<PseudoDistanceSelector> >(output, shape, range, scale, translate);
|
generateDistanceField<SimpleContourCombiner<PseudoDistanceSelector> >(output, shape, range, scale, translate);
|
||||||
}
|
}
|
||||||
|
|
||||||
void generateMSDF(Bitmap<FloatRGB> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, double edgeThreshold, bool overlapSupport) {
|
void generateMSDF(const BitmapRef<float, 3> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, double edgeThreshold, bool overlapSupport) {
|
||||||
if (overlapSupport)
|
if (overlapSupport)
|
||||||
generateDistanceField<OverlappingContourCombiner<MultiDistanceSelector> >(output, shape, range, scale, translate);
|
generateDistanceField<OverlappingContourCombiner<MultiDistanceSelector> >(output, shape, range, scale, translate);
|
||||||
else
|
else
|
||||||
|
|
@ -100,10 +96,10 @@ void generateMSDF(Bitmap<FloatRGB> &output, const Shape &shape, double range, co
|
||||||
msdfErrorCorrection(output, edgeThreshold/(scale*range));
|
msdfErrorCorrection(output, edgeThreshold/(scale*range));
|
||||||
}
|
}
|
||||||
|
|
||||||
inline static bool detectClash(const FloatRGB &a, const FloatRGB &b, double threshold) {
|
inline static bool detectClash(const float *a, const float *b, double threshold) {
|
||||||
// Sort channels so that pairs (a0, b0), (a1, b1), (a2, b2) go from biggest to smallest absolute difference
|
// Sort channels so that pairs (a0, b0), (a1, b1), (a2, b2) go from biggest to smallest absolute difference
|
||||||
float a0 = a.r, a1 = a.g, a2 = a.b;
|
float a0 = a[0], a1 = a[1], a2 = a[2];
|
||||||
float b0 = b.r, b1 = b.g, b2 = b.b;
|
float b0 = b[0], b1 = b[1], b2 = b[2];
|
||||||
float tmp;
|
float tmp;
|
||||||
if (fabsf(b0-a0) < fabsf(b1-a1)) {
|
if (fabsf(b0-a0) < fabsf(b1-a1)) {
|
||||||
tmp = a0, a0 = a1, a1 = tmp;
|
tmp = a0, a0 = a1, a1 = tmp;
|
||||||
|
|
@ -122,9 +118,9 @@ inline static bool detectClash(const FloatRGB &a, const FloatRGB &b, double thre
|
||||||
fabsf(a2-.5f) >= fabsf(b2-.5f); // Out of the pair, only flag the pixel farther from a shape edge
|
fabsf(a2-.5f) >= fabsf(b2-.5f); // Out of the pair, only flag the pixel farther from a shape edge
|
||||||
}
|
}
|
||||||
|
|
||||||
void msdfErrorCorrection(Bitmap<FloatRGB> &output, const Vector2 &threshold) {
|
void msdfErrorCorrection(const BitmapRef<float, 3> &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)
|
||||||
for (int x = 0; x < w; ++x) {
|
for (int x = 0; x < w; ++x) {
|
||||||
if (
|
if (
|
||||||
|
|
@ -136,9 +132,9 @@ void msdfErrorCorrection(Bitmap<FloatRGB> &output, const Vector2 &threshold) {
|
||||||
clashes.push_back(std::make_pair(x, y));
|
clashes.push_back(std::make_pair(x, y));
|
||||||
}
|
}
|
||||||
for (std::vector<std::pair<int, int> >::const_iterator clash = clashes.begin(); clash != clashes.end(); ++clash) {
|
for (std::vector<std::pair<int, int> >::const_iterator clash = clashes.begin(); clash != clashes.end(); ++clash) {
|
||||||
FloatRGB &pixel = output(clash->first, clash->second);
|
float *pixel = output(clash->first, clash->second);
|
||||||
float med = median(pixel.r, pixel.g, pixel.b);
|
float med = median(pixel[0], pixel[1], pixel[2]);
|
||||||
pixel.r = med, pixel.g = med, pixel.b = med;
|
pixel[0] = med, pixel[1] = med, pixel[2] = med;
|
||||||
}
|
}
|
||||||
#ifndef MSDFGEN_NO_DIAGONAL_CLASH_DETECTION
|
#ifndef MSDFGEN_NO_DIAGONAL_CLASH_DETECTION
|
||||||
clashes.clear();
|
clashes.clear();
|
||||||
|
|
@ -153,23 +149,22 @@ void msdfErrorCorrection(Bitmap<FloatRGB> &output, const Vector2 &threshold) {
|
||||||
clashes.push_back(std::make_pair(x, y));
|
clashes.push_back(std::make_pair(x, y));
|
||||||
}
|
}
|
||||||
for (std::vector<std::pair<int, int> >::const_iterator clash = clashes.begin(); clash != clashes.end(); ++clash) {
|
for (std::vector<std::pair<int, int> >::const_iterator clash = clashes.begin(); clash != clashes.end(); ++clash) {
|
||||||
FloatRGB &pixel = output(clash->first, clash->second);
|
float *pixel = output(clash->first, clash->second);
|
||||||
float med = median(pixel.r, pixel.g, pixel.b);
|
float med = median(pixel[0], pixel[1], pixel[2]);
|
||||||
pixel.r = med, pixel.g = med, pixel.b = med;
|
pixel[0] = med, pixel[1] = med, pixel[2] = med;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// Legacy version
|
// Legacy version
|
||||||
|
|
||||||
void generateSDF_legacy(Bitmap<float> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate) {
|
void generateSDF_legacy(const BitmapRef<float, 1> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate) {
|
||||||
int w = output.width(), h = output.height();
|
|
||||||
#ifdef MSDFGEN_USE_OPENMP
|
#ifdef MSDFGEN_USE_OPENMP
|
||||||
#pragma omp parallel for
|
#pragma omp parallel for
|
||||||
#endif
|
#endif
|
||||||
for (int y = 0; y < h; ++y) {
|
for (int y = 0; y < output.height; ++y) {
|
||||||
int row = shape.inverseYAxis ? h-y-1 : y;
|
int row = shape.inverseYAxis ? output.height-y-1 : y;
|
||||||
for (int x = 0; x < w; ++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;
|
||||||
SignedDistance minDistance;
|
SignedDistance minDistance;
|
||||||
|
|
@ -179,19 +174,18 @@ void generateSDF_legacy(Bitmap<float> &output, const Shape &shape, double range,
|
||||||
if (distance < minDistance)
|
if (distance < minDistance)
|
||||||
minDistance = distance;
|
minDistance = distance;
|
||||||
}
|
}
|
||||||
output(x, row) = float(minDistance.distance/range+.5);
|
*output(x, row) = float(minDistance.distance/range+.5);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void generatePseudoSDF_legacy(Bitmap<float> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate) {
|
void generatePseudoSDF_legacy(const BitmapRef<float, 1> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate) {
|
||||||
int w = output.width(), h = output.height();
|
|
||||||
#ifdef MSDFGEN_USE_OPENMP
|
#ifdef MSDFGEN_USE_OPENMP
|
||||||
#pragma omp parallel for
|
#pragma omp parallel for
|
||||||
#endif
|
#endif
|
||||||
for (int y = 0; y < h; ++y) {
|
for (int y = 0; y < output.height; ++y) {
|
||||||
int row = shape.inverseYAxis ? h-y-1 : y;
|
int row = shape.inverseYAxis ? output.height-y-1 : y;
|
||||||
for (int x = 0; x < w; ++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;
|
||||||
const EdgeHolder *nearEdge = NULL;
|
const EdgeHolder *nearEdge = NULL;
|
||||||
|
|
@ -208,19 +202,18 @@ void generatePseudoSDF_legacy(Bitmap<float> &output, const Shape &shape, double
|
||||||
}
|
}
|
||||||
if (nearEdge)
|
if (nearEdge)
|
||||||
(*nearEdge)->distanceToPseudoDistance(minDistance, p, nearParam);
|
(*nearEdge)->distanceToPseudoDistance(minDistance, p, nearParam);
|
||||||
output(x, row) = float(minDistance.distance/range+.5);
|
*output(x, row) = float(minDistance.distance/range+.5);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void generateMSDF_legacy(Bitmap<FloatRGB> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, double edgeThreshold) {
|
void generateMSDF_legacy(const BitmapRef<float, 3> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, double edgeThreshold) {
|
||||||
int w = output.width(), h = output.height();
|
|
||||||
#ifdef MSDFGEN_USE_OPENMP
|
#ifdef MSDFGEN_USE_OPENMP
|
||||||
#pragma omp parallel for
|
#pragma omp parallel for
|
||||||
#endif
|
#endif
|
||||||
for (int y = 0; y < h; ++y) {
|
for (int y = 0; y < output.height; ++y) {
|
||||||
int row = shape.inverseYAxis ? h-y-1 : y;
|
int row = shape.inverseYAxis ? output.height-y-1 : y;
|
||||||
for (int x = 0; x < w; ++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;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
|
|
@ -258,9 +251,9 @@ void generateMSDF_legacy(Bitmap<FloatRGB> &output, const Shape &shape, double ra
|
||||||
(*g.nearEdge)->distanceToPseudoDistance(g.minDistance, p, g.nearParam);
|
(*g.nearEdge)->distanceToPseudoDistance(g.minDistance, p, g.nearParam);
|
||||||
if (b.nearEdge)
|
if (b.nearEdge)
|
||||||
(*b.nearEdge)->distanceToPseudoDistance(b.minDistance, p, b.nearParam);
|
(*b.nearEdge)->distanceToPseudoDistance(b.minDistance, p, b.nearParam);
|
||||||
output(x, row).r = float(r.minDistance.distance/range+.5);
|
output(x, row)[0] = float(r.minDistance.distance/range+.5);
|
||||||
output(x, row).g = float(g.minDistance.distance/range+.5);
|
output(x, row)[1] = float(g.minDistance.distance/range+.5);
|
||||||
output(x, row).b = float(b.minDistance.distance/range+.5);
|
output(x, row)[2] = float(b.minDistance.distance/range+.5);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "arithmetics.hpp"
|
||||||
|
|
||||||
|
namespace msdfgen {
|
||||||
|
|
||||||
|
typedef unsigned char byte;
|
||||||
|
|
||||||
|
inline byte pixelFloatToByte(float x) {
|
||||||
|
return byte(clamp(256.f*x, 255.f));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline float pixelByteToFloat(byte x) {
|
||||||
|
return 1.f/255.f*float(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -21,44 +21,42 @@ static bool interpretFillRule(int intersections, FillRule fillRule) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void rasterize(Bitmap<float> &output, const Shape &shape, const Vector2 &scale, const Vector2 &translate, FillRule fillRule) {
|
void rasterize(const BitmapRef<float, 1> &output, const Shape &shape, const Vector2 &scale, const Vector2 &translate, FillRule fillRule) {
|
||||||
int w = output.width(), h = output.height();
|
|
||||||
Point2 p;
|
Point2 p;
|
||||||
Scanline scanline;
|
Scanline scanline;
|
||||||
for (int y = 0; y < h; ++y) {
|
for (int y = 0; y < output.height; ++y) {
|
||||||
int row = shape.inverseYAxis ? h-y-1 : y;
|
int row = shape.inverseYAxis ? output.height-y-1 : y;
|
||||||
p.y = (y+.5)/scale.y-translate.y;
|
p.y = (y+.5)/scale.y-translate.y;
|
||||||
shape.scanline(scanline, p.y);
|
shape.scanline(scanline, p.y);
|
||||||
for (int x = 0; x < w; ++x) {
|
for (int x = 0; x < output.width; ++x) {
|
||||||
p.x = (x+.5)/scale.x-translate.x;
|
p.x = (x+.5)/scale.x-translate.x;
|
||||||
int intersections = scanline.sumIntersections(p.x);
|
int intersections = scanline.sumIntersections(p.x);
|
||||||
bool fill = interpretFillRule(intersections, fillRule);
|
bool fill = interpretFillRule(intersections, fillRule);
|
||||||
output(x, row) = (float) fill;
|
*output(x, row) = (float) fill;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void distanceSignCorrection(Bitmap<float> &sdf, const Shape &shape, const Vector2 &scale, const Vector2 &translate, FillRule fillRule) {
|
void distanceSignCorrection(const BitmapRef<float, 1> &sdf, const Shape &shape, const Vector2 &scale, const Vector2 &translate, FillRule fillRule) {
|
||||||
int w = sdf.width(), h = sdf.height();
|
|
||||||
Point2 p;
|
Point2 p;
|
||||||
Scanline scanline;
|
Scanline scanline;
|
||||||
for (int y = 0; y < h; ++y) {
|
for (int y = 0; y < sdf.height; ++y) {
|
||||||
int row = shape.inverseYAxis ? h-y-1 : y;
|
int row = shape.inverseYAxis ? sdf.height-y-1 : y;
|
||||||
p.y = (y+.5)/scale.y-translate.y;
|
p.y = (y+.5)/scale.y-translate.y;
|
||||||
shape.scanline(scanline, p.y);
|
shape.scanline(scanline, p.y);
|
||||||
for (int x = 0; x < w; ++x) {
|
for (int x = 0; x < sdf.width; ++x) {
|
||||||
p.x = (x+.5)/scale.x-translate.x;
|
p.x = (x+.5)/scale.x-translate.x;
|
||||||
int intersections = scanline.sumIntersections(p.x);
|
int intersections = scanline.sumIntersections(p.x);
|
||||||
bool fill = interpretFillRule(intersections, fillRule);
|
bool fill = interpretFillRule(intersections, fillRule);
|
||||||
float &sd = sdf(x, row);
|
float &sd = *sdf(x, row);
|
||||||
if ((sd > .5f) != fill)
|
if ((sd > .5f) != fill)
|
||||||
sd = 1.f-sd;
|
sd = 1.f-sd;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void distanceSignCorrection(Bitmap<FloatRGB> &sdf, const Shape &shape, const Vector2 &scale, const Vector2 &translate, FillRule fillRule) {
|
void distanceSignCorrection(const BitmapRef<float, 3> &sdf, const Shape &shape, const Vector2 &scale, const Vector2 &translate, FillRule fillRule) {
|
||||||
int w = sdf.width(), h = sdf.height();
|
int w = sdf.width, h = sdf.height;
|
||||||
if (!(w*h))
|
if (!(w*h))
|
||||||
return;
|
return;
|
||||||
Point2 p;
|
Point2 p;
|
||||||
|
|
@ -75,14 +73,14 @@ void distanceSignCorrection(Bitmap<FloatRGB> &sdf, const Shape &shape, const Vec
|
||||||
p.x = (x+.5)/scale.x-translate.x;
|
p.x = (x+.5)/scale.x-translate.x;
|
||||||
int intersections = scanline.sumIntersections(p.x);
|
int intersections = scanline.sumIntersections(p.x);
|
||||||
bool fill = interpretFillRule(intersections, fillRule);
|
bool fill = interpretFillRule(intersections, fillRule);
|
||||||
FloatRGB &msd = sdf(x, row);
|
float *msd = sdf(x, row);
|
||||||
float sd = median(msd.r, msd.g, msd.b);
|
float sd = median(msd[0], msd[1], msd[2]);
|
||||||
if (sd == .5f)
|
if (sd == .5f)
|
||||||
ambiguous = true;
|
ambiguous = true;
|
||||||
else if ((sd > .5f) != fill) {
|
else if ((sd > .5f) != fill) {
|
||||||
msd.r = 1.f-msd.r;
|
msd[0] = 1.f-msd[0];
|
||||||
msd.g = 1.f-msd.g;
|
msd[1] = 1.f-msd[1];
|
||||||
msd.b = 1.f-msd.b;
|
msd[2] = 1.f-msd[2];
|
||||||
*match = -1;
|
*match = -1;
|
||||||
} else
|
} else
|
||||||
*match = 1;
|
*match = 1;
|
||||||
|
|
@ -102,10 +100,10 @@ void distanceSignCorrection(Bitmap<FloatRGB> &sdf, const Shape &shape, const Vec
|
||||||
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) {
|
||||||
FloatRGB &msd = sdf(x, row);
|
float *msd = sdf(x, row);
|
||||||
msd.r = 1.f-msd.r;
|
msd[0] = 1.f-msd[0];
|
||||||
msd.g = 1.f-msd.g;
|
msd[1] = 1.f-msd[1];
|
||||||
msd.b = 1.f-msd.b;
|
msd[2] = 1.f-msd[2];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
++match;
|
++match;
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
#include "Vector2.h"
|
#include "Vector2.h"
|
||||||
#include "Shape.h"
|
#include "Shape.h"
|
||||||
#include "Bitmap.h"
|
#include "BitmapRef.hpp"
|
||||||
|
|
||||||
namespace msdfgen {
|
namespace msdfgen {
|
||||||
|
|
||||||
|
|
@ -16,9 +16,9 @@ enum FillRule {
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Rasterizes the shape into a monochrome bitmap.
|
/// Rasterizes the shape into a monochrome bitmap.
|
||||||
void rasterize(Bitmap<float> &output, const Shape &shape, const Vector2 &scale, const Vector2 &translate, FillRule fillRule = FILL_NONZERO);
|
void rasterize(const BitmapRef<float, 1> &output, const Shape &shape, const Vector2 &scale, const Vector2 &translate, 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(Bitmap<float> &sdf, 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(Bitmap<FloatRGB> &sdf, const Shape &shape, const Vector2 &scale, const Vector2 &translate, FillRule fillRule = FILL_NONZERO);
|
void distanceSignCorrection(const BitmapRef<float, 3> &sdf, const Shape &shape, const Vector2 &scale, const Vector2 &translate, FillRule fillRule = FILL_NONZERO);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,106 +2,87 @@
|
||||||
#include "render-sdf.h"
|
#include "render-sdf.h"
|
||||||
|
|
||||||
#include "arithmetics.hpp"
|
#include "arithmetics.hpp"
|
||||||
|
#include "pixel-conversion.hpp"
|
||||||
|
|
||||||
namespace msdfgen {
|
namespace msdfgen {
|
||||||
|
|
||||||
template <typename S>
|
template <typename T, int N>
|
||||||
inline FloatRGB mix(FloatRGB a, FloatRGB b, S weight) {
|
static void sample(T *output, const BitmapConstRef<T, N> &bitmap, Point2 pos) {
|
||||||
FloatRGB output = {
|
double x = pos.x*bitmap.width-.5;
|
||||||
mix(a.r, b.r, weight),
|
double y = pos.y*bitmap.height-.5;
|
||||||
mix(a.g, b.g, weight),
|
|
||||||
mix(a.b, b.b, weight)
|
|
||||||
};
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
static T sample(const Bitmap<T> &bitmap, Point2 pos) {
|
|
||||||
int w = bitmap.width(), h = bitmap.height();
|
|
||||||
double x = pos.x*w-.5;
|
|
||||||
double y = pos.y*h-.5;
|
|
||||||
int l = (int) floor(x);
|
int l = (int) floor(x);
|
||||||
int b = (int) floor(y);
|
int b = (int) floor(y);
|
||||||
int r = l+1;
|
int r = l+1;
|
||||||
int t = b+1;
|
int t = b+1;
|
||||||
double lr = x-l;
|
double lr = x-l;
|
||||||
double bt = y-b;
|
double bt = y-b;
|
||||||
l = clamp(l, w-1), r = clamp(r, w-1);
|
l = clamp(l, bitmap.width-1), r = clamp(r, bitmap.width-1);
|
||||||
b = clamp(b, h-1), t = clamp(t, h-1);
|
b = clamp(b, bitmap.height-1), t = clamp(t, bitmap.height-1);
|
||||||
return mix(mix(bitmap(l, b), bitmap(r, b), lr), mix(bitmap(l, t), bitmap(r, t), lr), bt);
|
for (int i = 0; i < N; ++i)
|
||||||
|
output[i] = mix(mix(bitmap(l, b)[i], bitmap(r, b)[i], lr), mix(bitmap(l, t)[i], bitmap(r, t)[i], lr), bt);
|
||||||
}
|
}
|
||||||
|
|
||||||
static float distVal(float dist, double pxRange) {
|
static float distVal(float dist, double pxRange) {
|
||||||
if (!pxRange)
|
if (!pxRange)
|
||||||
return dist > .5f;
|
return (float) (dist > .5f);
|
||||||
return (float) clamp((dist-.5f)*pxRange+.5);
|
return (float) clamp((dist-.5f)*pxRange+.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
void renderSDF(Bitmap<float> &output, const Bitmap<float> &sdf, double pxRange) {
|
void renderSDF(const BitmapRef<float, 1> &output, const BitmapConstRef<float, 1> &sdf, double pxRange) {
|
||||||
int w = output.width(), h = output.height();
|
pxRange *= (double) (output.width+output.height)/(sdf.width+sdf.height);
|
||||||
pxRange *= (double) (w+h)/(sdf.width()+sdf.height());
|
for (int y = 0; y < output.height; ++y)
|
||||||
for (int y = 0; y < h; ++y)
|
for (int x = 0; x < output.width; ++x) {
|
||||||
for (int x = 0; x < w; ++x) {
|
float sd;
|
||||||
float s = sample(sdf, Point2((x+.5)/w, (y+.5)/h));
|
sample(&sd, sdf, Point2((x+.5)/output.width, (y+.5)/output.height));
|
||||||
output(x, y) = distVal(s, pxRange);
|
*output(x, y) = distVal(sd, pxRange);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void renderSDF(Bitmap<FloatRGB> &output, const Bitmap<float> &sdf, double pxRange) {
|
void renderSDF(const BitmapRef<float, 3> &output, const BitmapConstRef<float, 1> &sdf, double pxRange) {
|
||||||
int w = output.width(), h = output.height();
|
pxRange *= (double) (output.width+output.height)/(sdf.width+sdf.height);
|
||||||
pxRange *= (double) (w+h)/(sdf.width()+sdf.height());
|
for (int y = 0; y < output.height; ++y)
|
||||||
for (int y = 0; y < h; ++y)
|
for (int x = 0; x < output.width; ++x) {
|
||||||
for (int x = 0; x < w; ++x) {
|
float sd;
|
||||||
float s = sample(sdf, Point2((x+.5)/w, (y+.5)/h));
|
sample(&sd, sdf, Point2((x+.5)/output.width, (y+.5)/output.height));
|
||||||
float v = distVal(s, pxRange);
|
float v = distVal(sd, pxRange);
|
||||||
output(x, y).r = v;
|
output(x, y)[0] = v;
|
||||||
output(x, y).g = v;
|
output(x, y)[1] = v;
|
||||||
output(x, y).b = v;
|
output(x, y)[2] = v;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void renderSDF(Bitmap<float> &output, const Bitmap<FloatRGB> &sdf, double pxRange) {
|
void renderSDF(const BitmapRef<float, 1> &output, const BitmapConstRef<float, 3> &sdf, double pxRange) {
|
||||||
int w = output.width(), h = output.height();
|
pxRange *= (double) (output.width+output.height)/(sdf.width+sdf.height);
|
||||||
pxRange *= (double) (w+h)/(sdf.width()+sdf.height());
|
for (int y = 0; y < output.height; ++y)
|
||||||
for (int y = 0; y < h; ++y)
|
for (int x = 0; x < output.width; ++x) {
|
||||||
for (int x = 0; x < w; ++x) {
|
float sd[3];
|
||||||
FloatRGB s = sample(sdf, Point2((x+.5)/w, (y+.5)/h));
|
sample(sd, sdf, Point2((x+.5)/output.width, (y+.5)/output.height));
|
||||||
output(x, y) = distVal(median(s.r, s.g, s.b), pxRange);
|
*output(x, y) = distVal(median(sd[0], sd[1], sd[2]), pxRange);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void renderSDF(Bitmap<FloatRGB> &output, const Bitmap<FloatRGB> &sdf, double pxRange) {
|
void renderSDF(const BitmapRef<float, 3> &output, const BitmapConstRef<float, 3> &sdf, double pxRange) {
|
||||||
int w = output.width(), h = output.height();
|
pxRange *= (double) (output.width+output.height)/(sdf.width+sdf.height);
|
||||||
pxRange *= (double) (w+h)/(sdf.width()+sdf.height());
|
for (int y = 0; y < output.height; ++y)
|
||||||
for (int y = 0; y < h; ++y)
|
for (int x = 0; x < output.width; ++x) {
|
||||||
for (int x = 0; x < w; ++x) {
|
float sd[3];
|
||||||
FloatRGB s = sample(sdf, Point2((x+.5)/w, (y+.5)/h));
|
sample(sd, sdf, Point2((x+.5)/output.width, (y+.5)/output.height));
|
||||||
output(x, y).r = distVal(s.r, pxRange);
|
output(x, y)[0] = distVal(sd[0], pxRange);
|
||||||
output(x, y).g = distVal(s.g, pxRange);
|
output(x, y)[1] = distVal(sd[1], pxRange);
|
||||||
output(x, y).b = distVal(s.b, pxRange);
|
output(x, y)[2] = distVal(sd[2], pxRange);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void simulate8bit(Bitmap<float> &bitmap) {
|
void simulate8bit(const BitmapRef<float, 1> &bitmap) {
|
||||||
int w = bitmap.width(), h = bitmap.height();
|
const float *end = bitmap.pixels+1*bitmap.width*bitmap.height;
|
||||||
for (int y = 0; y < h; ++y)
|
for (float *p = bitmap.pixels; p < end; ++p)
|
||||||
for (int x = 0; x < w; ++x) {
|
*p = pixelByteToFloat(pixelFloatToByte(*p));
|
||||||
unsigned char v = clamp(int(bitmap(x, y)*0x100), 0xff);
|
|
||||||
bitmap(x, y) = v/255.f;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void simulate8bit(Bitmap<FloatRGB> &bitmap) {
|
void simulate8bit(const BitmapRef<float, 3> &bitmap) {
|
||||||
int w = bitmap.width(), h = bitmap.height();
|
const float *end = bitmap.pixels+3*bitmap.width*bitmap.height;
|
||||||
for (int y = 0; y < h; ++y)
|
for (float *p = bitmap.pixels; p < end; ++p)
|
||||||
for (int x = 0; x < w; ++x) {
|
*p = pixelByteToFloat(pixelFloatToByte(*p));
|
||||||
unsigned char r = clamp(int(bitmap(x, y).r*0x100), 0xff);
|
|
||||||
unsigned char g = clamp(int(bitmap(x, y).g*0x100), 0xff);
|
|
||||||
unsigned char b = clamp(int(bitmap(x, y).b*0x100), 0xff);
|
|
||||||
bitmap(x, y).r = r/255.f;
|
|
||||||
bitmap(x, y).g = g/255.f;
|
|
||||||
bitmap(x, y).b = b/255.f;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,18 +2,18 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "Vector2.h"
|
#include "Vector2.h"
|
||||||
#include "Bitmap.h"
|
#include "BitmapRef.hpp"
|
||||||
|
|
||||||
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(Bitmap<float> &output, const Bitmap<float> &sdf, double pxRange = 0);
|
void renderSDF(const BitmapRef<float, 1> &output, const BitmapConstRef<float, 1> &sdf, double pxRange = 0);
|
||||||
void renderSDF(Bitmap<FloatRGB> &output, const Bitmap<float> &sdf, double pxRange = 0);
|
void renderSDF(const BitmapRef<float, 3> &output, const BitmapConstRef<float, 1> &sdf, double pxRange = 0);
|
||||||
void renderSDF(Bitmap<float> &output, const Bitmap<FloatRGB> &sdf, double pxRange = 0);
|
void renderSDF(const BitmapRef<float, 1> &output, const BitmapConstRef<float, 3> &sdf, double pxRange = 0);
|
||||||
void renderSDF(Bitmap<FloatRGB> &output, const Bitmap<FloatRGB> &sdf, double pxRange = 0);
|
void renderSDF(const BitmapRef<float, 3> &output, const BitmapConstRef<float, 3> &sdf, double pxRange = 0);
|
||||||
|
|
||||||
/// 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(Bitmap<float> &bitmap);
|
void simulate8bit(const BitmapRef<float, 1> &bitmap);
|
||||||
void simulate8bit(Bitmap<FloatRGB> &bitmap);
|
void simulate8bit(const BitmapRef<float, 3> &bitmap);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
|
|
||||||
#include "save-bmp.h"
|
|
||||||
|
|
||||||
#define _CRT_SECURE_NO_WARNINGS
|
#define _CRT_SECURE_NO_WARNINGS
|
||||||
|
|
||||||
|
#include "save-bmp.h"
|
||||||
|
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
|
|
||||||
#ifdef MSDFGEN_USE_CPP11
|
#ifdef MSDFGEN_USE_CPP11
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
typedef unsigned char uint8_t;
|
typedef unsigned char uint8_t;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "arithmetics.hpp"
|
#include "pixel-conversion.hpp"
|
||||||
|
|
||||||
namespace msdfgen {
|
namespace msdfgen {
|
||||||
|
|
||||||
|
|
@ -60,47 +60,97 @@ static bool writeBmpHeader(FILE *file, int width, int height, int &paddedWidth)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool saveBmp(const Bitmap<float> &bitmap, const char *filename) {
|
bool saveBmp(const BitmapConstRef<byte, 1> &bitmap, const char *filename) {
|
||||||
FILE *file = fopen(filename, "wb");
|
FILE *file = fopen(filename, "wb");
|
||||||
if (!file)
|
if (!file)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
int paddedWidth;
|
int paddedWidth;
|
||||||
writeBmpHeader(file, bitmap.width(), bitmap.height(), paddedWidth);
|
writeBmpHeader(file, bitmap.width, bitmap.height, paddedWidth);
|
||||||
|
|
||||||
const uint8_t padding[4] = { };
|
const uint8_t padding[4] = { };
|
||||||
for (int y = 0; y < bitmap.height(); ++y) {
|
int padLength = paddedWidth-3*bitmap.width;
|
||||||
for (int x = 0; x < bitmap.width(); ++x) {
|
for (int y = 0; y < bitmap.height; ++y) {
|
||||||
uint8_t px = (uint8_t) clamp(int(bitmap(x, y)*0x100), 0xff);
|
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(&px, sizeof(uint8_t), 1, file);
|
||||||
fwrite(&px, sizeof(uint8_t), 1, file);
|
fwrite(&px, sizeof(uint8_t), 1, file);
|
||||||
}
|
}
|
||||||
fwrite(padding, 1, paddedWidth-3*bitmap.width(), file);
|
fwrite(padding, 1, padLength, file);
|
||||||
}
|
}
|
||||||
|
|
||||||
return !fclose(file);
|
return !fclose(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool saveBmp(const Bitmap<FloatRGB> &bitmap, const char *filename) {
|
bool saveBmp(const BitmapConstRef<byte, 3> &bitmap, const char *filename) {
|
||||||
FILE *file = fopen(filename, "wb");
|
FILE *file = fopen(filename, "wb");
|
||||||
if (!file)
|
if (!file)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
int paddedWidth;
|
int paddedWidth;
|
||||||
writeBmpHeader(file, bitmap.width(), bitmap.height(), paddedWidth);
|
writeBmpHeader(file, bitmap.width, bitmap.height, paddedWidth);
|
||||||
|
|
||||||
const uint8_t padding[4] = { };
|
const uint8_t padding[4] = { };
|
||||||
for (int y = 0; y < bitmap.height(); ++y) {
|
int padLength = paddedWidth-3*bitmap.width;
|
||||||
for (int x = 0; x < bitmap.width(); ++x) {
|
for (int y = 0; y < bitmap.height; ++y) {
|
||||||
|
for (int x = 0; x < bitmap.width; ++x) {
|
||||||
uint8_t bgr[3] = {
|
uint8_t bgr[3] = {
|
||||||
(uint8_t) clamp(int(bitmap(x, y).b*0x100), 0xff),
|
(uint8_t) bitmap(x, y)[2],
|
||||||
(uint8_t) clamp(int(bitmap(x, y).g*0x100), 0xff),
|
(uint8_t) bitmap(x, y)[1],
|
||||||
(uint8_t) clamp(int(bitmap(x, y).r*0x100), 0xff)
|
(uint8_t) bitmap(x, y)[0]
|
||||||
};
|
};
|
||||||
fwrite(bgr, sizeof(uint8_t), 3, file);
|
fwrite(bgr, sizeof(uint8_t), 3, file);
|
||||||
}
|
}
|
||||||
fwrite(padding, 1, paddedWidth-3*bitmap.width(), file);
|
fwrite(padding, 1, padLength, file);
|
||||||
|
}
|
||||||
|
|
||||||
|
return !fclose(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
return !fclose(file);
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,14 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "Bitmap.h"
|
#include "BitmapRef.hpp"
|
||||||
|
|
||||||
namespace msdfgen {
|
namespace msdfgen {
|
||||||
|
|
||||||
/// Saves the bitmap as a BMP file.
|
/// Saves the bitmap as a BMP file.
|
||||||
bool saveBmp(const Bitmap<float> &bitmap, const char *filename);
|
bool saveBmp(const BitmapConstRef<byte, 1> &bitmap, const char *filename);
|
||||||
bool saveBmp(const Bitmap<FloatRGB> &bitmap, const char *filename);
|
bool saveBmp(const BitmapConstRef<byte, 3> &bitmap, const char *filename);
|
||||||
|
bool saveBmp(const BitmapConstRef<float, 1> &bitmap, const char *filename);
|
||||||
|
bool saveBmp(const BitmapConstRef<float, 3> &bitmap, const char *filename);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,30 +1,38 @@
|
||||||
|
|
||||||
#include "save-png.h"
|
#include "save-png.h"
|
||||||
|
|
||||||
#include "../core/arithmetics.hpp"
|
|
||||||
#include <lodepng.h>
|
#include <lodepng.h>
|
||||||
|
#include "../core/pixel-conversion.hpp"
|
||||||
|
|
||||||
namespace msdfgen {
|
namespace msdfgen {
|
||||||
|
|
||||||
bool savePng(const Bitmap<float> &bitmap, const char *filename) {
|
bool savePng(const BitmapConstRef<byte, 1> &bitmap, const char *filename) {
|
||||||
std::vector<unsigned char> pixels(bitmap.width()*bitmap.height());
|
return !lodepng::encode(filename, bitmap.pixels, bitmap.width, bitmap.height, LCT_GREY);
|
||||||
std::vector<unsigned char>::iterator it = pixels.begin();
|
|
||||||
for (int y = bitmap.height()-1; y >= 0; --y)
|
|
||||||
for (int x = 0; x < bitmap.width(); ++x)
|
|
||||||
*it++ = clamp(int(bitmap(x, y)*0x100), 0xff);
|
|
||||||
return !lodepng::encode(filename, pixels, bitmap.width(), bitmap.height(), LCT_GREY);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool savePng(const Bitmap<FloatRGB> &bitmap, const char *filename) {
|
bool savePng(const BitmapConstRef<byte, 3> &bitmap, const char *filename) {
|
||||||
std::vector<unsigned char> pixels(3*bitmap.width()*bitmap.height());
|
return !lodepng::encode(filename, bitmap.pixels, bitmap.width, bitmap.height, LCT_RGB);
|
||||||
std::vector<unsigned char>::iterator it = pixels.begin();
|
}
|
||||||
for (int y = bitmap.height()-1; y >= 0; --y)
|
|
||||||
for (int x = 0; x < bitmap.width(); ++x) {
|
bool savePng(const BitmapConstRef<float, 1> &bitmap, const char *filename) {
|
||||||
*it++ = clamp(int(bitmap(x, y).r*0x100), 0xff);
|
std::vector<byte> pixels(bitmap.width*bitmap.height);
|
||||||
*it++ = clamp(int(bitmap(x, y).g*0x100), 0xff);
|
std::vector<byte>::iterator it = pixels.begin();
|
||||||
*it++ = clamp(int(bitmap(x, y).b*0x100), 0xff);
|
for (int y = bitmap.height-1; y >= 0; --y)
|
||||||
|
for (int x = 0; x < bitmap.width; ++x)
|
||||||
|
*it++ = pixelFloatToByte(*bitmap(x, y));
|
||||||
|
return !lodepng::encode(filename, pixels, bitmap.width, bitmap.height, LCT_GREY);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool savePng(const BitmapConstRef<float, 3> &bitmap, const char *filename) {
|
||||||
|
std::vector<byte> pixels(3*bitmap.width*bitmap.height);
|
||||||
|
std::vector<byte>::iterator it = pixels.begin();
|
||||||
|
for (int y = bitmap.height-1; y >= 0; --y)
|
||||||
|
for (int x = 0; x < bitmap.width; ++x) {
|
||||||
|
*it++ = pixelFloatToByte(bitmap(x, y)[0]);
|
||||||
|
*it++ = pixelFloatToByte(bitmap(x, y)[1]);
|
||||||
|
*it++ = pixelFloatToByte(bitmap(x, y)[2]);
|
||||||
}
|
}
|
||||||
return !lodepng::encode(filename, pixels, bitmap.width(), bitmap.height(), LCT_RGB);
|
return !lodepng::encode(filename, pixels, bitmap.width, bitmap.height, LCT_RGB);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,14 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "../core/Bitmap.h"
|
#include "../core/BitmapRef.hpp"
|
||||||
|
|
||||||
namespace msdfgen {
|
namespace msdfgen {
|
||||||
|
|
||||||
/// Saves the bitmap as a PNG file.
|
/// Saves the bitmap as a PNG file.
|
||||||
bool savePng(const Bitmap<float> &bitmap, const char *filename);
|
bool savePng(const BitmapConstRef<byte, 1> &bitmap, const char *filename);
|
||||||
bool savePng(const Bitmap<FloatRGB> &bitmap, const char *filename);
|
bool savePng(const BitmapConstRef<byte, 3> &bitmap, const char *filename);
|
||||||
|
bool savePng(const BitmapConstRef<float, 1> &bitmap, const char *filename);
|
||||||
|
bool savePng(const BitmapConstRef<float, 3> &bitmap, const char *filename);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
62
main.cpp
62
main.cpp
|
|
@ -131,19 +131,11 @@ static void parseColoring(Shape &shape, const char *edgeAssignment) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void invertColor(Bitmap<float> &bitmap) {
|
template <int N>
|
||||||
for (int y = 0; y < bitmap.height(); ++y)
|
static void invertColor(const BitmapRef<float, N> &bitmap) {
|
||||||
for (int x = 0; x < bitmap.width(); ++x)
|
const float *end = bitmap.pixels+N*bitmap.width*bitmap.height;
|
||||||
bitmap(x, y) = 1.f-bitmap(x, y);
|
for (float *p = bitmap.pixels; p < end; ++p)
|
||||||
}
|
*p = 1.f-*p;
|
||||||
|
|
||||||
static void invertColor(Bitmap<FloatRGB> &bitmap) {
|
|
||||||
for (int y = 0; y < bitmap.height(); ++y)
|
|
||||||
for (int x = 0; x < bitmap.width(); ++x) {
|
|
||||||
bitmap(x, y).r = 1.f-bitmap(x, y).r;
|
|
||||||
bitmap(x, y).g = 1.f-bitmap(x, y).g;
|
|
||||||
bitmap(x, y).b = 1.f-bitmap(x, y).b;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool writeTextBitmap(FILE *file, const float *values, int cols, int rows) {
|
static bool writeTextBitmap(FILE *file, const float *values, int cols, int rows) {
|
||||||
|
|
@ -205,8 +197,8 @@ static bool cmpExtension(const char *path, const char *ext) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <int N>
|
||||||
static const char * writeOutput(const Bitmap<T> &bitmap, const char *filename, Format format) {
|
static const char * writeOutput(const BitmapConstRef<float, N> &bitmap, const char *filename, Format format) {
|
||||||
if (filename) {
|
if (filename) {
|
||||||
if (format == AUTO) {
|
if (format == AUTO) {
|
||||||
if (cmpExtension(filename, ".png")) format = PNG;
|
if (cmpExtension(filename, ".png")) format = PNG;
|
||||||
|
|
@ -223,9 +215,9 @@ static const char * writeOutput(const Bitmap<T> &bitmap, const char *filename, F
|
||||||
FILE *file = fopen(filename, "w");
|
FILE *file = fopen(filename, "w");
|
||||||
if (!file) return "Failed to write output text file.";
|
if (!file) return "Failed to write output text file.";
|
||||||
if (format == TEXT)
|
if (format == TEXT)
|
||||||
writeTextBitmap(file, reinterpret_cast<const float *>(&bitmap(0, 0)), sizeof(T)/sizeof(float)*bitmap.width(), bitmap.height());
|
writeTextBitmap(file, bitmap.pixels, N*bitmap.width, bitmap.height);
|
||||||
else if (format == TEXT_FLOAT)
|
else if (format == TEXT_FLOAT)
|
||||||
writeTextBitmapFloat(file, reinterpret_cast<const float *>(&bitmap(0, 0)), sizeof(T)/sizeof(float)*bitmap.width(), bitmap.height());
|
writeTextBitmapFloat(file, bitmap.pixels, N*bitmap.width, bitmap.height);
|
||||||
fclose(file);
|
fclose(file);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
@ -233,11 +225,11 @@ static const char * writeOutput(const Bitmap<T> &bitmap, const char *filename, F
|
||||||
FILE *file = fopen(filename, "wb");
|
FILE *file = fopen(filename, "wb");
|
||||||
if (!file) return "Failed to write output binary file.";
|
if (!file) return "Failed to write output binary file.";
|
||||||
if (format == BINARY)
|
if (format == BINARY)
|
||||||
writeBinBitmap(file, reinterpret_cast<const float *>(&bitmap(0, 0)), sizeof(T)/sizeof(float)*bitmap.width()*bitmap.height());
|
writeBinBitmap(file, bitmap.pixels, N*bitmap.width*bitmap.height);
|
||||||
else if (format == BINARY_FLOAT)
|
else if (format == BINARY_FLOAT)
|
||||||
writeBinBitmapFloat(file, reinterpret_cast<const float *>(&bitmap(0, 0)), sizeof(T)/sizeof(float)*bitmap.width()*bitmap.height());
|
writeBinBitmapFloat(file, bitmap.pixels, N*bitmap.width*bitmap.height);
|
||||||
else if (format == BINART_FLOAT_BE)
|
else if (format == BINART_FLOAT_BE)
|
||||||
writeBinBitmapFloatBE(file, reinterpret_cast<const float *>(&bitmap(0, 0)), sizeof(T)/sizeof(float)*bitmap.width()*bitmap.height());
|
writeBinBitmapFloatBE(file, bitmap.pixels, N*bitmap.width*bitmap.height);
|
||||||
fclose(file);
|
fclose(file);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
@ -245,9 +237,9 @@ static const char * writeOutput(const Bitmap<T> &bitmap, const char *filename, F
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (format == AUTO || format == TEXT)
|
if (format == AUTO || format == TEXT)
|
||||||
writeTextBitmap(stdout, reinterpret_cast<const float *>(&bitmap(0, 0)), sizeof(T)/sizeof(float)*bitmap.width(), bitmap.height());
|
writeTextBitmap(stdout, bitmap.pixels, N*bitmap.width, bitmap.height);
|
||||||
else if (format == TEXT_FLOAT)
|
else if (format == TEXT_FLOAT)
|
||||||
writeTextBitmapFloat(stdout, reinterpret_cast<const float *>(&bitmap(0, 0)), sizeof(T)/sizeof(float)*bitmap.width(), bitmap.height());
|
writeTextBitmapFloat(stdout, bitmap.pixels, N*bitmap.width, bitmap.height);
|
||||||
else
|
else
|
||||||
return "Unsupported format for standard output.";
|
return "Unsupported format for standard output.";
|
||||||
}
|
}
|
||||||
|
|
@ -771,11 +763,11 @@ int main(int argc, const char * const *argv) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compute output
|
// Compute output
|
||||||
Bitmap<float> sdf;
|
Bitmap<float, 1> sdf;
|
||||||
Bitmap<FloatRGB> msdf;
|
Bitmap<float, 3> msdf;
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
case SINGLE: {
|
case SINGLE: {
|
||||||
sdf = Bitmap<float>(width, height);
|
sdf = Bitmap<float, 1>(width, height);
|
||||||
if (legacyMode)
|
if (legacyMode)
|
||||||
generateSDF_legacy(sdf, shape, range, scale, translate);
|
generateSDF_legacy(sdf, shape, range, scale, translate);
|
||||||
else
|
else
|
||||||
|
|
@ -783,7 +775,7 @@ int main(int argc, const char * const *argv) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case PSEUDO: {
|
case PSEUDO: {
|
||||||
sdf = Bitmap<float>(width, height);
|
sdf = Bitmap<float, 1>(width, height);
|
||||||
if (legacyMode)
|
if (legacyMode)
|
||||||
generatePseudoSDF_legacy(sdf, shape, range, scale, translate);
|
generatePseudoSDF_legacy(sdf, shape, range, scale, translate);
|
||||||
else
|
else
|
||||||
|
|
@ -795,7 +787,7 @@ int main(int argc, const char * const *argv) {
|
||||||
edgeColoringSimple(shape, angleThreshold, coloringSeed);
|
edgeColoringSimple(shape, angleThreshold, coloringSeed);
|
||||||
if (edgeAssignment)
|
if (edgeAssignment)
|
||||||
parseColoring(shape, edgeAssignment);
|
parseColoring(shape, edgeAssignment);
|
||||||
msdf = Bitmap<FloatRGB>(width, height);
|
msdf = Bitmap<float, 3>(width, height);
|
||||||
if (legacyMode)
|
if (legacyMode)
|
||||||
generateMSDF_legacy(msdf, shape, range, scale, translate, scanlinePass ? 0 : edgeThreshold);
|
generateMSDF_legacy(msdf, shape, range, scale, translate, scanlinePass ? 0 : edgeThreshold);
|
||||||
else
|
else
|
||||||
|
|
@ -822,10 +814,10 @@ int main(int argc, const char * const *argv) {
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
case SINGLE:
|
case SINGLE:
|
||||||
case PSEUDO:
|
case PSEUDO:
|
||||||
invertColor(sdf);
|
invertColor<1>(sdf);
|
||||||
break;
|
break;
|
||||||
case MULTI:
|
case MULTI:
|
||||||
invertColor(msdf);
|
invertColor<3>(msdf);
|
||||||
break;
|
break;
|
||||||
default:;
|
default:;
|
||||||
}
|
}
|
||||||
|
|
@ -858,38 +850,38 @@ int main(int argc, const char * const *argv) {
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
case SINGLE:
|
case SINGLE:
|
||||||
case PSEUDO:
|
case PSEUDO:
|
||||||
error = writeOutput(sdf, output, format);
|
error = writeOutput<1>(sdf, output, format);
|
||||||
if (error)
|
if (error)
|
||||||
ABORT(error);
|
ABORT(error);
|
||||||
if (testRenderMulti || testRender)
|
if (testRenderMulti || testRender)
|
||||||
simulate8bit(sdf);
|
simulate8bit(sdf);
|
||||||
if (testRenderMulti) {
|
if (testRenderMulti) {
|
||||||
Bitmap<FloatRGB> render(testWidthM, testHeightM);
|
Bitmap<float, 3> render(testWidthM, testHeightM);
|
||||||
renderSDF(render, sdf, avgScale*range);
|
renderSDF(render, sdf, avgScale*range);
|
||||||
if (!savePng(render, testRenderMulti))
|
if (!savePng(render, testRenderMulti))
|
||||||
puts("Failed to write test render file.");
|
puts("Failed to write test render file.");
|
||||||
}
|
}
|
||||||
if (testRender) {
|
if (testRender) {
|
||||||
Bitmap<float> render(testWidth, testHeight);
|
Bitmap<float, 1> render(testWidth, testHeight);
|
||||||
renderSDF(render, sdf, avgScale*range);
|
renderSDF(render, sdf, avgScale*range);
|
||||||
if (!savePng(render, testRender))
|
if (!savePng(render, testRender))
|
||||||
puts("Failed to write test render file.");
|
puts("Failed to write test render file.");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case MULTI:
|
case MULTI:
|
||||||
error = writeOutput(msdf, output, format);
|
error = writeOutput<3>(msdf, output, format);
|
||||||
if (error)
|
if (error)
|
||||||
ABORT(error);
|
ABORT(error);
|
||||||
if (testRenderMulti || testRender)
|
if (testRenderMulti || testRender)
|
||||||
simulate8bit(msdf);
|
simulate8bit(msdf);
|
||||||
if (testRenderMulti) {
|
if (testRenderMulti) {
|
||||||
Bitmap<FloatRGB> render(testWidthM, testHeightM);
|
Bitmap<float, 3> render(testWidthM, testHeightM);
|
||||||
renderSDF(render, msdf, avgScale*range);
|
renderSDF(render, msdf, avgScale*range);
|
||||||
if (!savePng(render, testRenderMulti))
|
if (!savePng(render, testRenderMulti))
|
||||||
puts("Failed to write test render file.");
|
puts("Failed to write test render file.");
|
||||||
}
|
}
|
||||||
if (testRender) {
|
if (testRender) {
|
||||||
Bitmap<float> render(testWidth, testHeight);
|
Bitmap<float, 1> render(testWidth, testHeight);
|
||||||
renderSDF(render, msdf, avgScale*range);
|
renderSDF(render, msdf, avgScale*range);
|
||||||
if (!savePng(render, testRender))
|
if (!savePng(render, testRender))
|
||||||
ABORT("Failed to write test render file.");
|
ABORT("Failed to write test render file.");
|
||||||
|
|
|
||||||
15
msdfgen.h
15
msdfgen.h
|
|
@ -18,6 +18,7 @@
|
||||||
#include "core/arithmetics.hpp"
|
#include "core/arithmetics.hpp"
|
||||||
#include "core/Vector2.h"
|
#include "core/Vector2.h"
|
||||||
#include "core/Shape.h"
|
#include "core/Shape.h"
|
||||||
|
#include "core/BitmapRef.hpp"
|
||||||
#include "core/Bitmap.h"
|
#include "core/Bitmap.h"
|
||||||
#include "core/edge-coloring.h"
|
#include "core/edge-coloring.h"
|
||||||
#include "core/render-sdf.h"
|
#include "core/render-sdf.h"
|
||||||
|
|
@ -30,20 +31,20 @@
|
||||||
namespace msdfgen {
|
namespace msdfgen {
|
||||||
|
|
||||||
/// Generates a conventional single-channel signed distance field.
|
/// Generates a conventional single-channel signed distance field.
|
||||||
void generateSDF(Bitmap<float> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, bool overlapSupport = true);
|
void generateSDF(const BitmapRef<float, 1> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, bool overlapSupport = true);
|
||||||
|
|
||||||
/// Generates a single-channel signed pseudo-distance field.
|
/// Generates a single-channel signed pseudo-distance field.
|
||||||
void generatePseudoSDF(Bitmap<float> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, bool overlapSupport = true);
|
void generatePseudoSDF(const BitmapRef<float, 1> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, bool overlapSupport = true);
|
||||||
|
|
||||||
/// 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(Bitmap<FloatRGB> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, double edgeThreshold = 1.001, bool overlapSupport = true);
|
void generateMSDF(const BitmapRef<float, 3> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, double edgeThreshold = 1.001, bool overlapSupport = true);
|
||||||
|
|
||||||
/// Resolves multi-channel signed distance field values that may cause interpolation artifacts. (Already called by generateMSDF)
|
/// Resolves multi-channel signed distance field values that may cause interpolation artifacts. (Already called by generateMSDF)
|
||||||
void msdfErrorCorrection(Bitmap<FloatRGB> &output, const Vector2 &threshold);
|
void msdfErrorCorrection(const BitmapRef<float, 3> &output, const Vector2 &threshold);
|
||||||
|
|
||||||
// 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(Bitmap<float> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate);
|
void generateSDF_legacy(const BitmapRef<float, 1> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate);
|
||||||
void generatePseudoSDF_legacy(Bitmap<float> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate);
|
void generatePseudoSDF_legacy(const BitmapRef<float, 1> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate);
|
||||||
void generateMSDF_legacy(Bitmap<FloatRGB> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, double edgeThreshold = 1.001);
|
void generateMSDF_legacy(const BitmapRef<float, 3> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, double edgeThreshold = 1.001);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue