Система управления «Сайт PRO»
Версия 20240107

Класс Thumb

Объект $Thumb: Cms\Root\Thumb наследует Cms\Site\Base

Объект для работы с изображениями

Исходный код
class Thumb extends \Cms\Site\Base { … }

Свойства

$quality

$Thumb->quality = 100;

JPEG quality.

Исходный код
    var $quality = 100;

$Request

$Thumb->Request
Исходный код
    var $Request;

Методы

route()

$Thumb->route($Request);

Обработка запроса

Параметры:

ИмяОписание
$Request

Cms\Root\Main\Request

Возвращает: true – запрос обработан; null – запрос не обработан

Исходный код
    function route($Request) {
        if (strpos($Request->getSelf(), '/thumb.') !== false) {
            $this->Request = $Request;
            $Request->setStatus(200);
            if (!$this->display()) $this->fallbackOutput();
            return true;
        }
    }

display()

$Thumb->display($uri=null, $cache=true);
Исходный код
    function display($uri = null, $cache = true) {
        $App = $this->App();
        $root = $this->root();
        $App->largeMemoryLimit();
        if ($uri === null) $uri = rawurldecode(array_shift(explode('?', $_SERVER['REQUEST_URI'], 2)));
        if ($this->imageCheck($uri)) return $this->fileOutput($uri);
        if (($x = strrpos($uri, '/')) !== false) {
            $dir = substr($uri, 0, $x);
            $image = substr($uri, $x + 1);
        } else {
            $dir = '';
            $image = $uri;
        }
        $parts = explode('.', $image);
        if (!in_array($ext = array_pop($parts), array( 'svg', 'jpg', 'webp' ))) return false;
        if (array_shift($parts) != 'thumb') return false;
        $count = count($parts);
        if ($count == 0) return false;
        if ($count == 1) return $this->fileOutput($dir . '/' . $parts[0]);
        $transform = explode('_', array_pop($parts));
        $last = $parts[$count - 2];
        if (($x = strrpos($last, '_')) !== false) $parts[$count - 2] = substr($last, 0, $x) . '.' . substr($last, $x + 1);
        $file = $dir . '/' . implode('.', $parts);
        if (!is_file($root . $file)) return false;
        $src = $this->imageRead($root . $file);
        if (!$src) {
            if ($parts[count($parts) - 1] === 'svg') return $this->fileOutput($file, 'image/svg+xml');
            return false;
        }
        foreach ($transform as $tr) {
            if (!strlen($tr)) {
                continue;
            } elseif (preg_match('~^r(-?\\d+)x(-?\\d+)(x([a-zA-Z0-9]{6}))?$~', $tr, $m)) {
                list($w, $h) = $this->imageGeometry($src, $m[1], $m[2]);
                $c = $this->imageColor($m[4]);
                $this->imageResize($src, $w, $h, $c);
            } elseif (preg_match('~^rc(-?\\d+)x(-?\\d+)(x([a-zA-Z0-9]{6}))?$~', $tr, $m)) {
                list($w, $h) = $this->imageGeometry($src, $m[1], $m[2]);
                $c = $this->imageColor($m[4]);
                $this->imageResizeCrop($src, $w, $h, $c);
            } elseif(preg_match('~^rf(-?\\d+)x(-?\\d+)(x([a-zA-Z0-9]{6}))?$~', $tr, $m)) {
                list($w, $h) = $this->imageGeometry($src, $m[1], $m[2]);
                $c = $this->imageColor($m[4]);
                $this->imageResizeField($src, $w, $h, $c);
            } elseif(preg_match('~^rh(-?\\d+)x(-?\\d+)(x([a-zA-Z0-9]{6}))?$~', $tr, $m)) {
                list($w, $h) = $this->imageGeometry($src, $m[1], $m[2]);
                $c = $this->imageColor($m[4]);
                $this->imageResizeHorizontal($src, $w, $h, $c);
            } elseif(preg_match('~^rv(-?\\d+)x(-?\\d+)(x([a-zA-Z0-9]{6}))?$~', $tr, $m)) {
                list($w, $h) = $this->imageGeometry($src, $m[1], $m[2]);
                $c = $this->imageColor($m[4]);
                $this->imageResizeVertical($src, $w, $h, $c);
            } elseif (preg_match('~^c(-?\\d+)x(-?\\d+)(x(-?\\d+)x(-?\\d+))?$~', $tr, $m)) {
                list($w, $h) = $this->imageGeometry($src, $m[1], $m[2]);
                $x = intval($m[4]);
                $y = intval($m[5]);
                $c = $this->imageColor(null);
                $this->imageCrop($src, $w, $h, $x, $y, $c);
            } elseif (preg_match('~^q(\\d+)$~', $tr, $m)) {
                $this->quality = intval($m[1]);
            }
        }
        if ($cache && $this->imageWrite($src, $root . $uri)) {
            $contentType = 'image/jpeg';
            if ($ext === 'svg') $contentType = 'image/svg+xml';
            if ($ext === 'webp') $contentType = 'image/webp';
            $this->fileOutput($uri, $contentType);
        } else {
            $this->imageOutput($src);
        }
        return true;
    }

imageColor()

$Thumb->imageColor($c);
Исходный код
    function imageColor($c) {
        if (!strlen($c)) return array(0xff, 0xff, 0xff);
        return array(
            hexdec(substr($c, 0, 2)),
            hexdec(substr($c, 2, 2)),
            hexdec(substr($c, 4, 2)),
        );
    }

imageRead()

$Thumb->imageRead($path, $info=null);
Исходный код
    function imageRead($path, $info = null) {
        if ($info === null) $info = @getimagesize($path);
        if (!$info) return;
        $type = $info[2];
        if ($type === IMAGETYPE_JPEG) {
            return @imagecreatefromjpeg($path);
        }
        if ($type === IMAGETYPE_PNG) {
            return @imagecreatefrompng($path);
        }
        if ($type === IMAGETYPE_GIF) {
            return @imagecreatefromgif($path);
        }
        if (defined('IMAGETYPE_AVIF') && $type === constant('IMAGETYPE_AVIF') && function_exists('imagecreatefromavif')) {
            return @imagecreatefromavif($path);
        }
        if (defined('IMAGETYPE_WEBP') && $type === constant('IMAGETYPE_WEBP') && function_exists('imagecreatefromwebp')) {
            return @imagecreatefromwebp($path);
        }
        if (defined('IMAGETYPE_BMP') && $type === constant('IMAGETYPE_BMP') && function_exists('imagecreatefrombmp')) {
            return @imagecreatefrombmp($path);
        }
        if (defined('IMAGETYPE_XBM') && $type === constant('IMAGETYPE_XBM') && function_exists('imagecreatefromxbm')) {
            return @imagecreatefromxbm($path);
        }
        if (defined('IMAGETYPE_WBMP') && $type === constant('IMAGETYPE_WBMP') && function_exists('imagecreatefromwbmp')) {
            return @imagecreatefromwbmp($path);
        }
        return $this->imageReadMagick($path);
    }

imageWrite()

$Thumb->imageWrite(&$src, $path);
Исходный код
    function imageWrite(&$src, $path) {
        if (substr($path, -5) === '.webp')
        if (@imagewebp($src, $path, $this->quality)) {
            @chmod($path, 0666);
            imagedestroy($src);
            return true;
        }
        if (@imagejpeg($src, $path, $this->quality)) {
            @chmod($path, 0666);
            imagedestroy($src);
            return true;
        }
    }

imageOutput()

$Thumb->imageOutput(&$src);
Исходный код
    function imageOutput(&$src) {
        $this->Request->addHeader('Content-Type: image/jpeg');
        @imagejpeg($src, null, $this->quality);
        imagedestroy($src);
        return true;
    }

imageReadMagick()

$Thumb->imageReadMagick($path);
Исходный код
    function imageReadMagick($path) {
        if (class_exists('Imagick')) {
            try {
                $src = new \Imagick;
                @$src->readImage($path);
                @$src->setImageFormat('png');
                $dst = @imagecreatefromstring($src->getImageBlob());
                @$src->clear();
                return $dst;
            } catch (\Exception $e) { }
        }
    }

imageGeometry()

$Thumb->imageGeometry(&$src, $w, $h);
Исходный код
    function imageGeometry(&$src, $w, $h) {
        $w = intval($w);
        $h = intval($h);
        if (!$w && !$h) {
            $w = imagesx($src);
            $h = imagesy($src);
        } elseif (!$w) {
            $w = intval(round($h * imagesx($src) / imagesy($src)));
        } elseif (!$h) {
            $h = intval(round($w * imagesy($src) / imagesx($src)));
        }
        return array($w, $h);
    }

imageResize()

$Thumb->imageResize(&$src, $w, $h, $c);
Исходный код
    function imageResize(&$src, $w, $h, $c) {
        if ($dst = imagecreatetruecolor($w, $h)) {
            imagefill($dst, 0, 0, imagecolorallocate($dst, $c[0], $c[1], $c[2]));
            if ($w == imagesx($src) && $h == imagesy($src)) {
                imagecopy($dst, $src, 0, 0, 0, 0, $w, $h);
            } else {
                imagecopyresampled($dst, $src, 0, 0, 0, 0, $w, $h, imagesx($src), imagesy($src));
                $this->imageSharpen($dst);
            }
            imagedestroy($src);
            $src = $dst;
            return true;
        }
    }

imageResizeCrop()

$Thumb->imageResizeCrop(&$src, $w, $h, $c);
Исходный код
    function imageResizeCrop(&$src, $w, $h, $c) {
        if ($dst = imagecreatetruecolor($w, $h)) {
            $width = imagesx($src);
            $height = imagesy($src);
            imagefill($dst, 0, 0, imagecolorallocate($dst, $c[0], $c[1], $c[2]));
            if ($w / $h < $width / $height) {
                imagecopyresampled($dst, $src, ($w - $h * $width / $height) / 2, 0, 0, 0, $h * $width / $height, $h, $width, $height);
            } elseif ($w / $h > $width / $height) {
                imagecopyresampled($dst, $src, 0, ($h - $w * $height / $width) / 2, 0, 0, $w, $w * $height / $width, $width, $height);
            } else {
                imagecopyresampled($dst, $src, 0, 0, 0, 0, $w, $h, $width, $height);
            }
            imagedestroy($src);
            $src = $dst;
            $this->imageSharpen($src);
            return true;
        }
    }

imageResizeField()

$Thumb->imageResizeField(&$src, $w, $h, $c);
Исходный код
    function imageResizeField(&$src, $w, $h, $c) {
        if ($dst = imagecreatetruecolor($w, $h)) {
            $width = imagesx($src);
            $height = imagesy($src);
            imagefill($dst, 0, 0, imagecolorallocate($dst, $c[0], $c[1], $c[2]));
            if ($w / $h > $width / $height) {
                imagecopyresampled($dst, $src, ($w - $h * $width / $height) / 2, 0, 0, 0, $h * $width / $height, $h, $width, $height);
            } elseif ($w / $h < $width / $height) {
                imagecopyresampled($dst, $src, 0, ($h - $w * $height / $width) / 2, 0, 0, $w, $w * $height / $width, $width, $height);
            } else {
                imagecopyresampled($dst, $src, 0, 0, 0, 0, $w, $h, $width, $height);
            }
            imagedestroy($src);
            $src = $dst;
            $this->imageSharpen($src);
            return true;
        }
    }

imageCrop()

$Thumb->imageCrop(&$src, $w, $h, $x, $y, $c);
Исходный код
    function imageCrop(&$src, $w, $h, $x, $y, $c) {
        if ($dst = imagecreatetruecolor($w, $h)) {
            imagefill($dst, 0, 0, imagecolorallocate($dst, $c[0], $c[1], $c[2]));
            imagecopy($dst, $src, 0, 0, $x, $y, $w, $h);
            imagedestroy($src);
            $src = $dst;
            return true;
        }
    }

imageResizeHorizontal()

$Thumb->imageResizeHorizontal(&$src, $w, $h, $c);
Исходный код
    function imageResizeHorizontal(&$src, $w, $h, $c) {
        if ($dst = imagecreatetruecolor($w, $h)) {
            $width = imagesx($src);
            $height = imagesy($src);
            imagefill($dst, 0, 0, imagecolorallocate($dst, $c[0], $c[1], $c[2]));
            if ($w / $h > $width / $height) {
                imagecopyresampled($dst, $src, round(($w - $h * $width / $height) / 2), 0, 0, 0, round($h * $width / $height), $h, $width, $height);
            } elseif ($w / $h < $width / $height) {
                imagecopyresampled($dst, $src, round(($w - $h * $width / $height) / 2), 0, 0, 0, round($h * $width / $height), $h, $width, $height);
            } else {
                imagecopyresampled($dst, $src, 0, 0, 0, 0, $w, $h, $width, $height);
            }
            imagedestroy($src);
            $src = $dst;
            $this->imageSharpen($src);
            return true;
        }
    }

imageResizeVertical()

$Thumb->imageResizeVertical(&$src, $w, $h, $c);
Исходный код
    function imageResizeVertical(&$src, $w, $h, $c) {
        if ($dst = imagecreatetruecolor($w, $h)) {
            $width = imagesx($src);
            $height = imagesy($src);
            imagefill($dst, 0, 0, imagecolorallocate($dst, $c[0], $c[1], $c[2]));
            if ($w / $h > $width / $height) {
                imagecopyresampled($dst, $src, 0, round(($h - $w * $height / $width) / 2), 0, 0, $w, round($w * $height / $width), $width, $height);
            } elseif ($w / $h < $width / $height) {
                imagecopyresampled($dst, $src, 0, round(($h - $w * $height / $width) / 2), 0, 0, $w, round($w * $height / $width), $width, $height);
            } else {
                imagecopyresampled($dst, $src, 0, 0, 0, 0, $w, $h, $width, $height);
            }
            imagedestroy($src);
            $src = $dst;
            $this->imageSharpen($src);
            return true;
        }
    }

imageSharpen()

$Thumb->imageSharpen(&$src);
Исходный код
    function imageSharpen(&$src) {
        if (!function_exists('imageconvolution')) return;
        $matrix = array(
            array(-1.2, -1, -1.2),
            array(-1, 20, -1),
            array(-1.2, -1, -1.2)
        );
        $divisor = array_sum(array_map('array_sum', $matrix));
        $offset = 0;
        imageconvolution($src, $matrix, $divisor, $offset);
    }

imageCheck()

$Thumb->imageCheck($uri);
Исходный код
    function imageCheck($uri) {
        $root = $this->root();
        if (strpos($uri, '/../') !== false) return false;
        if (strpos($uri, '.php') !== false) return false;
        if (!is_file($root . $uri)) return false;
        return true;
    }

fileOutput()

$Thumb->fileOutput($file=null, $mime=null);
Исходный код
    function fileOutput($file = null, $mime = null) {
        if (!$file) return $this->fallbackOutput();
        $root = $this->root();
        $real = $root . $file;
        if (!is_file($real)) return false;
        if (!is_readable($real)) return false;
        if (!$mime) {
            $info = @getimagesize($real);
            if (!$info) return false;
            $mime = $info['mime'];
        }
        $this->Request->addHeader('Content-Type: ' . $mime);
        $this->Request->addHeader('Content-Length: ' . filesize($real));
        readfile($real);
        return true;
    }

fallbackOutput()

$Thumb->fallbackOutput();
Исходный код
    function fallbackOutput() {
        $data = base64_decode('R0lGODlhAQABAJEAAAAAAP///////wAAACH5BAEAAAIALAAAAAABAAEAAAICVAEAOw==');
        $this->Request->addHeader('Content-Type: image/gif');
        $this->Request->addHeader('Content-Length: ' . strlen($data));
        print $data;
        return true;
    }

truncate()

$Thumb->truncate($path, $maxWidth=0, $maxHeight=0, $quality=0);
Исходный код
    function truncate($path, $maxWidth = 0, $maxHeight = 0, $quality = 0) {
        $App = $this->App();
        $Conf = $this->Conf();
        if ($maxWidth <= 0 || $maxHeight <= 0) {
            $imageSize = intval($Conf->get('image-size'));
            if ($imageSize <= 0) return;
            if ($maxWidth <= 0) $maxWidth = $imageSize;
            if ($maxHeight <= 0) $maxHeight = $imageSize;
        }
        if ($quality <= 0) {
            $quality = intval($Conf->get('image-quality'));
            if ($quality <= 0) $quality = 100;
        }
        if (@filesize($path) > 1024 * 1024 * 100) {
            return;
        }
        if (!$info = @getimagesize($path)) {
            return;
        }
        if ($info[0] > 9999 || $info[1] > 9999) {
            return;
        }
        if ($info[0] <= $maxWidth && $info[1] <= $maxHeight) {
            return;
        }
        $App->largeMemoryLimit();
        if (!$src = $this->imageRead($path, $info)) {
            return;
        }
        if ($info[0] > $info[1]) {
            $w = $maxWidth;
            $h = round($maxWidth * $info[1] / $info[0]);
        } else {
            $h = $maxHeight;
            $w = round($maxHeight * $info[0] / $info[1]);
        }
        if ($info[2] == IMAGETYPE_JPEG) {
            if (function_exists('exif_read_data')) {
                $rotate = 0;
                $exif = @exif_read_data($path);
                if ($exif && $exif['Orientation']) switch ($exif['Orientation']) {
                    case 3: $rotate = 180; break;
                    case 6: $rotate = 270; break;
                    case 8: $rotate = 90; break;
                }
                if ($rotate) {
                    if ($dst = @imagerotate($src, $rotate, imagecolorat($src, 0, 0))) {
                        $src = $dst;
                        if ($rotate == 90 || $rotate == 270) {
                            $_ = $w;
                            $w = $h;
                            $h = $_;
                        }
                    }
                }
            }
        }
        $dst = @imagecreatetruecolor($w, $h);
        @imagefill($dst, 0, 0, imagecolorallocate($dst, 0xff, 0xff, 0xff));
        @imagecopyresampled($dst, $src, 0, 0, 0, 0, $w, $h, imagesx($src), imagesy($src));
        @imagejpeg($dst, $path, $quality);
        @imagedestroy($src);
        @imagedestroy($dst);
    }

removeFileThumb()

$Thumb->removeFileThumb($filename);
Исходный код
    function removeFileThumb($filename) {
        $root = $this->root();
        if (substr($filename, 0, strlen($root)) !== $root) {
            $filename = $root . $filename;
        }
        $dirname = dirname($filename);
        $basename = basename($filename);
        $thumbPrefix = 'thumb.' . $basename . '.';
        $thumbPrefixLength = strlen($thumbPrefix);
        if ($D = @opendir($dirname)) {
            while (strval($filename = @readdir($D)) !== '') {
                if (substr($filename, 0, $thumbPrefixLength) === $thumbPrefix) {
                    @unlink($dirname . '/' . $filename);
                }
            }
            @closedir($D);
        }
    }

thumbQualityJpeg()

$Thumb->thumbQualityJpeg();
Исходный код
    function thumbQualityJpeg() {
        static $quality;
        if (!isset($quality)) {
            if (!$quality = intval($this->Conf()->get('thumb-quality-jpeg'))) $quality = $this->quality;
        }
        return $quality;
    }

thumbQualityWebp()

$Thumb->thumbQualityWebp();
Исходный код
    function thumbQualityWebp() {
        static $quality;
        if (!isset($quality)) {
            if (!$quality = intval($this->Conf()->get('thumb-quality-webp'))) $quality = $this->quality;
        }
        return $quality;
    }