From 7f6a8aebbaa489d89735b5c98a0f6f10b508a9ac Mon Sep 17 00:00:00 2001 From: Chlumsky Date: Sat, 24 Feb 2024 23:17:58 +0100 Subject: [PATCH] Experimental version of 7TSDF mode --- core/contour-combiners.cpp | 22 ++++++ core/edge-coloring.cpp | 136 +++++++++++++++++++++++++++++++++++++ core/edge-coloring.h | 2 + core/edge-selectors.cpp | 135 ++++++++++++++++++++++++++++++++++++ core/edge-selectors.h | 21 ++++++ core/msdfgen.cpp | 25 +++++++ main.cpp | 29 ++++++++ msdfgen.h | 3 + 8 files changed, 373 insertions(+) diff --git a/core/contour-combiners.cpp b/core/contour-combiners.cpp index bac5534..b2f636a 100644 --- a/core/contour-combiners.cpp +++ b/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(a) < *reinterpret_cast(b); +} + +static double resolveDistance(M7AndTrueDistanceSelector::DistanceType distance) { + qsort(distance.p, 7, sizeof(*distance.p), &cmpDbl); + return distance.p[3]; +} + template SimpleContourCombiner::SimpleContourCombiner(const Shape &shape) { } @@ -46,6 +66,7 @@ template class SimpleContourCombiner; template class SimpleContourCombiner; template class SimpleContourCombiner; template class SimpleContourCombiner; +template class SimpleContourCombiner; template OverlappingContourCombiner::OverlappingContourCombiner(const Shape &shape) { @@ -130,5 +151,6 @@ template class OverlappingContourCombiner; template class OverlappingContourCombiner; template class OverlappingContourCombiner; template class OverlappingContourCombiner; +template class OverlappingContourCombiner; } diff --git a/core/edge-coloring.cpp b/core/edge-coloring.cpp index da9b037..a7b7320 100644 --- a/core/edge-coloring.cpp +++ b/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 corners; + for (std::vector::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::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::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; + } + } + } +} + } diff --git a/core/edge-coloring.h b/core/edge-coloring.h index ffd5e6d..98cf0fb 100644 --- a/core/edge-coloring.h +++ b/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); + } diff --git a/core/edge-selectors.cpp b/core/edge-selectors.cpp index aee7884..8102c2c 100644 --- a/core/edge-selectors.cpp +++ b/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; +} + } diff --git a/core/edge-selectors.h b/core/edge-selectors.h index 6b8e2d9..fe37d09 100644 --- a/core/edge-selectors.h +++ b/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]; + +}; + } diff --git a/core/msdfgen.cpp b/core/msdfgen.cpp index 0289295..0acd908 100644 --- a/core/msdfgen.cpp +++ b/core/msdfgen.cpp @@ -49,6 +49,24 @@ public: } }; +template <> +class DistancePixelConversion { + double invRange; +public: + typedef BitmapRef 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 void generateDistanceField(const typename DistancePixelConversion::BitmapRefType &output, const Shape &shape, const Projection &projection, double range) { DistancePixelConversion distancePixelConversion(range); @@ -104,6 +122,13 @@ void generateMTSDF(const BitmapRef &output, const Shape &shape, const msdfErrorCorrection(output, shape, projection, range, config); } +void generate7TSDF(const BitmapRef &output, const Shape &shape, const Projection &projection, double range, const MSDFGeneratorConfig &config) { + if (config.overlapSupport) + generateDistanceField >(output, shape, projection, range); + else + generateDistanceField >(output, shape, projection, range); +} + // Legacy API void generateSDF(const BitmapRef &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, bool overlapSupport) { diff --git a/main.cpp b/main.cpp index 5253a44..bebd5a6 100644 --- a/main.cpp +++ b/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 sdf; Bitmap msdf; Bitmap mtsdf; + Bitmap 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(width, height); + generate7TSDF(m7tsdf, shape, projection, range, generatorConfig); + mtsdf = Bitmap(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:; } diff --git a/msdfgen.h b/msdfgen.h index dff8beb..5c04a2c 100644 --- a/msdfgen.h +++ b/msdfgen.h @@ -49,6 +49,9 @@ void generateMSDF(const BitmapRef &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 &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 &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 &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, bool overlapSupport = true); void generatePseudoSDF(const BitmapRef &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, bool overlapSupport = true);