mirror of
https://github.com/love2d/love.git
synced 2026-08-16 08:11:02 +02:00
fix conflicts
This commit is contained in:
@@ -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));
|
||||
|
||||
@@ -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.
|
||||
**/
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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()
|
||||
{
|
||||
}
|
||||
|
||||
@@ -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,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,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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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()
|
||||
|
||||
@@ -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
@@ -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,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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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));
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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;
|
||||
|
||||
@@ -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:
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,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
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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,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
|
||||
@@ -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
|
||||
|
||||
@@ -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,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
|
||||
|
||||
@@ -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
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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,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
|
||||
@@ -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,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
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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,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
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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];
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
+63
-158
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
+87
-188
@@ -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
@@ -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
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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 ≷
|
||||
};
|
||||
|
||||
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;
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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},
|
||||
};
|
||||
|
||||
|
||||
@@ -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[];
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user