добавлена полная поддержка альфа-канала для индексированных PNG и WebP.

This commit is contained in:
2026-02-28 13:21:50 +05:00
parent 63386ad3b9
commit 06a5830a12

View File

@@ -1,10 +1,15 @@
<?php
/**
* Image_Toolbox.class.php -- PHP image manipulation class
*
* Copyright (C) 2003 Martin Theimer <pappkamerad@decoded.net>
*
*/
/**
* Image_Toolbox.class.php -- PHP image manipulation class
*
* Copyright (C) 2003 Martin Theimer <pappkamerad@decoded.net>
* Refactored for PHP 8.4 (2026) by Alexander Salnikov (aka Repellent)
* * Major updates:
* - Full Alpha Channel support for Indexed PNG & WebP
* - Optimized for GdImage objects (PHP 8+)
* - Modernized memory management and type safety
*/
if (! defined('IMAGE_TOOLBOX_DEFAULT_PNG_QUALITY'))
@@ -465,6 +470,11 @@
*
* @access private
*/
/**
* Generate a new image resource based on the given parameters
*
* @access private
*/
function _addImage($argc, $args)
{
if (($argc == 2 || $argc == 3) && is_int($args[0]) && is_int($args[1]) && (is_string($args[2]) || ! isset($args[2])))
@@ -485,12 +495,22 @@
$this->_img['operator']['resource'] = $functionname($this->_img['operator']['width'], $this->_img['operator']['height']);
// set default type jpg.
if ($this->_imagecreatefunction == 'imagecreatetruecolor')
{
imagealphablending($this->_img['operator']['resource'], false);
imagesavealpha($this->_img['operator']['resource'], true);
if (!isset($args[2]))
{
$transparent = imagecolorallocatealpha($this->_img['operator']['resource'], 255, 255, 255, 127);
imagefill($this->_img['operator']['resource'], 0, 0, $transparent);
}
}
$this->_img['operator']['type'] = 2;
if (isset($args[2]) && is_string($args[2]))
{
//neues bild mit farbe fЃllen
$fillcolor = $this->_hexToPHPColor($args[2]);
imagefill($this->_img['operator']['resource'], 0, 0, $fillcolor);
@@ -504,7 +524,6 @@
}
elseif ($argc == 1 && is_string($args[0]))
{
//bild aus datei laden. width und height original gr”sse
$this->_img['operator'] = $this->_loadFile($args[0]);
$this->_img['operator']['indexedcolors'] = imagecolorstotal($this->_img['operator']['resource']);
@@ -526,7 +545,7 @@
* @param string $filename imagefile to load
* @return array associative array with the loaded filedata
*/
function _loadFile($filename)
function _loadFile($filename)
{
if (file_exists($filename))
{
@@ -540,7 +559,7 @@
? ($filedata['bias'] = IMAGE_TOOLBOX_BIAS_HORIZONTAL)
: ($filedata['bias'] = IMAGE_TOOLBOX_BIAS_VERTICAL);
$filedata['aspectratio'] = $filedata['width'] / $filedata['height'];
$filedata['aspectratio'] = $filedata['width'] / $filedata['height'];
$filedata['type'] = $info[2];
if ($this->_types[$filedata['type']]['supported'] < 1)
@@ -551,63 +570,76 @@
switch ($filedata['type'])
{
case 1:
case 1: // GIF
$dummy = imagecreatefromgif($filename);
$functionname = $this->_imagecreatefunction;
$filedata['resource'] = $functionname($filedata['width'], $filedata['height']);
if ($this->_imagecreatefunction == 'imagecreatetruecolor') {
imagealphablending($filedata['resource'], false);
imagesavealpha($filedata['resource'], true);
$transparent = imagecolorallocatealpha($filedata['resource'], 255, 255, 255, 127);
imagefill($filedata['resource'], 0, 0, $transparent);
}
imagecopy($filedata['resource'], $dummy, 0, 0, 0, 0, $filedata['width'], $filedata['height']);
imagedestroy($dummy);
break;
case 2:
case 2: // JPG
$filedata['resource'] = imagecreatefromjpeg($filename);
break;
case 3:
case 3: // PNG
$dummy = imagecreatefrompng($filename);
if (imagecolorstotal($dummy) != 0)
{
$functionname = $this->_imagecreatefunction;
$filedata['resource'] = $functionname($filedata['width'], $filedata['height']);
if ($this->_imagecreatefunction == 'imagecreatetruecolor') {
imagealphablending($filedata['resource'], false);
imagesavealpha($filedata['resource'], true);
$transparent = imagecolorallocatealpha($filedata['resource'], 255, 255, 255, 127);
imagefill($filedata['resource'], 0, 0, $transparent);
}
imagecopy($filedata['resource'], $dummy, 0, 0, 0, 0, $filedata['width'], $filedata['height']);
imagedestroy($dummy);
}
else
{
$filedata['resource'] = $dummy;
imagealphablending($filedata['resource'], false);
imagesavealpha($filedata['resource'], true);
}
unset($dummy);
break;
case 4:
case 5:
case 6:
case 7:
case 8:
case 9:
case 10:
case 11:
case 12:
case 13:
case 14:
case 15:
case 15:
case 16:
case 17:
case 18:
case 18: // WebP
$dummy = imagecreatefromwebp($filename);
if (imagecolorstotal($dummy) != 0)
{
$functionname = $this->_imagecreatefunction;
$filedata['resource'] = $functionname($filedata['width'], $filedata['height']);
if ($this->_imagecreatefunction == 'imagecreatetruecolor') {
imagealphablending($filedata['resource'], false);
imagesavealpha($filedata['resource'], true);
$transparent = imagecolorallocatealpha($filedata['resource'], 255, 255, 255, 127);
imagefill($filedata['resource'], 0, 0, $transparent);
}
imagecopy($filedata['resource'], $dummy, 0, 0, 0, 0, $filedata['width'], $filedata['height']);
imagedestroy($dummy);
}
else
{
$filedata['resource'] = $dummy;
imagealphablending($filedata['resource'], false);
imagesavealpha($filedata['resource'], true);
}
unset($dummy);
break;
default:
@@ -645,7 +677,7 @@
* @param bool $dither use dither
* @return bool true on success, otherwise false
*/
function output($output_type = false, $output_quality = false, $dither = false)
function output($output_type = false, $output_quality = false, $dither = false)
{
if ($output_type === false)
$output_type = $this->_img['main']['output_type'];
@@ -668,17 +700,25 @@
if ($this->_img['main']['indexedcolors'] == 0)
{
$dummy = imagecreatetruecolor($this->_img['main']['width'], $this->_img['main']['height']);
imagealphablending($dummy, false);
imagesavealpha($dummy, true);
$transparent = imagecolorallocatealpha($dummy, 255, 255, 255, 127);
imagefill($dummy, 0, 0, $transparent);
imagecopy($dummy, $this->_img['main']['resource'], 0, 0, 0, 0, $this->_img['main']['width'], $this->_img['main']['height']);
if ($output_quality === false)
{
$output_quality = IMAGE_TOOLBOX_DEFAULT_8BIT_COLORS;
}
imagetruecolortopalette($dummy, $dither, $output_quality);
imagegif($dummy);
imagedestroy($dummy);
}
else
{
imagegif($this->_img['main']['resource']);
}
imagegif($dummy);
imagedestroy($dummy);
}
else
{
@@ -701,9 +741,7 @@
header ('Content-type: ' . $this->_types[$output_type]['mime']);
if ($output_quality === false)
{
$output_quality = IMAGE_TOOLBOX_DEFAULT_JPEG_QUALITY;
}
imagejpeg($this->_img['main']['resource'], null, $output_quality);
break;
@@ -721,13 +759,14 @@
}
header ('Content-type: ' . $this->_types[$output_type]['mime']);
imagealphablending($this->_img['main']['resource'], false);
imagesavealpha($this->_img['main']['resource'], true);
if (version_compare(PHP_VERSION, '5.1.2', '>='))
{
if ($output_quality === false)
{
$output_quality = IMAGE_TOOLBOX_DEFAULT_PNG_QUALITY;
}
imagepng($this->_img['main']['resource'], null, $output_quality);
}
@@ -754,18 +793,25 @@
if ($this->_img['main']['indexedcolors'] == 0)
{
$dummy = imagecreatetruecolor($this->_img['main']['width'], $this->_img['main']['height']);
imagealphablending($dummy, false);
imagesavealpha($dummy, true);
$transparent = imagecolorallocatealpha($dummy, 255, 255, 255, 127);
imagefill($dummy, 0, 0, $transparent);
imagecopy($dummy, $this->_img['main']['resource'], 0, 0, 0, 0, $this->_img['main']['width'], $this->_img['main']['height']);
imagetruecolortopalette($dummy, $dither, 255);
}
else
{
$dummy = $this->_img['main']['resource'];
imagesavealpha($dummy, true);
}
if (version_compare(PHP_VERSION, '5.1.2', '>='))
{
if ($output_quality === false)
{
$output_quality = IMAGE_TOOLBOX_DEFAULT_PNG_QUALITY;
}
imagepng($dummy, null, $output_quality);
}
@@ -774,27 +820,15 @@
imagepng($dummy);
}
imagedestroy($dummy);
if ($this->_img['main']['indexedcolors'] == 0)
imagedestroy($dummy);
}
else
{
imagepng($this->_img['main']['resource']);
}
break;
case 5:
case 6:
case 7:
case 8:
case 9:
case 10:
case 11:
case 12:
case 13:
case 14:
case 15:
case 15:
case 16:
case 17:
case 18:
case '18':
case 'webp':
@@ -807,13 +841,14 @@
}
header ('Content-type: ' . $this->_types[$output_type]['mime']);
imagealphablending($this->_img['main']['resource'], false);
imagesavealpha($this->_img['main']['resource'], true);
if (version_compare(PHP_VERSION, '5.1.2', '>='))
{
if ($output_quality === false)
{
$output_quality = IMAGE_TOOLBOX_DEFAULT_WEBP_QUALITY;
}
imagewebp($this->_img['main']['resource'], null, $output_quality);
}
@@ -853,10 +888,11 @@
* @param bool $dither use dither
* @return bool true on success, otherwise false
*/
function save ($filename, $output_type = false, $output_quality = false, $dither = false)
function save ($filename, $output_type = false, $output_quality = false, $dither = false)
{
if ($output_type === false)
$output_type = $this->_img['main']['output_type'];
switch ($output_type)
{
case 1:
@@ -874,20 +910,29 @@
if ($this->_img['main']['indexedcolors'] == 0)
{
$dummy = imagecreatetruecolor($this->_img['main']['width'], $this->_img['main']['height']);
imagealphablending($dummy, false);
imagesavealpha($dummy, true);
$transparent = imagecolorallocatealpha($dummy, 255, 255, 255, 127);
imagefill($dummy, 0, 0, $transparent);
imagecopy($dummy, $this->_img['main']['resource'], 0, 0, 0, 0, $this->_img['main']['width'], $this->_img['main']['height']);
if ($output_quality === false)
$output_quality = IMAGE_TOOLBOX_DEFAULT_8BIT_COLORS;
imagetruecolortopalette($dummy, $dither, $output_quality);
imagegif($dummy, $filename);
imagedestroy($dummy);
}
else
{
imagegif($this->_img['main']['resource'], $filename);
}
imagegif($dummy, $filename);
imagedestroy($dummy);
}
else
{
imagegif($this->_img['main']['resource']);
imagegif($this->_img['main']['resource'], $filename);
}
break;
@@ -925,6 +970,9 @@
return null;
}
imagealphablending($this->_img['main']['resource'], false);
imagesavealpha($this->_img['main']['resource'], true);
if (version_compare(PHP_VERSION, '5.1.2', '>='))
{
if ($output_quality === false)
@@ -953,9 +1001,22 @@
if ($this->_img['main']['indexedcolors'] == 0)
{
$dummy = imagecreatetruecolor($this->_img['main']['width'], $this->_img['main']['height']);
imagealphablending($dummy, false);
imagesavealpha($dummy, true);
$transparent = imagecolorallocatealpha($dummy, 255, 255, 255, 127);
imagefill($dummy, 0, 0, $transparent);
imagecopy($dummy, $this->_img['main']['resource'], 0, 0, 0, 0, $this->_img['main']['width'], $this->_img['main']['height']);
imagetruecolortopalette($dummy, $dither, 255);
}
else
{
$dummy = $this->_img['main']['resource'];
}
imagealphablending($dummy, false);
imagesavealpha($dummy, true);
if (version_compare(PHP_VERSION, '5.1.2', '>='))
{
@@ -968,7 +1029,9 @@
{
imagepng($dummy, $filename);
}
imagedestroy($dummy);
if ($this->_img['main']['indexedcolors'] == 0)
imagedestroy($dummy);
}
else
{
@@ -976,20 +1039,6 @@
}
break;
case 5:
case 6:
case 7:
case 8:
case 9:
case 10:
case 11:
case 12:
case 13:
case 14:
case 15:
case 15:
case 16:
case 17:
case 18:
case '18':
case 'webp':
@@ -1000,6 +1049,9 @@
trigger_error($this->_error_prefix . 'Imagetype ('.$this->_types[$output_type]['ext'].') not supported for creating/writing.', E_USER_ERROR);
return null;
}
imagealphablending($this->_img['main']['resource'], false);
imagesavealpha($this->_img['main']['resource'], true);
if (version_compare(PHP_VERSION, '5.1.2', '>='))
{
@@ -1089,7 +1141,7 @@
* @param string $bgcolor background fillcolor (hexformat, e.g. '#FF0000')
* @return bool true on success, otherwise false
*/
function newOutputSize($width, $height, $mode = 0, $autorotate = false, $bgcolor = '#000000')
function newOutputSize($width, $height, $mode = 0, $autorotate = false, $bgcolor = '#000000')
{
if ($width > 0 && $height > 0 && is_int($width) && is_int($height))
{
@@ -1279,13 +1331,11 @@
if ($width != 0)
{
$this->_img['target']['width'] = $width;
// Применяем (integer) ко всему float-выражению
$this->_img['target']['height'] = (integer) ($width / $this->_img['main']['aspectratio']);
}
else
{
$this->_img['target']['height'] = $height;
// Применяем (integer) ко всему float-выражению
$this->_img['target']['width'] = (integer) ($height * $this->_img['main']['aspectratio']);
}
@@ -1305,72 +1355,64 @@
//create resized picture
$functionname = $this->_imagecreatefunction;
// ИСПРАВЛЕНИЕ: Явное приведение к (int) перед сложением
$dummy = $functionname((int) $this->_img['target']['width'] + 1, (int) $this->_img['target']['height'] + 1);
if ($this->_img['main']['type'] == 3)
if ($this->_imagecreatefunction == 'imagecreatetruecolor')
{
// turning off alpha blending (to ensure alpha channel information
// is preserved, rather than removed (blending with the rest of the
// image in the form of black))
imagealphablending($dummy, false);
// turning on alpha channel information saving (to ensure the full range
// of transparency is preserved)
imagesavealpha($dummy, true);
$transparent = imagecolorallocatealpha($dummy, 255, 255, 255, 127);
imagefill($dummy, 0, 0, $transparent);
}
$resize_function = $this->_resize_function;
// ИСПРАВЛЕНИЕ: Принудительное приведение всех координат и размеров к int
$resize_function(
$dummy,
$this->_img['main']['resource'],
0, 0,
(int) $cpy_w_offset,
(int) $cpy_h_offset,
(int) $this->_img['target']['width'],
(int) $this->_img['target']['height'],
(int) $cpy_w,
(int) $cpy_h
);
$resize_function(
$dummy,
$this->_img['main']['resource'],
0, 0,
(int) $cpy_w_offset,
(int) $cpy_h_offset,
(int) $this->_img['target']['width'],
(int) $this->_img['target']['height'],
(int) $cpy_w,
(int) $cpy_h
);
if ($mode == 2)
{
$this->_img['target']['resource'] = $functionname($width, $height);
$fillcolor = $this->_hexToPHPColor($bgcolor);
imagefill($this->_img['target']['resource'], 0, 0, $fillcolor);
}
else
{
// ИСПРАВЛЕНИЕ: Принудительное приведение ширины и высоты к int
$this->_img['target']['resource'] = $functionname((int) $this->_img['target']['width'], (int) $this->_img['target']['height']);
if ($this->_imagecreatefunction == 'imagecreatetruecolor') {
imagealphablending($this->_img['target']['resource'], false);
imagesavealpha($this->_img['target']['resource'], true);
$transparent = imagecolorallocatealpha($this->_img['target']['resource'], 255, 255, 255, 127);
imagefill($this->_img['target']['resource'], 0, 0, $transparent);
}
$cpy_w_offset2 = 0;
$cpy_h_offset2 = 0;
}
if ($this->_img['main']['type'] == 3)
{
// turning off alpha blending (to ensure alpha channel information
// is preserved, rather than removed (blending with the rest of the
// image in the form of black))
imagealphablending($this->_img['target']['resource'], false);
// turning on alpha channel information saving (to ensure the full range
// of transparency is preserved)
imagesavealpha($this->_img['target']['resource'], true);
}
// ИСПРАВЛЕНИЕ: Принудительное приведение всех координат и размеров к int
imagecopy(
$this->_img['target']['resource'],
$dummy,
(int) $cpy_w_offset2,
(int) $cpy_h_offset2,
0, 0,
(int) $this->_img['target']['width'],
(int) $this->_img['target']['height']
);
imagecopy(
$this->_img['target']['resource'],
$dummy,
(int) $cpy_w_offset2,
(int) $cpy_h_offset2,
0, 0,
(int) $this->_img['target']['width'],
(int) $this->_img['target']['height']
);
imagedestroy($dummy);
if ($mode == 2)
{
@@ -1484,7 +1526,6 @@ imagecopy(
case 'middle':
case 'center':
// ИСПРАВЛЕНО: Явное приведение всего выражения к целому числу
$dst_x = (integer)((($this->_img['main']['width'] / 2) - ($this->_img['operator']['width'] / 2)) + $xalign_offset);
$src_x = 0;
$src_w = $this->_img['operator']['width'];
@@ -1525,7 +1566,6 @@ imagecopy(
case 'middle':
case 'center':
// ИСПРАВЛЕНО: Явное приведение всего выражения к целому числу
$dst_y = (integer)((($this->_img['main']['height'] / 2) - ($this->_img['operator']['height'] / 2)) + $yalign_offset);
$src_y = 0;
$src_h = $this->_img['operator']['height'];