mirror of
https://github.com/love2d/love.git
synced 2026-08-16 00:02:12 +02:00
Merge branch '12.0-development' into metal
This commit is contained in:
+154
-55
@@ -333,24 +333,59 @@ void showRecordingPermissionMissingDialog()
|
||||
env->DeleteLocalRef(activity);
|
||||
}
|
||||
|
||||
/* A container for AssetManager Java object */
|
||||
class AssetManagerObject
|
||||
{
|
||||
public:
|
||||
AssetManagerObject()
|
||||
{
|
||||
JNIEnv *env = (JNIEnv *) SDL_AndroidGetJNIEnv();
|
||||
jobject am = getLocalAssetManager(env);
|
||||
|
||||
assetManager = env->NewGlobalRef(am);
|
||||
env->DeleteLocalRef(am);
|
||||
}
|
||||
|
||||
~AssetManagerObject()
|
||||
{
|
||||
JNIEnv *env = (JNIEnv *) SDL_AndroidGetJNIEnv();
|
||||
env->DeleteGlobalRef(assetManager);
|
||||
}
|
||||
|
||||
static jobject getLocalAssetManager(JNIEnv *env) {
|
||||
jobject self = (jobject) SDL_AndroidGetActivity();
|
||||
jclass activity = env->GetObjectClass(self);
|
||||
jmethodID method = env->GetMethodID(activity, "getAssets", "()Landroid/content/res/AssetManager;");
|
||||
jobject am = env->CallObjectMethod(self, method);
|
||||
|
||||
env->DeleteLocalRef(self);
|
||||
env->DeleteLocalRef(activity);
|
||||
return am;
|
||||
}
|
||||
|
||||
explicit operator jobject()
|
||||
{
|
||||
return assetManager;
|
||||
};
|
||||
private:
|
||||
jobject assetManager;
|
||||
};
|
||||
|
||||
/*
|
||||
* Helper functions to aid new fusing method
|
||||
*/
|
||||
|
||||
// This returns *global* reference, no need to free it.
|
||||
static jobject getJavaAssetManager()
|
||||
{
|
||||
static AssetManagerObject assetManager;
|
||||
return (jobject) assetManager;
|
||||
}
|
||||
|
||||
static AAssetManager *getAssetManager()
|
||||
{
|
||||
JNIEnv *env = (JNIEnv*) SDL_AndroidGetJNIEnv();
|
||||
jobject self = (jobject) SDL_AndroidGetActivity();
|
||||
jclass activity = env->GetObjectClass(self);
|
||||
jmethodID method = env->GetMethodID(activity, "getAssetManager", "()Landroid/content/res/AssetManager;");
|
||||
|
||||
jobject assetManager = env->CallObjectMethod(self, method);
|
||||
AAssetManager *assetManagerObject = AAssetManager_fromJava(env, assetManager);
|
||||
|
||||
env->DeleteLocalRef(assetManager);
|
||||
env->DeleteLocalRef(activity);
|
||||
env->DeleteLocalRef(self);
|
||||
|
||||
return assetManagerObject;
|
||||
return AAssetManager_fromJava(env, (jobject) getJavaAssetManager());
|
||||
}
|
||||
|
||||
namespace aasset
|
||||
@@ -368,35 +403,6 @@ struct AssetInfo
|
||||
|
||||
static std::unordered_map<std::string, PHYSFS_FileType> fileTree;
|
||||
|
||||
// Workaround AAsset can't detect whetever something is a directory or doesn't exist
|
||||
static void buildFileLookup(
|
||||
AAssetManager *assetManager,
|
||||
std::unordered_map<std::string, PHYSFS_FileType> &out,
|
||||
const std::string &path = ""
|
||||
)
|
||||
{
|
||||
if (!path.empty())
|
||||
{
|
||||
AAsset *test = AAssetManager_open(assetManager, path.c_str(), AASSET_MODE_STREAMING);
|
||||
if (test)
|
||||
{
|
||||
AAsset_close(test);
|
||||
out[path] = PHYSFS_FILETYPE_REGULAR;
|
||||
return;
|
||||
}
|
||||
|
||||
out[path] = PHYSFS_FILETYPE_DIRECTORY;
|
||||
}
|
||||
|
||||
AAssetDir *dir = AAssetManager_openDir(assetManager, path.c_str());
|
||||
const char *file;
|
||||
|
||||
while ((file = AAssetDir_getNextFileName(dir)) != nullptr)
|
||||
buildFileLookup(assetManager, out, file);
|
||||
|
||||
AAssetDir_close(dir);
|
||||
}
|
||||
|
||||
PHYSFS_sint64 read(PHYSFS_Io *io, void *buf, PHYSFS_uint64 len)
|
||||
{
|
||||
AAsset *asset = ((AssetInfo *) io->opaque)->asset;
|
||||
@@ -505,7 +511,31 @@ void *openArchive(PHYSFS_Io *io, const char *name, int forWrite, int *claimed)
|
||||
AAssetManager *assetManager = getAssetManager();
|
||||
|
||||
if (io::fileTree.empty())
|
||||
io::buildFileLookup(assetManager, io::fileTree);
|
||||
{
|
||||
// AAssetDir_getNextFileName intentionally excludes directories, so
|
||||
// we have to use JNI that calls AssetManager.list() recursively.
|
||||
JNIEnv *env = (JNIEnv *) SDL_AndroidGetJNIEnv();
|
||||
jobject activity = (jobject) SDL_AndroidGetActivity();
|
||||
jclass clazz = env->GetObjectClass(activity);
|
||||
|
||||
jmethodID method = env->GetMethodID(clazz, "buildFileTree", "()[Ljava/lang/String;");
|
||||
jobjectArray list = (jobjectArray) env->CallObjectMethod(activity, method);
|
||||
|
||||
for (jsize i = 0; i < env->GetArrayLength(list); i++)
|
||||
{
|
||||
jstring jstr = (jstring) env->GetObjectArrayElement(list, i);
|
||||
const char *str = env->GetStringUTFChars(jstr, nullptr);
|
||||
|
||||
io::fileTree[str + 1] = str[0] == 'd' ? PHYSFS_FILETYPE_DIRECTORY : PHYSFS_FILETYPE_REGULAR;
|
||||
|
||||
env->ReleaseStringUTFChars(jstr, str);
|
||||
env->DeleteLocalRef(jstr);
|
||||
}
|
||||
|
||||
env->DeleteLocalRef(list);
|
||||
env->DeleteLocalRef(clazz);
|
||||
env->DeleteLocalRef(activity);
|
||||
}
|
||||
|
||||
return assetManager;
|
||||
}
|
||||
@@ -518,33 +548,60 @@ PHYSFS_EnumerateCallbackResult enumerate(
|
||||
void *callbackdata
|
||||
)
|
||||
{
|
||||
using FileTreeIterator = std::unordered_map<std::string, PHYSFS_FileType>::iterator;
|
||||
LOVE_UNUSED(opaque);
|
||||
|
||||
const char *path = dirname;
|
||||
if (path == nullptr || (path[0] == '/' && path[1] == 0))
|
||||
path = "";
|
||||
|
||||
AAssetManager *assetManager = (AAssetManager *) opaque;
|
||||
AAssetDir *dir = AAssetManager_openDir(assetManager, path);
|
||||
|
||||
if (dir == nullptr)
|
||||
if (path[0] != 0)
|
||||
{
|
||||
PHYSFS_setErrorCode(PHYSFS_ERR_NOT_FOUND);
|
||||
return PHYSFS_ENUM_ERROR;
|
||||
FileTreeIterator result = io::fileTree.find(path);
|
||||
|
||||
if (result == io::fileTree.end() || result->second != PHYSFS_FILETYPE_DIRECTORY)
|
||||
{
|
||||
PHYSFS_setErrorCode(PHYSFS_ERR_NOT_FOUND);
|
||||
return PHYSFS_ENUM_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
JNIEnv *env = (JNIEnv *) SDL_AndroidGetJNIEnv();
|
||||
jobject assetManager = getJavaAssetManager();
|
||||
jclass clazz = env->GetObjectClass(assetManager);
|
||||
jmethodID method = env->GetMethodID(clazz, "list", "(Ljava/lang/String;)[Ljava/lang/String;");
|
||||
|
||||
jstring jstringDir = env->NewStringUTF(path);
|
||||
jobjectArray dir = (jobjectArray) env->CallObjectMethod(assetManager, method, jstringDir);
|
||||
|
||||
PHYSFS_EnumerateCallbackResult ret = PHYSFS_ENUM_OK;
|
||||
|
||||
while (ret == PHYSFS_ENUM_OK)
|
||||
if (env->ExceptionCheck())
|
||||
{
|
||||
const char *name = AAssetDir_getNextFileName(dir);
|
||||
// IOException occured
|
||||
ret = PHYSFS_ENUM_ERROR;
|
||||
env->ExceptionClear();
|
||||
}
|
||||
else
|
||||
{
|
||||
jsize i = 0;
|
||||
jsize len = env->GetArrayLength(dir);
|
||||
|
||||
// No more files?
|
||||
if (name == nullptr)
|
||||
break;
|
||||
while (ret == PHYSFS_ENUM_OK && i < len) {
|
||||
jstring jstr = (jstring) env->GetObjectArrayElement(dir, i++);
|
||||
const char *name = env->GetStringUTFChars(jstr, nullptr);
|
||||
|
||||
ret = cb(callbackdata, origdir, name);
|
||||
ret = cb(callbackdata, origdir, name);
|
||||
|
||||
env->ReleaseStringUTFChars(jstr, name);
|
||||
env->DeleteLocalRef(jstr);
|
||||
}
|
||||
|
||||
env->DeleteLocalRef(dir);
|
||||
}
|
||||
|
||||
AAssetDir_close(dir);
|
||||
env->DeleteLocalRef(jstringDir);
|
||||
env->DeleteLocalRef(clazz);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -724,6 +781,48 @@ bool checkFusedGame(void **physfsIO_Out)
|
||||
return false;
|
||||
}
|
||||
|
||||
const char *getCRequirePath()
|
||||
{
|
||||
static bool initialized = false;
|
||||
static const char *path = nullptr;
|
||||
|
||||
if (!initialized)
|
||||
{
|
||||
JNIEnv *env = (JNIEnv*) SDL_AndroidGetJNIEnv();
|
||||
jobject activity = (jobject) SDL_AndroidGetActivity();
|
||||
|
||||
jclass clazz(env->GetObjectClass(activity));
|
||||
jmethodID method_id = env->GetMethodID(clazz, "getCRequirePath", "()Ljava/lang/String;");
|
||||
|
||||
path = "";
|
||||
initialized = true;
|
||||
|
||||
if (method_id)
|
||||
{
|
||||
jstring cpath = (jstring) env->CallObjectMethod(activity, method_id);
|
||||
const char *utf = env->GetStringUTFChars(cpath, nullptr);
|
||||
if (utf)
|
||||
{
|
||||
path = SDL_strdup(utf);
|
||||
env->ReleaseStringUTFChars(cpath, utf);
|
||||
}
|
||||
|
||||
env->DeleteLocalRef(cpath);
|
||||
}
|
||||
else
|
||||
{
|
||||
// NoSuchMethodException is thrown in case methodID is null
|
||||
env->ExceptionClear();
|
||||
return "";
|
||||
}
|
||||
|
||||
env->DeleteLocalRef(activity);
|
||||
env->DeleteLocalRef(clazz);
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
} // android
|
||||
} // love
|
||||
|
||||
|
||||
@@ -101,6 +101,8 @@ void deinitializeVirtualArchive();
|
||||
*/
|
||||
bool checkFusedGame(void **physfsIO_Out);
|
||||
|
||||
const char *getCRequirePath();
|
||||
|
||||
} // android
|
||||
} // love
|
||||
|
||||
|
||||
@@ -1156,7 +1156,9 @@ void luax_runwrapper(lua_State *L, const char *filedata, size_t datalen, const c
|
||||
// functions struct pointer as arguments.
|
||||
if (lua_istable(L, -1))
|
||||
{
|
||||
luaL_loadbuffer(L, filedata, datalen, filename);
|
||||
std::string chunkname = std::string("=[love \"") + std::string(filename) + std::string("\"]");
|
||||
|
||||
luaL_loadbuffer(L, filedata, datalen, chunkname.c_str());
|
||||
lua_pushvalue(L, -2);
|
||||
if (ffifuncs != nullptr)
|
||||
luax_pushpointerasstring(L, ffifuncs);
|
||||
@@ -1179,8 +1181,10 @@ int luax_resume(lua_State *L, int nargs, int* nres)
|
||||
#if LUA_VERSION_NUM >= 504
|
||||
return lua_resume(L, nullptr, nargs, nres);
|
||||
#elif LUA_VERSION_NUM >= 502
|
||||
LOVE_UNUSED(nres);
|
||||
return lua_resume(L, nullptr, nargs);
|
||||
#else
|
||||
LOVE_UNUSED(nres);
|
||||
return lua_resume(L, nargs);
|
||||
#endif
|
||||
}
|
||||
|
||||
+40
-14
@@ -118,11 +118,30 @@ static size_t find_peer_index(lua_State *l, ENetHost *enet_host, ENetPeer *peer)
|
||||
#define ENET_ALIGNOF(x) alignof(x)
|
||||
#endif
|
||||
|
||||
// For use with the enet_peers registry.
|
||||
// Using the pointer directly via lightuserdata would be ideal, but LuaJIT
|
||||
// cannot use lightuserdata with more than 47 bits whereas some newer arm64
|
||||
// architectures allow pointers which use more than that.
|
||||
static lua_Number compute_peer_key(lua_State *L, ENetPeer *peer)
|
||||
static bool supports_full_lightuserdata(lua_State *L)
|
||||
{
|
||||
static bool checked = false;
|
||||
static bool supported = false;
|
||||
|
||||
if (!checked)
|
||||
{
|
||||
lua_pushcclosure(L, [](lua_State* L) -> int
|
||||
{
|
||||
// Try to push pointer with all bits set.
|
||||
lua_pushlightuserdata(L, (void*)(~((size_t)0)));
|
||||
return 1;
|
||||
}, 0);
|
||||
|
||||
supported = lua_pcall(L, 0, 1, 0) == 0;
|
||||
checked = true;
|
||||
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
return supported;
|
||||
}
|
||||
|
||||
static uintptr_t compute_peer_key(lua_State *L, ENetPeer *peer)
|
||||
{
|
||||
// ENet peers are be allocated on the heap in an array. Lua numbers
|
||||
// (doubles) can store all possible integers up to 2^53. We can store
|
||||
@@ -140,21 +159,28 @@ static lua_Number compute_peer_key(lua_State *L, ENetPeer *peer)
|
||||
|
||||
static const size_t shift = (size_t) log2((double) minalign);
|
||||
|
||||
key >>= shift;
|
||||
return key >> shift;
|
||||
}
|
||||
|
||||
// Make sure our key isn't larger than 2^53.
|
||||
if (key > 0x20000000000000ULL)
|
||||
luaL_error(L, "Cannot push enet peer to Lua: pointer value %p is too large", peer);
|
||||
|
||||
return (lua_Number) key;
|
||||
static void push_peer_key(lua_State *L, uintptr_t key)
|
||||
{
|
||||
// If full 64-bit lightuserdata is supported, always use that. Otherwise,
|
||||
// if the key is smaller than 2^53 (which is integer precision for double
|
||||
// datatype), then push number. Otherwise, throw error.
|
||||
if (supports_full_lightuserdata(L))
|
||||
lua_pushlightuserdata(L, (void*) key);
|
||||
else if (key > 0x20000000000000ULL) // 2^53
|
||||
luaL_error(L, "Cannot push enet peer to Lua: pointer value %p is too large", key);
|
||||
else
|
||||
lua_pushnumber(L, (lua_Number) key);
|
||||
}
|
||||
|
||||
static void push_peer(lua_State *l, ENetPeer *peer) {
|
||||
lua_Number key = compute_peer_key(l, peer);
|
||||
uintptr_t key = compute_peer_key(l, peer);
|
||||
|
||||
// try to find in peer table
|
||||
lua_getfield(l, LUA_REGISTRYINDEX, "enet_peers");
|
||||
lua_pushnumber(l, key);
|
||||
push_peer_key(l, key);
|
||||
lua_gettable(l, -2);
|
||||
|
||||
if (lua_isnil(l, -1)) {
|
||||
@@ -165,7 +191,7 @@ static void push_peer(lua_State *l, ENetPeer *peer) {
|
||||
luaL_getmetatable(l, "enet_peer");
|
||||
lua_setmetatable(l, -2);
|
||||
|
||||
lua_pushnumber(l, key);
|
||||
push_peer_key(l, key);
|
||||
lua_pushvalue(l, -2);
|
||||
|
||||
lua_settable(l, -4);
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -540,5 +540,5 @@ static const unsigned char B1[]={
|
||||
|
||||
};
|
||||
|
||||
if (luaL_loadbuffer(L,(const char*)B1,sizeof(B1),"ftp.lua")==0) lua_call(L, 0, LUA_MULTRET);
|
||||
if (luaL_loadbuffer(L,(const char*)B1,sizeof(B1),"=[socket \"ftp.lua\"]")==0) lua_call(L, 0, LUA_MULTRET);
|
||||
}
|
||||
|
||||
@@ -192,5 +192,5 @@ static const unsigned char B1[]={
|
||||
108,101,114, 34, 44, 10,125, 10, 10,114,101,116,117,114,110, 32, 95, 77,
|
||||
};
|
||||
|
||||
if (luaL_loadbuffer(L,(const char*)B1,sizeof(B1),"headers.lua")==0) lua_call(L, 0, LUA_MULTRET);
|
||||
if (luaL_loadbuffer(L,(const char*)B1,sizeof(B1),"=[socket \"headers.lua\"]")==0) lua_call(L, 0, LUA_MULTRET);
|
||||
}
|
||||
|
||||
@@ -668,5 +668,5 @@ static const unsigned char B1[]={
|
||||
101,110,100, 41, 10, 10,114,101,116,117,114,110, 32, 95, 77, 10,
|
||||
};
|
||||
|
||||
if (luaL_loadbuffer(L,(const char*)B1,sizeof(B1),"http.lua")==0) lua_call(L, 0, LUA_MULTRET);
|
||||
if (luaL_loadbuffer(L,(const char*)B1,sizeof(B1),"=[socket \"http.lua\"]")==0) lua_call(L, 0, LUA_MULTRET);
|
||||
}
|
||||
|
||||
@@ -442,5 +442,5 @@ static const unsigned char B1[]={
|
||||
110,100, 10,101,110,100, 10, 10,114,101,116,117,114,110, 32, 95, 77, 10,
|
||||
};
|
||||
|
||||
if (luaL_loadbuffer(L,(const char*)B1,sizeof(B1),"ltn12.lua")==0) lua_call(L, 0, LUA_MULTRET);
|
||||
if (luaL_loadbuffer(L,(const char*)B1,sizeof(B1),"=[socket \"ltn12.lua\"]")==0) lua_call(L, 0, LUA_MULTRET);
|
||||
}
|
||||
|
||||
@@ -140,5 +140,5 @@ static const unsigned char B1[]={
|
||||
110,100, 10, 10,114,101,116,117,114,110, 32, 95, 77, 10,
|
||||
};
|
||||
|
||||
if (luaL_loadbuffer(L,(const char*)B1,sizeof(B1),"mbox.lua")==0) lua_call(L, 0, LUA_MULTRET);
|
||||
if (luaL_loadbuffer(L,(const char*)B1,sizeof(B1),"=[socket \"mbox.lua\"]")==0) lua_call(L, 0, LUA_MULTRET);
|
||||
}
|
||||
|
||||
@@ -132,5 +132,5 @@ static const unsigned char B1[]={
|
||||
116,117,114,110, 32, 95, 77,
|
||||
};
|
||||
|
||||
if (luaL_loadbuffer(L,(const char*)B1,sizeof(B1),"mime.lua")==0) lua_call(L, 0, LUA_MULTRET);
|
||||
if (luaL_loadbuffer(L,(const char*)B1,sizeof(B1),"=[socket \"mime.lua\"]")==0) lua_call(L, 0, LUA_MULTRET);
|
||||
}
|
||||
|
||||
@@ -411,5 +411,5 @@ static const unsigned char B1[]={
|
||||
110,100, 41, 10, 10,114,101,116,117,114,110, 32, 95, 77,
|
||||
};
|
||||
|
||||
if (luaL_loadbuffer(L,(const char*)B1,sizeof(B1),"smtp.lua")==0) lua_call(L, 0, LUA_MULTRET);
|
||||
if (luaL_loadbuffer(L,(const char*)B1,sizeof(B1),"=[socket \"smtp.lua\"]")==0) lua_call(L, 0, LUA_MULTRET);
|
||||
}
|
||||
|
||||
@@ -230,5 +230,5 @@ static const unsigned char B1[]={
|
||||
116,117,114,110, 32, 95, 77, 10,
|
||||
};
|
||||
|
||||
if (luaL_loadbuffer(L,(const char*)B1,sizeof(B1),"socket.lua")==0) lua_call(L, 0, LUA_MULTRET);
|
||||
if (luaL_loadbuffer(L,(const char*)B1,sizeof(B1),"=[socket \"socket.lua\"]")==0) lua_call(L, 0, LUA_MULTRET);
|
||||
}
|
||||
|
||||
@@ -196,5 +196,5 @@ static const unsigned char B1[]={
|
||||
114,110, 32, 95, 77, 10,
|
||||
};
|
||||
|
||||
if (luaL_loadbuffer(L,(const char*)B1,sizeof(B1),"tp.lua")==0) lua_call(L, 0, LUA_MULTRET);
|
||||
if (luaL_loadbuffer(L,(const char*)B1,sizeof(B1),"=[socket \"tp.lua\"]")==0) lua_call(L, 0, LUA_MULTRET);
|
||||
}
|
||||
|
||||
@@ -563,5 +563,5 @@ static const unsigned char B1[]={
|
||||
110, 32, 95, 77, 10,
|
||||
};
|
||||
|
||||
if (luaL_loadbuffer(L,(const char*)B1,sizeof(B1),"url.lua")==0) lua_call(L, 0, LUA_MULTRET);
|
||||
if (luaL_loadbuffer(L,(const char*)B1,sizeof(B1),"=[socket \"url.lua\"]")==0) lua_call(L, 0, LUA_MULTRET);
|
||||
}
|
||||
|
||||
@@ -106,47 +106,47 @@ unsigned char Noise1234::perm[] = {151,160,137,91,90,15,
|
||||
* float SLnoise = (Noise1234::noise(x,y,z) + 1.0) * 0.5;
|
||||
*/
|
||||
|
||||
float Noise1234::grad( int hash, float x ) {
|
||||
double Noise1234::grad( int hash, double x ) {
|
||||
int h = hash & 15;
|
||||
float grad = 1.0 + (h & 7); // Gradient value 1.0, 2.0, ..., 8.0
|
||||
double grad = 1.0 + (h & 7); // Gradient value 1.0, 2.0, ..., 8.0
|
||||
if (h&8) grad = -grad; // and a random sign for the gradient
|
||||
return ( grad * x ); // Multiply the gradient with the distance
|
||||
}
|
||||
|
||||
float Noise1234::grad( int hash, float x, float y ) {
|
||||
int h = hash & 7; // Convert low 3 bits of hash code
|
||||
float u = h<4 ? x : y; // into 8 simple gradient directions,
|
||||
float v = h<4 ? y : x; // and compute the dot product with (x,y).
|
||||
double Noise1234::grad( int hash, double x, double y ) {
|
||||
int h = hash & 7; // Convert low 3 bits of hash code
|
||||
double u = h<4 ? x : y; // into 8 simple gradient directions,
|
||||
double v = h<4 ? y : x; // and compute the dot product with (x,y).
|
||||
return ((h&1)? -u : u) + ((h&2)? -2.0*v : 2.0*v);
|
||||
}
|
||||
|
||||
float Noise1234::grad( int hash, float x, float y , float z ) {
|
||||
int h = hash & 15; // Convert low 4 bits of hash code into 12 simple
|
||||
float u = h<8 ? x : y; // gradient directions, and compute dot product.
|
||||
float v = h<4 ? y : h==12||h==14 ? x : z; // Fix repeats at h = 12 to 15
|
||||
double Noise1234::grad( int hash, double x, double y , double z ) {
|
||||
int h = hash & 15; // Convert low 4 bits of hash code into 12 simple
|
||||
double u = h<8 ? x : y; // gradient directions, and compute dot product.
|
||||
double v = h<4 ? y : h==12||h==14 ? x : z; // Fix repeats at h = 12 to 15
|
||||
return ((h&1)? -u : u) + ((h&2)? -v : v);
|
||||
}
|
||||
|
||||
float Noise1234::grad( int hash, float x, float y, float z, float t ) {
|
||||
int h = hash & 31; // Convert low 5 bits of hash code into 32 simple
|
||||
float u = h<24 ? x : y; // gradient directions, and compute dot product.
|
||||
float v = h<16 ? y : z;
|
||||
float w = h<8 ? z : t;
|
||||
double Noise1234::grad( int hash, double x, double y, double z, double t ) {
|
||||
int h = hash & 31; // Convert low 5 bits of hash code into 32 simple
|
||||
double u = h<24 ? x : y; // gradient directions, and compute dot product.
|
||||
double v = h<16 ? y : z;
|
||||
double w = h<8 ? z : t;
|
||||
return ((h&1)? -u : u) + ((h&2)? -v : v) + ((h&4)? -w : w);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
/** 1D float Perlin noise, SL "noise()"
|
||||
*/
|
||||
float Noise1234::noise( float x )
|
||||
float Noise1234::noise( double x )
|
||||
{
|
||||
int ix0, ix1;
|
||||
float fx0, fx1;
|
||||
float s, n0, n1;
|
||||
double fx0, fx1;
|
||||
double s, n0, n1;
|
||||
|
||||
ix0 = FASTFLOOR( x ); // Integer part of x
|
||||
fx0 = x - ix0; // Fractional part of x
|
||||
fx1 = fx0 - 1.0f;
|
||||
fx0 = x - ix0; // Fractional part of x
|
||||
fx1 = fx0 - 1.0;
|
||||
ix1 = ( ix0+1 ) & 0xff;
|
||||
ix0 = ix0 & 0xff; // Wrap to 0..255
|
||||
|
||||
@@ -160,17 +160,17 @@ float Noise1234::noise( float x )
|
||||
//---------------------------------------------------------------------
|
||||
/** 1D float Perlin periodic noise, SL "pnoise()"
|
||||
*/
|
||||
float Noise1234::pnoise( float x, int px )
|
||||
float Noise1234::pnoise( double x, int px )
|
||||
{
|
||||
int ix0, ix1;
|
||||
float fx0, fx1;
|
||||
float s, n0, n1;
|
||||
double fx0, fx1;
|
||||
double s, n0, n1;
|
||||
|
||||
ix0 = FASTFLOOR( x ); // Integer part of x
|
||||
fx0 = x - ix0; // Fractional part of x
|
||||
fx1 = fx0 - 1.0f;
|
||||
fx0 = x - ix0; // Fractional part of x
|
||||
fx1 = fx0 - 1.0;
|
||||
ix1 = (( ix0 + 1 ) % px) & 0xff; // Wrap to 0..px-1 *and* wrap to 0..255
|
||||
ix0 = ( ix0 % px ) & 0xff; // (because px might be greater than 256)
|
||||
ix0 = ( ix0 % px ) & 0xff; // (because px might be greater than 256)
|
||||
|
||||
s = FADE( fx0 );
|
||||
|
||||
@@ -183,23 +183,23 @@ float Noise1234::pnoise( float x, int px )
|
||||
//---------------------------------------------------------------------
|
||||
/** 2D float Perlin noise.
|
||||
*/
|
||||
float Noise1234::noise( float x, float y )
|
||||
float Noise1234::noise( double x, double y )
|
||||
{
|
||||
int ix0, iy0, ix1, iy1;
|
||||
float fx0, fy0, fx1, fy1;
|
||||
float s, t, nx0, nx1, n0, n1;
|
||||
double fx0, fy0, fx1, fy1;
|
||||
double s, t, nx0, nx1, n0, n1;
|
||||
|
||||
ix0 = FASTFLOOR( x ); // Integer part of x
|
||||
iy0 = FASTFLOOR( y ); // Integer part of y
|
||||
fx0 = x - ix0; // Fractional part of x
|
||||
fy0 = y - iy0; // Fractional part of y
|
||||
fx1 = fx0 - 1.0f;
|
||||
fy1 = fy0 - 1.0f;
|
||||
fx1 = fx0 - 1.0;
|
||||
fy1 = fy0 - 1.0;
|
||||
ix1 = (ix0 + 1) & 0xff; // Wrap to 0..255
|
||||
iy1 = (iy0 + 1) & 0xff;
|
||||
ix0 = ix0 & 0xff;
|
||||
iy0 = iy0 & 0xff;
|
||||
|
||||
|
||||
t = FADE( fy0 );
|
||||
s = FADE( fx0 );
|
||||
|
||||
@@ -217,23 +217,23 @@ float Noise1234::noise( float x, float y )
|
||||
//---------------------------------------------------------------------
|
||||
/** 2D float Perlin periodic noise.
|
||||
*/
|
||||
float Noise1234::pnoise( float x, float y, int px, int py )
|
||||
float Noise1234::pnoise( double x, double y, int px, int py )
|
||||
{
|
||||
int ix0, iy0, ix1, iy1;
|
||||
float fx0, fy0, fx1, fy1;
|
||||
float s, t, nx0, nx1, n0, n1;
|
||||
double fx0, fy0, fx1, fy1;
|
||||
double s, t, nx0, nx1, n0, n1;
|
||||
|
||||
ix0 = FASTFLOOR( x ); // Integer part of x
|
||||
iy0 = FASTFLOOR( y ); // Integer part of y
|
||||
fx0 = x - ix0; // Fractional part of x
|
||||
fy0 = y - iy0; // Fractional part of y
|
||||
fx1 = fx0 - 1.0f;
|
||||
fy1 = fy0 - 1.0f;
|
||||
fx1 = fx0 - 1.0;
|
||||
fy1 = fy0 - 1.0;
|
||||
ix1 = (( ix0 + 1 ) % px) & 0xff; // Wrap to 0..px-1 and wrap to 0..255
|
||||
iy1 = (( iy0 + 1 ) % py) & 0xff; // Wrap to 0..py-1 and wrap to 0..255
|
||||
ix0 = ( ix0 % px ) & 0xff;
|
||||
iy0 = ( iy0 % py ) & 0xff;
|
||||
|
||||
|
||||
t = FADE( fy0 );
|
||||
s = FADE( fx0 );
|
||||
|
||||
@@ -252,12 +252,12 @@ float Noise1234::pnoise( float x, float y, int px, int py )
|
||||
//---------------------------------------------------------------------
|
||||
/** 3D float Perlin noise.
|
||||
*/
|
||||
float Noise1234::noise( float x, float y, float z )
|
||||
float Noise1234::noise( double x, double y, double z )
|
||||
{
|
||||
int ix0, iy0, ix1, iy1, iz0, iz1;
|
||||
float fx0, fy0, fz0, fx1, fy1, fz1;
|
||||
float s, t, r;
|
||||
float nxy0, nxy1, nx0, nx1, n0, n1;
|
||||
double fx0, fy0, fz0, fx1, fy1, fz1;
|
||||
double s, t, r;
|
||||
double nxy0, nxy1, nx0, nx1, n0, n1;
|
||||
|
||||
ix0 = FASTFLOOR( x ); // Integer part of x
|
||||
iy0 = FASTFLOOR( y ); // Integer part of y
|
||||
@@ -265,16 +265,16 @@ float Noise1234::noise( float x, float y, float z )
|
||||
fx0 = x - ix0; // Fractional part of x
|
||||
fy0 = y - iy0; // Fractional part of y
|
||||
fz0 = z - iz0; // Fractional part of z
|
||||
fx1 = fx0 - 1.0f;
|
||||
fy1 = fy0 - 1.0f;
|
||||
fz1 = fz0 - 1.0f;
|
||||
fx1 = fx0 - 1.0;
|
||||
fy1 = fy0 - 1.0;
|
||||
fz1 = fz0 - 1.0;
|
||||
ix1 = ( ix0 + 1 ) & 0xff; // Wrap to 0..255
|
||||
iy1 = ( iy0 + 1 ) & 0xff;
|
||||
iz1 = ( iz0 + 1 ) & 0xff;
|
||||
ix0 = ix0 & 0xff;
|
||||
iy0 = iy0 & 0xff;
|
||||
iz0 = iz0 & 0xff;
|
||||
|
||||
|
||||
r = FADE( fz0 );
|
||||
t = FADE( fy0 );
|
||||
s = FADE( fx0 );
|
||||
@@ -298,19 +298,19 @@ float Noise1234::noise( float x, float y, float z )
|
||||
nx1 = LERP( r, nxy0, nxy1 );
|
||||
|
||||
n1 = LERP( t, nx0, nx1 );
|
||||
|
||||
|
||||
return 0.936f * ( LERP( s, n0, n1 ) );
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
/** 3D float Perlin periodic noise.
|
||||
*/
|
||||
float Noise1234::pnoise( float x, float y, float z, int px, int py, int pz )
|
||||
float Noise1234::pnoise( double x, double y, double z, int px, int py, int pz )
|
||||
{
|
||||
int ix0, iy0, ix1, iy1, iz0, iz1;
|
||||
float fx0, fy0, fz0, fx1, fy1, fz1;
|
||||
float s, t, r;
|
||||
float nxy0, nxy1, nx0, nx1, n0, n1;
|
||||
double fx0, fy0, fz0, fx1, fy1, fz1;
|
||||
double s, t, r;
|
||||
double nxy0, nxy1, nx0, nx1, n0, n1;
|
||||
|
||||
ix0 = FASTFLOOR( x ); // Integer part of x
|
||||
iy0 = FASTFLOOR( y ); // Integer part of y
|
||||
@@ -318,16 +318,16 @@ float Noise1234::pnoise( float x, float y, float z, int px, int py, int pz )
|
||||
fx0 = x - ix0; // Fractional part of x
|
||||
fy0 = y - iy0; // Fractional part of y
|
||||
fz0 = z - iz0; // Fractional part of z
|
||||
fx1 = fx0 - 1.0f;
|
||||
fy1 = fy0 - 1.0f;
|
||||
fz1 = fz0 - 1.0f;
|
||||
fx1 = fx0 - 1.0;
|
||||
fy1 = fy0 - 1.0;
|
||||
fz1 = fz0 - 1.0;
|
||||
ix1 = (( ix0 + 1 ) % px ) & 0xff; // Wrap to 0..px-1 and wrap to 0..255
|
||||
iy1 = (( iy0 + 1 ) % py ) & 0xff; // Wrap to 0..py-1 and wrap to 0..255
|
||||
iz1 = (( iz0 + 1 ) % pz ) & 0xff; // Wrap to 0..pz-1 and wrap to 0..255
|
||||
ix0 = ( ix0 % px ) & 0xff;
|
||||
iy0 = ( iy0 % py ) & 0xff;
|
||||
iz0 = ( iz0 % pz ) & 0xff;
|
||||
|
||||
|
||||
r = FADE( fz0 );
|
||||
t = FADE( fy0 );
|
||||
s = FADE( fx0 );
|
||||
@@ -351,7 +351,7 @@ float Noise1234::pnoise( float x, float y, float z, int px, int py, int pz )
|
||||
nx1 = LERP( r, nxy0, nxy1 );
|
||||
|
||||
n1 = LERP( t, nx0, nx1 );
|
||||
|
||||
|
||||
return 0.936f * ( LERP( s, n0, n1 ) );
|
||||
}
|
||||
|
||||
@@ -360,12 +360,12 @@ float Noise1234::pnoise( float x, float y, float z, int px, int py, int pz )
|
||||
/** 4D float Perlin noise.
|
||||
*/
|
||||
|
||||
float Noise1234::noise( float x, float y, float z, float w )
|
||||
float Noise1234::noise( double x, double y, double z, double w )
|
||||
{
|
||||
int ix0, iy0, iz0, iw0, ix1, iy1, iz1, iw1;
|
||||
float fx0, fy0, fz0, fw0, fx1, fy1, fz1, fw1;
|
||||
float s, t, r, q;
|
||||
float nxyz0, nxyz1, nxy0, nxy1, nx0, nx1, n0, n1;
|
||||
double fx0, fy0, fz0, fw0, fx1, fy1, fz1, fw1;
|
||||
double s, t, r, q;
|
||||
double nxyz0, nxyz1, nxy0, nxy1, nx0, nx1, n0, n1;
|
||||
|
||||
ix0 = FASTFLOOR( x ); // Integer part of x
|
||||
iy0 = FASTFLOOR( y ); // Integer part of y
|
||||
@@ -375,10 +375,10 @@ float Noise1234::noise( float x, float y, float z, float w )
|
||||
fy0 = y - iy0; // Fractional part of y
|
||||
fz0 = z - iz0; // Fractional part of z
|
||||
fw0 = w - iw0; // Fractional part of w
|
||||
fx1 = fx0 - 1.0f;
|
||||
fy1 = fy0 - 1.0f;
|
||||
fz1 = fz0 - 1.0f;
|
||||
fw1 = fw0 - 1.0f;
|
||||
fx1 = fx0 - 1.0;
|
||||
fy1 = fy0 - 1.0;
|
||||
fz1 = fz0 - 1.0;
|
||||
fw1 = fw0 - 1.0;
|
||||
ix1 = ( ix0 + 1 ) & 0xff; // Wrap to 0..255
|
||||
iy1 = ( iy0 + 1 ) & 0xff;
|
||||
iz1 = ( iz0 + 1 ) & 0xff;
|
||||
@@ -396,17 +396,17 @@ float Noise1234::noise( float x, float y, float z, float w )
|
||||
nxyz0 = grad(perm[ix0 + perm[iy0 + perm[iz0 + perm[iw0]]]], fx0, fy0, fz0, fw0);
|
||||
nxyz1 = grad(perm[ix0 + perm[iy0 + perm[iz0 + perm[iw1]]]], fx0, fy0, fz0, fw1);
|
||||
nxy0 = LERP( q, nxyz0, nxyz1 );
|
||||
|
||||
|
||||
nxyz0 = grad(perm[ix0 + perm[iy0 + perm[iz1 + perm[iw0]]]], fx0, fy0, fz1, fw0);
|
||||
nxyz1 = grad(perm[ix0 + perm[iy0 + perm[iz1 + perm[iw1]]]], fx0, fy0, fz1, fw1);
|
||||
nxy1 = LERP( q, nxyz0, nxyz1 );
|
||||
|
||||
|
||||
nx0 = LERP ( r, nxy0, nxy1 );
|
||||
|
||||
nxyz0 = grad(perm[ix0 + perm[iy1 + perm[iz0 + perm[iw0]]]], fx0, fy1, fz0, fw0);
|
||||
nxyz1 = grad(perm[ix0 + perm[iy1 + perm[iz0 + perm[iw1]]]], fx0, fy1, fz0, fw1);
|
||||
nxy0 = LERP( q, nxyz0, nxyz1 );
|
||||
|
||||
|
||||
nxyz0 = grad(perm[ix0 + perm[iy1 + perm[iz1 + perm[iw0]]]], fx0, fy1, fz1, fw0);
|
||||
nxyz1 = grad(perm[ix0 + perm[iy1 + perm[iz1 + perm[iw1]]]], fx0, fy1, fz1, fw1);
|
||||
nxy1 = LERP( q, nxyz0, nxyz1 );
|
||||
@@ -418,7 +418,7 @@ float Noise1234::noise( float x, float y, float z, float w )
|
||||
nxyz0 = grad(perm[ix1 + perm[iy0 + perm[iz0 + perm[iw0]]]], fx1, fy0, fz0, fw0);
|
||||
nxyz1 = grad(perm[ix1 + perm[iy0 + perm[iz0 + perm[iw1]]]], fx1, fy0, fz0, fw1);
|
||||
nxy0 = LERP( q, nxyz0, nxyz1 );
|
||||
|
||||
|
||||
nxyz0 = grad(perm[ix1 + perm[iy0 + perm[iz1 + perm[iw0]]]], fx1, fy0, fz1, fw0);
|
||||
nxyz1 = grad(perm[ix1 + perm[iy0 + perm[iz1 + perm[iw1]]]], fx1, fy0, fz1, fw1);
|
||||
nxy1 = LERP( q, nxyz0, nxyz1 );
|
||||
@@ -428,7 +428,7 @@ float Noise1234::noise( float x, float y, float z, float w )
|
||||
nxyz0 = grad(perm[ix1 + perm[iy1 + perm[iz0 + perm[iw0]]]], fx1, fy1, fz0, fw0);
|
||||
nxyz1 = grad(perm[ix1 + perm[iy1 + perm[iz0 + perm[iw1]]]], fx1, fy1, fz0, fw1);
|
||||
nxy0 = LERP( q, nxyz0, nxyz1 );
|
||||
|
||||
|
||||
nxyz0 = grad(perm[ix1 + perm[iy1 + perm[iz1 + perm[iw0]]]], fx1, fy1, fz1, fw0);
|
||||
nxyz1 = grad(perm[ix1 + perm[iy1 + perm[iz1 + perm[iw1]]]], fx1, fy1, fz1, fw1);
|
||||
nxy1 = LERP( q, nxyz0, nxyz1 );
|
||||
@@ -444,13 +444,13 @@ float Noise1234::noise( float x, float y, float z, float w )
|
||||
/** 4D float Perlin periodic noise.
|
||||
*/
|
||||
|
||||
float Noise1234::pnoise( float x, float y, float z, float w,
|
||||
float Noise1234::pnoise( double x, double y, double z, double w,
|
||||
int px, int py, int pz, int pw )
|
||||
{
|
||||
int ix0, iy0, iz0, iw0, ix1, iy1, iz1, iw1;
|
||||
float fx0, fy0, fz0, fw0, fx1, fy1, fz1, fw1;
|
||||
float s, t, r, q;
|
||||
float nxyz0, nxyz1, nxy0, nxy1, nx0, nx1, n0, n1;
|
||||
double fx0, fy0, fz0, fw0, fx1, fy1, fz1, fw1;
|
||||
double s, t, r, q;
|
||||
double nxyz0, nxyz1, nxy0, nxy1, nx0, nx1, n0, n1;
|
||||
|
||||
ix0 = FASTFLOOR( x ); // Integer part of x
|
||||
iy0 = FASTFLOOR( y ); // Integer part of y
|
||||
@@ -460,10 +460,10 @@ float Noise1234::pnoise( float x, float y, float z, float w,
|
||||
fy0 = y - iy0; // Fractional part of y
|
||||
fz0 = z - iz0; // Fractional part of z
|
||||
fw0 = w - iw0; // Fractional part of w
|
||||
fx1 = fx0 - 1.0f;
|
||||
fy1 = fy0 - 1.0f;
|
||||
fz1 = fz0 - 1.0f;
|
||||
fw1 = fw0 - 1.0f;
|
||||
fx1 = fx0 - 1.0;
|
||||
fy1 = fy0 - 1.0;
|
||||
fz1 = fz0 - 1.0;
|
||||
fw1 = fw0 - 1.0;
|
||||
ix1 = (( ix0 + 1 ) % px ) & 0xff; // Wrap to 0..px-1 and wrap to 0..255
|
||||
iy1 = (( iy0 + 1 ) % py ) & 0xff; // Wrap to 0..py-1 and wrap to 0..255
|
||||
iz1 = (( iz0 + 1 ) % pz ) & 0xff; // Wrap to 0..pz-1 and wrap to 0..255
|
||||
@@ -481,17 +481,17 @@ float Noise1234::pnoise( float x, float y, float z, float w,
|
||||
nxyz0 = grad(perm[ix0 + perm[iy0 + perm[iz0 + perm[iw0]]]], fx0, fy0, fz0, fw0);
|
||||
nxyz1 = grad(perm[ix0 + perm[iy0 + perm[iz0 + perm[iw1]]]], fx0, fy0, fz0, fw1);
|
||||
nxy0 = LERP( q, nxyz0, nxyz1 );
|
||||
|
||||
|
||||
nxyz0 = grad(perm[ix0 + perm[iy0 + perm[iz1 + perm[iw0]]]], fx0, fy0, fz1, fw0);
|
||||
nxyz1 = grad(perm[ix0 + perm[iy0 + perm[iz1 + perm[iw1]]]], fx0, fy0, fz1, fw1);
|
||||
nxy1 = LERP( q, nxyz0, nxyz1 );
|
||||
|
||||
|
||||
nx0 = LERP ( r, nxy0, nxy1 );
|
||||
|
||||
nxyz0 = grad(perm[ix0 + perm[iy1 + perm[iz0 + perm[iw0]]]], fx0, fy1, fz0, fw0);
|
||||
nxyz1 = grad(perm[ix0 + perm[iy1 + perm[iz0 + perm[iw1]]]], fx0, fy1, fz0, fw1);
|
||||
nxy0 = LERP( q, nxyz0, nxyz1 );
|
||||
|
||||
|
||||
nxyz0 = grad(perm[ix0 + perm[iy1 + perm[iz1 + perm[iw0]]]], fx0, fy1, fz1, fw0);
|
||||
nxyz1 = grad(perm[ix0 + perm[iy1 + perm[iz1 + perm[iw1]]]], fx0, fy1, fz1, fw1);
|
||||
nxy1 = LERP( q, nxyz0, nxyz1 );
|
||||
@@ -503,7 +503,7 @@ float Noise1234::pnoise( float x, float y, float z, float w,
|
||||
nxyz0 = grad(perm[ix1 + perm[iy0 + perm[iz0 + perm[iw0]]]], fx1, fy0, fz0, fw0);
|
||||
nxyz1 = grad(perm[ix1 + perm[iy0 + perm[iz0 + perm[iw1]]]], fx1, fy0, fz0, fw1);
|
||||
nxy0 = LERP( q, nxyz0, nxyz1 );
|
||||
|
||||
|
||||
nxyz0 = grad(perm[ix1 + perm[iy0 + perm[iz1 + perm[iw0]]]], fx1, fy0, fz1, fw0);
|
||||
nxyz1 = grad(perm[ix1 + perm[iy0 + perm[iz1 + perm[iw1]]]], fx1, fy0, fz1, fw1);
|
||||
nxy1 = LERP( q, nxyz0, nxyz1 );
|
||||
@@ -513,7 +513,7 @@ float Noise1234::pnoise( float x, float y, float z, float w,
|
||||
nxyz0 = grad(perm[ix1 + perm[iy1 + perm[iz0 + perm[iw0]]]], fx1, fy1, fz0, fw0);
|
||||
nxyz1 = grad(perm[ix1 + perm[iy1 + perm[iz0 + perm[iw1]]]], fx1, fy1, fz0, fw1);
|
||||
nxy0 = LERP( q, nxyz0, nxyz1 );
|
||||
|
||||
|
||||
nxyz0 = grad(perm[ix1 + perm[iy1 + perm[iz1 + perm[iw0]]]], fx1, fy1, fz1, fw0);
|
||||
nxyz1 = grad(perm[ix1 + perm[iy1 + perm[iz1 + perm[iw1]]]], fx1, fy1, fz1, fw1);
|
||||
nxy1 = LERP( q, nxyz0, nxyz1 );
|
||||
|
||||
@@ -20,10 +20,6 @@
|
||||
* This is a clean, fast, modern and free Perlin noise class in C++.
|
||||
* Being a stand-alone class with no external dependencies, it is
|
||||
* highly reusable without source code modifications.
|
||||
*
|
||||
* Note:
|
||||
* Replacing the "float" type with "double" can actually make this run faster
|
||||
* on some platforms. A templatized version of Noise1234 could be useful.
|
||||
*/
|
||||
|
||||
class Noise1234 {
|
||||
@@ -34,24 +30,24 @@ class Noise1234 {
|
||||
|
||||
/** 1D, 2D, 3D and 4D float Perlin noise, SL "noise()"
|
||||
*/
|
||||
static float noise( float x );
|
||||
static float noise( float x, float y );
|
||||
static float noise( float x, float y, float z );
|
||||
static float noise( float x, float y, float z, float w );
|
||||
static float noise( double x );
|
||||
static float noise( double x, double y );
|
||||
static float noise( double x, double y, double z );
|
||||
static float noise( double x, double y, double z, double w );
|
||||
|
||||
/** 1D, 2D, 3D and 4D float Perlin periodic noise, SL "pnoise()"
|
||||
*/
|
||||
static float pnoise( float x, int px );
|
||||
static float pnoise( float x, float y, int px, int py );
|
||||
static float pnoise( float x, float y, float z, int px, int py, int pz );
|
||||
static float pnoise( float x, float y, float z, float w,
|
||||
static float pnoise( double x, int px );
|
||||
static float pnoise( double x, double y, int px, int py );
|
||||
static float pnoise( double x, double y, double z, int px, int py, int pz );
|
||||
static float pnoise( double x, double y, double z, double w,
|
||||
int px, int py, int pz, int pw );
|
||||
|
||||
private:
|
||||
static unsigned char perm[];
|
||||
static float grad( int hash, float x );
|
||||
static float grad( int hash, float x, float y );
|
||||
static float grad( int hash, float x, float y , float z );
|
||||
static float grad( int hash, float x, float y, float z, float t );
|
||||
static double grad( int hash, double x );
|
||||
static double grad( int hash, double x, double y );
|
||||
static double grad( int hash, double x, double y , double z );
|
||||
static double grad( int hash, double x, double y, double z, double t );
|
||||
|
||||
};
|
||||
|
||||
@@ -85,7 +85,7 @@ unsigned char SimplexNoise1234::perm[512] = {151,160,137,91,90,15,
|
||||
129,22,39,253, 19,98,108,110,79,113,224,232,178,185, 112,104,218,246,97,228,
|
||||
251,34,242,193,238,210,144,12,191,179,162,241, 81,51,145,235,249,14,239,107,
|
||||
49,192,214, 31,181,199,106,157,184, 84,204,176,115,121,50,45,127, 4,150,254,
|
||||
138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180
|
||||
138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180
|
||||
};
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
@@ -103,37 +103,35 @@ unsigned char SimplexNoise1234::perm[512] = {151,160,137,91,90,15,
|
||||
* float SLnoise = (SimplexNoise1234::noise(x,y,z) + 1.0) * 0.5;
|
||||
*/
|
||||
|
||||
float SimplexNoise1234::grad( int hash, float x ) {
|
||||
double SimplexNoise1234::grad( int hash, double x ) {
|
||||
int h = hash & 15;
|
||||
float grad = 1.0f + (h & 7); // Gradient value 1.0, 2.0, ..., 8.0
|
||||
double grad = 1.0 + (h & 7); // Gradient value 1.0, 2.0, ..., 8.0
|
||||
if (h&8) grad = -grad; // Set a random sign for the gradient
|
||||
return ( grad * x ); // Multiply the gradient with the distance
|
||||
}
|
||||
|
||||
float SimplexNoise1234::grad( int hash, float x, float y ) {
|
||||
double SimplexNoise1234::grad( int hash, double x, double y ) {
|
||||
int h = hash & 7; // Convert low 3 bits of hash code
|
||||
float u = h<4 ? x : y; // into 8 simple gradient directions,
|
||||
float v = h<4 ? y : x; // and compute the dot product with (x,y).
|
||||
return ((h&1)? -u : u) + ((h&2)? -2.0f*v : 2.0f*v);
|
||||
double u = h<4 ? x : y; // into 8 simple gradient directions,
|
||||
double v = h<4 ? y : x; // and compute the dot product with (x,y).
|
||||
return ((h&1)? -u : u) + ((h&2)? -2.0*v : 2.0*v);
|
||||
}
|
||||
|
||||
// 1D simplex noise
|
||||
float SimplexNoise1234::noise(float x) {
|
||||
float SimplexNoise1234::noise(double x) {
|
||||
|
||||
int i0 = FASTFLOOR(x);
|
||||
int i1 = i0 + 1;
|
||||
float x0 = x - i0;
|
||||
float x1 = x0 - 1.0f;
|
||||
double x0 = x - i0;
|
||||
double x1 = x0 - 1.0;
|
||||
|
||||
float n0, n1;
|
||||
double n0, n1;
|
||||
|
||||
float t0 = 1.0f - x0*x0;
|
||||
// if(t0 < 0.0f) t0 = 0.0f;
|
||||
double t0 = 1.0 - x0*x0;
|
||||
t0 *= t0;
|
||||
n0 = t0 * t0 * grad(perm[i0 & 0xff], x0);
|
||||
|
||||
float t1 = 1.0f - x1*x1;
|
||||
// if(t1 < 0.0f) t1 = 0.0f;
|
||||
double t1 = 1.0 - x1*x1;
|
||||
t1 *= t1;
|
||||
n1 = t1 * t1 * grad(perm[i1 & 0xff], x1);
|
||||
// The maximum value of this noise is 8*(3/4)^4 = 2.53125
|
||||
@@ -143,25 +141,25 @@ float SimplexNoise1234::noise(float x) {
|
||||
}
|
||||
|
||||
// 2D simplex noise
|
||||
float SimplexNoise1234::noise(float x, float y) {
|
||||
float SimplexNoise1234::noise(double x, double y) {
|
||||
|
||||
#define F2 0.366025403 // F2 = 0.5*(sqrt(3.0)-1.0)
|
||||
#define G2 0.211324865 // G2 = (3.0-Math.sqrt(3.0))/6.0
|
||||
|
||||
float n0, n1, n2; // Noise contributions from the three corners
|
||||
double n0, n1, n2; // Noise contributions from the three corners
|
||||
|
||||
// Skew the input space to determine which simplex cell we're in
|
||||
float s = (x+y)*F2; // Hairy factor for 2D
|
||||
float xs = x + s;
|
||||
float ys = y + s;
|
||||
double s = (x+y)*F2; // Hairy factor for 2D
|
||||
double xs = x + s;
|
||||
double ys = y + s;
|
||||
int i = FASTFLOOR(xs);
|
||||
int j = FASTFLOOR(ys);
|
||||
|
||||
float t = (float)(i+j)*G2;
|
||||
float X0 = i-t; // Unskew the cell origin back to (x,y) space
|
||||
float Y0 = j-t;
|
||||
float x0 = x-X0; // The x,y distances from the cell origin
|
||||
float y0 = y-Y0;
|
||||
double t = (i+j)*G2;
|
||||
double X0 = i-t; // Unskew the cell origin back to (x,y) space
|
||||
double Y0 = j-t;
|
||||
double x0 = x-X0; // The x,y distances from the cell origin
|
||||
double y0 = y-Y0;
|
||||
|
||||
// For the 2D case, the simplex shape is an equilateral triangle.
|
||||
// Determine which simplex we are in.
|
||||
@@ -173,32 +171,32 @@ float SimplexNoise1234::noise(float x, float y) {
|
||||
// a step of (0,1) in (i,j) means a step of (-c,1-c) in (x,y), where
|
||||
// c = (3-sqrt(3))/6
|
||||
|
||||
float x1 = x0 - i1 + G2; // Offsets for middle corner in (x,y) unskewed coords
|
||||
float y1 = y0 - j1 + G2;
|
||||
float x2 = x0 - 1.0f + 2.0f * G2; // Offsets for last corner in (x,y) unskewed coords
|
||||
float y2 = y0 - 1.0f + 2.0f * G2;
|
||||
double x1 = x0 - i1 + G2; // Offsets for middle corner in (x,y) unskewed coords
|
||||
double y1 = y0 - j1 + G2;
|
||||
double x2 = x0 - 1.0 + 2.0 * G2; // Offsets for last corner in (x,y) unskewed coords
|
||||
double y2 = y0 - 1.0 + 2.0 * G2;
|
||||
|
||||
// Wrap the integer indices at 256, to avoid indexing perm[] out of bounds
|
||||
int ii = i & 0xff;
|
||||
int jj = j & 0xff;
|
||||
|
||||
// Calculate the contribution from the three corners
|
||||
float t0 = 0.5f - x0*x0-y0*y0;
|
||||
if(t0 < 0.0f) n0 = 0.0f;
|
||||
double t0 = 0.5 - x0*x0-y0*y0;
|
||||
if(t0 < 0.0) n0 = 0.0;
|
||||
else {
|
||||
t0 *= t0;
|
||||
n0 = t0 * t0 * grad(perm[ii+perm[jj]], x0, y0);
|
||||
n0 = t0 * t0 * grad(perm[ii+perm[jj]], x0, y0);
|
||||
}
|
||||
|
||||
float t1 = 0.5f - x1*x1-y1*y1;
|
||||
if(t1 < 0.0f) n1 = 0.0f;
|
||||
double t1 = 0.5 - x1*x1-y1*y1;
|
||||
if(t1 < 0.0) n1 = 0.0;
|
||||
else {
|
||||
t1 *= t1;
|
||||
n1 = t1 * t1 * grad(perm[ii+i1+perm[jj+j1]], x1, y1);
|
||||
}
|
||||
|
||||
float t2 = 0.5f - x2*x2-y2*y2;
|
||||
if(t2 < 0.0f) n2 = 0.0f;
|
||||
double t2 = 0.5 - x2*x2-y2*y2;
|
||||
if(t2 < 0.0) n2 = 0.0;
|
||||
else {
|
||||
t2 *= t2;
|
||||
n2 = t2 * t2 * grad(perm[ii+1+perm[jj+1]], x2, y2);
|
||||
|
||||
@@ -25,11 +25,6 @@
|
||||
* This is a clean, fast, modern and free Perlin Simplex noise class in C++.
|
||||
* Being a stand-alone class with no external dependencies, it is
|
||||
* highly reusable without source code modifications.
|
||||
*
|
||||
*
|
||||
* Note:
|
||||
* Replacing the "float" type with "double" can actually make this run faster
|
||||
* on some platforms. A templatized version of SimplexNoise1234 could be useful.
|
||||
*/
|
||||
|
||||
class SimplexNoise1234 {
|
||||
@@ -40,12 +35,12 @@ class SimplexNoise1234 {
|
||||
|
||||
/** 1D and 2D float Perlin noise
|
||||
*/
|
||||
static float noise( float x );
|
||||
static float noise( float x, float y );
|
||||
static float noise( double x );
|
||||
static float noise( double x, double y );
|
||||
|
||||
private:
|
||||
static unsigned char perm[];
|
||||
static float grad( int hash, float x );
|
||||
static float grad( int hash, float x, float y );
|
||||
static double grad( int hash, double x );
|
||||
static double grad( int hash, double x, double y );
|
||||
|
||||
};
|
||||
|
||||
+208
-73
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
+16
-8
@@ -143,14 +143,6 @@ enum DoneAction
|
||||
|
||||
static DoneAction runlove(int argc, char **argv, int &retval, love::Variant &restartvalue)
|
||||
{
|
||||
#ifdef LOVE_LEGENDARY_APP_ARGV_HACK
|
||||
int hack_argc = 0;
|
||||
char **hack_argv = 0;
|
||||
get_app_arguments(argc, argv, hack_argc, hack_argv);
|
||||
argc = hack_argc;
|
||||
argv = hack_argv;
|
||||
#endif // LOVE_LEGENDARY_APP_ARGV_HACK
|
||||
|
||||
// Oh, you just want the version? Okay!
|
||||
if (argc > 1 && strcmp(argv[1], "--version") == 0)
|
||||
{
|
||||
@@ -167,6 +159,22 @@ static DoneAction runlove(int argc, char **argv, int &retval, love::Variant &res
|
||||
lua_State *L = luaL_newstate();
|
||||
luaL_openlibs(L);
|
||||
|
||||
// LuaJIT-specific setup needs to be done as early as possible - before
|
||||
// get_app_arguments because that loads external library code. This is also
|
||||
// loaded inside require("love"). Note that it doesn't use the love table.
|
||||
love_preload(L, luaopen_love_jitsetup, "love.jitsetup");
|
||||
lua_getglobal(L, "require");
|
||||
lua_pushstring(L, "love.jitsetup");
|
||||
lua_call(L, 1, 0);
|
||||
|
||||
#ifdef LOVE_LEGENDARY_APP_ARGV_HACK
|
||||
int hack_argc = 0;
|
||||
char **hack_argv = nullptr;
|
||||
get_app_arguments(argc, argv, hack_argc, hack_argv);
|
||||
argc = hack_argc;
|
||||
argv = hack_argv;
|
||||
#endif // LOVE_LEGENDARY_APP_ARGV_HACK
|
||||
|
||||
// Add love to package.preload for easy requiring.
|
||||
love_preload(L, luaopen_love, "love");
|
||||
|
||||
|
||||
@@ -31,8 +31,19 @@ ByteData *luax_checkbytedata(lua_State *L, int idx)
|
||||
return luax_checktype<ByteData>(L, idx);
|
||||
}
|
||||
|
||||
int w_ByteData_clone(lua_State *L)
|
||||
{
|
||||
ByteData *t = luax_checkbytedata(L, 1);
|
||||
ByteData *c = nullptr;
|
||||
luax_catchexcept(L, [&](){ c = t->clone(); });
|
||||
luax_pushtype(L, c);
|
||||
c->release();
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const luaL_Reg w_ByteData_functions[] =
|
||||
{
|
||||
{ "clone", w_ByteData_clone },
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
|
||||
@@ -26,13 +26,24 @@ namespace love
|
||||
namespace data
|
||||
{
|
||||
|
||||
DataView *luax_checkdataView(lua_State *L, int idx)
|
||||
DataView *luax_checkdataview(lua_State *L, int idx)
|
||||
{
|
||||
return luax_checktype<DataView>(L, idx);
|
||||
}
|
||||
|
||||
int w_DataView_clone(lua_State *L)
|
||||
{
|
||||
DataView *t = luax_checkdataview(L, 1);
|
||||
DataView *c = nullptr;
|
||||
luax_catchexcept(L, [&](){ c = t->clone(); });
|
||||
luax_pushtype(L, c);
|
||||
c->release();
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const luaL_Reg w_DataView_functions[] =
|
||||
{
|
||||
{ "clone", w_DataView_clone },
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
|
||||
@@ -180,7 +180,7 @@ extern "C" int luaopen_love_event(lua_State *L)
|
||||
|
||||
int ret = luax_register_module(L, w);
|
||||
|
||||
if (luaL_loadbuffer(L, (const char *)event_lua, sizeof(event_lua), "wrap_Event.lua") == 0)
|
||||
if (luaL_loadbuffer(L, (const char *)event_lua, sizeof(event_lua), "=[love \"wrap_Event.lua\"]") == 0)
|
||||
lua_call(L, 0, 0);
|
||||
else
|
||||
lua_error(L);
|
||||
|
||||
@@ -122,7 +122,7 @@ static bool getContainingDirectory(const std::string &path, std::string &newpath
|
||||
newpath = path.substr(0, index);
|
||||
|
||||
// Bail if the root has been stripped out.
|
||||
return newpath.find("/\\") != std::string::npos;
|
||||
return newpath.find_first_of("/\\") != std::string::npos;
|
||||
}
|
||||
|
||||
static bool createDirectoryRaw(const std::string &path)
|
||||
|
||||
@@ -240,15 +240,13 @@ bool Filesystem::setSource(const char *source)
|
||||
|
||||
PHYSFS_Io *gameLoveIO;
|
||||
bool hasFusedGame = love::android::checkFusedGame((void **) &gameLoveIO);
|
||||
bool isAAssetMounted = false;
|
||||
|
||||
if (hasFusedGame)
|
||||
{
|
||||
if (gameLoveIO)
|
||||
{
|
||||
// Actually we should just be able to mount gameLoveIO, but that's experimental.
|
||||
gameLoveIO->destroy(gameLoveIO);
|
||||
goto oldschool;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!love::android::initializeVirtualArchive())
|
||||
@@ -256,11 +254,15 @@ bool Filesystem::setSource(const char *source)
|
||||
SDL_Log("Unable to mount AAsset: %s", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
|
||||
return false;
|
||||
}
|
||||
|
||||
// See love::android::initializeVirtualArchive()
|
||||
new_search_path = "ASET.AASSET";
|
||||
isAAssetMounted = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
if (!isAAssetMounted)
|
||||
{
|
||||
oldschool:
|
||||
new_search_path = love::android::getSelectedGameFile();
|
||||
|
||||
// try mounting first, if that fails, load to memory and mount
|
||||
|
||||
@@ -29,6 +29,10 @@
|
||||
|
||||
#include "physfs/Filesystem.h"
|
||||
|
||||
#ifdef LOVE_ANDROID
|
||||
#include "common/android.h"
|
||||
#endif
|
||||
|
||||
// SDL
|
||||
#include <SDL_loadso.h>
|
||||
|
||||
@@ -380,11 +384,22 @@ int w_newFileData(lua_State *L)
|
||||
}
|
||||
|
||||
size_t length = 0;
|
||||
const char *str = luaL_checklstring(L, 1, &length);
|
||||
const void *ptr = nullptr;
|
||||
if (luax_istype(L, 1, Data::type))
|
||||
{
|
||||
Data *data = data::luax_checkdata(L, 1);
|
||||
ptr = data->getData();
|
||||
length = data->getSize();
|
||||
}
|
||||
else if (lua_isstring(L, 1))
|
||||
ptr = luaL_checklstring(L, 1, &length);
|
||||
else
|
||||
return luaL_argerror(L, 1, "string or Data expected");
|
||||
|
||||
const char *filename = luaL_checkstring(L, 2);
|
||||
|
||||
FileData *t = nullptr;
|
||||
luax_catchexcept(L, [&](){ t = instance()->newFileData(str, length, filename); });
|
||||
luax_catchexcept(L, [&](){ t = instance()->newFileData(ptr, length, filename); });
|
||||
|
||||
luax_pushtype(L, t);
|
||||
t->release();
|
||||
@@ -849,6 +864,23 @@ int extloader(lua_State *L)
|
||||
void *handle = nullptr;
|
||||
auto *inst = instance();
|
||||
|
||||
#ifdef LOVE_ANDROID
|
||||
// Specifically Android, look the library path based on getCRequirePath first
|
||||
std::string androidPath(love::android::getCRequirePath());
|
||||
|
||||
if (!androidPath.empty())
|
||||
{
|
||||
// Replace ? with just the dotted filename (not tokenized_name)
|
||||
replaceAll(androidPath, "?", filename);
|
||||
|
||||
// Load directly, don't check for existence.
|
||||
handle = SDL_LoadObject(androidPath.c_str());
|
||||
}
|
||||
|
||||
if (!handle)
|
||||
{
|
||||
#endif // LOVE_ANDROID
|
||||
|
||||
for (const std::string &el : inst->getCRequirePath())
|
||||
{
|
||||
for (const char *ext : library_extensions)
|
||||
@@ -878,6 +910,10 @@ int extloader(lua_State *L)
|
||||
break;
|
||||
}
|
||||
|
||||
#ifdef LOVE_ANDROID
|
||||
} // if (!handle)
|
||||
#endif
|
||||
|
||||
if (!handle)
|
||||
{
|
||||
lua_pushfstring(L, "\n\tno file '%s' in LOVE paths.", tokenized_name.c_str());
|
||||
|
||||
@@ -147,11 +147,30 @@ Buffer::Buffer(Graphics *gfx, const Settings &settings, const std::vector<DataDe
|
||||
|
||||
// GLSL's std430 packing rules. We also assume all matrices are
|
||||
// column-major.
|
||||
if (info.isMatrix)
|
||||
alignment = info.matrixRows * info.componentSize;
|
||||
else
|
||||
alignment = info.components * info.componentSize;
|
||||
// https://www.khronos.org/registry/OpenGL/specs/gl/glspec46.core.pdf
|
||||
|
||||
// "If the member is a column-major matrix with C columns and R rows,
|
||||
// the matrix is stored identically to an array of C column vectors
|
||||
// with R components each".
|
||||
// "If the member is a three-component vector with components
|
||||
// consuming N basic machine units, the base alignment is 4N."
|
||||
int c = info.isMatrix ? info.matrixRows : info.components;
|
||||
alignment = c == 3 ? 4 * info.componentSize : c * info.componentSize;
|
||||
|
||||
// std430 will effectively turn a floatmat3x3 into a floatmat4x3
|
||||
// because of its vec3 padding rules. For now we'd rather not
|
||||
// support those formats at all, because it's not easy for users to
|
||||
// deal with.
|
||||
if (alignment != c * info.componentSize && (decl.arrayLength > 0 || info.isMatrix))
|
||||
{
|
||||
const char *fstr = "unknown";
|
||||
getConstant(decl.format, fstr);
|
||||
throw love::Exception("Data format %s%s is not currently supported in shader storage buffers.", fstr, decl.arrayLength > 0 ? " array" : "");
|
||||
}
|
||||
|
||||
// "If the member is a structure, the base alignment of the structure
|
||||
// is N, where N is the largest base alignment value of any of its
|
||||
// members"
|
||||
structurealignment = std::max(structurealignment, alignment);
|
||||
|
||||
memberoffset = alignUp(memberoffset, alignment);
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
@@ -901,14 +901,11 @@ void Font::getWrap(const ColoredCodepoints &codepoints, float wraplimit, std::ve
|
||||
}
|
||||
|
||||
// Push the last line.
|
||||
if (!wline.cps.empty())
|
||||
{
|
||||
lines.push_back(wline);
|
||||
lines.push_back(wline);
|
||||
|
||||
// Ignore the width of any trailing spaces, for individual lines.
|
||||
if (linewidths)
|
||||
linewidths->push_back(width - widthoftrailingspace);
|
||||
}
|
||||
// Ignore the width of any trailing spaces, for individual lines.
|
||||
if (linewidths)
|
||||
linewidths->push_back(width - widthoftrailingspace);
|
||||
}
|
||||
|
||||
void Font::getWrap(const std::vector<ColoredString> &text, float wraplimit, std::vector<std::string> &lines, std::vector<int> *linewidths)
|
||||
|
||||
@@ -1526,7 +1526,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;
|
||||
@@ -1585,7 +1585,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)
|
||||
|
||||
@@ -177,7 +177,7 @@ void ParticleSystem::resetOffset()
|
||||
else
|
||||
{
|
||||
Quad::Viewport v = quads[0]->getViewport();
|
||||
offset = love::Vector2(v.x*0.5f, v.y*0.5f);
|
||||
offset = love::Vector2(v.w*0.5f, v.h*0.5f);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -896,14 +896,14 @@ bool Shader::validateInternal(StrongRef<ShaderStage> stages[], std::string &err,
|
||||
const glslang::TTypeList *structure = type->getStruct();
|
||||
if (structure == nullptr || structure->size() != 1)
|
||||
{
|
||||
err = "Shader validation error:\nStorage Buffer block '" + info.name + "' must contain a single unsized array of structs.";
|
||||
err = "Shader validation error:\nStorage Buffer block '" + info.name + "' must contain a single unsized array of base types or structs.";
|
||||
return false;
|
||||
}
|
||||
|
||||
const glslang::TType* structtype = (*structure)[0].type;
|
||||
if (structtype == nullptr || structtype->getBasicType() != glslang::EbtStruct || !structtype->isUnsizedArray())
|
||||
const glslang::TType* elementtype = (*structure)[0].type;
|
||||
if (elementtype == nullptr || !elementtype->isUnsizedArray())
|
||||
{
|
||||
err = "Shader validation error:\nStorage Buffer block '" + info.name + "' must contain a single unsized array of structs.";
|
||||
err = "Shader validation error:\nStorage Buffer block '" + info.name + "' must contain a single unsized array of base types or structs.";
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -166,6 +166,12 @@ void Shader::mapActiveUniforms()
|
||||
u.storageTextureFormat = reflectionit->second.format;
|
||||
u.access = reflectionit->second.access;
|
||||
}
|
||||
else
|
||||
{
|
||||
// No reflection info - maybe glslang was better at detecting
|
||||
// dead code than the driver's compiler?
|
||||
continue;
|
||||
}
|
||||
|
||||
StorageTextureBinding binding = {};
|
||||
binding.gltexture = gl.getDefaultTexture(u.textureType, u.dataBaseType);
|
||||
@@ -352,6 +358,7 @@ void Shader::mapActiveUniforms()
|
||||
glGetProgramInterfaceiv(program, GL_SHADER_STORAGE_BLOCK, GL_ACTIVE_RESOURCES, &numstoragebuffers);
|
||||
|
||||
char namebuffer[2048] = { '\0' };
|
||||
int nextstoragebufferbinding = 0;
|
||||
|
||||
for (int sindex = 0; sindex < numstoragebuffers; sindex++)
|
||||
{
|
||||
@@ -372,6 +379,12 @@ void Shader::mapActiveUniforms()
|
||||
u.bufferMemberCount = reflectionit->second.memberCount;
|
||||
u.access = reflectionit->second.access;
|
||||
}
|
||||
else
|
||||
{
|
||||
// No reflection info - maybe glslang was better at detecting
|
||||
// dead code than the driver's compiler?
|
||||
continue;
|
||||
}
|
||||
|
||||
// Make sure previously set uniform data is preserved, and shader-
|
||||
// initialized values are retrieved.
|
||||
@@ -393,8 +406,11 @@ void Shader::mapActiveUniforms()
|
||||
memset(u.buffers, 0, sizeof(Buffer*)* u.count);
|
||||
}
|
||||
|
||||
GLenum props[] = { GL_BUFFER_BINDING };
|
||||
glGetProgramResourceiv(program, GL_SHADER_STORAGE_BLOCK, sindex, 1, props, 1, nullptr, u.ints);
|
||||
// Unlike local uniforms and attributes, OpenGL doesn't auto-assign storage
|
||||
// block bindings if they're unspecified in the shader. So we overwrite them
|
||||
// regardless, here.
|
||||
u.ints[0] = nextstoragebufferbinding++;
|
||||
glShaderStorageBlockBinding(program, sindex, u.ints[0]);
|
||||
|
||||
BufferBinding binding;
|
||||
binding.bindingindex = u.ints[0];
|
||||
@@ -1074,6 +1090,9 @@ Shader::MatrixSize Shader::getMatrixSize(GLenum type) const
|
||||
m.columns = 4;
|
||||
m.rows = 3;
|
||||
break;
|
||||
default:
|
||||
m.columns = m.rows = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
return m;
|
||||
|
||||
@@ -45,7 +45,7 @@ public:
|
||||
|
||||
struct StorageTextureBinding
|
||||
{
|
||||
Texture *texture = nullptr;
|
||||
love::graphics::Texture *texture = nullptr;
|
||||
GLuint gltexture = 0;
|
||||
TextureType type = TEXTURE_2D;
|
||||
GLenum access = GL_READ_ONLY;
|
||||
|
||||
@@ -22,6 +22,8 @@
|
||||
#include "Buffer.h"
|
||||
#include "common/Data.h"
|
||||
|
||||
#include <limits>
|
||||
|
||||
namespace love
|
||||
{
|
||||
namespace graphics
|
||||
|
||||
@@ -3657,7 +3657,7 @@ extern "C" int luaopen_love_graphics(lua_State *L)
|
||||
|
||||
int n = luax_register_module(L, w);
|
||||
|
||||
if (luaL_loadbuffer(L, (const char *)graphics_lua, sizeof(graphics_lua), "wrap_Graphics.lua") == 0)
|
||||
if (luaL_loadbuffer(L, (const char *)graphics_lua, sizeof(graphics_lua), "=[love \"wrap_Graphics.lua\"]") == 0)
|
||||
lua_call(L, 0, 0);
|
||||
else
|
||||
lua_error(L);
|
||||
|
||||
@@ -447,18 +447,12 @@ int w_Shader_send(lua_State *L)
|
||||
|
||||
const Shader::UniformInfo *info = shader->getUniformInfo(name);
|
||||
if (info == nullptr)
|
||||
{
|
||||
luax_pushboolean(L, false);
|
||||
return 1;
|
||||
}
|
||||
return luaL_error(L, "Shader uniform '%s' does not exist.\nA common error is to define but not use the variable.", name);
|
||||
|
||||
if (luax_istype(L, 3, Data::type) || (info->baseType == Shader::UNIFORM_MATRIX && luax_istype(L, 4, Data::type)))
|
||||
w_Shader_sendData(L, 3, shader, info, false);
|
||||
return w_Shader_sendData(L, 3, shader, info, false);
|
||||
else
|
||||
w_Shader_sendLuaValues(L, 3, shader, info, name);
|
||||
|
||||
luax_pushboolean(L, true);
|
||||
return 1;
|
||||
return w_Shader_sendLuaValues(L, 3, shader, info, name);
|
||||
}
|
||||
|
||||
int w_Shader_sendColors(lua_State *L)
|
||||
|
||||
@@ -168,7 +168,7 @@ int luaopen_video(lua_State *L)
|
||||
{
|
||||
int ret = luax_register_type(L, &Video::type, functions, nullptr);
|
||||
|
||||
luaL_loadbuffer(L, video_lua, sizeof(video_lua), "Video.lua");
|
||||
luaL_loadbuffer(L, video_lua, sizeof(video_lua), "=[love \"Video.lua\"]");
|
||||
luax_gettypemetatable(L, Video::type);
|
||||
lua_call(L, 1, 0);
|
||||
|
||||
|
||||
@@ -440,6 +440,11 @@ bool Joystick::checkCreateHaptic()
|
||||
|
||||
bool Joystick::isVibrationSupported()
|
||||
{
|
||||
#if SDL_VERSION_ATLEAST(2, 0, 18)
|
||||
if (isConnected() && SDL_JoystickHasRumble(joyhandle) == SDL_TRUE)
|
||||
return true;
|
||||
#endif
|
||||
|
||||
if (!checkCreateHaptic())
|
||||
return false;
|
||||
|
||||
@@ -490,8 +495,12 @@ bool Joystick::setVibration(float left, float right, float duration)
|
||||
if (left == 0.0f && right == 0.0f)
|
||||
return setVibration();
|
||||
|
||||
if (!checkCreateHaptic())
|
||||
if (!isConnected())
|
||||
{
|
||||
vibration.left = vibration.right = 0.0f;
|
||||
vibration.endtime = SDL_HAPTIC_INFINITY;
|
||||
return false;
|
||||
}
|
||||
|
||||
Uint32 length = SDL_HAPTIC_INFINITY;
|
||||
if (duration >= 0.0f)
|
||||
@@ -501,10 +510,19 @@ bool Joystick::setVibration(float left, float right, float duration)
|
||||
}
|
||||
|
||||
bool success = false;
|
||||
|
||||
#if SDL_VERSION_ATLEAST(2, 0, 9)
|
||||
if (SDL_JoystickRumble(joyhandle, (Uint16)(left * LOVE_UINT16_MAX), (Uint16)(right * LOVE_UINT16_MAX), length) == 0)
|
||||
success = true;
|
||||
#endif
|
||||
|
||||
if (!success && !checkCreateHaptic())
|
||||
return false;
|
||||
|
||||
unsigned int features = SDL_HapticQuery(haptic);
|
||||
int axes = SDL_HapticNumAxes(haptic);
|
||||
|
||||
if ((features & SDL_HAPTIC_LEFTRIGHT) != 0)
|
||||
if (!success && (features & SDL_HAPTIC_LEFTRIGHT) != 0)
|
||||
{
|
||||
memset(&vibration.effect, 0, sizeof(SDL_HapticEffect));
|
||||
vibration.effect.type = SDL_HAPTIC_LEFTRIGHT;
|
||||
@@ -576,9 +594,14 @@ bool Joystick::setVibration(float left, float right, float duration)
|
||||
|
||||
bool Joystick::setVibration()
|
||||
{
|
||||
bool success = true;
|
||||
bool success = false;
|
||||
|
||||
if (SDL_WasInit(SDL_INIT_HAPTIC) && haptic && SDL_HapticIndex(haptic) != -1)
|
||||
#if SDL_VERSION_ATLEAST(2, 0, 9)
|
||||
if (!success)
|
||||
success = isConnected() && SDL_JoystickRumble(joyhandle, 0, 0, 0) == 0;
|
||||
#endif
|
||||
|
||||
if (!success && SDL_WasInit(SDL_INIT_HAPTIC) && haptic && SDL_HapticIndex(haptic) != -1)
|
||||
success = (SDL_HapticStopEffect(haptic, vibration.id) == 0);
|
||||
|
||||
if (success)
|
||||
|
||||
@@ -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"--"
|
||||
@@ -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"--"
|
||||
@@ -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()
|
||||
if not love.graphics.isActive() then return end
|
||||
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!"
|
||||
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"--"
|
||||
@@ -0,0 +1,71 @@
|
||||
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.
|
||||
--]]
|
||||
|
||||
if type(jit) ~= "table" or not jit.status() then
|
||||
return
|
||||
end
|
||||
|
||||
-- Double the defaults.
|
||||
jit.opt.start("maxtrace=2000", "maxrecord=8000")
|
||||
|
||||
-- Somewhat arbitrary value. Needs to be higher than the combined sizes below,
|
||||
-- and higher than the default (512) because that's already too low.
|
||||
jit.opt.start("maxmcode=16384")
|
||||
|
||||
if jit.arch == "arm64" then
|
||||
-- https://github.com/LuaJIT/LuaJIT/issues/285
|
||||
-- LuaJIT 2.1 on arm64 currently (as of commit b4b2dce) can only use memory
|
||||
-- for JIT compilation within a certain short range. Other libraries such as
|
||||
-- SDL can take all the usable space in that range and cause attempts at JIT
|
||||
-- compilation to both fail and take a long time.
|
||||
-- This is a very hacky attempt at a workaround. LuaJIT allocates executable
|
||||
-- code in pools. We'll try "reserving" pools before any external code is
|
||||
-- executed, by causing JIT compilation via a small loop. We can't easily
|
||||
-- tell if JIT compilation succeeded, so we do several successively smaller
|
||||
-- pool allocations in case previous ones fail.
|
||||
-- This is a really hacky hack and by no means foolproof - there are a lot of
|
||||
-- potential situations (especially when threads are used) where previously
|
||||
-- executed external code will still take up space that LuaJIT needed for itself.
|
||||
|
||||
jit.opt.start("sizemcode=2048")
|
||||
for i=1, 100 do end
|
||||
|
||||
jit.opt.start("sizemcode=1024")
|
||||
for i=1, 100 do end
|
||||
|
||||
jit.opt.start("sizemcode=512")
|
||||
for i=1, 100 do end
|
||||
|
||||
jit.opt.start("sizemcode=256")
|
||||
for i=1, 100 do end
|
||||
|
||||
jit.opt.start("sizemcode=128")
|
||||
for i=1, 100 do end
|
||||
else
|
||||
-- Somewhat arbitrary value (>= the default).
|
||||
jit.opt.start("sizemcode=128")
|
||||
end
|
||||
|
||||
-- DO NOT REMOVE THE NEXT LINE. It is used to load this file as a C++ string.
|
||||
--)luastring"--"
|
||||
+98
-11
@@ -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,10 +33,20 @@
|
||||
|
||||
#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
|
||||
#include <SDL.h>
|
||||
extern "C"
|
||||
{
|
||||
#include "luajit.h"
|
||||
}
|
||||
#endif // LOVE_ANDROID
|
||||
|
||||
#ifdef LOVE_LEGENDARY_CONSOLE_IO_HACK
|
||||
@@ -75,9 +86,25 @@
|
||||
# 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"
|
||||
;
|
||||
|
||||
static const char jit_setup_lua[] =
|
||||
#include "jitsetup.lua"
|
||||
;
|
||||
|
||||
// All modules define a c-accessible luaopen
|
||||
// so let's make use of those, instead
|
||||
@@ -142,6 +169,9 @@ extern "C"
|
||||
extern int luaopen_love_window(lua_State*);
|
||||
#endif
|
||||
extern int luaopen_love_nogame(lua_State*);
|
||||
extern int luaopen_love_jitsetup(lua_State*);
|
||||
extern int luaopen_love_arg(lua_State*);
|
||||
extern int luaopen_love_callbacks(lua_State*);
|
||||
extern int luaopen_love_boot(lua_State*);
|
||||
}
|
||||
|
||||
@@ -204,6 +234,9 @@ static const luaL_Reg modules[] = {
|
||||
{ "love.window", luaopen_love_window },
|
||||
#endif
|
||||
{ "love.nogame", luaopen_love_nogame },
|
||||
{ "love.jitsetup", luaopen_love_jitsetup },
|
||||
{ "love.arg", luaopen_love_arg },
|
||||
{ "love.callbacks", luaopen_love_callbacks },
|
||||
{ "love.boot", luaopen_love_boot },
|
||||
{ 0, 0 }
|
||||
};
|
||||
@@ -413,6 +446,15 @@ static void luax_addcompatibilityalias(lua_State *L, const char *module, const c
|
||||
|
||||
int luaopen_love(lua_State *L)
|
||||
{
|
||||
// Preload module loaders.
|
||||
for (int i = 0; modules[i].name != nullptr; i++)
|
||||
love::luax_preload(L, modules[i].func, modules[i].name);
|
||||
|
||||
// jitsetup is also loaded in the love executable runlove function. It's
|
||||
// needed here too for threads. Note that it doesn't use the love table.
|
||||
love::luax_require(L, "love.jitsetup");
|
||||
lua_pop(L, 1);
|
||||
|
||||
love::luax_insistpinnedthread(L);
|
||||
|
||||
love::luax_insistglobal(L, "love");
|
||||
@@ -432,6 +474,7 @@ int luaopen_love(lua_State *L)
|
||||
lua_setfield(L, -2, "_version_codename");
|
||||
|
||||
#ifdef LOVE_ANDROID
|
||||
luaJIT_setmode(L, 0, LUAJIT_MODE_ENGINE | LUAJIT_MODE_OFF);
|
||||
lua_register(L, "print", w_print_sdl_log);
|
||||
#endif
|
||||
|
||||
@@ -515,10 +558,6 @@ int luaopen_love(lua_State *L)
|
||||
lua_setfield(L, -2, "hasDeprecationOutput");
|
||||
}
|
||||
|
||||
// Preload module loaders.
|
||||
for (int i = 0; modules[i].name != nullptr; i++)
|
||||
love::luax_preload(L, modules[i].func, modules[i].name);
|
||||
|
||||
// Necessary for Data-creating methods to work properly in Data subclasses.
|
||||
love::luax_require(L, "love.data");
|
||||
lua_pop(L, 1);
|
||||
@@ -540,6 +579,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;
|
||||
}
|
||||
|
||||
@@ -638,7 +704,31 @@ int w__setAccelerometerAsJoystick(lua_State *L)
|
||||
|
||||
int luaopen_love_nogame(lua_State *L)
|
||||
{
|
||||
if (luaL_loadbuffer(L, (const char *)love::nogame_lua, sizeof(love::nogame_lua), "nogame.lua") == 0)
|
||||
if (luaL_loadbuffer(L, (const char *)love::nogame_lua, sizeof(love::nogame_lua), "=[love \"nogame.lua\"]") == 0)
|
||||
lua_call(L, 0, 1);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int luaopen_love_jitsetup(lua_State *L)
|
||||
{
|
||||
if (luaL_loadbuffer(L, jit_setup_lua, sizeof(jit_setup_lua), "=[love \"jitsetup.lua\"]") == 0)
|
||||
lua_call(L, 0, 1);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int luaopen_love_arg(lua_State *L)
|
||||
{
|
||||
if (luaL_loadbuffer(L, arg_lua, sizeof(arg_lua), "=[love \"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), "=[love \"callbacks.lua\"]") == 0)
|
||||
lua_call(L, 0, 1);
|
||||
|
||||
return 1;
|
||||
@@ -646,11 +736,8 @@ int luaopen_love_nogame(lua_State *L)
|
||||
|
||||
int luaopen_love_boot(lua_State *L)
|
||||
{
|
||||
if (luaL_loadbuffer(L, (const char *)love::boot_lua, sizeof(love::boot_lua), "boot.lua") == 0)
|
||||
if (luaL_loadbuffer(L, boot_lua, sizeof(boot_lua), "=[love \"boot.lua\"]") == 0)
|
||||
lua_call(L, 0, 1);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -36,6 +36,9 @@ LOVE_EXPORT const char *love_version();
|
||||
LOVE_EXPORT const char *love_codename();
|
||||
LOVE_EXPORT int luaopen_love(lua_State *L);
|
||||
LOVE_EXPORT int luaopen_love_nogame(lua_State *L);
|
||||
LOVE_EXPORT int luaopen_love_jitsetup(lua_State *L);
|
||||
LOVE_EXPORT int luaopen_love_arg(lua_State *L);
|
||||
LOVE_EXPORT int luaopen_love_callbacks(lua_State *L);
|
||||
LOVE_EXPORT int luaopen_love_boot(lua_State *L);
|
||||
|
||||
#ifdef LOVE_LEGENDARY_CONSOLE_IO_HACK
|
||||
|
||||
@@ -83,10 +83,10 @@ float linearToGamma(float c);
|
||||
*
|
||||
* @return Noise value in the range of [0, 1].
|
||||
**/
|
||||
static float noise1(float x);
|
||||
static float noise2(float x, float y);
|
||||
static float noise3(float x, float y, float z);
|
||||
static float noise4(float x, float y, float z, float w);
|
||||
static float noise1(double x);
|
||||
static float noise2(double x, double y);
|
||||
static float noise3(double x, double y, double z);
|
||||
static float noise4(double x, double y, double z, double w);
|
||||
|
||||
|
||||
class Math : public Module
|
||||
@@ -132,12 +132,12 @@ private:
|
||||
}; // Math
|
||||
|
||||
|
||||
static inline float noise1(float x)
|
||||
static inline float noise1(double x)
|
||||
{
|
||||
return SimplexNoise1234::noise(x) * 0.5f + 0.5f;
|
||||
}
|
||||
|
||||
static inline float noise2(float x, float y)
|
||||
static inline float noise2(double x, double y)
|
||||
{
|
||||
return SimplexNoise1234::noise(x, y) * 0.5f + 0.5f;
|
||||
}
|
||||
@@ -145,12 +145,12 @@ static inline float noise2(float x, float y)
|
||||
// Perlin noise is used instead of Simplex noise in the 3D and 4D cases to avoid
|
||||
// patent issues.
|
||||
|
||||
static inline float noise3(float x, float y, float z)
|
||||
static inline float noise3(double x, double y, double z)
|
||||
{
|
||||
return Noise1234::noise(x, y, z) * 0.5f + 0.5f;
|
||||
}
|
||||
|
||||
static inline float noise4(float x, float y, float z, float w)
|
||||
static inline float noise4(double x, double y, double z, double w)
|
||||
{
|
||||
return Noise1234::noise(x, y, z, w) * 0.5f + 0.5f;
|
||||
}
|
||||
|
||||
@@ -319,10 +319,10 @@ int w_linearToGamma(lua_State *L)
|
||||
int w_noise(lua_State *L)
|
||||
{
|
||||
int nargs = std::min(std::max(lua_gettop(L), 1), 4);
|
||||
float args[4];
|
||||
double args[4];
|
||||
|
||||
for (int i = 0; i < nargs; i++)
|
||||
args[i] = (float) luaL_checknumber(L, i + 1);
|
||||
args[i] = luaL_checknumber(L, i + 1);
|
||||
|
||||
float val = 0.0f;
|
||||
|
||||
@@ -349,10 +349,10 @@ int w_noise(lua_State *L)
|
||||
// C functions in a struct, necessary for the FFI versions of math functions.
|
||||
struct FFI_Math
|
||||
{
|
||||
float (*noise1)(float x);
|
||||
float (*noise2)(float x, float y);
|
||||
float (*noise3)(float x, float y, float z);
|
||||
float (*noise4)(float x, float y, float z, float w);
|
||||
float (*noise1)(double x);
|
||||
float (*noise2)(double x, double y);
|
||||
float (*noise3)(double x, double y, double z);
|
||||
float (*noise4)(double x, double y, double z, double w);
|
||||
|
||||
float (*gammaToLinear)(float c);
|
||||
float (*linearToGamma)(float c);
|
||||
@@ -415,7 +415,7 @@ extern "C" int luaopen_love_math(lua_State *L)
|
||||
int n = luax_register_module(L, w);
|
||||
|
||||
// Execute wrap_Math.lua, sending the math table and ffifuncs pointer as args.
|
||||
luaL_loadbuffer(L, math_lua, sizeof(math_lua), "wrap_Math.lua");
|
||||
luaL_loadbuffer(L, math_lua, sizeof(math_lua), "=[love \"wrap_Math.lua\"]");
|
||||
lua_pushvalue(L, -2);
|
||||
luax_pushpointerasstring(L, &ffifuncs);
|
||||
lua_call(L, 2, 0);
|
||||
|
||||
@@ -93,10 +93,10 @@ if not status then return end
|
||||
pcall(ffi.cdef, [[
|
||||
typedef struct FFI_Math
|
||||
{
|
||||
float (*noise1)(float x);
|
||||
float (*noise2)(float x, float y);
|
||||
float (*noise3)(float x, float y, float z);
|
||||
float (*noise4)(float x, float y, float z, float w);
|
||||
float (*noise1)(double x);
|
||||
float (*noise2)(double x, double y);
|
||||
float (*noise3)(double x, double y, double z);
|
||||
float (*noise4)(double x, double y, double z, double w);
|
||||
|
||||
float (*gammaToLinear)(float c);
|
||||
float (*linearToGamma)(float c);
|
||||
|
||||
@@ -101,8 +101,9 @@ float RevoluteJoint::getMotorSpeed() const
|
||||
return joint->GetMotorSpeed();
|
||||
}
|
||||
|
||||
float RevoluteJoint::getMotorTorque(float inv_dt) const
|
||||
float RevoluteJoint::getMotorTorque(float dt) const
|
||||
{
|
||||
float inv_dt = 1.0f / dt;
|
||||
return Physics::scaleUp(Physics::scaleUp(joint->GetMotorTorque(inv_dt)));
|
||||
}
|
||||
|
||||
|
||||
@@ -87,9 +87,9 @@ public:
|
||||
|
||||
/**
|
||||
* Get the current motor torque, usually in N-m.
|
||||
* @param inv_dt The inverse timestep.
|
||||
* @param dt The timestep.
|
||||
**/
|
||||
float getMotorTorque(float inv_dt) const;
|
||||
float getMotorTorque(float dt) const;
|
||||
|
||||
/**
|
||||
* Get the maximum motor torque, usually in N-m.
|
||||
|
||||
@@ -91,8 +91,8 @@ int w_RevoluteJoint_getMotorSpeed(lua_State *L)
|
||||
int w_RevoluteJoint_getMotorTorque(lua_State *L)
|
||||
{
|
||||
RevoluteJoint *t = luax_checkrevolutejoint(L, 1);
|
||||
float inv_dt = (float)luaL_checknumber(L, 2);
|
||||
lua_pushnumber(L, t->getMotorTorque(inv_dt));
|
||||
float dt = (float)luaL_checknumber(L, 2);
|
||||
lua_pushnumber(L, t->getMotorTorque(dt));
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ OggDemuxer::~OggDemuxer()
|
||||
ogg_sync_clear(&sync);
|
||||
}
|
||||
|
||||
void OggDemuxer::readPage()
|
||||
bool OggDemuxer::readPage(bool erroreof)
|
||||
{
|
||||
char *syncBuffer = nullptr;
|
||||
while (ogg_sync_pageout(&sync, &page) != 1)
|
||||
@@ -53,8 +53,13 @@ void OggDemuxer::readPage()
|
||||
|
||||
syncBuffer = ogg_sync_buffer(&sync, 8192);
|
||||
size_t read = file->read(syncBuffer, 8192);
|
||||
if (read == 0 && erroreof)
|
||||
return false;
|
||||
|
||||
ogg_sync_wrote(&sync, read);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OggDemuxer::readPacket(ogg_packet &packet, bool mustSucceed)
|
||||
@@ -118,22 +123,25 @@ OggDemuxer::StreamType OggDemuxer::findStream()
|
||||
if (streamInited)
|
||||
{
|
||||
eos = false;
|
||||
streamInited = false;
|
||||
file->seek(0);
|
||||
ogg_stream_clear(&stream);
|
||||
ogg_sync_reset(&sync);
|
||||
}
|
||||
|
||||
streamInited = true;
|
||||
while (true)
|
||||
{
|
||||
if (!readPage(true))
|
||||
return TYPE_UNKNOWN;
|
||||
|
||||
// If this page isn't at the start of a stream, we've seen all streams
|
||||
readPage();
|
||||
if (!ogg_page_bos(&page))
|
||||
break;
|
||||
|
||||
videoSerial = ogg_page_serialno(&page);
|
||||
ogg_stream_init(&stream, videoSerial);
|
||||
ogg_stream_pagein(&stream, &page);
|
||||
streamInited = true;
|
||||
|
||||
StreamType type = determineType();
|
||||
switch(type)
|
||||
@@ -145,10 +153,15 @@ OggDemuxer::StreamType OggDemuxer::findStream()
|
||||
}
|
||||
|
||||
ogg_stream_clear(&stream);
|
||||
streamInited = false;
|
||||
}
|
||||
|
||||
if (streamInited)
|
||||
{
|
||||
streamInited = false;
|
||||
ogg_stream_clear(&stream);
|
||||
}
|
||||
|
||||
streamInited = false;
|
||||
ogg_stream_clear(&stream);
|
||||
ogg_sync_reset(&sync);
|
||||
|
||||
return TYPE_UNKNOWN;
|
||||
|
||||
@@ -67,7 +67,7 @@ private:
|
||||
int videoSerial;
|
||||
bool eos;
|
||||
|
||||
void readPage();
|
||||
bool readPage(bool erroreof = false);
|
||||
StreamType determineType();
|
||||
}; // OggDemuxer
|
||||
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user