~jamii/focus

ref: 3036cc5a38b6177ae941502632364a53761cff2f focus/lib/focus/atlas.zig -rw-r--r-- 4.0 KiB
3036cc5aJamie Brandon Remove memory 11 months ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
const focus = @import("../focus.zig");
usingnamespace focus.common;
pub const ui = focus.ui;

const fira_code = @embedFile("../../fonts/Fira_Code_v5.2/woff/FiraCode-Regular.woff");

pub const Atlas = struct {
    allocator: *Allocator,
    point_size: usize,
    font: *c.TTF_Font,
    texture: []Color,
    texture_dims: Vec2,
    char_width: Coord,
    char_height: Coord,
    char_to_rect: []Rect,
    white_rect: Rect,
    right_down_arrow_rect: Rect,
    down_right_arrow_rect: Rect,

    pub fn init(allocator: *Allocator, point_size: usize) Atlas {

        // init SDL2_ttf
        if (c.TTF_Init() != 0)
            panic("TTF_Init failed: {s}", .{c.TTF_GetError()});

        // load font
        var reader = c.SDL_RWFromConstMem(fira_code, @intCast(c_int, fira_code.len)) orelse panic("Font reader failed: {s}", .{c.SDL_GetError()});
        const font = c.TTF_OpenFontRW(
            reader,
            1, // automatically close reader
            @intCast(c_int, point_size),
        ) orelse panic("Font load failed: {s}", .{c.TTF_GetError()});

        // text to be rendered
        var text = allocator.allocSentinel(u8, 128 + 6, 0) catch oom();
        defer allocator.free(text);

        // going to overwrite this with a white block in final texture
        text[0] = ' ';

        // add all ascii chars
        {
            var char: usize = 1;
            while (char < text.len) : (char += 1) {
                text[char] = @intCast(u8, char);
            }
        }

        // add special characters
        std.mem.copy(u8, text[128..], "⤵⤷");

        // render
        const surface = c.TTF_RenderUTF8_Blended(font, text, c.SDL_Color{ .r = 255, .g = 255, .b = 255, .a = 255 }) orelse panic("Atlas render failed: {s}", .{c.TTF_GetError()});
        defer c.SDL_FreeSurface(surface);

        // copy the texture
        assert(surface.*.format.*.format == c.SDL_PIXELFORMAT_ARGB8888);
        const texture = std.mem.dupe(allocator, Color, @ptrCast([*]Color, surface.*.pixels)[0..@intCast(usize, surface.*.w * surface.*.h)]) catch oom();

        // make a white pixel
        texture[0] = Color{ .r = 255, .g = 255, .b = 255, .a = 255 };
        const white_rect = Rect{ .x = 0, .y = 0, .w = 1, .h = 1 };

        // calculate char sizes
        // assume monospaced font
        const char_width = @intCast(Coord, @divTrunc(@intCast(usize, surface.*.w), 128 + 2));
        const char_height = @intCast(Coord, surface.*.h);

        // calculate location of each char
        var char_to_rect = allocator.alloc(Rect, text.len) catch oom();
        char_to_rect[0] = .{ .x = 0, .y = 0, .w = 0, .h = 0 };
        {
            var char: usize = 1;
            while (char < text.len) : (char += 1) {
                char_to_rect[char] = .{
                    .x = @intCast(Coord, char) * char_width,
                    .y = 0,
                    .w = char_width,
                    .h = char_height,
                };
            }
        }
        const right_down_arrow_rect = Rect{
            .x = 128 * char_width,
            .y = 0,
            .w = char_width,
            .h = char_height,
        };
        const down_right_arrow_rect = Rect{
            .x = 129 * char_width,
            .y = 0,
            .w = char_width,
            .h = char_height,
        };

        return Atlas{
            .allocator = allocator,
            .point_size = point_size,
            .font = font,
            .texture = texture,
            .texture_dims = .{
                .x = @intCast(Coord, surface.*.w),
                .y = @intCast(Coord, surface.*.h),
            },
            .char_width = char_width,
            .char_height = char_height,
            .char_to_rect = char_to_rect,
            .white_rect = white_rect,
            .right_down_arrow_rect = right_down_arrow_rect,
            .down_right_arrow_rect = down_right_arrow_rect,
        };
    }

    pub fn deinit(self: *Atlas) void {
        self.allocator.free(self.char_to_rect);
        self.allocator.free(self.texture);
        c.TTF_CloseFont(self.font);
        c.TTF_Quit();
    }
};