From 333a32208e2988661f76cd04d6680ffcd4e0f575 Mon Sep 17 00:00:00 2001 From: Daniel Wennberg Date: Thu, 18 Sep 2025 14:01:00 -0700 Subject: [PATCH] Factor out glyph rect function --- src/font/face/freetype.zig | 120 ++++++++++++------------------------- 1 file changed, 39 insertions(+), 81 deletions(-) diff --git a/src/font/face/freetype.zig b/src/font/face/freetype.zig index e3d4b34cc..0dc4c4c03 100644 --- a/src/font/face/freetype.zig +++ b/src/font/face/freetype.zig @@ -385,6 +385,34 @@ pub const Face = struct { }; } + /// Get a rect that represents the position and size of the loaded glyph. + fn getGlyphSize(glyph: freetype.c.FT_GlyphSlot) font.face.RenderOptions.Constraint.GlyphSize { + // If we're dealing with an outline glyph then we get the + // outline's bounding box instead of using the built-in + // metrics, since that's more precise and allows better + // cell-fitting. + if (glyph.*.format == freetype.c.FT_GLYPH_FORMAT_OUTLINE) { + // Get the glyph's bounding box before we transform it at all. + // We use this rather than the metrics, since it's more precise. + var bbox: freetype.c.FT_BBox = undefined; + _ = freetype.c.FT_Outline_Get_BBox(&glyph.*.outline, &bbox); + + return .{ + .x = f26dot6ToF64(bbox.xMin), + .y = f26dot6ToF64(bbox.yMin), + .width = f26dot6ToF64(bbox.xMax - bbox.xMin), + .height = f26dot6ToF64(bbox.yMax - bbox.yMin), + }; + } + + return .{ + .x = f26dot6ToF64(glyph.*.metrics.horiBearingX), + .y = f26dot6ToF64(glyph.*.metrics.horiBearingY - glyph.*.metrics.height), + .width = f26dot6ToF64(glyph.*.metrics.width), + .height = f26dot6ToF64(glyph.*.metrics.height), + }; + } + /// Render a glyph using the glyph index. The rendered glyph is stored in the /// given texture atlas. pub fn renderGlyph( @@ -403,37 +431,7 @@ pub const Face = struct { // We get a rect that represents the position // and size of the glyph before any changes. - const rect: struct { - x: f64, - y: f64, - width: f64, - height: f64, - } = metrics: { - // If we're dealing with an outline glyph then we get the - // outline's bounding box instead of using the built-in - // metrics, since that's more precise and allows better - // cell-fitting. - if (glyph.*.format == freetype.c.FT_GLYPH_FORMAT_OUTLINE) { - // Get the glyph's bounding box before we transform it at all. - // We use this rather than the metrics, since it's more precise. - var bbox: freetype.c.FT_BBox = undefined; - _ = freetype.c.FT_Outline_Get_BBox(&glyph.*.outline, &bbox); - - break :metrics .{ - .x = f26dot6ToF64(bbox.xMin), - .y = f26dot6ToF64(bbox.yMin), - .width = f26dot6ToF64(bbox.xMax - bbox.xMin), - .height = f26dot6ToF64(bbox.yMax - bbox.yMin), - }; - } - - break :metrics .{ - .x = f26dot6ToF64(glyph.*.metrics.horiBearingX), - .y = f26dot6ToF64(glyph.*.metrics.horiBearingY - glyph.*.metrics.height), - .width = f26dot6ToF64(glyph.*.metrics.width), - .height = f26dot6ToF64(glyph.*.metrics.height), - }; - }; + const rect = getGlyphSize(glyph); // If our glyph is smaller than a quarter pixel in either axis // then it has no outlines or they're too small to render. @@ -988,21 +986,9 @@ pub const Face = struct { f26dot6ToF64(glyph.*.advance.x), max, ); - // We use the outline's bbox instead of the built-in - // metrics for better accuracy (see renderGlyph()). - const ymin, const ymax = metrics: { - if (glyph.*.format == freetype.c.FT_GLYPH_FORMAT_OUTLINE) { - var bbox: freetype.c.FT_BBox = undefined; - _ = freetype.c.FT_Outline_Get_BBox(&glyph.*.outline, &bbox); - break :metrics .{ bbox.yMin, bbox.yMax }; - } - break :metrics .{ - glyph.*.metrics.horiBearingY - glyph.*.metrics.height, - glyph.*.metrics.horiBearingY, - }; - }; - top = @max(f26dot6ToF64(ymax), top); - bottom = @min(f26dot6ToF64(ymin), bottom); + const rect = getGlyphSize(glyph); + top = @max(rect.y + rect.height, top); + bottom = @min(rect.y, bottom); } else |_| {} } } @@ -1042,15 +1028,7 @@ pub const Face = struct { defer self.ft_mutex.unlock(); if (face.getCharIndex('H')) |glyph_index| { if (face.loadGlyph(glyph_index, self.glyphLoadFlags(false))) { - const glyph = face.handle.*.glyph; - // We use the outline's bbox instead of the built-in - // metrics for better accuracy (see renderGlyph()). - if (glyph.*.format == freetype.c.FT_GLYPH_FORMAT_OUTLINE) { - var bbox: freetype.c.FT_BBox = undefined; - _ = freetype.c.FT_Outline_Get_BBox(&glyph.*.outline, &bbox); - break :cap f26dot6ToF64(bbox.yMax - bbox.yMin); - } - break :cap f26dot6ToF64(glyph.*.metrics.height); + break :cap getGlyphSize(face.handle.*.glyph).height; } else |_| {} } break :cap null; @@ -1060,15 +1038,7 @@ pub const Face = struct { defer self.ft_mutex.unlock(); if (face.getCharIndex('x')) |glyph_index| { if (face.loadGlyph(glyph_index, self.glyphLoadFlags(false))) { - const glyph = face.handle.*.glyph; - // We use the outline's bbox instead of the built-in - // metrics for better accuracy (see renderGlyph()). - if (glyph.*.format == freetype.c.FT_GLYPH_FORMAT_OUTLINE) { - var bbox: freetype.c.FT_BBox = undefined; - _ = freetype.c.FT_Outline_Get_BBox(&glyph.*.outline, &bbox); - break :ex f26dot6ToF64(bbox.yMax - bbox.yMin); - } - break :ex f26dot6ToF64(glyph.*.metrics.height); + break :ex getGlyphSize(face.handle.*.glyph).height; } else |_| {} } break :ex null; @@ -1095,31 +1065,19 @@ pub const Face = struct { // This can sometimes happen if there's a CJK font that has been // patched with the nerd fonts patcher and it butchers the advance // values so the advance ends up half the width of the actual glyph. - const ft_glyph_width = ft_glyph_width: { - // We use the outline's bbox instead of the built-in - // metrics for better accuracy (see renderGlyph()). - if (ft_glyph.*.format == freetype.c.FT_GLYPH_FORMAT_OUTLINE) { - var bbox: freetype.c.FT_BBox = undefined; - _ = freetype.c.FT_Outline_Get_BBox(&ft_glyph.*.outline, &bbox); - break :ft_glyph_width bbox.xMax - bbox.xMin; - } - break :ft_glyph_width ft_glyph.*.metrics.width; - }; - if (ft_glyph_width > ft_glyph.*.advance.x) { + const ft_glyph_width = getGlyphSize(ft_glyph).width; + const advance = f26dot6ToF64(ft_glyph.*.advance.x); + if (ft_glyph_width > advance) { var buf: [1024]u8 = undefined; const font_name = self.name(&buf) catch ""; log.warn( "(getMetrics) Width of glyph '水' for font \"{s}\" is greater than its advance ({d} > {d}), discarding ic_width metric.", - .{ - font_name, - f26dot6ToF64(ft_glyph_width), - f26dot6ToF64(ft_glyph.*.advance.x), - }, + .{ font_name, ft_glyph_width, advance }, ); break :ic_width null; } - break :ic_width f26dot6ToF64(ft_glyph.*.advance.x); + break :ic_width advance; }; return .{