Вход Регистрация
Файл: toolkit/AcImage.php
Строк: 679
<?php
/**
 * @package image
 *
 * @author Андрей Загорцев <freeron@ya.ru>
 * @author Антон Кургузенков <kurguzenkov@list.ru>
 *
 * @version 2.0.1
 * @since 2013-03-12
 */

require_once 'geometry/Rectangle.php';
require_once 
'geometry/exceptions.php';
require_once 
'AcColor.php';
require_once 
'AcImageJPG.php';
require_once 
'AcImageGIF.php';
require_once 
'AcImagePNG.php';

/**
 * Класс, описывающий изображение, и содержащий методы для работы с ним.
 */

class AcImage
{
    const 
PNG = 'image/png';
    const 
JPEG = 'image/jpeg';
    const 
GIF = 'image/gif';

    const 
PROPORTION = 'pr';
    const 
PIXELS = 'px';
    const 
PERCENT = '%';

    const 
TOP_LEFT = 0;
    const 
TOP_RIGHT = 1;
    const 
BOTTOM_RIGHT = 2;
    const 
BOTTOM_LEFT = 3;

    private static 
$correctCorners = array (0, 1, 2, 3);
    private static 
$cornerLogo = 2; // BOTTOM_RIGHT

    /**
     * Путь к файлу с изображением
     *
     * @var string
     */

    
private $filePath;

    
/**
     * Размер (высота и ширина) изображения
     *
     * @var Size
     */

    
private $size;

    
/**
     * Массив с информацией об изображении,
     * который возвращаыет функция getimagesize
     *
     * @var array
     */

    
private $imageInfo;

    
/**
     * Ресурс изображения
     */

    
private $resource;

    
/**
     * Цвет фона по умолчанию
     * @var int|AcColor
     */

    
private static $backgroundColor = AcColor::WHITE;

    
/**
     * Качество по умолчанию
     *
     * @var int
     */

    
private static $quality = 85;

    
/**
     * Вкл/откл прозрачность
     * @var boolean
     */

    
private static $transparency = true;

    
/**
     * Информация о поддерживаемых типах изображений
     *
     * @var array
     */

    
private static $gdInfo;

    
/**
     * Разрешить перезаписывание существующих файлов
     * @var boolean
     */

    
private static $rewrite = false;

    
/**
     * Какую часть максимальную часть лого может занимать от стороны исходного изображения
     * @var float
     */

    
private static $maxProportionLogo = 0.1;
    private static 
$paddingProportionLogo = 0.02;

    
/**
     * @param string путь к файлу с изображением
     */

    
protected function __construct($filePath)
    {
        if (!
self::isFileExists($filePath))
            throw new 
FileNotFoundException();

        
$this->filePath = $filePath;

        
$imageInfo = $this->getImageInfo();
        if (!
is_array($imageInfo))
            throw new 
InvalidFileException($filePath);

        
$this->setSize(new Size($imageInfo[0], $imageInfo[1]));
    }

    
/**
     * Cоздаёт экземпляры классов AcImageJPG, AcImageGIF, AcImagePNG
     * в зависимости от типа изображения.
     *
     * @param string путь к файлу с изображением
     * @return AcImageJPG|AcImagePNG|AcImageGIF
     */

    
public static function createImage($filePath)
    {
        
$image = new AcImage($filePath);

        if (!
self::isSupportedGD())
            throw new 
GDnotInstalledException();

        
$imageInfo = $image->getImageInfo();
        if (!
is_array($imageInfo))
            throw new 
InvalidFileException($filePath);

        
$mimeType = $imageInfo['mime'];

        switch (
$mimeType)
        {
            case 
self::JPEG :
                return new 
AcImageJPG($filePath);
            case 
self::PNG :
                return new 
AcImagePNG($filePath);
            case 
self::GIF :
                return new 
AcImageGIF($filePath);
            default:
                throw new 
InvalidFileException($filePath);
        }
    }

    
/**
     * Сохраняет изображение в формате jpg
     *
     * @param string путь, по которому сохранится изображение
     * @return AcImage
     *
     * @throws UnsupportedFormatException
     * @throws FileAlreadyExistsException
     * @throws FileNotSaveException
     */

    
public function saveAsJPG($path)
    {
        if(!
AcImageJPG::isSupport())
            throw new 
UnsupportedFormatException();

        if (!
self::getRewrite() && self::isFileExists($path))
            throw new 
FileAlreadyExistsException($path);

        
$this->putBackground(self::$backgroundColor);
        if(!
imagejpeg(self::getResource(), $path, self::getQuality()))
            throw new 
FileNotSaveException($path);

        return 
$this;
    }

    
/**
     * Сохраняет изображение в формате png
     *
     * @param string путь, по которому сохранится изображение
     * @return AcImage
     *
     * @throws UnsupportedFormatException
     * @throws FileAlreadyExistsException
     * @throws FileNotSaveException
     */

    
public function saveAsPNG($path)
    {
        if(!
AcImagePNG::isSupport())
            throw new 
UnsupportedFormatException('png');

        if (!
self::getRewrite() && self::isFileExists($path))
            throw new 
FileAlreadyExistsException($path);

        if (!
self::getTransparency())
            
$this->putBackground(self::$backgroundColor);
        
// php >= 5.1.2
        
if(!imagePng(self::getResource(), $path, AcImagePNG::getQuality()))
            throw new 
FileNotSaveException($path);

        return 
$this;
    }

 
/**
     * Сохраняет изображение в формате GIF
     *
     * @param string путь, по которому сохранится изображение
     * @return AcImage
     *
     * @throws UnsupportedFormatException
     * @throws FileAlreadyExistsException
     * @throws FileNotSaveException
     */

    
public function saveAsGIF($path)
    {
        if(!
AcImageGIF::isSupportedGD())
            throw new 
UnsupportedFormatException();

        if (!
self::getRewrite() && self::isFileExists($path))
            throw new 
FileAlreadyExistsException($path);;

        if(!
self::getTransparency())
            
$this->putBackground(self::$backgroundColor);

        if(!
imagegif(self::getResource(), $path))
            throw new 
FileNotSaveException($path);

        return 
$this;
    }

    
/**
     * Вписывает изображение в рамки.
     * Принимает размер (рамку) в которую вписывается изображение
     * или высоту и ширину этой рамки.
     *
     * @return AcImage
     * @throws IllegalArgumentException
     */

    
public function resize() // Size $s || $width, $height
    
{
        
$args = func_get_args();
        if (
count($args) == 2) // $width, $height
        
{
            return 
$this->resize(new Size($args[0], $args[1]));
        }
        else if (
count($args) == 1 && $args[0] instanceof Size)
        {
            
$size = $args[0];
            
$imageSize = $this->getSize()->getByFrame($size);

            
$newImageResource = imagecreatetruecolor($imageSize->getWidth(), $imageSize->getHeight());
            
imageAlphaBlending($newImageResource, false);
            
imageSaveAlpha($newImageResource, true);
            
imagecopyresampled($newImageResource, $this->getResource(), 0, 0, 0, 0, $imageSize->getWidth(),
                
$imageSize->getHeight(), $this->getWidth(), $this->getHeight());
            
$this->setResource($newImageResource);
            
$this->setSize($imageSize);
            return 
$this;
        }
        throw new 
IllegalArgumentException();
    }

    
/**
     * Ужимает изображение по <i>ширине</i>.
     *
     * @param int ширина рамки
     * @return AcImage
     * @throws IllegalArgumentException
     */

    
public function resizeByWidth($width)
    {
        if (!
is_int($width) || $width <= 0)
            throw new 
IllegalArgumentException();

        return 
$this->resize($width, $this->getHeight());
    }

    
/**
     * Ужимает изображение по <i>высоте</i>.
     *
     * @param int высота рамки
     * @return AcImage
     * @throws IllegalArgumentException
     */

    
public function resizeByHeight($height)
    {
        if (!
is_int($height) || $height <= 0)
            throw new 
IllegalArgumentException();

        return 
$this->resize($this->getWidth(), $height);
    }

    
/**
     * Производит кроп изображения, то есть
     * вырезает из него произвольную прямоугольную область.
     * Метод принимает либо вырезаемый прямоугольник, либо его параметры.
     *
     * @return AcImage
     * @throws IllegalArgumentException
     */

    
public function crop() // Rectangle $rect || $x, $y, $w, $h
    
{
        
$a = func_get_args();

        if (
count($a) == 4)
            
$rect = new Rectangle($a[0], $a[1], $a[2], $a[3]);
        else if (
count($a) == 1 && $a[0] instanceof Rectangle)
            
$rect = $a[0];
        
$rect = $rect->getIntersectsWith(new Rectangle(new Point(0, 0), $this->getSize()));

        if (!
$rect)
            throw new 
IllegalArgumentException();

        
$width = $rect->getWidth();
        
$height = $rect->getHeight();
        
$x = $rect->getLeft();
        
$y = $rect->getTop();

        if (
$width == 0 || $height == 0)
            throw new 
IllegalArgumentException();

        
$newImageResource = imagecreatetruecolor($width, $height);
        
imageAlphaBlending($newImageResource, false);
        
imageSaveAlpha($newImageResource, true);
        
imagecopyresampled($newImageResource, $this->getResource(), 0, 0, $x, $y, $width, $height, $width, $height);
        
$this->setResource($newImageResource);
        
$this->setSize($rect->getSize());
        return 
$this;
    }

    
/**
     * Вырезает произвольный квадрат из изображения.
     * Метод может принимать вырезаемый прямоугольник, обязанный быть квадратом,
     * либо параметры для создания такого прямоугольника.
     *
     * @throws IllegalArgumentException
     * @return AcImage
     */

    
public function cropSquare() // Rectnagle $square || Point $p, $a || $x, $y, $a
    
{
        
$a = func_get_args();
        if (
count($a) == 1 && $a[0] instanceof Rectangle && $a[0]->isSquare())
        {
            
$square = $a[0];
        }
        else if (
count($a) == 2 && $a[0] instanceof Point && is_int($a[1]))
        {
            
$square = new Rectangle($a[0], new Size($a[1], $a[1]));
        }
        else if(
count($a) == 3)
        {
            
$square = new Rectangle(new Point($a[0], $a[1]), new Size($a[2], $a[2]));
        }
        else
        {
            throw new 
IllegalArgumentException();
        }

        if (!
$square->isInner(new Rectangle(new Point(0, 0), $this->getSize())))
            throw new 
IllegalArgumentException();

        return 
$this->crop($square);
    }

    
/**
     * Вырезает центральную область изображения.
     * Принимает высоту и ширину вырезаемой области.
     *
     * @param int|string ширина вырезаемой области
     * @param int|string высота вырезаемой области
     *
     * @return AcImage
     * @throws IllegalArgumentException
     */

    
public function cropCenter($width, $height) // int, int || string, string || int, string || string, int
    
{
        
$result = self::parseCropCenterArg($width);
        
$widthUnits = $result['units'];
        
$width = $result['value'];

        
$result = self::parseCropCenterArg($height);
        
$heightUnits = $result['units'];
        
$height = $result['value'];

        
// true тогда и только тогда если слева и справа от xor разные значения
        
if ($widthUnits == self::PROPORTION xor $heightUnits == self::PROPORTION)
            throw new 
IllegalArgumentException();

        if (
$widthUnits == self::PERCENT)
            
$width = self::percentToPixels($width, $this->getWidth());

        if (
$heightUnits == self::PERCENT)
            
$height = self::percentToPixels($height, $this->getHeight());

        if (
$widthUnits == self::PROPORTION)
        {
            
$size = $this->getSizeByProportion($width, $height);

            
$width = $size->getWidth();
            
$height = $size->getHeight();
        }

        
$width = (int)min($width, $this->getWidth());
        
$height = (int)min($height, $this->getHeight());

        
$imageRect = new Rectangle(0, 0, $this->getWidth(), $this->getHeight());
        
$cropRect = new Rectangle(0, 0, $width, $height);
        
$cropRect = $cropRect->center($imageRect);

        return 
$this->crop($cropRect);
    }

    
/**
     * @ignore
     */

    
private static function percentToPixels($value, $imageSide)
    {
        return (int)
round(($imageSide / 100 * $value));
    }

    
/**
     * @ignore
     */

    
private function getSizeByProportion($width, $height)
    {
        
$imageWidth = $this->getWidth();
        
$imageHeight = $this->getHeight();

        if(
$height / $imageHeight > $width / $imageWidth)
        {
            return new 
Size((int)round($imageHeight / $height * $width), $imageHeight);
        }
        else
        {
            return new 
Size($imageWidth, (int)round($imageWidth / $width * $height));
        }
    }

    
/**
     * @ignore
     */

    
private static function parseCropCenterArg($arg)
    {
        
$pattern = '/^(d+(.d+)*)(px|%|pr)$/'; // in constants?

        
$matches = array();
        if (
is_int($arg))
        {
            
$units = self::PIXELS;
            
$value = $arg;
        }
        else if (
preg_match($pattern, $arg, $matches))
        {
            
$units = $matches[3]; // (px|%|pr)
            
$value = $matches[1]; // (d+(.d+)*)
        
}
        else
        {
            throw new 
IllegalArgumentException();
        }
        return array (
            
'units' => $units,
            
'value' => $value
        
);
    }

    
/**
     * "Умное" создание миниатюр.
     *
     * @param int ширина
     * @param int высота
     * @param float коэффициент превышения.
     * @throws IllegalArgumentException
     * @return AcImage
     */

    
public function thumbnail($width, $height, $c = 2) // $width, $height, [$c]
    
{
        if (
$c <= 1 || !is_finite($c) || $width <= 0 || $height <= 0)
            throw new 
IllegalArgumentException();

        
$size = new Size($width, $height);

        if(
$this->getSize()->lessThen($size))
            
$size = $this->getSize();

        
$imageSize = $this->getSize();

        
$isRotate = false;
        if(
$size->getWidth() / $imageSize->getWidth() <= $size->getHeight() / $imageSize->getHeight())
        {
            
$size->flip();
            
$imageSize->flip();
            
$isRotate = true;
        }

        
$width = $size->getWidth();
        
$height = $size->getHeight();

        
$imageWidth = $imageSize->getWidth();
        
$imageHeight = $imageSize->getHeight();

        
$lim = (int)($c * $height);
        
$newHeight = (int)($imageHeight * $width / $imageWidth);

        if (
$imageWidth > $width)
        {
            if(
$newHeight <= $lim)
            {
                
$size = new Size($width, $newHeight);
            }
            else
            {
                if(
$newHeight <= 2 * $lim)
                {
                    
$size = new Size((int)($imageWidth * $lim / $imageHeight), $lim);
                }
                else
                {
                    
$size = new Size((int)($width / 2), (int)($imageHeight * $width / $imageWidth));
                }
            }
        }
        else
        {
            if(
$imageHeight <= $lim)
            {
                
$size = $this->getSize();
            }
            else
            {
                if(
$imageHeight <= 2 * $lim)
                {
                    if(
$imageWidth * $lim / $imageHeight >= $width / 2)
                    {
                        
$size = new Size((int)($imageWidth * $lim / $imageHeight), $lim);
                    }
                    else
                    {
                        
$size = new Size((int)($width / 2), (int)($imageHeight * $width / ($imageWidth * 2)));
                    }
                }
                else
                {
                    
$size = new Size((int)($width / 2), (int)($imageHeight * $width / ($imageWidth * 2)));
                }
            }
        }
        if (
$isRotate)
        {
            
$size->flip();
            
$imageSize->flip();
        }
        return 
$this->resize($size);
    }

    
/**
     * Наносит лого на изображение.
     *
     * @param mixed
     * @param int номер угла, в котором будет размещенно лого<br />
     * 0 1<br />
     * 2 3
     *
     * @see AcImage::TOP_LEFT
     * @see AcImage::TOP_RIGHT
     * @see AcImage::BOTTOM_LEFT
     * @see AcImage::BOTTOM_RIGHT
     *
     * @return AcImage
     * @throws IllegalArgumentException
     */

    
public function drawLogo($logo, $corner = null) // string $logo [$corner] || AcImage $logo [$corner]
    
{
        if (
is_null($corner))
            
$corner = self::$cornerLogo;

        if (!
AcImage::isCorrectCorner($corner))
            throw new 
IllegalArgumentException();

        if (
is_string($logo))
            
$logo = AcImage::createImage($logo);

        if (!(
$logo instanceof AcImage))
            throw new 
IllegalArgumentException();

        
$maxWidthLogo = (int)($this->getWidth() * self::$maxProportionLogo);
        
$maxHeightLogo = (int)($this->getHeight() * self::$maxProportionLogo);

        
$logo->resize($maxWidthLogo, $maxHeightLogo);

        if (!
self::getTransparency())
            
$logo->putBackground(self::$backgroundColor);

        
imagealphablending($this->getResource(), true);
        
$logoSize = $logo->getSize();

        
$location = $this->getLogoPosition($corner, $logoSize->getWidth(), $logoSize->getHeight());
        
imagecopy($this->getResource(), $logo->getResource(), $location->getX(), $location->getY(), 0, 0,
                        
$logoSize->getWidth(), $logoSize->getHeight());

        return 
$this;
    }

    
/**
     * @ignore
     */

    
private function getLogoPosition($corner, $width, $height)
    {
        
$paddingX = $this->getWidth() * self::$paddingProportionLogo;
        
$paddingY = $this->getHeight() * self::$paddingProportionLogo;


        if (
$corner == self::BOTTOM_RIGHT || $corner == self::BOTTOM_LEFT)
            
$y = $this->getHeight() - $paddingY - $height;
        else
            
$y = $paddingY;

        if (
$corner == self::BOTTOM_RIGHT  || $corner == self::TOP_RIGHT)
            
$x = $this->getWidth() - $paddingX - $width;
        else
            
$x = $paddingX;

        return new 
Point((int)$x, (int)$y);
    }

    
/**
     * @ignore
     */

    
private static function isCorrectCorner($corner)
    {
        return 
in_array($corner, self::$correctCorners);
    }

    
/**
     * Проверяет, существует ли файл.
     *
     * @param string путь к файлу
     * @return boolean
     */

    
public static function isFileExists($filePath)
    {
        if (@
file_exists($filePath))
            return 
true;

        if(!
preg_match("|^http(s)?|", $filePath))
            return 
false;

        
$headers = @get_headers($filePath);
        if(
preg_match("|200|", $headers[0]))
            return 
true;

        return 
false;
    }

    
/**
     * Проверяет, является ли файл изображением.
     *
     * @param string путь к файлу
     * @return boolean
     */

    
public static function isFileImage($filePath)
    {
        if (!
self::isFileExists($filePath))
            return 
false;

        
$imageInfo = @getimagesize($filePath);
        return 
is_array($imageInfo);
    }

    
/**
     * @ignore
     */

    
protected function putBackground()
    {
        
$newImageResource = imagecreatetruecolor($this->getWidth(), $this->getHeight());
        
imagefill($newImageResource , 0, 0, self::getBackgroundColor()->getCode());
        
imagecopyresampled($newImageResource , $this->getResource(), 0, 0, 0, 0,
            
$this->getWidth(), $this->getHeight(), $this->getWidth(), $this->getHeight());
        
$this->setResource($newImageResource);
    }

    
/**
     * @param boolean
     * @throws IllegalArgumentException
     */

    
public static function setRewrite($mode)
    {
        if (!
is_bool($mode))
            throw new 
IllegalArgumentException();

        
self::$rewrite = $mode;
    }

    public static function 
getRewrite()
    {
        return 
self::$rewrite;
    }

    public function 
getImageInfo()
    {
        if (!isset(
$this->imageInfo))
            
$this->imageInfo = @getimagesize($this->getFilePath());

        return 
$this->imageInfo;
    }

    
/**
     * Возвращает две первые цифры
     * версии php, разделённые точкой.
     * Например: 5.2, 5.3
     *
     * @since 2.0.1
     *
     * @return string
     */

    
public static function getShortPHPVersion()
    {
        
$matches = array();
        
preg_match("@^d.d@", phpversion(), $matches);
        return 
$matches[0];
    }

    public static function 
isSupportedGD()
    {
        return 
function_exists('gd_info');
    }

    
/**
     *
     * Возвращает результат работы функции gd_info()
     * или false если библиотека gd не доступна
     *
     * @return array|bool
     */

    
public static function getGDinfo()
    {
        if (!
self::isSupportedGD())
            return 
false;

        if (!isset(
self::$gdInfo))
            
self::$gdInfo = gd_info();

        return 
self::$gdInfo;
    }

    public function 
getFilePath()
    {
        return 
$this->filePath;
    }

    private function 
setSize(Size $s)
    {
        
$this->size = $s;
    }

    public function 
getSourceImage()
    {
        return 
$this->sourceImage;
    }

    public function 
getMimeType()
    {
        
$imageInfo = $this->getImageInfo();
        return 
$imageInfo['mime'];
    }

    public function 
getSize()
    {
        return clone 
$this->size;
    }

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

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

    public function 
getResource()
    {
        return 
$this->resource;
    }

    
/**
     * @ignore
     */

    
protected function setResource($resource)
    {
        return 
$this->resource = $resource;
    }

    
// static getters and setters

    /**
     *
     * @param int качество изображения от 0 до 100
     * @throws IllegalArgumentException
     */

    
public static function setQuality($q)
    {
        
$q = (int)$q;
        if (!
is_integer($q) || $q <= 0 || $q > 100)
            throw new 
IllegalArgumentException();

        
self::$quality = $q;
    }

    public static function 
getQuality()
    {
        return 
self::$quality;
    }

    
/**
     *
     * @param boolean
     * @throws IllegalArgumentException
     */

    
public static function setTransparency($mode)
    {
        if(!
is_bool($mode))
            throw new 
IllegalArgumentException();

        
self::$transparency = $mode;
    }

    public static function 
getTransparency()
    {
        return 
self::$transparency;
    }

    public static function 
setBackgroundColor() // $color || $r, $r, $b || $code
    
{
        
$a = func_get_args();
        if (
count($a) == 1)
        {
            if (
$a[0] instanceof AcColor)
            {
                
self::$backgroundColor = $a[0];
            }
            else
            {
                
self::$backgroundColor = new AcColor($a[0]);
            }
        }
        else if (
count($a) == 3)
        {
            
self::$backgroundColor = new AcColor($a[0], $a[1], $a[2]);
        }
        else
        {
            throw new 
IllegalArgumentException();
        }
    }

    public static function 
getBackgroundColor()
    {
        if (
is_integer(self::$backgroundColor))
            
self::$backgroundColor = new AcColor(self::$backgroundColor);

        return 
self::$backgroundColor;
    }

    public function 
getCornerLogo()
    {
        return 
self::$cornerLogo;
    }

    
/**
     *
     * @param int номер угла изображения<br />
     * 0 1<br />
     * 2 3
     *
     * @see AcImage::TOP_LEFT
     * @see AcImage::TOP_RIGHT
     * @see AcImage::BOTTOM_RIGHT
     * @see AcImage::BOTTOM_LEFT
     *
     * @throws IllegalArgumentException
     */

    
public function setCornerLogo($corner)
    {
        if(!
self::isCorrectCorner($corner))
            throw new 
IllegalArgumentException();

        
self::$cornerLogo = $corner;
    }

    
/**
     *
     * @param float от 0 <= $maxPropotionsLogo < 1
     * @throws IllegalArgumentException
     */

    
public static function setMaxProportionLogo($maxPropotionsLogo)
    {
        if (!
is_float($maxPropotionsLogo) || $maxPropotionsLogo > 1 ||
                        
$maxPropotionsLogo <= 0)
            throw new 
IllegalArgumentException();

        
self::$maxProportionLogo = $maxPropotionsLogo;
    }

    public static function 
getMaxProportionLogo()
    {
        return 
self::$maxProportionLogo;
    }

    
/**
     *
     * @param float от 0 <= $paddingProportionLogo < 1
     * @throws IllegalArgumentException
     */

    
public static function setPaddingProportionLogo($paddingProportionLogo)
    {
        if (!
is_float($paddingProportionLogo) || $paddingProportionLogo > 1 ||
                        
$paddingProportionLogo <= 0)
            throw new 
IllegalArgumentException();

        
self::$paddingProportionLogo = $paddingProportionLogo;
    }

    public static function 
getPaddingProportionLogo()
    {
            return 
self::$paddingProportionLogo;
    }
}
?>
Онлайн: 5
Реклама