Encode alpha channel for compressed A1R5G5B5 images

This commit is contained in:
Nils Dagsson Moskopp 2023-10-18 01:40:56 +02:00
parent 2e0471c81f
commit 619e49d3b7
Signed by: erlehmann
GPG Key ID: A3BC671C35191080
1 changed files with 99 additions and 1 deletions

100
init.lua
View File

@ -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:
-- <https://www.w3.org/TR/2003/REC-PNG-20031110/#13Sample-depth-rescaling>
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 = {}