php class ImageDither public static function plus_truncate_uchar if 0x

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
<?php
class ImageDither {
public static function plus_truncate_uchar($a, $b) {
if (($a & 0xff) + $b < 0) {
return 0;
} else {
if (($a & 0xff) + $b > 255) {
return 255;
} else {
return $a + $b;
}
}
}
public static function findNearestColor($color, $palette) {
$minDistanceSquared = 255 * 255 + 255 * 255 + 255 * 255 + 1;
$bestIndex = 0;
for ($i = 0; $i < count($palette); $i++) {
$Rdiff = ($color -> channels[0] & 0xff) - ($palette[$i] -> channels[0] & 0xff);
$Gdiff = ($color -> channels[1] & 0xff) - ($palette[$i] -> channels[1] & 0xff);
$Bdiff = ($color -> channels[2] & 0xff) - ($palette[$i] -> channels[2] & 0xff);
$distanceSquared = $Rdiff * $Rdiff + $Gdiff * $Gdiff + $Bdiff * $Bdiff;
if ($distanceSquared < $minDistanceSquared) {
$minDistanceSquared = $distanceSquared;
$bestIndex = $i;
}
}
return $bestIndex;
}
public static function floydSteinbergDither($image, $palette) {
$result = array();
for ($y = 0; $y < count($image); $y++) {
for ($x = 0; $x < count($image[$y]); $x++) {
$currentPixel = $image[$y][$x];
$index = self::findNearestColor($currentPixel, $palette);
$result[$y][$x] = $index;
for ($i = 0; $i < 3; $i++) {
$error = ($currentPixel -> channels[$i] & 0xff) - ($palette[$index] -> channels[$i] & 0xff);
if ($x + 1 < count($image[0])) {
$image[$y][$x + 1] -> channels[$i] = self::plus_truncate_uchar($image[$y][$x + 1] -> channels[$i], ($error * 7) >> 4);
}
if ($y + 1 < count($image)) {
if ($x - 1 > 0) {
$image[$y + 1][$x - 1] -> channels[$i] = self::plus_truncate_uchar($image[$y + 1][$x - 1] -> channels[$i], ($error * 3) >> 4);
}
$image[$y + 1][$x] -> channels[$i] = self::plus_truncate_uchar($image[$y + 1][$x + 0] -> channels[$i], ($error * 5) >> 4);
if ($x + 1 < count($image[0])) {
$image[$y + 1][$x + 1] -> channels[i] = self::plus_truncate_uchar($image[$y + 1][$x + 1] -> channels[$i], ($error * 1) >> 4);
}
}
}
}
}
return $result;
}
public static function toDither($img) {
$w = imagesx($img);
$h = imagesy($img);
$new_img = imagecreatetruecolor($w, $h);
$image = array();
$i = 0;
for ($y = 0; $y < $h; $y++) {
for ($x = 0; $x < $w; $x++) {
$c = imagecolorat($img, $x, $y);
$image[$y][$x] = new RGBTriple();
$image[$y][$x] -> channels[0] = ($c >> 16 & 0xff);
$image[$y][$x] -> channels[1] = ($c >> 8 & 0xff);
$image[$y][$x] -> channels[2] = ($c & 0xff);
$i++;
}
}
$palette = array(
new RGBTriple(0, 0, 0),
new RGBTriple(255, 255, 255)
);
$result = self::floydSteinbergDither($image, $palette);
$i = 0;
for ($y = 0; $y < $h; $y++) {
for ($x = 0; $x < $w; $x++) {
$color = (int)($palette[$result[$y][$x]] -> channels[0]) << 16
| (int)($palette[$result[$y][$x]] -> channels[1]) << 8
| (int)($palette[$result[$y][$x]] -> channels[2]);
imagesetpixel($new_img, $x, $y, $color);
$i++;
}
}
return $new_img;
}
}
class RGBTriple {
public $channels;
public function __construct($R = false, $G = false, $B = false) {
if($R !== false && $G !== false && $B !== false) {
$this -> channels = array((int)$R, (int)$G, (int)$B);
} else {
$this -> channels = array(0, 0, 0);
}
}
}
?>