From: Alexander Ebert Date: Mon, 11 Jan 2021 17:33:56 +0000 (+0100) Subject: Image adapters now support saving to GIF/JPG/PNG/WebP (#3869) X-Git-Tag: 5.4.0_Alpha_1~458 X-Git-Url: https://git.stricted.de/?a=commitdiff_plain;h=96fcf43c58aa8f87af1be30f11f5bb090fa13fcc;p=GitHub%2FWoltLab%2FWCF.git Image adapters now support saving to GIF/JPG/PNG/WebP (#3869) * Image adapters now support saving to GIF/JPG/PNG/WebP * Adjusted the usage of exception * Missing remark on the version support --- diff --git a/wcfsetup/install/files/lib/system/image/adapter/GDImageAdapter.class.php b/wcfsetup/install/files/lib/system/image/adapter/GDImageAdapter.class.php index 9d61e08f67..e68d97e493 100644 --- a/wcfsetup/install/files/lib/system/image/adapter/GDImageAdapter.class.php +++ b/wcfsetup/install/files/lib/system/image/adapter/GDImageAdapter.class.php @@ -347,8 +347,11 @@ class GDImageAdapter implements IImageAdapter { ob_start(); + // fix PNG alpha channel handling + // see http://php.net/manual/en/function.imagecopymerge.php#92787 imagealphablending($image, false); imagesavealpha($image, true); + if ($this->type == IMAGETYPE_GIF) { imagegif($image); } @@ -493,6 +496,49 @@ class GDImageAdapter implements IImageAdapter { // does nothing } + /** + * @inheritDoc + */ + public function saveImageAs($image, string $filename, string $type, int $quality = 100): void { + if (!$this->isImage($image)) { + throw new \InvalidArgumentException("Given image is not a valid image resource."); + } + + ob_start(); + + // fix PNG alpha channel handling + // see http://php.net/manual/en/function.imagecopymerge.php#92787 + imagealphablending($image, false); + imagesavealpha($image, true); + + switch ($type) { + case "gif": + imagegif($image); + break; + + case "jpg": + case "jpeg": + imagejpeg($image, null, $quality); + break; + + case "png": + imagepng($image, null, $quality); + break; + + case "webp": + imagewebp($image, null, $quality); + break; + + default: + throw new \InvalidArgumentException("Unreachable"); + } + + $stream = ob_get_contents(); + ob_end_clean(); + + file_put_contents($filename, $stream); + } + /** * @inheritDoc */ diff --git a/wcfsetup/install/files/lib/system/image/adapter/IImageAdapter.class.php b/wcfsetup/install/files/lib/system/image/adapter/IImageAdapter.class.php index 4bef9df373..6b4b9ae4f5 100644 --- a/wcfsetup/install/files/lib/system/image/adapter/IImageAdapter.class.php +++ b/wcfsetup/install/files/lib/system/image/adapter/IImageAdapter.class.php @@ -221,6 +221,13 @@ interface IImageAdapter { */ public function overlayImageRelative($file, $position, $margin, $opacity); + /** + * Saves an image using a different file type. + * + * @since 5.4 + */ + public function saveImageAs($image, string $filename, string $type, int $quality = 100): void; + /** * Determines if an image adapter is supported. * diff --git a/wcfsetup/install/files/lib/system/image/adapter/ImageAdapter.class.php b/wcfsetup/install/files/lib/system/image/adapter/ImageAdapter.class.php index 5f68955439..ff81973d0b 100644 --- a/wcfsetup/install/files/lib/system/image/adapter/ImageAdapter.class.php +++ b/wcfsetup/install/files/lib/system/image/adapter/ImageAdapter.class.php @@ -366,6 +366,30 @@ class ImageAdapter implements IImageAdapter, IMemoryAwareImageAdapter { return FileUtil::checkMemoryLimit($width * $height * $channels * 2.1); } + /** + * @inheritDoc + */ + public function saveImageAs($image, string $filename, string $type, int $quality = 100): void { + switch ($type) { + case "gif": + case "jpg": + case "jpeg": + case "png": + case "webp": + break; + + default: + throw new \InvalidArgumentException("Unsupported image format '{$type}'."); + } + + if ($quality < 0 || $quality > 100) { + throw new \InvalidArgumentException("The quality must be an integer between 0 and 100."); + } + + $this->adapter->saveImageAs($image, $filename, $type, $quality); + } + + /** * @inheritDoc */ diff --git a/wcfsetup/install/files/lib/system/image/adapter/ImagickImageAdapter.class.php b/wcfsetup/install/files/lib/system/image/adapter/ImagickImageAdapter.class.php index eb1912e9ec..09ee4073fa 100644 --- a/wcfsetup/install/files/lib/system/image/adapter/ImagickImageAdapter.class.php +++ b/wcfsetup/install/files/lib/system/image/adapter/ImagickImageAdapter.class.php @@ -461,6 +461,46 @@ class ImagickImageAdapter implements IImageAdapter { return $match['version']; } + /** + * @inheritDoc + */ + public function saveImageAs($image, string $filename, string $type, int $quality = 100): void { + if (!($image instanceof \Imagick)) { + throw new \InvalidArgumentException("Given image is not a valid Imagick-object."); + } + + // Greatly reduces the time required to create the image and drastically + // reduces the filesize to more reasonable levels without a visible + // quality loss. + // + // See https://github.com/Imagick/imagick/issues/360 + if ($image->getImageFormat() == "GIF") { + $image = $image->deconstructImages(); + $image->quantizeImages(256, \Imagick::COLORSPACE_SRGB, 0, false, false); + } + + switch ($type) { + case "jpg": + case "jpeg": + $fileFormat = "jpg"; + break; + + case "png": + $fileFormat = "png"; + break; + + case "webp": + $fileFormat = "webp"; + break; + + default: + throw new \LogicException("Unreachable"); + } + + $image->writeImages("{$fileFormat}:{$filename}", true); + } + + /** * @param string $version * @return bool