Don't crash if Lua asks for an invalid event
[jpcrr.git] / lua / textrender.lua
blob207b6e07d6fb86ceef33a30670e5c1762e8252e6
1 local cached_fonts = {};
2 local font_file = {};
3 local use_chargen = true;
5 local xD800 = 55296;
6 local xDC00 = 56320;
7 local xDFFF = 57343;
9 set_font_file = function(file)
10 font_file = file;
11 use_chargen = false;
12 cached_fonts = {}; -- Flush cache.
13 end
15 local check_codepoint = function(codepoint)
16 if type(codepoint) == "string" then
17 if codepoint == "nonexistent" then
18 return true;
19 elseif codepoint == "invalid" then
20 return true;
21 else
22 return false;
23 end
24 elseif type(codepoint) == "number" then
25 local planechar = codepoint % 65536;
26 local plane = (codepoint - planechar) / 65536;
28 -- Negative codepoints are not valid, planes have 65534 characters
29 -- and there are 17 planes.
30 if codepoint < 0 or planechar > 65533 or plane > 16 then
31 return false;
32 end
33 -- Surrogate range not valid.
34 if codepoint >= xD800 and codepoint <= xDFFF then
35 return false;
36 end
37 return true; -- Ok.
38 else
39 return false;
40 end
41 end
43 local load_codepoint = function(codepoint)
44 local bitmap = "";
45 local metric_w = 0;
46 local metric_h = 0;
48 if not font_file or not font_file.member then
49 cached_fonts[codepoint] = false;
50 return;
51 end
53 local file, err;
54 file, err = font_file:member(tostring(codepoint));
55 if not file then
56 cached_fonts[codepoint] = false;
57 return;
58 end
59 local file2 = file:four_to_five();
60 bitmap = "";
61 local line;
62 local raw = file2:read();
63 if #raw < 2 then
64 metric_h = 0;
65 metric_w = 0;
66 bitmap = "";
67 else
68 local w1 = string.byte(raw, 1);
69 local w2 = string.byte(raw, 2);
70 local w, h, l;
71 if w1 > 127 then
72 metric_w = (w1 - 128) + 128 * w2;
73 l = #raw - 2;
74 else
75 metric_w = w1;
76 l = #raw - 1;
77 end
78 metric_h = ((8 * l) - (8 * l) % metric_w) / metric_w;
79 bitmap = raw;
80 end
81 file2:close();
83 cached_fonts[codepoint] = {bitmap = bitmap, metric_w = metric_w, metric_h = metric_h};
84 end
86 local get_character = function(codepoint)
87 if not check_codepoint(codepoint) then
88 codepoint = "invalid";
89 end
90 if cached_fonts[codepoint] == nil then
91 load_codepoint(codepoint);
92 end
93 if not cached_fonts[codepoint] then
94 -- No such codepoint in font. Fall back.
95 codepoint = "nonexistent";
96 end
97 if cached_fonts[codepoint] == nil then
98 load_codepoint(codepoint);
99 end
100 if not cached_fonts[codepoint] then
101 -- Fine, doesn't exist and no fallback.
102 return {bitmap = "", metric_w = 0, metric_h = 0};
104 return cached_fonts[codepoint];
107 local next_character = function(str, index)
108 local c1 = string.byte(str, index);
109 local c2 = nil;
110 if index < #str then
111 c2 = string.byte(str, index);
113 local codelen = 0;
114 local point = 0;
116 if c1 < xD800 or c1 > xDFFF then
117 point = c1;
118 codelen = 1;
119 elseif c1 >= xDC00 then
120 point = "invalid";
121 codelen = 1;
122 elseif not c2 then
123 point = "invalid";
124 codelen = 1;
125 elseif c2 < xDC00 or c2 > xDFFF then
126 point = "invalid";
127 codelen = 2;
128 else
129 c1 = c1 - xD800;
130 c2 = c2 - xDC00;
131 point = 65536 + c1 * 1024 + c2;
132 codelen = 2;
134 if index + codelen <= #str then
135 return point, index + codelen;
136 else
137 return point, nil;
141 text_metrics = function(str, singleline)
142 local metric_w = 0;
143 local metric_h = 0;
144 local metric_w_curline = 0;
145 local metric_h_curline = 0;
146 local index = 1;
148 while index do
149 local codepoint;
150 local fontdata;
151 codepoint, index = next_character(str, index);
152 if singleline or (codepoint ~= 10 and codepoint ~= 13) then
153 if use_chargen then
154 fontdata = get_character(codepoint);
155 if fontdata.metric_h > metric_h_curline then
156 metric_h_curline = fontdata.metric_h;
158 metric_w_curline = metric_w_curline + fontdata.metric_w;
159 else
160 metric_h_curline = 16;
161 metric_w_curline = metric_w_curline + 8;
163 else
164 if metric_w_curline > metric_w then
165 metric_w = metric_w_curline;
167 metric_h = metric_h + metric_h_curline;
168 metric_w_curline = 0;
169 metric_h_curline = 0;
172 if metric_w_curline > metric_w then
173 metric_w = metric_w_curline;
175 metric_h = metric_h + metric_h_curline;
176 metric_w_curline = 0;
177 metric_h_curline = 0;
179 return metric_w, metric_h;
182 render_text = function(flags, x, y, str, singleline, fgr, fgg, fgb, fga, bgr, bgg, bgb, bga)
183 local metric_w = 0;
184 local metric_h = 0;
185 local metric_w_curline = 0;
186 local metric_h_curline = 0;
187 local index = 1;
189 fga = fga or 255;
190 bgr = bgr or 0;
191 bgg = bgg or 0;
192 bgb = bgb or 0;
193 bga = bga or 0;
195 if use_chargen then
196 jpcrr.hud.chargen(flags, x, y, str, not singleline, fgr, fgg, fgb, fga, bgr, bgg, bgb, bga);
197 return;
200 while index do
201 local codepoint;
202 local fontdata;
203 codepoint, index = next_character(str, index);
204 if singleline or (codepoint ~= 10 and codepoint ~= 13) then
205 fontdata = get_character(codepoint);
206 jpcrr.hud.bitmap_binary(flags, x + metric_w_curline, y + metric_h, fontdata.bitmap, fgr, fgg,
207 fgb, fga, bgr, bgg, bgb, bga);
208 if fontdata.metric_h > metric_h_curline then
209 metric_h_curline = fontdata.metric_h;
211 metric_w_curline = metric_w_curline + fontdata.metric_w;
212 else
213 if metric_w_curline > metric_w then
214 metric_w = metric_w_curline;
216 metric_h = metric_h + metric_h_curline;
217 metric_w_curline = 0;
218 metric_h_curline = 0;