Version 1.2 - Reverse order option, edge coloring seed, glyph loading fix

This commit is contained in:
Viktor Chlumský 2016-07-21 18:26:15 +02:00
parent fdc26858d0
commit 4e443d8a72
7 changed files with 94 additions and 20 deletions

View File

@ -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 \<n\>), 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)**

View File

@ -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<int> corners;
for (std::vector<Contour>::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;
}
}
}

View File

@ -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);
}

View File

@ -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) {

View File

@ -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<FloatRGB> &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<float> &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<T> &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>\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 <N> 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<FloatRGB>(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;
}

Binary file not shown.

View File

@ -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 {