diff --git a/class/class.thumbnail.php b/class/class.thumbnail.php index a8d7693..6f9f505 100644 --- a/class/class.thumbnail.php +++ b/class/class.thumbnail.php @@ -1,10 +1,15 @@ - * - */ + +/** + * Image_Toolbox.class.php -- PHP image manipulation class + * + * Copyright (C) 2003 Martin Theimer + * 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'];