Some tweaks to Lua docs
[lsnes.git] / src / library / framebuffer-font2.cpp
blob6fc9e812309a1b1fcae41c706b82e5b5a3996d04
1 #include "framebuffer-font2.hpp"
2 #include "serialization.hpp"
3 #include <cstring>
4 #include "zip.hpp"
5 #include "string.hpp"
7 namespace framebuffer
9 namespace
11 void bound(int32_t c, uint32_t odim, uint32_t dim, uint32_t& dc, uint32_t& off, uint32_t& size)
13 if(c >= (int32_t)dim || c + odim <= 0) {
14 //Outside the screen.
15 dc = 0;
16 off = 0;
17 size = 0;
18 } else if(c >= 0) {
19 dc = c;
20 off = 0;
21 size = odim;
22 } else {
23 dc = 0;
24 off = -c;
25 size = odim + c;
27 if(dc + size > dim)
28 size = dim - dc;
31 inline bool readfont(const font2::glyph& fglyph, uint32_t xp1, uint32_t yp1)
33 if(xp1 < 1 || xp1 > fglyph.width || yp1 < 1 || yp1 > fglyph.height)
34 return false;
35 xp1--;
36 yp1--;
37 size_t ge = yp1 * fglyph.stride + (xp1 / 32);
38 size_t gb = 31 - xp1 % 32;
39 return ((fglyph.fglyph[ge] >> gb) & 1);
42 template<bool T> void _render(const font2::glyph& fglyph, fb<T>& fb, int32_t x, int32_t y,
43 color fg, color bg, color hl)
45 uint32_t xdc, xoff, xsize;
46 uint32_t ydc, yoff, ysize;
47 if(hl) {
48 bound(x - 1, fglyph.width + 2, fb.get_width(), xdc, xoff, xsize);
49 bound(y - 1, fglyph.height + 2, fb.get_height(), ydc, yoff, ysize);
50 if(!xsize || !ysize)
51 return;
52 for(unsigned i = 0; i < ysize; i++) {
53 auto p = fb.rowptr(i + ydc) + xdc;
54 for(unsigned j = 0; j < xsize; j++) {
55 bool in_halo = false;
56 in_halo |= readfont(fglyph, j + xoff - 1, i + yoff - 1);
57 in_halo |= readfont(fglyph, j + xoff, i + yoff - 1);
58 in_halo |= readfont(fglyph, j + xoff + 1, i + yoff - 1);
59 in_halo |= readfont(fglyph, j + xoff - 1, i + yoff);
60 in_halo |= readfont(fglyph, j + xoff + 1, i + yoff);
61 in_halo |= readfont(fglyph, j + xoff - 1, i + yoff + 1);
62 in_halo |= readfont(fglyph, j + xoff, i + yoff + 1);
63 in_halo |= readfont(fglyph, j + xoff + 1, i + yoff + 1);
64 if(readfont(fglyph, j + xoff, i + yoff))
65 fg.apply(p[j]);
66 else if(in_halo)
67 hl.apply(p[j]);
68 else
69 bg.apply(p[j]);
73 } else {
74 bound(x, fglyph.width, fb.get_width(), xdc, xoff, xsize);
75 bound(y, fglyph.height, fb.get_height(), ydc, yoff, ysize);
76 if(!xsize || !ysize)
77 return;
78 for(unsigned i = 0; i < ysize; i++) {
79 auto p = fb.rowptr(i + ydc) + xdc;
80 for(unsigned j = 0; j < xsize; j++) {
81 size_t ge = (i + yoff) * fglyph.stride + ((j + xoff) / 32);
82 size_t gb = 31 - (j + xoff) % 32;
83 if((fglyph.fglyph[ge] >> gb) & 1)
84 fg.apply(p[j]);
85 else
86 bg.apply(p[j]);
93 font2::glyph::glyph()
95 stride = width = height = 0;
98 font2::glyph::glyph(std::istream& s)
100 char header[40];
101 bool old = true;
102 bool upside_down = true;
103 size_t rcount = 26;
104 s.read(header, 26);
105 if(!s)
106 throw std::runtime_error("Can't read glyph bitmap header");
107 if(serialization::u16l(header + 0) != 0x4D42)
108 throw std::runtime_error("Bad glyph BMP magic");
109 if(serialization::u16l(header + 14) != 12) {
110 //Not OS/2 format.
111 old = false;
112 rcount = 40;
113 s.read(header + 26, 14);
114 if(!s)
115 throw std::runtime_error("Can't read glyph bitmap header");
118 uint32_t startoff = serialization::u32l(header + 10);
119 if(old) {
120 width = serialization::u16l(header + 18);
121 height = serialization::u16l(header + 20);
122 if(serialization::u16l(header + 22) != 1)
123 throw std::runtime_error("Bad glyph BMP planecount");
124 if(serialization::u16l(header + 24) != 1)
125 throw std::runtime_error("Bad glyph BMP bitdepth");
126 if(startoff < 26)
127 throw std::runtime_error("Glyph BMP data can't overlap header");
128 } else {
129 long _width = serialization::s32l(header + 18);
130 long _height = serialization::s32l(header + 22);
131 if(_width < 0)
132 throw std::runtime_error("Bad glyph BMP size");
133 if(_height < 0)
134 upside_down = false;
135 width = _width;
136 height = (_height >= 0) ? height : -height;
138 if(serialization::u16l(header + 26) != 1)
139 throw std::runtime_error("Bad glyph BMP planecount");
140 if(serialization::u16l(header + 28) != 1)
141 throw std::runtime_error("Bad glyph BMP bitdepth");
142 if(serialization::u32l(header + 30) != 0)
143 throw std::runtime_error("Bad glyph BMP compression method");
144 if(startoff < 40)
145 throw std::runtime_error("Glyph BMP data can't overlap header");
147 //Discard data until start of bitmap.
148 while(rcount < startoff) {
149 s.get();
150 if(!s)
151 throw std::runtime_error("EOF while skipping to BMP data");
152 rcount++;
154 stride = (width + 31) / 32;
155 fglyph.resize(stride * height);
156 memset(&fglyph[0], 0, sizeof(uint32_t) * fglyph.size());
157 size_t toskip = (4 - ((width + 7) / 8) % 4) % 4;
158 for(size_t i = 0; i < height; i++) {
159 size_t y = upside_down ? (height - i - 1) : i;
160 size_t bpos = y * stride * 32;
161 for(size_t j = 0; j < width; j += 8) {
162 size_t e = (bpos + j) / 32;
163 size_t b = (bpos + j) % 32;
164 int c = s.get();
165 if(!s)
166 throw std::runtime_error("EOF while reading BMP data");
167 fglyph[e] |= ((uint32_t)c << (24 - b));
169 for(size_t j = 0; j < toskip; j++) {
170 s.get();
171 if(!s)
172 throw std::runtime_error("EOF while reading BMP data");
177 void font2::glyph::render(fb<false>& fb, int32_t x, int32_t y, color fg,
178 color bg, color hl) const
180 _render(*this, fb, x, y, fg, bg, hl);
183 void font2::glyph::render(fb<true>& fb, int32_t x, int32_t y, color fg,
184 color bg, color hl) const
186 _render(*this, fb, x, y, fg, bg, hl);
190 font2::font2()
192 rowadvance = 0;
195 font2::font2(const std::string& file)
197 std::istream* toclose = NULL;
198 rowadvance = 0;
199 try {
200 zip::reader r(file);
201 for(auto member : r) {
202 //Parse the key out of filename.
203 std::u32string key;
204 std::string tname = member;
205 std::string tmp;
206 if(tname == "bad") {
207 //Special, no key.
208 } else if(regex_match("[0-9]+(-[0-9]+)*", tname))
209 for(auto& tmp : token_iterator_foreach(tname, {"-"}))
210 key.append(1, parse_value<uint32_t>(tmp));
211 else {
212 delete toclose;
213 toclose = NULL;
214 continue;
216 std::istream& s = r[member];
217 toclose = &s;
218 try {
219 add(key, glyph(s));
220 } catch(std::bad_alloc& e) {
221 throw;
222 } catch(std::exception& e) {
223 throw std::runtime_error(tname + std::string(": ") + e.what());
225 delete toclose;
226 toclose = NULL;
228 } catch(std::bad_alloc& e) {
229 if(toclose)
230 delete toclose;
231 throw;
232 } catch(std::exception& e) {
233 if(toclose)
234 delete toclose;
235 throw std::runtime_error(std::string("Error reading font: ") + e.what());
239 font2::font2(struct font& bfont)
241 auto s = bfont.get_glyphs_set();
242 for(auto i = s.begin();;i++) {
243 const font::glyph& j = (i != s.end()) ? bfont.get_glyph(*i) : bfont.get_bad_glyph();
244 glyph k;
245 k.width = j.wide ? 16 : 8;
246 k.height = 16;
247 k.stride = 1;
248 k.fglyph.resize(16);
249 for(size_t y = 0; y < 16; y++) {
250 k.fglyph[y] = 0;
251 uint32_t r = j.data[y / (j.wide ? 2 : 4)];
252 if(j.wide)
253 r >>= 16 - ((y & 1) << 4);
254 else
255 r >>= 24 - ((y & 3) << 3);
256 for(size_t x = 0; x < k.width; x++) {
257 uint32_t b = (j.wide ? 15 : 7) - x;
258 if(((r >> b) & 1) != 0)
259 k.fglyph[y] |= 1UL << (31 - x);
262 std::u32string key = (i != s.end()) ? std::u32string(1, *i) : std::u32string();
263 glyphs[key] = k;
264 if(i == s.end()) break;
266 rowadvance = 16;
269 std::ostream& operator<<(std::ostream& os, const std::u32string& lkey)
271 if(!lkey.length())
272 return (os << "bad");
273 for(size_t i = 0; i < lkey.length(); i++) {
274 if(i)
275 os << "-";
276 os << static_cast<uint32_t>(lkey[i]);
278 return os;
281 void font2::add(const std::u32string& key, const glyph& fglyph) throw(std::bad_alloc)
283 glyphs[key] = fglyph;
284 if(fglyph.height > rowadvance)
285 rowadvance = fglyph.height;
288 std::u32string font2::best_ligature_match(const std::u32string& codepoints, size_t start) const
289 throw(std::bad_alloc)
291 std::u32string tmp;
292 if(start >= codepoints.length())
293 return tmp; //Bad.
294 std::u32string best = tmp;
295 for(size_t i = 1; i <= codepoints.size() - start; i++) {
296 tmp.append(1, codepoints[start + i - 1]);
297 std::u32string lkey = tmp;
298 if(glyphs.count(lkey))
299 best = lkey;
300 auto j = glyphs.lower_bound(lkey);
301 //If lower_bound is greater than equivalent length of string, there can be no better match.
302 if(j == glyphs.end())
303 break;
304 const std::u32string& tmp2 = j->first;
305 bool best_found = false;
306 for(size_t k = 0; k < tmp2.length() && start + k < codepoints.length(); k++)
307 if(tmp2[k] > codepoints[start + k]) {
308 best_found = true;
309 break;
310 } else if(tmp2[k] < codepoints[start + k])
311 break;
312 if(best_found)
313 break;
315 return best;
318 const font2::glyph& font2::lookup_glyph(const std::u32string& key) const throw()
320 static glyph empty_glyph;
321 auto i = glyphs.find(key);
322 return (i == glyphs.end()) ? empty_glyph : i->second;