From f9629b44f84fdc024526f27a4f794cd7b27377e8 Mon Sep 17 00:00:00 2001 From: themancalledjakob Date: Sat, 27 May 2023 16:06:00 +0200 Subject: [PATCH] don't flip contour for inside shapes --- src/gpufont/font.hpp | 92 ++++++++++++++++++++++++++++++++------------ 1 file changed, 67 insertions(+), 25 deletions(-) diff --git a/src/gpufont/font.hpp b/src/gpufont/font.hpp index fa2bb70..71f60d5 100644 --- a/src/gpufont/font.hpp +++ b/src/gpufont/font.hpp @@ -255,6 +255,13 @@ class Font { 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. struct FontVariationAxisParameters { @@ -879,11 +886,60 @@ class Font { BufferGlyph bufferGlyph; bufferGlyph.start = static_cast (bufferCurves.size()); - 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. - convertContour(bufferCurves, &face->glyph->outline, start, face->glyph->outline.contours[i], emSize); - start = face->glyph->outline.contours[i] + 1; + bool autoFixOutline = true; + if(autoFixOutline){ + short start = 0; + int n_contours = face->glyph->outline.n_contours; + std::vector directions(n_contours, 0); + std::vector > curves(n_contours); + std::vector 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 (bufferCurves.size()) - bufferGlyph.start; @@ -906,10 +962,7 @@ class Font { // This function takes a single contour (defined by firstIndex and // lastIndex, both inclusive) from outline and converts it into individual // quadratic bezier curves, which are added to the curves vector. - void convertContour(std::vector & all_curves, 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 curves; + void convertContour(std::vector & curves, float & direction, BBox & bb, const FT_Outline * outline, short firstIndex, short lastIndex, float emSize){ // See https://freetype.org/freetype2/docs/glyphs/glyphs-6.html // for a detailed description of the outline format. @@ -1000,14 +1053,11 @@ class Font { return (p1.x - p0.x) * (p1.y + p0.y); }; - // keep track of collected direction - float direction = 0; - // We will inject getDirection in makeCurve, // so anytime the curve is made it updates the direction. // This is less error prone than calling it separately. // 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; result.x0 = p0.x; result.y0 = p0.y; @@ -1016,6 +1066,10 @@ class Font { result.x2 = p2.x; result.y2 = p2.y; 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; }; @@ -1105,18 +1159,6 @@ class Font { }else{ 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()); }