don't flip contour for inside shapes

This commit is contained in:
jrkb 2023-05-27 16:06:00 +02:00
parent a3fd4a6e9b
commit f9629b44f8

View file

@ -255,6 +255,13 @@ class Font {
p3 = mat * p3; p3 = mat * p3;
} }
}; };
// and here a mini variant
struct BBox {
float xMin;
float xMax;
float yMin;
float yMax;
};
/// A structure to model a given axis of a variable font. /// A structure to model a given axis of a variable font.
struct FontVariationAxisParameters { struct FontVariationAxisParameters {
@ -879,11 +886,60 @@ class Font {
BufferGlyph bufferGlyph; BufferGlyph bufferGlyph;
bufferGlyph.start = static_cast <int32_t>(bufferCurves.size()); bufferGlyph.start = static_cast <int32_t>(bufferCurves.size());
short start = 0; bool autoFixOutline = true;
for(int i = 0; i < face->glyph->outline.n_contours; i++){ if(autoFixOutline){
// Note: The end indices in face->glyph->outline.contours are inclusive. short start = 0;
convertContour(bufferCurves, &face->glyph->outline, start, face->glyph->outline.contours[i], emSize); int n_contours = face->glyph->outline.n_contours;
start = face->glyph->outline.contours[i] + 1; std::vector <float> directions(n_contours, 0);
std::vector <std::vector <BufferCurve> > curves(n_contours);
std::vector <BBox> bbs(n_contours);
for(int i = 0; i < n_contours; i++){
// Note: The end indices in face->glyph->outline.contours are inclusive.
bbs[i].xMin = FLT_MAX;
bbs[i].xMax = -FLT_MAX;
bbs[i].yMin = FLT_MAX;
bbs[i].yMax = -FLT_MAX;
convertContour(curves[i], directions[i], bbs[i], &face->glyph->outline, start, face->glyph->outline.contours[i], emSize);
start = face->glyph->outline.contours[i] + 1;
}
for(int i = 0; i < n_contours; i++){
if(directions[i] < 0){
bool flip = true;
for(int j = 0; j < n_contours; j++){
if(i != j){
bool inside = bbs[j].xMin <= bbs[i].xMin
&& bbs[j].xMax >= bbs[i].xMax
&& bbs[j].yMin <= bbs[i].yMin
&& bbs[j].yMax >= bbs[i].yMax;
if(inside){
flip = false;
break;
}
}
}
if(flip){
for(BufferCurve & curve : curves[i]){
float tmp_x = curve.x0;
float tmp_y = curve.y0;
curve.x0 = curve.x2;
curve.y0 = curve.y2;
curve.x2 = tmp_x;
curve.y2 = tmp_y;
}
std::reverse(curves[i].begin(), curves[i].end());
}
}
bufferCurves.insert(bufferCurves.end(), curves[i].begin(), curves[i].end());
}
}else{
short start = 0;
for(int i = 0; i < face->glyph->outline.n_contours; i++){
// Note: The end indices in face->glyph->outline.contours are inclusive.
float direction = 0; // ignored
BBox bb; // ignored
convertContour(bufferCurves, direction, bb, &face->glyph->outline, start, face->glyph->outline.contours[i], emSize);
start = face->glyph->outline.contours[i] + 1;
}
} }
bufferGlyph.count = static_cast <int32_t>(bufferCurves.size()) - bufferGlyph.start; bufferGlyph.count = static_cast <int32_t>(bufferCurves.size()) - bufferGlyph.start;
@ -906,10 +962,7 @@ class Font {
// This function takes a single contour (defined by firstIndex and // This function takes a single contour (defined by firstIndex and
// lastIndex, both inclusive) from outline and converts it into individual // lastIndex, both inclusive) from outline and converts it into individual
// quadratic bezier curves, which are added to the curves vector. // quadratic bezier curves, which are added to the curves vector.
void convertContour(std::vector <BufferCurve> & all_curves, const FT_Outline * outline, short firstIndex, short lastIndex, float emSize){ void convertContour(std::vector <BufferCurve> & curves, float & direction, BBox & bb, const FT_Outline * outline, short firstIndex, short lastIndex, float emSize){
// we might have to flip the direction
// so let's buffer our buffercurves in a buffer curves of buffercurves
std::vector <BufferCurve> curves;
// See https://freetype.org/freetype2/docs/glyphs/glyphs-6.html // See https://freetype.org/freetype2/docs/glyphs/glyphs-6.html
// for a detailed description of the outline format. // for a detailed description of the outline format.
@ -1000,14 +1053,11 @@ class Font {
return (p1.x - p0.x) * (p1.y + p0.y); return (p1.x - p0.x) * (p1.y + p0.y);
}; };
// keep track of collected direction
float direction = 0;
// We will inject getDirection in makeCurve, // We will inject getDirection in makeCurve,
// so anytime the curve is made it updates the direction. // so anytime the curve is made it updates the direction.
// This is less error prone than calling it separately. // This is less error prone than calling it separately.
// wisdom: Any error you cannot make, you will not make. :) // wisdom: Any error you cannot make, you will not make. :)
auto makeCurve = [&direction, getDirection](const glm::vec2 & p0, const glm::vec2 & p1, const glm::vec2 & p2){ auto makeCurve = [&direction, getDirection, &bb](const glm::vec2 & p0, const glm::vec2 & p1, const glm::vec2 & p2){
BufferCurve result; BufferCurve result;
result.x0 = p0.x; result.x0 = p0.x;
result.y0 = p0.y; result.y0 = p0.y;
@ -1016,6 +1066,10 @@ class Font {
result.x2 = p2.x; result.x2 = p2.x;
result.y2 = p2.y; result.y2 = p2.y;
direction += getDirection(p0, p2); direction += getDirection(p0, p2);
bb.xMin = min(bb.xMin, min(p0.x, min(p1.x, p2.x)));
bb.xMax = max(bb.xMax, max(p0.x, max(p1.x, p2.x)));
bb.yMin = min(bb.yMin, min(p0.y, min(p1.y, p2.y)));
bb.yMax = max(bb.yMax, max(p0.y, max(p1.y, p2.y)));
return result; return result;
}; };
@ -1105,18 +1159,6 @@ class Font {
}else{ }else{
curves.push_back(makeCurve(start, previous, first)); curves.push_back(makeCurve(start, previous, first));
} }
if(direction < 0){
for(BufferCurve & curve : curves){
float tmp_x = curve.x0;
float tmp_y = curve.y0;
curve.x0 = curve.x2;
curve.y0 = curve.y2;
curve.x2 = tmp_x;
curve.y2 = tmp_y;
}
std::reverse(curves.begin(), curves.end());
}
all_curves.insert(all_curves.end(), curves.begin(), curves.end());
} }