texgen/dithering.lua

103 lines
2.6 KiB
Lua

-- Simple error diffusion dithering
-- TODO use HSV/HSL for dithering closer to human perception
local matrices = {
none = {
fraction = 0
},
floyd_steinberg = {
fraction = 1/16,
{x_off = 1, 7};
{x_off = -1, 3, 5, 1};
},
jarvis_judice_ninke = {
fraction = 1/48,
{x_off = 1, 7, 5};
{x_off = -2, 3, 5, 7, 5, 3};
{x_off = -2, 1, 3, 5, 3, 1};
},
stucke = {
fraction = 1/42,
{x_off = 1, 8, 4};
{x_off = -2, 2, 4, 8, 4, 2};
{x_off = -2, 1, 2, 4, 2, 1};
},
atkinson = {
fraction = 1/8,
{x_off = 1, 1, 1};
{x_off = -1, 1, 1, 1 };
{x_off = 0, 1 };
},
burkes = {
fraction = 1/42,
{x_off = 1, 8, 4};
{x_off = -2, 2, 4, 8, 4, 2};
},
sierra = {
fraction = 1/16,
{x_off = 1, 4, 3};
{x_off = -2, 1, 2, 3, 2, 1};
},
sierra_lite = {
fraction = 1/4,
{x_off = 1, 2},
{x_off = -1, 1, 1 },
},
two_row_sierra = {
fraction = 1/32,
{x_off = 1, 5, 3},
{x_off = -2, 2, 4, 5, 4, 2},
{x_off = -1, 2, 3, 2 },
},
}
texgen.dithering_methods = matrices
local clamp = modlib.math.clamp
return function(method)
local matrix = assert(matrices[method])
return function(png, closest_color)
local width = png.width
local diffused_errors = {}
local zero_err = {r = 0, g = 0, b = 0}
for index, color in ipairs(png.data) do
local x, y = (index - 1) % width, math.floor((index - 1) / width)
color = modlib.minetest.colorspec.from_number(color)
local diff_err = diffused_errors[index] or zero_err
diffused_errors[index] = nil
local col_err = {
r = clamp(color.r + diff_err.r, 0, 255),
g = clamp(color.g + diff_err.g, 0, 255),
b = clamp(color.b + diff_err.b, 0, 255)
}
local new_color = closest_color(col_err)
png.data[index] = modlib.minetest.colorspec.new{
r = new_color.r,
g = new_color.g,
b = new_color.b,
a = color.a -- keep alpha
}:to_number()
-- Diffuse error
local weight = matrix.fraction * color.a / 255
if weight > 0 then
local err = {
r = weight * (new_color.r - col_err.r),
g = weight * (new_color.g - col_err.g),
b = weight * (new_color.b - col_err.b)
}
for y_off, row in ipairs(matrix) do
local ey = y + y_off - 1
for x_off, factor in ipairs(row) do
local ex = x + row.x_off + x_off - 1
local idx = ey * width + ex
local derr = diffused_errors[idx] or {r = 0, g = 0, b = 0}
derr.r = derr.r + factor * err.r
derr.g = derr.g + factor * err.g
derr.b = derr.b + factor * err.b
diffused_errors[idx] = derr
end
end
end
end
end
end