lsnes rr0-β15
[lsnes.git] / generic / render.cpp
bloba1c6ed65d223c5dd789ee5b113983e177c0cae59
1 #include "lsnes.hpp"
2 #include <snes/snes.hpp>
4 #include "render.hpp"
5 #include "png.hpp"
6 #include <sstream>
7 #include <list>
8 #include <iomanip>
9 #include <cstdint>
10 #include <string>
11 typedef uint8_t uint8;
12 typedef uint16_t uint16;
13 typedef uint32_t uint32;
14 typedef int8_t int8;
15 typedef int16_t int16;
16 typedef int32_t int32;
17 #include <nall/platform.hpp>
18 #include <nall/endian.hpp>
19 #include <nall/varint.hpp>
20 #include <nall/bit.hpp>
21 #include <nall/serializer.hpp>
22 #include <nall/property.hpp>
23 using namespace nall;
24 #include <ui-libsnes/libsnes.hpp>
25 using namespace SNES;
28 extern uint32_t fontdata[];
30 namespace
32 //This is Jenkin's MIX function.
33 uint32_t keyhash(uint32_t key, uint32_t item, uint32_t mod) throw()
35 uint32_t a = key;
36 uint32_t b = 0;
37 uint32_t c = item;
38 a=a-b; a=a-c; a=a^(c >> 13);
39 b=b-c; b=b-a; b=b^(a << 8);
40 c=c-a; c=c-b; c=c^(b >> 13);
41 a=a-b; a=a-c; a=a^(c >> 12);
42 b=b-c; b=b-a; b=b^(a << 16);
43 c=c-a; c=c-b; c=c^(b >> 5);
44 a=a-b; a=a-c; a=a^(c >> 3);
45 b=b-c; b=b-a; b=b^(a << 10);
46 c=c-a; c=c-b; c=c^(b >> 15);
47 return c % mod;
51 //Locate glyph in font. Returns <width, offset> pair. Zero offset should be interpretted as an empty
52 //glyph.
53 std::pair<uint32_t, size_t> find_glyph(uint32_t codepoint, int32_t x, int32_t y, int32_t orig_x,
54 int32_t& next_x, int32_t& next_y) throw()
56 uint32_t cwidth = 0;
57 if(codepoint == 9) {
58 cwidth = 64 - (x - orig_x) % 64;
59 next_x = x + cwidth;
60 next_y = y;
61 return std::make_pair(cwidth, 0);
62 } else if(codepoint == 10) {
63 next_x = orig_x;
64 next_y = y + 16;
65 return std::make_pair(0, 0);
66 } else if(codepoint == 32) {
67 next_x = x + 8;
68 next_y = y;
69 return std::make_pair(8, 0);
70 } else {
71 uint32_t mdir = fontdata[0];
72 uint32_t mseed = fontdata[mdir];
73 uint32_t msize = fontdata[mdir + 1];
74 uint32_t midx = keyhash(mseed, codepoint, msize);
75 uint32_t sdir = fontdata[mdir + 2 + midx];
76 if(!fontdata[sdir + 1]) {
77 //Character not found.
78 next_x = x + 8;
79 next_y = y;
80 return std::make_pair(8, 0);
82 uint32_t sseed = fontdata[sdir];
83 uint32_t ssize = fontdata[sdir + 1];
84 uint32_t sidx = keyhash(sseed, codepoint, ssize);
85 if(fontdata[sdir + 2 + 2 * sidx] != codepoint) {
86 //Character not found.
87 next_x = x + 8;
88 next_y = y;
89 return std::make_pair(8, 0);
91 bool wide = (fontdata[fontdata[sdir + 2 + 2 * sidx + 1]] != 0);
92 next_x = x + (wide ? 16 : 8);
93 next_y = y;
94 return std::make_pair(wide ? 16 : 8, fontdata[sdir + 2 + 2 * sidx + 1] + 1);
98 render_object::~render_object() throw()
102 void render_text(struct screen& scr, int32_t x, int32_t y, const std::string& text, premultiplied_color fg,
103 premultiplied_color bg) throw(std::bad_alloc)
105 int32_t orig_x = x;
106 uint32_t unicode_code = 0;
107 uint8_t unicode_left = 0;
108 for(size_t i = 0; i < text.length(); i++) {
109 uint8_t ch = text[i];
110 if(ch < 128)
111 unicode_code = text[i];
112 else if(ch < 192) {
113 if(!unicode_left)
114 continue;
115 unicode_code = 64 * unicode_code + ch - 128;
116 if(--unicode_left)
117 continue;
118 } else if(ch < 224) {
119 unicode_code = ch - 192;
120 unicode_left = 1;
121 continue;
122 } else if(ch < 240) {
123 unicode_code = ch - 224;
124 unicode_left = 2;
125 continue;
126 } else if(ch < 248) {
127 unicode_code = ch - 240;
128 unicode_left = 3;
129 continue;
130 } else
131 continue;
132 int32_t next_x, next_y;
133 auto p = find_glyph(unicode_code, x, y, orig_x, next_x, next_y);
134 uint32_t dx = 0;
135 uint32_t dw = p.first;
136 uint32_t dy = 0;
137 uint32_t dh = 16;
138 uint32_t cx = static_cast<uint32_t>(static_cast<int32_t>(scr.originx) + x);
139 uint32_t cy = static_cast<uint32_t>(static_cast<int32_t>(scr.originy) + y);
140 while(cx > scr.width && dw > 0) {
141 dx++;
142 dw--;
143 cx++;
145 while(cy > scr.height && dh > 0) {
146 dy++;
147 dh--;
148 cy++;
150 while(cx + dw > scr.width && dw > 0)
151 dw--;
152 while(cy + dh > scr.height && dh > 0)
153 dh--;
154 if(!dw || !dh)
155 continue; //Outside screen.
157 if(p.second == 0) {
158 //Blank glyph.
159 for(uint32_t j = 0; j < dh; j++) {
160 uint16_t* base = scr.rowptr(cy + j) + cx;
161 for(uint32_t i = 0; i < dw; i++)
162 bg.apply(base[i]);
164 } else {
165 //narrow/wide glyph.
166 for(uint32_t j = 0; j < dh; j++) {
167 uint32_t dataword = fontdata[p.second + (dy + j) / (32 / p.first)];
168 uint16_t* base = scr.rowptr(cy + j) + cx;
169 for(uint32_t i = 0; i < dw; i++)
170 if(((dataword >> (31 - ((dy + j) % (32 / p.first)) * p.first - (dx + i))) & 1))
171 fg.apply(base[i]);
172 else
173 bg.apply(base[i]);
176 x = next_x;
177 y = next_y;
181 void render_queue::add(struct render_object& obj) throw(std::bad_alloc)
183 q.push_back(&obj);
186 void render_queue::run(struct screen& scr) throw()
188 for(auto i = q.begin(); i != q.end(); i++) {
189 try {
190 (**i)(scr);
191 } catch(...) {
193 delete *i;
195 q.clear();
198 void render_queue::clear() throw()
200 for(auto i = q.begin(); i != q.end(); i++)
201 delete *i;
202 q.clear();
205 render_queue::~render_queue() throw()
207 clear();
210 uint16_t screen::make_color(uint8_t r, uint8_t g, uint8_t b) throw()
212 uint16_t _r = r / 8;
213 uint16_t _g = g / 8;
214 uint16_t _b = b / 8;
215 return (_r << 10) + (_g << 5) + _b;
218 lcscreen::lcscreen(const uint16_t* mem, bool hires, bool interlace, bool overscan, bool region) throw()
220 uint32_t dataoffset = 0;
221 width = hires ? 512 : 256;
222 height = 0;
223 if(region) {
224 //PAL.
225 height = 239;
226 dataoffset = overscan ? 9 : 1;
227 } else {
228 //presumably NTSC.
229 height = 224;
230 dataoffset = overscan ? 16 : 9;
232 if(interlace)
233 height <<= 1;
234 memory = mem + dataoffset * 1024;
235 pitch = interlace ? 512 : 1024;
236 user_memory = false;
239 lcscreen::lcscreen(const uint16_t* mem, uint32_t _width, uint32_t _height) throw()
241 width = _width;
242 height = _height;
243 memory = mem;
244 pitch = width;
245 user_memory = false;
248 lcscreen::lcscreen() throw()
250 width = 0;
251 height = 0;
252 memory = NULL;
253 user_memory = true;
254 pitch = 0;
255 allocated = 0;
258 lcscreen::lcscreen(const lcscreen& ls) throw(std::bad_alloc)
260 width = ls.width;
261 height = ls.height;
262 pitch = width;
263 user_memory = true;
264 allocated = static_cast<size_t>(width) * height;
265 memory = new uint16_t[allocated];
266 for(size_t l = 0; l < height; l++)
267 memcpy(const_cast<uint16_t*>(memory + l * width), ls.memory + l * ls.pitch, 2 * width);
270 lcscreen& lcscreen::operator=(const lcscreen& ls) throw(std::bad_alloc, std::runtime_error)
272 if(!user_memory)
273 throw std::runtime_error("Can't copy to non-user memory");
274 if(this == &ls)
275 return *this;
276 if(allocated < static_cast<size_t>(ls.width) * ls.height) {
277 size_t p_allocated = static_cast<size_t>(ls.width) * ls.height;
278 memory = new uint16_t[p_allocated];
279 allocated = p_allocated;
281 width = ls.width;
282 height = ls.height;
283 pitch = width;
284 for(size_t l = 0; l < height; l++)
285 memcpy(const_cast<uint16_t*>(memory + l * width), ls.memory + l * ls.pitch, 2 * width);
286 return *this;
289 lcscreen::~lcscreen()
291 if(user_memory)
292 delete[] const_cast<uint16_t*>(memory);
295 void lcscreen::load(const std::vector<char>& data) throw(std::bad_alloc, std::runtime_error)
297 if(!user_memory)
298 throw std::runtime_error("Can't load to non-user memory");
299 const uint8_t* data2 = reinterpret_cast<const uint8_t*>(&data[0]);
300 if(data.size() < 2)
301 throw std::runtime_error("Corrupt saved screenshot data");
302 uint32_t _width = static_cast<uint32_t>(data2[0]) * 256 + static_cast<uint32_t>(data2[1]);
303 if(_width > 1 && data.size() % (2 * _width) != 2)
304 throw std::runtime_error("Corrupt saved screenshot data");
305 uint32_t _height = (data.size() - 2) / (2 * _width);
306 if(allocated < static_cast<size_t>(_width) * _height) {
307 size_t p_allocated = static_cast<size_t>(_width) * _height;
308 memory = new uint16_t[p_allocated];
309 allocated = p_allocated;
311 uint16_t* mem = const_cast<uint16_t*>(memory);
312 width = _width;
313 height = _height;
314 pitch = width;
315 for(size_t i = 0; i < (data.size() - 2) / 2; i++)
316 mem[i] = static_cast<uint16_t>(data2[2 + 2 * i]) * 256 +
317 static_cast<uint16_t>(data2[2 + 2 * i + 1]);
320 void lcscreen::save(std::vector<char>& data) throw(std::bad_alloc)
322 data.resize(2 + 2 * static_cast<size_t>(width) * height);
323 uint8_t* data2 = reinterpret_cast<uint8_t*>(&data[0]);
324 data2[0] = (width >> 8);
325 data2[1] = width;
326 for(size_t i = 0; i < (data.size() - 2) / 2; i++) {
327 data[2 + 2 * i] = memory[(i / width) * pitch + (i % width)] >> 8;
328 data[2 + 2 * i + 1] = memory[(i / width) * pitch + (i % width)];
332 void lcscreen::save_png(const std::string& file) throw(std::bad_alloc, std::runtime_error)
334 unsigned char clevels[32];
335 for(unsigned i = 0; i < 32; i++)
336 clevels[i] = 255 * i / 31;
337 uint8_t* buffer = new uint8_t[3 * static_cast<size_t>(width) * height];
338 for(uint32_t j = 0; j < height; j++)
339 for(uint32_t i = 0; i < width; i++) {
340 uint16_t word = memory[pitch * j + i];
341 buffer[3 * static_cast<size_t>(width) * j + 3 * i + 0] = clevels[(word >> 10) & 0x1F];
342 buffer[3 * static_cast<size_t>(width) * j + 3 * i + 1] = clevels[(word >> 5) & 0x1F];
343 buffer[3 * static_cast<size_t>(width) * j + 3 * i + 2] = clevels[(word) & 0x1F];
345 try {
346 save_png_data(file, buffer, width, height);
347 delete[] buffer;
348 } catch(...) {
349 delete[] buffer;
350 throw;
354 void screen::copy_from(lcscreen& scr, uint32_t hscale, uint32_t vscale) throw()
356 uint32_t copyable_width = (width - originx) / hscale;
357 uint32_t copyable_height = (height - originy) / vscale;
358 copyable_width = (copyable_width > scr.width) ? scr.width : copyable_width;
359 copyable_height = (copyable_height > scr.height) ? scr.height : copyable_height;
360 for(uint32_t y = 0; y < height; y++) {
361 memset(rowptr(y), 0, 2 * width);
363 for(uint32_t y = 0; y < copyable_height; y++) {
364 uint32_t line = y * vscale + originy;
365 uint16_t* ptr = rowptr(line) + originx;
366 const uint16_t* sbase = scr.memory + y * scr.pitch;
367 for(uint32_t x = 0; x < copyable_width; x++) {
368 uint16_t c = sbase[x] % 32768;
369 for(uint32_t i = 0; i < hscale; i++)
370 *(ptr++) = c;
372 for(uint32_t j = 1; j < vscale; j++)
373 memcpy(rowptr(line + j) + originx, rowptr(line) + originx, 2 * hscale * copyable_width);
377 void screen::reallocate(uint32_t _width, uint32_t _height, uint32_t _originx, uint32_t _originy, bool upside_down)
378 throw(std::bad_alloc)
380 if(_width == width && _height == height) {
381 originx = _originx;
382 originy = _originy;
383 return;
385 if(!_width || !_height) {
386 width = height = originx = originy = pitch = 0;
387 if(memory && !user_memory)
388 delete[] memory;
389 memory = NULL;
390 user_memory = false;
391 flipped = upside_down;
392 return;
394 uint16_t* newmem = new uint16_t[_width * _height];
395 width = _width;
396 height = _height;
397 originx = _originx;
398 originy = _originy;
399 pitch = 2 * _width;
400 if(memory && !user_memory)
401 delete[] memory;
402 memory = newmem;
403 user_memory = false;
404 flipped = upside_down;
407 void screen::set(uint16_t* _memory, uint32_t _width, uint32_t _height, uint32_t _originx, uint32_t _originy,
408 uint32_t _pitch) throw()
410 if(memory && !user_memory)
411 delete[] memory;
412 width = _width;
413 height = _height;
414 originx = _originx;
415 originy = _originy;
416 pitch = _pitch;
417 user_memory = true;
418 memory = _memory;
419 flipped = false;
422 uint16_t* screen::rowptr(uint32_t row) throw()
424 if(flipped)
425 row = height - row - 1;
426 return reinterpret_cast<uint16_t*>(reinterpret_cast<uint8_t*>(memory) + row * pitch);
429 screen::screen() throw()
431 memory = NULL;
432 width = height = originx = originy = pitch = 0;
433 user_memory = false;
434 flipped = false;
437 screen::~screen() throw()
439 if(memory && !user_memory)
440 delete[] memory;
443 void clip_range(uint32_t origin, uint32_t size, int32_t base, int32_t& minc, int32_t& maxc) throw()
445 int64_t _origin = origin;
446 int64_t _size = size;
447 int64_t _base = base;
448 int64_t _minc = minc;
449 int64_t _maxc = maxc;
450 int64_t mincoordinate = _base + _origin + _minc;
451 int64_t maxcoordinate = _base + _origin + _maxc;
452 if(mincoordinate < 0)
453 _minc = _minc - mincoordinate;
454 if(maxcoordinate > _size)
455 _maxc = _maxc - (maxcoordinate - _size);
456 if(_minc >= maxc) {
457 minc = 0;
458 maxc = 0;
459 } else {
460 minc = _minc;
461 maxc = _maxc;