§ Дизеринг Флойда-Штейнберга
Как пользоваться:
php make16.php [infile] [outfile]
<?php
$im = imagecreatefrompng($argv[1] ?? "ping.png");
$palette = [
[0x00, 0x00, 0x00],
[0x00, 0x00, 0x80],
[0x00, 0x80, 0x00],
[0x00, 0x80, 0x80],
[0x80, 0x00, 0x00],
[0x80, 0x00, 0x80],
[0x80, 0x80, 0x00],
[0xcc, 0xcc, 0xcc],
[0x80, 0x80, 0x80],
[0x00, 0x00, 0xff],
[0x00, 0xff, 0x00],
[0x00, 0xff, 0xff],
[0xff, 0x00, 0x00],
[0xff, 0x00, 0xff],
[0xff, 0xff, 0x00],
[0xff, 0xff, 0xff],
];
function search($r, $g, $b) {
global $palette;
$d = [];
foreach ($palette as $i => $k) {
$d[$i] = pow($r - $k[0], 2) + pow($g - $k[1], 2) + pow($b - $k[2], 2);
}
asort($d);
$k = key($d);
$t = $palette[$k];
return [$k, $t[0], $t[1], $t[2]];
}
function dither_floyd($im) {
[$sx, $sy] = [imagesx($im), imagesy($im)];
$image = [];
for ($y = 0; $y < $sy; $y++)
for ($x = 0; $x < $sx; $x++) {
$k = imagecolorat($im, $x, $y);
$r = ($k >> 16) & 255;
$g = ($k >> 8) & 255;
$b = ($k) & 255;
$image[$x][$y] = [$r, $g, $b];
}
for ($y = 0; $y < $sy; $y++)
for ($x = 0; $x < $sx; $x++) {
[$r, $g, $b] = $image[$x][$y];
[$pix, $rn, $gn, $bn] = search($r, $g, $b);
$quant_r = ($r - $rn);
$quant_g = ($g - $gn);
$quant_b = ($b - $bn);
if ($x + 1 < $sx) {
$image[$x + 1][$y][0] += ($quant_r * 7.0/16.0);
$image[$x + 1][$y][1] += ($quant_g * 7.0/16.0);
$image[$x + 1][$y][2] += ($quant_b * 7.0/16.0);
}
if ($x - 1 >= 0 && $y + 1 < $sy) {
$image[$x - 1][$y + 1][0] += ($quant_r * 3.0/16.0);
$image[$x - 1][$y + 1][1] += ($quant_g * 3.0/16.0);
$image[$x - 1][$y + 1][2] += ($quant_b * 3.0/16.0);
}
if ($y + 1 < $sy) {
$image[$x][$y + 1][0] += ($quant_r * 5.0/16.0);
$image[$x][$y + 1][1] += ($quant_g * 5.0/16.0);
$image[$x][$y + 1][2] += ($quant_b * 5.0/16.0);
}
if ($x + 1 < $sx && $y + 1 < $sy) {
$image[$x + 1][$y + 1][0] += ($quant_r * 1.0/16.0);
$image[$x + 1][$y + 1][1] += ($quant_g * 1.0/16.0);
$image[$x + 1][$y + 1][2] += ($quant_b * 1.0/16.0);
}
imagesetpixel($im, $x, $y, $rn*65536 + $gn*256 + $bn);
}
return $im;
}
$im = dither_floyd($im);
imagepng($im, $argv[2] ?? 'result.png');