Allow color-mapped encoding for RGB images

This commit is contained in:
Nils Dagsson Moskopp 2022-05-16 17:01:38 +02:00
parent ed061e68ff
commit 9f9b78eed9
No known key found for this signature in database
GPG key ID: A3BC671C35191080
2 changed files with 111 additions and 19 deletions

View file

@ -100,3 +100,29 @@ end
tga_encoder.image(pixels):save("fractal_8bpp.tga", {color_format="Y8"}) tga_encoder.image(pixels):save("fractal_8bpp.tga", {color_format="Y8"})
tga_encoder.image(pixels):save("fractal_16bpp.tga", {color_format="A1R5G5B5"}) tga_encoder.image(pixels):save("fractal_16bpp.tga", {color_format="A1R5G5B5"})
tga_encoder.image(pixels):save("fractal_24bpp.tga", {color_format="B8G8R8"}) tga_encoder.image(pixels):save("fractal_24bpp.tga", {color_format="B8G8R8"})
-- encode a colormapped bitmap
local K = { 0 }
local B = { 1 }
local R = { 2 }
local G = { 3 }
local W = { 4 }
local colormap = {
{ 1, 2, 3 }, -- K
{ 0, 0, 255 }, -- B
{ 255, 0, 0 }, -- R
{ 0, 255, 0 }, -- G
{ 253, 254, 255 }, -- W
}
local pixels = {
{ W, K, W, K, W, K, W },
{ R, G, B, R, G, B, K },
{ K, W, K, W, K, W, K },
{ G, B, R, G, B, R, W },
{ W, W, W, K, K, K, W },
{ B, R, G, B, R, G, K },
{ B, R, G, B, R, G, W },
}
-- note that the uncompressed colormapped TGA file written in this
-- example is 108 bytes but an optimized PNG file is 121 bytes …
tga_encoder.image(pixels):save("colormapped_8bpp.tga", {colormap=colormap})

View file

@ -14,11 +14,21 @@ function image:constructor(pixels)
self.height = #pixels self.height = #pixels
end end
function image:encode_colormap_spec() function image:encode_colormap_spec(properties)
self.data = self.data local colormap_spec
.. string.char(0, 0) -- first entry index if nil ~= properties.colormap then
.. string.char(0, 0) -- number of entries local color_count = #properties.colormap
.. string.char(0) -- bits per pixel colormap_spec =
string.char(0, 0) .. -- first entry index
string.char(color_count % 256, math.floor(color_count / 256)) .. -- number of entries
string.char(#properties.colormap[1] * 8) -- bits per pixel
else -- no colormap
colormap_spec =
string.char(0, 0) .. -- first entry index
string.char(0, 0) .. -- number of entries
string.char(0) -- bits per pixel
end
self.data = self.data .. colormap_spec
end end
function image:encode_image_spec(properties) function image:encode_image_spec(properties)
@ -35,7 +45,12 @@ function image:encode_image_spec(properties)
["B8G8R8"] = 24, ["B8G8R8"] = 24,
["B8G8R8A8"] = 32, ["B8G8R8A8"] = 32,
} }
local pixel_depth = pixel_depth_by_color_format[color_format] local pixel_depth
if nil ~= properties.colormap then
pixel_depth = self.pixel_depth
else
pixel_depth = pixel_depth_by_color_format[color_format]
end
assert( nil ~= pixel_depth) assert( nil ~= pixel_depth)
self.data = self.data self.data = self.data
.. string.char(0, 0) -- X-origin .. string.char(0, 0) -- X-origin
@ -46,8 +61,31 @@ function image:encode_image_spec(properties)
.. string.char(0) -- image descriptor .. string.char(0) -- image descriptor
end end
function image:encode_colormap(properties)
local colormap = properties.colormap
if nil == colormap then
return
end
local colormap_pixel_depth = #colormap[1] * 8
assert( 24 == colormap_pixel_depth )
local colors = {}
if 24 == colormap_pixel_depth then
for i = 1,#colormap,1 do
local color = colormap[i]
local color_bytes = string.char(
color[3], -- B
color[2], -- G
color[1] -- R
)
colors[#colors + 1] = color_bytes
end
end
self.data = self.data .. table.concat(colors)
end
function image:encode_header(properties) function image:encode_header(properties)
local color_format = properties.color_format local color_format = properties.color_format
local colormap = properties.colormap
local compression = properties.compression local compression = properties.compression
local image_type local image_type
if "Y8" == color_format and "RAW" == compression then if "Y8" == color_format and "RAW" == compression then
@ -58,22 +96,32 @@ function image:encode_header(properties)
"B8G8R8A8" == color_format "B8G8R8A8" == color_format
) then ) then
if "RAW" == compression then if "RAW" == compression then
if nil ~= colormap then
image_type = 1 -- colormapped RGB(A)
else
image_type = 2 -- RAW RGB(A) image_type = 2 -- RAW RGB(A)
end
elseif "RLE" == compression then elseif "RLE" == compression then
image_type = 10 -- RLE RGB image_type = 10 -- RLE RGB
end end
end end
assert( nil ~= image_type ) assert( nil ~= image_type )
local colormap_type = 0
if nil ~= colormap then
colormap_type = 1
end
self.data = self.data self.data = self.data
.. string.char(0) -- image id .. string.char(0) -- image id
.. string.char(0) -- color map type .. string.char(colormap_type)
.. string.char(image_type) .. string.char(image_type)
self:encode_colormap_spec() -- color map specification self:encode_colormap_spec(properties) -- color map specification
self:encode_image_spec(properties) -- image specification self:encode_image_spec(properties) -- image specification
self:encode_colormap(properties)
end end
function image:encode_data(properties) function image:encode_data(properties)
local color_format = properties.color_format local color_format = properties.color_format
local colormap = properties.colormap
local compression = properties.compression local compression = properties.compression
local data_length_before = #self.data local data_length_before = #self.data
@ -90,11 +138,19 @@ function image:encode_data(properties)
self:encode_data_R8G8B8_as_A1R5G5B5_rle() self:encode_data_R8G8B8_as_A1R5G5B5_rle()
end end
elseif "B8G8R8" == color_format then elseif "B8G8R8" == color_format then
if nil ~= colormap then
if "RAW" == compression then
if 8 == self.pixel_depth then
self:encode_data_Y8_as_Y8_raw()
end
end
else
if "RAW" == compression then if "RAW" == compression then
self:encode_data_R8G8B8_as_B8G8R8_raw() self:encode_data_R8G8B8_as_B8G8R8_raw()
elseif "RLE" == compression then elseif "RLE" == compression then
self:encode_data_R8G8B8_as_B8G8R8_rle() self:encode_data_R8G8B8_as_B8G8R8_rle()
end end
end
elseif "B8G8R8A8" == color_format then elseif "B8G8R8A8" == color_format then
if "RAW" == compression then if "RAW" == compression then
self:encode_data_R8G8B8A8_as_B8G8R8A8_raw() self:encode_data_R8G8B8A8_as_B8G8R8A8_raw()
@ -456,13 +512,23 @@ function image:save(filename, properties)
properties.compression = properties.compression or "RAW" properties.compression = properties.compression or "RAW"
self.pixel_depth = #self.pixels[1][1] * 8 self.pixel_depth = #self.pixels[1][1] * 8
local color_format_defaults_by_pixel_depth = {
[8] = "Y8",
[24] = "B8G8R8",
[32] = "B8G8R8A8",
}
if nil == properties.color_format then if nil == properties.color_format then
if 8 == self.pixel_depth then if nil ~= properties.colormap then
properties.color_format = "Y8" properties.color_format =
elseif 24 == self.pixel_depth then color_format_defaults_by_pixel_depth[
properties.color_format = "B8G8R8" #properties.colormap[1] * 8
elseif 32 == self.pixel_depth then ]
properties.color_format = "B8G8R8A8" else
properties.color_format =
color_format_defaults_by_pixel_depth[
self.pixel_depth
]
end end
end end
assert( nil ~= properties.color_format ) assert( nil ~= properties.color_format )