--HG--
branch : minor
This commit is contained in:
Kyle McLamb
2017-08-02 19:33:42 -04:00
148 changed files with 3019 additions and 2467 deletions
@@ -18,13 +18,11 @@
* 3. This notice may not be removed or altered from any source distribution.
**/
#ifndef LOVE_GRAPHICS_COLOR_H
#define LOVE_GRAPHICS_COLOR_H
#ifndef LOVE_COLOR_H
#define LOVE_COLOR_H
namespace love
{
namespace graphics
{
template <typename T>
struct ColorT
@@ -153,7 +151,6 @@ inline Colorf toColorf(Color c)
return Colorf(c.r / 255.0f, c.g / 255.0f, c.b / 255.0f, c.a / 255.0f);
}
} // graphics
} // love
#endif // LOVE_GRAPHICS_COLOR_H
#endif // LOVE_COLOR_H
+16
View File
@@ -225,6 +225,22 @@ bool createStorageDirectories()
return true;
}
bool hasBackgroundMusic()
{
JNIEnv *env = (JNIEnv*) SDL_AndroidGetJNIEnv();
jobject activity = (jobject) SDL_AndroidGetActivity();
jclass clazz(env->GetObjectClass(activity));
jmethodID method_id = env->GetMethodID(clazz, "hasBackgroundMusic", "()Z");
jboolean result = env->CallBooleanMethod(activity, method_id);
env->DeleteLocalRef(activity);
env->DeleteLocalRef(clazz);
return result;
}
} // android
} // love
+2
View File
@@ -66,6 +66,8 @@ bool mkdir(const char *path);
bool createStorageDirectories();
bool hasBackgroundMusic();
} // android
} // love
+10
View File
@@ -64,6 +64,16 @@ std::string getExecutablePath();
**/
void vibrate();
/**
* Enable mix mode (e.g. with background music apps) and playback with a muted device.
**/
void setAudioMixWithOthers(bool mixEnabled);
/**
* Returns whether another application is playing audio.
**/
bool hasBackgroundMusic();
} // ios
} // love
+23
View File
@@ -26,6 +26,7 @@
#import <UIKit/UIKit.h>
#import <AudioToolbox/AudioServices.h>
#import <AVFoundation/AVFoundation.h>
#include <vector>
@@ -344,6 +345,28 @@ void vibrate()
}
}
void setAudioMixWithOthers(bool mixEnabled)
{
@autoreleasepool
{
NSString *category = AVAudioSessionCategorySoloAmbient;
NSError *err;
if (mixEnabled)
category = AVAudioSessionCategoryAmbient;
if (![[AVAudioSession sharedInstance] setCategory:category error:&err])
NSLog(@"Error in AVAudioSession setCategory: %@", [err localizedDescription]);
}
}
bool hasBackgroundMusic()
{
if ([[AVAudioSession sharedInstance] respondsToSelector:@selector(secondaryAudioShouldBeSilencedHint)])
return [[AVAudioSession sharedInstance] secondaryAudioShouldBeSilencedHint];
return false;
}
} // ios
} // love
+14 -16
View File
@@ -1,25 +1,23 @@
/**
* Simple DDS data parser for compressed 2D textures.
*
* Copyright (c) 2013 Alexander Szpakowski.
* Copyright (c) 2013-2017 Alex Szpakowski
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* 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.
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the 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:
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
* 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.
*
*
* Enums and structs copied from Microsoft.
+14 -16
View File
@@ -1,25 +1,23 @@
/**
* Simple DDS data parser for compressed 2D textures.
*
* Copyright (c) 2013 Alexander Szpakowski.
* Copyright (c) 2013-2017 Alex Szpakowski
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* 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.
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the 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:
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
* 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 "ddsparse.h"
+14 -16
View File
@@ -1,25 +1,23 @@
/**
* Simple DDS data parser for compressed 2D textures.
*
* Copyright (c) 2013 Alexander Szpakowski.
* Copyright (c) 2013-2017 Alex Szpakowski
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* 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.
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the 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:
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
* 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 DDS_PARSE_H
File diff suppressed because it is too large Load Diff
+18 -2
View File
@@ -13,18 +13,34 @@
* 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 Audio versions must be plainly marked as such, and must not be
* 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 Audio distribution.
* 3. This notice may not be removed or altered from any source distribution.
**/
#include "Audio.h"
#include "common/config.h"
#ifdef LOVE_IOS
#include "common/ios.h"
#endif
namespace love
{
namespace audio
{
bool Audio::setMixWithSystem(bool mix)
{
#ifdef LOVE_IOS
love::ios::setAudioMixWithOthers(mix);
return true;
#else
LOVE_UNUSED(mix);
return false;
#endif
}
StringMap<Audio::DistanceModel, Audio::DISTANCE_MAX_ENUM>::Entry Audio::distanceModelEntries[] =
{
{"none", Audio::DISTANCE_NONE},
+8 -2
View File
@@ -9,7 +9,7 @@
* 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 = 0; you must not
* 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.
@@ -236,7 +236,7 @@ public:
* @param list List of EFX names to fill.
* @return true if effect was present, false otherwise.
*/
virtual bool getEffectsList(std::vector<std::string> &list) = 0;
virtual bool getActiveEffects(std::vector<std::string> &list) const = 0;
/**
* Gets maximum number of scene EFX effects.
@@ -256,6 +256,12 @@ public:
*/
virtual bool isEFXsupported() const = 0;
/**
* Sets whether audio from other apps mixes with love.audio or is muted,
* on supported platforms.
**/
bool setMixWithSystem(bool mix);
private:
static StringMap<DistanceModel, DISTANCE_MAX_ENUM>::Entry distanceModelEntries[];
+1 -1
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2016 LOVE Development Team
* Copyright (c) 2006-2017 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
+1 -1
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2016 LOVE Development Team
* Copyright (c) 2006-2017 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
+12 -7
View File
@@ -9,7 +9,7 @@
* 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 = 0; you must not
* 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.
@@ -37,15 +37,14 @@ public:
static love::Type type;
static const int DEFAULT_SAMPLES = 8192;
static const int DEFAULT_SAMPLE_RATE = 8000;
static const int DEFAULT_BIT_DEPTH = 16;
static const int DEFAULT_CHANNELS = 1;
RecordingDevice();
virtual ~RecordingDevice();
/**
* Begins audio input recording process. using default (previous) parameters.
* @return True if recording started successfully.
**/
virtual bool start() = 0;
/**
* Begins audio input recording process.
* @param samples Number of samples to buffer.
@@ -77,6 +76,11 @@ public:
**/
virtual int getSampleCount() const = 0;
/**
* Gets the maximum number of samples that will be buffered, as set by start().
**/
virtual int getMaxSamples() const = 0;
/**
* @return Sample rate for recording.
**/
@@ -96,6 +100,7 @@ public:
* @return True if currently recording.
**/
virtual bool isRecording() const = 0;
}; //RecordingDevice
} //audio
+1 -1
View File
@@ -117,7 +117,7 @@ public:
virtual bool setEffect(const char *effect, const std::map<Filter::Parameter, float> &params) = 0;
virtual bool unsetEffect(const char *effect) = 0;
virtual bool getEffect(const char *effect, std::map<Filter::Parameter, float> &params) = 0;
virtual bool getEffectsList(std::vector<std::string> &list) = 0;
virtual bool getActiveEffects(std::vector<std::string> &list) const = 0;
virtual int getFreeBufferCount() const = 0;
virtual bool queue(void *data, size_t length, int dataSampleRate, int dataBitDepth, int dataChannels) = 0;
+1 -1
View File
@@ -183,7 +183,7 @@ bool Audio::getEffect(const char *, std::map<Effect::Parameter, float> &)
return false;
}
bool Audio::getEffectsList(std::vector<std::string> &list)
bool Audio::getActiveEffects(std::vector<std::string> &) const
{
return false;
}
+1 -1
View File
@@ -81,7 +81,7 @@ public:
bool setEffect(const char *, std::map<Effect::Parameter, float> &params);
bool unsetEffect(const char *);
bool getEffect(const char *, std::map<Effect::Parameter, float> &params);
bool getEffectsList(std::vector<std::string> &list);
bool getActiveEffects(std::vector<std::string> &list) const;
int getMaxSceneEffects() const;
int getMaxSourceEffects() const;
bool isEFXsupported() const;
+9 -9
View File
@@ -9,7 +9,7 @@
* 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 = 0; you must not
* 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.
@@ -38,11 +38,6 @@ RecordingDevice::~RecordingDevice()
{
}
bool RecordingDevice::start()
{
return false;
}
bool RecordingDevice::start(int, int, int, int)
{
return false;
@@ -62,19 +57,24 @@ int RecordingDevice::getSampleCount() const
return 0;
}
int RecordingDevice::getMaxSamples() const
{
return 0;
}
int RecordingDevice::getSampleRate() const
{
return 8000;
return 0;
}
int RecordingDevice::getBitDepth() const
{
return 16;
return 0;
}
int RecordingDevice::getChannels() const
{
return 1;
return 0;
}
const char *RecordingDevice::getName() const
+2 -2
View File
@@ -9,7 +9,7 @@
* 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 = 0; you must not
* 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.
@@ -36,11 +36,11 @@ class RecordingDevice : public love::audio::RecordingDevice
public:
RecordingDevice(const char *name);
virtual ~RecordingDevice();
virtual bool start();
virtual bool start(int samples, int sampleRate, int bitDepth, int channels);
virtual void stop();
virtual love::sound::SoundData *getData();
virtual const char *getName() const;
virtual int getMaxSamples() const;
virtual int getSampleCount() const;
virtual int getSampleRate() const;
virtual int getBitDepth() const;
+1 -1
View File
@@ -274,7 +274,7 @@ bool Source::getEffect(const char *, std::map<Filter::Parameter, float> &)
return false;
}
bool Source::getEffectsList(std::vector<std::string> &)
bool Source::getActiveEffects(std::vector<std::string> &) const
{
return false;
}
+1 -1
View File
@@ -90,7 +90,7 @@ public:
virtual bool setEffect(const char *effect, const std::map<Filter::Parameter, float> &params);
virtual bool unsetEffect(const char *effect);
virtual bool getEffect(const char *effect, std::map<Filter::Parameter, float> &params);
virtual bool getEffectsList(std::vector<std::string> &list);
virtual bool getActiveEffects(std::vector<std::string> &list) const;
private:
+1 -1
View File
@@ -543,7 +543,7 @@ bool Audio::getEffect(const char *name, std::map<Effect::Parameter, float> &para
return true;
}
bool Audio::getEffectsList(std::vector<std::string> &list)
bool Audio::getActiveEffects(std::vector<std::string> &list) const
{
if (effectmap.empty())
return false;
+1 -1
View File
@@ -118,7 +118,7 @@ public:
bool setEffect(const char *name, std::map<Effect::Parameter, float> &params);
bool unsetEffect(const char *name);
bool getEffect(const char *name, std::map<Effect::Parameter, float> &params);
bool getEffectsList(std::vector<std::string> &list);
bool getActiveEffects(std::vector<std::string> &list) const;
int getMaxSceneEffects() const;
int getMaxSourceEffects() const;
bool isEFXsupported() const;
+12 -17
View File
@@ -9,7 +9,7 @@
* 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 = 0; you must not
* 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.
@@ -49,26 +49,11 @@ RecordingDevice::RecordingDevice(const char *name)
RecordingDevice::~RecordingDevice()
{
if (!isRecording())
return;
alcCaptureStop(device);
alcCaptureCloseDevice(device);
}
bool RecordingDevice::start()
{
return start(samples, sampleRate, bitDepth, channels);
stop();
}
bool RecordingDevice::start(int samples, int sampleRate, int bitDepth, int channels)
{
if (isRecording())
{
alcCaptureStop(device);
alcCaptureCloseDevice(device);
}
ALenum format = Audio::getFormat(bitDepth, channels);
if (format == AL_NONE)
throw InvalidFormatException(channels, bitDepth);
@@ -79,15 +64,20 @@ bool RecordingDevice::start(int samples, int sampleRate, int bitDepth, int chann
if (sampleRate <= 0)
throw love::Exception("Invalid sample rate.");
if (isRecording())
stop();
device = alcCaptureOpenDevice(name.c_str(), sampleRate, format, samples);
if (device == nullptr)
return false;
alcCaptureStart(device);
this->samples = samples;
this->sampleRate = sampleRate;
this->bitDepth = bitDepth;
this->channels = channels;
return true;
}
@@ -127,6 +117,11 @@ int RecordingDevice::getSampleCount() const
return (int)samples;
}
int RecordingDevice::getMaxSamples() const
{
return samples;
}
int RecordingDevice::getSampleRate() const
{
return sampleRate;
+10 -6
View File
@@ -9,7 +9,7 @@
* 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 = 0; you must not
* 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.
@@ -49,26 +49,30 @@ namespace openal
class RecordingDevice : public love::audio::RecordingDevice
{
public:
RecordingDevice(const char *name);
virtual ~RecordingDevice();
virtual bool start();
virtual bool start(int samples, int sampleRate, int bitDepth, int channels);
virtual void stop();
virtual love::sound::SoundData *getData();
virtual const char *getName() const;
virtual int getSampleCount() const;
virtual int getMaxSamples() const;
virtual int getSampleRate() const;
virtual int getBitDepth() const;
virtual int getChannels() const;
virtual bool isRecording() const;
private:
int samples = 8192;
int sampleRate = 8000;
int bitDepth = 16;
int channels = 1;
int samples = DEFAULT_SAMPLES;
int sampleRate = DEFAULT_SAMPLE_RATE;
int bitDepth = DEFAULT_BIT_DEPTH;
int channels = DEFAULT_CHANNELS;
std::string name;
ALCdevice *device = nullptr;
}; //RecordingDevice
} //openal
+3 -3
View File
@@ -1041,7 +1041,7 @@ void Source::stop(const std::vector<love::audio::Source*> &sources)
sourceIds.push_back(source->source);
}
alSourceStopv((ALsizei) sources.size(), &sourceIds[0]);
alSourceStopv((ALsizei) sourceIds.size(), &sourceIds[0]);
for (auto &_source : sources)
{
@@ -1068,7 +1068,7 @@ void Source::pause(const std::vector<love::audio::Source*> &sources)
sourceIds.push_back(source->source);
}
alSourcePausev((ALsizei) sources.size(), &sourceIds[0]);
alSourcePausev((ALsizei) sourceIds.size(), &sourceIds[0]);
}
std::vector<love::audio::Source*> Source::pause(Pool *pool)
@@ -1488,7 +1488,7 @@ bool Source::getEffect(const char *name, std::map<Filter::Parameter, float> &par
return true;
}
bool Source::getEffectsList(std::vector<std::string> &list)
bool Source::getActiveEffects(std::vector<std::string> &list) const
{
if (effectmap.empty())
return false;
+1 -1
View File
@@ -152,7 +152,7 @@ public:
virtual bool setEffect(const char *effect, const std::map<Filter::Parameter, float> &params);
virtual bool unsetEffect(const char *effect);
virtual bool getEffect(const char *effect, std::map<Filter::Parameter, float> &params);
virtual bool getEffectsList(std::vector<std::string> &list);
virtual bool getActiveEffects(std::vector<std::string> &list) const;
virtual int getFreeBufferCount() const;
virtual bool queue(void *data, size_t length, int dataSampleRate, int dataBitDepth, int dataChannels);
+39 -11
View File
@@ -84,7 +84,7 @@ int w_newQueueableSource(lua_State *L)
Source *t = nullptr;
luax_catchexcept(L, [&]() {
t = instance()->newSource((int)luaL_checknumber(L, 1), (int)luaL_checknumber(L, 2), (int)luaL_checknumber(L, 3), (int)luaL_optnumber(L, 4, 0));
t = instance()->newSource((int)luaL_checkinteger(L, 1), (int)luaL_checkinteger(L, 2), (int)luaL_checkinteger(L, 3), (int)luaL_optinteger(L, 4, 0));
});
if (t != nullptr)
@@ -115,16 +115,34 @@ static std::vector<Source*> readSourceList(lua_State *L, int n)
return sources;
}
static std::vector<Source*> readSourceVararg(lua_State *L, int i)
{
const int top = lua_gettop(L);
if (i < 0)
i += top + 1;
int items = top - i + 1;
std::vector<Source*> sources(items);
for (int pos = 0; i <= top; i++, pos++)
sources[pos] = luax_checksource(L, i);
return sources;
}
int w_play(lua_State *L)
{
if (lua_istable(L, 1))
{
luax_pushboolean(L, instance()->play(readSourceList(L, 1)));
return 1;
else if (lua_gettop(L) > 1)
luax_pushboolean(L, instance()->play(readSourceVararg(L, 1)));
else
{
Source *s = luax_checksource(L, 1);
luax_pushboolean(L, instance()->play(s));
}
Source *s = luax_checksource(L, 1);
luax_pushboolean(L, instance()->play(s));
return 1;
}
@@ -134,6 +152,8 @@ int w_stop(lua_State *L)
instance()->stop();
else if (lua_istable(L, 1))
instance()->stop(readSourceList(L, 1));
else if (lua_gettop(L) > 1)
instance()->stop(readSourceVararg(L, 1));
else
{
Source *s = luax_checksource(L, 1);
@@ -158,6 +178,8 @@ int w_pause(lua_State *L)
}
else if (lua_istable(L, 1))
instance()->pause(readSourceList(L, 1));
else if (lua_gettop(L) > 1)
instance()->pause(readSourceVararg(L, 1));
else
{
Source *s = luax_checksource(L, 1);
@@ -463,14 +485,13 @@ int w_getEffect(lua_State *L)
return 1;
}
int w_getEffectsList(lua_State *L)
int w_getActiveEffects(lua_State *L)
{
std::vector<std::string> list;
if (!instance()->getEffectsList(list))
return 0;
instance()->getActiveEffects(list);
lua_createtable(L, 0, list.size());
for (unsigned int i = 0; i < list.size(); i++)
lua_createtable(L, 0, (int) list.size());
for (int i = 0; i < (int) list.size(); i++)
{
lua_pushnumber(L, i + 1);
lua_pushstring(L, list[i].c_str());
@@ -497,6 +518,12 @@ int w_isEffectsSupported(lua_State *L)
return 1;
}
int w_setMixWithSystem(lua_State *L)
{
luax_pushboolean(L, instance()->setMixWithSystem(luax_toboolean(L, 1)));
return 1;
}
// List of functions to wrap.
static const luaL_Reg functions[] =
{
@@ -523,10 +550,11 @@ static const luaL_Reg functions[] =
{ "getRecordingDevices", w_getRecordingDevices },
{ "setEffect", w_setEffect },
{ "getEffect", w_getEffect },
{ "getEffectsList", w_getEffectsList },
{ "getActiveEffects", w_getActiveEffects },
{ "getMaxSceneEffects", w_getMaxSceneEffects },
{ "getMaxSourceEffects", w_getMaxSourceEffects },
{ "isEffectsSupported", w_isEffectsSupported },
{ "setMixWithSystem", w_setMixWithSystem },
{ 0, 0 }
};
+14 -9
View File
@@ -35,19 +35,24 @@ RecordingDevice *luax_checkrecordingdevice(lua_State *L, int idx)
int w_RecordingDevice_start(lua_State *L)
{
RecordingDevice *d = luax_checkrecordingdevice(L, 1);
int samples = d->getMaxSamples();
int samplerate = d->getSampleRate();
int bitdepth = d->getBitDepth();
int channels = d->getChannels();
if (lua_gettop(L) > 1)
{
int samples = (int) luaL_checkinteger(L, 2);
int sampleRate = (int) luaL_checkinteger(L, 3);
int bitDepth = (int) luaL_checkinteger(L, 4);
int channels = (int) luaL_checkinteger(L, 5);
luax_catchexcept(L, [&](){
lua_pushboolean(L, d->start(samples, sampleRate, bitDepth, channels));
});
samples = (int) luaL_checkinteger(L, 2);
samplerate = (int) luaL_optinteger(L, 3, RecordingDevice::DEFAULT_SAMPLE_RATE);
bitdepth = (int) luaL_optinteger(L, 4, RecordingDevice::DEFAULT_BIT_DEPTH);
channels = (int) (int) luaL_optinteger(L, 5, RecordingDevice::DEFAULT_CHANNELS);
}
else
luax_catchexcept(L, [&](){ lua_pushboolean(L, d->start()); });
bool success = false;
luax_catchexcept(L, [&]() { success = d->start(samples, samplerate, bitdepth, channels); });
luax_pushboolean(L, success);
return 1;
}
+7 -7
View File
@@ -493,15 +493,15 @@ int w_Source_getEffect(lua_State *L)
return 1;
}
int w_Source_getEffectsList(lua_State *L)
int w_Source_getActiveEffects(lua_State *L)
{
Source *t = luax_checksource(L, 1);
std::vector<std::string> list;
if (!t->getEffectsList(list))
return 0;
lua_createtable(L, 0, list.size());
for (unsigned int i = 0; i < list.size(); i++)
std::vector<std::string> list;
t->getActiveEffects(list);
lua_createtable(L, 0, (int) list.size());
for (int i = 0; i < (int) list.size(); i++)
{
lua_pushnumber(L, i + 1);
lua_pushstring(L, list[i].c_str());
@@ -624,7 +624,7 @@ static const luaL_Reg w_Source_functions[] =
{ "getFilter", w_Source_getFilter },
{ "setEffect", w_Source_setEffect },
{ "getEffect", w_Source_getEffect },
{ "getEffectsList", w_Source_getEffectsList },
{ "getActiveEffects", w_Source_getActiveEffects },
{ "getFreeBufferCount", w_Source_getFreeBufferCount },
{ "queue", w_Source_queue },
+6 -7
View File
@@ -65,7 +65,7 @@ bool File::open(Mode mode)
throw love::Exception("Could not open file %s. Does not exist.", filename.c_str());
// Check whether the write directory is set.
if ((mode == MODE_APPEND || mode == MODE_WRITE) && (PHYSFS_getWriteDir() == 0) && !hack_setupWriteDirectory())
if ((mode == MODE_APPEND || mode == MODE_WRITE) && (PHYSFS_getWriteDir() == nullptr) && !hack_setupWriteDirectory())
throw love::Exception("Could not set write directory.");
// File already open?
@@ -151,8 +151,6 @@ int64 File::read(void *dst, int64 size)
int64 max = (int64)PHYSFS_fileLength(file);
size = (size == ALL) ? max : size;
size = (size > max) ? max : size;
// Sadly, we'll have to clamp to 32 bits here
size = (size > LOVE_UINT32_MAX) ? LOVE_UINT32_MAX : size;
if (size < 0)
throw love::Exception("Invalid read size.");
@@ -160,6 +158,8 @@ int64 File::read(void *dst, int64 size)
#ifdef LOVE_USE_PHYSFS_2_1
int64 read = PHYSFS_readBytes(file, dst, (PHYSFS_uint64) size);
#else
// Sadly, we'll have to clamp to 32 bits here
size = (size > LOVE_UINT32_MAX) ? LOVE_UINT32_MAX : size;
int64 read = (int64)PHYSFS_read(file, dst, 1, (PHYSFS_uint32) size);
#endif
@@ -171,9 +171,6 @@ bool File::write(const void *data, int64 size)
if (!file || (mode != MODE_WRITE && mode != MODE_APPEND))
throw love::Exception("File is not opened for writing.");
// Another clamp, for the time being.
size = (size > LOVE_UINT32_MAX) ? LOVE_UINT32_MAX : size;
if (size < 0)
throw love::Exception("Invalid write size.");
@@ -181,6 +178,8 @@ bool File::write(const void *data, int64 size)
#ifdef LOVE_USE_PHYSFS_2_1
int64 written = PHYSFS_writeBytes(file, data, (PHYSFS_uint64) size);
#else
// Another clamp, for the time being.
size = (size > LOVE_UINT32_MAX) ? LOVE_UINT32_MAX : size;
int64 written = (int64) PHYSFS_write(file, data, 1, (PHYSFS_uint32) size);
#endif
@@ -191,7 +190,7 @@ bool File::write(const void *data, int64 size)
// Manually flush the buffer in BUFFER_LINE mode if we find a newline.
if (bufferMode == BUFFER_LINE && bufferSize > size)
{
if (memchr(data, '\n', (size_t) size) != NULL)
if (memchr(data, '\n', (size_t) size) != nullptr)
flush();
}
+49 -29
View File
@@ -366,7 +366,7 @@ 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;
Data *data = nullptr;
try
{
data = instance()->read(filename, len);
@@ -376,7 +376,7 @@ int w_read(lua_State *L)
return luax_ioError(L, "%s", e.what());
}
if (data == 0)
if (data == nullptr)
return luax_ioError(L, "File could not be read.");
// Push the string.
@@ -395,7 +395,7 @@ static int w_write_or_append(lua_State *L, File::Mode mode)
{
const char *filename = luaL_checkstring(L, 1);
const char *input = 0;
const char *input = nullptr;
size_t len = 0;
if (luax_istype(L, 2, love::Data::type))
@@ -459,11 +459,9 @@ int w_getDirectoryItems(lua_State *L)
int w_lines(lua_State *L)
{
File *file;
if (lua_isstring(L, 1))
{
file = instance()->newFile(lua_tostring(L, 1));
File *file = instance()->newFile(lua_tostring(L, 1));
bool success = false;
luax_catchexcept(L, [&](){ success = file->open(File::MODE_READ); });
@@ -488,7 +486,7 @@ int w_load(lua_State *L)
{
std::string filename = std::string(luaL_checkstring(L, 1));
Data *data = 0;
Data *data = nullptr;
try
{
data = instance()->read(filename.c_str());
@@ -634,6 +632,22 @@ int w_setCRequirePath(lua_State *L)
return 0;
}
static void replaceAll(std::string &str, const std::string &substr, const std::string &replacement)
{
std::vector<size_t> locations;
size_t pos = 0;
size_t sublen = substr.length();
while ((pos = str.find(substr, pos)) != std::string::npos)
{
locations.push_back(pos);
pos += sublen;
}
for (int i = (int) locations.size() - 1; i >= 0; i--)
str.replace(locations[i], sublen, replacement);
}
int loader(lua_State *L)
{
std::string modulename = luax_tostring(L, 1);
@@ -647,9 +661,7 @@ int loader(lua_State *L)
auto *inst = instance();
for (std::string element : inst->getRequirePath())
{
size_t pos = 0;
while ((pos = element.find('?', pos)) != std::string::npos)
element.replace(pos, 1, modulename);
replaceAll(element, "?", modulename);
if (inst->isFile(element.c_str()))
{
@@ -665,14 +677,16 @@ int loader(lua_State *L)
return 1;
}
inline const char *library_extension()
static const char *library_extensions[] =
{
#ifdef LOVE_WINDOWS
return ".dll";
".dll"
#elif defined(LOVE_MACOSX) || defined(LOVE_IOS)
".dylib", ".so"
#else
return ".so";
".so"
#endif
}
};
int extloader(lua_State *L)
{
@@ -694,25 +708,31 @@ int extloader(lua_State *L)
void *handle = nullptr;
auto *inst = instance();
for (std::string element : inst->getCRequirePath())
for (const std::string &el : inst->getCRequirePath())
{
// Replace ?? with the filename and extension
size_t pos = element.find("??");
if (pos != std::string::npos)
element.replace(pos, 2, tokenized_name + library_extension());
// Or ? with just the filename
pos = element.find('?');
if (pos != std::string::npos)
element.replace(pos, 1, tokenized_name);
for (const char *ext : library_extensions)
{
std::string element = el;
if (!inst->isFile(element.c_str()))
continue;
// Replace ?? with the filename and extension
replaceAll(element, "??", tokenized_name + ext);
// Now resolve the full path, as we're bypassing physfs for the next part.
element = inst->getRealDirectory(element.c_str()) + LOVE_PATH_SEPARATOR + element;
// And ? with just the filename
replaceAll(element, "?", tokenized_name);
if (!inst->isFile(element.c_str()))
continue;
// Now resolve the full path, as we're bypassing physfs for the next part.
std::string filepath = inst->getRealDirectory(element.c_str()) + LOVE_PATH_SEPARATOR + element;
handle = SDL_LoadObject(filepath.c_str());
// Can fail, for instance if it turned out the source was a zip
if (handle)
break;
}
handle = SDL_LoadObject(element.c_str());
// Can fail, for instance if it turned out the source was a zip
if (handle)
break;
}
+11 -9
View File
@@ -146,7 +146,12 @@ BMFontRasterizer::BMFontRasterizer(love::filesystem::FileData *fontdef, const st
// The parseConfig function will try to load any missing page images.
for (int i = 0; i < (int) imagelist.size(); i++)
{
if (imagelist[i]->getFormat() != PIXELFORMAT_RGBA8)
throw love::Exception("Only 32-bit RGBA images are supported in BMFonts.");
images[i] = imagelist[i];
}
std::string configtext((const char *) fontdef->getData(), fontdef->getSize());
@@ -296,29 +301,26 @@ GlyphData *BMFontRasterizer::getGlyphData(uint32 glyph) const
return new GlyphData(glyph, GlyphMetrics(), PIXELFORMAT_RGBA8);
const BMFontCharacter &c = it->second;
GlyphData *g = new GlyphData(glyph, c.metrics, PIXELFORMAT_RGBA8);
const auto &imagepair = images.find(c.page);
if (imagepair == images.end())
{
g->release();
return new GlyphData(glyph, GlyphMetrics(), PIXELFORMAT_RGBA8);
}
image::ImageData *imagedata = imagepair->second.get();
GlyphData *g = new GlyphData(glyph, c.metrics, PIXELFORMAT_RGBA8);
size_t pixelsize = imagedata->getPixelSize();
image::pixel *pixels = (image::pixel *) g->getData();
const image::pixel *ipixels = (const image::pixel *) imagedata->getData();
uint8 *pixels = (uint8 *) g->getData();
const uint8 *ipixels = (const uint8 *) imagedata->getData();
love::thread::Lock lock(imagedata->getMutex());
// Copy the subsection of the texture from the ImageData to the GlyphData.
for (int y = 0; y < c.metrics.height; y++)
{
size_t idindex = (c.y + y) * imagedata->getWidth() + c.x;
memcpy(&pixels[y * c.metrics.width], &ipixels[idindex], pixelsize * c.metrics.width);
size_t idindex = ((c.y + y) * imagedata->getWidth() + c.x) * pixelsize;
memcpy(&pixels[y * c.metrics.width * pixelsize], &ipixels[idindex], pixelsize * c.metrics.width);
}
return g;
+9 -12
View File
@@ -29,10 +29,7 @@ namespace love
namespace font
{
inline bool equal(const love::image::pixel &a, const love::image::pixel &b)
{
return (a.r == b.r && a.g == b.g && a.b == b.b && a.a == b.a);
}
static_assert(sizeof(Color) == 4, "sizeof(Color) must equal 4 bytes!");
ImageRasterizer::ImageRasterizer(love::image::ImageData *data, uint32 *glyphs, int numglyphs, int extraspacing, float pixeldensity)
: imageData(data)
@@ -79,17 +76,17 @@ GlyphData *ImageRasterizer::getGlyphData(uint32 glyph) const
// We don't want another thread modifying our ImageData mid-copy.
love::thread::Lock lock(imageData->getMutex());
love::image::pixel *gdpixels = (love::image::pixel *) g->getData();
love::image::pixel *imagepixels = (love::image::pixel *) imageData->getData();
Color *gdpixels = (Color *) g->getData();
const Color *imagepixels = (const Color *) imageData->getData();
// copy glyph pixels from imagedata to glyphdata
for (int i = 0; i < g->getWidth() * g->getHeight(); i++)
{
love::image::pixel p = imagepixels[it->second.x + (i % gm.width) + (imageData->getWidth() * (i / gm.width))];
Color p = imagepixels[it->second.x + (i % gm.width) + (imageData->getWidth() * (i / gm.width))];
// Use transparency instead of the spacer color
if (equal(p, spacer))
gdpixels[i].r = gdpixels[i].g = gdpixels[i].b = gdpixels[i].a = 0;
if (p == spacer)
gdpixels[i] = Color(0, 0, 0, 0);
else
gdpixels[i] = p;
}
@@ -99,7 +96,7 @@ GlyphData *ImageRasterizer::getGlyphData(uint32 glyph) const
void ImageRasterizer::load()
{
love::image::pixel *pixels = (love::image::pixel *) imageData->getData();
const Color *pixels = (const Color *) imageData->getData();
int imgw = imageData->getWidth();
int imgh = imageData->getHeight();
@@ -121,13 +118,13 @@ void ImageRasterizer::load()
start = end;
// Finds out where the first character starts
while (start < imgw && equal(pixels[start], spacer))
while (start < imgw && pixels[start] == spacer)
++start;
end = start;
// Find where glyph ends.
while (end < imgw && !equal(pixels[end], spacer))
while (end < imgw && pixels[end] != spacer)
++end;
if (start >= end)
+2 -2
View File
@@ -22,9 +22,9 @@
#define LOVE_FONT_IMAGE_RASTERIZER_H
// LOVE
#include "filesystem/File.h"
#include "font/Rasterizer.h"
#include "image/ImageData.h"
#include "common/Color.h"
#include <map>
@@ -76,7 +76,7 @@ private:
std::map<uint32, ImageGlyphData> imageGlyphs;
// Color used to identify glyph separation in the source ImageData
love::image::pixel spacer;
Color spacer;
}; // ImageRasterizer
+3 -3
View File
@@ -72,7 +72,7 @@ int w_newTrueTypeRasterizer(lua_State *L)
if (lua_type(L, 1) == LUA_TNUMBER || lua_isnone(L, 1))
{
// First argument is a number: use the default TrueType font.
int size = (int) luaL_optnumber(L, 1, 12);
int size = (int) luaL_optinteger(L, 1, 12);
const char *hintstr = lua_isnoneornil(L, 2) ? nullptr : luaL_checkstring(L, 2);
if (hintstr && !TrueTypeRasterizer::getConstant(hintstr, hinting))
@@ -98,7 +98,7 @@ int w_newTrueTypeRasterizer(lua_State *L)
else
d = filesystem::luax_getfiledata(L, 1);
int size = (int) luaL_optnumber(L, 2, 12);
int size = (int) luaL_optinteger(L, 2, 12);
const char *hintstr = lua_isnoneornil(L, 3) ? nullptr : luaL_checkstring(L, 3);
if (hintstr && !TrueTypeRasterizer::getConstant(hintstr, hinting))
@@ -180,7 +180,7 @@ int w_newImageRasterizer(lua_State *L)
image::ImageData *d = luax_checktype<image::ImageData>(L, 1);
std::string glyphs = luax_checkstring(L, 2);
int extraspacing = (int) luaL_optnumber(L, 3, 0);
int extraspacing = (int) luaL_optinteger(L, 3, 0);
float pixeldensity = (float) luaL_optnumber(L, 4, 1.0);
luax_catchexcept(L, [&](){ t = instance()->newImageRasterizer(d, glyphs, extraspacing, pixeldensity); });
+1 -1
View File
@@ -151,7 +151,7 @@ IndexDataType QuadIndices::getType(size_t s) const
return vertex::getIndexDataTypeFromMax(getIndexCount(s));
}
size_t QuadIndices::getElementSize()
size_t QuadIndices::getElementSize() const
{
return elementSize;
}
+1 -1
View File
@@ -238,7 +238,7 @@ public:
* Can be used with getPointer to calculate an offset into the array based
* on a number of elements.
**/
size_t getElementSize();
size_t getElementSize() const;
/**
* Returns the pointer to the Buffer.
+1 -37
View File
@@ -97,43 +97,7 @@ Canvas::Canvas(const Settings &settings)
throw love::Exception("%s textures are not supported on this system!", textypestr);
}
int maxsize = 0;
switch (texType)
{
case TEXTURE_2D:
maxsize = (int) caps.limits[Graphics::LIMIT_TEXTURE_SIZE];
if (pixelWidth > maxsize)
throw TextureTooLargeException("width", pixelWidth);
else if (pixelHeight > maxsize)
throw TextureTooLargeException("height", pixelHeight);
break;
case TEXTURE_VOLUME:
maxsize = (int) caps.limits[Graphics::LIMIT_VOLUME_TEXTURE_SIZE];
if (pixelWidth > maxsize)
throw TextureTooLargeException("width", pixelWidth);
else if (pixelHeight > maxsize)
throw TextureTooLargeException("height", pixelHeight);
else if (depth > maxsize)
throw TextureTooLargeException("depth", depth);
break;
case TEXTURE_2D_ARRAY:
maxsize = (int) caps.limits[Graphics::LIMIT_TEXTURE_SIZE];
if (pixelWidth > maxsize)
throw TextureTooLargeException("width", pixelWidth);
else if (pixelHeight > maxsize)
throw TextureTooLargeException("height", pixelHeight);
else if (layers > (int) caps.limits[Graphics::LIMIT_TEXTURE_LAYERS])
throw TextureTooLargeException("array layer count", layers);
break;
case TEXTURE_CUBE:
if (pixelWidth != pixelHeight)
throw love::Exception("Cubemap textures must have equal width and height.");
else if (pixelWidth > (int) caps.limits[Graphics::LIMIT_CUBE_TEXTURE_SIZE])
throw TextureTooLargeException("width", pixelWidth);
break;
default:
break;
}
validateDimensions(true);
canvasCount++;
}
+172 -52
View File
@@ -112,6 +112,7 @@ Graphics::Graphics()
, streamBufferState()
, projectionMatrix()
, canvasSwitchCount(0)
, drawCallsBatched(0)
, capabilities()
{
transformStack.reserve(16);
@@ -430,6 +431,128 @@ void Graphics::setCanvas(const RenderTargetsStrongRef &rts)
return setCanvas(targets);
}
void Graphics::setCanvas(const RenderTargets &rts)
{
DisplayState &state = states.back();
int ncanvases = (int) rts.colors.size();
if (ncanvases == 0 && rts.depthStencil.canvas == nullptr)
return setCanvas();
else if (ncanvases == 0)
throw love::Exception("At least one color render target is required when using a custom depth/stencil buffer.");
const auto &prevRTs = state.renderTargets;
if (ncanvases == (int) prevRTs.colors.size())
{
bool modified = false;
for (int i = 0; i < ncanvases; i++)
{
if (rts.colors[i].canvas != prevRTs.colors[i].canvas.get()
|| rts.colors[i].slice != prevRTs.colors[i].slice
|| rts.colors[i].mipmap != prevRTs.colors[i].mipmap)
{
modified = true;
break;
}
}
if (!modified && (rts.depthStencil.canvas != prevRTs.depthStencil.canvas
|| rts.depthStencil.slice != prevRTs.depthStencil.slice
|| rts.depthStencil.mipmap != prevRTs.depthStencil.mipmap))
{
modified = true;
}
if (rts.temporaryRTFlags != prevRTs.temporaryRTFlags)
modified = true;
if (!modified)
return;
}
if (ncanvases > capabilities.limits[LIMIT_MULTI_CANVAS])
throw love::Exception("This system can't simultaneously render to %d canvases.", ncanvases);
love::graphics::Canvas *firstcanvas = rts.colors[0].canvas;
bool multiformatsupported = capabilities.features[FEATURE_MULTI_CANVAS_FORMATS];
PixelFormat firstformat = firstcanvas->getPixelFormat();
if (isPixelFormatDepthStencil(firstformat))
throw love::Exception("Depth/stencil format Canvases must be used with the 'depthstencil' field of the table passed into setCanvas.");
if (rts.colors[0].mipmap < 0 || rts.colors[0].mipmap >= firstcanvas->getMipmapCount())
throw love::Exception("Invalid mipmap level %d.", rts.colors[0].mipmap + 1);
bool hasSRGBcanvas = firstformat == PIXELFORMAT_sRGBA8;
int pixelw = firstcanvas->getPixelWidth(rts.colors[0].mipmap);
int pixelh = firstcanvas->getPixelHeight(rts.colors[0].mipmap);
for (int i = 1; i < ncanvases; i++)
{
love::graphics::Canvas *c = rts.colors[i].canvas;
PixelFormat format = c->getPixelFormat();
int mip = rts.colors[i].mipmap;
if (mip < 0 || mip >= c->getMipmapCount())
throw love::Exception("Invalid mipmap level %d.", mip + 1);
if (c->getPixelWidth(mip) != pixelw || c->getPixelHeight(mip) != pixelh)
throw love::Exception("All canvases must have the same pixel dimensions.");
if (!multiformatsupported && format != firstformat)
throw love::Exception("This system doesn't support multi-canvas rendering with different canvas formats.");
if (c->getRequestedMSAA() != firstcanvas->getRequestedMSAA())
throw love::Exception("All Canvases must have the same MSAA value.");
if (isPixelFormatDepthStencil(format))
throw love::Exception("Depth/stencil format Canvases must be used with the 'depthstencil' field of the table passed into setCanvas.");
if (format == PIXELFORMAT_sRGBA8)
hasSRGBcanvas = true;
}
if (rts.depthStencil.canvas != nullptr)
{
love::graphics::Canvas *c = rts.depthStencil.canvas;
int mip = rts.depthStencil.mipmap;
if (!isPixelFormatDepthStencil(c->getPixelFormat()))
throw love::Exception("Only depth/stencil format Canvases can be used with the 'depthstencil' field of the table passed into setCanvas.");
if (c->getPixelWidth(mip) != pixelw || c->getPixelHeight(mip) != pixelh)
throw love::Exception("All canvases must have the same pixel dimensions.");
if (c->getRequestedMSAA() != firstcanvas->getRequestedMSAA())
throw love::Exception("All Canvases must have the same MSAA value.");
if (mip < 0 || mip >= c->getMipmapCount())
throw love::Exception("Invalid mipmap level %d.", mip + 1);
}
int w = firstcanvas->getWidth(rts.colors[0].mipmap);
int h = firstcanvas->getHeight(rts.colors[0].mipmap);
flushStreamDraws();
setCanvasInternal(rts, w, h, pixelw, pixelh, hasSRGBcanvas);
RenderTargetsStrongRef refs;
refs.colors.reserve(rts.colors.size());
for (auto c : rts.colors)
refs.colors.emplace_back(c.canvas, c.slice);
refs.depthStencil = RenderTargetStrongRef(rts.depthStencil.canvas, rts.depthStencil.slice);
refs.temporaryRTFlags = rts.temporaryRTFlags;
std::swap(state.renderTargets, refs);
canvasSwitchCount++;
}
Graphics::RenderTargets Graphics::getCanvas() const
{
const auto &curRTs = states.back().renderTargets;
@@ -757,6 +880,9 @@ Graphics::StreamVertexData Graphics::requestStreamDraw(const StreamDrawRequest &
}
}
if (state.vertexCount > 0)
drawCallsBatched++;
state.vertexCount += req.vertexCount;
state.indexCount += reqIndexCount;
@@ -829,7 +955,7 @@ void Graphics::printf(const std::vector<Font::ColoredString> &str, Font *font, f
* Primitives (points, shapes, lines).
**/
void Graphics::points(const float *coords, const Colorf *colors, size_t numpoints)
void Graphics::points(const Vector2 *positions, const Colorf *colors, size_t numpoints)
{
const Matrix4 &t = getTransform();
bool is2D = t.isAffine2DTransform();
@@ -843,9 +969,9 @@ void Graphics::points(const float *coords, const Colorf *colors, size_t numpoint
StreamVertexData data = requestStreamDraw(req);
if (is2D)
t.transformXY((Vector2 *) data.stream[0], (const Vector2 *) coords, req.vertexCount);
t.transformXY((Vector2 *) data.stream[0], positions, req.vertexCount);
else
t.transformXY0((Vector3 *) data.stream[0], (const Vector2 *) coords, req.vertexCount);
t.transformXY0((Vector3 *) data.stream[0], positions, req.vertexCount);
Color *colordata = (Color *) data.stream[1];
@@ -886,7 +1012,7 @@ int Graphics::calculateEllipsePoints(float rx, float ry) const
return std::max(points, 8);
}
void Graphics::polyline(const float *coords, size_t count)
void Graphics::polyline(const Vector2 *vertices, size_t count)
{
float halfwidth = getLineWidth() * 0.5f;
LineJoin linejoin = getLineJoin();
@@ -897,27 +1023,27 @@ void Graphics::polyline(const float *coords, size_t count)
if (linejoin == LINE_JOIN_NONE)
{
NoneJoinPolyline line;
line.render(coords, count, halfwidth, pixelsize, linestyle == LINE_SMOOTH);
line.render(vertices, count, halfwidth, pixelsize, linestyle == LINE_SMOOTH);
line.draw(this);
}
else if (linejoin == LINE_JOIN_BEVEL)
{
BevelJoinPolyline line;
line.render(coords, count, halfwidth, pixelsize, linestyle == LINE_SMOOTH);
line.render(vertices, count, halfwidth, pixelsize, linestyle == LINE_SMOOTH);
line.draw(this);
}
else if (linejoin == LINE_JOIN_MITER)
{
MiterJoinPolyline line;
line.render(coords, count, halfwidth, pixelsize, linestyle == LINE_SMOOTH);
line.render(vertices, count, halfwidth, pixelsize, linestyle == LINE_SMOOTH);
line.draw(this);
}
}
void Graphics::rectangle(DrawMode mode, float x, float y, float w, float h)
{
float coords[] = {x,y, x,y+h, x+w,y+h, x+w,y, x,y};
polygon(mode, coords, 5 * 2);
Vector2 coords[] = {Vector2(x,y), Vector2(x,y+h), Vector2(x+w,y+h), Vector2(x+w,y), Vector2(x,y)};
polygon(mode, coords, 5);
}
void Graphics::rectangle(DrawMode mode, float x, float y, float w, float h, float rx, float ry, int points)
@@ -940,44 +1066,43 @@ void Graphics::rectangle(DrawMode mode, float x, float y, float w, float h, floa
const float half_pi = static_cast<float>(LOVE_M_PI / 2);
float angle_shift = half_pi / ((float) points + 1.0f);
int num_coords = (points + 2) * 8;
float *coords = getScratchBuffer<float>(num_coords + 2);
int num_coords = (points + 2) * 4;
Vector2 *coords = getScratchBuffer<Vector2>(num_coords + 1);
float phi = .0f;
for (int i = 0; i <= points + 2; ++i, phi += angle_shift)
{
coords[2 * i + 0] = x + rx * (1 - cosf(phi));
coords[2 * i + 1] = y + ry * (1 - sinf(phi));
coords[i].x = x + rx * (1 - cosf(phi));
coords[i].y = y + ry * (1 - sinf(phi));
}
phi = half_pi;
for (int i = points + 2; i <= 2 * (points + 2); ++i, phi += angle_shift)
{
coords[2 * i + 0] = x + w - rx * (1 + cosf(phi));
coords[2 * i + 1] = y + ry * (1 - sinf(phi));
coords[i].x = x + w - rx * (1 + cosf(phi));
coords[i].y = y + ry * (1 - sinf(phi));
}
phi = 2 * half_pi;
for (int i = 2 * (points + 2); i <= 3 * (points + 2); ++i, phi += angle_shift)
{
coords[2 * i + 0] = x + w - rx * (1 + cosf(phi));
coords[2 * i + 1] = y + h - ry * (1 + sinf(phi));
coords[i].x = x + w - rx * (1 + cosf(phi));
coords[i].y = y + h - ry * (1 + sinf(phi));
}
phi = 3 * half_pi;
phi = 3 * half_pi;
for (int i = 3 * (points + 2); i <= 4 * (points + 2); ++i, phi += angle_shift)
{
coords[2 * i + 0] = x + rx * (1 - cosf(phi));
coords[2 * i + 1] = y + h - ry * (1 + sinf(phi));
coords[i].x = x + rx * (1 - cosf(phi));
coords[i].y = y + h - ry * (1 + sinf(phi));
}
coords[num_coords + 0] = coords[0];
coords[num_coords + 1] = coords[1];
coords[num_coords] = coords[0];
polygon(mode, coords, num_coords + 2);
polygon(mode, coords, num_coords + 1);
}
void Graphics::rectangle(DrawMode mode, float x, float y, float w, float h, float rx, float ry)
@@ -1002,17 +1127,16 @@ void Graphics::ellipse(DrawMode mode, float x, float y, float a, float b, int po
float angle_shift = (two_pi / points);
float phi = .0f;
float *coords = getScratchBuffer<float>(2 * (points + 1));
Vector2 *coords = getScratchBuffer<Vector2>(points + 1);
for (int i = 0; i < points; ++i, phi += angle_shift)
{
coords[2*i+0] = x + a * cosf(phi);
coords[2*i+1] = y + b * sinf(phi);
coords[i].x = x + a * cosf(phi);
coords[i].y = y + b * sinf(phi);
}
coords[2*points+0] = coords[0];
coords[2*points+1] = coords[1];
coords[points] = coords[0];
polygon(mode, coords, (points + 1) * 2);
polygon(mode, coords, points + 1);
}
void Graphics::ellipse(DrawMode mode, float x, float y, float a, float b)
@@ -1051,45 +1175,43 @@ void Graphics::arc(DrawMode drawmode, ArcMode arcmode, float x, float y, float r
float phi = angle1;
float *coords = nullptr;
Vector2 *coords = nullptr;
int num_coords = 0;
const auto createPoints = [&](float *coordinates)
const auto createPoints = [&](Vector2 *coordinates)
{
for (int i = 0; i <= points; ++i, phi += angle_shift)
{
coordinates[2 * i + 0] = x + radius * cosf(phi);
coordinates[2 * i + 1] = y + radius * sinf(phi);
coordinates[i].x = x + radius * cosf(phi);
coordinates[i].y = y + radius * sinf(phi);
}
};
if (arcmode == ARC_PIE)
{
num_coords = (points + 3) * 2;
coords = getScratchBuffer<float>(num_coords);
num_coords = points + 3;
coords = getScratchBuffer<Vector2>(num_coords);
coords[0] = coords[num_coords - 2] = x;
coords[1] = coords[num_coords - 1] = y;
coords[0] = coords[num_coords - 1] = Vector2(x, y);
createPoints(coords + 2);
createPoints(coords + 1);
}
else if (arcmode == ARC_OPEN)
{
num_coords = (points + 1) * 2;
coords = getScratchBuffer<float>(num_coords);
num_coords = points + 1;
coords = getScratchBuffer<Vector2>(num_coords);
createPoints(coords);
}
else // ARC_CLOSED
{
num_coords = (points + 2) * 2;
coords = getScratchBuffer<float>(num_coords);
num_coords = points + 2;
coords = getScratchBuffer<Vector2>(num_coords);
createPoints(coords);
// Connect the ends of the arc.
coords[num_coords - 2] = coords[0];
coords[num_coords - 1] = coords[1];
coords[num_coords - 1] = coords[0];
}
polygon(drawmode, coords, num_coords);
@@ -1107,13 +1229,10 @@ void Graphics::arc(DrawMode drawmode, ArcMode arcmode, float x, float y, float r
arc(drawmode, arcmode, x, y, radius, angle1, angle2, (int) (points + 0.5f));
}
/// @param mode the draw mode
/// @param coords the coordinate array
/// @param count the number of coordinates/size of the array
void Graphics::polygon(DrawMode mode, const float *coords, size_t count)
void Graphics::polygon(DrawMode mode, const Vector2 *coords, size_t count)
{
// coords is an array of a closed loop of vertices, i.e.
// coords[count-2] = coords[0], coords[count-1] = coords[1]
// coords[count-1] == coords[0]
if (mode == DRAW_LINE)
{
polyline(coords, count);
@@ -1127,14 +1246,14 @@ void Graphics::polygon(DrawMode mode, const float *coords, size_t count)
req.formats[0] = vertex::getSinglePositionFormat(is2D);
req.formats[1] = vertex::CommonFormat::RGBAub;
req.indexMode = vertex::TriangleIndexMode::FAN;
req.vertexCount = (int)count/2 - 1;
req.vertexCount = (int)count - 1;
StreamVertexData data = requestStreamDraw(req);
if (is2D)
t.transformXY((Vector2 *) data.stream[0], (const Vector2 *) coords, req.vertexCount);
t.transformXY((Vector2 *) data.stream[0], coords, req.vertexCount);
else
t.transformXY0((Vector3 *) data.stream[0], (const Vector2 *) coords, req.vertexCount);
t.transformXY0((Vector3 *) data.stream[0], coords, req.vertexCount);
Color c = toColor(getColor());
Color *colordata = (Color *) data.stream[1];
@@ -1158,6 +1277,7 @@ Graphics::Stats Graphics::getStats() const
stats.drawCalls++;
stats.canvasSwitches = canvasSwitchCount;
stats.drawCallsBatched = drawCallsBatched;
stats.canvases = Canvas::canvasCount;
stats.images = Image::imageCount;
stats.fonts = Font::fontCount;
+14 -12
View File
@@ -28,9 +28,9 @@
#include "common/Vector.h"
#include "common/Optional.h"
#include "common/int.h"
#include "common/Color.h"
#include "StreamBuffer.h"
#include "vertex.h"
#include "Color.h"
#include "Texture.h"
#include "Canvas.h"
#include "Font.h"
@@ -216,6 +216,7 @@ public:
struct Stats
{
int drawCalls;
int drawCallsBatched;
int canvasSwitches;
int shaderSwitches;
int canvases;
@@ -478,7 +479,7 @@ public:
Shader *getShader() const;
void setCanvas(RenderTarget rt, uint32 temporaryRTFlags);
virtual void setCanvas(const RenderTargets &rts) = 0;
void setCanvas(const RenderTargets &rts);
void setCanvas(const RenderTargetsStrongRef &rts);
virtual void setCanvas() = 0;
@@ -620,18 +621,16 @@ public:
void printf(const std::vector<Font::ColoredString> &str, Font *font, float wrap, Font::AlignMode align, const Matrix4 &m);
/**
* Draws a point at (x,y).
* @param x Point along x-axis.
* @param y Point along y-axis.
* Draws a series of points at the specified positions.
**/
void points(const float *coords, const Colorf *colors, size_t numpoints);
void points(const Vector2 *positions, const Colorf *colors, size_t numpoints);
/**
* Draws a series of lines connecting the given vertices.
* @param coords Vertex components (x1, y1, ..., xn, yn). If x1,y1 == xn,yn the line will be drawn closed.
* @param count Number of items in the array, i.e. count = 2 * n
* @param coords Vertex positions (v1, ..., vn). If v1 == vn the line will be drawn closed.
* @param count Number of vertices.
**/
void polyline(const float *coords, size_t count);
void polyline(const Vector2 *vertices, size_t count);
/**
* Draws a rectangle.
@@ -696,10 +695,10 @@ public:
/**
* Draws a polygon with an arbitrary number of vertices.
* @param mode The type of drawing (line/filled).
* @param coords Vertex components (x1, y1, x2, y2, etc.)
* @param count Coord array size
* @param coords Vertex positions.
* @param count Vertex array size.
**/
void polygon(DrawMode mode, const float *coords, size_t count);
void polygon(DrawMode mode, const Vector2 *vertices, size_t count);
/**
* Gets the graphics capabilities (feature support, limit values, and
@@ -861,6 +860,8 @@ protected:
virtual StreamBuffer *newStreamBuffer(BufferType type, size_t size) = 0;
virtual void setCanvasInternal(const RenderTargets &rts, int w, int h, int pixelw, int pixelh, bool hasSRGBcanvas) = 0;
virtual void initCapabilities() = 0;
virtual void getAPIStats(int &drawcalls, int &shaderswitches) const = 0;
@@ -900,6 +901,7 @@ protected:
std::vector<Canvas *> temporaryCanvases;
int canvasSwitchCount;
int drawCallsBatched;
Capabilities capabilities;
+78 -1
View File
@@ -564,11 +564,88 @@ bool Mesh::getDrawRange(int &start, int &count) const
return true;
}
void Mesh::draw(love::graphics::Graphics *gfx, const love::Matrix4 &m)
void Mesh::draw(Graphics *gfx, const love::Matrix4 &m)
{
drawInstanced(gfx, m, 1);
}
void Mesh::drawInstanced(Graphics *gfx, const Matrix4 &m, int instancecount)
{
if (vertexCount <= 0 || instancecount <= 0)
return;
if (instancecount > 1 && !gfx->getCapabilities().features[Graphics::FEATURE_INSTANCING])
throw love::Exception("Instancing is not supported on this system.");
gfx->flushStreamDraws();
if (Shader::isDefaultActive())
Shader::attachDefault(Shader::STANDARD_DEFAULT);
if (Shader::current && texture.get())
Shader::current->checkMainTexture(texture);
uint32 enabledattribs = 0;
uint32 instancedattribs = 0;
for (const auto &attrib : attachedAttributes)
{
if (!attrib.second.enabled)
continue;
love::graphics::Mesh *mesh = attrib.second.mesh;
int location = mesh->bindAttributeToShaderInput(attrib.second.index, attrib.first);
if (location >= 0)
{
uint32 bit = 1u << (uint32) location;
enabledattribs |= bit;
if (attrib.second.step == STEP_PER_INSTANCE)
instancedattribs |= bit;
}
}
// Not supported on all platforms or GL versions, I believe.
if (!(enabledattribs & ATTRIBFLAG_POS))
throw love::Exception("Mesh must have an enabled VertexPosition attribute to be drawn.");
bool useindexbuffer = useIndexBuffer && ibo != nullptr && elementCount > 0;
int start = 0;
int count = 0;
if (useindexbuffer)
{
// Make sure the index buffer isn't mapped (sends data to GPU if needed.)
ibo->unmap();
start = std::min(std::max(0, rangeStart), (int) elementCount - 1);
count = (int) elementCount;
if (rangeCount > 0)
count = std::min(count, rangeCount);
count = std::min(count, (int) elementCount - start);
}
else
{
start = std::min(std::max(0, rangeStart), (int) vertexCount - 1);
count = (int) vertexCount;
if (rangeCount > 0)
count = std::min(count, rangeCount);
count = std::min(count, (int) vertexCount - start);
}
Graphics::TempTransform transform(gfx, m);
if (count > 0)
drawInternal(start, count, instancecount, useindexbuffer, enabledattribs, instancedattribs);
}
size_t Mesh::getAttribFormatSize(const AttribFormat &format)
{
switch (format.type)
+4 -2
View File
@@ -195,11 +195,11 @@ public:
virtual int bindAttributeToShaderInput(int attributeindex, const std::string &inputname) = 0;
virtual void drawInstanced(Graphics *gfx, const Matrix4 &m, int instancecount) = 0;
// Implements Drawable.
void draw(Graphics *gfx, const Matrix4 &m) override;
void drawInstanced(Graphics *gfx, const Matrix4 &m, int instancecount);
static bool getConstant(const char *in, DrawMode &out);
static bool getConstant(DrawMode in, const char *&out);
@@ -223,6 +223,8 @@ protected:
void calculateAttributeSizes();
size_t getAttributeOffset(size_t attribindex) const;
virtual void drawInternal(int start, int count, int instancecount, bool useindexbuffer, uint32 attribflags, uint32 instancedattribflags) const = 0;
static size_t getAttribFormatSize(const AttribFormat &format);
static std::vector<AttribFormat> getDefaultVertexFormat();
+10 -3
View File
@@ -1048,15 +1048,21 @@ void ParticleSystem::update(float dt)
prevPosition = position;
}
bool ParticleSystem::prepareDraw(Graphics *gfx)
void ParticleSystem::draw(Graphics *gfx, const Matrix4 &m)
{
uint32 pCount = getCount();
if (pCount == 0 || texture.get() == nullptr || pMem == nullptr || buffer == nullptr)
return false;
return;
gfx->flushStreamDraws();
if (Shader::isDefaultActive())
Shader::attachDefault(Shader::STANDARD_DEFAULT);
if (Shader::current && texture.get())
Shader::current->checkMainTexture(texture);
const Vector2 *positions = texture->getQuad()->getVertexPositions();
const Vector2 *texcoords = texture->getQuad()->getVertexTexCoords();
@@ -1098,7 +1104,8 @@ bool ParticleSystem::prepareDraw(Graphics *gfx)
buffer->unmap();
return true;
Graphics::TempTransform transform(gfx, m);
drawInternal();
}
bool ParticleSystem::getConstant(const char *in, AreaSpreadDistribution &out)
+5 -2
View File
@@ -25,8 +25,8 @@
#include "common/int.h"
#include "common/math.h"
#include "common/Vector.h"
#include "common/Color.h"
#include "Drawable.h"
#include "Color.h"
#include "Quad.h"
#include "Texture.h"
#include "Buffer.h"
@@ -569,6 +569,9 @@ public:
**/
void update(float dt);
// Implements Drawable.
void draw(Graphics *gfx, const Matrix4 &m) override;
static bool getConstant(const char *in, AreaSpreadDistribution &out);
static bool getConstant(AreaSpreadDistribution in, const char *&out);
@@ -612,7 +615,7 @@ protected:
int quadIndex;
};
bool prepareDraw(Graphics *gfx);
virtual void drawInternal() const = 0;
// Pointer to the beginning of the allocated memory.
Particle *pMem;
+8 -8
View File
@@ -33,7 +33,7 @@ namespace love
namespace graphics
{
void Polyline::render(const float *coords, size_t count, size_t size_hint, float halfwidth, float pixel_size, bool draw_overdraw)
void Polyline::render(const Vector2 *coords, size_t count, size_t size_hint, float halfwidth, float pixel_size, bool draw_overdraw)
{
static std::vector<Vector2> anchors;
anchors.clear();
@@ -48,26 +48,26 @@ void Polyline::render(const float *coords, size_t count, size_t size_hint, float
halfwidth -= pixel_size * 0.3f;
// compute sleeve
bool is_looping = (coords[0] == coords[count - 2]) && (coords[1] == coords[count - 1]);
bool is_looping = (coords[0] == coords[count - 1]);
Vector2 s;
if (!is_looping) // virtual starting point at second point mirrored on first point
s = Vector2(coords[2] - coords[0], coords[3] - coords[1]);
s = coords[1] - coords[0];
else // virtual starting point at last vertex
s = Vector2(coords[0] - coords[count - 4], coords[1] - coords[count - 3]);
s = coords[0] - coords[count - 2];
float len_s = s.getLength();
Vector2 ns = s.getNormal(halfwidth / len_s);
Vector2 q, r(coords[0], coords[1]);
for (size_t i = 0; i + 3 < count; i += 2)
Vector2 q, r(coords[0]);
for (size_t i = 0; i + 1 < count; i++)
{
q = r;
r = Vector2(coords[i + 2], coords[i + 3]);
r = coords[i + 1];
renderEdge(anchors, normals, s, len_s, ns, q, r, halfwidth);
}
q = r;
r = is_looping ? Vector2(coords[2], coords[3]) : r + s;
r = is_looping ? coords[1] : r + s;
renderEdge(anchors, normals, s, len_s, ns, q, r, halfwidth);
vertex_count = normals.size();
+8 -8
View File
@@ -57,13 +57,13 @@ public:
/**
* @param vertices Vertices defining the core line segments
* @param count Number of coordinates (= size of the array vertices)
* @param count Number of vertices
* @param size_hint Expected number of vertices of the rendering sleeve around the core line.
* @param halfwidth linewidth / 2.
* @param pixel_size Dimension of one pixel on the screen in world coordinates.
* @param draw_overdraw Fake antialias the line.
*/
void render(const float *vertices, size_t count, size_t size_hint, float halfwidth, float pixel_size, bool draw_overdraw);
void render(const Vector2 *vertices, size_t count, size_t size_hint, float halfwidth, float pixel_size, bool draw_overdraw);
/** Draws the line on the screen
*/
@@ -112,9 +112,9 @@ public:
: Polyline(vertex::TriangleIndexMode::QUADS)
{}
void render(const float *vertices, size_t count, float halfwidth, float pixel_size, bool draw_overdraw)
void render(const Vector2 *vertices, size_t count, float halfwidth, float pixel_size, bool draw_overdraw)
{
Polyline::render(vertices, count, 2 * count - 4, halfwidth, pixel_size, draw_overdraw);
Polyline::render(vertices, count, 4 * count - 4, halfwidth, pixel_size, draw_overdraw);
// discard the first and last two vertices. (these are redundant)
for (size_t i = 0; i < vertex_count - 4; ++i)
@@ -149,9 +149,9 @@ class MiterJoinPolyline : public Polyline
{
public:
void render(const float *vertices, size_t count, float halfwidth, float pixel_size, bool draw_overdraw)
void render(const Vector2 *vertices, size_t count, float halfwidth, float pixel_size, bool draw_overdraw)
{
Polyline::render(vertices, count, count, halfwidth, pixel_size, draw_overdraw);
Polyline::render(vertices, count, 2 * count, halfwidth, pixel_size, draw_overdraw);
}
protected:
@@ -171,9 +171,9 @@ class BevelJoinPolyline : public Polyline
{
public:
void render(const float *vertices, size_t count, float halfwidth, float pixel_size, bool draw_overdraw)
void render(const Vector2 *vertices, size_t count, float halfwidth, float pixel_size, bool draw_overdraw)
{
Polyline::render(vertices, count, 2 * count - 4, halfwidth, pixel_size, draw_overdraw);
Polyline::render(vertices, count, 4 * count - 4, halfwidth, pixel_size, draw_overdraw);
}
protected:
+10 -10
View File
@@ -351,16 +351,16 @@ StringMap<Shader::ShaderStage, Shader::STAGE_MAX_ENUM> Shader::stageNames(Shader
StringMap<Shader::BuiltinUniform, Shader::BUILTIN_MAX_ENUM>::Entry Shader::builtinNameEntries[] =
{
{ "MainTex", BUILTIN_TEXTURE_MAIN },
{ "love_VideoYChannel", BUILTIN_TEXTURE_VIDEO_Y },
{ "love_VideoCbChannel", BUILTIN_TEXTURE_VIDEO_CB },
{ "love_VideoCrChannel", BUILTIN_TEXTURE_VIDEO_CR },
{ "TransformMatrix", BUILTIN_MATRIX_TRANSFORM },
{ "ProjectionMatrix", BUILTIN_MATRIX_PROJECTION },
{ "TransformProjectionMatrix", BUILTIN_MATRIX_TRANSFORM_PROJECTION },
{ "NormalMatrix", BUILTIN_MATRIX_NORMAL },
{ "love_PointSize", BUILTIN_POINT_SIZE },
{ "love_ScreenSize", BUILTIN_SCREEN_SIZE },
{ "MainTex", BUILTIN_TEXTURE_MAIN },
{ "love_VideoYChannel", BUILTIN_TEXTURE_VIDEO_Y },
{ "love_VideoCbChannel", BUILTIN_TEXTURE_VIDEO_CB },
{ "love_VideoCrChannel", BUILTIN_TEXTURE_VIDEO_CR },
{ "ViewSpaceFromLocal", BUILTIN_MATRIX_VIEW_FROM_LOCAL },
{ "ClipSpaceFromView", BUILTIN_MATRIX_CLIP_FROM_VIEW },
{ "ClipSpaceFromLocal", BUILTIN_MATRIX_CLIP_FROM_LOCAL },
{ "ViewNormalFromLocal", BUILTIN_MATRIX_VIEW_NORMAL_FROM_LOCAL },
{ "love_PointSize", BUILTIN_POINT_SIZE },
{ "love_ScreenSize", BUILTIN_SCREEN_SIZE },
};
StringMap<Shader::BuiltinUniform, Shader::BUILTIN_MAX_ENUM> Shader::builtinNames(Shader::builtinNameEntries, sizeof(Shader::builtinNameEntries));
+4 -4
View File
@@ -73,10 +73,10 @@ public:
BUILTIN_TEXTURE_VIDEO_Y,
BUILTIN_TEXTURE_VIDEO_CB,
BUILTIN_TEXTURE_VIDEO_CR,
BUILTIN_MATRIX_TRANSFORM,
BUILTIN_MATRIX_PROJECTION,
BUILTIN_MATRIX_TRANSFORM_PROJECTION,
BUILTIN_MATRIX_NORMAL,
BUILTIN_MATRIX_VIEW_FROM_LOCAL,
BUILTIN_MATRIX_CLIP_FROM_VIEW,
BUILTIN_MATRIX_CLIP_FROM_LOCAL,
BUILTIN_MATRIX_VIEW_NORMAL_FROM_LOCAL,
BUILTIN_POINT_SIZE,
BUILTIN_SCREEN_SIZE,
BUILTIN_MAX_ENUM
+62 -8
View File
@@ -62,9 +62,9 @@ SpriteBatch::SpriteBatch(Graphics *gfx, Texture *texture, int size, vertex::Usag
else
vertex_format = vertex::CommonFormat::XYf_STf_RGBAub;
format_stride = vertex::getFormatStride(vertex_format);
vertex_stride = vertex::getFormatStride(vertex_format);
size_t vertex_size = format_stride * 4 * size;
size_t vertex_size = vertex_stride * 4 * size;
array_buf = gfx->newBuffer(vertex_size, nullptr, BUFFER_VERTEX, usage, Buffer::MAP_EXPLICIT_RANGE_MODIFY);
}
@@ -95,7 +95,7 @@ int SpriteBatch::add(Quad *quad, const Matrix4 &m, int index /*= -1*/)
const Vector2 *quadtexcoords = quad->getVertexTexCoords();
// Always keep the VBO mapped when adding data (it'll be unmapped on draw.)
size_t offset = (index == -1 ? next : index) * format_stride * 4;
size_t offset = (index == -1 ? next : index) * vertex_stride * 4;
auto verts = (XYf_STf_RGBAub *) ((uint8 *) array_buf->map() + offset);
m.transformXY(verts, quadpositions, 4);
@@ -107,7 +107,7 @@ int SpriteBatch::add(Quad *quad, const Matrix4 &m, int index /*= -1*/)
verts[i].color = color;
}
array_buf->setMappedRangeModified(offset, format_stride * 4);
array_buf->setMappedRangeModified(offset, vertex_stride * 4);
// Increment counter.
if (index == -1)
@@ -141,7 +141,7 @@ int SpriteBatch::addLayer(int layer, Quad *quad, const Matrix4 &m, int index)
const Vector2 *quadtexcoords = quad->getVertexTexCoords();
// Always keep the VBO mapped when adding data (it'll be unmapped on draw.)
size_t offset = (index == -1 ? next : index) * format_stride * 4;
size_t offset = (index == -1 ? next : index) * vertex_stride * 4;
auto verts = (XYf_STPf_RGBAub *) ((uint8 *) array_buf->map() + offset);
m.transformXY(verts, quadpositions, 4);
@@ -154,7 +154,7 @@ int SpriteBatch::addLayer(int layer, Quad *quad, const Matrix4 &m, int index)
verts[i].color = color;
}
array_buf->setMappedRangeModified(offset, format_stride * 4);
array_buf->setMappedRangeModified(offset, vertex_stride * 4);
// Increment counter.
if (index == -1)
@@ -225,7 +225,7 @@ void SpriteBatch::setBufferSize(int newsize)
if (newsize == size)
return;
size_t vertex_size = format_stride * 4 * newsize;
size_t vertex_size = vertex_stride * 4 * newsize;
love::graphics::Buffer *new_array_buf = nullptr;
int new_next = std::min(next, newsize);
@@ -236,7 +236,7 @@ void SpriteBatch::setBufferSize(int newsize)
new_array_buf = gfx->newBuffer(vertex_size, nullptr, array_buf->getType(), array_buf->getUsage(), array_buf->getMapFlags());
// Copy as much of the old data into the new GLBuffer as can fit.
size_t copy_size = format_stride * 4 * new_next;
size_t copy_size = vertex_stride * 4 * new_next;
array_buf->copyTo(0, copy_size, new_array_buf, 0);
quad_indices = QuadIndices(gfx, newsize);
@@ -307,5 +307,59 @@ bool SpriteBatch::getDrawRange(int &start, int &count) const
return true;
}
void SpriteBatch::draw(Graphics *gfx, const Matrix4 &m)
{
using namespace vertex;
if (next == 0)
return;
gfx->flushStreamDraws();
if (texture.get())
{
if (Shader::isDefaultActive())
{
Shader::StandardShader defaultshader = Shader::STANDARD_DEFAULT;
if (texture->getTextureType() == TEXTURE_2D_ARRAY)
defaultshader = Shader::STANDARD_ARRAY;
Shader::attachDefault(defaultshader);
}
if (Shader::current)
Shader::current->checkMainTexture(texture);
}
// Make sure the VBO isn't mapped when we draw (sends data to GPU if needed.)
array_buf->unmap();
CommonFormat format = vertex_format;
if (!color_active)
{
if (format == CommonFormat::XYf_STPf_RGBAub)
format = CommonFormat::XYf_STPf;
else
format = CommonFormat::XYf_STf;
}
int start = std::min(std::max(0, range_start), next - 1);
int count = next;
if (range_count > 0)
count = std::min(count, range_count);
count = std::min(count, next - start);
size_t indexbytestart = quad_indices.getIndexCount(start) * quad_indices.getElementSize();
size_t indexcount = quad_indices.getIndexCount(count);
Graphics::TempTransform transform(gfx, m);
if (count > 0)
drawInternal(format, indexbytestart, indexcount);
}
} // graphics
} // love
+8 -2
View File
@@ -29,9 +29,10 @@
// LOVE
#include "common/math.h"
#include "common/Matrix.h"
#include "common/Color.h"
#include "Drawable.h"
#include "Color.h"
#include "Mesh.h"
#include "vertex.h"
namespace love
{
@@ -105,6 +106,9 @@ public:
void setDrawRange();
bool getDrawRange(int &start, int &count) const;
// Implements Drawable.
void draw(Graphics *gfx, const Matrix4 &m) override;
protected:
struct AttachedAttribute
@@ -119,6 +123,8 @@ protected:
**/
void setBufferSize(int newsize);
virtual void drawInternal(vertex::CommonFormat format, size_t indexbytestart, size_t indexcount) = 0;
StrongRef<Texture> texture;
// Max number of sprites in the batch.
@@ -133,7 +139,7 @@ protected:
bool color_active;
vertex::CommonFormat vertex_format;
size_t format_stride;
size_t vertex_stride;
love::graphics::Buffer *array_buf;
QuadIndices quad_indices;
+31
View File
@@ -235,5 +235,36 @@ int Text::getHeight(int index) const
return text_data[index].text_info.height;
}
void Text::draw(Graphics *gfx, const Matrix4 &m)
{
if (vbo == nullptr || draw_commands.empty())
return;
gfx->flushStreamDraws();
if (Shader::isDefaultActive())
Shader::attachDefault(Shader::STANDARD_DEFAULT);
if (Shader::current)
Shader::current->checkMainTextureType(TEXTURE_2D, false);
// Re-generate the text if the Font's texture cache was invalidated.
if (font->getTextureCacheID() != texture_cache_id)
regenerateVertices();
int totalverts = 0;
for (const Font::DrawCommand &cmd : draw_commands)
totalverts = std::max(cmd.startvertex + cmd.vertexcount, totalverts);
if ((size_t) totalverts / 4 > quadIndices.getSize())
quadIndices = QuadIndices(gfx, (size_t) totalverts / 4);
vbo->unmap(); // Make sure all pending data is flushed to the GPU.
Graphics::TempTransform transform(gfx, m);
drawInternal(draw_commands);
}
} // graphics
} // love
+5
View File
@@ -63,6 +63,9 @@ public:
**/
int getHeight(int index = 0) const;
// Implements Drawable.
void draw(love::graphics::Graphics *gfx, const Matrix4 &m) override;
protected:
struct TextData
@@ -80,6 +83,8 @@ protected:
void regenerateVertices();
void addTextData(const TextData &s);
virtual void drawInternal(const std::vector<Font::DrawCommand> &commands) const = 0;
StrongRef<Font> font;
Buffer *vbo;
QuadIndices quadIndices;
+57
View File
@@ -317,6 +317,63 @@ int Texture::getMipmapCount(int w, int h, int d)
return (int) log2(std::max(std::max(w, h), d)) + 1;
}
bool Texture::validateDimensions(bool throwException) const
{
bool success = true;
auto gfx = Module::getInstance<Graphics>(Module::M_GRAPHICS);
if (gfx == nullptr)
return false;
const Graphics::Capabilities &caps = gfx->getCapabilities();
int max2Dsize = (int) caps.limits[Graphics::LIMIT_TEXTURE_SIZE];
int max3Dsize = (int) caps.limits[Graphics::LIMIT_VOLUME_TEXTURE_SIZE];
int maxcubesize = (int) caps.limits[Graphics::LIMIT_CUBE_TEXTURE_SIZE];
int maxlayers = (int) caps.limits[Graphics::LIMIT_TEXTURE_LAYERS];
int largestdim = 0;
const char *largestname = nullptr;
if ((texType == TEXTURE_2D || texType == TEXTURE_2D_ARRAY) && (pixelWidth > max2Dsize || pixelHeight > max2Dsize))
{
success = false;
largestdim = std::max(pixelWidth, pixelHeight);
largestname = pixelWidth > pixelHeight ? "pixel width" : "pixel height";
}
else if (texType == TEXTURE_2D_ARRAY && layers > maxlayers)
{
success = false;
largestdim = layers;
largestname = "array layer count";
}
else if (texType == TEXTURE_CUBE && (pixelWidth > maxcubesize || pixelWidth != pixelHeight))
{
success = false;
largestdim = std::max(pixelWidth, pixelHeight);
largestname = pixelWidth > pixelHeight ? "pixel width" : "pixel height";
if (throwException && pixelWidth != pixelHeight)
throw love::Exception("Cubemap textures must have equal width and height.");
}
else if (texType == TEXTURE_VOLUME && (pixelWidth > max3Dsize || pixelHeight > max3Dsize || depth > max3Dsize))
{
success = false;
largestdim = std::max(std::max(pixelWidth, pixelHeight), depth);
if (largestdim == pixelWidth)
largestname = "pixel width";
else if (largestdim == pixelHeight)
largestname = "pixel height";
else
largestname = "pixel depth";
}
if (throwException && largestname != nullptr)
throw love::Exception("Cannot create texture: %s of %d is too large for this system.", largestname, largestdim);
return success;
}
bool Texture::getConstant(const char *in, TextureType &out)
{
return texTypes.find(in, out);
+2
View File
@@ -174,6 +174,8 @@ protected:
void initQuad();
void setGraphicsMemorySize(int64 size);
bool validateDimensions(bool throwException) const;
TextureType texType;
PixelFormat format;
+1 -1
View File
@@ -22,7 +22,7 @@
#define LOVE_GRAPHICS_OPENGL_CANVAS_H
#include "common/config.h"
#include "graphics/Color.h"
#include "common/Color.h"
#include "common/int.h"
#include "graphics/Canvas.h"
#include "graphics/Volatile.h"
+6 -118
View File
@@ -492,107 +492,9 @@ void Graphics::setDebug(bool enable)
::printf("OpenGL debug output enabled (LOVE_GRAPHICS_DEBUG=1)\n");
}
void Graphics::setCanvas(const RenderTargets &rts)
void Graphics::setCanvasInternal(const RenderTargets &rts, int w, int h, int pixelw, int pixelh, bool hasSRGBcanvas)
{
DisplayState &state = states.back();
int ncanvases = (int) rts.colors.size();
if (ncanvases == 0 && rts.depthStencil.canvas == nullptr)
return setCanvas();
else if (ncanvases == 0)
throw love::Exception("At least one color render target is required when using a custom depth/stencil buffer.");
const auto &prevRTs = state.renderTargets;
if (ncanvases == (int) prevRTs.colors.size())
{
bool modified = false;
for (int i = 0; i < ncanvases; i++)
{
if (rts.colors[i].canvas != prevRTs.colors[i].canvas.get()
|| rts.colors[i].slice != prevRTs.colors[i].slice
|| rts.colors[i].mipmap != prevRTs.colors[i].mipmap)
{
modified = true;
break;
}
}
if (!modified && (rts.depthStencil.canvas != prevRTs.depthStencil.canvas
|| rts.depthStencil.slice != prevRTs.depthStencil.slice
|| rts.depthStencil.mipmap != prevRTs.depthStencil.mipmap))
{
modified = true;
}
if (rts.temporaryRTFlags != prevRTs.temporaryRTFlags)
modified = true;
if (!modified)
return;
}
if (ncanvases > gl.getMaxRenderTargets())
throw love::Exception("This system can't simultaneously render to %d canvases.", ncanvases);
love::graphics::Canvas *firstcanvas = rts.colors[0].canvas;
bool multiformatsupported = Canvas::isMultiFormatMultiCanvasSupported();
PixelFormat firstformat = firstcanvas->getPixelFormat();
if (isPixelFormatDepthStencil(firstformat))
throw love::Exception("Depth/stencil format Canvases must be used with the 'depthstencil' field of the table passed into setCanvas.");
if (rts.colors[0].mipmap < 0 || rts.colors[0].mipmap >= firstcanvas->getMipmapCount())
throw love::Exception("Invalid mipmap level %d.", rts.colors[0].mipmap + 1);
bool hasSRGBcanvas = firstformat == PIXELFORMAT_sRGBA8;
int pixelwidth = firstcanvas->getPixelWidth(rts.colors[0].mipmap);
int pixelheight = firstcanvas->getPixelHeight(rts.colors[0].mipmap);
for (int i = 1; i < ncanvases; i++)
{
love::graphics::Canvas *c = rts.colors[i].canvas;
PixelFormat format = c->getPixelFormat();
int mip = rts.colors[i].mipmap;
if (mip < 0 || mip >= c->getMipmapCount())
throw love::Exception("Invalid mipmap level %d.", mip + 1);
if (c->getPixelWidth(mip) != pixelwidth || c->getPixelHeight(mip) != pixelheight)
throw love::Exception("All canvases must have the same pixel dimensions.");
if (!multiformatsupported && format != firstformat)
throw love::Exception("This system doesn't support multi-canvas rendering with different canvas formats.");
if (c->getRequestedMSAA() != firstcanvas->getRequestedMSAA())
throw love::Exception("All Canvases must have the same MSAA value.");
if (isPixelFormatDepthStencil(format))
throw love::Exception("Depth/stencil format Canvases must be used with the 'depthstencil' field of the table passed into setCanvas.");
if (format == PIXELFORMAT_sRGBA8)
hasSRGBcanvas = true;
}
if (rts.depthStencil.canvas != nullptr)
{
love::graphics::Canvas *c = rts.depthStencil.canvas;
int mip = rts.depthStencil.mipmap;
if (!isPixelFormatDepthStencil(c->getPixelFormat()))
throw love::Exception("Only depth/stencil format Canvases can be used with the 'depthstencil' field of the table passed into setCanvas.");
if (c->getPixelWidth(mip) != pixelwidth || c->getPixelHeight(mip) != pixelheight)
throw love::Exception("All canvases must have the same pixel dimensions.");
if (c->getRequestedMSAA() != firstcanvas->getRequestedMSAA())
throw love::Exception("All Canvases must have the same MSAA value.");
if (mip < 0 || mip >= c->getMipmapCount())
throw love::Exception("Invalid mipmap level %d.", mip + 1);
}
const DisplayState &state = states.back();
OpenGL::TempDebugGroup debuggroup("setCanvas(...)");
@@ -600,15 +502,13 @@ void Graphics::setCanvas(const RenderTargets &rts)
bindCachedFBO(rts);
gl.setViewport({0, 0, pixelwidth, pixelheight});
gl.setViewport({0, 0, pixelw, pixelh});
// Re-apply the scissor if it was active, since the rectangle passed to
// glScissor is affected by the viewport dimensions.
if (state.scissor)
setScissor(state.scissorRect);
int w = firstcanvas->getWidth(rts.colors[0].mipmap);
int h = firstcanvas->getHeight(rts.colors[0].mipmap);
projectionMatrix = Matrix4::ortho(0.0, (float) w, 0.0, (float) h);
// Make sure the correct sRGB setting is used when drawing to the canvases.
@@ -619,19 +519,6 @@ void Graphics::setCanvas(const RenderTargets &rts)
else if (!hasSRGBcanvas && gl.hasFramebufferSRGB())
gl.setFramebufferSRGB(false);
}
RenderTargetsStrongRef refs;
refs.colors.reserve(rts.colors.size());
for (auto c : rts.colors)
refs.colors.emplace_back(c.canvas, c.slice);
refs.depthStencil = RenderTargetStrongRef(rts.depthStencil.canvas, rts.depthStencil.slice);
refs.temporaryRTFlags = rts.temporaryRTFlags;
std::swap(state.renderTargets, refs);
canvasSwitchCount++;
}
void Graphics::setCanvas()
@@ -643,6 +530,7 @@ void Graphics::setCanvas()
OpenGL::TempDebugGroup debuggroup("setCanvas()");
flushStreamDraws();
endPass();
state.renderTargets = RenderTargetsStrongRef();
@@ -673,8 +561,6 @@ void Graphics::setCanvas()
void Graphics::endPass()
{
flushStreamDraws();
auto &rts = states.back().renderTargets;
love::graphics::Canvas *depthstencil = rts.depthStencil.canvas.get();
@@ -1013,6 +899,7 @@ void Graphics::present(void *screenshotCallbackData)
if (isCanvasActive())
throw love::Exception("present cannot be called while a Canvas is active.");
flushStreamDraws();
endPass();
gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, gl.getDefaultFBO());
@@ -1123,6 +1010,7 @@ void Graphics::present(void *screenshotCallbackData)
gl.stats.drawCalls = 0;
gl.stats.shaderSwitches = 0;
canvasSwitchCount = 0;
drawCallsBatched = 0;
}
void Graphics::setScissor(const Rect &rect)
+2 -2
View File
@@ -31,7 +31,7 @@
// LOVE
#include "graphics/Graphics.h"
#include "graphics/Color.h"
#include "common/Color.h"
#include "image/Image.h"
#include "image/ImageData.h"
@@ -97,7 +97,6 @@ public:
void setColor(Colorf c) override;
void setCanvas(const RenderTargets &rts) override;
void setCanvas() override;
void setScissor(const Rect &rect) override;
@@ -128,6 +127,7 @@ public:
private:
love::graphics::StreamBuffer *newStreamBuffer(BufferType type, size_t size) override;
void setCanvasInternal(const RenderTargets &rts, int w, int h, int pixelw, int pixelh, bool hasSRGBcanvas) override;
void initCapabilities() override;
void getAPIStats(int &drawcalls, int &shaderswitches) const override;
+1 -15
View File
@@ -221,22 +221,8 @@ bool Image::loadVolatile()
glGenTextures(1, &texture);
gl.bindTextureToUnit(this, 0, false);
bool loaddefault = false;
int max2Dsize = gl.getMax2DTextureSize();
int max3Dsize = gl.getMax3DTextureSize();
if ((texType == TEXTURE_2D || texType == TEXTURE_2D_ARRAY) && (pixelWidth > max2Dsize || pixelHeight > max2Dsize))
loaddefault = true;
else if (texType == TEXTURE_2D_ARRAY && layers > gl.getMaxTextureLayers())
loaddefault = true;
else if (texType == TEXTURE_CUBE && (pixelWidth > gl.getMaxCubeTextureSize() || pixelWidth != pixelHeight))
loaddefault = true;
else if (texType == TEXTURE_VOLUME && (pixelWidth > max3Dsize || pixelHeight > max3Dsize || depth > max3Dsize))
loaddefault = true;
// Use a default texture if the size is too big for the system.
if (loaddefault)
if (!validateDimensions(false))
{
loadDefaultTexture();
return true;
+8 -74
View File
@@ -91,94 +91,28 @@ int Mesh::bindAttributeToShaderInput(int attributeindex, const std::string &inpu
return attriblocation;
}
void Mesh::drawInstanced(love::graphics::Graphics *gfx, const love::Matrix4 &m, int instancecount)
void Mesh::drawInternal(int start, int count, int instancecount, bool useindexbuffer, uint32 attribflags, uint32 instancedattribflags) const
{
if (vertexCount <= 0 || instancecount <= 0)
return;
if (instancecount > 1 && !gl.isInstancingSupported())
throw love::Exception("Instancing is not supported on this system.");
gfx->flushStreamDraws();
if (Shader::isDefaultActive())
Shader::attachDefault(Shader::STANDARD_DEFAULT);
if (Shader::current && texture.get())
Shader::current->checkMainTexture(texture);
OpenGL::TempDebugGroup debuggroup("Mesh draw");
uint32 enabledattribs = 0;
uint32 instancedattribs = 0;
for (const auto &attrib : attachedAttributes)
{
if (!attrib.second.enabled)
continue;
love::graphics::Mesh *mesh = attrib.second.mesh;
int location = mesh->bindAttributeToShaderInput(attrib.second.index, attrib.first);
if (location >= 0)
{
uint32 bit = 1u << (uint32) location;
enabledattribs |= bit;
if (attrib.second.step == STEP_PER_INSTANCE)
instancedattribs |= bit;
}
}
// Not supported on all platforms or GL versions, I believe.
if (!(enabledattribs & ATTRIBFLAG_POS))
throw love::Exception("Mesh must have an enabled VertexPosition attribute to be drawn.");
gl.useVertexAttribArrays(enabledattribs, instancedattribs);
gl.useVertexAttribArrays(attribflags, instancedattribflags);
gl.bindTextureToUnit(texture, 0, false);
Graphics::TempTransform transform(gfx, m);
gl.prepareDraw();
if (useIndexBuffer && ibo && elementCount > 0)
GLenum gldrawmode = getGLDrawMode(drawMode);
if (useindexbuffer)
{
// Use the custom vertex map (index buffer) to draw the vertices.
gl.bindBuffer(BUFFER_INDEX, (GLuint) ibo->getHandle());
// Make sure the index buffer isn't mapped (sends data to GPU if needed.)
ibo->unmap();
int start = std::min(std::max(0, rangeStart), (int) elementCount - 1);
int count = (int) elementCount;
if (rangeCount > 0)
count = std::min(count, rangeCount);
count = std::min(count, (int) elementCount - start);
size_t elementsize = vertex::getIndexDataSize(elementDataType);
const void *indices = BUFFER_OFFSET(start * elementsize);
GLenum type = OpenGL::getGLIndexDataType(elementDataType);
if (count > 0)
gl.drawElements(getGLDrawMode(drawMode), count, type, indices, instancecount);
gl.bindBuffer(BUFFER_INDEX, (GLuint) ibo->getHandle());
gl.drawElements(gldrawmode, count, type, indices, instancecount);
}
else
{
int start = std::min(std::max(0, rangeStart), (int) vertexCount - 1);
int count = (int) vertexCount;
if (rangeCount > 0)
count = std::min(count, rangeCount);
count = std::min(count, (int) vertexCount - start);
// Normal non-indexed drawing (no custom vertex map.)
if (count > 0)
gl.drawArrays(getGLDrawMode(drawMode), start, count, instancecount);
gl.drawArrays(gldrawmode, start, count, instancecount);
}
}
+4 -1
View File
@@ -44,7 +44,10 @@ public:
virtual ~Mesh();
int bindAttributeToShaderInput(int attributeindex, const std::string &inputname) override;
void drawInstanced(Graphics *gfx, const Matrix4 &m, int instancecount) override;
protected:
void drawInternal(int start, int count, int instancecount, bool useindexbuffer, uint32 attribflags, uint32 instancedattribflags) const override;
private:
+2 -1
View File
@@ -261,7 +261,8 @@ void OpenGL::initVendor()
// http://feedback.wildfiregames.com/report/opengl/feature/GL_VENDOR
// http://stackoverflow.com/questions/2093594/opengl-extensions-available-on-different-android-devices
if (strstr(vstr, "ATI Technologies"))
// http://opengl.gpuinfo.org/gl_stats_caps_single.php?listreportsbycap=GL_VENDOR
if (strstr(vstr, "ATI Technologies") || strstr(vstr, "AMD") || strstr(vstr, "Advanced Micro Devices"))
vendor = VENDOR_AMD;
else if (strstr(vstr, "NVIDIA"))
vendor = VENDOR_NVIDIA;
+1 -1
View File
@@ -25,7 +25,7 @@
#include "common/config.h"
#include "common/int.h"
#include "common/math.h"
#include "graphics/Color.h"
#include "common/Color.h"
#include "graphics/Texture.h"
#include "graphics/vertex.h"
#include "graphics/depthstencil.h"
+1 -12
View File
@@ -50,21 +50,10 @@ ParticleSystem *ParticleSystem::clone()
return new ParticleSystem(*this);
}
void ParticleSystem::draw(Graphics *gfx, const Matrix4 &m)
void ParticleSystem::drawInternal() const
{
using namespace vertex;
if (!prepareDraw(gfx))
return;
Graphics::TempTransform transform(gfx, m);
if (Shader::isDefaultActive())
Shader::attachDefault(Shader::STANDARD_DEFAULT);
if (Shader::current && texture.get())
Shader::current->checkMainTexture(texture);
OpenGL::TempDebugGroup debuggroup("ParticleSystem draw");
gl.bindTextureToUnit(texture, 0, false);
+4 -1
View File
@@ -44,7 +44,10 @@ public:
virtual ~ParticleSystem();
ParticleSystem *clone() override;
void draw(Graphics *gfx, const Matrix4 &m) override;
private:
void drawInternal() const override;
}; // ParticleSystem
+4 -4
View File
@@ -855,14 +855,14 @@ void Shader::updateBuiltinUniforms()
// Only upload the matrices if they've changed.
if (memcmp(curxform.getElements(), lastTransformMatrix.getElements(), sizeof(float) * 16) != 0)
{
GLint location = builtinUniforms[BUILTIN_MATRIX_TRANSFORM];
GLint location = builtinUniforms[BUILTIN_MATRIX_VIEW_FROM_LOCAL];
if (location >= 0)
glUniformMatrix4fv(location, 1, GL_FALSE, curxform.getElements());
// Also upload the re-calculated normal matrix, if possible. The normal
// matrix is the transpose of the inverse of the rotation portion
// (top-left 3x3) of the transform matrix.
location = builtinUniforms[BUILTIN_MATRIX_NORMAL];
location = builtinUniforms[BUILTIN_MATRIX_VIEW_NORMAL_FROM_LOCAL];
if (location >= 0)
{
Matrix3 normalmatrix = Matrix3(curxform).transposedInverse();
@@ -875,7 +875,7 @@ void Shader::updateBuiltinUniforms()
if (memcmp(curproj.getElements(), lastProjectionMatrix.getElements(), sizeof(float) * 16) != 0)
{
GLint location = builtinUniforms[BUILTIN_MATRIX_PROJECTION];
GLint location = builtinUniforms[BUILTIN_MATRIX_CLIP_FROM_VIEW];
if (location >= 0)
glUniformMatrix4fv(location, 1, GL_FALSE, curproj.getElements());
@@ -885,7 +885,7 @@ void Shader::updateBuiltinUniforms()
if (tpmatrixneedsupdate)
{
GLint location = builtinUniforms[BUILTIN_MATRIX_TRANSFORM_PROJECTION];
GLint location = builtinUniforms[BUILTIN_MATRIX_CLIP_FROM_LOCAL];
if (location >= 0)
{
Matrix4 tp_matrix(curproj, curxform);
+9 -56
View File
@@ -51,52 +51,15 @@ SpriteBatch::~SpriteBatch()
{
}
void SpriteBatch::draw(Graphics *gfx, const Matrix4 &m)
void SpriteBatch::drawInternal(vertex::CommonFormat format, size_t indexbytestart, size_t indexcount)
{
using namespace vertex;
if (next == 0)
return;
gfx->flushStreamDraws();
if (texture.get())
{
if (Shader::isDefaultActive())
{
Shader::StandardShader defaultshader = Shader::STANDARD_DEFAULT;
if (texture->getTextureType() == TEXTURE_2D_ARRAY)
defaultshader = Shader::STANDARD_ARRAY;
Shader::attachDefault(defaultshader);
}
if (Shader::current)
Shader::current->checkMainTexture(texture);
}
OpenGL::TempDebugGroup debuggroup("SpriteBatch draw");
Graphics::TempTransform transform(gfx, m);
gl.bindTextureToUnit(texture, 0, false);
// Make sure the VBO isn't mapped when we draw (sends data to GPU if needed.)
array_buf->unmap();
CommonFormat format = vertex_format;
if (!color_active)
{
if (format == CommonFormat::XYf_STPf_RGBAub)
format = CommonFormat::XYf_STPf;
else
format = CommonFormat::XYf_STf;
}
uint32 enabledattribs = getFormatFlags(format);
gl.setVertexPointers(format, array_buf, format_stride, 0);
// We want attached attributes to override local attributes, so we should
// call this before binding attached attributes.
gl.setVertexPointers(format, array_buf, vertex_stride, 0);
for (const auto &it : attached_attributes)
{
@@ -114,26 +77,16 @@ void SpriteBatch::draw(Graphics *gfx, const Matrix4 &m)
}
gl.useVertexAttribArrays(enabledattribs);
gl.bindTextureToUnit(texture, 0, false);
gl.prepareDraw();
int start = std::min(std::max(0, range_start), next - 1);
gl.bindBuffer(BUFFER_INDEX, (GLuint) quad_indices.getBuffer()->getHandle());
int count = next;
if (range_count > 0)
count = std::min(count, range_count);
const void *indices = BUFFER_OFFSET(indexbytestart);
GLenum gltype = OpenGL::getGLIndexDataType(quad_indices.getType());
count = std::min(count, next - start);
if (count > 0)
{
gl.bindBuffer(BUFFER_INDEX, (GLuint) quad_indices.getBuffer()->getHandle());
const void *indices = BUFFER_OFFSET(start * quad_indices.getElementSize());
GLenum gltype = OpenGL::getGLIndexDataType(quad_indices.getType());
gl.drawElements(GL_TRIANGLES, (GLsizei) quad_indices.getIndexCount(count), gltype, indices);
}
gl.drawElements(GL_TRIANGLES, (GLsizei) indexcount, gltype, indices);
}
} // opengl
+3 -2
View File
@@ -37,8 +37,9 @@ public:
SpriteBatch(Graphics *gfx, Texture *texture, int size, vertex::Usage usage);
virtual ~SpriteBatch();
// Implements Drawable.
void draw(Graphics *gfx, const Matrix4 &m) override;
protected:
void drawInternal(vertex::CommonFormat format, size_t indexbytestart, size_t indexcount) override;
}; // SpriteBatch
+2 -30
View File
@@ -40,36 +40,10 @@ Text::~Text()
{
}
void Text::draw(Graphics *gfx, const Matrix4 &m)
void Text::drawInternal(const std::vector<Font::DrawCommand> &commands) const
{
if (vbo == nullptr || draw_commands.empty())
return;
gfx->flushStreamDraws();
if (Shader::isDefaultActive())
Shader::attachDefault(Shader::STANDARD_DEFAULT);
if (Shader::current)
Shader::current->checkMainTextureType(TEXTURE_2D, false);
OpenGL::TempDebugGroup debuggroup("Text object draw");
// Re-generate the text if the Font's texture cache was invalidated.
if (font->getTextureCacheID() != texture_cache_id)
regenerateVertices();
int totalverts = 0;
for (const Font::DrawCommand &cmd : draw_commands)
totalverts = std::max(cmd.startvertex + cmd.vertexcount, totalverts);
if ((size_t) totalverts / 4 > quadIndices.getSize())
quadIndices = QuadIndices(gfx, (size_t) totalverts / 4);
vbo->unmap(); // Make sure all pending data is flushed to the GPU.
Graphics::TempTransform transform(gfx, m);
gl.prepareDraw();
gl.setVertexPointers(Font::vertexFormat, vbo, 0);
@@ -82,14 +56,12 @@ void Text::draw(Graphics *gfx, const Matrix4 &m)
// We need a separate draw call for every section of the text which uses a
// different texture than the previous section.
for (const Font::DrawCommand &cmd : draw_commands)
for (const Font::DrawCommand &cmd : commands)
{
GLsizei count = (cmd.vertexcount / 4) * 6;
size_t offset = (cmd.startvertex / 4) * 6 * elemsize;
// TODO: Use glDrawElementsBaseVertex when supported?
gl.bindTextureToUnit(cmd.texture, 0, false);
gl.drawElements(GL_TRIANGLES, count, gltype, BUFFER_OFFSET(offset));
}
}
+3 -2
View File
@@ -38,8 +38,9 @@ public:
Text(love::graphics::Graphics *gfx, love::graphics::Font *font, const std::vector<Font::ColoredString> &text = {});
virtual ~Text();
// Implements Drawable.
void draw(love::graphics::Graphics *gfx, const Matrix4 &m) override;
protected:
void drawInternal(const std::vector<Font::DrawCommand> &commands) const override;
}; // Text
+1 -1
View File
@@ -22,7 +22,7 @@
// LOVE
#include "common/int.h"
#include "Color.h"
#include "common/Color.h"
// C
#include <stddef.h>
+1 -1
View File
@@ -46,7 +46,7 @@ int w_Canvas_renderTo(lua_State *L)
if (rt.canvas->getTextureType() != TEXTURE_2D)
{
rt.slice = (int) luaL_checknumber(L, 2) - 1;
rt.slice = (int) luaL_checkinteger(L, 2) - 1;
startidx++;
}
+160 -80
View File
@@ -24,6 +24,7 @@
#include "image/ImageData.h"
#include "image/Image.h"
#include "font/Rasterizer.h"
#include "filesystem/Filesystem.h"
#include "filesystem/wrap_Filesystem.h"
#include "video/VideoStream.h"
#include "image/wrap_Image.h"
@@ -321,7 +322,7 @@ int w_setCanvas(lua_State *L)
if (i == 1 && type != TEXTURE_2D)
{
target.slice = (int) luaL_checknumber(L, i + 1) - 1;
target.slice = (int) luaL_checkinteger(L, i + 1) - 1;
target.mipmap = (int) luaL_optinteger(L, i + 2, 1) - 1;
targets.colors.push_back(target);
break;
@@ -439,8 +440,54 @@ static void screenshotCallback(love::image::ImageData *i, Reference *ref, void *
delete ref;
}
static int screenshotSaveToFile(lua_State *L)
{
image::ImageData *id = image::luax_checkimagedata(L, 1);
const char *filename = luaL_checkstring(L, lua_upvalueindex(1));
const char *ext = luaL_checkstring(L, lua_upvalueindex(2));
image::FormatHandler::EncodedFormat format;
if (!image::ImageData::getConstant(ext, format))
return 0;
try
{
id->encode(format, filename, true);
}
catch (love::Exception &e)
{
printf("Screenshot encoding or saving failed: %s", e.what());
// Do nothing...
}
return 0;
}
int w_captureScreenshot(lua_State *L)
{
if (lua_isstring(L, 1))
{
std::string filename = luax_checkstring(L, 1);
std::string ext;
size_t dotpos = filename.rfind('.');
if (dotpos != std::string::npos)
ext = filename.substr(dotpos + 1);
std::transform(ext.begin(), ext.end(), ext.begin(), tolower);
image::FormatHandler::EncodedFormat format;
if (!image::ImageData::getConstant(ext.c_str(), format))
return luaL_error(L, "Invalid encoded image format: %s", ext.c_str());
lua_pushvalue(L, 1);
luax_pushstring(L, ext);
lua_pushcclosure(L, screenshotSaveToFile, 2);
lua_replace(L, 1);
}
luaL_checktype(L, 1, LUA_TFUNCTION);
Graphics::ScreenshotInfo info;
@@ -470,10 +517,10 @@ int w_setScissor(lua_State *L)
}
Rect rect;
rect.x = (int) luaL_checknumber(L, 1);
rect.y = (int) luaL_checknumber(L, 2);
rect.w = (int) luaL_checknumber(L, 3);
rect.h = (int) luaL_checknumber(L, 4);
rect.x = (int) luaL_checkinteger(L, 1);
rect.y = (int) luaL_checkinteger(L, 2);
rect.w = (int) luaL_checkinteger(L, 3);
rect.h = (int) luaL_checkinteger(L, 4);
if (rect.w < 0 || rect.h < 0)
return luaL_error(L, "Can't set scissor with negative width and/or height.");
@@ -485,10 +532,10 @@ int w_setScissor(lua_State *L)
int w_intersectScissor(lua_State *L)
{
Rect rect;
rect.x = (int) luaL_checknumber(L, 1);
rect.y = (int) luaL_checknumber(L, 2);
rect.w = (int) luaL_checknumber(L, 3);
rect.h = (int) luaL_checknumber(L, 4);
rect.x = (int) luaL_checkinteger(L, 1);
rect.y = (int) luaL_checkinteger(L, 2);
rect.w = (int) luaL_checkinteger(L, 3);
rect.h = (int) luaL_checkinteger(L, 4);
if (rect.w < 0 || rect.h < 0)
return luaL_error(L, "Can't set scissor with negative width and/or height.");
@@ -524,7 +571,7 @@ int w_stencil(lua_State *L)
return luaL_error(L, "Invalid stencil draw action: %s", actionstr);
}
int stencilvalue = (int) luaL_optnumber(L, 3, 1);
int stencilvalue = (int) luaL_optinteger(L, 3, 1);
// Fourth argument: whether to keep the contents of the stencil buffer.
OptionalInt stencilclear;
@@ -561,7 +608,7 @@ int w_setStencilTest(lua_State *L)
if (!getConstant(comparestr, compare))
return luaL_error(L, "Invalid compare mode: %s", comparestr);
comparevalue = (int) luaL_checknumber(L, 2);
comparevalue = (int) luaL_checkinteger(L, 2);
}
luax_catchexcept(L, [&](){ instance()->setStencilTest(compare, comparevalue); });
@@ -942,14 +989,14 @@ int w_newQuad(lua_State *L)
}
else if (luax_istype(L, 6, Texture::type))
{
layer = (int) luaL_checknumber(L, 5) - 1;
layer = (int) luaL_checkinteger(L, 5) - 1;
Texture *texture = luax_checktexture(L, 6);
sw = texture->getWidth();
sh = texture->getHeight();
}
else if (!lua_isnoneornil(L, 7))
{
layer = (int) luaL_checknumber(L, 5) - 1;
layer = (int) luaL_checkinteger(L, 5) - 1;
sw = luaL_checknumber(L, 6);
sh = luaL_checknumber(L, 7);
}
@@ -1030,7 +1077,7 @@ int w_newSpriteBatch(lua_State *L)
luax_checkgraphicscreated(L);
Texture *texture = luax_checktexture(L, 1);
int size = (int) luaL_optnumber(L, 2, 1000);
int size = (int) luaL_optinteger(L, 2, 1000);
vertex::Usage usage = vertex::USAGE_DYNAMIC;
if (lua_gettop(L) > 2)
{
@@ -1075,8 +1122,8 @@ int w_newCanvas(lua_State *L)
Canvas::Settings settings;
// check if width and height are given. else default to screen dimensions.
settings.width = (int) luaL_optnumber(L, 1, instance()->getWidth());
settings.height = (int) luaL_optnumber(L, 2, instance()->getHeight());
settings.width = (int) luaL_optinteger(L, 1, instance()->getWidth());
settings.height = (int) luaL_optinteger(L, 2, instance()->getHeight());
// Default to the screen's current pixel density scale.
settings.pixeldensity = instance()->getScreenPixelDensity();
@@ -1085,7 +1132,7 @@ int w_newCanvas(lua_State *L)
if (lua_isnumber(L, 3))
{
settings.layers = (int) luaL_checknumber(L, 3);
settings.layers = (int) luaL_checkinteger(L, 3);
settings.type = TEXTURE_2D_ARRAY;
startidx = 4;
}
@@ -1142,36 +1189,48 @@ int w_newCanvas(lua_State *L)
static int w_getShaderSource(lua_State *L, int startidx, bool gles, Shader::ShaderSource &source)
{
using namespace love::filesystem;
luax_checkgraphicscreated(L);
auto fs = Module::getInstance<Filesystem>(Module::M_FILESYSTEM);
// read any filepath arguments
for (int i = startidx; i < startidx + 2; i++)
{
if (!lua_isstring(L, i))
continue;
// call love.filesystem.isFile(arg_i)
luax_getfunction(L, "filesystem", "isFile");
lua_pushvalue(L, i);
lua_call(L, 1, 1);
bool isFile = luax_toboolean(L, -1);
lua_pop(L, 1);
if (isFile)
{
luax_getfunction(L, "filesystem", "read");
lua_pushvalue(L, i);
lua_call(L, 1, 1);
if (luax_cangetfiledata(L, i))
{
FileData *fd = luax_getfiledata(L, i);
lua_pushlstring(L, (const char *) fd->getData(), fd->getSize());
fd->release();
lua_replace(L, i);
}
continue;
}
size_t slen = 0;
const char *str = lua_tolstring(L, i, &slen);
if (fs != nullptr && fs->isFile(str))
{
FileData *fd = nullptr;
luax_catchexcept(L, [&](){ fd = fs->read(str); });
lua_pushlstring(L, (const char *) fd->getData(), fd->getSize());
fd->release();
lua_replace(L, i);
}
else
{
// Check if the argument looks like a filepath - we want a nicer
// error for misspelled filepath arguments.
size_t slen = 0;
const char *str = lua_tolstring(L, i, &slen);
if (slen > 0 && slen < 256 && !strchr(str, '\n'))
if (slen > 0 && slen < 64 && !strchr(str, '\n'))
{
const char *ext = strchr(str, '.');
if (ext != nullptr && !strchr(ext, ';') && !strchr(ext, ' '))
@@ -1352,7 +1411,7 @@ static Mesh *newStandardMesh(lua_State *L)
}
else
{
int count = (int) luaL_checknumber(L, 1);
int count = (int) luaL_checkinteger(L, 1);
luax_catchexcept(L, [&](){ t = instance()->newMesh(count, drawmode, usage); });
}
@@ -1397,7 +1456,7 @@ static Mesh *newCustomMesh(lua_State *L)
return nullptr;
}
format.components = (int) luaL_checknumber(L, -1);
format.components = (int) luaL_checkinteger(L, -1);
if (format.components <= 0 || format.components > 4)
{
luaL_error(L, "Number of vertex attribute components must be between 1 and 4 (got %d)", format.components);
@@ -1410,7 +1469,7 @@ static Mesh *newCustomMesh(lua_State *L)
if (lua_isnumber(L, 2))
{
int vertexcount = (int) luaL_checknumber(L, 2);
int vertexcount = (int) luaL_checkinteger(L, 2);
luax_catchexcept(L, [&](){ t = instance()->newMesh(vertexformat, vertexcount, drawmode, usage); });
}
else if (luax_istype(L, 2, Data::type))
@@ -2073,6 +2132,9 @@ int w_getStats(lua_State *L)
lua_pushinteger(L, stats.drawCalls);
lua_setfield(L, -2, "drawcalls");
lua_pushinteger(L, stats.drawCallsBatched);
lua_setfield(L, -2, "drawcallsbatched");
lua_pushinteger(L, stats.canvasSwitches);
lua_setfield(L, -2, "canvasswitches");
@@ -2135,7 +2197,7 @@ int w_drawLayer(lua_State *L)
{
Texture *texture = luax_checktexture(L, 1);
Quad *quad = nullptr;
int layer = (int) luaL_checknumber(L, 2) - 1;
int layer = (int) luaL_checkinteger(L, 2) - 1;
int startidx = 3;
if (luax_istype(L, startidx, Quad::type))
@@ -2279,23 +2341,23 @@ int w_points(lua_State *L)
if (args % 2 != 0 && !is_table_of_tables)
return luaL_error(L, "Number of vertex components must be a multiple of two");
int numpoints = args / 2;
int numpositions = args / 2;
if (is_table_of_tables)
numpoints = args;
numpositions = args;
float *coords = nullptr;
Vector2 *positions = nullptr;
Colorf *colors = nullptr;
if (is_table_of_tables)
{
size_t datasize = (sizeof(float) * 2 + sizeof(Colorf)) * numpoints;
size_t datasize = (sizeof(Vector2) + sizeof(Colorf)) * numpositions;
uint8 *data = instance()->getScratchBuffer<uint8>(datasize);
coords = (float *) data;
colors = (Colorf *) (data + sizeof(float) * numpoints * 2);
positions = (Vector2 *) data;
colors = (Colorf *) (data + sizeof(Vector2) * numpositions);
}
else
coords = instance()->getScratchBuffer<float>(numpoints * 2);
positions = instance()->getScratchBuffer<Vector2>(numpositions);
if (is_table)
{
@@ -2308,13 +2370,13 @@ int w_points(lua_State *L)
for (int j = 1; j <= 6; j++)
lua_rawgeti(L, -j, j);
coords[i * 2 + 0] = luax_tofloat(L, -6);
coords[i * 2 + 1] = luax_tofloat(L, -5);
positions[i].x = luax_tofloat(L, -6);
positions[i].y = luax_tofloat(L, -5);
colors[i].r = luaL_optnumber(L, -4, 1.0);
colors[i].g = luaL_optnumber(L, -3, 1.0);
colors[i].b = luaL_optnumber(L, -2, 1.0);
colors[i].a = luaL_optnumber(L, -1, 1.0);
colors[i].r = (float) luaL_optnumber(L, -4, 1.0);
colors[i].g = (float) luaL_optnumber(L, -3, 1.0);
colors[i].b = (float) luaL_optnumber(L, -2, 1.0);
colors[i].a = (float) luaL_optnumber(L, -1, 1.0);
lua_pop(L, 7);
}
@@ -2322,21 +2384,26 @@ int w_points(lua_State *L)
else
{
// points({x1, y1, x2, y2, ...})
for (int i = 0; i < args; i++)
for (int i = 0; i < numpositions; i++)
{
lua_rawgeti(L, 1, i + 1);
coords[i] = luax_tofloat(L, -1);
lua_pop(L, 1);
lua_rawgeti(L, 1, i * 2 + 1);
lua_rawgeti(L, 1, i * 2 + 2);
positions[i].x = luax_tofloat(L, -2);
positions[i].y = luax_tofloat(L, -1);
lua_pop(L, 2);
}
}
}
else
{
for (int i = 0; i < args; i++)
coords[i] = luax_tofloat(L, i + 1);
for (int i = 0; i < numpositions; i++)
{
positions[i].x = luax_tofloat(L, i * 2 + 1);
positions[i].y = luax_tofloat(L, i * 2 + 2);
}
}
luax_catchexcept(L, [&](){ instance()->points(coords, colors, numpoints); });
luax_catchexcept(L, [&](){ instance()->points(positions, colors, numpositions); });
return 0;
}
@@ -2355,24 +2422,31 @@ int w_line(lua_State *L)
else if (args < 4)
return luaL_error(L, "Need at least two vertices to draw a line");
float *coords = instance()->getScratchBuffer<float>(args);
int numvertices = args / 2;
Vector2 *coords = instance()->getScratchBuffer<Vector2>(numvertices);
if (is_table)
{
for (int i = 0; i < args; ++i)
for (int i = 0; i < numvertices; ++i)
{
lua_rawgeti(L, 1, i + 1);
coords[i] = luax_tofloat(L, -1);
lua_pop(L, 1);
lua_rawgeti(L, 1, (i * 2) + 1);
lua_rawgeti(L, 1, (i * 2) + 2);
coords[i].x = luax_tofloat(L, -2);
coords[i].y = luax_tofloat(L, -1);
lua_pop(L, 2);
}
}
else
{
for (int i = 0; i < args; ++i)
coords[i] = luax_tofloat(L, i + 1);
for (int i = 0; i < numvertices; ++i)
{
coords[i].x = luax_tofloat(L, (i * 2) + 1);
coords[i].y = luax_tofloat(L, (i * 2) + 2);
}
}
luax_catchexcept(L,
[&](){ instance()->polyline(coords, args); }
[&](){ instance()->polyline(coords, numvertices); }
);
return 0;
@@ -2403,7 +2477,7 @@ int w_rectangle(lua_State *L)
luax_catchexcept(L, [&](){ instance()->rectangle(mode, x, y, w, h, rx, ry); });
else
{
int points = (int) luaL_checknumber(L, 8);
int points = (int) luaL_checkinteger(L, 8);
luax_catchexcept(L, [&](){ instance()->rectangle(mode, x, y, w, h, rx, ry, points); });
}
@@ -2425,7 +2499,7 @@ int w_circle(lua_State *L)
luax_catchexcept(L, [&](){ instance()->circle(mode, x, y, radius); });
else
{
int points = (int) luaL_checknumber(L, 5);
int points = (int) luaL_checkinteger(L, 5);
luax_catchexcept(L, [&](){ instance()->circle(mode, x, y, radius, points); });
}
@@ -2448,7 +2522,7 @@ int w_ellipse(lua_State *L)
luax_catchexcept(L, [&](){ instance()->ellipse(mode, x, y, a, b); });
else
{
int points = (int) luaL_checknumber(L, 6);
int points = (int) luaL_checkinteger(L, 6);
luax_catchexcept(L, [&](){ instance()->ellipse(mode, x, y, a, b, points); });
}
@@ -2485,7 +2559,7 @@ int w_arc(lua_State *L)
luax_catchexcept(L, [&](){ instance()->arc(drawmode, arcmode, x, y, radius, angle1, angle2); });
else
{
int points = (int) luaL_checknumber(L, startidx + 5);
int points = (int) luaL_checkinteger(L, startidx + 5);
luax_catchexcept(L, [&](){ instance()->arc(drawmode, arcmode, x, y, radius, angle1, angle2, points); });
}
@@ -2513,28 +2587,34 @@ int w_polygon(lua_State *L)
else if (args < 6)
return luaL_error(L, "Need at least three vertices to draw a polygon");
int numvertices = args / 2;
// fetch coords
float *coords = instance()->getScratchBuffer<float>(args + 2);
Vector2 *coords = instance()->getScratchBuffer<Vector2>(numvertices + 1);
if (is_table)
{
for (int i = 0; i < args; ++i)
for (int i = 0; i < numvertices; ++i)
{
lua_rawgeti(L, 2, i + 1);
coords[i] = luax_tofloat(L, -1);
lua_pop(L, 1);
lua_rawgeti(L, 2, (i * 2) + 1);
lua_rawgeti(L, 2, (i * 2) + 2);
coords[i].x = luax_tofloat(L, -2);
coords[i].y = luax_tofloat(L, -1);
lua_pop(L, 2);
}
}
else
{
for (int i = 0; i < args; ++i)
coords[i] = luax_tofloat(L, i + 2);
for (int i = 0; i < numvertices; ++i)
{
coords[i].x = luax_tofloat(L, (i * 2) + 2);
coords[i].y = luax_tofloat(L, (i * 2) + 3);
}
}
// make a closed loop
coords[args] = coords[0];
coords[args+1] = coords[1];
coords[numvertices] = coords[0];
luax_catchexcept(L, [&](){ instance()->polygon(mode, coords, args+2); });
luax_catchexcept(L, [&](){ instance()->polygon(mode, coords, numvertices+1); });
return 0;
}
+16 -9
View File
@@ -72,11 +72,18 @@ GLSL.UNIFORMS = [[
// According to the GLSL ES 1.0 spec, uniform precision must match between stages,
// but we can't guarantee that highp is always supported in fragment shaders...
// We *really* don't want to use mediump for these in vertex shaders though.
uniform LOVE_HIGHP_OR_MEDIUMP mat4 TransformMatrix;
uniform LOVE_HIGHP_OR_MEDIUMP mat4 ProjectionMatrix;
uniform LOVE_HIGHP_OR_MEDIUMP mat4 TransformProjectionMatrix;
uniform LOVE_HIGHP_OR_MEDIUMP mat3 NormalMatrix;
uniform LOVE_HIGHP_OR_MEDIUMP vec4 love_ScreenSize;]]
uniform LOVE_HIGHP_OR_MEDIUMP mat4 ViewSpaceFromLocal;
uniform LOVE_HIGHP_OR_MEDIUMP mat4 ClipSpaceFromView;
uniform LOVE_HIGHP_OR_MEDIUMP mat4 ClipSpaceFromLocal;
uniform LOVE_HIGHP_OR_MEDIUMP mat3 ViewNormalFromLocal;
uniform LOVE_HIGHP_OR_MEDIUMP vec4 love_ScreenSize;
// Compatibility
#define TransformMatrix ViewSpaceFromLocal
#define ProjectionMatrix ClipSpaceFromView
#define TransformProjectionMatrix ClipSpaceFromLocal
#define NormalMatrix ViewNormalFromLocal
]]
GLSL.FUNCTIONS = [[
#ifdef GL_ES
@@ -222,13 +229,13 @@ attribute vec4 ConstantColor;
varying vec4 VaryingTexCoord;
varying vec4 VaryingColor;
vec4 position(mat4 transform_proj, vec4 vertpos);
vec4 position(mat4 clipSpaceFromLocal, vec4 localPosition);
void main() {
VaryingTexCoord = VertexTexCoord;
VaryingColor = gammaCorrectColor(VertexColor) * ConstantColor;
setPointSize();
love_Position = position(TransformProjectionMatrix, VertexPosition);
love_Position = position(ClipSpaceFromLocal, VertexPosition);
}]],
}
@@ -424,8 +431,8 @@ end
local defaultcode = {
vertex = [[
vec4 position(mat4 transform_proj, vec4 vertpos) {
return transform_proj * vertpos;
vec4 position(mat4 clipSpaceFromLocal, vec4 localPosition) {
return clipSpaceFromLocal * localPosition;
}]],
pixel = [[
vec4 effect(vec4 vcolor, Image tex, vec2 texcoord, vec2 pixcoord) {
+3 -3
View File
@@ -56,12 +56,12 @@ int w_Image_replacePixels(lua_State *L)
if (i->getTextureType() != TEXTURE_2D)
{
slice = (int) luaL_checknumber(L, 3) - 1;
slice = (int) luaL_checkinteger(L, 3) - 1;
if (!reloadmipmaps)
mipmap = (int) luaL_optnumber(L, 4, 1) - 1;
mipmap = (int) luaL_optinteger(L, 4, 1) - 1;
}
else if (!reloadmipmaps)
mipmap = (int) luaL_optnumber(L, 3, 1) - 1;
mipmap = (int) luaL_optinteger(L, 3, 1) - 1;
luax_catchexcept(L, [&](){ i->replacePixels(id, slice, mipmap, reloadmipmaps); });
return 0;
+3 -3
View File
@@ -386,7 +386,7 @@ int w_Mesh_setVertexMap(lua_State *L)
size_t datatypesize = vertex::getIndexDataSize(indextype);
int indexcount = (int) luaL_optnumber(L, 4, d->getSize() / datatypesize);
int indexcount = (int) luaL_optinteger(L, 4, d->getSize() / datatypesize);
if (indexcount < 1 || indexcount * datatypesize > d->getSize())
return luaL_error(L, "Invalid index count: %d", indexcount);
@@ -515,8 +515,8 @@ int w_Mesh_setDrawRange(lua_State *L)
t->setDrawRange();
else
{
int start = (int) luaL_checknumber(L, 2) - 1;
int count = (int) luaL_checknumber(L, 3);
int start = (int) luaL_checkinteger(L, 2) - 1;
int count = (int) luaL_checkinteger(L, 3);
luax_catchexcept(L, [&](){ t->setDrawRange(start, count); });
}
+1 -1
View File
@@ -699,7 +699,7 @@ int w_ParticleSystem_reset(lua_State *L)
int w_ParticleSystem_emit(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
int num = (int) luaL_checknumber(L, 2);
int num = (int) luaL_checkinteger(L, 2);
t->emit(num);
return 0;
}
+1 -1
View File
@@ -77,7 +77,7 @@ int w_Quad_getTextureDimensions(lua_State *L)
int w_Quad_setLayer(lua_State *L)
{
Quad *quad = luax_checkquad(L, 1);
int layer = (int) luaL_checknumber(L, 2) - 1;
int layer = (int) luaL_checkinteger(L, 2) - 1;
quad->setLayer(layer);
return 0;
}
+5 -5
View File
@@ -63,7 +63,7 @@ static inline int w_SpriteBatch_add_or_set(lua_State *L, SpriteBatch *t, int sta
static int w_SpriteBatch_addLayer_or_setLayer(lua_State *L, SpriteBatch *t, int startidx, int index)
{
Quad *quad = nullptr;
int layer = (int) luaL_checknumber(L, startidx) - 1;
int layer = (int) luaL_checkinteger(L, startidx) - 1;
startidx++;
if (luax_istype(L, startidx, Quad::type))
@@ -101,7 +101,7 @@ int w_SpriteBatch_add(lua_State *L)
int w_SpriteBatch_set(lua_State *L)
{
SpriteBatch *t = luax_checkspritebatch(L, 1);
int index = (int) luaL_checknumber(L, 2) - 1;
int index = (int) luaL_checkinteger(L, 2) - 1;
w_SpriteBatch_add_or_set(L, t, 3, index);
@@ -121,7 +121,7 @@ int w_SpriteBatch_addLayer(lua_State *L)
int w_SpriteBatch_setLayer(lua_State *L)
{
SpriteBatch *t = luax_checkspritebatch(L, 1);
int index = (int) luaL_checknumber(L, 2) - 1;
int index = (int) luaL_checkinteger(L, 2) - 1;
w_SpriteBatch_addLayer_or_setLayer(L, t, 3, index);
@@ -251,8 +251,8 @@ int w_SpriteBatch_setDrawRange(lua_State *L)
t->setDrawRange();
else
{
int start = (int) luaL_checknumber(L, 2) - 1;
int count = (int) luaL_checknumber(L, 3);
int start = (int) luaL_checkinteger(L, 2) - 1;
int count = (int) luaL_checkinteger(L, 3);
luax_catchexcept(L, [&](){ t->setDrawRange(start, count); });
}
+3 -3
View File
@@ -183,7 +183,7 @@ int w_Text_getFont(lua_State *L)
int w_Text_getWidth(lua_State *L)
{
Text *t = luax_checktext(L, 1);
int index = (int) luaL_optnumber(L, 2, 0) - 1;
int index = (int) luaL_optinteger(L, 2, 0) - 1;
lua_pushnumber(L, t->getWidth(index));
return 1;
}
@@ -191,7 +191,7 @@ int w_Text_getWidth(lua_State *L)
int w_Text_getHeight(lua_State *L)
{
Text *t = luax_checktext(L, 1);
int index = (int) luaL_optnumber(L, 2, 0) - 1;
int index = (int) luaL_optinteger(L, 2, 0) - 1;
lua_pushnumber(L, t->getHeight(index));
return 1;
}
@@ -199,7 +199,7 @@ int w_Text_getHeight(lua_State *L)
int w_Text_getDimensions(lua_State *L)
{
Text *t = luax_checktext(L, 1);
int index = (int) luaL_optnumber(L, 2, 0) - 1;
int index = (int) luaL_optinteger(L, 2, 0) - 1;
lua_pushnumber(L, t->getWidth(index));
lua_pushnumber(L, t->getHeight(index));
return 2;
@@ -18,20 +18,17 @@
* 3. This notice may not be removed or altered from any source distribution.
**/
#ifndef LOVE_IMAGE_MAGPIE_COMPRESSED_HANDLER_H
#define LOVE_IMAGE_MAGPIE_COMPRESSED_HANDLER_H
#pragma once
// LOVE
#include "filesystem/FileData.h"
#include "image/CompressedImageData.h"
#include "common/Object.h"
#include "filesystem/FileData.h"
#include "CompressedSlice.h"
namespace love
{
namespace image
{
namespace magpie
{
/**
* Base class for all CompressedImageData parser library interfaces.
@@ -45,8 +42,8 @@ public:
virtual ~CompressedFormatHandler() {}
/**
* Determines whether a particular FileData can be parsed as CompressedImageData
* by this handler.
* Determines whether a particular FileData can be parsed as
* CompressedImageData by this handler.
* @param data The data to parse.
**/
virtual bool canParse(const filesystem::FileData *data) = 0;
@@ -56,21 +53,18 @@ public:
* a single block of memory containing all the images.
*
* @param[in] filedata The data to parse.
* @param[out] images The list of sub-images generated. Byte data is a pointer
* to the returned data.
* @param[out] images The list of sub-images generated. Byte data is a
* pointer to the returned data.
* @param[out] format The format of the Compressed Data.
* @param[out] sRGB Whether the texture is sRGB-encoded.
*
* @return The single block of memory containing the parsed images.
**/
virtual StrongRef<CompressedImageData::Memory> parse(filesystem::FileData *filedata,
std::vector<StrongRef<CompressedImageData::Slice>> &images,
virtual StrongRef<CompressedMemory> parse(filesystem::FileData *filedata,
std::vector<StrongRef<CompressedSlice>> &images,
PixelFormat &format, bool &sRGB) = 0;
}; // CompressedFormatHandler
} // magpie
} // image
} // love
#endif // LOVE_IMAGE_MAGPIE_COMPRESSED_HANDLER_H
+46 -50
View File
@@ -27,58 +27,54 @@ namespace image
love::Type CompressedImageData::type("CompressedImageData", &Data::type);
CompressedImageData::Memory::Memory(size_t size)
: data(nullptr)
, size(size)
{
try
{
data = new uint8[size];
}
catch (std::exception &)
{
throw love::Exception("Out of memory.");
}
}
CompressedImageData::Memory::~Memory()
{
delete[] data;
}
CompressedImageData::Slice::Slice(PixelFormat format, int width, int height, Memory *memory, size_t offset, size_t size)
: memory(memory)
, offset(offset)
, dataSize(size)
{
this->format = format;
this->width = width;
this->height = height;
}
CompressedImageData::Slice::Slice(const Slice &s)
: memory(s.memory)
, offset(s.offset)
, dataSize(s.dataSize)
{
this->format = s.getFormat();
this->width = s.getWidth();
this->height = s.getHeight();
}
CompressedImageData::Slice::~Slice()
{
}
CompressedImageData::Slice *CompressedImageData::Slice::clone() const
{
return new Slice(*this);
}
CompressedImageData::CompressedImageData()
CompressedImageData::CompressedImageData(const std::list<CompressedFormatHandler *> &formats, love::filesystem::FileData *filedata)
: format(PIXELFORMAT_UNKNOWN)
, sRGB(false)
{
CompressedFormatHandler *parser = nullptr;
for (CompressedFormatHandler *handler : formats)
{
if (handler->canParse(filedata))
{
parser = handler;
break;
}
}
if (parser == nullptr)
throw love::Exception("Could not parse compressed data: Unknown format.");
memory = parser->parse(filedata, dataImages, format, sRGB);
if (memory == nullptr)
throw love::Exception("Could not parse compressed data.");
if (format == PIXELFORMAT_UNKNOWN)
throw love::Exception("Could not parse compressed data: Unknown format.");
if (dataImages.size() == 0 || memory->size == 0)
throw love::Exception("Could not parse compressed data: No valid data?");
}
CompressedImageData::CompressedImageData(const CompressedImageData &c)
: format(c.format)
, sRGB(c.sRGB)
{
memory.set(new CompressedMemory(c.memory->size), Acquire::NORETAIN);
memcpy(memory->data, c.memory->data, memory->size);
for (const auto &i : c.dataImages)
{
auto slice = new CompressedSlice(i->getFormat(), i->getWidth(), i->getHeight(), memory, i->getOffset(), i->getSize());
dataImages.push_back(slice);
slice->release();
}
}
CompressedImageData *CompressedImageData::clone() const
{
return new CompressedImageData(*this);
}
CompressedImageData::~CompressedImageData()
@@ -143,7 +139,7 @@ bool CompressedImageData::isSRGB() const
return sRGB;
}
CompressedImageData::Slice *CompressedImageData::getSlice(int slice, int miplevel) const
CompressedSlice *CompressedImageData::getSlice(int slice, int miplevel) const
{
checkSliceExists(slice, miplevel);
+12 -47
View File
@@ -25,10 +25,13 @@
#include "common/StringMap.h"
#include "common/int.h"
#include "common/pixelformat.h"
#include "ImageDataBase.h"
#include "filesystem/FileData.h"
#include "CompressedSlice.h"
#include "CompressedFormatHandler.h"
// STL
#include <vector>
#include <list>
namespace love
{
@@ -44,53 +47,16 @@ class CompressedImageData : public Data
{
public:
class Memory : public Object
{
public:
Memory(size_t size);
virtual ~Memory();
uint8 *data;
size_t size;
}; // Memory
// Compressed image data can have multiple mipmap levels, each represented
// by a sub-image.
class Slice : public ImageDataBase
{
public:
Slice(PixelFormat format, int width, int height, Memory *memory, size_t offset, size_t size);
Slice(const Slice &slice);
virtual ~Slice();
Slice *clone() const override;
void *getData() const override { return memory->data + offset; }
size_t getSize() const override { return dataSize; }
bool isSRGB() const override { return sRGB; }
size_t getOffset() const { return offset; }
private:
StrongRef<Memory> memory;
size_t offset;
size_t dataSize;
bool sRGB;
}; // Slice
static love::Type type;
CompressedImageData();
CompressedImageData(const std::list<CompressedFormatHandler *> &formats, love::filesystem::FileData *filedata);
CompressedImageData(const CompressedImageData &c);
virtual ~CompressedImageData();
// Implements Data.
virtual CompressedImageData *clone() const = 0;
virtual void *getData() const;
virtual size_t getSize() const;
CompressedImageData *clone() const override;
void *getData() const override;
size_t getSize() const override;
/**
* Gets the number of mipmaps in this Compressed Image Data.
@@ -130,19 +96,18 @@ public:
bool isSRGB() const;
Slice *getSlice(int slice, int miplevel) const;
CompressedSlice *getSlice(int slice, int miplevel) const;
protected:
PixelFormat format;
bool sRGB;
// Single block of memory containing all of the sub-images.
StrongRef<Memory> memory;
StrongRef<CompressedMemory> memory;
// Texture info for each mipmap level.
std::vector<StrongRef<Slice>> dataImages;
std::vector<StrongRef<CompressedSlice>> dataImages;
void checkSliceExists(int slice, int miplevel) const;
@@ -18,37 +18,61 @@
* 3. This notice may not be removed or altered from any source distribution.
**/
#ifndef LOVE_IMAGE_MAGPIE_COMPRESSED_IMAGE_DATA_H
#define LOVE_IMAGE_MAGPIE_COMPRESSED_IMAGE_DATA_H
// LOVE
#include "CompressedFormatHandler.h"
#include "filesystem/FileData.h"
#include "image/CompressedImageData.h"
// C++
#include <list>
#include "CompressedSlice.h"
#include "common/Exception.h"
namespace love
{
namespace image
{
namespace magpie
CompressedMemory::CompressedMemory(size_t size)
: data(nullptr)
, size(size)
{
try
{
data = new uint8[size];
}
catch (std::exception &)
{
throw love::Exception("Out of memory.");
}
}
class CompressedImageData : public love::image::CompressedImageData
CompressedMemory::~CompressedMemory()
{
public:
delete[] data;
}
CompressedImageData(std::list<CompressedFormatHandler *> formats, love::filesystem::FileData *filedata);
CompressedImageData(const CompressedImageData &c);
virtual ~CompressedImageData();
CompressedSlice::CompressedSlice(PixelFormat format, int width, int height, CompressedMemory *memory, size_t offset, size_t size)
: memory(memory)
, offset(offset)
, dataSize(size)
{
this->format = format;
this->width = width;
this->height = height;
}
virtual CompressedImageData *clone() const;
}; // CompressedImageData
CompressedSlice::CompressedSlice(const CompressedSlice &s)
: memory(s.memory)
, offset(s.offset)
, dataSize(s.dataSize)
{
this->format = s.getFormat();
this->width = s.getWidth();
this->height = s.getHeight();
}
CompressedSlice::~CompressedSlice()
{
}
CompressedSlice *CompressedSlice::clone() const
{
return new CompressedSlice(*this);
}
} // magpie
} // image
} // love
#endif // LOVE_IMAGE_MAGPIE_COMPRESSED_IMAGE_DATA_H
+72
View File
@@ -0,0 +1,72 @@
/**
* Copyright (c) 2006-2017 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.
**/
#pragma once
// LOVE
#include "common/int.h"
#include "common/pixelformat.h"
#include "common/Object.h"
#include "ImageDataBase.h"
namespace love
{
namespace image
{
class CompressedMemory : public Object
{
public:
CompressedMemory(size_t size);
virtual ~CompressedMemory();
uint8 *data;
size_t size;
}; // CompressedMemory
// Compressed image data can have multiple mipmap levels, each represented by a
// sub-image.
class CompressedSlice : public ImageDataBase
{
public:
CompressedSlice(PixelFormat format, int width, int height, CompressedMemory *memory, size_t offset, size_t size);
CompressedSlice(const CompressedSlice &slice);
virtual ~CompressedSlice();
CompressedSlice *clone() const override;
void *getData() const override { return memory->data + offset; }
size_t getSize() const override { return dataSize; }
bool isSRGB() const override { return sRGB; }
size_t getOffset() const { return offset; }
private:
StrongRef<CompressedMemory> memory;
size_t offset;
size_t dataSize;
bool sRGB;
}; // CompressedSlice
} // image
} // love
@@ -26,8 +26,6 @@ namespace love
{
namespace image
{
namespace magpie
{
FormatHandler::FormatHandler()
{
@@ -42,7 +40,7 @@ bool FormatHandler::canDecode(love::filesystem::FileData* /*data*/)
return false;
}
bool FormatHandler::canEncode(PixelFormat /*rawFormat*/, ImageData::EncodedFormat /*encodedFormat*/)
bool FormatHandler::canEncode(PixelFormat /*rawFormat*/, EncodedFormat /*encodedFormat*/)
{
return false;
}
@@ -52,7 +50,7 @@ FormatHandler::DecodedImage FormatHandler::decode(love::filesystem::FileData* /*
throw love::Exception("Image decoding is not implemented for this format backend.");
}
FormatHandler::EncodedImage FormatHandler::encode(const DecodedImage& /*img*/, ImageData::EncodedFormat /*format*/)
FormatHandler::EncodedImage FormatHandler::encode(const DecodedImage& /*img*/, EncodedFormat /*format*/)
{
throw love::Exception("Image encoding is not implemented for this format backend.");
}
@@ -62,6 +60,5 @@ void FormatHandler::free(unsigned char *mem)
delete[] mem;
}
} // magpie
} // image
} // love
@@ -18,20 +18,17 @@
* 3. This notice may not be removed or altered from any source distribution.
**/
#ifndef LOVE_IMAGE_MAGPIE_FORMAT_HANDLER_H
#define LOVE_IMAGE_MAGPIE_FORMAT_HANDLER_H
#pragma once
// LOVE
#include "image/ImageData.h"
#include "filesystem/FileData.h"
#include "common/Object.h"
#include "common/pixelformat.h"
#include "filesystem/FileData.h"
namespace love
{
namespace image
{
namespace magpie
{
/**
* Base class for all ImageData encoder/decoder library interfaces.
@@ -41,6 +38,13 @@ class FormatHandler : public love::Object
{
public:
enum EncodedFormat
{
ENCODED_TGA,
ENCODED_PNG,
ENCODED_MAX_ENUM
};
// Raw RGBA pixel data.
struct DecodedImage
{
@@ -76,7 +80,7 @@ public:
/**
* Whether this format handler can encode to a particular format.
**/
virtual bool canEncode(PixelFormat rawFormat, ImageData::EncodedFormat encodedFormat);
virtual bool canEncode(PixelFormat rawFormat, EncodedFormat encodedFormat);
/**
* Decodes an image from its encoded form into raw pixel data.
@@ -91,7 +95,7 @@ public:
* @param format The format to encode to.
* @return The encoded image data.
**/
virtual EncodedImage encode(const DecodedImage &img, ImageData::EncodedFormat format);
virtual EncodedImage encode(const DecodedImage &img, EncodedFormat format);
/**
* Frees memory allocated by the format handler.
@@ -100,8 +104,5 @@ public:
}; // FormatHandler
} // magpie
} // image
} // love
#endif // LOVE_IMAGE_MAGPIE_FORMAT_HANDLER_H
+84
View File
@@ -20,6 +20,17 @@
// LOVE
#include "Image.h"
#include "common/config.h"
#include "magpie/PNGHandler.h"
#include "magpie/STBHandler.h"
#include "magpie/EXRHandler.h"
#include "magpie/ddsHandler.h"
#include "magpie/PVRHandler.h"
#include "magpie/KTXHandler.h"
#include "magpie/PKMHandler.h"
#include "magpie/ASTCHandler.h"
namespace love
{
@@ -28,6 +39,79 @@ namespace image
love::Type Image::type("image", &Module::type);
Image::Image()
{
using namespace magpie;
halfInit(); // Makes sure half-float conversions can be used.
formatHandlers = {
new PNGHandler,
new STBHandler,
new EXRHandler,
};
compressedFormatHandlers = {
new DDSHandler,
new PVRHandler,
new KTXHandler,
new PKMHandler,
new ASTCHandler,
};
}
Image::~Image()
{
// ImageData objects reference the FormatHandlers in our list, so we should
// release them instead of deleting them completely here.
for (FormatHandler *handler : formatHandlers)
handler->release();
for (CompressedFormatHandler *handler : compressedFormatHandlers)
handler->release();
}
const char *Image::getName() const
{
return "love.image.magpie";
}
love::image::ImageData *Image::newImageData(love::filesystem::FileData *data)
{
return new ImageData(data);
}
love::image::ImageData *Image::newImageData(int width, int height, PixelFormat format)
{
return new ImageData(width, height, format);
}
love::image::ImageData *Image::newImageData(int width, int height, PixelFormat format, void *data, bool own)
{
return new ImageData(width, height, format, data, own);
}
love::image::CompressedImageData *Image::newCompressedData(love::filesystem::FileData *data)
{
return new CompressedImageData(compressedFormatHandlers, data);
}
bool Image::isCompressed(love::filesystem::FileData *data)
{
for (CompressedFormatHandler *handler : compressedFormatHandlers)
{
if (handler->canParse(data))
return true;
}
return false;
}
const std::list<FormatHandler *> &Image::getFormatHandlers() const
{
return formatHandlers;
}
ImageData *Image::newPastedImageData(ImageData *src, int sx, int sy, int w, int h)
{
ImageData *res = newImageData(w, h, src->getFormat());
+20 -7
View File
@@ -28,6 +28,9 @@
#include "ImageData.h"
#include "CompressedImageData.h"
// C++
#include <list>
namespace love
{
namespace image
@@ -46,17 +49,19 @@ public:
static love::Type type;
virtual ~Image() {}
Image();
virtual ~Image();
// Implements Module.
virtual ModuleType getModuleType() const { return M_IMAGE; }
ModuleType getModuleType() const override { return M_IMAGE; }
const char *getName() const override;
/**
* Creates new ImageData from FileData.
* @param data The FileData containing the encoded image data.
* @return The new ImageData.
**/
virtual ImageData *newImageData(love::filesystem::FileData *data) = 0;
ImageData *newImageData(love::filesystem::FileData *data);
/**
* Creates empty ImageData with the given size.
@@ -64,7 +69,7 @@ public:
* @param height The height of the ImageData.
* @return The new ImageData.
**/
virtual ImageData *newImageData(int width, int height, PixelFormat format = PIXELFORMAT_RGBA8) = 0;
ImageData *newImageData(int width, int height, PixelFormat format = PIXELFORMAT_RGBA8);
/**
* Creates empty ImageData with the given size.
@@ -75,28 +80,36 @@ public:
* copy it.
* @return The new ImageData.
**/
virtual ImageData *newImageData(int width, int height, PixelFormat format, void *data, bool own = false) = 0;
ImageData *newImageData(int width, int height, PixelFormat format, void *data, bool own = false);
/**
* Creates new CompressedImageData from FileData.
* @param data The FileData containing the compressed image data.
* @return The new CompressedImageData.
**/
virtual CompressedImageData *newCompressedData(love::filesystem::FileData *data) = 0;
CompressedImageData *newCompressedData(love::filesystem::FileData *data);
/**
* Determines whether a FileData is Compressed image data or not.
* @param data The FileData to test.
**/
virtual bool isCompressed(love::filesystem::FileData *data) = 0;
bool isCompressed(love::filesystem::FileData *data);
std::vector<StrongRef<ImageData>> newCubeFaces(ImageData *src);
std::vector<StrongRef<ImageData>> newVolumeLayers(ImageData *src);
const std::list<FormatHandler *> &getFormatHandlers() const;
private:
ImageData *newPastedImageData(ImageData *src, int sx, int sy, int w, int h);
// Image format handlers we can use for decoding and encoding ImageData.
std::list<FormatHandler *> formatHandlers;
// Compressed image format handers we can use for parsing CompressedImageData.
std::list<CompressedFormatHandler *> compressedFormatHandlers;
}; // Image
} // image
+204 -8
View File
@@ -19,6 +19,8 @@
**/
#include "ImageData.h"
#include "Image.h"
#include "filesystem/Filesystem.h"
using love::thread::Lock;
@@ -29,13 +31,207 @@ namespace image
love::Type ImageData::type("ImageData", &Data::type);
ImageData::ImageData()
: data(nullptr)
ImageData::ImageData(love::filesystem::FileData *data)
{
decode(data);
}
ImageData::ImageData(int width, int height, PixelFormat format)
{
if (!validPixelFormat(format))
throw love::Exception("Unsupported pixel format for ImageData");
this->width = width;
this->height = height;
this->format = format;
create(width, height, format);
// Set to black/transparency.
memset(data, 0, getSize());
}
ImageData::ImageData(int width, int height, PixelFormat format, void *data, bool own)
{
if (!validPixelFormat(format))
throw love::Exception("Unsupported pixel format for ImageData");
this->width = width;
this->height = height;
this->format = format;
if (own)
this->data = (unsigned char *) data;
else
create(width, height, format, data);
}
ImageData::ImageData(const ImageData &c)
{
width = c.width;
height = c.height;
format = c.format;
create(width, height, format, c.getData());
}
ImageData::~ImageData()
{
if (decodeHandler.get())
decodeHandler->free(data);
else
delete[] data;
}
love::image::ImageData *ImageData::clone() const
{
return new ImageData(*this);
}
void ImageData::create(int width, int height, PixelFormat format, void *data)
{
size_t datasize = width * height * getPixelFormatSize(format);
try
{
this->data = new unsigned char[datasize];
}
catch(std::bad_alloc &)
{
throw love::Exception("Out of memory");
}
if (data)
memcpy(this->data, data, datasize);
decodeHandler = nullptr;
this->format = format;
}
void ImageData::decode(love::filesystem::FileData *data)
{
FormatHandler *decoder = nullptr;
FormatHandler::DecodedImage decodedimage;
auto module = Module::getInstance<Image>(Module::M_IMAGE);
if (module == nullptr)
throw love::Exception("love.image must be loaded in order to decode an ImageData.");
for (FormatHandler *handler : module->getFormatHandlers())
{
if (handler->canDecode(data))
{
decoder = handler;
break;
}
}
if (decoder)
decodedimage = decoder->decode(data);
if (decodedimage.data == nullptr)
{
const std::string &name = data->getFilename();
throw love::Exception("Could not decode file '%s' to ImageData: unsupported file format", name.c_str());
}
if (decodedimage.size != decodedimage.width * decodedimage.height * getPixelFormatSize(decodedimage.format))
{
decoder->free(decodedimage.data);
throw love::Exception("Could not convert image!");
}
// 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;
this->format = decodedimage.format;
decodeHandler = decoder;
}
love::filesystem::FileData *ImageData::encode(FormatHandler::EncodedFormat encodedFormat, const char *filename, bool writefile) const
{
FormatHandler *encoder = nullptr;
FormatHandler::EncodedImage encodedimage;
FormatHandler::DecodedImage rawimage;
rawimage.width = width;
rawimage.height = height;
rawimage.size = getSize();
rawimage.data = data;
rawimage.format = format;
auto module = Module::getInstance<Image>(Module::M_IMAGE);
if (module == nullptr)
throw love::Exception("love.image must be loaded in order to encode an ImageData.");
for (FormatHandler *handler : module->getFormatHandlers())
{
if (handler->canEncode(format, encodedFormat))
{
encoder = handler;
break;
}
}
if (encoder != nullptr)
{
thread::Lock lock(mutex);
encodedimage = encoder->encode(rawimage, encodedFormat);
}
if (encoder == nullptr || encodedimage.data == nullptr)
{
const char *fname = "unknown";
love::getConstant(format, fname);
throw love::Exception("No suitable image encoder for %s format.", fname);
}
love::filesystem::FileData *filedata = nullptr;
try
{
filedata = new love::filesystem::FileData(encodedimage.size, filename);
}
catch (love::Exception &)
{
encoder->free(encodedimage.data);
throw;
}
memcpy(filedata->getData(), encodedimage.data, encodedimage.size);
encoder->free(encodedimage.data);
if (writefile)
{
auto fs = Module::getInstance<filesystem::Filesystem>(Module::M_FILESYSTEM);
if (fs == nullptr)
{
filedata->release();
throw love::Exception("love.filesystem must be loaded in order to write an encoded ImageData to a file.");
}
try
{
fs->write(filename, filedata->getData(), filedata->getSize());
}
catch (love::Exception &)
{
filedata->release();
throw;
}
}
return filedata;
}
size_t ImageData::getSize() const
@@ -289,23 +485,23 @@ bool ImageData::validPixelFormat(PixelFormat format)
}
}
bool ImageData::getConstant(const char *in, EncodedFormat &out)
bool ImageData::getConstant(const char *in, FormatHandler::EncodedFormat &out)
{
return encodedFormats.find(in, out);
}
bool ImageData::getConstant(EncodedFormat in, const char *&out)
bool ImageData::getConstant(FormatHandler::EncodedFormat in, const char *&out)
{
return encodedFormats.find(in, out);
}
StringMap<ImageData::EncodedFormat, ImageData::ENCODED_MAX_ENUM>::Entry ImageData::encodedFormatEntries[] =
StringMap<FormatHandler::EncodedFormat, FormatHandler::ENCODED_MAX_ENUM>::Entry ImageData::encodedFormatEntries[] =
{
{"tga", ENCODED_TGA},
{"png", ENCODED_PNG},
{"tga", FormatHandler::ENCODED_TGA},
{"png", FormatHandler::ENCODED_PNG},
};
StringMap<ImageData::EncodedFormat, ImageData::ENCODED_MAX_ENUM> ImageData::encodedFormats(ImageData::encodedFormatEntries, sizeof(ImageData::encodedFormatEntries));
StringMap<FormatHandler::EncodedFormat, FormatHandler::ENCODED_MAX_ENUM> ImageData::encodedFormats(ImageData::encodedFormatEntries, sizeof(ImageData::encodedFormatEntries));
} // image
} // love
+26 -31
View File
@@ -29,6 +29,7 @@
#include "filesystem/FileData.h"
#include "thread/threads.h"
#include "ImageDataBase.h"
#include "FormatHandler.h"
using love::thread::Mutex;
@@ -37,13 +38,6 @@ namespace love
namespace image
{
// Pixel format structure.
struct pixel
{
// Red, green, blue, alpha.
unsigned char r, g, b, a;
};
union Pixel
{
uint8 rgba8[4];
@@ -61,14 +55,10 @@ public:
static love::Type type;
enum EncodedFormat
{
ENCODED_TGA,
ENCODED_PNG,
ENCODED_MAX_ENUM
};
ImageData();
ImageData(love::filesystem::FileData *data);
ImageData(int width, int height, PixelFormat format = PIXELFORMAT_RGBA8);
ImageData(int width, int height, PixelFormat format, void *data, bool own);
ImageData(const ImageData &c);
virtual ~ImageData();
/**
@@ -111,12 +101,12 @@ public:
* @param f The file to save the encoded image data to.
* @param format The format of the encoded data.
**/
virtual love::filesystem::FileData *encode(EncodedFormat format, const char *filename) = 0;
love::filesystem::FileData *encode(FormatHandler::EncodedFormat format, const char *filename, bool writefile) const;
love::thread::Mutex *getMutex() const;
// Implements ImageDataBase.
virtual ImageData *clone() const override = 0;
ImageData *clone() const override;
void *getData() const override;
size_t getSize() const override;
bool isSRGB() const override;
@@ -125,18 +115,8 @@ public:
static bool validPixelFormat(PixelFormat format);
static bool getConstant(const char *in, EncodedFormat &out);
static bool getConstant(EncodedFormat in, const char *&out);
protected:
// The actual data.
unsigned char *data;
// We need to be thread-safe
// so we lock when we're accessing our
// data
love::thread::MutexRef mutex;
static bool getConstant(const char *in, FormatHandler::EncodedFormat &out);
static bool getConstant(FormatHandler::EncodedFormat in, const char *&out);
private:
@@ -148,6 +128,21 @@ private:
float *f32;
};
// Create imagedata. Initialize with data if not null.
void create(int width, int height, PixelFormat format, void *data = nullptr);
// Decode and load an encoded format.
void decode(love::filesystem::FileData *data);
// The actual data.
unsigned char *data = nullptr;
love::thread::MutexRef mutex;
// 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.
StrongRef<FormatHandler> decodeHandler;
static void pasteRGBA8toRGBA16(Row src, Row dst, int w);
static void pasteRGBA8toRGBA16F(Row src, Row dst, int w);
static void pasteRGBA8toRGBA32F(Row src, Row dst, int w);
@@ -164,8 +159,8 @@ private:
static void pasteRGBA32FtoRGBA16(Row src, Row dst, int w);
static void pasteRGBA32FtoRGBA16F(Row src, Row dst, int w);
static StringMap<EncodedFormat, ENCODED_MAX_ENUM>::Entry encodedFormatEntries[];
static StringMap<EncodedFormat, ENCODED_MAX_ENUM> encodedFormats;
static StringMap<FormatHandler::EncodedFormat, FormatHandler::ENCODED_MAX_ENUM>::Entry encodedFormatEntries[];
static StringMap<FormatHandler::EncodedFormat, FormatHandler::ENCODED_MAX_ENUM> encodedFormats;
}; // ImageData
+3 -3
View File
@@ -104,7 +104,7 @@ bool ASTCHandler::canParse(const filesystem::FileData *data)
return true;
}
StrongRef<CompressedImageData::Memory> ASTCHandler::parse(filesystem::FileData *filedata, std::vector<StrongRef<CompressedImageData::Slice>> &images, PixelFormat &format, bool &sRGB)
StrongRef<CompressedMemory> ASTCHandler::parse(filesystem::FileData *filedata, std::vector<StrongRef<CompressedSlice>> &images, PixelFormat &format, bool &sRGB)
{
if (!canParse(filedata))
throw love::Exception("Could not decode compressed data (not an .astc file?)");
@@ -129,12 +129,12 @@ StrongRef<CompressedImageData::Memory> ASTCHandler::parse(filesystem::FileData *
if (totalsize + sizeof(header) > filedata->getSize())
throw love::Exception("Could not parse .astc file: file is too small.");
StrongRef<CompressedImageData::Memory> memory(new CompressedImageData::Memory(totalsize), Acquire::NORETAIN);
StrongRef<CompressedMemory> memory(new CompressedMemory(totalsize), Acquire::NORETAIN);
// .astc files only store a single mipmap level.
memcpy(memory->data, (uint8 *) filedata->getData() + sizeof(ASTCHeader), totalsize);
images.emplace_back(new CompressedImageData::Slice(cformat, sizeX, sizeY, memory, 0, totalsize), Acquire::NORETAIN);
images.emplace_back(new CompressedSlice(cformat, sizeX, sizeY, memory, 0, totalsize), Acquire::NORETAIN);
format = cformat;
sRGB = false;
+4 -7
View File
@@ -18,11 +18,10 @@
* 3. This notice may not be removed or altered from any source distribution.
**/
#ifndef LOVE_IMAGE_MAGPIE_ASTC_HANDLER_H
#define LOVE_IMAGE_MAGPIE_ASTC_HANDLER_H
#pragma once
#include "common/config.h"
#include "CompressedFormatHandler.h"
#include "image/CompressedFormatHandler.h"
namespace love
{
@@ -44,8 +43,8 @@ public:
// Implements CompressedFormatHandler.
bool canParse(const filesystem::FileData *data) override;
StrongRef<CompressedImageData::Memory> parse(filesystem::FileData *filedata,
std::vector<StrongRef<CompressedImageData::Slice>> &images,
StrongRef<CompressedMemory> parse(filesystem::FileData *filedata,
std::vector<StrongRef<CompressedSlice>> &images,
PixelFormat &format, bool &sRGB) override;
}; // ASTCHandler
@@ -53,5 +52,3 @@ public:
} // magpie
} // image
} // love
#endif // LOVE_IMAGE_MAGPIE_ASTC_HANDLER_H
@@ -1,85 +0,0 @@
/**
* Copyright (c) 2006-2017 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 "CompressedImageData.h"
namespace love
{
namespace image
{
namespace magpie
{
CompressedImageData::CompressedImageData(std::list<CompressedFormatHandler *> formats, love::filesystem::FileData *filedata)
{
CompressedFormatHandler *parser = nullptr;
for (CompressedFormatHandler *handler : formats)
{
if (handler->canParse(filedata))
{
parser = handler;
break;
}
}
if (parser == nullptr)
throw love::Exception("Could not parse compressed data: Unknown format.");
memory = parser->parse(filedata, dataImages, format, sRGB);
if (memory == nullptr)
throw love::Exception("Could not parse compressed data.");
if (format == PIXELFORMAT_UNKNOWN)
throw love::Exception("Could not parse compressed data: Unknown format.");
if (dataImages.size() == 0 || memory->size == 0)
throw love::Exception("Could not parse compressed data: No valid data?");
}
CompressedImageData::CompressedImageData(const CompressedImageData &c)
{
format = c.format;
sRGB = c.sRGB;
memory.set(new Memory(c.memory->size), Acquire::NORETAIN);
memcpy(memory->data, c.memory->data, memory->size);
for (const auto &i : c.dataImages)
{
Slice *slice = new Slice(i->getFormat(), i->getWidth(), i->getHeight(), memory, i->getOffset(), i->getSize());
dataImages.push_back(slice);
slice->release();
}
}
CompressedImageData *CompressedImageData::clone() const
{
return new CompressedImageData(*this);
}
CompressedImageData::~CompressedImageData()
{
}
} // magpie
} // image
} // love
+3 -2
View File
@@ -20,6 +20,7 @@
// LOVE
#include "EXRHandler.h"
#include "common/halffloat.h"
// tinyexr
#define TINYEXR_IMPLEMENTATION
@@ -41,7 +42,7 @@ bool EXRHandler::canDecode(love::filesystem::FileData *data)
return ParseEXRVersionFromMemory(&version, (const unsigned char *) data->getData(), data->getSize()) == TINYEXR_SUCCESS;
}
bool EXRHandler::canEncode(PixelFormat /*rawFormat*/, ImageData::EncodedFormat /*encodedFormat*/)
bool EXRHandler::canEncode(PixelFormat /*rawFormat*/, EncodedFormat /*encodedFormat*/)
{
return false;
}
@@ -186,7 +187,7 @@ FormatHandler::DecodedImage EXRHandler::decode(love::filesystem::FileData *data)
return img;
}
FormatHandler::EncodedImage EXRHandler::encode(const DecodedImage & /*img*/, ImageData::EncodedFormat /*encodedFormat*/)
FormatHandler::EncodedImage EXRHandler::encode(const DecodedImage & /*img*/, EncodedFormat /*encodedFormat*/)
{
throw love::Exception("Invalid format.");
}
+4 -7
View File
@@ -18,10 +18,9 @@
* 3. This notice may not be removed or altered from any source distribution.
**/
#ifndef LOVE_IMAGE_MAGPIE_EXR_HANDLER_H
#define LOVE_IMAGE_MAGPIE_EXR_HANDLER_H
#pragma once
#include "FormatHandler.h"
#include "image/FormatHandler.h"
namespace love
{
@@ -40,10 +39,10 @@ public:
// Implements FormatHandler.
virtual bool canDecode(love::filesystem::FileData *data);
virtual bool canEncode(PixelFormat rawFormat, ImageData::EncodedFormat encodedFormat);
virtual bool canEncode(PixelFormat rawFormat, EncodedFormat encodedFormat);
virtual DecodedImage decode(love::filesystem::FileData *data);
virtual EncodedImage encode(const DecodedImage &img, ImageData::EncodedFormat format);
virtual EncodedImage encode(const DecodedImage &img, EncodedFormat format);
virtual void free(unsigned char *mem);
@@ -52,5 +51,3 @@ public:
} // magpie
} // image
} // love
#endif // LOVE_IMAGE_MAGPIE_EXR_HANDLER_H
-113
View File
@@ -1,113 +0,0 @@
/**
* Copyright (c) 2006-2017 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 "common/config.h"
#include "Image.h"
#include "ImageData.h"
#include "CompressedImageData.h"
#include "PNGHandler.h"
#include "STBHandler.h"
#include "EXRHandler.h"
#include "ddsHandler.h"
#include "PVRHandler.h"
#include "KTXHandler.h"
#include "PKMHandler.h"
#include "ASTCHandler.h"
namespace love
{
namespace image
{
namespace magpie
{
Image::Image()
{
halfInit(); // Makes sure half-float conversions can be used.
formatHandlers = {
new PNGHandler,
new STBHandler,
new EXRHandler,
};
compressedFormatHandlers = {
new DDSHandler,
new PVRHandler,
new KTXHandler,
new PKMHandler,
new ASTCHandler,
};
}
Image::~Image()
{
// ImageData objects reference the FormatHandlers in our list, so we should
// release them instead of deleting them completely here.
for (FormatHandler *handler : formatHandlers)
handler->release();
for (CompressedFormatHandler *handler : compressedFormatHandlers)
handler->release();
}
const char *Image::getName() const
{
return "love.image.magpie";
}
love::image::ImageData *Image::newImageData(love::filesystem::FileData *data)
{
return new ImageData(formatHandlers, data);
}
love::image::ImageData *Image::newImageData(int width, int height, PixelFormat format)
{
return new ImageData(formatHandlers, width, height, format);
}
love::image::ImageData *Image::newImageData(int width, int height, PixelFormat format, void *data, bool own)
{
return new ImageData(formatHandlers, width, height, format, data, own);
}
love::image::CompressedImageData *Image::newCompressedData(love::filesystem::FileData *data)
{
return new CompressedImageData(compressedFormatHandlers, data);
}
bool Image::isCompressed(love::filesystem::FileData *data)
{
for (CompressedFormatHandler *handler : compressedFormatHandlers)
{
if (handler->canParse(data))
return true;
}
return false;
}
} // magpie
} // image
} // love
-76
View File
@@ -1,76 +0,0 @@
/**
* Copyright (c) 2006-2017 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_IMAGE_H
#define LOVE_IMAGE_MAGPIE_IMAGE_H
// LOVE
#include "image/Image.h"
#include "FormatHandler.h"
#include "CompressedFormatHandler.h"
// C++
#include <list>
namespace love
{
namespace image
{
namespace magpie
{
/**
* Similar to love.sound's Lullaby module, love.image.magpie interfaces with
* multiple image libraries and determines the correct one to use on a
* per-image basis at runtime.
**/
class Image : public love::image::Image
{
public:
Image();
~Image();
// Implements Module.
const char *getName() const;
love::image::ImageData *newImageData(love::filesystem::FileData *data);
love::image::ImageData *newImageData(int width, int height, PixelFormat format = PIXELFORMAT_RGBA8);
love::image::ImageData *newImageData(int width, int height, PixelFormat format, void *data, bool own = false);
love::image::CompressedImageData *newCompressedData(love::filesystem::FileData *data);
bool isCompressed(love::filesystem::FileData *data);
private:
// Image format handlers we can use for decoding and encoding ImageData.
std::list<FormatHandler *> formatHandlers;
// Compressed image format handers we can use for parsing CompressedImageData.
std::list<CompressedFormatHandler *> compressedFormatHandlers;
}; // Image
} // magpie
} // image
} // love
#endif // LOVE_IMAGE_MAGPIE_IMAGE_H
-228
View File
@@ -1,228 +0,0 @@
/**
* Copyright (c) 2006-2017 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 "ImageData.h"
namespace love
{
namespace image
{
namespace magpie
{
ImageData::ImageData(std::list<FormatHandler *> formatHandlers, love::filesystem::FileData *data)
: formatHandlers(formatHandlers)
, decodeHandler(nullptr)
{
for (FormatHandler *handler : formatHandlers)
handler->retain();
decode(data);
}
ImageData::ImageData(std::list<FormatHandler *> formatHandlers, int width, int height, PixelFormat format)
: formatHandlers(formatHandlers)
, decodeHandler(nullptr)
{
if (!validPixelFormat(format))
throw love::Exception("Unsupported pixel format for ImageData");
this->width = width;
this->height = height;
this->format = format;
create(width, height, format);
// Set to black/transparency.
memset(data, 0, getSize());
for (FormatHandler *handler : formatHandlers)
handler->retain();
}
ImageData::ImageData(std::list<FormatHandler *> formatHandlers, int width, int height, PixelFormat format, void *data, bool own)
: formatHandlers(formatHandlers)
, decodeHandler(nullptr)
{
if (!validPixelFormat(format))
throw love::Exception("Unsupported pixel format for ImageData");
this->width = width;
this->height = height;
this->format = format;
if (own)
this->data = (unsigned char *) data;
else
create(width, height, format, data);
for (FormatHandler *handler : formatHandlers)
handler->retain();
}
ImageData::ImageData(const ImageData &c)
: formatHandlers(c.formatHandlers)
, decodeHandler(nullptr)
{
width = c.width;
height = c.height;
format = c.format;
for (FormatHandler *handler : formatHandlers)
handler->retain();
create(width, height, format, c.getData());
}
ImageData::~ImageData()
{
if (decodeHandler)
decodeHandler->free(data);
else
delete[] data;
for (FormatHandler *handler : formatHandlers)
handler->release();
}
love::image::ImageData *ImageData::clone() const
{
return new ImageData(*this);
}
void ImageData::create(int width, int height, PixelFormat format, void *data)
{
size_t datasize = width * height * getPixelFormatSize(format);
try
{
this->data = new unsigned char[datasize];
}
catch(std::bad_alloc &)
{
throw love::Exception("Out of memory");
}
if (data)
memcpy(this->data, data, datasize);
decodeHandler = nullptr;
this->format = format;
}
void ImageData::decode(love::filesystem::FileData *data)
{
FormatHandler *decoder = nullptr;
FormatHandler::DecodedImage decodedimage;
for (FormatHandler *handler : formatHandlers)
{
if (handler->canDecode(data))
{
decoder = handler;
break;
}
}
if (decoder)
decodedimage = decoder->decode(data);
if (decodedimage.data == nullptr)
{
const std::string &name = data->getFilename();
throw love::Exception("Could not decode file '%s' to ImageData: unsupported file format", name.c_str());
}
if (decodedimage.size != decodedimage.width * decodedimage.height * getPixelFormatSize(decodedimage.format))
{
decoder->free(decodedimage.data);
throw love::Exception("Could not convert image!");
}
// 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;
this->format = decodedimage.format;
decodeHandler = decoder;
}
love::filesystem::FileData *ImageData::encode(EncodedFormat encodedFormat, const char *filename)
{
FormatHandler *encoder = nullptr;
FormatHandler::EncodedImage encodedimage;
FormatHandler::DecodedImage rawimage;
rawimage.width = width;
rawimage.height = height;
rawimage.size = getSize();
rawimage.data = data;
rawimage.format = format;
for (FormatHandler *handler : formatHandlers)
{
if (handler->canEncode(format, encodedFormat))
{
encoder = handler;
break;
}
}
if (encoder != nullptr)
{
thread::Lock lock(mutex);
encodedimage = encoder->encode(rawimage, encodedFormat);
}
if (encoder == nullptr || encodedimage.data == nullptr)
{
const char *fname = "unknown";
love::getConstant(format, fname);
throw love::Exception("No suitable image encoder for %s format.", fname);
}
love::filesystem::FileData *filedata = nullptr;
try
{
filedata = new love::filesystem::FileData(encodedimage.size, filename);
}
catch (love::Exception &)
{
encoder->free(encodedimage.data);
throw;
}
memcpy(filedata->getData(), encodedimage.data, encodedimage.size);
encoder->free(encodedimage.data);
return filedata;
}
} // magpie
} // image
} // love
-73
View File
@@ -1,73 +0,0 @@
/**
* Copyright (c) 2006-2017 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_IMAGE_DATA_H
#define LOVE_IMAGE_MAGPIE_IMAGE_DATA_H
// LOVE
#include "FormatHandler.h"
#include "image/ImageData.h"
// C++
#include <list>
namespace love
{
namespace image
{
namespace magpie
{
class ImageData : public love::image::ImageData
{
public:
ImageData(std::list<FormatHandler *> formatHandlers, love::filesystem::FileData *data);
ImageData(std::list<FormatHandler *> formatHandlers, int width, int height, PixelFormat format = PIXELFORMAT_RGBA8);
ImageData(std::list<FormatHandler *> formatHandlers, int width, int height, PixelFormat format, void *data, bool own);
ImageData(const ImageData &c);
virtual ~ImageData();
// Implements image::ImageData.
virtual love::image::ImageData *clone() const;
virtual love::filesystem::FileData *encode(EncodedFormat encodedFormat, const char *filename);
private:
// Create imagedata. Initialize with data if not null.
void create(int width, int height, PixelFormat format, void *data = nullptr);
// Decode and load an encoded format.
void decode(love::filesystem::FileData *data);
// 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
} // image
} // love
#endif // LOVE_IMAGE_MAGPIE_IMAGE_DATA_H

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