diff --git a/src/Drivers/Abstract/AbstractImage.php b/src/Drivers/Abstract/AbstractImage.php index c1bfb75a..98c65038 100644 --- a/src/Drivers/Abstract/AbstractImage.php +++ b/src/Drivers/Abstract/AbstractImage.php @@ -385,6 +385,13 @@ abstract class AbstractImage implements ImageInterface return is_null($query) ? $this->exif : $this->exif->get($query); } + public function setResolution(float $x, float $y): ImageInterface + { + return $this->modify( + $this->resolveDriverClass('Modifiers\ResolutionModifier', $x, $y) + ); + } + public function destroy(): void { $this->modify( diff --git a/src/Drivers/Gd/Image.php b/src/Drivers/Gd/Image.php index c2bca68b..2607dc89 100644 --- a/src/Drivers/Gd/Image.php +++ b/src/Drivers/Gd/Image.php @@ -13,6 +13,8 @@ use Intervention\Image\Interfaces\ColorspaceInterface; use Intervention\Image\Interfaces\FrameInterface; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ProfileInterface; +use Intervention\Image\Interfaces\ResolutionInterface; +use Intervention\Image\Resolution; use IteratorAggregate; use Traversable; @@ -78,6 +80,16 @@ class Image extends AbstractImage implements ImageInterface, IteratorAggregate return imagesy($this->frame()->core()); } + /** + * {@inheritdoc} + * + * @see ImageInterface::resolution() + */ + public function resolution(): ResolutionInterface + { + return new Resolution(...imageresolution($this->frame()->core())); + } + public function pickColor(int $x, int $y, int $frame_key = 0): ColorInterface { return $this->integerToColor( diff --git a/src/Drivers/Gd/Modifiers/ResolutionModifier.php b/src/Drivers/Gd/Modifiers/ResolutionModifier.php new file mode 100644 index 00000000..c33cf2d5 --- /dev/null +++ b/src/Drivers/Gd/Modifiers/ResolutionModifier.php @@ -0,0 +1,23 @@ +core(), $this->x, $this->y); + } + + return $image; + } +} diff --git a/src/Drivers/Gd/Modifiers/RotateModifier.php b/src/Drivers/Gd/Modifiers/RotateModifier.php index 4033b399..528089db 100644 --- a/src/Drivers/Gd/Modifiers/RotateModifier.php +++ b/src/Drivers/Gd/Modifiers/RotateModifier.php @@ -14,6 +14,10 @@ class RotateModifier extends AbstractRotateModifier implements ModifierInterface public function apply(ImageInterface $image): ImageInterface { foreach ($image as $frame) { + // retain resolution because imagerotate() seems to reset density to 96dpi + $resolution = imageresolution($frame->core()); + + // rotate image $frame->setCore( imagerotate( $frame->core(), @@ -21,6 +25,9 @@ class RotateModifier extends AbstractRotateModifier implements ModifierInterface $this->colorToInteger($this->backgroundColor()) ) ); + + // restore original image resolution + imageresolution($frame->core(), $resolution[0], $resolution[1]); } return $image; diff --git a/src/Drivers/Imagick/Image.php b/src/Drivers/Imagick/Image.php index 28c0b2dc..c36d25ee 100644 --- a/src/Drivers/Imagick/Image.php +++ b/src/Drivers/Imagick/Image.php @@ -19,6 +19,8 @@ use Intervention\Image\Interfaces\ColorspaceInterface; use Intervention\Image\Interfaces\FrameInterface; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ProfileInterface; +use Intervention\Image\Interfaces\ResolutionInterface; +use Intervention\Image\Resolution; use Iterator; class Image extends AbstractImage implements ImageInterface, Iterator @@ -134,6 +136,16 @@ class Image extends AbstractImage implements ImageInterface, Iterator return $this->frame()->core()->getImageHeight(); } + /** + * {@inheritdoc} + * + * @see ImageInterface::resolution() + */ + public function resolution(): ResolutionInterface + { + return new Resolution(...$this->frame()->core()->getImageResolution()); + } + /** * {@inheritdoc} * diff --git a/src/Drivers/Imagick/Modifiers/ResolutionModifier.php b/src/Drivers/Imagick/Modifiers/ResolutionModifier.php new file mode 100644 index 00000000..e4166b94 --- /dev/null +++ b/src/Drivers/Imagick/Modifiers/ResolutionModifier.php @@ -0,0 +1,26 @@ +failIfNotClass($image, Image::class)->getImagick(); + $imagick->setImageResolution($this->x, $this->y); + + return $image; + } +} diff --git a/src/Interfaces/ImageInterface.php b/src/Interfaces/ImageInterface.php index 9b3e38c2..a5bb64e1 100644 --- a/src/Interfaces/ImageInterface.php +++ b/src/Interfaces/ImageInterface.php @@ -386,6 +386,22 @@ interface ImageInterface extends Traversable, Countable */ public function height(): int; + /** + * Return image resolution/density + * + * @return ResolutionInterface + */ + public function resolution(): ResolutionInterface; + + /** + * Se the image resolution/density + * + * @param float $x + * @param float $y + * @return ImageInterface + */ + public function setResolution(float $x, float $y): ImageInterface; + /** * Destroy current image instance and free up memory * diff --git a/src/Interfaces/ResolutionInterface.php b/src/Interfaces/ResolutionInterface.php new file mode 100644 index 00000000..35f1cc7d --- /dev/null +++ b/src/Interfaces/ResolutionInterface.php @@ -0,0 +1,9 @@ +x; + } + + public function y(): float + { + return $this->y; + } +} diff --git a/tests/Drivers/Abstract/AbstractImageTest.php b/tests/Drivers/Abstract/AbstractImageTest.php index bbf08adb..211404db 100644 --- a/tests/Drivers/Abstract/AbstractImageTest.php +++ b/tests/Drivers/Abstract/AbstractImageTest.php @@ -11,6 +11,7 @@ use Intervention\Image\Interfaces\EncoderInterface; use Intervention\Image\Interfaces\FrameInterface; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; +use Intervention\Image\Resolution; use Intervention\Image\Tests\TestCase; use Mockery; diff --git a/tests/Drivers/Gd/ImageTest.php b/tests/Drivers/Gd/ImageTest.php index 4d0b0840..1fe99d2a 100644 --- a/tests/Drivers/Gd/ImageTest.php +++ b/tests/Drivers/Gd/ImageTest.php @@ -12,6 +12,7 @@ use Intervention\Image\Colors\Rgb\Colorspace as RgbColorspace; use Intervention\Image\Colors\Cmyk\Colorspace as CmykColorspace; use Intervention\Image\Exceptions\NotSupportedException; use Intervention\Image\Exceptions\AnimationException; +use Intervention\Image\Resolution; /** * @requires extension gd @@ -152,4 +153,10 @@ class ImageTest extends TestCase $this->expectException(NotSupportedException::class); $this->image->setColorspace(new CmykColorspace()); } + + public function testResolution(): void + { + $result = $this->image->resolution(); + $this->assertInstanceOf(Resolution::class, $result); + } } diff --git a/tests/Drivers/Gd/Modifiers/ResolutionModifierTest.php b/tests/Drivers/Gd/Modifiers/ResolutionModifierTest.php new file mode 100644 index 00000000..fe2ccdaf --- /dev/null +++ b/tests/Drivers/Gd/Modifiers/ResolutionModifierTest.php @@ -0,0 +1,26 @@ +createTestImage('test.jpg'); + $this->assertEquals(72.0, $image->resolution()->x()); + $this->assertEquals(72.0, $image->resolution()->y()); + $image->modify(new ResolutionModifier(1, 2)); + $this->assertEquals(1.0, $image->resolution()->x()); + $this->assertEquals(2.0, $image->resolution()->y()); + } +} diff --git a/tests/Drivers/Imagick/ImageTest.php b/tests/Drivers/Imagick/ImageTest.php index e17c4b9c..4f0a5b09 100644 --- a/tests/Drivers/Imagick/ImageTest.php +++ b/tests/Drivers/Imagick/ImageTest.php @@ -12,6 +12,7 @@ use Intervention\Image\Drivers\Imagick\Image; use Intervention\Image\Exceptions\AnimationException; use Intervention\Image\Exceptions\ColorException; use Intervention\Image\Geometry\Rectangle; +use Intervention\Image\Resolution; use Intervention\Image\Tests\TestCase; /** @@ -155,4 +156,10 @@ class ImageTest extends TestCase $this->expectException(ColorException::class); $image->profile(); } + + public function testResolution(): void + { + $result = $this->image->resolution(); + $this->assertInstanceOf(Resolution::class, $result); + } } diff --git a/tests/Drivers/Imagick/Modifiers/ResolutionModifierTest.php b/tests/Drivers/Imagick/Modifiers/ResolutionModifierTest.php new file mode 100644 index 00000000..bf33cecc --- /dev/null +++ b/tests/Drivers/Imagick/Modifiers/ResolutionModifierTest.php @@ -0,0 +1,26 @@ +createTestImage('test.jpg'); + $this->assertEquals(72.0, $image->resolution()->x()); + $this->assertEquals(72.0, $image->resolution()->y()); + $image->modify(new ResolutionModifier(1, 2)); + $this->assertEquals(1.0, $image->resolution()->x()); + $this->assertEquals(2.0, $image->resolution()->y()); + } +}