Merge branch 'rr1-maint'

This commit is contained in:
Ilari Liusvaara 2013-07-17 22:58:24 +03:00
commit ff86d2f0c2
7 changed files with 394 additions and 217 deletions

View file

@ -5,6 +5,7 @@
#include <cstdint>
#include <vector>
#include <string>
#include <iostream>
struct png_decoded_image
{
@ -15,6 +16,7 @@ struct png_decoded_image
std::vector<uint32_t> palette;
};
void decode_png(std::istream& file, png_decoded_image& out);
void decode_png(const std::string& file, png_decoded_image& out);
#endif

View file

@ -41,6 +41,7 @@ struct lua_loaded_bitmap
bool d;
std::vector<int64_t> bitmap;
std::vector<int64_t> palette;
static struct lua_loaded_bitmap load(std::istream& stream);
static struct lua_loaded_bitmap load(const std::string& name);
};

View file

@ -1953,6 +1953,31 @@ First non-whitespace is '#': Ignored
transparent: Fully transparent color
\end_layout
\begin_layout Subsubsection
gui.bitmap_load_str(string content)
\end_layout
\begin_layout Standard
Like gui.bitmap_load, but reads the specified string directly as content.
\end_layout
\begin_layout Subsubsection
gui.bitmap_load_png_str(string base64content)
\end_layout
\begin_layout Standard
Like gui.bitmap_load_png, but reads the specified string (as base64-encoded)
directly as content.
\end_layout
\begin_layout Subsubsection
gui.bitmap_load_pal_str(string content)
\end_layout
\begin_layout Standard
Like gui.bitmap_load_pal, but reads the specified string directly as content.
\end_layout
\begin_layout Subsubsection
gui.repaint()
\end_layout

View file

@ -953,34 +953,86 @@ Parameters:
May be absent or nil for no colorkey blit.
7.3.23 gui.repaint()
7.3.23 gui.bitmap_load_png(string filename)
Load a bitmap from PNG file. Parameters:
• filename: The name of file to load the bitmap frame.
Return value:
• If the PNG is of color type 3 (PALETTE), returns two value.
First is BITMAP containing the image data from the PNG and
second is PALETTE containg the palette data from the PNG.
• For color types 0 (GRAY), 2 (RGB), 4 (GRAY_ALPHA) and 6 (RGBA),
returns one DBITMAP containg the image data loaded from the
PNG.
7.3.24 gui.bitmap_load_pal(string filename)
Load a palette from file. Parameters:
• filename: The name of the file.
The kinds of lines supported
• Blank or just whitespace: Ignored
• First non-whitespace is '#': Ignored
• <r> <g> <b>: Fully opaque color with specified RGB values
(0-255)
• <r> <g> <b> <a>: Color with specified RGB values (0-255) and
specified alpha (0-256, 0 being fully transparent and 256 fully
opaque).
• transparent: Fully transparent color
7.3.25 gui.bitmap_load_str(string content)
Like gui.bitmap_load, but reads the specified string directly as
content.
7.3.26 gui.bitmap_load_png_str(string base64content)
Like gui.bitmap_load_png, but reads the specified string (as
base64-encoded) directly as content.
7.3.27 gui.bitmap_load_pal_str(string content)
Like gui.bitmap_load_pal, but reads the specified string directly
as content.
7.3.28 gui.repaint()
Request on_repaint() to happen as soon as possible. Can be used
anywhere.
7.3.24 gui.subframe_update(boolean on)
7.3.29 gui.subframe_update(boolean on)
Request subframe updates (calling on_paint() on subframes) to
happen (on=true) or not happen (on=false). Can be used anywhere.
7.3.25 gui.screenshot(string filename)
7.3.30 gui.screenshot(string filename)
Write PNG screenshot of the current frame (no drawings) to
specified file. Can be used anywhere.
7.3.26 gui.color(number r, number g, number b[, number a])
7.3.31 gui.color(number r, number g, number b[, number a])
Returns color (in notation Lua scripts use) corresponding to
color (r,g,b), each component in scale 0-255. If a is specified,
that is alpha (0 is fully transparent, 256(sic) is fully opaque).
The default alpha is 256.
7.3.27 gui.status(string name, string value)
7.3.32 gui.status(string name, string value)
Set status field “L[<name>]” to <value> in status area. Can be
used anywhere.
7.3.28 gui.rainbow(number step, number steps[, number color])
7.3.33 gui.rainbow(number step, number steps[, number color])
Perform hue rotation of color <color> (default bright red), by
<step> steps. The number of steps per full rotation is given by
@ -988,19 +1040,19 @@ absolute value of <steps>.
If <steps> is negative, the rotation will be counterclockwise.
7.3.29 gui.screenshot(string filename)
7.3.34 gui.screenshot(string filename)
Saves a screenshot into specified file.
7.3.30 gui.renderq_new(number width, number height)
7.3.35 gui.renderq_new(number width, number height)
Create render queue with specified reported size and return it.
7.3.31 gui.renderq_clear(RENDERQUEUE queue)
7.3.36 gui.renderq_clear(RENDERQUEUE queue)
Clear specified render queue.
7.3.32 gui.renderq_set(RENDERQUEUE queue)
7.3.37 gui.renderq_set(RENDERQUEUE queue)
Switch to specified render queue. Use nil as queue to switch to
default queue.
@ -1008,18 +1060,18 @@ default queue.
• When switched to another queue, all drawing functions work and
draw there, even outside on_video/on_paint.
7.3.33 gui.renderq_run(RENDERQUEUE queue)
7.3.38 gui.renderq_run(RENDERQUEUE queue)
Run specified render queue, copying the objects to current render
queue.
• Warning: Don't try to run the current render queue.
7.3.34 gui.loadfont(string filename)
7.3.39 gui.loadfont(string filename)
Loads font from specified file (CUSTOMFONT object).
7.3.35 CUSTOMFONT(number x, number y, string text[, number fgc[,
7.3.40 CUSTOMFONT(number x, number y, string text[, number fgc[,
number bgc[, number hlc]]])
Draw string with custom font to screen. The parameters are the

View file

@ -690,127 +690,127 @@ badtype:
if(interlace == 1) return *new png_interlacing_adam(3);
throw std::runtime_error("Unknown interlace type");
}
}
void decode_png(std::istream& stream, png_decoded_image& out)
{
png_dechunker dechunk(stream);
if(!dechunk.next_chunk())
throw std::runtime_error("PNG file has no chunks");
ihdr_chunk hdr(dechunk);
autorelease<png_decompressor> idat_decomp(png_decompressor::get(dechunk, hdr.compression));
autorelease<png_filterbank> filterbank(png_filterbank::get(*idat_decomp, hdr.filter, hdr.type,
hdr.depth, hdr.width));
autorelease<png_pixel_decoder> pixdecoder(png_pixel_decoder::get(*filterbank, hdr.type, hdr.depth,
hdr.width));
autorelease<png_interlacing> interlace(png_interlacing::get(hdr.interlace));
std::vector<uint32_t> ndata;
std::vector<uint32_t> npalette;
ndata.resize(hdr.width * hdr.height);
if(ndata.size() / hdr.width != hdr.height)
throw std::bad_alloc();
if(hdr.type == 3) {
npalette.resize(1 << hdr.depth);
for(size_t i = 0; i < npalette.size(); i++)
npalette[i] = 0xFF000000U;
}
if(!dechunk.next_chunk())
throw std::runtime_error("PNG file has no chunks besides header");
uint8_t trans[6];
uint8_t* _trans = NULL;
for(size_t pass = 0; pass < interlace->passes(); pass++) {
size_t scanline = 0;
png_interlacing::pass_info pinfo = interlace->pass(pass);
auto resolution = png_interlacing::pass_size(hdr.width, hdr.height, pinfo);
pixdecoder->adjust_row(resolution.first);
std::vector<uint32_t> scanlineb;
scanlineb.resize(resolution.first);
while(true) {
switch(dechunk.chunk_type()) {
case 0x49454E44: //IEND.
throw std::runtime_error("Unexpected IEND chunk");
case 0x504C5445: //PLTE.
if(hdr.type == 0 || hdr.type == 4)
throw std::runtime_error("Illegal PLTE in types 0/4");
if(hdr.type == 2 || hdr.type == 6)
break; //Advisory.
if(dechunk.chunk_size() > 3 * npalette.size())
throw std::runtime_error("PLTE too large");
for(size_t i = 0; i < dechunk.chunk_size() / 3; i++) {
uint8_t buf[3];
dechunk.chunk_read(buf, 3);
npalette[i] = (npalette[i] & 0xFF000000U) |
((uint32_t)buf[0] << 16) |
((uint32_t)buf[1] << 8) |
((uint32_t)buf[2]);
}
break;
case 0x74524E53: //tRNS.
if(hdr.type == 4 || hdr.type == 6)
throw std::runtime_error("Illegal tRNS in types 4/6");
else if(hdr.type == 0) {
if(dechunk.chunk_size() != 2)
throw std::runtime_error("Expected 2-byte tRNS for type0");
dechunk.chunk_read(trans, 2);
} else if(hdr.type == 2) {
if(dechunk.chunk_size() != 6)
throw std::runtime_error("Expected 6-byte tRNS for type2");
dechunk.chunk_read(trans, 6);
} else if(hdr.type == 3) {
if(dechunk.chunk_size() > npalette.size())
throw std::runtime_error("tRNS too large");
for(size_t i = 0; i < dechunk.chunk_size(); i++) {
uint8_t buf[1];
dechunk.chunk_read(buf, 1);
npalette[i] = (npalette[i] & 0x00FFFFFFU) |
((uint32_t)buf[0] << 24);
}
}
_trans = trans;
break;
case 0x49444154: //IDAT.
if(scanline == resolution.second)
goto next_pass;
while(pixdecoder->decode(&scanlineb[0], _trans)) {
size_t rline = scanline * pinfo.ymod + pinfo.yoff;
for(size_t i = 0; i < resolution.first; i++) {
ndata[rline * hdr.width + (i * pinfo.xmod + pinfo.xoff)] =
scanlineb[i];
}
scanline++;
if(scanline == resolution.second)
goto next_pass;
}
break;
default:
if((dechunk.chunk_type() & 0x20000000U) == 0)
throw std::runtime_error("Unknown critical chunk");
break;
}
dechunk.next_chunk();
}
next_pass:
;
}
while(dechunk.next_chunk()) {
void decode_png(std::istream& stream, png_decoded_image& out)
{
png_dechunker dechunk(stream);
if(!dechunk.next_chunk())
throw std::runtime_error("PNG file has no chunks");
ihdr_chunk hdr(dechunk);
autorelease<png_decompressor> idat_decomp(png_decompressor::get(dechunk, hdr.compression));
autorelease<png_filterbank> filterbank(png_filterbank::get(*idat_decomp, hdr.filter, hdr.type,
hdr.depth, hdr.width));
autorelease<png_pixel_decoder> pixdecoder(png_pixel_decoder::get(*filterbank, hdr.type, hdr.depth,
hdr.width));
autorelease<png_interlacing> interlace(png_interlacing::get(hdr.interlace));
std::vector<uint32_t> ndata;
std::vector<uint32_t> npalette;
ndata.resize(hdr.width * hdr.height);
if(ndata.size() / hdr.width != hdr.height)
throw std::bad_alloc();
if(hdr.type == 3) {
npalette.resize(1 << hdr.depth);
for(size_t i = 0; i < npalette.size(); i++)
npalette[i] = 0xFF000000U;
}
if(!dechunk.next_chunk())
throw std::runtime_error("PNG file has no chunks besides header");
uint8_t trans[6];
uint8_t* _trans = NULL;
for(size_t pass = 0; pass < interlace->passes(); pass++) {
size_t scanline = 0;
png_interlacing::pass_info pinfo = interlace->pass(pass);
auto resolution = png_interlacing::pass_size(hdr.width, hdr.height, pinfo);
pixdecoder->adjust_row(resolution.first);
std::vector<uint32_t> scanlineb;
scanlineb.resize(resolution.first);
while(true) {
switch(dechunk.chunk_type()) {
case 0x49454E44: //IEND.
goto out;
case 0x504C5445: //PLTE
throw std::runtime_error("PLTE not allowed after image data");
throw std::runtime_error("Unexpected IEND chunk");
case 0x504C5445: //PLTE.
if(hdr.type == 0 || hdr.type == 4)
throw std::runtime_error("Illegal PLTE in types 0/4");
if(hdr.type == 2 || hdr.type == 6)
break; //Advisory.
if(dechunk.chunk_size() > 3 * npalette.size())
throw std::runtime_error("PLTE too large");
for(size_t i = 0; i < dechunk.chunk_size() / 3; i++) {
uint8_t buf[3];
dechunk.chunk_read(buf, 3);
npalette[i] = (npalette[i] & 0xFF000000U) |
((uint32_t)buf[0] << 16) |
((uint32_t)buf[1] << 8) |
((uint32_t)buf[2]);
}
break;
case 0x74524E53: //tRNS.
if(hdr.type == 4 || hdr.type == 6)
throw std::runtime_error("Illegal tRNS in types 4/6");
else if(hdr.type == 0) {
if(dechunk.chunk_size() != 2)
throw std::runtime_error("Expected 2-byte tRNS for type0");
dechunk.chunk_read(trans, 2);
} else if(hdr.type == 2) {
if(dechunk.chunk_size() != 6)
throw std::runtime_error("Expected 6-byte tRNS for type2");
dechunk.chunk_read(trans, 6);
} else if(hdr.type == 3) {
if(dechunk.chunk_size() > npalette.size())
throw std::runtime_error("tRNS too large");
for(size_t i = 0; i < dechunk.chunk_size(); i++) {
uint8_t buf[1];
dechunk.chunk_read(buf, 1);
npalette[i] = (npalette[i] & 0x00FFFFFFU) |
((uint32_t)buf[0] << 24);
}
}
_trans = trans;
break;
case 0x49444154: //IDAT.
if(scanline == resolution.second)
goto next_pass;
while(pixdecoder->decode(&scanlineb[0], _trans)) {
size_t rline = scanline * pinfo.ymod + pinfo.yoff;
for(size_t i = 0; i < resolution.first; i++) {
ndata[rline * hdr.width + (i * pinfo.xmod + pinfo.xoff)] =
scanlineb[i];
}
scanline++;
if(scanline == resolution.second)
goto next_pass;
}
break;
default:
if((dechunk.chunk_type() & 0x20000000U) == 0)
throw std::runtime_error("Unknown critical chunk");
break;
}
dechunk.next_chunk();
}
out:
std::swap(out.data, ndata);
std::swap(out.palette, npalette);
out.has_palette = (hdr.type == 3);
out.width = hdr.width;
out.height = hdr.height;
next_pass:
;
}
while(dechunk.next_chunk()) {
switch(dechunk.chunk_type()) {
case 0x49454E44: //IEND.
goto out;
case 0x504C5445: //PLTE
throw std::runtime_error("PLTE not allowed after image data");
case 0x49444154: //IDAT.
break;
default:
if((dechunk.chunk_type() & 0x20000000U) == 0)
throw std::runtime_error("Unknown critical chunk");
}
}
out:
std::swap(out.data, ndata);
std::swap(out.palette, npalette);
out.has_palette = (hdr.type == 3);
out.width = hdr.width;
out.height = hdr.height;
}
void decode_png(const std::string& file, png_decoded_image& out)
@ -850,5 +850,4 @@ int main(int argc, char** argv)
}
//evaluate-lua b,p=gui.bitmap_load_png("/tmp/tbgn2c16.png"); on_paint = function() gui.bitmap_draw(0,0,b,p); end
*/

View file

@ -7,6 +7,7 @@
#include "lua/bitmap.hpp"
#include "library/threadtypes.hpp"
#include <vector>
#include <sstream>
lua_bitmap::lua_bitmap(uint32_t w, uint32_t h)
{
@ -300,9 +301,10 @@ namespace
return 0;
});
function_ptr_luafun gui_loadbitmap(LS, "gui.bitmap_load", [](lua_state& L, const std::string& fname) -> int {
std::string name = L.get_string(1, fname.c_str());
auto bitmap = lua_loaded_bitmap::load(name);
int bitmap_load_fn(lua_state& L, std::function<lua_loaded_bitmap()> src)
{
uint32_t w, h;
auto bitmap = src();
if(bitmap.d) {
lua_dbitmap* b = lua_class<lua_dbitmap>::create(L, bitmap.w, bitmap.h);
for(size_t i = 0; i < bitmap.w * bitmap.h; i++)
@ -318,6 +320,20 @@ namespace
p->colors[i] = premultiplied_color(bitmap.palette[i]);
return 2;
}
}
function_ptr_luafun gui_loadbitmap(LS, "gui.bitmap_load", [](lua_state& L, const std::string& fname) -> int {
std::string name = L.get_string(1, fname.c_str());
return bitmap_load_fn(L, [&name]() -> lua_loaded_bitmap { return lua_loaded_bitmap::load(name); });
});
function_ptr_luafun gui_loadbitmap2(LS, "gui.bitmap_load_str", [](lua_state& L, const std::string& fname)
-> int {
std::string contents = L.get_string(1, fname.c_str());
return bitmap_load_fn(L, [&contents]() -> lua_loaded_bitmap {
std::istringstream strm(contents);
return lua_loaded_bitmap::load(strm);
});
});
inline int64_t mangle_color(uint32_t c)
@ -328,11 +344,60 @@ namespace
return ((256 - (c >> 24) - (c >> 31)) << 24) | (c & 0xFFFFFF);
}
function_ptr_luafun gui_loadbitmappng(LS, "gui.bitmap_load_png", [](lua_state& L, const std::string& fname)
-> int {
std::string name = L.get_string(1, fname.c_str());
int base64val(char ch)
{
if(ch >= 'A' && ch <= 'Z')
return ch - 65;
if(ch >= 'a' && ch <= 'z')
return ch - 97 + 26;
if(ch >= '0' && ch <= '9')
return ch - 48 + 52;
if(ch == '+')
return 62;
if(ch == '/')
return 63;
if(ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n')
return -1;
if(ch == '=')
return -2;
return -3;
}
std::string base64_decode(const std::string& str)
{
bool end = 0;
uint32_t memory = 0;
uint32_t memsize = 1;
int posmod = 0;
std::ostringstream x;
for(auto i : str) {
int v = base64val(i);
if(v == -1)
continue;
posmod = (posmod + 1) & 3;
if(v == -2 && (posmod == 1 || posmod == 2))
throw std::runtime_error("Invalid Base64");
if(v == -2) {
end = true;
continue;
}
if(v == -3 || end)
throw std::runtime_error("Invalid Base64");
memory = memory * 64 + v;
memsize = memsize * 64;
if(memsize >= 256) {
memsize >>= 8;
x << static_cast<uint8_t>(memory / memsize);
memory %= memsize;
}
}
return x.str();
}
int bitmap_load_png_fn(lua_state& L, std::function<void(png_decoded_image&)> src)
{
png_decoded_image img;
decode_png(name, img);
src(img);
if(img.has_palette) {
lua_bitmap* b = lua_class<lua_bitmap>::create(L, img.width, img.height);
lua_palette* p = lua_class<lua_palette>::create(L);
@ -348,50 +413,78 @@ namespace
b->pixels[i] = premultiplied_color(mangle_color(img.data[i]));
return 1;
}
}
function_ptr_luafun gui_loadbitmappng(LS, "gui.bitmap_load_png", [](lua_state& L, const std::string& fname)
-> int {
std::string name = L.get_string(1, fname.c_str());
return bitmap_load_png_fn(L, [&name](png_decoded_image& img) { decode_png(name, img); });
});
function_ptr_luafun gui_loadbitmappng2(LS, "gui.bitmap_load_png_str", [](lua_state& L,
const std::string& fname) -> int {
std::string contents = base64_decode(L.get_string(1, fname.c_str()));
return bitmap_load_png_fn(L, [&contents](png_decoded_image& img) {
std::istringstream strm(contents);
decode_png(strm, img);
});
});
int bitmap_palette_fn(lua_state& L, std::istream& s)
{
lua_palette* p = lua_class<lua_palette>::create(L);
while(s) {
std::string line;
std::getline(s, line);
istrip_CR(line);
regex_results r;
if(r = regex("[ \t]*([0-9]+)[ \t]+([0-9]+)[ \t]+([0-9]+)[ \t]+([0-9]+)[ \t]*", line)) {
int64_t cr, cg, cb, ca;
cr = parse_value<uint8_t>(r[1]);
cg = parse_value<uint8_t>(r[2]);
cb = parse_value<uint8_t>(r[3]);
ca = 256 - parse_value<uint16_t>(r[4]);
int64_t clr;
if(ca == 256)
p->colors.push_back(premultiplied_color(-1));
else
p->colors.push_back(premultiplied_color((ca << 24) | (cr << 16)
| (cg << 8) | cb));
} else if(r = regex("[ \t]*([0-9]+)[ \t]+([0-9]+)[ \t]+([0-9]+)[ \t]*", line)) {
int64_t cr, cg, cb;
cr = parse_value<uint8_t>(r[1]);
cg = parse_value<uint8_t>(r[2]);
cb = parse_value<uint8_t>(r[3]);
p->colors.push_back(premultiplied_color((cr << 16) | (cg << 8) | cb));
} else if(regex_match("[ \t]*transparent[ \t]*", line)) {
p->colors.push_back(premultiplied_color(-1));
} else if(!regex_match("[ \t]*(#.*)?", line))
throw std::runtime_error("Invalid line format (" + line + ")");
}
return 1;
}
function_ptr_luafun gui_loadpalette(LS, "gui.bitmap_load_pal", [](lua_state& L, const std::string& fname)
-> int {
std::string name = L.get_string(1, fname.c_str());
std::istream& s = open_file_relative(name, "");
try {
lua_palette* p = lua_class<lua_palette>::create(L);
while(s) {
std::string line;
std::getline(s, line);
istrip_CR(line);
regex_results r;
if(r = regex("[ \t]*([0-9]+)[ \t]+([0-9]+)[ \t]+([0-9]+)[ \t]+([0-9]+)[ \t]*", line)) {
int64_t cr, cg, cb, ca;
cr = parse_value<uint8_t>(r[1]);
cg = parse_value<uint8_t>(r[2]);
cb = parse_value<uint8_t>(r[3]);
ca = 256 - parse_value<uint16_t>(r[4]);
int64_t clr;
if(ca == 256)
p->colors.push_back(premultiplied_color(-1));
else
p->colors.push_back(premultiplied_color((ca << 24) | (cr << 16)
| (cg << 8) | cb));
} else if(r = regex("[ \t]*([0-9]+)[ \t]+([0-9]+)[ \t]+([0-9]+)[ \t]*", line)) {
int64_t cr, cg, cb;
cr = parse_value<uint8_t>(r[1]);
cg = parse_value<uint8_t>(r[2]);
cb = parse_value<uint8_t>(r[3]);
p->colors.push_back(premultiplied_color((cr << 16) | (cg << 8) | cb));
} else if(regex_match("[ \t]*transparent[ \t]*", line)) {
p->colors.push_back(premultiplied_color(-1));
} else if(!regex_match("[ \t]*(#.*)?", line))
throw std::runtime_error("Invalid line format (" + line + ")");
}
int r = bitmap_palette_fn(L, s);
delete &s;
return 1;
return r;
} catch(...) {
delete &s;
throw;
}
});
function_ptr_luafun gui_loadpalette2(LS, "gui.bitmap_load_pal_str", [](lua_state& L, const std::string& fname)
-> int {
std::string content = L.get_string(1, fname.c_str());
std::istringstream s(content);
return bitmap_palette_fn(L, s);
});
function_ptr_luafun gui_dpalette(LS, "gui.palette_debug", [](lua_state& L, const std::string& fname) -> int {
lua_palette* p = lua_class<lua_palette>::get(L, 1, fname.c_str());
size_t i = 0;

View file

@ -14,67 +14,72 @@ namespace
}
}
struct lua_loaded_bitmap lua_loaded_bitmap::load(std::istream& file)
{
struct lua_loaded_bitmap b;
std::string magic;
unsigned pcolors;
unsigned R;
unsigned G;
unsigned B;
unsigned A;
file >> magic;
if(magic != "LSNES-BITMAP")
throw std::runtime_error("Bitmap load: Wrong magic");
file >> b.w;
file >> b.h;
if(b.h >= std::numeric_limits<size_t>::max() / b.w)
throw std::runtime_error("Bitmap load: Bitmap too large");
b.bitmap.resize(b.w * b.h);
file >> pcolors;
if(pcolors > 65536)
throw std::runtime_error("Bitmap load: Palette too big");
if(pcolors > 0) {
//Paletted.
b.d = false;
b.palette.resize(pcolors);
for(size_t i = 0; i < pcolors; i++) {
file >> R;
file >> G;
file >> B;
file >> A;
if(R > 255 || G > 255 || B > 255 || A > 256) //Yes, a can be 256.
throw std::runtime_error("Bitmap load: Palette entry out of range");
b.palette[i] = tocolor(R, G, B, A);
}
for(size_t i = 0; i < b.w * b.h; i++) {
file >> R;
if(R >= pcolors)
throw std::runtime_error("Bitmap load: color index out of range");
b.bitmap[i] = R;
}
} else {
//Direct.
b.d = true;
for(size_t i = 0; i < b.w * b.h; i++) {
file >> R;
file >> G;
file >> B;
file >> A;
if(R > 255 || G > 255 || B > 255 || A > 256) //Yes, a can be 256.
throw std::runtime_error("Bitmap load: Color out of range");
b.bitmap[i] = tocolor(R, G, B, A);
}
}
if(!file)
throw std::runtime_error("Bitmap load: Error reading bitmap");
}
struct lua_loaded_bitmap lua_loaded_bitmap::load(const std::string& name)
{
struct lua_loaded_bitmap b;
std::istream* file = NULL;
std::istream& file = open_file_relative(name, "");
try {
std::string magic;
unsigned pcolors;
unsigned R;
unsigned G;
unsigned B;
unsigned A;
file = &open_file_relative(name, "");
*file >> magic;
if(magic != "LSNES-BITMAP")
throw std::runtime_error("Bitmap load: Wrong magic");
*file >> b.w;
*file >> b.h;
if(b.h >= std::numeric_limits<size_t>::max() / b.w)
throw std::runtime_error("Bitmap load: Bitmap too large");
b.bitmap.resize(b.w * b.h);
*file >> pcolors;
if(pcolors > 65536)
throw std::runtime_error("Bitmap load: Palette too big");
if(pcolors > 0) {
//Paletted.
b.d = false;
b.palette.resize(pcolors);
for(size_t i = 0; i < pcolors; i++) {
*file >> R;
*file >> G;
*file >> B;
*file >> A;
if(R > 255 || G > 255 || B > 255 || A > 256) //Yes, a can be 256.
throw std::runtime_error("Bitmap load: Palette entry out of range");
b.palette[i] = tocolor(R, G, B, A);
}
for(size_t i = 0; i < b.w * b.h; i++) {
*file >> R;
if(R >= pcolors)
throw std::runtime_error("Bitmap load: color index out of range");
b.bitmap[i] = R;
}
} else {
//Direct.
b.d = true;
for(size_t i = 0; i < b.w * b.h; i++) {
*file >> R;
*file >> G;
*file >> B;
*file >> A;
if(R > 255 || G > 255 || B > 255 || A > 256) //Yes, a can be 256.
throw std::runtime_error("Bitmap load: Color out of range");
b.bitmap[i] = tocolor(R, G, B, A);
}
}
if(!*file)
throw std::runtime_error("Bitmap load: Error reading bitmap");
b = lua_loaded_bitmap::load(file);
delete &file;
return b;
} catch(...) {
delete file;
delete &file;
throw;
}
delete file;
return b;
}