updated to latest mobile-common (0cf55b7da58e), removed libtiff, libpng, libmng, devil, jasper, jpeg-8d, added libturbo-jpeg

This commit is contained in:
fysx
2014-08-27 23:19:11 +02:00
parent aba2f47b31
commit abb819335b
1910 changed files with 78167 additions and 922995 deletions
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+4 -4
View File
@@ -51,7 +51,7 @@ public:
*/
enum DistanceModel
{
DISTANCE_NONE = 1,
DISTANCE_NONE,
DISTANCE_INVERSE,
DISTANCE_INVERSE_CLAMPED,
DISTANCE_LINEAR,
@@ -64,11 +64,11 @@ public:
static bool getConstant(const char *in, DistanceModel &out);
static bool getConstant(DistanceModel in, const char *&out);
/**
* Destructor.
**/
virtual ~Audio() {}
// Implements Module.
virtual ModuleType getModuleType() const { return M_AUDIO; }
virtual Source *newSource(love::sound::Decoder *decoder) = 0;
virtual Source *newSource(love::sound::SoundData *soundData) = 0;
+2 -2
View File
@@ -36,14 +36,14 @@ public:
enum Type
{
TYPE_STATIC = 1,
TYPE_STATIC,
TYPE_STREAM,
TYPE_MAX_ENUM
}; // Type
enum Unit
{
UNIT_SECONDS = 1,
UNIT_SECONDS,
UNIT_SAMPLES,
UNIT_MAX_ENUM
};
+9 -13
View File
@@ -98,7 +98,6 @@ Source::Source(Pool *pool, love::sound::Decoder *decoder)
, decoder(decoder)
, toLoop(0)
{
decoder->retain();
alGenBuffers(MAX_BUFFERS, streamBuffers);
float z[3] = {0, 0, 0};
@@ -132,13 +131,15 @@ Source::Source(const Source &s)
{
if (type == TYPE_STREAM)
{
if (s.decoder)
decoder = s.decoder->clone();
if (s.decoder.get())
{
love::sound::Decoder *dec = s.decoder->clone();
decoder.set(dec);
dec->release();
}
alGenBuffers(MAX_BUFFERS, streamBuffers);
}
else
staticBuffer->retain();
setFloatv(position, s.position);
setFloatv(velocity, s.velocity);
@@ -152,12 +153,6 @@ Source::~Source()
if (type == TYPE_STREAM)
alDeleteBuffers(MAX_BUFFERS, streamBuffers);
if (staticBuffer)
staticBuffer->release();
if (decoder)
decoder->release();
}
love::audio::Source *Source::clone()
@@ -269,7 +264,7 @@ bool Source::update()
offsetSamples += (curOffsetSamples - newOffsetSamples);
offsetSeconds += (curOffsetSecs - newOffsetSecs);
streamAtomic(buffer, decoder);
streamAtomic(buffer, decoder.get());
alSourceQueueBuffers(source, 1, &buffer);
}
return true;
@@ -510,7 +505,7 @@ bool Source::playAtomic()
for (unsigned int i = 0; i < MAX_BUFFERS; i++)
{
streamAtomic(streamBuffers[i], decoder);
streamAtomic(streamBuffers[i], decoder.get());
++usedBuffers;
if (decoder->isFinished())
break;
@@ -658,6 +653,7 @@ int Source::streamAtomic(ALuint buffer, love::sound::Decoder *d)
{
// Get more sound data.
int decoded = d->decode();
decoded = decoded >= 0 ? decoded : 0;
int fmt = getFormat(d->getChannels(), d->getBitDepth());
+2 -2
View File
@@ -148,7 +148,7 @@ private:
static const unsigned int MAX_BUFFERS = 32;
ALuint streamBuffers[MAX_BUFFERS];
StaticDataBuffer *staticBuffer;
Object::StrongRef<StaticDataBuffer> staticBuffer;
float pitch;
float volume;
@@ -182,7 +182,7 @@ private:
int channels;
love::sound::Decoder *decoder;
Object::StrongRef<love::sound::Decoder> decoder;
unsigned int toLoop;
+41 -33
View File
@@ -31,20 +31,17 @@ namespace love
namespace audio
{
static Audio *instance = 0;
#define instance() (Module::getInstance<Audio>(Module::M_AUDIO))
int w_getSourceCount(lua_State *L)
{
lua_pushinteger(L, instance->getSourceCount());
lua_pushinteger(L, instance()->getSourceCount());
return 1;
}
int w_newSource(lua_State *L)
{
if (lua_isstring(L, 1) || luax_istype(L, 1, FILESYSTEM_FILE_T))
luax_convobj(L, 1, "filesystem", "newFileData");
if (luax_istype(L, 1, FILESYSTEM_FILE_DATA_T))
if (lua_isstring(L, 1) || luax_istype(L, 1, FILESYSTEM_FILE_T) || luax_istype(L, 1, FILESYSTEM_FILE_DATA_T))
luax_convobj(L, 1, "sound", "newDecoder");
Source::Type stype = Source::TYPE_STREAM;
@@ -59,13 +56,14 @@ int w_newSource(lua_State *L)
Source *t = 0;
if (luax_istype(L, 1, SOUND_SOUND_DATA_T))
t = instance->newSource(luax_totype<love::sound::SoundData>(L, 1, "SoundData", SOUND_SOUND_DATA_T));
t = instance()->newSource(luax_totype<love::sound::SoundData>(L, 1, "SoundData", SOUND_SOUND_DATA_T));
else if (luax_istype(L, 1, SOUND_DECODER_T))
t = instance->newSource(luax_totype<love::sound::Decoder>(L, 1, "Decoder", SOUND_DECODER_T));
t = instance()->newSource(luax_totype<love::sound::Decoder>(L, 1, "Decoder", SOUND_DECODER_T));
if (t)
{
luax_pushtype(L, "Source", AUDIO_SOURCE_T, t);
t->release();
return 1;
}
else
@@ -75,7 +73,7 @@ int w_newSource(lua_State *L)
int w_play(lua_State *L)
{
Source *s = luax_checksource(L, 1);
luax_pushboolean(L, instance->play(s));
luax_pushboolean(L, instance()->play(s));
return 1;
}
@@ -83,7 +81,7 @@ int w_stop(lua_State *L)
{
if (lua_gettop(L) == 0)
{
instance->stop();
instance()->stop();
}
else
{
@@ -97,7 +95,7 @@ int w_pause(lua_State *L)
{
if (lua_gettop(L) == 0)
{
instance->pause();
instance()->pause();
}
else
{
@@ -112,7 +110,7 @@ int w_resume(lua_State *L)
{
if (lua_gettop(L) == 0)
{
instance->resume();
instance()->resume();
}
else
{
@@ -126,7 +124,7 @@ int w_rewind(lua_State *L)
{
if (lua_gettop(L) == 0)
{
instance->rewind();
instance()->rewind();
}
else
{
@@ -139,13 +137,13 @@ int w_rewind(lua_State *L)
int w_setVolume(lua_State *L)
{
float v = (float)luaL_checknumber(L, 1);
instance->setVolume(v);
instance()->setVolume(v);
return 0;
}
int w_getVolume(lua_State *L)
{
lua_pushnumber(L, instance->getVolume());
lua_pushnumber(L, instance()->getVolume());
return 1;
}
@@ -155,14 +153,14 @@ int w_setPosition(lua_State *L)
v[0] = (float)luaL_checknumber(L, 1);
v[1] = (float)luaL_checknumber(L, 2);
v[2] = (float)luaL_optnumber(L, 3, 0);
instance->setPosition(v);
instance()->setPosition(v);
return 0;
}
int w_getPosition(lua_State *L)
{
float v[3];
instance->getPosition(v);
instance()->getPosition(v);
lua_pushnumber(L, v[0]);
lua_pushnumber(L, v[1]);
lua_pushnumber(L, v[2]);
@@ -178,14 +176,14 @@ int w_setOrientation(lua_State *L)
v[3] = (float)luaL_checknumber(L, 4);
v[4] = (float)luaL_checknumber(L, 5);
v[5] = (float)luaL_checknumber(L, 6);
instance->setOrientation(v);
instance()->setOrientation(v);
return 0;
}
int w_getOrientation(lua_State *L)
{
float v[6];
instance->getOrientation(v);
instance()->getOrientation(v);
lua_pushnumber(L, v[0]);
lua_pushnumber(L, v[1]);
lua_pushnumber(L, v[2]);
@@ -201,14 +199,14 @@ int w_setVelocity(lua_State *L)
v[0] = (float)luaL_checknumber(L, 1);
v[1] = (float)luaL_checknumber(L, 2);
v[2] = (float)luaL_optnumber(L, 3, 0);
instance->setVelocity(v);
instance()->setVelocity(v);
return 0;
}
int w_getVelocity(lua_State *L)
{
float v[3];
instance->getVelocity(v);
instance()->getVelocity(v);
lua_pushnumber(L, v[0]);
lua_pushnumber(L, v[1]);
lua_pushnumber(L, v[2]);
@@ -217,17 +215,20 @@ int w_getVelocity(lua_State *L)
int w_record(lua_State *)
{
instance->record();
instance()->record();
return 0;
}
int w_getRecordedData(lua_State *L)
{
love::sound::SoundData *sd = instance->getRecordedData();
love::sound::SoundData *sd = instance()->getRecordedData();
if (!sd)
lua_pushnil(L);
else
{
luax_pushtype(L, "SoundData", SOUND_SOUND_DATA_T, sd);
sd->release();
}
return 1;
}
@@ -235,18 +236,23 @@ int w_stopRecording(lua_State *L)
{
if (luax_optboolean(L, 1, true))
{
love::sound::SoundData *sd = instance->stopRecording(true);
if (!sd) lua_pushnil(L);
else luax_pushtype(L, "SoundData", SOUND_SOUND_DATA_T, sd);
love::sound::SoundData *sd = instance()->stopRecording(true);
if (!sd)
lua_pushnil(L);
else
{
luax_pushtype(L, "SoundData", SOUND_SOUND_DATA_T, sd);
sd->release();
}
return 1;
}
instance->stopRecording(false);
instance()->stopRecording(false);
return 0;
}
int w_canRecord(lua_State *L)
{
luax_pushboolean(L, instance->canRecord());
luax_pushboolean(L, instance()->canRecord());
return 1;
}
@@ -256,13 +262,13 @@ int w_setDistanceModel(lua_State *L)
Audio::DistanceModel distanceModel;
if (!Audio::getConstant(modelStr, distanceModel))
return luaL_error(L, "Invalid distance model: %s", modelStr);
instance->setDistanceModel(distanceModel);
instance()->setDistanceModel(distanceModel);
return 0;
}
int w_getDistanceModel(lua_State *L)
{
Audio::DistanceModel distanceModel = instance->getDistanceModel();
Audio::DistanceModel distanceModel = instance()->getDistanceModel();
const char *modelStr;
if (!Audio::getConstant(distanceModel, modelStr))
return 0;
@@ -304,8 +310,10 @@ static const lua_CFunction types[] =
extern "C" int luaopen_love_audio(lua_State *L)
{
Audio *instance = instance();
#ifdef LOVE_ENABLE_AUDIO_OPENAL
if (instance == 0)
if (instance == nullptr)
{
// Try OpenAL first.
try
@@ -322,7 +330,7 @@ extern "C" int luaopen_love_audio(lua_State *L)
#endif
#ifdef LOVE_ENABLE_AUDIO_NULL
if (instance == 0)
if (instance == nullptr)
{
// Fall back to nullaudio.
try
@@ -336,7 +344,7 @@ extern "C" int luaopen_love_audio(lua_State *L)
}
#endif
if (instance == 0)
if (instance == nullptr)
return luaL_error(L, "Could not open any audio module.");
WrappedModule w;
+2 -1
View File
@@ -36,8 +36,9 @@ int w_Source_clone(lua_State *L)
{
Source *t = luax_checksource(L, 1);
Source *clone = nullptr;
EXCEPT_GUARD(clone = t->clone();)
luax_catchexcept(L, [&](){ clone = t->clone(); });
luax_pushtype(L, "Source", AUDIO_SOURCE_T, clone);
clone->release();
return 1;
}
+9 -22
View File
@@ -28,34 +28,24 @@ namespace love
namespace event
{
Message::Message(const std::string &name, const std::vector<Variant *> &vargs)
Message::Message(const std::string &name, const std::vector<Object::StrongRef<Variant>> &vargs)
: name(name)
, args(vargs)
{
for (auto it = args.begin(); it != args.end(); ++it)
{
if ((*it) != nullptr)
(*it)->retain();
}
}
Message::~Message()
{
for (auto it = args.begin(); it != args.end(); ++it)
{
if ((*it) != nullptr)
(*it)->release();
}
}
int Message::toLua(lua_State *L)
{
luax_pushstring(L, name);
for (auto it = args.begin(); it != args.end(); ++it)
for (const Object::StrongRef<Variant> &v : args)
{
if ((*it) != nullptr)
(*it)->toLua(L);
if (v.get() != nullptr)
v->toLua(L);
else
lua_pushnil(L);
}
@@ -66,7 +56,7 @@ int Message::toLua(lua_State *L)
Message *Message::fromLua(lua_State *L, int n)
{
std::string name = luax_checkstring(L, n);
std::vector<Variant *> vargs;
std::vector<Object::StrongRef<Variant>> vargs;
int count = lua_gettop(L) - n;
n++;
@@ -78,17 +68,14 @@ Message *Message::fromLua(lua_State *L, int n)
vargs.push_back(Variant::fromLua(L, n+i));
if (!vargs.back())
if (!vargs.back().get())
{
for (auto it = vargs.begin(); it != vargs.end(); ++it)
{
if ((*it) != nullptr)
(*it)->release();
}
vargs.clear();
luaL_error(L, "Argument %d can't be stored safely\nExpected boolean, number, string or userdata.", n+i);
return nullptr;
}
vargs.back()->release(); // fromLua gave a +1 ref.
}
return new Message(name, vargs);
+5 -2
View File
@@ -43,7 +43,7 @@ class Message : public Object
{
public:
Message(const std::string &name, const std::vector<Variant *> &vargs = std::vector<Variant *>());
Message(const std::string &name, const std::vector<Object::StrongRef<Variant>> &vargs = std::vector<Object::StrongRef<Variant>>());
~Message();
int toLua(lua_State *L);
@@ -52,7 +52,7 @@ public:
private:
std::string name;
std::vector<Variant *> args;
std::vector<Object::StrongRef<Variant>> args;
}; // Message
@@ -63,6 +63,9 @@ public:
Event();
virtual ~Event();
// Implements Module.
virtual ModuleType getModuleType() const { return M_EVENT; }
void push(Message *msg);
bool poll(Message *&msg);
virtual void clear();
+23 -22
View File
@@ -44,10 +44,9 @@ namespace sdl
// we want them in pixel coordinates (may be different with high-DPI enabled.)
static void windowToPixelCoords(int *x, int *y)
{
#ifndef LOVE_ANDROID
double scale = 1.0;
window::Window *window = (window::Window *) Module::findInstance("love.window.");
window::Window *window = Module::getInstance<window::Window>(Module::M_WINDOW);
if (window != nullptr)
scale = window->getPixelScale();
@@ -56,7 +55,6 @@ static void windowToPixelCoords(int *x, int *y)
if (y != nullptr)
*y = int(double(*y) * scale);
#endif
}
// SDL's event watch callbacks trigger when the event is actually posted inside
@@ -64,7 +62,7 @@ static void windowToPixelCoords(int *x, int *y)
// 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.");
graphics::Graphics *gfx = Module::getInstance<graphics::Graphics>(Module::M_GRAPHICS);
switch (event->type)
{
@@ -151,7 +149,7 @@ Message *Event::convert(const SDL_Event &e) const
{
Message *msg = nullptr;
std::vector<Variant *> vargs;
std::vector<StrongRef<Variant>> vargs;
vargs.reserve(4);
love::keyboard::Keyboard *kb = nullptr;
@@ -167,7 +165,7 @@ Message *Event::convert(const SDL_Event &e) const
case SDL_KEYDOWN:
if (e.key.repeat)
{
kb = (love::keyboard::Keyboard *) Module::findInstance("love.keyboard.");
kb = Module::getInstance<love::keyboard::Keyboard>(Module::M_KEYBOARD);
if (kb && !kb->hasKeyRepeat())
break;
}
@@ -301,10 +299,11 @@ Message *Event::convert(const SDL_Event &e) const
break;
}
for (auto it = vargs.begin(); it != vargs.end(); ++it)
for (const StrongRef<Variant> &v : vargs)
{
if ((*it) != nullptr)
(*it)->release();
// We gave +1 refs to the StrongRef list, so we should release them.
if (v.get() != nullptr)
v->release();
}
return msg;
@@ -312,13 +311,13 @@ Message *Event::convert(const SDL_Event &e) const
Message *Event::convertJoystickEvent(const SDL_Event &e) const
{
joystick::JoystickModule *joymodule = (joystick::JoystickModule *) Module::findInstance("love.joystick.");
joystick::JoystickModule *joymodule = Module::getInstance<joystick::JoystickModule>(Module::M_JOYSTICK);
if (!joymodule)
return nullptr;
Message *msg = nullptr;
std::vector<Variant *> vargs;
std::vector<StrongRef<Variant>> vargs;
vargs.reserve(4);
Proxy proxy;
@@ -431,10 +430,11 @@ Message *Event::convertJoystickEvent(const SDL_Event &e) const
break;
}
for (auto it = vargs.begin(); it != vargs.end(); ++it)
for (const StrongRef<Variant> &v : vargs)
{
if ((*it) != nullptr)
(*it)->release();
// We gave +1 refs to the StrongRef list, so we should release them.
if (v.get() != nullptr)
v->release();
}
return msg;
@@ -444,7 +444,7 @@ Message *Event::convertWindowEvent(const SDL_Event &e) const
{
Message *msg = nullptr;
std::vector<Variant *> vargs;
std::vector<StrongRef<Variant>> vargs;
vargs.reserve(4);
window::Window *win = nullptr;
@@ -476,7 +476,7 @@ Message *Event::convertWindowEvent(const SDL_Event &e) const
msg = new Message("visible", vargs);
break;
case SDL_WINDOWEVENT_RESIZED:
win = (window::Window *) Module::findInstance("love.window.");
win = Module::getInstance<window::Window>(Module::M_WINDOW);
if (win)
{
int px_w = e.window.data1;
@@ -491,7 +491,7 @@ Message *Event::convertWindowEvent(const SDL_Event &e) const
win->onWindowResize(e.window.data1, e.window.data2);
graphics::Graphics *gfx = (graphics::Graphics *) Module::findInstance("love.graphics.");
graphics::Graphics *gfx = Module::getInstance<graphics::Graphics>(Module::M_GRAPHICS);
if (gfx)
gfx->setViewportSize(px_w, px_h);
@@ -505,14 +505,14 @@ Message *Event::convertWindowEvent(const SDL_Event &e) const
#ifdef LOVE_ANDROID
case SDL_WINDOWEVENT_MINIMIZED:
{
audio::Audio *audio = (audio::Audio *) Module::findInstance("love.audio.");
audio::Audio *audio = (audio::Audio *) Module::getInstance("love.audio.");
if (audio)
audio->pause();
}
break;
case SDL_WINDOWEVENT_RESTORED:
{
audio::Audio *audio = (audio::Audio *) Module::findInstance("love.audio.");
audio::Audio *audio = (audio::Audio *) Module::getInstance("love.audio.");
if (audio)
audio->resume();
}
@@ -520,10 +520,11 @@ Message *Event::convertWindowEvent(const SDL_Event &e) const
#endif
}
for (auto it = vargs.begin(); it != vargs.end(); ++it)
for (const StrongRef<Variant> &v : vargs)
{
if ((*it) != nullptr)
(*it)->release();
// We gave +1 refs to the StrongRef list, so we should release them.
if (v.get() != nullptr)
v->release();
}
return msg;
+10 -9
View File
@@ -33,13 +33,13 @@ namespace event
namespace sdl
{
static Event *instance = nullptr;
#define instance() (Module::getInstance<Event>(Module::M_EVENT))
static int poll_i(lua_State *L)
{
Message *m = nullptr;
while (instance->poll(m))
while (instance()->poll(m))
{
int args = m->toLua(L);
m->release();
@@ -52,7 +52,7 @@ static int poll_i(lua_State *L)
int w_pump(lua_State *)
{
instance->pump();
instance()->pump();
return 0;
}
@@ -66,7 +66,7 @@ int w_wait(lua_State *L)
{
Message *m = nullptr;
if ((m = instance->wait()))
if ((m = instance()->wait()))
{
int args = m->toLua(L);
m->release();
@@ -86,7 +86,7 @@ int w_push(lua_State *L)
if (!success)
return 1;
instance->push(m);
instance()->push(m);
m->release();
return 1;
@@ -94,14 +94,14 @@ int w_push(lua_State *L)
int w_clear(lua_State *)
{
instance->clear();
instance()->clear();
return 0;
}
int w_quit(lua_State *L)
{
Message *m = new Message("quit");
instance->push(m);
instance()->push(m);
m->release();
luax_pushboolean(L, true);
return 1;
@@ -121,9 +121,10 @@ static const luaL_Reg functions[] =
extern "C" int luaopen_love_event(lua_State *L)
{
if (instance == 0)
Event *instance = instance();
if (instance == nullptr)
{
EXCEPT_GUARD(instance = new Event();)
luax_catchexcept(L, [&](){ instance = new Event(); });
}
else
instance->retain();
@@ -31,11 +31,12 @@ namespace love
{
namespace filesystem
{
namespace physfs
{
extern bool hack_setupWriteDirectory();
namespace physfs
{
File::File(const std::string &filename)
: filename(filename)
, file(0)
@@ -68,23 +69,36 @@ bool File::open(Mode mode)
if (file != 0)
return false;
this->mode = mode;
PHYSFS_getLastError(); // Clear the error buffer.
PHYSFS_File *handle = nullptr;
switch (mode)
{
case READ:
file = PHYSFS_openRead(filename.c_str());
handle = PHYSFS_openRead(filename.c_str());
break;
case APPEND:
file = PHYSFS_openAppend(filename.c_str());
handle = PHYSFS_openAppend(filename.c_str());
break;
case WRITE:
file = PHYSFS_openWrite(filename.c_str());
handle = PHYSFS_openWrite(filename.c_str());
break;
default:
break;
}
if (handle == nullptr)
{
const char *err = PHYSFS_getLastError();
if (err == nullptr)
err = "unknown error";
throw love::Exception("Could not open file %s (%s)", filename.c_str(), err);
}
file = handle;
this->mode = mode;
if (file != 0 && !setBuffer(bufferMode, bufferSize))
{
// Revert to buffer defaults if we don't successfully set the buffer.
@@ -21,6 +21,7 @@
#include "common/config.h"
#include <iostream>
#include <sstream>
#include "common/utf8.h"
#include "common/b64.h"
@@ -165,6 +166,22 @@ namespace
return true;
}
#endif
std::string normalize(const std::string &input)
{
std::stringstream out;
bool seenSep = false, isSep = false;
for (size_t i = 0; i < input.size(); ++i)
{
isSep = (input[i] == LOVE_PATH_SEPARATOR[0]);
if (!isSep || !seenSep)
out << input[i];
seenSep = isSep;
}
return out.str();
}
}
namespace love
@@ -239,6 +256,8 @@ bool Filesystem::setIdentity(const char *ident, bool appendToPath)
else
save_path_full += save_path_relative;
save_path_full = normalize(save_path_full);
#ifdef LOVE_ANDROID
if (save_identity == "")
save_identity = "unnamed";
@@ -532,40 +551,33 @@ const char *Filesystem::getWorkingDirectory()
std::string Filesystem::getUserDirectory()
{
return std::string(PHYSFS_getUserDir());
static std::string userDir = normalize(PHYSFS_getUserDir());
return userDir;
}
std::string Filesystem::getAppdataDirectory()
{
#ifdef LOVE_WINDOWS
if (appdata.empty())
{
#ifdef LOVE_WINDOWS
wchar_t *w_appdata = _wgetenv(L"APPDATA");
appdata = to_utf8(w_appdata);
replace_char(appdata, '\\', '/');
}
return appdata;
#elif defined(LOVE_MACOSX)
if (appdata.empty())
{
std::string udir = getUserDirectory();
udir.append("/Library/Application Support");
appdata = udir;
}
return appdata;
appdata = normalize(udir);
#elif defined(LOVE_LINUX)
if (appdata.empty())
{
char *xdgdatahome = getenv("XDG_DATA_HOME");
if (!xdgdatahome)
appdata = std::string(getUserDirectory()) + "/.local/share/";
appdata = normalize(std::string(getUserDirectory()) + "/.local/share/");
else
appdata = xdgdatahome;
#else
appdata = getUserDirectory();
#endif
}
return appdata;
#else
return getUserDirectory();
#endif
}
@@ -828,6 +840,21 @@ int64 Filesystem::getSize(const char *filename) const
return size;
}
void Filesystem::setSymlinksEnabled(bool enable)
{
PHYSFS_permitSymbolicLinks(enable ? 1 : 0);
}
bool Filesystem::areSymlinksEnabled() const
{
return PHYSFS_symbolicLinksPermitted() != 0;
}
bool Filesystem::isSymlink(const char *filename) const
{
return PHYSFS_isSymbolicLink(filename) != 0;
}
} // physfs
} // filesystem
} // love
@@ -80,6 +80,8 @@ public:
Filesystem();
virtual ~Filesystem();
// Implements Module.
virtual ModuleType getModuleType() const { return M_FILESYSTEM; }
const char *getName() const;
void init(const char *arg0);
@@ -245,6 +247,22 @@ public:
**/
int64 getSize(const char *filename) const;
/**
* Enable or disable symbolic link support in love.filesystem.
**/
void setSymlinksEnabled(bool enable);
/**
* Gets whether symbolic link support is enabled.
**/
bool areSymlinksEnabled() const;
/**
* Gets whether a filepath is actually a symlink.
* Always returns false if symlinks are not enabled.
**/
bool isSymlink(const char *filename) const;
/**
* Text file line-reading iterator function used and
* pushed on the Lua stack by love.filesystem.lines
@@ -0,0 +1,327 @@
/**
* 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.
**/
#include "wrap_File.h"
#include "physfs/Filesystem.h"
#include "common/Data.h"
#include "common/Exception.h"
#include "common/int.h"
namespace love
{
namespace filesystem
{
int luax_ioError(lua_State *L, const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
lua_pushnil(L);
lua_pushvfstring(L, fmt, args);
va_end(args);
return 2;
}
File *luax_checkfile(lua_State *L, int idx)
{
return luax_checktype<File>(L, idx, "File", FILESYSTEM_FILE_T);
}
int w_File_getSize(lua_State *L)
{
File *t = luax_checkfile(L, 1);
int64 size = -1;
try
{
size = t->getSize();
}
catch (love::Exception &e)
{
return luax_ioError(L, "%s", e.what());
}
// Push nil on failure or if size does not fit into a double precision floating-point number.
if (size == -1)
return luax_ioError(L, "Could not determine file size.");
else if (size >= 0x20000000000000LL)
return luax_ioError(L, "Size is too large.");
lua_pushnumber(L, (lua_Number) size);
return 1;
}
int w_File_open(lua_State *L)
{
File *file = luax_checkfile(L, 1);
const char *str = luaL_checkstring(L, 2);
File::Mode mode;
if (!File::getConstant(str, mode))
return luaL_error(L, "Incorrect file open mode: %s", str);
try
{
luax_pushboolean(L, file->open(mode));
}
catch (love::Exception &e)
{
return luax_ioError(L, "%s", e.what());
}
return 1;
}
int w_File_close(lua_State *L)
{
File *file = luax_checkfile(L, 1);
luax_pushboolean(L, file->close());
return 1;
}
int w_File_isOpen(lua_State *L)
{
File *file = luax_checkfile(L, 1);
luax_pushboolean(L, file->isOpen());
return 1;
}
int w_File_read(lua_State *L)
{
File *file = luax_checkfile(L, 1);
Data *d = 0;
int64 size = (int64)luaL_optnumber(L, 2, File::ALL);
try
{
d = file->read(size);
}
catch (love::Exception &e)
{
return luax_ioError(L, "%s", e.what());
}
lua_pushlstring(L, (const char *) d->getData(), d->getSize());
lua_pushnumber(L, d->getSize());
d->release();
return 2;
}
int w_File_write(lua_State *L)
{
File *file = luax_checkfile(L, 1);
bool result = false;
if (lua_isstring(L, 2))
{
try
{
size_t datasize = 0;
const char *data = lua_tolstring(L, 2, &datasize);
if (!lua_isnoneornil(L, 3))
datasize = luaL_checkinteger(L, 3);
result = file->write(data, datasize);
}
catch (love::Exception &e)
{
return luax_ioError(L, "%s", e.what());
}
}
else if (luax_istype(L, 2, DATA_T))
{
try
{
love::Data *data = luax_totype<love::Data>(L, 2, "Data", DATA_T);
result = file->write(data, luaL_optinteger(L, 3, data->getSize()));
}
catch (love::Exception &e)
{
return luax_ioError(L, "%s", e.what());
}
}
else
{
return luaL_argerror(L, 2, "string or data expected");
}
luax_pushboolean(L, result);
return 1;
}
int w_File_flush(lua_State *L)
{
File *file = luax_checkfile(L, 1);
bool success = false;
try
{
success = file->flush();
}
catch (love::Exception &e)
{
return luax_ioError(L, "%s", e.what());
}
luax_pushboolean(L, success);
return 1;
}
int w_File_eof(lua_State *L)
{
File *file = luax_checkfile(L, 1);
luax_pushboolean(L, file->eof());
return 1;
}
int w_File_tell(lua_State *L)
{
File *file = luax_checkfile(L, 1);
int64 pos = file->tell();
// Push nil on failure or if pos does not fit into a double precision floating-point number.
if (pos == -1)
return luax_ioError(L, "Invalid position.");
else if (pos >= 0x20000000000000LL)
return luax_ioError(L, "Number is too large.");
else
lua_pushnumber(L, (lua_Number)pos);
return 1;
}
int w_File_seek(lua_State *L)
{
File *file = luax_checkfile(L, 1);
lua_Number pos = luaL_checknumber(L, 2);
// Push false on negative and precision-problematic numbers.
// Better fail than seek to an unknown position.
if (pos < 0.0 || pos >= 9007199254740992.0)
luax_pushboolean(L, false);
else
luax_pushboolean(L, file->seek((uint64)pos));
return 1;
}
int w_File_lines(lua_State *L)
{
File *file = luax_checkfile(L, 1);
lua_pushnumber(L, 0); // File position.
luax_pushboolean(L, file->getMode() != File::CLOSED); // Save current file mode.
if (file->getMode() != File::READ)
{
if (file->getMode() != File::CLOSED)
file->close();
bool success = false;
luax_catchexcept(L, [&](){ success = file->open(File::READ); });
if (!success)
return luaL_error(L, "Could not open file.");
}
lua_pushcclosure(L, physfs::Filesystem::lines_i, 3);
return 1;
}
int w_File_setBuffer(lua_State *L)
{
File *file = luax_checkfile(L, 1);
const char *str = luaL_checkstring(L, 2);
int64 size = (int64) luaL_optnumber(L, 3, 0.0);
File::BufferMode bufmode;
if (!File::getConstant(str, bufmode))
return luaL_error(L, "Incorrect file buffer mode: %s", str);
bool success = false;
try
{
success = file->setBuffer(bufmode, size);
}
catch (love::Exception &e)
{
return luax_ioError(L, "%s", e.what());
}
luax_pushboolean(L, success);
return 1;
}
int w_File_getBuffer(lua_State *L)
{
File *file = luax_checkfile(L, 1);
int64 size = 0;
File::BufferMode bufmode = file->getBuffer(size);
const char *str = 0;
if (!File::getConstant(bufmode, str))
return luax_ioError(L, "Unknown file buffer mode.");
lua_pushstring(L, str);
lua_pushnumber(L, (lua_Number) size);
return 2;
}
int w_File_getMode(lua_State *L)
{
File *file = luax_checkfile(L, 1);
File::Mode mode = file->getMode();
const char *str = 0;
if (!File::getConstant(mode, str))
return luax_ioError(L, "Unknown file mode.");
lua_pushstring(L, str);
return 1;
}
static const luaL_Reg functions[] =
{
{ "getSize", w_File_getSize },
{ "open", w_File_open },
{ "close", w_File_close },
{ "isOpen", w_File_isOpen },
{ "read", w_File_read },
{ "write", w_File_write },
{ "flush", w_File_flush },
{ "eof", w_File_eof },
{ "tell", w_File_tell },
{ "seek", w_File_seek },
{ "lines", w_File_lines },
{ "setBuffer", w_File_setBuffer },
{ "getBuffer", w_File_getBuffer },
{ "getMode", w_File_getMode },
{ 0, 0 }
};
extern "C" int luaopen_file(lua_State *L)
{
return luax_register_type(L, "File", functions);
}
} // filesystem
} // love
@@ -0,0 +1,56 @@
/**
* 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_FILESYSTEM_WRAP_FILE_H
#define LOVE_FILESYSTEM_WRAP_FILE_H
// LOVE
#include "common/runtime.h"
#include "File.h"
namespace love
{
namespace filesystem
{
// Does not use lua_error, so it's safe to call in exception handling code.
int luax_ioError(lua_State *L, const char *fmt, ...);
File *luax_checkfile(lua_State *L, int idx);
int w_File_getSize(lua_State *L);
int w_File_open(lua_State *L);
int w_File_close(lua_State *L);
int w_File_isOpen(lua_State *L);
int w_File_read(lua_State *L);
int w_File_write(lua_State *L);
int w_File_flush(lua_State *L);
int w_File_eof(lua_State *L);
int w_File_tell(lua_State *L);
int w_File_seek(lua_State *L);
int w_File_lines(lua_State *L);
int w_File_setBuffer(lua_State *L);
int w_File_getBuffer(lua_State *L);
int w_File_getMode(lua_State *L);
extern "C" int luaopen_file(lua_State *L);
} // filesystem
} // love
#endif // LOVE_FILESYSTEM_WRAP_FILE_H
@@ -0,0 +1,68 @@
/**
* 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.
**/
#include "wrap_FileData.h"
#include "common/wrap_Data.h"
namespace love
{
namespace filesystem
{
FileData *luax_checkfiledata(lua_State *L, int idx)
{
return luax_checktype<FileData>(L, idx, "FileData", FILESYSTEM_FILE_DATA_T);
}
int w_FileData_getFilename(lua_State *L)
{
FileData *t = luax_checkfiledata(L, 1);
lua_pushstring(L, t->getFilename().c_str());
return 1;
}
int w_FileData_getExtension(lua_State *L)
{
FileData *t = luax_checkfiledata(L, 1);
lua_pushstring(L, t->getExtension().c_str());
return 1;
}
static const luaL_Reg w_FileData_functions[] =
{
// Data
{ "getString", w_Data_getString },
{ "getPointer", w_Data_getPointer },
{ "getSize", w_Data_getSize },
{ "getFilename", w_FileData_getFilename },
{ "getExtension", w_FileData_getExtension },
{ 0, 0 }
};
extern "C" int luaopen_filedata(lua_State *L)
{
return luax_register_type(L, "FileData", w_FileData_functions);
}
} // filesystem
} // love
@@ -0,0 +1,41 @@
/**
* 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_FILESYSTEM_WRAP_FILE_DATA_H
#define LOVE_FILESYSTEM_WRAP_FILE_DATA_H
// LOVE
#include "common/runtime.h"
#include "FileData.h"
namespace love
{
namespace filesystem
{
FileData *luax_checkfiledata(lua_State *L, int idx);
int w_FileData_getFilename(lua_State *L);
int w_FileData_getExtension(lua_State *L);
extern "C" int luaopen_filedata(lua_State *L);
} // filesystem
} // love
#endif // LOVE_FILESYSTEM_WRAP_FILE_DATA_H
@@ -0,0 +1,713 @@
/**
* 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 "wrap_Filesystem.h"
#include "wrap_File.h"
#include "wrap_FileData.h"
#include "physfs/Filesystem.h"
// SDL
#include <SDL_loadso.h>
namespace love
{
namespace filesystem
{
#define instance() (Module::getInstance<physfs::Filesystem>(Module::M_FILESYSTEM))
bool hack_setupWriteDirectory()
{
if (instance() != 0)
return instance()->setupWriteDirectory();
return false;
}
int w_init(lua_State *L)
{
const char *arg0 = luaL_checkstring(L, 1);
luax_catchexcept(L, [&](){ instance()->init(arg0); });
return 0;
}
int w_setFused(lua_State *L)
{
// no error checking needed, everything, even nothing
// can be converted to a boolean
instance()->setFused(luax_toboolean(L, 1));
return 0;
}
int w_isFused(lua_State *L)
{
luax_pushboolean(L, instance()->isFused());
return 1;
}
int w_setIdentity(lua_State *L)
{
const char *arg = luaL_checkstring(L, 1);
bool append = luax_optboolean(L, 2, false);
if (!instance()->setIdentity(arg, append))
return luaL_error(L, "Could not set write directory.");
return 0;
}
int w_getIdentity(lua_State *L)
{
lua_pushstring(L, instance()->getIdentity());
return 1;
}
int w_setSource(lua_State *L)
{
const char *arg = luaL_checkstring(L, 1);
if (!instance()->setSource(arg))
return luaL_error(L, "Could not set source.");
return 0;
}
int w_getSource(lua_State *L)
{
lua_pushstring(L, instance()->getSource());
return 1;
}
int w_mount(lua_State *L)
{
const char *archive = luaL_checkstring(L, 1);
const char *mountpoint = luaL_checkstring(L, 2);
bool append = luax_optboolean(L, 3, false);
luax_pushboolean(L, instance()->mount(archive, mountpoint, append));
return 1;
}
int w_unmount(lua_State *L)
{
const char *archive = luaL_checkstring(L, 1);
luax_pushboolean(L, instance()->unmount(archive));
return 1;
}
int w_newFile(lua_State *L)
{
const char *filename = luaL_checkstring(L, 1);
const char *str = 0;
File::Mode mode = File::CLOSED;
if (lua_isstring(L, 2))
{
str = luaL_checkstring(L, 2);
if (!File::getConstant(str, mode))
return luaL_error(L, "Incorrect file open mode: %s", str);
}
File *t = instance()->newFile(filename);
if (mode != File::CLOSED)
{
try
{
if (!t->open(mode))
throw love::Exception("Could not open file.");
}
catch (love::Exception &e)
{
t->release();
return luax_ioError(L, "%s", e.what());
}
}
luax_pushtype(L, "File", FILESYSTEM_FILE_T, t);
t->release();
return 1;
}
FileData *luax_getfiledata(lua_State *L, int idx)
{
FileData *data = nullptr;
File *file = nullptr;
if (lua_isstring(L, idx))
{
const char *filename = luaL_checkstring(L, idx);
file = instance()->newFile(filename);
}
else if (luax_istype(L, idx, FILESYSTEM_FILE_T))
{
file = luax_checkfile(L, idx);
file->retain();
}
else if (luax_istype(L, idx, FILESYSTEM_FILE_DATA_T))
{
data = luax_checkfiledata(L, idx);
data->retain();
}
if (!data && !file)
{
luaL_argerror(L, idx, "filename, File, or FileData expected");
return nullptr; // Never reached.
}
if (file)
{
luax_catchexcept(L,
[&]() { data = file->read(); },
[&]() { file->release(); }
);
}
return data;
}
int w_newFileData(lua_State *L)
{
// Single argument: treat as filepath or File.
if (lua_gettop(L) == 1)
{
// We don't use luax_getfiledata because we want to use an ioError.
if (lua_isstring(L, 1))
luax_convobj(L, 1, "filesystem", "newFile");
// Get FileData from the File.
if (luax_istype(L, 1, FILESYSTEM_FILE_T))
{
File *file = luax_checkfile(L, 1);
FileData *data = 0;
try
{
data = file->read();
}
catch (love::Exception &e)
{
return luax_ioError(L, "%s", e.what());
}
luax_pushtype(L, "FileData", FILESYSTEM_FILE_DATA_T, data);
data->release();
return 1;
}
else
return luaL_argerror(L, 1, "filename or File expected");
}
size_t length = 0;
const char *str = luaL_checklstring(L, 1, &length);
const char *filename = luaL_checkstring(L, 2);
const char *decstr = lua_isstring(L, 3) ? lua_tostring(L, 3) : 0;
FileData::Decoder decoder = FileData::FILE;
if (decstr && !FileData::getConstant(decstr, decoder))
return luaL_error(L, "Invalid FileData decoder: %s", decstr);
FileData *t = 0;
switch (decoder)
{
case FileData::FILE:
t = instance()->newFileData((void *)str, (int)length, filename);
break;
case FileData::BASE64:
t = instance()->newFileData(str, filename);
break;
default:
return luaL_error(L, "Invalid FileData decoder: %s", decstr);
}
luax_pushtype(L, "FileData", FILESYSTEM_FILE_DATA_T, t);
t->release();
return 1;
}
int w_getWorkingDirectory(lua_State *L)
{
lua_pushstring(L, instance()->getWorkingDirectory());
return 1;
}
int w_getUserDirectory(lua_State *L)
{
luax_pushstring(L, instance()->getUserDirectory());
return 1;
}
int w_getAppdataDirectory(lua_State *L)
{
luax_pushstring(L, instance()->getAppdataDirectory());
return 1;
}
int w_getSaveDirectory(lua_State *L)
{
lua_pushstring(L, instance()->getSaveDirectory());
return 1;
}
int w_getSourceBaseDirectory(lua_State *L)
{
luax_pushstring(L, instance()->getSourceBaseDirectory());
return 1;
}
int w_exists(lua_State *L)
{
const char *arg = luaL_checkstring(L, 1);
luax_pushboolean(L, instance()->exists(arg));
return 1;
}
int w_isDirectory(lua_State *L)
{
const char *arg = luaL_checkstring(L, 1);
luax_pushboolean(L, instance()->isDirectory(arg));
return 1;
}
int w_isFile(lua_State *L)
{
const char *arg = luaL_checkstring(L, 1);
luax_pushboolean(L, instance()->isFile(arg));
return 1;
}
int w_createDirectory(lua_State *L)
{
const char *arg = luaL_checkstring(L, 1);
luax_pushboolean(L, instance()->createDirectory(arg));
return 1;
}
int w_remove(lua_State *L)
{
const char *arg = luaL_checkstring(L, 1);
luax_pushboolean(L, instance()->remove(arg));
return 1;
}
int w_read(lua_State *L)
{
const char *filename = luaL_checkstring(L, 1);
int64 len = (int64) luaL_optinteger(L, 2, File::ALL);
Data *data = 0;
try
{
data = instance()->read(filename, len);
}
catch (love::Exception &e)
{
return luax_ioError(L, "%s", e.what());
}
if (data == 0)
return luax_ioError(L, "File could not be read.");
// Push the string.
lua_pushlstring(L, (const char *) data->getData(), data->getSize());
// Push the size.
lua_pushinteger(L, data->getSize());
// Lua has a copy now, so we can free it.
data->release();
return 2;
}
static int w_write_or_append(lua_State *L, File::Mode mode)
{
const char *filename = luaL_checkstring(L, 1);
const char *input = 0;
size_t len = 0;
if (luax_istype(L, 2, DATA_T))
{
love::Data *data = luax_totype<love::Data>(L, 2, "Data", DATA_T);
input = (const char *) data->getData();
len = data->getSize();
}
else if (lua_isstring(L, 2))
input = lua_tolstring(L, 2, &len);
else
return luaL_argerror(L, 2, "string or Data expected");
// Get how much we should write. Length of string default.
len = luaL_optinteger(L, 3, len);
try
{
if (mode == File::APPEND)
instance()->append(filename, (const void *) input, len);
else
instance()->write(filename, (const void *) input, len);
}
catch (love::Exception &e)
{
return luax_ioError(L, "%s", e.what());
}
luax_pushboolean(L, true);
return 1;
}
int w_write(lua_State *L)
{
return w_write_or_append(L, File::WRITE);
}
int w_append(lua_State *L)
{
return w_write_or_append(L, File::APPEND);
}
int w_getDirectoryItems(lua_State *L)
{
return instance()->getDirectoryItems(L);
}
int w_lines(lua_State *L)
{
File *file;
if (lua_isstring(L, 1))
{
file = instance()->newFile(lua_tostring(L, 1));
bool success = false;
luax_catchexcept(L, [&](){ success = file->open(File::READ); });
if (!success)
{
file->release();
return luaL_error(L, "Could not open file.");
}
luax_pushtype(L, "File", FILESYSTEM_FILE_T, file);
file->release();
}
else
return luaL_argerror(L, 1, "expected filename.");
lua_pushcclosure(L, physfs::Filesystem::lines_i, 1);
return 1;
}
int w_load(lua_State *L)
{
std::string filename = std::string(luaL_checkstring(L, 1));
Data *data = 0;
try
{
data = instance()->read(filename.c_str());
}
catch (love::Exception &e)
{
return luax_ioError(L, "%s", e.what());
}
int status = luaL_loadbuffer(L, (const char *)data->getData(), data->getSize(), ("@" + filename).c_str());
data->release();
// Load the chunk, but don't run it.
switch (status)
{
case LUA_ERRMEM:
return luaL_error(L, "Memory allocation error: %s\n", lua_tostring(L, -1));
case LUA_ERRSYNTAX:
return luaL_error(L, "Syntax error: %s\n", lua_tostring(L, -1));
default: // success
return 1;
}
}
int w_getLastModified(lua_State *L)
{
const char *filename = luaL_checkstring(L, 1);
int64 time = 0;
try
{
time = instance()->getLastModified(filename);
}
catch (love::Exception &e)
{
return luax_ioError(L, "%s", e.what());
}
lua_pushnumber(L, static_cast<lua_Number>(time));
return 1;
}
int w_getSize(lua_State *L)
{
const char *filename = luaL_checkstring(L, 1);
int64 size = -1;
try
{
size = instance()->getSize(filename);
}
catch (love::Exception &e)
{
return luax_ioError(L, "%s", e.what());
}
// Error on failure or if size does not fit into a double precision floating-point number.
if (size == -1)
return luax_ioError(L, "Could not determine file size.");
else if (size >= 0x20000000000000LL)
return luax_ioError(L, "Size too large to fit into a Lua number!");
lua_pushnumber(L, (lua_Number) size);
return 1;
}
int w_setSymlinksEnabled(lua_State *L)
{
instance()->setSymlinksEnabled(luax_toboolean(L, 1));
return 0;
}
int w_areSymlinksEnabled(lua_State *L)
{
luax_pushboolean(L, instance()->areSymlinksEnabled());
return 1;
}
int w_isSymlink(lua_State *L)
{
const char *filename = luaL_checkstring(L, 1);
luax_pushboolean(L, instance()->isSymlink(filename));
return 1;
}
int loader(lua_State *L)
{
const char *filename = lua_tostring(L, -1);
std::string tmp(filename);
tmp += ".lua";
int size = tmp.size();
for (int i=0; i<size-4; i++)
{
if (tmp[i] == '.')
{
tmp[i] = '/';
}
}
// Check whether file exists.
if (instance()->exists(tmp.c_str()))
{
lua_pop(L, 1);
lua_pushstring(L, tmp.c_str());
// Ok, load it.
return w_load(L);
}
tmp = filename;
size = tmp.size();
for (int i=0; i<size; i++)
{
if (tmp[i] == '.')
{
tmp[i] = '/';
}
}
if (instance()->isDirectory(tmp.c_str()))
{
tmp += "/init.lua";
if (instance()->exists(tmp.c_str()))
{
lua_pop(L, 1);
lua_pushstring(L, tmp.c_str());
// Ok, load it.
return w_load(L);
}
}
std::string errstr = "\n\tno file '%s' in LOVE game directories.";
errstr += errstr;
lua_pushfstring(L, errstr.c_str(), (tmp + ".lua").c_str(), (tmp + "/init.lua").c_str());
return 1;
}
inline const char *library_extension()
{
#ifdef LOVE_WINDOWS
return ".dll";
#else
return ".so";
#endif
}
int extloader(lua_State *L)
{
const char *filename = lua_tostring(L, -1);
std::string tokenized_name(filename);
std::string tokenized_function(filename);
for (unsigned int i = 0; i < tokenized_name.size(); i++)
{
if (tokenized_name[i] == '.')
{
tokenized_name[i] = '/';
tokenized_function[i] = '_';
}
}
tokenized_name += library_extension();
void *handle = nullptr;
// If the game is fused, try looking for the DLL in the game's read paths.
if (instance()->isFused())
{
try
{
std::string dir = instance()->getRealDirectory(tokenized_name.c_str());
// We don't want to look in the game's source, because it can be a
// zip sometimes and a folder other times.
if (dir.find(instance()->getSource()) == std::string::npos)
handle = SDL_LoadObject((dir + LOVE_PATH_SEPARATOR + tokenized_name).c_str());
}
catch (love::Exception &)
{
// Nothing...
}
}
if (!handle)
{
std::string path = std::string(instance()->getAppdataDirectory()) + LOVE_PATH_SEPARATOR LOVE_APPDATA_FOLDER LOVE_PATH_SEPARATOR + tokenized_name;
handle = SDL_LoadObject(path.c_str());
}
if (!handle)
{
lua_pushfstring(L, "\n\tno file '%s' in LOVE paths.", tokenized_name.c_str());
return 1;
}
void *func = SDL_LoadFunction(handle, ("loveopen_" + tokenized_function).c_str());
if (!func)
func = SDL_LoadFunction(handle, ("luaopen_" + tokenized_function).c_str());
if (!func)
{
SDL_UnloadObject(handle);
lua_pushfstring(L, "\n\tC library '%s' is incompatible.", tokenized_name.c_str());
return 1;
}
lua_pushcfunction(L, (lua_CFunction) func);
return 1;
}
// List of functions to wrap.
static const luaL_Reg functions[] =
{
{ "init", w_init },
{ "setFused", w_setFused },
{ "isFused", w_isFused },
{ "setIdentity", w_setIdentity },
{ "getIdentity", w_getIdentity },
{ "setSource", w_setSource },
{ "getSource", w_getSource },
{ "mount", w_mount },
{ "unmount", w_unmount },
{ "newFile", w_newFile },
{ "getWorkingDirectory", w_getWorkingDirectory },
{ "getUserDirectory", w_getUserDirectory },
{ "getAppdataDirectory", w_getAppdataDirectory },
{ "getSaveDirectory", w_getSaveDirectory },
{ "getSourceBaseDirectory", w_getSourceBaseDirectory },
{ "exists", w_exists },
{ "isDirectory", w_isDirectory },
{ "isFile", w_isFile },
{ "createDirectory", w_createDirectory },
{ "remove", w_remove },
{ "read", w_read },
{ "write", w_write },
{ "append", w_append },
{ "getDirectoryItems", w_getDirectoryItems },
{ "lines", w_lines },
{ "load", w_load },
{ "getLastModified", w_getLastModified },
{ "getSize", w_getSize },
{ "setSymlinksEnabled", w_setSymlinksEnabled },
{ "areSymlinksEnabled", w_areSymlinksEnabled },
{ "isSymlink", w_isSymlink },
{ "newFileData", w_newFileData },
{ 0, 0 }
};
static const lua_CFunction types[] =
{
luaopen_file,
luaopen_filedata,
0
};
extern "C" int luaopen_love_filesystem(lua_State *L)
{
physfs::Filesystem *instance = instance();
if (instance == nullptr)
{
luax_catchexcept(L, [&](){ instance = new physfs::Filesystem(); });
}
else
instance->retain();
// The love loaders should be tried after package.preload.
love::luax_register_searcher(L, loader, 2);
love::luax_register_searcher(L, extloader, 3);
WrappedModule w;
w.module = instance;
w.name = "filesystem";
w.flags = MODULE_FILESYSTEM_T;
w.functions = functions;
w.types = types;
return luax_register_module(L, w);
}
} // filesystem
} // love
@@ -0,0 +1,84 @@
/**
* 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_FILESYSTEM_WRAP_FILESYSTEM_H
#define LOVE_FILESYSTEM_WRAP_FILESYSTEM_H
// LOVE
#include "common/runtime.h"
#include "FileData.h"
namespace love
{
namespace filesystem
{
/**
* Gets FileData at the specified index. If the index contains a filepath or
* a File object, the FileData will be created from that.
* Note that this function retains the FileData object (possibly by creating it),
* so a matching release() is required!
* May trigger a Lua error.
**/
FileData *luax_getfiledata(lua_State *L, int idx);
bool hack_setupWriteDirectory();
int w_init(lua_State *L);
int w_setFused(lua_State *L);
int w_isFused(lua_State *L);
int w_setIdentity(lua_State *L);
int w_getIdentity(lua_State *L);
int w_setSource(lua_State *L);
int w_getSource(lua_State *L);
int w_mount(lua_State *L);
int w_unmount(lua_State *L);
int w_newFile(lua_State *L);
int w_newFileData(lua_State *L);
int w_getWorkingDirectory(lua_State *L);
int w_getUserDirectory(lua_State *L);
int w_getAppdataDirectory(lua_State *L);
int w_getSaveDirectory(lua_State *L);
int w_getSourceBaseDirectory(lua_State *L);
int w_exists(lua_State *L);
int w_isDirectory(lua_State *L);
int w_isFile(lua_State *L);
int w_createDirectory(lua_State *L);
int w_remove(lua_State *L);
int w_open(lua_State *L);
int w_close(lua_State *L);
int w_read(lua_State *L);
int w_write(lua_State *L);
int w_append(lua_State *L);
int w_getDirectoryItems(lua_State *L);
int w_lines(lua_State *L);
int w_load(lua_State *L);
int w_getLastModified(lua_State *L);
int w_getSize(lua_State *L);
int w_setSymlinksEnabled(lua_State *L);
int w_areSymlinksEnabled(lua_State *L);
int w_isSymlink(lua_State *L);
int loader(lua_State *L);
int extloader(lua_State *L);
extern "C" LOVE_EXPORT int luaopen_love_filesystem(lua_State *L);
} // filesystem
} // love
#endif // LOVE_FILESYSTEM_WRAP_FILESYSTEM_H
+5
View File
@@ -40,6 +40,11 @@ class Font : public Module
public:
virtual ~Font() {}
// Implements Module.
virtual ModuleType getModuleType() const { return M_FONT; }
virtual Rasterizer *newRasterizer(Data *data, int size) = 0;
virtual Rasterizer *newRasterizer(love::image::ImageData *data, const std::string &glyphs) = 0;
virtual Rasterizer *newRasterizer(love::image::ImageData *data, uint32 *glyphs, int length) = 0;
@@ -39,13 +39,11 @@ ImageRasterizer::ImageRasterizer(love::image::ImageData *data, uint32 *glyphs, i
, glyphs(glyphs)
, numglyphs(numglyphs)
{
imageData->retain();
load();
}
ImageRasterizer::~ImageRasterizer()
{
imageData->release();
}
int ImageRasterizer::getLineHeight() const
+1 -1
View File
@@ -53,7 +53,7 @@ private:
void load();
// The image data
love::image::ImageData *imageData;
Object::StrongRef<love::image::ImageData> imageData;
// The glyphs in the font
uint32 *glyphs;
@@ -51,14 +51,11 @@ TrueTypeRasterizer::TrueTypeRasterizer(FT_Library library, Data *data, int size)
metrics.ascent = s.ascender >> 6;
metrics.descent = s.descender >> 6;
metrics.height = s.height >> 6;
data->retain();
}
TrueTypeRasterizer::~TrueTypeRasterizer()
{
FT_Done_Face(face);
data->release();
}
int TrueTypeRasterizer::getLineHeight() const
@@ -58,7 +58,7 @@ private:
FT_Face face;
// File data
Data *data;
Object::StrongRef<Data> data;
}; // FreetypeRasterizer
} // freetype
@@ -25,6 +25,8 @@
#include "font/wrap_GlyphData.h"
#include "font/wrap_Rasterizer.h"
#include "filesystem/wrap_Filesystem.h"
#include "TrueTypeRasterizer.h"
namespace love
@@ -34,55 +36,55 @@ namespace font
namespace freetype
{
static Font *instance = 0;
#define instance() (Module::getInstance<Font>(Module::M_FONT))
int w_newRasterizer(lua_State *L)
{
// Convert to FileData, if necessary.
if (lua_isstring(L, 1) || luax_istype(L, 1, FILESYSTEM_FILE_T))
luax_convobj(L, 1, "filesystem", "newFileData");
Rasterizer *t = nullptr;
Rasterizer *t = 0;
EXCEPT_GUARD(
if (luax_istype(L, 1, IMAGE_IMAGE_DATA_T))
{
love::image::ImageData *d = luax_checktype<love::image::ImageData>(L, 1, "ImageData", IMAGE_IMAGE_DATA_T);
const char *g = luaL_checkstring(L, 2);
std::string glyphs(g);
t = instance->newRasterizer(d, glyphs);
}
else if (luax_istype(L, 1, DATA_T))
{
Data *d = luax_checkdata(L, 1);
int size = luaL_checkint(L, 2);
t = instance->newRasterizer(d, size);
}
)
if (luax_istype(L, 1, IMAGE_IMAGE_DATA_T))
{
love::image::ImageData *d = luax_checktype<love::image::ImageData>(L, 1, "ImageData", IMAGE_IMAGE_DATA_T);
const char *g = luaL_checkstring(L, 2);
std::string glyphs(g);
luax_catchexcept(L, [&](){ t = instance()->newRasterizer(d, glyphs); });
}
else if (lua_isstring(L, 1) || luax_istype(L, 1, FILESYSTEM_FILE_T) || luax_istype(L, 1, FILESYSTEM_FILE_DATA_T))
{
love::filesystem::FileData *d = love::filesystem::luax_getfiledata(L, 1);
int size = luaL_checkint(L, 2);
luax_catchexcept(L,
[&]() { t = instance()->newRasterizer(d, size); },
[&]() { d->release(); }
);
}
else
return luaL_argerror(L, 1, "expected ImageData, filename, or FileData");
luax_pushtype(L, "Rasterizer", FONT_RASTERIZER_T, t);
t->release();
return 1;
}
int w_newGlyphData(lua_State *L)
{
Rasterizer *r = luax_checkrasterizer(L, 1);
GlyphData *t = 0;
GlyphData *t = nullptr;
// newGlyphData accepts a unicode character or a codepoint number.
if (lua_type(L, 2) == LUA_TSTRING)
{
std::string glyph = luax_checkstring(L, 2);
EXCEPT_GUARD(t = instance->newGlyphData(r, glyph);)
luax_catchexcept(L, [&](){ t = instance()->newGlyphData(r, glyph); });
}
else
{
uint32 g = (uint32) luaL_checknumber(L, 2);
t = instance->newGlyphData(r, g);
t = instance()->newGlyphData(r, g);
}
luax_pushtype(L, "GlyphData", FONT_GLYPH_DATA_T, t);
t->release();
return 1;
}
@@ -103,9 +105,10 @@ static const lua_CFunction types[] =
extern "C" int luaopen_love_font(lua_State *L)
{
if (instance == 0)
Font *instance = instance();
if (instance == nullptr)
{
EXCEPT_GUARD(instance = new Font();)
luax_catchexcept(L, [&](){ instance = new Font(); });
}
else
instance->retain();
+1 -1
View File
@@ -64,7 +64,7 @@ int w_GlyphData_getGlyphString(lua_State *L)
{
GlyphData *t = luax_checkglyphdata(L, 1);
EXCEPT_GUARD(luax_pushstring(L, t->getGlyphString());)
luax_catchexcept(L, [&](){ luax_pushstring(L, t->getGlyphString()); });
return 1;
}
@@ -72,7 +72,7 @@ int w_Rasterizer_getGlyphData(lua_State *L)
Rasterizer *t = luax_checkrasterizer(L, 1);
GlyphData *g = 0;
EXCEPT_GUARD(
luax_catchexcept(L, [&]() {
// getGlyphData accepts a unicode character or a codepoint number.
if (lua_type(L, 2) == LUA_TSTRING)
{
@@ -84,9 +84,10 @@ int w_Rasterizer_getGlyphData(lua_State *L)
uint32 glyph = (uint32) luaL_checknumber(L, 2);
g = t->getGlyphData(glyph);
}
)
});
luax_pushtype(L, "GlyphData", FONT_GLYPH_DATA_T, g);
g->release();
return 1;
}
@@ -106,7 +107,7 @@ int w_Rasterizer_hasGlyphs(lua_State *L)
int count = lua_gettop(L) - 1;
count = count < 1 ? 1 : count;
EXCEPT_GUARD(
luax_catchexcept(L, [&]() {
for (int i = 2; i < count + 2; i++)
{
if (lua_type(L, i) == LUA_TSTRING)
@@ -117,7 +118,7 @@ int w_Rasterizer_hasGlyphs(lua_State *L)
if (!hasglyph)
break;
}
)
});
luax_pushboolean(L, hasglyph);
return 1;
+19 -1
View File
@@ -109,6 +109,16 @@ bool Graphics::getConstant(SystemLimit in, const char *&out)
return systemLimits.find(in, out);
}
bool Graphics::getConstant(const char *in, StackType &out)
{
return stackTypes.find(in, out);
}
bool Graphics::getConstant(StackType in, const char *&out)
{
return stackTypes.find(in, out);
}
StringMap<Graphics::DrawMode, Graphics::DRAW_MAX_ENUM>::Entry Graphics::drawModeEntries[] =
{
{ "line", Graphics::DRAW_LINE },
@@ -178,7 +188,6 @@ StringMap<Graphics::Support, Graphics::SUPPORT_MAX_ENUM>::Entry Graphics::suppor
{ "bc5", Graphics::SUPPORT_BC5 },
{ "etc1", Graphics::SUPPORT_ETC1 },
{ "pvrtc1", Graphics::SUPPORT_PVRTC1 },
{ "instancing", Graphics::SUPPORT_INSTANCING },
{ "srgb", Graphics::SUPPORT_SRGB },
};
@@ -190,9 +199,18 @@ StringMap<Graphics::SystemLimit, Graphics::LIMIT_MAX_ENUM>::Entry Graphics::syst
{"texturesize", Graphics::LIMIT_TEXTURE_SIZE},
{"multicanvas", Graphics::LIMIT_MULTI_CANVAS},
{"canvasfsaa", Graphics::LIMIT_CANVAS_FSAA},
{"canvasmsaa", Graphics::LIMIT_CANVAS_MSAA},
};
StringMap<Graphics::SystemLimit, Graphics::LIMIT_MAX_ENUM> Graphics::systemLimits(Graphics::systemLimitEntries, sizeof(Graphics::systemLimitEntries));
StringMap<Graphics::StackType, Graphics::STACK_MAX_ENUM>::Entry Graphics::stackTypeEntries[] =
{
{"all", Graphics::STACK_ALL},
{"transform", Graphics::STACK_TRANSFORM},
};
StringMap<Graphics::StackType, Graphics::STACK_MAX_ENUM> Graphics::stackTypes(Graphics::stackTypeEntries, sizeof(Graphics::stackTypeEntries));
} // graphics
} // love
+36 -18
View File
@@ -25,6 +25,9 @@
#include "common/Module.h"
#include "common/StringMap.h"
// C++
#include <string>
namespace love
{
namespace graphics
@@ -36,14 +39,14 @@ public:
enum DrawMode
{
DRAW_LINE = 1,
DRAW_LINE,
DRAW_FILL,
DRAW_MAX_ENUM
};
enum AlignMode
{
ALIGN_LEFT = 1,
ALIGN_LEFT,
ALIGN_CENTER,
ALIGN_RIGHT,
ALIGN_JUSTIFY,
@@ -52,7 +55,7 @@ public:
enum BlendMode
{
BLEND_ALPHA = 1,
BLEND_ALPHA,
BLEND_ADDITIVE,
BLEND_SUBTRACTIVE,
BLEND_MULTIPLICATIVE,
@@ -64,14 +67,14 @@ public:
enum LineStyle
{
LINE_ROUGH = 1,
LINE_ROUGH,
LINE_SMOOTH,
LINE_MAX_ENUM
};
enum LineJoin
{
LINE_JOIN_NONE = 1,
LINE_JOIN_NONE,
LINE_JOIN_MITER,
LINE_JOIN_BEVEL,
LINE_JOIN_MAX_ENUM
@@ -79,14 +82,14 @@ public:
enum PointStyle
{
POINT_ROUGH = 1,
POINT_ROUGH,
POINT_SMOOTH,
POINT_MAX_ENUM
};
enum Support
{
SUPPORT_CANVAS = 1,
SUPPORT_CANVAS,
SUPPORT_HDR_CANVAS,
SUPPORT_MULTI_CANVAS,
SUPPORT_SHADER,
@@ -97,7 +100,6 @@ public:
SUPPORT_BC5,
SUPPORT_ETC1,
SUPPORT_PVRTC1,
SUPPORT_INSTANCING,
SUPPORT_SRGB,
SUPPORT_MAX_ENUM
};
@@ -109,26 +111,36 @@ public:
RENDERER_MAX_ENUM
};
enum RendererInfo
{
RENDERER_INFO_NAME = 1,
RENDERER_INFO_VERSION,
RENDERER_INFO_VENDOR,
RENDERER_INFO_DEVICE,
RENDERER_INFO_MAX_ENUM
};
enum SystemLimit
{
LIMIT_POINT_SIZE,
LIMIT_TEXTURE_SIZE,
LIMIT_MULTI_CANVAS,
LIMIT_CANVAS_FSAA,
LIMIT_CANVAS_FSAA, // For backward-compatibility. TODO: remove!
LIMIT_CANVAS_MSAA,
LIMIT_MAX_ENUM
};
enum StackType
{
STACK_ALL,
STACK_TRANSFORM,
STACK_MAX_ENUM
};
struct RendererInfo
{
std::string name;
std::string version;
std::string vendor;
std::string device;
};
virtual ~Graphics();
// Implements Module.
virtual ModuleType getModuleType() const { return M_GRAPHICS; }
/**
* Sets the current graphics display viewport dimensions.
**/
@@ -185,6 +197,9 @@ public:
static bool getConstant(const char *in, SystemLimit &out);
static bool getConstant(SystemLimit in, const char *&out);
static bool getConstant(const char *in, StackType &out);
static bool getConstant(StackType in, const char *&out);
private:
static StringMap<DrawMode, DRAW_MAX_ENUM>::Entry drawModeEntries[];
@@ -211,6 +226,9 @@ private:
static StringMap<SystemLimit, LIMIT_MAX_ENUM>::Entry systemLimitEntries[];
static StringMap<SystemLimit, LIMIT_MAX_ENUM> systemLimits;
static StringMap<StackType, STACK_MAX_ENUM>::Entry stackTypeEntries[];
static StringMap<StackType, STACK_MAX_ENUM> stackTypes;
}; // Graphics
} // graphics
+1 -1
View File
@@ -96,7 +96,7 @@ public:
virtual void setFilter(const Filter &f) = 0;
virtual const Filter &getFilter() const;
virtual void setWrap(const Wrap &w) = 0;
virtual bool setWrap(const Wrap &w) = 0;
virtual const Wrap &getWrap() const;
virtual const Vertex *getVertices() const;
+62 -64
View File
@@ -451,15 +451,15 @@ static void getStrategy()
}
}
Canvas::Canvas(int width, int height, Format format, int fsaa)
Canvas::Canvas(int width, int height, Format format, int msaa)
: fbo(0)
, resolve_fbo(0)
, texture(0)
, fsaa_buffer(0)
, msaa_buffer(0)
, depth_stencil(0)
, format(format)
, fsaa_samples(fsaa)
, fsaa_dirty(false)
, msaa_samples(msaa)
, msaa_dirty(false)
{
this->width = width;
this->height = height;
@@ -501,7 +501,7 @@ Canvas::~Canvas()
unloadVolatile();
}
bool Canvas::createFSAAFBO(GLenum internalformat)
bool Canvas::createMSAAFBO(GLenum internalformat)
{
// Create our FBO without a texture.
status = strategy->createFBO(fbo, 0);
@@ -516,7 +516,7 @@ bool Canvas::createFSAAFBO(GLenum internalformat)
}
// Create and attach the MSAA buffer for our FBO.
if (strategy->createMSAABuffer(width, height, fsaa_samples, internalformat, fsaa_buffer))
if (strategy->createMSAABuffer(width, height, msaa_samples, internalformat, msaa_buffer))
status = GL_FRAMEBUFFER_COMPLETE;
else
status = GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
@@ -528,10 +528,10 @@ bool Canvas::createFSAAFBO(GLenum internalformat)
if (status != GL_FRAMEBUFFER_COMPLETE)
{
// Clean up.
strategy->deleteFBO(fbo, 0, fsaa_buffer);
strategy->deleteFBO(fbo, 0, msaa_buffer);
strategy->deleteFBO(resolve_fbo, 0, 0);
fbo = fsaa_buffer = resolve_fbo = 0;
fsaa_samples = 0;
fbo = msaa_buffer = resolve_fbo = 0;
msaa_samples = 0;
}
if (current != this)
@@ -543,7 +543,7 @@ bool Canvas::createFSAAFBO(GLenum internalformat)
bool Canvas::loadVolatile()
{
fbo = depth_stencil = texture = 0;
resolve_fbo = fsaa_buffer = 0;
resolve_fbo = msaa_buffer = 0;
status = GL_FRAMEBUFFER_COMPLETE;
// glTexImage2D is guaranteed to error in this case.
@@ -597,16 +597,16 @@ bool Canvas::loadVolatile()
glGetIntegerv(GL_MAX_SAMPLES, &max_samples);
}
if (fsaa_samples > max_samples)
fsaa_samples = max_samples;
if (msaa_samples > max_samples)
msaa_samples = max_samples;
// Try to create a FSAA FBO if requested.
bool fsaasuccess = false;
if (fsaa_samples > 1)
fsaasuccess = createFSAAFBO(internalformat);
// Try to create a MSAA FBO if requested.
bool msaasuccess = false;
if (msaa_samples > 1)
msaasuccess = createMSAAFBO(internalformat);
// On failure (or no requested FSAA), fall back to a regular FBO.
if (!fsaasuccess)
// On failure (or no requested MSAA), fall back to a regular FBO.
if (!msaasuccess)
status = strategy->createFBO(fbo, texture);
if (status != GL_FRAMEBUFFER_COMPLETE)
@@ -614,31 +614,28 @@ bool Canvas::loadVolatile()
clear(Color(0, 0, 0, 0));
fsaa_dirty = (fsaa_buffer != 0);
msaa_dirty = (msaa_buffer != 0);
return true;
}
void Canvas::unloadVolatile()
{
strategy->deleteFBO(fbo, depth_stencil, fsaa_buffer);
strategy->deleteFBO(fbo, depth_stencil, msaa_buffer);
strategy->deleteFBO(resolve_fbo, 0, 0);
gl.deleteTexture(texture);
fbo = depth_stencil = texture = 0;
resolve_fbo = fsaa_buffer = 0;
for (size_t i = 0; i < attachedCanvases.size(); i++)
attachedCanvases[i]->release();
resolve_fbo = msaa_buffer = 0;
attachedCanvases.clear();
}
void Canvas::drawv(const Matrix &t, const Vertex *v)
{
gl.matrices.transform.push(gl.matrices.transform.top());
gl.matrices.transform.top() *= t;
OpenGL::TempTransform transform(gl);
transform.get() *= t;
gl.prepareDraw();
@@ -656,8 +653,6 @@ void Canvas::drawv(const Matrix &t, const Vertex *v)
gl.disableVertexAttribArray(OpenGL::ATTRIB_TEXCOORD);
postdraw();
gl.matrices.transform.pop();
}
void Canvas::draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky)
@@ -684,11 +679,24 @@ void Canvas::setFilter(const Texture::Filter &f)
gl.setTextureFilter(filter);
}
void Canvas::setWrap(const Texture::Wrap &w)
bool Canvas::setWrap(const Texture::Wrap &w)
{
bool success = true;
wrap = w;
if (hasLimitedNpot() && (width != next_p2(width) || height != next_p2(height)))
{
if (wrap.s != WRAP_CLAMP || wrap.t != WRAP_CLAMP)
success = false;
// If we only have limited NPOT support then the wrap mode must be CLAMP.
wrap.s = wrap.t = WRAP_CLAMP;
}
gl.bindTexture(texture);
gl.setTextureWrap(wrap);
return success;
}
GLuint Canvas::getGLTexture() const
@@ -727,8 +735,8 @@ void Canvas::setupGrab()
strategy->bindFBO(fbo);
gl.setViewport(OpenGL::Viewport(0, 0, width, height));
// Set up orthographic view (no depth)
gl.matrices.projection.push(Matrix::ortho(0.0, width, 0.0, height));
// Set up the projection matrix
gl.matrices.projection.push_back(Matrix::ortho(0.0, width, 0.0, height));
// Make sure the correct sRGB setting is used when drawing to the canvas.
if (GLAD_VERSION_1_1 || GLAD_EXT_sRGB_write_control)
@@ -739,8 +747,8 @@ void Canvas::setupGrab()
glDisable(GL_FRAMEBUFFER_SRGB);
}
if (fsaa_buffer != 0)
fsaa_dirty = true;
if (msaa_buffer != 0)
msaa_dirty = true;
}
void Canvas::startGrab(const std::vector<Canvas *> &canvases)
@@ -757,8 +765,8 @@ void Canvas::startGrab(const std::vector<Canvas *> &canvases)
if ((int) canvases.size() + 1 > gl.getMaxRenderTargets())
throw love::Exception("This system can't simultaniously render to %d canvases.", canvases.size()+1);
if (fsaa_samples != 0)
throw love::Exception("Multi-canvas rendering is not supported with FSAA.");
if (msaa_samples != 0)
throw love::Exception("Multi-canvas rendering is not supported with MSAA.");
}
for (size_t i = 0; i < canvases.size(); i++)
@@ -769,8 +777,8 @@ void Canvas::startGrab(const std::vector<Canvas *> &canvases)
if (canvases[i]->getTextureFormat() != format)
throw love::Exception("All canvas arguments must have the same texture format.");
if (canvases[i]->getFSAA() != 0)
throw love::Exception("Multi-canvas rendering is not supported with FSAA.");
if (canvases[i]->getMSAA() != 0)
throw love::Exception("Multi-canvas rendering is not supported with MSAA.");
if (!canvaseschanged && canvases[i] != attachedCanvases[i])
canvaseschanged = true;
@@ -785,11 +793,8 @@ void Canvas::startGrab(const std::vector<Canvas *> &canvases)
// Attach the canvas textures to the active FBO and set up MRTs.
strategy->setAttachments(canvases);
for (size_t i = 0; i < canvases.size(); i++)
canvases[i]->retain();
for (size_t i = 0; i < attachedCanvases.size(); i++)
attachedCanvases[i]->release();
// We want to avoid reference cycles, so we don't retain the attached
// Canvases here. The code in Graphics::setCanvas retains them.
attachedCanvases = canvases;
}
@@ -804,10 +809,6 @@ void Canvas::startGrab()
// make sure the FBO is only using a single canvas
strategy->setAttachments();
// release any previously attached canvases
for (size_t i = 0; i < attachedCanvases.size(); i++)
attachedCanvases[i]->release();
attachedCanvases.clear();
}
@@ -824,6 +825,8 @@ void Canvas::stopGrab(bool switchingToOtherCanvas)
glDiscardFramebufferEXT(GL_FRAMEBUFFER, 2, attachments);
}
gl.matrices.projection.pop_back();
if (switchingToOtherCanvas)
{
if (GLAD_VERSION_1_1 || GLAD_EXT_sRGB_write_control)
@@ -847,8 +850,6 @@ void Canvas::stopGrab(bool switchingToOtherCanvas)
glEnable(GL_FRAMEBUFFER_SRGB);
}
}
gl.matrices.projection.pop();
}
void Canvas::clear(Color c)
@@ -902,8 +903,8 @@ void Canvas::clear(Color c)
if (current != this)
strategy->bindFBO(previous);
if (fsaa_buffer != 0)
fsaa_dirty = true;
if (msaa_buffer != 0)
msaa_dirty = true;
}
bool Canvas::checkCreateStencil()
@@ -915,7 +916,7 @@ bool Canvas::checkCreateStencil()
if (current != this)
strategy->bindFBO(fbo);
bool success = strategy->createStencil(width, height, fsaa_samples, depth_stencil);
bool success = strategy->createStencil(width, height, msaa_samples, depth_stencil);
if (current && current != this)
strategy->bindFBO(current->fbo);
@@ -934,9 +935,9 @@ love::image::ImageData *Canvas::getImageData(love::image::Image *image)
GLubyte *pixels = new GLubyte[size];
// Our texture is attached to 'resolve_fbo' when we use MSAA.
if (fsaa_samples > 1 && (GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_object))
if (msaa_samples > 1 && (GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_object))
glBindFramebuffer(GL_READ_FRAMEBUFFER, resolve_fbo);
else if (fsaa_samples > 1 && GLAD_EXT_framebuffer_multisample)
else if (msaa_samples > 1 && GLAD_EXT_framebuffer_multisample)
glBindFramebufferEXT(GL_READ_FRAMEBUFFER, resolve_fbo);
else
strategy->bindFBO(fbo);
@@ -955,12 +956,15 @@ love::image::ImageData *Canvas::getImageData(love::image::Image *image)
void Canvas::getPixel(unsigned char* pixel_rgba, int x, int y)
{
if (x < 0 || x >= width || y < 0 || y >= width)
throw love::Exception("Attempt to get out-of-range pixel (%d,%d)!", x, y);
resolveMSAA();
// Our texture is attached to 'resolve_fbo' when we use MSAA.
if (fsaa_samples > 1 && (GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_object))
if (msaa_samples > 1 && (GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_object))
glBindFramebuffer(GL_READ_FRAMEBUFFER, resolve_fbo);
else if (fsaa_samples > 1 && GLAD_EXT_framebuffer_multisample)
else if (msaa_samples > 1 && GLAD_EXT_framebuffer_multisample)
glBindFramebufferEXT(GL_READ_FRAMEBUFFER, resolve_fbo);
else if (current != this)
strategy->bindFBO(fbo);
@@ -975,10 +979,10 @@ void Canvas::getPixel(unsigned char* pixel_rgba, int x, int y)
bool Canvas::resolveMSAA()
{
if (resolve_fbo == 0 || fsaa_buffer == 0)
if (resolve_fbo == 0 || msaa_buffer == 0)
return false;
if (!fsaa_dirty)
if (!msaa_dirty)
return true;
GLuint previous = 0;
@@ -1006,7 +1010,7 @@ bool Canvas::resolveMSAA()
strategy->bindFBO(previous);
if (current != this)
fsaa_dirty = false;
msaa_dirty = false;
return true;
}
@@ -1203,12 +1207,6 @@ bool Canvas::isFormatSupported(Canvas::Format format)
return supported;
}
void Canvas::bindDefaultCanvas()
{
if (current != nullptr)
current->stopGrab();
}
bool Canvas::getConstant(const char *in, Format &out)
{
return formats.find(in, out);
@@ -57,7 +57,7 @@ public:
FORMAT_MAX_ENUM
};
Canvas(int width, int height, Format format = FORMAT_NORMAL, int fsaa = 0);
Canvas(int width, int height, Format format = FORMAT_NORMAL, int msaa = 0);
virtual ~Canvas();
// Implements Volatile.
@@ -70,7 +70,7 @@ public:
// Implements Texture.
virtual void drawq(Quad *quad, float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky);
virtual void setFilter(const Texture::Filter &f);
virtual void setWrap(const Texture::Wrap &w);
virtual bool setWrap(const Texture::Wrap &w);
virtual GLuint getGLTexture() const;
virtual void predraw();
@@ -108,9 +108,9 @@ public:
return format;
}
inline int getFSAA() const
inline int getMSAA() const
{
return fsaa_samples;
return msaa_samples;
}
bool resolveMSAA();
@@ -120,7 +120,6 @@ public:
static bool isFormatSupported(Format format);
static Canvas *current;
static void bindDefaultCanvas();
// The viewport dimensions of the system (default) framebuffer.
static OpenGL::Viewport systemViewport;
@@ -133,7 +132,7 @@ public:
private:
bool createFSAAFBO(GLenum internalformat);
bool createMSAAFBO(GLenum internalformat);
static Format getSizedFormat(Format format);
static void convertFormat(Format format, GLenum &internalformat, GLenum &externalformat, GLenum &type);
@@ -142,7 +141,7 @@ private:
GLuint resolve_fbo;
GLuint texture;
GLuint fsaa_buffer;
GLuint msaa_buffer;
GLuint depth_stencil;
Format format;
@@ -151,8 +150,8 @@ private:
std::vector<Canvas *> attachedCanvases;
int fsaa_samples;
bool fsaa_dirty;
int msaa_samples;
bool msaa_dirty;
void setupGrab();
void drawv(const Matrix &t, const Vertex *v);
@@ -90,14 +90,11 @@ Font::Font(love::font::Rasterizer *r, const Texture::Filter &filter)
}
delete gd;
rasterizer->retain();
}
Font::~Font()
{
delete indexBuffer;
rasterizer->release();
unloadVolatile();
}
@@ -385,11 +382,11 @@ void Font::print(const std::string &text, float x, float y, float extra_spacing,
indexBuffer = newIndexBuffer;
}
gl.matrices.transform.push(gl.matrices.transform.top());
Matrix t;
t.setTransformation(ceilf(x), ceilf(y), angle, sx, sy, ox, oy, kx, ky);
gl.matrices.transform.top() *= t;
OpenGL::TempTransform transform(gl);
transform.get() *= t;
gl.enableVertexAttribArray(OpenGL::ATTRIB_POS);
gl.enableVertexAttribArray(OpenGL::ATTRIB_TEXCOORD);
@@ -430,8 +427,6 @@ void Font::print(const std::string &text, float x, float y, float extra_spacing,
gl.disableVertexAttribArray(OpenGL::ATTRIB_POS);
gl.disableVertexAttribArray(OpenGL::ATTRIB_TEXCOORD);
gl.matrices.transform.pop();
}
int Font::getWidth(const std::string &str)
+2 -2
View File
@@ -143,7 +143,7 @@ private:
enum FontType
{
FONT_TRUETYPE = 1,
FONT_TRUETYPE,
FONT_IMAGE,
FONT_UNKNOWN
};
@@ -184,7 +184,7 @@ private:
Glyph *addGlyph(uint32 glyph);
Glyph *findGlyph(uint32 glyph);
love::font::Rasterizer *rasterizer;
Object::StrongRef<love::font::Rasterizer> rasterizer;
VertexIndex *indexBuffer;
File diff suppressed because it is too large Load Diff
+70 -60
View File
@@ -57,48 +57,7 @@ namespace opengl
// During display mode changing, certain
// variables about the OpenGL context are
// lost.
struct DisplayState
{
// Colors.
Color color;
Color backgroundColor;
// Blend mode.
Graphics::BlendMode blendMode;
// Line.
Graphics::LineStyle lineStyle;
Graphics::LineJoin lineJoin;
// Point.
float pointSize;
Graphics::PointStyle pointStyle;
// Scissor.
bool scissor;
OpenGL::Viewport scissorBox;
// Color mask.
bool colorMask[4];
bool wireframe;
// Default values.
DisplayState()
{
color.set(255,255,255,255);
backgroundColor.set(0, 0, 0, 255);
blendMode = Graphics::BLEND_ALPHA;
lineStyle = Graphics::LINE_SMOOTH;
lineJoin = Graphics::LINE_JOIN_MITER;
pointSize = 1.0f;
pointStyle = Graphics::POINT_SMOOTH;
scissor = false;
colorMask[0] = colorMask[1] = colorMask[2] = colorMask[3] = true;
wireframe = false;
}
};
class Graphics : public love::graphics::Graphics
{
@@ -110,10 +69,6 @@ public:
// Implements Module.
const char *getName() const;
DisplayState saveState();
void restoreState(const DisplayState &s);
virtual void setViewportSize(int width, int height);
virtual bool setMode(int width, int height, bool &sRGB);
virtual void unSetMode();
@@ -211,7 +166,7 @@ public:
ParticleSystem *newParticleSystem(Texture *texture, int size);
Canvas *newCanvas(int width, int height, Canvas::Format format = Canvas::FORMAT_NORMAL, int fsaa = 0);
Canvas *newCanvas(int width, int height, Canvas::Format format = Canvas::FORMAT_NORMAL, int msaa = 0);
Shader *newShader(const Shader::ShaderSources &sources);
@@ -249,10 +204,22 @@ public:
**/
Font *getFont() const;
void setShader(Shader *shader);
void setShader();
Shader *getShader() const;
void setCanvas(Canvas *canvas);
void setCanvas(const std::vector<Canvas *> &canvases);
void setCanvas(const std::vector<Object::StrongRef<Canvas>> &canvases);
void setCanvas();
std::vector<Canvas *> getCanvas() const;
/**
* Sets the enabled color components when rendering.
**/
void setColorMask(bool r, bool g, bool b, bool a);
void setColorMask(const bool mask[4]);
/**
* Gets the current color mask.
@@ -447,20 +414,25 @@ public:
love::image::ImageData *newScreenshot(love::image::Image *image, bool copyAlpha = true);
/**
* Returns a string containing system-dependent renderer information.
* Returned string can vary greatly between systems! Do not rely on it for
* Returns system-dependent renderer information.
* Returned string s can vary greatly between systems! Do not rely on it for
* anything!
* @param infotype The type of information to return.
**/
std::string getRendererInfo(Graphics::RendererInfo infotype) const;
RendererInfo getRendererInfo() const;
/**
* Gets the system-dependent numeric limit for the specified parameter.
**/
double getSystemLimit(SystemLimit limittype) const;
void push();
/**
* Gets whether a graphics feature is supported on this system.
**/
bool isSupported(Support feature) const;
void push(StackType type = STACK_TRANSFORM);
void pop();
void rotate(float r);
void scale(float x, float y = 1.0f);
void translate(float x, float y);
@@ -469,16 +441,51 @@ public:
private:
Font *currentFont;
struct DisplayState
{
// Colors.
Color color;
Color backgroundColor;
// Blend mode.
BlendMode blendMode;
// Line.
float lineWidth;
LineStyle lineStyle;
LineJoin lineJoin;
// Point.
float pointSize;
PointStyle pointStyle;
// Scissor.
bool scissor;
OpenGL::Viewport scissorBox;
Object::StrongRef<Font> font;
Object::StrongRef<Shader> shader;
std::vector<Object::StrongRef<Canvas>> canvases;
// Color mask.
bool colorMask[4];
bool wireframe;
DisplayState();
DisplayState(const DisplayState &other);
~DisplayState();
DisplayState &operator = (const DisplayState &other);
};
void restoreState(const DisplayState &s);
void restoreStateChecked(const DisplayState &s);
love::window::Window *currentWindow;
std::vector<double> pixel_size_stack; // stores current size of a pixel (needed for line drawing)
LineStyle lineStyle;
LineJoin lineJoin;
float lineWidth;
size_t matrixLimit;
bool colorMask[4];
bool wireframe;
int width;
int height;
@@ -487,7 +494,10 @@ private:
bool activeStencil;
DisplayState savedState;
std::vector<DisplayState> states;
std::vector<StackType> stackTypes; // Keeps track of the pushed stack types.
static const size_t MAX_USER_STACK_DEPTH = 64;
}; // Graphics
+42 -29
View File
@@ -50,8 +50,6 @@ Image::Image(love::image::ImageData *data, Format format)
{
width = data->getWidth();
height = data->getHeight();
data->retain();
preload();
}
@@ -69,28 +67,22 @@ Image::Image(love::image::CompressedData *cdata, Format format)
{
width = cdata->getWidth(0);
height = cdata->getHeight(0);
cdata->retain();
preload();
}
Image::~Image()
{
if (data != nullptr)
data->release();
if (cdata != nullptr)
cdata->release();
unload();
}
love::image::ImageData *Image::getImageData() const
{
return data;
return data.get();
}
love::image::CompressedData *Image::getCompressedData() const
{
return cdata;
return cdata.get();
}
void Image::draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky)
@@ -140,7 +132,11 @@ GLuint Image::getGLTexture() const
void Image::uploadCompressedMipmaps()
{
if (!isCompressed() || !cdata || !hasCompressedTextureSupport(cdata->getFormat()))
if (!isCompressed() || !cdata.get() || !hasCompressedTextureSupport(cdata->getFormat()))
return;
// NPOT textures don't support mipmapping without full NPOT support.
if (hasLimitedNpot() && (width != next_p2(width) || height != next_p2(height)))
return;
bind();
@@ -175,7 +171,7 @@ void Image::uploadCompressedMipmaps()
void Image::createMipmaps()
{
// Only valid for Images created with ImageData.
if (!data || isCompressed())
if (!data.get() || isCompressed())
return;
if (!hasMipmapSupport())
@@ -222,6 +218,7 @@ void Image::createMipmaps()
GL_RGBA,
GL_UNSIGNED_BYTE,
data->getData());
glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_FALSE);
}
}
@@ -230,9 +227,9 @@ void Image::checkMipmapsCreated()
if (mipmapsCreated || filter.mipmap == FILTER_NONE || usingDefaultTexture)
return;
if (isCompressed() && cdata && hasCompressedTextureSupport(cdata->getFormat()))
if (isCompressed() && cdata.get() && hasCompressedTextureSupport(cdata->getFormat()))
uploadCompressedMipmaps();
else if (data)
else if (data.get())
createMipmaps();
else
return;
@@ -251,17 +248,35 @@ void Image::setFilter(const Texture::Filter &f)
filter.min = filter.mag = FILTER_NEAREST;
}
if (hasLimitedNpot() && (width != next_p2(width) || height != next_p2(height)))
{
// If we only have limited NPOT support then mipmapping isn't supported.
filter.mipmap = FILTER_NONE;
}
bind();
gl.setTextureFilter(filter);
checkMipmapsCreated();
}
void Image::setWrap(const Texture::Wrap &w)
bool Image::setWrap(const Texture::Wrap &w)
{
bool success = true;
wrap = w;
if (hasLimitedNpot() && (width != next_p2(width) || height != next_p2(height)))
{
if (wrap.s != WRAP_CLAMP || wrap.t != WRAP_CLAMP)
success = false;
// If we only have limited NPOT support then the wrap mode must be CLAMP.
wrap.s = wrap.t = WRAP_CLAMP;
}
bind();
gl.setTextureWrap(w);
return success;
}
void Image::setMipmapSharpness(float sharpness)
@@ -333,7 +348,7 @@ bool Image::loadVolatile()
if (format == FORMAT_SRGB && !hasSRGBSupport())
throw love::Exception("sRGB images are not supported on this system.");
if (isCompressed() && cdata && !hasCompressedTextureSupport(cdata->getFormat()))
if (isCompressed() && cdata.get() && !hasCompressedTextureSupport(cdata->getFormat()))
{
const char *str;
if (image::CompressedData::getConstant(cdata->getFormat(), str))
@@ -351,8 +366,8 @@ bool Image::loadVolatile()
glGenTextures(1, &texture);
gl.bindTexture(texture);
filter.anisotropy = gl.setTextureFilter(filter);
gl.setTextureWrap(wrap);
setFilter(filter);
setWrap(wrap);
setMipmapSharpness(mipmapSharpness);
paddedWidth = width;
@@ -374,7 +389,7 @@ bool Image::loadVolatile()
// Mutex lock will potentially cover texture loading and mipmap creation.
love::thread::EmptyLock lock;
if (data)
if (data.get())
lock.setLock(data->getMutex());
while (glGetError() != GL_NO_ERROR); // Clear errors.
@@ -397,13 +412,13 @@ bool Image::loadVolatile()
void Image::uploadTexturePadded()
{
if (isCompressed() && cdata)
if (isCompressed() && cdata.get())
{
// Padded textures don't really work if they're compressed...
throw love::Exception("Cannot create image: "
"compressed NPOT images are not supported on this system.");
}
else if (data)
else if (data.get())
{
GLenum iformat = (format == FORMAT_SRGB) ? GL_SRGB_ALPHA : GL_RGBA;
glTexImage2D(GL_TEXTURE_2D,
@@ -429,7 +444,7 @@ void Image::uploadTexturePadded()
void Image::uploadTexture()
{
if (isCompressed() && cdata)
if (isCompressed() && cdata.get())
{
GLenum format = getCompressedFormat(cdata->getFormat());
glCompressedTexImage2D(GL_TEXTURE_2D,
@@ -441,7 +456,7 @@ void Image::uploadTexture()
GLsizei(cdata->getSize(0)),
cdata->getData(0));
}
else if (data)
else if (data.get())
{
GLenum iformat = (format == FORMAT_SRGB) ? GL_SRGB_ALPHA : GL_RGBA;
glTexImage2D(GL_TEXTURE_2D,
@@ -483,7 +498,7 @@ bool Image::refresh()
bind();
if (data && !isCompressed())
if (data.get() && !isCompressed())
lock.setLock(data->getMutex());
while (glGetError() != GL_NO_ERROR); // Clear errors.
@@ -525,10 +540,10 @@ void Image::uploadDefaultTexture()
void Image::drawv(const Matrix &t, const Vertex *v)
{
predraw();
OpenGL::TempTransform transform(gl);
transform.get() *= t;
gl.matrices.transform.push(gl.matrices.transform.top());
gl.matrices.transform.top() *= t;
predraw();
gl.prepareDraw();
@@ -543,8 +558,6 @@ void Image::drawv(const Matrix &t, const Vertex *v)
gl.disableVertexAttribArray(OpenGL::ATTRIB_POS);
gl.disableVertexAttribArray(OpenGL::ATTRIB_TEXCOORD);
gl.matrices.transform.pop();
postdraw();
}
+3 -3
View File
@@ -102,7 +102,7 @@ public:
virtual GLuint getGLTexture() const;
virtual void setFilter(const Texture::Filter &f);
virtual void setWrap(const Texture::Wrap &w);
virtual bool setWrap(const Texture::Wrap &w);
void setMipmapSharpness(float sharpness);
float getMipmapSharpness() const;
@@ -155,11 +155,11 @@ private:
// The ImageData from which the texture is created. May be null if
// Compressed image data was used to create the texture.
love::image::ImageData *data;
Object::StrongRef<love::image::ImageData> data;
// Or the Compressed Image Data from which the texture is created. May be
// null if raw ImageData was used to create the texture.
love::image::CompressedData *cdata;
Object::StrongRef<love::image::CompressedData> cdata;
// Real dimensions of the texture, if it was auto-padded to POT size.
int paddedWidth, paddedHeight;
+37 -67
View File
@@ -39,7 +39,6 @@ Mesh::Mesh(const std::vector<Vertex> &verts, Mesh::DrawMode mode)
, ibo(nullptr)
, element_count(0)
, element_data_type(getGLDataTypeFromMax(verts.size()))
, instance_count(1)
, draw_mode(mode)
, range_min(-1)
, range_max(-1)
@@ -175,20 +174,7 @@ void Mesh::setVertexMap(const std::vector<uint32> &map)
GLenum datatype = getGLDataTypeFromMax(vertex_count);
// Calculate the size in bytes of the index buffer data.
size_t size = map.size();
switch (datatype)
{
case GL_UNSIGNED_BYTE:
size *= sizeof(uint8);
break;
case GL_UNSIGNED_SHORT:
size *= sizeof(uint16);
break;
case GL_UNSIGNED_INT:
default:
size *= sizeof(uint32);
break;
}
size_t size = map.size() * getGLDataTypeSize(datatype);
if (ibo && size > ibo->getSize())
{
@@ -232,10 +218,10 @@ void Mesh::setVertexMap(const std::vector<uint32> &map)
* Copies index data from a mapped buffer to a vector.
**/
template <typename T>
static void copyFromIndexBuffer(void *buffer, std::vector<uint32> &indices, size_t maxval)
static void copyFromIndexBuffer(void *buffer, size_t count, std::vector<uint32> &indices)
{
T *elems = (T *) buffer;
for (size_t i = 0; i < maxval; i++)
for (size_t i = 0; i < count; i++)
indices.push_back((uint32) elems[i]);
}
@@ -256,14 +242,14 @@ void Mesh::getVertexMap(std::vector<uint32> &map) const
switch (element_data_type)
{
case GL_UNSIGNED_BYTE:
copyFromIndexBuffer<uint8>(buffer, map, vertex_count);
copyFromIndexBuffer<uint8>(buffer, element_count, map);
break;
case GL_UNSIGNED_SHORT:
copyFromIndexBuffer<uint16>(buffer, map, vertex_count);
copyFromIndexBuffer<uint16>(buffer, element_count, map);
break;
case GL_UNSIGNED_INT:
default:
copyFromIndexBuffer<uint32>(buffer, map, vertex_count);
copyFromIndexBuffer<uint32>(buffer, element_count, map);
break;
}
}
@@ -273,37 +259,19 @@ size_t Mesh::getVertexMapCount() const
return element_count;
}
void Mesh::setInstanceCount(int count)
{
instance_count = std::max(count, 1);
}
int Mesh::getInstanceCount() const
{
return instance_count;
}
void Mesh::setTexture(Texture *tex)
{
tex->retain();
if (texture)
texture->release();
texture = tex;
texture.set(tex);
}
void Mesh::setTexture()
{
if (texture)
texture->release();
texture = nullptr;
texture.set(nullptr);
}
Texture *Mesh::getTexture() const
{
return texture;
return texture.get();
}
void Mesh::setDrawMode(Mesh::DrawMode mode)
@@ -355,7 +323,7 @@ void Mesh::draw(float x, float y, float angle, float sx, float sy, float ox, flo
if (vertex_count == 0)
return;
if (texture)
if (texture.get())
texture->predraw();
else
gl.bindTexture(gl.getDefaultTexture());
@@ -363,8 +331,8 @@ void Mesh::draw(float x, float y, float angle, float sx, float sy, float ox, flo
Matrix m;
m.setTransformation(x, y, angle, sx, sy, ox, oy, kx, ky);
gl.matrices.transform.push(gl.matrices.transform.top());
gl.matrices.transform.top() *= m;
OpenGL::TempTransform transform(gl);
transform.get() *= m;
VertexBuffer::Bind vbo_bind(*vbo);
@@ -398,35 +366,29 @@ void Mesh::draw(float x, float y, float angle, float sx, float sy, float ox, flo
int max = element_count - 1;
if (range_max >= 0)
max = std::min(std::max(range_max, 0), (int) element_count - 1);
max = std::min(range_max, max);
int min = 0;
if (range_min >= 0)
min = std::min(std::max(range_min, 0), max);
min = std::min(range_min, max);
const void *indices = ibo->getPointer(min * sizeof(uint32));
GLenum type = element_data_type;
const void *indices = ibo->getPointer(min * getGLDataTypeSize(type));
if (instance_count > 1)
gl.drawElementsInstanced(mode, max - min + 1, type, indices, instance_count);
else
glDrawElements(mode, max - min + 1, type, indices);
glDrawElements(mode, max - min + 1, type, indices);
}
else
{
int max = vertex_count - 1;
if (range_max >= 0)
max = std::min(std::max(range_max, 0), (int) vertex_count - 1);
max = std::min(range_max, max);
int min = 0;
if (range_min >= 0)
min = std::min(std::max(range_min, 0), max);
min = std::min(range_min, max);
// Normal non-indexed drawing (no custom vertex map.)
if (instance_count > 1)
gl.drawArraysInstanced(mode, min, max - min + 1, instance_count);
else
glDrawArrays(mode, min, max - min + 1);
glDrawArrays(mode, min, max - min + 1);
}
gl.disableVertexAttribArray(OpenGL::ATTRIB_POS);
@@ -439,9 +401,7 @@ void Mesh::draw(float x, float y, float angle, float sx, float sy, float ox, flo
gl.setColor(gl.getColor());
}
gl.matrices.transform.pop();
if (texture)
if (texture.get())
texture->postdraw();
}
@@ -454,24 +414,34 @@ GLenum Mesh::getGLDrawMode(DrawMode mode) const
case DRAW_MODE_STRIP:
return GL_TRIANGLE_STRIP;
case DRAW_MODE_TRIANGLES:
default:
return GL_TRIANGLES;
case DRAW_MODE_POINTS:
return GL_POINTS;
default:
break;
}
return GL_TRIANGLES;
}
GLenum Mesh::getGLDataTypeFromMax(size_t maxvalue) const
{
if (maxvalue > LOVE_UINT16_MAX)
return GL_UNSIGNED_INT;
else if (maxvalue > LOVE_UINT8_MAX)
return GL_UNSIGNED_SHORT;
else
return GL_UNSIGNED_BYTE;
return GL_UNSIGNED_SHORT;
}
size_t Mesh::getGLDataTypeSize(GLenum datatype) const
{
switch (datatype)
{
case GL_UNSIGNED_BYTE:
return sizeof(uint8);
case GL_UNSIGNED_SHORT:
return sizeof(uint16);
case GL_UNSIGNED_INT:
return sizeof(uint32);
default:
return 0;
}
}
bool Mesh::getConstant(const char *in, Mesh::DrawMode &out)
+2 -12
View File
@@ -119,15 +119,6 @@ public:
**/
size_t getVertexMapCount() const;
/**
* Sets the number of instances of this Mesh to draw (uses hardware
* instancing when possible.)
* A custom vertex shader is necessary in order to introduce differences
* in each instance.
**/
void setInstanceCount(int count);
int getInstanceCount() const;
/**
* Sets the texture used when drawing the Mesh.
**/
@@ -171,6 +162,7 @@ private:
GLenum getGLDrawMode(DrawMode mode) const;
GLenum getGLDataTypeFromMax(size_t maxvalue) const;
size_t getGLDataTypeSize(GLenum datatype) const;
// Vertex buffer.
VertexBuffer *vbo;
@@ -181,14 +173,12 @@ private:
size_t element_count;
GLenum element_data_type;
int instance_count;
DrawMode draw_mode;
int range_min;
int range_max;
Texture *texture;
Object::StrongRef<Texture> texture;
// Whether the per-vertex colors are used when drawing.
bool colors_enabled;
+54 -16
View File
@@ -28,6 +28,7 @@
// C++
#include <algorithm>
#include <limits>
// C
#include <cstring>
@@ -47,6 +48,8 @@ OpenGL::OpenGL()
, vendor(VENDOR_UNKNOWN)
, state()
{
matrices.transform.reserve(10);
matrices.projection.reserve(2);
}
bool OpenGL::initContext()
@@ -135,6 +138,14 @@ bool OpenGL::initContext()
state.lastPseudoInstanceID = -1;
// Invalidate the cached matrices by setting some elements to NaN.
float nan = std::numeric_limits<float>::quiet_NaN();
state.lastProjectionMatrix.setTranslation(nan, nan);
state.lastTransformMatrix.setTranslation(nan, nan);
if (GLAD_VERSION_1_1)
glMatrixMode(GL_MODELVIEW);
contextInitialized = true;
return true;
}
@@ -234,14 +245,11 @@ void OpenGL::initMaxValues()
void OpenGL::initMatrices()
{
while (matrices.transform.size() > 0)
matrices.transform.pop();
matrices.transform.clear();
matrices.projection.clear();
while (matrices.projection.size() > 0)
matrices.projection.pop();
matrices.transform.push(Matrix());
matrices.projection.push(Matrix());
matrices.transform.push_back(Matrix());
matrices.projection.push_back(Matrix());
}
void OpenGL::createDefaultTexture()
@@ -268,10 +276,25 @@ void OpenGL::createDefaultTexture()
bindTexture(curtexture);
}
void OpenGL::pushTransform()
{
matrices.transform.push_back(matrices.transform.back());
}
void OpenGL::popTransform()
{
matrices.transform.pop_back();
}
Matrix &OpenGL::getTransform()
{
return matrices.transform.back();
}
void OpenGL::prepareDraw()
{
const Matrix &transform = matrices.transform.top();
const Matrix &proj = matrices.projection.top();
const Matrix &transform = matrices.transform.back();
const Matrix &proj = matrices.projection.back();
Shader *shader = Shader::current;
@@ -292,11 +315,10 @@ void OpenGL::prepareDraw()
// We need to make sure antialiased Canvases are properly resolved
// before sampling from their textures in a shader.
// This is kind of a big hack. :(
const std::map<std::string, Object *> &r = shader->getBoundRetainables();
for (auto it = r.begin(); it != r.end(); ++it)
for (auto &r : shader->getBoundRetainables())
{
// Even bigger hack! D:
Canvas *canvas = dynamic_cast<Canvas *>(it->second);
Canvas *canvas = dynamic_cast<Canvas *>(r.second);
if (canvas != nullptr)
canvas->resolveMSAA();
}
@@ -315,10 +337,26 @@ void OpenGL::prepareDraw()
}
else if (GLAD_VERSION_1_0)
{
glMatrixMode(GL_PROJECTION);
glLoadMatrixf(proj.getElements());
glMatrixMode(GL_MODELVIEW);
glLoadMatrixf(transform.getElements());
const float *lastproj = state.lastProjectionMatrix.getElements();
// We only need to re-upload the projection matrix if it's changed.
if (memcmp(proj.getElements(), lastproj, sizeof(float) * 16) != 0)
{
glMatrixMode(GL_PROJECTION);
glLoadMatrixf(proj.getElements());
glMatrixMode(GL_MODELVIEW);
state.lastProjectionMatrix = proj;
}
const float *lastxform = state.lastTransformMatrix.getElements();
// Same with the transform matrix.
if (memcmp(transform.getElements(), lastxform, sizeof(float) * 16) != 0)
{
glLoadMatrixf(transform.getElements());
state.lastTransformMatrix = transform;
}
}
}
+35 -3
View File
@@ -21,6 +21,8 @@
#ifndef LOVE_GRAPHICS_OPENGL_OPENGL_H
#define LOVE_GRAPHICS_OPENGL_OPENGL_H
#include <stddef.h>
// LOVE
#include "graphics/Color.h"
#include "graphics/Texture.h"
@@ -115,13 +117,36 @@ public:
GLenum func;
};
// Transformation matrix stacks.
struct
{
std::stack<Matrix> transform;
std::stack<Matrix> projection;
std::vector<Matrix> transform;
std::vector<Matrix> projection;
} matrices;
class TempTransform
{
public:
TempTransform(OpenGL &gl)
: gl(gl)
{
gl.pushTransform();
}
~TempTransform()
{
gl.popTransform();
}
Matrix &get()
{
return gl.getTransform();
}
private:
OpenGL &gl;
};
OpenGL();
virtual ~OpenGL() {}
@@ -138,6 +163,10 @@ public:
**/
void deInitContext();
void pushTransform();
void popTransform();
Matrix &getTransform();
/**
* Set up necessary state (matrices etc.) for drawing. This *must* be called
* directly before GL draws.
@@ -360,6 +389,9 @@ private:
// The last ID value used for pseudo-instancing.
int lastPseudoInstanceID;
Matrix lastProjectionMatrix;
Matrix lastTransformMatrix;
} state;
}; // OpenGL
@@ -103,7 +103,6 @@ ParticleSystem::ParticleSystem(Texture *texture, uint32 size)
sizes.push_back(1.0f);
colors.push_back(Colorf(1.0f, 1.0f, 1.0f, 1.0f));
setBufferSize(size);
texture->retain();
}
ParticleSystem::ParticleSystem(const ParticleSystem &p)
@@ -147,19 +146,14 @@ ParticleSystem::ParticleSystem(const ParticleSystem &p)
, offsetX(p.offsetX)
, offsetY(p.offsetY)
, colors(p.colors)
, quads(p.quads)
, relativeRotation(p.relativeRotation)
{
setBufferSize(maxParticles);
if (texture != nullptr)
texture->retain();
}
ParticleSystem::~ParticleSystem()
{
if (texture != nullptr)
texture->release();
deleteBuffers();
}
@@ -315,6 +309,8 @@ void ParticleSystem::initParticle(Particle *p, float t)
p->angle += atan2f(p->velocity.y, p->velocity.x);
p->color = colors[0];
p->quadIndex = 0;
}
void ParticleSystem::insertTop(Particle *p)
@@ -423,19 +419,14 @@ ParticleSystem::Particle *ParticleSystem::removeParticle(Particle *p)
return pNext;
}
void ParticleSystem::setTexture(Texture *texture)
void ParticleSystem::setTexture(Texture *tex)
{
Object::AutoRelease imagerelease(this->texture);
this->texture = texture;
if (texture)
texture->retain();
texture.set(tex);
}
Texture *ParticleSystem::getTexture() const
{
return texture;
return texture.get();
}
void ParticleSystem::setInsertMode(InsertMode mode)
@@ -731,6 +722,33 @@ std::vector<Color> ParticleSystem::getColor() const
return ncolors;
}
void ParticleSystem::setQuads(const std::vector<Quad *> &newQuads)
{
std::vector<StrongRef<Quad>> quadlist;
quadlist.reserve(newQuads.size());
for (Quad *q : newQuads)
quadlist.push_back(q);
quads = quadlist;
}
void ParticleSystem::setQuads()
{
quads.clear();
}
std::vector<Quad *> ParticleSystem::getQuads() const
{
std::vector<Quad *> quadlist;
quadlist.reserve(quads.size());
for (const Object::StrongRef<Quad> &q : quads)
quadlist.push_back(q.get());
return quadlist;
}
void ParticleSystem::setRelativeRotation(bool enable)
{
relativeRotation = enable;
@@ -815,7 +833,7 @@ bool ParticleSystem::isFull() const
void ParticleSystem::draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky)
{
uint32 pCount = getCount();
if (pCount == 0 || texture == nullptr || pMem == nullptr || particleVerts == nullptr)
if (pCount == 0 || texture.get() == nullptr || pMem == nullptr || particleVerts == nullptr)
return;
Color curcolor = gl.getColor();
@@ -823,16 +841,21 @@ void ParticleSystem::draw(float x, float y, float angle, float sx, float sy, flo
Matrix t;
t.setTransformation(x, y, angle, sx, sy, ox, oy, kx, ky);
gl.matrices.transform.push(gl.matrices.transform.top());
gl.matrices.transform.top() *= t;
OpenGL::TempTransform transform(gl);
transform.get() *= t;
const Vertex *textureVerts = texture->getVertices();
Vertex *pVerts = particleVerts;
Particle *p = pHead;
bool useQuads = !quads.empty();
// set the vertex data for each particle (transformation, texcoords, color)
while (p)
{
if (useQuads)
textureVerts = quads[p->quadIndex]->getVertices();
// particle vertices are image vertices transformed by particle information
t.setTransformation(p->position[0], p->position[1], p->angle, p->size, p->size, offsetX, offsetY, 0.0f, 0.0f);
t.transform(pVerts, textureVerts, 4);
@@ -876,8 +899,6 @@ void ParticleSystem::draw(float x, float y, float angle, float sx, float sy, flo
texture->postdraw();
gl.matrices.transform.pop();
gl.setColor(curcolor);
}
@@ -961,6 +982,15 @@ void ParticleSystem::update(float dt)
s -= (float)i; // 0 <= s <= 1
p->color = colors[i] * (1.0f - s) + colors[k] * s;
// Update the quad index.
k = quads.size();
if (k > 0)
{
s = t * (float) k; // [0:numquads-1] (clamped below)
i = (s > 0.0f) ? (size_t) s : 0;
p->quadIndex = (i < k) ? i : k - 1;
}
// Next particle.
p = p->next;
}
@@ -28,6 +28,7 @@
#include "graphics/Drawable.h"
#include "graphics/Color.h"
#include "VertexBuffer.h"
#include "graphics/Quad.h"
#include "Texture.h"
// STL
@@ -422,6 +423,17 @@ public:
**/
std::vector<Color> getColor() const;
/**
* Sets a list of Quads to use for particles over their lifetime.
**/
void setQuads(const std::vector<Quad *> &newQuads);
void setQuads();
/**
* Gets the Quads used when drawing the particles.
**/
std::vector<Quad *> getQuads() const;
/**
* sets whether particle angles & rotations are relative to their velocities.
**/
@@ -532,6 +544,8 @@ protected:
float spinEnd;
Colorf color;
int quadIndex;
};
// Pointer to the beginning of the allocated memory.
@@ -553,7 +567,7 @@ protected:
VertexIndex *ibo;
// The texture to be drawn.
Texture *texture;
Object::StrongRef<Texture> texture;
// Whether the particle emitter is active.
bool active;
@@ -629,6 +643,9 @@ protected:
// Color.
std::vector<Colorf> colors;
// Quads.
std::vector<Object::StrongRef<Quad>> quads;
bool relativeRotation;
void createBuffers(size_t size);
@@ -18,14 +18,14 @@
* 3. This notice may not be removed or altered from any source distribution.
**/
#include <algorithm>
// LOVE
#include "Polyline.h"
// OpenGL
#include "OpenGL.h"
#include <algorithm>
// treat adjacent segments with angles between their directions <5 degree as straight
static const float LINES_PARALLEL_EPS = 0.05f;
@@ -36,6 +36,16 @@ namespace graphics
namespace opengl
{
Polyline::Polyline(GLenum mode, bool quadindices)
: vertices(nullptr)
, overdraw(nullptr)
, vertex_count(0)
, overdraw_vertex_count(0)
, draw_mode(mode)
, use_quad_indices(quadindices)
{
}
void Polyline::render(const float *coords, size_t count, size_t size_hint, float halfwidth, float pixel_size, bool draw_overdraw)
{
static std::vector<Vector> anchors;
@@ -327,13 +337,43 @@ Polyline::~Polyline()
void Polyline::draw()
{
GLushort *indices = nullptr;
if (use_quad_indices)
{
size_t numindices = (vertex_count / 4) * 6;
if (overdraw)
numindices = std::max(numindices, (overdraw_vertex_count / 4) * 6);
indices = new GLushort[numindices];
// Fill the index array to make the draw call render 2 triangles from
// a 4-vertex quad.
for (size_t i = 0; i < numindices / 6; i++)
{
// First triangle: vertices 0-1-2.
indices[i * 6 + 0] = GLushort(i * 4 + 0);
indices[i * 6 + 1] = GLushort(i * 4 + 1);
indices[i * 6 + 2] = GLushort(i * 4 + 2);
// Second triangle: vertices 0-2-3.
indices[i * 6 + 3] = GLushort(i * 4 + 0);
indices[i * 6 + 4] = GLushort(i * 4 + 2);
indices[i * 6 + 5] = GLushort(i * 4 + 3);
}
}
gl.prepareDraw();
// draw the core line
gl.bindTexture(gl.getDefaultTexture());
gl.enableVertexAttribArray(OpenGL::ATTRIB_POS);
gl.setVertexAttribArray(OpenGL::ATTRIB_POS, 2, GL_FLOAT, 0, (GLvoid *) vertices);
glDrawArrays(draw_mode, 0, vertex_count);
gl.setVertexAttribArray(OpenGL::ATTRIB_POS, 2, GL_FLOAT, 0, vertices);
if (use_quad_indices)
glDrawElements(draw_mode, (vertex_count / 4) * 6, GL_UNSIGNED_SHORT, indices);
else
glDrawArrays(draw_mode, 0, vertex_count);
if (overdraw)
{
@@ -344,10 +384,13 @@ void Polyline::draw()
gl.enableVertexAttribArray(OpenGL::ATTRIB_COLOR);
gl.setVertexAttribArray(OpenGL::ATTRIB_POS, 2, GL_FLOAT, 0, (GLvoid *) overdraw);
gl.setVertexAttribArray(OpenGL::ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, 0, (GLvoid *) colors);
gl.setVertexAttribArray(OpenGL::ATTRIB_POS, 2, GL_FLOAT, 0, overdraw);
gl.setVertexAttribArray(OpenGL::ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, 0, colors);
glDrawArrays(draw_mode, 0, overdraw_vertex_count);
if (use_quad_indices)
glDrawElements(draw_mode, (overdraw_vertex_count / 4) * 6, GL_UNSIGNED_SHORT, indices);
else
glDrawArrays(draw_mode, 0, overdraw_vertex_count);
gl.disableVertexAttribArray(OpenGL::ATTRIB_COLOR);
gl.setColor(c);
@@ -356,6 +399,8 @@ void Polyline::draw()
}
gl.disableVertexAttribArray(OpenGL::ATTRIB_POS);
delete[] indices;
}
void Polyline::fill_color_array(Color *colors, const Color &c)
@@ -24,6 +24,7 @@
#include <vector>
// LOVE
#include "common/config.h"
#include "common/Vector.h"
// OpenGL
@@ -43,13 +44,8 @@ namespace opengl
class Polyline
{
public:
Polyline(GLenum mode = GL_TRIANGLE_STRIP)
: vertices(NULL)
, overdraw(NULL)
, vertex_count(0)
, overdraw_vertex_count(0)
, draw_mode(mode)
{}
Polyline(GLenum mode = GL_TRIANGLE_STRIP, bool quadindices = false);
virtual ~Polyline();
/**
@@ -90,6 +86,7 @@ protected:
size_t vertex_count;
size_t overdraw_vertex_count;
GLenum draw_mode;
bool use_quad_indices;
}; // Polyline
@@ -102,8 +99,8 @@ class NoneJoinPolyline : public Polyline
{
public:
NoneJoinPolyline()
// TODO: replace GL_QUADS (indexed triangles?)
: Polyline(GL_QUADS)
// Draw quads using GL_TRIANGLES and a vertex index array.
: Polyline(GL_TRIANGLES, true)
{}
void render(const float *vertices, size_t count, float halfwidth, float pixel_size, bool draw_overdraw)
+18 -19
View File
@@ -100,8 +100,8 @@ Shader::~Shader()
if (current == this)
detach();
for (auto it = boundRetainables.begin(); it != boundRetainables.end(); ++it)
it->second->release();
for (const auto &retainable : boundRetainables)
retainable.second->release();
boundRetainables.clear();
@@ -181,9 +181,8 @@ void Shader::createProgram(const std::vector<GLuint> &shaderids)
if (program == 0)
throw love::Exception("Cannot create shader program object.");
std::vector<GLuint>::const_iterator it;
for (it = shaderids.begin(); it != shaderids.end(); ++it)
glAttachShader(program, *it);
for (GLuint id : shaderids)
glAttachShader(program, id);
// Bind generic vertex attribute indices to names in the shader.
for (int i = 0; i < int(OpenGL::ATTRIB_MAX_ENUM); i++)
@@ -208,8 +207,8 @@ void Shader::createProgram(const std::vector<GLuint> &shaderids)
glLinkProgram(program);
// flag shaders for auto-deletion when the program object is deleted.
for (it = shaderids.begin(); it != shaderids.end(); ++it)
glDeleteShader(*it);
for (GLuint id : shaderids)
glDeleteShader(id);
GLint status;
glGetProgramiv(program, GL_LINK_STATUS, &status);
@@ -272,6 +271,10 @@ void Shader::mapActiveUniforms()
bool Shader::loadVolatile()
{
// Creating the shader will invalidate the uniforms that rely on these.
lastCanvas = (Canvas *) -1;
lastViewport = OpenGL::Viewport();
// zero out active texture list
activeTexUnits.clear();
activeTexUnits.insert(activeTexUnits.begin(), maxTexUnits, 0);
@@ -282,10 +285,9 @@ bool Shader::loadVolatile()
std::vector<GLuint> shaderids;
ShaderSources::const_iterator source;
for (source = shaderSources.begin(); source != shaderSources.end(); ++source)
for (const auto &source : shaderSources)
{
GLuint shaderid = compileCode(source->first, source->second);
GLuint shaderid = compileCode(source.first, source.second);
shaderids.push_back(shaderid);
}
@@ -294,7 +296,7 @@ bool Shader::loadVolatile()
{
ShaderSources &defaults = defaultCode[Graphics::RENDERER_OPENGLES];
source = shaderSources.find(TYPE_VERTEX);
ShaderSources::const_iterator source = shaderSources.find(TYPE_VERTEX);
if (source == shaderSources.end())
shaderids.push_back(compileCode(TYPE_VERTEX, defaults[TYPE_VERTEX]));
@@ -325,6 +327,7 @@ bool Shader::loadVolatile()
// make sure glUseProgram gets called.
current = nullptr;
attach();
checkSetScreenParams();
}
return true;
@@ -385,11 +388,10 @@ std::string Shader::getWarnings() const
const char *typestr;
// Get the individual shader stage warnings
std::map<ShaderType, std::string>::const_iterator it;
for (it = shaderWarnings.begin(); it != shaderWarnings.end(); ++it)
for (const auto &warning : shaderWarnings)
{
if (typeNames.find(it->first, typestr))
warnings += std::string(typestr) + std::string(" shader:\n") + it->second;
if (typeNames.find(warning.first, typestr))
warnings += std::string(typestr) + std::string(" shader:\n") + warning.second;
}
warnings += getProgramWarnings();
@@ -403,12 +405,9 @@ void Shader::attach(bool temporary)
if (oldshader != this)
{
glUseProgram(program);
current = this;
current->retain();
if (oldshader != nullptr)
oldshader->release();
// retain/release happens in Graphics::setShader.
}
if (!temporary)
@@ -46,8 +46,10 @@ SpriteBatch::SpriteBatch(Texture *texture, int size, int usage)
, size(size)
, next(0)
, color(0)
, array_buf(0)
, element_buf(0)
, array_buf(nullptr)
, element_buf(nullptr)
, buffer_used_offset(0)
, buffer_used_size(0)
{
if (size <= 0)
throw love::Exception("Invalid SpriteBatch size.");
@@ -86,14 +88,10 @@ SpriteBatch::SpriteBatch(Texture *texture, int size, int usage)
delete element_buf;
throw love::Exception("Out of memory.");
}
texture->retain();
}
SpriteBatch::~SpriteBatch()
{
texture->release();
delete color;
delete array_buf;
delete element_buf;
@@ -160,31 +158,22 @@ void SpriteBatch::clear()
next = 0;
}
void *SpriteBatch::lock()
void SpriteBatch::flush()
{
VertexBuffer::Bind bind(*array_buf);
array_buf->unmap(buffer_used_offset, buffer_used_size);
return array_buf->map();
}
void SpriteBatch::unlock()
{
VertexBuffer::Bind bind(*array_buf);
array_buf->unmap();
buffer_used_offset = buffer_used_size = 0;
}
void SpriteBatch::setTexture(Texture *newtexture)
{
Object::AutoRelease imagerelease(texture);
newtexture->retain();
texture = newtexture;
texture.set(newtexture);
}
Texture *SpriteBatch::getTexture()
{
return texture;
return texture.get();
}
void SpriteBatch::setColor(const Color &color)
@@ -220,33 +209,31 @@ void SpriteBatch::setBufferSize(int newsize)
return;
// Map (lock) the old VertexBuffer to get a pointer to its data.
void *old_data = lock();
void *old_data = nullptr;
{
VertexBuffer::Bind bind(*array_buf);
old_data = array_buf->map();
}
size_t vertex_size = sizeof(Vertex) * 4 * newsize;
VertexBuffer *new_array_buf = 0;
VertexIndex *new_element_buf = 0;
void *new_data = 0;
VertexBuffer *new_array_buf = nullptr;
VertexIndex *new_element_buf = nullptr;
try
{
new_array_buf = VertexBuffer::Create(vertex_size, array_buf->getTarget(), array_buf->getUsage());
new_element_buf = new VertexIndex(newsize);
// VBO::map can throw an exception. Also we want to scope the bind.
VertexBuffer::Bind bind(*new_array_buf);
new_data = new_array_buf->map();
}
catch (love::Exception &)
{
delete new_array_buf;
delete new_element_buf;
unlock();
throw;
}
// Copy as much of the old data into the new VertexBuffer as can fit.
memcpy(new_data, old_data, sizeof(Vertex) * 4 * std::min(newsize, size));
new_array_buf->fill(0, sizeof(Vertex) * 4 * std::min(newsize, size), old_data);
// We don't need to unmap the old VertexBuffer since we're deleting it.
delete array_buf;
@@ -258,8 +245,8 @@ void SpriteBatch::setBufferSize(int newsize)
next = std::min(next, newsize);
// But we should unmap (unlock) the new one!
unlock();
// The new VertexBuffer isn't mapped, so we should reset these variables.
buffer_used_offset = buffer_used_size = 0;
}
int SpriteBatch::getBufferSize() const
@@ -269,29 +256,27 @@ int SpriteBatch::getBufferSize() const
void SpriteBatch::draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky)
{
const size_t vertex_offset = offsetof(Vertex, x);
const size_t pos_offset = offsetof(Vertex, x);
const size_t texel_offset = offsetof(Vertex, s);
const size_t color_offset = offsetof(Vertex, r);
if (next == 0)
return;
Matrix t;
static Matrix t;
t.setTransformation(x, y, angle, sx, sy, ox, oy, kx, ky);
gl.matrices.transform.push(gl.matrices.transform.top());
gl.matrices.transform.top() *= t;
OpenGL::TempTransform transform(gl);
transform.get() *= t;
texture->predraw();
VertexBuffer::Bind array_bind(*array_buf);
VertexBuffer::Bind element_bind(*element_buf->getVertexBuffer());
gl.enableVertexAttribArray(OpenGL::ATTRIB_POS);
gl.enableVertexAttribArray(OpenGL::ATTRIB_TEXCOORD);
gl.setVertexAttribArray(OpenGL::ATTRIB_POS, 2, GL_FLOAT, sizeof(Vertex), array_buf->getPointer(vertex_offset));
gl.setVertexAttribArray(OpenGL::ATTRIB_TEXCOORD, 2, GL_FLOAT, sizeof(Vertex), array_buf->getPointer(texel_offset));
// Make sure the VBO isn't mapped when we draw (sends data to GPU if needed.)
array_buf->unmap(buffer_used_offset, buffer_used_size);
buffer_used_offset = buffer_used_size = 0;
Color curcolor = gl.getColor();
@@ -302,6 +287,12 @@ void SpriteBatch::draw(float x, float y, float angle, float sx, float sy, float
gl.setVertexAttribArray(OpenGL::ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, sizeof(Vertex), array_buf->getPointer(color_offset));
}
gl.enableVertexAttribArray(OpenGL::ATTRIB_POS);
gl.setVertexAttribArray(OpenGL::ATTRIB_POS, 2, GL_FLOAT, sizeof(Vertex), array_buf->getPointer(pos_offset));
gl.enableVertexAttribArray(OpenGL::ATTRIB_TEXCOORD);
gl.setVertexAttribArray(OpenGL::ATTRIB_TEXCOORD, 2, GL_FLOAT, sizeof(Vertex), array_buf->getPointer(texel_offset));
gl.prepareDraw();
glDrawElements(GL_TRIANGLES, element_buf->getIndexCount(next), element_buf->getType(), element_buf->getPointer(0));
@@ -315,15 +306,22 @@ void SpriteBatch::draw(float x, float y, float angle, float sx, float sy, float
}
texture->postdraw();
gl.matrices.transform.pop();
}
void SpriteBatch::addv(const Vertex *v, int index)
{
static const int sprite_size = 4 * sizeof(Vertex); // bytecount
static const size_t sprite_size = 4 * sizeof(Vertex); // bytecount
VertexBuffer::Bind bind(*array_buf);
// Always keep the VBO mapped when adding data for now (it'll be unmapped
// on draw.)
array_buf->map();
array_buf->fill(index * sprite_size, sprite_size, v);
buffer_used_offset = std::min(buffer_used_offset, index * sprite_size);
buffer_used_size = std::max(buffer_used_size, (index + 1) * sprite_size - buffer_used_offset);
}
void SpriteBatch::setColorv(Vertex *v, const Color &color)
@@ -52,7 +52,7 @@ public:
enum UsageHint
{
USAGE_DYNAMIC = 1,
USAGE_DYNAMIC,
USAGE_STATIC,
USAGE_STREAM,
USAGE_MAX_ENUM
@@ -65,8 +65,7 @@ public:
int addq(Quad *quad, float x, float y, float a, float sx, float sy, float ox, float oy, float kx, float ky, int index = -1);
void clear();
void *lock();
void unlock();
void flush();
void setTexture(Texture *newtexture);
Texture *getTexture();
@@ -127,7 +126,7 @@ private:
*/
void setColorv(Vertex *v, const Color &color);
Texture *texture;
Object::StrongRef<Texture> texture;
// Max number of sprites in the batch.
int size;
@@ -142,6 +141,10 @@ private:
VertexBuffer *array_buf;
VertexIndex *element_buf;
// The portion of the vertex buffer that's been modified while mapped.
size_t buffer_used_offset;
size_t buffer_used_size;
static StringMap<UsageHint, USAGE_MAX_ENUM>::Entry usageHintEntries[];
static StringMap<UsageHint, USAGE_MAX_ENUM> usageHints;
@@ -55,6 +55,14 @@ public:
**/
virtual void postdraw() {}
/**
* Unextended OpenGL ES 2 doesn't support non-clamp wrap modes or mipmapping
* with non-power-of-two textures.
**/
static bool hasLimitedNpot()
{
return GLAD_ES_VERSION_2_0 && !(GLAD_ES_VERSION_3_0 || GLAD_OES_texture_npot);
}
}; // Texture
@@ -21,7 +21,6 @@
#include "VertexBuffer.h"
#include "common/Exception.h"
#include "common/config.h"
#include <cstdlib>
#include <cstring>
@@ -89,7 +88,7 @@ void *VertexArray::map()
return buf;
}
void VertexArray::unmap()
void VertexArray::unmap(size_t /*usedOffset*/, size_t /*usedSize*/)
{
is_mapped = false;
}
@@ -121,7 +120,7 @@ VBO::VBO(size_t size, GLenum target, GLenum usage, MemoryBacking backing)
// ES2 can't do glGetBufferSubData.
: VertexBuffer(size, target, usage, GLAD_ES_VERSION_2_0 ? BACKING_FULL : backing)
, vbo(0)
, memory_map(0)
, memory_map(nullptr)
, is_dirty(false)
{
if (!(GLAD_ARB_vertex_buffer_object || GLAD_VERSION_1_5 || GLAD_ES_VERSION_2_0))
@@ -133,7 +132,7 @@ VBO::VBO(size_t size, GLenum target, GLenum usage, MemoryBacking backing)
backing = BACKING_FULL;
if (getMemoryBacking() == BACKING_FULL)
memory_map = malloc(getSize());
memory_map = (char *) malloc(getSize());
bool ok = load(false);
@@ -160,7 +159,7 @@ void *VBO::map()
if (!memory_map)
{
memory_map = malloc(getSize());
memory_map = (char * ) malloc(getSize());
if (!memory_map)
throw love::Exception("Out of memory (oh the humanity!)");
}
@@ -176,11 +175,28 @@ void *VBO::map()
return memory_map;
}
void VBO::unmap()
void VBO::unmapStatic(size_t offset, size_t size)
{
// Upload the mapped data to the buffer.
glBufferSubData(getTarget(), (GLintptr) offset, (GLsizeiptr) size, memory_map + offset);
}
void VBO::unmapStream()
{
// "orphan" current buffer to avoid implicit synchronisation on the GPU:
// http://www.seas.upenn.edu/~pcozzi/OpenGLInsights/OpenGLInsights-AsynchronousBufferTransfers.pdf
glBufferData(getTarget(), (GLsizeiptr) getSize(), nullptr, getUsage());
glBufferData(getTarget(), (GLsizeiptr) getSize(), memory_map, getUsage());
}
void VBO::unmap(size_t usedOffset, size_t usedSize)
{
if (!is_mapped)
return;
usedOffset = std::min(usedOffset, getSize());
usedSize = std::min(usedSize, getSize() - usedOffset);
// VBO::bind is a no-op when the VBO is mapped, so we have to make sure it's
// bound here.
if (!is_bound)
@@ -189,17 +205,23 @@ void VBO::unmap()
is_bound = true;
}
if (getUsage() == GL_STATIC_DRAW)
switch (getUsage())
{
// Upload the mapped data to the buffer.
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());
case GL_STATIC_DRAW:
unmapStatic(usedOffset, usedSize);
break;
case GL_STREAM_DRAW:
unmapStream();
break;
case GL_DYNAMIC_DRAW:
default:
// It's probably more efficient to treat it like a streaming buffer if
// more than a third of its contents have been modified during the map().
if (usedSize >= getSize() / 3)
unmapStream();
else
unmapStatic(usedOffset, usedSize);
break;
}
is_mapped = false;
@@ -225,31 +247,11 @@ void VBO::unbind()
void VBO::fill(size_t offset, size_t size, const void *data)
{
if (is_mapped || getMemoryBacking() == BACKING_FULL)
memcpy(static_cast<char *>(memory_map) + offset, data, size);
memcpy(memory_map + offset, data, size);
if (!is_mapped)
{
// Not all systems have access to some faster paths...
if (GLAD_APPLE_flush_buffer_range)
{
void *mapdata = glMapBuffer(getTarget(), GL_WRITE_ONLY);
if (mapdata)
{
// We specified in VBO::load that we'll do manual flushing.
// Now we tell the driver it only needs to deal with the data
// we changed.
memcpy(static_cast<char *>(mapdata) + offset, data, size);
glFlushMappedBufferRangeAPPLE(getTarget(), (GLintptr) offset, (GLsizei) size);
}
glUnmapBuffer(getTarget());
}
else
{
// Fall back to a possibly slower SubData (more chance of syncing.)
glBufferSubData(getTarget(), (GLintptr) offset, (GLsizeiptr) size, data);
}
glBufferSubData(getTarget(), (GLintptr) offset, (GLsizeiptr) size, data);
if (getMemoryBacking() != BACKING_FULL)
is_dirty = true;
@@ -278,17 +280,11 @@ bool VBO::load(bool restore)
VertexBuffer::Bind bind(*this);
// Copy the old buffer only if 'restore' was requested.
const GLvoid *src = restore ? memory_map : 0;
const GLvoid *src = restore ? memory_map : nullptr;
while (GL_NO_ERROR != glGetError())
/* clear error messages */;
// We don't want to flush the entire buffer when we just modify a small
// portion of it (VBO::fill without VBO::map), so we'll handle the flushing
// ourselves when we can.
if (GLAD_APPLE_flush_buffer_range)
glBufferParameteriAPPLE(getTarget(), GL_BUFFER_FLUSHING_UNMAP_APPLE, GL_FALSE);
// Note that if 'src' is '0', no data will be copied.
glBufferData(getTarget(), (GLsizeiptr) getSize(), src, getUsage());
GLenum err = glGetError();
@@ -302,13 +298,11 @@ void VBO::unload(bool save)
if (save && getMemoryBacking() == BACKING_PARTIAL)
{
VertexBuffer::Bind bind(*this);
bool mapped = is_mapped;
map(); // saves buffer content to memory_map.
is_mapped = mapped;
}
is_mapped = false;
glDeleteBuffers(1, &vbo);
vbo = 0;
}
@@ -23,11 +23,15 @@
#define LOVE_GRAPHICS_OPENGL_VERTEX_BUFFER_H
// LOVE
#include "common/config.h"
#include "graphics/Volatile.h"
// OpenGL
#include "OpenGL.h"
// C
#include <stddef.h>
namespace love
{
namespace graphics
@@ -152,8 +156,12 @@ public:
* when used to draw elements.
*
* The VertexBuffer must be bound to use this function.
*
* @param usedOffset The offset into the mapped buffer indicating the
* sub-range of data modified. Optional.
* @param usedSize The size of the sub-range of modified data. Optional.
*/
virtual void unmap() = 0;
virtual void unmap(size_t usedOffset = 0, size_t usedSize = -1) = 0;
/**
* Bind the VertexBuffer to its specified target.
@@ -294,7 +302,7 @@ public:
// Implements VertexBuffer.
virtual void *map();
virtual void unmap();
virtual void unmap(size_t usedOffset = 0, size_t usedSize = -1);
virtual void bind();
virtual void unbind();
virtual void fill(size_t offset, size_t size, const void *data);
@@ -328,7 +336,7 @@ public:
// Implements VertexBuffer.
virtual void *map();
virtual void unmap();
virtual void unmap(size_t usedOffset = 0, size_t usedSize = -1);
virtual void bind();
virtual void unbind();
virtual void fill(size_t offset, size_t size, const void *data);
@@ -355,12 +363,15 @@ private:
*/
void unload(bool save);
void unmapStatic(size_t offset, size_t size);
void unmapStream();
// The VBO identifier. Assigned by OpenGL.
GLuint vbo;
// A pointer to mapped memory. Will be inialized on the first
// call to map().
void *memory_map;
char *memory_map;
// Set if the buffer was modified while operating on gpu memory
// and needs to be synchronized.
@@ -19,6 +19,7 @@
**/
#include "wrap_Canvas.h"
#include "Graphics.h"
namespace love
{
@@ -37,18 +38,26 @@ int w_Canvas_renderTo(lua_State *L)
Canvas *canvas = luax_checkcanvas(L, 1);
luaL_checktype(L, 2, LUA_TFUNCTION);
// Save the current Canvas so we can restore it when we're done.
Canvas *oldcanvas = Canvas::current;
Graphics *graphics = Module::getInstance<Graphics>(Module::M_GRAPHICS);
EXCEPT_GUARD(canvas->startGrab();)
if (graphics)
{
// Save the current Canvas so we can restore it when we're done.
std::vector<Canvas *> oldcanvases = graphics->getCanvas();
lua_settop(L, 2); // make sure the function is on top of the stack
lua_call(L, 0, 0);
for (Canvas *c : oldcanvases)
c->retain();
if (oldcanvas != nullptr)
oldcanvas->startGrab(oldcanvas->getAttachedCanvases());
else
Canvas::bindDefaultCanvas();
luax_catchexcept(L, [&](){ graphics->setCanvas(canvas); });
lua_settop(L, 2); // make sure the function is on top of the stack
lua_call(L, 0, 0);
graphics->setCanvas(oldcanvases);
for (Canvas *c : oldcanvases)
c->release();
}
return 0;
}
@@ -59,6 +68,7 @@ int w_Canvas_getImageData(lua_State *L)
love::image::Image *image = luax_getmodule<love::image::Image>(L, "image", MODULE_IMAGE_T);
love::image::ImageData *img = canvas->getImageData(image);
luax_pushtype(L, "ImageData", IMAGE_IMAGE_DATA_T, img);
img->release();
return 1;
}
@@ -69,7 +79,7 @@ int w_Canvas_getPixel(lua_State * L)
int y = luaL_checkint(L, 3);
unsigned char c[4];
EXCEPT_GUARD(canvas->getPixel(c, x, y);)
luax_catchexcept(L, [&](){ canvas->getPixel(c, x, y); });
lua_pushnumber(L, c[0]);
lua_pushnumber(L, c[1]);
@@ -122,10 +132,10 @@ int w_Canvas_getFormat(lua_State *L)
return 1;
}
int w_Canvas_getFSAA(lua_State *L)
int w_Canvas_getMSAA(lua_State *L)
{
Canvas *canvas = luax_checkcanvas(L, 1);
lua_pushinteger(L, canvas->getFSAA());
lua_pushinteger(L, canvas->getMSAA());
return 1;
}
@@ -145,10 +155,14 @@ static const luaL_Reg functions[] =
{ "getPixel", w_Canvas_getPixel },
{ "clear", w_Canvas_clear },
{ "getFormat", w_Canvas_getFormat },
{ "getFSAA", w_Canvas_getFSAA },
{ "getMSAA", w_Canvas_getMSAA },
// Deprecated since 0.9.1.
{ "getType", w_Canvas_getFormat },
// Deprecated since 0.9.2.
{ "getFSAA", w_Canvas_getMSAA },
{ 0, 0 }
};
@@ -40,7 +40,7 @@ int w_Canvas_getImageData(lua_State *L);
int w_Canvas_getPixel(lua_State * L);
int w_Canvas_clear(lua_State *L);
int w_Canvas_getFormat(lua_State *L);
int w_Canvas_getFSAA(lua_State *L);
int w_Canvas_getMSAA(lua_State *L);
extern "C" int luaopen_canvas(lua_State *L);
} // opengl
@@ -45,7 +45,7 @@ int w_Font_getWidth(lua_State *L)
Font *t = luax_checkfont(L, 1);
const char *str = luaL_checkstring(L, 2);
EXCEPT_GUARD(lua_pushinteger(L, t->getWidth(str));)
luax_catchexcept(L, [&](){ lua_pushinteger(L, t->getWidth(str)); });
return 1;
}
@@ -56,10 +56,10 @@ int w_Font_getWrap(lua_State *L)
float wrap = (float) luaL_checknumber(L, 3);
int max_width = 0, numlines = 0;
EXCEPT_GUARD(
luax_catchexcept(L, [&]() {
std::vector<std::string> lines = t->getWrap(str, wrap, &max_width);
numlines = lines.size();
)
});
lua_pushinteger(L, max_width);
lua_pushinteger(L, numlines);
@@ -96,7 +96,7 @@ int w_Font_setFilter(lua_State *L)
f.anisotropy = (float) luaL_optnumber(L, 4, 1.0);
EXCEPT_GUARD(t->setFilter(f);)
luax_catchexcept(L, [&](){ t->setFilter(f); });
return 0;
}
@@ -143,7 +143,7 @@ int w_Font_hasGlyphs(lua_State *L)
int count = lua_gettop(L) - 1;
count = count < 1 ? 1 : count;
EXCEPT_GUARD(
luax_catchexcept(L, [&]() {
for (int i = 2; i < count + 2; i++)
{
if (lua_type(L, i) == LUA_TSTRING)
@@ -154,7 +154,7 @@ int w_Font_hasGlyphs(lua_State *L)
if (!hasglyph)
break;
}
)
});
luax_pushboolean(L, hasglyph);
return 1;
File diff suppressed because it is too large Load Diff
@@ -95,7 +95,8 @@ int w_setShader(lua_State *L);
int w_getShader(lua_State *L);
int w_setDefaultShaderCode(lua_State *L);
int w_isSupported(lua_State *L);
int w_hasCanvasFormat(lua_State *L);
int w_getCanvasFormats(lua_State *L);
int w_getCompressedImageFormats(lua_State *L);
int w_getRendererInfo(lua_State *L);
int w_getSystemLimit(lua_State *L);
int w_draw(lua_State *L);
@@ -47,7 +47,7 @@ int w_Image_setMipmapFilter(lua_State *L)
return luaL_error(L, "Invalid filter mode: %s", mipmapstr);
}
EXCEPT_GUARD(t->setFilter(f);)
luax_catchexcept(L, [&](){ t->setFilter(f); });
float sharpness = (float) luaL_optnumber(L, 3, 0);
t->setMipmapSharpness(sharpness);
@@ -81,7 +81,7 @@ int w_Image_isCompressed(lua_State *L)
int w_Image_refresh(lua_State *L)
{
Image *i = luax_checkimage(L, 1);
EXCEPT_GUARD(i->refresh();)
luax_catchexcept(L, [&](){ i->refresh(); });
return 0;
}
@@ -93,10 +93,7 @@ int w_Image_getData(lua_State *L)
{
love::image::CompressedData *t = i->getCompressedData();
if (t)
{
t->retain();
luax_pushtype(L, "CompressedData", IMAGE_COMPRESSED_DATA_T, t);
}
else
lua_pushnil(L);
}
@@ -104,10 +101,7 @@ int w_Image_getData(lua_State *L)
{
love::image::ImageData *t = i->getImageData();
if (t)
{
t->retain();
luax_pushtype(L, "ImageData", IMAGE_IMAGE_DATA_T, t);
}
else
lua_pushnil(L);
}
@@ -74,7 +74,7 @@ int w_Mesh_setVertex(lua_State *L)
v.a = luaL_optinteger(L, 10, 255);
}
EXCEPT_GUARD(t->setVertex(i, v);)
luax_catchexcept(L, [&](){ t->setVertex(i, v); });
return 0;
}
@@ -84,7 +84,7 @@ int w_Mesh_getVertex(lua_State *L)
size_t i = (size_t) (luaL_checkinteger(L, 2) - 1);
Vertex v;
EXCEPT_GUARD(v = t->getVertex(i);)
luax_catchexcept(L, [&](){ v = t->getVertex(i); });
lua_pushnumber(L, v.x);
lua_pushnumber(L, v.y);
@@ -134,7 +134,7 @@ int w_Mesh_setVertices(lua_State *L)
vertices.push_back(v);
}
EXCEPT_GUARD(t->setVertices(vertices);)
luax_catchexcept(L, [&](){ t->setVertices(vertices); });
return 0;
}
@@ -216,7 +216,7 @@ int w_Mesh_setVertexMap(lua_State *L)
vertexmap.push_back(uint32(luaL_checkinteger(L, i + 2) - 1));
}
EXCEPT_GUARD(t->setVertexMap(vertexmap);)
luax_catchexcept(L, [&](){ t->setVertexMap(vertexmap); });
return 0;
}
@@ -225,7 +225,7 @@ int w_Mesh_getVertexMap(lua_State *L)
Mesh *t = luax_checkmesh(L, 1);
std::vector<uint32> vertex_map;
EXCEPT_GUARD(t->getVertexMap(vertex_map);)
luax_catchexcept(L, [&](){ t->getVertexMap(vertex_map); });
size_t element_count = vertex_map.size();
@@ -240,20 +240,6 @@ int w_Mesh_getVertexMap(lua_State *L)
return 1;
}
int w_Mesh_setInstanceCount(lua_State *L)
{
Mesh *t = luax_checkmesh(L, 1);
t->setInstanceCount(luaL_checkint(L, 2));
return 0;
}
int w_Mesh_getInstanceCount(lua_State *L)
{
Mesh *t = luax_checkmesh(L, 1);
lua_pushinteger(L, t->getInstanceCount());
return 1;
}
int w_Mesh_setTexture(lua_State *L)
{
Mesh *t = luax_checkmesh(L, 1);
@@ -277,18 +263,13 @@ int w_Mesh_getTexture(lua_State *L)
if (tex == nullptr)
return 0;
tex->retain();
// FIXME: big hack right here.
if (typeid(*tex) == typeid(Image))
luax_pushtype(L, "Image", GRAPHICS_IMAGE_T, tex);
else if (typeid(*tex) == typeid(Canvas))
luax_pushtype(L, "Canvas", GRAPHICS_CANVAS_T, tex);
else
{
tex->release();
return luaL_error(L, "Unable to determine texture type.");
}
return 1;
}
@@ -329,7 +310,7 @@ int w_Mesh_setDrawRange(lua_State *L)
{
int rangemin = luaL_checkint(L, 2) - 1;
int rangemax = luaL_checkint(L, 3) - 1;
EXCEPT_GUARD(t->setDrawRange(rangemin, rangemax);)
luax_catchexcept(L, [&](){ t->setDrawRange(rangemin, rangemax); });
}
return 0;
@@ -374,12 +355,6 @@ static const luaL_Reg functions[] =
{ "getVertexCount", w_Mesh_getVertexCount },
{ "setVertexMap", w_Mesh_setVertexMap },
{ "getVertexMap", w_Mesh_getVertexMap },
// Disabled for now, since implementation is incomplete and might change
// if/when VertexBuffers / custom vertex attributes are added.
// { "setInstanceCount", w_Mesh_setInstanceCount },
// { "getInstanceCount", w_Mesh_getInstanceCount },
{ "setTexture", w_Mesh_setTexture },
{ "getTexture", w_Mesh_getTexture },
{ "setDrawMode", w_Mesh_setDrawMode },
@@ -41,8 +41,6 @@ int w_Mesh_getVertices(lua_State *L);
int w_Mesh_getVertexCount(lua_State *L);
int w_Mesh_setVertexMap(lua_State *L);
int w_Mesh_getVertexMap(lua_State *L);
int w_Mesh_setInstanceCount(lua_State *L);
int w_Mesh_getInstanceCount(lua_State *L);
int w_Mesh_setTexture(lua_State *L);
int w_Mesh_getTexture(lua_State *L);
int w_Mesh_setDrawMode(lua_State *L);
@@ -49,9 +49,10 @@ int w_ParticleSystem_clone(lua_State *L)
ParticleSystem *t = luax_checkparticlesystem(L, 1);
ParticleSystem *clone = nullptr;
EXCEPT_GUARD(clone = t->clone();)
luax_catchexcept(L, [&](){ clone = t->clone(); });
luax_pushtype(L, "ParticleSystem", GRAPHICS_PARTICLE_SYSTEM_T, clone);
clone->release();
return 1;
}
@@ -67,7 +68,6 @@ int w_ParticleSystem_getTexture(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
Texture *tex = t->getTexture();
tex->retain();
// FIXME: big hack right here.
if (typeid(*tex) == typeid(Image))
@@ -75,10 +75,7 @@ int w_ParticleSystem_getTexture(lua_State *L)
else if (typeid(*tex) == typeid(Canvas))
luax_pushtype(L, "Canvas", GRAPHICS_CANVAS_T, tex);
else
{
tex->release();
return luaL_error(L, "Unable to determine texture type.");
}
return 1;
}
@@ -90,7 +87,7 @@ int w_ParticleSystem_setBufferSize(lua_State *L)
if (arg1 < 1.0 || arg1 > ParticleSystem::MAX_PARTICLES)
return luaL_error(L, "Invalid buffer size");
EXCEPT_GUARD(t->setBufferSize((uint32) arg1);)
luax_catchexcept(L, [&](){ t->setBufferSize((uint32) arg1); });
return 0;
}
@@ -128,7 +125,7 @@ int w_ParticleSystem_setEmissionRate(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
float arg1 = (float) luaL_checknumber(L, 2);
EXCEPT_GUARD(t->setEmissionRate(arg1);)
luax_catchexcept(L, [&](){ t->setEmissionRate(arg1); });
return 0;
}
@@ -569,6 +566,52 @@ int w_ParticleSystem_getColors(lua_State *L)
return colors.size();
}
int w_ParticleSystem_setQuads(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
std::vector<Quad *> quads;
if (lua_istable(L, 2))
{
for (size_t i = 1; i <= lua_objlen(L, 2); i++)
{
lua_rawgeti(L, 2, i);
Quad *q = luax_checktype<Quad>(L, -1, "Quad", GRAPHICS_QUAD_T);
quads.push_back(q);
lua_pop(L, 1);
}
}
else
{
for (int i = 2; i <= lua_gettop(L); i++)
{
Quad *q = luax_checktype<Quad>(L, i, "Quad", GRAPHICS_QUAD_T);
quads.push_back(q);
}
}
t->setQuads(quads);
return 0;
}
int w_ParticleSystem_getQuads(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
const std::vector<Quad *> quads = t->getQuads();
lua_createtable(L, (int) quads.size(), 0);
for (size_t i = 0; i < quads.size(); i++)
{
luax_pushtype(L, "Quad", GRAPHICS_QUAD_T, quads[i]);
lua_rawseti(L, -2, i + 1);
}
return 1;
}
int w_ParticleSystem_setRelativeRotation(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
@@ -699,6 +742,8 @@ static const luaL_Reg functions[] =
{ "getSpinVariation", w_ParticleSystem_getSpinVariation },
{ "setColors", w_ParticleSystem_setColors },
{ "getColors", w_ParticleSystem_getColors },
{ "setQuads", w_ParticleSystem_setQuads },
{ "getQuads", w_ParticleSystem_getQuads },
{ "setOffset", w_ParticleSystem_setOffset },
{ "getOffset", w_ParticleSystem_getOffset },
{ "setRelativeRotation", w_ParticleSystem_setRelativeRotation },
@@ -75,6 +75,8 @@ int w_ParticleSystem_setSpinVariation(lua_State *L);
int w_ParticleSystem_getSpinVariation(lua_State *L);
int w_ParticleSystem_setColors(lua_State *L);
int w_ParticleSystem_getColors(lua_State *L);
int w_ParticleSystem_setQuads(lua_State *L);
int w_ParticleSystem_getQuads(lua_State *L);
int w_ParticleSystem_setOffset(lua_State *L);
int w_ParticleSystem_getOffset(lua_State *L);
int w_ParticleSystem_setRelativeRotation(lua_State *L);
@@ -232,22 +232,10 @@ int w_Shader_sendMatrix(lua_State *L)
lua_pop(L, 1 + dimension);
}
bool should_error = false;
try
{
shader->sendMatrix(name, dimension, values, count);
}
catch(love::Exception &e)
{
should_error = true;
lua_pushstring(L, e.what());
}
delete[] values;
if (should_error)
return luaL_error(L, "%s", lua_tostring(L, -1));
luax_catchexcept(L,
[&]() { shader->sendMatrix(name, dimension, values, count); },
[&]() { delete[] values; }
);
return 0;
}
@@ -258,7 +246,7 @@ int w_Shader_sendTexture(lua_State *L)
const char *name = luaL_checkstring(L, 2);
Texture *texture = luax_checktexture(L, 3);
EXCEPT_GUARD(shader->sendTexture(name, texture);)
luax_catchexcept(L, [&](){ shader->sendTexture(name, texture); });
return 0;
}
@@ -64,12 +64,12 @@ int w_SpriteBatch_add(lua_State *L)
float ky = (float) luaL_optnumber(L, startidx + 8, 0.0);
int id = 0;
EXCEPT_GUARD(
luax_catchexcept(L, [&]() {
if (quad)
id = t->addq(quad, x, y, a, sx, sy, ox, oy, kx, ky);
else
id = t->add(x, y, a, sx, sy, ox, oy, kx, ky);
)
});
lua_pushinteger(L, id);
return 1;
@@ -101,12 +101,12 @@ int w_SpriteBatch_set(lua_State *L)
float kx = (float) luaL_optnumber(L, startidx + 7, 0.0);
float ky = (float) luaL_optnumber(L, startidx + 8, 0.0);
EXCEPT_GUARD(
luax_catchexcept(L, [&]() {
if (quad)
t->addq(quad, x, y, a, sx, sy, ox, oy, kx, ky, id);
else
t->add(x, y, a, sx, sy, ox, oy, kx, ky, id);
)
});
return 0;
}
@@ -118,17 +118,16 @@ int w_SpriteBatch_clear(lua_State *L)
return 0;
}
int w_SpriteBatch_bind(lua_State *L)
int w_SpriteBatch_bind(lua_State* /*L*/)
{
SpriteBatch *t = luax_checkspritebatch(L, 1);
EXCEPT_GUARD(t->lock();)
// No-op (deprecated.)
return 0;
}
int w_SpriteBatch_unbind(lua_State *L)
int w_SpriteBatch_flush(lua_State *L)
{
SpriteBatch *t = luax_checkspritebatch(L, 1);
t->unlock();
t->flush();
return 0;
}
@@ -144,7 +143,6 @@ int w_SpriteBatch_getTexture(lua_State *L)
{
SpriteBatch *t = luax_checkspritebatch(L, 1);
Texture *tex = t->getTexture();
tex->retain();
// FIXME: big hack right here.
if (typeid(*tex) == typeid(Image))
@@ -152,10 +150,7 @@ int w_SpriteBatch_getTexture(lua_State *L)
else if (typeid(*tex) == typeid(Canvas))
luax_pushtype(L, "Canvas", GRAPHICS_CANVAS_T, tex);
else
{
tex->release();
return luaL_error(L, "Unable to determine texture type.");
}
return 1;
}
@@ -223,7 +218,7 @@ int w_SpriteBatch_setBufferSize(lua_State *L)
{
SpriteBatch *t = luax_checkspritebatch(L, 1);
int size = luaL_checkint(L, 2);
EXCEPT_GUARD(t->setBufferSize(size);)
luax_catchexcept(L, [&]() {t->setBufferSize(size); });
return 0;
}
@@ -239,8 +234,7 @@ static const luaL_Reg functions[] =
{ "add", w_SpriteBatch_add },
{ "set", w_SpriteBatch_set },
{ "clear", w_SpriteBatch_clear },
{ "bind", w_SpriteBatch_bind },
{ "unbind", w_SpriteBatch_unbind },
{ "flush", w_SpriteBatch_flush },
{ "setTexture", w_SpriteBatch_setTexture },
{ "getTexture", w_SpriteBatch_getTexture },
{ "setColor", w_SpriteBatch_setColor },
@@ -252,6 +246,11 @@ static const luaL_Reg functions[] =
// Deprecated since 0.9.1.
{ "setImage", w_SpriteBatch_setTexture },
{ "getImage", w_SpriteBatch_getTexture },
// Deprecated since 0.9.2.
{ "bind", w_SpriteBatch_bind },
{ "unbind", w_SpriteBatch_flush },
{ 0, 0 }
};
@@ -38,7 +38,7 @@ int w_SpriteBatch_set(lua_State *L);
int w_SpriteBatch_setg(lua_State *L);
int w_SpriteBatch_clear(lua_State *L);
int w_SpriteBatch_bind(lua_State *L);
int w_SpriteBatch_unbind(lua_State *L);
int w_SpriteBatch_flush(lua_State *L);
int w_SpriteBatch_setTexture(lua_State *L);
int w_SpriteBatch_getTexture(lua_State *L);
int w_SpriteBatch_setColor(lua_State *L);
@@ -69,7 +69,7 @@ int w_Texture_setFilter(lua_State *L)
f.anisotropy = (float) luaL_optnumber(L, 4, 1.0);
EXCEPT_GUARD(t->setFilter(f);)
luax_catchexcept(L, [&](){ t->setFilter(f); });
return 0;
}
@@ -105,8 +105,8 @@ int w_Texture_setWrap(lua_State *L)
if (!Texture::getConstant(tstr, w.t))
return luaL_error(L, "Invalid wrap mode, %s", tstr);
t->setWrap(w);
return 0;
luax_pushboolean(L, t->setWrap(w));
return 1;
}
int w_Texture_getWrap(lua_State *L)
@@ -87,7 +87,7 @@ CompressedData::Format CompressedData::getFormat() const
void CompressedData::checkMipmapLevelExists(int miplevel) const
{
if (miplevel < 0 || miplevel >= (int) dataImages.size())
throw love::Exception("Mipmap level %d does not exist", miplevel);
throw love::Exception("Mipmap level %d does not exist", miplevel + 1);
}
bool CompressedData::getConstant(const char *in, CompressedData::Format &out)
+3 -3
View File
@@ -44,11 +44,11 @@ class Image : public Module
{
public:
/**
* Destructor.
**/
virtual ~Image() {}
// Implements Module.
virtual ModuleType getModuleType() const { return M_IMAGE; }
/**
* Creates new ImageData from FileData.
* @param data The FileData containing the encoded image data.
+1 -1
View File
@@ -49,7 +49,7 @@ public:
enum Format
{
FORMAT_TGA = 1,
FORMAT_TGA,
FORMAT_BMP,
FORMAT_JPG,
FORMAT_PNG,
@@ -20,6 +20,8 @@
#include "DevilHandler.h"
#ifndef LOVE_NO_DEVIL
// LOVE
#include "common/Exception.h"
#include "common/math.h"
@@ -226,3 +228,5 @@ DevilHandler::EncodedImage DevilHandler::encode(const DecodedImage &img, ImageDa
} // magpie
} // image
} // love
#endif // LOVE_NO_DEVIL
@@ -22,6 +22,10 @@
#define LOVE_IMAGE_MAGPIE_DEVIL_HANDLER_H
// LOVE
#include "common/config.h"
#ifndef LOVE_NO_DEVIL
#include "filesystem/FileData.h"
#include "FormatHandler.h"
#include "thread/threads.h"
@@ -61,4 +65,6 @@ private:
} // image
} // love
#endif // !LOVE_NO_DEVIL
#endif // LOVE_IMAGE_MAGPIE_DEVIL_HANDLER_H
@@ -57,6 +57,11 @@ FormatHandler::EncodedImage FormatHandler::encode(const DecodedImage& /*img*/, I
throw love::Exception("Image encoding is not implemented for this format backend.");
}
void FormatHandler::free(unsigned char *mem)
{
delete[] mem;
}
} // magpie
} // image
} // love
@@ -93,6 +93,11 @@ public:
**/
virtual EncodedImage encode(const DecodedImage &img, ImageData::Format format);
/**
* Frees memory allocated by the format handler.
**/
virtual void free(unsigned char *mem);
}; // FormatHandler
} // magpie
@@ -23,6 +23,9 @@
#include "ImageData.h"
#include "CompressedData.h"
#include "PNGHandler.h"
#include "STBHandler.h"
#include "JPEGHandler.h"
#include "DevilHandler.h"
#include "ddsHandler.h"
@@ -39,7 +42,16 @@ namespace magpie
Image::Image()
{
formatHandlers.push_back(new PNGHandler);
formatHandlers.push_back(new STBHandler);
#ifdef LOVE_TURBO_JPEG
formatHandlers.push_back(new JPEGHandler);
#endif
#ifndef LOVE_NO_DEVIL
formatHandlers.push_back(new DevilHandler);
#endif
compressedFormatHandlers.push_back(new DDSHandler);
compressedFormatHandlers.push_back(new PVRHandler);
+59 -33
View File
@@ -30,18 +30,20 @@ namespace magpie
ImageData::ImageData(std::list<FormatHandler *> formats, love::filesystem::FileData *data)
: formatHandlers(formats)
, decodeHandler(nullptr)
{
for (auto it = formatHandlers.begin(); it != formatHandlers.end(); ++it)
(*it)->retain();
for (FormatHandler *handler : formatHandlers)
handler->retain();
decode(data);
}
ImageData::ImageData(std::list<FormatHandler *> formats, int width, int height)
: formatHandlers(formats)
, decodeHandler(nullptr)
{
for (auto it = formatHandlers.begin(); it != formatHandlers.end(); ++it)
(*it)->retain();
for (FormatHandler *handler : formatHandlers)
handler->retain();
this->width = width;
this->height = height;
@@ -54,9 +56,10 @@ ImageData::ImageData(std::list<FormatHandler *> formats, int width, int height)
ImageData::ImageData(std::list<FormatHandler *> formats, int width, int height, void *data, bool own)
: formatHandlers(formats)
, decodeHandler(nullptr)
{
for (auto it = formatHandlers.begin(); it != formatHandlers.end(); ++it)
(*it)->retain();
for (FormatHandler *handler : formatHandlers)
handler->retain();
this->width = width;
this->height = height;
@@ -69,10 +72,13 @@ ImageData::ImageData(std::list<FormatHandler *> formats, int width, int height,
ImageData::~ImageData()
{
delete[] data;
if (decodeHandler)
decodeHandler->free(data);
else
delete[] data;
for (auto it = formatHandlers.begin(); it != formatHandlers.end(); ++it)
(*it)->release();
for (FormatHandler *handler : formatHandlers)
handler->release();
}
void ImageData::create(int width, int height, void *data)
@@ -88,23 +94,32 @@ void ImageData::create(int width, int height, void *data)
if (data)
memcpy(this->data, data, width*height*sizeof(pixel));
decodeHandler = nullptr;
}
void ImageData::decode(love::filesystem::FileData *data)
{
FormatHandler *decoder = nullptr;
FormatHandler::DecodedImage decodedimage;
for (auto it = formatHandlers.begin(); it != formatHandlers.end(); ++it)
for (FormatHandler *handler : formatHandlers)
{
if ((*it)->canDecode(data))
if (handler->canDecode(data))
{
decodedimage = (*it)->decode(data);
decoder = handler;
break;
}
}
if (decoder)
decodedimage = decoder->decode(data);
if (decodedimage.data == nullptr)
throw love::Exception("Could not decode image: unrecognized format.");
{
const char *ext = data->getExtension().c_str();
throw love::Exception("Could not decode to ImageData: unrecognized format (%s)", ext);
}
// The decoder *must* output a 32 bits-per-pixel image.
if (decodedimage.size != decodedimage.width*decodedimage.height*sizeof(pixel))
@@ -113,39 +128,50 @@ void ImageData::decode(love::filesystem::FileData *data)
throw love::Exception("Could not convert image!");
}
if (this->data)
// Clean up any old data.
if (decodeHandler)
decodeHandler->free(this->data);
else
delete[] this->data;
this->width = decodedimage.width;
this->height = decodedimage.height;
this->data = decodedimage.data;
decodeHandler = decoder;
}
void ImageData::encode(love::filesystem::File *f, ImageData::Format format)
{
FormatHandler *encoder = nullptr;
FormatHandler::EncodedImage encodedimage;
FormatHandler::DecodedImage rawimage;
rawimage.width = width;
rawimage.height = height;
rawimage.size = width*height*sizeof(pixel);
rawimage.data = data;
for (FormatHandler *handler : formatHandlers)
{
// We only need to lock this mutex when actually encoding the ImageData.
thread::Lock lock(mutex);
FormatHandler::DecodedImage rawimage;
rawimage.width = width;
rawimage.height = height;
rawimage.size = width*height*sizeof(pixel);
rawimage.data = data;
for (auto it = formatHandlers.begin(); it != formatHandlers.end(); ++it)
if (handler->canEncode(format))
{
if ((*it)->canEncode(format))
{
encodedimage = (*it)->encode(rawimage, format);
break;
}
encoder = handler;
break;
}
}
if (encodedimage.data == nullptr)
throw love::Exception("Image format has no suitable encoder.");
if (encoder != nullptr)
{
thread::Lock lock(mutex);
encodedimage = encoder->encode(rawimage, format);
}
if (encoder == nullptr || encodedimage.data == nullptr)
{
const char *fname = "unknown";
getConstant(format, fname);
throw love::Exception("no suitable image encoder for %s format.", fname);
}
try
@@ -156,11 +182,11 @@ void ImageData::encode(love::filesystem::File *f, ImageData::Format format)
}
catch (love::Exception &)
{
delete[] encodedimage.data;
encoder->free(encodedimage.data);
throw;
}
delete[] encodedimage.data;
encoder->free(encodedimage.data);
}
} // magpie
@@ -59,6 +59,10 @@ private:
// Image format handlers we can use for decoding and encoding.
std::list<FormatHandler *> formatHandlers;
// The format handler that was used to decode the ImageData. We need to know
// this so we can properly delete memory allocated by the decoder.
FormatHandler *decodeHandler;
}; // ImageData
} // magpie
@@ -0,0 +1,166 @@
/**
* 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 "JPEGHandler.h"
#ifdef LOVE_TURBO_JPEG
#include "common/Exception.h"
#include "common/math.h"
namespace love
{
namespace image
{
namespace magpie
{
JPEGHandler::JPEGHandler()
{
mutex = love::thread::newMutex();
decompressor = tjInitDecompress();
compressor = tjInitCompress();
}
JPEGHandler::~JPEGHandler()
{
delete mutex;
if (decompressor)
tjDestroy(decompressor);
if (compressor)
tjDestroy(compressor);
}
bool JPEGHandler::canDecode(love::filesystem::FileData *data)
{
if (!decompressor)
return false;
int w, h, subsamp;
int status = tjDecompressHeader2(decompressor,
(unsigned char *) data->getData(),
data->getSize(),
&w, &h, &subsamp);
return (status == 0);
}
bool JPEGHandler::canEncode(ImageData::Format format)
{
if (!compressor)
return false;
return format == ImageData::FORMAT_JPG;
}
FormatHandler::DecodedImage JPEGHandler::decode(love::filesystem::FileData *data)
{
if (!decompressor)
throw love::Exception("Could not decode jpeg image: %s", tjGetErrorStr());
DecodedImage img;
unsigned char *jpegdata = (unsigned char *) data->getData();
love::thread::Lock lock(mutex);
int unused;
int status = tjDecompressHeader2(decompressor,
jpegdata, data->getSize(),
&img.width, &img.height,
&unused);
if (status < 0)
throw love::Exception("Could not decode jpeg image: %s", tjGetErrorStr());
img.size = img.width * img.height * sizeof(pixel);
try
{
img.data = new unsigned char[img.size];
}
catch (std::bad_alloc &)
{
throw love::Exception("Out of memory.");
}
status = tjDecompress2(decompressor,
jpegdata, data->getSize(),
img.data, 0, 0, 0, TJPF_RGBA, 0);
if (status < 0)
{
delete[] img.data;
throw love::Exception("Could not decode jpeg image: %s", tjGetErrorStr());
}
return img;
}
FormatHandler::EncodedImage JPEGHandler::encode(const DecodedImage &img, ImageData::Format format)
{
if (!canEncode(format))
throw love::Exception("JPEG encoder cannot encode specified format.");
EncodedImage encodedimage;
love::thread::Lock lock(mutex);
unsigned long tjsize = tjBufSize(img.width, img.height, TJSAMP_444);
try
{
// We want to allocate the memory ourselves instead of letting
// TurboJPEG do it, so we can safely use delete[] in ImageData.cpp.
encodedimage.data = new unsigned char[tjsize];
}
catch (std::bad_alloc &)
{
throw love::Exception("Out of memory.");
}
unsigned long jpegSize = tjsize;
int status = tjCompress2(compressor,
img.data,
img.width, 0, img.height,
TJPF_RGBA,
&encodedimage.data, &jpegSize,
TJSAMP_444, COMPRESS_QUALITY, TJFLAG_NOREALLOC);
if (status < 0)
{
delete[] encodedimage.data;
throw love::Exception("Could not encode jpeg image: %s", tjGetErrorStr());
}
encodedimage.size = jpegSize;
return encodedimage;
}
} // magpie
} // image
} // love
#endif // LOVE_TURBO_JPEG
@@ -0,0 +1,81 @@
/**
* 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_JPEG_HANDLER_H
#define LOVE_IMAGE_MAGPIE_JPEG_HANDLER_H
// LOVE
#include "common/config.h"
#ifdef LOVE_TURBO_JPEG
#include "FormatHandler.h"
#include "thread/threads.h"
// libjpeg-turbo
#ifdef LOVE_MACOSX_USE_FRAMEWORKS
#include <jpeg-turbo/turbojpeg.h>
#else
#include <turbojpeg.h>
#endif
namespace love
{
namespace image
{
namespace magpie
{
/**
* Interface between ImageData and TurboJPEG.
**/
class JPEGHandler : public FormatHandler
{
public:
// Implements FormatHandler.
JPEGHandler();
virtual ~JPEGHandler();
virtual bool canDecode(love::filesystem::FileData *data);
virtual bool canEncode(ImageData::Format format);
virtual DecodedImage decode(love::filesystem::FileData *data);
virtual EncodedImage encode(const DecodedImage &img, ImageData::Format format);
private:
Mutex *mutex;
tjhandle decompressor;
tjhandle compressor;
static const int COMPRESS_QUALITY = 90;
}; // JPEGHandler
} // magpie
} // image
} // love
#endif // LOVE_TURBO_JPEG
#endif // LOVE_IMAGE_MAGPIE_JPEG_HANDLER_H
@@ -0,0 +1,216 @@
/**
* 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.
**/
#include "PNGHandler.h"
// LOVE
#include "common/Exception.h"
#include "common/math.h"
// LodePNG
#include "lodepng/lodepng.h"
// zlib
#include <zlib.h>
// C++
#include <algorithm>
namespace love
{
namespace image
{
namespace magpie
{
// Custom PNG decompression function for LodePNG, using zlib.
static unsigned zlibDecompress(unsigned char **out, size_t *outsize, const unsigned char *in,
size_t insize, const LodePNGDecompressSettings* /*settings*/)
{
int status = Z_OK;
uLongf outdataSize = insize;
size_t sizeMultiplier = 0;
unsigned char *outdata = nullptr;
while (true)
{
// Enough size to hold the decompressed data, hopefully.
outdataSize = insize << (++sizeMultiplier);
try
{
outdata = new unsigned char[outdataSize];
}
catch (std::bad_alloc &)
{
return 83; // "Memory allocation failed" error code for LodePNG.
}
// Use zlib to decompress the PNG data.
status = uncompress(outdata, &outdataSize, in, insize);
// If the out buffer was big enough, break out of the loop.
if (status != Z_BUF_ERROR)
break;
// Otherwise delete the out buffer and try again with a larger size...
delete[] outdata;
outdata = nullptr;
}
if (status != Z_OK)
{
delete[] outdata;
return 10000; // "Unknown error code" for LodePNG.
}
if (out != nullptr)
*out = outdata;
if (outsize != nullptr)
*outsize = outdataSize;
return 0; // Success.
}
// Custom PNG compression function for LodePNG, using zlib.
static unsigned zlibCompress(unsigned char **out, size_t *outsize, const unsigned char *in,
size_t insize, const LodePNGCompressSettings* /*settings*/)
{
// Get the maximum compressed size of the data.
uLongf outdataSize = compressBound(insize);
unsigned char *outdata = nullptr;
try
{
outdata = new unsigned char[outdataSize];
}
catch (std::bad_alloc &)
{
return 83; // "Memory allocation failed" error code for LodePNG.
}
// Use zlib to compress the PNG data.
int status = compress(outdata, &outdataSize, in, insize);
if (status != Z_OK)
{
delete[] outdata;
return 10000; // "Unknown error code" for LodePNG.
}
if (out != nullptr)
*out = outdata;
if (outsize != nullptr)
*outsize = (size_t) outdataSize;
return 0; // Success.
}
bool PNGHandler::canDecode(love::filesystem::FileData *data)
{
unsigned int width = 0, height = 0;
unsigned char *indata = (unsigned char *) data->getData();
size_t insize = data->getSize();
lodepng::State state;
unsigned status = lodepng_inspect(&width, &height, &state, indata, insize);
return status == 0 && width > 0 && height > 0;
}
bool PNGHandler::canEncode(ImageData::Format format)
{
return format == ImageData::FORMAT_PNG;
}
PNGHandler::DecodedImage PNGHandler::decode(love::filesystem::FileData *fdata)
{
unsigned int width = 0, height = 0;
unsigned char *indata = (unsigned char *) fdata->getData();
size_t insize = fdata->getSize();
DecodedImage img;
lodepng::State state;
state.info_raw.colortype = LCT_RGBA;
state.info_raw.bitdepth = 8;
state.decoder.zlibsettings.custom_zlib = zlibDecompress;
unsigned status = lodepng_decode(&img.data, &width, &height,
&state, indata, insize);
if (status != 0)
{
const char *err = lodepng_error_text(status);
throw love::Exception("Could not decode PNG image (%s)", err);
}
img.width = (int) width;
img.height = (int) height;
img.size = width * height * 4;
return img;
}
PNGHandler::EncodedImage PNGHandler::encode(const DecodedImage &img, ImageData::Format format)
{
if (format != ImageData::FORMAT_PNG)
throw love::Exception("PNG encoder cannot encode to non-PNG format.");
EncodedImage encimg;
lodepng::State state;
state.info_raw.colortype = LCT_RGBA;
state.info_raw.bitdepth = 8;
// TODO: support plain RGB (24-bit) encoding in the future?
state.info_png.color.colortype = LCT_RGBA;
state.info_png.color.bitdepth = 8;
state.encoder.zlibsettings.custom_zlib = zlibCompress;
unsigned status = lodepng_encode(&encimg.data, &encimg.size,
img.data, img.width, img.height, &state);
if (status != 0)
{
const char *err = lodepng_error_text(status);
throw love::Exception("Could not encode PNG image (%s)", err);
}
return encimg;
}
void PNGHandler::free(unsigned char *mem)
{
// LodePNG uses malloc, realloc, and free.
if (mem)
::free(mem);
}
} // magpie
} // image
} // love
@@ -0,0 +1,57 @@
/**
* 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_PNG_HANDLER_H
#define LOVE_IMAGE_MAGPIE_PNG_HANDLER_H
// LOVE
#include "FormatHandler.h"
namespace love
{
namespace image
{
namespace magpie
{
/**
* Interface between ImageData and LodePNG.
**/
class PNGHandler : public FormatHandler
{
public:
// Implements FormatHandler.
virtual bool canDecode(love::filesystem::FileData *data);
virtual bool canEncode(ImageData::Format format);
virtual DecodedImage decode(love::filesystem::FileData *data);
virtual EncodedImage encode(const DecodedImage &img, ImageData::Format format);
virtual void free(unsigned char *mem);
}; // PNGHandler
} // magpie
} // image
} // love
#endif // LOVE_IMAGE_MAGPIE_PNG_HANDLER_H
@@ -0,0 +1,149 @@
/**
* 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 "STBHandler.h"
static void loveSTBAssert(bool test, const char *teststr)
{
if (!test)
throw love::Exception("Could not decode image (stb_image assertion '%s' failed)", teststr);
}
// stb_image
#define STBI_NO_HDR
#define STBI_NO_STDIO
#define STB_IMAGE_IMPLEMENTATION
#define STBI_ASSERT(A) loveSTBAssert((A), #A)
#include "libraries/stb/stb_image.h"
// C
#include <cstdlib>
namespace love
{
namespace image
{
namespace magpie
{
bool STBHandler::canDecode(love::filesystem::FileData *data)
{
stbi__context s = {};
stbi__start_mem(&s, (const stbi_uc *) data->getData(), (int) data->getSize());
// These are internal stb_image functions, but we can still use them!
return stbi__bmp_test(&s) || stbi__tga_test(&s);
}
bool STBHandler::canEncode(ImageData::Format format)
{
return format == ImageData::FORMAT_TGA;
}
FormatHandler::DecodedImage STBHandler::decode(love::filesystem::FileData *data)
{
DecodedImage img;
int comp = 0;
img.data = stbi_load_from_memory((const stbi_uc *) data->getData(),
(int) data->getSize(),
&img.width, &img.height,
&comp, 4);
if (img.data == nullptr || img.width <= 0 || img.height <= 0)
throw love::Exception("Could not decode TGA or BMP image.");
img.size = img.width * img.height * 4;
return img;
}
FormatHandler::EncodedImage STBHandler::encode(const DecodedImage &img, ImageData::Format format)
{
if (!canEncode(format))
throw love::Exception("Invalid format.");
// We don't actually use stb_image for encoding, but this code is small
// enough that it might as well stay here.
EncodedImage encimg;
const size_t headerlen = 18;
const size_t bpp = 4;
encimg.size = (img.width * img.height * bpp) + headerlen;
// We need to use malloc because we use stb_image_free (which uses free())
// as our custom free() function, which is called by the ImageData after
// encode() is complete.
// stb_image's source code is compiled with this source, so calling malloc()
// directly is fine.
encimg.data = (unsigned char *) malloc(encimg.size);
if (encimg.data == nullptr)
throw love::Exception("Out of memory.");
// here's the header for the Targa file format.
encimg.data[0] = 0; // ID field size
encimg.data[1] = 0; // colormap type
encimg.data[2] = 2; // image type
encimg.data[3] = encimg.data[4] = 0; // colormap start
encimg.data[5] = encimg.data[6] = 0; // colormap length
encimg.data[7] = 32; // colormap bits
encimg.data[8] = encimg.data[9] = 0; // x origin
encimg.data[10] = encimg.data[11] = 0; // y origin
// Targa is little endian, so:
encimg.data[12] = img.width & 255; // least significant byte of width
encimg.data[13] = img.width >> 8; // most significant byte of width
encimg.data[14] = img.height & 255; // least significant byte of height
encimg.data[15] = img.height >> 8; // most significant byte of height
encimg.data[16] = bpp * 8; // bits per pixel
encimg.data[17] = 0x20; // descriptor bits (flip bits: 0x10 horizontal, 0x20 vertical)
// header done. write the pixel data to TGA:
memcpy(encimg.data + headerlen, img.data, img.width * img.height * bpp);
// convert the pixels from RGBA to BGRA.
pixel *encodedpixels = (pixel *) (encimg.data + headerlen);
for (int y = 0; y < img.height; y++)
{
for (int x = 0; x < img.width; x++)
{
unsigned char r = encodedpixels[y * img.width + x].r;
unsigned char b = encodedpixels[y * img.width + x].b;
encodedpixels[y * img.width + x].r = b;
encodedpixels[y * img.width + x].b = r;
}
}
return encimg;
}
void STBHandler::free(unsigned char *mem)
{
// The STB decoder gave memory allocated directly by stb_image to the
// ImageData, so we use stb_image_free to delete it.
stbi_image_free(mem);
}
} // magpie
} // image
} // love
@@ -0,0 +1,61 @@
/**
* 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_STB_HANDLER_H
#define LOVE_IMAGE_MAGPIE_STB_HANDLER_H
#include "FormatHandler.h"
namespace love
{
namespace image
{
namespace magpie
{
/**
* Interface between ImageData and the stb_image library, for decoding TGA and
* BMP images.
*
* We could use stb_image to decode PNG and JPEG as well, but performance and
* comprehensive format support is lacking compared to some alternatives, plus
* stb_image_write doesn't have JPEG support.
**/
class STBHandler : public FormatHandler
{
public:
// Implements FormatHandler.
virtual bool canDecode(love::filesystem::FileData *data);
virtual bool canEncode(ImageData::Format format);
virtual DecodedImage decode(love::filesystem::FileData *data);
virtual EncodedImage encode(const DecodedImage &img, ImageData::Format format);
virtual void free(unsigned char *mem);
}; // STBHandler
} // magpie
} // image
} // love
#endif // LOVE_IMAGE_MAGPIE_STB_HANDLER_H
@@ -37,7 +37,7 @@ int w_CompressedData_getWidth(lua_State *L)
int miplevel = luaL_optinteger(L, 2, 1);
int width = 0;
EXCEPT_GUARD(width = t->getWidth(miplevel >= 1 ? miplevel - 1 : 0);)
luax_catchexcept(L, [&](){ width = t->getWidth(miplevel - 1); });
lua_pushinteger(L, width);
return 1;
@@ -49,7 +49,7 @@ int w_CompressedData_getHeight(lua_State *L)
int miplevel = luaL_optinteger(L, 2, 1);
int height = 0;
EXCEPT_GUARD(height = t->getHeight(miplevel >= 1 ? miplevel - 1 : 0);)
luax_catchexcept(L, [&](){ height = t->getHeight(miplevel - 1); });
lua_pushinteger(L, height);
return 1;
@@ -61,10 +61,11 @@ int w_CompressedData_getDimensions(lua_State *L)
int miplevel = luaL_optinteger(L, 2, 1);
int width = 0, height = 0;
EXCEPT_GUARD(
width = t->getWidth(miplevel >= 1 ? miplevel - 1 : 0);
height = t->getHeight(miplevel >= 1 ? miplevel - 1 : 0);
)
luax_catchexcept(L, [&]()
{
width = t->getWidth(miplevel - 1);
height = t->getHeight(miplevel - 1);
});
lua_pushinteger(L, width);
lua_pushinteger(L, height);
+26 -28
View File
@@ -25,12 +25,14 @@
#include "magpie/Image.h"
#include "filesystem/wrap_Filesystem.h"
namespace love
{
namespace image
{
static Image *instance = 0;
#define instance() (Module::getInstance<Image>(Module::M_IMAGE))
int w_newImageData(lua_State *L)
{
@@ -42,53 +44,48 @@ int w_newImageData(lua_State *L)
if (w <= 0 || h <= 0)
return luaL_error(L, "Invalid image size.");
ImageData *t = 0;
EXCEPT_GUARD(t = instance->newImageData(w, h);)
ImageData *t = nullptr;
luax_catchexcept(L, [&](){ t = instance()->newImageData(w, h); });
luax_pushtype(L, "ImageData", IMAGE_IMAGE_DATA_T, t);
t->release();
return 1;
}
// Case 2: File(Data).
love::filesystem::FileData *data = love::filesystem::luax_getfiledata(L, 1);
// Convert to FileData, if necessary.
if (lua_isstring(L, 1) || luax_istype(L, 1, FILESYSTEM_FILE_T))
luax_convobj(L, 1, "filesystem", "newFileData");
love::filesystem::FileData *data = luax_checktype<love::filesystem::FileData>(L, 1, "FileData", FILESYSTEM_FILE_DATA_T);
ImageData *t = 0;
EXCEPT_GUARD(t = instance->newImageData(data);)
ImageData *t = nullptr;
luax_catchexcept(L,
[&]() { t = instance()->newImageData(data); },
[&]() { data->release(); }
);
luax_pushtype(L, "ImageData", IMAGE_IMAGE_DATA_T, t);
t->release();
return 1;
}
int w_newCompressedData(lua_State *L)
{
// Convert to FileData, if necessary.
if (lua_isstring(L, 1) || luax_istype(L, 1, FILESYSTEM_FILE_T))
luax_convobj(L, 1, "filesystem", "newFileData");
love::filesystem::FileData *data = love::filesystem::luax_getfiledata(L, 1);
love::filesystem::FileData *data = luax_checktype<love::filesystem::FileData>(L, 1, "FileData", FILESYSTEM_FILE_DATA_T);
CompressedData *t = 0;
EXCEPT_GUARD(t = instance->newCompressedData(data);)
CompressedData *t = nullptr;
luax_catchexcept(L,
[&]() { t = instance()->newCompressedData(data); },
[&]() { data->release(); }
);
luax_pushtype(L, "CompressedData", IMAGE_COMPRESSED_DATA_T, t);
t->release();
return 1;
}
int w_isCompressed(lua_State *L)
{
// Convert to FileData, if necessary.
if (lua_isstring(L, 1) || luax_istype(L, 1, FILESYSTEM_FILE_T))
luax_convobj(L, 1, "filesystem", "newFileData");
love::filesystem::FileData *data = luax_checktype<love::filesystem::FileData>(L, 1, "FileData", FILESYSTEM_FILE_DATA_T);
bool compressed = false;
EXCEPT_GUARD(compressed = instance->isCompressed(data);)
love::filesystem::FileData *data = love::filesystem::luax_getfiledata(L, 1);
bool compressed = instance()->isCompressed(data);
data->release();
luax_pushboolean(L, compressed);
return 1;
@@ -112,9 +109,10 @@ static const lua_CFunction types[] =
extern "C" int luaopen_love_image(lua_State *L)
{
if (instance == 0)
Image *instance = instance();
if (instance == nullptr)
{
EXCEPT_GUARD(instance = new love::image::magpie::Image();)
luax_catchexcept(L, [&](){ instance = new love::image::magpie::Image(); });
}
else
instance->retain();
@@ -62,7 +62,7 @@ int w_ImageData_getPixel(lua_State *L)
int y = luaL_checkint(L, 3);
pixel c;
EXCEPT_GUARD(c = t->getPixel(x, y);)
luax_catchexcept(L, [&](){ c = t->getPixel(x, y); });
lua_pushnumber(L, c.r);
lua_pushnumber(L, c.g);
@@ -98,7 +98,7 @@ int w_ImageData_setPixel(lua_State *L)
c.a = (unsigned char)luaL_optinteger(L, 7, 255);
}
EXCEPT_GUARD(t->setPixel(x, y, c);)
luax_catchexcept(L, [&](){ t->setPixel(x, y, c); });
return 0;
}
@@ -261,7 +261,7 @@ int w_ImageData_encode(lua_State *L)
return luaL_error(L, "Invalid image format '%s'.", fmt);
}
EXCEPT_GUARD(t->encode(file, format);)
luax_catchexcept(L, [&](){ t->encode(file, format); });
return 0;
}
@@ -36,6 +36,9 @@ public:
virtual ~JoystickModule() {}
// Implements Module.
virtual ModuleType getModuleType() const { return M_JOYSTICK; }
/**
* Adds a connected Joystick device and opens it for use.
* Returns NULL if the Joystick could not be added.
@@ -85,6 +88,20 @@ public:
**/
virtual Joystick::JoystickInput getGamepadMapping(const std::string &pguid, Joystick::GamepadInput gpinput) = 0;
/**
* Loads a newline-separated list of virtual Gamepad mapping strings for
* multiple joysticks at a time. The mapping strings must have been
* generated with saveGamepadMappings, via Steam, or some other tool which
* generates SDL GameController mappings.
**/
virtual void loadGamepadMappings(const std::string &mappings) = 0;
/**
* Gets a newline-separated list of virtual Gamepad mapping strings for
* all used or modified Joysticks which are identified as Gamepads.
**/
virtual std::string saveGamepadMappings() = 0;
}; // JoystickModule
} // joystick
@@ -191,12 +191,14 @@ bool Joystick::isDown(const std::vector<int> &buttonlist) const
if (!isConnected())
return false;
int num = getButtonCount();
int numbuttons = getButtonCount();
for (size_t i = 0; i < buttonlist.size(); i++)
for (int button : buttonlist)
{
int button = buttonlist[i];
if (button >= 0 && button < num && SDL_JoystickGetButton(joyhandle, button) == 1)
if (button < 0 || button >= numbuttons)
continue;
if (SDL_JoystickGetButton(joyhandle, button) == 1)
return true;
}
@@ -244,9 +246,9 @@ bool Joystick::isGamepadDown(const std::vector<GamepadButton> &blist) const
SDL_GameControllerButton sdlbutton;
for (size_t i = 0; i < blist.size(); i++)
for (GamepadButton button : blist)
{
if (!getConstant(blist[i], sdlbutton))
if (!getConstant(button, sdlbutton))
continue;
if (SDL_GameControllerGetButton(controller, sdlbutton) == 1)
@@ -57,10 +57,10 @@ JoystickModule::JoystickModule()
JoystickModule::~JoystickModule()
{
// Close any open Joysticks.
for (auto it = joysticks.begin(); it != joysticks.end(); ++it)
for (auto stick : joysticks)
{
(*it)->close();
(*it)->release();
stick->close();
stick->release();
}
if (SDL_WasInit(SDL_INIT_HAPTIC) != 0)
@@ -101,10 +101,10 @@ int JoystickModule::getJoystickCount() const
love::joystick::Joystick *JoystickModule::getJoystickFromID(int instanceid)
{
for (size_t i = 0; i < activeSticks.size(); i++)
for (auto stick : activeSticks)
{
if (instanceid == activeSticks[i]->getInstanceID())
return activeSticks[i];
if (stick->getInstanceID() == instanceid)
return stick;
}
return nullptr;
@@ -119,12 +119,12 @@ love::joystick::Joystick *JoystickModule::addJoystick(int deviceindex)
joystick::Joystick *joystick = 0;
bool reused = false;
for (auto it = joysticks.begin(); it != joysticks.end(); ++it)
for (auto stick : joysticks)
{
// Try to re-use a disconnected Joystick with the same GUID.
if (!(*it)->isConnected() && (*it)->getGUID() == guidstr)
if (!stick->isConnected() && stick->getGUID() == guidstr)
{
joystick = *it;
joystick = stick;
reused = true;
break;
}
@@ -144,9 +144,9 @@ love::joystick::Joystick *JoystickModule::addJoystick(int deviceindex)
// Make sure multiple instances of the same physical joystick aren't added
// to the active list.
for (auto it = activeSticks.begin(); it != activeSticks.end(); ++it)
for (auto activestick : activeSticks)
{
if (joystick->getHandle() == (*it)->getHandle())
if (joystick->getHandle() == activestick->getHandle())
{
joystick->close();
@@ -157,10 +157,13 @@ love::joystick::Joystick *JoystickModule::addJoystick(int deviceindex)
joystick->release();
}
return *it;
return activestick;
}
}
if (joystick->isGamepad())
recentGamepadGUIDs[joystick->getGUID()] = true;
activeSticks.push_back(joystick);
return joystick;
}
@@ -257,6 +260,9 @@ bool JoystickModule::setGamepadMapping(const std::string &guid, Joystick::Gamepa
// 1 == added, 0 == updated, -1 == error.
int status = SDL_GameControllerAddMapping(mapstr.c_str());
if (status != -1)
recentGamepadGUIDs[guid] = true;
// FIXME: massive hack until missing APIs are added to SDL 2:
// https://bugzilla.libsdl.org/show_bug.cgi?id=1975
if (status == 1)
@@ -427,22 +433,23 @@ void JoystickModule::checkGamepads(const std::string &guid) const
if (guid.compare(getDeviceGUID(d_index)) != 0)
continue;
for (auto it = activeSticks.begin(); it != activeSticks.end(); ++it)
for (auto stick : activeSticks)
{
if ((*it)->isGamepad() || guid.compare((*it)->getGUID()) != 0)
if (stick->isGamepad() || guid.compare(stick->getGUID()) != 0)
continue;
// Big hack time: open the index as a game controller and compare
// the underlying joystick handle to the active stick's.
SDL_GameController *ctrl = SDL_GameControllerOpen(d_index);
if (ctrl == nullptr)
SDL_GameController *controller = SDL_GameControllerOpen(d_index);
if (controller == nullptr)
continue;
SDL_Joystick *stick = SDL_GameControllerGetJoystick(ctrl);
if (stick == (SDL_Joystick *) (*it)->getHandle())
(*it)->openGamepad(d_index);
SDL_Joystick *sdlstick = SDL_GameControllerGetJoystick(controller);
if (sdlstick == (SDL_Joystick *) stick->getHandle())
stick->openGamepad(d_index);
SDL_GameControllerClose(ctrl);
// GameController objects are reference-counted in SDL.
SDL_GameControllerClose(controller);
}
}
}
@@ -462,6 +469,80 @@ std::string JoystickModule::getDeviceGUID(int deviceindex) const
return std::string(guidstr);
}
void JoystickModule::loadGamepadMappings(const std::string &mappings)
{
// TODO: We should use SDL_GameControllerAddMappingsFromRW. We're
// duplicating its functionality for now because it was added after
// SDL 2.0.0's release, and we want runtime compat with 2.0.0 on Linux...
std::stringstream ss(mappings);
std::string mapping;
bool success = false;
// The mappings string contains newline-separated mappings.
while (std::getline(ss, mapping))
{
if (mapping.empty())
continue;
// Strip out and compare any "platform:XYZ," in the mapping.
size_t pstartpos = mapping.find("platform:");
if (pstartpos != std::string::npos)
{
pstartpos += strlen("platform:");
size_t pendpos = mapping.find_first_of(',', pstartpos);
std::string platform = mapping.substr(pstartpos, pendpos - pstartpos);
if (platform.compare(SDL_GetPlatform()) != 0)
continue;
pstartpos -= strlen("platform:");
mapping.erase(pstartpos, pendpos - pstartpos + 1);
}
if (SDL_GameControllerAddMapping(mapping.c_str()) != -1)
{
success = true;
std::string guid = mapping.substr(0, mapping.find_first_of(','));
recentGamepadGUIDs[guid] = true;
// FIXME: massive hack until missing APIs are added to SDL 2:
// https://bugzilla.libsdl.org/show_bug.cgi?id=1975
checkGamepads(guid);
}
}
if (!success)
throw love::Exception("Invalid gamepad mappings.");
}
std::string JoystickModule::saveGamepadMappings()
{
std::string mappings;
for (const auto &g : recentGamepadGUIDs)
{
SDL_JoystickGUID sdlguid = SDL_JoystickGetGUIDFromString(g.first.c_str());
char *sdlmapping = SDL_GameControllerMappingForGUID(sdlguid);
if (sdlmapping == nullptr)
continue;
std::string mapping = sdlmapping;
SDL_free(sdlmapping);
if (mapping.find_last_of(',') != mapping.length() - 1)
mapping += ",";
// Matches SDL_GameControllerAddMappingsFromRW.
mapping += "platform:" + std::string(SDL_GetPlatform()) + ",\n";
mappings += mapping;
}
return mappings;
}
} // sdl
} // joystick
} // love
@@ -28,6 +28,7 @@
#include <string>
#include <vector>
#include <list>
#include <map>
namespace love
{
@@ -56,6 +57,8 @@ public:
bool setGamepadMapping(const std::string &guid, Joystick::GamepadInput gpinput, Joystick::JoystickInput joyinput);
Joystick::JoystickInput getGamepadMapping(const std::string &guid, Joystick::GamepadInput gpinput);
void loadGamepadMappings(const std::string &mappings);
std::string saveGamepadMappings();
private:
@@ -74,6 +77,10 @@ private:
// Persistent list of all Joysticks which have been connected at some point.
std::list<Joystick *> joysticks;
// Persistent map indicating GUIDs for Gamepads which have been connected or
// modified at some point.
std::map<std::string, bool> recentGamepadGUIDs;
}; // JoystickModule
} // sdl
@@ -0,0 +1,262 @@
/**
* 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 "wrap_Joystick.h"
#include "wrap_JoystickModule.h"
#include <vector>
namespace love
{
namespace joystick
{
Joystick *luax_checkjoystick(lua_State *L, int idx)
{
return luax_checktype<Joystick>(L, idx, "Joystick", JOYSTICK_JOYSTICK_T);
}
int w_Joystick_isConnected(lua_State *L)
{
Joystick *j = luax_checkjoystick(L, 1);
luax_pushboolean(L, j->isConnected());
return 1;
}
int w_Joystick_getName(lua_State *L)
{
Joystick *j = luax_checkjoystick(L, 1);
lua_pushstring(L, j->getName());
return 1;
}
int w_Joystick_getID(lua_State *L)
{
Joystick *j = luax_checkjoystick(L, 1);
// IDs are 1-based in Lua.
lua_pushinteger(L, j->getID() + 1);
int instanceid = j->getInstanceID();
if (instanceid >= 0)
lua_pushinteger(L, instanceid + 1);
else
lua_pushnil(L);
return 2;
}
int w_Joystick_getGUID(lua_State *L)
{
Joystick *j = luax_checkjoystick(L, 1);
luax_pushstring(L, j->getGUID());
return 1;
}
int w_Joystick_getAxisCount(lua_State *L)
{
Joystick *j = luax_checkjoystick(L, 1);
lua_pushinteger(L, j->getAxisCount());
return 1;
}
int w_Joystick_getButtonCount(lua_State *L)
{
Joystick *j = luax_checkjoystick(L, 1);
lua_pushinteger(L, j->getButtonCount());
return 1;
}
int w_Joystick_getHatCount(lua_State *L)
{
Joystick *j = luax_checkjoystick(L, 1);
lua_pushinteger(L, j->getHatCount());
return 1;
}
int w_Joystick_getAxis(lua_State *L)
{
Joystick *j = luax_checkjoystick(L, 1);
int axisindex = luaL_checkint(L, 2) - 1;
lua_pushnumber(L, j->getAxis(axisindex));
return 1;
}
int w_Joystick_getAxes(lua_State *L)
{
Joystick *j = luax_checkjoystick(L, 1);
std::vector<float> axes = j->getAxes();
for (size_t i = 0; i < axes.size(); i++)
lua_pushnumber(L, axes[i]);
return (int) axes.size();
}
int w_Joystick_getHat(lua_State *L)
{
Joystick *j = luax_checkjoystick(L, 1);
int hatindex = luaL_checkint(L, 2) - 1;
Joystick::Hat h = j->getHat(hatindex);
const char *direction = "";
Joystick::getConstant(h, direction);
lua_pushstring(L, direction);
return 1;
}
int w_Joystick_isDown(lua_State *L)
{
Joystick *j = luax_checkjoystick(L, 1);
luaL_checkinteger(L, 2);
std::vector<int> buttons;
for (int i = 2; i <= lua_gettop(L); i++)
buttons.push_back(luaL_checkint(L, i) - 1);
luax_pushboolean(L, j->isDown(buttons));
return 1;
}
int w_Joystick_isGamepad(lua_State *L)
{
Joystick *j = luax_checkjoystick(L, 1);
luax_pushboolean(L, j->isGamepad());
return 1;
}
int w_Joystick_getGamepadAxis(lua_State *L)
{
Joystick *j = luax_checkjoystick(L, 1);
const char *str = luaL_checkstring(L, 2);
Joystick::GamepadAxis axis;
if (!joystick::Joystick::getConstant(str, axis))
return luaL_error(L, "Invalid gamepad axis: %s", str);
lua_pushnumber(L, j->getGamepadAxis(axis));
return 1;
}
int w_Joystick_isGamepadDown(lua_State *L)
{
Joystick *j = luax_checkjoystick(L, 1);
std::vector<Joystick::GamepadButton> buttons;
buttons.reserve(lua_gettop(L) - 1);
luaL_checkstring(L, 2);
for (int i = 2; i <= lua_gettop(L); i++)
{
const char *str = luaL_checkstring(L, i);
Joystick::GamepadButton button;
if (!joystick::Joystick::getConstant(str, button))
return luaL_error(L, "Invalid gamepad button: %s", str);
buttons.push_back(button);
}
luax_pushboolean(L, j->isGamepadDown(buttons));
return 1;
}
int w_Joystick_isVibrationSupported(lua_State *L)
{
Joystick *j = luax_checkjoystick(L, 1);
luax_pushboolean(L, j->isVibrationSupported());
return 1;
}
int w_Joystick_setVibration(lua_State *L)
{
Joystick *j = luax_checkjoystick(L, 1);
bool success = false;
if (lua_isnoneornil(L, 2))
{
// Disable joystick vibration if no argument is given.
success = j->setVibration();
}
else
{
float left = (float) luaL_checknumber(L, 2);
float right = (float) luaL_optnumber(L, 3, left);
float duration = (float) luaL_optnumber(L, 4, -1.0); // -1 is infinite.
success = j->setVibration(left, right, duration);
}
luax_pushboolean(L, success);
return 1;
}
int w_Joystick_getVibration(lua_State *L)
{
Joystick *j = luax_checkjoystick(L, 1);
float left, right;
j->getVibration(left, right);
lua_pushnumber(L, left);
lua_pushnumber(L, right);
return 2;
}
// List of functions to wrap.
static const luaL_Reg functions[] =
{
{ "isConnected", w_Joystick_isConnected },
{ "getName", w_Joystick_getName },
{ "getID", w_Joystick_getID },
{ "getGUID", w_Joystick_getGUID },
{ "getAxisCount", w_Joystick_getAxisCount },
{ "getButtonCount", w_Joystick_getButtonCount },
{ "getHatCount", w_Joystick_getHatCount },
{ "getAxis", w_Joystick_getAxis },
{ "getAxes", w_Joystick_getAxes },
{ "getHat", w_Joystick_getHat },
{ "isDown", w_Joystick_isDown },
{ "isGamepad", w_Joystick_isGamepad },
{ "getGamepadAxis", w_Joystick_getGamepadAxis },
{ "isGamepadDown", w_Joystick_isGamepadDown },
{ "isVibrationSupported", w_Joystick_isVibrationSupported },
{ "setVibration", w_Joystick_setVibration },
{ "getVibration", w_Joystick_getVibration },
// From wrap_JoystickModule.
{ "getConnectedIndex", w_getIndex },
{ "getGamepadMapping", w_getGamepadMapping },
{ 0, 0 },
};
extern "C" int luaopen_joystick(lua_State *L)
{
return luax_register_type(L, "Joystick", functions);
}
} // joystick
} // love
@@ -0,0 +1,57 @@
/**
* 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_JOYSTICK_WRAP_JOYSTICK_H
#define LOVE_JOYSTICK_WRAP_JOYSTICK_H
// LOVE
#include "common/config.h"
#include "Joystick.h"
#include "common/runtime.h"
namespace love
{
namespace joystick
{
Joystick *luax_checkjoystick(lua_State *L, int idx);
int w_Joystick_isConnected(lua_State *L);
int w_Joystick_getName(lua_State *L);
int w_Joystick_getID(lua_State *L);
int w_Joystick_getGUID(lua_State *L);
int w_Joystick_getAxisCount(lua_State *L);
int w_Joystick_getButtonCount(lua_State *L);
int w_Joystick_getHatCount(lua_State *L);
int w_Joystick_getAxis(lua_State *L);
int w_Joystick_getAxes(lua_State *L);
int w_Joystick_getHat(lua_State *L);
int w_Joystick_isDown(lua_State *L);
int w_Joystick_isGamepad(lua_State *L);
int w_Joystick_getGamepadAxis(lua_State *L);
int w_Joystick_isGamepadDown(lua_State *L);
int w_Joystick_isVibrationSupported(lua_State *L);
int w_Joystick_setVibration(lua_State *L);
int w_Joystick_getVibration(lua_State *L);
extern "C" int luaopen_joystick(lua_State *L);
} // joystick
} // love
#endif // LOVE_JOYSTICK_SDL_WRAP_JOYSTICK_H
@@ -0,0 +1,261 @@
/**
* 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.
**/
#include "wrap_JoystickModule.h"
#include "wrap_Joystick.h"
#include "filesystem/wrap_Filesystem.h"
#include "sdl/JoystickModule.h"
namespace love
{
namespace joystick
{
#define instance() (Module::getInstance<JoystickModule>(Module::M_JOYSTICK))
int w_getJoysticks(lua_State *L)
{
int stickcount = instance()->getJoystickCount();
lua_createtable(L, stickcount, 0);
for (int i = 0; i < stickcount; i++)
{
Joystick *stick = instance()->getJoystick(i);
luax_pushtype(L, "Joystick", JOYSTICK_JOYSTICK_T, stick);
lua_rawseti(L, -2, i + 1);
}
return 1;
}
int w_getIndex(lua_State *L)
{
Joystick *j = luax_checkjoystick(L, 1);
int index = instance()->getIndex(j);
if (index >= 0)
lua_pushinteger(L, index + 1);
else
lua_pushnil(L);
return 1;
}
int w_getJoystickCount(lua_State *L)
{
lua_pushinteger(L, instance()->getJoystickCount());
return 1;
}
int w_setGamepadMapping(lua_State *L)
{
// Only accept a GUID string. We don't accept a Joystick object because
// the gamepad mapping applies to all joysticks with the same GUID (e.g. all
// Xbox 360 controllers on the system), rather than individual objects.
const char *guid = luaL_checkstring(L, 1);
const char *gpbindstr = luaL_checkstring(L, 2);
Joystick::GamepadInput gpinput;
if (Joystick::getConstant(gpbindstr, gpinput.axis))
gpinput.type = Joystick::INPUT_TYPE_AXIS;
else if (Joystick::getConstant(gpbindstr, gpinput.button))
gpinput.type = Joystick::INPUT_TYPE_BUTTON;
else
return luaL_error(L, "Invalid gamepad axis/button: %s", gpbindstr);
const char *jinputtypestr = luaL_checkstring(L, 3);
Joystick::JoystickInput jinput;
if (!Joystick::getConstant(jinputtypestr, jinput.type))
return luaL_error(L, "Invalid joystick input type: %s", jinputtypestr);
const char *hatstr;
switch (jinput.type)
{
case Joystick::INPUT_TYPE_AXIS:
jinput.axis = luaL_checkint(L, 4) - 1;
break;
case Joystick::INPUT_TYPE_BUTTON:
jinput.button = luaL_checkint(L, 4) - 1;
break;
case Joystick::INPUT_TYPE_HAT:
// Hats need both a hat index and a hat value.
jinput.hat.index = luaL_checkint(L, 4) - 1;
hatstr = luaL_checkstring(L, 5);
if (!Joystick::getConstant(hatstr, jinput.hat.value))
return luaL_error(L, "Invalid joystick hat: %s", hatstr);
break;
default:
return luaL_error(L, "Invalid joystick input type: %s", jinputtypestr);
}
bool success = false;
luax_catchexcept(L, [&](){ success = instance()->setGamepadMapping(guid, gpinput, jinput); });
luax_pushboolean(L, success);
return 1;
}
int w_getGamepadMapping(lua_State *L)
{
std::string guid;
// Accept either a GUID string or a Joystick object. This way we can re-use
// the function for Joystick:getGamepadMapping.
if (lua_type(L, 1) == LUA_TSTRING)
guid = luax_checkstring(L, 1);
else
{
Joystick *stick = luax_checkjoystick(L, 1);
guid = stick->getGUID();
}
const char *gpbindstr = luaL_checkstring(L, 2);
Joystick::GamepadInput gpinput;
if (Joystick::getConstant(gpbindstr, gpinput.axis))
gpinput.type = Joystick::INPUT_TYPE_AXIS;
else if (Joystick::getConstant(gpbindstr, gpinput.button))
gpinput.type = Joystick::INPUT_TYPE_BUTTON;
else
return luaL_error(L, "Invalid gamepad axis/button: %s", gpbindstr);
Joystick::JoystickInput jinput;
jinput.type = Joystick::INPUT_TYPE_MAX_ENUM;
luax_catchexcept(L, [&](){ jinput = instance()->getGamepadMapping(guid, gpinput); });
if (jinput.type == Joystick::INPUT_TYPE_MAX_ENUM)
return 0;
const char *inputtypestr;
if (!Joystick::getConstant(jinput.type, inputtypestr))
return luaL_error(L, "Unknown joystick input type.");
lua_pushstring(L, inputtypestr);
const char *hatstr;
switch (jinput.type)
{
case Joystick::INPUT_TYPE_AXIS:
lua_pushinteger(L, jinput.axis + 1);
return 2;
case Joystick::INPUT_TYPE_BUTTON:
lua_pushinteger(L, jinput.button + 1);
return 2;
case Joystick::INPUT_TYPE_HAT:
lua_pushinteger(L, jinput.hat.index + 1);
if (Joystick::getConstant(jinput.hat.value, hatstr))
{
lua_pushstring(L, hatstr);
return 3;
}
else
return luaL_error(L, "Unknown joystick hat.");
default:
break; // ?
}
return 1;
}
int w_loadGamepadMappings(lua_State *L)
{
lua_pushvalue(L, 1);
luax_convobj(L, -1, "filesystem", "isFile");
bool isfile = luax_toboolean(L, -1);
lua_pop(L, 1);
std::string mappings;
if (isfile)
{
love::filesystem::FileData *fd = love::filesystem::luax_getfiledata(L, 1);
mappings = std::string((const char *) fd->getData(), fd->getSize());
fd->release();
}
else
mappings = luax_checkstring(L, 1);
luax_catchexcept(L, [&](){ instance()->loadGamepadMappings(mappings); });
return 0;
}
int w_saveGamepadMappings(lua_State *L)
{
lua_settop(L, 1);
std::string mappings = instance()->saveGamepadMappings();
// Optionally write the mappings string to a file.
if (!lua_isnoneornil(L, 1))
{
luax_pushstring(L, mappings);
int idxs[] = {1, 2};
luax_convobj(L, idxs, 2, "filesystem", "write");
lua_pop(L, 1); // Pop the return value.
}
// Return the actual string even if we also wrote it to a file.
luax_pushstring(L, mappings);
return 1;
}
// List of functions to wrap.
static const luaL_Reg functions[] =
{
{ "getJoysticks", w_getJoysticks },
{ "getJoystickCount", w_getJoystickCount },
{ "setGamepadMapping", w_setGamepadMapping },
{ "getGamepadMapping", w_getGamepadMapping },
{ "loadGamepadMappings", w_loadGamepadMappings },
{ "saveGamepadMappings", w_saveGamepadMappings },
{ 0, 0 }
};
static const lua_CFunction types[] =
{
luaopen_joystick,
0,
};
extern "C" int luaopen_love_joystick(lua_State *L)
{
JoystickModule *instance = instance();
if (instance == nullptr)
{
luax_catchexcept(L, [&](){ instance = new sdl::JoystickModule(); });
}
else
instance->retain();
WrappedModule w;
w.module = instance;
w.name = "joystick";
w.flags = MODULE_T;
w.functions = functions;
w.types = types;
return luax_register_module(L, w);
}
} // joystick
} // love
@@ -0,0 +1,46 @@
/**
* 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_JOYSTICK_WRAP_JOYSTICK_MODULE_H
#define LOVE_JOYSTICK_WRAP_JOYSTICK_MODULE_H
// LOVE
#include "common/config.h"
#include "common/runtime.h"
#include "JoystickModule.h"
namespace love
{
namespace joystick
{
int w_getJoysticks(lua_State *L);
int w_getIndex(lua_State *L);
int w_getJoystickCount(lua_State *L);
int w_setGamepadMapping(lua_State *L);
int w_getGamepadMapping(lua_State *L);
int w_loadGamepadMappings(lua_State *L);
int w_saveGamepadMappings(lua_State *L);
extern "C" LOVE_EXPORT int luaopen_love_joystick(lua_State *L);
} // joystick
} // love
#endif // LOVE_JOYSTICK_WRAP_JOYSTICK_MODULE_H
+15
View File
@@ -247,6 +247,9 @@ public:
virtual ~Keyboard() {}
// Implements Module.
virtual ModuleType getModuleType() const { return M_KEYBOARD; }
/**
* Sets whether repeat keypress events should be sent if a key is held down.
* Does not affect text input events.
@@ -272,11 +275,23 @@ public:
**/
virtual void setTextInput(bool enable) = 0;
/**
* Sets whether text input events should be received, and sets the
* rectangle used for on-screen keyboards.
**/
virtual void setTextInput(bool enable, int x, int y, int w, int h) = 0;
/**
* Gets whether text input events are enabled.
**/
virtual bool hasTextInput() const = 0;
/**
* Gets whether the system can display an on-screen keyboard when text input
* events are enabled.
**/
virtual bool hasScreenKeyboard() const = 0;
static bool getConstant(const char *in, Key &out);
static bool getConstant(Key in, const char *&out);
@@ -71,11 +71,24 @@ void Keyboard::setTextInput(bool enable)
SDL_StopTextInput();
}
void Keyboard::setTextInput(bool enable, int x, int y, int w, int h)
{
setTextInput(enable);
SDL_Rect textrect = {x, y, w, h};
SDL_SetTextInputRect(&textrect);
}
bool Keyboard::hasTextInput() const
{
return SDL_IsTextInputActive();
}
bool Keyboard::hasScreenKeyboard() const
{
return SDL_HasScreenKeyboardSupport();
}
std::map<Keyboard::Key, SDL_Keycode> Keyboard::createKeyMap()
{
std::map<Keyboard::Key, SDL_Keycode> k;
@@ -52,7 +52,9 @@ public:
bool isDown(Key *keylist) const;
void setTextInput(bool enable);
void setTextInput(bool enable, int x, int y, int w, int h);
bool hasTextInput() const;
bool hasScreenKeyboard() const;
private:
@@ -29,17 +29,17 @@ namespace love
namespace keyboard
{
static Keyboard *instance = nullptr;
#define instance() (Module::getInstance<Keyboard>(Module::M_KEYBOARD))
int w_setKeyRepeat(lua_State *L)
{
instance->setKeyRepeat(luax_toboolean(L, 1));
instance()->setKeyRepeat(luax_toboolean(L, 1));
return 0;
}
int w_hasKeyRepeat(lua_State *L)
{
luax_pushboolean(L, instance->hasKeyRepeat());
luax_pushboolean(L, instance()->hasKeyRepeat());
return 1;
}
@@ -57,20 +57,38 @@ int w_isDown(lua_State *L)
}
keylist[counter] = Keyboard::KEY_MAX_ENUM;
luax_pushboolean(L, instance->isDown(keylist));
luax_pushboolean(L, instance()->isDown(keylist));
delete[] keylist;
return 1;
}
int w_setTextInput(lua_State *L)
{
instance->setTextInput(luax_toboolean(L, 1));
bool enable = luax_toboolean(L, 1);
if (lua_gettop(L) <= 1)
instance()->setTextInput(enable);
else
{
int x = (int) luaL_checkinteger(L, 2);
int y = (int) luaL_checkinteger(L, 3);
int w = (int) luaL_checkinteger(L, 4);
int h = (int) luaL_checkinteger(L, 5);
instance()->setTextInput(enable, x, y, w, h);
}
return 0;
}
int w_hasTextInput(lua_State *L)
{
luax_pushboolean(L, instance->hasTextInput());
luax_pushboolean(L, instance()->hasTextInput());
return 1;
}
int w_hasScreenKeyboard(lua_State *L)
{
luax_pushboolean(L, instance()->hasScreenKeyboard());
return 1;
}
@@ -81,15 +99,17 @@ static const luaL_Reg functions[] =
{ "hasKeyRepeat", w_hasKeyRepeat },
{ "setTextInput", w_setTextInput },
{ "hasTextInput", w_hasTextInput },
{ "hasScreenKeyboard", w_hasScreenKeyboard },
{ "isDown", w_isDown },
{ 0, 0 }
};
extern "C" int luaopen_love_keyboard(lua_State *L)
{
Keyboard *instance = instance();
if (instance == nullptr)
{
EXCEPT_GUARD(instance = new love::keyboard::sdl::Keyboard();)
luax_catchexcept(L, [&](){ instance = new love::keyboard::sdl::Keyboard(); });
}
else
instance->retain();

Some files were not shown because too many files have changed in this diff Show More