mirror of
https://github.com/love2d/love-android.git
synced 2026-08-17 19:24:07 +02:00
using latest love-android @ changeset 8659be0e75a3
This commit is contained in:
@@ -21,6 +21,15 @@
|
|||||||
#ifndef LOVE_INT_H
|
#ifndef LOVE_INT_H
|
||||||
#define LOVE_INT_H
|
#define LOVE_INT_H
|
||||||
|
|
||||||
|
// fixes compile errors of the form:
|
||||||
|
// "error: expected unqualified-id before '__extension__'"
|
||||||
|
#ifdef LOVE_ANDROID
|
||||||
|
#include <sys/endian.h>
|
||||||
|
#undef swap16
|
||||||
|
#undef swap32
|
||||||
|
#undef swap64
|
||||||
|
#endif
|
||||||
|
|
||||||
// C standard sized integer types.
|
// C standard sized integer types.
|
||||||
// This header was added to Visual studio in VS 2012, which is LOVE's current
|
// This header was added to Visual studio in VS 2012, which is LOVE's current
|
||||||
// minimum supported VS version (as of this comment's commit date.)
|
// minimum supported VS version (as of this comment's commit date.)
|
||||||
@@ -47,6 +56,27 @@ typedef uint32_t uint32;
|
|||||||
typedef int64_t int64;
|
typedef int64_t int64;
|
||||||
typedef uint64_t uint64;
|
typedef uint64_t uint64;
|
||||||
|
|
||||||
|
static inline uint16 swap16(uint16 x)
|
||||||
|
{
|
||||||
|
return (x >> 8) | (x << 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint32 swap32(uint32 x)
|
||||||
|
{
|
||||||
|
return ((x & 0x000000FF) << 24) |
|
||||||
|
((x & 0x0000FF00) << 8) |
|
||||||
|
((x & 0x00FF0000) >> 8) |
|
||||||
|
((x & 0xFF000000) >> 24);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint64 swap64(uint64 x)
|
||||||
|
{
|
||||||
|
return ((x << 56) & 0xFF00000000000000ULL) | ((x << 40) & 0x00FF000000000000ULL) |
|
||||||
|
((x << 24) & 0x0000FF0000000000ULL) | ((x << 8) & 0x000000FF00000000ULL) |
|
||||||
|
((x >> 8) & 0x00000000FF000000ULL) | ((x >> 24) & 0x0000000000FF0000ULL) |
|
||||||
|
((x >> 40) & 0x000000000000FF00ULL) | ((x >> 56) & 0x00000000000000FFULL);
|
||||||
|
}
|
||||||
|
|
||||||
} // love
|
} // love
|
||||||
|
|
||||||
#endif // LOVE_INT_H
|
#endif // LOVE_INT_H
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ Audio::PoolThread::PoolThread(Pool *pool)
|
|||||||
, finish(false)
|
, finish(false)
|
||||||
{
|
{
|
||||||
mutex = thread::newMutex();
|
mutex = thread::newMutex();
|
||||||
|
threadName = "AudioPool";
|
||||||
}
|
}
|
||||||
|
|
||||||
Audio::PoolThread::~PoolThread()
|
Audio::PoolThread::~PoolThread()
|
||||||
|
|||||||
@@ -255,10 +255,7 @@ bool Source::update()
|
|||||||
|
|
||||||
alGetSourcef(source, AL_SAMPLE_OFFSET, &curOffsetSamples);
|
alGetSourcef(source, AL_SAMPLE_OFFSET, &curOffsetSamples);
|
||||||
|
|
||||||
ALint b;
|
int freq = decoder->getSampleRate();
|
||||||
alGetSourcei(source, AL_BUFFER, &b);
|
|
||||||
int freq;
|
|
||||||
alGetBufferi(b, AL_FREQUENCY, &freq);
|
|
||||||
curOffsetSecs = curOffsetSamples / freq;
|
curOffsetSecs = curOffsetSamples / freq;
|
||||||
|
|
||||||
// Get a free buffer.
|
// Get a free buffer.
|
||||||
@@ -334,11 +331,7 @@ void Source::seekAtomic(float offset, void *unit)
|
|||||||
if (type == TYPE_STREAM)
|
if (type == TYPE_STREAM)
|
||||||
{
|
{
|
||||||
offsetSamples = offset;
|
offsetSamples = offset;
|
||||||
ALint buffer;
|
offset /= decoder->getSampleRate();
|
||||||
alGetSourcei(source, AL_BUFFER, &buffer);
|
|
||||||
int freq;
|
|
||||||
alGetBufferi(buffer, AL_FREQUENCY, &freq);
|
|
||||||
offset /= freq;
|
|
||||||
offsetSeconds = offset;
|
offsetSeconds = offset;
|
||||||
decoder->seek(offset);
|
decoder->seek(offset);
|
||||||
}
|
}
|
||||||
@@ -353,11 +346,7 @@ void Source::seekAtomic(float offset, void *unit)
|
|||||||
{
|
{
|
||||||
offsetSeconds = offset;
|
offsetSeconds = offset;
|
||||||
decoder->seek(offset);
|
decoder->seek(offset);
|
||||||
ALint buffer;
|
offsetSamples = offset * decoder->getSampleRate();
|
||||||
alGetSourcei(source, AL_BUFFER, &buffer);
|
|
||||||
int freq;
|
|
||||||
alGetBufferi(buffer, AL_FREQUENCY, &freq);
|
|
||||||
offsetSamples = offset*freq;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -399,11 +388,7 @@ float Source::tellAtomic(void *unit) const
|
|||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
alGetSourcef(source, AL_SAMPLE_OFFSET, &offset);
|
alGetSourcef(source, AL_SAMPLE_OFFSET, &offset);
|
||||||
ALint buffer;
|
offset /= decoder->getSampleRate();
|
||||||
alGetSourcei(source, AL_BUFFER, &buffer);
|
|
||||||
int freq;
|
|
||||||
alGetBufferi(buffer, AL_FREQUENCY, &freq);
|
|
||||||
offset /= freq;
|
|
||||||
if (type == TYPE_STREAM) offset += offsetSeconds;
|
if (type == TYPE_STREAM) offset += offsetSeconds;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -59,6 +59,34 @@ static void windowToPixelCoords(int *x, int *y)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SDL's event watch callbacks trigger when the event is actually posted inside
|
||||||
|
// SDL, unlike with SDL_PollEvents. This is useful for some events which require
|
||||||
|
// handling inside the function which triggered them on some backends.
|
||||||
|
static int SDLCALL watchAppEvents(void * /*udata*/, SDL_Event *event)
|
||||||
|
{
|
||||||
|
graphics::Graphics *gfx = (graphics::Graphics *) Module::findInstance("love.graphics.");
|
||||||
|
|
||||||
|
switch (event->type)
|
||||||
|
{
|
||||||
|
// On iOS, calling any OpenGL ES function after the function which triggers
|
||||||
|
// SDL_APP_DIDENTERBACKGROUND is called will kill the app, so we handle it
|
||||||
|
// with an event watch callback, which will be called inside that function.
|
||||||
|
case SDL_APP_DIDENTERBACKGROUND:
|
||||||
|
case SDL_APP_WILLENTERFOREGROUND:
|
||||||
|
// On Android, before calling setActive we should probably also call a
|
||||||
|
// window function to un-set the graphics mode and delete the context,
|
||||||
|
// after DIDENTERBACKGROUND (and the reverse after WILLENTERFOREGROUND.)
|
||||||
|
if (gfx)
|
||||||
|
gfx->setActive(event->type == SDL_APP_WILLENTERFOREGROUND);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't prevent the event from being processed further.
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
const char *Event::getName() const
|
const char *Event::getName() const
|
||||||
{
|
{
|
||||||
@@ -69,10 +97,13 @@ Event::Event()
|
|||||||
{
|
{
|
||||||
if (SDL_InitSubSystem(SDL_INIT_EVENTS) < 0)
|
if (SDL_InitSubSystem(SDL_INIT_EVENTS) < 0)
|
||||||
throw love::Exception("%s", SDL_GetError());
|
throw love::Exception("%s", SDL_GetError());
|
||||||
|
|
||||||
|
SDL_AddEventWatch(watchAppEvents, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
Event::~Event()
|
Event::~Event()
|
||||||
{
|
{
|
||||||
|
SDL_DelEventWatch(watchAppEvents, this);
|
||||||
SDL_QuitSubSystem(SDL_INIT_EVENTS);
|
SDL_QuitSubSystem(SDL_INIT_EVENTS);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -260,6 +291,7 @@ Message *Event::convert(const SDL_Event &e) const
|
|||||||
SDL_free(e.drop.file);
|
SDL_free(e.drop.file);
|
||||||
break;
|
break;
|
||||||
case SDL_QUIT:
|
case SDL_QUIT:
|
||||||
|
case SDL_APP_TERMINATING:
|
||||||
msg = new Message("quit");
|
msg = new Message("quit");
|
||||||
break;
|
break;
|
||||||
case SDL_APP_LOWMEMORY:
|
case SDL_APP_LOWMEMORY:
|
||||||
|
|||||||
@@ -279,6 +279,11 @@ bool Filesystem::setIdentity(const char *ident, bool appendToPath)
|
|||||||
// (No error on fail, it means that the path doesn't exist).
|
// (No error on fail, it means that the path doesn't exist).
|
||||||
PHYSFS_addToSearchPath(save_path_full.c_str(), appendToPath);
|
PHYSFS_addToSearchPath(save_path_full.c_str(), appendToPath);
|
||||||
|
|
||||||
|
// HACK: This forces setupWriteDirectory to be called the next time a file
|
||||||
|
// is opened for writing - otherwise it won't be called at all if it was
|
||||||
|
// already called at least once before.
|
||||||
|
PHYSFS_setWriteDir(nullptr);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -349,16 +354,36 @@ bool Filesystem::setupWriteDirectory()
|
|||||||
if (save_identity.empty() || save_path_full.empty() || save_path_relative.empty())
|
if (save_identity.empty() || save_path_full.empty() || save_path_relative.empty())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Set the appdata folder as writable directory.
|
// We need to make sure the write directory is created. To do that, we also
|
||||||
|
// need to make sure all its parent directories are also created.
|
||||||
|
std::string temp_writedir = getDriveRoot(save_path_full);
|
||||||
|
std::string temp_createdir = skipDriveRoot(save_path_full);
|
||||||
|
|
||||||
|
// On some sandboxed platforms, physfs will break when its write directory
|
||||||
|
// is the root of the drive and it tries to create a folder (even if the
|
||||||
|
// folder's path is in a writable location.) If the user's home folder is
|
||||||
|
// in the save path, we'll try starting from there instead.
|
||||||
|
if (save_path_full.find(getUserDirectory()) == 0)
|
||||||
|
{
|
||||||
|
temp_writedir = getUserDirectory();
|
||||||
|
temp_createdir = save_path_full.substr(getUserDirectory().length());
|
||||||
|
|
||||||
|
// Strip leading '/' characters from the path we want to create.
|
||||||
|
size_t startpos = temp_createdir.find_first_not_of('/');
|
||||||
|
if (startpos != std::string::npos)
|
||||||
|
temp_createdir = temp_createdir.substr(startpos);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set either '/' or the user's home as a writable directory.
|
||||||
// (We must create the save folder before mounting it).
|
// (We must create the save folder before mounting it).
|
||||||
if (!PHYSFS_setWriteDir(getDriveRoot(save_path_full).c_str()))
|
if (!PHYSFS_setWriteDir(temp_writedir.c_str()))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Create the save folder. (We're now "at" %APPDATA%).
|
// Create the save folder. (We're now "at" either '/' or the user's home).
|
||||||
if (!createDirectory(skipDriveRoot(save_path_full).c_str()))
|
if (!createDirectory(temp_createdir.c_str()))
|
||||||
{
|
{
|
||||||
// Clear the write directory in case of error.
|
// Clear the write directory in case of error.
|
||||||
PHYSFS_setWriteDir(0);
|
PHYSFS_setWriteDir(nullptr);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -369,7 +394,7 @@ bool Filesystem::setupWriteDirectory()
|
|||||||
// Add the directory. (Will not be readded if already present).
|
// Add the directory. (Will not be readded if already present).
|
||||||
if (!PHYSFS_addToSearchPath(save_path_full.c_str(), 0))
|
if (!PHYSFS_addToSearchPath(save_path_full.c_str(), 0))
|
||||||
{
|
{
|
||||||
PHYSFS_setWriteDir(0); // Clear the write directory in case of error.
|
PHYSFS_setWriteDir(nullptr); // Clear the write directory in case of error.
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -505,12 +530,12 @@ const char *Filesystem::getWorkingDirectory()
|
|||||||
return cwd.c_str();
|
return cwd.c_str();
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *Filesystem::getUserDirectory()
|
std::string Filesystem::getUserDirectory()
|
||||||
{
|
{
|
||||||
return PHYSFS_getUserDir();
|
return std::string(PHYSFS_getUserDir());
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *Filesystem::getAppdataDirectory()
|
std::string Filesystem::getAppdataDirectory()
|
||||||
{
|
{
|
||||||
#ifdef LOVE_WINDOWS
|
#ifdef LOVE_WINDOWS
|
||||||
if (appdata.empty())
|
if (appdata.empty())
|
||||||
@@ -519,7 +544,7 @@ const char *Filesystem::getAppdataDirectory()
|
|||||||
appdata = to_utf8(w_appdata);
|
appdata = to_utf8(w_appdata);
|
||||||
replace_char(appdata, '\\', '/');
|
replace_char(appdata, '\\', '/');
|
||||||
}
|
}
|
||||||
return appdata.c_str();
|
return appdata;
|
||||||
#elif defined(LOVE_MACOSX)
|
#elif defined(LOVE_MACOSX)
|
||||||
if (appdata.empty())
|
if (appdata.empty())
|
||||||
{
|
{
|
||||||
@@ -527,7 +552,7 @@ const char *Filesystem::getAppdataDirectory()
|
|||||||
udir.append("/Library/Application Support");
|
udir.append("/Library/Application Support");
|
||||||
appdata = udir;
|
appdata = udir;
|
||||||
}
|
}
|
||||||
return appdata.c_str();
|
return appdata;
|
||||||
#elif defined(LOVE_LINUX)
|
#elif defined(LOVE_LINUX)
|
||||||
if (appdata.empty())
|
if (appdata.empty())
|
||||||
{
|
{
|
||||||
@@ -537,7 +562,7 @@ const char *Filesystem::getAppdataDirectory()
|
|||||||
else
|
else
|
||||||
appdata = xdgdatahome;
|
appdata = xdgdatahome;
|
||||||
}
|
}
|
||||||
return appdata.c_str();
|
return appdata;
|
||||||
#else
|
#else
|
||||||
return getUserDirectory();
|
return getUserDirectory();
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -145,14 +145,14 @@ public:
|
|||||||
/**
|
/**
|
||||||
* Gets the user home directory.
|
* Gets the user home directory.
|
||||||
**/
|
**/
|
||||||
const char *getUserDirectory();
|
std::string getUserDirectory();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the APPDATA directory. On Windows, this is the folder
|
* Gets the APPDATA directory. On Windows, this is the folder
|
||||||
* in the %APPDATA% enviroment variable. On Linux, this is the
|
* in the %APPDATA% enviroment variable. On Linux, this is the
|
||||||
* user home folder.
|
* user home folder.
|
||||||
**/
|
**/
|
||||||
const char *getAppdataDirectory();
|
std::string getAppdataDirectory();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the full path of the save folder.
|
* Gets the full path of the save folder.
|
||||||
|
|||||||
@@ -211,13 +211,13 @@ int w_getWorkingDirectory(lua_State *L)
|
|||||||
|
|
||||||
int w_getUserDirectory(lua_State *L)
|
int w_getUserDirectory(lua_State *L)
|
||||||
{
|
{
|
||||||
lua_pushstring(L, instance->getUserDirectory());
|
luax_pushstring(L, instance->getUserDirectory());
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int w_getAppdataDirectory(lua_State *L)
|
int w_getAppdataDirectory(lua_State *L)
|
||||||
{
|
{
|
||||||
lua_pushstring(L, instance->getAppdataDirectory());
|
luax_pushstring(L, instance->getAppdataDirectory());
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -176,6 +176,8 @@ StringMap<Graphics::Support, Graphics::SUPPORT_MAX_ENUM>::Entry Graphics::suppor
|
|||||||
{ "mipmap", Graphics::SUPPORT_MIPMAP },
|
{ "mipmap", Graphics::SUPPORT_MIPMAP },
|
||||||
{ "dxt", Graphics::SUPPORT_DXT },
|
{ "dxt", Graphics::SUPPORT_DXT },
|
||||||
{ "bc5", Graphics::SUPPORT_BC5 },
|
{ "bc5", Graphics::SUPPORT_BC5 },
|
||||||
|
{ "etc1", Graphics::SUPPORT_ETC1 },
|
||||||
|
{ "pvrtc1", Graphics::SUPPORT_PVRTC1 },
|
||||||
{ "instancing", Graphics::SUPPORT_INSTANCING },
|
{ "instancing", Graphics::SUPPORT_INSTANCING },
|
||||||
{ "srgb", Graphics::SUPPORT_SRGB },
|
{ "srgb", Graphics::SUPPORT_SRGB },
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -95,6 +95,8 @@ public:
|
|||||||
SUPPORT_MIPMAP,
|
SUPPORT_MIPMAP,
|
||||||
SUPPORT_DXT,
|
SUPPORT_DXT,
|
||||||
SUPPORT_BC5,
|
SUPPORT_BC5,
|
||||||
|
SUPPORT_ETC1,
|
||||||
|
SUPPORT_PVRTC1,
|
||||||
SUPPORT_INSTANCING,
|
SUPPORT_INSTANCING,
|
||||||
SUPPORT_SRGB,
|
SUPPORT_SRGB,
|
||||||
SUPPORT_MAX_ENUM
|
SUPPORT_MAX_ENUM
|
||||||
@@ -145,6 +147,20 @@ public:
|
|||||||
**/
|
**/
|
||||||
virtual void unSetMode() = 0;
|
virtual void unSetMode() = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets whether the module is active (internal use only.)
|
||||||
|
**/
|
||||||
|
virtual void setActive(bool active) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets whether the module is active. Graphics module methods are only
|
||||||
|
* guaranteed to work when it is active. Calling them otherwise may cause
|
||||||
|
* the program to crash (or worse.)
|
||||||
|
* Normally the module will always be active as long as a window exists, it
|
||||||
|
* may be different on some platforms (especially mobile ones.)
|
||||||
|
**/
|
||||||
|
virtual bool isActive() const = 0;
|
||||||
|
|
||||||
static bool getConstant(const char *in, DrawMode &out);
|
static bool getConstant(const char *in, DrawMode &out);
|
||||||
static bool getConstant(DrawMode in, const char *&out);
|
static bool getConstant(DrawMode in, const char *&out);
|
||||||
|
|
||||||
|
|||||||
@@ -109,16 +109,6 @@ bool Texture::getConstant(WrapMode in, const char *&out)
|
|||||||
return wrapModes.find(in, out);
|
return wrapModes.find(in, out);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Texture::getConstant(const char *in, Format &out)
|
|
||||||
{
|
|
||||||
return formats.find(in, out);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Texture::getConstant(Format in, const char *&out)
|
|
||||||
{
|
|
||||||
return formats.find(in, out);
|
|
||||||
}
|
|
||||||
|
|
||||||
StringMap<Texture::FilterMode, Texture::FILTER_MAX_ENUM>::Entry Texture::filterModeEntries[] =
|
StringMap<Texture::FilterMode, Texture::FILTER_MAX_ENUM>::Entry Texture::filterModeEntries[] =
|
||||||
{
|
{
|
||||||
{ "linear", Texture::FILTER_LINEAR },
|
{ "linear", Texture::FILTER_LINEAR },
|
||||||
@@ -135,15 +125,5 @@ StringMap<Texture::WrapMode, Texture::WRAP_MAX_ENUM>::Entry Texture::wrapModeEnt
|
|||||||
|
|
||||||
StringMap<Texture::WrapMode, Texture::WRAP_MAX_ENUM> Texture::wrapModes(Texture::wrapModeEntries, sizeof(Texture::wrapModeEntries));
|
StringMap<Texture::WrapMode, Texture::WRAP_MAX_ENUM> Texture::wrapModes(Texture::wrapModeEntries, sizeof(Texture::wrapModeEntries));
|
||||||
|
|
||||||
StringMap<Texture::Format, Texture::FORMAT_MAX_ENUM>::Entry Texture::formatEntries[] =
|
|
||||||
{
|
|
||||||
{"normal", Texture::FORMAT_NORMAL},
|
|
||||||
{"hdr", Texture::FORMAT_HDR},
|
|
||||||
{"srgb", Texture::FORMAT_SRGB},
|
|
||||||
};
|
|
||||||
|
|
||||||
StringMap<Texture::Format, Texture::FORMAT_MAX_ENUM> Texture::formats(Texture::formatEntries, sizeof(Texture::formatEntries));
|
|
||||||
|
|
||||||
|
|
||||||
} // graphics
|
} // graphics
|
||||||
} // love
|
} // love
|
||||||
|
|||||||
@@ -55,14 +55,6 @@ public:
|
|||||||
FILTER_MAX_ENUM
|
FILTER_MAX_ENUM
|
||||||
};
|
};
|
||||||
|
|
||||||
enum Format
|
|
||||||
{
|
|
||||||
FORMAT_NORMAL,
|
|
||||||
FORMAT_HDR,
|
|
||||||
FORMAT_SRGB,
|
|
||||||
FORMAT_MAX_ENUM
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Filter
|
struct Filter
|
||||||
{
|
{
|
||||||
Filter();
|
Filter();
|
||||||
@@ -119,9 +111,6 @@ public:
|
|||||||
static bool getConstant(const char *in, WrapMode &out);
|
static bool getConstant(const char *in, WrapMode &out);
|
||||||
static bool getConstant(WrapMode in, const char *&out);
|
static bool getConstant(WrapMode in, const char *&out);
|
||||||
|
|
||||||
static bool getConstant(const char *in, Format &out);
|
|
||||||
static bool getConstant(Format in, const char *&out);
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
int width;
|
int width;
|
||||||
@@ -143,9 +132,6 @@ private:
|
|||||||
static StringMap<WrapMode, WRAP_MAX_ENUM>::Entry wrapModeEntries[];
|
static StringMap<WrapMode, WRAP_MAX_ENUM>::Entry wrapModeEntries[];
|
||||||
static StringMap<WrapMode, WRAP_MAX_ENUM> wrapModes;
|
static StringMap<WrapMode, WRAP_MAX_ENUM> wrapModes;
|
||||||
|
|
||||||
static StringMap<Format, FORMAT_MAX_ENUM>::Entry formatEntries[];
|
|
||||||
static StringMap<Format, FORMAT_MAX_ENUM> formats;
|
|
||||||
|
|
||||||
}; // Texture
|
}; // Texture
|
||||||
|
|
||||||
} // graphics
|
} // graphics
|
||||||
|
|||||||
@@ -451,7 +451,7 @@ static void getStrategy()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Canvas::Canvas(int width, int height, Texture::Format format, int fsaa)
|
Canvas::Canvas(int width, int height, Format format, int fsaa)
|
||||||
: fbo(0)
|
: fbo(0)
|
||||||
, resolve_fbo(0)
|
, resolve_fbo(0)
|
||||||
, texture(0)
|
, texture(0)
|
||||||
@@ -556,36 +556,31 @@ bool Canvas::loadVolatile()
|
|||||||
glGenTextures(1, &texture);
|
glGenTextures(1, &texture);
|
||||||
gl.bindTexture(texture);
|
gl.bindTexture(texture);
|
||||||
|
|
||||||
|
if (GLAD_ANGLE_texture_usage)
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_USAGE_ANGLE, GL_FRAMEBUFFER_ATTACHMENT_ANGLE);
|
||||||
|
|
||||||
setFilter(filter);
|
setFilter(filter);
|
||||||
setWrap(wrap);
|
setWrap(wrap);
|
||||||
|
|
||||||
GLint internalformat;
|
GLenum internalformat = GL_RGBA;
|
||||||
GLenum textype;
|
GLenum externalformat = GL_RGBA;
|
||||||
switch (format)
|
GLenum textype = GL_UNSIGNED_BYTE;
|
||||||
{
|
|
||||||
case Texture::FORMAT_HDR:
|
convertFormat(format, internalformat, externalformat, textype);
|
||||||
internalformat = GL_RGBA16F;
|
|
||||||
textype = GL_FLOAT;
|
// in ES2, the internalformat and format params of TexImage have to match.
|
||||||
break;
|
if (GLAD_ES_VERSION_2_0 && !GLAD_ES_VERSION_3_0)
|
||||||
case Texture::FORMAT_SRGB:
|
internalformat = externalformat;
|
||||||
internalformat = GL_SRGB_ALPHA;
|
|
||||||
textype = GL_UNSIGNED_BYTE;
|
|
||||||
break;
|
|
||||||
case Texture::FORMAT_NORMAL:
|
|
||||||
default:
|
|
||||||
internalformat = GL_RGBA;
|
|
||||||
textype = GL_UNSIGNED_BYTE;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (glGetError() != GL_NO_ERROR)
|
while (glGetError() != GL_NO_ERROR)
|
||||||
/* Clear the error buffer. */;
|
/* Clear the error buffer. */;
|
||||||
|
|
||||||
glTexImage2D(GL_TEXTURE_2D,
|
glTexImage2D(GL_TEXTURE_2D,
|
||||||
0,
|
0,
|
||||||
internalformat,
|
(GLint) internalformat,
|
||||||
width, height,
|
width, height,
|
||||||
0,
|
0,
|
||||||
GL_RGBA,
|
externalformat,
|
||||||
textype,
|
textype,
|
||||||
nullptr);
|
nullptr);
|
||||||
|
|
||||||
@@ -736,10 +731,13 @@ void Canvas::setupGrab()
|
|||||||
gl.matrices.projection.push(Matrix::ortho(0.0, width, 0.0, height));
|
gl.matrices.projection.push(Matrix::ortho(0.0, width, 0.0, height));
|
||||||
|
|
||||||
// Make sure the correct sRGB setting is used when drawing to the canvas.
|
// Make sure the correct sRGB setting is used when drawing to the canvas.
|
||||||
if (format == FORMAT_SRGB)
|
if (GLAD_VERSION_1_1 || GLAD_EXT_sRGB_write_control)
|
||||||
glEnable(GL_FRAMEBUFFER_SRGB);
|
{
|
||||||
else if (screenHasSRGB)
|
if (format == FORMAT_SRGB)
|
||||||
glDisable(GL_FRAMEBUFFER_SRGB);
|
glEnable(GL_FRAMEBUFFER_SRGB);
|
||||||
|
else if (screenHasSRGB)
|
||||||
|
glDisable(GL_FRAMEBUFFER_SRGB);
|
||||||
|
}
|
||||||
|
|
||||||
if (fsaa_buffer != 0)
|
if (fsaa_buffer != 0)
|
||||||
fsaa_dirty = true;
|
fsaa_dirty = true;
|
||||||
@@ -828,8 +826,11 @@ void Canvas::stopGrab(bool switchingToOtherCanvas)
|
|||||||
|
|
||||||
if (switchingToOtherCanvas)
|
if (switchingToOtherCanvas)
|
||||||
{
|
{
|
||||||
if (format == FORMAT_SRGB)
|
if (GLAD_VERSION_1_1 || GLAD_EXT_sRGB_write_control)
|
||||||
glDisable(GL_FRAMEBUFFER_SRGB);
|
{
|
||||||
|
if (format == FORMAT_SRGB)
|
||||||
|
glDisable(GL_FRAMEBUFFER_SRGB);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -838,10 +839,13 @@ void Canvas::stopGrab(bool switchingToOtherCanvas)
|
|||||||
current = nullptr;
|
current = nullptr;
|
||||||
gl.setViewport(systemViewport);
|
gl.setViewport(systemViewport);
|
||||||
|
|
||||||
if (format == FORMAT_SRGB && !screenHasSRGB)
|
if (GLAD_VERSION_1_1 || GLAD_EXT_sRGB_write_control)
|
||||||
glDisable(GL_FRAMEBUFFER_SRGB);
|
{
|
||||||
else if (format != FORMAT_SRGB && screenHasSRGB)
|
if (format == FORMAT_SRGB && !screenHasSRGB)
|
||||||
glEnable(GL_FRAMEBUFFER_SRGB);
|
glDisable(GL_FRAMEBUFFER_SRGB);
|
||||||
|
else if (format != FORMAT_SRGB && screenHasSRGB)
|
||||||
|
glEnable(GL_FRAMEBUFFER_SRGB);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
gl.matrices.projection.pop();
|
gl.matrices.projection.pop();
|
||||||
@@ -1007,41 +1011,231 @@ bool Canvas::resolveMSAA()
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Canvas::Format Canvas::getSizedFormat(Canvas::Format format)
|
||||||
|
{
|
||||||
|
switch (format)
|
||||||
|
{
|
||||||
|
case FORMAT_NORMAL:
|
||||||
|
// 32-bit render targets don't have guaranteed support on OpenGL ES 2.
|
||||||
|
if (GLAD_ES_VERSION_2_0 && !(GLAD_ES_VERSION_3_0 || GLAD_OES_rgb8_rgba8 || GLAD_ARM_rgba8))
|
||||||
|
return FORMAT_RGBA4;
|
||||||
|
else
|
||||||
|
return FORMAT_RGBA8;
|
||||||
|
case FORMAT_HDR:
|
||||||
|
return FORMAT_RGBA16F;
|
||||||
|
default:
|
||||||
|
return format;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Canvas::convertFormat(Canvas::Format format, GLenum &internalformat, GLenum &externalformat, GLenum &type)
|
||||||
|
{
|
||||||
|
format = getSizedFormat(format);
|
||||||
|
externalformat = GL_RGBA;
|
||||||
|
|
||||||
|
switch (format)
|
||||||
|
{
|
||||||
|
case FORMAT_RGBA8:
|
||||||
|
default:
|
||||||
|
internalformat = GL_RGBA8;
|
||||||
|
type = GL_UNSIGNED_BYTE;
|
||||||
|
break;
|
||||||
|
case FORMAT_RGBA4:
|
||||||
|
internalformat = GL_RGBA4;
|
||||||
|
type = GL_UNSIGNED_SHORT_4_4_4_4;
|
||||||
|
break;
|
||||||
|
case FORMAT_RGB5A1:
|
||||||
|
internalformat = GL_RGB5_A1;
|
||||||
|
type = GL_UNSIGNED_SHORT_5_5_5_1;
|
||||||
|
break;
|
||||||
|
case FORMAT_RGB565:
|
||||||
|
internalformat = GL_RGB565;
|
||||||
|
externalformat = GL_RGB;
|
||||||
|
type = GL_UNSIGNED_SHORT_5_6_5;
|
||||||
|
break;
|
||||||
|
case FORMAT_RGB10A2:
|
||||||
|
internalformat = GL_RGB10_A2;
|
||||||
|
type = GL_UNSIGNED_INT_2_10_10_10_REV;
|
||||||
|
break;
|
||||||
|
case FORMAT_RG11B10F:
|
||||||
|
internalformat = GL_R11F_G11F_B10F;
|
||||||
|
externalformat = GL_RGB;
|
||||||
|
type = GL_UNSIGNED_INT_10F_11F_11F_REV;
|
||||||
|
break;
|
||||||
|
case FORMAT_RGBA16F:
|
||||||
|
internalformat = GL_RGBA16F;
|
||||||
|
if (GLAD_OES_texture_float)
|
||||||
|
type = GL_HALF_FLOAT_OES;
|
||||||
|
else if (GLAD_VERSION_1_1)
|
||||||
|
type = GL_FLOAT;
|
||||||
|
else
|
||||||
|
type = GL_HALF_FLOAT;
|
||||||
|
break;
|
||||||
|
case FORMAT_RGBA32F:
|
||||||
|
internalformat = GL_RGBA32F;
|
||||||
|
type = GL_FLOAT;
|
||||||
|
break;
|
||||||
|
case FORMAT_SRGB:
|
||||||
|
internalformat = GL_SRGB8_ALPHA8;
|
||||||
|
type = GL_UNSIGNED_BYTE;
|
||||||
|
if (GLAD_ES_VERSION_2_0)
|
||||||
|
externalformat = GL_SRGB_ALPHA;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool Canvas::isSupported()
|
bool Canvas::isSupported()
|
||||||
{
|
{
|
||||||
getStrategy();
|
getStrategy();
|
||||||
return (strategy != &strategyNone);
|
return (strategy != &strategyNone);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Canvas::isHDRSupported()
|
|
||||||
{
|
|
||||||
return GLAD_VERSION_3_0 || (isSupported() && GLAD_ARB_texture_float);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Canvas::isSRGBSupported()
|
|
||||||
{
|
|
||||||
if (GLAD_VERSION_3_0)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
if (!isSupported())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return (GLAD_ARB_framebuffer_sRGB || GLAD_EXT_framebuffer_sRGB)
|
|
||||||
&& GLAD_EXT_texture_sRGB;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Canvas::isMultiCanvasSupported()
|
bool Canvas::isMultiCanvasSupported()
|
||||||
{
|
{
|
||||||
// system must support at least 4 simultanious active canvases.
|
// system must support at least 4 simultanious active canvases.
|
||||||
return gl.getMaxRenderTargets() >= 4;
|
return gl.getMaxRenderTargets() >= 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Canvas::supportedFormats[] = {false};
|
||||||
|
bool Canvas::checkedFormats[] = {false};
|
||||||
|
|
||||||
|
bool Canvas::isFormatSupported(Canvas::Format format)
|
||||||
|
{
|
||||||
|
if (!isSupported())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
bool supported = true;
|
||||||
|
format = getSizedFormat(format);
|
||||||
|
|
||||||
|
switch (format)
|
||||||
|
{
|
||||||
|
case FORMAT_RGBA8:
|
||||||
|
if (GLAD_VERSION_1_1)
|
||||||
|
supported = true;
|
||||||
|
else
|
||||||
|
supported = GLAD_ES_VERSION_3_0 || GLAD_OES_rgb8_rgba8 || GLAD_ARM_rgba8;
|
||||||
|
break;
|
||||||
|
case FORMAT_RGBA4:
|
||||||
|
case FORMAT_RGB5A1:
|
||||||
|
supported = true;
|
||||||
|
break;
|
||||||
|
case FORMAT_RGB565:
|
||||||
|
supported = GLAD_ES_VERSION_2_0 || GLAD_VERSION_4_2 || GLAD_ARB_ES2_compatibility;
|
||||||
|
break;
|
||||||
|
case FORMAT_RGB10A2:
|
||||||
|
if (GLAD_VERSION_1_1)
|
||||||
|
supported = true;
|
||||||
|
else
|
||||||
|
supported = GLAD_ES_VERSION_3_0;
|
||||||
|
break;
|
||||||
|
case FORMAT_RG11B10F:
|
||||||
|
if (GLAD_VERSION_1_1)
|
||||||
|
supported = GLAD_VERSION_3_0 || GLAD_EXT_packed_float;
|
||||||
|
else
|
||||||
|
supported = GLAD_ES_VERSION_3_0 /*|| GLAD_NV_packed_float*/;
|
||||||
|
break;
|
||||||
|
case FORMAT_RGBA16F:
|
||||||
|
if (GLAD_VERSION_1_1)
|
||||||
|
supported = GLAD_VERSION_3_0 || GLAD_ARB_texture_float;
|
||||||
|
else
|
||||||
|
supported = GLAD_EXT_color_buffer_half_float && (GLAD_ES_VERSION_3_0 || GLAD_OES_texture_half_float);
|
||||||
|
break;
|
||||||
|
case FORMAT_RGBA32F:
|
||||||
|
if (GLAD_VERSION_1_1)
|
||||||
|
supported = GLAD_VERSION_3_0 || GLAD_ARB_texture_float;
|
||||||
|
else
|
||||||
|
supported = false; // GLAD_EXT_color_buffer_float && GLAD_ES_VERSION_3_0;
|
||||||
|
break;
|
||||||
|
case FORMAT_SRGB:
|
||||||
|
if (GLAD_VERSION_1_1)
|
||||||
|
supported = GLAD_VERSION_3_0 || ((GLAD_ARB_framebuffer_sRGB || GLAD_EXT_framebuffer_sRGB)
|
||||||
|
&& (GLAD_VERSION_2_1 || GLAD_EXT_texture_sRGB));
|
||||||
|
else
|
||||||
|
supported = GLAD_ES_VERSION_3_0 || GLAD_EXT_sRGB;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
supported = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!supported)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (checkedFormats[format])
|
||||||
|
return supportedFormats[format];
|
||||||
|
|
||||||
|
// Even though we might have the necessary OpenGL version or extension,
|
||||||
|
// drivers are still allowed to throw FRAMEBUFFER_UNSUPPORTED when attaching
|
||||||
|
// a texture to a FBO whose format the driver doesn't like. So we should
|
||||||
|
// test with an actual FBO.
|
||||||
|
|
||||||
|
GLenum internalformat = GL_RGBA;
|
||||||
|
GLenum externalformat = GL_RGBA;
|
||||||
|
GLenum textype = GL_UNSIGNED_BYTE;
|
||||||
|
convertFormat(format, internalformat, externalformat, textype);
|
||||||
|
|
||||||
|
if (GLAD_ES_VERSION_2_0 && !GLAD_ES_VERSION_3_0)
|
||||||
|
internalformat = externalformat;
|
||||||
|
|
||||||
|
GLuint texture = 0;
|
||||||
|
glGenTextures(1, &texture);
|
||||||
|
gl.bindTexture(texture);
|
||||||
|
|
||||||
|
Texture::Filter f;
|
||||||
|
f.min = f.mag = Texture::FILTER_NEAREST;
|
||||||
|
gl.setTextureFilter(f);
|
||||||
|
|
||||||
|
Texture::Wrap w;
|
||||||
|
gl.setTextureWrap(w);
|
||||||
|
|
||||||
|
glTexImage2D(GL_TEXTURE_2D, 0, internalformat, 2, 2, 0, externalformat, textype, nullptr);
|
||||||
|
|
||||||
|
GLuint fbo = 0;
|
||||||
|
supported = (strategy->createFBO(fbo, texture) == GL_FRAMEBUFFER_COMPLETE);
|
||||||
|
strategy->deleteFBO(fbo, 0, 0);
|
||||||
|
|
||||||
|
gl.deleteTexture(texture);
|
||||||
|
|
||||||
|
// Cache the result so we don't do this for every isFormatSupported call.
|
||||||
|
checkedFormats[format] = true;
|
||||||
|
supportedFormats[format] = supported;
|
||||||
|
|
||||||
|
return supported;
|
||||||
|
}
|
||||||
|
|
||||||
void Canvas::bindDefaultCanvas()
|
void Canvas::bindDefaultCanvas()
|
||||||
{
|
{
|
||||||
if (current != nullptr)
|
if (current != nullptr)
|
||||||
current->stopGrab();
|
current->stopGrab();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Canvas::getConstant(const char *in, Format &out)
|
||||||
|
{
|
||||||
|
return formats.find(in, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Canvas::getConstant(Format in, const char *&out)
|
||||||
|
{
|
||||||
|
return formats.find(in, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
StringMap<Canvas::Format, Canvas::FORMAT_MAX_ENUM>::Entry Canvas::formatEntries[] =
|
||||||
|
{
|
||||||
|
{"normal", Canvas::FORMAT_NORMAL},
|
||||||
|
{"hdr", Canvas::FORMAT_HDR},
|
||||||
|
{"rgba8", Canvas::FORMAT_RGBA8},
|
||||||
|
{"rgba4", Canvas::FORMAT_RGBA4},
|
||||||
|
{"rgb5a1", Canvas::FORMAT_RGB5A1},
|
||||||
|
{"rgb565", Canvas::FORMAT_RGB565},
|
||||||
|
{"rgb10a2", Canvas::FORMAT_RGB10A2},
|
||||||
|
{"rg11b10f", Canvas::FORMAT_RG11B10F},
|
||||||
|
{"rgba16f", Canvas::FORMAT_RGBA16F},
|
||||||
|
{"rgba32f", Canvas::FORMAT_RGBA32F},
|
||||||
|
{"srgb", Canvas::FORMAT_SRGB},
|
||||||
|
};
|
||||||
|
|
||||||
|
StringMap<Canvas::Format, Canvas::FORMAT_MAX_ENUM> Canvas::formats(Canvas::formatEntries, sizeof(Canvas::formatEntries));
|
||||||
|
|
||||||
} // opengl
|
} // opengl
|
||||||
} // graphics
|
} // graphics
|
||||||
} // love
|
} // love
|
||||||
|
|||||||
@@ -25,6 +25,7 @@
|
|||||||
#include "image/Image.h"
|
#include "image/Image.h"
|
||||||
#include "image/ImageData.h"
|
#include "image/ImageData.h"
|
||||||
#include "common/Matrix.h"
|
#include "common/Matrix.h"
|
||||||
|
#include "common/StringMap.h"
|
||||||
#include "Texture.h"
|
#include "Texture.h"
|
||||||
#include "OpenGL.h"
|
#include "OpenGL.h"
|
||||||
|
|
||||||
@@ -39,7 +40,24 @@ class Canvas : public Texture
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
Canvas(int width, int height, Texture::Format format = Texture::FORMAT_NORMAL, int fsaa = 0);
|
// Different Canvas render target formats.
|
||||||
|
enum Format
|
||||||
|
{
|
||||||
|
FORMAT_NORMAL, // Usually RGBA8 or a similar fallback. Always supported.
|
||||||
|
FORMAT_HDR, // Usually RGBA16F. Not always supported.
|
||||||
|
FORMAT_RGBA8, // RGBA with 8 bits per component.
|
||||||
|
FORMAT_RGBA4, // RGBA with 4 bits per component.
|
||||||
|
FORMAT_RGB5A1, // RGB with 5 bits per component, and A with 1 bit.
|
||||||
|
FORMAT_RGB565, // RGB with 5, 6, and 5 bits each, respectively.
|
||||||
|
FORMAT_RGB10A2, // RGB with 10 bits each, and A with 2 bits.
|
||||||
|
FORMAT_RG11B10F, // Floating point [0, +inf]. RG with 11 FP bits each, and B with 10 FP bits.
|
||||||
|
FORMAT_RGBA16F, // Floating point [-inf, +inf]. RGBA with 16 FP bits per component.
|
||||||
|
FORMAT_RGBA32F, // Floating point [-inf, +inf]. RGBA with 32 FP bits per component.
|
||||||
|
FORMAT_SRGB, // sRGB with 8 bits per component, plus 8 bit linear A.
|
||||||
|
FORMAT_MAX_ENUM
|
||||||
|
};
|
||||||
|
|
||||||
|
Canvas(int width, int height, Format format = FORMAT_NORMAL, int fsaa = 0);
|
||||||
virtual ~Canvas();
|
virtual ~Canvas();
|
||||||
|
|
||||||
// Implements Volatile.
|
// Implements Volatile.
|
||||||
@@ -85,7 +103,7 @@ public:
|
|||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline Texture::Format getTextureFormat() const
|
inline Format getTextureFormat() const
|
||||||
{
|
{
|
||||||
return format;
|
return format;
|
||||||
}
|
}
|
||||||
@@ -98,9 +116,8 @@ public:
|
|||||||
bool resolveMSAA();
|
bool resolveMSAA();
|
||||||
|
|
||||||
static bool isSupported();
|
static bool isSupported();
|
||||||
static bool isHDRSupported();
|
|
||||||
static bool isSRGBSupported();
|
|
||||||
static bool isMultiCanvasSupported();
|
static bool isMultiCanvasSupported();
|
||||||
|
static bool isFormatSupported(Format format);
|
||||||
|
|
||||||
static Canvas *current;
|
static Canvas *current;
|
||||||
static void bindDefaultCanvas();
|
static void bindDefaultCanvas();
|
||||||
@@ -111,10 +128,16 @@ public:
|
|||||||
// Whether the main screen should have linear -> sRGB conversions enabled.
|
// Whether the main screen should have linear -> sRGB conversions enabled.
|
||||||
static bool screenHasSRGB;
|
static bool screenHasSRGB;
|
||||||
|
|
||||||
|
static bool getConstant(const char *in, Format &out);
|
||||||
|
static bool getConstant(Format in, const char *&out);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
bool createFSAAFBO(GLenum internalformat);
|
bool createFSAAFBO(GLenum internalformat);
|
||||||
|
|
||||||
|
static Format getSizedFormat(Format format);
|
||||||
|
static void convertFormat(Format format, GLenum &internalformat, GLenum &externalformat, GLenum &type);
|
||||||
|
|
||||||
GLuint fbo;
|
GLuint fbo;
|
||||||
GLuint resolve_fbo;
|
GLuint resolve_fbo;
|
||||||
|
|
||||||
@@ -134,6 +157,12 @@ private:
|
|||||||
void setupGrab();
|
void setupGrab();
|
||||||
void drawv(const Matrix &t, const Vertex *v);
|
void drawv(const Matrix &t, const Vertex *v);
|
||||||
|
|
||||||
|
static bool supportedFormats[FORMAT_MAX_ENUM];
|
||||||
|
static bool checkedFormats[FORMAT_MAX_ENUM];
|
||||||
|
|
||||||
|
static StringMap<Format, FORMAT_MAX_ENUM>::Entry formatEntries[];
|
||||||
|
static StringMap<Format, FORMAT_MAX_ENUM> formats;
|
||||||
|
|
||||||
}; // Canvas
|
}; // Canvas
|
||||||
|
|
||||||
} // opengl
|
} // opengl
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ const int Font::TEXTURE_HEIGHTS[] = {128, 128, 256, 256, 512, 512, 1024};
|
|||||||
|
|
||||||
Font::Font(love::font::Rasterizer *r, const Texture::Filter &filter)
|
Font::Font(love::font::Rasterizer *r, const Texture::Filter &filter)
|
||||||
: rasterizer(r)
|
: rasterizer(r)
|
||||||
|
, indexBuffer(nullptr)
|
||||||
, height(r->getHeight())
|
, height(r->getHeight())
|
||||||
, lineHeight(1)
|
, lineHeight(1)
|
||||||
, mSpacing(1)
|
, mSpacing(1)
|
||||||
@@ -68,6 +69,8 @@ Font::Font(love::font::Rasterizer *r, const Texture::Filter &filter)
|
|||||||
textureWidth = TEXTURE_WIDTHS[textureSizeIndex];
|
textureWidth = TEXTURE_WIDTHS[textureSizeIndex];
|
||||||
textureHeight = TEXTURE_HEIGHTS[textureSizeIndex];
|
textureHeight = TEXTURE_HEIGHTS[textureSizeIndex];
|
||||||
|
|
||||||
|
indexBuffer = new VertexIndex(20);
|
||||||
|
|
||||||
love::font::GlyphData *gd = nullptr;
|
love::font::GlyphData *gd = nullptr;
|
||||||
|
|
||||||
try
|
try
|
||||||
@@ -93,6 +96,7 @@ Font::Font(love::font::Rasterizer *r, const Texture::Filter &filter)
|
|||||||
|
|
||||||
Font::~Font()
|
Font::~Font()
|
||||||
{
|
{
|
||||||
|
delete indexBuffer;
|
||||||
rasterizer->release();
|
rasterizer->release();
|
||||||
unloadVolatile();
|
unloadVolatile();
|
||||||
}
|
}
|
||||||
@@ -368,28 +372,17 @@ void Font::print(const std::string &text, float x, float y, float extra_spacing,
|
|||||||
// second (using the struct's < operator).
|
// second (using the struct's < operator).
|
||||||
std::sort(glyphinfolist.begin(), glyphinfolist.end());
|
std::sort(glyphinfolist.begin(), glyphinfolist.end());
|
||||||
|
|
||||||
std::vector<uint16> indices;
|
int maxvertices = 0;
|
||||||
|
|
||||||
int indicescount = 0;
|
|
||||||
for (auto it = glyphinfolist.begin(); it != glyphinfolist.end(); ++it)
|
for (auto it = glyphinfolist.begin(); it != glyphinfolist.end(); ++it)
|
||||||
|
maxvertices = std::max(maxvertices, it->vertexcount);
|
||||||
|
|
||||||
|
// If the index buffer is too small for the number of glyphs we're going to
|
||||||
|
// draw, we need to make a new one that has the correct size.
|
||||||
|
if ((size_t) maxvertices / 4 > indexBuffer->getSize())
|
||||||
{
|
{
|
||||||
if ((it->vertexcount / 4) * 6 > indicescount)
|
VertexIndex *newIndexBuffer = new VertexIndex(maxvertices / 4);
|
||||||
indicescount = (it->vertexcount / 4) * 6;
|
delete indexBuffer;
|
||||||
}
|
indexBuffer = newIndexBuffer;
|
||||||
|
|
||||||
indices.reserve(indicescount);
|
|
||||||
|
|
||||||
for (int i = 0; i < indicescount / 6; i++)
|
|
||||||
{
|
|
||||||
// First triangle.
|
|
||||||
indices.push_back(i * 4 + 0);
|
|
||||||
indices.push_back(i * 4 + 1);
|
|
||||||
indices.push_back(i * 4 + 2);
|
|
||||||
|
|
||||||
// Second triangle.
|
|
||||||
indices.push_back(i * 4 + 0);
|
|
||||||
indices.push_back(i * 4 + 2);
|
|
||||||
indices.push_back(i * 4 + 3);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
gl.matrices.transform.push(gl.matrices.transform.top());
|
gl.matrices.transform.push(gl.matrices.transform.top());
|
||||||
@@ -403,16 +396,36 @@ void Font::print(const std::string &text, float x, float y, float extra_spacing,
|
|||||||
|
|
||||||
gl.prepareDraw();
|
gl.prepareDraw();
|
||||||
|
|
||||||
|
VertexBuffer::Bind index_bind(*indexBuffer->getVertexBuffer());
|
||||||
|
GLenum elemtype = indexBuffer->getType();
|
||||||
|
const GLvoid *elemoffset = indexBuffer->getPointer(0);
|
||||||
|
|
||||||
|
// Optimization: we can take these calls out of the loop if we have support.
|
||||||
|
if (GLAD_VERSION_3_2 || GLAD_ARB_draw_elements_base_vertex)
|
||||||
|
{
|
||||||
|
gl.setVertexAttribArray(OpenGL::ATTRIB_POS, 2, GL_FLOAT, sizeof(GlyphVertex), &glyphverts[0].x);
|
||||||
|
gl.setVertexAttribArray(OpenGL::ATTRIB_TEXCOORD, 2, GL_FLOAT, sizeof(GlyphVertex), &glyphverts[0].s);
|
||||||
|
}
|
||||||
|
|
||||||
// We need to draw a new vertex array for every section of the string which
|
// We need to draw a new vertex array for every section of the string which
|
||||||
// uses a different texture than the previous section.
|
// uses a different texture than the previous section.
|
||||||
for (auto it = glyphinfolist.begin(); it != glyphinfolist.end(); ++it)
|
for (auto it = glyphinfolist.begin(); it != glyphinfolist.end(); ++it)
|
||||||
{
|
{
|
||||||
gl.bindTexture(it->texture);
|
gl.bindTexture(it->texture);
|
||||||
|
|
||||||
gl.setVertexAttribArray(OpenGL::ATTRIB_POS, 2, GL_FLOAT, sizeof(GlyphVertex), (GLvoid *)&glyphverts[it->startvertex].x);
|
if (GLAD_VERSION_3_2 || GLAD_ARB_draw_elements_base_vertex)
|
||||||
gl.setVertexAttribArray(OpenGL::ATTRIB_TEXCOORD, 2, GL_FLOAT, sizeof(GlyphVertex), (GLvoid *)&glyphverts[it->startvertex].s);
|
{
|
||||||
|
// Optimization: setting a base vertex is faster than redoing the
|
||||||
|
// setVertexAttribArray calls before each draw.
|
||||||
|
glDrawElementsBaseVertex(GL_TRIANGLES, (it->vertexcount / 4) * 6, elemtype, elemoffset, it->startvertex);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
gl.setVertexAttribArray(OpenGL::ATTRIB_POS, 2, GL_FLOAT, sizeof(GlyphVertex), &glyphverts[it->startvertex].x);
|
||||||
|
gl.setVertexAttribArray(OpenGL::ATTRIB_TEXCOORD, 2, GL_FLOAT, sizeof(GlyphVertex), &glyphverts[it->startvertex].s);
|
||||||
|
|
||||||
glDrawElements(GL_TRIANGLES, (it->vertexcount / 4) * 6, GL_UNSIGNED_SHORT, &indices[0]);
|
glDrawElements(GL_TRIANGLES, (it->vertexcount / 4) * 6, elemtype, elemoffset);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
gl.disableVertexAttribArray(OpenGL::ATTRIB_POS);
|
gl.disableVertexAttribArray(OpenGL::ATTRIB_POS);
|
||||||
|
|||||||
@@ -31,6 +31,7 @@
|
|||||||
#include "font/Rasterizer.h"
|
#include "font/Rasterizer.h"
|
||||||
#include "graphics/Texture.h"
|
#include "graphics/Texture.h"
|
||||||
#include "graphics/Volatile.h"
|
#include "graphics/Volatile.h"
|
||||||
|
#include "VertexBuffer.h"
|
||||||
|
|
||||||
#include "OpenGL.h"
|
#include "OpenGL.h"
|
||||||
|
|
||||||
@@ -185,6 +186,8 @@ private:
|
|||||||
|
|
||||||
love::font::Rasterizer *rasterizer;
|
love::font::Rasterizer *rasterizer;
|
||||||
|
|
||||||
|
VertexIndex *indexBuffer;
|
||||||
|
|
||||||
int height;
|
int height;
|
||||||
float lineHeight;
|
float lineHeight;
|
||||||
float mSpacing; // modifies the spacing by multiplying it with this value
|
float mSpacing; // modifies the spacing by multiplying it with this value
|
||||||
|
|||||||
@@ -52,6 +52,7 @@ Graphics::Graphics()
|
|||||||
, width(0)
|
, width(0)
|
||||||
, height(0)
|
, height(0)
|
||||||
, created(false)
|
, created(false)
|
||||||
|
, active(true)
|
||||||
, activeStencil(false)
|
, activeStencil(false)
|
||||||
, savedState()
|
, savedState()
|
||||||
{
|
{
|
||||||
@@ -72,7 +73,10 @@ Graphics::~Graphics()
|
|||||||
currentFont->release();
|
currentFont->release();
|
||||||
|
|
||||||
if (Shader::defaultShader)
|
if (Shader::defaultShader)
|
||||||
|
{
|
||||||
Shader::defaultShader->release();
|
Shader::defaultShader->release();
|
||||||
|
Shader::defaultShader = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
currentWindow->release();
|
currentWindow->release();
|
||||||
}
|
}
|
||||||
@@ -235,12 +239,16 @@ bool Graphics::setMode(int width, int height, bool &sRGB)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Set whether drawing converts input from linear -> sRGB colorspace.
|
// Set whether drawing converts input from linear -> sRGB colorspace.
|
||||||
if (GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_sRGB || GLAD_EXT_framebuffer_sRGB)
|
if (GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_sRGB || GLAD_EXT_framebuffer_sRGB
|
||||||
|
|| (GLAD_ES_VERSION_3_0 || GLAD_EXT_sRGB))
|
||||||
{
|
{
|
||||||
if (sRGB)
|
if (GLAD_VERSION_1_1 || GLAD_EXT_sRGB_write_control)
|
||||||
glEnable(GL_FRAMEBUFFER_SRGB);
|
{
|
||||||
else
|
if (sRGB)
|
||||||
glDisable(GL_FRAMEBUFFER_SRGB);
|
glEnable(GL_FRAMEBUFFER_SRGB);
|
||||||
|
else
|
||||||
|
glDisable(GL_FRAMEBUFFER_SRGB);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
sRGB = false;
|
sRGB = false;
|
||||||
@@ -282,6 +290,23 @@ void Graphics::unSetMode()
|
|||||||
created = false;
|
created = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Graphics::setActive(bool active)
|
||||||
|
{
|
||||||
|
// Make sure all pending OpenGL commands have fully executed before
|
||||||
|
// returning, if we're going from active to inactive.
|
||||||
|
if (isCreated() && this->active && !active)
|
||||||
|
glFinish();
|
||||||
|
|
||||||
|
this->active = active;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Graphics::isActive() const
|
||||||
|
{
|
||||||
|
// The graphics module is only completely 'active' if there's a window, a
|
||||||
|
// context, and the active variable is set.
|
||||||
|
return active && isCreated() && currentWindow && currentWindow->isCreated();
|
||||||
|
}
|
||||||
|
|
||||||
static void APIENTRY debugCB(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei /*len*/, const GLchar *msg, const GLvoid* /*usr*/)
|
static void APIENTRY debugCB(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei /*len*/, const GLchar *msg, const GLvoid* /*usr*/)
|
||||||
{
|
{
|
||||||
// Human-readable strings for the debug info.
|
// Human-readable strings for the debug info.
|
||||||
@@ -364,6 +389,9 @@ void Graphics::clear()
|
|||||||
|
|
||||||
void Graphics::present()
|
void Graphics::present()
|
||||||
{
|
{
|
||||||
|
if (!isActive())
|
||||||
|
return;
|
||||||
|
|
||||||
// Make sure we don't have a canvas active.
|
// Make sure we don't have a canvas active.
|
||||||
Canvas *c = Canvas::current;
|
Canvas *c = Canvas::current;
|
||||||
Canvas::bindDefaultCanvas();
|
Canvas::bindDefaultCanvas();
|
||||||
@@ -463,7 +491,7 @@ void Graphics::discardStencil()
|
|||||||
activeStencil = false;
|
activeStencil = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Image *Graphics::newImage(love::image::ImageData *data, Texture::Format format)
|
Image *Graphics::newImage(love::image::ImageData *data, Image::Format format)
|
||||||
{
|
{
|
||||||
// Create the image.
|
// Create the image.
|
||||||
Image *image = new Image(data, format);
|
Image *image = new Image(data, format);
|
||||||
@@ -490,7 +518,7 @@ Image *Graphics::newImage(love::image::ImageData *data, Texture::Format format)
|
|||||||
return image;
|
return image;
|
||||||
}
|
}
|
||||||
|
|
||||||
Image *Graphics::newImage(love::image::CompressedData *cdata, Texture::Format format)
|
Image *Graphics::newImage(love::image::CompressedData *cdata, Image::Format format)
|
||||||
{
|
{
|
||||||
// Create the image.
|
// Create the image.
|
||||||
Image *image = new Image(cdata, format);
|
Image *image = new Image(cdata, format);
|
||||||
@@ -537,13 +565,14 @@ ParticleSystem *Graphics::newParticleSystem(Texture *texture, int size)
|
|||||||
return new ParticleSystem(texture, size);
|
return new ParticleSystem(texture, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
Canvas *Graphics::newCanvas(int width, int height, Texture::Format format, int fsaa)
|
Canvas *Graphics::newCanvas(int width, int height, Canvas::Format format, int fsaa)
|
||||||
{
|
{
|
||||||
if (format == Texture::FORMAT_HDR && !Canvas::isHDRSupported())
|
if (!Canvas::isFormatSupported(format))
|
||||||
throw Exception("HDR Canvases are not supported by your OpenGL implementation");
|
{
|
||||||
|
const char *fstr = "rgba8";
|
||||||
if (format == Texture::FORMAT_SRGB && !Canvas::isSRGBSupported())
|
Canvas::getConstant(format, fstr);
|
||||||
throw Exception("sRGB Canvases are not supported by your OpenGL implementation");
|
throw love::Exception("The %s canvas format is not supported by your OpenGL implementation.", fstr);
|
||||||
|
}
|
||||||
|
|
||||||
if (width > gl.getMaxTextureSize())
|
if (width > gl.getMaxTextureSize())
|
||||||
throw Exception("Cannot create canvas: width of %d pixels is too large for this system.", width);
|
throw Exception("Cannot create canvas: width of %d pixels is too large for this system.", width);
|
||||||
@@ -594,8 +623,8 @@ Canvas *Graphics::newCanvas(int width, int height, Texture::Format format, int f
|
|||||||
}
|
}
|
||||||
|
|
||||||
canvas->release();
|
canvas->release();
|
||||||
throw Exception(error_string.str().c_str());
|
throw Exception("%s", error_string.str().c_str());
|
||||||
return NULL; // never reached
|
return nullptr; // never reached
|
||||||
}
|
}
|
||||||
|
|
||||||
Shader *Graphics::newShader(const Shader::ShaderSources &sources)
|
Shader *Graphics::newShader(const Shader::ShaderSources &sources)
|
||||||
@@ -972,7 +1001,24 @@ void Graphics::circle(DrawMode mode, float x, float y, float radius, int points)
|
|||||||
float angle_shift = (two_pi / points);
|
float angle_shift = (two_pi / points);
|
||||||
float phi = .0f;
|
float phi = .0f;
|
||||||
|
|
||||||
float *coords = new float[2 * (points + 1)];
|
float *coords = nullptr;
|
||||||
|
int extrapoints = 1;
|
||||||
|
|
||||||
|
// Fill mode will use a triangle fan internally, so we want the "hub" of the
|
||||||
|
// fan (its first vertex) to be the midpoint of the circle.
|
||||||
|
if (mode == DRAW_FILL)
|
||||||
|
{
|
||||||
|
extrapoints = 2;
|
||||||
|
coords = new float[2 * (points + extrapoints)];
|
||||||
|
|
||||||
|
coords[0] = x;
|
||||||
|
coords[1] = y;
|
||||||
|
|
||||||
|
coords += 2;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
coords = new float[2 * (points + extrapoints)];
|
||||||
|
|
||||||
for (int i = 0; i < points; ++i, phi += angle_shift)
|
for (int i = 0; i < points; ++i, phi += angle_shift)
|
||||||
{
|
{
|
||||||
coords[2*i] = x + radius * cosf(phi);
|
coords[2*i] = x + radius * cosf(phi);
|
||||||
@@ -982,7 +1028,10 @@ void Graphics::circle(DrawMode mode, float x, float y, float radius, int points)
|
|||||||
coords[2*points] = coords[0];
|
coords[2*points] = coords[0];
|
||||||
coords[2*points+1] = coords[1];
|
coords[2*points+1] = coords[1];
|
||||||
|
|
||||||
polygon(mode, coords, (points + 1) * 2);
|
if (mode == DRAW_FILL)
|
||||||
|
coords -= 2;
|
||||||
|
|
||||||
|
polygon(mode, coords, 2 * (points + extrapoints));
|
||||||
|
|
||||||
delete[] coords;
|
delete[] coords;
|
||||||
}
|
}
|
||||||
@@ -1059,7 +1108,7 @@ void Graphics::polygon(DrawMode mode, const float *coords, size_t count)
|
|||||||
gl.enableVertexAttribArray(OpenGL::ATTRIB_POS);
|
gl.enableVertexAttribArray(OpenGL::ATTRIB_POS);
|
||||||
gl.setVertexAttribArray(OpenGL::ATTRIB_POS, 2, GL_FLOAT, 0, (GLvoid *) coords);
|
gl.setVertexAttribArray(OpenGL::ATTRIB_POS, 2, GL_FLOAT, 0, (GLvoid *) coords);
|
||||||
|
|
||||||
glDrawArrays(GL_TRIANGLE_FAN, 0, count/2-1); // opengl will close the polygon for us
|
glDrawArrays(GL_TRIANGLE_FAN, 0, count / 2);
|
||||||
|
|
||||||
gl.disableVertexAttribArray(OpenGL::ATTRIB_POS);
|
gl.disableVertexAttribArray(OpenGL::ATTRIB_POS);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -118,6 +118,9 @@ public:
|
|||||||
virtual bool setMode(int width, int height, bool &sRGB);
|
virtual bool setMode(int width, int height, bool &sRGB);
|
||||||
virtual void unSetMode();
|
virtual void unSetMode();
|
||||||
|
|
||||||
|
virtual void setActive(bool active);
|
||||||
|
virtual bool isActive() const;
|
||||||
|
|
||||||
void setDebug(bool enable);
|
void setDebug(bool enable);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -194,8 +197,8 @@ public:
|
|||||||
/**
|
/**
|
||||||
* Creates an Image object with padding and/or optimization.
|
* Creates an Image object with padding and/or optimization.
|
||||||
**/
|
**/
|
||||||
Image *newImage(love::image::ImageData *data, Texture::Format format = Texture::FORMAT_NORMAL);
|
Image *newImage(love::image::ImageData *data, Image::Format format = Image::FORMAT_NORMAL);
|
||||||
Image *newImage(love::image::CompressedData *cdata, Texture::Format format = Texture::FORMAT_NORMAL);
|
Image *newImage(love::image::CompressedData *cdata, Image::Format format = Image::FORMAT_NORMAL);
|
||||||
|
|
||||||
Quad *newQuad(Quad::Viewport v, float sw, float sh);
|
Quad *newQuad(Quad::Viewport v, float sw, float sh);
|
||||||
|
|
||||||
@@ -208,7 +211,7 @@ public:
|
|||||||
|
|
||||||
ParticleSystem *newParticleSystem(Texture *texture, int size);
|
ParticleSystem *newParticleSystem(Texture *texture, int size);
|
||||||
|
|
||||||
Canvas *newCanvas(int width, int height, Texture::Format format = Texture::FORMAT_NORMAL, int fsaa = 0);
|
Canvas *newCanvas(int width, int height, Canvas::Format format = Canvas::FORMAT_NORMAL, int fsaa = 0);
|
||||||
|
|
||||||
Shader *newShader(const Shader::ShaderSources &sources);
|
Shader *newShader(const Shader::ShaderSources &sources);
|
||||||
|
|
||||||
@@ -480,6 +483,7 @@ private:
|
|||||||
int width;
|
int width;
|
||||||
int height;
|
int height;
|
||||||
bool created;
|
bool created;
|
||||||
|
bool active;
|
||||||
|
|
||||||
bool activeStencil;
|
bool activeStencil;
|
||||||
|
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ float Image::maxMipmapSharpness = 0.0f;
|
|||||||
Texture::FilterMode Image::defaultMipmapFilter = Texture::FILTER_NONE;
|
Texture::FilterMode Image::defaultMipmapFilter = Texture::FILTER_NONE;
|
||||||
float Image::defaultMipmapSharpness = 0.0f;
|
float Image::defaultMipmapSharpness = 0.0f;
|
||||||
|
|
||||||
Image::Image(love::image::ImageData *data, Texture::Format format)
|
Image::Image(love::image::ImageData *data, Format format)
|
||||||
: data(data)
|
: data(data)
|
||||||
, cdata(nullptr)
|
, cdata(nullptr)
|
||||||
, paddedWidth(width)
|
, paddedWidth(width)
|
||||||
@@ -55,7 +55,7 @@ Image::Image(love::image::ImageData *data, Texture::Format format)
|
|||||||
preload();
|
preload();
|
||||||
}
|
}
|
||||||
|
|
||||||
Image::Image(love::image::CompressedData *cdata, Texture::Format format)
|
Image::Image(love::image::CompressedData *cdata, Format format)
|
||||||
: data(nullptr)
|
: data(nullptr)
|
||||||
, cdata(cdata)
|
, cdata(cdata)
|
||||||
, paddedWidth(width)
|
, paddedWidth(width)
|
||||||
@@ -504,7 +504,7 @@ bool Image::refresh()
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Texture::Format Image::getFormat() const
|
Image::Format Image::getFormat() const
|
||||||
{
|
{
|
||||||
return format;
|
return format;
|
||||||
}
|
}
|
||||||
@@ -602,6 +602,20 @@ GLenum Image::getCompressedFormat(image::CompressedData::Format cformat) const
|
|||||||
return GL_COMPRESSED_RG_RGTC2;
|
return GL_COMPRESSED_RG_RGTC2;
|
||||||
case image::CompressedData::FORMAT_BC5s:
|
case image::CompressedData::FORMAT_BC5s:
|
||||||
return GL_COMPRESSED_SIGNED_RG_RGTC2;
|
return GL_COMPRESSED_SIGNED_RG_RGTC2;
|
||||||
|
case image::CompressedData::FORMAT_ETC1:
|
||||||
|
// The ETC2 format can load ETC1 textures.
|
||||||
|
if (GLAD_VERSION_4_3 || GLAD_ES_VERSION_3_0 || GLAD_ARB_ES3_compatibility)
|
||||||
|
return GL_COMPRESSED_RGB8_ETC2;
|
||||||
|
else
|
||||||
|
return GL_ETC1_RGB8_OES;
|
||||||
|
case image::CompressedData::FORMAT_PVR1_RGB2:
|
||||||
|
return GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG;
|
||||||
|
case image::CompressedData::FORMAT_PVR1_RGB4:
|
||||||
|
return GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG;
|
||||||
|
case image::CompressedData::FORMAT_PVR1_RGBA2:
|
||||||
|
return GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG;
|
||||||
|
case image::CompressedData::FORMAT_PVR1_RGBA4:
|
||||||
|
return GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG;
|
||||||
default:
|
default:
|
||||||
if (srgb)
|
if (srgb)
|
||||||
return GL_SRGB_ALPHA;
|
return GL_SRGB_ALPHA;
|
||||||
@@ -635,14 +649,24 @@ bool Image::hasCompressedTextureSupport(image::CompressedData::Format format)
|
|||||||
switch (format)
|
switch (format)
|
||||||
{
|
{
|
||||||
case image::CompressedData::FORMAT_DXT1:
|
case image::CompressedData::FORMAT_DXT1:
|
||||||
|
return GLAD_EXT_texture_compression_s3tc || GLAD_EXT_texture_compression_dxt1;
|
||||||
case image::CompressedData::FORMAT_DXT3:
|
case image::CompressedData::FORMAT_DXT3:
|
||||||
|
return GLAD_EXT_texture_compression_s3tc || GLAD_ANGLE_texture_compression_dxt3;
|
||||||
case image::CompressedData::FORMAT_DXT5:
|
case image::CompressedData::FORMAT_DXT5:
|
||||||
return GLAD_EXT_texture_compression_s3tc;
|
return GLAD_EXT_texture_compression_s3tc || GLAD_ANGLE_texture_compression_dxt5;
|
||||||
case image::CompressedData::FORMAT_BC4:
|
case image::CompressedData::FORMAT_BC4:
|
||||||
case image::CompressedData::FORMAT_BC4s:
|
case image::CompressedData::FORMAT_BC4s:
|
||||||
case image::CompressedData::FORMAT_BC5:
|
case image::CompressedData::FORMAT_BC5:
|
||||||
case image::CompressedData::FORMAT_BC5s:
|
case image::CompressedData::FORMAT_BC5s:
|
||||||
return (GLAD_VERSION_3_0 || GLAD_ARB_texture_compression_rgtc || GLAD_EXT_texture_compression_rgtc);
|
return (GLAD_VERSION_3_0 || GLAD_ARB_texture_compression_rgtc || GLAD_EXT_texture_compression_rgtc);
|
||||||
|
case image::CompressedData::FORMAT_ETC1:
|
||||||
|
// ETC2 support guarantees ETC1 support as well.
|
||||||
|
return GLAD_VERSION_4_3 || GLAD_ES_VERSION_3_0 || GLAD_ARB_ES3_compatibility || GLAD_OES_compressed_ETC1_RGB8_texture;
|
||||||
|
case image::CompressedData::FORMAT_PVR1_RGB2:
|
||||||
|
case image::CompressedData::FORMAT_PVR1_RGB4:
|
||||||
|
case image::CompressedData::FORMAT_PVR1_RGBA2:
|
||||||
|
case image::CompressedData::FORMAT_PVR1_RGBA4:
|
||||||
|
return GLAD_IMG_texture_compression_pvrtc;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -655,6 +679,24 @@ bool Image::hasSRGBSupport()
|
|||||||
return GLAD_VERSION_2_1 || GLAD_EXT_texture_sRGB || GLAD_EXT_sRGB;
|
return GLAD_VERSION_2_1 || GLAD_EXT_texture_sRGB || GLAD_EXT_sRGB;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Image::getConstant(const char *in, Format &out)
|
||||||
|
{
|
||||||
|
return formats.find(in, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Image::getConstant(Format in, const char *&out)
|
||||||
|
{
|
||||||
|
return formats.find(in, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
StringMap<Image::Format, Image::FORMAT_MAX_ENUM>::Entry Image::formatEntries[] =
|
||||||
|
{
|
||||||
|
{"normal", Image::FORMAT_NORMAL},
|
||||||
|
{"srgb", Image::FORMAT_SRGB},
|
||||||
|
};
|
||||||
|
|
||||||
|
StringMap<Image::Format, Image::FORMAT_MAX_ENUM> Image::formats(Image::formatEntries, sizeof(Image::formatEntries));
|
||||||
|
|
||||||
} // opengl
|
} // opengl
|
||||||
} // graphics
|
} // graphics
|
||||||
} // love
|
} // love
|
||||||
|
|||||||
@@ -25,6 +25,7 @@
|
|||||||
#include "common/config.h"
|
#include "common/config.h"
|
||||||
#include "common/Matrix.h"
|
#include "common/Matrix.h"
|
||||||
#include "common/Vector.h"
|
#include "common/Vector.h"
|
||||||
|
#include "common/StringMap.h"
|
||||||
#include "common/math.h"
|
#include "common/math.h"
|
||||||
#include "image/ImageData.h"
|
#include "image/ImageData.h"
|
||||||
#include "image/CompressedData.h"
|
#include "image/CompressedData.h"
|
||||||
@@ -50,20 +51,27 @@ class Image : public Texture
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
enum Format
|
||||||
|
{
|
||||||
|
FORMAT_NORMAL,
|
||||||
|
FORMAT_SRGB,
|
||||||
|
FORMAT_MAX_ENUM
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new Image. Not that anything is ready to use
|
* Creates a new Image. Not that anything is ready to use
|
||||||
* before load is called.
|
* before load is called.
|
||||||
*
|
*
|
||||||
* @param data The data from which to load the image.
|
* @param data The data from which to load the image.
|
||||||
**/
|
**/
|
||||||
Image(love::image::ImageData *data, Texture::Format format = Texture::FORMAT_NORMAL);
|
Image(love::image::ImageData *data, Format format = FORMAT_NORMAL);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new Image with compressed image data.
|
* Creates a new Image with compressed image data.
|
||||||
*
|
*
|
||||||
* @param cdata The compressed data from which to load the image.
|
* @param cdata The compressed data from which to load the image.
|
||||||
**/
|
**/
|
||||||
Image(love::image::CompressedData *cdata, Texture::Format format = Texture::FORMAT_NORMAL);
|
Image(love::image::CompressedData *cdata, Format format = FORMAT_NORMAL);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Destructor. Deletes the hardware texture and other resources.
|
* Destructor. Deletes the hardware texture and other resources.
|
||||||
@@ -120,7 +128,7 @@ public:
|
|||||||
**/
|
**/
|
||||||
bool refresh();
|
bool refresh();
|
||||||
|
|
||||||
Texture::Format getFormat() const;
|
Format getFormat() const;
|
||||||
|
|
||||||
static void setDefaultMipmapSharpness(float sharpness);
|
static void setDefaultMipmapSharpness(float sharpness);
|
||||||
static float getDefaultMipmapSharpness();
|
static float getDefaultMipmapSharpness();
|
||||||
@@ -136,6 +144,9 @@ public:
|
|||||||
|
|
||||||
static bool hasSRGBSupport();
|
static bool hasSRGBSupport();
|
||||||
|
|
||||||
|
static bool getConstant(const char *in, Format &out);
|
||||||
|
static bool getConstant(Format in, const char *&out);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
void uploadDefaultTexture();
|
void uploadDefaultTexture();
|
||||||
@@ -165,8 +176,8 @@ private:
|
|||||||
// Whether this Image is using a compressed texture.
|
// Whether this Image is using a compressed texture.
|
||||||
bool compressed;
|
bool compressed;
|
||||||
|
|
||||||
// The format to interpret the texture's data as.
|
// The format to interpret the image's data as.
|
||||||
Texture::Format format;
|
Format format;
|
||||||
|
|
||||||
// True if the image wasn't able to be properly created and it had to fall
|
// True if the image wasn't able to be properly created and it had to fall
|
||||||
// back to a default texture.
|
// back to a default texture.
|
||||||
@@ -188,6 +199,9 @@ private:
|
|||||||
|
|
||||||
GLenum getCompressedFormat(image::CompressedData::Format cformat) const;
|
GLenum getCompressedFormat(image::CompressedData::Format cformat) const;
|
||||||
|
|
||||||
|
static StringMap<Format, FORMAT_MAX_ENUM>::Entry formatEntries[];
|
||||||
|
static StringMap<Format, FORMAT_MAX_ENUM> formats;
|
||||||
|
|
||||||
}; // Image
|
}; // Image
|
||||||
|
|
||||||
} // opengl
|
} // opengl
|
||||||
|
|||||||
@@ -160,6 +160,7 @@ void OpenGL::initVendor()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// http://feedback.wildfiregames.com/report/opengl/feature/GL_VENDOR
|
// http://feedback.wildfiregames.com/report/opengl/feature/GL_VENDOR
|
||||||
|
// http://stackoverflow.com/questions/2093594/opengl-extensions-available-on-different-android-devices
|
||||||
if (strstr(vstr, "ATI Technologies"))
|
if (strstr(vstr, "ATI Technologies"))
|
||||||
vendor = VENDOR_ATI_AMD;
|
vendor = VENDOR_ATI_AMD;
|
||||||
else if (strstr(vstr, "NVIDIA"))
|
else if (strstr(vstr, "NVIDIA"))
|
||||||
@@ -172,6 +173,16 @@ void OpenGL::initVendor()
|
|||||||
vendor = VENDOR_APPLE;
|
vendor = VENDOR_APPLE;
|
||||||
else if (strstr(vstr, "Microsoft"))
|
else if (strstr(vstr, "Microsoft"))
|
||||||
vendor = VENDOR_MICROSOFT;
|
vendor = VENDOR_MICROSOFT;
|
||||||
|
else if (strstr(vstr, "Imagination"))
|
||||||
|
vendor = VENDOR_IMGTEC;
|
||||||
|
else if (strstr(vstr, "ARM"))
|
||||||
|
vendor = VENDOR_ARM;
|
||||||
|
else if (strstr(vstr, "Qualcomm"))
|
||||||
|
vendor = VENDOR_QUALCOMM;
|
||||||
|
else if (strstr(vstr, "Broadcom"))
|
||||||
|
vendor = VENDOR_BROADCOM;
|
||||||
|
else if (strstr(vstr, "Vivante"))
|
||||||
|
vendor = VENDOR_VIVANTE;
|
||||||
else
|
else
|
||||||
vendor = VENDOR_UNKNOWN;
|
vendor = VENDOR_UNKNOWN;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,8 +66,13 @@ public:
|
|||||||
VENDOR_NVIDIA,
|
VENDOR_NVIDIA,
|
||||||
VENDOR_INTEL,
|
VENDOR_INTEL,
|
||||||
VENDOR_MESA_SOFT, // Software renderer.
|
VENDOR_MESA_SOFT, // Software renderer.
|
||||||
VENDOR_APPLE, // Software renderer.
|
VENDOR_APPLE, // Software renderer (or Apple A7 chips and newer.)
|
||||||
VENDOR_MICROSOFT, // Software renderer.
|
VENDOR_MICROSOFT, // Software renderer.
|
||||||
|
VENDOR_IMGTEC,
|
||||||
|
VENDOR_ARM,
|
||||||
|
VENDOR_QUALCOMM,
|
||||||
|
VENDOR_BROADCOM,
|
||||||
|
VENDOR_VIVANTE,
|
||||||
VENDOR_UNKNOWN
|
VENDOR_UNKNOWN
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -101,10 +101,9 @@ Shader::~Shader()
|
|||||||
detach();
|
detach();
|
||||||
|
|
||||||
for (auto it = boundRetainables.begin(); it != boundRetainables.end(); ++it)
|
for (auto it = boundRetainables.begin(); it != boundRetainables.end(); ++it)
|
||||||
{
|
|
||||||
it->second->release();
|
it->second->release();
|
||||||
boundRetainables.erase(it);
|
|
||||||
}
|
boundRetainables.clear();
|
||||||
|
|
||||||
unloadVolatile();
|
unloadVolatile();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -189,10 +189,18 @@ void VBO::unmap()
|
|||||||
is_bound = true;
|
is_bound = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// "orphan" current buffer to avoid implicit synchronisation on the GPU:
|
if (getUsage() == GL_STATIC_DRAW)
|
||||||
// http://www.seas.upenn.edu/~pcozzi/OpenGLInsights/OpenGLInsights-AsynchronousBufferTransfers.pdf
|
{
|
||||||
glBufferData(getTarget(), (GLsizeiptr) getSize(), NULL, getUsage());
|
// Upload the mapped data to the buffer.
|
||||||
glBufferData(getTarget(), (GLsizeiptr) getSize(), memory_map, getUsage());
|
glBufferSubData(getTarget(), 0, (GLsizeiptr) getSize(), memory_map);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// "orphan" current buffer to avoid implicit synchronisation on the GPU:
|
||||||
|
// http://www.seas.upenn.edu/~pcozzi/OpenGLInsights/OpenGLInsights-AsynchronousBufferTransfers.pdf
|
||||||
|
glBufferData(getTarget(), (GLsizeiptr) getSize(), NULL, getUsage());
|
||||||
|
glBufferData(getTarget(), (GLsizeiptr) getSize(), memory_map, getUsage());
|
||||||
|
}
|
||||||
|
|
||||||
is_mapped = false;
|
is_mapped = false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -113,10 +113,10 @@ int w_Canvas_clear(lua_State *L)
|
|||||||
int w_Canvas_getFormat(lua_State *L)
|
int w_Canvas_getFormat(lua_State *L)
|
||||||
{
|
{
|
||||||
Canvas *canvas = luax_checkcanvas(L, 1);
|
Canvas *canvas = luax_checkcanvas(L, 1);
|
||||||
Texture::Format format = canvas->getTextureFormat();
|
Canvas::Format format = canvas->getTextureFormat();
|
||||||
const char *str;
|
const char *str;
|
||||||
if (!Texture::getConstant(format, str))
|
if (!Canvas::getConstant(format, str))
|
||||||
return luaL_error(L, "Unknown texture format.");
|
return luaL_error(L, "Unknown Canvas format.");
|
||||||
|
|
||||||
lua_pushstring(L, str);
|
lua_pushstring(L, str);
|
||||||
return 1;
|
return 1;
|
||||||
|
|||||||
@@ -60,6 +60,12 @@ int w_isCreated(lua_State *L)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int w_isActive(lua_State *L)
|
||||||
|
{
|
||||||
|
luax_pushboolean(L, instance->isActive());
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
int w_getWidth(lua_State *L)
|
int w_getWidth(lua_State *L)
|
||||||
{
|
{
|
||||||
lua_pushinteger(L, instance->getWidth());
|
lua_pushinteger(L, instance->getWidth());
|
||||||
@@ -155,14 +161,11 @@ int w_newImage(lua_State *L)
|
|||||||
love::image::ImageData *data = nullptr;
|
love::image::ImageData *data = nullptr;
|
||||||
love::image::CompressedData *cdata = nullptr;
|
love::image::CompressedData *cdata = nullptr;
|
||||||
|
|
||||||
Texture::Format format = Texture::FORMAT_NORMAL;
|
Image::Format format = Image::FORMAT_NORMAL;
|
||||||
const char *fstr = lua_isnoneornil(L, 2) ? nullptr : luaL_checkstring(L, 2);
|
const char *fstr = lua_isnoneornil(L, 2) ? nullptr : luaL_checkstring(L, 2);
|
||||||
|
|
||||||
if (fstr != nullptr && !Texture::getConstant(fstr, format))
|
if (fstr != nullptr && !Image::getConstant(fstr, format))
|
||||||
return luaL_error(L, "Invalid texture format: %s", fstr);
|
return luaL_error(L, "Invalid Image format: %s", fstr);
|
||||||
|
|
||||||
if (format == Texture::FORMAT_HDR) // For now...
|
|
||||||
return luaL_error(L, "HDR images are not supported.");
|
|
||||||
|
|
||||||
// Convert to FileData, if necessary.
|
// Convert to FileData, if necessary.
|
||||||
if (lua_isstring(L, 1) || luax_istype(L, 1, FILESYSTEM_FILE_T))
|
if (lua_isstring(L, 1) || luax_istype(L, 1, FILESYSTEM_FILE_T))
|
||||||
@@ -334,9 +337,9 @@ int w_newCanvas(lua_State *L)
|
|||||||
const char *str = luaL_optstring(L, 3, "normal");
|
const char *str = luaL_optstring(L, 3, "normal");
|
||||||
int fsaa = luaL_optint(L, 4, 0);
|
int fsaa = luaL_optint(L, 4, 0);
|
||||||
|
|
||||||
Texture::Format format;
|
Canvas::Format format;
|
||||||
if (!Texture::getConstant(str, format))
|
if (!Canvas::getConstant(str, format))
|
||||||
return luaL_error(L, "Invalid texture format: %s", str);
|
return luaL_error(L, "Invalid Canvas format: %s", str);
|
||||||
|
|
||||||
Canvas *canvas = nullptr;
|
Canvas *canvas = nullptr;
|
||||||
EXCEPT_GUARD(canvas = instance->newCanvas(width, height, format, fsaa);)
|
EXCEPT_GUARD(canvas = instance->newCanvas(width, height, format, fsaa);)
|
||||||
@@ -1015,7 +1018,7 @@ int w_isSupported(lua_State *L)
|
|||||||
supported = false;
|
supported = false;
|
||||||
break;
|
break;
|
||||||
case Graphics::SUPPORT_HDR_CANVAS:
|
case Graphics::SUPPORT_HDR_CANVAS:
|
||||||
if (!Canvas::isHDRSupported())
|
if (!Canvas::isFormatSupported(Canvas::FORMAT_HDR))
|
||||||
supported = false;
|
supported = false;
|
||||||
break;
|
break;
|
||||||
case Graphics::SUPPORT_MULTI_CANVAS:
|
case Graphics::SUPPORT_MULTI_CANVAS:
|
||||||
@@ -1046,12 +1049,20 @@ int w_isSupported(lua_State *L)
|
|||||||
if (!Image::hasCompressedTextureSupport(image::CompressedData::FORMAT_BC5))
|
if (!Image::hasCompressedTextureSupport(image::CompressedData::FORMAT_BC5))
|
||||||
supported = false;
|
supported = false;
|
||||||
break;
|
break;
|
||||||
|
case Graphics::SUPPORT_ETC1:
|
||||||
|
if (!Image::hasCompressedTextureSupport(image::CompressedData::FORMAT_ETC1))
|
||||||
|
supported = false;
|
||||||
|
break;
|
||||||
|
case Graphics::SUPPORT_PVRTC1:
|
||||||
|
if (!Image::hasCompressedTextureSupport(image::CompressedData::FORMAT_PVR1_RGBA4))
|
||||||
|
supported = false;
|
||||||
|
break;
|
||||||
case Graphics::SUPPORT_INSTANCING:
|
case Graphics::SUPPORT_INSTANCING:
|
||||||
if (!(GLAD_ARB_draw_instanced || (GLAD_ES_VERSION_2_0 && GLAD_EXT_draw_instanced)))
|
if (!(GLAD_ARB_draw_instanced || (GLAD_ES_VERSION_2_0 && GLAD_EXT_draw_instanced)))
|
||||||
supported = false;
|
supported = false;
|
||||||
break;
|
break;
|
||||||
case Graphics::SUPPORT_SRGB:
|
case Graphics::SUPPORT_SRGB:
|
||||||
if (!Canvas::isSRGBSupported())
|
if (!Canvas::isFormatSupported(Canvas::FORMAT_SRGB))
|
||||||
supported = false;
|
supported = false;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@@ -1064,6 +1075,18 @@ int w_isSupported(lua_State *L)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int w_hasCanvasFormat(lua_State *L)
|
||||||
|
{
|
||||||
|
const char *str = luaL_checkstring(L, 1);
|
||||||
|
Canvas::Format format;
|
||||||
|
|
||||||
|
if (!Canvas::getConstant(str, format))
|
||||||
|
return luaL_error(L, "Invalid canvas format: %s", str);
|
||||||
|
|
||||||
|
luax_pushboolean(L, Canvas::isFormatSupported(format));
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
int w_getRendererInfo(lua_State *L)
|
int w_getRendererInfo(lua_State *L)
|
||||||
{
|
{
|
||||||
std::string name, version, vendor, device;
|
std::string name, version, vendor, device;
|
||||||
@@ -1441,6 +1464,7 @@ static const luaL_Reg functions[] =
|
|||||||
{ "_setDefaultShaderCode", w_setDefaultShaderCode },
|
{ "_setDefaultShaderCode", w_setDefaultShaderCode },
|
||||||
|
|
||||||
{ "isSupported", w_isSupported },
|
{ "isSupported", w_isSupported },
|
||||||
|
{ "hasCanvasFormat", w_hasCanvasFormat },
|
||||||
{ "getRendererInfo", w_getRendererInfo },
|
{ "getRendererInfo", w_getRendererInfo },
|
||||||
{ "getSystemLimit", w_getSystemLimit },
|
{ "getSystemLimit", w_getSystemLimit },
|
||||||
|
|
||||||
@@ -1450,6 +1474,7 @@ static const luaL_Reg functions[] =
|
|||||||
{ "printf", w_printf },
|
{ "printf", w_printf },
|
||||||
|
|
||||||
{ "isCreated", w_isCreated },
|
{ "isCreated", w_isCreated },
|
||||||
|
{ "isActive", w_isActive },
|
||||||
{ "getWidth", w_getWidth },
|
{ "getWidth", w_getWidth },
|
||||||
{ "getHeight", w_getHeight },
|
{ "getHeight", w_getHeight },
|
||||||
{ "getDimensions", w_getDimensions },
|
{ "getDimensions", w_getDimensions },
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ int w_reset(lua_State *L);
|
|||||||
int w_clear(lua_State *L);
|
int w_clear(lua_State *L);
|
||||||
int w_present(lua_State *L);
|
int w_present(lua_State *L);
|
||||||
int w_isCreated(lua_State *L);
|
int w_isCreated(lua_State *L);
|
||||||
|
int w_isActive(lua_State *L);
|
||||||
int w_getWidth(lua_State *L);
|
int w_getWidth(lua_State *L);
|
||||||
int w_getHeight(lua_State *L);
|
int w_getHeight(lua_State *L);
|
||||||
int w_getDimensions(lua_State *L);
|
int w_getDimensions(lua_State *L);
|
||||||
@@ -94,6 +95,7 @@ int w_setShader(lua_State *L);
|
|||||||
int w_getShader(lua_State *L);
|
int w_getShader(lua_State *L);
|
||||||
int w_setDefaultShaderCode(lua_State *L);
|
int w_setDefaultShaderCode(lua_State *L);
|
||||||
int w_isSupported(lua_State *L);
|
int w_isSupported(lua_State *L);
|
||||||
|
int w_hasCanvasFormat(lua_State *L);
|
||||||
int w_getRendererInfo(lua_State *L);
|
int w_getRendererInfo(lua_State *L);
|
||||||
int w_getSystemLimit(lua_State *L);
|
int w_getSystemLimit(lua_State *L);
|
||||||
int w_draw(lua_State *L);
|
int w_draw(lua_State *L);
|
||||||
|
|||||||
@@ -110,6 +110,11 @@ StringMap<CompressedData::Format, CompressedData::FORMAT_MAX_ENUM>::Entry Compre
|
|||||||
{"bc4s", CompressedData::FORMAT_BC4s},
|
{"bc4s", CompressedData::FORMAT_BC4s},
|
||||||
{"bc5", CompressedData::FORMAT_BC5},
|
{"bc5", CompressedData::FORMAT_BC5},
|
||||||
{"bc5s", CompressedData::FORMAT_BC5s},
|
{"bc5s", CompressedData::FORMAT_BC5s},
|
||||||
|
{"etc1", CompressedData::FORMAT_ETC1},
|
||||||
|
{"pvr1rgb2", CompressedData::FORMAT_PVR1_RGB2},
|
||||||
|
{"pvr1rgb4", CompressedData::FORMAT_PVR1_RGB4},
|
||||||
|
{"pvr1rgba2", CompressedData::FORMAT_PVR1_RGBA2},
|
||||||
|
{"pvr1rgba4", CompressedData::FORMAT_PVR1_RGBA4},
|
||||||
};
|
};
|
||||||
|
|
||||||
StringMap<CompressedData::Format, CompressedData::FORMAT_MAX_ENUM> CompressedData::formats(CompressedData::formatEntries, sizeof(CompressedData::formatEntries));
|
StringMap<CompressedData::Format, CompressedData::FORMAT_MAX_ENUM> CompressedData::formats(CompressedData::formatEntries, sizeof(CompressedData::formatEntries));
|
||||||
|
|||||||
@@ -54,6 +54,11 @@ public:
|
|||||||
FORMAT_BC4s,
|
FORMAT_BC4s,
|
||||||
FORMAT_BC5,
|
FORMAT_BC5,
|
||||||
FORMAT_BC5s,
|
FORMAT_BC5s,
|
||||||
|
FORMAT_ETC1,
|
||||||
|
FORMAT_PVR1_RGB2,
|
||||||
|
FORMAT_PVR1_RGB4,
|
||||||
|
FORMAT_PVR1_RGBA2,
|
||||||
|
FORMAT_PVR1_RGBA4,
|
||||||
FORMAT_MAX_ENUM
|
FORMAT_MAX_ENUM
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -29,29 +29,47 @@ namespace image
|
|||||||
namespace magpie
|
namespace magpie
|
||||||
{
|
{
|
||||||
|
|
||||||
CompressedData::CompressedData(love::filesystem::FileData *filedata)
|
CompressedData::CompressedData(std::list<CompressedFormatHandler *> formats, love::filesystem::FileData *filedata)
|
||||||
|
: formatHandlers(formats)
|
||||||
{
|
{
|
||||||
|
for (CompressedFormatHandler *handler : formatHandlers)
|
||||||
|
handler->retain();
|
||||||
|
|
||||||
load(filedata);
|
load(filedata);
|
||||||
}
|
}
|
||||||
|
|
||||||
CompressedData::~CompressedData()
|
CompressedData::~CompressedData()
|
||||||
{
|
{
|
||||||
delete[] data;
|
delete[] data;
|
||||||
|
|
||||||
|
for (CompressedFormatHandler *handler : formatHandlers)
|
||||||
|
handler->release();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CompressedData::load(love::filesystem::FileData *filedata)
|
void CompressedData::load(love::filesystem::FileData *filedata)
|
||||||
{
|
{
|
||||||
|
CompressedFormatHandler *parser = nullptr;
|
||||||
|
|
||||||
|
for (CompressedFormatHandler *handler : formatHandlers)
|
||||||
|
{
|
||||||
|
if (handler->canParse(filedata))
|
||||||
|
{
|
||||||
|
parser = handler;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parser == nullptr)
|
||||||
|
throw love::Exception("Could not parse compressed data: Unknown format.");
|
||||||
|
|
||||||
// SubImage vector will be populated by a parser.
|
// SubImage vector will be populated by a parser.
|
||||||
std::vector<SubImage> parsedimages;
|
std::vector<SubImage> parsedimages;
|
||||||
Format texformat = FORMAT_UNKNOWN;
|
Format texformat = FORMAT_UNKNOWN;
|
||||||
|
|
||||||
uint8 *newdata = 0;
|
|
||||||
size_t newdata_size = 0;
|
size_t newdata_size = 0;
|
||||||
|
uint8 *newdata = parser->parse(filedata, parsedimages, newdata_size, texformat);
|
||||||
|
|
||||||
if (ddsHandler::canParse(filedata))
|
if (newdata == nullptr)
|
||||||
newdata = ddsHandler::parse(filedata, parsedimages, newdata_size, texformat);
|
|
||||||
|
|
||||||
if (newdata == 0)
|
|
||||||
throw love::Exception("Could not parse compressed data.");
|
throw love::Exception("Could not parse compressed data.");
|
||||||
|
|
||||||
if (texformat == FORMAT_UNKNOWN)
|
if (texformat == FORMAT_UNKNOWN)
|
||||||
@@ -76,14 +94,6 @@ void CompressedData::load(love::filesystem::FileData *filedata)
|
|||||||
format = texformat;
|
format = texformat;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CompressedData::isCompressed(love::filesystem::FileData *filedata)
|
|
||||||
{
|
|
||||||
if (ddsHandler::canParse(filedata))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // magpie
|
} // magpie
|
||||||
} // image
|
} // image
|
||||||
} // love
|
} // love
|
||||||
|
|||||||
@@ -22,9 +22,13 @@
|
|||||||
#define LOVE_IMAGE_MAGPIE_COMPRESSED_DATA_H
|
#define LOVE_IMAGE_MAGPIE_COMPRESSED_DATA_H
|
||||||
|
|
||||||
// LOVE
|
// LOVE
|
||||||
|
#include "CompressedFormatHandler.h"
|
||||||
#include "filesystem/FileData.h"
|
#include "filesystem/FileData.h"
|
||||||
#include "image/CompressedData.h"
|
#include "image/CompressedData.h"
|
||||||
|
|
||||||
|
// C++
|
||||||
|
#include <list>
|
||||||
|
|
||||||
namespace love
|
namespace love
|
||||||
{
|
{
|
||||||
namespace image
|
namespace image
|
||||||
@@ -36,15 +40,16 @@ class CompressedData : public love::image::CompressedData
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
CompressedData(love::filesystem::FileData *filedata);
|
CompressedData(std::list<CompressedFormatHandler *> formats, love::filesystem::FileData *filedata);
|
||||||
virtual ~CompressedData();
|
virtual ~CompressedData();
|
||||||
|
|
||||||
static bool isCompressed(love::filesystem::FileData *filedata);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
void load(love::filesystem::FileData *filedata);
|
void load(love::filesystem::FileData *filedata);
|
||||||
|
|
||||||
|
// Compressed image format handlers we use for parsing.
|
||||||
|
std::list<CompressedFormatHandler *> formatHandlers;
|
||||||
|
|
||||||
}; // CompressedData
|
}; // CompressedData
|
||||||
|
|
||||||
} // magpie
|
} // magpie
|
||||||
|
|||||||
@@ -0,0 +1,74 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2006-2014 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.
|
||||||
|
**/
|
||||||
|
|
||||||
|
#ifndef LOVE_IMAGE_MAGPIE_COMPRESSED_HANDLER_H
|
||||||
|
#define LOVE_IMAGE_MAGPIE_COMPRESSED_HANDLER_H
|
||||||
|
|
||||||
|
// LOVE
|
||||||
|
#include "filesystem/FileData.h"
|
||||||
|
#include "image/CompressedData.h"
|
||||||
|
#include "common/Object.h"
|
||||||
|
|
||||||
|
namespace love
|
||||||
|
{
|
||||||
|
namespace image
|
||||||
|
{
|
||||||
|
namespace magpie
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base class for all CompressedData parser library interfaces.
|
||||||
|
* We inherit from love::Object to take advantage of reference counting...
|
||||||
|
**/
|
||||||
|
class CompressedFormatHandler : public love::Object
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
CompressedFormatHandler() {}
|
||||||
|
virtual ~CompressedFormatHandler() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines whether a particular FileData can be parsed as CompressedData
|
||||||
|
* by this handler.
|
||||||
|
* @param data The data to parse.
|
||||||
|
**/
|
||||||
|
virtual bool canParse(const filesystem::FileData *data) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses compressed image filedata into a list of sub-images and returns
|
||||||
|
* a single block of memory containing all the images.
|
||||||
|
*
|
||||||
|
* @param[in] filedata The data to parse.
|
||||||
|
* @param[out] image The list of sub-images generated. Byte data is a pointer
|
||||||
|
* to the returned data.
|
||||||
|
* @param[out] dataSize The total size in bytes of the returned data.
|
||||||
|
* @param[out] format The format of the Compressed Data.
|
||||||
|
*
|
||||||
|
* @return The single block of memory containing the parsed images.
|
||||||
|
**/
|
||||||
|
virtual uint8 *parse(filesystem::FileData *filedata, std::vector<CompressedData::SubImage> &images, size_t &dataSize, CompressedData::Format &format) = 0;
|
||||||
|
|
||||||
|
}; // CompressedFormatHandler
|
||||||
|
|
||||||
|
} // magpie
|
||||||
|
} // image
|
||||||
|
} // love
|
||||||
|
|
||||||
|
#endif // LOVE_IMAGE_MAGPIE_COMPRESSED_HANDLER_H
|
||||||
@@ -25,6 +25,11 @@
|
|||||||
|
|
||||||
#include "DevilHandler.h"
|
#include "DevilHandler.h"
|
||||||
|
|
||||||
|
#include "ddsHandler.h"
|
||||||
|
#include "PVRHandler.h"
|
||||||
|
#include "KTXHandler.h"
|
||||||
|
#include "PKMHandler.h"
|
||||||
|
|
||||||
namespace love
|
namespace love
|
||||||
{
|
{
|
||||||
namespace image
|
namespace image
|
||||||
@@ -35,14 +40,22 @@ namespace magpie
|
|||||||
Image::Image()
|
Image::Image()
|
||||||
{
|
{
|
||||||
formatHandlers.push_back(new DevilHandler);
|
formatHandlers.push_back(new DevilHandler);
|
||||||
|
|
||||||
|
compressedFormatHandlers.push_back(new DDSHandler);
|
||||||
|
compressedFormatHandlers.push_back(new PVRHandler);
|
||||||
|
compressedFormatHandlers.push_back(new KTXHandler);
|
||||||
|
compressedFormatHandlers.push_back(new PKMHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
Image::~Image()
|
Image::~Image()
|
||||||
{
|
{
|
||||||
// ImageData objects reference the FormatHandlers in our list, so we should
|
// ImageData objects reference the FormatHandlers in our list, so we should
|
||||||
// release them instead of deleting them completely here.
|
// release them instead of deleting them completely here.
|
||||||
for (auto it = formatHandlers.begin(); it != formatHandlers.end(); ++it)
|
for (FormatHandler *handler : formatHandlers)
|
||||||
(*it)->release();
|
handler->release();
|
||||||
|
|
||||||
|
for (CompressedFormatHandler *handler : compressedFormatHandlers)
|
||||||
|
handler->release();
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *Image::getName() const
|
const char *Image::getName() const
|
||||||
@@ -67,12 +80,18 @@ love::image::ImageData *Image::newImageData(int width, int height, void *data, b
|
|||||||
|
|
||||||
love::image::CompressedData *Image::newCompressedData(love::filesystem::FileData *data)
|
love::image::CompressedData *Image::newCompressedData(love::filesystem::FileData *data)
|
||||||
{
|
{
|
||||||
return new CompressedData(data);
|
return new CompressedData(compressedFormatHandlers, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Image::isCompressed(love::filesystem::FileData *data)
|
bool Image::isCompressed(love::filesystem::FileData *data)
|
||||||
{
|
{
|
||||||
return CompressedData::isCompressed(data);
|
for (CompressedFormatHandler *handler : compressedFormatHandlers)
|
||||||
|
{
|
||||||
|
if (handler->canParse(data))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // magpie
|
} // magpie
|
||||||
|
|||||||
@@ -24,6 +24,7 @@
|
|||||||
// LOVE
|
// LOVE
|
||||||
#include "image/Image.h"
|
#include "image/Image.h"
|
||||||
#include "FormatHandler.h"
|
#include "FormatHandler.h"
|
||||||
|
#include "CompressedFormatHandler.h"
|
||||||
|
|
||||||
// C++
|
// C++
|
||||||
#include <list>
|
#include <list>
|
||||||
@@ -63,6 +64,9 @@ private:
|
|||||||
// Image format handlers we can use for decoding and encoding ImageData.
|
// Image format handlers we can use for decoding and encoding ImageData.
|
||||||
std::list<FormatHandler *> formatHandlers;
|
std::list<FormatHandler *> formatHandlers;
|
||||||
|
|
||||||
|
// Compressed image format handers we can use for parsing CompressedData.
|
||||||
|
std::list<CompressedFormatHandler *> compressedFormatHandlers;
|
||||||
|
|
||||||
}; // Image
|
}; // Image
|
||||||
|
|
||||||
} // magpie
|
} // magpie
|
||||||
|
|||||||
@@ -0,0 +1,241 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2006-2014 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.
|
||||||
|
**/
|
||||||
|
|
||||||
|
// LOVE
|
||||||
|
#include "KTXHandler.h"
|
||||||
|
#include "common/int.h"
|
||||||
|
|
||||||
|
// C
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
// C++
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
namespace love
|
||||||
|
{
|
||||||
|
namespace image
|
||||||
|
{
|
||||||
|
namespace magpie
|
||||||
|
{
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
|
||||||
|
#define KTX_IDENTIFIER_REF {0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31, 0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A}
|
||||||
|
#define KTX_ENDIAN_REF (0x04030201)
|
||||||
|
#define KTX_ENDIAN_REF_REV (0x01020304)
|
||||||
|
#define KTX_HEADER_SIZE (64)
|
||||||
|
|
||||||
|
struct KTXHeader
|
||||||
|
{
|
||||||
|
uint8 identifier[12];
|
||||||
|
uint32 endianness;
|
||||||
|
uint32 glType;
|
||||||
|
uint32 glTypeSize;
|
||||||
|
uint32 glFormat;
|
||||||
|
uint32 glInternalFormat;
|
||||||
|
uint32 glBaseInternalFormat;
|
||||||
|
uint32 pixelWidth;
|
||||||
|
uint32 pixelHeight;
|
||||||
|
uint32 pixelDepth;
|
||||||
|
uint32 numberOfArrayElements;
|
||||||
|
uint32 numberOfFaces;
|
||||||
|
uint32 numberOfMipmapLevels;
|
||||||
|
uint32 bytesOfKeyValueData;
|
||||||
|
};
|
||||||
|
|
||||||
|
static_assert(sizeof(KTXHeader) == KTX_HEADER_SIZE, "Real size of KTX header doesn't match struct size!");
|
||||||
|
|
||||||
|
enum KTXGLInternalFormat
|
||||||
|
{
|
||||||
|
KTX_GL_ETC1_RGB8_OES = 0x8D64,
|
||||||
|
|
||||||
|
// LOVE doesn't support EAC or ETC2 yet, but it won't be hard to add.
|
||||||
|
KTX_GL_COMPRESSED_R11_EAC = 0x9270,
|
||||||
|
KTX_GL_COMPRESSED_SIGNED_R11_EAC = 0x9271,
|
||||||
|
KTX_GL_COMPRESSED_RG11_EAC = 0x9272,
|
||||||
|
KTX_GL_COMPRESSED_SIGNED_RG11_EAC = 0x9273,
|
||||||
|
KTX_GL_COMPRESSED_RGB8_ETC2 = 0x9274,
|
||||||
|
KTX_GL_COMPRESSED_SRGB8_ETC2 = 0x9275,
|
||||||
|
KTX_GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 = 0x9276,
|
||||||
|
KTX_GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2 = 0x9277,
|
||||||
|
KTX_GL_COMPRESSED_RGBA8_ETC2_EAC = 0x9278,
|
||||||
|
KTX_GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC = 0x9279,
|
||||||
|
|
||||||
|
// I don't know if any KTX file contains PVR data, but why not support it.
|
||||||
|
KTX_GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG = 0x8C00,
|
||||||
|
KTX_GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG = 0x8C01,
|
||||||
|
KTX_GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG = 0x8C02,
|
||||||
|
KTX_GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG = 0x8C03,
|
||||||
|
|
||||||
|
// Same with DXT1/3/5.
|
||||||
|
KTX_GL_COMPRESSED_RGB_S3TC_DXT1_EXT = 0x83F0,
|
||||||
|
KTX_GL_COMPRESSED_RGBA_S3TC_DXT3_EXT = 0x83F2,
|
||||||
|
KTX_GL_COMPRESSED_RGBA_S3TC_DXT5_EXT = 0x83F3
|
||||||
|
};
|
||||||
|
|
||||||
|
CompressedData::Format convertFormat(uint32 glformat)
|
||||||
|
{
|
||||||
|
switch (glformat)
|
||||||
|
{
|
||||||
|
case KTX_GL_ETC1_RGB8_OES:
|
||||||
|
return CompressedData::FORMAT_ETC1;
|
||||||
|
case KTX_GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG:
|
||||||
|
return CompressedData::FORMAT_PVR1_RGB4;
|
||||||
|
case KTX_GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG:
|
||||||
|
return CompressedData::FORMAT_PVR1_RGB2;
|
||||||
|
case KTX_GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG:
|
||||||
|
return CompressedData::FORMAT_PVR1_RGBA4;
|
||||||
|
case KTX_GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG:
|
||||||
|
return CompressedData::FORMAT_PVR1_RGBA2;
|
||||||
|
case KTX_GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
|
||||||
|
return CompressedData::FORMAT_DXT1;
|
||||||
|
case KTX_GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
|
||||||
|
return CompressedData::FORMAT_DXT3;
|
||||||
|
case KTX_GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
|
||||||
|
return CompressedData::FORMAT_DXT5;
|
||||||
|
default:
|
||||||
|
return CompressedData::FORMAT_UNKNOWN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // Anonymous namespace.
|
||||||
|
|
||||||
|
bool KTXHandler::canParse(const filesystem::FileData *data)
|
||||||
|
{
|
||||||
|
if (data->getSize() < sizeof(KTXHeader))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
KTXHeader *header = (KTXHeader *) data->getData();
|
||||||
|
uint8 ktxidentifier[12] = KTX_IDENTIFIER_REF;
|
||||||
|
|
||||||
|
if (memcmp(header->identifier, ktxidentifier, 12) != 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (header->endianness != KTX_ENDIAN_REF && header->endianness != KTX_ENDIAN_REF_REV)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8 *KTXHandler::parse(filesystem::FileData *filedata, std::vector<CompressedData::SubImage> &images, size_t &dataSize, CompressedData::Format &format)
|
||||||
|
{
|
||||||
|
if (!canParse(filedata))
|
||||||
|
throw love::Exception("Could not decode compressed data (not a KTX file?)");
|
||||||
|
|
||||||
|
KTXHeader header = *(KTXHeader *) filedata->getData();
|
||||||
|
|
||||||
|
if (header.endianness == KTX_ENDIAN_REF_REV)
|
||||||
|
{
|
||||||
|
uint32 *headerArray = (uint32 *) &header.glType;
|
||||||
|
for (int i = 0; i < 12; i++)
|
||||||
|
headerArray[i] = swap32(headerArray[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
header.numberOfMipmapLevels = std::max(header.numberOfMipmapLevels, 1u);
|
||||||
|
|
||||||
|
CompressedData::Format cformat = convertFormat(header.glInternalFormat);
|
||||||
|
|
||||||
|
if (cformat == CompressedData::FORMAT_UNKNOWN)
|
||||||
|
throw love::Exception("Unsupported image format in KTX file.");
|
||||||
|
|
||||||
|
if (header.numberOfArrayElements > 0)
|
||||||
|
throw love::Exception("Texture arrays in KTX files are not supported.");
|
||||||
|
|
||||||
|
if (header.pixelDepth > 1)
|
||||||
|
throw love::Exception("3D textures in KTX files are not supported.");
|
||||||
|
|
||||||
|
if (header.numberOfFaces > 1)
|
||||||
|
throw love::Exception("Cubemap textures in KTX files are not supported.");
|
||||||
|
|
||||||
|
size_t fileoffset = sizeof(KTXHeader) + header.bytesOfKeyValueData;
|
||||||
|
const uint8 *filebytes = (uint8 *) filedata->getData();
|
||||||
|
size_t totalsize = 0;
|
||||||
|
|
||||||
|
// Calculate the total size needed to hold the data in memory.
|
||||||
|
for (int i = 0; i < (int) header.numberOfMipmapLevels; i++)
|
||||||
|
{
|
||||||
|
if (fileoffset + sizeof(uint32) > filedata->getSize())
|
||||||
|
throw love::Exception("Could not parse KTX file: unexpected EOF.");
|
||||||
|
|
||||||
|
uint32 mipsize = *(uint32 *) (filebytes + fileoffset);
|
||||||
|
|
||||||
|
if (header.endianness == KTX_ENDIAN_REF_REV)
|
||||||
|
mipsize = swap32(mipsize);
|
||||||
|
|
||||||
|
fileoffset += sizeof(uint32);
|
||||||
|
|
||||||
|
// All mipsize fields are at a file offset that's a multiple of 4, so
|
||||||
|
// there might be some padding after the actual data in this mip level.
|
||||||
|
uint32 mipsizepadded = (mipsize + 3) & ~uint32(3);
|
||||||
|
|
||||||
|
totalsize += mipsizepadded;
|
||||||
|
fileoffset += mipsizepadded;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8 *data = nullptr;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
data = new uint8[totalsize];
|
||||||
|
}
|
||||||
|
catch (std::bad_alloc &)
|
||||||
|
{
|
||||||
|
throw love::Exception("Out of memory.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset the file offset to the start of the file's image data.
|
||||||
|
fileoffset = sizeof(KTXHeader) + header.bytesOfKeyValueData;
|
||||||
|
size_t dataoffset = 0;
|
||||||
|
|
||||||
|
// Copy each mipmap level of the image from the file to our block of memory.
|
||||||
|
for (int i = 0; i < (int) header.numberOfMipmapLevels; i++)
|
||||||
|
{
|
||||||
|
uint32 mipsize = *(uint32 *) (filebytes + fileoffset);
|
||||||
|
|
||||||
|
if (header.endianness == KTX_ENDIAN_REF_REV)
|
||||||
|
mipsize = swap32(mipsize);
|
||||||
|
|
||||||
|
fileoffset += sizeof(uint32);
|
||||||
|
|
||||||
|
uint32 mipsizepadded = (mipsize + 3) & ~uint32(3);
|
||||||
|
|
||||||
|
CompressedData::SubImage mip;
|
||||||
|
mip.width = (int) std::max(header.pixelWidth >> i, 1u);
|
||||||
|
mip.height = (int) std::max(header.pixelHeight >> i, 1u);
|
||||||
|
mip.size = mipsize;
|
||||||
|
|
||||||
|
memcpy(data + dataoffset, filebytes + fileoffset, mipsize);
|
||||||
|
mip.data = data + dataoffset;
|
||||||
|
|
||||||
|
fileoffset += mipsizepadded;
|
||||||
|
dataoffset += mipsizepadded;
|
||||||
|
|
||||||
|
images.push_back(mip);
|
||||||
|
}
|
||||||
|
|
||||||
|
dataSize = totalsize;
|
||||||
|
format = cformat;
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // magpie
|
||||||
|
} // image
|
||||||
|
} // love
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2006-2014 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.
|
||||||
|
**/
|
||||||
|
|
||||||
|
#ifndef LOVE_IMAGE_MAGPIE_KTX_HANDLER_H
|
||||||
|
#define LOVE_IMAGE_MAGPIE_KTX_HANDLER_H
|
||||||
|
|
||||||
|
#include "common/config.h"
|
||||||
|
#include "CompressedFormatHandler.h"
|
||||||
|
|
||||||
|
namespace love
|
||||||
|
{
|
||||||
|
namespace image
|
||||||
|
{
|
||||||
|
namespace magpie
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles KTX files with compressed image data inside.
|
||||||
|
**/
|
||||||
|
class KTXHandler : public CompressedFormatHandler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
virtual ~KTXHandler() {}
|
||||||
|
|
||||||
|
// Implements CompressedFormatHandler.
|
||||||
|
virtual bool canParse(const filesystem::FileData *data);
|
||||||
|
virtual uint8 *parse(filesystem::FileData *filedata, std::vector<CompressedData::SubImage> &images, size_t &dataSize, CompressedData::Format &format);
|
||||||
|
|
||||||
|
}; // KTXHandler
|
||||||
|
|
||||||
|
} // magpie
|
||||||
|
} // image
|
||||||
|
} // love
|
||||||
|
|
||||||
|
#endif // LOVE_IMAGE_MAGPIE_KTX_HANDLER_H
|
||||||
@@ -0,0 +1,156 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2006-2014 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.
|
||||||
|
**/
|
||||||
|
|
||||||
|
// LOVE
|
||||||
|
#include "PKMHandler.h"
|
||||||
|
#include "common/int.h"
|
||||||
|
|
||||||
|
namespace love
|
||||||
|
{
|
||||||
|
namespace image
|
||||||
|
{
|
||||||
|
namespace magpie
|
||||||
|
{
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
|
||||||
|
// Big endian to host (and vice versa.)
|
||||||
|
inline uint16 swap16big(uint16 x)
|
||||||
|
{
|
||||||
|
#ifdef LOVE_BIG_ENDIAN
|
||||||
|
return x;
|
||||||
|
#else
|
||||||
|
return swap16(x);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static const uint8 pkmIdentifier[] = {'P','K','M',' '};
|
||||||
|
|
||||||
|
struct PKMHeader
|
||||||
|
{
|
||||||
|
uint8 identifier[4];
|
||||||
|
uint8 version[2];
|
||||||
|
uint16 textureFormatBig;
|
||||||
|
uint16 extendedWidthBig;
|
||||||
|
uint16 extendedHeightBig;
|
||||||
|
uint16 widthBig;
|
||||||
|
uint16 heightBig;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum PKMTextureFormat
|
||||||
|
{
|
||||||
|
ETC1_RGB_NO_MIPMAPS = 0,
|
||||||
|
ETC2PACKAGE_RGB_NO_MIPMAPS,
|
||||||
|
ETC2PACKAGE_RGBA_NO_MIPMAPS_OLD,
|
||||||
|
ETC2PACKAGE_RGBA_NO_MIPMAPS,
|
||||||
|
ETC2PACKAGE_RGBA1_NO_MIPMAPS,
|
||||||
|
ETC2PACKAGE_R_NO_MIPMAPS,
|
||||||
|
ETC2PACKAGE_RG_NO_MIPMAPS,
|
||||||
|
ETC2PACKAGE_R_SIGNED_NO_MIPMAPS,
|
||||||
|
ETC2PACKAGE_RG_SIGNED_NO_MIPMAPS
|
||||||
|
};
|
||||||
|
|
||||||
|
CompressedData::Format convertFormat(uint16 texformat)
|
||||||
|
{
|
||||||
|
switch (texformat)
|
||||||
|
{
|
||||||
|
case ETC1_RGB_NO_MIPMAPS:
|
||||||
|
return CompressedData::FORMAT_ETC1;
|
||||||
|
default:
|
||||||
|
return CompressedData::FORMAT_UNKNOWN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // Anonymous namespace.
|
||||||
|
|
||||||
|
bool PKMHandler::canParse(const filesystem::FileData *data)
|
||||||
|
{
|
||||||
|
if (data->getSize() <= sizeof(PKMHeader))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const PKMHeader *header = (const PKMHeader *) data->getData();
|
||||||
|
|
||||||
|
if (memcmp(header->identifier, pkmIdentifier, 4) != 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// At the time of this writing, only v1.0 and v2.0 exist.
|
||||||
|
if ((header->version[0] != '2' && header->version[0] != '1') || header->version[1] != '0')
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8 *PKMHandler::parse(filesystem::FileData *filedata, std::vector<CompressedData::SubImage> &images, size_t &dataSize, CompressedData::Format &format)
|
||||||
|
{
|
||||||
|
if (!canParse(filedata))
|
||||||
|
throw love::Exception("Could not decode compressed data (not a PKM file?)");
|
||||||
|
|
||||||
|
PKMHeader header = *(const PKMHeader *) filedata->getData();
|
||||||
|
|
||||||
|
header.textureFormatBig = swap16big(header.textureFormatBig);
|
||||||
|
header.extendedWidthBig = swap16big(header.extendedWidthBig);
|
||||||
|
header.extendedHeightBig = swap16big(header.extendedHeightBig);
|
||||||
|
header.widthBig = swap16big(header.widthBig);
|
||||||
|
header.heightBig = swap16big(header.heightBig);
|
||||||
|
|
||||||
|
CompressedData::Format cformat = convertFormat(header.textureFormatBig);
|
||||||
|
|
||||||
|
if (cformat == CompressedData::FORMAT_UNKNOWN)
|
||||||
|
throw love::Exception("Could not parse PKM file: unsupported texture format.");
|
||||||
|
|
||||||
|
// The rest of the file after the header is all texture data.
|
||||||
|
size_t totalsize = filedata->getSize() - sizeof(PKMHeader);
|
||||||
|
uint8 *data = nullptr;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
data = new uint8[totalsize];
|
||||||
|
}
|
||||||
|
catch (std::bad_alloc &)
|
||||||
|
{
|
||||||
|
throw love::Exception("Out of memory.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// PKM files only store a single mipmap level.
|
||||||
|
memcpy(data, (uint8 *) filedata->getData() + sizeof(PKMHeader), totalsize);
|
||||||
|
|
||||||
|
CompressedData::SubImage mip;
|
||||||
|
|
||||||
|
// TODO: verify whether glCompressedTexImage works properly with the unpadded
|
||||||
|
// width and height values (extended == padded.)
|
||||||
|
mip.width = header.widthBig;
|
||||||
|
mip.height = header.heightBig;
|
||||||
|
|
||||||
|
mip.size = totalsize;
|
||||||
|
mip.data = data;
|
||||||
|
|
||||||
|
images.push_back(mip);
|
||||||
|
|
||||||
|
dataSize = totalsize;
|
||||||
|
format = cformat;
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // magpie
|
||||||
|
} // image
|
||||||
|
} // love
|
||||||
|
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2006-2014 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.
|
||||||
|
**/
|
||||||
|
|
||||||
|
#ifndef LOVE_IMAGE_MAGPIE_PKM_HANDLER_H
|
||||||
|
#define LOVE_IMAGE_MAGPIE_PKM_HANDLER_H
|
||||||
|
|
||||||
|
#include "common/config.h"
|
||||||
|
#include "CompressedFormatHandler.h"
|
||||||
|
|
||||||
|
namespace love
|
||||||
|
{
|
||||||
|
namespace image
|
||||||
|
{
|
||||||
|
namespace magpie
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles PKM files with compressed ETC data inside.
|
||||||
|
**/
|
||||||
|
class PKMHandler : public CompressedFormatHandler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
virtual ~PKMHandler() {}
|
||||||
|
|
||||||
|
// Implements CompressedFormatHandler.
|
||||||
|
virtual bool canParse(const filesystem::FileData *data);
|
||||||
|
virtual uint8 *parse(filesystem::FileData *filedata, std::vector<CompressedData::SubImage> &images, size_t &dataSize, CompressedData::Format &format);
|
||||||
|
|
||||||
|
}; // PKMHandler
|
||||||
|
|
||||||
|
} // magpie
|
||||||
|
} // image
|
||||||
|
} // love
|
||||||
|
|
||||||
|
#endif // LOVE_IMAGE_MAGPIE_PKM_HANDLER_H
|
||||||
@@ -0,0 +1,385 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2006-2014 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.
|
||||||
|
**/
|
||||||
|
|
||||||
|
// LOVE
|
||||||
|
#include "PVRHandler.h"
|
||||||
|
#include "common/int.h"
|
||||||
|
|
||||||
|
// C++
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
namespace love
|
||||||
|
{
|
||||||
|
namespace image
|
||||||
|
{
|
||||||
|
namespace magpie
|
||||||
|
{
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
|
||||||
|
// 'P' 'V' 'R' 3
|
||||||
|
static const uint32 PVRTEX3_IDENT = 0x03525650;
|
||||||
|
static const uint32 PVRTEX3_IDENT_REV = 0x50565203;
|
||||||
|
|
||||||
|
#pragma pack(push, 4)
|
||||||
|
struct PVRTexHeaderV3
|
||||||
|
{
|
||||||
|
uint32 version; /// Version of the file header, used to identify it.
|
||||||
|
uint32 flags; /// Various format flags.
|
||||||
|
uint64 pixelFormat; /// The pixel format, 8cc value storing the 4 channel identifiers and their respective sizes.
|
||||||
|
uint32 colorSpace; /// The Color Space of the texture, currently either linear RGB or sRGB.
|
||||||
|
uint32 channelType; /// Variable type that the channel is stored in. Supports signed/unsigned int/short/byte or float for now.
|
||||||
|
uint32 height; /// Height of the texture.
|
||||||
|
uint32 width; /// Width of the texture.
|
||||||
|
uint32 depth; /// Depth of the texture. (Z-slices)
|
||||||
|
uint32 numSurfaces; /// Number of members in a Texture Array.
|
||||||
|
uint32 numFaces; /// Number of faces in a Cube Map. Maybe be a value other than 6.
|
||||||
|
uint32 numMipmaps; /// Number of MIP Maps in the texture - NB: Includes top level.
|
||||||
|
uint32 metaDataSize; /// Size of the accompanying meta data.
|
||||||
|
};
|
||||||
|
#pragma pack(pop)
|
||||||
|
|
||||||
|
enum PVRV3PixelFormat
|
||||||
|
{
|
||||||
|
ePVRTPF_PVRTCI_2bpp_RGB = 0x00,
|
||||||
|
ePVRTPF_PVRTCI_2bpp_RGBA,
|
||||||
|
ePVRTPF_PVRTCI_4bpp_RGB,
|
||||||
|
ePVRTPF_PVRTCI_4bpp_RGBA,
|
||||||
|
ePVRTPF_PVRTCII_2bpp,
|
||||||
|
ePVRTPF_PVRTCII_4bpp,
|
||||||
|
ePVRTPF_ETC1 = 0x06,
|
||||||
|
ePVRTPF_DXT1,
|
||||||
|
ePVRTPF_DXT2,
|
||||||
|
ePVRTPF_DXT3,
|
||||||
|
ePVRTPF_DXT4,
|
||||||
|
ePVRTPF_DXT5,
|
||||||
|
ePVRTF_UNKNOWN_FORMAT = 0x7F
|
||||||
|
};
|
||||||
|
|
||||||
|
// 'P' 'V' 'R' '!'
|
||||||
|
static const uint32 PVRTEX2_IDENT = 0x21525650;
|
||||||
|
static const uint32 PVRTEX2_IDENT_REV = 0x50565221;
|
||||||
|
|
||||||
|
struct PVRTexHeaderV2
|
||||||
|
{
|
||||||
|
uint32 headerSize;
|
||||||
|
uint32 height;
|
||||||
|
uint32 width;
|
||||||
|
uint32 numMipmaps;
|
||||||
|
uint32 flags;
|
||||||
|
uint32 dataSize;
|
||||||
|
uint32 bpp;
|
||||||
|
uint32 bitmaskRed;
|
||||||
|
uint32 bitmaskGreen;
|
||||||
|
uint32 bitmaskBlue;
|
||||||
|
uint32 bitmaskAlpha;
|
||||||
|
uint32 pvrTag;
|
||||||
|
uint32 numSurfaces;
|
||||||
|
};
|
||||||
|
|
||||||
|
// The legacy V2 pixel types we support.
|
||||||
|
enum PVRPixelTypeV2
|
||||||
|
{
|
||||||
|
PixelTypePVRTC2 = 0x18,
|
||||||
|
PixelTypePVRTC4,
|
||||||
|
PixelTypePVRTCII2 = 0x1C,
|
||||||
|
PixelTypePVRTCII4,
|
||||||
|
PixelTypeDXT1 = 0x20,
|
||||||
|
PixelTypeDXT3 = 0x22,
|
||||||
|
PixelTypeDXT5 = 0x24,
|
||||||
|
PixelTypeETC1 = 0x36
|
||||||
|
};
|
||||||
|
|
||||||
|
// Convert a V2 header to V3.
|
||||||
|
void ConvertPVRHeader(PVRTexHeaderV2 header2, PVRTexHeaderV3 *header3)
|
||||||
|
{
|
||||||
|
// If the header's endianness doesn't match our own, we swap everything.
|
||||||
|
if (header2.pvrTag == PVRTEX2_IDENT_REV)
|
||||||
|
{
|
||||||
|
// All of the struct's members are uint32 values, so we can do this.
|
||||||
|
uint32 *headerArray = (uint32 *) &header2;
|
||||||
|
for (size_t i = 0; i < sizeof(PVRTexHeaderV2) / sizeof(uint32); i++)
|
||||||
|
headerArray[i] = swap32(headerArray[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(header3, 0, sizeof(PVRTexHeaderV3));
|
||||||
|
|
||||||
|
header3->version = PVRTEX3_IDENT;
|
||||||
|
header3->height = header2.height;
|
||||||
|
header3->width = header2.width;
|
||||||
|
header3->depth = 1;
|
||||||
|
header3->numSurfaces = header2.numSurfaces;
|
||||||
|
header3->numFaces = 1;
|
||||||
|
header3->numMipmaps = header2.numMipmaps;
|
||||||
|
header3->metaDataSize = 0;
|
||||||
|
|
||||||
|
switch ((PVRPixelTypeV2) (header2.flags & 0xFF))
|
||||||
|
{
|
||||||
|
case PixelTypePVRTC2:
|
||||||
|
header3->pixelFormat = ePVRTPF_PVRTCI_2bpp_RGBA;
|
||||||
|
break;
|
||||||
|
case PixelTypePVRTC4:
|
||||||
|
header3->pixelFormat = ePVRTPF_PVRTCI_4bpp_RGBA;
|
||||||
|
break;
|
||||||
|
case PixelTypePVRTCII2:
|
||||||
|
header3->pixelFormat = ePVRTPF_PVRTCII_2bpp;
|
||||||
|
break;
|
||||||
|
case PixelTypePVRTCII4:
|
||||||
|
header3->pixelFormat = ePVRTPF_PVRTCII_4bpp;
|
||||||
|
break;
|
||||||
|
case PixelTypeDXT1:
|
||||||
|
header3->pixelFormat = ePVRTPF_DXT1;
|
||||||
|
break;
|
||||||
|
case PixelTypeDXT3:
|
||||||
|
header3->pixelFormat = ePVRTPF_DXT3;
|
||||||
|
break;
|
||||||
|
case PixelTypeDXT5:
|
||||||
|
header3->pixelFormat = ePVRTPF_DXT5;
|
||||||
|
break;
|
||||||
|
case PixelTypeETC1:
|
||||||
|
header3->pixelFormat = ePVRTPF_ETC1;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
header3->pixelFormat = ePVRTF_UNKNOWN_FORMAT;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CompressedData::Format convertFormat(PVRV3PixelFormat format)
|
||||||
|
{
|
||||||
|
switch (format)
|
||||||
|
{
|
||||||
|
case ePVRTPF_PVRTCI_2bpp_RGB:
|
||||||
|
return CompressedData::FORMAT_PVR1_RGB2;
|
||||||
|
case ePVRTPF_PVRTCI_2bpp_RGBA:
|
||||||
|
return CompressedData::FORMAT_PVR1_RGBA2;
|
||||||
|
case ePVRTPF_PVRTCI_4bpp_RGB:
|
||||||
|
return CompressedData::FORMAT_PVR1_RGB4;
|
||||||
|
case ePVRTPF_PVRTCI_4bpp_RGBA:
|
||||||
|
return CompressedData::FORMAT_PVR1_RGBA4;
|
||||||
|
case ePVRTPF_ETC1:
|
||||||
|
return CompressedData::FORMAT_ETC1;
|
||||||
|
case ePVRTPF_DXT1:
|
||||||
|
return CompressedData::FORMAT_DXT1;
|
||||||
|
case ePVRTPF_DXT3:
|
||||||
|
return CompressedData::FORMAT_DXT3;
|
||||||
|
case ePVRTPF_DXT5:
|
||||||
|
return CompressedData::FORMAT_DXT5;
|
||||||
|
default:
|
||||||
|
return CompressedData::FORMAT_UNKNOWN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int getBitsPerPixel(uint64 pixelformat)
|
||||||
|
{
|
||||||
|
// Uncompressed formats have their bits per pixel stored in the high bits.
|
||||||
|
if ((pixelformat & 0xFFFFFFFF) != pixelformat)
|
||||||
|
{
|
||||||
|
const uint8 *charformat = (const uint8 *) &pixelformat;
|
||||||
|
return charformat[4] + charformat[5] + charformat[6] + charformat[7];
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (pixelformat)
|
||||||
|
{
|
||||||
|
case ePVRTPF_PVRTCI_2bpp_RGB:
|
||||||
|
case ePVRTPF_PVRTCI_2bpp_RGBA:
|
||||||
|
case ePVRTPF_PVRTCII_2bpp:
|
||||||
|
return 2;
|
||||||
|
case ePVRTPF_PVRTCI_4bpp_RGB:
|
||||||
|
case ePVRTPF_PVRTCI_4bpp_RGBA:
|
||||||
|
case ePVRTPF_PVRTCII_4bpp:
|
||||||
|
case ePVRTPF_ETC1:
|
||||||
|
case ePVRTPF_DXT1:
|
||||||
|
return 4;
|
||||||
|
case ePVRTPF_DXT2:
|
||||||
|
case ePVRTPF_DXT3:
|
||||||
|
case ePVRTPF_DXT4:
|
||||||
|
case ePVRTPF_DXT5:
|
||||||
|
return 8;
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void getFormatMinDimensions(uint64 pixelformat, int &minX, int &minY)
|
||||||
|
{
|
||||||
|
switch (pixelformat)
|
||||||
|
{
|
||||||
|
case ePVRTPF_PVRTCI_2bpp_RGB:
|
||||||
|
case ePVRTPF_PVRTCI_2bpp_RGBA:
|
||||||
|
minX = 16;
|
||||||
|
minY = 8;
|
||||||
|
break;
|
||||||
|
case ePVRTPF_PVRTCI_4bpp_RGB:
|
||||||
|
case ePVRTPF_PVRTCI_4bpp_RGBA:
|
||||||
|
minX = minY = 8;
|
||||||
|
break;
|
||||||
|
case ePVRTPF_PVRTCII_2bpp:
|
||||||
|
minX = 8;
|
||||||
|
minY = 4;
|
||||||
|
break;
|
||||||
|
case ePVRTPF_PVRTCII_4bpp:
|
||||||
|
minX = minY = 4;
|
||||||
|
break;
|
||||||
|
case ePVRTPF_DXT1:
|
||||||
|
case ePVRTPF_DXT2:
|
||||||
|
case ePVRTPF_DXT3:
|
||||||
|
case ePVRTPF_DXT4:
|
||||||
|
case ePVRTPF_DXT5:
|
||||||
|
case ePVRTPF_ETC1:
|
||||||
|
minX = minY = 4;
|
||||||
|
break;
|
||||||
|
default: // We don't handle all possible formats, but that's fine.
|
||||||
|
minX = minY = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t getMipLevelSize(const PVRTexHeaderV3 &header, int miplevel)
|
||||||
|
{
|
||||||
|
int smallestwidth = 1;
|
||||||
|
int smallestheight = 1;
|
||||||
|
getFormatMinDimensions(header.pixelFormat, smallestwidth, smallestheight);
|
||||||
|
|
||||||
|
int width = std::max((int) header.width >> miplevel, 1);
|
||||||
|
int height = std::max((int) header.height >> miplevel, 1);
|
||||||
|
int depth = std::max((int) header.depth >> miplevel, 1);
|
||||||
|
|
||||||
|
// Pad the dimensions.
|
||||||
|
width += (-width) % smallestwidth;
|
||||||
|
height += (-height) % smallestheight;
|
||||||
|
|
||||||
|
return getBitsPerPixel(header.pixelFormat) * width * height * depth / 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // Anonymous namespace.
|
||||||
|
|
||||||
|
|
||||||
|
bool PVRHandler::canParse(const filesystem::FileData *data)
|
||||||
|
{
|
||||||
|
if (data->getSize() < sizeof(PVRTexHeaderV2) || data->getSize() < sizeof(PVRTexHeaderV3))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
PVRTexHeaderV3 *header3 = (PVRTexHeaderV3 *) data->getData();
|
||||||
|
|
||||||
|
// Magic number (FourCC identifier.)
|
||||||
|
if (header3->version == PVRTEX3_IDENT || header3->version == PVRTEX3_IDENT_REV)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// Maybe it has a V2 header.
|
||||||
|
PVRTexHeaderV2 *header2 = (PVRTexHeaderV2 *) data->getData();
|
||||||
|
|
||||||
|
// FourCC identifier.
|
||||||
|
if (header2->pvrTag == PVRTEX2_IDENT || header2->pvrTag == PVRTEX2_IDENT_REV)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8 *PVRHandler::parse(filesystem::FileData *filedata, std::vector<CompressedData::SubImage> &images, size_t &dataSize, CompressedData::Format &format)
|
||||||
|
{
|
||||||
|
if (!canParse(filedata))
|
||||||
|
throw love::Exception("Could not decode compressed data (not a PVR file?)");
|
||||||
|
|
||||||
|
PVRTexHeaderV3 header3 = *(PVRTexHeaderV3 *) filedata->getData();
|
||||||
|
|
||||||
|
// If the header isn't the V3 format, assume it's V2 and convert.
|
||||||
|
if (header3.version != PVRTEX3_IDENT && header3.version != PVRTEX3_IDENT_REV)
|
||||||
|
ConvertPVRHeader(*(PVRTexHeaderV2 *) filedata->getData(), &header3);
|
||||||
|
|
||||||
|
// If the header's endianness doesn't match our own, then we swap everything.
|
||||||
|
if (header3.version == PVRTEX3_IDENT_REV)
|
||||||
|
{
|
||||||
|
header3.version = PVRTEX3_IDENT;
|
||||||
|
header3.flags = swap32(header3.flags);
|
||||||
|
header3.pixelFormat = swap64(header3.pixelFormat);
|
||||||
|
header3.colorSpace = swap32(header3.colorSpace);
|
||||||
|
header3.channelType = swap32(header3.channelType);
|
||||||
|
header3.height = swap32(header3.height);
|
||||||
|
header3.width = swap32(header3.width);
|
||||||
|
header3.depth = swap32(header3.depth);
|
||||||
|
header3.numFaces = swap32(header3.numFaces);
|
||||||
|
header3.numMipmaps = swap32(header3.numMipmaps);
|
||||||
|
header3.metaDataSize = swap32(header3.metaDataSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (header3.depth > 1)
|
||||||
|
throw love::Exception("Image depths greater than 1 in PVR files are unsupported.");
|
||||||
|
|
||||||
|
CompressedData::Format cformat = convertFormat((PVRV3PixelFormat) header3.pixelFormat);
|
||||||
|
|
||||||
|
if (cformat == CompressedData::FORMAT_UNKNOWN)
|
||||||
|
throw love::Exception("Could not parse PVR file: unsupported image format.");
|
||||||
|
|
||||||
|
size_t totalsize = 0;
|
||||||
|
uint8 *data = nullptr;
|
||||||
|
|
||||||
|
// Ignore faces and surfaces except the first ones (for now.)
|
||||||
|
for (int i = 0; i < (int) header3.numMipmaps; i++)
|
||||||
|
totalsize += getMipLevelSize(header3, i);
|
||||||
|
|
||||||
|
size_t fileoffset = sizeof(PVRTexHeaderV3) + header3.metaDataSize;
|
||||||
|
|
||||||
|
// Make sure the file actually holds this much data...
|
||||||
|
if (filedata->getSize() < fileoffset + totalsize)
|
||||||
|
throw love::Exception("Could not parse PVR file: invalid size calculation.");
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
data = new uint8[totalsize];
|
||||||
|
}
|
||||||
|
catch (std::bad_alloc &)
|
||||||
|
{
|
||||||
|
throw love::Exception("Out of memory.");
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t curoffset = 0;
|
||||||
|
const uint8 *filebytes = (uint8 *) filedata->getData() + fileoffset;
|
||||||
|
|
||||||
|
for (int i = 0; i < (int) header3.numMipmaps; i++)
|
||||||
|
{
|
||||||
|
size_t mipsize = getMipLevelSize(header3, i);
|
||||||
|
|
||||||
|
if (curoffset + mipsize > totalsize)
|
||||||
|
break; // Just in case.
|
||||||
|
|
||||||
|
CompressedData::SubImage mip;
|
||||||
|
mip.width = std::max((int) header3.width >> i, 1);
|
||||||
|
mip.height = std::max((int) header3.height >> i, 1);
|
||||||
|
mip.size = mipsize;
|
||||||
|
|
||||||
|
memcpy(data + curoffset, filebytes + curoffset, mipsize);
|
||||||
|
mip.data = data + curoffset;
|
||||||
|
|
||||||
|
curoffset += mipsize;
|
||||||
|
|
||||||
|
images.push_back(mip);
|
||||||
|
}
|
||||||
|
|
||||||
|
dataSize = totalsize;
|
||||||
|
format = cformat;
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // magpie
|
||||||
|
} // image
|
||||||
|
} // love
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2006-2014 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.
|
||||||
|
**/
|
||||||
|
|
||||||
|
#ifndef LOVE_IMAGE_MAGPIE_PVR_HANDLER_H
|
||||||
|
#define LOVE_IMAGE_MAGPIE_PVR_HANDLER_H
|
||||||
|
|
||||||
|
// LOVE
|
||||||
|
#include "common/config.h"
|
||||||
|
#include "CompressedFormatHandler.h"
|
||||||
|
|
||||||
|
namespace love
|
||||||
|
{
|
||||||
|
namespace image
|
||||||
|
{
|
||||||
|
namespace magpie
|
||||||
|
{
|
||||||
|
|
||||||
|
class PVRHandler : public CompressedFormatHandler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
virtual ~PVRHandler() {}
|
||||||
|
|
||||||
|
// Implements CompressedFormatHandler.
|
||||||
|
virtual bool canParse(const filesystem::FileData *data);
|
||||||
|
virtual uint8 *parse(filesystem::FileData *filedata, std::vector<CompressedData::SubImage> &images, size_t &dataSize, CompressedData::Format &format);
|
||||||
|
|
||||||
|
}; // PVRHandler
|
||||||
|
|
||||||
|
} // magpie
|
||||||
|
} // image
|
||||||
|
} // love
|
||||||
|
|
||||||
|
#endif // LOVE_IMAGE_MAGPIE_PVR_HANDLER_H
|
||||||
@@ -18,8 +18,10 @@
|
|||||||
* 3. This notice may not be removed or altered from any source distribution.
|
* 3. This notice may not be removed or altered from any source distribution.
|
||||||
**/
|
**/
|
||||||
|
|
||||||
|
// LOVE
|
||||||
#include "ddsHandler.h"
|
#include "ddsHandler.h"
|
||||||
|
|
||||||
|
// C++
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
namespace love
|
namespace love
|
||||||
@@ -29,7 +31,7 @@ namespace image
|
|||||||
namespace magpie
|
namespace magpie
|
||||||
{
|
{
|
||||||
|
|
||||||
bool ddsHandler::canParse(const filesystem::FileData *data)
|
bool DDSHandler::canParse(const filesystem::FileData *data)
|
||||||
{
|
{
|
||||||
std::string ext = data->getExtension();
|
std::string ext = data->getExtension();
|
||||||
std::transform(ext.begin(), ext.end(), ext.begin(), tolower);
|
std::transform(ext.begin(), ext.end(), ext.begin(), tolower);
|
||||||
@@ -40,14 +42,14 @@ bool ddsHandler::canParse(const filesystem::FileData *data)
|
|||||||
return dds::isCompressedDDS(data->getData(), data->getSize());
|
return dds::isCompressedDDS(data->getData(), data->getSize());
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8 *ddsHandler::parse(filesystem::FileData *filedata, std::vector<CompressedData::SubImage> &images, size_t &dataSize, CompressedData::Format &format)
|
uint8 *DDSHandler::parse(filesystem::FileData *filedata, std::vector<CompressedData::SubImage> &images, size_t &dataSize, CompressedData::Format &format)
|
||||||
{
|
{
|
||||||
if (!dds::isDDS(filedata->getData(), filedata->getSize()))
|
if (!dds::isDDS(filedata->getData(), filedata->getSize()))
|
||||||
throw love::Exception("Could not decode compressed data (not a DDS file?)");
|
throw love::Exception("Could not decode compressed data (not a DDS file?)");
|
||||||
|
|
||||||
CompressedData::Format texformat = CompressedData::FORMAT_UNKNOWN;
|
CompressedData::Format texformat = CompressedData::FORMAT_UNKNOWN;
|
||||||
|
|
||||||
uint8 *data = 0;
|
uint8 *data = nullptr;
|
||||||
dataSize = 0;
|
dataSize = 0;
|
||||||
images.clear();
|
images.clear();
|
||||||
|
|
||||||
@@ -107,7 +109,7 @@ uint8 *ddsHandler::parse(filesystem::FileData *filedata, std::vector<CompressedD
|
|||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
CompressedData::Format ddsHandler::convertFormat(dds::Format ddsformat)
|
CompressedData::Format DDSHandler::convertFormat(dds::Format ddsformat)
|
||||||
{
|
{
|
||||||
switch (ddsformat)
|
switch (ddsformat)
|
||||||
{
|
{
|
||||||
@@ -128,8 +130,6 @@ CompressedData::Format ddsHandler::convertFormat(dds::Format ddsformat)
|
|||||||
default:
|
default:
|
||||||
return CompressedData::FORMAT_UNKNOWN;
|
return CompressedData::FORMAT_UNKNOWN;
|
||||||
}
|
}
|
||||||
|
|
||||||
return CompressedData::FORMAT_UNKNOWN;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // magpie
|
} // magpie
|
||||||
|
|||||||
@@ -22,9 +22,7 @@
|
|||||||
#define LOVE_IMAGE_MAGPIE_DDS_HANDLER_H
|
#define LOVE_IMAGE_MAGPIE_DDS_HANDLER_H
|
||||||
|
|
||||||
// LOVE
|
// LOVE
|
||||||
#include "common/EnumMap.h"
|
#include "CompressedFormatHandler.h"
|
||||||
#include "filesystem/FileData.h"
|
|
||||||
#include "image/CompressedData.h"
|
|
||||||
|
|
||||||
// dds parser
|
// dds parser
|
||||||
#include "ddsparse/ddsparse.h"
|
#include "ddsparse/ddsparse.h"
|
||||||
@@ -42,36 +40,21 @@ namespace magpie
|
|||||||
/**
|
/**
|
||||||
* Interface between CompressedData and the ddsparse library.
|
* Interface between CompressedData and the ddsparse library.
|
||||||
**/
|
**/
|
||||||
class ddsHandler
|
class DDSHandler : public CompressedFormatHandler
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
/**
|
virtual ~DDSHandler() {}
|
||||||
* Determines whether a particular FileData can be parsed as CompressedData
|
|
||||||
* by this handler.
|
|
||||||
* @param data The data to parse.
|
|
||||||
**/
|
|
||||||
static bool canParse(const filesystem::FileData *data);
|
|
||||||
|
|
||||||
/**
|
// Implements CompressedFormatHandler.
|
||||||
* Parses compressed image filedata into a list of sub-images and returns
|
virtual bool canParse(const filesystem::FileData *data);
|
||||||
* a single block of memory containing all the images.
|
virtual uint8 *parse(filesystem::FileData *filedata, std::vector<CompressedData::SubImage> &images, size_t &dataSize, CompressedData::Format &format);
|
||||||
*
|
|
||||||
* @param[in] filedata The data to parse.
|
|
||||||
* @param[out] image The list of sub-images generated. Byte data is a pointer
|
|
||||||
* to the returned data.
|
|
||||||
* @param[out] dataSize The total size in bytes of the returned data.
|
|
||||||
* @param[out] format The format of the Compressed Data.
|
|
||||||
*
|
|
||||||
* @return The single block of memory containing the parsed images.
|
|
||||||
**/
|
|
||||||
static uint8 *parse(filesystem::FileData *filedata, std::vector<CompressedData::SubImage> &images, size_t &dataSize, CompressedData::Format &format);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
static CompressedData::Format convertFormat(dds::Format ddsformat);
|
static CompressedData::Format convertFormat(dds::Format ddsformat);
|
||||||
|
|
||||||
}; // ddsHandler
|
}; // DDSHandler
|
||||||
|
|
||||||
} // magpie
|
} // magpie
|
||||||
} // image
|
} // image
|
||||||
|
|||||||
@@ -157,9 +157,9 @@ public:
|
|||||||
virtual int getID() const = 0;
|
virtual int getID() const = 0;
|
||||||
|
|
||||||
virtual bool isVibrationSupported() = 0;
|
virtual bool isVibrationSupported() = 0;
|
||||||
virtual bool setVibration(float left, float right) = 0;
|
virtual bool setVibration(float left, float right, float duration = -1.0f) = 0;
|
||||||
virtual bool setVibration() = 0;
|
virtual bool setVibration() = 0;
|
||||||
virtual void getVibration(float &left, float &right) const = 0;
|
virtual void getVibration(float &left, float &right) = 0;
|
||||||
|
|
||||||
static bool getConstant(const char *in, Hat &out);
|
static bool getConstant(const char *in, Hat &out);
|
||||||
static bool getConstant(Hat in, const char *&out);
|
static bool getConstant(Hat in, const char *&out);
|
||||||
|
|||||||
@@ -25,6 +25,11 @@
|
|||||||
|
|
||||||
// C++
|
// C++
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
|
#ifndef SDL_TICKS_PASSED
|
||||||
|
#define SDL_TICKS_PASSED(A, B) ((Sint32)((B) - (A)) <= 0)
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace love
|
namespace love
|
||||||
{
|
{
|
||||||
@@ -34,9 +39,9 @@ namespace sdl
|
|||||||
{
|
{
|
||||||
|
|
||||||
Joystick::Joystick(int id)
|
Joystick::Joystick(int id)
|
||||||
: joyhandle(0)
|
: joyhandle(nullptr)
|
||||||
, controller(0)
|
, controller(nullptr)
|
||||||
, haptic(0)
|
, haptic(nullptr)
|
||||||
, instanceid(-1)
|
, instanceid(-1)
|
||||||
, id(id)
|
, id(id)
|
||||||
, vibration()
|
, vibration()
|
||||||
@@ -44,9 +49,9 @@ Joystick::Joystick(int id)
|
|||||||
}
|
}
|
||||||
|
|
||||||
Joystick::Joystick(int id, int joyindex)
|
Joystick::Joystick(int id, int joyindex)
|
||||||
: joyhandle(0)
|
: joyhandle(nullptr)
|
||||||
, controller(0)
|
, controller(nullptr)
|
||||||
, haptic(0)
|
, haptic(nullptr)
|
||||||
, instanceid(-1)
|
, instanceid(-1)
|
||||||
, id(id)
|
, id(id)
|
||||||
, vibration()
|
, vibration()
|
||||||
@@ -95,10 +100,7 @@ bool Joystick::open(int deviceindex)
|
|||||||
void Joystick::close()
|
void Joystick::close()
|
||||||
{
|
{
|
||||||
if (haptic)
|
if (haptic)
|
||||||
{
|
|
||||||
SDL_HapticRumbleStop(haptic);
|
|
||||||
SDL_HapticClose(haptic);
|
SDL_HapticClose(haptic);
|
||||||
}
|
|
||||||
|
|
||||||
if (controller)
|
if (controller)
|
||||||
SDL_GameControllerClose(controller);
|
SDL_GameControllerClose(controller);
|
||||||
@@ -106,16 +108,16 @@ void Joystick::close()
|
|||||||
if (joyhandle)
|
if (joyhandle)
|
||||||
SDL_JoystickClose(joyhandle);
|
SDL_JoystickClose(joyhandle);
|
||||||
|
|
||||||
joyhandle = 0;
|
joyhandle = nullptr;
|
||||||
controller = 0;
|
controller = nullptr;
|
||||||
haptic = 0;
|
haptic = nullptr;
|
||||||
instanceid = -1;
|
instanceid = -1;
|
||||||
vibration = Vibration();
|
vibration = Vibration();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Joystick::isConnected() const
|
bool Joystick::isConnected() const
|
||||||
{
|
{
|
||||||
return joyhandle != 0 && SDL_JoystickGetAttached(joyhandle);
|
return joyhandle != nullptr && SDL_JoystickGetAttached(joyhandle);
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *Joystick::getName() const
|
const char *Joystick::getName() const
|
||||||
@@ -209,7 +211,7 @@ bool Joystick::openGamepad(int deviceindex)
|
|||||||
if (isGamepad())
|
if (isGamepad())
|
||||||
{
|
{
|
||||||
SDL_GameControllerClose(controller);
|
SDL_GameControllerClose(controller);
|
||||||
controller = 0;
|
controller = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
controller = SDL_GameControllerOpen(deviceindex);
|
controller = SDL_GameControllerOpen(deviceindex);
|
||||||
@@ -218,7 +220,7 @@ bool Joystick::openGamepad(int deviceindex)
|
|||||||
|
|
||||||
bool Joystick::isGamepad() const
|
bool Joystick::isGamepad() const
|
||||||
{
|
{
|
||||||
return controller != 0;
|
return controller != nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
float Joystick::getGamepadAxis(love::joystick::Joystick::GamepadAxis axis) const
|
float Joystick::getGamepadAxis(love::joystick::Joystick::GamepadAxis axis) const
|
||||||
@@ -289,13 +291,13 @@ bool Joystick::checkCreateHaptic()
|
|||||||
if (haptic)
|
if (haptic)
|
||||||
{
|
{
|
||||||
SDL_HapticClose(haptic);
|
SDL_HapticClose(haptic);
|
||||||
haptic = 0;
|
haptic = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
haptic = SDL_HapticOpenFromJoystick(joyhandle);
|
haptic = SDL_HapticOpenFromJoystick(joyhandle);
|
||||||
vibration = Vibration();
|
vibration = Vibration();
|
||||||
|
|
||||||
return haptic != 0;
|
return haptic != nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Joystick::isVibrationSupported()
|
bool Joystick::isVibrationSupported()
|
||||||
@@ -312,8 +314,8 @@ bool Joystick::isVibrationSupported()
|
|||||||
if (isGamepad() && (features & SDL_HAPTIC_CUSTOM) != 0)
|
if (isGamepad() && (features & SDL_HAPTIC_CUSTOM) != 0)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
// Check SDL's simple rumble as a last resort.
|
// Test for simple sine wave support as a last resort.
|
||||||
if (SDL_HapticRumbleSupported(haptic) == 1)
|
if ((features & SDL_HAPTIC_SINE) != 0)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@@ -325,7 +327,7 @@ bool Joystick::runVibrationEffect()
|
|||||||
{
|
{
|
||||||
if (SDL_HapticUpdateEffect(haptic, vibration.id, &vibration.effect) == 0)
|
if (SDL_HapticUpdateEffect(haptic, vibration.id, &vibration.effect) == 0)
|
||||||
{
|
{
|
||||||
if (SDL_HapticRunEffect(haptic, vibration.id, 1) != -1)
|
if (SDL_HapticRunEffect(haptic, vibration.id, 1) == 0)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -336,17 +338,14 @@ bool Joystick::runVibrationEffect()
|
|||||||
|
|
||||||
vibration.id = SDL_HapticNewEffect(haptic, &vibration.effect);
|
vibration.id = SDL_HapticNewEffect(haptic, &vibration.effect);
|
||||||
|
|
||||||
if (vibration.id != -1 && SDL_HapticRunEffect(haptic, vibration.id, 1) != -1)
|
if (vibration.id != -1 && SDL_HapticRunEffect(haptic, vibration.id, 1) == 0)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Joystick::setVibration(float left, float right)
|
bool Joystick::setVibration(float left, float right, float duration)
|
||||||
{
|
{
|
||||||
// TODO: support non-infinite durations? The working Tattiebogle Xbox
|
|
||||||
// controller driver in OS X seems to ignore durations under 1 second.
|
|
||||||
|
|
||||||
left = std::min(std::max(left, 0.0f), 1.0f);
|
left = std::min(std::max(left, 0.0f), 1.0f);
|
||||||
right = std::min(std::max(right, 0.0f), 1.0f);
|
right = std::min(std::max(right, 0.0f), 1.0f);
|
||||||
|
|
||||||
@@ -356,24 +355,32 @@ bool Joystick::setVibration(float left, float right)
|
|||||||
if (!checkCreateHaptic())
|
if (!checkCreateHaptic())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
Uint32 length = SDL_HAPTIC_INFINITY;
|
||||||
|
if (duration >= 0.0f)
|
||||||
|
{
|
||||||
|
float maxduration = std::numeric_limits<Uint32>::max() / 1000.0f;
|
||||||
|
length = Uint32(std::min(duration, maxduration) * 1000);
|
||||||
|
}
|
||||||
|
|
||||||
bool success = false;
|
bool success = false;
|
||||||
unsigned int features = SDL_HapticQuery(haptic);
|
unsigned int features = SDL_HapticQuery(haptic);
|
||||||
|
int axes = SDL_HapticNumAxes(haptic);
|
||||||
|
|
||||||
if ((features & SDL_HAPTIC_LEFTRIGHT) != 0)
|
if ((features & SDL_HAPTIC_LEFTRIGHT) != 0)
|
||||||
{
|
{
|
||||||
memset(&vibration.effect, 0, sizeof(SDL_HapticEffect));
|
memset(&vibration.effect, 0, sizeof(SDL_HapticEffect));
|
||||||
vibration.effect.type = SDL_HAPTIC_LEFTRIGHT;
|
vibration.effect.type = SDL_HAPTIC_LEFTRIGHT;
|
||||||
|
|
||||||
vibration.effect.leftright.length = SDL_HAPTIC_INFINITY;
|
vibration.effect.leftright.length = length;
|
||||||
vibration.effect.leftright.large_magnitude = Uint16(left * LOVE_UINT16_MAX);
|
vibration.effect.leftright.large_magnitude = Uint16(left * LOVE_UINT16_MAX);
|
||||||
vibration.effect.leftright.small_magnitude = Uint16(right * LOVE_UINT16_MAX);
|
vibration.effect.leftright.small_magnitude = Uint16(right * LOVE_UINT16_MAX);
|
||||||
|
|
||||||
success = runVibrationEffect();
|
success = runVibrationEffect();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Some gamepad drivers only give support for controlling the motors via
|
// Some gamepad drivers only give support for controlling individual motors
|
||||||
// a custom FF effect.
|
// through a custom FF effect.
|
||||||
if (!success && isGamepad() && (features & SDL_HAPTIC_CUSTOM) != 0)
|
if (!success && isGamepad() && (features & SDL_HAPTIC_CUSTOM) && axes == 2)
|
||||||
{
|
{
|
||||||
// NOTE: this may cause issues with drivers which support custom effects
|
// NOTE: this may cause issues with drivers which support custom effects
|
||||||
// but aren't similar to https://github.com/d235j/360Controller .
|
// but aren't similar to https://github.com/d235j/360Controller .
|
||||||
@@ -385,7 +392,7 @@ bool Joystick::setVibration(float left, float right)
|
|||||||
memset(&vibration.effect, 0, sizeof(SDL_HapticEffect));
|
memset(&vibration.effect, 0, sizeof(SDL_HapticEffect));
|
||||||
vibration.effect.type = SDL_HAPTIC_CUSTOM;
|
vibration.effect.type = SDL_HAPTIC_CUSTOM;
|
||||||
|
|
||||||
vibration.effect.custom.length = SDL_HAPTIC_INFINITY;
|
vibration.effect.custom.length = length;
|
||||||
vibration.effect.custom.channels = 2;
|
vibration.effect.custom.channels = 2;
|
||||||
vibration.effect.custom.period = 10;
|
vibration.effect.custom.period = 10;
|
||||||
vibration.effect.custom.samples = 2;
|
vibration.effect.custom.samples = 2;
|
||||||
@@ -394,19 +401,36 @@ bool Joystick::setVibration(float left, float right)
|
|||||||
success = runVibrationEffect();
|
success = runVibrationEffect();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fall back to a simple rumble if all else fails. SDL's simple rumble API
|
// Fall back to a simple sine wave if all else fails. This only supports a
|
||||||
// only supports a single strength value.
|
// single strength value.
|
||||||
if (!success && SDL_HapticRumbleInit(haptic) == 0)
|
if (!success && (features & SDL_HAPTIC_SINE) != 0)
|
||||||
{
|
{
|
||||||
|
memset(&vibration.effect, 0, sizeof(SDL_HapticEffect));
|
||||||
|
vibration.effect.type = SDL_HAPTIC_SINE;
|
||||||
|
|
||||||
|
vibration.effect.periodic.length = length;
|
||||||
|
vibration.effect.periodic.period = 10;
|
||||||
|
|
||||||
float strength = std::max(left, right);
|
float strength = std::max(left, right);
|
||||||
int played = SDL_HapticRumblePlay(haptic, strength, SDL_HAPTIC_INFINITY);
|
vibration.effect.periodic.magnitude = Sint16(strength * 0x7FFF);
|
||||||
success = (played == 0);
|
|
||||||
|
success = runVibrationEffect();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (success)
|
if (success)
|
||||||
{
|
{
|
||||||
vibration.left = left;
|
vibration.left = left;
|
||||||
vibration.right = right;
|
vibration.right = right;
|
||||||
|
|
||||||
|
if (length == SDL_HAPTIC_INFINITY)
|
||||||
|
vibration.endtime = SDL_HAPTIC_INFINITY;
|
||||||
|
else
|
||||||
|
vibration.endtime = SDL_GetTicks() + length;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
vibration.left = vibration.right = 0.0f;
|
||||||
|
vibration.endtime = SDL_HAPTIC_INFINITY;
|
||||||
}
|
}
|
||||||
|
|
||||||
return success;
|
return success;
|
||||||
@@ -417,12 +441,7 @@ bool Joystick::setVibration()
|
|||||||
bool success = true;
|
bool success = true;
|
||||||
|
|
||||||
if (SDL_WasInit(SDL_INIT_HAPTIC) && haptic && SDL_HapticIndex(haptic) != -1)
|
if (SDL_WasInit(SDL_INIT_HAPTIC) && haptic && SDL_HapticIndex(haptic) != -1)
|
||||||
{
|
success = (SDL_HapticStopEffect(haptic, vibration.id) == 0);
|
||||||
// Stop all playing effects on the haptic device.
|
|
||||||
// FIXME: We should only stop the vibration effect, in case we use the
|
|
||||||
// Haptic API for other things in the future.
|
|
||||||
success = (SDL_HapticStopAll(haptic) == 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (success)
|
if (success)
|
||||||
vibration.left = vibration.right = 0.0f;
|
vibration.left = vibration.right = 0.0f;
|
||||||
@@ -430,8 +449,25 @@ bool Joystick::setVibration()
|
|||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Joystick::getVibration(float &left, float &right) const
|
void Joystick::getVibration(float &left, float &right)
|
||||||
{
|
{
|
||||||
|
if (vibration.endtime != SDL_HAPTIC_INFINITY)
|
||||||
|
{
|
||||||
|
// With some drivers, the effect physically stops at the right time, but
|
||||||
|
// SDL_HapticGetEffectStatus still thinks it's playing. So we explicitly
|
||||||
|
// stop it once it's done, just to be sure.
|
||||||
|
if (SDL_TICKS_PASSED(SDL_GetTicks(), vibration.endtime))
|
||||||
|
{
|
||||||
|
setVibration();
|
||||||
|
vibration.endtime = SDL_HAPTIC_INFINITY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the haptic effect has stopped playing.
|
||||||
|
int id = vibration.id;
|
||||||
|
if (!haptic || id == -1 || SDL_HapticGetEffectStatus(haptic, id) != 1)
|
||||||
|
vibration.left = vibration.right = 0.0f;
|
||||||
|
|
||||||
left = vibration.left;
|
left = vibration.left;
|
||||||
right = vibration.right;
|
right = vibration.right;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -74,9 +74,9 @@ public:
|
|||||||
int getID() const;
|
int getID() const;
|
||||||
|
|
||||||
bool isVibrationSupported();
|
bool isVibrationSupported();
|
||||||
bool setVibration(float left, float right);
|
bool setVibration(float left, float right, float duration = -1.0f);
|
||||||
bool setVibration();
|
bool setVibration();
|
||||||
void getVibration(float &left, float &right) const;
|
void getVibration(float &left, float &right);
|
||||||
|
|
||||||
static bool getConstant(Hat in, Uint8 &out);
|
static bool getConstant(Hat in, Uint8 &out);
|
||||||
static bool getConstant(Uint8 in, Hat &out);
|
static bool getConstant(Uint8 in, Hat &out);
|
||||||
@@ -111,8 +111,12 @@ private:
|
|||||||
Uint16 data[4];
|
Uint16 data[4];
|
||||||
int id;
|
int id;
|
||||||
|
|
||||||
|
Uint32 endtime;
|
||||||
|
|
||||||
Vibration()
|
Vibration()
|
||||||
: left(0.0f), right(0.0f), effect(), data(), id(-1)
|
: left(0.0f), right(0.0f)
|
||||||
|
, effect(), data(), id(-1)
|
||||||
|
, endtime(SDL_HAPTIC_INFINITY)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
} vibration;
|
} vibration;
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ const char *JoystickModule::getName() const
|
|||||||
love::joystick::Joystick *JoystickModule::getJoystick(int joyindex)
|
love::joystick::Joystick *JoystickModule::getJoystick(int joyindex)
|
||||||
{
|
{
|
||||||
if (joyindex < 0 || (size_t) joyindex >= activeSticks.size())
|
if (joyindex < 0 || (size_t) joyindex >= activeSticks.size())
|
||||||
return 0;
|
return nullptr;
|
||||||
|
|
||||||
return activeSticks[joyindex];
|
return activeSticks[joyindex];
|
||||||
}
|
}
|
||||||
@@ -107,13 +107,13 @@ love::joystick::Joystick *JoystickModule::getJoystickFromID(int instanceid)
|
|||||||
return activeSticks[i];
|
return activeSticks[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
love::joystick::Joystick *JoystickModule::addJoystick(int deviceindex)
|
love::joystick::Joystick *JoystickModule::addJoystick(int deviceindex)
|
||||||
{
|
{
|
||||||
if (deviceindex < 0 || deviceindex >= SDL_NumJoysticks())
|
if (deviceindex < 0 || deviceindex >= SDL_NumJoysticks())
|
||||||
return 0;
|
return nullptr;
|
||||||
|
|
||||||
std::string guidstr = getDeviceGUID(deviceindex);
|
std::string guidstr = getDeviceGUID(deviceindex);
|
||||||
joystick::Joystick *joystick = 0;
|
joystick::Joystick *joystick = 0;
|
||||||
@@ -140,7 +140,7 @@ love::joystick::Joystick *JoystickModule::addJoystick(int deviceindex)
|
|||||||
removeJoystick(joystick);
|
removeJoystick(joystick);
|
||||||
|
|
||||||
if (!joystick->open(deviceindex))
|
if (!joystick->open(deviceindex))
|
||||||
return 0;
|
return nullptr;
|
||||||
|
|
||||||
// Make sure multiple instances of the same physical joystick aren't added
|
// Make sure multiple instances of the same physical joystick aren't added
|
||||||
// to the active list.
|
// to the active list.
|
||||||
@@ -262,7 +262,7 @@ bool JoystickModule::setGamepadMapping(const std::string &guid, Joystick::Gamepa
|
|||||||
if (status == 1)
|
if (status == 1)
|
||||||
checkGamepads(guid);
|
checkGamepads(guid);
|
||||||
|
|
||||||
return status >= 0;
|
return status >= 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
Joystick::JoystickInput JoystickModule::getGamepadMapping(const std::string &guid, Joystick::GamepadInput gpinput)
|
Joystick::JoystickInput JoystickModule::getGamepadMapping(const std::string &guid, Joystick::GamepadInput gpinput)
|
||||||
@@ -318,7 +318,7 @@ std::string JoystickModule::stringFromGamepadInput(Joystick::GamepadInput gpinpu
|
|||||||
SDL_GameControllerAxis sdlaxis;
|
SDL_GameControllerAxis sdlaxis;
|
||||||
SDL_GameControllerButton sdlbutton;
|
SDL_GameControllerButton sdlbutton;
|
||||||
|
|
||||||
const char *gpinputname = 0;
|
const char *gpinputname = nullptr;
|
||||||
|
|
||||||
switch (gpinput.type)
|
switch (gpinput.type)
|
||||||
{
|
{
|
||||||
@@ -435,7 +435,7 @@ void JoystickModule::checkGamepads(const std::string &guid) const
|
|||||||
// Big hack time: open the index as a game controller and compare
|
// Big hack time: open the index as a game controller and compare
|
||||||
// the underlying joystick handle to the active stick's.
|
// the underlying joystick handle to the active stick's.
|
||||||
SDL_GameController *ctrl = SDL_GameControllerOpen(d_index);
|
SDL_GameController *ctrl = SDL_GameControllerOpen(d_index);
|
||||||
if (ctrl == NULL)
|
if (ctrl == nullptr)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
SDL_Joystick *stick = SDL_GameControllerGetJoystick(ctrl);
|
SDL_Joystick *stick = SDL_GameControllerGetJoystick(ctrl);
|
||||||
|
|||||||
@@ -207,7 +207,8 @@ int w_Joystick_setVibration(lua_State *L)
|
|||||||
{
|
{
|
||||||
float left = (float) luaL_checknumber(L, 2);
|
float left = (float) luaL_checknumber(L, 2);
|
||||||
float right = (float) luaL_optnumber(L, 3, left);
|
float right = (float) luaL_optnumber(L, 3, left);
|
||||||
success = j->setVibration(left, right);
|
float duration = (float) luaL_optnumber(L, 4, -1.0); // -1 is infinite.
|
||||||
|
success = j->setVibration(left, right, duration);
|
||||||
}
|
}
|
||||||
|
|
||||||
luax_pushboolean(L, success);
|
luax_pushboolean(L, success);
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ namespace joystick
|
|||||||
namespace sdl
|
namespace sdl
|
||||||
{
|
{
|
||||||
|
|
||||||
static JoystickModule *instance = 0;
|
static JoystickModule *instance = nullptr;
|
||||||
|
|
||||||
int w_getJoysticks(lua_State *L)
|
int w_getJoysticks(lua_State *L)
|
||||||
{
|
{
|
||||||
@@ -194,7 +194,7 @@ static const lua_CFunction types[] =
|
|||||||
|
|
||||||
extern "C" int luaopen_love_joystick(lua_State *L)
|
extern "C" int luaopen_love_joystick(lua_State *L)
|
||||||
{
|
{
|
||||||
if (instance == 0)
|
if (instance == nullptr)
|
||||||
{
|
{
|
||||||
EXCEPT_GUARD(instance = new JoystickModule();)
|
EXCEPT_GUARD(instance = new JoystickModule();)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -117,7 +117,7 @@ int Mpg123Decoder::decode()
|
|||||||
size += numbytes;
|
size += numbytes;
|
||||||
long rate = 0;
|
long rate = 0;
|
||||||
int encoding = 0;
|
int encoding = 0;
|
||||||
int ret = mpg123_getformat(handle, &rate, &channels, &encoding);
|
mpg123_getformat(handle, &rate, &channels, &encoding);
|
||||||
if (rate == 0)
|
if (rate == 0)
|
||||||
rate = sampleRate;
|
rate = sampleRate;
|
||||||
else
|
else
|
||||||
@@ -126,7 +126,7 @@ int Mpg123Decoder::decode()
|
|||||||
channels = MPG123_STEREO;
|
channels = MPG123_STEREO;
|
||||||
if (encoding == 0)
|
if (encoding == 0)
|
||||||
encoding = MPG123_ENC_SIGNED_16;
|
encoding = MPG123_ENC_SIGNED_16;
|
||||||
ret = mpg123_format(handle, rate, channels, encoding);
|
int ret = mpg123_format(handle, rate, channels, encoding);
|
||||||
if (ret != MPG123_OK)
|
if (ret != MPG123_OK)
|
||||||
throw love::Exception("Could not set output format.");
|
throw love::Exception("Could not set output format.");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,8 +27,9 @@
|
|||||||
#elif defined(LOVE_ANDROID)
|
#elif defined(LOVE_ANDROID)
|
||||||
#include "common/android.h"
|
#include "common/android.h"
|
||||||
#elif defined(LOVE_LINUX)
|
#elif defined(LOVE_LINUX)
|
||||||
#include <stdlib.h>
|
#include <spawn.h>
|
||||||
#include <unistd.h>
|
//#include <stdlib.h>
|
||||||
|
//#include <unistd.h>
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
#elif defined(LOVE_WINDOWS)
|
#elif defined(LOVE_WINDOWS)
|
||||||
#include "common/utf8.h"
|
#include "common/utf8.h"
|
||||||
@@ -57,12 +58,17 @@ std::string System::getOS() const
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern "C"
|
||||||
|
{
|
||||||
|
extern char **environ; // The environment, always available
|
||||||
|
}
|
||||||
|
|
||||||
bool System::openURL(const std::string &url) const
|
bool System::openURL(const std::string &url) const
|
||||||
{
|
{
|
||||||
bool success = false;
|
|
||||||
|
|
||||||
#if defined(LOVE_MACOSX)
|
#if defined(LOVE_MACOSX)
|
||||||
|
|
||||||
|
bool success = false;
|
||||||
// We could be lazy and use system("open " + url), but this is safer.
|
// We could be lazy and use system("open " + url), but this is safer.
|
||||||
CFURLRef cfurl = CFURLCreateWithBytes(nullptr,
|
CFURLRef cfurl = CFURLCreateWithBytes(nullptr,
|
||||||
(const UInt8 *) url.c_str(),
|
(const UInt8 *) url.c_str(),
|
||||||
@@ -72,38 +78,28 @@ bool System::openURL(const std::string &url) const
|
|||||||
|
|
||||||
success = LSOpenCFURLRef(cfurl, nullptr) == noErr;
|
success = LSOpenCFURLRef(cfurl, nullptr) == noErr;
|
||||||
CFRelease(cfurl);
|
CFRelease(cfurl);
|
||||||
|
return success;
|
||||||
|
|
||||||
#elif defined(LOVE_ANDROID)
|
#elif defined(LOVE_ANDROID)
|
||||||
|
|
||||||
success = love::android::openURL (url);
|
return love::android::openURL (url);
|
||||||
|
|
||||||
#elif defined(LOVE_LINUX)
|
#elif defined(LOVE_LINUX)
|
||||||
|
|
||||||
// Spawn a child process, which we'll replace with xdg-open.
|
pid_t pid;
|
||||||
pid_t pid = vfork();
|
const char *argv[] = {"xdg-open", url.c_str(), nullptr};
|
||||||
|
|
||||||
if (pid == 0) // Child process.
|
// Note: at the moment this process inherits our file descriptors.
|
||||||
{
|
// Note: the below const_cast is really ugly as well.
|
||||||
// Replace the child process with xdg-open and pass in the URL.
|
if (posix_spawnp(&pid, "xdg-open", nullptr, nullptr, const_cast<char **>(argv), environ) != 0)
|
||||||
execlp("xdg-open", "xdg-open", url.c_str(), nullptr);
|
return false;
|
||||||
|
|
||||||
// exec will only return if it errored, so we should exit with non-zero.
|
// Wait for xdg-open to complete (or fail.)
|
||||||
_exit(1);
|
int status = 0;
|
||||||
}
|
if (waitpid(pid, &status, 0) == pid)
|
||||||
else if (pid > 0) // Parent process.
|
return (status == 0);
|
||||||
{
|
|
||||||
// Wait for xdg-open to complete (or fail.)
|
|
||||||
int status = 0;
|
|
||||||
if (waitpid(pid, &status, 0) == pid)
|
|
||||||
success = (status == 0);
|
|
||||||
else
|
|
||||||
success = false;
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
return false;
|
||||||
// vfork() failed.
|
|
||||||
success = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
#elif defined(LOVE_WINDOWS)
|
#elif defined(LOVE_WINDOWS)
|
||||||
|
|
||||||
@@ -117,11 +113,9 @@ bool System::openURL(const std::string &url) const
|
|||||||
nullptr,
|
nullptr,
|
||||||
SW_SHOW);
|
SW_SHOW);
|
||||||
|
|
||||||
success = (int) result > 32;
|
return (int) result > 32;
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return success;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool System::getConstant(const char *in, System::PowerState &out)
|
bool System::getConstant(const char *in, System::PowerState &out)
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ LuaThread::LuaThread(const std::string &name, love::Data *code)
|
|||||||
, nargs(0)
|
, nargs(0)
|
||||||
{
|
{
|
||||||
code->retain();
|
code->retain();
|
||||||
|
threadName = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
LuaThread::~LuaThread()
|
LuaThread::~LuaThread()
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ bool Thread::start()
|
|||||||
return false;
|
return false;
|
||||||
if (thread) // Clean old handle up
|
if (thread) // Clean old handle up
|
||||||
SDL_WaitThread(thread, 0);
|
SDL_WaitThread(thread, 0);
|
||||||
thread = SDL_CreateThread(thread_runner, NULL, this);
|
thread = SDL_CreateThread(thread_runner, t->getThreadName(), this);
|
||||||
running = (thread != 0);
|
running = (thread != 0);
|
||||||
return running;
|
return running;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -99,5 +99,10 @@ bool Threadable::isRunning() const
|
|||||||
return owner->isRunning();
|
return owner->isRunning();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char *Threadable::getThreadName() const
|
||||||
|
{
|
||||||
|
return threadName.empty() ? nullptr : threadName.c_str();
|
||||||
|
}
|
||||||
|
|
||||||
} // thread
|
} // thread
|
||||||
} // love
|
} // love
|
||||||
|
|||||||
@@ -21,9 +21,13 @@
|
|||||||
#ifndef LOVE_THREAD_THREADS_H
|
#ifndef LOVE_THREAD_THREADS_H
|
||||||
#define LOVE_THREAD_THREADS_H
|
#define LOVE_THREAD_THREADS_H
|
||||||
|
|
||||||
|
// LOVE
|
||||||
#include "common/config.h"
|
#include "common/config.h"
|
||||||
#include "Thread.h"
|
#include "Thread.h"
|
||||||
|
|
||||||
|
// C++
|
||||||
|
#include <string>
|
||||||
|
|
||||||
namespace love
|
namespace love
|
||||||
{
|
{
|
||||||
namespace thread
|
namespace thread
|
||||||
@@ -83,9 +87,13 @@ public:
|
|||||||
bool start();
|
bool start();
|
||||||
void wait();
|
void wait();
|
||||||
bool isRunning() const;
|
bool isRunning() const;
|
||||||
|
const char *getThreadName() const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
Thread *owner;
|
Thread *owner;
|
||||||
|
std::string threadName;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Mutex *newMutex();
|
Mutex *newMutex();
|
||||||
|
|||||||
@@ -269,11 +269,14 @@ bool Window::setContext(int fsaa, bool vsync, bool sRGB)
|
|||||||
if (!context)
|
if (!context)
|
||||||
{
|
{
|
||||||
int flags = 0;
|
int flags = 0;
|
||||||
|
int profilemask = 0;
|
||||||
SDL_GL_GetAttribute(SDL_GL_CONTEXT_FLAGS, &flags);
|
SDL_GL_GetAttribute(SDL_GL_CONTEXT_FLAGS, &flags);
|
||||||
|
SDL_GL_GetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, &profilemask);
|
||||||
if (flags & SDL_GL_CONTEXT_DEBUG_FLAG)
|
if (flags & SDL_GL_CONTEXT_DEBUG_FLAG)
|
||||||
{
|
{
|
||||||
|
profilemask &= ~SDL_GL_CONTEXT_PROFILE_COMPATIBILITY;
|
||||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, 0);
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, 0);
|
||||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, 0);
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, profilemask);
|
||||||
context = SDL_GL_CreateContext(window);
|
context = SDL_GL_CreateContext(window);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -315,6 +318,7 @@ void Window::setWindowGLAttributes(int fsaa, bool /* sRGB */) const
|
|||||||
SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);
|
SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);
|
||||||
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
|
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
|
||||||
SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 1);
|
SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 1);
|
||||||
|
SDL_GL_SetAttribute(SDL_GL_RETAINED_BACKING, 0);
|
||||||
|
|
||||||
// FSAA.
|
// FSAA.
|
||||||
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, (fsaa > 0) ? 1 : 0);
|
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, (fsaa > 0) ? 1 : 0);
|
||||||
@@ -331,19 +335,26 @@ void Window::setWindowGLAttributes(int fsaa, bool /* sRGB */) const
|
|||||||
|
|
||||||
int contextprofile = 0;
|
int contextprofile = 0;
|
||||||
|
|
||||||
bool tryES2 = false;
|
bool tryGLES = false;
|
||||||
|
|
||||||
const char *driver = SDL_GetCurrentVideoDriver();
|
const char *driver = SDL_GetCurrentVideoDriver();
|
||||||
|
const char *glesdrivers[] = {"RPI", "Android", "uikit", "winrt"};
|
||||||
|
|
||||||
// We always want to use OpenGL ES on certain video backends.
|
// We always want to use OpenGL ES on certain video backends.
|
||||||
if (driver && (strstr(driver, "RPI") || strstr(driver, "Android") || strstr(driver, "uikit")))
|
for (size_t i = 0; i < sizeof(glesdrivers) / sizeof(glesdrivers[0]); i++)
|
||||||
tryES2 = true;
|
{
|
||||||
|
if (driver && strstr(driver, glesdrivers[i]))
|
||||||
|
{
|
||||||
|
tryGLES = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const char *hint = SDL_GetHint("LOVE_GRAPHICS_USE_OPENGLES");
|
const char *hint = SDL_GetHint("LOVE_GRAPHICS_USE_OPENGLES");
|
||||||
if (hint)
|
if (hint)
|
||||||
tryES2 = (*hint) != '0';
|
tryGLES = (*hint) != '0';
|
||||||
|
|
||||||
if (tryES2)
|
if (tryGLES)
|
||||||
{
|
{
|
||||||
// Use OpenGL ES 2+.
|
// Use OpenGL ES 2+.
|
||||||
contextprofile = SDL_GL_CONTEXT_PROFILE_ES;
|
contextprofile = SDL_GL_CONTEXT_PROFILE_ES;
|
||||||
@@ -363,7 +374,7 @@ void Window::setWindowGLAttributes(int fsaa, bool /* sRGB */) const
|
|||||||
if (debugenv && *debugenv == '1')
|
if (debugenv && *debugenv == '1')
|
||||||
{
|
{
|
||||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG);
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG);
|
||||||
if (!tryES2)
|
if (!tryGLES)
|
||||||
contextprofile |= SDL_GL_CONTEXT_PROFILE_COMPATIBILITY;
|
contextprofile |= SDL_GL_CONTEXT_PROFILE_COMPATIBILITY;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -526,7 +526,7 @@ function love.run()
|
|||||||
-- Call update and draw
|
-- Call update and draw
|
||||||
if love.update then love.update(dt) end -- will pass 0 if love.timer is disabled
|
if love.update then love.update(dt) end -- will pass 0 if love.timer is disabled
|
||||||
|
|
||||||
if love.window and love.graphics and love.window.isCreated() then
|
if love.window and love.graphics and love.graphics.isActive() then
|
||||||
love.graphics.clear()
|
love.graphics.clear()
|
||||||
love.graphics.origin()
|
love.graphics.origin()
|
||||||
if love.draw then love.draw() end
|
if love.draw then love.draw() end
|
||||||
|
|||||||
@@ -943,8 +943,8 @@ const unsigned char boot_lua[] =
|
|||||||
0x73, 0x20, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x0a,
|
0x73, 0x20, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x0a,
|
||||||
0x09, 0x09, 0x69, 0x66, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x20, 0x61,
|
0x09, 0x09, 0x69, 0x66, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x20, 0x61,
|
||||||
0x6e, 0x64, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x20, 0x61,
|
0x6e, 0x64, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x20, 0x61,
|
||||||
0x6e, 0x64, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x69, 0x73, 0x43,
|
0x6e, 0x64, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x2e, 0x69,
|
||||||
0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x28, 0x29, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a,
|
0x73, 0x41, 0x63, 0x74, 0x69, 0x76, 0x65, 0x28, 0x29, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a,
|
||||||
0x09, 0x09, 0x09, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x2e, 0x63,
|
0x09, 0x09, 0x09, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x2e, 0x63,
|
||||||
0x6c, 0x65, 0x61, 0x72, 0x28, 0x29, 0x0a,
|
0x6c, 0x65, 0x61, 0x72, 0x28, 0x29, 0x0a,
|
||||||
0x09, 0x09, 0x09, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x2e, 0x6f,
|
0x09, 0x09, 0x09, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x2e, 0x6f,
|
||||||
|
|||||||
@@ -1317,9 +1317,9 @@ uniform mat4 TransformMatrix;
|
|||||||
uniform mat4 ProjectionMatrix;
|
uniform mat4 ProjectionMatrix;
|
||||||
uniform mat4 TransformProjectionMatrix;
|
uniform mat4 TransformProjectionMatrix;
|
||||||
// uniform mat4 NormalMatrix;
|
// uniform mat4 NormalMatrix;
|
||||||
uniform float love_PointSize;
|
|
||||||
uniform sampler2D _tex0_;
|
uniform sampler2D _tex0_;
|
||||||
uniform vec4 love_ScreenSize;]]
|
uniform mediump vec4 love_ScreenSize;
|
||||||
|
uniform mediump float love_PointSize;]]
|
||||||
|
|
||||||
GLSL.VERTEX = {
|
GLSL.VERTEX = {
|
||||||
HEADER = [[
|
HEADER = [[
|
||||||
@@ -1357,7 +1357,7 @@ attribute vec4 VertexPosition;
|
|||||||
attribute vec4 VertexTexCoord;
|
attribute vec4 VertexTexCoord;
|
||||||
attribute vec4 VertexColor;
|
attribute vec4 VertexColor;
|
||||||
|
|
||||||
varying vec4 VaryingTexCoord;
|
varying mediump vec4 VaryingTexCoord;
|
||||||
varying lowp vec4 VaryingColor;
|
varying lowp vec4 VaryingColor;
|
||||||
|
|
||||||
//#if defined(GL_EXT_draw_instanced)
|
//#if defined(GL_EXT_draw_instanced)
|
||||||
@@ -1414,18 +1414,36 @@ void main() {
|
|||||||
|
|
||||||
precision mediump float;
|
precision mediump float;
|
||||||
|
|
||||||
varying vec4 VaryingTexCoord;
|
varying mediump vec4 VaryingTexCoord;
|
||||||
varying lowp vec4 VaryingColor;]],
|
varying lowp vec4 VaryingColor;]],
|
||||||
|
|
||||||
FOOTER = GLSL.PIXEL.FOOTER,
|
FOOTER = GLSL.PIXEL.FOOTER,
|
||||||
FOOTER_MULTI_CANVAS = GLSL.PIXEL.FOOTER_MULTI_CANVAS,
|
FOOTER_MULTI_CANVAS = GLSL.PIXEL.FOOTER_MULTI_CANVAS,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
-- Unfortunately, GLSL 1.20 can't deal with precision qualifier statements.
|
||||||
|
local function stripPrecision(code)
|
||||||
|
-- Remove "precision mediump float;", etc.
|
||||||
|
code = code:gsub("precision(%s+)lowp(%s+)(%w+)(%s*);", "")
|
||||||
|
code = code:gsub("precision(%s+)mediump(%s+)(%w+)(%s*);", "")
|
||||||
|
code = code:gsub("precision(%s+)highp(%s+)(%w+)(%s*);", "")
|
||||||
|
|
||||||
|
-- Remove all remaining "lowp", "mediump", "highp".
|
||||||
|
code = code:gsub("(%W+)lowp(%s+)", "%1"):gsub("^lowp(%s+)", "")
|
||||||
|
code = code:gsub("(%W+)mediump(%s+)", "%1"):gsub("^mediump(%s+)", "")
|
||||||
|
code = code:gsub("(%W+)highp(%s+)", "%1"):gsub("^highp(%s+)", "")
|
||||||
|
|
||||||
|
return code
|
||||||
|
end
|
||||||
|
|
||||||
local function createVertexCode(vertexcode, lang)
|
local function createVertexCode(vertexcode, lang)
|
||||||
|
if lang == GLSL then
|
||||||
|
vertexcode = stripPrecision(vertexcode)
|
||||||
|
end
|
||||||
local vertexcodes = {
|
local vertexcodes = {
|
||||||
lang.VERSION,
|
lang.VERSION,
|
||||||
lang.SYNTAX, lang.VERTEX.HEADER, lang.UNIFORMS,
|
lang.SYNTAX, lang.VERTEX.HEADER, lang.UNIFORMS,
|
||||||
"#line 1",
|
lang == GLSLES and "#line 1" or "#line 0", -- specification differences for #line...
|
||||||
vertexcode,
|
vertexcode,
|
||||||
lang.VERTEX.FOOTER,
|
lang.VERTEX.FOOTER,
|
||||||
}
|
}
|
||||||
@@ -1433,10 +1451,13 @@ varying lowp vec4 VaryingColor;]],
|
|||||||
end
|
end
|
||||||
|
|
||||||
local function createPixelCode(pixelcode, lang, is_multicanvas)
|
local function createPixelCode(pixelcode, lang, is_multicanvas)
|
||||||
|
if lang == GLSL then
|
||||||
|
pixelcode = stripPrecision(pixelcode)
|
||||||
|
end
|
||||||
local pixelcodes = {
|
local pixelcodes = {
|
||||||
lang.VERSION,
|
lang.VERSION,
|
||||||
lang.SYNTAX, lang.PIXEL.HEADER, lang.UNIFORMS,
|
lang.SYNTAX, lang.PIXEL.HEADER, lang.UNIFORMS,
|
||||||
"#line 1",
|
lang == GLSLES and "#line 1" or "#line 0",
|
||||||
pixelcode,
|
pixelcode,
|
||||||
is_multicanvas and lang.PIXEL.FOOTER_MULTI_CANVAS or lang.PIXEL.FOOTER,
|
is_multicanvas and lang.PIXEL.FOOTER_MULTI_CANVAS or lang.PIXEL.FOOTER,
|
||||||
}
|
}
|
||||||
@@ -1539,7 +1560,7 @@ vec4 position(mat4 transform_proj, vec4 vertpos) {
|
|||||||
return transform_proj * vertpos;
|
return transform_proj * vertpos;
|
||||||
}]],
|
}]],
|
||||||
pixel = [[
|
pixel = [[
|
||||||
vec4 effect(vec4 vcolor, Image texture, vec2 texcoord, vec2 pixcoord) {
|
lowp vec4 effect(lowp vec4 vcolor, Image texture, vec2 texcoord, vec2 pixcoord) {
|
||||||
return Texel(texture, texcoord) * vcolor;
|
return Texel(texture, texcoord) * vcolor;
|
||||||
}]]
|
}]]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6312,12 +6312,14 @@ const unsigned char graphics_lua[] =
|
|||||||
0x69, 0x78, 0x3b, 0x0a,
|
0x69, 0x78, 0x3b, 0x0a,
|
||||||
0x2f, 0x2f, 0x20, 0x75, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x20, 0x6d, 0x61, 0x74, 0x34, 0x20, 0x4e, 0x6f,
|
0x2f, 0x2f, 0x20, 0x75, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x20, 0x6d, 0x61, 0x74, 0x34, 0x20, 0x4e, 0x6f,
|
||||||
0x72, 0x6d, 0x61, 0x6c, 0x4d, 0x61, 0x74, 0x72, 0x69, 0x78, 0x3b, 0x0a,
|
0x72, 0x6d, 0x61, 0x6c, 0x4d, 0x61, 0x74, 0x72, 0x69, 0x78, 0x3b, 0x0a,
|
||||||
0x75, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x6c, 0x6f, 0x76, 0x65,
|
|
||||||
0x5f, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x53, 0x69, 0x7a, 0x65, 0x3b, 0x0a,
|
|
||||||
0x75, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x20, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x72, 0x32, 0x44, 0x20,
|
0x75, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x20, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x72, 0x32, 0x44, 0x20,
|
||||||
0x5f, 0x74, 0x65, 0x78, 0x30, 0x5f, 0x3b, 0x0a,
|
0x5f, 0x74, 0x65, 0x78, 0x30, 0x5f, 0x3b, 0x0a,
|
||||||
0x75, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x20, 0x76, 0x65, 0x63, 0x34, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x5f,
|
0x75, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x20, 0x6d, 0x65, 0x64, 0x69, 0x75, 0x6d, 0x70, 0x20, 0x76, 0x65,
|
||||||
0x53, 0x63, 0x72, 0x65, 0x65, 0x6e, 0x53, 0x69, 0x7a, 0x65, 0x3b, 0x5d, 0x5d, 0x0a,
|
0x63, 0x34, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x5f, 0x53, 0x63, 0x72, 0x65, 0x65, 0x6e, 0x53, 0x69, 0x7a, 0x65,
|
||||||
|
0x3b, 0x0a,
|
||||||
|
0x75, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x20, 0x6d, 0x65, 0x64, 0x69, 0x75, 0x6d, 0x70, 0x20, 0x66, 0x6c,
|
||||||
|
0x6f, 0x61, 0x74, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x5f, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x53, 0x69, 0x7a, 0x65,
|
||||||
|
0x3b, 0x5d, 0x5d, 0x0a,
|
||||||
0x09, 0x47, 0x4c, 0x53, 0x4c, 0x2e, 0x56, 0x45, 0x52, 0x54, 0x45, 0x58, 0x20, 0x3d, 0x20, 0x7b, 0x0a,
|
0x09, 0x47, 0x4c, 0x53, 0x4c, 0x2e, 0x56, 0x45, 0x52, 0x54, 0x45, 0x58, 0x20, 0x3d, 0x20, 0x7b, 0x0a,
|
||||||
0x09, 0x09, 0x48, 0x45, 0x41, 0x44, 0x45, 0x52, 0x20, 0x3d, 0x20, 0x5b, 0x5b, 0x0a,
|
0x09, 0x09, 0x48, 0x45, 0x41, 0x44, 0x45, 0x52, 0x20, 0x3d, 0x20, 0x5b, 0x5b, 0x0a,
|
||||||
0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x56, 0x45, 0x52, 0x54, 0x45, 0x58, 0x0a,
|
0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x56, 0x45, 0x52, 0x54, 0x45, 0x58, 0x0a,
|
||||||
@@ -6373,8 +6375,9 @@ const unsigned char graphics_lua[] =
|
|||||||
0x74, 0x65, 0x78, 0x54, 0x65, 0x78, 0x43, 0x6f, 0x6f, 0x72, 0x64, 0x3b, 0x0a,
|
0x74, 0x65, 0x78, 0x54, 0x65, 0x78, 0x43, 0x6f, 0x6f, 0x72, 0x64, 0x3b, 0x0a,
|
||||||
0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x76, 0x65, 0x63, 0x34, 0x20, 0x56, 0x65, 0x72,
|
0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x76, 0x65, 0x63, 0x34, 0x20, 0x56, 0x65, 0x72,
|
||||||
0x74, 0x65, 0x78, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x3b, 0x0a,
|
0x74, 0x65, 0x78, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x3b, 0x0a,
|
||||||
0x76, 0x61, 0x72, 0x79, 0x69, 0x6e, 0x67, 0x20, 0x76, 0x65, 0x63, 0x34, 0x20, 0x56, 0x61, 0x72, 0x79, 0x69,
|
0x76, 0x61, 0x72, 0x79, 0x69, 0x6e, 0x67, 0x20, 0x6d, 0x65, 0x64, 0x69, 0x75, 0x6d, 0x70, 0x20, 0x76, 0x65,
|
||||||
0x6e, 0x67, 0x54, 0x65, 0x78, 0x43, 0x6f, 0x6f, 0x72, 0x64, 0x3b, 0x0a,
|
0x63, 0x34, 0x20, 0x56, 0x61, 0x72, 0x79, 0x69, 0x6e, 0x67, 0x54, 0x65, 0x78, 0x43, 0x6f, 0x6f, 0x72, 0x64,
|
||||||
|
0x3b, 0x0a,
|
||||||
0x76, 0x61, 0x72, 0x79, 0x69, 0x6e, 0x67, 0x20, 0x6c, 0x6f, 0x77, 0x70, 0x20, 0x76, 0x65, 0x63, 0x34, 0x20,
|
0x76, 0x61, 0x72, 0x79, 0x69, 0x6e, 0x67, 0x20, 0x6c, 0x6f, 0x77, 0x70, 0x20, 0x76, 0x65, 0x63, 0x34, 0x20,
|
||||||
0x56, 0x61, 0x72, 0x79, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x3b, 0x0a,
|
0x56, 0x61, 0x72, 0x79, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x3b, 0x0a,
|
||||||
0x2f, 0x2f, 0x23, 0x69, 0x66, 0x20, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x28, 0x47, 0x4c, 0x5f, 0x45,
|
0x2f, 0x2f, 0x23, 0x69, 0x66, 0x20, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x28, 0x47, 0x4c, 0x5f, 0x45,
|
||||||
@@ -6470,8 +6473,9 @@ const unsigned char graphics_lua[] =
|
|||||||
0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x50, 0x49, 0x58, 0x45, 0x4c, 0x0a,
|
0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x50, 0x49, 0x58, 0x45, 0x4c, 0x0a,
|
||||||
0x70, 0x72, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x6d, 0x65, 0x64, 0x69, 0x75, 0x6d, 0x70, 0x20,
|
0x70, 0x72, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x6d, 0x65, 0x64, 0x69, 0x75, 0x6d, 0x70, 0x20,
|
||||||
0x66, 0x6c, 0x6f, 0x61, 0x74, 0x3b, 0x0a,
|
0x66, 0x6c, 0x6f, 0x61, 0x74, 0x3b, 0x0a,
|
||||||
0x76, 0x61, 0x72, 0x79, 0x69, 0x6e, 0x67, 0x20, 0x76, 0x65, 0x63, 0x34, 0x20, 0x56, 0x61, 0x72, 0x79, 0x69,
|
0x76, 0x61, 0x72, 0x79, 0x69, 0x6e, 0x67, 0x20, 0x6d, 0x65, 0x64, 0x69, 0x75, 0x6d, 0x70, 0x20, 0x76, 0x65,
|
||||||
0x6e, 0x67, 0x54, 0x65, 0x78, 0x43, 0x6f, 0x6f, 0x72, 0x64, 0x3b, 0x0a,
|
0x63, 0x34, 0x20, 0x56, 0x61, 0x72, 0x79, 0x69, 0x6e, 0x67, 0x54, 0x65, 0x78, 0x43, 0x6f, 0x6f, 0x72, 0x64,
|
||||||
|
0x3b, 0x0a,
|
||||||
0x76, 0x61, 0x72, 0x79, 0x69, 0x6e, 0x67, 0x20, 0x6c, 0x6f, 0x77, 0x70, 0x20, 0x76, 0x65, 0x63, 0x34, 0x20,
|
0x76, 0x61, 0x72, 0x79, 0x69, 0x6e, 0x67, 0x20, 0x6c, 0x6f, 0x77, 0x70, 0x20, 0x76, 0x65, 0x63, 0x34, 0x20,
|
||||||
0x56, 0x61, 0x72, 0x79, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x3b, 0x5d, 0x5d, 0x2c, 0x0a,
|
0x56, 0x61, 0x72, 0x79, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x3b, 0x5d, 0x5d, 0x2c, 0x0a,
|
||||||
0x09, 0x09, 0x46, 0x4f, 0x4f, 0x54, 0x45, 0x52, 0x20, 0x3d, 0x20, 0x47, 0x4c, 0x53, 0x4c, 0x2e, 0x50, 0x49,
|
0x09, 0x09, 0x46, 0x4f, 0x4f, 0x54, 0x45, 0x52, 0x20, 0x3d, 0x20, 0x47, 0x4c, 0x53, 0x4c, 0x2e, 0x50, 0x49,
|
||||||
@@ -6481,16 +6485,65 @@ const unsigned char graphics_lua[] =
|
|||||||
0x4f, 0x4f, 0x54, 0x45, 0x52, 0x5f, 0x4d, 0x55, 0x4c, 0x54, 0x49, 0x5f, 0x43, 0x41, 0x4e, 0x56, 0x41, 0x53,
|
0x4f, 0x4f, 0x54, 0x45, 0x52, 0x5f, 0x4d, 0x55, 0x4c, 0x54, 0x49, 0x5f, 0x43, 0x41, 0x4e, 0x56, 0x41, 0x53,
|
||||||
0x2c, 0x0a,
|
0x2c, 0x0a,
|
||||||
0x09, 0x7d, 0x0a,
|
0x09, 0x7d, 0x0a,
|
||||||
|
0x09, 0x2d, 0x2d, 0x20, 0x55, 0x6e, 0x66, 0x6f, 0x72, 0x74, 0x75, 0x6e, 0x61, 0x74, 0x65, 0x6c, 0x79, 0x2c,
|
||||||
|
0x20, 0x47, 0x4c, 0x53, 0x4c, 0x20, 0x31, 0x2e, 0x32, 0x30, 0x20, 0x63, 0x61, 0x6e, 0x27, 0x74, 0x20, 0x64,
|
||||||
|
0x65, 0x61, 0x6c, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x70, 0x72, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e,
|
||||||
|
0x20, 0x71, 0x75, 0x61, 0x6c, 0x69, 0x66, 0x69, 0x65, 0x72, 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65,
|
||||||
|
0x6e, 0x74, 0x73, 0x2e, 0x0a,
|
||||||
|
0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x73, 0x74,
|
||||||
|
0x72, 0x69, 0x70, 0x50, 0x72, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x28, 0x63, 0x6f, 0x64, 0x65, 0x29, 0x0a,
|
||||||
|
0x09, 0x09, 0x2d, 0x2d, 0x20, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x20, 0x22, 0x70, 0x72, 0x65, 0x63, 0x69,
|
||||||
|
0x73, 0x69, 0x6f, 0x6e, 0x20, 0x6d, 0x65, 0x64, 0x69, 0x75, 0x6d, 0x70, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74,
|
||||||
|
0x3b, 0x22, 0x2c, 0x20, 0x65, 0x74, 0x63, 0x2e, 0x0a,
|
||||||
|
0x09, 0x09, 0x63, 0x6f, 0x64, 0x65, 0x20, 0x3d, 0x20, 0x63, 0x6f, 0x64, 0x65, 0x3a, 0x67, 0x73, 0x75, 0x62,
|
||||||
|
0x28, 0x22, 0x70, 0x72, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x28, 0x25, 0x73, 0x2b, 0x29, 0x6c, 0x6f,
|
||||||
|
0x77, 0x70, 0x28, 0x25, 0x73, 0x2b, 0x29, 0x28, 0x25, 0x77, 0x2b, 0x29, 0x28, 0x25, 0x73, 0x2a, 0x29, 0x3b,
|
||||||
|
0x22, 0x2c, 0x20, 0x22, 0x22, 0x29, 0x0a,
|
||||||
|
0x09, 0x09, 0x63, 0x6f, 0x64, 0x65, 0x20, 0x3d, 0x20, 0x63, 0x6f, 0x64, 0x65, 0x3a, 0x67, 0x73, 0x75, 0x62,
|
||||||
|
0x28, 0x22, 0x70, 0x72, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x28, 0x25, 0x73, 0x2b, 0x29, 0x6d, 0x65,
|
||||||
|
0x64, 0x69, 0x75, 0x6d, 0x70, 0x28, 0x25, 0x73, 0x2b, 0x29, 0x28, 0x25, 0x77, 0x2b, 0x29, 0x28, 0x25, 0x73,
|
||||||
|
0x2a, 0x29, 0x3b, 0x22, 0x2c, 0x20, 0x22, 0x22, 0x29, 0x0a,
|
||||||
|
0x09, 0x09, 0x63, 0x6f, 0x64, 0x65, 0x20, 0x3d, 0x20, 0x63, 0x6f, 0x64, 0x65, 0x3a, 0x67, 0x73, 0x75, 0x62,
|
||||||
|
0x28, 0x22, 0x70, 0x72, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x28, 0x25, 0x73, 0x2b, 0x29, 0x68, 0x69,
|
||||||
|
0x67, 0x68, 0x70, 0x28, 0x25, 0x73, 0x2b, 0x29, 0x28, 0x25, 0x77, 0x2b, 0x29, 0x28, 0x25, 0x73, 0x2a, 0x29,
|
||||||
|
0x3b, 0x22, 0x2c, 0x20, 0x22, 0x22, 0x29, 0x0a,
|
||||||
|
0x09, 0x09, 0x2d, 0x2d, 0x20, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x20, 0x61, 0x6c, 0x6c, 0x20, 0x72, 0x65,
|
||||||
|
0x6d, 0x61, 0x69, 0x6e, 0x69, 0x6e, 0x67, 0x20, 0x22, 0x6c, 0x6f, 0x77, 0x70, 0x22, 0x2c, 0x20, 0x22, 0x6d,
|
||||||
|
0x65, 0x64, 0x69, 0x75, 0x6d, 0x70, 0x22, 0x2c, 0x20, 0x22, 0x68, 0x69, 0x67, 0x68, 0x70, 0x22, 0x2e, 0x0a,
|
||||||
|
0x09, 0x09, 0x63, 0x6f, 0x64, 0x65, 0x20, 0x3d, 0x20, 0x63, 0x6f, 0x64, 0x65, 0x3a, 0x67, 0x73, 0x75, 0x62,
|
||||||
|
0x28, 0x22, 0x28, 0x25, 0x57, 0x2b, 0x29, 0x6c, 0x6f, 0x77, 0x70, 0x28, 0x25, 0x73, 0x2b, 0x29, 0x22, 0x2c,
|
||||||
|
0x20, 0x22, 0x25, 0x31, 0x22, 0x29, 0x3a, 0x67, 0x73, 0x75, 0x62, 0x28, 0x22, 0x5e, 0x6c, 0x6f, 0x77, 0x70,
|
||||||
|
0x28, 0x25, 0x73, 0x2b, 0x29, 0x22, 0x2c, 0x20, 0x22, 0x22, 0x29, 0x0a,
|
||||||
|
0x09, 0x09, 0x63, 0x6f, 0x64, 0x65, 0x20, 0x3d, 0x20, 0x63, 0x6f, 0x64, 0x65, 0x3a, 0x67, 0x73, 0x75, 0x62,
|
||||||
|
0x28, 0x22, 0x28, 0x25, 0x57, 0x2b, 0x29, 0x6d, 0x65, 0x64, 0x69, 0x75, 0x6d, 0x70, 0x28, 0x25, 0x73, 0x2b,
|
||||||
|
0x29, 0x22, 0x2c, 0x20, 0x22, 0x25, 0x31, 0x22, 0x29, 0x3a, 0x67, 0x73, 0x75, 0x62, 0x28, 0x22, 0x5e, 0x6d,
|
||||||
|
0x65, 0x64, 0x69, 0x75, 0x6d, 0x70, 0x28, 0x25, 0x73, 0x2b, 0x29, 0x22, 0x2c, 0x20, 0x22, 0x22, 0x29, 0x0a,
|
||||||
|
0x09, 0x09, 0x63, 0x6f, 0x64, 0x65, 0x20, 0x3d, 0x20, 0x63, 0x6f, 0x64, 0x65, 0x3a, 0x67, 0x73, 0x75, 0x62,
|
||||||
|
0x28, 0x22, 0x28, 0x25, 0x57, 0x2b, 0x29, 0x68, 0x69, 0x67, 0x68, 0x70, 0x28, 0x25, 0x73, 0x2b, 0x29, 0x22,
|
||||||
|
0x2c, 0x20, 0x22, 0x25, 0x31, 0x22, 0x29, 0x3a, 0x67, 0x73, 0x75, 0x62, 0x28, 0x22, 0x5e, 0x68, 0x69, 0x67,
|
||||||
|
0x68, 0x70, 0x28, 0x25, 0x73, 0x2b, 0x29, 0x22, 0x2c, 0x20, 0x22, 0x22, 0x29, 0x0a,
|
||||||
|
0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x63, 0x6f, 0x64, 0x65, 0x0a,
|
||||||
|
0x09, 0x65, 0x6e, 0x64, 0x0a,
|
||||||
0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x63, 0x72,
|
0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x63, 0x72,
|
||||||
0x65, 0x61, 0x74, 0x65, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, 0x43, 0x6f, 0x64, 0x65, 0x28, 0x76, 0x65, 0x72,
|
0x65, 0x61, 0x74, 0x65, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, 0x43, 0x6f, 0x64, 0x65, 0x28, 0x76, 0x65, 0x72,
|
||||||
0x74, 0x65, 0x78, 0x63, 0x6f, 0x64, 0x65, 0x2c, 0x20, 0x6c, 0x61, 0x6e, 0x67, 0x29, 0x0a,
|
0x74, 0x65, 0x78, 0x63, 0x6f, 0x64, 0x65, 0x2c, 0x20, 0x6c, 0x61, 0x6e, 0x67, 0x29, 0x0a,
|
||||||
|
0x09, 0x09, 0x69, 0x66, 0x20, 0x6c, 0x61, 0x6e, 0x67, 0x20, 0x3d, 0x3d, 0x20, 0x47, 0x4c, 0x53, 0x4c, 0x20,
|
||||||
|
0x74, 0x68, 0x65, 0x6e, 0x0a,
|
||||||
|
0x09, 0x09, 0x09, 0x76, 0x65, 0x72, 0x74, 0x65, 0x78, 0x63, 0x6f, 0x64, 0x65, 0x20, 0x3d, 0x20, 0x73, 0x74,
|
||||||
|
0x72, 0x69, 0x70, 0x50, 0x72, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x28, 0x76, 0x65, 0x72, 0x74, 0x65,
|
||||||
|
0x78, 0x63, 0x6f, 0x64, 0x65, 0x29, 0x0a,
|
||||||
|
0x09, 0x09, 0x65, 0x6e, 0x64, 0x0a,
|
||||||
0x09, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x76, 0x65, 0x72, 0x74, 0x65, 0x78, 0x63, 0x6f, 0x64, 0x65,
|
0x09, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x76, 0x65, 0x72, 0x74, 0x65, 0x78, 0x63, 0x6f, 0x64, 0x65,
|
||||||
0x73, 0x20, 0x3d, 0x20, 0x7b, 0x0a,
|
0x73, 0x20, 0x3d, 0x20, 0x7b, 0x0a,
|
||||||
0x09, 0x09, 0x09, 0x6c, 0x61, 0x6e, 0x67, 0x2e, 0x56, 0x45, 0x52, 0x53, 0x49, 0x4f, 0x4e, 0x2c, 0x0a,
|
0x09, 0x09, 0x09, 0x6c, 0x61, 0x6e, 0x67, 0x2e, 0x56, 0x45, 0x52, 0x53, 0x49, 0x4f, 0x4e, 0x2c, 0x0a,
|
||||||
0x09, 0x09, 0x09, 0x6c, 0x61, 0x6e, 0x67, 0x2e, 0x53, 0x59, 0x4e, 0x54, 0x41, 0x58, 0x2c, 0x20, 0x6c, 0x61,
|
0x09, 0x09, 0x09, 0x6c, 0x61, 0x6e, 0x67, 0x2e, 0x53, 0x59, 0x4e, 0x54, 0x41, 0x58, 0x2c, 0x20, 0x6c, 0x61,
|
||||||
0x6e, 0x67, 0x2e, 0x56, 0x45, 0x52, 0x54, 0x45, 0x58, 0x2e, 0x48, 0x45, 0x41, 0x44, 0x45, 0x52, 0x2c, 0x20,
|
0x6e, 0x67, 0x2e, 0x56, 0x45, 0x52, 0x54, 0x45, 0x58, 0x2e, 0x48, 0x45, 0x41, 0x44, 0x45, 0x52, 0x2c, 0x20,
|
||||||
0x6c, 0x61, 0x6e, 0x67, 0x2e, 0x55, 0x4e, 0x49, 0x46, 0x4f, 0x52, 0x4d, 0x53, 0x2c, 0x0a,
|
0x6c, 0x61, 0x6e, 0x67, 0x2e, 0x55, 0x4e, 0x49, 0x46, 0x4f, 0x52, 0x4d, 0x53, 0x2c, 0x0a,
|
||||||
0x09, 0x09, 0x09, 0x22, 0x23, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x31, 0x22, 0x2c, 0x0a,
|
0x09, 0x09, 0x09, 0x6c, 0x61, 0x6e, 0x67, 0x20, 0x3d, 0x3d, 0x20, 0x47, 0x4c, 0x53, 0x4c, 0x45, 0x53, 0x20,
|
||||||
|
0x61, 0x6e, 0x64, 0x20, 0x22, 0x23, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x31, 0x22, 0x20, 0x6f, 0x72, 0x20, 0x22,
|
||||||
|
0x23, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x30, 0x22, 0x2c, 0x20, 0x2d, 0x2d, 0x20, 0x73, 0x70, 0x65, 0x63, 0x69,
|
||||||
|
0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x64, 0x69, 0x66, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63,
|
||||||
|
0x65, 0x73, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x23, 0x6c, 0x69, 0x6e, 0x65, 0x2e, 0x2e, 0x2e, 0x0a,
|
||||||
0x09, 0x09, 0x09, 0x76, 0x65, 0x72, 0x74, 0x65, 0x78, 0x63, 0x6f, 0x64, 0x65, 0x2c, 0x0a,
|
0x09, 0x09, 0x09, 0x76, 0x65, 0x72, 0x74, 0x65, 0x78, 0x63, 0x6f, 0x64, 0x65, 0x2c, 0x0a,
|
||||||
0x09, 0x09, 0x09, 0x6c, 0x61, 0x6e, 0x67, 0x2e, 0x56, 0x45, 0x52, 0x54, 0x45, 0x58, 0x2e, 0x46, 0x4f, 0x4f,
|
0x09, 0x09, 0x09, 0x6c, 0x61, 0x6e, 0x67, 0x2e, 0x56, 0x45, 0x52, 0x54, 0x45, 0x58, 0x2e, 0x46, 0x4f, 0x4f,
|
||||||
0x54, 0x45, 0x52, 0x2c, 0x0a,
|
0x54, 0x45, 0x52, 0x2c, 0x0a,
|
||||||
@@ -6503,13 +6556,21 @@ const unsigned char graphics_lua[] =
|
|||||||
0x65, 0x61, 0x74, 0x65, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x43, 0x6f, 0x64, 0x65, 0x28, 0x70, 0x69, 0x78, 0x65,
|
0x65, 0x61, 0x74, 0x65, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x43, 0x6f, 0x64, 0x65, 0x28, 0x70, 0x69, 0x78, 0x65,
|
||||||
0x6c, 0x63, 0x6f, 0x64, 0x65, 0x2c, 0x20, 0x6c, 0x61, 0x6e, 0x67, 0x2c, 0x20, 0x69, 0x73, 0x5f, 0x6d, 0x75,
|
0x6c, 0x63, 0x6f, 0x64, 0x65, 0x2c, 0x20, 0x6c, 0x61, 0x6e, 0x67, 0x2c, 0x20, 0x69, 0x73, 0x5f, 0x6d, 0x75,
|
||||||
0x6c, 0x74, 0x69, 0x63, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x29, 0x0a,
|
0x6c, 0x74, 0x69, 0x63, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x29, 0x0a,
|
||||||
|
0x09, 0x09, 0x69, 0x66, 0x20, 0x6c, 0x61, 0x6e, 0x67, 0x20, 0x3d, 0x3d, 0x20, 0x47, 0x4c, 0x53, 0x4c, 0x20,
|
||||||
|
0x74, 0x68, 0x65, 0x6e, 0x0a,
|
||||||
|
0x09, 0x09, 0x09, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x63, 0x6f, 0x64, 0x65, 0x20, 0x3d, 0x20, 0x73, 0x74, 0x72,
|
||||||
|
0x69, 0x70, 0x50, 0x72, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x28, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x63,
|
||||||
|
0x6f, 0x64, 0x65, 0x29, 0x0a,
|
||||||
|
0x09, 0x09, 0x65, 0x6e, 0x64, 0x0a,
|
||||||
0x09, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x63, 0x6f, 0x64, 0x65, 0x73,
|
0x09, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x63, 0x6f, 0x64, 0x65, 0x73,
|
||||||
0x20, 0x3d, 0x20, 0x7b, 0x0a,
|
0x20, 0x3d, 0x20, 0x7b, 0x0a,
|
||||||
0x09, 0x09, 0x09, 0x6c, 0x61, 0x6e, 0x67, 0x2e, 0x56, 0x45, 0x52, 0x53, 0x49, 0x4f, 0x4e, 0x2c, 0x0a,
|
0x09, 0x09, 0x09, 0x6c, 0x61, 0x6e, 0x67, 0x2e, 0x56, 0x45, 0x52, 0x53, 0x49, 0x4f, 0x4e, 0x2c, 0x0a,
|
||||||
0x09, 0x09, 0x09, 0x6c, 0x61, 0x6e, 0x67, 0x2e, 0x53, 0x59, 0x4e, 0x54, 0x41, 0x58, 0x2c, 0x20, 0x6c, 0x61,
|
0x09, 0x09, 0x09, 0x6c, 0x61, 0x6e, 0x67, 0x2e, 0x53, 0x59, 0x4e, 0x54, 0x41, 0x58, 0x2c, 0x20, 0x6c, 0x61,
|
||||||
0x6e, 0x67, 0x2e, 0x50, 0x49, 0x58, 0x45, 0x4c, 0x2e, 0x48, 0x45, 0x41, 0x44, 0x45, 0x52, 0x2c, 0x20, 0x6c,
|
0x6e, 0x67, 0x2e, 0x50, 0x49, 0x58, 0x45, 0x4c, 0x2e, 0x48, 0x45, 0x41, 0x44, 0x45, 0x52, 0x2c, 0x20, 0x6c,
|
||||||
0x61, 0x6e, 0x67, 0x2e, 0x55, 0x4e, 0x49, 0x46, 0x4f, 0x52, 0x4d, 0x53, 0x2c, 0x0a,
|
0x61, 0x6e, 0x67, 0x2e, 0x55, 0x4e, 0x49, 0x46, 0x4f, 0x52, 0x4d, 0x53, 0x2c, 0x0a,
|
||||||
0x09, 0x09, 0x09, 0x22, 0x23, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x31, 0x22, 0x2c, 0x0a,
|
0x09, 0x09, 0x09, 0x6c, 0x61, 0x6e, 0x67, 0x20, 0x3d, 0x3d, 0x20, 0x47, 0x4c, 0x53, 0x4c, 0x45, 0x53, 0x20,
|
||||||
|
0x61, 0x6e, 0x64, 0x20, 0x22, 0x23, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x31, 0x22, 0x20, 0x6f, 0x72, 0x20, 0x22,
|
||||||
|
0x23, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x30, 0x22, 0x2c, 0x0a,
|
||||||
0x09, 0x09, 0x09, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x63, 0x6f, 0x64, 0x65, 0x2c, 0x0a,
|
0x09, 0x09, 0x09, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x63, 0x6f, 0x64, 0x65, 0x2c, 0x0a,
|
||||||
0x09, 0x09, 0x09, 0x69, 0x73, 0x5f, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x63, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x20,
|
0x09, 0x09, 0x09, 0x69, 0x73, 0x5f, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x63, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x20,
|
||||||
0x61, 0x6e, 0x64, 0x20, 0x6c, 0x61, 0x6e, 0x67, 0x2e, 0x50, 0x49, 0x58, 0x45, 0x4c, 0x2e, 0x46, 0x4f, 0x4f,
|
0x61, 0x6e, 0x64, 0x20, 0x6c, 0x61, 0x6e, 0x67, 0x2e, 0x50, 0x49, 0x58, 0x45, 0x4c, 0x2e, 0x46, 0x4f, 0x4f,
|
||||||
@@ -6725,10 +6786,11 @@ const unsigned char graphics_lua[] =
|
|||||||
0x70, 0x72, 0x6f, 0x6a, 0x20, 0x2a, 0x20, 0x76, 0x65, 0x72, 0x74, 0x70, 0x6f, 0x73, 0x3b, 0x09, 0x0a,
|
0x70, 0x72, 0x6f, 0x6a, 0x20, 0x2a, 0x20, 0x76, 0x65, 0x72, 0x74, 0x70, 0x6f, 0x73, 0x3b, 0x09, 0x0a,
|
||||||
0x7d, 0x5d, 0x5d, 0x2c, 0x0a,
|
0x7d, 0x5d, 0x5d, 0x2c, 0x0a,
|
||||||
0x09, 0x09, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x20, 0x3d, 0x20, 0x5b, 0x5b, 0x0a,
|
0x09, 0x09, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x20, 0x3d, 0x20, 0x5b, 0x5b, 0x0a,
|
||||||
0x76, 0x65, 0x63, 0x34, 0x20, 0x65, 0x66, 0x66, 0x65, 0x63, 0x74, 0x28, 0x76, 0x65, 0x63, 0x34, 0x20, 0x76,
|
0x6c, 0x6f, 0x77, 0x70, 0x20, 0x76, 0x65, 0x63, 0x34, 0x20, 0x65, 0x66, 0x66, 0x65, 0x63, 0x74, 0x28, 0x6c,
|
||||||
0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x2c, 0x20, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x20, 0x74, 0x65, 0x78, 0x74, 0x75,
|
0x6f, 0x77, 0x70, 0x20, 0x76, 0x65, 0x63, 0x34, 0x20, 0x76, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x2c, 0x20, 0x49,
|
||||||
0x72, 0x65, 0x2c, 0x20, 0x76, 0x65, 0x63, 0x32, 0x20, 0x74, 0x65, 0x78, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x2c,
|
0x6d, 0x61, 0x67, 0x65, 0x20, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x2c, 0x20, 0x76, 0x65, 0x63, 0x32,
|
||||||
0x20, 0x76, 0x65, 0x63, 0x32, 0x20, 0x70, 0x69, 0x78, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x29, 0x20, 0x7b, 0x0a,
|
0x20, 0x74, 0x65, 0x78, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x2c, 0x20, 0x76, 0x65, 0x63, 0x32, 0x20, 0x70, 0x69,
|
||||||
|
0x78, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x29, 0x20, 0x7b, 0x0a,
|
||||||
0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x54, 0x65, 0x78, 0x65, 0x6c, 0x28, 0x74, 0x65, 0x78, 0x74,
|
0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x54, 0x65, 0x78, 0x65, 0x6c, 0x28, 0x74, 0x65, 0x78, 0x74,
|
||||||
0x75, 0x72, 0x65, 0x2c, 0x20, 0x74, 0x65, 0x78, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x29, 0x20, 0x2a, 0x20, 0x76,
|
0x75, 0x72, 0x65, 0x2c, 0x20, 0x74, 0x65, 0x78, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x29, 0x20, 0x2a, 0x20, 0x76,
|
||||||
0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x3b, 0x09, 0x0a,
|
0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x3b, 0x09, 0x0a,
|
||||||
|
|||||||
Reference in New Issue
Block a user