From a3831cad6bc6d10ca20b1ed3788ae9a6f531d3b1 Mon Sep 17 00:00:00 2001 From: "DESKTOP-G4C80F7\\geckon01" Date: Thu, 30 Mar 2023 00:28:32 +0300 Subject: [PATCH] Initial commit --- .gitignore | 2 + composer.json | 25 ++++++ src/CompressedImage.php | 100 ++++++++++++++++++++++ src/SimpleImageCompressor.php | 153 ++++++++++++++++++++++++++++++++++ 4 files changed, 280 insertions(+) create mode 100644 composer.json create mode 100644 src/CompressedImage.php create mode 100644 src/SimpleImageCompressor.php diff --git a/.gitignore b/.gitignore index a67d42b..2ef3e63 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,5 @@ composer.phar # Commit your application's lock file https://getcomposer.org/doc/01-basic-usage.md#commit-your-composer-lock-file-to-version-control # You may choose to ignore a library lock file http://getcomposer.org/doc/02-libraries.md#lock-file # composer.lock +*.lock +.idea/ diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..bc405bd --- /dev/null +++ b/composer.json @@ -0,0 +1,25 @@ +{ + "name": "geckon01/simple-image-compressor", + "description": "SimpleImageCompressor is a tiny simple PHP image compressor lib which allows you to compress any image easily on the fly", + "version": "0.3.1", + "keywords": [ + "php", + "images", + "compress", + "png", + "jpeg", + "gif" + ], + "require": { + "php": ">=7.4", + "ext-gd": "*" + }, + "homepage": "https://github.com/Geckon01/simple-image-compressor", + "license": "MIT", + "autoload": { + "psr-4": { + "geckon01\\SimpleImageCompressor\\": "src/" + } + }, + "require-dev": {} +} \ No newline at end of file diff --git a/src/CompressedImage.php b/src/CompressedImage.php new file mode 100644 index 0000000..76ca7ee --- /dev/null +++ b/src/CompressedImage.php @@ -0,0 +1,100 @@ +qualityRate = $qualityRate; + $this->imageType = $imageType; + $this->imageObject = $imageObject; + } + + /** + * Mapping one value to another + * @param int $value value + * @param int $fromMin origin value min + * @param int $fromMax origin value max + * @param int $toMin target value min + * @param int $toMax target value max + * @return false|float + */ + private function mapValue(int $value, int $fromMin, int $fromMax, int $toMin, int $toMax) { + $fromRange = $fromMax - $fromMin; + $toRange = $toMax - $toMin; + + $scaled = ($value - $fromMin) / $fromRange; + + return ceil($toMin + ($scaled * $toRange)); + } + + /** + * Outputs current image as base64 string + * @return string + */ + public function toBase64(): string + { + ob_start(); + switch ($this->imageType) + { + case "image/jpeg": + imagejpeg($this->imageObject, null, $this->qualityRate); + break; + case "image/png": + imagepng( $this->imageObject, null, self::mapValue($this->qualityRate, 0, 100, 0, 9)); + break; + case "image/gif": + imagegif($this->imageObject); + break; + } + $base64_output = ob_get_contents(); + ob_end_clean(); + + $base64_output = base64_encode($base64_output); + return $base64_output; + } + + /** Outputs current image to file with provided path + * @param $filePath string new file name without extension + */ + public function toFile($filePath): void + { + switch ($this->imageType) + { + case "image/jpeg": + imagejpeg($this->imageObject, $filePath.".jpg", $this->qualityRate); + break; + case "image/png": + imagepng( $this->imageObject, $filePath.".png", self::mapValue($this->qualityRate, 0, 100, 0, 9)); + break; + case "image/gif": + imagegif($this->imageObject, $filePath.".gif"); + break; + } + } + + /** + * Outputs current image to GdImage + * @return \GdImage + */ + public function toGdImage(): \GdImage + { + return $this->imageObject; + } +} \ No newline at end of file diff --git a/src/SimpleImageCompressor.php b/src/SimpleImageCompressor.php new file mode 100644 index 0000000..72b5f03 --- /dev/null +++ b/src/SimpleImageCompressor.php @@ -0,0 +1,153 @@ +imageResourceUrl = $url; + } + + /** + * Returns current loaded image type + * @return string + */ + public function getImageType(): string + { + return $this->imageType; + } + + /** + * @return int + */ + public function getApproxMinimumHeight(): int + { + return $this->approxMinimumHeight; + } + + /** + * @return int + */ + public function getApproxMinimumWidth(): int + { + return $this->approxMinimumWidth; + } + + /** + * @param int $approxMinimumHeight + */ + public function setApproxMinimumHeight(int $approxMinimumHeight): SimpleImageCompressor + { + $this->approxMinimumHeight = $approxMinimumHeight; + return $this; + } + + /** + * @param int $approxMinimumWidth + */ + public function setApproxMinimumWidth(int $approxMinimumWidth): SimpleImageCompressor + { + $this->approxMinimumWidth = $approxMinimumWidth; + return $this; + } + + /** + * Determine current loaded image type + */ + private function loadImageType() { + $filetype = ''; + + if (substr($this->imageData, 0, 2) === "\xFF\xD8") { + $filetype = 'image/jpeg'; + } elseif (substr($this->imageData, 0, 3) === "\x89\x50\x4E") { + $filetype = 'image/png'; + } elseif (substr($this->imageData, 0, 4) === "\x47\x49\x46\x38") { + $filetype = 'image/gif'; + } + + $this->imageType = $filetype; + } + + /** + * Loading image from provided url. Whether it local file or internet resource + */ + private function readImageToString(): void { + $imageData = file_get_contents($this->imageResourceUrl); + $this->imageData = $imageData; + } + + /** + * Resizes and compressing image. + * Note that gif compression not supported + * @param int $resolutionReductionPercent percent shows how much image resolution to original will be. The greater percent, the lower resolution + * @param int $quality + * @return CompressedImage + */ + public function resizeAndCompress($resolutionReductionPercent = 5, $quality = 90): CompressedImage + { + $im = imagecreatefromstring($this->imageData); + $width = imagesx($im); + $height = imagesy($im); + + $totalPixelCount = $width * $height; + $minimumPixelCount = $this->approxMinimumWidth * $this->approxMinimumHeight; + $maximumResolutionReductionPercent = round(abs(100 - ($minimumPixelCount / $totalPixelCount * 100))); + + //Due to saving proportion we can't guarantee that width and height be equals max and min + //As example, if we have original image 1920*1080 which we want to get 50% of original resolution + //If we want to save 16*9 aspect ration it must be 960*540 + //So, we override $maximumResolutionReductionPercent to value which satisfy origin aspect ratio + if($maximumResolutionReductionPercent < $resolutionReductionPercent) + $resolutionReductionPercent = $maximumResolutionReductionPercent; + + $newWidth = round($width - ($width * $resolutionReductionPercent) / 100); + $newHeight = round($height - ($height * $resolutionReductionPercent) / 100); + + $thumb = imagecreatetruecolor($newWidth, $newHeight); + imagecopyresized($thumb, $im, 0, 0, 0, 0, $newWidth, $newHeight, $width, $height); + + return new CompressedImage($quality, $this->imageType,$thumb); + } + + /** + * Initalize lib from provided image file path + * @param string $urlImageResource image resource path. Can be local file or internet URL + * @return SimpleImageCompressor + * @throws \Exception + */ + public static function load(string $urlImageResource): SimpleImageCompressor + { + $compressorObject = new SimpleImageCompressor($urlImageResource); + $compressorObject->readImageToString(); + $compressorObject->loadImageType(); + + if(!str_contains(self::ALLOWED_IMAGE_FORMAT, $compressorObject->getImageType()) + || $compressorObject->getImageType() === "") + throw new \Exception("Provided file resource must contain one of image mime types: ".self::ALLOWED_IMAGE_FORMAT); + + return $compressorObject; + } +} \ No newline at end of file