Browse Source

Experimental version of 7TSDF mode

Chlumsky 1 year ago
parent
commit
7f6a8aebba
8 changed files with 373 additions and 0 deletions
  1. 22 0
      core/contour-combiners.cpp
  2. 136 0
      core/edge-coloring.cpp
  3. 2 0
      core/edge-coloring.h
  4. 135 0
      core/edge-selectors.cpp
  5. 21 0
      core/edge-selectors.h
  6. 25 0
      core/msdfgen.cpp
  7. 29 0
      main.cpp
  8. 3 0
      msdfgen.h

+ 22 - 0
core/contour-combiners.cpp

@@ -16,6 +16,17 @@ static void initDistance(MultiDistance &distance) {
     distance.b = -DBL_MAX;
 }
 
+static void initDistance(M7AndTrueDistanceSelector::DistanceType &distance) {
+    distance.p[0] = -DBL_MAX;
+    distance.p[1] = -DBL_MAX;
+    distance.p[2] = -DBL_MAX;
+    distance.p[3] = -DBL_MAX;
+    distance.p[4] = -DBL_MAX;
+    distance.p[5] = -DBL_MAX;
+    distance.p[6] = -DBL_MAX;
+    distance.t = -DBL_MAX;
+}
+
 static double resolveDistance(double distance) {
     return distance;
 }
@@ -24,6 +35,15 @@ static double resolveDistance(const MultiDistance &distance) {
     return median(distance.r, distance.g, distance.b);
 }
 
+static int cmpDbl(const void *a, const void *b) {
+    return *reinterpret_cast<const double *>(a) < *reinterpret_cast<const double *>(b);
+}
+
+static double resolveDistance(M7AndTrueDistanceSelector::DistanceType distance) {
+    qsort(distance.p, 7, sizeof(*distance.p), &cmpDbl);
+    return distance.p[3];
+}
+
 template <class EdgeSelector>
 SimpleContourCombiner<EdgeSelector>::SimpleContourCombiner(const Shape &shape) { }
 
@@ -46,6 +66,7 @@ template class SimpleContourCombiner<TrueDistanceSelector>;
 template class SimpleContourCombiner<PseudoDistanceSelector>;
 template class SimpleContourCombiner<MultiDistanceSelector>;
 template class SimpleContourCombiner<MultiAndTrueDistanceSelector>;
+template class SimpleContourCombiner<M7AndTrueDistanceSelector>;
 
 template <class EdgeSelector>
 OverlappingContourCombiner<EdgeSelector>::OverlappingContourCombiner(const Shape &shape) {
@@ -130,5 +151,6 @@ template class OverlappingContourCombiner<TrueDistanceSelector>;
 template class OverlappingContourCombiner<PseudoDistanceSelector>;
 template class OverlappingContourCombiner<MultiDistanceSelector>;
 template class OverlappingContourCombiner<MultiAndTrueDistanceSelector>;
+template class OverlappingContourCombiner<M7AndTrueDistanceSelector>;
 
 }

+ 136 - 0
core/edge-coloring.cpp

@@ -498,4 +498,140 @@ void edgeColoringByDistance(Shape &shape, double angleThreshold, unsigned long l
     }
 }
 
+void edgeColoring7Random(Shape &shape, double angleThreshold, unsigned long long seed) {
+    /*int colorPool[] = {
+        0b00001111,
+        0b00010111,
+        0b00011011,
+        0b00011101,
+        0b00011110,
+        0b00100111,
+        0b00101011,
+        0b00101101,
+        0b00101110,
+        0b00110011,
+        0b00110101,
+        0b00110110,
+        0b00111001,
+        0b00111010,
+        0b00111100,
+        0b01000111,
+        0b01001011,
+        0b01001101,
+        0b01001110,
+        0b01010011,
+        0b01010101,
+        0b01010110,
+        0b01011001,
+        0b01011010,
+        0b01011100,
+        0b01100011,
+        0b01100101,
+        0b01100110,
+        0b01101001,
+        0b01101010,
+        0b01101100,
+        0b01110001,
+        0b01110010,
+        0b01110100,
+        0b01111000,
+    };
+    static_assert(sizeof(colorPool) == 35*sizeof(*colorPool), "Expected 35 colors in color pool");*/
+    int colorPool[] = {
+        // All pairs share exactly 2 set bits
+        0b00001111,
+        0b00111100,
+        0b00110011,
+        0b01010101,
+        0b01011010,
+        0b01100110,
+        0b01101001,
+    };
+    
+    int colorI = 0;
+    #define NEXT_COLOR() EdgeColor(colorPool[colorI++%(int(sizeof(colorPool)/sizeof(*colorPool)))])
+
+    double crossThreshold = sin(angleThreshold);
+    std::vector<int> corners;
+    for (std::vector<Contour>::iterator contour = shape.contours.begin(); contour != shape.contours.end(); ++contour) {
+        if (seed) {
+            // Shuffle colorPool
+            for (int i = int(sizeof(colorPool)/sizeof(*colorPool))-1; i; --i) {
+                seed = 6364136223846793005ll*seed+1442695040888963407ll; // LCG
+                int j = int(seed%(i+1));
+                int tmp = colorPool[i];
+                colorPool[i] = colorPool[j];
+                colorPool[j] = tmp;
+            }
+        }
+        // Identify corners
+        corners.clear();
+        if (!contour->edges.empty()) {
+            Vector2 prevDirection = contour->edges.back()->direction(1);
+            int index = 0;
+            for (std::vector<EdgeHolder>::const_iterator edge = contour->edges.begin(); edge != contour->edges.end(); ++edge, ++index) {
+                if (isCorner(prevDirection.normalize(), (*edge)->direction(0).normalize(), crossThreshold))
+                    corners.push_back(index);
+                prevDirection = (*edge)->direction(1);
+            }
+        }
+
+        // Smooth contour
+        if (corners.empty()) {
+            EdgeColor color = NEXT_COLOR();
+            for (std::vector<EdgeHolder>::iterator edge = contour->edges.begin(); edge != contour->edges.end(); ++edge)
+                (*edge)->color = color;
+        }
+        // "Teardrop" case
+        else if (corners.size() == 1) {
+            EdgeColor colors[3];
+            colors[0] = NEXT_COLOR();
+            colors[2] = NEXT_COLOR();
+            colors[1] = EdgeColor(colors[0]|colors[2]);
+            int corner = corners[0];
+            if (contour->edges.size() >= 3) {
+                int m = (int) contour->edges.size();
+                for (int i = 0; i < m; ++i)
+                    contour->edges[(corner+i)%m]->color = (colors+1)[int(3+2.875*i/(m-1)-1.4375+.5)-3];
+            } else if (contour->edges.size() >= 1) {
+                // Less than three edge segments for three colors => edges must be split
+                EdgeSegment *parts[7] = { };
+                contour->edges[0]->splitInThirds(parts[0+3*corner], parts[1+3*corner], parts[2+3*corner]);
+                if (contour->edges.size() >= 2) {
+                    contour->edges[1]->splitInThirds(parts[3-3*corner], parts[4-3*corner], parts[5-3*corner]);
+                    parts[0]->color = parts[1]->color = colors[0];
+                    parts[2]->color = parts[3]->color = colors[1];
+                    parts[4]->color = parts[5]->color = colors[2];
+                } else {
+                    parts[0]->color = colors[0];
+                    parts[1]->color = colors[1];
+                    parts[2]->color = colors[2];
+                }
+                contour->edges.clear();
+                for (int i = 0; parts[i]; ++i)
+                    contour->edges.push_back(EdgeHolder(parts[i]));
+            }
+        }
+        // Multiple corners
+        else {
+            int cornerCount = (int) corners.size();
+            int spline = 0;
+            int start = corners[0];
+            int m = (int) contour->edges.size();
+            EdgeColor color = NEXT_COLOR();
+            EdgeColor initialColor = color;
+            for (int i = 0; i < m; ++i) {
+                int index = (start+i)%m;
+                if (spline+1 < cornerCount && corners[spline+1] == index) {
+                    ++spline;
+                    color = NEXT_COLOR();
+                    if (spline == cornerCount-1 && color == initialColor)
+                        color = NEXT_COLOR();
+                }
+                contour->edges[index]->color = color;
+            }
+        }
+    }
+}
+
 }

+ 2 - 0
core/edge-coloring.h

@@ -26,4 +26,6 @@ void edgeColoringInkTrap(Shape &shape, double angleThreshold, unsigned long long
  */
 void edgeColoringByDistance(Shape &shape, double angleThreshold, unsigned long long seed = 0);
 
+void edgeColoring7Random(Shape &shape, double angleThreshold, unsigned long long seed = 0);
+
 }

+ 135 - 0
core/edge-selectors.cpp

@@ -258,4 +258,139 @@ MultiAndTrueDistanceSelector::DistanceType MultiAndTrueDistanceSelector::distanc
     return mtd;
 }
 
+void M7AndTrueDistanceSelector::reset(const Point2 &p) {
+    double delta = DISTANCE_DELTA_FACTOR*(p-this->p).length();
+    b[0].reset(delta);
+    b[1].reset(delta);
+    b[2].reset(delta);
+    b[3].reset(delta);
+    b[4].reset(delta);
+    b[5].reset(delta);
+    b[6].reset(delta);
+    this->p = p;
+}
+
+void M7AndTrueDistanceSelector::addEdge(EdgeCache &cache, const EdgeSegment *prevEdge, const EdgeSegment *edge, const EdgeSegment *nextEdge) {
+    if (
+        (edge->color&1<<0 && b[0].isEdgeRelevant(cache, edge, p)) ||
+        (edge->color&1<<1 && b[1].isEdgeRelevant(cache, edge, p)) ||
+        (edge->color&1<<2 && b[2].isEdgeRelevant(cache, edge, p)) ||
+        (edge->color&1<<3 && b[3].isEdgeRelevant(cache, edge, p)) ||
+        (edge->color&1<<4 && b[4].isEdgeRelevant(cache, edge, p)) ||
+        (edge->color&1<<5 && b[5].isEdgeRelevant(cache, edge, p)) ||
+        (edge->color&1<<6 && b[6].isEdgeRelevant(cache, edge, p))
+    ) {
+        double param;
+        SignedDistance distance = edge->signedDistance(p, param);
+        if (edge->color&1<<0)
+            b[0].addEdgeTrueDistance(edge, distance, param);
+        if (edge->color&1<<1)
+            b[1].addEdgeTrueDistance(edge, distance, param);
+        if (edge->color&1<<2)
+            b[2].addEdgeTrueDistance(edge, distance, param);
+        if (edge->color&1<<3)
+            b[3].addEdgeTrueDistance(edge, distance, param);
+        if (edge->color&1<<4)
+            b[4].addEdgeTrueDistance(edge, distance, param);
+        if (edge->color&1<<5)
+            b[5].addEdgeTrueDistance(edge, distance, param);
+        if (edge->color&1<<6)
+            b[6].addEdgeTrueDistance(edge, distance, param);
+        cache.point = p;
+        cache.absDistance = fabs(distance.distance);
+
+        Vector2 ap = p-edge->point(0);
+        Vector2 bp = p-edge->point(1);
+        Vector2 aDir = edge->direction(0).normalize(true);
+        Vector2 bDir = edge->direction(1).normalize(true);
+        Vector2 prevDir = prevEdge->direction(1).normalize(true);
+        Vector2 nextDir = nextEdge->direction(0).normalize(true);
+        double add = dotProduct(ap, (prevDir+aDir).normalize(true));
+        double bdd = -dotProduct(bp, (bDir+nextDir).normalize(true));
+        if (add > 0) {
+            double pd = distance.distance;
+            if (PseudoDistanceSelectorBase::getPseudoDistance(pd, ap, -aDir)) {
+                pd = -pd;
+                if (edge->color&1<<0)
+                    b[0].addEdgePseudoDistance(pd);
+                if (edge->color&1<<1)
+                    b[1].addEdgePseudoDistance(pd);
+                if (edge->color&1<<2)
+                    b[2].addEdgePseudoDistance(pd);
+                if (edge->color&1<<3)
+                    b[3].addEdgePseudoDistance(pd);
+                if (edge->color&1<<4)
+                    b[4].addEdgePseudoDistance(pd);
+                if (edge->color&1<<5)
+                    b[5].addEdgePseudoDistance(pd);
+                if (edge->color&1<<6)
+                    b[6].addEdgePseudoDistance(pd);
+            }
+            cache.aPseudoDistance = pd;
+        }
+        if (bdd > 0) {
+            double pd = distance.distance;
+            if (PseudoDistanceSelectorBase::getPseudoDistance(pd, bp, bDir)) {
+                if (edge->color&1<<0)
+                    b[0].addEdgePseudoDistance(pd);
+                if (edge->color&1<<1)
+                    b[1].addEdgePseudoDistance(pd);
+                if (edge->color&1<<2)
+                    b[2].addEdgePseudoDistance(pd);
+                if (edge->color&1<<3)
+                    b[3].addEdgePseudoDistance(pd);
+                if (edge->color&1<<4)
+                    b[4].addEdgePseudoDistance(pd);
+                if (edge->color&1<<5)
+                    b[5].addEdgePseudoDistance(pd);
+                if (edge->color&1<<6)
+                    b[6].addEdgePseudoDistance(pd);
+            }
+            cache.bPseudoDistance = pd;
+        }
+        cache.aDomainDistance = add;
+        cache.bDomainDistance = bdd;
+    }
+}
+
+void M7AndTrueDistanceSelector::merge(const M7AndTrueDistanceSelector &other) {
+    b[0].merge(other.b[0]);
+    b[1].merge(other.b[1]);
+    b[2].merge(other.b[2]);
+    b[3].merge(other.b[3]);
+    b[4].merge(other.b[4]);
+    b[5].merge(other.b[5]);
+    b[6].merge(other.b[6]);
+}
+
+M7AndTrueDistanceSelector::DistanceType M7AndTrueDistanceSelector::distance() const {
+    DistanceType multiDistance;
+    multiDistance.p[0] = b[0].computeDistance(p);
+    multiDistance.p[1] = b[1].computeDistance(p);
+    multiDistance.p[2] = b[2].computeDistance(p);
+    multiDistance.p[3] = b[3].computeDistance(p);
+    multiDistance.p[4] = b[4].computeDistance(p);
+    multiDistance.p[5] = b[5].computeDistance(p);
+    multiDistance.p[6] = b[6].computeDistance(p);
+    multiDistance.t = trueDistance().distance;
+    return multiDistance;
+}
+
+SignedDistance M7AndTrueDistanceSelector::trueDistance() const {
+    SignedDistance distance = b[0].trueDistance();
+    if (b[1].trueDistance() < distance)
+        distance = b[1].trueDistance();
+    if (b[2].trueDistance() < distance)
+        distance = b[2].trueDistance();
+    if (b[3].trueDistance() < distance)
+        distance = b[3].trueDistance();
+    if (b[4].trueDistance() < distance)
+        distance = b[4].trueDistance();
+    if (b[5].trueDistance() < distance)
+        distance = b[5].trueDistance();
+    if (b[6].trueDistance() < distance)
+        distance = b[6].trueDistance();
+    return distance;
+}
+
 }

+ 21 - 0
core/edge-selectors.h

@@ -114,4 +114,25 @@ public:
 
 };
 
+class M7AndTrueDistanceSelector {
+
+public:
+    typedef PseudoDistanceSelectorBase::EdgeCache EdgeCache;
+    struct DistanceType {
+        double p[7];
+        double t;
+    };
+
+    void reset(const Point2 &p);
+    void addEdge(EdgeCache &cache, const EdgeSegment *prevEdge, const EdgeSegment *edge, const EdgeSegment *nextEdge);
+    void merge(const M7AndTrueDistanceSelector &other);
+    DistanceType distance() const;
+    SignedDistance trueDistance() const;
+
+private:
+    Point2 p;
+    PseudoDistanceSelectorBase b[7];
+
+};
+
 }

+ 25 - 0
core/msdfgen.cpp

@@ -49,6 +49,24 @@ public:
     }
 };
 
+template <>
+class DistancePixelConversion<M7AndTrueDistanceSelector::DistanceType> {
+    double invRange;
+public:
+    typedef BitmapRef<float, 8> BitmapRefType;
+    inline explicit DistancePixelConversion(double range) : invRange(1/range) { }
+    inline void operator()(float *pixels, const M7AndTrueDistanceSelector::DistanceType &distance) const {
+        pixels[0] = float(invRange*distance.p[0]+.5);
+        pixels[1] = float(invRange*distance.p[1]+.5);
+        pixels[2] = float(invRange*distance.p[2]+.5);
+        pixels[3] = float(invRange*distance.p[3]+.5);
+        pixels[4] = float(invRange*distance.p[4]+.5);
+        pixels[5] = float(invRange*distance.p[5]+.5);
+        pixels[6] = float(invRange*distance.p[6]+.5);
+        pixels[7] = float(invRange*distance.t+.5);
+    }
+};
+
 template <class ContourCombiner>
 void generateDistanceField(const typename DistancePixelConversion<typename ContourCombiner::DistanceType>::BitmapRefType &output, const Shape &shape, const Projection &projection, double range) {
     DistancePixelConversion<typename ContourCombiner::DistanceType> distancePixelConversion(range);
@@ -104,6 +122,13 @@ void generateMTSDF(const BitmapRef<float, 4> &output, const Shape &shape, const
     msdfErrorCorrection(output, shape, projection, range, config);
 }
 
+void generate7TSDF(const BitmapRef<float, 8> &output, const Shape &shape, const Projection &projection, double range, const MSDFGeneratorConfig &config) {
+    if (config.overlapSupport)
+        generateDistanceField<OverlappingContourCombiner<M7AndTrueDistanceSelector> >(output, shape, projection, range);
+    else
+        generateDistanceField<SimpleContourCombiner<M7AndTrueDistanceSelector> >(output, shape, projection, range);
+}
+
 // Legacy API
 
 void generateSDF(const BitmapRef<float, 1> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, bool overlapSupport) {

+ 29 - 0
main.cpp

@@ -516,6 +516,7 @@ int main(int argc, const char *const *argv) {
         PSEUDO,
         MULTI,
         MULTI_AND_TRUE,
+        MULTI7_AND_TRUE,
         METRICS
     } mode = MULTI;
     enum {
@@ -592,6 +593,7 @@ int main(int argc, const char *const *argv) {
         ARG_MODE("psdf", PSEUDO)
         ARG_MODE("msdf", MULTI)
         ARG_MODE("mtsdf", MULTI_AND_TRUE)
+        ARG_MODE("7tsdf", MULTI7_AND_TRUE)
         ARG_MODE("metrics", METRICS)
 
     #if defined(MSDFGEN_EXTENSIONS) && !defined(MSDFGEN_DISABLE_SVG)
@@ -1140,6 +1142,7 @@ int main(int argc, const char *const *argv) {
     Bitmap<float, 1> sdf;
     Bitmap<float, 3> msdf;
     Bitmap<float, 4> mtsdf;
+    Bitmap<float, 8> m7tsdf;
     MSDFGeneratorConfig postErrorCorrectionConfig(generatorConfig);
     if (scanlinePass) {
         if (explicitErrorCorrectionMode && generatorConfig.errorCorrection.distanceCheckMode != ErrorCorrectionConfig::DO_NOT_CHECK_DISTANCE) {
@@ -1196,6 +1199,27 @@ int main(int argc, const char *const *argv) {
                 generateMTSDF(mtsdf, shape, projection, range, generatorConfig);
             break;
         }
+        case MULTI7_AND_TRUE:
+            edgeColoring7Random(shape, angleThreshold, coloringSeed);
+            m7tsdf = Bitmap<float, 8>(width, height);
+            generate7TSDF(m7tsdf, shape, projection, range, generatorConfig);
+            mtsdf = Bitmap<float, 4>(width, 2*height);
+            for (float
+                *dstLow = mtsdf(0, 0),
+                *dstHigh = mtsdf(0, height),
+                *src = m7tsdf(0, 0),
+                *srcEnd = m7tsdf(0, height);
+                src < srcEnd; dstLow += 4, dstHigh += 4, src += 8
+            ) {
+                dstLow[0] = src[0];
+                dstLow[1] = src[1];
+                dstLow[2] = src[2];
+                dstLow[3] = src[3];
+                dstHigh[0] = src[4];
+                dstHigh[1] = src[5];
+                dstHigh[2] = src[6];
+                dstHigh[3] = src[7];
+            }
         default:;
     }
 
@@ -1343,6 +1367,11 @@ int main(int argc, const char *const *argv) {
                     fputs("Failed to write test render file.\n", stderr);
             }
             break;
+        case MULTI7_AND_TRUE:
+            if ((error = writeOutput<4>(mtsdf, output, format))) {
+                fprintf(stderr, "%s\n", error);
+                return 1;
+            }
         default:;
     }
 

+ 3 - 0
msdfgen.h

@@ -49,6 +49,9 @@ void generateMSDF(const BitmapRef<float, 3> &output, const Shape &shape, const P
 /// 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 Projection &projection, double range, const MSDFGeneratorConfig &config = MSDFGeneratorConfig());
 
+/// Generates a 7+1 channel signed distance field with true distance in the last channel. Edge colors must be assigned first.
+void generate7TSDF(const BitmapRef<float, 8> &output, const Shape &shape, const Projection &projection, double range, 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, double range, const Vector2 &scale, const Vector2 &translate, bool overlapSupport = true);
 void generatePseudoSDF(const BitmapRef<float, 1> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, bool overlapSupport = true);