2467 lines
81 KiB
Lua
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
|
|
}
|