2011-09-13 17:50:18 +03:00
|
|
|
#include "lsnes.hpp"
|
|
|
|
#include <snes/snes.hpp>
|
|
|
|
|
2011-11-06 17:37:53 +02:00
|
|
|
#include "core/misc.hpp"
|
2011-11-06 14:41:41 +02:00
|
|
|
#include "core/png.hpp"
|
|
|
|
#include "core/render.hpp"
|
|
|
|
|
2011-09-13 17:50:18 +03:00
|
|
|
#include <sstream>
|
|
|
|
#include <list>
|
|
|
|
#include <iomanip>
|
|
|
|
#include <cstdint>
|
|
|
|
#include <string>
|
2011-11-06 17:37:53 +02:00
|
|
|
#include <map>
|
|
|
|
#include <vector>
|
2011-09-13 17:50:18 +03:00
|
|
|
|
2011-11-06 17:37:53 +02:00
|
|
|
#define TAG_ZEROWIDTH 0
|
|
|
|
#define TAG_NARROW 1
|
|
|
|
#define TAG_WIDE 2
|
|
|
|
#define TAG_TABULATION 3
|
|
|
|
#define TAG_WIDTH_MASK 3
|
|
|
|
#define TAG_LINECHANGE 4
|
|
|
|
|
|
|
|
extern const char* font_hex_data;
|
2011-09-13 17:50:18 +03:00
|
|
|
|
|
|
|
namespace
|
|
|
|
{
|
2011-11-06 17:37:53 +02:00
|
|
|
std::vector<uint32_t> font_glyph_data;
|
|
|
|
std::map<uint32_t, uint32_t> font_glyph_offsets;
|
|
|
|
|
|
|
|
uint32_t parse_word(const char* x)
|
|
|
|
{
|
|
|
|
char buf[9] = {0};
|
|
|
|
char* end;
|
|
|
|
memcpy(buf, x, 8);
|
|
|
|
unsigned long v = strtoul(buf, &end, 16);
|
|
|
|
if(end != buf + 8)
|
|
|
|
v = 0xFFFFFFFFUL;
|
|
|
|
//std::cerr << "Parse word " << buf << std::endl;
|
|
|
|
return v;
|
|
|
|
}
|
|
|
|
|
|
|
|
void init_font()
|
|
|
|
{
|
|
|
|
static bool iflag = false;
|
|
|
|
if(iflag)
|
|
|
|
return;
|
|
|
|
//Special glyph data.
|
|
|
|
font_glyph_data.resize(7);
|
|
|
|
//Space & Unknown.
|
|
|
|
font_glyph_data[0] = TAG_NARROW;
|
|
|
|
font_glyph_data[1] = 0;
|
|
|
|
font_glyph_data[2] = 0;
|
|
|
|
font_glyph_data[3] = 0;
|
|
|
|
font_glyph_data[4] = 0;
|
|
|
|
//Tabulation.
|
|
|
|
font_glyph_data[5] = TAG_TABULATION;
|
|
|
|
//Linefeed.
|
|
|
|
font_glyph_data[6] = TAG_ZEROWIDTH | TAG_LINECHANGE;
|
|
|
|
|
|
|
|
size_t lsptr = 0;
|
|
|
|
uint32_t lc = 1;
|
|
|
|
for(size_t i = 0;; i++) {
|
|
|
|
//Skip spaces.
|
|
|
|
switch(font_hex_data[i]) {
|
|
|
|
case ' ':
|
|
|
|
case '\t':
|
|
|
|
//Skip spaces at start of line.
|
|
|
|
if(lsptr == i)
|
|
|
|
lsptr++;
|
|
|
|
case '\r':
|
|
|
|
case '\n':
|
|
|
|
case '\0': {
|
|
|
|
char* end;
|
|
|
|
uint32_t cp;
|
|
|
|
size_t fdatastart;
|
|
|
|
//Is this a comment?
|
|
|
|
if(lsptr == i || font_hex_data[lsptr] == '#')
|
|
|
|
goto skip_line;
|
|
|
|
cp = strtoul(font_hex_data + lsptr, &end, 16);
|
|
|
|
if(*end != ':') {
|
|
|
|
messages << "Malformed line " << lc << " in font data" << std::endl;
|
|
|
|
goto skip_line;
|
|
|
|
}
|
|
|
|
fdatastart = end - font_hex_data + 1;
|
|
|
|
if(i - fdatastart == 32) {
|
|
|
|
//Narrow glyph.
|
|
|
|
font_glyph_offsets[cp] = font_glyph_data.size();
|
|
|
|
font_glyph_data.push_back(TAG_NARROW);
|
|
|
|
for(uint32_t k = 0; k < 4; k++)
|
|
|
|
font_glyph_data.push_back(parse_word(end + 1 + 8 * k));
|
|
|
|
} else if(i - fdatastart == 64) {
|
|
|
|
//Wide glyph.
|
|
|
|
font_glyph_offsets[cp] = font_glyph_data.size();
|
|
|
|
font_glyph_data.push_back(TAG_WIDE);
|
|
|
|
for(uint32_t k = 0; k < 8; k++)
|
|
|
|
font_glyph_data.push_back(parse_word(end + 1 + 8 * k));
|
|
|
|
} else {
|
|
|
|
messages << "Malformed line " << lc << " in font data" << std::endl;
|
|
|
|
goto skip_line;
|
|
|
|
}
|
|
|
|
skip_line:
|
|
|
|
if(font_hex_data[i] != '\r' || font_hex_data[i + 1] != '\n')
|
|
|
|
lc++;
|
|
|
|
lsptr = i + 1;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
if(!font_hex_data[i])
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
//Special characters.
|
|
|
|
font_glyph_offsets[9] = 5;
|
|
|
|
font_glyph_offsets[10] = 6;
|
|
|
|
font_glyph_offsets[32] = 0;
|
|
|
|
|
|
|
|
uint32_t glyphs = 0;
|
|
|
|
uint32_t glyphs_narrow = 0;
|
|
|
|
uint32_t glyphs_wide = 0;
|
|
|
|
uint32_t glyphs_special = 0;
|
|
|
|
for(auto i : font_glyph_offsets) {
|
|
|
|
if(font_glyph_data[i.second] == TAG_NARROW)
|
|
|
|
glyphs_narrow++;
|
|
|
|
else if(font_glyph_data[i.second] == TAG_WIDE)
|
|
|
|
glyphs_wide++;
|
|
|
|
else
|
|
|
|
glyphs_special++;
|
|
|
|
glyphs++;
|
|
|
|
}
|
|
|
|
messages << "Loaded font data: " << glyphs << " glyphs (" << glyphs_narrow << " narrow, " <<
|
|
|
|
glyphs_wide << " wide, " << glyphs_special << " special)." << std::endl;
|
|
|
|
iflag = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline uint32_t find_font_glyph_offset(uint32_t cp)
|
|
|
|
{
|
2011-12-27 09:41:28 +02:00
|
|
|
return font_glyph_offsets.count(cp) ? font_glyph_offsets[cp] : 0;
|
2011-11-06 17:37:53 +02:00
|
|
|
}
|
|
|
|
|
2011-12-25 00:26:25 +02:00
|
|
|
inline uint32_t process_tag(uint32_t tag, int32_t& x, int32_t& y, int32_t orig_x, bool hdbl, bool vdbl)
|
2011-11-06 17:37:53 +02:00
|
|
|
{
|
|
|
|
uint32_t dwidth;
|
|
|
|
switch(tag & TAG_WIDTH_MASK) {
|
|
|
|
case TAG_ZEROWIDTH:
|
|
|
|
dwidth = 0;
|
|
|
|
break;
|
|
|
|
case TAG_NARROW:
|
|
|
|
dwidth = 8;
|
|
|
|
break;
|
|
|
|
case TAG_WIDE:
|
|
|
|
dwidth = 16;
|
|
|
|
break;
|
|
|
|
case TAG_TABULATION:
|
|
|
|
dwidth = 0x40 - (x & 0x3F);
|
|
|
|
break;
|
|
|
|
}
|
2011-12-25 00:26:25 +02:00
|
|
|
x += dwidth * (hdbl ? 2 : 1);
|
2011-11-06 17:37:53 +02:00
|
|
|
if(tag & TAG_LINECHANGE) {
|
2011-12-25 00:26:25 +02:00
|
|
|
y += 16 * (vdbl ? 2 : 1);
|
2011-11-06 17:37:53 +02:00
|
|
|
x = orig_x;
|
|
|
|
}
|
|
|
|
return dwidth;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline bool is_visible(uint32_t tag)
|
2011-09-13 17:50:18 +03:00
|
|
|
{
|
2011-11-06 17:37:53 +02:00
|
|
|
return ((tag & TAG_WIDTH_MASK) == TAG_NARROW || (tag & TAG_WIDTH_MASK) == TAG_WIDE);
|
2011-09-13 17:50:18 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-11-06 17:37:53 +02:00
|
|
|
|
|
|
|
void do_init_font()
|
|
|
|
{
|
|
|
|
init_font();
|
|
|
|
}
|
|
|
|
|
2011-11-06 15:58:26 +02:00
|
|
|
std::pair<uint32_t, const uint32_t*> find_glyph(uint32_t codepoint, int32_t x, int32_t y, int32_t orig_x,
|
2011-12-25 00:26:25 +02:00
|
|
|
int32_t& next_x, int32_t& next_y, bool hdbl, bool vdbl) throw()
|
2011-09-13 17:50:18 +03:00
|
|
|
{
|
2011-11-06 17:37:53 +02:00
|
|
|
init_font();
|
|
|
|
next_x = x;
|
|
|
|
next_y = y;
|
|
|
|
uint32_t offset = find_font_glyph_offset(codepoint);
|
|
|
|
uint32_t tag = font_glyph_data[offset];
|
2011-12-25 00:26:25 +02:00
|
|
|
uint32_t dwidth = process_tag(tag, next_x, next_y, orig_x, hdbl, vdbl);
|
2011-11-06 17:37:53 +02:00
|
|
|
bool visible = is_visible(tag);
|
|
|
|
return std::pair<uint32_t, const uint32_t*>(dwidth, visible ? &font_glyph_data[offset + 1] : NULL);
|
2011-09-13 17:50:18 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
render_object::~render_object() throw()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2011-09-27 02:52:35 +03:00
|
|
|
void render_text(struct screen& scr, int32_t x, int32_t y, const std::string& text, premultiplied_color fg,
|
2011-12-25 00:26:25 +02:00
|
|
|
premultiplied_color bg, bool hdbl, bool vdbl) throw(std::bad_alloc)
|
2011-09-13 17:50:18 +03:00
|
|
|
{
|
2011-09-14 21:24:15 +03:00
|
|
|
int32_t orig_x = x;
|
|
|
|
uint32_t unicode_code = 0;
|
|
|
|
uint8_t unicode_left = 0;
|
|
|
|
for(size_t i = 0; i < text.length(); i++) {
|
|
|
|
uint8_t ch = text[i];
|
|
|
|
if(ch < 128)
|
|
|
|
unicode_code = text[i];
|
|
|
|
else if(ch < 192) {
|
|
|
|
if(!unicode_left)
|
|
|
|
continue;
|
|
|
|
unicode_code = 64 * unicode_code + ch - 128;
|
|
|
|
if(--unicode_left)
|
|
|
|
continue;
|
|
|
|
} else if(ch < 224) {
|
|
|
|
unicode_code = ch - 192;
|
|
|
|
unicode_left = 1;
|
|
|
|
continue;
|
|
|
|
} else if(ch < 240) {
|
|
|
|
unicode_code = ch - 224;
|
|
|
|
unicode_left = 2;
|
|
|
|
continue;
|
|
|
|
} else if(ch < 248) {
|
|
|
|
unicode_code = ch - 240;
|
|
|
|
unicode_left = 3;
|
|
|
|
continue;
|
|
|
|
} else
|
|
|
|
continue;
|
|
|
|
int32_t next_x, next_y;
|
2011-12-25 00:26:25 +02:00
|
|
|
auto p = find_glyph(unicode_code, x, y, orig_x, next_x, next_y, hdbl, vdbl);
|
2011-09-14 21:24:15 +03:00
|
|
|
uint32_t dx = 0;
|
2011-12-25 00:26:25 +02:00
|
|
|
uint32_t dw = p.first * (hdbl ? 2 : 1);
|
2011-09-14 21:24:15 +03:00
|
|
|
uint32_t dy = 0;
|
2011-12-25 00:26:25 +02:00
|
|
|
uint32_t dh = 16 * (vdbl ? 2 : 1);
|
2011-09-14 21:24:15 +03:00
|
|
|
uint32_t cx = static_cast<uint32_t>(static_cast<int32_t>(scr.originx) + x);
|
|
|
|
uint32_t cy = static_cast<uint32_t>(static_cast<int32_t>(scr.originy) + y);
|
|
|
|
while(cx > scr.width && dw > 0) {
|
|
|
|
dx++;
|
|
|
|
dw--;
|
|
|
|
cx++;
|
|
|
|
}
|
|
|
|
while(cy > scr.height && dh > 0) {
|
|
|
|
dy++;
|
|
|
|
dh--;
|
|
|
|
cy++;
|
|
|
|
}
|
|
|
|
while(cx + dw > scr.width && dw > 0)
|
|
|
|
dw--;
|
|
|
|
while(cy + dh > scr.height && dh > 0)
|
|
|
|
dh--;
|
|
|
|
if(!dw || !dh)
|
|
|
|
continue; //Outside screen.
|
|
|
|
|
2011-12-25 00:26:25 +02:00
|
|
|
uint32_t rshift = (p.first == 16) ? (vdbl ? 2 : 1) : (vdbl ? 3 : 2);
|
|
|
|
uint32_t rishift = (p.first == 16) ? 4 : 3;
|
|
|
|
uint32_t xshift = hdbl ? 1 : 0;
|
|
|
|
uint32_t yshift = vdbl ? 1 : 0;
|
|
|
|
uint32_t b = dx & 1;
|
|
|
|
|
2011-11-06 15:58:26 +02:00
|
|
|
if(p.second == NULL) {
|
2011-09-14 21:24:15 +03:00
|
|
|
//Blank glyph.
|
|
|
|
for(uint32_t j = 0; j < dh; j++) {
|
2011-10-19 13:59:54 +03:00
|
|
|
uint32_t* base = scr.rowptr(cy + j) + cx;
|
2011-09-14 21:24:15 +03:00
|
|
|
for(uint32_t i = 0; i < dw; i++)
|
2011-09-27 02:52:35 +03:00
|
|
|
bg.apply(base[i]);
|
2011-09-14 21:24:15 +03:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
for(uint32_t j = 0; j < dh; j++) {
|
2011-12-25 00:26:25 +02:00
|
|
|
uint32_t dataword = p.second[(dy + j) >> rshift];
|
2011-10-19 13:59:54 +03:00
|
|
|
uint32_t* base = scr.rowptr(cy + j) + cx;
|
2011-12-25 00:26:25 +02:00
|
|
|
uint32_t rbit = (~((dy + j) >> yshift << rishift) & 31) - (dx >> xshift);
|
|
|
|
if(hdbl) {
|
|
|
|
for(uint32_t i = 0; i < dw; i++)
|
|
|
|
if((dataword >> (rbit - ((i + b) >> 1))) & 1)
|
|
|
|
fg.apply(base[i]);
|
|
|
|
else
|
|
|
|
bg.apply(base[i]);
|
|
|
|
} else {
|
|
|
|
for(uint32_t i = 0; i < dw; i++)
|
|
|
|
if((dataword >> (rbit - i)) & 1)
|
|
|
|
fg.apply(base[i]);
|
|
|
|
else
|
|
|
|
bg.apply(base[i]);
|
|
|
|
}
|
2011-09-14 21:24:15 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
x = next_x;
|
|
|
|
y = next_y;
|
|
|
|
}
|
2011-09-13 17:50:18 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void render_queue::add(struct render_object& obj) throw(std::bad_alloc)
|
|
|
|
{
|
|
|
|
q.push_back(&obj);
|
|
|
|
}
|
|
|
|
|
|
|
|
void render_queue::run(struct screen& scr) throw()
|
|
|
|
{
|
2011-10-19 19:25:31 +03:00
|
|
|
for(auto i : q) {
|
2011-09-13 17:50:18 +03:00
|
|
|
try {
|
2011-10-19 19:25:31 +03:00
|
|
|
(*i)(scr);
|
2011-09-13 17:50:18 +03:00
|
|
|
} catch(...) {
|
|
|
|
}
|
2011-10-19 19:25:31 +03:00
|
|
|
delete i;
|
2011-09-13 17:50:18 +03:00
|
|
|
}
|
|
|
|
q.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
void render_queue::clear() throw()
|
|
|
|
{
|
2011-10-19 19:25:31 +03:00
|
|
|
for(auto i : q)
|
|
|
|
delete i;
|
2011-09-13 17:50:18 +03:00
|
|
|
q.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
render_queue::~render_queue() throw()
|
|
|
|
{
|
|
|
|
clear();
|
|
|
|
}
|
|
|
|
|
2011-10-19 13:59:54 +03:00
|
|
|
uint32_t screen::make_color(uint8_t r, uint8_t g, uint8_t b) throw()
|
2011-09-13 17:50:18 +03:00
|
|
|
{
|
2011-10-19 13:59:54 +03:00
|
|
|
uint32_t _r = r;
|
|
|
|
uint32_t _g = g;
|
|
|
|
uint32_t _b = b;
|
|
|
|
return (_r << 16) + (_g << 8) + _b;
|
2011-09-13 17:50:18 +03:00
|
|
|
}
|
|
|
|
|
2011-10-19 13:59:54 +03:00
|
|
|
lcscreen::lcscreen(const uint32_t* mem, bool hires, bool interlace, bool overscan, bool region) throw()
|
2011-09-13 17:50:18 +03:00
|
|
|
{
|
|
|
|
uint32_t dataoffset = 0;
|
|
|
|
width = hires ? 512 : 256;
|
|
|
|
height = 0;
|
|
|
|
if(region) {
|
|
|
|
//PAL.
|
|
|
|
height = 239;
|
|
|
|
dataoffset = overscan ? 9 : 1;
|
|
|
|
} else {
|
|
|
|
//presumably NTSC.
|
|
|
|
height = 224;
|
|
|
|
dataoffset = overscan ? 16 : 9;
|
|
|
|
}
|
|
|
|
if(interlace)
|
|
|
|
height <<= 1;
|
|
|
|
memory = mem + dataoffset * 1024;
|
|
|
|
pitch = interlace ? 512 : 1024;
|
|
|
|
user_memory = false;
|
|
|
|
}
|
|
|
|
|
2011-10-19 13:59:54 +03:00
|
|
|
lcscreen::lcscreen(const uint32_t* mem, uint32_t _width, uint32_t _height) throw()
|
2011-09-13 17:50:18 +03:00
|
|
|
{
|
|
|
|
width = _width;
|
|
|
|
height = _height;
|
|
|
|
memory = mem;
|
|
|
|
pitch = width;
|
|
|
|
user_memory = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
lcscreen::lcscreen() throw()
|
|
|
|
{
|
|
|
|
width = 0;
|
|
|
|
height = 0;
|
|
|
|
memory = NULL;
|
|
|
|
user_memory = true;
|
|
|
|
pitch = 0;
|
|
|
|
allocated = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
lcscreen::lcscreen(const lcscreen& ls) throw(std::bad_alloc)
|
|
|
|
{
|
|
|
|
width = ls.width;
|
|
|
|
height = ls.height;
|
|
|
|
pitch = width;
|
|
|
|
user_memory = true;
|
|
|
|
allocated = static_cast<size_t>(width) * height;
|
2011-10-19 13:59:54 +03:00
|
|
|
memory = new uint32_t[allocated];
|
2011-09-13 17:50:18 +03:00
|
|
|
for(size_t l = 0; l < height; l++)
|
2011-10-19 13:59:54 +03:00
|
|
|
memcpy(const_cast<uint32_t*>(memory + l * width), ls.memory + l * ls.pitch, 4 * width);
|
2011-09-13 17:50:18 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
lcscreen& lcscreen::operator=(const lcscreen& ls) throw(std::bad_alloc, std::runtime_error)
|
|
|
|
{
|
|
|
|
if(!user_memory)
|
|
|
|
throw std::runtime_error("Can't copy to non-user memory");
|
|
|
|
if(this == &ls)
|
|
|
|
return *this;
|
|
|
|
if(allocated < static_cast<size_t>(ls.width) * ls.height) {
|
|
|
|
size_t p_allocated = static_cast<size_t>(ls.width) * ls.height;
|
2011-10-19 13:59:54 +03:00
|
|
|
memory = new uint32_t[p_allocated];
|
2011-09-13 17:50:18 +03:00
|
|
|
allocated = p_allocated;
|
|
|
|
}
|
|
|
|
width = ls.width;
|
|
|
|
height = ls.height;
|
|
|
|
pitch = width;
|
|
|
|
for(size_t l = 0; l < height; l++)
|
2011-10-19 13:59:54 +03:00
|
|
|
memcpy(const_cast<uint32_t*>(memory + l * width), ls.memory + l * ls.pitch, 4 * width);
|
2011-09-13 17:50:18 +03:00
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
lcscreen::~lcscreen()
|
|
|
|
{
|
|
|
|
if(user_memory)
|
2011-10-19 13:59:54 +03:00
|
|
|
delete[] const_cast<uint32_t*>(memory);
|
2011-09-13 17:50:18 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void lcscreen::load(const std::vector<char>& data) throw(std::bad_alloc, std::runtime_error)
|
|
|
|
{
|
|
|
|
if(!user_memory)
|
|
|
|
throw std::runtime_error("Can't load to non-user memory");
|
|
|
|
const uint8_t* data2 = reinterpret_cast<const uint8_t*>(&data[0]);
|
2011-09-15 22:56:33 +03:00
|
|
|
if(data.size() < 2)
|
2011-09-13 17:50:18 +03:00
|
|
|
throw std::runtime_error("Corrupt saved screenshot data");
|
|
|
|
uint32_t _width = static_cast<uint32_t>(data2[0]) * 256 + static_cast<uint32_t>(data2[1]);
|
2011-10-19 13:59:54 +03:00
|
|
|
if(_width > 1 && data.size() % (3 * _width) != 2)
|
2011-09-13 17:50:18 +03:00
|
|
|
throw std::runtime_error("Corrupt saved screenshot data");
|
2011-10-19 13:59:54 +03:00
|
|
|
uint32_t _height = (data.size() - 2) / (3 * _width);
|
2011-09-13 17:50:18 +03:00
|
|
|
if(allocated < static_cast<size_t>(_width) * _height) {
|
|
|
|
size_t p_allocated = static_cast<size_t>(_width) * _height;
|
2011-10-19 13:59:54 +03:00
|
|
|
memory = new uint32_t[p_allocated];
|
2011-09-13 17:50:18 +03:00
|
|
|
allocated = p_allocated;
|
|
|
|
}
|
2011-10-19 13:59:54 +03:00
|
|
|
uint32_t* mem = const_cast<uint32_t*>(memory);
|
2011-09-13 17:50:18 +03:00
|
|
|
width = _width;
|
|
|
|
height = _height;
|
|
|
|
pitch = width;
|
2011-11-09 19:48:58 +02:00
|
|
|
for(size_t i = 0; i < (data.size() - 2) / 3; i++)
|
2011-10-19 13:59:54 +03:00
|
|
|
mem[i] = static_cast<uint32_t>(data2[2 + 3 * i]) * 65536 +
|
|
|
|
static_cast<uint32_t>(data2[2 + 3 * i + 1]) * 256 +
|
|
|
|
static_cast<uint32_t>(data2[2 + 3 * i + 2]);
|
2011-09-13 17:50:18 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void lcscreen::save(std::vector<char>& data) throw(std::bad_alloc)
|
|
|
|
{
|
2011-10-19 13:59:54 +03:00
|
|
|
data.resize(2 + 3 * static_cast<size_t>(width) * height);
|
2011-09-13 17:50:18 +03:00
|
|
|
uint8_t* data2 = reinterpret_cast<uint8_t*>(&data[0]);
|
|
|
|
data2[0] = (width >> 8);
|
|
|
|
data2[1] = width;
|
2011-10-19 13:59:54 +03:00
|
|
|
for(size_t i = 0; i < (data.size() - 2) / 3; i++) {
|
|
|
|
data[2 + 3 * i] = memory[(i / width) * pitch + (i % width)] >> 16;
|
|
|
|
data[2 + 3 * i + 1] = memory[(i / width) * pitch + (i % width)] >> 8;
|
|
|
|
data[2 + 3 * i + 2] = memory[(i / width) * pitch + (i % width)];
|
2011-09-13 17:50:18 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void lcscreen::save_png(const std::string& file) throw(std::bad_alloc, std::runtime_error)
|
|
|
|
{
|
|
|
|
uint8_t* buffer = new uint8_t[3 * static_cast<size_t>(width) * height];
|
|
|
|
for(uint32_t j = 0; j < height; j++)
|
|
|
|
for(uint32_t i = 0; i < width; i++) {
|
2011-10-19 13:59:54 +03:00
|
|
|
uint32_t word = memory[pitch * j + i];
|
2011-11-08 23:09:49 +02:00
|
|
|
uint32_t l = 1 + ((word >> 15) & 0xF);
|
2011-10-29 18:28:37 +03:00
|
|
|
uint32_t r = l * ((word >> 0) & 0x1F);
|
|
|
|
uint32_t g = l * ((word >> 5) & 0x1F);
|
|
|
|
uint32_t b = l * ((word >> 10) & 0x1F);
|
2011-11-08 23:09:49 +02:00
|
|
|
buffer[3 * static_cast<size_t>(width) * j + 3 * i + 0] = r * 255 / 496;
|
|
|
|
buffer[3 * static_cast<size_t>(width) * j + 3 * i + 1] = g * 255 / 496;
|
|
|
|
buffer[3 * static_cast<size_t>(width) * j + 3 * i + 2] = b * 255 / 496;
|
2011-09-13 17:50:18 +03:00
|
|
|
}
|
|
|
|
try {
|
|
|
|
save_png_data(file, buffer, width, height);
|
|
|
|
delete[] buffer;
|
|
|
|
} catch(...) {
|
|
|
|
delete[] buffer;
|
|
|
|
throw;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void screen::copy_from(lcscreen& scr, uint32_t hscale, uint32_t vscale) throw()
|
|
|
|
{
|
2011-11-15 05:10:12 +02:00
|
|
|
if(width < originx || height < originy) {
|
|
|
|
//Just clear the screen.
|
|
|
|
for(uint32_t y = 0; y < height; y++)
|
|
|
|
memset(rowptr(y), 0, 4 * width);
|
|
|
|
return;
|
|
|
|
}
|
2011-09-13 17:50:18 +03:00
|
|
|
uint32_t copyable_width = (width - originx) / hscale;
|
|
|
|
uint32_t copyable_height = (height - originy) / vscale;
|
|
|
|
copyable_width = (copyable_width > scr.width) ? scr.width : copyable_width;
|
|
|
|
copyable_height = (copyable_height > scr.height) ? scr.height : copyable_height;
|
2011-11-15 05:10:12 +02:00
|
|
|
for(uint32_t y = 0; y < height; y++)
|
2011-10-19 13:59:54 +03:00
|
|
|
memset(rowptr(y), 0, 4 * width);
|
2011-09-13 17:50:18 +03:00
|
|
|
for(uint32_t y = 0; y < copyable_height; y++) {
|
|
|
|
uint32_t line = y * vscale + originy;
|
2011-10-19 13:59:54 +03:00
|
|
|
uint32_t* ptr = rowptr(line) + originx;
|
|
|
|
const uint32_t* sbase = scr.memory + y * scr.pitch;
|
2011-09-13 17:50:18 +03:00
|
|
|
for(uint32_t x = 0; x < copyable_width; x++) {
|
2011-10-29 18:28:37 +03:00
|
|
|
uint32_t c = palette[sbase[x] & 0x7FFFF];
|
2011-09-13 17:50:18 +03:00
|
|
|
for(uint32_t i = 0; i < hscale; i++)
|
|
|
|
*(ptr++) = c;
|
|
|
|
}
|
|
|
|
for(uint32_t j = 1; j < vscale; j++)
|
2011-10-19 13:59:54 +03:00
|
|
|
memcpy(rowptr(line + j) + originx, rowptr(line) + originx, 4 * hscale * copyable_width);
|
2011-09-13 17:50:18 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-11-15 05:10:12 +02:00
|
|
|
void screen::reallocate(uint32_t _width, uint32_t _height, bool upside_down) throw(std::bad_alloc)
|
2011-09-13 17:50:18 +03:00
|
|
|
{
|
2011-11-15 05:10:12 +02:00
|
|
|
if(_width == width && _height == height)
|
2011-09-13 17:50:18 +03:00
|
|
|
return;
|
|
|
|
if(!_width || !_height) {
|
|
|
|
width = height = originx = originy = pitch = 0;
|
|
|
|
if(memory && !user_memory)
|
|
|
|
delete[] memory;
|
|
|
|
memory = NULL;
|
|
|
|
user_memory = false;
|
|
|
|
flipped = upside_down;
|
|
|
|
return;
|
|
|
|
}
|
2011-10-19 13:59:54 +03:00
|
|
|
uint32_t* newmem = new uint32_t[_width * _height];
|
2011-09-13 17:50:18 +03:00
|
|
|
width = _width;
|
|
|
|
height = _height;
|
2011-10-19 13:59:54 +03:00
|
|
|
pitch = 4 * _width;
|
2011-09-13 17:50:18 +03:00
|
|
|
if(memory && !user_memory)
|
|
|
|
delete[] memory;
|
|
|
|
memory = newmem;
|
|
|
|
user_memory = false;
|
|
|
|
flipped = upside_down;
|
|
|
|
}
|
|
|
|
|
2011-11-15 05:10:12 +02:00
|
|
|
void screen::set(uint32_t* _memory, uint32_t _width, uint32_t _height, uint32_t _pitch) throw()
|
2011-09-13 17:50:18 +03:00
|
|
|
{
|
|
|
|
if(memory && !user_memory)
|
|
|
|
delete[] memory;
|
|
|
|
width = _width;
|
|
|
|
height = _height;
|
|
|
|
pitch = _pitch;
|
|
|
|
user_memory = true;
|
|
|
|
memory = _memory;
|
|
|
|
flipped = false;
|
|
|
|
}
|
|
|
|
|
2011-11-15 05:10:12 +02:00
|
|
|
void screen::set_origin(uint32_t _originx, uint32_t _originy) throw()
|
|
|
|
{
|
|
|
|
originx = _originx;
|
|
|
|
originy = _originy;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-10-19 13:59:54 +03:00
|
|
|
uint32_t* screen::rowptr(uint32_t row) throw()
|
2011-09-13 17:50:18 +03:00
|
|
|
{
|
|
|
|
if(flipped)
|
|
|
|
row = height - row - 1;
|
2011-10-19 13:59:54 +03:00
|
|
|
return reinterpret_cast<uint32_t*>(reinterpret_cast<uint8_t*>(memory) + row * pitch);
|
2011-09-13 17:50:18 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
screen::screen() throw()
|
|
|
|
{
|
|
|
|
memory = NULL;
|
|
|
|
width = height = originx = originy = pitch = 0;
|
|
|
|
user_memory = false;
|
|
|
|
flipped = false;
|
2011-10-29 18:28:37 +03:00
|
|
|
palette = NULL;
|
|
|
|
set_palette(16, 8, 0);
|
2011-09-13 17:50:18 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
screen::~screen() throw()
|
|
|
|
{
|
|
|
|
if(memory && !user_memory)
|
|
|
|
delete[] memory;
|
|
|
|
}
|
2011-09-28 00:14:09 +03:00
|
|
|
|
|
|
|
void clip_range(uint32_t origin, uint32_t size, int32_t base, int32_t& minc, int32_t& maxc) throw()
|
|
|
|
{
|
|
|
|
int64_t _origin = origin;
|
|
|
|
int64_t _size = size;
|
|
|
|
int64_t _base = base;
|
|
|
|
int64_t _minc = minc;
|
|
|
|
int64_t _maxc = maxc;
|
|
|
|
int64_t mincoordinate = _base + _origin + _minc;
|
|
|
|
int64_t maxcoordinate = _base + _origin + _maxc;
|
|
|
|
if(mincoordinate < 0)
|
|
|
|
_minc = _minc - mincoordinate;
|
|
|
|
if(maxcoordinate > _size)
|
|
|
|
_maxc = _maxc - (maxcoordinate - _size);
|
|
|
|
if(_minc >= maxc) {
|
|
|
|
minc = 0;
|
|
|
|
maxc = 0;
|
|
|
|
} else {
|
|
|
|
minc = _minc;
|
|
|
|
maxc = _maxc;
|
|
|
|
}
|
|
|
|
}
|
2011-10-29 18:28:37 +03:00
|
|
|
|
|
|
|
void screen::set_palette(uint32_t r, uint32_t g, uint32_t b)
|
|
|
|
{
|
|
|
|
if(!palette)
|
|
|
|
palette = new uint32_t[0x80000];
|
|
|
|
else if(r == palette_r && g == palette_g && b == palette_b)
|
|
|
|
return;
|
|
|
|
for(size_t i = 0; i < static_cast<size_t>(width) * height; i++) {
|
|
|
|
uint32_t word = memory[i];
|
|
|
|
uint32_t R = (word >> palette_r) & 0xFF;
|
|
|
|
uint32_t G = (word >> palette_g) & 0xFF;
|
|
|
|
uint32_t B = (word >> palette_b) & 0xFF;
|
|
|
|
memory[i] = (R << r) | (G << g) | (B << b);
|
|
|
|
}
|
|
|
|
for(unsigned i = 0; i < 0x80000; i++) {
|
2011-11-08 23:09:49 +02:00
|
|
|
unsigned l = 1 + ((i >> 15) & 0xF);
|
2011-10-29 18:28:37 +03:00
|
|
|
unsigned R = (i >> 0) & 0x1F;
|
|
|
|
unsigned G = (i >> 5) & 0x1F;
|
|
|
|
unsigned B = (i >> 10) & 0x1F;
|
|
|
|
double _l = static_cast<double>(l);
|
2011-11-08 23:09:49 +02:00
|
|
|
double m = 255.0 / 496.0;
|
2011-10-29 18:28:37 +03:00
|
|
|
R = floor(m * R * _l + 0.5);
|
|
|
|
G = floor(m * G * _l + 0.5);
|
|
|
|
B = floor(m * B * _l + 0.5);
|
|
|
|
palette[i] = (R << r) | (G << g) | (B << b);
|
|
|
|
}
|
|
|
|
palette_r = r;
|
|
|
|
palette_g = g;
|
|
|
|
palette_b = b;
|
|
|
|
}
|
2011-12-25 08:30:41 +02:00
|
|
|
|
|
|
|
void premultiplied_color::set_palette(unsigned rshift, unsigned gshift, unsigned bshift) throw()
|
|
|
|
{
|
|
|
|
uint32_t r = (orig >> 16) & 0xFF;
|
|
|
|
uint32_t g = (orig >> 8) & 0xFF;
|
|
|
|
uint32_t b = orig & 0xFF;
|
|
|
|
uint32_t color = (r << rshift) | (g << gshift) | (b << bshift);
|
|
|
|
hi = color & 0xFF00FF;
|
|
|
|
lo = (color & 0xFF00FF00) >> 8;
|
|
|
|
hi *= origa;
|
|
|
|
lo *= origa;
|
|
|
|
}
|