diff --git a/src/Image/Image.php b/src/Image/Image.php index a9ca35a..aa51f56 100644 --- a/src/Image/Image.php +++ b/src/Image/Image.php @@ -97,8 +97,6 @@ public static function getGravityTypes(): array } /** - * @return Image - * * @throws \Throwable */ public function crop(int $width, int $height, string $gravity = Image::GRAVITY_CENTER): self @@ -369,65 +367,15 @@ public function save(?string $path = null, string $type = '', int $quality = 75) case 'avif': case 'heic': - $signature = $this->image->getImageSignature(); - - $temp = tempnam(sys_get_temp_dir(), 'temp-'.$signature); - if ($temp === false) { - throw new Exception('Failed to create temporary file'); - } - - $output = tempnam(sys_get_temp_dir(), 'output-'.$signature); - if ($output === false) { - \unlink($temp); - throw new Exception('Failed to create output file'); - } - - $temp .= '.'.\strtolower($this->image->getImageFormat()); - $output .= '.'.$type; - - try { - // save temp - $this->image->writeImages($temp, true); - - // convert temp - $command = ['magick convert', \escapeshellarg($temp)]; - - $quality = (int) $quality; - if ($quality >= 0) { - $command = [...$command, '-quality', $quality]; - } - - $command = [ - ...$command, \escapeshellarg($output), '2>&1', // 2>&1 redirect stderr to stdout - ]; - - \exec(implode(' ', $command), $outputArray, $returnCode); - - if ($returnCode !== 0) { - throw new Exception("Image conversion failed with status {$returnCode}: ".implode("\n", $outputArray)); - } - - $data = \file_get_contents($output); - - // save to path - if (! empty($path)) { - \file_put_contents($path, $data, LOCK_EX); - - return; - } - - return $data; - } finally { - if (file_exists($temp)) { - \unlink($temp); - } - if (file_exists($output)) { - \unlink($output); - } - - $this->image->clear(); - $this->image->destroy(); + $this->image->setImageFormat($type); + if ($quality >= 0) { + // setImageCompressionQuality() is silently ignored by the libheif coder — + // setCompressionQuality() (object-level, not image-level) must be called + // after setImageFormat() for quality to take effect on AVIF/HEIC output. + // See: https://github.com/Imagick/imagick/issues/711 + $this->image->setCompressionQuality($quality); } + break; case 'webp': $temp = null; diff --git a/tests/Image/ImageTest.php b/tests/Image/ImageTest.php index 583c7a8..766ffc9 100644 --- a/tests/Image/ImageTest.php +++ b/tests/Image/ImageTest.php @@ -471,7 +471,7 @@ public function test_crop100x100_heic(): void $this->assertEquals(\is_readable($target), true); $this->assertGreaterThan(500, \filesize($target)); - $this->assertEquals(8426, \filesize($target)); + $this->assertEquals(8490, \filesize($target)); $this->assertEquals(\mime_content_type($target), \mime_content_type($original)); $this->assertFileExists($target); $this->assertNotEmpty(\file_get_contents($target));