Add features table, refactor test texture generation, encode alpha for A1R5G5B5 images #1
95
init.lua
95
init.lua
|
@ -1,5 +1,23 @@
|
|||
tga_encoder = {}
|
||||
|
||||
tga_encoder.features = {}
|
||||
tga_encoder.features.color_format = {
|
||||
["A1R5G5B5"] = true,
|
||||
["B8G8R8"] = true,
|
||||
["B8G8R8A8"] = true,
|
||||
["Y8"] = true,
|
||||
}
|
||||
tga_encoder.features.colormap = {
|
||||
}
|
||||
tga_encoder.features.compression = {
|
||||
["RAW"] = true,
|
||||
["RLE"] = true,
|
||||
}
|
||||
tga_encoder.features.scanline_order = {
|
||||
["bottom_top"] = true,
|
||||
["top_bottom"] = true,
|
||||
}
|
||||
|
||||
local image = setmetatable({}, {
|
||||
__call = function(self, ...)
|
||||
local t = setmetatable({}, {__index = self})
|
||||
|
@ -58,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
|
||||
|
@ -73,16 +91,21 @@ 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
|
||||
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,
|
||||
|
@ -112,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)
|
||||
|
@ -207,9 +231,17 @@ 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()
|
||||
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
|
||||
|
@ -284,6 +316,23 @@ end
|
|||
|
||||
function image:encode_data_R8G8B8_as_A1R5G5B5_raw()
|
||||
assert(24 == self.pixel_depth)
|
||||
for _, row in ipairs(self.pixels) do
|
||||
for _, pixel in ipairs(row) do
|
||||
pixel[4] = 255
|
||||
end
|
||||
end
|
||||
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()
|
||||
assert(32 == self.pixel_depth)
|
||||
local raw_pixels = {}
|
||||
-- Sample depth rescaling is done according to the algorithm presented in:
|
||||
-- <https://www.w3.org/TR/2003/REC-PNG-20031110/#13Sample-depth-rescaling>
|
||||
|
@ -291,7 +340,8 @@ function image:encode_data_R8G8B8_as_A1R5G5B5_raw()
|
|||
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 +
|
||||
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)
|
||||
|
@ -304,10 +354,28 @@ end
|
|||
|
||||
function image:encode_data_R8G8B8_as_A1R5G5B5_rle()
|
||||
assert(24 == self.pixel_depth)
|
||||
for _, row in ipairs(self.pixels) do
|
||||
for _, pixel in ipairs(row) do
|
||||
pixel[4] = 255
|
||||
end
|
||||
end
|
||||
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
|
||||
end
|
||||
self.pixel_depth = 24
|
||||
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
|
||||
|
@ -320,9 +388,10 @@ function image:encode_data_R8G8B8_as_A1R5G5B5_rle()
|
|||
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 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 = 32768 +
|
||||
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)
|
||||
|
@ -357,12 +426,14 @@ function image:encode_data_R8G8B8_as_A1R5G5B5_rle()
|
|||
previous_r = pixel[1]
|
||||
previous_g = pixel[2]
|
||||
previous_b = pixel[3]
|
||||
previous_a = pixel[4]
|
||||
else
|
||||
count = count + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
colorword = 32768 +
|
||||
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)
|
||||
|
@ -585,7 +656,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
|
||||
|
||||
|
|
|
@ -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,59 +25,85 @@ 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, },
|
||||
}
|
||||
|
||||
image_colormapped_bt = tga_encoder.image(pixels_colormapped_bt)
|
||||
image_colormapped_tb = tga_encoder.image(pixels_colormapped_tb)
|
||||
local pixels_colormapped_by_scanline_order = {
|
||||
["bottom_top"] = pixels_colormapped_bt,
|
||||
["top_bottom"] = pixels_colormapped_tb,
|
||||
}
|
||||
|
||||
colormap_32bpp = {
|
||||
{ 0, 0, 0, 128 },
|
||||
local colormap_32bpp = {
|
||||
{ 0, 0, 0, 127 },
|
||||
{ 255, 0, 0, 255 },
|
||||
{ 0, 255, 0, 255 },
|
||||
{ 0, 0, 255, 255 },
|
||||
{ 255, 255, 255, 128 },
|
||||
}
|
||||
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 },
|
||||
{ 255, 255, 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 _ = { 0, 0, 0, 128 }
|
||||
local colormap_16bpp = {
|
||||
{ 0, 0, 0, 0 },
|
||||
{ 255, 0, 0, 255 },
|
||||
{ 0, 255, 0, 255 },
|
||||
{ 0, 0, 255, 255 },
|
||||
{ 255, 255, 255, 255 },
|
||||
}
|
||||
|
||||
local colormap_by_color_format = {
|
||||
["A1R5G5B5"] = colormap_16bpp,
|
||||
["B8G8R8"] = colormap_24bpp,
|
||||
["B8G8R8A8"] = colormap_32bpp,
|
||||
}
|
||||
|
||||
for color_format, _ in pairs(
|
||||
tga_encoder.features.color_format
|
||||
) do
|
||||
if ("Y8" ~= color_format) then
|
||||
for scanline_order, _ in pairs(
|
||||
tga_encoder.features.scanline_order
|
||||
) do
|
||||
local filename
|
||||
local pixels
|
||||
filename = "type1" ..
|
||||
'-' .. color_format ..
|
||||
'-' .. scanline_order ..
|
||||
'.tga'
|
||||
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
|
||||
end
|
||||
end
|
||||
|
||||
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, _, _, _, _, _, },
|
||||
|
@ -92,38 +119,24 @@ 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, },
|
||||
}
|
||||
|
||||
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 }
|
||||
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, _, _, _, _, _, },
|
||||
|
@ -139,42 +152,71 @@ 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, },
|
||||
}
|
||||
|
||||
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,
|
||||
["Y8"] = pixels_rgb_by_scanline_order,
|
||||
}
|
||||
|
||||
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 pairs(
|
||||
tga_encoder.features.compression
|
||||
) do
|
||||
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 pairs(
|
||||
tga_encoder.features.scanline_order
|
||||
) do
|
||||
local filename = "type" .. tga_type ..
|
||||
'-' .. color_format ..
|
||||
'-' .. scanline_order ..
|
||||
'.tga'
|
||||
local pixels = pixels_by_scanline_order[
|
||||
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
|
||||
|
|
Loading…
Reference in New Issue