mirror of
https://github.com/ellite/Wallos.git
synced 2026-07-30 17:55:53 -04:00
119 lines
3.8 KiB
PHP
119 lines
3.8 KiB
PHP
<?php
|
|
/*
|
|
Makes every pixel within the tolerance of the given color fully transparent.
|
|
Used by the logo/icon upload endpoints for background removal.
|
|
*/
|
|
function gdRemoveBackgroundColor($image, $backgroundRed, $backgroundGreen, $backgroundBlue, $fuzz = 0.1)
|
|
{
|
|
// On palette images imagecolorat() returns palette indexes, not RGB values
|
|
if (!imageistruecolor($image)) {
|
|
imagepalettetotruecolor($image);
|
|
}
|
|
|
|
$width = imagesx($image);
|
|
$height = imagesy($image);
|
|
|
|
imagealphablending($image, false);
|
|
imagesavealpha($image, true);
|
|
|
|
$transparent = imagecolorallocatealpha($image, 0, 0, 0, 127);
|
|
$maxChannelDelta = (int) round(255 * $fuzz);
|
|
$maxDistanceSquared = 3 * $maxChannelDelta * $maxChannelDelta;
|
|
|
|
for ($y = 0; $y < $height; $y++) {
|
|
for ($x = 0; $x < $width; $x++) {
|
|
$color = imagecolorat($image, $x, $y);
|
|
$red = ($color >> 16) & 0xFF;
|
|
$green = ($color >> 8) & 0xFF;
|
|
$blue = $color & 0xFF;
|
|
|
|
$distanceSquared = ($red - $backgroundRed) ** 2
|
|
+ ($green - $backgroundGreen) ** 2
|
|
+ ($blue - $backgroundBlue) ** 2;
|
|
|
|
if ($distanceSquared <= $maxDistanceSquared) {
|
|
imagesetpixel($image, $x, $y, $transparent);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function gdCropTransparent($image, $padding = 2)
|
|
{
|
|
if (!imageistruecolor($image)) {
|
|
imagepalettetotruecolor($image);
|
|
}
|
|
|
|
// imagecrop() (called below) doesn't reliably preserve the alpha channel
|
|
// on images that went through imagepalettetotruecolor() at any point --
|
|
// even once imageistruecolor() reports true and imagecolorat() reads
|
|
// back the correct alpha, imagecrop() can still flatten transparent
|
|
// pixels to opaque black. Re-copying onto a freshly created truecolor+
|
|
// alpha canvas unconditionally sidesteps this, since a "genuinely native"
|
|
// truecolor image doesn't have the issue.
|
|
$width = imagesx($image);
|
|
$height = imagesy($image);
|
|
$fixed = imagecreatetruecolor($width, $height);
|
|
imagealphablending($fixed, false);
|
|
imagesavealpha($fixed, true);
|
|
$transparent = imagecolorallocatealpha($fixed, 0, 0, 0, 127);
|
|
imagefill($fixed, 0, 0, $transparent);
|
|
imagealphablending($fixed, false);
|
|
imagecopy($fixed, $image, 0, 0, 0, 0, $width, $height);
|
|
imagedestroy($image);
|
|
$image = $fixed;
|
|
|
|
$top = null;
|
|
$bottom = null;
|
|
$left = null;
|
|
$right = null;
|
|
|
|
for ($y = 0; $y < $height; $y++) {
|
|
for ($x = 0; $x < $width; $x++) {
|
|
$color = imagecolorat($image, $x, $y);
|
|
$alpha = ($color >> 24) & 0x7F; // 0 is fully opaque, 127 is fully transparent
|
|
if ($alpha < 125) { // Pixel is not fully transparent (threshold 125 out of 127)
|
|
if ($top === null) {
|
|
$top = $y;
|
|
}
|
|
$bottom = $y;
|
|
if ($left === null || $x < $left) {
|
|
$left = $x;
|
|
}
|
|
if ($right === null || $x > $right) {
|
|
$right = $x;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ($top === null) {
|
|
// Image is completely transparent, do nothing
|
|
return $image;
|
|
}
|
|
|
|
$newWidth = $right - $left + 1;
|
|
$newHeight = $bottom - $top + 1;
|
|
|
|
$left = max(0, $left - $padding);
|
|
$top = max(0, $top - $padding);
|
|
$newWidth = min($width - $left, $newWidth + ($padding * 2));
|
|
$newHeight = min($height - $top, $newHeight + ($padding * 2));
|
|
|
|
$cropped = imagecrop($image, [
|
|
'x' => $left,
|
|
'y' => $top,
|
|
'width' => $newWidth,
|
|
'height' => $newHeight
|
|
]);
|
|
|
|
if ($cropped !== false) {
|
|
imagealphablending($cropped, false);
|
|
imagesavealpha($cropped, true);
|
|
imagedestroy($image);
|
|
return $cropped;
|
|
}
|
|
|
|
return $image;
|
|
}
|