|
| 1 | +// Debug tool to Visualize Freetype Glyphs by turning them into Polyline objects |
| 2 | + |
| 3 | +class FreetypeHatchBuilder |
| 4 | +{ |
| 5 | +public: |
| 6 | + // Start a new line from here |
| 7 | + void moveTo(const float64_t2 to) |
| 8 | + { |
| 9 | + lastPosition = to; |
| 10 | + } |
| 11 | + |
| 12 | + // Continue the last line started with moveTo (could also use the last |
| 13 | + // position from a lineTo) |
| 14 | + void lineTo(const float64_t2 to) |
| 15 | + { |
| 16 | + if (to != lastPosition) { |
| 17 | + std::vector<float64_t2> linePoints; |
| 18 | + linePoints.push_back(lastPosition); |
| 19 | + linePoints.push_back(to); |
| 20 | + currentPolyline.addLinePoints(linePoints); |
| 21 | + |
| 22 | + lastPosition = to; |
| 23 | + } |
| 24 | + } |
| 25 | + |
| 26 | + // Continue the last moveTo or lineTo with a quadratic bezier: |
| 27 | + // [last position, control, end] |
| 28 | + void quadratic(const float64_t2 control, const float64_t2 to) |
| 29 | + { |
| 30 | + shapes::QuadraticBezier<double> bezier = shapes::QuadraticBezier<double>::construct( |
| 31 | + lastPosition, |
| 32 | + control, |
| 33 | + to |
| 34 | + ); |
| 35 | + currentPolyline.addQuadBeziers({ &bezier, 1 }); |
| 36 | + lastPosition = to; |
| 37 | + } |
| 38 | + |
| 39 | + // Continue the last moveTo or lineTo with a cubic bezier: |
| 40 | + // [last position, control1, control2, end] |
| 41 | + void cubic(const float64_t2 control1, const float64_t2 control2, const float64_t2 to) |
| 42 | + { |
| 43 | + std::vector<shapes::QuadraticBezier<double>> quadBeziers; |
| 44 | + curves::CubicCurve myCurve( |
| 45 | + float64_t4(lastPosition.x, lastPosition.y, control1.x, control1.y), |
| 46 | + float64_t4(control2.x, control2.y, to.x, to.y) |
| 47 | + ); |
| 48 | + |
| 49 | + curves::Subdivision::AddBezierFunc addToBezier = [&](shapes::QuadraticBezier<double>&& info) -> void |
| 50 | + { |
| 51 | + quadBeziers.push_back(info); |
| 52 | + }; |
| 53 | + |
| 54 | + curves::Subdivision::adaptive(myCurve, 0.0, 1.0, 1e-5, addToBezier, 10u); |
| 55 | + currentPolyline.addQuadBeziers(quadBeziers); |
| 56 | + |
| 57 | + lastPosition = to; |
| 58 | + } |
| 59 | + |
| 60 | + void finish() |
| 61 | + { |
| 62 | + if (currentPolyline.getSectionsCount() > 0) |
| 63 | + polylines.push_back(currentPolyline); |
| 64 | + } |
| 65 | + |
| 66 | + std::vector<CPolyline> polylines; |
| 67 | + CPolyline currentPolyline = {}; |
| 68 | + // Set with move to and line to |
| 69 | + float64_t2 lastPosition = float64_t2(0.0); |
| 70 | +}; |
| 71 | + |
| 72 | +// TODO: Figure out what this is supposed to do |
| 73 | +static double f26dot6ToDouble(float x) |
| 74 | +{ |
| 75 | + return (1 / 64. * double(x)); |
| 76 | +} |
| 77 | + |
| 78 | +static float64_t2 ftPoint2(const FT_Vector& vector) { |
| 79 | + return float64_t2(f26dot6ToDouble(vector.x), f26dot6ToDouble(vector.y)); |
| 80 | +} |
| 81 | + |
| 82 | +static int ftMoveTo(const FT_Vector* to, void* user) { |
| 83 | + FreetypeHatchBuilder* context = reinterpret_cast<FreetypeHatchBuilder*>(user); |
| 84 | + context->moveTo(ftPoint2(*to)); |
| 85 | + return 0; |
| 86 | +} |
| 87 | +static int ftLineTo(const FT_Vector* to, void* user) { |
| 88 | + FreetypeHatchBuilder* context = reinterpret_cast<FreetypeHatchBuilder*>(user); |
| 89 | + context->lineTo(ftPoint2(*to)); |
| 90 | + return 0; |
| 91 | +} |
| 92 | + |
| 93 | +static int ftConicTo(const FT_Vector* control, const FT_Vector* to, void* user) { |
| 94 | + FreetypeHatchBuilder* context = reinterpret_cast<FreetypeHatchBuilder*>(user); |
| 95 | + context->quadratic(ftPoint2(*control), ftPoint2(*to)); |
| 96 | + return 0; |
| 97 | +} |
| 98 | + |
| 99 | +static int ftCubicTo(const FT_Vector* control1, const FT_Vector* control2, const FT_Vector* to, void* user) { |
| 100 | + FreetypeHatchBuilder* context = reinterpret_cast<FreetypeHatchBuilder*>(user); |
| 101 | + context->cubic(ftPoint2(*control1), ftPoint2(*control2), ftPoint2(*to)); |
| 102 | + return 0; |
| 103 | +} |
| 104 | + |
| 105 | +static int ftMoveToMSDF(const FT_Vector* to, void* user) { |
| 106 | + nbl::ext::TextRendering::GlyphShapeBuilder* context = reinterpret_cast<nbl::ext::TextRendering::GlyphShapeBuilder*>(user); |
| 107 | + context->moveTo(ftPoint2(*to)); |
| 108 | + return 0; |
| 109 | +} |
| 110 | +static int ftLineToMSDF(const FT_Vector* to, void* user) { |
| 111 | + nbl::ext::TextRendering::GlyphShapeBuilder* context = reinterpret_cast<nbl::ext::TextRendering::GlyphShapeBuilder*>(user); |
| 112 | + context->lineTo(ftPoint2(*to)); |
| 113 | + return 0; |
| 114 | +} |
| 115 | + |
| 116 | +static int ftConicToMSDF(const FT_Vector* control, const FT_Vector* to, void* user) { |
| 117 | + nbl::ext::TextRendering::GlyphShapeBuilder* context = reinterpret_cast<nbl::ext::TextRendering::GlyphShapeBuilder*>(user); |
| 118 | + context->quadratic(ftPoint2(*control), ftPoint2(*to)); |
| 119 | + return 0; |
| 120 | +} |
| 121 | + |
| 122 | +static int ftCubicToMSDF(const FT_Vector* control1, const FT_Vector* control2, const FT_Vector* to, void* user) { |
| 123 | + nbl::ext::TextRendering::GlyphShapeBuilder* context = reinterpret_cast<nbl::ext::TextRendering::GlyphShapeBuilder*>(user); |
| 124 | + context->cubic(ftPoint2(*control1), ftPoint2(*control2), ftPoint2(*to)); |
| 125 | + return 0; |
| 126 | +} |
| 127 | + |
| 128 | +bool drawFreetypeGlyph(msdfgen::Shape& shape, FT_Library library, FT_Face face) |
| 129 | +{ |
| 130 | + nbl::ext::TextRendering::GlyphShapeBuilder builder(shape); |
| 131 | + FT_Outline_Funcs ftFunctions; |
| 132 | + ftFunctions.move_to = &ftMoveToMSDF; |
| 133 | + ftFunctions.line_to = &ftLineToMSDF; |
| 134 | + ftFunctions.conic_to = &ftConicToMSDF; |
| 135 | + ftFunctions.cubic_to = &ftCubicToMSDF; |
| 136 | + ftFunctions.shift = 0; |
| 137 | + ftFunctions.delta = 0; |
| 138 | + FT_Error error = FT_Outline_Decompose(&face->glyph->outline, &ftFunctions, &builder); |
| 139 | + if (error) |
| 140 | + return false; |
| 141 | + builder.finish(); |
| 142 | + return true; |
| 143 | +} |
0 commit comments