<?php
namespace XF\Image;
use XF\Util\Color;
use function intval, is_int;
abstract class AbstractDriver
{
const ORIENTATION_LANDSCAPE = 'landscape';
const ORIENTATION_PORTRAIT = 'portrait';
const ORIENTATION_SQUARE = 'square';
const FLIP_HORIZONTAL = 1;
const FLIP_VERTICAL = 2;
const FLIP_BOTH = 3;
const COLOR_HEX = 1;
const COLOR_RGB = 2;
const COLOR_ARRAY = 3;
/**
* @var Manager
*/
protected $manager;
protected $type;
protected $width;
protected $height;
abstract protected function _imageFromFile($file, $type);
abstract protected function _createImage($width, $height);
abstract public function resizeTo($width, $height);
abstract public function crop($width, $height, $x = 0, $y = 0, $srcWidth = null, $srcHeight = null);
abstract public function rotate($angle);
abstract public function flip($mode);
abstract public function setOpacity($opacity);
abstract public function appendImageAt($x, $y, $toAppend);
abstract public function save($file, $format = null, $quality = null);
abstract public function output($format = null, $quality = null);
abstract public function isValid();
public function __construct(Manager $manager)
{
$this->manager = $manager;
}
public static function isDriverUsable()
{
return true;
}
/**
* Resizes an image. If height is specified, aspect ratio is not maintained.
*
* @param $width
* @param null $height
* @param bool $upsize
*
* @return $this
*/
public function resize($width, $height = null, $upsize = false)
{
$width = max(1, intval($width));
if ($height === null)
{
$height = $width;
}
else
{
$height = max(1, intval($height));
}
if ($this->width < $width && $this->height < $height && !$upsize)
{
// already done
return $this;
}
$ratio = $this->width / $this->height;
$newRatio = ($width / $height);
if ($newRatio > $ratio)
{
$newWidth = max(1, ceil($height * $ratio));
$newHeight = $height;
}
else
{
$newWidth = $width;
$newHeight = max(1, ceil($width / $ratio));
}
$this->resizeTo($newWidth, $newHeight);
return $this;
}
public function resizeAndCrop($maxWidth, $maxHeight = null)
{
$maxWidth = max(1, intval($maxWidth));
if ($maxHeight === null)
{
$maxHeight = $maxWidth;
}
else
{
$maxHeight = max(1, intval($maxHeight));
}
$newWidth = $this->height * $maxWidth / $maxHeight;
$newHeight = $this->width * $maxHeight / $maxWidth;
$x = 0;
$y = 0;
if ($newWidth > $this->width)
{
$y = (($this->height - $newHeight) / 2);
$newWidth = $this->width;
}
else
{
$x = (($this->width - $newWidth) / 2);
$newHeight = $this->height;
}
$this->crop($maxWidth, $maxHeight, $x, $y, $newWidth, $newHeight);
return $this;
}
/**
* Resizes an image to a specific length of the shortest side, maintaining aspect ratio
*
* @param $length
* @param bool $upsize
*
* @return $this
*/
public function resizeShortEdge($length, $upsize = false)
{
$length = max(1, intval($length));
$ratio = $this->width / $this->height;
if ($ratio > 1) // landscape
{
if ($this->height < $length && !$upsize)
{
return $this;
}
$width = ceil($length * $ratio);
$height = $length;
}
else
{
if ($this->width < $length && !$upsize)
{
return $this;
}
$width = $length;
$height = max(1, ceil($length / $ratio));
}
$this->resizeTo($width, $height);
return $this;
}
/**
* Resizes an image to a specific height, maintaining aspect ratio
*
* @param $height
* @param bool $upsize
*
* @return $this
*/
public function resizeHeight($height, $upsize = false)
{
$height = max(1, intval($height));
if ($height > $this->height && !$upsize)
{
return $this;
}
$width = ceil($this->width * ($height / $this->height));
$this->resizeTo($width, $height);
return $this;
}
public function resizeWidth($width, $upsize = false)
{
$width = max(1, intval($width));
if ($width > $this->width && !$upsize)
{
return $this;
}
$height = ceil($this->height * ($width / $this->width));
$this->resizeTo($width, $height);
return $this;
}
public function transformByExif($orientation)
{
$transforms = [
2 => 'flip-h',
3 => 180,
4 => 'flip-v',
5 => 'transpose',
6 => 90,
7 => 'transverse',
8 => 270
];
if (isset($transforms[$orientation]))
{
$transform = $transforms[$orientation];
switch ($transform)
{
case 'flip-h':
$this->flip(self::FLIP_HORIZONTAL);
break;
case 'flip-v':
$this->flip(self::FLIP_VERTICAL);
break;
case 'transpose':
$this->rotate(90);
$this->flip(self::FLIP_HORIZONTAL);
break;
case 'transverse':
$this->rotate(90);
$this->flip(self::FLIP_VERTICAL);
break;
default:
if (is_int($transform))
{
$this->rotate($transform);
}
else
{
throw new \InvalidArgumentException('Invalid transform: ' . $transform);
}
}
}
}
public function unsharpMask($radius = 2, $sigma = 0.5, $amount = 0.7, $threshold = 0)
{
return $this->_unsharpMask($radius, $sigma, $amount, $threshold);
}
protected function _unsharpMask($radius, $sigma, $amount, $threshold)
{
return $this;
}
/**
* @param string $file
*
* @return bool
*/
public function imageFromFile($file)
{
if (!file_exists($file) || !is_readable($file))
{
throw new \InvalidArgumentException("File '$file' is invalid");
}
if (!filesize($file))
{
throw new \InvalidArgumentException("File '$file' is an empty file");
}
$imageInfo = @getimagesize($file);
if (!$imageInfo)
{
throw new \InvalidArgumentException("File '$file' is not a valid image");
}
$type = $imageInfo[2];
switch ($type)
{
case IMAGETYPE_GIF:
case IMAGETYPE_JPEG:
case IMAGETYPE_PNG:
break;
default:
throw new \InvalidArgumentException("File '$file' is not a valid image type");
}
$this->type = $type;
$this->width = $imageInfo[0];
$this->height = $imageInfo[1];
return $this->_imageFromFile($file, $type);
}
public function createImage($width, $height)
{
$this->width = $width;
$this->height = $height;
return $this->_createImage($width, $height);
}
public function getWidth()
{
return $this->width;
}
public function getHeight()
{
return $this->height;
}
public function getType()
{
return $this->type;
}
protected function getPixelColorArray($x, $y)
{
throw new LogicException('You must override the getPixelColorArray() method in the driver class.');
}
public function getColorAt($x, $y, $returnType = 'array')
{
$color = $this->getPixelColorArray($x, $y);
switch ($returnType)
{
case Color::RGB:
return implode(',', $color);
case Color::HEX:
return sprintf('%02X%02X%02X', $color['red'], $color['green'], $color['blue']);
default:
return $color;
}
}
public function getOrientation()
{
$w = $this->getWidth();
$h = $this->getHeight();
if ($w == $h)
{
return self::ORIENTATION_SQUARE;
}
else if ($w > $h)
{
return self::ORIENTATION_LANDSCAPE;
}
else
{
return self::ORIENTATION_PORTRAIT;
}
}
public function getDataUri($tempFile)
{
$this->save($tempFile);
switch ($this->type)
{
case IMAGETYPE_GIF:
$mimeType = 'image/gif';
break;
case IMAGETYPE_PNG:
$mimeType = 'image/png';
break;
case IMAGETYPE_JPEG:
default:
$mimeType = 'image/jpeg';
break;
}
$data = file_get_contents($tempFile);
return 'data:' . $mimeType . ';base64,' . base64_encode($data);
}
public function isSquare()
{
return ($this->getWidth() == $this->getHeight());
}
}