mirror of
https://github.com/love2d/love.git
synced 2026-08-16 00:02:12 +02:00
Added the ability to use custom mipmaps in Images, via love.graphics.newImage(filename, {mipmap1, mipmap2, ...}). Resolves issue #1064.
All mipmap levels must be present if custom mipmaps are used (and their sizes must be half the size of the previous mipmap level, rounded down.) Image:getData now returns all custom mipmaps in an Image.
This commit is contained in:
@@ -44,7 +44,7 @@ namespace sdl
|
||||
// we want them in pixel coordinates (may be different with high-DPI enabled.)
|
||||
static void windowToPixelCoords(double *x, double *y)
|
||||
{
|
||||
window::Window *window = Module::getInstance<window::Window>(Module::M_WINDOW);
|
||||
auto window = Module::getInstance<window::Window>(Module::M_WINDOW);
|
||||
if (window)
|
||||
window->windowToPixelCoords(x, y);
|
||||
}
|
||||
@@ -52,7 +52,7 @@ static void windowToPixelCoords(double *x, double *y)
|
||||
#ifndef LOVE_MACOSX
|
||||
static void normalizedToPixelCoords(double *x, double *y)
|
||||
{
|
||||
window::Window *window = Module::getInstance<window::Window>(Module::M_WINDOW);
|
||||
auto window = Module::getInstance<window::Window>(Module::M_WINDOW);
|
||||
int w = 1, h = 1;
|
||||
|
||||
if (window)
|
||||
@@ -70,7 +70,7 @@ static void normalizedToPixelCoords(double *x, double *y)
|
||||
// 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);
|
||||
auto gfx = Module::getInstance<graphics::Graphics>(Module::M_GRAPHICS);
|
||||
|
||||
switch (event->type)
|
||||
{
|
||||
@@ -152,7 +152,6 @@ Message *Event::convert(const SDL_Event &e) const
|
||||
std::vector<StrongRef<Variant>> vargs;
|
||||
vargs.reserve(4);
|
||||
|
||||
love::keyboard::Keyboard *kb = nullptr;
|
||||
love::filesystem::Filesystem *filesystem = nullptr;
|
||||
|
||||
love::keyboard::Keyboard::Key key = love::keyboard::Keyboard::KEY_UNKNOWN;
|
||||
@@ -176,7 +175,7 @@ Message *Event::convert(const SDL_Event &e) const
|
||||
case SDL_KEYDOWN:
|
||||
if (e.key.repeat)
|
||||
{
|
||||
kb = Module::getInstance<love::keyboard::Keyboard>(Module::M_KEYBOARD);
|
||||
auto kb = Module::getInstance<love::keyboard::Keyboard>(Module::M_KEYBOARD);
|
||||
if (kb && !kb->hasKeyRepeat())
|
||||
break;
|
||||
}
|
||||
@@ -394,7 +393,7 @@ Message *Event::convert(const SDL_Event &e) const
|
||||
|
||||
Message *Event::convertJoystickEvent(const SDL_Event &e) const
|
||||
{
|
||||
joystick::JoystickModule *joymodule = Module::getInstance<joystick::JoystickModule>(Module::M_JOYSTICK);
|
||||
auto joymodule = Module::getInstance<joystick::JoystickModule>(Module::M_JOYSTICK);
|
||||
if (!joymodule)
|
||||
return nullptr;
|
||||
|
||||
|
||||
@@ -190,8 +190,8 @@ void BMFontRasterizer::parseConfig(const std::string &configtext)
|
||||
{
|
||||
using namespace love::filesystem;
|
||||
|
||||
Filesystem *filesystem = Module::getInstance<Filesystem>(Module::M_FILESYSTEM);
|
||||
image::Image *imagemodule = Module::getInstance<image::Image>(Module::M_IMAGE);
|
||||
auto filesystem = Module::getInstance<Filesystem>(Module::M_FILESYSTEM);
|
||||
auto imagemodule = Module::getInstance<image::Image>(Module::M_IMAGE);
|
||||
|
||||
if (!filesystem)
|
||||
throw love::Exception("Filesystem module not loaded!");
|
||||
|
||||
@@ -194,7 +194,7 @@ void Graphics::checkSetDefaultFont()
|
||||
// Create a new default font if we don't have one yet.
|
||||
if (!defaultFont.get())
|
||||
{
|
||||
font::Font *fontmodule = Module::getInstance<font::Font>(M_FONT);
|
||||
auto fontmodule = Module::getInstance<font::Font>(M_FONT);
|
||||
if (!fontmodule)
|
||||
throw love::Exception("Font module has not been loaded.");
|
||||
|
||||
@@ -664,12 +664,12 @@ void Graphics::clearStencil()
|
||||
glClear(GL_STENCIL_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
}
|
||||
|
||||
Image *Graphics::newImage(love::image::ImageData *data, const Image::Flags &flags)
|
||||
Image *Graphics::newImage(const std::vector<love::image::ImageData *> &data, const Image::Flags &flags)
|
||||
{
|
||||
return new Image(data, flags);
|
||||
}
|
||||
|
||||
Image *Graphics::newImage(love::image::CompressedImageData *cdata, const Image::Flags &flags)
|
||||
Image *Graphics::newImage(const std::vector<love::image::CompressedImageData *> &cdata, const Image::Flags &flags)
|
||||
{
|
||||
return new Image(cdata, flags);
|
||||
}
|
||||
|
||||
@@ -157,8 +157,8 @@ public:
|
||||
/**
|
||||
* Creates an Image object with padding and/or optimization.
|
||||
**/
|
||||
Image *newImage(love::image::ImageData *data, const Image::Flags &flags);
|
||||
Image *newImage(love::image::CompressedImageData *cdata, const Image::Flags &flags);
|
||||
Image *newImage(const std::vector<love::image::ImageData *> &data, const Image::Flags &flags);
|
||||
Image *newImage(const std::vector<love::image::CompressedImageData *> &cdata, const Image::Flags &flags);
|
||||
|
||||
Quad *newQuad(Quad::Viewport v, float sw, float sh);
|
||||
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
#include "common/int.h"
|
||||
|
||||
// STD
|
||||
#include <cstring> // For memcpy
|
||||
#include <algorithm> // for min/max
|
||||
|
||||
namespace love
|
||||
@@ -40,18 +39,62 @@ float Image::maxMipmapSharpness = 0.0f;
|
||||
Texture::FilterMode Image::defaultMipmapFilter = Texture::FILTER_LINEAR;
|
||||
float Image::defaultMipmapSharpness = 0.0f;
|
||||
|
||||
Image::Image(love::image::ImageData *data, const Flags &flags)
|
||||
: data(data)
|
||||
, cdata(nullptr)
|
||||
, texture(0)
|
||||
static int getMipmapCount(int basewidth, int baseheight)
|
||||
{
|
||||
return (int) log2(std::max(basewidth, baseheight)) + 1;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static bool verifyMipmapLevels(const std::vector<T> &miplevels)
|
||||
{
|
||||
int numlevels = (int) miplevels.size();
|
||||
|
||||
if (numlevels == 1)
|
||||
return false;
|
||||
|
||||
int width = miplevels[0]->getWidth();
|
||||
int height = miplevels[0]->getHeight();
|
||||
|
||||
int expectedlevels = getMipmapCount(width, height);
|
||||
|
||||
// All mip levels must be present when not using auto-generated mipmaps.
|
||||
if (numlevels != expectedlevels)
|
||||
throw love::Exception("Image does not have all required mipmap levels (expected %d, got %d)", expectedlevels, numlevels);
|
||||
|
||||
// Verify the size of each mip level.
|
||||
for (int i = 1; i < numlevels; i++)
|
||||
{
|
||||
width = std::max(width / 2, 1);
|
||||
height = std::max(height / 2, 1);
|
||||
|
||||
if (miplevels[i]->getWidth() != width)
|
||||
throw love::Exception("Width of image mipmap level %d is incorrect (expected %d, got %d)", i+1, width, miplevels[i]->getWidth());
|
||||
if (miplevels[i]->getHeight() != height)
|
||||
throw love::Exception("Height of image mipmap level %d is incorrect (expected %d, got %d)", i+1, height, miplevels[i]->getHeight());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Image::Image(const std::vector<love::image::ImageData *> &imagedata, const Flags &flags)
|
||||
: texture(0)
|
||||
, mipmapSharpness(defaultMipmapSharpness)
|
||||
, compressed(false)
|
||||
, flags(flags)
|
||||
, usingDefaultTexture(false)
|
||||
, textureMemorySize(0)
|
||||
{
|
||||
width = data->getWidth();
|
||||
height = data->getHeight();
|
||||
if (imagedata.empty())
|
||||
throw love::Exception("");
|
||||
|
||||
width = imagedata[0]->getWidth();
|
||||
height = imagedata[0]->getHeight();
|
||||
|
||||
if (verifyMipmapLevels(imagedata))
|
||||
this->flags.mipmaps = true;
|
||||
|
||||
for (const auto &id : imagedata)
|
||||
data.push_back(id);
|
||||
|
||||
preload();
|
||||
loadVolatile();
|
||||
@@ -59,27 +102,29 @@ Image::Image(love::image::ImageData *data, const Flags &flags)
|
||||
++imageCount;
|
||||
}
|
||||
|
||||
Image::Image(love::image::CompressedImageData *cdata, const Flags &flags)
|
||||
: data(nullptr)
|
||||
, cdata(cdata)
|
||||
, texture(0)
|
||||
Image::Image(const std::vector<love::image::CompressedImageData *> &compresseddata, const Flags &flags)
|
||||
: texture(0)
|
||||
, mipmapSharpness(defaultMipmapSharpness)
|
||||
, compressed(true)
|
||||
, flags(flags)
|
||||
, usingDefaultTexture(false)
|
||||
, textureMemorySize(0)
|
||||
{
|
||||
this->flags.sRGB = (flags.sRGB || cdata->isSRGB());
|
||||
this->flags.sRGB = (flags.sRGB || cdata[0]->isSRGB());
|
||||
|
||||
width = cdata->getWidth(0);
|
||||
height = cdata->getHeight(0);
|
||||
width = compresseddata[0]->getWidth(0);
|
||||
height = compresseddata[0]->getHeight(0);
|
||||
|
||||
if (flags.mipmaps)
|
||||
if (verifyMipmapLevels(compresseddata))
|
||||
this->flags.mipmaps = true;
|
||||
else if (flags.mipmaps && getMipmapCount(width, height) != compresseddata[0]->getMipmapCount())
|
||||
throw love::Exception("Image cannot have mipmaps: compressed image data does not have all required mipmap levels.");
|
||||
|
||||
for (const auto &cd : compresseddata)
|
||||
{
|
||||
// The mipmap texture data comes from the CompressedImageData in this case,
|
||||
// so we should make sure it has all necessary mipmap levels.
|
||||
if (cdata->getMipmapCount() < (int) log2(std::max(width, height)) + 1)
|
||||
throw love::Exception("Image cannot have mipmaps: compressed image data does not have all required mipmap levels.");
|
||||
cdata.push_back(cd);
|
||||
if (cd->getFormat() != cdata[0]->getFormat())
|
||||
throw love::Exception("All image mipmap levels must have the same format.");
|
||||
}
|
||||
|
||||
preload();
|
||||
@@ -159,14 +204,25 @@ void Image::loadDefaultTexture()
|
||||
|
||||
void Image::loadFromCompressedData()
|
||||
{
|
||||
GLenum iformat = getCompressedFormat(cdata->getFormat());
|
||||
int count = flags.mipmaps ? cdata->getMipmapCount() : 1;
|
||||
GLenum iformat = getCompressedFormat(cdata[0]->getFormat());
|
||||
|
||||
int count = 1;
|
||||
|
||||
if (flags.mipmaps && cdata.size() > 1)
|
||||
count = (int) cdata.size();
|
||||
else if (flags.mipmaps)
|
||||
count = cdata[0]->getMipmapCount();
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
glCompressedTexImage2D(GL_TEXTURE_2D, i, iformat,
|
||||
cdata->getWidth(i), cdata->getHeight(i), 0,
|
||||
(GLsizei) cdata->getSize(i), cdata->getData(i));
|
||||
// Compressed image mipmaps can come from separate CompressedImageData
|
||||
// objects, or all from a single object.
|
||||
auto cd = cdata.size() > 1 ? cdata[i].get() : cdata[0].get();
|
||||
int datamip = cdata.size() > 1 ? 0 : i;
|
||||
|
||||
glCompressedTexImage2D(GL_TEXTURE_2D, i, iformat, cd->getWidth(datamip),
|
||||
cd->getHeight(datamip), 0,
|
||||
(GLsizei) cd->getSize(datamip), cd->getData(datamip));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -182,23 +238,29 @@ void Image::loadFromImageData()
|
||||
iformat = format;
|
||||
}
|
||||
|
||||
int mipcount = flags.mipmaps ? (int) data.size() : 1;
|
||||
|
||||
for (int i = 0; i < mipcount; i++)
|
||||
{
|
||||
love::thread::Lock lock(data->getMutex());
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, iformat, width, height, 0, format,
|
||||
GL_UNSIGNED_BYTE, data->getData());
|
||||
love::image::ImageData *id = data[i].get();
|
||||
love::thread::Lock lock(id->getMutex());
|
||||
|
||||
glTexImage2D(GL_TEXTURE_2D, i, iformat, id->getWidth(), id->getHeight(),
|
||||
0, format, GL_UNSIGNED_BYTE, id->getData());
|
||||
}
|
||||
|
||||
generateMipmaps();
|
||||
if (data.size() <= 1)
|
||||
generateMipmaps();
|
||||
}
|
||||
|
||||
bool Image::loadVolatile()
|
||||
{
|
||||
OpenGL::TempDebugGroup debuggroup("Image load");
|
||||
|
||||
if (isCompressed() && !hasCompressedTextureSupport(cdata->getFormat(), flags.sRGB))
|
||||
if (isCompressed() && !hasCompressedTextureSupport(cdata[0]->getFormat(), flags.sRGB))
|
||||
{
|
||||
const char *str;
|
||||
if (image::CompressedImageData::getConstant(cdata->getFormat(), str))
|
||||
if (image::CompressedImageData::getConstant(cdata[0]->getFormat(), str))
|
||||
{
|
||||
throw love::Exception("Cannot create image: "
|
||||
"%s%s compressed images are not supported on this system.", flags.sRGB ? "sRGB " : "", str);
|
||||
@@ -212,7 +274,8 @@ bool Image::loadVolatile()
|
||||
throw love::Exception("sRGB images are not supported on this system.");
|
||||
|
||||
// GL_EXT_sRGB doesn't support glGenerateMipmap for sRGB textures.
|
||||
if (flags.sRGB && (GLAD_ES_VERSION_2_0 && GLAD_EXT_sRGB && !GLAD_ES_VERSION_3_0))
|
||||
if (flags.sRGB && (GLAD_ES_VERSION_2_0 && GLAD_EXT_sRGB && !GLAD_ES_VERSION_3_0)
|
||||
&& data.size() <= 1)
|
||||
{
|
||||
flags.mipmaps = false;
|
||||
filter.mipmap = FILTER_NONE;
|
||||
@@ -244,13 +307,10 @@ bool Image::loadVolatile()
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((isCompressed() || !flags.mipmaps) && (GLAD_ES_VERSION_3_0 || GLAD_VERSION_1_0))
|
||||
{
|
||||
int count = (flags.mipmaps && isCompressed()) ? cdata->getMipmapCount() : 1;
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, count - 1);
|
||||
}
|
||||
if (!flags.mipmaps && (GLAD_ES_VERSION_3_0 || GLAD_VERSION_1_0))
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
|
||||
|
||||
if (flags.mipmaps && !isCompressed() &&
|
||||
if (flags.mipmaps && !isCompressed() && data.size() <= 1 &&
|
||||
!(GLAD_ES_VERSION_2_0 || GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_object))
|
||||
{
|
||||
// Auto-generate mipmaps every time the texture is modified, if
|
||||
@@ -281,17 +341,12 @@ bool Image::loadVolatile()
|
||||
size_t prevmemsize = textureMemorySize;
|
||||
|
||||
if (isCompressed())
|
||||
{
|
||||
textureMemorySize = 0;
|
||||
for (int i = 0; i < (flags.mipmaps ? cdata->getMipmapCount() : 1); i++)
|
||||
textureMemorySize += cdata->getSize(i);
|
||||
}
|
||||
textureMemorySize = cdata[0]->getSize();
|
||||
else
|
||||
{
|
||||
textureMemorySize = width * height * 4;
|
||||
if (flags.mipmaps)
|
||||
textureMemorySize *= 1.333;
|
||||
}
|
||||
textureMemorySize = data[0]->getSize();
|
||||
|
||||
if (flags.mipmaps)
|
||||
textureMemorySize *= 1.33334;
|
||||
|
||||
gl.updateTextureMemorySize(prevmemsize, textureMemorySize);
|
||||
|
||||
@@ -323,31 +378,44 @@ bool Image::refresh(int xoffset, int yoffset, int w, int h)
|
||||
throw love::Exception("Invalid rectangle dimensions.");
|
||||
}
|
||||
|
||||
OpenGL::TempDebugGroup debuggroup("Image refresh");
|
||||
|
||||
gl.bindTexture(texture);
|
||||
|
||||
if (isCompressed())
|
||||
loadFromCompressedData();
|
||||
else
|
||||
{
|
||||
const image::pixel *pdata = (const image::pixel *) data->getData();
|
||||
pdata += yoffset * data->getWidth() + xoffset;
|
||||
|
||||
GLenum format = GL_RGBA;
|
||||
|
||||
// In ES2, the format parameter of TexSubImage must match the internal
|
||||
// format of the texture.
|
||||
if (flags.sRGB && (GLAD_ES_VERSION_2_0 && !GLAD_ES_VERSION_3_0))
|
||||
format = GL_SRGB_ALPHA;
|
||||
|
||||
{
|
||||
thread::Lock lock(data->getMutex());
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, xoffset, yoffset, w, h, format,
|
||||
GL_UNSIGNED_BYTE, pdata);
|
||||
}
|
||||
|
||||
generateMipmaps();
|
||||
loadFromCompressedData();
|
||||
return true;
|
||||
}
|
||||
|
||||
GLenum format = GL_RGBA;
|
||||
|
||||
// In ES2, the format parameter of TexSubImage must match the internal
|
||||
// format of the texture.
|
||||
if (flags.sRGB && (GLAD_ES_VERSION_2_0 && !GLAD_ES_VERSION_3_0))
|
||||
format = GL_SRGB_ALPHA;
|
||||
|
||||
int mipcount = flags.mipmaps ? (int) data.size() : 1;
|
||||
|
||||
// Reupload the sub-rectangle of each mip level (if we have custom mipmaps.)
|
||||
for (int i = 0; i < mipcount; i++)
|
||||
{
|
||||
const image::pixel *pdata = (const image::pixel *) data[i]->getData();
|
||||
pdata += yoffset * data[i]->getWidth() + xoffset;
|
||||
|
||||
thread::Lock lock(data[i]->getMutex());
|
||||
glTexSubImage2D(GL_TEXTURE_2D, i, xoffset, yoffset, w, h, format,
|
||||
GL_UNSIGNED_BYTE, pdata);
|
||||
|
||||
xoffset /= 2;
|
||||
yoffset /= 2;
|
||||
w = std::max(w / 2, 1);
|
||||
h = std::max(h / 2, 1);
|
||||
}
|
||||
|
||||
if (data.size() <= 1)
|
||||
generateMipmaps();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -388,14 +456,14 @@ const void *Image::getHandle() const
|
||||
return &texture;
|
||||
}
|
||||
|
||||
love::image::ImageData *Image::getImageData() const
|
||||
const std::vector<StrongRef<love::image::ImageData>> &Image::getImageData() const
|
||||
{
|
||||
return data.get();
|
||||
return data;
|
||||
}
|
||||
|
||||
love::image::CompressedImageData *Image::getCompressedData() const
|
||||
const std::vector<StrongRef<love::image::CompressedImageData>> &Image::getCompressedData() const
|
||||
{
|
||||
return cdata.get();
|
||||
return cdata;
|
||||
}
|
||||
|
||||
void Image::setFilter(const Texture::Filter &f)
|
||||
|
||||
@@ -69,16 +69,18 @@ public:
|
||||
* Creates a new Image. Not that anything is ready to use
|
||||
* before load is called.
|
||||
*
|
||||
* @param data The data from which to load the image.
|
||||
* @param data The data from which to load the image. Each element in the
|
||||
* array is a mipmap level. If more than the base level is present, all
|
||||
* mip levels must be present.
|
||||
**/
|
||||
Image(love::image::ImageData *data, const Flags &flags);
|
||||
Image(const std::vector<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::CompressedImageData *cdata, const Flags &flags);
|
||||
Image(const std::vector<love::image::CompressedImageData *> &cdata, const Flags &flags);
|
||||
|
||||
virtual ~Image();
|
||||
|
||||
@@ -98,8 +100,8 @@ public:
|
||||
|
||||
virtual const void *getHandle() const;
|
||||
|
||||
love::image::ImageData *getImageData() const;
|
||||
love::image::CompressedImageData *getCompressedData() const;
|
||||
const std::vector<StrongRef<love::image::ImageData>> &getImageData() const;
|
||||
const std::vector<StrongRef<love::image::CompressedImageData>> &getCompressedData() const;
|
||||
|
||||
virtual void setFilter(const Texture::Filter &f);
|
||||
virtual bool setWrap(const Texture::Wrap &w);
|
||||
@@ -147,13 +149,14 @@ private:
|
||||
|
||||
GLenum getCompressedFormat(image::CompressedImageData::Format cformat) const;
|
||||
|
||||
// The ImageData from which the texture is created. May be null if
|
||||
// The ImageData from which the texture is created. May be empty if
|
||||
// Compressed image data was used to create the texture.
|
||||
StrongRef<love::image::ImageData> data;
|
||||
// Each element in the array is a mipmap level.
|
||||
std::vector<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.
|
||||
StrongRef<love::image::CompressedImageData> cdata;
|
||||
// empty if raw ImageData was used to create the texture.
|
||||
std::vector<StrongRef<love::image::CompressedImageData>> cdata;
|
||||
|
||||
// OpenGL texture identifier.
|
||||
GLuint texture;
|
||||
|
||||
@@ -38,7 +38,7 @@ int w_Canvas_renderTo(lua_State *L)
|
||||
Canvas *canvas = luax_checkcanvas(L, 1);
|
||||
luaL_checktype(L, 2, LUA_TFUNCTION);
|
||||
|
||||
Graphics *graphics = Module::getInstance<Graphics>(Module::M_GRAPHICS);
|
||||
auto graphics = Module::getInstance<Graphics>(Module::M_GRAPHICS);
|
||||
|
||||
if (graphics)
|
||||
{
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "image/Image.h"
|
||||
#include "font/Rasterizer.h"
|
||||
#include "filesystem/wrap_Filesystem.h"
|
||||
#include "image/wrap_Image.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
@@ -236,8 +237,8 @@ static const char *imageFlagName(Image::FlagType flagtype)
|
||||
|
||||
int w_newImage(lua_State *L)
|
||||
{
|
||||
love::image::ImageData *data = nullptr;
|
||||
love::image::CompressedImageData *cdata = nullptr;
|
||||
std::vector<love::image::ImageData *> data;
|
||||
std::vector<love::image::CompressedImageData *> cdata;
|
||||
|
||||
Image::Flags flags;
|
||||
if (!lua_isnoneornil(L, 2))
|
||||
@@ -252,23 +253,23 @@ int w_newImage(lua_State *L)
|
||||
// Convert to ImageData / CompressedImageData, if necessary.
|
||||
if (lua_isstring(L, 1) || luax_istype(L, 1, FILESYSTEM_FILE_ID) || luax_istype(L, 1, FILESYSTEM_FILE_DATA_ID))
|
||||
{
|
||||
love::image::Image *image = Module::getInstance<love::image::Image>(Module::M_IMAGE);
|
||||
if (image == nullptr)
|
||||
auto imagemodule = Module::getInstance<love::image::Image>(Module::M_IMAGE);
|
||||
if (imagemodule == nullptr)
|
||||
return luaL_error(L, "Cannot load images without the love.image module.");
|
||||
|
||||
love::filesystem::FileData *fdata = love::filesystem::luax_getfiledata(L, 1);
|
||||
|
||||
if (image->isCompressed(fdata))
|
||||
if (imagemodule->isCompressed(fdata))
|
||||
{
|
||||
luax_catchexcept(L,
|
||||
[&]() { cdata = image->newCompressedData(fdata); },
|
||||
[&]() { cdata.push_back(imagemodule->newCompressedData(fdata)); },
|
||||
[&](bool) { fdata->release(); }
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
luax_catchexcept(L,
|
||||
[&]() { data = image->newImageData(fdata); },
|
||||
[&]() { data.push_back(imagemodule->newImageData(fdata)); },
|
||||
[&](bool) { fdata->release(); }
|
||||
);
|
||||
}
|
||||
@@ -277,27 +278,61 @@ int w_newImage(lua_State *L)
|
||||
releasedata = true;
|
||||
}
|
||||
else if (luax_istype(L, 1, IMAGE_COMPRESSED_IMAGE_DATA_ID))
|
||||
cdata = luax_checktype<love::image::CompressedImageData>(L, 1, IMAGE_COMPRESSED_IMAGE_DATA_ID);
|
||||
cdata.push_back(love::image::luax_checkcompressedimagedata(L, 1));
|
||||
else
|
||||
data = luax_checktype<love::image::ImageData>(L, 1, IMAGE_IMAGE_DATA_ID);
|
||||
data.push_back(love::image::luax_checkimagedata(L, 1));
|
||||
|
||||
if (!data && !cdata)
|
||||
return luaL_error(L, "Error creating image (could not load data.)");
|
||||
if (lua_istable(L, 2))
|
||||
{
|
||||
lua_getfield(L, 2, imageFlagName(Image::FLAG_TYPE_MIPMAPS));
|
||||
|
||||
// Add all manually specified mipmap images to the array of imagedata.
|
||||
// i.e. flags = {mipmaps = {mip1, mip2, ...}}.
|
||||
if (lua_istable(L, -1))
|
||||
{
|
||||
for (size_t i = 1; i <= luax_objlen(L, -1); i++)
|
||||
{
|
||||
lua_rawgeti(L, -1, i);
|
||||
|
||||
if (!data.empty())
|
||||
{
|
||||
if (!luax_istype(L, -1, IMAGE_IMAGE_DATA_ID))
|
||||
luax_convobj(L, -1, "image", "newImageData");
|
||||
|
||||
data.push_back(love::image::luax_checkimagedata(L, -1));
|
||||
}
|
||||
else if (!cdata.empty())
|
||||
{
|
||||
if (!luax_istype(L, -1, IMAGE_COMPRESSED_IMAGE_DATA_ID))
|
||||
luax_convobj(L, -1, "image", "newCompressedData");
|
||||
|
||||
cdata.push_back(love::image::luax_checkcompressedimagedata(L, -1));
|
||||
}
|
||||
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
}
|
||||
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
// Create the image.
|
||||
Image *image = nullptr;
|
||||
luax_catchexcept(L,
|
||||
[&]() {
|
||||
if (cdata)
|
||||
if (!cdata.empty())
|
||||
image = instance()->newImage(cdata, flags);
|
||||
else if (data)
|
||||
else if (!data.empty())
|
||||
image = instance()->newImage(data, flags);
|
||||
},
|
||||
[&](bool) {
|
||||
if (releasedata && data)
|
||||
data->release();
|
||||
else if (releasedata && cdata)
|
||||
cdata->release();
|
||||
if (releasedata)
|
||||
{
|
||||
for (auto d : data)
|
||||
d->release();
|
||||
for (auto d : cdata)
|
||||
d->release();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
@@ -363,10 +398,10 @@ int w_newImageFont(lua_State *L)
|
||||
{
|
||||
Image *i = luax_checktype<Image>(L, 1, GRAPHICS_IMAGE_ID);
|
||||
filter = i->getFilter();
|
||||
love::image::ImageData *id = i->getImageData();
|
||||
if (!id)
|
||||
const auto &idlevels = i->getImageData();
|
||||
if (idlevels.empty())
|
||||
return luaL_argerror(L, 1, "Image must not be compressed.");
|
||||
luax_pushtype(L, IMAGE_IMAGE_DATA_ID, id);
|
||||
luax_pushtype(L, IMAGE_IMAGE_DATA_ID, idlevels[0].get());
|
||||
lua_replace(L, 1);
|
||||
}
|
||||
|
||||
|
||||
@@ -92,13 +92,26 @@ int w_Image_refresh(lua_State *L)
|
||||
int w_Image_getData(lua_State *L)
|
||||
{
|
||||
Image *i = luax_checkimage(L, 1);
|
||||
int n = 0;
|
||||
|
||||
if (i->isCompressed())
|
||||
luax_pushtype(L, IMAGE_COMPRESSED_IMAGE_DATA_ID, i->getCompressedData());
|
||||
{
|
||||
for (const auto &cdata : i->getCompressedData())
|
||||
{
|
||||
luax_pushtype(L, IMAGE_COMPRESSED_IMAGE_DATA_ID, cdata.get());
|
||||
n++;
|
||||
}
|
||||
}
|
||||
else
|
||||
luax_pushtype(L, IMAGE_IMAGE_DATA_ID, i->getImageData());
|
||||
{
|
||||
for (const auto &data : i->getImageData())
|
||||
{
|
||||
luax_pushtype(L, IMAGE_IMAGE_DATA_ID, data.get());
|
||||
n++;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
return n;
|
||||
}
|
||||
|
||||
static const char *imageFlagName(Image::FlagType flagtype)
|
||||
|
||||
@@ -107,12 +107,12 @@ public:
|
||||
/**
|
||||
* Gets the width of a sub-image at the specified mipmap level.
|
||||
**/
|
||||
int getWidth(int miplevel) const;
|
||||
int getWidth(int miplevel = 0) const;
|
||||
|
||||
/**
|
||||
* Gets the height of a sub-image at the specified mipmap level.
|
||||
**/
|
||||
int getHeight(int miplevel) const;
|
||||
int getHeight(int miplevel = 0) const;
|
||||
|
||||
/**
|
||||
* Gets the format of the compressed data.
|
||||
|
||||
@@ -122,7 +122,7 @@ void Keyboard::setTextInput(bool enable, double x, double y, double w, double h)
|
||||
{
|
||||
// SDL_SetTextInputRect expects coordinates in window-space but setTextInput
|
||||
// takes pixels, so we should convert.
|
||||
window::Window *window = Module::getInstance<window::Window>(M_WINDOW);
|
||||
auto window = Module::getInstance<window::Window>(M_WINDOW);
|
||||
if (window)
|
||||
{
|
||||
window->pixelToWindowCoords(&x, &y);
|
||||
|
||||
@@ -36,7 +36,7 @@ namespace sdl
|
||||
// we want them in pixel coordinates (may be different with high-DPI enabled.)
|
||||
static void windowToPixelCoords(double *x, double *y)
|
||||
{
|
||||
window::Window *window = Module::getInstance<window::Window>(Module::M_WINDOW);
|
||||
auto window = Module::getInstance<window::Window>(Module::M_WINDOW);
|
||||
if (window)
|
||||
window->windowToPixelCoords(x, y);
|
||||
}
|
||||
@@ -44,7 +44,7 @@ static void windowToPixelCoords(double *x, double *y)
|
||||
// And vice versa for setting mouse coordinates.
|
||||
static void pixelToWindowCoords(double *x, double *y)
|
||||
{
|
||||
window::Window *window = Module::getInstance<window::Window>(Module::M_WINDOW);
|
||||
auto window = Module::getInstance<window::Window>(Module::M_WINDOW);
|
||||
if (window)
|
||||
window->pixelToWindowCoords(x, y);
|
||||
}
|
||||
@@ -146,7 +146,7 @@ void Mouse::getPosition(double &x, double &y) const
|
||||
|
||||
void Mouse::setPosition(double x, double y)
|
||||
{
|
||||
window::Window *window = Module::getInstance<window::Window>(Module::M_WINDOW);
|
||||
auto window = Module::getInstance<window::Window>(Module::M_WINDOW);
|
||||
|
||||
SDL_Window *handle = nullptr;
|
||||
if (window)
|
||||
@@ -211,14 +211,14 @@ bool Mouse::isVisible() const
|
||||
|
||||
void Mouse::setGrabbed(bool grab)
|
||||
{
|
||||
window::Window *window = Module::getInstance<window::Window>(Module::M_WINDOW);
|
||||
auto window = Module::getInstance<window::Window>(Module::M_WINDOW);
|
||||
if (window)
|
||||
window->setMouseGrab(grab);
|
||||
}
|
||||
|
||||
bool Mouse::isGrabbed() const
|
||||
{
|
||||
window::Window *window = Module::getInstance<window::Window>(Module::M_WINDOW);
|
||||
auto window = Module::getInstance<window::Window>(Module::M_WINDOW);
|
||||
if (window)
|
||||
return window->isMouseGrabbed();
|
||||
else
|
||||
|
||||
@@ -118,8 +118,8 @@ void LuaThread::onError()
|
||||
if (error.empty())
|
||||
return;
|
||||
|
||||
event::Event *event = Module::getInstance<event::Event>(Module::M_EVENT);
|
||||
if (!event)
|
||||
auto eventmodule = Module::getInstance<event::Event>(Module::M_EVENT);
|
||||
if (!eventmodule)
|
||||
return;
|
||||
|
||||
Proxy p;
|
||||
@@ -136,7 +136,7 @@ void LuaThread::onError()
|
||||
for (const StrongRef<Variant> &v : vargs)
|
||||
v->release();
|
||||
|
||||
event->push(msg);
|
||||
eventmodule->push(msg);
|
||||
msg->release();
|
||||
}
|
||||
|
||||
|
||||
@@ -466,7 +466,7 @@ bool Window::onSizeChanged(int width, int height)
|
||||
|
||||
SDL_GL_GetDrawableSize(window, &curMode.pixelwidth, &curMode.pixelheight);
|
||||
|
||||
graphics::Graphics *gfx = Module::getInstance<graphics::Graphics>(Module::M_GRAPHICS);
|
||||
auto gfx = Module::getInstance<graphics::Graphics>(Module::M_GRAPHICS);
|
||||
if (gfx != nullptr)
|
||||
gfx->setViewportSize(curMode.pixelwidth, curMode.pixelheight);
|
||||
|
||||
@@ -609,7 +609,7 @@ bool Window::setFullscreen(bool fullscreen, Window::FullscreenType fstype)
|
||||
updateSettings(newsettings);
|
||||
|
||||
// Update the viewport size now instead of waiting for event polling.
|
||||
graphics::Graphics *gfx = Module::getInstance<graphics::Graphics>(Module::M_GRAPHICS);
|
||||
auto gfx = Module::getInstance<graphics::Graphics>(Module::M_GRAPHICS);
|
||||
if (gfx != nullptr)
|
||||
gfx->setViewportSize(curMode.pixelwidth, curMode.pixelheight);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user