fix conflicts

This commit is contained in:
muddmaker
2015-03-21 18:15:41 -07:00
481 changed files with 58747 additions and 67796 deletions
+4 -4
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2014 LOVE Development Team
* Copyright (c) 2006-2015 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
@@ -29,11 +29,11 @@ StringMap<Audio::DistanceModel, Audio::DISTANCE_MAX_ENUM>::Entry Audio::distance
{
{"none", Audio::DISTANCE_NONE},
{"inverse", Audio::DISTANCE_INVERSE},
{"inverse clamped", Audio::DISTANCE_INVERSE_CLAMPED},
{"inverseclamped", Audio::DISTANCE_INVERSE_CLAMPED},
{"linear", Audio::DISTANCE_LINEAR},
{"linear clamped", Audio::DISTANCE_LINEAR_CLAMPED},
{"linearclamped", Audio::DISTANCE_LINEAR_CLAMPED},
{"exponent", Audio::DISTANCE_EXPONENT},
{"exponent clamped", Audio::DISTANCE_EXPONENT_CLAMPED}
{"exponentclamped", Audio::DISTANCE_EXPONENT_CLAMPED}
};
StringMap<Audio::DistanceModel, Audio::DISTANCE_MAX_ENUM> Audio::distanceModels(Audio::distanceModelEntries, sizeof(Audio::distanceModelEntries));
+4 -1
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2014 LOVE Development Team
* Copyright (c) 2006-2015 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
@@ -185,6 +185,9 @@ public:
**/
virtual void setVelocity(float *v) = 0;
virtual void setDopplerScale(float scale) = 0;
virtual float getDopplerScale() const = 0;
/**
* Begins recording audio input from the microphone.
**/
+6 -1
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2014 LOVE Development Team
* Copyright (c) 2006-2015 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
@@ -34,6 +34,11 @@ Source::~Source()
{
}
Source::Type Source::getType() const
{
return type;
}
bool Source::getConstant(const char *in, Type &out)
{
return types.find(in, out);
+3 -2
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2014 LOVE Development Team
* Copyright (c) 2006-2015 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
@@ -88,7 +88,6 @@ public:
virtual void setLooping(bool looping) = 0;
virtual bool isLooping() const = 0;
virtual bool isStatic() const = 0;
virtual void setMinVolume(float volume) = 0;
virtual float getMinVolume() const = 0;
@@ -103,6 +102,7 @@ public:
virtual float getMaxDistance() const = 0;
virtual int getChannels() const = 0;
virtual Type getType() const;
static bool getConstant(const char *in, Type &out);
static bool getConstant(Type in, const char *&out);
@@ -110,6 +110,7 @@ public:
static bool getConstant(Unit in, const char *&out);
protected:
Type type;
private:
+10 -1
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2014 LOVE Development Team
* Copyright (c) 2006-2015 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
@@ -132,6 +132,15 @@ void Audio::setVelocity(float *)
{
}
void Audio::setDopplerScale(float)
{
}
float Audio::getDopplerScale() const
{
return 1.0f;
}
void Audio::record()
{
}
+4 -1
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2014 LOVE Development Team
* Copyright (c) 2006-2015 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
@@ -67,6 +67,9 @@ public:
void getVelocity(float *v) const;
void setVelocity(float *v);
void setDopplerScale(float scale);
float getDopplerScale() const;
void record();
love::sound::SoundData *getRecordedData();
love::sound::SoundData *stopRecording(bool returnData);
+1 -6
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2014 LOVE Development Team
* Copyright (c) 2006-2015 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
@@ -170,11 +170,6 @@ bool Source::isLooping() const
return looping;
}
bool Source::isStatic() const
{
return (type == TYPE_STATIC);
}
void Source::setMinVolume(float volume)
{
this->minVolume = volume;
+1 -2
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2014 LOVE Development Team
* Copyright (c) 2006-2015 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
@@ -66,7 +66,6 @@ public:
virtual bool isRelative() const;
void setLooping(bool looping);
bool isLooping() const;
bool isStatic() const;
virtual void setMinVolume(float volume);
virtual float getMinVolume() const;
virtual void setMaxVolume(float volume);
+12 -1
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2014 LOVE Development Team
* Copyright (c) 2006-2015 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
@@ -256,6 +256,17 @@ void Audio::setVelocity(float *v)
alListenerfv(AL_VELOCITY, v);
}
void Audio::setDopplerScale(float scale)
{
if (scale >= 0.0f)
alDopplerFactor(scale);
}
float Audio::getDopplerScale() const
{
return alGetFloat(AL_DOPPLER_FACTOR);
}
void Audio::record()
{
if (!canRecord()) return;
+10 -2
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2014 LOVE Development Team
* Copyright (c) 2006-2015 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
@@ -36,9 +36,14 @@
#include "thread/threads.h"
// OpenAL
#ifdef LOVE_MACOSX_USE_FRAMEWORKS // Frameworks have different include paths.
#ifdef LOVE_APPLE_USE_FRAMEWORKS // Frameworks have different include paths.
#ifdef LOVE_IOS
#include <OpenAL/alc.h>
#include <OpenAL/al.h>
#else
#include <OpenAL-Soft/alc.h>
#include <OpenAL-Soft/al.h>
#endif
#else
#include <AL/alc.h>
#include <AL/al.h>
@@ -85,6 +90,9 @@ public:
void getVelocity(float *v) const;
void setVelocity(float *v);
void setDopplerScale(float scale);
float getDopplerScale() const;
void record();
love::sound::SoundData *getRecordedData();
love::sound::SoundData *stopRecording(bool returnData);
+4 -2
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2014 LOVE Development Team
* Copyright (c) 2006-2015 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
@@ -56,7 +56,9 @@ Pool::Pool()
// Create the mutex.
mutex = thread::newMutex();
#ifdef AL_SOFT_direct_channels
ALboolean hasext = alIsExtensionPresent("AL_SOFT_direct_channels");
#endif
// Make all sources available initially.
for (int i = 0; i < totalSources; i++)
@@ -126,7 +128,7 @@ void Pool::update()
int Pool::getSourceCount() const
{
return playing.size();
return (int) playing.size();
}
int Pool::getMaxSources() const
+9 -2
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2014 LOVE Development Team
* Copyright (c) 2006-2015 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
@@ -32,10 +32,17 @@
#include "thread/threads.h"
// OpenAL
#ifdef LOVE_MACOSX_USE_FRAMEWORKS
#ifdef LOVE_APPLE_USE_FRAMEWORKS
#ifdef LOVE_IOS
#include <OpenAL/alc.h>
#include <OpenAL/al.h>
#include <OpenAL/oalMacOSX_OALExtensions.h>
#include <OpenAL/oalStaticBufferExtension.h>
#else
#include <OpenAL-Soft/alc.h>
#include <OpenAL-Soft/al.h>
#include <OpenAL-Soft/alext.h>
#endif
#else
#include <AL/alc.h>
#include <AL/al.h>
+136 -53
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2014 LOVE Development Team
* Copyright (c) 2006-2015 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
@@ -25,6 +25,7 @@
// STD
#include <iostream>
#include <float.h>
#include <algorithm>
namespace love
{
@@ -33,6 +34,36 @@ namespace audio
namespace openal
{
#ifdef LOVE_IOS
// OpenAL on iOS barfs if the max distance is +inf.
static const float MAX_ATTENUATION_DISTANCE = 1000000.0f;
#else
static const float MAX_ATTENUATION_DISTANCE = FLT_MAX;
#endif
class InvalidFormatException : public love::Exception
{
public:
InvalidFormatException(int channels, int bitdepth)
: Exception("%d-channel Sources with %d bits per sample are not supported.", channels, bitdepth)
{
}
};
class SpatialSupportException : public love::Exception
{
public:
SpatialSupportException()
: Exception("This spatial audio functionality is only available for mono Sources. \
Ensure the Source is not multi-channel before calling this function.")
{
}
};
StaticDataBuffer::StaticDataBuffer(ALenum format, const ALvoid *data, ALsizei size, ALsizei freq)
{
alGenBuffers(1, &buffer);
@@ -58,7 +89,7 @@ Source::Source(Pool *pool, love::sound::SoundData *soundData)
, maxVolume(1.0f)
, referenceDistance(1.0f)
, rolloffFactor(1.0f)
, maxDistance(FLT_MAX)
, maxDistance(MAX_ATTENUATION_DISTANCE)
, cone()
, offsetSamples(0)
, offsetSeconds(0)
@@ -68,7 +99,10 @@ Source::Source(Pool *pool, love::sound::SoundData *soundData)
, toLoop(0)
{
ALenum fmt = getFormat(soundData->getChannels(), soundData->getBitDepth());
staticBuffer.set(new StaticDataBuffer(fmt, soundData->getData(), soundData->getSize(), soundData->getSampleRate()));
if (fmt == 0)
throw InvalidFormatException(soundData->getChannels(), soundData->getBitDepth());
staticBuffer.set(new StaticDataBuffer(fmt, soundData->getData(), (ALsizei) soundData->getSize(), soundData->getSampleRate()));
// The buffer has a +2 retain count right now, but we want it to have +1.
staticBuffer->release();
@@ -94,7 +128,7 @@ Source::Source(Pool *pool, love::sound::Decoder *decoder)
, maxVolume(1.0f)
, referenceDistance(1.0f)
, rolloffFactor(1.0f)
, maxDistance(FLT_MAX)
, maxDistance(MAX_ATTENUATION_DISTANCE)
, cone()
, offsetSamples(0)
, offsetSeconds(0)
@@ -103,6 +137,9 @@ Source::Source(Pool *pool, love::sound::Decoder *decoder)
, decoder(decoder)
, toLoop(0)
{
if (getFormat(decoder->getChannels(), decoder->getBitDepth()) == 0)
throw InvalidFormatException(decoder->getChannels(), decoder->getBitDepth());
alGenBuffers(MAX_BUFFERS, streamBuffers);
float z[3] = {0, 0, 0};
@@ -228,13 +265,14 @@ bool Source::isPaused() const
bool Source::isFinished() const
{
return type == TYPE_STATIC ? isStopped() : isStopped() && !isLooping() && decoder->isFinished();
return type == TYPE_STATIC ? isStopped() : (isStopped() && !isLooping() && decoder->isFinished());
}
bool Source::update()
{
if (!valid)
return false;
if (type == TYPE_STATIC)
{
// Looping mode could have changed.
@@ -273,8 +311,10 @@ bool Source::update()
streamAtomic(buffer, decoder.get());
alSourceQueueBuffers(source, 1, &buffer);
}
return true;
}
return false;
}
@@ -302,9 +342,7 @@ float Source::getPitch() const
void Source::setVolume(float volume)
{
if (valid)
{
alSourcef(source, AL_GAIN, volume);
}
this->volume = volume;
}
@@ -337,9 +375,7 @@ void Source::seekAtomic(float offset, void *unit)
decoder->seek(offset);
}
else
{
alSourcef(source, AL_SAMPLE_OFFSET, offset);
}
break;
case Source::UNIT_SECONDS:
default:
@@ -350,9 +386,7 @@ void Source::seekAtomic(float offset, void *unit)
offsetSamples = offset * decoder->getSampleRate();
}
else
{
alSourcef(source, AL_SEC_OFFSET, offset);
}
break;
}
if (type == TYPE_STREAM)
@@ -406,6 +440,9 @@ float Source::tell(Source::Unit unit)
void Source::setPosition(float *v)
{
if (channels > 1)
throw SpatialSupportException();
if (valid)
alSourcefv(source, AL_POSITION, v);
@@ -414,6 +451,9 @@ void Source::setPosition(float *v)
void Source::getPosition(float *v) const
{
if (channels > 1)
throw SpatialSupportException();
if (valid)
alGetSourcefv(source, AL_POSITION, v);
else
@@ -422,6 +462,9 @@ void Source::getPosition(float *v) const
void Source::setVelocity(float *v)
{
if (channels > 1)
throw SpatialSupportException();
if (valid)
alSourcefv(source, AL_VELOCITY, v);
@@ -430,6 +473,9 @@ void Source::setVelocity(float *v)
void Source::getVelocity(float *v) const
{
if (channels > 1)
throw SpatialSupportException();
if (valid)
alGetSourcefv(source, AL_VELOCITY, v);
else
@@ -438,6 +484,9 @@ void Source::getVelocity(float *v) const
void Source::setDirection(float *v)
{
if (channels > 1)
throw SpatialSupportException();
if (valid)
alSourcefv(source, AL_DIRECTION, v);
else
@@ -446,6 +495,9 @@ void Source::setDirection(float *v)
void Source::getDirection(float *v) const
{
if (channels > 1)
throw SpatialSupportException();
if (valid)
alGetSourcefv(source, AL_DIRECTION, v);
else
@@ -454,8 +506,11 @@ void Source::getDirection(float *v) const
void Source::setCone(float innerAngle, float outerAngle, float outerVolume)
{
cone.innerAngle = (int) LOVE_TODEG(innerAngle);
cone.outerAngle = (int) LOVE_TODEG(outerAngle);
if (channels > 1)
throw SpatialSupportException();
cone.innerAngle = (int) LOVE_TODEG(innerAngle);
cone.outerAngle = (int) LOVE_TODEG(outerAngle);
cone.outerVolume = outerVolume;
if (valid)
@@ -468,13 +523,19 @@ void Source::setCone(float innerAngle, float outerAngle, float outerVolume)
void Source::getCone(float &innerAngle, float &outerAngle, float &outerVolume) const
{
innerAngle = LOVE_TORAD(cone.innerAngle);
outerAngle = LOVE_TORAD(cone.outerAngle);
if (channels > 1)
throw SpatialSupportException();
innerAngle = LOVE_TORAD(cone.innerAngle);
outerAngle = LOVE_TORAD(cone.outerAngle);
outerVolume = cone.outerVolume;
}
void Source::setRelative(bool enable)
{
if (channels > 1)
throw SpatialSupportException();
if (valid)
alSourcei(source, AL_SOURCE_RELATIVE, relative ? AL_TRUE : AL_FALSE);
@@ -483,15 +544,18 @@ void Source::setRelative(bool enable)
bool Source::isRelative() const
{
if (channels > 1)
throw SpatialSupportException();
return relative;
}
void Source::setLooping(bool looping)
void Source::setLooping(bool enable)
{
if (valid && type == TYPE_STATIC)
alSourcei(source, AL_LOOPING, looping ? AL_TRUE : AL_FALSE);
alSourcei(source, AL_LOOPING, enable ? AL_TRUE : AL_FALSE);
this->looping = looping;
looping = enable;
}
bool Source::isLooping() const
@@ -546,9 +610,7 @@ void Source::stopAtomic()
if (valid)
{
if (type == TYPE_STATIC)
{
alSourceStop(source);
}
else if (type == TYPE_STREAM)
{
alSourceStop(source);
@@ -561,8 +623,10 @@ void Source::stopAtomic()
alSourceUnqueueBuffers(source, 1, &buffer);
}
}
alSourcei(source, AL_BUFFER, AL_NONE);
}
toLoop = 0;
valid = false;
}
@@ -627,7 +691,7 @@ void Source::reset()
alSourcef(source, AL_REFERENCE_DISTANCE, referenceDistance);
alSourcef(source, AL_ROLLOFF_FACTOR, rolloffFactor);
alSourcef(source, AL_MAX_DISTANCE, maxDistance);
alSourcei(source, AL_LOOPING, isStatic() && isLooping() ? AL_TRUE : AL_FALSE);
alSourcei(source, AL_LOOPING, (type == TYPE_STATIC) && isLooping() ? AL_TRUE : AL_FALSE);
alSourcei(source, AL_SOURCE_RELATIVE, relative ? AL_TRUE : AL_FALSE);
alSourcei(source, AL_CONE_INNER_ANGLE, cone.innerAngle);
alSourcei(source, AL_CONE_OUTER_ANGLE, cone.outerAngle);
@@ -651,8 +715,22 @@ ALenum Source::getFormat(int channels, int bitDepth) const
return AL_FORMAT_STEREO8;
else if (channels == 2 && bitDepth == 16)
return AL_FORMAT_STEREO16;
else
return 0;
#ifdef AL_EXT_MCFORMATS
if (alIsExtensionPresent("AL_EXT_MCFORMATS"))
{
if (channels == 6 && bitDepth == 8)
return AL_FORMAT_51CHN8;
else if (channels == 6 && bitDepth == 16)
return AL_FORMAT_51CHN16;
else if (channels == 8 && bitDepth == 8)
return AL_FORMAT_71CHN8;
else if (channels == 8 && bitDepth == 16)
return AL_FORMAT_71CHN16;
}
#endif
return 0;
}
int Source::streamAtomic(ALuint buffer, love::sound::Decoder *d)
@@ -690,19 +768,12 @@ int Source::streamAtomic(ALuint buffer, love::sound::Decoder *d)
return decoded;
}
bool Source::isStatic() const
{
return (type == TYPE_STATIC);
}
void Source::setMinVolume(float volume)
{
if (valid)
{
alSourcef(source, AL_MIN_GAIN, volume);
}
this->minVolume = volume;
minVolume = volume;
}
float Source::getMinVolume() const
@@ -721,11 +792,9 @@ float Source::getMinVolume() const
void Source::setMaxVolume(float volume)
{
if (valid)
{
alSourcef(source, AL_MAX_GAIN, volume);
}
this->maxVolume = volume;
maxVolume = volume;
}
float Source::getMaxVolume() const
@@ -738,21 +807,25 @@ float Source::getMaxVolume() const
}
// In case the Source isn't playing.
return this->maxVolume;
return maxVolume;
}
void Source::setReferenceDistance(float distance)
{
if (valid)
{
alSourcef(source, AL_REFERENCE_DISTANCE, distance);
}
if (channels > 1)
throw SpatialSupportException();
this->referenceDistance = distance;
if (valid)
alSourcef(source, AL_REFERENCE_DISTANCE, distance);
referenceDistance = distance;
}
float Source::getReferenceDistance() const
{
if (channels > 1)
throw SpatialSupportException();
if (valid)
{
ALfloat f;
@@ -761,21 +834,25 @@ float Source::getReferenceDistance() const
}
// In case the Source isn't playing.
return this->referenceDistance;
return referenceDistance;
}
void Source::setRolloffFactor(float factor)
{
if (valid)
{
alSourcef(source, AL_ROLLOFF_FACTOR, factor);
}
if (channels > 1)
throw SpatialSupportException();
this->rolloffFactor = factor;
if (valid)
alSourcef(source, AL_ROLLOFF_FACTOR, factor);
rolloffFactor = factor;
}
float Source::getRolloffFactor() const
{
if (channels > 1)
throw SpatialSupportException();
if (valid)
{
ALfloat f;
@@ -784,21 +861,27 @@ float Source::getRolloffFactor() const
}
// In case the Source isn't playing.
return this->rolloffFactor;
return rolloffFactor;
}
void Source::setMaxDistance(float distance)
{
if (valid)
{
alSourcef(source, AL_MAX_DISTANCE, distance);
}
if (channels > 1)
throw SpatialSupportException();
this->maxDistance = distance;
distance = std::min(distance, MAX_ATTENUATION_DISTANCE);
if (valid)
alSourcef(source, AL_MAX_DISTANCE, distance);
maxDistance = distance;
}
float Source::getMaxDistance() const
{
if (channels > 1)
throw SpatialSupportException();
if (valid)
{
ALfloat f;
@@ -807,7 +890,7 @@ float Source::getMaxDistance() const
}
// In case the Source isn't playing.
return this->maxDistance;
return maxDistance;
}
int Source::getChannels() const
+12 -14
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2014 LOVE Development Team
* Copyright (c) 2006-2015 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
@@ -29,9 +29,14 @@
#include "sound/Decoder.h"
// OpenAL
#ifdef LOVE_MACOSX_USE_FRAMEWORKS
#ifdef LOVE_APPLE_USE_FRAMEWORKS
#ifdef LOVE_IOS
#include <OpenAL/alc.h>
#include <OpenAL/al.h>
#else
#include <OpenAL-Soft/alc.h>
#include <OpenAL-Soft/al.h>
#endif
#else
#include <AL/alc.h>
#include <AL/al.h>
@@ -105,7 +110,6 @@ public:
virtual bool isRelative() const;
void setLooping(bool looping);
bool isLooping() const;
bool isStatic() const;
virtual void setMinVolume(float volume);
virtual float getMinVolume() const;
virtual void setMaxVolume(float volume);
@@ -148,7 +152,7 @@ private:
static const unsigned int MAX_BUFFERS = 8;
ALuint streamBuffers[MAX_BUFFERS];
Object::StrongRef<StaticDataBuffer> staticBuffer;
StrongRef<StaticDataBuffer> staticBuffer;
float pitch;
float volume;
@@ -166,15 +170,9 @@ private:
struct Cone
{
int innerAngle; // degrees
int outerAngle; // degrees
float outerVolume;
Cone()
: innerAngle(360)
, outerAngle(360)
, outerVolume(0.0f)
{}
int innerAngle = 360; // degrees
int outerAngle = 360; // degrees
float outerVolume = 0.0f;
} cone;
float offsetSamples;
@@ -183,7 +181,7 @@ private:
int sampleRate;
int channels;
Object::StrongRef<love::sound::Decoder> decoder;
StrongRef<love::sound::Decoder> decoder;
unsigned int toLoop;
+27 -11
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2014 LOVE Development Team
* Copyright (c) 2006-2015 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
@@ -44,7 +44,7 @@ int w_getSourceCount(lua_State *L)
int w_newSource(lua_State *L)
{
if (lua_isstring(L, 1) || luax_istype(L, 1, FILESYSTEM_FILE_T) || luax_istype(L, 1, FILESYSTEM_FILE_DATA_T))
if (lua_isstring(L, 1) || luax_istype(L, 1, FILESYSTEM_FILE_ID) || luax_istype(L, 1, FILESYSTEM_FILE_DATA_ID))
luax_convobj(L, 1, "sound", "newDecoder");
Source::Type stype = Source::TYPE_STREAM;
@@ -53,19 +53,21 @@ int w_newSource(lua_State *L)
if (stypestr && !Source::getConstant(stypestr, stype))
return luaL_error(L, "Invalid source type: %s", stypestr);
if (stype == Source::TYPE_STATIC && luax_istype(L, 1, SOUND_DECODER_T))
if (stype == Source::TYPE_STATIC && luax_istype(L, 1, SOUND_DECODER_ID))
luax_convobj(L, 1, "sound", "newSoundData");
Source *t = 0;
if (luax_istype(L, 1, SOUND_SOUND_DATA_T))
t = instance()->newSource(luax_totype<love::sound::SoundData>(L, 1, "SoundData", SOUND_SOUND_DATA_T));
else if (luax_istype(L, 1, SOUND_DECODER_T))
t = instance()->newSource(luax_totype<love::sound::Decoder>(L, 1, "Decoder", SOUND_DECODER_T));
luax_catchexcept(L, [&]() {
if (luax_istype(L, 1, SOUND_SOUND_DATA_ID))
t = instance()->newSource(luax_totype<love::sound::SoundData>(L, 1, SOUND_SOUND_DATA_ID));
else if (luax_istype(L, 1, SOUND_DECODER_ID))
t = instance()->newSource(luax_totype<love::sound::Decoder>(L, 1, SOUND_DECODER_ID));
});
if (t)
{
luax_pushtype(L, "Source", AUDIO_SOURCE_T, t);
luax_pushtype(L, AUDIO_SOURCE_ID, t);
t->release();
return 1;
}
@@ -216,6 +218,18 @@ int w_getVelocity(lua_State *L)
return 3;
}
int w_setDopplerScale(lua_State *L)
{
instance()->setDopplerScale(luax_checkfloat(L, 1));
return 0;
}
int w_getDopplerScale(lua_State *L)
{
lua_pushnumber(L, instance()->getDopplerScale());
return 1;
}
int w_record(lua_State *)
{
instance()->record();
@@ -229,7 +243,7 @@ int w_getRecordedData(lua_State *L)
lua_pushnil(L);
else
{
luax_pushtype(L, "SoundData", SOUND_SOUND_DATA_T, sd);
luax_pushtype(L, SOUND_SOUND_DATA_ID, sd);
sd->release();
}
return 1;
@@ -244,7 +258,7 @@ int w_stopRecording(lua_State *L)
lua_pushnil(L);
else
{
luax_pushtype(L, "SoundData", SOUND_SOUND_DATA_T, sd);
luax_pushtype(L, SOUND_SOUND_DATA_ID, sd);
sd->release();
}
return 1;
@@ -297,6 +311,8 @@ static const luaL_Reg functions[] =
{ "getOrientation", w_getOrientation },
{ "setVelocity", w_setVelocity },
{ "getVelocity", w_getVelocity },
{ "setDopplerScale", w_setDopplerScale },
{ "getDopplerScale", w_getDopplerScale },
/*{ "record", w_record },
{ "getRecordedData", w_getRecordedData },
{ "stopRecording", w_stopRecording },*/
@@ -353,7 +369,7 @@ extern "C" int luaopen_love_audio(lua_State *L)
WrappedModule w;
w.module = instance;
w.name = "audio";
w.flags = MODULE_T;
w.type = MODULE_ID;
w.functions = functions;
w.types = types;
+3 -1
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2014 LOVE Development Team
* Copyright (c) 2006-2015 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
@@ -47,6 +47,8 @@ int w_setOrientation(lua_State *L);
int w_getOrientation(lua_State *L);
int w_setVelocity(lua_State *L);
int w_getVelocity(lua_State *L);
int w_setDopplerScale(lua_State *L);
int w_getDopplerScale(lua_State *L);
int w_record(lua_State *L);
int w_getRecordedData(lua_State *L);
int w_stopRecording(lua_State *L);
+38 -28
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2014 LOVE Development Team
* Copyright (c) 2006-2015 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
@@ -29,7 +29,7 @@ namespace audio
Source *luax_checksource(lua_State *L, int idx)
{
return luax_checktype<Source>(L, idx, "Source", AUDIO_SOURCE_T);
return luax_checktype<Source>(L, idx, AUDIO_SOURCE_ID);
}
int w_Source_clone(lua_State *L)
@@ -37,7 +37,7 @@ int w_Source_clone(lua_State *L)
Source *t = luax_checksource(L, 1);
Source *clone = nullptr;
luax_catchexcept(L, [&](){ clone = t->clone(); });
luax_pushtype(L, "Source", AUDIO_SOURCE_T, clone);
luax_pushtype(L, AUDIO_SOURCE_ID, clone);
clone->release();
return 1;
}
@@ -147,7 +147,7 @@ int w_Source_setPosition(lua_State *L)
v[0] = (float)luaL_checknumber(L, 2);
v[1] = (float)luaL_checknumber(L, 3);
v[2] = (float)luaL_optnumber(L, 4, 0);
t->setPosition(v);
luax_catchexcept(L, [&](){ t->setPosition(v); });
return 0;
}
@@ -155,7 +155,7 @@ int w_Source_getPosition(lua_State *L)
{
Source *t = luax_checksource(L, 1);
float v[3];
t->getPosition(v);
luax_catchexcept(L, [&](){ t->getPosition(v); });
lua_pushnumber(L, v[0]);
lua_pushnumber(L, v[1]);
lua_pushnumber(L, v[2]);
@@ -169,7 +169,7 @@ int w_Source_setVelocity(lua_State *L)
v[0] = (float)luaL_checknumber(L, 2);
v[1] = (float)luaL_checknumber(L, 3);
v[2] = (float)luaL_optnumber(L, 4, 0);
t->setVelocity(v);
luax_catchexcept(L, [&](){ t->setVelocity(v); });
return 0;
}
@@ -177,7 +177,7 @@ int w_Source_getVelocity(lua_State *L)
{
Source *t = luax_checksource(L, 1);
float v[3];
t->getVelocity(v);
luax_catchexcept(L, [&](){ t->getVelocity(v); });
lua_pushnumber(L, v[0]);
lua_pushnumber(L, v[1]);
lua_pushnumber(L, v[2]);
@@ -191,7 +191,7 @@ int w_Source_setDirection(lua_State *L)
v[0] = (float)luaL_checknumber(L, 2);
v[1] = (float)luaL_checknumber(L, 3);
v[2] = (float)luaL_optnumber(L, 4, 0);
t->setDirection(v);
luax_catchexcept(L, [&](){ t->setDirection(v); });
return 0;
}
@@ -199,7 +199,7 @@ int w_Source_getDirection(lua_State *L)
{
Source *t = luax_checksource(L, 1);
float v[3];
t->getDirection(v);
luax_catchexcept(L, [&](){ t->getDirection(v); });
lua_pushnumber(L, v[0]);
lua_pushnumber(L, v[1]);
lua_pushnumber(L, v[2]);
@@ -212,7 +212,7 @@ int w_Source_setCone(lua_State *L)
float innerAngle = (float) luaL_checknumber(L, 2);
float outerAngle = (float) luaL_checknumber(L, 3);
float outerVolume = (float) luaL_optnumber(L, 4, 0.0);
t->setCone(innerAngle, outerAngle, outerVolume);
luax_catchexcept(L, [&](){ t->setCone(innerAngle, outerAngle, outerVolume); });
return 0;
}
@@ -220,7 +220,7 @@ int w_Source_getCone(lua_State *L)
{
Source *t = luax_checksource(L, 1);
float innerAngle, outerAngle, outerVolume;
t->getCone(innerAngle, outerAngle, outerVolume);
luax_catchexcept(L, [&](){ t->getCone(innerAngle, outerAngle, outerVolume); });
lua_pushnumber(L, innerAngle);
lua_pushnumber(L, outerAngle);
lua_pushnumber(L, outerVolume);
@@ -230,14 +230,14 @@ int w_Source_getCone(lua_State *L)
int w_Source_setRelative(lua_State *L)
{
Source *t = luax_checksource(L, 1);
t->setRelative(luax_toboolean(L, 2));
luax_catchexcept(L, [&](){ t->setRelative(luax_toboolean(L, 2)); });
return 0;
}
int w_Source_isRelative(lua_State *L)
{
Source *t = luax_checksource(L, 1);
luax_pushboolean(L, t->isRelative());
luax_catchexcept(L, [&](){ luax_pushboolean(L, t->isRelative()); });
return 1;
}
@@ -276,13 +276,6 @@ int w_Source_isPlaying(lua_State *L)
return 1;
}
int w_Source_isStatic(lua_State *L)
{
Source *t = luax_checksource(L, 1);
luax_pushboolean(L, t->isStatic());
return 1;
}
int w_Source_setVolumeLimits(lua_State *L)
{
Source *t = luax_checksource(L, 1);
@@ -310,16 +303,20 @@ int w_Source_setAttenuationDistances(lua_State *L)
float dmax = (float)luaL_checknumber(L, 3);
if (dref < .0f || dmax < .0f)
return luaL_error(L, "Invalid distances: %f, %f. Must be > 0", dref, dmax);
t->setReferenceDistance(dref);
t->setMaxDistance(dmax);
luax_catchexcept(L, [&]() {
t->setReferenceDistance(dref);
t->setMaxDistance(dmax);
});
return 0;
}
int w_Source_getAttenuationDistances(lua_State *L)
{
Source *t = luax_checksource(L, 1);
lua_pushnumber(L, t->getReferenceDistance());
lua_pushnumber(L, t->getMaxDistance());
luax_catchexcept(L, [&]() {
lua_pushnumber(L, t->getReferenceDistance());
lua_pushnumber(L, t->getMaxDistance());
});
return 2;
}
@@ -329,14 +326,14 @@ int w_Source_setRolloff(lua_State *L)
float rolloff = (float)luaL_checknumber(L, 2);
if (rolloff < .0f)
return luaL_error(L, "Invalid rolloff: %f. Must be > 0.", rolloff);
t->setRolloffFactor(rolloff);
luax_catchexcept(L, [&](){ t->setRolloffFactor(rolloff); });
return 0;
}
int w_Source_getRolloff(lua_State *L)
{
Source *t = luax_checksource(L, 1);
lua_pushnumber(L, t->getRolloffFactor());
luax_catchexcept(L, [&](){ lua_pushnumber(L, t->getRolloffFactor()); });
return 1;
}
@@ -347,6 +344,19 @@ int w_Source_getChannels(lua_State *L)
return 1;
}
int w_Source_getType(lua_State *L)
{
Source *t = luax_checksource(L, 1);
Source::Type type = t->getType();
const char *str = nullptr;
if (!Source::getConstant(type, str))
return luaL_error(L, "Unknown Source type.");
lua_pushstring(L, str);
return 1;
}
static const luaL_Reg functions[] =
{
{ "clone", w_Source_clone },
@@ -380,7 +390,6 @@ static const luaL_Reg functions[] =
{ "isStopped", w_Source_isStopped },
{ "isPaused", w_Source_isPaused },
{ "isPlaying", w_Source_isPlaying },
{ "isStatic", w_Source_isStatic },
{ "setVolumeLimits", w_Source_setVolumeLimits },
{ "getVolumeLimits", w_Source_getVolumeLimits },
@@ -390,13 +399,14 @@ static const luaL_Reg functions[] =
{ "getRolloff", w_Source_getRolloff},
{ "getChannels", w_Source_getChannels },
{ "getType", w_Source_getType },
{ 0, 0 }
};
extern "C" int luaopen_source(lua_State *L)
{
return luax_register_type(L, "Source", functions);
return luax_register_type(L, AUDIO_SOURCE_ID, functions);
}
} // audio
+2 -2
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2014 LOVE Development Team
* Copyright (c) 2006-2015 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
@@ -57,7 +57,6 @@ int w_Source_isLooping(lua_State *L);
int w_Source_isStopped(lua_State *L);
int w_Source_isPaused(lua_State *L);
int w_Source_isPlaying(lua_State *L);
int w_Source_isStatic(lua_State *L);
int w_Source_setVolumeLimits(lua_State *L);
int w_Source_getVolumeLimits(lua_State *L);
int w_Source_setAttenuationDistances(lua_State *L);
@@ -65,6 +64,7 @@ int w_Source_getAttenuationDistances(lua_State *L);
int w_Source_setRolloff(lua_State *L);
int w_Source_getRolloff(lua_State *L);
int w_Source_getChannels(lua_State *L);
int w_Source_getType(lua_State *L);
extern "C" int luaopen_source(lua_State *L);
} // audio
+28 -27
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2014 LOVE Development Team
* Copyright (c) 2006-2015 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
@@ -28,56 +28,57 @@ namespace love
namespace event
{
Message::Message(const std::string &name, Variant *a, Variant *b, Variant *c, Variant *d)
Message::Message(const std::string &name, const std::vector<StrongRef<Variant>> &vargs)
: name(name)
, nargs(0)
, args(vargs)
{
args[0] = a;
args[1] = b;
args[2] = c;
args[3] = d;
for (int i = 0; i < 4; i++)
{
if (!args[i])
break;
args[i]->retain();
nargs++;
}
}
Message::~Message()
{
for (int i = 0; i < nargs; i++)
args[i]->release();
}
int Message::toLua(lua_State *L)
{
luax_pushstring(L, name);
for (int i = 0; i < nargs; i++)
args[i]->toLua(L);
return nargs+1;
for (const StrongRef<Variant> &v : args)
{
if (v.get() != nullptr)
v->toLua(L);
else
lua_pushnil(L);
}
return (int) args.size() + 1;
}
Message *Message::fromLua(lua_State *L, int n)
{
std::string name = luax_checkstring(L, n);
std::vector<StrongRef<Variant>> vargs;
int count = lua_gettop(L) - n;
n++;
Message *m = new Message(name);
for (int i = 0; i < 4; i++)
for (int i = 0; i < count; i++)
{
if (lua_isnoneornil(L, n+i))
break;
m->args[i] = Variant::fromLua(L, n+i);
if (!m->args[i])
vargs.push_back(Variant::fromLua(L, n+i));
if (!vargs.back().get())
{
delete m;
vargs.clear();
luaL_error(L, "Argument %d can't be stored safely\nExpected boolean, number, string or userdata.", n+i);
return NULL;
return nullptr;
}
m->nargs++;
vargs.back()->release(); // fromLua gave a +1 ref.
}
return m;
return new Message(name, vargs);
}
Event::Event()
+14 -9
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2014 LOVE Development Team
* Copyright (c) 2006-2015 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
@@ -30,27 +30,31 @@
#include "joystick/Joystick.h"
#include "thread/threads.h"
// STL
// C++
#include <queue>
#include <vector>
namespace love
{
namespace event
{
class Message : public Object
{
private:
std::string name;
Variant *args[4];
int nargs;
public:
Message(const std::string &name, Variant *a = NULL, Variant *b = NULL, Variant *c = NULL, Variant *d = NULL);
Message(const std::string &name, const std::vector<StrongRef<Variant>> &vargs = {});
~Message();
int toLua(lua_State *L);
static Message *fromLua(lua_State *L, int n);
};
private:
std::string name;
std::vector<StrongRef<Variant>> args;
}; // Message
class Event : public Module
{
@@ -66,6 +70,7 @@ public:
virtual void clear();
virtual void pump() = 0;
virtual Message *wait() = 0;
protected:
thread::Mutex *mutex;
+300 -159
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2014 LOVE Development Team
* Copyright (c) 2006-2015 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
@@ -20,10 +20,13 @@
#include "Event.h"
#include "keyboard/Keyboard.h"
#include "filesystem/DroppedFile.h"
#include "filesystem/Filesystem.h"
#include "keyboard/sdl/Keyboard.h"
#include "mouse/Mouse.h"
#include "joystick/JoystickModule.h"
#include "joystick/sdl/Joystick.h"
#include "touch/sdl/Touch.h"
#include "graphics/Graphics.h"
#include "window/Window.h"
#include "common/Exception.h"
@@ -40,16 +43,52 @@ namespace sdl
// SDL reports mouse coordinates in the window coordinate system in OS X, but
// we want them in pixel coordinates (may be different with high-DPI enabled.)
static void windowToPixelCoords(int *x, int *y)
static void windowToPixelCoords(double *x, double *y)
{
window::Window *window = Module::getInstance<window::Window>(Module::M_WINDOW);
if (window && x)
*x = (int) window->toPixels(*x);
if (window && y)
*y = (int) window->toPixels(*y);
if (window)
window->windowToPixelCoords(x, y);
}
#ifndef LOVE_MACOSX
static void normalizedToPixelCoords(double *x, double *y)
{
window::Window *window = Module::getInstance<window::Window>(Module::M_WINDOW);
int w = 1, h = 1;
if (window)
window->getPixelDimensions(w, h);
if (x)
*x = ((*x) * (double) w);
if (y)
*y = ((*y) * (double) h);
}
#endif
// SDL's event watch callbacks trigger when the event is actually posted inside
// SDL, unlike with SDL_PollEvents. This is useful for some events which require
// handling inside the function which triggered them on some backends.
static int SDLCALL watchAppEvents(void * /*udata*/, SDL_Event *event)
{
graphics::Graphics *gfx = Module::getInstance<graphics::Graphics>(Module::M_GRAPHICS);
switch (event->type)
{
// On iOS, calling any OpenGL ES function after the function which triggers
// SDL_APP_DIDENTERBACKGROUND is called will kill the app, so we handle it
// with an event watch callback, which will be called inside that function.
case SDL_APP_DIDENTERBACKGROUND:
case SDL_APP_WILLENTERFOREGROUND:
if (gfx)
gfx->setActive(event->type == SDL_APP_WILLENTERFOREGROUND);
break;
default:
break;
}
return 1;
}
const char *Event::getName() const
{
@@ -60,10 +99,13 @@ Event::Event()
{
if (SDL_InitSubSystem(SDL_INIT_EVENTS) < 0)
throw love::Exception("%s", SDL_GetError());
SDL_AddEventWatch(watchAppEvents, this);
}
Event::~Event()
{
SDL_DelEventWatch(watchAppEvents, this);
SDL_QuitSubSystem(SDL_INIT_EVENTS);
}
@@ -106,16 +148,31 @@ void Event::clear()
Message *Event::convert(const SDL_Event &e) const
{
Message *msg = NULL;
Message *msg = nullptr;
love::keyboard::Keyboard *kb = 0;
std::vector<StrongRef<Variant>> vargs;
vargs.reserve(4);
love::keyboard::Keyboard::Key key;
love::keyboard::Keyboard *kb = nullptr;
love::filesystem::Filesystem *filesystem = nullptr;
love::keyboard::Keyboard::Key key = love::keyboard::Keyboard::KEY_UNKNOWN;
love::keyboard::Keyboard::Scancode scancode = love::keyboard::Keyboard::SCANCODE_UNKNOWN;
love::mouse::Mouse::Button button;
Variant *arg1, *arg2, *arg3;
const char *txt;
const char *txt2;
std::map<SDL_Keycode, love::keyboard::Keyboard::Key>::const_iterator keyit;
#ifndef LOVE_MACOSX
love::touch::sdl::Touch *touchmodule = nullptr;
love::touch::Touch::TouchInfo touchinfo;
#endif
#ifdef LOVE_LINUX
static bool touchNormalizationBug = false;
#endif
switch (e.type)
{
case SDL_KEYDOWN:
@@ -129,83 +186,139 @@ Message *Event::convert(const SDL_Event &e) const
keyit = keys.find(e.key.keysym.sym);
if (keyit != keys.end())
key = keyit->second;
else
key = love::keyboard::Keyboard::KEY_UNKNOWN;
if (!love::keyboard::Keyboard::getConstant(key, txt))
txt = "unknown";
arg1 = new Variant(txt, strlen(txt));
arg2 = new Variant(e.key.repeat != 0);
msg = new Message("keypressed", arg1, arg2);
arg1->release();
arg2->release();
love::keyboard::sdl::Keyboard::getConstant(e.key.keysym.scancode, scancode);
if (!love::keyboard::Keyboard::getConstant(scancode, txt2))
txt2 = "unknown";
vargs.push_back(new Variant(txt, strlen(txt)));
vargs.push_back(new Variant(txt2, strlen(txt2)));
vargs.push_back(new Variant(e.key.repeat != 0));
msg = new Message("keypressed", vargs);
break;
case SDL_KEYUP:
keyit = keys.find(e.key.keysym.sym);
if (keyit != keys.end())
key = keyit->second;
else
key = love::keyboard::Keyboard::KEY_UNKNOWN;
if (!love::keyboard::Keyboard::getConstant(key, txt))
txt = "unknown";
arg1 = new Variant(txt, strlen(txt));
msg = new Message("keyreleased", arg1);
arg1->release();
love::keyboard::sdl::Keyboard::getConstant(e.key.keysym.scancode, scancode);
if (!love::keyboard::Keyboard::getConstant(scancode, txt2))
txt2 = "unknown";
vargs.push_back(new Variant(txt, strlen(txt)));
vargs.push_back(new Variant(txt2, strlen(txt2)));
msg = new Message("keyreleased", vargs);
break;
case SDL_TEXTINPUT:
txt = e.text.text;
arg1 = new Variant(txt, strlen(txt));
msg = new Message("textinput", arg1);
arg1->release();
vargs.push_back(new Variant(txt, strlen(txt)));
msg = new Message("textinput", vargs);
break;
case SDL_TEXTEDITING:
txt = e.edit.text;
arg1 = new Variant(txt, strlen(txt));
arg2 = new Variant((double) e.edit.start);
arg3 = new Variant((double) e.edit.length);
msg = new Message("textedit", arg1, arg2, arg3);
arg1->release();
arg2->release();
arg3->release();
vargs.push_back(new Variant(txt, strlen(txt)));
vargs.push_back(new Variant((double) e.edit.start));
vargs.push_back(new Variant((double) e.edit.length));
msg = new Message("textedit", vargs);
break;
case SDL_MOUSEMOTION:
{
double x = (double) e.motion.x;
double y = (double) e.motion.y;
double xrel = (double) e.motion.xrel;
double yrel = (double) e.motion.yrel;
windowToPixelCoords(&x, &y);
windowToPixelCoords(&xrel, &yrel);
vargs.push_back(new Variant(x));
vargs.push_back(new Variant(y));
vargs.push_back(new Variant(xrel));
vargs.push_back(new Variant(yrel));
vargs.push_back(new Variant(e.motion.which == SDL_TOUCH_MOUSEID));
msg = new Message("mousemoved", vargs);
}
break;
case SDL_MOUSEBUTTONDOWN:
case SDL_MOUSEBUTTONUP:
if (buttons.find(e.button.button, button) && mouse::Mouse::getConstant(button, txt))
{
int x = e.button.x;
int y = e.button.y;
double x = (double) e.button.x;
double y = (double) e.button.y;
windowToPixelCoords(&x, &y);
arg1 = new Variant((double) x);
arg2 = new Variant((double) y);
arg3 = new Variant(txt, strlen(txt));
vargs.push_back(new Variant(x));
vargs.push_back(new Variant(y));
vargs.push_back(new Variant(txt, strlen(txt)));
vargs.push_back(new Variant(e.button.which == SDL_TOUCH_MOUSEID));
msg = new Message((e.type == SDL_MOUSEBUTTONDOWN) ?
"mousepressed" : "mousereleased",
arg1, arg2, arg3);
arg1->release();
arg2->release();
arg3->release();
vargs);
}
break;
case SDL_MOUSEWHEEL:
if (e.wheel.y != 0)
vargs.push_back(new Variant((double) e.wheel.x));
vargs.push_back(new Variant((double) e.wheel.y));
msg = new Message("wheelmoved", vargs);
break;
case SDL_FINGERDOWN:
case SDL_FINGERUP:
case SDL_FINGERMOTION:
// Touch events are disabled in OS X because we only actually want touch
// screen events, but most touch devices in OS X aren't touch screens
// (and SDL doesn't differentiate.) Non-screen touch devices like Mac
// trackpads won't give touch coords in the window's coordinate-space.
#ifndef LOVE_MACOSX
touchinfo.id = (int64) e.tfinger.fingerId;
touchinfo.x = e.tfinger.x;
touchinfo.y = e.tfinger.y;
touchinfo.dx = e.tfinger.dx;
touchinfo.dy = e.tfinger.dy;
#ifdef LOVE_LINUX
// FIXME: hacky workaround for SDL not normalizing touch coordinates in
// its X11 backend: https://bugzilla.libsdl.org/show_bug.cgi?id=2307
if (touchNormalizationBug || fabs(touchinfo.x) >= 1.5 || fabs(touchinfo.y) >= 1.5 || fabs(touchinfo.dx) >= 1.5 || fabs(touchinfo.dy) >= 1.5)
{
button = (e.wheel.y > 0) ? mouse::Mouse::BUTTON_WHEELUP : mouse::Mouse::BUTTON_WHEELDOWN;
if (!love::mouse::Mouse::getConstant(button, txt))
break;
int mx, my;
SDL_GetMouseState(&mx, &my);
windowToPixelCoords(&mx, &my);
arg1 = new Variant((double) mx);
arg2 = new Variant((double) my);
arg3 = new Variant(txt, strlen(txt));
msg = new Message("mousepressed", arg1, arg2, arg3);
arg1->release();
arg2->release();
arg3->release();
touchNormalizationBug = true;
windowToPixelCoords(&touchinfo.x, &touchinfo.y);
windowToPixelCoords(&touchinfo.dx, &touchinfo.dy);
}
else
#endif
{
// SDL's coords are normalized to [0, 1], but we want them in pixels.
normalizedToPixelCoords(&touchinfo.x, &touchinfo.y);
normalizedToPixelCoords(&touchinfo.dx, &touchinfo.dy);
}
// We need to update the love.touch.sdl internal state from here.
touchmodule = (touch::sdl::Touch *) Module::getInstance("love.touch.sdl");
if (touchmodule)
touchmodule->onEvent(e.type, touchinfo);
// This is a bit hackish and we lose the higher 32 bits of the id on
// 32-bit systems, but SDL only ever gives id's that at most use as many
// bits as can fit in a pointer (for now.)
// We use lightuserdata instead of a lua_Number (double) because doubles
// can't represent all possible id values on 64-bit systems.
vargs.push_back(new Variant((void *) (intptr_t) touchinfo.id));
vargs.push_back(new Variant(touchinfo.x));
vargs.push_back(new Variant(touchinfo.y));
vargs.push_back(new Variant(touchinfo.dx));
vargs.push_back(new Variant(touchinfo.dy));
if (e.type == SDL_FINGERDOWN)
txt = "touchpressed";
else if (e.type == SDL_FINGERUP)
txt = "touchreleased";
else
txt = "touchmoved";
msg = new Message(txt, vargs);
#endif
break;
case SDL_JOYBUTTONDOWN:
case SDL_JOYBUTTONUP:
@@ -223,15 +336,47 @@ Message *Event::convert(const SDL_Event &e) const
msg = convertWindowEvent(e);
break;
case SDL_DROPFILE:
filesystem = Module::getInstance<filesystem::Filesystem>(Module::M_FILESYSTEM);
if (filesystem != nullptr)
{
// Allow mounting any dropped path, so zips or dirs can be mounted.
filesystem->allowMountingForPath(e.drop.file);
if (filesystem->isRealDirectory(e.drop.file))
{
vargs.push_back(new Variant(e.drop.file, strlen(e.drop.file)));
msg = new Message("directorydropped", vargs);
}
else
{
Proxy proxy;
proxy.object = new love::filesystem::DroppedFile(e.drop.file);
proxy.type = FILESYSTEM_DROPPED_FILE_ID;
vargs.push_back(new Variant(proxy.type, &proxy));
msg = new Message("filedropped", vargs);
proxy.object->release();
}
}
SDL_free(e.drop.file);
break;
case SDL_QUIT:
case SDL_APP_TERMINATING:
msg = new Message("quit");
break;
case SDL_APP_LOWMEMORY:
msg = new Message("lowmemory");
break;
default:
break;
}
// We gave +1 refs to the StrongRef list, so we should release them.
for (const StrongRef<Variant> &v : vargs)
{
if (v.get() != nullptr)
v->release();
}
return msg;
}
@@ -239,66 +384,61 @@ Message *Event::convertJoystickEvent(const SDL_Event &e) const
{
joystick::JoystickModule *joymodule = Module::getInstance<joystick::JoystickModule>(Module::M_JOYSTICK);
if (!joymodule)
return 0;
return nullptr;
Message *msg = nullptr;
std::vector<StrongRef<Variant>> vargs;
vargs.reserve(4);
Message *msg = 0;
Proxy proxy;
love::joystick::Joystick::Hat hat;
love::joystick::Joystick::GamepadButton padbutton;
love::joystick::Joystick::GamepadAxis padaxis;
Variant *arg1, *arg2, *arg3;
const char *txt;
switch (e.type)
{
case SDL_JOYBUTTONDOWN:
case SDL_JOYBUTTONUP:
proxy.flags = JOYSTICK_JOYSTICK_T;
proxy.data = joymodule->getJoystickFromID(e.jbutton.which);
if (!proxy.data)
proxy.type = JOYSTICK_JOYSTICK_ID;
proxy.object = joymodule->getJoystickFromID(e.jbutton.which);
if (!proxy.object)
break;
arg1 = new Variant(JOYSTICK_JOYSTICK_ID, (void *) &proxy);
arg2 = new Variant((double)(e.jbutton.button+1));
vargs.push_back(new Variant(proxy.type, (void *) &proxy));
vargs.push_back(new Variant((double)(e.jbutton.button+1)));
msg = new Message((e.type == SDL_JOYBUTTONDOWN) ?
"joystickpressed" : "joystickreleased",
arg1, arg2);
arg1->release();
arg2->release();
vargs);
break;
case SDL_JOYAXISMOTION:
{
proxy.flags = JOYSTICK_JOYSTICK_T;
proxy.data = joymodule->getJoystickFromID(e.jaxis.which);
if (!proxy.data)
proxy.type = JOYSTICK_JOYSTICK_ID;
proxy.object = joymodule->getJoystickFromID(e.jaxis.which);
if (!proxy.object)
break;
arg1 = new Variant(JOYSTICK_JOYSTICK_ID, (void *) &proxy);
arg2 = new Variant((double)(e.jaxis.axis+1));
vargs.push_back(new Variant(proxy.type, (void *) &proxy));
vargs.push_back(new Variant((double)(e.jaxis.axis+1)));
float value = joystick::Joystick::clampval(e.jaxis.value / 32768.0f);
arg3 = new Variant((double) value);
msg = new Message("joystickaxis", arg1, arg2, arg3);
arg1->release();
arg2->release();
arg3->release();
vargs.push_back(new Variant((double) value));
msg = new Message("joystickaxis", vargs);
}
break;
case SDL_JOYHATMOTION:
if (!joystick::sdl::Joystick::getConstant(e.jhat.value, hat) || !joystick::Joystick::getConstant(hat, txt))
break;
proxy.flags = JOYSTICK_JOYSTICK_T;
proxy.data = joymodule->getJoystickFromID(e.jhat.which);
if (!proxy.data)
proxy.type = JOYSTICK_JOYSTICK_ID;
proxy.object = joymodule->getJoystickFromID(e.jhat.which);
if (!proxy.object)
break;
arg1 = new Variant(JOYSTICK_JOYSTICK_ID, (void *) &proxy);
arg2 = new Variant((double)(e.jhat.hat+1));
arg3 = new Variant(txt, strlen(txt));
msg = new Message("joystickhat", arg1, arg2, arg3);
arg1->release();
arg2->release();
arg3->release();
vargs.push_back(new Variant(proxy.type, (void *) &proxy));
vargs.push_back(new Variant((double)(e.jhat.hat+1)));
vargs.push_back(new Variant(txt, strlen(txt)));
msg = new Message("joystickhat", vargs);
break;
case SDL_CONTROLLERBUTTONDOWN:
case SDL_CONTROLLERBUTTONUP:
@@ -308,17 +448,15 @@ Message *Event::convertJoystickEvent(const SDL_Event &e) const
if (!joystick::Joystick::getConstant(padbutton, txt))
break;
proxy.flags = JOYSTICK_JOYSTICK_T;
proxy.data = joymodule->getJoystickFromID(e.cbutton.which);
if (!proxy.data)
proxy.type = JOYSTICK_JOYSTICK_ID;
proxy.object = joymodule->getJoystickFromID(e.cbutton.which);
if (!proxy.object)
break;
arg1 = new Variant(JOYSTICK_JOYSTICK_ID, (void *) &proxy);
arg2 = new Variant(txt, strlen(txt));
vargs.push_back(new Variant(proxy.type, (void *) &proxy));
vargs.push_back(new Variant(txt, strlen(txt)));
msg = new Message(e.type == SDL_CONTROLLERBUTTONDOWN ?
"gamepadpressed" : "gamepadreleased", arg1, arg2);
arg1->release();
arg2->release();
"gamepadpressed" : "gamepadreleased", vargs);
break;
case SDL_CONTROLLERAXISMOTION:
if (joystick::sdl::Joystick::getConstant((SDL_GameControllerAxis) e.caxis.axis, padaxis))
@@ -326,60 +464,65 @@ Message *Event::convertJoystickEvent(const SDL_Event &e) const
if (!joystick::Joystick::getConstant(padaxis, txt))
break;
proxy.flags = JOYSTICK_JOYSTICK_T;
proxy.data = joymodule->getJoystickFromID(e.caxis.which);
if (!proxy.data)
proxy.type = JOYSTICK_JOYSTICK_ID;
proxy.object = joymodule->getJoystickFromID(e.caxis.which);
if (!proxy.object)
break;
arg1 = new Variant(JOYSTICK_JOYSTICK_ID, (void *) &proxy);
vargs.push_back(new Variant(proxy.type, (void *) &proxy));
arg2 = new Variant(txt, strlen(txt));
vargs.push_back(new Variant(txt, strlen(txt)));
float value = joystick::Joystick::clampval(e.caxis.value / 32768.0f);
arg3 = new Variant((double) value);
msg = new Message("gamepadaxis", arg1, arg2, arg3);
arg1->release();
arg2->release();
arg3->release();
vargs.push_back(new Variant((double) value));
msg = new Message("gamepadaxis", vargs);
}
break;
case SDL_JOYDEVICEADDED:
// jdevice.which is the joystick device index.
proxy.data = joymodule->addJoystick(e.jdevice.which);
proxy.flags = JOYSTICK_JOYSTICK_T;
if (proxy.data)
proxy.object = joymodule->addJoystick(e.jdevice.which);
proxy.type = JOYSTICK_JOYSTICK_ID;
if (proxy.object)
{
arg1 = new Variant(JOYSTICK_JOYSTICK_ID, (void *) &proxy);
msg = new Message("joystickadded", arg1);
arg1->release();
vargs.push_back(new Variant(proxy.type, (void *) &proxy));
msg = new Message("joystickadded", vargs);
}
break;
case SDL_JOYDEVICEREMOVED:
// jdevice.which is the joystick instance ID now.
proxy.data = joymodule->getJoystickFromID(e.jdevice.which);
proxy.flags = JOYSTICK_JOYSTICK_T;
if (proxy.data)
proxy.object = joymodule->getJoystickFromID(e.jdevice.which);
proxy.type = JOYSTICK_JOYSTICK_ID;
if (proxy.object)
{
joymodule->removeJoystick((joystick::Joystick *) proxy.data);
arg1 = new Variant(JOYSTICK_JOYSTICK_ID, (void *) &proxy);
msg = new Message("joystickremoved", arg1);
arg1->release();
joymodule->removeJoystick((joystick::Joystick *) proxy.object);
vargs.push_back(new Variant(proxy.type, (void *) &proxy));
msg = new Message("joystickremoved", vargs);
}
break;
default:
break;
}
// We gave +1 refs to the StrongRef list, so we should release them.
for (const StrongRef<Variant> &v : vargs)
{
if (v.get() != nullptr)
v->release();
}
return msg;
}
Message *Event::convertWindowEvent(const SDL_Event &e) const
{
Message *msg = 0;
Variant *arg1, *arg2, *arg3, *arg4;
window::Window *win = 0;
Message *msg = nullptr;
std::vector<StrongRef<Variant>> vargs;
vargs.reserve(4);
window::Window *win = nullptr;
if (e.type != SDL_WINDOWEVENT)
return 0;
return nullptr;
switch (e.window.event)
{
@@ -391,53 +534,47 @@ Message *Event::convertWindowEvent(const SDL_Event &e) const
SDL_DisableScreenSaver();
else
SDL_EnableScreenSaver();
arg1 = new Variant(e.window.event == SDL_WINDOWEVENT_FOCUS_GAINED);
msg = new Message("focus", arg1);
arg1->release();
vargs.push_back(new Variant(e.window.event == SDL_WINDOWEVENT_FOCUS_GAINED));
msg = new Message("focus", vargs);
break;
case SDL_WINDOWEVENT_ENTER:
case SDL_WINDOWEVENT_LEAVE:
arg1 = new Variant(e.window.event == SDL_WINDOWEVENT_ENTER);
msg = new Message("mousefocus", arg1);
arg1->release();
vargs.push_back(new Variant(e.window.event == SDL_WINDOWEVENT_ENTER));
msg = new Message("mousefocus", vargs);
break;
case SDL_WINDOWEVENT_SHOWN:
case SDL_WINDOWEVENT_HIDDEN:
arg1 = new Variant(e.window.event == SDL_WINDOWEVENT_SHOWN);
msg = new Message("visible", arg1);
arg1->release();
vargs.push_back(new Variant(e.window.event == SDL_WINDOWEVENT_SHOWN));
msg = new Message("visible", vargs);
break;
case SDL_WINDOWEVENT_RESIZED:
win = Module::getInstance<window::Window>(Module::M_WINDOW);
if (win)
{
int px_w = e.window.data1;
int px_h = e.window.data2;
// FIXME: disabled in Linux for runtime SDL 2.0.0 compatibility.
#if SDL_VERSION_ATLEAST(2,0,1) && !defined(LOVE_LINUX)
SDL_Window *sdlwin = SDL_GetWindowFromID(e.window.windowID);
if (sdlwin)
SDL_GL_GetDrawableSize(sdlwin, &px_w, &px_h);
#endif
win->onWindowResize(e.window.data1, e.window.data2);
graphics::Graphics *gfx = Module::getInstance<graphics::Graphics>(Module::M_GRAPHICS);
if (gfx)
gfx->setViewportSize(px_w, px_h);
arg1 = new Variant((double) px_w);
arg2 = new Variant((double) px_h);
arg3 = new Variant((double) e.window.data1);
arg4 = new Variant((double) e.window.data2);
msg = new Message("resize", arg1, arg2, arg3, arg4);
arg1->release();
arg2->release();
arg3->release();
arg4->release();
vargs.push_back(new Variant((double) px_w));
vargs.push_back(new Variant((double) px_h));
vargs.push_back(new Variant((double) e.window.data1));
vargs.push_back(new Variant((double) e.window.data2));
msg = new Message("resize", vargs);
}
break;
case SDL_WINDOWEVENT_SIZE_CHANGED:
win = Module::getInstance<window::Window>(Module::M_WINDOW);
if (win)
win->onSizeChanged(e.window.data1, e.window.data2);
break;
}
// We gave +1 refs to the StrongRef list, so we should release them.
for (const StrongRef<Variant> &v : vargs)
{
if (v.get() != nullptr)
v->release();
}
return msg;
@@ -459,6 +596,7 @@ std::map<SDL_Keycode, love::keyboard::Keyboard::Key> Event::createKeyMap()
k[SDLK_EXCLAIM] = Keyboard::KEY_EXCLAIM;
k[SDLK_QUOTEDBL] = Keyboard::KEY_QUOTEDBL;
k[SDLK_HASH] = Keyboard::KEY_HASH;
k[SDLK_PERCENT] = Keyboard::KEY_PERCENT;
k[SDLK_DOLLAR] = Keyboard::KEY_DOLLAR;
k[SDLK_AMPERSAND] = Keyboard::KEY_AMPERSAND;
k[SDLK_QUOTE] = Keyboard::KEY_QUOTE;
@@ -660,11 +798,14 @@ std::map<SDL_Keycode, love::keyboard::Keyboard::Key> Event::keys = Event::create
EnumMap<love::mouse::Mouse::Button, Uint8, love::mouse::Mouse::BUTTON_MAX_ENUM>::Entry Event::buttonEntries[] =
{
{ love::mouse::Mouse::BUTTON_LEFT, SDL_BUTTON_LEFT},
{ love::mouse::Mouse::BUTTON_MIDDLE, SDL_BUTTON_MIDDLE},
{ love::mouse::Mouse::BUTTON_RIGHT, SDL_BUTTON_RIGHT},
{ love::mouse::Mouse::BUTTON_X1, SDL_BUTTON_X1},
{ love::mouse::Mouse::BUTTON_X2, SDL_BUTTON_X2},
{love::mouse::Mouse::BUTTON_LEFT, SDL_BUTTON_LEFT},
{love::mouse::Mouse::BUTTON_MIDDLE, SDL_BUTTON_MIDDLE},
{love::mouse::Mouse::BUTTON_RIGHT, SDL_BUTTON_RIGHT},
{love::mouse::Mouse::BUTTON_X1, SDL_BUTTON_X1},
{love::mouse::Mouse::BUTTON_X2, SDL_BUTTON_X2+0},
{love::mouse::Mouse::BUTTON_X3, SDL_BUTTON_X2+1},
{love::mouse::Mouse::BUTTON_X4, SDL_BUTTON_X2+2},
{love::mouse::Mouse::BUTTON_X5, SDL_BUTTON_X2+3},
};
EnumMap<love::mouse::Mouse::Button, Uint8, love::mouse::Mouse::BUTTON_MAX_ENUM> Event::buttons(Event::buttonEntries, sizeof(Event::buttonEntries));
+1 -1
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2014 LOVE Development Team
* Copyright (c) 2006-2015 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-2014 LOVE Development Team
* Copyright (c) 2006-2015 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
@@ -23,15 +23,12 @@
// LOVE
#include "common/runtime.h"
// sdlevent
#include "Event.h"
#include "sdl/Event.h"
namespace love
{
namespace event
{
namespace sdl
{
#define instance() (Module::getInstance<Event>(Module::M_EVENT))
@@ -124,7 +121,7 @@ extern "C" int luaopen_love_event(lua_State *L)
Event *instance = instance();
if (instance == nullptr)
{
luax_catchexcept(L, [&](){ instance = new Event(); });
luax_catchexcept(L, [&](){ instance = new love::event::sdl::Event(); });
}
else
instance->retain();
@@ -132,13 +129,12 @@ extern "C" int luaopen_love_event(lua_State *L)
WrappedModule w;
w.module = instance;
w.name = "event";
w.flags = MODULE_T;
w.type = MODULE_ID;
w.functions = functions;
w.types = 0;
return luax_register_module(L, w);
}
} // sdl
} // event
} // love
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2014 LOVE Development Team
* Copyright (c) 2006-2015 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
@@ -18,8 +18,8 @@
* 3. This notice may not be removed or altered from any source distribution.
**/
#ifndef LOVE_EVENT_SDL_WRAP_EVENT_H
#define LOVE_EVENT_SDL_WRAP_EVENT_H
#ifndef LOVE_EVENT_WRAP_EVENT_H
#define LOVE_EVENT_WRAP_EVENT_H
// LOVE
#include "common/config.h"
@@ -29,8 +29,6 @@ namespace love
{
namespace event
{
namespace sdl
{
int w_pump(lua_State *L);
int w_poll(lua_State *L);
@@ -41,8 +39,7 @@ int w_quit(lua_State *L);
extern "C" LOVE_EXPORT int luaopen_love_event(lua_State *L);
} // sdl
} // event
} // love
#endif // LOVE_EVENT_SDL_WRAP_EVENT_H
#endif // LOVE_EVENT_WRAP_EVENT_H
+252
View File
@@ -0,0 +1,252 @@
/**
* Copyright (c) 2006-2015 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 "DroppedFile.h"
#include "common/utf8.h"
// Assume POSIX or Visual Studio.
#include <sys/types.h>
#include <sys/stat.h>
#ifdef LOVE_WINDOWS
#include <wchar.h>
#else
#include <unistd.h> // POSIX.
#endif
namespace love
{
namespace filesystem
{
DroppedFile::DroppedFile(const std::string &filename)
: filename(filename)
, file(nullptr)
, mode(MODE_CLOSED)
, bufferMode(BUFFER_NONE)
, bufferSize(0)
{
}
DroppedFile::~DroppedFile()
{
if (mode != MODE_CLOSED)
close();
}
bool DroppedFile::open(Mode newmode)
{
if (newmode == MODE_CLOSED)
return true;
// File already open?
if (file != nullptr)
return false;
#ifdef LOVE_WINDOWS
// make sure non-ASCII filenames work.
std::wstring modestr = to_widestr(getModeString(newmode));
file = _wfopen(to_widestr(filename).c_str(), modestr.c_str());
#else
file = fopen(filename.c_str(), getModeString(newmode));
#endif
if (newmode == MODE_READ && file == nullptr)
throw love::Exception("Could not open file %s. Does not exist.", filename.c_str());
mode = newmode;
if (file != nullptr && !setBuffer(bufferMode, bufferSize))
{
// Revert to buffer defaults if we don't successfully set the buffer.
bufferMode = BUFFER_NONE;
bufferSize = 0;
}
return file != nullptr;
}
bool DroppedFile::close()
{
if (file == nullptr || fclose(file) != 0)
return false;
mode = MODE_CLOSED;
file = nullptr;
return true;
}
bool DroppedFile::isOpen() const
{
return mode != MODE_CLOSED && file != nullptr;
}
int64 DroppedFile::getSize()
{
#ifdef LOVE_WINDOWS
// make sure non-ASCII filenames work.
struct _stat buf;
if (_wstat(to_widestr(filename).c_str(), &buf) != 0)
return -1;
return (int64) buf.st_size;
#else
// Assume POSIX support...
struct stat buf;
if (stat(filename.c_str(), &buf) != 0)
return -1;
return (int64) buf.st_size;
#endif
}
int64 DroppedFile::read(void *dst, int64 size)
{
if (!file || mode != MODE_READ)
throw love::Exception("File is not opened for reading.");
if (size < 0)
throw love::Exception("Invalid read size.");
size_t read = fread(dst, 1, (size_t) size, file);
return (int64) read;
}
bool DroppedFile::write(const void *data, int64 size)
{
if (!file || (mode != MODE_WRITE && mode != MODE_APPEND))
throw love::Exception("File is not opened for writing.");
if (size < 0)
throw love::Exception("Invalid write size.");
int64 written = (int64) fwrite(data, 1, (size_t) size, file);
return written == size;
}
bool DroppedFile::flush()
{
if (!file || (mode != MODE_WRITE && mode != MODE_APPEND))
throw love::Exception("File is not opened for writing.");
return fflush(file) == 0;
}
bool DroppedFile::isEOF()
{
return file == nullptr || feof(file) != 0;
}
int64 DroppedFile::tell()
{
if (file == nullptr)
return -1;
return (int64) ftell(file);
}
bool DroppedFile::seek(uint64 pos)
{
return file != nullptr && fseek(file, (long) pos, SEEK_SET) == 0;
}
bool DroppedFile::setBuffer(BufferMode bufmode, int64 size)
{
if (size < 0)
return false;
if (bufmode == BUFFER_NONE)
size = 0;
// If the file isn't open, we'll make sure the buffer values are set in
// DroppedFile::open.
if (!isOpen())
{
bufferMode = bufmode;
bufferSize = size;
return true;
}
int vbufmode;
switch (bufmode)
{
case File::BUFFER_NONE:
default:
vbufmode = _IONBF;
break;
case File::BUFFER_LINE:
vbufmode = _IOLBF;
break;
case File::BUFFER_FULL:
vbufmode = _IOFBF;
break;
}
if (setvbuf(file, nullptr, vbufmode, (size_t) size) != 0)
return false;
bufferMode = bufmode;
bufferSize = size;
return true;
}
File::BufferMode DroppedFile::getBuffer(int64 &size) const
{
size = bufferSize;
return bufferMode;
}
const std::string &DroppedFile::getFilename() const
{
return filename;
}
File::Mode DroppedFile::getMode() const
{
return mode;
}
const char *DroppedFile::getModeString(Mode mode)
{
switch (mode)
{
case File::MODE_CLOSED:
default:
return "c";
case File::MODE_READ:
return "rb";
case File::MODE_WRITE:
return "wb";
case File::MODE_APPEND:
return "ab";
}
}
} // filesystem
} // love
+83
View File
@@ -0,0 +1,83 @@
/**
* Copyright (c) 2006-2015 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#ifndef LOVE_FILESYSTEM_DROPPED_FILE_H
#define LOVE_FILESYSTEM_DROPPED_FILE_H
// LOVE
#include "common/config.h"
#include "File.h"
// C
#include <cstdio>
namespace love
{
namespace filesystem
{
/**
* File which is created when a user drags and drops an actual file onto the
* LOVE game. Uses C's stdio. Filenames are system-dependent full paths.
**/
class DroppedFile : public File
{
public:
DroppedFile(const std::string &filename);
virtual ~DroppedFile();
// Implements File.
using File::read;
using File::write;
bool open(Mode mode) override;
bool close() override;
bool isOpen() const override;
int64 getSize() override;
int64 read(void *dst, int64 size) override;
bool write(const void *data, int64 size) override;
bool flush() override;
bool isEOF() override;
int64 tell() override;
bool seek(uint64 pos) override;
bool setBuffer(BufferMode bufmode, int64 size) override;
BufferMode getBuffer(int64 &size) const override;
Mode getMode() const override;
const std::string &getFilename() const override;
private:
static const char *getModeString(Mode mode);
std::string filename;
FILE *file;
Mode mode;
BufferMode bufferMode;
int64 bufferSize;
}; // DroppedFile
} // filesystem
} // love
#endif // LOVE_FILESYSTEM_DROPPED_FILE_H
+67 -5
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2014 LOVE Development Team
* Copyright (c) 2006-2015 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
@@ -29,6 +29,68 @@ File::~File()
{
}
FileData *File::read(int64 size)
{
bool isopen = isOpen();
if (!isopen && !open(MODE_READ))
throw love::Exception("Could not read file %s.", getFilename().c_str());
int64 max = getSize();
int64 cur = tell();
size = (size == ALL) ? max : size;
if (size < 0)
throw love::Exception("Invalid read size.");
// Clamping because the file offset may be in a weird position.
if (cur < 0)
cur = 0;
else if (cur > max)
cur = max;
if (cur + size > max)
size = max - cur;
FileData *fileData = new FileData(size, getFilename());
int64 bytesRead = read(fileData->getData(), size);
if (bytesRead < 0 || (bytesRead == 0 && bytesRead != size))
{
delete fileData;
throw love::Exception("Could not read from file.");
}
if (bytesRead < size)
{
FileData *tmpFileData = new FileData(bytesRead, getFilename());
memcpy(tmpFileData->getData(), fileData->getData(), (size_t) bytesRead);
fileData->release();
fileData = tmpFileData;
}
if (!isopen)
close();
return fileData;
}
bool File::write(const Data *data, int64 size)
{
return write(data->getData(), (size == ALL) ? data->getSize() : size);
}
std::string File::getExtension() const
{
const std::string &filename = getFilename();
std::string::size_type idx = filename.rfind('.');
if (idx != std::string::npos)
return filename.substr(idx+1);
else
return std::string();
}
bool File::getConstant(const char *in, Mode &out)
{
return modes.find(in, out);
@@ -51,10 +113,10 @@ bool File::getConstant(BufferMode in, const char *&out)
StringMap<File::Mode, File::MODE_MAX_ENUM>::Entry File::modeEntries[] =
{
{"c", File::CLOSED},
{"r", File::READ},
{"w", File::WRITE},
{"a", File::APPEND},
{"c", File::MODE_CLOSED},
{"r", File::MODE_READ},
{"w", File::MODE_WRITE},
{"a", File::MODE_APPEND},
};
StringMap<File::Mode, File::MODE_MAX_ENUM> File::modes(File::modeEntries, sizeof(File::modeEntries));
+11 -11
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2014 LOVE Development Team
* Copyright (c) 2006-2015 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
@@ -49,10 +49,10 @@ public:
**/
enum Mode
{
CLOSED,
READ,
WRITE,
APPEND,
MODE_CLOSED,
MODE_READ,
MODE_WRITE,
MODE_APPEND,
MODE_MAX_ENUM
};
@@ -77,7 +77,7 @@ public:
/**
* Opens the file in a certain mode.
*
* @param mode READ, WRITE, APPEND.
* @param mode MODE_READ, MODE_WRITE, MODE_APPEND.
* @return True if successful, false otherwise.
**/
virtual bool open(Mode mode) = 0;
@@ -107,7 +107,7 @@ public:
* @param size The number of bytes to attempt reading, or -1 for EOF.
* @return A newly allocated Data object.
**/
virtual FileData *read(int64 size = ALL) = 0;
virtual FileData *read(int64 size = ALL);
/**
* Reads data into the destination buffer.
@@ -134,7 +134,7 @@ public:
* @param size The number of bytes to attempt writing, or -1 for everything.
* @return True of success, false otherwise.
**/
virtual bool write(const Data *data, int64 size = ALL) = 0;
virtual bool write(const Data *data, int64 size = ALL);
/**
* Flushes the currently buffered file data to disk. Only applicable in
@@ -147,7 +147,7 @@ public:
*
* @return True if EOF, false otherwise.
**/
virtual bool eof() = 0;
virtual bool isEOF() = 0;
/**
* Gets the current position in the File.
@@ -192,13 +192,13 @@ public:
* Gets the filename for this File, or empty string if none.
* @return The filename for this File.
**/
virtual std::string getFilename() const = 0;
virtual const std::string &getFilename() const = 0;
/**
* Gets the file extension for this File, or empty string if none.
* @return The file extension for this File (without the dot).
**/
virtual std::string getExtension() const = 0;
virtual std::string getExtension() const;
static bool getConstant(const char *in, Mode &out);
static bool getConstant(Mode in, const char *&out);
+11 -2
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2014 LOVE Development Team
* Copyright (c) 2006-2015 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
@@ -30,10 +30,19 @@ namespace filesystem
{
FileData::FileData(uint64 size, const std::string &filename)
: data(new char[(size_t) size])
: data(nullptr)
, size((size_t) size)
, filename(filename)
{
try
{
data = new char[(size_t) size];
}
catch (std::bad_alloc &)
{
throw love::Exception("Out of memory.");
}
if (filename.rfind('.') != std::string::npos)
extension = filename.substr(filename.rfind('.')+1);
}
+1 -1
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2014 LOVE Development Team
* Copyright (c) 2006-2015 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
+62
View File
@@ -0,0 +1,62 @@
/**
* Copyright (c) 2006-2015 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 "Filesystem.h"
#include "common/utf8.h"
// Assume POSIX or Visual Studio.
#include <sys/types.h>
#include <sys/stat.h>
namespace love
{
namespace filesystem
{
Filesystem::Filesystem()
{
}
Filesystem::~Filesystem()
{
}
bool Filesystem::isRealDirectory(const std::string &path) const
{
#ifdef LOVE_WINDOWS
// make sure non-ASCII paths work.
struct _stat buf;
if (_wstat(to_widestr(path).c_str(), &buf) != 0)
return false;
return (buf.st_mode & _S_IFDIR) == _S_IFDIR;
#else
// Assume POSIX support...
struct stat buf;
if (stat(path.c_str(), &buf) != 0)
return false;
return S_ISDIR(buf.st_mode) != 0;
#endif
}
} // filesystem
} // love
+261
View File
@@ -0,0 +1,261 @@
/**
* Copyright (c) 2006-2015 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#ifndef LOVE_FILESYSTEM_FILESYSTEM_H
#define LOVE_FILESYSTEM_FILESYSTEM_H
// LOVE
#include "common/config.h"
#include "common/runtime.h"
#include "common/Module.h"
#include "common/int.h"
#include "FileData.h"
#include "File.h"
// C++
#include <string>
#include <vector>
// In Windows, we would like to use "LOVE" as the
// application folder, but in Linux, we like .love.
#define LOVE_APPDATA_PREFIX ""
#ifdef LOVE_WINDOWS
# define LOVE_APPDATA_FOLDER "LOVE"
# define LOVE_PATH_SEPARATOR "/"
# define LOVE_MAX_PATH _MAX_PATH
#else
# if defined(LOVE_MACOSX) || defined(LOVE_IOS)
# define LOVE_APPDATA_FOLDER "LOVE"
# elif defined(LOVE_LINUX)
# define LOVE_APPDATA_FOLDER "love"
# else
# define LOVE_APPDATA_PREFIX "."
# define LOVE_APPDATA_FOLDER "love"
# endif
# define LOVE_PATH_SEPARATOR "/"
# define LOVE_MAX_PATH MAXPATHLEN
#endif
namespace love
{
namespace filesystem
{
class Filesystem : public Module
{
public:
Filesystem();
virtual ~Filesystem();
// Implements Module.
virtual ModuleType getModuleType() const { return M_FILESYSTEM; }
virtual void init(const char *arg0) = 0;
virtual void setFused(bool fused) = 0;
virtual bool isFused() const = 0;
/**
* This sets up the save directory. If the
* it is already set up, nothing happens.
* @return True on success, false otherwise.
**/
virtual bool setupWriteDirectory() = 0;
/**
* Sets the name of the save folder.
* @param ident The name of the game. Will be used to
* to create the folder in the LOVE data folder.
**/
virtual bool setIdentity(const char *ident, bool appendToPath = false) = 0;
virtual const char *getIdentity() const = 0;
/**
* Sets the path to the game source.
* This can only be set once.
* @param source Path to a directory or a .love-file.
**/
virtual bool setSource(const char *source) = 0;
/**
* Gets the path to the game source.
* Returns a 0-length string if the source has not been set.
**/
virtual const char *getSource() const = 0;
virtual bool mount(const char *archive, const char *mountpoint, bool appendToPath = false) = 0;
virtual bool unmount(const char *archive) = 0;
/**
* Creates a new file.
**/
virtual File *newFile(const char *filename) const = 0;
/**
* Creates a new FileData object. Data will be copied.
* @param data Pointer to the data.
* @param size The size of the data.
* @param filename The full filename used to file type identification.
**/
virtual FileData *newFileData(void *data, unsigned int size, const char *filename) const = 0;
/**
* Creates a new FileData object from base64 data.
* @param b64 The base64 data.
**/
virtual FileData *newFileData(const char *b64, const char *filename) const = 0;
/**
* Gets the current working directory.
**/
virtual const char *getWorkingDirectory() = 0;
/**
* Gets the user home directory.
**/
virtual std::string getUserDirectory() = 0;
/**
* Gets the APPDATA directory. On Windows, this is the folder
* in the %APPDATA% enviroment variable. On Linux, this is the
* user home folder.
**/
virtual std::string getAppdataDirectory() = 0;
/**
* Gets the full path of the save folder.
**/
virtual const char *getSaveDirectory() = 0;
/**
* Gets the full path to the directory containing the game source.
* For example if the game source is C:\Games\mygame.love, this will return
* C:\Games.
**/
virtual std::string getSourceBaseDirectory() const = 0;
/**
* Gets the real directory path containing the file.
**/
virtual std::string getRealDirectory(const char *filename) const = 0;
/**
* Checks if a path is a directory.
* @param dir The directory name to check.
**/
virtual bool isDirectory(const char *dir) const = 0;
/**
* Checks if a filename exists.
* @param file The filename to check.
**/
virtual bool isFile(const char *file) const = 0;
/**
* Creates a directory. Write dir must be set.
* @param dir The directory to create.
**/
virtual bool createDirectory(const char *dir) = 0;
/**
* Removes a file (or directory).
* @param file The file or directory to remove.
**/
virtual bool remove(const char *file) = 0;
/**
* Reads data from a file.
* @param filename The name of the file to read from.
* @param size The size in bytes of the data to read.
**/
virtual FileData *read(const char *filename, int64 size = File::ALL) const = 0;
/**
* Write data to a file.
* @param filename The name of the file to write to.
* @param data The data to write.
* @param size The size in bytes of the data to write.
**/
virtual void write(const char *filename, const void *data, int64 size) const = 0;
/**
* Append data to a file, creating it if it doesn't exist.
* @param filename The name of the file to write to.
* @param data The data to append.
* @param size The size in bytes of the data to append.
**/
virtual void append(const char *filename, const void *data, int64 size) const = 0;
/**
* This "native" method returns a table of all
* files in a given directory.
**/
virtual int getDirectoryItems(lua_State *L) = 0;
/**
* Gets the last modification time of a file, in seconds
* since the Unix epoch.
* @param filename The name of the file.
**/
virtual int64 getLastModified(const char *filename) const = 0;
/**
* Gets the size of a file in bytes.
* @param filename The name of the file.
**/
virtual int64 getSize(const char *filename) const = 0;
/**
* Enable or disable symbolic link support in love.filesystem.
**/
virtual void setSymlinksEnabled(bool enable) = 0;
/**
* Gets whether symbolic link support is enabled.
**/
virtual bool areSymlinksEnabled() const = 0;
/**
* Gets whether a filepath is actually a symlink.
* Always returns false if symlinks are not enabled.
**/
virtual bool isSymlink(const char *filename) const = 0;
// Require path accessors
// Not const because it's R/W
virtual std::vector<std::string> &getRequirePath() = 0;
/**
* Allows a full (OS-dependent) path to be used with Filesystem::mount.
**/
virtual void allowMountingForPath(const std::string &path) = 0;
/**
* Gets whether the given full (OS-dependent) path is a directory.
**/
virtual bool isRealDirectory(const std::string &path) const;
}; // Filesystem
} // filesystem
} // love
#endif // LOVE_FILESYSTEM_FILESYSTEM_H
+33 -100
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2014 LOVE Development Team
* Copyright (c) 2006-2015 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
@@ -39,8 +39,8 @@ namespace physfs
File::File(const std::string &filename)
: filename(filename)
, file(0)
, mode(CLOSED)
, file(nullptr)
, mode(MODE_CLOSED)
, bufferMode(BUFFER_NONE)
, bufferSize(0)
{
@@ -48,25 +48,25 @@ File::File(const std::string &filename)
File::~File()
{
if (mode != CLOSED)
if (mode != MODE_CLOSED)
close();
}
bool File::open(Mode mode)
{
if (mode == CLOSED)
if (mode == MODE_CLOSED)
return true;
// File must exist if read mode.
if ((mode == READ) && !PHYSFS_exists(filename.c_str()))
if ((mode == MODE_READ) && !PHYSFS_exists(filename.c_str()))
throw love::Exception("Could not open file %s. Does not exist.", filename.c_str());
// Check whether the write directory is set.
if ((mode == APPEND || mode == WRITE) && (PHYSFS_getWriteDir() == 0) && !hack_setupWriteDirectory())
if ((mode == MODE_APPEND || mode == MODE_WRITE) && (PHYSFS_getWriteDir() == 0) && !hack_setupWriteDirectory())
throw love::Exception("Could not set write directory.");
// File already open?
if (file != 0)
if (file != nullptr)
return false;
PHYSFS_getLastError(); // Clear the error buffer.
@@ -74,13 +74,13 @@ bool File::open(Mode mode)
switch (mode)
{
case READ:
case MODE_READ:
handle = PHYSFS_openRead(filename.c_str());
break;
case APPEND:
case MODE_APPEND:
handle = PHYSFS_openAppend(filename.c_str());
break;
case WRITE:
case MODE_WRITE:
handle = PHYSFS_openWrite(filename.c_str());
break;
default:
@@ -99,94 +99,50 @@ bool File::open(Mode mode)
this->mode = mode;
if (file != 0 && !setBuffer(bufferMode, bufferSize))
if (file != nullptr && !setBuffer(bufferMode, bufferSize))
{
// Revert to buffer defaults if we don't successfully set the buffer.
bufferMode = BUFFER_NONE;
bufferSize = 0;
}
return (file != 0);
return (file != nullptr);
}
bool File::close()
{
if (!PHYSFS_close(file))
if (file == nullptr || !PHYSFS_close(file))
return false;
mode = CLOSED;
file = 0;
mode = MODE_CLOSED;
file = nullptr;
return true;
}
bool File::isOpen() const
{
return mode != CLOSED && file != 0;
return mode != MODE_CLOSED && file != nullptr;
}
int64 File::getSize()
{
// If the file is closed, open it to
// check the size.
if (file == 0)
if (file == nullptr)
{
open(READ);
int64 size = (int64)PHYSFS_fileLength(file);
open(MODE_READ);
int64 size = (int64) PHYSFS_fileLength(file);
close();
return size;
}
return (int64)PHYSFS_fileLength(file);
}
FileData *File::read(int64 size)
{
bool isOpen = (file != 0);
if (!isOpen && !open(READ))
throw love::Exception("Could not read file %s.", filename.c_str());
int64 max = getSize();
int64 cur = tell();
size = (size == ALL) ? max : size;
if (size < 0)
throw love::Exception("Invalid read size.");
// Clamping because the file offset may be in a weird position.
if (cur < 0)
cur = 0;
else if (cur > max)
cur = max;
if (cur + size > max)
size = max - cur;
FileData *fileData = new FileData(size, getFilename());
int64 bytesRead = read(fileData->getData(), size);
if (bytesRead < 0 || (bytesRead == 0 && bytesRead != size))
{
delete fileData;
throw love::Exception("Could not read from file.");
}
if (bytesRead < size)
{
FileData *tmpFileData = new FileData(bytesRead, getFilename());
memcpy(tmpFileData->getData(), fileData->getData(), (size_t) bytesRead);
delete fileData;
fileData = tmpFileData;
}
if (!isOpen)
close();
return fileData;
return (int64) PHYSFS_fileLength(file);
}
int64 File::read(void *dst, int64 size)
{
if (!file || mode != READ)
if (!file || mode != MODE_READ)
throw love::Exception("File is not opened for reading.");
int64 max = (int64)PHYSFS_fileLength(file);
@@ -205,7 +161,7 @@ int64 File::read(void *dst, int64 size)
bool File::write(const void *data, int64 size)
{
if (!file || (mode != WRITE && mode != APPEND))
if (!file || (mode != MODE_WRITE && mode != MODE_APPEND))
throw love::Exception("File is not opened for writing.");
// Another clamp, for the time being.
@@ -215,7 +171,7 @@ bool File::write(const void *data, int64 size)
throw love::Exception("Invalid write size.");
// Try to write.
int64 written = static_cast<int64>(PHYSFS_write(file, data, 1, (PHYSFS_uint32) size));
int64 written = (int64) PHYSFS_write(file, data, 1, (PHYSFS_uint32) size);
// Check that correct amount of data was written.
if (written != size)
@@ -231,14 +187,9 @@ bool File::write(const void *data, int64 size)
return true;
}
bool File::write(const Data *data, int64 size)
{
return write(data->getData(), (size == ALL) ? data->getSize() : size);
}
bool File::flush()
{
if (!file || (mode != WRITE && mode != APPEND))
if (!file || (mode != MODE_WRITE && mode != MODE_APPEND))
throw love::Exception("File is not opened for writing.");
return PHYSFS_flush(file) != 0;
@@ -261,16 +212,14 @@ inline bool test_eof(File *, PHYSFS_File *file)
}
#endif
bool File::eof()
bool File::isEOF()
{
if (file == 0 || test_eof(this, file))
return true;
return false;
return file == nullptr || test_eof(this, file);
}
int64 File::tell()
{
if (file == 0)
if (file == nullptr)
return -1;
return (int64) PHYSFS_tell(file);
@@ -278,23 +227,17 @@ int64 File::tell()
bool File::seek(uint64 pos)
{
if (file == 0)
return false;
if (!PHYSFS_seek(file, (PHYSFS_uint64) pos))
return false;
return true;
return file != nullptr && PHYSFS_seek(file, (PHYSFS_uint64) pos) != 0;
}
bool File::setBuffer(BufferMode bufmode, int64 size)
{
// No negativity allowed!
if (size < 0)
return false;
// If the file isn't open, we'll make sure the buffer values are set in
// File::open.
if (file == 0 || mode == CLOSED)
if (!isOpen())
{
bufferMode = bufmode;
bufferSize = size;
@@ -331,21 +274,11 @@ File::BufferMode File::getBuffer(int64 &size) const
return bufferMode;
}
std::string File::getFilename() const
const std::string &File::getFilename() const
{
return filename;
}
std::string File::getExtension() const
{
std::string::size_type idx = filename.rfind('.');
if (idx != std::string::npos)
return filename.substr(idx+1);
else
return std::string();
}
filesystem::File::Mode File::getMode() const
{
return mode;
+18 -19
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2014 LOVE Development Team
* Copyright (c) 2006-2015 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
@@ -25,7 +25,7 @@
#include "filesystem/File.h"
// PhysFS
#ifdef LOVE_MACOSX_USE_FRAMEWORKS
#ifdef LOVE_APPLE_USE_FRAMEWORKS
#include <physfs/physfs.h>
#else
#include <physfs.h>
@@ -54,23 +54,22 @@ public:
virtual ~File();
// Implements love::filesystem::File.
bool open(Mode mode);
bool close();
bool isOpen() const;
int64 getSize();
FileData *read(int64 size = ALL);
int64 read(void *dst, int64 size);
bool write(const void *data, int64 size);
bool write(const Data *data, int64 size = ALL);
bool flush();
bool eof();
int64 tell();
bool seek(uint64 pos);
bool setBuffer(BufferMode bufmode, int64 size);
BufferMode getBuffer(int64 &size) const;
Mode getMode() const;
std::string getFilename() const;
std::string getExtension() const;
using love::filesystem::File::read;
using love::filesystem::File::write;
bool open(Mode mode) override;
bool close() override;
bool isOpen() const override;
int64 getSize() override;
virtual int64 read(void *dst, int64 size) override;
bool write(const void *data, int64 size) override;
bool flush() override;
bool isEOF() override;
int64 tell() override;
bool seek(uint64 pos) override;
bool setBuffer(BufferMode bufmode, int64 size) override;
BufferMode getBuffer(int64 &size) const override;
Mode getMode() const override;
const std::string &getFilename() const override;
private:
+78 -134
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2014 LOVE Development Team
* Copyright (c) 2006-2015 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
@@ -18,15 +18,37 @@
* 3. This notice may not be removed or altered from any source distribution.
**/
#include "common/config.h"
#include <iostream>
#include <sstream>
#include <algorithm>
#include "common/utf8.h"
#include "common/b64.h"
#include "Filesystem.h"
#include "File.h"
// PhysFS
#ifdef LOVE_APPLE_USE_FRAMEWORKS
#include <physfs/physfs.h>
#else
#include <physfs.h>
#endif
// For great CWD. (Current Working Directory)
// Using this instead of boost::filesystem which totally
// cramped our style.
#ifdef LOVE_WINDOWS
# include <windows.h>
# include <direct.h>
#else
# include <sys/param.h>
# include <unistd.h>
#endif
#ifdef LOVE_IOS
# include "common/iOS.h"
#endif
namespace
{
@@ -73,15 +95,15 @@ namespace physfs
{
Filesystem::Filesystem()
: initialized(false)
, fused(false)
: fused(false)
, fusedSet(false)
{
requirePath = {"?.lua", "?/init.lua"};
}
Filesystem::~Filesystem()
{
if (initialized)
if (PHYSFS_isInit())
PHYSFS_deinit();
}
@@ -93,8 +115,14 @@ const char *Filesystem::getName() const
void Filesystem::init(const char *arg0)
{
if (!PHYSFS_init(arg0))
throw Exception(PHYSFS_getLastError());
initialized = true;
throw love::Exception("%s", PHYSFS_getLastError());
PHYSFS_Version version = {};
PHYSFS_getLinkedVersion(&version);
// FIXME: This is a workaround for a bug in PHYSFS_enumerateFiles in 2.1-alpha.
if (version.major == 2 && version.minor == 1)
PHYSFS_permitSymbolicLinks(1);
}
void Filesystem::setFused(bool fused)
@@ -114,7 +142,7 @@ bool Filesystem::isFused() const
bool Filesystem::setIdentity(const char *ident, bool appendToPath)
{
if (!initialized)
if (!PHYSFS_isInit())
return false;
std::string old_save_path = save_path_full;
@@ -146,7 +174,7 @@ bool Filesystem::setIdentity(const char *ident, bool appendToPath)
// Try to add the save directory to the search path.
// (No error on fail, it means that the path doesn't exist).
PHYSFS_addToSearchPath(save_path_full.c_str(), appendToPath);
PHYSFS_mount(save_path_full.c_str(), nullptr, appendToPath);
// HACK: This forces setupWriteDirectory to be called the next time a file
// is opened for writing - otherwise it won't be called at all if it was
@@ -163,7 +191,7 @@ const char *Filesystem::getIdentity() const
bool Filesystem::setSource(const char *source)
{
if (!initialized)
if (!PHYSFS_isInit())
return false;
// Check whether directory is already set.
@@ -171,7 +199,7 @@ bool Filesystem::setSource(const char *source)
return false;
// Add the directory.
if (!PHYSFS_addToSearchPath(source, 1))
if (!PHYSFS_mount(source, nullptr, 1))
return false;
// Save the game source.
@@ -187,7 +215,7 @@ const char *Filesystem::getSource() const
bool Filesystem::setupWriteDirectory()
{
if (!initialized)
if (!PHYSFS_isInit())
return false;
// These must all be set.
@@ -232,7 +260,7 @@ bool Filesystem::setupWriteDirectory()
return false;
// Add the directory. (Will not be readded if already present).
if (!PHYSFS_addToSearchPath(save_path_full.c_str(), 0))
if (!PHYSFS_mount(save_path_full.c_str(), nullptr, 0))
{
PHYSFS_setWriteDir(nullptr); // Clear the write directory in case of error.
return false;
@@ -243,13 +271,18 @@ bool Filesystem::setupWriteDirectory()
bool Filesystem::mount(const char *archive, const char *mountpoint, bool appendToPath)
{
if (!initialized || !archive)
if (!PHYSFS_isInit() || !archive)
return false;
std::string realPath;
std::string sourceBase = getSourceBaseDirectory();
if (isFused() && sourceBase.compare(archive) == 0)
// Check whether the given archive path is in the list of allowed full paths.
auto it = std::find(allowedMountPaths.begin(), allowedMountPaths.end(), archive);
if (it != allowedMountPaths.end())
realPath = *it;
else if (isFused() && sourceBase.compare(archive) == 0)
{
// Special case: if the game is fused and the archive is the source's
// base directory, mount it even though it's outside of the save dir.
@@ -284,13 +317,18 @@ bool Filesystem::mount(const char *archive, const char *mountpoint, bool appendT
bool Filesystem::unmount(const char *archive)
{
if (!initialized || !archive)
if (!PHYSFS_isInit() || !archive)
return false;
std::string realPath;
std::string sourceBase = getSourceBaseDirectory();
if (isFused() && sourceBase.compare(archive) == 0)
// Check whether the given archive path is in the list of allowed full paths.
auto it = std::find(allowedMountPaths.begin(), allowedMountPaths.end(), archive);
if (it != allowedMountPaths.end())
realPath = *it;
else if (isFused() && sourceBase.compare(archive) == 0)
{
// Special case: if the game is fused and the archive is the source's
// base directory, unmount it even though it's outside of the save dir.
@@ -318,7 +356,7 @@ bool Filesystem::unmount(const char *archive)
return PHYSFS_removeFromSearchPath(realPath.c_str());
}
File *Filesystem::newFile(const char *filename) const
love::filesystem::File *Filesystem::newFile(const char *filename) const
{
return new File(filename);
}
@@ -335,7 +373,7 @@ FileData *Filesystem::newFileData(void *data, unsigned int size, const char *fil
FileData *Filesystem::newFileData(const char *b64, const char *filename) const
{
int size = strlen(b64);
int size = (int) strlen(b64);
int outsize = 0;
char *dst = b64_decode(b64, size, outsize);
FileData *fd = new FileData(outsize, std::string(filename));
@@ -388,6 +426,8 @@ std::string Filesystem::getAppdataDirectory()
std::string udir = getUserDirectory();
udir.append("/Library/Application Support");
appdata = normalize(udir);
#elif defined(LOVE_IOS)
appdata = normalize(love::ios::getAppdataDirectory());
#elif defined(LOVE_LINUX)
char *xdgdatahome = getenv("XDG_DATA_HOME");
if (!xdgdatahome)
@@ -443,19 +483,14 @@ std::string Filesystem::getRealDirectory(const char *filename) const
return std::string(dir);
}
bool Filesystem::exists(const char *file) const
bool Filesystem::isDirectory(const char *dir) const
{
return PHYSFS_exists(file);
}
bool Filesystem::isDirectory(const char *file) const
{
return PHYSFS_isDirectory(file);
return PHYSFS_isDirectory(dir);
}
bool Filesystem::isFile(const char *file) const
{
return exists(file) && !isDirectory(file);
return PHYSFS_exists(file) && !PHYSFS_isDirectory(file);
}
bool Filesystem::createDirectory(const char *dir)
@@ -478,11 +513,11 @@ bool Filesystem::remove(const char *file)
return true;
}
Data *Filesystem::read(const char *filename, int64 size) const
FileData *Filesystem::read(const char *filename, int64 size) const
{
File file(filename);
file.open(File::READ);
file.open(File::MODE_READ);
// close() is called in the File destructor.
return file.read(size);
@@ -492,7 +527,7 @@ void Filesystem::write(const char *filename, const void *data, int64 size) const
{
File file(filename);
file.open(File::WRITE);
file.open(File::MODE_WRITE);
// close() is called in the File destructor.
if (!file.write(data, size))
@@ -503,7 +538,7 @@ void Filesystem::append(const char *filename, const void *data, int64 size) cons
{
File file(filename);
file.open(File::APPEND);
file.open(File::MODE_APPEND);
// close() is called in the File destructor.
if (!file.write(data, size))
@@ -542,108 +577,6 @@ int Filesystem::getDirectoryItems(lua_State *L)
return 1;
}
int Filesystem::lines_i(lua_State *L)
{
const int bufsize = 1024;
char buf[bufsize];
int linesize = 0;
bool newline = false;
File *file = luax_checktype<File>(L, lua_upvalueindex(1), "File", FILESYSTEM_FILE_T);
// Only accept read mode at this point.
if (file->getMode() != File::READ)
return luaL_error(L, "File needs to stay in read mode.");
int64 pos = file->tell();
int64 userpos = -1;
if (lua_isnoneornil(L, lua_upvalueindex(2)) == 0)
{
// User may have changed the file position.
userpos = pos;
pos = (int64) lua_tonumber(L, lua_upvalueindex(2));
if (userpos != pos)
file->seek(pos);
}
while (!newline && !file->eof())
{
// This 64-bit to 32-bit integer cast should be safe as it never exceeds bufsize.
int read = (int) file->read(buf, bufsize);
if (read < 0)
return luaL_error(L, "Could not read from file.");
linesize += read;
for (int i = 0; i < read; i++)
{
if (buf[i] == '\n')
{
linesize -= read - i;
newline = true;
break;
}
}
}
if (newline || (file->eof() && linesize > 0))
{
if (linesize < bufsize)
{
// We have the line in the buffer on the stack. No 'new' and 'read' needed.
lua_pushlstring(L, buf, linesize > 0 && buf[linesize - 1] == '\r' ? linesize - 1 : linesize);
if (userpos < 0)
file->seek(pos + linesize + 1);
}
else
{
char *str = 0;
try
{
str = new char[linesize + 1];
}
catch(std::bad_alloc &)
{
// Can't lua_error (longjmp) in exception handlers.
}
if (!str)
return luaL_error(L, "Out of memory.");
file->seek(pos);
// Read the \n anyway and save us a call to seek.
if (file->read(str, linesize + 1) == -1)
{
delete [] str;
return luaL_error(L, "Could not read from file.");
}
lua_pushlstring(L, str, str[linesize - 1] == '\r' ? linesize - 1 : linesize);
delete [] str;
}
if (userpos >= 0)
{
// Save new position in upvalue.
lua_pushnumber(L, (lua_Number)(pos + linesize + 1));
lua_replace(L, lua_upvalueindex(2));
file->seek(userpos);
}
return 1;
}
// EOF reached.
if (userpos >= 0 && luax_toboolean(L, lua_upvalueindex(3)))
file->seek(userpos);
else
file->close();
return 0;
}
int64 Filesystem::getLastModified(const char *filename) const
{
PHYSFS_sint64 time = PHYSFS_getLastModTime(filename);
@@ -676,6 +609,17 @@ bool Filesystem::isSymlink(const char *filename) const
return PHYSFS_isSymbolicLink(filename) != 0;
}
std::vector<std::string> &Filesystem::getRequirePath()
{
return requirePath;
}
void Filesystem::allowMountingForPath(const std::string &path)
{
if (std::find(allowedMountPaths.begin(), allowedMountPaths.end(), path) == allowedMountPaths.end())
allowedMountPaths.push_back(path);
}
} // physfs
} // filesystem
} // love
+13 -181
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2014 LOVE Development Team
* Copyright (c) 2006-2015 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
@@ -24,46 +24,9 @@
// STD
#include <cstdlib>
#include <cstring>
#include <string>
// LOVE
#include "common/Module.h"
#include "common/config.h"
#include "common/int.h"
#include "common/runtime.h"
#include "filesystem/FileData.h"
#include "File.h"
// For great CWD. (Current Working Directory)
// Using this instead of boost::filesystem which totally
// cramped our style.
#ifdef LOVE_WINDOWS
# include <windows.h>
# include <direct.h>
#else
# include <sys/param.h>
# include <unistd.h>
#endif
// In Windows, we would like to use "LOVE" as the
// application folder, but in Linux, we like .love.
#define LOVE_APPDATA_PREFIX ""
#ifdef LOVE_WINDOWS
# define LOVE_APPDATA_FOLDER "LOVE"
# define LOVE_PATH_SEPARATOR "/"
# define LOVE_MAX_PATH _MAX_PATH
#else
# ifdef LOVE_MACOSX
# define LOVE_APPDATA_FOLDER "LOVE"
# elif defined(LOVE_LINUX)
# define LOVE_APPDATA_FOLDER "love"
# else
# define LOVE_APPDATA_PREFIX "."
# define LOVE_APPDATA_FOLDER "love"
# endif
# define LOVE_PATH_SEPARATOR "/"
# define LOVE_MAX_PATH MAXPATHLEN
#endif
#include "filesystem/Filesystem.h"
namespace love
{
@@ -72,7 +35,7 @@ namespace filesystem
namespace physfs
{
class Filesystem : public Module
class Filesystem : public love::filesystem::Filesystem
{
public:
@@ -80,7 +43,6 @@ public:
virtual ~Filesystem();
// Implements Module.
virtual ModuleType getModuleType() const { return M_FILESYSTEM; }
const char *getName() const;
void init(const char *arg0);
@@ -88,186 +50,54 @@ public:
void setFused(bool fused);
bool isFused() const;
/**
* This sets up the save directory. If the
* it is already set up, nothing happens.
* @return True on success, false otherwise.
**/
bool setupWriteDirectory();
/**
* Sets the name of the save folder.
* @param ident The name of the game. Will be used to
* to create the folder in the LOVE data folder.
**/
bool setIdentity(const char *ident, bool appendToPath = false);
const char *getIdentity() const;
/**
* Sets the path to the game source.
* This can only be set once.
* @param source Path to a directory or a .love-file.
**/
bool setSource(const char *source);
/**
* Gets the path to the game source.
* Returns a 0-length string if the source has not been set.
**/
const char *getSource() const;
bool mount(const char *archive, const char *mountpoint, bool appendToPath = false);
bool unmount(const char *archive);
/**
* Creates a new file.
**/
File *newFile(const char *filename) const;
/**
* Creates a new FileData object. Data will be copied.
* @param data Pointer to the data.
* @param size The size of the data.
* @param filename The full filename used to file type identification.
**/
FileData *newFileData(void *data, unsigned int size, const char *filename) const;
/**
* Creates a new FileData object from base64 data.
* @param b64 The base64 data.
**/
FileData *newFileData(const char *b64, const char *filename) const;
/**
* Gets the current working directory.
**/
const char *getWorkingDirectory();
/**
* Gets the user home directory.
**/
std::string getUserDirectory();
/**
* Gets the APPDATA directory. On Windows, this is the folder
* in the %APPDATA% enviroment variable. On Linux, this is the
* user home folder.
**/
std::string getAppdataDirectory();
/**
* Gets the full path of the save folder.
**/
const char *getSaveDirectory();
/**
* Gets the full path to the directory containing the game source.
* For example if the game source is C:\Games\mygame.love, this will return
* C:\Games.
**/
std::string getSourceBaseDirectory() const;
/**
* Gets the real directory path containing the file.
**/
std::string getRealDirectory(const char *filename) const;
/**
* Checks whether a file exists in the current search path
* or not.
* @param file The filename to check.
**/
bool exists(const char *file) const;
/**
* Checks if an existing file really is a directory.
* @param file The filename to check.
**/
bool isDirectory(const char *file) const;
/**
* Checks if an existing file really is a file,
* and not a directory.
* @param file The filename to check.
**/
bool isDirectory(const char *dir) const;
bool isFile(const char *file) const;
/**
* Creates a directory. Write dir must be set.
* @param dir The directory to create.
**/
bool createDirectory(const char *dir);
/**
* Removes a file (or directory).
* @param file The file or directory to remove.
**/
bool remove(const char *file);
/**
* Reads data from a file.
* @param filename The name of the file to read from.
* @param size The size in bytes of the data to read.
**/
Data *read(const char *filename, int64 size = File::ALL) const;
/**
* Write data to a file.
* @param filename The name of the file to write to.
* @param data The data to write.
* @param size The size in bytes of the data to write.
**/
FileData *read(const char *filename, int64 size = File::ALL) const;
void write(const char *filename, const void *data, int64 size) const;
/**
* Append data to a file, creating it if it doesn't exist.
* @param filename The name of the file to write to.
* @param data The data to append.
* @param size The size in bytes of the data to append.
**/
void append(const char *filename, const void *data, int64 size) const;
/**
* This "native" method returns a table of all
* files in a given directory.
**/
int getDirectoryItems(lua_State *L);
/**
* Gets the last modification time of a file, in seconds
* since the Unix epoch.
* @param filename The name of the file.
**/
int64 getLastModified(const char *filename) const;
/**
* Gets the size of a file in bytes.
* @param filename The name of the file.
**/
int64 getSize(const char *filename) const;
/**
* Enable or disable symbolic link support in love.filesystem.
**/
void setSymlinksEnabled(bool enable);
/**
* Gets whether symbolic link support is enabled.
**/
bool areSymlinksEnabled() const;
/**
* Gets whether a filepath is actually a symlink.
* Always returns false if symlinks are not enabled.
**/
bool isSymlink(const char *filename) const;
/**
* Text file line-reading iterator function used and
* pushed on the Lua stack by love.filesystem.lines
* and File:lines.
**/
static int lines_i(lua_State *L);
std::vector<std::string> &getRequirePath();
void allowMountingForPath(const std::string &path);
private:
@@ -289,14 +119,16 @@ private:
// The full path to the source of the game.
std::string game_source;
// Workaround for machines without PhysFS 2.0
bool initialized;
// Allow saving outside of the LOVE_APPDATA_FOLDER
// for release 'builds'
bool fused;
bool fusedSet;
// Search path for require
std::vector<std::string> requirePath;
std::vector<std::string> allowedMountPaths;
}; // Filesystem
} // physfs
@@ -0,0 +1,62 @@
/**
* Copyright (c) 2006-2015 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#include "wrap_DroppedFile.h"
#include "wrap_File.h"
namespace love
{
namespace filesystem
{
DroppedFile *luax_checkdroppedfile(lua_State *L, int idx)
{
return luax_checktype<DroppedFile>(L, idx, FILESYSTEM_DROPPED_FILE_ID);
}
static const luaL_Reg functions[] =
{
// Inherits from File.
{ "getSize", w_File_getSize },
{ "open", w_File_open },
{ "close", w_File_close },
{ "isOpen", w_File_isOpen },
{ "read", w_File_read },
{ "write", w_File_write },
{ "flush", w_File_flush },
{ "isEOF", w_File_isEOF },
{ "tell", w_File_tell },
{ "seek", w_File_seek },
{ "lines", w_File_lines },
{ "setBuffer", w_File_setBuffer },
{ "getBuffer", w_File_getBuffer },
{ "getMode", w_File_getMode },
{ "getFilename", w_File_getFilename },
{ "getExtension", w_File_getExtension },
{ 0, 0 }
};
extern "C" int luaopen_droppedfile(lua_State *L)
{
return luax_register_type(L, FILESYSTEM_DROPPED_FILE_ID, functions);
}
} // filesystem
} // love
+39
View File
@@ -0,0 +1,39 @@
/**
* Copyright (c) 2006-2015 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#ifndef LOVE_FILESYSTEM_WRAP_DROPPED_FILE_H
#define LOVE_FILESYSTEM_WRAP_DROPPED_FILE_H
// LOVE
#include "common/runtime.h"
#include "DroppedFile.h"
namespace love
{
namespace filesystem
{
DroppedFile *luax_checkdroppedfile(lua_State *L, int idx);
extern "C" int luaopen_droppedfile(lua_State *L);
} // filesystem
} // love
#endif // LOVE_FILESYSTEM_WRAP_DROPPED_FILE_H
+131 -15
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2014 LOVE Development Team
* Copyright (c) 2006-2015 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
@@ -20,8 +20,6 @@
#include "wrap_File.h"
#include "physfs/Filesystem.h"
#include "common/Data.h"
#include "common/Exception.h"
#include "common/int.h"
@@ -45,7 +43,7 @@ int luax_ioError(lua_State *L, const char *fmt, ...)
File *luax_checkfile(lua_State *L, int idx)
{
return luax_checktype<File>(L, idx, "File", FILESYSTEM_FILE_T);
return luax_checktype<File>(L, idx, FILESYSTEM_FILE_ID);
}
int w_File_getSize(lua_State *L)
@@ -151,11 +149,11 @@ int w_File_write(lua_State *L)
return luax_ioError(L, "%s", e.what());
}
}
else if (luax_istype(L, 2, DATA_T))
else if (luax_istype(L, 2, DATA_ID))
{
try
{
love::Data *data = luax_totype<love::Data>(L, 2, "Data", DATA_T);
love::Data *data = luax_totype<love::Data>(L, 2, DATA_ID);
result = file->write(data, luaL_optinteger(L, 3, data->getSize()));
}
catch (love::Exception &e)
@@ -188,10 +186,10 @@ int w_File_flush(lua_State *L)
return 1;
}
int w_File_eof(lua_State *L)
int w_File_isEOF(lua_State *L)
{
File *file = luax_checkfile(L, 1);
luax_pushboolean(L, file->eof());
luax_pushboolean(L, file->isEOF());
return 1;
}
@@ -223,26 +221,128 @@ int w_File_seek(lua_State *L)
return 1;
}
int w_File_lines_i(lua_State *L)
{
const int bufsize = 1024;
char buf[bufsize];
int linesize = 0;
bool newline = false;
File *file = luax_checktype<File>(L, lua_upvalueindex(1), FILESYSTEM_FILE_ID);
// Only accept read mode at this point.
if (file->getMode() != File::MODE_READ)
return luaL_error(L, "File needs to stay in read mode.");
int64 pos = file->tell();
int64 userpos = -1;
if (lua_isnoneornil(L, lua_upvalueindex(2)) == 0)
{
// User may have changed the file position.
userpos = pos;
pos = (int64) lua_tonumber(L, lua_upvalueindex(2));
if (userpos != pos)
file->seek(pos);
}
while (!newline && !file->isEOF())
{
// This 64-bit to 32-bit integer cast should be safe as it never exceeds bufsize.
int read = (int) file->read(buf, bufsize);
if (read < 0)
return luaL_error(L, "Could not read from file.");
linesize += read;
for (int i = 0; i < read; i++)
{
if (buf[i] == '\n')
{
linesize -= read - i;
newline = true;
break;
}
}
}
if (newline || (file->isEOF() && linesize > 0))
{
if (linesize < bufsize)
{
// We have the line in the buffer on the stack. No 'new' and 'read' needed.
lua_pushlstring(L, buf, linesize > 0 && buf[linesize - 1] == '\r' ? linesize - 1 : linesize);
if (userpos < 0)
file->seek(pos + linesize + 1);
}
else
{
char *str = 0;
try
{
str = new char[linesize + 1];
}
catch(std::bad_alloc &)
{
// Can't lua_error (longjmp) in exception handlers.
}
if (!str)
return luaL_error(L, "Out of memory.");
file->seek(pos);
// Read the \n anyway and save us a call to seek.
if (file->read(str, linesize + 1) == -1)
{
delete [] str;
return luaL_error(L, "Could not read from file.");
}
lua_pushlstring(L, str, str[linesize - 1] == '\r' ? linesize - 1 : linesize);
delete [] str;
}
if (userpos >= 0)
{
// Save new position in upvalue.
lua_pushnumber(L, (lua_Number)(pos + linesize + 1));
lua_replace(L, lua_upvalueindex(2));
file->seek(userpos);
}
return 1;
}
// EOF reached.
if (userpos >= 0 && luax_toboolean(L, lua_upvalueindex(3)))
file->seek(userpos);
else
file->close();
return 0;
}
int w_File_lines(lua_State *L)
{
File *file = luax_checkfile(L, 1);
lua_pushnumber(L, 0); // File position.
luax_pushboolean(L, file->getMode() != File::CLOSED); // Save current file mode.
luax_pushboolean(L, file->getMode() != File::MODE_CLOSED); // Save current file mode.
if (file->getMode() != File::READ)
if (file->getMode() != File::MODE_READ)
{
if (file->getMode() != File::CLOSED)
if (file->getMode() != File::MODE_CLOSED)
file->close();
bool success = false;
luax_catchexcept(L, [&](){ success = file->open(File::READ); });
luax_catchexcept(L, [&](){ success = file->open(File::MODE_READ); });
if (!success)
return luaL_error(L, "Could not open file.");
}
lua_pushcclosure(L, physfs::Filesystem::lines_i, 3);
lua_pushcclosure(L, w_File_lines_i, 3);
return 1;
}
@@ -299,6 +399,20 @@ int w_File_getMode(lua_State *L)
return 1;
}
int w_File_getFilename(lua_State *L)
{
File *file = luax_checkfile(L, 1);
luax_pushstring(L, file->getFilename());
return 1;
}
int w_File_getExtension(lua_State *L)
{
File *file = luax_checkfile(L, 1);
luax_pushstring(L, file->getExtension());
return 1;
}
static const luaL_Reg functions[] =
{
{ "getSize", w_File_getSize },
@@ -308,19 +422,21 @@ static const luaL_Reg functions[] =
{ "read", w_File_read },
{ "write", w_File_write },
{ "flush", w_File_flush },
{ "eof", w_File_eof },
{ "isEOF", w_File_isEOF },
{ "tell", w_File_tell },
{ "seek", w_File_seek },
{ "lines", w_File_lines },
{ "setBuffer", w_File_setBuffer },
{ "getBuffer", w_File_getBuffer },
{ "getMode", w_File_getMode },
{ "getFilename", w_File_getFilename },
{ "getExtension", w_File_getExtension },
{ 0, 0 }
};
extern "C" int luaopen_file(lua_State *L)
{
return luax_register_type(L, "File", functions);
return luax_register_type(L, FILESYSTEM_FILE_ID, functions);
}
} // filesystem
+5 -2
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2014 LOVE Development Team
* Copyright (c) 2006-2015 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
@@ -41,13 +41,16 @@ int w_File_isOpen(lua_State *L);
int w_File_read(lua_State *L);
int w_File_write(lua_State *L);
int w_File_flush(lua_State *L);
int w_File_eof(lua_State *L);
int w_File_isEOF(lua_State *L);
int w_File_tell(lua_State *L);
int w_File_seek(lua_State *L);
int w_File_lines_i(lua_State *L);
int w_File_lines(lua_State *L);
int w_File_setBuffer(lua_State *L);
int w_File_getBuffer(lua_State *L);
int w_File_getMode(lua_State *L);
int w_File_getFilename(lua_State *L);
int w_File_getExtension(lua_State *L);
extern "C" int luaopen_file(lua_State *L);
} // filesystem
+3 -3
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2014 LOVE Development Team
* Copyright (c) 2006-2015 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
@@ -29,7 +29,7 @@ namespace filesystem
FileData *luax_checkfiledata(lua_State *L, int idx)
{
return luax_checktype<FileData>(L, idx, "FileData", FILESYSTEM_FILE_DATA_T);
return luax_checktype<FileData>(L, idx, FILESYSTEM_FILE_DATA_ID);
}
int w_FileData_getFilename(lua_State *L)
@@ -61,7 +61,7 @@ static const luaL_Reg w_FileData_functions[] =
extern "C" int luaopen_filedata(lua_State *L)
{
return luax_register_type(L, "FileData", w_FileData_functions);
return luax_register_type(L, FILESYSTEM_FILE_DATA_ID, w_FileData_functions);
}
} // filesystem
+1 -1
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2014 LOVE Development Team
* Copyright (c) 2006-2015 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
+90 -61
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2014 LOVE Development Team
* Copyright (c) 2006-2015 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
@@ -21,6 +21,7 @@
// LOVE
#include "wrap_Filesystem.h"
#include "wrap_File.h"
#include "wrap_DroppedFile.h"
#include "wrap_FileData.h"
#include "physfs/Filesystem.h"
@@ -28,12 +29,17 @@
// SDL
#include <SDL_loadso.h>
// STL
#include <vector>
#include <string>
#include <sstream>
namespace love
{
namespace filesystem
{
#define instance() (Module::getInstance<physfs::Filesystem>(Module::M_FILESYSTEM))
#define instance() (Module::getInstance<Filesystem>(Module::M_FILESYSTEM))
bool hack_setupWriteDirectory()
{
@@ -119,7 +125,7 @@ int w_newFile(lua_State *L)
const char *filename = luaL_checkstring(L, 1);
const char *str = 0;
File::Mode mode = File::CLOSED;
File::Mode mode = File::MODE_CLOSED;
if (lua_isstring(L, 2))
{
@@ -130,7 +136,7 @@ int w_newFile(lua_State *L)
File *t = instance()->newFile(filename);
if (mode != File::CLOSED)
if (mode != File::MODE_CLOSED)
{
try
{
@@ -144,7 +150,7 @@ int w_newFile(lua_State *L)
}
}
luax_pushtype(L, "File", FILESYSTEM_FILE_T, t);
luax_pushtype(L, FILESYSTEM_FILE_ID, t);
t->release();
return 1;
}
@@ -159,12 +165,12 @@ FileData *luax_getfiledata(lua_State *L, int idx)
const char *filename = luaL_checkstring(L, idx);
file = instance()->newFile(filename);
}
else if (luax_istype(L, idx, FILESYSTEM_FILE_T))
else if (luax_istype(L, idx, FILESYSTEM_FILE_ID))
{
file = luax_checkfile(L, idx);
file->retain();
}
else if (luax_istype(L, idx, FILESYSTEM_FILE_DATA_T))
else if (luax_istype(L, idx, FILESYSTEM_FILE_DATA_ID))
{
data = luax_checkfiledata(L, idx);
data->retain();
@@ -197,7 +203,7 @@ int w_newFileData(lua_State *L)
luax_convobj(L, 1, "filesystem", "newFile");
// Get FileData from the File.
if (luax_istype(L, 1, FILESYSTEM_FILE_T))
if (luax_istype(L, 1, FILESYSTEM_FILE_ID))
{
File *file = luax_checkfile(L, 1);
@@ -210,7 +216,7 @@ int w_newFileData(lua_State *L)
{
return luax_ioError(L, "%s", e.what());
}
luax_pushtype(L, "FileData", FILESYSTEM_FILE_DATA_T, data);
luax_pushtype(L, FILESYSTEM_FILE_DATA_ID, data);
data->release();
return 1;
}
@@ -242,7 +248,7 @@ int w_newFileData(lua_State *L)
return luaL_error(L, "Invalid FileData decoder: %s", decstr);
}
luax_pushtype(L, "FileData", FILESYSTEM_FILE_DATA_T, t);
luax_pushtype(L, FILESYSTEM_FILE_DATA_ID, t);
t->release();
return 1;
}
@@ -277,10 +283,21 @@ int w_getSourceBaseDirectory(lua_State *L)
return 1;
}
int w_exists(lua_State *L)
int w_getRealDirectory(lua_State *L)
{
const char *arg = luaL_checkstring(L, 1);
luax_pushboolean(L, instance()->exists(arg));
const char *filename = luaL_checkstring(L, 1);
std::string dir;
try
{
dir = instance()->getRealDirectory(filename);
}
catch (love::Exception &e)
{
return luax_ioError(L, "%s", e.what());
}
lua_pushstring(L, dir.c_str());
return 1;
}
@@ -349,9 +366,9 @@ static int w_write_or_append(lua_State *L, File::Mode mode)
const char *input = 0;
size_t len = 0;
if (luax_istype(L, 2, DATA_T))
if (luax_istype(L, 2, DATA_ID))
{
love::Data *data = luax_totype<love::Data>(L, 2, "Data", DATA_T);
love::Data *data = luax_totype<love::Data>(L, 2, DATA_ID);
input = (const char *) data->getData();
len = data->getSize();
}
@@ -365,7 +382,7 @@ static int w_write_or_append(lua_State *L, File::Mode mode)
try
{
if (mode == File::APPEND)
if (mode == File::MODE_APPEND)
instance()->append(filename, (const void *) input, len);
else
instance()->write(filename, (const void *) input, len);
@@ -381,12 +398,12 @@ static int w_write_or_append(lua_State *L, File::Mode mode)
int w_write(lua_State *L)
{
return w_write_or_append(L, File::WRITE);
return w_write_or_append(L, File::MODE_WRITE);
}
int w_append(lua_State *L)
{
return w_write_or_append(L, File::APPEND);
return w_write_or_append(L, File::MODE_APPEND);
}
int w_getDirectoryItems(lua_State *L)
@@ -403,7 +420,7 @@ int w_lines(lua_State *L)
file = instance()->newFile(lua_tostring(L, 1));
bool success = false;
luax_catchexcept(L, [&](){ success = file->open(File::READ); });
luax_catchexcept(L, [&](){ success = file->open(File::MODE_READ); });
if (!success)
{
@@ -411,13 +428,13 @@ int w_lines(lua_State *L)
return luaL_error(L, "Could not open file.");
}
luax_pushtype(L, "File", FILESYSTEM_FILE_T, file);
luax_pushtype(L, FILESYSTEM_FILE_ID, file);
file->release();
}
else
return luaL_argerror(L, 1, "expected filename.");
lua_pushcclosure(L, physfs::Filesystem::lines_i, 1);
lua_pushcclosure(L, w_File_lines_i, 1);
return 1;
}
@@ -512,58 +529,67 @@ int w_isSymlink(lua_State *L)
return 1;
}
int w_getRequirePath(lua_State *L)
{
std::stringstream path;
bool seperator = false;
for (auto &element : instance()->getRequirePath())
{
if (seperator)
path << ";";
else
seperator = true;
path << element;
}
luax_pushstring(L, path.str());
return 1;
}
int w_setRequirePath(lua_State *L)
{
std::string element = luax_checkstring(L, 1);
auto &requirePath = instance()->getRequirePath();
requirePath.clear();
std::stringstream path;
path << element;
while(std::getline(path, element, ';'))
requirePath.push_back(element);
return 0;
}
int loader(lua_State *L)
{
const char *filename = lua_tostring(L, -1);
std::string modulename = luax_tostring(L, 1);
std::string tmp(filename);
tmp += ".lua";
int size = tmp.size();
for (int i=0; i<size-4; i++)
for (char &c : modulename)
{
if (tmp[i] == '.')
{
tmp[i] = '/';
}
if (c == '.')
c = '/';
}
// Check whether file exists.
if (instance()->exists(tmp.c_str()))
auto *inst = instance();
for (std::string element : inst->getRequirePath())
{
lua_pop(L, 1);
lua_pushstring(L, tmp.c_str());
// Ok, load it.
return w_load(L);
}
size_t pos = element.find('?');
if (pos != std::string::npos)
element.replace(pos, 1, modulename);
tmp = filename;
size = tmp.size();
for (int i=0; i<size; i++)
{
if (tmp[i] == '.')
{
tmp[i] = '/';
}
}
if (instance()->isDirectory(tmp.c_str()))
{
tmp += "/init.lua";
if (instance()->exists(tmp.c_str()))
if (inst->isFile(element.c_str()))
{
lua_pop(L, 1);
lua_pushstring(L, tmp.c_str());
// Ok, load it.
lua_pushstring(L, element.c_str());
return w_load(L);
}
}
std::string errstr = "\n\tno file '%s' in LOVE game directories.";
errstr += errstr;
std::string errstr = "\n\tno '%s' in LOVE game directories.";
lua_pushfstring(L, errstr.c_str(), (tmp + ".lua").c_str(), (tmp + "/init.lua").c_str());
lua_pushfstring(L, errstr.c_str(), modulename.c_str());
return 1;
}
@@ -658,7 +684,7 @@ static const luaL_Reg functions[] =
{ "getAppdataDirectory", w_getAppdataDirectory },
{ "getSaveDirectory", w_getSaveDirectory },
{ "getSourceBaseDirectory", w_getSourceBaseDirectory },
{ "exists", w_exists },
{ "getRealDirectory", w_getRealDirectory },
{ "isDirectory", w_isDirectory },
{ "isFile", w_isFile },
{ "createDirectory", w_createDirectory },
@@ -675,19 +701,22 @@ static const luaL_Reg functions[] =
{ "areSymlinksEnabled", w_areSymlinksEnabled },
{ "isSymlink", w_isSymlink },
{ "newFileData", w_newFileData },
{ "getRequirePath", w_getRequirePath },
{ "setRequirePath", w_setRequirePath },
{ 0, 0 }
};
static const lua_CFunction types[] =
{
luaopen_file,
luaopen_droppedfile,
luaopen_filedata,
0
};
extern "C" int luaopen_love_filesystem(lua_State *L)
{
physfs::Filesystem *instance = instance();
Filesystem *instance = instance();
if (instance == nullptr)
{
luax_catchexcept(L, [&](){ instance = new physfs::Filesystem(); });
@@ -702,7 +731,7 @@ extern "C" int luaopen_love_filesystem(lua_State *L)
WrappedModule w;
w.module = instance;
w.name = "filesystem";
w.flags = MODULE_FILESYSTEM_T;
w.type = MODULE_FILESYSTEM_ID;
w.functions = functions;
w.types = types;
+2 -2
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2014 LOVE Development Team
* Copyright (c) 2006-2015 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
@@ -56,7 +56,7 @@ int w_getUserDirectory(lua_State *L);
int w_getAppdataDirectory(lua_State *L);
int w_getSaveDirectory(lua_State *L);
int w_getSourceBaseDirectory(lua_State *L);
int w_exists(lua_State *L);
int w_getRealDirectory(lua_State *L);
int w_isDirectory(lua_State *L);
int w_isFile(lua_State *L);
int w_createDirectory(lua_State *L);
+322
View File
@@ -0,0 +1,322 @@
/**
* Copyright (c) 2006-2015 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 "BMFontRasterizer.h"
#include "filesystem/Filesystem.h"
#include "image/Image.h"
// C++
#include <sstream>
#include <vector>
#include <algorithm>
// C
#include <cstdlib>
#include <cstring>
namespace love
{
namespace font
{
namespace
{
/**
* Helper class for parsing lines in BMFont definition files.
* NOTE: Does not properly handle multi-value attributes (e.g. 'padding' or
* 'spacing'.)
**/
class BMFontLine
{
public:
BMFontLine(const std::string &line);
const std::string &getTag() const { return tag; }
int getAttributeInt(const char *name) const;
std::string getAttributeString(const char *name) const;
private:
std::string tag;
std::map<std::string, std::string> attributes;
};
// This is not entirely robust...
BMFontLine::BMFontLine(const std::string &line)
{
// The tag name should always be at the start of the line.
tag = line.substr(0, line.find(' '));
size_t startpos = 0;
while (startpos < line.length())
{
// Find the next '=', which indicates a key-value pair.
size_t fpos = line.find('=', startpos);
if (fpos == std::string::npos || fpos + 1 >= line.length())
break;
// The key should be between a space character and the '='.
size_t keystart = line.rfind(' ', fpos);
if (keystart == std::string::npos)
break;
keystart++;
std::string key = line.substr(keystart, fpos - keystart);
size_t valstart = fpos + 1;
size_t valend = valstart + 1;
if (line[valstart] == '"')
{
// Values can be surrounded by quotes (a literal string.)
valstart++;
valend = line.find('"', valstart) - 1;
}
else
{
// Otherwise look for the next space character after the '='.
valend = line.find(' ', valstart + 1) - 1;
}
valend = std::min(valend, line.length() - 1);
attributes[key] = line.substr(valstart, valend - valstart + 1);
startpos = valend + 1;
}
}
int BMFontLine::getAttributeInt(const char *name) const
{
auto it = attributes.find(name);
if (it == attributes.end())
return 0;
return (int) strtol(it->second.c_str(), nullptr, 10);
}
std::string BMFontLine::getAttributeString(const char *name) const
{
auto it = attributes.find(name);
if (it == attributes.end())
return "";
return it->second;
}
} // anonymous namespace
BMFontRasterizer::BMFontRasterizer(love::filesystem::FileData *fontdef, const std::vector<image::ImageData *> &imagelist)
: fontSize(0)
, unicode(false)
, lineHeight(0)
{
const std::string &filename = fontdef->getFilename();
size_t separatorpos = filename.rfind('/');
if (separatorpos != std::string::npos)
fontFolder = filename.substr(0, separatorpos);
// The parseConfig function will try to load any missing page images.
for (int i = 0; i < (int) imagelist.size(); i++)
images[i] = imagelist[i];
std::string configtext((const char *) fontdef->getData(), fontdef->getSize());
parseConfig(configtext);
}
BMFontRasterizer::~BMFontRasterizer()
{
}
void BMFontRasterizer::parseConfig(const std::string &configtext)
{
std::stringstream ss(configtext);
std::string line;
while (std::getline(ss, line))
{
BMFontLine cline(line);
const std::string &tag = cline.getTag();
if (tag == "info")
{
fontSize = cline.getAttributeInt("size");
unicode = cline.getAttributeInt("unicode") > 0;
}
else if (tag == "common")
{
lineHeight = cline.getAttributeInt("lineHeight");
metrics.ascent = cline.getAttributeInt("base");
}
else if (tag == "page")
{
int pageindex = cline.getAttributeInt("id");
std::string filename = cline.getAttributeString("file");
// The file name is relative to the font file's folder.
if (!fontFolder.empty())
filename = fontFolder + "/" + filename;
// Load the page file from disk into an ImageData, if necessary.
if (images[pageindex].get() == nullptr)
{
using namespace love::filesystem;
Filesystem *filesystem = Module::getInstance<Filesystem>(Module::M_FILESYSTEM);
image::Image *imagemodule = Module::getInstance<image::Image>(Module::M_IMAGE);
if (!filesystem)
throw love::Exception("Filesystem module not loaded!");
if (!imagemodule)
throw love::Exception("Image module not loaded!");
// Release these variables right away since StrongRef retains.
StrongRef<filesystem::FileData> data = filesystem->read(filename.c_str());
data->release();
images[pageindex].set(imagemodule->newImageData(data.get()));
images[pageindex]->release();
}
}
else if (tag == "char")
{
BMFontCharacter c = {};
uint32 id = (uint32) cline.getAttributeInt("id");
c.x = cline.getAttributeInt("x");
c.y = cline.getAttributeInt("y");
c.page = cline.getAttributeInt("page");
c.metrics.width = cline.getAttributeInt("width");
c.metrics.height = cline.getAttributeInt("height");
c.metrics.bearingX = cline.getAttributeInt("xoffset");
c.metrics.bearingY = -cline.getAttributeInt("yoffset");
c.metrics.advance = cline.getAttributeInt("xadvance");
characters[id] = c;
}
else if (tag == "kerning")
{
// TODO
}
}
if (characters.size() == 0)
throw love::Exception("Invalid BMFont file (no character definitions?)");
// Try to guess the line height if the lineheight attribute isn't found.
bool guessheight = lineHeight == 0;
// Verify the glyph character attributes.
for (const auto &cpair : characters)
{
const BMFontCharacter &c = cpair.second;
if (!unicode && cpair.first > 127)
throw love::Exception("Invalid BMFont character id (only unicode and ASCII are supported)");
if (c.page < 0 || images[c.page].get() == nullptr)
throw love::Exception("Invalid BMFont character page id: %d", c.page);
const image::ImageData *id = images[c.page].get();
if (!id->inside(c.x, c.y) || !id->inside(c.x + c.metrics.width - 1, c.y + c.metrics.height - 1))
throw love::Exception("Invalid BMFont character coordinates.");
if (guessheight)
lineHeight = std::max(lineHeight, c.metrics.height);
}
metrics.height = lineHeight;
}
int BMFontRasterizer::getLineHeight() const
{
return lineHeight;
}
GlyphData *BMFontRasterizer::getGlyphData(uint32 glyph) const
{
auto it = characters.find(glyph);
// Return an empty GlyphData if we don't have the glyph character.
if (it == characters.end())
return new GlyphData(glyph, GlyphMetrics(), GlyphData::FORMAT_RGBA);
const BMFontCharacter &c = it->second;
GlyphData *g = new GlyphData(glyph, c.metrics, GlyphData::FORMAT_RGBA);
const auto &imagepair = images.find(c.page);
if (imagepair == images.end())
{
g->release();
return new GlyphData(glyph, GlyphMetrics(), GlyphData::FORMAT_RGBA);
}
image::ImageData *imagedata = imagepair->second.get();
image::pixel *pixels = (image::pixel *) g->getData();
const image::pixel *ipixels = (const image::pixel *) 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], sizeof(image::pixel) * c.metrics.width);
}
return g;
}
int BMFontRasterizer::getGlyphCount() const
{
return (int) characters.size();
}
bool BMFontRasterizer::hasGlyph(uint32 glyph) const
{
return characters.find(glyph) != characters.end();
}
bool BMFontRasterizer::accepts(love::filesystem::FileData *fontdef)
{
const char *data = (const char *) fontdef->getData();
// Check if the "info" tag is at the start of the file. This is a truly
// crappy test. Is the tag even guaranteed to be at the start?
return fontdef->getSize() > 4 && memcmp(data, "info", 4) == 0;
}
} // font
} // love
+86
View File
@@ -0,0 +1,86 @@
/**
* Copyright (c) 2006-2015 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_FONT_BMFONT_RASTERIZER_H
#define LOVE_FONT_BMFONT_RASTERIZER_H
// LOVE
#include "common/config.h"
#include "Rasterizer.h"
#include "image/ImageData.h"
// C++
#include <map>
#include <vector>
namespace love
{
namespace font
{
/**
* Rasterizer for BMFont bitmap fonts.
**/
class BMFontRasterizer : public Rasterizer
{
public:
BMFontRasterizer(love::filesystem::FileData *fontdef, const std::vector<image::ImageData *> &imagelist);
virtual ~BMFontRasterizer();
// Implements Rasterizer.
int getLineHeight() const override;
GlyphData *getGlyphData(uint32 glyph) const override;
int getGlyphCount() const override;
bool hasGlyph(uint32 glyph) const override;
static bool accepts(love::filesystem::FileData *fontdef);
private:
struct BMFontCharacter
{
int x;
int y;
int page;
GlyphMetrics metrics;
};
void parseConfig(const std::string &config);
std::string fontFolder;
// Image pages, indexed by their page id.
std::map<int, StrongRef<image::ImageData>> images;
// Glyph characters, indexed by their glyph id.
std::map<uint32, BMFontCharacter> characters;
int fontSize;
bool unicode;
int lineHeight;
}; // BMFontRasterizer
} // font
} // love
#endif // LOVE_FONT_BMFONT_RASTERIZER_H
+105
View File
@@ -0,0 +1,105 @@
/**
* Copyright (c) 2006-2015 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 "Font.h"
#include "BMFontRasterizer.h"
#include "ImageRasterizer.h"
#include "libraries/utf8/utf8.h"
namespace love
{
namespace font
{
// Default TrueType font.
#include "Vera.ttf.h"
class DefaultFontData : public love::Data
{
public:
virtual void *getData() const { return Vera_ttf; }
virtual size_t getSize() const { return sizeof(Vera_ttf); }
};
Rasterizer *Font::newTrueTypeRasterizer(int size, TrueTypeRasterizer::Hinting hinting)
{
StrongRef<DefaultFontData> data(new DefaultFontData);
data->release();
return newTrueTypeRasterizer(data.get(), size, hinting);
}
Rasterizer *Font::newBMFontRasterizer(love::filesystem::FileData *fontdef, const std::vector<image::ImageData *> &images)
{
return new BMFontRasterizer(fontdef, images);
}
Rasterizer *Font::newImageRasterizer(love::image::ImageData *data, const std::string &text, int extraspacing)
{
std::vector<uint32> glyphs;
glyphs.reserve(text.size());
try
{
utf8::iterator<std::string::const_iterator> i(text.begin(), text.begin(), text.end());
utf8::iterator<std::string::const_iterator> end(text.end(), text.begin(), text.end());
while (i != end)
glyphs.push_back(*i++);
}
catch (utf8::exception &e)
{
throw love::Exception("UTF-8 decoding error: %s", e.what());
}
return newImageRasterizer(data, &glyphs[0], (int) glyphs.size(), extraspacing);
}
Rasterizer *Font::newImageRasterizer(love::image::ImageData *data, uint32 *glyphs, int numglyphs, int extraspacing)
{
return new ImageRasterizer(data, glyphs, numglyphs, extraspacing);
}
GlyphData *Font::newGlyphData(Rasterizer *r, const std::string &text)
{
uint32 codepoint = 0;
try
{
codepoint = utf8::peek_next(text.begin(), text.end());
}
catch (utf8::exception &e)
{
throw love::Exception("UTF-8 decoding error: %s", e.what());
}
return r->getGlyphData(codepoint);
}
GlyphData *Font::newGlyphData(Rasterizer *r, uint32 glyph)
{
return r->getGlyphData(glyph);
}
} // font
} // love
+19 -11
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2014 LOVE Development Team
* Copyright (c) 2006-2015 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
@@ -23,12 +23,15 @@
// LOVE
#include "Rasterizer.h"
#include "TrueTypeRasterizer.h"
#include "image/ImageData.h"
#include "filesystem/FileData.h"
#include "common/Module.h"
#include "common/int.h"
// STD
// C++
#include <string>
#include <vector>
namespace love
{
@@ -42,16 +45,21 @@ public:
virtual ~Font() {}
// Implements Module.
virtual Rasterizer *newRasterizer(love::filesystem::FileData *data) = 0;
virtual Rasterizer *newTrueTypeRasterizer(int size, TrueTypeRasterizer::Hinting hinting);
virtual Rasterizer *newTrueTypeRasterizer(love::Data *data, int size, TrueTypeRasterizer::Hinting hinting) = 0;
virtual Rasterizer *newBMFontRasterizer(love::filesystem::FileData *fontdef, const std::vector<image::ImageData *> &images);
virtual Rasterizer *newImageRasterizer(love::image::ImageData *data, const std::string &glyphs, int extraspacing);
virtual Rasterizer *newImageRasterizer(love::image::ImageData *data, uint32 *glyphs, int length, int extraspacing);
virtual GlyphData *newGlyphData(Rasterizer *r, const std::string &glyph);
virtual GlyphData *newGlyphData(Rasterizer *r, uint32 glyph);
// Implement Module.
virtual ModuleType getModuleType() const { return M_FONT; }
virtual Rasterizer *newRasterizer(Data *data, int size) = 0;
virtual Rasterizer *newRasterizer(love::image::ImageData *data, const std::string &glyphs) = 0;
virtual Rasterizer *newRasterizer(love::image::ImageData *data, uint32 *glyphs, int length) = 0;
virtual GlyphData *newGlyphData(Rasterizer *r, const std::string &glyph) = 0;
virtual GlyphData *newGlyphData(Rasterizer *r, uint32 glyph) = 0;
// Implement Module
virtual const char *getName() const = 0;
}; // Font
+2 -2
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2014 LOVE Development Team
* Copyright (c) 2006-2015 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
@@ -168,7 +168,7 @@ bool GlyphData::getConstant(GlyphData::Format in, const char *&out)
StringMap<GlyphData::Format, GlyphData::FORMAT_MAX_ENUM>::Entry GlyphData::formatEntries[] =
{
{"luminance alpha", GlyphData::FORMAT_LUMINANCE_ALPHA},
{"luminancealpha", GlyphData::FORMAT_LUMINANCE_ALPHA},
{"rgba", GlyphData::FORMAT_RGBA},
};
+1 -1
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2014 LOVE Development Team
* Copyright (c) 2006-2015 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
+5 -19
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2014 LOVE Development Team
* Copyright (c) 2006-2015 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
@@ -34,10 +34,11 @@ 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);
}
ImageRasterizer::ImageRasterizer(love::image::ImageData *data, uint32 *glyphs, int numglyphs)
ImageRasterizer::ImageRasterizer(love::image::ImageData *data, uint32 *glyphs, int numglyphs, int extraspacing)
: imageData(data)
, glyphs(glyphs)
, numglyphs(numglyphs)
, extraSpacing(extraspacing)
{
load();
}
@@ -53,15 +54,14 @@ int ImageRasterizer::getLineHeight() const
GlyphData *ImageRasterizer::getGlyphData(uint32 glyph) const
{
GlyphMetrics gm;
memset(&gm, 0, sizeof(GlyphMetrics));
GlyphMetrics gm = {};
// Set relevant glyph metrics if the glyph is in this ImageFont
std::map<uint32, ImageGlyphData>::const_iterator it = imageGlyphs.find(glyph);
if (it != imageGlyphs.end())
{
gm.width = it->second.width;
gm.advance = it->second.width + it->second.spacing;
gm.advance = it->second.width + extraSpacing;
}
gm.height = metrics.height;
@@ -119,10 +119,6 @@ void ImageRasterizer::load()
while (start < imgw && equal(pixels[start], spacer))
++start;
// set previous glyph's spacing
if (i > 0 && imageGlyphs.size() > 0)
imageGlyphs[glyphs[i - 1]].spacing = (start > end) ? (start - end) : 0;
end = start;
// Find where glyph ends.
@@ -138,16 +134,6 @@ void ImageRasterizer::load()
imageGlyphs[glyphs[i]] = imageGlyph;
}
// Find spacing of last glyph
if (numglyphs > 0)
{
start = end;
while (start < imgw && equal(pixels[start], spacer))
++start;
imageGlyphs[glyphs[numglyphs - 1]].spacing = (start > end) ? (start - end) : 0;
}
}
int ImageRasterizer::getGlyphCount() const
+12 -10
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2014 LOVE Development Team
* Copyright (c) 2006-2015 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
@@ -39,7 +39,7 @@ namespace font
class ImageRasterizer : public Rasterizer
{
public:
ImageRasterizer(love::image::ImageData *imageData, uint32 *glyphs, int numglyphs);
ImageRasterizer(love::image::ImageData *imageData, uint32 *glyphs, int numglyphs, int extraspacing);
virtual ~ImageRasterizer();
// Implement Rasterizer
@@ -49,11 +49,19 @@ public:
virtual bool hasGlyph(uint32 glyph) const;
private:
// Information about a glyph in the ImageData
struct ImageGlyphData
{
int x;
int width;
};
// Load all the glyph positions into memory
void load();
// The image data
Object::StrongRef<love::image::ImageData> imageData;
StrongRef<love::image::ImageData> imageData;
// The glyphs in the font
uint32 *glyphs;
@@ -61,13 +69,7 @@ private:
// Number of glyphs in the font
int numglyphs;
// Information about a glyph in the ImageData
struct ImageGlyphData
{
int x;
int width;
int spacing;
};
int extraSpacing;
std::map<uint32, ImageGlyphData> imageGlyphs;
+1 -1
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2014 LOVE Development Team
* Copyright (c) 2006-2015 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
+1 -1
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2014 LOVE Development Team
* Copyright (c) 2006-2015 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
+49
View File
@@ -0,0 +1,49 @@
/**
* Copyright (c) 2006-2015 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 "TrueTypeRasterizer.h"
namespace love
{
namespace font
{
bool TrueTypeRasterizer::getConstant(const char *in, Hinting &out)
{
return hintings.find(in, out);
}
bool TrueTypeRasterizer::getConstant(Hinting in, const char *&out)
{
return hintings.find(in, out);
}
StringMap<TrueTypeRasterizer::Hinting, TrueTypeRasterizer::HINTING_MAX_ENUM>::Entry TrueTypeRasterizer::hintingEntries[] =
{
{"normal", HINTING_NORMAL},
{"light", HINTING_LIGHT},
{"mono", HINTING_MONO},
{"none", HINTING_NONE},
};
StringMap<TrueTypeRasterizer::Hinting, TrueTypeRasterizer::HINTING_MAX_ENUM> TrueTypeRasterizer::hintings(TrueTypeRasterizer::hintingEntries, sizeof(TrueTypeRasterizer::hintingEntries));
} // font
} // love
+62
View File
@@ -0,0 +1,62 @@
/**
* Copyright (c) 2006-2015 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_FONT_TRUE_TYPE_RASTERIZER_H
#define LOVE_FONT_TRUE_TYPE_RASTERIZER_H
// LOVE
#include "Rasterizer.h"
#include "common/StringMap.h"
namespace love
{
namespace font
{
class TrueTypeRasterizer : public Rasterizer
{
public:
// Types of hinting for TrueType font glyphs.
enum Hinting
{
HINTING_NORMAL,
HINTING_LIGHT,
HINTING_MONO,
HINTING_NONE,
HINTING_MAX_ENUM
};
virtual ~TrueTypeRasterizer() {}
static bool getConstant(const char *in, Hinting &out);
static bool getConstant(Hinting in, const char *&out);
private:
static StringMap<Hinting, HINTING_MAX_ENUM>::Entry hintingEntries[];
static StringMap<Hinting, HINTING_MAX_ENUM> hintings;
}; // TrueTypeRasterizer
} // font
} // love
#endif // LOVE_FONT_TRUE_TYPE_RASTERIZER_H
File diff suppressed because it is too large Load Diff
+13 -56
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2014 LOVE Development Team
* Copyright (c) 2006-2015 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
@@ -21,9 +21,9 @@
#include "Font.h"
#include "TrueTypeRasterizer.h"
#include "font/ImageRasterizer.h"
#include "font/BMFontRasterizer.h"
#include "libraries/utf8/utf8.h"
#include <string.h>
namespace love
{
@@ -35,7 +35,7 @@ namespace freetype
Font::Font()
{
if (FT_Init_FreeType(&library))
throw love::Exception("TrueTypeFont Loading error: FT_Init_FreeType failed\n");
throw love::Exception("TrueTypeFont Loading error: FT_Init_FreeType failed");
}
Font::~Font()
@@ -43,62 +43,19 @@ Font::~Font()
FT_Done_FreeType(library);
}
Rasterizer *Font::newRasterizer(Data *data, int size)
Rasterizer *Font::newRasterizer(love::filesystem::FileData *data)
{
return new TrueTypeRasterizer(library, data, size);
if (TrueTypeRasterizer::accepts(library, data))
return newTrueTypeRasterizer(data, 12, TrueTypeRasterizer::HINTING_NORMAL);
else if (BMFontRasterizer::accepts(data))
return newBMFontRasterizer(data, {});
throw love::Exception("Invalid font file: %s", data->getFilename().c_str());
}
Rasterizer *Font::newRasterizer(love::image::ImageData *data, const std::string &text)
Rasterizer *Font::newTrueTypeRasterizer(love::Data *data, int size, TrueTypeRasterizer::Hinting hinting)
{
size_t strlen = text.size();
size_t numglyphs = 0;
uint32 *glyphs = new uint32[strlen];
try
{
utf8::iterator<std::string::const_iterator> i(text.begin(), text.begin(), text.end());
utf8::iterator<std::string::const_iterator> end(text.end(), text.begin(), text.end());
while (i != end)
glyphs[numglyphs++] = *i++;
}
catch (utf8::exception &e)
{
delete [] glyphs;
throw love::Exception("UTF-8 decoding error: %s", e.what());
}
Rasterizer *r = newRasterizer(data, glyphs, numglyphs);
delete [] glyphs;
return r;
}
Rasterizer *Font::newRasterizer(love::image::ImageData *data, uint32 *glyphs, int numglyphs)
{
return new ImageRasterizer(data, glyphs, numglyphs);
}
GlyphData *Font::newGlyphData(Rasterizer *r, const std::string &text)
{
uint32 codepoint = 0;
try
{
codepoint = utf8::peek_next(text.begin(), text.end());
}
catch (utf8::exception &e)
{
throw love::Exception("UTF-8 decoding error: %s", e.what());
}
return r->getGlyphData(codepoint);
}
GlyphData *Font::newGlyphData(Rasterizer *r, uint32 glyph)
{
return r->getGlyphData(glyph);
return new TrueTypeRasterizer(library, data, size, hinting);
}
const char *Font::getName() const
+4 -14
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2014 LOVE Development Team
* Copyright (c) 2006-2015 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
@@ -25,11 +25,7 @@
#include "font/Font.h"
// FreeType2
#ifdef LOVE_MACOSX_USE_FRAMEWORKS
#include <freetype/ft2build.h>
#else
#include <ft2build.h>
#endif
#include FT_FREETYPE_H
#include FT_GLYPH_H
@@ -45,18 +41,11 @@ class Font : public love::font::Font
public:
Font();
/**
* Destructor.
**/
virtual ~Font();
// Implements Font
Rasterizer *newRasterizer(Data *data, int size);
Rasterizer *newRasterizer(love::image::ImageData *data, const std::string &text);
Rasterizer *newRasterizer(love::image::ImageData *data, uint32 *glyphs, int numglyphs);
GlyphData *newGlyphData(Rasterizer *r, const std::string &glyph);
GlyphData *newGlyphData(Rasterizer *r, uint32 glyph);
Rasterizer *newRasterizer(love::filesystem::FileData *data);
Rasterizer *newTrueTypeRasterizer(love::Data *data, int size, TrueTypeRasterizer::Hinting hinting);
// Implement Module
const char *getName() const;
@@ -65,6 +54,7 @@ private:
// FreeType library
FT_Library library;
}; // Font
} // freetype
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2014 LOVE Development Team
* Copyright (c) 2006-2015 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
@@ -30,9 +30,13 @@ namespace font
namespace freetype
{
TrueTypeRasterizer::TrueTypeRasterizer(FT_Library library, Data *data, int size)
TrueTypeRasterizer::TrueTypeRasterizer(FT_Library library, love::Data *data, int size, Hinting hinting)
: data(data)
, hinting(hinting)
{
if (size <= 0)
throw love::Exception("Invalid TrueType font size: %d", size);
FT_Error err = FT_Err_Ok;
err = FT_New_Memory_Face(library,
(const FT_Byte *)data->getData(), /* first byte in memory */
@@ -41,16 +45,19 @@ TrueTypeRasterizer::TrueTypeRasterizer(FT_Library library, Data *data, int size)
&face);
if (err != FT_Err_Ok)
throw love::Exception("TrueType Font Loading error: FT_New_Face failed: 0x%x (problem with font file?)", err);
throw love::Exception("TrueType Font loading error: FT_New_Face failed: 0x%x (problem with font file?)", err);
FT_Set_Pixel_Sizes(face, size, size);
err = FT_Set_Pixel_Sizes(face, size, size);
if (err != FT_Err_Ok)
throw love::Exception("TrueType Font loading error: FT_Set_Pixel_Sizes failed: 0x%x (invalid size?)", err);
// Set global metrics
FT_Size_Metrics s = face->size->metrics;
metrics.advance = s.max_advance >> 6;
metrics.ascent = s.ascender >> 6;
metrics.descent = s.descender >> 6;
metrics.height = s.height >> 6;
metrics.advance = (int) (s.max_advance >> 6);
metrics.ascent = (int) (s.ascender >> 6);
metrics.descent = (int) (s.descender >> 6);
metrics.height = (int) (s.height >> 6);
}
TrueTypeRasterizer::~TrueTypeRasterizer()
@@ -69,19 +76,27 @@ GlyphData *TrueTypeRasterizer::getGlyphData(uint32 glyph) const
FT_Glyph ftglyph;
FT_Error err = FT_Err_Ok;
FT_ULong loadoption = hintingToLoadOption(hinting);
// Initialize
err = FT_Load_Glyph(face, FT_Get_Char_Index(face, glyph), FT_LOAD_DEFAULT);
err = FT_Load_Glyph(face, FT_Get_Char_Index(face, glyph), FT_LOAD_DEFAULT | loadoption);
if (err != FT_Err_Ok)
throw love::Exception("TrueType Font Loading error: FT_Load_Glyph failed (0x%x)", err);
throw love::Exception("TrueType Font glyph error: FT_Load_Glyph failed (0x%x)", err);
err = FT_Get_Glyph(face->glyph, &ftglyph);
if (err != FT_Err_Ok)
throw love::Exception("TrueType Font Loading error: FT_Get_Glyph failed (0x%x)", err);
throw love::Exception("TrueType Font glyph error: FT_Get_Glyph failed (0x%x)", err);
FT_Glyph_To_Bitmap(&ftglyph, FT_RENDER_MODE_NORMAL, 0, 1);
FT_Render_Mode rendermode = FT_RENDER_MODE_NORMAL;
if (hinting == HINTING_MONO)
rendermode = FT_RENDER_MODE_MONO;
err = FT_Glyph_To_Bitmap(&ftglyph, rendermode, 0, 1);
if (err != FT_Err_Ok)
throw love::Exception("TrueType Font glyph error: FT_Glyph_To_Bitmap failed (0x%x)", err);
FT_BitmapGlyph bitmap_glyph = (FT_BitmapGlyph) ftglyph;
FT_Bitmap &bitmap = bitmap_glyph->bitmap; //just to make things easier
@@ -91,7 +106,7 @@ GlyphData *TrueTypeRasterizer::getGlyphData(uint32 glyph) const
glyphMetrics.bearingY = bitmap_glyph->top;
glyphMetrics.height = bitmap.rows;
glyphMetrics.width = bitmap.width;
glyphMetrics.advance = ftglyph->advance.x >> 16;
glyphMetrics.advance = (int) (ftglyph->advance.x >> 16);
GlyphData *glyphData = new GlyphData(glyph, glyphMetrics, GlyphData::FORMAT_LUMINANCE_ALPHA);
@@ -142,7 +157,7 @@ GlyphData *TrueTypeRasterizer::getGlyphData(uint32 glyph) const
int TrueTypeRasterizer::getGlyphCount() const
{
return face->num_glyphs;
return (int) face->num_glyphs;
}
bool TrueTypeRasterizer::hasGlyph(uint32 glyph) const
@@ -150,6 +165,31 @@ bool TrueTypeRasterizer::hasGlyph(uint32 glyph) const
return FT_Get_Char_Index(face, glyph) != 0;
}
bool TrueTypeRasterizer::accepts(FT_Library library, love::Data *data)
{
const FT_Byte *fbase = (const FT_Byte *) data->getData();
FT_Long fsize = (FT_Long) data->getSize();
// Pasing in -1 for the face index lets us test if the data is valid.
return FT_New_Memory_Face(library, fbase, fsize, -1, nullptr) == 0;
}
FT_ULong TrueTypeRasterizer::hintingToLoadOption(Hinting hint)
{
switch (hint)
{
case HINTING_NORMAL:
default:
return FT_LOAD_TARGET_NORMAL;
case HINTING_LIGHT:
return FT_LOAD_TARGET_LIGHT;
case HINTING_MONO:
return FT_LOAD_TARGET_MONO;
case HINTING_NONE:
return FT_LOAD_NO_HINTING;
}
}
} // freetype
} // font
} // love
+17 -9
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2014 LOVE Development Team
* Copyright (c) 2006-2015 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
@@ -22,10 +22,10 @@
#define LOVE_FONT_FREETYPE_TRUE_TYPE_RASTERIZER_H
// LOVE
#include "filesystem/File.h"
#include "font/Rasterizer.h"
#include "filesystem/FileData.h"
#include "font/TrueTypeRasterizer.h"
// TrueType2
// FreeType2
#include <ft2build.h>
#include FT_FREETYPE_H
#include FT_GLYPH_H
@@ -40,10 +40,11 @@ namespace freetype
/**
* Holds data for a font object.
**/
class TrueTypeRasterizer : public Rasterizer
class TrueTypeRasterizer : public love::font::TrueTypeRasterizer
{
public:
TrueTypeRasterizer(FT_Library library, Data *data, int size);
TrueTypeRasterizer(FT_Library library, love::Data *data, int size, Hinting hinting);
virtual ~TrueTypeRasterizer();
// Implement Rasterizer
@@ -52,14 +53,21 @@ public:
virtual int getGlyphCount() const;
virtual bool hasGlyph(uint32 glyph) const;
static bool accepts(FT_Library library, love::Data *data);
private:
static FT_ULong hintingToLoadOption(Hinting hinting);
// TrueType face
FT_Face face;
// File data
Object::StrongRef<Data> data;
}; // FreetypeRasterizer
// Font data
StrongRef<love::Data> data;
Hinting hinting;
}; // TrueTypeRasterizer
} // freetype
} // font
-128
View File
@@ -1,128 +0,0 @@
/**
* Copyright (c) 2006-2014 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#include "wrap_Font.h"
#include "Font.h"
#include "font/wrap_GlyphData.h"
#include "font/wrap_Rasterizer.h"
#include "filesystem/wrap_Filesystem.h"
#include "TrueTypeRasterizer.h"
namespace love
{
namespace font
{
namespace freetype
{
#define instance() (Module::getInstance<Font>(Module::M_FONT))
int w_newRasterizer(lua_State *L)
{
Rasterizer *t = nullptr;
if (luax_istype(L, 1, IMAGE_IMAGE_DATA_T))
{
love::image::ImageData *d = luax_checktype<love::image::ImageData>(L, 1, "ImageData", IMAGE_IMAGE_DATA_T);
const char *g = luaL_checkstring(L, 2);
std::string glyphs(g);
luax_catchexcept(L, [&](){ t = instance()->newRasterizer(d, glyphs); });
}
else if (lua_isstring(L, 1) || luax_istype(L, 1, FILESYSTEM_FILE_T) || luax_istype(L, 1, FILESYSTEM_FILE_DATA_T))
{
love::filesystem::FileData *d = love::filesystem::luax_getfiledata(L, 1);
int size = luaL_checkint(L, 2);
luax_catchexcept(L,
[&]() { t = instance()->newRasterizer(d, size); },
[&]() { d->release(); }
);
}
else
return luaL_argerror(L, 1, "expected ImageData, filename, or FileData");
luax_pushtype(L, "Rasterizer", FONT_RASTERIZER_T, t);
t->release();
return 1;
}
int w_newGlyphData(lua_State *L)
{
Rasterizer *r = luax_checkrasterizer(L, 1);
GlyphData *t = nullptr;
// newGlyphData accepts a unicode character or a codepoint number.
if (lua_type(L, 2) == LUA_TSTRING)
{
std::string glyph = luax_checkstring(L, 2);
luax_catchexcept(L, [&](){ t = instance()->newGlyphData(r, glyph); });
}
else
{
uint32 g = (uint32) luaL_checknumber(L, 2);
t = instance()->newGlyphData(r, g);
}
luax_pushtype(L, "GlyphData", FONT_GLYPH_DATA_T, t);
t->release();
return 1;
}
// List of functions to wrap.
static const luaL_Reg functions[] =
{
{ "newRasterizer", w_newRasterizer },
{ "newGlyphData", w_newGlyphData },
{ 0, 0 }
};
static const lua_CFunction types[] =
{
luaopen_glyphdata,
luaopen_rasterizer,
0
};
extern "C" int luaopen_love_font(lua_State *L)
{
Font *instance = instance();
if (instance == nullptr)
{
luax_catchexcept(L, [&](){ instance = new Font(); });
}
else
instance->retain();
WrappedModule w;
w.module = instance;
w.name = "font";
w.flags = MODULE_T;
w.functions = functions;
w.types = types;
return luax_register_module(L, w);
}
} // freetype
} // font
} // love
+235
View File
@@ -0,0 +1,235 @@
/**
* Copyright (c) 2006-2015 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#include "wrap_Font.h"
#include "Font.h"
#include "freetype/Font.h"
#include "wrap_GlyphData.h"
#include "wrap_Rasterizer.h"
#include "filesystem/wrap_Filesystem.h"
namespace love
{
namespace font
{
#define instance() (Module::getInstance<Font>(Module::M_FONT))
int w_newRasterizer(lua_State *L)
{
if (lua_type(L, 1) == LUA_TNUMBER || lua_type(L, 2) == LUA_TNUMBER)
{
// First or second argument is a number: call newTrueTypeRasterizer.
return w_newTrueTypeRasterizer(L);
}
else if (lua_isnoneornil(L, 2))
{
// Single argument of another type: call Font::newRasterizer.
Rasterizer *t = nullptr;
filesystem::FileData *d = filesystem::luax_getfiledata(L, 1);
luax_catchexcept(L,
[&]() { t = instance()->newRasterizer(d); },
[&]() { d->release(); }
);
luax_pushtype(L, FONT_RASTERIZER_ID, t);
t->release();
return 1;
}
else
{
// Otherwise call newBMFontRasterizer.
return w_newBMFontRasterizer(L);
}
}
int w_newTrueTypeRasterizer(lua_State *L)
{
Rasterizer *t = nullptr;
TrueTypeRasterizer::Hinting hinting = TrueTypeRasterizer::HINTING_NORMAL;
if (lua_type(L, 1) == LUA_TNUMBER)
{
// First argument is a number: use the default TrueType font.
int size = luaL_checkint(L, 1);
const char *hintstr = lua_isnoneornil(L, 2) ? nullptr : luaL_checkstring(L, 2);
if (hintstr && !TrueTypeRasterizer::getConstant(hintstr, hinting))
return luaL_error(L, "Invalid TrueType font hinting mode: %s", hintstr);
luax_catchexcept(L, [&](){ t = instance()->newTrueTypeRasterizer(size, hinting); });
}
else
{
love::Data *d = nullptr;
if (luax_istype(L, 1, DATA_ID))
d = luax_checkdata(L, 1);
else
d = filesystem::luax_getfiledata(L, 1);
int size = luaL_optint(L, 2, 12);
const char *hintstr = lua_isnoneornil(L, 3) ? nullptr : luaL_checkstring(L, 3);
if (hintstr && !TrueTypeRasterizer::getConstant(hintstr, hinting))
return luaL_error(L, "Invalid TrueType font hinting mode: %s", hintstr);
luax_catchexcept(L,
[&]() { t = instance()->newTrueTypeRasterizer(d, size, hinting); },
[&]() { d->release(); }
);
}
luax_pushtype(L, FONT_RASTERIZER_ID, t);
t->release();
return 1;
}
static void convimagedata(lua_State *L, int idx)
{
if (lua_type(L, 1) == LUA_TSTRING || luax_istype(L, idx, FILESYSTEM_FILE_ID) || luax_istype(L, idx, FILESYSTEM_FILE_DATA_ID))
luax_convobj(L, idx, "image", "newImageData");
}
int w_newBMFontRasterizer(lua_State *L)
{
Rasterizer *t = nullptr;
filesystem::FileData *d = filesystem::luax_getfiledata(L, 1);
std::vector<image::ImageData *> images;
if (lua_istable(L, 2))
{
for (int i = 1; i <= (int) lua_objlen(L, 2); i++)
{
lua_rawgeti(L, 2, i);
convimagedata(L, -1);
image::ImageData *id = luax_checktype<image::ImageData>(L, -1, IMAGE_IMAGE_DATA_ID);
images.push_back(id);
id->retain();
lua_pop(L, 1);
}
}
else
{
for (int i = 2; i <= lua_gettop(L); i++)
{
convimagedata(L, i);
image::ImageData *id = luax_checktype<image::ImageData>(L, i, IMAGE_IMAGE_DATA_ID);
images.push_back(id);
id->retain();
}
}
luax_catchexcept(L,
[&]() { t = instance()->newBMFontRasterizer(d, images); },
[&]() { d->release(); for (auto id : images) id->release(); }
);
luax_pushtype(L, FONT_RASTERIZER_ID, t);
t->release();
return 1;
}
int w_newImageRasterizer(lua_State *L)
{
Rasterizer *t = nullptr;
convimagedata(L, 1);
image::ImageData *d = luax_checktype<image::ImageData>(L, 1, IMAGE_IMAGE_DATA_ID);
std::string glyphs = luax_checkstring(L, 2);
int extraspacing = luaL_optint(L, 3, 0);
luax_catchexcept(L, [&](){ t = instance()->newImageRasterizer(d, glyphs, extraspacing); });
luax_pushtype(L, FONT_RASTERIZER_ID, t);
t->release();
return 1;
}
int w_newGlyphData(lua_State *L)
{
Rasterizer *r = luax_checkrasterizer(L, 1);
GlyphData *t = nullptr;
// newGlyphData accepts a unicode character or a codepoint number.
if (lua_type(L, 2) == LUA_TSTRING)
{
std::string glyph = luax_checkstring(L, 2);
luax_catchexcept(L, [&](){ t = instance()->newGlyphData(r, glyph); });
}
else
{
uint32 g = (uint32) luaL_checknumber(L, 2);
t = instance()->newGlyphData(r, g);
}
luax_pushtype(L, FONT_GLYPH_DATA_ID, t);
t->release();
return 1;
}
// List of functions to wrap.
static const luaL_Reg functions[] =
{
{ "newRasterizer", w_newRasterizer },
{ "newTrueTypeRasterizer", w_newTrueTypeRasterizer },
{ "newBMFontRasterizer", w_newBMFontRasterizer },
{ "newImageRasterizer", w_newImageRasterizer },
{ "newGlyphData", w_newGlyphData },
{ 0, 0 }
};
static const lua_CFunction types[] =
{
luaopen_glyphdata,
luaopen_rasterizer,
0
};
extern "C" int luaopen_love_font(lua_State *L)
{
Font *instance = instance();
if (instance == nullptr)
{
luax_catchexcept(L, [&](){ instance = new freetype::Font(); });
}
else
instance->retain();
WrappedModule w;
w.module = instance;
w.name = "font";
w.type = MODULE_ID;
w.functions = functions;
w.types = types;
return luax_register_module(L, w);
}
} // font
} // love
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2014 LOVE Development Team
* Copyright (c) 2006-2015 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
@@ -18,8 +18,8 @@
* 3. This notice may not be removed or altered from any source distribution.
**/
#ifndef LOVE_FONT_FREETYPE_WRAP_FONT_H
#define LOVE_FONT_FREETYPE_WRAP_FONT_H
#ifndef LOVE_FONT_WRAP_FONT_H
#define LOVE_FONT_WRAP_FONT_H
// LOVE
#include "common/config.h"
@@ -29,15 +29,15 @@ namespace love
{
namespace font
{
namespace freetype
{
int w_newRasterizer(lua_State *L);
int w_newTrueTypeRasterizer(lua_State *L);
int w_newBMFontRasterizer(lua_State *L);
int w_newImageRasterizer(lua_State *L);
int w_newGlyphData(lua_State *L);
extern "C" LOVE_EXPORT int luaopen_love_font(lua_State *L);
} // freetype
} // font
} // love
#endif // LOVE_FONT_FREETYPE_WRAP_FONT_H
#endif // LOVE_FONT_WRAP_FONT_H
+3 -3
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2014 LOVE Development Team
* Copyright (c) 2006-2015 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
@@ -27,7 +27,7 @@ namespace font
GlyphData *luax_checkglyphdata(lua_State *L, int idx)
{
return luax_checktype<GlyphData>(L, idx, "GlyphData", FONT_GLYPH_DATA_T);
return luax_checktype<GlyphData>(L, idx, FONT_GLYPH_DATA_ID);
}
int w_GlyphData_getWidth(lua_State *L)
@@ -136,7 +136,7 @@ static const luaL_Reg functions[] =
extern "C" int luaopen_glyphdata(lua_State *L)
{
return luax_register_type(L, "GlyphData", functions);
return luax_register_type(L, FONT_GLYPH_DATA_ID, functions);
}
} // font
+1 -1
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2014 LOVE Development Team
* Copyright (c) 2006-2015 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
+4 -4
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2014 LOVE Development Team
* Copyright (c) 2006-2015 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
@@ -29,7 +29,7 @@ namespace font
Rasterizer *luax_checkrasterizer(lua_State *L, int idx)
{
return luax_checktype<Rasterizer>(L, idx, "Rasterizer", FONT_RASTERIZER_T);
return luax_checktype<Rasterizer>(L, idx, FONT_RASTERIZER_ID);
}
int w_Rasterizer_getHeight(lua_State *L)
@@ -86,7 +86,7 @@ int w_Rasterizer_getGlyphData(lua_State *L)
}
});
luax_pushtype(L, "GlyphData", FONT_GLYPH_DATA_T, g);
luax_pushtype(L, FONT_GLYPH_DATA_ID, g);
g->release();
return 1;
}
@@ -139,7 +139,7 @@ static const luaL_Reg functions[] =
extern "C" int luaopen_rasterizer(lua_State *L)
{
return luax_register_type(L, "Rasterizer", functions);
return luax_register_type(L, FONT_RASTERIZER_ID, functions);
}
} // font
+1 -1
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2014 LOVE Development Team
* Copyright (c) 2006-2015 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
+1 -1
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2014 LOVE Development Team
* Copyright (c) 2006-2015 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
+35 -81
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2014 LOVE Development Team
* Copyright (c) 2006-2015 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
@@ -34,27 +34,17 @@ bool Graphics::getConstant(const char *in, DrawMode &out)
return drawModes.find(in, out);
}
bool Graphics::getConstant(DrawMode in, const char *&out)
bool Graphics::getConstant(DrawMode in, const char *&out)
{
return drawModes.find(in, out);
}
bool Graphics::getConstant(const char *in, AlignMode &out)
{
return alignModes.find(in, out);
}
bool Graphics::getConstant(AlignMode in, const char *&out)
{
return alignModes.find(in, out);
}
bool Graphics::getConstant(const char *in, BlendMode &out)
{
return blendModes.find(in, out);
}
bool Graphics::getConstant(BlendMode in, const char *&out)
bool Graphics::getConstant(BlendMode in, const char *&out)
{
return blendModes.find(in, out);
}
@@ -64,7 +54,7 @@ bool Graphics::getConstant(const char *in, LineStyle &out)
return lineStyles.find(in, out);
}
bool Graphics::getConstant(LineStyle in, const char *&out)
bool Graphics::getConstant(LineStyle in, const char *&out)
{
return lineStyles.find(in, out);
}
@@ -74,27 +64,17 @@ bool Graphics::getConstant(const char *in, LineJoin &out)
return lineJoins.find(in, out);
}
bool Graphics::getConstant(LineJoin in, const char *&out)
bool Graphics::getConstant(LineJoin in, const char *&out)
{
return lineJoins.find(in, out);
}
bool Graphics::getConstant(const char *in, PointStyle &out)
{
return pointStyles.find(in, out);
}
bool Graphics::getConstant(PointStyle in, const char *&out)
{
return pointStyles.find(in, out);
}
bool Graphics::getConstant(const char *in, Support &out)
{
return support.find(in, out);
}
bool Graphics::getConstant(Support in, const char *&out)
bool Graphics::getConstant(Support in, const char *&out)
{
return support.find(in, out);
}
@@ -131,103 +111,77 @@ bool Graphics::getConstant(StatType in, const char *&out)
StringMap<Graphics::DrawMode, Graphics::DRAW_MAX_ENUM>::Entry Graphics::drawModeEntries[] =
{
{ "line", Graphics::DRAW_LINE },
{ "fill", Graphics::DRAW_FILL },
{ "line", DRAW_LINE },
{ "fill", DRAW_FILL },
};
StringMap<Graphics::DrawMode, Graphics::DRAW_MAX_ENUM> Graphics::drawModes(Graphics::drawModeEntries, sizeof(Graphics::drawModeEntries));
StringMap<Graphics::AlignMode, Graphics::ALIGN_MAX_ENUM>::Entry Graphics::alignModeEntries[] =
{
{ "left", Graphics::ALIGN_LEFT },
{ "right", Graphics::ALIGN_RIGHT },
{ "center", Graphics::ALIGN_CENTER },
{ "justify", Graphics::ALIGN_JUSTIFY },
};
StringMap<Graphics::AlignMode, Graphics::ALIGN_MAX_ENUM> Graphics::alignModes(Graphics::alignModeEntries, sizeof(Graphics::alignModeEntries));
StringMap<Graphics::BlendMode, Graphics::BLEND_MAX_ENUM>::Entry Graphics::blendModeEntries[] =
{
{ "alpha", Graphics::BLEND_ALPHA },
{ "additive", Graphics::BLEND_ADDITIVE },
{ "subtractive", Graphics::BLEND_SUBTRACTIVE },
{ "multiplicative", Graphics::BLEND_MULTIPLICATIVE },
{ "premultiplied", Graphics::BLEND_PREMULTIPLIED },
{ "screen", Graphics::BLEND_SCREEN },
{ "replace", Graphics::BLEND_REPLACE },
{ "alpha", BLEND_ALPHA },
{ "add", BLEND_ADD },
{ "subtract", BLEND_SUBTRACT },
{ "multiply", BLEND_MULTIPLY },
{ "premultiplied", BLEND_PREMULTIPLIED },
{ "screen", BLEND_SCREEN },
{ "replace", BLEND_REPLACE },
};
StringMap<Graphics::BlendMode, Graphics::BLEND_MAX_ENUM> Graphics::blendModes(Graphics::blendModeEntries, sizeof(Graphics::blendModeEntries));
StringMap<Graphics::LineStyle, Graphics::LINE_MAX_ENUM>::Entry Graphics::lineStyleEntries[] =
{
{ "smooth", Graphics::LINE_SMOOTH },
{ "rough", Graphics::LINE_ROUGH }
{ "smooth", LINE_SMOOTH },
{ "rough", LINE_ROUGH }
};
StringMap<Graphics::LineStyle, Graphics::LINE_MAX_ENUM> Graphics::lineStyles(Graphics::lineStyleEntries, sizeof(Graphics::lineStyleEntries));
StringMap<Graphics::LineJoin, Graphics::LINE_JOIN_MAX_ENUM>::Entry Graphics::lineJoinEntries[] =
{
{ "none", Graphics::LINE_JOIN_NONE },
{ "miter", Graphics::LINE_JOIN_MITER },
{ "bevel", Graphics::LINE_JOIN_BEVEL }
{ "none", LINE_JOIN_NONE },
{ "miter", LINE_JOIN_MITER },
{ "bevel", LINE_JOIN_BEVEL }
};
StringMap<Graphics::LineJoin, Graphics::LINE_JOIN_MAX_ENUM> Graphics::lineJoins(Graphics::lineJoinEntries, sizeof(Graphics::lineJoinEntries));
StringMap<Graphics::PointStyle, Graphics::POINT_MAX_ENUM>::Entry Graphics::pointStyleEntries[] =
{
{ "smooth", Graphics::POINT_SMOOTH },
{ "rough", Graphics::POINT_ROUGH }
};
StringMap<Graphics::PointStyle, Graphics::POINT_MAX_ENUM> Graphics::pointStyles(Graphics::pointStyleEntries, sizeof(Graphics::pointStyleEntries));
StringMap<Graphics::Support, Graphics::SUPPORT_MAX_ENUM>::Entry Graphics::supportEntries[] =
{
{ "canvas", Graphics::SUPPORT_CANVAS },
{ "hdrcanvas", Graphics::SUPPORT_HDR_CANVAS },
{ "multicanvas", Graphics::SUPPORT_MULTI_CANVAS },
{ "shader", Graphics::SUPPORT_SHADER },
{ "npot", Graphics::SUPPORT_NPOT },
{ "subtractive", Graphics::SUPPORT_SUBTRACTIVE },
{ "mipmap", Graphics::SUPPORT_MIPMAP },
{ "dxt", Graphics::SUPPORT_DXT },
{ "bc5", Graphics::SUPPORT_BC5 },
{ "srgb", Graphics::SUPPORT_SRGB },
{ "multicanvas", SUPPORT_MULTI_CANVAS },
{ "multicanvasformats", SUPPORT_MULTI_CANVAS_FORMATS },
{ "srgb", SUPPORT_SRGB },
};
StringMap<Graphics::Support, Graphics::SUPPORT_MAX_ENUM> Graphics::support(Graphics::supportEntries, sizeof(Graphics::supportEntries));
StringMap<Graphics::SystemLimit, Graphics::LIMIT_MAX_ENUM>::Entry Graphics::systemLimitEntries[] =
{
{"pointsize", Graphics::LIMIT_POINT_SIZE},
{"texturesize", Graphics::LIMIT_TEXTURE_SIZE},
{"multicanvas", Graphics::LIMIT_MULTI_CANVAS},
{"canvasfsaa", Graphics::LIMIT_CANVAS_FSAA},
{"canvasmsaa", Graphics::LIMIT_CANVAS_MSAA},
{ "pointsize", LIMIT_POINT_SIZE },
{ "texturesize", LIMIT_TEXTURE_SIZE },
{ "multicanvas", LIMIT_MULTI_CANVAS },
{ "canvasmsaa", LIMIT_CANVAS_MSAA },
};
StringMap<Graphics::SystemLimit, Graphics::LIMIT_MAX_ENUM> Graphics::systemLimits(Graphics::systemLimitEntries, sizeof(Graphics::systemLimitEntries));
StringMap<Graphics::StackType, Graphics::STACK_MAX_ENUM>::Entry Graphics::stackTypeEntries[] =
{
{"all", Graphics::STACK_ALL},
{"transform", Graphics::STACK_TRANSFORM},
{ "all", STACK_ALL },
{ "transform", STACK_TRANSFORM },
};
StringMap<Graphics::StackType, Graphics::STACK_MAX_ENUM> Graphics::stackTypes(Graphics::stackTypeEntries, sizeof(Graphics::stackTypeEntries));
StringMap<Graphics::StatType, Graphics::STAT_MAX_ENUM>::Entry Graphics::statTypeEntries[] =
{
{"drawcalls", Graphics::STAT_DRAW_CALLS},
{"canvasswitches", Graphics::STAT_CANVAS_SWITCHES},
{"canvases", Graphics::STAT_CANVASES},
{"images", Graphics::STAT_IMAGES},
{"fonts", Graphics::STAT_FONTS},
{"texturememory", Graphics::STAT_TEXTURE_MEMORY},
{ "drawcalls", STAT_DRAW_CALLS },
{ "canvasswitches", STAT_CANVAS_SWITCHES },
{ "canvases", STAT_CANVASES },
{ "images", STAT_IMAGES },
{ "fonts", STAT_FONTS },
{ "texturememory", STAT_TEXTURE_MEMORY },
};
StringMap<Graphics::StatType, Graphics::STAT_MAX_ENUM> Graphics::statTypes(Graphics::statTypeEntries, sizeof(Graphics::statTypeEntries));
+46 -46
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2014 LOVE Development Team
* Copyright (c) 2006-2015 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
@@ -44,21 +44,12 @@ public:
DRAW_MAX_ENUM
};
enum AlignMode
{
ALIGN_LEFT,
ALIGN_CENTER,
ALIGN_RIGHT,
ALIGN_JUSTIFY,
ALIGN_MAX_ENUM
};
enum BlendMode
{
BLEND_ALPHA,
BLEND_ADDITIVE,
BLEND_SUBTRACTIVE,
BLEND_MULTIPLICATIVE,
BLEND_ADD,
BLEND_SUBTRACT,
BLEND_MULTIPLY,
BLEND_PREMULTIPLIED,
BLEND_SCREEN,
BLEND_REPLACE,
@@ -80,34 +71,26 @@ public:
LINE_JOIN_MAX_ENUM
};
enum PointStyle
{
POINT_ROUGH,
POINT_SMOOTH,
POINT_MAX_ENUM
};
enum Support
{
SUPPORT_CANVAS,
SUPPORT_HDR_CANVAS,
SUPPORT_MULTI_CANVAS,
SUPPORT_SHADER,
SUPPORT_NPOT,
SUPPORT_SUBTRACTIVE,
SUPPORT_MIPMAP,
SUPPORT_DXT,
SUPPORT_BC5,
SUPPORT_MULTI_CANVAS_FORMATS,
SUPPORT_SRGB,
SUPPORT_MAX_ENUM
};
enum Renderer
{
RENDERER_OPENGL = 0,
RENDERER_OPENGLES,
RENDERER_MAX_ENUM
};
enum SystemLimit
{
LIMIT_POINT_SIZE,
LIMIT_TEXTURE_SIZE,
LIMIT_MULTI_CANVAS,
LIMIT_CANVAS_FSAA, // For backward-compatibility. TODO: remove!
LIMIT_CANVAS_MSAA,
LIMIT_MAX_ENUM
};
@@ -148,6 +131,21 @@ public:
size_t textureMemory;
};
struct ColorMask
{
bool r, g, b, a;
bool operator == (const ColorMask &m) const
{
return r == m.r && g == m.g && b == m.b && a == m.a;
}
bool operator != (const ColorMask &m) const
{
return !(operator == (m));
}
};
virtual ~Graphics();
// Implements Module.
@@ -171,26 +169,34 @@ public:
**/
virtual void unSetMode() = 0;
static bool getConstant(const char *in, DrawMode &out);
static bool getConstant(DrawMode in, const char *&out);
/**
* Sets whether the module is active (internal use only.)
**/
virtual void setActive(bool active) = 0;
static bool getConstant(const char *in, AlignMode &out);
static bool getConstant(AlignMode in, const char *&out);
/**
* Gets whether the module is active. Graphics module methods are only
* guaranteed to work when it is active. Calling them otherwise may cause
* the program to crash (or worse.)
* Normally the module will always be active as long as a window exists, it
* may be different on some platforms (especially mobile ones.)
**/
virtual bool isActive() const = 0;
static bool getConstant(const char *in, DrawMode &out);
static bool getConstant(DrawMode in, const char *&out);
static bool getConstant(const char *in, BlendMode &out);
static bool getConstant(BlendMode in, const char *&out);
static bool getConstant(BlendMode in, const char *&out);
static bool getConstant(const char *in, LineStyle &out);
static bool getConstant(LineStyle in, const char *&out);
static bool getConstant(LineStyle in, const char *&out);
static bool getConstant(const char *in, LineJoin &out);
static bool getConstant(LineJoin in, const char *&out);
static bool getConstant(const char *in, PointStyle &out);
static bool getConstant(PointStyle in, const char *&out);
static bool getConstant(LineJoin in, const char *&out);
static bool getConstant(const char *in, Support &out);
static bool getConstant(Support in, const char *&out);
static bool getConstant(Support in, const char *&out);
static bool getConstant(const char *in, SystemLimit &out);
static bool getConstant(SystemLimit in, const char *&out);
@@ -206,9 +212,6 @@ private:
static StringMap<DrawMode, DRAW_MAX_ENUM>::Entry drawModeEntries[];
static StringMap<DrawMode, DRAW_MAX_ENUM> drawModes;
static StringMap<AlignMode, ALIGN_MAX_ENUM>::Entry alignModeEntries[];
static StringMap<AlignMode, ALIGN_MAX_ENUM> alignModes;
static StringMap<BlendMode, BLEND_MAX_ENUM>::Entry blendModeEntries[];
static StringMap<BlendMode, BLEND_MAX_ENUM> blendModes;
@@ -218,9 +221,6 @@ private:
static StringMap<LineJoin, LINE_JOIN_MAX_ENUM>::Entry lineJoinEntries[];
static StringMap<LineJoin, LINE_JOIN_MAX_ENUM> lineJoins;
static StringMap<PointStyle, POINT_MAX_ENUM>::Entry pointStyleEntries[];
static StringMap<PointStyle, POINT_MAX_ENUM> pointStyles;
static StringMap<Support, SUPPORT_MAX_ENUM>::Entry supportEntries[];
static StringMap<Support, SUPPORT_MAX_ENUM> support;
+14 -9
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2014 LOVE Development Team
* Copyright (c) 2006-2015 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
@@ -33,7 +33,7 @@ Quad::Quad(const Quad::Viewport &v, float sw, float sh)
: sw(sw)
, sh(sh)
{
memset(vertices, 255, sizeof(Vertex) * NUM_VERTICES);
memset(vertices, 255, sizeof(Vertex) * 4);
refresh(v, sw, sh);
}
@@ -45,23 +45,28 @@ void Quad::refresh(const Quad::Viewport &v, float sw, float sh)
{
viewport = v;
vertices[0].x = 0;
vertices[0].y = 0;
vertices[1].x = 0;
// Vertices are ordered for use with triangle strips:
// 0----2
// | / |
// | / |
// 1----3
vertices[0].x = 0.0f;
vertices[0].y = 0.0f;
vertices[1].x = 0.0f;
vertices[1].y = v.h;
vertices[2].x = v.w;
vertices[2].y = v.h;
vertices[2].y = 0.0f;
vertices[3].x = v.w;
vertices[3].y = 0;
vertices[3].y = v.h;
vertices[0].s = v.x/sw;
vertices[0].t = v.y/sh;
vertices[1].s = v.x/sw;
vertices[1].t = (v.y+v.h)/sh;
vertices[2].s = (v.x+v.w)/sw;
vertices[2].t = (v.y+v.h)/sh;
vertices[2].t = v.y/sh;
vertices[3].s = (v.x+v.w)/sw;
vertices[3].t = v.y/sh;
vertices[3].t = (v.y+v.h)/sh;
}
void Quad::setViewport(const Quad::Viewport &v)
+2 -4
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2014 LOVE Development Team
* Copyright (c) 2006-2015 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
@@ -40,8 +40,6 @@ public:
float w, h;
};
static const size_t NUM_VERTICES = 4;
Quad(const Viewport &v, float sw, float sh);
virtual ~Quad();
@@ -53,7 +51,7 @@ public:
private:
Vertex vertices[NUM_VERTICES];
Vertex vertices[4];
Viewport viewport;
float sw;
+20 -15
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2014 LOVE Development Team
* Copyright (c) 2006-2015 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
@@ -27,20 +27,6 @@ namespace graphics
Texture::Filter Texture::defaultFilter;
Texture::Filter::Filter()
: min(FILTER_LINEAR)
, mag(FILTER_LINEAR)
, mipmap(FILTER_NONE)
, anisotropy(1.0f)
{
}
Texture::Wrap::Wrap()
: s(WRAP_CLAMP)
, t(WRAP_CLAMP)
{
}
Texture::Texture()
: width(0)
, height(0)
@@ -89,6 +75,23 @@ const Texture::Filter &Texture::getDefaultFilter()
return defaultFilter;
}
bool Texture::validateFilter(const Filter &f, bool mipmapsAllowed)
{
if (!mipmapsAllowed && f.mipmap != FILTER_NONE)
return false;
if (f.mag != FILTER_LINEAR && f.mag != FILTER_NEAREST)
return false;
if (f.min != FILTER_LINEAR && f.min != FILTER_NEAREST)
return false;
if (f.mipmap != FILTER_LINEAR && f.mipmap != FILTER_NEAREST && f.mipmap != FILTER_NONE)
return false;
return true;
}
bool Texture::getConstant(const char *in, FilterMode &out)
{
return filterModes.find(in, out);
@@ -113,6 +116,7 @@ StringMap<Texture::FilterMode, Texture::FILTER_MAX_ENUM>::Entry Texture::filterM
{
{ "linear", Texture::FILTER_LINEAR },
{ "nearest", Texture::FILTER_NEAREST },
{ "none", Texture::FILTER_NONE },
};
StringMap<Texture::FilterMode, Texture::FILTER_MAX_ENUM> Texture::filterModes(Texture::filterModeEntries, sizeof(Texture::filterModeEntries));
@@ -121,6 +125,7 @@ StringMap<Texture::WrapMode, Texture::WRAP_MAX_ENUM>::Entry Texture::wrapModeEnt
{
{ "clamp", Texture::WRAP_CLAMP },
{ "repeat", Texture::WRAP_REPEAT },
{ "mirroredrepeat", Texture::WRAP_MIRRORED_REPEAT },
};
StringMap<Texture::WrapMode, Texture::WRAP_MAX_ENUM> Texture::wrapModes(Texture::wrapModeEntries, sizeof(Texture::wrapModeEntries));
+13 -10
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2014 LOVE Development Team
* Copyright (c) 2006-2015 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
@@ -44,6 +44,7 @@ public:
{
WRAP_CLAMP,
WRAP_REPEAT,
WRAP_MIRRORED_REPEAT,
WRAP_MAX_ENUM
};
@@ -57,18 +58,16 @@ public:
struct Filter
{
Filter();
FilterMode min;
FilterMode mag;
FilterMode mipmap;
float anisotropy;
FilterMode min = FILTER_LINEAR;
FilterMode mag = FILTER_LINEAR;
FilterMode mipmap = FILTER_NONE;
float anisotropy = 1.0f;
};
struct Wrap
{
Wrap();
WrapMode s;
WrapMode t;
WrapMode s = WRAP_CLAMP;
WrapMode t = WRAP_CLAMP;
};
Texture();
@@ -96,15 +95,19 @@ public:
virtual void setFilter(const Filter &f) = 0;
virtual const Filter &getFilter() const;
virtual void setWrap(const Wrap &w) = 0;
virtual bool setWrap(const Wrap &w) = 0;
virtual const Wrap &getWrap() const;
virtual const Vertex *getVertices() const;
virtual const void *getHandle() const = 0;
// The default filter.
static void setDefaultFilter(const Filter &f);
static const Filter &getDefaultFilter();
static bool validateFilter(const Filter &f, bool mipmapsAllowed);
static bool getConstant(const char *in, FilterMode &out);
static bool getConstant(FilterMode in, const char *&out);
+1 -1
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2014 LOVE Development Team
* Copyright (c) 2006-2015 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
+1 -1
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2014 LOVE Development Team
* Copyright (c) 2006-2015 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
File diff suppressed because it is too large Load Diff
+29 -26
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2014 LOVE Development Team
* Copyright (c) 2006-2015 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
@@ -21,13 +21,15 @@
#ifndef LOVE_GRAPHICS_OPENGL_CANVAS_H
#define LOVE_GRAPHICS_OPENGL_CANVAS_H
#include "common/config.h"
#include "graphics/Color.h"
#include "image/Image.h"
#include "image/ImageData.h"
#include "common/Matrix.h"
#include "common/StringMap.h"
#include "common/int.h"
#include "Texture.h"
#include "graphics/Texture.h"
#include "graphics/Volatile.h"
#include "OpenGL.h"
namespace love
@@ -37,7 +39,7 @@ namespace graphics
namespace opengl
{
class Canvas : public Texture
class Canvas : public Texture, public Volatile
{
public:
@@ -46,15 +48,21 @@ public:
{
FORMAT_NORMAL, // Usually RGBA8 or a similar fallback. Always supported.
FORMAT_HDR, // Usually RGBA16F. Not always supported.
FORMAT_RGBA8, // RGBA with 8 bits per component.
FORMAT_RGBA4, // RGBA with 4 bits per component.
FORMAT_RGB5A1, // RGB with 5 bits per component, and A with 1 bit.
FORMAT_RGBA4, // RGBA with 4 bits per channel.
FORMAT_RGB5A1, // RGB with 5 bits per channel, and A with 1 bit.
FORMAT_RGB565, // RGB with 5, 6, and 5 bits each, respectively.
FORMAT_R8, // Single (red) 8-bit channel.
FORMAT_RG8, // Two-channel (red and green) with 8 bits per channel.
FORMAT_RGBA8, // RGBA with 8 bits per channel.
FORMAT_RGB10A2, // RGB with 10 bits each, and A with 2 bits.
FORMAT_RG11B10F, // Floating point [0, +inf]. RG with 11 FP bits each, and B with 10 FP bits.
FORMAT_RGBA16F, // Floating point [-inf, +inf]. RGBA with 16 FP bits per component.
FORMAT_RGBA32F, // Floating point [-inf, +inf]. RGBA with 32 FP bits per component.
FORMAT_SRGB, // sRGB with 8 bits per component, plus 8 bit linear A.
FORMAT_R16F, // Floating point [-inf, +inf]. R with 16 FP bits.
FORMAT_RG16F, // Floating point [-inf, +inf]. RG with 16 FP bits per channel.
FORMAT_RGBA16F, // Floating point [-inf, +inf]. RGBA with 16 FP bits per channel.
FORMAT_R32F, // Floating point [-inf, +inf]. R with 32 FP bits.
FORMAT_RG32F, // Floating point [-inf, +inf]. RG with 32 FP bits per channel.
FORMAT_RGBA32F, // Floating point [-inf, +inf]. RGBA with 32 FP bits per channel.
FORMAT_SRGB, // sRGB with 8 bits per channel, plus 8 bit linear A.
FORMAT_MAX_ENUM
};
@@ -71,9 +79,8 @@ public:
// Implements Texture.
virtual void drawq(Quad *quad, float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky);
virtual void setFilter(const Texture::Filter &f);
virtual void setWrap(const Texture::Wrap &w);
virtual GLuint getGLTexture() const;
virtual void predraw();
virtual bool setWrap(const Texture::Wrap &w);
virtual const void *getHandle() const;
/**
* @param canvases A list of other canvases to temporarily attach to this one,
@@ -83,16 +90,12 @@ public:
void startGrab();
void stopGrab(bool switchingToOtherCanvas = false);
void clear(Color c);
/**
* Create and attach a stencil buffer to this Canvas' framebuffer, if necessary.
**/
bool checkCreateStencil();
love::image::ImageData *getImageData(love::image::Image *image);
void getPixel(unsigned char* pixel_rgba, int x, int y);
love::image::ImageData *newImageData(love::image::Image *image, int x, int y, int w, int h);
inline const std::vector<Canvas *> &getAttachedCanvases() const
{
@@ -111,13 +114,12 @@ public:
inline int getMSAA() const
{
return msaa_samples;
return actual_samples;
}
bool resolveMSAA();
static bool isSupported();
static bool isMultiCanvasSupported();
static bool isMultiFormatMultiCanvasSupported();
static bool isFormatSupported(Format format);
static Canvas *current;
@@ -128,7 +130,6 @@ public:
// Whether the main screen should have linear -> sRGB conversions enabled.
static bool screenHasSRGB;
static int switchCount;
static int canvasCount;
static bool getConstant(const char *in, Format &out);
@@ -136,7 +137,12 @@ public:
private:
void setupGrab();
bool createMSAAFBO(GLenum internalformat);
bool resolveMSAA(bool restoreprev);
void drawv(const Matrix &t, const Vertex *v);
static Format getSizedFormat(Format format);
static void convertFormat(Format format, GLenum &internalformat, GLenum &externalformat, GLenum &type);
@@ -155,14 +161,11 @@ private:
std::vector<Canvas *> attachedCanvases;
int msaa_samples;
bool msaa_dirty;
int requested_samples;
int actual_samples;
size_t texture_memory;
void setupGrab();
void drawv(const Matrix &t, const Vertex *v);
static bool supportedFormats[FORMAT_MAX_ENUM];
static bool checkedFormats[FORMAT_MAX_ENUM];
+421 -219
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2014 LOVE Development Team
* Copyright (c) 2006-2015 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
@@ -29,6 +29,7 @@
#include <math.h>
#include <sstream>
#include <algorithm> // for max
#include <limits>
namespace love
{
@@ -39,57 +40,44 @@ namespace opengl
int Font::fontCount = 0;
const int Font::TEXTURE_WIDTHS[] = {128, 256, 256, 512, 512, 1024, 1024};
const int Font::TEXTURE_HEIGHTS[] = {128, 128, 256, 256, 512, 512, 1024};
Font::Font(love::font::Rasterizer *r, const Texture::Filter &filter)
: rasterizer(r)
, height(r->getHeight())
, lineHeight(1)
, mSpacing(1)
, textureWidth(128)
, textureHeight(128)
, filter(filter)
, useSpacesAsTab(false)
, quadIndices(20) // We make this bigger at draw-time, if needed.
, textureCacheID(0)
, textureMemorySize(0)
{
this->filter.mipmap = Texture::FILTER_NONE;
// Try to find the best texture size match for the font size. default to the
// largest texture size if no rough match is found.
textureSizeIndex = NUM_TEXTURE_SIZES - 1;
for (int i = 0; i < NUM_TEXTURE_SIZES; i++)
while (true)
{
// Make a rough estimate of the total used texture size, based on glyph
// height. The estimated size is likely larger than the actual total
// size, which is good because texture switching is expensive.
if ((height * 0.8) * height * 95 <= TEXTURE_WIDTHS[i] * TEXTURE_HEIGHTS[i])
{
textureSizeIndex = i;
if ((height * 0.8) * height * 30 <= textureWidth * textureHeight)
break;
}
TextureSize nextsize = getNextTextureSize();
if (nextsize.width <= textureWidth && nextsize.height <= textureHeight)
break;
textureWidth = nextsize.width;
textureHeight = nextsize.height;
}
textureWidth = TEXTURE_WIDTHS[textureSizeIndex];
textureHeight = TEXTURE_HEIGHTS[textureSizeIndex];
love::font::GlyphData *gd = r->getGlyphData(32); // Space character.
type = (gd->getFormat() == font::GlyphData::FORMAT_LUMINANCE_ALPHA) ? FONT_TRUETYPE : FONT_IMAGE;
gd->release();
love::font::GlyphData *gd = nullptr;
if (!r->hasGlyph(9)) // No tab character in the Rasterizer.
useSpacesAsTab = true;
try
{
gd = r->getGlyphData(32); // Space character.
type = (gd->getFormat() == love::font::GlyphData::FORMAT_LUMINANCE_ALPHA) ? FONT_TRUETYPE : FONT_IMAGE;
if (!r->hasGlyph(9)) // No tab character in the Rasterizer.
useSpacesAsTab = true;
loadVolatile();
}
catch (love::Exception &)
{
delete gd;
throw;
}
delete gd;
loadVolatile();
++fontCount;
}
@@ -101,109 +89,137 @@ Font::~Font()
--fontCount;
}
bool Font::initializeTexture(GLenum format)
Font::TextureSize Font::getNextTextureSize() const
{
GLint internalformat = (format == GL_LUMINANCE_ALPHA) ? GL_LUMINANCE8_ALPHA8 : GL_RGBA8;
TextureSize size = {textureWidth, textureHeight};
// clear errors before initializing
while (glGetError() != GL_NO_ERROR);
int maxsize = std::min(4096, gl.getMaxTextureSize());
glTexImage2D(GL_TEXTURE_2D,
0,
internalformat,
(GLsizei)textureWidth,
(GLsizei)textureHeight,
0,
format,
GL_UNSIGNED_BYTE,
NULL);
if (size.width * 2 <= maxsize || size.height * 2 <= maxsize)
{
// {128, 128} -> {256, 128} -> {256, 256} -> {512, 256} -> etc.
if (size.width == size.height)
size.width *= 2;
else
size.height *= 2;
}
return glGetError() == GL_NO_ERROR;
return size;
}
void Font::createTexture()
{
textureX = textureY = rowHeight = TEXTURE_PADDING;
OpenGL::TempDebugGroup debuggroup("Font create texture");
GLuint t;
glGenTextures(1, &t);
textures.push_back(t);
GLenum format = type == FONT_TRUETYPE ? GL_LUMINANCE_ALPHA : GL_RGBA;
size_t bpp = format == GL_LUMINANCE_ALPHA ? 2 : 4;
size_t prevmemsize = textureMemorySize;
if (prevmemsize > 0)
{
textureMemorySize -= (textureWidth * textureHeight * bpp);
gl.updateTextureMemorySize(prevmemsize, textureMemorySize);
}
GLuint t = 0;
TextureSize size = {textureWidth, textureHeight};
TextureSize nextsize = getNextTextureSize();
bool recreatetexture = false;
// If we have an existing texture already, we'll try replacing it with a
// larger-sized one rather than creating a second one. Having a single
// texture reduces texture switches and draw calls when rendering.
if ((nextsize.width > size.width || nextsize.height > size.height)
&& !textures.empty())
{
recreatetexture = true;
size = nextsize;
t = textures.back();
}
else
glGenTextures(1, &t);
gl.bindTexture(t);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
gl.setTextureFilter(filter);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
GLenum format = (type == FONT_TRUETYPE ? GL_LUMINANCE_ALPHA : GL_RGBA);
GLenum internalformat = type == FONT_TRUETYPE ? GL_LUMINANCE8_ALPHA8 : GL_RGBA8;
// Initialize the texture, attempting smaller sizes if initialization fails.
bool initialized = false;
while (textureSizeIndex >= 0)
// in GLES2, the internalformat and format params of TexImage have to match.
if (GLAD_ES_VERSION_2_0 && !GLAD_ES_VERSION_3_0)
internalformat = format;
// Initialize the texture with transparent black.
std::vector<GLubyte> emptydata(size.width * size.height * bpp, 0);
// Clear errors before initializing.
while (glGetError() != GL_NO_ERROR);
glTexImage2D(GL_TEXTURE_2D, 0, internalformat, size.width, size.height, 0,
format, GL_UNSIGNED_BYTE, &emptydata[0]);
if (glGetError() != GL_NO_ERROR)
{
textureWidth = TEXTURE_WIDTHS[textureSizeIndex];
textureHeight = TEXTURE_HEIGHTS[textureSizeIndex];
initialized = initializeTexture(format);
if (initialized || textureSizeIndex <= 0)
break;
--textureSizeIndex;
}
if (!initialized)
{
// Clean up before throwing.
gl.deleteTexture(t);
gl.bindTexture(0);
textures.pop_back();
if (!recreatetexture)
gl.deleteTexture(t);
throw love::Exception("Could not create font texture!");
}
// Fill the texture with transparent black.
std::vector<GLubyte> emptyData(textureWidth * textureHeight * (type == FONT_TRUETYPE ? 2 : 4), 0);
glTexSubImage2D(GL_TEXTURE_2D,
0,
0, 0,
(GLsizei)textureWidth,
(GLsizei)textureHeight,
format,
GL_UNSIGNED_BYTE,
&emptyData[0]);
textureWidth = size.width;
textureHeight = size.height;
setFilter(filter);
rowHeight = textureX = textureY = TEXTURE_PADDING;
size_t prevmemsize = textureMemorySize;
textureMemorySize += emptyData.size();
prevmemsize = textureMemorySize;
textureMemorySize += emptydata.size();
gl.updateTextureMemorySize(prevmemsize, textureMemorySize);
// Re-add the old glyphs if we re-created the existing texture object.
if (recreatetexture)
{
textureCacheID++;
std::vector<uint32> glyphstoadd;
for (const auto &glyphpair : glyphs)
glyphstoadd.push_back(glyphpair.first);
glyphs.clear();
for (uint32 g : glyphstoadd)
addGlyph(g);
}
else
textures.push_back(t);
}
Font::Glyph *Font::addGlyph(uint32 glyph)
love::font::GlyphData *Font::getRasterizerGlyphData(uint32 glyph)
{
love::font::GlyphData *gd = nullptr;
// Use spaces for the tab 'glyph'.
if (glyph == 9 && useSpacesAsTab)
{
love::font::GlyphData *spacegd = rasterizer->getGlyphData(32);
love::font::GlyphData::Format fmt = spacegd->getFormat();
love::font::GlyphMetrics gm = {};
gm.advance = spacegd->getAdvance() * SPACES_PER_TAB;
gm.bearingX = spacegd->getBearingX();
gm.bearingY = spacegd->getBearingY();
love::font::GlyphData::Format f = spacegd->getFormat();
spacegd->release();
gd = new love::font::GlyphData(glyph, gm, f);
return new love::font::GlyphData(glyph, gm, fmt);
}
else
gd = rasterizer->getGlyphData(glyph);
return rasterizer->getGlyphData(glyph);
}
const Font::Glyph &Font::addGlyph(uint32 glyph)
{
love::font::GlyphData *gd = getRasterizerGlyphData(glyph);
int w = gd->getWidth();
int h = gd->getHeight();
@@ -229,89 +245,91 @@ Font::Glyph *Font::addGlyph(uint32 glyph)
}
}
Glyph *g = new Glyph;
Glyph g;
g->texture = 0;
g->spacing = gd->getAdvance();
g.texture = 0;
g.spacing = gd->getAdvance();
memset(g->vertices, 0, sizeof(GlyphVertex) * 4);
memset(g.vertices, 0, sizeof(GlyphVertex) * 4);
// don't waste space for empty glyphs. also fixes a division by zero bug with ati drivers
// don't waste space for empty glyphs. also fixes a divide by zero bug with ATI drivers
if (w > 0 && h > 0)
{
const GLuint t = textures.back();
gl.bindTexture(t);
glTexSubImage2D(GL_TEXTURE_2D,
0,
textureX,
textureY,
w, h,
(type == FONT_TRUETYPE ? GL_LUMINANCE_ALPHA : GL_RGBA),
GL_UNSIGNED_BYTE,
gd->getData());
glTexSubImage2D(GL_TEXTURE_2D, 0, textureX, textureY, w, h,
(type == FONT_TRUETYPE ? GL_LUMINANCE_ALPHA : GL_RGBA),
GL_UNSIGNED_BYTE, gd->getData());
g->texture = t;
g.texture = t;
float tX = (float) textureX, tY = (float) textureY;
float tWidth = (float) textureWidth, tHeight = (float) textureHeight;
// 0----2
// | / |
// | / |
// 1----3
const GlyphVertex verts[4] = {
{ 0.0f, 0.0f, float(textureX)/float(textureWidth), float(textureY)/float(textureHeight)},
{ 0.0f, float(h), float(textureX)/float(textureWidth), float(textureY+h)/float(textureHeight)},
{float(w), float(h), float(textureX+w)/float(textureWidth), float(textureY+h)/float(textureHeight)},
{float(w), 0.0f, float(textureX+w)/float(textureWidth), float(textureY)/float(textureHeight)},
{ 0.0f, 0.0f, tX/tWidth, tY/tHeight},
{ 0.0f, float(h), tX/tWidth, (tY+h)/tHeight},
{float(w), 0.0f, (tX+w)/tWidth, tY/tHeight},
{float(w), float(h), (tX+w)/tWidth, (tY+h)/tHeight}
};
// copy vertex data to the glyph and set proper bearing
// Copy vertex data to the glyph and set proper bearing.
for (int i = 0; i < 4; i++)
{
g->vertices[i] = verts[i];
g->vertices[i].x += gd->getBearingX();
g->vertices[i].y -= gd->getBearingY();
g.vertices[i] = verts[i];
g.vertices[i].x += gd->getBearingX();
g.vertices[i].y -= gd->getBearingY();
}
}
if (w > 0)
textureX += (w + TEXTURE_PADDING);
if (h > 0)
rowHeight = std::max(rowHeight, h + TEXTURE_PADDING);
delete gd;
gd->release();
glyphs[glyph] = g;
const auto p = glyphs.insert(std::make_pair(glyph, g));
return g;
return p.first->second;
}
Font::Glyph *Font::findGlyph(uint32 glyph)
const Font::Glyph &Font::findGlyph(uint32 glyph)
{
auto it = glyphs.find(glyph);
const auto it = glyphs.find(glyph);
if (it != glyphs.end())
return it->second;
else
return addGlyph(glyph);
return addGlyph(glyph);
}
float Font::getHeight() const
{
return static_cast<float>(height);
return (float) height;
}
void Font::print(const std::string &text, float x, float y, float extra_spacing, float angle, float sx, float sy, float ox, float oy, float kx, float ky)
std::vector<Font::DrawCommand> Font::generateVertices(const std::string &text, std::vector<GlyphVertex> &vertices, float extra_spacing, Vector offset, TextInfo *info)
{
// Spacing counter and newline handling.
float dx = 0.0f;
float dy = 0.0f;
float dx = offset.x;
float dy = offset.y;
float lineheight = getBaseline();
int maxwidth = 0;
// Keeps track of when we need to switch textures in our vertex array.
std::vector<GlyphArrayDrawInfo> glyphinfolist;
std::vector<DrawCommand> drawcommands;
// Pre-allocate space for the maximum possible number of vertices.
std::vector<GlyphVertex> glyphverts;
glyphverts.reserve(text.length() * 4);
int vertexcount = 0;
size_t vertstartsize = vertices.size();
vertices.reserve(vertstartsize + text.length() * 4);
try
{
@@ -324,41 +342,57 @@ void Font::print(const std::string &text, float x, float y, float extra_spacing,
if (g == '\n')
{
if (dx > maxwidth)
maxwidth = (int) dx;
// Wrap newline, but do not print it.
dy += floorf(getHeight() * getLineHeight() + 0.5f);
dx = 0.0f;
dx = offset.x;
continue;
}
Glyph *glyph = findGlyph(g);
uint32 cacheid = textureCacheID;
if (glyph->texture != 0)
const Glyph &glyph = findGlyph(g);
// If findGlyph invalidates the texture cache, re-start the loop.
if (cacheid != textureCacheID)
{
i = utf8::iterator<std::string::const_iterator>(text.begin(), text.begin(), text.end());
maxwidth = 0;
dx = offset.x;
dy = offset.y;
drawcommands.clear();
vertices.resize(vertstartsize);
continue;
}
if (glyph.texture != 0)
{
// Copy the vertices and set their proper relative positions.
for (int j = 0; j < 4; j++)
{
glyphverts.push_back(glyph->vertices[j]);
glyphverts.back().x += dx;
glyphverts.back().y += dy + lineheight;
vertices.push_back(glyph.vertices[j]);
vertices.back().x += dx;
vertices.back().y += dy + lineheight;
}
// Check if glyph texture has changed since the last iteration.
if (glyphinfolist.size() == 0 || glyphinfolist.back().texture != glyph->texture)
if (drawcommands.empty() || drawcommands.back().texture != glyph.texture)
{
// keep track of each sub-section of the string whose glyphs use different textures than the previous section
GlyphArrayDrawInfo gdrawinfo;
gdrawinfo.startvertex = vertexcount;
gdrawinfo.vertexcount = 0;
gdrawinfo.texture = glyph->texture;
glyphinfolist.push_back(gdrawinfo);
// Add a new draw command if the texture has changed.
DrawCommand cmd;
cmd.startvertex = (int) vertices.size() - 4;
cmd.vertexcount = 0;
cmd.texture = glyph.texture;
drawcommands.push_back(cmd);
}
vertexcount += 4;
glyphinfolist.back().vertexcount += 4;
drawcommands.back().vertexcount += 4;
}
// Advance the x position for the next glyph.
dx += glyph->spacing;
dx += glyph.spacing;
// Account for extra spacing given to space characters.
if (g == ' ' && extra_spacing != 0.0f)
@@ -370,38 +404,201 @@ void Font::print(const std::string &text, float x, float y, float extra_spacing,
throw love::Exception("UTF-8 decoding error: %s", e.what());
}
if (vertexcount <= 0 || glyphinfolist.size() == 0)
// Sort draw commands by texture first, and quad position in memory second
// (using the struct's < operator).
std::sort(drawcommands.begin(), drawcommands.end());
if (dx > maxwidth)
maxwidth = (int) dx;
if (info != nullptr)
{
info->width = maxwidth - offset.x;;
info->height = (int) dy + (dx > 0.0f ? floorf(getHeight() * getLineHeight() + 0.5f) : 0) - offset.y;
}
return drawcommands;
}
std::vector<Font::DrawCommand> Font::generateVerticesFormatted(const std::string &text, float wrap, AlignMode align, std::vector<GlyphVertex> &vertices, TextInfo *info)
{
if (wrap < 0.0f)
wrap = std::numeric_limits<float>::max();
uint32 cacheid = textureCacheID;
std::vector<DrawCommand> drawcommands;
vertices.reserve(text.length() * 4);
// wrappedlines indicates which lines were automatically wrapped. It
// has the same number of elements as lines_to_draw.
std::vector<bool> wrappedlines;
std::vector<int> widths;
std::vector<std::string> lines;
// We only need the list of wrapped lines in 'justify' mode.
getWrap(text, wrap, lines, &widths, align == ALIGN_JUSTIFY ? &wrappedlines : nullptr);
float extraspacing = 0.0f;
int numspaces = 0;
int i = 0;
float y = 0.0f;
float maxwidth = 0;
for (const std::string &line : lines)
{
extraspacing = 0.0f;
float width = (float) widths[i];
love::Vector offset(0.0f, floorf(y));
if (width > maxwidth)
maxwidth = width;
switch (align)
{
case ALIGN_RIGHT:
offset.x = floorf(wrap - width);
break;
case ALIGN_CENTER:
offset.x = floorf((wrap - width) / 2.0f);
break;
case ALIGN_JUSTIFY:
numspaces = (int) std::count(line.begin(), line.end(), ' ');
if (wrappedlines[i] && numspaces >= 1)
extraspacing = (wrap - width) / float(numspaces);
else
extraspacing = 0.0f;
break;
case ALIGN_LEFT:
default:
break;
}
std::vector<DrawCommand> commands = generateVertices(line, vertices, extraspacing, offset);
if (!commands.empty())
{
auto firstcmd = commands.begin();
// If the first draw command in the new list has the same texture
// as the last one in the existing list we're building and its
// vertices are in-order, we can combine them (saving a draw call.)
if (!drawcommands.empty())
{
auto prevcmd = drawcommands.back();
if (prevcmd.texture == firstcmd->texture && (prevcmd.startvertex + prevcmd.vertexcount) == firstcmd->startvertex)
{
drawcommands.back().vertexcount += firstcmd->vertexcount;
++firstcmd;
}
}
// Append the new draw commands to the list we're building.
drawcommands.insert(drawcommands.end(), firstcmd, commands.end());
}
y += getHeight() * getLineHeight();
i++;
}
if (info != nullptr)
{
info->width = (int) maxwidth;
info->height = (int) y;
}
if (cacheid != textureCacheID)
{
vertices.clear();
drawcommands = generateVerticesFormatted(text, wrap, align, vertices);
}
return drawcommands;
}
void Font::drawVertices(const std::vector<DrawCommand> &drawcommands)
{
// Vertex attribute pointers need to be set before calling this function.
// This assumes that the attribute pointers are constant for all vertices.
int totalverts = 0;
for (const DrawCommand &cmd : drawcommands)
totalverts = std::max(cmd.startvertex + cmd.vertexcount, totalverts);
if ((size_t) totalverts / 4 > quadIndices.getSize())
quadIndices = VertexIndex((size_t) totalverts / 4);
gl.prepareDraw();
const GLenum gltype = quadIndices.getType();
const size_t elemsize = quadIndices.getElementSize();
GLBuffer::Bind bind(*quadIndices.getBuffer());
// We need a separate draw call for every section of the text which uses a
// different texture than the previous section.
for (const DrawCommand &cmd : drawcommands)
{
GLsizei count = (cmd.vertexcount / 4) * 6;
size_t offset = (cmd.startvertex / 4) * 6 * elemsize;
// TODO: Use glDrawElementsBaseVertex when supported?
gl.bindTexture(cmd.texture);
gl.drawElements(GL_TRIANGLES, count, gltype, quadIndices.getPointer(offset));
}
}
void Font::printv(const Matrix &t, const std::vector<DrawCommand> &drawcommands, const std::vector<GlyphVertex> &vertices)
{
if (vertices.empty() || drawcommands.empty())
return;
// Sort glyph draw info list by texture first, and quad position in memory
// second (using the struct's < operator).
std::sort(glyphinfolist.begin(), glyphinfolist.end());
Matrix t;
t.setTransformation(ceilf(x), ceilf(y), angle, sx, sy, ox, oy, kx, ky);
OpenGL::TempDebugGroup debuggroup("Font print");
OpenGL::TempTransform transform(gl);
transform.get() *= t;
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glEnableVertexAttribArray(ATTRIB_POS);
glEnableVertexAttribArray(ATTRIB_TEXCOORD);
glVertexPointer(2, GL_FLOAT, sizeof(GlyphVertex), (GLvoid *)&glyphverts[0].x);
glTexCoordPointer(2, GL_FLOAT, sizeof(GlyphVertex), (GLvoid *)&glyphverts[0].s);
glVertexAttribPointer(ATTRIB_POS, 2, GL_FLOAT, GL_FALSE, sizeof(GlyphVertex), &vertices[0].x);
glVertexAttribPointer(ATTRIB_TEXCOORD, 2, GL_FLOAT, GL_FALSE, sizeof(GlyphVertex), &vertices[0].s);
gl.prepareDraw();
// We need to draw a new vertex array for every section of the string which
// uses a different texture than the previous section.
std::vector<GlyphArrayDrawInfo>::const_iterator it;
for (it = glyphinfolist.begin(); it != glyphinfolist.end(); ++it)
try
{
gl.bindTexture(it->texture);
gl.drawArrays(GL_QUADS, it->startvertex, it->vertexcount);
drawVertices(drawcommands);
}
catch (love::Exception &)
{
glDisableVertexAttribArray(ATTRIB_TEXCOORD);
glDisableVertexAttribArray(ATTRIB_POS);
throw;
}
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);
glDisableVertexAttribArray(ATTRIB_TEXCOORD);
glDisableVertexAttribArray(ATTRIB_POS);
}
void Font::print(const std::string &text, float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky)
{
std::vector<GlyphVertex> vertices;
std::vector<DrawCommand> drawcommands = generateVertices(text, vertices);
Matrix t;
t.setTransformation(ceilf(x), ceilf(y), angle, sx, sy, ox, oy, kx, ky);
printv(t, drawcommands, vertices);
}
void Font::printf(const std::string &text, float x, float y, float wrap, AlignMode align, float angle, float sx, float sy, float ox, float oy, float kx, float ky)
{
std::vector<GlyphVertex> vertices;
std::vector<DrawCommand> drawcommands = generateVerticesFormatted(text, wrap, align, vertices);
Matrix t;
t.setTransformation(ceilf(x), ceilf(y), angle, sx, sy, ox, oy, kx, ky);
printv(t, drawcommands, vertices);
}
int Font::getWidth(const std::string &str)
@@ -410,7 +607,6 @@ int Font::getWidth(const std::string &str)
std::istringstream iss(str);
std::string line;
Glyph *g;
int max_width = 0;
while (getline(iss, line, '\n'))
@@ -423,8 +619,8 @@ int Font::getWidth(const std::string &str)
while (i != end)
{
uint32 c = *i++;
g = findGlyph(c);
width += static_cast<int>(g->spacing * mSpacing);
const Glyph &g = findGlyph(c);
width += g.spacing;
}
}
catch(utf8::exception &e)
@@ -441,16 +637,14 @@ int Font::getWidth(const std::string &str)
int Font::getWidth(char character)
{
Glyph *g = findGlyph(character);
return g->spacing;
const Glyph &g = findGlyph(character);
return g.spacing;
}
std::vector<std::string> Font::getWrap(const std::string &text, float wrap, int *max_width, std::vector<bool> *wrappedlines)
void Font::getWrap(const std::string &text, float wrap, std::vector<std::string> &lines, std::vector<int> *linewidths, std::vector<bool> *wrappedlines)
{
using namespace std;
const float width_space = static_cast<float>(getWidth(' '));
vector<string> lines_to_draw;
int maxw = 0;
const float width_space = (float) getWidth(' ');
//split text at newlines
istringstream iss(text);
@@ -481,12 +675,13 @@ std::vector<std::string> Font::getWrap(const std::string &text, float wrap, int
// remove trailing space
string tmp = string_builder.str();
lines_to_draw.push_back(tmp.substr(0,tmp.size()-1));
lines.push_back(tmp.substr(0,tmp.size()-1));
string_builder.str("");
width = static_cast<float>(getWidth(word));
realw -= (int) width;
if (realw > maxw)
maxw = realw;
if (linewidths)
linewidths->push_back(realw);
// Indicate that this line was automatically wrapped.
if (wrappedlines)
@@ -497,25 +692,21 @@ std::vector<std::string> Font::getWrap(const std::string &text, float wrap, int
oldwidth = width;
}
// push last line
if (width > maxw)
maxw = (int) width;
if (linewidths)
linewidths->push_back(width);
string tmp = string_builder.str();
lines_to_draw.push_back(tmp.substr(0,tmp.size()-1));
lines.push_back(tmp.substr(0,tmp.size()-1));
// Indicate that this line was not automatically wrapped.
if (wrappedlines)
wrappedlines->push_back(false);
}
if (max_width)
*max_width = maxw;
return lines_to_draw;
}
void Font::setLineHeight(float height)
{
this->lineHeight = height;
lineHeight = height;
}
float Font::getLineHeight() const
@@ -523,23 +714,16 @@ float Font::getLineHeight() const
return lineHeight;
}
void Font::setSpacing(float amount)
{
mSpacing = amount;
}
float Font::getSpacing() const
{
return mSpacing;
}
void Font::setFilter(const Texture::Filter &f)
{
if (!Texture::validateFilter(f, false))
throw love::Exception("Invalid texture filter.");
filter = f;
for (auto it = textures.begin(); it != textures.end(); ++it)
for (GLuint texture : textures)
{
gl.bindTexture(*it);
gl.bindTexture(texture);
gl.setTextureFilter(filter);
}
}
@@ -552,26 +736,19 @@ const Texture::Filter &Font::getFilter()
bool Font::loadVolatile()
{
createTexture();
textureCacheID++;
return true;
}
void Font::unloadVolatile()
{
// nuke everything from orbit
std::map<uint32, Glyph *>::iterator it = glyphs.begin();
Glyph *g;
while (it != glyphs.end())
{
g = it->second;
delete g;
glyphs.erase(it++);
}
std::vector<GLuint>::iterator iter = textures.begin();
while (iter != textures.end())
{
gl.deleteTexture(*iter);
iter++;
}
glyphs.clear();
for (GLuint texture : textures)
gl.deleteTexture(texture);
textures.clear();
gl.updateTextureMemorySize(textureMemorySize, 0);
@@ -604,6 +781,31 @@ bool Font::hasGlyphs(const std::string &text) const
return rasterizer->hasGlyphs(text);
}
uint32 Font::getTextureCacheID() const
{
return textureCacheID;
}
bool Font::getConstant(const char *in, AlignMode &out)
{
return alignModes.find(in, out);
}
bool Font::getConstant(AlignMode in, const char *&out)
{
return alignModes.find(in, out);
}
StringMap<Font::AlignMode, Font::ALIGN_MAX_ENUM>::Entry Font::alignModeEntries[] =
{
{ "left", Font::ALIGN_LEFT },
{ "right", Font::ALIGN_RIGHT },
{ "center", Font::ALIGN_CENTER },
{ "justify", Font::ALIGN_JUSTIFY },
};
StringMap<Font::AlignMode, Font::ALIGN_MAX_ENUM> Font::alignModes(Font::alignModeEntries, sizeof(Font::alignModeEntries));
} // opengl
} // graphics
} // love
+79 -50
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2014 LOVE Development Team
* Copyright (c) 2006-2015 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
@@ -22,15 +22,20 @@
#define LOVE_GRAPHICS_OPENGL_FONT_H
// STD
#include <map>
#include <unordered_map>
#include <string>
#include <vector>
// LOVE
#include "common/config.h"
#include "common/Object.h"
#include "common/Matrix.h"
#include "common/Vector.h"
#include "font/Rasterizer.h"
#include "graphics/Texture.h"
#include "graphics/Volatile.h"
#include "GLBuffer.h"
#include "OpenGL.h"
@@ -45,17 +50,60 @@ class Font : public Object, public Volatile
{
public:
enum AlignMode
{
ALIGN_LEFT,
ALIGN_CENTER,
ALIGN_RIGHT,
ALIGN_JUSTIFY,
ALIGN_MAX_ENUM
};
struct GlyphVertex
{
float x, y;
float s, t;
};
struct TextInfo
{
int width;
int height;
};
// Used to determine when to change textures in the generated vertex array.
struct DrawCommand
{
GLuint texture;
int startvertex;
int vertexcount;
// used when sorting with std::sort.
bool operator < (const DrawCommand &other) const
{
// Texture binds are expensive, so we should sort by that first.
if (texture != other.texture)
return texture < other.texture;
else
return startvertex < other.startvertex;
}
};
Font(love::font::Rasterizer *r, const Texture::Filter &filter = Texture::getDefaultFilter());
virtual ~Font();
std::vector<DrawCommand> generateVertices(const std::string &text, std::vector<GlyphVertex> &vertices, float extra_spacing = 0.0f, Vector offset = Vector(), TextInfo *info = nullptr);
std::vector<DrawCommand> generateVerticesFormatted(const std::string &text, float wrap, AlignMode align, std::vector<GlyphVertex> &vertices, TextInfo *info = nullptr);
void drawVertices(const std::vector<DrawCommand> &drawcommands);
/**
* Prints the text at the designated position with rotation and scaling.
*
* @param text A string.
* @param x The x-coordinate.
* @param y The y-coordinate.
* @param extra_spacing Additional spacing added to spaces (" ").
* @param angle The amount of rotation.
* @param sx Scale along the x axis.
* @param sy Scale along the y axis.
@@ -64,7 +112,9 @@ public:
* @param kx Shear along the x axis.
* @param ky Shear along the y axis.
**/
void print(const std::string &text, float x, float y, float extra_spacing = 0.0f, float angle = 0.0f, float sx = 1.0f, float sy = 1.0f, float ox = 0.0f, float oy = 0.0f, float kx = 0.0f, float ky = 0.0f);
void print(const std::string &text, float x, float y, float angle = 0.0f, float sx = 1.0f, float sy = 1.0f, float ox = 0.0f, float oy = 0.0f, float kx = 0.0f, float ky = 0.0f);
void printf(const std::string &text, float x, float y, float wrap, AlignMode align, float angle = 0.0f, float sx = 1.0f, float sy = 1.0f, float ox = 0.0f, float oy = 0.0f, float kx = 0.0f, float ky = 0.0f);
/**
* Returns the height of the font.
@@ -96,7 +146,7 @@ public:
* auto-wrapped. Indices correspond to indices of the returned value.
* Returns a vector with the lines.
**/
std::vector<std::string> getWrap(const std::string &text, float wrap, int *max_width = 0, std::vector<bool> *wrapped_lines = 0);
void getWrap(const std::string &text, float wrap, std::vector<std::string> &lines, std::vector<int> *line_widths = 0, std::vector<bool> *wrapped_lines = 0);
/**
* Sets the line height (which should be a number to multiply the font size by,
@@ -110,19 +160,6 @@ public:
**/
float getLineHeight() const;
/**
* Sets the spacing modifier (changes the spacing between the characters the
* same way that the line height does [multiplication]).
* Note: The spacing must be set BEFORE the font is loaded to have any effect.
* @param amount The amount of modification.
**/
void setSpacing(float amount);
/**
* Returns the spacing modifier.
**/
float getSpacing() const;
void setFilter(const Texture::Filter &f);
const Texture::Filter &getFilter();
@@ -138,6 +175,11 @@ public:
bool hasGlyph(uint32 glyph) const;
bool hasGlyphs(const std::string &text) const;
uint32 getTextureCacheID() const;
static bool getConstant(const char *in, AlignMode &out);
static bool getConstant(AlignMode in, const char *&out);
static int fontCount;
private:
@@ -149,12 +191,6 @@ private:
FONT_UNKNOWN
};
struct GlyphVertex
{
float x, y;
float s, t;
};
struct Glyph
{
GLuint texture;
@@ -162,36 +198,24 @@ private:
GlyphVertex vertices[4];
};
// used to determine when to change textures in the vertex array generated when printing text
struct GlyphArrayDrawInfo
struct TextureSize
{
GLuint texture;
int startvertex;
int vertexcount;
// used when sorting with std::sort
// sorts by texture first (binding textures is expensive) and relative position in memory second
bool operator < (const GlyphArrayDrawInfo &other) const
{
if (texture != other.texture)
return texture < other.texture;
else
return startvertex < other.startvertex;
}
int width;
int height;
};
bool initializeTexture(GLenum format);
TextureSize getNextTextureSize() const;
void createTexture();
Glyph *addGlyph(uint32 glyph);
Glyph *findGlyph(uint32 glyph);
love::font::GlyphData *getRasterizerGlyphData(uint32 glyph);
const Glyph &addGlyph(uint32 glyph);
const Glyph &findGlyph(uint32 glyph);
void printv(const Matrix &t, const std::vector<DrawCommand> &drawcommands, const std::vector<GlyphVertex> &vertices);
Object::StrongRef<love::font::Rasterizer> rasterizer;
StrongRef<love::font::Rasterizer> rasterizer;
int height;
float lineHeight;
float mSpacing; // modifies the spacing by multiplying it with this value
int textureSizeIndex;
int textureWidth;
int textureHeight;
@@ -199,7 +223,7 @@ private:
std::vector<GLuint> textures;
// maps glyphs to glyph texture information
std::map<uint32, Glyph *> glyphs;
std::unordered_map<uint32, Glyph> glyphs;
FontType type;
Texture::Filter filter;
@@ -209,17 +233,22 @@ private:
bool useSpacesAsTab;
size_t textureMemorySize;
// Index buffer used for drawing quads with GL_TRIANGLES.
VertexIndex quadIndices;
static const int NUM_TEXTURE_SIZES = 7;
static const int TEXTURE_WIDTHS[NUM_TEXTURE_SIZES];
static const int TEXTURE_HEIGHTS[NUM_TEXTURE_SIZES];
// ID which is incremented when the texture cache is invalidated.
uint32 textureCacheID;
size_t textureMemorySize;
static const int TEXTURE_PADDING = 1;
// This will be used if the Rasterizer doesn't have a tab character itself.
static const int SPACES_PER_TAB = 4;
static StringMap<AlignMode, ALIGN_MAX_ENUM>::Entry alignModeEntries[];
static StringMap<AlignMode, ALIGN_MAX_ENUM> alignModes;
}; // Font
} // opengl
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2014 LOVE Development Team
* Copyright (c) 2006-2015 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
@@ -18,7 +18,7 @@
* 3. This notice may not be removed or altered from any source distribution.
**/
#include "VertexBuffer.h"
#include "GLBuffer.h"
#include "common/Exception.h"
@@ -27,11 +27,6 @@
#include <algorithm>
#include <limits>
// Conflicts with std::numeric_limits<GLushort>::max() (Windows).
#ifdef max
# undef max
#endif
namespace love
{
namespace graphics
@@ -39,150 +34,73 @@ namespace graphics
namespace opengl
{
// VertexBuffer
// GLBuffer
VertexBuffer *VertexBuffer::Create(size_t size, GLenum target, GLenum usage, MemoryBacking backing)
GLBuffer *GLBuffer::Create(size_t size, GLenum target, GLenum usage)
{
try
{
// Try to create a VBO.
return new VBO(size, target, usage, backing);
}
catch(const love::Exception &)
{
// VBO not supported ... create regular array.
return new VertexArray(size, target, usage, backing);
}
return new GLBuffer(size, target, usage);
}
VertexBuffer::VertexBuffer(size_t size, GLenum target, GLenum usage, MemoryBacking backing)
GLBuffer::GLBuffer(size_t size, GLenum target, GLenum usage)
: is_bound(false)
, is_mapped(false)
, size(size)
, target(target)
, usage(usage)
, backing(backing)
{
}
VertexBuffer::~VertexBuffer()
{
}
// VertexArray
VertexArray::VertexArray(size_t size, GLenum target, GLenum usage, MemoryBacking backing)
: VertexBuffer(size, target, usage, backing)
, buf(new char[size])
{
}
VertexArray::~VertexArray()
{
delete [] buf;
}
void *VertexArray::map()
{
is_mapped = true;
return buf;
}
void VertexArray::unmap(size_t /*usedOffset*/, size_t /*usedSize*/)
{
is_mapped = false;
}
void VertexArray::bind()
{
is_bound = true;
}
void VertexArray::unbind()
{
is_bound = false;
}
void VertexArray::fill(size_t offset, size_t size, const void *data)
{
memcpy(buf + offset, data, size);
}
const void *VertexArray::getPointer(size_t offset) const
{
return buf + offset;
}
// VBO
VBO::VBO(size_t size, GLenum target, GLenum usage, MemoryBacking backing)
: VertexBuffer(size, target, usage, backing)
, vbo(0)
, memory_map(nullptr)
, is_dirty(false)
{
if (!(GLEE_ARB_vertex_buffer_object || GLEE_VERSION_1_5))
throw love::Exception("Not supported");
if (getMemoryBacking() == BACKING_FULL)
memory_map = (char *) malloc(getSize());
try
{
memory_map = new char[size];
}
catch (std::bad_alloc &)
{
throw love::Exception("Out of memory.");
}
bool ok = load(false);
if (!ok)
{
free(memory_map);
delete[] memory_map;
throw love::Exception("Could not load VBO.");
}
}
VBO::~VBO()
GLBuffer::~GLBuffer()
{
if (vbo != 0)
unload(false);
unload();
if (memory_map)
free(memory_map);
delete[] memory_map;
}
void *VBO::map()
void *GLBuffer::map()
{
if (is_mapped)
return memory_map;
if (!memory_map)
{
memory_map = (char * ) malloc(getSize());
if (!memory_map)
throw love::Exception("Out of memory (oh the humanity!)");
}
if (is_dirty)
{
glGetBufferSubDataARB(getTarget(), 0, (GLsizeiptr) getSize(), memory_map);
is_dirty = false;
}
is_mapped = true;
return memory_map;
}
void VBO::unmapStatic(size_t offset, size_t size)
void GLBuffer::unmapStatic(size_t offset, size_t size)
{
// Upload the mapped data to the buffer.
glBufferSubDataARB(getTarget(), (GLintptr) offset, (GLsizeiptr) size, memory_map + offset);
glBufferSubData(getTarget(), (GLintptr) offset, (GLsizeiptr) size, memory_map + offset);
}
void VBO::unmapStream()
void GLBuffer::unmapStream()
{
// "orphan" current buffer to avoid implicit synchronisation on the GPU:
// http://www.seas.upenn.edu/~pcozzi/OpenGLInsights/OpenGLInsights-AsynchronousBufferTransfers.pdf
glBufferDataARB(getTarget(), (GLsizeiptr) getSize(), nullptr, getUsage());
glBufferDataARB(getTarget(), (GLsizeiptr) getSize(), memory_map, getUsage());
glBufferData(getTarget(), (GLsizeiptr) getSize(), nullptr, getUsage());
glBufferData(getTarget(), (GLsizeiptr) getSize(), memory_map, getUsage());
}
void VBO::unmap(size_t usedOffset, size_t usedSize)
void GLBuffer::unmap(size_t usedOffset, size_t usedSize)
{
if (!is_mapped)
return;
@@ -194,7 +112,7 @@ void VBO::unmap(size_t usedOffset, size_t usedSize)
// bound here.
if (!is_bound)
{
glBindBufferARB(getTarget(), vbo);
glBindBuffer(getTarget(), vbo);
is_bound = true;
}
@@ -220,83 +138,66 @@ void VBO::unmap(size_t usedOffset, size_t usedSize)
is_mapped = false;
}
void VBO::bind()
void GLBuffer::bind()
{
if (!is_mapped)
{
glBindBufferARB(getTarget(), vbo);
glBindBuffer(getTarget(), vbo);
is_bound = true;
}
}
void VBO::unbind()
void GLBuffer::unbind()
{
if (is_bound)
glBindBufferARB(getTarget(), 0);
glBindBuffer(getTarget(), 0);
is_bound = false;
}
void VBO::fill(size_t offset, size_t size, const void *data)
void GLBuffer::fill(size_t offset, size_t size, const void *data)
{
if (is_mapped || getMemoryBacking() == BACKING_FULL)
memcpy(memory_map + offset, data, size);
memcpy(memory_map + offset, data, size);
if (!is_mapped)
{
glBufferSubDataARB(getTarget(), (GLintptr) offset, (GLsizeiptr) size, data);
if (getMemoryBacking() != BACKING_FULL)
is_dirty = true;
}
glBufferSubData(getTarget(), (GLintptr) offset, (GLsizeiptr) size, data);
}
const void *VBO::getPointer(size_t offset) const
const void *GLBuffer::getPointer(size_t offset) const
{
return BUFFER_OFFSET(offset);
}
bool VBO::loadVolatile()
bool GLBuffer::loadVolatile()
{
return load(true);
}
void VBO::unloadVolatile()
void GLBuffer::unloadVolatile()
{
unload(true);
unload();
}
bool VBO::load(bool restore)
bool GLBuffer::load(bool restore)
{
glGenBuffersARB(1, &vbo);
glGenBuffers(1, &vbo);
VertexBuffer::Bind bind(*this);
GLBuffer::Bind bind(*this);
// Copy the old buffer only if 'restore' was requested.
const GLvoid *src = restore ? memory_map : nullptr;
while (GL_NO_ERROR != glGetError())
/* clear error messages */;
// Note that if 'src' is '0', no data will be copied.
glBufferDataARB(getTarget(), (GLsizeiptr) getSize(), src, getUsage());
GLenum err = glGetError();
glBufferData(getTarget(), (GLsizeiptr) getSize(), src, getUsage());
return (GL_NO_ERROR == err);
return true;
}
void VBO::unload(bool save)
void GLBuffer::unload()
{
// Save data before unloading, if we need to.
if (save && getMemoryBacking() == BACKING_PARTIAL)
{
VertexBuffer::Bind bind(*this);
map(); // saves buffer content to memory_map.
}
is_mapped = false;
glDeleteBuffersARB(1, &vbo);
glDeleteBuffers(1, &vbo);
vbo = 0;
}
@@ -306,7 +207,7 @@ void VBO::unload(bool save)
size_t VertexIndex::maxSize = 0;
size_t VertexIndex::elementSize = 0;
std::list<size_t> VertexIndex::sizeRefs;
VertexBuffer *VertexIndex::element_array = NULL;
GLBuffer *VertexIndex::element_array = NULL;
VertexIndex::VertexIndex(size_t size)
: size(size)
@@ -363,7 +264,7 @@ size_t VertexIndex::getElementSize()
return elementSize;
}
VertexBuffer *VertexIndex::getVertexBuffer() const
GLBuffer *VertexIndex::getBuffer() const
{
return element_array;
}
@@ -417,7 +318,7 @@ void VertexIndex::resize(size_t size)
return;
}
VertexBuffer *new_element_array;
GLBuffer *new_element_array;
// Depending on the size, a switch to int and more memory is needed.
GLenum target_type = getType(size);
@@ -426,18 +327,18 @@ void VertexIndex::resize(size_t size)
size_t array_size = elem_size * 6 * size;
// Create may throw out-of-memory exceptions.
// VertexIndex will propagate the exception and keep the old VertexBuffer.
// VertexIndex will propagate the exception and keep the old GLBuffer.
try
{
new_element_array = VertexBuffer::Create(array_size, GL_ELEMENT_ARRAY_BUFFER, GL_STATIC_DRAW);
new_element_array = GLBuffer::Create(array_size, GL_ELEMENT_ARRAY_BUFFER, GL_STATIC_DRAW);
}
catch (std::bad_alloc &)
{
throw love::Exception("Out of memory.");
}
// Allocation of the new VertexBuffer succeeded.
// The old VertexBuffer can now be deleted.
// Allocation of the new GLBuffer succeeded.
// The old GLBuffer can now be deleted.
delete element_array;
element_array = new_element_array;
maxSize = size;
@@ -457,20 +358,24 @@ void VertexIndex::resize(size_t size)
template <typename T>
void VertexIndex::fill()
{
VertexBuffer::Bind bind(*element_array);
VertexBuffer::Mapper mapper(*element_array);
GLBuffer::Bind bind(*element_array);
GLBuffer::Mapper mapper(*element_array);
T *indices = (T *) mapper.get();
// 0----2
// | / |
// | / |
// 1----3
for (size_t i = 0; i < maxSize; ++i)
{
indices[i*6+0] = i * 4 + 0;
indices[i*6+1] = i * 4 + 1;
indices[i*6+2] = i * 4 + 2;
indices[i*6+0] = T(i * 4 + 0);
indices[i*6+1] = T(i * 4 + 1);
indices[i*6+2] = T(i * 4 + 2);
indices[i*6+3] = i * 4 + 0;
indices[i*6+4] = i * 4 + 2;
indices[i*6+5] = i * 4 + 3;
indices[i*6+3] = T(i * 4 + 2);
indices[i*6+4] = T(i * 4 + 1);
indices[i*6+5] = T(i * 4 + 3);
}
}
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2014 LOVE Development Team
* Copyright (c) 2006-2015 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
@@ -19,8 +19,8 @@
**/
#ifndef LOVE_GRAPHICS_OPENGL_VERTEX_BUFFER_H
#define LOVE_GRAPHICS_OPENGL_VERTEX_BUFFER_H
#ifndef LOVE_GRAPHICS_OPENGL_GL_BUFFER_H
#define LOVE_GRAPHICS_OPENGL_GL_BUFFER_H
// LOVE
#include "common/config.h"
@@ -40,64 +40,43 @@ namespace opengl
{
/**
* VertexBuffer is an abstraction over VBOs (Vertex Buffer Objects), which
* falls back to regular vertex arrays if VBOs are not supported.
*
* This allows code to take advantage of VBOs where available, but still
* work on older systems where it's *not* available. Everyone's happy.
*
* The class is (for now) meant for internal use.
* GLBuffer is a thin abstraction over OpenGL's Buffer Objects.
* The class is meant for internal use.
*/
class VertexBuffer
class GLBuffer : public Volatile
{
public:
// Different guarantees for VertexBuffer data storage.
enum MemoryBacking
{
// The VertexBuffer is will have a valid copy of its data in main memory
// at all times.
BACKING_FULL,
// The VertexBuffer will have a valid copy of its data in main memory
// when it needs to be reloaded and when it's mapped.
BACKING_PARTIAL
};
/**
* Create a new VertexBuffer (either a plain vertex array, or a VBO),
* based on what's supported on the system.
* Create a new GLBuffer.
*
* If VBOs are not supported, a plain vertex array will automatically
* be created and returned instead.
*
* @param size The size of the VertexBuffer (in bytes).
* @param size The size of the GLBuffer (in bytes).
* @param target GL_ARRAY_BUFFER, GL_ELEMENT_ARRAY_BUFFER.
* @param usage GL_DYNAMIC_DRAW, etc.
* @param backing Determines what guarantees are placed on the data.
* @return A new VertexBuffer.
* @return A new GLBuffer.
*/
static VertexBuffer *Create(size_t size, GLenum target, GLenum usage, MemoryBacking backing = BACKING_PARTIAL);
static GLBuffer *Create(size_t size, GLenum target, GLenum usage);
/**
* Constructor.
*
* @param size The size of the VertexBuffer in bytes.
* @param target The target VertexBuffer object, e.g. GL_ARRAY_BUFFER.
* @param size The size of the GLBuffer in bytes.
* @param target The target GLBuffer object, e.g. GL_ARRAY_BUFFER.
* @param usage Usage hint, e.g. GL_DYNAMIC_DRAW.
* @param backing Determines what guarantees are placed on the data.
*/
VertexBuffer(size_t size, GLenum target, GLenum usage, MemoryBacking backing = BACKING_PARTIAL);
GLBuffer(size_t size, GLenum target, GLenum usage);
/**
* Destructor. Does nothing, but must be declared virtual.
* Destructor.
*/
virtual ~VertexBuffer();
virtual ~GLBuffer();
/**
* Get the size of the VertexBuffer, in bytes.
* Get the size of the GLBuffer, in bytes.
*
* @return The size of the VertexBuffer.
* @return The size of the GLBuffer.
*/
size_t getSize() const
{
@@ -115,7 +94,7 @@ public:
}
/**
* Get the usage hint for this VertexBuffer.
* Get the usage hint for this GLBuffer.
*
* @return The usage hint, e.g. GL_DYNAMIC_DRAW.
*/
@@ -134,56 +113,51 @@ public:
return is_mapped;
}
MemoryBacking getMemoryBacking() const
{
return backing;
}
/**
* Map the VertexBuffer to client memory.
* Map the GLBuffer to client memory.
*
* This can be faster for large changes to the buffer. For smaller
* changes, see fill().
*
* The VertexBuffer must be bound to use this function.
* The GLBuffer must be bound to use this function.
*
* @return A pointer to memory which represents the buffer.
*/
virtual void *map() = 0;
virtual void *map();
/**
* Unmap a previously mapped VertexBuffer. The buffer must be unmapped
* Unmap a previously mapped GLBuffer. The buffer must be unmapped
* when used to draw elements.
*
* The VertexBuffer must be bound to use this function.
* The GLBuffer must be bound to use this function.
*
* @param usedOffset The offset into the mapped buffer indicating the
* sub-range of data modified. Optional.
* @param usedSize The size of the sub-range of modified data. Optional.
*/
virtual void unmap(size_t usedOffset = 0, size_t usedSize = -1) = 0;
virtual void unmap(size_t usedOffset = 0, size_t usedSize = -1);
/**
* Bind the VertexBuffer to its specified target.
* Bind the GLBuffer to its specified target.
* (GL_ARRAY_BUFFER, GL_ELEMENT_ARRAY_BUFFER, etc).
*/
virtual void bind() = 0;
virtual void bind();
/**
* Unbind a prevously bound VertexBuffer.
* Unbind a prevously bound GLBuffer.
*/
virtual void unbind() = 0;
virtual void unbind();
/**
* Fill a portion of the buffer with data.
*
* The VertexBuffer must be bound to use this function.
* The GLBuffer must be bound to use this function.
*
* @param offset The offset in the VertexBuffer to store the data.
* @param offset The offset in the GLBuffer to store the data.
* @param size The size of the incoming data.
* @param data Pointer to memory to copy data from.
*/
virtual void fill(size_t offset, size_t size, const void *data) = 0;
virtual void fill(size_t offset, size_t size, const void *data);
/**
* Get a pointer which represents the specified byte offset.
@@ -191,10 +165,14 @@ public:
* @param offset The byte offset. (0 is first byte).
* @return A pointer which represents the offset.
*/
virtual const void *getPointer(size_t offset) const = 0;
virtual const void *getPointer(size_t offset) const;
// Implements Volatile.
virtual bool loadVolatile();
virtual void unloadVolatile();
/**
* This helper class can bind a VertexArray temporarily, and
* This helper class can bind a GLBuffer temporarily, and
* automatically un-bind when it's destroyed.
*/
class Bind
@@ -202,16 +180,16 @@ public:
public:
/**
* Bind a VertexBuffer.
* Bind a GLBuffer.
*/
Bind(VertexBuffer &buf)
Bind(GLBuffer &buf)
: buf(buf)
{
buf.bind();
}
/**
* Unbinds a VertexBuffer.
* Unbinds a GLBuffer.
*/
~Bind()
{
@@ -220,17 +198,19 @@ public:
private:
// VertexBuffer to work on.
VertexBuffer &buf;
};
// GLBuffer to work on.
GLBuffer &buf;
}; // Bind
class Mapper
{
public:
/**
* Memory-maps a VertexBuffer.
* Memory-maps a GLBuffer.
*/
Mapper(VertexBuffer &buffer)
Mapper(GLBuffer &buffer)
: buf(buffer)
{
elems = buf.map();
@@ -253,98 +233,11 @@ public:
}
private:
VertexBuffer &buf;
GLBuffer &buf;
void *elems;
};
protected:
// Whether the buffer is currently bound.
bool is_bound;
// Whether the buffer is currently mapped to main memory.
bool is_mapped;
private:
// The size of the buffer, in bytes.
size_t size;
// The target buffer object. (GL_ARRAY_BUFFER, GL_ELEMENT_ARRAY_BUFFER).
GLenum target;
// Usage hint. GL_[DYNAMIC, STATIC, STREAM]_DRAW.
GLenum usage;
//
MemoryBacking backing;
};
/**
* Implementation of VertexBuffer which uses plain arrays to store the data.
*
* This implementation should be supported everywhere, and acts as a fallback
* on systems which do not support VBOs.
*/
class VertexArray : public VertexBuffer
{
public:
/**
* @copydoc VertexBuffer(int, GLenum, GLenum, Backing)
*/
VertexArray(size_t size, GLenum target, GLenum usage, MemoryBacking backing);
/**
* Frees the data we've allocated.
*/
virtual ~VertexArray();
// Implements VertexBuffer.
virtual void *map();
virtual void unmap(size_t usedOffset = 0, size_t usedSize = -1);
virtual void bind();
virtual void unbind();
virtual void fill(size_t offset, size_t size, const void *data);
virtual const void *getPointer(size_t offset) const ;
private:
// Holds the data.
char *buf;
};
/**
* Vertex Buffer Object (VBO) implementation of VertexBuffer.
*
* This will be used on all systems that support it. It's in general
* faster than vertex arrays, but especially in use-cases where there
* is no need to update the data every frame.
**/
class VBO : public VertexBuffer, public Volatile
{
public:
/**
* @copydoc VertexBuffer(size_t, GLenum, GLenum, Backing)
**/
VBO(size_t size, GLenum target, GLenum usage, MemoryBacking backing);
/**
* Deletes the VBOs from OpenGL.
**/
virtual ~VBO();
// Implements VertexBuffer.
virtual void *map();
virtual void unmap(size_t usedOffset = 0, size_t usedSize = -1);
virtual void bind();
virtual void unbind();
virtual void fill(size_t offset, size_t size, const void *data);
virtual const void *getPointer(size_t offset) const ;
// Implements Volatile.
bool loadVolatile();
void unloadVolatile();
}; // Mapper
private:
@@ -355,31 +248,37 @@ private:
* @return True on success, false otherwise.
*/
bool load(bool restore);
/**
* Optionally save the data in the VBO, then delete it.
*
* @param save True to save the data before deleting.
*/
void unload(bool save);
void unload();
void unmapStatic(size_t offset, size_t size);
void unmapStream();
// Whether the buffer is currently bound.
bool is_bound;
// Whether the buffer is currently mapped to main memory.
bool is_mapped;
// The size of the buffer, in bytes.
size_t size;
// The target buffer object. (GL_ARRAY_BUFFER, GL_ELEMENT_ARRAY_BUFFER).
GLenum target;
// Usage hint. GL_[DYNAMIC, STATIC, STREAM]_DRAW.
GLenum usage;
// The VBO identifier. Assigned by OpenGL.
GLuint vbo;
// A pointer to mapped memory. Will be inialized on the first
// call to map().
// A pointer to mapped memory.
char *memory_map;
// Set if the buffer was modified while operating on gpu memory
// and needs to be synchronized.
bool is_dirty;
};
}; // GLBuffer
/**
* VertexIndex manages one shared VertexBuffer that stores the indices for an
* VertexIndex manages one shared GLBuffer that stores the indices for an
* element array. Vertex arrays using the vertex structure (or anything else
* that can use the pattern below) can request a size and use it for the
* drawElements call.
@@ -388,23 +287,23 @@ private:
* indices[i*6 + 1] = i*4 + 1;
* indices[i*6 + 2] = i*4 + 2;
*
* indices[i*6 + 3] = i*4 + 0;
* indices[i*6 + 4] = i*4 + 2;
* indices[i*6 + 3] = i*4 + 2;
* indices[i*6 + 4] = i*4 + 1;
* indices[i*6 + 5] = i*4 + 3;
*
* There will always be a large enough VertexBuffer around until all
* There will always be a large enough GLBuffer around until all
* VertexIndex instances have been deleted.
*
* Q: Why have something like VertexIndex?
* A: The indices for the SpriteBatch do not change, only the array size
* varies. Using one VertexBuffer for all element arrays removes this
* varies. Using one GLBuffer for all element arrays removes this
* duplicated data and saves some memory.
*/
class VertexIndex
{
public:
/**
* Adds an entry to the list of sizes and resizes the VertexBuffer
* Adds an entry to the list of sizes and resizes the GLBuffer
* if needed. A size of 1 allocates a group of 6 indices for 4 vertices
* creating 1 face.
*
@@ -416,7 +315,7 @@ public:
VertexIndex &operator = (const VertexIndex &other);
/**
* Removes an entry from the list of sizes and resizes the VertexBuffer
* Removes an entry from the list of sizes and resizes the GLBuffer
* if needed.
*/
~VertexIndex();
@@ -463,14 +362,14 @@ public:
size_t getElementSize();
/**
* Returns the pointer to the VertexBuffer.
* Returns the pointer to the GLBuffer.
* The pointer will change if a new size request or removal causes
* a VertexBuffer resize. It is recommended to retrieve the pointer
* a GLBuffer resize. It is recommended to retrieve the pointer
* value directly before the drawing call.
*
* @return The pointer to the VertexBuffer.
* @return The pointer to the GLBuffer.
*/
VertexBuffer *getVertexBuffer() const;
GLBuffer *getBuffer() const;
/**
* Returns a pointer which represents the specified byte offset.
@@ -497,12 +396,12 @@ private:
void removeSize(size_t oldSize);
/**
* Resizes the VertexBuffer to the requested size.
* Resizes the GLBuffer to the requested size.
* This function takes care of choosing the correct integer type and
* allocating and deleting the VertexBuffer instance. It also has some
* allocating and deleting the GLBuffer instance. It also has some
* fallback logic in case the memory ran out.
*
* @param size The requested VertexBuffer size. Passing 0 deletes the VertexBuffer without allocating a new one.
* @param size The requested GLBuffer size. Passing 0 deletes the GLBuffer without allocating a new one.
*/
void resize(size_t size);
@@ -518,16 +417,16 @@ private:
// The size in bytes of an element in the element array.
static size_t elementSize;
// The current VertexBuffer size. 0 means no VertexBuffer.
// The current GLBuffer size. 0 means no GLBuffer.
static size_t maxSize;
// The list of sizes. Needs to be kept sorted in ascending order.
static std::list<size_t> sizeRefs;
// The VertexBuffer for the element array. Can be NULL.
static VertexBuffer *element_array;
// The GLBuffer for the element array. Can be NULL.
static GLBuffer *element_array;
};
} // opengl
} // graphics
} // love
#endif // LOVE_GRAPHICS_OPENGL_SPRITE_BATCH_H
#endif // LOVE_GRAPHICS_OPENGL_GL_BUFFER_H
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+52 -60
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2014 LOVE Development Team
* Copyright (c) 2006-2015 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
@@ -40,11 +40,13 @@
#include "Font.h"
#include "Image.h"
#include "graphics/Quad.h"
#include "graphics/Texture.h"
#include "SpriteBatch.h"
#include "ParticleSystem.h"
#include "Canvas.h"
#include "Shader.h"
#include "Mesh.h"
#include "Text.h"
namespace love
{
@@ -53,11 +55,6 @@ namespace graphics
namespace opengl
{
// During display mode changing, certain
// variables about the OpenGL context are
// lost.
class Graphics : public love::graphics::Graphics
{
public:
@@ -72,6 +69,9 @@ public:
virtual bool setMode(int width, int height, bool &sRGB);
virtual void unSetMode();
virtual void setActive(bool active);
virtual bool isActive() const;
void setDebug(bool enable);
/**
@@ -82,9 +82,19 @@ public:
void reset();
/**
* Clears the screen.
* Clears the screen to a specific color.
**/
void clear();
void clear(Color c);
/**
* Clears each active canvas to a different color.
**/
void clear(const std::vector<Color> &colors);
/**
* Discards the contents of the screen.
**/
void discard(const std::vector<bool> &colorbuffers, bool stencil);
/**
* Flips buffers. (Rendered geometry is presented on screen).
@@ -128,28 +138,27 @@ public:
bool getScissor(int &x, int &y, int &width, int &height) const;
/**
* Enables the stencil buffer and set stencil function to fill it
*/
void defineStencil();
* Enables or disables drawing to the stencil buffer. When enabled, the
* color buffer is disabled.
**/
void drawToStencilBuffer(bool enable);
/**
* Set stencil function to mask the following drawing calls using
* the current stencil buffer
* @param invert Invert the mask, i.e. draw everywhere expect where
* the mask is defined.
*/
void useStencil(bool invert = false);
* Sets whether stencil testing is enabled.
**/
void setStencilTest(bool enable, bool invert);
void getStencilTest(bool &enable, bool &invert);
/**
* Disables the stencil buffer
*/
void discardStencil();
* Clear the stencil buffer in the active Canvas(es.)
**/
void clearStencil();
/**
* Creates an Image object with padding and/or optimization.
**/
Image *newImage(love::image::ImageData *data, Image::Format format = Image::FORMAT_NORMAL);
Image *newImage(love::image::CompressedData *cdata, Image::Format format = Image::FORMAT_NORMAL);
Image *newImage(love::image::ImageData *data, const Image::Flags &flags);
Image *newImage(love::image::CompressedData *cdata, const Image::Flags &flags);
Quad *newQuad(Quad::Viewport v, float sw, float sh);
@@ -169,6 +178,8 @@ public:
Mesh *newMesh(const std::vector<Vertex> &vertices, Mesh::DrawMode mode = Mesh::DRAW_MODE_FAN);
Mesh *newMesh(int vertexcount, Mesh::DrawMode mode = Mesh::DRAW_MODE_FAN);
Text *newText(Font *font, const std::string &text = "");
/**
* Sets the foreground color.
* @param c The new foreground color.
@@ -190,15 +201,8 @@ public:
**/
Color getBackgroundColor() const;
/**
* Sets the current font.
* @param font A Font object.
**/
void setFont(Font *font);
/**
* Gets the current Font, or nil if none.
**/
Font *getFont() const;
Font *getFont();
void setShader(Shader *shader);
void setShader();
@@ -207,7 +211,7 @@ public:
void setCanvas(Canvas *canvas);
void setCanvas(const std::vector<Canvas *> &canvases);
void setCanvas(const std::vector<Object::StrongRef<Canvas>> &canvases);
void setCanvas(const std::vector<StrongRef<Canvas>> &canvases);
void setCanvas();
std::vector<Canvas *> getCanvas() const;
@@ -215,13 +219,12 @@ public:
/**
* Sets the enabled color components when rendering.
**/
void setColorMask(const bool mask[4]);
void setColorMask(ColorMask mask);
/**
* Gets the current color mask.
* Returns an array of 4 booleans representing the mask.
**/
const bool *getColorMask() const;
ColorMask getColorMask() const;
/**
* Sets the current blend mode.
@@ -287,22 +290,11 @@ public:
**/
void setPointSize(float size);
/**
* Sets the style of points.
* @param style POINT_SMOOTH or POINT_ROUGH.
**/
void setPointStyle(PointStyle style);
/**
* Gets the point size.
**/
float getPointSize() const;
/**
* Gets the point style.
**/
PointStyle getPointStyle() const;
/**
* Sets whether graphics will be drawn as wireframe lines instead of filled
* triangles (has no effect for drawn points.)
@@ -347,7 +339,7 @@ public:
* @param kx Shear along the x-axis.
* @param ky Shear along the y-axis.
**/
void printf(const std::string &str, float x, float y, float wrap, AlignMode align, float angle, float sx, float sy, float ox, float oy, float kx, float ky);
void printf(const std::string &str, float x, float y, float wrap, Font::AlignMode align, float angle, float sx, float sy, float ox, float oy, float kx, float ky);
/**
* Draws a point at (x,y).
@@ -444,40 +436,35 @@ private:
struct DisplayState
{
// Colors.
Color color;
Color backgroundColor;
// Blend mode.
BlendMode blendMode;
// Line.
float lineWidth;
LineStyle lineStyle;
LineJoin lineJoin;
// Point.
float pointSize;
PointStyle pointStyle;
// Scissor.
bool scissor;
OpenGL::Viewport scissorBox;
Object::StrongRef<Font> font;
Object::StrongRef<Shader> shader;
// Stencil.
bool stencilTest;
bool stencilInvert;
std::vector<Object::StrongRef<Canvas>> canvases;
StrongRef<Font> font;
StrongRef<Shader> shader;
// Color mask.
bool colorMask[4];
std::vector<StrongRef<Canvas>> canvases;
ColorMask colorMask;
bool wireframe;
// Default filter.
Texture::Filter defaultFilter;
// Default mipmap filter and sharpness.
Texture::FilterMode defaultMipmapFilter;
float defaultMipmapSharpness;
@@ -491,15 +478,20 @@ private:
void restoreState(const DisplayState &s);
void restoreStateChecked(const DisplayState &s);
void checkSetDefaultFont();
love::window::Window *currentWindow;
StrongRef<Font> defaultFont;
std::vector<double> pixel_size_stack; // stores current size of a pixel (needed for line drawing)
int width;
int height;
bool created;
bool active;
bool activeStencil;
bool writingToStencil;
std::vector<DisplayState> states;
std::vector<StackType> stackTypes; // Keeps track of the pushed stack types.
File diff suppressed because it is too large Load Diff
+44 -70
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2014 LOVE Development Team
* Copyright (c) 2006-2015 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
@@ -29,7 +29,8 @@
#include "common/math.h"
#include "image/ImageData.h"
#include "image/CompressedData.h"
#include "Texture.h"
#include "graphics/Texture.h"
#include "graphics/Volatile.h"
// OpenGL
#include "OpenGL.h"
@@ -47,15 +48,21 @@ namespace opengl
*
* @author Anders Ruud
**/
class Image : public Texture
class Image : public Texture, public Volatile
{
public:
enum Format
enum FlagType
{
FORMAT_NORMAL,
FORMAT_SRGB,
FORMAT_MAX_ENUM
FLAG_TYPE_MIPMAPS,
FLAG_TYPE_SRGB,
FLAG_TYPE_MAX_ENUM
};
struct Flags
{
bool mipmaps = false;
bool sRGB = false;;
};
/**
@@ -64,22 +71,20 @@ public:
*
* @param data The data from which to load the image.
**/
Image(love::image::ImageData *data, Format format = FORMAT_NORMAL);
Image(love::image::ImageData *data, const Flags &flags);
/**
* Creates a new Image with compressed image data.
*
* @param cdata The compressed data from which to load the image.
**/
Image(love::image::CompressedData *cdata, Format format = FORMAT_NORMAL);
Image(love::image::CompressedData *cdata, const Flags &flags);
/**
* Destructor. Deletes the hardware texture and other resources.
**/
virtual ~Image();
love::image::ImageData *getImageData() const;
love::image::CompressedData *getCompressedData() const;
// Implements Volatile.
bool loadVolatile();
void unloadVolatile();
/**
* @copydoc Drawable::draw()
@@ -91,18 +96,13 @@ public:
**/
void drawq(Quad *quad, float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky);
/**
* Call before using this Image's texture to draw. Binds the texture,
* globally scales texture coordinates if the Image has NPOT dimensions and
* NPOT isn't supported, etc.
**/
virtual void predraw();
virtual void postdraw();
virtual const void *getHandle() const;
virtual GLuint getGLTexture() const;
love::image::ImageData *getImageData() const;
love::image::CompressedData *getCompressedData() const;
virtual void setFilter(const Texture::Filter &f);
virtual void setWrap(const Texture::Wrap &w);
virtual bool setWrap(const Texture::Wrap &w);
void setMipmapSharpness(float sharpness);
float getMipmapSharpness() const;
@@ -112,60 +112,48 @@ public:
**/
bool isCompressed() const;
void bind() const;
bool load();
void unload();
// Implements Volatile.
bool loadVolatile();
void unloadVolatile();
/**
* Re-uploads the ImageData or CompressedData associated with this Image to
* the GPU, allowing situations where lovers modify an ImageData after image
* creation from the ImageData, and apply the changes with Image:refresh().
* the GPU.
**/
bool refresh();
bool refresh(int xoffset, int yoffset, int w, int h);
Format getFormat() const;
const Flags &getFlags() const;
static void setDefaultMipmapSharpness(float sharpness);
static float getDefaultMipmapSharpness();
static void setDefaultMipmapFilter(FilterMode f);
static FilterMode getDefaultMipmapFilter();
static bool hasNpot();
static bool hasAnisotropicFilteringSupport();
static bool hasMipmapSupport();
static bool hasMipmapSharpnessSupport();
static bool hasCompressedTextureSupport();
static bool hasCompressedTextureSupport(image::CompressedData::Format format);
static bool hasCompressedTextureSupport(image::CompressedData::Format format, bool sRGB);
static bool hasSRGBSupport();
static bool getConstant(const char *in, Format &out);
static bool getConstant(Format in, const char *&out);
static bool getConstant(const char *in, FlagType &out);
static bool getConstant(FlagType in, const char *&out);
static int imageCount;
private:
void uploadDefaultTexture();
void drawv(const Matrix &t, const Vertex *v);
void preload();
void generateMipmaps();
void loadDefaultTexture();
void loadFromCompressedData();
void loadFromImageData();
GLenum getCompressedFormat(image::CompressedData::Format cformat) const;
// The ImageData from which the texture is created. May be null if
// Compressed image data was used to create the texture.
Object::StrongRef<love::image::ImageData> data;
StrongRef<love::image::ImageData> data;
// Or the Compressed Image Data from which the texture is created. May be
// null if raw ImageData was used to create the texture.
Object::StrongRef<love::image::CompressedData> cdata;
// Real dimensions of the texture, if it was auto-padded to POT size.
int paddedWidth, paddedHeight;
StrongRef<love::image::CompressedData> cdata;
// OpenGL texture identifier.
GLuint texture;
@@ -173,14 +161,11 @@ private:
// Mipmap texture LOD bias (sharpness) value.
float mipmapSharpness;
// True if mipmaps have been created for this Image.
bool mipmapsCreated;
// Whether this Image is using a compressed texture.
bool compressed;
// The format to interpret the image's data as.
Format format;
// The flags used to initialize this Image.
Flags flags;
// True if the image wasn't able to be properly created and it had to fall
// back to a default texture.
@@ -188,24 +173,13 @@ private:
size_t textureMemorySize;
void preload();
void uploadTexturePadded();
void uploadTexture();
void uploadCompressedMipmaps();
void createMipmaps();
void checkMipmapsCreated();
static float maxMipmapSharpness;
static FilterMode defaultMipmapFilter;
static float defaultMipmapSharpness;
GLenum getCompressedFormat(image::CompressedData::Format cformat) const;
static StringMap<Format, FORMAT_MAX_ENUM>::Entry formatEntries[];
static StringMap<Format, FORMAT_MAX_ENUM> formats;
static StringMap<FlagType, FLAG_TYPE_MAX_ENUM>::Entry flagNameEntries[];
static StringMap<FlagType, FLAG_TYPE_MAX_ENUM> flagNames;
}; // Image
+29 -36
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2014 LOVE Development Team
* Copyright (c) 2006-2015 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
@@ -97,15 +97,12 @@ void Mesh::setVertices(const std::vector<Vertex> &verts)
}
if (!vbo)
{
// Full memory backing because we might access the data at any time.
vbo = VertexBuffer::Create(size, GL_ARRAY_BUFFER, GL_DYNAMIC_DRAW, VertexBuffer::BACKING_FULL);
}
vbo = GLBuffer::Create(size, GL_ARRAY_BUFFER, GL_DYNAMIC_DRAW);
vertex_count = verts.size();
VertexBuffer::Bind vbo_bind(*vbo);
VertexBuffer::Mapper vbo_mapper(*vbo);
GLBuffer::Bind vbo_bind(*vbo);
GLBuffer::Mapper vbo_mapper(*vbo);
// Fill the buffer with the vertices.
memcpy(vbo_mapper.get(), &verts[0], size);
@@ -115,7 +112,7 @@ const Vertex *Mesh::getVertices() const
{
if (vbo)
{
VertexBuffer::Bind vbo_bind(*vbo);
GLBuffer::Bind vbo_bind(*vbo);
return (Vertex *) vbo->map();
}
@@ -127,7 +124,7 @@ void Mesh::setVertex(size_t index, const Vertex &v)
if (index >= vertex_count)
throw love::Exception("Invalid vertex index: %ld", index + 1);
VertexBuffer::Bind vbo_bind(*vbo);
GLBuffer::Bind vbo_bind(*vbo);
// We unmap the vertex buffer in Mesh::draw. This lets us coalesce the
// buffer transfer calls into just one.
@@ -140,7 +137,7 @@ Vertex Mesh::getVertex(size_t index) const
if (index >= vertex_count)
throw love::Exception("Invalid vertex index: %ld", index + 1);
VertexBuffer::Bind vbo_bind(*vbo);
GLBuffer::Bind vbo_bind(*vbo);
// We unmap the vertex buffer in Mesh::draw.
Vertex *vertices = (Vertex *) vbo->map();
@@ -156,7 +153,7 @@ size_t Mesh::getVertexCount() const
* Copies index data from a vector to a mapped index buffer.
**/
template <typename T>
static void copyToIndexBuffer(const std::vector<uint32> &indices, VertexBuffer::Mapper &buffermap, size_t maxval)
static void copyToIndexBuffer(const std::vector<uint32> &indices, GLBuffer::Mapper &buffermap, size_t maxval)
{
T *elems = (T *) buffermap.get();
@@ -183,18 +180,15 @@ void Mesh::setVertexMap(const std::vector<uint32> &map)
}
if (!ibo && size > 0)
{
// Full memory backing because we might access the data at any time.
ibo = VertexBuffer::Create(size, GL_ELEMENT_ARRAY_BUFFER, GL_DYNAMIC_DRAW, VertexBuffer::BACKING_FULL);
}
ibo = GLBuffer::Create(size, GL_ELEMENT_ARRAY_BUFFER, GL_DYNAMIC_DRAW);
element_count = map.size();
if (!ibo || element_count == 0)
return;
VertexBuffer::Bind ibo_bind(*ibo);
VertexBuffer::Mapper ibo_map(*ibo);
GLBuffer::Bind ibo_bind(*ibo);
GLBuffer::Mapper ibo_map(*ibo);
// Fill the buffer with the index values from the vector.
switch (datatype)
@@ -233,7 +227,7 @@ void Mesh::getVertexMap(std::vector<uint32> &map) const
map.clear();
map.reserve(element_count);
VertexBuffer::Bind ibo_bind(*ibo);
GLBuffer::Bind ibo_bind(*ibo);
// We unmap the buffer in Mesh::draw and Mesh::setVertexMap.
void *buffer = ibo->map();
@@ -323,10 +317,12 @@ void Mesh::draw(float x, float y, float angle, float sx, float sy, float ox, flo
if (vertex_count == 0)
return;
OpenGL::TempDebugGroup debuggroup("Mesh draw");
if (texture.get())
texture->predraw();
gl.bindTexture(*(GLuint *) texture->getHandle());
else
gl.bindTexture(0);
gl.bindTexture(gl.getDefaultTexture());
Matrix m;
m.setTransformation(x, y, angle, sx, sy, ox, oy, kx, ky);
@@ -334,22 +330,22 @@ void Mesh::draw(float x, float y, float angle, float sx, float sy, float ox, flo
OpenGL::TempTransform transform(gl);
transform.get() *= m;
VertexBuffer::Bind vbo_bind(*vbo);
GLBuffer::Bind vbo_bind(*vbo);
// Make sure the VBO isn't mapped when we draw (sends data to GPU if needed.)
vbo->unmap();
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glEnableVertexAttribArray(ATTRIB_POS);
glEnableVertexAttribArray(ATTRIB_TEXCOORD);
glVertexPointer(2, GL_FLOAT, sizeof(Vertex), vbo->getPointer(pos_offset));
glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), vbo->getPointer(tex_offset));
glVertexAttribPointer(ATTRIB_POS, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), vbo->getPointer(pos_offset));
glVertexAttribPointer(ATTRIB_TEXCOORD, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), vbo->getPointer(tex_offset));
if (hasVertexColors())
{
// Per-vertex colors.
glEnableClientState(GL_COLOR_ARRAY);
glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(Vertex), vbo->getPointer(color_offset));
glEnableVertexAttribArray(ATTRIB_COLOR);
glVertexAttribPointer(ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(Vertex), vbo->getPointer(color_offset));
}
GLenum mode = getGLDrawMode(draw_mode);
@@ -359,12 +355,12 @@ void Mesh::draw(float x, float y, float angle, float sx, float sy, float ox, flo
if (ibo && element_count > 0)
{
// Use the custom vertex map (index buffer) to draw the vertices.
VertexBuffer::Bind ibo_bind(*ibo);
GLBuffer::Bind ibo_bind(*ibo);
// Make sure the index buffer isn't mapped (sends data to GPU if needed.)
ibo->unmap();
int max = element_count - 1;
int max = (int) element_count - 1;
if (range_max >= 0)
max = std::min(range_max, max);
@@ -379,7 +375,7 @@ void Mesh::draw(float x, float y, float angle, float sx, float sy, float ox, flo
}
else
{
int max = vertex_count - 1;
int max = (int) vertex_count - 1;
if (range_max >= 0)
max = std::min(range_max, max);
@@ -391,18 +387,15 @@ void Mesh::draw(float x, float y, float angle, float sx, float sy, float ox, flo
gl.drawArrays(mode, min, max - min + 1);
}
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glDisableVertexAttribArray(ATTRIB_TEXCOORD);
glDisableVertexAttribArray(ATTRIB_POS);
if (hasVertexColors())
{
glDisableClientState(GL_COLOR_ARRAY);
glDisableVertexAttribArray(ATTRIB_COLOR);
// Using the color array leaves the GL constant color undefined.
gl.setColor(gl.getColor());
}
if (texture.get())
texture->postdraw();
}
GLenum Mesh::getGLDrawMode(DrawMode mode) const
+6 -6
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2014 LOVE Development Team
* Copyright (c) 2006-2015 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
@@ -27,8 +27,8 @@
#include "common/math.h"
#include "common/StringMap.h"
#include "graphics/Drawable.h"
#include "Texture.h"
#include "VertexBuffer.h"
#include "graphics/Texture.h"
#include "GLBuffer.h"
// C++
#include <vector>
@@ -165,11 +165,11 @@ private:
size_t getGLDataTypeSize(GLenum datatype) const;
// Vertex buffer.
VertexBuffer *vbo;
GLBuffer *vbo;
size_t vertex_count;
// Element (vertex index) buffer, for the vertex map.
VertexBuffer *ibo;
GLBuffer *ibo;
size_t element_count;
GLenum element_data_type;
@@ -178,7 +178,7 @@ private:
int range_min;
int range_max;
Object::StrongRef<Texture> texture;
StrongRef<Texture> texture;
// Whether the per-vertex colors are used when drawing.
bool colors_enabled;
+225 -258
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2014 LOVE Development Team
* Copyright (c) 2006-2015 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
@@ -33,6 +33,13 @@
// C
#include <cstring>
// For SDL_GL_GetProcAddress.
#include <SDL_video.h>
#ifdef LOVE_IOS
#include <SDL_system.h>
#endif
namespace love
{
namespace graphics
@@ -45,7 +52,9 @@ OpenGL::OpenGL()
, contextInitialized(false)
, maxAnisotropy(1.0f)
, maxTextureSize(0)
, maxRenderTargets(0)
, maxRenderTargets(1)
, maxRenderbufferSamples(0)
, maxTextureUnits(1)
, vendor(VENDOR_UNKNOWN)
, state()
{
@@ -53,29 +62,33 @@ OpenGL::OpenGL()
matrices.projection.reserve(2);
}
void OpenGL::initContext()
bool OpenGL::initContext()
{
if (contextInitialized)
return;
return true;
if (!gladLoadGLLoader(SDL_GL_GetProcAddress))
return false;
initOpenGLFunctions();
initVendor();
initMatrices();
// Store the current color so we don't have to get it through GL later.
GLfloat glcolor[4];
glGetFloatv(GL_CURRENT_COLOR, glcolor);
state.color.r = glcolor[0] * 255;
state.color.g = glcolor[1] * 255;
state.color.b = glcolor[2] * 255;
state.color.a = glcolor[3] * 255;
contextInitialized = true;
// Same with the current clear color.
glGetFloatv(GL_COLOR_CLEAR_VALUE, glcolor);
state.clearColor.r = glcolor[0] * 255;
state.clearColor.g = glcolor[1] * 255;
state.clearColor.b = glcolor[2] * 255;
state.clearColor.a = glcolor[3] * 255;
return true;
}
void OpenGL::setupContext()
{
if (!contextInitialized)
return;
initMaxValues();
state.color = Color(255, 255, 255, 255);
GLfloat glcolor[4] = {1.0f, 1.0f, 1.0f, 1.0f};
glVertexAttrib4fv(ATTRIB_COLOR, glcolor);
// Get the current viewport.
glGetIntegerv(GL_VIEWPORT, (GLint *) &state.viewport.x);
@@ -85,51 +98,38 @@ void OpenGL::initContext()
glGetIntegerv(GL_SCISSOR_BOX, (GLint *) &state.scissor.x);
state.scissor.y = state.viewport.h - (state.scissor.y + state.scissor.h);
// Initialize multiple texture unit support for shaders, if available.
state.textureUnits.clear();
if (Shader::isSupported())
{
GLint maxtextureunits;
glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &maxtextureunits);
state.textureUnits.resize(maxtextureunits, 0);
GLenum curgltextureunit;
glGetIntegerv(GL_ACTIVE_TEXTURE, (GLint *) &curgltextureunit);
state.curTextureUnit = (int) curgltextureunit - GL_TEXTURE0;
// Retrieve currently bound textures for each texture unit.
for (size_t i = 0; i < state.textureUnits.size(); i++)
{
glActiveTexture(GL_TEXTURE0 + i);
glGetIntegerv(GL_TEXTURE_BINDING_2D, (GLint *) &state.textureUnits[i]);
}
glActiveTexture(curgltextureunit);
}
if (GLAD_VERSION_1_0)
glGetFloatv(GL_POINT_SIZE, &state.pointSize);
else
state.pointSize = 1.0f;
// Initialize multiple texture unit support for shaders.
state.boundTextures.clear();
state.boundTextures.resize(maxTextureUnits, 0);
GLenum curgltextureunit;
glGetIntegerv(GL_ACTIVE_TEXTURE, (GLint *) &curgltextureunit);
state.curTextureUnit = (int) curgltextureunit - GL_TEXTURE0;
// Retrieve currently bound textures for each texture unit.
for (int i = 0; i < (int) state.boundTextures.size(); i++)
{
// Multitexturing not supported, so we only have 1 texture unit.
state.textureUnits.resize(1, 0);
state.curTextureUnit = 0;
glGetIntegerv(GL_TEXTURE_BINDING_2D, (GLint *) &state.textureUnits[0]);
glActiveTexture(GL_TEXTURE0 + i);
glGetIntegerv(GL_TEXTURE_BINDING_2D, (GLint *) &state.boundTextures[i]);
}
BlendState blend = {GL_ONE, GL_ONE, GL_ZERO, GL_ZERO, GL_FUNC_ADD};
setBlendState(blend);
glActiveTexture(curgltextureunit);
initMaxValues();
createDefaultTexture();
state.lastPseudoInstanceID = -1;
// Invalidate the cached matrices by setting some elements to NaN.
float nan = std::numeric_limits<float>::quiet_NaN();
state.lastProjectionMatrix.setTranslation(nan, nan);
state.lastTransformMatrix.setTranslation(nan, nan);
glMatrixMode(GL_MODELVIEW);
if (GLAD_VERSION_1_0)
glMatrixMode(GL_MODELVIEW);
contextInitialized = true;
}
@@ -139,6 +139,9 @@ void OpenGL::deInitContext()
if (!contextInitialized)
return;
glDeleteTextures(1, &state.defaultTexture);
state.defaultTexture = 0;
contextInitialized = false;
}
@@ -152,73 +155,105 @@ 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"))
vendor = VENDOR_ATI_AMD;
vendor = VENDOR_AMD;
else if (strstr(vstr, "NVIDIA"))
vendor = VENDOR_NVIDIA;
else if (strstr(vstr, "Intel"))
vendor = VENDOR_INTEL;
else if (strstr(vstr, "Mesa"))
vendor = VENDOR_MESA_SOFT;
else if (strstr(vstr, "Apple Computer"))
else if (strstr(vstr, "Apple Computer") || strstr(vstr, "Apple Inc."))
vendor = VENDOR_APPLE;
else if (strstr(vstr, "Microsoft"))
vendor = VENDOR_MICROSOFT;
else if (strstr(vstr, "Imagination"))
vendor = VENDOR_IMGTEC;
else if (strstr(vstr, "ARM"))
vendor = VENDOR_ARM;
else if (strstr(vstr, "Qualcomm"))
vendor = VENDOR_QUALCOMM;
else if (strstr(vstr, "Broadcom"))
vendor = VENDOR_BROADCOM;
else if (strstr(vstr, "Vivante"))
vendor = VENDOR_VIVANTE;
else
vendor = VENDOR_UNKNOWN;
}
void OpenGL::initOpenGLFunctions()
{
// The functionality of the core and ARB VBOs are identical, so we can
// assign the pointers of the core functions to the names of the ARB
// functions, if the latter isn't supported but the former is.
if (GLEE_VERSION_1_5 && !GLEE_ARB_vertex_buffer_object)
// Alias extension-suffixed framebuffer functions to core versions since
// there are so many different-named extensions that do the same things...
if (!(GLAD_ES_VERSION_3_0 || GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_object))
{
glBindBufferARB = (GLEEPFNGLBINDBUFFERARBPROC) glBindBuffer;
glBufferDataARB = (GLEEPFNGLBUFFERDATAARBPROC) glBufferData;
glBufferSubDataARB = (GLEEPFNGLBUFFERSUBDATAARBPROC) glBufferSubData;
glDeleteBuffersARB = (GLEEPFNGLDELETEBUFFERSARBPROC) glDeleteBuffers;
glGenBuffersARB = (GLEEPFNGLGENBUFFERSARBPROC) glGenBuffers;
glGetBufferParameterivARB = (GLEEPFNGLGETBUFFERPARAMETERIVARBPROC) glGetBufferParameteriv;
glGetBufferPointervARB = (GLEEPFNGLGETBUFFERPOINTERVARBPROC) glGetBufferPointerv;
glGetBufferSubDataARB = (GLEEPFNGLGETBUFFERSUBDATAARBPROC) glGetBufferSubData;
glIsBufferARB = (GLEEPFNGLISBUFFERARBPROC) glIsBuffer;
glMapBufferARB = (GLEEPFNGLMAPBUFFERARBPROC) glMapBuffer;
glUnmapBufferARB = (GLEEPFNGLUNMAPBUFFERARBPROC) glUnmapBuffer;
}
if (GLAD_VERSION_1_0 && GLAD_EXT_framebuffer_object)
{
fp_glBindRenderbuffer = fp_glBindRenderbufferEXT;
fp_glDeleteRenderbuffers = fp_glDeleteRenderbuffersEXT;
fp_glGenRenderbuffers = fp_glGenRenderbuffersEXT;
fp_glRenderbufferStorage = fp_glRenderbufferStorageEXT;
fp_glGetRenderbufferParameteriv = fp_glGetRenderbufferParameterivEXT;
fp_glBindFramebuffer = fp_glBindFramebufferEXT;
fp_glDeleteFramebuffers = fp_glDeleteFramebuffersEXT;
fp_glGenFramebuffers = fp_glGenFramebuffersEXT;
fp_glCheckFramebufferStatus = fp_glCheckFramebufferStatusEXT;
fp_glFramebufferTexture2D = fp_glFramebufferTexture2DEXT;
fp_glFramebufferRenderbuffer = fp_glFramebufferRenderbufferEXT;
fp_glGetFramebufferAttachmentParameteriv = fp_glGetFramebufferAttachmentParameterivEXT;
fp_glGenerateMipmap = fp_glGenerateMipmapEXT;
}
// Same deal for compressed textures.
if (GLEE_VERSION_1_3 && !GLEE_ARB_texture_compression)
{
glCompressedTexImage2DARB = (GLEEPFNGLCOMPRESSEDTEXIMAGE2DARBPROC) glCompressedTexImage2D;
glCompressedTexSubImage2DARB = (GLEEPFNGLCOMPRESSEDTEXSUBIMAGE2DARBPROC) glCompressedTexSubImage2D;
glGetCompressedTexImageARB = (GLEEPFNGLGETCOMPRESSEDTEXIMAGEARBPROC) glGetCompressedTexImage;
if (GLAD_EXT_framebuffer_blit)
fp_glBlitFramebuffer = fp_glBlitFramebufferEXT;
else if (GLAD_ANGLE_framebuffer_blit)
fp_glBlitFramebuffer = fp_glBlitFramebufferANGLE;
else if (GLAD_NV_framebuffer_blit)
fp_glBlitFramebuffer = fp_glBlitFramebufferNV;
if (GLAD_EXT_framebuffer_multisample)
fp_glRenderbufferStorageMultisample = fp_glRenderbufferStorageMultisampleEXT;
else if (GLAD_APPLE_framebuffer_multisample)
fp_glRenderbufferStorageMultisample = fp_glRenderbufferStorageMultisampleAPPLE;
else if (GLAD_ANGLE_framebuffer_multisample)
fp_glRenderbufferStorageMultisample = fp_glRenderbufferStorageMultisampleANGLE;
else if (GLAD_NV_framebuffer_multisample)
fp_glRenderbufferStorageMultisample = fp_glRenderbufferStorageMultisampleNV;
}
}
void OpenGL::initMaxValues()
{
// We'll need this value to clamp anisotropy.
if (GLEE_EXT_texture_filter_anisotropic)
if (GLAD_EXT_texture_filter_anisotropic)
glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &maxAnisotropy);
else
maxAnisotropy = 1.0f;
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);
if (Canvas::isSupported() && (GLEE_VERSION_2_0 || GLEE_ARB_draw_buffers))
int maxattachments = 1;
int maxdrawbuffers = 1;
if (GLAD_ES_VERSION_3_0 || GLAD_VERSION_2_0)
{
int maxattachments = 0;
glGetIntegerv(GL_MAX_COLOR_ATTACHMENTS, &maxattachments);
int maxdrawbuffers = 0;
glGetIntegerv(GL_MAX_DRAW_BUFFERS, &maxdrawbuffers);
}
maxRenderTargets = std::min(maxattachments, maxdrawbuffers);
maxRenderTargets = std::min(maxattachments, maxdrawbuffers);
if (GLAD_ES_VERSION_3_0 || GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_object
|| GLAD_EXT_framebuffer_multisample || GLAD_APPLE_framebuffer_multisample
|| GLAD_ANGLE_framebuffer_multisample)
{
glGetIntegerv(GL_MAX_SAMPLES, &maxRenderbufferSamples);
}
else
maxRenderTargets = 0;
maxRenderbufferSamples = 0;
glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &maxTextureUnits);
}
void OpenGL::initMatrices()
@@ -237,8 +272,10 @@ void OpenGL::createDefaultTexture()
// primitives, which would create the need to use different "passthrough"
// shaders for untextured primitives vs images.
GLuint curtexture = state.textureUnits[state.curTextureUnit];
bindTexture(0);
GLuint curtexture = state.boundTextures[state.curTextureUnit];
glGenTextures(1, &state.defaultTexture);
bindTexture(state.defaultTexture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
@@ -246,8 +283,8 @@ void OpenGL::createDefaultTexture()
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
GLubyte pix = 255;
glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE8, 1, 1, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, &pix);
GLubyte pix[] = {255, 255, 255, 255};
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, pix);
bindTexture(curtexture);
}
@@ -269,54 +306,51 @@ Matrix &OpenGL::getTransform()
void OpenGL::prepareDraw()
{
TempDebugGroup debuggroup("Prepare OpenGL draw");
Shader *shader = Shader::current;
if (shader != nullptr)
{
// Make sure the active shader has the correct values for its
// love-provided uniforms.
shader->checkSetScreenParams();
// Make sure the Instance ID variable is up-to-date when
// pseudo-instancing is used.
if (state.lastPseudoInstanceID != 0 && shader->hasVertexAttrib(ATTRIB_PSEUDO_INSTANCE_ID))
{
glVertexAttrib1f((GLuint) ATTRIB_PSEUDO_INSTANCE_ID, 0.0f);
state.lastPseudoInstanceID = 0;
}
// We need to make sure antialiased Canvases are properly resolved
// before sampling from their textures in a shader.
// This is kind of a big hack. :(
for (auto &r : shader->getBoundRetainables())
{
// Even bigger hack! D:
Canvas *canvas = dynamic_cast<Canvas *>(r.second);
if (canvas != nullptr)
canvas->resolveMSAA();
}
}
const float *curproj = matrices.projection.back().getElements();
const float *lastproj = state.lastProjectionMatrix.getElements();
const Matrix &curproj = matrices.projection.back();
const Matrix &curxform = matrices.transform.back();
// We only need to re-upload the projection matrix if it's changed.
if (memcmp(curproj, lastproj, sizeof(float) * 16) != 0)
if (GLAD_ES_VERSION_2_0 && shader)
{
glMatrixMode(GL_PROJECTION);
glLoadMatrixf(curproj);
glMatrixMode(GL_MODELVIEW);
// Send built-in uniforms to the current shader.
shader->sendBuiltinMatrix(Shader::BUILTIN_TRANSFORM_MATRIX, 4, curxform.getElements(), 1);
shader->sendBuiltinMatrix(Shader::BUILTIN_PROJECTION_MATRIX, 4, curproj.getElements(), 1);
state.lastProjectionMatrix = matrices.projection.back();
Matrix tp_matrix(curproj * curxform);
shader->sendBuiltinMatrix(Shader::BUILTIN_TRANSFORM_PROJECTION_MATRIX, 4, tp_matrix.getElements(), 1);
shader->checkSetPointSize(state.pointSize);
}
const float *curxform = matrices.transform.back().getElements();
const float *lastxform = state.lastTransformMatrix.getElements();
// Same with the transform matrix.
if (memcmp(curxform, lastxform, sizeof(float) * 16) != 0)
else if (GLAD_VERSION_1_0)
{
glLoadMatrixf(curxform);
state.lastTransformMatrix = matrices.transform.back();
const Matrix &lastproj = state.lastProjectionMatrix;
const Matrix &lastxform = state.lastTransformMatrix;
// We only need to re-upload the projection matrix if it's changed.
if (memcmp(curproj.getElements(), lastproj.getElements(), sizeof(float) * 16) != 0)
{
glMatrixMode(GL_PROJECTION);
glLoadMatrixf(curproj.getElements());
glMatrixMode(GL_MODELVIEW);
state.lastProjectionMatrix = matrices.projection.back();
}
// Same with the transform matrix.
if (memcmp(curxform.getElements(), lastxform.getElements(), sizeof(float) * 16) != 0)
{
glLoadMatrixf(curxform.getElements());
state.lastTransformMatrix = matrices.transform.back();
}
}
}
@@ -332,61 +366,11 @@ void OpenGL::drawElements(GLenum mode, GLsizei count, GLenum type, const void *i
++stats.drawCalls;
}
void OpenGL::drawArraysInstanced(GLenum mode, GLint first, GLsizei count, GLsizei primcount)
{
Shader *shader = Shader::current;
if (GLEE_ARB_draw_instanced)
glDrawArraysInstancedARB(mode, first, count, primcount);
else
{
bool shaderHasID = shader && shader->hasVertexAttrib(ATTRIB_PSEUDO_INSTANCE_ID);
// Pseudo-instancing fallback.
for (int i = 0; i < primcount; i++)
{
if (shaderHasID)
glVertexAttrib1f((GLuint) ATTRIB_PSEUDO_INSTANCE_ID, (GLfloat) i);
glDrawArrays(mode, first, count);
}
if (shaderHasID)
state.lastPseudoInstanceID = primcount - 1;
}
++stats.drawCalls;
}
void OpenGL::drawElementsInstanced(GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei primcount)
{
Shader *shader = Shader::current;
if (GLEE_ARB_draw_instanced)
glDrawElementsInstancedARB(mode, count, type, indices, primcount);
else
{
bool shaderHasID = shader && shader->hasVertexAttrib(ATTRIB_PSEUDO_INSTANCE_ID);
// Pseudo-instancing fallback.
for (int i = 0; i < primcount; i++)
{
if (shaderHasID)
glVertexAttrib1f((GLuint) ATTRIB_PSEUDO_INSTANCE_ID, (GLfloat) i);
glDrawElements(mode, count, type, indices);
}
if (shaderHasID)
state.lastPseudoInstanceID = primcount - 1;
}
++stats.drawCalls;
}
void OpenGL::setColor(const Color &c)
{
glColor4ubv(&c.r);
GLfloat glc[] = {c.r / 255.0f, c.g / 255.0f, c.b / 255.0f, c.a / 255.0f};
glVertexAttrib4fv(ATTRIB_COLOR, glc);
state.color = c;
}
@@ -395,17 +379,6 @@ Color OpenGL::getColor() const
return state.color;
}
void OpenGL::setClearColor(const Color &c)
{
glClearColor(c.r / 255.0f, c.g / 255.0f, c.b / 255.0f, c.a / 255.0f);
state.clearColor = c;
}
Color OpenGL::getClearColor() const
{
return state.clearColor;
}
void OpenGL::setViewport(const OpenGL::Viewport &v)
{
glViewport(v.x, v.y, v.w, v.h);
@@ -441,75 +414,73 @@ OpenGL::Viewport OpenGL::getScissor() const
return state.scissor;
}
void OpenGL::setBlendState(const BlendState &blend)
void OpenGL::setPointSize(float size)
{
if (GLEE_VERSION_1_4 || GLEE_ARB_imaging)
glBlendEquation(blend.func);
else if (GLEE_EXT_blend_minmax && GLEE_EXT_blend_subtract)
glBlendEquationEXT(blend.func);
else
{
if (blend.func == GL_FUNC_REVERSE_SUBTRACT)
throw love::Exception("This graphics card does not support the subtractive blend mode!");
// GL_FUNC_ADD is the default even without access to glBlendEquation, so that'll still work.
}
if (GLAD_VERSION_1_0)
glPointSize(size);
if (blend.srcRGB == blend.srcA && blend.dstRGB == blend.dstA)
glBlendFunc(blend.srcRGB, blend.dstRGB);
else
{
if (GLEE_VERSION_1_4)
glBlendFuncSeparate(blend.srcRGB, blend.dstRGB, blend.srcA, blend.dstA);
else if (GLEE_EXT_blend_func_separate)
glBlendFuncSeparateEXT(blend.srcRGB, blend.dstRGB, blend.srcA, blend.dstA);
else
throw love::Exception("This graphics card does not support separated rgb and alpha blend functions!");
}
state.blend = blend;
state.pointSize = size;
}
OpenGL::BlendState OpenGL::getBlendState() const
float OpenGL::getPointSize() const
{
return state.blend;
return state.pointSize;
}
void OpenGL::bindFramebuffer(GLenum target, GLuint framebuffer)
{
glBindFramebuffer(target, framebuffer);
if (target == GL_FRAMEBUFFER)
++stats.framebufferBinds;
}
GLuint OpenGL::getDefaultFBO() const
{
#ifdef LOVE_IOS
// Hack: iOS uses a custom FBO.
return SDL_iPhoneGetViewFramebuffer(SDL_GL_GetCurrentWindow());
#else
return 0;
#endif
}
GLuint OpenGL::getDefaultTexture() const
{
return state.defaultTexture;
}
void OpenGL::setTextureUnit(int textureunit)
{
if (textureunit < 0 || (size_t) textureunit >= state.textureUnits.size())
if (textureunit < 0 || (size_t) textureunit >= state.boundTextures.size())
throw love::Exception("Invalid texture unit index (%d).", textureunit);
if (textureunit != state.curTextureUnit)
{
if (state.textureUnits.size() > 1)
glActiveTexture(GL_TEXTURE0 + textureunit);
else
throw love::Exception("Multitexturing is not supported.");
}
glActiveTexture(GL_TEXTURE0 + textureunit);
state.curTextureUnit = textureunit;
}
void OpenGL::bindTexture(GLuint texture)
{
if (texture != state.textureUnits[state.curTextureUnit])
if (texture != state.boundTextures[state.curTextureUnit])
{
state.textureUnits[state.curTextureUnit] = texture;
state.boundTextures[state.curTextureUnit] = texture;
glBindTexture(GL_TEXTURE_2D, texture);
}
}
void OpenGL::bindTextureToUnit(GLuint texture, int textureunit, bool restoreprev)
{
if (textureunit < 0 || (size_t) textureunit >= state.textureUnits.size())
if (textureunit < 0 || (size_t) textureunit >= state.boundTextures.size())
throw love::Exception("Invalid texture unit index.");
if (texture != state.textureUnits[textureunit])
if (texture != state.boundTextures[textureunit])
{
int oldtextureunit = state.curTextureUnit;
setTextureUnit(textureunit);
state.textureUnits[textureunit] = texture;
state.boundTextures[textureunit] = texture;
glBindTexture(GL_TEXTURE_2D, texture);
if (restoreprev)
@@ -521,7 +492,7 @@ void OpenGL::deleteTexture(GLuint texture)
{
// glDeleteTextures binds texture 0 to all texture units the deleted texture
// was bound to before deletion.
for (GLuint &texid : state.textureUnits)
for (GLuint &texid : state.boundTextures)
{
if (texid == texture)
texid = 0;
@@ -530,7 +501,7 @@ void OpenGL::deleteTexture(GLuint texture)
glDeleteTextures(1, &texture);
}
float OpenGL::setTextureFilter(graphics::Texture::Filter &f)
void OpenGL::setTextureFilter(graphics::Texture::Filter &f)
{
GLint gmin, gmag;
@@ -555,7 +526,6 @@ float OpenGL::setTextureFilter(graphics::Texture::Filter &f)
gmin = GL_LINEAR;
}
switch (f.mag)
{
case Texture::FILTER_NEAREST:
@@ -570,43 +540,33 @@ float OpenGL::setTextureFilter(graphics::Texture::Filter &f)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gmin);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gmag);
if (GLEE_EXT_texture_filter_anisotropic)
if (GLAD_EXT_texture_filter_anisotropic)
{
f.anisotropy = std::min(std::max(f.anisotropy, 1.0f), maxAnisotropy);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, f.anisotropy);
}
return f.anisotropy;
else
f.anisotropy = 1.0f;
}
void OpenGL::setTextureWrap(const graphics::Texture::Wrap &w)
{
GLint gs, gt;
switch (w.s)
auto glWrapMode = [](Texture::WrapMode wmode) -> GLint
{
case Texture::WRAP_CLAMP:
gs = GL_CLAMP_TO_EDGE;
break;
case Texture::WRAP_REPEAT:
default:
gs = GL_REPEAT;
break;
}
switch (wmode)
{
case Texture::WRAP_CLAMP:
default:
return GL_CLAMP_TO_EDGE;
case Texture::WRAP_REPEAT:
return GL_REPEAT;
case Texture::WRAP_MIRRORED_REPEAT:
return GL_MIRRORED_REPEAT;
}
};
switch (w.t)
{
case Texture::WRAP_CLAMP:
gt = GL_CLAMP_TO_EDGE;
break;
case Texture::WRAP_REPEAT:
default:
gt = GL_REPEAT;
break;
}
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, gs);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, gt);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, glWrapMode(w.s));
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, glWrapMode(w.t));
}
int OpenGL::getMaxTextureSize() const
@@ -619,6 +579,16 @@ int OpenGL::getMaxRenderTargets() const
return maxRenderTargets;
}
int OpenGL::getMaxRenderbufferSamples() const
{
return maxRenderbufferSamples;
}
int OpenGL::getMaxTextureUnits() const
{
return maxTextureUnits;
}
void OpenGL::updateTextureMemorySize(size_t oldsize, size_t newsize)
{
int64 memsize = (int64) stats.textureMemory + ((int64 )newsize - (int64) oldsize);
@@ -641,9 +611,8 @@ const char *OpenGL::debugSeverityString(GLenum severity)
case GL_DEBUG_SEVERITY_LOW:
return "low";
default:
break;
return "unknown";
}
return "unknown";
}
const char *OpenGL::debugSourceString(GLenum source)
@@ -663,9 +632,8 @@ const char *OpenGL::debugSourceString(GLenum source)
case GL_DEBUG_SOURCE_OTHER:
return "other";
default:
break;
return "unknown";
}
return "unknown";
}
const char *OpenGL::debugTypeString(GLenum type)
@@ -685,9 +653,8 @@ const char *OpenGL::debugTypeString(GLenum type)
case GL_DEBUG_TYPE_OTHER:
return "other";
default:
break;
return "unknown";
}
return "unknown";
}
+97 -64
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2014 LOVE Development Team
* Copyright (c) 2006-2015 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
@@ -21,13 +21,15 @@
#ifndef LOVE_GRAPHICS_OPENGL_OPENGL_H
#define LOVE_GRAPHICS_OPENGL_OPENGL_H
#include "GLee.h"
// LOVE
#include "common/config.h"
#include "graphics/Color.h"
#include "graphics/Texture.h"
#include "common/Matrix.h"
// GLAD
#include "libraries/glad/gladfuncs.hpp"
// C++
#include <vector>
#include <stack>
@@ -42,6 +44,21 @@ namespace graphics
namespace opengl
{
// Awful, but the library uses the namespace in order to use the functions sanely
// with proper autocomplete in IDEs while having name mangling safety -
// no clashes with other GL libraries when linking, etc.
using namespace glad;
// Vertex attribute indices used in shaders by LOVE. The values map to OpenGL
// generic vertex attribute indices.
enum VertexAttribID
{
ATTRIB_POS = 0,
ATTRIB_TEXCOORD,
ATTRIB_COLOR,
ATTRIB_MAX_ENUM
};
/**
* Thin layer between OpenGL and the rest of the program.
* Internally shadows some OpenGL context state for improved efficiency and
@@ -56,53 +73,32 @@ public:
// OpenGL GPU vendors.
enum Vendor
{
VENDOR_ATI_AMD,
VENDOR_AMD,
VENDOR_NVIDIA,
VENDOR_INTEL,
VENDOR_MESA_SOFT, // Software renderer.
VENDOR_APPLE, // Software renderer.
VENDOR_APPLE, // Software renderer on desktops.
VENDOR_MICROSOFT, // Software renderer.
VENDOR_IMGTEC,
VENDOR_ARM,
VENDOR_QUALCOMM,
VENDOR_BROADCOM,
VENDOR_VIVANTE,
VENDOR_UNKNOWN
};
// Vertex attributes used in shaders by LOVE. The values map to OpenGL
// generic vertex attribute indices, when applicable.
// LOVE uses the old hard-coded attribute APIs for positions, colors, etc.
// (for now.)
enum VertexAttrib
{
// Instance ID when pseudo-instancing is used.
ATTRIB_PSEUDO_INSTANCE_ID = 1,
ATTRIB_MAX_ENUM
};
// A rectangle representing an OpenGL viewport or a scissor box.
struct Viewport
{
int x, y;
int w, h;
Viewport()
: x(0), y(0), w(0), h(0)
{}
Viewport(int _x, int _y, int _w, int _h)
: x(_x), y(_y), w(_w), h(_h)
{}
bool operator == (const Viewport &rhs) const
{
return x == rhs.x && y == rhs.y && w == rhs.w && h == rhs.h;
}
};
struct BlendState
{
GLenum srcRGB, srcA;
GLenum dstRGB, dstA;
GLenum func;
};
struct
{
std::vector<Matrix> transform;
@@ -133,19 +129,48 @@ public:
OpenGL &gl;
};
class TempDebugGroup
{
public:
#if defined(DEBUG) && DEBUG == 1
TempDebugGroup(const char *name)
{
if (GLAD_EXT_debug_marker)
glPushGroupMarkerEXT(0, (const GLchar *) name);
}
#else
TempDebugGroup(const char *) {}
#endif
#if defined(DEBUG) && DEBUG == 1
~TempDebugGroup()
{
if (GLAD_EXT_debug_marker)
glPopGroupMarkerEXT();
}
#endif
};
struct Stats
{
size_t textureMemory;
int drawCalls;
int framebufferBinds;
} stats;
OpenGL();
/**
* Initializes some required context state based on current and default
* OpenGL state. Call this directly after creating an OpenGL context!
* Initializes the active OpenGL context.
**/
void initContext();
bool initContext();
/**
* Sets up some required context state based on current and default OpenGL
* state. Call this directly after initializing an OpenGL context!
**/
void setupContext();
/**
* Marks current context state as invalid and deletes OpenGL objects owned
@@ -171,13 +196,6 @@ public:
void drawArrays(GLenum mode, GLint first, GLsizei count);
void drawElements(GLenum mode, GLsizei count, GLenum type, const void *indices);
/**
* glDrawArraysInstanced and glDrawElementsInstanced with pseudo-instancing
* fallbacks. They also increment the draw-call counter (once per call).
**/
void drawArraysInstanced(GLenum mode, GLint first, GLsizei count, GLsizei primcount);
void drawElementsInstanced(GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei primcount);
/**
* Sets the current constant color.
**/
@@ -188,16 +206,6 @@ public:
**/
Color getColor() const;
/**
* Sets the current clear color for all framebuffer objects.
**/
void setClearColor(const Color &c);
/**
* Gets the current clear color.
**/
Color getClearColor() const;
/**
* Sets the OpenGL rendering viewport to the specified rectangle.
* The y-coordinate starts at the top.
@@ -221,15 +229,30 @@ public:
Viewport getScissor() const;
/**
* Sets blending functionality.
* Note: This does not globally enable or disable blending.
* Sets the global point size.
**/
void setBlendState(const BlendState &blend);
void setPointSize(float size);
/**
* Gets the currently set blending functionality.
* Gets the global point size.
**/
BlendState getBlendState() const;
float getPointSize() const;
/**
* Binds a Framebuffer Object to the specified target.
**/
void bindFramebuffer(GLenum target, GLuint framebuffer);
/**
* This will usually be 0 (system drawable), but some platforms require a
* non-zero FBO for rendering.
**/
GLuint getDefaultFBO() const;
/**
* Gets the ID for love's default texture (used for "untextured" primitives.)
**/
GLuint getDefaultTexture() const;
/**
* Helper for setting the active texture unit.
@@ -260,9 +283,10 @@ public:
/**
* Sets the texture filter mode for the currently bound texture.
* Returns the actual amount of anisotropic filtering set.
* The anisotropy parameter of the argument is set to the actual amount of
* anisotropy that was used.
**/
float setTextureFilter(graphics::Texture::Filter &f);
void setTextureFilter(graphics::Texture::Filter &f);
/**
* Sets the texture wrap mode for the currently bound texture.
@@ -279,6 +303,16 @@ public:
**/
int getMaxRenderTargets() const;
/**
* Returns the maximum supported number of MSAA samples for renderbuffers.
**/
int getMaxRenderbufferSamples() const;
/**
* Returns the maximum number of accessible texture units.
**/
int getMaxTextureUnits() const;
void updateTextureMemorySize(size_t oldsize, size_t newsize);
/**
@@ -304,6 +338,8 @@ private:
float maxAnisotropy;
int maxTextureSize;
int maxRenderTargets;
int maxRenderbufferSamples;
int maxTextureUnits;
Vendor vendor;
@@ -313,10 +349,8 @@ private:
// Current constant color.
Color color;
Color clearColor;
// Texture unit state (currently bound texture for each texture unit.)
std::vector<GLuint> textureUnits;
std::vector<GLuint> boundTextures;
// Currently active texture unit.
int curTextureUnit;
@@ -324,10 +358,9 @@ private:
Viewport viewport;
Viewport scissor;
BlendState blend;
float pointSize;
// The last ID value used for pseudo-instancing.
int lastPseudoInstanceID;
GLuint defaultTexture;
Matrix lastProjectionMatrix;
Matrix lastTransformMatrix;
+50 -26
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2014 LOVE Development Team
* Copyright (c) 2006-2015 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
@@ -64,6 +64,7 @@ ParticleSystem::ParticleSystem(Texture *texture, uint32 size)
, pHead(nullptr)
, pTail(nullptr)
, particleVerts(nullptr)
, quadIndices(1)
, texture(texture)
, active(true)
, insertMode(INSERT_MODE_TOP)
@@ -94,8 +95,8 @@ ParticleSystem::ParticleSystem(Texture *texture, uint32 size)
, spinStart(0)
, spinEnd(0)
, spinVariation(0)
, offsetX(float(texture->getWidth())*0.5f)
, offsetY(float(texture->getHeight())*0.5f)
, offset(float(texture->getWidth())*0.5f, float(texture->getHeight())*0.5f)
, defaultOffset(true)
, relativeRotation(false)
{
if (size == 0 || size > MAX_PARTICLES)
@@ -112,6 +113,7 @@ ParticleSystem::ParticleSystem(const ParticleSystem &p)
, pHead(nullptr)
, pTail(nullptr)
, particleVerts(nullptr)
, quadIndices(p.quadIndices)
, texture(p.texture)
, active(p.active)
, insertMode(p.insertMode)
@@ -146,8 +148,8 @@ ParticleSystem::ParticleSystem(const ParticleSystem &p)
, spinStart(p.spinStart)
, spinEnd(p.spinEnd)
, spinVariation(p.spinVariation)
, offsetX(p.offsetX)
, offsetY(p.offsetY)
, offset(p.offset)
, defaultOffset(p.defaultOffset)
, colors(p.colors)
, quads(p.quads)
, relativeRotation(p.relativeRotation)
@@ -165,6 +167,17 @@ ParticleSystem *ParticleSystem::clone()
return new ParticleSystem(*this);
}
void ParticleSystem::resetOffset()
{
if (quads.empty())
offset = love::Vector(float(texture->getWidth())*0.5f, float(texture->getHeight())*0.5f);
else
{
Quad::Viewport v = quads[0]->getViewport();
offset = love::Vector(v.x*0.5f, v.y*0.5f);
}
}
void ParticleSystem::createBuffers(size_t size)
{
try
@@ -196,6 +209,7 @@ void ParticleSystem::setBufferSize(uint32 size)
{
if (size == 0 || size > MAX_PARTICLES)
throw love::Exception("Invalid buffer size");
quadIndices = VertexIndex(size);
deleteBuffers();
createBuffers(size);
reset();
@@ -420,6 +434,9 @@ ParticleSystem::Particle *ParticleSystem::removeParticle(Particle *p)
void ParticleSystem::setTexture(Texture *tex)
{
texture.set(tex);
if (defaultOffset)
resetOffset();
}
Texture *ParticleSystem::getTexture() const
@@ -680,13 +697,13 @@ float ParticleSystem::getSpinVariation() const
void ParticleSystem::setOffset(float x, float y)
{
offsetX = x;
offsetY = y;
offset = love::Vector(x, y);
defaultOffset = false;
}
love::Vector ParticleSystem::getOffset() const
{
return love::Vector(offsetX, offsetY);
return offset;
}
void ParticleSystem::setColor(const Color &color)
@@ -727,6 +744,9 @@ void ParticleSystem::setQuads(const std::vector<Quad *> &newQuads)
quadlist.push_back(q);
quads = quadlist;
if (defaultOffset)
resetOffset();
}
void ParticleSystem::setQuads()
@@ -739,7 +759,7 @@ std::vector<Quad *> ParticleSystem::getQuads() const
std::vector<Quad *> quadlist;
quadlist.reserve(quads.size());
for (const Object::StrongRef<Quad> &q : quads)
for (const StrongRef<Quad> &q : quads)
quadlist.push_back(q.get());
return quadlist;
@@ -832,6 +852,8 @@ void ParticleSystem::draw(float x, float y, float angle, float sx, float sy, flo
if (pCount == 0 || texture.get() == nullptr || pMem == nullptr || particleVerts == nullptr)
return;
OpenGL::TempDebugGroup debuggroup("ParticleSystem draw");
Color curcolor = gl.getColor();
static Matrix t;
@@ -853,7 +875,7 @@ void ParticleSystem::draw(float x, float y, float angle, float sx, float sy, flo
textureVerts = quads[p->quadIndex]->getVertices();
// particle vertices are image vertices transformed by particle information
t.setTransformation(p->position[0], p->position[1], p->angle, p->size, p->size, offsetX, offsetY, 0.0f, 0.0f);
t.setTransformation(p->position[0], p->position[1], p->angle, p->size, p->size, offset.x, offset.y, 0.0f, 0.0f);
t.transform(pVerts, textureVerts, 4);
// set the texture coordinate and color data for particle vertices
@@ -873,24 +895,26 @@ void ParticleSystem::draw(float x, float y, float angle, float sx, float sy, flo
p = p->next;
}
texture->predraw();
glEnableClientState(GL_COLOR_ARRAY);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(Vertex), (GLvoid *) &particleVerts[0].r);
glVertexPointer(2, GL_FLOAT, sizeof(Vertex), (GLvoid *) &particleVerts[0].x);
glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), (GLvoid *) &particleVerts[0].s);
gl.bindTexture(*(GLuint *) texture->getHandle());
gl.prepareDraw();
gl.drawArrays(GL_QUADS, 0, pCount * 4);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_COLOR_ARRAY);
glEnableVertexAttribArray(ATTRIB_COLOR);
glEnableVertexAttribArray(ATTRIB_POS);
glEnableVertexAttribArray(ATTRIB_TEXCOORD);
texture->postdraw();
glVertexAttribPointer(ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(Vertex), &particleVerts[0].r);
glVertexAttribPointer(ATTRIB_POS, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), &particleVerts[0].x);
glVertexAttribPointer(ATTRIB_TEXCOORD, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), &particleVerts[0].s);
{
GLsizei count = (GLsizei) quadIndices.getIndexCount(pCount);
GLBuffer::Bind ibo_bind(*quadIndices.getBuffer());
gl.drawElements(GL_TRIANGLES, count, quadIndices.getType(), quadIndices.getPointer(0));
}
glDisableVertexAttribArray(ATTRIB_TEXCOORD);
glDisableVertexAttribArray(ATTRIB_POS);
glDisableVertexAttribArray(ATTRIB_COLOR);
gl.setColor(curcolor);
}
@@ -984,7 +1008,7 @@ void ParticleSystem::update(float dt)
{
s = t * (float) k; // [0:numquads-1] (clamped below)
i = (s > 0.0f) ? (size_t) s : 0;
p->quadIndex = (i < k) ? i : k - 1;
p->quadIndex = (int) ((i < k) ? i : k - 1);
}
// Next particle.
+14 -6
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2014 LOVE Development Team
* Copyright (c) 2006-2015 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
@@ -28,7 +28,8 @@
#include "graphics/Drawable.h"
#include "graphics/Color.h"
#include "graphics/Quad.h"
#include "Texture.h"
#include "graphics/Texture.h"
#include "GLBuffer.h"
// STL
#include <vector>
@@ -575,8 +576,11 @@ protected:
// array of transformed vertex data for all particles, for drawing
Vertex *particleVerts;
// Vertex index buffer.
VertexIndex quadIndices;
// The texture to be drawn.
Object::StrongRef<Texture> texture;
StrongRef<Texture> texture;
// Whether the particle emitter is active.
bool active;
@@ -649,17 +653,21 @@ protected:
float spinVariation;
// Offsets
float offsetX;
float offsetY;
love::Vector offset;
// Is the ParticleSystem using a default offset?
bool defaultOffset;
// Color.
std::vector<Colorf> colors;
// Quads.
std::vector<Object::StrongRef<Quad>> quads;
std::vector<StrongRef<Quad>> quads;
bool relativeRotation;
void resetOffset();
void createBuffers(size_t size);
void deleteBuffers();
+61 -13
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2014 LOVE Development Team
* Copyright (c) 2006-2015 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
@@ -18,14 +18,15 @@
* 3. This notice may not be removed or altered from any source distribution.
**/
#include <algorithm>
// LOVE
#include "Polyline.h"
// OpenGL
#include "OpenGL.h"
// C++
#include <algorithm>
// treat adjacent segments with angles between their directions <5 degree as straight
static const float LINES_PARALLEL_EPS = 0.05f;
@@ -327,13 +328,52 @@ Polyline::~Polyline()
void Polyline::draw()
{
OpenGL::TempDebugGroup debuggroup("Line draw");
GLushort *indices = nullptr;
// TODO: We should probably be using a reusable index buffer.
if (use_quad_indices)
{
size_t numindices = (vertex_count / 4) * 6;
if (overdraw)
numindices = std::max(numindices, (overdraw_vertex_count / 4) * 6);
try
{
indices = new GLushort[numindices];
}
catch (std::bad_alloc &)
{
throw love::Exception("Out of memory.");
}
// Fill the index array to make 2 triangles from each quad.
for (size_t i = 0; i < numindices / 6; i++)
{
// First triangle.
indices[i * 6 + 0] = GLushort(i * 4 + 0);
indices[i * 6 + 1] = GLushort(i * 4 + 1);
indices[i * 6 + 2] = GLushort(i * 4 + 2);
// Second triangle.
indices[i * 6 + 3] = GLushort(i * 4 + 2);
indices[i * 6 + 4] = GLushort(i * 4 + 1);
indices[i * 6 + 5] = GLushort(i * 4 + 3);
}
}
gl.prepareDraw();
// draw the core line
gl.bindTexture(0);
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(2, GL_FLOAT, 0, (const GLvoid *)vertices);
gl.drawArrays(draw_mode, 0, vertex_count);
gl.bindTexture(gl.getDefaultTexture());
glEnableVertexAttribArray(ATTRIB_POS);
glVertexAttribPointer(ATTRIB_POS, 2, GL_FLOAT, GL_FALSE, 0, vertices);
if (use_quad_indices)
gl.drawElements(draw_mode, (int) (vertex_count / 4) * 6, GL_UNSIGNED_SHORT, indices);
else
gl.drawArrays(draw_mode, 0, (int) vertex_count);
if (overdraw)
{
@@ -342,18 +382,26 @@ void Polyline::draw()
Color *colors = new Color[overdraw_vertex_count];
fill_color_array(colors, c);
glEnableClientState(GL_COLOR_ARRAY);
glColorPointer(4, GL_UNSIGNED_BYTE, 0, colors);
glVertexPointer(2, GL_FLOAT, 0, (const GLvoid *)overdraw);
gl.drawArrays(draw_mode, 0, overdraw_vertex_count);
glDisableClientState(GL_COLOR_ARRAY);
glEnableVertexAttribArray(ATTRIB_COLOR);
glVertexAttribPointer(ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, colors);
glVertexAttribPointer(ATTRIB_POS, 2, GL_FLOAT, GL_FALSE, 0, overdraw);
if (use_quad_indices)
gl.drawElements(draw_mode, (int) (overdraw_vertex_count / 4) * 6, GL_UNSIGNED_SHORT, indices);
else
gl.drawArrays(draw_mode, 0, (int) overdraw_vertex_count);
glDisableVertexAttribArray(ATTRIB_COLOR);
delete[] colors;
gl.setColor(c);
}
glDisableClientState(GL_VERTEX_ARRAY);
glDisableVertexAttribArray(ATTRIB_POS);
if (indices)
delete[] indices;
}
void Polyline::fill_color_array(Color *colors, const Color &c)
+9 -5
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2014 LOVE Development Team
* Copyright (c) 2006-2015 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
@@ -21,14 +21,16 @@
#ifndef LOVE_GRAPHICS_OPENGL_POLYLINE_H
#define LOVE_GRAPHICS_OPENGL_POLYLINE_H
#include <vector>
// LOVE
#include "common/config.h"
#include "common/Vector.h"
// OpenGL
#include "OpenGL.h"
// C++
#include <vector>
namespace love
{
namespace graphics
@@ -43,12 +45,13 @@ namespace opengl
class Polyline
{
public:
Polyline(GLenum mode = GL_TRIANGLE_STRIP)
Polyline(GLenum mode = GL_TRIANGLE_STRIP, bool quadindices = false)
: vertices(NULL)
, overdraw(NULL)
, vertex_count(0)
, overdraw_vertex_count(0)
, draw_mode(mode)
, use_quad_indices(quadindices)
{}
virtual ~Polyline();
@@ -90,6 +93,7 @@ protected:
size_t vertex_count;
size_t overdraw_vertex_count;
GLenum draw_mode;
bool use_quad_indices;
}; // Polyline
@@ -102,7 +106,7 @@ class NoneJoinPolyline : public Polyline
{
public:
NoneJoinPolyline()
: Polyline(GL_QUADS)
: Polyline(GL_TRIANGLES, true)
{}
void render(const float *vertices, size_t count, float halfwidth, float pixel_size, bool draw_overdraw)
+167 -130
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2014 LOVE Development Team
* Copyright (c) 2006-2015 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
@@ -62,8 +62,10 @@ namespace
Shader *Shader::current = nullptr;
Shader *Shader::defaultShader = nullptr;
Shader::ShaderSource Shader::defaultCode[Graphics::RENDERER_MAX_ENUM];
GLint Shader::maxTexUnits = 0;
std::vector<int> Shader::textureCounters;
Shader::Shader(const ShaderSource &source)
@@ -73,20 +75,14 @@ Shader::Shader(const ShaderSource &source)
, builtinAttributes()
, lastCanvas((Canvas *) -1)
, lastViewport()
, lastPointSize(0.0f)
{
if (source.vertex.empty() && source.pixel.empty())
throw love::Exception("Cannot create shader: no source code!");
if (maxTexUnits <= 0)
{
GLint maxtexunits;
glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &maxtexunits);
maxTexUnits = std::max(maxtexunits - 1, 0);
}
// initialize global texture id counters if needed
if (textureCounters.size() < (size_t) maxTexUnits)
textureCounters.resize(maxTexUnits, 0);
if ((int) textureCounters.size() < gl.getMaxTextureUnits() - 1)
textureCounters.resize(gl.getMaxTextureUnits() - 1, 0);
// load shader source and create program object
loadVolatile();
@@ -126,9 +122,6 @@ GLuint Shader::compileCode(ShaderStage stage, const std::string &code)
break;
}
// clear existing errors
while (glGetError() != GL_NO_ERROR);
GLuint shaderid = glCreateShader(glstage);
if (shaderid == 0)
@@ -218,19 +211,32 @@ void Shader::mapActiveUniforms()
bool Shader::loadVolatile()
{
OpenGL::TempDebugGroup debuggroup("Shader load");
// Recreating the shader program will invalidate uniforms that rely on these.
lastCanvas = (Canvas *) -1;
lastViewport = OpenGL::Viewport();
lastPointSize = 0.0f;
// zero out active texture list
activeTexUnits.clear();
activeTexUnits.insert(activeTexUnits.begin(), maxTexUnits, 0);
activeTexUnits.insert(activeTexUnits.begin(), gl.getMaxTextureUnits() - 1, 0);
std::vector<GLuint> shaderids;
const ShaderSource *defaults = &defaultCode[Graphics::RENDERER_OPENGL];
if (GLAD_ES_VERSION_2_0)
defaults = &defaultCode[Graphics::RENDERER_OPENGLES];
// The shader program must have both vertex and pixel shader stages.
const std::string &vertexcode = shaderSource.vertex.empty() ? defaults->vertex : shaderSource.vertex;
const std::string &pixelcode = shaderSource.pixel.empty() ? defaults->pixel : shaderSource.pixel;
try
{
if (!shaderSource.vertex.empty())
shaderids.push_back(compileCode(STAGE_VERTEX, shaderSource.vertex));
if (!shaderSource.pixel.empty())
shaderids.push_back(compileCode(STAGE_PIXEL, shaderSource.pixel));
shaderids.push_back(compileCode(STAGE_VERTEX, vertexcode));
shaderids.push_back(compileCode(STAGE_PIXEL, pixelcode));
}
catch (love::Exception &)
{
@@ -239,9 +245,6 @@ bool Shader::loadVolatile()
throw;
}
if (shaderids.empty())
throw love::Exception("Cannot create shader: no valid source code!");
program = glCreateProgram();
if (program == 0)
@@ -255,18 +258,10 @@ bool Shader::loadVolatile()
glAttachShader(program, id);
// Bind generic vertex attribute indices to names in the shader.
for (int i = 0; i < int(OpenGL::ATTRIB_MAX_ENUM); i++)
for (int i = 0; i < int(ATTRIB_MAX_ENUM); i++)
{
OpenGL::VertexAttrib attrib = (OpenGL::VertexAttrib) i;
// FIXME: We skip this both because pseudo-instancing is temporarily
// disabled (see graphics.lua), and because binding a non-existant
// attribute name to a location causes a shader linker warning.
if (attrib == OpenGL::ATTRIB_PSEUDO_INSTANCE_ID)
continue;
const char *name = nullptr;
if (attribNames.find(attrib, name))
if (attribNames.find((VertexAttribID) i, name))
glBindAttribLocation(program, i, (const GLchar *) name);
}
@@ -290,10 +285,10 @@ bool Shader::loadVolatile()
// Retreive all active uniform variables in this shader from OpenGL.
mapActiveUniforms();
for (int i = 0; i < int(OpenGL::ATTRIB_MAX_ENUM); i++)
for (int i = 0; i < int(ATTRIB_MAX_ENUM); i++)
{
const char *name = nullptr;
if (attribNames.find(OpenGL::VertexAttrib(i), name))
if (attribNames.find(VertexAttribID(i), name))
builtinAttributes[i] = glGetAttribLocation(program, name);
else
builtinAttributes[i] = -1;
@@ -304,6 +299,7 @@ bool Shader::loadVolatile()
// make sure glUseProgram gets called.
current = nullptr;
attach();
checkSetScreenParams();
}
return true;
@@ -329,7 +325,7 @@ void Shader::unloadVolatile()
// active texture list is probably invalid, clear it
activeTexUnits.clear();
activeTexUnits.insert(activeTexUnits.begin(), maxTexUnits, 0);
activeTexUnits.resize(gl.getMaxTextureUnits() - 1, 0);
// same with uniform location list
uniforms.clear();
@@ -404,6 +400,14 @@ void Shader::attach(bool temporary)
void Shader::detach()
{
if (defaultShader)
{
if (current != defaultShader)
defaultShader->attach();
return;
}
if (current != nullptr)
glUseProgram(0);
@@ -421,72 +425,6 @@ const Shader::Uniform &Shader::getUniform(const std::string &name) const
return it->second;
}
int Shader::getUniformTypeSize(GLenum type) const
{
switch (type)
{
case GL_INT:
case GL_FLOAT:
case GL_BOOL:
case GL_SAMPLER_1D:
case GL_SAMPLER_2D:
case GL_SAMPLER_3D:
return 1;
case GL_INT_VEC2:
case GL_FLOAT_VEC2:
case GL_FLOAT_MAT2:
case GL_BOOL_VEC2:
return 2;
case GL_INT_VEC3:
case GL_FLOAT_VEC3:
case GL_FLOAT_MAT3:
case GL_BOOL_VEC3:
return 3;
case GL_INT_VEC4:
case GL_FLOAT_VEC4:
case GL_FLOAT_MAT4:
case GL_BOOL_VEC4:
return 4;
default:
break;
}
return 1;
}
Shader::UniformType Shader::getUniformBaseType(GLenum type) const
{
switch (type)
{
case GL_INT:
case GL_INT_VEC2:
case GL_INT_VEC3:
case GL_INT_VEC4:
return UNIFORM_INT;
case GL_FLOAT:
case GL_FLOAT_VEC2:
case GL_FLOAT_VEC3:
case GL_FLOAT_VEC4:
case GL_FLOAT_MAT2:
case GL_FLOAT_MAT3:
case GL_FLOAT_MAT4:
return UNIFORM_FLOAT;
case GL_BOOL:
case GL_BOOL_VEC2:
case GL_BOOL_VEC3:
case GL_BOOL_VEC4:
return UNIFORM_BOOL;
case GL_SAMPLER_1D:
case GL_SAMPLER_2D:
case GL_SAMPLER_3D:
return UNIFORM_SAMPLER;
default:
break;
}
return UNIFORM_UNKNOWN;
}
void Shader::checkSetUniformError(const Uniform &u, int size, int count, UniformType sendtype) const
{
if (!program)
@@ -587,7 +525,7 @@ void Shader::sendMatrix(const std::string &name, int size, const GLfloat *m, int
void Shader::sendTexture(const std::string &name, Texture *texture)
{
GLuint gltex = texture->getGLTexture();
GLuint gltex = *(GLuint *) texture->getHandle();
TemporaryAttacher attacher(this);
@@ -597,13 +535,10 @@ void Shader::sendTexture(const std::string &name, Texture *texture)
checkSetUniformError(u, 1, 1, UNIFORM_SAMPLER);
// bind texture to assigned texture unit and send uniform to shader program
gl.bindTextureToUnit(gltex, texunit, false);
gl.bindTextureToUnit(gltex, texunit, true);
glUniform1i(u.location, texunit);
// reset texture unit
gl.setTextureUnit(0);
// increment global shader texture id counter for this texture unit, if we haven't already
if (activeTexUnits[texunit-1] == 0)
++textureCounters[texunit-1];
@@ -616,11 +551,12 @@ void Shader::sendTexture(const std::string &name, Texture *texture)
void Shader::retainObject(const std::string &name, Object *object)
{
object->retain();
auto it = boundRetainables.find(name);
if (it != boundRetainables.end())
it->second->release();
object->retain();
boundRetainables[name] = object;
}
@@ -639,7 +575,7 @@ int Shader::getTextureUnit(const std::string &name)
if (freeunit_it != textureCounters.end())
{
// we don't want to use unit 0
texunit = std::distance(textureCounters.begin(), freeunit_it) + 1;
texunit = (int) std::distance(textureCounters.begin(), freeunit_it) + 1;
}
else
{
@@ -650,7 +586,7 @@ int Shader::getTextureUnit(const std::string &name)
throw love::Exception("No more texture units available for shader.");
// we don't want to use unit 0
texunit = std::distance(activeTexUnits.begin(), nextunit_it) + 1;
texunit = (int) std::distance(activeTexUnits.begin(), nextunit_it) + 1;
}
texUnitPool[name] = texunit;
@@ -674,7 +610,7 @@ Shader::UniformType Shader::getExternVariable(const std::string &name, int &comp
return it->second.baseType;
}
bool Shader::hasVertexAttrib(OpenGL::VertexAttrib attrib) const
bool Shader::hasVertexAttrib(VertexAttribID attrib) const
{
return builtinAttributes[int(attrib)] != -1;
}
@@ -684,6 +620,33 @@ bool Shader::hasBuiltinUniform(BuiltinUniform builtin) const
return builtinUniforms[int(builtin)] != -1;
}
bool Shader::sendBuiltinMatrix(BuiltinUniform builtin, int size, const GLfloat *m, int count)
{
if (!hasBuiltinUniform(builtin))
return false;
GLint location = builtinUniforms[GLint(builtin)];
TemporaryAttacher attacher(this);
switch (size)
{
case 2:
glUniformMatrix2fv(location, count, GL_FALSE, m);
break;
case 3:
glUniformMatrix3fv(location, count, GL_FALSE, m);
break;
case 4:
glUniformMatrix4fv(location, count, GL_FALSE, m);
break;
default:
return false;
}
return true;
}
bool Shader::sendBuiltinFloat(BuiltinUniform builtin, int size, const GLfloat *vec, int count)
{
if (!hasBuiltinUniform(builtin))
@@ -710,7 +673,7 @@ bool Shader::sendBuiltinFloat(BuiltinUniform builtin, int size, const GLfloat *v
default:
return false;
}
return true;
}
@@ -722,26 +685,26 @@ void Shader::checkSetScreenParams()
return;
// In the shader, we do pixcoord.y = gl_FragCoord.y * params.z + params.w.
// This lets us flip pixcoord.y when needed, to be consistent (Canvases
// have flipped y-values for pixel coordinates.)
// This lets us flip pixcoord.y when needed, to be consistent (drawing with
// no Canvas active makes the y-values for pixel coordinates flipped.)
GLfloat params[] = {
(GLfloat) view.w, (GLfloat) view.h,
0.0f, 0.0f,
};
if (Canvas::current != nullptr)
{
// gl_FragCoord.y is flipped in Canvases, so we un-flip:
// pixcoord.y = gl_FragCoord.y * -1.0 + height.
params[2] = -1.0f;
params[3] = (GLfloat) view.h;
}
else
{
// No flipping: pixcoord.y = gl_FragCoord.y * 1.0 + 0.0.
params[2] = 1.0f;
params[3] = 0.0f;
}
else
{
// gl_FragCoord.y is flipped when drawing to the screen, so we un-flip:
// pixcoord.y = gl_FragCoord.y * -1.0 + height.
params[2] = -1.0f;
params[3] = (GLfloat) view.h;
}
sendBuiltinFloat(BUILTIN_SCREEN_SIZE, 4, params, 1);
@@ -749,6 +712,16 @@ void Shader::checkSetScreenParams()
lastViewport = view;
}
void Shader::checkSetPointSize(float size)
{
if (size == lastPointSize)
return;
sendBuiltinFloat(BUILTIN_POINT_SIZE, 1, &size, 1);
lastPointSize = size;
}
const std::map<std::string, Object *> &Shader::getBoundRetainables() const
{
return boundRetainables;
@@ -756,11 +729,7 @@ const std::map<std::string, Object *> &Shader::getBoundRetainables() const
std::string Shader::getGLSLVersion()
{
const char *tmp = nullptr;
// GL_SHADING_LANGUAGE_VERSION isn't available in OpenGL < 2.0.
if (GLEE_VERSION_2_0 || GLEE_ARB_shading_language_100)
tmp = (const char *) glGetString(GL_SHADING_LANGUAGE_VERSION);
const char *tmp = (const char *) glGetString(GL_SHADING_LANGUAGE_VERSION);
if (tmp == nullptr)
return "0.0";
@@ -777,7 +746,69 @@ std::string Shader::getGLSLVersion()
bool Shader::isSupported()
{
return GLEE_VERSION_2_0 && getGLSLVersion() >= "1.2";
return GLAD_ES_VERSION_2_0 || (getGLSLVersion() >= "1.2");
}
int Shader::getUniformTypeSize(GLenum type) const
{
switch (type)
{
case GL_INT:
case GL_FLOAT:
case GL_BOOL:
case GL_SAMPLER_1D:
case GL_SAMPLER_2D:
case GL_SAMPLER_3D:
return 1;
case GL_INT_VEC2:
case GL_FLOAT_VEC2:
case GL_FLOAT_MAT2:
case GL_BOOL_VEC2:
return 2;
case GL_INT_VEC3:
case GL_FLOAT_VEC3:
case GL_FLOAT_MAT3:
case GL_BOOL_VEC3:
return 3;
case GL_INT_VEC4:
case GL_FLOAT_VEC4:
case GL_FLOAT_MAT4:
case GL_BOOL_VEC4:
return 4;
default:
return 1;
}
}
Shader::UniformType Shader::getUniformBaseType(GLenum type) const
{
switch (type)
{
case GL_INT:
case GL_INT_VEC2:
case GL_INT_VEC3:
case GL_INT_VEC4:
return UNIFORM_INT;
case GL_FLOAT:
case GL_FLOAT_VEC2:
case GL_FLOAT_VEC3:
case GL_FLOAT_VEC4:
case GL_FLOAT_MAT2:
case GL_FLOAT_MAT3:
case GL_FLOAT_MAT4:
return UNIFORM_FLOAT;
case GL_BOOL:
case GL_BOOL_VEC2:
case GL_BOOL_VEC3:
case GL_BOOL_VEC4:
return UNIFORM_BOOL;
case GL_SAMPLER_1D:
case GL_SAMPLER_2D:
case GL_SAMPLER_3D:
return UNIFORM_SAMPLER;
default:
return UNIFORM_UNKNOWN;
}
}
bool Shader::getConstant(const char *in, UniformType &out)
@@ -809,15 +840,21 @@ StringMap<Shader::UniformType, Shader::UNIFORM_MAX_ENUM>::Entry Shader::uniformT
StringMap<Shader::UniformType, Shader::UNIFORM_MAX_ENUM> Shader::uniformTypes(Shader::uniformTypeEntries, sizeof(Shader::uniformTypeEntries));
StringMap<OpenGL::VertexAttrib, OpenGL::ATTRIB_MAX_ENUM>::Entry Shader::attribNameEntries[] =
StringMap<VertexAttribID, ATTRIB_MAX_ENUM>::Entry Shader::attribNameEntries[] =
{
{"love_PseudoInstanceID", OpenGL::ATTRIB_PSEUDO_INSTANCE_ID},
{"VertexPosition", ATTRIB_POS},
{"VertexTexCoord", ATTRIB_TEXCOORD},
{"VertexColor", ATTRIB_COLOR},
};
StringMap<OpenGL::VertexAttrib, OpenGL::ATTRIB_MAX_ENUM> Shader::attribNames(Shader::attribNameEntries, sizeof(Shader::attribNameEntries));
StringMap<VertexAttribID, ATTRIB_MAX_ENUM> Shader::attribNames(Shader::attribNameEntries, sizeof(Shader::attribNameEntries));
StringMap<Shader::BuiltinUniform, Shader::BUILTIN_MAX_ENUM>::Entry Shader::builtinNameEntries[] =
{
{"TransformMatrix", Shader::BUILTIN_TRANSFORM_MATRIX},
{"ProjectionMatrix", Shader::BUILTIN_PROJECTION_MATRIX},
{"TransformProjectionMatrix", Shader::BUILTIN_TRANSFORM_PROJECTION_MATRIX},
{"love_PointSize", Shader::BUILTIN_POINT_SIZE},
{"love_ScreenSize", Shader::BUILTIN_SCREEN_SIZE},
};
+24 -11
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2014 LOVE Development Team
* Copyright (c) 2006-2015 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
@@ -24,8 +24,10 @@
// LOVE
#include "common/Object.h"
#include "common/StringMap.h"
#include "graphics/Graphics.h"
#include "graphics/Texture.h"
#include "graphics/Volatile.h"
#include "OpenGL.h"
#include "Texture.h"
// STL
#include <string>
@@ -46,9 +48,6 @@ class Shader : public Object, public Volatile
{
public:
// Pointer to currently active Shader.
static Shader *current;
enum ShaderStage
{
STAGE_VERTEX,
@@ -59,6 +58,10 @@ public:
// Built-in uniform (extern) variables.
enum BuiltinUniform
{
BUILTIN_TRANSFORM_MATRIX = 0,
BUILTIN_PROJECTION_MATRIX,
BUILTIN_TRANSFORM_PROJECTION_MATRIX,
BUILTIN_POINT_SIZE,
BUILTIN_SCREEN_SIZE,
BUILTIN_MAX_ENUM
};
@@ -80,6 +83,15 @@ public:
std::string pixel;
};
// Pointer to currently active Shader.
static Shader *current;
// Pointer to the default Shader.
static Shader *defaultShader;
// Default shader code (a shader is always required internally.)
static ShaderSource defaultCode[Graphics::RENDERER_MAX_ENUM];
/**
* Creates a new Shader using a list of source codes.
* Source must contain either vertex or pixel shader code, or both.
@@ -165,10 +177,12 @@ public:
/**
* Internal use only.
**/
bool hasVertexAttrib(OpenGL::VertexAttrib attrib) const;
bool hasVertexAttrib(VertexAttribID attrib) const;
bool hasBuiltinUniform(BuiltinUniform builtin) const;
bool sendBuiltinMatrix(BuiltinUniform builtin, int size, const GLfloat *m, int count);
bool sendBuiltinFloat(BuiltinUniform builtin, int size, const GLfloat *m, int count);
void checkSetScreenParams();
void checkSetPointSize(float size);
const std::map<std::string, Object *> &getBoundRetainables() const;
@@ -221,7 +235,7 @@ private:
GLint builtinUniforms[BUILTIN_MAX_ENUM];
// Location values for any generic vertex attribute variables.
GLint builtinAttributes[OpenGL::ATTRIB_MAX_ENUM];
GLint builtinAttributes[ATTRIB_MAX_ENUM];
// Uniform location buffer map
std::map<std::string, Uniform> uniforms;
@@ -237,8 +251,7 @@ private:
Canvas *lastCanvas;
OpenGL::Viewport lastViewport;
// Max GPU texture units available for sent images
static GLint maxTexUnits;
float lastPointSize;
// Counts total number of textures bound to each texture unit in all shaders
static std::vector<int> textureCounters;
@@ -250,8 +263,8 @@ private:
static StringMap<UniformType, UNIFORM_MAX_ENUM> uniformTypes;
// Names for the generic vertex attributes used by love.
static StringMap<OpenGL::VertexAttrib, OpenGL::ATTRIB_MAX_ENUM>::Entry attribNameEntries[];
static StringMap<OpenGL::VertexAttrib, OpenGL::ATTRIB_MAX_ENUM> attribNames;
static StringMap<VertexAttribID, ATTRIB_MAX_ENUM>::Entry attribNameEntries[];
static StringMap<VertexAttribID, ATTRIB_MAX_ENUM> attribNames;
// Names for the built-in uniform variables.
static StringMap<BuiltinUniform, BUILTIN_MAX_ENUM>::Entry builtinNameEntries[];
+33 -33
View File
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2006-2014 LOVE Development Team
* Copyright (c) 2006-2015 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
@@ -25,8 +25,8 @@
#include "OpenGL.h"
// LOVE
#include "VertexBuffer.h"
#include "Texture.h"
#include "GLBuffer.h"
#include "graphics/Texture.h"
// C++
#include <algorithm>
@@ -47,7 +47,7 @@ SpriteBatch::SpriteBatch(Texture *texture, int size, int usage)
, next(0)
, color(0)
, array_buf(nullptr)
, element_buf(size)
, quad_indices(size)
, buffer_used_offset(0)
, buffer_used_size(0)
{
@@ -73,7 +73,7 @@ SpriteBatch::SpriteBatch(Texture *texture, int size, int usage)
try
{
array_buf = VertexBuffer::Create(vertex_size, GL_ARRAY_BUFFER, gl_usage);
array_buf = GLBuffer::Create(vertex_size, GL_ARRAY_BUFFER, gl_usage);
}
catch (love::Exception &)
{
@@ -135,7 +135,7 @@ void SpriteBatch::clear()
void SpriteBatch::flush()
{
VertexBuffer::Bind bind(*array_buf);
GLBuffer::Bind bind(*array_buf);
array_buf->unmap(buffer_used_offset, buffer_used_size);
buffer_used_offset = buffer_used_size = 0;
@@ -183,26 +183,26 @@ void SpriteBatch::setBufferSize(int newsize)
if (newsize == size)
return;
// Map (lock) the old VertexBuffer to get a pointer to its data.
// Map the old GLBuffer to get a pointer to its data.
void *old_data = nullptr;
{
VertexBuffer::Bind bind(*array_buf);
GLBuffer::Bind bind(*array_buf);
old_data = array_buf->map();
}
size_t vertex_size = sizeof(Vertex) * 4 * newsize;
VertexBuffer *new_array_buf = nullptr;
GLBuffer *new_array_buf = nullptr;
try
{
new_array_buf = VertexBuffer::Create(vertex_size, array_buf->getTarget(), array_buf->getUsage());
new_array_buf = GLBuffer::Create(vertex_size, array_buf->getTarget(), array_buf->getUsage());
// Copy as much of the old data into the new VertexBuffer as can fit.
VertexBuffer::Bind bind(*new_array_buf);
// Copy as much of the old data into the new GLBuffer as can fit.
GLBuffer::Bind bind(*new_array_buf);
void *new_data = new_array_buf->map();
memcpy(new_data, old_data, sizeof(Vertex) * 4 * std::min(newsize, size));
element_buf = VertexIndex(newsize);
quad_indices = VertexIndex(newsize);
}
catch (love::Exception &)
{
@@ -210,7 +210,7 @@ void SpriteBatch::setBufferSize(int newsize)
throw;
}
// We don't need to unmap the old VertexBuffer since we're deleting it.
// We don't need to unmap the old GLBuffer since we're deleting it.
delete array_buf;
array_buf = new_array_buf;
@@ -226,22 +226,24 @@ int SpriteBatch::getBufferSize() const
void SpriteBatch::draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky)
{
const size_t pos_offset = offsetof(Vertex, x);
const size_t pos_offset = offsetof(Vertex, x);
const size_t texel_offset = offsetof(Vertex, s);
const size_t color_offset = offsetof(Vertex, r);
if (next == 0)
return;
OpenGL::TempDebugGroup debuggroup("SpriteBatch draw");
Matrix t(x, y, angle, sx, sy, ox, oy, kx, ky);
OpenGL::TempTransform transform(gl);
transform.get() *= t;
texture->predraw();
gl.bindTexture(*(GLuint *) texture->getHandle());
VertexBuffer::Bind array_bind(*array_buf);
VertexBuffer::Bind element_bind(*element_buf.getVertexBuffer());
GLBuffer::Bind array_bind(*array_buf);
GLBuffer::Bind element_bind(*quad_indices.getBuffer());
// Make sure the VBO isn't mapped when we draw (sends data to GPU if needed.)
array_buf->unmap(buffer_used_offset, buffer_used_size);
@@ -252,29 +254,27 @@ void SpriteBatch::draw(float x, float y, float angle, float sx, float sy, float
// Apply per-sprite color, if a color is set.
if (color)
{
glEnableClientState(GL_COLOR_ARRAY);
glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(Vertex), array_buf->getPointer(color_offset));
glEnableVertexAttribArray(ATTRIB_COLOR);
glVertexAttribPointer(ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(Vertex), array_buf->getPointer(color_offset));
}
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(2, GL_FLOAT, sizeof(Vertex), array_buf->getPointer(pos_offset));
glEnableVertexAttribArray(ATTRIB_POS);
glVertexAttribPointer(ATTRIB_POS, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), array_buf->getPointer(pos_offset));
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), array_buf->getPointer(texel_offset));
glEnableVertexAttribArray(ATTRIB_TEXCOORD);
glVertexAttribPointer(ATTRIB_TEXCOORD, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), array_buf->getPointer(texel_offset));
gl.prepareDraw();
gl.drawElements(GL_TRIANGLES, element_buf.getIndexCount(next), element_buf.getType(), element_buf.getPointer(0));
gl.drawElements(GL_TRIANGLES, (GLsizei) quad_indices.getIndexCount(next), quad_indices.getType(), quad_indices.getPointer(0));
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glDisableVertexAttribArray(ATTRIB_TEXCOORD);
glDisableVertexAttribArray(ATTRIB_POS);
if (color)
{
glDisableClientState(GL_COLOR_ARRAY);
glDisableVertexAttribArray(ATTRIB_COLOR);
gl.setColor(curcolor);
}
texture->postdraw();
}
void SpriteBatch::addv(const Vertex *v, const Matrix &m, int index)
@@ -288,7 +288,7 @@ void SpriteBatch::addv(const Vertex *v, const Matrix &m, int index)
if (color)
setColorv(sprite, *color);
VertexBuffer::Bind bind(*array_buf);
GLBuffer::Bind bind(*array_buf);
// Always keep the VBO mapped when adding data for now (it'll be unmapped
// on draw.)
@@ -324,8 +324,8 @@ bool SpriteBatch::getConstant(UsageHint in, const char *&out)
StringMap<SpriteBatch::UsageHint, SpriteBatch::USAGE_MAX_ENUM>::Entry SpriteBatch::usageHintEntries[] =
{
{"dynamic", SpriteBatch::USAGE_DYNAMIC},
{"static", SpriteBatch::USAGE_STATIC},
{"stream", SpriteBatch::USAGE_STREAM},
{"static", SpriteBatch::USAGE_STATIC},
{"stream", SpriteBatch::USAGE_STREAM},
};
StringMap<SpriteBatch::UsageHint, SpriteBatch::USAGE_MAX_ENUM> SpriteBatch::usageHints(usageHintEntries, sizeof(usageHintEntries));

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