godot/thirdparty/msdfgen/core/shape-description.cpp
bruvzg 4c3f7d1290 Makes FontData importable resource.
Adds multi-channel SDF font texture generation and rendering support.
Adds per-font oversampling support.
Adds FontData import plugins (for dynamic fonts, BMFonts and monospaced image fonts), font texture cache pre-generation and loading.
Adds BMFont binary format and outline support.
2021-08-27 15:43:18 +03:00

285 lines
9.6 KiB
C++

#define _CRT_SECURE_NO_WARNINGS
#include "shape-description.h"
namespace msdfgen {
int readCharF(FILE *input) {
int c = '\0';
do {
c = fgetc(input);
} while (c == ' ' || c == '\t' || c == '\r' || c == '\n');
return c;
}
int readCharS(const char **input) {
int c = '\0';
do {
c = *(*input)++;
} while (c == ' ' || c == '\t' || c == '\r' || c == '\n');
if (!c) {
--c;
return EOF;
}
return c;
}
int readCoordF(FILE *input, Point2 &coord) {
return fscanf(input, "%lf,%lf", &coord.x, &coord.y);
}
int readCoordS(const char **input, Point2 &coord) {
int read = 0;
int result = sscanf(*input, "%lf,%lf%n", &coord.x, &coord.y, &read);
*input += read;
return result;
}
static bool writeCoord(FILE *output, Point2 coord) {
fprintf(output, "%.12g, %.12g", coord.x, coord.y);
return true;
}
template <typename T, int (*readChar)(T *), int (*readCoord)(T *, Point2 &)>
static int readControlPoints(T *input, Point2 *output) {
int result = readCoord(input, output[0]);
if (result == 2) {
switch (readChar(input)) {
case ')':
return 1;
case ';':
break;
default:
return -1;
}
result = readCoord(input, output[1]);
if (result == 2 && readChar(input) == ')')
return 2;
} else if (result != 1 && readChar(input) == ')')
return 0;
return -1;
}
template <typename T, int (*readChar)(T *), int (*readCoord)(T *, Point2 &)>
static bool readContour(T *input, Contour &output, const Point2 *first, int terminator, bool &colorsSpecified) {
Point2 p[4], start;
if (first)
p[0] = *first;
else {
int result = readCoord(input, p[0]);
if (result != 2)
return result != 1 && readChar(input) == terminator;
}
start = p[0];
int c = '\0';
while ((c = readChar(input)) != terminator) {
if (c != ';')
return false;
EdgeColor color = WHITE;
int result = readCoord(input, p[1]);
if (result == 2) {
output.addEdge(EdgeHolder(p[0], p[1], color));
p[0] = p[1];
continue;
} else if (result == 1)
return false;
else {
int controlPoints = 0;
switch ((c = readChar(input))) {
case '#':
output.addEdge(EdgeHolder(p[0], start, color));
p[0] = start;
continue;
case ';':
goto FINISH_EDGE;
case '(':
goto READ_CONTROL_POINTS;
case 'C': case 'c':
color = CYAN;
colorsSpecified = true;
break;
case 'M': case 'm':
color = MAGENTA;
colorsSpecified = true;
break;
case 'Y': case 'y':
color = YELLOW;
colorsSpecified = true;
break;
case 'W': case 'w':
color = WHITE;
colorsSpecified = true;
break;
default:
return c == terminator;
}
switch (readChar(input)) {
case ';':
goto FINISH_EDGE;
case '(':
READ_CONTROL_POINTS:
if ((controlPoints = readControlPoints<T, readChar, readCoord>(input, p+1)) < 0)
return false;
break;
default:
return false;
}
if (readChar(input) != ';')
return false;
FINISH_EDGE:
result = readCoord(input, p[1+controlPoints]);
if (result != 2) {
if (result == 1)
return false;
else {
if (readChar(input) == '#')
p[1+controlPoints] = start;
else
return false;
}
}
switch (controlPoints) {
case 0:
output.addEdge(EdgeHolder(p[0], p[1], color));
p[0] = p[1];
continue;
case 1:
output.addEdge(EdgeHolder(p[0], p[1], p[2], color));
p[0] = p[2];
continue;
case 2:
output.addEdge(EdgeHolder(p[0], p[1], p[2], p[3], color));
p[0] = p[3];
continue;
}
}
}
return true;
}
bool readShapeDescription(FILE *input, Shape &output, bool *colorsSpecified) {
bool locColorsSpec = false;
output.contours.clear();
output.inverseYAxis = false;
Point2 p;
int result = readCoordF(input, p);
if (result == 2) {
return readContour<FILE, readCharF, readCoordF>(input, output.addContour(), &p, EOF, locColorsSpec);
} else if (result == 1)
return false;
else {
int c = readCharF(input);
if (c == '@') {
char after = '\0';
if (fscanf(input, "invert-y%c", &after) != 1)
return feof(input) != 0;
output.inverseYAxis = true;
c = after;
if (c == ' ' || c == '\t' || c == '\r' || c == '\n')
c = readCharF(input);
}
for (; c == '{'; c = readCharF(input))
if (!readContour<FILE, readCharF, readCoordF>(input, output.addContour(), NULL, '}', locColorsSpec))
return false;
if (colorsSpecified)
*colorsSpecified = locColorsSpec;
return c == EOF && feof(input);
}
}
bool readShapeDescription(const char *input, Shape &output, bool *colorsSpecified) {
bool locColorsSpec = false;
output.contours.clear();
output.inverseYAxis = false;
Point2 p;
int result = readCoordS(&input, p);
if (result == 2) {
return readContour<const char *, readCharS, readCoordS>(&input, output.addContour(), &p, EOF, locColorsSpec);
} else if (result == 1)
return false;
else {
int c = readCharS(&input);
if (c == '@') {
for (int i = 0; i < (int) sizeof("invert-y")-1; ++i)
if (input[i] != "invert-y"[i])
return false;
output.inverseYAxis = true;
input += sizeof("invert-y")-1;
c = readCharS(&input);
}
for (; c == '{'; c = readCharS(&input))
if (!readContour<const char *, readCharS, readCoordS>(&input, output.addContour(), NULL, '}', locColorsSpec))
return false;
if (colorsSpecified)
*colorsSpecified = locColorsSpec;
return c == EOF;
}
}
static bool isColored(const Shape &shape) {
for (std::vector<Contour>::const_iterator contour = shape.contours.begin(); contour != shape.contours.end(); ++contour)
for (std::vector<EdgeHolder>::const_iterator edge = contour->edges.begin(); edge != contour->edges.end(); ++edge)
if ((*edge)->color != WHITE)
return true;
return false;
}
bool writeShapeDescription(FILE *output, const Shape &shape) {
if (!shape.validate())
return false;
bool writeColors = isColored(shape);
if (shape.inverseYAxis)
fprintf(output, "@invert-y\n");
for (std::vector<Contour>::const_iterator contour = shape.contours.begin(); contour != shape.contours.end(); ++contour) {
fprintf(output, "{\n");
if (!contour->edges.empty()) {
for (std::vector<EdgeHolder>::const_iterator edge = contour->edges.begin(); edge != contour->edges.end(); ++edge) {
char colorCode = '\0';
if (writeColors) {
switch ((*edge)->color) {
case YELLOW: colorCode = 'y'; break;
case MAGENTA: colorCode = 'm'; break;
case CYAN: colorCode = 'c'; break;
case WHITE: colorCode = 'w'; break;
default:;
}
}
if (const LinearSegment *e = dynamic_cast<const LinearSegment *>(&**edge)) {
fprintf(output, "\t");
writeCoord(output, e->p[0]);
fprintf(output, ";\n");
if (colorCode)
fprintf(output, "\t\t%c;\n", colorCode);
}
if (const QuadraticSegment *e = dynamic_cast<const QuadraticSegment *>(&**edge)) {
fprintf(output, "\t");
writeCoord(output, e->p[0]);
fprintf(output, ";\n\t\t");
if (colorCode)
fprintf(output, "%c", colorCode);
fprintf(output, "(");
writeCoord(output, e->p[1]);
fprintf(output, ");\n");
}
if (const CubicSegment *e = dynamic_cast<const CubicSegment *>(&**edge)) {
fprintf(output, "\t");
writeCoord(output, e->p[0]);
fprintf(output, ";\n\t\t");
if (colorCode)
fprintf(output, "%c", colorCode);
fprintf(output, "(");
writeCoord(output, e->p[1]);
fprintf(output, "; ");
writeCoord(output, e->p[2]);
fprintf(output, ");\n");
}
}
fprintf(output, "\t#\n");
}
fprintf(output, "}\n");
}
return true;
}
}