Add on_snoop Lua callback
[lsnes.git] / render.cpp
blob38003d19c7b468526db2c1d956ca7c5f60fd2159
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 inline uint32_t blend(uint32_t orig, uint16_t ialpha, uint32_t pl, uint32_t ph) throw()
34 const uint32_t X = 0xFF00FFU;
35 const uint32_t Y = 0xFF00FF00U;
36 return ((ialpha * ((orig >> 8) & X) + ph) & Y) | (((ialpha * (orig & X) + pl)
37 >> 8) & X);
40 //This is Jenkin's MIX function.
41 uint32_t keyhash(uint32_t key, uint32_t item, uint32_t mod) throw()
43 uint32_t a = key;
44 uint32_t b = 0;
45 uint32_t c = item;
46 a=a-b; a=a-c; a=a^(c >> 13);
47 b=b-c; b=b-a; b=b^(a << 8);
48 c=c-a; c=c-b; c=c^(b >> 13);
49 a=a-b; a=a-c; a=a^(c >> 12);
50 b=b-c; b=b-a; b=b^(a << 16);
51 c=c-a; c=c-b; c=c^(b >> 5);
52 a=a-b; a=a-c; a=a^(c >> 3);
53 b=b-c; b=b-a; b=b^(a << 10);
54 c=c-a; c=c-b; c=c^(b >> 15);
55 return c % mod;
59 //Locate glyph in font. Returns <width, offset> pair. Zero offset should be interpretted as an empty
60 //glyph.
61 std::pair<uint32_t, size_t> find_glyph(uint32_t codepoint, int32_t x, int32_t y, int32_t orig_x,
62 int32_t& next_x, int32_t& next_y) throw()
64 uint32_t cwidth = 0;
65 if(codepoint == 9) {
66 cwidth = 64 - (x - orig_x) % 64;
67 next_x = x + cwidth;
68 next_y = y;
69 return std::make_pair(cwidth, 0);
70 } else if(codepoint == 10) {
71 next_x = orig_x;
72 next_y = y + 16;
73 return std::make_pair(0, 0);
74 } else if(codepoint == 32) {
75 next_x = x + 8;
76 next_y = y;
77 return std::make_pair(8, 0);
78 } else {
79 uint32_t mdir = fontdata[0];
80 uint32_t mseed = fontdata[mdir];
81 uint32_t msize = fontdata[mdir + 1];
82 uint32_t midx = keyhash(mseed, codepoint, msize);
83 uint32_t sdir = fontdata[mdir + 2 + midx];
84 if(!fontdata[sdir + 1]) {
85 //Character not found.
86 next_x = x + 8;
87 next_y = y;
88 return std::make_pair(8, 0);
90 uint32_t sseed = fontdata[sdir];
91 uint32_t ssize = fontdata[sdir + 1];
92 uint32_t sidx = keyhash(sseed, codepoint, ssize);
93 if(fontdata[sdir + 2 + 2 * sidx] != codepoint) {
94 //Character not found.
95 next_x = x + 8;
96 next_y = y;
97 return std::make_pair(8, 0);
99 bool wide = (fontdata[fontdata[sdir + 2 + 2 * sidx + 1]] != 0);
100 next_x = x + (wide ? 16 : 8);
101 next_y = y;
102 return std::make_pair(wide ? 16 : 8, fontdata[sdir + 2 + 2 * sidx + 1] + 1);
106 render_object::~render_object() throw()
110 void render_text(struct screen& scr, int32_t x, int32_t y, const std::string& text, uint32_t fg,
111 uint16_t fgalpha, uint32_t bg, uint16_t bgalpha) throw(std::bad_alloc)
113 uint32_t pfgl = (fg & 0xFF00FF) * fgalpha;
114 uint32_t pfgh = ((fg >> 8) & 0xFF00FF) * fgalpha;
115 uint32_t pbgl = (bg & 0xFF00FF) * bgalpha;
116 uint32_t pbgh = ((bg >> 8) & 0xFF00FF) * bgalpha;
117 uint16_t ifga = 256 - fgalpha;
118 uint16_t ibga = 256 - bgalpha;
119 int32_t orig_x = x;
120 uint32_t unicode_code = 0;
121 uint8_t unicode_left = 0;
122 for(size_t i = 0; i < text.length(); i++) {
123 uint8_t ch = text[i];
124 if(ch < 128)
125 unicode_code = text[i];
126 else if(ch < 192) {
127 if(!unicode_left)
128 continue;
129 unicode_code = 64 * unicode_code + ch - 128;
130 if(--unicode_left)
131 continue;
132 } else if(ch < 224) {
133 unicode_code = ch - 192;
134 unicode_left = 1;
135 continue;
136 } else if(ch < 240) {
137 unicode_code = ch - 224;
138 unicode_left = 2;
139 continue;
140 } else if(ch < 248) {
141 unicode_code = ch - 240;
142 unicode_left = 3;
143 continue;
144 } else
145 continue;
146 int32_t next_x, next_y;
147 auto p = find_glyph(unicode_code, x, y, orig_x, next_x, next_y);
148 uint32_t dx = 0;
149 uint32_t dw = p.first;
150 uint32_t dy = 0;
151 uint32_t dh = 16;
152 uint32_t cx = static_cast<uint32_t>(static_cast<int32_t>(scr.originx) + x);
153 uint32_t cy = static_cast<uint32_t>(static_cast<int32_t>(scr.originy) + y);
154 while(cx > scr.width && dw > 0) {
155 dx++;
156 dw--;
157 cx++;
159 while(cy > scr.height && dh > 0) {
160 dy++;
161 dh--;
162 cy++;
164 while(cx + dw > scr.width && dw > 0)
165 dw--;
166 while(cy + dh > scr.height && dh > 0)
167 dh--;
168 if(!dw || !dh)
169 continue; //Outside screen.
171 if(p.second == 0) {
172 //Blank glyph.
173 for(uint32_t j = 0; j < dh; j++) {
174 uint32_t* base = scr.rowptr(cy + j) + cx;
175 for(uint32_t i = 0; i < dw; i++)
176 base[i] = blend(base[i], ibga, pbgl, pbgh);
178 } else {
179 //narrow/wide glyph.
180 for(uint32_t j = 0; j < dh; j++) {
181 uint32_t dataword = fontdata[p.second + (dy + j) / (32 / p.first)];
182 uint32_t* base = scr.rowptr(cy + j) + cx;
183 for(uint32_t i = 0; i < dw; i++)
184 if(((dataword >> (31 - ((dy + j) % (32 / p.first)) * p.first - (dx + i))) & 1))
185 base[i] = blend(base[i], ifga, pfgl, pfgh);
186 else
187 base[i] = blend(base[i], ibga, pbgl, pbgh);
190 x = next_x;
191 y = next_y;
195 void render_queue::add(struct render_object& obj) throw(std::bad_alloc)
197 q.push_back(&obj);
200 void render_queue::run(struct screen& scr) throw()
202 for(auto i = q.begin(); i != q.end(); i++) {
203 try {
204 (**i)(scr);
205 } catch(...) {
207 delete *i;
209 q.clear();
212 void render_queue::clear() throw()
214 for(auto i = q.begin(); i != q.end(); i++)
215 delete *i;
216 q.clear();
219 render_queue::~render_queue() throw()
221 clear();
224 uint32_t screen::make_color(uint8_t r, uint8_t g, uint8_t b) throw()
226 return (static_cast<uint32_t>(r) << active_rshift) |
227 (static_cast<uint32_t>(g) << active_gshift) |
228 (static_cast<uint32_t>(b) << active_bshift);
231 lcscreen::lcscreen(const uint16_t* mem, bool hires, bool interlace, bool overscan, bool region) throw()
233 uint32_t dataoffset = 0;
234 width = hires ? 512 : 256;
235 height = 0;
236 if(region) {
237 //PAL.
238 height = 239;
239 dataoffset = overscan ? 9 : 1;
240 } else {
241 //presumably NTSC.
242 height = 224;
243 dataoffset = overscan ? 16 : 9;
245 if(interlace)
246 height <<= 1;
247 memory = mem + dataoffset * 1024;
248 pitch = interlace ? 512 : 1024;
249 user_memory = false;
252 lcscreen::lcscreen(const uint16_t* mem, uint32_t _width, uint32_t _height) throw()
254 width = _width;
255 height = _height;
256 memory = mem;
257 pitch = width;
258 user_memory = false;
261 lcscreen::lcscreen() throw()
263 width = 0;
264 height = 0;
265 memory = NULL;
266 user_memory = true;
267 pitch = 0;
268 allocated = 0;
271 lcscreen::lcscreen(const lcscreen& ls) throw(std::bad_alloc)
273 width = ls.width;
274 height = ls.height;
275 pitch = width;
276 user_memory = true;
277 allocated = static_cast<size_t>(width) * height;
278 memory = new uint16_t[allocated];
279 for(size_t l = 0; l < height; l++)
280 memcpy(const_cast<uint16_t*>(memory + l * width), ls.memory + l * ls.pitch, 2 * width);
283 lcscreen& lcscreen::operator=(const lcscreen& ls) throw(std::bad_alloc, std::runtime_error)
285 if(!user_memory)
286 throw std::runtime_error("Can't copy to non-user memory");
287 if(this == &ls)
288 return *this;
289 if(allocated < static_cast<size_t>(ls.width) * ls.height) {
290 size_t p_allocated = static_cast<size_t>(ls.width) * ls.height;
291 memory = new uint16_t[p_allocated];
292 allocated = p_allocated;
294 width = ls.width;
295 height = ls.height;
296 pitch = width;
297 for(size_t l = 0; l < height; l++)
298 memcpy(const_cast<uint16_t*>(memory + l * width), ls.memory + l * ls.pitch, 2 * width);
299 return *this;
302 lcscreen::~lcscreen()
304 if(user_memory)
305 delete[] const_cast<uint16_t*>(memory);
308 void lcscreen::load(const std::vector<char>& data) throw(std::bad_alloc, std::runtime_error)
310 if(!user_memory)
311 throw std::runtime_error("Can't load to non-user memory");
312 const uint8_t* data2 = reinterpret_cast<const uint8_t*>(&data[0]);
313 if(data.size() < 2)
314 throw std::runtime_error("Corrupt saved screenshot data");
315 uint32_t _width = static_cast<uint32_t>(data2[0]) * 256 + static_cast<uint32_t>(data2[1]);
316 if(_width > 1 && data.size() % (2 * _width) != 2)
317 throw std::runtime_error("Corrupt saved screenshot data");
318 uint32_t _height = (data.size() - 2) / (2 * _width);
319 if(allocated < static_cast<size_t>(_width) * _height) {
320 size_t p_allocated = static_cast<size_t>(_width) * _height;
321 memory = new uint16_t[p_allocated];
322 allocated = p_allocated;
324 uint16_t* mem = const_cast<uint16_t*>(memory);
325 width = _width;
326 height = _height;
327 pitch = width;
328 for(size_t i = 0; i < (data.size() - 2) / 2; i++)
329 mem[i] = static_cast<uint16_t>(data2[2 + 2 * i]) * 256 +
330 static_cast<uint16_t>(data2[2 + 2 * i + 1]);
333 void lcscreen::save(std::vector<char>& data) throw(std::bad_alloc)
335 data.resize(2 + 2 * static_cast<size_t>(width) * height);
336 uint8_t* data2 = reinterpret_cast<uint8_t*>(&data[0]);
337 data2[0] = (width >> 8);
338 data2[1] = width;
339 for(size_t i = 0; i < (data.size() - 2) / 2; i++) {
340 data[2 + 2 * i] = memory[(i / width) * pitch + (i % width)] >> 8;
341 data[2 + 2 * i + 1] = memory[(i / width) * pitch + (i % width)];
345 void lcscreen::save_png(const std::string& file) throw(std::bad_alloc, std::runtime_error)
347 unsigned char clevels[32];
348 for(unsigned i = 0; i < 32; i++)
349 clevels[i] = 255 * i / 31;
350 uint8_t* buffer = new uint8_t[3 * static_cast<size_t>(width) * height];
351 for(uint32_t j = 0; j < height; j++)
352 for(uint32_t i = 0; i < width; i++) {
353 uint16_t word = memory[pitch * j + i];
354 buffer[3 * static_cast<size_t>(width) * j + 3 * i + 0] = clevels[(word >> 10) & 0x1F];
355 buffer[3 * static_cast<size_t>(width) * j + 3 * i + 1] = clevels[(word >> 5) & 0x1F];
356 buffer[3 * static_cast<size_t>(width) * j + 3 * i + 2] = clevels[(word) & 0x1F];
358 try {
359 save_png_data(file, buffer, width, height);
360 delete[] buffer;
361 } catch(...) {
362 delete[] buffer;
363 throw;
367 void screen::copy_from(lcscreen& scr, uint32_t hscale, uint32_t vscale) throw()
369 uint32_t copyable_width = (width - originx) / hscale;
370 uint32_t copyable_height = (height - originy) / vscale;
371 copyable_width = (copyable_width > scr.width) ? scr.width : copyable_width;
372 copyable_height = (copyable_height > scr.height) ? scr.height : copyable_height;
373 for(uint32_t y = 0; y < height; y++) {
374 memset(rowptr(y), 0, 4 * width);
376 for(uint32_t y = 0; y < copyable_height; y++) {
377 uint32_t line = y * vscale + originy;
378 uint32_t* ptr = rowptr(line) + originx;
379 const uint16_t* sbase = scr.memory + y * scr.pitch;
380 for(uint32_t x = 0; x < copyable_width; x++) {
381 uint32_t c = palette[sbase[x] % 32768];
382 for(uint32_t i = 0; i < hscale; i++)
383 *(ptr++) = c;
385 for(uint32_t j = 1; j < vscale; j++)
386 memcpy(rowptr(line + j), rowptr(line), 4 * hscale * copyable_width);
390 void screen::reallocate(uint32_t _width, uint32_t _height, uint32_t _originx, uint32_t _originy, bool upside_down)
391 throw(std::bad_alloc)
393 if(_width == width && _height == height) {
394 originx = _originx;
395 originy = _originy;
396 return;
398 if(!_width || !_height) {
399 width = height = originx = originy = pitch = 0;
400 if(memory && !user_memory)
401 delete[] memory;
402 memory = NULL;
403 user_memory = false;
404 flipped = upside_down;
405 return;
407 uint32_t* newmem = new uint32_t[_width * _height];
408 width = _width;
409 height = _height;
410 originx = _originx;
411 originy = _originy;
412 pitch = 4 * _width;
413 if(memory && !user_memory)
414 delete[] memory;
415 memory = newmem;
416 user_memory = false;
417 flipped = upside_down;
420 void screen::set(uint32_t* _memory, uint32_t _width, uint32_t _height, uint32_t _originx, uint32_t _originy,
421 uint32_t _pitch) throw()
423 if(memory && !user_memory)
424 delete[] memory;
425 width = _width;
426 height = _height;
427 originx = _originx;
428 originy = _originy;
429 pitch = _pitch;
430 user_memory = true;
431 memory = _memory;
432 flipped = false;
435 uint32_t* screen::rowptr(uint32_t row) throw()
437 if(flipped)
438 row = height - row - 1;
439 return reinterpret_cast<uint32_t*>(reinterpret_cast<uint8_t*>(memory) + row * pitch);
442 screen::screen() throw()
444 uint32_t _magic = 403703808;
445 uint8_t* magic = reinterpret_cast<uint8_t*>(&_magic);
446 memory = NULL;
447 width = height = originx = originy = pitch = 0;
448 user_memory = false;
449 flipped = false;
450 active_rshift = active_gshift = active_bshift = 255;
451 set_palette(magic[0], magic[1], magic[2]);
454 screen::~screen() throw()
456 if(memory && !user_memory)
457 delete[] memory;
460 void screen::set_palette(uint32_t rshift, uint32_t gshift, uint32_t bshift) throw()
462 if(rshift == active_rshift && gshift == active_gshift && bshift == active_bshift)
463 return;
464 uint32_t old_rshift = active_rshift;
465 uint32_t old_gshift = active_gshift;
466 uint32_t old_bshift = active_bshift;
467 uint32_t xpalette[32];
468 for(unsigned i = 0; i < 32; i++)
469 xpalette[i] = (i * 255 / 31);
470 for(unsigned i = 0; i < 32768; i++) {
471 palette[i] = (xpalette[(i >> 10) & 31] << rshift) +
472 (xpalette[(i >> 5) & 31] << gshift) +
473 (xpalette[i & 31] << bshift);
475 active_rshift = rshift;
476 active_gshift = gshift;
477 active_bshift = bshift;
478 //Convert the data.
479 for(uint32_t j = 0; j < height; j++) {
480 uint32_t* rp = rowptr(j);
481 for(uint32_t i = 0; i < width; i++) {
482 uint32_t x = rp[i];
483 uint32_t r = (x >> old_rshift) & 0xFF;
484 uint32_t g = (x >> old_gshift) & 0xFF;
485 uint32_t b = (x >> old_bshift) & 0xFF;
486 x = (r << active_rshift) | (g << active_gshift) | (b << active_bshift);
487 rp[i] = x;