§ Дизеринг Флойда-Штейнберга
Как пользоваться:
php make16.php [infile] [outfile]
1<?php
2$im = imagecreatefrompng($argv[1] ?? "ping.png");
3
4
5$palette = [
6 [0x00, 0x00, 0x00],
7 [0x00, 0x00, 0x80],
8 [0x00, 0x80, 0x00],
9 [0x00, 0x80, 0x80],
10 [0x80, 0x00, 0x00],
11 [0x80, 0x00, 0x80],
12 [0x80, 0x80, 0x00],
13 [0xcc, 0xcc, 0xcc],
14 [0x80, 0x80, 0x80],
15 [0x00, 0x00, 0xff],
16 [0x00, 0xff, 0x00],
17 [0x00, 0xff, 0xff],
18 [0xff, 0x00, 0x00],
19 [0xff, 0x00, 0xff],
20 [0xff, 0xff, 0x00],
21 [0xff, 0xff, 0xff],
22];
23
24function search($r, $g, $b) {
25
26 global $palette;
27
28 $d = [];
29 foreach ($palette as $i => $k) {
30 $d[$i] = pow($r - $k[0], 2) + pow($g - $k[1], 2) + pow($b - $k[2], 2);
31 }
32 asort($d);
33 $k = key($d);
34 $t = $palette[$k];
35 return [$k, $t[0], $t[1], $t[2]];
36}
37
38function dither_floyd($im) {
39
40 [$sx, $sy] = [imagesx($im), imagesy($im)];
41
42 $image = [];
43 for ($y = 0; $y < $sy; $y++)
44 for ($x = 0; $x < $sx; $x++) {
45
46 $k = imagecolorat($im, $x, $y);
47 $r = ($k >> 16) & 255;
48 $g = ($k >> 8) & 255;
49 $b = ($k) & 255;
50 $image[$x][$y] = [$r, $g, $b];
51 }
52
53 for ($y = 0; $y < $sy; $y++)
54 for ($x = 0; $x < $sx; $x++) {
55
56 [$r, $g, $b] = $image[$x][$y];
57 [$pix, $rn, $gn, $bn] = search($r, $g, $b);
58
59
60 $quant_r = ($r - $rn);
61 $quant_g = ($g - $gn);
62 $quant_b = ($b - $bn);
63
64
65
66
67
68 if ($x + 1 < $sx) {
69 $image[$x + 1][$y][0] += ($quant_r * 7.0/16.0);
70 $image[$x + 1][$y][1] += ($quant_g * 7.0/16.0);
71 $image[$x + 1][$y][2] += ($quant_b * 7.0/16.0);
72 }
73
74
75 if ($x - 1 >= 0 && $y + 1 < $sy) {
76 $image[$x - 1][$y + 1][0] += ($quant_r * 3.0/16.0);
77 $image[$x - 1][$y + 1][1] += ($quant_g * 3.0/16.0);
78 $image[$x - 1][$y + 1][2] += ($quant_b * 3.0/16.0);
79 }
80
81
82 if ($y + 1 < $sy) {
83 $image[$x][$y + 1][0] += ($quant_r * 5.0/16.0);
84 $image[$x][$y + 1][1] += ($quant_g * 5.0/16.0);
85 $image[$x][$y + 1][2] += ($quant_b * 5.0/16.0);
86 }
87
88
89 if ($x + 1 < $sx && $y + 1 < $sy) {
90 $image[$x + 1][$y + 1][0] += ($quant_r * 1.0/16.0);
91 $image[$x + 1][$y + 1][1] += ($quant_g * 1.0/16.0);
92 $image[$x + 1][$y + 1][2] += ($quant_b * 1.0/16.0);
93 }
94
95 imagesetpixel($im, $x, $y, $rn*65536 + $gn*256 + $bn);
96 }
97
98 return $im;
99}
100
101$im = dither_floyd($im);
102imagepng($im, $argv[2] ?? 'result.png');