aboutsummaryrefslogtreecommitdiff
path: root/advtrains/unifont.lua
blob: 426be947375f6407e9d6300a8ce26105db3254f8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
--[[
Note on copyright:
The Unifont website (http://unifoundry.com/unifont/index.html) says that
the font files are licensed "under the GNU General Public License,
either version 2 or (at your option) a later version". Section 13 of the
GNU GPLv3 gives the "permission to link or combine any covered work with
a work licensed under version 3 of the GNU Affero General Public License
into a single combined work". The use of Unifont here should fall under
this case considering that
- Unifont is licensed under GNU GPLv2+, and, in this case, the terms of
the GNU GPLv3 is used here
- We are combining Unifont into advtrains, and the latter is licensed
under the GNU AGPLv3.
However, as I am not a lawyer, I can not be sure whether this
interpretation is legally accepted.
- Y.W.
]]

local tonumber, unpack = tonumber, unpack
local sbyte, schar, sformat, smatch, ssub = string.byte, string.char, string.format, string.match, string.sub
local tconcat = table.concat

local texture_dir = tconcat({advtrains.modpath, "textures", "unifont"}, DIR_DELIM)
minetest.mkdir(texture_dir)
local function texture_file(cp)
	return sformat(cp < 65536 and "%s_%04x.bmp" or "%s_%06x.bmp", "advtrains_unifont", cp)
end
local function texture_path(cp)
	return texture_dir .. DIR_DELIM .. texture_file(cp)
end


local cpwidth = {}

local function mbstocps(str)
	local t = {}
	local i = 1
	while i <= #str do
		local c = sbyte(str, i)
		local bt = {}
		i = i+1
		if c < 128 then
			-- nop
		elseif c < 192 then
			c = 0
		elseif c < 224 then
			bt = {sbyte(str, i, i)}
		elseif c < 240 then
			bt = {sbyte(str, i, i+1)}
		elseif c < 248 then
			bt = {sbyte(str, i, i+2)}
		else
			c = 0
		end
		for i = 1, #bt do
			c = c*64+(bt[i]%64)
		end
		i = i + #bt
		t[#t+1] = c
	end
	return t
end

local function renderer(opts)
	local opts = opts or {}
	local x0, y0 = (opts.x or 0), (opts.y or 0)
	local width, height = opts.width, opts.height
	local halign, valign = (opts.halign or 0.5), (opts.valign or 0.5)
	local textcolor = opts.textcolor or "black"
	local bgcolor = opts.bgcolor
	local function break_lines(cps)
		local lastline = {width = 0}
		local lines = {lastline}
		local i = 1
		while i <= #cps do
			local char = cps[i]
			if char == 10 then
				lastline = {width = 0}
				lines[#lines+1] = lastline
			elseif cpwidth[char] then
				lastline.width = lastline.width + cpwidth[char]
				lastline[#lastline+1] = char
			end
			i = i+1
		end
		return lines
	end
	return function(str)
		local lines = break_lines(mbstocps(str))
		local textheight = 16*#lines
		local y = y0 + (height-textheight)*valign
		local st = {
			"[combine",
			sformat("%dx%d", x0+width, y0+height)
		}
		for i = 1, #lines do
			local line = lines[i]
			local x = x0 + (width - line.width)*halign
			for j = 1, #line do
				local cp = line[j]
				st[#st+1] = sformat("%d,%d=%s", x, y, texture_file(cp))
				x = x + cpwidth[cp]
			end
		end
		local prefix = ""
		if bgcolor then
			prefix = sformat("[combine:%dx%d:%d,%d=\\(advtrains_hud_bg.png\\^[resize\\:%dx%d\\^[colorize\\:%s\\:alpha\\)^",
				x0+width, y0+height, x0, y0, width, height, bgcolor)
		end
		return sformat("(%s(%s^[makealpha:000000^[multiply:%s))", prefix, tconcat(st, ":"), textcolor)
	end
end

local function render(str, opts)
	return renderer(opts)(str)
end

-- Generate texture files
local bmp_headers = {}
for _, v in pairs {8, 16} do
	bmp_headers[v] = tconcat{
		"BM", -- starting bytes
		"\125\0\0\0", -- file size
		"\0\0\0\0", -- reserved fields
		"\62\0\0\0", -- offset of the pixel array
		"\40\0\0\0", -- BITMAPINFOHEADER
		schar(v, 0, 0, 0), -- image width
		"\16\0\0\0", -- image height
		"\1\0", -- number of color planes (must be 1, apparently)
		"\1\0", -- bits per pixel
		"\0\0\0\0", -- no compression
		"\64\0\0\0", -- size of the raw bitmap data
		"\0\0\0\0\0\0\0\0", -- image resolution (irrelevant here)
		"\2\0\0\0", -- number of colors in the palette
		"\0\0\0\0", -- "important" colors
		-- palette
		"\0\0\0\0",
		"\255\255\255\0",
	}
end

local t0 = os.clock()
local f = io.open(advtrains.modpath .. DIR_DELIM .. "unifont.hex", "rb") or error("Cannot open unifont.hex")
for l in f:lines() do
	local cp, raw = smatch(l, "^(%x+):(%x+)$")
	local cp = tonumber(cp, 16) or error("Cannot parse codepoint from " .. cp)
	local rowsize = #raw/16
	local width = rowsize*4
	cpwidth[cp] = width
	local rowbytes = rowsize/2
	local bytes = {bmp_headers[width]}
	for i = 0, 15 do
		local row = {}
		local offset = i*rowsize
		for j = 1, rowbytes do
			local offset = offset+2*j-1
			local data = ssub(raw, offset, offset+1)
			row[j] = tonumber(data, 16)
		end
		for j = rowbytes+1, 4 do
			row[j] = 0
		end
		bytes[17-i] = schar(unpack(row))
	end
	minetest.safe_file_write(texture_path(cp), tconcat(bytes))
end
minetest.log("action",sformat("[advtrains/unifont] Generated textures in %f seconds", os.clock()-t0))

return {
	texture_dir = texture_dir,
	renderer = renderer,
	render = render
}