Actually call on_reset callback
[lsnes.git] / src / library / framebuffer-font2.cpp
blob3e4acc392f97009bd769e69a3484062deafd42cf
1 #include "framebuffer-font2.hpp"
2 #include "range.hpp"
3 #include "serialization.hpp"
4 #include <functional>
5 #include <cstring>
6 #include <sstream>
7 #include "zip.hpp"
8 #include "string.hpp"
10 namespace framebuffer
12 namespace
14 inline bool readfont(const font2::glyph& fglyph, uint32_t xp1, uint32_t yp1)
16 if(xp1 < 1 || xp1 > fglyph.width || yp1 < 1 || yp1 > fglyph.height)
17 return false;
18 xp1--;
19 yp1--;
20 size_t ge = yp1 * fglyph.stride + (xp1 / 32);
21 size_t gb = 31 - xp1 % 32;
22 return ((fglyph.fglyph[ge] >> gb) & 1);
25 template<bool T> void _render(const font2::glyph& fglyph, fb<T>& fb, int32_t x, int32_t y,
26 color fg, color bg, color hl)
28 uint32_t _x = x;
29 uint32_t _y = y;
30 if(hl) {
31 _x--;
32 _y--;
33 range bX = (range::make_w(fb.get_width()) - _x) & range::make_w(fglyph.width + 2);
34 range bY = (range::make_w(fb.get_height()) - _y) & range::make_w(fglyph.height + 2);
35 for(unsigned i = bY.low(); i < bY.high(); i++) {
36 auto p = fb.rowptr(i + _y) + (_x + bX.low());
37 for(unsigned j = bX.low(); j < bX.high(); j++) {
38 bool in_halo = false;
39 in_halo |= readfont(fglyph, j - 1, i - 1);
40 in_halo |= readfont(fglyph, j, i - 1);
41 in_halo |= readfont(fglyph, j + 1, i - 1);
42 in_halo |= readfont(fglyph, j - 1, i );
43 in_halo |= readfont(fglyph, j + 1, i );
44 in_halo |= readfont(fglyph, j - 1, i + 1);
45 in_halo |= readfont(fglyph, j, i + 1);
46 in_halo |= readfont(fglyph, j + 1, i + 1);
47 if(readfont(fglyph, j, i))
48 fg.apply(p[j]);
49 else if(in_halo)
50 hl.apply(p[j]);
51 else
52 bg.apply(p[j]);
56 } else {
57 range bX = (range::make_w(fb.get_width()) - _x) & range::make_w(fglyph.width);
58 range bY = (range::make_w(fb.get_height()) - _y) & range::make_w(fglyph.height);
59 for(unsigned i = bY.low(); i < bY.high(); i++) {
60 auto p = fb.rowptr(i + _y) + (_x + bX.low());
61 for(unsigned j = bX.low(); j < bX.high(); j++) {
62 size_t ge = i * fglyph.stride + (j / 32);
63 size_t gb = 31 - j % 32;
64 if((fglyph.fglyph[ge] >> gb) & 1)
65 fg.apply(p[j]);
66 else
67 bg.apply(p[j]);
74 font2::glyph::glyph()
76 stride = width = height = 0;
79 font2::glyph::glyph(std::istream& s)
81 char header[40];
82 bool old = true;
83 bool upside_down = true;
84 size_t rcount = 26;
85 s.read(header, 26);
86 if(!s)
87 throw std::runtime_error("Can't read glyph bitmap header");
88 if(serialization::u16l(header + 0) != 0x4D42)
89 throw std::runtime_error("Bad glyph BMP magic");
90 if(serialization::u16l(header + 14) != 12) {
91 //Not OS/2 format.
92 old = false;
93 rcount = 40;
94 s.read(header + 26, 14);
95 if(!s)
96 throw std::runtime_error("Can't read glyph bitmap header");
99 uint32_t startoff = serialization::u32l(header + 10);
100 if(old) {
101 width = serialization::u16l(header + 18);
102 height = serialization::u16l(header + 20);
103 if(serialization::u16l(header + 22) != 1)
104 throw std::runtime_error("Bad glyph BMP planecount");
105 if(serialization::u16l(header + 24) != 1)
106 throw std::runtime_error("Bad glyph BMP bitdepth");
107 if(startoff < 26)
108 throw std::runtime_error("Glyph BMP data can't overlap header");
109 } else {
110 long _width = serialization::s32l(header + 18);
111 long _height = serialization::s32l(header + 22);
112 if(_width < 0)
113 throw std::runtime_error("Bad glyph BMP size");
114 if(_height < 0)
115 upside_down = false;
116 width = _width;
117 height = (_height >= 0) ? height : -height;
119 if(serialization::u16l(header + 26) != 1)
120 throw std::runtime_error("Bad glyph BMP planecount");
121 if(serialization::u16l(header + 28) != 1)
122 throw std::runtime_error("Bad glyph BMP bitdepth");
123 if(serialization::u32l(header + 30) != 0)
124 throw std::runtime_error("Bad glyph BMP compression method");
125 if(startoff < 40)
126 throw std::runtime_error("Glyph BMP data can't overlap header");
128 //Discard data until start of bitmap.
129 while(rcount < startoff) {
130 s.get();
131 if(!s)
132 throw std::runtime_error("EOF while skipping to BMP data");
133 rcount++;
135 stride = (width + 31) / 32;
136 fglyph.resize(stride * height);
137 memset(&fglyph[0], 0, sizeof(uint32_t) * fglyph.size());
138 size_t toskip = (4 - ((width + 7) / 8) % 4) % 4;
139 for(size_t i = 0; i < height; i++) {
140 size_t y = upside_down ? (height - i - 1) : i;
141 size_t bpos = y * stride * 32;
142 for(size_t j = 0; j < width; j += 8) {
143 size_t e = (bpos + j) / 32;
144 size_t b = (bpos + j) % 32;
145 int c = s.get();
146 if(!s)
147 throw std::runtime_error("EOF while reading BMP data");
148 fglyph[e] |= ((uint32_t)c << (24 - b));
150 for(size_t j = 0; j < toskip; j++) {
151 s.get();
152 if(!s)
153 throw std::runtime_error("EOF while reading BMP data");
158 void font2::glyph::dump(std::ostream& s) const
160 static uint8_t hdr[32] = {
161 0x42, 0x4d, 0x38, 0, 0, 0, 0, 0, 0, 0, 0x20, 0, 0, 0, 0x0c, 0,
162 0, 0, 0xFF, 0xFF, 0xFF, 0xFF, 1, 0, 1, 0, 0, 0, 0, 0xff, 0xff, 0xff
164 serialization::u16l(hdr + 18, width);
165 serialization::u16l(hdr + 20, height);
166 s.write((char*)hdr, 32);
167 uint32_t rowsize = (width + 7) / 8;
168 rowsize = (rowsize + 3) & (~3);
169 uint8_t buf[rowsize];
170 memset(buf, 0, rowsize);
171 for(size_t i = 0; i < height; i++) {
172 size_t y = height - i - 1;
173 size_t bpos = y * stride * 32;
174 for(size_t j = 0; j < width; j += 8) {
175 size_t e = (bpos + j) / 32;
176 size_t b = (bpos + j) % 32;
177 buf[j >> 3] = fglyph[e] >> (24 - b);
179 s.write((char*)&buf[0], rowsize);
183 void font2::glyph::render(fb<false>& fb, int32_t x, int32_t y, color fg,
184 color bg, color hl) const
186 _render(*this, fb, x, y, fg, bg, hl);
189 void font2::glyph::render(fb<true>& fb, int32_t x, int32_t y, color fg,
190 color bg, color hl) const
192 _render(*this, fb, x, y, fg, bg, hl);
195 void font2::glyph::render(uint8_t* buf, size_t _stride, uint32_t u, uint32_t v, uint32_t w, uint32_t h) const
197 //Clip the bounding box to valid range.
198 u = std::min(u, (uint32_t)width);
199 v = std::min(v, (uint32_t)height);
200 w = std::min(w, (uint32_t)width);
201 h = std::min(h, (uint32_t)height);
202 if(u + w > width) w = width - u;
203 if(v + h > height) h = height - v;
204 if(!w || !h) return;
205 //Do the actual render.
206 size_t ge = v * stride;
207 for(unsigned j = 0; j < h; j++) {
208 for(unsigned i = 0; i < w; i++) {
209 unsigned dx = u + i;
210 size_t gb = 31 - (dx & 31);
211 buf[i] = (fglyph[ge + (dx >> 5)] >> gb) & 1;
213 buf += _stride;
214 ge += stride;
218 font2::font2()
220 rowadvance = 0;
223 font2::font2(const std::string& file)
225 std::istream* toclose = NULL;
226 rowadvance = 0;
227 try {
228 zip::reader r(file);
229 for(auto member : r) {
230 //Parse the key out of filename.
231 std::u32string key;
232 std::string tname = member;
233 std::string tmp;
234 if(tname == "bad") {
235 //Special, no key.
236 } else if(regex_match("[0-9]+(-[0-9]+)*", tname))
237 for(auto& tmp : token_iterator<char>::foreach(tname, {"-"}))
238 key.append(1, parse_value<uint32_t>(tmp));
239 else {
240 delete toclose;
241 toclose = NULL;
242 continue;
244 std::istream& s = r[member];
245 toclose = &s;
246 try {
247 add(key, glyph(s));
248 } catch(std::bad_alloc& e) {
249 throw;
250 } catch(std::exception& e) {
251 throw std::runtime_error(tname + std::string(": ") + e.what());
253 delete toclose;
254 toclose = NULL;
256 } catch(std::bad_alloc& e) {
257 if(toclose)
258 delete toclose;
259 throw;
260 } catch(std::exception& e) {
261 if(toclose)
262 delete toclose;
263 throw std::runtime_error(std::string("Error reading font: ") + e.what());
267 font2::font2(struct font& bfont)
269 auto s = bfont.get_glyphs_set();
270 rowadvance = 0;
271 for(auto i = s.begin();;i++) {
272 const font::glyph& j = (i != s.end()) ? bfont.get_glyph(*i) : bfont.get_bad_glyph();
273 glyph k;
274 k.width = j.get_width();
275 k.height = j.get_height();
276 k.stride = 1;
277 k.fglyph.resize(16);
278 for(size_t y = 0; y < k.height; y++) {
279 for(size_t x = 0; x < k.width; x++) {
280 if(j.read_pixel(x, y))
281 k.fglyph[y] |= 1UL << (31 - x);
284 rowadvance = std::max((size_t)rowadvance, (size_t)j.get_height());
285 std::u32string key = (i != s.end()) ? std::u32string(1, *i) : std::u32string();
286 glyphs[key] = k;
287 if(i == s.end()) break;
291 std::ostream& operator<<(std::ostream& os, const std::u32string& lkey)
293 if(!lkey.length())
294 return (os << "bad");
295 for(size_t i = 0; i < lkey.length(); i++) {
296 if(i)
297 os << "-";
298 os << static_cast<uint32_t>(lkey[i]);
300 return os;
303 void font2::add(const std::u32string& key, const glyph& fglyph)
305 glyphs[key] = fglyph;
306 if(fglyph.height > rowadvance)
307 rowadvance = fglyph.height;
310 std::u32string font2::best_ligature_match(const std::u32string& codepoints, size_t start) const
312 std::u32string tmp;
313 if(start >= codepoints.length())
314 return tmp; //Bad.
315 std::u32string best = tmp;
316 for(size_t i = 1; i <= codepoints.size() - start; i++) {
317 tmp.append(1, codepoints[start + i - 1]);
318 std::u32string lkey = tmp;
319 if(glyphs.count(lkey))
320 best = lkey;
321 auto j = glyphs.lower_bound(lkey);
322 //If lower_bound is greater than equivalent length of string, there can be no better match.
323 if(j == glyphs.end())
324 break;
325 const std::u32string& tmp2 = j->first;
326 bool best_found = false;
327 for(size_t k = 0; k < tmp2.length() && start + k < codepoints.length(); k++)
328 if(tmp2[k] > codepoints[start + k]) {
329 best_found = true;
330 break;
331 } else if(tmp2[k] < codepoints[start + k])
332 break;
333 if(best_found)
334 break;
336 return best;
339 const font2::glyph& font2::lookup_glyph(const std::u32string& key) const throw()
341 static glyph empty_glyph;
342 auto i = glyphs.find(key);
343 return (i == glyphs.end()) ? empty_glyph : i->second;
346 std::pair<uint32_t, uint32_t> font2::get_metrics(const std::u32string& str, uint32_t xalign) const
348 uint32_t w = 0;
349 uint32_t h = 0;
350 for_each_glyph(str, xalign, [&w, &h](uint32_t x, uint32_t y, const glyph& g) {
351 w = std::max(w, x + (uint32_t)g.width);
352 h = std::max(h, y + (uint32_t)g.height);
354 return std::make_pair(w, h);
357 void font2::for_each_glyph(const std::u32string& str, uint32_t xalign, std::function<void(uint32_t x, uint32_t y,
358 const glyph& g)> cb) const
360 uint32_t drawx = 0;
361 uint32_t orig_x = 0;
362 uint32_t drawy = 0;
363 for(size_t i = 0; i < str.size();) {
364 uint32_t cp = str[i];
365 std::u32string k = best_ligature_match(str, i);
366 const glyph& g = lookup_glyph(k);
367 if(k.length())
368 i += k.length();
369 else
370 i++;
371 if(cp == 9) {
372 drawx = (((drawx + xalign) + 64) >> 6 << 6) - xalign;
373 } else if(cp == 10) {
374 drawx = orig_x;
375 drawy += get_rowadvance();
376 } else {
377 cb(drawx, drawy, g);
378 drawx += g.width;
383 void font2::dump(const std::string& file) const
385 zip::writer w(file, 9);
386 for(auto& i : glyphs) {
387 std::string key = "";
388 if(i.first == U"")
389 key = "bad";
390 else {
391 std::ostringstream x;
392 auto len = i.first.length();
393 bool first = true;
394 for(unsigned j = 0; j < len; j++) {
395 if(!first) x << "-";
396 x << i.first[j];
397 first = false;
399 key = x.str();
401 std::ostream& s = w.create_file(key);
402 i.second.dump(s);
403 w.close_file();
405 w.commit();