Merge branch 'main' into 12.0-development

This commit is contained in:
Alex Szpakowski
2021-10-19 21:50:19 -03:00
13 changed files with 1399 additions and 2581 deletions
+136 -49
View File
@@ -1,7 +1,7 @@
/*
LodePNG version 20200306
LodePNG version 20210627
Copyright (c) 2005-2020 Lode Vandevenne
Copyright (c) 2005-2021 Lode Vandevenne
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
@@ -44,7 +44,7 @@ Rename this file to lodepng.cpp to use it for C++, or to lodepng.c to use it for
#pragma warning( disable : 4996 ) /*VS does not like fopen, but fopen_s is not standard C so unusable here*/
#endif /*_MSC_VER */
const char* LODEPNG_VERSION_STRING = "20200306";
const char* LODEPNG_VERSION_STRING = "20210627";
/*
This source file is built up in the following large parts. The code sections
@@ -299,6 +299,7 @@ static void string_cleanup(char** out) {
*out = NULL;
}
/*also appends null termination character*/
static char* alloc_string_sized(const char* in, size_t insize) {
char* out = (char*)lodepng_malloc(insize + 1);
if(out) {
@@ -1260,7 +1261,7 @@ static unsigned getTreeInflateDynamic(HuffmanTree* tree_ll, HuffmanTree* tree_d,
/*inflate a block with dynamic of fixed Huffman tree. btype must be 1 or 2.*/
static unsigned inflateHuffmanBlock(ucvector* out, LodePNGBitReader* reader,
unsigned btype) {
unsigned btype, size_t max_output_size) {
unsigned error = 0;
HuffmanTree tree_ll; /*the huffman tree for literal and length codes*/
HuffmanTree tree_d; /*the huffman tree for distance codes*/
@@ -1341,6 +1342,9 @@ static unsigned inflateHuffmanBlock(ucvector* out, LodePNGBitReader* reader,
/* TODO: revise error codes 10,11,50: the above comment is no longer valid */
ERROR_BREAK(51); /*error, bit pointer jumps past memory*/
}
if(max_output_size && out->size > max_output_size) {
ERROR_BREAK(109); /*error, larger than max size*/
}
}
HuffmanTree_cleanup(&tree_ll);
@@ -1398,9 +1402,9 @@ static unsigned lodepng_inflatev(ucvector* out,
if(BTYPE == 3) return 20; /*error: invalid BTYPE*/
else if(BTYPE == 0) error = inflateNoCompression(out, &reader, settings); /*no compression*/
else error = inflateHuffmanBlock(out, &reader, BTYPE); /*compression, BTYPE 01 or 10*/
if(error) return error;
else error = inflateHuffmanBlock(out, &reader, BTYPE, settings->max_output_size); /*compression, BTYPE 01 or 10*/
if(!error && settings->max_output_size && out->size > settings->max_output_size) error = 109;
if(error) break;
}
return error;
@@ -1421,6 +1425,12 @@ static unsigned inflatev(ucvector* out, const unsigned char* in, size_t insize,
if(settings->custom_inflate) {
unsigned error = settings->custom_inflate(&out->data, &out->size, in, insize, settings);
out->allocsize = out->size;
if(error) {
/*the custom inflate is allowed to have its own error codes, however, we translate it to code 110*/
error = 110;
/*if there's a max output size, and the custom zlib returned error, then indicate that error instead*/
if(settings->max_output_size && out->size > settings->max_output_size) error = 109;
}
return error;
} else {
return lodepng_inflatev(out, in, insize, settings);
@@ -2116,7 +2126,9 @@ static unsigned deflate(unsigned char** out, size_t* outsize,
const unsigned char* in, size_t insize,
const LodePNGCompressSettings* settings) {
if(settings->custom_deflate) {
return settings->custom_deflate(out, outsize, in, insize, settings);
unsigned error = settings->custom_deflate(out, outsize, in, insize, settings);
/*the custom deflate is allowed to have its own error codes, however, we translate it to code 111*/
return error ? 111 : 0;
} else {
return lodepng_deflate(out, outsize, in, insize, settings);
}
@@ -2213,10 +2225,16 @@ unsigned lodepng_zlib_decompress(unsigned char** out, size_t* outsize, const uns
/*expected_size is expected output size, to avoid intermediate allocations. Set to 0 if not known. */
static unsigned zlib_decompress(unsigned char** out, size_t* outsize, size_t expected_size,
const unsigned char* in, size_t insize, const LodePNGDecompressSettings* settings) {
unsigned error;
if(settings->custom_zlib) {
return settings->custom_zlib(out, outsize, in, insize, settings);
error = settings->custom_zlib(out, outsize, in, insize, settings);
if(error) {
/*the custom zlib is allowed to have its own error codes, however, we translate it to code 110*/
error = 110;
/*if there's a max output size, and the custom zlib returned error, then indicate that error instead*/
if(settings->max_output_size && *outsize > settings->max_output_size) error = 109;
}
} else {
unsigned error;
ucvector v = ucvector_init(*out, *outsize);
if(expected_size) {
/*reserve the memory to avoid intermediate reallocations*/
@@ -2226,8 +2244,8 @@ static unsigned zlib_decompress(unsigned char** out, size_t* outsize, size_t exp
error = lodepng_zlib_decompressv(&v, in, insize, settings);
*out = v.data;
*outsize = v.size;
return error;
}
return error;
}
#endif /*LODEPNG_COMPILE_DECODER*/
@@ -2275,7 +2293,9 @@ unsigned lodepng_zlib_compress(unsigned char** out, size_t* outsize, const unsig
static unsigned zlib_compress(unsigned char** out, size_t* outsize, const unsigned char* in,
size_t insize, const LodePNGCompressSettings* settings) {
if(settings->custom_zlib) {
return settings->custom_zlib(out, outsize, in, insize, settings);
unsigned error = settings->custom_zlib(out, outsize, in, insize, settings);
/*the custom zlib is allowed to have its own error codes, however, we translate it to code 111*/
return error ? 111 : 0;
} else {
return lodepng_zlib_compress(out, outsize, in, insize, settings);
}
@@ -2334,13 +2354,14 @@ const LodePNGCompressSettings lodepng_default_compress_settings = {2, 1, DEFAULT
void lodepng_decompress_settings_init(LodePNGDecompressSettings* settings) {
settings->ignore_adler32 = 0;
settings->ignore_nlen = 0;
settings->max_output_size = 0;
settings->custom_zlib = 0;
settings->custom_inflate = 0;
settings->custom_context = 0;
}
const LodePNGDecompressSettings lodepng_default_decompress_settings = {0, 0, 0, 0, 0};
const LodePNGDecompressSettings lodepng_default_decompress_settings = {0, 0, 0, 0, 0, 0};
#endif /*LODEPNG_COMPILE_DECODER*/
@@ -2872,8 +2893,8 @@ static void LodePNGText_cleanup(LodePNGInfo* info) {
static unsigned LodePNGText_copy(LodePNGInfo* dest, const LodePNGInfo* source) {
size_t i = 0;
dest->text_keys = 0;
dest->text_strings = 0;
dest->text_keys = NULL;
dest->text_strings = NULL;
dest->text_num = 0;
for(i = 0; i != source->text_num; ++i) {
CERROR_TRY_RETURN(lodepng_add_text(dest, source->text_keys[i], source->text_strings[i]));
@@ -2932,10 +2953,10 @@ static void LodePNGIText_cleanup(LodePNGInfo* info) {
static unsigned LodePNGIText_copy(LodePNGInfo* dest, const LodePNGInfo* source) {
size_t i = 0;
dest->itext_keys = 0;
dest->itext_langtags = 0;
dest->itext_transkeys = 0;
dest->itext_strings = 0;
dest->itext_keys = NULL;
dest->itext_langtags = NULL;
dest->itext_transkeys = NULL;
dest->itext_strings = NULL;
dest->itext_num = 0;
for(i = 0; i != source->itext_num; ++i) {
CERROR_TRY_RETURN(lodepng_add_itext(dest, source->itext_keys[i], source->itext_langtags[i],
@@ -4093,10 +4114,12 @@ static unsigned unfilterScanline(unsigned char* recon, const unsigned char* scan
case 0:
for(i = 0; i != length; ++i) recon[i] = scanline[i];
break;
case 1:
case 1: {
size_t j = 0;
for(i = 0; i != bytewidth; ++i) recon[i] = scanline[i];
for(i = bytewidth; i < length; ++i) recon[i] = scanline[i] + recon[i - bytewidth];
for(i = bytewidth; i != length; ++i, ++j) recon[i] = scanline[i] + recon[j];
break;
}
case 2:
if(precon) {
for(i = 0; i != length; ++i) recon[i] = scanline[i] + precon[i];
@@ -4106,24 +4129,56 @@ static unsigned unfilterScanline(unsigned char* recon, const unsigned char* scan
break;
case 3:
if(precon) {
size_t j = 0;
for(i = 0; i != bytewidth; ++i) recon[i] = scanline[i] + (precon[i] >> 1u);
for(i = bytewidth; i < length; ++i) recon[i] = scanline[i] + ((recon[i - bytewidth] + precon[i]) >> 1u);
/* Unroll independent paths of this predictor. A 6x and 8x version is also possible but that adds
too much code. Whether this speeds up anything depends on compiler and settings. */
if(bytewidth >= 4) {
for(; i + 3 < length; i += 4, j += 4) {
unsigned char s0 = scanline[i + 0], r0 = recon[j + 0], p0 = precon[i + 0];
unsigned char s1 = scanline[i + 1], r1 = recon[j + 1], p1 = precon[i + 1];
unsigned char s2 = scanline[i + 2], r2 = recon[j + 2], p2 = precon[i + 2];
unsigned char s3 = scanline[i + 3], r3 = recon[j + 3], p3 = precon[i + 3];
recon[i + 0] = s0 + ((r0 + p0) >> 1u);
recon[i + 1] = s1 + ((r1 + p1) >> 1u);
recon[i + 2] = s2 + ((r2 + p2) >> 1u);
recon[i + 3] = s3 + ((r3 + p3) >> 1u);
}
} else if(bytewidth >= 3) {
for(; i + 2 < length; i += 3, j += 3) {
unsigned char s0 = scanline[i + 0], r0 = recon[j + 0], p0 = precon[i + 0];
unsigned char s1 = scanline[i + 1], r1 = recon[j + 1], p1 = precon[i + 1];
unsigned char s2 = scanline[i + 2], r2 = recon[j + 2], p2 = precon[i + 2];
recon[i + 0] = s0 + ((r0 + p0) >> 1u);
recon[i + 1] = s1 + ((r1 + p1) >> 1u);
recon[i + 2] = s2 + ((r2 + p2) >> 1u);
}
} else if(bytewidth >= 2) {
for(; i + 1 < length; i += 2, j += 2) {
unsigned char s0 = scanline[i + 0], r0 = recon[j + 0], p0 = precon[i + 0];
unsigned char s1 = scanline[i + 1], r1 = recon[j + 1], p1 = precon[i + 1];
recon[i + 0] = s0 + ((r0 + p0) >> 1u);
recon[i + 1] = s1 + ((r1 + p1) >> 1u);
}
}
for(; i != length; ++i, ++j) recon[i] = scanline[i] + ((recon[j] + precon[i]) >> 1u);
} else {
size_t j = 0;
for(i = 0; i != bytewidth; ++i) recon[i] = scanline[i];
for(i = bytewidth; i < length; ++i) recon[i] = scanline[i] + (recon[i - bytewidth] >> 1u);
for(i = bytewidth; i != length; ++i, ++j) recon[i] = scanline[i] + (recon[j] >> 1u);
}
break;
case 4:
if(precon) {
size_t j = 0;
for(i = 0; i != bytewidth; ++i) {
recon[i] = (scanline[i] + precon[i]); /*paethPredictor(0, precon[i], 0) is always precon[i]*/
}
/* Unroll independent paths of the paeth predictor. A 6x and 8x version would also be possible but that
adds too much code. Whether this actually speeds anything up at all depends on compiler and settings. */
/* Unroll independent paths of the paeth predictor. A 6x and 8x version is also possible but that
adds too much code. Whether this speeds up anything depends on compiler and settings. */
if(bytewidth >= 4) {
for(; i + 3 < length; i += 4) {
size_t j = i - bytewidth;
for(; i + 3 < length; i += 4, j += 4) {
unsigned char s0 = scanline[i + 0], s1 = scanline[i + 1], s2 = scanline[i + 2], s3 = scanline[i + 3];
unsigned char r0 = recon[j + 0], r1 = recon[j + 1], r2 = recon[j + 2], r3 = recon[j + 3];
unsigned char p0 = precon[i + 0], p1 = precon[i + 1], p2 = precon[i + 2], p3 = precon[i + 3];
@@ -4134,8 +4189,7 @@ static unsigned unfilterScanline(unsigned char* recon, const unsigned char* scan
recon[i + 3] = s3 + paethPredictor(r3, p3, q3);
}
} else if(bytewidth >= 3) {
for(; i + 2 < length; i += 3) {
size_t j = i - bytewidth;
for(; i + 2 < length; i += 3, j += 3) {
unsigned char s0 = scanline[i + 0], s1 = scanline[i + 1], s2 = scanline[i + 2];
unsigned char r0 = recon[j + 0], r1 = recon[j + 1], r2 = recon[j + 2];
unsigned char p0 = precon[i + 0], p1 = precon[i + 1], p2 = precon[i + 2];
@@ -4145,8 +4199,7 @@ static unsigned unfilterScanline(unsigned char* recon, const unsigned char* scan
recon[i + 2] = s2 + paethPredictor(r2, p2, q2);
}
} else if(bytewidth >= 2) {
for(; i + 1 < length; i += 2) {
size_t j = i - bytewidth;
for(; i + 1 < length; i += 2, j += 2) {
unsigned char s0 = scanline[i + 0], s1 = scanline[i + 1];
unsigned char r0 = recon[j + 0], r1 = recon[j + 1];
unsigned char p0 = precon[i + 0], p1 = precon[i + 1];
@@ -4156,16 +4209,17 @@ static unsigned unfilterScanline(unsigned char* recon, const unsigned char* scan
}
}
for(; i != length; ++i) {
recon[i] = (scanline[i] + paethPredictor(recon[i - bytewidth], precon[i], precon[i - bytewidth]));
for(; i != length; ++i, ++j) {
recon[i] = (scanline[i] + paethPredictor(recon[i - bytewidth], precon[i], precon[j]));
}
} else {
size_t j = 0;
for(i = 0; i != bytewidth; ++i) {
recon[i] = scanline[i];
}
for(i = bytewidth; i < length; ++i) {
for(i = bytewidth; i != length; ++i, ++j) {
/*paethPredictor(recon[i - bytewidth], 0, 0) is always recon[i - bytewidth]*/
recon[i] = (scanline[i] + recon[i - bytewidth]);
recon[i] = (scanline[i] + recon[j]);
}
}
break;
@@ -4447,10 +4501,13 @@ static unsigned readChunk_tEXt(LodePNGInfo* info, const unsigned char* data, siz
}
/*compressed text chunk (zTXt)*/
static unsigned readChunk_zTXt(LodePNGInfo* info, const LodePNGDecompressSettings* zlibsettings,
static unsigned readChunk_zTXt(LodePNGInfo* info, const LodePNGDecoderSettings* decoder,
const unsigned char* data, size_t chunkLength) {
unsigned error = 0;
/*copy the object to change parameters in it*/
LodePNGDecompressSettings zlibsettings = decoder->zlibsettings;
unsigned length, string2_begin;
char *key = 0;
unsigned char* str = 0;
@@ -4473,12 +4530,14 @@ static unsigned readChunk_zTXt(LodePNGInfo* info, const LodePNGDecompressSetting
if(string2_begin > chunkLength) CERROR_BREAK(error, 75); /*no null termination, corrupt?*/
length = (unsigned)chunkLength - string2_begin;
zlibsettings.max_output_size = decoder->max_text_size;
/*will fail if zlib error, e.g. if length is too small*/
error = zlib_decompress(&str, &size, 0, &data[string2_begin],
length, zlibsettings);
length, &zlibsettings);
/*error: compressed text larger than decoder->max_text_size*/
if(error && size > zlibsettings.max_output_size) error = 112;
if(error) break;
error = lodepng_add_text_sized(info, key, (char*)str, size);
break;
}
@@ -4489,11 +4548,14 @@ static unsigned readChunk_zTXt(LodePNGInfo* info, const LodePNGDecompressSetting
}
/*international text chunk (iTXt)*/
static unsigned readChunk_iTXt(LodePNGInfo* info, const LodePNGDecompressSettings* zlibsettings,
static unsigned readChunk_iTXt(LodePNGInfo* info, const LodePNGDecoderSettings* decoder,
const unsigned char* data, size_t chunkLength) {
unsigned error = 0;
unsigned i;
/*copy the object to change parameters in it*/
LodePNGDecompressSettings zlibsettings = decoder->zlibsettings;
unsigned length, begin, compressed;
char *key = 0, *langtag = 0, *transkey = 0;
@@ -4550,9 +4612,12 @@ static unsigned readChunk_iTXt(LodePNGInfo* info, const LodePNGDecompressSetting
if(compressed) {
unsigned char* str = 0;
size_t size = 0;
zlibsettings.max_output_size = decoder->max_text_size;
/*will fail if zlib error, e.g. if length is too small*/
error = zlib_decompress(&str, &size, 0, &data[begin],
length, zlibsettings);
length, &zlibsettings);
/*error: compressed text larger than decoder->max_text_size*/
if(error && size > zlibsettings.max_output_size) error = 112;
if(!error) error = lodepng_add_itext_sized(info, key, langtag, transkey, (char*)str, size);
lodepng_free(str);
} else {
@@ -4628,11 +4693,13 @@ static unsigned readChunk_sRGB(LodePNGInfo* info, const unsigned char* data, siz
return 0; /* OK */
}
static unsigned readChunk_iCCP(LodePNGInfo* info, const LodePNGDecompressSettings* zlibsettings,
static unsigned readChunk_iCCP(LodePNGInfo* info, const LodePNGDecoderSettings* decoder,
const unsigned char* data, size_t chunkLength) {
unsigned error = 0;
unsigned i;
size_t size = 0;
/*copy the object to change parameters in it*/
LodePNGDecompressSettings zlibsettings = decoder->zlibsettings;
unsigned length, string2_begin;
@@ -4655,9 +4722,12 @@ static unsigned readChunk_iCCP(LodePNGInfo* info, const LodePNGDecompressSetting
if(string2_begin > chunkLength) return 75; /*no null termination, corrupt?*/
length = (unsigned)chunkLength - string2_begin;
zlibsettings.max_output_size = decoder->max_icc_size;
error = zlib_decompress(&info->iccp_profile, &size, 0,
&data[string2_begin],
length, zlibsettings);
length, &zlibsettings);
/*error: ICC profile larger than decoder->max_icc_size*/
if(error && size > zlibsettings.max_output_size) error = 113;
info->iccp_profile_size = size;
if(!error && !info->iccp_profile_size) error = 100; /*invalid ICC profile size*/
return error;
@@ -4688,9 +4758,9 @@ unsigned lodepng_inspect_chunk(LodePNGState* state, size_t pos,
} else if(lodepng_chunk_type_equals(chunk, "tEXt")) {
error = readChunk_tEXt(&state->info_png, data, chunkLength);
} else if(lodepng_chunk_type_equals(chunk, "zTXt")) {
error = readChunk_zTXt(&state->info_png, &state->decoder.zlibsettings, data, chunkLength);
error = readChunk_zTXt(&state->info_png, &state->decoder, data, chunkLength);
} else if(lodepng_chunk_type_equals(chunk, "iTXt")) {
error = readChunk_iTXt(&state->info_png, &state->decoder.zlibsettings, data, chunkLength);
error = readChunk_iTXt(&state->info_png, &state->decoder, data, chunkLength);
} else if(lodepng_chunk_type_equals(chunk, "tIME")) {
error = readChunk_tIME(&state->info_png, data, chunkLength);
} else if(lodepng_chunk_type_equals(chunk, "pHYs")) {
@@ -4702,7 +4772,7 @@ unsigned lodepng_inspect_chunk(LodePNGState* state, size_t pos,
} else if(lodepng_chunk_type_equals(chunk, "sRGB")) {
error = readChunk_sRGB(&state->info_png, data, chunkLength);
} else if(lodepng_chunk_type_equals(chunk, "iCCP")) {
error = readChunk_iCCP(&state->info_png, &state->decoder.zlibsettings, data, chunkLength);
error = readChunk_iCCP(&state->info_png, &state->decoder, data, chunkLength);
#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
} else {
/* unhandled chunk is ok (is not an error) */
@@ -4820,13 +4890,13 @@ static void decodeGeneric(unsigned char** out, unsigned* w, unsigned* h,
} else if(lodepng_chunk_type_equals(chunk, "zTXt")) {
/*compressed text chunk (zTXt)*/
if(state->decoder.read_text_chunks) {
state->error = readChunk_zTXt(&state->info_png, &state->decoder.zlibsettings, data, chunkLength);
state->error = readChunk_zTXt(&state->info_png, &state->decoder, data, chunkLength);
if(state->error) break;
}
} else if(lodepng_chunk_type_equals(chunk, "iTXt")) {
/*international text chunk (iTXt)*/
if(state->decoder.read_text_chunks) {
state->error = readChunk_iTXt(&state->info_png, &state->decoder.zlibsettings, data, chunkLength);
state->error = readChunk_iTXt(&state->info_png, &state->decoder, data, chunkLength);
if(state->error) break;
}
} else if(lodepng_chunk_type_equals(chunk, "tIME")) {
@@ -4845,7 +4915,7 @@ static void decodeGeneric(unsigned char** out, unsigned* w, unsigned* h,
state->error = readChunk_sRGB(&state->info_png, data, chunkLength);
if(state->error) break;
} else if(lodepng_chunk_type_equals(chunk, "iCCP")) {
state->error = readChunk_iCCP(&state->info_png, &state->decoder.zlibsettings, data, chunkLength);
state->error = readChunk_iCCP(&state->info_png, &state->decoder, data, chunkLength);
if(state->error) break;
#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
} else /*it's not an implemented chunk type, so ignore it: skip over the data*/ {
@@ -4871,7 +4941,7 @@ static void decodeGeneric(unsigned char** out, unsigned* w, unsigned* h,
if(!IEND) chunk = lodepng_chunk_next_const(chunk, in + insize);
}
if(state->info_png.color.colortype == LCT_PALETTE && !state->info_png.color.palette) {
if(!state->error && state->info_png.color.colortype == LCT_PALETTE && !state->info_png.color.palette) {
state->error = 106; /* error: PNG file must have PLTE chunk if color type is palette */
}
@@ -4955,6 +5025,11 @@ unsigned lodepng_decode_memory(unsigned char** out, unsigned* w, unsigned* h, co
lodepng_state_init(&state);
state.info_raw.colortype = colortype;
state.info_raw.bitdepth = bitdepth;
#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
/*disable reading things that this function doesn't output*/
state.decoder.read_text_chunks = 0;
state.decoder.remember_unknown_chunks = 0;
#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
error = lodepng_decode(out, w, h, &state, in, insize);
lodepng_state_cleanup(&state);
return error;
@@ -4997,6 +5072,8 @@ void lodepng_decoder_settings_init(LodePNGDecoderSettings* settings) {
#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
settings->read_text_chunks = 1;
settings->remember_unknown_chunks = 0;
settings->max_text_size = 16777216;
settings->max_icc_size = 16777216; /* 16MB is much more than enough for any reasonable ICC profile */
#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
settings->ignore_crc = 0;
settings->ignore_critical = 0;
@@ -6204,6 +6281,16 @@ const char* lodepng_error_text(unsigned code) {
case 106: return "PNG file must have PLTE chunk if color type is palette";
case 107: return "color convert from palette mode requested without setting the palette data in it";
case 108: return "tried to add more than 256 values to a palette";
/*this limit can be configured in LodePNGDecompressSettings*/
case 109: return "tried to decompress zlib or deflate data larger than desired max_output_size";
case 110: return "custom zlib or inflate decompression failed";
case 111: return "custom zlib or deflate compression failed";
/*max text size limit can be configured in LodePNGDecoderSettings. This error prevents
unreasonable memory consumption when decoding due to impossibly large text sizes.*/
case 112: return "compressed text unreasonably large";
/*max ICC size limit can be configured in LodePNGDecoderSettings. This error prevents
unreasonable memory consumption when decoding due to impossibly large ICC profile*/
case 113: return "ICC profile unreasonably large";
}
return "unknown error code";
}
+91 -17
View File
@@ -1,7 +1,7 @@
/*
LodePNG version 20200306
LodePNG version 20210627
Copyright (c) 2005-2020 Lode Vandevenne
Copyright (c) 2005-2021 Lode Vandevenne
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
@@ -142,16 +142,24 @@ unsigned lodepng_decode24(unsigned char** out, unsigned* w, unsigned* h,
/*
Load PNG from disk, from file with given name.
Same as the other decode functions, but instead takes a filename as input.
*/
NOTE: Wide-character filenames are not supported, you can use an external method
to handle such files and decode in-memory.*/
unsigned lodepng_decode_file(unsigned char** out, unsigned* w, unsigned* h,
const char* filename,
LodePNGColorType colortype, unsigned bitdepth);
/*Same as lodepng_decode_file, but always decodes to 32-bit RGBA raw image.*/
/*Same as lodepng_decode_file, but always decodes to 32-bit RGBA raw image.
NOTE: Wide-character filenames are not supported, you can use an external method
to handle such files and decode in-memory.*/
unsigned lodepng_decode32_file(unsigned char** out, unsigned* w, unsigned* h,
const char* filename);
/*Same as lodepng_decode_file, but always decodes to 24-bit RGB raw image.*/
/*Same as lodepng_decode_file, but always decodes to 24-bit RGB raw image.
NOTE: Wide-character filenames are not supported, you can use an external method
to handle such files and decode in-memory.*/
unsigned lodepng_decode24_file(unsigned char** out, unsigned* w, unsigned* h,
const char* filename);
#endif /*LODEPNG_COMPILE_DISK*/
@@ -191,17 +199,26 @@ unsigned lodepng_encode24(unsigned char** out, size_t* outsize,
/*
Converts raw pixel data into a PNG file on disk.
Same as the other encode functions, but instead takes a filename as output.
NOTE: This overwrites existing files without warning!
*/
NOTE: Wide-character filenames are not supported, you can use an external method
to handle such files and encode in-memory.*/
unsigned lodepng_encode_file(const char* filename,
const unsigned char* image, unsigned w, unsigned h,
LodePNGColorType colortype, unsigned bitdepth);
/*Same as lodepng_encode_file, but always encodes from 32-bit RGBA raw image.*/
/*Same as lodepng_encode_file, but always encodes from 32-bit RGBA raw image.
NOTE: Wide-character filenames are not supported, you can use an external method
to handle such files and encode in-memory.*/
unsigned lodepng_encode32_file(const char* filename,
const unsigned char* image, unsigned w, unsigned h);
/*Same as lodepng_encode_file, but always encodes from 24-bit RGB raw image.*/
/*Same as lodepng_encode_file, but always encodes from 24-bit RGB raw image.
NOTE: Wide-character filenames are not supported, you can use an external method
to handle such files and encode in-memory.*/
unsigned lodepng_encode24_file(const char* filename,
const unsigned char* image, unsigned w, unsigned h);
#endif /*LODEPNG_COMPILE_DISK*/
@@ -223,6 +240,9 @@ unsigned decode(std::vector<unsigned char>& out, unsigned& w, unsigned& h,
/*
Converts PNG file from disk to raw pixel data in memory.
Same as the other decode functions, but instead takes a filename as input.
NOTE: Wide-character filenames are not supported, you can use an external method
to handle such files and decode in-memory.
*/
unsigned decode(std::vector<unsigned char>& out, unsigned& w, unsigned& h,
const std::string& filename,
@@ -243,7 +263,11 @@ unsigned encode(std::vector<unsigned char>& out,
/*
Converts 32-bit RGBA raw pixel data into a PNG file on disk.
Same as the other encode functions, but instead takes a filename as output.
NOTE: This overwrites existing files without warning!
NOTE: Wide-character filenames are not supported, you can use an external method
to handle such files and decode in-memory.
*/
unsigned encode(const std::string& filename,
const unsigned char* in, unsigned w, unsigned h,
@@ -270,12 +294,21 @@ struct LodePNGDecompressSettings {
unsigned ignore_adler32; /*if 1, continue and don't give an error message if the Adler32 checksum is corrupted*/
unsigned ignore_nlen; /*ignore complement of len checksum in uncompressed blocks*/
/*use custom zlib decoder instead of built in one (default: null)*/
/*Maximum decompressed size, beyond this the decoder may (and is encouraged to) stop decoding,
return an error, output a data size > max_output_size and all the data up to that point. This is
not hard limit nor a guarantee, but can prevent excessive memory usage. This setting is
ignored by the PNG decoder, but is used by the deflate/zlib decoder and can be used by custom ones.
Set to 0 to impose no limit (the default).*/
size_t max_output_size;
/*use custom zlib decoder instead of built in one (default: null).
Should return 0 if success, any non-0 if error (numeric value not exposed).*/
unsigned (*custom_zlib)(unsigned char**, size_t*,
const unsigned char*, size_t,
const LodePNGDecompressSettings*);
/*use custom deflate decoder instead of built in one (default: null)
if custom_zlib is not null, custom_inflate is ignored (the zlib format uses deflate)*/
if custom_zlib is not null, custom_inflate is ignored (the zlib format uses deflate).
Should return 0 if success, any non-0 if error (numeric value not exposed).*/
unsigned (*custom_inflate)(unsigned char**, size_t*,
const unsigned char*, size_t,
const LodePNGDecompressSettings*);
@@ -454,30 +487,36 @@ typedef struct LodePNGInfo {
unsigned background_b; /*blue component of suggested background color*/
/*
non-international text chunks (tEXt and zTXt)
Non-international text chunks (tEXt and zTXt)
The char** arrays each contain num strings. The actual messages are in
text_strings, while text_keys are keywords that give a short description what
the actual text represents, e.g. Title, Author, Description, or anything else.
All the string fields below including keys, names and language tags are null terminated.
All the string fields below including strings, keys, names and language tags are null terminated.
The PNG specification uses null characters for the keys, names and tags, and forbids null
characters to appear in the main text which is why we can use null termination everywhere here.
A keyword is minimum 1 character and maximum 79 characters long. It's
discouraged to use a single line length longer than 79 characters for texts.
A keyword is minimum 1 character and maximum 79 characters long (plus the
additional null terminator). It's discouraged to use a single line length
longer than 79 characters for texts.
Don't allocate these text buffers yourself. Use the init/cleanup functions
correctly and use lodepng_add_text and lodepng_clear_text.
Standard text chunk keywords and strings are encoded using Latin-1.
*/
size_t text_num; /*the amount of texts in these char** buffers (there may be more texts in itext)*/
char** text_keys; /*the keyword of a text chunk (e.g. "Comment")*/
char** text_strings; /*the actual text*/
/*
international text chunks (iTXt)
International text chunks (iTXt)
Similar to the non-international text chunks, but with additional strings
"langtags" and "transkeys".
"langtags" and "transkeys", and the following text encodings are used:
keys: Latin-1, langtags: ASCII, transkeys and strings: UTF-8.
keys must be 1-79 characters (plus the additional null terminator), the other
strings are any length.
*/
size_t itext_num; /*the amount of international texts in this PNG*/
char** itext_keys; /*the English keyword of the text chunk (e.g. "Comment")*/
@@ -639,8 +678,19 @@ typedef struct LodePNGDecoderSettings {
#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
unsigned read_text_chunks; /*if false but remember_unknown_chunks is true, they're stored in the unknown chunks*/
/*store all bytes from unknown chunks in the LodePNGInfo (off by default, useful for a png editor)*/
unsigned remember_unknown_chunks;
/* maximum size for decompressed text chunks. If a text chunk's text is larger than this, an error is returned,
unless reading text chunks is disabled or this limit is set higher or disabled. Set to 0 to allow any size.
By default it is a value that prevents unreasonably large strings from hogging memory. */
size_t max_text_size;
/* maximum size for compressed ICC chunks. If the ICC profile is larger than this, an error will be returned. Set to
0 to allow any size. By default this is a value that prevents ICC profiles that would be much larger than any
legitimate profile could be to hog memory. */
size_t max_icc_size;
#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
} LodePNGDecoderSettings;
@@ -950,6 +1000,9 @@ out: output parameter, contains pointer to loaded buffer.
outsize: output parameter, size of the allocated out buffer
filename: the path to the file to load
return value: error code (0 means ok)
NOTE: Wide-character filenames are not supported, you can use an external method
to handle such files and decode in-memory.
*/
unsigned lodepng_load_file(unsigned char** out, size_t* outsize, const char* filename);
@@ -960,6 +1013,9 @@ buffer: the buffer to write
buffersize: size of the buffer to write
filename: the path to the file to save to
return value: error code (0 means ok)
NOTE: Wide-character filenames are not supported, you can use an external method
to handle such files and encode in-memory
*/
unsigned lodepng_save_file(const unsigned char* buffer, size_t buffersize, const char* filename);
#endif /*LODEPNG_COMPILE_DISK*/
@@ -1000,12 +1056,18 @@ unsigned encode(std::vector<unsigned char>& out,
/*
Load a file from disk into an std::vector.
return value: error code (0 means ok)
NOTE: Wide-character filenames are not supported, you can use an external method
to handle such files and decode in-memory
*/
unsigned load_file(std::vector<unsigned char>& buffer, const std::string& filename);
/*
Save the binary data in an std::vector to a file on disk. The file is overwritten
without warning.
NOTE: Wide-character filenames are not supported, you can use an external method
to handle such files and encode in-memory
*/
unsigned save_file(const std::vector<unsigned char>& buffer, const std::string& filename);
#endif /* LODEPNG_COMPILE_DISK */
@@ -1505,6 +1567,11 @@ of the error in English as a string.
Check the implementation of lodepng_error_text to see the meaning of each code.
It is not recommended to use the numerical values to programmatically make
different decisions based on error types as the numbers are not guaranteed to
stay backwards compatible. They are for human consumption only. Programmatically
only 0 or non-0 matter.
8. chunks and PNG editing
-------------------------
@@ -1678,6 +1745,9 @@ try to fix it if the compiler is modern and standards compliant.
This decoder example shows the most basic usage of LodePNG. More complex
examples can be found on the LodePNG website.
NOTE: these examples do not support wide-character filenames, you can use an
external method to handle such files and encode or decode in-memory
10.1. decoder C++ example
-------------------------
@@ -1775,6 +1845,10 @@ symbol.
Not all changes are listed here, the commit history in github lists more:
https://github.com/lvandeve/lodepng
*) 27 jun 2021: added warnings that file reading/writing functions don't support
wide-character filenames (support for this is not planned, opening files is
not the core part of PNG decoding/decoding and is platform dependent).
*) 17 okt 2020: prevent decoding too large text/icc chunks by default.
*) 06 mar 2020: simplified some of the dynamic memory allocations.
*) 12 jan 2020: (!) added 'end' argument to lodepng_chunk_next to allow correct
overflow checks.
@@ -1941,5 +2015,5 @@ Domain: gmail dot com.
Account: lode dot vandevenne.
Copyright (c) 2005-2020 Lode Vandevenne
Copyright (c) 2005-2021 Lode Vandevenne
*/
+208 -73
View File
@@ -1,4 +1,4 @@
/* stb_image - v2.26 - public domain image loader - http://nothings.org/stb
/* stb_image - v2.27 - public domain image loader - http://nothings.org/stb
no warranty implied; use at your own risk
Do this:
@@ -48,6 +48,7 @@ LICENSE
RECENT REVISION HISTORY:
2.27 (2021-07-11) document stbi_info better, 16-bit PNM support, bug fixes
2.26 (2020-07-13) many minor fixes
2.25 (2020-02-02) fix warnings
2.24 (2020-02-02) fix warnings; thread-local failure_reason and flip_vertically
@@ -89,7 +90,7 @@ RECENT REVISION HISTORY:
Jeremy Sawicki (handle all ImageNet JPGs)
Optimizations & bugfixes Mikhail Morozov (1-bit BMP)
Fabian "ryg" Giesen Anael Seghezzi (is-16-bit query)
Arseny Kapoulkine
Arseny Kapoulkine Simon Breuss (16-bit PNM)
John-Mark Allen
Carmelo J Fdez-Aguera
@@ -102,7 +103,7 @@ RECENT REVISION HISTORY:
Thomas Ruf Ronny Chevalier github:rlyeh
Janez Zemva John Bartholomew Michal Cichon github:romigrou
Jonathan Blow Ken Hamada Tero Hanninen github:svdijk
Laurent Gomila Cort Stratton github:snagar
Eugene Golushkov Laurent Gomila Cort Stratton github:snagar
Aruelien Pocheville Sergio Gonzalez Thibault Reuille github:Zelex
Cass Everitt Ryamond Barbiero github:grim210
Paul Du Bois Engin Manap Aldo Culquicondor github:sammyhw
@@ -110,11 +111,13 @@ RECENT REVISION HISTORY:
Josh Tobin Matthew Gregan github:poppolopoppo
Julian Raschke Gregory Mullen Christian Floisand github:darealshinji
Baldur Karlsson Kevin Schmidt JR Smith github:Michaelangel007
Brad Weinberger Matvey Cherevko [reserved]
Brad Weinberger Matvey Cherevko github:mosra
Luca Sas Alexander Veselov Zack Middleton [reserved]
Ryan C. Gordon [reserved] [reserved]
DO NOT ADD YOUR NAME HERE
Jacko Dirks
To add your name to the credits, pick a random blank space in the middle and fill it.
80% of merge conflicts on stb PRs are due to people adding their name at the end
of the credits.
@@ -176,6 +179,32 @@ RECENT REVISION HISTORY:
//
// Paletted PNG, BMP, GIF, and PIC images are automatically depalettized.
//
// To query the width, height and component count of an image without having to
// decode the full file, you can use the stbi_info family of functions:
//
// int x,y,n,ok;
// ok = stbi_info(filename, &x, &y, &n);
// // returns ok=1 and sets x, y, n if image is a supported format,
// // 0 otherwise.
//
// Note that stb_image pervasively uses ints in its public API for sizes,
// including sizes of memory buffers. This is now part of the API and thus
// hard to change without causing breakage. As a result, the various image
// loaders all have certain limits on image size; these differ somewhat
// by format but generally boil down to either just under 2GB or just under
// 1GB. When the decoded image would be larger than this, stb_image decoding
// will fail.
//
// Additionally, stb_image will reject image files that have any of their
// dimensions set to a larger value than the configurable STBI_MAX_DIMENSIONS,
// which defaults to 2**24 = 16777216 pixels. Due to the above memory limit,
// the only way to have an image with such dimensions load correctly
// is for it to have a rather extreme aspect ratio. Either way, the
// assumption here is that such larger images are likely to be malformed
// or malicious. If you do need to load an image with individual dimensions
// larger than that, and it still fits in the overall size limit, you can
// #define STBI_MAX_DIMENSIONS on your own to be something larger.
//
// ===========================================================================
//
// UNICODE:
@@ -281,11 +310,10 @@ RECENT REVISION HISTORY:
//
// iPhone PNG support:
//
// By default we convert iphone-formatted PNGs back to RGB, even though
// they are internally encoded differently. You can disable this conversion
// by calling stbi_convert_iphone_png_to_rgb(0), in which case
// you will always just get the native iphone "format" through (which
// is BGR stored in RGB).
// We optionally support converting iPhone-formatted PNGs (which store
// premultiplied BGRA) back to RGB, even though they're internally encoded
// differently. To enable this conversion, call
// stbi_convert_iphone_png_to_rgb(1).
//
// Call stbi_set_unpremultiply_on_load(1) as well to force a divide per
// pixel to remove any premultiplied alpha *only* if the image file explicitly
@@ -489,6 +517,8 @@ STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip);
// as above, but only applies to images loaded on the thread that calls the function
// this function is only available if your compiler supports thread-local variables;
// calling it will fail to link if your compiler doesn't
STBIDEF void stbi_set_unpremultiply_on_load_thread(int flag_true_if_should_unpremultiply);
STBIDEF void stbi_convert_iphone_png_to_rgb_thread(int flag_true_if_should_convert);
STBIDEF void stbi_set_flip_vertically_on_load_thread(int flag_true_if_should_flip);
// ZLIB client - used by PNG, available for other purposes
@@ -634,7 +664,7 @@ typedef unsigned char validate_uint32[sizeof(stbi__uint32)==4 ? 1 : -1];
#ifdef STBI_HAS_LROTL
#define stbi_lrot(x,y) _lrotl(x,y)
#else
#define stbi_lrot(x,y) (((x) << (y)) | ((x) >> (32 - (y))))
#define stbi_lrot(x,y) (((x) << (y)) | ((x) >> (-(y) & 31)))
#endif
#if defined(STBI_MALLOC) && defined(STBI_FREE) && (defined(STBI_REALLOC) || defined(STBI_REALLOC_SIZED))
@@ -748,9 +778,12 @@ static int stbi__sse2_available(void)
#ifdef STBI_NEON
#include <arm_neon.h>
// assume GCC or Clang on ARM targets
#ifdef _MSC_VER
#define STBI_SIMD_ALIGN(type, name) __declspec(align(16)) type name
#else
#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16)))
#endif
#endif
#ifndef STBI_SIMD_ALIGN
#define STBI_SIMD_ALIGN(type, name) type name
@@ -924,6 +957,7 @@ static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp);
static int stbi__pnm_test(stbi__context *s);
static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri);
static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp);
static int stbi__pnm_is16(stbi__context *s);
#endif
static
@@ -998,7 +1032,7 @@ static int stbi__mad3sizes_valid(int a, int b, int c, int add)
}
// returns 1 if "a*b*c*d + add" has no negative terms/factors and doesn't overflow
#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR)
#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) || !defined(STBI_NO_PNM)
static int stbi__mad4sizes_valid(int a, int b, int c, int d, int add)
{
return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) &&
@@ -1021,7 +1055,7 @@ static void *stbi__malloc_mad3(int a, int b, int c, int add)
return stbi__malloc(a*b*c + add);
}
#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR)
#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) || !defined(STBI_NO_PNM)
static void *stbi__malloc_mad4(int a, int b, int c, int d, int add)
{
if (!stbi__mad4sizes_valid(a, b, c, d, add)) return NULL;
@@ -1087,9 +1121,8 @@ static void *stbi__load_main(stbi__context *s, int *x, int *y, int *comp, int re
ri->channel_order = STBI_ORDER_RGB; // all current input & output are this, but this is here so we can add BGR order
ri->num_channels = 0;
#ifndef STBI_NO_JPEG
if (stbi__jpeg_test(s)) return stbi__jpeg_load(s,x,y,comp,req_comp, ri);
#endif
// test the formats with a very explicit header first (at least a FOURCC
// or distinctive magic number first)
#ifndef STBI_NO_PNG
if (stbi__png_test(s)) return stbi__png_load(s,x,y,comp,req_comp, ri);
#endif
@@ -1107,6 +1140,13 @@ static void *stbi__load_main(stbi__context *s, int *x, int *y, int *comp, int re
#ifndef STBI_NO_PIC
if (stbi__pic_test(s)) return stbi__pic_load(s,x,y,comp,req_comp, ri);
#endif
// then the formats that can end up attempting to load with just 1 or 2
// bytes matching expectations; these are prone to false positives, so
// try them later
#ifndef STBI_NO_JPEG
if (stbi__jpeg_test(s)) return stbi__jpeg_load(s,x,y,comp,req_comp, ri);
#endif
#ifndef STBI_NO_PNM
if (stbi__pnm_test(s)) return stbi__pnm_load(s,x,y,comp,req_comp, ri);
#endif
@@ -1262,12 +1302,12 @@ static void stbi__float_postprocess(float *result, int *x, int *y, int *comp, in
#ifndef STBI_NO_STDIO
#if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8)
#if defined(_WIN32) && defined(STBI_WINDOWS_UTF8)
STBI_EXTERN __declspec(dllimport) int __stdcall MultiByteToWideChar(unsigned int cp, unsigned long flags, const char *str, int cbmb, wchar_t *widestr, int cchwide);
STBI_EXTERN __declspec(dllimport) int __stdcall WideCharToMultiByte(unsigned int cp, unsigned long flags, const wchar_t *widestr, int cchwide, char *str, int cbmb, const char *defchar, int *used_default);
#endif
#if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8)
#if defined(_WIN32) && defined(STBI_WINDOWS_UTF8)
STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input)
{
return WideCharToMultiByte(65001 /* UTF8 */, 0, input, -1, buffer, (int) bufferlen, NULL, NULL);
@@ -1277,16 +1317,16 @@ STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wch
static FILE *stbi__fopen(char const *filename, char const *mode)
{
FILE *f;
#if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8)
#if defined(_WIN32) && defined(STBI_WINDOWS_UTF8)
wchar_t wMode[64];
wchar_t wFilename[1024];
if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, filename, -1, wFilename, sizeof(wFilename)))
if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, filename, -1, wFilename, sizeof(wFilename)/sizeof(*wFilename)))
return 0;
if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, mode, -1, wMode, sizeof(wMode)))
if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, mode, -1, wMode, sizeof(wMode)/sizeof(*wMode)))
return 0;
#if _MSC_VER >= 1400
#if defined(_MSC_VER) && _MSC_VER >= 1400
if (0 != _wfopen_s(&f, wFilename, wMode))
f = 0;
#else
@@ -1662,7 +1702,8 @@ static int stbi__get16le(stbi__context *s)
static stbi__uint32 stbi__get32le(stbi__context *s)
{
stbi__uint32 z = stbi__get16le(s);
return z + (stbi__get16le(s) << 16);
z += (stbi__uint32)stbi__get16le(s) << 16;
return z;
}
#endif
@@ -2090,13 +2131,12 @@ stbi_inline static int stbi__extend_receive(stbi__jpeg *j, int n)
int sgn;
if (j->code_bits < n) stbi__grow_buffer_unsafe(j);
sgn = (stbi__int32)j->code_buffer >> 31; // sign bit is always in MSB
sgn = j->code_buffer >> 31; // sign bit always in MSB; 0 if MSB clear (positive), 1 if MSB set (negative)
k = stbi_lrot(j->code_buffer, n);
if (n < 0 || n >= (int) (sizeof(stbi__bmask)/sizeof(*stbi__bmask))) return 0;
j->code_buffer = k & ~stbi__bmask[n];
k &= stbi__bmask[n];
j->code_bits -= n;
return k + (stbi__jbias[n] & ~sgn);
return k + (stbi__jbias[n] & (sgn - 1));
}
// get some unsigned bits
@@ -2146,7 +2186,7 @@ static int stbi__jpeg_decode_block(stbi__jpeg *j, short data[64], stbi__huffman
if (j->code_bits < 16) stbi__grow_buffer_unsafe(j);
t = stbi__jpeg_huff_decode(j, hdc);
if (t < 0) return stbi__err("bad huffman code","Corrupt JPEG");
if (t < 0 || t > 15) return stbi__err("bad huffman code","Corrupt JPEG");
// 0 all the ac values now so we can do it 32-bits at a time
memset(data,0,64*sizeof(data[0]));
@@ -2203,12 +2243,12 @@ static int stbi__jpeg_decode_block_prog_dc(stbi__jpeg *j, short data[64], stbi__
// first scan for DC coefficient, must be first
memset(data,0,64*sizeof(data[0])); // 0 all the ac values now
t = stbi__jpeg_huff_decode(j, hdc);
if (t == -1) return stbi__err("can't merge dc and ac", "Corrupt JPEG");
if (t < 0 || t > 15) return stbi__err("can't merge dc and ac", "Corrupt JPEG");
diff = t ? stbi__extend_receive(j, t) : 0;
dc = j->img_comp[b].dc_pred + diff;
j->img_comp[b].dc_pred = dc;
data[0] = (short) (dc << j->succ_low);
data[0] = (short) (dc * (1 << j->succ_low));
} else {
// refinement scan for DC coefficient
if (stbi__jpeg_get_bit(j))
@@ -2245,7 +2285,7 @@ static int stbi__jpeg_decode_block_prog_ac(stbi__jpeg *j, short data[64], stbi__
j->code_buffer <<= s;
j->code_bits -= s;
zig = stbi__jpeg_dezigzag[k++];
data[zig] = (short) ((r >> 8) << shift);
data[zig] = (short) ((r >> 8) * (1 << shift));
} else {
int rs = stbi__jpeg_huff_decode(j, hac);
if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG");
@@ -2263,7 +2303,7 @@ static int stbi__jpeg_decode_block_prog_ac(stbi__jpeg *j, short data[64], stbi__
} else {
k += r;
zig = stbi__jpeg_dezigzag[k++];
data[zig] = (short) (stbi__extend_receive(j,s) << shift);
data[zig] = (short) (stbi__extend_receive(j,s) * (1 << shift));
}
}
} while (k <= j->spec_end);
@@ -3227,6 +3267,13 @@ static int stbi__process_frame_header(stbi__jpeg *z, int scan)
if (z->img_comp[i].v > v_max) v_max = z->img_comp[i].v;
}
// check that plane subsampling factors are integer ratios; our resamplers can't deal with fractional ratios
// and I've never seen a non-corrupted JPEG file actually use them
for (i=0; i < s->img_n; ++i) {
if (h_max % z->img_comp[i].h != 0) return stbi__err("bad H","Corrupt JPEG");
if (v_max % z->img_comp[i].v != 0) return stbi__err("bad V","Corrupt JPEG");
}
// compute interleaved mcu info
z->img_h_max = h_max;
z->img_v_max = v_max;
@@ -3782,6 +3829,10 @@ static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp
else
decode_n = z->s->img_n;
// nothing to do if no components requested; check this now to avoid
// accessing uninitialized coutput[0] later
if (decode_n <= 0) { stbi__cleanup_jpeg(z); return NULL; }
// resample and color-convert
{
int k;
@@ -3924,6 +3975,7 @@ static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int re
{
unsigned char* result;
stbi__jpeg* j = (stbi__jpeg*) stbi__malloc(sizeof(stbi__jpeg));
if (!j) return stbi__errpuc("outofmem", "Out of memory");
STBI_NOTUSED(ri);
j->s = s;
stbi__setup_jpeg(j);
@@ -3936,6 +3988,7 @@ static int stbi__jpeg_test(stbi__context *s)
{
int r;
stbi__jpeg* j = (stbi__jpeg*)stbi__malloc(sizeof(stbi__jpeg));
if (!j) return stbi__err("outofmem", "Out of memory");
j->s = s;
stbi__setup_jpeg(j);
r = stbi__decode_jpeg_header(j, STBI__SCAN_type);
@@ -3960,6 +4013,7 @@ static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp)
{
int result;
stbi__jpeg* j = (stbi__jpeg*) (stbi__malloc(sizeof(stbi__jpeg)));
if (!j) return stbi__err("outofmem", "Out of memory");
j->s = s;
result = stbi__jpeg_info_raw(j, x, y, comp);
STBI_FREE(j);
@@ -3979,6 +4033,7 @@ static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp)
// fast-way is faster to check than jpeg huffman, but slow way is slower
#define STBI__ZFAST_BITS 9 // accelerate all cases in default tables
#define STBI__ZFAST_MASK ((1 << STBI__ZFAST_BITS) - 1)
#define STBI__ZNSYMS 288 // number of symbols in literal/length alphabet
// zlib-style huffman encoding
// (jpegs packs from left, zlib from right, so can't share code)
@@ -3988,8 +4043,8 @@ typedef struct
stbi__uint16 firstcode[16];
int maxcode[17];
stbi__uint16 firstsymbol[16];
stbi_uc size[288];
stbi__uint16 value[288];
stbi_uc size[STBI__ZNSYMS];
stbi__uint16 value[STBI__ZNSYMS];
} stbi__zhuffman;
stbi_inline static int stbi__bitreverse16(int n)
@@ -4120,7 +4175,7 @@ static int stbi__zhuffman_decode_slowpath(stbi__zbuf *a, stbi__zhuffman *z)
if (s >= 16) return -1; // invalid code!
// code size is s, so:
b = (k >> (16-s)) - z->firstcode[s] + z->firstsymbol[s];
if (b >= sizeof (z->size)) return -1; // some data was corrupt somewhere!
if (b >= STBI__ZNSYMS) return -1; // some data was corrupt somewhere!
if (z->size[b] != s) return -1; // was originally an assert, but report failure instead.
a->code_buffer >>= s;
a->num_bits -= s;
@@ -4317,7 +4372,7 @@ static int stbi__parse_zlib_header(stbi__zbuf *a)
return 1;
}
static const stbi_uc stbi__zdefault_length[288] =
static const stbi_uc stbi__zdefault_length[STBI__ZNSYMS] =
{
8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
@@ -4363,7 +4418,7 @@ static int stbi__parse_zlib(stbi__zbuf *a, int parse_header)
} else {
if (type == 1) {
// use fixed code lengths
if (!stbi__zbuild_huffman(&a->z_length , stbi__zdefault_length , 288)) return 0;
if (!stbi__zbuild_huffman(&a->z_length , stbi__zdefault_length , STBI__ZNSYMS)) return 0;
if (!stbi__zbuild_huffman(&a->z_distance, stbi__zdefault_distance, 32)) return 0;
} else {
if (!stbi__compute_huffman_codes(a)) return 0;
@@ -4759,6 +4814,7 @@ static int stbi__create_png_image(stbi__png *a, stbi_uc *image_data, stbi__uint3
// de-interlacing
final = (stbi_uc *) stbi__malloc_mad3(a->s->img_x, a->s->img_y, out_bytes, 0);
if (!final) return stbi__err("outofmem", "Out of memory");
for (p=0; p < 7; ++p) {
int xorig[] = { 0,4,0,2,0,1,0 };
int yorig[] = { 0,0,4,0,2,0,1 };
@@ -4879,19 +4935,46 @@ static int stbi__expand_png_palette(stbi__png *a, stbi_uc *palette, int len, int
return 1;
}
static int stbi__unpremultiply_on_load = 0;
static int stbi__de_iphone_flag = 0;
static int stbi__unpremultiply_on_load_global = 0;
static int stbi__de_iphone_flag_global = 0;
STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply)
{
stbi__unpremultiply_on_load = flag_true_if_should_unpremultiply;
stbi__unpremultiply_on_load_global = flag_true_if_should_unpremultiply;
}
STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert)
{
stbi__de_iphone_flag = flag_true_if_should_convert;
stbi__de_iphone_flag_global = flag_true_if_should_convert;
}
#ifndef STBI_THREAD_LOCAL
#define stbi__unpremultiply_on_load stbi__unpremultiply_on_load_global
#define stbi__de_iphone_flag stbi__de_iphone_flag_global
#else
static STBI_THREAD_LOCAL int stbi__unpremultiply_on_load_local, stbi__unpremultiply_on_load_set;
static STBI_THREAD_LOCAL int stbi__de_iphone_flag_local, stbi__de_iphone_flag_set;
STBIDEF void stbi__unpremultiply_on_load_thread(int flag_true_if_should_unpremultiply)
{
stbi__unpremultiply_on_load_local = flag_true_if_should_unpremultiply;
stbi__unpremultiply_on_load_set = 1;
}
STBIDEF void stbi_convert_iphone_png_to_rgb_thread(int flag_true_if_should_convert)
{
stbi__de_iphone_flag_local = flag_true_if_should_convert;
stbi__de_iphone_flag_set = 1;
}
#define stbi__unpremultiply_on_load (stbi__unpremultiply_on_load_set \
? stbi__unpremultiply_on_load_local \
: stbi__unpremultiply_on_load_global)
#define stbi__de_iphone_flag (stbi__de_iphone_flag_set \
? stbi__de_iphone_flag_local \
: stbi__de_iphone_flag_global)
#endif // STBI_THREAD_LOCAL
static void stbi__de_iphone(stbi__png *z)
{
stbi__context *s = z->s;
@@ -5272,6 +5355,32 @@ typedef struct
int extra_read;
} stbi__bmp_data;
static int stbi__bmp_set_mask_defaults(stbi__bmp_data *info, int compress)
{
// BI_BITFIELDS specifies masks explicitly, don't override
if (compress == 3)
return 1;
if (compress == 0) {
if (info->bpp == 16) {
info->mr = 31u << 10;
info->mg = 31u << 5;
info->mb = 31u << 0;
} else if (info->bpp == 32) {
info->mr = 0xffu << 16;
info->mg = 0xffu << 8;
info->mb = 0xffu << 0;
info->ma = 0xffu << 24;
info->all_a = 0; // if all_a is 0 at end, then we loaded alpha channel but it was all 0
} else {
// otherwise, use defaults, which is all-0
info->mr = info->mg = info->mb = info->ma = 0;
}
return 1;
}
return 0; // error
}
static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info)
{
int hsz;
@@ -5299,6 +5408,8 @@ static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info)
if (hsz != 12) {
int compress = stbi__get32le(s);
if (compress == 1 || compress == 2) return stbi__errpuc("BMP RLE", "BMP type not supported: RLE");
if (compress >= 4) return stbi__errpuc("BMP JPEG/PNG", "BMP type not supported: unsupported compression"); // this includes PNG/JPEG modes
if (compress == 3 && info->bpp != 16 && info->bpp != 32) return stbi__errpuc("bad BMP", "bad BMP"); // bitfields requires 16 or 32 bits/pixel
stbi__get32le(s); // discard sizeof
stbi__get32le(s); // discard hres
stbi__get32le(s); // discard vres
@@ -5313,17 +5424,7 @@ static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info)
}
if (info->bpp == 16 || info->bpp == 32) {
if (compress == 0) {
if (info->bpp == 32) {
info->mr = 0xffu << 16;
info->mg = 0xffu << 8;
info->mb = 0xffu << 0;
info->ma = 0xffu << 24;
info->all_a = 0; // if all_a is 0 at end, then we loaded alpha channel but it was all 0
} else {
info->mr = 31u << 10;
info->mg = 31u << 5;
info->mb = 31u << 0;
}
stbi__bmp_set_mask_defaults(info, compress);
} else if (compress == 3) {
info->mr = stbi__get32le(s);
info->mg = stbi__get32le(s);
@@ -5338,6 +5439,7 @@ static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info)
return stbi__errpuc("bad BMP", "bad BMP");
}
} else {
// V4/V5 header
int i;
if (hsz != 108 && hsz != 124)
return stbi__errpuc("bad BMP", "bad BMP");
@@ -5345,6 +5447,8 @@ static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info)
info->mg = stbi__get32le(s);
info->mb = stbi__get32le(s);
info->ma = stbi__get32le(s);
if (compress != 3) // override mr/mg/mb unless in BI_BITFIELDS mode, as per docs
stbi__bmp_set_mask_defaults(info, compress);
stbi__get32le(s); // discard color space
for (i=0; i < 12; ++i)
stbi__get32le(s); // discard color space parameters
@@ -5394,8 +5498,7 @@ static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req
psize = (info.offset - info.extra_read - info.hsz) >> 2;
}
if (psize == 0) {
STBI_ASSERT(info.offset == s->callback_already_read + (int) (s->img_buffer - s->img_buffer_original));
if (info.offset != s->callback_already_read + (s->img_buffer - s->buffer_start)) {
if (info.offset != s->callback_already_read + (s->img_buffer - s->img_buffer_original)) {
return stbi__errpuc("bad offset", "Corrupt BMP");
}
}
@@ -6342,6 +6445,7 @@ static void *stbi__pic_load(stbi__context *s,int *px,int *py,int *comp,int req_c
// intermediate buffer is RGBA
result = (stbi_uc *) stbi__malloc_mad3(x, y, 4, 0);
if (!result) return stbi__errpuc("outofmem", "Out of memory");
memset(result, 0xff, x*y*4);
if (!stbi__pic_load_core(s,x,y,comp, result)) {
@@ -6457,6 +6561,7 @@ static int stbi__gif_header(stbi__context *s, stbi__gif *g, int *comp, int is_in
static int stbi__gif_info_raw(stbi__context *s, int *x, int *y, int *comp)
{
stbi__gif* g = (stbi__gif*) stbi__malloc(sizeof(stbi__gif));
if (!g) return stbi__err("outofmem", "Out of memory");
if (!stbi__gif_header(s, g, comp, 1)) {
STBI_FREE(g);
stbi__rewind( s );
@@ -6766,6 +6871,17 @@ static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, i
}
}
static void *stbi__load_gif_main_outofmem(stbi__gif *g, stbi_uc *out, int **delays)
{
STBI_FREE(g->out);
STBI_FREE(g->history);
STBI_FREE(g->background);
if (out) STBI_FREE(out);
if (delays && *delays) STBI_FREE(*delays);
return stbi__errpuc("outofmem", "Out of memory");
}
static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp)
{
if (stbi__gif_test(s)) {
@@ -6777,6 +6893,10 @@ static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y,
int stride;
int out_size = 0;
int delays_size = 0;
STBI_NOTUSED(out_size);
STBI_NOTUSED(delays_size);
memset(&g, 0, sizeof(g));
if (delays) {
*delays = 0;
@@ -6794,26 +6914,29 @@ static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y,
if (out) {
void *tmp = (stbi_uc*) STBI_REALLOC_SIZED( out, out_size, layers * stride );
if (NULL == tmp) {
STBI_FREE(g.out);
STBI_FREE(g.history);
STBI_FREE(g.background);
return stbi__errpuc("outofmem", "Out of memory");
}
if (!tmp)
return stbi__load_gif_main_outofmem(&g, out, delays);
else {
out = (stbi_uc*) tmp;
out_size = layers * stride;
}
if (delays) {
*delays = (int*) STBI_REALLOC_SIZED( *delays, delays_size, sizeof(int) * layers );
int *new_delays = (int*) STBI_REALLOC_SIZED( *delays, delays_size, sizeof(int) * layers );
if (!new_delays)
return stbi__load_gif_main_outofmem(&g, out, delays);
*delays = new_delays;
delays_size = layers * sizeof(int);
}
} else {
out = (stbi_uc*)stbi__malloc( layers * stride );
if (!out)
return stbi__load_gif_main_outofmem(&g, out, delays);
out_size = layers * stride;
if (delays) {
*delays = (int*) stbi__malloc( layers * sizeof(int) );
if (!*delays)
return stbi__load_gif_main_outofmem(&g, out, delays);
delays_size = layers * sizeof(int);
}
}
@@ -7138,9 +7261,10 @@ static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp)
info.all_a = 255;
p = stbi__bmp_parse_header(s, &info);
stbi__rewind( s );
if (p == NULL)
if (p == NULL) {
stbi__rewind( s );
return 0;
}
if (x) *x = s->img_x;
if (y) *y = s->img_y;
if (comp) {
@@ -7206,8 +7330,8 @@ static int stbi__psd_is16(stbi__context *s)
stbi__rewind( s );
return 0;
}
(void) stbi__get32be(s);
(void) stbi__get32be(s);
STBI_NOTUSED(stbi__get32be(s));
STBI_NOTUSED(stbi__get32be(s));
depth = stbi__get16be(s);
if (depth != 16) {
stbi__rewind( s );
@@ -7286,7 +7410,6 @@ static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp)
// Known limitations:
// Does not support comments in the header section
// Does not support ASCII image data (formats P2 and P3)
// Does not support 16-bit-per-channel
#ifndef STBI_NO_PNM
@@ -7307,7 +7430,8 @@ static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req
stbi_uc *out;
STBI_NOTUSED(ri);
if (!stbi__pnm_info(s, (int *)&s->img_x, (int *)&s->img_y, (int *)&s->img_n))
ri->bits_per_channel = stbi__pnm_info(s, (int *)&s->img_x, (int *)&s->img_y, (int *)&s->img_n);
if (ri->bits_per_channel == 0)
return 0;
if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)");
@@ -7317,12 +7441,12 @@ static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req
*y = s->img_y;
if (comp) *comp = s->img_n;
if (!stbi__mad3sizes_valid(s->img_n, s->img_x, s->img_y, 0))
if (!stbi__mad4sizes_valid(s->img_n, s->img_x, s->img_y, ri->bits_per_channel / 8, 0))
return stbi__errpuc("too large", "PNM too large");
out = (stbi_uc *) stbi__malloc_mad3(s->img_n, s->img_x, s->img_y, 0);
out = (stbi_uc *) stbi__malloc_mad4(s->img_n, s->img_x, s->img_y, ri->bits_per_channel / 8, 0);
if (!out) return stbi__errpuc("outofmem", "Out of memory");
stbi__getn(s, out, s->img_n * s->img_x * s->img_y);
stbi__getn(s, out, s->img_n * s->img_x * s->img_y * (ri->bits_per_channel / 8));
if (req_comp && req_comp != s->img_n) {
out = stbi__convert_format(out, s->img_n, req_comp, s->img_x, s->img_y);
@@ -7398,11 +7522,19 @@ static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp)
stbi__pnm_skip_whitespace(s, &c);
maxv = stbi__pnm_getinteger(s, &c); // read max value
if (maxv > 255)
return stbi__err("max value > 255", "PPM image not 8-bit");
if (maxv > 65535)
return stbi__err("max value > 65535", "PPM image supports only 8-bit and 16-bit images");
else if (maxv > 255)
return 16;
else
return 1;
return 8;
}
static int stbi__pnm_is16(stbi__context *s)
{
if (stbi__pnm_info(s, NULL, NULL, NULL) == 16)
return 1;
return 0;
}
#endif
@@ -7458,6 +7590,9 @@ static int stbi__is_16_main(stbi__context *s)
if (stbi__psd_is16(s)) return 1;
#endif
#ifndef STBI_NO_PNM
if (stbi__pnm_is16(s)) return 1;
#endif
return 0;
}
+1 -1
View File
@@ -338,7 +338,7 @@ float Font::getKerning(uint32 leftglyph, uint32 rightglyph)
if (it != kerning.end())
return it->second;
float k = rasterizers[0]->getKerning(leftglyph, rightglyph) / dpiScale + 0.5f;
float k = floorf(rasterizers[0]->getKerning(leftglyph, rightglyph) / dpiScale + 0.5f);
for (const auto &r : rasterizers)
{
+3 -2
View File
@@ -1480,7 +1480,7 @@ void Graphics::rectangle(DrawMode mode, float x, float y, float w, float h)
void Graphics::rectangle(DrawMode mode, float x, float y, float w, float h, float rx, float ry, int points)
{
if (rx == 0 || ry == 0)
if (rx <= 0 || ry <= 0)
{
rectangle(mode, x, y, w, h);
return;
@@ -1539,7 +1539,8 @@ void Graphics::rectangle(DrawMode mode, float x, float y, float w, float h, floa
void Graphics::rectangle(DrawMode mode, float x, float y, float w, float h, float rx, float ry)
{
rectangle(mode, x, y, w, h, rx, ry, calculateEllipsePoints(rx, ry));
int points = calculateEllipsePoints(std::min(rx, std::abs(w/2)), std::min(ry, std::abs(h/2)));
rectangle(mode, x, y, w, h, rx, ry, points);
}
void Graphics::circle(DrawMode mode, float x, float y, float radius, int points)
+186
View File
@@ -0,0 +1,186 @@
R"luastring"--(
-- DO NOT REMOVE THE ABOVE LINE. It is used to load this file as a C++ string.
-- There is a matching delimiter at the bottom of the file.
--[[
Copyright (c) 2006-2021 LOVE Development Team
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
--]]
local love = require("love")
-- Used for setup:
love.path = {}
love.arg = {}
-- Replace any \ with /.
function love.path.normalslashes(p)
return p:gsub("\\", "/")
end
-- Makes sure there is a slash at the end
-- of a path.
function love.path.endslash(p)
if p:sub(-1) ~= "/" then
return p .. "/"
else
return p
end
end
-- Checks whether a path is absolute or not.
function love.path.abs(p)
local tmp = love.path.normalslashes(p)
-- Path is absolute if it starts with a "/".
if tmp:find("/") == 1 then
return true
end
-- Path is absolute if it starts with a
-- letter followed by a colon.
if tmp:find("%a:") == 1 then
return true
end
-- Relative.
return false
end
-- Converts any path into a full path.
function love.path.getFull(p)
if love.path.abs(p) then
return love.path.normalslashes(p)
end
local cwd = love.filesystem.getWorkingDirectory()
cwd = love.path.normalslashes(cwd)
cwd = love.path.endslash(cwd)
-- Construct a full path.
local full = cwd .. love.path.normalslashes(p)
-- Remove trailing /., if applicable
return full:match("(.-)/%.$") or full
end
-- Returns the leaf of a full path.
function love.path.leaf(p)
p = love.path.normalslashes(p)
local a = 1
local last = p
while a do
a = p:find("/", a+1)
if a then
last = p:sub(a+1)
end
end
return last
end
-- Finds the key in the table with the lowest integral index. The lowest
-- will typically the executable, for instance "lua5.1.exe".
function love.arg.getLow(a)
local m = math.huge
for k,v in pairs(a) do
if k < m then
m = k
end
end
return a[m], m
end
love.arg.options = {
console = { a = 0 },
fused = {a = 0 },
game = { a = 1 }
}
love.arg.optionIndices = {}
function love.arg.parseOption(m, i)
m.set = true
if m.a > 0 then
m.arg = {}
for j=i,i+m.a-1 do
love.arg.optionIndices[j] = true
table.insert(m.arg, arg[j])
end
end
return m.a
end
function love.arg.parseOptions(arg)
local game
local argc = #arg
local i = 1
while i <= argc do
-- Look for options.
local m = arg[i]:match("^%-%-(.*)")
if m and m ~= "" and love.arg.options[m] and not love.arg.options[m].set then
love.arg.optionIndices[i] = true
i = i + love.arg.parseOption(love.arg.options[m], i+1)
elseif m == "" then -- handle '--' as an option
love.arg.optionIndices[i] = true
if not game then -- handle '--' followed by game name
game = i + 1
end
break
elseif not game then
game = i
end
i = i + 1
end
if not love.arg.options.game.set then
love.arg.parseOption(love.arg.options.game, game or 0)
end
end
-- Returns the arguments that are passed to your game via love.load()
-- arguments that were parsed as options are skipped.
function love.arg.parseGameArguments(a)
local out = {}
local _, lowindex = love.arg.getLow(a)
local o = lowindex
for i=lowindex, #a do
if not love.arg.optionIndices[i] then
out[o] = a[i]
o = o + 1
end
end
return out
end
-- DO NOT REMOVE THE NEXT LINE. It is used to load this file as a C++ string.
--)luastring"--"
+388
View File
@@ -0,0 +1,388 @@
R"luastring"--(
-- DO NOT REMOVE THE ABOVE LINE. It is used to load this file as a C++ string.
-- There is a matching delimiter at the bottom of the file.
--[[
Copyright (c) 2006-2021 LOVE Development Team
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
--]]
-- Make sure love exists.
local love = require("love")
-- Essential code boot/init.
require("love.arg")
require("love.callbacks")
local function uridecode(s)
return s:gsub("%%%x%x", function(str)
return string.char(tonumber(str:sub(2), 16))
end)
end
local no_game_code = false
local invalid_game_path = nil
-- This can't be overridden.
function love.boot()
-- This is absolutely needed.
require("love.filesystem")
love.rawGameArguments = arg
local arg0 = love.arg.getLow(love.rawGameArguments)
love.filesystem.init(arg0)
local exepath = love.filesystem.getExecutablePath()
if #exepath == 0 then
-- This shouldn't happen, but just in case we'll fall back to arg0.
exepath = arg0
end
no_game_code = false
invalid_game_path = nil
-- Is this one of those fancy "fused" games?
local can_has_game = pcall(love.filesystem.setSource, exepath)
-- It's a fused game, don't parse --game argument
if can_has_game then
love.arg.options.game.set = true
end
-- Parse options now that we know which options we're looking for.
love.arg.parseOptions(love.rawGameArguments)
-- parseGameArguments can only be called after parseOptions.
love.parsedGameArguments = love.arg.parseGameArguments(love.rawGameArguments)
local o = love.arg.options
local is_fused_game = can_has_game or love.arg.options.fused.set
love.filesystem.setFused(is_fused_game)
love.setDeprecationOutput(not love.filesystem.isFused())
local identity = ""
if not can_has_game and o.game.set and o.game.arg[1] then
local nouri = o.game.arg[1]
if nouri:sub(1, 7) == "file://" then
nouri = uridecode(nouri:sub(8))
end
local full_source = love.path.getFull(nouri)
can_has_game = pcall(love.filesystem.setSource, full_source)
if not can_has_game then
invalid_game_path = full_source
end
-- Use the name of the source .love as the identity for now.
identity = love.path.leaf(full_source)
else
-- Use the name of the exe as the identity for now.
identity = love.path.leaf(exepath)
end
-- Try to use the archive containing main.lua as the identity name. It
-- might not be available, in which case the fallbacks above are used.
local realdir = love.filesystem.getRealDirectory("main.lua")
if realdir then
identity = love.path.leaf(realdir)
end
identity = identity:gsub("^([%.]+)", "") -- strip leading "."'s
identity = identity:gsub("%.([^%.]+)$", "") -- strip extension
identity = identity:gsub("%.", "_") -- replace remaining "."'s with "_"
identity = #identity > 0 and identity or "lovegame"
-- When conf.lua is initially loaded, the main source should be checked
-- before the save directory (the identity should be appended.)
pcall(love.filesystem.setIdentity, identity, true)
if can_has_game and not (love.filesystem.getInfo("main.lua") or love.filesystem.getInfo("conf.lua")) then
no_game_code = true
end
if not can_has_game then
local nogame = require("love.nogame")
nogame()
end
end
function love.init()
-- Create default configuration settings.
-- NOTE: Adding a new module to the modules list
-- will NOT make it load, see below.
local c = {
title = "Untitled",
version = love._version,
window = {
width = 800,
height = 600,
x = nil,
y = nil,
minwidth = 1,
minheight = 1,
fullscreen = false,
fullscreentype = "desktop",
display = 1,
vsync = 1,
msaa = 0,
borderless = false,
resizable = false,
centered = true,
usedpiscale = true,
},
modules = {
data = true,
event = true,
keyboard = true,
mouse = true,
timer = true,
joystick = true,
touch = true,
image = true,
graphics = true,
audio = true,
math = true,
physics = true,
sound = true,
system = true,
font = true,
thread = true,
window = true,
video = true,
},
audio = {
mixwithsystem = true, -- Only relevant for Android / iOS.
mic = false, -- Only relevant for Android.
},
console = false, -- Only relevant for windows.
identity = false,
appendidentity = false,
externalstorage = false, -- Only relevant for Android.
accelerometerjoystick = true, -- Only relevant for Android / iOS.
gammacorrect = false,
highdpi = false,
}
-- Console hack, part 1.
local openedconsole = false
if love.arg.options.console.set and love._openConsole then
love._openConsole()
openedconsole = true
end
-- If config file exists, load it and allow it to update config table.
local confok, conferr
if (not love.conf) and love.filesystem and love.filesystem.getInfo("conf.lua") then
confok, conferr = pcall(require, "conf")
end
-- Yes, conf.lua might not exist, but there are other ways of making
-- love.conf appear, so we should check for it anyway.
if love.conf then
confok, conferr = pcall(love.conf, c)
-- If love.conf errors, we'll trigger the error after loading modules so
-- the error message can be displayed in the window.
end
-- Console hack, part 2.
if c.console and love._openConsole and not openedconsole then
love._openConsole()
end
-- Hack for disabling accelerometer-as-joystick on Android / iOS.
if love._setAccelerometerAsJoystick then
love._setAccelerometerAsJoystick(c.accelerometerjoystick)
end
if love._setGammaCorrect then
love._setGammaCorrect(c.gammacorrect)
end
if love._setHighDPIAllowed then
love._setHighDPIAllowed(c.highdpi)
end
if love._setAudioMixWithSystem then
if c.audio and c.audio.mixwithsystem ~= nil then
love._setAudioMixWithSystem(c.audio.mixwithsystem)
end
end
if love._requestRecordingPermission then
love._requestRecordingPermission(c.audio and c.audio.mic)
end
-- Gets desired modules.
for k,v in ipairs{
"data",
"thread",
"timer",
"event",
"keyboard",
"joystick",
"mouse",
"touch",
"sound",
"system",
"audio",
"image",
"video",
"font",
"window",
"graphics",
"math",
"physics",
} do
if c.modules[v] then
require("love." .. v)
end
end
if love.event then
love.createhandlers()
end
-- Check the version
c.version = tostring(c.version)
if not love.isVersionCompatible(c.version) then
local major, minor, revision = c.version:match("^(%d+)%.(%d+)%.(%d+)$")
if (not major or not minor or not revision) or (major ~= love._version_major and minor ~= love._version_minor) then
local msg = ("This game indicates it was made for version '%s' of LOVE.\n"..
"It may not be compatible with the running version (%s)."):format(c.version, love._version)
print(msg)
if love.window then
love.window.showMessageBox("Compatibility Warning", msg, "warning")
end
end
end
if not confok and conferr then
error(conferr)
end
-- Setup window here.
if c.window and c.modules.window then
love.window.setTitle(c.window.title or c.title)
assert(love.window.setMode(c.window.width, c.window.height,
{
fullscreen = c.window.fullscreen,
fullscreentype = c.window.fullscreentype,
vsync = c.window.vsync,
msaa = c.window.msaa,
stencil = c.window.stencil,
depth = c.window.depth,
resizable = c.window.resizable,
minwidth = c.window.minwidth,
minheight = c.window.minheight,
borderless = c.window.borderless,
centered = c.window.centered,
display = c.window.display,
highdpi = c.window.highdpi, -- deprecated
usedpiscale = c.window.usedpiscale,
x = c.window.x,
y = c.window.y,
}), "Could not set window mode")
if c.window.icon then
assert(love.image, "If an icon is set in love.conf, love.image must be loaded!")
love.window.setIcon(love.image.newImageData(c.window.icon))
end
end
-- Our first timestep, because window creation can take some time
if love.timer then
love.timer.step()
end
if love.filesystem then
love.filesystem._setAndroidSaveExternal(c.externalstorage)
love.filesystem.setIdentity(c.identity or love.filesystem.getIdentity(), c.appendidentity)
if love.filesystem.getInfo("main.lua") then
require("main")
end
end
if no_game_code then
error("No code to run\nYour game might be packaged incorrectly.\nMake sure main.lua is at the top level of the zip.")
elseif invalid_game_path then
error("Cannot load game at path '" .. invalid_game_path .. "'.\nMake sure a folder exists at the specified path.")
end
end
local print, debug, tostring = print, debug, tostring
local function error_printer(msg, layer)
print((debug.traceback("Error: " .. tostring(msg), 1+(layer or 1)):gsub("\n[^\n]+$", "")))
end
-----------------------------------------------------------
-- The root of all calls.
-----------------------------------------------------------
return function()
local func
local inerror = false
local function deferErrhand(...)
local errhand = love.errorhandler or love.errhand
local handler = (not inerror and errhand) or error_printer
inerror = true
func = handler(...)
end
local function earlyinit()
-- If love.boot fails, return 1 and finish immediately
local result = xpcall(love.boot, error_printer)
if not result then return 1 end
-- If love.init or love.run fails, don't return a value,
-- as we want the error handler to take over
result = xpcall(love.init, deferErrhand)
if not result then return end
-- NOTE: We can't assign to func directly, as we'd
-- overwrite the result of deferErrhand with nil on error
local main
result, main = xpcall(love.run, deferErrhand)
if result then
func = main
end
end
func = earlyinit
while func do
local _, retval, restartvalue = xpcall(func, deferErrhand)
if retval then return retval, restartvalue end
coroutine.yield()
end
return 1
end
-- DO NOT REMOVE THE NEXT LINE. It is used to load this file as a C++ string.
--)luastring"--"
+313
View File
@@ -0,0 +1,313 @@
R"luastring"--(
-- DO NOT REMOVE THE ABOVE LINE. It is used to load this file as a C++ string.
-- There is a matching delimiter at the bottom of the file.
--[[
Copyright (c) 2006-2021 LOVE Development Team
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
--]]
local love = require("love")
function love.createhandlers()
-- Standard callback handlers.
love.handlers = setmetatable({
keypressed = function (b,s,r)
if love.keypressed then return love.keypressed(b,s,r) end
end,
keyreleased = function (b,s)
if love.keyreleased then return love.keyreleased(b,s) end
end,
textinput = function (t)
if love.textinput then return love.textinput(t) end
end,
textedited = function (t,s,l)
if love.textedited then return love.textedited(t,s,l) end
end,
mousemoved = function (x,y,dx,dy,t)
if love.mousemoved then return love.mousemoved(x,y,dx,dy,t) end
end,
mousepressed = function (x,y,b,t,c)
if love.mousepressed then return love.mousepressed(x,y,b,t,c) end
end,
mousereleased = function (x,y,b,t,c)
if love.mousereleased then return love.mousereleased(x,y,b,t,c) end
end,
wheelmoved = function (x,y)
if love.wheelmoved then return love.wheelmoved(x,y) end
end,
touchpressed = function (id,x,y,dx,dy,p)
if love.touchpressed then return love.touchpressed(id,x,y,dx,dy,p) end
end,
touchreleased = function (id,x,y,dx,dy,p)
if love.touchreleased then return love.touchreleased(id,x,y,dx,dy,p) end
end,
touchmoved = function (id,x,y,dx,dy,p)
if love.touchmoved then return love.touchmoved(id,x,y,dx,dy,p) end
end,
joystickpressed = function (j,b)
if love.joystickpressed then return love.joystickpressed(j,b) end
end,
joystickreleased = function (j,b)
if love.joystickreleased then return love.joystickreleased(j,b) end
end,
joystickaxis = function (j,a,v)
if love.joystickaxis then return love.joystickaxis(j,a,v) end
end,
joystickhat = function (j,h,v)
if love.joystickhat then return love.joystickhat(j,h,v) end
end,
gamepadpressed = function (j,b)
if love.gamepadpressed then return love.gamepadpressed(j,b) end
end,
gamepadreleased = function (j,b)
if love.gamepadreleased then return love.gamepadreleased(j,b) end
end,
gamepadaxis = function (j,a,v)
if love.gamepadaxis then return love.gamepadaxis(j,a,v) end
end,
joystickadded = function (j)
if love.joystickadded then return love.joystickadded(j) end
end,
joystickremoved = function (j)
if love.joystickremoved then return love.joystickremoved(j) end
end,
focus = function (f)
if love.focus then return love.focus(f) end
end,
mousefocus = function (f)
if love.mousefocus then return love.mousefocus(f) end
end,
visible = function (v)
if love.visible then return love.visible(v) end
end,
quit = function ()
return
end,
threaderror = function (t, err)
if love.threaderror then return love.threaderror(t, err) end
end,
resize = function (w, h)
if love.resize then return love.resize(w, h) end
end,
filedropped = function (f)
if love.filedropped then return love.filedropped(f) end
end,
directorydropped = function (dir)
if love.directorydropped then return love.directorydropped(dir) end
end,
lowmemory = function ()
if love.lowmemory then love.lowmemory() end
collectgarbage()
collectgarbage()
end,
displayrotated = function (display, orient)
if love.displayrotated then return love.displayrotated(display, orient) end
end,
}, {
__index = function(self, name)
error("Unknown event: " .. name)
end,
})
end
-----------------------------------------------------------
-- Default callbacks.
-----------------------------------------------------------
function love.run()
if love.load then love.load(love.parsedGameArguments, love.rawGameArguments) end
-- We don't want the first frame's dt to include time taken by love.load.
if love.timer then love.timer.step() end
-- Main loop time.
return function()
-- Process events.
if love.event then
love.event.pump()
for name, a,b,c,d,e,f in love.event.poll() do
if name == "quit" then
if not love.quit or not love.quit() then
return a or 0, b
end
end
love.handlers[name](a,b,c,d,e,f)
end
end
-- Update dt, as we'll be passing it to update
local dt = love.timer and love.timer.step() or 0
-- Call update and draw
if love.update then love.update(dt) end -- will pass 0 if love.timer is disabled
if love.graphics and love.graphics.isActive() then
love.graphics.origin()
love.graphics.clear(love.graphics.getBackgroundColor())
if love.draw then love.draw() end
love.graphics.present()
end
if love.timer then love.timer.sleep(0.001) end
end
end
local debug, print, tostring, error = debug, print, tostring, error
function love.threaderror(t, err)
error("Thread error ("..tostring(t)..")\n\n"..err, 0)
end
local utf8 = require("utf8")
local function error_printer(msg, layer)
print((debug.traceback("Error: " .. tostring(msg), 1+(layer or 1)):gsub("\n[^\n]+$", "")))
end
function love.errhand(msg)
msg = tostring(msg)
error_printer(msg, 2)
if not love.window or not love.graphics or not love.event then
return
end
if not love.graphics.isCreated() or not love.window.isOpen() then
local success, status = pcall(love.window.setMode, 800, 600)
if not success or not status then
return
end
end
-- Reset state.
if love.mouse then
love.mouse.setVisible(true)
love.mouse.setGrabbed(false)
love.mouse.setRelativeMode(false)
if love.mouse.isCursorSupported() then
love.mouse.setCursor()
end
end
if love.joystick then
-- Stop all joystick vibrations.
for i,v in ipairs(love.joystick.getJoysticks()) do
v:setVibration()
end
end
if love.audio then love.audio.stop() end
love.graphics.reset()
local font = love.graphics.setNewFont(14)
love.graphics.setColor(1, 1, 1)
local trace = debug.traceback()
love.graphics.origin()
local sanitizedmsg = {}
for char in msg:gmatch(utf8.charpattern) do
table.insert(sanitizedmsg, char)
end
sanitizedmsg = table.concat(sanitizedmsg)
local err = {}
table.insert(err, "Error\n")
table.insert(err, sanitizedmsg)
if #sanitizedmsg ~= #msg then
table.insert(err, "Invalid UTF-8 string in error message.")
end
table.insert(err, "\n")
for l in trace:gmatch("(.-)\n") do
if not l:match("boot.lua") then
l = l:gsub("stack traceback:", "Traceback\n")
table.insert(err, l)
end
end
local p = table.concat(err, "\n")
p = p:gsub("\t", "")
p = p:gsub("%[string \"(.-)\"%]", "%1")
local function draw()
local pos = 70
love.graphics.clear(89/255, 157/255, 220/255)
love.graphics.printf(p, pos, pos, love.graphics.getWidth() - pos)
love.graphics.present()
end
local fullErrorText = p
local function copyToClipboard()
if not love.system then return end
love.system.setClipboardText(fullErrorText)
p = p .. "\nCopied to clipboard!"
draw()
end
if love.system then
p = p .. "\n\nPress Ctrl+C or tap to copy this error"
end
return function()
love.event.pump()
for e, a, b, c in love.event.poll() do
if e == "quit" then
return 1
elseif e == "keypressed" and a == "escape" then
return 1
elseif e == "keypressed" and a == "c" and love.keyboard.isDown("lctrl", "rctrl") then
copyToClipboard()
elseif e == "touchpressed" then
local name = love.window.getTitle()
if #name == 0 or name == "Untitled" then name = "Game" end
local buttons = {"OK", "Cancel"}
if love.system then
buttons[3] = "Copy to clipboard"
end
local pressed = love.window.showMessageBox("Quit "..name.."?", "", buttons)
if pressed == 1 then
return 1
elseif pressed == 3 then
copyToClipboard()
end
end
end
draw()
if love.timer then
love.timer.sleep(0.1)
end
end
end
-- DO NOT REMOVE THE NEXT LINE. It is used to load this file as a C++ string.
--)luastring"--"
+67 -4
View File
@@ -23,6 +23,7 @@
#include "common/version.h"
#include "common/deprecation.h"
#include "common/runtime.h"
#include "modules/window/Window.h"
#include "love.h"
@@ -32,6 +33,12 @@
#ifdef LOVE_WINDOWS
#include <windows.h>
#if defined(_MSC_VER) && (_MSC_VER < 1900)
// VS 2013 and earlier doesn't have snprintf
#define snprintf sprintf_s
#endif // defined(_MSC_VER) && (_MSC_VER < 1900)
#endif // LOVE_WINDOWS
#ifdef LOVE_ANDROID
@@ -79,9 +86,21 @@ extern "C"
# include "audio/Audio.h"
#endif
// Scripts
// Scripts.
#include "scripts/nogame.lua.h"
#include "scripts/boot.lua.h"
// Put the Lua code directly into a raw string literal.
static const char arg_lua[] =
#include "arg.lua"
;
static const char callbacks_lua[] =
#include "callbacks.lua"
;
static const char boot_lua[] =
#include "boot.lua"
;
// All modules define a c-accessible luaopen
// so let's make use of those, instead
@@ -146,6 +165,8 @@ extern "C"
extern int luaopen_love_window(lua_State*);
#endif
extern int luaopen_love_nogame(lua_State*);
extern int luaopen_love_arg(lua_State*);
extern int luaopen_love_callbacks(lua_State*);
extern int luaopen_love_boot(lua_State*);
}
@@ -208,6 +229,8 @@ static const luaL_Reg modules[] = {
{ "love.window", luaopen_love_window },
#endif
{ "love.nogame", luaopen_love_nogame },
{ "love.arg", luaopen_love_arg },
{ "love.callbacks", luaopen_love_callbacks },
{ "love.boot", luaopen_love_boot },
{ 0, 0 }
};
@@ -545,6 +568,33 @@ int luaopen_love(lua_State *L)
love::luax_preload(L, luaopen_luautf8, "utf8");
#endif
#ifdef LOVE_ENABLE_WINDOW
// In some environments, LuaJIT is limited to 2GB and LuaJIT sometimes panic when it
// reaches OOM and closes the whole program, leaving the user confused about what's
// going on.
// We can't recover the state at this point, but it's better to inform user that
// something very bad happening instead of silently exiting.
// Note that this is not foolproof. In some cases, the whole process crashes by
// uncaught exception that LuaJIT throws or simply exit as if calling
// love.event.quit("not enough memory")
lua_atpanic(L, [](lua_State *L)
{
using namespace love;
using namespace love::window;
char message[128];
Window* windowModule = Module::getInstance<Window>(Module::M_WINDOW);
snprintf(message, sizeof(message), "PANIC: unprotected error in call to Lua API (%s)", lua_tostring(L, -1));
if (windowModule)
windowModule->showMessageBox("Lua Fatal Error", message, Window::MESSAGEBOX_ERROR, windowModule->isOpen());
fprintf(stderr, "%s\n", message);
return 0;
});
#endif
return 1;
}
@@ -649,13 +699,26 @@ int luaopen_love_nogame(lua_State *L)
return 1;
}
int luaopen_love_boot(lua_State *L)
int luaopen_love_arg(lua_State *L)
{
if (luaL_loadbuffer(L, (const char *)love::boot_lua, sizeof(love::boot_lua), "boot.lua") == 0)
if (luaL_loadbuffer(L, arg_lua, sizeof(arg_lua), "arg.lua") == 0)
lua_call(L, 0, 1);
return 1;
}
int luaopen_love_callbacks(lua_State *L)
{
if (luaL_loadbuffer(L, callbacks_lua, sizeof(callbacks_lua), "callbacks.lua") == 0)
lua_call(L, 0, 1);
return 1;
}
int luaopen_love_boot(lua_State *L)
{
if (luaL_loadbuffer(L, boot_lua, sizeof(boot_lua), "boot.lua") == 0)
lua_call(L, 0, 1);
return 1;
}
-814
View File
@@ -1,814 +0,0 @@
--[[
Copyright (c) 2006-2021 LOVE Development Team
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
--]]
-- Make sure love exists.
local love = require("love")
-- Used for setup:
love.path = {}
love.arg = {}
-- Replace any \ with /.
function love.path.normalslashes(p)
return p:gsub("\\", "/")
end
-- Makes sure there is a slash at the end
-- of a path.
function love.path.endslash(p)
if p:sub(-1) ~= "/" then
return p .. "/"
else
return p
end
end
-- Checks whether a path is absolute or not.
function love.path.abs(p)
local tmp = love.path.normalslashes(p)
-- Path is absolute if it starts with a "/".
if tmp:find("/") == 1 then
return true
end
-- Path is absolute if it starts with a
-- letter followed by a colon.
if tmp:find("%a:") == 1 then
return true
end
-- Relative.
return false
end
-- Converts any path into a full path.
function love.path.getFull(p)
if love.path.abs(p) then
return love.path.normalslashes(p)
end
local cwd = love.filesystem.getWorkingDirectory()
cwd = love.path.normalslashes(cwd)
cwd = love.path.endslash(cwd)
-- Construct a full path.
local full = cwd .. love.path.normalslashes(p)
-- Remove trailing /., if applicable
return full:match("(.-)/%.$") or full
end
-- Returns the leaf of a full path.
function love.path.leaf(p)
p = love.path.normalslashes(p)
local a = 1
local last = p
while a do
a = p:find("/", a+1)
if a then
last = p:sub(a+1)
end
end
return last
end
-- Finds the key in the table with the lowest integral index. The lowest
-- will typically the executable, for instance "lua5.1.exe".
function love.arg.getLow(a)
local m = math.huge
for k,v in pairs(a) do
if k < m then
m = k
end
end
return a[m], m
end
love.arg.options = {
console = { a = 0 },
fused = {a = 0 },
game = { a = 1 }
}
love.arg.optionIndices = {}
function love.arg.parseOption(m, i)
m.set = true
if m.a > 0 then
m.arg = {}
for j=i,i+m.a-1 do
love.arg.optionIndices[j] = true
table.insert(m.arg, arg[j])
end
end
return m.a
end
function love.arg.parseOptions(arg)
local game
local argc = #arg
local i = 1
while i <= argc do
-- Look for options.
local m = arg[i]:match("^%-%-(.*)")
if m and m ~= "" and love.arg.options[m] and not love.arg.options[m].set then
love.arg.optionIndices[i] = true
i = i + love.arg.parseOption(love.arg.options[m], i+1)
elseif m == "" then -- handle '--' as an option
love.arg.optionIndices[i] = true
if not game then -- handle '--' followed by game name
game = i + 1
end
break
elseif not game then
game = i
end
i = i + 1
end
if not love.arg.options.game.set then
love.arg.parseOption(love.arg.options.game, game or 0)
end
end
-- Returns the arguments that are passed to your game via love.load()
-- arguments that were parsed as options are skipped.
function love.arg.parseGameArguments(a)
local out = {}
local _, lowindex = love.arg.getLow(a)
local o = lowindex
for i=lowindex, #a do
if not love.arg.optionIndices[i] then
out[o] = a[i]
o = o + 1
end
end
return out
end
function love.createhandlers()
-- Standard callback handlers.
love.handlers = setmetatable({
keypressed = function (b,s,r)
if love.keypressed then return love.keypressed(b,s,r) end
end,
keyreleased = function (b,s)
if love.keyreleased then return love.keyreleased(b,s) end
end,
textinput = function (t)
if love.textinput then return love.textinput(t) end
end,
textedited = function (t,s,l)
if love.textedited then return love.textedited(t,s,l) end
end,
mousemoved = function (x,y,dx,dy,t)
if love.mousemoved then return love.mousemoved(x,y,dx,dy,t) end
end,
mousepressed = function (x,y,b,t,c)
if love.mousepressed then return love.mousepressed(x,y,b,t,c) end
end,
mousereleased = function (x,y,b,t,c)
if love.mousereleased then return love.mousereleased(x,y,b,t,c) end
end,
wheelmoved = function (x,y)
if love.wheelmoved then return love.wheelmoved(x,y) end
end,
touchpressed = function (id,x,y,dx,dy,p)
if love.touchpressed then return love.touchpressed(id,x,y,dx,dy,p) end
end,
touchreleased = function (id,x,y,dx,dy,p)
if love.touchreleased then return love.touchreleased(id,x,y,dx,dy,p) end
end,
touchmoved = function (id,x,y,dx,dy,p)
if love.touchmoved then return love.touchmoved(id,x,y,dx,dy,p) end
end,
joystickpressed = function (j,b)
if love.joystickpressed then return love.joystickpressed(j,b) end
end,
joystickreleased = function (j,b)
if love.joystickreleased then return love.joystickreleased(j,b) end
end,
joystickaxis = function (j,a,v)
if love.joystickaxis then return love.joystickaxis(j,a,v) end
end,
joystickhat = function (j,h,v)
if love.joystickhat then return love.joystickhat(j,h,v) end
end,
gamepadpressed = function (j,b)
if love.gamepadpressed then return love.gamepadpressed(j,b) end
end,
gamepadreleased = function (j,b)
if love.gamepadreleased then return love.gamepadreleased(j,b) end
end,
gamepadaxis = function (j,a,v)
if love.gamepadaxis then return love.gamepadaxis(j,a,v) end
end,
joystickadded = function (j)
if love.joystickadded then return love.joystickadded(j) end
end,
joystickremoved = function (j)
if love.joystickremoved then return love.joystickremoved(j) end
end,
focus = function (f)
if love.focus then return love.focus(f) end
end,
mousefocus = function (f)
if love.mousefocus then return love.mousefocus(f) end
end,
visible = function (v)
if love.visible then return love.visible(v) end
end,
quit = function ()
return
end,
threaderror = function (t, err)
if love.threaderror then return love.threaderror(t, err) end
end,
resize = function (w, h)
if love.resize then return love.resize(w, h) end
end,
filedropped = function (f)
if love.filedropped then return love.filedropped(f) end
end,
directorydropped = function (dir)
if love.directorydropped then return love.directorydropped(dir) end
end,
lowmemory = function ()
if love.lowmemory then love.lowmemory() end
collectgarbage()
collectgarbage()
end,
displayrotated = function (display, orient)
if love.displayrotated then return love.displayrotated(display, orient) end
end,
}, {
__index = function(self, name)
error("Unknown event: " .. name)
end,
})
end
local function uridecode(s)
return s:gsub("%%%x%x", function(str)
return string.char(tonumber(str:sub(2), 16))
end)
end
local no_game_code = false
local invalid_game_path = nil
-- This can't be overridden.
function love.boot()
-- This is absolutely needed.
require("love.filesystem")
love.rawGameArguments = arg
local arg0 = love.arg.getLow(love.rawGameArguments)
love.filesystem.init(arg0)
local exepath = love.filesystem.getExecutablePath()
if #exepath == 0 then
-- This shouldn't happen, but just in case we'll fall back to arg0.
exepath = arg0
end
no_game_code = false
invalid_game_path = nil
-- Is this one of those fancy "fused" games?
local can_has_game = pcall(love.filesystem.setSource, exepath)
-- It's a fused game, don't parse --game argument
if can_has_game then
love.arg.options.game.set = true
end
-- Parse options now that we know which options we're looking for.
love.arg.parseOptions(love.rawGameArguments)
-- parseGameArguments can only be called after parseOptions.
love.parsedGameArguments = love.arg.parseGameArguments(love.rawGameArguments)
local o = love.arg.options
local is_fused_game = can_has_game or love.arg.options.fused.set
love.filesystem.setFused(is_fused_game)
love.setDeprecationOutput(not love.filesystem.isFused())
local identity = ""
if not can_has_game and o.game.set and o.game.arg[1] then
local nouri = o.game.arg[1]
if nouri:sub(1, 7) == "file://" then
nouri = uridecode(nouri:sub(8))
end
local full_source = love.path.getFull(nouri)
can_has_game = pcall(love.filesystem.setSource, full_source)
if not can_has_game then
invalid_game_path = full_source
end
-- Use the name of the source .love as the identity for now.
identity = love.path.leaf(full_source)
else
-- Use the name of the exe as the identity for now.
identity = love.path.leaf(exepath)
end
-- Try to use the archive containing main.lua as the identity name. It
-- might not be available, in which case the fallbacks above are used.
local realdir = love.filesystem.getRealDirectory("main.lua")
if realdir then
identity = love.path.leaf(realdir)
end
identity = identity:gsub("^([%.]+)", "") -- strip leading "."'s
identity = identity:gsub("%.([^%.]+)$", "") -- strip extension
identity = identity:gsub("%.", "_") -- replace remaining "."'s with "_"
identity = #identity > 0 and identity or "lovegame"
-- When conf.lua is initially loaded, the main source should be checked
-- before the save directory (the identity should be appended.)
pcall(love.filesystem.setIdentity, identity, true)
if can_has_game and not (love.filesystem.getInfo("main.lua") or love.filesystem.getInfo("conf.lua")) then
no_game_code = true
end
if not can_has_game then
local nogame = require("love.nogame")
nogame()
end
end
function love.init()
-- Create default configuration settings.
-- NOTE: Adding a new module to the modules list
-- will NOT make it load, see below.
local c = {
title = "Untitled",
version = love._version,
window = {
width = 800,
height = 600,
x = nil,
y = nil,
minwidth = 1,
minheight = 1,
fullscreen = false,
fullscreentype = "desktop",
display = 1,
vsync = 1,
msaa = 0,
borderless = false,
resizable = false,
centered = true,
usedpiscale = true,
},
modules = {
data = true,
event = true,
keyboard = true,
mouse = true,
timer = true,
joystick = true,
touch = true,
image = true,
graphics = true,
audio = true,
math = true,
physics = true,
sound = true,
system = true,
font = true,
thread = true,
window = true,
video = true,
},
audio = {
mixwithsystem = true, -- Only relevant for Android / iOS.
mic = false, -- Only relevant for Android.
},
console = false, -- Only relevant for windows.
identity = false,
appendidentity = false,
externalstorage = false, -- Only relevant for Android.
accelerometerjoystick = true, -- Only relevant for Android / iOS.
gammacorrect = false,
highdpi = false,
}
-- Console hack, part 1.
local openedconsole = false
if love.arg.options.console.set and love._openConsole then
love._openConsole()
openedconsole = true
end
-- If config file exists, load it and allow it to update config table.
local confok, conferr
if (not love.conf) and love.filesystem and love.filesystem.getInfo("conf.lua") then
confok, conferr = pcall(require, "conf")
end
-- Yes, conf.lua might not exist, but there are other ways of making
-- love.conf appear, so we should check for it anyway.
if love.conf then
confok, conferr = pcall(love.conf, c)
-- If love.conf errors, we'll trigger the error after loading modules so
-- the error message can be displayed in the window.
end
-- Console hack, part 2.
if c.console and love._openConsole and not openedconsole then
love._openConsole()
end
-- Hack for disabling accelerometer-as-joystick on Android / iOS.
if love._setAccelerometerAsJoystick then
love._setAccelerometerAsJoystick(c.accelerometerjoystick)
end
if love._setGammaCorrect then
love._setGammaCorrect(c.gammacorrect)
end
if love._setHighDPIAllowed then
love._setHighDPIAllowed(c.highdpi)
end
if love._setAudioMixWithSystem then
if c.audio and c.audio.mixwithsystem ~= nil then
love._setAudioMixWithSystem(c.audio.mixwithsystem)
end
end
if love._requestRecordingPermission then
love._requestRecordingPermission(c.audio and c.audio.mic)
end
-- Gets desired modules.
for k,v in ipairs{
"data",
"thread",
"timer",
"event",
"keyboard",
"joystick",
"mouse",
"touch",
"sound",
"system",
"audio",
"image",
"video",
"font",
"window",
"graphics",
"math",
"physics",
} do
if c.modules[v] then
require("love." .. v)
end
end
if love.event then
love.createhandlers()
end
-- Check the version
c.version = tostring(c.version)
if not love.isVersionCompatible(c.version) then
local major, minor, revision = c.version:match("^(%d+)%.(%d+)%.(%d+)$")
if (not major or not minor or not revision) or (major ~= love._version_major and minor ~= love._version_minor) then
local msg = ("This game indicates it was made for version '%s' of LOVE.\n"..
"It may not be compatible with the running version (%s)."):format(c.version, love._version)
print(msg)
if love.window then
love.window.showMessageBox("Compatibility Warning", msg, "warning")
end
end
end
if not confok and conferr then
error(conferr)
end
-- Setup window here.
if c.window and c.modules.window then
love.window.setTitle(c.window.title or c.title)
assert(love.window.setMode(c.window.width, c.window.height,
{
fullscreen = c.window.fullscreen,
fullscreentype = c.window.fullscreentype,
vsync = c.window.vsync,
msaa = c.window.msaa,
stencil = c.window.stencil,
depth = c.window.depth,
resizable = c.window.resizable,
minwidth = c.window.minwidth,
minheight = c.window.minheight,
borderless = c.window.borderless,
centered = c.window.centered,
display = c.window.display,
highdpi = c.window.highdpi, -- deprecated
usedpiscale = c.window.usedpiscale,
x = c.window.x,
y = c.window.y,
}), "Could not set window mode")
if c.window.icon then
assert(love.image, "If an icon is set in love.conf, love.image must be loaded!")
love.window.setIcon(love.image.newImageData(c.window.icon))
end
end
-- Our first timestep, because window creation can take some time
if love.timer then
love.timer.step()
end
if love.filesystem then
love.filesystem._setAndroidSaveExternal(c.externalstorage)
love.filesystem.setIdentity(c.identity or love.filesystem.getIdentity(), c.appendidentity)
if love.filesystem.getInfo("main.lua") then
require("main")
end
end
if no_game_code then
error("No code to run\nYour game might be packaged incorrectly.\nMake sure main.lua is at the top level of the zip.")
elseif invalid_game_path then
error("Cannot load game at path '" .. invalid_game_path .. "'.\nMake sure a folder exists at the specified path.")
end
end
-----------------------------------------------------------
-- Default callbacks.
-----------------------------------------------------------
function love.run()
if love.load then love.load(love.parsedGameArguments, love.rawGameArguments) end
-- We don't want the first frame's dt to include time taken by love.load.
if love.timer then love.timer.step() end
-- Main loop time.
return function()
-- Process events.
if love.event then
love.event.pump()
for name, a,b,c,d,e,f in love.event.poll() do
if name == "quit" then
if not love.quit or not love.quit() then
return a or 0, b
end
end
love.handlers[name](a,b,c,d,e,f)
end
end
-- Update dt, as we'll be passing it to update
local dt = love.timer and love.timer.step() or 0
-- Call update and draw
if love.update then love.update(dt) end -- will pass 0 if love.timer is disabled
if love.graphics and love.graphics.isActive() then
love.graphics.origin()
love.graphics.clear(love.graphics.getBackgroundColor())
if love.draw then love.draw() end
love.graphics.present()
end
if love.timer then love.timer.sleep(0.001) end
end
end
local debug, print, error = debug, print, error
function love.threaderror(t, err)
error("Thread error ("..tostring(t)..")\n\n"..err, 0)
end
local utf8 = require("utf8")
local function error_printer(msg, layer)
print((debug.traceback("Error: " .. tostring(msg), 1+(layer or 1)):gsub("\n[^\n]+$", "")))
end
function love.errhand(msg)
msg = tostring(msg)
error_printer(msg, 2)
if not love.window or not love.graphics or not love.event then
return
end
if not love.graphics.isCreated() or not love.window.isOpen() then
local success, status = pcall(love.window.setMode, 800, 600)
if not success or not status then
return
end
end
-- Reset state.
if love.mouse then
love.mouse.setVisible(true)
love.mouse.setGrabbed(false)
love.mouse.setRelativeMode(false)
if love.mouse.isCursorSupported() then
love.mouse.setCursor()
end
end
if love.joystick then
-- Stop all joystick vibrations.
for i,v in ipairs(love.joystick.getJoysticks()) do
v:setVibration()
end
end
if love.audio then love.audio.stop() end
love.graphics.reset()
local font = love.graphics.setNewFont(14)
love.graphics.setColor(1, 1, 1)
local trace = debug.traceback()
love.graphics.origin()
local sanitizedmsg = {}
for char in msg:gmatch(utf8.charpattern) do
table.insert(sanitizedmsg, char)
end
sanitizedmsg = table.concat(sanitizedmsg)
local err = {}
table.insert(err, "Error\n")
table.insert(err, sanitizedmsg)
if #sanitizedmsg ~= #msg then
table.insert(err, "Invalid UTF-8 string in error message.")
end
table.insert(err, "\n")
for l in trace:gmatch("(.-)\n") do
if not l:match("boot.lua") then
l = l:gsub("stack traceback:", "Traceback\n")
table.insert(err, l)
end
end
local p = table.concat(err, "\n")
p = p:gsub("\t", "")
p = p:gsub("%[string \"(.-)\"%]", "%1")
local function draw()
local pos = 70
love.graphics.clear(89/255, 157/255, 220/255)
love.graphics.printf(p, pos, pos, love.graphics.getWidth() - pos)
love.graphics.present()
end
local fullErrorText = p
local function copyToClipboard()
if not love.system then return end
love.system.setClipboardText(fullErrorText)
p = p .. "\nCopied to clipboard!"
draw()
end
if love.system then
p = p .. "\n\nPress Ctrl+C or tap to copy this error"
end
return function()
love.event.pump()
for e, a, b, c in love.event.poll() do
if e == "quit" then
return 1
elseif e == "keypressed" and a == "escape" then
return 1
elseif e == "keypressed" and a == "c" and love.keyboard.isDown("lctrl", "rctrl") then
copyToClipboard()
elseif e == "touchpressed" then
local name = love.window.getTitle()
if #name == 0 or name == "Untitled" then name = "Game" end
local buttons = {"OK", "Cancel"}
if love.system then
buttons[3] = "Copy to clipboard"
end
local pressed = love.window.showMessageBox("Quit "..name.."?", "", buttons)
if pressed == 1 then
return 1
elseif pressed == 3 then
copyToClipboard()
end
end
end
draw()
if love.timer then
love.timer.sleep(0.1)
end
end
end
-----------------------------------------------------------
-- The root of all calls.
-----------------------------------------------------------
return function()
local func
local inerror = false
local function deferErrhand(...)
local errhand = love.errorhandler or love.errhand
local handler = (not inerror and errhand) or error_printer
inerror = true
func = handler(...)
end
local function earlyinit()
-- If love.boot fails, return 1 and finish immediately
local result = xpcall(love.boot, error_printer)
if not result then return 1 end
-- If love.init or love.run fails, don't return a value,
-- as we want the error handler to take over
result = xpcall(love.init, deferErrhand)
if not result then return end
-- NOTE: We can't assign to func directly, as we'd
-- overwrite the result of deferErrhand with nil on error
local main
result, main = xpcall(love.run, deferErrhand)
if result then
func = main
end
end
func = earlyinit
while func do
local _, retval, restartvalue = xpcall(func, deferErrhand)
if retval then return retval, restartvalue end
coroutine.yield()
end
return 1
end
File diff suppressed because it is too large Load Diff