mirror of
https://github.com/Intervention/image.git
synced 2025-03-15 22:49:40 +01:00
Change encoding logic
The logic for encoding image data in connection with a limit on total colors has been removed. The number of colors of an image can now be reduced with the corresponding modifier, but this is not related to the output of the encoder. Furthermore, there is now an AutoEncoder, which resumes the original format of the original image source and selects the encoder accordingly. This Origin attribute is set during the decoding process so that it can be read out again later. The AutoEncoder is now the default value of the parameter for Image::encode().
This commit is contained in:
parent
dbe27c6ab5
commit
9863a1f7d7
@ -55,10 +55,7 @@ class BinaryImageDecoder extends AbstractDecoder implements DecoderInterface
|
||||
|
||||
if ($info = getimagesizefromstring($input)) {
|
||||
$image->setOrigin(
|
||||
new Origin(
|
||||
$info['mime'],
|
||||
imagecolorstotal($gd)
|
||||
),
|
||||
new Origin($info['mime'])
|
||||
);
|
||||
}
|
||||
|
||||
@ -95,9 +92,10 @@ class BinaryImageDecoder extends AbstractDecoder implements DecoderInterface
|
||||
);
|
||||
}
|
||||
|
||||
return new Image(
|
||||
new Driver(),
|
||||
$core
|
||||
$image = new Image(new Driver(), $core);
|
||||
|
||||
return $image->setOrigin(
|
||||
new Origin('image/gif')
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -24,6 +24,10 @@ class FilePathImageDecoder extends BinaryImageDecoder implements DecoderInterfac
|
||||
throw new DecoderException('Unable to decode input');
|
||||
}
|
||||
|
||||
return parent::decode(file_get_contents($input));
|
||||
$image = parent::decode(file_get_contents($input));
|
||||
|
||||
// set origin
|
||||
|
||||
return $image;
|
||||
}
|
||||
}
|
||||
|
@ -3,21 +3,15 @@
|
||||
namespace Intervention\Image\Drivers\Gd\Encoders;
|
||||
|
||||
use Intervention\Image\Drivers\DriverSpecializedEncoder;
|
||||
use Intervention\Image\Modifiers\LimitColorsModifier;
|
||||
use Intervention\Image\EncodedImage;
|
||||
use Intervention\Image\Interfaces\ImageInterface;
|
||||
|
||||
/**
|
||||
* @property int $color_limit
|
||||
*/
|
||||
class BmpEncoder extends DriverSpecializedEncoder
|
||||
{
|
||||
public function encode(ImageInterface $image): EncodedImage
|
||||
{
|
||||
$image = $image->modify(new LimitColorsModifier($this->color_limit));
|
||||
$gd = $image->core()->native();
|
||||
$data = $this->getBuffered(function () use ($gd) {
|
||||
imagebmp($gd, null, false);
|
||||
$data = $this->getBuffered(function () use ($image) {
|
||||
imagebmp($image->core()->native(), null, false);
|
||||
});
|
||||
|
||||
return new EncodedImage($data, 'image/bmp');
|
||||
|
@ -4,13 +4,9 @@ namespace Intervention\Image\Drivers\Gd\Encoders;
|
||||
|
||||
use Intervention\Gif\Builder as GifBuilder;
|
||||
use Intervention\Image\Drivers\DriverSpecializedEncoder;
|
||||
use Intervention\Image\Modifiers\LimitColorsModifier;
|
||||
use Intervention\Image\EncodedImage;
|
||||
use Intervention\Image\Interfaces\ImageInterface;
|
||||
|
||||
/**
|
||||
* @property int $color_limit
|
||||
*/
|
||||
class GifEncoder extends DriverSpecializedEncoder
|
||||
{
|
||||
public function encode(ImageInterface $image): EncodedImage
|
||||
@ -19,7 +15,6 @@ class GifEncoder extends DriverSpecializedEncoder
|
||||
return $this->encodeAnimated($image);
|
||||
}
|
||||
|
||||
$image = $image->modify(new LimitColorsModifier($this->color_limit));
|
||||
$gd = $image->core()->native();
|
||||
$data = $this->getBuffered(function () use ($gd) {
|
||||
imagegif($gd);
|
||||
|
@ -5,19 +5,13 @@ namespace Intervention\Image\Drivers\Gd\Encoders;
|
||||
use Intervention\Image\Drivers\DriverSpecializedEncoder;
|
||||
use Intervention\Image\EncodedImage;
|
||||
use Intervention\Image\Interfaces\ImageInterface;
|
||||
use Intervention\Image\Modifiers\LimitColorsModifier;
|
||||
|
||||
/**
|
||||
* @property int $color_limit
|
||||
*/
|
||||
class PngEncoder extends DriverSpecializedEncoder
|
||||
{
|
||||
public function encode(ImageInterface $image): EncodedImage
|
||||
{
|
||||
$image = $image->modify(new LimitColorsModifier($this->color_limit));
|
||||
$gd = $image->core()->native();
|
||||
$data = $this->getBuffered(function () use ($gd) {
|
||||
imagepng($gd, null, -1);
|
||||
$data = $this->getBuffered(function () use ($image) {
|
||||
imagepng($image->core()->native(), null, -1);
|
||||
});
|
||||
|
||||
return new EncodedImage($data, 'image/png');
|
||||
|
@ -27,8 +27,11 @@ class LimitColorsModifier extends DriverSpecializedModifier
|
||||
$height = $image->height();
|
||||
|
||||
foreach ($image as $frame) {
|
||||
// create empty gd
|
||||
$reduced = imagecreatetruecolor($width, $height);
|
||||
// limitied color image must be palette (indexed) GDImage
|
||||
$reduced = imagecreate($width, $height);
|
||||
|
||||
// save alpha channel
|
||||
imagesavealpha($reduced, true);
|
||||
|
||||
// create matte
|
||||
$matte = imagecolorallocatealpha($reduced, 255, 255, 255, 127);
|
||||
@ -36,24 +39,24 @@ class LimitColorsModifier extends DriverSpecializedModifier
|
||||
// fill with matte
|
||||
imagefill($reduced, 0, 0, $matte);
|
||||
|
||||
// disable alpha blending for the copy process
|
||||
imagealphablending($reduced, false);
|
||||
|
||||
// set transparency and get transparency index
|
||||
imagecolortransparent($reduced, $matte);
|
||||
|
||||
// copy original image
|
||||
// copy original image (colors are limited automatically in the copy process)
|
||||
imagecopy($reduced, $frame->native(), 0, 0, 0, 0, $width, $height);
|
||||
|
||||
// reduce limit by one to include possible transparency in palette
|
||||
$limit = imagecolortransparent($frame->native()) === -1 ? $this->limit : $this->limit - 1;
|
||||
// $limit = imagecolortransparent($frame->native()) === -1 ? $this->limit : $this->limit - 1;
|
||||
|
||||
// decrease colors
|
||||
imagetruecolortopalette($reduced, true, $limit);
|
||||
// imagetruecolortopalette($reduced, true, $limit);
|
||||
|
||||
$frame->setNative($reduced);
|
||||
}
|
||||
|
||||
|
||||
return $image;
|
||||
}
|
||||
}
|
||||
|
@ -56,8 +56,7 @@ class BinaryImageDecoder extends AbstractDecoder implements DecoderInterface
|
||||
);
|
||||
|
||||
$image->setOrigin(new Origin(
|
||||
$imagick->getImageMimeType(),
|
||||
$imagick->getImageColors()
|
||||
$imagick->getImageMimeType()
|
||||
));
|
||||
|
||||
return $image;
|
||||
|
@ -4,13 +4,9 @@ namespace Intervention\Image\Drivers\Imagick\Encoders;
|
||||
|
||||
use Imagick;
|
||||
use Intervention\Image\Drivers\DriverSpecializedEncoder;
|
||||
use Intervention\Image\Modifiers\LimitColorsModifier;
|
||||
use Intervention\Image\EncodedImage;
|
||||
use Intervention\Image\Interfaces\ImageInterface;
|
||||
|
||||
/**
|
||||
* @property int $color_limit
|
||||
*/
|
||||
class BmpEncoder extends DriverSpecializedEncoder
|
||||
{
|
||||
public function encode(ImageInterface $image): EncodedImage
|
||||
@ -18,7 +14,6 @@ class BmpEncoder extends DriverSpecializedEncoder
|
||||
$format = 'bmp';
|
||||
$compression = Imagick::COMPRESSION_NO;
|
||||
|
||||
$image = $image->modify(new LimitColorsModifier($this->color_limit));
|
||||
$imagick = $image->core()->native();
|
||||
$imagick->setFormat($format);
|
||||
$imagick->setImageFormat($format);
|
||||
|
@ -4,20 +4,13 @@ namespace Intervention\Image\Drivers\Imagick\Encoders;
|
||||
|
||||
use Imagick;
|
||||
use Intervention\Image\Drivers\DriverSpecializedEncoder;
|
||||
use Intervention\Image\Modifiers\LimitColorsModifier;
|
||||
use Intervention\Image\EncodedImage;
|
||||
use Intervention\Image\Interfaces\ImageInterface;
|
||||
|
||||
/**
|
||||
* @property int $color_limit
|
||||
*/
|
||||
class GifEncoder extends DriverSpecializedEncoder
|
||||
{
|
||||
public function encode(ImageInterface $image): EncodedImage
|
||||
{
|
||||
$image = $image->modify(new LimitColorsModifier($this->color_limit));
|
||||
|
||||
|
||||
$format = 'gif';
|
||||
$compression = Imagick::COMPRESSION_LZW;
|
||||
|
||||
|
@ -4,13 +4,9 @@ namespace Intervention\Image\Drivers\Imagick\Encoders;
|
||||
|
||||
use Imagick;
|
||||
use Intervention\Image\Drivers\DriverSpecializedEncoder;
|
||||
use Intervention\Image\Modifiers\LimitColorsModifier;
|
||||
use Intervention\Image\EncodedImage;
|
||||
use Intervention\Image\Interfaces\ImageInterface;
|
||||
|
||||
/**
|
||||
* @property int $color_limit
|
||||
*/
|
||||
class PngEncoder extends DriverSpecializedEncoder
|
||||
{
|
||||
public function encode(ImageInterface $image): EncodedImage
|
||||
@ -18,7 +14,6 @@ class PngEncoder extends DriverSpecializedEncoder
|
||||
$format = 'png';
|
||||
$compression = Imagick::COMPRESSION_ZIP;
|
||||
|
||||
$image = $image->modify(new LimitColorsModifier($this->color_limit));
|
||||
$imagick = $image->core()->native();
|
||||
$imagick->setFormat($format);
|
||||
$imagick->setImageFormat($format);
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
namespace Intervention\Image\Encoders;
|
||||
|
||||
use Intervention\Gif\Exception\EncoderException;
|
||||
use Intervention\Image\Interfaces\EncodedImageInterface;
|
||||
use Intervention\Image\Interfaces\EncoderInterface;
|
||||
use Intervention\Image\Interfaces\ImageInterface;
|
||||
@ -10,14 +11,16 @@ class AutoEncoder implements EncoderInterface
|
||||
{
|
||||
public function encode(ImageInterface $image): EncodedImageInterface
|
||||
{
|
||||
$type = $image->origin()->mimetype();
|
||||
return $image->encode(
|
||||
match ($image->origin()->mimetype()) {
|
||||
match ($type) {
|
||||
'image/webp' => new WebpEncoder(),
|
||||
'image/avif' => new AvifEncoder(),
|
||||
'image/jpeg' => new JpegEncoder(),
|
||||
'image/bmp' => new BmpEncoder(),
|
||||
'image/gif' => new GifEncoder(),
|
||||
default => new PngEncoder(),
|
||||
'image/png' => new PngEncoder(),
|
||||
default => throw new EncoderException('No encoder found for media type (' . $type . ').'),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
@ -4,7 +4,4 @@ namespace Intervention\Image\Encoders;
|
||||
|
||||
class BmpEncoder extends AbstractEncoder
|
||||
{
|
||||
public function __construct(public int $color_limit = 0)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,4 @@ namespace Intervention\Image\Encoders;
|
||||
|
||||
class GifEncoder extends AbstractEncoder
|
||||
{
|
||||
public function __construct(public int $color_limit = 0)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,4 @@ namespace Intervention\Image\Encoders;
|
||||
|
||||
class PngEncoder extends AbstractEncoder
|
||||
{
|
||||
public function __construct(public int $color_limit = 0)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ use Intervention\Image\Analyzers\PixelColorsAnalyzer;
|
||||
use Intervention\Image\Analyzers\ProfileAnalyzer;
|
||||
use Intervention\Image\Analyzers\ResolutionAnalyzer;
|
||||
use Intervention\Image\Analyzers\WidthAnalyzer;
|
||||
use Intervention\Image\Encoders\AutoEncoder;
|
||||
use Intervention\Image\Encoders\AvifEncoder;
|
||||
use Intervention\Image\Encoders\BmpEncoder;
|
||||
use Intervention\Image\Encoders\GifEncoder;
|
||||
@ -78,13 +79,27 @@ use Intervention\Image\Typography\FontFactory;
|
||||
|
||||
final class Image implements ImageInterface, Countable
|
||||
{
|
||||
/**
|
||||
* The origin from which the image was created
|
||||
*
|
||||
* @var Origin
|
||||
*/
|
||||
protected Origin $origin;
|
||||
|
||||
/**
|
||||
* Create new instance
|
||||
*
|
||||
* @param DriverInterface $driver
|
||||
* @param CoreInterface $core
|
||||
* @param CollectionInterface $exif
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(
|
||||
protected DriverInterface $driver,
|
||||
protected CoreInterface $core,
|
||||
protected CollectionInterface $exif = new Collection()
|
||||
) {
|
||||
$this->origin = new Origin();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -226,7 +241,7 @@ final class Image implements ImageInterface, Countable
|
||||
*
|
||||
* @see ImageInterface::encode()
|
||||
*/
|
||||
public function encode(EncoderInterface $encoder): EncodedImage
|
||||
public function encode(EncoderInterface $encoder = new AutoEncoder()): EncodedImage
|
||||
{
|
||||
return $this->driver->resolve($encoder)->encode($this);
|
||||
}
|
||||
@ -743,9 +758,9 @@ final class Image implements ImageInterface, Countable
|
||||
*
|
||||
* @see ImageInterface::toPng()
|
||||
*/
|
||||
public function toPng(int $color_limit = 0): EncodedImageInterface
|
||||
public function toPng(): EncodedImageInterface
|
||||
{
|
||||
return $this->encode(new PngEncoder($color_limit));
|
||||
return $this->encode(new PngEncoder());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -753,9 +768,9 @@ final class Image implements ImageInterface, Countable
|
||||
*
|
||||
* @see ImageInterface::toGif()
|
||||
*/
|
||||
public function toGif(int $color_limit = 0): EncodedImageInterface
|
||||
public function toGif(): EncodedImageInterface
|
||||
{
|
||||
return $this->encode(new GifEncoder($color_limit));
|
||||
return $this->encode(new GifEncoder());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -773,20 +788,19 @@ final class Image implements ImageInterface, Countable
|
||||
*
|
||||
* @see ImageInterface::toBitmap()
|
||||
*/
|
||||
public function toBitmap(int $color_limit = 0): EncodedImageInterface
|
||||
public function toBitmap(): EncodedImageInterface
|
||||
{
|
||||
return $this->encode(new BmpEncoder($color_limit));
|
||||
return $this->encode(new BmpEncoder());
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias if self::toBitmap()
|
||||
*
|
||||
* @param int $color_limit
|
||||
* @return EncodedImageInterface
|
||||
*/
|
||||
public function toBmp(int $color_limit = 0): EncodedImageInterface
|
||||
public function toBmp(): EncodedImageInterface
|
||||
{
|
||||
return $this->toBitmap($color_limit);
|
||||
return $this->toBitmap();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -567,26 +567,23 @@ interface ImageInterface extends IteratorAggregate, Countable
|
||||
/**
|
||||
* Encode image to PNG format
|
||||
*
|
||||
* @param int $color_limit
|
||||
* @return EncodedImageInterface
|
||||
*/
|
||||
public function toPng(int $color_limit = 0): EncodedImageInterface;
|
||||
public function toPng(): EncodedImageInterface;
|
||||
|
||||
/**
|
||||
* Encode image to GIF format
|
||||
*
|
||||
* @param int $color_limit
|
||||
* @return EncodedImageInterface
|
||||
*/
|
||||
public function toGif(int $color_limit = 0): EncodedImageInterface;
|
||||
public function toGif(): EncodedImageInterface;
|
||||
|
||||
/**
|
||||
* Encode image to Bitmap format
|
||||
*
|
||||
* @param int $color_limit
|
||||
* @return EncodedImageInterface
|
||||
*/
|
||||
public function toBitmap(int $color_limit = 0): EncodedImageInterface;
|
||||
public function toBitmap(): EncodedImageInterface;
|
||||
|
||||
/**
|
||||
* Encode image to AVIF format
|
||||
|
@ -4,9 +4,19 @@ namespace Intervention\Image;
|
||||
|
||||
class Origin
|
||||
{
|
||||
/**
|
||||
* Create new origin instance
|
||||
*
|
||||
* @param string $mimetype
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(
|
||||
protected string $mimetype,
|
||||
protected int $colors
|
||||
protected string $mimetype = 'application/octet-stream'
|
||||
) {
|
||||
}
|
||||
|
||||
public function mimetype(): string
|
||||
{
|
||||
return $this->mimetype;
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ declare(strict_types=1);
|
||||
namespace Intervention\Image\Tests\Drivers;
|
||||
|
||||
use Intervention\Image\Drivers\DriverSpecializedEncoder;
|
||||
use Intervention\Image\Encoders\PngEncoder;
|
||||
use Intervention\Image\Encoders\JpegEncoder;
|
||||
use Intervention\Image\Interfaces\DriverInterface;
|
||||
use Intervention\Image\Tests\TestCase;
|
||||
use Mockery;
|
||||
@ -29,10 +29,10 @@ class DriverSpecializedEncoderTest extends TestCase
|
||||
public function testGetAttributes(): void
|
||||
{
|
||||
$encoder = Mockery::mock(DriverSpecializedEncoder::class, [
|
||||
new PngEncoder(color_limit: 123),
|
||||
new JpegEncoder(quality: 10),
|
||||
Mockery::mock(DriverInterface::class),
|
||||
])->makePartial();
|
||||
|
||||
$this->assertEquals(123, $encoder->color_limit);
|
||||
$this->assertEquals(10, $encoder->quality);
|
||||
}
|
||||
}
|
||||
|
@ -46,15 +46,4 @@ class GifEncoderTest extends TestCase
|
||||
$result = $encoder->encode($image);
|
||||
$this->assertMimeType('image/gif', (string) $result);
|
||||
}
|
||||
|
||||
public function testEncodeReduced(): void
|
||||
{
|
||||
$image = $this->readTestImage('gradient.gif');
|
||||
$gd = $image->core()->native();
|
||||
$this->assertEquals(15, imagecolorstotal($gd));
|
||||
$encoder = new GifEncoder(2);
|
||||
$result = $encoder->encode($image);
|
||||
$gd = imagecreatefromstring((string) $result);
|
||||
$this->assertEquals(2, imagecolorstotal($gd));
|
||||
}
|
||||
}
|
||||
|
@ -35,15 +35,4 @@ class PngEncoderTest extends TestCase
|
||||
$result = $encoder->encode($image);
|
||||
$this->assertMimeType('image/png', (string) $result);
|
||||
}
|
||||
|
||||
public function testEncodeReduced(): void
|
||||
{
|
||||
$image = $this->readTestImage('tile.png');
|
||||
$gd = $image->core()->native();
|
||||
$this->assertEquals(3, imagecolorstotal($gd));
|
||||
$encoder = new PngEncoder(2);
|
||||
$result = $encoder->encode($image);
|
||||
$gd = imagecreatefromstring((string) $result);
|
||||
$this->assertEquals(2, imagecolorstotal($gd));
|
||||
}
|
||||
}
|
||||
|
@ -37,16 +37,4 @@ class BmpEncoderTest extends TestCase
|
||||
$result = $encoder->encode($image);
|
||||
$this->assertMimeType(['image/bmp', 'image/x-ms-bmp'], (string) $result);
|
||||
}
|
||||
|
||||
public function testEncodeReduced(): void
|
||||
{
|
||||
$image = $this->readTestImage('gradient.bmp');
|
||||
$imagick = $image->core()->native();
|
||||
$this->assertEquals(15, $imagick->getImageColors());
|
||||
$encoder = new BmpEncoder(2);
|
||||
$result = $encoder->encode($image);
|
||||
$imagick = new Imagick();
|
||||
$imagick->readImageBlob((string) $result);
|
||||
$this->assertEquals(2, $imagick->getImageColors());
|
||||
}
|
||||
}
|
||||
|
@ -51,16 +51,4 @@ class GifEncoderTest extends TestCase
|
||||
$result = $encoder->encode($image);
|
||||
$this->assertMimeType('image/gif', (string) $result);
|
||||
}
|
||||
|
||||
public function testEncodeReduced(): void
|
||||
{
|
||||
$image = $this->readTestImage('gradient.gif');
|
||||
$imagick = $image->core()->native();
|
||||
$this->assertEquals(15, $imagick->getImageColors());
|
||||
$encoder = new GifEncoder(2);
|
||||
$result = $encoder->encode($image);
|
||||
$imagick = new Imagick();
|
||||
$imagick->readImageBlob((string) $result);
|
||||
$this->assertEquals(2, $imagick->getImageColors());
|
||||
}
|
||||
}
|
||||
|
@ -38,16 +38,4 @@ final class PngEncoderTest extends TestCase
|
||||
$result = $encoder->encode($image);
|
||||
$this->assertMimeType('image/png', (string) $result);
|
||||
}
|
||||
|
||||
public function testEncodeReduced(): void
|
||||
{
|
||||
$image = $this->readTestImage('tile.png');
|
||||
$imagick = $image->core()->native();
|
||||
$this->assertEquals(3, $imagick->getImageColors());
|
||||
$encoder = new PngEncoder(2);
|
||||
$result = $encoder->encode($image);
|
||||
$imagick = new Imagick();
|
||||
$imagick->readImageBlob((string) $result);
|
||||
$this->assertEquals(2, $imagick->getImageColors());
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user