Glyph atlas

Shows the whole sheet natiny.font.get_atlas bakes glyphs into. Press 1, 2 or 3 to add a code point range with natiny.font.preload_range and watch it fill; F switches the atlas filter.


    
main.lua
local natiny = require("natiny")

natiny.backend.init()

local win = natiny.window.create("Glyph atlas", 900, 600)
natiny.window.set_scale_mode(win, natiny.window.SCALE_MODE_STRETCH)

local res   = natiny.resource.load("common/fonts/roboto.ttf")
local font  = natiny.font.create(res, 44)
local small = natiny.font.create(res, 18)

local BG = natiny.color.from_hex("#151E2C")
local PANEL = natiny.color.from_hex("#0F1621")
local OBJECT = natiny.color.from_hex("#A9B3C5")
local ACCENT = natiny.color.from_hex("#A8C238")
local LIGHT_TEXT = natiny.color.from_hex("#F3F5FA")
-- A font bakes a glyph the first time something asks for it. preload_range
-- asks for a whole block at once, so the cost is paid here instead of mid-scene.
local RANGES = {
    { key = natiny.input.KEY_1, name = "1  Latin  U+0020..U+007E",
      first = 0x20,   last = 0x7E,   sample = "Sphinx of black quartz" },
    { key = natiny.input.KEY_2, name = "2  Cyrillic  U+0410..U+044F",
      first = 0x0410, last = 0x044F, sample = "Съешь ещё этих булок" },
    { key = natiny.input.KEY_3, name = "3  Greek  U+0391..U+03C9",
      first = 0x0391, last = 0x03C9, sample = "Ταχίστη αλώπηξ" },
}

local ATLAS_X, ATLAS_Y, ATLAS_SIZE = 60, 110, 420
local nearest = false

natiny.backend.loop(function(dt)
    natiny.window.bind(win, function()
        for _, range in ipairs(RANGES) do
            if natiny.input.get_key_pressed(range.key) and not range.loaded then
                natiny.font.preload_range(font, range.first, range.last)
                range.loaded = true
            end
        end
        if natiny.input.get_key_pressed(natiny.input.KEY_F) then
            nearest = not nearest
            natiny.font.set_filter(font, nearest and natiny.font.FILTER_NEAREST
                                                 or natiny.font.FILTER_LINEAR)
        end

        local atlas = natiny.font.get_atlas(font)

        natiny.render.clear(BG)

        natiny.render.set_color(LIGHT_TEXT)
        natiny.render.text(small, ATLAS_X, 50,
            "press 1, 2, 3 to bake a range into the atlas - F switches its filter")

        local sheet = natiny.texture.get_width(atlas)

        natiny.render.set_color(PANEL)
        natiny.render.rectangle(ATLAS_X, ATLAS_Y, ATLAS_SIZE, ATLAS_SIZE)

        -- The whole sheet, scaled down to the panel: the empty part below the
        -- baked rows is the room the next range will be packed into.
        natiny.render.set_color(natiny.color.from_hex("#FFFFFF"))
        natiny.render.texture(atlas, ATLAS_X, ATLAS_Y, ATLAS_SIZE, ATLAS_SIZE)

        natiny.render.set_color(natiny.color.from_hex("#A9B3C566"))
        natiny.render.rectangle_outline(ATLAS_X, ATLAS_Y, ATLAS_SIZE, ATLAS_SIZE, 2)

        natiny.render.set_color(OBJECT)
        natiny.render.text(small, ATLAS_X, ATLAS_Y + ATLAS_SIZE + 10, string.format(
            "the whole %d x %d sheet, %s",
            sheet, natiny.texture.get_height(atlas),
            nearest and "nearest" or "linear"))

        local x, y = ATLAS_X + ATLAS_SIZE + 50, ATLAS_Y
        for _, range in ipairs(RANGES) do
            local ink = range.loaded and ACCENT or OBJECT
            natiny.render.set_color(ink)
            natiny.render.text(small, x, y, range.name)

            -- has_glyph answers without baking anything, fallbacks included.
            local ok = natiny.font.has_glyph(font, range.first + 1)
            natiny.render.set_color(natiny.color.from_hex("#A9B3C5B3"))
            natiny.render.text(small, x, y + 24,
                ok and (range.loaded and range.sample or "in the font, not yet baked")
                    or "this font has no such glyphs")
            y = y + 74
        end
    end)
end)

natiny.backend.shutdown()