<?php
namespace App\Common;
use GdImage;
class Resize
{
// *** Class variables
private ?GdImage $image;
private int $width = -1;
private int $height = -1;
private ?GdImage $imageResized;
private string $extension = "";
public function build(string $fileName)
{
// *** Open up the file
$this->image = $this->openImage($fileName);
// *** Get width and height
$this->width = imagesx($this->image);
$this->height = imagesy($this->image);
if (!$this->image || $this->width <= 0 || $this->height<=0) {
throw new \Exception('Unable to open image.');
}
}
## --------------------------------------------------------
private function openImage($file)
{
// *** Get extension
$this->extension = strtolower(strrchr($file, '.'));
$img = match ($this->extension) {
'.jpg', '.jpeg' => @imagecreatefromjpeg($file),
'.gif' => @imagecreatefromgif($file),
'.png' => @imagecreatefrompng($file),
default => false,
};
if (!$img) {
throw new \Exception('Unable to open image.');
}
// *** Get EXIF data
$exif = exif_read_data($file);
// *** Handle Rotation
if ($exif && isset($exif['Orientation'])) {
$orientation = $exif['Orientation'];
if ($orientation != 1) {
$deg = 0;
switch ($orientation) {
case 3:
$deg = 180;
break;
case 6:
$deg = 270;
break;
case 8:
$deg = 90;
break;
}
if ($deg) {
$img = imagerotate($img, $deg, 0);
}
}
}
return $img;
}
## --------------------------------------------------------
public function resizeImage($newWidth, $newHeight, $option = "auto")
{
// *** Get optimal width and height - based on $option
$optionArray = $this->getDimensions($newWidth, $newHeight, $option);
$optimalWidth = $optionArray['optimalWidth'];
$optimalHeight = $optionArray['optimalHeight'];
// *** Resample - create image canvas of x, y size
$this->imageResized = imagecreatetruecolor($optimalWidth, $optimalHeight);
imagecopyresampled(
$this->imageResized,
$this->image,
0,
0,
0,
0,
$optimalWidth,
$optimalHeight,
$this->width,
$this->height
);
// *** if option is 'crop', then crop too
if ($option == 'crop') {
$this->crop($optimalWidth, $optimalHeight, $newWidth, $newHeight);
}
}
## --------------------------------------------------------
private function getDimensions($newWidth, $newHeight, $option): array
{
switch ($option) {
case 'exact':
$optimalWidth = $newWidth;
$optimalHeight = $newHeight;
break;
case 'portrait':
$optimalWidth = $this->getSizeByFixedHeight($newHeight);
$optimalHeight = $newHeight;
break;
case 'landscape':
$optimalWidth = $newWidth;
$optimalHeight = $this->getSizeByFixedWidth($newWidth);
break;
case 'crop':
$optionArray = $this->getOptimalCrop($newWidth, $newHeight);
$optimalWidth = $optionArray['optimalWidth'];
$optimalHeight = $optionArray['optimalHeight'];
break;
case 'auto':
default:
$optionArray = $this->getSizeByAuto($newWidth, $newHeight);
$optimalWidth = $optionArray['optimalWidth'];
$optimalHeight = $optionArray['optimalHeight'];
break;
}
return ['optimalWidth' => $optimalWidth, 'optimalHeight' => $optimalHeight];
}
## --------------------------------------------------------
private function getSizeByFixedHeight($newHeight)
{
$ratio = $this->width / $this->height;
return $newHeight * $ratio;
}
private function getSizeByFixedWidth($newWidth)
{
$ratio = $this->height / $this->width;
return $newWidth * $ratio;
}
private function getOptimalCrop($newWidth, $newHeight): array
{
$heightRatio = $this->height / $newHeight;
$widthRatio = $this->width / $newWidth;
if ($heightRatio < $widthRatio) {
$optimalRatio = $heightRatio;
} else {
$optimalRatio = $widthRatio;
}
$optimalHeight = $this->height / $optimalRatio;
$optimalWidth = $this->width / $optimalRatio;
return ['optimalWidth' => $optimalWidth, 'optimalHeight' => $optimalHeight];
}
## --------------------------------------------------------
private function getSizeByAuto($newWidth, $newHeight): array
{
if ($this->height < $this->width) { // *** Image to be resized is wider (landscape)
$optimalWidth = $newWidth;
$optimalHeight = $this->getSizeByFixedWidth($newWidth);
} elseif ($this->height > $this->width) { // *** Image to be resized is taller (portrait)
$optimalWidth = $this->getSizeByFixedHeight($newHeight);
$optimalHeight = $newHeight;
} else { // *** Image to be resizerd is a square
if ($newHeight < $newWidth) {
$optimalWidth = $newWidth;
$optimalHeight = $this->getSizeByFixedWidth($newWidth);
} else {
if ($newHeight > $newWidth) {
$optimalWidth = $this->getSizeByFixedHeight($newHeight);
$optimalHeight = $newHeight;
} else {
// *** Sqaure being resized to a square
$optimalWidth = $newWidth;
$optimalHeight = $newHeight;
}
}
}
return ['optimalWidth' => $optimalWidth, 'optimalHeight' => $optimalHeight];
}
## --------------------------------------------------------
private function crop($optimalWidth, $optimalHeight, $newWidth, $newHeight)
{
// *** Find center - this will be used for the crop
$cropStartX = ($optimalWidth / 2) - ($newWidth / 2);
$cropStartY = ($optimalHeight / 2) - ($newHeight / 2);
$crop = $this->imageResized;
//imagedestroy($this->imageResized);
// *** Now crop from center to exact requested size
$this->imageResized = imagecreatetruecolor($newWidth, $newHeight);
imagecopyresampled(
$this->imageResized,
$crop,
0,
0,
$cropStartX,
$cropStartY,
$newWidth,
$newHeight,
$newWidth,
$newHeight
);
}
## --------------------------------------------------------
public function saveImage($savePath, $imageQuality = "100")
{
// *** Get extension
$extension = strrchr($savePath, '.');
$extension = strtolower($extension);
switch ($extension) {
case '.jpg':
case '.jpeg':
if (imagetypes() & IMG_JPG) {
imagejpeg($this->imageResized, $savePath, $imageQuality);
}
break;
case '.gif':
if (imagetypes() & IMG_GIF) {
imagegif($this->imageResized, $savePath);
}
break;
case '.png':
// *** Scale quality from 0-100 to 0-9
$scaleQuality = round(($imageQuality / 100) * 9);
// *** Invert quality setting as 0 is best, not 9
$invertScaleQuality = 9 - $scaleQuality;
if (imagetypes() & IMG_PNG) {
imagepng($this->imageResized, $savePath, $invertScaleQuality);
}
break;
default:
// *** No extension - No save.
break;
}
imagedestroy($this->imageResized);
}
## --------------------------------------------------------
public function getImage()
{
switch ($this->extension) {
case '.jpg':
case '.jpeg':
if (imagetypes() & IMG_JPG) {
imagejpeg($this->imageResized);
}
break;
case '.gif':
if (imagetypes() & IMG_GIF) {
imagegif($this->imageResized);
}
break;
case '.png':
if (imagetypes() & IMG_PNG) {
imagepng($this->imageResized);
}
break;
default:
// *** No extension - No save.
break;
}
imagedestroy($this->imageResized);
}
## --------------------------------------------------------
public function getContentType(): string
{
return match ($this->extension) {
".jpeg", ".jpg" => "image/jpeg",
".gif" => "image/gif",
".png" => "image/png",
default => "",
};
}
public function getHeight(): int
{
return $this->height;
}
public function getWidth(): int
{
return $this->width;
}
}