Open Bulmanator opened 5 years ago
Can confirm this is still happening on latest master.
The issue is that stbtt_GetGlyphSDF doesn't handle STBTT_vcubic vertex type. It turns out getting the closest point on cubic bezier curve is not trivial problem, but there are some solvers for this: https://github.com/nrtaylor/CubicSplineClosestPoint, https://github.com/erich666/GraphicsGems/blob/master/gems/NearestPoint.c But it might be easier and/or faster to convert the curve into linear segments and use those to generate SDF.
Yeah, makes sense, I think the vcubic support was probably added after the SDF support. Note we solve quadratics directly (requires solving a cubic), so the non-trivial solution to the cubic curve (with a fifth-degree polynomial solver) isn't impossible, but yeah, it probably makes more sense to tesselate to quadratics or line segments.
Distance to a cubic requires solving a fifth-order polynomial though. This would require some decidedly non-trivial changes.
Spent some time trying to figure out what I was doing wrong before finding this, would be nice to have a note in the stbtt_GetGlyphSDF docs for this limitation.
Has anyone worked out a solution for this?
Hey! I did a very quick and !!!VERY!!! dirty fix this morning. I do not recommed using this the way it is. But for future reference and if anyone wants to pick up where I left off: ` int num_verts = stbtt_GetGlyphShape(info, glyph, &verts);
//CUSTOM FIXING CODE STARTS HERE
int num_extraverts = 0;
constexpr int cubicTesselationUpperBound = 4096;
for (i = 0, j = num_verts - 1; i < num_verts; j = i++)
{
if (verts[i].type == STBTT_vcubic)
{
num_extraverts += cubicTesselationUpperBound;
}
}
if (num_extraverts > 0)
{
stbtt_vertex* verts2 = (stbtt_vertex*)STBTT_malloc((num_verts+num_extraverts)*sizeof(stbtt_vertex), info->userdata);
int vc = 0;
for (i = 0, j = num_verts - 1; i < num_verts; j = i++)
{
if (verts[i].type == STBTT_vcubic)
{
int num_points = 0;
stbtt__point points[cubicTesselationUpperBound];
float Flatness = 0.35f / scale;
float objspace_flatness_squared = Flatness * Flatness;
stbtt__tesselate_cubic(points, &num_points, verts[j].x, verts[j].y,
verts[i].cx, verts[i].cy,
verts[i].cx1, verts[i].cy1,
verts[i].x, verts[i].y,
objspace_flatness_squared, 0);
for (int k = 0; k < num_points; ++k)
{
verts2[vc].x = (short)points[k].x;
verts2[vc].y = (short)points[k].y;
verts2[vc].type = STBTT_vline;
vc++;
}
}
else
{
verts2[vc] = verts[i];
vc++;
}
}
STBTT_free(verts, info->userdata);
verts = verts2;
num_verts = vc;
}
//CUSTOM FIXING CODE ENDS HERE
data = (unsigned char*)STBTT_malloc(w * h, info->userdata);
`
I essentially used the tesselation code to convert the cubic curve to a bunch of line segments. The main issue is that it is hard to predict how many new points are going to be inserted. I just used a buffer of 4k points. But no idea what is actually needed in which cases. In theory the tesselation function can add 2^16 points per cubic vertex. Sooo....
But it renders the glyphs somewhat nice, while retaining the properties of the stbtt_GetGlyphSDF function.
Generating SDF values using the Noto Font Family (specifically the NotoSansCJK.ttc font file) produces incorrect results. I have also tried Source Han Sans and this also produces incorrect result. I think Noto uses the CJK glyphs from Source Han Sans so this problem must stem from that instead of Noto itself. Incorrect values shown below (all images produced by writing out the data with stbi_write_png):
However, when using the built in Windows font Yu Gothic, the results are perfectly fine:
It can also produced packed atlases with stbtt_PackFontRanges perfectly fine with Noto.
The code used to generate the SDF glyph: