Created
November 23, 2022 03:32
-
-
Save delfigamer/adfe216216789c7033c7f5e66164e20f to your computer and use it in GitHub Desktop.
Simulates artifacts of a certain lossy image compression
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
local png = {} | |
local ffi = require('ffi') | |
local libpng = ffi.load('libpng16.dll') | |
ffi.cdef[[ | |
typedef struct png_control *png_controlp; | |
typedef struct | |
{ | |
png_controlp opaque; /* Initialize to NULL, free with png_image_free */ | |
uint32_t version; /* Set to PNG_IMAGE_VERSION */ | |
uint32_t width; /* Image width in pixels (columns) */ | |
uint32_t height; /* Image height in pixels (rows) */ | |
uint32_t format; /* Image format as defined below */ | |
uint32_t flags; /* A bit mask containing informational flags */ | |
uint32_t colormap_entries; | |
/* Number of entries in the color-map */ | |
uint32_t warning_or_error; | |
char message[64]; | |
} png_image, *png_imagep; | |
typedef struct png_color_struct | |
{ | |
uint8_t* red; | |
uint8_t* green; | |
uint8_t* blue; | |
} png_color; | |
typedef png_color * png_colorp; | |
typedef const png_color * png_const_colorp; | |
int png_image_begin_read_from_file( | |
png_imagep image, const char *file_name); | |
int png_image_finish_read( | |
png_imagep image, | |
png_const_colorp background, | |
void *buffer, int32_t row_stride, | |
void *colormap); | |
void png_image_free(png_imagep image); | |
int png_image_write_to_file( | |
png_imagep image, | |
const char *file, int convert_to_8bit, const void *buffer, | |
int32_t row_stride, const void *colormap); | |
]] | |
local PNG_IMAGE_VERSION = 1 | |
local PNG_IMAGE_WARNING = 1 | |
local PNG_IMAGE_ERROR = 2 | |
local PNG_FORMAT_RGBA = 3 | |
local function newimage() | |
local i = ffi.gc(ffi.new('png_image'), libpng.png_image_free) | |
i.version = PNG_IMAGE_VERSION | |
return i | |
end | |
local function checkerror(i) | |
local woe = bit.band(i.warning_or_error, 3) ~= 0 | |
if woe == 2 or woe == 3 then | |
error(ffi.string(i.message)) | |
elseif woe == 1 then | |
printf('png warning: ' .. ffi.string(i.message)) | |
end | |
end | |
function png.readfile(path) | |
local i = newimage() | |
libpng.png_image_begin_read_from_file(i, path) | |
checkerror(i) | |
local bm = ffi.new(ffi.typeof('uint8_t[$][$][4]', i.height, i.width)) | |
i.format = PNG_FORMAT_RGBA | |
i.flags = 0 | |
libpng.png_image_finish_read(i, nil, bm, i.width * 4, nil) | |
checkerror(i) | |
libpng.png_image_free(i) | |
return i.width, i.height, bm | |
end | |
function png.writefile(path, width, height, bm) | |
local i = newimage() | |
i.width = width | |
i.height = height | |
i.format = PNG_FORMAT_RGBA | |
i.flags = 0 | |
libpng.png_image_write_to_file(i, path, 0, bm, width * 4, nil) | |
checkerror(i) | |
end | |
local ka = 7 | |
local blocksize = 64 | |
local function quantize(q, ra, rd) | |
if rd <= 0 then | |
return q | |
else | |
local u = (q - ra) / rd | |
local v = math.floor(u * ka + 0.5) / ka | |
return math.floor(v * rd + ra + 0.5) | |
end | |
end | |
local function compress(p, xa, xb, ya, yb) | |
local samples = {[0] = {}, {}, {}, {}} | |
for y = ya, yb - 1 do | |
for x = xa, xb - 1 do | |
for c = 0, 3 do | |
local v = p[y][x][c] | |
samples[c][#samples[c] + 1] = v | |
end | |
end | |
end | |
local loa = {} | |
local lod = {} | |
local hia = {} | |
local hid = {} | |
for c = 0, 3 do | |
table.sort(samples[c]) | |
local maxdensv = 0 | |
local maxdensi = 2 | |
for i = 2, #samples[c] - 2 do | |
local lospan = (samples[c][i] - samples[c][1]) | |
local hispan = (samples[c][#samples[c]] - samples[c][i + 1]) | |
if lospan > 0 and hispan > 0 then | |
local lodens = i*(i - 2) / lospan | |
local hidens = (#samples[c] - i)*(#samples[c] - i - 2) / hispan | |
local dens = lodens + hidens | |
if dens > maxdensv then | |
maxdensv = dens | |
maxdensi = i | |
end | |
end | |
end | |
loa[c] = samples[c][1] | |
lod[c] = samples[c][maxdensi] - samples[c][1] | |
hia[c] = samples[c][maxdensi + 1] | |
hid[c] = samples[c][#samples[c]] - samples[c][maxdensi + 1] | |
end | |
for y = ya, yb - 1 do | |
for x = xa, xb - 1 do | |
for c = 0, 3 do | |
local v = p[y][x][c] | |
if v > hia[c] then | |
p[y][x][c] = quantize(v, hia[c], hid[c]) | |
else | |
p[y][x][c] = quantize(v, loa[c], lod[c]) | |
end | |
end | |
end | |
end | |
end | |
local width, height, p = png.readfile('C:\\Users\\delfi\\Desktop\\i3.png') | |
for ya = 0, height - 1, blocksize do | |
local yb = ya + blocksize | |
if yb > height then | |
yb = height | |
end | |
for xa = 0, width - 1, blocksize do | |
local xb = xa + blocksize | |
if xb > width then | |
xb = width | |
end | |
compress(p, xa, xb, ya, yb) | |
end | |
end | |
png.writefile('C:\\Users\\delfi\\Desktop\\r3.png', width, height, p) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment