ImageData (and Images loaded from them) now support different data formats. Resolves issue #1048.

Currently exposed formats are rgba8 and rgba16 (normalized), and rgba16f and rgba32f (floating-point). Some systems, especially mobile ones, won't support every format when creating a love.graphics Image. Use love.graphics.getRawImageFormats to check for support.

love.image.newImageData now takes an optional format parameter as its third argument when creating an empty sized ImageData. It defaults to rgba8.

16-bit PNGs, .hdr images, and floating-point OpenEXR images can now be loaded via love.image.newImageData and love.graphics.newImage.

--HG--
branch : minor
This commit is contained in:
Alex Szpakowski
2016-05-22 15:28:20 -03:00
parent 3e6d01748e
commit e71f95595c
31 changed files with 12018 additions and 241 deletions
+14
View File
@@ -91,6 +91,8 @@ set(LOVE_SRC_COMMON
src/common/EnumMap.h
src/common/Exception.cpp
src/common/Exception.h
src/common/halffloat.cpp
src/common/halffloat.h
src/common/int.h
src/common/math.h
src/common/Matrix.cpp
@@ -370,6 +372,8 @@ set(LOVE_SRC_MODULE_IMAGE_MAGPIE
src/modules/image/magpie/CompressedFormatHandler.h
src/modules/image/magpie/ddsHandler.cpp
src/modules/image/magpie/ddsHandler.h
src/modules/image/magpie/EXRHandler.cpp
src/modules/image/magpie/EXRHandler.h
src/modules/image/magpie/FormatHandler.cpp
src/modules/image/magpie/FormatHandler.h
src/modules/image/magpie/Image.cpp
@@ -1167,6 +1171,16 @@ set(LOVE_SRC_3P_STB
# stb_image has no implementation files of its own.
#
# tiny exr
#
set(LOVE_SRC_3P_TINEXR
src/libraries/tinyexr/tinyexr.h
)
# tinyexr has no implementation files of its own.
#
# utf8
#
@@ -816,6 +816,10 @@
FA0B7EE91A95902D000E1D17 /* wrap_Window.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA0B7CCB1A95902C000E1D17 /* wrap_Window.cpp */; };
FA0B7EEA1A95902D000E1D17 /* wrap_Window.h in Headers */ = {isa = PBXBuildFile; fileRef = FA0B7CCC1A95902C000E1D17 /* wrap_Window.h */; };
FA0B7EF21A959D2C000E1D17 /* ios.mm in Sources */ = {isa = PBXBuildFile; fileRef = FA0B7EF11A959D2C000E1D17 /* ios.mm */; };
FA1557C01CE90A2C00AFF582 /* tinyexr.h in Headers */ = {isa = PBXBuildFile; fileRef = FA1557BF1CE90A2C00AFF582 /* tinyexr.h */; };
FA1557C31CE90BD200AFF582 /* EXRHandler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA1557C11CE90BD200AFF582 /* EXRHandler.cpp */; };
FA1557C41CE90BD200AFF582 /* EXRHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = FA1557C21CE90BD200AFF582 /* EXRHandler.h */; };
FA1557C51CE90BD900AFF582 /* EXRHandler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA1557C11CE90BD200AFF582 /* EXRHandler.cpp */; };
FA19C4C51B4B0BD50059B0B3 /* wrap_Video.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA19C4C21B4B0BD50059B0B3 /* wrap_Video.cpp */; };
FA19C4C61B4B0BD50059B0B3 /* wrap_Video.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA19C4C21B4B0BD50059B0B3 /* wrap_Video.cpp */; };
FA19C4C71B4B0BD50059B0B3 /* wrap_Video.h in Headers */ = {isa = PBXBuildFile; fileRef = FA19C4C31B4B0BD50059B0B3 /* wrap_Video.h */; };
@@ -878,6 +882,9 @@
FA8951A21AA2EDF300EC385A /* wrap_Event.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA8951A01AA2EDF300EC385A /* wrap_Event.cpp */; };
FA8951A31AA2EDF300EC385A /* wrap_Event.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA8951A01AA2EDF300EC385A /* wrap_Event.cpp */; };
FA8951A41AA2EDF300EC385A /* wrap_Event.h in Headers */ = {isa = PBXBuildFile; fileRef = FA8951A11AA2EDF300EC385A /* wrap_Event.h */; };
FA91591E1CF1ED7500A7053F /* halffloat.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA91591C1CF1ED7500A7053F /* halffloat.cpp */; };
FA91591F1CF1ED7500A7053F /* halffloat.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA91591C1CF1ED7500A7053F /* halffloat.cpp */; };
FA9159201CF1ED7500A7053F /* halffloat.h in Headers */ = {isa = PBXBuildFile; fileRef = FA91591D1CF1ED7500A7053F /* halffloat.h */; };
FA9B4A0816E1578300074F42 /* SDL2.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FA9B4A0716E1578300074F42 /* SDL2.framework */; };
FAA3A9AE1B7D465A00CED060 /* android.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FAA3A9AC1B7D465A00CED060 /* android.cpp */; };
FAA3A9AF1B7D465A00CED060 /* android.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FAA3A9AC1B7D465A00CED060 /* android.cpp */; };
@@ -1501,6 +1508,9 @@
FA0B7CCC1A95902C000E1D17 /* wrap_Window.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = wrap_Window.h; sourceTree = "<group>"; };
FA0B7EF01A959D2C000E1D17 /* ios.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ios.h; sourceTree = "<group>"; };
FA0B7EF11A959D2C000E1D17 /* ios.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ios.mm; sourceTree = "<group>"; };
FA1557BF1CE90A2C00AFF582 /* tinyexr.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = tinyexr.h; sourceTree = "<group>"; };
FA1557C11CE90BD200AFF582 /* EXRHandler.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = EXRHandler.cpp; sourceTree = "<group>"; };
FA1557C21CE90BD200AFF582 /* EXRHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EXRHandler.h; sourceTree = "<group>"; };
FA19C4C21B4B0BD50059B0B3 /* wrap_Video.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = wrap_Video.cpp; sourceTree = "<group>"; };
FA19C4C31B4B0BD50059B0B3 /* wrap_Video.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = wrap_Video.h; sourceTree = "<group>"; };
FA19C4C41B4B0BD50059B0B3 /* wrap_Video.lua */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = wrap_Video.lua; sourceTree = "<group>"; };
@@ -1558,6 +1568,8 @@
FA7DA04C1C16874A0056B200 /* wrap_Math.lua */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = wrap_Math.lua; sourceTree = "<group>"; };
FA8951A01AA2EDF300EC385A /* wrap_Event.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = wrap_Event.cpp; sourceTree = "<group>"; };
FA8951A11AA2EDF300EC385A /* wrap_Event.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = wrap_Event.h; sourceTree = "<group>"; };
FA91591C1CF1ED7500A7053F /* halffloat.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = halffloat.cpp; sourceTree = "<group>"; };
FA91591D1CF1ED7500A7053F /* halffloat.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = halffloat.h; sourceTree = "<group>"; };
FA9B4A0716E1578300074F42 /* SDL2.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SDL2.framework; path = /Library/Frameworks/SDL2.framework; sourceTree = "<absolute>"; };
FAA3A9AC1B7D465A00CED060 /* android.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = android.cpp; sourceTree = "<group>"; };
FAA3A9AD1B7D465A00CED060 /* android.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = android.h; sourceTree = "<group>"; };
@@ -1680,9 +1692,13 @@
FA0B78FD1A958E3B000E1D17 /* EnumMap.h */,
FA0B78FE1A958E3B000E1D17 /* Exception.cpp */,
FA0B78FF1A958E3B000E1D17 /* Exception.h */,
FA91591C1CF1ED7500A7053F /* halffloat.cpp */,
FA91591D1CF1ED7500A7053F /* halffloat.h */,
FA0B79001A958E3B000E1D17 /* int.h */,
FA0B7EF01A959D2C000E1D17 /* ios.h */,
FA0B7EF11A959D2C000E1D17 /* ios.mm */,
FA0B790A1A958E3B000E1D17 /* macosx.h */,
FA0B790B1A958E3B000E1D17 /* macosx.mm */,
FA0B79011A958E3B000E1D17 /* math.h */,
FA0B79021A958E3B000E1D17 /* Matrix.cpp */,
FA0B79031A958E3B000E1D17 /* Matrix.h */,
@@ -1692,8 +1708,6 @@
FA0B79071A958E3B000E1D17 /* Module.h */,
FA0B79081A958E3B000E1D17 /* Object.cpp */,
FA0B79091A958E3B000E1D17 /* Object.h */,
FA0B790A1A958E3B000E1D17 /* macosx.h */,
FA0B790B1A958E3B000E1D17 /* macosx.mm */,
FA0B790C1A958E3B000E1D17 /* Reference.cpp */,
FA0B790D1A958E3B000E1D17 /* Reference.h */,
FA0B790E1A958E3B000E1D17 /* runtime.cpp */,
@@ -1728,6 +1742,7 @@
FAB17BE31ABFAA9000F9BA27 /* lz4 */,
FA0B7A141A958EA3000E1D17 /* noise1234 */,
FA0B7A171A958EA3000E1D17 /* stb */,
FA1557BE1CE90A2C00AFF582 /* tinyexr */,
FA0B7A191A958EA3000E1D17 /* utf8 */,
FA0B7A1F1A958EA3000E1D17 /* Wuff */,
);
@@ -2369,6 +2384,8 @@
FA0B7BCA1A95902C000E1D17 /* CompressedImageData.h */,
FA0B7BCC1A95902C000E1D17 /* ddsHandler.cpp */,
FA0B7BCD1A95902C000E1D17 /* ddsHandler.h */,
FA1557C11CE90BD200AFF582 /* EXRHandler.cpp */,
FA1557C21CE90BD200AFF582 /* EXRHandler.h */,
FA0B7BCE1A95902C000E1D17 /* FormatHandler.cpp */,
FA0B7BCF1A95902C000E1D17 /* FormatHandler.h */,
FA0B7BD01A95902C000E1D17 /* Image.cpp */,
@@ -2792,6 +2809,14 @@
name = macosx;
sourceTree = "<group>";
};
FA1557BE1CE90A2C00AFF582 /* tinyexr */ = {
isa = PBXGroup;
children = (
FA1557BF1CE90A2C00AFF582 /* tinyexr.h */,
);
path = tinyexr;
sourceTree = "<group>";
};
FA27B3881B498151008A9DCE /* video */ = {
isa = PBXGroup;
children = (
@@ -2942,6 +2967,7 @@
FA0B7D7E1A95902C000E1D17 /* Texture.h in Headers */,
FA0B7E561A95902C000E1D17 /* wrap_GearJoint.h in Headers */,
FA0B7E1D1A95902C000E1D17 /* MouseJoint.h in Headers */,
FA1557C41CE90BD200AFF582 /* EXRHandler.h in Headers */,
FA0B7DC91A95902C000E1D17 /* Keyboard.h in Headers */,
FA0B7D4A1A95902C000E1D17 /* Polyline.h in Headers */,
FA0B7DB31A95902C000E1D17 /* wrap_Image.h in Headers */,
@@ -3006,6 +3032,7 @@
FA0B7EDE1A95902D000E1D17 /* Touch.h in Headers */,
FA0B7A541A958EA3000E1D17 /* b2Math.h in Headers */,
FA0B7ED01A95902C000E1D17 /* wrap_LuaThread.h in Headers */,
FA9159201CF1ED7500A7053F /* halffloat.h in Headers */,
FA0B7CE41A95902C000E1D17 /* wrap_Audio.h in Headers */,
FA0B7A7F1A958EA3000E1D17 /* b2ContactSolver.h in Headers */,
FA0B79491A958E3B000E1D17 /* version.h in Headers */,
@@ -3069,6 +3096,7 @@
FA0B7A401A958EA3000E1D17 /* b2ChainShape.h in Headers */,
FA0B7AA61A958EA3000E1D17 /* b2RevoluteJoint.h in Headers */,
FA0B7EEA1A95902D000E1D17 /* wrap_Window.h in Headers */,
FA1557C01CE90A2C00AFF582 /* tinyexr.h in Headers */,
FA0B7E381A95902C000E1D17 /* WheelJoint.h in Headers */,
FA0B7D851A95902C000E1D17 /* Image.h in Headers */,
FA0B7E7D1A95902C000E1D17 /* wrap_World.h in Headers */,
@@ -3378,6 +3406,7 @@
FA0B7D041A95902C000E1D17 /* wrap_DroppedFile.cpp in Sources */,
FA0B7B261A958EA3000E1D17 /* lutf8lib.c in Sources */,
FA0B7D461A95902C000E1D17 /* ParticleSystem.cpp in Sources */,
FA91591F1CF1ED7500A7053F /* halffloat.cpp in Sources */,
FA8951A31AA2EDF300EC385A /* wrap_Event.cpp in Sources */,
FA0B7A361A958EA3000E1D17 /* b2Distance.cpp in Sources */,
FA0B7D4C1A95902C000E1D17 /* Shader.cpp in Sources */,
@@ -3530,6 +3559,7 @@
FA0B7E761A95902C000E1D17 /* wrap_WeldJoint.cpp in Sources */,
FA0B7D561A95902C000E1D17 /* GLBuffer.cpp in Sources */,
FA0B7A6C1A958EA3000E1D17 /* b2World.cpp in Sources */,
FA1557C51CE90BD900AFF582 /* EXRHandler.cpp in Sources */,
FA57FB991AE1993600F2AD6D /* noise1234.cpp in Sources */,
FA0B7E221A95902C000E1D17 /* PolygonShape.cpp in Sources */,
FA0B7A651A958EA3000E1D17 /* b2Fixture.cpp in Sources */,
@@ -3774,6 +3804,7 @@
FA27B3A01B498151008A9DCE /* VideoStream.cpp in Sources */,
FA0B7CEB1A95902C000E1D17 /* Event.cpp in Sources */,
FA0B7B0B1A958EA3000E1D17 /* tcp.c in Sources */,
FA1557C31CE90BD200AFF582 /* EXRHandler.cpp in Sources */,
FA27B3AA1B498151008A9DCE /* VideoStream.cpp in Sources */,
FA0B7D961A95902C000E1D17 /* ImageData.cpp in Sources */,
FA0B7E571A95902C000E1D17 /* wrap_Joint.cpp in Sources */,
@@ -3858,6 +3889,7 @@
FA1DC2631C5D9555008F99A0 /* HashFunction.cpp in Sources */,
FA620A3A1AA305F6005DB4C2 /* types.cpp in Sources */,
FA0B7DD31A95902C000E1D17 /* BezierCurve.cpp in Sources */,
FA91591E1CF1ED7500A7053F /* halffloat.cpp in Sources */,
FA0B7E7B1A95902C000E1D17 /* wrap_World.cpp in Sources */,
FA0B7B281A958EA3000E1D17 /* simplexnoise1234.cpp in Sources */,
FA0B7D901A95902C000E1D17 /* FormatHandler.cpp in Sources */,
+1 -1
View File
@@ -26,7 +26,7 @@
# define LOVE_WINDOWS 1
// If _USING_V110_SDK71_ is defined it means we are using the xp toolset.
# if defined(_MSC_VER) && (_MSC_VER >= 1700) && !_USING_V110_SDK71_
# include <winapifamily.h>
# include <winapifamily.h>
# if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP) && !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
# define LOVE_WINDOWS_UWP 1
# define LOVE_NO_MODPLUG 1
+156
View File
@@ -0,0 +1,156 @@
/**
* Copyright (c) 2006-2016 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 "halffloat.h"
namespace love
{
// Code from ftp://www.fox-toolkit.org/pub/fasthalffloatconversion.pdf
static bool initialized = false;
// tables for half -> float conversions
static uint32 mantissatable[2048];
static uint16 offsettable[64];
static uint32 exponenttable[64];
// tables for float -> half conversions
static uint16 basetable[512];
static uint8 shifttable[512];
static uint32 convertMantissa(uint32 i)
{
uint32 m = i << 13; // Zero pad mantissa bits
uint32 e = 0; // Zero exponent
while (!(m & 0x00800000)) // While not normalized
{
e -= 0x00800000; // Decrement exponent (1<<23)
m <<= 1; // Shift mantissa
}
m &= ~(0x00800000); // Clear leading 1 bit
e += 0x38800000; // Adjust bias ((127-14)<<23)
return m | e; // Return combined number
}
void halfInit()
{
if (initialized)
return;
initialized = true;
// tables for half -> float conversions.
mantissatable[0] = 0;
for (uint32 i = 1; i < 1024; i++)
mantissatable[i] = convertMantissa(i);
for (uint32 i = 1024; i < 2048; i++)
mantissatable[i] = 0x38000000 + ((i - 1024) << 13);
exponenttable[0] = 0;
exponenttable[32] = 0x80000000;
for (uint32 i = 0; i < 31; i++)
exponenttable[i] = i << 23;
for (uint32 i = 33; i < 63; i++)
exponenttable[i] = 0x80000000 + ((i - 32) << 23);
exponenttable[31] = 0x47800000;
exponenttable[63] = 0xC7800000;
for (int i = 0; i < 64; i++)
{
if (i == 0 || i == 32)
offsettable[i] = 0;
else
offsettable[i] = 1024;
}
// tables for float -> half conversions.
for (uint32 i = 0; i < 256; i++)
{
int e = (int) i - 127;
if (e < -24) // Very small numbers map to zero
{
basetable[i | 0x000] = 0x0000;
basetable[i | 0x100] = 0x8000;
shifttable[i | 0x000] = 24;
shifttable[i | 0x100] = 24;
}
else if (e < -14) // Small numbers map to denorms
{
basetable[i | 0x000] = (0x0400 >> (-e - 14));
basetable[i | 0x100] = (0x0400 >> (-e - 14)) | 0x8000;
shifttable[i | 0x000] = -e - 1;
shifttable[i | 0x100] = -e - 1;
}
else if (e <= 15) // Normal numbers just lose precision
{
basetable[i | 0x000] = ((e + 15) << 10);
basetable[i | 0x100] = ((e + 15) << 10) | 0x8000;
shifttable[i | 0x000] = 13;
shifttable[i | 0x100] = 13;
}
else if (e < 128) // Large numbers map to Infinity
{
basetable[i | 0x000] = 0x7C00;
basetable[i | 0x100] = 0xFC00;
shifttable[i | 0x000] = 24;
shifttable[i | 0x100] = 24;
}
else // Infinity and NaN's stay Infinity and NaN's
{
basetable[i | 0x000] = 0x7C00;
basetable[i | 0x100] = 0xFC00;
shifttable[i | 0x000] = 13;
shifttable[i | 0x100] = 13;
}
}
}
float halfToFloat(half h)
{
union { float f; uint32 i; } conv;
conv.i = mantissatable[offsettable[h >> 10] + (h & 0x3FF)] + exponenttable[h >> 10];
return conv.f;
}
half floatToHalf(float f)
{
union { float f; uint32 i; } conv;
conv.f = f;
return basetable[(conv.i >> 23) & 0x1FF] + ((conv.i & 0x007FFFFF) >> shifttable[(conv.i >> 23) & 0x1FF]);
}
} // love
+38
View File
@@ -0,0 +1,38 @@
/**
* Copyright (c) 2006-2016 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_HALF_FLOAT_H
#define LOVE_HALF_FLOAT_H
#include "int.h"
namespace love
{
typedef uint16 half;
void halfInit();
float halfToFloat(half h);
half floatToHalf(float f);
} // love
#endif // LOVE_HALF_FLOAT_H
File diff suppressed because it is too large Load Diff
+13 -2
View File
@@ -189,6 +189,7 @@ void BMFontRasterizer::parseConfig(const std::string &configtext)
if (images[pageindex].get() == nullptr)
{
using namespace love::filesystem;
using namespace love::image;
auto filesystem = Module::getInstance<Filesystem>(Module::M_FILESYSTEM);
auto imagemodule = Module::getInstance<image::Image>(Module::M_IMAGE);
@@ -201,8 +202,16 @@ void BMFontRasterizer::parseConfig(const std::string &configtext)
// read() returns a retained ref already.
StrongRef<FileData> data(filesystem->read(filename.c_str()), Acquire::NORETAIN);
ImageData *imagedata = imagemodule->newImageData(data.get());
if (imagedata->getFormat() != ImageData::FORMAT_RGBA8)
{
imagedata->release();
throw love::Exception("Only 32-bit RGBA images are supported in BMFonts.");
}
// Same with newImageData.
images[pageindex].set(imagemodule->newImageData(data.get()), Acquire::NORETAIN);
images[pageindex].set(imagedata, Acquire::NORETAIN);
}
}
else if (tag == "char")
@@ -296,6 +305,8 @@ GlyphData *BMFontRasterizer::getGlyphData(uint32 glyph) const
}
image::ImageData *imagedata = imagepair->second.get();
size_t pixelsize = imagedata->getPixelSize();
image::pixel *pixels = (image::pixel *) g->getData();
const image::pixel *ipixels = (const image::pixel *) imagedata->getData();
@@ -305,7 +316,7 @@ GlyphData *BMFontRasterizer::getGlyphData(uint32 glyph) const
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);
memcpy(&pixels[y * c.metrics.width], &ipixels[idindex], pixelsize * c.metrics.width);
}
return g;
+3
View File
@@ -40,6 +40,9 @@ ImageRasterizer::ImageRasterizer(love::image::ImageData *data, uint32 *glyphs, i
, numglyphs(numglyphs)
, extraSpacing(extraspacing)
{
if (data->getFormat() != image::ImageData::FORMAT_RGBA8)
throw love::Exception("Only 32-bit RGBA images are supported in Image Fonts!");
load();
}
+1 -1
View File
@@ -633,7 +633,7 @@ love::image::ImageData *Canvas::newImageData(love::image::Image *image, int x, i
gl.bindFramebuffer(GL_FRAMEBUFFER, prevfbo);
// The new ImageData now owns the pixel data, so we don't delete it here.
return image->newImageData(w, h, pixels, true);
return image->newImageData(w, h, image::ImageData::FORMAT_RGBA8, pixels, true);
}
bool Canvas::resolveMSAA(bool restoreprev)
+1 -1
View File
@@ -1600,7 +1600,7 @@ love::image::ImageData *Graphics::newScreenshot(love::image::Image *image, bool
{
// Tell the new ImageData that it owns the screenshot data, so we don't
// need to delete it here.
img = image->newImageData(w, h, (void *) screenshot, true);
img = image->newImageData(w, h, image::ImageData::FORMAT_RGBA8, screenshot, true);
}
catch (love::Exception &)
{
+96 -22
View File
@@ -66,6 +66,8 @@ static bool verifyMipmapLevels(const std::vector<T> &miplevels)
int width = miplevels[0]->getWidth();
int height = miplevels[0]->getHeight();
auto format = miplevels[0]->getFormat();
int expectedlevels = getMipmapCount(width, height);
// All mip levels must be present when not using auto-generated mipmaps.
@@ -82,6 +84,9 @@ static bool verifyMipmapLevels(const std::vector<T> &miplevels)
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());
if (miplevels[i]->getFormat() != format)
throw love::Exception("All image mipmap levels must have the same format.");
}
return true;
@@ -140,12 +145,8 @@ Image::Image(const std::vector<love::image::CompressedImageData *> &compressedda
}
}
for (const auto &cd : compresseddata)
{
for (image::CompressedImageData *cd : compresseddata)
cdata.push_back(cd);
if (cd->getFormat() != cdata[0]->getFormat())
throw love::Exception("All image mipmap levels must have the same format.");
}
preload();
loadVolatile();
@@ -256,15 +257,12 @@ void Image::loadFromCompressedData()
void Image::loadFromImageData()
{
GLenum iformat = sRGB ? GL_SRGB8_ALPHA8 : GL_RGBA8;
GLenum format = GL_RGBA;
GLenum glformat = GL_RGBA;
GLenum gltype = GL_UNSIGNED_BYTE;
GLenum iformat = getFormat(data[0]->getFormat(), glformat, gltype, sRGB);
// in GLES2, the internalformat and format params of TexImage have to match.
if (GLAD_ES_VERSION_2_0 && !GLAD_ES_VERSION_3_0)
{
format = sRGB ? GL_SRGB_ALPHA : GL_RGBA;
iformat = format;
}
if (isGammaCorrect() && !sRGB)
flags.linear = true;
int mipcount = flags.mipmaps ? (int) data.size() : 1;
@@ -274,7 +272,7 @@ void Image::loadFromImageData()
love::thread::Lock lock(id->getMutex());
glTexImage2D(GL_TEXTURE_2D, i, iformat, id->getWidth(), id->getHeight(),
0, format, GL_UNSIGNED_BYTE, id->getData());
0, glformat, gltype, id->getData());
}
if (data.size() <= 1)
@@ -416,12 +414,9 @@ bool Image::refresh(int xoffset, int yoffset, int w, int h)
return true;
}
GLenum format = GL_RGBA;
// In ES2, the format parameter of TexSubImage must match the internal
// format of the texture.
if (sRGB && (GLAD_ES_VERSION_2_0 && !GLAD_ES_VERSION_3_0))
format = GL_SRGB_ALPHA;
GLenum glformat = GL_RGBA;
GLenum gltype = GL_UNSIGNED_BYTE;
getFormat(data[0]->getFormat(), glformat, gltype, sRGB);
int mipcount = flags.mipmaps ? (int) data.size() : 1;
@@ -432,8 +427,8 @@ bool Image::refresh(int xoffset, int yoffset, int w, int h)
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);
glTexSubImage2D(GL_TEXTURE_2D, i, xoffset, yoffset, w, h, glformat,
gltype, pdata);
xoffset /= 2;
yoffset /= 2;
@@ -596,6 +591,53 @@ bool Image::isCompressed() const
return compressed;
}
GLenum Image::getFormat(image::ImageData::Format format, GLenum &glformat, GLenum &gltype, bool &isSRGB) const
{
using image::ImageData;
GLenum internalformat = GL_RGBA8;
glformat = GL_RGBA;
gltype = GL_UNSIGNED_BYTE;
switch (format)
{
case ImageData::FORMAT_RGBA8:
if (isSRGB)
{
internalformat = GL_SRGB8_ALPHA8;
if (GLAD_ES_VERSION_2_0 && !GLAD_ES_VERSION_3_0)
glformat = GL_SRGB_ALPHA;
}
break;
case ImageData::FORMAT_RGBA16:
internalformat = GL_RGBA16;
gltype = GL_UNSIGNED_SHORT;
isSRGB = false;
break;
case ImageData::FORMAT_RGBA16F:
internalformat = GL_RGBA16F;
// HALF_FLOAT_OES has a different value than HALF_FLOAT... of course
if (GLAD_OES_texture_half_float && !GLAD_ES_VERSION_3_0)
gltype = GL_HALF_FLOAT_OES;
else
gltype = GL_HALF_FLOAT;
isSRGB = false;
break;
case ImageData::FORMAT_RGBA32F:
internalformat = GL_RGBA32F;
gltype = GL_FLOAT;
isSRGB = false;
break;
default:
break;
}
if (GLAD_ES_VERSION_2_0 && !GLAD_ES_VERSION_3_0)
internalformat = glformat;
return internalformat;
}
GLenum Image::getCompressedFormat(image::CompressedImageData::Format cformat, bool &isSRGB) const
{
using image::CompressedImageData;
@@ -701,6 +743,38 @@ bool Image::hasAnisotropicFilteringSupport()
return GLAD_EXT_texture_filter_anisotropic != GL_FALSE;
}
bool Image::hasTextureSupport(image::ImageData::Format format)
{
using image::ImageData;
switch (format)
{
case ImageData::FORMAT_RGBA8:
return true;
case ImageData::FORMAT_RGBA16:
return GLAD_VERSION_1_1 || GLAD_EXT_texture_norm16;
case ImageData::FORMAT_RGBA16F:
return GLAD_VERSION_3_0 || (GLAD_ARB_texture_float && GLAD_ARB_half_float_pixel) || GLAD_ES_VERSION_3_0 || GLAD_OES_texture_half_float;
case ImageData::FORMAT_RGBA32F:
return GLAD_VERSION_3_0 || GLAD_ARB_texture_float || GLAD_ES_VERSION_3_0 || GLAD_OES_texture_float;
default:
return false;
}
}
bool Image::hasTextureFilteringSupport(image::ImageData::Format format)
{
switch (format)
{
case image::ImageData::FORMAT_RGBA16F:
return GLAD_VERSION_1_1 || GLAD_ES_VERSION_3_0 || GLAD_OES_texture_half_float_linear;
case image::ImageData::FORMAT_RGBA32F:
return GLAD_VERSION_1_1;
default:
return true;
}
}
bool Image::hasCompressedTextureSupport(image::CompressedImageData::Format format, bool sRGB)
{
using image::CompressedImageData;
+3
View File
@@ -128,6 +128,8 @@ public:
static FilterMode getDefaultMipmapFilter();
static bool hasAnisotropicFilteringSupport();
static bool hasTextureSupport(image::ImageData::Format format);
static bool hasTextureFilteringSupport(image::ImageData::Format format);
static bool hasCompressedTextureSupport(image::CompressedImageData::Format format, bool sRGB);
static bool hasSRGBSupport();
@@ -147,6 +149,7 @@ private:
void loadFromCompressedData();
void loadFromImageData();
GLenum getFormat(image::ImageData::Format format, GLenum &glformat, GLenum &gltype, bool &isSRGB) const;
GLenum getCompressedFormat(image::CompressedImageData::Format cformat, bool &isSRGB) const;
// The ImageData from which the texture is created. May be empty if
+21 -1
View File
@@ -1416,13 +1416,32 @@ int w_getCanvasFormats(lua_State *L)
return 1;
}
int w_getRawImageFormats(lua_State *L)
{
lua_createtable(L, 0, (int) image::ImageData::FORMAT_MAX_ENUM);
for (int i = 0; i < (int) image::ImageData::FORMAT_MAX_ENUM; i++)
{
auto format = (image::ImageData::Format) i;
const char *name = nullptr;
if (!image::ImageData::getConstant(format, name))
continue;
luax_pushboolean(L, Image::hasTextureSupport(format));
lua_setfield(L, -2, name);
}
return 1;
}
int w_getCompressedImageFormats(lua_State *L)
{
lua_createtable(L, 0, (int) image::CompressedImageData::FORMAT_MAX_ENUM);
for (int i = 0; i < (int) image::CompressedImageData::FORMAT_MAX_ENUM; i++)
{
image::CompressedImageData::Format format = (image::CompressedImageData::Format) i;
auto format = (image::CompressedImageData::Format) i;
const char *name = nullptr;
if (format == image::CompressedImageData::FORMAT_UNKNOWN)
@@ -1996,6 +2015,7 @@ static const luaL_Reg functions[] =
{ "getSupported", w_getSupported },
{ "getCanvasFormats", w_getCanvasFormats },
{ "getRawImageFormats", w_getRawImageFormats },
{ "getCompressedImageFormats", w_getCompressedImageFormats },
{ "getRendererInfo", w_getRendererInfo },
{ "getSystemLimits", w_getSystemLimits },
+2 -2
View File
@@ -62,7 +62,7 @@ public:
* @param height The height of the ImageData.
* @return The new ImageData.
**/
virtual ImageData *newImageData(int width, int height) = 0;
virtual ImageData *newImageData(int width, int height, ImageData::Format format = ImageData::FORMAT_RGBA8) = 0;
/**
* Creates empty ImageData with the given size.
@@ -73,7 +73,7 @@ public:
* copy it.
* @return The new ImageData.
**/
virtual ImageData *newImageData(int width, int height, void *data, bool own = false) = 0;
virtual ImageData *newImageData(int width, int height, ImageData::Format format, void *data, bool own = false) = 0;
/**
* Creates new CompressedImageData from FileData.
+66 -25
View File
@@ -38,7 +38,7 @@ ImageData::~ImageData()
size_t ImageData::getSize() const
{
return size_t(getWidth()*getHeight())*sizeof(pixel);
return size_t(getWidth() * getHeight()) * getPixelSize();
}
void *ImageData::getData() const
@@ -51,6 +51,11 @@ bool ImageData::inside(int x, int y) const
return x >= 0 && x < getWidth() && y >= 0 && y < getHeight();
}
ImageData::Format ImageData::getFormat() const
{
return format;
}
int ImageData::getWidth() const
{
return width;
@@ -61,47 +66,41 @@ int ImageData::getHeight() const
return height;
}
void ImageData::setPixel(int x, int y, pixel c)
void ImageData::setPixel(int x, int y, const Pixel &p)
{
if (!inside(x, y))
throw love::Exception("Attempt to set out-of-range pixel!");
size_t pixelsize = getPixelSize();
unsigned char *pixeldata = data + ((y * width + x) * pixelsize);
Lock lock(mutex);
pixel *pixels = (pixel *) getData();
pixels[y*width+x] = c;
memcpy(pixeldata, &p, pixelsize);
}
void ImageData::setPixelUnsafe(int x, int y, pixel c)
{
pixel *pixels = (pixel *) getData();
pixels[y*width+x] = c;
}
pixel ImageData::getPixel(int x, int y) const
void ImageData::getPixel(int x, int y, Pixel &p) const
{
if (!inside(x, y))
throw love::Exception("Attempt to get out-of-range pixel!");
size_t pixelsize = getPixelSize();
Lock lock(mutex);
const pixel *pixels = (const pixel *) getData();
return pixels[y*width+x];
}
pixel ImageData::getPixelUnsafe(int x, int y) const
{
const pixel *pixels = (const pixel *) getData();
return pixels[y*width+x];
memcpy(&p, data + ((y * width + x) * pixelsize), pixelsize);
}
void ImageData::paste(ImageData *src, int dx, int dy, int sx, int sy, int sw, int sh)
{
if (getFormat() != src->getFormat())
throw love::Exception("ImageData formats must match!");
Lock lock2(src->mutex);
Lock lock1(mutex);
pixel *s = (pixel *)src->getData();
pixel *d = (pixel *)getData();
uint8 *s = (uint8 *) src->getData();
uint8 *d = (uint8 *) getData();
size_t pixelsize = getPixelSize();
// Check bounds; if the data ends up completely out of bounds, get out early.
if (sx >= src->getWidth() || sx + sw < 0 || sy >= src->getHeight() || sy + sh < 0
@@ -150,13 +149,13 @@ void ImageData::paste(ImageData *src, int dx, int dy, int sx, int sy, int sw, in
if (sw == getWidth() && getWidth() == src->getWidth()
&& sh == getHeight() && getHeight() == src->getHeight())
{
memcpy(d, s, sizeof(pixel) * sw * sh);
memcpy(d, s, pixelsize * sw * sh);
}
else if (sw > 0)
{
// Otherwise, copy each row individually.
for (int i = 0; i < sh; i++)
memcpy(d + dx + (i + dy) * getWidth(), s + sx + (i + sy) * src->getWidth(), sizeof(pixel) * sw);
memcpy(d + dx + (i + dy) * getWidth(), s + (sx + (i + sy) * src->getWidth()) * pixelsize, pixelsize * sw);
}
}
@@ -165,6 +164,37 @@ love::thread::Mutex *ImageData::getMutex() const
return mutex;
}
size_t ImageData::getPixelSize() const
{
return getPixelSize(format);
}
size_t ImageData::getPixelSize(Format format)
{
switch (format)
{
case FORMAT_RGBA8:
return 4;
case FORMAT_RGBA16:
case FORMAT_RGBA16F:
return 8;
case FORMAT_RGBA32F:
return 16;
default:
return 0;
}
}
bool ImageData::getConstant(const char *in, Format &out)
{
return formats.find(in, out);
}
bool ImageData::getConstant(Format in, const char *&out)
{
return formats.find(in, out);
}
bool ImageData::getConstant(const char *in, EncodedFormat &out)
{
return encodedFormats.find(in, out);
@@ -175,6 +205,17 @@ bool ImageData::getConstant(EncodedFormat in, const char *&out)
return encodedFormats.find(in, out);
}
StringMap<ImageData::Format, ImageData::FORMAT_MAX_ENUM>::Entry ImageData::formatEntries[] =
{
{"rgba8", FORMAT_RGBA8 },
{"rgba16", FORMAT_RGBA16 },
{"rgba16f", FORMAT_RGBA16F},
{"rgba32f", FORMAT_RGBA32F},
};
StringMap<ImageData::Format, ImageData::FORMAT_MAX_ENUM> ImageData::formats(ImageData::formatEntries, sizeof(ImageData::formatEntries));
StringMap<ImageData::EncodedFormat, ImageData::ENCODED_MAX_ENUM>::Entry ImageData::encodedFormatEntries[] =
{
{"tga", ENCODED_TGA},
+35 -16
View File
@@ -24,6 +24,8 @@
// LOVE
#include "common/Data.h"
#include "common/StringMap.h"
#include "common/int.h"
#include "common/halffloat.h"
#include "filesystem/FileData.h"
#include "thread/threads.h"
@@ -41,6 +43,14 @@ struct pixel
unsigned char r, g, b, a;
};
union Pixel
{
uint8 rgba8[4];
uint16 rgba16[4];
half rgba16f[4];
float rgba32f[4];
};
/**
* Represents raw pixel data.
**/
@@ -48,6 +58,15 @@ class ImageData : public Data
{
public:
enum Format
{
FORMAT_RGBA8,
FORMAT_RGBA16,
FORMAT_RGBA16F,
FORMAT_RGBA32F,
FORMAT_MAX_ENUM
};
enum EncodedFormat
{
ENCODED_TGA,
@@ -58,6 +77,8 @@ public:
ImageData();
virtual ~ImageData();
Format getFormat() const;
/**
* Paste part of one ImageData onto another. The subregion defined by the top-left
* corner (sx, sy) and the size (sw,sh) will be pasted to (dx,dy) in this ImageData.
@@ -79,13 +100,11 @@ public:
/**
* Gets the width of this ImageData.
* @return The width of this ImageData.
**/
int getWidth() const;
/**
* Gets the height of this ImageData.
* @return The height of this ImageData.
**/
int getHeight() const;
@@ -95,13 +114,7 @@ public:
* @param y The location along the y-axis.
* @param p The color to use for the given location.
**/
void setPixel(int x, int y, pixel p);
/**
* Sets the pixel at location (x,y).
* Not thread-safe, and doesn't verify the coordinates!
**/
void setPixelUnsafe(int x, int y, pixel p);
void setPixel(int x, int y, const Pixel &p);
/**
* Gets the pixel at location (x,y).
@@ -109,13 +122,7 @@ public:
* @param y The location along the y-axis.
* @return The color for the given location.
**/
pixel getPixel(int x, int y) const;
/**
* Gets the pixel at location (x,y).
* Not thread-safe, and doesn't verify the coordinates!
**/
pixel getPixelUnsafe(int x, int y) const;
void getPixel(int x, int y, Pixel &p) const;
/**
* Encodes raw pixel data into a given format.
@@ -130,11 +137,20 @@ public:
virtual void *getData() const;
virtual size_t getSize() const;
size_t getPixelSize() const;
static size_t getPixelSize(Format format);
static bool getConstant(const char *in, Format &out);
static bool getConstant(Format in, const char *&out);
static bool getConstant(const char *in, EncodedFormat &out);
static bool getConstant(EncodedFormat in, const char *&out);
protected:
Format format;
// The width of the image data.
int width;
@@ -151,6 +167,9 @@ protected:
private:
static StringMap<Format, FORMAT_MAX_ENUM>::Entry formatEntries[];
static StringMap<Format, FORMAT_MAX_ENUM> formats;
static StringMap<EncodedFormat, ENCODED_MAX_ENUM>::Entry encodedFormatEntries[];
static StringMap<EncodedFormat, ENCODED_MAX_ENUM> encodedFormats;
+202
View File
@@ -0,0 +1,202 @@
/**
* Copyright (c) 2006-2016 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 "EXRHandler.h"
// tinyexr
#define TINYEXR_IMPLEMENTATION
#include "libraries/tinyexr/tinyexr.h"
// C
#include <cstdlib>
namespace love
{
namespace image
{
namespace magpie
{
bool EXRHandler::canDecode(love::filesystem::FileData *data)
{
const char *err;
EXRImage exrImage;
InitEXRImage(&exrImage);
if (ParseMultiChannelEXRHeaderFromMemory(&exrImage, (const unsigned char *) data->getData(), &err) != 0)
return false;
FreeEXRImage(&exrImage);
return exrImage.width > 0 && exrImage.height > 0;
}
bool EXRHandler::canEncode(ImageData::Format /*rawFormat*/, ImageData::EncodedFormat /*encodedFormat*/)
{
return false;
}
template <typename T>
static void getEXRChannels(const EXRImage &exrImage, T *rgba[4])
{
for (int i = 0; i < exrImage.num_channels; i++)
{
if (exrImage.channel_names[i] == nullptr)
continue;
switch (*exrImage.channel_names[i])
{
case 'R':
rgba[0] = (T *) exrImage.images[i];
break;
case 'G':
rgba[1] = (T *) exrImage.images[i];
break;
case 'B':
rgba[2] = (T *) exrImage.images[i];
break;
case 'A':
rgba[3] = (T *) exrImage.images[i];
break;
}
}
}
template <typename T>
static T *loadEXRChannels(int width, int height, T *rgba[4], T one)
{
T *data = nullptr;
try
{
data = new T[width * height * 4];
}
catch (std::exception &)
{
throw love::Exception("Out of memory.");
}
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
size_t offset = y * width + x;
data[offset * 4 + 0] = rgba[0] != nullptr ? rgba[0][offset] : 0;
data[offset * 4 + 1] = rgba[1] != nullptr ? rgba[1][offset] : 0;
data[offset * 4 + 2] = rgba[2] != nullptr ? rgba[2][offset] : 0;
data[offset * 4 + 3] = rgba[3] != nullptr ? rgba[3][offset] : one;
}
}
return data;
}
FormatHandler::DecodedImage EXRHandler::decode(love::filesystem::FileData *data)
{
const char *err;
auto mem = (const unsigned char *) data->getData();
DecodedImage img;
EXRImage exrImage;
InitEXRImage(&exrImage);
if (ParseMultiChannelEXRHeaderFromMemory(&exrImage, mem, &err) != 0)
throw love::Exception("Could not parse EXR image: %s", err);
if (LoadMultiChannelEXRFromMemory(&exrImage, mem, &err) != 0)
throw love::Exception("Could not decode EXR image: %s", err);
int pixelType = exrImage.pixel_types[0];
for (int i = 1; i < exrImage.num_channels; i++)
{
if (pixelType != exrImage.pixel_types[i])
{
FreeEXRImage(&exrImage);
throw love::Exception("Could not decode EXR image: all channels must have the same data type.");
}
}
img.width = exrImage.width;
img.height = exrImage.height;
if (pixelType == TINYEXR_PIXELTYPE_HALF)
{
img.format = ImageData::FORMAT_RGBA16F;
half *rgba[4] = {nullptr};
getEXRChannels(exrImage, rgba);
try
{
img.data = (unsigned char *) loadEXRChannels(img.width, img.height, rgba, floatToHalf(1.0f));
}
catch (love::Exception &)
{
FreeEXRImage(&exrImage);
throw;
}
}
else if (pixelType == TINYEXR_PIXELTYPE_FLOAT)
{
img.format = ImageData::FORMAT_RGBA32F;
float *rgba[4] = {nullptr};
getEXRChannels(exrImage, rgba);
try
{
img.data = (unsigned char *) loadEXRChannels(img.width, img.height, rgba, 1.0f);
}
catch (love::Exception &)
{
FreeEXRImage(&exrImage);
throw;
}
}
else
{
FreeEXRImage(&exrImage);
throw love::Exception("Could not decode EXR image: unknown pixel format.");
}
img.size = img.width * img.height * ImageData::getPixelSize(img.format);
FreeEXRImage(&exrImage);
return img;
}
FormatHandler::EncodedImage EXRHandler::encode(const DecodedImage & /*img*/, ImageData::EncodedFormat /*encodedFormat*/)
{
throw love::Exception("Invalid format.");
}
void EXRHandler::free(unsigned char *mem)
{
delete[] mem;
}
} // magpie
} // image
} // love
+56
View File
@@ -0,0 +1,56 @@
/**
* Copyright (c) 2006-2016 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#ifndef LOVE_IMAGE_MAGPIE_EXR_HANDLER_H
#define LOVE_IMAGE_MAGPIE_EXR_HANDLER_H
#include "FormatHandler.h"
namespace love
{
namespace image
{
namespace magpie
{
/**
* Interface between ImageData and TinyEXR library, for decoding exr files.
**/
class EXRHandler : public FormatHandler
{
public:
// Implements FormatHandler.
virtual bool canDecode(love::filesystem::FileData *data);
virtual bool canEncode(ImageData::Format rawFormat, ImageData::EncodedFormat encodedFormat);
virtual DecodedImage decode(love::filesystem::FileData *data);
virtual EncodedImage encode(const DecodedImage &img, ImageData::EncodedFormat format);
virtual void free(unsigned char *mem);
}; // EXRHandler
} // magpie
} // image
} // love
#endif // LOVE_IMAGE_MAGPIE_EXR_HANDLER_H
+1 -1
View File
@@ -42,7 +42,7 @@ bool FormatHandler::canDecode(love::filesystem::FileData* /*data*/)
return false;
}
bool FormatHandler::canEncode(ImageData::EncodedFormat /*format*/)
bool FormatHandler::canEncode(ImageData::Format /*rawFormat*/, ImageData::EncodedFormat /*encodedFormat*/)
{
return false;
}
+2 -1
View File
@@ -44,6 +44,7 @@ public:
// Raw RGBA pixel data.
struct DecodedImage
{
ImageData::Format format = ImageData::FORMAT_RGBA8;
int width = 0;
int height = 0;
size_t size = 0;
@@ -75,7 +76,7 @@ public:
/**
* Whether this format handler can encode to a particular format.
**/
virtual bool canEncode(ImageData::EncodedFormat format);
virtual bool canEncode(ImageData::Format rawFormat, ImageData::EncodedFormat encodedFormat);
/**
* Decodes an image from its encoded form into raw pixel data.
+8 -4
View File
@@ -27,6 +27,7 @@
#include "PNGHandler.h"
#include "STBHandler.h"
#include "EXRHandler.h"
#include "ddsHandler.h"
#include "PVRHandler.h"
@@ -43,9 +44,12 @@ namespace magpie
Image::Image()
{
halfInit(); // Makes sure half-float conversions can be used.
formatHandlers = {
new PNGHandler,
new STBHandler,
new EXRHandler,
};
compressedFormatHandlers = {
@@ -78,14 +82,14 @@ love::image::ImageData *Image::newImageData(love::filesystem::FileData *data)
return new ImageData(formatHandlers, data);
}
love::image::ImageData *Image::newImageData(int width, int height)
love::image::ImageData *Image::newImageData(int width, int height, ImageData::Format format)
{
return new ImageData(formatHandlers, width, height);
return new ImageData(formatHandlers, width, height, format);
}
love::image::ImageData *Image::newImageData(int width, int height, void *data, bool own)
love::image::ImageData *Image::newImageData(int width, int height, ImageData::Format format, void *data, bool own)
{
return new ImageData(formatHandlers, width, height, data, own);
return new ImageData(formatHandlers, width, height, format, data, own);
}
love::image::CompressedImageData *Image::newCompressedData(love::filesystem::FileData *data)
+2 -2
View File
@@ -52,8 +52,8 @@ public:
const char *getName() const;
love::image::ImageData *newImageData(love::filesystem::FileData *data);
love::image::ImageData *newImageData(int width, int height);
love::image::ImageData *newImageData(int width, int height, void *data, bool own = false);
love::image::ImageData *newImageData(int width, int height, ImageData::Format format = ImageData::FORMAT_RGBA8);
love::image::ImageData *newImageData(int width, int height, ImageData::Format format, void *data, bool own = false);
love::image::CompressedImageData *newCompressedData(love::filesystem::FileData *data);
+25 -20
View File
@@ -28,8 +28,8 @@ namespace image
namespace magpie
{
ImageData::ImageData(std::list<FormatHandler *> formats, love::filesystem::FileData *data)
: formatHandlers(formats)
ImageData::ImageData(std::list<FormatHandler *> formatHandlers, love::filesystem::FileData *data)
: formatHandlers(formatHandlers)
, decodeHandler(nullptr)
{
for (FormatHandler *handler : formatHandlers)
@@ -38,8 +38,8 @@ ImageData::ImageData(std::list<FormatHandler *> formats, love::filesystem::FileD
decode(data);
}
ImageData::ImageData(std::list<FormatHandler *> formats, int width, int height)
: formatHandlers(formats)
ImageData::ImageData(std::list<FormatHandler *> formatHandlers, int width, int height, Format format)
: formatHandlers(formatHandlers)
, decodeHandler(nullptr)
{
for (FormatHandler *handler : formatHandlers)
@@ -47,15 +47,16 @@ ImageData::ImageData(std::list<FormatHandler *> formats, int width, int height)
this->width = width;
this->height = height;
this->format = format;
create(width, height);
create(width, height, format);
// Set to black/transparency.
memset(data, 0, width*height*sizeof(pixel));
memset(data, 0, getSize());
}
ImageData::ImageData(std::list<FormatHandler *> formats, int width, int height, void *data, bool own)
: formatHandlers(formats)
ImageData::ImageData(std::list<FormatHandler *> formatHandlers, int width, int height, Format format, void *data, bool own)
: formatHandlers(formatHandlers)
, decodeHandler(nullptr)
{
for (FormatHandler *handler : formatHandlers)
@@ -63,11 +64,12 @@ ImageData::ImageData(std::list<FormatHandler *> formats, int width, int height,
this->width = width;
this->height = height;
this->format = format;
if (own)
this->data = (unsigned char *) data;
else
create(width, height, data);
create(width, height, format, data);
}
ImageData::~ImageData()
@@ -81,11 +83,13 @@ ImageData::~ImageData()
handler->release();
}
void ImageData::create(int width, int height, void *data)
void ImageData::create(int width, int height, Format format, void *data)
{
size_t datasize = width * height * getPixelSize(format);
try
{
this->data = new unsigned char[width*height*sizeof(pixel)];
this->data = new unsigned char[datasize];
}
catch(std::bad_alloc &)
{
@@ -93,9 +97,10 @@ void ImageData::create(int width, int height, void *data)
}
if (data)
memcpy(this->data, data, width*height*sizeof(pixel));
memcpy(this->data, data, datasize);
decodeHandler = nullptr;
this->format = format;
}
void ImageData::decode(love::filesystem::FileData *data)
@@ -121,8 +126,7 @@ void ImageData::decode(love::filesystem::FileData *data)
throw love::Exception("Could not decode file '%s' to ImageData: unsupported file format", name.c_str());
}
// The decoder *must* output a 32 bits-per-pixel image.
if (decodedimage.size != decodedimage.width*decodedimage.height*sizeof(pixel))
if (decodedimage.size != decodedimage.width * decodedimage.height * getPixelSize(decodedimage.format))
{
decoder->free(decodedimage.data);
throw love::Exception("Could not convert image!");
@@ -134,14 +138,15 @@ void ImageData::decode(love::filesystem::FileData *data)
else
delete[] this->data;
this->width = decodedimage.width;
this->width = decodedimage.width;
this->height = decodedimage.height;
this->data = decodedimage.data;
this->data = decodedimage.data;
this->format = decodedimage.format;
decodeHandler = decoder;
}
love::filesystem::FileData *ImageData::encode(EncodedFormat format, const char *filename)
love::filesystem::FileData *ImageData::encode(EncodedFormat encodedFormat, const char *filename)
{
FormatHandler *encoder = nullptr;
FormatHandler::EncodedImage encodedimage;
@@ -149,12 +154,12 @@ love::filesystem::FileData *ImageData::encode(EncodedFormat format, const char *
rawimage.width = width;
rawimage.height = height;
rawimage.size = width*height*sizeof(pixel);
rawimage.size = getSize();
rawimage.data = data;
for (FormatHandler *handler : formatHandlers)
{
if (handler->canEncode(format))
if (handler->canEncode(format, encodedFormat))
{
encoder = handler;
break;
@@ -164,7 +169,7 @@ love::filesystem::FileData *ImageData::encode(EncodedFormat format, const char *
if (encoder != nullptr)
{
thread::Lock lock(mutex);
encodedimage = encoder->encode(rawimage, format);
encodedimage = encoder->encode(rawimage, encodedFormat);
}
if (encoder == nullptr || encodedimage.data == nullptr)
+5 -5
View File
@@ -39,18 +39,18 @@ class ImageData : public love::image::ImageData
{
public:
ImageData(std::list<FormatHandler *> formats, love::filesystem::FileData *data);
ImageData(std::list<FormatHandler *> formats, int width, int height);
ImageData(std::list<FormatHandler *> formats, int width, int height, void *data, bool own);
ImageData(std::list<FormatHandler *> formatHandlers, love::filesystem::FileData *data);
ImageData(std::list<FormatHandler *> formatHandlers, int width, int height, Format format = FORMAT_RGBA8);
ImageData(std::list<FormatHandler *> formatHandlers, int width, int height, Format format, void *data, bool own);
virtual ~ImageData();
// Implements image::ImageData.
virtual love::filesystem::FileData *encode(EncodedFormat format, const char *filename);
virtual love::filesystem::FileData *encode(EncodedFormat encodedFormat, const char *filename);
private:
// Create imagedata. Initialize with data if not null.
void create(int width, int height, void *data = 0);
void create(int width, int height, Format format, void *data = nullptr);
// Decode and load an encoded format.
void decode(love::filesystem::FileData *data);
+34 -12
View File
@@ -140,9 +140,10 @@ bool PNGHandler::canDecode(love::filesystem::FileData *data)
return status == 0 && width > 0 && height > 0;
}
bool PNGHandler::canEncode(ImageData::EncodedFormat format)
bool PNGHandler::canEncode(ImageData::Format rawFormat, ImageData::EncodedFormat encodedFormat)
{
return format == ImageData::ENCODED_PNG;
return encodedFormat == ImageData::ENCODED_PNG
&& (rawFormat == ImageData::FORMAT_RGBA8 || rawFormat == ImageData::FORMAT_RGBA16);
}
PNGHandler::DecodedImage PNGHandler::decode(love::filesystem::FileData *fdata)
@@ -154,14 +155,23 @@ PNGHandler::DecodedImage PNGHandler::decode(love::filesystem::FileData *fdata)
DecodedImage img;
lodepng::State state;
unsigned status = lodepng_inspect(&width, &height, &state, indata, insize);
state.info_raw.colortype = LCT_RGBA;
state.info_raw.bitdepth = 8;
if (status != 0)
{
const char *err = lodepng_error_text(status);
throw love::Exception("Could not decode PNG image (%s)", err);
}
state.decoder.zlibsettings.custom_zlib = zlibDecompress;
state.info_raw.colortype = LCT_RGBA;
unsigned status = lodepng_decode(&img.data, &width, &height,
&state, indata, insize);
if (state.info_png.color.bitdepth == 16)
state.info_raw.bitdepth = 16;
else
state.info_raw.bitdepth = 8;
status = lodepng_decode(&img.data, &width, &height, &state, indata, insize);
if (status != 0)
{
@@ -171,14 +181,27 @@ PNGHandler::DecodedImage PNGHandler::decode(love::filesystem::FileData *fdata)
img.width = (int) width;
img.height = (int) height;
img.size = width * height * 4;
img.size = width * height * (state.info_raw.bitdepth * 4 / 8);
img.format = state.info_raw.bitdepth == 16 ? ImageData::FORMAT_RGBA16 : ImageData::FORMAT_RGBA8;
// LodePNG keeps raw 16 bit images stored as big-endian.
#ifndef LOVE_BIG_ENDIAN
if (state.info_raw.bitdepth == 16)
{
uint16 *pixeldata = (uint16 *) img.data;
uint16 numpixelcomponents = img.size / sizeof(uint16);
for (size_t i = 0; i < numpixelcomponents; i++)
pixeldata[i] = swapuint16(pixeldata[i]);
}
#endif
return img;
}
PNGHandler::EncodedImage PNGHandler::encode(const DecodedImage &img, ImageData::EncodedFormat format)
PNGHandler::EncodedImage PNGHandler::encode(const DecodedImage &img, ImageData::EncodedFormat encodedFormat)
{
if (format != ImageData::ENCODED_PNG)
if (!canEncode(img.format, encodedFormat))
throw love::Exception("PNG encoder cannot encode to non-PNG format.");
EncodedImage encimg;
@@ -186,11 +209,10 @@ PNGHandler::EncodedImage PNGHandler::encode(const DecodedImage &img, ImageData::
lodepng::State state;
state.info_raw.colortype = LCT_RGBA;
state.info_raw.bitdepth = 8;
state.info_raw.bitdepth = img.format == ImageData::FORMAT_RGBA16 ? 16 : 8;
// TODO: support plain RGB (24-bit) encoding in the future?
state.info_png.color.colortype = LCT_RGBA;
state.info_png.color.bitdepth = 8;
state.info_png.color.bitdepth = state.info_raw.bitdepth;
state.encoder.zlibsettings.custom_zlib = zlibCompress;
+1 -1
View File
@@ -41,7 +41,7 @@ public:
// Implements FormatHandler.
virtual bool canDecode(love::filesystem::FileData *data);
virtual bool canEncode(ImageData::EncodedFormat format);
virtual bool canEncode(ImageData::Format rawFormat, ImageData::EncodedFormat encodedFormat);
virtual DecodedImage decode(love::filesystem::FileData *data);
virtual EncodedImage encode(const DecodedImage &img, ImageData::EncodedFormat format);
+27 -17
View File
@@ -32,6 +32,7 @@ static void loveSTBIAssert(bool test, const char *teststr)
// #define STBI_ONLY_PNG
#define STBI_ONLY_BMP
#define STBI_ONLY_TGA
#define STBI_ONLY_HDR
#define STBI_NO_STDIO
#define STB_IMAGE_IMPLEMENTATION
#define STBI_ASSERT(A) loveSTBIAssert((A), #A)
@@ -59,20 +60,31 @@ bool STBHandler::canDecode(love::filesystem::FileData *data)
return status == 1 && w > 0 && h > 0;
}
bool STBHandler::canEncode(ImageData::EncodedFormat format)
bool STBHandler::canEncode(ImageData::Format rawFormat, ImageData::EncodedFormat encodedFormat)
{
return format == ImageData::ENCODED_TGA;
return encodedFormat == ImageData::ENCODED_TGA && rawFormat == ImageData::FORMAT_RGBA8;
}
FormatHandler::DecodedImage STBHandler::decode(love::filesystem::FileData *data)
{
DecodedImage img;
const stbi_uc *buffer = (const stbi_uc *) data->getData();
int bufferlen = (int) data->getSize();
int comp = 0;
img.data = stbi_load_from_memory((const stbi_uc *) data->getData(),
(int) data->getSize(),
&img.width, &img.height,
&comp, 4);
if (stbi_is_hdr_from_memory(buffer, bufferlen))
{
img.data = (unsigned char *) stbi_loadf_from_memory(buffer, bufferlen, &img.width, &img.height, &comp, 4);
img.size = img.width * img.height * 4 * sizeof(float);
img.format = ImageData::FORMAT_RGBA32F;
}
else
{
img.data = stbi_load_from_memory(buffer, bufferlen, &img.width, &img.height, &comp, 4);
img.size = img.width * img.height * 4;
img.format = ImageData::FORMAT_RGBA8;
}
if (img.data == nullptr || img.width <= 0 || img.height <= 0)
{
@@ -82,14 +94,12 @@ FormatHandler::DecodedImage STBHandler::decode(love::filesystem::FileData *data)
throw love::Exception("Could not decode image with stb_image (%s).", err);
}
img.size = img.width * img.height * 4;
return img;
}
FormatHandler::EncodedImage STBHandler::encode(const DecodedImage &img, ImageData::EncodedFormat format)
FormatHandler::EncodedImage STBHandler::encode(const DecodedImage &img, ImageData::EncodedFormat encodedFormat)
{
if (!canEncode(format))
if (!canEncode(img.format, encodedFormat))
throw love::Exception("Invalid format.");
// We don't actually use stb_image for encoding, but this code is small
@@ -113,13 +123,13 @@ FormatHandler::EncodedImage STBHandler::encode(const DecodedImage &img, ImageDat
throw love::Exception("Out of memory.");
// here's the header for the Targa file format.
encimg.data[0] = 0; // ID field size
encimg.data[1] = 0; // colormap type
encimg.data[2] = 2; // image type
encimg.data[3] = encimg.data[4] = 0; // colormap start
encimg.data[5] = encimg.data[6] = 0; // colormap length
encimg.data[7] = 32; // colormap bits
encimg.data[8] = encimg.data[9] = 0; // x origin
encimg.data[0] = 0; // ID field size
encimg.data[1] = 0; // colormap type
encimg.data[2] = 2; // image type
encimg.data[3] = encimg.data[4] = 0; // colormap start
encimg.data[5] = encimg.data[6] = 0; // colormap length
encimg.data[7] = 32; // colormap bits
encimg.data[8] = encimg.data[9] = 0; // x origin
encimg.data[10] = encimg.data[11] = 0; // y origin
// Targa is little endian, so:
encimg.data[12] = img.width & 255; // least significant byte of width
+1 -1
View File
@@ -44,7 +44,7 @@ public:
// Implements FormatHandler.
virtual bool canDecode(love::filesystem::FileData *data);
virtual bool canEncode(ImageData::EncodedFormat format);
virtual bool canEncode(ImageData::Format rawFormat, ImageData::EncodedFormat encodedFormat);
virtual DecodedImage decode(love::filesystem::FileData *data);
virtual EncodedImage encode(const DecodedImage &img, ImageData::EncodedFormat format);
+13 -4
View File
@@ -36,7 +36,7 @@ namespace image
int w_newImageData(lua_State *L)
{
// Case 1: Integers.
// Case 1: width & height.
if (lua_isnumber(L, 1))
{
int w = (int) luaL_checknumber(L, 1);
@@ -44,14 +44,23 @@ int w_newImageData(lua_State *L)
if (w <= 0 || h <= 0)
return luaL_error(L, "Invalid image size.");
ImageData::Format format = ImageData::FORMAT_RGBA8;
if (!lua_isnoneornil(L, 3))
{
const char *fstr = luaL_checkstring(L, 3);
if (!ImageData::getConstant(fstr, format))
return luaL_error(L, "Invalid ImageData format: %s", fstr);
}
size_t numbytes = 0;
const char *bytes = nullptr;
if (!lua_isnoneornil(L, 3))
bytes = luaL_checklstring(L, 3, &numbytes);
if (!lua_isnoneornil(L, 4))
bytes = luaL_checklstring(L, 4, &numbytes);
ImageData *t = nullptr;
luax_catchexcept(L, [&](){ t = instance()->newImageData(w, h); });
luax_catchexcept(L, [&](){ t = instance()->newImageData(w, h, format); });
if (bytes)
{
+132 -79
View File
@@ -43,6 +43,19 @@ ImageData *luax_checkimagedata(lua_State *L, int idx)
return luax_checktype<ImageData>(L, idx, IMAGE_IMAGE_DATA_ID);
}
int w_ImageData_getFormat(lua_State *L)
{
ImageData *t = luax_checkimagedata(L, 1);
ImageData::Format format = t->getFormat();
const char *fstr = nullptr;
if (!ImageData::getConstant(format, fstr))
return luaL_error(L, "Unknown ImageData format.");
lua_pushstring(L, fstr);
return 1;
}
int w_ImageData_getWidth(lua_State *L)
{
ImageData *t = luax_checkimagedata(L, 1);
@@ -65,20 +78,99 @@ int w_ImageData_getDimensions(lua_State *L)
return 2;
}
// TODO: rgba16f
static void luax_checkpixel_rgba8(lua_State *L, int startidx, Pixel &p)
{
for (int i = 0; i < 3; i++)
p.rgba8[i] = (uint8) (luaL_checknumber(L, startidx + i) * 255.0);
p.rgba8[3] = (uint8) (luaL_optnumber(L, startidx + 3, 1.0) * 255.0);
}
static void luax_checkpixel_rgba16(lua_State *L, int startidx, Pixel &p)
{
for (int i = 0; i < 3; i++)
p.rgba16[i] = (uint16) (luaL_checknumber(L, startidx + i) * 65535.0);
p.rgba16[3] = (uint16) (luaL_optnumber(L, startidx + 3, 1.0) * 65535.0);
}
static void luax_checkpixel_rgba16f(lua_State *L, int startidx, Pixel &p)
{
for (int i = 0; i < 3; i++)
p.rgba16f[i] = floatToHalf((float) luaL_checknumber(L, startidx + i));
p.rgba16f[3] = floatToHalf((float) luaL_optnumber(L, startidx + 3, 1.0));
}
static void luax_checkpixel_rgba32f(lua_State *L, int startidx, Pixel &p)
{
for (int i = 0; i < 3; i++)
p.rgba32f[i] = (float) luaL_checknumber(L, startidx + i);
p.rgba32f[3] = (float) luaL_optnumber(L, startidx + 3, 1.0);
}
static int luax_pushpixel_rgba8(lua_State *L, const Pixel &p)
{
for (int i = 0; i < 4; i++)
lua_pushnumber(L, (lua_Number) p.rgba8[i] / 255.0);
return 4;
}
static int luax_pushpixel_rgba16(lua_State *L, const Pixel &p)
{
for (int i = 0; i < 4; i++)
lua_pushnumber(L, (lua_Number) p.rgba16[i] / 65535.0);
return 4;
}
static int luax_pushpixel_rgba16f(lua_State *L, const Pixel &p)
{
for (int i = 0; i < 4; i++)
lua_pushnumber(L, (lua_Number) halfToFloat(p.rgba16f[i]));
return 4;
}
static int luax_pushpixel_rgba32f(lua_State *L, const Pixel &p)
{
for (int i = 0; i < 4; i++)
lua_pushnumber(L, (lua_Number) p.rgba32f[i]);
return 4;
}
typedef void(*checkpixel)(lua_State *L, int startidx, Pixel &p);
typedef int(*pushpixel)(lua_State *L, const Pixel &p);
static checkpixel checkFormats[ImageData::FORMAT_MAX_ENUM] =
{
luax_checkpixel_rgba8,
luax_checkpixel_rgba16,
luax_checkpixel_rgba16f,
luax_checkpixel_rgba32f,
};
static pushpixel pushFormats[ImageData::FORMAT_MAX_ENUM] =
{
luax_pushpixel_rgba8,
luax_pushpixel_rgba16,
luax_pushpixel_rgba16f,
luax_pushpixel_rgba32f,
};
int w_ImageData_getPixel(lua_State *L)
{
ImageData *t = luax_checkimagedata(L, 1);
int x = (int) luaL_checknumber(L, 2);
int y = (int) luaL_checknumber(L, 3);
pixel c;
luax_catchexcept(L, [&](){ c = t->getPixel(x, y); });
ImageData::Format format = t->getFormat();
lua_pushnumber(L, (lua_Number) c.r / 255.0);
lua_pushnumber(L, (lua_Number) c.g / 255.0);
lua_pushnumber(L, (lua_Number) c.b / 255.0);
lua_pushnumber(L, (lua_Number) c.a / 255.0);
return 4;
Pixel p;
luax_catchexcept(L, [&](){ t->getPixel(x, y, p); });
return pushFormats[format](L, p);
}
int w_ImageData_setPixel(lua_State *L)
@@ -86,60 +178,27 @@ int w_ImageData_setPixel(lua_State *L)
ImageData *t = luax_checkimagedata(L, 1);
int x = (int) luaL_checknumber(L, 2);
int y = (int) luaL_checknumber(L, 3);
pixel c;
ImageData::Format format = t->getFormat();
Pixel p;
if (lua_istable(L, 4))
{
for (int i = 1; i <= 4; i++)
lua_rawgeti(L, 4, i);
c.r = (unsigned char) (luaL_checknumber(L, -4) * 255.0);
c.g = (unsigned char) (luaL_checknumber(L, -3) * 255.0);
c.b = (unsigned char) (luaL_checknumber(L, -2) * 255.0);
c.a = (unsigned char) (luaL_optnumber(L, -1, 1.0) * 255.0);
checkFormats[format](L, -4, p);
lua_pop(L, 4);
}
else
{
c.r = (unsigned char) (luaL_checknumber(L, 4) * 255.0);
c.g = (unsigned char) (luaL_checknumber(L, 5) * 255.0);
c.b = (unsigned char) (luaL_checknumber(L, 6) * 255.0);
c.a = (unsigned char) (luaL_optnumber(L, 7, 1.0) * 255.0);
}
checkFormats[format](L, 4, p);
luax_catchexcept(L, [&](){ t->setPixel(x, y, c); });
luax_catchexcept(L, [&](){ t->setPixel(x, y, p); });
return 0;
}
// Gets the result of luaL_where as a string.
static std::string luax_getwhere(lua_State *L, int level)
{
luaL_where(L, level);
const char *str = lua_tostring(L, -1);
std::string where;
if (str)
where = str;
lua_pop(L, 1);
return where;
}
// Generates a Lua error with a nice error string when a return value of a
// called function is not a number.
static int luax_retnumbererror(lua_State *L, int level, int retnum, int ttype)
{
if (ttype == LUA_TNUMBER)
return 0;
const char *where = luax_getwhere(L, level).c_str();
const char *ttypename = lua_typename(L, ttype);
return luaL_error(L, "%sbad return value #%d (number expected, got %s)",
where, retnum, ttypename);
}
// ImageData:mapPixel. Not thread-safe! See wrap_ImageData.lua for the thread-
// safe wrapper function.
int w_ImageData__mapPixelUnsafe(lua_State *L)
@@ -156,45 +215,32 @@ int w_ImageData__mapPixelUnsafe(lua_State *L)
if (!(t->inside(sx, sy) && t->inside(sx+w-1, sy+h-1)))
return luaL_error(L, "Invalid rectangle dimensions.");
// Cache-friendlier loop. :)
int iw = t->getWidth();
ImageData::Format format = t->getFormat();
auto checkpixel = checkFormats[format];
auto pushpixel = pushFormats[format];
uint8 *data = (uint8 *) t->getData();
size_t pixelsize = t->getPixelSize();
for (int y = sy; y < sy+h; y++)
{
for (int x = sx; x < sx+w; x++)
{
lua_pushvalue(L, 2);
Pixel *pixeldata = (Pixel *) (data + (y * iw + x) * pixelsize);
lua_pushvalue(L, 2); // ImageData
lua_pushnumber(L, x);
lua_pushnumber(L, y);
pixel c = t->getPixelUnsafe(x, y);
lua_pushnumber(L, c.r / 255.0);
lua_pushnumber(L, c.g / 255.0);
lua_pushnumber(L, c.b / 255.0);
lua_pushnumber(L, c.a / 255.0);
pushpixel(L, *pixeldata);
lua_call(L, 6, 4);
// If we used luaL_checkX / luaL_optX then we would get messy error
// messages (e.g. Error: bad argument #-1 to '?'), so while this is
// messier code, at least the errors are a bit more descriptive.
// Treat the pixel as an array for less code duplication. :(
unsigned char *parray = (unsigned char *) &c;
for (int i = 0; i < 4; i++)
{
int ttype = lua_type(L, -4 + i);
if (ttype == LUA_TNUMBER)
parray[i] = (unsigned char) (lua_tonumber(L, -4 + i) * 255.0);
else if (i == 3 && (ttype == LUA_TNONE || ttype == LUA_TNIL))
parray[i] = 255; // Alpha component defaults to 255.
else
// Error (level 2 because this is function will be wrapped.)
return luax_retnumbererror(L, 2, i + 1, ttype);
}
// Pop return values.
lua_pop(L, 4);
// We're locking the entire function, instead of each setPixel call.
t->setPixelUnsafe(x, y, c);
checkpixel(L, -4, *pixeldata);
lua_pop(L, 4); // Pop return values.
}
}
@@ -275,6 +321,9 @@ struct FFI_ImageData
{
void (*lockMutex)(Proxy *p);
void (*unlockMutex)(Proxy *p);
float (*halfToFloat)(half h);
half (*floatToHalf)(float f);
};
static FFI_ImageData ffifuncs =
@@ -291,11 +340,15 @@ static FFI_ImageData ffifuncs =
{
ImageData *i = (ImageData *) p->object;
i->getMutex()->unlock();
}
},
halfToFloat,
floatToHalf,
};
static const luaL_Reg w_ImageData_functions[] =
{
{ "getFormat", w_ImageData_getFormat },
{ "getWidth", w_ImageData_getWidth },
{ "getHeight", w_ImageData_getHeight },
{ "getDimensions", w_ImageData_getDimensions },
+94 -21
View File
@@ -69,26 +69,98 @@ if not status then return end
pcall(ffi.cdef, [[
typedef struct Proxy Proxy;
typedef uint16_t half;
typedef struct FFI_ImageData
{
void (*lockMutex)(Proxy *p);
void (*unlockMutex)(Proxy *p);
float (*halfToFloat)(half h);
half (*floatToHalf)(float f);
} FFI_ImageData;
typedef struct ImageData_Pixel
typedef struct ImageData_Pixel_RGBA8
{
uint8_t r, g, b, a;
} ImageData_Pixel;
} ImageData_Pixel_RGBA8;
typedef struct ImageData_Pixel_RGBA16
{
uint16_t r, g, b, a;
} ImageData_Pixel_RGBA16;
typedef struct ImageData_Pixel_RGBA16F
{
half r, g, b, a;
} ImageData_Pixel_RGBA16F;
typedef struct ImageData_Pixel_RGBA32F
{
float r, g, b, a;
} ImageData_Pixel_RGBA32F;
]])
local ffifuncs = ffi.cast("FFI_ImageData *", ffifuncspointer)
local pixelpointer = ffi.typeof("ImageData_Pixel *")
local conversions = {
rgba8 = {
pointer = ffi.typeof("ImageData_Pixel_RGBA8 *"),
tolua = function(self)
return tonumber(self.r) / 255, tonumber(self.g) / 255, tonumber(self.b) / 255, tonumber(self.a) / 255
end,
fromlua = function(self, r, g, b, a)
self.r = r * 255
self.g = g * 255
self.b = b * 255
self.a = a == nil and 255 or a * 255
end,
},
rgba16 = {
pointer = ffi.typeof("ImageData_Pixel_RGBA16 *"),
tolua = function(self)
return tonumber(self.r) / 65535, tonumber(self.g) / 65535, tonumber(self.b) / 65535, tonumber(self.a) / 65535
end,
fromlua = function(self, r, g, b, a)
self.r = r * 65535
self.g = g * 65535
self.b = b * 65535
self.a = a == nil and 65535 or a * 65535
end,
},
rgba16f = {
pointer = ffi.typeof("ImageData_Pixel_RGBA16F *"),
tolua = function(self)
return tonumber(ffifuncs.halfToFloat(self.r)),
tonumber(ffifuncs.halfToFloat(self.g)),
tonumber(ffifuncs.halfToFloat(self.b)),
tonumber(ffifuncs.halfToFloat(self.a))
end,
fromlua = function(self, r, g, b, a)
self.r = ffifuncs.floatToHalf(r)
self.g = ffifuncs.floatToHalf(g)
self.b = ffifuncs.floatToHalf(b)
self.a = ffifuncs.floatToHalf(a == nil and 1.0 or a)
end,
},
rgba32f = {
pointer = ffi.typeof("ImageData_Pixel_RGBA32F *"),
tolua = function(self)
return tonumber(self.r), tonumber(self.g), tonumber(self.b), tonumber(self.a)
end,
fromlua = function(self, r, g, b, a)
self.r = r
self.g = g
self.b = b
self.a = a == nil and 1.0 or a
end,
},
}
local _getWidth = ImageData.getWidth
local _getHeight = ImageData.getHeight
local _getDimensions = ImageData.getDimensions
local _getFormat = ImageData.getFormat
-- Table which holds ImageData objects as keys, and information about the objects
-- as values. Uses weak keys so the ImageData objects can still be GC'd properly.
@@ -96,12 +168,17 @@ local objectcache = setmetatable({}, {
__mode = "k",
__index = function(self, imagedata)
local width, height = _getDimensions(imagedata)
local pointer = ffi.cast(pixelpointer, imagedata:getPointer())
local format = _getFormat(imagedata)
local conv = conversions[format]
local p = {
width = width,
height = height,
pointer = pointer,
format = format,
pointer = ffi.cast(conv.pointer, imagedata:getPointer()),
tolua = conv.tolua,
fromlua = conv.fromlua,
}
self[imagedata] = p
@@ -127,15 +204,14 @@ function ImageData:_mapPixelUnsafe(func, ix, iy, iw, ih)
local idw, idh = p.width, p.height
local pixels = p.pointer
local tolua = p.tolua
local fromlua = p.fromlua
for y=iy, iy+ih-1 do
for x=ix, ix+iw-1 do
local p = pixels[y*idw+x]
local r, g, b, a = func(x, y, tonumber(p.r) / 255, tonumber(p.g) / 255, tonumber(p.b) / 255, tonumber(p.a) / 255)
pixels[y*idw+x].r = r * 255
pixels[y*idw+x].g = g * 255
pixels[y*idw+x].b = b * 255
pixels[y*idw+x].a = a == nil and 255 or (a*255)
local pixel = pixels[y*idw+x]
local r, g, b, a = func(x, y, tolua(pixel))
fromlua(pixel, r, g, b, a)
end
end
end
@@ -149,14 +225,12 @@ function ImageData:getPixel(x, y)
ffifuncs.lockMutex(self)
local pixel = p.pointer[y * p.width + x]
local r, g, b, a = tonumber(pixel.r), tonumber(pixel.g), tonumber(pixel.b), tonumber(pixel.a)
local r, g, b, a = p.tolua(pixel)
ffifuncs.unlockMutex(self)
return r / 255, g / 255, b / 255, a / 255
return r, g, b, a
end
local temppixel = ffi.new("ImageData_Pixel")
function ImageData:setPixel(x, y, r, g, b, a)
if type(x) ~= "number" then error("bad argument #1 to ImageData:setPixel (expected number)", 2) end
if type(y) ~= "number" then error("bad argument #2 to ImageData:setPixel (expected number)", 2) end
@@ -174,13 +248,8 @@ function ImageData:setPixel(x, y, r, g, b, a)
local p = objectcache[self]
if not inside(x, y, p.width, p.height) then error("Attempt to set out-of-range pixel!", 2) end
temppixel.r = r * 255
temppixel.g = g * 255
temppixel.b = b * 255
temppixel.a = a == nil and 255 or a * 255
ffifuncs.lockMutex(self)
p.pointer[y * p.width + x] = temppixel
p.fromlua(p.pointer[y * p.width + x], r, g, b, a)
ffifuncs.unlockMutex(self)
end
@@ -197,5 +266,9 @@ function ImageData:getDimensions()
return p.width, p.height
end
function ImageData:getFormat()
return objectcache[self].format
end
-- DO NOT REMOVE THE NEXT LINE. It is used to load this file as a C++ string.
--)luastring"--"