Merge branch 'main' into 12.0-development

This commit is contained in:
Sasha Szpakowski
2023-08-19 21:35:55 -03:00
45 changed files with 1213 additions and 339 deletions
@@ -67,7 +67,11 @@ bool RecordingDevice::start(int samples, int sampleRate, int bitDepth, int chann
if (isRecording())
stop();
// This hard-crashes on iOS with Apple's OpenAL implementation, even when
// the user gives permission to the app.
#ifndef LOVE_IOS
device = alcCaptureOpenDevice(name.c_str(), sampleRate, format, samples);
#endif
if (device == nullptr)
return false;
+51 -60
View File
@@ -51,6 +51,13 @@ inline uint64 rightrot(uint64 x, uint8 amount)
return (x >> amount) | (x << (64 - amount));
}
// Extend the value of `a` to make it a multiple of `n`.
inline uint64 extend_multiple(uint64 a, uint64 n)
{
uint64 r = a % n;
return r == 0 ? a : a + (n-r);
}
/**
* The following implementation is based on the pseudocode provided by multiple
* authors on wikipedia: https://en.wikipedia.org/wiki/MD5
@@ -80,25 +87,22 @@ public:
uint32 c0 = 0x98badcfe;
uint32 d0 = 0x10325476;
//Do the required padding (MD5, SHA1 and SHA2 use the same padding)
uint64 paddedLength = length + 1; //Consider the appended bit
if (paddedLength % 64 < 56)
paddedLength += 56 - paddedLength % 64;
if (paddedLength % 64 > 56)
paddedLength += 120 - paddedLength % 64;
// Compute final padded length, accounting for the appended bit (byte) and size
uint64 paddedLength = extend_multiple(length + 1 + 8, 64);
uint8 *padded = new uint8[paddedLength + 8];
uint32 *padded = new uint32[paddedLength / 4];
memcpy(padded, input, length);
memset(padded + length, 0, paddedLength - length);
padded[length] = 0x80;
memset(((uint8*)padded) + length, 0, paddedLength - length);
*(((uint8*)padded) + length) = 0x80; // append bit
//Now we need the length in bits
*((uint64*) &padded[paddedLength]) = length * 8;
paddedLength += 8;
// Append length in bits
uint64 bit_length = length * 8;
memcpy(((uint8*)padded) + paddedLength - 8, &bit_length, 8);
for (uint64 i = 0; i < paddedLength; i += 64)
// Process chunks
for (uint64 i = 0; i < paddedLength/4; i += 16)
{
uint32 *chunk = (uint32*) &padded[i];
uint32 *chunk = &padded[i];
uint32 A = a0;
uint32 B = b0;
@@ -201,29 +205,25 @@ public:
0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0
};
//Do the required padding (MD5, SHA1 and SHA2 use the same padding)
uint64 paddedLength = length + 1; //Consider the appended bit
if (paddedLength % 64 < 56)
paddedLength += 56 - paddedLength % 64;
if (paddedLength % 64 > 56)
paddedLength += 120 - paddedLength % 64;
// Compute final padded length, accounting for the appended bit (byte) and size
uint64 paddedLength = extend_multiple(length + 1 + 8, 64);
uint8 *padded = new uint8[paddedLength + 8];
uint32 *padded = new uint32[paddedLength / 4];
memcpy(padded, input, length);
memset(padded + length, 0, paddedLength - length);
padded[length] = 0x80;
memset(((uint8*)padded) + length, 0, paddedLength - length);
*(((uint8*)padded) + length) = 0x80; // append bit
// Now we need the length in bits (big endian)
length *= 8;
for (int i = 0; i < 8; ++i, ++paddedLength)
padded[paddedLength] = (length >> (56 - i * 8)) & 0xFF;
// Append length in bits (big endian)
uint64 bit_length = length * 8;
for (int i = 0; i < 8; ++i)
*(((uint8*)padded) + (paddedLength - 8 + i)) = (bit_length >> (56 - i * 8)) & 0xFF;
// Allocate our extended words
uint32 words[80];
for (uint64 i = 0; i < paddedLength; i += 64)
for (uint64 i = 0; i < paddedLength/4; i += 16)
{
uint32 *chunk = (uint32*) &padded[i];
uint32 *chunk = &padded[i];
for (int j = 0; j < 16; j++)
{
char *c = (char*) &words[j];
@@ -304,22 +304,18 @@ public:
if (!isSupported(function))
throw love::Exception("Hash function not supported by SHA-224/SHA-256 implementation");
//Do the required padding (MD5, SHA1 and SHA2 use the same padding)
uint64 paddedLength = length + 1; //Consider the appended bit
if (paddedLength % 64 < 56)
paddedLength += 56 - paddedLength % 64;
if (paddedLength % 64 > 56)
paddedLength += 120 - paddedLength % 64;
// Compute final padded length, accounting for the appended bit (byte) and size
uint64 paddedLength = extend_multiple(length + 1 + 8, 64);
uint8 *padded = new uint8[paddedLength + 8];
uint32 *padded = new uint32[paddedLength / 4];
memcpy(padded, input, length);
memset(padded + length, 0, paddedLength - length);
padded[length] = 0x80;
memset(((uint8*)padded) + length, 0, paddedLength - length);
*(((uint8*)padded) + length) = 0x80; // append bit
// Now we need the length in bits (big endian)
length *= 8;
for (int i = 0; i < 8; ++i, ++paddedLength)
padded[paddedLength] = (length >> (56 - i * 8)) & 0xFF;
// Append length in bits (big endian)
uint64 bit_length = length * 8;
for (int i = 0; i < 8; ++i)
*(((uint8*)padded) + (paddedLength - 8 + i)) = (bit_length >> (56 - i * 8)) & 0xFF;
uint32 intermediate[8];
if (function == FUNCTION_SHA224)
@@ -330,9 +326,9 @@ public:
// Allocate our extended words
uint32 words[64];
for (uint64 i = 0; i < paddedLength; i += 64)
for (uint64 i = 0; i < paddedLength/4; i += 16)
{
uint32 *chunk = (uint32*) &padded[i];
uint32 *chunk = &padded[i];
for (int j = 0; j < 16; j++)
{
char *c = (char*) &words[j];
@@ -460,31 +456,26 @@ public:
else
memcpy(intermediates, initial512, sizeof(intermediates));
//Do the required padding
uint64 paddedLength = length + 1; //Consider the appended bit
if (paddedLength % 128 < 112)
paddedLength += 112 - paddedLength % 128;
if (paddedLength % 128 > 112)
paddedLength += 240 - paddedLength % 128;
// Compute final padded length, accounting for the appended bit (byte) and size
uint64 paddedLength = extend_multiple(length + 1 + 16, 128);
uint8 *padded = new uint8[paddedLength + 16];
paddedLength += 8;
uint64 *padded = new uint64[paddedLength / 8];
memcpy(padded, input, length);
memset(padded + length, 0, paddedLength - length);
padded[length] = 0x80;
memset(((uint8*)padded) + length, 0, paddedLength - length);
*(((uint8*)padded) + length) = 0x80; // append bit
// Now we need the length in bits (big endian), note we only write a 64-bit int, so
// Append length in bits (big endian), note we only write a 64-bit int, so
// we have filled the first 8 bytes with zeroes
length *= 8;
for (int i = 0; i < 8; ++i, ++paddedLength)
padded[paddedLength] = (length >> (56 - i * 8)) & 0xFF;
uint64 bit_length = length * 8;
for (int i = 0; i < 8; ++i)
*(((uint8*)padded) + (paddedLength - 8 + i)) = (bit_length >> (56 - i * 8)) & 0xFF;
// Allocate our extended words
uint64 words[80];
for (uint64 i = 0; i < paddedLength; i += 128)
for (uint64 i = 0; i < paddedLength/8; i += 16)
{
uint64 *chunk = (uint64*) &padded[i];
uint64 *chunk = &padded[i];
for (int j = 0; j < 16; ++j)
{
char *c = (char*) &words[j];
+18
View File
@@ -54,6 +54,13 @@ static void windowToDPICoords(double *x, double *y)
window->windowToDPICoords(x, y);
}
static void clampToWindow(double *x, double *y)
{
auto window = Module::getInstance<window::Window>(Module::M_WINDOW);
if (window)
window->clampPositionInWindow(x, y);
}
#ifndef LOVE_MACOS
static void normalizedToDPICoords(double *x, double *y)
{
@@ -253,8 +260,16 @@ Message *Event::convert(const SDL_Event &e)
double y = (double) e.motion.y;
double xrel = (double) e.motion.xrel;
double yrel = (double) e.motion.yrel;
// SDL reports mouse coordinates outside the window bounds when click-and-
// dragging. For compatibility we clamp instead since user code may not be
// able to handle out-of-bounds coordinates. SDL has a hint to turn off
// auto capture, but it doesn't report the mouse's position at the edge of
// the window if the mouse moves fast enough when it's off.
clampToWindow(&x, &y);
windowToDPICoords(&x, &y);
windowToDPICoords(&xrel, &yrel);
vargs.emplace_back(x);
vargs.emplace_back(y);
vargs.emplace_back(xrel);
@@ -280,7 +295,10 @@ Message *Event::convert(const SDL_Event &e)
double px = (double) e.button.x;
double py = (double) e.button.y;
clampToWindow(&px, &py);
windowToDPICoords(&px, &py);
vargs.emplace_back(px);
vargs.emplace_back(py);
vargs.emplace_back((double) button);
+27 -2
View File
@@ -27,6 +27,7 @@
#include "Filesystem.h"
#include "File.h"
#include "PhysfsIo.h"
// PhysFS
#include "libraries/physfs/physfs.h"
@@ -134,6 +135,10 @@ const char *Filesystem::getName() const
void Filesystem::init(const char *arg0)
{
#ifdef LOVE_ANDROID
arg0 = love::android::getArg0();
#endif
if (!PHYSFS_init(arg0))
throw love::Exception("Failed to initialize filesystem: %s", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
@@ -156,7 +161,7 @@ bool Filesystem::isFused() const
return fused;
}
bool Filesystem::setIdentity(const char *ident, bool appendToPath)
bool Filesystem::setIdentity(const char *ident, bool appendToPath)
{
if (!PHYSFS_isInit())
return false;
@@ -279,7 +284,27 @@ bool Filesystem::setSource(const char *source)
// Add the directory.
if (!PHYSFS_mount(new_search_path.c_str(), nullptr, 1))
return false;
{
// It's possible there is additional data at the end of the fused executable,
// e.g. for signed windows executables (the signature).
// In this case let's try a little bit harder to find the zip file.
// This is not used by default because I assume that the physfs IOs are probably
// more robust and more performant, so they should be favored, if possible.
auto io = StripSuffixIo::create(new_search_path);
if (!io->determineStrippedLength())
{
delete io;
return false;
}
if (!PHYSFS_mountIo(io, io->filename.c_str(), nullptr, 1))
{
// If PHYSFS_mountIo fails, io->destroy(io) is not called and we have
// to delete ourselves.
delete io;
return false;
}
return true;
}
// Save the game source.
gameSource = new_search_path;
+203
View File
@@ -0,0 +1,203 @@
/**
* Copyright (c) 2006-2023 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 <cassert>
#include <algorithm>
#include "PhysfsIo.h"
namespace love
{
namespace filesystem
{
namespace physfs
{
bool StripSuffixIo::determineStrippedLength()
{
if (!file) {
return false;
}
const int64_t fullSize = fullLength();
int64_t chunkSize = std::min(fullSize, (int64_t)8192);
std::string buffer;
buffer.reserve(chunkSize);
int64_t i = fullSize - chunkSize;
// I don't think we really need to go through the whole file. The main known use
// case for this functionality is to skip windows codesign signatures, which are
// from what I have seen ~12KB or so, but trying is better than just failing.
while (i >= 0)
{
buffer.resize(chunkSize);
if (seek(i) == 0)
return false;
const auto n = read(&buffer[0], chunkSize);
if (n <= 0)
return false;
buffer.resize(n);
// We are looking for the magic bytes that indicate the start
// of the "End of cental directory record (EOCD)".
// As this is most likely not a multi-disk zip, we could include 4 bytes of 0x00,
// but I'd rather make fewer assumptions.
const auto endOfCentralDirectory = buffer.rfind("\x50\x4B\x05\x06");
if (endOfCentralDirectory != std::string::npos)
{
i = i + endOfCentralDirectory;
break;
}
if (i == 0)
break;
i = std::max((int64_t)0, i - chunkSize);
}
if (i > 0)
{
// The EOCD record is at least 22 bytes but may include a comment
if (i + 22 > fullSize)
return false; // Incomplete central directory
// The comment length (u16) is located 20 bytes from the start of the EOCD record
if (seek(i + 20) == 0)
return false;
uint8_t buffer[2];
const auto n = read(buffer, 2);
if (n <= 0)
return false;
const auto commentSize = (buffer[1] << 8) | buffer[0];
if (i + 22 + commentSize > fullSize) // Comment incomplete
return false;
// We pretend the file ends just after the comment
// (which should be the end of the embedded zip file)
strippedLength_ = i + 22 + commentSize;
}
else
{
strippedLength_ = fullSize;
}
if (seek(0) == 0)
return false;
return true;
}
int64_t StripSuffixIo::read(void* buf, uint64_t len)
{
if (!file)
{
PHYSFS_setErrorCode(PHYSFS_ERR_OS_ERROR);
return -1;
}
const auto ret = std::fread(buf, 1, len, file);
if (ret == 0)
{
if (std::feof(file))
{
PHYSFS_setErrorCode(PHYSFS_ERR_OK);
return 0;
}
else
{
PHYSFS_setErrorCode(PHYSFS_ERR_OS_ERROR);
return -1;
}
}
else if (ret < len && std::ferror(file))
{
PHYSFS_setErrorCode(PHYSFS_ERR_OS_ERROR);
return -1;
}
PHYSFS_setErrorCode(PHYSFS_ERR_OK);
return ret;
}
int64_t StripSuffixIo::write(const void* /*buf*/, uint64_t /*len*/)
{
PHYSFS_setErrorCode(PHYSFS_ERR_READ_ONLY);
return -1;
}
int64_t StripSuffixIo::seek(uint64_t offset)
{
if (!file)
{
PHYSFS_setErrorCode(PHYSFS_ERR_OS_ERROR);
return 0;
}
const auto ret = std::fseek(file, offset, SEEK_SET);
PHYSFS_setErrorCode(ret != 0 ? PHYSFS_ERR_OS_ERROR : PHYSFS_ERR_OK);
return ret == 0 ? 1 : 0;
}
int64_t StripSuffixIo::tell()
{
if (!file)
{
PHYSFS_setErrorCode(PHYSFS_ERR_OS_ERROR);
return -1;
}
return std::ftell(file);
}
int64_t StripSuffixIo::length()
{
return strippedLength_;
}
int64_t StripSuffixIo::flush()
{
if (!file)
{
PHYSFS_setErrorCode(PHYSFS_ERR_OS_ERROR);
return 0;
}
return std::fflush(file) == 0 ? 1 : 0;
}
int64_t StripSuffixIo::fullLength()
{
assert(file);
const auto cur = std::ftell(file);
if (cur == -1)
{
PHYSFS_setErrorCode(PHYSFS_ERR_OS_ERROR);
return -1;
}
if (std::fseek(file, 0, SEEK_END) != 0)
{
PHYSFS_setErrorCode(PHYSFS_ERR_OS_ERROR);
return -1;
}
const auto len = std::ftell(file);
if (len == -1)
{
PHYSFS_setErrorCode(PHYSFS_ERR_OS_ERROR);
return -1;
}
if (std::fseek(file, cur, SEEK_SET) != 0)
{
// We do have the length now, but something is wrong, so we return an error anyways
PHYSFS_setErrorCode(PHYSFS_ERR_OS_ERROR);
return -1;
}
return len;
}
} // physfs
} // filesystem
} // love
+167
View File
@@ -0,0 +1,167 @@
/**
* Copyright (c) 2006-2023 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_PHYSFS_PHYSFSIO_H
#define LOVE_FILESYSTEM_PHYSFS_PHYSFSIO_H
#include <cstdint>
#include <cstdio>
#include <string>
#include "libraries/physfs/physfs.h"
namespace love
{
namespace filesystem
{
namespace physfs
{
template <typename Derived>
struct PhysfsIo : PHYSFS_Io
{
protected:
PhysfsIo()
: PHYSFS_Io()
{
// Direct initialization of PHYSFS_Io members in the initializer list
// doesn't work in VS2013.
this->version = Derived::version;
this->opaque = this;
this->read = staticRead; // May be null.
this->write = staticWrite; // May be null.
this->seek = staticSeek;
this->tell = staticTell;
this->length = staticLength;
this->duplicate = staticDuplicate;
this->flush = staticFlush; // May be null.
this->destroy = staticDestroy;
}
virtual ~PhysfsIo() {}
private:
// Returns: number of bytes read, 0 on EOF, -1 on failure
static PHYSFS_sint64 staticRead(struct PHYSFS_Io* io, void* buf, PHYSFS_uint64 len)
{
return derived(io)->read(buf, len);
}
// Returns: number of bytes written, -1 on failure
static PHYSFS_sint64 staticWrite(struct PHYSFS_Io* io, const void* buf, PHYSFS_uint64 len)
{
return derived(io)->write(buf, len);
}
// Returns: non-zero on success, zero on error
static int staticSeek(struct PHYSFS_Io* io, PHYSFS_uint64 offset)
{
return derived(io)->seek(offset);
}
// Returns: current offset from start, -1 on error
static PHYSFS_sint64 staticTell(struct PHYSFS_Io* io)
{
return derived(io)->tell();
}
// Returns: total size in bytes, -1 on error
static PHYSFS_sint64 staticLength(struct PHYSFS_Io* io)
{
return derived(io)->length();
}
static struct PHYSFS_Io* staticDuplicate(struct PHYSFS_Io* io)
{
// Just use copy constructor
return new Derived(*derived(io));
}
// Returns: non-zero on success, zero on error
static int staticFlush(struct PHYSFS_Io* io)
{
return derived(io)->flush();
}
static void staticDestroy(struct PHYSFS_Io* io)
{
// Just use destructor
delete derived(io);
}
static Derived* derived(PHYSFS_Io* io)
{
return static_cast<Derived*>(reinterpret_cast<PhysfsIo*>(io->opaque));
}
};
struct StripSuffixIo : public PhysfsIo<StripSuffixIo>
{
static const uint32_t version = 0;
std::string filename;
FILE* file = nullptr;
// The constructor is private in favor of this function to prevent stack allocation
// because Physfs will take ownership of this object and call destroy on it later.
static StripSuffixIo* create(std::string f) { return new StripSuffixIo(f); }
virtual ~StripSuffixIo()
{
if (file)
{
std::fclose(file);
}
}
StripSuffixIo(const StripSuffixIo& other)
: StripSuffixIo(other.filename)
{
}
bool determineStrippedLength();
int64_t read(void* buf, uint64_t len);
int64_t write(const void* buf, uint64_t len);
int64_t seek(uint64_t offset);
int64_t tell();
int64_t length();
int64_t flush();
private:
StripSuffixIo(std::string f)
: filename(std::move(f))
, file(std::fopen(filename.c_str(), "rb"))
{
}
int64_t fullLength();
int64_t strippedLength_ = -1;
};
} // physfs
} // filesystem
} // love
#endif
+3 -1
View File
@@ -425,7 +425,9 @@ std::string Joystick::getGamepadMappingString() const
// Matches SDL_GameControllerAddMappingsFromRW.
if (mappingstr.find_last_of(',') != mappingstr.length() - 1)
mappingstr += ",";
mappingstr += "platform:" + std::string(SDL_GetPlatform());
if (mappingstr.find("platform:") == std::string::npos)
mappingstr += "platform:" + std::string(SDL_GetPlatform());
return mappingstr;
}
+15 -6
View File
@@ -259,12 +259,17 @@ bool JoystickModule::setGamepadMapping(const std::string &guid, Joystick::Gamepa
if (endpos == std::string::npos)
endpos = mapstr.length() - 1;
mapstr.replace(findpos + 1, endpos - findpos + 1, insertstr);
mapstr.replace(findpos + 1, endpos - findpos, insertstr);
}
else
{
// Just append to the end if we don't need to replace anything.
mapstr += insertstr;
// Just append to the end (or before the platform section if that exists),
// if we don't need to replace anything.
size_t platformpos = mapstr.find("platform:");
if (platformpos != std::string::npos)
mapstr.insert(platformpos, insertstr);
else
mapstr += insertstr;
}
// 1 == added, 0 == updated, -1 == error.
@@ -465,7 +470,9 @@ std::string JoystickModule::getGamepadMappingString(const std::string &guid) con
// Matches SDL_GameControllerAddMappingsFromRW.
if (mapping.find_last_of(',') != mapping.length() - 1)
mapping += ",";
mapping += "platform:" + std::string(SDL_GetPlatform());
if (mapping.find("platform:") == std::string::npos)
mapping += "platform:" + std::string(SDL_GetPlatform());
return mapping;
}
@@ -489,8 +496,10 @@ std::string JoystickModule::saveGamepadMappings()
mapping += ",";
// Matches SDL_GameControllerAddMappingsFromRW.
mapping += "platform:" + std::string(SDL_GetPlatform()) + ",\n";
mappings += mapping;
if (mapping.find("platform:") == std::string::npos)
mapping += "platform:" + std::string(SDL_GetPlatform()) + ",";
mappings += mapping + "\n";
}
return mappings;
+21 -14
View File
@@ -49,6 +49,13 @@ static void DPIToWindowCoords(double *x, double *y)
window->DPIToWindowCoords(x, y);
}
static void clampToWindow(double *x, double *y)
{
auto window = Module::getInstance<window::Window>(Module::M_WINDOW);
if (window)
window->clampPositionInWindow(x, y);
}
const char *Mouse::getName() const
{
return "love.mouse.sdl";
@@ -119,24 +126,16 @@ bool Mouse::isCursorSupported() const
double Mouse::getX() const
{
int x;
SDL_GetMouseState(&x, nullptr);
double dx = (double) x;
windowToDPICoords(&dx, nullptr);
return dx;
double x, y;
getPosition(x, y);
return x;
}
double Mouse::getY() const
{
int y;
SDL_GetMouseState(nullptr, &y);
double dy = (double) y;
windowToDPICoords(nullptr, &dy);
return dy;
double x, y;
getPosition(x, y);
return y;
}
void Mouse::getPosition(double &x, double &y) const
@@ -146,6 +145,14 @@ void Mouse::getPosition(double &x, double &y) const
x = (double) mx;
y = (double) my;
// SDL reports mouse coordinates outside the window bounds when click-and-
// dragging. For compatibility we clamp instead since user code may not be
// able to handle out-of-bounds coordinates. SDL has a hint to turn off
// auto capture, but it doesn't report the mouse's position at the edge of
// the window if the mouse moves fast enough when it's off.
clampToWindow(&x, &y);
windowToDPICoords(&x, &y);
}
@@ -232,14 +232,18 @@ void TheoraVideoStream::threadedFillBackBuffer(double dt)
th_decode_ycbcr_out(decoder, bufferinfo);
hasFrame = true;
ogg_int64_t granulePosition;
ogg_int64_t decoderPosition;
do
{
if (demuxer.readPacket(packet))
return;
} while (th_decode_packetin(decoder, &packet, &granulePosition) != 0);
if (packet.granulepos > 0)
th_decode_ctl(decoder, TH_DECCTL_SET_GRANPOS, &packet.granulepos, sizeof(packet.granulepos));
} while (th_decode_packetin(decoder, &packet, &decoderPosition) != 0);
lastFrame = nextFrame;
nextFrame = th_granule_time(decoder, granulePosition);
nextFrame = th_granule_time(decoder, decoderPosition);
}
// Only swap once, even if we read many frames to get here
+2
View File
@@ -199,6 +199,8 @@ public:
virtual int getPixelWidth() const = 0;
virtual int getPixelHeight() const = 0;
virtual void clampPositionInWindow(double *wx, double *wy) const = 0;
// Note: window-space coordinates are not necessarily the same as
// density-independent units (which toPixels and fromPixels use.)
virtual void windowToPixelCoords(double *x, double *y) const = 0;
+7
View File
@@ -1309,6 +1309,13 @@ int Window::getPixelHeight() const
return pixelHeight;
}
void Window::clampPositionInWindow(double *wx, double *wy) const
{
if (wx != nullptr)
*wx = std::min(std::max(0.0, *wx), (double) getWidth() - 1);
if (wy != nullptr)
*wy = std::min(std::max(0.0, *wy), (double) getHeight() - 1);
}
void Window::windowToPixelCoords(double *x, double *y) const
{
+2
View File
@@ -107,6 +107,8 @@ public:
int getPixelWidth() const override;
int getPixelHeight() const override;
void clampPositionInWindow(double *wx, double *wy) const override;
void windowToPixelCoords(double *x, double *y) const override;
void pixelToWindowCoords(double *x, double *y) const override;