mirror of
https://github.com/love2d/love.git
synced 2026-08-18 03:34:23 +02:00
merge
--HG-- branch : minor
This commit is contained in:
@@ -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
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -66,6 +66,8 @@ bool mkdir(const char *path);
|
||||
|
||||
bool createStorageDirectories();
|
||||
|
||||
bool hasBackgroundMusic();
|
||||
|
||||
} // android
|
||||
} // love
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
+991
-569
File diff suppressed because it is too large
Load Diff
@@ -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},
|
||||
|
||||
@@ -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,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,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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -117,7 +117,7 @@ public:
|
||||
virtual bool setEffect(const char *effect, const std::map<Filter::Parameter, float> ¶ms) = 0;
|
||||
virtual bool unsetEffect(const char *effect) = 0;
|
||||
virtual bool getEffect(const char *effect, std::map<Filter::Parameter, float> ¶ms) = 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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -81,7 +81,7 @@ public:
|
||||
bool setEffect(const char *, std::map<Effect::Parameter, float> ¶ms);
|
||||
bool unsetEffect(const char *);
|
||||
bool getEffect(const char *, std::map<Effect::Parameter, float> ¶ms);
|
||||
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,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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -90,7 +90,7 @@ public:
|
||||
virtual bool setEffect(const char *effect, const std::map<Filter::Parameter, float> ¶ms);
|
||||
virtual bool unsetEffect(const char *effect);
|
||||
virtual bool getEffect(const char *effect, std::map<Filter::Parameter, float> ¶ms);
|
||||
virtual bool getEffectsList(std::vector<std::string> &list);
|
||||
virtual bool getActiveEffects(std::vector<std::string> &list) const;
|
||||
|
||||
private:
|
||||
|
||||
|
||||
@@ -543,7 +543,7 @@ bool Audio::getEffect(const char *name, std::map<Effect::Parameter, float> ¶
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Audio::getEffectsList(std::vector<std::string> &list)
|
||||
bool Audio::getActiveEffects(std::vector<std::string> &list) const
|
||||
{
|
||||
if (effectmap.empty())
|
||||
return false;
|
||||
|
||||
@@ -118,7 +118,7 @@ public:
|
||||
bool setEffect(const char *name, std::map<Effect::Parameter, float> ¶ms);
|
||||
bool unsetEffect(const char *name);
|
||||
bool getEffect(const char *name, std::map<Effect::Parameter, float> ¶ms);
|
||||
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,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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -152,7 +152,7 @@ public:
|
||||
virtual bool setEffect(const char *effect, const std::map<Filter::Parameter, float> ¶ms);
|
||||
virtual bool unsetEffect(const char *effect);
|
||||
virtual bool getEffect(const char *effect, std::map<Filter::Parameter, float> ¶ms);
|
||||
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);
|
||||
|
||||
@@ -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 }
|
||||
};
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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 },
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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); });
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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++;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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,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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -174,6 +174,8 @@ protected:
|
||||
void initQuad();
|
||||
void setGraphicsMemorySize(int64 size);
|
||||
|
||||
bool validateDimensions(bool throwException) const;
|
||||
|
||||
TextureType texType;
|
||||
|
||||
PixelFormat format;
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -44,7 +44,10 @@ public:
|
||||
virtual ~ParticleSystem();
|
||||
|
||||
ParticleSystem *clone() override;
|
||||
void draw(Graphics *gfx, const Matrix4 &m) override;
|
||||
|
||||
private:
|
||||
|
||||
void drawInternal() const override;
|
||||
|
||||
}; // ParticleSystem
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
|
||||
// LOVE
|
||||
#include "common/int.h"
|
||||
#include "Color.h"
|
||||
#include "common/Color.h"
|
||||
|
||||
// C
|
||||
#include <stddef.h>
|
||||
|
||||
@@ -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++;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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); });
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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); });
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
+9
-15
@@ -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
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
+45
-21
@@ -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
|
||||
@@ -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
|
||||
@@ -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());
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
@@ -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.");
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
Reference in New Issue
Block a user