Browse Source

Reorganize ImageData:set/mapPixel code so less of it is in the Lua wrapper and more of it is in the ImageData class itself.

Alex Szpakowski 5 years ago
parent
commit
e11d43507e

+ 32 - 0
src/common/pixelformat.cpp

@@ -176,4 +176,36 @@ size_t getPixelFormatSize(PixelFormat format)
 	}
 }
 
+int getPixelFormatColorComponents(PixelFormat format)
+{
+	switch (format)
+	{
+	case PIXELFORMAT_R8:
+	case PIXELFORMAT_R16:
+	case PIXELFORMAT_R16F:
+	case PIXELFORMAT_R32F:
+		return 1;
+	case PIXELFORMAT_RG8:
+	case PIXELFORMAT_RG16:
+	case PIXELFORMAT_RG16F:
+	case PIXELFORMAT_RG32F:
+	case PIXELFORMAT_LA8:
+		return 2;
+	case PIXELFORMAT_RGB565:
+	case PIXELFORMAT_RG11B10F:
+		return 3;
+	case PIXELFORMAT_RGBA8:
+	case PIXELFORMAT_sRGBA8:
+	case PIXELFORMAT_RGBA16:
+	case PIXELFORMAT_RGBA16F:
+	case PIXELFORMAT_RGBA32F:
+	case PIXELFORMAT_RGBA4:
+	case PIXELFORMAT_RGB5A1:
+	case PIXELFORMAT_RGB10A2:
+		return 4;
+	default:
+		return 0;
+	}
+}
+
 } // love

+ 5 - 0
src/common/pixelformat.h

@@ -135,4 +135,9 @@ bool isPixelFormatStencil(PixelFormat format);
  **/
 size_t getPixelFormatSize(PixelFormat format);
 
+/**
+ * Gets the number of color components in the given pixel format.
+ **/
+int getPixelFormatColorComponents(PixelFormat format);
+
 } // love

+ 347 - 5
src/modules/image/ImageData.cpp

@@ -98,6 +98,9 @@ void ImageData::create(int width, int height, PixelFormat format, void *data)
 
 	decodeHandler = nullptr;
 	this->format = format;
+
+	pixelSetFunction = getPixelSetFunction(format);
+	pixelGetFunction = getPixelGetFunction(format);
 }
 
 void ImageData::decode(Data *data)
@@ -153,6 +156,9 @@ void ImageData::decode(Data *data)
 	this->format = decodedimage.format;
 
 	decodeHandler = decoder;
+
+	pixelSetFunction = getPixelSetFunction(format);
+	pixelGetFunction = getPixelGetFunction(format);
 }
 
 love::filesystem::FileData *ImageData::encode(FormatHandler::EncodedFormat encodedFormat, const char *filename, bool writefile) const
@@ -253,27 +259,313 @@ bool ImageData::inside(int x, int y) const
 	return x >= 0 && x < getWidth() && y >= 0 && y < getHeight();
 }
 
-void ImageData::setPixel(int x, int y, const Pixel &p)
+static float clamp01(float x)
+{
+	return std::min(std::max(x, 0.0f), 1.0f);
+}
+
+static void setPixelR8(const Colorf &c, ImageData::Pixel *p)
+{
+	p->rgba8[0] = (uint8) (clamp01(c.r) * 255.0f + 0.5f);
+}
+
+static void setPixelRG8(const Colorf &c, ImageData::Pixel *p)
+{
+	p->rgba8[0] = (uint8) (clamp01(c.r) * 255.0f + 0.5f);
+	p->rgba8[1] = (uint8) (clamp01(c.g) * 255.0f + 0.5f);
+}
+
+static void setPixelRGBA8(const Colorf &c, ImageData::Pixel *p)
+{
+	p->rgba8[0] = (uint8) (clamp01(c.r) * 255.0f + 0.5f);
+	p->rgba8[1] = (uint8) (clamp01(c.g) * 255.0f + 0.5f);
+	p->rgba8[2] = (uint8) (clamp01(c.b) * 255.0f + 0.5f);
+	p->rgba8[3] = (uint8) (clamp01(c.a) * 255.0f + 0.5f);
+}
+
+static void setPixelR16(const Colorf &c, ImageData::Pixel *p)
+{
+	p->rgba16[0] = (uint16) (clamp01(c.r) * 65535.0f + 0.5f);
+}
+
+static void setPixelRG16(const Colorf &c, ImageData::Pixel *p)
+{
+	p->rgba16[0] = (uint16) (clamp01(c.r) * 65535.0f + 0.5f);
+	p->rgba16[1] = (uint16) (clamp01(c.g) * 65535.0f + 0.5f);
+}
+
+static void setPixelRGBA16(const Colorf &c, ImageData::Pixel *p)
+{
+	p->rgba16[0] = (uint16) (clamp01(c.r) * 65535.0f + 0.5f);
+	p->rgba16[1] = (uint16) (clamp01(c.b) * 65535.0f + 0.5f);
+	p->rgba16[2] = (uint16) (clamp01(c.g) * 65535.0f + 0.5f);
+	p->rgba16[3] = (uint16) (clamp01(c.a) * 65535.0f + 0.5f);
+}
+
+static void setPixelR16F(const Colorf &c, ImageData::Pixel *p)
+{
+	p->rgba16f[0] = float32to16(c.r);
+}
+
+static void setPixelRG16F(const Colorf &c, ImageData::Pixel *p)
+{
+	p->rgba16f[0] = float32to16(c.r);
+	p->rgba16f[1] = float32to16(c.g);
+}
+
+static void setPixelRGBA16F(const Colorf &c, ImageData::Pixel *p)
+{
+	p->rgba16f[0] = float32to16(c.r);
+	p->rgba16f[1] = float32to16(c.g);
+	p->rgba16f[2] = float32to16(c.b);
+	p->rgba16f[3] = float32to16(c.a);
+}
+
+static void setPixelR32F(const Colorf &c, ImageData::Pixel *p)
+{
+	p->rgba32f[0] = c.r;
+}
+
+static void setPixelRG32F(const Colorf &c, ImageData::Pixel *p)
+{
+	p->rgba32f[0] = c.r;
+	p->rgba32f[1] = c.g;
+}
+
+static void setPixelRGBA32F(const Colorf &c, ImageData::Pixel *p)
+{
+	p->rgba32f[0] = c.r;
+	p->rgba32f[1] = c.g;
+	p->rgba32f[2] = c.b;
+	p->rgba32f[3] = c.a;
+}
+
+static void setPixelRGBA4(const Colorf &c, ImageData::Pixel *p)
+{
+	// LSB->MSB: [a, b, g, r]
+	uint16 r = (uint16) (clamp01(c.r) * 0xF + 0.5);
+	uint16 g = (uint16) (clamp01(c.g) * 0xF + 0.5);
+	uint16 b = (uint16) (clamp01(c.b) * 0xF + 0.5);
+	uint16 a = (uint16) (clamp01(c.a) * 0xF + 0.5);
+	p->packed16 = (r << 12) | (g << 8) | (b << 4) | (a << 0);
+}
+
+static void setPixelRGB5A1(const Colorf &c, ImageData::Pixel *p)
+{
+	// LSB->MSB: [a, b, g, r]
+	uint16 r = (uint16) (clamp01(c.r) * 0x1F + 0.5);
+	uint16 g = (uint16) (clamp01(c.g) * 0x1F + 0.5);
+	uint16 b = (uint16) (clamp01(c.b) * 0x1F + 0.5);
+	uint16 a = (uint16) (clamp01(c.a) * 0x1 + 0.5);
+	p->packed16 = (r << 11) | (g << 6) | (b << 1) | (a << 0);
+}
+
+static void setPixelRGB565(const Colorf &c, ImageData::Pixel *p)
+{
+	// LSB->MSB: [b, g, r]
+	uint16 r = (uint16) (clamp01(c.r) * 0x1F + 0.5);
+	uint16 g = (uint16) (clamp01(c.g) * 0x3F + 0.5);
+	uint16 b = (uint16) (clamp01(c.b) * 0x1F + 0.5);
+	p->packed16 = (r << 11) | (g << 5) | (b << 0);
+}
+
+static void setPixelRGB10A2(const Colorf &c, ImageData::Pixel *p)
+{
+	// LSB->MSB: [r, g, b, a]
+	uint32 r = (uint32) (clamp01(c.r) * 0x3FF + 0.5);
+	uint32 g = (uint32) (clamp01(c.g) * 0x3FF + 0.5);
+	uint32 b = (uint32) (clamp01(c.b) * 0x3FF + 0.5);
+	uint32 a = (uint32) (clamp01(c.a) * 0x3 + 0.5);
+	p->packed32 = (r << 0) | (g << 10) | (b << 20) | (a << 30);
+}
+
+static void setPixelRG11B10F(const Colorf &c, ImageData::Pixel *p)
+{
+	// LSB->MSB: [r, g, b]
+	float11 r = float32to11(c.r);
+	float11 g = float32to11(c.g);
+	float10 b = float32to10(c.b);
+	p->packed32 = (r << 0) | (g << 11) | (b << 22);
+}
+
+static void getPixelR8(const ImageData::Pixel *p, Colorf &c)
+{
+	c.r = p->rgba8[0] / 255.0f;
+	c.g = 0.0f;
+	c.b = 0.0f;
+	c.a = 1.0f;
+}
+
+static void getPixelRG8(const ImageData::Pixel *p, Colorf &c)
+{
+	c.r = p->rgba8[0] / 255.0f;
+	c.g = p->rgba8[1] / 255.0f;
+	c.b = 0.0f;
+	c.a = 1.0f;
+}
+
+static void getPixelRGBA8(const ImageData::Pixel *p, Colorf &c)
+{
+	c.r = p->rgba8[0] / 255.0f;
+	c.g = p->rgba8[1] / 255.0f;
+	c.b = p->rgba8[2] / 255.0f;
+	c.a = p->rgba8[3] / 255.0f;
+}
+
+static void getPixelR16(const ImageData::Pixel *p, Colorf &c)
+{
+	c.r = p->rgba16[0] / 65535.0f;
+	c.g = 0.0f;
+	c.b = 0.0f;
+	c.a = 1.0f;
+}
+
+static void getPixelRG16(const ImageData::Pixel *p, Colorf &c)
+{
+	c.r = p->rgba16[0] / 65535.0f;
+	c.g = p->rgba16[1] / 65535.0f;
+	c.b = 0.0f;
+	c.a = 1.0f;
+}
+
+static void getPixelRGBA16(const ImageData::Pixel *p, Colorf &c)
+{
+	c.r = p->rgba16[0] / 65535.0f;
+	c.g = p->rgba16[1] / 65535.0f;
+	c.b = p->rgba16[2] / 65535.0f;
+	c.a = p->rgba16[3] / 65535.0f;
+}
+
+static void getPixelR16F(const ImageData::Pixel *p, Colorf &c)
+{
+	c.r = float16to32(p->rgba16f[0]);
+	c.g = 0.0f;
+	c.b = 0.0f;
+	c.a = 1.0f;
+}
+
+static void getPixelRG16F(const ImageData::Pixel *p, Colorf &c)
+{
+	c.r = float16to32(p->rgba16f[0]);
+	c.g = float16to32(p->rgba16f[1]);
+	c.b = 0.0f;
+	c.a = 1.0f;
+}
+
+static void getPixelRGBA16F(const ImageData::Pixel *p, Colorf &c)
+{
+	c.r = float16to32(p->rgba16f[0]);
+	c.g = float16to32(p->rgba16f[1]);
+	c.b = float16to32(p->rgba16f[2]);
+	c.a = float16to32(p->rgba16f[3]);
+}
+
+static void getPixelR32F(const ImageData::Pixel *p, Colorf &c)
+{
+	c.r = p->rgba32f[0];
+	c.g = 0.0f;
+	c.b = 0.0f;
+	c.a = 1.0f;
+}
+
+static void getPixelRG32F(const ImageData::Pixel *p, Colorf &c)
+{
+	c.r = p->rgba32f[0];
+	c.g = p->rgba32f[1];
+	c.b = 0.0f;
+	c.a = 1.0f;
+}
+
+static void getPixelRGBA32F(const ImageData::Pixel *p, Colorf &c)
+{
+	c.r = p->rgba32f[0];
+	c.g = p->rgba32f[1];
+	c.b = p->rgba32f[2];
+	c.a = p->rgba32f[3];
+}
+
+static void getPixelRGBA4(const ImageData::Pixel *p, Colorf &c)
+{
+	// LSB->MSB: [a, b, g, r]
+	c.r = ((p->packed16 >> 12) & 0xF) / (float)0xF;
+	c.g = ((p->packed16 >>  8) & 0xF) / (float)0xF;
+	c.b = ((p->packed16 >>  4) & 0xF) / (float)0xF;
+	c.a = ((p->packed16 >>  0) & 0xF) / (float)0xF;
+}
+
+static void getPixelRGB5A1(const ImageData::Pixel *p, Colorf &c)
+{
+	// LSB->MSB: [a, b, g, r]
+	c.r = ((p->packed16 >> 11) & 0x1F) / (float)0x1F;
+	c.g = ((p->packed16 >>  6) & 0x1F) / (float)0x1F;
+	c.b = ((p->packed16 >>  1) & 0x1F) / (float)0x1F;
+	c.a = ((p->packed16 >>  0) & 0x1)  / (float)0x1;
+}
+
+static void getPixelRGB565(const ImageData::Pixel *p, Colorf &c)
+{
+	// LSB->MSB: [b, g, r]
+	c.r = ((p->packed16 >> 11) & 0x1F) / (float)0x1F;
+	c.g = ((p->packed16 >>  5) & 0x3F) / (float)0x3F;
+	c.b = ((p->packed16 >>  0) & 0x1F) / (float)0x1F;
+	c.a = 1.0f;
+}
+
+static void getPixelRGB10A2(const ImageData::Pixel *p, Colorf &c)
+{
+	// LSB->MSB: [r, g, b, a]
+	c.r = ((p->packed32 >>  0) & 0x3FF) / (float)0x3FF;
+	c.g = ((p->packed32 >> 10) & 0x3FF) / (float)0x3FF;
+	c.b = ((p->packed32 >> 20) & 0x3FF) / (float)0x3FF;
+	c.a = ((p->packed32 >> 30) & 0x3)   / (float)0x3;
+}
+
+static void getPixelRG11B10F(const ImageData::Pixel *p, Colorf &c)
+{
+	// LSB->MSB: [r, g, b]
+	c.r = float11to32((float11) ((p->packed32 >>  0) & 0x7FF));
+	c.g = float11to32((float11) ((p->packed32 >> 11) & 0x7FF));
+	c.b = float10to32((float10) ((p->packed32 >> 22) & 0x3FF));
+	c.a = 1.0f;
+}
+
+void ImageData::setPixel(int x, int y, const Colorf &c)
 {
 	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);
+	Pixel *p = (Pixel *) (data + ((y * width + x) * pixelsize));
+
+	if (pixelSetFunction == nullptr)
+		throw love::Exception("Unhandled pixel format %d in ImageData::setPixel", format);
 
 	Lock lock(mutex);
-	memcpy(pixeldata, &p, pixelsize);
+
+	pixelSetFunction(c, p);
 }
 
-void ImageData::getPixel(int x, int y, Pixel &p) const
+void ImageData::getPixel(int x, int y, Colorf &c) const
 {
 	if (!inside(x, y))
 		throw love::Exception("Attempt to get out-of-range pixel!");
 
 	size_t pixelsize = getPixelSize();
+	const Pixel *p = (const Pixel *) (data + ((y * width + x) * pixelsize));
+
+	if (pixelGetFunction == nullptr)
+		throw love::Exception("Unhandled pixel format %d in ImageData::setPixel", format);
 
 	Lock lock(mutex);
-	memcpy(&p, data + ((y * width + x) * pixelsize), pixelsize);
+
+	pixelGetFunction(p, c);
+}
+
+Colorf ImageData::getPixel(int x, int y) const
+{
+	Colorf c;
+	getPixel(x, y, c);
+	return c;
 }
 
 union Row
@@ -521,6 +813,56 @@ bool ImageData::canPaste(PixelFormat src, PixelFormat dst)
 	return true;
 }
 
+ImageData::PixelSetFunction ImageData::getPixelSetFunction(PixelFormat format)
+{
+	switch (format)
+	{
+		case PIXELFORMAT_R8: return setPixelR8;
+		case PIXELFORMAT_RG8: return setPixelRG8;
+		case PIXELFORMAT_RGBA8: return setPixelRGBA8;
+		case PIXELFORMAT_R16: return setPixelR16;
+		case PIXELFORMAT_RG16: return setPixelRG16;
+		case PIXELFORMAT_RGBA16: return setPixelRGBA16;
+		case PIXELFORMAT_R16F: return setPixelR16F;
+		case PIXELFORMAT_RG16F: return setPixelRG16F;
+		case PIXELFORMAT_RGBA16F: return setPixelRGBA16F;
+		case PIXELFORMAT_R32F: return setPixelR32F;
+		case PIXELFORMAT_RG32F: return setPixelRG32F;
+		case PIXELFORMAT_RGBA32F: return setPixelRGBA32F;
+		case PIXELFORMAT_RGBA4: return setPixelRGBA4;
+		case PIXELFORMAT_RGB5A1: return setPixelRGB5A1;
+		case PIXELFORMAT_RGB565: return setPixelRGB565;
+		case PIXELFORMAT_RGB10A2: return setPixelRGB10A2;
+		case PIXELFORMAT_RG11B10F: return setPixelRG11B10F;
+		default: return nullptr;
+	}
+}
+
+ImageData::PixelGetFunction ImageData::getPixelGetFunction(PixelFormat format)
+{
+	switch (format)
+	{
+		case PIXELFORMAT_R8: return getPixelR8;
+		case PIXELFORMAT_RG8: return getPixelRG8;
+		case PIXELFORMAT_RGBA8: return getPixelRGBA8;
+		case PIXELFORMAT_R16: return getPixelR16;
+		case PIXELFORMAT_RG16: return getPixelRG16;
+		case PIXELFORMAT_RGBA16: return getPixelRGBA16;
+		case PIXELFORMAT_R16F: return getPixelR16F;
+		case PIXELFORMAT_RG16F: return getPixelRG16F;
+		case PIXELFORMAT_RGBA16F: return getPixelRGBA16F;
+		case PIXELFORMAT_R32F: return getPixelR32F;
+		case PIXELFORMAT_RG32F: return getPixelRG32F;
+		case PIXELFORMAT_RGBA32F: return getPixelRGBA32F;
+		case PIXELFORMAT_RGBA4: return getPixelRGBA4;
+		case PIXELFORMAT_RGB5A1: return getPixelRGB5A1;
+		case PIXELFORMAT_RGB565: return getPixelRGB565;
+		case PIXELFORMAT_RGB10A2: return getPixelRGB10A2;
+		case PIXELFORMAT_RG11B10F: return getPixelRG11B10F;
+		default: return nullptr;
+	}
+}
+
 bool ImageData::getConstant(const char *in, FormatHandler::EncodedFormat &out)
 {
 	return encodedFormats.find(in, out);

+ 26 - 12
src/modules/image/ImageData.h

@@ -26,6 +26,7 @@
 #include "common/int.h"
 #include "common/pixelformat.h"
 #include "common/floattypes.h"
+#include "common/Color.h"
 #include "filesystem/FileData.h"
 #include "thread/threads.h"
 #include "ImageDataBase.h"
@@ -38,16 +39,6 @@ namespace love
 namespace image
 {
 
-union Pixel
-{
-	uint8   rgba8[4];
-	uint16  rgba16[4];
-	float16 rgba16f[4];
-	float   rgba32f[4];
-	uint16  packed16;
-	uint32  packed32;
-};
-
 /**
  * Represents raw pixel data.
  **/
@@ -55,6 +46,19 @@ class ImageData : public ImageDataBase
 {
 public:
 
+	union Pixel
+	{
+		uint8   rgba8[4];
+		uint16  rgba16[4];
+		float16 rgba16f[4];
+		float   rgba32f[4];
+		uint16  packed16;
+		uint32  packed32;
+	};
+
+	typedef void (*PixelSetFunction)(const Colorf &c, Pixel *p);
+	typedef void (*PixelGetFunction)(const Pixel *p, Colorf &c);
+
 	static love::Type type;
 
 	ImageData(Data *data);
@@ -88,7 +92,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, const Pixel &p);
+	void setPixel(int x, int y, const Colorf &p);
 
 	/**
 	 * Gets the pixel at location (x,y).
@@ -96,7 +100,8 @@ public:
 	 * @param y The location along the y-axis.
 	 * @return The color for the given location.
 	 **/
-	void getPixel(int x, int y, Pixel &p) const;
+	void getPixel(int x, int y, Colorf &c) const;
+	Colorf getPixel(int x, int y) const;
 
 	/**
 	 * Encodes raw pixel data into a given format.
@@ -115,9 +120,15 @@ public:
 
 	size_t getPixelSize() const;
 
+	PixelSetFunction getPixelSetFunction() const { return pixelSetFunction; }
+	PixelGetFunction getPixelGetFunction() const { return pixelGetFunction; }
+
 	static bool validPixelFormat(PixelFormat format);
 	static bool canPaste(PixelFormat src, PixelFormat dst);
 
+	static PixelSetFunction getPixelSetFunction(PixelFormat format);
+	static PixelGetFunction getPixelGetFunction(PixelFormat format);
+
 	static bool getConstant(const char *in, FormatHandler::EncodedFormat &out);
 	static bool getConstant(FormatHandler::EncodedFormat in, const char *&out);
 	static std::vector<std::string> getConstants(FormatHandler::EncodedFormat);
@@ -139,6 +150,9 @@ private:
 	// this so we can properly delete memory allocated by the decoder.
 	StrongRef<FormatHandler> decodeHandler;
 
+	PixelSetFunction pixelSetFunction;
+	PixelGetFunction pixelGetFunction;
+
 	static StringMap<FormatHandler::EncodedFormat, FormatHandler::ENCODED_MAX_ENUM>::Entry encodedFormatEntries[];
 	static StringMap<FormatHandler::EncodedFormat, FormatHandler::ENCODED_MAX_ENUM> encodedFormats;
 

+ 53 - 358
src/modules/image/wrap_ImageData.cpp

@@ -88,320 +88,20 @@ int w_ImageData_getDimensions(lua_State *L)
 	return 2;
 }
 
-template <int components>
-static void luax_checkpixel_unorm8(lua_State *L, int startidx, Pixel &p)
-{
-	for (int i = 0; i < std::min(components, 3); i++)
-		p.rgba8[i] = (uint8) ((luax_checknumberclamped01(L, startidx + i) * 255.0) + 0.5);
-	if (components > 3)
-		p.rgba8[3] = (uint8) ((luax_optnumberclamped01(L, startidx + 3, 1.0) * 255.0) + 0.5);
-}
-
-template <int components>
-static void luax_checkpixel_unorm16(lua_State *L, int startidx, Pixel &p)
-{
-	for (int i = 0; i < std::min(components, 3); i++)
-		p.rgba16[i] = (uint16) ((luax_checknumberclamped01(L, startidx + i) * 65535.0) + 0.5);
-	if (components > 3)
-		p.rgba16[3] = (uint16) ((luax_optnumberclamped01(L, startidx + 3, 1.0) * 65535.0) + 0.5);
-}
-
-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] = float32to16((float) luaL_checknumber(L, startidx + i));
-	 if (components > 3)
-		p.rgba16f[3] = float32to16((float) luaL_optnumber(L, startidx + 3, 1.0));
-}
-
-template <int components>
-static void luax_checkpixel_float32(lua_State *L, int startidx, Pixel &p)
-{
-	for (int i = 0; i < std::min(components, 3); i++)
-		p.rgba32f[i] = (float) luaL_checknumber(L, startidx + i);
-	if (components > 3)
-		p.rgba32f[3] = (float) luaL_optnumber(L, startidx + 3, 1.0);
-}
-
-static void luax_checkpixel_r8(lua_State *L, int startidx, Pixel &p)
-{
-	luax_checkpixel_unorm8<1>(L, startidx, p);
-}
-
-static void luax_checkpixel_rg8(lua_State *L, int startidx, Pixel &p)
-{
-	luax_checkpixel_unorm8<2>(L, startidx, p);
-}
-
-static void luax_checkpixel_rgba8(lua_State *L, int startidx, Pixel &p)
-{
-	luax_checkpixel_unorm8<4>(L, startidx, p);
-}
-
-static void luax_checkpixel_r16(lua_State *L, int startidx, Pixel &p)
-{
-	luax_checkpixel_unorm16<1>(L, startidx, p);
-}
-
-static void luax_checkpixel_rg16(lua_State *L, int startidx, Pixel &p)
-{
-	luax_checkpixel_unorm16<2>(L, startidx, p);
-}
-
-static void luax_checkpixel_rgba16(lua_State *L, int startidx, Pixel &p)
-{
-	luax_checkpixel_unorm16<4>(L, startidx, p);
-}
-
-static void luax_checkpixel_r16f(lua_State *L, int startidx, Pixel &p)
-{
-	luax_checkpixel_float16<1>(L, startidx, p);
-}
-
-static void luax_checkpixel_rg16f(lua_State *L, int startidx, Pixel &p)
-{
-	luax_checkpixel_float16<2>(L, startidx, p);
-}
-
-static void luax_checkpixel_rgba16f(lua_State *L, int startidx, Pixel &p)
-{
-	luax_checkpixel_float16<4>(L, startidx, p);
-}
-
-static void luax_checkpixel_r32f(lua_State *L, int startidx, Pixel &p)
-{
-	luax_checkpixel_float32<1>(L, startidx, p);
-}
-
-static void luax_checkpixel_rg32f(lua_State *L, int startidx, Pixel &p)
-{
-	luax_checkpixel_float32<2>(L, startidx, p);
-}
-
-static void luax_checkpixel_rgba32f(lua_State *L, int startidx, Pixel &p)
-{
-	luax_checkpixel_float32<4>(L, startidx, 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);
-	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);
-	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);
-	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);
-	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};
-
-template <int components>
-static int luax_pushpixel_unorm8(lua_State *L, const Pixel &p)
-{
-	for (int i = 0; i < components; i++)
-		lua_pushnumber(L, (lua_Number) p.rgba8[i] / 255.0);
-	for (int i = components; i < 4; i++)
-		lua_pushnumber(L, fillValues[i]);
-	return 4;
-}
-
-template <int components>
-static int luax_pushpixel_unorm16(lua_State *L, const Pixel &p)
-{
-	for (int i = 0; i < components; i++)
-		lua_pushnumber(L, (lua_Number) p.rgba16[i] / 65535.0);
-	for (int i = components; i < 4; i++)
-		lua_pushnumber(L, fillValues[i]);
-	return 4;
-}
-
-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) float16to32(p.rgba16f[i]));
-	for (int i = components; i < 4; i++)
-		lua_pushnumber(L, fillValues[i]);
-	return 4;
-}
-
-template <int components>
-static int luax_pushpixel_float32(lua_State *L, const Pixel &p)
-{
-	for (int i = 0; i < components; i++)
-		lua_pushnumber(L, (lua_Number) p.rgba32f[i]);
-	for (int i = components; i < 4; i++)
-		lua_pushnumber(L, fillValues[i]);
-	return 4;
-}
-
-static int luax_pushpixel_r8(lua_State *L, const Pixel &p)
-{
-	return luax_pushpixel_unorm8<1>(L, p);
-}
-
-static int luax_pushpixel_rg8(lua_State *L, const Pixel &p)
-{
-	return luax_pushpixel_unorm8<2>(L, p);
-}
-
-static int luax_pushpixel_rgba8(lua_State *L, const Pixel &p)
-{
-	return luax_pushpixel_unorm8<4>(L, p);
-}
-
-static int luax_pushpixel_r16(lua_State *L, const Pixel &p)
-{
-	return luax_pushpixel_unorm16<1>(L, p);
-}
-
-static int luax_pushpixel_rg16(lua_State *L, const Pixel &p)
-{
-	return luax_pushpixel_unorm16<2>(L, p);
-}
-
-static int luax_pushpixel_rgba16(lua_State *L, const Pixel &p)
-{
-	return luax_pushpixel_unorm16<4>(L, p);
-}
-
-static int luax_pushpixel_r16f(lua_State *L, const Pixel &p)
-{
-	return luax_pushpixel_float16<1>(L, p);
-}
-
-static int luax_pushpixel_rg16f(lua_State *L, const Pixel &p)
-{
-	return luax_pushpixel_float16<2>(L, p);
-}
-
-static int luax_pushpixel_rgba16f(lua_State *L, const Pixel &p)
-{
-	return luax_pushpixel_float16<4>(L, p);
-}
-
-static int luax_pushpixel_r32f(lua_State *L, const Pixel &p)
-{
-	return luax_pushpixel_float32<1>(L, p);
-}
-
-static int luax_pushpixel_rg32f(lua_State *L, const Pixel &p)
-{
-	return luax_pushpixel_float32<2>(L, p);
-}
-
-static int luax_pushpixel_rgba32f(lua_State *L, const Pixel &p)
-{
-	return luax_pushpixel_float32<4>(L, p);
-}
-
-static int luax_pushpixel_rgba4(lua_State *L, const Pixel &p)
-{
-	// LSB->MSB: [a, b, g, r]
-	lua_pushnumber(L, ((p.packed16 >> 12) & 0xF) / (double)0xF);
-	lua_pushnumber(L, ((p.packed16 >>  8) & 0xF) / (double)0xF);
-	lua_pushnumber(L, ((p.packed16 >>  4) & 0xF) / (double)0xF);
-	lua_pushnumber(L, ((p.packed16 >>  0) & 0xF) / (double)0xF);
-	return 4;
-}
-
-static int luax_pushpixel_rgb5a1(lua_State *L, const Pixel &p)
-{
-	// LSB->MSB: [a, b, g, r]
-	lua_pushnumber(L, ((p.packed16 >> 11) & 0x1F) / (double)0x1F);
-	lua_pushnumber(L, ((p.packed16 >>  6) & 0x1F) / (double)0x1F);
-	lua_pushnumber(L, ((p.packed16 >>  1) & 0x1F) / (double)0x1F);
-	lua_pushnumber(L, ((p.packed16 >>  0) & 0x1)  / (double)0x1);
-	return 4;
-}
-
-static int luax_pushpixel_rgb565(lua_State *L, const Pixel &p)
-{
-	// LSB->MSB: [b, g, r]
-	lua_pushnumber(L, ((p.packed16 >> 11) & 0x1F) / (double)0x1F);
-	lua_pushnumber(L, ((p.packed16 >>  5) & 0x3F) / (double)0x3F);
-	lua_pushnumber(L, ((p.packed16 >>  0) & 0x1F) / (double)0x1F);
-	lua_pushnumber(L, 1.0);
-	return 4;
-}
-
-static int luax_pushpixel_rgb10a2(lua_State *L, const Pixel &p)
-{
-	// LSB->MSB: [r, g, b, a]
-	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;
-}
-
-typedef void(*checkpixel)(lua_State *L, int startidx, Pixel &p);
-typedef int(*pushpixel)(lua_State *L, const Pixel &p);
-
-static checkpixel checkFormats[PIXELFORMAT_MAX_ENUM] = {};
-static pushpixel pushFormats[PIXELFORMAT_MAX_ENUM] = {};
-
 int w_ImageData_getPixel(lua_State *L)
 {
 	ImageData *t = luax_checkimagedata(L, 1);
 	int x = (int) luaL_checkinteger(L, 2);
 	int y = (int) luaL_checkinteger(L, 3);
 
-	PixelFormat format = t->getFormat();
-
-	Pixel p;
-	luax_catchexcept(L, [&](){ t->getPixel(x, y, p); });
+	Colorf c;
+	luax_catchexcept(L, [&](){ t->getPixel(x, y, c); });
 
-	return pushFormats[format](L, p);
+	lua_pushnumber(L, c.r);
+	lua_pushnumber(L, c.g);
+	lua_pushnumber(L, c.b);
+	lua_pushnumber(L, c.a);
+	return 4;
 }
 
 int w_ImageData_setPixel(lua_State *L)
@@ -410,23 +110,37 @@ int w_ImageData_setPixel(lua_State *L)
 	int x = (int) luaL_checkinteger(L, 2);
 	int y = (int) luaL_checkinteger(L, 3);
 
-	PixelFormat format = t->getFormat();
+	int components = getPixelFormatColorComponents(t->getFormat());
 
-	Pixel p;
+	Colorf c;
 
 	if (lua_istable(L, 4))
 	{
-		for (int i = 1; i <= 4; i++)
-			lua_rawgeti(L, 4, i);
-
-		checkFormats[format](L, -4, p);
-
-		lua_pop(L, 4);
+		for (int i = 1; i <= components; i++)
+			lua_rawgeti(L, components, i);
+
+		c.r = (float) luaL_checknumber(L, -components);
+		if (components > 1)
+			c.g = (float) luaL_checknumber(L, (-components) + 1);
+		if (components > 2)
+			c.b = (float) luaL_checknumber(L, (-components) + 2);
+		if (components > 3)
+			c.a = (float) luaL_optnumber(L, (-components) + 3, 1.0);
+
+		lua_pop(L, components);
 	}
 	else
-		checkFormats[format](L, 4, p);
+	{
+		c.r = (float) luaL_checknumber(L, 4);
+		if (components > 1)
+			c.g = (float) luaL_checknumber(L, 5);
+		if (components > 2)
+			c.b = (float) luaL_checknumber(L, 6);
+		if (components > 3)
+			c.a = (float) luaL_optnumber(L, 7, 1.0);
+	}
 
-	luax_catchexcept(L, [&](){ t->setPixel(x, y, p); });
+	luax_catchexcept(L, [&](){ t->setPixel(x, y, c); });
 	return 0;
 }
 
@@ -449,9 +163,10 @@ int w_ImageData__mapPixelUnsafe(lua_State *L)
 	int iw = t->getWidth();
 
 	PixelFormat format = t->getFormat();
+	int components = getPixelFormatColorComponents(format);
 
-	auto checkpixel = checkFormats[format];
-	auto pushpixel = pushFormats[format];
+	auto pixelsetfunction = t->getPixelSetFunction();
+	auto pixelgetfunction = t->getPixelGetFunction();
 
 	uint8 *data = (uint8 *) t->getData();
 	size_t pixelsize = t->getPixelSize();
@@ -460,17 +175,33 @@ int w_ImageData__mapPixelUnsafe(lua_State *L)
 	{
 		for (int x = sx; x < sx+w; x++)
 		{
-			Pixel *pixeldata = (Pixel *) (data + (y * iw + x) * pixelsize);
+			auto pixeldata = (ImageData::Pixel *) (data + (y * iw + x) * pixelsize);
+
+			Colorf c;
+			pixelgetfunction(pixeldata, c);
 
 			lua_pushvalue(L, 2); // ImageData
+
 			lua_pushnumber(L, x);
 			lua_pushnumber(L, y);
 
-			pushpixel(L, *pixeldata);
+			lua_pushnumber(L, c.r);
+			lua_pushnumber(L, c.g);
+			lua_pushnumber(L, c.b);
+			lua_pushnumber(L, c.a);
 
 			lua_call(L, 6, 4);
 
-			checkpixel(L, -4, *pixeldata);
+			c.r = (float) luaL_checknumber(L, -4);
+			if (components > 1)
+				c.g = (float) luaL_checknumber(L, -3);
+			if (components > 2)
+				c.b = (float) luaL_checknumber(L, -2);
+			if (components > 3)
+				c.a = (float) luaL_optnumber(L, -1, 1.0);
+
+			pixelsetfunction(c, pixeldata);
+
 			lua_pop(L, 4); // Pop return values.
 		}
 	}
@@ -600,42 +331,6 @@ 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;
-	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);
 
 	love::data::luax_rundatawrapper(L, ImageData::type);