From 48d94720c10ada906ed10545b70fc3012856a150 Mon Sep 17 00:00:00 2001 From: Nils Dagsson Moskopp Date: Tue, 17 Oct 2023 12:33:29 +0200 Subject: [PATCH 01/15] Add features table --- init.lua | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/init.lua b/init.lua index 1a0bd47..dbf1d1d 100644 --- a/init.lua +++ b/init.lua @@ -1,5 +1,23 @@ tga_encoder = {} +tga_encoder.features = {} +tga_encoder.features.color_format = { + "A1R5G5B5", + "B8G8R8", + "B8G8R8A8", + "Y8", +} +tga_encoder.features.colormap = { +} +tga_encoder.features.compression = { + "RAW", + "RLE", +} +tga_encoder.features.scanline_order = { + "bottom-top", + "top-bottom", +} + local image = setmetatable({}, { __call = function(self, ...) local t = setmetatable({}, {__index = self}) -- 2.40.1 From 6db7be7b9d110e5ac2e80fcff21f3c8517ee7078 Mon Sep 17 00:00:00 2001 From: Nils Dagsson Moskopp Date: Tue, 17 Oct 2023 19:16:09 +0200 Subject: [PATCH 02/15] Generate TGA type 1 test textures using loops --- tools/generate_test_textures.lua | 76 ++++++++++++++++++++------------ 1 file changed, 48 insertions(+), 28 deletions(-) diff --git a/tools/generate_test_textures.lua b/tools/generate_test_textures.lua index 24e4b32..f75e068 100755 --- a/tools/generate_test_textures.lua +++ b/tools/generate_test_textures.lua @@ -28,46 +28,66 @@ local pixels_colormapped_tb = { { _, _, _, _, _, B, _, B, }, } -image_colormapped_bt = tga_encoder.image(pixels_colormapped_bt) -image_colormapped_tb = tga_encoder.image(pixels_colormapped_tb) +local pixels_by_scanline_order = { + ["bottom-top"] = pixels_colormapped_bt, + ["top-bottom"] = pixels_colormapped_tb, +} -colormap_32bpp = { +local colormap_32bpp = { { 0, 0, 0, 128 }, { 255, 0, 0, 255 }, { 0, 255, 0, 255 }, { 0, 0, 255, 255 }, } -image_colormapped_bt:save( - "type1_32bpp_bt.tga", - { colormap = colormap_32bpp, color_format = "B8G8R8A8", scanline_order = "bottom-top" } -) -image_colormapped_tb:save( - "type1_32bpp_tb.tga", - { colormap = colormap_32bpp, color_format = "B8G8R8A8", scanline_order = "top-bottom" } -) -image_colormapped_bt:save( - "type1_16bpp_bt.tga", - { colormap = colormap_32bpp, color_format = "A1R5G5B5", scanline_order = "bottom-top" } -) -image_colormapped_tb:save( - "type1_16bpp_tb.tga", - { colormap = colormap_32bpp, color_format = "A1R5G5B5", scanline_order = "top-bottom" } -) -colormap_24bpp = { +local colormap_24bpp = { { 0, 0, 0 }, { 255, 0, 0 }, { 0, 255, 0 }, { 0, 0, 255 }, } -image_colormapped_bt:save( - "type1_24bpp_bt.tga", - { colormap = colormap_32bpp, color_format = "B8G8R8", scanline_order = "bottom-top" } -) -image_colormapped_tb:save( - "type1_24bpp_tb.tga", - { colormap = colormap_32bpp, color_format = "B8G8R8", scanline_order = "top-bottom" } -) + +local colormap_16bpp = { + { 0, 0, 0, 0 }, + { 255, 0, 0, 255 }, + { 0, 255, 0, 255 }, + { 0, 0, 255, 255 }, +} + +local colormap_by_color_format = { + ["A1R5G5B5"] = colormap_16bpp, + ["B8G8R8"] = colormap_24bpp, + ["B8G8R8A8"] = colormap_32bpp, +} + +for _, color_format in ipairs( + tga_encoder.features.color_format +) do + -- a grayscale colormap makes little sense + if ("Y8" ~= color_format) then + for _, scanline_order in ipairs( + tga_encoder.features.scanline_order + ) do + local filename = "type1" .. + '_' .. color_format .. + '_' .. scanline_order .. + '.tga' + print(filename) + local colormap = colormap_by_color_format[ + color_format + ] + local properties = { + colormap = colormap, + color_format = color_format, + scanline_order = scanline_order, + } + local image = tga_encoder.image( + pixels_by_scanline_order[scanline_order] + ) + image:save(filename, properties) + end + end +end local _ = { 0, 0, 0, 128 } local R = { 255, 0, 0, 255 } -- 2.40.1 From fa28f4c8ded6e3ea0c01990af47ff9d4713809c1 Mon Sep 17 00:00:00 2001 From: Nils Dagsson Moskopp Date: Tue, 17 Oct 2023 20:12:23 +0200 Subject: [PATCH 03/15] Generate TGA type 3 test textures using loops --- tools/generate_test_textures.lua | 84 ++++++++++++++++++++++++-------- 1 file changed, 63 insertions(+), 21 deletions(-) diff --git a/tools/generate_test_textures.lua b/tools/generate_test_textures.lua index f75e068..e442769 100755 --- a/tools/generate_test_textures.lua +++ b/tools/generate_test_textures.lua @@ -28,11 +28,43 @@ local pixels_colormapped_tb = { { _, _, _, _, _, B, _, B, }, } -local pixels_by_scanline_order = { +local pixels_colormapped_by_scanline_order = { ["bottom-top"] = pixels_colormapped_bt, ["top-bottom"] = pixels_colormapped_tb, } +local _ = { 0 } +local R = { 127 } +local G = { 255 } +local B = { 191 } + +local pixels_grayscale_bt = { + { _, _, _, _, _, B, _, B, }, + { _, _, _, _, _, B, B, B, }, + { _, _, G, G, G, B, _, B, }, + { _, _, G, _, G, B, B, B, }, + { _, R, G, _, _, _, _, _, }, + { _, R, G, G, G, _, _, _, }, + { _, R, _, _, _, _, _, _, }, + { R, R, R, _, _, _, _, _, }, +} + +local pixels_grayscale_tb = { + { R, R, R, _, _, _, _, _, }, + { _, R, _, _, _, _, _, _, }, + { _, R, G, G, G, _, _, _, }, + { _, R, G, _, _, _, _, _, }, + { _, _, G, _, G, B, B, B, }, + { _, _, G, G, G, B, _, B, }, + { _, _, _, _, _, B, B, B, }, + { _, _, _, _, _, B, _, B, }, +} + +local pixels_grayscale_by_scanline_order = { + ["bottom-top"] = pixels_grayscale_bt, + ["top-bottom"] = pixels_grayscale_tb, +} + local colormap_32bpp = { { 0, 0, 0, 128 }, { 255, 0, 0, 255 }, @@ -63,29 +95,39 @@ local colormap_by_color_format = { for _, color_format in ipairs( tga_encoder.features.color_format ) do - -- a grayscale colormap makes little sense - if ("Y8" ~= color_format) then - for _, scanline_order in ipairs( - tga_encoder.features.scanline_order - ) do - local filename = "type1" .. + for _, scanline_order in ipairs( + tga_encoder.features.scanline_order + ) do + local filename + local pixels + if ("Y8" == color_format) then + filename = "type3" .. '_' .. color_format .. - '_' .. scanline_order .. - '.tga' - print(filename) - local colormap = colormap_by_color_format[ - color_format + '_' .. scanline_order .. + '.tga' + pixels = pixels_grayscale_by_scanline_order[ + scanline_order + ] + else + filename = "type1" .. + '_' .. color_format .. + '_' .. scanline_order .. + '.tga' + pixels = pixels_colormapped_by_scanline_order[ + scanline_order ] - local properties = { - colormap = colormap, - color_format = color_format, - scanline_order = scanline_order, - } - local image = tga_encoder.image( - pixels_by_scanline_order[scanline_order] - ) - image:save(filename, properties) end + local colormap = colormap_by_color_format[ + color_format + ] + local properties = { + colormap = colormap, + color_format = color_format, + scanline_order = scanline_order, + } + print(filename) + local image = tga_encoder.image(pixels) + image:save(filename, properties) end end -- 2.40.1 From 2e0471c81f649035eb119a40433beff9e5d175e2 Mon Sep 17 00:00:00 2001 From: Nils Dagsson Moskopp Date: Wed, 18 Oct 2023 01:23:08 +0200 Subject: [PATCH 04/15] Encode alpha channel for uncompressed A1R5G5B5 images --- init.lua | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/init.lua b/init.lua index dbf1d1d..18699f4 100644 --- a/init.lua +++ b/init.lua @@ -225,7 +225,11 @@ function image:encode_data(properties) end else if "RAW" == compression then - self:encode_data_R8G8B8_as_A1R5G5B5_raw() + if 24 == self.pixel_depth then + self:encode_data_R8G8B8_as_A1R5G5B5_raw() + elseif 32 == self.pixel_depth then + self:encode_data_R8G8B8A8_as_A1R5G5B5_raw() + end elseif "RLE" == compression then self:encode_data_R8G8B8_as_A1R5G5B5_rle() end @@ -320,6 +324,27 @@ function image:encode_data_R8G8B8_as_A1R5G5B5_raw() self.data = self.data .. table.concat(raw_pixels) end +function image:encode_data_R8G8B8A8_as_A1R5G5B5_raw() + assert(32 == self.pixel_depth) + local raw_pixels = {} + -- Sample depth rescaling is done according to the algorithm presented in: + -- + local max_sample_in = math.pow(2, 8) - 1 + local max_sample_out = math.pow(2, 5) - 1 + for _, row in ipairs(self.pixels) do + for _, pixel in ipairs(row) do + local colorword = 0 + + ((math.floor((pixel[4] / max_sample_in) + 0.5)) * 32768) + -- transparent if alpha < 128 + ((math.floor((pixel[1] * max_sample_out / max_sample_in) + 0.5)) * 1024) + + ((math.floor((pixel[2] * max_sample_out / max_sample_in) + 0.5)) * 32) + + ((math.floor((pixel[3] * max_sample_out / max_sample_in) + 0.5)) * 1) + local raw_pixel = string.char(colorword % 256, math.floor(colorword / 256)) + raw_pixels[#raw_pixels + 1] = raw_pixel + end + end + self.data = self.data .. table.concat(raw_pixels) +end + function image:encode_data_R8G8B8_as_A1R5G5B5_rle() assert(24 == self.pixel_depth) local colorword = nil -- 2.40.1 From 619e49d3b795870e500f05865ca5802929835d3b Mon Sep 17 00:00:00 2001 From: Nils Dagsson Moskopp Date: Wed, 18 Oct 2023 01:40:56 +0200 Subject: [PATCH 05/15] Encode alpha channel for compressed A1R5G5B5 images --- init.lua | 100 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 99 insertions(+), 1 deletion(-) diff --git a/init.lua b/init.lua index 18699f4..379cc8b 100644 --- a/init.lua +++ b/init.lua @@ -231,7 +231,11 @@ function image:encode_data(properties) self:encode_data_R8G8B8A8_as_A1R5G5B5_raw() end elseif "RLE" == compression then - self:encode_data_R8G8B8_as_A1R5G5B5_rle() + if 24 == self.pixel_depth then + self:encode_data_R8G8B8_as_A1R5G5B5_rle() + elseif 32 == self.pixel_depth then + self:encode_data_R8G8B8A8_as_A1R5G5B5_rle() + end end end elseif "B8G8R8" == color_format then @@ -435,6 +439,100 @@ function image:encode_data_R8G8B8_as_A1R5G5B5_rle() self.data = self.data .. table.concat(packets) end +function image:encode_data_R8G8B8A8_as_A1R5G5B5_rle() + assert(32 == self.pixel_depth) + local colorword = nil + local previous_r = nil + local previous_g = nil + local previous_b = nil + local previous_a = nil + local raw_pixel = '' + local raw_pixels = {} + local count = 1 + local packets = {} + local raw_packet = '' + local rle_packet = '' + -- Sample depth rescaling is done according to the algorithm presented in: + -- + local max_sample_in = math.pow(2, 8) - 1 + local max_sample_out = math.pow(2, 5) - 1 + for _, row in ipairs(self.pixels) do + for _, pixel in ipairs(row) do + if pixel[1] ~= previous_r or pixel[2] ~= previous_g or pixel[3] ~= previous_b or pixel[4] ~= previous_a or count == 128 then + if nil ~= previous_r then + colorword = 0 + + ((math.floor((previous_a / max_sample_in) + 0.5)) * 32768) + -- transparent if alpha < 128 + ((math.floor((previous_r * max_sample_out / max_sample_in) + 0.5)) * 1024) + + ((math.floor((previous_g * max_sample_out / max_sample_in) + 0.5)) * 32) + + ((math.floor((previous_b * max_sample_out / max_sample_in) + 0.5)) * 1) + if 1 == count then + -- remember pixel verbatim for raw encoding + raw_pixel = string.char(colorword % 256, math.floor(colorword / 256)) + raw_pixels[#raw_pixels + 1] = raw_pixel + if 128 == #raw_pixels then + raw_packet = string.char(#raw_pixels - 1) + packets[#packets + 1] = raw_packet + for i=1, #raw_pixels do + packets[#packets +1] = raw_pixels[i] + end + raw_pixels = {} + end + else + -- encode raw pixels, if any + if #raw_pixels > 0 then + raw_packet = string.char(#raw_pixels - 1) + packets[#packets + 1] = raw_packet + for i=1, #raw_pixels do + packets[#packets +1] = raw_pixels[i] + end + raw_pixels = {} + end + -- RLE encoding + rle_packet = string.char(128 + count - 1, colorword % 256, math.floor(colorword / 256)) + packets[#packets +1] = rle_packet + end + end + count = 1 + previous_r = pixel[1] + previous_g = pixel[2] + previous_b = pixel[3] + previous_a = pixel[4] + else + count = count + 1 + end + end + end + colorword = 0 + + ((math.floor((previous_a / max_sample_in) + 0.5)) * 32768) + -- transparent if alpha < 128 + ((math.floor((previous_r * max_sample_out / max_sample_in) + 0.5)) * 1024) + + ((math.floor((previous_g * max_sample_out / max_sample_in) + 0.5)) * 32) + + ((math.floor((previous_b * max_sample_out / max_sample_in) + 0.5)) * 1) + if 1 == count then + raw_pixel = string.char(colorword % 256, math.floor(colorword / 256)) + raw_pixels[#raw_pixels + 1] = raw_pixel + raw_packet = string.char(#raw_pixels - 1) + packets[#packets + 1] = raw_packet + for i=1, #raw_pixels do + packets[#packets +1] = raw_pixels[i] + end + raw_pixels = {} + else + -- encode raw pixels, if any + if #raw_pixels > 0 then + raw_packet = string.char(#raw_pixels - 1) + packets[#packets + 1] = raw_packet + for i=1, #raw_pixels do + packets[#packets +1] = raw_pixels[i] + end + raw_pixels = {} + end + -- RLE encoding + rle_packet = string.char(128 + count - 1, colorword % 256, math.floor(colorword / 256)) + packets[#packets +1] = rle_packet + end + self.data = self.data .. table.concat(packets) +end + function image:encode_data_R8G8B8_as_B8G8R8_raw() assert(24 == self.pixel_depth) local raw_pixels = {} -- 2.40.1 From 64d20ccab8a0c80150681d0a1e37b90375796434 Mon Sep 17 00:00:00 2001 From: Nils Dagsson Moskopp Date: Wed, 18 Oct 2023 00:27:04 +0200 Subject: [PATCH 06/15] Generate TGA type 2 and type 10 test textures using loops --- tools/generate_test_textures.lua | 110 +++++++++++++++---------------- 1 file changed, 55 insertions(+), 55 deletions(-) diff --git a/tools/generate_test_textures.lua b/tools/generate_test_textures.lua index e442769..54fb36f 100755 --- a/tools/generate_test_textures.lua +++ b/tools/generate_test_textures.lua @@ -66,7 +66,7 @@ local pixels_grayscale_by_scanline_order = { } local colormap_32bpp = { - { 0, 0, 0, 128 }, + { 0, 0, 0, 127 }, { 255, 0, 0, 255 }, { 0, 255, 0, 255 }, { 0, 0, 255, 255 }, @@ -131,7 +131,7 @@ for _, color_format in ipairs( end end -local _ = { 0, 0, 0, 128 } +local _ = { 0, 0, 0, 127 } local R = { 255, 0, 0, 255 } local G = { 0, 255, 0, 255 } local B = { 0, 0, 255, 255 } @@ -158,25 +158,10 @@ local pixels_rgba_tb = { { _, _, _, _, _, B, _, B, }, } -image_rgba_bt = tga_encoder.image(pixels_rgba_bt) -image_rgba_tb = tga_encoder.image(pixels_rgba_tb) - -image_rgba_bt:save( - "type2_32bpp_bt.tga", - { color_format="B8G8R8A8", compression="RAW", scanline_order = "bottom-top" } -) -image_rgba_tb:save( - "type2_32bpp_tb.tga", - { color_format="B8G8R8A8", compression="RAW", scanline_order = "top-bottom" } -) -image_rgba_bt:save( - "type10_32bpp_bt.tga", - { color_format="B8G8R8A8", compression="RLE", scanline_order = "bottom-top" } -) -image_rgba_tb:save( - "type10_32bpp_tb.tga", - { color_format="B8G8R8A8", compression="RLE", scanline_order = "top-bottom" } -) +local pixels_rgba_by_scanline_order = { + ["bottom-top"] = pixels_rgba_bt, + ["top-bottom"] = pixels_rgba_tb, +} local _ = { 0, 0, 0 } local R = { 255, 0, 0 } @@ -205,38 +190,53 @@ local pixels_rgb_tb = { { _, _, _, _, _, B, _, B, }, } -image_rgb_bt = tga_encoder.image(pixels_rgb_bt) -image_rgb_tb = tga_encoder.image(pixels_rgb_tb) +local pixels_rgb_by_scanline_order = { + ["bottom-top"] = pixels_rgb_bt, + ["top-bottom"] = pixels_rgb_tb, +} -image_rgb_bt:save( - "type2_24bpp_bt.tga", - { color_format="B8G8R8", compression="RAW", scanline_order = "bottom-top" } -) -image_rgb_tb:save( - "type2_24bpp_tb.tga", - { color_format="B8G8R8", compression="RAW", scanline_order = "top-bottom" } -) -image_rgb_bt:save( - "type10_24bpp_bt.tga", - { color_format="B8G8R8", compression="RLE", scanline_order = "bottom-top" } -) -image_rgb_tb:save( - "type10_24bpp_tb.tga", - { color_format="B8G8R8", compression="RLE", scanline_order = "top-bottom" } -) -image_rgb_bt:save( - "type2_16bpp_bt.tga", - { color_format="A1R5G5B5", compression="RAW", scanline_order = "bottom-top" } -) -image_rgb_tb:save( - "type2_16bpp_tb.tga", - { color_format="A1R5G5B5", compression="RAW", scanline_order = "top-bottom" } -) -image_rgb_bt:save( - "type10_16bpp_bt.tga", - { color_format="A1R5G5B5", compression="RLE", scanline_order = "bottom-top" } -) -image_rgb_tb:save( - "type10_16bpp_tb.tga", - { color_format="A1R5G5B5", compression="RLE", scanline_order = "top-bottom" } -) +local tga_type_by_compression = { + ["RAW"] = "2", + ["RLE"] = "10", +} + +local pixels_by_scanline_order_by_color_format = { + ["A1R5G5B5"] = pixels_rgba_by_scanline_order, + ["B8G8R8"] = pixels_rgb_by_scanline_order, + ["B8G8R8A8"] = pixels_rgba_by_scanline_order, +} + +for _, color_format in ipairs( + tga_encoder.features.color_format +) do + for _, compression in ipairs( + tga_encoder.features.compression + ) do + for _, scanline_order in ipairs( + tga_encoder.features.scanline_order + ) do + if ("Y8" ~= color_format) then + local tga_type = tga_type_by_compression[ + compression + ] + local filename = "type" .. tga_type .. + '_' .. color_format .. + '_' .. scanline_order .. + '.tga' + local pixels = pixels_by_scanline_order_by_color_format[ + color_format + ][ + scanline_order + ] + local properties = { + color_format = color_format, + compression = compression, + scanline_order = scanline_order, + } + print(filename) + local image = tga_encoder.image(pixels) + image:save(filename, properties) + end + end + end +end -- 2.40.1 From 2b4faee266fabad370ae2367d73e070ab21e9823 Mon Sep 17 00:00:00 2001 From: Nils Dagsson Moskopp Date: Wed, 18 Oct 2023 05:02:41 +0200 Subject: [PATCH 07/15] Refactor encoding of compressed A1R5G5B5 images --- init.lua | 87 +++++--------------------------------------------------- 1 file changed, 7 insertions(+), 80 deletions(-) diff --git a/init.lua b/init.lua index 379cc8b..adc5a50 100644 --- a/init.lua +++ b/init.lua @@ -351,92 +351,19 @@ end function image:encode_data_R8G8B8_as_A1R5G5B5_rle() assert(24 == self.pixel_depth) - local colorword = nil - local previous_r = nil - local previous_g = nil - local previous_b = nil - local raw_pixel = '' - local raw_pixels = {} - local count = 1 - local packets = {} - local raw_packet = '' - local rle_packet = '' - -- Sample depth rescaling is done according to the algorithm presented in: - -- - local max_sample_in = math.pow(2, 8) - 1 - local max_sample_out = math.pow(2, 5) - 1 for _, row in ipairs(self.pixels) do for _, pixel in ipairs(row) do - if pixel[1] ~= previous_r or pixel[2] ~= previous_g or pixel[3] ~= previous_b or count == 128 then - if nil ~= previous_r then - colorword = 32768 + - ((math.floor((previous_r * max_sample_out / max_sample_in) + 0.5)) * 1024) + - ((math.floor((previous_g * max_sample_out / max_sample_in) + 0.5)) * 32) + - ((math.floor((previous_b * max_sample_out / max_sample_in) + 0.5)) * 1) - if 1 == count then - -- remember pixel verbatim for raw encoding - raw_pixel = string.char(colorword % 256, math.floor(colorword / 256)) - raw_pixels[#raw_pixels + 1] = raw_pixel - if 128 == #raw_pixels then - raw_packet = string.char(#raw_pixels - 1) - packets[#packets + 1] = raw_packet - for i=1, #raw_pixels do - packets[#packets +1] = raw_pixels[i] - end - raw_pixels = {} - end - else - -- encode raw pixels, if any - if #raw_pixels > 0 then - raw_packet = string.char(#raw_pixels - 1) - packets[#packets + 1] = raw_packet - for i=1, #raw_pixels do - packets[#packets +1] = raw_pixels[i] - end - raw_pixels = {} - end - -- RLE encoding - rle_packet = string.char(128 + count - 1, colorword % 256, math.floor(colorword / 256)) - packets[#packets +1] = rle_packet - end - end - count = 1 - previous_r = pixel[1] - previous_g = pixel[2] - previous_b = pixel[3] - else - count = count + 1 - end + pixel[4] = 255 end end - colorword = 32768 + - ((math.floor((previous_r * max_sample_out / max_sample_in) + 0.5)) * 1024) + - ((math.floor((previous_g * max_sample_out / max_sample_in) + 0.5)) * 32) + - ((math.floor((previous_b * max_sample_out / max_sample_in) + 0.5)) * 1) - if 1 == count then - raw_pixel = string.char(colorword % 256, math.floor(colorword / 256)) - raw_pixels[#raw_pixels + 1] = raw_pixel - raw_packet = string.char(#raw_pixels - 1) - packets[#packets + 1] = raw_packet - for i=1, #raw_pixels do - packets[#packets +1] = raw_pixels[i] + self.pixel_depth = 32 + self:encode_data_R8G8B8A8_as_A1R5G5B5_rle() + for _, row in ipairs(self.pixels) do + for _, pixel in ipairs(row) do + pixel[4] = nil end - raw_pixels = {} - else - -- encode raw pixels, if any - if #raw_pixels > 0 then - raw_packet = string.char(#raw_pixels - 1) - packets[#packets + 1] = raw_packet - for i=1, #raw_pixels do - packets[#packets +1] = raw_pixels[i] - end - raw_pixels = {} - end - -- RLE encoding - rle_packet = string.char(128 + count - 1, colorword % 256, math.floor(colorword / 256)) - packets[#packets +1] = rle_packet end - self.data = self.data .. table.concat(packets) + self.pixel_depth = 24 end function image:encode_data_R8G8B8A8_as_A1R5G5B5_rle() -- 2.40.1 From c0cc855a0265fd0dcc68c339da09d803ef61e1cb Mon Sep 17 00:00:00 2001 From: Nils Dagsson Moskopp Date: Wed, 18 Oct 2023 05:08:16 +0200 Subject: [PATCH 08/15] Refactor encoding of uncompressed A1R5G5B5 images --- init.lua | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/init.lua b/init.lua index adc5a50..df9a1f8 100644 --- a/init.lua +++ b/init.lua @@ -310,22 +310,19 @@ end function image:encode_data_R8G8B8_as_A1R5G5B5_raw() assert(24 == self.pixel_depth) - local raw_pixels = {} - -- Sample depth rescaling is done according to the algorithm presented in: - -- - local max_sample_in = math.pow(2, 8) - 1 - local max_sample_out = math.pow(2, 5) - 1 for _, row in ipairs(self.pixels) do for _, pixel in ipairs(row) do - local colorword = 32768 + - ((math.floor((pixel[1] * max_sample_out / max_sample_in) + 0.5)) * 1024) + - ((math.floor((pixel[2] * max_sample_out / max_sample_in) + 0.5)) * 32) + - ((math.floor((pixel[3] * max_sample_out / max_sample_in) + 0.5)) * 1) - local raw_pixel = string.char(colorword % 256, math.floor(colorword / 256)) - raw_pixels[#raw_pixels + 1] = raw_pixel + pixel[4] = 255 end end - self.data = self.data .. table.concat(raw_pixels) + self.pixel_depth = 32 + self:encode_data_R8G8B8A8_as_A1R5G5B5_raw() + for _, row in ipairs(self.pixels) do + for _, pixel in ipairs(row) do + pixel[4] = nil + end + end + self.pixel_depth = 24 end function image:encode_data_R8G8B8A8_as_A1R5G5B5_raw() -- 2.40.1 From e329052c7f856ee9dd5cf162509cbb5ef6feef1e Mon Sep 17 00:00:00 2001 From: Nils Dagsson Moskopp Date: Fri, 20 Oct 2023 01:00:47 +0200 Subject: [PATCH 09/15] Generate TGA type 3 test textures from RGB input --- tools/generate_test_textures.lua | 110 ++++++++++++------------------- 1 file changed, 42 insertions(+), 68 deletions(-) diff --git a/tools/generate_test_textures.lua b/tools/generate_test_textures.lua index 54fb36f..725dbd0 100755 --- a/tools/generate_test_textures.lua +++ b/tools/generate_test_textures.lua @@ -33,38 +33,6 @@ local pixels_colormapped_by_scanline_order = { ["top-bottom"] = pixels_colormapped_tb, } -local _ = { 0 } -local R = { 127 } -local G = { 255 } -local B = { 191 } - -local pixels_grayscale_bt = { - { _, _, _, _, _, B, _, B, }, - { _, _, _, _, _, B, B, B, }, - { _, _, G, G, G, B, _, B, }, - { _, _, G, _, G, B, B, B, }, - { _, R, G, _, _, _, _, _, }, - { _, R, G, G, G, _, _, _, }, - { _, R, _, _, _, _, _, _, }, - { R, R, R, _, _, _, _, _, }, -} - -local pixels_grayscale_tb = { - { R, R, R, _, _, _, _, _, }, - { _, R, _, _, _, _, _, _, }, - { _, R, G, G, G, _, _, _, }, - { _, R, G, _, _, _, _, _, }, - { _, _, G, _, G, B, B, B, }, - { _, _, G, G, G, B, _, B, }, - { _, _, _, _, _, B, B, B, }, - { _, _, _, _, _, B, _, B, }, -} - -local pixels_grayscale_by_scanline_order = { - ["bottom-top"] = pixels_grayscale_bt, - ["top-bottom"] = pixels_grayscale_tb, -} - local colormap_32bpp = { { 0, 0, 0, 127 }, { 255, 0, 0, 255 }, @@ -95,20 +63,12 @@ local colormap_by_color_format = { for _, color_format in ipairs( tga_encoder.features.color_format ) do - for _, scanline_order in ipairs( - tga_encoder.features.scanline_order - ) do - local filename - local pixels - if ("Y8" == color_format) then - filename = "type3" .. - '_' .. color_format .. - '_' .. scanline_order .. - '.tga' - pixels = pixels_grayscale_by_scanline_order[ - scanline_order - ] - else + if ("Y8" ~= color_format) then + for _, scanline_order in ipairs( + tga_encoder.features.scanline_order + ) do + local filename + local pixels filename = "type1" .. '_' .. color_format .. '_' .. scanline_order .. @@ -116,18 +76,18 @@ for _, color_format in ipairs( pixels = pixels_colormapped_by_scanline_order[ scanline_order ] + local colormap = colormap_by_color_format[ + color_format + ] + local properties = { + colormap = colormap, + color_format = color_format, + scanline_order = scanline_order, + } + print(filename) + local image = tga_encoder.image(pixels) + image:save(filename, properties) end - local colormap = colormap_by_color_format[ - color_format - ] - local properties = { - colormap = colormap, - color_format = color_format, - scanline_order = scanline_order, - } - print(filename) - local image = tga_encoder.image(pixels) - image:save(filename, properties) end end @@ -204,28 +164,42 @@ local pixels_by_scanline_order_by_color_format = { ["A1R5G5B5"] = pixels_rgba_by_scanline_order, ["B8G8R8"] = pixels_rgb_by_scanline_order, ["B8G8R8A8"] = pixels_rgba_by_scanline_order, + ["Y8"] = pixels_rgb_by_scanline_order, } for _, color_format in ipairs( tga_encoder.features.color_format ) do + local pixels_by_scanline_order = pixels_by_scanline_order_by_color_format[ + color_format + ] for _, compression in ipairs( tga_encoder.features.compression ) do - for _, scanline_order in ipairs( - tga_encoder.features.scanline_order - ) do - if ("Y8" ~= color_format) then - local tga_type = tga_type_by_compression[ - compression - ] - local filename = "type" .. tga_type .. + local tga_type + if ( + "Y8" == color_format and + "RAW" == compression + ) then + tga_type = "3" + else + tga_type = tga_type_by_compression[ + compression + ] + end + -- tga_encoder can not encode grayscale RLE (type 11) + if not( + "Y8" == color_format and + "RLE" == compression + ) then + for _, scanline_order in ipairs( + tga_encoder.features.scanline_order + ) do + local filename = "type" .. tga_type .. '_' .. color_format .. '_' .. scanline_order .. '.tga' - local pixels = pixels_by_scanline_order_by_color_format[ - color_format - ][ + local pixels = pixels_by_scanline_order[ scanline_order ] local properties = { -- 2.40.1 From e599c9fe8363619df18c5558c19f9a3d175575ae Mon Sep 17 00:00:00 2001 From: Nils Dagsson Moskopp Date: Mon, 27 Nov 2023 23:06:49 +0100 Subject: [PATCH 10/15] Use keys with truthy values in features table MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If the features table uses keys with truthy values, it is easy to check for the presence of some feature by checking if the corresponding table entry (e.g. “tga_encoder.features.color_format.A1R5G5B5”) is truthy. --- init.lua | 16 ++++++++-------- tools/generate_test_textures.lua | 10 +++++----- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/init.lua b/init.lua index df9a1f8..683a4a1 100644 --- a/init.lua +++ b/init.lua @@ -2,20 +2,20 @@ tga_encoder = {} tga_encoder.features = {} tga_encoder.features.color_format = { - "A1R5G5B5", - "B8G8R8", - "B8G8R8A8", - "Y8", + ["A1R5G5B5"] = true, + ["B8G8R8"] = true, + ["B8G8R8A8"] = true, + ["Y8"] = true, } tga_encoder.features.colormap = { } tga_encoder.features.compression = { - "RAW", - "RLE", + ["RAW"] = true, + ["RLE"] = true, } tga_encoder.features.scanline_order = { - "bottom-top", - "top-bottom", + ["bottom-top"] = true, + ["top-bottom"] = true, } local image = setmetatable({}, { diff --git a/tools/generate_test_textures.lua b/tools/generate_test_textures.lua index 725dbd0..a6c9fee 100755 --- a/tools/generate_test_textures.lua +++ b/tools/generate_test_textures.lua @@ -60,11 +60,11 @@ local colormap_by_color_format = { ["B8G8R8A8"] = colormap_32bpp, } -for _, color_format in ipairs( +for color_format, _ in pairs( tga_encoder.features.color_format ) do if ("Y8" ~= color_format) then - for _, scanline_order in ipairs( + for scanline_order, _ in pairs( tga_encoder.features.scanline_order ) do local filename @@ -167,13 +167,13 @@ local pixels_by_scanline_order_by_color_format = { ["Y8"] = pixels_rgb_by_scanline_order, } -for _, color_format in ipairs( +for color_format, _ in pairs( tga_encoder.features.color_format ) do local pixels_by_scanline_order = pixels_by_scanline_order_by_color_format[ color_format ] - for _, compression in ipairs( + for compression, _ in pairs( tga_encoder.features.compression ) do local tga_type @@ -192,7 +192,7 @@ for _, color_format in ipairs( "Y8" == color_format and "RLE" == compression ) then - for _, scanline_order in ipairs( + for scanline_order, _ in pairs( tga_encoder.features.scanline_order ) do local filename = "type" .. tga_type .. -- 2.40.1 From 0272fa5ccdaed1c3f329efad0b14e83fc0bc8aa4 Mon Sep 17 00:00:00 2001 From: Nils Dagsson Moskopp Date: Tue, 28 Nov 2023 21:35:00 +0100 Subject: [PATCH 11/15] Change scanline order names MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The scanline order ”bottom-top” has been changed to ”bottom_top”. The scanline order “top-bottom” has been changed to “top_bottom”. The scanline order names were changed so that API consumers can check if “tga_encoder.features.scanline_order.top_bottom” is truthy instead of having to check “tga_encoder.features.scanline_order["top-bottom"]”. This is a breaking change, but since no release of tga_encoder happened since commit 01bcbbf9272b39cd998f0daefc3b915ffa7d8300 which allowed the scanline order to be specified, it can not affect any mod on ContentDB. --- init.lua | 14 +++++++------- tools/generate_test_textures.lua | 12 ++++++------ 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/init.lua b/init.lua index 683a4a1..87c47fd 100644 --- a/init.lua +++ b/init.lua @@ -14,8 +14,8 @@ tga_encoder.features.compression = { ["RLE"] = true, } tga_encoder.features.scanline_order = { - ["bottom-top"] = true, - ["top-bottom"] = true, + ["bottom_top"] = true, + ["top_bottom"] = true, } local image = setmetatable({}, { @@ -76,8 +76,8 @@ function image:encode_image_spec(properties) ) local scanline_order = properties.scanline_order assert ( - "bottom-top" == scanline_order or - "top-bottom" == scanline_order + "bottom_top" == scanline_order or + "top_bottom" == scanline_order ) local pixel_depth if 0 ~= #properties.colormap then @@ -91,12 +91,12 @@ function image:encode_image_spec(properties) local x_origin_hi = 0 local y_origin_lo = 0 local y_origin_hi = 0 - local image_descriptor = 0 -- equal to bottom-top scanline order + local image_descriptor = 0 -- equal to bottom_top scanline order local width_lo = self.width % 256 local width_hi = math.floor(self.width / 256) local height_lo = self.height % 256 local height_hi = math.floor(self.height / 256) - if "top-bottom" == scanline_order then + if "top_bottom" == scanline_order then image_descriptor = 32 y_origin_lo = height_lo y_origin_hi = height_hi @@ -650,7 +650,7 @@ function image:encode(properties) local properties = properties or {} properties.colormap = properties.colormap or {} properties.compression = properties.compression or "RAW" - properties.scanline_order = properties.scanline_order or "bottom-top" + properties.scanline_order = properties.scanline_order or "bottom_top" self.pixel_depth = #self.pixels[1][1] * 8 diff --git a/tools/generate_test_textures.lua b/tools/generate_test_textures.lua index a6c9fee..7bc9d86 100755 --- a/tools/generate_test_textures.lua +++ b/tools/generate_test_textures.lua @@ -29,8 +29,8 @@ local pixels_colormapped_tb = { } local pixels_colormapped_by_scanline_order = { - ["bottom-top"] = pixels_colormapped_bt, - ["top-bottom"] = pixels_colormapped_tb, + ["bottom_top"] = pixels_colormapped_bt, + ["top_bottom"] = pixels_colormapped_tb, } local colormap_32bpp = { @@ -119,8 +119,8 @@ local pixels_rgba_tb = { } local pixels_rgba_by_scanline_order = { - ["bottom-top"] = pixels_rgba_bt, - ["top-bottom"] = pixels_rgba_tb, + ["bottom_top"] = pixels_rgba_bt, + ["top_bottom"] = pixels_rgba_tb, } local _ = { 0, 0, 0 } @@ -151,8 +151,8 @@ local pixels_rgb_tb = { } local pixels_rgb_by_scanline_order = { - ["bottom-top"] = pixels_rgb_bt, - ["top-bottom"] = pixels_rgb_tb, + ["bottom_top"] = pixels_rgb_bt, + ["top_bottom"] = pixels_rgb_tb, } local tga_type_by_compression = { -- 2.40.1 From 72a4f41aa7fb8fe4cbd0076b0d87cfa1021bc5f1 Mon Sep 17 00:00:00 2001 From: Nils Dagsson Moskopp Date: Wed, 29 Nov 2023 17:38:46 +0100 Subject: [PATCH 12/15] Use hyphen-minus to separate test texture filename parts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The scanline orders “bottom_top” and “top_bottom” contain an underscore. Therefore, the underscore is no longer an appropriate separator for the test texture filename parts (TGA type, color format, scanline order). --- tools/generate_test_textures.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tools/generate_test_textures.lua b/tools/generate_test_textures.lua index 7bc9d86..a0d2e3e 100755 --- a/tools/generate_test_textures.lua +++ b/tools/generate_test_textures.lua @@ -70,8 +70,8 @@ for color_format, _ in pairs( local filename local pixels filename = "type1" .. - '_' .. color_format .. - '_' .. scanline_order .. + '-' .. color_format .. + '-' .. scanline_order .. '.tga' pixels = pixels_colormapped_by_scanline_order[ scanline_order @@ -196,8 +196,8 @@ for color_format, _ in pairs( tga_encoder.features.scanline_order ) do local filename = "type" .. tga_type .. - '_' .. color_format .. - '_' .. scanline_order .. + '-' .. color_format .. + '-' .. scanline_order .. '.tga' local pixels = pixels_by_scanline_order[ scanline_order -- 2.40.1 From f6cf59222832c5745d7a275494392cb137c778af Mon Sep 17 00:00:00 2001 From: Nils Dagsson Moskopp Date: Fri, 1 Dec 2023 00:20:31 +0100 Subject: [PATCH 13/15] Encode number of attribute/alpha bits in image descriptor --- init.lua | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/init.lua b/init.lua index 87c47fd..cd36b69 100644 --- a/init.lua +++ b/init.lua @@ -101,6 +101,11 @@ function image:encode_image_spec(properties) y_origin_lo = height_lo y_origin_hi = height_hi end + if "A1R5G5B5" == color_format then + image_descriptor = image_descriptor + 1 -- 1 bit alpha channel + elseif "B8G8R8A8" == color_format then + image_descriptor = image_descriptor + 8 -- 8 bit alpha channel + end self.data = self.data .. string.char ( x_origin_lo, x_origin_hi, y_origin_lo, y_origin_hi, -- 2.40.1 From 9497a0303cfc2740e34e553527813bfaf9308270 Mon Sep 17 00:00:00 2001 From: Nils Dagsson Moskopp Date: Tue, 19 Mar 2024 11:39:05 +0100 Subject: [PATCH 14/15] =?UTF-8?q?Add=202=C3=972=20white=20square=20to=20bo?= =?UTF-8?q?ttom=20left=20corner=20of=20test=20textures?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As the alpha channel of the color is above 127, the square should show as opaque white in TGA images with the color format A1R5G5B5. --- tools/generate_test_textures.lua | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/tools/generate_test_textures.lua b/tools/generate_test_textures.lua index a0d2e3e..b3e2765 100755 --- a/tools/generate_test_textures.lua +++ b/tools/generate_test_textures.lua @@ -5,10 +5,11 @@ local _ = { 0 } local R = { 1 } local G = { 2 } local B = { 3 } +local W = { 4 } local pixels_colormapped_bt = { - { _, _, _, _, _, B, _, B, }, - { _, _, _, _, _, B, B, B, }, + { W, W, _, _, _, B, _, B, }, + { W, W, _, _, _, B, B, B, }, { _, _, G, G, G, B, _, B, }, { _, _, G, _, G, B, B, B, }, { _, R, G, _, _, _, _, _, }, @@ -24,8 +25,8 @@ local pixels_colormapped_tb = { { _, R, G, _, _, _, _, _, }, { _, _, G, _, G, B, B, B, }, { _, _, G, G, G, B, _, B, }, - { _, _, _, _, _, B, B, B, }, - { _, _, _, _, _, B, _, B, }, + { W, W, _, _, _, B, B, B, }, + { W, W, _, _, _, B, _, B, }, } local pixels_colormapped_by_scanline_order = { @@ -38,6 +39,7 @@ local colormap_32bpp = { { 255, 0, 0, 255 }, { 0, 255, 0, 255 }, { 0, 0, 255, 255 }, + { 255, 255, 255, 128 }, } local colormap_24bpp = { @@ -45,6 +47,7 @@ local colormap_24bpp = { { 255, 0, 0 }, { 0, 255, 0 }, { 0, 0, 255 }, + { 255, 255, 255 }, } local colormap_16bpp = { @@ -52,6 +55,7 @@ local colormap_16bpp = { { 255, 0, 0, 255 }, { 0, 255, 0, 255 }, { 0, 0, 255, 255 }, + { 255, 255, 255, 255 }, } local colormap_by_color_format = { @@ -95,10 +99,11 @@ local _ = { 0, 0, 0, 127 } local R = { 255, 0, 0, 255 } local G = { 0, 255, 0, 255 } local B = { 0, 0, 255, 255 } +local W = { 255, 255, 255, 128 } local pixels_rgba_bt = { - { _, _, _, _, _, B, _, B, }, - { _, _, _, _, _, B, B, B, }, + { W, W, _, _, _, B, _, B, }, + { W, W, _, _, _, B, B, B, }, { _, _, G, G, G, B, _, B, }, { _, _, G, _, G, B, B, B, }, { _, R, G, _, _, _, _, _, }, @@ -114,8 +119,8 @@ local pixels_rgba_tb = { { _, R, G, _, _, _, _, _, }, { _, _, G, _, G, B, B, B, }, { _, _, G, G, G, B, _, B, }, - { _, _, _, _, _, B, B, B, }, - { _, _, _, _, _, B, _, B, }, + { W, W, _, _, _, B, B, B, }, + { W, W, _, _, _, B, _, B, }, } local pixels_rgba_by_scanline_order = { @@ -127,10 +132,11 @@ local _ = { 0, 0, 0 } local R = { 255, 0, 0 } local G = { 0, 255, 0 } local B = { 0, 0, 255 } +local W = { 255, 255, 255 } local pixels_rgb_bt = { - { _, _, _, _, _, B, _, B, }, - { _, _, _, _, _, B, B, B, }, + { W, W, _, _, _, B, _, B, }, + { W, W, _, _, _, B, B, B, }, { _, _, G, G, G, B, _, B, }, { _, _, G, _, G, B, B, B, }, { _, R, G, _, _, _, _, _, }, @@ -146,8 +152,8 @@ local pixels_rgb_tb = { { _, R, G, _, _, _, _, _, }, { _, _, G, _, G, B, B, B, }, { _, _, G, G, G, B, _, B, }, - { _, _, _, _, _, B, B, B, }, - { _, _, _, _, _, B, _, B, }, + { W, W, _, _, _, B, B, B, }, + { W, W, _, _, _, B, _, B, }, } local pixels_rgb_by_scanline_order = { -- 2.40.1 From 50466ca08b917fdb4063f9220e30f9431ace96c8 Mon Sep 17 00:00:00 2001 From: Nils Dagsson Moskopp Date: Tue, 19 Mar 2024 11:55:15 +0100 Subject: [PATCH 15/15] Encode alpha channel for colormapped A1R5G5B5 images --- init.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/init.lua b/init.lua index cd36b69..52918be 100644 --- a/init.lua +++ b/init.lua @@ -135,7 +135,8 @@ function image:encode_colormap(properties) local max_sample_out = math.pow(2, 5) - 1 for i = 1,#colormap,1 do local color = colormap[i] - local colorword = 32768 + + local colorword = 0 + + ((math.floor((color[4] / max_sample_in) + 0.5)) * 32768) + -- transparent if alpha < 128 ((math.floor((color[1] * max_sample_out / max_sample_in) + 0.5)) * 1024) + ((math.floor((color[2] * max_sample_out / max_sample_in) + 0.5)) * 32) + ((math.floor((color[3] * max_sample_out / max_sample_in) + 0.5)) * 1) -- 2.40.1