Use std::u32string instead of std::vector<uint32_t> for UTF-32 strings
[lsnes.git] / src / library / customfont.cpp
blob100ff169b44bd0c0bf981b3da761620b9f52c2e1
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 template<bool T> void _render(const font_glyph_data& glyph, framebuffer<T>& fb, int32_t x, int32_t y,
30 premultiplied_color fg, premultiplied_color bg)
32 uint32_t xdc, xoff, xsize;
33 uint32_t ydc, yoff, ysize;
34 bound(x, glyph.width, fb.get_width(), xdc, xoff, xsize);
35 bound(y, glyph.height, fb.get_height(), ydc, yoff, ysize);
36 if(!xsize || !ysize)
37 return;
38 for(unsigned i = 0; i < ysize; i++) {
39 auto p = fb.rowptr(i + ydc);
40 for(unsigned j = 0; j < xsize; j++) {
41 size_t ge = (i + yoff) * glyph.stride + ((j + xoff) / 32);
42 size_t gb = 31 - (j + xoff) % 32;
43 if((glyph.glyph[ge] >> gb) & 1)
44 fg.apply(p[j + xdc]);
45 else
46 bg.apply(p[j + xdc]);
52 font_glyph_data::font_glyph_data()
54 stride = width = height = 0;
57 font_glyph_data::font_glyph_data(std::istream& s)
59 char header[40];
60 bool old = true;
61 bool upside_down = true;
62 size_t rcount = 26;
63 s.read(header, 26);
64 if(!s)
65 throw std::runtime_error("Can't read glyph bitmap header");
66 if(read16ule(header + 0) != 0x4D42)
67 throw std::runtime_error("Bad glyph BMP magic");
68 if(read16ule(header + 14) != 12) {
69 //Not OS/2 format.
70 old = false;
71 rcount = 40;
72 s.read(header + 26, 14);
73 if(!s)
74 throw std::runtime_error("Can't read glyph bitmap header");
77 uint32_t startoff = read32ule(header + 10);
78 if(old) {
79 width = read16ule(header + 18);
80 height = read16ule(header + 20);
81 if(read16ule(header + 22) != 1)
82 throw std::runtime_error("Bad glyph BMP planecount");
83 if(read16ule(header + 24) != 1)
84 throw std::runtime_error("Bad glyph BMP bitdepth");
85 if(startoff < 26)
86 throw std::runtime_error("Glyph BMP data can't overlap header");
87 } else {
88 long _width = read32sle(header + 18);
89 long _height = read32sle(header + 22);
90 if(_width < 0)
91 throw std::runtime_error("Bad glyph BMP size");
92 if(_height < 0)
93 upside_down = false;
94 width = _width;
95 height = (_height >= 0) ? height : -height;
97 if(read16ule(header + 26) != 1)
98 throw std::runtime_error("Bad glyph BMP planecount");
99 if(read16ule(header + 28) != 1)
100 throw std::runtime_error("Bad glyph BMP bitdepth");
101 if(read32ule(header + 30) != 0)
102 throw std::runtime_error("Bad glyph BMP compression method");
103 if(startoff < 40)
104 throw std::runtime_error("Glyph BMP data can't overlap header");
106 //Discard data until start of bitmap.
107 while(rcount < startoff) {
108 s.get();
109 if(!s)
110 throw std::runtime_error("EOF while skipping to BMP data");
111 rcount++;
113 stride = (width + 31) / 32;
114 glyph.resize(stride * height);
115 memset(&glyph[0], 0, sizeof(uint32_t) * glyph.size());
116 size_t toskip = (4 - ((width + 7) / 8) % 4) % 4;
117 for(size_t i = 0; i < height; i++) {
118 size_t y = upside_down ? (height - i - 1) : i;
119 size_t bpos = y * stride * 32;
120 for(size_t j = 0; j < width; j += 8) {
121 size_t e = (bpos + j) / 32;
122 size_t b = (bpos + j) % 32;
123 int c = s.get();
124 if(!s)
125 throw std::runtime_error("EOF while reading BMP data");
126 glyph[e] |= ((uint32_t)c << (24 - b));
128 for(size_t j = 0; j < toskip; j++) {
129 s.get();
130 if(!s)
131 throw std::runtime_error("EOF while reading BMP data");
136 void font_glyph_data::render(framebuffer<false>& fb, int32_t x, int32_t y, premultiplied_color fg,
137 premultiplied_color bg) const
139 _render(*this, fb, x, y, fg, bg);
142 void font_glyph_data::render(framebuffer<true>& fb, int32_t x, int32_t y, premultiplied_color fg,
143 premultiplied_color bg) const
145 _render(*this, fb, x, y, fg, bg);
149 custom_font::custom_font()
151 rowadvance = 0;
154 custom_font::custom_font(const std::string& file)
156 std::istream* toclose = NULL;
157 rowadvance = 0;
158 try {
159 zip_reader r(file);
160 for(auto member : r) {
161 //Parse the key out of filename.
162 std::u32string key;
163 std::string tname = member;
164 std::string tmp;
165 if(tname == "bad") {
166 //Special, no key.
167 } else if(regex_match("[0-9]+(-[0-9]+)*", tname))
168 while(tname != "") {
169 extract_token(tname, tmp, "-");
170 key.append(1, parse_value<uint32_t>(tmp));
172 else {
173 delete toclose;
174 toclose = NULL;
175 continue;
177 std::istream& s = r[member];
178 toclose = &s;
179 try {
180 add(key, font_glyph_data(s));
181 } catch(std::bad_alloc& e) {
182 throw;
183 } catch(std::exception& e) {
184 throw std::runtime_error(tname + std::string(": ") + e.what());
186 delete toclose;
187 toclose = NULL;
189 } catch(std::bad_alloc& e) {
190 if(toclose)
191 delete toclose;
192 throw;
193 } catch(std::exception& e) {
194 if(toclose)
195 delete toclose;
196 throw std::runtime_error(std::string("Error reading font: ") + e.what());
200 std::ostream& operator<<(std::ostream& os, const std::u32string& lkey)
202 if(!lkey.length())
203 return (os << "bad");
204 for(size_t i = 0; i < lkey.length(); i++) {
205 if(i)
206 os << "-";
207 os << static_cast<uint32_t>(lkey[i]);
209 return os;
212 void custom_font::add(const std::u32string& key, const font_glyph_data& glyph) throw(std::bad_alloc)
214 glyphs[key] = glyph;
215 if(glyph.height > rowadvance)
216 rowadvance = glyph.height;
219 std::u32string custom_font::best_ligature_match(const std::u32string& codepoints, size_t start) const
220 throw(std::bad_alloc)
222 std::u32string tmp;
223 if(start >= codepoints.length())
224 return tmp; //Bad.
225 std::u32string best = tmp;
226 for(size_t i = 1; i <= codepoints.size() - start; i++) {
227 tmp.append(1, codepoints[start + i - 1]);
228 std::u32string lkey = tmp;
229 if(glyphs.count(lkey))
230 best = lkey;
231 auto j = glyphs.lower_bound(lkey);
232 //If lower_bound is greater than equivalent length of string, there can be no better match.
233 if(j == glyphs.end())
234 break;
235 const std::u32string& tmp2 = j->first;
236 bool best_found = false;
237 for(size_t k = 0; k < tmp2.length() && start + k < codepoints.length(); k++)
238 if(tmp2[k] > codepoints[start + k]) {
239 best_found = true;
240 break;
241 } else if(tmp2[k] < codepoints[start + k])
242 break;
243 if(best_found)
244 break;
246 return best;
249 const font_glyph_data& custom_font::lookup_glyph(const std::u32string& key) const throw()
251 static font_glyph_data empty_glyph;
252 auto i = glyphs.find(key);
253 return (i == glyphs.end()) ? empty_glyph : i->second;