DVUI

init

Load the underlying font at an integer size <= font.size (guaranteed to have a minimum pixel size of 1)

Parameters

#
gpa:std.mem.Allocator
std.mem.Allocator
ttf_bytes:[]const u8
[]const u8
font:Font
Font

Source

Implementation

#
pub fn init(gpa: std.mem.Allocator, ttf_bytes: []const u8, font: Font) Error!Entry {
            const min_pixel_size = 1;

            const fname = font.name(gpa);
            errdefer gpa.free(fname);

            var self: Entry = if (impl == .FreeType) blk: {
                var face: c.FT_Face = undefined;
                var args: c.FT_Open_Args = undefined;
                args.flags = @as(u32, @bitCast(FreeType.OpenFlags{ .memory = true }));
                args.memory_base = ttf_bytes.ptr;
                args.memory_size = @as(u31, @intCast(ttf_bytes.len));
                FreeType.intToError(c.FT_Open_Face(dvui.ft2lib, &args, 0, &face)) catch |err| {
                    dvui.log.warn("Font.Cache.Entry.init() freetype error {any} trying to FT_Open_Face font {s}\n", .{ err, fname });
                    return Error.FontError;
                };

                // "pixel size" for freetype doesn't actually mean you'll get that height, it's more like using pts
                // so we search for a font that has a height <= font.size
                var pixel_size = @as(u32, @trunc(@max(min_pixel_size, @floor(font.size))));
                pixel_size += 20;

                while (true) : (pixel_size -= 1) {
                    FreeType.intToError(c.FT_Set_Pixel_Sizes(face, pixel_size, pixel_size)) catch |err| {
                        dvui.log.warn("Font.Cache.Entry.init() freetype error {any} trying to FT_Set_Pixel_Sizes font {s}\n", .{ err, fname });
                        return Error.FontError;
                    };

                    const ascender = @as(f32, @floatFromInt(face.*.ascender)) / 64.0;
                    const ss = @as(f32, @floatFromInt(face.*.size.*.metrics.y_scale)) / 0x10000;
                    const fascent = ascender * ss;
                    const descender = @as(f32, @floatFromInt(face.*.descender)) / 64.0;
                    const descent = descender * ss;

                    var entry: Entry = .{
                        .face = face,
                        .name = fname,
                        .scaleFactor = 1.0, // not used with freetype
                        .height = fascent - descent,
                        .ascent = @trunc(fascent), // cheat ascent a bit, must be an integer
                        .em_height = undefined, // below
                        .glyph_info_ascii = undefined,
                    };

                    const M = try entry.glyphInfoGenerate('M');
                    if (M.h <= 0) {
                        dvui.log.warn("Font.Cache.Entry.init() freetype error getting M font {s} size {d}\n", .{ fname, font.size });
                        return error.FontError;
                    }

                    if (M.h <= font.size or pixel_size == min_pixel_size) {
                        //std.debug.print("{s} pixel_size {d} ascent {d} descent {d} height {d}\n", .{fname, pixel_size, ascent, descent, ascent - descent});
                        entry.em_height = M.h;
                        break :blk entry;
                    }
                }
            } else blk: {
                const offset = c.stbtt_GetFontOffsetForIndex(ttf_bytes.ptr, 0);
                if (offset < 0) {
                    dvui.log.warn("Font.Cache.Entry.init() stbtt error when calling stbtt_GetFontOffsetForIndex font {s}\n", .{fname});
                    return Error.FontError;
                }
                var face: c.stbtt_fontinfo = undefined;
                if (c.stbtt_InitFont(&face, ttf_bytes.ptr, offset) != 1) {
                    dvui.log.warn("Font.Cache.Entry.init() stbtt error when calling stbtt_InitFont font {s}\n", .{fname});
                    return Error.FontError;
                }

                var face_ascent: c_int = undefined;
                var face_descent: c_int = undefined;
                c.stbtt_GetFontVMetrics(&face, &face_ascent, &face_descent, null);

                var entry: Entry = .{
                    .face = face,
                    .name = fname,
                    .scaleFactor = 1.0, // adjusted below
                    .height = 1.0, // adjusted below
                    .ascent = 1.0, // adjusted below
                    .em_height = undefined, // below
                    .glyph_info_ascii = undefined,
                };

                const M = try entry.glyphInfoGenerate('M');
                if (M.h <= 0) {
                    dvui.log.warn("Font.Cache.Entry.init() stbtt error getting M font {s} size {d}\n", .{ fname, font.size });
                    return error.FontError;
                }
                entry.scaleFactor = @max(min_pixel_size, @floor(font.size)) / M.h;
                const ascender = entry.scaleFactor * @as(f32, @floatFromInt(face_ascent));
                const descender = entry.scaleFactor * @as(f32, @floatFromInt(face_descent));
                entry.ascent = @trunc(ascender); // cheat the ascent a bit, must be integer
                entry.height = ascender - descender;
                entry.em_height = entry.scaleFactor * M.h;

                //std.debug.print("{s} ascent {d} descent {d} computed ascent {d} height {d}\n", .{fname, ascender, descender, entry.ascent, entry.height});
                break :blk entry;
            };

            // Pre-generate the ascii glyphs
            //std.debug.print("char,codepoint,hx,w,hy,h,adv\n", .{});
            for (0..self.glyph_info_ascii.len) |i| {
                self.glyph_info_ascii[i] = try self.glyphInfoGenerate(@intCast(i + ascii_start));
            }

            //const gi = self.glyph_info_ascii['M' - ascii_start];
            //std.debug.print("{s} size {d} height {d} M left {d} top {d} wxh {d}x{d}\n", .{ fname, font.size, self.height, gi.leftBearing, gi.topBearing, gi.w, gi.h});

            return self;
        }