diff --git a/CHANGELOG.md b/CHANGELOG.md
index a427adb..065fca4 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -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.
- Pseudo-distance evaluation reworked to eliminate discontinuities at the midpoint between edges.
diff --git a/Msdfgen.vcxproj b/Msdfgen.vcxproj
index b4a20f9..7c9e65a 100644
--- a/Msdfgen.vcxproj
+++ b/Msdfgen.vcxproj
@@ -291,6 +291,8 @@
+
+
@@ -299,6 +301,7 @@
+
@@ -315,7 +318,6 @@
-
diff --git a/Msdfgen.vcxproj.filters b/Msdfgen.vcxproj.filters
index 9008ea4..86d5a29 100644
--- a/Msdfgen.vcxproj.filters
+++ b/Msdfgen.vcxproj.filters
@@ -90,14 +90,20 @@
Core
+
+ Core
+
+
+ Core
+
+
+ Core
+
Standalone
-
- Core
-
Core
diff --git a/core/Bitmap.cpp b/core/Bitmap.cpp
deleted file mode 100644
index 645aebc..0000000
--- a/core/Bitmap.cpp
+++ /dev/null
@@ -1,77 +0,0 @@
-
-#include "Bitmap.h"
-
-#include
-
-namespace msdfgen {
-
-template
-Bitmap::Bitmap() : content(NULL), w(0), h(0) { }
-
-template
-Bitmap::Bitmap(int width, int height) : w(width), h(height) {
- content = new T[w*h];
-}
-
-template
-Bitmap::Bitmap(const Bitmap &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
-Bitmap::Bitmap(Bitmap &&orig) : content(orig.content), w(orig.w), h(orig.h) {
- orig.content = NULL;
-}
-#endif
-
-template
-Bitmap::~Bitmap() {
- delete [] content;
-}
-
-template
-Bitmap & Bitmap::operator=(const Bitmap &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
-Bitmap & Bitmap::operator=(Bitmap &&orig) {
- delete [] content;
- content = orig.content;
- w = orig.w, h = orig.h;
- orig.content = NULL;
- return *this;
-}
-#endif
-
-template
-int Bitmap::width() const {
- return w;
-}
-
-template
-int Bitmap::height() const {
- return h;
-}
-
-template
-T & Bitmap::operator()(int x, int y) {
- return content[y*w+x];
-}
-
-template
-const T & Bitmap::operator()(int x, int y) const {
- return content[y*w+x];
-}
-
-template class Bitmap;
-template class Bitmap;
-
-}
diff --git a/core/Bitmap.h b/core/Bitmap.h
index 6ae84ed..3ad23a8 100644
--- a/core/Bitmap.h
+++ b/core/Bitmap.h
@@ -1,40 +1,45 @@
#pragma once
+#include "BitmapRef.hpp"
+
namespace msdfgen {
-/// A floating-point RGB pixel.
-struct FloatRGB {
- float r, g, b;
-};
-
-/// A 2D image bitmap.
-template
+/// A 2D image bitmap with N channels of type T. Pixel memory is managed by the class.
+template
class Bitmap {
public:
Bitmap();
Bitmap(int width, int height);
- Bitmap(const Bitmap &orig);
+ Bitmap(const BitmapConstRef &orig);
+ Bitmap(const Bitmap &orig);
#ifdef MSDFGEN_USE_CPP11
- Bitmap(Bitmap &&orig);
+ Bitmap(Bitmap &&orig);
#endif
~Bitmap();
- Bitmap & operator=(const Bitmap &orig);
+ Bitmap & operator=(const BitmapConstRef &orig);
+ Bitmap & operator=(const Bitmap &orig);
#ifdef MSDFGEN_USE_CPP11
- Bitmap & operator=(Bitmap &&orig);
+ Bitmap & operator=(Bitmap &&orig);
#endif
/// Bitmap width in pixels.
int width() const;
/// Bitmap height in pixels.
int height() const;
- T & operator()(int x, int y);
- const T & operator()(int x, int y) const;
+ T * operator()(int x, int y);
+ const T * operator()(int x, int y) const;
+ explicit operator T *();
+ explicit operator const T *() const;
+ operator BitmapRef();
+ operator BitmapConstRef() const;
private:
- T *content;
+ T *pixels;
int w, h;
};
}
+
+#include "Bitmap.hpp"
diff --git a/core/Bitmap.hpp b/core/Bitmap.hpp
new file mode 100644
index 0000000..cb16cac
--- /dev/null
+++ b/core/Bitmap.hpp
@@ -0,0 +1,117 @@
+
+#include "Bitmap.h"
+
+#include
+#include
+
+namespace msdfgen {
+
+template
+Bitmap::Bitmap() : pixels(NULL), w(0), h(0) { }
+
+template
+Bitmap::Bitmap(int width, int height) : w(width), h(height) {
+ pixels = new T[N*w*h];
+}
+
+template
+Bitmap::Bitmap(const BitmapConstRef &orig) : w(orig.width), h(orig.height) {
+ pixels = new T[N*w*h];
+ memcpy(pixels, orig.pixels, sizeof(T)*N*w*h);
+}
+
+template
+Bitmap::Bitmap(const Bitmap &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
+Bitmap::Bitmap(Bitmap &&orig) : pixels(orig.pixels), w(orig.w), h(orig.h) {
+ orig.pixels = NULL;
+ orig.w = 0, orig.h = 0;
+}
+#endif
+
+template
+Bitmap::~Bitmap() {
+ delete [] pixels;
+}
+
+template
+Bitmap & Bitmap::operator=(const BitmapConstRef &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
+Bitmap & Bitmap::operator=(const Bitmap &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
+Bitmap & Bitmap::operator=(Bitmap &&orig) {
+ if (this != &orig) {
+ delete [] pixels;
+ pixels = orig.pixels;
+ w = orig.w, h = orig.h;
+ orig.pixels = NULL;
+ }
+ return *this;
+}
+#endif
+
+template
+int Bitmap::width() const {
+ return w;
+}
+
+template
+int Bitmap::height() const {
+ return h;
+}
+
+template
+T * Bitmap::operator()(int x, int y) {
+ return pixels+N*(w*y+x);
+}
+
+template
+const T * Bitmap::operator()(int x, int y) const {
+ return pixels+N*(w*y+x);
+}
+
+template
+Bitmap::operator T *() {
+ return pixels;
+}
+
+template
+Bitmap::operator const T *() const {
+ return pixels;
+}
+
+template
+Bitmap::operator BitmapRef() {
+ return BitmapRef(pixels, w, h);
+}
+
+template
+Bitmap::operator BitmapConstRef() const {
+ return BitmapConstRef(pixels, w, h);
+}
+
+}
diff --git a/core/BitmapRef.hpp b/core/BitmapRef.hpp
new file mode 100644
index 0000000..6f9620d
--- /dev/null
+++ b/core/BitmapRef.hpp
@@ -0,0 +1,43 @@
+
+#pragma once
+
+#include
+
+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
+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
+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 &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);
+ }
+
+};
+
+}
diff --git a/core/contour-combiners.cpp b/core/contour-combiners.cpp
index 61b5dfb..e52f159 100644
--- a/core/contour-combiners.cpp
+++ b/core/contour-combiners.cpp
@@ -32,7 +32,7 @@ void SimpleContourCombiner::reset(const Point2 &p) {
}
template
-void SimpleContourCombiner::setContourEdge(int i, const EdgeSelector &edgeSelector) {
+void SimpleContourCombiner::setContourEdgeSelection(int i, const EdgeSelector &edgeSelector) {
shapeEdgeSelector.merge(edgeSelector);
}
@@ -61,7 +61,7 @@ void OverlappingContourCombiner::reset(const Point2 &p) {
}
template
-void OverlappingContourCombiner::setContourEdge(int i, const EdgeSelector &edgeSelector) {
+void OverlappingContourCombiner::setContourEdgeSelection(int i, const EdgeSelector &edgeSelector) {
DistanceType edgeDistance = edgeSelector.distance();
edgeSelectors[i] = edgeSelector;
shapeEdgeSelector.merge(edgeSelector);
diff --git a/core/contour-combiners.h b/core/contour-combiners.h
index c057968..d8ddbcf 100644
--- a/core/contour-combiners.h
+++ b/core/contour-combiners.h
@@ -16,7 +16,7 @@ public:
explicit SimpleContourCombiner(const Shape &shape);
void reset(const Point2 &p);
- void setContourEdge(int i, const EdgeSelector &edgeSelector);
+ void setContourEdgeSelection(int i, const EdgeSelector &edgeSelector);
DistanceType distance() const;
private:
@@ -34,7 +34,7 @@ public:
explicit OverlappingContourCombiner(const Shape &shape);
void reset(const Point2 &p);
- void setContourEdge(int i, const EdgeSelector &edgeSelector);
+ void setContourEdgeSelection(int i, const EdgeSelector &edgeSelector);
DistanceType distance() const;
private:
diff --git a/core/edge-segments.cpp b/core/edge-segments.cpp
index 1f8bbef..4c369bf 100644
--- a/core/edge-segments.cpp
+++ b/core/edge-segments.cpp
@@ -82,7 +82,10 @@ Vector2 LinearSegment::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 {
@@ -119,19 +122,21 @@ SignedDistance QuadraticSegment::signedDistance(Point2 origin, double ¶m) co
double t[3];
int solutions = solveCubic(t, a, b, c, d);
- double minDistance = nonZeroSign(crossProduct(ab, qa))*qa.length(); // distance from A
- param = -dotProduct(qa, ab)/dotProduct(ab, ab);
+ Vector2 epDir = direction(0);
+ 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)) {
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) {
if (t[i] > 0 && t[i] < 1) {
- Point2 endpoint = p[0]+2*t[i]*ab+t[i]*t[i]*br;
- double distance = nonZeroSign(crossProduct(p[2]-p[0], endpoint-origin))*(endpoint-origin).length();
+ Point2 qe = p[0]+2*t[i]*ab+t[i]*t[i]*br-origin;
+ double distance = nonZeroSign(crossProduct(p[2]-p[0], qe))*qe.length();
if (fabs(distance) <= fabs(minDistance)) {
minDistance = distance;
param = t[i];
@@ -142,9 +147,9 @@ SignedDistance QuadraticSegment::signedDistance(Point2 origin, double ¶m) co
if (param >= 0 && param <= 1)
return SignedDistance(minDistance, 0);
if (param < .5)
- return SignedDistance(minDistance, fabs(dotProduct(ab.normalize(), qa.normalize())));
+ return SignedDistance(minDistance, fabs(dotProduct(direction(0).normalize(), qa.normalize())));
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 {
@@ -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
if (fabs(distance) < fabs(minDistance)) {
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
for (int i = 0; i <= MSDFGEN_CUBIC_SEARCH_STARTS; ++i) {
double t = (double) i/MSDFGEN_CUBIC_SEARCH_STARTS;
for (int step = 0;; ++step) {
- Vector2 qpt = point(t)-origin;
- double distance = nonZeroSign(crossProduct(direction(t), qpt))*qpt.length();
+ 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), qe))*qe.length();
if (fabs(distance) < fabs(minDistance)) {
minDistance = distance;
param = t;
@@ -179,7 +184,7 @@ SignedDistance CubicSegment::signedDistance(Point2 origin, double ¶m) const
// Improve t
Vector2 d1 = 3*as*t*t+6*br*t+3*ab;
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)
break;
}
diff --git a/core/edge-selectors.cpp b/core/edge-selectors.cpp
index c1679dd..1df94d6 100644
--- a/core/edge-selectors.cpp
+++ b/core/edge-selectors.cpp
@@ -103,7 +103,7 @@ void MultiDistanceSelector::addEdge(const EdgeSegment *prevEdge, const EdgeSegme
g.addEdgeTrueDistance(edge, distance, param);
if (edge->color&BLUE)
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);
if (edge->color&RED)
r.addEdgePseudoDistance(distance);
diff --git a/core/msdfgen.cpp b/core/msdfgen.cpp
index 708e1b7..2dc095c 100644
--- a/core/msdfgen.cpp
+++ b/core/msdfgen.cpp
@@ -13,29 +13,25 @@ class DistancePixelConversion;
template <>
class DistancePixelConversion {
public:
- typedef float PixelType;
- inline static PixelType convert(double distance, double range) {
- return PixelType(distance/range+.5);
+ typedef BitmapRef BitmapRefType;
+ inline static void convert(float *pixels, double distance, double range) {
+ *pixels = float(distance/range+.5);
}
};
template <>
class DistancePixelConversion {
public:
- typedef FloatRGB PixelType;
- inline static PixelType convert(const MultiDistance &distance, double range) {
- PixelType pixel;
- pixel.r = float(distance.r/range+.5);
- pixel.g = float(distance.g/range+.5);
- pixel.b = float(distance.b/range+.5);
- return pixel;
+ typedef BitmapRef BitmapRefType;
+ inline static void convert(float *pixels, const MultiDistance &distance, double range) {
+ pixels[0] = float(distance.r/range+.5);
+ pixels[1] = float(distance.g/range+.5);
+ pixels[2] = float(distance.b/range+.5);
}
};
template
-void generateDistanceField(Bitmap::PixelType> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate) {
- int w = output.width(), h = output.height();
-
+void generateDistanceField(const typename DistancePixelConversion::BitmapRefType &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate) {
#ifdef MSDFGEN_USE_OPENMP
#pragma omp parallel
#endif
@@ -45,10 +41,10 @@ void generateDistanceField(Bitmap::convert(distance, range);
+ DistancePixelConversion::convert(output(x, row), distance, range);
}
}
}
}
-void generateSDF(Bitmap &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, bool overlapSupport) {
+void generateSDF(const BitmapRef &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, bool overlapSupport) {
if (overlapSupport)
generateDistanceField >(output, shape, range, scale, translate);
else
generateDistanceField >(output, shape, range, scale, translate);
}
-void generatePseudoSDF(Bitmap &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, bool overlapSupport) {
+void generatePseudoSDF(const BitmapRef &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, bool overlapSupport) {
if (overlapSupport)
generateDistanceField >(output, shape, range, scale, translate);
else
generateDistanceField >(output, shape, range, scale, translate);
}
-void generateMSDF(Bitmap &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, double edgeThreshold, bool overlapSupport) {
+void generateMSDF(const BitmapRef &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, double edgeThreshold, bool overlapSupport) {
if (overlapSupport)
generateDistanceField >(output, shape, range, scale, translate);
else
@@ -100,10 +96,10 @@ void generateMSDF(Bitmap &output, const Shape &shape, double range, co
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
- float a0 = a.r, a1 = a.g, a2 = a.b;
- float b0 = b.r, b1 = b.g, b2 = b.b;
+ float a0 = a[0], a1 = a[1], a2 = a[2];
+ float b0 = b[0], b1 = b[1], b2 = b[2];
float tmp;
if (fabsf(b0-a0) < fabsf(b1-a1)) {
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
}
-void msdfErrorCorrection(Bitmap &output, const Vector2 &threshold) {
+void msdfErrorCorrection(const BitmapRef &output, const Vector2 &threshold) {
std::vector > clashes;
- int w = output.width(), h = output.height();
+ int w = output.width, h = output.height;
for (int y = 0; y < h; ++y)
for (int x = 0; x < w; ++x) {
if (
@@ -136,9 +132,9 @@ void msdfErrorCorrection(Bitmap &output, const Vector2 &threshold) {
clashes.push_back(std::make_pair(x, y));
}
for (std::vector >::const_iterator clash = clashes.begin(); clash != clashes.end(); ++clash) {
- FloatRGB &pixel = output(clash->first, clash->second);
- float med = median(pixel.r, pixel.g, pixel.b);
- pixel.r = med, pixel.g = med, pixel.b = med;
+ float *pixel = output(clash->first, clash->second);
+ float med = median(pixel[0], pixel[1], pixel[2]);
+ pixel[0] = med, pixel[1] = med, pixel[2] = med;
}
#ifndef MSDFGEN_NO_DIAGONAL_CLASH_DETECTION
clashes.clear();
@@ -153,23 +149,22 @@ void msdfErrorCorrection(Bitmap &output, const Vector2 &threshold) {
clashes.push_back(std::make_pair(x, y));
}
for (std::vector >::const_iterator clash = clashes.begin(); clash != clashes.end(); ++clash) {
- FloatRGB &pixel = output(clash->first, clash->second);
- float med = median(pixel.r, pixel.g, pixel.b);
- pixel.r = med, pixel.g = med, pixel.b = med;
+ float *pixel = output(clash->first, clash->second);
+ float med = median(pixel[0], pixel[1], pixel[2]);
+ pixel[0] = med, pixel[1] = med, pixel[2] = med;
}
#endif
}
// Legacy version
-void generateSDF_legacy(Bitmap &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate) {
- int w = output.width(), h = output.height();
+void generateSDF_legacy(const BitmapRef &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate) {
#ifdef MSDFGEN_USE_OPENMP
#pragma omp parallel for
#endif
- for (int y = 0; y < h; ++y) {
- int row = shape.inverseYAxis ? h-y-1 : y;
- for (int x = 0; x < w; ++x) {
+ for (int y = 0; y < output.height; ++y) {
+ int row = shape.inverseYAxis ? output.height-y-1 : y;
+ for (int x = 0; x < output.width; ++x) {
double dummy;
Point2 p = Vector2(x+.5, y+.5)/scale-translate;
SignedDistance minDistance;
@@ -179,19 +174,18 @@ void generateSDF_legacy(Bitmap &output, const Shape &shape, double range,
if (distance < minDistance)
minDistance = distance;
}
- output(x, row) = float(minDistance.distance/range+.5);
+ *output(x, row) = float(minDistance.distance/range+.5);
}
}
}
-void generatePseudoSDF_legacy(Bitmap &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate) {
- int w = output.width(), h = output.height();
+void generatePseudoSDF_legacy(const BitmapRef &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate) {
#ifdef MSDFGEN_USE_OPENMP
#pragma omp parallel for
#endif
- for (int y = 0; y < h; ++y) {
- int row = shape.inverseYAxis ? h-y-1 : y;
- for (int x = 0; x < w; ++x) {
+ for (int y = 0; y < output.height; ++y) {
+ int row = shape.inverseYAxis ? output.height-y-1 : y;
+ for (int x = 0; x < output.width; ++x) {
Point2 p = Vector2(x+.5, y+.5)/scale-translate;
SignedDistance minDistance;
const EdgeHolder *nearEdge = NULL;
@@ -208,19 +202,18 @@ void generatePseudoSDF_legacy(Bitmap &output, const Shape &shape, double
}
if (nearEdge)
(*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 &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, double edgeThreshold) {
- int w = output.width(), h = output.height();
+void generateMSDF_legacy(const BitmapRef &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, double edgeThreshold) {
#ifdef MSDFGEN_USE_OPENMP
#pragma omp parallel for
#endif
- for (int y = 0; y < h; ++y) {
- int row = shape.inverseYAxis ? h-y-1 : y;
- for (int x = 0; x < w; ++x) {
+ for (int y = 0; y < output.height; ++y) {
+ int row = shape.inverseYAxis ? output.height-y-1 : y;
+ for (int x = 0; x < output.width; ++x) {
Point2 p = Vector2(x+.5, y+.5)/scale-translate;
struct {
@@ -258,9 +251,9 @@ void generateMSDF_legacy(Bitmap &output, const Shape &shape, double ra
(*g.nearEdge)->distanceToPseudoDistance(g.minDistance, p, g.nearParam);
if (b.nearEdge)
(*b.nearEdge)->distanceToPseudoDistance(b.minDistance, p, b.nearParam);
- output(x, row).r = float(r.minDistance.distance/range+.5);
- output(x, row).g = float(g.minDistance.distance/range+.5);
- output(x, row).b = float(b.minDistance.distance/range+.5);
+ output(x, row)[0] = float(r.minDistance.distance/range+.5);
+ output(x, row)[1] = float(g.minDistance.distance/range+.5);
+ output(x, row)[2] = float(b.minDistance.distance/range+.5);
}
}
diff --git a/core/pixel-conversion.hpp b/core/pixel-conversion.hpp
new file mode 100644
index 0000000..7e9b6d0
--- /dev/null
+++ b/core/pixel-conversion.hpp
@@ -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);
+}
+
+}
diff --git a/core/rasterization.cpp b/core/rasterization.cpp
index ac0a4cc..6fc4284 100644
--- a/core/rasterization.cpp
+++ b/core/rasterization.cpp
@@ -21,44 +21,42 @@ static bool interpretFillRule(int intersections, FillRule fillRule) {
return false;
}
-void rasterize(Bitmap &output, const Shape &shape, const Vector2 &scale, const Vector2 &translate, FillRule fillRule) {
- int w = output.width(), h = output.height();
+void rasterize(const BitmapRef &output, const Shape &shape, const Vector2 &scale, const Vector2 &translate, FillRule fillRule) {
Point2 p;
Scanline scanline;
- for (int y = 0; y < h; ++y) {
- int row = shape.inverseYAxis ? h-y-1 : y;
+ for (int y = 0; y < output.height; ++y) {
+ int row = shape.inverseYAxis ? output.height-y-1 : y;
p.y = (y+.5)/scale.y-translate.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;
int intersections = scanline.sumIntersections(p.x);
bool fill = interpretFillRule(intersections, fillRule);
- output(x, row) = (float) fill;
+ *output(x, row) = (float) fill;
}
}
}
-void distanceSignCorrection(Bitmap &sdf, const Shape &shape, const Vector2 &scale, const Vector2 &translate, FillRule fillRule) {
- int w = sdf.width(), h = sdf.height();
+void distanceSignCorrection(const BitmapRef &sdf, const Shape &shape, const Vector2 &scale, const Vector2 &translate, FillRule fillRule) {
Point2 p;
Scanline scanline;
- for (int y = 0; y < h; ++y) {
- int row = shape.inverseYAxis ? h-y-1 : y;
+ for (int y = 0; y < sdf.height; ++y) {
+ int row = shape.inverseYAxis ? sdf.height-y-1 : y;
p.y = (y+.5)/scale.y-translate.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;
int intersections = scanline.sumIntersections(p.x);
bool fill = interpretFillRule(intersections, fillRule);
- float &sd = sdf(x, row);
+ float &sd = *sdf(x, row);
if ((sd > .5f) != fill)
sd = 1.f-sd;
}
}
}
-void distanceSignCorrection(Bitmap &sdf, const Shape &shape, const Vector2 &scale, const Vector2 &translate, FillRule fillRule) {
- int w = sdf.width(), h = sdf.height();
+void distanceSignCorrection(const BitmapRef &sdf, const Shape &shape, const Vector2 &scale, const Vector2 &translate, FillRule fillRule) {
+ int w = sdf.width, h = sdf.height;
if (!(w*h))
return;
Point2 p;
@@ -75,14 +73,14 @@ void distanceSignCorrection(Bitmap &sdf, const Shape &shape, const Vec
p.x = (x+.5)/scale.x-translate.x;
int intersections = scanline.sumIntersections(p.x);
bool fill = interpretFillRule(intersections, fillRule);
- FloatRGB &msd = sdf(x, row);
- float sd = median(msd.r, msd.g, msd.b);
+ float *msd = sdf(x, row);
+ float sd = median(msd[0], msd[1], msd[2]);
if (sd == .5f)
ambiguous = true;
else if ((sd > .5f) != fill) {
- msd.r = 1.f-msd.r;
- msd.g = 1.f-msd.g;
- msd.b = 1.f-msd.b;
+ msd[0] = 1.f-msd[0];
+ msd[1] = 1.f-msd[1];
+ msd[2] = 1.f-msd[2];
*match = -1;
} else
*match = 1;
@@ -102,10 +100,10 @@ void distanceSignCorrection(Bitmap &sdf, const Shape &shape, const Vec
if (y > 0) neighborMatch += *(match-w);
if (y < h-1) neighborMatch += *(match+w);
if (neighborMatch < 0) {
- FloatRGB &msd = sdf(x, row);
- msd.r = 1.f-msd.r;
- msd.g = 1.f-msd.g;
- msd.b = 1.f-msd.b;
+ float *msd = sdf(x, row);
+ msd[0] = 1.f-msd[0];
+ msd[1] = 1.f-msd[1];
+ msd[2] = 1.f-msd[2];
}
}
++match;
diff --git a/core/rasterization.h b/core/rasterization.h
index 10e8f41..91bf02c 100644
--- a/core/rasterization.h
+++ b/core/rasterization.h
@@ -3,7 +3,7 @@
#include "Vector2.h"
#include "Shape.h"
-#include "Bitmap.h"
+#include "BitmapRef.hpp"
namespace msdfgen {
@@ -16,9 +16,9 @@ enum FillRule {
};
/// Rasterizes the shape into a monochrome bitmap.
-void rasterize(Bitmap &output, const Shape &shape, const Vector2 &scale, const Vector2 &translate, FillRule fillRule = FILL_NONZERO);
+void rasterize(const BitmapRef &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.
-void distanceSignCorrection(Bitmap &sdf, const Shape &shape, const Vector2 &scale, const Vector2 &translate, FillRule fillRule = FILL_NONZERO);
-void distanceSignCorrection(Bitmap &sdf, const Shape &shape, const Vector2 &scale, const Vector2 &translate, FillRule fillRule = FILL_NONZERO);
+void distanceSignCorrection(const BitmapRef &sdf, const Shape &shape, const Vector2 &scale, const Vector2 &translate, FillRule fillRule = FILL_NONZERO);
+void distanceSignCorrection(const BitmapRef &sdf, const Shape &shape, const Vector2 &scale, const Vector2 &translate, FillRule fillRule = FILL_NONZERO);
}
diff --git a/core/render-sdf.cpp b/core/render-sdf.cpp
index 7a9cc12..223d8cf 100644
--- a/core/render-sdf.cpp
+++ b/core/render-sdf.cpp
@@ -2,106 +2,87 @@
#include "render-sdf.h"
#include "arithmetics.hpp"
+#include "pixel-conversion.hpp"
namespace msdfgen {
-template
-inline FloatRGB mix(FloatRGB a, FloatRGB b, S weight) {
- FloatRGB output = {
- mix(a.r, b.r, weight),
- mix(a.g, b.g, weight),
- mix(a.b, b.b, weight)
- };
- return output;
-}
-
-template
-static T sample(const Bitmap &bitmap, Point2 pos) {
- int w = bitmap.width(), h = bitmap.height();
- double x = pos.x*w-.5;
- double y = pos.y*h-.5;
+template
+static void sample(T *output, const BitmapConstRef &bitmap, Point2 pos) {
+ double x = pos.x*bitmap.width-.5;
+ double y = pos.y*bitmap.height-.5;
int l = (int) floor(x);
int b = (int) floor(y);
int r = l+1;
int t = b+1;
double lr = x-l;
double bt = y-b;
- l = clamp(l, w-1), r = clamp(r, w-1);
- b = clamp(b, h-1), t = clamp(t, h-1);
- return mix(mix(bitmap(l, b), bitmap(r, b), lr), mix(bitmap(l, t), bitmap(r, t), lr), bt);
+ l = clamp(l, bitmap.width-1), r = clamp(r, bitmap.width-1);
+ b = clamp(b, bitmap.height-1), t = clamp(t, bitmap.height-1);
+ 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) {
if (!pxRange)
- return dist > .5f;
+ return (float) (dist > .5f);
return (float) clamp((dist-.5f)*pxRange+.5);
}
-void renderSDF(Bitmap &output, const Bitmap &sdf, double pxRange) {
- int w = output.width(), h = output.height();
- pxRange *= (double) (w+h)/(sdf.width()+sdf.height());
- for (int y = 0; y < h; ++y)
- for (int x = 0; x < w; ++x) {
- float s = sample(sdf, Point2((x+.5)/w, (y+.5)/h));
- output(x, y) = distVal(s, pxRange);
+void renderSDF(const BitmapRef &output, const BitmapConstRef &sdf, double pxRange) {
+ pxRange *= (double) (output.width+output.height)/(sdf.width+sdf.height);
+ for (int y = 0; y < output.height; ++y)
+ for (int x = 0; x < output.width; ++x) {
+ float sd;
+ sample(&sd, sdf, Point2((x+.5)/output.width, (y+.5)/output.height));
+ *output(x, y) = distVal(sd, pxRange);
}
}
-void renderSDF(Bitmap &output, const Bitmap &sdf, double pxRange) {
- int w = output.width(), h = output.height();
- pxRange *= (double) (w+h)/(sdf.width()+sdf.height());
- for (int y = 0; y < h; ++y)
- for (int x = 0; x < w; ++x) {
- float s = sample(sdf, Point2((x+.5)/w, (y+.5)/h));
- float v = distVal(s, pxRange);
- output(x, y).r = v;
- output(x, y).g = v;
- output(x, y).b = v;
+void renderSDF(const BitmapRef &output, const BitmapConstRef &sdf, double pxRange) {
+ pxRange *= (double) (output.width+output.height)/(sdf.width+sdf.height);
+ for (int y = 0; y < output.height; ++y)
+ for (int x = 0; x < output.width; ++x) {
+ float sd;
+ sample(&sd, sdf, Point2((x+.5)/output.width, (y+.5)/output.height));
+ float v = distVal(sd, pxRange);
+ output(x, y)[0] = v;
+ output(x, y)[1] = v;
+ output(x, y)[2] = v;
}
}
-void renderSDF(Bitmap &output, const Bitmap &sdf, double pxRange) {
- int w = output.width(), h = output.height();
- pxRange *= (double) (w+h)/(sdf.width()+sdf.height());
- for (int y = 0; y < h; ++y)
- for (int x = 0; x < w; ++x) {
- FloatRGB s = sample(sdf, Point2((x+.5)/w, (y+.5)/h));
- output(x, y) = distVal(median(s.r, s.g, s.b), pxRange);
+void renderSDF(const BitmapRef &output, const BitmapConstRef &sdf, double pxRange) {
+ pxRange *= (double) (output.width+output.height)/(sdf.width+sdf.height);
+ for (int y = 0; y < output.height; ++y)
+ for (int x = 0; x < output.width; ++x) {
+ float sd[3];
+ sample(sd, sdf, Point2((x+.5)/output.width, (y+.5)/output.height));
+ *output(x, y) = distVal(median(sd[0], sd[1], sd[2]), pxRange);
}
}
-void renderSDF(Bitmap &output, const Bitmap &sdf, double pxRange) {
- int w = output.width(), h = output.height();
- pxRange *= (double) (w+h)/(sdf.width()+sdf.height());
- for (int y = 0; y < h; ++y)
- for (int x = 0; x < w; ++x) {
- FloatRGB s = sample(sdf, Point2((x+.5)/w, (y+.5)/h));
- output(x, y).r = distVal(s.r, pxRange);
- output(x, y).g = distVal(s.g, pxRange);
- output(x, y).b = distVal(s.b, pxRange);
+void renderSDF(const BitmapRef &output, const BitmapConstRef &sdf, double pxRange) {
+ pxRange *= (double) (output.width+output.height)/(sdf.width+sdf.height);
+ for (int y = 0; y < output.height; ++y)
+ for (int x = 0; x < output.width; ++x) {
+ float sd[3];
+ sample(sd, sdf, Point2((x+.5)/output.width, (y+.5)/output.height));
+ output(x, y)[0] = distVal(sd[0], pxRange);
+ output(x, y)[1] = distVal(sd[1], pxRange);
+ output(x, y)[2] = distVal(sd[2], pxRange);
}
}
-void simulate8bit(Bitmap &bitmap) {
- int w = bitmap.width(), h = bitmap.height();
- for (int y = 0; y < h; ++y)
- for (int x = 0; x < w; ++x) {
- unsigned char v = clamp(int(bitmap(x, y)*0x100), 0xff);
- bitmap(x, y) = v/255.f;
- }
+void simulate8bit(const BitmapRef &bitmap) {
+ const float *end = bitmap.pixels+1*bitmap.width*bitmap.height;
+ for (float *p = bitmap.pixels; p < end; ++p)
+ *p = pixelByteToFloat(pixelFloatToByte(*p));
}
-void simulate8bit(Bitmap &bitmap) {
- int w = bitmap.width(), h = bitmap.height();
- for (int y = 0; y < h; ++y)
- for (int x = 0; x < w; ++x) {
- 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;
- }
+void simulate8bit(const BitmapRef &bitmap) {
+ const float *end = bitmap.pixels+3*bitmap.width*bitmap.height;
+ for (float *p = bitmap.pixels; p < end; ++p)
+ *p = pixelByteToFloat(pixelFloatToByte(*p));
}
}
diff --git a/core/render-sdf.h b/core/render-sdf.h
index c63186d..a388e31 100644
--- a/core/render-sdf.h
+++ b/core/render-sdf.h
@@ -2,18 +2,18 @@
#pragma once
#include "Vector2.h"
-#include "Bitmap.h"
+#include "BitmapRef.hpp"
namespace msdfgen {
/// Reconstructs the shape's appearance into output from the distance field sdf.
-void renderSDF(Bitmap &output, const Bitmap &sdf, double pxRange = 0);
-void renderSDF(Bitmap &output, const Bitmap &sdf, double pxRange = 0);
-void renderSDF(Bitmap &output, const Bitmap &sdf, double pxRange = 0);
-void renderSDF(Bitmap &output, const Bitmap &sdf, double pxRange = 0);
+void renderSDF(const BitmapRef &output, const BitmapConstRef &sdf, double pxRange = 0);
+void renderSDF(const BitmapRef &output, const BitmapConstRef &sdf, double pxRange = 0);
+void renderSDF(const BitmapRef &output, const BitmapConstRef &sdf, double pxRange = 0);
+void renderSDF(const BitmapRef &output, const BitmapConstRef &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.
-void simulate8bit(Bitmap &bitmap);
-void simulate8bit(Bitmap &bitmap);
+void simulate8bit(const BitmapRef &bitmap);
+void simulate8bit(const BitmapRef &bitmap);
}
diff --git a/core/save-bmp.cpp b/core/save-bmp.cpp
index 6ff8fba..5ba8920 100644
--- a/core/save-bmp.cpp
+++ b/core/save-bmp.cpp
@@ -1,8 +1,8 @@
-#include "save-bmp.h"
-
#define _CRT_SECURE_NO_WARNINGS
+#include "save-bmp.h"
+
#include
#ifdef MSDFGEN_USE_CPP11
@@ -14,7 +14,7 @@
typedef unsigned char uint8_t;
#endif
-#include "arithmetics.hpp"
+#include "pixel-conversion.hpp"
namespace msdfgen {
@@ -60,47 +60,97 @@ static bool writeBmpHeader(FILE *file, int width, int height, int &paddedWidth)
return true;
}
-bool saveBmp(const Bitmap &bitmap, const char *filename) {
+bool saveBmp(const BitmapConstRef &bitmap, const char *filename) {
FILE *file = fopen(filename, "wb");
if (!file)
return false;
int paddedWidth;
- writeBmpHeader(file, bitmap.width(), bitmap.height(), paddedWidth);
+ writeBmpHeader(file, bitmap.width, bitmap.height, paddedWidth);
const uint8_t padding[4] = { };
- for (int y = 0; y < bitmap.height(); ++y) {
- for (int x = 0; x < bitmap.width(); ++x) {
- uint8_t px = (uint8_t) clamp(int(bitmap(x, y)*0x100), 0xff);
+ 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, paddedWidth-3*bitmap.width(), file);
+ fwrite(padding, 1, padLength, file);
}
return !fclose(file);
}
-bool saveBmp(const Bitmap &bitmap, const char *filename) {
+bool saveBmp(const BitmapConstRef &bitmap, const char *filename) {
FILE *file = fopen(filename, "wb");
if (!file)
return false;
int paddedWidth;
- writeBmpHeader(file, bitmap.width(), bitmap.height(), paddedWidth);
+ writeBmpHeader(file, bitmap.width, bitmap.height, paddedWidth);
const uint8_t padding[4] = { };
- for (int y = 0; y < bitmap.height(); ++y) {
- for (int x = 0; x < bitmap.width(); ++x) {
+ 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) clamp(int(bitmap(x, y).b*0x100), 0xff),
- (uint8_t) clamp(int(bitmap(x, y).g*0x100), 0xff),
- (uint8_t) clamp(int(bitmap(x, y).r*0x100), 0xff)
+ (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, paddedWidth-3*bitmap.width(), file);
+ fwrite(padding, 1, padLength, file);
+ }
+
+ return !fclose(file);
+}
+
+bool saveBmp(const BitmapConstRef &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 &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);
diff --git a/core/save-bmp.h b/core/save-bmp.h
index 9642f2d..4fbe703 100644
--- a/core/save-bmp.h
+++ b/core/save-bmp.h
@@ -1,12 +1,14 @@
#pragma once
-#include "Bitmap.h"
+#include "BitmapRef.hpp"
namespace msdfgen {
/// Saves the bitmap as a BMP file.
-bool saveBmp(const Bitmap &bitmap, const char *filename);
-bool saveBmp(const Bitmap &bitmap, const char *filename);
+bool saveBmp(const BitmapConstRef &bitmap, const char *filename);
+bool saveBmp(const BitmapConstRef &bitmap, const char *filename);
+bool saveBmp(const BitmapConstRef &bitmap, const char *filename);
+bool saveBmp(const BitmapConstRef &bitmap, const char *filename);
}
diff --git a/ext/save-png.cpp b/ext/save-png.cpp
index 06dbdd6..6eb2f55 100644
--- a/ext/save-png.cpp
+++ b/ext/save-png.cpp
@@ -1,30 +1,38 @@
#include "save-png.h"
-#include "../core/arithmetics.hpp"
#include
+#include "../core/pixel-conversion.hpp"
namespace msdfgen {
-bool savePng(const Bitmap &bitmap, const char *filename) {
- std::vector pixels(bitmap.width()*bitmap.height());
- std::vector::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 BitmapConstRef &bitmap, const char *filename) {
+ return !lodepng::encode(filename, bitmap.pixels, bitmap.width, bitmap.height, LCT_GREY);
}
-bool savePng(const Bitmap &bitmap, const char *filename) {
- std::vector pixels(3*bitmap.width()*bitmap.height());
- std::vector::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).r*0x100), 0xff);
- *it++ = clamp(int(bitmap(x, y).g*0x100), 0xff);
- *it++ = clamp(int(bitmap(x, y).b*0x100), 0xff);
+bool savePng(const BitmapConstRef &bitmap, const char *filename) {
+ return !lodepng::encode(filename, bitmap.pixels, bitmap.width, bitmap.height, LCT_RGB);
+}
+
+bool savePng(const BitmapConstRef &bitmap, const char *filename) {
+ std::vector pixels(bitmap.width*bitmap.height);
+ std::vector::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));
+ return !lodepng::encode(filename, pixels, bitmap.width, bitmap.height, LCT_GREY);
+}
+
+bool savePng(const BitmapConstRef &bitmap, const char *filename) {
+ std::vector pixels(3*bitmap.width*bitmap.height);
+ std::vector::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);
}
}
diff --git a/ext/save-png.h b/ext/save-png.h
index e1103ca..c0c2743 100644
--- a/ext/save-png.h
+++ b/ext/save-png.h
@@ -1,12 +1,14 @@
#pragma once
-#include "../core/Bitmap.h"
+#include "../core/BitmapRef.hpp"
namespace msdfgen {
/// Saves the bitmap as a PNG file.
-bool savePng(const Bitmap &bitmap, const char *filename);
-bool savePng(const Bitmap &bitmap, const char *filename);
+bool savePng(const BitmapConstRef &bitmap, const char *filename);
+bool savePng(const BitmapConstRef &bitmap, const char *filename);
+bool savePng(const BitmapConstRef &bitmap, const char *filename);
+bool savePng(const BitmapConstRef &bitmap, const char *filename);
}
diff --git a/main.cpp b/main.cpp
index 7c750dd..5c4da3c 100644
--- a/main.cpp
+++ b/main.cpp
@@ -131,19 +131,11 @@ static void parseColoring(Shape &shape, const char *edgeAssignment) {
}
}
-static void invertColor(Bitmap &bitmap) {
- for (int y = 0; y < bitmap.height(); ++y)
- for (int x = 0; x < bitmap.width(); ++x)
- bitmap(x, y) = 1.f-bitmap(x, y);
-}
-
-static void invertColor(Bitmap &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;
- }
+template
+static void invertColor(const BitmapRef &bitmap) {
+ const float *end = bitmap.pixels+N*bitmap.width*bitmap.height;
+ for (float *p = bitmap.pixels; p < end; ++p)
+ *p = 1.f-*p;
}
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;
}
-template
-static const char * writeOutput(const Bitmap &bitmap, const char *filename, Format format) {
+template
+static const char * writeOutput(const BitmapConstRef &bitmap, const char *filename, Format format) {
if (filename) {
if (format == AUTO) {
if (cmpExtension(filename, ".png")) format = PNG;
@@ -223,9 +215,9 @@ static const char * writeOutput(const Bitmap &bitmap, const char *filename, F
FILE *file = fopen(filename, "w");
if (!file) return "Failed to write output text file.";
if (format == TEXT)
- writeTextBitmap(file, reinterpret_cast(&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)
- writeTextBitmapFloat(file, reinterpret_cast(&bitmap(0, 0)), sizeof(T)/sizeof(float)*bitmap.width(), bitmap.height());
+ writeTextBitmapFloat(file, bitmap.pixels, N*bitmap.width, bitmap.height);
fclose(file);
return NULL;
}
@@ -233,11 +225,11 @@ static const char * writeOutput(const Bitmap &bitmap, const char *filename, F
FILE *file = fopen(filename, "wb");
if (!file) return "Failed to write output binary file.";
if (format == BINARY)
- writeBinBitmap(file, reinterpret_cast