diff --git a/fonts/ttf/ttf.cpp b/fonts/ttf/ttf.cpp index 1f09915..191608a 100644 --- a/fonts/ttf/ttf.cpp +++ b/fonts/ttf/ttf.cpp @@ -37,7 +37,6 @@ static f26_6 render_codepoint(ttf_face_t* face, SSINT32 cp, EFI_GRAPHICS_OUTPUT_BLT_PIXEL color) { SUINT16 gid = ttf_cmap_lookup(face, cp); - if (gid == 0 && cp != 0) gid = 0; // notdef if (!ttf_load_glyph(face, gid, pixel_size, &s_outline)) { return 0; // composite / parse error @@ -83,8 +82,6 @@ static f26_6 render_codepoint(ttf_face_t* face, SSINT32 cp, ttf_rasterize(s_segs, n_segs, 0, 0, pw, ph, s_coverage, N); // Screen origin of bitmap - // bitmap (0, 0) is at font (xmin, ymax) which is at screen - // (x + xmin/64, y - ymax/64) since screen y is flipped SSINT32 px_screen = x + (s_outline.xmin >> 6); SSINT32 py_screen = y - (s_outline.ymax >> 6); diff --git a/fonts/ttf/ttf_parse.cpp b/fonts/ttf/ttf_parse.cpp index c67321a..e4849a1 100644 --- a/fonts/ttf/ttf_parse.cpp +++ b/fonts/ttf/ttf_parse.cpp @@ -145,7 +145,137 @@ bool ttf_load_glyph(ttf_face_t* face, SUINT16 glyph_id, const SUINT8* g = face->glyf + off0; SSINT16 numContours = rd16s(g + 0); if (numContours < 0) { - // Composite — unsupported in v1. Keep advance, no outline. + // Composite glyph — parse component records and merge outlines + const SUINT8* cp = g + 10; + SSINT32 all_xmin = 0x7FFFFFFF, all_ymin = 0x7FFFFFFF; + SSINT32 all_xmax = -0x7FFFFFFF, all_ymax = -0x7FFFFFFF; + + // Composite glyph flags (OpenType spec): + // 0x0001 = ARG_1_AND_2_ARE_WORDS (16-bit args; else 8-bit) + // 0x0002 = ARGS_ARE_XY_VALUES (offsets; else point indices) + // 0x0008 = WE_HAVE_A_SCALE + // 0x0040 = WE_HAVE_A_2x2 + // 0x0080 = WE_HAVE_AN_X_AND_Y_SCALE + // 0x0020 = MORE_COMPONENTS + + for (;;) { + SUINT16 comp_flags = rd16(cp); cp += 2; + SUINT16 comp_glyph = rd16(cp); cp += 2; + + // Read arguments (size depends on ARG_1_AND_2_ARE_WORDS) + SSINT32 arg1, arg2; + if (comp_flags & 0x0001) { + // 16-bit signed words + arg1 = rd16s(cp); cp += 2; + arg2 = rd16s(cp); cp += 2; + } else { + // 8-bit signed bytes + arg1 = (SSINT8)cp[0]; + arg2 = (SSINT8)cp[1]; + cp += 2; + } + + // Read transform scale if present + f26_6 scale = F26_ONE; + if (comp_flags & 0x0008) { + // WE_HAVE_A_SCALE: 16.16 fixed-point + SSINT16 s16 = rd16s(cp); cp += 2; + scale = (f26_6)s16; // already in f26.6 from 16.16 + } + + // Read x/y scale if present + f26_6 scaleX = F26_ONE, scaleY = F26_ONE; + if (comp_flags & 0x0080) { + // WE_HAVE_AN_X_AND_Y_SCALE: two 16.16 values + SSINT16 sx16 = rd16s(cp); cp += 2; + SSINT16 sy16 = rd16s(cp); cp += 2; + scaleX = (f26_6)sx16; + scaleY = (f26_6)sy16; + } + + // Read 2x2 matrix if present + f26_6 m00 = F26_ONE, m01 = 0, m10 = 0, m11 = F26_ONE; + if (comp_flags & 0x0040) { + // WE_HAVE_A_2x2: four 2.14 values + SSINT16 a = rd16s(cp); cp += 2; + SSINT16 b = rd16s(cp); cp += 2; + SSINT16 c = rd16s(cp); cp += 2; + SSINT16 d = rd16s(cp); cp += 2; + m00 = (f26_6)(a >> 8); // 2.14 -> 26.6: shift right 8 + m01 = (f26_6)(b >> 8); + m10 = (f26_6)(c >> 8); + m11 = (f26_6)(d >> 8); + } + + // Load component glyph + ttf_outline_t comp; + if (!ttf_load_glyph(face, comp_glyph, pixel_size_px, &comp)) + return false; + + // Determine if args are offsets (XY values) or point indices + SSINT32 offset_x = 0, offset_y = 0; + if (comp_flags & 0x0002) { + // ARGS_ARE_XY_VALUES: args are pixel offsets (already scaled) + // Scale from font units to pixel space like simple glyphs + offset_x = (SSINT32)(((SSINT64)arg1 * (SSINT64)pixel_size_px * 64 / face->units_per_em)); + offset_y = (SSINT32)(((SSINT64)arg2 * (SSINT64)pixel_size_px * 64 / face->units_per_em)); + } + + // Transform and merge component points + for (SUINT16 i = 0; i < comp.num_points; i++) { + f26_6 fx = comp.x[i]; + f26_6 fy = comp.y[i]; + f26_6 tx, ty; + + if (comp_flags & 0x0040) { + // 2x2 matrix transform + tx = f26_mul(m00, fx) + f26_mul(m01, fy); + ty = f26_mul(m10, fx) + f26_mul(m11, fy); + } else if (comp_flags & 0x0008) { + // Uniform scale + tx = f26_mul(scale, fx); + ty = f26_mul(scale, fy); + } else if (comp_flags & 0x0080) { + // X/Y scale + tx = f26_mul(scaleX, fx); + ty = f26_mul(scaleY, fy); + } else { + tx = fx; + ty = fy; + } + + f26_6 final_x = tx + offset_x; + f26_6 final_y = ty + offset_y; + + out->x[out->num_points] = final_x; + out->y[out->num_points] = final_y; + out->on_curve[out->num_points] = comp.on_curve[i]; + out->num_points++; + if (out->num_points > 1024) return false; + + SSINT32 xi = F26_FLOOR(final_x); + SSINT32 yi = F26_FLOOR(final_y); + if (xi < all_xmin) all_xmin = xi; + if (xi > all_xmax) all_xmax = xi; + if (yi < all_ymin) all_ymin = yi; + if (yi > all_ymax) all_ymax = yi; + } + + for (SUINT16 i = 0; i < comp.num_contours; i++) { + if (out->num_contours >= 64) return false; + out->first[out->num_contours] = (SUINT16)(out->num_points - comp.num_points + comp.first[i]); + out->last[out->num_contours] = (SUINT16)(out->num_points - comp.num_points + comp.last[i]); + out->num_contours++; + } + + if (!(comp_flags & 0x0020)) break; // no more components + } + + if (out->num_points == 0) return true; + out->xmin = all_xmin; + out->ymin = all_ymin; + out->xmax = all_xmax; + out->ymax = all_ymax; return true; } if (numContours == 0) return true; @@ -173,7 +303,7 @@ bool ttf_load_glyph(ttf_face_t* face, SUINT16 glyph_id, flags[pi++] = f; if (f & 0x08) { SUINT8 rep = *p++; - for (SUINT16 k = 1; k < rep && pi < numPoints; k++) + for (SUINT16 k = 1; k <= rep && pi < numPoints; k++) flags[pi++] = f; } } diff --git a/fonts/ttf/ttf_render.cpp b/fonts/ttf/ttf_render.cpp index af1f3ce..d6b469c 100644 --- a/fonts/ttf/ttf_render.cpp +++ b/fonts/ttf/ttf_render.cpp @@ -193,6 +193,16 @@ void ttf_rasterize(const ttf_seg_t* segs, SUINT32 num_segs, while (j > 0 && xs[j-1] > v) { xs[j] = xs[j-1]; j--; } xs[j] = v; } + // Deduplicate: merge intersections within 1 pixel (64 in 26.6) + SUINT32 nxd = 0; + for (SUINT32 i = 0; i < nxs; i++) { + if (nxd > 0 && (xs[i] - xs[nxd - 1]) < 64) { + // Near-duplicate — skip to avoid spurious fill slivers + continue; + } + xs[nxd++] = xs[i]; + } + nxs = nxd; // Fill alternating pairs for (SUINT32 i = 0; i + 1 < nxs; i += 2) { f26_6 xa = xs[i];