Skip to content

Instantly share code, notes, and snippets.

@JPGygax68
Created June 8, 2020 12:45
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save JPGygax68/dd4e573d4aeb61ea0c12b2f7141190d1 to your computer and use it in GitHub Desktop.
Save JPGygax68/dd4e573d4aeb61ea0c12b2f7141190d1 to your computer and use it in GitHub Desktop.
(Posted to help debugging a ZLS problem under VSCode)
const std = @import("std");
const c = @cImport({
// @cInclude("imgui/imgui.h");
// @cInclude("imgui/imgui_internal.h");
@cInclude("imgui/imstb_truetype.h");
@cInclude("imgui/imstb_rectpack.h");
});
const assert = std.debug.assert;
const Allocator = std.mem.Allocator;
const ARCFAST_TESSELLATION_MULTIPLIER = 1;
const UNICODE_CODEPOINT_MAX = 0x10FFFF;
const UChar = u21; // Unicode character
pub const Vec2 = struct {
x: f32 = 0,
y: f32 = 0,
};
pub const Vec4 = struct {
x: f32 = 0,
y: f32 = 0,
z: f32 = 0,
w: f32 = 0,
};
pub const FontGlyph = packed struct {
CodePoint: u31, // 0x0000..0xFFFF
Visible: u1, // Flag to allow early out when rendering
AdvanceX: f32, // Distance to next character (= data from font + ImFontConfig::GlyphExtraSpacing.x baked in)
X0: f32,
Y0: f32,
X1: f32,
Y1: f32, // Glyph corners
U0: f32,
V0: f32,
U1: f32,
V1: f32, // Texture coordinates
};
pub const GlyphRange = struct {
start: UChar,
end: UChar,
};
pub const FontConfig = struct {
FontData: []const u8, // // TTF/OTF data
FontDataOwnedByAtlas: bool, // true // TTF/OTF data ownership taken by the container ImFontAtlas (will delete memory itself).
FontNo: c_int, // 0 // Index of font within TTF/OTF file
SizePixels: f32, // // Size in pixels for rasterizer (more or less maps to the resulting font height).
OversampleH: u8, // 3 // Rasterize at higher quality for sub-pixel positioning. Read https://github.com/nothings/stb/blob/master/tests/oversample/README.md for details.
OversampleV: u8, // 1 // Rasterize at higher quality for sub-pixel positioning. We don't use sub-pixel positions on the Y axis.
PixelSnapH: bool, // false // Align every glyph to pixel boundary. Useful e.g. if you are merging a non-pixel aligned font with the default font. If enabled, you can set OversampleH/V to 1.
GlyphExtraSpacing: Vec2, // 0, 0 // Extra spacing (in pixels) between glyphs. Only X axis is supported for now.
GlyphOffset: Vec2, // 0, 0 // Offset all glyphs from this font input.
GlyphRanges: ?[]const GlyphRange, // NULL // Pointer to a user-provided list of Unicode range (2 value per range, values are inclusive, zero-terminated list). THE ARRAY DATA NEEDS TO PERSIST AS LONG AS THE FONT IS ALIVE.
GlyphMinAdvanceX: f32, // 0 // Minimum AdvanceX for glyphs, set Min to align font icons, set both Min/Max to enforce mono-space font
GlyphMaxAdvanceX: f32, // FLT_MAX // Maximum AdvanceX for glyphs
MergeMode: bool, // false // Merge into previous ImFont, so you can combine multiple inputs font into one ImFont (e.g. ASCII font + icons + Japanese glyphs). You may want to use GlyphOffset.y when merge font of different heights.
RasterizerFlags: u32, // 0x00 // Settings for custom font rasterizer (e.g. ImGuiFreeType). Leave as zero if you aren't using one.
RasterizerMultiply: f32, // 1.0f // Brighten (>1.0f) or darken (<1.0f) font output. Brightening small fonts may be a good workaround to make them more readable.
EllipsisChar: UChar, // -1 // Explicitly specify unicode codepoint of ellipsis character. When fonts are being merged first specified ellipsis will be used.
// [Internal]
Name: [40]u8, // Name (strictly to ease debugging)
DstFont: ?*Font,
pub fn init() FontConfig {
return FontConfig{
.FontData = &[_]u8{},
.FontDataOwnedByAtlas = true,
.FontNo = 0,
.SizePixels = 0.0,
.OversampleH = 3, // FIXME: 2 may be a better default?
.OversampleV = 1,
.PixelSnapH = false,
.GlyphExtraSpacing = .{ .x = 0, .y = 0 },
.GlyphOffset = .{ .x = 0, .y = 0 },
.GlyphRanges = null,
.GlyphMinAdvanceX = 0.0,
.GlyphMaxAdvanceX = std.math.f32_max,
.MergeMode = false,
.RasterizerFlags = 0x00,
.RasterizerMultiply = 1.0,
.EllipsisChar = std.math.maxInt(UChar),
.Name = std.mem.zeroes([40]u8),
.DstFont = null,
};
}
};
pub const FontAtlasFlags = packed struct {
NoPowerOfTwoHeight: bool = false, // Don't round the height to next power of two
NoMouseCursors: bool = false, // Don't build software mouse cursors into the atlas
};
pub const FontAtlasCustomRect = struct {
Width: u16,
Height: u16, // Input // Desired rectangle dimension
X: u16,
Y: u16, // Output // Packed position in Atlas
GlyphID: u32, // Input // For custom font glyphs only (ID < 0x110000)
GlyphAdvanceX: f32, // Input // For custom font glyphs only: glyph xadvance
GlyphOffset: Vec2, // Input // For custom font glyphs only: glyph display offset
Font: *Font, // Input // For custom font glyphs only: target font
pub fn init() FontAtlasCustomRect {
return FontAtlasCustomRect{
.Width = 0,
.Height = 0,
.X = 0,
.Y = 0xFFFF,
.GlyphID = 0,
.GlyphAdvanceX = 0,
.GlyphOffset = .{ 0, 0 },
.Font = null,
};
}
pub fn IsPacked(self: FontAtlasCustomRect) bool {
return self.X != 0xFFFF;
}
};
// Load and rasterize multiple TTF/OTF fonts into a same texture. The font atlas will build a single texture holding:
// - One or more fonts.
// - Custom graphics data needed to render the shapes needed by Dear ImGui.
// - Mouse cursor shapes for software cursor rendering (unless setting 'Flags |= ImFontAtlasFlags_NoMouseCursors' in the font atlas).
// It is the user-code responsibility to setup/build the atlas, then upload the pixel data into a texture accessible by your graphics api.
// - Optionally, call any of the AddFont*** functions. If you don't call any, the default font embedded in the code will be loaded for you.
// - Call GetTexDataAsAlpha8() or GetTexDataAsRGBA32() to build and retrieve pixels data.
// - Upload the pixels data into a texture within your graphics system (see imgui_impl_xxxx.cpp examples)
// - Call SetTexID(my_tex_id); and pass the pointer/identifier to your texture in a format natural to your graphics API.
// This value will be passed back to you during rendering to identify the texture. Read FAQ entry about ImTextureID for more details.
// Common pitfalls:
// - If you pass a 'glyph_ranges' array to AddFont*** functions, you need to make sure that your array persist up until the
// atlas is build (when calling GetTexData*** or Build()). We only copy the pointer, not the data.
// - Important: By default, AddFontFromMemoryTTF() takes ownership of the data. Even though we are not writing to it, we will free the pointer on destruction.
// You can set font_cfg->FontDataOwnedByAtlas=false to keep ownership of your data and it won't be freed,
// - Even though many functions are suffixed with "TTF", OTF data is supported just as well.
// - This is an old API and it is currently awkward for those and and various other reasons! We will address them in the future!
pub const FontAtlas = struct {
const Error = error {
FontNotContainedInAtlas,
InitFontFailed,
};
pub fn init(alltor: *std.mem.Allocator) FontAtlas {
var atlas = FontAtlas{
.Locked = false,
.Flags = .{},
.TexID = 0,
.TexDesiredWidth = 0,
.TexGlyphPadding = 1,
.TexPixelsAlpha8 = null,
.TexPixelsRGBA32 = null,
.TexWidth = 0,
.TexHeight = 0,
.TexUvScale = Vec2{ .x = 0, .y = 0 },
.TexUvWhitePixel = Vec2{ .x = 0, .y = 0 },
.Fonts = std.ArrayList(*Font).init(alltor),
.CustomRects = std.ArrayList(FontAtlasCustomRect).init(alltor),
.ConfigData = std.ArrayList(FontConfig).init(alltor),
.CustomRectIds = .{-1},
};
return atlas;
}
pub fn AddFont(self: *FontAtlas, font_cfg: FontConfig, alltor: *std.mem.Allocator) anyerror!*Font {
assert(!self.Locked); // and "Cannot modify a locked ImFontAtlas between NewFrame() and EndFrame/Render()!");
assert(font_cfg.SizePixels > 0);
// Create new font
var font: *Font = &(try alltor.alloc(Font, 1))[0];
if (!font_cfg.MergeMode)
try self.Fonts.append(font)
else {
// When using MergeMode make sure that a font has already been added before. You can use ImGui::GetIO().Fonts->AddFontDefault() to add the default imgui font.
assert(self.Fonts.items.len > 0); // and "Cannot use MergeMode for the first font");
font = self.Fonts.items[self.Fonts.items.len - 1];
}
try self.ConfigData.append(font_cfg);
var new_font_cfg = &self.ConfigData.items[self.ConfigData.items.len - 1];
if (new_font_cfg.DstFont == null)
new_font_cfg.DstFont = self.Fonts.items[self.Fonts.items.len - 1];
if (!new_font_cfg.FontDataOwnedByAtlas) {
new_font_cfg.FontData = try alltor.alloc(u8, new_font_cfg.FontData.len);
new_font_cfg.FontData = font_cfg.FontData;
// std.mem.copy([new_font_cfg.FontDataSize]u8, new_font_cfg.FontData, font_cfg.FontData);
new_font_cfg.FontDataOwnedByAtlas = true;
}
if (new_font_cfg.DstFont.?.*.EllipsisChar == std.math.maxInt(UChar))
new_font_cfg.DstFont.?.*.EllipsisChar = font_cfg.EllipsisChar;
// Invalidate texture
self.ClearTexData();
return new_font_cfg.DstFont.?;
}
pub fn AddFontDefault(self: FontAtlas, font_cfg_tmpl: ?FontConfig) *Font {
var font_cfg: FontConfig = if (font_cfg_tmpl != null) font_cfg_tmpl.? else FontConfig.init();
if (font_cfg_tmpl == null) {
font_cfg.OversampleH = 1; font_cfg.OversampleV = 1;
font_cfg.PixelSnapH = true;
}
if (font_cfg.SizePixels <= 0.0)
font_cfg.SizePixels = 13.0 * 1.0;
if (font_cfg.Name[0] == '\x00')
std.fmt.bufPrint(font_cfg.Name[0..], "ProggyClean.ttf, {}px", font_cfg.SizePixels);
font_cfg.EllipsisChar = 0x0085;
const ttf_compressed_base85 = GetDefaultCompressedFontDataTTFBase85();
const glyph_ranges = if (font_cfg.GlyphRanges != null) font_cfg.GlyphRanges else GetGlyphRangesDefault();
var font = AddFontFromMemoryCompressedBase85TTF(ttf_compressed_base85, font_cfg.SizePixels, &font_cfg, glyph_ranges);
font.DisplayOffset.y = 1.0;
return font;
}
pub fn AddFontFromFileTTF(
self: *FontAtlas,
filename: []const u8,
size_pixels: f32,
alltor: *Allocator,
font_cfg_tmpl: ?FontConfig,
glyph_ranges: ?[]const GlyphRange
) !*Font {
assert(!self.Locked); // "Cannot modify a locked ImFontAtlas between NewFrame() and EndFrame/Render()!");
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
defer arena.deinit();
var data = try std.fs.cwd().readFileAlloc(&arena.allocator, filename, 10 * 1024 * 1024);
var font_cfg = if (font_cfg_tmpl != null) font_cfg_tmpl.? else FontConfig.init();
if (font_cfg.Name[0] == '\x00')
std.mem.copy(u8, &font_cfg.Name, filename);
return self.*.AddFontFromMemoryTTF(data, size_pixels, alltor, font_cfg, glyph_ranges);
}
// Note: Transfer ownership of 'ttf_data' to ImFontAtlas! Will be deleted after destruction of the atlas. Set font_cfg->FontDataOwnedByAtlas=false to keep ownership of your data and it won't be freed.
pub fn AddFontFromMemoryTTF(
self: *FontAtlas,
ttf_data: []const u8,
size_pixels: f32,
alltor: *Allocator,
font_cfg_tmpl: ?FontConfig,
glyph_ranges: ?[]const GlyphRange
) anyerror!*Font
{
assert(!self.*.Locked); // && "Cannot modify a locked ImFontAtlas between NewFrame() and EndFrame/Render()!");
var font_cfg = if (font_cfg_tmpl != null) font_cfg_tmpl.? else FontConfig.init();
assert(font_cfg.FontData.len == 0);
font_cfg.FontData = ttf_data;
font_cfg.SizePixels = size_pixels;
font_cfg.GlyphRanges = glyph_ranges;
return self.*.AddFont(font_cfg, alltor);
}
// 'compressed_font_data' still owned by caller. Compress with binary_to_compressed_c.cpp.
pub fn AddFontFromMemoryCompressedTTF(self: FontAtlas, compressed_font_data: var, compressed_font_size: i32, size_pixels: f32, font_cfg: ?FontConfig, glyph_ranges: ?[_]UChar) Font {
// TODO
}
// 'compressed_font_data_base85' still owned by caller. Compress with binary_to_compressed_c.cpp with -base85 parameter.
pub fn AddFontFromMemoryCompressedBase85TTF(self: FontAtlas, compressed_font_data_base85: [_]u8, size_pixels: f32, font_cfg: ?FontConfig, glyph_ranges: ?[_]UChar) Font {
// TODO
}
// Clear input data (all ImFontConfig structures including sizes, TTF data, glyph ranges, etc.) = all the data used to build the texture and fonts.
pub fn ClearInputData(self: FontAtlas) void {
// TODO
}
// Clear output texture data (CPU side). Saves RAM once the texture has been copied to graphics memory.
pub fn ClearTexData(self: FontAtlas) void {
// TODO
}
// Clear output font data (glyphs storage, UV coordinates).
pub fn ClearFonts(self: FontAtlas) void {
// TODO
}
// Clear all input and output.
pub fn Clear() void {
// TODO
}
// Build atlas, retrieve pixel data.
// User is in charge of copying the pixels into graphics memory (e.g. create a texture with your engine). Then store your texture handle with SetTexID().
// The pitch is always = Width * BytesPerPixels (1 or 4)
// Building in RGBA32 format is provided for convenience and compatibility, but note that unless you manually manipulate or copy color data into
// the texture (e.g. when using the AddCustomRect*** api), then the RGB pixels emitted will always be white (~75% of memory/bandwidth waste.
// Build pixels data. This is called automatically for you by the GetTexData*** functions.
pub fn Build(self: *FontAtlas, alltor: *Allocator) !void {
assert(!self.Locked); // "Cannot modify a locked ImFontAtlas between NewFrame() and EndFrame/Render()!");
return FontAtlasBuildWithStbTruetype(self, alltor);
}
// 1 byte per-pixel
pub fn GetTexDataAsAlpha8(
self: *FontAtlas,
out_pixels: ?*[]const u8,
out_width: *usize, out_height: *usize,
out_bytes_per_pixel: ?*usize,
alltor: *Allocator
) void {
// Build atlas on demand
if (self.TexPixelsAlpha8 == null) {
if (self.ConfigData.items.len == 0)
_ = self.AddFontDefault(null);
try self.Build(alltor);
}
out_pixels.? = self.TexPixelsAlpha8.?;
if (out_width != null) out_width.? = self.TexWidth;
if (out_height != null) out_height.? = self.TexHeight;
if (out_bytes_per_pixel != null) out_bytes_per_pixel.? = 1;
}
// 4 bytes-per-pixel
pub fn GetTexDataAsRGBA32(out_pixels: ?*[]const u8, out_width: *usize, out_height: *usize, out_bytes_per_pixel: ?*usize) void {
// TODO
}
pub fn IsBuilt() bool {
return Fonts.Size > 0 and (TexPixelsAlpha8 != null or TexPixelsRGBA32 != null);
}
pub fn SetTexID(id: TextureID) void {
TexID = id;
}
//-------------------------------------------
// Glyph Ranges
//-------------------------------------------
// Helpers to retrieve list of common Unicode ranges (2 values per range, values are inclusive, zero-terminated list)
// NB: Make sure that your string are UTF-8 and NOT in your local code page. In C++11, you can create UTF-8 string literal using the u8"Hello world" syntax. See FAQ for details.
// NB: Consider using ImFontGlyphRangesBuilder to build glyph ranges from textual data.
// Basic Latin, Extended Latin
pub fn GetGlyphRangesDefault() []const GlyphRange {
return []const GlyphRange {
.{ .start = 0x0020, .end = 0x00FF }, // Basic Latin + Latin Supplement
};
}
// Default + Korean characters
pub fn GetGlyphRangesKorean() [:0]UChar {}
// Default + Hiragana, Katakana, Half-Width, Selection of 1946 Ideographs
pub fn GetGlyphRangesJapanese() [:0]UChar {}
// Default + Half-Width + Japanese Hiragana/Katakana + full set of about 21000 CJK Unified Ideographs
pub fn GetGlyphRangesChineseFull() [:0]UChar {}
// Default + Half-Width + Japanese Hiragana/Katakana + set of 2500 CJK Unified Ideographs for common simplified Chinese
pub fn GetGlyphRangesChineseSimplifiedCommon() [:0]UChar {}
// Default + about 400 Cyrillic characters
pub fn GetGlyphRangesCyrillic() [:0]UChar {}
// Default + Thai characters
pub fn GetGlyphRangesThai() [:0]UChar {}
// Default + Vietnamese characters
pub fn GetGlyphRangesVietnamese() [:0]UChar {}
//-------------------------------------------
// [BETA] Custom Rectangles/Glyphs API
//-------------------------------------------
// You can request arbitrary rectangles to be packed into the atlas, for your own purposes.
// After calling Build(), you can query the rectangle position and render your pixels.
// You can also request your rectangles to be mapped as font glyph (given a font + Unicode point),
// so you can render e.g. custom colorful icons and use them as regular glyphs.
// Read docs/FONTS.txt for more details about using colorful icons.
// Note: this API may be redesigned later in order to support multi-monitor varying DPI settings.
pub fn AddCustomRectRegular(width, height: u32) i32 {
// TODO
}
pub fn AddCustomRectFontGlyph(font: Font, id: UChar, width, height: u32, advance_x: float, offset: ?Vec2) i32 {
// TODO
}
pub fn GetCustomRectByIndex(index: i32) FontAtlasCustomRect {
if (index < 0) return NULL;
return &CustomRects[index];
}
// [Internal]
pub fn CalcCustomRectUV(rect: FontAtlasCustomRect, out_uv_min: *Vec2, out_uv_max: *Vec2) void {
// TODO
}
pub fn GetMouseCursorTexData(cursor: MouseCursor, out_offset: *Vec2, out_size: *Vec2, out_uv_border: *[2]Vec2, out_uv_fill: *[2]Vec2) bool {
// TODO
}
//-------------------------------------------
// Members
//-------------------------------------------
Locked: bool, // Marked as Locked by ImGui::NewFrame() so attempt to modify the atlas will assert.
Flags: FontAtlasFlags, // Build flags (see FontAtlasFlags_)
TexID: usize, // User data to refer to the texture once it has been uploaded to user's graphic systems. It is passed back to you during rendering via the ImDrawCmd structure.
TexDesiredWidth: u32, // Texture width desired by user before Build(). Must be a power-of-two. If have many glyphs your graphics API have texture size restrictions you may want to increase texture width to decrease height.
TexGlyphPadding: c_int, // Padding between glyphs within texture in pixels. Defaults to 1. If your rendering method doesn't rely on bilinear filtering you may set this to 0.
// [Internal]
// NB: Access texture data via GetTexData*() calls! Which will setup a default font for you.
TexPixelsAlpha8: ?[]u8, // 1 component per pixel, each component is unsigned 8-bit. Total size = TexWidth * TexHeight
TexPixelsRGBA32: ?[]u32, // 4 component per pixel, each component is unsigned 8-bit. Total size = TexWidth * TexHeight * 4
TexWidth: u32, // Texture width calculated during Build().
TexHeight: u32, // Texture height calculated during Build().
TexUvScale: Vec2, // = (1.0f/TexWidth, 1.0f/TexHeight)
TexUvWhitePixel: Vec2, // Texture coordinates to a white pixel
Fonts: std.ArrayList(*Font), // Hold all the fonts returned by AddFont*. Fonts[0] is the default font upon calling ImGui::NewFrame(), use ImGui::PushFont()/PopFont() to change the current font.
CustomRects: std.ArrayList(FontAtlasCustomRect), // Rectangles for packing custom texture data into the atlas.
ConfigData: std.ArrayList(FontConfig), // Internal data
CustomRectIds: [1]i32, // Identifiers of custom texture rectangle used by ImFontAtlas/ImDrawList
};
pub const Font = struct {
// Members: Hot ~20/24 bytes (for CalcTextSize)
IndexAdvanceX: std.ArrayList(f32), // 12-16 // out // // Sparse. Glyphs->AdvanceX in a directly indexable way (cache-friendly for CalcTextSize functions which only this this info, and are often bottleneck in large UI).
FallbackAdvanceX: f32, // 4 // out // = FallbackGlyph->AdvanceX
FontSize: f32, // 4 // in // // Height of characters/line, set during loading (don't change after loading)
// Members: Hot ~36/48 bytes (for CalcTextSize + render loop)
IndexLookup: std.ArrayList(UChar), // 12-16 // out // // Sparse. Index glyphs by Unicode code-point.
Glyphs: std.ArrayList(FontGlyph), // 12-16 // out // // All glyphs.
FallbackGlyph: *FontGlyph, // 4-8 // out // = FindGlyph(FontFallbackChar)
DisplayOffset: Vec2, // 8 // in // = (0,0) // Offset font rendering by xx pixels
// Members: Cold ~32/40 bytes
ContainerAtlas: *FontAtlas, // 4-8 // out // // What we has been loaded into
ConfigData: FontConfig, // 4-8 // in // // Pointer within ContainerAtlas->ConfigData
ConfigDataCount: u16, // 2 // in // ~ 1 // Number of ImFontConfig involved in creating this font. Bigger than 1 when merging multiple font sources into one ImFont.
FallbackChar: UChar, // 2 // in // = '?' // Replacement character if a glyph isn't found. Only set via SetFallbackChar()
EllipsisChar: UChar, // 2 // out // = -1 // Character used for ellipsis rendering.
DirtyLookupTables: bool, // 1 // out //
Scale: f32, // 4 // in // = 1.f // Base font scale, multiplied by the per-window font scale which you can adjust with SetWindowFontScale()
Ascent: f32, // 4+4 // out // // Ascent: distance from top to bottom of e.g. 'A' [0..FontSize]
Descent: f32,
MetricsTotalSurface: u32, // 4 // out // // Total surface in pixels to get an idea of the font rasterization/texture cost (not exact, we approximate the cost of padding between glyphs)
Used4kPagesMap: [(UNICODE_CODEPOINT_MAX + 1) / 4096 / 8]u8, // 2 bytes if ImWchar=ImWchar16, 34 bytes if ImWchar==ImWchar32. Store 1-bit for each block of 4K codepoints that has one active glyph. This is mainly used to facilitate iterations across all used codepoints.
// Methods
// TODO IMGUI_API ImFont();
// TODO: IMGUI_API ~ImFont();
pub fn FindGlyph(self: Font, c: UChar) *FontGlyph {
// TODO
}
pub fn FindGlyphNoFallback(self: Font, c: UChar) *FontGlyph {
// TODO
}
pub fn GetCharAdvance(self: Font, c: UChar) f32 {
return if (c < IndexAdvanceX.Size) IndexAdvanceX[c] else FallbackAdvanceX;
}
pub fn IsLoaded(self: Font) bool {
return ContainerAtlas != null;
}
pub fn GetDebugName(self: Font) [_]u8 {
return if (ConfigData) ConfigData.Name else "<unknown>";
}
// 'max_width' stops rendering after a certain width (could be turned into a 2d size). FLT_MAX to disable.
// 'wrap_width' enable automatic word-wrapping across multiple lines to fit into given width. 0.0f to disable.
// utf8
pub fn CalcTextSizeA(size, max_width, wrap_width: f32, text: []const u8) struct { size: Vec2, remaining: []const u8 } {
// TODO
}
pub fn CalcWordWrapPositionA(scale: f32, text: []const u8, wrap_width: f32) []const u8 {
// TODO
}
pub fn RenderChar(draw_list: DrawList, size: f32, pos: Vec2, col: u32, c: UChar) void {
// TODO
}
pub fn RenderText(draw_list: DrawList, size: f32, pos: Vec2, col: u32, clip_rect: Vec4, text: []const u8, wrap_width: ?f32, cpu_fine_clip: ?bool) void {
// TODO
}
// [Internal] Don't use!
fn BuildLookupTable() void {
// TODO
}
fn ClearOutputData() void {
// TODO
}
fn GrowIndex(new_size: u32) void {
// TODO
}
fn AddGlyph(self: *Font, codepoint: UChar, x0: f32, y0: f32, x1: f32, y1: f32, _u0: f32, v0: f32, _u1: f32, v1: f32, advance_x: f32) void {
_ = try self.Glyphs.resize(self.Glyphs.items.len + 1);
//// C code, not yet adapted:
//ImFontGlyph& glyph = self.Glyphs.back();
//glyph.Codepoint = codepoint;
//glyph.Visible = (x0 != x1) and (y0 != y1);
//glyph.X0 = x0;
//glyph.Y0 = y0;
//glyph.X1 = x1;
//glyph.Y1 = y1;
//glyph.U0 = u0;
//glyph.V0 = v0;
//glyph.U1 = u1;
//glyph.V1 = v1;
//glyph.AdvanceX = advance_x + ConfigData.GlyphExtraSpacing.x; // Bake spacing into AdvanceX
//
//if (ConfigData.PixelSnapH)
// glyph.AdvanceX = std.math.round(glyph.AdvanceX);
//
//// Compute rough surface usage metrics (+1 to account for average padding, +0.99 to round)
//self.DirtyLookupTables = true;
//self.MetricsTotalSurface += @floatToInt(u32, (glyph.U1 - glyph.U0) * self.ContainerAtlas.TexWidth + 1.99) *
// @floatToInt(u32, (glyph.V1 - glyph.V0) * self.ContainerAtlas.TexHeight + 1.99);
}
fn AddRemapChar(dst, src: UChar, overwrite_dst: ?bool) void {
// TODO
}
fn SetGlyphVisible(c: UChar, visible: bool) void {
// TODO
}
fn IsGlyphRangeUnused(c_begin, c_end: UChar) bool {
// TODO
}
};
pub const DrawListFlags = enum(u3) {
None = 0,
AntiAliasedLines = 1 << 0, // Lines are anti-aliased (*2 the number of triangles for 1.0f wide line, otherwise *3 the number of triangles)
AntiAliasedFill = 1 << 1, // Filled shapes have anti-aliased edges (*2 the number of vertices)
AllowVtxOffset = 1 << 2, // Can emit 'VtxOffset > 0' to allow large meshes. Set when 'ImGuiBackendFlags_RendererHasVtxOffset' is enabled.
};
pub const DrawListSharedData = struct {
TexUvWhitePixel: Vec2, // UV of white pixel in the atlas
Font: ?Font, // Current/default font size (optional, for simplified AddText overload)
FontSize: f32, // Current/default font (optional, for simplified AddText overload)
CurveTessellationTol: f32, // Tessellation tolerance when using PathBezierCurveTo()
CircleSegmentMaxError: f32, // Number of circle segments to use per pixel of radius for AddCircle() etc
ClipRectFullscreen: Vec4, // Value for PushClipRectFullscreen()
InitialFlags: DrawListFlags, // Initial flags at the beginning of the frame (it is possible to alter flags on a per-drawlist basis afterwards)
// [Internal] Lookup tables
ArcFastVtx: [12 * ARCFAST_TESSELLATION_MULTIPLIER]Vec2, // FIXME: Bake rounded corners fill/borders in atlas
CircleSegmentCounts: [64]u8, // Precomputed segment count for given radius (array index + 1) before we calculate it dynamically (to avoid calculation overhead)
pub fn init() DrawListSharedData {
var data = DrawListSharedData{
.TexUvWhitePixel = .{ .x = 0, .y = 0 },
.Font = null,
.FontSize = 0.0,
.CurveTessellationTol = 0.0,
.CircleSegmentMaxError = 0.0,
.ClipRectFullscreen = Vec4{ .x = -8192, .y = -8192, .z = 8192, .w = 8192 },
.InitialFlags = DrawListFlags.None, // TODO: Zig-style enum
.ArcFastVtx = undefined,
.CircleSegmentCounts = [_]u8{0} ** 64,
};
for (data.ArcFastVtx) |*vtx, i| {
const a: f32 = @intToFloat(f32, i) * 2 * std.math.pi / @intToFloat(f32, data.ArcFastVtx.len);
vtx.* = Vec2{ .x = std.math.cos(a), .y = std.math.sin(a) };
}
return data;
}
pub fn SetCircleSegmentMaxError(max_error: f32) void {}
};
// Register default custom rectangles (this is called/shared by both the stb_truetype and the FreeType builder)
fn FontAtlasBuildInit(atlas: FontAtlas) void {
if (atlas.CustomRectIds[0] < 0) {
if ((atlas.Flags & FontAtlas.Flags.NoMouseCursors) != 0)
atlas.CustomRectIds[0] = atlas.AddCustomRectRegular(FONT_ATLAS_DEFAULT_TEX_DATA_W_HALF*2+1, FONT_ATLAS_DEFAULT_TEX_DATA_H)
else
atlas.CustomRectIds[0] = atlas.AddCustomRectRegular(2, 2);
}
}
fn FontAtlasBuildWithStbTruetype(atlas: *FontAtlas, alltor: *Allocator) !void {
// Temporary data for one source font (multiple source fonts can be merged into one destination ImFont)
// (C++03 doesn't allow instancing ImVector<> with function-local types so we declare the type here.)
const FontBuildSrcData = struct {
FontInfo: c.stbtt_fontinfo,
PackRange: c.stbtt_pack_range, // Hold the list of codepoints to pack (essentially points to Codepoints.Data)
Rects: []c.stbrp_rect, // Rectangles to pack. We first fill in their sizes and the packer will give us their position.
PackedChars: []c.stbtt_packedchar, // Output glyphs
SrcRanges: []const GlyphRange, // Ranges as requested by user (user is allowed to request too much, e.g. 0x0020..0xFFFF)
DstIndex: i32, // Index into atlas->Fonts[] and dst_tmp_array[]
GlyphsHighest: usize, // Highest requested codepoint
GlyphsCount: usize, // Glyph count (excluding missing glyphs and glyphs already set by an earlier source font)
GlyphsSet: std.ArrayList(u1), // Glyph bit map (random access, 1-bit per codepoint. This will be a maximum of 8KB)
GlyphsList: std.ArrayList(c_int), // Glyph codepoints list (flattened version of GlyphsMap)
};
// Temporary data for one destination ImFont* (multiple source fonts can be merged into one destination ImFont)
const FontBuildDstData = struct {
SrcCount: i32, // Number of source fonts targeting this destination font.
GlyphsHighest: usize,
GlyphsCount: usize,
GlyphsSet: std.ArrayList(u1), // This is used to resolve collision when multiple sources are merged into a same destination font.
};
assert(atlas.ConfigData.items.len > 0);
FontAtlasBuildInit(atlas.*);
// Clear atlas
atlas.TexID = 0;
atlas.TexWidth = 0;
atlas.TexHeight = 0;
atlas.TexUvScale = Vec2{ .x = 0, .y = 0 };
atlas.TexUvWhitePixel = Vec2{ .x = 0, .y = 0 };
atlas.ClearTexData();
// Temporary storage for building
var src_tmp_array = std.ArrayList(FontBuildSrcData).init(alltor);
var dst_tmp_array = std.ArrayList(FontBuildDstData).init(alltor);
_ = try src_tmp_array.resize(atlas.ConfigData.items.len);
_ = try dst_tmp_array.resize(atlas.Fonts.items.len);
for (src_tmp_array.items) |*item| item.* = std.mem.zeroes(FontBuildSrcData);
for (dst_tmp_array.items) |*item| item.* = std.mem.zeroes(FontBuildDstData);
// 1. Initialize font loading structure, check font data validity
for (atlas.ConfigData.items) |cfg, src_i| {
var src_tmp = &src_tmp_array.items[src_i];
assert(cfg.DstFont != null and (!cfg.DstFont.?.IsLoaded() or cfg.DstFont.?.ContainerAtlas == atlas));
// Find index from cfg.DstFont (we allow the user to set cfg.DstFont. Also it makes casual debugging nicer than when storing indices)
src_tmp.DstIndex = -1;
for (atlas.Fonts.items) |font, index| {
if (cfg.DstFont == font) src_tmp.DstIndex = @intCast(i32, index);
}
assert(src_tmp.DstIndex != -1); // cfg.DstFont not pointing within atlas->Fonts[] array?
if (src_tmp.DstIndex == -1) return FontAtlas.Error.FontNotContainedInAtlas;
// Initialize helper structure for font loading and verify that the TTF/OTF data is correct
const font_offset = c.stbtt_GetFontOffsetForIndex(&cfg.FontData[0], cfg.FontNo);
assert(font_offset >= 0); // "FontData is incorrect, or FontNo cannot be found."
if (c.stbtt_InitFont(&src_tmp.FontInfo, &cfg.FontData[0], font_offset) == 0)
return FontAtlas.Error.InitFontFailed;
// Measure highest codepoints
var dst_tmp = &dst_tmp_array.items[@intCast(usize, src_tmp.DstIndex)];
src_tmp.SrcRanges = if (cfg.GlyphRanges != null) cfg.GlyphRanges.? else FontAtlas.GetGlyphRangesDefault();
for (src_tmp.SrcRanges) |range| {
if (range.end > src_tmp.GlyphsHighest) src_tmp.GlyphsHighest = range.end;
}
dst_tmp.SrcCount += 1;
dst_tmp.GlyphsHighest = std.math.max(dst_tmp.GlyphsHighest, src_tmp.GlyphsHighest);
}
// 2. For every requested codepoint, check for their presence in the font data, and handle redundancy or overlaps between source fonts to avoid unused glyphs.
var total_glyphs_count: usize = 0;
for (src_tmp_array.items) |*src_tmp, src_i| {
var dst_tmp = &dst_tmp_array.items[@intCast(usize, src_tmp.DstIndex)];
_ = try src_tmp.GlyphsSet.resize(src_tmp.GlyphsHighest + 1);
if (dst_tmp.GlyphsSet.items.len == 0)
_ = try dst_tmp.GlyphsSet.resize(dst_tmp.GlyphsHighest + 1);
for (src_tmp.SrcRanges) |range, i| {
var codepoint: UChar = 0;
while (codepoint < range.end) {
if (dst_tmp.GlyphsSet.items[codepoint] != 0) // Don't overwrite existing glyphs. We could make this an option for MergeMode (e.g. MergeOverwrite==true)
continue;
if (c.stbtt_FindGlyphIndex(&src_tmp.FontInfo, codepoint) != 0) // It is actually in the font?
continue;
// Add to avail set/counters
src_tmp.GlyphsCount += 1;
dst_tmp.GlyphsCount += 1;
src_tmp.GlyphsSet.items[codepoint] = 1;
dst_tmp.GlyphsSet.items[codepoint] = 1;
total_glyphs_count += 1;
codepoint += 1;
}
}
}
// 3. Unpack our bit map into a flat list (we now have all the Unicode points that we know are requested _and_ available _and_ not overlapping another)
for (src_tmp_array.items) |*src_tmp| {
try src_tmp.GlyphsList.ensureCapacity(@intCast(usize, src_tmp.GlyphsCount));
UnpackBitVectorToFlatIndexList(src_tmp.GlyphsSet, &src_tmp.GlyphsList);
try src_tmp.GlyphsSet.resize(0);
assert(src_tmp.GlyphsList.items.len == src_tmp.GlyphsCount);
}
for (dst_tmp_array.items) |*dst_tmp| try dst_tmp.GlyphsSet.resize(0);
try dst_tmp_array.resize(0);
// Allocate packing character data and flag packed characters buffer as non-packed (x0=y0=x1=y1=0)
// (We technically don't need to zero-clear buf_rects, but let's do it for the sake of sanity)
var buf_rects = std.ArrayList(c.stbrp_rect).init(alltor);
var buf_packedchars = std.ArrayList(c.stbtt_packedchar).init(alltor);
try buf_rects.resize(total_glyphs_count);
try buf_packedchars.resize(total_glyphs_count);
for (buf_rects.items) |*br| br.* = std.mem.zeroes(c.stbrp_rect);
for (buf_packedchars.items) |*bpc| bpc.* = std.mem.zeroes(c.stbtt_packedchar);
// 4. Gather glyphs sizes so we can pack them in our virtual canvas.
var total_surface: f32 = 0;
var buf_rects_out_n: usize = 0;
var buf_packedchars_out_n: usize = 0;
for (src_tmp_array.items) |*src_tmp, src_i| {
if (src_tmp.GlyphsCount == 0) continue;
src_tmp.Rects = buf_rects.items[buf_rects_out_n .. buf_rects_out_n + src_tmp.GlyphsList.items.len];
src_tmp.PackedChars = buf_packedchars.items[buf_packedchars_out_n .. buf_packedchars_out_n + src_tmp.GlyphsCount];
buf_rects_out_n += src_tmp.GlyphsCount;
buf_packedchars_out_n += src_tmp.GlyphsCount;
// Convert our ranges in the format stb_truetype wants
const cfg = &atlas.ConfigData.items[src_i];
src_tmp.PackRange.font_size = cfg.SizePixels;
src_tmp.PackRange.first_unicode_codepoint_in_range = 0;
src_tmp.PackRange.array_of_unicode_codepoints = src_tmp.GlyphsList.items.ptr;
src_tmp.PackRange.num_chars = @intCast(c_int, src_tmp.GlyphsList.items.len);
src_tmp.PackRange.chardata_for_range = &src_tmp.PackedChars[0];
src_tmp.PackRange.h_oversample = cfg.OversampleH;
src_tmp.PackRange.v_oversample = cfg.OversampleV;
// Gather the sizes of all rectangles we will need to pack (this loop is based on stbtt_PackFontRangesGatherRects)
const scale = if (cfg.SizePixels > 0) c.stbtt_ScaleForPixelHeight(&src_tmp.FontInfo, cfg.SizePixels) else c.stbtt_ScaleForMappingEmToPixels(&src_tmp.FontInfo, -cfg.SizePixels);
const padding = atlas.TexGlyphPadding;
for (src_tmp.GlyphsList.items) |glyph, glyph_i| {
var x0: c_int = 0; var y0: c_int = 0; var x1: c_int = 0; var y1: c_int = 0;
const glyph_index_in_font = c.stbtt_FindGlyphIndex(&src_tmp.FontInfo, src_tmp.GlyphsList.items[glyph_i]);
assert(glyph_index_in_font != 0);
c.stbtt_GetGlyphBitmapBoxSubpixel(&src_tmp.FontInfo, glyph_index_in_font,
std.math.floor(scale * @intToFloat(f32, cfg.OversampleH)), std.math.floor(scale * @intToFloat(f32, cfg.OversampleV)), 0, 0,
&x0, &y0, &x1, &y1);
src_tmp.Rects[glyph_i].w = @intCast(c_ushort, x1 - x0 + padding + @intCast(c_int, cfg.OversampleH) - 1);
src_tmp.Rects[glyph_i].h = @intCast(c_ushort, y1 - y0 + padding + @intCast(c_int, cfg.OversampleV) - 1);
total_surface += @intToFloat(f32, src_tmp.Rects[glyph_i].w * src_tmp.Rects[glyph_i].h);
}
}
// We need a width for the skyline algorithm, any width!
// The exact width doesn't really matter much, but some API/GPU have texture size limitations and increasing width can decrease height.
// User can override TexDesiredWidth and TexGlyphPadding if they wish, otherwise we use a simple heuristic to select the width based on expected surface.
const surface_sqrt: f32 = std.math.sqrt(total_surface) + 1;
atlas.TexHeight = 0;
if (atlas.TexDesiredWidth > 0)
atlas.TexWidth = atlas.TexDesiredWidth
else
atlas.TexWidth = if (surface_sqrt >= 4096*0.7) @as(u32, 4096)
else if (surface_sqrt >= 2048*0.7) @as(u32, 2048)
else if (surface_sqrt >= 1024*0.7) @as(u32, 1024)
else @as(u32, 512);
// 5. Start packing
// Pack our extra data rectangles first, so it will be on the upper-left corner of our texture (UV will have small values).
const TEX_HEIGHT_MAX = 1024 * 32;
var spc: c.stbtt_pack_context = std.mem.zeroes(c.stbtt_pack_context);
_ = c.stbtt_PackBegin(&spc, null, @intCast(c_int, atlas.TexWidth), TEX_HEIGHT_MAX, 0, atlas.TexGlyphPadding, null);
FontAtlasBuildPackCustomRects(atlas, spc.pack_info);
// 6. Pack each source font. No rendering yet, we are working with rectangles in an infinitely tall texture at this point.
for (src_tmp_array.items) |src_tmp| {
if (src_tmp.GlyphsCount == 0) continue;
_ = c.stbrp_pack_rects(
@ptrCast(*c.stbrp_context, @alignCast(@alignOf(*c.stbrp_context), spc.pack_info)),
&src_tmp.Rects[0], @intCast(c_int, src_tmp.GlyphsCount));
// Extend texture height and mark missing glyphs as non-packed so we won't render them.
// FIXME: We are not handling packing failure here (would happen if we got off TEX_HEIGHT_MAX or if a single if larger than TexWidth?)
for (src_tmp.Rects) |rect| {
if (rect.was_packed != 0)
atlas.TexHeight = std.math.max(atlas.TexHeight, rect.y + rect.h);
}
}
// 7. Allocate texture
atlas.TexHeight = if (atlas.Flags.NoPowerOfTwoHeight) (atlas.TexHeight + 1) else try std.math.ceilPowerOfTwo(u32, atlas.TexHeight);
atlas.TexUvScale = Vec2{ .x = 1.0 / @intToFloat(f32, atlas.TexWidth), .y = 1.0 / @intToFloat(f32, atlas.TexHeight) };
atlas.TexPixelsAlpha8 = try alltor.alloc(u8, atlas.TexWidth * atlas.TexHeight);
for (atlas.TexPixelsAlpha8.?) |*px| px.* = 0;
// memset(atlas->TexPixelsAlpha8, 0, atlas->TexWidth * atlas->TexHeight);
spc.pixels = atlas.TexPixelsAlpha8.?.ptr;
spc.height = @intCast(c_int, atlas.TexHeight);
// 8. Render/rasterize font characters into the texture
for (src_tmp_array.items) |*src_tmp, src_i| {
if (src_tmp.GlyphsCount == 0) continue;
var cfg = &atlas.ConfigData.items[src_i];
_ = c.stbtt_PackFontRangesRenderIntoRects(&spc, &src_tmp.FontInfo, &src_tmp.PackRange, 1, src_tmp.Rects.ptr);
// Apply multiply operator
if (cfg.RasterizerMultiply != 1.0) {
var multiply_table: [256]u8 = undefined;
FontAtlasBuildMultiplyCalcLookupTable(&multiply_table, cfg.RasterizerMultiply);
var glyph_i: usize = 0;
while (glyph_i < src_tmp.GlyphsCount) {
const r = &src_tmp.Rects[glyph_i];
if (r.was_packed != 0)
FontAtlasBuildMultiplyRectAlpha8(multiply_table, atlas.TexPixelsAlpha8.?,
r.x, r.y, r.w, r.h, atlas.TexWidth * 1);
glyph_i += 1;
}
}
src_tmp.Rects.len = 0;
}
// End packing
c.stbtt_PackEnd(&spc);
try buf_rects.resize(0);
// 9. Setup ImFont and glyphs for runtime
for (src_tmp_array.items) |src_tmp, src_i| {
if (src_tmp.GlyphsCount == 0) continue;
var cfg = &atlas.ConfigData.items[src_i];
var dst_font = cfg.DstFont.?; // We can have multiple input fonts writing into a same destination font (when using MergeMode=true)
const font_scale = c.stbtt_ScaleForPixelHeight(&src_tmp.FontInfo, cfg.SizePixels);
var unscaled_ascent: c_int = 0; var unscaled_descent: c_int = 0; var unscaled_line_gap: c_int = 0;
c.stbtt_GetFontVMetrics(&src_tmp.FontInfo, &unscaled_ascent, &unscaled_descent, &unscaled_line_gap);
const ascent : f32 = std.math.floor(@intToFloat(f32, unscaled_ascent ) * font_scale +
@as(f32, if (unscaled_ascent > 0) 1.0 else -1.0));
const descent: f32 = std.math.floor(@intToFloat(f32, unscaled_descent) * font_scale +
@as(f32, if (unscaled_descent > 0) 1.0 else -1.0));
FontAtlasBuildSetupFont(atlas.*, dst_font, cfg.*, ascent, descent);
const font_off_x = cfg.GlyphOffset.x;
const font_off_y = cfg.GlyphOffset.y + std.math.round(dst_font.Ascent);
var glyph_i: usize = 0;
while (glyph_i < src_tmp.GlyphsCount) {
const codepoint = @intCast(UChar, src_tmp.GlyphsList.items[glyph_i]);
const pc = &src_tmp.PackedChars[glyph_i];
const char_advance_x_org = pc.xadvance;
const char_advance_x_mod = std.math.clamp(char_advance_x_org, cfg.GlyphMinAdvanceX, cfg.GlyphMaxAdvanceX);
var char_off_x = font_off_x;
if (char_advance_x_org != char_advance_x_mod)
char_off_x += if (cfg.PixelSnapH)
std.math.floor((char_advance_x_mod - char_advance_x_org) * 0.5)
else (char_advance_x_mod - char_advance_x_org) * 0.5;
// Register glyph
var q: c.stbtt_aligned_quad = undefined;
var dummy_x: f32 = 0.0; var dummy_y: f32 = 0.0;
c.stbtt_GetPackedQuad(src_tmp.PackedChars.ptr, @intCast(c_int, atlas.TexWidth), @intCast(c_int, atlas.TexHeight), @intCast(c_int, glyph_i),
&dummy_x, &dummy_y, &q, 0);
dst_font.AddGlyph(codepoint, q.x0 + char_off_x, q.y0 + font_off_y, q.x1 + char_off_x, q.y1 + font_off_y, q.s0, q.t0, q.s1, q.t1, char_advance_x_mod);
glyph_i += 1;
}
}
// Cleanup temporary (ImVector doesn't honor destructor)
// for (int src_i = 0; src_i < src_tmp_array.Size; src_i++)
// src_tmp_array[src_i].~ImFontBuildSrcData();
FontAtlasBuildFinish(atlas);
}
// TODO: bit-packed version of ArrayList(u1)!
fn UnpackBitVectorToFlatIndexList(in: std.ArrayList(u1), out: *std.ArrayList(c_int)) void {
for (in.items) |on, i| {
if (on != 0) out.append(@intCast(c_int, i));
}
}
fn FontAtlasBuildPackCustomRects(atlas: *FontAtlas, stbrp_context_opaque: *c_void) void {
var pack_context = @ptrCast(c.stbrp_context, stbrp_context_opaque);
assert(pack_context != null);
var user_rects = atlas.CustomRects;
assert(user_rects.items.len >= 1); // We expect at least the default custom rects to be registered, else something went wrong.
var pack_rects = std.ArrayList(c.stbrp_rect);
pack_rects.resize(user_rects.items.len);
for (pack_rects) |*rect| rect.* = std.mem.zeroes(c.stbrp_rect);
var i = 0;
while (i < user_rects.items.len) {
pack_rects[i].w = user_rects[i].Width;
pack_rects[i].h = user_rects[i].Height;
i += 1;
}
c.stbrp_pack_rects(pack_context, &pack_rects.items[0], pack_rects.items.len);
i = 0;
while (i < pack_rects.items.len) {
if (pack_rects[i].was_packed) {
user_rects[i].X = pack_rects[i].x;
user_rects[i].Y = pack_rects[i].y;
assert(pack_rects[i].w == user_rects[i].Width and pack_rects[i].h == user_rects[i].Height);
atlas.TexHeight = std.math.max(atlas.TexHeight, pack_rects[i].y + pack_rects[i].h);
}
}
}
fn FontAtlasBuildMultiplyCalcLookupTable(out_table: *[256]u8, in_brighten_factor: f32) void {
for (out_table) |*value| {
var new_val: f32 = @intToFloat(f32, value.*) * in_brighten_factor;
value.* = if (new_val > 255) 255 else @floatToInt(u8, new_val);
}
}
fn FontAtlasBuildMultiplyRectAlpha8(table: [256]u8, pixels: []u8, x: i32, y: i32, w: i32, h: i32, stride: u32) void {
var data = pixels[@intCast(usize, x + y * stride) ..];
var j = h;
while (j > 0) {
var i = 0;
while (i < w) {
data[i] = table[data[i]];
i += 1;
}
data += stride;
j -= 1;
}
}
fn FontAtlasBuildSetupFont(atlas: FontAtlas, font: *Font, font_config: FontConfig, ascent: f32, descent: f32) void {
if (!font_config.MergeMode) {
font.ClearOutputData();
font.FontSize = font_config.SizePixels;
font.ConfigData = font_config;
font.ContainerAtlas = atlas;
font.Ascent = ascent;
font.Descent = descent;
}
font.ConfigDataCount += 1;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment