mirror of https://github.com/Chlumsky/msdfgen.git
Version 1.2 - Reverse order option, edge coloring seed, glyph loading fix
This commit is contained in:
parent
fdc26858d0
commit
4e443d8a72
|
|
@ -14,6 +14,11 @@ The following comparison demonstrates the improvement in image quality.
|
|||

|
||||

|
||||
|
||||
## 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)**
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
54
main.cpp
54
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<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;
|
||||
}
|
||||
|
||||
|
|
|
|||
BIN
msdfgen.exe
BIN
msdfgen.exe
Binary file not shown.
|
|
@ -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 {
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue