Location: PHPKode > projects > Energine > energine-2.2/core2a/modules/image/components/Image.class.php
<?php
/**
 * Класс Image.
 *
 * @package energine
 * @author 1m.dm
 * @copyright ColoCall 2006
 * @version $Id: Image.class.php,v 1.12 2007/12/24 10:13:23 pavka Exp $
 */

//require_once('core/framework/Object.class.php');
//require_once('core/framework/SystemException.class.php');

/**
 * Класс для работы с изображениями.
 *
 * @package energine
 * @subpackage image
 */
class Image extends Object {

    /**
     * Количество памяти по умолчанию(если cкомпилировано без memory-limit)
     * 16M
     *
     */
    const DEFAULT_MEMORY_LIMIT = 16777216;

    /**
     * Тип изображения:
     */
    /**
     * Неизвестный
     */
    const TYPE_UNKNOWN = 0;
    /**
     * Portable Network Graphics
     */
    const TYPE_PNG     = 1;
    /**
     * Graphics Interchange Format
     */
    const TYPE_GIF     = 2;
    /**
     * Joint Photographic Experts Group
     */
    const TYPE_JPEG    = 3;

    ////////////////////////////////////////////////////////////////////////////

    /**
     * Сопоставление расширений файлов типам.
     *
     * @access private
     * @var array
     */
    private $extensions = array(
    'png'  => self::TYPE_PNG,
    'gif'  => self::TYPE_GIF,
    'jpg'  => self::TYPE_JPEG,
    'jpe'  => self::TYPE_JPEG,
    'jpeg' => self::TYPE_JPEG
    );

    ////////////////////////////////////////////////////////////////////////////

    /**
     * Изображение.
     *
     * @access private
     * @var resource
     */
    private $image;

    /**
     * Ширина изображения.
     *
     * @access private
     * @var int
     */
    private $width;

    /**
     * Высота изображения.
     *
     * @access private
     * @var int
     */
    private $height;

    /**
     * Тип изображения (см. выше список типов).
     *
     * @access private
     * @var int
     */
    private $type;

    ////////////////////////////////////////////////////////////////////////////

    /**
     * Конструктор класса.
     *
     * @access public
     * @return void
     */
    public function __construct() {
        parent::__construct();
    }

    /**
	 * Создание изображения заданной ширины и высоты.
	 *
	 * @access public
	 * @param int $width - ширина
	 * @param int $height - высота
	 * @return void
	 */
    public function create($width, $height) {
        if (!$this->checkAvailableMemory($width, $height)) {
            throw new SystemException('ERR_MEMORY_NOT_AVAILABLE', SystemException::ERR_NOTICE);
        }

        $this->image  = imagecreatetruecolor($width, $height);
        $this->width  = $width;
        $this->height = $height;
        $this->type   = self::TYPE_UNKNOWN;
    }

    ////////

    private function checkAvailableMemory($width, $height) {
        $isMemoryAvailable = true;
        $memoryLimit = ini_get('memory_limit');

        if ($memoryLimit != -1) {
            $memoryLimit = $this->convertSizeToBytes($memoryLimit);
            $memoryNeeded = $width * $height * 4/*B = 32BPP*/ * 1.5; // 1.5 - Хрен Его Знает Что За Фактор :)
            if (function_exists('memory_get_usage')) {
                /*
                * если мы можем узнать занимаемую скриптом память,
                * проверяем количество свободной памяти простым вичитанием
                * занимаемой памяти из установленного лимита.
                */
                $memoryUsage = memory_get_usage();
                $isMemoryAvailable = $memoryNeeded < ($memoryLimit - $memoryUsage);
            }
        }
        return $isMemoryAvailable;
    }

    private function convertSizeToBytes($size) {
        if (!empty($size)) {
            $size = trim($size);
            $measurement = strtolower($size[strlen($size)-1]);
            switch($measurement) {
                case 'g': $size *= 1024;
                case 'm': $size *= 1024;
                case 'k': $size *= 1024;
            }
        }
        else {
            $size = self::DEFAULT_MEMORY_LIMIT;
        }

        return $size;
    }

    ////////

    public function getWidth() {
        return $this->width;
    }

    public function getHeight() {
        return $this->height;
    }

    /**
	 * Загружает изображение из файла.
	 * Если тип изображения явно не указан, метод попытается определить его
	 * самостоятельно, основываясь на расширении файла. В случае неудачи
	 * будет возбуждено исключение.
	 *
	 * @access public
	 * @param int $filename - имя файла
	 * @param int $type - тип изображения
	 * @return void
	 */
    public function loadFromFile($filename, $type = self::TYPE_UNKNOWN) {
        if (!file_exists($filename)) {
            throw new SystemException('ERR_DEV_FILE_DOESNT_EXISTS', SystemException::ERR_DEVELOPER, $filename);
        }

        if ($type == self::TYPE_UNKNOWN) {
            // пробуем определить тип по расширению
            $ext = $this->getExtension($filename);
            if (!array_key_exists($ext, $this->extensions)) {
                throw new SystemException('ERR_CANNOT_DETERMINE_IMAGE_TYPE', SystemException::ERR_WARNING, $filename);
            }
            $type = $this->extensions[$ext];
        }

        if (!$imageSize = @getimagesize($filename)) {
            throw new SystemException('ERR_BAD_FILE_FORMAT', SystemException::ERR_WARNING, $filename);
        }
        if (!$this->checkAvailableMemory($imageSize[0], $imageSize[1])) {
            throw new SystemException('ERR_MEMORY_NOT_AVAILABLE', SystemException::ERR_NOTICE);
        }

        $image = null;
        switch ($type) {
            case self::TYPE_PNG:  $image = imagecreatefrompng($filename);  break;
            case self::TYPE_GIF:  $image = imagecreatefromgif($filename);  break;
            case self::TYPE_JPEG: $image = imagecreatefromjpeg($filename); break;
            default: // unreachable
        }

        if (!$image) {
            throw new SystemException('ERR_BAD_FILE_FORMAT', SystemException::ERR_WARNING, $filename);
        }

        $this->image  = $image;
        $this->width  = imagesx($this->image);
        $this->height = imagesy($this->image);
        $this->type   = $type;
    }

    /**
	 * Сохраняет изображение в файл.
	 * Если тип изображения явно не указан, метод попытается определить его
	 * самостоятельно, основываясь на расширении файла. В случае неудачи
	 * будет возбуждено исключение.
	 *
	 * @access public
	 * @param int $filename - имя файла
	 * @param int $type - тип изображения
	 * @return void
	 */
    public function saveToFile($filename, $type = self::TYPE_UNKNOWN) {
        if (!$this->image) {
            throw new SystemException('ERR_DEV_NO_IMAGE_TO_SAVE', SystemException::ERR_DEVELOPER, $filename);
        }

        if ($type == self::TYPE_UNKNOWN) {
            $ext = $this->getExtension($filename);
            if (array_key_exists($ext, $this->extensions)) {
                $type = $this->extensions[$ext];
            }
            elseif ($this->type != self::TYPE_UNKNOWN) {
                $type = $this->type;
            }
            else {
                throw new SystemException('ERR_CANNOT_DETERMINE_IMAGE_TYPE', SystemException::ERR_WARNING, $filename);
            }
        }

        $success = false;
        switch ($type) {
            case self::TYPE_PNG:  $success = @imagepng ($this->image, $filename); break;
            case self::TYPE_GIF:  $success = @imagegif ($this->image, $filename); break;
            case self::TYPE_JPEG: $success = @imagejpeg($this->image, $filename); break;
            default: // unreachable
        }

        if (!$success) {
            throw new SystemException('ERR_CANT_SAVE_FILE', SystemException::ERR_WARNING, $filename);
        }
        try {
            @chmod($filename, 0777);
        }
        catch (Exception $e){}
        return $success;
    }

    /**
	 * Изменяет размер (разрешение) изображения.
	 * Если ширина ИЛИ высота равны null -- они вычисляются пропорционально.
	 *
	 * @access public
	 * @param mixed $newWidth - новая ширина
	 * @param mixed $newHeight - новая высота
	 * @param bool тупо ресайзить
	 * @return void
	 */
    public function resize($newWidth, $newHeight, $dumbResize = false) {
        if (!isset($newWidth) && !isset($newHeight)) {
            throw new SystemException('ERR_DEV_BAD_SIZE', SystemException::ERR_DEVELOPER);
        }
        if ($dumbResize && !(isset($newWidth) || isset($newHeight))) {
        	throw new SystemException('ERR_DEV_BAD_SIZE', SystemException::ERR_DEVELOPER);
        }
        $posX = $posY = 0;
        if (!$dumbResize) {
            $aspectRatio = $this->width / $this->height;
            $newCanvasWidth = $newWidth;
            $newCanvasHeight = $newHeight;


            if (!isset($newWidth)) {
                $newWidth = $newHeight * $aspectRatio;
                $newCanvasWidth = $newWidth;
            }
            elseif (!isset($newHeight)) {
                $newHeight = $newWidth / $aspectRatio;
                $newCanvasHeight = $newHeight;
            }
            else {
                if ($newWidth > $newHeight) {
                    $newWidth = $newHeight * $aspectRatio;
                    $posX = ($newCanvasWidth - $newWidth) / 2;
                    $posY = 0;
                }
                else {
                    $newHeight = $newWidth / $aspectRatio;
                    $posX = 0;
                    $posY = ($newCanvasHeight - $newHeight) / 2;
                }
            }

            $newCanvasWidth = ceil($newCanvasWidth);
            $newCanvasHeight = ceil($newCanvasHeight);

        }
        else {
            $newCanvasWidth = $newWidth;
            $newCanvasHeight = $newHeight;
            if ($this->width > $this->height) {
                $newHeight=$this->height*($newHeight/$this->height);
            }
            if ($this->width < $this->height) {
                $newWidth=$this->width*($newWidth/$this->width);
            }

            $posX = ceil(($this->width/2)-($newWidth/2));
            $posY = 0;//($this->height/2)-($newHeight/2);
        }

        if (!$this->checkAvailableMemory($newCanvasWidth, $newCanvasHeight)) {
            throw new SystemException('ERR_MEMORY_NOT_AVAILABLE', SystemException::ERR_NOTICE);
        }

        $resizedImage = imagecreatetruecolor($newCanvasWidth, $newCanvasHeight);
        $bgColor = imagecolorallocate($resizedImage, 255, 255, 255);
        imagefill($resizedImage, 0, 0, $bgColor);

        imagecopyresampled(
        $resizedImage, $this->image,
        $posX, $posY, 0, 0,
        $newWidth, $newHeight,
        $this->width, $this->height
        );

        imagedestroy($this->image);

        $this->image  = $resizedImage;
        $this->width  = $newWidth;
        $this->height = $newHeight;
    }

    ////////////////////////////////////////////////////////////////////////////

    /**
	 * Возвращает расширение файла.
	 *
	 * @access private
	 * @param string $filename
	 * @return string
	 */
    private function getExtension($filename) {
        return substr(strrchr($filename, '.'), 1);
    }

    public function __destruct() {
        if ($this->image) {
            imagedestroy($this->image);
        }
    }

    /**
	 * Выводит картинку
	 *
	 * @return void
	 * @access public
	 */

    public function show() {

        switch ($this->type){
            case self::TYPE_PNG:
                $contentType = 'image/png';
                $funcName = 'imagepng';
                break;
            case self::TYPE_JPEG :
                $contentType = 'image/jpg';
                $funcName = 'imagejpeg';
                break;
            case self::TYPE_GIF:
                $contentType = 'image/gif';
                $funcName = 'imagegif';
                break;
            default:
                return;
        }
        $response = Response::getInstance();
        $response->setHeader('Content-type', $contentType);
        ob_start();
        call_user_func($funcName, $this->image);
        $data = ob_get_clean();
        $response->write($data);
        $response->commit();
    }
}

Return current item: Energine