Browse Source

ImageData and Images now support the rg11b10f format.

Alex Szpakowski 5 years ago
parent
commit
c4fac73dc4

+ 2 - 2
CMakeLists.txt

@@ -279,8 +279,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/floattypes.cpp
+	src/common/floattypes.h
 	src/common/int.h
 	src/common/math.h
 	src/common/Matrix.cpp

+ 10 - 10
platform/xcode/liblove.xcodeproj/project.pbxproj

@@ -64,6 +64,9 @@
 		217DFC101D9F6D490055D849 /* url.lua.h in Headers */ = {isa = PBXBuildFile; fileRef = 217DFBD41D9F6D490055D849 /* url.lua.h */; };
 		217DFC111D9F6D490055D849 /* usocket.c in Sources */ = {isa = PBXBuildFile; fileRef = 217DFBD51D9F6D490055D849 /* usocket.c */; };
 		217DFC121D9F6D490055D849 /* usocket.h in Headers */ = {isa = PBXBuildFile; fileRef = 217DFBD61D9F6D490055D849 /* usocket.h */; };
+		FA0A3A5F23366CE9001C269E /* floattypes.h in Headers */ = {isa = PBXBuildFile; fileRef = FA0A3A5D23366CE9001C269E /* floattypes.h */; };
+		FA0A3A6023366CE9001C269E /* floattypes.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA0A3A5E23366CE9001C269E /* floattypes.cpp */; };
+		FA0A3A6123366CE9001C269E /* floattypes.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA0A3A5E23366CE9001C269E /* floattypes.cpp */; };
 		FA0B791B1A958E3B000E1D17 /* b64.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA0B78F71A958E3B000E1D17 /* b64.cpp */; };
 		FA0B791C1A958E3B000E1D17 /* b64.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA0B78F71A958E3B000E1D17 /* b64.cpp */; };
 		FA0B791D1A958E3B000E1D17 /* b64.h in Headers */ = {isa = PBXBuildFile; fileRef = FA0B78F81A958E3B000E1D17 /* b64.h */; };
@@ -937,9 +940,6 @@
 		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 */; };
 		FA91DA8B1F377C3900C80E33 /* deprecation.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA91DA891F377C3900C80E33 /* deprecation.cpp */; };
 		FA91DA8C1F377C3900C80E33 /* deprecation.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA91DA891F377C3900C80E33 /* deprecation.cpp */; };
 		FA91DA8D1F377C3900C80E33 /* deprecation.h in Headers */ = {isa = PBXBuildFile; fileRef = FA91DA8A1F377C3900C80E33 /* deprecation.h */; };
@@ -1281,6 +1281,8 @@
 		217DFBD61D9F6D490055D849 /* usocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = usocket.h; sourceTree = "<group>"; };
 		503971A86B7167A91B670FBA /* boot.lua.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = boot.lua.h; sourceTree = "<group>"; };
 		FA08F5AE16C7525600F007B5 /* liblove-macosx.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = "liblove-macosx.plist"; path = "macosx/liblove-macosx.plist"; sourceTree = "<group>"; };
+		FA0A3A5D23366CE9001C269E /* floattypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = floattypes.h; sourceTree = "<group>"; };
+		FA0A3A5E23366CE9001C269E /* floattypes.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = floattypes.cpp; sourceTree = "<group>"; };
 		FA0B78DD1A958B90000E1D17 /* liblove.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = liblove.a; sourceTree = BUILT_PRODUCTS_DIR; };
 		FA0B78F71A958E3B000E1D17 /* b64.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; lineEnding = 0; path = b64.cpp; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.cpp; };
 		FA0B78F81A958E3B000E1D17 /* b64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = b64.h; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; };
@@ -1876,8 +1878,6 @@
 		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>"; };
 		FA91DA891F377C3900C80E33 /* deprecation.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = deprecation.cpp; sourceTree = "<group>"; };
 		FA91DA8A1F377C3900C80E33 /* deprecation.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = deprecation.h; sourceTree = "<group>"; };
 		FA93C4501F315B960087CCD4 /* FormatHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FormatHandler.h; sourceTree = "<group>"; };
@@ -2164,8 +2164,8 @@
 				FA0B78FD1A958E3B000E1D17 /* EnumMap.h */,
 				FA0B78FE1A958E3B000E1D17 /* Exception.cpp */,
 				FA0B78FF1A958E3B000E1D17 /* Exception.h */,
-				FA91591C1CF1ED7500A7053F /* halffloat.cpp */,
-				FA91591D1CF1ED7500A7053F /* halffloat.h */,
+				FA0A3A5E23366CE9001C269E /* floattypes.cpp */,
+				FA0A3A5D23366CE9001C269E /* floattypes.h */,
 				FA0B79001A958E3B000E1D17 /* int.h */,
 				FA0B7EF01A959D2C000E1D17 /* ios.h */,
 				FA0B7EF11A959D2C000E1D17 /* ios.mm */,
@@ -3795,7 +3795,6 @@
 				217DFBEE1D9F6D490055D849 /* luasocket.h in Headers */,
 				FACA02F31F5E396B0084B28F /* HashFunction.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 */,
 				FADF540F1E3D7CDD00012CC0 /* wrap_Video.h in Headers */,
@@ -3926,6 +3925,7 @@
 				FA0B7CD81A95902C000E1D17 /* Audio.h in Headers */,
 				FA0B7CF61A95902C000E1D17 /* File.h in Headers */,
 				FA0B7E961A95902C000E1D17 /* Mpg123Decoder.h in Headers */,
+				FA0A3A5F23366CE9001C269E /* floattypes.h in Headers */,
 				FADF54091E3D78F700012CC0 /* Video.h in Headers */,
 				FAA54ACB1F91660400A8FA7B /* TheoraVideoStream.h in Headers */,
 				FA0B7DD51A95902C000E1D17 /* BezierCurve.h in Headers */,
@@ -4271,7 +4271,6 @@
 				FA0B7EA41A95902C000E1D17 /* SoundData.cpp in Sources */,
 				FA0B7D041A95902C000E1D17 /* wrap_DroppedFile.cpp in Sources */,
 				FAF1406A1E20934C00F898D2 /* glslang_tab.cpp in Sources */,
-				FA91591F1CF1ED7500A7053F /* halffloat.cpp in Sources */,
 				FA8951A31AA2EDF300EC385A /* wrap_Event.cpp in Sources */,
 				FADF540E1E3D7CDD00012CC0 /* wrap_Video.cpp in Sources */,
 				FA0B7A361A958EA3000E1D17 /* b2Distance.cpp in Sources */,
@@ -4404,6 +4403,7 @@
 				FA0B7E791A95902C000E1D17 /* wrap_WheelJoint.cpp in Sources */,
 				FA0B7DDD1A95902C000E1D17 /* wrap_BezierCurve.cpp in Sources */,
 				FA0B7A2A1A958EA3000E1D17 /* b2BroadPhase.cpp in Sources */,
+				FA0A3A6123366CE9001C269E /* floattypes.cpp in Sources */,
 				FA0B7A811A958EA3000E1D17 /* b2EdgeAndCircleContact.cpp in Sources */,
 				FAF188A01E9DBC4B008C1479 /* depthstencil.cpp in Sources */,
 				FA0B7D071A95902C000E1D17 /* wrap_File.cpp in Sources */,
@@ -4794,6 +4794,7 @@
 				FAF140711E20934C00F898D2 /* Intermediate.cpp in Sources */,
 				FAC7CD7D1FE35E95006A60C7 /* physfs_platform_winrt.cpp in Sources */,
 				FA0B7E541A95902C000E1D17 /* wrap_GearJoint.cpp in Sources */,
+				FA0A3A6023366CE9001C269E /* floattypes.cpp in Sources */,
 				FA41A3C81C0A1F950084430C /* ASTCHandler.cpp in Sources */,
 				FA0B7E781A95902C000E1D17 /* wrap_WheelJoint.cpp in Sources */,
 				FA0B7DDC1A95902C000E1D17 /* wrap_BezierCurve.cpp in Sources */,
@@ -4907,7 +4908,6 @@
 				FAC756F51E4F99B400B91289 /* Effect.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 */,
 				FA0B7D421A95902C000E1D17 /* OpenGL.cpp in Sources */,

+ 112 - 14
src/common/halffloat.cpp → src/common/floattypes.cpp

@@ -18,14 +18,17 @@
  * 3. This notice may not be removed or altered from any source distribution.
  **/
 
-#include "halffloat.h"
+#include "floattypes.h"
+
+#include <limits>
+#include <cmath>
 
 namespace love
 {
 
 // Code from ftp://www.fox-toolkit.org/pub/fasthalffloatconversion.pdf
 
-static bool initialized = false;
+static bool halfInitialized = false;
 
 // tables for half -> float conversions
 static uint32 mantissatable[2048];
@@ -54,15 +57,15 @@ static uint32 convertMantissa(uint32 i)
 	return m | e; // Return combined number
 }
 
-void halfInit()
+void float16Init()
 {
-	if (initialized)
+	if (halfInitialized)
 		return;
 
-	initialized = true;
+	halfInitialized = true;
 
 
-	// tables for half -> float conversions.
+	// tables for float16 -> float32 conversions.
 
 	mantissatable[0] = 0;
 
@@ -93,7 +96,7 @@ void halfInit()
 	}
 
 
-	// tables for float -> half conversions.
+	// tables for float32 -> float16 conversions.
 
 	for (uint32 i = 0; i < 256; i++)
 	{
@@ -137,20 +140,115 @@ void halfInit()
 	}
 }
 
-float halfToFloat(half h)
+static inline uint32 asuint32(float f)
 {
-	union { float f; uint32 i; } conv;
+	union { float f; uint32 u; } conv;
+	conv.f = f;
+	return conv.u;
+}
 
-	conv.i = mantissatable[offsettable[h >> 10] + (h & 0x3FF)] + exponenttable[h >> 10];
+static inline float asfloat32(uint32 u)
+{
+	union { float f; uint32 u; } conv;
+	conv.u = u;
 	return conv.f;
 }
 
-half floatToHalf(float f)
+float float16to32(float16 f)
 {
-	union { float f; uint32 i; } conv;
-	conv.f = f;
+	return asfloat32(mantissatable[offsettable[f >> 10] + (f & 0x3FF)] + exponenttable[f >> 10]);
+}
+
+float16 float32to16(float f)
+{
+	uint32 u = asuint32(f);
+	return basetable[(u >> 23) & 0x1FF] + ((u & 0x007FFFFF) >> shifttable[(u >> 23) & 0x1FF]);
+}
+
+// Adapted from https://stackoverflow.com/questions/41532085/how-to-pack-unpack-11-and-10-bit-floats-in-javascript-for-webgl2
+
+float float11to32(float11 f)
+{
+	uint16 exponent = f >> 6;
+	uint16 mantissa = f & 0x3F;
+
+	if (exponent == 0)
+		return mantissa == 0 ? 0 : powf(2.0f, -14.0f) * (mantissa / 64.0f);
+
+	if (exponent < 31)
+		return powf(2.0f, exponent - 15) * (1.0f + mantissa / 64.0f);
+
+	return mantissa == 0 ? std::numeric_limits<float>::infinity() : std::numeric_limits<float>::quiet_NaN();
+}
+
+float11 float32to11(float f)
+{
+	const uint16 EXPONENT_BITS = 0x1F;
+	const uint16 EXPONENT_SHIFT = 6;
+	const uint16 EXPONENT_BIAS = 15;
+	const uint16 MANTISSA_BITS = 0x3F;
+	const uint16 MANTISSA_SHIFT = (23 - EXPONENT_SHIFT);
+	const uint16 MAX_EXPONENT = (EXPONENT_BITS << EXPONENT_SHIFT);
+
+	uint32 u = asuint32(f);
+
+	if (u & 0x80000000)
+		return 0; // Negative values go to 0.
+
+	// Map exponent to the range [-127,128]
+	int32 exponent = (int32)((u >> 23) & 0xFF) - 127;
+	uint32 mantissa = u & 0x007FFFFF;
+
+	if (exponent > 15) // Infinity or NaN
+		return MAX_EXPONENT | (exponent == 128 ? (mantissa & MANTISSA_BITS) : 0);
+	else if (exponent <= -15)
+		return 0;
+
+	exponent += EXPONENT_BIAS;
+
+	return ((uint16)exponent << EXPONENT_SHIFT) | (mantissa >> MANTISSA_SHIFT);
+}
+
+float float10to32(float10 f)
+{
+	uint16 exponent = f >> 5;
+	uint16 mantissa = f & 0x1F;
+
+	if (exponent == 0)
+		return mantissa == 0 ? 0 : powf(2.0f, -14.0f) * (mantissa / 32.0f);
+
+	if (exponent < 31)
+		return powf(2.0f, exponent - 15) * (1.0f + mantissa / 32.0f);
+
+	return mantissa == 0 ? std::numeric_limits<float>::infinity() : std::numeric_limits<float>::quiet_NaN();
+}
+
+float10 float32to10(float f)
+{
+	const uint16 EXPONENT_BITS = 0x1F;
+	const uint16 EXPONENT_SHIFT = 5;
+	const uint16 EXPONENT_BIAS = 15;
+	const uint16 MANTISSA_BITS = 0x1F;
+	const uint16 MANTISSA_SHIFT = (23 - EXPONENT_SHIFT);
+	const uint16 MAX_EXPONENT = (EXPONENT_BITS << EXPONENT_SHIFT);
+
+	uint32 u = asuint32(f);
+
+	if (u & 0x80000000)
+		return 0; // Negative values go to 0.
+
+	// Map exponent to the range [-127,128]
+	int32 exponent = (int32)((u >> 23) & 0xFF) - 127;
+	uint32 mantissa = u & 0x007FFFFF;
+
+	if (exponent > 15) // Infinity or NaN
+		return MAX_EXPONENT | (exponent == 128 ? (mantissa & MANTISSA_BITS) : 0);
+	else if (exponent <= -15)
+		return 0;
+
+	exponent += EXPONENT_BIAS;
 
-	return basetable[(conv.i >> 23) & 0x1FF] + ((conv.i & 0x007FFFFF) >> shifttable[(conv.i >> 23) & 0x1FF]);
+	return ((uint16)exponent << EXPONENT_SHIFT) | (mantissa >> MANTISSA_SHIFT);
 }
 
 } // love

+ 13 - 8
src/common/halffloat.h → src/common/floattypes.h

@@ -18,21 +18,26 @@
  * 3. This notice may not be removed or altered from any source distribution.
  **/
 
-#ifndef LOVE_HALF_FLOAT_H
-#define LOVE_HALF_FLOAT_H
+#pragma once
 
 #include "int.h"
 
 namespace love
 {
 
-typedef uint16 half;
+typedef uint16 float16;
+typedef uint16 float11;
+typedef uint16 float10;
 
-void halfInit();
+void float16Init();
 
-float halfToFloat(half h);
-half floatToHalf(float f);
+float float16to32(float16 f);
+float16 float32to16(float f);
 
-} // love
+float float11to32(float11 f);
+float11 float32to11(float f);
+
+float float10to32(float10 f);
+float10 float32to10(float f);
 
-#endif // LOVE_HALF_FLOAT_H
+} // love

+ 1 - 1
src/modules/image/Image.cpp

@@ -43,7 +43,7 @@ Image::Image()
 {
 	using namespace magpie;
 
-	halfInit(); // Makes sure half-float conversions can be used.
+	float16Init(); // Makes sure half-float conversions can be used.
 
 	formatHandlers = {
 		new PNGHandler,

+ 8 - 7
src/modules/image/ImageData.cpp

@@ -280,7 +280,7 @@ union Row
 {
 	uint8 *u8;
 	uint16 *u16;
-	half *f16;
+	float16 *f16;
 	float *f32;
 };
 
@@ -293,7 +293,7 @@ static void pasteRGBA8toRGBA16(Row src, Row dst, int w)
 static void pasteRGBA8toRGBA16F(Row src, Row dst, int w)
 {
 	for (int i = 0; i < w * 4; i++)
-		dst.f16[i] = floatToHalf(src.u8[i] / 255.0f);
+		dst.f16[i] = float32to16(src.u8[i] / 255.0f);
 }
 
 static void pasteRGBA8toRGBA32F(Row src, Row dst, int w)
@@ -311,7 +311,7 @@ static void pasteRGBA16toRGBA8(Row src, Row dst, int w)
 static void pasteRGBA16toRGBA16F(Row src, Row dst, int w)
 {
 	for (int i = 0; i < w * 4; i++)
-		dst.f16[i] = floatToHalf(src.u16[i] / 65535.0f);
+		dst.f16[i] = float32to16(src.u16[i] / 65535.0f);
 }
 
 static void pasteRGBA16toRGBA32F(Row src, Row dst, int w)
@@ -323,19 +323,19 @@ static void pasteRGBA16toRGBA32F(Row src, Row dst, int w)
 static void pasteRGBA16FtoRGBA8(Row src, Row dst, int w)
 {
 	for (int i = 0; i < w * 4; i++)
-		dst.u8[i] = (uint8) (halfToFloat(src.f16[i]) * 255.0f);
+		dst.u8[i] = (uint8) (float16to32(src.f16[i]) * 255.0f);
 }
 
 static void pasteRGBA16FtoRGBA16(Row src, Row dst, int w)
 {
 	for (int i = 0; i < w * 4; i++)
-		dst.u16[i] = (uint16) (halfToFloat(src.f16[i]) * 65535.0f);
+		dst.u16[i] = (uint16) (float16to32(src.f16[i]) * 65535.0f);
 }
 
 static void pasteRGBA16FtoRGBA32F(Row src, Row dst, int w)
 {
 	for (int i = 0; i < w * 4; i++)
-		dst.f32[i] = halfToFloat(src.f16[i]);
+		dst.f32[i] = float16to32(src.f16[i]);
 }
 
 static void pasteRGBA32FtoRGBA8(Row src, Row dst, int w)
@@ -353,7 +353,7 @@ static void pasteRGBA32FtoRGBA16(Row src, Row dst, int w)
 static void pasteRGBA32FtoRGBA16F(Row src, Row dst, int w)
 {
 	for (int i = 0; i < w * 4; i++)
-		dst.f16[i] = (uint16) floatToHalf(src.f32[i]);
+		dst.f16[i] = (uint16) float32to16(src.f32[i]);
 }
 
 void ImageData::paste(ImageData *src, int dx, int dy, int sx, int sy, int sw, int sh)
@@ -498,6 +498,7 @@ bool ImageData::validPixelFormat(PixelFormat format)
 	case PIXELFORMAT_RGB5A1:
 	case PIXELFORMAT_RGB565:
 	case PIXELFORMAT_RGB10A2:
+	case PIXELFORMAT_RG11B10F:
 		return true;
 	default:
 		return false;

+ 7 - 7
src/modules/image/ImageData.h

@@ -25,7 +25,7 @@
 #include "common/StringMap.h"
 #include "common/int.h"
 #include "common/pixelformat.h"
-#include "common/halffloat.h"
+#include "common/floattypes.h"
 #include "filesystem/FileData.h"
 #include "thread/threads.h"
 #include "ImageDataBase.h"
@@ -40,12 +40,12 @@ namespace image
 
 union Pixel
 {
-	uint8  rgba8[4];
-	uint16 rgba16[4];
-	half   rgba16f[4];
-	float  rgba32f[4];
-	uint16 packed16;
-	uint32 packed32;
+	uint8   rgba8[4];
+	uint16  rgba16[4];
+	float16 rgba16f[4];
+	float   rgba32f[4];
+	uint16  packed16;
+	uint32  packed32;
 };
 
 /**

+ 3 - 3
src/modules/image/magpie/EXRHandler.cpp

@@ -20,7 +20,7 @@
 
 // LOVE
 #include "EXRHandler.h"
-#include "common/halffloat.h"
+#include "common/floattypes.h"
 #include "common/Exception.h"
 
 // zlib (for tinyexr)
@@ -157,12 +157,12 @@ FormatHandler::DecodedImage EXRHandler::decode(Data *data)
 	{
 		img.format = PIXELFORMAT_RGBA16F;
 
-		half *rgba[4] = {nullptr};
+		float16 *rgba[4] = {nullptr};
 		getEXRChannels(exrHeader, exrImage, rgba);
 
 		try
 		{
-			img.data = (unsigned char *) loadEXRChannels(img.width, img.height, rgba, floatToHalf(1.0f));
+			img.data = (unsigned char *) loadEXRChannels(img.width, img.height, rgba, float32to16(1.0f));
 		}
 		catch (love::Exception &)
 		{

+ 94 - 63
src/modules/image/wrap_ImageData.cpp

@@ -110,9 +110,9 @@ template <int components>
 static void luax_checkpixel_float16(lua_State *L, int startidx, Pixel &p)
 {
 	for (int i = 0; i < std::min(components, 3); i++)
-		p.rgba16f[i] = floatToHalf((float) luaL_checknumber(L, startidx + i));
+		p.rgba16f[i] = float32to16((float) luaL_checknumber(L, startidx + i));
 	 if (components > 3)
-		p.rgba16f[3] = floatToHalf((float) luaL_optnumber(L, startidx + 3, 1.0));
+		p.rgba16f[3] = float32to16((float) luaL_optnumber(L, startidx + 3, 1.0));
 }
 
 template <int components>
@@ -187,40 +187,49 @@ static void luax_checkpixel_rgba32f(lua_State *L, int startidx, Pixel &p)
 static void luax_checkpixel_rgba4(lua_State *L, int startidx, Pixel &p)
 {
 	// LSB->MSB: [a, b, g, r]
-	uint16 r = ((uint16) ((luax_checknumberclamped01(L, startidx + 0) * 0xF) + 0.5)) << 12;
-	uint16 g = ((uint16) ((luax_checknumberclamped01(L, startidx + 1) * 0xF) + 0.5)) << 8;
-	uint16 b = ((uint16) ((luax_checknumberclamped01(L, startidx + 2) * 0xF) + 0.5)) << 4;
-	uint16 a = ((uint16) ((luax_optnumberclamped01(L, startidx + 3, 1.0) * 0xF) + 0.5)) << 0;
-	p.packed16 = r | g | b | a;
+	uint16 r = (uint16) ((luax_checknumberclamped01(L, startidx + 0) * 0xF) + 0.5);
+	uint16 g = (uint16) ((luax_checknumberclamped01(L, startidx + 1) * 0xF) + 0.5);
+	uint16 b = (uint16) ((luax_checknumberclamped01(L, startidx + 2) * 0xF) + 0.5);
+	uint16 a = (uint16) ((luax_optnumberclamped01(L, startidx + 3, 1.0) * 0xF) + 0.5);
+	p.packed16 = (r << 12) | (g << 8) | (b << 4) | (a << 0);
 }
 
 static void luax_checkpixel_rgb5a1(lua_State *L, int startidx, Pixel &p)
 {
 	// LSB->MSB: [a, b, g, r]
-	uint16 r = ((uint16) ((luax_checknumberclamped01(L, startidx + 0) * 0x1F) + 0.5)) << 11;
-	uint16 g = ((uint16) ((luax_checknumberclamped01(L, startidx + 1) * 0x1F) + 0.5)) << 6;
-	uint16 b = ((uint16) ((luax_checknumberclamped01(L, startidx + 2) * 0x1F) + 0.5)) << 1;
-	uint16 a = ((uint16) ((luax_optnumberclamped01(L, startidx + 3, 1.0) * 0x1) + 0.5)) << 0;
-	p.packed16 = r | g | b | a;
+	uint16 r = (uint16) ((luax_checknumberclamped01(L, startidx + 0) * 0x1F) + 0.5);
+	uint16 g = (uint16) ((luax_checknumberclamped01(L, startidx + 1) * 0x1F) + 0.5);
+	uint16 b = (uint16) ((luax_checknumberclamped01(L, startidx + 2) * 0x1F) + 0.5);
+	uint16 a = (uint16) ((luax_optnumberclamped01(L, startidx + 3, 1.0) * 0x1) + 0.5);
+	p.packed16 = (r << 11) | (g << 6) | (b << 1) | (a << 0);
 }
 
 static void luax_checkpixel_rgb565(lua_State *L, int startidx, Pixel &p)
 {
 	// LSB->MSB: [b, g, r]
-	uint16 r = ((uint16) ((luax_checknumberclamped01(L, startidx + 0) * 0x1F) + 0.5)) << 11;
-	uint16 g = ((uint16) ((luax_checknumberclamped01(L, startidx + 1) * 0x3F) + 0.5)) << 5;
-	uint16 b = ((uint16) ((luax_checknumberclamped01(L, startidx + 2) * 0x1F) + 0.5)) << 0;
-	p.packed16 = r | g | b;
+	uint16 r = (uint16) ((luax_checknumberclamped01(L, startidx + 0) * 0x1F) + 0.5);
+	uint16 g = (uint16) ((luax_checknumberclamped01(L, startidx + 1) * 0x3F) + 0.5);
+	uint16 b = (uint16) ((luax_checknumberclamped01(L, startidx + 2) * 0x1F) + 0.5);
+	p.packed16 = (r << 11) | (g << 5) | (b << 0);
 }
 
 static void luax_checkpixel_rgb10a2(lua_State *L, int startidx, Pixel &p)
 {
 	// LSB->MSB: [r, g, b, a]
-	uint32 r = ((uint32) ((luax_checknumberclamped01(L, startidx + 0) * 0x3FF) + 0.5)) << 0;
-	uint32 g = ((uint32) ((luax_checknumberclamped01(L, startidx + 1) * 0x3FF) + 0.5)) << 10;
-	uint32 b = ((uint32) ((luax_checknumberclamped01(L, startidx + 2) * 0x3FF) + 0.5)) << 20;
-	uint32 a = ((uint32) ((luax_optnumberclamped01(L, startidx + 3, 1.0) * 0x3) + 0.5)) << 30;
-	p.packed32 = r | g | b | a;
+	uint32 r = (uint32) ((luax_checknumberclamped01(L, startidx + 0) * 0x3FF) + 0.5);
+	uint32 g = (uint32) ((luax_checknumberclamped01(L, startidx + 1) * 0x3FF) + 0.5);
+	uint32 b = (uint32) ((luax_checknumberclamped01(L, startidx + 2) * 0x3FF) + 0.5);
+	uint32 a = (uint32) ((luax_optnumberclamped01(L, startidx + 3, 1.0) * 0x3) + 0.5);
+	p.packed32 = (r << 0) | (g << 10) | (b << 20) | (a << 30);
+}
+
+static void luax_checkpixel_rg11b10f(lua_State *L, int startidx, Pixel &p)
+{
+	// LSB->MSB: [r, g, b]
+	float11 r = float32to11((float) luaL_checknumber(L, startidx + 0));
+	float11 g = float32to11((float) luaL_checknumber(L, startidx + 1));
+	float10 b = float32to10((float) luaL_checknumber(L, startidx + 2));
+	p.packed32 = (r << 0) | (g << 11) | (b << 22);
 }
 
 static lua_Number fillValues[] = {0.0, 0.0, 0.0, 1.0};
@@ -249,7 +258,7 @@ template <int components>
 static int luax_pushpixel_float16(lua_State *L, const Pixel &p)
 {
 	for (int i = 0; i < components; i++)
-		lua_pushnumber(L, (lua_Number) halfToFloat(p.rgba16f[i]));
+		lua_pushnumber(L, (lua_Number) float16to32(p.rgba16f[i]));
 	for (int i = components; i < 4; i++)
 		lua_pushnumber(L, fillValues[i]);
 	return 4;
@@ -358,10 +367,20 @@ static int luax_pushpixel_rgb565(lua_State *L, const Pixel &p)
 static int luax_pushpixel_rgb10a2(lua_State *L, const Pixel &p)
 {
 	// LSB->MSB: [r, g, b, a]
-	lua_pushnumber(L, ((p.packed16 >>  0) & 0x3FF) / (double)0x3FF);
-	lua_pushnumber(L, ((p.packed16 >> 10) & 0x3FF) / (double)0x3FF);
-	lua_pushnumber(L, ((p.packed16 >> 20) & 0x3FF) / (double)0x3FF);
-	lua_pushnumber(L, ((p.packed16 >> 30) & 0x3)   / (double)0x3);
+	lua_pushnumber(L, ((p.packed32 >>  0) & 0x3FF) / (double)0x3FF);
+	lua_pushnumber(L, ((p.packed32 >> 10) & 0x3FF) / (double)0x3FF);
+	lua_pushnumber(L, ((p.packed32 >> 20) & 0x3FF) / (double)0x3FF);
+	lua_pushnumber(L, ((p.packed32 >> 30) & 0x3)   / (double)0x3);
+	return 4;
+}
+
+static int luax_pushpixel_rg11b10f(lua_State *L, const Pixel &p)
+{
+	// LSB->MSB: [r, g, b]
+	lua_pushnumber(L, float11to32((float11) ((p.packed32 >>  0) & 0x7FF)));
+	lua_pushnumber(L, float11to32((float11) ((p.packed32 >> 11) & 0x7FF)));
+	lua_pushnumber(L, float10to32((float10) ((p.packed32 >> 22) & 0x3FF)));
+	lua_pushnumber(L, 1.0);
 	return 4;
 }
 
@@ -526,8 +545,14 @@ struct FFI_ImageData
 	void (*lockMutex)(Proxy *p);
 	void (*unlockMutex)(Proxy *p);
 
-	float (*halfToFloat)(half h);
-	half (*floatToHalf)(float f);
+	float (*float16to32)(float16 f);
+	float16 (*float32to16)(float f);
+
+	float (*float11to32)(float11 f);
+	float11 (*float32to11)(float f);
+
+	float (*float10to32)(float10 f);
+	float10 (*float32to10)(float f);
 };
 
 static FFI_ImageData ffifuncs =
@@ -546,8 +571,12 @@ static FFI_ImageData ffifuncs =
 		i->getMutex()->unlock();
 	},
 
-	halfToFloat,
-	floatToHalf,
+	float16to32,
+	float32to16,
+	float11to32,
+	float32to11,
+	float10to32,
+	float32to10,
 };
 
 static const luaL_Reg w_ImageData_functions[] =
@@ -571,39 +600,41 @@ static const luaL_Reg w_ImageData_functions[] =
 
 extern "C" int luaopen_imagedata(lua_State *L)
 {
-	checkFormats[PIXELFORMAT_R8]      = luax_checkpixel_r8;
-	checkFormats[PIXELFORMAT_RG8]     = luax_checkpixel_rg8;
-	checkFormats[PIXELFORMAT_RGBA8]   = luax_checkpixel_rgba8;
-	checkFormats[PIXELFORMAT_R16]     = luax_checkpixel_r16;
-	checkFormats[PIXELFORMAT_RG16]    = luax_checkpixel_rg16;
-	checkFormats[PIXELFORMAT_RGBA16]  = luax_checkpixel_rgba16;
-	checkFormats[PIXELFORMAT_R16F]    = luax_checkpixel_r16f;
-	checkFormats[PIXELFORMAT_RG16F]   = luax_checkpixel_rg16f;
-	checkFormats[PIXELFORMAT_RGBA16F] = luax_checkpixel_rgba16f;
-	checkFormats[PIXELFORMAT_R32F]    = luax_checkpixel_r32f;
-	checkFormats[PIXELFORMAT_RG32F]   = luax_checkpixel_rg32f;
-	checkFormats[PIXELFORMAT_RGBA32F] = luax_checkpixel_rgba32f;
-	checkFormats[PIXELFORMAT_RGBA4]   = luax_checkpixel_rgba4;
-	checkFormats[PIXELFORMAT_RGB5A1]  = luax_checkpixel_rgb5a1;
-	checkFormats[PIXELFORMAT_RGB565]  = luax_checkpixel_rgb565;
-	checkFormats[PIXELFORMAT_RGB10A2] = luax_checkpixel_rgb10a2;
-
-	pushFormats[PIXELFORMAT_R8]      = luax_pushpixel_r8;
-	pushFormats[PIXELFORMAT_RG8]     = luax_pushpixel_rg8;
-	pushFormats[PIXELFORMAT_RGBA8]   = luax_pushpixel_rgba8;
-	pushFormats[PIXELFORMAT_R16]     = luax_pushpixel_r16;
-	pushFormats[PIXELFORMAT_RG16]    = luax_pushpixel_rg16;
-	pushFormats[PIXELFORMAT_RGBA16]  = luax_pushpixel_rgba16;
-	pushFormats[PIXELFORMAT_R16F]    = luax_pushpixel_r16f;
-	pushFormats[PIXELFORMAT_RG16F]   = luax_pushpixel_rg16f;
-	pushFormats[PIXELFORMAT_RGBA16F] = luax_pushpixel_rgba16f;
-	pushFormats[PIXELFORMAT_R32F]    = luax_pushpixel_r32f;
-	pushFormats[PIXELFORMAT_RG32F]   = luax_pushpixel_rg32f;
-	pushFormats[PIXELFORMAT_RGBA32F] = luax_pushpixel_rgba32f;
-	pushFormats[PIXELFORMAT_RGBA4]   = luax_pushpixel_rgba4;
-	pushFormats[PIXELFORMAT_RGB5A1]  = luax_pushpixel_rgb5a1;
-	pushFormats[PIXELFORMAT_RGB565]  = luax_pushpixel_rgb565;
-	pushFormats[PIXELFORMAT_RGB10A2] = luax_pushpixel_rgb10a2;
+	checkFormats[PIXELFORMAT_R8]       = luax_checkpixel_r8;
+	checkFormats[PIXELFORMAT_RG8]      = luax_checkpixel_rg8;
+	checkFormats[PIXELFORMAT_RGBA8]    = luax_checkpixel_rgba8;
+	checkFormats[PIXELFORMAT_R16]      = luax_checkpixel_r16;
+	checkFormats[PIXELFORMAT_RG16]     = luax_checkpixel_rg16;
+	checkFormats[PIXELFORMAT_RGBA16]   = luax_checkpixel_rgba16;
+	checkFormats[PIXELFORMAT_R16F]     = luax_checkpixel_r16f;
+	checkFormats[PIXELFORMAT_RG16F]    = luax_checkpixel_rg16f;
+	checkFormats[PIXELFORMAT_RGBA16F]  = luax_checkpixel_rgba16f;
+	checkFormats[PIXELFORMAT_R32F]     = luax_checkpixel_r32f;
+	checkFormats[PIXELFORMAT_RG32F]    = luax_checkpixel_rg32f;
+	checkFormats[PIXELFORMAT_RGBA32F]  = luax_checkpixel_rgba32f;
+	checkFormats[PIXELFORMAT_RGBA4]    = luax_checkpixel_rgba4;
+	checkFormats[PIXELFORMAT_RGB5A1]   = luax_checkpixel_rgb5a1;
+	checkFormats[PIXELFORMAT_RGB565]   = luax_checkpixel_rgb565;
+	checkFormats[PIXELFORMAT_RGB10A2]  = luax_checkpixel_rgb10a2;
+	checkFormats[PIXELFORMAT_RG11B10F] = luax_checkpixel_rg11b10f;
+
+	pushFormats[PIXELFORMAT_R8]       = luax_pushpixel_r8;
+	pushFormats[PIXELFORMAT_RG8]      = luax_pushpixel_rg8;
+	pushFormats[PIXELFORMAT_RGBA8]    = luax_pushpixel_rgba8;
+	pushFormats[PIXELFORMAT_R16]      = luax_pushpixel_r16;
+	pushFormats[PIXELFORMAT_RG16]     = luax_pushpixel_rg16;
+	pushFormats[PIXELFORMAT_RGBA16]   = luax_pushpixel_rgba16;
+	pushFormats[PIXELFORMAT_R16F]     = luax_pushpixel_r16f;
+	pushFormats[PIXELFORMAT_RG16F]    = luax_pushpixel_rg16f;
+	pushFormats[PIXELFORMAT_RGBA16F]  = luax_pushpixel_rgba16f;
+	pushFormats[PIXELFORMAT_R32F]     = luax_pushpixel_r32f;
+	pushFormats[PIXELFORMAT_RG32F]    = luax_pushpixel_rg32f;
+	pushFormats[PIXELFORMAT_RGBA32F]  = luax_pushpixel_rgba32f;
+	pushFormats[PIXELFORMAT_RGBA4]    = luax_pushpixel_rgba4;
+	pushFormats[PIXELFORMAT_RGB5A1]   = luax_pushpixel_rgb5a1;
+	pushFormats[PIXELFORMAT_RGB565]   = luax_pushpixel_rgb565;
+	pushFormats[PIXELFORMAT_RGB10A2]  = luax_pushpixel_rgb10a2;
+	pushFormats[PIXELFORMAT_RG11B10F] = luax_pushpixel_rg11b10f;
 
 	int ret = luax_register_type(L, &ImageData::type, data::w_Data_functions, w_ImageData_functions, nullptr);
 

+ 46 - 19
src/modules/image/wrap_ImageData.lua

@@ -78,15 +78,23 @@ if not bitstatus then return end
 
 pcall(ffi.cdef, [[
 typedef struct Proxy Proxy;
-typedef uint16_t half;
+typedef uint16_t float16;
+typedef uint16_t float11;
+typedef uint16_t float10;
 
 typedef struct FFI_ImageData
 {
 	void (*lockMutex)(Proxy *p);
 	void (*unlockMutex)(Proxy *p);
 
-	float (*halfToFloat)(half h);
-	half (*floatToHalf)(float f);
+	float (*float16to32)(float16 f);
+	float16 (*float32to16)(float f);
+
+	float (*float11to32)(float11 f);
+	float11 (*float32to11)(float f);
+
+	float (*float10to32)(float10 f);
+	float10 (*float32to10)(float f);
 } FFI_ImageData;
 
 struct ImageData_Pixel_R8 { uint8_t r; };
@@ -97,9 +105,9 @@ struct ImageData_Pixel_R16 { uint16_t r; };
 struct ImageData_Pixel_RG16 { uint16_t r, g; };
 struct ImageData_Pixel_RGBA16 { uint16_t r, g, b, a; };
 
-struct ImageData_Pixel_R16F { half r; };
-struct ImageData_Pixel_RG16F { half r, g; };
-struct ImageData_Pixel_RGBA16F { half r, g, b, a; };
+struct ImageData_Pixel_R16F { float16 r; };
+struct ImageData_Pixel_RG16F { float16 r, g; };
+struct ImageData_Pixel_RGBA16F { float16 r, g, b, a; };
 
 struct ImageData_Pixel_R32F { float r; };
 struct ImageData_Pixel_RG32F { float r, g; };
@@ -109,6 +117,7 @@ struct ImageData_Pixel_RGBA4 { uint16_t rgba; };
 struct ImageData_Pixel_RGB5A1 { uint16_t rgba; };
 struct ImageData_Pixel_RGB565 { uint16_t rgb; };
 struct ImageData_Pixel_RGB10A2 { uint32_t rgba; };
+struct ImageData_Pixel_RG11B10F { uint32_t rgb; };
 ]])
 
 local ffifuncs = ffi.cast("FFI_ImageData **", ffifuncspointer_str)[0]
@@ -179,35 +188,35 @@ local conversions = {
 	r16f = {
 		pointer = ffi.typeof("struct ImageData_Pixel_R16F *"),
 		tolua = function(self)
-			return tonumber(ffifuncs.halfToFloat(self.r)), 0, 0, 1
+			return tonumber(ffifuncs.float16to32(self.r)), 0, 0, 1
 		end,
 		fromlua = function(self, r)
-			self.r = ffifuncs.floatToHalf(r)
+			self.r = ffifuncs.float32to16(r)
 		end,
 	},
 	rg16f = {
 		pointer = ffi.typeof("struct ImageData_Pixel_RG16F *"),
 		tolua = function(self)
-			return tonumber(ffifuncs.halfToFloat(self.r)), tonumber(ffifuncs.halfToFloat(self.g)), 0, 1
+			return tonumber(ffifuncs.float16to32(self.r)), tonumber(ffifuncs.float16to32(self.g)), 0, 1
 		end,
 		fromlua = function(self, r, g, b, a)
-			self.r = ffifuncs.floatToHalf(r)
-			self.g = ffifuncs.floatToHalf(g)
+			self.r = ffifuncs.float32to16(r)
+			self.g = ffifuncs.float32to16(g)
 		end,
 	},
 	rgba16f = {
 		pointer = ffi.typeof("struct 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))
+			return tonumber(ffifuncs.float16to32(self.r)),
+			       tonumber(ffifuncs.float16to32(self.g)),
+			       tonumber(ffifuncs.float16to32(self.b)),
+			       tonumber(ffifuncs.float16to32(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)
+			self.r = ffifuncs.float32to16(r)
+			self.g = ffifuncs.float32to16(g)
+			self.b = ffifuncs.float32to16(b)
+			self.a = ffifuncs.float32to16(a == nil and 1.0 or a)
 		end,
 	},
 	r32f = {
@@ -319,6 +328,24 @@ local conversions = {
 			self.rgba = bit.bor(r, bit.lshift(g, 10), bit.lshift(b, 20), bit.lshift(a, 30))
 		end,
 	},
+	rg11b10f = {
+		-- LSB->MSB: [r, g, b]
+		pointer = ffi.typeof("struct ImageData_Pixel_RG11B10F *"),
+		tolua = function(self)
+			local rgb = self.rgb
+			local r = tonumber(ffifuncs.float11to32(bit.band(rgb, 0x7FF)))
+			local g = tonumber(ffifuncs.float11to32(bit.band(bit.rshift(rgb, 11), 0x7FF)))
+			local b = tonumber(ffifuncs.float10to32(bit.band(bit.rshift(rgb, 22), 0x3FF)))
+			return r, g, b, 1
+		end,
+		fromlua = function(self, r, g, b, a)
+			self.rgb = bit.bor(
+				ffifuncs.float32to11(r),
+				bit.lshift(ffifuncs.float32to11(g), 11),
+				bit.lshift(ffifuncs.float32to10(b), 22)
+			)
+		end,
+	},
 }
 
 local _getWidth = ImageData.getWidth