Skip to content

Commit

Permalink
Output encoder results directly to file pointer
Browse files Browse the repository at this point in the history
  • Loading branch information
olivervogel committed Sep 19, 2024
1 parent e2f4584 commit 9aaf5d3
Show file tree
Hide file tree
Showing 18 changed files with 75 additions and 80 deletions.
20 changes: 12 additions & 8 deletions src/Drivers/AbstractEncoder.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,17 @@

namespace Intervention\Image\Drivers;

use Intervention\Image\EncodedImage;
use Intervention\Image\Exceptions\RuntimeException;
use Intervention\Image\Interfaces\EncodedImageInterface;
use Intervention\Image\Interfaces\EncoderInterface;
use Intervention\Image\Interfaces\ImageInterface;
use Intervention\Image\Traits\CanBuildFilePointer;

abstract class AbstractEncoder implements EncoderInterface
{
use CanBuildFilePointer;

public const DEFAULT_QUALITY = 75;

/**
Expand All @@ -23,18 +28,17 @@ public function encode(ImageInterface $image): EncodedImageInterface
}

/**
* Get return value of callback through output buffer
* Build new file pointer, run callback with it and return result as encoded image
*
* @param callable $callback
* @return string
* @throws RuntimeException
* @return EncodedImage
*/
protected function buffered(callable $callback): string
protected function createEncodedImage(callable $callback): EncodedImage
{
ob_start();
$callback();
$buffer = ob_get_contents();
ob_end_clean();
$pointer = $this->buildFilePointer();
$callback($pointer);

return $buffer;
return new EncodedImage($pointer);
}
}
7 changes: 2 additions & 5 deletions src/Drivers/Gd/Encoders/AvifEncoder.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,8 @@ class AvifEncoder extends GenericAvifEncoder implements SpecializedInterface
*/
public function encode(ImageInterface $image): EncodedImage
{
$gd = $image->core()->native();
$data = $this->buffered(function () use ($gd) {
imageavif($gd, null, $this->quality);
return $this->createEncodedImage(function ($pointer) use ($image) {
imageavif($image->core()->native(), $pointer, $this->quality);
});

return new EncodedImage($data);
}
}
6 changes: 2 additions & 4 deletions src/Drivers/Gd/Encoders/BmpEncoder.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,8 @@ class BmpEncoder extends GenericBmpEncoder implements SpecializedInterface
*/
public function encode(ImageInterface $image): EncodedImage
{
$data = $this->buffered(function () use ($image) {
imagebmp($image->core()->native(), null, false);
return $this->createEncodedImage(function ($pointer) use ($image) {
imagebmp($image->core()->native(), $pointer, false);
});

return new EncodedImage($data);
}
}
7 changes: 3 additions & 4 deletions src/Drivers/Gd/Encoders/GifEncoder.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,11 @@ public function encode(ImageInterface $image): EncodedImage
}

$gd = Cloner::clone($image->core()->native());
$data = $this->buffered(function () use ($gd) {

return $this->createEncodedImage(function ($pointer) use ($gd) {
imageinterlace($gd, $this->interlaced);
imagegif($gd);
imagegif($gd, $pointer);
});

return new EncodedImage($data);
}

/**
Expand Down
6 changes: 2 additions & 4 deletions src/Drivers/Gd/Encoders/JpegEncoder.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,9 @@ public function encode(ImageInterface $image): EncodedImage
background: $blendingColor
);

$data = $this->buffered(function () use ($output) {
return $this->createEncodedImage(function ($pointer) use ($output) {
imageinterlace($output, $this->progressive);
imagejpeg($output, null, $this->quality);
imagejpeg($output, $pointer, $this->quality);
});

return new EncodedImage($data);
}
}
7 changes: 2 additions & 5 deletions src/Drivers/Gd/Encoders/PngEncoder.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,10 @@ public function encode(ImageInterface $image): EncodedImage
{
$output = $this->prepareOutput($image);

// encode
$data = $this->buffered(function () use ($output) {
return $this->createEncodedImage(function ($pointer) use ($output) {
imageinterlace($output, $this->interlaced);
imagepng($output, null, -1);
imagepng($output, $pointer, -1);
});

return new EncodedImage($data);
}

/**
Expand Down
7 changes: 3 additions & 4 deletions src/Drivers/Gd/Encoders/WebpEncoder.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,9 @@ class WebpEncoder extends GenericWebpEncoder implements SpecializedInterface
public function encode(ImageInterface $image): EncodedImage
{
$quality = $this->quality === 100 ? IMG_WEBP_LOSSLESS : $this->quality;
$data = $this->buffered(function () use ($image, $quality) {
imagewebp($image->core()->native(), null, $quality);
});

return new EncodedImage($data);
return $this->createEncodedImage(function ($pointer) use ($image, $quality) {
imagewebp($image->core()->native(), $pointer, $quality);
});
}
}
8 changes: 5 additions & 3 deletions src/Drivers/Imagick/Encoders/AvifEncoder.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@
namespace Intervention\Image\Drivers\Imagick\Encoders;

use Imagick;
use Intervention\Image\EncodedImage;
use Intervention\Image\Encoders\AvifEncoder as GenericAvifEncoder;
use Intervention\Image\Interfaces\EncodedImageInterface;
use Intervention\Image\Interfaces\ImageInterface;
use Intervention\Image\Interfaces\SpecializedInterface;

class AvifEncoder extends GenericAvifEncoder implements SpecializedInterface
{
public function encode(ImageInterface $image): EncodedImage
public function encode(ImageInterface $image): EncodedImageInterface
{
$format = 'AVIF';
$compression = Imagick::COMPRESSION_ZIP;
Expand All @@ -25,6 +25,8 @@ public function encode(ImageInterface $image): EncodedImage
$imagick->setCompressionQuality($this->quality);
$imagick->setImageCompressionQuality($this->quality);

return new EncodedImage($imagick->getImagesBlob());
return $this->createEncodedImage(function ($pointer) use ($imagick, $format) {
$imagick->writeImageFile($pointer, $format);
});
}
}
8 changes: 5 additions & 3 deletions src/Drivers/Imagick/Encoders/BmpEncoder.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@
namespace Intervention\Image\Drivers\Imagick\Encoders;

use Imagick;
use Intervention\Image\EncodedImage;
use Intervention\Image\Encoders\BmpEncoder as GenericBmpEncoder;
use Intervention\Image\Interfaces\EncodedImageInterface;
use Intervention\Image\Interfaces\ImageInterface;
use Intervention\Image\Interfaces\SpecializedInterface;

class BmpEncoder extends GenericBmpEncoder implements SpecializedInterface
{
public function encode(ImageInterface $image): EncodedImage
public function encode(ImageInterface $image): EncodedImageInterface
{
$format = 'BMP';
$compression = Imagick::COMPRESSION_NO;
Expand All @@ -23,6 +23,8 @@ public function encode(ImageInterface $image): EncodedImage
$imagick->setCompression($compression);
$imagick->setImageCompression($compression);

return new EncodedImage($imagick->getImagesBlob());
return $this->createEncodedImage(function ($pointer) use ($imagick, $format) {
$imagick->writeImageFile($pointer, $format);
});
}
}
8 changes: 5 additions & 3 deletions src/Drivers/Imagick/Encoders/GifEncoder.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@
namespace Intervention\Image\Drivers\Imagick\Encoders;

use Imagick;
use Intervention\Image\EncodedImage;
use Intervention\Image\Encoders\GifEncoder as GenericGifEncoder;
use Intervention\Image\Interfaces\EncodedImageInterface;
use Intervention\Image\Interfaces\ImageInterface;
use Intervention\Image\Interfaces\SpecializedInterface;

class GifEncoder extends GenericGifEncoder implements SpecializedInterface
{
public function encode(ImageInterface $image): EncodedImage
public function encode(ImageInterface $image): EncodedImageInterface
{
$format = 'GIF';
$compression = Imagick::COMPRESSION_LZW;
Expand All @@ -28,6 +28,8 @@ public function encode(ImageInterface $image): EncodedImage
$imagick->setInterlaceScheme(Imagick::INTERLACE_LINE);
}

return new EncodedImage($imagick->getImagesBlob());
return $this->createEncodedImage(function ($pointer) use ($imagick, $format) {
$imagick->writeImageFile($pointer, $format);
});
}
}
7 changes: 4 additions & 3 deletions src/Drivers/Imagick/Encoders/HeicEncoder.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,9 @@

namespace Intervention\Image\Drivers\Imagick\Encoders;

use Intervention\Image\EncodedImage;
use Intervention\Image\Encoders\HeicEncoder as GenericHeicEncoder;
use Intervention\Image\Interfaces\ImageInterface;
use Intervention\Image\Interfaces\EncodedImageInterface;
use Intervention\Image\Interfaces\ImageInterface;
use Intervention\Image\Interfaces\SpecializedInterface;

class HeicEncoder extends GenericHeicEncoder implements SpecializedInterface
Expand All @@ -23,6 +22,8 @@ public function encode(ImageInterface $image): EncodedImageInterface
$imagick->setCompressionQuality($this->quality);
$imagick->setImageCompressionQuality($this->quality);

return new EncodedImage($imagick->getImagesBlob());
return $this->createEncodedImage(function ($pointer) use ($imagick, $format) {
$imagick->writeImageFile($pointer, $format);
});
}
}
3 changes: 3 additions & 0 deletions src/Drivers/Imagick/Encoders/Jpeg2000Encoder.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ public function encode(ImageInterface $image): EncodedImageInterface
$imagick->setCompressionQuality($this->quality);
$imagick->setImageCompressionQuality($this->quality);

// encoding fails with Imagick::writeImageFile() for JP2 format
// The reasons are unknown, but could be fixed by Imagick/Imagemagick
// in the future. Until then, I use getImagesBlob() for Jpeg2000.
return new EncodedImage($imagick->getImagesBlob());
}
}
8 changes: 5 additions & 3 deletions src/Drivers/Imagick/Encoders/JpegEncoder.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@
namespace Intervention\Image\Drivers\Imagick\Encoders;

use Imagick;
use Intervention\Image\EncodedImage;
use Intervention\Image\Encoders\JpegEncoder as GenericJpegEncoder;
use Intervention\Image\Interfaces\EncodedImageInterface;
use Intervention\Image\Interfaces\ImageInterface;
use Intervention\Image\Interfaces\SpecializedInterface;

class JpegEncoder extends GenericJpegEncoder implements SpecializedInterface
{
public function encode(ImageInterface $image): EncodedImage
public function encode(ImageInterface $image): EncodedImageInterface
{
$format = 'JPEG';
$compression = Imagick::COMPRESSION_JPEG;
Expand Down Expand Up @@ -44,6 +44,8 @@ public function encode(ImageInterface $image): EncodedImage
$imagick->setInterlaceScheme(Imagick::INTERLACE_PLANE);
}

return new EncodedImage($imagick->getImagesBlob());
return $this->createEncodedImage(function ($pointer) use ($imagick, $format) {
$imagick->writeImageFile($pointer, $format);
});
}
}
23 changes: 13 additions & 10 deletions src/Drivers/Imagick/Encoders/PngEncoder.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@

use Imagick;
use ImagickException;
use Intervention\Image\EncodedImage;
use Intervention\Image\Encoders\PngEncoder as GenericPngEncoder;
use Intervention\Image\Exceptions\AnimationException;
use Intervention\Image\Exceptions\RuntimeException;
use Intervention\Image\Exceptions\ColorException;
use Intervention\Image\Interfaces\EncodedImageInterface;
use Intervention\Image\Interfaces\ImageInterface;
use Intervention\Image\Interfaces\SpecializedInterface;

Expand All @@ -21,7 +21,7 @@ class PngEncoder extends GenericPngEncoder implements SpecializedInterface
*
* @see EncoderInterface::encode()
*/
public function encode(ImageInterface $image): EncodedImage
public function encode(ImageInterface $image): EncodedImageInterface
{
$output = $this->prepareOutput($image);

Expand All @@ -32,7 +32,9 @@ public function encode(ImageInterface $image): EncodedImage
$output->setInterlaceScheme(Imagick::INTERLACE_LINE);
}

return new EncodedImage($output->getImagesBlob());
return $this->createEncodedImage(function ($pointer) use ($output) {
$output->writeImageFile($pointer, $this->format());

Check failure on line 36 in src/Drivers/Imagick/Encoders/PngEncoder.php

View workflow job for this annotation

GitHub Actions / PHP 8.1 - prefer-stable - ImageMagick 6.9.12-55

Method Imagick::writeImageFile() invoked with 2 parameters, 1 required.

Check failure on line 36 in src/Drivers/Imagick/Encoders/PngEncoder.php

View workflow job for this annotation

GitHub Actions / PHP 8.1 - prefer-stable - ImageMagick 7.1.1-32

Method Imagick::writeImageFile() invoked with 2 parameters, 1 required.

Check failure on line 36 in src/Drivers/Imagick/Encoders/PngEncoder.php

View workflow job for this annotation

GitHub Actions / PHP 8.2 - prefer-stable - ImageMagick 6.9.12-55

Method Imagick::writeImageFile() invoked with 2 parameters, 1 required.

Check failure on line 36 in src/Drivers/Imagick/Encoders/PngEncoder.php

View workflow job for this annotation

GitHub Actions / PHP 8.2 - prefer-stable - ImageMagick 7.1.1-32

Method Imagick::writeImageFile() invoked with 2 parameters, 1 required.

Check failure on line 36 in src/Drivers/Imagick/Encoders/PngEncoder.php

View workflow job for this annotation

GitHub Actions / PHP 8.3 - prefer-stable - ImageMagick 6.9.12-55

Method Imagick::writeImageFile() invoked with 2 parameters, 1 required.

Check failure on line 36 in src/Drivers/Imagick/Encoders/PngEncoder.php

View workflow job for this annotation

GitHub Actions / PHP 8.3 - prefer-stable - ImageMagick 7.1.1-32

Method Imagick::writeImageFile() invoked with 2 parameters, 1 required.

Check failure on line 36 in src/Drivers/Imagick/Encoders/PngEncoder.php

View workflow job for this annotation

GitHub Actions / PHP 8.1 - prefer-stable - ImageMagick 6.9.12-55

Method Imagick::writeImageFile() invoked with 2 parameters, 1 required.

Check failure on line 36 in src/Drivers/Imagick/Encoders/PngEncoder.php

View workflow job for this annotation

GitHub Actions / PHP 8.4 - prefer-stable - ImageMagick 6.9.12-55

Method Imagick::writeImageFile() invoked with 2 parameters, 1 required.

Check failure on line 36 in src/Drivers/Imagick/Encoders/PngEncoder.php

View workflow job for this annotation

GitHub Actions / PHP 8.1 - prefer-stable - ImageMagick 7.1.1-32

Method Imagick::writeImageFile() invoked with 2 parameters, 1 required.

Check failure on line 36 in src/Drivers/Imagick/Encoders/PngEncoder.php

View workflow job for this annotation

GitHub Actions / PHP 8.4 - prefer-stable - ImageMagick 7.1.1-32

Method Imagick::writeImageFile() invoked with 2 parameters, 1 required.

Check failure on line 36 in src/Drivers/Imagick/Encoders/PngEncoder.php

View workflow job for this annotation

GitHub Actions / PHP 8.2 - prefer-stable - ImageMagick 6.9.12-55

Method Imagick::writeImageFile() invoked with 2 parameters, 1 required.

Check failure on line 36 in src/Drivers/Imagick/Encoders/PngEncoder.php

View workflow job for this annotation

GitHub Actions / PHP 8.2 - prefer-stable - ImageMagick 7.1.1-32

Method Imagick::writeImageFile() invoked with 2 parameters, 1 required.

Check failure on line 36 in src/Drivers/Imagick/Encoders/PngEncoder.php

View workflow job for this annotation

GitHub Actions / PHP 8.3 - prefer-stable - ImageMagick 6.9.12-55

Method Imagick::writeImageFile() invoked with 2 parameters, 1 required.

Check failure on line 36 in src/Drivers/Imagick/Encoders/PngEncoder.php

View workflow job for this annotation

GitHub Actions / PHP 8.3 - prefer-stable - ImageMagick 7.1.1-32

Method Imagick::writeImageFile() invoked with 2 parameters, 1 required.

Check failure on line 36 in src/Drivers/Imagick/Encoders/PngEncoder.php

View workflow job for this annotation

GitHub Actions / PHP 8.4 - prefer-stable - ImageMagick 6.9.12-55

Method Imagick::writeImageFile() invoked with 2 parameters, 1 required.

Check failure on line 36 in src/Drivers/Imagick/Encoders/PngEncoder.php

View workflow job for this annotation

GitHub Actions / PHP 8.4 - prefer-stable - ImageMagick 7.1.1-32

Method Imagick::writeImageFile() invoked with 2 parameters, 1 required.
});
}

/**
Expand All @@ -52,20 +54,21 @@ private function prepareOutput(ImageInterface $image): Imagick
if ($this->indexed) {
// reduce colors
$output->reduceColors(256);

$output = $output->core()->native();

$output->setFormat('PNG');
$output->setImageFormat('PNG');

return $output;
}

// ensure to encode PNG image type 6 (true color alpha)
$output = clone $image->core()->native();
$output->setFormat('PNG32');
$output->setImageFormat('PNG32');

return $output;
}

private function format(): string
{
return match ($this->indexed) {
true => 'PNG',
false => 'PNG32', // ensure to encode PNG image type 6 (true color alpha)
};
}
}
3 changes: 3 additions & 0 deletions src/Drivers/Imagick/Encoders/TiffEncoder.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ public function encode(ImageInterface $image): EncodedImageInterface
$imagick->setCompressionQuality($this->quality);
$imagick->setImageCompressionQuality($this->quality);

// encoding fails with Imagick::writeImageFile() for JP2 format
// the reasons are unknown, but could be fixed by Imagick/Imagemagick
// in the future. Until then, I use getImagesBlob() for Jpeg2000.
return new EncodedImage($imagick->getImagesBlob());
}
}
8 changes: 5 additions & 3 deletions src/Drivers/Imagick/Encoders/WebpEncoder.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@

use Imagick;
use ImagickPixel;
use Intervention\Image\EncodedImage;
use Intervention\Image\Encoders\WebpEncoder as GenericWebpEncoder;
use Intervention\Image\Interfaces\EncodedImageInterface;
use Intervention\Image\Interfaces\ImageInterface;
use Intervention\Image\Interfaces\SpecializedInterface;

class WebpEncoder extends GenericWebpEncoder implements SpecializedInterface
{
public function encode(ImageInterface $image): EncodedImage
public function encode(ImageInterface $image): EncodedImageInterface
{
$format = 'WEBP';
$compression = Imagick::COMPRESSION_ZIP;
Expand All @@ -31,6 +31,8 @@ public function encode(ImageInterface $image): EncodedImage
$imagick->setImageCompression($compression);
$imagick->setImageCompressionQuality($this->quality);

return new EncodedImage($imagick->getImagesBlob());
return $this->createEncodedImage(function ($pointer) use ($imagick, $format) {
$imagick->writeImageFile($pointer, $format);
});
}
}
2 changes: 1 addition & 1 deletion src/Traits/CanBuildFilePointer.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ trait CanBuildFilePointer
* @throws RuntimeException
* @return resource|false
*/
public function buildFilePointer(mixed $data)
public function buildFilePointer(mixed $data = null)
{
switch (true) {
case is_string($data):
Expand Down
Loading

0 comments on commit 9aaf5d3

Please sign in to comment.