Upload UI
[lsnes.git] / src / library / customfont.cpp
blob8fa07660ca52cd03d479bc8cea6b064869ad0c1b
1 #include "customfont.hpp"
2 #include "serialization.hpp"
3 #include <cstring>
4 #include "zip.hpp"
5 #include "string.hpp"
7 namespace
9 void bound(int32_t c, uint32_t odim, uint32_t dim, uint32_t& dc, uint32_t& off, uint32_t& size)
11 if(c >= (int32_t)dim || c + odim <= 0) {
12 //Outside the screen.
13 dc = 0;
14 off = 0;
15 size = 0;
16 } else if(c >= 0) {
17 dc = c;
18 off = 0;
19 size = odim;
20 } else {
21 dc = 0;
22 off = -c;
23 size = odim + c;
25 if(dc + size > dim)
26 size = dim - dc;
29 inline bool readfont(const font_glyph_data& glyph, uint32_t xp1, uint32_t yp1)
31 if(xp1 < 1 || xp1 > glyph.width || yp1 < 1 || yp1 > glyph.height)
32 return false;
33 xp1--;
34 yp1--;
35 size_t ge = yp1 * glyph.stride + (xp1 / 32);
36 size_t gb = 31 - xp1 % 32;
37 return ((glyph.glyph[ge] >> gb) & 1);
40 template<bool T> void _render(const font_glyph_data& glyph, framebuffer<T>& fb, int32_t x, int32_t y,
41 premultiplied_color fg, premultiplied_color bg, premultiplied_color hl)
43 uint32_t xdc, xoff, xsize;
44 uint32_t ydc, yoff, ysize;
45 if(hl) {
46 bound(x - 1, glyph.width + 2, fb.get_width(), xdc, xoff, xsize);
47 bound(y - 1, glyph.height + 2, fb.get_height(), ydc, yoff, ysize);
48 if(!xsize || !ysize)
49 return;
50 for(unsigned i = 0; i < ysize; i++) {
51 auto p = fb.rowptr(i + ydc) + xdc;
52 for(unsigned j = 0; j < xsize; j++) {
53 bool in_halo = false;
54 in_halo |= readfont(glyph, j + xoff - 1, i + yoff - 1);
55 in_halo |= readfont(glyph, j + xoff, i + yoff - 1);
56 in_halo |= readfont(glyph, j + xoff + 1, i + yoff - 1);
57 in_halo |= readfont(glyph, j + xoff - 1, i + yoff);
58 in_halo |= readfont(glyph, j + xoff + 1, i + yoff);
59 in_halo |= readfont(glyph, j + xoff - 1, i + yoff + 1);
60 in_halo |= readfont(glyph, j + xoff, i + yoff + 1);
61 in_halo |= readfont(glyph, j + xoff + 1, i + yoff + 1);
62 if(readfont(glyph, j + xoff, i + yoff))
63 fg.apply(p[j]);
64 else if(in_halo)
65 hl.apply(p[j]);
66 else
67 bg.apply(p[j]);
71 } else {
72 bound(x, glyph.width, fb.get_width(), xdc, xoff, xsize);
73 bound(y, glyph.height, fb.get_height(), ydc, yoff, ysize);
74 if(!xsize || !ysize)
75 return;
76 for(unsigned i = 0; i < ysize; i++) {
77 auto p = fb.rowptr(i + ydc) + xdc;
78 for(unsigned j = 0; j < xsize; j++) {
79 size_t ge = (i + yoff) * glyph.stride + ((j + xoff) / 32);
80 size_t gb = 31 - (j + xoff) % 32;
81 if((glyph.glyph[ge] >> gb) & 1)
82 fg.apply(p[j]);
83 else
84 bg.apply(p[j]);
91 font_glyph_data::font_glyph_data()
93 stride = width = height = 0;
96 font_glyph_data::font_glyph_data(std::istream& s)
98 char header[40];
99 bool old = true;
100 bool upside_down = true;
101 size_t rcount = 26;
102 s.read(header, 26);
103 if(!s)
104 throw std::runtime_error("Can't read glyph bitmap header");
105 if(read16ule(header + 0) != 0x4D42)
106 throw std::runtime_error("Bad glyph BMP magic");
107 if(read16ule(header + 14) != 12) {
108 //Not OS/2 format.
109 old = false;
110 rcount = 40;
111 s.read(header + 26, 14);
112 if(!s)
113 throw std::runtime_error("Can't read glyph bitmap header");
116 uint32_t startoff = read32ule(header + 10);
117 if(old) {
118 width = read16ule(header + 18);
119 height = read16ule(header + 20);
120 if(read16ule(header + 22) != 1)
121 throw std::runtime_error("Bad glyph BMP planecount");
122 if(read16ule(header + 24) != 1)
123 throw std::runtime_error("Bad glyph BMP bitdepth");
124 if(startoff < 26)
125 throw std::runtime_error("Glyph BMP data can't overlap header");
126 } else {
127 long _width = read32sle(header + 18);
128 long _height = read32sle(header + 22);
129 if(_width < 0)
130 throw std::runtime_error("Bad glyph BMP size");
131 if(_height < 0)
132 upside_down = false;
133 width = _width;
134 height = (_height >= 0) ? height : -height;
136 if(read16ule(header + 26) != 1)
137 throw std::runtime_error("Bad glyph BMP planecount");
138 if(read16ule(header + 28) != 1)
139 throw std::runtime_error("Bad glyph BMP bitdepth");
140 if(read32ule(header + 30) != 0)
141 throw std::runtime_error("Bad glyph BMP compression method");
142 if(startoff < 40)
143 throw std::runtime_error("Glyph BMP data can't overlap header");
145 //Discard data until start of bitmap.
146 while(rcount < startoff) {
147 s.get();
148 if(!s)
149 throw std::runtime_error("EOF while skipping to BMP data");
150 rcount++;
152 stride = (width + 31) / 32;
153 glyph.resize(stride * height);
154 memset(&glyph[0], 0, sizeof(uint32_t) * glyph.size());
155 size_t toskip = (4 - ((width + 7) / 8) % 4) % 4;
156 for(size_t i = 0; i < height; i++) {
157 size_t y = upside_down ? (height - i - 1) : i;
158 size_t bpos = y * stride * 32;
159 for(size_t j = 0; j < width; j += 8) {
160 size_t e = (bpos + j) / 32;
161 size_t b = (bpos + j) % 32;
162 int c = s.get();
163 if(!s)
164 throw std::runtime_error("EOF while reading BMP data");
165 glyph[e] |= ((uint32_t)c << (24 - b));
167 for(size_t j = 0; j < toskip; j++) {
168 s.get();
169 if(!s)
170 throw std::runtime_error("EOF while reading BMP data");
175 void font_glyph_data::render(framebuffer<false>& fb, int32_t x, int32_t y, premultiplied_color fg,
176 premultiplied_color bg, premultiplied_color hl) const
178 _render(*this, fb, x, y, fg, bg, hl);
181 void font_glyph_data::render(framebuffer<true>& fb, int32_t x, int32_t y, premultiplied_color fg,
182 premultiplied_color bg, premultiplied_color hl) const
184 _render(*this, fb, x, y, fg, bg, hl);
188 custom_font::custom_font()
190 rowadvance = 0;
193 custom_font::custom_font(const std::string& file)
195 std::istream* toclose = NULL;
196 rowadvance = 0;
197 try {
198 zip_reader r(file);
199 for(auto member : r) {
200 //Parse the key out of filename.
201 std::u32string key;
202 std::string tname = member;
203 std::string tmp;
204 if(tname == "bad") {
205 //Special, no key.
206 } else if(regex_match("[0-9]+(-[0-9]+)*", tname))
207 while(tname != "") {
208 extract_token(tname, tmp, "-");
209 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, font_glyph_data(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 std::ostream& operator<<(std::ostream& os, const std::u32string& lkey)
241 if(!lkey.length())
242 return (os << "bad");
243 for(size_t i = 0; i < lkey.length(); i++) {
244 if(i)
245 os << "-";
246 os << static_cast<uint32_t>(lkey[i]);
248 return os;
251 void custom_font::add(const std::u32string& key, const font_glyph_data& glyph) throw(std::bad_alloc)
253 glyphs[key] = glyph;
254 if(glyph.height > rowadvance)
255 rowadvance = glyph.height;
258 std::u32string custom_font::best_ligature_match(const std::u32string& codepoints, size_t start) const
259 throw(std::bad_alloc)
261 std::u32string tmp;
262 if(start >= codepoints.length())
263 return tmp; //Bad.
264 std::u32string best = tmp;
265 for(size_t i = 1; i <= codepoints.size() - start; i++) {
266 tmp.append(1, codepoints[start + i - 1]);
267 std::u32string lkey = tmp;
268 if(glyphs.count(lkey))
269 best = lkey;
270 auto j = glyphs.lower_bound(lkey);
271 //If lower_bound is greater than equivalent length of string, there can be no better match.
272 if(j == glyphs.end())
273 break;
274 const std::u32string& tmp2 = j->first;
275 bool best_found = false;
276 for(size_t k = 0; k < tmp2.length() && start + k < codepoints.length(); k++)
277 if(tmp2[k] > codepoints[start + k]) {
278 best_found = true;
279 break;
280 } else if(tmp2[k] < codepoints[start + k])
281 break;
282 if(best_found)
283 break;
285 return best;
288 const font_glyph_data& custom_font::lookup_glyph(const std::u32string& key) const throw()
290 static font_glyph_data empty_glyph;
291 auto i = glyphs.find(key);
292 return (i == glyphs.end()) ? empty_glyph : i->second;