mirror of https://github.com/Chlumsky/msdfgen.git
Ink trap edge coloring heuristic added
This commit is contained in:
parent
e93c8d988c
commit
e88da21071
|
|
@ -7,6 +7,17 @@ static bool isCorner(const Vector2 &aDir, const Vector2 &bDir, double crossThres
|
|||
return dotProduct(aDir, bDir) <= 0 || fabs(crossProduct(aDir, bDir)) > crossThreshold;
|
||||
}
|
||||
|
||||
static double estimateEdgeLength(const EdgeSegment *edge) {
|
||||
double len = 0;
|
||||
Point2 prev = edge->point(0);
|
||||
for (int i = 1; i <= MSDFGEN_EDGE_LENGTH_PRECISION; ++i) {
|
||||
Point2 cur = edge->point(1./MSDFGEN_EDGE_LENGTH_PRECISION*i);
|
||||
len += (cur-prev).length();
|
||||
prev = cur;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
static void switchColor(EdgeColor &color, unsigned long long &seed, EdgeColor banned = BLACK) {
|
||||
EdgeColor combined = EdgeColor(color&banned);
|
||||
if (combined == RED || combined == GREEN || combined == BLUE) {
|
||||
|
|
@ -94,4 +105,114 @@ void edgeColoringSimple(Shape &shape, double angleThreshold, unsigned long long
|
|||
}
|
||||
}
|
||||
|
||||
struct EdgeColoringInkTrapCorner {
|
||||
int index;
|
||||
double prevEdgeLengthEstimate;
|
||||
bool minor;
|
||||
EdgeColor color;
|
||||
};
|
||||
|
||||
void edgeColoringInkTrap(Shape &shape, double angleThreshold, unsigned long long seed) {
|
||||
typedef EdgeColoringInkTrapCorner Corner;
|
||||
double crossThreshold = sin(angleThreshold);
|
||||
std::vector<Corner> corners;
|
||||
for (std::vector<Contour>::iterator contour = shape.contours.begin(); contour != shape.contours.end(); ++contour) {
|
||||
// Identify corners
|
||||
double splineLength = 0;
|
||||
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)) {
|
||||
Corner corner = { index, splineLength };
|
||||
corners.push_back(corner);
|
||||
splineLength = 0;
|
||||
}
|
||||
splineLength += estimateEdgeLength(*edge);
|
||||
prevDirection = (*edge)->direction(1);
|
||||
}
|
||||
}
|
||||
|
||||
// Smooth contour
|
||||
if (corners.empty())
|
||||
for (std::vector<EdgeHolder>::iterator edge = contour->edges.begin(); edge != contour->edges.end(); ++edge)
|
||||
(*edge)->color = WHITE;
|
||||
// "Teardrop" case
|
||||
else if (corners.size() == 1) {
|
||||
EdgeColor colors[3] = { WHITE, WHITE };
|
||||
switchColor(colors[0], seed);
|
||||
switchColor(colors[2] = colors[0], seed);
|
||||
int corner = corners[0].index;
|
||||
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 majorCornerCount = cornerCount;
|
||||
if (cornerCount > 3) {
|
||||
corners.begin()->prevEdgeLengthEstimate += splineLength;
|
||||
for (int i = 0; i < cornerCount; ++i) {
|
||||
if (
|
||||
corners[i].prevEdgeLengthEstimate > corners[(i+1)%cornerCount].prevEdgeLengthEstimate &&
|
||||
corners[(i+1)%cornerCount].prevEdgeLengthEstimate < corners[(i+2)%cornerCount].prevEdgeLengthEstimate
|
||||
) {
|
||||
corners[i].minor = true;
|
||||
--majorCornerCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
EdgeColor color = WHITE;
|
||||
EdgeColor initialColor = BLACK;
|
||||
for (int i = 0; i < cornerCount; ++i) {
|
||||
if (!corners[i].minor) {
|
||||
--majorCornerCount;
|
||||
switchColor(color, seed, EdgeColor(!majorCornerCount*initialColor));
|
||||
corners[i].color = color;
|
||||
if (!initialColor)
|
||||
initialColor = color;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < cornerCount; ++i) {
|
||||
if (corners[i].minor) {
|
||||
EdgeColor nextColor = corners[(i+1)%cornerCount].color;
|
||||
corners[i].color = EdgeColor((color&nextColor)^WHITE);
|
||||
} else
|
||||
color = corners[i].color;
|
||||
}
|
||||
int spline = 0;
|
||||
int start = corners[0].index;
|
||||
color = corners[0].color;
|
||||
int m = (int) contour->edges.size();
|
||||
for (int i = 0; i < m; ++i) {
|
||||
int index = (start+i)%m;
|
||||
if (spline+1 < cornerCount && corners[spline+1].index == index)
|
||||
color = corners[++spline].color;
|
||||
contour->edges[index]->color = color;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@
|
|||
|
||||
#include "Shape.h"
|
||||
|
||||
#define MSDFGEN_EDGE_LENGTH_PRECISION 4
|
||||
|
||||
namespace msdfgen {
|
||||
|
||||
/** Assigns colors to edges of the shape in accordance to the multi-channel distance field technique.
|
||||
|
|
@ -12,4 +14,10 @@ namespace msdfgen {
|
|||
*/
|
||||
void edgeColoringSimple(Shape &shape, double angleThreshold, unsigned long long seed = 0);
|
||||
|
||||
/** The alternative "ink trap" coloring strategy is designed for better results with typefaces
|
||||
* that use ink traps as a design feature. It guarantees that even if all edges that are shorter than
|
||||
* both their neighboring edges are removed, the coloring remains consistent with the established rules.
|
||||
*/
|
||||
void edgeColoringInkTrap(Shape &shape, double angleThreshold, unsigned long long seed = 0);
|
||||
|
||||
}
|
||||
|
|
|
|||
23
main.cpp
23
main.cpp
|
|
@ -290,6 +290,8 @@ static const char *helpText =
|
|||
"\tSets the scale used to convert shape units to pixels asymmetrically.\n"
|
||||
" -autoframe\n"
|
||||
"\tAutomatically scales (unless specified) and translates the shape to fit.\n"
|
||||
" -coloringstrategy <simple / inktrap>\n"
|
||||
"\tSelects the strategy of the edge coloring heuristic.\n"
|
||||
" -edgecolors <sequence>\n"
|
||||
"\tOverrides automatic edge coloring with the specified color sequence.\n"
|
||||
" -errorcorrection <threshold>\n"
|
||||
|
|
@ -400,6 +402,7 @@ int main(int argc, const char * const *argv) {
|
|||
GUESS
|
||||
} orientation = KEEP;
|
||||
unsigned long long coloringSeed = 0;
|
||||
void (*edgeColoring)(Shape &, double, unsigned long long) = edgeColoringSimple;
|
||||
|
||||
int argPos = 1;
|
||||
bool suggestHelp = false;
|
||||
|
|
@ -575,14 +578,22 @@ int main(int argc, const char * const *argv) {
|
|||
argPos += 2;
|
||||
continue;
|
||||
}
|
||||
ARG_CASE("-coloringstrategy", 1) {
|
||||
if (!strcmp(argv[argPos+1], "simple")) edgeColoring = edgeColoringSimple;
|
||||
else if (!strcmp(argv[argPos+1], "inktrap")) edgeColoring = edgeColoringInkTrap;
|
||||
else
|
||||
puts("Unknown coloring strategy specified.");
|
||||
argPos += 2;
|
||||
continue;
|
||||
}
|
||||
ARG_CASE("-edgecolors", 1) {
|
||||
static const char *allowed = " ?,cmyCMY";
|
||||
static const char *allowed = " ?,cmwyCMWY";
|
||||
for (int i = 0; argv[argPos+1][i]; ++i) {
|
||||
for (int j = 0; allowed[j]; ++j)
|
||||
if (argv[argPos+1][i] == allowed[j])
|
||||
goto ROLL_ARG;
|
||||
ABORT("Invalid edge coloring sequence. Use -assign <color sequence> with only the colors C, M, and Y. Separate contours by commas and use ? to keep the default assigment for a contour.");
|
||||
ROLL_ARG:;
|
||||
goto EDGE_COLOR_VERIFIED;
|
||||
ABORT("Invalid edge coloring sequence. Use -edgecolors <color sequence> with only the colors C, M, Y, and W. Separate contours by commas and use ? to keep the default assigment for a contour.");
|
||||
EDGE_COLOR_VERIFIED:;
|
||||
}
|
||||
edgeAssignment = argv[argPos+1];
|
||||
argPos += 2;
|
||||
|
|
@ -807,7 +818,7 @@ int main(int argc, const char * const *argv) {
|
|||
}
|
||||
case MULTI: {
|
||||
if (!skipColoring)
|
||||
edgeColoringSimple(shape, angleThreshold, coloringSeed);
|
||||
edgeColoring(shape, angleThreshold, coloringSeed);
|
||||
if (edgeAssignment)
|
||||
parseColoring(shape, edgeAssignment);
|
||||
msdf = Bitmap<float, 3>(width, height);
|
||||
|
|
@ -819,7 +830,7 @@ int main(int argc, const char * const *argv) {
|
|||
}
|
||||
case MULTI_AND_TRUE: {
|
||||
if (!skipColoring)
|
||||
edgeColoringSimple(shape, angleThreshold, coloringSeed);
|
||||
edgeColoring(shape, angleThreshold, coloringSeed);
|
||||
if (edgeAssignment)
|
||||
parseColoring(shape, edgeAssignment);
|
||||
mtsdf = Bitmap<float, 4>(width, height);
|
||||
|
|
|
|||
Loading…
Reference in New Issue