Ver código fonte

Y-axis orientation rework, non-contiguous bitmap section

Chlumsky 16 horas atrás
pai
commit
e36d71409b

+ 1 - 0
README.md

@@ -203,6 +203,7 @@ The text shape description has the following syntax.
  - The last point of each contour must be equal to the first, or the symbol `#` can be used, which represents the first point.
  - There can be an edge segment specification between any two points, also separated by semicolons.
    This can include the edge's color (`c`, `m`, `y` or `w`) and/or one or two Bézier curve control points inside parentheses.
+ - At the beginning, there may be an Y-axis direction specifier `@y-up` or `@y-down`.
 
 For example,
 ```

+ 12 - 2
core/Bitmap.h

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

+ 66 - 11
core/Bitmap.hpp

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

+ 121 - 8
core/BitmapRef.hpp

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

+ 9 - 9
core/Contour.cpp

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

+ 2 - 2
core/Contour.h

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

+ 63 - 53
core/MSDFErrorCorrection.cpp

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

+ 7 - 7
core/MSDFErrorCorrection.h

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

+ 13 - 5
core/Shape.cpp

@@ -71,7 +71,7 @@ void Shape::normalize() {
             contour->edges.push_back(EdgeHolder(parts[0]));
             contour->edges.push_back(EdgeHolder(parts[1]));
             contour->edges.push_back(EdgeHolder(parts[2]));
-        } else {
+        } else if (!contour->edges.empty()) {
             // Push apart convergent edge segments
             EdgeHolder *prevEdge = &contour->edges.back();
             for (std::vector<EdgeHolder>::iterator edge = contour->edges.begin(); edge != contour->edges.end(); ++edge) {
@@ -91,14 +91,14 @@ void Shape::normalize() {
     }
 }
 
-void Shape::bound(double &l, double &b, double &r, double &t) const {
+void Shape::bound(double &xMin, double &yMin, double &xMax, double &yMax) const {
     for (std::vector<Contour>::const_iterator contour = contours.begin(); contour != contours.end(); ++contour)
-        contour->bound(l, b, r, t);
+        contour->bound(xMin, yMin, xMax, yMax);
 }
 
-void Shape::boundMiters(double &l, double &b, double &r, double &t, double border, double miterLimit, int polarity) const {
+void Shape::boundMiters(double &xMin, double &yMin, double &xMax, double &yMax, double border, double miterLimit, int polarity) const {
     for (std::vector<Contour>::const_iterator contour = contours.begin(); contour != contours.end(); ++contour)
-        contour->boundMiters(l, b, r, t, border, miterLimit, polarity);
+        contour->boundMiters(xMin, yMin, xMax, yMax, border, miterLimit, polarity);
 }
 
 Shape::Bounds Shape::getBounds(double border, double miterLimit, int polarity) const {
@@ -197,4 +197,12 @@ void Shape::orientContours() {
             contours[i].reverse();
 }
 
+YAxisOrientation Shape::getYAxisOrientation() const {
+    return inverseYAxis ? MSDFGEN_Y_AXIS_NONDEFAULT_ORIENTATION : MSDFGEN_Y_AXIS_DEFAULT_ORIENTATION;
+}
+
+void Shape::setYAxisOrientation(YAxisOrientation yAxisOrientation) {
+    inverseYAxis = yAxisOrientation != MSDFGEN_Y_AXIS_DEFAULT_ORIENTATION;
+}
+
 }

+ 9 - 2
core/Shape.h

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

+ 17 - 0
core/YAxisOrientation.h

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

+ 3 - 1
core/bitmap-interpolation.hpp

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

+ 18 - 18
core/edge-segments.cpp

@@ -402,37 +402,37 @@ int CubicSegment::scanlineIntersections(double x[3], int dy[3], double y) const
     return total;
 }
 
-static void pointBounds(Point2 p, double &l, double &b, double &r, double &t) {
-    if (p.x < l) l = p.x;
-    if (p.y < b) b = p.y;
-    if (p.x > r) r = p.x;
-    if (p.y > t) t = p.y;
+static void pointBounds(Point2 p, double &xMin, double &yMin, double &xMax, double &yMax) {
+    if (p.x < xMin) xMin = p.x;
+    if (p.y < yMin) yMin = p.y;
+    if (p.x > xMax) xMax = p.x;
+    if (p.y > yMax) yMax = p.y;
 }
 
-void LinearSegment::bound(double &l, double &b, double &r, double &t) const {
-    pointBounds(p[0], l, b, r, t);
-    pointBounds(p[1], l, b, r, t);
+void LinearSegment::bound(double &xMin, double &yMin, double &xMax, double &yMax) const {
+    pointBounds(p[0], xMin, yMin, xMax, yMax);
+    pointBounds(p[1], xMin, yMin, xMax, yMax);
 }
 
-void QuadraticSegment::bound(double &l, double &b, double &r, double &t) const {
-    pointBounds(p[0], l, b, r, t);
-    pointBounds(p[2], l, b, r, t);
+void QuadraticSegment::bound(double &xMin, double &yMin, double &xMax, double &yMax) const {
+    pointBounds(p[0], xMin, yMin, xMax, yMax);
+    pointBounds(p[2], xMin, yMin, xMax, yMax);
     Vector2 bot = (p[1]-p[0])-(p[2]-p[1]);
     if (bot.x) {
         double param = (p[1].x-p[0].x)/bot.x;
         if (param > 0 && param < 1)
-            pointBounds(point(param), l, b, r, t);
+            pointBounds(point(param), xMin, yMin, xMax, yMax);
     }
     if (bot.y) {
         double param = (p[1].y-p[0].y)/bot.y;
         if (param > 0 && param < 1)
-            pointBounds(point(param), l, b, r, t);
+            pointBounds(point(param), xMin, yMin, xMax, yMax);
     }
 }
 
-void CubicSegment::bound(double &l, double &b, double &r, double &t) const {
-    pointBounds(p[0], l, b, r, t);
-    pointBounds(p[3], l, b, r, t);
+void CubicSegment::bound(double &xMin, double &yMin, double &xMax, double &yMax) const {
+    pointBounds(p[0], xMin, yMin, xMax, yMax);
+    pointBounds(p[3], xMin, yMin, xMax, yMax);
     Vector2 a0 = p[1]-p[0];
     Vector2 a1 = 2*(p[2]-p[1]-a0);
     Vector2 a2 = p[3]-3*p[2]+3*p[1]-p[0];
@@ -441,11 +441,11 @@ void CubicSegment::bound(double &l, double &b, double &r, double &t) const {
     solutions = solveQuadratic(params, a2.x, a1.x, a0.x);
     for (int i = 0; i < solutions; ++i)
         if (params[i] > 0 && params[i] < 1)
-            pointBounds(point(params[i]), l, b, r, t);
+            pointBounds(point(params[i]), xMin, yMin, xMax, yMax);
     solutions = solveQuadratic(params, a2.y, a1.y, a0.y);
     for (int i = 0; i < solutions; ++i)
         if (params[i] > 0 && params[i] < 1)
-            pointBounds(point(params[i]), l, b, r, t);
+            pointBounds(point(params[i]), xMin, yMin, xMax, yMax);
 }
 
 void LinearSegment::reverse() {

+ 4 - 4
core/edge-segments.h

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

+ 2 - 2
core/export-svg.cpp

@@ -51,7 +51,7 @@ static void writeSvgPathDef(FILE *f, const Shape &shape) {
 bool saveSvgShape(const Shape &shape, const char *filename) {
     if (FILE *f = fopen(filename, "w")) {
         fputs("<svg xmlns=\"http://www.w3.org/2000/svg\"><path", f);
-        if (!shape.inverseYAxis)
+        if (shape.getYAxisOrientation() == Y_UPWARD)
             fputs(" transform=\"scale(1 -1)\"", f);
         fputs(" d=\"", f);
         writeSvgPathDef(f, shape);
@@ -65,7 +65,7 @@ bool saveSvgShape(const Shape &shape, const char *filename) {
 bool saveSvgShape(const Shape &shape, const Shape::Bounds &bounds, const char *filename) {
     if (FILE *f = fopen(filename, "w")) {
         fprintf(f, "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"%.17g %.17g %.17g %.17g\"><path", bounds.l, bounds.b, bounds.r-bounds.l, bounds.t-bounds.b);
-        if (!shape.inverseYAxis)
+        if (shape.getYAxisOrientation() == Y_UPWARD)
             fprintf(f, " transform=\"translate(0 %.17g) scale(1 -1)\"", bounds.b+bounds.t);
         fputs(" d=\"", f);
         writeSvgPathDef(f, shape);

+ 22 - 23
core/msdf-error-correction.cpp

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

+ 18 - 18
core/msdf-error-correction.h

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

+ 42 - 41
core/msdfgen.cpp

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

+ 16 - 17
core/rasterization.cpp

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

+ 8 - 8
core/rasterization.h

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

+ 9 - 9
core/render-sdf.cpp

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

+ 9 - 9
core/render-sdf.h

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

+ 12 - 6
core/save-bmp.cpp

@@ -89,12 +89,13 @@ static bool writeBmpHeader(FILE *file, int bytesPerPixel, int width, int height,
     return true;
 }
 
-bool saveBmp(const BitmapConstRef<byte, 1> &bitmap, const char *filename) {
+bool saveBmp(BitmapConstSection<byte, 1> bitmap, const char *filename) {
     FILE *file = fopen(filename, "wb");
     if (!file)
         return false;
 
     int paddedWidth;
+    bitmap.reorient(Y_UPWARD);
     writeBmpHeader(file, 1, bitmap.width, bitmap.height, paddedWidth);
 
     byte padding[4] = { };
@@ -107,12 +108,13 @@ bool saveBmp(const BitmapConstRef<byte, 1> &bitmap, const char *filename) {
     return !fclose(file);
 }
 
-bool saveBmp(const BitmapConstRef<byte, 3> &bitmap, const char *filename) {
+bool saveBmp(BitmapConstSection<byte, 3> bitmap, const char *filename) {
     FILE *file = fopen(filename, "wb");
     if (!file)
         return false;
 
     int paddedWidth;
+    bitmap.reorient(Y_UPWARD);
     writeBmpHeader(file, 3, bitmap.width, bitmap.height, paddedWidth);
 
     byte padding[4] = { };
@@ -132,12 +134,13 @@ bool saveBmp(const BitmapConstRef<byte, 3> &bitmap, const char *filename) {
     return !fclose(file);
 }
 
-bool saveBmp(const BitmapConstRef<byte, 4> &bitmap, const char *filename) {
+bool saveBmp(BitmapConstSection<byte, 4> bitmap, const char *filename) {
     FILE *file = fopen(filename, "wb");
     if (!file)
         return false;
 
     int dummyPaddedWidth;
+    bitmap.reorient(Y_UPWARD);
     writeBmpHeader(file, 4, bitmap.width, bitmap.height, dummyPaddedWidth);
 
     for (int y = 0; y < bitmap.height; ++y) {
@@ -155,12 +158,13 @@ bool saveBmp(const BitmapConstRef<byte, 4> &bitmap, const char *filename) {
     return !fclose(file);
 }
 
-bool saveBmp(const BitmapConstRef<float, 1> &bitmap, const char *filename) {
+bool saveBmp(BitmapConstSection<float, 1> bitmap, const char *filename) {
     FILE *file = fopen(filename, "wb");
     if (!file)
         return false;
 
     int paddedWidth;
+    bitmap.reorient(Y_UPWARD);
     writeBmpHeader(file, 1, bitmap.width, bitmap.height, paddedWidth);
 
     byte padding[4] = { };
@@ -176,12 +180,13 @@ bool saveBmp(const BitmapConstRef<float, 1> &bitmap, const char *filename) {
     return !fclose(file);
 }
 
-bool saveBmp(const BitmapConstRef<float, 3> &bitmap, const char *filename) {
+bool saveBmp(BitmapConstSection<float, 3> bitmap, const char *filename) {
     FILE *file = fopen(filename, "wb");
     if (!file)
         return false;
 
     int paddedWidth;
+    bitmap.reorient(Y_UPWARD);
     writeBmpHeader(file, 3, bitmap.width, bitmap.height, paddedWidth);
 
     byte padding[4] = { };
@@ -201,12 +206,13 @@ bool saveBmp(const BitmapConstRef<float, 3> &bitmap, const char *filename) {
     return !fclose(file);
 }
 
-bool saveBmp(const BitmapConstRef<float, 4> &bitmap, const char *filename) {
+bool saveBmp(BitmapConstSection<float, 4> bitmap, const char *filename) {
     FILE *file = fopen(filename, "wb");
     if (!file)
         return false;
 
     int dummyPaddedWidth;
+    bitmap.reorient(Y_UPWARD);
     writeBmpHeader(file, 4, bitmap.width, bitmap.height, dummyPaddedWidth);
 
     for (int y = 0; y < bitmap.height; ++y) {

+ 6 - 6
core/save-bmp.h

@@ -6,11 +6,11 @@
 namespace msdfgen {
 
 /// Saves the bitmap as a BMP file.
-bool saveBmp(const BitmapConstRef<byte, 1> &bitmap, const char *filename);
-bool saveBmp(const BitmapConstRef<byte, 3> &bitmap, const char *filename);
-bool saveBmp(const BitmapConstRef<byte, 4> &bitmap, const char *filename);
-bool saveBmp(const BitmapConstRef<float, 1> &bitmap, const char *filename);
-bool saveBmp(const BitmapConstRef<float, 3> &bitmap, const char *filename);
-bool saveBmp(const BitmapConstRef<float, 4> &bitmap, const char *filename);
+bool saveBmp(BitmapConstSection<byte, 1> bitmap, const char *filename);
+bool saveBmp(BitmapConstSection<byte, 3> bitmap, const char *filename);
+bool saveBmp(BitmapConstSection<byte, 4> bitmap, const char *filename);
+bool saveBmp(BitmapConstSection<float, 1> bitmap, const char *filename);
+bool saveBmp(BitmapConstSection<float, 3> bitmap, const char *filename);
+bool saveBmp(BitmapConstSection<float, 4> bitmap, const char *filename);
 
 }

+ 8 - 6
core/save-fl32.cpp

@@ -9,8 +9,9 @@ namespace msdfgen {
 #ifndef __BIG_ENDIAN__
 
 template <int N>
-bool saveFl32(const BitmapConstRef<float, N> &bitmap, const char *filename) {
+bool saveFl32(BitmapConstSection<float, N> bitmap, const char *filename) {
     if (FILE *f = fopen(filename, "wb")) {
+        bitmap.reorient(Y_UPWARD);
         byte header[16] = { byte('F'), byte('L'), byte('3'), byte('2') };
         header[4] = byte(bitmap.height);
         header[5] = byte(bitmap.height>>8);
@@ -22,17 +23,18 @@ bool saveFl32(const BitmapConstRef<float, N> &bitmap, const char *filename) {
         header[11] = byte(bitmap.width>>24);
         header[12] = byte(N);
         fwrite(header, 1, 16, f);
-        fwrite(bitmap.pixels, sizeof(float), N*bitmap.width*bitmap.height, f);
+        for (int y = 0; y < bitmap.height; ++y)
+            fwrite(bitmap(0, y), sizeof(float), N*bitmap.width, f);
         fclose(f);
         return true;
     }
     return false;
 }
 
-template bool saveFl32(const BitmapConstRef<float, 1> &bitmap, const char *filename);
-template bool saveFl32(const BitmapConstRef<float, 2> &bitmap, const char *filename);
-template bool saveFl32(const BitmapConstRef<float, 3> &bitmap, const char *filename);
-template bool saveFl32(const BitmapConstRef<float, 4> &bitmap, const char *filename);
+template bool saveFl32(BitmapConstSection<float, 1> bitmap, const char *filename);
+template bool saveFl32(BitmapConstSection<float, 2> bitmap, const char *filename);
+template bool saveFl32(BitmapConstSection<float, 3> bitmap, const char *filename);
+template bool saveFl32(BitmapConstSection<float, 4> bitmap, const char *filename);
 
 #endif
 

+ 1 - 1
core/save-fl32.h

@@ -7,6 +7,6 @@ namespace msdfgen {
 
 /// Saves the bitmap as an uncompressed floating-point FL32 file, which can be decoded trivially.
 template <int N>
-bool saveFl32(const BitmapConstRef<float, N> &bitmap, const char *filename);
+bool saveFl32(BitmapConstSection<float, N> bitmap, const char *filename);
 
 }

+ 18 - 12
core/save-rgba.cpp

@@ -40,11 +40,12 @@ public:
 
 };
 
-bool saveRgba(const BitmapConstRef<byte, 1> &bitmap, const char *filename) {
+bool saveRgba(BitmapConstSection<byte, 1> bitmap, const char *filename) {
     RgbaFileOutput output(filename, bitmap.width, bitmap.height);
     if (output) {
         byte rgba[4] = { byte(0), byte(0), byte(0), byte(0xff) };
-        for (int y = bitmap.height; y--;) {
+        bitmap.reorient(Y_DOWNWARD);
+        for (int y = 0; y < bitmap.height; ++y) {
             for (const byte *p = bitmap(0, y), *end = p+bitmap.width; p < end; ++p) {
                 rgba[0] = rgba[1] = rgba[2] = *p;
                 output.writePixel(rgba);
@@ -55,11 +56,12 @@ bool saveRgba(const BitmapConstRef<byte, 1> &bitmap, const char *filename) {
     return false;
 }
 
-bool saveRgba(const BitmapConstRef<byte, 3> &bitmap, const char *filename) {
+bool saveRgba(BitmapConstSection<byte, 3> bitmap, const char *filename) {
     RgbaFileOutput output(filename, bitmap.width, bitmap.height);
     if (output) {
         byte rgba[4] = { byte(0), byte(0), byte(0), byte(0xff) };
-        for (int y = bitmap.height; y--;) {
+        bitmap.reorient(Y_DOWNWARD);
+        for (int y = 0; y < bitmap.height; ++y) {
             for (const byte *p = bitmap(0, y), *end = p+3*bitmap.width; p < end; p += 3) {
                 rgba[0] = p[0], rgba[1] = p[1], rgba[2] = p[2];
                 output.writePixel(rgba);
@@ -70,21 +72,23 @@ bool saveRgba(const BitmapConstRef<byte, 3> &bitmap, const char *filename) {
     return false;
 }
 
-bool saveRgba(const BitmapConstRef<byte, 4> &bitmap, const char *filename) {
+bool saveRgba(BitmapConstSection<byte, 4> bitmap, const char *filename) {
     RgbaFileOutput output(filename, bitmap.width, bitmap.height);
     if (output) {
-        for (int y = bitmap.height; y--;)
+        bitmap.reorient(Y_DOWNWARD);
+        for (int y = 0; y < bitmap.height; ++y)
             fwrite(bitmap(0, y), 1, 4*bitmap.width, output);
         return true;
     }
     return false;
 }
 
-bool saveRgba(const BitmapConstRef<float, 1> &bitmap, const char *filename) {
+bool saveRgba(BitmapConstSection<float, 1> bitmap, const char *filename) {
     RgbaFileOutput output(filename, bitmap.width, bitmap.height);
     if (output) {
         byte rgba[4] = { byte(0), byte(0), byte(0), byte(0xff) };
-        for (int y = bitmap.height; y--;) {
+        bitmap.reorient(Y_DOWNWARD);
+        for (int y = 0; y < bitmap.height; ++y) {
             for (const float *p = bitmap(0, y), *end = p+bitmap.width; p < end; ++p) {
                 rgba[0] = rgba[1] = rgba[2] = pixelFloatToByte(*p);
                 output.writePixel(rgba);
@@ -95,11 +99,12 @@ bool saveRgba(const BitmapConstRef<float, 1> &bitmap, const char *filename) {
     return false;
 }
 
-bool saveRgba(const BitmapConstRef<float, 3> &bitmap, const char *filename) {
+bool saveRgba(BitmapConstSection<float, 3> bitmap, const char *filename) {
     RgbaFileOutput output(filename, bitmap.width, bitmap.height);
     if (output) {
         byte rgba[4] = { byte(0), byte(0), byte(0), byte(0xff) };
-        for (int y = bitmap.height; y--;) {
+        bitmap.reorient(Y_DOWNWARD);
+        for (int y = 0; y < bitmap.height; ++y) {
             for (const float *p = bitmap(0, y), *end = p+3*bitmap.width; p < end; p += 3) {
                 rgba[0] = pixelFloatToByte(p[0]);
                 rgba[1] = pixelFloatToByte(p[1]);
@@ -112,11 +117,12 @@ bool saveRgba(const BitmapConstRef<float, 3> &bitmap, const char *filename) {
     return false;
 }
 
-bool saveRgba(const BitmapConstRef<float, 4> &bitmap, const char *filename) {
+bool saveRgba(BitmapConstSection<float, 4> bitmap, const char *filename) {
     RgbaFileOutput output(filename, bitmap.width, bitmap.height);
     if (output) {
         byte rgba[4];
-        for (int y = bitmap.height; y--;) {
+        bitmap.reorient(Y_DOWNWARD);
+        for (int y = 0; y < bitmap.height; ++y) {
             for (const float *p = bitmap(0, y), *end = p+4*bitmap.width; p < end; p += 4) {
                 rgba[0] = pixelFloatToByte(p[0]);
                 rgba[1] = pixelFloatToByte(p[1]);

+ 6 - 6
core/save-rgba.h

@@ -6,11 +6,11 @@
 namespace msdfgen {
 
 /// Saves the bitmap as a simple RGBA file, which can be decoded trivially.
-bool saveRgba(const BitmapConstRef<byte, 1> &bitmap, const char *filename);
-bool saveRgba(const BitmapConstRef<byte, 3> &bitmap, const char *filename);
-bool saveRgba(const BitmapConstRef<byte, 4> &bitmap, const char *filename);
-bool saveRgba(const BitmapConstRef<float, 1> &bitmap, const char *filename);
-bool saveRgba(const BitmapConstRef<float, 3> &bitmap, const char *filename);
-bool saveRgba(const BitmapConstRef<float, 4> &bitmap, const char *filename);
+bool saveRgba(BitmapConstSection<byte, 1> bitmap, const char *filename);
+bool saveRgba(BitmapConstSection<byte, 3> bitmap, const char *filename);
+bool saveRgba(BitmapConstSection<byte, 4> bitmap, const char *filename);
+bool saveRgba(BitmapConstSection<float, 1> bitmap, const char *filename);
+bool saveRgba(BitmapConstSection<float, 3> bitmap, const char *filename);
+bool saveRgba(BitmapConstSection<float, 4> bitmap, const char *filename);
 
 }

+ 6 - 5
core/save-tiff.cpp

@@ -171,23 +171,24 @@ static bool writeTiffHeader(FILE *file, int width, int height, int channels) {
 }
 
 template <int N>
-bool saveTiffFloat(const BitmapConstRef<float, N> &bitmap, const char *filename) {
+bool saveTiffFloat(BitmapConstSection<float, N> bitmap, const char *filename) {
     FILE *file = fopen(filename, "wb");
     if (!file)
         return false;
+    bitmap.reorient(Y_DOWNWARD);
     writeTiffHeader(file, bitmap.width, bitmap.height, N);
-    for (int y = bitmap.height-1; y >= 0; --y)
+    for (int y = 0; y < bitmap.height; ++y)
         fwrite(bitmap(0, y), sizeof(float), N*bitmap.width, file);
     return !fclose(file);
 }
 
-bool saveTiff(const BitmapConstRef<float, 1> &bitmap, const char *filename) {
+bool saveTiff(const BitmapConstSection<float, 1> &bitmap, const char *filename) {
     return saveTiffFloat(bitmap, filename);
 }
-bool saveTiff(const BitmapConstRef<float, 3> &bitmap, const char *filename) {
+bool saveTiff(const BitmapConstSection<float, 3> &bitmap, const char *filename) {
     return saveTiffFloat(bitmap, filename);
 }
-bool saveTiff(const BitmapConstRef<float, 4> &bitmap, const char *filename) {
+bool saveTiff(const BitmapConstSection<float, 4> &bitmap, const char *filename) {
     return saveTiffFloat(bitmap, filename);
 }
 

+ 3 - 3
core/save-tiff.h

@@ -6,8 +6,8 @@
 namespace msdfgen {
 
 /// Saves the bitmap as an uncompressed floating-point TIFF file.
-bool saveTiff(const BitmapConstRef<float, 1> &bitmap, const char *filename);
-bool saveTiff(const BitmapConstRef<float, 3> &bitmap, const char *filename);
-bool saveTiff(const BitmapConstRef<float, 4> &bitmap, const char *filename);
+bool saveTiff(const BitmapConstSection<float, 1> &bitmap, const char *filename);
+bool saveTiff(const BitmapConstSection<float, 3> &bitmap, const char *filename);
+bool saveTiff(const BitmapConstSection<float, 4> &bitmap, const char *filename);
 
 }

+ 31 - 19
core/sdf-error-estimation.cpp

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

+ 15 - 12
core/sdf-error-estimation.h

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

+ 43 - 11
core/shape-description.cpp

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

+ 1 - 1
ext/import-font.cpp

@@ -143,7 +143,7 @@ FontHandle *adoptFreetypeFont(FT_Face ftFace) {
 
 FT_Error readFreetypeOutline(Shape &output, FT_Outline *outline, double scale) {
     output.contours.clear();
-    output.inverseYAxis = false;
+    output.setYAxisOrientation(Y_UPWARD);
     FtContext context = { };
     context.scale = scale;
     context.shape = &output;

+ 6 - 6
ext/import-svg.cpp

@@ -352,7 +352,7 @@ bool loadSvgShape(Shape &output, const char *filename, int pathIndex, Vector2 *d
     if (dimensions)
         *dimensions = dims;
     output.contours.clear();
-    output.inverseYAxis = true;
+    output.setYAxisOrientation(Y_DOWNWARD);
     return buildShapeFromSvgPath(output, pd, ENDPOINT_SNAP_RANGE_PROPORTION*dims.length());
 }
 
@@ -531,7 +531,7 @@ bool loadSvgShape(Shape &output, const char *filename, int pathIndex, Vector2 *d
     if (dimensions)
         *dimensions = dims;
     output.contours.clear();
-    output.inverseYAxis = true;
+    output.setYAxisOrientation(Y_DOWNWARD);
     return buildShapeFromSvgPath(output, xmlDecode(pathAggregator.pathDefs[pathIndex].start, pathAggregator.pathDefs[pathIndex].end).c_str(), ENDPOINT_SNAP_RANGE_PROPORTION*dims.length());
 }
 
@@ -565,7 +565,7 @@ int loadSvgShape(Shape &output, Shape::Bounds &viewBox, const char *filename) {
     viewBox.r = viewBox.l+dims.x;
     viewBox.t = viewBox.b+dims.y;
     output.contours.clear();
-    output.inverseYAxis = true;
+    output.setYAxisOrientation(Y_DOWNWARD);
     if (!buildShapeFromSvgPath(output, pd, ENDPOINT_SNAP_RANGE_PROPORTION*dims.length()))
         return SVG_IMPORT_FAILURE;
     return flags;
@@ -592,7 +592,7 @@ int loadSvgShape(Shape &output, Shape::Bounds &viewBox, const char *filename) {
     viewBox.r = viewBox.l+dims.x;
     viewBox.t = viewBox.b+dims.y;
     output.contours.clear();
-    output.inverseYAxis = true;
+    output.setYAxisOrientation(Y_DOWNWARD);
     if (!buildShapeFromSvgPath(output, xmlDecode(pathAggregator.pathDefs.back().start, pathAggregator.pathDefs.back().end).c_str(), ENDPOINT_SNAP_RANGE_PROPORTION*dims.length()))
         return SVG_IMPORT_FAILURE;
     return SVG_IMPORT_SUCCESS_FLAG|pathAggregator.flags;
@@ -762,7 +762,7 @@ int loadSvgShape(Shape &output, Shape::Bounds &viewBox, const char *filename) {
     if (!((flags&SVG_IMPORT_SUCCESS_FLAG) && Simplify(fullPath, &fullPath)))
         return SVG_IMPORT_FAILURE;
     shapeFromSkiaPath(output, fullPath);
-    output.inverseYAxis = true;
+    output.setYAxisOrientation(Y_DOWNWARD);
     output.orientContours();
 
     viewBox.l = 0, viewBox.b = 0;
@@ -1038,7 +1038,7 @@ int parseSvgShape(Shape &output, Shape::Bounds &viewBox, const char *svgData, si
         return SVG_IMPORT_FAILURE;
 
     shapeFromSkiaPath(output, svg.fullPath);
-    output.inverseYAxis = true;
+    output.setYAxisOrientation(Y_DOWNWARD);
     output.orientContours();
 
     viewBox = svg.viewBox;

+ 51 - 32
ext/save-png.cpp

@@ -45,7 +45,7 @@ static void pngFlush(png_structp png) {
     fflush(reinterpret_cast<FILE *>(png_get_io_ptr(png)));
 }
 
-static bool pngSave(const byte *pixels, int width, int height, int channels, int colorType, const char *filename) {
+static bool pngSave(const byte *pixels, int width, int height, int rowStride, int colorType, const char *filename) {
     if (!(pixels && width && height))
         return false;
     png_structp png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, &pngIgnoreError, &pngIgnoreError);
@@ -61,7 +61,7 @@ static bool pngSave(const byte *pixels, int width, int height, int channels, int
     guard.setFile(file);
     std::vector<const byte *> rows(height);
     for (int y = 0; y < height; ++y)
-        rows[y] = pixels+channels*width*(height-y-1);
+        rows[y] = pixels+rowStride*y;
     if (setjmp(png_jmpbuf(png)))
         return false;
     png_set_write_fn(png, file, &pngWrite, &pngFlush);
@@ -72,38 +72,48 @@ static bool pngSave(const byte *pixels, int width, int height, int channels, int
     return true;
 }
 
-static bool pngSave(const float *pixels, int width, int height, int channels, int colorType, const char *filename) {
+static bool pngSave(const float *pixels, int width, int height, int rowStride, int channels, int colorType, const char *filename) {
     if (!(pixels && width && height))
         return false;
-    int subpixels = channels*width*height;
-    std::vector<byte> bytePixels(subpixels);
-    for (int i = 0; i < subpixels; ++i)
-        bytePixels[i] = pixelFloatToByte(pixels[i]);
-    return pngSave(&bytePixels[0], width, height, channels, colorType, filename);
+    std::vector<byte> bytePixels(channels*width*height);
+    byte *dst = &bytePixels[0];
+    const float *rowStart = pixels;
+    for (int y = 0; y < height; ++y) {
+        for (const float *src = rowStart, *end = rowStart+channels*width; src < end; ++src)
+            *dst++ = pixelFloatToByte(*src);
+        rowStart += rowStride;
+    }
+    return pngSave(&bytePixels[0], width, height, channels*width, colorType, filename);
 }
 
-bool savePng(const BitmapConstRef<byte, 1> &bitmap, const char *filename) {
-    return pngSave(bitmap.pixels, bitmap.width, bitmap.height, 1, PNG_COLOR_TYPE_GRAY, filename);
+bool savePng(BitmapConstSection<byte, 1> bitmap, const char *filename) {
+    bitmap.reorient(Y_DOWNWARD);
+    return pngSave(bitmap.pixels, bitmap.width, bitmap.height, bitmap.rowStride, PNG_COLOR_TYPE_GRAY, filename);
 }
 
-bool savePng(const BitmapConstRef<byte, 3> &bitmap, const char *filename) {
-    return pngSave(bitmap.pixels, bitmap.width, bitmap.height, 3, PNG_COLOR_TYPE_RGB, filename);
+bool savePng(BitmapConstSection<byte, 3> bitmap, const char *filename) {
+    bitmap.reorient(Y_DOWNWARD);
+    return pngSave(bitmap.pixels, bitmap.width, bitmap.height, bitmap.rowStride, PNG_COLOR_TYPE_RGB, filename);
 }
 
-bool savePng(const BitmapConstRef<byte, 4> &bitmap, const char *filename) {
-    return pngSave(bitmap.pixels, bitmap.width, bitmap.height, 4, PNG_COLOR_TYPE_RGB_ALPHA, filename);
+bool savePng(BitmapConstSection<byte, 4> bitmap, const char *filename) {
+    bitmap.reorient(Y_DOWNWARD);
+    return pngSave(bitmap.pixels, bitmap.width, bitmap.height, bitmap.rowStride, PNG_COLOR_TYPE_RGB_ALPHA, filename);
 }
 
-bool savePng(const BitmapConstRef<float, 1> &bitmap, const char *filename) {
-    return pngSave(bitmap.pixels, bitmap.width, bitmap.height, 1, PNG_COLOR_TYPE_GRAY, filename);
+bool savePng(BitmapConstSection<float, 1> bitmap, const char *filename) {
+    bitmap.reorient(Y_DOWNWARD);
+    return pngSave(bitmap.pixels, bitmap.width, bitmap.height, bitmap.rowStride, 1, PNG_COLOR_TYPE_GRAY, filename);
 }
 
-bool savePng(const BitmapConstRef<float, 3> &bitmap, const char *filename) {
-    return pngSave(bitmap.pixels, bitmap.width, bitmap.height, 3, PNG_COLOR_TYPE_RGB, filename);
+bool savePng(BitmapConstSection<float, 3> bitmap, const char *filename) {
+    bitmap.reorient(Y_DOWNWARD);
+    return pngSave(bitmap.pixels, bitmap.width, bitmap.height, bitmap.rowStride, 3, PNG_COLOR_TYPE_RGB, filename);
 }
 
-bool savePng(const BitmapConstRef<float, 4> &bitmap, const char *filename) {
-    return pngSave(bitmap.pixels, bitmap.width, bitmap.height, 4, PNG_COLOR_TYPE_RGB_ALPHA, filename);
+bool savePng(BitmapConstSection<float, 4> bitmap, const char *filename) {
+    bitmap.reorient(Y_DOWNWARD);
+    return pngSave(bitmap.pixels, bitmap.width, bitmap.height, bitmap.rowStride, 4, PNG_COLOR_TYPE_RGB_ALPHA, filename);
 }
 
 }
@@ -116,58 +126,67 @@ bool savePng(const BitmapConstRef<float, 4> &bitmap, const char *filename) {
 
 namespace msdfgen {
 
-bool savePng(const BitmapConstRef<byte, 1> &bitmap, const char *filename) {
+bool savePng(BitmapConstSection<byte, 1> bitmap, const char *filename) {
     std::vector<byte> pixels(bitmap.width*bitmap.height);
+    bitmap.reorient(Y_DOWNWARD);
     for (int y = 0; y < bitmap.height; ++y)
-        memcpy(&pixels[bitmap.width*y], bitmap(0, bitmap.height-y-1), bitmap.width);
+        memcpy(&pixels[bitmap.width*y], bitmap(0, y), 1*bitmap.width);
     return !lodepng::encode(filename, pixels, bitmap.width, bitmap.height, LCT_GREY);
 }
 
-bool savePng(const BitmapConstRef<byte, 3> &bitmap, const char *filename) {
+bool savePng(BitmapConstSection<byte, 3> bitmap, const char *filename) {
     std::vector<byte> pixels(3*bitmap.width*bitmap.height);
+    bitmap.reorient(Y_DOWNWARD);
     for (int y = 0; y < bitmap.height; ++y)
-        memcpy(&pixels[3*bitmap.width*y], bitmap(0, bitmap.height-y-1), 3*bitmap.width);
+        memcpy(&pixels[3*bitmap.width*y], bitmap(0, y), 3*bitmap.width);
     return !lodepng::encode(filename, pixels, bitmap.width, bitmap.height, LCT_RGB);
 }
 
-bool savePng(const BitmapConstRef<byte, 4> &bitmap, const char *filename) {
+bool savePng(BitmapConstSection<byte, 4> bitmap, const char *filename) {
     std::vector<byte> pixels(4*bitmap.width*bitmap.height);
+    bitmap.reorient(Y_DOWNWARD);
     for (int y = 0; y < bitmap.height; ++y)
-        memcpy(&pixels[4*bitmap.width*y], bitmap(0, bitmap.height-y-1), 4*bitmap.width);
+        memcpy(&pixels[4*bitmap.width*y], bitmap(0, y), 4*bitmap.width);
     return !lodepng::encode(filename, pixels, bitmap.width, bitmap.height, LCT_RGBA);
 }
 
-bool savePng(const BitmapConstRef<float, 1> &bitmap, const char *filename) {
+bool savePng(BitmapConstSection<float, 1> bitmap, const char *filename) {
     std::vector<byte> pixels(bitmap.width*bitmap.height);
     std::vector<byte>::iterator it = pixels.begin();
-    for (int y = bitmap.height-1; y >= 0; --y)
+    bitmap.reorient(Y_DOWNWARD);
+    for (int y = 0; y < bitmap.height; ++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) {
+bool savePng(BitmapConstSection<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)
+    bitmap.reorient(Y_DOWNWARD);
+    for (int y = 0; y < bitmap.height; ++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);
 }
 
-bool savePng(const BitmapConstRef<float, 4> &bitmap, const char *filename) {
+bool savePng(BitmapConstSection<float, 4> bitmap, const char *filename) {
     std::vector<byte> pixels(4*bitmap.width*bitmap.height);
     std::vector<byte>::iterator it = pixels.begin();
-    for (int y = bitmap.height-1; y >= 0; --y)
+    bitmap.reorient(Y_DOWNWARD);
+    for (int y = 0; y < bitmap.height; ++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]);
             *it++ = pixelFloatToByte(bitmap(x, y)[3]);
         }
+    }
     return !lodepng::encode(filename, pixels, bitmap.width, bitmap.height, LCT_RGBA);
 }
 

+ 6 - 6
ext/save-png.h

@@ -8,12 +8,12 @@
 namespace msdfgen {
 
 /// Saves the bitmap as a PNG file.
-bool savePng(const BitmapConstRef<byte, 1> &bitmap, const char *filename);
-bool savePng(const BitmapConstRef<byte, 3> &bitmap, const char *filename);
-bool savePng(const BitmapConstRef<byte, 4> &bitmap, const char *filename);
-bool savePng(const BitmapConstRef<float, 1> &bitmap, const char *filename);
-bool savePng(const BitmapConstRef<float, 3> &bitmap, const char *filename);
-bool savePng(const BitmapConstRef<float, 4> &bitmap, const char *filename);
+bool savePng(BitmapConstSection<byte, 1> bitmap, const char *filename);
+bool savePng(BitmapConstSection<byte, 3> bitmap, const char *filename);
+bool savePng(BitmapConstSection<byte, 4> bitmap, const char *filename);
+bool savePng(BitmapConstSection<float, 1> bitmap, const char *filename);
+bool savePng(BitmapConstSection<float, 3> bitmap, const char *filename);
+bool savePng(BitmapConstSection<float, 4> bitmap, const char *filename);
 
 }
 

+ 57 - 36
main.cpp

@@ -194,60 +194,75 @@ static FontHandle *loadVarFont(FreetypeHandle *library, const char *filename) {
 #endif
 
 template <int N>
-static void invertColor(const BitmapRef<float, N> &bitmap) {
-    const float *end = bitmap.pixels+N*bitmap.width*bitmap.height;
-    for (float *p = bitmap.pixels; p < end; ++p)
-        *p = 1.f-*p;
+static void invertColor(const BitmapSection<float, N> &bitmap) {
+    for (int y = 0; y < bitmap.height; ++y) {
+        float *p = bitmap(0, y);
+        for (const float *end = p+N*bitmap.width; p < end; ++p)
+            *p = 1.f-*p;
+    }
 }
 
-static bool writeTextBitmap(FILE *file, const float *values, int cols, int rows) {
+static bool writeTextBitmap(FILE *file, const float *values, int cols, int rows, int rowStride) {
     for (int row = 0; row < rows; ++row) {
-        for (int col = 0; col < cols; ++col) {
-            int v = clamp(int((*values++)*0x100), 0xff);
-            fprintf(file, col ? " %02X" : "%02X", v);
-        }
+        const float *cur = values;
+        for (int col = 0; col < cols; ++col)
+            fprintf(file, col ? " %02X" : "%02X", int(pixelFloatToByte(*cur++)));
         fprintf(file, "\n");
+        values += rowStride;
     }
     return true;
 }
 
-static bool writeTextBitmapFloat(FILE *file, const float *values, int cols, int rows) {
+static bool writeTextBitmapFloat(FILE *file, const float *values, int cols, int rows, int rowStride) {
     for (int row = 0; row < rows; ++row) {
-        for (int col = 0; col < cols; ++col) {
-            fprintf(file, col ? " %.9g" : "%.9g", *values++);
-        }
+        const float *cur = values;
+        for (int col = 0; col < cols; ++col)
+            fprintf(file, col ? " %.9g" : "%.9g", *cur++);
         fprintf(file, "\n");
+        values += rowStride;
     }
     return true;
 }
 
-static bool writeBinBitmap(FILE *file, const float *values, int count) {
-    for (int pos = 0; pos < count; ++pos) {
-        unsigned char v = clamp(int((*values++)*0x100), 0xff);
-        fwrite(&v, 1, 1, file);
+static bool writeBinBitmap(FILE *file, const float *values, int cols, int rows, int rowStride) {
+    for (int row = 0; row < rows; ++row) {
+        const float *cur = values;
+        for (int col = 0; col < cols; ++col) {
+            byte v = pixelFloatToByte(*cur++);
+            fwrite(&v, 1, 1, file);
+        }
+        values += rowStride;
     }
     return true;
 }
 
 #ifdef __BIG_ENDIAN__
-static bool writeBinBitmapFloatBE(FILE *file, const float *values, int count)
+static bool writeBinBitmapFloatBE(FILE *file, const float *values, int cols, int rows, int rowStride)
 #else
-static bool writeBinBitmapFloat(FILE *file, const float *values, int count)
+static bool writeBinBitmapFloat(FILE *file, const float *values, int cols, int rows, int rowStride)
 #endif
 {
-    return (int) fwrite(values, sizeof(float), count, file) == count;
+    for (int row = 0; row < rows; ++row) {
+        fwrite(values, sizeof(float), cols, file);
+        values += rowStride;
+    }
+    return true;
 }
 
 #ifdef __BIG_ENDIAN__
-static bool writeBinBitmapFloat(FILE *file, const float *values, int count)
+static bool writeBinBitmapFloat(FILE *file, const float *values, int cols, int rows, int rowStride)
 #else
-static bool writeBinBitmapFloatBE(FILE *file, const float *values, int count)
+static bool writeBinBitmapFloatBE(FILE *file, const float *values, int cols, int rows, int rowStride)
 #endif
 {
-    for (int pos = 0; pos < count; ++pos) {
-        const unsigned char *b = reinterpret_cast<const unsigned char *>(values++);
-        for (int i = sizeof(float)-1; i >= 0; --i)
-            fwrite(b+i, 1, 1, file);
+    for (int row = 0; row < rows; ++row) {
+        const float *cur = values;
+        for (int col = 0; col < cols; ++col) {
+            const unsigned char *b = reinterpret_cast<const unsigned char *>(cur++);
+            for (int i = int(sizeof(float)); i--;)
+                fwrite(b+i, 1, 1, file);
+        }
+        values += rowStride;
     }
     return true;
 }
@@ -260,7 +275,7 @@ static bool cmpExtension(const char *path, const char *ext) {
 }
 
 template <int N>
-static const char *writeOutput(const BitmapConstRef<float, N> &bitmap, const char *filename, Format &format) {
+static const char *writeOutput(const BitmapConstSection<float, N> &bitmap, const char *filename, Format &format) {
     if (filename) {
         if (format == AUTO) {
         #if defined(MSDFGEN_EXTENSIONS) && !defined(MSDFGEN_DISABLE_PNG)
@@ -290,9 +305,9 @@ static const char *writeOutput(const BitmapConstRef<float, N> &bitmap, const cha
                 FILE *file = fopen(filename, "w");
                 if (!file) return "Failed to write output text file.";
                 if (format == TEXT)
-                    writeTextBitmap(file, bitmap.pixels, N*bitmap.width, bitmap.height);
+                    writeTextBitmap(file, bitmap.pixels, N*bitmap.width, bitmap.height, bitmap.rowStride);
                 else if (format == TEXT_FLOAT)
-                    writeTextBitmapFloat(file, bitmap.pixels, N*bitmap.width, bitmap.height);
+                    writeTextBitmapFloat(file, bitmap.pixels, N*bitmap.width, bitmap.height, bitmap.rowStride);
                 fclose(file);
                 return NULL;
             }
@@ -300,11 +315,11 @@ static const char *writeOutput(const BitmapConstRef<float, N> &bitmap, const cha
                 FILE *file = fopen(filename, "wb");
                 if (!file) return "Failed to write output binary file.";
                 if (format == BINARY)
-                    writeBinBitmap(file, bitmap.pixels, N*bitmap.width*bitmap.height);
+                    writeBinBitmap(file, bitmap.pixels, N*bitmap.width, bitmap.height, bitmap.rowStride);
                 else if (format == BINARY_FLOAT)
-                    writeBinBitmapFloat(file, bitmap.pixels, N*bitmap.width*bitmap.height);
+                    writeBinBitmapFloat(file, bitmap.pixels, N*bitmap.width, bitmap.height, bitmap.rowStride);
                 else if (format == BINARY_FLOAT_BE)
-                    writeBinBitmapFloatBE(file, bitmap.pixels, N*bitmap.width*bitmap.height);
+                    writeBinBitmapFloatBE(file, bitmap.pixels, N*bitmap.width, bitmap.height, bitmap.rowStride);
                 fclose(file);
                 return NULL;
             }
@@ -312,9 +327,9 @@ static const char *writeOutput(const BitmapConstRef<float, N> &bitmap, const cha
         }
     } else {
         if (format == AUTO || format == TEXT)
-            writeTextBitmap(stdout, bitmap.pixels, N*bitmap.width, bitmap.height);
+            writeTextBitmap(stdout, bitmap.pixels, N*bitmap.width, bitmap.height, bitmap.rowStride);
         else if (format == TEXT_FLOAT)
-            writeTextBitmapFloat(stdout, bitmap.pixels, N*bitmap.width, bitmap.height);
+            writeTextBitmapFloat(stdout, bitmap.pixels, N*bitmap.width, bitmap.height, bitmap.rowStride);
         else
             return "Unsupported format for standard output.";
     }
@@ -1172,8 +1187,14 @@ int main(int argc, const char *const *argv) {
             out = fopen(output, "w");
         if (!out)
             ABORT("Failed to write output file.");
-        if (shape.inverseYAxis)
-            fprintf(out, "inverseY = true\n");
+        switch (shape.getYAxisOrientation()) {
+            case Y_UPWARD:
+                fprintf(out, "Y-axis upward\n");
+                break;
+            case Y_DOWNWARD:
+                fprintf(out, "Y-axis downward\n");
+                break;
+        }
         if (svgViewBox.l < svgViewBox.r && svgViewBox.b < svgViewBox.t)
             fprintf(out, "view box = %.17g, %.17g, %.17g, %.17g\n", svgViewBox.l, svgViewBox.b, svgViewBox.r, svgViewBox.t);
         if (bounds.l < bounds.r && bounds.b < bounds.t)

+ 19 - 19
msdfgen.h

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