diff --git a/src/modules/image/ImageData.cpp b/src/modules/image/ImageData.cpp index 791696bc8..d67576fbd 100644 --- a/src/modules/image/ImageData.cpp +++ b/src/modules/image/ImageData.cpp @@ -463,7 +463,7 @@ void ImageData::paste(ImageData *src, int dx, int dy, int sx, int sy, int sw, in pasteRGBA32FtoRGBA16F(rowsrc, rowdst, sw); else - throw love::Exception("Unsupported pixel format combination in ImageData:paste!"); + throw love::Exception("Unsupported pixel format combination in ImageData:paste."); } } } @@ -494,12 +494,32 @@ bool ImageData::validPixelFormat(PixelFormat format) case PIXELFORMAT_R32F: case PIXELFORMAT_RG32F: case PIXELFORMAT_RGBA32F: + case PIXELFORMAT_RGBA4: + case PIXELFORMAT_RGB5A1: + case PIXELFORMAT_RGB565: + case PIXELFORMAT_RGB10A2: return true; default: return false; } } +bool ImageData::canPaste(PixelFormat src, PixelFormat dst) +{ + if (src == dst) + return true; + + if (!(src == PIXELFORMAT_RGBA8 || src == PIXELFORMAT_RGBA16 + || src == PIXELFORMAT_RGBA16F || src == PIXELFORMAT_RGBA32F)) + return false; + + if (!(dst == PIXELFORMAT_RGBA8 || dst == PIXELFORMAT_RGBA16 + || dst == PIXELFORMAT_RGBA16F || dst == PIXELFORMAT_RGBA32F)) + return false; + + return true; +} + bool ImageData::getConstant(const char *in, FormatHandler::EncodedFormat &out) { return encodedFormats.find(in, out); diff --git a/src/modules/image/ImageData.h b/src/modules/image/ImageData.h index a7bc9042b..b6d99644e 100644 --- a/src/modules/image/ImageData.h +++ b/src/modules/image/ImageData.h @@ -44,6 +44,8 @@ union Pixel uint16 rgba16[4]; half rgba16f[4]; float rgba32f[4]; + uint16 packed16; + uint32 packed32; }; /** @@ -114,6 +116,7 @@ public: size_t getPixelSize() const; static bool validPixelFormat(PixelFormat format); + static bool canPaste(PixelFormat src, PixelFormat dst); static bool getConstant(const char *in, FormatHandler::EncodedFormat &out); static bool getConstant(FormatHandler::EncodedFormat in, const char *&out); diff --git a/src/modules/image/wrap_ImageData.cpp b/src/modules/image/wrap_ImageData.cpp index 029af308e..c17e0e38a 100644 --- a/src/modules/image/wrap_ImageData.cpp +++ b/src/modules/image/wrap_ImageData.cpp @@ -92,18 +92,18 @@ template 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); + 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); + p.rgba8[3] = (uint8) ((luax_optnumberclamped01(L, startidx + 3, 1.0) * 255.0) + 0.5); } template 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); + 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); + p.rgba16[3] = (uint16) ((luax_optnumberclamped01(L, startidx + 3, 1.0) * 65535.0) + 0.5); } template @@ -184,6 +184,45 @@ 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)) << 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; +} + +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; +} + +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; +} + +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; +} + static lua_Number fillValues[] = {0.0, 0.0, 0.0, 1.0}; template @@ -286,6 +325,46 @@ 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.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); + return 4; +} + typedef void(*checkpixel)(lua_State *L, int startidx, Pixel &p); typedef int(*pushpixel)(lua_State *L, const Pixel &p); @@ -504,6 +583,10 @@ extern "C" int luaopen_imagedata(lua_State *L) 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; @@ -517,6 +600,10 @@ extern "C" int luaopen_imagedata(lua_State *L) 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; int ret = luax_register_type(L, &ImageData::type, data::w_Data_functions, w_ImageData_functions, nullptr); diff --git a/src/modules/image/wrap_ImageData.lua b/src/modules/image/wrap_ImageData.lua index 5b8ac0b2c..07f72cba9 100644 --- a/src/modules/image/wrap_ImageData.lua +++ b/src/modules/image/wrap_ImageData.lua @@ -73,6 +73,9 @@ end local status, ffi = pcall(require, "ffi") if not status then return end +local bitstatus, bit = pcall(require, "bit") +if not bitstatus then return end + pcall(ffi.cdef, [[ typedef struct Proxy Proxy; typedef uint16_t half; @@ -101,6 +104,11 @@ struct ImageData_Pixel_RGBA16F { half r, g, b, a; }; struct ImageData_Pixel_R32F { float r; }; struct ImageData_Pixel_RG32F { float r, g; }; struct ImageData_Pixel_RGBA32F { float r, g, b, a; }; + +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; }; ]]) local ffifuncs = ffi.cast("FFI_ImageData **", ffifuncspointer_str)[0] @@ -112,7 +120,7 @@ local conversions = { return tonumber(self.r) / 255, 0, 0, 1 end, fromlua = function(self, r) - self.r = clamp01(r) * 255 + self.r = (clamp01(r) * 255) + 0.5 end, }, rg8 = { @@ -121,8 +129,8 @@ local conversions = { return tonumber(self.r) / 255, tonumber(self.g) / 255, 0, 1 end, fromlua = function(self, r, g) - self.r = clamp01(r) * 255 - self.g = clamp01(g) * 255 + self.r = (clamp01(r) * 255) + 0.5 + self.g = (clamp01(g) * 255) + 0.5 end, }, rgba8 = { @@ -131,10 +139,10 @@ local conversions = { 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 = clamp01(r) * 255 - self.g = clamp01(g) * 255 - self.b = clamp01(b) * 255 - self.a = a == nil and 255 or clamp01(a) * 255 + self.r = (clamp01(r) * 255) + 0.5 + self.g = (clamp01(g) * 255) + 0.5 + self.b = (clamp01(b) * 255) + 0.5 + self.a = a == nil and 255 or (clamp01(a) * 255) + 0.5 end, }, r16 = { @@ -143,7 +151,7 @@ local conversions = { return tonumber(self.r) / 65535, 0, 0, 1 end, fromlua = function(self, r) - self.r = clamp01(r) * 65535 + self.r = (clamp01(r) * 65535) + 0.5 end, }, rg16 = { @@ -152,8 +160,8 @@ local conversions = { return tonumber(self.r) / 65535, tonumber(self.g) / 65535, 0, 1 end, fromlua = function(self, r, g) - self.r = clamp01(r) * 65535 - self.g = clamp01(g) * 65535 + self.r = (clamp01(r) * 65535) + 0.5 + self.g = (clamp01(g) * 65535) + 0.5 end, }, rgba16 = { @@ -162,10 +170,10 @@ local conversions = { 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 = clamp01(r) * 65535 - self.g = clamp01(g) * 65535 - self.b = clamp01(b) * 65535 - self.a = a == nil and 65535 or clamp01(a) * 65535 + self.r = (clamp01(r) * 65535) + 0.5 + self.g = (clamp01(g) * 65535) + 0.5 + self.b = (clamp01(b) * 65535) + 0.5 + self.a = a == nil and 65535 or (clamp01(a) * 65535) + 0.5 end, }, r16f = { @@ -233,6 +241,84 @@ local conversions = { self.a = a == nil and 1.0 or a end, }, + rgba4 = { + -- LSB->MSB: [a, b, g, r] + pointer = ffi.typeof("struct ImageData_Pixel_RGBA4 *"), + tolua = function(self) + local rgba = self.rgba + local a = tonumber(bit.band(rgba, 0xF)) / 0xF + local b = tonumber(bit.band(bit.rshift(rgba, 4), 0xF)) / 0xF + local g = tonumber(bit.band(bit.rshift(rgba, 8), 0xF)) / 0xF + local r = tonumber(bit.rshift(rgba, 12)) / 0xF + return r, g, b, a + end, + fromlua = function(self, r, g, b, a) + -- bit functions round internally. + r = clamp01(r) * 0xF + g = clamp01(g) * 0xF + b = clamp01(b) * 0xF + a = a == nil and 0xF or clamp01(a) * 0xF + self.rgba = bit.bor(a, bit.lshift(b, 4), bit.lshift(g, 8), bit.lshift(r, 12)) + end, + }, + rgb5a1 = { + -- LSB->MSB: [a, b, g, r] + pointer = ffi.typeof("struct ImageData_Pixel_RGB5A1 *"), + tolua = function(self) + local rgba = self.rgba + local r = tonumber(bit.band(bit.rshift(rgba, 11), 0x1F)) / 0x1F + local g = tonumber(bit.band(bit.rshift(rgba, 6), 0x1F)) / 0x1F + local b = tonumber(bit.band(bit.rshift(rgba, 1), 0x1F)) / 0x1F + local a = tonumber(bit.band(rgba, 0x1)) + return r, g, b, a + end, + fromlua = function(self, r, g, b, a) + -- bit functions round internally. + r = clamp01(r) * 0x1F + g = clamp01(g) * 0x1F + b = clamp01(b) * 0x1F + a = a == nil and 1 or clamp01(a) + self.rgba = bit.bor(bit.lshift(r, 11), bit.lshift(g, 6), bit.lshift(b, 1), a) + end, + }, + rgb565 = { + -- LSB->MSB: [b, g, r] + pointer = ffi.typeof("struct ImageData_Pixel_RGB565 *"), + tolua = function(self) + local rgb = self.rgb + local r = bit.band(bit.rshift(rgb, 11), 0x1F) / 0x1F + local g = bit.band(bit.rshift(rgb, 5), 0x3F) / 0x3F + local b = bit.band(rgb, 0x1F) / 0x1F + return r, g, b, 1 + end, + fromlua = function(self, r, g, b) + -- bit functions round internally. + r = clamp01(r) * 0x1F + g = clamp01(g) * 0x3F + b = clamp01(b) * 0x1F + self.rgb = bit.bor(bit.lshift(r, 11), bit.lshift(g, 5), b) + end, + }, + rgb10a2 = { + -- LSB->MSB: [r, g, b, a] + pointer = ffi.typeof("struct ImageData_Pixel_RGB10A2 *"), + tolua = function(self) + local rgba = self.rgba + local r = tonumber(bit.band(rgba, 0x3FF)) / 0x3FF + local g = tonumber(bit.band(bit.rshift(rgba, 10), 0x3FF)) / 0x3FF + local b = tonumber(bit.band(bit.rshift(rgba, 20), 0x3FF)) / 0x3FF + local a = tonumber(bit.rshift(rgba, 30)) / 0x3 + return r, g, b, a + end, + fromlua = function(self, r, g, b, a) + -- bit functions round internally. + r = clamp01(r) * 0x3FF + g = clamp01(g) * 0x3FF + b = clamp01(b) * 0x3FF + a = a == nil and 0x3 or clamp01(a) * 0x3 + self.rgba = bit.bor(r, bit.lshift(g, 10), bit.lshift(b, 20), bit.lshift(a, 30)) + end, + }, } local _getWidth = ImageData.getWidth