diff --git a/README.md b/README.md index 7019c90..f302b6b 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,11 @@ The following comparison demonstrates the improvement in image quality. ![demo-sdf16](https://cloud.githubusercontent.com/assets/18639794/14770360/20c51156-0a70-11e6-8f03-ed7632d07997.png) ![demo-sdf32](https://cloud.githubusercontent.com/assets/18639794/14770361/251a4406-0a70-11e6-95a7-e30e235ac729.png) +## New in version 1.2 + - Option to specify that shape is defined in reverse order (-reverseorder) + - Option to set a seed for the edge coloring heuristic (-seed \), which can be used to adjust the output + - Fixed parsing of glyph contours starting that start with a curve control point. + ## Getting started The project can be used either as a library or as a console program. is divided into two parts, **[core](core)** diff --git a/core/edge-coloring.cpp b/core/edge-coloring.cpp index 2978d89..334671b 100644 --- a/core/edge-coloring.cpp +++ b/core/edge-coloring.cpp @@ -7,7 +7,24 @@ static bool isCorner(const Vector2 &aDir, const Vector2 &bDir, double crossThres return dotProduct(aDir, bDir) <= 0 || fabs(crossProduct(aDir, bDir)) > crossThreshold; } -void edgeColoringSimple(Shape &shape, double angleThreshold) { +static void switchColor(EdgeColor &color, unsigned long long &seed, EdgeColor banned = BLACK) { + EdgeColor combined = EdgeColor(color&banned); + if (combined == RED || combined == GREEN || combined == BLUE) { + color = EdgeColor(combined^WHITE); + return; + } + if (color == BLACK || color == WHITE) { + static const EdgeColor start[3] = { CYAN, MAGENTA, YELLOW }; + color = start[seed%3]; + seed /= 3; + return; + } + int shifted = color<<(1+(seed&1)); + color = EdgeColor((shifted|shifted>>3)&WHITE); + seed >>= 1; +} + +void edgeColoringSimple(Shape &shape, double angleThreshold, unsigned long long seed) { double crossThreshold = sin(angleThreshold); std::vector corners; for (std::vector::iterator contour = shape.contours.begin(); contour != shape.contours.end(); ++contour) { @@ -29,7 +46,9 @@ void edgeColoringSimple(Shape &shape, double angleThreshold) { (*edge)->color = WHITE; // "Teardrop" case else if (corners.size() == 1) { - const EdgeColor colors[] = { MAGENTA, WHITE, YELLOW }; + EdgeColor colors[3] = { WHITE, WHITE }; + switchColor(colors[0], seed); + switchColor(colors[2] = colors[0], seed); int corner = corners[0]; if (contour->edges.size() >= 3) { int m = contour->edges.size(); @@ -57,16 +76,19 @@ void edgeColoringSimple(Shape &shape, double angleThreshold) { // Multiple corners else { int cornerCount = corners.size(); - // CMYCMYCMYCMY / YMYCMYC if corner count % 3 == 1 - EdgeColor colors[] = { cornerCount%3 == 1 ? YELLOW : CYAN, CYAN, MAGENTA, YELLOW }; int spline = 0; int start = corners[0]; int m = contour->edges.size(); + EdgeColor color = WHITE; + switchColor(color, seed); + EdgeColor initialColor = color; for (int i = 0; i < m; ++i) { int index = (start+i)%m; - if (cornerCount > spline+1 && corners[spline+1] == index) + if (spline+1 < cornerCount && corners[spline+1] == index) { ++spline; - contour->edges[index]->color = (colors+1)[spline%3-!spline]; + switchColor(color, seed, EdgeColor((spline == cornerCount-1)*initialColor)); + } + contour->edges[index]->color = color; } } } diff --git a/core/edge-coloring.h b/core/edge-coloring.h index 4193015..cc1bd52 100644 --- a/core/edge-coloring.h +++ b/core/edge-coloring.h @@ -10,6 +10,6 @@ namespace msdfgen { * angleThreshold specifies the maximum angle (in radians) to be considered a corner, for example 3 (~172 degrees). * Values below 1/2 PI will be treated as the external angle. */ -void edgeColoringSimple(Shape &shape, double angleThreshold); +void edgeColoringSimple(Shape &shape, double angleThreshold, unsigned long long seed = 0); } diff --git a/ext/import-font.cpp b/ext/import-font.cpp index dfdff1a..a7c3a1d 100644 --- a/ext/import-font.cpp +++ b/ext/import-font.cpp @@ -109,6 +109,7 @@ bool loadGlyph(Shape &output, FontHandle *font, int unicode, double *advance) { Contour &contour = output.addContour(); int first = last+1; + int firstPathPoint = -1; last = font->face->glyph->outline.contours[i]; PointType state = NONE; @@ -117,20 +118,24 @@ bool loadGlyph(Shape &output, FontHandle *font, int unicode, double *advance) { // For each point on the contour for (int round = 0, index = first; round == 0; ++index) { - // Close contour if (index > last) { + REQUIRE(firstPathPoint >= 0); index = first; - round++; } + // Close contour + if (index == firstPathPoint) + ++round; Point2 point(font->face->glyph->outline.points[index].x/64., font->face->glyph->outline.points[index].y/64.); PointType pointType = font->face->glyph->outline.tags[index]&1 ? PATH_POINT : font->face->glyph->outline.tags[index]&2 ? CUBIC_POINT : QUADRATIC_POINT; switch (state) { case NONE: - REQUIRE(pointType == PATH_POINT); - startPoint = point; - state = PATH_POINT; + if (pointType == PATH_POINT) { + firstPathPoint = index; + startPoint = point; + state = PATH_POINT; + } break; case PATH_POINT: if (pointType == PATH_POINT) { diff --git a/main.cpp b/main.cpp index 544e1ab..e0b4d37 100644 --- a/main.cpp +++ b/main.cpp @@ -1,6 +1,6 @@ /* - * MULTI-CHANNEL SIGNED DISTANCE FIELD GENERATOR v1.1 (2016-05-08) - standalone console program + * MULTI-CHANNEL SIGNED DISTANCE FIELD GENERATOR v1.2 (2016-07-20) - standalone console program * -------------------------------------------------------------------------------------------- * A utility by Viktor Chlumsky, (c) 2014 - 2016 * @@ -44,6 +44,11 @@ static bool parseUnsigned(unsigned &value, const char *arg) { return sscanf(arg, "%u%c", &value, &c) == 1; } +static bool parseUnsignedLL(unsigned long long &value, const char *arg) { + static char c; + return sscanf(arg, "%llu%c", &value, &c) == 1; +} + static bool parseUnsignedHex(unsigned &value, const char *arg) { static char c; return sscanf(arg, "%x%c", &value, &c) == 1; @@ -126,6 +131,21 @@ static void parseColoring(Shape &shape, const char *edgeAssignment) { } } +static void invertColor(Bitmap &bitmap) { + for (int y = 0; y < bitmap.height(); ++y) + for (int x = 0; x < bitmap.width(); ++x) { + bitmap(x, y).r = .5f-bitmap(x, y).r; + bitmap(x, y).g = .5f-bitmap(x, y).g; + bitmap(x, y).b = .5f-bitmap(x, y).b; + } +} + +static void invertColor(Bitmap &bitmap) { + for (int y = 0; y < bitmap.height(); ++y) + for (int x = 0; x < bitmap.width(); ++x) + bitmap(x, y) = .5f-bitmap(x, y); +} + static bool writeTextBitmap(FILE *file, const float *values, int cols, int rows) { for (int row = 0; row < rows; ++row) { for (int col = 0; col < cols; ++col) { @@ -222,7 +242,7 @@ static const char * writeOutput(const Bitmap &bitmap, const char *filename, F return NULL; } default: - break; + break; } } else { if (format == AUTO || format == TEXT) @@ -302,6 +322,10 @@ static const char *helpText = "\tSets the translation of the shape in shape units.\n" " -yflip\n" "\tInverts the Y axis in the output distance field. The default order is bottom to top.\n" + " -reverseorder\n" + "\tReverses the order of the points in each contour.\n" + " -seed \n" + "\tSets the random seed for edge coloring heuristic.\n" "\n"; int main(int argc, const char * const *argv) { @@ -351,6 +375,8 @@ int main(int argc, const char * const *argv) { bool yFlip = false; bool printMetrics = false; bool skipColoring = false; + bool reverseOrder = false; + unsigned long long coloringSeed = 0; int argPos = 1; bool suggestHelp = false; @@ -540,6 +566,17 @@ int main(int argc, const char * const *argv) { argPos += 1; continue; } + ARG_CASE("-reverseorder", 0) { + reverseOrder = true; + argPos += 1; + continue; + } + ARG_CASE("-seed", 1) { + if (!parseUnsignedLL(coloringSeed, argv[argPos+1])) + ABORT("Invalid seed. Use -seed with N being a non-negative integer."); + argPos += 2; + continue; + } ARG_CASE("-help", 0) ABORT(helpText); printf("Unknown setting or insufficient parameters: %s\n", arg); @@ -600,7 +637,7 @@ int main(int argc, const char * const *argv) { break; } default: - break; + break; } // Validate and normalize shape @@ -692,7 +729,7 @@ int main(int argc, const char * const *argv) { } case MULTI: { if (!skipColoring) - edgeColoringSimple(shape, angleThreshold); + edgeColoringSimple(shape, angleThreshold, coloringSeed); if (edgeAssignment) parseColoring(shape, edgeAssignment); msdf = Bitmap(width, height); @@ -700,7 +737,12 @@ int main(int argc, const char * const *argv) { break; } default: - break; + break; + } + + if (reverseOrder) { + invertColor(sdf); + invertColor(msdf); } // Save output @@ -753,7 +795,7 @@ int main(int argc, const char * const *argv) { ABORT("Failed to write test render file."); } break; - default: + default: break; } diff --git a/msdfgen.exe b/msdfgen.exe index 50b1550..84b6c51 100644 Binary files a/msdfgen.exe and b/msdfgen.exe differ diff --git a/msdfgen.h b/msdfgen.h index ea6b30d..df69ce2 100644 --- a/msdfgen.h +++ b/msdfgen.h @@ -2,7 +2,7 @@ #pragma once /* - * MULTI-CHANNEL SIGNED DISTANCE FIELD GENERATOR v1.1 (2016-05-08) + * MULTI-CHANNEL SIGNED DISTANCE FIELD GENERATOR v1.2 (2016-07-20) * --------------------------------------------------------------- * A utility by Viktor Chlumsky, (c) 2014 - 2016 * @@ -24,7 +24,7 @@ #include "core/save-bmp.h" #include "core/shape-description.h" -#define MSDFGEN_VERSION "1.1" +#define MSDFGEN_VERSION "1.2" namespace msdfgen {