Do color upconversion when copying lcscreen -> screen
[lsnes.git] / generic / render.cpp
blob4f2a5ee384fdf27a7ec8fb5553ca897f63c983ce
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 uint32_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 uint32_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) {
189 try {
190 (*i)(scr);
191 } catch(...) {
193 delete i;
195 q.clear();
198 void render_queue::clear() throw()
200 for(auto i : q)
201 delete i;
202 q.clear();
205 render_queue::~render_queue() throw()
207 clear();
210 uint32_t screen::make_color(uint8_t r, uint8_t g, uint8_t b) throw()
212 uint32_t _r = r;
213 uint32_t _g = g;
214 uint32_t _b = b;
215 return (_r << 16) + (_g << 8) + _b;
218 lcscreen::lcscreen(const uint32_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 uint32_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 uint32_t[allocated];
266 for(size_t l = 0; l < height; l++)
267 memcpy(const_cast<uint32_t*>(memory + l * width), ls.memory + l * ls.pitch, 4 * 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 uint32_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<uint32_t*>(memory + l * width), ls.memory + l * ls.pitch, 4 * width);
286 return *this;
289 lcscreen::~lcscreen()
291 if(user_memory)
292 delete[] const_cast<uint32_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() % (3 * _width) != 2)
304 throw std::runtime_error("Corrupt saved screenshot data");
305 uint32_t _height = (data.size() - 2) / (3 * _width);
306 if(allocated < static_cast<size_t>(_width) * _height) {
307 size_t p_allocated = static_cast<size_t>(_width) * _height;
308 memory = new uint32_t[p_allocated];
309 allocated = p_allocated;
311 uint32_t* mem = const_cast<uint32_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<uint32_t>(data2[2 + 3 * i]) * 65536 +
317 static_cast<uint32_t>(data2[2 + 3 * i + 1]) * 256 +
318 static_cast<uint32_t>(data2[2 + 3 * i + 2]);
321 void lcscreen::save(std::vector<char>& data) throw(std::bad_alloc)
323 data.resize(2 + 3 * static_cast<size_t>(width) * height);
324 uint8_t* data2 = reinterpret_cast<uint8_t*>(&data[0]);
325 data2[0] = (width >> 8);
326 data2[1] = width;
327 for(size_t i = 0; i < (data.size() - 2) / 3; i++) {
328 data[2 + 3 * i] = memory[(i / width) * pitch + (i % width)] >> 16;
329 data[2 + 3 * i + 1] = memory[(i / width) * pitch + (i % width)] >> 8;
330 data[2 + 3 * i + 2] = memory[(i / width) * pitch + (i % width)];
334 void lcscreen::save_png(const std::string& file) throw(std::bad_alloc, std::runtime_error)
336 uint8_t* buffer = new uint8_t[3 * static_cast<size_t>(width) * height];
337 for(uint32_t j = 0; j < height; j++)
338 for(uint32_t i = 0; i < width; i++) {
339 uint32_t word = memory[pitch * j + i];
340 uint32_t l = (word >> 15) & 0xF;
341 uint32_t r = l * ((word >> 0) & 0x1F);
342 uint32_t g = l * ((word >> 5) & 0x1F);
343 uint32_t b = l * ((word >> 10) & 0x1F);
344 buffer[3 * static_cast<size_t>(width) * j + 3 * i + 0] = r * 255 / 465;
345 buffer[3 * static_cast<size_t>(width) * j + 3 * i + 1] = g * 255 / 465;
346 buffer[3 * static_cast<size_t>(width) * j + 3 * i + 2] = b * 255 / 465;
348 try {
349 save_png_data(file, buffer, width, height);
350 delete[] buffer;
351 } catch(...) {
352 delete[] buffer;
353 throw;
357 void screen::copy_from(lcscreen& scr, uint32_t hscale, uint32_t vscale) throw()
359 uint32_t copyable_width = (width - originx) / hscale;
360 uint32_t copyable_height = (height - originy) / vscale;
361 copyable_width = (copyable_width > scr.width) ? scr.width : copyable_width;
362 copyable_height = (copyable_height > scr.height) ? scr.height : copyable_height;
363 for(uint32_t y = 0; y < height; y++) {
364 memset(rowptr(y), 0, 4 * width);
366 for(uint32_t y = 0; y < copyable_height; y++) {
367 uint32_t line = y * vscale + originy;
368 uint32_t* ptr = rowptr(line) + originx;
369 const uint32_t* sbase = scr.memory + y * scr.pitch;
370 for(uint32_t x = 0; x < copyable_width; x++) {
371 uint32_t c = palette[sbase[x] & 0x7FFFF];
372 for(uint32_t i = 0; i < hscale; i++)
373 *(ptr++) = c;
375 for(uint32_t j = 1; j < vscale; j++)
376 memcpy(rowptr(line + j) + originx, rowptr(line) + originx, 4 * hscale * copyable_width);
380 void screen::reallocate(uint32_t _width, uint32_t _height, uint32_t _originx, uint32_t _originy, bool upside_down)
381 throw(std::bad_alloc)
383 if(_width == width && _height == height) {
384 originx = _originx;
385 originy = _originy;
386 return;
388 if(!_width || !_height) {
389 width = height = originx = originy = pitch = 0;
390 if(memory && !user_memory)
391 delete[] memory;
392 memory = NULL;
393 user_memory = false;
394 flipped = upside_down;
395 return;
397 uint32_t* newmem = new uint32_t[_width * _height];
398 width = _width;
399 height = _height;
400 originx = _originx;
401 originy = _originy;
402 pitch = 4 * _width;
403 if(memory && !user_memory)
404 delete[] memory;
405 memory = newmem;
406 user_memory = false;
407 flipped = upside_down;
410 void screen::set(uint32_t* _memory, uint32_t _width, uint32_t _height, uint32_t _originx, uint32_t _originy,
411 uint32_t _pitch) throw()
413 if(memory && !user_memory)
414 delete[] memory;
415 width = _width;
416 height = _height;
417 originx = _originx;
418 originy = _originy;
419 pitch = _pitch;
420 user_memory = true;
421 memory = _memory;
422 flipped = false;
425 uint32_t* screen::rowptr(uint32_t row) throw()
427 if(flipped)
428 row = height - row - 1;
429 return reinterpret_cast<uint32_t*>(reinterpret_cast<uint8_t*>(memory) + row * pitch);
432 screen::screen() throw()
434 memory = NULL;
435 width = height = originx = originy = pitch = 0;
436 user_memory = false;
437 flipped = false;
438 palette = NULL;
439 set_palette(16, 8, 0);
442 screen::~screen() throw()
444 if(memory && !user_memory)
445 delete[] memory;
448 void clip_range(uint32_t origin, uint32_t size, int32_t base, int32_t& minc, int32_t& maxc) throw()
450 int64_t _origin = origin;
451 int64_t _size = size;
452 int64_t _base = base;
453 int64_t _minc = minc;
454 int64_t _maxc = maxc;
455 int64_t mincoordinate = _base + _origin + _minc;
456 int64_t maxcoordinate = _base + _origin + _maxc;
457 if(mincoordinate < 0)
458 _minc = _minc - mincoordinate;
459 if(maxcoordinate > _size)
460 _maxc = _maxc - (maxcoordinate - _size);
461 if(_minc >= maxc) {
462 minc = 0;
463 maxc = 0;
464 } else {
465 minc = _minc;
466 maxc = _maxc;
470 void screen::set_palette(uint32_t r, uint32_t g, uint32_t b)
472 if(!palette)
473 palette = new uint32_t[0x80000];
474 else if(r == palette_r && g == palette_g && b == palette_b)
475 return;
476 for(size_t i = 0; i < static_cast<size_t>(width) * height; i++) {
477 uint32_t word = memory[i];
478 uint32_t R = (word >> palette_r) & 0xFF;
479 uint32_t G = (word >> palette_g) & 0xFF;
480 uint32_t B = (word >> palette_b) & 0xFF;
481 memory[i] = (R << r) | (G << g) | (B << b);
483 for(unsigned i = 0; i < 0x80000; i++) {
484 unsigned l = (i >> 15) & 0xF;
485 unsigned R = (i >> 0) & 0x1F;
486 unsigned G = (i >> 5) & 0x1F;
487 unsigned B = (i >> 10) & 0x1F;
488 double _l = static_cast<double>(l);
489 double m = 17.0 / 31.0;
490 R = floor(m * R * _l + 0.5);
491 G = floor(m * G * _l + 0.5);
492 B = floor(m * B * _l + 0.5);
493 palette[i] = (R << r) | (G << g) | (B << b);
495 palette_r = r;
496 palette_g = g;
497 palette_b = b;