diff --git a/public/user-assets.php b/public/user-assets.php index b620a7f8..cdb9e6bf 100644 --- a/public/user-assets.php +++ b/public/user-assets.php @@ -1,7 +1,7 @@ setImageFormat($avatarImage->getNumberImages() > 1 ? 'gif' : 'png'); - $avatarImage = $avatarImage->coalesceImages(); - - $avatarOriginalWidth = $avatarImage->getImageWidth(); - $avatarOriginalHeight = $avatarImage->getImageHeight(); - - if($avatarOriginalWidth > $avatarOriginalHeight) { - $avatarWidth = $avatarOriginalWidth * $dimensions / $avatarOriginalHeight; - $avatarHeight = $dimensions; - } else { - $avatarWidth = $dimensions; - $avatarHeight = $avatarOriginalHeight * $dimensions / $avatarOriginalWidth; - } - - do { - $avatarImage->resizeImage( - $avatarWidth, - $avatarHeight, - Imagick::FILTER_LANCZOS, - 0.9 - ); - - $avatarImage->cropImage( - $dimensions, - $dimensions, - ($avatarWidth - $dimensions) / 2, - ($avatarHeight - $dimensions) / 2 - ); - - $avatarImage->setImagePage( - $dimensions, - $dimensions, - 0, - 0 - ); - } while($avatarImage->nextImage()); - - $avatarImage->deconstructImages()->writeImages($filename = $avatarCropped, true); + $avatarImage = Image::create($avatarOriginal); + $avatarImage->squareCrop($dimensions); + $avatarImage->save($filename = $avatarCropped); } catch(Exception $ex) {} } } diff --git a/src/Imaging/GdImage.php b/src/Imaging/GdImage.php new file mode 100644 index 00000000..c3544cc3 --- /dev/null +++ b/src/Imaging/GdImage.php @@ -0,0 +1,112 @@ + 'imagecreatefromgif', + IMAGETYPE_JPEG => 'imagecreatefromjpeg', + IMAGETYPE_PNG => 'imagecreatefrompng', + IMAGETYPE_BMP => 'imagecreatefrombmp', + IMAGETYPE_WBMP => 'imagecreatefromwbmp', + IMAGETYPE_WEBP => 'imagecreatefromwebp', + ]; + + private const SAVERS = [ + IMAGETYPE_GIF => 'imagegif', + IMAGETYPE_JPEG => 'imagejpeg', + IMAGETYPE_PNG => 'imagepng', + IMAGETYPE_BMP => 'imagebmp', + IMAGETYPE_WBMP => 'imagewbmp', + IMAGETYPE_WEBP => 'imagewebp', + ]; + + public function __construct($pathOrWidth, int $height = -1) { + parent::__construct($pathOrWidth, $height); + + if(is_int($pathOrWidth)) { + $this->gd = imagecreatetruecolor($pathOrWidth, $height < 1 ? $pathOrWidth : $height); + $this->type = IMAGETYPE_PNG; + } elseif(is_string($pathOrWidth)) { + $imageInfo = getimagesize($pathOrWidth); + + if($imageInfo !== false) { + $this->type = $imageInfo[2]; + + if(isset(self::CONSTRUCTORS[$this->type])) + $this->gd = self::CONSTRUCTORS[$this->type]($pathOrWidth); + } + } + + if(!isset($this->gd)) { + throw new InvalidArgumentException('Unsupported image format.'); + } + } + + public function __destruct() { + if(isset($this->gd)) + $this->destroy(); + } + + public function getWidth(): int { + return imagesx($this->gd); + } + + public function getHeight(): int { + return imagesy($this->gd); + } + + public function hasFrames(): bool { + return false; + } + + public function next(): bool { + return false; + } + + public function resize(int $width, int $height): bool { + $resized = imagescale($this->gd, $width, $height, IMG_BICUBIC_FIXED); + + if($resized === false) + return false; + + imagedestroy($this->gd); + $this->gd = $resized; + + return true; + } + + public function crop(int $width, int $height, int $x, int $y): bool { + $cropped = imagecrop($this->gd, compact('width', 'height', 'x', 'y')); + + if($cropped === false) + return false; + + imagedestroy($this->gd); + $this->gd = $cropped; + + return true; + } + + public function setPage(int $width, int $height, int $x, int $y): bool { + return false; + } + + public function save(string $path): bool { + if(isset(self::SAVERS[$this->type])) + return self::SAVERS[$this->type]($this->gd, $path); + + return false; + } + + public function destroy(): void { + if(imagedestroy($this->gd)) { + $this->gd = null; + $this->type = 0; + } + } +} diff --git a/src/Imaging/Image.php b/src/Imaging/Image.php new file mode 100644 index 00000000..015a299a --- /dev/null +++ b/src/Imaging/Image.php @@ -0,0 +1,53 @@ +getWidth(); + $originalHeight = $this->getHeight(); + + if($originalWidth > $originalHeight) { + $targetWidth = $originalWidth * $dimensions / $originalHeight; + $targetHeight = $dimensions; + } else { + $targetWidth = $dimensions; + $targetHeight = $originalHeight * $dimensions / $originalWidth; + } + + do { + $this->resize($targetWidth, $targetHeight); + $this->crop( + $dimensions, $dimensions, + ($targetWidth - $dimensions) / 2, + ($targetHeight - $dimensions) / 2 + ); + $this->setPage($dimensions, $dimensions, 0, 0); + } while($this->next()); + } +} diff --git a/src/Imaging/ImagickImage.php b/src/Imaging/ImagickImage.php new file mode 100644 index 00000000..a6c8c71f --- /dev/null +++ b/src/Imaging/ImagickImage.php @@ -0,0 +1,76 @@ +imagick = new Imagick(); + $this->newImage($pathOrWidth, $height < 1 ? $pathOrWidth : $height, 'none'); + $this->setImageFormat('png'); + } elseif(is_string($pathOrWidth)) { + $imagick = new Imagick($pathOrWidth); + $imagick->setImageFormat($imagick->getNumberImages() > 1 ? 'gif' : 'png'); + $this->imagick = $imagick->coalesceImages(); + } + + if(!isset($this->imagick)) + throw new InvalidArgumentException('Unsupported image format.'); + } + + public function __destruct() { + if(isset($this->imagick)) + $this->destroy(); + } + + public function getImagick(): Imagick { + return $this->imagick; + } + + public function getWidth(): int { + return $this->imagick->getImageWidth(); + } + + public function getHeight(): int { + return $this->imagick->getImageHeight(); + } + + public function hasFrames(): bool { + return $this->imagick->getNumberImages() > 1; + } + + public function next(): bool { + return $this->imagick->nextImage(); + } + + public function resize(int $width, int $height): bool { + return $this->imagick->resizeImage( + $width, $height, Imagick::FILTER_LANCZOS, 0.9 + ); + } + + public function crop(int $width, int $height, int $x, int $y): bool { + return $this->imagick->cropImage($width, $height, $x, $y); + } + + public function setPage(int $width, int $height, int $x, int $y): bool { + return $this->imagick->setImagePage($width, $height, $x, $y); + } + + public function save(string $path): bool { + return $this->imagick + ->deconstructImages() + ->writeImages($path, true); + } + + public function destroy(): void { + if($this->imagick->destroy()) + $this->imagick = null; + } +}