mirror of
https://github.com/love2d/love.git
synced 2026-08-14 01:20:56 +02:00
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:
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 &)
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 },
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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},
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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 },
|
||||
|
||||
@@ -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"--"
|
||||
|
||||
Reference in New Issue
Block a user