Files
imgui-luau/imgui/imstb_truetype.lua
T
2026-10-07 13:03:05 -05:00

2467 lines
81 KiB
Lua

if _G.__IMGUI_ENV then setfenv(1, _G.__IMGUI_ENV) end -- [Roblox] shared env, see init.lua
--- ImGui Sincerely
-- This is a Lua port of original `imstb_truetype.h`
--- STBTT_RASTERIZER_VERSION == 2
local stbtt_fontinfo
local stbtt_InitFont
local stbtt_MakeGlyphBitmapSubpixelPrefilter
local stbtt_GetFontVMetrics
local stbtt__GetGlyphInfoT2
local stbtt__GetGlyphShapeT2
local stbtt_GetGlyphShape
local stbtt_GetGlyphBitmapBox
local stbtt_GetGlyphBitmapBoxSubpixel
local stbtt_GetGlyphHMetrics
local stbtt_GetFontOffsetForIndex
local stbtt_ScaleForPixelHeight
local stbtt_FindGlyphIndex
local STBTT_MAX_OVERSAMPLE = 8
--- @enum STBTT_PLATFORM_ID
local STBTT_PLATFORM_ID = {
UNICODE = 0,
MAC = 1,
ISO = 2,
MICROSOFT = 3,
}
--- @enum STBTT_MS_EID
local STBTT_MS_EID = {
SYMBOL = 0,
UNICODE_BMP = 1,
SHIFTJIS = 2,
UNICODE_FULL = 10
}
-- original enum doesn't have a name, so we name it
--- @enum STBTT_VTX
local STBTT_VTX = {
MOVE = 1,
LINE = 2,
CURVE = 3,
CUBIC = 4
}
local bit32 = bit32
local math = math
local STBTT_assert = assert
local STBTT_sort = table.sort
local STBTT_sqrt = math.sqrt
local STBTT_fabs = math.abs
local STBTT_ifloor = math.floor
local STBTT_iceil = math.ceil
local trunc = function(x)
if x >= 0 then
return math.floor(x)
else
return math.ceil(x)
end
end
local function STBTT__NOTUSED(_) return end
--- @alias stbtt_int32 int
local stbtt_int32 = ImS32
local stbtt_uint32 = ImU32
local stbtt_int16 = ImS16
local stbtt_uint16 = ImU16
local stbtt_int8 = ImS8
local stbtt_uint8 = ImU8
local unsigned_char = stbtt_uint8
--- @class stbtt__buf
--- @field data? table # 1-based byte table
--- @field offset int # Every access to `data` will be offset by this
--- @field cursor int # >= 0
--- @field size int
--- @param initial_offset? int
--- @return stbtt__buf
--- @nodiscard
local function stbtt__buf(initial_offset)
return {
data = nil,
offset = initial_offset or 0,
cursor = 0,
size = nil
}
end
--- @class stbtt_fontinfo
--- @field userdata any
--- @field data table # pointer to .ttf file
--- @field fontstart int # offset of start of font
--- @field numGlyphs int # number of glyphs, needed for range checking
--- @field loca int # table location as offset from start of .ttf
--- @field head int # table location as offset from start of .ttf
--- @field glyf int # table location as offset from start of .ttf
--- @field hhea int # table location as offset from start of .ttf
--- @field hmtx int # table location as offset from start of .ttf
--- @field kern int # table location as offset from start of .ttf
--- @field gpos int # table location as offset from start of .ttf
--- @field svg int # table location as offset from start of .ttf
--- @field index_map int # a cmap mapping for our chosen character encoding
--- @field indexToLocFormat int # format needed to map from glyph index to glyph
--- @field cff stbtt__buf # cff font data
--- @field charstrings stbtt__buf # the charstring index
--- @field gsubrs stbtt__buf # global charstring subroutines index
--- @field subrs stbtt__buf # private charstring subroutines index
--- @field fontdicts stbtt__buf # array of font dicts
--- @field fdselect stbtt__buf # map from glyph to fontdict
--- @return stbtt_fontinfo
function stbtt_fontinfo()
return {
data = nil,
fontstart = nil,
numGlyphs = nil,
loca = nil,
head = nil,
glyf = nil,
hhea = nil,
hmtx = nil,
kern = nil,
gpos = nil,
svg = nil,
index_map = nil,
indexToLocFormat = nil,
cff = nil,
charstrings = nil,
gsubrs = nil,
subrs = nil,
fontdicts = nil,
fdselect = nil
}
end
--- @class stbtt_vertex
--- @field x int
--- @field y int
--- @field cx int
--- @field cy int
--- @field cx1 int
--- @field cy1 int
--- @field type int
--- @field padding int
--- @return stbtt_vertex
--- @nodiscard
local function stbtt_vertex()
return {
x = nil,
y = nil,
cx = nil,
cy = nil,
cx1 = nil,
cy1 = nil,
type = nil,
padding = nil
}
end
--- @class stbtt__csctx
--- @field bounds bool
--- @field started bool
--- @field first_x float
--- @field first_y float
--- @field x float
--- @field y float
--- @field min_x int
--- @field max_x int
--- @field min_y int
--- @field max_y int
--- @field pvertices stbtt_vertex[]
--- @field num_vertices int
--- @return stbtt__csctx
--- @nodiscard
local function stbtt__csctx()
return {
bounds = false,
started = false,
first_x = nil,
first_y = nil,
x = nil,
y = nil,
min_x = nil,
max_x = nil,
min_y = nil,
max_y = nil,
pvertices = nil,
num_vertices = nil
}
end
--- @param bounds bool
--- @return stbtt__csctx
--- @nodiscard
local function STBTT__CSCTX_INIT(bounds)
local this = stbtt__csctx()
this.bounds = bounds
this.started = false
this.first_x = 0
this.first_y = 0
this.x = 0
this.y = 0
this.min_x = 0
this.max_x = 0
this.min_y = 0
this.max_y = 0
this.pvertices = nil
this.num_vertices = 0
return this
end
--- stbtt_kerningentry
--- stbtt_pack_range
--- stbtt_packedchar
--- @class stbtt__edge
--- @field x0 float
--- @field y0 float
--- @field x1 float
--- @field y1 float
--- @field invert bool
--- @return stbtt__edge
--- @nodiscard
local function stbtt__edge()
return {
x0 = nil,
y0 = nil,
x1 = nil,
y1 = nil,
invert = false
}
end
--- @class stbtt__active_edge
--- @return stbtt__active_edge
--- @nodiscard
local function stbtt__active_edge()
return {
next = nil,
fx = nil,
fdx = nil,
fdy = nil,
direction = nil,
sy = nil,
ey = nil
}
end
--- @return stbtt__active_edge
--- @nodiscard
local function stbtt__new_active(e, off_x, start_point)
local z = stbtt__active_edge()
local dxdy = (e.x1 - e.x0) / (e.y1 - e.y0)
z.fdx = dxdy
z.fdy = (dxdy ~= 0.0) and (1.0 / dxdy) or 0.0
z.fx = e.x0 + dxdy * (start_point - e.y0)
z.fx = z.fx - off_x
z.direction = (e.invert) and 1.0 or -1.0
z.sy = e.y0
z.ey = e.y1
z.next = nil
return z
end
--- @class stbtt__bitmap
--- @field w int
--- @field h int
--- @field stride int
--- @field pixels unsigned_char[]
--- @return stbtt__bitmap
--- @nodiscard
local function stbtt__bitmap()
return {
w = nil,
h = nil,
stride = nil,
pixels = nil
}
end
--- @class stbtt__point
--- @field x number
--- @field y number
--- @return stbtt__point
--- @nodiscard
local function stbtt__point() return { x = nil, y = nil } end
----------------------------------------------
----------------------------------------------
---
--- stbtt__buf helpers to parse data from file
---
---
--- @param b stbtt__buf
local function stbtt__buf_get8(b)
if b.cursor >= b.size then return 0 end
local result = b.data[b.offset + b.cursor + 1]
b.cursor = b.cursor + 1
return result
end
--- @param b stbtt__buf
local function stbtt__buf_peek8(b)
if b.cursor >= b.size then return 0 end
return b.data[b.offset + b.cursor + 1]
end
--- @param b stbtt__buf
--- @param o int
local function stbtt__buf_seek(b, o)
STBTT_assert(not (o > b.size or o < 0))
if o > b.size or o < 0 then
b.cursor = b.size
else
b.cursor = o
end
end
--- @param b stbtt__buf
--- @param o int
local function stbtt__buf_skip(b, o)
stbtt__buf_seek(b, b.cursor + o)
end
local function stbtt__buf_get(b, n)
local v = 0
STBTT_assert(n >= 1 and n <= 4)
for _ = 1, n do
v = bit32.bor(bit32.lshift(v, 8), stbtt__buf_get8(b))
end
return v
end
--- @param p? table
--- @param size int
--- @param offset? int
--- @return stbtt__buf
--- @nodiscard
local function stbtt__new_buf(p, size, offset)
local r = stbtt__buf(offset)
STBTT_assert(size < 0x40000000)
r.data = p
r.size = trunc(size)
r.cursor = 0
return r
end
--- @param b stbtt__buf
local function stbtt__buf_get16(b) return stbtt__buf_get(b, 2) end
--- @param b stbtt__buf
local function stbtt__buf_get32(b) return stbtt__buf_get(b, 4) end
--- @param b stbtt__buf
--- @param o int
--- @param s int
--- @return stbtt__buf
--- @nodiscard
local function stbtt__buf_range(b, o, s)
local r = stbtt__new_buf(nil, 0)
if (o < 0 or s < 0 or o > b.size or s > (b.size - o)) then
return r
end
r.data = b.data
r.offset = b.offset + o
r.size = s
return r
end
--- @param b stbtt__buf
local function stbtt__cff_get_index(b)
local count, start, offsize
start = b.cursor
count = stbtt__buf_get16(b)
if count > 0 then
offsize = stbtt__buf_get8(b)
STBTT_assert(offsize >= 1 and offsize <= 4)
stbtt__buf_skip(b, count * offsize)
stbtt__buf_skip(b, stbtt__buf_get(b, offsize) - 1)
end
return stbtt__buf_range(b, start, b.cursor - start)
end
--- @param b stbtt__buf
local function stbtt__cff_int(b)
local b0 = stbtt__buf_get8(b)
if b0 >= 32 and b0 <= 246 then return b0 - 139
elseif b0 >= 247 and b0 <= 250 then return (b0 - 247) * 256 + stbtt__buf_get8(b) + 108
elseif b0 >= 251 and b0 <= 254 then return -(b0 - 251) * 256 - stbtt__buf_get8(b) - 108
elseif b0 == 28 then return stbtt__buf_get16(b)
elseif b0 == 29 then return stbtt__buf_get32(b)
end
STBTT_assert(false)
return 0
end
--- @param b stbtt__buf
local function stbtt__cff_skip_operand(b)
local v
local b0 = stbtt__buf_peek8(b)
STBTT_assert(b0 >= 28)
if b0 == 30 then
stbtt__buf_skip(b, 1)
while b.cursor < b.size do
v = stbtt__buf_get8(b)
if (bit32.band(v, 0xF) == 0xF or bit32.rshift(v, 4) == 0xF) then
break
end
end
else
stbtt__cff_int(b)
end
end
--- @param b stbtt__buf
--- @param key int
--- @return stbtt__buf
--- @nodiscard
local function stbtt__dict_get(b, key)
stbtt__buf_seek(b, 0)
while b.cursor < b.size do
local start = b.cursor
local _end, op
while stbtt__buf_peek8(b) >= 28 do
stbtt__cff_skip_operand(b)
end
_end = b.cursor
op = stbtt__buf_get8(b)
if op == 12 then op = bit32.bor(stbtt__buf_get8(b), 0x100) end
if op == key then return stbtt__buf_range(b, start, _end - start) end
end
return stbtt__buf_range(b, 0, 0)
end
--- @param b stbtt__buf
--- @param key int
--- @param outcount int
--- @return int[]
--- @nodiscard
local function stbtt__dict_get_ints(b, key, outcount)
local operands = stbtt__dict_get(b, key)
local out = {}
for i = 1, outcount do
if operands.cursor >= operands.size then break end
out[i] = stbtt__cff_int(operands)
end
return out
end
--- @param b stbtt__buf
local function stbtt__cff_index_count(b)
stbtt__buf_seek(b, 0)
return stbtt__buf_get16(b)
end
--- @param b stbtt__buf
--- @param i int
--- @return stbtt__buf
--- @nodiscard
local function stbtt__cff_index_get(b, i)
local count, offsize, start, _end
stbtt__buf_seek(b, 0)
count = stbtt__buf_get16(b)
offsize = stbtt__buf_get8(b)
STBTT_assert(i >= 0 and i < count)
STBTT_assert(offsize >= 1 and offsize <= 4)
stbtt__buf_skip(b, i * offsize)
start = stbtt__buf_get(b, offsize)
_end = stbtt__buf_get(b, offsize)
return stbtt__buf_range(b, 2 + (count + 1) * offsize + start, _end - start)
end
-------------------------------------
-------------------------------------
---
--- Accessors to parse data from file
---
---
--- UNIMPLEMENTED / UNUSED:
--- stbtt_IsGlyphEmpty
--- stbtt_GetKerningTableLength
--- stbtt_GetKerningTable
--- stbtt__GetGlyphKernInfoAdvance
--- stbtt__GetCoverageIndex
--- stbtt__GetGlyphClass
--- stbtt__GetGlyphGPOSInfoAdvance
--- stbtt_GetGlyphKernAdvance
--- stbtt_FindSVGDoc
--- stbtt_GetGlyphSVG
--- stbtt_GetCodepointSVG
--- stbtt_GetCodepointKernAdvance
--- stbtt_GetFontVMetricsOS2
--- stbtt_GetCodepointHMetrics
--- stbtt_ScaleForMappingEmToPixels
--- stbtt_GetFontBoundingBox
--- stbtt_GetCodepointBitmapBox
--- stbtt_GetCodepointBitmapBoxSubpixel
--- stbtt_GetCodepointBox
--- stbtt_GetNumberOfFonts_internal
--- stbtt__get_svg
local function ttUSHORT(p, offset) return stbtt_uint16(p[offset + 1] * 256 + p[offset + 2]) end
local function ttSHORT(p, offset) return stbtt_int16(p[offset + 1] * 256 + p[offset + 2]) end
local function ttULONG(p, offset) return stbtt_uint32(bit32.lshift(p[offset + 1], 24) + bit32.lshift(p[offset + 2], 16) + bit32.lshift(p[offset + 3], 8) + p[offset + 4]) end
local function ttLONG(p, offset) return stbtt_int32(bit32.lshift(p[offset + 1], 24) + bit32.lshift(p[offset + 2], 16) + bit32.lshift(p[offset + 3], 8) + p[offset + 4]) end
local function ttBYTE(p, offset) return stbtt_uint8(p[offset + 1]) end
local function ttCHAR(p, offset) return stbtt_int8(p[offset + 1]) end
local function stbtt_tag4(p, c0, c1, c2, c3, offset) offset = offset or 0; return p[offset + 1] == c0 and p[offset + 2] == c1 and p[offset + 3] == c2 and p[offset + 4] == c3 end
local function stbtt_tag(p, str, offset) local c0, c1, c2, c3 = string.byte(str, 1, 4); return stbtt_tag4(p, c0, c1, c2, c3, offset) end
local function stbtt__isfont(font)
if stbtt_tag4(font, 49, 0, 0, 0) then return true end
if stbtt_tag(font, "typ1") then return true end
if stbtt_tag(font, "OTTO") then return true end
if stbtt_tag4(font, 0, 1, 0, 0) then return true end
if stbtt_tag(font, "true") then return true end
return false
end
local function stbtt__find_table(data, font_start, tag)
local num_tables = ttUSHORT(data, font_start + 4)
local tabledir = font_start + 12
for i = 0, num_tables - 1 do
local loc = tabledir + i * 16
if stbtt_tag(data, tag, loc + 0) then
return ttULONG(data, loc + 8)
end
end
return 0
end
local function stbtt_GetFontOffsetForIndex_internal(font_collection, index)
if stbtt__isfont(font_collection) then
if index == 0 then return 0 else return -1 end
end
if stbtt_tag(font_collection, "ttcf") then
if ttULONG(font_collection, 4) == 0x00010000 or ttULONG(font_collection, 4) == 0x00020000 then
local n = ttLONG(font_collection, 8)
if index >= n then
return -1
end
return ttULONG(font_collection, 12 + index * 4)
end
end
return -1
end
--- @param cff stbtt__buf
--- @param fontdict stbtt__buf
local function stbtt__get_subrs(cff, fontdict)
local private_loc = stbtt__dict_get_ints(fontdict, 18, 2)
if (private_loc[2] == 0 or private_loc[1] == 0) then
return stbtt__new_buf(nil, 0)
end
local pdict = stbtt__buf_range(cff, private_loc[2], private_loc[1])
local subrsoff = stbtt__dict_get_ints(pdict, 19, 1)
if subrsoff[1] == 0 then
return stbtt__new_buf(nil, 0)
end
stbtt__buf_seek(cff, private_loc[2] + subrsoff[1])
return stbtt__cff_get_index(cff)
end
--- @return bool
local function stbtt_InitFont_internal(info, data, fontstart)
local cmap
local numTables
info.data = data
info.fontstart = fontstart
info.cff = stbtt__new_buf(nil, 0)
cmap = stbtt__find_table(data, fontstart, "cmap")
info.loca = stbtt__find_table(data, fontstart, "loca")
info.head = stbtt__find_table(data, fontstart, "head")
info.glyf = stbtt__find_table(data, fontstart, "glyf")
info.hhea = stbtt__find_table(data, fontstart, "hhea")
info.hmtx = stbtt__find_table(data, fontstart, "hmtx")
info.kern = stbtt__find_table(data, fontstart, "kern")
info.gpos = stbtt__find_table(data, fontstart, "GPOS")
if (cmap == 0 or info.head == 0 or info.hhea == 0 or info.hmtx == 0) then
return false
end
if info.glyf ~= 0 then
if info.loca == 0 then
return false
end
else
local cff = stbtt__find_table(data, fontstart, "CFF ")
if cff == 0 then
return false
end
info.fontdicts = stbtt__new_buf(nil, 0)
info.fdselect = stbtt__new_buf(nil, 0)
info.cff = stbtt__new_buf(data, 64 * 1024 * 1024, cff) -- TODO: this should use size from table
local b = info.cff
-- read the header
stbtt__buf_skip(b, 2)
stbtt__buf_seek(b, stbtt__buf_get8(b))
-- TODO: the name INDEX could list multiple fonts, but we just use the first one.
stbtt__cff_get_index(b) -- name INDEX
local topdictidx = stbtt__cff_get_index(b)
local topdict = stbtt__cff_index_get(topdictidx, 0)
stbtt__cff_get_index(b) -- string INDEX
info.gsubrs = stbtt__cff_get_index(b)
local charstrings = stbtt__dict_get_ints(topdict, 17, 1)
local cstype = stbtt__dict_get_ints(topdict, bit32.bor(0x100, 6), 1)
local fdarrayoff = stbtt__dict_get_ints(topdict, bit32.bor(0x100, 36), 1)
local fdselectoff = stbtt__dict_get_ints(topdict, bit32.bor(0x100, 37), 1)
info.subrs = stbtt__get_subrs(b, topdict)
if cstype[1] ~= 2 then
return false
end
if charstrings[1] == 0 then
return false
end
if fdarrayoff[1] ~= 0 then
if fdselectoff[1] == 0 then
return false
end
stbtt__buf_seek(b, fdarrayoff[1])
info.fontdicts = stbtt__cff_get_index(b)
info.fdselect = stbtt__buf_range(b, fdselectoff[1], b.size - fdselectoff[1])
end
stbtt__buf_seek(b, charstrings[1])
info.charstrings = stbtt__cff_get_index(b)
end
local t = stbtt__find_table(data, fontstart, "maxp")
if t ~= 0 then
info.numGlyphs = ttUSHORT(data, t + 4)
else
info.numGlyphs = 0xffff
end
info.svg = -1
numTables = ttUSHORT(data, cmap + 2)
info.index_map = 0
for i = 0, numTables - 1 do
local encoding_record = cmap + 4 + 8 * i
local platform_id = ttUSHORT(data, encoding_record)
if platform_id == STBTT_PLATFORM_ID.MICROSOFT then
local ms_eid = ttUSHORT(data, encoding_record + 2)
if ms_eid == STBTT_MS_EID.UNICODE_BMP or ms_eid == STBTT_MS_EID.UNICODE_FULL then
info.index_map = cmap + ttULONG(data, encoding_record + 4)
end
elseif platform_id == STBTT_PLATFORM_ID.UNICODE then
info.index_map = cmap + ttULONG(data, encoding_record + 4)
end
end
if info.index_map == 0 then
return false
end
info.indexToLocFormat = ttUSHORT(data, info.head + 50)
return true
end
--- @param info stbtt_fontinfo
--- @param unicode_codepoint int
function stbtt_FindGlyphIndex(info, unicode_codepoint)
local data = info.data
local index_map = info.index_map
local format = ttUSHORT(data, index_map + 0)
if format == 0 then
local bytes = ttUSHORT(data, index_map + 2)
if unicode_codepoint < bytes - 6 then
return ttBYTE(data, index_map + 6 + unicode_codepoint)
end
return 0
elseif format == 6 then
local first = ttUSHORT(data, index_map + 6)
local count = ttUSHORT(data, index_map + 8)
if unicode_codepoint >= first and unicode_codepoint < first + count then
return ttUSHORT(data, index_map + 10 + (unicode_codepoint - first) * 2)
end
return 0
elseif format == 2 then
STBTT_assert(false) -- TODO: high-byte mapping for japanese/chinese/korean
return 0
elseif format == 4 then
local segcount = bit32.rshift(ttUSHORT(data, index_map + 6), 1)
local searchRange = bit32.rshift(ttUSHORT(data, index_map + 8), 1)
local entrySelector = ttUSHORT(data, index_map + 10)
local rangeShift = bit32.rshift(ttUSHORT(data, index_map + 12), 1)
local endCount = index_map + 14
local search = endCount
if unicode_codepoint > 0xffff then
return 0
end
if unicode_codepoint >= ttUSHORT(data, search + rangeShift * 2) then
search = search + rangeShift * 2
end
search = search - 2
while entrySelector ~= 0 do
searchRange = bit32.rshift(searchRange, 1)
local _end = ttUSHORT(data, search + searchRange * 2)
if unicode_codepoint > _end then
search = search + searchRange * 2
end
entrySelector = entrySelector - 1
end
search = search + 2
do
local item = stbtt_uint16(bit32.rshift(search - endCount, 1))
local start = ttUSHORT(data, index_map + 14 + segcount * 2 + 2 + 2 * item)
local last = ttUSHORT(data, endCount + 2 * item)
if unicode_codepoint < start or unicode_codepoint > last then
return 0
end
local offset = ttUSHORT(data, index_map + 14 + segcount * 6 + 2 + 2 * item)
if offset == 0 then
return stbtt_uint16(unicode_codepoint + ttSHORT(data, index_map + 14 + segcount * 4 + 2 + 2 * item))
end
return ttUSHORT(data, offset + (unicode_codepoint - start) * 2 + index_map + 14 + segcount * 6 + 2 + 2 * item)
end
elseif format == 12 or format == 13 then
local ngroups = ttULONG(data, index_map + 12)
local low = 0
local high = ngroups
while low < high do
local mid = low + bit32.rshift(high - low, 1)
local start_char = ttULONG(data, index_map + 16 + mid * 12)
local end_char = ttULONG(data, index_map + 16 + mid * 12 + 4)
if unicode_codepoint < start_char then
high = mid
elseif unicode_codepoint > end_char then
low = mid + 1
else
local start_glyph = ttULONG(data, index_map + 16 + mid * 12 + 8)
if format == 12 then
return start_glyph + unicode_codepoint - start_char
else -- format == 13
return start_glyph
end
end
end
return 0
end
-- TODO:
STBTT_assert(false)
return 0
end
--- @param v stbtt_vertex
--- @param vtx_type STBTT_VTX
--- @param x stbtt_int32
--- @param y stbtt_int32
--- @param cx stbtt_int32
--- @param cy stbtt_int32
local function stbtt_setvertex(v, vtx_type, x, y, cx, cy)
v.type = vtx_type
v.x = (stbtt_int16)(x)
v.y = (stbtt_int16)(y)
v.cx = (stbtt_int16)(cx)
v.cy = (stbtt_int16)(cy)
end
local function stbtt__GetGlyfOffset(info, glyph_index)
STBTT_assert(info.cff.size == 0)
if glyph_index >= info.numGlyphs then return -1 end
if info.indexToLocFormat >= 2 then return -1 end
local g1, g2
if info.indexToLocFormat == 0 then
g1 = info.glyf + ttUSHORT(info.data, info.loca + glyph_index * 2) * 2
g2 = info.glyf + ttUSHORT(info.data, info.loca + glyph_index * 2 + 2) * 2
else
g1 = info.glyf + ttULONG(info.data, info.loca + glyph_index * 4)
g2 = info.glyf + ttULONG(info.data, info.loca + glyph_index * 4 + 4)
end
if g1 == g2 then return -1 else return g1 end
end
--- @return integer, integer?, integer?, integer?, integer?
local function stbtt_GetGlyphBox(info, glyph_index)
local n, x0, y0, x1, y1
if info.cff.size ~= 0 then
n, x0, y0, x1, y1 = stbtt__GetGlyphInfoT2(info, glyph_index)
else
local g = stbtt__GetGlyfOffset(info, glyph_index)
if g < 0 then return 0 end
x0 = ttSHORT(info.data, g + 2)
y0 = ttSHORT(info.data, g + 4)
x1 = ttSHORT(info.data, g + 6)
y1 = ttSHORT(info.data, g + 8)
end
return 1, x0, y0, x1, y1
end
--- @param vertices stbtt_vertex[]
--- @param num_vertices int
--- @param was_off bool
--- @param start_off bool
--- @param sx stbtt_int32
--- @param sy stbtt_int32
--- @param scx stbtt_int32
--- @param scy stbtt_int32
--- @param cx stbtt_int32
--- @param cy stbtt_int32
local function stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx, sy, scx, scy, cx, cy)
if start_off then
if was_off then
stbtt_setvertex(vertices[num_vertices + 1], STBTT_VTX.CURVE, bit32.rshift(cx + scx, 1), bit32.rshift(cy + scy, 1), cx, cy)
num_vertices = num_vertices + 1
end
stbtt_setvertex(vertices[num_vertices + 1], STBTT_VTX.CURVE, sx, sy, scx, scy)
num_vertices = num_vertices + 1
else
if was_off then
stbtt_setvertex(vertices[num_vertices + 1], STBTT_VTX.CURVE, sx, sy, cx, cy)
num_vertices = num_vertices + 1
else
stbtt_setvertex(vertices[num_vertices + 1], STBTT_VTX.LINE, sx, sy, 0, 0)
num_vertices = num_vertices + 1
end
end
return num_vertices
end
--- @param info stbtt_fontinfo
--- @param glyph_index int
--- @return int num_vertices
--- @return stbtt_vertex[]? vertices
local function stbtt__GetGlyphShapeTT(info, glyph_index)
local data = info.data
local num_vertices
local vertices = nil
local g = stbtt__GetGlyfOffset(info, glyph_index)
if g < 0 then return 0 end
local numberOfContours = ttSHORT(data, g)
if numberOfContours > 0 then
local endPtsOfContours = g + 10
local ins = ttUSHORT(data, endPtsOfContours + numberOfContours * 2)
local points = (endPtsOfContours + numberOfContours * 2 + 2 + ins) + 1 -- 1-based idx
local n = 1 + ttUSHORT(data, endPtsOfContours + numberOfContours * 2 - 2)
local m = n + 2 * numberOfContours
vertices = {}
for i = 1, m do vertices[i] = stbtt_vertex() end
local j = 0
local was_off = false
local start_off = false
local next_move = 0
local flagcount = 0
local off = m - n
-- first load flags
local flags = 0
for i = 1, n do
if flagcount == 0 then
flags = data[points]
points = points + 1
if bit32.band(flags, 8) ~= 0 then
flagcount = data[points]
points = points + 1
end
else
flagcount = flagcount - 1
end
vertices[off + i].type = flags
end
-- now load x coordinates
local x = 0
for i = 1, n do
flags = vertices[off + i].type
if bit32.band(flags, 2) ~= 0 then
local dx = data[points]
points = points + 1
if bit32.band(flags, 16) ~= 0 then
x = x + dx
else
x = x - dx
end
else
if bit32.band(flags, 16) == 0 then
x = x + stbtt_int16(data[points] * 256 + data[points + 1])
points = points + 2
end
end
vertices[off + i].x = stbtt_int16(x)
end
-- now load y coordinates
local y = 0
for i = 1, n do
flags = vertices[off + i].type
if bit32.band(flags, 4) ~= 0 then
local dy = data[points]
points = points + 1
if bit32.band(flags, 32) ~= 0 then
y = y + dy
else
y = y - dy
end
else
if bit32.band(flags, 32) == 0 then
y = y + stbtt_int16(data[points] * 256 + data[points + 1])
points = points + 2
end
end
vertices[off + i].y = stbtt_int16(y)
end
-- now convert them to our format
num_vertices = 0
local sx, sy, cx, cy, scx, scy = 0, 0, 0, 0, 0, 0
for i = 1, n do
flags = vertices[off + i].type
x = stbtt_int16(vertices[off + i].x)
y = stbtt_int16(vertices[off + i].y)
if next_move == i - 1 then
if i ~= 1 then
num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx, sy, scx, scy, cx, cy)
end
-- now start the new one
start_off = (bit32.band(flags, 1) == 0)
if start_off then
scx = x
scy = y
if bit32.band(vertices[off + i + 1].type, 1) == 0 then
sx = bit32.rshift(x + stbtt_int32(vertices[off + i + 1].x), 1)
sy = bit32.rshift(y + stbtt_int32(vertices[off + i + 1].y), 1)
else
sx = stbtt_int32(vertices[off + i + 1].x)
sy = stbtt_int32(vertices[off + i + 1].y)
i = i + 1
end
else
sx = x
sy = y
end
stbtt_setvertex(vertices[num_vertices + 1], STBTT_VTX.MOVE, sx, sy, 0, 0)
num_vertices = num_vertices + 1
was_off = false
next_move = 1 + ttUSHORT(data, endPtsOfContours + j * 2)
j = j + 1
else
if bit32.band(flags, 1) == 0 then
if was_off then
stbtt_setvertex(vertices[num_vertices + 1], STBTT_VTX.CURVE, bit32.rshift(cx + x, 1), bit32.rshift(cy + y, 1), cx, cy)
num_vertices = num_vertices + 1
end
cx = x
cy = y
was_off = true
else
if was_off then
stbtt_setvertex(vertices[num_vertices + 1], STBTT_VTX.CURVE, x, y, cx, cy)
num_vertices = num_vertices + 1
else
stbtt_setvertex(vertices[num_vertices + 1], STBTT_VTX.LINE, x, y, 0, 0)
num_vertices = num_vertices + 1
end
was_off = false
end
end
end
num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx, sy, scx, scy, cx, cy)
elseif numberOfContours < 0 then
local more = 1
local comp = g + 10
num_vertices = 0
while more ~= 0 do
local flags, gidx
local comp_num_vertices = 0
local comp_verts
local mtx = {1, 0, 0, 1, 0, 0}
local m, n
flags = ttSHORT(data, comp) comp = comp + 2
gidx = ttSHORT(data, comp) comp = comp + 2
if bit32.band(flags, 2) ~= 0 then
if bit32.band(flags, 1) ~= 0 then
mtx[5] = ttSHORT(data, comp) comp = comp + 2
mtx[6] = ttSHORT(data, comp) comp = comp + 2
else
mtx[5] = ttCHAR(data, comp) comp = comp + 1
mtx[6] = ttCHAR(data, comp) comp = comp + 1
end
else
-- TODO: handle matching point
STBTT_assert(false)
end
if bit32.band(flags, bit32.lshift(1, 3)) ~= 0 then -- WE_HAVE_A_SCALE
mtx[4] = ttSHORT(data, comp) / 16384.0
mtx[1] = mtx[4]
comp = comp + 2
mtx[3] = 0
mtx[2] = mtx[3]
elseif bit32.band(flags, bit32.lshift(1, 6)) ~= 0 then -- WE_HAVE_AN_X_AND_YSCALE
mtx[1] = ttSHORT(data, comp) / 16384.0
comp = comp + 2
mtx[3] = 0
mtx[2] = mtx[3]
mtx[4] = ttSHORT(data, comp) / 16384.0
comp = comp + 2
elseif bit32.band(flags, bit32.lshift(1, 7)) ~= 0 then -- WE_HAVE_A_TWO_BY_TWO
mtx[1] = ttSHORT(data, comp) / 16384.0 comp = comp + 2
mtx[2] = ttSHORT(data, comp) / 16384.0 comp = comp + 2
mtx[3] = ttSHORT(data, comp) / 16384.0 comp = comp + 2
mtx[4] = ttSHORT(data, comp) / 16384.0 comp = comp + 2
end
m = STBTT_sqrt(mtx[1] * mtx[1] + mtx[2] * mtx[2])
n = STBTT_sqrt(mtx[3] * mtx[3] + mtx[4] * mtx[4])
comp_num_vertices, comp_verts = stbtt_GetGlyphShape(info, gidx)
if comp_num_vertices > 0 then
for i = 1, comp_num_vertices do
local v = comp_verts[i]
local x = v.x
local y = v.y
v.x = stbtt_int16(m * (mtx[1] * x + mtx[3] * y + mtx[5]))
v.y = stbtt_int16(n * (mtx[2] * x + mtx[4] * y + mtx[6]))
x = v.cx
y = v.cy
v.cx = stbtt_int16(m * (mtx[1] * x + mtx[3] * y + mtx[5]))
v.cy = stbtt_int16(n * (mtx[2] * x + mtx[4] * y + mtx[6]))
end
if num_vertices == 0 then -- LUA: No malloc
vertices = comp_verts
else
for i = 1, comp_num_vertices do
vertices[num_vertices + i] = comp_verts[i]
end
end
num_vertices = num_vertices + comp_num_vertices
end
more = bit32.band(flags, bit32.lshift(1, 5))
end
end
return num_vertices, vertices
end
--- @param c stbtt__csctx
--- @param x stbtt_int32
--- @param y stbtt_int32
local function stbtt__track_vertex(c, x, y)
if x > c.max_x or not c.started then c.max_x = x end
if y > c.max_y or not c.started then c.max_y = y end
if x < c.min_x or not c.started then c.min_x = x end
if y < c.min_y or not c.started then c.min_y = y end
c.started = true
end
--- @param c stbtt__csctx
--- @param vtx_type STBTT_VTX
--- @param x stbtt_int32
--- @param y stbtt_int32
--- @param cx stbtt_int32
--- @param cy stbtt_int32
--- @param cx1 stbtt_int32
--- @param cy1 stbtt_int32
local function stbtt__csctx_v(c, vtx_type, x, y, cx, cy, cx1, cy1)
if c.bounds then
stbtt__track_vertex(c, x, y)
if vtx_type == STBTT_VTX.CUBIC then
stbtt__track_vertex(c, cx, cy)
stbtt__track_vertex(c, cx1, cy1)
end
else
stbtt_setvertex(c.pvertices[c.num_vertices + 1], vtx_type, x, y, cx, cy)
c.pvertices[c.num_vertices + 1].cx1 = stbtt_int16(cx1)
c.pvertices[c.num_vertices + 1].cy1 = stbtt_int16(cy1)
end
end
--- @param ctx stbtt__csctx
local function stbtt__csctx_close_shape(ctx)
if ctx.first_x ~= ctx.x or ctx.first_y ~= ctx.y then
stbtt__csctx_v(ctx, STBTT_VTX.LINE, trunc(ctx.first_x), trunc(ctx.first_y), 0, 0, 0, 0)
end
end
--- @param ctx stbtt__csctx
--- @param dx float
--- @param dy float
local function stbtt__csctx_rmove_to(ctx, dx, dy)
stbtt__csctx_close_shape(ctx)
ctx.x = ctx.x + dx
ctx.first_x = ctx.x
ctx.y = ctx.y + dy
ctx.first_y = ctx.y
stbtt__csctx_v(ctx, STBTT_VTX.MOVE, trunc(ctx.x), trunc(ctx.y), 0, 0, 0, 0)
end
--- @param ctx stbtt__csctx
--- @param dx float
--- @param dy float
local function stbtt__csctx_rline_to(ctx, dx, dy)
ctx.x = ctx.x + dx
ctx.y = ctx.y + dy
stbtt__csctx_v(ctx, STBTT_VTX.LINE, trunc(ctx.x), trunc(ctx.y), 0, 0, 0, 0)
end
--- @param ctx stbtt__csctx
--- @param dx1 float
--- @param dy1 float
--- @param dx2 float
--- @param dy2 float
--- @param dx3 float
--- @param dy3 float
local function stbtt__csctx_rccurve_to(ctx, dx1, dy1, dx2, dy2, dx3, dy3)
local cx1 = ctx.x + dx1
local cy1 = ctx.y + dy1
local cx2 = cx1 + dx2
local cy2 = cy1 + dy2
ctx.x = cx2 + dx3
ctx.y = cy2 + dy3
stbtt__csctx_v(ctx, STBTT_VTX.CUBIC, trunc(ctx.x), trunc(ctx.y), trunc(cx1), trunc(cy1), trunc(cx2), trunc(cy2))
end
local function stbtt__get_subr(idx, n)
local count = stbtt__cff_index_count(idx)
local bias = 107
if count >= 33900 then
bias = 32768
elseif count >= 1240 then
bias = 1131
end
n = n + bias
if n < 0 or n >= count then
return stbtt__new_buf(nil, 0)
end
return stbtt__cff_index_get(idx, n)
end
local function stbtt__cid_get_glyph_subrs(info, glyph_index)
local fdselect = info.fdselect
local fdselector = -1
stbtt__buf_seek(fdselect, 0)
local fmt = stbtt__buf_get8(fdselect)
if fmt == 0 then
-- untested
stbtt__buf_skip(fdselect, glyph_index)
fdselector = stbtt__buf_get8(fdselect)
elseif fmt == 3 then
local nranges = stbtt__buf_get16(fdselect)
local start = stbtt__buf_get16(fdselect)
for i = 0, nranges - 1 do
local v = stbtt__buf_get8(fdselect)
local _end = stbtt__buf_get16(fdselect)
if glyph_index >= start and glyph_index < _end then
fdselector = v
break
end
start = _end
end
end
if fdselector == -1 then return stbtt__new_buf(nil, 0) end -- [DEAR IMGUI] fixed, see #6007 and nothings/stb#1422
return stbtt__get_subrs(info.cff, stbtt__cff_index_get(info.fontdicts, fdselector))
end
--- @param info stbtt_fontinfo
--- @param glyph_index int
--- @param c stbtt__csctx
local function stbtt__run_charstring(info, glyph_index, c)
local in_header = true
local maskbits = 0
local subr_stack_height = 0
local sp = 0
local has_subrs = 0
local clear_stack
local s = {} -- size = 48, 0-based
local subr_stack = {} for i = 1, 10 do subr_stack[i] = stbtt__buf() end
local subrs = info.subrs
local b, f
local function STBTT__CSERR(_s) return 0 end
-- this currently ignores the initial width value, which isn't needed if we have hmtx
b = stbtt__cff_index_get(info.charstrings, glyph_index)
local i, v, b0
while b.cursor < b.size do
i = 0
clear_stack = 1
b0 = stbtt__buf_get8(b)
-- TODO: implement hinting
if b0 == 0x13 or b0 == 0x14 then -- hintmask or cntrmask
if in_header then
maskbits = maskbits + trunc(sp / 2) -- implicit "vstem"
end
in_header = false
stbtt__buf_skip(b, trunc((maskbits + 7) / 8))
elseif b0 == 0x01 or b0 == 0x03 or b0 == 0x12 or b0 == 0x17 then -- hstem, vstem, hstemhm, vstemhm
maskbits = maskbits + trunc(sp / 2)
elseif b0 == 0x15 then -- rmoveto
in_header = false
if sp < 2 then return STBTT__CSERR("rmoveto stack") end
stbtt__csctx_rmove_to(c, s[sp - 2], s[sp - 1])
elseif b0 == 0x04 then -- vmoveto
in_header = false
if sp < 1 then return STBTT__CSERR("vmoveto stack") end
stbtt__csctx_rmove_to(c, 0, s[sp - 1])
elseif b0 == 0x16 then -- hmoveto
in_header = false
if sp < 1 then return STBTT__CSERR("hmoveto stack") end
stbtt__csctx_rmove_to(c, s[sp - 1], 0)
elseif b0 == 0x05 then -- rlineto
if sp < 2 then return STBTT__CSERR("rlineto stack") end
while i + 1 < sp do
stbtt__csctx_rline_to(c, s[i], s[i + 1])
i = i + 2
end
-- hlineto/vlineto and vhcurveto/hvcurveto alternate horizontal and vertical
-- starting from a different place.
elseif b0 == 0x07 then -- vlineto
if sp < 1 then return STBTT__CSERR("vlineto stack") end
while true do
if i >= sp then break end
stbtt__csctx_rline_to(c, 0, s[i])
i = i + 1
if i >= sp then break end
stbtt__csctx_rline_to(c, s[i], 0)
i = i + 1
end
elseif b0 == 0x06 then -- hlineto
if sp < 1 then return STBTT__CSERR("hlineto stack") end
while true do
if i >= sp then break end
stbtt__csctx_rline_to(c, s[i], 0)
i = i + 1
-- vlineto
if i >= sp then break end
stbtt__csctx_rline_to(c, 0, s[i])
i = i + 1
end
elseif b0 == 0x1F then -- hvcurveto
if sp < 4 then return STBTT__CSERR("hvcurveto stack") end
while true do
if i + 3 >= sp then break end
stbtt__csctx_rccurve_to(c, s[i], 0, s[i + 1], s[i + 2], ((sp - i) == 5) and s[i + 4] or 0.0, s[i + 3])
i = i + 4
if i + 3 >= sp then break end
stbtt__csctx_rccurve_to(c, 0, s[i], s[i + 1], s[i + 2], s[i + 3], ((sp - i) == 5) and s[i + 4] or 0.0)
i = i + 4
end
elseif b0 == 0x1E then -- vhcurveto
if sp < 4 then return STBTT__CSERR("vhcurveto stack") end
while true do
if i + 3 >= sp then break end
stbtt__csctx_rccurve_to(c, 0, s[i], s[i + 1], s[i + 2], s[i + 3], ((sp - i) == 5) and s[i + 4] or 0.0)
i = i + 4
-- hvcurveto
if i + 3 >= sp then break end
stbtt__csctx_rccurve_to(c, s[i], 0, s[i + 1], s[i + 2], ((sp - i) == 5) and s[i + 4] or 0.0, s[i + 3])
i = i + 4
end
elseif b0 == 0x08 then -- rrcurveto
if sp < 6 then return STBTT__CSERR("rcurveline stack") end
while i + 5 < sp do
stbtt__csctx_rccurve_to(c, s[i], s[i + 1], s[i + 2], s[i + 3], s[i + 4], s[i + 5])
i = i + 6
end
elseif b0 == 0x18 then -- rcurveline
if sp < 8 then return STBTT__CSERR("rcurveline stack") end
while i + 5 < sp - 2 do
stbtt__csctx_rccurve_to(c, s[i], s[i + 1], s[i + 2], s[i + 3], s[i + 4], s[i + 5])
i = i + 6
end
if i + 1 >= sp then return STBTT__CSERR("rcurveline stack") end
stbtt__csctx_rline_to(c, s[i], s[i + 1])
elseif b0 == 0x19 then -- rlinecurve
if sp < 8 then return STBTT__CSERR("rlinecurve stack") end
while i + 1 < sp - 6 do
stbtt__csctx_rline_to(c, s[i], s[i + 1])
i = i + 2
end
if i + 5 >= sp then return STBTT__CSERR("rlinecurve stack") end
stbtt__csctx_rccurve_to(c, s[i], s[i + 1], s[i + 2], s[i + 3], s[i + 4], s[i + 5])
elseif b0 == 0x1A or b0 == 0x1B then -- vvcurveto or hhcurveto
if sp < 4 then return STBTT__CSERR("(vv|hh)curveto stack") end
f = 0.0
if bit32.band(sp, 1) ~= 0 then
f = s[i]
i = i + 1
end
while i + 3 < sp do
if b0 == 0x1B then
stbtt__csctx_rccurve_to(c, s[i], f, s[i + 1], s[i + 2], s[i + 3], 0.0)
else
stbtt__csctx_rccurve_to(c, f, s[i], s[i + 1], s[i + 2], 0.0, s[i + 3])
end
f = 0.0
i = i + 4
end
elseif b0 == 0x0A -- callsubr
or b0 == 0x1D then
if b0 == 0x0A and has_subrs == 0 then
if info.fdselect.size ~= 0 then
subrs = stbtt__cid_get_glyph_subrs(info, glyph_index)
end
has_subrs = 1
end
-- callgsubr
if sp < 1 then return STBTT__CSERR("call(g|)subr stack") end
sp = sp - 1
v = trunc(s[sp])
if subr_stack_height >= 10 then return STBTT__CSERR("recursion limit") end
subr_stack[subr_stack_height + 1] = b
subr_stack_height = subr_stack_height + 1
b = stbtt__get_subr((b0 == 0x0A) and subrs or info.gsubrs, v)
if b.size == 0 then return STBTT__CSERR("subr not found") end
b.cursor = 0
clear_stack = 0
elseif b0 == 0x0B then -- return
if subr_stack_height <= 0 then return STBTT__CSERR("return outside subr") end
subr_stack_height = subr_stack_height - 1
b = subr_stack[subr_stack_height + 1]
clear_stack = 0
elseif b0 == 0x0E then -- endchar
stbtt__csctx_close_shape(c)
return 1
elseif b0 == 0x0C then -- two-byte escape
local b1 = stbtt__buf_get8(b)
if b1 == 0x22 then -- hflex
if sp < 7 then return STBTT__CSERR("hflex stack") end
local dx1 = s[0]
local dx2 = s[1]
local dy2 = s[2]
local dx3 = s[3]
local dx4 = s[4]
local dx5 = s[5]
local dx6 = s[6]
stbtt__csctx_rccurve_to(c, dx1, 0, dx2, dy2, dx3, 0)
stbtt__csctx_rccurve_to(c, dx4, 0, dx5, -dy2, dx6, 0)
elseif b1 == 0x23 then -- flex
if sp < 13 then return STBTT__CSERR("flex stack") end
local dx1 = s[0]
local dy1 = s[1]
local dx2 = s[2]
local dy2 = s[3]
local dx3 = s[4]
local dy3 = s[5]
local dx4 = s[6]
local dy4 = s[7]
local dx5 = s[8]
local dy5 = s[9]
local dx6 = s[10]
local dy6 = s[11]
-- fd is s[12]
stbtt__csctx_rccurve_to(c, dx1, dy1, dx2, dy2, dx3, dy3)
stbtt__csctx_rccurve_to(c, dx4, dy4, dx5, dy5, dx6, dy6)
elseif b1 == 0x24 then -- hflex1
if sp < 9 then return STBTT__CSERR("hflex1 stack") end
local dx1 = s[0]
local dy1 = s[1]
local dx2 = s[2]
local dy2 = s[3]
local dx3 = s[4]
local dx4 = s[5]
local dx5 = s[6]
local dy5 = s[7]
local dx6 = s[8]
stbtt__csctx_rccurve_to(c, dx1, dy1, dx2, dy2, dx3, 0)
stbtt__csctx_rccurve_to(c, dx4, 0, dx5, dy5, dx6, -(dy1 + dy2 + dy5))
elseif b1 == 0x25 then -- flex1
if sp < 11 then return STBTT__CSERR("flex1 stack") end
local dx1 = s[0]
local dy1 = s[1]
local dx2 = s[2]
local dy2 = s[3]
local dx3 = s[4]
local dy3 = s[5]
local dx4 = s[6]
local dy4 = s[7]
local dx5 = s[8]
local dy5 = s[9]
local dx6 = s[10]
local dy6 = s[10]
local dx = dx1 + dx2 + dx3 + dx4 + dx5
local dy = dy1 + dy2 + dy3 + dy4 + dy5
if STBTT_fabs(dx) > STBTT_fabs(dy) then
dy6 = -dy
else
dx6 = -dx
end
stbtt__csctx_rccurve_to(c, dx1, dy1, dx2, dy2, dx3, dy3)
stbtt__csctx_rccurve_to(c, dx4, dy4, dx5, dy5, dx6, dy6)
else
return STBTT__CSERR("unimplemented")
end
else
if b0 ~= 255 and b0 ~= 28 and b0 < 32 then
return STBTT__CSERR("reserved operator")
end
-- push immediate
if b0 == 255 then
f = stbtt__buf_get32(b) / 0x10000
else
stbtt__buf_skip(b, -1)
f = stbtt_int16(stbtt__cff_int(b))
end
if sp >= 48 then return STBTT__CSERR("push stack overflow") end
s[sp] = f
sp = sp + 1
clear_stack = 0
end
if clear_stack ~= 0 then
sp = 0
end
end
return STBTT__CSERR("no endchar")
end
--- @param info stbtt_fontinfo
--- @param glyph_index int
--- @return int num_vertices
--- @return stbtt_vertex[]? vertices
function stbtt__GetGlyphShapeT2(info, glyph_index)
-- runs the charstring twice, once to count and once to output (to avoid realloc)
local vertices = nil
local count_ctx = STBTT__CSCTX_INIT(true)
local output_ctx = STBTT__CSCTX_INIT(false)
if stbtt__run_charstring(info, glyph_index, count_ctx) ~= 0 then
vertices = {} for i = 1, count_ctx.num_vertices do vertices[i] = stbtt_vertex() end
output_ctx.pvertices = vertices
if stbtt__run_charstring(info, glyph_index, output_ctx) ~= 0 then
STBTT_assert(output_ctx.num_vertices == count_ctx.num_vertices)
return output_ctx.num_vertices, vertices
end
end
return 0, vertices
end
--- @param info stbtt_fontinfo
--- @param glyph_index int
--- @return int
--- @return int x0
--- @return int y0
--- @return int x1
--- @return int y1
function stbtt__GetGlyphInfoT2(info, glyph_index)
local c = STBTT__CSCTX_INIT(true)
local r = stbtt__run_charstring(info, glyph_index, c)
local x0 = (r ~= 0) and c.min_x or 0
local y0 = (r ~= 0) and c.min_y or 0
local x1 = (r ~= 0) and c.max_x or 0
local y1 = (r ~= 0) and c.max_y or 0
return ((r ~= 0) and c.num_vertices or 0), x0, y0, x1, y1
end
--- @param info stbtt_fontinfo
--- @param glyph_index int
function stbtt_GetGlyphShape(info, glyph_index)
if info.cff.size == 0 then
return stbtt__GetGlyphShapeTT(info, glyph_index)
else
return stbtt__GetGlyphShapeT2(info, glyph_index)
end
end
--- @param info stbtt_fontinfo
--- @param glyph_index int
function stbtt_GetGlyphHMetrics(info, glyph_index)
local numOfLongHorMetrics = ttUSHORT(info.data, info.hhea + 34)
local advanceWidth, leftSideBearing
if glyph_index < numOfLongHorMetrics then
advanceWidth = ttSHORT(info.data, info.hmtx + 4 * glyph_index)
leftSideBearing = ttSHORT(info.data, info.hmtx + 4 * glyph_index + 2)
else
advanceWidth = ttSHORT(info.data, info.hmtx + 4 * (numOfLongHorMetrics - 1))
leftSideBearing = ttSHORT(info.data, info.hmtx + 4 * numOfLongHorMetrics + 2 * (glyph_index - numOfLongHorMetrics))
end
return advanceWidth, leftSideBearing
end
--- @param info stbtt_fontinfo
function stbtt_GetFontVMetrics(info)
local ascent = ttSHORT(info.data, info.hhea + 4)
local descent = ttSHORT(info.data, info.hhea + 6)
local lineGap = ttSHORT(info.data, info.hhea + 8)
return ascent, descent, lineGap
end
--- @param info stbtt_fontinfo
--- @param height float
function stbtt_ScaleForPixelHeight(info, height)
local fheight = ttSHORT(info.data, info.hhea + 4) - ttSHORT(info.data, info.hhea + 6)
return height / fheight
end
------------------------------------
------------------------------------
---
--- Antialiasing software rasterizer
---
---
--- @param font stbtt_fontinfo
--- @param glyph int
--- @param scale_x float
--- @param scale_y float
--- @param shift_x float
--- @param shift_y float
function stbtt_GetGlyphBitmapBoxSubpixel(font, glyph, scale_x, scale_y, shift_x, shift_y)
local n, x0, y0, x1, y1 = stbtt_GetGlyphBox(font, glyph)
local ix0, iy0, ix1, iy1
if n == 0 then
-- e.g. space character
ix0 = 0
iy0 = 0
ix1 = 0
iy1 = 0
else
-- move to integral bboxes (treating pixels as little squares, what pixels get touched)?
ix0 = STBTT_ifloor( x0 * scale_x + shift_x)
iy0 = STBTT_ifloor(-y1 * scale_y + shift_y)
ix1 = STBTT_iceil( x1 * scale_x + shift_x)
iy1 = STBTT_iceil(-y0 * scale_y + shift_y)
end
return ix0, iy0, ix1, iy1
end
--- @param font stbtt_fontinfo
--- @param glyph int
--- @param scale_x float
--- @param scale_y float
function stbtt_GetGlyphBitmapBox(font, glyph, scale_x, scale_y)
return stbtt_GetGlyphBitmapBoxSubpixel(font, glyph, scale_x, scale_y, 0.0, 0.0)
end
--------------
--------------
---
--- Rasterizer
---
---
--- UNIMPLEMENTED / UNUSED:
--- stbtt_MakeCodepointBitmap
--- stbtt_MakeCodepointBitmapSubpixelPrefilter
--- stbtt_GetGlyphBitmap
--- stbtt_MakeGlyphBitmap
--- stbtt_GetGlyphBitmapSubpixel
--- @param scanline float[]
--- @param x int
--- @param e stbtt__active_edge
--- @param x0 float
--- @param y0 float
--- @param x1 float
--- @param y1 float
local function stbtt__handle_clipped_edge(scanline, x, e, x0, y0, x1, y1)
if y0 == y1 then return end
STBTT_assert(y0 < y1)
STBTT_assert(e.sy <= e.ey)
if y0 > e.ey then return end
if y1 < e.sy then return end
if y0 < e.sy then
x0 = x0 + (x1 - x0) * (e.sy - y0) / (y1 - y0)
y0 = e.sy
end
if y1 > e.ey then
x1 = x1 + (x1 - x0) * (e.ey - y1) / (y1 - y0)
y1 = e.ey
end
if x0 == x then
STBTT_assert(x1 <= x + 1)
elseif x0 == x + 1 then
STBTT_assert(x1 >= x)
elseif x0 <= x then
STBTT_assert(x1 <= x)
elseif x0 >= x + 1 then
STBTT_assert(x1 >= x + 1)
else
STBTT_assert(x1 >= x and x1 <= x + 1)
end
if x0 <= x and x1 <= x then
scanline[x] = scanline[x] + e.direction * (y1 - y0)
elseif x0 >= x + 1 and x1 >= x + 1 then
-- do nothing
else
STBTT_assert(x0 >= x and x0 <= x + 1 and x1 >= x and x1 <= x + 1)
scanline[x] = scanline[x] + e.direction * (y1 - y0) * (1 - ((x0 - x) + (x1 - x)) / 2)
end
end
--- @param height float
--- @param top_width float
--- @param bottom_width float
local function stbtt__sized_trapezoid_area(height, top_width, bottom_width)
STBTT_assert(top_width >= 0)
STBTT_assert(bottom_width >= 0)
return (top_width + bottom_width) / 2.0 * height
end
--- @param height float
--- @param tx0 float
--- @param tx1 float
--- @param bx0 float
--- @param bx1 float
local function stbtt__position_trapezoid_area(height, tx0, tx1, bx0, bx1)
return stbtt__sized_trapezoid_area(height, tx1 - tx0, bx1 - bx0)
end
--- @param height float
--- @param width float
local function stbtt__sized_triangle_area(height, width)
return height * width / 2
end
--- @param scanline float[]
--- @param scanline_fill float[]
--- @param len int
--- @param e stbtt__active_edge
--- @param y_top float
local function stbtt__fill_active_edges_new(scanline, scanline_fill, len, e, y_top)
local y_bottom = y_top + 1
while e do
-- brute force every pixel
-- compute intersection points with top & bottom
STBTT_assert(e.ey >= y_top)
if e.fdx == 0 then
local x0 = e.fx
if x0 < len then
if x0 >= 0 then
stbtt__handle_clipped_edge(scanline, trunc(x0), e, x0, y_top, x0, y_bottom)
stbtt__handle_clipped_edge(scanline_fill, trunc(x0) + 1, e, x0, y_top, x0, y_bottom)
else
stbtt__handle_clipped_edge(scanline_fill, 0, e, x0, y_top, x0, y_bottom)
end
end
else
local x0 = e.fx
local dx = e.fdx
local xb = x0 + dx
local x_top, x_bottom
local sy0, sy1
local dy = e.fdy
STBTT_assert(e.sy <= y_bottom and e.ey >= y_top)
-- compute endpoints of line segment clipped to this scanline (if the
-- line segment starts on this scanline. x0 is the intersection of the
-- line with y_top, but that may be off the line segment.
if e.sy > y_top then
x_top = x0 + dx * (e.sy - y_top)
sy0 = e.sy
else
x_top = x0
sy0 = y_top
end
if e.ey < y_bottom then
x_bottom = x0 + dx * (e.ey - y_top)
sy1 = e.ey
else
x_bottom = xb
sy1 = y_bottom
end
if x_top >= 0 and x_bottom >= 0 and x_top < len and x_bottom < len then
-- from here on, we don't have to range check x values
if trunc(x_top) == trunc(x_bottom) then
-- simple case, only spans one pixel
local height
local x = trunc(x_top)
height = (sy1 - sy0) * e.direction
STBTT_assert(x >= 0 and x < len)
scanline[x] = scanline[x] + stbtt__position_trapezoid_area(height, x_top, x + 1.0, x_bottom, x + 1.0)
scanline_fill[x + 1] = scanline_fill[x + 1] + height -- everything right of this pixel is filled
else
local x1, x2
local y_crossing, y_final, step, sign, area
-- covers 2+ pixels
if x_top > x_bottom then
-- flip scanline vertically; signed area is the same
sy0 = y_bottom - (sy0 - y_top)
sy1 = y_bottom - (sy1 - y_top)
local t
t = sy0; sy0 = sy1; sy1 = t
t = x_bottom; x_bottom = x_top; x_top = t
dx = -dx
dy = -dy
t = x0; x0 = xb; xb = t
end
STBTT_assert(dy >= 0)
STBTT_assert(dx >= 0)
x1 = trunc(x_top)
x2 = trunc(x_bottom)
-- compute intersection with y axis at x1+1
y_crossing = y_top + dy * (x1 + 1 - x0)
-- compute intersection with y axis at x2
y_final = y_top + dy * (x2 - x0)
-- if x2 is right at the right edge of x1, y_crossing can blow up, github #1057
-- TODO: maybe test against sy1 rather than y_bottom?
if y_crossing > y_bottom then
y_crossing = y_bottom
end
sign = e.direction
-- area of the rectangle covered from sy0..y_crossing
area = sign * (y_crossing - sy0)
-- area of the triangle (x_top,sy0), (x1+1,sy0), (x1+1,y_crossing)
scanline[x1] = scanline[x1] + stbtt__sized_triangle_area(area, x1 + 1 - x_top)
-- check if final y_crossing is blown up; no test case for this
if y_final > y_bottom then
local denom = (x2 - (x1 + 1))
y_final = y_bottom
if denom ~= 0 then -- [DEAR IMGUI] Avoid div by zero (https://github.com/nothings/stb/issues/1316)
dy = (y_final - y_crossing) / denom -- if denom=0, y_final = y_crossing, so y_final <= y_bottom
end
end
step = sign * dy * 1 -- dy is dy/dx, change in y for every 1 change in x,
-- which multiplied by 1-pixel-width is how much pixel area changes for each step in x
-- so the area advances by 'step' every time
for x = x1 + 1, x2 - 1 do
scanline[x] = scanline[x] + area + step / 2 -- area of trapezoid is 1*step/2
area = area + step
end
STBTT_assert(STBTT_fabs(area) <= 1.01) -- accumulated error from area += step unless we round step down
STBTT_assert(sy1 > y_final - 0.01)
-- area covered in the last pixel is the rectangle from all the pixels to the left,
-- plus the trapezoid filled by the line segment in this pixel all the way to the right edge
scanline[x2] = scanline[x2] + area + sign * stbtt__position_trapezoid_area(sy1 - y_final, x2, x2 + 1.0, x_bottom, x2 + 1.0)
-- the rest of the line is filled based on the total height of the line segment in this pixel
scanline_fill[x2 + 1] = scanline_fill[x2 + 1] + sign * (sy1 - sy0)
end
else
for x = 0, len - 1 do
-- rename variables to clearly-defined pairs
local y0 = y_top
local x1 = x
local x2 = x + 1
local x3 = xb
local y3 = y_bottom
-- x = e->x + e->dx * (y-y_top)
-- (y-y_top) = (x - e->x) / e->dx
-- y = (x - e->x) / e->dx + y_top
local y1 = (x - x0) / dx + y_top
local y2 = (x + 1 - x0) / dx + y_top
if x0 < x1 and x3 > x2 then -- three segments descending down-right
stbtt__handle_clipped_edge(scanline, x, e, x0, y0, x1, y1)
stbtt__handle_clipped_edge(scanline, x, e, x1, y1, x2, y2)
stbtt__handle_clipped_edge(scanline, x, e, x2, y2, x3, y3)
elseif x3 < x1 and x0 > x2 then -- three segments descending down-left
stbtt__handle_clipped_edge(scanline, x, e, x0, y0, x2, y2)
stbtt__handle_clipped_edge(scanline, x, e, x2, y2, x1, y1)
stbtt__handle_clipped_edge(scanline, x, e, x1, y1, x3, y3)
elseif x0 < x1 and x3 > x1 then -- two segments across x, down-right
stbtt__handle_clipped_edge(scanline, x, e, x0, y0, x1, y1)
stbtt__handle_clipped_edge(scanline, x, e, x1, y1, x3, y3)
elseif x3 < x1 and x0 > x1 then -- two segments across x, down-left
stbtt__handle_clipped_edge(scanline, x, e, x0, y0, x1, y1)
stbtt__handle_clipped_edge(scanline, x, e, x1, y1, x3, y3)
elseif x0 < x2 and x3 > x2 then -- two segments across x+1, down-right
stbtt__handle_clipped_edge(scanline, x, e, x0, y0, x2, y2)
stbtt__handle_clipped_edge(scanline, x, e, x2, y2, x3, y3)
elseif x3 < x2 and x0 > x2 then -- two segments across x+1, down-left
stbtt__handle_clipped_edge(scanline, x, e, x0, y0, x2, y2)
stbtt__handle_clipped_edge(scanline, x, e, x2, y2, x3, y3)
else -- one segment
stbtt__handle_clipped_edge(scanline, x, e, x0, y0, x3, y3)
end
end
end
end
e = e.next
end
end
--- @param result stbtt__bitmap
--- @param e stbtt__edge
--- @param n int
--- @param vsubsample int
--- @param off_x int
--- @param off_y int
local function stbtt__rasterize_sorted_edges(result, e, n, vsubsample, off_x, off_y)
local active = nil
local scanline = {} -- size = result.w, 0-based
local scanline2 = {} -- size = result.w + 1, 0-based
STBTT__NOTUSED(vsubsample)
local y = off_y
e[n + 1].y0 = off_y + result.h + 1
local j = 0
local p = 1
while j < result.h do
-- find center of pixel for this scanline
local scan_y_top = y + 0.0
local scan_y_bottom = y + 1.0
for i = 0, result.w - 1 do scanline[i] = 0 end
for i = 0, result.w do scanline2[i] = 0 end
local prev = nil
local curr = active
while curr do
local next = curr.next
if curr.ey <= scan_y_top then
if prev then prev.next = next
else active = next end
curr.direction = 0
else
prev = curr
end
curr = next
end
local edge = e[p]
while edge.y0 <= scan_y_bottom do
if edge.y0 ~= edge.y1 then
local z = stbtt__new_active(edge, off_x, scan_y_top)
if j == 0 and off_y ~= 0 and z.ey < scan_y_top then
z.ey = scan_y_top
end
STBTT_assert(z.ey >= scan_y_top)
z.next = active
active = z
end
p = p + 1
edge = e[p]
end
if active then
stbtt__fill_active_edges_new(scanline, scanline2, result.w, active, scan_y_top)
end
do
local sum = 0
for i = 0, result.w - 1 do
local k
local m
sum = sum + scanline2[i]
k = scanline[i] + sum
k = STBTT_fabs(k) * 255 + 0.5
m = trunc(k)
if m > 255 then m = 255 end
result.pixels[j * result.stride + i + 1] = unsigned_char(m)
end
end
step = active
while step do
step.fx = step.fx + step.fdx
step = step.next
end
y = y + 1
j = j + 1
end
end
--- @param a stbtt__edge
--- @param b stbtt__edge
local STBTT__COMPARE = function(a, b)
if a.y0 == nil then return false end -- sentinels on the back
if b.y0 == nil then return true end
return a.y0 < b.y0
end
--- @param p stbtt__edge[]
local function stbtt__sort_edges(p)
STBTT_sort(p, STBTT__COMPARE)
end
--- @param result stbtt__bitmap
--- @param pts stbtt__point[]
--- @param wcount int[]
--- @param windings int
--- @param scale_x float
--- @param scale_y float
--- @param shift_x float
--- @param shift_y float
--- @param off_x int
--- @param off_y int
--- @param invert bool
local function stbtt__rasterize(result, pts, wcount, windings, scale_x, scale_y, shift_x, shift_y, off_x, off_y, invert)
local y_scale_inv = (invert) and -scale_y or scale_y
local vsubsample = 1 -- STBTT_RASTERIZER_VERSION == 2
-- now we have to blow out the windings into explicit edge lists
local n = 0
for i = 1, windings do
n = n + wcount[i]
end
local e = {} for i = 1, n + 1 do e[i] = stbtt__edge() end -- add an extra one as a sentinel
n = 0
local m = 0
local j
for i = 1, windings do
local p = m
m = m + wcount[i]
j = wcount[i]
for k = 1, wcount[i] do
repeat
local a = k
local b = j
-- skip the edge if horizontal
if pts[p + j].y == pts[p + k].y then
break
end
-- add edge from j to k to the list
n = n + 1
e[n].invert = false
if invert then
if pts[p + j].y > pts[p + k].y then
e[n].invert = true
a = j
b = k
end
else
if pts[p + j].y < pts[p + k].y then
e[n].invert = true
a = j
b = k
end
end
e[n].x0 = pts[p + a].x * scale_x + shift_x
e[n].y0 = (pts[p + a].y * y_scale_inv + shift_y) * vsubsample
e[n].x1 = pts[p + b].x * scale_x + shift_x
e[n].y1 = (pts[p + b].y * y_scale_inv + shift_y) * vsubsample
until true
j = k
end
end
-- now sort the edges by their highest point (should snap to integer, and then by x)
stbtt__sort_edges(e)
-- now, traverse the scanlines and find the intersections on each scanline, use xor winding rule
stbtt__rasterize_sorted_edges(result, e, n, vsubsample, off_x, off_y)
end
--- @param points? stbtt__point[]
--- @param n int
--- @param x float
--- @param y float
local function stbtt__add_point(points, n, x, y)
if points == nil then return end -- during first pass, it's unallocated
points[n + 1].x = x
points[n + 1].y = y
end
-- tessellate until threshold p is happy...
-- TODO: warped to compensate for non-linear stretching
--- @param points? stbtt__point[]
--- @param num_points int
--- @param x0 float
--- @param y0 float
--- @param x1 float
--- @param y1 float
--- @param x2 float
--- @param y2 float
--- @param objspace_flatness_squared float
--- @param n int
--- @return int num_points # updated num_points passed in
local function stbtt__tesselate_curve(points, num_points, x0, y0, x1, y1, x2, y2, objspace_flatness_squared, n)
-- midpoint
local mx = (x0 + 2 * x1 + x2) / 4
local my = (y0 + 2 * y1 + y2) / 4
-- versus directly drawn line
local dx = (x0 + x2) / 2 - mx
local dy = (y0 + y2) / 2 - my
if n > 16 then -- 65536 segments on one curve better be enough!
return num_points
end
if dx * dx + dy * dy > objspace_flatness_squared then -- half-pixel error allowed... need to be smaller if AA
num_points = stbtt__tesselate_curve(points, num_points, x0, y0, (x0 + x1) / 2.0, (y0 + y1) / 2.0, mx, my, objspace_flatness_squared, n + 1)
num_points = stbtt__tesselate_curve(points, num_points, mx, my, (x1 + x2) / 2.0, (y1 + y2) / 2.0, x2, y2, objspace_flatness_squared, n + 1)
else
stbtt__add_point(points, num_points, x2, y2)
num_points = num_points + 1
end
return num_points
end
--- @param points? stbtt__point[]
--- @param num_points int
--- @param x0 float
--- @param y0 float
--- @param x1 float
--- @param y1 float
--- @param x2 float
--- @param y2 float
--- @param x3 float
--- @param y3 float
--- @param objspace_flatness_squared float
--- @param n int
--- @return int num_points # updated num_points passed in
local function stbtt__tesselate_cubic(points, num_points, x0, y0, x1, y1, x2, y2, x3, y3, objspace_flatness_squared, n)
-- TODO: this "flatness" calculation is just made-up nonsense that seems to work well enough
local dx0 = x1 - x0
local dy0 = y1 - y0
local dx1 = x2 - x1
local dy1 = y2 - y1
local dx2 = x3 - x2
local dy2 = y3 - y2
local dx = x3 - x0
local dy = y3 - y0
local longlen = STBTT_sqrt(dx0 * dx0 + dy0 * dy0) + STBTT_sqrt(dx1 * dx1 + dy1 * dy1) + STBTT_sqrt(dx2 * dx2 + dy2 * dy2)
local shortlen = STBTT_sqrt(dx * dx + dy * dy)
local flatness_squared = longlen * longlen - shortlen * shortlen
if n > 16 then -- 65536 segments on one curve better be enough!
return num_points
end
if flatness_squared > objspace_flatness_squared then
local x01 = (x0 + x1) / 2
local y01 = (y0 + y1) / 2
local x12 = (x1 + x2) / 2
local y12 = (y1 + y2) / 2
local x23 = (x2 + x3) / 2
local y23 = (y2 + y3) / 2
local xa = (x01 + x12) / 2
local ya = (y01 + y12) / 2
local xb = (x12 + x23) / 2
local yb = (y12 + y23) / 2
local mx = (xa + xb) / 2
local my = (ya + yb) / 2
num_points = stbtt__tesselate_cubic(points, num_points, x0, y0, x01, y01, xa, ya, mx, my, objspace_flatness_squared, n + 1)
num_points = stbtt__tesselate_cubic(points, num_points, mx, my, xb, yb, x23, y23, x3, y3, objspace_flatness_squared, n + 1)
else
stbtt__add_point(points, num_points, x3, y3)
num_points = num_points + 1
end
return num_points
end
--- @param vertices stbtt_vertex[]
--- @param num_verts int
--- @param objspace_flatness float
--- @return stbtt__point[]? points
--- @return int? num_contours
--- @return int[]? contour_lengths
local function stbtt_FlattenCurves(vertices, num_verts, objspace_flatness)
local points = nil
local num_points = 0
local objspace_flatness_squared = objspace_flatness * objspace_flatness
local n = 0
local start = 0
-- count how many "moves" there are to get the contour count
for i = 1, num_verts do
if vertices[i].type == STBTT_VTX.MOVE then
n = n + 1
end
end
local num_contours = n
if n == 0 then return nil end
local contour_lengths = {} -- size = n
-- make two passes through the points so we don't need to realloc
for pass = 0, 1 do
local x, y = 0, 0
if pass == 1 then
points = {}
for i = 1, num_points do points[i] = stbtt__point() end
end
num_points = 0
n = -1
for i = 1, num_verts do
if vertices[i].type == STBTT_VTX.MOVE then
-- start the next contour
if n >= 0 then
contour_lengths[n + 1] = num_points - start
end
n = n + 1
start = num_points
x = vertices[i].x
y = vertices[i].y
stbtt__add_point(points, num_points, x, y)
num_points = num_points + 1
elseif vertices[i].type == STBTT_VTX.LINE then
x = vertices[i].x
y = vertices[i].y
stbtt__add_point(points, num_points, x, y)
num_points = num_points + 1
elseif vertices[i].type == STBTT_VTX.CURVE then
num_points = stbtt__tesselate_curve(points, num_points, x, y,
vertices[i].cx, vertices[i].cy,
vertices[i].x, vertices[i].y,
objspace_flatness_squared, 0)
x = vertices[i].x
y = vertices[i].y
elseif vertices[i].type == STBTT_VTX.CUBIC then
num_points = stbtt__tesselate_cubic(points, num_points, x, y,
vertices[i].cx, vertices[i].cy,
vertices[i].cx1, vertices[i].cy1,
vertices[i].x, vertices[i].y,
objspace_flatness_squared, 0)
x = vertices[i].x
y = vertices[i].y
end
end
contour_lengths[n + 1] = num_points - start
end
return points, num_contours, contour_lengths
end
--- @param result stbtt__bitmap
--- @param flatness_in_pixels float
--- @param vertices stbtt_vertex[]
--- @param num_verts int
--- @param scale_x float
--- @param scale_y float
--- @param shift_x float
--- @param shift_y float
--- @param x_off int
--- @param y_off int
--- @param invert bool
local function stbtt_Rasterize(result, flatness_in_pixels, vertices, num_verts, scale_x, scale_y, shift_x, shift_y, x_off, y_off, invert)
local scale = (scale_x > scale_y) and scale_y or scale_x
local windings, winding_count, winding_lengths = stbtt_FlattenCurves(vertices, num_verts, flatness_in_pixels / scale)
if windings then
--- @cast winding_lengths int[]
--- @cast winding_count int
stbtt__rasterize(result, windings, winding_lengths, winding_count, scale_x, scale_y, shift_x, shift_y, x_off, y_off, invert)
end
end
--- @param info stbtt_fontinfo
--- @param output unsigned_char[]
--- @param out_w int
--- @param out_h int
--- @param out_stride int
--- @param scale_x float
--- @param scale_y float
--- @param shift_x float
--- @param shift_y float
--- @param glyph int
local function stbtt_MakeGlyphBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, shift_x, shift_y, glyph)
local num_verts, vertices = stbtt_GetGlyphShape(info, glyph)
local gbm = stbtt__bitmap()
local ix0, iy0 = stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale_x, scale_y, shift_x, shift_y)
gbm.pixels = output
gbm.w = out_w
gbm.h = out_h
gbm.stride = out_stride
if gbm.w ~= 0 and gbm.h ~= 0 then
stbtt_Rasterize(gbm, 0.35, vertices, num_verts, scale_x, scale_y, shift_x, shift_y, ix0, iy0, true)
end
end
-----------------
-----------------
---
--- Bitmap baking
---
---
--- UNIMPLEMENTED / UNUSED:
--- stbtt_BakeFontBitmap_internal
--- stbtt_BakeFontBitmap
--- stbtt_GetBakedQuad
---------------------------
---------------------------
---
--- Bitmap baking / packing
---
---
--- UNIMPLEMENTED / UNUSED:
--- stbtt_PackBegin
--- stbtt_PackSetOversampling
--- stbtt_PackSetSkipMissingCodepoints
--- stbtt_PackFontRangesGatherRects
--- stbtt_PackFontRangesRenderIntoRects
--- stbtt_PackFontRangesPackRects
--- stbtt_PackFontRanges
--- stbtt_PackFontRange
--- stbtt_GetPackedQuad
--- stbtt_GetScaledFontVMetrics
local STBTT__OVER_MASK = STBTT_MAX_OVERSAMPLE - 1
--- @param pixels table
--- @param w int
--- @param h int
--- @param stride_in_bytes int
--- @param kernel_width unsigned_int
local function stbtt__h_prefilter(pixels, w, h, stride_in_bytes, kernel_width)
local buffer = {}
local safe_w = w - kernel_width
for j = 0, h - 1 do
local total = 0
local row_offset = j * stride_in_bytes
for i = 1, kernel_width do buffer[i] = 0 end
for i = 0, safe_w do
total = total + pixels[row_offset + i + 1] - buffer[bit32.band(i, STBTT__OVER_MASK) + 1]
buffer[bit32.band(i + kernel_width, STBTT__OVER_MASK) + 1] = pixels[row_offset + i + 1]
pixels[row_offset + i + 1] = unsigned_char(total / kernel_width)
end
for i = safe_w + 1, w - 1 do
STBTT_assert(pixels[row_offset + i + 1] == 0)
total = total - buffer[bit32.band(i, STBTT__OVER_MASK) + 1]
pixels[row_offset + i + 1] = unsigned_char(total / kernel_width)
end
end
end
--- @param pixels table
--- @param w int
--- @param h int
--- @param stride_in_bytes int
--- @param kernel_width unsigned_int
local function stbtt__v_prefilter(pixels, w, h, stride_in_bytes, kernel_width)
local buffer = {}
local safe_h = h - kernel_width
for j = 0, w - 1 do
local total = 0
local col_offset = j
for i = 1, kernel_width do buffer[i] = 0 end
for i = 0, safe_h do
total = total + pixels[col_offset + i * stride_in_bytes + 1] - buffer[bit32.band(i, STBTT__OVER_MASK) + 1]
buffer[bit32.band(i + kernel_width, STBTT__OVER_MASK) + 1] = pixels[col_offset + i * stride_in_bytes + 1]
pixels[col_offset + i * stride_in_bytes + 1] = unsigned_char(total / kernel_width)
end
for i = safe_h + 1, h - 1 do
STBTT_assert(pixels[col_offset + i * stride_in_bytes + 1] == 0)
total = total - buffer[bit32.band(i, STBTT__OVER_MASK) + 1]
pixels[col_offset + i * stride_in_bytes + 1] = unsigned_char(total / kernel_width)
end
end
end
--- @param oversample int
--- @return float
local function stbtt__oversample_shift(oversample)
if oversample == 0 then
return 0.0
end
return -(oversample - 1) / (2.0 * oversample)
end
--- @param info stbtt_fontinfo
--- @param output unsigned_char[]
--- @param out_w int
--- @param out_h int
--- @param out_stride int
--- @param scale_x float
--- @param scale_y float
--- @param shift_x float
--- @param shift_y float
--- @param prefilter_x int
--- @param prefilter_y int
--- @param glyph int
function stbtt_MakeGlyphBitmapSubpixelPrefilter(info, output, out_w, out_h, out_stride, scale_x, scale_y, shift_x, shift_y, prefilter_x, prefilter_y, glyph)
stbtt_MakeGlyphBitmapSubpixel(info, output, out_w - (prefilter_x - 1), out_h - (prefilter_y - 1), out_stride, scale_x, scale_y, shift_x, shift_y, glyph)
if prefilter_x > 1 then
stbtt__h_prefilter(output, out_w, out_h, out_stride, prefilter_x)
end
if prefilter_y > 1 then
stbtt__v_prefilter(output, out_w, out_h, out_stride, prefilter_y)
end
local sub_x, sub_y
sub_x = stbtt__oversample_shift(prefilter_x)
sub_y = stbtt__oversample_shift(prefilter_y)
return sub_x, sub_y
end
-------------------
-------------------
---
--- SDF computation
---
---
--- UNIMPLEMENTED / UNUSED:
--- stbtt__ray_intersect_bezier
--- stbtt__compute_crossings_x
--- stbtt__cuberoot
--- stbtt__solve_cubic
--- stbtt_GetGlyphSDF
--- stbtt_GetCodepointSDF
-----------------------------------------------------
-----------------------------------------------------
---
--- Font name matching -- recommended not to use this
---
---
--- UNIMPLEMENTED / UNUSED:
--- stbtt__CompareUTF8toUTF16_bigendian_prefix
--- stbtt_CompareUTF8toUTF16_bigendian_internal
--- stbtt_GetFontNameString
--- stbtt__matchpair
--- stbtt__matches
--- @param data table
--- @param index int
function stbtt_GetFontOffsetForIndex(data, index) return stbtt_GetFontOffsetForIndex_internal(data, index) end
--- @param info stbtt_fontinfo
--- @param data table
--- @param offset int
function stbtt_InitFont(info, data, offset) return stbtt_InitFont_internal(info, data, offset) end
return {
fontinfo = stbtt_fontinfo,
InitFont = stbtt_InitFont,
FindGlyphIndex = stbtt_FindGlyphIndex,
MakeGlyphBitmapSubpixelPrefilter = stbtt_MakeGlyphBitmapSubpixelPrefilter,
GetFontVMetrics = stbtt_GetFontVMetrics,
GetGlyphBitmapBox = stbtt_GetGlyphBitmapBox,
GetGlyphBitmapBoxSubpixel = stbtt_GetGlyphBitmapBoxSubpixel,
GetGlyphHMetrics = stbtt_GetGlyphHMetrics,
GetFontOffsetForIndex = stbtt_GetFontOffsetForIndex,
ScaleForPixelHeight = stbtt_ScaleForPixelHeight
}