lsnes rr2-β24
[lsnes.git] / src / library / framebuffer-font2.cpp
blob53d19712292b7e332699e867ed91ef23c974e865
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) throw(std::bad_alloc)
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
311 throw(std::bad_alloc)
313 std::u32string tmp;
314 if(start >= codepoints.length())
315 return tmp; //Bad.
316 std::u32string best = tmp;
317 for(size_t i = 1; i <= codepoints.size() - start; i++) {
318 tmp.append(1, codepoints[start + i - 1]);
319 std::u32string lkey = tmp;
320 if(glyphs.count(lkey))
321 best = lkey;
322 auto j = glyphs.lower_bound(lkey);
323 //If lower_bound is greater than equivalent length of string, there can be no better match.
324 if(j == glyphs.end())
325 break;
326 const std::u32string& tmp2 = j->first;
327 bool best_found = false;
328 for(size_t k = 0; k < tmp2.length() && start + k < codepoints.length(); k++)
329 if(tmp2[k] > codepoints[start + k]) {
330 best_found = true;
331 break;
332 } else if(tmp2[k] < codepoints[start + k])
333 break;
334 if(best_found)
335 break;
337 return best;
340 const font2::glyph& font2::lookup_glyph(const std::u32string& key) const throw()
342 static glyph empty_glyph;
343 auto i = glyphs.find(key);
344 return (i == glyphs.end()) ? empty_glyph : i->second;
347 std::pair<uint32_t, uint32_t> font2::get_metrics(const std::u32string& str, uint32_t xalign) const
349 uint32_t w = 0;
350 uint32_t h = 0;
351 for_each_glyph(str, xalign, [&w, &h](uint32_t x, uint32_t y, const glyph& g) {
352 w = std::max(w, x + (uint32_t)g.width);
353 h = std::max(h, y + (uint32_t)g.height);
355 return std::make_pair(w, h);
358 void font2::for_each_glyph(const std::u32string& str, uint32_t xalign, std::function<void(uint32_t x, uint32_t y,
359 const glyph& g)> cb) const
361 uint32_t drawx = 0;
362 uint32_t orig_x = 0;
363 uint32_t drawy = 0;
364 for(size_t i = 0; i < str.size();) {
365 uint32_t cp = str[i];
366 std::u32string k = best_ligature_match(str, i);
367 const glyph& g = lookup_glyph(k);
368 if(k.length())
369 i += k.length();
370 else
371 i++;
372 if(cp == 9) {
373 drawx = (((drawx + xalign) + 64) >> 6 << 6) - xalign;
374 } else if(cp == 10) {
375 drawx = orig_x;
376 drawy += get_rowadvance();
377 } else {
378 cb(drawx, drawy, g);
379 drawx += g.width;
384 void font2::dump(const std::string& file) const
386 zip::writer w(file, 9);
387 for(auto& i : glyphs) {
388 std::string key = "";
389 if(i.first == U"")
390 key = "bad";
391 else {
392 std::ostringstream x;
393 auto len = i.first.length();
394 bool first = true;
395 for(unsigned j = 0; j < len; j++) {
396 if(!first) x << "-";
397 x << i.first[j];
398 first = false;
400 key = x.str();
402 std::ostream& s = w.create_file(key);
403 i.second.dump(s);
404 w.close_file();
406 w.commit();