From 95e72b877b38f96e52996a0ab367ebc64a249395 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Thu, 21 Oct 2021 14:32:05 +0200 Subject: [PATCH 001/476] Version 3 prototype --- .travis.yml | 36 - LICENSE | 9 - README.md | 55 - composer.json | 37 +- phpunit.xml | 1 - provides.json | 11 - src/Collection.php | 137 ++ src/Drivers/Abstract/AbstractColor.php | 17 + src/Drivers/Abstract/AbstractFrame.php | 8 + src/Drivers/Abstract/AbstractImage.php | 81 + src/Drivers/Abstract/AbstractInputHandler.php | 17 + .../Abstract/Decoders/AbstractDecoder.php | 47 + .../Abstract/Encoders/AbstractEncoder.php | 41 + src/Drivers/Gd/Color.php | 57 + src/Drivers/Gd/Decoders/ArrayColorDecoder.php | 33 + .../Gd/Decoders/Base64ImageDecoder.php | 22 + .../Gd/Decoders/BinaryImageDecoder.php | 55 + .../Gd/Decoders/DataUriImageDecoder.php | 33 + .../Gd/Decoders/FilePathImageDecoder.php | 28 + src/Drivers/Gd/Encoders/GifEncoder.php | 33 + src/Drivers/Gd/Encoders/JpegEncoder.php | 17 + src/Drivers/Gd/Frame.php | 111 ++ src/Drivers/Gd/Image.php | 36 + src/Drivers/Gd/ImageFactory.php | 19 + src/Drivers/Gd/InputHandler.php | 22 + .../Gd/Modifiers/GreyscaleModifier.php | 18 + src/Drivers/Imagick/Color.php | 53 + .../Imagick/Decoders/ArrayColorDecoder.php | 31 + .../Imagick/Decoders/Base64ImageDecoder.php | 22 + .../Imagick/Decoders/BinaryImageDecoder.php | 34 + .../Imagick/Decoders/DataUriImageDecoder.php | 33 + .../Imagick/Decoders/FilePathImageDecoder.php | 28 + src/Drivers/Imagick/Encoders/GifEncoder.php | 29 + src/Drivers/Imagick/Encoders/JpegEncoder.php | 29 + src/Drivers/Imagick/Frame.php | 83 + src/Drivers/Imagick/Image.php | 35 + src/Drivers/Imagick/InputHandler.php | 22 + .../Imagick/Modifiers/GreyscaleModifier.php | 18 + src/Exceptions/DecoderException.php | 8 + src/Exceptions/NotWritableException.php | 8 + src/Exceptions/RuntimeException.php | 8 + src/Geometry/Point.php | 135 ++ src/Geometry/Size.php | 92 + src/ImageManager.php | 96 + src/Interfaces/CollectionInterface.php | 12 + src/Interfaces/ColorInterface.php | 13 + src/Interfaces/DecoderInterface.php | 8 + src/Interfaces/EncoderInterface.php | 8 + src/Interfaces/FactoryInterface.php | 8 + src/Interfaces/FrameInterface.php | 18 + src/Interfaces/ImageInterface.php | 12 + src/Interfaces/ModifierInterface.php | 8 + src/Interfaces/PointInterface.php | 9 + src/Interfaces/SizeInterface.php | 16 + src/Intervention/Image/AbstractColor.php | 229 --- src/Intervention/Image/AbstractDecoder.php | 364 ---- src/Intervention/Image/AbstractDriver.php | 140 -- src/Intervention/Image/AbstractEncoder.php | 271 --- src/Intervention/Image/AbstractFont.php | 295 --- src/Intervention/Image/AbstractShape.php | 71 - .../Image/Commands/AbstractCommand.php | 81 - src/Intervention/Image/Commands/Argument.php | 225 --- .../Image/Commands/ChecksumCommand.php | 29 - .../Image/Commands/CircleCommand.php | 35 - .../Image/Commands/EllipseCommand.php | 36 - .../Image/Commands/ExifCommand.php | 61 - .../Image/Commands/IptcCommand.php | 68 - .../Image/Commands/LineCommand.php | 36 - .../Image/Commands/OrientateCommand.php | 48 - .../Image/Commands/PolygonCommand.php | 49 - .../Image/Commands/PsrResponseCommand.php | 45 - .../Image/Commands/RectangleCommand.php | 36 - .../Image/Commands/ResponseCommand.php | 26 - .../Image/Commands/StreamCommand.php | 39 - .../Image/Commands/TextCommand.php | 34 - src/Intervention/Image/Constraint.php | 92 - .../Image/Exception/ImageException.php | 8 - .../Exception/InvalidArgumentException.php | 8 - .../Exception/MissingDependencyException.php | 8 - .../Image/Exception/NotFoundException.php | 8 - .../Image/Exception/NotReadableException.php | 8 - .../Image/Exception/NotSupportedException.php | 8 - .../Image/Exception/NotWritableException.php | 8 - .../Image/Exception/RuntimeException.php | 8 - src/Intervention/Image/Facades/Image.php | 19 - src/Intervention/Image/File.php | 92 - src/Intervention/Image/Filters/DemoFilter.php | 44 - .../Image/Filters/FilterInterface.php | 16 - src/Intervention/Image/Gd/Color.php | 227 --- .../Image/Gd/Commands/BackupCommand.php | 25 - .../Image/Gd/Commands/BlurCommand.php | 25 - .../Image/Gd/Commands/BrightnessCommand.php | 21 - .../Image/Gd/Commands/ColorizeCommand.php | 29 - .../Image/Gd/Commands/ContrastCommand.php | 21 - .../Image/Gd/Commands/CropCommand.php | 40 - .../Image/Gd/Commands/DestroyCommand.php | 27 - .../Image/Gd/Commands/FillCommand.php | 69 - .../Image/Gd/Commands/FitCommand.php | 32 - .../Image/Gd/Commands/FlipCommand.php | 37 - .../Image/Gd/Commands/GammaCommand.php | 21 - .../Image/Gd/Commands/GetSizeCommand.php | 25 - .../Image/Gd/Commands/GreyscaleCommand.php | 19 - .../Image/Gd/Commands/HeightenCommand.php | 28 - .../Image/Gd/Commands/InsertCommand.php | 34 - .../Image/Gd/Commands/InterlaceCommand.php | 23 - .../Image/Gd/Commands/InvertCommand.php | 19 - .../Image/Gd/Commands/LimitColorsCommand.php | 53 - .../Image/Gd/Commands/MaskCommand.php | 83 - .../Image/Gd/Commands/OpacityCommand.php | 31 - .../Image/Gd/Commands/PickColorCommand.php | 37 - .../Image/Gd/Commands/PixelCommand.php | 25 - .../Image/Gd/Commands/PixelateCommand.php | 21 - .../Image/Gd/Commands/ResetCommand.php | 39 - .../Image/Gd/Commands/ResizeCanvasCommand.php | 83 - .../Image/Gd/Commands/ResizeCommand.php | 84 - .../Image/Gd/Commands/RotateCommand.php | 30 - .../Image/Gd/Commands/SharpenCommand.php | 34 - .../Image/Gd/Commands/TrimCommand.php | 176 -- .../Image/Gd/Commands/WidenCommand.php | 28 - src/Intervention/Image/Gd/Decoder.php | 171 -- src/Intervention/Image/Gd/Driver.php | 89 - src/Intervention/Image/Gd/Encoder.php | 180 -- src/Intervention/Image/Gd/Font.php | 277 --- .../Image/Gd/Shapes/CircleShape.php | 40 - .../Image/Gd/Shapes/EllipseShape.php | 65 - .../Image/Gd/Shapes/LineShape.php | 90 - .../Image/Gd/Shapes/PolygonShape.php | 49 - .../Image/Gd/Shapes/RectangleShape.php | 76 - src/Intervention/Image/Image.php | 370 ---- src/Intervention/Image/ImageManager.php | 142 -- src/Intervention/Image/ImageManagerStatic.php | 88 - .../Image/ImageServiceProvider.php | 87 - .../Image/ImageServiceProviderLaravel4.php | 112 -- .../ImageServiceProviderLaravelRecent.php | 106 - .../Image/ImageServiceProviderLeague.php | 42 - .../Image/ImageServiceProviderLumen.php | 34 - src/Intervention/Image/Imagick/Color.php | 279 --- .../Image/Imagick/Commands/BackupCommand.php | 25 - .../Image/Imagick/Commands/BlurCommand.php | 21 - .../Imagick/Commands/BrightnessCommand.php | 21 - .../Imagick/Commands/ColorizeCommand.php | 44 - .../Imagick/Commands/ContrastCommand.php | 21 - .../Image/Imagick/Commands/CropCommand.php | 45 - .../Image/Imagick/Commands/DestroyCommand.php | 27 - .../Image/Imagick/Commands/ExifCommand.php | 63 - .../Image/Imagick/Commands/FillCommand.php | 105 - .../Image/Imagick/Commands/FitCommand.php | 42 - .../Image/Imagick/Commands/FlipCommand.php | 27 - .../Image/Imagick/Commands/GammaCommand.php | 21 - .../Image/Imagick/Commands/GetSizeCommand.php | 28 - .../Imagick/Commands/GreyscaleCommand.php | 19 - .../Imagick/Commands/HeightenCommand.php | 28 - .../Image/Imagick/Commands/InsertCommand.php | 33 - .../Imagick/Commands/InterlaceCommand.php | 29 - .../Image/Imagick/Commands/InvertCommand.php | 19 - .../Imagick/Commands/LimitColorsCommand.php | 59 - .../Image/Imagick/Commands/MaskCommand.php | 60 - .../Image/Imagick/Commands/OpacityCommand.php | 23 - .../Imagick/Commands/PickColorCommand.php | 30 - .../Image/Imagick/Commands/PixelCommand.php | 31 - .../Imagick/Commands/PixelateCommand.php | 27 - .../Image/Imagick/Commands/ResetCommand.php | 40 - .../Imagick/Commands/ResizeCanvasCommand.php | 91 - .../Image/Imagick/Commands/ResizeCommand.php | 29 - .../Image/Imagick/Commands/RotateCommand.php | 30 - .../Image/Imagick/Commands/SharpenCommand.php | 21 - .../Image/Imagick/Commands/TrimCommand.php | 121 -- .../Image/Imagick/Commands/WidenCommand.php | 28 - src/Intervention/Image/Imagick/Decoder.php | 124 -- src/Intervention/Image/Imagick/Driver.php | 74 - src/Intervention/Image/Imagick/Encoder.php | 239 --- src/Intervention/Image/Imagick/Font.php | 122 -- .../Image/Imagick/Shapes/CircleShape.php | 40 - .../Image/Imagick/Shapes/EllipseShape.php | 66 - .../Image/Imagick/Shapes/LineShape.php | 94 - .../Image/Imagick/Shapes/PolygonShape.php | 81 - .../Image/Imagick/Shapes/RectangleShape.php | 84 - src/Intervention/Image/Point.php | 64 - src/Intervention/Image/Response.php | 78 - src/Intervention/Image/Size.php | 374 ---- src/Traits/CanDecodeDataUri.php | 75 + src/Traits/CanResolveDriverClass.php | 48 + src/Traits/CanValidateBase64.php | 15 + src/Traits/CanValidateColorArray.php | 31 + src/config/config.php | 20 - tests/AbstractColorTest.php | 20 - tests/AbstractCommandTest.php | 48 - tests/AbstractDecoderTest.php | 160 -- tests/AbstractDriverTest.php | 21 - tests/AbstractFontTest.php | 86 - tests/AbstractShapeTest.php | 43 - tests/ArgumentTest.php | 422 ---- tests/BackupCommandTest.php | 65 - tests/BlurCommandTest.php | 34 - tests/BrightnessCommandTest.php | 34 - tests/ChecksumCommandTest.php | 27 - tests/CircleCommandTest.php | 43 - tests/CircleShapeTest.php | 52 - tests/CollectionTest.php | 102 + tests/ColorizeCommandTest.php | 37 - tests/ConstraintTest.php | 58 - tests/ContrastCommandTest.php | 34 - tests/CropCommandTest.php | 36 - tests/DestroyCommandTest.php | 46 - tests/DriverTest.php | 61 - tests/Drivers/Gd/ColorTest.php | 75 + .../Gd/Decoders/ArrayColorDecoderTest.php | 21 + .../Gd/Decoders/BinaryImageDecoderTest.php | 40 + tests/Drivers/Gd/Encoders/GifEncoderTest.php | 40 + tests/Drivers/Gd/Encoders/JpegEncoderTest.php | 29 + tests/Drivers/Gd/FrameTest.php | 79 + tests/Drivers/Gd/ImageFactoryTest.php | 17 + tests/Drivers/Gd/ImageTest.php | 46 + tests/Drivers/Gd/InputHandlerTest.php | 62 + tests/Drivers/Imagick/ColorTest.php | 87 + .../Decoders/ArrayColorDecoderTest.php | 21 + .../Imagick/Encoders/GifEncoderTest.php | 42 + .../Imagick/Encoders/JpegEncoderTest.php | 33 + tests/Drivers/Imagick/FrameTest.php | 87 + tests/Drivers/Imagick/ImageTest.php | 51 + tests/Drivers/Imagick/InputHandlerTest.php | 62 + tests/EllipseCommandTest.php | 43 - tests/EllipseShapeTest.php | 55 - tests/EncoderTest.php | 339 ---- tests/ExifCommandTest.php | 112 -- tests/FileTest.php | 28 - tests/FillCommandTest.php | 93 - tests/FitCommandTest.php | 93 - tests/FlipCommandTest.php | 45 - tests/GammaCommandTest.php | 34 - tests/GdColorTest.php | 296 --- tests/GdSystemTest.php | 1703 ----------------- tests/Geometry/PointTest.php | 82 + tests/Geometry/SizeTest.php | 101 + tests/GetsizeCommandTest.php | 39 - tests/GreyscaleCommandTest.php | 34 - tests/HeightenCommandTest.php | 49 - tests/ImageManagerStaticTest.php | 36 - tests/ImageManagerTest.php | 50 +- tests/ImageTest.php | 143 -- tests/ImagickColorTest.php | 338 ---- tests/ImagickSystemTest.php | 1650 ---------------- tests/InsertCommandTest.php | 67 - tests/InterlaceCommandTest.php | 34 - tests/InvertCommandTest.php | 34 - tests/IptcCommandTest.php | 72 - tests/LimitColorsCommandTest.php | 43 - tests/LineCommandTest.php | 43 - tests/LineShapeTest.php | 50 - tests/MaskCommandTest.php | 68 - tests/OpacityCommandTest.php | 44 - tests/OrientateCommandTest.php | 94 - tests/PickColorCommandTest.php | 67 - tests/PixelCommandTest.php | 34 - tests/PixelateCommandTest.php | 37 - tests/PointTest.php | 47 - tests/PolygonCommandTest.php | 45 - tests/PolygonShapeTest.php | 57 - tests/PsrResponseCommandTest.php | 58 - tests/RectangleCommandTest.php | 43 - tests/RectangleShapeTest.php | 54 - tests/ResetCommandTest.php | 71 - tests/ResizeCanvasCommandTest.php | 80 - tests/ResizeCommandTest.php | 49 - tests/ResponseTest.php | 30 - tests/RotateCommandTest.php | 48 - tests/SharpenCommandTest.php | 34 - tests/SizeTest.php | 436 ----- tests/StreamCommandTest.php | 36 - tests/TestCase.php | 17 + tests/TextCommandTest.php | 32 - tests/TrimCommandTest.php | 54 - tests/WidenCommandTest.php | 49 - tests/images/animation.gif | Bin 0 -> 592 bytes tests/images/black-friday.png | Bin 4964 -> 0 bytes tests/images/blue.gif | Bin 0 -> 47 bytes tests/images/broken.png | Bin 42 -> 0 bytes tests/images/cats.gif | Bin 0 -> 1721 bytes tests/images/circle.png | Bin 383 -> 0 bytes tests/images/exif.jpg | Bin 7791 -> 0 bytes tests/images/gradient.png | Bin 531 -> 0 bytes tests/images/green.gif | Bin 0 -> 47 bytes tests/images/iptc.jpg | Bin 10526 -> 0 bytes tests/images/red.gif | Bin 0 -> 47 bytes tests/images/star.png | Bin 463 -> 0 bytes tests/images/test.jpg | Bin 9183 -> 0 bytes tests/images/test.webp | Bin 82 -> 0 bytes tests/images/trim.png | Bin 258 -> 0 bytes tests/tmp/.gitkeep | 0 289 files changed, 3010 insertions(+), 17483 deletions(-) delete mode 100644 .travis.yml delete mode 100644 LICENSE delete mode 100755 README.md delete mode 100644 provides.json create mode 100644 src/Collection.php create mode 100644 src/Drivers/Abstract/AbstractColor.php create mode 100644 src/Drivers/Abstract/AbstractFrame.php create mode 100644 src/Drivers/Abstract/AbstractImage.php create mode 100644 src/Drivers/Abstract/AbstractInputHandler.php create mode 100644 src/Drivers/Abstract/Decoders/AbstractDecoder.php create mode 100644 src/Drivers/Abstract/Encoders/AbstractEncoder.php create mode 100644 src/Drivers/Gd/Color.php create mode 100644 src/Drivers/Gd/Decoders/ArrayColorDecoder.php create mode 100644 src/Drivers/Gd/Decoders/Base64ImageDecoder.php create mode 100644 src/Drivers/Gd/Decoders/BinaryImageDecoder.php create mode 100644 src/Drivers/Gd/Decoders/DataUriImageDecoder.php create mode 100644 src/Drivers/Gd/Decoders/FilePathImageDecoder.php create mode 100644 src/Drivers/Gd/Encoders/GifEncoder.php create mode 100644 src/Drivers/Gd/Encoders/JpegEncoder.php create mode 100644 src/Drivers/Gd/Frame.php create mode 100644 src/Drivers/Gd/Image.php create mode 100644 src/Drivers/Gd/ImageFactory.php create mode 100644 src/Drivers/Gd/InputHandler.php create mode 100644 src/Drivers/Gd/Modifiers/GreyscaleModifier.php create mode 100644 src/Drivers/Imagick/Color.php create mode 100644 src/Drivers/Imagick/Decoders/ArrayColorDecoder.php create mode 100644 src/Drivers/Imagick/Decoders/Base64ImageDecoder.php create mode 100644 src/Drivers/Imagick/Decoders/BinaryImageDecoder.php create mode 100644 src/Drivers/Imagick/Decoders/DataUriImageDecoder.php create mode 100644 src/Drivers/Imagick/Decoders/FilePathImageDecoder.php create mode 100644 src/Drivers/Imagick/Encoders/GifEncoder.php create mode 100644 src/Drivers/Imagick/Encoders/JpegEncoder.php create mode 100644 src/Drivers/Imagick/Frame.php create mode 100644 src/Drivers/Imagick/Image.php create mode 100644 src/Drivers/Imagick/InputHandler.php create mode 100644 src/Drivers/Imagick/Modifiers/GreyscaleModifier.php create mode 100644 src/Exceptions/DecoderException.php create mode 100644 src/Exceptions/NotWritableException.php create mode 100644 src/Exceptions/RuntimeException.php create mode 100644 src/Geometry/Point.php create mode 100644 src/Geometry/Size.php create mode 100644 src/ImageManager.php create mode 100644 src/Interfaces/CollectionInterface.php create mode 100644 src/Interfaces/ColorInterface.php create mode 100644 src/Interfaces/DecoderInterface.php create mode 100644 src/Interfaces/EncoderInterface.php create mode 100644 src/Interfaces/FactoryInterface.php create mode 100644 src/Interfaces/FrameInterface.php create mode 100644 src/Interfaces/ImageInterface.php create mode 100644 src/Interfaces/ModifierInterface.php create mode 100644 src/Interfaces/PointInterface.php create mode 100644 src/Interfaces/SizeInterface.php delete mode 100644 src/Intervention/Image/AbstractColor.php delete mode 100644 src/Intervention/Image/AbstractDecoder.php delete mode 100644 src/Intervention/Image/AbstractDriver.php delete mode 100644 src/Intervention/Image/AbstractEncoder.php delete mode 100644 src/Intervention/Image/AbstractFont.php delete mode 100644 src/Intervention/Image/AbstractShape.php delete mode 100644 src/Intervention/Image/Commands/AbstractCommand.php delete mode 100644 src/Intervention/Image/Commands/Argument.php delete mode 100644 src/Intervention/Image/Commands/ChecksumCommand.php delete mode 100644 src/Intervention/Image/Commands/CircleCommand.php delete mode 100644 src/Intervention/Image/Commands/EllipseCommand.php delete mode 100644 src/Intervention/Image/Commands/ExifCommand.php delete mode 100644 src/Intervention/Image/Commands/IptcCommand.php delete mode 100644 src/Intervention/Image/Commands/LineCommand.php delete mode 100644 src/Intervention/Image/Commands/OrientateCommand.php delete mode 100644 src/Intervention/Image/Commands/PolygonCommand.php delete mode 100644 src/Intervention/Image/Commands/PsrResponseCommand.php delete mode 100644 src/Intervention/Image/Commands/RectangleCommand.php delete mode 100644 src/Intervention/Image/Commands/ResponseCommand.php delete mode 100644 src/Intervention/Image/Commands/StreamCommand.php delete mode 100644 src/Intervention/Image/Commands/TextCommand.php delete mode 100644 src/Intervention/Image/Constraint.php delete mode 100644 src/Intervention/Image/Exception/ImageException.php delete mode 100644 src/Intervention/Image/Exception/InvalidArgumentException.php delete mode 100644 src/Intervention/Image/Exception/MissingDependencyException.php delete mode 100644 src/Intervention/Image/Exception/NotFoundException.php delete mode 100644 src/Intervention/Image/Exception/NotReadableException.php delete mode 100644 src/Intervention/Image/Exception/NotSupportedException.php delete mode 100644 src/Intervention/Image/Exception/NotWritableException.php delete mode 100644 src/Intervention/Image/Exception/RuntimeException.php delete mode 100644 src/Intervention/Image/Facades/Image.php delete mode 100644 src/Intervention/Image/File.php delete mode 100644 src/Intervention/Image/Filters/DemoFilter.php delete mode 100644 src/Intervention/Image/Filters/FilterInterface.php delete mode 100644 src/Intervention/Image/Gd/Color.php delete mode 100644 src/Intervention/Image/Gd/Commands/BackupCommand.php delete mode 100644 src/Intervention/Image/Gd/Commands/BlurCommand.php delete mode 100644 src/Intervention/Image/Gd/Commands/BrightnessCommand.php delete mode 100644 src/Intervention/Image/Gd/Commands/ColorizeCommand.php delete mode 100644 src/Intervention/Image/Gd/Commands/ContrastCommand.php delete mode 100644 src/Intervention/Image/Gd/Commands/CropCommand.php delete mode 100644 src/Intervention/Image/Gd/Commands/DestroyCommand.php delete mode 100644 src/Intervention/Image/Gd/Commands/FillCommand.php delete mode 100644 src/Intervention/Image/Gd/Commands/FitCommand.php delete mode 100644 src/Intervention/Image/Gd/Commands/FlipCommand.php delete mode 100644 src/Intervention/Image/Gd/Commands/GammaCommand.php delete mode 100644 src/Intervention/Image/Gd/Commands/GetSizeCommand.php delete mode 100644 src/Intervention/Image/Gd/Commands/GreyscaleCommand.php delete mode 100644 src/Intervention/Image/Gd/Commands/HeightenCommand.php delete mode 100644 src/Intervention/Image/Gd/Commands/InsertCommand.php delete mode 100644 src/Intervention/Image/Gd/Commands/InterlaceCommand.php delete mode 100644 src/Intervention/Image/Gd/Commands/InvertCommand.php delete mode 100644 src/Intervention/Image/Gd/Commands/LimitColorsCommand.php delete mode 100644 src/Intervention/Image/Gd/Commands/MaskCommand.php delete mode 100644 src/Intervention/Image/Gd/Commands/OpacityCommand.php delete mode 100644 src/Intervention/Image/Gd/Commands/PickColorCommand.php delete mode 100644 src/Intervention/Image/Gd/Commands/PixelCommand.php delete mode 100644 src/Intervention/Image/Gd/Commands/PixelateCommand.php delete mode 100644 src/Intervention/Image/Gd/Commands/ResetCommand.php delete mode 100644 src/Intervention/Image/Gd/Commands/ResizeCanvasCommand.php delete mode 100644 src/Intervention/Image/Gd/Commands/ResizeCommand.php delete mode 100644 src/Intervention/Image/Gd/Commands/RotateCommand.php delete mode 100644 src/Intervention/Image/Gd/Commands/SharpenCommand.php delete mode 100644 src/Intervention/Image/Gd/Commands/TrimCommand.php delete mode 100644 src/Intervention/Image/Gd/Commands/WidenCommand.php delete mode 100644 src/Intervention/Image/Gd/Decoder.php delete mode 100644 src/Intervention/Image/Gd/Driver.php delete mode 100644 src/Intervention/Image/Gd/Encoder.php delete mode 100644 src/Intervention/Image/Gd/Font.php delete mode 100644 src/Intervention/Image/Gd/Shapes/CircleShape.php delete mode 100644 src/Intervention/Image/Gd/Shapes/EllipseShape.php delete mode 100644 src/Intervention/Image/Gd/Shapes/LineShape.php delete mode 100644 src/Intervention/Image/Gd/Shapes/PolygonShape.php delete mode 100644 src/Intervention/Image/Gd/Shapes/RectangleShape.php delete mode 100644 src/Intervention/Image/Image.php delete mode 100644 src/Intervention/Image/ImageManager.php delete mode 100644 src/Intervention/Image/ImageManagerStatic.php delete mode 100644 src/Intervention/Image/ImageServiceProvider.php delete mode 100755 src/Intervention/Image/ImageServiceProviderLaravel4.php delete mode 100644 src/Intervention/Image/ImageServiceProviderLaravelRecent.php delete mode 100644 src/Intervention/Image/ImageServiceProviderLeague.php delete mode 100644 src/Intervention/Image/ImageServiceProviderLumen.php delete mode 100644 src/Intervention/Image/Imagick/Color.php delete mode 100644 src/Intervention/Image/Imagick/Commands/BackupCommand.php delete mode 100644 src/Intervention/Image/Imagick/Commands/BlurCommand.php delete mode 100644 src/Intervention/Image/Imagick/Commands/BrightnessCommand.php delete mode 100644 src/Intervention/Image/Imagick/Commands/ColorizeCommand.php delete mode 100644 src/Intervention/Image/Imagick/Commands/ContrastCommand.php delete mode 100644 src/Intervention/Image/Imagick/Commands/CropCommand.php delete mode 100644 src/Intervention/Image/Imagick/Commands/DestroyCommand.php delete mode 100644 src/Intervention/Image/Imagick/Commands/ExifCommand.php delete mode 100644 src/Intervention/Image/Imagick/Commands/FillCommand.php delete mode 100644 src/Intervention/Image/Imagick/Commands/FitCommand.php delete mode 100644 src/Intervention/Image/Imagick/Commands/FlipCommand.php delete mode 100644 src/Intervention/Image/Imagick/Commands/GammaCommand.php delete mode 100644 src/Intervention/Image/Imagick/Commands/GetSizeCommand.php delete mode 100644 src/Intervention/Image/Imagick/Commands/GreyscaleCommand.php delete mode 100644 src/Intervention/Image/Imagick/Commands/HeightenCommand.php delete mode 100644 src/Intervention/Image/Imagick/Commands/InsertCommand.php delete mode 100644 src/Intervention/Image/Imagick/Commands/InterlaceCommand.php delete mode 100644 src/Intervention/Image/Imagick/Commands/InvertCommand.php delete mode 100644 src/Intervention/Image/Imagick/Commands/LimitColorsCommand.php delete mode 100644 src/Intervention/Image/Imagick/Commands/MaskCommand.php delete mode 100644 src/Intervention/Image/Imagick/Commands/OpacityCommand.php delete mode 100644 src/Intervention/Image/Imagick/Commands/PickColorCommand.php delete mode 100644 src/Intervention/Image/Imagick/Commands/PixelCommand.php delete mode 100644 src/Intervention/Image/Imagick/Commands/PixelateCommand.php delete mode 100644 src/Intervention/Image/Imagick/Commands/ResetCommand.php delete mode 100644 src/Intervention/Image/Imagick/Commands/ResizeCanvasCommand.php delete mode 100644 src/Intervention/Image/Imagick/Commands/ResizeCommand.php delete mode 100644 src/Intervention/Image/Imagick/Commands/RotateCommand.php delete mode 100644 src/Intervention/Image/Imagick/Commands/SharpenCommand.php delete mode 100644 src/Intervention/Image/Imagick/Commands/TrimCommand.php delete mode 100644 src/Intervention/Image/Imagick/Commands/WidenCommand.php delete mode 100644 src/Intervention/Image/Imagick/Decoder.php delete mode 100644 src/Intervention/Image/Imagick/Driver.php delete mode 100644 src/Intervention/Image/Imagick/Encoder.php delete mode 100644 src/Intervention/Image/Imagick/Font.php delete mode 100644 src/Intervention/Image/Imagick/Shapes/CircleShape.php delete mode 100644 src/Intervention/Image/Imagick/Shapes/EllipseShape.php delete mode 100644 src/Intervention/Image/Imagick/Shapes/LineShape.php delete mode 100644 src/Intervention/Image/Imagick/Shapes/PolygonShape.php delete mode 100644 src/Intervention/Image/Imagick/Shapes/RectangleShape.php delete mode 100644 src/Intervention/Image/Point.php delete mode 100644 src/Intervention/Image/Response.php delete mode 100644 src/Intervention/Image/Size.php create mode 100644 src/Traits/CanDecodeDataUri.php create mode 100644 src/Traits/CanResolveDriverClass.php create mode 100644 src/Traits/CanValidateBase64.php create mode 100644 src/Traits/CanValidateColorArray.php delete mode 100644 src/config/config.php delete mode 100644 tests/AbstractColorTest.php delete mode 100644 tests/AbstractCommandTest.php delete mode 100644 tests/AbstractDecoderTest.php delete mode 100644 tests/AbstractDriverTest.php delete mode 100644 tests/AbstractFontTest.php delete mode 100644 tests/AbstractShapeTest.php delete mode 100644 tests/ArgumentTest.php delete mode 100644 tests/BackupCommandTest.php delete mode 100644 tests/BlurCommandTest.php delete mode 100644 tests/BrightnessCommandTest.php delete mode 100644 tests/ChecksumCommandTest.php delete mode 100644 tests/CircleCommandTest.php delete mode 100644 tests/CircleShapeTest.php create mode 100644 tests/CollectionTest.php delete mode 100644 tests/ColorizeCommandTest.php delete mode 100644 tests/ConstraintTest.php delete mode 100644 tests/ContrastCommandTest.php delete mode 100644 tests/CropCommandTest.php delete mode 100644 tests/DestroyCommandTest.php delete mode 100644 tests/DriverTest.php create mode 100644 tests/Drivers/Gd/ColorTest.php create mode 100644 tests/Drivers/Gd/Decoders/ArrayColorDecoderTest.php create mode 100644 tests/Drivers/Gd/Decoders/BinaryImageDecoderTest.php create mode 100644 tests/Drivers/Gd/Encoders/GifEncoderTest.php create mode 100644 tests/Drivers/Gd/Encoders/JpegEncoderTest.php create mode 100644 tests/Drivers/Gd/FrameTest.php create mode 100644 tests/Drivers/Gd/ImageFactoryTest.php create mode 100644 tests/Drivers/Gd/ImageTest.php create mode 100644 tests/Drivers/Gd/InputHandlerTest.php create mode 100644 tests/Drivers/Imagick/ColorTest.php create mode 100644 tests/Drivers/Imagick/Decoders/ArrayColorDecoderTest.php create mode 100644 tests/Drivers/Imagick/Encoders/GifEncoderTest.php create mode 100644 tests/Drivers/Imagick/Encoders/JpegEncoderTest.php create mode 100644 tests/Drivers/Imagick/FrameTest.php create mode 100644 tests/Drivers/Imagick/ImageTest.php create mode 100644 tests/Drivers/Imagick/InputHandlerTest.php delete mode 100644 tests/EllipseCommandTest.php delete mode 100644 tests/EllipseShapeTest.php delete mode 100644 tests/EncoderTest.php delete mode 100644 tests/ExifCommandTest.php delete mode 100644 tests/FileTest.php delete mode 100644 tests/FillCommandTest.php delete mode 100644 tests/FitCommandTest.php delete mode 100644 tests/FlipCommandTest.php delete mode 100644 tests/GammaCommandTest.php delete mode 100644 tests/GdColorTest.php delete mode 100644 tests/GdSystemTest.php create mode 100644 tests/Geometry/PointTest.php create mode 100644 tests/Geometry/SizeTest.php delete mode 100644 tests/GetsizeCommandTest.php delete mode 100644 tests/GreyscaleCommandTest.php delete mode 100644 tests/HeightenCommandTest.php delete mode 100644 tests/ImageManagerStaticTest.php delete mode 100644 tests/ImageTest.php delete mode 100644 tests/ImagickColorTest.php delete mode 100644 tests/ImagickSystemTest.php delete mode 100644 tests/InsertCommandTest.php delete mode 100644 tests/InterlaceCommandTest.php delete mode 100644 tests/InvertCommandTest.php delete mode 100644 tests/IptcCommandTest.php delete mode 100644 tests/LimitColorsCommandTest.php delete mode 100644 tests/LineCommandTest.php delete mode 100644 tests/LineShapeTest.php delete mode 100644 tests/MaskCommandTest.php delete mode 100644 tests/OpacityCommandTest.php delete mode 100644 tests/OrientateCommandTest.php delete mode 100644 tests/PickColorCommandTest.php delete mode 100644 tests/PixelCommandTest.php delete mode 100644 tests/PixelateCommandTest.php delete mode 100644 tests/PointTest.php delete mode 100644 tests/PolygonCommandTest.php delete mode 100644 tests/PolygonShapeTest.php delete mode 100644 tests/PsrResponseCommandTest.php delete mode 100644 tests/RectangleCommandTest.php delete mode 100644 tests/RectangleShapeTest.php delete mode 100644 tests/ResetCommandTest.php delete mode 100644 tests/ResizeCanvasCommandTest.php delete mode 100644 tests/ResizeCommandTest.php delete mode 100644 tests/ResponseTest.php delete mode 100644 tests/RotateCommandTest.php delete mode 100644 tests/SharpenCommandTest.php delete mode 100644 tests/SizeTest.php delete mode 100644 tests/StreamCommandTest.php create mode 100644 tests/TestCase.php delete mode 100644 tests/TextCommandTest.php delete mode 100644 tests/TrimCommandTest.php delete mode 100644 tests/WidenCommandTest.php create mode 100644 tests/images/animation.gif delete mode 100644 tests/images/black-friday.png create mode 100644 tests/images/blue.gif delete mode 100644 tests/images/broken.png create mode 100644 tests/images/cats.gif delete mode 100644 tests/images/circle.png delete mode 100644 tests/images/exif.jpg delete mode 100644 tests/images/gradient.png create mode 100644 tests/images/green.gif delete mode 100644 tests/images/iptc.jpg create mode 100644 tests/images/red.gif delete mode 100644 tests/images/star.png delete mode 100644 tests/images/test.jpg delete mode 100755 tests/images/test.webp delete mode 100644 tests/images/trim.png delete mode 100644 tests/tmp/.gitkeep diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 86ddce95..00000000 --- a/.travis.yml +++ /dev/null @@ -1,36 +0,0 @@ -language: php - -sudo: false - -jobs: - include: - - dist: trusty - php: 5.4 - - dist: trusty - php: 5.5 - - dist: xenial - php: 5.6 - - dist: xenial - php: 7.0 - - dist: xenial - php: 7.1 - - dist: xenial - php: 7.2 - - dist: xenial - php: 7.3 - - dist: xenial - php: 7.4 - - dist: xenial - php: nightly - - dist: xenial - php: hhvm - allow_failures: - - php: nightly - - php: hhvm - -before_script: - - printf "\n" | pecl install imagick - - composer self-update || true - - composer install --prefer-dist --no-interaction --no-progress --no-suggest --optimize-autoloader - -script: vendor/bin/phpunit diff --git a/LICENSE b/LICENSE deleted file mode 100644 index bc444ba2..00000000 --- a/LICENSE +++ /dev/null @@ -1,9 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2014 Oliver Vogel - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md deleted file mode 100755 index 350f471c..00000000 --- a/README.md +++ /dev/null @@ -1,55 +0,0 @@ -# Intervention Image - -Intervention Image is a **PHP image handling and manipulation** library providing an easier and expressive way to create, edit, and compose images. The package includes ServiceProviders and Facades for easy **Laravel** integration. - -[![Latest Version](https://img.shields.io/packagist/v/intervention/image.svg)](https://packagist.org/packages/intervention/image) -[![Build Status](https://travis-ci.org/Intervention/image.png?branch=master)](https://travis-ci.org/Intervention/image) -[![Monthly Downloads](https://img.shields.io/packagist/dm/intervention/image.svg)](https://packagist.org/packages/intervention/image/stats) - -## Requirements - -- PHP >=5.4 -- Fileinfo Extension - -## Supported Image Libraries - -- GD Library (>=2.0) -- Imagick PHP extension (>=6.5.7) - -## Getting started - -- [Installation](http://image.intervention.io/getting_started/installation) -- [Laravel Framework Integration](http://image.intervention.io/getting_started/installation#laravel) -- [Basic Usage](http://image.intervention.io/use/basics) - -## Code Examples - -```php -// open an image file -$img = Image::make('public/foo.jpg'); - -// resize image instance -$img->resize(320, 240); - -// insert a watermark -$img->insert('public/watermark.png'); - -// save image in desired format -$img->save('public/bar.jpg'); -``` - -Refer to the [official documentation](http://image.intervention.io/) to learn more about Intervention Image. - -## Contributing - -Contributions to the Intervention Image library are welcome. Please note the following guidelines before submitting your pull request. - -- Follow [PSR-2](http://www.php-fig.org/psr/psr-2/) coding standards. -- Write tests for new functions and added features -- API calls should work consistently with both GD and Imagick drivers - -## License - -Intervention Image is licensed under the [MIT License](http://opensource.org/licenses/MIT). - -Copyright 2017 [Oliver Vogel](http://olivervogel.com/) diff --git a/composer.json b/composer.json index e750b406..93776f08 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,6 @@ { "name": "intervention/image", - "description": "Image handling and manipulation library with support for Laravel integration", + "description": "PHP image manipulation", "homepage": "http://image.intervention.io/", "keywords": ["image", "gd", "imagick", "laravel", "watermark", "thumbnail"], "license": "MIT", @@ -8,40 +8,25 @@ { "name": "Oliver Vogel", "email": "oliver@olivervogel.com", - "homepage": "http://olivervogel.com/" + "homepage": "http://intervention.io/" } ], "require": { - "php": ">=5.4.0", - "ext-fileinfo": "*", - "guzzlehttp/psr7": "~1.1 || ^2.0" + "php": "^8", + "intervention/gif": "dev-master", + "intervention/mimesniffer": "^0.4.2" }, "require-dev": { - "phpunit/phpunit": "^4.8 || ^5.7 || ^7.5.15", - "mockery/mockery": "~0.9.2" - }, - "suggest": { - "ext-gd": "to use GD library based image processing.", - "ext-imagick": "to use Imagick based image processing.", - "intervention/imagecache": "Caching extension for the Intervention Image library" + "phpunit/phpunit": "^9" }, "autoload": { "psr-4": { - "Intervention\\Image\\": "src/Intervention/Image" + "Intervention\\Image\\": "src" } }, - "extra": { - "branch-alias": { - "dev-master": "2.4-dev" - }, - "laravel": { - "providers": [ - "Intervention\\Image\\ImageServiceProvider" - ], - "aliases": { - "Image": "Intervention\\Image\\Facades\\Image" - } + "autoload-dev": { + "psr-4": { + "Intervention\\Image\\Tests\\": "tests" } - }, - "minimum-stability": "stable" + } } diff --git a/phpunit.xml b/phpunit.xml index 3347b75b..422eeac6 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -8,7 +8,6 @@ convertWarningsToExceptions="true" processIsolation="false" stopOnFailure="false" - syntaxCheck="false" > diff --git a/provides.json b/provides.json deleted file mode 100644 index a8cd1b6a..00000000 --- a/provides.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "providers": [ - "Intervention\\Image\\ImageServiceProvider" - ], - "aliases": [ - { - "alias": "Image", - "facade": "Intervention\\Image\\Facades\\Image" - } - ] -} diff --git a/src/Collection.php b/src/Collection.php new file mode 100644 index 00000000..2c40af19 --- /dev/null +++ b/src/Collection.php @@ -0,0 +1,137 @@ +items); + } + + public function toArray(): array + { + return $this->items; + } + + /** + * Count items in collection + * + * @return integer + */ + public function count(): int + { + return count($this->items); + } + + /** + * Append new item to collection + * + * @param mixed $item + * @return CollectionInterface + */ + public function push($item): CollectionInterface + { + $this->items[] = $item; + + return $this; + } + + /** + * Return first item in collection + * + * @return mixed + */ + public function first() + { + if ($item = reset($this->items)) { + return $item; + } + + return null; + } + + /** + * Returns last item in collection + * + * @return mixed + */ + public function last() + { + if ($item = end($this->items)) { + return $item; + } + + return null; + } + + /** + * Return item with given key + * + * @param integer $key + * @return mixed + */ + public function get(int $key = 0) + { + if (! array_key_exists($key, $this->items)) { + return null; + } + + return $this->items[$key]; + } + + public function map(callable $callback): self + { + $items = array_map(function ($item) use ($callback) { + return $callback($item); + }, $this->items); + + return new self($items); + } + + public function pushEach(array $data, ?callable $callback = null): CollectionInterface + { + if (! is_iterable($data)) { + throw new RuntimeException('Unable to iterate given data.'); + } + + foreach ($data as $item) { + $this->push(is_callable($callback) ? $callback($item) : $item); + } + + return $this; + } +} diff --git a/src/Drivers/Abstract/AbstractColor.php b/src/Drivers/Abstract/AbstractColor.php new file mode 100644 index 00000000..449e4747 --- /dev/null +++ b/src/Drivers/Abstract/AbstractColor.php @@ -0,0 +1,17 @@ +red(), + $this->green(), + $this->blue() + ); + } +} diff --git a/src/Drivers/Abstract/AbstractFrame.php b/src/Drivers/Abstract/AbstractFrame.php new file mode 100644 index 00000000..c83c8af3 --- /dev/null +++ b/src/Drivers/Abstract/AbstractFrame.php @@ -0,0 +1,8 @@ +frames; + } + + public function getFrames(): Collection + { + return $this->frames; + } + + public function addFrame(FrameInterface $frame): ImageInterface + { + $this->frames->push($frame); + + return $this; + } + + public function size(): SizeInterface + { + return new Size($this->width(), $this->height()); + } + + public function isAnimated(): bool + { + return $this->getFrames()->count() > 1; + } + + public function modify(ModifierInterface $modifier): ImageInterface + { + return $modifier->apply($this); + } + + public function encode(EncoderInterface $encoder, ?string $path = null): string + { + $encoded = $encoder->encode($this); + + if ($path) { + $saved = @file_put_contents($path, $encoded); + if ($saved === false) { + throw new NotWritableException( + "Can't write image data to path ({$path})." + ); + } + } + + return $encoded; + } + + public function toJpeg(?int $quality = null, ?string $path = null): string + { + return $this->encode( + $this->resolveDriverClass('Encoders\JpegEncoder', $quality), + $path + ); + } + + public function toGif(?string $path = null): string + { + return $this->encode( + $this->resolveDriverClass('Encoders\GifEncoder'), + $path + ); + } +} diff --git a/src/Drivers/Abstract/AbstractInputHandler.php b/src/Drivers/Abstract/AbstractInputHandler.php new file mode 100644 index 00000000..fce61f85 --- /dev/null +++ b/src/Drivers/Abstract/AbstractInputHandler.php @@ -0,0 +1,17 @@ +chain()->handle($input); + } +} diff --git a/src/Drivers/Abstract/Decoders/AbstractDecoder.php b/src/Drivers/Abstract/Decoders/AbstractDecoder.php new file mode 100644 index 00000000..93740041 --- /dev/null +++ b/src/Drivers/Abstract/Decoders/AbstractDecoder.php @@ -0,0 +1,47 @@ +decode($input); + } catch (DecoderException $e) { + if ($this->hasSuccessor()) { + return $this->successor->handle($input); + } + + $this->fail(); + } + + return $decoded; + } + + protected function hasSuccessor(): bool + { + return $this->successor !== null; + } + + protected function fail(): void + { + throw new DecoderException("Unable to decode given input."); + } + + protected function inputType($input): AbstractType + { + return MimeSniffer::createFromString($input)->getType(); + } +} diff --git a/src/Drivers/Abstract/Encoders/AbstractEncoder.php b/src/Drivers/Abstract/Encoders/AbstractEncoder.php new file mode 100644 index 00000000..31a53ac5 --- /dev/null +++ b/src/Drivers/Abstract/Encoders/AbstractEncoder.php @@ -0,0 +1,41 @@ +quality = $quality; + + return $this; + } + + public function getQuality(): int + { + return $this->quality; + } +} diff --git a/src/Drivers/Gd/Color.php b/src/Drivers/Gd/Color.php new file mode 100644 index 00000000..351072bd --- /dev/null +++ b/src/Drivers/Gd/Color.php @@ -0,0 +1,57 @@ +value = $value; + } + + public function red(): int + { + return $this->toArray()[0]; + } + + public function green(): int + { + return $this->toArray()[1]; + } + + public function blue(): int + { + return $this->toArray()[2]; + } + + public function alpha(): float + { + return $this->toArray()[3]; + } + + public function toArray(): array + { + $a = ($this->value >> 24) & 0xFF; + $r = ($this->value >> 16) & 0xFF; + $g = ($this->value >> 8) & 0xFF; + $b = $this->value & 0xFF; + $a = (float) round(1 - $a / 127, 2); + + return [$r, $g, $b, $a]; + } +} diff --git a/src/Drivers/Gd/Decoders/ArrayColorDecoder.php b/src/Drivers/Gd/Decoders/ArrayColorDecoder.php new file mode 100644 index 00000000..7a1b7161 --- /dev/null +++ b/src/Drivers/Gd/Decoders/ArrayColorDecoder.php @@ -0,0 +1,33 @@ +isValidColorArray($input)) { + $this->fail(); + } + + list($r, $g, $b, $a) = $input; + + return new Color( + ($this->opacityToGdAlpha($a) << 24) + ($r << 16) + ($g << 8) + $b + ); + } + + protected function opacityToGdAlpha(float $opacity): int + { + return intval(round($opacity * 127 * -1 + 127)); + } +} diff --git a/src/Drivers/Gd/Decoders/Base64ImageDecoder.php b/src/Drivers/Gd/Decoders/Base64ImageDecoder.php new file mode 100644 index 00000000..fb22e3ba --- /dev/null +++ b/src/Drivers/Gd/Decoders/Base64ImageDecoder.php @@ -0,0 +1,22 @@ +isValidBase64($input)) { + $this->fail(); + } + + return parent::decode(base64_decode($input)); + } +} diff --git a/src/Drivers/Gd/Decoders/BinaryImageDecoder.php b/src/Drivers/Gd/Decoders/BinaryImageDecoder.php new file mode 100644 index 00000000..a5e69c0b --- /dev/null +++ b/src/Drivers/Gd/Decoders/BinaryImageDecoder.php @@ -0,0 +1,55 @@ +inputType($input)->isBinary()) { + $this->fail(); + } + + if (is_a($this->inputType($input), ImageGif::class)) { + return $this->decodeGif($input); // decode (animated) gif + } + + $resource = @imagecreatefromstring($input); + + if ($resource === false) { + $this->fail(); + } + + return new Image(new Collection([new Frame($resource)])); + } + + protected function decodeGif($input): ImageInterface + { + $image = new Image(new Collection()); + $gif = GifDecoder::decode($input); + + if (!$gif->isAnimated()) { + return $image->addFrame(new Frame(@imagecreatefromstring($input))); + } + + $splitter = GifSplitter::create($gif)->split(); + $delays = $splitter->getDelays(); + foreach ($splitter->coalesceToResources() as $key => $gd) { + $image->addFrame((new Frame($gd))->setDelay($delays[$key])); + } + + return $image; + } +} diff --git a/src/Drivers/Gd/Decoders/DataUriImageDecoder.php b/src/Drivers/Gd/Decoders/DataUriImageDecoder.php new file mode 100644 index 00000000..b1c8f245 --- /dev/null +++ b/src/Drivers/Gd/Decoders/DataUriImageDecoder.php @@ -0,0 +1,33 @@ +fail(); + } + + $uri = $this->decodeDataUri($input); + + if (! $uri->isValid()) { + $this->fail(); + } + + if ($uri->isBase64Encoded()) { + return parent::decode(base64_decode($uri->data())); + } + + return parent::decode(urldecode($uri->data())); + } +} diff --git a/src/Drivers/Gd/Decoders/FilePathImageDecoder.php b/src/Drivers/Gd/Decoders/FilePathImageDecoder.php new file mode 100644 index 00000000..6448a645 --- /dev/null +++ b/src/Drivers/Gd/Decoders/FilePathImageDecoder.php @@ -0,0 +1,28 @@ +fail(); + } + + try { + if (! @is_file($input)) { + $this->fail(); + } + } catch (Exception $e) { + $this->fail(); + } + + return parent::decode(file_get_contents($input)); + } +} diff --git a/src/Drivers/Gd/Encoders/GifEncoder.php b/src/Drivers/Gd/Encoders/GifEncoder.php new file mode 100644 index 00000000..d4a99a02 --- /dev/null +++ b/src/Drivers/Gd/Encoders/GifEncoder.php @@ -0,0 +1,33 @@ +isAnimated()) { + return $this->encodeAnimated($image); + } + + return $this->getBuffered(function () use ($image) { + imagegif($image->getFrames()->first()->getCore()); + }); + } + + protected function encodeAnimated($image): string + { + $builder = GifBuilder::canvas($image->width(), $image->height(), 2); + foreach ($image as $key => $frame) { + $source = $this->encode($frame->toImage()); + $builder->addFrame($source, $frame->getDelay()); + } + + return $builder->encode(); + } +} diff --git a/src/Drivers/Gd/Encoders/JpegEncoder.php b/src/Drivers/Gd/Encoders/JpegEncoder.php new file mode 100644 index 00000000..408c7b95 --- /dev/null +++ b/src/Drivers/Gd/Encoders/JpegEncoder.php @@ -0,0 +1,17 @@ +getBuffered(function () use ($image) { + imagejpeg($image->getFrames()->first()->getCore(), null, $this->quality); + }); + } +} diff --git a/src/Drivers/Gd/Frame.php b/src/Drivers/Gd/Frame.php new file mode 100644 index 00000000..e5c9e0d1 --- /dev/null +++ b/src/Drivers/Gd/Frame.php @@ -0,0 +1,111 @@ +core; + } + + public function getDelay(): int + { + return $this->delay; + } + + public function setDelay(int $delay): FrameInterface + { + $this->delay = $delay; + + return $this; + } + + public function getDispose(): int + { + return $this->dispose; + } + + public function setDispose(int $dispose): FrameInterface + { + $this->dispose = $dispose; + + return $this; + } + + public function setOffset(int $left, int $top): FrameInterface + { + $this->offset_left = $left; + $this->offset_top = $top; + + return $this; + } + + public function getOffsetLeft(): int + { + return $this->offset_left; + } + + public function setOffsetLeft(int $offset): FrameInterface + { + $this->offset_left = $offset; + + return $this; + } + + public function getOffsetTop(): int + { + return $this->offset_top; + } + + public function setOffsetTop(int $offset): FrameInterface + { + $this->offset_top = $offset; + + return $this; + } + + public function toImage(): ImageInterface + { + return new Image(new Collection([$this])); + } +} diff --git a/src/Drivers/Gd/Image.php b/src/Drivers/Gd/Image.php new file mode 100644 index 00000000..40742d24 --- /dev/null +++ b/src/Drivers/Gd/Image.php @@ -0,0 +1,36 @@ +frames->first()->getCore()); + } + + public function height(): int + { + return imagesy($this->frames->first()->getCore()); + } + + public function greyscale(): ImageInterface + { + return $this->modify(new Modifiers\GreyscaleModifier()); + } +} diff --git a/src/Drivers/Gd/ImageFactory.php b/src/Drivers/Gd/ImageFactory.php new file mode 100644 index 00000000..0127f36c --- /dev/null +++ b/src/Drivers/Gd/ImageFactory.php @@ -0,0 +1,19 @@ +getCore(), IMG_FILTER_GRAYSCALE); + } + + return $image; + } +} diff --git a/src/Drivers/Imagick/Color.php b/src/Drivers/Imagick/Color.php new file mode 100644 index 00000000..f07a82ca --- /dev/null +++ b/src/Drivers/Imagick/Color.php @@ -0,0 +1,53 @@ +pixel = $pixel; + } + + public function red(): int + { + return round($this->pixel->getColorValue(Imagick::COLOR_RED) * 255); + } + + public function green(): int + { + return round($this->pixel->getColorValue(Imagick::COLOR_GREEN) * 255); + } + + public function blue(): int + { + return round($this->pixel->getColorValue(Imagick::COLOR_BLUE) * 255); + } + + public function alpha(): float + { + return round($this->pixel->getColorValue(Imagick::COLOR_ALPHA), 2); + } + + public function toArray(): array + { + return [ + $this->red(), + $this->green(), + $this->blue(), + $this->alpha() + ]; + } +} diff --git a/src/Drivers/Imagick/Decoders/ArrayColorDecoder.php b/src/Drivers/Imagick/Decoders/ArrayColorDecoder.php new file mode 100644 index 00000000..e2110744 --- /dev/null +++ b/src/Drivers/Imagick/Decoders/ArrayColorDecoder.php @@ -0,0 +1,31 @@ +isValidColorArray($input)) { + $this->fail(); + } + + list($r, $g, $b, $a) = $input; + + $pixel = new ImagickPixel( + sprintf('rgba(%d, %d, %d, %.2F)', $r, $g, $b, $a) + ); + + return new Color($pixel); + } +} diff --git a/src/Drivers/Imagick/Decoders/Base64ImageDecoder.php b/src/Drivers/Imagick/Decoders/Base64ImageDecoder.php new file mode 100644 index 00000000..46d34430 --- /dev/null +++ b/src/Drivers/Imagick/Decoders/Base64ImageDecoder.php @@ -0,0 +1,22 @@ +isValidBase64($input)) { + $this->fail(); + } + + return parent::decode(base64_decode($input)); + } +} diff --git a/src/Drivers/Imagick/Decoders/BinaryImageDecoder.php b/src/Drivers/Imagick/Decoders/BinaryImageDecoder.php new file mode 100644 index 00000000..96cc9a98 --- /dev/null +++ b/src/Drivers/Imagick/Decoders/BinaryImageDecoder.php @@ -0,0 +1,34 @@ +inputType($input)->isBinary()) { + $this->fail(); + } + + $imagick = new Imagick(); + $imagick->readImageBlob($input); + $imagick = $imagick->coalesceImages(); + + $image = new Image(new Collection()); + + foreach ($imagick as $frame_content) { + $image->addFrame(new Frame($frame_content)); + } + + return $image; + } +} diff --git a/src/Drivers/Imagick/Decoders/DataUriImageDecoder.php b/src/Drivers/Imagick/Decoders/DataUriImageDecoder.php new file mode 100644 index 00000000..9f80b117 --- /dev/null +++ b/src/Drivers/Imagick/Decoders/DataUriImageDecoder.php @@ -0,0 +1,33 @@ +fail(); + } + + $uri = $this->decodeDataUri($input); + + if (! $uri->isValid()) { + $this->fail(); + } + + if ($uri->isBase64Encoded()) { + return parent::decode(base64_decode($uri->data())); + } + + return parent::decode(urldecode($uri->data())); + } +} diff --git a/src/Drivers/Imagick/Decoders/FilePathImageDecoder.php b/src/Drivers/Imagick/Decoders/FilePathImageDecoder.php new file mode 100644 index 00000000..a4fe1d30 --- /dev/null +++ b/src/Drivers/Imagick/Decoders/FilePathImageDecoder.php @@ -0,0 +1,28 @@ +fail(); + } + + try { + if (! @is_file($input)) { + $this->fail(); + } + } catch (Exception $e) { + $this->fail(); + } + + return parent::decode(file_get_contents($input)); + } +} diff --git a/src/Drivers/Imagick/Encoders/GifEncoder.php b/src/Drivers/Imagick/Encoders/GifEncoder.php new file mode 100644 index 00000000..4b62dae2 --- /dev/null +++ b/src/Drivers/Imagick/Encoders/GifEncoder.php @@ -0,0 +1,29 @@ +getFrames() as $frame) { + $gif->addImage($frame->getCore()); + } + + $gif->setFormat($format); + $gif->setImageFormat($format); + $gif->setCompression($compression); + $gif->setImageCompression($compression); + + return $gif->getImagesBlob(); + } +} diff --git a/src/Drivers/Imagick/Encoders/JpegEncoder.php b/src/Drivers/Imagick/Encoders/JpegEncoder.php new file mode 100644 index 00000000..179531d7 --- /dev/null +++ b/src/Drivers/Imagick/Encoders/JpegEncoder.php @@ -0,0 +1,29 @@ +getFrames()->first()->getCore(); + $imagick->setImageBackgroundColor('white'); + $imagick->setBackgroundColor('white'); + $imagick->setFormat($format); + $imagick->setImageFormat($format); + $imagick->setCompression($compression); + $imagick->setImageCompression($compression); + $imagick->setCompressionQuality($this->quality); + $imagick->setImageCompressionQuality($this->quality); + + return $imagick->getImagesBlob(); + } +} diff --git a/src/Drivers/Imagick/Frame.php b/src/Drivers/Imagick/Frame.php new file mode 100644 index 00000000..c5c5652a --- /dev/null +++ b/src/Drivers/Imagick/Frame.php @@ -0,0 +1,83 @@ +core; + } + + public function getDelay(): int + { + return $this->core->getImageDelay(); + } + + public function setDelay(int $delay): FrameInterface + { + $this->core->setImageDelay($delay); + + return $this; + } + + public function getDispose(): int + { + return $this->core->getImageDispose(); + } + + public function setDispose(int $dispose): FrameInterface + { + $this->core->setImageDispose($dispose); + + return $this; + } + + public function setOffset(int $left, int $top): FrameInterface + { + $this->core->setImagePage( + $this->core->getImageWidth(), + $this->core->getImageHeight(), + $left, + $top + ); + + return $this; + } + + public function getOffsetLeft(): int + { + return $this->core->getImagePage()['x']; + } + + public function setOffsetLeft(int $offset): FrameInterface + { + return $this->setOffset($offset, $this->getOffsetTop()); + } + + public function getOffsetTop(): int + { + return $this->core->getImagePage()['y']; + } + + public function setOffsetTop(int $offset): FrameInterface + { + return $this->setOffset($this->getOffsetLeft(), $offset); + } + + public function toImage(): ImageInterface + { + return new Image(new Collection([$this])); + } +} diff --git a/src/Drivers/Imagick/Image.php b/src/Drivers/Imagick/Image.php new file mode 100644 index 00000000..be0a3afc --- /dev/null +++ b/src/Drivers/Imagick/Image.php @@ -0,0 +1,35 @@ +frames->first()->getCore()->getImageWidth(); + } + + public function height(): int + { + return $this->frames->first()->getCore()->getImageHeight(); + } + + public function greyscale(): ImageInterface + { + return $this->modify(new Modifiers\GreyscaleModifier()); + } +} diff --git a/src/Drivers/Imagick/InputHandler.php b/src/Drivers/Imagick/InputHandler.php new file mode 100644 index 00000000..d1fcbde2 --- /dev/null +++ b/src/Drivers/Imagick/InputHandler.php @@ -0,0 +1,22 @@ +image as $frame) { + $frame->getCore()->modulateImage(100, 0, 100); + } + + return $this->image; + } +} diff --git a/src/Exceptions/DecoderException.php b/src/Exceptions/DecoderException.php new file mode 100644 index 00000000..9271e202 --- /dev/null +++ b/src/Exceptions/DecoderException.php @@ -0,0 +1,8 @@ +x = $x; + $this->y = $y; + } + + /** + * Sets X coordinate + * + * @param integer $x + */ + public function setX(int $x): self + { + $this->x = $x; + + return $this; + } + + /** + * Get X coordinate + * + * @return integer + */ + public function getX(): int + { + return $this->x; + } + + /** + * Sets Y coordinate + * + * @param integer $y + */ + public function setY(int $y): self + { + $this->y = $y; + + return $this; + } + + /** + * Get Y coordinate + * + * @return integer + */ + public function getY(): int + { + return $this->y; + } + + /** + * Move X coordinate + * + * @param integer $x + */ + public function moveX(int $value): self + { + $this->x += $value; + + return $this; + } + + /** + * Move Y coordinate + * + * @param integer $y + */ + public function moveY(int $value): self + { + $this->y += $value; + + return $this; + } + + /** + * Sets both X and Y coordinate + * + * @param integer $x + * @param integer $y + * @return Point + */ + public function setPosition(int $x, int $y): self + { + $this->setX($x); + $this->setY($y); + + return $this; + } + + /** + * Rotate point ccw around pivot + * + * @param float $angle + * @param Point $pivot + * @return Point + */ + public function rotate(float $angle, Point $pivot): self + { + $sin = round(sin(deg2rad($angle)), 6); + $cos = round(cos(deg2rad($angle)), 6); + + return $this->setPosition( + $cos * ($this->x - $pivot->x) - $sin * ($this->y - $pivot->y) + $pivot->x, + $sin * ($this->x - $pivot->x) + $cos * ($this->y - $pivot->y) + $pivot->y + ); + } +} diff --git a/src/Geometry/Size.php b/src/Geometry/Size.php new file mode 100644 index 00000000..4f19bc30 --- /dev/null +++ b/src/Geometry/Size.php @@ -0,0 +1,92 @@ +width = $width; + $this->height = $height; + $this->pivot = $pivot ? $pivot : new Point(); + } + + public function getWidth(): int + { + return $this->width; + } + + public function getHeight(): int + { + return $this->height; + } + + /** + * Get current pivot point + * + * @return Point + */ + public function getPivot(): PointInterface + { + return $this->pivot; + } + + public function setWidth(int $width): SizeInterface + { + $this->width = $width; + + return $this; + } + + public function setHeight(int $height): SizeInterface + { + $this->height = $height; + + return $this; + } + + public function getAspectRatio(): float + { + return $this->width / $this->height; + } + + public function fitsInto(SizeInterface $size): bool + { + if ($this->getWidth() > $size->getWidth()) { + return false; + } + + if ($this->getHeight() > $size->getHeight()) { + return false; + } + + return true; + } + + /** + * Determine if size is landscape format + * + * @return boolean + */ + public function isLandscape(): bool + { + return $this->getWidth() > $this->getHeight(); + } + + /** + * Determine if size is portrait format + * + * @return boolean + */ + public function isPortrait(): bool + { + return $this->getWidth() < $this->getHeight(); + } +} diff --git a/src/ImageManager.php b/src/ImageManager.php new file mode 100644 index 00000000..428df54b --- /dev/null +++ b/src/ImageManager.php @@ -0,0 +1,96 @@ + 'gd', + ]; + + /** + * Create new instance + * + * @param array $config + */ + public function __construct(array $config = []) + { + $this->configure($config); + } + + /** + * Override configuration settings + * + * @param array $config + */ + public function configure(array $config = []): self + { + $this->config = array_replace($this->config, $config); + + return $this; + } + + /** + * Return given value of configuration + * + * @param string $key + * @param mixed $default + * @return mixed + */ + public function getConfig($key, $default = null) + { + return array_key_exists($key, $this->config) ? $this->config[$key] : $default; + } + + /** + * Create new image instance from scratch + * + * @param int $width + * @param int $height + * @return ImageInterface + */ + public function create(int $width, int $height): ImageInterface + { + return $this->resolve('ImageFactory')->newImage($width, $height); + } + + /** + * Create new image instance from input + * + * @param mixed $input + * @return ImageInterface + */ + public function make($input): ImageInterface + { + return $this->resolve('InputHandler')->handle($input); + } + + /** + * Resolve given classname according to current configuration + * + * @param string $classname + * @param array $arguments + * @return mixed + */ + private function resolve(string $classname, ...$arguments) + { + $classname = sprintf( + "Intervention\\Image\\Drivers\\%s\\%s", + ucfirst($this->config['driver']), + $classname + ); + + $reflection = new ReflectionClass($classname); + + return $reflection->newInstanceArgs($arguments); + } +} diff --git a/src/Interfaces/CollectionInterface.php b/src/Interfaces/CollectionInterface.php new file mode 100644 index 00000000..5c63e5a7 --- /dev/null +++ b/src/Interfaces/CollectionInterface.php @@ -0,0 +1,12 @@ +parse($value); - } - - /** - * Parses given value as color - * - * @param mixed $value - * @return \Intervention\Image\AbstractColor - */ - public function parse($value) - { - switch (true) { - - case is_string($value): - $this->initFromString($value); - break; - - case is_int($value): - $this->initFromInteger($value); - break; - - case is_array($value): - $this->initFromArray($value); - break; - - case is_object($value): - $this->initFromObject($value); - break; - - case is_null($value): - $this->initFromArray([255, 255, 255, 0]); - break; - - default: - throw new NotReadableException( - "Color format ({$value}) cannot be read." - ); - } - - return $this; - } - - /** - * Formats current color instance into given format - * - * @param string $type - * @return mixed - */ - public function format($type) - { - switch (strtolower($type)) { - - case 'rgba': - return $this->getRgba(); - - case 'hex': - return $this->getHex('#'); - - case 'int': - case 'integer': - return $this->getInt(); - - case 'array': - return $this->getArray(); - - case 'obj': - case 'object': - return $this; - - default: - throw new NotSupportedException( - "Color format ({$type}) is not supported." - ); - } - } - - /** - * Reads RGBA values from string into array - * - * @param string $value - * @return array - */ - protected function rgbaFromString($value) - { - $result = false; - - // parse color string in hexidecimal format like #cccccc or cccccc or ccc - $hexPattern = '/^#?([a-f0-9]{1,2})([a-f0-9]{1,2})([a-f0-9]{1,2})$/i'; - - // parse color string in format rgb(140, 140, 140) - $rgbPattern = '/^rgb ?\(([0-9]{1,3}), ?([0-9]{1,3}), ?([0-9]{1,3})\)$/i'; - - // parse color string in format rgba(255, 0, 0, 0.5) - $rgbaPattern = '/^rgba ?\(([0-9]{1,3}), ?([0-9]{1,3}), ?([0-9]{1,3}), ?([0-9.]{1,4})\)$/i'; - - if (preg_match($hexPattern, $value, $matches)) { - $result = []; - $result[0] = strlen($matches[1]) == '1' ? hexdec($matches[1].$matches[1]) : hexdec($matches[1]); - $result[1] = strlen($matches[2]) == '1' ? hexdec($matches[2].$matches[2]) : hexdec($matches[2]); - $result[2] = strlen($matches[3]) == '1' ? hexdec($matches[3].$matches[3]) : hexdec($matches[3]); - $result[3] = 1; - } elseif (preg_match($rgbPattern, $value, $matches)) { - $result = []; - $result[0] = ($matches[1] >= 0 && $matches[1] <= 255) ? intval($matches[1]) : 0; - $result[1] = ($matches[2] >= 0 && $matches[2] <= 255) ? intval($matches[2]) : 0; - $result[2] = ($matches[3] >= 0 && $matches[3] <= 255) ? intval($matches[3]) : 0; - $result[3] = 1; - } elseif (preg_match($rgbaPattern, $value, $matches)) { - $result = []; - $result[0] = ($matches[1] >= 0 && $matches[1] <= 255) ? intval($matches[1]) : 0; - $result[1] = ($matches[2] >= 0 && $matches[2] <= 255) ? intval($matches[2]) : 0; - $result[2] = ($matches[3] >= 0 && $matches[3] <= 255) ? intval($matches[3]) : 0; - $result[3] = ($matches[4] >= 0 && $matches[4] <= 1) ? $matches[4] : 0; - } else { - throw new NotReadableException( - "Unable to read color ({$value})." - ); - } - - return $result; - } -} diff --git a/src/Intervention/Image/AbstractDecoder.php b/src/Intervention/Image/AbstractDecoder.php deleted file mode 100644 index 743cc5c3..00000000 --- a/src/Intervention/Image/AbstractDecoder.php +++ /dev/null @@ -1,364 +0,0 @@ -data = $data; - } - - /** - * Init from given URL - * - * @param string $url - * @return \Intervention\Image\Image - */ - public function initFromUrl($url) - { - - $options = [ - 'http' => [ - 'method'=>"GET", - 'protocol_version'=>1.1, // force use HTTP 1.1 for service mesh environment with envoy - 'header'=>"Accept-language: en\r\n". - "User-Agent: Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.2 (KHTML, like Gecko) Chrome/22.0.1216.0 Safari/537.2\r\n" - ] - ]; - - $context = stream_context_create($options); - - - if ($data = @file_get_contents($url, false, $context)) { - return $this->initFromBinary($data); - } - - throw new NotReadableException( - "Unable to init from given url (".$url.")." - ); - } - - /** - * Init from given stream - * - * @param StreamInterface|resource $stream - * @return \Intervention\Image\Image - */ - public function initFromStream($stream) - { - if (!$stream instanceof StreamInterface) { - $stream = new Stream($stream); - } - - try { - $offset = $stream->tell(); - } catch (\RuntimeException $e) { - $offset = 0; - } - - $shouldAndCanSeek = $offset !== 0 && $stream->isSeekable(); - - if ($shouldAndCanSeek) { - $stream->rewind(); - } - - try { - $data = $stream->getContents(); - } catch (\RuntimeException $e) { - $data = null; - } - - if ($shouldAndCanSeek) { - $stream->seek($offset); - } - - if ($data) { - return $this->initFromBinary($data); - } - - throw new NotReadableException( - "Unable to init from given stream" - ); - } - - /** - * Determines if current source data is GD resource - * - * @return boolean - */ - public function isGdResource() - { - if (is_resource($this->data)) { - return (get_resource_type($this->data) == 'gd'); - } - - if ($this->data instanceof \GdImage) { - return true; - } - - return false; - } - - /** - * Determines if current source data is Imagick object - * - * @return boolean - */ - public function isImagick() - { - return is_a($this->data, 'Imagick'); - } - - /** - * Determines if current source data is Intervention\Image\Image object - * - * @return boolean - */ - public function isInterventionImage() - { - return is_a($this->data, '\Intervention\Image\Image'); - } - - /** - * Determines if current data is SplFileInfo object - * - * @return boolean - */ - public function isSplFileInfo() - { - return is_a($this->data, 'SplFileInfo'); - } - - /** - * Determines if current data is Symfony UploadedFile component - * - * @return boolean - */ - public function isSymfonyUpload() - { - return is_a($this->data, 'Symfony\Component\HttpFoundation\File\UploadedFile'); - } - - /** - * Determines if current source data is file path - * - * @return boolean - */ - public function isFilePath() - { - if (is_string($this->data)) { - try { - return is_file($this->data); - } catch (\Exception $e) { - return false; - } - } - - return false; - } - - /** - * Determines if current source data is url - * - * @return boolean - */ - public function isUrl() - { - return (bool) filter_var($this->data, FILTER_VALIDATE_URL); - } - - /** - * Determines if current source data is a stream resource - * - * @return boolean - */ - public function isStream() - { - if ($this->data instanceof StreamInterface) return true; - if (!is_resource($this->data)) return false; - if (get_resource_type($this->data) !== 'stream') return false; - - return true; - } - - /** - * Determines if current source data is binary data - * - * @return boolean - */ - public function isBinary() - { - if (is_string($this->data)) { - $mime = finfo_buffer(finfo_open(FILEINFO_MIME_TYPE), $this->data); - return (substr($mime, 0, 4) != 'text' && $mime != 'application/x-empty'); - } - - return false; - } - - /** - * Determines if current source data is data-url - * - * @return boolean - */ - public function isDataUrl() - { - $data = $this->decodeDataUrl($this->data); - - return is_null($data) ? false : true; - } - - /** - * Determines if current source data is base64 encoded - * - * @return boolean - */ - public function isBase64() - { - if (!is_string($this->data)) { - return false; - } - - return base64_encode(base64_decode($this->data)) === str_replace(["\n", "\r"], '', $this->data); - } - - /** - * Initiates new Image from Intervention\Image\Image - * - * @param Image $object - * @return \Intervention\Image\Image - */ - public function initFromInterventionImage($object) - { - return $object; - } - - /** - * Parses and decodes binary image data from data-url - * - * @param string $data_url - * @return string - */ - private function decodeDataUrl($data_url) - { - if (!is_string($data_url)) { - return null; - } - - $pattern = "/^data:(?:image\/[a-zA-Z\-\.]+)(?:charset=\".+\")?;base64,(?P.+)$/"; - preg_match($pattern, str_replace(["\n", "\r"], '', $data_url), $matches); - - if (is_array($matches) && array_key_exists('data', $matches)) { - return base64_decode($matches['data']); - } - - return null; - } - - /** - * Initiates new image from mixed data - * - * @param mixed $data - * @return \Intervention\Image\Image - */ - public function init($data) - { - $this->data = $data; - - switch (true) { - - case $this->isGdResource(): - return $this->initFromGdResource($this->data); - - case $this->isImagick(): - return $this->initFromImagick($this->data); - - case $this->isInterventionImage(): - return $this->initFromInterventionImage($this->data); - - case $this->isSplFileInfo(): - return $this->initFromPath($this->data->getRealPath()); - - case $this->isBinary(): - return $this->initFromBinary($this->data); - - case $this->isUrl(): - return $this->initFromUrl($this->data); - - case $this->isStream(): - return $this->initFromStream($this->data); - - case $this->isDataUrl(): - return $this->initFromBinary($this->decodeDataUrl($this->data)); - - case $this->isFilePath(): - return $this->initFromPath($this->data); - - // isBase64 has to be after isFilePath to prevent false positives - case $this->isBase64(): - return $this->initFromBinary(base64_decode($this->data)); - - default: - throw new NotReadableException("Image source not readable"); - } - } - - /** - * Decoder object transforms to string source data - * - * @return string - */ - public function __toString() - { - return (string) $this->data; - } -} diff --git a/src/Intervention/Image/AbstractDriver.php b/src/Intervention/Image/AbstractDriver.php deleted file mode 100644 index 6c591a79..00000000 --- a/src/Intervention/Image/AbstractDriver.php +++ /dev/null @@ -1,140 +0,0 @@ -decoder->init($data); - } - - /** - * Encodes given image - * - * @param Image $image - * @param string $format - * @param int $quality - * @return \Intervention\Image\Image - */ - public function encode($image, $format, $quality) - { - return $this->encoder->process($image, $format, $quality); - } - - /** - * Executes named command on given image - * - * @param Image $image - * @param string $name - * @param array $arguments - * @return \Intervention\Image\Commands\AbstractCommand - */ - public function executeCommand($image, $name, $arguments) - { - $commandName = $this->getCommandClassName($name); - $command = new $commandName($arguments); - $command->execute($image); - - return $command; - } - - /** - * Returns classname of given command name - * - * @param string $name - * @return string - */ - private function getCommandClassName($name) - { - if (extension_loaded('mbstring')) { - $name = mb_strtoupper(mb_substr($name, 0, 1)) . mb_substr($name, 1); - } else { - $name = strtoupper(substr($name, 0, 1)) . substr($name, 1); - } - - $drivername = $this->getDriverName(); - $classnameLocal = sprintf('\Intervention\Image\%s\Commands\%sCommand', $drivername, ucfirst($name)); - $classnameGlobal = sprintf('\Intervention\Image\Commands\%sCommand', ucfirst($name)); - - if (class_exists($classnameLocal)) { - return $classnameLocal; - } elseif (class_exists($classnameGlobal)) { - return $classnameGlobal; - } - - throw new NotSupportedException( - "Command ({$name}) is not available for driver ({$drivername})." - ); - } - - /** - * Returns name of current driver instance - * - * @return string - */ - public function getDriverName() - { - $reflect = new \ReflectionClass($this); - $namespace = $reflect->getNamespaceName(); - - return substr(strrchr($namespace, "\\"), 1); - } -} diff --git a/src/Intervention/Image/AbstractEncoder.php b/src/Intervention/Image/AbstractEncoder.php deleted file mode 100644 index c25f8bff..00000000 --- a/src/Intervention/Image/AbstractEncoder.php +++ /dev/null @@ -1,271 +0,0 @@ -setImage($image); - $this->setFormat($format); - $this->setQuality($quality); - - switch (strtolower($this->format)) { - - case 'data-url': - $this->result = $this->processDataUrl(); - break; - - case 'gif': - case 'image/gif': - $this->result = $this->processGif(); - break; - - case 'png': - case 'image/png': - case 'image/x-png': - $this->result = $this->processPng(); - break; - - case 'jpg': - case 'jpeg': - case 'jfif': - case 'image/jp2': - case 'image/jpg': - case 'image/jpeg': - case 'image/pjpeg': - case 'image/jfif': - $this->result = $this->processJpeg(); - break; - - case 'tif': - case 'tiff': - case 'image/tiff': - case 'image/tif': - case 'image/x-tif': - case 'image/x-tiff': - $this->result = $this->processTiff(); - break; - - case 'bmp': - case 'ms-bmp': - case 'x-bitmap': - case 'x-bmp': - case 'x-ms-bmp': - case 'x-win-bitmap': - case 'x-windows-bmp': - case 'x-xbitmap': - case 'image/ms-bmp': - case 'image/x-bitmap': - case 'image/x-bmp': - case 'image/x-ms-bmp': - case 'image/x-win-bitmap': - case 'image/x-windows-bmp': - case 'image/x-xbitmap': - $this->result = $this->processBmp(); - break; - - case 'ico': - case 'image/x-ico': - case 'image/x-icon': - case 'image/vnd.microsoft.icon': - $this->result = $this->processIco(); - break; - - case 'psd': - case 'image/vnd.adobe.photoshop': - $this->result = $this->processPsd(); - break; - - case 'webp': - case 'image/webp': - case 'image/x-webp': - $this->result = $this->processWebp(); - break; - - case 'avif': - case 'image/avif': - $this->result = $this->processAvif(); - break; - - case 'heic': - case 'image/heic': - case 'image/heif': - $this->result = $this->processHeic(); - break; - - default: - throw new NotSupportedException( - "Encoding format ({$this->format}) is not supported." - ); - } - - $this->setImage(null); - - return $image->setEncoded($this->result); - } - - /** - * Processes and returns encoded image as data-url string - * - * @return string - */ - protected function processDataUrl() - { - $mime = $this->image->mime ? $this->image->mime : 'image/png'; - - return sprintf('data:%s;base64,%s', - $mime, - base64_encode($this->process($this->image, $mime, $this->quality)) - ); - } - - /** - * Sets image to process - * - * @param Image $image - */ - protected function setImage($image) - { - $this->image = $image; - } - - /** - * Determines output format - * - * @param string $format - */ - protected function setFormat($format = null) - { - if ($format == '' && $this->image instanceof Image) { - $format = $this->image->mime; - } - - $this->format = $format ? $format : 'jpg'; - - return $this; - } - - /** - * Determines output quality - * - * @param int $quality - */ - protected function setQuality($quality) - { - $quality = is_null($quality) ? 90 : $quality; - $quality = $quality === 0 ? 1 : $quality; - - if ($quality < 0 || $quality > 100) { - throw new InvalidArgumentException( - 'Quality must range from 0 to 100.' - ); - } - - $this->quality = intval($quality); - - return $this; - } -} diff --git a/src/Intervention/Image/AbstractFont.php b/src/Intervention/Image/AbstractFont.php deleted file mode 100644 index 35c1825a..00000000 --- a/src/Intervention/Image/AbstractFont.php +++ /dev/null @@ -1,295 +0,0 @@ -text = $text; - } - - /** - * Set text to be written - * - * @param String $text - * @return self - */ - public function text($text) - { - $this->text = $text; - - return $this; - } - - /** - * Get text to be written - * - * @return String - */ - public function getText() - { - return $this->text; - } - - /** - * Set font size in pixels - * - * @param int $size - * @return self - */ - public function size($size) - { - $this->size = $size; - - return $this; - } - - /** - * Get font size in pixels - * - * @return int - */ - public function getSize() - { - return $this->size; - } - - /** - * Set color of text to be written - * - * @param mixed $color - * @return self - */ - public function color($color) - { - $this->color = $color; - - return $this; - } - - /** - * Get color of text - * - * @return mixed - */ - public function getColor() - { - return $this->color; - } - - /** - * Set rotation angle of text - * - * @param int $angle - * @return self - */ - public function angle($angle) - { - $this->angle = $angle; - - return $this; - } - - /** - * Get rotation angle of text - * - * @return int - */ - public function getAngle() - { - return $this->angle; - } - - /** - * Set horizontal text alignment - * - * @param string $align - * @return self - */ - public function align($align) - { - $this->align = $align; - - return $this; - } - - /** - * Get horizontal text alignment - * - * @return string - */ - public function getAlign() - { - return $this->align; - } - - /** - * Set vertical text alignment - * - * @param string $valign - * @return self - */ - public function valign($valign) - { - $this->valign = $valign; - - return $this; - } - - /** - * Get vertical text alignment - * - * @return string - */ - public function getValign() - { - return $this->valign; - } - - /** - * Set text kerning - * - * @param string $kerning - * @return void - */ - public function kerning($kerning) - { - $this->kerning = $kerning; - } - - /** - * Get kerning - * - * @return float - */ - public function getKerning() - { - return $this->kerning; - } - - /** - * Set path to font file - * - * @param string $file - * @return self - */ - public function file($file) - { - $this->file = $file; - - return $this; - } - - /** - * Get path to font file - * - * @return string - */ - public function getFile() - { - return $this->file; - } - - /** - * Checks if current font has access to an applicable font file - * - * @return boolean - */ - protected function hasApplicableFontFile() - { - if (is_string($this->file)) { - return file_exists($this->file); - } - - return false; - } - - /** - * Counts lines of text to be written - * - * @return int - */ - public function countLines() - { - return count(explode(PHP_EOL, $this->text)); - } -} diff --git a/src/Intervention/Image/AbstractShape.php b/src/Intervention/Image/AbstractShape.php deleted file mode 100644 index cd4a9f1c..00000000 --- a/src/Intervention/Image/AbstractShape.php +++ /dev/null @@ -1,71 +0,0 @@ -background = $color; - } - - /** - * Set border width and color of current shape - * - * @param int $width - * @param string $color - * @return void - */ - public function border($width, $color = null) - { - $this->border_width = is_numeric($width) ? intval($width) : 0; - $this->border_color = is_null($color) ? '#000000' : $color; - } - - /** - * Determines if current shape has border - * - * @return boolean - */ - public function hasBorder() - { - return ($this->border_width >= 1); - } -} diff --git a/src/Intervention/Image/Commands/AbstractCommand.php b/src/Intervention/Image/Commands/AbstractCommand.php deleted file mode 100644 index e31078ce..00000000 --- a/src/Intervention/Image/Commands/AbstractCommand.php +++ /dev/null @@ -1,81 +0,0 @@ -arguments = $arguments; - } - - /** - * Creates new argument instance from given argument key - * - * @param int $key - * @return \Intervention\Image\Commands\Argument - */ - public function argument($key) - { - return new Argument($this, $key); - } - - /** - * Returns output data of current command - * - * @return mixed - */ - public function getOutput() - { - return $this->output ? $this->output : null; - } - - /** - * Determines if current instance has output data - * - * @return boolean - */ - public function hasOutput() - { - return ! is_null($this->output); - } - - /** - * Sets output data of current command - * - * @param mixed $value - */ - public function setOutput($value) - { - $this->output = $value; - } -} diff --git a/src/Intervention/Image/Commands/Argument.php b/src/Intervention/Image/Commands/Argument.php deleted file mode 100644 index 9538199f..00000000 --- a/src/Intervention/Image/Commands/Argument.php +++ /dev/null @@ -1,225 +0,0 @@ -command = $command; - $this->key = $key; - } - - /** - * Returns name of current arguments command - * - * @return string - */ - public function getCommandName() - { - preg_match("/\\\\([\w]+)Command$/", get_class($this->command), $matches); - return isset($matches[1]) ? lcfirst($matches[1]).'()' : 'Method'; - } - - /** - * Returns value of current argument - * - * @param mixed $default - * @return mixed - */ - public function value($default = null) - { - $arguments = $this->command->arguments; - - if (is_array($arguments)) { - return isset($arguments[$this->key]) ? $arguments[$this->key] : $default; - } - - return $default; - } - - /** - * Defines current argument as required - * - * @return \Intervention\Image\Commands\Argument - */ - public function required() - { - if ( ! array_key_exists($this->key, $this->command->arguments)) { - throw new InvalidArgumentException( - sprintf("Missing argument %d for %s", $this->key + 1, $this->getCommandName()) - ); - } - - return $this; - } - - /** - * Determines that current argument must be of given type - * - * @return \Intervention\Image\Commands\Argument - */ - public function type($type) - { - $valid = true; - $value = $this->value(); - - if ($value === null) { - return $this; - } - - switch (strtolower($type)) { - case 'bool': - case 'boolean': - $valid = \is_bool($value); - $message = '%s accepts only boolean values as argument %d.'; - break; - case 'int': - case 'integer': - $valid = \is_int($value); - $message = '%s accepts only integer values as argument %d.'; - break; - case 'num': - case 'numeric': - $valid = is_numeric($value); - $message = '%s accepts only numeric values as argument %d.'; - break; - case 'str': - case 'string': - $valid = \is_string($value); - $message = '%s accepts only string values as argument %d.'; - break; - case 'array': - $valid = \is_array($value); - $message = '%s accepts only array as argument %d.'; - break; - case 'closure': - $valid = is_a($value, '\Closure'); - $message = '%s accepts only Closure as argument %d.'; - break; - case 'digit': - $valid = $this->isDigit($value); - $message = '%s accepts only integer values as argument %d.'; - break; - } - - if (! $valid) { - $commandName = $this->getCommandName(); - $argument = $this->key + 1; - - if (isset($message)) { - $message = sprintf($message, $commandName, $argument); - } else { - $message = sprintf('Missing argument for %d.', $argument); - } - - throw new InvalidArgumentException( - $message - ); - } - - return $this; - } - - /** - * Determines that current argument value must be numeric between given values - * - * @return \Intervention\Image\Commands\Argument - */ - public function between($x, $y) - { - $value = $this->type('numeric')->value(); - - if (is_null($value)) { - return $this; - } - - $alpha = min($x, $y); - $omega = max($x, $y); - - if ($value < $alpha || $value > $omega) { - throw new InvalidArgumentException( - sprintf('Argument %d must be between %s and %s.', $this->key, $x, $y) - ); - } - - return $this; - } - - /** - * Determines that current argument must be over a minimum value - * - * @return \Intervention\Image\Commands\Argument - */ - public function min($value) - { - $v = $this->type('numeric')->value(); - - if (is_null($v)) { - return $this; - } - - if ($v < $value) { - throw new InvalidArgumentException( - sprintf('Argument %d must be at least %s.', $this->key, $value) - ); - } - - return $this; - } - - /** - * Determines that current argument must be under a maxiumum value - * - * @return \Intervention\Image\Commands\Argument - */ - public function max($value) - { - $v = $this->type('numeric')->value(); - - if (is_null($v)) { - return $this; - } - - if ($v > $value) { - throw new InvalidArgumentException( - sprintf('Argument %d may not be greater than %s.', $this->key, $value) - ); - } - - return $this; - } - - /** - * Checks if value is "PHP" integer (120 but also 120.0) - * - * @param mixed $value - * @return boolean - */ - private function isDigit($value) - { - return is_numeric($value) ? intval($value) == $value : false; - } -} diff --git a/src/Intervention/Image/Commands/ChecksumCommand.php b/src/Intervention/Image/Commands/ChecksumCommand.php deleted file mode 100644 index 9acc4030..00000000 --- a/src/Intervention/Image/Commands/ChecksumCommand.php +++ /dev/null @@ -1,29 +0,0 @@ -getSize(); - - for ($x=0; $x <= ($size->width-1); $x++) { - for ($y=0; $y <= ($size->height-1); $y++) { - $colors[] = $image->pickColor($x, $y, 'array'); - } - } - - $this->setOutput(md5(serialize($colors))); - - return true; - } -} diff --git a/src/Intervention/Image/Commands/CircleCommand.php b/src/Intervention/Image/Commands/CircleCommand.php deleted file mode 100644 index c627818e..00000000 --- a/src/Intervention/Image/Commands/CircleCommand.php +++ /dev/null @@ -1,35 +0,0 @@ -argument(0)->type('numeric')->required()->value(); - $x = $this->argument(1)->type('numeric')->required()->value(); - $y = $this->argument(2)->type('numeric')->required()->value(); - $callback = $this->argument(3)->type('closure')->value(); - - $circle_classname = sprintf('\Intervention\Image\%s\Shapes\CircleShape', - $image->getDriver()->getDriverName()); - - $circle = new $circle_classname($diameter); - - if ($callback instanceof Closure) { - $callback($circle); - } - - $circle->applyToImage($image, $x, $y); - - return true; - } -} diff --git a/src/Intervention/Image/Commands/EllipseCommand.php b/src/Intervention/Image/Commands/EllipseCommand.php deleted file mode 100644 index 4637e020..00000000 --- a/src/Intervention/Image/Commands/EllipseCommand.php +++ /dev/null @@ -1,36 +0,0 @@ -argument(0)->type('numeric')->required()->value(); - $height = $this->argument(1)->type('numeric')->required()->value(); - $x = $this->argument(2)->type('numeric')->required()->value(); - $y = $this->argument(3)->type('numeric')->required()->value(); - $callback = $this->argument(4)->type('closure')->value(); - - $ellipse_classname = sprintf('\Intervention\Image\%s\Shapes\EllipseShape', - $image->getDriver()->getDriverName()); - - $ellipse = new $ellipse_classname($width, $height); - - if ($callback instanceof Closure) { - $callback($ellipse); - } - - $ellipse->applyToImage($image, $x, $y); - - return true; - } -} diff --git a/src/Intervention/Image/Commands/ExifCommand.php b/src/Intervention/Image/Commands/ExifCommand.php deleted file mode 100644 index 063df6ef..00000000 --- a/src/Intervention/Image/Commands/ExifCommand.php +++ /dev/null @@ -1,61 +0,0 @@ -argument(0)->value(); - - // try to read exif data from image file - try { - if ($image->dirname && $image->basename) { - $stream = $image->dirname . '/' . $image->basename; - } elseif (version_compare(PHP_VERSION, '7.2.0', '>=')) { - // https://www.php.net/manual/en/function.exif-read-data.php#refsect1-function.exif-read-data-changelog - $stream = $image->stream()->detach(); - } else { - // https://bugs.php.net/bug.php?id=65187 - $stream = $image->encode('data-url')->encoded; - } - - $data = @exif_read_data($stream); - - if (!is_null($key) && is_array($data)) { - $data = array_key_exists($key, $data) ? $data[$key] : false; - } - - } catch (\Exception $e) { - throw new NotReadableException( - sprintf( - "Cannot read the Exif data from the filename (%s) provided ", - $image->dirname . '/' . $image->basename - ), - $e->getCode(), - $e - ); - } - - $this->setOutput($data); - return true; - } -} diff --git a/src/Intervention/Image/Commands/IptcCommand.php b/src/Intervention/Image/Commands/IptcCommand.php deleted file mode 100644 index b78a2a8e..00000000 --- a/src/Intervention/Image/Commands/IptcCommand.php +++ /dev/null @@ -1,68 +0,0 @@ -argument(0)->value(); - - $info = []; - @getimagesize($image->dirname .'/'. $image->basename, $info); - - $data = []; - - if (array_key_exists('APP13', $info)) { - $iptc = iptcparse($info['APP13']); - - if (is_array($iptc)) { - $data['DocumentTitle'] = isset($iptc["2#005"][0]) ? $iptc["2#005"][0] : null; - $data['Urgency'] = isset($iptc["2#010"][0]) ? $iptc["2#010"][0] : null; - $data['Category'] = isset($iptc["2#015"][0]) ? $iptc["2#015"][0] : null; - $data['Subcategories'] = isset($iptc["2#020"][0]) ? $iptc["2#020"][0] : null; - $data['Keywords'] = isset($iptc["2#025"][0]) ? $iptc["2#025"] : null; - $data['ReleaseDate'] = isset($iptc["2#030"][0]) ? $iptc["2#030"][0] : null; - $data['ReleaseTime'] = isset($iptc["2#035"][0]) ? $iptc["2#035"][0] : null; - $data['SpecialInstructions'] = isset($iptc["2#040"][0]) ? $iptc["2#040"][0] : null; - $data['CreationDate'] = isset($iptc["2#055"][0]) ? $iptc["2#055"][0] : null; - $data['CreationTime'] = isset($iptc["2#060"][0]) ? $iptc["2#060"][0] : null; - $data['AuthorByline'] = isset($iptc["2#080"][0]) ? $iptc["2#080"][0] : null; - $data['AuthorTitle'] = isset($iptc["2#085"][0]) ? $iptc["2#085"][0] : null; - $data['City'] = isset($iptc["2#090"][0]) ? $iptc["2#090"][0] : null; - $data['SubLocation'] = isset($iptc["2#092"][0]) ? $iptc["2#092"][0] : null; - $data['State'] = isset($iptc["2#095"][0]) ? $iptc["2#095"][0] : null; - $data['Country'] = isset($iptc["2#101"][0]) ? $iptc["2#101"][0] : null; - $data['OTR'] = isset($iptc["2#103"][0]) ? $iptc["2#103"][0] : null; - $data['Headline'] = isset($iptc["2#105"][0]) ? $iptc["2#105"][0] : null; - $data['Source'] = isset($iptc["2#110"][0]) ? $iptc["2#110"][0] : null; - $data['PhotoSource'] = isset($iptc["2#115"][0]) ? $iptc["2#115"][0] : null; - $data['Copyright'] = isset($iptc["2#116"][0]) ? $iptc["2#116"][0] : null; - $data['Caption'] = isset($iptc["2#120"][0]) ? $iptc["2#120"][0] : null; - $data['CaptionWriter'] = isset($iptc["2#122"][0]) ? $iptc["2#122"][0] : null; - } - } - - if (! is_null($key) && is_array($data)) { - $data = array_key_exists($key, $data) ? $data[$key] : false; - } - - $this->setOutput($data); - - return true; - } -} diff --git a/src/Intervention/Image/Commands/LineCommand.php b/src/Intervention/Image/Commands/LineCommand.php deleted file mode 100644 index a068c662..00000000 --- a/src/Intervention/Image/Commands/LineCommand.php +++ /dev/null @@ -1,36 +0,0 @@ -argument(0)->type('numeric')->required()->value(); - $y1 = $this->argument(1)->type('numeric')->required()->value(); - $x2 = $this->argument(2)->type('numeric')->required()->value(); - $y2 = $this->argument(3)->type('numeric')->required()->value(); - $callback = $this->argument(4)->type('closure')->value(); - - $line_classname = sprintf('\Intervention\Image\%s\Shapes\LineShape', - $image->getDriver()->getDriverName()); - - $line = new $line_classname($x2, $y2); - - if ($callback instanceof Closure) { - $callback($line); - } - - $line->applyToImage($image, $x1, $y1); - - return true; - } -} diff --git a/src/Intervention/Image/Commands/OrientateCommand.php b/src/Intervention/Image/Commands/OrientateCommand.php deleted file mode 100644 index 552482cb..00000000 --- a/src/Intervention/Image/Commands/OrientateCommand.php +++ /dev/null @@ -1,48 +0,0 @@ -exif('Orientation')) { - - case 2: - $image->flip(); - break; - - case 3: - $image->rotate(180); - break; - - case 4: - $image->rotate(180)->flip(); - break; - - case 5: - $image->rotate(270)->flip(); - break; - - case 6: - $image->rotate(270); - break; - - case 7: - $image->rotate(90)->flip(); - break; - - case 8: - $image->rotate(90); - break; - } - - return true; - } -} diff --git a/src/Intervention/Image/Commands/PolygonCommand.php b/src/Intervention/Image/Commands/PolygonCommand.php deleted file mode 100644 index a2fa9978..00000000 --- a/src/Intervention/Image/Commands/PolygonCommand.php +++ /dev/null @@ -1,49 +0,0 @@ -argument(0)->type('array')->required()->value(); - $callback = $this->argument(1)->type('closure')->value(); - - $vertices_count = count($points); - - // check if number if coordinates is even - if ($vertices_count % 2 !== 0) { - throw new InvalidArgumentException( - "The number of given polygon vertices must be even." - ); - } - - if ($vertices_count < 6) { - throw new InvalidArgumentException( - "You must have at least 3 points in your array." - ); - } - - $polygon_classname = sprintf('\Intervention\Image\%s\Shapes\PolygonShape', - $image->getDriver()->getDriverName()); - - $polygon = new $polygon_classname($points); - - if ($callback instanceof Closure) { - $callback($polygon); - } - - $polygon->applyToImage($image); - - return true; - } -} diff --git a/src/Intervention/Image/Commands/PsrResponseCommand.php b/src/Intervention/Image/Commands/PsrResponseCommand.php deleted file mode 100644 index d75cd903..00000000 --- a/src/Intervention/Image/Commands/PsrResponseCommand.php +++ /dev/null @@ -1,45 +0,0 @@ -argument(0)->value(); - $quality = $this->argument(1)->between(0, 100)->value(); - - //Encoded property will be populated at this moment - $stream = $image->stream($format, $quality); - - $mimetype = finfo_buffer( - finfo_open(FILEINFO_MIME_TYPE), - $image->getEncoded() - ); - - $this->setOutput(new Response( - 200, - [ - 'Content-Type' => $mimetype, - 'Content-Length' => strlen($image->getEncoded()) - ], - $stream - )); - - return true; - } -} \ No newline at end of file diff --git a/src/Intervention/Image/Commands/RectangleCommand.php b/src/Intervention/Image/Commands/RectangleCommand.php deleted file mode 100644 index 24378b38..00000000 --- a/src/Intervention/Image/Commands/RectangleCommand.php +++ /dev/null @@ -1,36 +0,0 @@ -argument(0)->type('numeric')->required()->value(); - $y1 = $this->argument(1)->type('numeric')->required()->value(); - $x2 = $this->argument(2)->type('numeric')->required()->value(); - $y2 = $this->argument(3)->type('numeric')->required()->value(); - $callback = $this->argument(4)->type('closure')->value(); - - $rectangle_classname = sprintf('\Intervention\Image\%s\Shapes\RectangleShape', - $image->getDriver()->getDriverName()); - - $rectangle = new $rectangle_classname($x1, $y1, $x2, $y2); - - if ($callback instanceof Closure) { - $callback($rectangle); - } - - $rectangle->applyToImage($image, $x1, $y1); - - return true; - } -} diff --git a/src/Intervention/Image/Commands/ResponseCommand.php b/src/Intervention/Image/Commands/ResponseCommand.php deleted file mode 100644 index 7903b5af..00000000 --- a/src/Intervention/Image/Commands/ResponseCommand.php +++ /dev/null @@ -1,26 +0,0 @@ -argument(0)->value(); - $quality = $this->argument(1)->between(0, 100)->value(); - - $response = new Response($image, $format, $quality); - - $this->setOutput($response->make()); - - return true; - } -} diff --git a/src/Intervention/Image/Commands/StreamCommand.php b/src/Intervention/Image/Commands/StreamCommand.php deleted file mode 100644 index bc2ff9ef..00000000 --- a/src/Intervention/Image/Commands/StreamCommand.php +++ /dev/null @@ -1,39 +0,0 @@ -argument(0)->value(); - $quality = $this->argument(1)->between(0, 100)->value(); - $data = $image->encode($format, $quality)->getEncoded(); - - $this->setOutput($this->getStream($data)); - - return true; - } - - /** - * Create stream from given data - * - * @param string $data - * @return \Psr\Http\Message\StreamInterface - */ - protected function getStream($data) - { - if (class_exists(\GuzzleHttp\Psr7\Utils::class)) { - return \GuzzleHttp\Psr7\Utils::streamFor($data); // guzzlehttp/psr7 >= 2.0 - } - - return \GuzzleHttp\Psr7\stream_for($data); // guzzlehttp/psr7 < 2.0 - } -} diff --git a/src/Intervention/Image/Commands/TextCommand.php b/src/Intervention/Image/Commands/TextCommand.php deleted file mode 100644 index 3c60b4e7..00000000 --- a/src/Intervention/Image/Commands/TextCommand.php +++ /dev/null @@ -1,34 +0,0 @@ -argument(0)->required()->value(); - $x = $this->argument(1)->type('numeric')->value(0); - $y = $this->argument(2)->type('numeric')->value(0); - $callback = $this->argument(3)->type('closure')->value(); - - $fontclassname = sprintf('\Intervention\Image\%s\Font', - $image->getDriver()->getDriverName()); - - $font = new $fontclassname($text); - - if ($callback instanceof Closure) { - $callback($font); - } - - $font->applyToImage($image, $x, $y); - - return true; - } -} diff --git a/src/Intervention/Image/Constraint.php b/src/Intervention/Image/Constraint.php deleted file mode 100644 index 44bdd67a..00000000 --- a/src/Intervention/Image/Constraint.php +++ /dev/null @@ -1,92 +0,0 @@ -size = $size; - } - - /** - * Returns current size of constraint - * - * @return \Intervention\Image\Size - */ - public function getSize() - { - return $this->size; - } - - /** - * Fix the given argument in current constraint - * - * @param int $type - * @return void - */ - public function fix($type) - { - $this->fixed = ($this->fixed & ~(1 << $type)) | (1 << $type); - } - - /** - * Checks if given argument is fixed in current constraint - * - * @param int $type - * @return boolean - */ - public function isFixed($type) - { - return (bool) ($this->fixed & (1 << $type)); - } - - /** - * Fixes aspect ratio in current constraint - * - * @return void - */ - public function aspectRatio() - { - $this->fix(self::ASPECTRATIO); - } - - /** - * Fixes possibility to size up in current constraint - * - * @return void - */ - public function upsize() - { - $this->fix(self::UPSIZE); - } -} diff --git a/src/Intervention/Image/Exception/ImageException.php b/src/Intervention/Image/Exception/ImageException.php deleted file mode 100644 index 83e6b91f..00000000 --- a/src/Intervention/Image/Exception/ImageException.php +++ /dev/null @@ -1,8 +0,0 @@ -dirname = array_key_exists('dirname', $info) ? $info['dirname'] : null; - $this->basename = array_key_exists('basename', $info) ? $info['basename'] : null; - $this->extension = array_key_exists('extension', $info) ? $info['extension'] : null; - $this->filename = array_key_exists('filename', $info) ? $info['filename'] : null; - - if (file_exists($path) && is_file($path)) { - $this->mime = finfo_file(finfo_open(FILEINFO_MIME_TYPE), $path); - } - - return $this; - } - - /** - * Get file size - * - * @return mixed - */ - public function filesize() - { - $path = $this->basePath(); - - if (file_exists($path) && is_file($path)) { - return filesize($path); - } - - return false; - } - - /** - * Get fully qualified path - * - * @return string - */ - public function basePath() - { - if ($this->dirname && $this->basename) { - return ($this->dirname .'/'. $this->basename); - } - - return null; - } - -} diff --git a/src/Intervention/Image/Filters/DemoFilter.php b/src/Intervention/Image/Filters/DemoFilter.php deleted file mode 100644 index 4e8f92b8..00000000 --- a/src/Intervention/Image/Filters/DemoFilter.php +++ /dev/null @@ -1,44 +0,0 @@ -size = is_numeric($size) ? intval($size) : self::DEFAULT_SIZE; - } - - /** - * Applies filter effects to given image - * - * @param \Intervention\Image\Image $image - * @return \Intervention\Image\Image - */ - public function applyFilter(Image $image) - { - $image->pixelate($this->size); - $image->greyscale(); - - return $image; - } -} diff --git a/src/Intervention/Image/Filters/FilterInterface.php b/src/Intervention/Image/Filters/FilterInterface.php deleted file mode 100644 index 88f6cbfe..00000000 --- a/src/Intervention/Image/Filters/FilterInterface.php +++ /dev/null @@ -1,16 +0,0 @@ -a = ($value >> 24) & 0xFF; - $this->r = ($value >> 16) & 0xFF; - $this->g = ($value >> 8) & 0xFF; - $this->b = $value & 0xFF; - } - - /** - * Initiates color object from given array - * - * @param array $value - * @return \Intervention\Image\AbstractColor - */ - public function initFromArray($array) - { - $array = array_values($array); - - if (count($array) == 4) { - - // color array with alpha value - list($r, $g, $b, $a) = $array; - $this->a = $this->alpha2gd($a); - - } elseif (count($array) == 3) { - - // color array without alpha value - list($r, $g, $b) = $array; - $this->a = 0; - - } - - $this->r = $r; - $this->g = $g; - $this->b = $b; - } - - /** - * Initiates color object from given string - * - * @param string $value - * @return \Intervention\Image\AbstractColor - */ - public function initFromString($value) - { - if ($color = $this->rgbaFromString($value)) { - $this->r = $color[0]; - $this->g = $color[1]; - $this->b = $color[2]; - $this->a = $this->alpha2gd($color[3]); - } - } - - /** - * Initiates color object from given R, G and B values - * - * @param int $r - * @param int $g - * @param int $b - * @return \Intervention\Image\AbstractColor - */ - public function initFromRgb($r, $g, $b) - { - $this->r = intval($r); - $this->g = intval($g); - $this->b = intval($b); - $this->a = 0; - } - - /** - * Initiates color object from given R, G, B and A values - * - * @param int $r - * @param int $g - * @param int $b - * @param float $a - * @return \Intervention\Image\AbstractColor - */ - public function initFromRgba($r, $g, $b, $a = 1) - { - $this->r = intval($r); - $this->g = intval($g); - $this->b = intval($b); - $this->a = $this->alpha2gd($a); - } - - /** - * Initiates color object from given ImagickPixel object - * - * @param ImagickPixel $value - * @return \Intervention\Image\AbstractColor - */ - public function initFromObject($value) - { - throw new NotSupportedException( - "GD colors cannot init from ImagickPixel objects." - ); - } - - /** - * Calculates integer value of current color instance - * - * @return int - */ - public function getInt() - { - return ($this->a << 24) + ($this->r << 16) + ($this->g << 8) + $this->b; - } - - /** - * Calculates hexadecimal value of current color instance - * - * @param string $prefix - * @return string - */ - public function getHex($prefix = '') - { - return sprintf('%s%02x%02x%02x', $prefix, $this->r, $this->g, $this->b); - } - - /** - * Calculates RGB(A) in array format of current color instance - * - * @return array - */ - public function getArray() - { - return [$this->r, $this->g, $this->b, round(1 - $this->a / 127, 2)]; - } - - /** - * Calculates RGBA in string format of current color instance - * - * @return string - */ - public function getRgba() - { - return sprintf('rgba(%d, %d, %d, %.2F)', $this->r, $this->g, $this->b, round(1 - $this->a / 127, 2)); - } - - /** - * Determines if current color is different from given color - * - * @param AbstractColor $color - * @param int $tolerance - * @return boolean - */ - public function differs(AbstractColor $color, $tolerance = 0) - { - $color_tolerance = round($tolerance * 2.55); - $alpha_tolerance = round($tolerance * 1.27); - - $delta = [ - 'r' => abs($color->r - $this->r), - 'g' => abs($color->g - $this->g), - 'b' => abs($color->b - $this->b), - 'a' => abs($color->a - $this->a) - ]; - - return ( - $delta['r'] > $color_tolerance or - $delta['g'] > $color_tolerance or - $delta['b'] > $color_tolerance or - $delta['a'] > $alpha_tolerance - ); - } - - /** - * Convert rgba alpha (0-1) value to gd value (0-127) - * - * @param float $input - * @return int - */ - private function alpha2gd($input) - { - $oldMin = 0; - $oldMax = 1; - - $newMin = 127; - $newMax = 0; - - return ceil(((($input- $oldMin) * ($newMax - $newMin)) / ($oldMax - $oldMin)) + $newMin); - } -} diff --git a/src/Intervention/Image/Gd/Commands/BackupCommand.php b/src/Intervention/Image/Gd/Commands/BackupCommand.php deleted file mode 100644 index 310dc03a..00000000 --- a/src/Intervention/Image/Gd/Commands/BackupCommand.php +++ /dev/null @@ -1,25 +0,0 @@ -argument(0)->value(); - - // clone current image resource - $clone = clone $image; - $image->setBackup($clone->getCore(), $backupName); - - return true; - } -} diff --git a/src/Intervention/Image/Gd/Commands/BlurCommand.php b/src/Intervention/Image/Gd/Commands/BlurCommand.php deleted file mode 100644 index c4fca0ea..00000000 --- a/src/Intervention/Image/Gd/Commands/BlurCommand.php +++ /dev/null @@ -1,25 +0,0 @@ -argument(0)->between(0, 100)->value(1); - - for ($i=0; $i < intval($amount); $i++) { - imagefilter($image->getCore(), IMG_FILTER_GAUSSIAN_BLUR); - } - - return true; - } -} diff --git a/src/Intervention/Image/Gd/Commands/BrightnessCommand.php b/src/Intervention/Image/Gd/Commands/BrightnessCommand.php deleted file mode 100644 index 4e48464b..00000000 --- a/src/Intervention/Image/Gd/Commands/BrightnessCommand.php +++ /dev/null @@ -1,21 +0,0 @@ -argument(0)->between(-100, 100)->required()->value(); - - return imagefilter($image->getCore(), IMG_FILTER_BRIGHTNESS, ($level * 2.55)); - } -} diff --git a/src/Intervention/Image/Gd/Commands/ColorizeCommand.php b/src/Intervention/Image/Gd/Commands/ColorizeCommand.php deleted file mode 100644 index 410917b3..00000000 --- a/src/Intervention/Image/Gd/Commands/ColorizeCommand.php +++ /dev/null @@ -1,29 +0,0 @@ -argument(0)->between(-100, 100)->required()->value(); - $green = $this->argument(1)->between(-100, 100)->required()->value(); - $blue = $this->argument(2)->between(-100, 100)->required()->value(); - - // normalize colorize levels - $red = round($red * 2.55); - $green = round($green * 2.55); - $blue = round($blue * 2.55); - - // apply filter - return imagefilter($image->getCore(), IMG_FILTER_COLORIZE, $red, $green, $blue); - } -} diff --git a/src/Intervention/Image/Gd/Commands/ContrastCommand.php b/src/Intervention/Image/Gd/Commands/ContrastCommand.php deleted file mode 100644 index 916c41f8..00000000 --- a/src/Intervention/Image/Gd/Commands/ContrastCommand.php +++ /dev/null @@ -1,21 +0,0 @@ -argument(0)->between(-100, 100)->required()->value(); - - return imagefilter($image->getCore(), IMG_FILTER_CONTRAST, ($level * -1)); - } -} diff --git a/src/Intervention/Image/Gd/Commands/CropCommand.php b/src/Intervention/Image/Gd/Commands/CropCommand.php deleted file mode 100644 index b7f59542..00000000 --- a/src/Intervention/Image/Gd/Commands/CropCommand.php +++ /dev/null @@ -1,40 +0,0 @@ -argument(0)->type('digit')->required()->value(); - $height = $this->argument(1)->type('digit')->required()->value(); - $x = $this->argument(2)->type('digit')->value(); - $y = $this->argument(3)->type('digit')->value(); - - if (is_null($width) || is_null($height)) { - throw new \Intervention\Image\Exception\InvalidArgumentException( - "Width and height of cutout needs to be defined." - ); - } - - $cropped = new Size($width, $height); - $position = new Point($x, $y); - - // align boxes - if (is_null($x) && is_null($y)) { - $position = $image->getSize()->align('center')->relativePosition($cropped->align('center')); - } - - // crop image core - return $this->modify($image, 0, 0, $position->x, $position->y, $cropped->width, $cropped->height, $cropped->width, $cropped->height); - } -} diff --git a/src/Intervention/Image/Gd/Commands/DestroyCommand.php b/src/Intervention/Image/Gd/Commands/DestroyCommand.php deleted file mode 100644 index 4503d10f..00000000 --- a/src/Intervention/Image/Gd/Commands/DestroyCommand.php +++ /dev/null @@ -1,27 +0,0 @@ -getCore()); - - // destroy backups - foreach ($image->getBackups() as $backup) { - imagedestroy($backup); - } - - return true; - } -} diff --git a/src/Intervention/Image/Gd/Commands/FillCommand.php b/src/Intervention/Image/Gd/Commands/FillCommand.php deleted file mode 100644 index cf190821..00000000 --- a/src/Intervention/Image/Gd/Commands/FillCommand.php +++ /dev/null @@ -1,69 +0,0 @@ -argument(0)->value(); - $x = $this->argument(1)->type('digit')->value(); - $y = $this->argument(2)->type('digit')->value(); - - $width = $image->getWidth(); - $height = $image->getHeight(); - $resource = $image->getCore(); - - try { - - // set image tile filling - $source = new Decoder; - $tile = $source->init($filling); - imagesettile($image->getCore(), $tile->getCore()); - $filling = IMG_COLOR_TILED; - - } catch (\Intervention\Image\Exception\NotReadableException $e) { - - // set solid color filling - $color = new Color($filling); - $filling = $color->getInt(); - } - - imagealphablending($resource, true); - - if (is_int($x) && is_int($y)) { - - // resource should be visible through transparency - $base = $image->getDriver()->newImage($width, $height)->getCore(); - imagecopy($base, $resource, 0, 0, 0, 0, $width, $height); - - // floodfill if exact position is defined - imagefill($resource, $x, $y, $filling); - - // copy filled original over base - imagecopy($base, $resource, 0, 0, 0, 0, $width, $height); - - // set base as new resource-core - $image->setCore($base); - imagedestroy($resource); - - } else { - // fill whole image otherwise - imagefilledrectangle($resource, 0, 0, $width - 1, $height - 1, $filling); - } - - isset($tile) ? imagedestroy($tile->getCore()) : null; - - return true; - } -} diff --git a/src/Intervention/Image/Gd/Commands/FitCommand.php b/src/Intervention/Image/Gd/Commands/FitCommand.php deleted file mode 100644 index d861ad94..00000000 --- a/src/Intervention/Image/Gd/Commands/FitCommand.php +++ /dev/null @@ -1,32 +0,0 @@ -argument(0)->type('digit')->required()->value(); - $height = $this->argument(1)->type('digit')->value($width); - $constraints = $this->argument(2)->type('closure')->value(); - $position = $this->argument(3)->type('string')->value('center'); - - // calculate size - $cropped = $image->getSize()->fit(new Size($width, $height), $position); - $resized = clone $cropped; - $resized = $resized->resize($width, $height, $constraints); - - // modify image - $this->modify($image, 0, 0, $cropped->pivot->x, $cropped->pivot->y, $resized->getWidth(), $resized->getHeight(), $cropped->getWidth(), $cropped->getHeight()); - - return true; - } -} diff --git a/src/Intervention/Image/Gd/Commands/FlipCommand.php b/src/Intervention/Image/Gd/Commands/FlipCommand.php deleted file mode 100644 index aa8f230e..00000000 --- a/src/Intervention/Image/Gd/Commands/FlipCommand.php +++ /dev/null @@ -1,37 +0,0 @@ -argument(0)->value('h'); - - $size = $image->getSize(); - $dst = clone $size; - - switch (strtolower($mode)) { - case 2: - case 'v': - case 'vert': - case 'vertical': - $size->pivot->y = $size->height - 1; - $size->height = $size->height * (-1); - break; - - default: - $size->pivot->x = $size->width - 1; - $size->width = $size->width * (-1); - break; - } - - return $this->modify($image, 0, 0, $size->pivot->x, $size->pivot->y, $dst->width, $dst->height, $size->width, $size->height); - } -} diff --git a/src/Intervention/Image/Gd/Commands/GammaCommand.php b/src/Intervention/Image/Gd/Commands/GammaCommand.php deleted file mode 100644 index 7de0fb8a..00000000 --- a/src/Intervention/Image/Gd/Commands/GammaCommand.php +++ /dev/null @@ -1,21 +0,0 @@ -argument(0)->type('numeric')->required()->value(); - - return imagegammacorrect($image->getCore(), 1, $gamma); - } -} diff --git a/src/Intervention/Image/Gd/Commands/GetSizeCommand.php b/src/Intervention/Image/Gd/Commands/GetSizeCommand.php deleted file mode 100644 index 9eb7e209..00000000 --- a/src/Intervention/Image/Gd/Commands/GetSizeCommand.php +++ /dev/null @@ -1,25 +0,0 @@ -setOutput(new Size( - imagesx($image->getCore()), - imagesy($image->getCore()) - )); - - return true; - } -} diff --git a/src/Intervention/Image/Gd/Commands/GreyscaleCommand.php b/src/Intervention/Image/Gd/Commands/GreyscaleCommand.php deleted file mode 100644 index 12921e79..00000000 --- a/src/Intervention/Image/Gd/Commands/GreyscaleCommand.php +++ /dev/null @@ -1,19 +0,0 @@ -getCore(), IMG_FILTER_GRAYSCALE); - } -} diff --git a/src/Intervention/Image/Gd/Commands/HeightenCommand.php b/src/Intervention/Image/Gd/Commands/HeightenCommand.php deleted file mode 100644 index d31e9cde..00000000 --- a/src/Intervention/Image/Gd/Commands/HeightenCommand.php +++ /dev/null @@ -1,28 +0,0 @@ -argument(0)->type('digit')->required()->value(); - $additionalConstraints = $this->argument(1)->type('closure')->value(); - - $this->arguments[0] = null; - $this->arguments[1] = $height; - $this->arguments[2] = function ($constraint) use ($additionalConstraints) { - $constraint->aspectRatio(); - if(is_callable($additionalConstraints)) - $additionalConstraints($constraint); - }; - - return parent::execute($image); - } -} diff --git a/src/Intervention/Image/Gd/Commands/InsertCommand.php b/src/Intervention/Image/Gd/Commands/InsertCommand.php deleted file mode 100644 index 47c77039..00000000 --- a/src/Intervention/Image/Gd/Commands/InsertCommand.php +++ /dev/null @@ -1,34 +0,0 @@ -argument(0)->required()->value(); - $position = $this->argument(1)->type('string')->value(); - $x = $this->argument(2)->type('digit')->value(0); - $y = $this->argument(3)->type('digit')->value(0); - - // build watermark - $watermark = $image->getDriver()->init($source); - - // define insertion point - $image_size = $image->getSize()->align($position, $x, $y); - $watermark_size = $watermark->getSize()->align($position); - $target = $image_size->relativePosition($watermark_size); - - // insert image at position - imagealphablending($image->getCore(), true); - return imagecopy($image->getCore(), $watermark->getCore(), $target->x, $target->y, 0, 0, $watermark_size->width, $watermark_size->height); - } -} diff --git a/src/Intervention/Image/Gd/Commands/InterlaceCommand.php b/src/Intervention/Image/Gd/Commands/InterlaceCommand.php deleted file mode 100644 index e3461cb0..00000000 --- a/src/Intervention/Image/Gd/Commands/InterlaceCommand.php +++ /dev/null @@ -1,23 +0,0 @@ -argument(0)->type('bool')->value(true); - - imageinterlace($image->getCore(), $mode); - - return true; - } -} diff --git a/src/Intervention/Image/Gd/Commands/InvertCommand.php b/src/Intervention/Image/Gd/Commands/InvertCommand.php deleted file mode 100644 index 1a514f1d..00000000 --- a/src/Intervention/Image/Gd/Commands/InvertCommand.php +++ /dev/null @@ -1,19 +0,0 @@ -getCore(), IMG_FILTER_NEGATE); - } -} diff --git a/src/Intervention/Image/Gd/Commands/LimitColorsCommand.php b/src/Intervention/Image/Gd/Commands/LimitColorsCommand.php deleted file mode 100644 index 0baed7e9..00000000 --- a/src/Intervention/Image/Gd/Commands/LimitColorsCommand.php +++ /dev/null @@ -1,53 +0,0 @@ -argument(0)->value(); - $matte = $this->argument(1)->value(); - - // get current image size - $size = $image->getSize(); - - // create empty canvas - $resource = imagecreatetruecolor($size->width, $size->height); - - // define matte - if (is_null($matte)) { - $matte = imagecolorallocatealpha($resource, 255, 255, 255, 127); - } else { - $matte = $image->getDriver()->parseColor($matte)->getInt(); - } - - // fill with matte and copy original image - imagefill($resource, 0, 0, $matte); - - // set transparency - imagecolortransparent($resource, $matte); - - // copy original image - imagecopy($resource, $image->getCore(), 0, 0, 0, 0, $size->width, $size->height); - - if (is_numeric($count) && $count <= 256) { - // decrease colors - imagetruecolortopalette($resource, true, $count); - } - - // set new resource - $image->setCore($resource); - - return true; - } -} diff --git a/src/Intervention/Image/Gd/Commands/MaskCommand.php b/src/Intervention/Image/Gd/Commands/MaskCommand.php deleted file mode 100644 index 4c61429b..00000000 --- a/src/Intervention/Image/Gd/Commands/MaskCommand.php +++ /dev/null @@ -1,83 +0,0 @@ -argument(0)->value(); - $mask_w_alpha = $this->argument(1)->type('bool')->value(false); - - $image_size = $image->getSize(); - - // create empty canvas - $canvas = $image->getDriver()->newImage($image_size->width, $image_size->height, [0,0,0,0]); - - // build mask image from source - $mask = $image->getDriver()->init($mask_source); - $mask_size = $mask->getSize(); - - // resize mask to size of current image (if necessary) - if ($mask_size != $image_size) { - $mask->resize($image_size->width, $image_size->height); - } - - imagealphablending($canvas->getCore(), false); - - if ( ! $mask_w_alpha) { - // mask from greyscale image - imagefilter($mask->getCore(), IMG_FILTER_GRAYSCALE); - } - - // redraw old image pixel by pixel considering alpha map - for ($x=0; $x < $image_size->width; $x++) { - for ($y=0; $y < $image_size->height; $y++) { - - $color = $image->pickColor($x, $y, 'array'); - $alpha = $mask->pickColor($x, $y, 'array'); - - if ($mask_w_alpha) { - $alpha = $alpha[3]; // use alpha channel as mask - } else { - - if ($alpha[3] == 0) { // transparent as black - $alpha = 0; - } else { - - // $alpha = floatval(round((($alpha[0] + $alpha[1] + $alpha[3]) / 3) / 255, 2)); - - // image is greyscale, so channel doesn't matter (use red channel) - $alpha = floatval(round($alpha[0] / 255, 2)); - } - - } - - // preserve alpha of original image... - if ($color[3] < $alpha) { - $alpha = $color[3]; - } - - // replace alpha value - $color[3] = $alpha; - - // redraw pixel - $canvas->pixel($color, $x, $y); - } - } - - - // replace current image with masked instance - $image->setCore($canvas->getCore()); - - return true; - } -} diff --git a/src/Intervention/Image/Gd/Commands/OpacityCommand.php b/src/Intervention/Image/Gd/Commands/OpacityCommand.php deleted file mode 100644 index 48492d24..00000000 --- a/src/Intervention/Image/Gd/Commands/OpacityCommand.php +++ /dev/null @@ -1,31 +0,0 @@ -argument(0)->between(0, 100)->required()->value(); - - // get size of image - $size = $image->getSize(); - - // build temp alpha mask - $mask_color = sprintf('rgba(0, 0, 0, %.1F)', $transparency / 100); - $mask = $image->getDriver()->newImage($size->width, $size->height, $mask_color); - - // mask image - $image->mask($mask->getCore(), true); - - return true; - } -} diff --git a/src/Intervention/Image/Gd/Commands/PickColorCommand.php b/src/Intervention/Image/Gd/Commands/PickColorCommand.php deleted file mode 100644 index bad96f49..00000000 --- a/src/Intervention/Image/Gd/Commands/PickColorCommand.php +++ /dev/null @@ -1,37 +0,0 @@ -argument(0)->type('digit')->required()->value(); - $y = $this->argument(1)->type('digit')->required()->value(); - $format = $this->argument(2)->type('string')->value('array'); - - // pick color - $color = imagecolorat($image->getCore(), $x, $y); - - if ( ! imageistruecolor($image->getCore())) { - $color = imagecolorsforindex($image->getCore(), $color); - $color['alpha'] = round(1 - $color['alpha'] / 127, 2); - } - - $color = new Color($color); - - // format to output - $this->setOutput($color->format($format)); - - return true; - } -} diff --git a/src/Intervention/Image/Gd/Commands/PixelCommand.php b/src/Intervention/Image/Gd/Commands/PixelCommand.php deleted file mode 100644 index 2a90ce34..00000000 --- a/src/Intervention/Image/Gd/Commands/PixelCommand.php +++ /dev/null @@ -1,25 +0,0 @@ -argument(0)->required()->value(); - $color = new Color($color); - $x = $this->argument(1)->type('digit')->required()->value(); - $y = $this->argument(2)->type('digit')->required()->value(); - - return imagesetpixel($image->getCore(), $x, $y, $color->getInt()); - } -} diff --git a/src/Intervention/Image/Gd/Commands/PixelateCommand.php b/src/Intervention/Image/Gd/Commands/PixelateCommand.php deleted file mode 100644 index 0934797a..00000000 --- a/src/Intervention/Image/Gd/Commands/PixelateCommand.php +++ /dev/null @@ -1,21 +0,0 @@ -argument(0)->type('digit')->value(10); - - return imagefilter($image->getCore(), IMG_FILTER_PIXELATE, $size, true); - } -} diff --git a/src/Intervention/Image/Gd/Commands/ResetCommand.php b/src/Intervention/Image/Gd/Commands/ResetCommand.php deleted file mode 100644 index a68b75ea..00000000 --- a/src/Intervention/Image/Gd/Commands/ResetCommand.php +++ /dev/null @@ -1,39 +0,0 @@ -argument(0)->value(); - $backup = $image->getBackup($backupName); - - if (is_resource($backup) || $backup instanceof \GdImage) { - - // destroy current resource - imagedestroy($image->getCore()); - - // clone backup - $backup = $image->getDriver()->cloneCore($backup); - - // reset to new resource - $image->setCore($backup); - - return true; - } - - throw new RuntimeException( - "Backup not available. Call backup() before reset()." - ); - } -} diff --git a/src/Intervention/Image/Gd/Commands/ResizeCanvasCommand.php b/src/Intervention/Image/Gd/Commands/ResizeCanvasCommand.php deleted file mode 100644 index 73f3df30..00000000 --- a/src/Intervention/Image/Gd/Commands/ResizeCanvasCommand.php +++ /dev/null @@ -1,83 +0,0 @@ -argument(0)->type('digit')->required()->value(); - $height = $this->argument(1)->type('digit')->required()->value(); - $anchor = $this->argument(2)->value('center'); - $relative = $this->argument(3)->type('boolean')->value(false); - $bgcolor = $this->argument(4)->value(); - - $original_width = $image->getWidth(); - $original_height = $image->getHeight(); - - // check of only width or height is set - $width = is_null($width) ? $original_width : intval($width); - $height = is_null($height) ? $original_height : intval($height); - - // check on relative width/height - if ($relative) { - $width = $original_width + $width; - $height = $original_height + $height; - } - - // check for negative width/height - $width = ($width <= 0) ? $width + $original_width : $width; - $height = ($height <= 0) ? $height + $original_height : $height; - - // create new canvas - $canvas = $image->getDriver()->newImage($width, $height, $bgcolor); - - // set copy position - $canvas_size = $canvas->getSize()->align($anchor); - $image_size = $image->getSize()->align($anchor); - $canvas_pos = $image_size->relativePosition($canvas_size); - $image_pos = $canvas_size->relativePosition($image_size); - - if ($width <= $original_width) { - $dst_x = 0; - $src_x = $canvas_pos->x; - $src_w = $canvas_size->width; - } else { - $dst_x = $image_pos->x; - $src_x = 0; - $src_w = $original_width; - } - - if ($height <= $original_height) { - $dst_y = 0; - $src_y = $canvas_pos->y; - $src_h = $canvas_size->height; - } else { - $dst_y = $image_pos->y; - $src_y = 0; - $src_h = $original_height; - } - - // make image area transparent to keep transparency - // even if background-color is set - $transparent = imagecolorallocatealpha($canvas->getCore(), 255, 255, 255, 127); - imagealphablending($canvas->getCore(), false); // do not blend / just overwrite - imagefilledrectangle($canvas->getCore(), $dst_x, $dst_y, $dst_x + $src_w - 1, $dst_y + $src_h - 1, $transparent); - - // copy image into new canvas - imagecopy($canvas->getCore(), $image->getCore(), $dst_x, $dst_y, $src_x, $src_y, $src_w, $src_h); - - // set new core to canvas - $image->setCore($canvas->getCore()); - - return true; - } -} diff --git a/src/Intervention/Image/Gd/Commands/ResizeCommand.php b/src/Intervention/Image/Gd/Commands/ResizeCommand.php deleted file mode 100644 index 382d9709..00000000 --- a/src/Intervention/Image/Gd/Commands/ResizeCommand.php +++ /dev/null @@ -1,84 +0,0 @@ -argument(0)->value(); - $height = $this->argument(1)->value(); - $constraints = $this->argument(2)->type('closure')->value(); - - // resize box - $resized = $image->getSize()->resize($width, $height, $constraints); - - // modify image - $this->modify($image, 0, 0, 0, 0, $resized->getWidth(), $resized->getHeight(), $image->getWidth(), $image->getHeight()); - - return true; - } - - /** - * Wrapper function for 'imagecopyresampled' - * - * @param Image $image - * @param int $dst_x - * @param int $dst_y - * @param int $src_x - * @param int $src_y - * @param int $dst_w - * @param int $dst_h - * @param int $src_w - * @param int $src_h - * @return boolean - */ - protected function modify($image, $dst_x, $dst_y, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h) - { - // create new image - $modified = imagecreatetruecolor($dst_w, $dst_h); - - // get current image - $resource = $image->getCore(); - - // preserve transparency - $transIndex = imagecolortransparent($resource); - - if ($transIndex != -1) { - $rgba = imagecolorsforindex($modified, $transIndex); - $transColor = imagecolorallocatealpha($modified, $rgba['red'], $rgba['green'], $rgba['blue'], 127); - imagefill($modified, 0, 0, $transColor); - imagecolortransparent($modified, $transColor); - } else { - imagealphablending($modified, false); - imagesavealpha($modified, true); - } - - // copy content from resource - $result = imagecopyresampled( - $modified, - $resource, - $dst_x, - $dst_y, - $src_x, - $src_y, - $dst_w, - $dst_h, - $src_w, - $src_h - ); - - // set new content as recource - $image->setCore($modified); - - return $result; - } -} diff --git a/src/Intervention/Image/Gd/Commands/RotateCommand.php b/src/Intervention/Image/Gd/Commands/RotateCommand.php deleted file mode 100644 index 682ec0d4..00000000 --- a/src/Intervention/Image/Gd/Commands/RotateCommand.php +++ /dev/null @@ -1,30 +0,0 @@ -argument(0)->type('numeric')->required()->value(); - $color = $this->argument(1)->value(); - $color = new Color($color); - - // restrict rotations beyond 360 degrees, since the end result is the same - $angle = fmod($angle, 360); - - // rotate image - $image->setCore(imagerotate($image->getCore(), $angle, $color->getInt())); - - return true; - } -} diff --git a/src/Intervention/Image/Gd/Commands/SharpenCommand.php b/src/Intervention/Image/Gd/Commands/SharpenCommand.php deleted file mode 100644 index 782e5650..00000000 --- a/src/Intervention/Image/Gd/Commands/SharpenCommand.php +++ /dev/null @@ -1,34 +0,0 @@ -argument(0)->between(0, 100)->value(10); - - // build matrix - $min = $amount >= 10 ? $amount * -0.01 : 0; - $max = $amount * -0.025; - $abs = ((4 * $min + 4 * $max) * -1) + 1; - $div = 1; - - $matrix = [ - [$min, $max, $min], - [$max, $abs, $max], - [$min, $max, $min] - ]; - - // apply the matrix - return imageconvolution($image->getCore(), $matrix, $div, 0); - } -} diff --git a/src/Intervention/Image/Gd/Commands/TrimCommand.php b/src/Intervention/Image/Gd/Commands/TrimCommand.php deleted file mode 100644 index 2e369752..00000000 --- a/src/Intervention/Image/Gd/Commands/TrimCommand.php +++ /dev/null @@ -1,176 +0,0 @@ -argument(0)->type('string')->value(); - $away = $this->argument(1)->value(); - $tolerance = $this->argument(2)->type('numeric')->value(0); - $feather = $this->argument(3)->type('numeric')->value(0); - - $width = $image->getWidth(); - $height = $image->getHeight(); - - // default values - $checkTransparency = false; - - // define borders to trim away - if (is_null($away)) { - $away = ['top', 'right', 'bottom', 'left']; - } elseif (is_string($away)) { - $away = [$away]; - } - - // lower border names - foreach ($away as $key => $value) { - $away[$key] = strtolower($value); - } - - // define base color position - switch (strtolower($base)) { - case 'transparent': - case 'trans': - $checkTransparency = true; - $base_x = 0; - $base_y = 0; - break; - - case 'bottom-right': - case 'right-bottom': - $base_x = $width - 1; - $base_y = $height - 1; - break; - - default: - case 'top-left': - case 'left-top': - $base_x = 0; - $base_y = 0; - break; - } - - // pick base color - if ($checkTransparency) { - $color = new Color; // color will only be used to compare alpha channel - } else { - $color = $image->pickColor($base_x, $base_y, 'object'); - } - - $top_x = 0; - $top_y = 0; - $bottom_x = $width; - $bottom_y = $height; - - // search upper part of image for colors to trim away - if (in_array('top', $away)) { - - for ($y=0; $y < ceil($height/2); $y++) { - for ($x=0; $x < $width; $x++) { - - $checkColor = $image->pickColor($x, $y, 'object'); - - if ($checkTransparency) { - $checkColor->r = $color->r; - $checkColor->g = $color->g; - $checkColor->b = $color->b; - } - - if ($color->differs($checkColor, $tolerance)) { - $top_y = max(0, $y - $feather); - break 2; - } - - } - } - - } - - // search left part of image for colors to trim away - if (in_array('left', $away)) { - - for ($x=0; $x < ceil($width/2); $x++) { - for ($y=$top_y; $y < $height; $y++) { - - $checkColor = $image->pickColor($x, $y, 'object'); - - if ($checkTransparency) { - $checkColor->r = $color->r; - $checkColor->g = $color->g; - $checkColor->b = $color->b; - } - - if ($color->differs($checkColor, $tolerance)) { - $top_x = max(0, $x - $feather); - break 2; - } - - } - } - - } - - // search lower part of image for colors to trim away - if (in_array('bottom', $away)) { - - for ($y=($height-1); $y >= floor($height/2)-1; $y--) { - for ($x=$top_x; $x < $width; $x++) { - - $checkColor = $image->pickColor($x, $y, 'object'); - - if ($checkTransparency) { - $checkColor->r = $color->r; - $checkColor->g = $color->g; - $checkColor->b = $color->b; - } - - if ($color->differs($checkColor, $tolerance)) { - $bottom_y = min($height, $y+1 + $feather); - break 2; - } - - } - } - - } - - // search right part of image for colors to trim away - if (in_array('right', $away)) { - - for ($x=($width-1); $x >= floor($width/2)-1; $x--) { - for ($y=$top_y; $y < $bottom_y; $y++) { - - $checkColor = $image->pickColor($x, $y, 'object'); - - if ($checkTransparency) { - $checkColor->r = $color->r; - $checkColor->g = $color->g; - $checkColor->b = $color->b; - } - - if ($color->differs($checkColor, $tolerance)) { - $bottom_x = min($width, $x+1 + $feather); - break 2; - } - - } - } - - } - - - // trim parts of image - return $this->modify($image, 0, 0, $top_x, $top_y, ($bottom_x-$top_x), ($bottom_y-$top_y), ($bottom_x-$top_x), ($bottom_y-$top_y)); - - } -} diff --git a/src/Intervention/Image/Gd/Commands/WidenCommand.php b/src/Intervention/Image/Gd/Commands/WidenCommand.php deleted file mode 100644 index 43000d5d..00000000 --- a/src/Intervention/Image/Gd/Commands/WidenCommand.php +++ /dev/null @@ -1,28 +0,0 @@ -argument(0)->type('digit')->required()->value(); - $additionalConstraints = $this->argument(1)->type('closure')->value(); - - $this->arguments[0] = $width; - $this->arguments[1] = null; - $this->arguments[2] = function ($constraint) use ($additionalConstraints) { - $constraint->aspectRatio(); - if(is_callable($additionalConstraints)) - $additionalConstraints($constraint); - }; - - return parent::execute($image); - } -} diff --git a/src/Intervention/Image/Gd/Decoder.php b/src/Intervention/Image/Gd/Decoder.php deleted file mode 100644 index f5c34aa5..00000000 --- a/src/Intervention/Image/Gd/Decoder.php +++ /dev/null @@ -1,171 +0,0 @@ -gdResourceToTruecolor($core); - - // build image - $image = $this->initFromGdResource($core); - $image->mime = $mime; - $image->setFileInfoFromPath($path); - - return $image; - } - - /** - * Initiates new image from GD resource - * - * @param Resource $resource - * @return \Intervention\Image\Image - */ - public function initFromGdResource($resource) - { - return new Image(new Driver, $resource); - } - - /** - * Initiates new image from Imagick object - * - * @param Imagick $object - * @return \Intervention\Image\Image - */ - public function initFromImagick(\Imagick $object) - { - throw new NotSupportedException( - "Gd driver is unable to init from Imagick object." - ); - } - - /** - * Initiates new image from binary data - * - * @param string $data - * @return \Intervention\Image\Image - */ - public function initFromBinary($binary) - { - $resource = @imagecreatefromstring($binary); - - if ($resource === false) { - throw new NotReadableException( - "Unable to init from given binary data." - ); - } - - $image = $this->initFromGdResource($resource); - $image->mime = finfo_buffer(finfo_open(FILEINFO_MIME_TYPE), $binary); - - return $image; - } - - /** - * Transform GD resource into Truecolor version - * - * @param resource $resource - * @return bool - */ - public function gdResourceToTruecolor(&$resource) - { - $width = imagesx($resource); - $height = imagesy($resource); - - // new canvas - $canvas = imagecreatetruecolor($width, $height); - - // fill with transparent color - imagealphablending($canvas, false); - $transparent = imagecolorallocatealpha($canvas, 255, 255, 255, 127); - imagefilledrectangle($canvas, 0, 0, $width, $height, $transparent); - imagecolortransparent($canvas, $transparent); - imagealphablending($canvas, true); - - // copy original - imagecopy($canvas, $resource, 0, 0, 0, 0, $width, $height); - imagedestroy($resource); - - $resource = $canvas; - - return true; - } -} diff --git a/src/Intervention/Image/Gd/Driver.php b/src/Intervention/Image/Gd/Driver.php deleted file mode 100644 index 5f2f23ea..00000000 --- a/src/Intervention/Image/Gd/Driver.php +++ /dev/null @@ -1,89 +0,0 @@ -coreAvailable()) { - throw new NotSupportedException( - "GD Library extension not available with this PHP installation." - ); - } - - $this->decoder = $decoder ? $decoder : new Decoder; - $this->encoder = $encoder ? $encoder : new Encoder; - } - - /** - * Creates new image instance - * - * @param int $width - * @param int $height - * @param mixed $background - * @return \Intervention\Image\Image - */ - public function newImage($width, $height, $background = null) - { - // create empty resource - $core = imagecreatetruecolor($width, $height); - $image = new Image(new static, $core); - - // set background color - $background = new Color($background); - imagefill($image->getCore(), 0, 0, $background->getInt()); - - return $image; - } - - /** - * Reads given string into color object - * - * @param string $value - * @return AbstractColor - */ - public function parseColor($value) - { - return new Color($value); - } - - /** - * Checks if core module installation is available - * - * @return boolean - */ - protected function coreAvailable() - { - return (extension_loaded('gd') && function_exists('gd_info')); - } - - /** - * Returns clone of given core - * - * @return mixed - */ - public function cloneCore($core) - { - $width = imagesx($core); - $height = imagesy($core); - $clone = imagecreatetruecolor($width, $height); - imagealphablending($clone, false); - imagesavealpha($clone, true); - $transparency = imagecolorallocatealpha($clone, 0, 0, 0, 127); - imagefill($clone, 0, 0, $transparency); - - imagecopy($clone, $core, 0, 0, 0, 0, $width, $height); - - return $clone; - } -} diff --git a/src/Intervention/Image/Gd/Encoder.php b/src/Intervention/Image/Gd/Encoder.php deleted file mode 100644 index 559d60dc..00000000 --- a/src/Intervention/Image/Gd/Encoder.php +++ /dev/null @@ -1,180 +0,0 @@ -image->getCore(), null, $this->quality); - $this->image->mime = image_type_to_mime_type(IMAGETYPE_JPEG); - $buffer = ob_get_contents(); - ob_end_clean(); - - return $buffer; - } - - /** - * Processes and returns encoded image as PNG string - * - * @return string - */ - protected function processPng() - { - ob_start(); - $resource = $this->image->getCore(); - imagealphablending($resource, false); - imagesavealpha($resource, true); - imagepng($resource, null, -1); - $this->image->mime = image_type_to_mime_type(IMAGETYPE_PNG); - $buffer = ob_get_contents(); - ob_end_clean(); - - return $buffer; - } - - /** - * Processes and returns encoded image as GIF string - * - * @return string - */ - protected function processGif() - { - ob_start(); - imagegif($this->image->getCore()); - $this->image->mime = image_type_to_mime_type(IMAGETYPE_GIF); - $buffer = ob_get_contents(); - ob_end_clean(); - - return $buffer; - } - - /** - * Processes and returns encoded image as WEBP string - * - * @return string - */ - protected function processWebp() - { - if ( ! function_exists('imagewebp')) { - throw new NotSupportedException( - "Webp format is not supported by PHP installation." - ); - } - - ob_start(); - imagepalettetotruecolor($this->image->getCore()); - imagealphablending($this->image->getCore(), true); - imagesavealpha($this->image->getCore(), true); - imagewebp($this->image->getCore(), null, $this->quality); - $this->image->mime = defined('IMAGETYPE_WEBP') ? image_type_to_mime_type(IMAGETYPE_WEBP) : 'image/webp'; - $buffer = ob_get_contents(); - ob_end_clean(); - - return $buffer; - } - - /** - * Processes and returns encoded image as TIFF string - * - * @return string - */ - protected function processTiff() - { - throw new NotSupportedException( - "TIFF format is not supported by Gd Driver." - ); - } - - /** - * Processes and returns encoded image as BMP string - * - * @return string - */ - protected function processBmp() - { - if ( ! function_exists('imagebmp')) { - throw new NotSupportedException( - "BMP format is not supported by PHP installation." - ); - } - - ob_start(); - imagebmp($this->image->getCore()); - $this->image->mime = defined('IMAGETYPE_BMP') ? image_type_to_mime_type(IMAGETYPE_BMP) : 'image/bmp'; - $buffer = ob_get_contents(); - ob_end_clean(); - - return $buffer; - } - - /** - * Processes and returns encoded image as ICO string - * - * @return string - */ - protected function processIco() - { - throw new NotSupportedException( - "ICO format is not supported by Gd Driver." - ); - } - - /** - * Processes and returns encoded image as PSD string - * - * @return string - */ - protected function processPsd() - { - throw new NotSupportedException( - "PSD format is not supported by Gd Driver." - ); - } - - /** - * Processes and returns encoded image as AVIF string - * - * @return string - */ - protected function processAvif() - { - if ( ! function_exists('imageavif')) { - throw new NotSupportedException( - "AVIF format is not supported by PHP installation." - ); - } - - ob_start(); - $resource = $this->image->getCore(); - imagepalettetotruecolor($resource); - imagealphablending($resource, true); - imagesavealpha($resource, true); - imageavif($resource, null, $this->quality); - $this->image->mime = defined('IMAGETYPE_AVIF') ? image_type_to_mime_type(IMAGETYPE_AVIF) : 'image/avif'; - $buffer = ob_get_contents(); - ob_end_clean(); - - return $buffer; - } - - /** - * Processes and returns encoded image as HEIC string - * - * @return string - */ - protected function processHeic() - { - throw new NotSupportedException( - "HEIC format is not supported by Gd Driver." - ); - } -} diff --git a/src/Intervention/Image/Gd/Font.php b/src/Intervention/Image/Gd/Font.php deleted file mode 100644 index 3f588878..00000000 --- a/src/Intervention/Image/Gd/Font.php +++ /dev/null @@ -1,277 +0,0 @@ -size * 0.75)); - } - - /** - * Filter function to access internal integer font values - * - * @return int - */ - private function getInternalFont() - { - $internalfont = is_null($this->file) ? 1 : $this->file; - $internalfont = is_numeric($internalfont) ? $internalfont : false; - - if ( ! in_array($internalfont, [1, 2, 3, 4, 5])) { - throw new NotSupportedException( - sprintf('Internal GD font (%s) not available. Use only 1-5.', $internalfont) - ); - } - - return intval($internalfont); - } - - /** - * Get width of an internal font character - * - * @return int - */ - private function getInternalFontWidth() - { - return $this->getInternalFont() + 4; - } - - /** - * Get height of an internal font character - * - * @return int - */ - private function getInternalFontHeight() - { - switch ($this->getInternalFont()) { - case 1: - return 8; - - case 2: - return 14; - - case 3: - return 14; - - case 4: - return 16; - - case 5: - return 16; - } - } - - /** - * Calculates bounding box of current font setting - * - * @return Array - */ - public function getBoxSize() - { - $box = []; - - if ($this->hasApplicableFontFile()) { - - // imagettfbbox() converts numeric entities to their respective - // character. Preserve any originally double encoded entities to be - // represented as is. - // eg: &#160; will render   rather than its character. - $this->text = preg_replace('/&(#(?:x[a-fA-F0-9]+|[0-9]+);)/', '&\1', $this->text); - $this->text = mb_encode_numericentity($this->text, array(0x0080, 0xffff, 0, 0xffff), 'UTF-8'); - - // get bounding box with angle 0 - $box = imagettfbbox($this->getPointSize(), 0, $this->file, $this->text); - - // rotate points manually - if ($this->angle != 0) { - - $angle = pi() * 2 - $this->angle * pi() * 2 / 360; - - for ($i=0; $i<4; $i++) { - $x = $box[$i * 2]; - $y = $box[$i * 2 + 1]; - $box[$i * 2] = cos($angle) * $x - sin($angle) * $y; - $box[$i * 2 + 1] = sin($angle) * $x + cos($angle) * $y; - } - } - - $box['width'] = intval(abs($box[4] - $box[0])); - $box['height'] = intval(abs($box[5] - $box[1])); - - } else { - - // get current internal font size - $width = $this->getInternalFontWidth(); - $height = $this->getInternalFontHeight(); - - if (strlen($this->text) == 0) { - // no text -> no boxsize - $box['width'] = 0; - $box['height'] = 0; - } else { - // calculate boxsize - $box['width'] = strlen($this->text) * $width; - $box['height'] = $height; - } - } - - return $box; - } - - /** - * Draws font to given image at given position - * - * @param Image $image - * @param int $posx - * @param int $posy - * @return void - */ - public function applyToImage(Image $image, $posx = 0, $posy = 0) - { - // parse text color - $color = new Color($this->color); - - if ($this->hasApplicableFontFile()) { - - if ($this->angle != 0 || is_string($this->align) || is_string($this->valign)) { - - $box = $this->getBoxSize(); - - $align = is_null($this->align) ? 'left' : strtolower($this->align); - $valign = is_null($this->valign) ? 'bottom' : strtolower($this->valign); - - // correction on position depending on v/h alignment - switch ($align.'-'.$valign) { - - case 'center-top': - $posx = $posx - round(($box[6]+$box[4])/2); - $posy = $posy - round(($box[7]+$box[5])/2); - break; - - case 'right-top': - $posx = $posx - $box[4]; - $posy = $posy - $box[5]; - break; - - case 'left-top': - $posx = $posx - $box[6]; - $posy = $posy - $box[7]; - break; - - case 'center-center': - case 'center-middle': - $posx = $posx - round(($box[0]+$box[4])/2); - $posy = $posy - round(($box[1]+$box[5])/2); - break; - - case 'right-center': - case 'right-middle': - $posx = $posx - round(($box[2]+$box[4])/2); - $posy = $posy - round(($box[3]+$box[5])/2); - break; - - case 'left-center': - case 'left-middle': - $posx = $posx - round(($box[0]+$box[6])/2); - $posy = $posy - round(($box[1]+$box[7])/2); - break; - - case 'center-bottom': - $posx = $posx - round(($box[0]+$box[2])/2); - $posy = $posy - round(($box[1]+$box[3])/2); - break; - - case 'right-bottom': - $posx = $posx - $box[2]; - $posy = $posy - $box[3]; - break; - - case 'left-bottom': - $posx = $posx - $box[0]; - $posy = $posy - $box[1]; - break; - } - } - - // enable alphablending for imagettftext - imagealphablending($image->getCore(), true); - - // draw ttf text - imagettftext($image->getCore(), $this->getPointSize(), $this->angle, $posx, $posy, $color->getInt(), $this->file, $this->text); - - } else { - - // get box size - $box = $this->getBoxSize(); - $width = $box['width']; - $height = $box['height']; - - // internal font specific position corrections - if ($this->getInternalFont() == 1) { - $top_correction = 1; - $bottom_correction = 2; - } elseif ($this->getInternalFont() == 3) { - $top_correction = 2; - $bottom_correction = 4; - } else { - $top_correction = 3; - $bottom_correction = 4; - } - - // x-position corrections for horizontal alignment - switch (strtolower($this->align)) { - case 'center': - $posx = ceil($posx - ($width / 2)); - break; - - case 'right': - $posx = ceil($posx - $width) + 1; - break; - } - - // y-position corrections for vertical alignment - switch (strtolower($this->valign)) { - case 'center': - case 'middle': - $posy = ceil($posy - ($height / 2)); - break; - - case 'top': - $posy = ceil($posy - $top_correction); - break; - - default: - case 'bottom': - $posy = round($posy - $height + $bottom_correction); - break; - } - - // draw text - imagestring($image->getCore(), $this->getInternalFont(), $posx, $posy, $this->text, $color->getInt()); - } - } - - /** - * Set text kerning - * - * @param string $kerning - * @return void - */ - public function kerning($kerning) - { - throw new \Intervention\Image\Exception\NotSupportedException( - "Kerning is not supported by GD driver." - ); - } - -} diff --git a/src/Intervention/Image/Gd/Shapes/CircleShape.php b/src/Intervention/Image/Gd/Shapes/CircleShape.php deleted file mode 100644 index c3c42144..00000000 --- a/src/Intervention/Image/Gd/Shapes/CircleShape.php +++ /dev/null @@ -1,40 +0,0 @@ -width = is_numeric($diameter) ? intval($diameter) : $this->diameter; - $this->height = is_numeric($diameter) ? intval($diameter) : $this->diameter; - $this->diameter = is_numeric($diameter) ? intval($diameter) : $this->diameter; - } - - /** - * Draw current circle on given image - * - * @param Image $image - * @param int $x - * @param int $y - * @return boolean - */ - public function applyToImage(Image $image, $x = 0, $y = 0) - { - return parent::applyToImage($image, $x, $y); - } -} diff --git a/src/Intervention/Image/Gd/Shapes/EllipseShape.php b/src/Intervention/Image/Gd/Shapes/EllipseShape.php deleted file mode 100644 index 78e5e4a5..00000000 --- a/src/Intervention/Image/Gd/Shapes/EllipseShape.php +++ /dev/null @@ -1,65 +0,0 @@ -width = is_numeric($width) ? intval($width) : $this->width; - $this->height = is_numeric($height) ? intval($height) : $this->height; - } - - /** - * Draw ellipse instance on given image - * - * @param Image $image - * @param int $x - * @param int $y - * @return boolean - */ - public function applyToImage(Image $image, $x = 0, $y = 0) - { - // parse background color - $background = new Color($this->background); - - if ($this->hasBorder()) { - // slightly smaller ellipse to keep 1px bordered edges clean - imagefilledellipse($image->getCore(), $x, $y, $this->width-1, $this->height-1, $background->getInt()); - - $border_color = new Color($this->border_color); - imagesetthickness($image->getCore(), $this->border_width); - - // gd's imageellipse doesn't respect imagesetthickness so i use imagearc with 359.9 degrees here - imagearc($image->getCore(), $x, $y, $this->width, $this->height, 0, 359.99, $border_color->getInt()); - } else { - imagefilledellipse($image->getCore(), $x, $y, $this->width, $this->height, $background->getInt()); - } - - return true; - } -} diff --git a/src/Intervention/Image/Gd/Shapes/LineShape.php b/src/Intervention/Image/Gd/Shapes/LineShape.php deleted file mode 100644 index ea38b513..00000000 --- a/src/Intervention/Image/Gd/Shapes/LineShape.php +++ /dev/null @@ -1,90 +0,0 @@ -x = is_numeric($x) ? intval($x) : $this->x; - $this->y = is_numeric($y) ? intval($y) : $this->y; - } - - /** - * Set current line color - * - * @param string $color - * @return void - */ - public function color($color) - { - $this->color = $color; - } - - /** - * Set current line width in pixels - * - * @param int $width - * @return void - */ - public function width($width) - { - throw new \Intervention\Image\Exception\NotSupportedException( - "Line width is not supported by GD driver." - ); - } - - /** - * Draw current instance of line to given endpoint on given image - * - * @param Image $image - * @param int $x - * @param int $y - * @return boolean - */ - public function applyToImage(Image $image, $x = 0, $y = 0) - { - $color = new Color($this->color); - imageline($image->getCore(), $x, $y, $this->x, $this->y, $color->getInt()); - - return true; - } -} diff --git a/src/Intervention/Image/Gd/Shapes/PolygonShape.php b/src/Intervention/Image/Gd/Shapes/PolygonShape.php deleted file mode 100644 index 5e14df40..00000000 --- a/src/Intervention/Image/Gd/Shapes/PolygonShape.php +++ /dev/null @@ -1,49 +0,0 @@ -points = $points; - } - - /** - * Draw polygon on given image - * - * @param Image $image - * @param int $x - * @param int $y - * @return boolean - */ - public function applyToImage(Image $image, $x = 0, $y = 0) - { - $background = new Color($this->background); - imagefilledpolygon($image->getCore(), $this->points, intval(count($this->points) / 2), $background->getInt()); - - if ($this->hasBorder()) { - $border_color = new Color($this->border_color); - imagesetthickness($image->getCore(), $this->border_width); - imagepolygon($image->getCore(), $this->points, intval(count($this->points) / 2), $border_color->getInt()); - } - - return true; - } -} diff --git a/src/Intervention/Image/Gd/Shapes/RectangleShape.php b/src/Intervention/Image/Gd/Shapes/RectangleShape.php deleted file mode 100644 index 5f69a7f9..00000000 --- a/src/Intervention/Image/Gd/Shapes/RectangleShape.php +++ /dev/null @@ -1,76 +0,0 @@ -x1 = is_numeric($x1) ? intval($x1) : $this->x1; - $this->y1 = is_numeric($y1) ? intval($y1) : $this->y1; - $this->x2 = is_numeric($x2) ? intval($x2) : $this->x2; - $this->y2 = is_numeric($y2) ? intval($y2) : $this->y2; - } - - /** - * Draw rectangle to given image at certain position - * - * @param Image $image - * @param int $x - * @param int $y - * @return boolean - */ - public function applyToImage(Image $image, $x = 0, $y = 0) - { - $background = new Color($this->background); - imagefilledrectangle($image->getCore(), $this->x1, $this->y1, $this->x2, $this->y2, $background->getInt()); - - if ($this->hasBorder()) { - $border_color = new Color($this->border_color); - imagesetthickness($image->getCore(), $this->border_width); - imagerectangle($image->getCore(), $this->x1, $this->y1, $this->x2, $this->y2, $border_color->getInt()); - } - - return true; - } -} diff --git a/src/Intervention/Image/Image.php b/src/Intervention/Image/Image.php deleted file mode 100644 index 7d5a59b7..00000000 --- a/src/Intervention/Image/Image.php +++ /dev/null @@ -1,370 +0,0 @@ -driver = $driver; - $this->core = $core; - } - - /** - * Magic method to catch all image calls - * usually any AbstractCommand - * - * @param string $name - * @param Array $arguments - * @return mixed - */ - public function __call($name, $arguments) - { - $command = $this->driver->executeCommand($this, $name, $arguments); - return $command->hasOutput() ? $command->getOutput() : $this; - } - - /** - * Starts encoding of current image - * - * @param string $format - * @param int $quality - * @return \Intervention\Image\Image - */ - public function encode($format = null, $quality = 90) - { - return $this->driver->encode($this, $format, $quality); - } - - /** - * Saves encoded image in filesystem - * - * @param string $path - * @param int $quality - * @param string $format - * @return \Intervention\Image\Image - */ - public function save($path = null, $quality = null, $format = null) - { - $path = is_null($path) ? $this->basePath() : $path; - - if (is_null($path)) { - throw new NotWritableException( - "Can't write to undefined path." - ); - } - - if ($format === null) { - $format = pathinfo($path, PATHINFO_EXTENSION); - } - - $data = $this->encode($format, $quality); - $saved = @file_put_contents($path, $data); - - if ($saved === false) { - throw new NotWritableException( - "Can't write image data to path ({$path})" - ); - } - - // set new file info - $this->setFileInfoFromPath($path); - - return $this; - } - - /** - * Runs a given filter on current image - * - * @param FiltersFilterInterface $filter - * @return \Intervention\Image\Image - */ - public function filter(Filters\FilterInterface $filter) - { - return $filter->applyFilter($this); - } - - /** - * Returns current image driver - * - * @return \Intervention\Image\AbstractDriver - */ - public function getDriver() - { - return $this->driver; - } - - /** - * Sets current image driver - * @param AbstractDriver $driver - */ - public function setDriver(AbstractDriver $driver) - { - $this->driver = $driver; - - return $this; - } - - /** - * Returns current image resource/obj - * - * @return mixed - */ - public function getCore() - { - return $this->core; - } - - /** - * Sets current image resource - * - * @param mixed $core - */ - public function setCore($core) - { - $this->core = $core; - - return $this; - } - - /** - * Returns current image backup - * - * @param string $name - * @return mixed - */ - public function getBackup($name = null) - { - $name = is_null($name) ? 'default' : $name; - - if ( ! $this->backupExists($name)) { - throw new RuntimeException( - "Backup with name ({$name}) not available. Call backup() before reset()." - ); - } - - return $this->backups[$name]; - } - - /** - * Returns all backups attached to image - * - * @return array - */ - public function getBackups() - { - return $this->backups; - } - - /** - * Sets current image backup - * - * @param mixed $resource - * @param string $name - * @return self - */ - public function setBackup($resource, $name = null) - { - $name = is_null($name) ? 'default' : $name; - - $this->backups[$name] = $resource; - - return $this; - } - - /** - * Checks if named backup exists - * - * @param string $name - * @return bool - */ - private function backupExists($name) - { - return array_key_exists($name, $this->backups); - } - - /** - * Checks if current image is already encoded - * - * @return boolean - */ - public function isEncoded() - { - return ! empty($this->encoded); - } - - /** - * Returns encoded image data of current image - * - * @return string - */ - public function getEncoded() - { - return $this->encoded; - } - - /** - * Sets encoded image buffer - * - * @param string $value - */ - public function setEncoded($value) - { - $this->encoded = $value; - - return $this; - } - - /** - * Calculates current image width - * - * @return int - */ - public function getWidth() - { - return $this->getSize()->width; - } - - /** - * Alias of getWidth() - * - * @return int - */ - public function width() - { - return $this->getWidth(); - } - - /** - * Calculates current image height - * - * @return int - */ - public function getHeight() - { - return $this->getSize()->height; - } - - /** - * Alias of getHeight - * - * @return int - */ - public function height() - { - return $this->getHeight(); - } - - /** - * Reads mime type - * - * @return string - */ - public function mime() - { - return $this->mime; - } - - /** - * Returns encoded image data in string conversion - * - * @return string - */ - public function __toString() - { - return $this->encoded; - } - - /** - * Cloning an image - */ - public function __clone() - { - $this->core = $this->driver->cloneCore($this->core); - } -} diff --git a/src/Intervention/Image/ImageManager.php b/src/Intervention/Image/ImageManager.php deleted file mode 100644 index 324fe7fa..00000000 --- a/src/Intervention/Image/ImageManager.php +++ /dev/null @@ -1,142 +0,0 @@ - 'gd' - ]; - - /** - * Creates new instance of Image Manager - * - * @param array $config - */ - public function __construct(array $config = []) - { - $this->checkRequirements(); - $this->configure($config); - } - - /** - * Overrides configuration settings - * - * @param array $config - * - * @return self - */ - public function configure(array $config = []) - { - $this->config = array_replace($this->config, $config); - - return $this; - } - - /** - * Initiates an Image instance from different input types - * - * @param mixed $data - * - * @return \Intervention\Image\Image - */ - public function make($data) - { - return $this->createDriver()->init($data); - } - - /** - * Creates an empty image canvas - * - * @param int $width - * @param int $height - * @param mixed $background - * - * @return \Intervention\Image\Image - */ - public function canvas($width, $height, $background = null) - { - return $this->createDriver()->newImage($width, $height, $background); - } - - /** - * Create new cached image and run callback - * (requires additional package intervention/imagecache) - * - * @param Closure $callback - * @param int $lifetime - * @param boolean $returnObj - * - * @return Image - */ - public function cache(Closure $callback, $lifetime = null, $returnObj = false) - { - if (class_exists('Intervention\\Image\\ImageCache')) { - // create imagecache - $imagecache = new ImageCache($this); - - // run callback - if (is_callable($callback)) { - $callback($imagecache); - } - - return $imagecache->get($lifetime, $returnObj); - } - - throw new MissingDependencyException( - "Please install package intervention/imagecache before running this function." - ); - } - - /** - * Creates a driver instance according to config settings - * - * @return \Intervention\Image\AbstractDriver - */ - private function createDriver() - { - if (is_string($this->config['driver'])) { - $drivername = ucfirst($this->config['driver']); - $driverclass = sprintf('Intervention\\Image\\%s\\Driver', $drivername); - - if (class_exists($driverclass)) { - return new $driverclass; - } - - throw new NotSupportedException( - "Driver ({$drivername}) could not be instantiated." - ); - } - - if ($this->config['driver'] instanceof AbstractDriver) { - return $this->config['driver']; - } - - throw new NotSupportedException( - "Unknown driver type." - ); - } - - /** - * Check if all requirements are available - * - * @return void - */ - private function checkRequirements() - { - if ( ! function_exists('finfo_buffer')) { - throw new MissingDependencyException( - "PHP Fileinfo extension must be installed/enabled to use Intervention Image." - ); - } - } -} diff --git a/src/Intervention/Image/ImageManagerStatic.php b/src/Intervention/Image/ImageManagerStatic.php deleted file mode 100644 index a1b56426..00000000 --- a/src/Intervention/Image/ImageManagerStatic.php +++ /dev/null @@ -1,88 +0,0 @@ -configure($config); - } - - /** - * Statically initiates an Image instance from different input types - * - * @param mixed $data - * - * @return \Intervention\Image\Image - * @throws \Intervention\Image\Exception\NotReadableException - */ - public static function make($data) - { - return self::getManager()->make($data); - } - - /** - * Statically creates an empty image canvas - * - * @param int $width - * @param int $height - * @param mixed $background - * - * @return \Intervention\Image\Image - */ - public static function canvas($width, $height, $background = null) - { - return self::getManager()->canvas($width, $height, $background); - } - - /** - * Create new cached image and run callback statically - * - * @param Closure $callback - * @param int $lifetime - * @param boolean $returnObj - * - * @return mixed - */ - public static function cache(Closure $callback, $lifetime = null, $returnObj = false) - { - return self::getManager()->cache($callback, $lifetime, $returnObj); - } -} diff --git a/src/Intervention/Image/ImageServiceProvider.php b/src/Intervention/Image/ImageServiceProvider.php deleted file mode 100644 index f99fe4a3..00000000 --- a/src/Intervention/Image/ImageServiceProvider.php +++ /dev/null @@ -1,87 +0,0 @@ -provider = $this->getProvider(); - } - - /** - * Bootstrap the application events. - * - * @return void - */ - public function boot() - { - if (method_exists($this->provider, 'boot')) { - return $this->provider->boot(); - } - } - - /** - * Register the service provider. - * - * @return void - */ - public function register() - { - return $this->provider->register(); - } - - /** - * Return ServiceProvider according to Laravel version - * - * @return \Intervention\Image\Provider\ProviderInterface - */ - private function getProvider() - { - if ($this->app instanceof LumenApplication) { - $provider = '\Intervention\Image\ImageServiceProviderLumen'; - } elseif (version_compare(IlluminateApplication::VERSION, '5.0', '<')) { - $provider = '\Intervention\Image\ImageServiceProviderLaravel4'; - } else { - $provider = '\Intervention\Image\ImageServiceProviderLaravelRecent'; - } - - return new $provider($this->app); - } - - /** - * Get the services provided by the provider. - * - * @return array - */ - public function provides() - { - return ['image']; - } -} diff --git a/src/Intervention/Image/ImageServiceProviderLaravel4.php b/src/Intervention/Image/ImageServiceProviderLaravel4.php deleted file mode 100755 index 3b1388f2..00000000 --- a/src/Intervention/Image/ImageServiceProviderLaravel4.php +++ /dev/null @@ -1,112 +0,0 @@ -package('intervention/image'); - - // try to create imagecache route only if imagecache is present - if (class_exists('Intervention\\Image\\ImageCache')) { - - $app = $this->app; - - // load imagecache config - $app['config']->package('intervention/imagecache', __DIR__.'/../../../../imagecache/src/config', 'imagecache'); - $config = $app['config']; - - // create dynamic manipulation route - if (is_string($config->get('imagecache::route'))) { - - // add original to route templates - $config->set('imagecache::templates.original', null); - - // setup image manipulator route - $app['router']->get($config->get('imagecache::route').'/{template}/{filename}', ['as' => 'imagecache', function ($template, $filename) use ($app, $config) { - - // disable session cookies for image route - $app['config']->set('session.driver', 'array'); - - // find file - foreach ($config->get('imagecache::paths') as $path) { - // don't allow '..' in filenames - $image_path = $path.'/'.str_replace('..', '', $filename); - if (file_exists($image_path) && is_file($image_path)) { - break; - } else { - $image_path = false; - } - } - - // abort if file not found - if ($image_path === false) { - $app->abort(404); - } - - // define template callback - $callback = $config->get("imagecache::templates.{$template}"); - - if (is_callable($callback) || class_exists($callback)) { - - // image manipulation based on callback - $content = $app['image']->cache(function ($image) use ($image_path, $callback) { - - switch (true) { - case is_callable($callback): - return $callback($image->make($image_path)); - break; - - case class_exists($callback): - return $image->make($image_path)->filter(new $callback); - break; - } - - }, $config->get('imagecache::lifetime')); - - } else { - - // get original image file contents - $content = file_get_contents($image_path); - } - - // define mime type - $mime = finfo_buffer(finfo_open(FILEINFO_MIME_TYPE), $content); - - // return http response - return new IlluminateResponse($content, 200, [ - 'Content-Type' => $mime, - 'Cache-Control' => 'max-age='.($config->get('imagecache::lifetime')*60).', public', - 'Etag' => md5($content) - ]); - - }])->where(['template' => join('|', array_keys($config->get('imagecache::templates'))), 'filename' => '[ \w\\.\\/\\-]+']); - } - } - } - - /** - * Register the service provider. - * - * @return void - */ - public function register() - { - $app = $this->app; - - $app['image'] = $app->share(function ($app) { - return new ImageManager($app['config']->get('image::config')); - }); - - $app->alias('image', 'Intervention\Image\ImageManager'); - } -} diff --git a/src/Intervention/Image/ImageServiceProviderLaravelRecent.php b/src/Intervention/Image/ImageServiceProviderLaravelRecent.php deleted file mode 100644 index 17a84d0a..00000000 --- a/src/Intervention/Image/ImageServiceProviderLaravelRecent.php +++ /dev/null @@ -1,106 +0,0 @@ -publishes([ - __DIR__.'/../../config/config.php' => config_path('image.php') - ]); - - // setup intervention/imagecache if package is installed - $this->cacheIsInstalled() ? $this->bootstrapImageCache() : null; - } - - /** - * Register the service provider. - * - * @return void - */ - public function register() - { - $app = $this->app; - - // merge default config - $this->mergeConfigFrom( - __DIR__.'/../../config/config.php', - 'image' - ); - - // create image - $app->singleton('image', function ($app) { - return new ImageManager($this->getImageConfig($app)); - }); - - $app->alias('image', 'Intervention\Image\ImageManager'); - } - - /** - * Bootstrap imagecache - * - * @return void - */ - protected function bootstrapImageCache() - { - $app = $this->app; - $config = __DIR__.'/../../../../imagecache/src/config/config.php'; - - $this->publishes([ - $config => config_path('imagecache.php') - ]); - - // merge default config - $this->mergeConfigFrom( - $config, - 'imagecache' - ); - - // imagecache route - if (is_string(config('imagecache.route'))) { - - $filename_pattern = '[ \w\\.\\/\\-\\@\(\)\=]+'; - - // route to access template applied image file - $app['router']->get(config('imagecache.route').'/{template}/{filename}', [ - 'uses' => 'Intervention\Image\ImageCacheController@getResponse', - 'as' => 'imagecache' - ])->where(['filename' => $filename_pattern]); - } - } - - /** - * Return image configuration as array - * - * @param Application $app - * @return array - */ - private function getImageConfig($app) - { - $config = $app['config']->get('image'); - - if (is_null($config)) { - return []; - } - - return $config; - } -} diff --git a/src/Intervention/Image/ImageServiceProviderLeague.php b/src/Intervention/Image/ImageServiceProviderLeague.php deleted file mode 100644 index b756a61f..00000000 --- a/src/Intervention/Image/ImageServiceProviderLeague.php +++ /dev/null @@ -1,42 +0,0 @@ -config = $config; - } - - /** - * Register the server provider. - * - * @return void - */ - public function register() - { - $this->getContainer()->share('Intervention\Image\ImageManager', function () { - return new ImageManager($this->config); - }); - } -} diff --git a/src/Intervention/Image/ImageServiceProviderLumen.php b/src/Intervention/Image/ImageServiceProviderLumen.php deleted file mode 100644 index 4a381ccd..00000000 --- a/src/Intervention/Image/ImageServiceProviderLumen.php +++ /dev/null @@ -1,34 +0,0 @@ -app; - - // merge default config - $this->mergeConfigFrom( - __DIR__.'/../../config/config.php', - 'image' - ); - - // set configuration - $app->configure('image'); - - // create image - $app->singleton('image',function ($app) { - return new ImageManager($app['config']->get('image')); - }); - - $app->alias('image', 'Intervention\Image\ImageManager'); - } -} diff --git a/src/Intervention/Image/Imagick/Color.php b/src/Intervention/Image/Imagick/Color.php deleted file mode 100644 index a41ff3a6..00000000 --- a/src/Intervention/Image/Imagick/Color.php +++ /dev/null @@ -1,279 +0,0 @@ -> 24) & 0xFF; - $r = ($value >> 16) & 0xFF; - $g = ($value >> 8) & 0xFF; - $b = $value & 0xFF; - $a = $this->rgb2alpha($a); - - $this->setPixel($r, $g, $b, $a); - } - - /** - * Initiates color object from given array - * - * @param array $value - * @return \Intervention\Image\AbstractColor - */ - public function initFromArray($array) - { - $array = array_values($array); - - if (count($array) == 4) { - - // color array with alpha value - list($r, $g, $b, $a) = $array; - - } elseif (count($array) == 3) { - - // color array without alpha value - list($r, $g, $b) = $array; - $a = 1; - } - - $this->setPixel($r, $g, $b, $a); - } - - /** - * Initiates color object from given string - * - * @param string $value - * - * @return \Intervention\Image\AbstractColor - */ - public function initFromString($value) - { - if ($color = $this->rgbaFromString($value)) { - $this->setPixel($color[0], $color[1], $color[2], $color[3]); - } - } - - /** - * Initiates color object from given ImagickPixel object - * - * @param ImagickPixel $value - * - * @return \Intervention\Image\AbstractColor - */ - public function initFromObject($value) - { - if (is_a($value, '\ImagickPixel')) { - $this->pixel = $value; - } - } - - /** - * Initiates color object from given R, G and B values - * - * @param int $r - * @param int $g - * @param int $b - * - * @return \Intervention\Image\AbstractColor - */ - public function initFromRgb($r, $g, $b) - { - $this->setPixel($r, $g, $b); - } - - /** - * Initiates color object from given R, G, B and A values - * - * @param int $r - * @param int $g - * @param int $b - * @param float $a - * - * @return \Intervention\Image\AbstractColor - */ - public function initFromRgba($r, $g, $b, $a) - { - $this->setPixel($r, $g, $b, $a); - } - - /** - * Calculates integer value of current color instance - * - * @return int - */ - public function getInt() - { - $r = $this->getRedValue(); - $g = $this->getGreenValue(); - $b = $this->getBlueValue(); - $a = intval(round($this->getAlphaValue() * 255)); - - return intval(($a << 24) + ($r << 16) + ($g << 8) + $b); - } - - /** - * Calculates hexadecimal value of current color instance - * - * @param string $prefix - * - * @return string - */ - public function getHex($prefix = '') - { - return sprintf('%s%02x%02x%02x', $prefix, - $this->getRedValue(), - $this->getGreenValue(), - $this->getBlueValue() - ); - } - - /** - * Calculates RGB(A) in array format of current color instance - * - * @return array - */ - public function getArray() - { - return [ - $this->getRedValue(), - $this->getGreenValue(), - $this->getBlueValue(), - $this->getAlphaValue() - ]; - } - - /** - * Calculates RGBA in string format of current color instance - * - * @return string - */ - public function getRgba() - { - return sprintf('rgba(%d, %d, %d, %.2F)', - $this->getRedValue(), - $this->getGreenValue(), - $this->getBlueValue(), - $this->getAlphaValue() - ); - } - - /** - * Determines if current color is different from given color - * - * @param AbstractColor $color - * @param int $tolerance - * @return boolean - */ - public function differs(AbstractColor $color, $tolerance = 0) - { - $color_tolerance = round($tolerance * 2.55); - $alpha_tolerance = round($tolerance); - - $delta = [ - 'r' => abs($color->getRedValue() - $this->getRedValue()), - 'g' => abs($color->getGreenValue() - $this->getGreenValue()), - 'b' => abs($color->getBlueValue() - $this->getBlueValue()), - 'a' => abs($color->getAlphaValue() - $this->getAlphaValue()) - ]; - - return ( - $delta['r'] > $color_tolerance or - $delta['g'] > $color_tolerance or - $delta['b'] > $color_tolerance or - $delta['a'] > $alpha_tolerance - ); - } - - /** - * Returns RGB red value of current color - * - * @return int - */ - public function getRedValue() - { - return intval(round($this->pixel->getColorValue(\Imagick::COLOR_RED) * 255)); - } - - /** - * Returns RGB green value of current color - * - * @return int - */ - public function getGreenValue() - { - return intval(round($this->pixel->getColorValue(\Imagick::COLOR_GREEN) * 255)); - } - - /** - * Returns RGB blue value of current color - * - * @return int - */ - public function getBlueValue() - { - return intval(round($this->pixel->getColorValue(\Imagick::COLOR_BLUE) * 255)); - } - - /** - * Returns RGB alpha value of current color - * - * @return float - */ - public function getAlphaValue() - { - return round($this->pixel->getColorValue(\Imagick::COLOR_ALPHA), 2); - } - - /** - * Initiates ImagickPixel from given RGBA values - * - * @return \ImagickPixel - */ - private function setPixel($r, $g, $b, $a = null) - { - $a = is_null($a) ? 1 : $a; - - return $this->pixel = new \ImagickPixel( - sprintf('rgba(%d, %d, %d, %.2F)', $r, $g, $b, $a) - ); - } - - /** - * Returns current color as ImagickPixel - * - * @return \ImagickPixel - */ - public function getPixel() - { - return $this->pixel; - } - - /** - * Calculates RGA integer alpha value into float value - * - * @param int $value - * @return float - */ - private function rgb2alpha($value) - { - // (255 -> 1.0) / (0 -> 0.0) - return (float) round($value/255, 2); - } - -} diff --git a/src/Intervention/Image/Imagick/Commands/BackupCommand.php b/src/Intervention/Image/Imagick/Commands/BackupCommand.php deleted file mode 100644 index 76b4f72b..00000000 --- a/src/Intervention/Image/Imagick/Commands/BackupCommand.php +++ /dev/null @@ -1,25 +0,0 @@ -argument(0)->value(); - - // clone current image resource - $clone = clone $image; - $image->setBackup($clone->getCore(), $backupName); - - return true; - } -} diff --git a/src/Intervention/Image/Imagick/Commands/BlurCommand.php b/src/Intervention/Image/Imagick/Commands/BlurCommand.php deleted file mode 100644 index d2533e0e..00000000 --- a/src/Intervention/Image/Imagick/Commands/BlurCommand.php +++ /dev/null @@ -1,21 +0,0 @@ -argument(0)->between(0, 100)->value(1); - - return $image->getCore()->blurImage(1 * $amount, 0.5 * $amount); - } -} diff --git a/src/Intervention/Image/Imagick/Commands/BrightnessCommand.php b/src/Intervention/Image/Imagick/Commands/BrightnessCommand.php deleted file mode 100644 index 03ac8478..00000000 --- a/src/Intervention/Image/Imagick/Commands/BrightnessCommand.php +++ /dev/null @@ -1,21 +0,0 @@ -argument(0)->between(-100, 100)->required()->value(); - - return $image->getCore()->modulateImage(100 + $level, 100, 100); - } -} diff --git a/src/Intervention/Image/Imagick/Commands/ColorizeCommand.php b/src/Intervention/Image/Imagick/Commands/ColorizeCommand.php deleted file mode 100644 index 3a6506f6..00000000 --- a/src/Intervention/Image/Imagick/Commands/ColorizeCommand.php +++ /dev/null @@ -1,44 +0,0 @@ -argument(0)->between(-100, 100)->required()->value(); - $green = $this->argument(1)->between(-100, 100)->required()->value(); - $blue = $this->argument(2)->between(-100, 100)->required()->value(); - - // normalize colorize levels - $red = $this->normalizeLevel($red); - $green = $this->normalizeLevel($green); - $blue = $this->normalizeLevel($blue); - - $qrange = $image->getCore()->getQuantumRange(); - - // apply - $image->getCore()->levelImage(0, $red, $qrange['quantumRangeLong'], \Imagick::CHANNEL_RED); - $image->getCore()->levelImage(0, $green, $qrange['quantumRangeLong'], \Imagick::CHANNEL_GREEN); - $image->getCore()->levelImage(0, $blue, $qrange['quantumRangeLong'], \Imagick::CHANNEL_BLUE); - - return true; - } - - private function normalizeLevel($level) - { - if ($level > 0) { - return $level/5; - } else { - return ($level+100)/100; - } - } -} diff --git a/src/Intervention/Image/Imagick/Commands/ContrastCommand.php b/src/Intervention/Image/Imagick/Commands/ContrastCommand.php deleted file mode 100644 index c4847c61..00000000 --- a/src/Intervention/Image/Imagick/Commands/ContrastCommand.php +++ /dev/null @@ -1,21 +0,0 @@ -argument(0)->between(-100, 100)->required()->value(); - - return $image->getCore()->sigmoidalContrastImage($level > 0, $level / 4, 0); - } -} diff --git a/src/Intervention/Image/Imagick/Commands/CropCommand.php b/src/Intervention/Image/Imagick/Commands/CropCommand.php deleted file mode 100644 index 618edea7..00000000 --- a/src/Intervention/Image/Imagick/Commands/CropCommand.php +++ /dev/null @@ -1,45 +0,0 @@ -argument(0)->type('digit')->required()->value(); - $height = $this->argument(1)->type('digit')->required()->value(); - $x = $this->argument(2)->type('digit')->value(); - $y = $this->argument(3)->type('digit')->value(); - - if (is_null($width) || is_null($height)) { - throw new InvalidArgumentException( - "Width and height of cutout needs to be defined." - ); - } - - $cropped = new Size($width, $height); - $position = new Point($x, $y); - - // align boxes - if (is_null($x) && is_null($y)) { - $position = $image->getSize()->align('center')->relativePosition($cropped->align('center')); - } - - // crop image core - $image->getCore()->cropImage($cropped->width, $cropped->height, $position->x, $position->y); - $image->getCore()->setImagePage(0,0,0,0); - - return true; - } -} diff --git a/src/Intervention/Image/Imagick/Commands/DestroyCommand.php b/src/Intervention/Image/Imagick/Commands/DestroyCommand.php deleted file mode 100644 index 58c9556e..00000000 --- a/src/Intervention/Image/Imagick/Commands/DestroyCommand.php +++ /dev/null @@ -1,27 +0,0 @@ -getCore()->clear(); - - // destroy backups - foreach ($image->getBackups() as $backup) { - $backup->clear(); - } - - return true; - } -} diff --git a/src/Intervention/Image/Imagick/Commands/ExifCommand.php b/src/Intervention/Image/Imagick/Commands/ExifCommand.php deleted file mode 100644 index 521b38b0..00000000 --- a/src/Intervention/Image/Imagick/Commands/ExifCommand.php +++ /dev/null @@ -1,63 +0,0 @@ -preferExtension = false; - } - - /** - * Read Exif data from the given image - * - * @param \Intervention\Image\Image $image - * @return boolean - */ - public function execute($image) - { - if ($this->preferExtension && function_exists('exif_read_data')) { - return parent::execute($image); - } - - $core = $image->getCore(); - - if ( ! method_exists($core, 'getImageProperties')) { - throw new NotSupportedException( - "Reading Exif data is not supported by this PHP installation." - ); - } - - $requestedKey = $this->argument(0)->value(); - if ($requestedKey !== null) { - $this->setOutput($core->getImageProperty('exif:' . $requestedKey)); - return true; - } - - $exif = []; - $properties = $core->getImageProperties(); - foreach ($properties as $key => $value) { - if (substr($key, 0, 5) !== 'exif:') { - continue; - } - - $exif[substr($key, 5)] = $value; - } - - $this->setOutput($exif); - return true; - } -} diff --git a/src/Intervention/Image/Imagick/Commands/FillCommand.php b/src/Intervention/Image/Imagick/Commands/FillCommand.php deleted file mode 100644 index 82baac53..00000000 --- a/src/Intervention/Image/Imagick/Commands/FillCommand.php +++ /dev/null @@ -1,105 +0,0 @@ -argument(0)->value(); - $x = $this->argument(1)->type('digit')->value(); - $y = $this->argument(2)->type('digit')->value(); - - $imagick = $image->getCore(); - - try { - // set image filling - $source = new Decoder; - $filling = $source->init($filling); - - } catch (NotReadableException $e) { - - // set solid color filling - $filling = new Color($filling); - } - - // flood fill if coordinates are set - if (is_int($x) && is_int($y)) { - - // flood fill with texture - if ($filling instanceof Image) { - - // create tile - $tile = clone $image->getCore(); - - // mask away color at position - $tile->transparentPaintImage($tile->getImagePixelColor($x, $y), 0, 0, false); - - // create canvas - $canvas = clone $image->getCore(); - - // fill canvas with texture - $canvas = $canvas->textureImage($filling->getCore()); - - // merge canvas and tile - $canvas->compositeImage($tile, \Imagick::COMPOSITE_DEFAULT, 0, 0); - - // replace image core - $image->setCore($canvas); - - // flood fill with color - } elseif ($filling instanceof Color) { - - // create canvas with filling - $canvas = new \Imagick; - $canvas->newImage($image->getWidth(), $image->getHeight(), $filling->getPixel(), 'png'); - - // create tile to put on top - $tile = clone $image->getCore(); - - // mask away color at pos. - $tile->transparentPaintImage($tile->getImagePixelColor($x, $y), 0, 0, false); - - // save alpha channel of original image - $alpha = clone $image->getCore(); - - // merge original with canvas and tile - $image->getCore()->compositeImage($canvas, \Imagick::COMPOSITE_DEFAULT, 0, 0); - $image->getCore()->compositeImage($tile, \Imagick::COMPOSITE_DEFAULT, 0, 0); - - // restore alpha channel of original image - $image->getCore()->compositeImage($alpha, \Imagick::COMPOSITE_COPYOPACITY, 0, 0); - } - - } else { - - if ($filling instanceof Image) { - - // fill whole image with texture - $image->setCore($image->getCore()->textureImage($filling->getCore())); - - } elseif ($filling instanceof Color) { - - // fill whole image with color - $draw = new \ImagickDraw(); - $draw->setFillColor($filling->getPixel()); - $draw->rectangle(0, 0, $image->getWidth(), $image->getHeight()); - $image->getCore()->drawImage($draw); - } - } - - return true; - } -} diff --git a/src/Intervention/Image/Imagick/Commands/FitCommand.php b/src/Intervention/Image/Imagick/Commands/FitCommand.php deleted file mode 100644 index 6d62ba67..00000000 --- a/src/Intervention/Image/Imagick/Commands/FitCommand.php +++ /dev/null @@ -1,42 +0,0 @@ -argument(0)->type('digit')->required()->value(); - $height = $this->argument(1)->type('digit')->value($width); - $constraints = $this->argument(2)->type('closure')->value(); - $position = $this->argument(3)->type('string')->value('center'); - - // calculate size - $cropped = $image->getSize()->fit(new Size($width, $height), $position); - $resized = clone $cropped; - $resized = $resized->resize($width, $height, $constraints); - - // crop image - $image->getCore()->cropImage( - $cropped->width, - $cropped->height, - $cropped->pivot->x, - $cropped->pivot->y - ); - - // resize image - $image->getCore()->scaleImage($resized->getWidth(), $resized->getHeight()); - $image->getCore()->setImagePage(0,0,0,0); - - return true; - } -} diff --git a/src/Intervention/Image/Imagick/Commands/FlipCommand.php b/src/Intervention/Image/Imagick/Commands/FlipCommand.php deleted file mode 100644 index abae16ad..00000000 --- a/src/Intervention/Image/Imagick/Commands/FlipCommand.php +++ /dev/null @@ -1,27 +0,0 @@ -argument(0)->value('h'); - - if (in_array(strtolower($mode), [2, 'v', 'vert', 'vertical'])) { - // flip vertical - return $image->getCore()->flipImage(); - } else { - // flip horizontal - return $image->getCore()->flopImage(); - } - } -} diff --git a/src/Intervention/Image/Imagick/Commands/GammaCommand.php b/src/Intervention/Image/Imagick/Commands/GammaCommand.php deleted file mode 100644 index 200515f3..00000000 --- a/src/Intervention/Image/Imagick/Commands/GammaCommand.php +++ /dev/null @@ -1,21 +0,0 @@ -argument(0)->type('numeric')->required()->value(); - - return $image->getCore()->gammaImage($gamma); - } -} diff --git a/src/Intervention/Image/Imagick/Commands/GetSizeCommand.php b/src/Intervention/Image/Imagick/Commands/GetSizeCommand.php deleted file mode 100644 index ccccedb0..00000000 --- a/src/Intervention/Image/Imagick/Commands/GetSizeCommand.php +++ /dev/null @@ -1,28 +0,0 @@ -getCore(); - - $this->setOutput(new Size( - $core->getImageWidth(), - $core->getImageHeight() - )); - - return true; - } -} diff --git a/src/Intervention/Image/Imagick/Commands/GreyscaleCommand.php b/src/Intervention/Image/Imagick/Commands/GreyscaleCommand.php deleted file mode 100644 index df0ff5b5..00000000 --- a/src/Intervention/Image/Imagick/Commands/GreyscaleCommand.php +++ /dev/null @@ -1,19 +0,0 @@ -getCore()->modulateImage(100, 0, 100); - } -} diff --git a/src/Intervention/Image/Imagick/Commands/HeightenCommand.php b/src/Intervention/Image/Imagick/Commands/HeightenCommand.php deleted file mode 100644 index 0b61e50c..00000000 --- a/src/Intervention/Image/Imagick/Commands/HeightenCommand.php +++ /dev/null @@ -1,28 +0,0 @@ -argument(0)->type('digit')->required()->value(); - $additionalConstraints = $this->argument(1)->type('closure')->value(); - - $this->arguments[0] = null; - $this->arguments[1] = $height; - $this->arguments[2] = function ($constraint) use ($additionalConstraints) { - $constraint->aspectRatio(); - if(is_callable($additionalConstraints)) - $additionalConstraints($constraint); - }; - - return parent::execute($image); - } -} diff --git a/src/Intervention/Image/Imagick/Commands/InsertCommand.php b/src/Intervention/Image/Imagick/Commands/InsertCommand.php deleted file mode 100644 index 2a997436..00000000 --- a/src/Intervention/Image/Imagick/Commands/InsertCommand.php +++ /dev/null @@ -1,33 +0,0 @@ -argument(0)->required()->value(); - $position = $this->argument(1)->type('string')->value(); - $x = $this->argument(2)->type('digit')->value(0); - $y = $this->argument(3)->type('digit')->value(0); - - // build watermark - $watermark = $image->getDriver()->init($source); - - // define insertion point - $image_size = $image->getSize()->align($position, $x, $y); - $watermark_size = $watermark->getSize()->align($position); - $target = $image_size->relativePosition($watermark_size); - - // insert image at position - return $image->getCore()->compositeImage($watermark->getCore(), \Imagick::COMPOSITE_DEFAULT, $target->x, $target->y); - } -} diff --git a/src/Intervention/Image/Imagick/Commands/InterlaceCommand.php b/src/Intervention/Image/Imagick/Commands/InterlaceCommand.php deleted file mode 100644 index 913cab7e..00000000 --- a/src/Intervention/Image/Imagick/Commands/InterlaceCommand.php +++ /dev/null @@ -1,29 +0,0 @@ -argument(0)->type('bool')->value(true); - - if ($mode) { - $mode = \Imagick::INTERLACE_LINE; - } else { - $mode = \Imagick::INTERLACE_NO; - } - - $image->getCore()->setInterlaceScheme($mode); - - return true; - } -} diff --git a/src/Intervention/Image/Imagick/Commands/InvertCommand.php b/src/Intervention/Image/Imagick/Commands/InvertCommand.php deleted file mode 100644 index 1d134301..00000000 --- a/src/Intervention/Image/Imagick/Commands/InvertCommand.php +++ /dev/null @@ -1,19 +0,0 @@ -getCore()->negateImage(false); - } -} diff --git a/src/Intervention/Image/Imagick/Commands/LimitColorsCommand.php b/src/Intervention/Image/Imagick/Commands/LimitColorsCommand.php deleted file mode 100644 index 16f9b82e..00000000 --- a/src/Intervention/Image/Imagick/Commands/LimitColorsCommand.php +++ /dev/null @@ -1,59 +0,0 @@ -argument(0)->value(); - $matte = $this->argument(1)->value(); - - // get current image size - $size = $image->getSize(); - - // build 2 color alpha mask from original alpha - $alpha = clone $image->getCore(); - $alpha->separateImageChannel(\Imagick::CHANNEL_ALPHA); - $alpha->transparentPaintImage('#ffffff', 0, 0, false); - $alpha->separateImageChannel(\Imagick::CHANNEL_ALPHA); - $alpha->negateImage(false); - - if ($matte) { - - // get matte color - $mattecolor = $image->getDriver()->parseColor($matte)->getPixel(); - - // create matte image - $canvas = new \Imagick; - $canvas->newImage($size->width, $size->height, $mattecolor, 'png'); - - // lower colors of original and copy to matte - $image->getCore()->quantizeImage($count, \Imagick::COLORSPACE_RGB, 0, false, false); - $canvas->compositeImage($image->getCore(), \Imagick::COMPOSITE_DEFAULT, 0, 0); - - // copy new alpha to canvas - $canvas->compositeImage($alpha, \Imagick::COMPOSITE_COPYOPACITY, 0, 0); - - // replace core - $image->setCore($canvas); - - } else { - - $image->getCore()->quantizeImage($count, \Imagick::COLORSPACE_RGB, 0, false, false); - $image->getCore()->compositeImage($alpha, \Imagick::COMPOSITE_COPYOPACITY, 0, 0); - - } - - return true; - - } -} diff --git a/src/Intervention/Image/Imagick/Commands/MaskCommand.php b/src/Intervention/Image/Imagick/Commands/MaskCommand.php deleted file mode 100644 index af9d6b2f..00000000 --- a/src/Intervention/Image/Imagick/Commands/MaskCommand.php +++ /dev/null @@ -1,60 +0,0 @@ -argument(0)->value(); - $mask_w_alpha = $this->argument(1)->type('bool')->value(false); - - // get imagick - $imagick = $image->getCore(); - - // build mask image from source - $mask = $image->getDriver()->init($mask_source); - - // resize mask to size of current image (if necessary) - $image_size = $image->getSize(); - if ($mask->getSize() != $image_size) { - $mask->resize($image_size->width, $image_size->height); - } - - $imagick->setImageMatte(true); - - if ($mask_w_alpha) { - - // just mask with alpha map - $imagick->compositeImage($mask->getCore(), \Imagick::COMPOSITE_DSTIN, 0, 0); - - } else { - - // get alpha channel of original as greyscale image - $original_alpha = clone $imagick; - $original_alpha->separateImageChannel(\Imagick::CHANNEL_ALPHA); - - // use red channel from mask ask alpha - $mask_alpha = clone $mask->getCore(); - $mask_alpha->compositeImage($mask->getCore(), \Imagick::COMPOSITE_DEFAULT, 0, 0); - // $mask_alpha->setImageAlphaChannel(\Imagick::ALPHACHANNEL_DEACTIVATE); - $mask_alpha->separateImageChannel(\Imagick::CHANNEL_ALL); - - // combine both alphas from original and mask - $original_alpha->compositeImage($mask_alpha, \Imagick::COMPOSITE_COPYOPACITY, 0, 0); - - // mask the image with the alpha combination - $imagick->compositeImage($original_alpha, \Imagick::COMPOSITE_DSTIN, 0, 0); - } - - return true; - } -} diff --git a/src/Intervention/Image/Imagick/Commands/OpacityCommand.php b/src/Intervention/Image/Imagick/Commands/OpacityCommand.php deleted file mode 100644 index b4708d89..00000000 --- a/src/Intervention/Image/Imagick/Commands/OpacityCommand.php +++ /dev/null @@ -1,23 +0,0 @@ -argument(0)->between(0, 100)->required()->value(); - - $transparency = $transparency > 0 ? (100 / $transparency) : 1000; - - return $image->getCore()->evaluateImage(\Imagick::EVALUATE_DIVIDE, $transparency, \Imagick::CHANNEL_ALPHA); - } -} diff --git a/src/Intervention/Image/Imagick/Commands/PickColorCommand.php b/src/Intervention/Image/Imagick/Commands/PickColorCommand.php deleted file mode 100644 index 978a1284..00000000 --- a/src/Intervention/Image/Imagick/Commands/PickColorCommand.php +++ /dev/null @@ -1,30 +0,0 @@ -argument(0)->type('digit')->required()->value(); - $y = $this->argument(1)->type('digit')->required()->value(); - $format = $this->argument(2)->type('string')->value('array'); - - // pick color - $color = new Color($image->getCore()->getImagePixelColor($x, $y)); - - // format to output - $this->setOutput($color->format($format)); - - return true; - } -} diff --git a/src/Intervention/Image/Imagick/Commands/PixelCommand.php b/src/Intervention/Image/Imagick/Commands/PixelCommand.php deleted file mode 100644 index 6eb6ae71..00000000 --- a/src/Intervention/Image/Imagick/Commands/PixelCommand.php +++ /dev/null @@ -1,31 +0,0 @@ -argument(0)->required()->value(); - $color = new Color($color); - $x = $this->argument(1)->type('digit')->required()->value(); - $y = $this->argument(2)->type('digit')->required()->value(); - - // prepare pixel - $draw = new \ImagickDraw; - $draw->setFillColor($color->getPixel()); - $draw->point($x, $y); - - // apply pixel - return $image->getCore()->drawImage($draw); - } -} diff --git a/src/Intervention/Image/Imagick/Commands/PixelateCommand.php b/src/Intervention/Image/Imagick/Commands/PixelateCommand.php deleted file mode 100644 index 6fe79491..00000000 --- a/src/Intervention/Image/Imagick/Commands/PixelateCommand.php +++ /dev/null @@ -1,27 +0,0 @@ -argument(0)->type('digit')->value(10); - - $width = $image->getWidth(); - $height = $image->getHeight(); - - $image->getCore()->scaleImage(max(1, ($width / $size)), max(1, ($height / $size))); - $image->getCore()->scaleImage($width, $height); - - return true; - } -} diff --git a/src/Intervention/Image/Imagick/Commands/ResetCommand.php b/src/Intervention/Image/Imagick/Commands/ResetCommand.php deleted file mode 100644 index 77b7f336..00000000 --- a/src/Intervention/Image/Imagick/Commands/ResetCommand.php +++ /dev/null @@ -1,40 +0,0 @@ -argument(0)->value(); - - $backup = $image->getBackup($backupName); - - if ($backup instanceof \Imagick) { - - // destroy current core - $image->getCore()->clear(); - - // clone backup - $backup = clone $backup; - - // reset to new resource - $image->setCore($backup); - - return true; - } - - throw new RuntimeException( - "Backup not available. Call backup({$backupName}) before reset()." - ); - } -} diff --git a/src/Intervention/Image/Imagick/Commands/ResizeCanvasCommand.php b/src/Intervention/Image/Imagick/Commands/ResizeCanvasCommand.php deleted file mode 100644 index ecf07610..00000000 --- a/src/Intervention/Image/Imagick/Commands/ResizeCanvasCommand.php +++ /dev/null @@ -1,91 +0,0 @@ -argument(0)->type('digit')->required()->value(); - $height = $this->argument(1)->type('digit')->required()->value(); - $anchor = $this->argument(2)->value('center'); - $relative = $this->argument(3)->type('boolean')->value(false); - $bgcolor = $this->argument(4)->value(); - - $original_width = $image->getWidth(); - $original_height = $image->getHeight(); - - // check of only width or height is set - $width = is_null($width) ? $original_width : intval($width); - $height = is_null($height) ? $original_height : intval($height); - - // check on relative width/height - if ($relative) { - $width = $original_width + $width; - $height = $original_height + $height; - } - - // check for negative width/height - $width = ($width <= 0) ? $width + $original_width : $width; - $height = ($height <= 0) ? $height + $original_height : $height; - - // create new canvas - $canvas = $image->getDriver()->newImage($width, $height, $bgcolor); - - // set copy position - $canvas_size = $canvas->getSize()->align($anchor); - $image_size = $image->getSize()->align($anchor); - $canvas_pos = $image_size->relativePosition($canvas_size); - $image_pos = $canvas_size->relativePosition($image_size); - - if ($width <= $original_width) { - $dst_x = 0; - $src_x = $canvas_pos->x; - $src_w = $canvas_size->width; - } else { - $dst_x = $image_pos->x; - $src_x = 0; - $src_w = $original_width; - } - - if ($height <= $original_height) { - $dst_y = 0; - $src_y = $canvas_pos->y; - $src_h = $canvas_size->height; - } else { - $dst_y = $image_pos->y; - $src_y = 0; - $src_h = $original_height; - } - - // make image area transparent to keep transparency - // even if background-color is set - $rect = new \ImagickDraw; - $fill = $canvas->pickColor(0, 0, 'hex'); - $fill = $fill == '#ff0000' ? '#00ff00' : '#ff0000'; - $rect->setFillColor($fill); - $rect->rectangle($dst_x, $dst_y, $dst_x + $src_w - 1, $dst_y + $src_h - 1); - $canvas->getCore()->drawImage($rect); - $canvas->getCore()->transparentPaintImage($fill, 0, 0, false); - - $canvas->getCore()->setImageColorspace($image->getCore()->getImageColorspace()); - - // copy image into new canvas - $image->getCore()->cropImage($src_w, $src_h, $src_x, $src_y); - $canvas->getCore()->compositeImage($image->getCore(), \Imagick::COMPOSITE_DEFAULT, $dst_x, $dst_y); - $canvas->getCore()->setImagePage(0,0,0,0); - - // set new core to canvas - $image->setCore($canvas->getCore()); - - return true; - } -} diff --git a/src/Intervention/Image/Imagick/Commands/ResizeCommand.php b/src/Intervention/Image/Imagick/Commands/ResizeCommand.php deleted file mode 100644 index 3d4dc5be..00000000 --- a/src/Intervention/Image/Imagick/Commands/ResizeCommand.php +++ /dev/null @@ -1,29 +0,0 @@ -argument(0)->value(); - $height = $this->argument(1)->value(); - $constraints = $this->argument(2)->type('closure')->value(); - - // resize box - $resized = $image->getSize()->resize($width, $height, $constraints); - - // modify image - $image->getCore()->scaleImage($resized->getWidth(), $resized->getHeight()); - - return true; - } -} diff --git a/src/Intervention/Image/Imagick/Commands/RotateCommand.php b/src/Intervention/Image/Imagick/Commands/RotateCommand.php deleted file mode 100644 index b3e12a53..00000000 --- a/src/Intervention/Image/Imagick/Commands/RotateCommand.php +++ /dev/null @@ -1,30 +0,0 @@ -argument(0)->type('numeric')->required()->value(); - $color = $this->argument(1)->value(); - $color = new Color($color); - - // restrict rotations beyond 360 degrees, since the end result is the same - $angle = fmod($angle, 360); - - // rotate image - $image->getCore()->rotateImage($color->getPixel(), ($angle * -1)); - - return true; - } -} diff --git a/src/Intervention/Image/Imagick/Commands/SharpenCommand.php b/src/Intervention/Image/Imagick/Commands/SharpenCommand.php deleted file mode 100644 index bc5e393f..00000000 --- a/src/Intervention/Image/Imagick/Commands/SharpenCommand.php +++ /dev/null @@ -1,21 +0,0 @@ -argument(0)->between(0, 100)->value(10); - - return $image->getCore()->unsharpMaskImage(1, 1, $amount / 6.25, 0); - } -} diff --git a/src/Intervention/Image/Imagick/Commands/TrimCommand.php b/src/Intervention/Image/Imagick/Commands/TrimCommand.php deleted file mode 100644 index bdc897be..00000000 --- a/src/Intervention/Image/Imagick/Commands/TrimCommand.php +++ /dev/null @@ -1,121 +0,0 @@ -argument(0)->type('string')->value(); - $away = $this->argument(1)->value(); - $tolerance = $this->argument(2)->type('numeric')->value(0); - $feather = $this->argument(3)->type('numeric')->value(0); - - $width = $image->getWidth(); - $height = $image->getHeight(); - - $checkTransparency = false; - - // define borders to trim away - if (is_null($away)) { - $away = ['top', 'right', 'bottom', 'left']; - } elseif (is_string($away)) { - $away = [$away]; - } - - // lower border names - foreach ($away as $key => $value) { - $away[$key] = strtolower($value); - } - - // define base color position - switch (strtolower($base)) { - case 'transparent': - case 'trans': - $checkTransparency = true; - $base_x = 0; - $base_y = 0; - break; - - case 'bottom-right': - case 'right-bottom': - $base_x = $width - 1; - $base_y = $height - 1; - break; - - default: - case 'top-left': - case 'left-top': - $base_x = 0; - $base_y = 0; - break; - } - - // pick base color - if ($checkTransparency) { - $base_color = new Color; // color will only be used to compare alpha channel - } else { - $base_color = $image->pickColor($base_x, $base_y, 'object'); - } - - // trim on clone to get only coordinates - $trimed = clone $image->getCore(); - - // add border to trim specific color - $trimed->borderImage($base_color->getPixel(), 1, 1); - - // trim image - $trimed->trimImage(65850 / 100 * $tolerance); - - // get coordinates of trim - $imagePage = $trimed->getImagePage(); - list($crop_x, $crop_y) = [$imagePage['x']-1, $imagePage['y']-1]; - // $trimed->setImagePage(0, 0, 0, 0); - list($crop_width, $crop_height) = [$trimed->width, $trimed->height]; - - // adjust settings if right should not be trimed - if ( ! in_array('right', $away)) { - $crop_width = $crop_width + ($width - ($width - $crop_x)); - } - - // adjust settings if bottom should not be trimed - if ( ! in_array('bottom', $away)) { - $crop_height = $crop_height + ($height - ($height - $crop_y)); - } - - // adjust settings if left should not be trimed - if ( ! in_array('left', $away)) { - $crop_width = $crop_width + $crop_x; - $crop_x = 0; - } - - // adjust settings if top should not be trimed - if ( ! in_array('top', $away)) { - $crop_height = $crop_height + $crop_y; - $crop_y = 0; - } - - // add feather - $crop_width = min($width, ($crop_width + $feather * 2)); - $crop_height = min($height, ($crop_height + $feather * 2)); - $crop_x = max(0, ($crop_x - $feather)); - $crop_y = max(0, ($crop_y - $feather)); - - // finally crop based on page - $image->getCore()->cropImage($crop_width, $crop_height, $crop_x, $crop_y); - $image->getCore()->setImagePage(0,0,0,0); - - $trimed->destroy(); - - return true; - } -} diff --git a/src/Intervention/Image/Imagick/Commands/WidenCommand.php b/src/Intervention/Image/Imagick/Commands/WidenCommand.php deleted file mode 100644 index a1967534..00000000 --- a/src/Intervention/Image/Imagick/Commands/WidenCommand.php +++ /dev/null @@ -1,28 +0,0 @@ -argument(0)->type('digit')->required()->value(); - $additionalConstraints = $this->argument(1)->type('closure')->value(); - - $this->arguments[0] = $width; - $this->arguments[1] = null; - $this->arguments[2] = function ($constraint) use ($additionalConstraints) { - $constraint->aspectRatio(); - if(is_callable($additionalConstraints)) - $additionalConstraints($constraint); - }; - - return parent::execute($image); - } -} diff --git a/src/Intervention/Image/Imagick/Decoder.php b/src/Intervention/Image/Imagick/Decoder.php deleted file mode 100644 index f4dde9a8..00000000 --- a/src/Intervention/Image/Imagick/Decoder.php +++ /dev/null @@ -1,124 +0,0 @@ -setBackgroundColor(new \ImagickPixel('transparent')); - $core->readImage($path); - $core->setImageType(defined('\Imagick::IMGTYPE_TRUECOLORALPHA') ? \Imagick::IMGTYPE_TRUECOLORALPHA : \Imagick::IMGTYPE_TRUECOLORMATTE); - - } catch (\ImagickException $e) { - throw new \Intervention\Image\Exception\NotReadableException( - "Unable to read image from path ({$path}).", - 0, - $e - ); - } - - // build image - $image = $this->initFromImagick($core); - $image->setFileInfoFromPath($path); - - return $image; - } - - /** - * Initiates new image from GD resource - * - * @param Resource $resource - * @return \Intervention\Image\Image - */ - public function initFromGdResource($resource) - { - throw new NotSupportedException( - 'Imagick driver is unable to init from GD resource.' - ); - } - - /** - * Initiates new image from Imagick object - * - * @param Imagick $object - * @return \Intervention\Image\Image - */ - public function initFromImagick(\Imagick $object) - { - // currently animations are not supported - // so all images are turned into static - $object = $this->removeAnimation($object); - - // reset image orientation - $object->setImageOrientation(\Imagick::ORIENTATION_UNDEFINED); - - return new Image(new Driver, $object); - } - - /** - * Initiates new image from binary data - * - * @param string $data - * @return \Intervention\Image\Image - */ - public function initFromBinary($binary) - { - $core = new \Imagick; - - try { - $core->setBackgroundColor(new \ImagickPixel('transparent')); - - $core->readImageBlob($binary); - - } catch (\ImagickException $e) { - throw new NotReadableException( - "Unable to read image from binary data.", - 0, - $e - ); - } - - // build image - $image = $this->initFromImagick($core); - $image->mime = finfo_buffer(finfo_open(FILEINFO_MIME_TYPE), $binary); - - return $image; - } - - /** - * Turns object into one frame Imagick object - * by removing all frames except first - * - * @param Imagick $object - * @return Imagick - */ - private function removeAnimation(\Imagick $object) - { - $imagick = new \Imagick; - - foreach ($object as $frame) { - $imagick->addImage($frame->getImage()); - break; - } - - $object->destroy(); - - return $imagick; - } -} diff --git a/src/Intervention/Image/Imagick/Driver.php b/src/Intervention/Image/Imagick/Driver.php deleted file mode 100644 index bb26ca0f..00000000 --- a/src/Intervention/Image/Imagick/Driver.php +++ /dev/null @@ -1,74 +0,0 @@ -coreAvailable()) { - throw new NotSupportedException( - "ImageMagick module not available with this PHP installation." - ); - } - - $this->decoder = $decoder ? $decoder : new Decoder; - $this->encoder = $encoder ? $encoder : new Encoder; - } - - /** - * Creates new image instance - * - * @param int $width - * @param int $height - * @param mixed $background - * @return \Intervention\Image\Image - */ - public function newImage($width, $height, $background = null) - { - $background = new Color($background); - - // create empty core - $core = new \Imagick; - $core->newImage($width, $height, $background->getPixel(), 'png'); - $core->setType(\Imagick::IMGTYPE_UNDEFINED); - $core->setImageType(\Imagick::IMGTYPE_UNDEFINED); - $core->setColorspace(\Imagick::COLORSPACE_UNDEFINED); - - // build image - $image = new Image(new static, $core); - - return $image; - } - - /** - * Reads given string into color object - * - * @param string $value - * @return AbstractColor - */ - public function parseColor($value) - { - return new Color($value); - } - - /** - * Checks if core module installation is available - * - * @return boolean - */ - protected function coreAvailable() - { - return (extension_loaded('imagick') && class_exists('Imagick')); - } -} diff --git a/src/Intervention/Image/Imagick/Encoder.php b/src/Intervention/Image/Imagick/Encoder.php deleted file mode 100644 index feb8a9d5..00000000 --- a/src/Intervention/Image/Imagick/Encoder.php +++ /dev/null @@ -1,239 +0,0 @@ -image->getCore(); - $imagick->setImageBackgroundColor('white'); - $imagick->setBackgroundColor('white'); - $imagick = $imagick->mergeImageLayers(\Imagick::LAYERMETHOD_MERGE); - $imagick->setFormat($format); - $imagick->setImageFormat($format); - $imagick->setCompression($compression); - $imagick->setImageCompression($compression); - $imagick->setCompressionQuality($this->quality); - $imagick->setImageCompressionQuality($this->quality); - - return $imagick->getImagesBlob(); - } - - /** - * Processes and returns encoded image as PNG string - * - * @return string - */ - protected function processPng() - { - $format = 'png'; - $compression = \Imagick::COMPRESSION_ZIP; - - $imagick = $this->image->getCore(); - $imagick->setFormat($format); - $imagick->setImageFormat($format); - $imagick->setCompression($compression); - $imagick->setImageCompression($compression); - - $this->image->mime = image_type_to_mime_type(IMAGETYPE_PNG); - - return $imagick->getImagesBlob(); - } - - /** - * Processes and returns encoded image as GIF string - * - * @return string - */ - protected function processGif() - { - $format = 'gif'; - $compression = \Imagick::COMPRESSION_LZW; - - $imagick = $this->image->getCore(); - $imagick->setFormat($format); - $imagick->setImageFormat($format); - $imagick->setCompression($compression); - $imagick->setImageCompression($compression); - - $this->image->mime = image_type_to_mime_type(IMAGETYPE_GIF); - - return $imagick->getImagesBlob(); - } - - protected function processWebp() - { - if ( ! \Imagick::queryFormats('WEBP')) { - throw new NotSupportedException( - "Webp format is not supported by Imagick installation." - ); - } - - $format = 'webp'; - $compression = \Imagick::COMPRESSION_JPEG; - - $imagick = $this->image->getCore(); - $imagick->setImageBackgroundColor(new \ImagickPixel('transparent')); - - $imagick = $imagick->mergeImageLayers(\Imagick::LAYERMETHOD_MERGE); - $imagick->setFormat($format); - $imagick->setImageFormat($format); - $imagick->setCompression($compression); - $imagick->setImageCompression($compression); - $imagick->setImageCompressionQuality($this->quality); - - return $imagick->getImagesBlob(); - } - - /** - * Processes and returns encoded image as TIFF string - * - * @return string - */ - protected function processTiff() - { - $format = 'tiff'; - $compression = \Imagick::COMPRESSION_UNDEFINED; - - $imagick = $this->image->getCore(); - $imagick->setFormat($format); - $imagick->setImageFormat($format); - $imagick->setCompression($compression); - $imagick->setImageCompression($compression); - $imagick->setCompressionQuality($this->quality); - $imagick->setImageCompressionQuality($this->quality); - - $this->image->mime = image_type_to_mime_type(IMAGETYPE_TIFF_II); - - return $imagick->getImagesBlob(); - } - - /** - * Processes and returns encoded image as BMP string - * - * @return string - */ - protected function processBmp() - { - $format = 'bmp'; - $compression = \Imagick::COMPRESSION_UNDEFINED; - - $imagick = $this->image->getCore(); - $imagick->setFormat($format); - $imagick->setImageFormat($format); - $imagick->setCompression($compression); - $imagick->setImageCompression($compression); - - $this->image->mime = image_type_to_mime_type(IMAGETYPE_BMP); - - return $imagick->getImagesBlob(); - } - - /** - * Processes and returns encoded image as ICO string - * - * @return string - */ - protected function processIco() - { - $format = 'ico'; - $compression = \Imagick::COMPRESSION_UNDEFINED; - - $imagick = $this->image->getCore(); - $imagick->setFormat($format); - $imagick->setImageFormat($format); - $imagick->setCompression($compression); - $imagick->setImageCompression($compression); - - $this->image->mime = image_type_to_mime_type(IMAGETYPE_ICO); - - return $imagick->getImagesBlob(); - } - - /** - * Processes and returns encoded image as PSD string - * - * @return string - */ - protected function processPsd() - { - $format = 'psd'; - $compression = \Imagick::COMPRESSION_UNDEFINED; - - $imagick = $this->image->getCore(); - $imagick->setFormat($format); - $imagick->setImageFormat($format); - $imagick->setCompression($compression); - $imagick->setImageCompression($compression); - - $this->image->mime = image_type_to_mime_type(IMAGETYPE_PSD); - - return $imagick->getImagesBlob(); - } - - /** - * Processes and returns encoded image as AVIF string - * - * @return string - */ - protected function processAvif() - { - if ( ! \Imagick::queryFormats('AVIF')) { - throw new NotSupportedException( - "AVIF format is not supported by Imagick installation." - ); - } - - $format = 'avif'; - $compression = \Imagick::COMPRESSION_UNDEFINED; - - $imagick = $this->image->getCore(); - $imagick->setFormat($format); - $imagick->setImageFormat($format); - $imagick->setCompression($compression); - $imagick->setImageCompression($compression); - $imagick->setCompressionQuality($this->quality); - $imagick->setImageCompressionQuality($this->quality); - - return $imagick->getImagesBlob(); - } - - /** - * Processes and returns encoded image as HEIC string - * - * @return string - */ - protected function processHeic() - { - if ( ! \Imagick::queryFormats('HEIC')) { - throw new NotSupportedException( - "HEIC format is not supported by Imagick installation." - ); - } - - $format = 'heic'; - $compression = \Imagick::COMPRESSION_UNDEFINED; - - $imagick = $this->image->getCore(); - $imagick->setFormat($format); - $imagick->setImageFormat($format); - $imagick->setCompression($compression); - $imagick->setImageCompression($compression); - $imagick->setCompressionQuality($this->quality); - $imagick->setImageCompressionQuality($this->quality); - - return $imagick->getImagesBlob(); - } -} diff --git a/src/Intervention/Image/Imagick/Font.php b/src/Intervention/Image/Imagick/Font.php deleted file mode 100644 index 9869d067..00000000 --- a/src/Intervention/Image/Imagick/Font.php +++ /dev/null @@ -1,122 +0,0 @@ -setStrokeAntialias(true); - $draw->setTextAntialias(true); - - // set font file - if ($this->hasApplicableFontFile()) { - $draw->setFont($this->file); - } else { - throw new RuntimeException( - "Font file must be provided to apply text to image." - ); - } - - // parse text color - $color = new Color($this->color); - - $draw->setFontSize($this->size); - $draw->setFillColor($color->getPixel()); - $draw->setTextKerning($this->kerning); - - // align horizontal - switch (strtolower($this->align)) { - case 'center': - $align = \Imagick::ALIGN_CENTER; - break; - - case 'right': - $align = \Imagick::ALIGN_RIGHT; - break; - - default: - $align = \Imagick::ALIGN_LEFT; - break; - } - - $draw->setTextAlignment($align); - - // align vertical - if (strtolower($this->valign) != 'bottom') { - - // corrections on y-position - switch (strtolower($this->valign)) { - case 'center': - case 'middle': - // calculate box size - $dimensions = $image->getCore()->queryFontMetrics($draw, $this->text); - $posy = $posy + $dimensions['textHeight'] * 0.65 / 2; - break; - - case 'top': - // calculate box size - $dimensions = $image->getCore()->queryFontMetrics($draw, $this->text, false); - $posy = $posy + $dimensions['characterHeight']; - break; - } - } - - // apply to image - $image->getCore()->annotateImage($draw, $posx, $posy, $this->angle * (-1), $this->text); - } - - /** - * Calculates bounding box of current font setting - * - * @return array - */ - public function getBoxSize() - { - $box = []; - - // build draw object - $draw = new \ImagickDraw(); - $draw->setStrokeAntialias(true); - $draw->setTextAntialias(true); - - // set font file - if ($this->hasApplicableFontFile()) { - $draw->setFont($this->file); - } else { - throw new RuntimeException( - "Font file must be provided to apply text to image." - ); - } - - $draw->setFontSize($this->size); - - $dimensions = (new \Imagick())->queryFontMetrics($draw, $this->text); - - if (strlen($this->text) == 0) { - // no text -> no boxsize - $box['width'] = 0; - $box['height'] = 0; - } else { - // get boxsize - $box['width'] = intval(abs($dimensions['textWidth'])); - $box['height'] = intval(abs($dimensions['textHeight'])); - } - - return $box; - } -} diff --git a/src/Intervention/Image/Imagick/Shapes/CircleShape.php b/src/Intervention/Image/Imagick/Shapes/CircleShape.php deleted file mode 100644 index 24172ea1..00000000 --- a/src/Intervention/Image/Imagick/Shapes/CircleShape.php +++ /dev/null @@ -1,40 +0,0 @@ -width = is_numeric($diameter) ? intval($diameter) : $this->diameter; - $this->height = is_numeric($diameter) ? intval($diameter) : $this->diameter; - $this->diameter = is_numeric($diameter) ? intval($diameter) : $this->diameter; - } - - /** - * Draw current circle on given image - * - * @param Image $image - * @param int $x - * @param int $y - * @return boolean - */ - public function applyToImage(Image $image, $x = 0, $y = 0) - { - return parent::applyToImage($image, $x, $y); - } -} diff --git a/src/Intervention/Image/Imagick/Shapes/EllipseShape.php b/src/Intervention/Image/Imagick/Shapes/EllipseShape.php deleted file mode 100644 index 99694b97..00000000 --- a/src/Intervention/Image/Imagick/Shapes/EllipseShape.php +++ /dev/null @@ -1,66 +0,0 @@ -width = is_numeric($width) ? intval($width) : $this->width; - $this->height = is_numeric($height) ? intval($height) : $this->height; - } - - /** - * Draw ellipse instance on given image - * - * @param Image $image - * @param int $x - * @param int $y - * @return boolean - */ - public function applyToImage(Image $image, $x = 0, $y = 0) - { - $circle = new \ImagickDraw; - - // set background - $bgcolor = new Color($this->background); - $circle->setFillColor($bgcolor->getPixel()); - - // set border - if ($this->hasBorder()) { - $border_color = new Color($this->border_color); - $circle->setStrokeWidth($this->border_width); - $circle->setStrokeColor($border_color->getPixel()); - } - - $circle->ellipse($x, $y, $this->width / 2, $this->height / 2, 0, 360); - - $image->getCore()->drawImage($circle); - - return true; - } -} diff --git a/src/Intervention/Image/Imagick/Shapes/LineShape.php b/src/Intervention/Image/Imagick/Shapes/LineShape.php deleted file mode 100644 index 0d6b3297..00000000 --- a/src/Intervention/Image/Imagick/Shapes/LineShape.php +++ /dev/null @@ -1,94 +0,0 @@ -x = is_numeric($x) ? intval($x) : $this->x; - $this->y = is_numeric($y) ? intval($y) : $this->y; - } - - /** - * Set current line color - * - * @param string $color - * @return void - */ - public function color($color) - { - $this->color = $color; - } - - /** - * Set current line width in pixels - * - * @param int $width - * @return void - */ - public function width($width) - { - $this->width = $width; - } - - /** - * Draw current instance of line to given endpoint on given image - * - * @param Image $image - * @param int $x - * @param int $y - * @return boolean - */ - public function applyToImage(Image $image, $x = 0, $y = 0) - { - $line = new \ImagickDraw; - - $color = new Color($this->color); - $line->setStrokeColor($color->getPixel()); - $line->setStrokeWidth($this->width); - - $line->line($this->x, $this->y, $x, $y); - $image->getCore()->drawImage($line); - - return true; - } -} diff --git a/src/Intervention/Image/Imagick/Shapes/PolygonShape.php b/src/Intervention/Image/Imagick/Shapes/PolygonShape.php deleted file mode 100644 index 45f44ad8..00000000 --- a/src/Intervention/Image/Imagick/Shapes/PolygonShape.php +++ /dev/null @@ -1,81 +0,0 @@ -points = $this->formatPoints($points); - } - - /** - * Draw polygon on given image - * - * @param Image $image - * @param int $x - * @param int $y - * @return boolean - */ - public function applyToImage(Image $image, $x = 0, $y = 0) - { - $polygon = new \ImagickDraw; - - // set background - $bgcolor = new Color($this->background); - $polygon->setFillColor($bgcolor->getPixel()); - - // set border - if ($this->hasBorder()) { - $border_color = new Color($this->border_color); - $polygon->setStrokeWidth($this->border_width); - $polygon->setStrokeColor($border_color->getPixel()); - } - - $polygon->polygon($this->points); - - $image->getCore()->drawImage($polygon); - - return true; - } - - /** - * Format polygon points to Imagick format - * - * @param Array $points - * @return Array - */ - private function formatPoints($points) - { - $ipoints = []; - $count = 1; - - foreach ($points as $key => $value) { - if ($count%2 === 0) { - $y = $value; - $ipoints[] = ['x' => $x, 'y' => $y]; - } else { - $x = $value; - } - $count++; - } - - return $ipoints; - } -} diff --git a/src/Intervention/Image/Imagick/Shapes/RectangleShape.php b/src/Intervention/Image/Imagick/Shapes/RectangleShape.php deleted file mode 100644 index 1166d77b..00000000 --- a/src/Intervention/Image/Imagick/Shapes/RectangleShape.php +++ /dev/null @@ -1,84 +0,0 @@ -x1 = is_numeric($x1) ? intval($x1) : $this->x1; - $this->y1 = is_numeric($y1) ? intval($y1) : $this->y1; - $this->x2 = is_numeric($x2) ? intval($x2) : $this->x2; - $this->y2 = is_numeric($y2) ? intval($y2) : $this->y2; - } - - /** - * Draw rectangle to given image at certain position - * - * @param Image $image - * @param int $x - * @param int $y - * @return boolean - */ - public function applyToImage(Image $image, $x = 0, $y = 0) - { - $rectangle = new \ImagickDraw; - - // set background - $bgcolor = new Color($this->background); - $rectangle->setFillColor($bgcolor->getPixel()); - - // set border - if ($this->hasBorder()) { - $border_color = new Color($this->border_color); - $rectangle->setStrokeWidth($this->border_width); - $rectangle->setStrokeColor($border_color->getPixel()); - } - - $rectangle->rectangle($this->x1, $this->y1, $this->x2, $this->y2); - - $image->getCore()->drawImage($rectangle); - - return true; - } -} diff --git a/src/Intervention/Image/Point.php b/src/Intervention/Image/Point.php deleted file mode 100644 index d51452e0..00000000 --- a/src/Intervention/Image/Point.php +++ /dev/null @@ -1,64 +0,0 @@ -x = is_numeric($x) ? intval($x) : 0; - $this->y = is_numeric($y) ? intval($y) : 0; - } - - /** - * Sets X coordinate - * - * @param int $x - */ - public function setX($x) - { - $this->x = intval($x); - } - - /** - * Sets Y coordinate - * - * @param int $y - */ - public function setY($y) - { - $this->y = intval($y); - } - - /** - * Sets both X and Y coordinate - * - * @param int $x - * @param int $y - */ - public function setPosition($x, $y) - { - $this->setX($x); - $this->setY($y); - } -} diff --git a/src/Intervention/Image/Response.php b/src/Intervention/Image/Response.php deleted file mode 100644 index 8e794081..00000000 --- a/src/Intervention/Image/Response.php +++ /dev/null @@ -1,78 +0,0 @@ -image = $image; - $this->format = $format ? $format : $image->mime; - $this->quality = $quality ? $quality : 90; - } - - /** - * Builds response according to settings - * - * @return mixed - */ - public function make() - { - $this->image->encode($this->format, $this->quality); - $data = $this->image->getEncoded(); - $mime = finfo_buffer(finfo_open(FILEINFO_MIME_TYPE), $data); - $length = strlen($data); - - if (function_exists('app') && is_a($app = app(), 'Illuminate\Foundation\Application')) { - - $response = IlluminateResponse::make($data); - $response->header('Content-Type', $mime); - $response->header('Content-Length', $length); - - } elseif (class_exists('\Symfony\Component\HttpFoundation\Response')) { - - $response = SymfonyResponse::create($data); - $response->headers->set('Content-Type', $mime); - $response->headers->set('Content-Length', $length); - - } else { - - header('Content-Type: ' . $mime); - header('Content-Length: ' . $length); - $response = $data; - } - - return $response; - } -} diff --git a/src/Intervention/Image/Size.php b/src/Intervention/Image/Size.php deleted file mode 100644 index f8b6dc8e..00000000 --- a/src/Intervention/Image/Size.php +++ /dev/null @@ -1,374 +0,0 @@ -width = is_numeric($width) ? intval($width) : 1; - $this->height = is_numeric($height) ? intval($height) : 1; - $this->pivot = $pivot ? $pivot : new Point; - } - - /** - * Set the width and height absolutely - * - * @param int $width - * @param int $height - */ - public function set($width, $height) - { - $this->width = $width; - $this->height = $height; - } - - /** - * Set current pivot point - * - * @param Point $point - */ - public function setPivot(Point $point) - { - $this->pivot = $point; - } - - /** - * Get the current width - * - * @return int - */ - public function getWidth() - { - return $this->width; - } - - /** - * Get the current height - * - * @return int - */ - public function getHeight() - { - return $this->height; - } - - /** - * Calculate the current aspect ratio - * - * @return float - */ - public function getRatio() - { - return $this->width / $this->height; - } - - /** - * Resize to desired width and/or height - * - * @param int $width - * @param int $height - * @param Closure $callback - * @return Size - */ - public function resize($width, $height, Closure $callback = null) - { - if (is_null($width) && is_null($height)) { - throw new InvalidArgumentException( - "Width or height needs to be defined." - ); - } - - // new size with dominant width - $dominant_w_size = clone $this; - $dominant_w_size->resizeHeight($height, $callback); - $dominant_w_size->resizeWidth($width, $callback); - - // new size with dominant height - $dominant_h_size = clone $this; - $dominant_h_size->resizeWidth($width, $callback); - $dominant_h_size->resizeHeight($height, $callback); - - // decide which size to use - if ($dominant_h_size->fitsInto(new self($width, $height))) { - $this->set($dominant_h_size->width, $dominant_h_size->height); - } else { - $this->set($dominant_w_size->width, $dominant_w_size->height); - } - - return $this; - } - - /** - * Scale size according to given constraints - * - * @param int $width - * @param Closure $callback - * @return Size - */ - private function resizeWidth($width, Closure $callback = null) - { - $constraint = $this->getConstraint($callback); - - if ($constraint->isFixed(Constraint::UPSIZE)) { - $max_width = $constraint->getSize()->getWidth(); - $max_height = $constraint->getSize()->getHeight(); - } - - if (is_numeric($width)) { - - if ($constraint->isFixed(Constraint::UPSIZE)) { - $this->width = ($width > $max_width) ? $max_width : $width; - } else { - $this->width = $width; - } - - if ($constraint->isFixed(Constraint::ASPECTRATIO)) { - $h = max(1, intval(round($this->width / $constraint->getSize()->getRatio()))); - - if ($constraint->isFixed(Constraint::UPSIZE)) { - $this->height = ($h > $max_height) ? $max_height : $h; - } else { - $this->height = $h; - } - } - } - } - - /** - * Scale size according to given constraints - * - * @param int $height - * @param Closure $callback - * @return Size - */ - private function resizeHeight($height, Closure $callback = null) - { - $constraint = $this->getConstraint($callback); - - if ($constraint->isFixed(Constraint::UPSIZE)) { - $max_width = $constraint->getSize()->getWidth(); - $max_height = $constraint->getSize()->getHeight(); - } - - if (is_numeric($height)) { - - if ($constraint->isFixed(Constraint::UPSIZE)) { - $this->height = ($height > $max_height) ? $max_height : $height; - } else { - $this->height = $height; - } - - if ($constraint->isFixed(Constraint::ASPECTRATIO)) { - $w = max(1, intval(round($this->height * $constraint->getSize()->getRatio()))); - - if ($constraint->isFixed(Constraint::UPSIZE)) { - $this->width = ($w > $max_width) ? $max_width : $w; - } else { - $this->width = $w; - } - } - } - } - - /** - * Calculate the relative position to another Size - * based on the pivot point settings of both sizes. - * - * @param Size $size - * @return \Intervention\Image\Point - */ - public function relativePosition(Size $size) - { - $x = $this->pivot->x - $size->pivot->x; - $y = $this->pivot->y - $size->pivot->y; - - return new Point($x, $y); - } - - /** - * Resize given Size to best fitting size of current size. - * - * @param Size $size - * @return \Intervention\Image\Size - */ - public function fit(Size $size, $position = 'center') - { - // create size with auto height - $auto_height = clone $size; - - $auto_height->resize($this->width, null, function ($constraint) { - $constraint->aspectRatio(); - }); - - // decide which version to use - if ($auto_height->fitsInto($this)) { - - $size = $auto_height; - - } else { - - // create size with auto width - $auto_width = clone $size; - - $auto_width->resize(null, $this->height, function ($constraint) { - $constraint->aspectRatio(); - }); - - $size = $auto_width; - } - - $this->align($position); - $size->align($position); - $size->setPivot($this->relativePosition($size)); - - return $size; - } - - /** - * Checks if given size fits into current size - * - * @param Size $size - * @return boolean - */ - public function fitsInto(Size $size) - { - return ($this->width <= $size->width) && ($this->height <= $size->height); - } - - /** - * Aligns current size's pivot point to given position - * and moves point automatically by offset. - * - * @param string $position - * @param int $offset_x - * @param int $offset_y - * @return \Intervention\Image\Size - */ - public function align($position, $offset_x = 0, $offset_y = 0) - { - switch (strtolower($position)) { - - case 'top': - case 'top-center': - case 'top-middle': - case 'center-top': - case 'middle-top': - $x = intval($this->width / 2); - $y = 0 + $offset_y; - break; - - case 'top-right': - case 'right-top': - $x = $this->width - $offset_x; - $y = 0 + $offset_y; - break; - - case 'left': - case 'left-center': - case 'left-middle': - case 'center-left': - case 'middle-left': - $x = 0 + $offset_x; - $y = intval($this->height / 2); - break; - - case 'right': - case 'right-center': - case 'right-middle': - case 'center-right': - case 'middle-right': - $x = $this->width - $offset_x; - $y = intval($this->height / 2); - break; - - case 'bottom-left': - case 'left-bottom': - $x = 0 + $offset_x; - $y = $this->height - $offset_y; - break; - - case 'bottom': - case 'bottom-center': - case 'bottom-middle': - case 'center-bottom': - case 'middle-bottom': - $x = intval($this->width / 2); - $y = $this->height - $offset_y; - break; - - case 'bottom-right': - case 'right-bottom': - $x = $this->width - $offset_x; - $y = $this->height - $offset_y; - break; - - case 'center': - case 'middle': - case 'center-center': - case 'middle-middle': - $x = intval($this->width / 2) + $offset_x; - $y = intval($this->height / 2) + $offset_y; - break; - - default: - case 'top-left': - case 'left-top': - $x = 0 + $offset_x; - $y = 0 + $offset_y; - break; - } - - $this->pivot->setPosition($x, $y); - - return $this; - } - - /** - * Runs constraints on current size - * - * @param Closure $callback - * @return \Intervention\Image\Constraint - */ - private function getConstraint(Closure $callback = null) - { - $constraint = new Constraint(clone $this); - - if (is_callable($callback)) { - $callback($constraint); - } - - return $constraint; - } -} diff --git a/src/Traits/CanDecodeDataUri.php b/src/Traits/CanDecodeDataUri.php new file mode 100644 index 00000000..350f4096 --- /dev/null +++ b/src/Traits/CanDecodeDataUri.php @@ -0,0 +1,75 @@ +\w+\/[-+.\w]+)?(?P(;[-\w]+=[-\w]+)*)(?P;base64)?,(?P.*)/"; + $result = preg_match($pattern, $value, $matches); + + return new class ($matches, $result) + { + private $matches; + private $result; + + public function __construct($matches, $result) + { + $this->matches = $matches; + $this->result = $result; + } + + public function isValid(): bool + { + return (bool) $this->result; + } + + public function mediaType(): ?string + { + if (isset($this->matches['mediatype']) && !empty($this->matches['mediatype'])) { + return $this->matches['mediatype']; + } + + return null; + } + + public function hasMediaType(): bool + { + return !empty($this->mediaType()); + } + + public function parameters(): array + { + if (isset($this->matches['parameters']) && !empty($this->matches['parameters'])) { + return explode(';', trim($this->matches['parameters'], ';')); + } + + return []; + } + + public function isBase64Encoded(): bool + { + if (isset($this->matches['base64']) && $this->matches['base64'] === ';base64') { + return true; + } + + return false; + } + + public function data(): ?string + { + if (isset($this->matches['data']) && !empty($this->matches['data'])) { + return $this->matches['data']; + } + + return null; + } + }; + } +} diff --git a/src/Traits/CanResolveDriverClass.php b/src/Traits/CanResolveDriverClass.php new file mode 100644 index 00000000..c7447e4f --- /dev/null +++ b/src/Traits/CanResolveDriverClass.php @@ -0,0 +1,48 @@ +getCurrentDriver()), + $classname + ); + + try { + $reflection = new ReflectionClass($classname); + } catch (ReflectionException $e) { + throw new RuntimeException( + 'Class (' . $classname . ') could not be resolved for current driver.' + ); + } + + return $reflection->newInstanceArgs($arguments); + } + + protected function getCurrentDriver() + { + $pattern = '/Intervention\\\Image\\\Drivers\\\(?P[A-Za-z]+)/'; + preg_match($pattern, get_class($this), $matches); + + if (! array_key_exists('driver', $matches)) { + throw new RuntimeException('Current driver could not be resolved.'); + } + + return strtolower($matches['driver']); + } +} diff --git a/src/Traits/CanValidateBase64.php b/src/Traits/CanValidateBase64.php new file mode 100644 index 00000000..7bfe1f21 --- /dev/null +++ b/src/Traits/CanValidateBase64.php @@ -0,0 +1,15 @@ + 4) { + return false; + } + + // validate rgb values + foreach ($input as $value) { + if ($value < 0 || $value > 255) { + return false; + } + } + + // validate alpha value + if ($input[3] > 1 || $input[3] < 0) { + return false; + } + + return true; + } +} diff --git a/src/config/config.php b/src/config/config.php deleted file mode 100644 index 2b1d2c3e..00000000 --- a/src/config/config.php +++ /dev/null @@ -1,20 +0,0 @@ - 'gd' - -]; diff --git a/tests/AbstractColorTest.php b/tests/AbstractColorTest.php deleted file mode 100644 index 0baeaa3b..00000000 --- a/tests/AbstractColorTest.php +++ /dev/null @@ -1,20 +0,0 @@ -getMockForAbstractClass('\Intervention\Image\AbstractColor'); - $color->format('xxxxxxxxxxxxxxxxxxxxxxx'); - } -} diff --git a/tests/AbstractCommandTest.php b/tests/AbstractCommandTest.php deleted file mode 100644 index a89bee49..00000000 --- a/tests/AbstractCommandTest.php +++ /dev/null @@ -1,48 +0,0 @@ -getTestCommand(); - $this->assertEquals('foo', $command->argument(0)->value()); - $this->assertEquals('bar', $command->argument(1)->value()); - } - - public function testGetOutput() - { - $command = $this->getTestCommand(); - $command->setOutput('foo'); - $this->assertEquals('foo', $command->getOutput()); - } - - public function testHasOutput() - { - $command = $this->getTestCommand(); - $this->assertEquals(false, $command->hasOutput()); - $command->setOutput('foo'); - $this->assertEquals(true, $command->hasOutput()); - } - - public function testSetOutput() - { - $command = $this->getTestCommand(); - $command->setOutput('foo'); - $this->assertEquals(true, $command->hasOutput()); - } - - public function getTestCommand() - { - $arguments = ['foo', 'bar']; - $command = $this->getMockForAbstractClass('\Intervention\Image\Commands\AbstractCommand', [$arguments]); - - return $command; - } -} diff --git a/tests/AbstractDecoderTest.php b/tests/AbstractDecoderTest.php deleted file mode 100644 index a06fc8a1..00000000 --- a/tests/AbstractDecoderTest.php +++ /dev/null @@ -1,160 +0,0 @@ -getTestDecoder(new \Imagick); - $this->assertTrue($source->isImagick()); - - $source = $this->getTestDecoder(new stdClass); - $this->assertFalse($source->isImagick()); - - $source = $this->getTestDecoder(null); - $this->assertFalse($source->isImagick()); - } - - public function testIsGdResource() - { - $resource = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); - $source = $this->getTestDecoder($resource); - $this->assertTrue($source->isGdResource()); - - $source = $this->getTestDecoder(tmpfile()); - $this->assertFalse($source->isGdResource()); - - $source = $this->getTestDecoder(null); - $this->assertFalse($source->isGdResource()); - } - - public function testIsFilepath() - { - $source = $this->getTestDecoder(__DIR__.'/AbstractDecoderTest.php'); - $this->assertTrue($source->isFilepath()); - - $source = $this->getTestDecoder(null); - $this->assertFalse($source->isFilepath()); - - $source = $this->getTestDecoder([]); - $this->assertFalse($source->isFilepath()); - - $source = $this->getTestDecoder(new stdClass); - $this->assertFalse($source->isFilepath()); - } - - public function testIsUrl() - { - $source = $this->getTestDecoder('http://foo.bar'); - $this->assertTrue($source->isUrl()); - - $source = $this->getTestDecoder(null); - $this->assertFalse($source->isUrl()); - } - - public function testIsStream() - { - $source = $this->getTestDecoder(fopen(__DIR__ . '/images/test.jpg', 'r')); - $this->assertTrue($source->isStream()); - - $source = $this->getTestDecoder(new \GuzzleHttp\Psr7\Stream(fopen(__DIR__ . '/images/test.jpg', 'r'))); - $this->assertTrue($source->isStream()); - - $source = $this->getTestDecoder(null); - $this->assertFalse($source->isStream()); - } - - public function testIsBinary() - { - $source = $this->getTestDecoder(file_get_contents(__DIR__.'/images/test.jpg')); - $this->assertTrue($source->isBinary()); - - $source = $this->getTestDecoder(null); - $this->assertFalse($source->isBinary()); - - $source = $this->getTestDecoder(1); - $this->assertFalse($source->isBinary()); - - $source = $this->getTestDecoder(0); - $this->assertFalse($source->isBinary()); - - $source = $this->getTestDecoder([1,2,3]); - $this->assertFalse($source->isBinary()); - - $source = $this->getTestDecoder(new stdClass); - $this->assertFalse($source->isBinary()); - } - - public function testIsInterventionImage() - { - $source = $this->getTestDecoder(1); - $this->assertFalse($source->isInterventionImage()); - - $img = Mockery::mock('Intervention\Image\Image'); - $source = $this->getTestDecoder($img); - $this->assertTrue($source->isInterventionImage()); - } - - public function testIsSplFileInfo() - { - $source = $this->getTestDecoder(1); - $this->assertFalse($source->isSplFileInfo()); - - $img = Mockery::mock('SplFileInfo'); - $source = $this->getTestDecoder($img); - $this->assertTrue($source->isSplFileInfo()); - - $img = Mockery::mock('Symfony\Component\HttpFoundation\File\UploadedFile', 'SplFileInfo'); - $this->assertTrue($source->isSplFileInfo()); - } - - public function testIsSymfonyUpload() - { - $source = $this->getTestDecoder(1); - $this->assertFalse($source->isSymfonyUpload()); - - $img = Mockery::mock('Symfony\Component\HttpFoundation\File\UploadedFile'); - $source = $this->getTestDecoder($img); - $this->assertTrue($source->isSymfonyUpload()); - } - - public function testIsDataUrl() - { - $source = $this->getTestDecoder('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAGElEQVQYlWM8c+bMfwYiABMxikYVUk8hAHWzA3cRvs4UAAAAAElFTkSuQmCC'); - $this->assertTrue($source->isDataUrl()); - - $source = $this->getTestDecoder(null); - $this->assertFalse($source->isDataUrl()); - } - - public function testIsBase64() - { - $decoder = $this->getTestDecoder(null); - $this->assertFalse($decoder->isBase64()); - - $decoder = $this->getTestDecoder('random'); - $this->assertFalse($decoder->isBase64()); - - $base64 = "iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAGElEQVQYlWM8c+bMfwYiABMxikYVUk8hAHWzA3cRvs4UAAAAAElFTkSuQmCC"; - $decoder = $this->getTestDecoder($base64); - $this->assertTrue($decoder->isBase64()); - - $base64WithNewlines = 'iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+' . "\n" . - '9AAAAGElEQVQYlWM8c+bMfwYiABMxikYVUk8hAHWzA3' . "\n" . - 'cRvs4UAAAAAElFTkSuQmCC'; - - $decoder = $this->getTestDecoder($base64WithNewlines); - $this->assertTrue($decoder->isBase64()); - } - - public function getTestDecoder($data) - { - return $this->getMockForAbstractClass('\Intervention\Image\AbstractDecoder', [$data]); - } -} diff --git a/tests/AbstractDriverTest.php b/tests/AbstractDriverTest.php deleted file mode 100644 index 704c0992..00000000 --- a/tests/AbstractDriverTest.php +++ /dev/null @@ -1,21 +0,0 @@ -getMockForAbstractClass('\Intervention\Image\AbstractDriver'); - $command = $driver->executeCommand($image, 'xxxxxxxxxxxxxxxxxxxxxxx', []); - } -} diff --git a/tests/AbstractFontTest.php b/tests/AbstractFontTest.php deleted file mode 100644 index 37dfe56d..00000000 --- a/tests/AbstractFontTest.php +++ /dev/null @@ -1,86 +0,0 @@ -getMockForAbstractClass('\Intervention\Image\AbstractFont', ['test']); - $this->assertEquals('test', $font->text); - } - - public function testText() - { - $font = $this->getMockForAbstractClass('\Intervention\Image\AbstractFont'); - $font->text('test'); - $this->assertEquals('test', $font->text); - } - - public function testSize() - { - $font = $this->getMockForAbstractClass('\Intervention\Image\AbstractFont'); - $font->size(16); - $this->assertEquals(16, $font->size); - } - - public function testColor() - { - $font = $this->getMockForAbstractClass('\Intervention\Image\AbstractFont'); - $font->color('#ffffff'); - $this->assertEquals('#ffffff', $font->color); - } - - public function testAngle() - { - $font = $this->getMockForAbstractClass('\Intervention\Image\AbstractFont'); - $font->angle(30); - $this->assertEquals(30, $font->angle); - } - - public function testAlign() - { - $font = $this->getMockForAbstractClass('\Intervention\Image\AbstractFont'); - $font->align('right'); - $this->assertEquals('right', $font->align); - } - - public function testValign() - { - $font = $this->getMockForAbstractClass('\Intervention\Image\AbstractFont'); - $font->valign('top'); - $this->assertEquals('top', $font->valign); - } - - public function testKerning() - { - $font = $this->getMockForAbstractClass('\Intervention\Image\AbstractFont'); - $font->kerning(10.5); - $this->assertEquals(10.5, $font->kerning); - } - - public function testFile() - { - $font = $this->getMockForAbstractClass('\Intervention\Image\AbstractFont'); - $font->file('test.ttf'); - $this->assertEquals('test.ttf', $font->file); - } - - public function testCountLines() - { - $font = $this->getMockForAbstractClass('\Intervention\Image\AbstractFont'); - $font->text('foo'.PHP_EOL.'bar'.PHP_EOL.'baz'); - $this->assertEquals(3, $font->countLines()); - $font->text("foo\nbar\nbaz"); - $this->assertEquals(3, $font->countLines()); - $font->text('foo - bar - baz'); - $this->assertEquals(3, $font->countLines()); - } -} diff --git a/tests/AbstractShapeTest.php b/tests/AbstractShapeTest.php deleted file mode 100644 index 64f88adc..00000000 --- a/tests/AbstractShapeTest.php +++ /dev/null @@ -1,43 +0,0 @@ -getMockForAbstractClass('\Intervention\Image\AbstractShape'); - $shape->background('foo'); - $this->assertEquals('foo', $shape->background); - $this->assertEquals(0, $shape->border_width); - } - - public function testBorder() - { - $shape = $this->getMockForAbstractClass('\Intervention\Image\AbstractShape'); - $shape->border(4); - $this->assertEquals(4, $shape->border_width); - $this->assertEquals('#000000', $shape->border_color); - } - - public function testBorderWithColor() - { - $shape = $this->getMockForAbstractClass('\Intervention\Image\AbstractShape'); - $shape->border(3, '#ff00ff'); - $this->assertEquals(3, $shape->border_width); - $this->assertEquals('#ff00ff', $shape->border_color); - } - - public function testHasBorder() - { - $shape = $this->getMockForAbstractClass('\Intervention\Image\AbstractShape'); - $this->assertFalse($shape->hasBorder()); - $shape->border(1); - $this->assertTrue($shape->hasBorder()); - } -} diff --git a/tests/ArgumentTest.php b/tests/ArgumentTest.php deleted file mode 100644 index a40a1080..00000000 --- a/tests/ArgumentTest.php +++ /dev/null @@ -1,422 +0,0 @@ -getMockedCommand(['foo'])); - $this->validateArgument($arg, 'foo'); - - $arg = new Argument($this->getMockedCommand(['foo', 'bar']), 1); - $this->validateArgument($arg, 'bar'); - - $arg = new Argument($this->getMockedCommand(), 0); - $this->validateArgument($arg, null); - } - - public function testRequiredPass() - { - $arg = new Argument($this->getMockedCommand(['foo'])); - $arg->required(); - $this->validateArgument($arg, 'foo'); - - $arg = new Argument($this->getMockedCommand([null])); - $arg->required(); - $this->validateArgument($arg, null); - - $arg = new Argument($this->getMockedCommand([0])); - $arg->required(); - $this->validateArgument($arg, 0); - } - - /** - * @expectedException \Intervention\Image\Exception\InvalidArgumentException - */ - public function testRequiredFail() - { - $arg = new Argument($this->getMockedCommand([])); - $arg->required(); - } - - /** - * @expectedException \Intervention\Image\Exception\InvalidArgumentException - */ - public function testRequiredFailSecondParameter() - { - $arg = new Argument($this->getMockedCommand(['foo']), 1); - $arg->required(); - } - - public function testTypeIntegerPass() - { - $arg = new Argument($this->getMockedCommand([])); - $arg->type('integer'); - $this->validateArgument($arg, null); - - $arg = new Argument($this->getMockedCommand([123])); - $arg->type('integer'); - $this->validateArgument($arg, 123); - } - - /** - * @expectedException \Intervention\Image\Exception\InvalidArgumentException - */ - public function testTypeIntegerFail() - { - $arg = new Argument($this->getMockedCommand(['foo'])); - $arg->type('integer'); - } - - public function testTypeArrayPass() - { - $arg = new Argument($this->getMockedCommand([])); - $arg->type('array'); - $this->validateArgument($arg, null); - - $arg = new Argument($this->getMockedCommand([[1,2,3]])); - $arg->type('array'); - $this->validateArgument($arg, [1,2,3]); - } - - /** - * @expectedException \Intervention\Image\Exception\InvalidArgumentException - */ - public function testTypeArrayFail() - { - $arg = new Argument($this->getMockedCommand(['foo'])); - $arg->type('array'); - } - - public function testTypeDigitPass() - { - $arg = new Argument($this->getMockedCommand([])); - $arg->type('digit'); - $this->validateArgument($arg, null); - - $arg = new Argument($this->getMockedCommand([0])); - $arg->type('digit'); - $this->validateArgument($arg, 0); - - $arg = new Argument($this->getMockedCommand([123])); - $arg->type('digit'); - $this->validateArgument($arg, 123); - - $arg = new Argument($this->getMockedCommand([5.0])); - $arg->type('digit'); - $this->validateArgument($arg, 5.0); - - $arg = new Argument($this->getMockedCommand([-10])); - $arg->type('digit'); - $this->validateArgument($arg, -10); - - $arg = new Argument($this->getMockedCommand([-10.0])); - $arg->type('digit'); - $this->validateArgument($arg, -10.0); - - $arg = new Argument($this->getMockedCommand(['12'])); - $arg->type('digit'); - $this->validateArgument($arg, '12'); - - $arg = new Argument($this->getMockedCommand(['12.0'])); - $arg->type('digit'); - $this->validateArgument($arg, '12.0'); - } - - /** - * @expectedException \Intervention\Image\Exception\InvalidArgumentException - */ - public function testTypeDigitFailString() - { - $arg = new Argument($this->getMockedCommand(['foo'])); - $arg->type('digit'); - } - - /** - * @expectedException \Intervention\Image\Exception\InvalidArgumentException - */ - public function testTypeDigitFailFloat() - { - $arg = new Argument($this->getMockedCommand([12.5])); - $arg->type('digit'); - } - - /** - * @expectedException \Intervention\Image\Exception\InvalidArgumentException - */ - public function testTypeDigitFailBool() - { - $arg = new Argument($this->getMockedCommand([true])); - $arg->type('digit'); - } - - public function testTypeNumericPass() - { - $arg = new Argument($this->getMockedCommand([])); - $arg->type('numeric'); - $this->validateArgument($arg, null); - - $arg = new Argument($this->getMockedCommand([12.3])); - $arg->type('numeric'); - $this->validateArgument($arg, 12.3); - } - - /** - * @expectedException \Intervention\Image\Exception\InvalidArgumentException - */ - public function testTypeNumericFail() - { - $arg = new Argument($this->getMockedCommand(['foo'])); - $arg->type('numeric'); - } - - public function testTypeBooleanPass() - { - $arg = new Argument($this->getMockedCommand([])); - $arg->type('boolean'); - $this->validateArgument($arg, null); - - $arg = new Argument($this->getMockedCommand([true])); - $arg->type('boolean'); - $this->validateArgument($arg, true); - - $arg = new Argument($this->getMockedCommand([false])); - $arg->type('boolean'); - $this->validateArgument($arg, false); - } - - /** - * @expectedException \Intervention\Image\Exception\InvalidArgumentException - */ - public function testTypeBooleanFail() - { - $arg = new Argument($this->getMockedCommand(['foo'])); - $arg->type('boolean'); - } - - public function testTypeStringPass() - { - $arg = new Argument($this->getMockedCommand([])); - $arg->type('string'); - $this->validateArgument($arg, null); - - $arg = new Argument($this->getMockedCommand(['foo'])); - $arg->type('string'); - $this->validateArgument($arg, 'foo'); - } - - /** - * @expectedException \Intervention\Image\Exception\InvalidArgumentException - */ - public function testTypeStringFail() - { - $arg = new Argument($this->getMockedCommand([12])); - $arg->type('string'); - } - - public function testTypeClosurePass() - { - $arg = new Argument($this->getMockedCommand([])); - $arg->type('closure'); - $this->validateArgument($arg, null); - - $c = function ($foo) {}; - $arg = new Argument($this->getMockedCommand([$c])); - $arg->type('closure'); - $this->validateArgument($arg, $c); - } - - /** - * @expectedException \Intervention\Image\Exception\InvalidArgumentException - */ - public function testTypeClosureFail() - { - $arg = new Argument($this->getMockedCommand(['foo'])); - $arg->type('closure'); - } - - public function testBetweenPass() - { - $arg = new Argument($this->getMockedCommand([])); - $arg->between(0, 10); - $this->validateArgument($arg, null); - - $arg = new Argument($this->getMockedCommand([null])); - $arg->between(0, 10); - $this->validateArgument($arg, null); - - $arg = new Argument($this->getMockedCommand([4.5])); - $arg->between(0, 10); - $this->validateArgument($arg, 4.5); - - $arg = new Argument($this->getMockedCommand([4.5])); - $arg->between(10, 1); - $this->validateArgument($arg, 4.5); - - $arg = new Argument($this->getMockedCommand([0])); - $arg->between(0, 10); - $this->validateArgument($arg, 0); - - $arg = new Argument($this->getMockedCommand([10])); - $arg->between(0, 10); - $this->validateArgument($arg, 10); - - $arg = new Argument($this->getMockedCommand([0])); - $arg->between(-100, 100); - $this->validateArgument($arg, 0); - - $arg = new Argument($this->getMockedCommand([-100])); - $arg->between(-100, 100); - $this->validateArgument($arg, -100); - } - - /** - * @expectedException \Intervention\Image\Exception\InvalidArgumentException - */ - public function testBetweenFailString() - { - $arg = new Argument($this->getMockedCommand(['foo'])); - $arg->between(1, 10); - } - - /** - * @expectedException \Intervention\Image\Exception\InvalidArgumentException - */ - public function testBetweenFailAbove() - { - $arg = new Argument($this->getMockedCommand([10.9])); - $arg->between(0, 10); - } - - /** - * @expectedException \Intervention\Image\Exception\InvalidArgumentException - */ - public function testBetweenFailBelow() - { - $arg = new Argument($this->getMockedCommand([-1])); - $arg->between(0, 10); - } - - /** - * @expectedException \Intervention\Image\Exception\InvalidArgumentException - */ - public function testBetweenFail() - { - $arg = new Argument($this->getMockedCommand([-1000])); - $arg->between(-100, 100); - } - - public function testMinPass() - { - $arg = new Argument($this->getMockedCommand([])); - $arg->min(10); - $this->validateArgument($arg, null); - - $arg = new Argument($this->getMockedCommand([null])); - $arg->min(10); - $this->validateArgument($arg, null); - - $arg = new Argument($this->getMockedCommand([50])); - $arg->min(10); - $this->validateArgument($arg, 50); - - $arg = new Argument($this->getMockedCommand([50])); - $arg->min(50); - $this->validateArgument($arg, 50); - - $arg = new Argument($this->getMockedCommand([50])); - $arg->min(-10); - $this->validateArgument($arg, 50); - - $arg = new Argument($this->getMockedCommand([-10])); - $arg->min(-10); - $this->validateArgument($arg, -10); - } - - /** - * @expectedException \Intervention\Image\Exception\InvalidArgumentException - */ - public function testMinFailString() - { - $arg = new Argument($this->getMockedCommand(['foo'])); - $arg->min(10); - } - - /** - * @expectedException \Intervention\Image\Exception\InvalidArgumentException - */ - public function testMinFail() - { - $arg = new Argument($this->getMockedCommand([10.9])); - $arg->min(11); - } - - public function testMaxPass() - { - $arg = new Argument($this->getMockedCommand([])); - $arg->max(100); - $this->validateArgument($arg, null); - - $arg = new Argument($this->getMockedCommand([null])); - $arg->max(100); - $this->validateArgument($arg, null); - - $arg = new Argument($this->getMockedCommand([50])); - $arg->max(100); - $this->validateArgument($arg, 50); - - $arg = new Argument($this->getMockedCommand([100])); - $arg->max(100); - $this->validateArgument($arg, 100); - - $arg = new Argument($this->getMockedCommand([-100])); - $arg->max(-10); - $this->validateArgument($arg, -100); - - $arg = new Argument($this->getMockedCommand([-10])); - $arg->max(-10); - $this->validateArgument($arg, -10); - } - - /** - * @expectedException \Intervention\Image\Exception\InvalidArgumentException - */ - public function testMaxFailString() - { - $arg = new Argument($this->getMockedCommand(['foo'])); - $arg->max(10); - } - - /** - * @expectedException \Intervention\Image\Exception\InvalidArgumentException - */ - public function testMaxFail() - { - $arg = new Argument($this->getMockedCommand([25])); - $arg->max(10); - } - - public function testValueDefault() - { - $arg = new Argument($this->getMockedCommand()); - $value = $arg->value('foo'); - $this->assertEquals('foo', $value); - - $arg = new Argument($this->getMockedCommand([null])); - $value = $arg->value('foo'); - $this->assertEquals('foo', $value); - } - - private function validateArgument($argument, $value) - { - $this->assertInstanceOf('\Intervention\Image\Commands\Argument', $argument); - $this->assertEquals($value, $argument->value()); - } - - private function getMockedCommand($arguments = []) - { - return $this->getMockForAbstractClass('\Intervention\Image\Commands\AbstractCommand', [$arguments]); - } -} diff --git a/tests/BackupCommandTest.php b/tests/BackupCommandTest.php deleted file mode 100644 index f7d6960f..00000000 --- a/tests/BackupCommandTest.php +++ /dev/null @@ -1,65 +0,0 @@ -shouldReceive('cloneCore')->once(); - $resource = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); - $image = Mockery::mock('Intervention\Image\Image', [$driver]); - $image->shouldReceive('getCore')->once()->andReturn($resource); - $image->shouldReceive('setBackup')->once(); - $command = new BackupGd([]); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testGdWithName() - { - $driver = Mockery::mock('Intervention\Image\Gd\Driver'); - $driver->shouldReceive('cloneCore')->once(); - $resource = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); - $image = Mockery::mock('Intervention\Image\Image', [$driver]); - $image->shouldReceive('getCore')->once()->andReturn($resource); - $image->shouldReceive('setBackup')->once(); - $command = new BackupGd(['name' => 'fooBackup']); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testImagickWithoutName() - { - $driver = Mockery::mock('Intervention\Image\Imagick\Driver'); - $driver->shouldReceive('cloneCore')->once(); - $imagick = Mockery::mock('Imagick'); - $image = Mockery::mock('Intervention\Image\Image', [$driver]); - $image->shouldReceive('getCore')->once()->andReturn($imagick); - $image->shouldReceive('setBackup')->once(); - $command = new BackupImagick([]); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testImagickWithName() - { - $driver = Mockery::mock('Intervention\Image\Imagick\Driver'); - $driver->shouldReceive('cloneCore')->once(); - $imagick = Mockery::mock('Imagick'); - $image = Mockery::mock('Intervention\Image\Image', [$driver]); - $image->shouldReceive('getCore')->once()->andReturn($imagick); - $image->shouldReceive('setBackup')->once(); - $command = new BackupImagick(['name' => 'fooBackup']); - $result = $command->execute($image); - $this->assertTrue($result); - } -} diff --git a/tests/BlurCommandTest.php b/tests/BlurCommandTest.php deleted file mode 100644 index 6267d48e..00000000 --- a/tests/BlurCommandTest.php +++ /dev/null @@ -1,34 +0,0 @@ -shouldReceive('getCore')->times(2)->andReturn($resource); - $command = new BlurGd([2]); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testImagick() - { - $imagick = Mockery::mock('Imagick'); - $imagick->shouldReceive('blurimage')->with(2, 1)->andReturn(true); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($imagick); - $command = new BlurImagick([2]); - $result = $command->execute($image); - $this->assertTrue($result); - } -} diff --git a/tests/BrightnessCommandTest.php b/tests/BrightnessCommandTest.php deleted file mode 100644 index 2c225d0b..00000000 --- a/tests/BrightnessCommandTest.php +++ /dev/null @@ -1,34 +0,0 @@ -shouldReceive('getCore')->once()->andReturn($resource); - $command = new BrightnessGd([12]); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testImagick() - { - $imagick = Mockery::mock('Imagick'); - $imagick->shouldReceive('modulateimage')->with(112, 100, 100)->andReturn(true); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($imagick); - $command = new BrightnessImagick([12]); - $result = $command->execute($image); - $this->assertTrue($result); - } -} diff --git a/tests/ChecksumCommandTest.php b/tests/ChecksumCommandTest.php deleted file mode 100644 index 5be7ca68..00000000 --- a/tests/ChecksumCommandTest.php +++ /dev/null @@ -1,27 +0,0 @@ -shouldReceive('getSize')->once()->andReturn($size); - $image->shouldReceive('pickColor')->times(9)->andReturn($color); - $command = new ChecksumCommand([]); - $result = $command->execute($image); - $this->assertTrue($result); - $this->assertTrue($command->hasOutput()); - $this->assertEquals('ec9cbdb71be04e26b4a89333f20c273b', $command->getOutput()); - } -} diff --git a/tests/CircleCommandTest.php b/tests/CircleCommandTest.php deleted file mode 100644 index 9c916a14..00000000 --- a/tests/CircleCommandTest.php +++ /dev/null @@ -1,43 +0,0 @@ -shouldReceive('getDriverName')->once()->andReturn('Gd'); - $image = Mockery::mock('\Intervention\Image\Image'); - $image->shouldReceive('getDriver')->once()->andReturn($driver); - $image->shouldReceive('getCore')->once()->andReturn($resource); - $command = new CircleCommand([250, 10, 20]); - $result = $command->execute($image); - $this->assertTrue($result); - $this->assertFalse($command->hasOutput()); - } - - public function testImagick() - { - $imagick = Mockery::mock('\Imagick'); - $imagick->shouldReceive('drawimage'); - $driver = Mockery::mock('\Intervention\Image\Imagick\Driver'); - $driver->shouldReceive('getDriverName')->once()->andReturn('Imagick'); - $image = Mockery::mock('\Intervention\Image\Image'); - $image->shouldReceive('getDriver')->once()->andReturn($driver); - $image->shouldReceive('getCore')->once()->andReturn($imagick); - - $command = new CircleCommand([25, 10, 20]); - $result = $command->execute($image); - $this->assertTrue($result); - $this->assertFalse($command->hasOutput()); - } - -} diff --git a/tests/CircleShapeTest.php b/tests/CircleShapeTest.php deleted file mode 100644 index 18a51482..00000000 --- a/tests/CircleShapeTest.php +++ /dev/null @@ -1,52 +0,0 @@ -assertInstanceOf('Intervention\Image\Gd\Shapes\CircleShape', $circle); - $this->assertEquals(250, $circle->diameter); - - } - - public function testGdApplyToImage() - { - $core = imagecreatetruecolor(300, 200); - $image = Mockery::mock('\Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($core); - $circle = new CircleGd(250); - $result = $circle->applyToImage($image, 10, 20); - $this->assertInstanceOf('Intervention\Image\Gd\Shapes\CircleShape', $circle); - $this->assertTrue($result); - } - - public function testImagickConstructor() - { - $circle = new CircleImagick(250); - $this->assertInstanceOf('Intervention\Image\Imagick\Shapes\CircleShape', $circle); - $this->assertEquals(250, $circle->width); - } - - public function testImagickApplyToImage() - { - $core = Mockery::mock('\Imagick'); - $core->shouldReceive('drawimage')->once(); - $image = Mockery::mock('\Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($core); - $circle = new CircleImagick(250); - $result = $circle->applyToImage($image, 10, 20); - $this->assertInstanceOf('Intervention\Image\Imagick\Shapes\CircleShape', $circle); - $this->assertTrue($result); - } - -} diff --git a/tests/CollectionTest.php b/tests/CollectionTest.php new file mode 100644 index 00000000..f02d1843 --- /dev/null +++ b/tests/CollectionTest.php @@ -0,0 +1,102 @@ +assertInstanceOf(Collection::class, $collection); + + $collection = Collection::create(['foo', 'bar', 'baz']); + $this->assertInstanceOf(Collection::class, $collection); + } + + public function testIterator() + { + $collection = new Collection(['foo', 'bar', 'baz']); + foreach ($collection as $key => $item) { + switch ($key) { + case 0: + $this->assertEquals('foo', $item); + break; + + case 1: + $this->assertEquals('bar', $item); + break; + + case 2: + $this->assertEquals('baz', $item); + break; + } + } + } + + public function testCount() + { + $collection = new Collection(['foo', 'bar', 'baz']); + $this->assertEquals(3, $collection->count()); + $this->assertEquals(3, count($collection)); + } + + public function testFirstLast() + { + $collection = new Collection(['foo', 'bar', 'baz']); + $this->assertEquals('foo', $collection->first()); + $this->assertEquals('baz', $collection->last()); + + $collection = new Collection(); + $this->assertNull($collection->first()); + $this->assertNull($collection->last()); + } + + public function testPush() + { + $collection = new Collection(['foo', 'bar', 'baz']); + $this->assertEquals(3, $collection->count()); + $result = $collection->push('test'); + $this->assertEquals(4, $collection->count()); + $this->assertInstanceOf(Collection::class, $result); + } + + public function testPushEach() + { + $collection = Collection::create()->pushEach(['foo', 'bar', 'baz'], function ($item) { + return strtoupper($item); + }); + $this->assertEquals(3, $collection->count()); + $this->assertEquals('FOO', $collection->get(0)); + $this->assertEquals('BAR', $collection->get(1)); + $this->assertEquals('BAZ', $collection->get(2)); + } + + public function testGet() + { + $collection = new Collection(['foo', 'bar', 'baz']); + $this->assertEquals('foo', $collection->get(0)); + $this->assertEquals('bar', $collection->get(1)); + $this->assertEquals('baz', $collection->get(2)); + $this->assertNull($collection->get(3)); + } + + public function testToArray() + { + $collection = new Collection(['foo', 'bar', 'baz']); + $this->assertEquals(['foo', 'bar', 'baz'], $collection->toArray()); + } + + public function testMap(): void + { + $collection = new Collection(['FOO', 'BAR', 'BAZ']); + $mapped = $collection->map(function ($item) { + return strtolower($item); + }); + $this->assertInstanceOf(Collection::class, $collection); + $this->assertInstanceOf(Collection::class, $mapped); + $this->assertEquals(['FOO', 'BAR', 'BAZ'], $collection->toArray()); + $this->assertEquals(['foo', 'bar', 'baz'], $mapped->toArray()); + } +} diff --git a/tests/ColorizeCommandTest.php b/tests/ColorizeCommandTest.php deleted file mode 100644 index 0b3f9ece..00000000 --- a/tests/ColorizeCommandTest.php +++ /dev/null @@ -1,37 +0,0 @@ -shouldReceive('getCore')->once()->andReturn($resource); - $command = new ColorizeGd([20, 0, -40]); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testImagick() - { - $imagick = Mockery::mock('Imagick'); - $imagick->shouldReceive('getquantumrange')->with()->once()->andReturn(['quantumRangeLong' => 42]); - $imagick->shouldReceive('levelimage')->with(0, 4, 42, \Imagick::CHANNEL_RED)->once()->andReturn(true); - $imagick->shouldReceive('levelimage')->with(0, 1, 42, \Imagick::CHANNEL_GREEN)->once()->andReturn(true); - $imagick->shouldReceive('levelimage')->with(0, 0.6, 42, \Imagick::CHANNEL_BLUE)->once()->andReturn(true); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getCore')->times(4)->andReturn($imagick); - $command = new ColorizeImagick([20, 0, -40]); - $result = $command->execute($image); - $this->assertTrue($result); - } -} diff --git a/tests/ConstraintTest.php b/tests/ConstraintTest.php deleted file mode 100644 index 0ac3375d..00000000 --- a/tests/ConstraintTest.php +++ /dev/null @@ -1,58 +0,0 @@ -getMockedSize(800, 600); - $constraint = new Constraint($size); - $this->assertInstanceOf('Intervention\Image\Constraint', $constraint); - $this->assertFalse($constraint->isFixed(Constraint::ASPECTRATIO)); - $this->assertFalse($constraint->isFixed(Constraint::UPSIZE)); - } - - public function testSetOnlyAspectRatio() - { - $size = $this->getMockedSize(800, 600); - $constraint = new Constraint($size); - $constraint->aspectRatio(); - $this->assertTrue($constraint->isFixed(Constraint::ASPECTRATIO)); - $this->assertFalse($constraint->isFixed(Constraint::UPSIZE)); - } - - public function testSetOnlyUpsize() - { - $size = $this->getMockedSize(800, 600); - $constraint = new Constraint($size); - $constraint->upsize(); - $this->assertFalse($constraint->isFixed(Constraint::ASPECTRATIO)); - $this->assertTrue($constraint->isFixed(Constraint::UPSIZE)); - } - - public function testSetAspectratioAndUpsize() - { - $size = $this->getMockedSize(800, 600); - $constraint = new Constraint($size); - $constraint->aspectRatio(); - $constraint->upsize(); - $this->assertTrue($constraint->isFixed(Constraint::ASPECTRATIO)); - $this->assertTrue($constraint->isFixed(Constraint::UPSIZE)); - } - - private function getMockedSize($width, $height) - { - $size = Mockery::mock('\Intervention\Image\Size', [$width, $height]); - $size->shouldReceive('getWidth')->andReturn($width); - $size->shouldReceive('getHeight')->andReturn($height); - $size->shouldReceive('getRatio')->andReturn($width/$height); - return $size; - } -} diff --git a/tests/ContrastCommandTest.php b/tests/ContrastCommandTest.php deleted file mode 100644 index 9b81917a..00000000 --- a/tests/ContrastCommandTest.php +++ /dev/null @@ -1,34 +0,0 @@ -shouldReceive('getCore')->once()->andReturn($resource); - $command = new ContrastGd([20]); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testImagick() - { - $imagick = Mockery::mock('Imagick'); - $imagick->shouldReceive('sigmoidalcontrastimage')->with(true, 5, 0)->andReturn(true); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($imagick); - $command = new ContrastImagick([20]); - $result = $command->execute($image); - $this->assertTrue($result); - } -} diff --git a/tests/CropCommandTest.php b/tests/CropCommandTest.php deleted file mode 100644 index 23ac95ba..00000000 --- a/tests/CropCommandTest.php +++ /dev/null @@ -1,36 +0,0 @@ -shouldReceive('getCore')->once()->andReturn($resource); - $image->shouldReceive('setCore')->once(); - $command = new CropGd([100, 150, 10, 20]); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testImagick() - { - $imagick = Mockery::mock('Imagick'); - $imagick->shouldReceive('cropimage')->with(100, 150, 10, 20)->andReturn(true); - $imagick->shouldReceive('setimagepage')->with(0, 0, 0, 0)->once(); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getCore')->times(2)->andReturn($imagick); - $command = new CropImagick([100, 150, 10, 20]); - $result = $command->execute($image); - $this->assertTrue($result); - } -} diff --git a/tests/DestroyCommandTest.php b/tests/DestroyCommandTest.php deleted file mode 100644 index cc788463..00000000 --- a/tests/DestroyCommandTest.php +++ /dev/null @@ -1,46 +0,0 @@ -shouldReceive('getCore')->once()->andReturn($resource); - $image->shouldReceive('getBackups')->once()->andReturn($backups); - $command = new DestroyGd([]); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testImagick() - { - $imagick = Mockery::mock('Imagick'); - $imagick->shouldReceive('clear')->with()->andReturn(true); - - $backup = Mockery::mock('Imagick'); - $backup->shouldReceive('clear')->with()->andReturn(true); - $backups = [$backup]; - - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($imagick); - $image->shouldReceive('getBackups')->once()->andReturn($backups); - $command = new DestroyImagick([]); - $result = $command->execute($image); - $this->assertTrue($result); - } -} diff --git a/tests/DriverTest.php b/tests/DriverTest.php deleted file mode 100644 index 5cd8a36e..00000000 --- a/tests/DriverTest.php +++ /dev/null @@ -1,61 +0,0 @@ -newImage(300, 200, '00ff00'); - $this->assertInstanceOf('\Intervention\Image\Image', $image); - $this->assertInstanceOf('\Intervention\Image\Gd\Driver', $image->getDriver()); - $this->assertInternalType('resource', $image->getCore()); - } - - public function testNewImageImagick() - { - $driver = new ImagickDriver( - Mockery::mock('\Intervention\Image\Imagick\Decoder'), - Mockery::mock('\Intervention\Image\Imagick\Encoder') - ); - - $image = $driver->newImage(300, 200, '00ff00'); - $this->assertInstanceOf('\Intervention\Image\Image', $image); - $this->assertInstanceOf('\Intervention\Image\Imagick\Driver', $image->getDriver()); - $this->assertInstanceOf('\Imagick', $image->getCore()); - } - - public function testParseColorGd() - { - $driver = new GdDriver( - Mockery::mock('\Intervention\Image\Gd\Decoder'), - Mockery::mock('\Intervention\Image\Gd\Encoder') - ); - - $color = $driver->parseColor('00ff00'); - $this->assertInstanceOf('\Intervention\Image\Gd\Color', $color); - } - - public function testParseColorImagick() - { - $driver = new ImagickDriver( - Mockery::mock('\Intervention\Image\Imagick\Decoder'), - Mockery::mock('\Intervention\Image\Imagick\Encoder') - ); - - $color = $driver->parseColor('00ff00'); - $this->assertInstanceOf('\Intervention\Image\Imagick\Color', $color); - } -} diff --git a/tests/Drivers/Gd/ColorTest.php b/tests/Drivers/Gd/ColorTest.php new file mode 100644 index 00000000..848cadee --- /dev/null +++ b/tests/Drivers/Gd/ColorTest.php @@ -0,0 +1,75 @@ +assertInstanceOf(Color::class, $this->getTestColor()); + } + + public function testRed(): void + { + $color = $this->getTestColor(255, 0, 0); + $this->assertEquals(255, $color->red()); + } + + public function testGreen(): void + { + $color = $this->getTestColor(0, 150, 0); + $this->assertEquals(150, $color->green()); + } + + public function testBlue(): void + { + $color = $this->getTestColor(0, 0, 120); + $this->assertEquals(120, $color->blue()); + } + + public function testAlpha(): void + { + $color = $this->getTestColor(0, 0, 120, 0); + $this->assertEquals(1, $color->alpha()); + + $color = $this->getTestColor(0, 0, 120, 127); + $this->assertEquals(0, $color->alpha()); + + $color = $this->getTestColor(0, 0, 120, 64); + $this->assertEquals(.5, $color->alpha()); + } + + public function testToArray(): void + { + $color = $this->getTestColor(0, 0, 120, 0); + $this->assertEquals([0, 0, 120, 1], $color->toArray()); + + $color = $this->getTestColor(0, 0, 120, 127); + $this->assertEquals([0, 0, 120, 0], $color->toArray()); + + $color = $this->getTestColor(0, 0, 120, 64); + $this->assertEquals([0, 0, 120, .5], $color->toArray()); + } + + public function testToHex(): void + { + $color = $this->getTestColor(181, 55, 23); + $this->assertEquals('b53717', $color->toHex()); + $this->assertEquals('#b53717', $color->toHex('#')); + + $color = $this->getTestColor(181, 55, 23, 127); + $this->assertEquals('b53717', $color->toHex()); + $this->assertEquals('#b53717', $color->toHex('#')); + } +} diff --git a/tests/Drivers/Gd/Decoders/ArrayColorDecoderTest.php b/tests/Drivers/Gd/Decoders/ArrayColorDecoderTest.php new file mode 100644 index 00000000..585d864a --- /dev/null +++ b/tests/Drivers/Gd/Decoders/ArrayColorDecoderTest.php @@ -0,0 +1,21 @@ +decode([181, 55, 23, .5]); + $this->assertInstanceOf(Color::class, $color); + $this->assertEquals(181, $color->red()); + $this->assertEquals(55, $color->green()); + $this->assertEquals(23, $color->blue()); + $this->assertEquals(.5, $color->alpha()); + } +} diff --git a/tests/Drivers/Gd/Decoders/BinaryImageDecoderTest.php b/tests/Drivers/Gd/Decoders/BinaryImageDecoderTest.php new file mode 100644 index 00000000..3ccb4f4a --- /dev/null +++ b/tests/Drivers/Gd/Decoders/BinaryImageDecoderTest.php @@ -0,0 +1,40 @@ +decode(file_get_contents(__DIR__ . '/../../../images/tile.png')); + $this->assertInstanceOf(Image::class, $image); + $this->assertEquals(16, $image->width()); + $this->assertEquals(16, $image->height()); + $this->assertCount(1, $image); + } + + public function testDecodeGif(): void + { + $decoder = new BinaryImageDecoder(); + $image = $decoder->decode(file_get_contents(__DIR__ . '/../../../images/red.gif')); + $this->assertInstanceOf(Image::class, $image); + $this->assertEquals(16, $image->width()); + $this->assertEquals(16, $image->height()); + $this->assertCount(1, $image); + } + + public function testDecodeAnimatedGif(): void + { + $decoder = new BinaryImageDecoder(); + $image = $decoder->decode(file_get_contents(__DIR__ . '/../../../images/cats.gif')); + $this->assertInstanceOf(Image::class, $image); + $this->assertEquals(75, $image->width()); + $this->assertEquals(50, $image->height()); + $this->assertCount(4, $image); + } +} diff --git a/tests/Drivers/Gd/Encoders/GifEncoderTest.php b/tests/Drivers/Gd/Encoders/GifEncoderTest.php new file mode 100644 index 00000000..e668ce22 --- /dev/null +++ b/tests/Drivers/Gd/Encoders/GifEncoderTest.php @@ -0,0 +1,40 @@ +setDelay(1); + $frame2 = new Frame($gd2); + $frame2->setDelay(.2); + $frame3 = new Frame($gd3); + $frame3->setDelay(1); + + return new Image(new Collection([$frame1, $frame2, $frame3])); + } + + public function testEncode(): void + { + $image = $this->getTestImage(); + $encoder = new GifEncoder(); + $result = $encoder->encode($image); + $this->assertTrue(MimeSniffer::createFromString($result)->matches(new ImageGif())); + } +} diff --git a/tests/Drivers/Gd/Encoders/JpegEncoderTest.php b/tests/Drivers/Gd/Encoders/JpegEncoderTest.php new file mode 100644 index 00000000..17af9db1 --- /dev/null +++ b/tests/Drivers/Gd/Encoders/JpegEncoderTest.php @@ -0,0 +1,29 @@ +getTestImage(); + $encoder = new JpegEncoder(); + $result = $encoder->encode($image); + $this->assertTrue(MimeSniffer::createFromString($result)->matches(new ImageJpeg)); + } +} \ No newline at end of file diff --git a/tests/Drivers/Gd/FrameTest.php b/tests/Drivers/Gd/FrameTest.php new file mode 100644 index 00000000..b17feb51 --- /dev/null +++ b/tests/Drivers/Gd/FrameTest.php @@ -0,0 +1,79 @@ +getTestFrame(); + $this->assertInstanceOf(Frame::class, $frame); + } + + public function testSetGetDelay() + { + $frame = $this->getTestFrame(); + $this->assertEquals(0, $frame->getDelay()); + + $result = $frame->setDelay(100); + $this->assertInstanceOf(Frame::class, $result); + $this->assertEquals(100, $frame->getDelay()); + } + + public function testSetGetDispose() + { + $frame = $this->getTestFrame(); + $this->assertEquals(1, $frame->getDispose()); + + $result = $frame->setDispose(100); + $this->assertInstanceOf(Frame::class, $result); + $this->assertEquals(100, $frame->getDispose()); + } + + public function testSetGetOffsetLeft() + { + $frame = $this->getTestFrame(); + $this->assertEquals(0, $frame->getOffsetLeft()); + + $result = $frame->setOffsetLeft(100); + $this->assertInstanceOf(Frame::class, $result); + $this->assertEquals(100, $frame->getOffsetLeft()); + } + + public function testSetGetOffsetTop() + { + $frame = $this->getTestFrame(); + $this->assertEquals(0, $frame->getOffsetTop()); + + $result = $frame->setOffsetTop(100); + $this->assertInstanceOf(Frame::class, $result); + $this->assertEquals(100, $frame->getOffsetTop()); + } + + public function testSetGetOffset() + { + $frame = $this->getTestFrame(); + $this->assertEquals(0, $frame->getOffsetTop()); + $this->assertEquals(0, $frame->getOffsetLeft()); + + $result = $frame->setOffset(100, 200); + $this->assertInstanceOf(Frame::class, $result); + $this->assertEquals(100, $frame->getOffsetLeft()); + $this->assertEquals(200, $frame->getOffsetTop()); + } + + public function testToImage(): void + { + $frame = $this->getTestFrame(); + $this->assertInstanceOf(Image::class, $frame->toImage()); + } +} diff --git a/tests/Drivers/Gd/ImageFactoryTest.php b/tests/Drivers/Gd/ImageFactoryTest.php new file mode 100644 index 00000000..20bc9abd --- /dev/null +++ b/tests/Drivers/Gd/ImageFactoryTest.php @@ -0,0 +1,17 @@ +newImage(3, 2); + $this->assertInstanceOf(Image::class, $image); + } +} diff --git a/tests/Drivers/Gd/ImageTest.php b/tests/Drivers/Gd/ImageTest.php new file mode 100644 index 00000000..db5ddb78 --- /dev/null +++ b/tests/Drivers/Gd/ImageTest.php @@ -0,0 +1,46 @@ +image = new Image(new Collection([new Frame(imagecreatetruecolor(3, 2))])); + } + + public function testConstructor(): void + { + $this->assertInstanceOf(Image::class, $this->image); + } + + public function testIterator(): void + { + foreach ($this->image as $frame) { + $this->assertInstanceOf(Frame::class, $frame); + } + } + + public function testWidth(): void + { + $this->assertEquals(3, $this->image->width()); + } + + public function testHeight(): void + { + $this->assertEquals(2, $this->image->height()); + } + + public function testSize(): void + { + $this->assertInstanceOf(Size::class, $this->image->size()); + } +} diff --git a/tests/Drivers/Gd/InputHandlerTest.php b/tests/Drivers/Gd/InputHandlerTest.php new file mode 100644 index 00000000..f4d2d258 --- /dev/null +++ b/tests/Drivers/Gd/InputHandlerTest.php @@ -0,0 +1,62 @@ +expectException(DecoderException::class); + $result = $handler->handle(''); + } + + public function testHandleBinaryImage(): void + { + $handler = new InputHandler(); + $input = file_get_contents(__DIR__ . '/../../images/animation.gif'); + $result = $handler->handle($input); + $this->assertInstanceOf(Image::class, $result); + } + + public function testHandleFilePathImage(): void + { + $handler = new InputHandler(); + $input = __DIR__ . '/../../images/animation.gif'; + $result = $handler->handle($input); + $this->assertInstanceOf(Image::class, $result); + } + + public function testHandleBase64Image(): void + { + $handler = new InputHandler(); + $input = base64_encode(file_get_contents(__DIR__ . '/../../images/animation.gif')); + $result = $handler->handle($input); + $this->assertInstanceOf(Image::class, $result); + } + + public function testHandleDataUriImage(): void + { + $handler = new InputHandler(); + $input = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=='; + $result = $handler->handle($input); + $this->assertInstanceOf(Image::class, $result); + } + + public function testHandleArrayColor(): void + { + $handler = new InputHandler(); + $input = [181, 55, 23, .5]; + $result = $handler->handle($input); + $this->assertInstanceOf(Color::class, $result); + } +} diff --git a/tests/Drivers/Imagick/ColorTest.php b/tests/Drivers/Imagick/ColorTest.php new file mode 100644 index 00000000..2768c7f6 --- /dev/null +++ b/tests/Drivers/Imagick/ColorTest.php @@ -0,0 +1,87 @@ +assertInstanceOf(Color::class, $this->getTestColor()); + } + + public function testRed(): void + { + $color = $this->getTestColor(255, 0, 0); + $this->assertEquals(255, $color->red()); + } + + public function testGreen(): void + { + $color = $this->getTestColor(0, 150, 0); + $this->assertEquals(150, $color->green()); + } + + public function testBlue(): void + { + $color = $this->getTestColor(0, 0, 120); + $this->assertEquals(120, $color->blue()); + } + + public function testAlpha(): void + { + $color = $this->getTestColor(0, 0, 120, 1); + $this->assertEquals(1, $color->alpha()); + + $color = $this->getTestColor(0, 0, 120, 0); + $this->assertEquals(0, $color->alpha()); + + $color = $this->getTestColor(0, 0, 120, .5); + $this->assertEquals(.5, $color->alpha()); + + $color = $this->getTestColor(0, 0, 120, .57); + $this->assertEquals(.57, $color->alpha()); + + $color = $this->getTestColor(0, 0, 120, .578); + $this->assertEquals(.58, $color->alpha()); + } + + public function testToArray(): void + { + $color = $this->getTestColor(0, 0, 120, 1); + $this->assertEquals([0, 0, 120, 1], $color->toArray()); + + $color = $this->getTestColor(0, 0, 120, 0); + $this->assertEquals([0, 0, 120, 0], $color->toArray()); + + $color = $this->getTestColor(0, 0, 120, .5); + $this->assertEquals([0, 0, 120, .5], $color->toArray()); + + $color = $this->getTestColor(0, 0, 120, .57); + $this->assertEquals([0, 0, 120, .57], $color->toArray()); + + $color = $this->getTestColor(0, 0, 120, .578); + $this->assertEquals([0, 0, 120, .58], $color->toArray()); + } + + public function testToHex(): void + { + $color = $this->getTestColor(181, 55, 23); + $this->assertEquals('b53717', $color->toHex()); + $this->assertEquals('#b53717', $color->toHex('#')); + + $color = $this->getTestColor(181, 55, 23, 127); + $this->assertEquals('b53717', $color->toHex()); + $this->assertEquals('#b53717', $color->toHex('#')); + } +} diff --git a/tests/Drivers/Imagick/Decoders/ArrayColorDecoderTest.php b/tests/Drivers/Imagick/Decoders/ArrayColorDecoderTest.php new file mode 100644 index 00000000..e0e16800 --- /dev/null +++ b/tests/Drivers/Imagick/Decoders/ArrayColorDecoderTest.php @@ -0,0 +1,21 @@ +decode([181, 55, 23, .5]); + $this->assertInstanceOf(Color::class, $color); + $this->assertEquals(181, $color->red()); + $this->assertEquals(55, $color->green()); + $this->assertEquals(23, $color->blue()); + $this->assertEquals(.5, $color->alpha()); + } +} diff --git a/tests/Drivers/Imagick/Encoders/GifEncoderTest.php b/tests/Drivers/Imagick/Encoders/GifEncoderTest.php new file mode 100644 index 00000000..9e51820e --- /dev/null +++ b/tests/Drivers/Imagick/Encoders/GifEncoderTest.php @@ -0,0 +1,42 @@ +newImage(30, 20, new ImagickPixel('red'), 'png'); + $frame1 = new Frame($imagick1); + $frame1->setDelay(50); + $imagick2 = new Imagick(); + $imagick2->newImage(30, 20, new ImagickPixel('green'), 'png'); + $frame2 = new Frame($imagick2); + $frame2->setDelay(50); + $imagick3 = new Imagick(); + $imagick3->newImage(30, 20, new ImagickPixel('blue'), 'png'); + $frame3 = new Frame($imagick3); + $frame3->setDelay(50); + + return new Image(new Collection([$frame1, $frame2, $frame3])); + } + + public function testEncode(): void + { + $image = $this->getTestImage(); + $encoder = new GifEncoder(); + $result = $encoder->encode($image); + $this->assertTrue(MimeSniffer::createFromString($result)->matches(new ImageGif())); + } +} diff --git a/tests/Drivers/Imagick/Encoders/JpegEncoderTest.php b/tests/Drivers/Imagick/Encoders/JpegEncoderTest.php new file mode 100644 index 00000000..7a01f54a --- /dev/null +++ b/tests/Drivers/Imagick/Encoders/JpegEncoderTest.php @@ -0,0 +1,33 @@ +newImage(3, 2, new ImagickPixel('red'), 'png'); + $frame = new Frame($imagick); + + return new Image(new Collection([$frame])); + } + + public function testEncode(): void + { + $image = $this->getTestImage(); + $encoder = new JpegEncoder(); + $result = $encoder->encode($image); + $this->assertTrue(MimeSniffer::createFromString($result)->matches(new ImageJpeg)); + } +} \ No newline at end of file diff --git a/tests/Drivers/Imagick/FrameTest.php b/tests/Drivers/Imagick/FrameTest.php new file mode 100644 index 00000000..c7c2a26d --- /dev/null +++ b/tests/Drivers/Imagick/FrameTest.php @@ -0,0 +1,87 @@ +newImage(3, 2, new ImagickPixel('red'), 'png'); + $imagick->setImageDelay(4); + $imagick->setImageDispose(5); + $imagick->setImagePage(3, 2, 8, 9); + + return new Frame($imagick); + } + + public function testConstructor(): void + { + $frame = $this->getTestFrame(); + $this->assertInstanceOf(Frame::class, $frame); + } + + public function testSetGetDelay() + { + $frame = $this->getTestFrame(); + $this->assertEquals(4, $frame->getDelay()); + + $result = $frame->setDelay(100); + $this->assertInstanceOf(Frame::class, $result); + $this->assertEquals(100, $frame->getDelay()); + } + + public function testSetGetDispose() + { + $frame = $this->getTestFrame(); + $this->assertEquals(5, $frame->getDispose()); + + $result = $frame->setDispose(100); + $this->assertInstanceOf(Frame::class, $result); + $this->assertEquals(100, $frame->getDispose()); + } + + public function testSetGetOffsetLeft() + { + $frame = $this->getTestFrame(); + $this->assertEquals(8, $frame->getOffsetLeft()); + + $result = $frame->setOffsetLeft(100); + $this->assertInstanceOf(Frame::class, $result); + $this->assertEquals(100, $frame->getOffsetLeft()); + } + + public function testSetGetOffsetTop() + { + $frame = $this->getTestFrame(); + $this->assertEquals(9, $frame->getOffsetTop()); + + $result = $frame->setOffsetTop(100); + $this->assertInstanceOf(Frame::class, $result); + $this->assertEquals(100, $frame->getOffsetTop()); + } + + public function testSetGetOffset() + { + $frame = $this->getTestFrame(); + $this->assertEquals(8, $frame->getOffsetLeft()); + $this->assertEquals(9, $frame->getOffsetTop()); + + $result = $frame->setOffset(100, 200); + $this->assertInstanceOf(Frame::class, $result); + $this->assertEquals(100, $frame->getOffsetLeft()); + $this->assertEquals(200, $frame->getOffsetTop()); + } + + public function testToImage(): void + { + $frame = $this->getTestFrame(); + $this->assertInstanceOf(Image::class, $frame->toImage()); + } +} diff --git a/tests/Drivers/Imagick/ImageTest.php b/tests/Drivers/Imagick/ImageTest.php new file mode 100644 index 00000000..611825d0 --- /dev/null +++ b/tests/Drivers/Imagick/ImageTest.php @@ -0,0 +1,51 @@ +newImage(3, 2, new ImagickPixel('red'), 'png'); + + $this->image = new Image(new Collection([new Frame($imagick)])); + } + + public function testConstructor(): void + { + $this->assertInstanceOf(Image::class, $this->image); + } + + public function testIterator(): void + { + foreach ($this->image as $frame) { + $this->assertInstanceOf(Frame::class, $frame); + } + } + + public function testWidth(): void + { + $this->assertEquals(3, $this->image->width()); + } + + public function testHeight(): void + { + $this->assertEquals(2, $this->image->height()); + } + + public function testSize(): void + { + $this->assertInstanceOf(Size::class, $this->image->size()); + } +} diff --git a/tests/Drivers/Imagick/InputHandlerTest.php b/tests/Drivers/Imagick/InputHandlerTest.php new file mode 100644 index 00000000..6bc689ba --- /dev/null +++ b/tests/Drivers/Imagick/InputHandlerTest.php @@ -0,0 +1,62 @@ +expectException(DecoderException::class); + $result = $handler->handle(''); + } + + public function testHandleBinaryImage(): void + { + $handler = new InputHandler(); + $input = file_get_contents(__DIR__ . '/../../images/animation.gif'); + $result = $handler->handle($input); + $this->assertInstanceOf(Image::class, $result); + } + + public function testHandleFilePathImage(): void + { + $handler = new InputHandler(); + $input = __DIR__ . '/../../images/animation.gif'; + $result = $handler->handle($input); + $this->assertInstanceOf(Image::class, $result); + } + + public function testHandleBase64Image(): void + { + $handler = new InputHandler(); + $input = base64_encode(file_get_contents(__DIR__ . '/../../images/animation.gif')); + $result = $handler->handle($input); + $this->assertInstanceOf(Image::class, $result); + } + + public function testHandleDataUriImage(): void + { + $handler = new InputHandler(); + $input = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=='; + $result = $handler->handle($input); + $this->assertInstanceOf(Image::class, $result); + } + + public function testHandleArrayColor(): void + { + $handler = new InputHandler(); + $input = [181, 55, 23, .5]; + $result = $handler->handle($input); + $this->assertInstanceOf(Color::class, $result); + } +} diff --git a/tests/EllipseCommandTest.php b/tests/EllipseCommandTest.php deleted file mode 100644 index d3b8b2b3..00000000 --- a/tests/EllipseCommandTest.php +++ /dev/null @@ -1,43 +0,0 @@ -shouldReceive('getDriverName')->once()->andReturn('Gd'); - $image = Mockery::mock('\Intervention\Image\Image'); - $image->shouldReceive('getDriver')->once()->andReturn($driver); - $image->shouldReceive('getCore')->once()->andReturn($resource); - $command = new EllipseCommand([250, 150, 10, 20]); - $result = $command->execute($image); - $this->assertTrue($result); - $this->assertFalse($command->hasOutput()); - } - - public function testImagick() - { - $imagick = Mockery::mock('\Imagick'); - $imagick->shouldReceive('drawimage'); - $driver = Mockery::mock('\Intervention\Image\Imagick\Driver'); - $driver->shouldReceive('getDriverName')->once()->andReturn('Imagick'); - $image = Mockery::mock('\Intervention\Image\Image'); - $image->shouldReceive('getDriver')->once()->andReturn($driver); - $image->shouldReceive('getCore')->once()->andReturn($imagick); - - $command = new EllipseCommand([250, 150, 10, 20]); - $result = $command->execute($image); - $this->assertTrue($result); - $this->assertFalse($command->hasOutput()); - } - -} diff --git a/tests/EllipseShapeTest.php b/tests/EllipseShapeTest.php deleted file mode 100644 index 9c857275..00000000 --- a/tests/EllipseShapeTest.php +++ /dev/null @@ -1,55 +0,0 @@ -assertInstanceOf('Intervention\Image\Gd\Shapes\EllipseShape', $ellipse); - $this->assertEquals(250, $ellipse->width); - $this->assertEquals(150, $ellipse->height); - - } - - public function testGdApplyToImage() - { - $core = imagecreatetruecolor(300, 200); - $image = Mockery::mock('\Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($core); - $ellipse = new EllipseGd(250, 150); - $result = $ellipse->applyToImage($image, 10, 20); - $this->assertInstanceOf('Intervention\Image\Gd\Shapes\EllipseShape', $ellipse); - $this->assertTrue($result); - } - - public function testImagickConstructor() - { - $ellipse = new EllipseImagick(250, 150); - $this->assertInstanceOf('Intervention\Image\Imagick\Shapes\EllipseShape', $ellipse); - $this->assertEquals(250, $ellipse->width); - $this->assertEquals(150, $ellipse->height); - - } - - public function testImagickApplyToImage() - { - $core = Mockery::mock('\Imagick'); - $core->shouldReceive('drawimage')->once(); - $image = Mockery::mock('\Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($core); - $ellipse = new EllipseImagick(250, 150); - $result = $ellipse->applyToImage($image, 10, 20); - $this->assertInstanceOf('Intervention\Image\Imagick\Shapes\EllipseShape', $ellipse); - $this->assertTrue($result); - } - -} diff --git a/tests/EncoderTest.php b/tests/EncoderTest.php deleted file mode 100644 index f68818df..00000000 --- a/tests/EncoderTest.php +++ /dev/null @@ -1,339 +0,0 @@ -shouldReceive('getCore')->once()->andReturn($core); - $image->shouldReceive('setEncoded')->once()->andReturn($image); - $img = $encoder->process($image, 'jpg', 90); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertEquals('image/jpeg; charset=binary', $this->getMime($encoder->result)); - } - - public function testProcessPngGd() - { - $core = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); - $encoder = new GdEncoder; - $image = Mockery::mock('\Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($core); - $image->shouldReceive('setEncoded')->once()->andReturn($image); - $img = $encoder->process($image, 'png', 90); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertEquals('image/png; charset=binary', $this->getMime($encoder->result)); - } - - public function testProcessGifGd() - { - $core = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); - $encoder = new GdEncoder; - $image = Mockery::mock('\Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($core); - $image->shouldReceive('setEncoded')->once()->andReturn($image); - $img = $encoder->process($image, 'gif', 90); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertEquals('image/gif; charset=binary', $this->getMime($encoder->result)); - } - - public function testProcessWebpGd() - { - if (function_exists('imagewebp')) { - $core = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); - $encoder = new GdEncoder; - $image = Mockery::mock('\Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($core); - $image->shouldReceive('setEncoded')->once()->andReturn($image); - $img = $encoder->process($image, 'webp', 90); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertEquals('image/webp; charset=binary', $this->getMime($encoder->result)); - } - } - - public function testProcessWebpGdWithUnSupportedPalette() - { - if (function_exists('imagewebp')) { - $core = imagecreatefrompng(__DIR__.'/images/black-friday.png'); - $encoder = new GdEncoder; - $image = Mockery::mock('\Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($core); - $image->shouldReceive('setEncoded')->once()->andReturn($image); - $img = $encoder->process($image, 'webp', 90); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertEquals('image/webp; charset=binary', $this->getMime($encoder->result)); - } - } - - public function testProcessAvifGd() - { - if (function_exists('imageavif')) { - $core = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); - $encoder = new GdEncoder; - $image = Mockery::mock('\Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($core); - $image->shouldReceive('setEncoded')->once()->andReturn($image); - $img = $encoder->process($image, 'avif', 90); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertEquals('image/avif; charset=binary', $this->getMime($encoder->result)); - } - } - - /** - * @expectedException \Intervention\Image\Exception\NotSupportedException - */ - public function testProcessHeicGd() - { - $core = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); - $encoder = new GdEncoder; - $image = Mockery::mock('\Intervention\Image\Image'); - $img = $encoder->process($image, 'heic', 90); - $this->assertInstanceOf('Intervention\Image\Image', $img); - } - - /** - * @expectedException \Intervention\Image\Exception\NotSupportedException - */ - public function testProcessTiffGd() - { - $core = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); - $encoder = new GdEncoder; - $image = Mockery::mock('\Intervention\Image\Image'); - $img = $encoder->process($image, 'tif', 90); - $this->assertInstanceOf('Intervention\Image\Image', $img); - } - - public function testProcessBmpGd() - { - if (function_exists('imagebmp')) { - $core = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); - $encoder = new GdEncoder; - $image = Mockery::mock('\Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($core); - $image->shouldReceive('setEncoded')->once()->andReturn($image); - $img = $encoder->process($image, 'bmp', 90); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertEquals('image/x-ms-bmp; charset=binary', $this->getMime($encoder->result)); - } - } - - /** - * @expectedException \Intervention\Image\Exception\NotSupportedException - */ - public function testProcessIcoGd() - { - $core = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); - $encoder = new GdEncoder; - $image = Mockery::mock('\Intervention\Image\Image'); - $img = $encoder->process($image, 'ico', 90); - $this->assertInstanceOf('Intervention\Image\Image', $img); - } - - /** - * @expectedException \Intervention\Image\Exception\NotSupportedException - */ - public function testProcessPsdGd() - { - $core = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); - $encoder = new GdEncoder; - $image = Mockery::mock('\Intervention\Image\Image'); - $img = $encoder->process($image, 'psd', 90); - $this->assertInstanceOf('Intervention\Image\Image', $img); - } - - public function testProcessUnknownWithMimeGd() - { - $core = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); - $encoder = new GdEncoder; - $image = Mockery::mock('\Intervention\Image\Image'); - $image->mime = 'image/jpeg'; - $image->shouldReceive('getCore')->once()->andReturn($core); - $image->shouldReceive('setEncoded')->once()->andReturn($image); - $img = $encoder->process($image, null); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertEquals('image/jpeg; charset=binary', $this->getMime($encoder->result)); - } - - public function testProcessUnknownGd() - { - $core = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); - $encoder = new GdEncoder; - $image = Mockery::mock('\Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($core); - $image->shouldReceive('setEncoded')->once()->andReturn($image); - $img = $encoder->process($image, null); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertEquals('image/jpeg; charset=binary', $this->getMime($encoder->result)); - } - - public function testProcessJpegImagick() - { - $core = $this->getImagickMock('jpeg'); - $encoder = new ImagickEncoder; - $image = Mockery::mock('\Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($core); - $image->shouldReceive('setEncoded')->once()->andReturn($image); - $img = $encoder->process($image, 'jpg', 90); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertEquals('mock-jpeg', $encoder->result); - } - - public function testProcessPngImagick() - { - $core = $this->getImagickMock('png'); - $encoder = new ImagickEncoder; - $image = Mockery::mock('\Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($core); - $image->shouldReceive('setEncoded')->once()->andReturn($image); - $img = $encoder->process($image, 'png', 90); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertEquals('mock-png', $encoder->result); - } - - public function testProcessGifImagick() - { - $core = $this->getImagickMock('gif'); - $encoder = new ImagickEncoder; - $image = Mockery::mock('\Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($core); - $image->shouldReceive('setEncoded')->once()->andReturn($image); - $img = $encoder->process($image, 'gif', 90); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertEquals('mock-gif', $encoder->result); - } - - /** - * @expectedException \Intervention\Image\Exception\NotSupportedException - */ - public function testProcessWebpImagick() - { - $encoder = new ImagickEncoder; - $image = Mockery::mock('\Intervention\Image\Image'); - $img = $encoder->process($image, 'webp', 90); - } - - /** - * @expectedException \Intervention\Image\Exception\NotSupportedException - */ - public function testProcessAvifImagick() - { - $encoder = new ImagickEncoder; - $image = Mockery::mock('\Intervention\Image\Image'); - $img = $encoder->process($image, 'avif', 90); - } - - /** - * @expectedException \Intervention\Image\Exception\NotSupportedException - */ - public function testProcessHeicImagick() - { - $encoder = new ImagickEncoder; - $image = Mockery::mock('\Intervention\Image\Image'); - $img = $encoder->process($image, 'heic', 90); - } - - public function testProcessTiffImagick() - { - $core = $this->getImagickMock('tiff'); - $encoder = new ImagickEncoder; - $image = Mockery::mock('\Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($core); - $image->shouldReceive('setEncoded')->once()->andReturn($image); - $img = $encoder->process($image, 'tiff', 90); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertEquals('mock-tiff', $encoder->result); - } - - public function testProcessBmpImagick() - { - $core = $this->getImagickMock('bmp'); - $encoder = new ImagickEncoder; - $image = Mockery::mock('\Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($core); - $image->shouldReceive('setEncoded')->once()->andReturn($image); - $img = $encoder->process($image, 'bmp', 90); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertEquals('mock-bmp', $encoder->result); - } - - public function testProcessIcoImagick() - { - $core = $this->getImagickMock('ico'); - $encoder = new ImagickEncoder; - $image = Mockery::mock('\Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($core); - $image->shouldReceive('setEncoded')->once()->andReturn($image); - $img = $encoder->process($image, 'ico', 90); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertEquals('mock-ico', $encoder->result); - } - - public function testProcessPsdImagick() - { - $core = $this->getImagickMock('psd'); - $encoder = new ImagickEncoder; - $image = Mockery::mock('\Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($core); - $image->shouldReceive('setEncoded')->once()->andReturn($image); - $img = $encoder->process($image, 'psd', 90); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertEquals('mock-psd', $encoder->result); - } - - public function testProcessUnknownWithMimeImagick() - { - $core = $this->getImagickMock('jpeg'); - $encoder = new ImagickEncoder; - $image = Mockery::mock('\Intervention\Image\Image'); - $image->mime = 'image/jpeg'; - $image->shouldReceive('getCore')->once()->andReturn($core); - $image->shouldReceive('setEncoded')->once()->andReturn($image); - $img = $encoder->process($image, null); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertEquals('mock-jpeg', $encoder->result); - } - - public function testProcessUnknownImagick() - { - $core = $this->getImagickMock('jpeg'); - $encoder = new ImagickEncoder; - $image = Mockery::mock('\Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($core); - $image->shouldReceive('setEncoded')->once()->andReturn($image); - $img = $encoder->process($image, null); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertEquals('mock-jpeg', $encoder->result); - } - - public function getImagickMock($type) - { - $imagick = Mockery::mock('Imagick'); - $imagick->shouldReceive('setformat')->with($type)->once(); - $imagick->shouldReceive('setimageformat')->once(); - $imagick->shouldReceive('setcompression')->once(); - $imagick->shouldReceive('setimagecompression')->once(); - $imagick->shouldReceive('setcompressionquality'); - $imagick->shouldReceive('setimagecompressionquality'); - $imagick->shouldReceive('setimagebackgroundcolor'); - $imagick->shouldReceive('setbackgroundcolor'); - $imagick->shouldReceive('mergeimagelayers')->andReturn($imagick); - $imagick->shouldReceive('getimagesblob')->once()->andReturn(sprintf('mock-%s', $type)); - return $imagick; - } - - public function getMime($data) - { - $finfo = new finfo(FILEINFO_MIME); - return $finfo->buffer($data); - } -} diff --git a/tests/ExifCommandTest.php b/tests/ExifCommandTest.php deleted file mode 100644 index 8b051c95..00000000 --- a/tests/ExifCommandTest.php +++ /dev/null @@ -1,112 +0,0 @@ -dirname = __DIR__.'/images'; - $image->basename = 'exif.jpg'; - $command = new ExifCommand([]); - $result = $command->execute($image); - $this->assertTrue($result); - $this->assertTrue($command->hasOutput()); - $this->assertInternalType('array', $command->getOutput()); - } - - public function testFetchDefined() - { - $image = new Image; - $image->dirname = __DIR__.'/images'; - $image->basename = 'exif.jpg'; - $command = new ExifCommand(['Artist']); - $result = $command->execute($image); - $this->assertTrue($result); - $this->assertTrue($command->hasOutput()); - $this->assertEquals('Oliver Vogel', $command->getOutput()); - } - - public function testFetchNonExisting() - { - $image = new Image; - $image->dirname = __DIR__.'/images'; - $image->basename = 'exif.jpg'; - $command = new ExifCommand(['xxx']); - $result = $command->execute($image); - $this->assertTrue($result); - $this->assertTrue($command->hasOutput()); - $this->assertEquals(null, $command->getOutput()); - } - - public function testFetchFromPng() - { - $image = new Image; - $image->dirname = __DIR__.'/images'; - $image->basename = 'star.png'; - $command = new ExifCommand(['Orientation']); - $result = $command->execute($image); - $this->assertTrue($result); - $this->assertTrue($command->hasOutput()); - $this->assertEquals(null, $command->getOutput()); - } - - public function testImagickFetchAll() - { - $image = $this->imagick()->make(__DIR__.'/images/exif.jpg'); - $command = new \Intervention\Image\Imagick\Commands\ExifCommand([]); - $command->dontPreferExtension(); - $result = $command->execute($image); - $this->assertTrue($result); - $this->assertTrue($command->hasOutput()); - $this->assertInternalType('array', $command->getOutput()); - $this->assertEquals('Oliver Vogel', $command->getOutput()['Artist']); - } - - public function testImagickFetchDefined() - { - $image = $this->imagick()->make(__DIR__.'/images/exif.jpg'); - $command = new \Intervention\Image\Imagick\Commands\ExifCommand(['Artist']); - $command->dontPreferExtension(); - $result = $command->execute($image); - $this->assertTrue($result); - $this->assertTrue($command->hasOutput()); - $this->assertEquals('Oliver Vogel', $command->getOutput()); - } - - public function testImagickNonExisting() - { - $image = $this->imagick()->make(__DIR__.'/images/exif.jpg'); - $command = new \Intervention\Image\Imagick\Commands\ExifCommand(['xx']); - $command->dontPreferExtension(); - $result = $command->execute($image); - $this->assertTrue($result); - $this->assertTrue($command->hasOutput()); - $this->assertEquals(null, $command->getOutput()); - } - - public function testImagickFallbackToExifExtenstion() - { - $image = $this->imagick()->make(__DIR__.'/images/exif.jpg'); - $command = new \Intervention\Image\Imagick\Commands\ExifCommand(['Artist']); - $result = $command->execute($image); - $this->assertTrue($result); - $this->assertTrue($command->hasOutput()); - $this->assertEquals('Oliver Vogel', $command->getOutput()); - } - - private function imagick() - { - return new \Intervention\Image\ImageManager([ - 'driver' => 'imagick' - ]); - } -} diff --git a/tests/FileTest.php b/tests/FileTest.php deleted file mode 100644 index e1c01fd2..00000000 --- a/tests/FileTest.php +++ /dev/null @@ -1,28 +0,0 @@ -setFileInfoFromPath('tests/images/test.jpg'); - $this->assertEquals('tests/images', $file->dirname); - $this->assertEquals('test.jpg', $file->basename); - $this->assertEquals('jpg', $file->extension); - $this->assertEquals('test', $file->filename); - $this->assertEquals('image/jpeg', $file->mime); - } - - public function testBasePath() - { - $file = new File; - $this->assertNull(null, $file->basePath() ?: ''); - - $file->dirname = 'foo'; - $file->basename = 'bar'; - $this->assertEquals('foo/bar', $file->basePath()); - } -} diff --git a/tests/FillCommandTest.php b/tests/FillCommandTest.php deleted file mode 100644 index cbd3f1ed..00000000 --- a/tests/FillCommandTest.php +++ /dev/null @@ -1,93 +0,0 @@ -shouldReceive('getCore')->once()->andReturn($resource); - $image->shouldReceive('getWidth')->once()->andReturn(800); - $image->shouldReceive('getHeight')->once()->andReturn(600); - $command = new FillGd(['666666']); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testGdFillArray() - { - $resource = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($resource); - $image->shouldReceive('getWidth')->once()->andReturn(800); - $image->shouldReceive('getHeight')->once()->andReturn(600); - $command = new FillGd([[50, 50, 50]]); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testGdFillArrayWithAlpha() - { - $resource = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($resource); - $image->shouldReceive('getWidth')->once()->andReturn(800); - $image->shouldReceive('getHeight')->once()->andReturn(600); - $command = new FillGd([[50, 50, 50, .50]]); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testGdFillWithCoordinates() - { - $driver = Mockery::mock('\Intervention\Image\Gd\Driver'); - $resource = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getDriver')->once()->andReturn($driver); - $image->shouldReceive('getCore')->times(2)->andReturn($resource); - $image->shouldReceive('getWidth')->once()->andReturn(800); - $image->shouldReceive('getHeight')->once()->andReturn(600); - $image->shouldReceive('setCore')->once(); - $driver->shouldReceive('newImage')->with(800, 600)->once()->andReturn($image); - $command = new FillGd(['#666666', 0, 0]); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testImagickFill() - { - $imagick = Mockery::mock('Imagick'); - $imagick->shouldReceive('drawimage')->once()->andReturn(true); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getWidth')->once()->andReturn(800); - $image->shouldReceive('getHeight')->once()->andReturn(600); - $image->shouldReceive('getCore')->andReturn($imagick); - $command = new FillImagick(['666666']); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testImagickFillWithCoordinates() - { - $imagick = Mockery::mock('Imagick'); - $imagick->shouldReceive('getimagepixelcolor')->once()->andReturn('#000000'); - $imagick->shouldReceive('transparentpaintimage')->once()->andReturn(true); - $imagick->shouldReceive('compositeimage')->times(3)->andReturn(true); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getCore')->andReturn($imagick); - $image->shouldReceive('getWidth')->andReturn(800); - $image->shouldReceive('getHeight')->andReturn(600); - $command = new FillImagick(['666666', 0, 0]); - $result = $command->execute($image); - $this->assertTrue($result); - } -} diff --git a/tests/FitCommandTest.php b/tests/FitCommandTest.php deleted file mode 100644 index 23b1cb3b..00000000 --- a/tests/FitCommandTest.php +++ /dev/null @@ -1,93 +0,0 @@ -shouldReceive('getWidth')->times(2)->andReturn(800); - $cropped_size->shouldReceive('getHeight')->times(2)->andReturn(400); - $cropped_size->shouldReceive('resize')->with(200, 100, null)->once()->andReturn($cropped_size); - $cropped_size->pivot = Mockery::mock('\Intervention\Image\Point', [0, 100]); - $original_size = Mockery::mock('\Intervention\Image\Size', [800, 600]); - $original_size->shouldReceive('fit')->with(Mockery::any(), 'center')->once()->andReturn($cropped_size); - $resource = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getSize')->once()->andReturn($original_size); - $image->shouldReceive('getCore')->once()->andReturn($resource); - $image->shouldReceive('setCore')->once(); - $command = new FitGd([200, 100]); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testGdFitWithPosition() - { - $cropped_size = Mockery::mock('\Intervention\Image\Size', [800, 400]); - $cropped_size->shouldReceive('getWidth')->times(2)->andReturn(800); - $cropped_size->shouldReceive('getHeight')->times(2)->andReturn(400); - $cropped_size->shouldReceive('resize')->with(200, 100, null)->once()->andReturn($cropped_size); - $cropped_size->pivot = Mockery::mock('\Intervention\Image\Point', [0, 100]); - $original_size = Mockery::mock('\Intervention\Image\Size', [800, 600]); - $original_size->shouldReceive('fit')->with(Mockery::any(), 'top-left')->once()->andReturn($cropped_size); - $resource = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getSize')->once()->andReturn($original_size); - $image->shouldReceive('getCore')->once()->andReturn($resource); - $image->shouldReceive('setCore')->once(); - $command = new FitGd([200, 100, null, 'top-left']); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testImagickFit() - { - $cropped_size = Mockery::mock('\Intervention\Image\Size', [800, 400]); - $cropped_size->shouldReceive('getWidth')->once()->andReturn(200); - $cropped_size->shouldReceive('getHeight')->once()->andReturn(100); - $cropped_size->shouldReceive('resize')->with(200, 100, null)->once()->andReturn($cropped_size); - $cropped_size->pivot = Mockery::mock('\Intervention\Image\Point', [0, 100]); - $original_size = Mockery::mock('\Intervention\Image\Size', [800, 600]); - $original_size->shouldReceive('fit')->with(Mockery::any(), 'center')->once()->andReturn($cropped_size); - $imagick = Mockery::mock('Imagick'); - $imagick->shouldReceive('cropimage')->with(800, 400, 0, 100)->andReturn(true); - $imagick->shouldReceive('scaleimage')->with(200, 100)->once()->andReturn(true); - $imagick->shouldReceive('setimagepage')->with(0, 0, 0, 0)->andReturn(true); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getSize')->once()->andReturn($original_size); - $image->shouldReceive('getCore')->times(3)->andReturn($imagick); - $command = new FitImagick([200, 100]); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testImagickFitWithPosition() - { - $cropped_size = Mockery::mock('\Intervention\Image\Size', [800, 400]); - $cropped_size->shouldReceive('getWidth')->once()->andReturn(200); - $cropped_size->shouldReceive('getHeight')->once()->andReturn(100); - $cropped_size->shouldReceive('resize')->with(200, 100, null)->once()->andReturn($cropped_size); - $cropped_size->pivot = Mockery::mock('\Intervention\Image\Point', [0, 100]); - $original_size = Mockery::mock('\Intervention\Image\Size', [800, 600]); - $original_size->shouldReceive('fit')->with(Mockery::any(), 'top-left')->once()->andReturn($cropped_size); - $imagick = Mockery::mock('Imagick'); - $imagick->shouldReceive('cropimage')->with(800, 400, 0, 100)->andReturn(true); - $imagick->shouldReceive('scaleimage')->with(200, 100)->once()->andReturn(true); - $imagick->shouldReceive('setimagepage')->with(0, 0, 0, 0)->andReturn(true); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getSize')->once()->andReturn($original_size); - $image->shouldReceive('getCore')->times(3)->andReturn($imagick); - $command = new FitImagick([200, 100, null, 'top-left']); - $result = $command->execute($image); - $this->assertTrue($result); - } -} diff --git a/tests/FlipCommandTest.php b/tests/FlipCommandTest.php deleted file mode 100644 index 09370eb6..00000000 --- a/tests/FlipCommandTest.php +++ /dev/null @@ -1,45 +0,0 @@ -shouldReceive('getSize')->once()->andReturn($size); - $image->shouldReceive('getCore')->once()->andReturn($resource); - $image->shouldReceive('setCore')->once(); - $command = new FlipGd(['h']); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testImagick() - { - $imagick = Mockery::mock('Imagick'); - $imagick->shouldReceive('flopimage')->with()->andReturn(true); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($imagick); - $command = new FlipImagick(['h']); - $result = $command->execute($image); - $this->assertTrue($result); - - $imagick = Mockery::mock('Imagick'); - $imagick->shouldReceive('flipimage')->with()->andReturn(true); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($imagick); - $command = new FlipImagick(['v']); - $result = $command->execute($image); - $this->assertTrue($result); - } -} diff --git a/tests/GammaCommandTest.php b/tests/GammaCommandTest.php deleted file mode 100644 index 6430236d..00000000 --- a/tests/GammaCommandTest.php +++ /dev/null @@ -1,34 +0,0 @@ -shouldReceive('getCore')->once()->andReturn($resource); - $command = new GammaGd([1.4]); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testImagick() - { - $imagick = Mockery::mock('Imagick'); - $imagick->shouldReceive('gammaimage')->with(1.4)->once()->andReturn(true); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($imagick); - $command = new GammaImagick([1.4]); - $result = $command->execute($image); - $this->assertTrue($result); - } -} diff --git a/tests/GdColorTest.php b/tests/GdColorTest.php deleted file mode 100644 index 7fa9f65f..00000000 --- a/tests/GdColorTest.php +++ /dev/null @@ -1,296 +0,0 @@ -validateColor($c, 255, 255, 255, 127); - } - - public function testParseNull() - { - $c = new Color; - $c->parse(null); - $this->validateColor($c, 255, 255, 255, 127); - } - - public function testParseInteger() - { - $c = new Color; - $c->parse(850736919); - $this->validateColor($c, 181, 55, 23, 50); - } - - public function testParseArray() - { - $c = new Color; - $c->parse([181, 55, 23, 0.5]); - $this->validateColor($c, 181, 55, 23, 64); - } - - public function testParseHexString() - { - $c = new Color; - $c->parse('#b53717'); - $this->validateColor($c, 181, 55, 23, 0); - } - - public function testParseRgbaString() - { - $c = new Color; - $c->parse('rgba(181, 55, 23, 1)'); - $this->validateColor($c, 181, 55, 23, 0); - } - - public function testInitFromInteger() - { - $c = new Color; - $c->initFromInteger(0); - $this->validateColor($c, 0, 0, 0, 0); - $c->initFromInteger(2147483647); - $this->validateColor($c, 255, 255, 255, 127); - $c->initFromInteger(16777215); - $this->validateColor($c, 255, 255, 255, 0); - $c->initFromInteger(2130706432); - $this->validateColor($c, 0, 0, 0, 127); - $c->initFromInteger(850736919); - $this->validateColor($c, 181, 55, 23, 50); - } - - public function testInitFromArray() - { - $c = new Color; - $c->initFromArray([0, 0, 0, 0]); - $this->validateColor($c, 0, 0, 0, 127); - $c->initFromArray([0, 0, 0, 1]); - $this->validateColor($c, 0, 0, 0, 0); - $c->initFromArray([255, 255, 255, 1]); - $this->validateColor($c, 255, 255, 255, 0); - $c->initFromArray([255, 255, 255, 0]); - $this->validateColor($c, 255, 255, 255, 127); - $c->initFromArray([255, 255, 255, 0.5]); - $this->validateColor($c, 255, 255, 255, 64); - $c->initFromArray([0, 0, 0]); - $this->validateColor($c, 0, 0, 0, 0); - $c->initFromArray([255, 255, 255]); - $this->validateColor($c, 255, 255, 255, 0); - $c->initFromArray([181, 55, 23]); - $this->validateColor($c, 181, 55, 23, 0); - $c->initFromArray([181, 55, 23, 0.5]); - $this->validateColor($c, 181, 55, 23, 64); - } - - public function testInitFromHexString() - { - $c = new Color; - $c->initFromString('#cccccc'); - $this->validateColor($c, 204, 204, 204, 0); - $c->initFromString('#b53717'); - $this->validateColor($c, 181, 55, 23, 0); - $c->initFromString('ffffff'); - $this->validateColor($c, 255, 255, 255, 0); - $c->initFromString('ff00ff'); - $this->validateColor($c, 255, 0, 255, 0); - $c->initFromString('#000'); - $this->validateColor($c, 0, 0, 0, 0); - $c->initFromString('000'); - $this->validateColor($c, 0, 0, 0, 0); - } - - public function testInitFromRgbString() - { - $c = new Color; - $c->initFromString('rgb(1, 14, 144)'); - $this->validateColor($c, 1, 14, 144, 0); - $c->initFromString('rgb (255, 255, 255)'); - $this->validateColor($c, 255, 255, 255, 0); - $c->initFromString('rgb(0,0,0)'); - $this->validateColor($c, 0, 0, 0, 0); - $c->initFromString('rgba(0,0,0,0)'); - $this->validateColor($c, 0, 0, 0, 127); - $c->initFromString('rgba(0,0,0,0.5)'); - $this->validateColor($c, 0, 0, 0, 64); - $c->initFromString('rgba(255, 0, 0, 0.5)'); - $this->validateColor($c, 255, 0, 0, 64); - $c->initFromString('rgba(204, 204, 204, 0.9)'); - $this->validateColor($c, 204, 204, 204, 13); - } - - public function testInitFromRgb() - { - $c = new Color; - $c->initFromRgb(0, 0, 0); - $this->validateColor($c, 0, 0, 0, 0); - $c->initFromRgb(255, 255, 255); - $this->validateColor($c, 255, 255, 255, 0); - $c->initFromRgb(181, 55, 23); - $this->validateColor($c, 181, 55, 23, 0); - } - - public function testInitFromRgba() - { - $c = new Color; - $c->initFromRgba(0, 0, 0, 1); - $this->validateColor($c, 0, 0, 0, 0); - $c->initFromRgba(255, 255, 255, 1); - $this->validateColor($c, 255, 255, 255, 0); - $c->initFromRgba(181, 55, 23, 1); - $this->validateColor($c, 181, 55, 23, 0); - $c->initFromRgba(181, 55, 23, 0); - $this->validateColor($c, 181, 55, 23, 127); - $c->initFromRgba(181, 55, 23, 0.5); - $this->validateColor($c, 181, 55, 23, 64); - } - - public function testGetInt() - { - $c = new Color; - $i = $c->getInt(); - $this->assertInternalType('int', $i); - $this->assertEquals(2147483647, $i); - - $c = new Color([255, 255, 255]); - $i = $c->getInt(); - $this->assertInternalType('int', $i); - $this->assertEquals($i, 16777215); - - $c = new Color([255, 255, 255, 1]); - $i = $c->getInt(); - $this->assertInternalType('int', $i); - $this->assertEquals($i, 16777215); - - $c = new Color([181, 55, 23, 0.5]); - $i = $c->getInt(); - $this->assertInternalType('int', $i); - $this->assertEquals($i, 1085617943); - - $c = new Color([181, 55, 23, 1]); - $i = $c->getInt(); - $this->assertInternalType('int', $i); - $this->assertEquals($i, 11876119); - - $c = new Color([0, 0, 0, 0]); - $i = $c->getInt(); - $this->assertInternalType('int', $i); - $this->assertEquals($i, 2130706432); - } - - public function testGetHex() - { - $c = new Color; - $i = $c->getHex(); - $this->assertInternalType('string', $i); - $this->assertEquals($i, 'ffffff'); - - $c = new Color([255, 255, 255, 1]); - $i = $c->getHex(); - $this->assertInternalType('string', $i); - $this->assertEquals($i, 'ffffff'); - - $c = new Color([181, 55, 23, 0.5]); - $i = $c->getHex(); - $this->assertInternalType('string', $i); - $this->assertEquals($i, 'b53717'); - - $c = new Color([0, 0, 0, 0]); - $i = $c->getHex('#'); - $this->assertInternalType('string', $i); - $this->assertEquals($i, '#000000'); - } - - public function testGetArray() - { - $c = new Color; - $i = $c->getArray(); - $this->assertInternalType('array', $i); - $this->assertEquals($i, [255, 255, 255, 0]); - - $c = new Color([255, 255, 255, 1]); - $i = $c->getArray(); - $this->assertInternalType('array', $i); - $this->assertEquals($i, [255, 255, 255, 1]); - - $c = new Color([181, 55, 23, 0.5]); - $i = $c->getArray(); - $this->assertInternalType('array', $i); - $this->assertEquals($i, [181, 55, 23, 0.5]); - - $c = new Color([0, 0, 0, 1]); - $i = $c->getArray(); - $this->assertInternalType('array', $i); - $this->assertEquals($i, [0, 0, 0, 1]); - } - - public function testGetRgba() - { - $c = new Color; - $i = $c->getRgba(); - $this->assertInternalType('string', $i); - $this->assertEquals($i, 'rgba(255, 255, 255, 0.00)'); - - $c = new Color([255, 255, 255, 1]); - $i = $c->getRgba(); - $this->assertInternalType('string', $i); - $this->assertEquals($i, 'rgba(255, 255, 255, 1.00)'); - - $c = new Color([181, 55, 23, 0.5]); - $i = $c->getRgba(); - $this->assertInternalType('string', $i); - $this->assertEquals($i, 'rgba(181, 55, 23, 0.50)'); - - $c = new Color([0, 0, 0, 1]); - $i = $c->getRgba(); - $this->assertInternalType('string', $i); - $this->assertEquals($i, 'rgba(0, 0, 0, 1.00)'); - } - - public function testDiffers() - { - $c1 = new Color([0, 0, 0]); - $c2 = new Color([0, 0, 0]); - $this->assertEquals(false, $c1->differs($c2)); - - $c1 = new Color([1, 0, 0]); - $c2 = new Color([0, 0, 0]); - $this->assertEquals(true, $c1->differs($c2)); - - $c1 = new Color([1, 0, 0]); - $c2 = new Color([0, 0, 0]); - $this->assertEquals(false, $c1->differs($c2, 10)); - - $c1 = new Color([127, 127, 127]); - $c2 = new Color([0, 0, 0]); - $this->assertEquals(true, $c1->differs($c2, 49)); - - $c1 = new Color([127, 127, 127]); - $c2 = new Color([0, 0, 0]); - $this->assertEquals(false, $c1->differs($c2, 50)); - } - - /** - * @expectedException \Intervention\Image\Exception\NotReadableException - */ - public function testParseUnknown() - { - $c = new Color('xxxxxxxxxxxxxxxxxxxx'); - } - - private function validateColor($obj, $r, $g, $b, $a) - { - $this->assertInstanceOf('Intervention\Image\Gd\Color', $obj); - $this->assertInternalType('int', $r); - $this->assertInternalType('int', $g); - $this->assertInternalType('int', $b); - $this->assertInternalType('int', $a); - $this->assertEquals($obj->r, $r); - $this->assertEquals($obj->g, $g); - $this->assertEquals($obj->b, $b); - $this->assertEquals($obj->a, $a); - } -} diff --git a/tests/GdSystemTest.php b/tests/GdSystemTest.php deleted file mode 100644 index 7d7d5ecd..00000000 --- a/tests/GdSystemTest.php +++ /dev/null @@ -1,1703 +0,0 @@ -manager()->make('tests/images/circle.png'); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInternalType('resource', $img->getCore()); - $this->assertInternalType('resource', $img->getCore()); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(50, $img->getWidth()); - $this->assertEquals(50, $img->getHeight()); - $this->assertEquals('image/png', $img->mime); - $this->assertEquals('tests/images', $img->dirname); - $this->assertEquals('circle.png', $img->basename); - $this->assertEquals('png', $img->extension); - $this->assertEquals('circle', $img->filename); - $this->assertEquals('image/png', $img->mime); - } - - /** - * @expectedException \Intervention\Image\Exception\NotReadableException - */ - public function testMakeFromPathBroken() - { - $this->manager()->make('tests/images/broken.png'); - } - - /** - * @expectedException \Intervention\Image\Exception\NotReadableException - */ - public function testMakeFromNotExisting() - { - $this->manager()->make('tests/images/not_existing.png'); - } - - public function testMakeFromString() - { - $str = file_get_contents('tests/images/circle.png'); - $img = $this->manager()->make($str); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInternalType('resource', $img->getCore()); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(50, $img->getWidth()); - $this->assertEquals(50, $img->getHeight()); - $this->assertEquals('image/png', $img->mime); - } - - public function testMakeFromResource() - { - $resource = imagecreatefrompng('tests/images/circle.png'); - $img = $this->manager()->make($resource); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInternalType('resource', $img->getCore()); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(50, $img->getWidth()); - $this->assertEquals(50, $img->getHeight()); - } - - public function testMakeFromDataUrl() - { - $img = $this->manager()->make('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAGElEQVQYlWM8c+bMfwYiABMxikYVUk8hAHWzA3cRvs4UAAAAAElFTkSuQmCC'); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInternalType('resource', $img->getCore()); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(10, $img->getWidth()); - $this->assertEquals(10, $img->getHeight()); - } - - public function testMakeFromBase64() - { - $img = $this->manager()->make('iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAGElEQVQYlWM8c+bMfwYiABMxikYVUk8hAHWzA3cRvs4UAAAAAElFTkSuQmCC'); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInternalType('resource', $img->getCore()); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(10, $img->getWidth()); - $this->assertEquals(10, $img->getHeight()); - } - - public function testMakeFromBase64WithNewlines() - { - $data = 'iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+' . "\n" . - '9AAAAGElEQVQYlWM8c+bMfwYiABMxikYVUk8hAHWzA3' . "\n" . - 'cRvs4UAAAAAElFTkSuQmCC'; - - $img = $this->manager()->make($data); - - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInternalType('resource', $img->getCore()); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(10, $img->getWidth()); - $this->assertEquals(10, $img->getHeight()); - } - - public function testMakeFromWebp() - { - if (function_exists('imagecreatefromwebp')) { - $img = $this->manager()->make('tests/images/test.webp'); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInternalType('resource', $img->getCore()); - $this->assertEquals(16, $img->getWidth()); - $this->assertEquals(16, $img->getHeight()); - $this->assertEquals('image/webp', $img->mime); - $this->assertEquals('tests/images', $img->dirname); - $this->assertEquals('test.webp', $img->basename); - $this->assertEquals('webp', $img->extension); - $this->assertEquals('test', $img->filename); - } - } - - public function testCanvas() - { - $img = $this->manager()->canvas(30, 20); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInternalType('resource', $img->getCore()); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(30, $img->getWidth()); - $this->assertEquals(20, $img->getHeight()); - $this->assertTransparentPosition($img, 0, 0); - } - - public function testCanvasWithSolidBackground() - { - $img = $this->manager()->canvas(30, 20, 'b53717'); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInternalType('resource', $img->getCore()); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(30, $img->getWidth()); - $this->assertEquals(20, $img->getHeight()); - $this->assertEquals('#b53717', $img->pickColor(15, 15, 'hex')); - } - - public function testGetSize() - { - $img = $this->manager()->make('tests/images/tile.png'); - $size = $img->getSize(); - $this->assertInstanceOf('Intervention\Image\Size', $size); - $this->assertInternalType('int', $size->width); - $this->assertInternalType('int', $size->height); - $this->assertEquals(16, $size->width); - $this->assertEquals(16, $size->height); - } - - public function testResizeImage() - { - $img = $this->manager()->make('tests/images/circle.png'); - $img->resize(120, 150); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInternalType('resource', $img->getCore()); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(120, $img->getWidth()); - $this->assertEquals(150, $img->getHeight()); - $this->assertTransparentPosition($img, 0, 0); - } - - public function testResizeImageOnlyWidth() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->resize(120, null); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInternalType('resource', $img->getCore()); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(120, $img->getWidth()); - $this->assertEquals(16, $img->getHeight()); - $this->assertTransparentPosition($img, 0, 15); - } - - public function testResizeImageOnlyHeight() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->resize(null, 150); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInternalType('resource', $img->getCore()); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(16, $img->getWidth()); - $this->assertEquals(150, $img->getHeight()); - $this->assertTransparentPosition($img, 15, 0); - } - - public function testResizeImageAutoHeight() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->resize(50, null, function ($constraint) { $constraint->aspectRatio(); }); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInternalType('resource', $img->getCore()); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(50, $img->getWidth()); - $this->assertEquals(50, $img->getHeight()); - $this->assertTransparentPosition($img, 30, 0); - } - - public function testResizeImageAutoWidth() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->resize(null, 50, function ($constraint) { $constraint->aspectRatio(); }); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInternalType('resource', $img->getCore()); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(50, $img->getWidth()); - $this->assertEquals(50, $img->getHeight()); - $this->assertTransparentPosition($img, 30, 0); - } - - public function testResizeDominantWidth() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->resize(100, 120, function ($constraint) { $constraint->aspectRatio(); }); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInternalType('resource', $img->getCore()); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(100, $img->getWidth()); - $this->assertEquals(100, $img->getHeight()); - $this->assertTransparentPosition($img, 60, 0); - } - - public function testResizeImagePreserveSimpleUpsizing() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->resize(100, 100, function ($constraint) { $constraint->aspectRatio(); $constraint->upsize(); }); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInternalType('resource', $img->getCore()); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(16, $img->getWidth()); - $this->assertEquals(16, $img->getHeight()); - $this->assertTransparentPosition($img, 15, 0); - } - - public function testWidenImage() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->widen(100); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInternalType('resource', $img->getCore()); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(100, $img->getWidth()); - $this->assertEquals(100, $img->getHeight()); - $this->assertTransparentPosition($img, 60, 0); - } - - public function testWidenImageWithConstraint() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->widen(100, function ($constraint) {$constraint->upsize();}); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInternalType('resource', $img->getCore()); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(16, $img->getWidth()); - $this->assertEquals(16, $img->getHeight()); - $this->assertTransparentPosition($img, 8, 0); - } - - public function testHeightenImage() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->heighten(100); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInternalType('resource', $img->getCore()); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(100, $img->getWidth()); - $this->assertEquals(100, $img->getHeight()); - $this->assertTransparentPosition($img, 60, 0); - } - - public function testHeightenImageWithConstraint() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->heighten(100, function ($constraint) {$constraint->upsize();}); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInternalType('resource', $img->getCore()); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(16, $img->getWidth()); - $this->assertEquals(16, $img->getHeight()); - $this->assertTransparentPosition($img, 8, 0); - } - - public function testResizeCanvasCenter() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->resizeCanvas(10, 10); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(10, $img->getWidth()); - $this->assertEquals(10, $img->getHeight()); - $this->assertColorAtPosition('#b4e000', $img, 0, 4); - $this->assertColorAtPosition('#445160', $img, 5, 5); - $this->assertTransparentPosition($img, 0, 5); - $this->assertTransparentPosition($img, 5, 4); - } - - public function testResizeCanvasTopLeft() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->resizeCanvas(10, 10, 'top-left'); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(10, $img->getWidth()); - $this->assertEquals(10, $img->getHeight()); - $this->assertColorAtPosition('#b4e000', $img, 0, 7); - $this->assertColorAtPosition('#445160', $img, 8, 8); - $this->assertTransparentPosition($img, 0, 8); - $this->assertTransparentPosition($img, 8, 7); - } - - public function testResizeCanvasTop() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->resizeCanvas(10, 10, 'top'); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(10, $img->getWidth()); - $this->assertEquals(10, $img->getHeight()); - $this->assertColorAtPosition('#b4e000', $img, 0, 7); - $this->assertColorAtPosition('#445160', $img, 5, 8); - $this->assertTransparentPosition($img, 0, 8); - $this->assertTransparentPosition($img, 5, 7); - } - - public function testResizeCanvasTopRight() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->resizeCanvas(10, 10, 'top-right'); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(10, $img->getWidth()); - $this->assertEquals(10, $img->getHeight()); - $this->assertColorAtPosition('#b4e000', $img, 0, 7); - $this->assertColorAtPosition('#445160', $img, 2, 8); - $this->assertTransparentPosition($img, 0, 8); - $this->assertTransparentPosition($img, 2, 7); - } - - public function testResizeCanvasLeft() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->resizeCanvas(10, 10, 'left'); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(10, $img->getWidth()); - $this->assertEquals(10, $img->getHeight()); - $this->assertColorAtPosition('#b4e000', $img, 0, 4); - $this->assertColorAtPosition('#445160', $img, 8, 5); - $this->assertTransparentPosition($img, 0, 5); - $this->assertTransparentPosition($img, 8, 4); - } - - public function testResizeCanvasRight() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->resizeCanvas(10, 10, 'right'); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(10, $img->getWidth()); - $this->assertEquals(10, $img->getHeight()); - $this->assertColorAtPosition('#b4e000', $img, 0, 4); - $this->assertColorAtPosition('#445160', $img, 2, 5); - $this->assertTransparentPosition($img, 0, 5); - $this->assertTransparentPosition($img, 2, 4); - } - - public function testResizeCanvasBottomLeft() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->resizeCanvas(10, 10, 'bottom-left'); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(10, $img->getWidth()); - $this->assertEquals(10, $img->getHeight()); - $this->assertColorAtPosition('#b4e000', $img, 0, 1); - $this->assertColorAtPosition('#445160', $img, 8, 2); - $this->assertTransparentPosition($img, 0, 2); - $this->assertTransparentPosition($img, 8, 1); - } - - public function testResizeCanvasBottomRight() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->resizeCanvas(10, 10, 'bottom-right'); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(10, $img->getWidth()); - $this->assertEquals(10, $img->getHeight()); - $this->assertColorAtPosition('#b4e000', $img, 0, 1); - $this->assertColorAtPosition('#445160', $img, 2, 2); - $this->assertTransparentPosition($img, 0, 2); - $this->assertTransparentPosition($img, 2, 1); - } - - public function testResizeCanvasBottom() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->resizeCanvas(10, 10, 'bottom'); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(10, $img->getWidth()); - $this->assertEquals(10, $img->getHeight()); - $this->assertColorAtPosition('#b4e000', $img, 0, 1); - $this->assertColorAtPosition('#445160', $img, 5, 2); - $this->assertTransparentPosition($img, 0, 2); - $this->assertTransparentPosition($img, 5, 1); - } - - public function testResizeCanvasRelativeWithBackground() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->resizeCanvas(4, 4, 'center', true, '#ff00ff'); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(20, $img->getWidth()); - $this->assertEquals(20, $img->getHeight()); - $this->assertColorAtPosition('#ff00ff', $img, 0, 0); - $this->assertColorAtPosition('#ff00ff', $img, 19, 19); - $this->assertColorAtPosition('#b4e000', $img, 2, 9); - $this->assertColorAtPosition('#445160', $img, 10, 10); - $this->assertTransparentPosition($img, 2, 10); - $this->assertTransparentPosition($img, 10, 9); - } - - public function testResizeCanvasJustWidth() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->resizeCanvas(10, null); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(10, $img->getWidth()); - $this->assertEquals(16, $img->getHeight()); - $this->assertColorAtPosition('#b4e000', $img, 0, 7); - $this->assertColorAtPosition('#445160', $img, 5, 8); - $this->assertTransparentPosition($img, 0, 8); - $this->assertTransparentPosition($img, 5, 7); - } - - public function testResizeCanvasJustHeight() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->resizeCanvas(null, 10); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(16, $img->getWidth()); - $this->assertEquals(10, $img->getHeight()); - $this->assertColorAtPosition('#b4e000', $img, 0, 4); - $this->assertColorAtPosition('#445160', $img, 8, 5); - $this->assertTransparentPosition($img, 0, 5); - $this->assertTransparentPosition($img, 8, 4); - } - - public function testResizeCanvasSmallerWidthLargerHeight() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->resizeCanvas(10, 20); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(10, $img->getWidth()); - $this->assertEquals(20, $img->getHeight()); - $this->assertColorAtPosition('#b4e000', $img, 0, 9); - $this->assertColorAtPosition('#445160', $img, 5, 10); - $this->assertTransparentPosition($img, 0, 10); - $this->assertTransparentPosition($img, 5, 9); - } - - public function testResizeCanvasLargerWidthSmallerHeight() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->resizeCanvas(20, 10); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(20, $img->getWidth()); - $this->assertEquals(10, $img->getHeight()); - $this->assertColorAtPosition('#b4e000', $img, 2, 4); - $this->assertColorAtPosition('#445160', $img, 10, 5); - $this->assertTransparentPosition($img, 0, 0); - $this->assertTransparentPosition($img, 2, 5); - $this->assertTransparentPosition($img, 10, 4); - } - - public function testResizeCanvasNegative() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->resizeCanvas(-4, -4); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(12, $img->getWidth()); - $this->assertEquals(12, $img->getHeight()); - $this->assertColorAtPosition('#b4e000', $img, 0, 5); - $this->assertColorAtPosition('#445160', $img, 6, 6); - $this->assertTransparentPosition($img, 0, 6); - $this->assertTransparentPosition($img, 6, 5); - } - - public function testResizeCanvasLargerHeightAutoWidth() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->resizeCanvas(null, 20, 'bottom-left', false, '#ff00ff'); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(16, $img->getWidth()); - $this->assertEquals(20, $img->getHeight()); - $this->assertColorAtPosition('#ff00ff', $img, 0, 0); - $this->assertColorAtPosition('#b4e000', $img, 0, 4); - $this->assertColorAtPosition('#b4e000', $img, 0, 11); - $this->assertColorAtPosition('#445160', $img, 8, 12); - $this->assertTransparentPosition($img, 0, 12); - $this->assertTransparentPosition($img, 8, 11); - } - - public function testResizeCanvasBorderNonRelative() - { - $img = $this->manager()->canvas(1, 1, 'ff0000'); - $img->resizeCanvas(17, 17, 'center', false, '333333'); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(17, $img->getWidth()); - $this->assertEquals(17, $img->getHeight()); - $this->assertColorAtPosition('#333333', $img, 0, 0); - $this->assertColorAtPosition('#333333', $img, 5, 5); - $this->assertColorAtPosition('#333333', $img, 7, 7); - $this->assertColorAtPosition('#ff0000', $img, 8, 8); - } - - public function testCropImage() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->crop(6, 6); // should be centered without pos. - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(6, $img->getWidth()); - $this->assertEquals(6, $img->getHeight()); - $this->assertColorAtPosition('#b4e000', $img, 0, 2); - $this->assertColorAtPosition('#445160', $img, 3, 3); - $this->assertTransparentPosition($img, 0, 3); - $this->assertTransparentPosition($img, 3, 2); - } - - public function testCropImageWithPosition() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->crop(4, 4, 7, 7); // should be centered without pos. - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(4, $img->getWidth()); - $this->assertEquals(4, $img->getHeight()); - $this->assertColorAtPosition('#b4e000', $img, 0, 0); - $this->assertColorAtPosition('#445160', $img, 1, 1); - $this->assertTransparentPosition($img, 0, 1); - $this->assertTransparentPosition($img, 1, 0); - } - - public function testFitImageSquare() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->fit(6); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(6, $img->getWidth()); - $this->assertEquals(6, $img->getHeight()); - $this->assertColorAtPosition('#b4e000', $img, 0, 2); - $this->assertColorAtPosition('#445060', $img, 3, 3); - $this->assertTransparentPosition($img, 0, 3); - $this->assertTransparentPosition($img, 3, 2); - } - - public function testFitImageRectangle() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->fit(12, 6); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(12, $img->getWidth()); - $this->assertEquals(6, $img->getHeight()); - $this->assertColorAtPosition('#b4e000', $img, 0, 2); - $this->assertColorAtPosition('#445160', $img, 6, 3); - $this->assertTransparentPosition($img, 0, 3); - $this->assertTransparentPosition($img, 6, 2); - } - - public function testFitImageWithConstraintUpsize() - { - $img = $this->manager()->make('tests/images/trim.png'); - $img->fit(300, 150, function ($constraint) {$constraint->upsize();}); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(50, $img->getWidth()); - $this->assertEquals(25, $img->getHeight()); - $this->assertColorAtPosition('#00aef0', $img, 0, 0); - $this->assertColorAtPosition('#afa94c', $img, 17, 0); - $this->assertColorAtPosition('#ffa601', $img, 24, 0); - } - - public function testFlipImageHorizontal() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->flip('h'); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(16, $img->getWidth()); - $this->assertEquals(16, $img->getHeight()); - $this->assertColorAtPosition('#b4e000', $img, 8, 7); - $this->assertColorAtPosition('#445160', $img, 0, 8); - $this->assertTransparentPosition($img, 0, 7); - $this->assertTransparentPosition($img, 8, 8); - } - - public function testFlipImageVertical() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->flip('v'); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(16, $img->getWidth()); - $this->assertEquals(16, $img->getHeight()); - $this->assertColorAtPosition('#b4e000', $img, 0, 8); - $this->assertColorAtPosition('#445160', $img, 8, 7); - $this->assertTransparentPosition($img, 0, 7); - $this->assertTransparentPosition($img, 8, 8); - } - - public function testRotateImage() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->rotate(90); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(16, $img->getWidth()); - $this->assertEquals(16, $img->getHeight()); - $this->assertColorAtPosition('#b4e000', $img, 0, 8); - $this->assertColorAtPosition('#445160', $img, 8, 7); - $this->assertTransparentPosition($img, 0, 7); - $this->assertTransparentPosition($img, 8, 8); - } - - public function testInsertImage() - { - $watermark = $this->manager()->canvas(16, 16, '#0000ff'); // create watermark - - // top-left anchor - $img = $this->manager()->canvas(32, 32, '#ff0000'); // create canvas - $img->insert($watermark, 'top-left', 0, 0); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals($img->getWidth(), 32); - $this->assertEquals($img->getHeight(), 32); - $this->assertEquals('#0000ff', $img->pickColor(0, 0, 'hex')); - $this->assertEquals('#ff0000', $img->pickColor(16, 16, 'hex')); - - // top-left anchor coordinates - $img = $this->manager()->canvas(32, 32, '#ff0000'); // create canvas - $img->insert($watermark, 'top-left', 10, 10); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals($img->getWidth(), 32); - $this->assertEquals($img->getHeight(), 32); - $this->assertEquals('#ff0000', $img->pickColor(9, 9, 'hex')); - $this->assertEquals('#0000ff', $img->pickColor(10, 10, 'hex')); - - // top anchor - $img = $this->manager()->canvas(32, 32, '#ff0000'); // create canvas - $img->insert($watermark, 'top', 0, 0); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals($img->getWidth(), 32); - $this->assertEquals($img->getHeight(), 32); - $this->assertEquals('#ff0000', $img->pickColor(0, 0, 'hex')); - $this->assertEquals('#0000ff', $img->pickColor(23, 15, 'hex')); - - // top anchor coordinates - $img = $this->manager()->canvas(32, 32, '#ff0000'); // create canvas - $img->insert($watermark, 'top', 10, 10); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals($img->getWidth(), 32); - $this->assertEquals($img->getHeight(), 32); - $this->assertEquals('#0000ff', $img->pickColor(18, 10, 'hex')); - $this->assertEquals('#ff0000', $img->pickColor(31, 26, 'hex')); - - // top-right anchor - $img = $this->manager()->canvas(32, 32, '#ff0000'); // create canvas - $img->insert($watermark, 'top-right', 0, 0); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals($img->getWidth(), 32); - $this->assertEquals($img->getHeight(), 32); - $this->assertEquals('#ff0000', $img->pickColor(15, 0, 'hex')); - $this->assertEquals('#0000ff', $img->pickColor(31, 0, 'hex')); - - // top-right anchor coordinates - $img = $this->manager()->canvas(32, 32, '#ff0000'); // create canvas - $img->insert($watermark, 'top-right', 10, 10); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals($img->getWidth(), 32); - $this->assertEquals($img->getHeight(), 32); - $this->assertEquals('#ff0000', $img->pickColor(6, 9, 'hex')); - $this->assertEquals('#0000ff', $img->pickColor(21, 25, 'hex')); - - // left anchor - $img = $this->manager()->canvas(32, 32, '#ff0000'); // create canvas - $img->insert($watermark, 'left', 0, 0); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals($img->getWidth(), 32); - $this->assertEquals($img->getHeight(), 32); - $this->assertEquals('#0000ff', $img->pickColor(15, 23, 'hex')); - $this->assertEquals('#ff0000', $img->pickColor(0, 7, 'hex')); - - // left anchor coordinates - $img = $this->manager()->canvas(32, 32, '#ff0000'); // create canvas - $img->insert($watermark, 'left', 10, 10); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals($img->getWidth(), 32); - $this->assertEquals($img->getHeight(), 32); - $this->assertEquals('#ff0000', $img->pickColor(8, 23, 'hex')); - $this->assertEquals('#ff0000', $img->pickColor(10, 7, 'hex')); - $this->assertEquals('#0000ff', $img->pickColor(25, 23, 'hex')); - $this->assertEquals('#0000ff', $img->pickColor(25, 8, 'hex')); - - // right anchor - $img = $this->manager()->canvas(32, 32, '#ff0000'); // create canvas - $img->insert($watermark, 'right', 0, 0); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals($img->getWidth(), 32); - $this->assertEquals($img->getHeight(), 32); - $this->assertEquals('#0000ff', $img->pickColor(31, 23, 'hex')); - $this->assertEquals('#ff0000', $img->pickColor(15, 15, 'hex')); - - // right anchor coordinates - $img = $this->manager()->canvas(32, 32, '#ff0000'); // create canvas - $img->insert($watermark, 'right', 10, 10); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals($img->getWidth(), 32); - $this->assertEquals($img->getHeight(), 32); - $this->assertEquals('#ff0000', $img->pickColor(5, 8, 'hex')); - $this->assertEquals('#ff0000', $img->pickColor(22, 23, 'hex')); - $this->assertEquals('#ff0000', $img->pickColor(21, 7, 'hex')); - $this->assertEquals('#0000ff', $img->pickColor(6, 8, 'hex')); - $this->assertEquals('#0000ff', $img->pickColor(21, 23, 'hex')); - $this->assertEquals('#0000ff', $img->pickColor(6, 23, 'hex')); - - // bottom-left anchor - $img = $this->manager()->canvas(32, 32, '#ff0000'); // create canvas - $img->insert($watermark, 'bottom-left', 0, 0); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals($img->getWidth(), 32); - $this->assertEquals($img->getHeight(), 32); - $this->assertEquals('#0000ff', $img->pickColor(15, 31, 'hex')); - $this->assertEquals('#ff0000', $img->pickColor(0, 15, 'hex')); - - // bottom-left anchor coordinates - $img = $this->manager()->canvas(32, 32, '#ff0000'); // create canvas - $img->insert($watermark, 'bottom-left', 10, 10); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals($img->getWidth(), 32); - $this->assertEquals($img->getHeight(), 32); - $this->assertEquals('#0000ff', $img->pickColor(10, 21, 'hex')); - $this->assertEquals('#ff0000', $img->pickColor(9, 20, 'hex')); - - // bottom anchor - $img = $this->manager()->canvas(32, 32, '#ff0000'); // create canvas - $img->insert($watermark, 'bottom', 0, 0); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals($img->getWidth(), 32); - $this->assertEquals($img->getHeight(), 32); - $this->assertEquals('#0000ff', $img->pickColor(8, 16, 'hex')); - $this->assertEquals('#ff0000', $img->pickColor(8, 15, 'hex')); - - // bottom anchor coordinates - $img = $this->manager()->canvas(32, 32, '#ff0000'); // create canvas - $img->insert($watermark, 'bottom', 10, 10); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals($img->getWidth(), 32); - $this->assertEquals($img->getHeight(), 32); - $this->assertEquals('#ff0000', $img->pickColor(5, 8, 'hex')); - $this->assertEquals('#ff0000', $img->pickColor(23, 22, 'hex')); - $this->assertEquals('#ff0000', $img->pickColor(24, 21, 'hex')); - $this->assertEquals('#ff0000', $img->pickColor(7, 6, 'hex')); - $this->assertEquals('#0000ff', $img->pickColor(8, 6, 'hex')); - $this->assertEquals('#0000ff', $img->pickColor(23, 21, 'hex')); - $this->assertEquals('#0000ff', $img->pickColor(23, 6, 'hex')); - - // bottom-right anchor - $img = $this->manager()->canvas(32, 32, '#ff0000'); // create canvas - $img->insert($watermark, 'bottom-right', 0, 0); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals($img->getWidth(), 32); - $this->assertEquals($img->getHeight(), 32); - $this->assertEquals('#0000ff', $img->pickColor(16, 16, 'hex')); - $this->assertEquals('#ff0000', $img->pickColor(15, 16, 'hex')); - - // bottom-right anchor coordinates - $img = $this->manager()->canvas(32, 32, '#ff0000'); // create canvas - $img->insert($watermark, 'bottom-right', 10, 10); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals($img->getWidth(), 32); - $this->assertEquals($img->getHeight(), 32); - $this->assertEquals('#0000ff', $img->pickColor(21, 21, 'hex')); - $this->assertEquals('#ff0000', $img->pickColor(22, 22, 'hex')); - - // center anchor - $img = $this->manager()->canvas(32, 32, '#ff0000'); // create canvas - $img->insert($watermark, 'center', 0, 0); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals($img->getWidth(), 32); - $this->assertEquals($img->getHeight(), 32); - $this->assertEquals('#0000ff', $img->pickColor(23, 23, 'hex')); - $this->assertEquals('#ff0000', $img->pickColor(8, 7, 'hex')); - - // center anchor coordinates / coordinates will be ignored for center - $img = $this->manager()->canvas(32, 32, '#ff0000'); // create canvas - $img->insert($watermark, 'center', 10, 10); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals($img->getWidth(), 32); - $this->assertEquals($img->getHeight(), 32); - $this->assertEquals('#0000ff', $img->pickColor(23, 23, 'hex')); - $this->assertEquals('#ff0000', $img->pickColor(8, 7, 'hex')); - } - - public function testInsertWithAlphaChannel() - { - $img = $this->manager()->canvas(50, 50, 'ff0000'); - $img->insert('tests/images/circle.png'); - $this->assertColorAtPosition('#ff0000', $img, 0, 0); - $this->assertColorAtPosition('#320000', $img, 30, 30); - } - - public function testInsertAfterResize() - { - $img = $this->manager()->make('tests/images/trim.png'); - $img->resize(16, 16)->insert('tests/images/tile.png'); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(16, $img->getWidth()); - $this->assertEquals(16, $img->getHeight()); - $this->assertColorAtPosition('#b4e000', $img, 0, 7); - $this->assertColorAtPosition('#00aef0', $img, 0, 8); - $this->assertColorAtPosition('#445160', $img, 8, 8); - $this->assertColorAtPosition('#ffa601', $img, 8, 7); - } - - public function testInsertResource() - { - $resource = imagecreatefrompng('tests/images/tile.png'); - $img = $this->manager()->make('tests/images/trim.png'); - $img->insert($resource); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertColorAtPosition('#b4e000', $img, 0, 7); - $this->assertColorAtPosition('#00aef0', $img, 0, 8); - $this->assertColorAtPosition('#445160', $img, 8, 8); - $this->assertColorAtPosition('#00aef0', $img, 8, 7); - $this->assertColorAtPosition('#ffa601', $img, 24, 24); - } - - public function testInsertBinary() - { - $data = file_get_contents('tests/images/tile.png'); - $img = $this->manager()->make('tests/images/trim.png'); - $img->insert($data); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertColorAtPosition('#b4e000', $img, 0, 7); - $this->assertColorAtPosition('#00aef0', $img, 0, 8); - $this->assertColorAtPosition('#445160', $img, 8, 8); - $this->assertColorAtPosition('#00aef0', $img, 8, 7); - $this->assertColorAtPosition('#ffa601', $img, 24, 24); - } - - public function testInsertInterventionImage() - { - $obj = $this->manager()->make('tests/images/tile.png'); - $img = $this->manager()->make('tests/images/trim.png'); - $img->insert($obj); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertColorAtPosition('#b4e000', $img, 0, 7); - $this->assertColorAtPosition('#00aef0', $img, 0, 8); - $this->assertColorAtPosition('#445160', $img, 8, 8); - $this->assertColorAtPosition('#00aef0', $img, 8, 7); - $this->assertColorAtPosition('#ffa601', $img, 24, 24); - } - - public function testOpacity() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->opacity(50); - $checkColor = $img->pickColor(7, 7, 'array'); - $this->assertEquals($checkColor[0], 180); - $this->assertEquals($checkColor[1], 224); - $this->assertEquals($checkColor[2], 0); - $this->assertEquals($checkColor[3], 0.5); - $checkColor = $img->pickColor(8, 8, 'array'); - $this->assertEquals($checkColor[0], 68); - $this->assertEquals($checkColor[1], 81); - $this->assertEquals($checkColor[2], 96); - $this->assertEquals($checkColor[3], 0.5); - $this->assertTransparentPosition($img, 0, 11); - } - - public function testMaskImage() - { - $img = $this->manager()->make('tests/images/trim.png'); - $img->mask('tests/images/gradient.png'); - $this->assertTransparentPosition($img, 0, 0); - $this->assertTransparentPosition($img, 23, 23); - $checkColor = $img->pickColor(23, 24, 'array'); - $this->assertEquals($checkColor[0], 255); - $this->assertEquals($checkColor[1], 166); - $this->assertEquals($checkColor[2], 1); - $this->assertEquals($checkColor[3], 0.97); - $checkColor = $img->pickColor(39, 25, 'array'); - $this->assertEquals($checkColor[0], 0); - $this->assertEquals($checkColor[1], 174); - $this->assertEquals($checkColor[2], 240); - $this->assertEquals($checkColor[3], 0.32); - } - - public function testMaskImageWithAlpha() - { - $img = $this->manager()->make('tests/images/trim.png'); - $img->mask('tests/images/star.png', true); - $this->assertTransparentPosition($img, 0, 0); - $this->assertTransparentPosition($img, 16, 16); - $checkColor = $img->pickColor(18, 18, 'array'); - $this->assertEquals($checkColor[0], 255); - $this->assertEquals($checkColor[1], 166); - $this->assertEquals($checkColor[2], 1); - $this->assertEquals($checkColor[3], 0.65); - $checkColor = $img->pickColor(24, 10, 'array'); - $this->assertEquals($checkColor[0], 0); - $this->assertEquals($checkColor[1], 174); - $this->assertEquals($checkColor[2], 240); - $this->assertEquals($checkColor[3], 0.80); - } - - public function testPixelateImage() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->pixelate(20); - $this->assertInstanceOf('Intervention\Image\Image', $img); - } - - public function testGreyscaleImage() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->greyscale(); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertTransparentPosition($img, 8, 0); - $this->assertColorAtPosition('#b9b9b9', $img, 0, 0); - } - - public function testInvertImage() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->invert(); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertTransparentPosition($img, 8, 0); - $this->assertColorAtPosition('#4b1fff', $img, 0, 0); - } - - public function testBlurImage() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->blur(1); - $this->assertInstanceOf('Intervention\Image\Image', $img); - } - - public function testFillImageWithColor() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->fill('b53717'); - $this->assertColorAtPosition('#b53717', $img, 0, 0); - $this->assertColorAtPosition('#b53717', $img, 15, 15); - } - - public function testFillImageWithColorAtPosition() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->fill('b53717', 0, 0); - $this->assertTransparentPosition($img, 0, 8); - $this->assertColorAtPosition('#b53717', $img, 0, 0); - $this->assertColorAtPosition('#445160', $img, 15, 15); - } - - public function testFillImageWithResource() - { - $resource = imagecreatefrompng('tests/images/tile.png'); - $img = $this->manager()->make('tests/images/trim.png'); - $img->fill($resource, 0, 0); - $this->assertColorAtPosition('#b4e000', $img, 0, 0); - $this->assertColorAtPosition('#445160', $img, 8, 8); - $this->assertColorAtPosition('#00aef0', $img, 8, 7); - $this->assertColorAtPosition('#ffa601', $img, 20, 20); - } - - public function testFillImageWithBinary() - { - $data = file_get_contents('tests/images/tile.png'); - $img = $this->manager()->make('tests/images/trim.png'); - $img->fill($data, 0, 0); - $this->assertColorAtPosition('#b4e000', $img, 0, 0); - $this->assertColorAtPosition('#445160', $img, 8, 8); - $this->assertColorAtPosition('#00aef0', $img, 8, 7); - $this->assertColorAtPosition('#ffa601', $img, 20, 20); - } - - public function testFillImageWithInterventionImage() - { - $obj = $this->manager()->make('tests/images/tile.png'); - $img = $this->manager()->make('tests/images/trim.png'); - $img->fill($obj, 0, 0); - $this->assertColorAtPosition('#b4e000', $img, 0, 0); - $this->assertColorAtPosition('#445160', $img, 8, 8); - $this->assertColorAtPosition('#00aef0', $img, 8, 7); - $this->assertColorAtPosition('#ffa601', $img, 20, 20); - } - - public function testPixelImage() - { - $img = $this->manager()->make('tests/images/tile.png'); - $coords = [[5, 5], [12, 12]]; - $img = $img->pixel('fdf5e4', $coords[0][0], $coords[0][1]); - $img = $img->pixel([255, 255, 255], $coords[1][0], $coords[1][1]); - $this->assertEquals('#fdf5e4', $img->pickColor($coords[0][0], $coords[0][1], 'hex')); - $this->assertEquals('#ffffff', $img->pickColor($coords[1][0], $coords[1][1], 'hex')); - } - - public function testTextImage() - { - $img = $this->manager()->canvas(16, 16, 'ffffff'); - $img = $img->text('0', 3, 11); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertEquals('a9e2b15452b2a4637b65625188d206f6', $img->checksum()); - - - $img = $this->manager()->canvas(16, 16, 'ffffff'); - $img = $img->text('0', 8, 2, function($font) { - $font->align('center'); - $font->valign('top'); - $font->color('000000'); - }); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertEquals('649f3f529d3931c56601155fd2680959', $img->checksum()); - - $img = $this->manager()->canvas(16, 16, 'ffffff'); - $img = $img->text('0', 8, 8, function($font) { - $font->align('right'); - $font->valign('middle'); - $font->file(2); - $font->color('000000'); - }); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertEquals('c0dda67589c46a90d78a97b891a811ee', $img->checksum()); - } - - public function testRectangleImage() - { - $img = $this->manager()->canvas(16, 16, 'ffffff'); - $img->rectangle(5, 5, 11, 11, function ($draw) { $draw->background('#ff0000'); $draw->border(1, '#0000ff'); }); - $this->assertEquals('e95487dcc29daf371a0e9190bff8dbfe', $img->checksum()); - } - - public function testLineImage() - { - $img = $this->manager()->canvas(16, 16, 'ffffff'); - $img->line(0, 0, 15, 15, function ($draw) { $draw->color('#ff0000'); }); - $this->assertEquals('a6237d34f6e95f30d2fc91a46bd058e6', $img->checksum()); - } - - public function testEllipseImage() - { - $img = $this->manager()->canvas(16, 16, 'ffffff'); - $img->ellipse(12, 8, 8, 8, function ($draw) { $draw->background('#ff0000'); $draw->border(1, '#0000ff'); }); - $this->assertEquals('080d9dd92ebe22f976c3c703cba33510', $img->checksum()); - } - - public function testCircleImage() - { - $img = $this->manager()->canvas(16, 16, 'ffffff'); - $img->circle(12, 8, 8, function ($draw) { $draw->background('#ff0000'); $draw->border(1, '#0000ff'); }); - $this->assertEquals('c3bff06c20244ba14e898e39ea0efd76', $img->checksum()); - } - - public function testPolygonImage() - { - $img = $this->manager()->canvas(16, 16, 'ffffff'); - $points = [3, 3, 11, 11, 7, 13]; - $img->polygon($points, function ($draw) { $draw->background('#ff0000'); $draw->border(1, '#0000ff'); }); - $this->assertEquals('e534ff90c8026f9317b99071fda01ed4', $img->checksum()); - } - - public function testResetImage() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->backup(); - $img->resize(30, 20); - $img->reset(); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(16, $img->getWidth()); - $this->assertEquals(16, $img->getHeight()); - } - - public function testResetEmptyImage() - { - $img = $this->manager()->canvas(16, 16, '#0000ff'); - $img->backup(); - $img->resize(30, 20); - $img->fill('#ff0000'); - $img->reset(); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(16, $img->getWidth()); - $this->assertEquals(16, $img->getHeight()); - $this->assertColorAtPosition('#0000ff', $img, 0, 0); - } - - public function testResetKeepTransparency() - { - $img = $this->manager()->make('tests/images/circle.png'); - $img->backup(); - $img->reset(); - $this->assertTransparentPosition($img, 0, 0); - } - - public function testResetToNamed() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->backup('original'); - $img->resize(30, 20); - $img->backup('30x20'); - - // reset to original - $img->reset('original'); - $this->assertEquals(16, $img->getWidth()); - $this->assertEquals(16, $img->getHeight()); - - // reset to 30x20 - // $img->reset('30x20'); - // $this->assertEquals(30, $img->getWidth()); - // $this->assertEquals(20, $img->getHeight()); - - // reset to original again - $img->reset('original'); - $this->assertEquals(16, $img->getWidth()); - $this->assertEquals(16, $img->getHeight()); - } - - public function testLimitColors() - { - $img = $this->manager()->make('tests/images/trim.png'); - $img->limitColors(4); - $this->assertLessThanOrEqual(5, imagecolorstotal($img->getCore())); - } - - public function testLimitColorsKeepTransparency() - { - $img = $this->manager()->make('tests/images/star.png'); - $img->limitColors(16); - $this->assertLessThanOrEqual(17, imagecolorstotal($img->getCore())); - $this->assertTransparentPosition($img, 0, 0); - $this->assertColorAtPosition('#0c02b4', $img, 6, 12); - $this->assertColorAtPosition('#fcbe04', $img, 22, 24); - } - - public function testLimitColorsKeepTransparencyWithMatte() - { - $img = $this->manager()->make('tests/images/star.png'); - $img->limitColors(64, '#00ff00'); - $this->assertLessThanOrEqual(65, imagecolorstotal($img->getCore())); - $this->assertTransparentPosition($img, 0, 0); - $this->assertColorAtPosition('#04f204', $img, 12, 10); - $this->assertColorAtPosition('#e40214', $img, 16, 21); - } - - public function testLimitColorsNullWithMatte() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->limitColors(null, '#ff00ff'); - $this->assertColorAtPosition('#b4e000', $img, 0, 0); - $this->assertColorAtPosition('#445160', $img, 8, 8); - $this->assertColorAtPosition('#ff00ff', $img, 0, 8); - $this->assertColorAtPosition('#ff00ff', $img, 15, 0); - } - - public function testPickColorFromTrueColor() - { - $img = $this->manager()->make('tests/images/star.png'); - $c = $img->pickColor(0, 0); - $this->assertEquals(255, $c[0]); - $this->assertEquals(255, $c[1]); - $this->assertEquals(255, $c[2]); - $this->assertEquals(0, $c[3]); - - $c = $img->pickColor(11, 11); - $this->assertEquals(34, $c[0]); - $this->assertEquals(0, $c[1]); - $this->assertEquals(160, $c[2]); - $this->assertEquals(0.46, $c[3]); - - $c = $img->pickColor(16, 16); - $this->assertEquals(231, $c[0]); - $this->assertEquals(0, $c[1]); - $this->assertEquals(18, $c[2]); - $this->assertEquals(1, $c[3]); - } - - public function testPickColorFromIndexed() - { - $img = $this->manager()->make('tests/images/tile.png'); - $c = $img->pickColor(0, 0); - $this->assertEquals(180, $c[0]); - $this->assertEquals(224, $c[1]); - $this->assertEquals(0, $c[2]); - $this->assertEquals(1, $c[3]); - - $c = $img->pickColor(8, 8); - $this->assertEquals(68, $c[0]); - $this->assertEquals(81, $c[1]); - $this->assertEquals(96, $c[2]); - $this->assertEquals(1, $c[3]); - - $c = $img->pickColor(0, 15); - $this->assertEquals(255, $c[0]); - $this->assertEquals(255, $c[1]); - $this->assertEquals(255, $c[2]); - $this->assertEquals(0, $c[3]); - } - - public function testPickColorFromPalette() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->limitColors(200); - - $c = $img->pickColor(0, 0); - $this->assertEquals(180, $c[0]); - $this->assertEquals(226, $c[1]); - $this->assertEquals(4, $c[2]); - $this->assertEquals(1, $c[3]); - - $c = $img->pickColor(8, 8); - $this->assertEquals(68, $c[0]); - $this->assertEquals(82, $c[1]); - $this->assertEquals(100, $c[2]); - $this->assertEquals(1, $c[3]); - - $c = $img->pickColor(0, 15); - $this->assertEquals(255, $c[0]); - $this->assertEquals(255, $c[1]); - $this->assertEquals(255, $c[2]); - $this->assertEquals(0, $c[3]); - } - - public function testInterlaceImage() - { - $img = $this->manager()->make('tests/images/trim.png'); - $img->interlace(); - $img->encode('png'); - $this->assertTrue((ord($img->encoded[28]) != '0')); - $img->interlace(false); - $img->encode('png'); - $this->assertFalse((ord($img->encoded[28]) != '0')); - } - - public function testGammaImage() - { - $img = $this->manager()->make('tests/images/trim.png'); - $img->gamma(1.6); - $this->assertColorAtPosition('#00c9f6', $img, 0, 0); - $this->assertColorAtPosition('#ffc308', $img, 24, 24); - } - - public function testBrightnessImage() - { - $img = $this->manager()->make('tests/images/trim.png'); - $img->brightness(35); - $this->assertColorAtPosition('#59ffff', $img, 0, 0); - $this->assertColorAtPosition('#ffff5a', $img, 24, 24); - } - - public function testContrastImage() - { - $img = $this->manager()->make('tests/images/trim.png'); - $img->contrast(35); - $this->assertColorAtPosition('#00d4ff', $img, 0, 0); - $this->assertColorAtPosition('#ffc500', $img, 24, 24); - } - - public function testColorizeImage() - { - $img = $this->manager()->make('tests/images/trim.png'); - $img->colorize(40, 25, -50); - $this->assertColorAtPosition('#66ee70', $img, 0, 0); - $this->assertColorAtPosition('#ffe600', $img, 24, 24); - } - - public function testTrimGradient() - { - $canvas = $this->manager()->make('tests/images/gradient.png'); - - $img = clone $canvas; - $img->trim(); - $this->assertEquals($img->getWidth(), 46); - $this->assertEquals($img->getHeight(), 46); - - $img = clone $canvas; - $img->trim(null, null, 10); - $this->assertEquals($img->getWidth(), 38); - $this->assertEquals($img->getHeight(), 38); - - $img = clone $canvas; - $img->trim(null, null, 20); - $this->assertEquals($img->getWidth(), 34); - $this->assertEquals($img->getHeight(), 34); - - $img = clone $canvas; - $img->trim(null, null, 30); - $this->assertEquals($img->getWidth(), 30); - $this->assertEquals($img->getHeight(), 30); - - $img = clone $canvas; - $img->trim(null, null, 40); - $this->assertEquals($img->getWidth(), 26); - $this->assertEquals($img->getHeight(), 26); - - $img = clone $canvas; - $img->trim(null, null, 50); - $this->assertEquals($img->getWidth(), 22); - $this->assertEquals($img->getHeight(), 22); - - $img = clone $canvas; - $img->trim(null, null, 60); - $this->assertEquals($img->getWidth(), 20); - $this->assertEquals($img->getHeight(), 20); - - $img = clone $canvas; - $img->trim(null, null, 70); - $this->assertEquals($img->getWidth(), 16); - $this->assertEquals($img->getHeight(), 16); - - $img = clone $canvas; - $img->trim(null, null, 80); - $this->assertEquals($img->getWidth(), 12); - $this->assertEquals($img->getHeight(), 12); - - $img = clone $canvas; - $img->trim(null, null, 90); - $this->assertEquals($img->getWidth(), 8); - $this->assertEquals($img->getHeight(), 8); - } - - public function testTrimOnlyLeftAndRight() - { - $img = $this->manager()->make('tests/images/gradient.png'); - $img->trim(null, ['left', 'right'], 60); - $this->assertEquals($img->getWidth(), 20); - $this->assertEquals($img->getHeight(), 50); - } - - public function testTrimOnlyTopAndBottom() - { - $img = $this->manager()->make('tests/images/gradient.png'); - $img->trim(null, ['top', 'bottom'], 60); - $this->assertEquals($img->getWidth(), 50); - $this->assertEquals($img->getHeight(), 20); - } - - public function testTrimOnlyTop() - { - $img = $this->manager()->make('tests/images/gradient.png'); - $img->trim(null, 'top', 60); - $this->assertEquals($img->getWidth(), 50); - $this->assertEquals($img->getHeight(), 35); - } - - public function testTrimOnlyBottom() - { - $img = $this->manager()->make('tests/images/gradient.png'); - $img->trim(null, 'top', 60); - $this->assertEquals($img->getWidth(), 50); - $this->assertEquals($img->getHeight(), 35); - } - - public function testTrimWithFeather() - { - $canvas = $this->manager()->make('tests/images/trim.png'); - - $img = clone $canvas; - $feather = 5; - $img->trim(null, null, null, $feather); - $this->assertEquals($img->getWidth(), 28 + $feather * 2); - $this->assertEquals($img->getHeight(), 28 + $feather * 2); - - $img = clone $canvas; - $feather = 10; - $img->trim(null, null, null, $feather); - $this->assertEquals($img->getWidth(), 28 + $feather * 2); - $this->assertEquals($img->getHeight(), 28 + $feather * 2); - - $img = clone $canvas; - $feather = 20; // must respect original dimensions of image - $img->trim(null, null, null, $feather); - $this->assertEquals($img->getWidth(), 50); - $this->assertEquals($img->getHeight(), 50); - - $img = clone $canvas; - $feather = -5; - $img->trim(null, null, null, $feather); - $this->assertEquals($img->getWidth(), 28 + $feather * 2); - $this->assertEquals($img->getHeight(), 28 + $feather * 2); - - $img = clone $canvas; - $feather = -10; - $img->trim(null, null, null, $feather); - $this->assertEquals($img->getWidth(), 28 + $feather * 2); - $this->assertEquals($img->getHeight(), 28 + $feather * 2); - - // trim only left and right with feather - $img = clone $canvas; - $feather = 10; - $img->trim(null, ['left', 'right'], null, $feather); - $this->assertEquals($img->getWidth(), 28 + $feather * 2); - $this->assertEquals($img->getHeight(), 50); - - // trim only top and bottom with feather - $img = clone $canvas; - $feather = 10; - $img->trim(null, ['top', 'bottom'], null, $feather); - $this->assertEquals($img->getWidth(), 50); - $this->assertEquals($img->getHeight(), 28 + $feather * 2); - - // trim with tolerance and feather - $canvas = $this->manager()->make('tests/images/gradient.png'); - - $img = clone $canvas; - $feather = 2; - $img->trim(null, null, 10, $feather); - $this->assertEquals($img->getWidth(), 38 + $feather * 2); - $this->assertEquals($img->getHeight(), 38 + $feather * 2); - - $img = clone $canvas; - $feather = 5; - $img->trim(null, null, 10, $feather); - $this->assertEquals($img->getWidth(), 38 + $feather * 2); - $this->assertEquals($img->getHeight(), 38 + $feather * 2); - - $img = clone $canvas; - $feather = 10; // should respect original dimensions - $img->trim(null, null, 20, $feather); - $this->assertEquals($img->getWidth(), 50); - $this->assertEquals($img->getHeight(), 50); - } - - public function testEncodeDefault() - { - $img = $this->manager()->make('tests/images/trim.png'); - $img->encode(); - $this->assertInternalType('resource', imagecreatefromstring($img->encoded)); - } - - public function testEncodeJpeg() - { - $img = $this->manager()->make('tests/images/trim.png'); - $img->encode('jpg'); - $this->assertInternalType('resource', imagecreatefromstring($img->encoded)); - } - - public function testEncodeGif() - { - $img = $this->manager()->make('tests/images/trim.png'); - $img->encode('gif'); - $this->assertInternalType('resource', imagecreatefromstring($img->encoded)); - } - - public function testEncodeWebp() - { - if (function_exists('imagewebp')) { - $img = $this->manager()->make('tests/images/trim.png'); - $data = (string) $img->encode('webp'); - $this->assertEquals('image/webp; charset=binary', $this->getMime($data)); - } - } - - public function testEncodeDataUrl() - { - $img = $this->manager()->make('tests/images/trim.png'); - $img->encode('data-url'); - $this->assertEquals('data:image/png;base64', substr($img->encoded, 0, 21)); - } - - public function testExifReadAll() - { - $img = $this->manager()->make('tests/images/exif.jpg'); - $data = $img->exif(); - $this->assertInternalType('array', $data); - $this->assertEquals(19, count($data)); - } - - public function testExifReadKey() - { - $img = $this->manager()->make('tests/images/exif.jpg'); - $data = $img->exif('Artist'); - $this->assertInternalType('string', $data); - $this->assertEquals('Oliver Vogel', $data); - } - - public function testExifReadNotExistingKey() - { - $img = $this->manager()->make('tests/images/exif.jpg'); - $data = $img->exif('xxx'); - $this->assertEquals(null, $data); - } - - public function testSaveImage() - { - $save_as = 'tests/tmp/foo.jpg'; - $img = $this->manager()->make('tests/images/trim.png'); - $img->save($save_as, 80); - $this->assertFileExists($save_as); - $this->assertEquals($img->dirname, 'tests/tmp'); - $this->assertEquals($img->basename, 'foo.jpg'); - $this->assertEquals($img->extension, 'jpg'); - $this->assertEquals($img->filename, 'foo'); - $this->assertEquals($img->mime, 'image/jpeg'); - @unlink($save_as); - - $save_as = 'tests/tmp/foo.png'; - $img = $this->manager()->make('tests/images/trim.png'); - $img->save($save_as); - $this->assertEquals($img->dirname, 'tests/tmp'); - $this->assertEquals($img->basename, 'foo.png'); - $this->assertEquals($img->extension, 'png'); - $this->assertEquals($img->filename, 'foo'); - $this->assertEquals($img->mime, 'image/png'); - $this->assertFileExists($save_as); - @unlink($save_as); - - $save_as = 'tests/tmp/foo.jpg'; - $img = $this->manager()->make('tests/images/trim.png'); - $img->save($save_as, 0); - $this->assertEquals($img->dirname, 'tests/tmp'); - $this->assertEquals($img->basename, 'foo.jpg'); - $this->assertEquals($img->extension, 'jpg'); - $this->assertEquals($img->filename, 'foo'); - $this->assertEquals($img->mime, 'image/jpeg'); - $this->assertFileExists($save_as); - @unlink($save_as); - } - - public function testSaveImageWithoutParameter() - { - $path = 'tests/tmp/bar.png'; - - // create temp. test image (red) - $img = $this->manager()->canvas(16, 16, '#ff0000'); - $img->save($path); - $img->destroy(); - - // open test image again - $img = $this->manager()->make($path); - $this->assertColorAtPosition('#ff0000', $img, 0, 0); - - // fill with green and save wthout paramater - $img->fill('#00ff00'); - $img->save(); - $img->destroy(); - - // re-open test image (should be green) - $img = $this->manager()->make($path); - $this->assertColorAtPosition('#00ff00', $img, 0, 0); - $img->destroy(); - - @unlink($path); - } - - public function testDestroy() - { - $img = $this->manager()->make('tests/images/trim.png'); - $img->backup(); - $img->destroy(); - // destroy should affect core - $this->assertEquals(get_resource_type($img->getCore()), 'Unknown'); - // destroy should affect backup - $this->assertEquals(get_resource_type($img->getBackup()), 'Unknown'); - } - - public function testStringConversion() - { - $img = $this->manager()->make('tests/images/trim.png'); - $value = strval($img); - $this->assertInternalType('string', $value); - } - - public function testFilter() - { - $img = $this->manager()->make('tests/images/trim.png'); - $img->filter(new \Intervention\Image\Filters\DemoFilter(10)); - $this->assertColorAtPosition('#818181', $img, 0, 0); - $this->assertColorAtPosition('#939393', $img, 18, 18); - $this->assertColorAtPosition('#939393', $img, 18, 18); - $this->assertColorAtPosition('#adadad', $img, 25, 25); - $this->assertColorAtPosition('#939393', $img, 35, 35); - } - - public function testCloneImageObject() - { - $img = $this->manager()->make('tests/images/trim.png'); - $cln = clone $img; - - // destroy original - $img->destroy(); - unset($img); - - // clone should be still intact - $this->assertInstanceOf('Intervention\Image\Image', $cln); - $this->assertInternalType('resource', $cln->getCore()); - } - - public function testGifConversionKeepsTransparency() - { - $save_as = 'tests/tmp/foo.gif'; - - // create gif image from transparent png - $img = $this->manager()->make('tests/images/star.png'); - $img->save($save_as); - - // new gif image should be transparent - $img = $this->manager()->make($save_as); - $this->assertTransparentPosition($img, 0, 0); - @unlink($save_as); - } - - private function assertColorAtPosition($color, $img, $x, $y) - { - $pick = $img->pickColor($x, $y, 'hex'); - $this->assertEquals($color, $pick); - $this->assertInstanceOf('Intervention\Image\Image', $img); - } - - private function assertTransparentPosition($img, $x, $y, $transparent = 0) - { - // background should be transparent - $color = $img->pickColor($x, $y, 'array'); - $this->assertEquals($transparent, $color[3]); // alpha channel - } - - private function manager() - { - return new \Intervention\Image\ImageManager([ - 'driver' => 'gd' - ]); - } - - private function getMime($data) - { - $finfo = new finfo(FILEINFO_MIME); - return $finfo->buffer($data); - } -} diff --git a/tests/Geometry/PointTest.php b/tests/Geometry/PointTest.php new file mode 100644 index 00000000..f2d15b2e --- /dev/null +++ b/tests/Geometry/PointTest.php @@ -0,0 +1,82 @@ +assertInstanceOf(Point::class, $point); + $this->assertEquals(0, $point->getX()); + $this->assertEquals(0, $point->getY()); + } + + public function testConstructorWithParameters() + { + $point = new Point(40, 50); + $this->assertInstanceOf(Point::class, $point); + $this->assertEquals(40, $point->getX()); + $this->assertEquals(50, $point->getY()); + } + + public function testSetX() + { + $point = new Point(0, 0); + $point->setX(100); + $this->assertEquals(100, $point->getX()); + $this->assertEquals(0, $point->getY()); + } + + public function testSetY() + { + $point = new Point(0, 0); + $point->setY(100); + $this->assertEquals(0, $point->getX()); + $this->assertEquals(100, $point->getY()); + } + + public function testmoveX() + { + $point = new Point(50, 50); + $point->moveX(100); + $this->assertEquals(150, $point->getX()); + $this->assertEquals(50, $point->getY()); + } + + public function testmoveY() + { + $point = new Point(50, 50); + $point->moveY(100); + $this->assertEquals(50, $point->getX()); + $this->assertEquals(150, $point->getY()); + } + + public function testSetPosition() + { + $point = new Point(0, 0); + $point->setPosition(100, 200); + $this->assertEquals(100, $point->getX()); + $this->assertEquals(200, $point->getY()); + } + + public function testRotate() + { + $point = new Point(30, 0); + $point->rotate(90, new Point(0, 0)); + $this->assertEquals(0, $point->getX()); + $this->assertEquals(30, $point->getY()); + + $point->rotate(90, new Point(0, 0)); + $this->assertEquals(-30, $point->getX()); + $this->assertEquals(0, $point->getY()); + + $point = new Point(300, 200); + $point->rotate(90, new Point(0, 0)); + $this->assertEquals(-200, $point->getX()); + $this->assertEquals(300, $point->getY()); + } +} diff --git a/tests/Geometry/SizeTest.php b/tests/Geometry/SizeTest.php new file mode 100644 index 00000000..e3e9c348 --- /dev/null +++ b/tests/Geometry/SizeTest.php @@ -0,0 +1,101 @@ +assertInstanceOf(Size::class, $size); + $this->assertInstanceOf(Point::class, $size->getPivot()); + $this->assertEquals(300, $size->getWidth()); + $this->assertEquals(200, $size->getHeight()); + } + + public function testGetWidth() + { + $size = new Size(800, 600); + $this->assertEquals(800, $size->getWidth()); + } + + public function testGetHeight() + { + $size = new Size(800, 600); + $this->assertEquals(600, $size->getHeight()); + } + + public function testGetAspectRatio() + { + $size = new Size(800, 600); + $this->assertEquals(1.33333333333, $size->getAspectRatio()); + + $size = new Size(100, 100); + $this->assertEquals(1, $size->getAspectRatio()); + + $size = new Size(1920, 1080); + $this->assertEquals(1.777777777778, $size->getAspectRatio()); + } + + public function testFitsInto() + { + $box = new Size(800, 600); + $fits = $box->fitsInto(new Size(100, 100)); + $this->assertFalse($fits); + + $box = new Size(800, 600); + $fits = $box->fitsInto(new Size(1000, 100)); + $this->assertFalse($fits); + + $box = new Size(800, 600); + $fits = $box->fitsInto(new Size(100, 1000)); + $this->assertFalse($fits); + + $box = new Size(800, 600); + $fits = $box->fitsInto(new Size(800, 600)); + $this->assertTrue($fits); + + $box = new Size(800, 600); + $fits = $box->fitsInto(new Size(1000, 1000)); + $this->assertTrue($fits); + + $box = new Size(100, 100); + $fits = $box->fitsInto(new Size(800, 600)); + $this->assertTrue($fits); + + $box = new Size(100, 100); + $fits = $box->fitsInto(new Size(80, 60)); + $this->assertFalse($fits); + } + + public function testIsLandscape() + { + $box = new Size(100, 100); + $this->assertFalse($box->isLandscape()); + + $box = new Size(100, 200); + $this->assertFalse($box->isLandscape()); + + $box = new Size(300, 200); + $this->assertTrue($box->isLandscape()); + } + + public function testIsPortrait() + { + $box = new Size(100, 100); + $this->assertFalse($box->isPortrait()); + + $box = new Size(200, 100); + $this->assertFalse($box->isPortrait()); + + $box = new Size(200, 300); + $this->assertTrue($box->isPortrait()); + } +} diff --git a/tests/GetsizeCommandTest.php b/tests/GetsizeCommandTest.php deleted file mode 100644 index d67f3b7f..00000000 --- a/tests/GetsizeCommandTest.php +++ /dev/null @@ -1,39 +0,0 @@ -shouldReceive('getCore')->times(2)->andReturn($resource); - $command = new GetSizeGd([]); - $result = $command->execute($image); - $this->assertTrue($result); - $this->assertTrue($command->hasOutput()); - $this->assertInstanceOf('Intervention\Image\Size', $command->getOutput()); - } - - public function testImagick() - { - $imagick = Mockery::mock('Imagick'); - $imagick->shouldReceive('getimagewidth')->with(); - $imagick->shouldReceive('getimageheight')->with(); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($imagick); - $command = new GetSizeImagick([]); - $result = $command->execute($image); - $this->assertTrue($result); - $this->assertTrue($command->hasOutput()); - $this->assertInstanceOf('Intervention\Image\Size', $command->getOutput()); - } -} diff --git a/tests/GreyscaleCommandTest.php b/tests/GreyscaleCommandTest.php deleted file mode 100644 index 84298199..00000000 --- a/tests/GreyscaleCommandTest.php +++ /dev/null @@ -1,34 +0,0 @@ -shouldReceive('getCore')->once()->andReturn($resource); - $command = new GreyscaleGd([]); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testImagick() - { - $imagick = Mockery::mock('Imagick'); - $imagick->shouldReceive('modulateimage')->with(100, 0, 100)->andReturn(true); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($imagick); - $command = new GreyscaleImagick([]); - $result = $command->execute($image); - $this->assertTrue($result); - } -} diff --git a/tests/HeightenCommandTest.php b/tests/HeightenCommandTest.php deleted file mode 100644 index 4016ff7d..00000000 --- a/tests/HeightenCommandTest.php +++ /dev/null @@ -1,49 +0,0 @@ -aspectRatio(); }; - $resource = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); - $image = Mockery::mock('Intervention\Image\Image'); - $size = Mockery::mock('Intervention\Image\Size', [800, 600]); - $size->shouldReceive('resize')->once()->andReturn($size); - $size->shouldReceive('getWidth')->once()->andReturn(800); - $size->shouldReceive('getHeight')->once()->andReturn(600); - $image->shouldReceive('getWidth')->once()->andReturn(800); - $image->shouldReceive('getHeight')->once()->andReturn(600); - $image->shouldReceive('getSize')->once()->andReturn($size); - $image->shouldReceive('getCore')->once()->andReturn($resource); - $image->shouldReceive('setCore')->once(); - $command = new HeightenGd([200]); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testImagick() - { - $callback = function ($constraint) { $constraint->upsize(); }; - $imagick = Mockery::mock('Imagick'); - $imagick->shouldReceive('scaleimage')->with(300, 200)->once()->andReturn(true); - $size = Mockery::mock('Intervention\Image\Size', [800, 600]); - $size->shouldReceive('resize')->once()->andReturn($size); - $size->shouldReceive('getWidth')->once()->andReturn(300); - $size->shouldReceive('getHeight')->once()->andReturn(200); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($imagick); - $image->shouldReceive('getSize')->once()->andReturn($size); - $command = new HeightenImagick([200]); - $result = $command->execute($image); - $this->assertTrue($result); - } -} diff --git a/tests/ImageManagerStaticTest.php b/tests/ImageManagerStaticTest.php deleted file mode 100644 index b4bb3f5d..00000000 --- a/tests/ImageManagerStaticTest.php +++ /dev/null @@ -1,36 +0,0 @@ -getManager(); - $this->assertInstanceOf('Intervention\Image\ImageManager', $m); - } - - public function testMake() - { - $manager = Mockery::mock('Intervention\Image\ImageManager'); - $manager->shouldReceive('make')->with('foo')->once(); - $managerStatic = new ImageManagerStatic($manager); - $managerStatic->make('foo'); - } - - public function testCanvas() - { - $manager = Mockery::mock('Intervention\Image\ImageManager'); - $manager->shouldReceive('canvas')->with(100, 100, null)->once(); - $managerStatic = new ImageManagerStatic($manager); - $managerStatic->canvas(100, 100); - } -} diff --git a/tests/ImageManagerTest.php b/tests/ImageManagerTest.php index 9312208a..2bdd2c6e 100644 --- a/tests/ImageManagerTest.php +++ b/tests/ImageManagerTest.php @@ -1,40 +1,46 @@ 'foo', 'bar' => 'baz']; - $manager = new ImageManager($config); - $this->assertEquals('foo', $manager->config['driver']); - $this->assertEquals('baz', $manager->config['bar']); + $manager = new ImageManager(['foo' => 'bar']); + $this->assertInstanceOf(ImageManager::class, $manager); + $this->assertEquals('gd', $manager->getConfig('driver')); + $this->assertEquals('bar', $manager->getConfig('foo')); } public function testConfigure() { - $overwrite = ['driver' => 'none', 'bar' => 'none']; - $config = ['driver' => 'foo', 'bar' => 'baz']; - $manager = new ImageManager($overwrite); - $manager->configure($config); - $this->assertEquals('foo', $manager->config['driver']); - $this->assertEquals('baz', $manager->config['bar']); + $manager = new ImageManager(['foo' => 'bar']); + $manager->configure(['foo' => 'baz', 'driver' => 'foo']); + $this->assertEquals('foo', $manager->getConfig('driver')); + $this->assertEquals('baz', $manager->getConfig('foo')); } - public function testConfigureObject() + public function testGetConfig() { - $config = ['driver' => new Intervention\Image\Imagick\Driver()]; - $manager = new ImageManager($config); + $manager = new ImageManager(['foo' => 'bar']); + $this->assertEquals('gd', $manager->getConfig('driver')); + $this->assertEquals('bar', $manager->getConfig('foo')); + } - $image = $manager->make('data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7'); - $this->assertInstanceOf('Intervention\Image\Image', $image); - $this->assertInstanceOf('Imagick', $image->getCore()); + public function testCreateGd() + { + $manager = new ImageManager(['driver' => 'gd']); + $image = $manager->create(5, 4); + $this->assertInstanceOf(ImageInterface::class, $image); + } + + public function testMakeGd() + { + $manager = new ImageManager(['driver' => 'gd']); + $image = $manager->make(__DIR__ . '/images/red.gif'); + $this->assertInstanceOf(ImageInterface::class, $image); } } diff --git a/tests/ImageTest.php b/tests/ImageTest.php deleted file mode 100644 index 870658cf..00000000 --- a/tests/ImageTest.php +++ /dev/null @@ -1,143 +0,0 @@ -getTestImage(); - $this->assertEquals('mock', $image->getCore()); - } - - public function testCommandCall() - { - $image = $this->getTestImage(); - $result = $image->test(1, 2, 3); - $this->assertEquals('mock', $result); - } - - public function testEncode() - { - $image = $this->getTestImage(); - $image->getDriver()->shouldReceive('encode')->with($image, 'png', 90)->once(); - $image->encode('png', 90); - } - - public function testSave() - { - $save_as = __DIR__.'/tmp/test.jpg'; - $image = $this->getTestImage(); - $image->getDriver()->shouldReceive('encode')->with($image, 'jpg', 85)->once()->andReturn('mock'); - $image = $image->save($save_as, 85); - $this->assertInstanceOf('\Intervention\Image\Image', $image); - $this->assertFileExists($save_as); - $this->assertEquals($image->basename, 'test.jpg'); - $this->assertEquals($image->extension, 'jpg'); - $this->assertEquals($image->filename, 'test'); - @unlink($save_as); - } - - public function testFormatSave() - { - $save_as = __DIR__.'/tmp/test'; - - $config = ['driver' => new Intervention\Image\Imagick\Driver()]; - $manager = new ImageManager($config); - - $image = $manager->make('data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7'); - $this->assertInstanceOf('Intervention\Image\Image', $image); - $this->assertInstanceOf('Imagick', $image->getCore()); - - $gifCore = $image->getCore(); - $this->assertEquals($gifCore->getImageMimeType(), 'image/gif'); - $image->save($save_as, null, 'jpg'); - - $this->assertEquals(\mime_content_type($save_as), 'image/jpeg'); - @unlink($save_as); - } - - public function testIsEncoded() - { - $image = $this->getTestImage(); - $this->assertFalse($image->isEncoded()); - - $image->setEncoded('foo'); - $this->assertTrue($image->isEncoded()); - } - - public function testFilter() - { - $demoFilter = Mockery::mock('\Intervention\Image\Filters\DemoFilter', [15]); - $image = $this->getTestImage(); - $demoFilter->shouldReceive('applyFilter')->with($image)->once()->andReturn($image); - $image->filter($demoFilter); - } - - public function testMime() - { - $image = $this->getTestImage(); - $this->assertEquals('image/png', $image->mime()); - } - - /** - * @expectedException \Intervention\Image\Exception\RuntimeException - */ - public function testGetBackupWithoutBackuo() - { - $image = $this->getTestImage(); - $image->getBackup(); - } - - public function testSetGetBackup() - { - $image = $this->getTestImage(); - $image->setBackup('foo'); - $backup = $image->getBackup(); - $this->assertEquals('foo', $backup); - } - - public function testGetBackups() - { - $image = $this->getTestImage(); - $backups = $image->getBackups(); - $this->assertEquals([], $backups); - - $image = $this->getTestImage(); - $image->setBackup('foo'); - $image->setBackup('bar'); - $image->setBackup('baz'); - $backups = $image->getBackups(); - $this->assertEquals(['default' => 'baz'], $backups); - - $image = $this->getTestImage(); - $image->setBackup('foo', 'a'); - $image->setBackup('bar', 'b'); - $image->setBackup('baz', 'c'); - $backups = $image->getBackups(); - $this->assertEquals(['a' => 'foo', 'b' => 'bar', 'c' => 'baz'], $backups); - } - - private function getTestImage() - { - $size = Mockery::mock('\Intervention\Image\Size', [800, 600]); - $driver = Mockery::mock('\Intervention\Image\AbstractDriver'); - $command = Mockery::mock('\Intervention\Image\Commands\AbstractCommand'); - $command->shouldReceive('hasOutput')->andReturn(true); - $command->shouldReceive('getOutput')->andReturn('mock'); - $driver->shouldReceive('executeCommand')->andReturn($command); - $image = new Image($driver, 'mock'); - $image->mime = 'image/png'; - $image->dirname = './tmp'; - $image->basename = 'foo.png'; - - return $image; - } -} diff --git a/tests/ImagickColorTest.php b/tests/ImagickColorTest.php deleted file mode 100644 index c51ec4da..00000000 --- a/tests/ImagickColorTest.php +++ /dev/null @@ -1,338 +0,0 @@ -pixel = Mockery::mock('ImagickPixel'); - $c->pixel->shouldReceive('getcolorvalue')->with(Imagick::COLOR_RED)->andReturn(0.956862745098); - $this->assertEquals(244, $c->getRedValue()); - - $c = new Color; - $c->pixel = Mockery::mock('ImagickPixel'); - $c->pixel->shouldReceive('getcolorvalue')->with(Imagick::COLOR_GREEN)->andReturn(0.0470588235294); - $this->assertEquals(12, $c->getGreenValue()); - - $c = new Color; - $c->pixel = Mockery::mock('ImagickPixel'); - $c->pixel->shouldReceive('getcolorvalue')->with(Imagick::COLOR_BLUE)->andReturn(0.0862745098039); - $this->assertEquals(22, $c->getBlueValue()); - - $c = new Color; - $c->pixel = Mockery::mock('ImagickPixel'); - $c->pixel->shouldReceive('getcolorvalue')->with(Imagick::COLOR_ALPHA)->andReturn(1); - $this->assertEquals(1, $c->getAlphaValue()); - - $c = new Color; - $c->pixel = Mockery::mock('ImagickPixel'); - $c->pixel->shouldReceive('getcolorvalue')->with(Imagick::COLOR_ALPHA)->andReturn(1); - $this->assertEquals(1, $c->getAlphaValue()); - } - - public function testConstructor() - { - $c = new Color; - $this->validateColor($c, 255, 255, 255, 0); - } - - public function testParseNull() - { - $c = new Color; - $c->parse(null); - $this->validateColor($c, 255, 255, 255, 0); - } - - public function testParseInteger() - { - $c = new Color; - $c->parse(16777215); - $this->validateColor($c, 255, 255, 255, 0); - - $c = new Color; - $c->parse(4294967295); - $this->validateColor($c, 255, 255, 255, 1); - } - - public function testParseArray() - { - $c = new Color; - $c->parse([181, 55, 23, 0.5]); - $this->validateColor($c, 181, 55, 23, 0.5); - } - - public function testParseHexString() - { - $c = new Color; - $c->parse('#b53717'); - $this->validateColor($c, 181, 55, 23, 1); - } - - public function testParseRgbaString() - { - $c = new Color; - $c->parse('rgba(181, 55, 23, 1)'); - $this->validateColor($c, 181, 55, 23, 1); - } - - public function testInitFromInteger() - { - $c = new Color; - $c->initFromInteger(0); - $this->validateColor($c, 0, 0, 0, 0); - $c->initFromInteger(2147483647); - $this->validateColor($c, 255, 255, 255, 0.5); - $c->initFromInteger(16777215); - $this->validateColor($c, 255, 255, 255, 0); - $c->initFromInteger(2130706432); - $this->validateColor($c, 0, 0, 0, 0.5); - $c->initFromInteger(867514135); - $this->validateColor($c, 181, 55, 23, 0.2); - } - - public function testInitFromArray() - { - $c = new Color; - $c->initFromArray([0, 0, 0, 0]); - $this->validateColor($c, 0, 0, 0, 0); - $c->initFromArray([0, 0, 0, 1]); - $this->validateColor($c, 0, 0, 0, 1); - $c->initFromArray([255, 255, 255, 1]); - $this->validateColor($c, 255, 255, 255, 1); - $c->initFromArray([255, 255, 255, 0]); - $this->validateColor($c, 255, 255, 255, 0); - $c->initFromArray([255, 255, 255, 0.5]); - $this->validateColor($c, 255, 255, 255, 0.5); - $c->initFromArray([0, 0, 0]); - $this->validateColor($c, 0, 0, 0, 1); - $c->initFromArray([255, 255, 255]); - $this->validateColor($c, 255, 255, 255, 1); - $c->initFromArray([181, 55, 23]); - $this->validateColor($c, 181, 55, 23, 1); - $c->initFromArray([181, 55, 23, 0.5]); - $this->validateColor($c, 181, 55, 23, 0.5); - } - - public function testInitFromHexString() - { - $c = new Color; - $c->initFromString('#cccccc'); - $this->validateColor($c, 204, 204, 204, 1); - $c->initFromString('#b53717'); - $this->validateColor($c, 181, 55, 23, 1); - $c->initFromString('ffffff'); - $this->validateColor($c, 255, 255, 255, 1); - $c->initFromString('ff00ff'); - $this->validateColor($c, 255, 0, 255, 1); - $c->initFromString('#000'); - $this->validateColor($c, 0, 0, 0, 1); - $c->initFromString('000'); - $this->validateColor($c, 0, 0, 0, 1); - } - - public function testInitFromRgbString() - { - $c = new Color; - $c->initFromString('rgb(1, 14, 144)'); - $this->validateColor($c, 1, 14, 144, 1); - $c->initFromString('rgb (255, 255, 255)'); - $this->validateColor($c, 255, 255, 255, 1); - $c->initFromString('rgb(0,0,0)'); - $this->validateColor($c, 0, 0, 0, 1); - $c->initFromString('rgba(0,0,0,0)'); - $this->validateColor($c, 0, 0, 0, 0); - $c->initFromString('rgba(0,0,0,0.5)'); - $this->validateColor($c, 0, 0, 0, 0.5); - $c->initFromString('rgba(255, 0, 0, 0.5)'); - $this->validateColor($c, 255, 0, 0, 0.5); - $c->initFromString('rgba(204, 204, 204, 0.9)'); - $this->validateColor($c, 204, 204, 204, 0.9); - } - - public function testInitFromRgb() - { - $c = new Color; - $c->initFromRgb(0, 0, 0); - $this->validateColor($c, 0, 0, 0, 1); - $c->initFromRgb(255, 255, 255); - $this->validateColor($c, 255, 255, 255, 1); - $c->initFromRgb(181, 55, 23); - $this->validateColor($c, 181, 55, 23, 1); - } - - public function testInitFromRgba() - { - $c = new Color; - $c->initFromRgba(0, 0, 0, 1); - $this->validateColor($c, 0, 0, 0, 1); - $c->initFromRgba(255, 255, 255, 1); - $this->validateColor($c, 255, 255, 255, 1); - $c->initFromRgba(181, 55, 23, 1); - $this->validateColor($c, 181, 55, 23, 1); - $c->initFromRgba(181, 55, 23, 0); - $this->validateColor($c, 181, 55, 23, 0); - $c->initFromRgba(181, 55, 23, 0.5); - $this->validateColor($c, 181, 55, 23, 0.5); - } - - public function testGetInt() - { - $c = new Color; - $i = $c->getInt(); - $this->assertInternalType('int', $i); - $this->assertEquals($i, 16777215); - - $c = new Color([255, 255, 255]); - $i = $c->getInt(); - $this->assertInternalType('int', $i); - $this->assertEquals($i, 4294967295); - - $c = new Color([255, 255, 255, 1]); - $i = $c->getInt(); - $this->assertInternalType('int', $i); - $this->assertEquals($i, 4294967295); - - $c = new Color([181, 55, 23, 0.2]); - $i = $c->getInt(); - $this->assertInternalType('int', $i); - $this->assertEquals($i, 867514135); - - $c = new Color([255, 255, 255, 0.5]); - $i = $c->getInt(); - $this->assertInternalType('int', $i); - $this->assertEquals($i, 2164260863); - - $c = new Color([181, 55, 23, 1]); - $i = $c->getInt(); - $this->assertInternalType('int', $i); - $this->assertEquals($i, 4290066199); - - $c = new Color([0, 0, 0, 0]); - $i = $c->getInt(); - $this->assertInternalType('int', $i); - $this->assertEquals($i, 0); - } - - public function testGetHex() - { - $c = new Color; - $i = $c->getHex(); - $this->assertInternalType('string', $i); - $this->assertEquals($i, 'ffffff'); - - $c = new Color([255, 255, 255, 1]); - $i = $c->getHex(); - $this->assertInternalType('string', $i); - $this->assertEquals($i, 'ffffff'); - - $c = new Color([181, 55, 23, 0.5]); - $i = $c->getHex(); - $this->assertInternalType('string', $i); - $this->assertEquals($i, 'b53717'); - - $c = new Color([0, 0, 0, 0]); - $i = $c->getHex('#'); - $this->assertInternalType('string', $i); - $this->assertEquals($i, '#000000'); - } - - public function testGetArray() - { - $c = new Color; - $i = $c->getArray(); - $this->assertInternalType('array', $i); - $this->assertEquals($i, [255, 255, 255, 0]); - - $c = new Color([255, 255, 255, 1]); - $i = $c->getArray(); - $this->assertInternalType('array', $i); - $this->assertEquals($i, [255, 255, 255, 1]); - - $c = new Color([181, 55, 23, 0.5]); - $i = $c->getArray(); - $this->assertInternalType('array', $i); - $this->assertEquals($i, [181, 55, 23, 0.5]); - - $c = new Color([0, 0, 0, 1]); - $i = $c->getArray(); - $this->assertInternalType('array', $i); - $this->assertEquals($i, [0, 0, 0, 1]); - } - - public function testGetRgba() - { - $c = new Color; - $i = $c->getRgba(); - $this->assertInternalType('string', $i); - $this->assertEquals($i, 'rgba(255, 255, 255, 0.00)'); - - $c = new Color([255, 255, 255, 1]); - $i = $c->getRgba(); - $this->assertInternalType('string', $i); - $this->assertEquals($i, 'rgba(255, 255, 255, 1.00)'); - - $c = new Color([181, 55, 23, 0.5]); - $i = $c->getRgba(); - $this->assertInternalType('string', $i); - $this->assertEquals($i, 'rgba(181, 55, 23, 0.50)'); - - $c = new Color([0, 0, 0, 1]); - $i = $c->getRgba(); - $this->assertInternalType('string', $i); - $this->assertEquals($i, 'rgba(0, 0, 0, 1.00)'); - - $c = new Color([255, 255, 255, 0.5]); - $i = $c->getRgba(); - $this->assertInternalType('string', $i); - $this->assertEquals($i, 'rgba(255, 255, 255, 0.50)'); - } - - public function testDiffers() - { - $c1 = new Color([0, 0, 0]); - $c2 = new Color([0, 0, 0]); - $this->assertEquals(false, $c1->differs($c2)); - - $c1 = new Color([1, 0, 0]); - $c2 = new Color([0, 0, 0]); - $this->assertEquals(true, $c1->differs($c2)); - - $c1 = new Color([1, 0, 0]); - $c2 = new Color([0, 0, 0]); - $this->assertEquals(false, $c1->differs($c2, 10)); - - $c1 = new Color([127, 127, 127]); - $c2 = new Color([0, 0, 0]); - $this->assertEquals(true, $c1->differs($c2, 49)); - - $c1 = new Color([127, 127, 127]); - $c2 = new Color([0, 0, 0]); - $this->assertEquals(false, $c1->differs($c2, 50)); - } - - /** - * @expectedException \Intervention\Image\Exception\NotReadableException - */ - public function testParseUnknown() - { - $c = new Color('xxxxxxxxxxxxxxxxxxxx'); - } - - private function validateColor($obj, $r, $g, $b, $a) - { - $this->assertInstanceOf('Intervention\Image\Imagick\Color', $obj); - $this->assertInstanceOf('ImagickPixel', $obj->pixel); - $this->assertEquals($r, round($obj->getRedValue(), 2)); - $this->assertEquals($g, round($obj->getGreenValue(), 2)); - $this->assertEquals($b, round($obj->getBlueValue(), 2)); - $this->assertEquals($a, round($obj->getAlphaValue(), 2)); - } -} diff --git a/tests/ImagickSystemTest.php b/tests/ImagickSystemTest.php deleted file mode 100644 index 25e961c6..00000000 --- a/tests/ImagickSystemTest.php +++ /dev/null @@ -1,1650 +0,0 @@ -manager()->make('tests/images/circle.png'); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInstanceOf('Imagick', $img->getCore()); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(50, $img->getWidth()); - $this->assertEquals(50, $img->getHeight()); - $this->assertEquals('image/png', $img->mime); - $this->assertEquals('tests/images', $img->dirname); - $this->assertEquals('circle.png', $img->basename); - $this->assertEquals('png', $img->extension); - $this->assertEquals('circle', $img->filename); - $this->assertEquals('image/png', $img->mime); - } - - public function testMakeFromString() - { - $str = file_get_contents('tests/images/circle.png'); - $img = $this->manager()->make($str); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInstanceOf('Imagick', $img->getCore()); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(50, $img->getWidth()); - $this->assertEquals(50, $img->getHeight()); - $this->assertEquals('image/png', $img->mime); - } - - public function testMakeFromImagick() - { - $imagick = new \Imagick; - $imagick->readImage('tests/images/circle.png'); - $img = $this->manager()->make($imagick); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInstanceOf('Imagick', $img->getCore()); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(50, $img->getWidth()); - $this->assertEquals(50, $img->getHeight()); - } - - public function testMakeFromDataUrl() - { - $str = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAGElEQVQYlWM8c+bMfwYiABMxikYVUk8hAHWzA3cRvs4UAAAAAElFTkSuQmCC'; - $img = $this->manager()->make($str); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInstanceOf('Imagick', $img->getCore()); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(10, $img->getWidth()); - $this->assertEquals(10, $img->getHeight()); - $this->assertEquals('image/png', $img->mime); - } - - public function testMakeFromBase64() - { - $str = 'iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAGElEQVQYlWM8c+bMfwYiABMxikYVUk8hAHWzA3cRvs4UAAAAAElFTkSuQmCC'; - $img = $this->manager()->make($str); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInstanceOf('Imagick', $img->getCore()); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(10, $img->getWidth()); - $this->assertEquals(10, $img->getHeight()); - $this->assertEquals('image/png', $img->mime); - } - - public function testMakeFromBase64WithNewlines() - { - $data = 'iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+' . "\n" . - '9AAAAGElEQVQYlWM8c+bMfwYiABMxikYVUk8hAHWzA3' . "\n" . - 'cRvs4UAAAAAElFTkSuQmCC'; - - $img = $this->manager()->make($data); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInstanceOf('Imagick', $img->getCore()); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(10, $img->getWidth()); - $this->assertEquals(10, $img->getHeight()); - $this->assertEquals('image/png', $img->mime); - } - - - public function testCanvas() - { - $img = $this->manager()->canvas(30, 20); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInstanceOf('Imagick', $img->getCore()); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(30, $img->getWidth()); - $this->assertEquals(20, $img->getHeight()); - $this->assertTransparentPosition($img, 0, 0); - } - - public function testCanvasWithSolidBackground() - { - $img = $this->manager()->canvas(30, 20, 'b53717'); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInstanceOf('Imagick', $img->getCore()); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(30, $img->getWidth()); - $this->assertEquals(20, $img->getHeight()); - $this->assertEquals('#b53717', $img->pickColor(15, 15, 'hex')); - } - - public function testGetSize() - { - $img = $this->manager()->make('tests/images/tile.png'); - $size = $img->getSize(); - $this->assertInstanceOf('Intervention\Image\Size', $size); - $this->assertInternalType('int', $size->width); - $this->assertInternalType('int', $size->height); - $this->assertEquals(16, $size->width); - $this->assertEquals(16, $size->height); - } - - public function testResizeImage() - { - $img = $this->manager()->make('tests/images/circle.png'); - $img->resize(120, 150); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInstanceOf('Imagick', $img->getCore()); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(120, $img->getWidth()); - $this->assertEquals(150, $img->getHeight()); - $this->assertTransparentPosition($img, 0, 0); - } - - public function testResizeImageOnlyWidth() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->resize(120, null); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInstanceOf('Imagick', $img->getCore()); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(120, $img->getWidth()); - $this->assertEquals(16, $img->getHeight()); - $this->assertTransparentPosition($img, 0, 15); - } - - public function testResizeImageOnlyHeight() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->resize(null, 150); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInstanceOf('Imagick', $img->getCore()); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(16, $img->getWidth()); - $this->assertEquals(150, $img->getHeight()); - $this->assertTransparentPosition($img, 15, 0); - } - - public function testResizeImageAutoHeight() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->resize(50, null, function ($constraint) { $constraint->aspectRatio(); }); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInstanceOf('Imagick', $img->getCore()); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(50, $img->getWidth()); - $this->assertEquals(50, $img->getHeight()); - $this->assertTransparentPosition($img, 30, 0); - } - - public function testResizeImageAutoWidth() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->resize(null, 50, function ($constraint) { $constraint->aspectRatio(); }); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInstanceOf('Imagick', $img->getCore()); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(50, $img->getWidth()); - $this->assertEquals(50, $img->getHeight()); - $this->assertTransparentPosition($img, 30, 0); - } - - public function testResizeDominantWidth() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->resize(100, 120, function ($constraint) { $constraint->aspectRatio(); }); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInstanceOf('Imagick', $img->getCore()); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(100, $img->getWidth()); - $this->assertEquals(100, $img->getHeight()); - $this->assertTransparentPosition($img, 60, 0); - } - - public function testResizeImagePreserveSimpleUpsizing() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->resize(100, 100, function ($constraint) { $constraint->aspectRatio(); $constraint->upsize(); }); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInstanceOf('Imagick', $img->getCore()); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(16, $img->getWidth()); - $this->assertEquals(16, $img->getHeight()); - $this->assertTransparentPosition($img, 15, 0); - } - - public function testWidenImage() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->widen(100); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInstanceOf('Imagick', $img->getCore()); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(100, $img->getWidth()); - $this->assertEquals(100, $img->getHeight()); - $this->assertTransparentPosition($img, 60, 0); - } - - public function testWidenImageWithConstraint() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->widen(100, function ($constraint) {$constraint->upsize();}); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInstanceOf('Imagick', $img->getCore()); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(16, $img->getWidth()); - $this->assertEquals(16, $img->getHeight()); - $this->assertTransparentPosition($img, 8, 0); - } - - public function testHeightenImage() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->heighten(100); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInstanceOf('Imagick', $img->getCore()); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(100, $img->getWidth()); - $this->assertEquals(100, $img->getHeight()); - $this->assertTransparentPosition($img, 60, 0); - } - - public function testHeightenImageWithConstraint() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->heighten(100, function ($constraint) {$constraint->upsize();}); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInstanceOf('Imagick', $img->getCore()); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(16, $img->getWidth()); - $this->assertEquals(16, $img->getHeight()); - $this->assertTransparentPosition($img, 8, 0); - } - - public function testResizeCanvasCenter() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->resizeCanvas(10, 10); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(10, $img->getWidth()); - $this->assertEquals(10, $img->getHeight()); - $this->assertColorAtPosition('#b4e000', $img, 0, 4); - $this->assertColorAtPosition('#445160', $img, 5, 5); - $this->assertTransparentPosition($img, 0, 5); - $this->assertTransparentPosition($img, 5, 4); - } - - public function testResizeCanvasTopLeft() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->resizeCanvas(10, 10, 'top-left'); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(10, $img->getWidth()); - $this->assertEquals(10, $img->getHeight()); - $this->assertColorAtPosition('#b4e000', $img, 0, 7); - $this->assertColorAtPosition('#445160', $img, 8, 8); - $this->assertTransparentPosition($img, 0, 8); - $this->assertTransparentPosition($img, 8, 7); - } - - public function testResizeCanvasTop() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->resizeCanvas(10, 10, 'top'); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(10, $img->getWidth()); - $this->assertEquals(10, $img->getHeight()); - $this->assertColorAtPosition('#b4e000', $img, 0, 7); - $this->assertColorAtPosition('#445160', $img, 5, 8); - $this->assertTransparentPosition($img, 0, 8); - $this->assertTransparentPosition($img, 5, 7); - } - - public function testResizeCanvasTopRight() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->resizeCanvas(10, 10, 'top-right'); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(10, $img->getWidth()); - $this->assertEquals(10, $img->getHeight()); - $this->assertColorAtPosition('#b4e000', $img, 0, 7); - $this->assertColorAtPosition('#445160', $img, 2, 8); - $this->assertTransparentPosition($img, 0, 8); - $this->assertTransparentPosition($img, 2, 7); - } - - public function testResizeCanvasLeft() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->resizeCanvas(10, 10, 'left'); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(10, $img->getWidth()); - $this->assertEquals(10, $img->getHeight()); - $this->assertColorAtPosition('#b4e000', $img, 0, 4); - $this->assertColorAtPosition('#445160', $img, 8, 5); - $this->assertTransparentPosition($img, 0, 5); - $this->assertTransparentPosition($img, 8, 4); - } - - public function testResizeCanvasRight() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->resizeCanvas(10, 10, 'right'); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(10, $img->getWidth()); - $this->assertEquals(10, $img->getHeight()); - $this->assertColorAtPosition('#b4e000', $img, 0, 4); - $this->assertColorAtPosition('#445160', $img, 2, 5); - $this->assertTransparentPosition($img, 0, 5); - $this->assertTransparentPosition($img, 2, 4); - } - - public function testResizeCanvasBottomLeft() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->resizeCanvas(10, 10, 'bottom-left'); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(10, $img->getWidth()); - $this->assertEquals(10, $img->getHeight()); - $this->assertColorAtPosition('#b4e000', $img, 0, 1); - $this->assertColorAtPosition('#445160', $img, 8, 2); - $this->assertTransparentPosition($img, 0, 2); - $this->assertTransparentPosition($img, 8, 1); - } - - public function testResizeCanvasBottomRight() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->resizeCanvas(10, 10, 'bottom-right'); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(10, $img->getWidth()); - $this->assertEquals(10, $img->getHeight()); - $this->assertColorAtPosition('#b4e000', $img, 0, 1); - $this->assertColorAtPosition('#445160', $img, 2, 2); - $this->assertTransparentPosition($img, 0, 2); - $this->assertTransparentPosition($img, 2, 1); - } - - public function testResizeCanvasBottom() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->resizeCanvas(10, 10, 'bottom'); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(10, $img->getWidth()); - $this->assertEquals(10, $img->getHeight()); - $this->assertColorAtPosition('#b4e000', $img, 0, 1); - $this->assertColorAtPosition('#445160', $img, 5, 2); - $this->assertTransparentPosition($img, 0, 2); - $this->assertTransparentPosition($img, 5, 1); - } - - public function testResizeCanvasRelativeWithBackground() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->resizeCanvas(4, 4, 'center', true, '#ff00ff'); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(20, $img->getWidth()); - $this->assertEquals(20, $img->getHeight()); - $this->assertColorAtPosition('#ff00ff', $img, 0, 0); - $this->assertColorAtPosition('#ff00ff', $img, 19, 19); - $this->assertColorAtPosition('#b4e000', $img, 2, 9); - $this->assertColorAtPosition('#445160', $img, 10, 10); - $this->assertTransparentPosition($img, 2, 10); - $this->assertTransparentPosition($img, 10, 9); - } - - public function testResizeCanvasJustWidth() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->resizeCanvas(10, null); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(10, $img->getWidth()); - $this->assertEquals(16, $img->getHeight()); - $this->assertColorAtPosition('#b4e000', $img, 0, 7); - $this->assertColorAtPosition('#445160', $img, 5, 8); - $this->assertTransparentPosition($img, 0, 8); - $this->assertTransparentPosition($img, 5, 7); - } - - public function testResizeCanvasJustHeight() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->resizeCanvas(null, 10); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(16, $img->getWidth()); - $this->assertEquals(10, $img->getHeight()); - $this->assertColorAtPosition('#b4e000', $img, 0, 4); - $this->assertColorAtPosition('#445160', $img, 8, 5); - $this->assertTransparentPosition($img, 0, 5); - $this->assertTransparentPosition($img, 8, 4); - } - - public function testResizeCanvasSmallerWidthLargerHeight() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->resizeCanvas(10, 20); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(10, $img->getWidth()); - $this->assertEquals(20, $img->getHeight()); - $this->assertColorAtPosition('#b4e000', $img, 0, 9); - $this->assertColorAtPosition('#445160', $img, 5, 10); - $this->assertTransparentPosition($img, 0, 10); - $this->assertTransparentPosition($img, 5, 9); - } - - public function testResizeCanvasLargerWidthSmallerHeight() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->resizeCanvas(20, 10); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(20, $img->getWidth()); - $this->assertEquals(10, $img->getHeight()); - $this->assertColorAtPosition('#b4e000', $img, 2, 4); - $this->assertColorAtPosition('#445160', $img, 10, 5); - $this->assertTransparentPosition($img, 0, 0); - $this->assertTransparentPosition($img, 2, 5); - $this->assertTransparentPosition($img, 10, 4); - } - - public function testResizeCanvasNegative() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->resizeCanvas(-4, -4); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(12, $img->getWidth()); - $this->assertEquals(12, $img->getHeight()); - $this->assertColorAtPosition('#b4e000', $img, 0, 5); - $this->assertColorAtPosition('#445160', $img, 6, 6); - $this->assertTransparentPosition($img, 0, 6); - $this->assertTransparentPosition($img, 6, 5); - } - - public function testResizeCanvasLargerHeightAutoWidth() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->resizeCanvas(null, 20, 'bottom-left', false, '#ff00ff'); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(16, $img->getWidth()); - $this->assertEquals(20, $img->getHeight()); - $this->assertColorAtPosition('#ff00ff', $img, 0, 0); - $this->assertColorAtPosition('#b4e000', $img, 0, 4); - $this->assertColorAtPosition('#b4e000', $img, 0, 11); - $this->assertColorAtPosition('#445160', $img, 8, 12); - $this->assertTransparentPosition($img, 0, 12); - $this->assertTransparentPosition($img, 8, 11); - } - - public function testResizeCanvasBorderNonRelative() - { - $img = $this->manager()->canvas(1, 1, 'ff0000'); - $img->resizeCanvas(17, 17, 'center', false, '333333'); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(17, $img->getWidth()); - $this->assertEquals(17, $img->getHeight()); - $this->assertColorAtPosition('#333333', $img, 0, 0); - $this->assertColorAtPosition('#333333', $img, 5, 5); - $this->assertColorAtPosition('#333333', $img, 7, 7); - $this->assertColorAtPosition('#ff0000', $img, 8, 8); - } - - public function testCropImage() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->crop(6, 6); // should be centered without pos. - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(6, $img->getWidth()); - $this->assertEquals(6, $img->getHeight()); - $this->assertColorAtPosition('#b4e000', $img, 0, 2); - $this->assertColorAtPosition('#445160', $img, 3, 3); - $this->assertTransparentPosition($img, 0, 3); - $this->assertTransparentPosition($img, 3, 2); - } - - public function testCropImageWithPosition() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->crop(4, 4, 7, 7); // should be centered without pos. - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(4, $img->getWidth()); - $this->assertEquals(4, $img->getHeight()); - $this->assertColorAtPosition('#b4e000', $img, 0, 0); - $this->assertColorAtPosition('#445160', $img, 1, 1); - $this->assertTransparentPosition($img, 0, 1); - $this->assertTransparentPosition($img, 1, 0); - } - - public function testFitImageSquare() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->fit(6); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(6, $img->getWidth()); - $this->assertEquals(6, $img->getHeight()); - $this->assertColorAtPosition('#b4e000', $img, 0, 2); - $this->assertColorAtPosition('#445160', $img, 3, 3); - $this->assertTransparentPosition($img, 0, 3); - $this->assertTransparentPosition($img, 3, 2); - } - - public function testFitImageRectangle() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->fit(12, 6); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(12, $img->getWidth()); - $this->assertEquals(6, $img->getHeight()); - $this->assertColorAtPosition('#b4e000', $img, 0, 2); - $this->assertColorAtPosition('#445160', $img, 6, 3); - $this->assertTransparentPosition($img, 0, 3); - $this->assertTransparentPosition($img, 6, 2); - } - - public function testFitImageWithConstraintUpsize() - { - $img = $this->manager()->make('tests/images/trim.png'); - $img->fit(300, 150, function ($constraint) {$constraint->upsize();}); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(50, $img->getWidth()); - $this->assertEquals(25, $img->getHeight()); - $this->assertColorAtPosition('#00aef0', $img, 0, 0); - $this->assertColorAtPosition('#afa94c', $img, 17, 0); - $this->assertColorAtPosition('#ffa601', $img, 24, 0); - } - - public function testFlipImageHorizontal() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->flip('h'); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(16, $img->getWidth()); - $this->assertEquals(16, $img->getHeight()); - $this->assertColorAtPosition('#b4e000', $img, 8, 7); - $this->assertColorAtPosition('#445160', $img, 0, 8); - $this->assertTransparentPosition($img, 0, 7); - $this->assertTransparentPosition($img, 8, 8); - } - - public function testFlipImageVertical() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->flip('v'); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(16, $img->getWidth()); - $this->assertEquals(16, $img->getHeight()); - $this->assertColorAtPosition('#b4e000', $img, 0, 8); - $this->assertColorAtPosition('#445160', $img, 8, 7); - $this->assertTransparentPosition($img, 0, 7); - $this->assertTransparentPosition($img, 8, 8); - } - - public function testRotateImage() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->rotate(90); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(16, $img->getWidth()); - $this->assertEquals(16, $img->getHeight()); - $this->assertColorAtPosition('#b4e000', $img, 0, 8); - $this->assertColorAtPosition('#445160', $img, 8, 7); - $this->assertTransparentPosition($img, 0, 7); - $this->assertTransparentPosition($img, 8, 8); - } - - public function testInsertImage() - { - $watermark = $this->manager()->canvas(16, 16, '#0000ff'); // create watermark - - // top-left anchor - $img = $this->manager()->canvas(32, 32, '#ff0000'); // create canvas - $img->insert($watermark, 'top-left', 0, 0); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals($img->getWidth(), 32); - $this->assertEquals($img->getHeight(), 32); - $this->assertEquals('#0000ff', $img->pickColor(0, 0, 'hex')); - $this->assertEquals('#ff0000', $img->pickColor(16, 16, 'hex')); - - // top-left anchor coordinates - $img = $this->manager()->canvas(32, 32, '#ff0000'); // create canvas - $img->insert($watermark, 'top-left', 10, 10); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals($img->getWidth(), 32); - $this->assertEquals($img->getHeight(), 32); - $this->assertEquals('#ff0000', $img->pickColor(9, 9, 'hex')); - $this->assertEquals('#0000ff', $img->pickColor(10, 10, 'hex')); - - // top anchor - $img = $this->manager()->canvas(32, 32, '#ff0000'); // create canvas - $img->insert($watermark, 'top', 0, 0); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals($img->getWidth(), 32); - $this->assertEquals($img->getHeight(), 32); - $this->assertEquals('#ff0000', $img->pickColor(0, 0, 'hex')); - $this->assertEquals('#0000ff', $img->pickColor(23, 15, 'hex')); - - // top anchor coordinates - $img = $this->manager()->canvas(32, 32, '#ff0000'); // create canvas - $img->insert($watermark, 'top', 10, 10); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals($img->getWidth(), 32); - $this->assertEquals($img->getHeight(), 32); - $this->assertEquals('#0000ff', $img->pickColor(18, 10, 'hex')); - $this->assertEquals('#ff0000', $img->pickColor(31, 26, 'hex')); - - // top-right anchor - $img = $this->manager()->canvas(32, 32, '#ff0000'); // create canvas - $img->insert($watermark, 'top-right', 0, 0); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals($img->getWidth(), 32); - $this->assertEquals($img->getHeight(), 32); - $this->assertEquals('#ff0000', $img->pickColor(15, 0, 'hex')); - $this->assertEquals('#0000ff', $img->pickColor(31, 0, 'hex')); - - // top-right anchor coordinates - $img = $this->manager()->canvas(32, 32, '#ff0000'); // create canvas - $img->insert($watermark, 'top-right', 10, 10); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals($img->getWidth(), 32); - $this->assertEquals($img->getHeight(), 32); - $this->assertEquals('#ff0000', $img->pickColor(6, 9, 'hex')); - $this->assertEquals('#0000ff', $img->pickColor(21, 25, 'hex')); - - // left anchor - $img = $this->manager()->canvas(32, 32, '#ff0000'); // create canvas - $img->insert($watermark, 'left', 0, 0); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals($img->getWidth(), 32); - $this->assertEquals($img->getHeight(), 32); - $this->assertEquals('#0000ff', $img->pickColor(15, 23, 'hex')); - $this->assertEquals('#ff0000', $img->pickColor(0, 7, 'hex')); - - // left anchor coordinates - $img = $this->manager()->canvas(32, 32, '#ff0000'); // create canvas - $img->insert($watermark, 'left', 10, 10); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals($img->getWidth(), 32); - $this->assertEquals($img->getHeight(), 32); - $this->assertEquals('#ff0000', $img->pickColor(8, 23, 'hex')); - $this->assertEquals('#ff0000', $img->pickColor(10, 7, 'hex')); - $this->assertEquals('#0000ff', $img->pickColor(25, 23, 'hex')); - $this->assertEquals('#0000ff', $img->pickColor(25, 8, 'hex')); - - // right anchor - $img = $this->manager()->canvas(32, 32, '#ff0000'); // create canvas - $img->insert($watermark, 'right', 0, 0); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals($img->getWidth(), 32); - $this->assertEquals($img->getHeight(), 32); - $this->assertEquals('#0000ff', $img->pickColor(31, 23, 'hex')); - $this->assertEquals('#ff0000', $img->pickColor(15, 15, 'hex')); - - // right anchor coordinates - $img = $this->manager()->canvas(32, 32, '#ff0000'); // create canvas - $img->insert($watermark, 'right', 10, 10); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals($img->getWidth(), 32); - $this->assertEquals($img->getHeight(), 32); - $this->assertEquals('#ff0000', $img->pickColor(5, 8, 'hex')); - $this->assertEquals('#ff0000', $img->pickColor(22, 23, 'hex')); - $this->assertEquals('#ff0000', $img->pickColor(21, 7, 'hex')); - $this->assertEquals('#0000ff', $img->pickColor(6, 8, 'hex')); - $this->assertEquals('#0000ff', $img->pickColor(21, 23, 'hex')); - $this->assertEquals('#0000ff', $img->pickColor(6, 23, 'hex')); - - // bottom-left anchor - $img = $this->manager()->canvas(32, 32, '#ff0000'); // create canvas - $img->insert($watermark, 'bottom-left', 0, 0); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals($img->getWidth(), 32); - $this->assertEquals($img->getHeight(), 32); - $this->assertEquals('#0000ff', $img->pickColor(15, 31, 'hex')); - $this->assertEquals('#ff0000', $img->pickColor(0, 15, 'hex')); - - // bottom-left anchor coordinates - $img = $this->manager()->canvas(32, 32, '#ff0000'); // create canvas - $img->insert($watermark, 'bottom-left', 10, 10); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals($img->getWidth(), 32); - $this->assertEquals($img->getHeight(), 32); - $this->assertEquals('#0000ff', $img->pickColor(10, 21, 'hex')); - $this->assertEquals('#ff0000', $img->pickColor(9, 20, 'hex')); - - // bottom anchor - $img = $this->manager()->canvas(32, 32, '#ff0000'); // create canvas - $img->insert($watermark, 'bottom', 0, 0); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals($img->getWidth(), 32); - $this->assertEquals($img->getHeight(), 32); - $this->assertEquals('#0000ff', $img->pickColor(8, 16, 'hex')); - $this->assertEquals('#ff0000', $img->pickColor(8, 15, 'hex')); - - // bottom anchor coordinates - $img = $this->manager()->canvas(32, 32, '#ff0000'); // create canvas - $img->insert($watermark, 'bottom', 10, 10); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals($img->getWidth(), 32); - $this->assertEquals($img->getHeight(), 32); - $this->assertEquals('#ff0000', $img->pickColor(5, 8, 'hex')); - $this->assertEquals('#ff0000', $img->pickColor(23, 22, 'hex')); - $this->assertEquals('#ff0000', $img->pickColor(24, 21, 'hex')); - $this->assertEquals('#ff0000', $img->pickColor(7, 6, 'hex')); - $this->assertEquals('#0000ff', $img->pickColor(8, 6, 'hex')); - $this->assertEquals('#0000ff', $img->pickColor(23, 21, 'hex')); - $this->assertEquals('#0000ff', $img->pickColor(23, 6, 'hex')); - - // bottom-right anchor - $img = $this->manager()->canvas(32, 32, '#ff0000'); // create canvas - $img->insert($watermark, 'bottom-right', 0, 0); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals($img->getWidth(), 32); - $this->assertEquals($img->getHeight(), 32); - $this->assertEquals('#0000ff', $img->pickColor(16, 16, 'hex')); - $this->assertEquals('#ff0000', $img->pickColor(15, 16, 'hex')); - - // bottom-right anchor coordinates - $img = $this->manager()->canvas(32, 32, '#ff0000'); // create canvas - $img->insert($watermark, 'bottom-right', 10, 10); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals($img->getWidth(), 32); - $this->assertEquals($img->getHeight(), 32); - $this->assertEquals('#0000ff', $img->pickColor(21, 21, 'hex')); - $this->assertEquals('#ff0000', $img->pickColor(22, 22, 'hex')); - - // center anchor - $img = $this->manager()->canvas(32, 32, '#ff0000'); // create canvas - $img->insert($watermark, 'center', 0, 0); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals($img->getWidth(), 32); - $this->assertEquals($img->getHeight(), 32); - $this->assertEquals('#0000ff', $img->pickColor(23, 23, 'hex')); - $this->assertEquals('#ff0000', $img->pickColor(8, 7, 'hex')); - - // center anchor coordinates / coordinates will be ignored for center - $img = $this->manager()->canvas(32, 32, '#ff0000'); // create canvas - $img->insert($watermark, 'center', 10, 10); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals($img->getWidth(), 32); - $this->assertEquals($img->getHeight(), 32); - $this->assertEquals('#0000ff', $img->pickColor(23, 23, 'hex')); - $this->assertEquals('#ff0000', $img->pickColor(8, 7, 'hex')); - } - - public function testInsertWithAlphaChannel() - { - $img = $this->manager()->canvas(50, 50, 'ff0000'); - $img->insert('tests/images/circle.png'); - $this->assertColorAtPosition('#ff0000', $img, 0, 0); - $this->assertColorAtPosition('#330000', $img, 30, 30); - } - - public function testInsertAfterResize() - { - $img = $this->manager()->make('tests/images/trim.png'); - $img->resize(16, 16)->insert('tests/images/tile.png'); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(16, $img->getWidth()); - $this->assertEquals(16, $img->getHeight()); - $this->assertColorAtPosition('#b4e000', $img, 0, 7); - $this->assertColorAtPosition('#00aef0', $img, 0, 8); - $this->assertColorAtPosition('#445160', $img, 8, 8); - $this->assertColorAtPosition('#ffa601', $img, 8, 7); - } - - public function testInsertImagick() - { - $imagick = new \Imagick; - $imagick->readImage('tests/images/tile.png'); - $img = $this->manager()->make('tests/images/trim.png'); - $img->insert($imagick); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertColorAtPosition('#b4e000', $img, 0, 7); - $this->assertColorAtPosition('#00aef0', $img, 0, 8); - $this->assertColorAtPosition('#445160', $img, 8, 8); - $this->assertColorAtPosition('#00aef0', $img, 8, 7); - $this->assertColorAtPosition('#ffa601', $img, 24, 24); - } - - public function testInsertBinary() - { - $data = file_get_contents('tests/images/tile.png'); - $img = $this->manager()->make('tests/images/trim.png'); - $img->insert($data); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertColorAtPosition('#b4e000', $img, 0, 7); - $this->assertColorAtPosition('#00aef0', $img, 0, 8); - $this->assertColorAtPosition('#445160', $img, 8, 8); - $this->assertColorAtPosition('#00aef0', $img, 8, 7); - $this->assertColorAtPosition('#ffa601', $img, 24, 24); - } - - public function testInsertInterventionImage() - { - $obj = $this->manager()->make('tests/images/tile.png'); - $img = $this->manager()->make('tests/images/trim.png'); - $img->insert($obj); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertColorAtPosition('#b4e000', $img, 0, 7); - $this->assertColorAtPosition('#00aef0', $img, 0, 8); - $this->assertColorAtPosition('#445160', $img, 8, 8); - $this->assertColorAtPosition('#00aef0', $img, 8, 7); - $this->assertColorAtPosition('#ffa601', $img, 24, 24); - } - - public function testOpacity() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->opacity(50); - $checkColor = $img->pickColor(7, 7, 'array'); - $this->assertEquals($checkColor[0], 180); - $this->assertEquals($checkColor[1], 224); - $this->assertEquals($checkColor[2], 0); - $this->assertEquals($checkColor[3], 0.5); - $checkColor = $img->pickColor(8, 8, 'array'); - $this->assertEquals($checkColor[0], 68); - $this->assertEquals($checkColor[1], 81); - $this->assertEquals($checkColor[2], 96); - $this->assertEquals($checkColor[3], 0.5); - $this->assertTransparentPosition($img, 0, 11); - } - - public function testMaskImage() - { - $img = $this->manager()->make('tests/images/trim.png'); - $img->mask('tests/images/gradient.png'); - $this->assertTransparentPosition($img, 0, 0); - $this->assertTransparentPosition($img, 18, 18); - $this->assertTransparentPosition($img, 23, 23); - $this->assertTransparentPosition($img, 30, 30); - $alpha = $img->pickColor(23, 24, 'array'); - $this->assertLessThan(1, $alpha[3]); - $this->assertGreaterThanOrEqual(0, $alpha[3]); - $alpha = $img->pickColor(35, 25, 'array'); - $this->assertLessThan(1, $alpha[3]); - $this->assertGreaterThanOrEqual(0, $alpha[3]); - $alpha = $img->pickColor(25, 42, 'array'); - $this->assertLessThan(1, $alpha[3]); - $this->assertGreaterThanOrEqual(0, $alpha[3]); - } - - public function testMaskImageWithAlpha() - { - $img = $this->manager()->make('tests/images/trim.png'); - $img->mask('tests/images/star.png', true); - $this->assertTransparentPosition($img, 0, 0); - $this->assertTransparentPosition($img, 16, 16); - $this->assertTransparentPosition($img, 36, 36); - $this->assertTransparentPosition($img, 47, 47); - $alpha = $img->pickColor(18, 18, 'array'); - $this->assertLessThan(1, $alpha[3]); - $this->assertGreaterThanOrEqual(0, $alpha[3]); - $alpha = $img->pickColor(22, 35, 'array'); - $this->assertLessThan(1, $alpha[3]); - $this->assertGreaterThanOrEqual(0, $alpha[3]); - } - - public function testPixelateImage() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->pixelate(20); - $this->assertInstanceOf('Intervention\Image\Image', $img); - } - - public function testGreyscaleImage() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->greyscale(); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertTransparentPosition($img, 8, 0); - $this->assertColorAtPosition('#707070', $img, 0, 0); - } - - public function testInvertImage() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->invert(); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertTransparentPosition($img, 8, 0); - $this->assertColorAtPosition('#4b1fff', $img, 0, 0); - } - - public function testBlurImage() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->blur(1); - $this->assertInstanceOf('Intervention\Image\Image', $img); - } - - public function testFillImageWithColor() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->fill('b53717'); - $this->assertColorAtPosition('#b53717', $img, 0, 0); - $this->assertColorAtPosition('#b53717', $img, 15, 15); - } - - public function testFillImageWithColorAtPosition() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->fill('b53717', 0, 0); - $this->assertTransparentPosition($img, 0, 8); - $this->assertColorAtPosition('#b53717', $img, 0, 0); - $this->assertColorAtPosition('#445160', $img, 15, 15); - } - - public function testFillImageWithImagick() - { - $imagick = new \Imagick; - $imagick->readImage('tests/images/tile.png'); - $img = $this->manager()->make('tests/images/trim.png'); - $img->fill($imagick, 0, 0); - $this->assertColorAtPosition('#b4e000', $img, 0, 0); - $this->assertColorAtPosition('#445160', $img, 8, 8); - $this->assertColorAtPosition('#00aef0', $img, 8, 7); - $this->assertColorAtPosition('#ffa601', $img, 20, 20); - } - - public function testFillImageWithBinary() - { - $data = file_get_contents('tests/images/tile.png'); - $img = $this->manager()->make('tests/images/trim.png'); - $img->fill($data, 0, 0); - $this->assertColorAtPosition('#b4e000', $img, 0, 0); - $this->assertColorAtPosition('#445160', $img, 8, 8); - $this->assertColorAtPosition('#00aef0', $img, 8, 7); - $this->assertColorAtPosition('#ffa601', $img, 20, 20); - } - - public function testFillImageWithInterventionImage() - { - $obj = $this->manager()->make('tests/images/tile.png'); - $img = $this->manager()->make('tests/images/trim.png'); - $img->fill($obj, 0, 0); - $this->assertColorAtPosition('#b4e000', $img, 0, 0); - $this->assertColorAtPosition('#445160', $img, 8, 8); - $this->assertColorAtPosition('#00aef0', $img, 8, 7); - $this->assertColorAtPosition('#ffa601', $img, 20, 20); - } - - public function testPixelImage() - { - $img = $this->manager()->make('tests/images/tile.png'); - $coords = [[5, 5], [12, 12]]; - $img = $img->pixel('fdf5e4', $coords[0][0], $coords[0][1]); - $img = $img->pixel([255, 255, 255], $coords[1][0], $coords[1][1]); - $this->assertEquals('#fdf5e4', $img->pickColor($coords[0][0], $coords[0][1], 'hex')); - $this->assertEquals('#ffffff', $img->pickColor($coords[1][0], $coords[1][1], 'hex')); - } - - public function testRectangleImage() - { - $img = $this->manager()->canvas(16, 16, 'ffffff'); - $img->rectangle(5, 5, 11, 11, function ($draw) { $draw->background('#ff0000'); $draw->border(1, '#0000ff'); }); - $this->assertEquals('32ceca9759d1973dd461b39664df604d', $img->checksum()); - } - - public function testLineImage() - { - $img = $this->manager()->canvas(16, 16, 'ffffff'); - $img->line(0, 0, 15, 15, function ($draw) { $draw->color('#ff0000'); }); - $this->assertEquals('f5c585019bff361d91e2928b2ac2286b', $img->checksum()); - } - - public function testEllipseImage() - { - $img = $this->manager()->canvas(16, 16, 'ffffff'); - $img->ellipse(12, 8, 8, 8, function ($draw) { $draw->background('#ff0000'); $draw->border(1, '#0000ff'); }); - $this->assertEquals('9dc5bbec6d45868610c082a1d67640b5', $img->checksum()); - } - - public function testCircleImage() - { - $img = $this->manager()->canvas(16, 16, 'ffffff'); - $img->circle(12, 8, 8, function ($draw) { $draw->background('#ff0000'); $draw->border(1, '#0000ff'); }); - $this->assertEquals('a433c7c1a842ef83e1cb45875371358c', $img->checksum()); - } - - public function testPolygonImage() - { - $img = $this->manager()->canvas(16, 16, 'ffffff'); - $points = [3, 3, 11, 11, 7, 13]; - $img->polygon($points, function ($draw) { $draw->background('#ff0000'); $draw->border(1, '#0000ff'); }); - $this->assertEquals('e301afe179da858d441ad8fc0eb5490a', $img->checksum()); - } - - public function testResetImage() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->backup(); - $img->resize(30, 20); - $img->reset(); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(16, $img->getWidth()); - $this->assertEquals(16, $img->getHeight()); - } - - public function testResetEmptyImage() - { - $img = $this->manager()->canvas(16, 16, '#0000ff'); - $img->backup(); - $img->resize(30, 20); - $img->fill('#ff0000'); - $img->reset(); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(16, $img->getWidth()); - $this->assertEquals(16, $img->getHeight()); - $this->assertColorAtPosition('#0000ff', $img, 0, 0); - } - - public function testResetKeepTransparency() - { - $img = $this->manager()->make('tests/images/circle.png'); - $img->backup(); - $img->reset(); - $this->assertTransparentPosition($img, 0, 0); - } - - public function testResetToNamed() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->backup('original'); - $img->resize(30, 20); - $img->backup('30x20'); - - // reset to original - $img->reset('original'); - $this->assertEquals(16, $img->getWidth()); - $this->assertEquals(16, $img->getHeight()); - - // reset to 30x20 - $img->reset('30x20'); - $this->assertEquals(30, $img->getWidth()); - $this->assertEquals(20, $img->getHeight()); - - // reset to original again - $img->reset('original'); - $this->assertEquals(16, $img->getWidth()); - $this->assertEquals(16, $img->getHeight()); - } - - public function testLimitColors() - { - $img = $this->manager()->make('tests/images/trim.png'); - $img->limitColors(4); - $this->assertLessThanOrEqual(5, $img->getCore()->getImageColors()); - } - - public function testLimitColorsKeepTransparency() - { - $img = $this->manager()->make('tests/images/star.png'); - $img->limitColors(16); - $this->assertLessThanOrEqual(17, $img->getCore()->getImageColors()); - $this->assertTransparentPosition($img, 0, 0); - $this->assertColorAtPosition('#680098', $img, 6, 12); - $this->assertColorAtPosition('#c2596a', $img, 22, 24); - } - - public function testLimitColorsKeepTransparencyWithMatte() - { - $img = $this->manager()->make('tests/images/star.png'); - $img->limitColors(32, '#00ff00'); - $this->assertLessThanOrEqual(33, $img->getCore()->getImageColors()); - $this->assertTransparentPosition($img, 0, 0); - $this->assertColorAtPosition('#00ff00', $img, 12, 10); - $this->assertColorAtPosition('#00ff00', $img, 22, 17); - $this->assertColorAtPosition('#e70012', $img, 16, 21); - } - - public function testLimitColorsNullWithMatte() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->limitColors(null, '#ff00ff'); - $this->assertColorAtPosition('#b4e000', $img, 0, 0); - $this->assertColorAtPosition('#445160', $img, 8, 8); - $this->assertColorAtPosition('#ff00ff', $img, 0, 8); - $this->assertColorAtPosition('#ff00ff', $img, 15, 0); - } - - public function testPickColorFromTrueColor() - { - $img = $this->manager()->make('tests/images/star.png'); - $c = $img->pickColor(0, 0); - $this->assertEquals(255, $c[0]); - $this->assertEquals(255, $c[1]); - $this->assertEquals(255, $c[2]); - $this->assertEquals(0, $c[3]); - - $c = $img->pickColor(11, 11); - $this->assertEquals(34, $c[0]); - $this->assertEquals(0, $c[1]); - $this->assertEquals(160, $c[2]); - $this->assertEquals(0.47, $c[3]); - - $c = $img->pickColor(16, 16); - $this->assertEquals(231, $c[0]); - $this->assertEquals(0, $c[1]); - $this->assertEquals(18, $c[2]); - $this->assertEquals(1, $c[3]); - } - - public function testPickColorFromIndexed() - { - $img = $this->manager()->make('tests/images/tile.png'); - $c = $img->pickColor(0, 0); - $this->assertEquals(180, $c[0]); - $this->assertEquals(224, $c[1]); - $this->assertEquals(0, $c[2]); - $this->assertEquals(1, $c[3]); - - $c = $img->pickColor(8, 8); - $this->assertEquals(68, $c[0]); - $this->assertEquals(81, $c[1]); - $this->assertEquals(96, $c[2]); - $this->assertEquals(1, $c[3]); - - $c = $img->pickColor(0, 15); - $this->assertEquals(0, $c[0]); - $this->assertEquals(0, $c[1]); - $this->assertEquals(0, $c[2]); - $this->assertEquals(0, $c[3]); - } - - public function testPickColorFromPalette() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->getCore()->quantizeImage(200, \Imagick::COLORSPACE_RGB, 0, false, false); - - $c = $img->pickColor(0, 0); - $this->assertEquals(180, $c[0]); - $this->assertEquals(224, $c[1]); - $this->assertEquals(0, $c[2]); - $this->assertEquals(1, $c[3]); - - $c = $img->pickColor(8, 8); - $this->assertEquals(68, $c[0]); - $this->assertEquals(81, $c[1]); - $this->assertEquals(96, $c[2]); - $this->assertEquals(1, $c[3]); - - $c = $img->pickColor(0, 15); - $this->assertEquals(0, $c[0]); - $this->assertEquals(0, $c[1]); - $this->assertEquals(0, $c[2]); - $this->assertEquals(0, $c[3]); - } - - public function testInterlaceImage() - { - $img = $this->manager()->make('tests/images/trim.png'); - $img->interlace(); - $img->encode('png'); - $this->assertTrue((ord($img->encoded[28]) != '0')); - $img->interlace(false); - $img->encode('png'); - $this->assertFalse((ord($img->encoded[28]) != '0')); - } - - public function testGammaImage() - { - $img = $this->manager()->make('tests/images/trim.png'); - $img->gamma(1.6); - $this->assertColorAtPosition('#00c9f6', $img, 0, 0); - $this->assertColorAtPosition('#ffc308', $img, 24, 24); - } - - public function testBrightnessImage() - { - $img = $this->manager()->make('tests/images/trim.png'); - $img->brightness(35); - $this->assertColorAtPosition('#45ccff', $img, 0, 0); - $this->assertColorAtPosition('#ffc55b', $img, 24, 24); - } - - public function testContrastImage() - { - $img = $this->manager()->make('tests/images/trim.png'); - $img->contrast(35); - $this->assertColorAtPosition('#00feff', $img, 0, 0); - $this->assertColorAtPosition('#fffd04', $img, 24, 24); - } - - public function testColorizeImage() - { - $img = $this->manager()->make('tests/images/trim.png'); - $img->colorize(40, 25, -50); - $this->assertColorAtPosition('#00ece2', $img, 0, 0); - $this->assertColorAtPosition('#ffea00', $img, 24, 24); - } - - public function testTrimGradient() - { - $canvas = $this->manager()->make('tests/images/gradient.png'); - - $img = clone $canvas; - $img->trim(); - $this->assertEquals($img->getWidth(), 46); - $this->assertEquals($img->getHeight(), 46); - - $img = clone $canvas; - $img->trim(null, null, 10); - $this->assertEquals($img->getWidth(), 38); - $this->assertEquals($img->getHeight(), 38); - - $img = clone $canvas; - $img->trim(null, null, 20); - $this->assertEquals($img->getWidth(), 34); - $this->assertEquals($img->getHeight(), 34); - - $img = clone $canvas; - $img->trim(null, null, 30); - $this->assertEquals($img->getWidth(), 30); - $this->assertEquals($img->getHeight(), 30); - - $img = clone $canvas; - $img->trim(null, null, 40); - $this->assertEquals($img->getWidth(), 26); - $this->assertEquals($img->getHeight(), 26); - - $img = clone $canvas; - $img->trim(null, null, 50); - $this->assertEquals($img->getWidth(), 22); - $this->assertEquals($img->getHeight(), 22); - - $img = clone $canvas; - $img->trim(null, null, 60); - $this->assertEquals($img->getWidth(), 20); - $this->assertEquals($img->getHeight(), 20); - - $img = clone $canvas; - $img->trim(null, null, 70); - $this->assertEquals($img->getWidth(), 16); - $this->assertEquals($img->getHeight(), 16); - - $img = clone $canvas; - $img->trim(null, null, 80); - $this->assertEquals($img->getWidth(), 12); - $this->assertEquals($img->getHeight(), 12); - - $img = clone $canvas; - $img->trim(null, null, 90); - $this->assertEquals($img->getWidth(), 8); - $this->assertEquals($img->getHeight(), 8); - } - - public function testTrimOnlyLeftAndRight() - { - $img = $this->manager()->make('tests/images/gradient.png'); - $img->trim(null, ['left', 'right'], 60); - $this->assertEquals($img->getWidth(), 20); - $this->assertEquals($img->getHeight(), 50); - } - - public function testTrimOnlyTopAndBottom() - { - $img = $this->manager()->make('tests/images/gradient.png'); - $img->trim(null, ['top', 'bottom'], 60); - $this->assertEquals($img->getWidth(), 50); - $this->assertEquals($img->getHeight(), 20); - } - - public function testTrimOnlyTop() - { - $img = $this->manager()->make('tests/images/gradient.png'); - $img->trim(null, 'top', 60); - $this->assertEquals($img->getWidth(), 50); - $this->assertEquals($img->getHeight(), 35); - } - - public function testTrimOnlyBottom() - { - $img = $this->manager()->make('tests/images/gradient.png'); - $img->trim(null, 'top', 60); - $this->assertEquals($img->getWidth(), 50); - $this->assertEquals($img->getHeight(), 35); - } - - public function testTrimWithFeather() - { - $img = $this->manager()->make('tests/images/trim.png'); - $feather = 5; - $img->trim(null, null, null, $feather); - $this->assertEquals($img->getWidth(), 28 + $feather * 2); - $this->assertEquals($img->getHeight(), 28 + $feather * 2); - $img->destroy(); - - $img = $this->manager()->make('tests/images/trim.png'); - $feather = 10; - $img->trim(null, null, null, $feather); - $this->assertEquals($img->getWidth(), 28 + $feather * 2); - $this->assertEquals($img->getHeight(), 28 + $feather * 2); - $img->destroy(); - - $img = $this->manager()->make('tests/images/trim.png'); - $feather = 20; // must respect original dimensions of image - $img->trim(null, null, null, $feather); - $this->assertEquals($img->getWidth(), 50); - $this->assertEquals($img->getHeight(), 50); - $img->destroy(); - - $img = $this->manager()->make('tests/images/trim.png'); - $feather = -5; - $img->trim(null, null, null, $feather); - $this->assertEquals($img->getWidth(), 28 + $feather * 2); - $this->assertEquals($img->getHeight(), 28 + $feather * 2); - $img->destroy(); - - $img = $this->manager()->make('tests/images/trim.png'); - $feather = -10; - $img->trim(null, null, null, $feather); - $this->assertEquals($img->getWidth(), 28 + $feather * 2); - $this->assertEquals($img->getHeight(), 28 + $feather * 2); - $img->destroy(); - - // trim only left and right with feather - $img = $this->manager()->make('tests/images/trim.png'); - $feather = 10; - $img->trim(null, ['left', 'right'], null, $feather); - $this->assertEquals($img->getWidth(), 28 + $feather * 2); - $this->assertEquals($img->getHeight(), 50); - $img->destroy(); - - // trim only top and bottom with feather - $img = $this->manager()->make('tests/images/trim.png'); - $feather = 10; - $img->trim(null, ['top', 'bottom'], null, $feather); - $this->assertEquals($img->getWidth(), 50); - $this->assertEquals($img->getHeight(), 28 + $feather * 2); - $img->destroy(); - - // trim with tolerance and feather - $img = $this->manager()->make('tests/images/gradient.png'); - $feather = 2; - $img->trim(null, null, 10, $feather); - $this->assertEquals($img->getWidth(), 38 + $feather * 2); - $this->assertEquals($img->getHeight(), 38 + $feather * 2); - $img->destroy(); - - $img = $this->manager()->make('tests/images/gradient.png'); - $feather = 5; - $img->trim(null, null, 10, $feather); - $this->assertEquals($img->getWidth(), 38 + $feather * 2); - $this->assertEquals($img->getHeight(), 38 + $feather * 2); - $img->destroy(); - - $img = $this->manager()->make('tests/images/gradient.png'); - $feather = 10; // should respect original dimensions - $img->trim(null, null, 20, $feather); - $this->assertEquals($img->getWidth(), 50); - $this->assertEquals($img->getHeight(), 50); - $img->destroy(); - } - - public function testEncodeDefault() - { - $img = $this->manager()->make('tests/images/trim.png'); - $img->encode(); - $mime = finfo_buffer(finfo_open(FILEINFO_MIME_TYPE), $img->encoded); - $this->assertEquals('image/png', $mime); - } - - public function testEncodeJpeg() - { - $img = $this->manager()->make('tests/images/trim.png'); - $img->encode('jpg'); - $mime = finfo_buffer(finfo_open(FILEINFO_MIME_TYPE), $img->encoded); - $this->assertEquals('image/jpeg', $mime); - } - - public function testEncodeGif() - { - $img = $this->manager()->make('tests/images/trim.png'); - $img->encode('gif'); - $mime = finfo_buffer(finfo_open(FILEINFO_MIME_TYPE), $img->encoded); - $this->assertEquals('image/gif', $mime); - } - - public function testEncodeDataUrl() - { - $img = $this->manager()->make('tests/images/trim.png'); - $img->encode('data-url'); - $this->assertEquals('data:image/png;base64', substr($img->encoded, 0, 21)); - } - - public function testExifReadAll() - { - $img = $this->manager()->make('tests/images/exif.jpg'); - $data = $img->exif(); - $this->assertInternalType('array', $data); - $this->assertGreaterThanOrEqual(13, count($data)); - } - - public function testExifReadKey() - { - $img = $this->manager()->make('tests/images/exif.jpg'); - $data = $img->exif('Artist'); - $this->assertInternalType('string', $data); - $this->assertEquals('Oliver Vogel', $data); - } - - public function testExifReadNotExistingKey() - { - $img = $this->manager()->make('tests/images/exif.jpg'); - $data = $img->exif('xxx'); - $this->assertEquals(null, $data); - } - - public function testSaveImage() - { - $save_as = 'tests/tmp/foo.jpg'; - $img = $this->manager()->make('tests/images/trim.png'); - $img->save($save_as, 80); - $this->assertFileExists($save_as); - $this->assertEquals($img->dirname, 'tests/tmp'); - $this->assertEquals($img->basename, 'foo.jpg'); - $this->assertEquals($img->extension, 'jpg'); - $this->assertEquals($img->filename, 'foo'); - $this->assertEquals($img->mime, 'image/jpeg'); - @unlink($save_as); - - $save_as = 'tests/tmp/foo.png'; - $img = $this->manager()->make('tests/images/trim.png'); - $img->save($save_as); - $this->assertEquals($img->dirname, 'tests/tmp'); - $this->assertEquals($img->basename, 'foo.png'); - $this->assertEquals($img->extension, 'png'); - $this->assertEquals($img->filename, 'foo'); - $this->assertEquals($img->mime, 'image/png'); - $this->assertFileExists($save_as); - @unlink($save_as); - - $save_as = 'tests/tmp/foo.jpg'; - $img = $this->manager()->make('tests/images/trim.png'); - $img->save($save_as, 0); - $this->assertEquals($img->dirname, 'tests/tmp'); - $this->assertEquals($img->basename, 'foo.jpg'); - $this->assertEquals($img->extension, 'jpg'); - $this->assertEquals($img->filename, 'foo'); - $this->assertEquals($img->mime, 'image/jpeg'); - $this->assertFileExists($save_as); - @unlink($save_as); - } - - public function testSaveImageWithoutParameter() - { - $path = 'tests/tmp/bar.png'; - - // create temp. test image (red) - $img = $this->manager()->canvas(16, 16, '#ff0000'); - $img->save($path); - $img->destroy(); - - // open test image again - $img = $this->manager()->make($path); - $this->assertColorAtPosition('#ff0000', $img, 0, 0); - - // fill with green and save wthout paramater - $img->fill('#00ff00'); - $img->save(); - $img->destroy(); - - // re-open test image (should be green) - $img = $this->manager()->make($path); - $this->assertColorAtPosition('#00ff00', $img, 0, 0); - $img->destroy(); - - @unlink($path); - } - - /** - * @expectedException ImagickException - */ - public function testDestroy() - { - $img = $this->manager()->make('tests/images/trim.png'); - $img->destroy(); - $img->getCore()->getImageWidth(); // try to get width (should throw exception) - } - - /** - * @expectedException Exception - */ - public function testDestroyWithBackup() - { - $img = $this->manager()->make('tests/images/trim.png'); - $img->backup(); - $img->destroy(); - $img->getBackup()->getImageWidth(); // try to get width (should throw exception) - } - - public function testStringConversion() - { - $img = $this->manager()->make('tests/images/trim.png'); - $value = strval($img); - $this->assertInternalType('string', $value); - } - - public function testFilter() - { - $img = $this->manager()->make('tests/images/trim.png'); - $img->filter(new \Intervention\Image\Filters\DemoFilter(10)); - $this->assertInstanceOf('Intervention\Image\Image', $img); - } - - public function testCloneImageObject() - { - $img = $this->manager()->make('tests/images/trim.png'); - $cln = clone $img; - - // destroy original - $img->destroy(); - unset($img); - - // clone should be still intact - $this->assertInstanceOf('Intervention\Image\Image', $cln); - $this->assertInstanceOf('Imagick', $cln->getCore()); - } - - public function testGifConversionKeepsTransparency() - { - $save_as = 'tests/tmp/foo.gif'; - - // create gif image from transparent png - $img = $this->manager()->make('tests/images/star.png'); - $img->save($save_as); - - // new gif image should be transparent - $img = $this->manager()->make($save_as); - $this->assertTransparentPosition($img, 0, 0); - @unlink($save_as); - } - - private function assertColorAtPosition($color, $img, $x, $y) - { - $pick = $img->pickColor($x, $y, 'hex'); - $this->assertEquals($color, $pick); - $this->assertInstanceOf('Intervention\Image\Image', $img); - } - - private function assertTransparentPosition($img, $x, $y, $transparent = 0) - { - // background should be transparent - $color = $img->pickColor($x, $y, 'array'); - $this->assertEquals($transparent, $color[3]); // alpha channel - } - - private function manager() - { - return new \Intervention\Image\ImageManager([ - 'driver' => 'imagick' - ]); - } -} diff --git a/tests/InsertCommandTest.php b/tests/InsertCommandTest.php deleted file mode 100644 index fa0b521c..00000000 --- a/tests/InsertCommandTest.php +++ /dev/null @@ -1,67 +0,0 @@ -shouldReceive('align')->with('center', 10, 20)->once()->andReturn($image_size); - $watermark_size = Mockery::mock('\Intervention\Image\Size', [800, 600]); - $watermark_size->shouldReceive('align')->with('center')->once()->andReturn($watermark_size); - $image_size->shouldReceive('relativePosition')->with($watermark_size)->once()->andReturn($position); - - $path = __DIR__.'/images/test.jpg'; - $resource = imagecreatefromjpeg($path); - $watermark = Mockery::mock('Intervention\Image\Image'); - $driver = Mockery::mock('Intervention\Image\Gd\Driver'); - $driver->shouldReceive('init')->with($path)->once()->andReturn($watermark); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getDriver')->once()->andReturn($driver); - $image->shouldReceive('getCore')->times(2)->andReturn($resource); - $image->shouldReceive('getSize')->once()->andReturn($image_size); - $watermark->shouldReceive('getSize')->once()->andReturn($watermark_size); - $watermark->shouldReceive('getCore')->once()->andReturn($resource); - - $command = new InsertGd([$path, 'center', 10, 20]); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testImagick() - { - $position = Mockery::mock('\Intervention\Image\Point', [10, 20]); - - $image_size = Mockery::mock('\Intervention\Image\Size', [800, 600]); - $image_size->shouldReceive('align')->with('center', 10, 20)->once()->andReturn($image_size); - $watermark_size = Mockery::mock('\Intervention\Image\Size', [800, 600]); - $watermark_size->shouldReceive('align')->with('center')->once()->andReturn($watermark_size); - $image_size->shouldReceive('relativePosition')->with($watermark_size)->once()->andReturn($position); - - $path = __DIR__.'/images/test.jpg'; - $watermark = Mockery::mock('Intervention\Image\Image'); - $driver = Mockery::mock('Intervention\Image\Imagick\Driver'); - $driver->shouldReceive('init')->with($path)->once()->andReturn($watermark); - $imagick = Mockery::mock('Imagick'); - $imagick->shouldReceive('compositeimage')->with($imagick, \Imagick::COMPOSITE_DEFAULT, 10, 20)->andReturn(true); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($imagick); - $image->shouldReceive('getDriver')->once()->andReturn($driver); - $image->shouldReceive('getSize')->once()->andReturn($image_size); - $watermark->shouldReceive('getSize')->once()->andReturn($watermark_size); - $watermark->shouldReceive('getCore')->once()->andReturn($imagick); - $command = new InsertImagick([$path, 'center', 10, 20]); - $result = $command->execute($image); - $this->assertTrue($result); - } -} diff --git a/tests/InterlaceCommandTest.php b/tests/InterlaceCommandTest.php deleted file mode 100644 index 907bc2cb..00000000 --- a/tests/InterlaceCommandTest.php +++ /dev/null @@ -1,34 +0,0 @@ -shouldReceive('getCore')->once()->andReturn($resource); - $command = new InterlaceGd([true]); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testImagick() - { - $imagick = Mockery::mock('Imagick'); - $imagick->shouldReceive('setinterlacescheme')->with(\Imagick::INTERLACE_LINE)->andReturn(true); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($imagick); - $command = new InterlaceImagick([true]); - $result = $command->execute($image); - $this->assertTrue($result); - } -} diff --git a/tests/InvertCommandTest.php b/tests/InvertCommandTest.php deleted file mode 100644 index 3cad4dda..00000000 --- a/tests/InvertCommandTest.php +++ /dev/null @@ -1,34 +0,0 @@ -shouldReceive('getCore')->once()->andReturn($resource); - $command = new InvertGd([]); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testImagick() - { - $imagick = Mockery::mock('Imagick'); - $imagick->shouldReceive('negateimage')->with(false)->andReturn(true); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($imagick); - $command = new InvertImagick([]); - $result = $command->execute($image); - $this->assertTrue($result); - } -} diff --git a/tests/IptcCommandTest.php b/tests/IptcCommandTest.php deleted file mode 100644 index 27c1ef34..00000000 --- a/tests/IptcCommandTest.php +++ /dev/null @@ -1,72 +0,0 @@ -dirname = __DIR__.'/images'; - $image->basename = 'iptc.jpg'; - $command = new IptcCommand([]); - $result = $command->execute($image); - $this->assertTrue($result); - $this->assertTrue($command->hasOutput()); - $this->assertInternalType('array', $command->getOutput()); - } - - public function testFetchDefined() - { - $image = Mockery::mock('Intervention\Image\Image'); - $image->dirname = __DIR__.'/images'; - $image->basename = 'exif.jpg'; - $command = new IptcCommand(['AuthorByline']); - $result = $command->execute($image); - $this->assertTrue($result); - $this->assertTrue($command->hasOutput()); - $this->assertEquals('Oliver Vogel', $command->getOutput()); - } - - - public function testFetchNonExisting() - { - $image = Mockery::mock('Intervention\Image\Image'); - $image->dirname = __DIR__.'/images'; - $image->basename = 'exif.jpg'; - $command = new IptcCommand(['xxx']); - $result = $command->execute($image); - $this->assertTrue($result); - $this->assertTrue($command->hasOutput()); - $this->assertEquals(null, $command->getOutput()); - } - - - public function testFetchFromPng() - { - $image = Mockery::mock('Intervention\Image\Image'); - $image->dirname = __DIR__.'/images'; - $image->basename = 'star.png'; - $command = new IptcCommand(['Orientation']); - $result = $command->execute($image); - $this->assertTrue($result); - $this->assertTrue($command->hasOutput()); - $this->assertEquals(null, $command->getOutput()); - } - - public function testReturnNullOnIptcReadFail() - { - $image = Mockery::mock('Intervention\Image\Image'); - $command = new IptcCommand(['Orientation']); - $result = $command->execute($image); - $this->assertTrue($result); - $this->assertTrue($command->hasOutput()); - $this->assertEquals(null, $command->getOutput()); - } -} diff --git a/tests/LimitColorsCommandTest.php b/tests/LimitColorsCommandTest.php deleted file mode 100644 index e76d57a4..00000000 --- a/tests/LimitColorsCommandTest.php +++ /dev/null @@ -1,43 +0,0 @@ -shouldReceive('getCore')->once()->andReturn($resource); - $image->shouldReceive('setCore')->once(); - $image->shouldReceive('getSize')->once()->andReturn($size); - $command = new LimitColorsGd([16]); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testImagick() - { - $size = Mockery::mock('\Intervention\Image\Size', [32, 32]); - $imagick = Mockery::mock('\Imagick'); - $imagick->shouldReceive('separateimagechannel')->with(\Imagick::CHANNEL_ALPHA)->times(2); - $imagick->shouldReceive('transparentpaintimage')->with('#ffffff', 0, 0, false)->once(); - $imagick->shouldReceive('negateimage')->with(false)->once(); - $imagick->shouldReceive('quantizeimage')->with(16, \Imagick::COLORSPACE_RGB, 0, false, false)->once(); - $imagick->shouldReceive('compositeimage')->once(); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getSize')->once()->andReturn($size); - $image->shouldReceive('getCore')->times(3)->andReturn($imagick); - $command = new LimitColorsImagick([16]); - $result = $command->execute($image); - $this->assertTrue($result); - } -} diff --git a/tests/LineCommandTest.php b/tests/LineCommandTest.php deleted file mode 100644 index 2dadb2a0..00000000 --- a/tests/LineCommandTest.php +++ /dev/null @@ -1,43 +0,0 @@ -shouldReceive('getDriverName')->once()->andReturn('Gd'); - $image = Mockery::mock('\Intervention\Image\Image'); - $image->shouldReceive('getDriver')->once()->andReturn($driver); - $image->shouldReceive('getCore')->once()->andReturn($resource); - $command = new LineCommand([10, 15, 100, 150]); - $result = $command->execute($image); - $this->assertTrue($result); - $this->assertFalse($command->hasOutput()); - } - - public function testImagick() - { - $imagick = Mockery::mock('\Imagick'); - $imagick->shouldReceive('drawimage'); - $driver = Mockery::mock('\Intervention\Image\Imagick\Driver'); - $driver->shouldReceive('getDriverName')->once()->andReturn('Imagick'); - $image = Mockery::mock('\Intervention\Image\Image'); - $image->shouldReceive('getDriver')->once()->andReturn($driver); - $image->shouldReceive('getCore')->once()->andReturn($imagick); - - $command = new LineCommand([10, 15, 100, 150]); - $result = $command->execute($image); - $this->assertTrue($result); - $this->assertFalse($command->hasOutput()); - } - -} diff --git a/tests/LineShapeTest.php b/tests/LineShapeTest.php deleted file mode 100644 index b95f71bf..00000000 --- a/tests/LineShapeTest.php +++ /dev/null @@ -1,50 +0,0 @@ -assertInstanceOf('Intervention\Image\Gd\Shapes\LineShape', $line); - $this->assertEquals(10, $line->x); - $this->assertEquals(15, $line->y); - - // imagick - $line = new LineImagick(10, 15); - $this->assertInstanceOf('Intervention\Image\Imagick\Shapes\LineShape', $line); - $this->assertEquals(10, $line->x); - $this->assertEquals(15, $line->y); - } - - public function testApplyToImage() - { - // gd - $core = imagecreatetruecolor(300, 200); - $image = Mockery::mock('\Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($core); - $line = new LineGd(10, 15); - $result = $line->applyToImage($image, 100, 200); - $this->assertInstanceOf('Intervention\Image\Gd\Shapes\LineShape', $line); - $this->assertTrue($result); - - // imagick - $core = Mockery::mock('\Imagick'); - $core->shouldReceive('drawimage')->once(); - $image = Mockery::mock('\Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($core); - $line = new LineImagick(10, 15); - $result = $line->applyToImage($image, 100, 200); - $this->assertInstanceOf('Intervention\Image\Imagick\Shapes\LineShape', $line); - $this->assertTrue($result); - } -} diff --git a/tests/MaskCommandTest.php b/tests/MaskCommandTest.php deleted file mode 100644 index 6330670a..00000000 --- a/tests/MaskCommandTest.php +++ /dev/null @@ -1,68 +0,0 @@ -shouldReceive('getSize')->once()->andReturn($mask_size); - $mask_image->shouldReceive('pickColor')->andReturn([0,0,0,0]); - - $canvas_image = Mockery::mock('Intervention\Image\Image'); - $canvas_core = imagecreatetruecolor(32, 32); - $canvas_image->shouldReceive('getCore')->times(2)->andReturn($canvas_core); - $canvas_image->shouldReceive('pixel'); - - $driver = Mockery::mock('Intervention\Image\Gd\Driver'); - $driver->shouldReceive('newImage')->with(32, 32, [0,0,0,0])->once()->andReturn($canvas_image); - $driver->shouldReceive('init')->with($mask_path)->once()->andReturn($mask_image); - - $image_size = Mockery::mock('Intervention\Image\Size', [32, 32]); - $image_core = imagecreatefrompng(__DIR__.'/images/trim.png'); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getSize')->once()->andReturn($image_size); - $image->shouldReceive('getDriver')->times(2)->andReturn($driver); - $image->shouldReceive('pickColor')->andReturn([0,0,0,0]); - $image->shouldReceive('setCore')->with($canvas_core)->once(); - - $command = new MaskGd([$mask_path, true]); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testImagick() - { - $mask_core = Mockery::mock('Imagick'); - $mask_path = __DIR__.'images/star.png'; - $mask_image = Mockery::mock('Intervention\Image\Image'); - $mask_image->shouldReceive('getCore')->once()->andReturn($mask_core); - $mask_size = Mockery::mock('Intervention\Image\Size', [32, 32]); - $mask_image->shouldReceive('getSize')->once()->andReturn($mask_size); - - $driver = Mockery::mock('Intervention\Image\Imagick\Driver'); - $driver->shouldReceive('init')->with($mask_path)->once()->andReturn($mask_image); - $imagick = Mockery::mock('Imagick'); - $imagick->shouldReceive('setimagematte')->with(true)->once(); - $imagick->shouldReceive('compositeimage')->with($mask_core, \Imagick::COMPOSITE_DSTIN, 0, 0)->once(); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($imagick); - $image_size = Mockery::mock('Intervention\Image\Size', [32, 32]); - $image->shouldReceive('getSize')->once()->andReturn($image_size); - $image->shouldReceive('getDriver')->once()->andReturn($driver); - - $command = new MaskImagick([$mask_path, true]); - $result = $command->execute($image); - $this->assertTrue($result); - } -} diff --git a/tests/OpacityCommandTest.php b/tests/OpacityCommandTest.php deleted file mode 100644 index aac04971..00000000 --- a/tests/OpacityCommandTest.php +++ /dev/null @@ -1,44 +0,0 @@ -shouldReceive('getCore')->once()->andReturn($mask_core); - - $resource = imagecreatefrompng(__DIR__.'/images/trim.png'); - $driver = Mockery::mock('\Intervention\Image\Gd\Driver'); - $driver->shouldReceive('newImage')->with(32, 32, 'rgba(0, 0, 0, 0.5)')->andReturn($mask); - - $size = Mockery::mock('\Intervention\Image\Size', [32, 32]); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getDriver')->once()->andReturn($driver); - $image->shouldReceive('getSize')->once()->andReturn($size); - $image->shouldReceive('mask')->with($mask_core, true)->once(); - $command = new OpacityGd([50]); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testImagick() - { - $imagick = Mockery::mock('Imagick'); - $imagick->shouldReceive('evaluateimage')->with(\Imagick::EVALUATE_DIVIDE, 2, \Imagick::CHANNEL_ALPHA)->andReturn(true); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($imagick); - $command = new OpacityImagick([50]); - $result = $command->execute($image); - $this->assertTrue($result); - } -} diff --git a/tests/OrientateCommandTest.php b/tests/OrientateCommandTest.php deleted file mode 100644 index 884fcbb1..00000000 --- a/tests/OrientateCommandTest.php +++ /dev/null @@ -1,94 +0,0 @@ -shouldReceive('exif')->with('Orientation')->once()->andReturn(2); - $image->shouldReceive('flip')->once(); - $command = new OrientateCommand([]); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testExecuteOrientationThree() - { - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('exif')->with('Orientation')->once()->andReturn(3); - $image->shouldReceive('rotate')->with(180)->once(); - $command = new OrientateCommand([]); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testExecuteOrientationFour() - { - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('exif')->with('Orientation')->once()->andReturn(4); - $image->shouldReceive('rotate')->with(180)->once()->andReturn($image); - $image->shouldReceive('flip')->once(); - $command = new OrientateCommand([]); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testExecuteOrientationFive() - { - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('exif')->with('Orientation')->once()->andReturn(5); - $image->shouldReceive('rotate')->with(270)->once()->andReturn($image); - $image->shouldReceive('flip')->once(); - $command = new OrientateCommand([]); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testExecuteOrientationSix() - { - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('exif')->with('Orientation')->once()->andReturn(6); - $image->shouldReceive('rotate')->with(270)->once(); - $command = new OrientateCommand([]); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testExecuteOrientationSeven() - { - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('exif')->with('Orientation')->once()->andReturn(7); - $image->shouldReceive('rotate')->with(90)->once()->andReturn($image); - $image->shouldReceive('flip')->once(); - $command = new OrientateCommand([]); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testExecuteOrientationEight() - { - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('exif')->with('Orientation')->once()->andReturn(8); - $image->shouldReceive('rotate')->with(90)->once(); - $command = new OrientateCommand([]); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testExecuteOrientationNoExifData() - { - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('exif')->with('Orientation')->once()->andReturn(null); - $command = new OrientateCommand([]); - $result = $command->execute($image); - $this->assertTrue($result); - } -} diff --git a/tests/PickColorCommandTest.php b/tests/PickColorCommandTest.php deleted file mode 100644 index fecc676f..00000000 --- a/tests/PickColorCommandTest.php +++ /dev/null @@ -1,67 +0,0 @@ -shouldReceive('getCore')->times(2)->andReturn($resource); - $command = new PickColorGd([1, 2]); - $result = $command->execute($image); - $this->assertTrue($result); - $this->assertTrue($command->hasOutput()); - $this->assertInternalType('array', $command->getOutput()); - $this->assertEquals(4, count($command->getOutput())); - } - - public function testGdWithFormat() - { - $resource = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getCore')->times(2)->andReturn($resource); - $command = new PickColorGd([1, 2, 'hex']); - $result = $command->execute($image); - $this->assertTrue($result); - $this->assertTrue($command->hasOutput()); - $this->assertInternalType('string', $command->getOutput()); - $this->assertEquals('#ffffff', $command->getOutput()); - } - - public function testImagickWithCoordinates() - { - $imagick = Mockery::mock('Imagick'); - $imagick->shouldReceive('getimagepixelcolor')->with(1, 2)->andReturn(new ImagickPixel); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($imagick); - $command = new PickColorImagick([1, 2]); - $result = $command->execute($image); - $this->assertTrue($result); - $this->assertTrue($command->hasOutput()); - $this->assertInternalType('array', $command->getOutput()); - $this->assertEquals(4, count($command->getOutput())); - } - - public function testImagickWithFormat() - { - $imagick = Mockery::mock('Imagick'); - $imagick->shouldReceive('getimagepixelcolor')->with(1, 2)->andReturn(new ImagickPixel('#ff0000')); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($imagick); - $command = new PickColorImagick([1, 2, 'hex']); - $result = $command->execute($image); - $this->assertTrue($result); - $this->assertTrue($command->hasOutput()); - $this->assertInternalType('string', $command->getOutput()); - $this->assertEquals('#ff0000', $command->getOutput()); - } -} diff --git a/tests/PixelCommandTest.php b/tests/PixelCommandTest.php deleted file mode 100644 index ad1681d7..00000000 --- a/tests/PixelCommandTest.php +++ /dev/null @@ -1,34 +0,0 @@ -shouldReceive('getCore')->once()->andReturn($resource); - $command = new PixelGd(['#b53717', 10, 20]); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testImagick() - { - $imagick = Mockery::mock('Imagick'); - $imagick->shouldReceive('drawimage')->once()->andReturn(true); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($imagick); - $command = new PixelImagick(['#b53717', 10, 20]); - $result = $command->execute($image); - $this->assertTrue($result); - } -} diff --git a/tests/PixelateCommandTest.php b/tests/PixelateCommandTest.php deleted file mode 100644 index 29eba75f..00000000 --- a/tests/PixelateCommandTest.php +++ /dev/null @@ -1,37 +0,0 @@ -shouldReceive('getCore')->once()->andReturn($resource); - $command = new PixelateGd([10]); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testImagick() - { - $imagick = Mockery::mock('Imagick'); - $imagick->shouldReceive('scaleimage')->with(80, 60)->once()->andReturn(true); - $imagick->shouldReceive('scaleimage')->with(800, 600)->once()->andReturn(true); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getCore')->times(2)->andReturn($imagick); - $image->shouldReceive('getWidth')->once()->andReturn(800); - $image->shouldReceive('getHeight')->once()->andReturn(600); - $command = new PixelateImagick([10]); - $result = $command->execute($image); - $this->assertTrue($result); - } -} diff --git a/tests/PointTest.php b/tests/PointTest.php deleted file mode 100644 index 816c1b13..00000000 --- a/tests/PointTest.php +++ /dev/null @@ -1,47 +0,0 @@ -assertInstanceOf('Intervention\Image\Point', $point); - $this->assertEquals(0, $point->x); - $this->assertEquals(0, $point->y); - } - - public function testConstructorWithParameters() - { - $point = new Point(40, 50); - $this->assertInstanceOf('Intervention\Image\Point', $point); - $this->assertEquals(40, $point->x); - $this->assertEquals(50, $point->y); - } - - public function testSetX() - { - $point = new Point(0, 0); - $point->setX(100); - $this->assertEquals(100, $point->x); - $this->assertEquals(0, $point->y); - } - - public function testSetY() - { - $point = new Point(0, 0); - $point->setY(100); - $this->assertEquals(0, $point->x); - $this->assertEquals(100, $point->y); - } - - public function testSetPosition() - { - $point = new Point(0, 0); - $point->setPosition(100, 200); - $this->assertEquals(100, $point->x); - $this->assertEquals(200, $point->y); - } -} diff --git a/tests/PolygonCommandTest.php b/tests/PolygonCommandTest.php deleted file mode 100644 index 1580d8bc..00000000 --- a/tests/PolygonCommandTest.php +++ /dev/null @@ -1,45 +0,0 @@ -shouldReceive('getDriverName')->once()->andReturn('Gd'); - $image = Mockery::mock('\Intervention\Image\Image'); - $image->shouldReceive('getDriver')->once()->andReturn($driver); - $image->shouldReceive('getCore')->once()->andReturn($resource); - $command = new PolygonCommand([$points]); - $result = $command->execute($image); - $this->assertTrue($result); - $this->assertFalse($command->hasOutput()); - } - - public function testImagick() - { - $points = [1, 2, 3, 4, 5, 6]; - $imagick = Mockery::mock('\Imagick'); - $imagick->shouldReceive('drawimage'); - $driver = Mockery::mock('\Intervention\Image\Imagick\Driver'); - $driver->shouldReceive('getDriverName')->once()->andReturn('Imagick'); - $image = Mockery::mock('\Intervention\Image\Image'); - $image->shouldReceive('getDriver')->once()->andReturn($driver); - $image->shouldReceive('getCore')->once()->andReturn($imagick); - - $command = new PolygonCommand([$points]); - $result = $command->execute($image); - $this->assertTrue($result); - $this->assertFalse($command->hasOutput()); - } - -} diff --git a/tests/PolygonShapeTest.php b/tests/PolygonShapeTest.php deleted file mode 100644 index be0001c4..00000000 --- a/tests/PolygonShapeTest.php +++ /dev/null @@ -1,57 +0,0 @@ -assertInstanceOf('Intervention\Image\Gd\Shapes\PolygonShape', $polygon); - $this->assertEquals([1, 2, 3, 4, 5, 6], $polygon->points); - - } - - public function testGdApplyToImage() - { - $core = imagecreatetruecolor(300, 200); - $image = Mockery::mock('\Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($core); - $polygon = new PolygonGd([1, 2, 3, 4, 5, 6]); - $result = $polygon->applyToImage($image); - $this->assertInstanceOf('Intervention\Image\Gd\Shapes\PolygonShape', $polygon); - $this->assertTrue($result); - } - - public function testImagickConstructor() - { - $polygon = new PolygonImagick([1, 2, 3, 4, 5, 6]); - $this->assertInstanceOf('Intervention\Image\Imagick\Shapes\PolygonShape', $polygon); - $this->assertEquals([ - ['x' => 1, 'y' => 2], - ['x' => 3, 'y' => 4], - ['x' => 5, 'y' => 6]], - $polygon->points); - - } - - public function testImagickApplyToImage() - { - $core = Mockery::mock('\Imagick'); - $core->shouldReceive('drawimage')->once(); - $image = Mockery::mock('\Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($core); - $polygon = new PolygonImagick([1, 2, 3, 4, 5, 6]); - $result = $polygon->applyToImage($image); - $this->assertInstanceOf('Intervention\Image\Imagick\Shapes\PolygonShape', $polygon); - $this->assertTrue($result); - } - -} diff --git a/tests/PsrResponseCommandTest.php b/tests/PsrResponseCommandTest.php deleted file mode 100644 index f7753f04..00000000 --- a/tests/PsrResponseCommandTest.php +++ /dev/null @@ -1,58 +0,0 @@ -'; - - $image = Mockery::mock('Intervention\Image\Image'); - $stream = \GuzzleHttp\Psr7\stream_for($encodedContent); - - $image->shouldReceive('stream') - ->with('jpg', 87) - ->once() - ->andReturn($stream); - - $image->shouldReceive('getEncoded') - ->twice() - ->andReturn($encodedContent); - - $command = new PsrResponseCommand(['jpg', 87]); - $result = $command->execute($image); - - $this->assertTrue($result); - $this->assertTrue($command->hasOutput()); - - $output = $command->getOutput(); - - $this->assertInstanceOf('Psr\Http\Message\ResponseInterface', $output); - - /** - * @var \Psr\Http\Message\ResponseInterface $output - */ - $this->assertEquals($stream, $output->getBody()); - $this->assertEquals($encodedContent, (string)$output->getBody()); - - $this->assertTrue($output->hasHeader('Content-Type')); - $this->assertTrue($output->hasHeader('Content-Length')); - - $this->assertTrue(in_array($output->getHeaderLine('Content-Type'), [ - 'application/xml', - 'text/xml', - ])); - - $this->assertEquals( - strlen($encodedContent), - $output->getHeaderLine('Content-Length') - ); - } -} diff --git a/tests/RectangleCommandTest.php b/tests/RectangleCommandTest.php deleted file mode 100644 index 3def6859..00000000 --- a/tests/RectangleCommandTest.php +++ /dev/null @@ -1,43 +0,0 @@ -shouldReceive('getDriverName')->once()->andReturn('Gd'); - $image = Mockery::mock('\Intervention\Image\Image'); - $image->shouldReceive('getDriver')->once()->andReturn($driver); - $image->shouldReceive('getCore')->once()->andReturn($resource); - $command = new RectangleCommand([10, 15, 100, 150]); - $result = $command->execute($image); - $this->assertTrue($result); - $this->assertFalse($command->hasOutput()); - } - - public function testImagick() - { - $imagick = Mockery::mock('\Imagick'); - $imagick->shouldReceive('drawimage'); - $driver = Mockery::mock('\Intervention\Image\Imagick\Driver'); - $driver->shouldReceive('getDriverName')->once()->andReturn('Imagick'); - $image = Mockery::mock('\Intervention\Image\Image'); - $image->shouldReceive('getDriver')->once()->andReturn($driver); - $image->shouldReceive('getCore')->once()->andReturn($imagick); - - $command = new RectangleCommand([10, 15, 100, 150]); - $result = $command->execute($image); - $this->assertTrue($result); - $this->assertFalse($command->hasOutput()); - } - -} diff --git a/tests/RectangleShapeTest.php b/tests/RectangleShapeTest.php deleted file mode 100644 index d4d7b2a0..00000000 --- a/tests/RectangleShapeTest.php +++ /dev/null @@ -1,54 +0,0 @@ -assertInstanceOf('Intervention\Image\Gd\Shapes\RectangleShape', $rectangle); - $this->assertEquals(10, $rectangle->x1); - $this->assertEquals(15, $rectangle->y1); - $this->assertEquals(100, $rectangle->x2); - $this->assertEquals(150, $rectangle->y2); - - // imagick - $rectangle = new RectangleImagick(10, 15, 100, 150); - $this->assertInstanceOf('Intervention\Image\Imagick\Shapes\RectangleShape', $rectangle); - $this->assertEquals(10, $rectangle->x1); - $this->assertEquals(15, $rectangle->y1); - $this->assertEquals(100, $rectangle->x2); - $this->assertEquals(150, $rectangle->y2); - } - - public function testApplyToImage() - { - // gd - $core = imagecreatetruecolor(300, 200); - $image = Mockery::mock('\Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($core); - $rectangle = new RectangleGd(10, 15, 100, 150); - $result = $rectangle->applyToImage($image, 10, 20); - $this->assertInstanceOf('Intervention\Image\Gd\Shapes\RectangleShape', $rectangle); - $this->assertTrue($result); - - // imagick - $core = Mockery::mock('\Imagick'); - $core->shouldReceive('drawimage')->once(); - $image = Mockery::mock('\Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($core); - $rectangle = new RectangleImagick(10, 15, 100, 150); - $result = $rectangle->applyToImage($image, 10, 20); - $this->assertInstanceOf('Intervention\Image\Imagick\Shapes\RectangleShape', $rectangle); - $this->assertTrue($result); - } -} diff --git a/tests/ResetCommandTest.php b/tests/ResetCommandTest.php deleted file mode 100644 index 29a545a2..00000000 --- a/tests/ResetCommandTest.php +++ /dev/null @@ -1,71 +0,0 @@ -shouldReceive('cloneCore')->with($resource)->once()->andReturn($resource); - $image->shouldReceive('getCore')->once()->andReturn($resource); - $image->shouldReceive('getDriver')->once()->andReturn($driver); - $image->shouldReceive('setCore')->once(); - $image->shouldReceive('getBackup')->once()->andReturn($resource); - $command = new ResetGd([]); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testGdWithName() - { - $size = Mockery::mock('Intervention\Image\Size', [800, 600]); - $resource = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); - $image = Mockery::mock('Intervention\Image\Image'); - $driver = Mockery::mock('Intervention\Image\Gd\Driver'); - $driver->shouldReceive('cloneCore')->with($resource)->once()->andReturn($resource); - $image->shouldReceive('getDriver')->once()->andReturn($driver); - $image->shouldReceive('getCore')->once()->andReturn($resource); - $image->shouldReceive('setCore')->once(); - $image->shouldReceive('getBackup')->once()->withArgs(['fooBackup'])->andReturn($resource); - $command = new ResetGd(['fooBackup']); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testImagickWithoutName() - { - $imagick = Mockery::mock('Imagick'); - $imagick->shouldReceive('clear')->once(); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($imagick); - $image->shouldReceive('setCore')->once(); - $image->shouldReceive('getBackup')->once()->andReturn($imagick); - $command = new ResetImagick([]); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testImagickWithName() - { - $imagick = Mockery::mock('Imagick'); - $imagick->shouldReceive('clear')->once(); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($imagick); - $image->shouldReceive('setCore')->once(); - $image->shouldReceive('getBackup')->once()->withArgs(['fooBackup'])->andReturn($imagick); - $command = new ResetImagick(['fooBackup']); - $result = $command->execute($image); - $this->assertTrue($result); - } -} diff --git a/tests/ResizeCanvasCommandTest.php b/tests/ResizeCanvasCommandTest.php deleted file mode 100644 index fb6480a1..00000000 --- a/tests/ResizeCanvasCommandTest.php +++ /dev/null @@ -1,80 +0,0 @@ -shouldReceive('align')->with('center')->andReturn($canvas_size); - $canvas_size->shouldReceive('relativePosition')->andReturn($canvas_pos); - $image_pos = Mockery::mock('\Intervention\Image\Point', [0, 0]); - $image_size = Mockery::mock('\Intervention\Image\Size', [800, 600]); - $image_size->shouldReceive('align')->with('center')->andReturn($image_size); - $image_size->shouldReceive('relativePosition')->andReturn($image_pos); - $canvas = Mockery::mock('\Intervention\Image\Image'); - $canvas->shouldReceive('getCore')->times(5)->andReturn($resource); - $canvas->shouldReceive('getSize')->andReturn($canvas_size); - $driver = Mockery::mock('\Intervention\Image\Gd\Driver'); - $driver->shouldReceive('newImage')->with(820, 640, '#b53717')->once()->andReturn($canvas); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getDriver')->once()->andReturn($driver); - $image->shouldReceive('getSize')->once()->andReturn($image_size); - $image->shouldReceive('getWidth')->once()->andReturn(800); - $image->shouldReceive('getHeight')->once()->andReturn(600); - $image->shouldReceive('getCore')->once()->andReturn($resource); - $image->shouldReceive('setCore')->once(); - $command = new ResizeCanvasGd([20, 40, 'center', true, '#b53717']); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testImagick() - { - $canvas_pos = Mockery::mock('\Intervention\Image\Point', [0, 0]); - $canvas_size = Mockery::mock('\Intervention\Image\Size', [820, 640]); - $canvas_size->shouldReceive('align')->with('center')->andReturn($canvas_size); - $canvas_size->shouldReceive('relativePosition')->andReturn($canvas_pos); - $image_pos = Mockery::mock('\Intervention\Image\Point', [0, 0]); - $image_size = Mockery::mock('\Intervention\Image\Size', [800, 600]); - $image_size->shouldReceive('align')->with('center')->andReturn($image_size); - $image_size->shouldReceive('relativePosition')->andReturn($image_pos); - $canvas = Mockery::mock('\Intervention\Image\Image'); - - $imagick = Mockery::mock('Imagick'); - $imagick->shouldReceive('cropimage')->with(800, 600, 0, 0)->once(); - $imagick->shouldReceive('compositeimage')->with($imagick, 40, 0, 0)->once(); - $imagick->shouldReceive('setimagepage')->with(0, 0, 0, 0)->once(); - $imagick->shouldReceive('drawimage')->once(); - $imagick->shouldReceive('transparentpaintimage')->once(); - $imagick->shouldReceive('getimagecolorspace')->once(); - $imagick->shouldReceive('setimagecolorspace')->once(); - - $canvas->shouldReceive('getCore')->times(6)->andReturn($imagick); - $canvas->shouldReceive('getSize')->andReturn($canvas_size); - $canvas->shouldReceive('pickColor')->with(0, 0, 'hex')->once()->andReturn('#000000'); - $driver = Mockery::mock('\Intervention\Image\Gd\Driver'); - $driver->shouldReceive('newImage')->with(820, 640, '#b53717')->once()->andReturn($canvas); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getDriver')->once()->andReturn($driver); - $image->shouldReceive('getSize')->once()->andReturn($image_size); - $image->shouldReceive('getWidth')->once()->andReturn(800); - $image->shouldReceive('getHeight')->once()->andReturn(600); - $image->shouldReceive('getCore')->times(3)->andReturn($imagick); - $image->shouldReceive('setCore')->once(); - $command = new ResizeCanvasImagick([20, 40, 'center', true, '#b53717']); - $result = $command->execute($image); - $this->assertTrue($result); - } - -} diff --git a/tests/ResizeCommandTest.php b/tests/ResizeCommandTest.php deleted file mode 100644 index 130ea4b2..00000000 --- a/tests/ResizeCommandTest.php +++ /dev/null @@ -1,49 +0,0 @@ -upsize(); }; - $resource = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); - $image = Mockery::mock('Intervention\Image\Image'); - $size = Mockery::mock('Intervention\Image\Size', [800, 600]); - $size->shouldReceive('resize')->with(300, 200, $callback)->once()->andReturn($size); - $size->shouldReceive('getWidth')->once()->andReturn(800); - $size->shouldReceive('getHeight')->once()->andReturn(600); - $image->shouldReceive('getWidth')->once()->andReturn(800); - $image->shouldReceive('getHeight')->once()->andReturn(600); - $image->shouldReceive('getSize')->once()->andReturn($size); - $image->shouldReceive('getCore')->once()->andReturn($resource); - $image->shouldReceive('setCore')->once(); - $command = new ResizeCommandGd([300, 200, $callback]); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testImagick() - { - $callback = function ($constraint) { $constraint->upsize(); }; - $imagick = Mockery::mock('Imagick'); - $imagick->shouldReceive('scaleimage')->with(300, 200)->once()->andReturn(true); - $size = Mockery::mock('Intervention\Image\Size', [800, 600]); - $size->shouldReceive('resize')->with(300, 200, $callback)->once()->andReturn($size); - $size->shouldReceive('getWidth')->once()->andReturn(300); - $size->shouldReceive('getHeight')->once()->andReturn(200); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($imagick); - $image->shouldReceive('getSize')->once()->andReturn($size); - $command = new ResizeCommandImagick([300, 200, $callback]); - $result = $command->execute($image); - $this->assertTrue($result); - } -} diff --git a/tests/ResponseTest.php b/tests/ResponseTest.php deleted file mode 100644 index 6a10b2c6..00000000 --- a/tests/ResponseTest.php +++ /dev/null @@ -1,30 +0,0 @@ -assertInstanceOf('\Intervention\Image\Response', $response); - $this->assertInstanceOf('\Intervention\Image\Image', $response->image); - } - - public function testConstructorWithParameters() - { - $image = Mockery::mock('\Intervention\Image\Image'); - $response = new Response($image, 'jpg', 75); - $this->assertInstanceOf('\Intervention\Image\Response', $response); - $this->assertInstanceOf('\Intervention\Image\Image', $response->image); - $this->assertEquals('jpg', $response->format); - $this->assertEquals(75, $response->quality); - } -} diff --git a/tests/RotateCommandTest.php b/tests/RotateCommandTest.php deleted file mode 100644 index 48facd16..00000000 --- a/tests/RotateCommandTest.php +++ /dev/null @@ -1,48 +0,0 @@ -shouldReceive('getCore')->once()->andReturn($resource); - $image->shouldReceive('setCore')->once()->andReturn($resource); - $command = new RotateGd([45, '#b53717']); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testImagick() - { - $pixel = Mockery::mock('ImagickPixel', ['#b53717']); - $imagick = Mockery::mock('Imagick'); - $imagick->shouldReceive('rotateimage')->andReturn(true); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($imagick); - $command = new RotateImagick([45, '#b53717']); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testImagickWithLargeRotation() - { - $rotation = 45; - $imagick = Mockery::mock('Imagick'); - $imagick->shouldReceive('rotateimage')->with(Mockery::type('object'), -$rotation)->andReturn(true); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($imagick); - $command = new RotateImagick([$rotation + (360 * 1000), '#b53717']); - $result = $command->execute($image); - $this->assertTrue($result); - } -} diff --git a/tests/SharpenCommandTest.php b/tests/SharpenCommandTest.php deleted file mode 100644 index ed187d87..00000000 --- a/tests/SharpenCommandTest.php +++ /dev/null @@ -1,34 +0,0 @@ -shouldReceive('getCore')->once()->andReturn($resource); - $command = new SharpenGd([50]); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testImagick() - { - $imagick = Mockery::mock('Imagick'); - $imagick->shouldReceive('unsharpmaskimage')->with(1, 1, 8, 0)->andReturn(true); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($imagick); - $command = new SharpenImagick([50]); - $result = $command->execute($image); - $this->assertTrue($result); - } -} diff --git a/tests/SizeTest.php b/tests/SizeTest.php deleted file mode 100644 index 35ced232..00000000 --- a/tests/SizeTest.php +++ /dev/null @@ -1,436 +0,0 @@ -assertInstanceOf('Intervention\Image\Size', $size); - $this->assertInstanceOf('Intervention\Image\Point', $size->pivot); - $this->assertEquals(1, $size->width); - $this->assertEquals(1, $size->height); - } - - public function testConstructorWithCoordinates() - { - $pivot = Mockery::mock('Intervention\Image\Point'); - $size = new Size(300, 200, $pivot); - $this->assertInstanceOf('Intervention\Image\Size', $size); - $this->assertInstanceOf('Intervention\Image\Point', $size->pivot); - $this->assertEquals(300, $size->width); - $this->assertEquals(200, $size->height); - } - - public function testGetWidth() - { - $size = new Size(800, 600); - $this->assertEquals(800, $size->getWidth()); - } - - public function testGetHeight() - { - $size = new Size(800, 600); - $this->assertEquals(600, $size->getHeight()); - } - - public function testGetRatio() - { - $size = new Size(800, 600); - $this->assertEquals(1.33333333333, $size->getRatio()); - - $size = new Size(100, 100); - $this->assertEquals(1, $size->getRatio()); - - $size = new Size(1920, 1080); - $this->assertEquals(1.777777777778, $size->getRatio()); - } - - public function testResize() - { - $size = new Size(800, 600); - $size->resize(1000, 2000); - $this->assertEquals(1000, $size->width); - $this->assertEquals(2000, $size->height); - - $size = new Size(800, 600); - $size->resize(2000, null); - $this->assertEquals(2000, $size->width); - $this->assertEquals(600, $size->height); - - $size = new Size(800, 600); - $size->resize(null, 1000); - $this->assertEquals(800, $size->width); - $this->assertEquals(1000, $size->height); - } - - public function testResizeWithCallbackAspectRatio() - { - $size = new Size(800, 600); - $size->resize(1000, 2000, function ($c) { $c->aspectRatio(); }); - $this->assertEquals(1000, $size->width); - $this->assertEquals(750, $size->height); - - $size = new Size(800, 600); - $size->resize(2000, 1000, function ($c) { $c->aspectRatio(); }); - $this->assertEquals(1333, $size->width); - $this->assertEquals(1000, $size->height); - - $size = new Size(800, 600); - $size->resize(null, 3000, function ($c) { $c->aspectRatio(); }); - $this->assertEquals(4000, $size->width); - $this->assertEquals(3000, $size->height); - - $size = new Size(800, 600); - $size->resize(8000, null, function ($c) { $c->aspectRatio(); }); - $this->assertEquals(8000, $size->width); - $this->assertEquals(6000, $size->height); - - $size = new Size(800, 600); - $size->resize(100, 400, function ($c) { $c->aspectRatio(); }); - $this->assertEquals(100, $size->width); - $this->assertEquals(75, $size->height); - - $size = new Size(800, 600); - $size->resize(400, 100, function ($c) { $c->aspectRatio(); }); - $this->assertEquals(133, $size->width); - $this->assertEquals(100, $size->height); - - $size = new Size(800, 600); - $size->resize(null, 300, function ($c) { $c->aspectRatio(); }); - $this->assertEquals(400, $size->width); - $this->assertEquals(300, $size->height); - - $size = new Size(800, 600); - $size->resize(80, null, function ($c) { $c->aspectRatio(); }); - $this->assertEquals(80, $size->width); - $this->assertEquals(60, $size->height); - - $size = new Size(640, 480); - $size->resize(225, null, function ($c) { $c->aspectRatio(); }); - $this->assertEquals(225, $size->width); - $this->assertEquals(169, $size->height); - - $size = new Size(640, 480); - $size->resize(223, null, function ($c) { $c->aspectRatio(); }); - $this->assertEquals(223, $size->width); - $this->assertEquals(167, $size->height); - - $size = new Size(600, 800); - $size->resize(300, 300, function ($c) { $c->aspectRatio(); }); - $this->assertEquals(225, $size->width); - $this->assertEquals(300, $size->height); - - $size = new Size(800, 600); - $size->resize(400, 10, function ($c) { $c->aspectRatio(); }); - $this->assertEquals(13, $size->width); - $this->assertEquals(10, $size->height); - - $size = new Size(800, 600); - $size->resize(1000, 1200, function ($c) { $c->aspectRatio(); }); - $this->assertEquals(1000, $size->width); - $this->assertEquals(750, $size->height); - } - - public function testResizeWithCallbackUpsize() - { - $size = new Size(800, 600); - $size->resize(1000, 2000, function ($c) { $c->upsize(); }); - $this->assertEquals(800, $size->width); - $this->assertEquals(600, $size->height); - - $size = new Size(800, 600); - $size->resize(400, 1000, function ($c) { $c->upsize(); }); - $this->assertEquals(400, $size->width); - $this->assertEquals(600, $size->height); - - $size = new Size(800, 600); - $size->resize(1000, 400, function ($c) { $c->upsize(); }); - $this->assertEquals(800, $size->width); - $this->assertEquals(400, $size->height); - - $size = new Size(800, 600); - $size->resize(400, 300, function ($c) { $c->upsize(); }); - $this->assertEquals(400, $size->width); - $this->assertEquals(300, $size->height); - - $size = new Size(800, 600); - $size->resize(1000, null, function ($c) { $c->upsize(); }); - $this->assertEquals(800, $size->width); - $this->assertEquals(600, $size->height); - - $size = new Size(800, 600); - $size->resize(null, 1000, function ($c) { $c->upsize(); }); - $this->assertEquals(800, $size->width); - $this->assertEquals(600, $size->height); - } - - public function testResizeWithCallbackAspectRatioAndUpsize() - { - $size = new Size(800, 600); - $size->resize(1000, 2000, function ($c) { $c->aspectRatio(); $c->upsize(); }); - $this->assertEquals(800, $size->width); - $this->assertEquals(600, $size->height); - - $size = new Size(800, 600); - $size->resize(1000, 600, function ($c) { $c->aspectRatio(); $c->upsize(); }); - $this->assertEquals(800, $size->width); - $this->assertEquals(600, $size->height); - - $size = new Size(800, 600); - $size->resize(1000, 300, function ($c) { $c->aspectRatio(); $c->upsize(); }); - $this->assertEquals(400, $size->width); - $this->assertEquals(300, $size->height); - - $size = new Size(800, 600); - $size->resize(400, 1000, function ($c) { $c->aspectRatio(); $c->upsize(); }); - $this->assertEquals(400, $size->width); - $this->assertEquals(300, $size->height); - - $size = new Size(800, 600); - $size->resize(400, null, function ($c) { $c->aspectRatio(); $c->upsize(); }); - $this->assertEquals(400, $size->width); - $this->assertEquals(300, $size->height); - - $size = new Size(800, 600); - $size->resize(null, 300, function ($c) { $c->aspectRatio(); $c->upsize(); }); - $this->assertEquals(400, $size->width); - $this->assertEquals(300, $size->height); - - $size = new Size(800, 600); - $size->resize(1000, null, function ($c) { $c->aspectRatio(); $c->upsize(); }); - $this->assertEquals(800, $size->width); - $this->assertEquals(600, $size->height); - - $size = new Size(800, 600); - $size->resize(null, 1000, function ($c) { $c->aspectRatio(); $c->upsize(); }); - $this->assertEquals(800, $size->width); - $this->assertEquals(600, $size->height); - - $size = new Size(800, 600); - $size->resize(100, 100, function ($c) { $c->aspectRatio(); $c->upsize(); }); - $this->assertEquals(100, $size->width); - $this->assertEquals(75, $size->height); - - $size = new Size(800, 600); - $size->resize(300, 200, function ($c) { $c->aspectRatio(); $c->upsize(); }); - $this->assertEquals(267, $size->width); - $this->assertEquals(200, $size->height); - - $size = new Size(600, 800); - $size->resize(300, 300, function ($c) { $c->aspectRatio(); $c->upsize(); }); - $this->assertEquals(225, $size->width); - $this->assertEquals(300, $size->height); - - $size = new Size(800, 600); - $size->resize(400, 10, function ($c) { $c->aspectRatio(); $c->upsize(); }); - $this->assertEquals(13, $size->width); - $this->assertEquals(10, $size->height); - } - - public function testRelativePosition() - { - $container = new Size(800, 600); - $input = new Size(200, 100); - $container->align('top-left'); - $input->align('top-left'); - $pos = $container->relativePosition($input); - $this->assertEquals(0, $pos->x); - $this->assertEquals(0, $pos->y); - - $container = new Size(800, 600); - $input = new Size(200, 100); - $container->align('center'); - $input->align('top-left'); - $pos = $container->relativePosition($input); - $this->assertEquals(400, $pos->x); - $this->assertEquals(300, $pos->y); - - $container = new Size(800, 600); - $input = new Size(200, 100); - $container->align('bottom-right'); - $input->align('top-right'); - $pos = $container->relativePosition($input); - $this->assertEquals(600, $pos->x); - $this->assertEquals(600, $pos->y); - - $container = new Size(800, 600); - $input = new Size(200, 100); - $container->align('center'); - $input->align('center'); - $pos = $container->relativePosition($input); - $this->assertEquals(300, $pos->x); - $this->assertEquals(250, $pos->y); - } - - public function testAlign() - { - $width = 640; - $height = 480; - $pivot = Mockery::mock('Intervention\Image\Point'); - $pivot->shouldReceive('setPosition')->with(0, 0)->once(); - $pivot->shouldReceive('setPosition')->with(intval($width/2), 0)->once(); - $pivot->shouldReceive('setPosition')->with($width, 0)->once(); - $pivot->shouldReceive('setPosition')->with(0, intval($height/2))->once(); - $pivot->shouldReceive('setPosition')->with(intval($width/2), intval($height/2))->once(); - $pivot->shouldReceive('setPosition')->with($width, intval($height/2))->once(); - $pivot->shouldReceive('setPosition')->with(0, $height)->once(); - $pivot->shouldReceive('setPosition')->with(intval($width/2), $height)->once(); - $pivot->shouldReceive('setPosition')->with($width, $height)->once(); - - $box = new Size($width, $height, $pivot); - $box->align('top-left'); - $box->align('top'); - $box->align('top-right'); - $box->align('left'); - $box->align('center'); - $box->align('right'); - $box->align('bottom-left'); - $box->align('bottom'); - $b = $box->align('bottom-right'); - $this->assertInstanceOf('Intervention\Image\Size', $b); - } - - public function testFit() - { - $box = new Size(800, 600); - $fitted = $box->fit(new Size(100, 100)); - $this->assertEquals(600, $fitted->width); - $this->assertEquals(600, $fitted->height); - $this->assertEquals(100, $fitted->pivot->x); - $this->assertEquals(0, $fitted->pivot->y); - - $box = new Size(800, 600); - $fitted = $box->fit(new Size(200, 100)); - $this->assertEquals(800, $fitted->width); - $this->assertEquals(400, $fitted->height); - $this->assertEquals(0, $fitted->pivot->x); - $this->assertEquals(100, $fitted->pivot->y); - - $box = new Size(800, 600); - $fitted = $box->fit(new Size(100, 200)); - $this->assertEquals(300, $fitted->width); - $this->assertEquals(600, $fitted->height); - $this->assertEquals(250, $fitted->pivot->x); - $this->assertEquals(0, $fitted->pivot->y); - - $box = new Size(800, 600); - $fitted = $box->fit(new Size(2000, 10)); - $this->assertEquals(800, $fitted->width); - $this->assertEquals(4, $fitted->height); - $this->assertEquals(0, $fitted->pivot->x); - $this->assertEquals(298, $fitted->pivot->y); - - $box = new Size(800, 600); - $fitted = $box->fit(new Size(10, 2000)); - $this->assertEquals(3, $fitted->width); - $this->assertEquals(600, $fitted->height); - $this->assertEquals(399, $fitted->pivot->x); - $this->assertEquals(0, $fitted->pivot->y); - - $box = new Size(800, 600); - $fitted = $box->fit(new Size(800, 600)); - $this->assertEquals(800, $fitted->width); - $this->assertEquals(600, $fitted->height); - $this->assertEquals(0, $fitted->pivot->x); - $this->assertEquals(0, $fitted->pivot->y); - - $box = new Size(400, 300); - $fitted = $box->fit(new Size(120, 120)); - $this->assertEquals(300, $fitted->width); - $this->assertEquals(300, $fitted->height); - $this->assertEquals(50, $fitted->pivot->x); - $this->assertEquals(0, $fitted->pivot->y); - - $box = new Size(600, 800); - $fitted = $box->fit(new Size(100, 100)); - $this->assertEquals(600, $fitted->width); - $this->assertEquals(600, $fitted->height); - $this->assertEquals(0, $fitted->pivot->x); - $this->assertEquals(100, $fitted->pivot->y); - } - - /** - * @dataProvider providerFitWithPosition - */ - public function testFitWithPosition(Size $box, $position, $x, $y) - { - $fitted = $box->fit(new Size(100, 100), $position); - $this->assertEquals(600, $fitted->width); - $this->assertEquals(600, $fitted->height); - $this->assertEquals($x, $fitted->pivot->x); - $this->assertEquals($y, $fitted->pivot->y); - } - - public function providerFitWithPosition() - { - return [ - [new Size(800, 600), 'top-left', 0, 0], - [new Size(800, 600), 'top', 100, 0], - [new Size(800, 600), 'top-right', 200, 0], - [new Size(800, 600), 'left', 0, 0], - [new Size(800, 600), 'center', 100, 0], - [new Size(800, 600), 'right', 200, 0], - [new Size(800, 600), 'bottom-left', 0, 0], - [new Size(800, 600), 'bottom', 100, 0], - [new Size(800, 600), 'bottom-right', 200, 0], - - [new Size(600, 800), 'top-left', 0, 0], - [new Size(600, 800), 'top', 0, 0], - [new Size(600, 800), 'top-right', 0, 0], - [new Size(600, 800), 'left', 0, 100], - [new Size(600, 800), 'center', 0, 100], - [new Size(600, 800), 'right', 0, 100], - [new Size(600, 800), 'bottom-left', 0, 200], - [new Size(600, 800), 'bottom', 0, 200], - [new Size(600, 800), 'bottom-right', 0, 200], - ]; - } - - public function testFitsInto() - { - $box = new Size(800, 600); - $fits = $box->fitsInto(new Size(100, 100)); - $this->assertFalse($fits); - - $box = new Size(800, 600); - $fits = $box->fitsInto(new Size(1000, 100)); - $this->assertFalse($fits); - - $box = new Size(800, 600); - $fits = $box->fitsInto(new Size(100, 1000)); - $this->assertFalse($fits); - - $box = new Size(800, 600); - $fits = $box->fitsInto(new Size(800, 600)); - $this->assertTrue($fits); - - $box = new Size(800, 600); - $fits = $box->fitsInto(new Size(1000, 1000)); - $this->assertTrue($fits); - - $box = new Size(100, 100); - $fits = $box->fitsInto(new Size(800, 600)); - $this->assertTrue($fits); - - $box = new Size(100, 100); - $fits = $box->fitsInto(new Size(80, 60)); - $this->assertFalse($fits); - } - - /** - * @expectedException \Intervention\Image\Exception\InvalidArgumentException - */ - public function testInvalidResize() - { - $size = new Size(800, 600); - $size->resize(null, null); - } -} diff --git a/tests/StreamCommandTest.php b/tests/StreamCommandTest.php deleted file mode 100644 index 1af12dbd..00000000 --- a/tests/StreamCommandTest.php +++ /dev/null @@ -1,36 +0,0 @@ -shouldReceive('encode') - ->with('jpg', 87) - ->once() - ->andReturnSelf(); - - $image->shouldReceive('getEncoded') - ->once() - ->andReturn($encodedContent); - - $command = new StreamCommand(['jpg', 87]); - $result = $command->execute($image); - - $this->assertTrue($result); - $this->assertTrue($command->hasOutput()); - - $output = $command->getOutput(); - $this->assertInstanceOf('Psr\Http\Message\StreamInterface', $output); - $this->assertEquals($encodedContent, (string)$output); - } -} diff --git a/tests/TestCase.php b/tests/TestCase.php new file mode 100644 index 00000000..f229c084 --- /dev/null +++ b/tests/TestCase.php @@ -0,0 +1,17 @@ +assertEquals($r, $color->getRgbRed()); + $this->assertEquals($g, $color->getRgbGreen()); + $this->assertEquals($b, $color->getRgbBlue()); + $this->assertEquals($a, $color->getOpacity()); + } +} diff --git a/tests/TextCommandTest.php b/tests/TextCommandTest.php deleted file mode 100644 index 8b19cbf0..00000000 --- a/tests/TextCommandTest.php +++ /dev/null @@ -1,32 +0,0 @@ -shouldReceive('getDriverName')->once()->andReturn('Gd'); - $image = Mockery::mock('\Intervention\Image\Image'); - $image->shouldReceive('getDriver')->once()->andReturn($driver); - $image->shouldReceive('getCore')->once()->andReturn($resource); - $command = new TextCommand(['test', 10, 20]); - $result = $command->execute($image); - $this->assertTrue($result); - $this->assertFalse($command->hasOutput()); - } - - public function testImagick() - { - # code... - } - -} diff --git a/tests/TrimCommandTest.php b/tests/TrimCommandTest.php deleted file mode 100644 index b7f5c770..00000000 --- a/tests/TrimCommandTest.php +++ /dev/null @@ -1,54 +0,0 @@ -shouldReceive('differs')->with($baseColor, 45)->andReturn(true); - $image->shouldReceive('getCore')->once()->andReturn($resource); - $image->shouldReceive('getWidth')->once()->andReturn(800); - $image->shouldReceive('getHeight')->once()->andReturn(600); - $image->shouldReceive('pickColor')->with(0, 0, 'object')->times(2)->andReturn($baseColor); - $image->shouldReceive('pickColor')->with(799, 0, 'object')->once()->andReturn($baseColor); - $image->shouldReceive('setCore')->once(); - $command = new TrimGd(['top-left', ['left', 'right'], 45, 2]); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testImagick() - { - $baseColorPixel = new \ImagickPixel; - $baseColor = Mockery::mock('Intervention\Image\Gd\Color'); - $baseColor->shouldReceive('getPixel')->once()->andReturn($baseColorPixel); - $imagick = Mockery::mock('Imagick'); - $imagick->width = 100; - $imagick->height = 100; - $imagick->shouldReceive('borderimage')->with($baseColorPixel, 1, 1)->once()->andReturn(true); - $imagick->shouldReceive('trimimage')->with(29632.5)->once()->andReturn(true); - $imagick->shouldReceive('getimagepage')->once()->andReturn(['x' => 50, 'y' => 50]); - $imagick->shouldReceive('cropimage')->with(104, 202, 47, 0)->once()->andReturn(true); - $imagick->shouldReceive('setimagepage')->with(0, 0, 0, 0)->once()->andReturn(true); - $imagick->shouldReceive('destroy')->with()->once()->andReturn(true); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getWidth')->once()->andReturn(800); - $image->shouldReceive('getHeight')->once()->andReturn(600); - $image->shouldReceive('pickColor')->with(0, 0, 'object')->once()->andReturn($baseColor); - $image->shouldReceive('getCore')->times(3)->andReturn($imagick); - $command = new TrimImagick(['top-left', ['left', 'right'], 45, 2]); - $result = $command->execute($image); - $this->assertTrue($result); - } -} diff --git a/tests/WidenCommandTest.php b/tests/WidenCommandTest.php deleted file mode 100644 index 3d8622d0..00000000 --- a/tests/WidenCommandTest.php +++ /dev/null @@ -1,49 +0,0 @@ -aspectRatio(); }; - $resource = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); - $image = Mockery::mock('Intervention\Image\Image'); - $size = Mockery::mock('Intervention\Image\Size', [800, 600]); - $size->shouldReceive('resize')->once()->andReturn($size); - $size->shouldReceive('getWidth')->once()->andReturn(800); - $size->shouldReceive('getHeight')->once()->andReturn(600); - $image->shouldReceive('getWidth')->once()->andReturn(800); - $image->shouldReceive('getHeight')->once()->andReturn(600); - $image->shouldReceive('getSize')->once()->andReturn($size); - $image->shouldReceive('getCore')->once()->andReturn($resource); - $image->shouldReceive('setCore')->once(); - $command = new WidenGd([200]); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testImagick() - { - $callback = function ($constraint) { $constraint->upsize(); }; - $imagick = Mockery::mock('Imagick'); - $imagick->shouldReceive('scaleimage')->with(300, 200)->once()->andReturn(true); - $size = Mockery::mock('Intervention\Image\Size', [800, 600]); - $size->shouldReceive('resize')->once()->andReturn($size); - $size->shouldReceive('getWidth')->once()->andReturn(300); - $size->shouldReceive('getHeight')->once()->andReturn(200); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($imagick); - $image->shouldReceive('getSize')->once()->andReturn($size); - $command = new WidenImagick([200]); - $result = $command->execute($image); - $this->assertTrue($result); - } -} diff --git a/tests/images/animation.gif b/tests/images/animation.gif new file mode 100644 index 0000000000000000000000000000000000000000..c45bb7771e1e8d034cb0e17b5f73709407104851 GIT binary patch literal 592 zcmZ?wbhEHb6k*_J_`=R$>7D$48KaYbylY@=ZnEdI*}Pea-aFe=YEgQMnE8qO#; zSy89+Z4v9|1?)j#;ivng&rOi3&2sv)l({L_zQ5S=(*lmS^SFL2W+~5bxiCSZDAnD; zFJW?t*}+bQV?DBp|GE8KLxPsJYn8J)y_3d`<@*ND%i^x#vAXn^t$K?el|_sz}Az3R}J)-SsxlReDu}cq0a9R zz0PIb!k(k0i$c~2AWVe$gn?Bg0qm2ERqJv~Kd%uDMs*sUEhl7C?3>jGY6B0RE zIOZHHU~1kdr4-qMP=oNDMuI@i5d(qw5;_jA^%|d_Ye>Ja?%+e+?VsmJpI3NX$s`-& zo+`}Iu?Xf8pan>NOHh#GOql0T)bXZi-?Bq;1$(WWdDVFrU*|c*@20^U$bUpo)j*Gt b|DmwM2Vcn@>iXFH1~d`LZ;)6*^P4pQdA7Tj literal 0 HcmV?d00001 diff --git a/tests/images/black-friday.png b/tests/images/black-friday.png deleted file mode 100644 index 129c9e9459f0dcee89f1a9f4a25e89b3e9d50a10..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4964 zcmV-q6PxUbP)8pD-|j;!^6Yy@bJ;m z(c|Oe_V)JH*4CMsnYXvM>gwvfy}e~+WwEib8!$PgrKR%n@}Hlda&mGuHa2Z-ZHtSG zTwGjye0)$)P(MFEE-o%cMn)SO8_LScS65dgBqTC2G7}RM3kwS#9v%P&6)Gq!8Wn9ZwP){k+SBv@f7dZtAz8Vg0$yfX-VZZ?7{Ypzm6Z!<^*X&ES=Y`$C#(*~ zi&BTNuicup^LeX6Lp4UOlWQd6tX|`|1_vIyijK9Vgj*`FKSJ6`C#bFdhU~sxueG z;d@0K=9~L#oBTMfCQdRAqDva+GB|vDkYtl!yES(*S`H1srxi38aMP! zaM-caRfz*ygo7p>$eK8UIDfkKoxUC|vvEx($K$X&5{0z;yGn5`J>+rK_^<(oKghb4 z;_R9v!ErdQol#xe`_-m&o6GTdP`I8J@&E%IfjN@s}N9PxitCa0RnF_S8EltG=M>Ek#xZlO<%u6GV(PWe|EbFUh( zp8pzlbJ$zKo<1sY5XVj)bHWdF&(G)u=j&_D2N=2|1|04Mb9BFvhaja$FoRR@jx_*O zdP*A;4(%A!e8$wV_$g#POMiahDx;g;7+GB+E05Dr;nBi7LPwSL8#&5#pKgD}R+V%d zjvhXDZmFk4*;#K|mZ9k|;N3npvk1L zy|naGe2Mhtob@F>62~Qq2r3%9tNEzqBq4{5;<@Y?J70 zrd<=IKt7DfZ1Y(HGgIHggrhj&H6JHg?E0~GJU7q}VLw`MTD0asdmM{1Jky0XCQ|4E z59JMu3z1DxONB|eLVj%A&I(2uEN{f&pZuCS;LJe3K<1dhHgOh1VGtlQFRo*av#cfz^}HBv9nAnaIMD6$g=7k;Q>@J12=flcRAsO)v2<2?r#{evz^x z1KHxn%# z+LOMpB8UQu{Li+d+FXaaDc z%9g_b>~_l1g@ccp!j0!492F&Sk8(pQ6l#x~Rgw!PhEG#C26n%Loh%r{Nl4s&%nm~* zv8q$VOfN+tDar>+-4l7DJ`$|WytC|z;Cj%w@Q$ZsA2xo|XpqmXfunxoG2`Ej+;X~$ zW)pU_NY@72fZJe|U3JVBKI~iOPfIZz()4AHzXJD9Tc(}>i^wPq2}ISyR8SPR!vGAV z^XtiIcY-5#G^I_Gx2f-D;fgD)parLtL%_~tZ50YSu~-`npH8c;KQVmS!N*`54IAcS zlT|q$G!Q{p7Uwb!XuqJce?>{w)t5{&dLC08rx_EmvDt^B*u-WV(=&>)v4t|7;vFGo z)S)pQE{0FoU)VY8!XJ~P|cXE%;ZCyPtfxeP`LRM{m~I=Qs3!J13kElN+_SFXc)CL5Hg zvYi7q?CKl`BuOjK7QQ>LZ7L$&^4xk;D0f)_S+?hVCD^}#d zkd;xK< zFWunuHZ3qfdmM5^3p=THTs}MD-eQTqrIK4oHcc$E6dcs!DV?E+xk1#VNTp$ezRKE~ zvi^wZiGKlD8|EdC(e{^S@POlpPT{Sj{*o_B4(bAw$ui&5& zp0+jsTqgK12YVvuQM!>q3hI_X7Bevps?=~9Kuy~3S%6p$MD9@E zu*3uf2RN;D93z%Ao33ooaQxGgaO^^x){TdTD`>(2_D$5!A-$={n}ctF9-s2!vQpB8eW}A(Us~lT*U*$W0{xV_LbEkY z8+4a))^qVWvXd0rB%Gqh%_NP#$}-^)UzD19UzCrYC>9bMkDsjKz=1_=6O~eJ{Vz|k z+*rSFi$m)Jv+e|V9eI8kT<;3qAE^dXP#JPt3ZEnKl4be`==IglVJo$Grw;PLnx+Nc zq*57G>Rb8NiQhk-^AaNXpimdiU46EwUAfg=Uf?JxT{dmCU5lMKH&ApsxTx}R33fm2$pCG= z6{Nx<4lw`)zEScc)HuHUJrsq>RzTjxs*H8PN>Zc3Cc?Mkiu7xu6){wUh-V@W$F{JP zcr?@$x7U)~bjY_X;Yh4@IH0gGM-p-)Djnukx&lAV0ilsxxoe-&kL4+cL{6=gp?1vh znNS~zvx0I$yY?Cl8ioNmSjRfkmeYwQrlI7fLwd57o-wlJaa~~Wu6RZ zfeQf~w;k@8phn(j6YOi>3*13DH1aUnEZ4Ky5t;kyWZ^U!;cJk}D%_TiLoCDMnS&3+ zG!vMwa)Hrd$wgjXFDU0|YPG^4xmDDwQg3<$4GZ@yGdK(((2|8$LF0AwmAYROz}dJ6 zJnB-vsu`md6zT(yJX9R8-)#K|m5f7v98xh=J8vaLp=e2;`UY(moVIwxkL*QuTJAoq zFZ0~@LpF^EHg7=eIm$R+`hxGt;^QhUUe+R6SX#BD;pkg!aNwmN)6(*mz8*D#QC@*# zedRZ0-EY#A+aJ6U|4hMAu~hPSNlk|uOei?~cHW9Tzm&MVeWD&m>sDEQwCit}r}eI` zu^ZvA9CFN`s;Yi=_ee?QU40@=_3N3{28Z?}G7MUvSZ+uMf|}pVr6in@f&&7pCQo!Q zNjTJ}XH^`2D{s|TQe!RPf^s<51Horj?2@LBuWmzgSIvaYt37k0MLp6IxVo28#@_t$ z9U$o|f9vr~yap2Ptl`>2OKv(W?Hx)Qj$tcrg;Tn_>bqvnBz0j)Z6WKvH@wzHJCcfT z)E`AGLt1oCkQOynK(O~ggr@cazL+hkf;0RiHt4{kR zEUS%QR^h-);I5th-%P?+nU)Jw;Bc)D$9aT;R+Hf?9`j$7)uU@XPAgH&O|Rao!clb9 z<2({(HQK!bN7&&wi%`>H{!I@vjxMx1kHq+)It{oF$F$9`Ri|p9mRy^$uEx=BhW4EL zaz9%y7n6CKJ5H8`VK|*m6RzD(81}Qw$@6qh7o5DX-ygwdb&Qrb97|pXj)&D|l4oHO zj|zfYUF6Yk7!N2mMUP%|Je zC`L&?pR6xgf8Wa$>9LiGNvo{`3n>~_tH*^ieQ6YQ`Cbv9e#MJw zSpB;CA@w1sQ{hRu$MCGNhC_Qqu(h`$E2XP`3GB7#E*OQa zsi=lER1fXQO#4z}9J6!$-u+cY7K<$!FCW%%Xiu!FIJC#h8+ogBo)j;=d1xVf9L^gA z$hu|Ut0}8Ew3jc`#If3ecy$d#GkN21Qm3ZqZ+O#(*%ci6t4WUbR$KP#1b>)<*g5i- z{S1~BFC$@6aBK`;Iy{QF{r3D`Tb}zT>D}k=KN{gs*DuI8^w(M)@>V+%Z~lJs=FNZp zXdG|<+UfT5_vgAe)YUyQj!7LH+vn%MKYsl9H=*(7&#oO5$6KOzpT2*qFqhtR+=$n_ zF-bTKuSBHMz16oLA3uEf^oi2waFjT1Z*Sib?W&2+M0>*<)%%&0kc2~fVJJv$Jih(- z`FSTsqr*WYSN^YP8IWH)n3SC)oi9ER8F%HC@G1ZrUi~~-#^^`^_E_*!=b7I$46=)Kw|b)H_9_N2#4ua>`Xsy>Ge#P>u~h; z;`kd3^jv9M>4`Li!|+1|5|7HubvX9p*h_=`Gy#*qVfq;l8AyktN(XAkvoXfyIMCC~ z0!OwV$IZ>p+qWGKv-aY6vlqsV^f_ACCVXG%I??D#sBXfr9|)a2QmH z;|Lfxc!em(aTE;#q{E@B3dd0}@LQBh9Lcdb2#^kkA~TL3N1K$tzrMtAJdK;1Uq3q> zvP?LRhk;gzG92n#DG%tspSm$CEFH)9TK42i;24g2UnW34b~uDo96#%tlmC1-{X42x zIT(-*M=2S{&qEyz6AwrT{%K3yjs()-00|tow;vk7;44Ha4t=TH@jyBpJ&fb?k+H4L zgC<1(J~d2Yr{@kwPfX6fYYL-W0!MI|mb%pf(%~RD{(4s@w$*u%gvg(jVX0d!ARP`; zIckSf3J$Ua&XV4-&``ggyM;Y1VGt|!D2~&-md*5N)gPn(y+4HsV~QiNPodv%10P_0` Ab^rhX literal 0 HcmV?d00001 diff --git a/tests/images/broken.png b/tests/images/broken.png deleted file mode 100644 index eaecd5c64023152ca7c424acf044cff4fe93b068..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 42 vcmeAS@N?(olHy`uVBq!ia0voZz6=a3NgQkp42-e*>=zjr6c{{R97DJOiY*3R diff --git a/tests/images/cats.gif b/tests/images/cats.gif new file mode 100644 index 0000000000000000000000000000000000000000..307d4f0e52fb0d170511f4d5d472d66194307522 GIT binary patch literal 1721 zcmeIx`&ZI;7{Ku_pbixix#txE5fux(jY>UVLKKvlBHmJ)x6E73Qk$7B5ET(`k@s7Y znrJs~U7VIOb%Lj+T~@AnoThWtX1PwS)7I9soil&iU$D>5&-26kd7Y0h--op`)*o;N zWB_pGV>|!=rf3w(%Fe0a zQja^nfQ~J#ByBfG|30Iv6o6r5jCI*fUBd3eF^SXZToeYi%MzXAfL-m+sVpKYw&OCQ zV?A@5T`kZWp;LE&`3*j`--A$5LVAAXM4ltIu!TGt=s1(%+ni=qwgVS(IRDv&Ol3Z? zW1mI43qE^@YA@?wtW8L#VO=GiYhQ{l@wXb+-EZ=78TKLbvnphbmmJ-SM%u*w^F5oa~xM#o-P`z5Yau zD%L74G2=34Yquv^94c&N;*izf)bMu>f;JaDh=j_Cx3|k9Q%*?KlO7lw_p9ViIGgax{^J6q_r?KSQ80%c$aUV%fI-$rNpJP^odF0y-&ouDCIEZ|fWR$z zJ*^rk9PJRL?NN=%kYv9mUhmln1;Hg{PTSiySwZBKSn~VYb*E{;-BG$eTRuY?m68^p zEEkl9!c7Rw<#SvFEI)4TamM40R^AY%T5a$YF!Lv)o-T@lMY1m%SSH<195{|XR}J!( zvSMv}*@tSlJiS$5cPhFZwCFfECq>S<4=Na!XDxC~pMl1t9yTutVnHRupD(vQnoGBS zg10yDfQbdTByUa~!+b7jEmk$12#D!TKD0e3BqQ2;96qowhR7i^R)eC4uD$qSRBCJ! zFns;jCGBanDSR{C1lR)L0o;1}Z2$(hq#*$yACBj-c{2;_B51HcOc+(Mi-{h#IgmS# zx%pd+is=lPYO(@mR!T=sw)+;u-zrVebT0DqjYZ`G=2|;PLZi-xM~J!XGd5W$Yz8J7 z@H8zhS(d+AB6aCo*QvwZfC{zH|LAM`K*$5A@#H)r2Ip9IF7=b95a7)L023p>88US2 z$rMo^)w=LU->TM)z6-m+o{a;L@Y&()#c{dRC2~-Rs3dAvzBYx?8DuJCtFxi`V5R`6 z-)IO2S>YgqxLHV8FC=^@)NK~R(!i?9R!tG~B+CK96l-LLruRw2W$!Ts9_>y9``C&C zB6EueyYGNOfqjV4+>BCeT*ov^R0rOR*VH+TxYh|zixj&G`^=w0r;7{oTm?={AH?BdLzRzqf(jE&ZF6C_K&m)0C7DkwA2}@k3vv*F`(|i TK8aaq5;Hw^f|?J30K5MNs4O4@ literal 0 HcmV?d00001 diff --git a/tests/images/circle.png b/tests/images/circle.png deleted file mode 100644 index 0ad4e6bdafdb58ed53f52ea8b123c55a63406d07..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 383 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 VHj8nfw%10P~g% Ab^rhX literal 0 HcmV?d00001 diff --git a/tests/images/iptc.jpg b/tests/images/iptc.jpg deleted file mode 100644 index 849dd0a3f9bf8420a4cd8501fac3935f3ebe6e5c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10526 zcmeG?TW}l4k+X{z9}q=~lqEYX7Ysce2gCv(KoT(}5g;Yf5o{U&E!%ZfuEp*ETxqcj z?+!?cA1PZ_;v_$bUwKt3`qVXh1NO$*XGugDnf+)nw>m7_`2NhI-H- z8uW2d#1C=4A+Ghmpd{D&9(Nd|3~=lMf$8N#9PbYVLp{BH{V*}Sl^o)IcI6=A{G5;X z^MO#XC*TXGpmNCPzhXxsFmt^)9KH4SWN_rJ2fm)$vv+6l@k(mkIP-KqwD0cH`Ezed zX8Mu`AGu@y>^)_9?umy;X4f-IFXOi!J^SRd%~#$&df?uNKl0dp-+S&Szx>iafB*Sc ze|1B3<)fee^1uAxzkd4biDM^ft9O3pAOH1-|NYu;h7iw#w0TgH5w1w=@Ph=m zMu!8*+wU5o5S!kHeI%=KUsV@`K z$j0NEIVRG$F(&Et_?p;=C&q+0y87^1LzGTo5~<*tqD@Et|6AXQMihBEdLmsA3XMEo zR_50YTw1@jEUllCrsU}1Ly@c3vTOB59TPFKR#!DMyEYwV%(GCY=y){3NXV(_DD&pX zjfKTX9wdvT$I>xL5R#F^SYmt<%=&={#H(ann20CRvBYFn$YjUUk@gjZ&J4MlEzTZo zcLmUNw5`+X>gw2PYD_n3@x;{BR9r~LlgSuR#LUwg5!YgxxyR-(i%rQ;8br~x2<0eN z^d^~(f}oZL^@hWCQ&ZW5vgFV;nuf|cm!&vXaUE;K1hXcn_Ka!64$)2%4{N8IjfLF^ zEL$jS-oPHVdfn;5By%gE#ybtNSw7vs@gg?$rXk_E6%cce-PuBeNaN5W9U6xVSV>}f znM97eDVUs0HK%`QTU2Vx#B^GFRJ0{F7>(0PGA=+biFIsMyE-Tg3n~|~= z)I$nWTo#Gw5RbZU?x=5(~ASIMeqU>KxTMLMNo!%aSqMOjreQ0AB?Qn_jJBAetV42580I_h5d zy<`N}bmR1Nw2VzsQB>JoOEqKG#t;g?a4rQ?8KzoChrB|7ORh=QE26aQ=29Ze@blO- zvF5JG>rIW&zGrchn9{N;YM$=PhNzi!#WcYtMO?)O)+A8c724d~7BrI>O^F&1rquF!1oShIRCd4F8$I_{c6dM=ErIcGs5`fL?#dT+?3t+ zI&d6C6V<k_=tPH^*TNGE1Dk{Df+#g(p#-Edo8V%jxL6YV5 z?UkT}Z*7-U<&b-FUmd4m9gq>LWD&+a3;-YsQE{8Yj7l6{G-PLC9@b!xV=}IaO_f9s zxs1@w-)cBEebWz$Ir)##M>vec%n;1*DKQT9IM&J7AlE^erx1$zotEemW}V9Nqq z7TB`D-;o76o2OWVxBFGt8g0E1KHK>oNR0_-GPh9hy#ZUu2=WJ3>VvnRtnO`A*URp3 zhQSF3Pc^UDu zbE7;){jNQ#QNGsILO}!Gt+-KM>mo6`VA2Jzp&c7P(MS1puR_d<*7%03^qMtO{5{mN zFt_N`LHrTxhuAFg_W_pSS?hiVx$SLHK^g&Cr7TLuiEDauYNo&YN!EcShLk#BH^OL5xty+%NUh2S?Gx#zJ`xfA4&2Coh%)&mjgfv8u0zVaD zB%>Hg00~VXcVBKCox$|yx!eSH1tn_ZwWg{9MDz1O$PQ_;d0pkC)Tu8@gm%qBSsC)~ zs;<=lX4i3CC2lxJ^m-d+ViaL7#RUgVRgtiHgL>SA=A5UW0ILJ^4)}!_urTZ59jY06 zv*AGnVLJ}?iLnMbKZL?_CCc5}I|g{0*d#h^`9fxeU>OjIW`x?reHJmr(3*vMEdr1C zT7Ms;&dPH)mZ2+`@x7RW3E%u~nxLqXvRL!r1|-DC zaxLpKs#3EpwcS#F-bO>qP~q_stmq@!Ao!lzeH#| zngLhRQ3YHof)>#-lnMMyR%!sn@SmZGpgby}3}tAoBKx2SKKJ}Bz0Y6u)sbtyvG#|T zhM)TK3hRA8s~cc_NMAyJ-wFk2v9<{5M-~}bnGyqzij8R4 z-i$pB@0;OEl4{`>4e7{Dlmoj4B|~qVfE)tdoTgc8+k6AGq6jkOm;<|_c-XG#S~<4a zc((QFYS3?2fNTz$dKVlFqgS6DNfGZlL`7??LsUV(-yiS?1A$;~5PtnV!C+5+xUa9j zuP;0pW>@=R0o`{l6bfw%Z`(aMxchHDLDdGKJoIR0S6L=cKJ#+V{yTd6c3*n--npX>KX${jO9wA|>y@|Z zK`Pw*$bHsfstcT@nw-CJdF^AL{ikpI`wKt&?UhptHz}t-{<*LG+kd?H^WVKcd(F)! z*FW+3uYU7CU;4%GtwUG7KmZPc`Fni6I(xIC=e}kF*Mr2tTW`NB`M}rDk39bLxjng^ z#ftG}YVUad%q4f1_B|*)@nXeN;-3KFc?{hE diff --git a/tests/images/red.gif b/tests/images/red.gif new file mode 100644 index 0000000000000000000000000000000000000000..408dff18335cddb345f42b408c795decce3c7f6d GIT binary patch literal 47 zcmZ?wbh9u|6krfwXkcLYpOV7Bz@P)fK)wJ20~23M|H{*E`4`XGa;tl@9V>%10Q4~o Ab^rhX literal 0 HcmV?d00001 diff --git a/tests/images/star.png b/tests/images/star.png deleted file mode 100644 index def6448d8fd404d07058f5b250dd3302b50ce3f5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 463 zcmV;=0WkiFP)5yNie))P-b|}pvCZrL7U+Lh;{+-f#Q1@B!LQ4fLI%db)YneFA3GmjNKj<3LMV_ z#7$84ejxr!t|8FQ*iLC+ReL#Exh%W;13rZZ22DQuth?R+Ss2dc+z+E6d z19j9aQlsD=5C@WAIX0jSHRB#RXGz5iQQC?f@Z{7@h^f~u7qZ559?VAKIa!T|sQ1^^I4V?IVO6o>!-002ovPDHLk FV1g$Yr*Z%Q diff --git a/tests/images/test.jpg b/tests/images/test.jpg deleted file mode 100644 index 4bb6549141d4179948fe680537487b31f1652f68..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9183 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 CK%zqc diff --git a/tests/images/test.webp b/tests/images/test.webp deleted file mode 100755 index ecd28246eacd3d168cc855baf289aab6a7effe30..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 82 zcmWIYbaV4!U|RvEwADaz}vCfAWgLl^a5>hU5Q*zRc z+T=m2n$d^67+o=@EX+@*4w02nv5~GmRvMl`7C?skC`c)2KUN?XC=M$QC>|>wC;=-0 zC=n}>P?*B>_K6`gtJDvMI7|Nc@rx0UOveQdB|iG}fZc3JPB5Mr?Ms}B?*IS*07*qo IM6N<$f^76>`Tzg` diff --git a/tests/tmp/.gitkeep b/tests/tmp/.gitkeep deleted file mode 100644 index e69de29b..00000000 From 8405300cc7e3663cebc57e3d54d9c7aa94ce053b Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Thu, 21 Oct 2021 15:07:56 +0200 Subject: [PATCH 002/476] EncodedImage class --- .gitignore | 1 + src/Drivers/Abstract/AbstractImage.php | 30 ++++++-------------------- src/EncodedImage.php | 30 ++++++++++++++++++++++++++ src/Interfaces/ImageInterface.php | 3 +++ 4 files changed, 41 insertions(+), 23 deletions(-) create mode 100644 src/EncodedImage.php diff --git a/.gitignore b/.gitignore index daaed11d..799dd941 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ composer.lock vendor/ dev/ .idea/ +.phpunit.result.cache \ No newline at end of file diff --git a/src/Drivers/Abstract/AbstractImage.php b/src/Drivers/Abstract/AbstractImage.php index 780b734b..4c48a781 100644 --- a/src/Drivers/Abstract/AbstractImage.php +++ b/src/Drivers/Abstract/AbstractImage.php @@ -3,6 +3,7 @@ namespace Intervention\Image\Drivers\Abstract; use Intervention\Image\Collection; +use Intervention\Image\EncodedImage; use Intervention\Image\Exceptions\NotWritableException; use Intervention\Image\Geometry\Size; use Intervention\Image\Interfaces\EncoderInterface; @@ -47,35 +48,18 @@ abstract class AbstractImage return $modifier->apply($this); } - public function encode(EncoderInterface $encoder, ?string $path = null): string + public function encode(EncoderInterface $encoder): EncodedImage { - $encoded = $encoder->encode($this); - - if ($path) { - $saved = @file_put_contents($path, $encoded); - if ($saved === false) { - throw new NotWritableException( - "Can't write image data to path ({$path})." - ); - } - } - - return $encoded; + return new EncodedImage($encoder->encode($this)); } - public function toJpeg(?int $quality = null, ?string $path = null): string + public function toJpeg(?int $quality = null): string { - return $this->encode( - $this->resolveDriverClass('Encoders\JpegEncoder', $quality), - $path - ); + return $this->encode($this->resolveDriverClass('Encoders\JpegEncoder', $quality)); } - public function toGif(?string $path = null): string + public function toGif(): string { - return $this->encode( - $this->resolveDriverClass('Encoders\GifEncoder'), - $path - ); + return $this->encode($this->resolveDriverClass('Encoders\GifEncoder')); } } diff --git a/src/EncodedImage.php b/src/EncodedImage.php new file mode 100644 index 00000000..b7691860 --- /dev/null +++ b/src/EncodedImage.php @@ -0,0 +1,30 @@ +data = $data; + } + + public function save(string $filepath): void + { + $saved = @file_put_contents($filepath, (string) $this); + if ($saved === false) { + throw new NotWritableException( + "Can't write image data to path ({$filepath})." + ); + } + } + + public function __toString(): string + { + return $this->data; + } +} diff --git a/src/Interfaces/ImageInterface.php b/src/Interfaces/ImageInterface.php index ed54858a..d4bca589 100644 --- a/src/Interfaces/ImageInterface.php +++ b/src/Interfaces/ImageInterface.php @@ -2,6 +2,8 @@ namespace Intervention\Image\Interfaces; +use Intervention\Image\EncodedImage; + interface ImageInterface { public function size(): SizeInterface; @@ -9,4 +11,5 @@ interface ImageInterface public function height(): int; public function isAnimated(): bool; public function greyscale(): ImageInterface; + public function encode(EncoderInterface $encoder): EncodedImage } From 0cf62e5f1d5f952b790d9a912f788aa9ea109ec9 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Thu, 21 Oct 2021 15:10:44 +0200 Subject: [PATCH 003/476] Bugfix --- src/Interfaces/ImageInterface.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Interfaces/ImageInterface.php b/src/Interfaces/ImageInterface.php index d4bca589..735f046f 100644 --- a/src/Interfaces/ImageInterface.php +++ b/src/Interfaces/ImageInterface.php @@ -11,5 +11,5 @@ interface ImageInterface public function height(): int; public function isAnimated(): bool; public function greyscale(): ImageInterface; - public function encode(EncoderInterface $encoder): EncodedImage + public function encode(EncoderInterface $encoder): EncodedImage; } From 2927abf1a2091bb3b78022b16b38efaa9b988fe7 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Thu, 21 Oct 2021 16:27:27 +0200 Subject: [PATCH 004/476] Normalized delay --- src/Drivers/Gd/Frame.php | 8 ++++---- src/Drivers/Imagick/Frame.php | 8 ++++---- src/Interfaces/FrameInterface.php | 4 ++-- tests/Drivers/Gd/FrameTest.php | 4 ++-- tests/Drivers/Imagick/FrameTest.php | 9 +++++---- 5 files changed, 17 insertions(+), 16 deletions(-) diff --git a/src/Drivers/Gd/Frame.php b/src/Drivers/Gd/Frame.php index e5c9e0d1..2c7d18e1 100644 --- a/src/Drivers/Gd/Frame.php +++ b/src/Drivers/Gd/Frame.php @@ -11,9 +11,9 @@ use Intervention\Image\Interfaces\ImageInterface; class Frame extends AbstractFrame implements FrameInterface { /** - * Delay time in miliseconds after next frame is shown + * Delay time in seconds after next frame is shown * - * @var integer + * @var float */ protected $delay = 0; @@ -48,12 +48,12 @@ class Frame extends AbstractFrame implements FrameInterface return $this->core; } - public function getDelay(): int + public function getDelay(): float { return $this->delay; } - public function setDelay(int $delay): FrameInterface + public function setDelay(float $delay): FrameInterface { $this->delay = $delay; diff --git a/src/Drivers/Imagick/Frame.php b/src/Drivers/Imagick/Frame.php index c5c5652a..44873299 100644 --- a/src/Drivers/Imagick/Frame.php +++ b/src/Drivers/Imagick/Frame.php @@ -20,14 +20,14 @@ class Frame extends AbstractFrame implements FrameInterface return $this->core; } - public function getDelay(): int + public function getDelay(): float { - return $this->core->getImageDelay(); + return $this->core->getImageDelay() / 100; } - public function setDelay(int $delay): FrameInterface + public function setDelay(float $delay): FrameInterface { - $this->core->setImageDelay($delay); + $this->core->setImageDelay(round($delay * 100)); return $this; } diff --git a/src/Interfaces/FrameInterface.php b/src/Interfaces/FrameInterface.php index 2af3408c..4febaea9 100644 --- a/src/Interfaces/FrameInterface.php +++ b/src/Interfaces/FrameInterface.php @@ -6,8 +6,8 @@ interface FrameInterface { public function toImage(): ImageInterface; public function getCore(); - public function getDelay(): int; - public function setDelay(int $delay): FrameInterface; + public function getDelay(): float; + public function setDelay(float $delay): FrameInterface; public function getDispose(): int; public function setDispose(int $dispose): FrameInterface; public function setOffset(int $left, int $top): FrameInterface; diff --git a/tests/Drivers/Gd/FrameTest.php b/tests/Drivers/Gd/FrameTest.php index b17feb51..bb72f17b 100644 --- a/tests/Drivers/Gd/FrameTest.php +++ b/tests/Drivers/Gd/FrameTest.php @@ -24,9 +24,9 @@ class FrameTest extends TestCase $frame = $this->getTestFrame(); $this->assertEquals(0, $frame->getDelay()); - $result = $frame->setDelay(100); + $result = $frame->setDelay(1.5); $this->assertInstanceOf(Frame::class, $result); - $this->assertEquals(100, $frame->getDelay()); + $this->assertEquals(1.5, $frame->getDelay()); } public function testSetGetDispose() diff --git a/tests/Drivers/Imagick/FrameTest.php b/tests/Drivers/Imagick/FrameTest.php index c7c2a26d..30ae1467 100644 --- a/tests/Drivers/Imagick/FrameTest.php +++ b/tests/Drivers/Imagick/FrameTest.php @@ -14,7 +14,7 @@ class FrameTest extends TestCase { $imagick = new Imagick(); $imagick->newImage(3, 2, new ImagickPixel('red'), 'png'); - $imagick->setImageDelay(4); + $imagick->setImageDelay(125); // 1.25 seconds $imagick->setImageDispose(5); $imagick->setImagePage(3, 2, 8, 9); @@ -30,11 +30,12 @@ class FrameTest extends TestCase public function testSetGetDelay() { $frame = $this->getTestFrame(); - $this->assertEquals(4, $frame->getDelay()); + $this->assertEquals(1.25, $frame->getDelay()); - $result = $frame->setDelay(100); + $result = $frame->setDelay(2.5); $this->assertInstanceOf(Frame::class, $result); - $this->assertEquals(100, $frame->getDelay()); + $this->assertEquals(2.5, $frame->getDelay()); + $this->assertEquals(250, $frame->getCore()->getImageDelay()); } public function testSetGetDispose() From 26a35946f10f9730161b190b37b45369cc7e3e75 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Thu, 21 Oct 2021 16:33:25 +0200 Subject: [PATCH 005/476] Normalized delay --- src/Drivers/Gd/Decoders/BinaryImageDecoder.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Drivers/Gd/Decoders/BinaryImageDecoder.php b/src/Drivers/Gd/Decoders/BinaryImageDecoder.php index a5e69c0b..0269da2d 100644 --- a/src/Drivers/Gd/Decoders/BinaryImageDecoder.php +++ b/src/Drivers/Gd/Decoders/BinaryImageDecoder.php @@ -47,7 +47,7 @@ class BinaryImageDecoder extends AbstractDecoder implements DecoderInterface $splitter = GifSplitter::create($gif)->split(); $delays = $splitter->getDelays(); foreach ($splitter->coalesceToResources() as $key => $gd) { - $image->addFrame((new Frame($gd))->setDelay($delays[$key])); + $image->addFrame((new Frame($gd))->setDelay($delays[$key] / 100)); } return $image; From 60f7b300c48d494b277f585c0bde7ed42a683171 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Thu, 21 Oct 2021 16:36:57 +0200 Subject: [PATCH 006/476] Fix --- src/Drivers/Abstract/AbstractImage.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Drivers/Abstract/AbstractImage.php b/src/Drivers/Abstract/AbstractImage.php index 4c48a781..84ae28ab 100644 --- a/src/Drivers/Abstract/AbstractImage.php +++ b/src/Drivers/Abstract/AbstractImage.php @@ -9,6 +9,7 @@ use Intervention\Image\Geometry\Size; use Intervention\Image\Interfaces\EncoderInterface; use Intervention\Image\Interfaces\FrameInterface; use Intervention\Image\Interfaces\ImageInterface; +use Intervention\Image\Interfaces\ModifierInterface; use Intervention\Image\Interfaces\SizeInterface; use Intervention\Image\Traits\CanResolveDriverClass; From 588178954949fb43a266c94e592ff557110cc2f7 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Thu, 21 Oct 2021 16:55:03 +0200 Subject: [PATCH 007/476] Animaton loops --- src/Drivers/Abstract/AbstractImage.php | 14 ++++++++++++++ src/Drivers/Gd/Decoders/BinaryImageDecoder.php | 2 ++ src/Drivers/Gd/Image.php | 2 ++ .../Imagick/Decoders/BinaryImageDecoder.php | 1 + src/Interfaces/ImageInterface.php | 2 ++ 5 files changed, 21 insertions(+) diff --git a/src/Drivers/Abstract/AbstractImage.php b/src/Drivers/Abstract/AbstractImage.php index 84ae28ab..9467c67d 100644 --- a/src/Drivers/Abstract/AbstractImage.php +++ b/src/Drivers/Abstract/AbstractImage.php @@ -17,6 +17,8 @@ abstract class AbstractImage { use CanResolveDriverClass; + protected $loops = 0; + public function getIterator(): Collection { return $this->frames; @@ -34,6 +36,18 @@ abstract class AbstractImage return $this; } + public function setLoops(int $count): ImageInterface + { + $this->loops = $count; + + return $this; + } + + public function loops(): int + { + return $this->loops; + } + public function size(): SizeInterface { return new Size($this->width(), $this->height()); diff --git a/src/Drivers/Gd/Decoders/BinaryImageDecoder.php b/src/Drivers/Gd/Decoders/BinaryImageDecoder.php index 0269da2d..fcc2c992 100644 --- a/src/Drivers/Gd/Decoders/BinaryImageDecoder.php +++ b/src/Drivers/Gd/Decoders/BinaryImageDecoder.php @@ -40,6 +40,8 @@ class BinaryImageDecoder extends AbstractDecoder implements DecoderInterface $image = new Image(new Collection()); $gif = GifDecoder::decode($input); + $image->setLoops($gif->getMainApplicationExtension()?->getLoops()); + if (!$gif->isAnimated()) { return $image->addFrame(new Frame(@imagecreatefromstring($input))); } diff --git a/src/Drivers/Gd/Image.php b/src/Drivers/Gd/Image.php index 40742d24..5f331569 100644 --- a/src/Drivers/Gd/Image.php +++ b/src/Drivers/Gd/Image.php @@ -14,6 +14,8 @@ use IteratorAggregate; class Image extends AbstractImage implements ImageInterface, IteratorAggregate { + protected $loops = 0; + public function __construct(protected Collection $frames) { // diff --git a/src/Drivers/Imagick/Decoders/BinaryImageDecoder.php b/src/Drivers/Imagick/Decoders/BinaryImageDecoder.php index 96cc9a98..72cdc718 100644 --- a/src/Drivers/Imagick/Decoders/BinaryImageDecoder.php +++ b/src/Drivers/Imagick/Decoders/BinaryImageDecoder.php @@ -24,6 +24,7 @@ class BinaryImageDecoder extends AbstractDecoder implements DecoderInterface $imagick = $imagick->coalesceImages(); $image = new Image(new Collection()); + $image->setLoops($imagick->getNumberImages()); foreach ($imagick as $frame_content) { $image->addFrame(new Frame($frame_content)); diff --git a/src/Interfaces/ImageInterface.php b/src/Interfaces/ImageInterface.php index 735f046f..a7609d05 100644 --- a/src/Interfaces/ImageInterface.php +++ b/src/Interfaces/ImageInterface.php @@ -12,4 +12,6 @@ interface ImageInterface public function isAnimated(): bool; public function greyscale(): ImageInterface; public function encode(EncoderInterface $encoder): EncodedImage; + public function setLoops(int $count): ImageInterface; + public function loops(): int; } From f540e07ed31c52abe48d78ed2c19054b46679a9a Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Thu, 21 Oct 2021 17:01:27 +0200 Subject: [PATCH 008/476] Fixes --- src/Drivers/Gd/Encoders/GifEncoder.php | 2 +- src/Drivers/Imagick/Decoders/BinaryImageDecoder.php | 2 +- src/Drivers/Imagick/Encoders/GifEncoder.php | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Drivers/Gd/Encoders/GifEncoder.php b/src/Drivers/Gd/Encoders/GifEncoder.php index d4a99a02..927fc670 100644 --- a/src/Drivers/Gd/Encoders/GifEncoder.php +++ b/src/Drivers/Gd/Encoders/GifEncoder.php @@ -22,7 +22,7 @@ class GifEncoder extends AbstractEncoder implements EncoderInterface protected function encodeAnimated($image): string { - $builder = GifBuilder::canvas($image->width(), $image->height(), 2); + $builder = GifBuilder::canvas($image->width(), $image->height(), $image->getLoops()); foreach ($image as $key => $frame) { $source = $this->encode($frame->toImage()); $builder->addFrame($source, $frame->getDelay()); diff --git a/src/Drivers/Imagick/Decoders/BinaryImageDecoder.php b/src/Drivers/Imagick/Decoders/BinaryImageDecoder.php index 72cdc718..76145fba 100644 --- a/src/Drivers/Imagick/Decoders/BinaryImageDecoder.php +++ b/src/Drivers/Imagick/Decoders/BinaryImageDecoder.php @@ -24,7 +24,7 @@ class BinaryImageDecoder extends AbstractDecoder implements DecoderInterface $imagick = $imagick->coalesceImages(); $image = new Image(new Collection()); - $image->setLoops($imagick->getNumberImages()); + $image->setLoops($imagick->getImageIterations()); foreach ($imagick as $frame_content) { $image->addFrame(new Frame($frame_content)); diff --git a/src/Drivers/Imagick/Encoders/GifEncoder.php b/src/Drivers/Imagick/Encoders/GifEncoder.php index 4b62dae2..a2eed02b 100644 --- a/src/Drivers/Imagick/Encoders/GifEncoder.php +++ b/src/Drivers/Imagick/Encoders/GifEncoder.php @@ -19,6 +19,7 @@ class GifEncoder extends AbstractEncoder implements EncoderInterface $gif->addImage($frame->getCore()); } + $gif->setImageIterations($image->getLoops()); $gif->setFormat($format); $gif->setImageFormat($format); $gif->setCompression($compression); From f7d6e26e4cc61d9cf5715fb9392c3201df6aec4e Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Thu, 21 Oct 2021 17:05:39 +0200 Subject: [PATCH 009/476] Fixes --- src/Drivers/Gd/Encoders/GifEncoder.php | 2 +- src/Drivers/Imagick/Encoders/GifEncoder.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Drivers/Gd/Encoders/GifEncoder.php b/src/Drivers/Gd/Encoders/GifEncoder.php index 927fc670..47dbb7cb 100644 --- a/src/Drivers/Gd/Encoders/GifEncoder.php +++ b/src/Drivers/Gd/Encoders/GifEncoder.php @@ -22,7 +22,7 @@ class GifEncoder extends AbstractEncoder implements EncoderInterface protected function encodeAnimated($image): string { - $builder = GifBuilder::canvas($image->width(), $image->height(), $image->getLoops()); + $builder = GifBuilder::canvas($image->width(), $image->height(), $image->loops()); foreach ($image as $key => $frame) { $source = $this->encode($frame->toImage()); $builder->addFrame($source, $frame->getDelay()); diff --git a/src/Drivers/Imagick/Encoders/GifEncoder.php b/src/Drivers/Imagick/Encoders/GifEncoder.php index a2eed02b..1998edfb 100644 --- a/src/Drivers/Imagick/Encoders/GifEncoder.php +++ b/src/Drivers/Imagick/Encoders/GifEncoder.php @@ -19,7 +19,7 @@ class GifEncoder extends AbstractEncoder implements EncoderInterface $gif->addImage($frame->getCore()); } - $gif->setImageIterations($image->getLoops()); + $gif->setImageIterations($image->loops()); $gif->setFormat($format); $gif->setImageFormat($format); $gif->setCompression($compression); From 99038d3a75d6cf1460b5175a8228e1b57bab3109 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Fri, 22 Oct 2021 16:15:49 +0200 Subject: [PATCH 010/476] Refactoring --- src/Drivers/Abstract/AbstractImage.php | 21 +++++++++++++++++-- .../Gd/Decoders/BinaryImageDecoder.php | 3 ++- src/Drivers/Gd/Image.php | 12 ----------- src/Drivers/Imagick/Image.php | 10 --------- 4 files changed, 21 insertions(+), 25 deletions(-) diff --git a/src/Drivers/Abstract/AbstractImage.php b/src/Drivers/Abstract/AbstractImage.php index 9467c67d..00f970a7 100644 --- a/src/Drivers/Abstract/AbstractImage.php +++ b/src/Drivers/Abstract/AbstractImage.php @@ -18,6 +18,12 @@ abstract class AbstractImage use CanResolveDriverClass; protected $loops = 0; + protected $frames; + + public function __construct(Collection $frames) + { + $this->frames = $frames; + } public function getIterator(): Collection { @@ -70,11 +76,22 @@ abstract class AbstractImage public function toJpeg(?int $quality = null): string { - return $this->encode($this->resolveDriverClass('Encoders\JpegEncoder', $quality)); + return $this->encode( + $this->resolveDriverClass('Encoders\JpegEncoder', $quality) + ); } public function toGif(): string { - return $this->encode($this->resolveDriverClass('Encoders\GifEncoder')); + return $this->encode( + $this->resolveDriverClass('Encoders\GifEncoder') + ); + } + + public function greyscale(): ImageInterface + { + return $this->modify( + $this->resolveDriverClass('Modifiers\GreyscaleModifier') + ); } } diff --git a/src/Drivers/Gd/Decoders/BinaryImageDecoder.php b/src/Drivers/Gd/Decoders/BinaryImageDecoder.php index fcc2c992..478ae7f6 100644 --- a/src/Drivers/Gd/Decoders/BinaryImageDecoder.php +++ b/src/Drivers/Gd/Decoders/BinaryImageDecoder.php @@ -40,12 +40,13 @@ class BinaryImageDecoder extends AbstractDecoder implements DecoderInterface $image = new Image(new Collection()); $gif = GifDecoder::decode($input); - $image->setLoops($gif->getMainApplicationExtension()?->getLoops()); if (!$gif->isAnimated()) { return $image->addFrame(new Frame(@imagecreatefromstring($input))); } + $image->setLoops($gif->getMainApplicationExtension()?->getLoops()); + $splitter = GifSplitter::create($gif)->split(); $delays = $splitter->getDelays(); foreach ($splitter->coalesceToResources() as $key => $gd) { diff --git a/src/Drivers/Gd/Image.php b/src/Drivers/Gd/Image.php index 5f331569..751f4d4b 100644 --- a/src/Drivers/Gd/Image.php +++ b/src/Drivers/Gd/Image.php @@ -14,13 +14,6 @@ use IteratorAggregate; class Image extends AbstractImage implements ImageInterface, IteratorAggregate { - protected $loops = 0; - - public function __construct(protected Collection $frames) - { - // - } - public function width(): int { return imagesx($this->frames->first()->getCore()); @@ -30,9 +23,4 @@ class Image extends AbstractImage implements ImageInterface, IteratorAggregate { return imagesy($this->frames->first()->getCore()); } - - public function greyscale(): ImageInterface - { - return $this->modify(new Modifiers\GreyscaleModifier()); - } } diff --git a/src/Drivers/Imagick/Image.php b/src/Drivers/Imagick/Image.php index be0a3afc..47073a25 100644 --- a/src/Drivers/Imagick/Image.php +++ b/src/Drivers/Imagick/Image.php @@ -13,11 +13,6 @@ use IteratorAggregate; class Image extends AbstractImage implements ImageInterface, IteratorAggregate { - public function __construct(protected Collection $frames) - { - // - } - public function width(): int { return $this->frames->first()->getCore()->getImageWidth(); @@ -27,9 +22,4 @@ class Image extends AbstractImage implements ImageInterface, IteratorAggregate { return $this->frames->first()->getCore()->getImageHeight(); } - - public function greyscale(): ImageInterface - { - return $this->modify(new Modifiers\GreyscaleModifier()); - } } From a149d87b332d69a7f48ba4371d4a07ce8010878e Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Fri, 29 Oct 2021 18:10:34 +0200 Subject: [PATCH 011/476] Methods --- src/EncodedImage.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/EncodedImage.php b/src/EncodedImage.php index b7691860..fa01f656 100644 --- a/src/EncodedImage.php +++ b/src/EncodedImage.php @@ -7,6 +7,7 @@ use Intervention\Image\Exceptions\NotWritableException; class EncodedImage { protected $data; + protected $mimetype; public function __construct(string $data) { @@ -23,6 +24,11 @@ class EncodedImage } } + public function toDataUrl(): string + { + return ''; + } + public function __toString(): string { return $this->data; From e475b28f013c166a5a4f406fb5abe3016460a237 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Fri, 29 Oct 2021 18:10:42 +0200 Subject: [PATCH 012/476] Added BlurModifiers --- src/Drivers/Gd/Modifiers/BlurModifier.php | 36 +++++++++++++++++++ .../Imagick/Modifiers/BlurModifier.php | 28 +++++++++++++++ 2 files changed, 64 insertions(+) create mode 100644 src/Drivers/Gd/Modifiers/BlurModifier.php create mode 100644 src/Drivers/Imagick/Modifiers/BlurModifier.php diff --git a/src/Drivers/Gd/Modifiers/BlurModifier.php b/src/Drivers/Gd/Modifiers/BlurModifier.php new file mode 100644 index 00000000..376b364f --- /dev/null +++ b/src/Drivers/Gd/Modifiers/BlurModifier.php @@ -0,0 +1,36 @@ +amount = $amount; + } + + public function apply(ImageInterface $image): ImageInterface + { + foreach ($image as $frame) { + $this->blurFrame($frame); + } + + return $image; + } + + protected function blurFrame(FrameInterface $frame): void + { + for ($i = 0; $i < $this->amount; $i++) { + imagefilter($frame->getCore(), IMG_FILTER_GAUSSIAN_BLUR); + } + } +} diff --git a/src/Drivers/Imagick/Modifiers/BlurModifier.php b/src/Drivers/Imagick/Modifiers/BlurModifier.php new file mode 100644 index 00000000..f22b5f01 --- /dev/null +++ b/src/Drivers/Imagick/Modifiers/BlurModifier.php @@ -0,0 +1,28 @@ +amount = $amount; + } + + public function apply(ImageInterface $image): ImageInterface + { + foreach ($this->image as $frame) { + $frame->getCore()->blurImage(1 * $this->amount, 0.5 * $this->amount); + } + + return $this->image; + } +} From 088f92147db4debe68409cb8bd5e21b49173e37c Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Fri, 29 Oct 2021 20:10:27 +0200 Subject: [PATCH 013/476] Added Frame::getSize() --- src/Drivers/Gd/Frame.php | 7 +++++++ src/Drivers/Imagick/Frame.php | 5 +++++ src/Interfaces/FrameInterface.php | 1 + 3 files changed, 13 insertions(+) diff --git a/src/Drivers/Gd/Frame.php b/src/Drivers/Gd/Frame.php index 2c7d18e1..0d3e0c64 100644 --- a/src/Drivers/Gd/Frame.php +++ b/src/Drivers/Gd/Frame.php @@ -5,8 +5,10 @@ namespace Intervention\Image\Drivers\Gd; use GdImage; use Intervention\Image\Collection; use Intervention\Image\Drivers\Abstract\AbstractFrame; +use Intervention\Image\Geometry\Size; use Intervention\Image\Interfaces\FrameInterface; use Intervention\Image\Interfaces\ImageInterface; +use Intervention\Image\Interfaces\SizeInterface; class Frame extends AbstractFrame implements FrameInterface { @@ -48,6 +50,11 @@ class Frame extends AbstractFrame implements FrameInterface return $this->core; } + public function getSize(): SizeInterface + { + return new Size(imagesx($this->core), imagesy($this->core)); + } + public function getDelay(): float { return $this->delay; diff --git a/src/Drivers/Imagick/Frame.php b/src/Drivers/Imagick/Frame.php index 44873299..97e02065 100644 --- a/src/Drivers/Imagick/Frame.php +++ b/src/Drivers/Imagick/Frame.php @@ -20,6 +20,11 @@ class Frame extends AbstractFrame implements FrameInterface return $this->core; } + public function getSize(): SizeInterface + { + return new Size($this->core->getImageWidth(), $this->core->getImageHeight()); + } + public function getDelay(): float { return $this->core->getImageDelay() / 100; diff --git a/src/Interfaces/FrameInterface.php b/src/Interfaces/FrameInterface.php index 4febaea9..3c27aea9 100644 --- a/src/Interfaces/FrameInterface.php +++ b/src/Interfaces/FrameInterface.php @@ -6,6 +6,7 @@ interface FrameInterface { public function toImage(): ImageInterface; public function getCore(); + public function getSize(): SizeInterface; public function getDelay(): float; public function setDelay(float $delay): FrameInterface; public function getDispose(): int; From be5301dd8972cdc654113cf88e1e8292f6c9772b Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Fri, 29 Oct 2021 20:10:40 +0200 Subject: [PATCH 014/476] Added blur() method --- src/Drivers/Abstract/AbstractImage.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/Drivers/Abstract/AbstractImage.php b/src/Drivers/Abstract/AbstractImage.php index 00f970a7..a01593ef 100644 --- a/src/Drivers/Abstract/AbstractImage.php +++ b/src/Drivers/Abstract/AbstractImage.php @@ -94,4 +94,11 @@ abstract class AbstractImage $this->resolveDriverClass('Modifiers\GreyscaleModifier') ); } + + public function blur(int $amount): ImageInterface + { + return $this->modify( + $this->resolveDriverClass('Modifiers\BlurModifier', $amount) + ); + } } From da92a66926de5ad5475b1006372dec6039bb403b Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Fri, 29 Oct 2021 20:12:55 +0200 Subject: [PATCH 015/476] Added tests --- src/Drivers/Imagick/Frame.php | 2 ++ tests/Drivers/Gd/FrameTest.php | 7 +++++++ tests/Drivers/Imagick/FrameTest.php | 7 +++++++ 3 files changed, 16 insertions(+) diff --git a/src/Drivers/Imagick/Frame.php b/src/Drivers/Imagick/Frame.php index 97e02065..94cc9c68 100644 --- a/src/Drivers/Imagick/Frame.php +++ b/src/Drivers/Imagick/Frame.php @@ -5,8 +5,10 @@ namespace Intervention\Image\Drivers\Imagick; use Imagick; use Intervention\Image\Collection; use Intervention\Image\Drivers\Abstract\AbstractFrame; +use Intervention\Image\Geometry\Size; use Intervention\Image\Interfaces\FrameInterface; use Intervention\Image\Interfaces\ImageInterface; +use Intervention\Image\Interfaces\SizeInterface; class Frame extends AbstractFrame implements FrameInterface { diff --git a/tests/Drivers/Gd/FrameTest.php b/tests/Drivers/Gd/FrameTest.php index bb72f17b..3c033103 100644 --- a/tests/Drivers/Gd/FrameTest.php +++ b/tests/Drivers/Gd/FrameTest.php @@ -4,6 +4,7 @@ namespace Intervention\Image\Tests\Drivers\Gd; use Intervention\Image\Drivers\Gd\Frame; use Intervention\Image\Drivers\Gd\Image; +use Intervention\Image\Geometry\Size; use Intervention\Image\Tests\TestCase; class FrameTest extends TestCase @@ -19,6 +20,12 @@ class FrameTest extends TestCase $this->assertInstanceOf(Frame::class, $frame); } + public function testGetSize(): void + { + $frame = $this->getTestFrame(); + $this->assertInstanceOf(Size::class, $frame->getSize()); + } + public function testSetGetDelay() { $frame = $this->getTestFrame(); diff --git a/tests/Drivers/Imagick/FrameTest.php b/tests/Drivers/Imagick/FrameTest.php index 30ae1467..e5764276 100644 --- a/tests/Drivers/Imagick/FrameTest.php +++ b/tests/Drivers/Imagick/FrameTest.php @@ -6,6 +6,7 @@ use Imagick; use ImagickPixel; use Intervention\Image\Drivers\Imagick\Frame; use Intervention\Image\Drivers\Imagick\Image; +use Intervention\Image\Geometry\Size; use Intervention\Image\Tests\TestCase; class FrameTest extends TestCase @@ -27,6 +28,12 @@ class FrameTest extends TestCase $this->assertInstanceOf(Frame::class, $frame); } + public function testGetSize(): void + { + $frame = $this->getTestFrame(); + $this->assertInstanceOf(Size::class, $frame->getSize()); + } + public function testSetGetDelay() { $frame = $this->getTestFrame(); From 72f4e2e6ead05950b936b1157382f1f7f9d7c997 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Fri, 29 Oct 2021 20:42:48 +0200 Subject: [PATCH 016/476] Resizer --- src/Geometry/Resizer.php | 194 +++++++++++++++++ tests/Geometry/ResizerTest.php | 370 +++++++++++++++++++++++++++++++++ 2 files changed, 564 insertions(+) create mode 100644 src/Geometry/Resizer.php create mode 100644 tests/Geometry/ResizerTest.php diff --git a/src/Geometry/Resizer.php b/src/Geometry/Resizer.php new file mode 100644 index 00000000..d804194c --- /dev/null +++ b/src/Geometry/Resizer.php @@ -0,0 +1,194 @@ +size = $size; + $this->target = new Size(0, 0); + } + + public static function fromSize(SizeInterface $size): self + { + return new self($size); + } + + public function getSize(): SizeInterface + { + return $this->size; + } + + protected function hasTargetWidth(): bool + { + return $this->target->getWidth() > 0; + } + + protected function hasTargetHeight(): bool + { + return $this->target->getHeight() > 0; + } + + public function width(int $width): self + { + $this->target->setWidth($width); + + return $this; + } + + public function height(int $height): self + { + $this->target->setHeight($height); + + return $this; + } + + public function setTargetSizeByArray(array $arguments): self + { + if (isset($arguments[0]) && is_callable($arguments[0])) { + $arguments[0]($this); + + return $this; + } + + if (isset($arguments[0]) && is_numeric($arguments[0])) { + $this->width($arguments[0]); + } + + if (isset($arguments[1]) && is_numeric($arguments[1])) { + $this->height($arguments[1]); + } + + return $this; + } + + public function setTargetSize(SizeInterface $size): self + { + $this->target = $size; + + return $this; + } + + public function resize(): SizeInterface + { + $resized = clone $this->size; + + if ($this->hasTargetWidth()) { + $resized->setWidth($this->target->getWidth()); + } + + if ($this->hasTargetHeight()) { + $resized->setHeight($this->target->getHeight()); + } + + return $resized; + } + + public function resizeDown(): SizeInterface + { + if ($this->hasTargetWidth()) { + $this->target->setWidth( + min($this->target->getWidth(), $this->size->getWidth()) + ); + } + + if ($this->hasTargetHeight()) { + $this->target->setHeight( + min($this->target->getHeight(), $this->size->getHeight()) + ); + } + + return $this->resize(); + } + + public function scale(): SizeInterface + { + if ($this->hasTargetWidth() && $this->hasTargetHeight()) { + $this->target->setWidth(min( + $this->getProportionalWidth(), + $this->target->getWidth() + )); + $this->target->setHeight(min( + $this->getProportionalHeight(), + $this->target->getHeight() + )); + } elseif ($this->hasTargetWidth()) { + $this->target->setHeight($this->getProportionalHeight()); + } elseif ($this->hasTargetHeight()) { + $this->target->setWidth($this->getProportionalWidth()); + } + + return $this->resize(); + } + + public function scaleDown(): SizeInterface + { + if ($this->hasTargetWidth() && $this->hasTargetHeight()) { + $this->target->setWidth(min( + $this->getProportionalWidth(), + $this->target->getWidth(), + $this->size->getWidth() + )); + $this->target->setHeight(min( + $this->getProportionalHeight(), + $this->target->getHeight(), + $this->size->getHeight() + )); + } elseif ($this->hasTargetWidth()) { + $this->target->setWidth(min( + $this->target->getWidth(), + $this->size->getWidth() + )); + $this->target->setHeight(min( + $this->getProportionalHeight(), + $this->size->getHeight() + )); + } elseif ($this->hasTargetHeight()) { + $this->target->setWidth(min( + $this->getProportionalWidth(), + $this->size->getWidth() + )); + $this->target->setHeight(min( + $this->target->getHeight(), + $this->size->getHeight() + )); + } + + return $this->resize(); + } + + protected function getProportionalWidth(): int + { + if (! $this->hasTargetHeight()) { + return $this->size->getWidth(); + } + + return (int) round($this->target->getHeight() * $this->size->getAspectRatio()); + } + + protected function getProportionalHeight(): int + { + if (! $this->hasTargetWidth()) { + return $this->size->getHeight(); + } + + return (int) round($this->target->getWidth() / $this->size->getAspectRatio()); + } +} diff --git a/tests/Geometry/ResizerTest.php b/tests/Geometry/ResizerTest.php new file mode 100644 index 00000000..5395aaff --- /dev/null +++ b/tests/Geometry/ResizerTest.php @@ -0,0 +1,370 @@ +setTargetSizeByArray([800, 600]); + $this->assertInstanceOf(Resizer::class, $result); + $this->assertEquals(800, $result->resize()->getWidth()); + $this->assertEquals(600, $result->resize()->getHeight()); + + $resizer = new Resizer(new Size(300, 200)); + $result = $resizer->setTargetSizeByArray([800]); + $this->assertInstanceOf(Resizer::class, $result); + $this->assertEquals(800, $result->resize()->getWidth()); + $this->assertEquals(200, $result->resize()->getHeight()); + + $resizer = new Resizer(new Size(300, 200)); + $result = $resizer->setTargetSizeByArray([function ($size) { + $size->width(80); + $size->height(40); + }]); + $this->assertInstanceOf(Resizer::class, $result); + $this->assertEquals(80, $result->resize()->getWidth()); + $this->assertEquals(40, $result->resize()->getHeight()); + + $resizer = new Resizer(new Size(300, 200)); + $result = $resizer->setTargetSizeByArray([function ($size) { + $size->width(80); + }]); + $this->assertInstanceOf(Resizer::class, $result); + $this->assertEquals(80, $result->resize()->getWidth()); + $this->assertEquals(200, $result->resize()->getHeight()); + + $resizer = new Resizer(new Size(300, 200)); + $result = $resizer->setTargetSizeByArray([function ($size) { + $size->height(10); + }]); + $this->assertInstanceOf(Resizer::class, $result); + $this->assertEquals(300, $result->resize()->getWidth()); + $this->assertEquals(10, $result->resize()->getHeight()); + } + + public function testSetTargetSize(): void + { + $resizer = new Resizer(new Size(300, 200)); + $result = $resizer->setTargetSize(new Size(200, 100)); + $this->assertInstanceOf(Resizer::class, $result); + $this->assertEquals(200, $result->resize()->getWidth()); + $this->assertEquals(100, $result->resize()->getHeight()); + } + + public function testResize() + { + $size = new Size(300, 200); + $resizer = new Resizer($size); + $resizer->width(150); + $result = $resizer->resize(); + $original = $resizer->getSize(); + $this->assertEquals(150, $result->getWidth()); + $this->assertEquals(200, $result->getHeight()); + $this->assertEquals(300, $original->getWidth()); + $this->assertEquals(200, $original->getHeight()); + + $size = new Size(300, 200); + $resizer = new Resizer($size); + $resizer->width(20); + $resizer->height(10); + $result = $resizer->resize(); + $original = $resizer->getSize(); + $this->assertEquals(20, $result->getWidth()); + $this->assertEquals(10, $result->getHeight()); + $this->assertEquals(300, $original->getWidth()); + $this->assertEquals(200, $original->getHeight()); + } + + public function testResizeDown() + { + // 800x600 > 1000x2000 = 800x600 + $size = new Size(800, 600); + $resizer = new Resizer($size); + $resizer->width(1000); + $resizer->height(2000); + $result = $resizer->resizeDown(); + $this->assertEquals(800, $result->getWidth()); + $this->assertEquals(600, $result->getHeight()); + + // 800x600 > 400x1000 = 400x600 + $size = new Size(800, 600); + $resizer = new Resizer($size); + $resizer->width(400); + $resizer->height(1000); + $result = $resizer->resizeDown(); + $this->assertEquals(400, $result->getWidth()); + $this->assertEquals(600, $result->getHeight()); + + // 800x600 > 1000x400 = 800x400 + $size = new Size(800, 600); + $resizer = new Resizer($size); + $resizer->width(1000); + $resizer->height(400); + $result = $resizer->resizeDown(); + $this->assertEquals(800, $result->getWidth()); + $this->assertEquals(400, $result->getHeight()); + + // 800x600 > 400x300 = 400x300 + $size = new Size(800, 600); + $resizer = new Resizer($size); + $resizer->width(400); + $resizer->height(300); + $result = $resizer->resizeDown(); + $this->assertEquals(400, $result->getWidth()); + $this->assertEquals(300, $result->getHeight()); + + // 800x600 > 1000xnull = 800x600 + $size = new Size(800, 600); + $resizer = new Resizer($size); + $resizer->width(1000); + $result = $resizer->resizeDown(); + $this->assertEquals(800, $result->getWidth()); + $this->assertEquals(600, $result->getHeight()); + + // 800x600 > nullx1000 = 800x600 + $size = new Size(800, 600); + $resizer = new Resizer($size); + $resizer->height(1000); + $result = $resizer->resizeDown(); + $this->assertEquals(800, $result->getWidth()); + $this->assertEquals(600, $result->getHeight()); + } + + public function testScale() + { + // 800x600 > 1000x2000 = 1000x750 + $size = new Size(800, 600); + $resizer = new Resizer($size); + $resizer->width(1000); + $resizer->height(2000); + $result = $resizer->scale(); + $this->assertEquals(1000, $result->getWidth()); + $this->assertEquals(750, $result->getHeight()); + + // 800x600 > 2000x1000 = 1333x1000 + $size = new Size(800, 600); + $resizer = new Resizer($size); + $resizer->width(2000); + $resizer->height(1000); + $result = $resizer->scale(); + $this->assertEquals(1333, $result->getWidth()); + $this->assertEquals(1000, $result->getHeight()); + + // // 800x600 > nullx3000 = 4000x3000 + $size = new Size(800, 600); + $resizer = new Resizer($size); + $resizer->height(3000); + $result = $resizer->scale(); + $this->assertEquals(4000, $result->getWidth()); + $this->assertEquals(3000, $result->getHeight()); + + // // 800x600 > 8000xnull = 8000x6000 + $size = new Size(800, 600); + $resizer = new Resizer($size); + $resizer->width(8000); + $result = $resizer->scale(); + $this->assertEquals(8000, $result->getWidth()); + $this->assertEquals(6000, $result->getHeight()); + + // // 800x600 > 100x400 = 100x75 + $size = new Size(800, 600); + $resizer = new Resizer($size); + $resizer->width(100); + $resizer->height(400); + $result = $resizer->scale(); + $this->assertEquals(100, $result->getWidth()); + $this->assertEquals(75, $result->getHeight()); + + // // 800x600 > 400x100 = 133x100 + $size = new Size(800, 600); + $resizer = new Resizer($size); + $resizer->width(400); + $resizer->height(100); + $result = $resizer->scale(); + $this->assertEquals(133, $result->getWidth()); + $this->assertEquals(100, $result->getHeight()); + + // // 800x600 > nullx300 = 400x300 + $size = new Size(800, 600); + $resizer = new Resizer($size); + $resizer->height(300); + $result = $resizer->scale(); + $this->assertEquals(400, $result->getWidth()); + $this->assertEquals(300, $result->getHeight()); + + // // 800x600 > 80xnull = 80x60 + $size = new Size(800, 600); + $resizer = new Resizer($size); + $resizer->width(80); + $result = $resizer->scale(); + $this->assertEquals(80, $result->getWidth()); + $this->assertEquals(60, $result->getHeight()); + + // // 640x480 > 225xnull = 225x169 + $size = new Size(640, 480); + $resizer = new Resizer($size); + $resizer->width(225); + $result = $resizer->scale(); + $this->assertEquals(225, $result->getWidth()); + $this->assertEquals(169, $result->getHeight()); + + // // 640x480 > 223xnull = 223x167 + $size = new Size(640, 480); + $resizer = new Resizer($size); + $resizer->width(223); + $result = $resizer->scale(); + $this->assertEquals(223, $result->getWidth()); + $this->assertEquals(167, $result->getHeight()); + + // // 600x800 > 300x300 = 225x300 + $size = new Size(600, 800); + $resizer = new Resizer($size); + $resizer->width(300); + $resizer->height(300); + $result = $resizer->scale(); + $this->assertEquals(225, $result->getWidth()); + $this->assertEquals(300, $result->getHeight()); + + // // 800x600 > 400x10 = 13x10 + $size = new Size(800, 600); + $resizer = new Resizer($size); + $resizer->width(400); + $resizer->height(10); + $result = $resizer->scale(); + $this->assertEquals(13, $result->getWidth()); + $this->assertEquals(10, $result->getHeight()); + + // // 800x600 > 1000x1200 = 1000x750 + $size = new Size(800, 600); + $resizer = new Resizer($size); + $resizer->width(1000); + $resizer->height(1200); + $result = $resizer->scale(); + $this->assertEquals(1000, $result->getWidth()); + $this->assertEquals(750, $result->getHeight()); + + $size = new Size(12000, 12); + $resizer = new Resizer($size); + $resizer->width(4000); + $resizer->height(3000); + $result = $resizer->scale(); + $this->assertEquals(4000, $result->getWidth()); + $this->assertEquals(4, $result->getHeight()); + + $size = new Size(12, 12000); + $resizer = new Resizer($size); + $resizer->width(4000); + $resizer->height(3000); + $result = $resizer->scale(); + $this->assertEquals(3, $result->getWidth()); + $this->assertEquals(3000, $result->getHeight()); + + $size = new Size(12000, 6000); + $resizer = new Resizer($size); + $resizer->width(4000); + $resizer->height(3000); + $result = $resizer->scale(); + $this->assertEquals(4000, $result->getWidth()); + $this->assertEquals(2000, $result->getHeight()); + } + + public function testScaleDown() + { + $size = new Size(800, 600); + $resizer = new Resizer($size); + $resizer->width(1000); + $resizer->height(2000); + $result = $resizer->scaleDown(); + $this->assertEquals(800, $result->getWidth()); + $this->assertEquals(600, $result->getHeight()); + + $size = new Size(800, 600); + $resizer = new Resizer($size); + $resizer->width(1000); + $resizer->height(600); + $result = $resizer->scaleDown(); + $this->assertEquals(800, $result->getWidth()); + $this->assertEquals(600, $result->getHeight()); + + $size = new Size(800, 600); + $resizer = new Resizer($size); + $resizer->width(1000); + $resizer->height(300); + $result = $resizer->scaleDown(); + $this->assertEquals(400, $result->getWidth()); + $this->assertEquals(300, $result->getHeight()); + + $size = new Size(800, 600); + $resizer = new Resizer($size); + $resizer->width(400); + $resizer->height(1000); + $result = $resizer->scaleDown(); + $this->assertEquals(400, $result->getWidth()); + $this->assertEquals(300, $result->getHeight()); + + $size = new Size(800, 600); + $resizer = new Resizer($size); + $resizer->width(400); + $result = $resizer->scaleDown(); + $this->assertEquals(400, $result->getWidth()); + $this->assertEquals(300, $result->getHeight()); + + $size = new Size(800, 600); + $resizer = new Resizer($size); + $resizer->height(300); + $result = $resizer->scaleDown(); + $this->assertEquals(400, $result->getWidth()); + $this->assertEquals(300, $result->getHeight()); + + $size = new Size(800, 600); + $resizer = new Resizer($size); + $resizer->width(1000); + $result = $resizer->scaleDown(); + $this->assertEquals(800, $result->getWidth()); + $this->assertEquals(600, $result->getHeight()); + + $size = new Size(800, 600); + $resizer = new Resizer($size); + $resizer->height(1000); + $result = $resizer->scaleDown(); + $this->assertEquals(800, $result->getWidth()); + $this->assertEquals(600, $result->getHeight()); + + $size = new Size(800, 600); + $resizer = new Resizer($size); + $resizer->width(100); + $result = $resizer->scaleDown(); + $this->assertEquals(100, $result->getWidth()); + $this->assertEquals(75, $result->getHeight()); + + $size = new Size(800, 600); + $resizer = new Resizer($size); + $resizer->width(300); + $resizer->height(200); + $result = $resizer->scaleDown(); + $this->assertEquals(267, $result->getWidth()); + $this->assertEquals(200, $result->getHeight()); + + $size = new Size(600, 800); + $resizer = new Resizer($size); + $resizer->width(300); + $resizer->height(300); + $result = $resizer->scaleDown(); + $this->assertEquals(225, $result->getWidth()); + $this->assertEquals(300, $result->getHeight()); + + $size = new Size(800, 600); + $resizer = new Resizer($size); + $resizer->width(400); + $resizer->height(10); + $result = $resizer->scaleDown(); + $this->assertEquals(13, $result->getWidth()); + $this->assertEquals(10, $result->getHeight()); + } +} From e351b6868326138b2735d62b4a69cc0b0b60c289 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 30 Oct 2021 12:16:23 +0200 Subject: [PATCH 017/476] Bugfix --- src/Drivers/Gd/Image.php | 9 +++++++++ src/Drivers/Imagick/Modifiers/GreyscaleModifier.php | 4 ++-- tests/Drivers/Gd/ImageTest.php | 7 +++++++ 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/Drivers/Gd/Image.php b/src/Drivers/Gd/Image.php index 751f4d4b..c3fde6eb 100644 --- a/src/Drivers/Gd/Image.php +++ b/src/Drivers/Gd/Image.php @@ -6,6 +6,7 @@ use GdImage; use Intervention\Image\Collection; use Intervention\Image\Drivers\Abstract\AbstractImage; use Intervention\Image\Drivers\Gd\Frame; +use Intervention\Image\Geometry\Resizer; use Intervention\Image\Geometry\Size; use Intervention\Image\Interfaces\EncoderInterface; use Intervention\Image\Interfaces\ImageInterface; @@ -23,4 +24,12 @@ class Image extends AbstractImage implements ImageInterface, IteratorAggregate { return imagesy($this->frames->first()->getCore()); } + + public function resize(...$arguments): self + { + $resizer = new Resizer($this->size()); + $resizer->setTargetSizeByArray($arguments)->resize(); + + return $this; + } } diff --git a/src/Drivers/Imagick/Modifiers/GreyscaleModifier.php b/src/Drivers/Imagick/Modifiers/GreyscaleModifier.php index d3452955..f5e65282 100644 --- a/src/Drivers/Imagick/Modifiers/GreyscaleModifier.php +++ b/src/Drivers/Imagick/Modifiers/GreyscaleModifier.php @@ -9,10 +9,10 @@ class GreyscaleModifier implements ModifierInterface { public function apply(ImageInterface $image): ImageInterface { - foreach ($this->image as $frame) { + foreach ($image as $frame) { $frame->getCore()->modulateImage(100, 0, 100); } - return $this->image; + return $image; } } diff --git a/tests/Drivers/Gd/ImageTest.php b/tests/Drivers/Gd/ImageTest.php index db5ddb78..b2275680 100644 --- a/tests/Drivers/Gd/ImageTest.php +++ b/tests/Drivers/Gd/ImageTest.php @@ -43,4 +43,11 @@ class ImageTest extends TestCase { $this->assertInstanceOf(Size::class, $this->image->size()); } + + public function testResize(): void + { + $this->assertInstanceOf(Image::class, $this->image->resize(function ($size) { + $size->width(300); + })); + } } From c71256a0a695a87ee5789f31fde0394b4e33e2a3 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 30 Oct 2021 17:06:19 +0200 Subject: [PATCH 018/476] Fixed Imagick Frame content --- src/Drivers/Imagick/Decoders/BinaryImageDecoder.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Drivers/Imagick/Decoders/BinaryImageDecoder.php b/src/Drivers/Imagick/Decoders/BinaryImageDecoder.php index 76145fba..1b931e53 100644 --- a/src/Drivers/Imagick/Decoders/BinaryImageDecoder.php +++ b/src/Drivers/Imagick/Decoders/BinaryImageDecoder.php @@ -27,7 +27,9 @@ class BinaryImageDecoder extends AbstractDecoder implements DecoderInterface $image->setLoops($imagick->getImageIterations()); foreach ($imagick as $frame_content) { - $image->addFrame(new Frame($frame_content)); + $image->addFrame( + new Frame($frame_content->getImage()) + ); } return $image; From b0731c89d7a74def0491bfec3c1bbc255fb1f2eb Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 30 Oct 2021 17:14:13 +0200 Subject: [PATCH 019/476] ResizeModifier --- src/Drivers/Abstract/AbstractImage.php | 7 ++ src/Drivers/Gd/Modifiers/ResizeModifier.php | 80 +++++++++++++++++++++ 2 files changed, 87 insertions(+) create mode 100644 src/Drivers/Gd/Modifiers/ResizeModifier.php diff --git a/src/Drivers/Abstract/AbstractImage.php b/src/Drivers/Abstract/AbstractImage.php index a01593ef..de1464f3 100644 --- a/src/Drivers/Abstract/AbstractImage.php +++ b/src/Drivers/Abstract/AbstractImage.php @@ -101,4 +101,11 @@ abstract class AbstractImage $this->resolveDriverClass('Modifiers\BlurModifier', $amount) ); } + + public function resize(int $width, int $height): ImageInterface + { + return $this->modify( + $this->resolveDriverClass('Modifiers\ResizeModifier', $width, $height) + ); + } } diff --git a/src/Drivers/Gd/Modifiers/ResizeModifier.php b/src/Drivers/Gd/Modifiers/ResizeModifier.php new file mode 100644 index 00000000..01ee3b96 --- /dev/null +++ b/src/Drivers/Gd/Modifiers/ResizeModifier.php @@ -0,0 +1,80 @@ +width = $width; + $this->height = $height; + } + + public function apply(ImageInterface $image): ImageInterface + { + foreach ($image as $frame) { + $this->modify($frame->getCore(), 0, 0, 0, 0, $this->width, $this->height, $image->getWidth(), $image->getHeight()); + } + + return $image; + } + + /** + * Wrapper function for 'imagecopyresampled' + * + * @param Image $image + * @param int $dst_x + * @param int $dst_y + * @param int $src_x + * @param int $src_y + * @param int $dst_w + * @param int $dst_h + * @param int $src_w + * @param int $src_h + * @return boolean + */ + protected function modify($gd, $dst_x, $dst_y, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h) + { + // create new image + $modified = imagecreatetruecolor($dst_w, $dst_h); + + // preserve transparency + $transIndex = imagecolortransparent($gd); + + if ($transIndex != -1) { + $rgba = imagecolorsforindex($modified, $transIndex); + $transColor = imagecolorallocatealpha($modified, $rgba['red'], $rgba['green'], $rgba['blue'], 127); + imagefill($modified, 0, 0, $transColor); + imagecolortransparent($modified, $transColor); + } else { + imagealphablending($modified, false); + imagesavealpha($modified, true); + } + + // copy content from gd + $result = imagecopyresampled( + $modified, + $gd, + $dst_x, + $dst_y, + $src_x, + $src_y, + $dst_w, + $dst_h, + $src_w, + $src_h + ); + + // set new content as recource + $image->setCore($modified); + + return $result; + } +} From d98f9069292980ad5f8104b7e6903baa0b091bbe Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 30 Oct 2021 17:30:23 +0200 Subject: [PATCH 020/476] ResizeModifier --- src/Drivers/Gd/Frame.php | 7 +++++++ src/Drivers/Gd/Image.php | 8 -------- src/Drivers/Gd/Modifiers/ResizeModifier.php | 14 ++++++++------ 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/src/Drivers/Gd/Frame.php b/src/Drivers/Gd/Frame.php index 0d3e0c64..76803cb8 100644 --- a/src/Drivers/Gd/Frame.php +++ b/src/Drivers/Gd/Frame.php @@ -50,6 +50,13 @@ class Frame extends AbstractFrame implements FrameInterface return $this->core; } + public function setCore(GdImage $core): self + { + $this->core = $core; + + return $this; + } + public function getSize(): SizeInterface { return new Size(imagesx($this->core), imagesy($this->core)); diff --git a/src/Drivers/Gd/Image.php b/src/Drivers/Gd/Image.php index c3fde6eb..be11857e 100644 --- a/src/Drivers/Gd/Image.php +++ b/src/Drivers/Gd/Image.php @@ -24,12 +24,4 @@ class Image extends AbstractImage implements ImageInterface, IteratorAggregate { return imagesy($this->frames->first()->getCore()); } - - public function resize(...$arguments): self - { - $resizer = new Resizer($this->size()); - $resizer->setTargetSizeByArray($arguments)->resize(); - - return $this; - } } diff --git a/src/Drivers/Gd/Modifiers/ResizeModifier.php b/src/Drivers/Gd/Modifiers/ResizeModifier.php index 01ee3b96..6805129c 100644 --- a/src/Drivers/Gd/Modifiers/ResizeModifier.php +++ b/src/Drivers/Gd/Modifiers/ResizeModifier.php @@ -20,7 +20,8 @@ class ResizeModifier implements ModifierInterface public function apply(ImageInterface $image): ImageInterface { foreach ($image as $frame) { - $this->modify($frame->getCore(), 0, 0, 0, 0, $this->width, $this->height, $image->getWidth(), $image->getHeight()); + $framesize = $frame->getSize(); + $this->modify($frame, 0, 0, 0, 0, $this->width, $this->height, $framesize->getWidth(), $framesize->getHeight()); } return $image; @@ -40,11 +41,14 @@ class ResizeModifier implements ModifierInterface * @param int $src_h * @return boolean */ - protected function modify($gd, $dst_x, $dst_y, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h) + protected function modify(Frame $frame, $dst_x, $dst_y, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h) { // create new image $modified = imagecreatetruecolor($dst_w, $dst_h); + // get current image + $gd = $frame->getCore(); + // preserve transparency $transIndex = imagecolortransparent($gd); @@ -58,7 +62,7 @@ class ResizeModifier implements ModifierInterface imagesavealpha($modified, true); } - // copy content from gd + // copy content from resource $result = imagecopyresampled( $modified, $gd, @@ -73,8 +77,6 @@ class ResizeModifier implements ModifierInterface ); // set new content as recource - $image->setCore($modified); - - return $result; + $frame->setCore($modified); } } From bab6cef4ae8f21f7538da77a3d078b748f98a6ef Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 30 Oct 2021 17:48:34 +0200 Subject: [PATCH 021/476] ResizeModifier --- src/Drivers/Gd/Modifiers/ResizeModifier.php | 4 ++- .../Imagick/Modifiers/ResizeModifier.php | 28 +++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 src/Drivers/Imagick/Modifiers/ResizeModifier.php diff --git a/src/Drivers/Gd/Modifiers/ResizeModifier.php b/src/Drivers/Gd/Modifiers/ResizeModifier.php index 6805129c..ea84aca4 100644 --- a/src/Drivers/Gd/Modifiers/ResizeModifier.php +++ b/src/Drivers/Gd/Modifiers/ResizeModifier.php @@ -41,7 +41,7 @@ class ResizeModifier implements ModifierInterface * @param int $src_h * @return boolean */ - protected function modify(Frame $frame, $dst_x, $dst_y, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h) + protected function modify(FrameInterface $frame, $dst_x, $dst_y, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h) { // create new image $modified = imagecreatetruecolor($dst_w, $dst_h); @@ -76,6 +76,8 @@ class ResizeModifier implements ModifierInterface $src_h ); + imagedestroy($gd); + // set new content as recource $frame->setCore($modified); } diff --git a/src/Drivers/Imagick/Modifiers/ResizeModifier.php b/src/Drivers/Imagick/Modifiers/ResizeModifier.php new file mode 100644 index 00000000..22e7f2f1 --- /dev/null +++ b/src/Drivers/Imagick/Modifiers/ResizeModifier.php @@ -0,0 +1,28 @@ +width = $width; + $this->height = $height; + } + + public function apply(ImageInterface $image): ImageInterface + { + foreach ($image as $frame) { + $frame->getCore()->scaleImage($this->width, $this->height); + } + + return $image; + } +} From f4b2f3eeb2ff72027712fc3f92ae38f4ed912fe2 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 30 Oct 2021 17:51:16 +0200 Subject: [PATCH 022/476] Bugfix --- src/Drivers/Imagick/Modifiers/BlurModifier.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Drivers/Imagick/Modifiers/BlurModifier.php b/src/Drivers/Imagick/Modifiers/BlurModifier.php index f22b5f01..eb4fefa1 100644 --- a/src/Drivers/Imagick/Modifiers/BlurModifier.php +++ b/src/Drivers/Imagick/Modifiers/BlurModifier.php @@ -19,10 +19,10 @@ class BlurModifier implements ModifierInterface public function apply(ImageInterface $image): ImageInterface { - foreach ($this->image as $frame) { + foreach ($image as $frame) { $frame->getCore()->blurImage(1 * $this->amount, 0.5 * $this->amount); } - return $this->image; + return $image; } } From a2da08af9b4529f04c73fe92808c75be808bd074 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 30 Oct 2021 17:53:44 +0200 Subject: [PATCH 023/476] DeconstructImages for Imagick gif encoding --- src/Drivers/Imagick/Encoders/GifEncoder.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Drivers/Imagick/Encoders/GifEncoder.php b/src/Drivers/Imagick/Encoders/GifEncoder.php index 1998edfb..fa846238 100644 --- a/src/Drivers/Imagick/Encoders/GifEncoder.php +++ b/src/Drivers/Imagick/Encoders/GifEncoder.php @@ -24,6 +24,7 @@ class GifEncoder extends AbstractEncoder implements EncoderInterface $gif->setImageFormat($format); $gif->setCompression($compression); $gif->setImageCompression($compression); + $gif = $gif->deconstructImages(); return $gif->getImagesBlob(); } From f48a206fdfedab1173db6fc99408eed42708465c Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 30 Oct 2021 18:10:49 +0200 Subject: [PATCH 024/476] resize methods --- src/Drivers/Abstract/AbstractImage.php | 39 ++++++++++++++++++- src/Drivers/Gd/Modifiers/ResizeModifier.php | 26 ++++++++++--- .../Imagick/Modifiers/ResizeModifier.php | 19 ++++++--- 3 files changed, 70 insertions(+), 14 deletions(-) diff --git a/src/Drivers/Abstract/AbstractImage.php b/src/Drivers/Abstract/AbstractImage.php index de1464f3..c643c5e5 100644 --- a/src/Drivers/Abstract/AbstractImage.php +++ b/src/Drivers/Abstract/AbstractImage.php @@ -5,6 +5,7 @@ namespace Intervention\Image\Drivers\Abstract; use Intervention\Image\Collection; use Intervention\Image\EncodedImage; use Intervention\Image\Exceptions\NotWritableException; +use Intervention\Image\Geometry\Resizer; use Intervention\Image\Geometry\Size; use Intervention\Image\Interfaces\EncoderInterface; use Intervention\Image\Interfaces\FrameInterface; @@ -59,6 +60,11 @@ abstract class AbstractImage return new Size($this->width(), $this->height()); } + public function getResizer(): Resizer + { + return new Resizer($this->size()); + } + public function isAnimated(): bool { return $this->getFrames()->count() > 1; @@ -102,10 +108,39 @@ abstract class AbstractImage ); } - public function resize(int $width, int $height): ImageInterface + public function resize(...$arguments): ImageInterface { + $size = $this->getResizer()->setTargetSizeByArray($arguments)->resize(); + return $this->modify( - $this->resolveDriverClass('Modifiers\ResizeModifier', $width, $height) + $this->resolveDriverClass('Modifiers\ResizeModifier', $size) + ); + } + + public function resizeDown(...$arguments): ImageInterface + { + $size = $this->getResizer()->setTargetSizeByArray($arguments)->resizeDown(); + + return $this->modify( + $this->resolveDriverClass('Modifiers\ResizeModifier', $size) + ); + } + + public function scale(...$arguments): ImageInterface + { + $size = $this->getResizer()->setTargetSizeByArray($arguments)->scale(); + + return $this->modify( + $this->resolveDriverClass('Modifiers\ResizeModifier', $size) + ); + } + + public function scaleDown(...$arguments): ImageInterface + { + $size = $this->getResizer()->setTargetSizeByArray($arguments)->scaleDown(); + + return $this->modify( + $this->resolveDriverClass('Modifiers\ResizeModifier', $size) ); } } diff --git a/src/Drivers/Gd/Modifiers/ResizeModifier.php b/src/Drivers/Gd/Modifiers/ResizeModifier.php index ea84aca4..673fe9fc 100644 --- a/src/Drivers/Gd/Modifiers/ResizeModifier.php +++ b/src/Drivers/Gd/Modifiers/ResizeModifier.php @@ -5,23 +5,37 @@ namespace Intervention\Image\Drivers\Gd\Modifiers; use Intervention\Image\Interfaces\FrameInterface; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; +use Intervention\Image\Interfaces\SizeInterface; class ResizeModifier implements ModifierInterface { - protected $width; - protected $height; + /** + * Target size + * + * @var SizeInterface + */ + protected $target; - public function __construct(int $width, int $height) + public function __construct(SizeInterface $target) { - $this->width = $width; - $this->height = $height; + $this->target = $target; } public function apply(ImageInterface $image): ImageInterface { foreach ($image as $frame) { $framesize = $frame->getSize(); - $this->modify($frame, 0, 0, 0, 0, $this->width, $this->height, $framesize->getWidth(), $framesize->getHeight()); + $this->modify( + $frame, + 0, + 0, + 0, + 0, + $this->target->getWidth(), + $this->target->getHeight(), + $framesize->getWidth(), + $framesize->getHeight() + ); } return $image; diff --git a/src/Drivers/Imagick/Modifiers/ResizeModifier.php b/src/Drivers/Imagick/Modifiers/ResizeModifier.php index 22e7f2f1..ee9de755 100644 --- a/src/Drivers/Imagick/Modifiers/ResizeModifier.php +++ b/src/Drivers/Imagick/Modifiers/ResizeModifier.php @@ -5,22 +5,29 @@ namespace Intervention\Image\Drivers\Imagick\Modifiers; use Intervention\Image\Interfaces\FrameInterface; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; +use Intervention\Image\Interfaces\SizeInterface; class ResizeModifier implements ModifierInterface { - protected $width; - protected $height; + /** + * Target size + * + * @var SizeInterface + */ + protected $target; - public function __construct(int $width, int $height) + public function __construct(SizeInterface $target) { - $this->width = $width; - $this->height = $height; + $this->target = $target; } public function apply(ImageInterface $image): ImageInterface { foreach ($image as $frame) { - $frame->getCore()->scaleImage($this->width, $this->height); + $frame->getCore()->scaleImage( + $this->target->getWidth(), + $this->target->getHeight() + ); } return $image; From 8c2766bcba3c861dbede9afbfef2260b93329f05 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 30 Oct 2021 16:29:05 +0000 Subject: [PATCH 025/476] Encoding --- src/Drivers/Abstract/AbstractImage.php | 10 +++++----- src/Drivers/Gd/Encoders/GifEncoder.php | 13 ++++++++----- src/Drivers/Gd/Encoders/JpegEncoder.php | 7 +++++-- src/Drivers/Imagick/Encoders/GifEncoder.php | 5 +++-- src/Drivers/Imagick/Encoders/JpegEncoder.php | 5 +++-- src/EncodedImage.php | 5 +++-- src/Interfaces/EncoderInterface.php | 4 +++- src/Interfaces/ImageInterface.php | 2 +- tests/Drivers/Gd/ImageTest.php | 11 ++--------- tests/Drivers/Imagick/ImageTest.php | 4 ++-- 10 files changed, 35 insertions(+), 31 deletions(-) diff --git a/src/Drivers/Abstract/AbstractImage.php b/src/Drivers/Abstract/AbstractImage.php index c643c5e5..3d682bf7 100644 --- a/src/Drivers/Abstract/AbstractImage.php +++ b/src/Drivers/Abstract/AbstractImage.php @@ -55,14 +55,14 @@ abstract class AbstractImage return $this->loops; } - public function size(): SizeInterface + public function getSize(): SizeInterface { return new Size($this->width(), $this->height()); } public function getResizer(): Resizer { - return new Resizer($this->size()); + return new Resizer($this->getSize()); } public function isAnimated(): bool @@ -77,17 +77,17 @@ abstract class AbstractImage public function encode(EncoderInterface $encoder): EncodedImage { - return new EncodedImage($encoder->encode($this)); + return $encoder->encode($this); } - public function toJpeg(?int $quality = null): string + public function toJpeg(?int $quality = null): EncodedImage { return $this->encode( $this->resolveDriverClass('Encoders\JpegEncoder', $quality) ); } - public function toGif(): string + public function toGif(): EncodedImage { return $this->encode( $this->resolveDriverClass('Encoders\GifEncoder') diff --git a/src/Drivers/Gd/Encoders/GifEncoder.php b/src/Drivers/Gd/Encoders/GifEncoder.php index 47dbb7cb..99d37001 100644 --- a/src/Drivers/Gd/Encoders/GifEncoder.php +++ b/src/Drivers/Gd/Encoders/GifEncoder.php @@ -2,25 +2,28 @@ namespace Intervention\Image\Drivers\Gd\Encoders; +use Intervention\Gif\Builder as GifBuilder; use Intervention\Image\Drivers\Abstract\Encoders\AbstractEncoder; +use Intervention\Image\EncodedImage; use Intervention\Image\Interfaces\EncoderInterface; use Intervention\Image\Interfaces\ImageInterface; -use Intervention\Gif\Builder as GifBuilder; class GifEncoder extends AbstractEncoder implements EncoderInterface { - public function encode(ImageInterface $image): string + public function encode(ImageInterface $image): EncodedImage { if ($image->isAnimated()) { return $this->encodeAnimated($image); } - return $this->getBuffered(function () use ($image) { + $data = $this->getBuffered(function () use ($image) { imagegif($image->getFrames()->first()->getCore()); }); + + return new EncodedImage($data, 'image/gif'); } - protected function encodeAnimated($image): string + protected function encodeAnimated($image): EncodedImage { $builder = GifBuilder::canvas($image->width(), $image->height(), $image->loops()); foreach ($image as $key => $frame) { @@ -28,6 +31,6 @@ class GifEncoder extends AbstractEncoder implements EncoderInterface $builder->addFrame($source, $frame->getDelay()); } - return $builder->encode(); + return new EncodedImage($builder->encode(), 'image/gif'); } } diff --git a/src/Drivers/Gd/Encoders/JpegEncoder.php b/src/Drivers/Gd/Encoders/JpegEncoder.php index 408c7b95..02a3e969 100644 --- a/src/Drivers/Gd/Encoders/JpegEncoder.php +++ b/src/Drivers/Gd/Encoders/JpegEncoder.php @@ -3,15 +3,18 @@ namespace Intervention\Image\Drivers\Gd\Encoders; use Intervention\Image\Drivers\Abstract\Encoders\AbstractEncoder; +use Intervention\Image\EncodedImage; use Intervention\Image\Interfaces\EncoderInterface; use Intervention\Image\Interfaces\ImageInterface; class JpegEncoder extends AbstractEncoder implements EncoderInterface { - public function encode(ImageInterface $image): string + public function encode(ImageInterface $image): EncodedImage { - return $this->getBuffered(function () use ($image) { + $data = $this->getBuffered(function () use ($image) { imagejpeg($image->getFrames()->first()->getCore(), null, $this->quality); }); + + return new EncodedImage($data, 'image/jpeg'); } } diff --git a/src/Drivers/Imagick/Encoders/GifEncoder.php b/src/Drivers/Imagick/Encoders/GifEncoder.php index fa846238..f795f174 100644 --- a/src/Drivers/Imagick/Encoders/GifEncoder.php +++ b/src/Drivers/Imagick/Encoders/GifEncoder.php @@ -4,12 +4,13 @@ namespace Intervention\Image\Drivers\Imagick\Encoders; use Imagick; use Intervention\Image\Drivers\Abstract\Encoders\AbstractEncoder; +use Intervention\Image\EncodedImage; use Intervention\Image\Interfaces\EncoderInterface; use Intervention\Image\Interfaces\ImageInterface; class GifEncoder extends AbstractEncoder implements EncoderInterface { - public function encode(ImageInterface $image): string + public function encode(ImageInterface $image): EncodedImage { $format = 'gif'; $compression = Imagick::COMPRESSION_LZW; @@ -26,6 +27,6 @@ class GifEncoder extends AbstractEncoder implements EncoderInterface $gif->setImageCompression($compression); $gif = $gif->deconstructImages(); - return $gif->getImagesBlob(); + return new EncodedImage($gif->getImagesBlob(), 'image/gif'); } } diff --git a/src/Drivers/Imagick/Encoders/JpegEncoder.php b/src/Drivers/Imagick/Encoders/JpegEncoder.php index 179531d7..45fd7220 100644 --- a/src/Drivers/Imagick/Encoders/JpegEncoder.php +++ b/src/Drivers/Imagick/Encoders/JpegEncoder.php @@ -4,12 +4,13 @@ namespace Intervention\Image\Drivers\Imagick\Encoders; use Imagick; use Intervention\Image\Drivers\Abstract\Encoders\AbstractEncoder; +use Intervention\Image\EncodedImage; use Intervention\Image\Interfaces\EncoderInterface; use Intervention\Image\Interfaces\ImageInterface; class JpegEncoder extends AbstractEncoder implements EncoderInterface { - public function encode(ImageInterface $image): string + public function encode(ImageInterface $image): EncodedImage { $format = 'jpeg'; $compression = Imagick::COMPRESSION_JPEG; @@ -24,6 +25,6 @@ class JpegEncoder extends AbstractEncoder implements EncoderInterface $imagick->setCompressionQuality($this->quality); $imagick->setImageCompressionQuality($this->quality); - return $imagick->getImagesBlob(); + return new EncodedImage($imagick->getImagesBlob(), 'image/jpeg'); } } diff --git a/src/EncodedImage.php b/src/EncodedImage.php index fa01f656..7f8d0848 100644 --- a/src/EncodedImage.php +++ b/src/EncodedImage.php @@ -9,9 +9,10 @@ class EncodedImage protected $data; protected $mimetype; - public function __construct(string $data) + public function __construct(string $data, string $mimetype = 'application/octet-stream') { $this->data = $data; + $this->mimetype = $mimetype; } public function save(string $filepath): void @@ -26,7 +27,7 @@ class EncodedImage public function toDataUrl(): string { - return ''; + return sprintf('data:%s;base64,%s', $this->mimetype, base64_encode($this->data)); } public function __toString(): string diff --git a/src/Interfaces/EncoderInterface.php b/src/Interfaces/EncoderInterface.php index 03163d22..c2d9c41c 100644 --- a/src/Interfaces/EncoderInterface.php +++ b/src/Interfaces/EncoderInterface.php @@ -2,7 +2,9 @@ namespace Intervention\Image\Interfaces; +use Intervention\Image\EncodedImage; + interface EncoderInterface { - public function encode(ImageInterface $image): string; + public function encode(ImageInterface $image): EncodedImage; } diff --git a/src/Interfaces/ImageInterface.php b/src/Interfaces/ImageInterface.php index a7609d05..33231f2c 100644 --- a/src/Interfaces/ImageInterface.php +++ b/src/Interfaces/ImageInterface.php @@ -6,7 +6,7 @@ use Intervention\Image\EncodedImage; interface ImageInterface { - public function size(): SizeInterface; + public function getSize(): SizeInterface; public function width(): int; public function height(): int; public function isAnimated(): bool; diff --git a/tests/Drivers/Gd/ImageTest.php b/tests/Drivers/Gd/ImageTest.php index b2275680..0f966290 100644 --- a/tests/Drivers/Gd/ImageTest.php +++ b/tests/Drivers/Gd/ImageTest.php @@ -39,15 +39,8 @@ class ImageTest extends TestCase $this->assertEquals(2, $this->image->height()); } - public function testSize(): void + public function testGetSize(): void { - $this->assertInstanceOf(Size::class, $this->image->size()); - } - - public function testResize(): void - { - $this->assertInstanceOf(Image::class, $this->image->resize(function ($size) { - $size->width(300); - })); + $this->assertInstanceOf(Size::class, $this->image->getSize()); } } diff --git a/tests/Drivers/Imagick/ImageTest.php b/tests/Drivers/Imagick/ImageTest.php index 611825d0..a70666dc 100644 --- a/tests/Drivers/Imagick/ImageTest.php +++ b/tests/Drivers/Imagick/ImageTest.php @@ -44,8 +44,8 @@ class ImageTest extends TestCase $this->assertEquals(2, $this->image->height()); } - public function testSize(): void + public function testGetSize(): void { - $this->assertInstanceOf(Size::class, $this->image->size()); + $this->assertInstanceOf(Size::class, $this->image->getSize()); } } From e9a3e4a034f02192ba8863dd0727c6b199b6bbd9 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 30 Oct 2021 17:47:52 +0000 Subject: [PATCH 026/476] EncodedImage test --- src/EncodedImage.php | 2 +- tests/EncodedImageTest.php | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 tests/EncodedImageTest.php diff --git a/src/EncodedImage.php b/src/EncodedImage.php index 7f8d0848..7244048c 100644 --- a/src/EncodedImage.php +++ b/src/EncodedImage.php @@ -25,7 +25,7 @@ class EncodedImage } } - public function toDataUrl(): string + public function toDataUri(): string { return sprintf('data:%s;base64,%s', $this->mimetype, base64_encode($this->data)); } diff --git a/tests/EncodedImageTest.php b/tests/EncodedImageTest.php new file mode 100644 index 00000000..7e4ac637 --- /dev/null +++ b/tests/EncodedImageTest.php @@ -0,0 +1,37 @@ +assertInstanceOf(EncodedImage::class, $image); + } + + public function testSave(): void + { + $image = new EncodedImage('foo', 'bar'); + $path = __DIR__ . '/foo.tmp'; + $this->assertFalse(file_exists($path)); + $image->save($path); + $this->assertTrue(file_exists($path)); + $this->assertEquals('foo', file_get_contents($path)); + unlink($path); + } + + public function testToDataUri(): void + { + $image = new EncodedImage('foo', 'bar'); + $this->assertEquals('data:bar;base64,Zm9v', $image->toDataUri()); + } + + public function testToString(): void + { + $image = new EncodedImage('foo', 'bar'); + $this->assertEquals('foo', (string) $image); + } +} From f043cebdd2a4834a031bef54fbe043387ebcbba1 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 30 Oct 2021 18:05:32 +0000 Subject: [PATCH 027/476] Factory methods --- src/Drivers/Gd/ImageFactory.php | 3 +++ src/Drivers/Gd/Modifiers/FillModifier.php | 31 +++++++++++++++++++++++ src/Drivers/Imagick/ImageFactory.php | 26 +++++++++++++++++++ 3 files changed, 60 insertions(+) create mode 100644 src/Drivers/Gd/Modifiers/FillModifier.php create mode 100644 src/Drivers/Imagick/ImageFactory.php diff --git a/src/Drivers/Gd/ImageFactory.php b/src/Drivers/Gd/ImageFactory.php index 0127f36c..e5a0990c 100644 --- a/src/Drivers/Gd/ImageFactory.php +++ b/src/Drivers/Gd/ImageFactory.php @@ -11,6 +11,9 @@ class ImageFactory implements FactoryInterface public function newImage(int $width, int $height): ImageInterface { $gd = imagecreatetruecolor($width, $height); + $color = imagecolorallocatealpha($gd, 0, 0, 0, 127); + imagefill($gd, 0, 0, $color); + imagesavealpha($gd, true); return new Image(new Collection([ new Frame($gd) diff --git a/src/Drivers/Gd/Modifiers/FillModifier.php b/src/Drivers/Gd/Modifiers/FillModifier.php new file mode 100644 index 00000000..9135653b --- /dev/null +++ b/src/Drivers/Gd/Modifiers/FillModifier.php @@ -0,0 +1,31 @@ +filling = $filling; + } + + public function apply(ImageInterface $image): ImageInterface + { + foreach ($image as $frame) { + $this->blurFrame($frame); + } + + return $image; + } + + protected function blurFrame(FrameInterface $frame): void + { + for ($i = 0; $i < $this->amount; $i++) { + imagefilter($frame->getCore(), IMG_FILTER_GAUSSIAN_BLUR); + } + } +} diff --git a/src/Drivers/Imagick/ImageFactory.php b/src/Drivers/Imagick/ImageFactory.php new file mode 100644 index 00000000..3aaba6a7 --- /dev/null +++ b/src/Drivers/Imagick/ImageFactory.php @@ -0,0 +1,26 @@ +newImage($width, $height, new ImagickPixel('rgba(0, 0, 0, 0)'), 'png'); + $imagick->setType(Imagick::IMGTYPE_UNDEFINED); + $imagick->setImageType(Imagick::IMGTYPE_UNDEFINED); + $imagick->setColorspace(Imagick::COLORSPACE_UNDEFINED); + + return new Image(new Collection([ + new Frame($imagick) + ])); + } +} From 6d387e1c38e151617ec91aad2304109d7a75d091 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 30 Oct 2021 20:23:48 +0200 Subject: [PATCH 028/476] FillModifier --- src/Drivers/Abstract/AbstractImage.php | 7 ++++ src/Drivers/Gd/ImageFactory.php | 8 +++-- src/Drivers/Gd/Modifiers/FillModifier.php | 15 ++++---- src/Drivers/Imagick/Color.php | 5 +++ src/Drivers/Imagick/ImageFactory.php | 8 +++-- .../Imagick/Modifiers/FillModifier.php | 34 +++++++++++++++++++ 6 files changed, 65 insertions(+), 12 deletions(-) create mode 100644 src/Drivers/Imagick/Modifiers/FillModifier.php diff --git a/src/Drivers/Abstract/AbstractImage.php b/src/Drivers/Abstract/AbstractImage.php index 3d682bf7..df736482 100644 --- a/src/Drivers/Abstract/AbstractImage.php +++ b/src/Drivers/Abstract/AbstractImage.php @@ -143,4 +143,11 @@ abstract class AbstractImage $this->resolveDriverClass('Modifiers\ResizeModifier', $size) ); } + + public function fill($filling): ImageInterface + { + return $this->modify( + $this->resolveDriverClass('Modifiers\FillModifier', $filling) + ); + } } diff --git a/src/Drivers/Gd/ImageFactory.php b/src/Drivers/Gd/ImageFactory.php index e5a0990c..5e93a729 100644 --- a/src/Drivers/Gd/ImageFactory.php +++ b/src/Drivers/Gd/ImageFactory.php @@ -15,8 +15,10 @@ class ImageFactory implements FactoryInterface imagefill($gd, 0, 0, $color); imagesavealpha($gd, true); - return new Image(new Collection([ - new Frame($gd) - ])); + return new Image( + new Collection([ + new Frame($imagick) + ]) + ); } } diff --git a/src/Drivers/Gd/Modifiers/FillModifier.php b/src/Drivers/Gd/Modifiers/FillModifier.php index 9135653b..6c3ce2bc 100644 --- a/src/Drivers/Gd/Modifiers/FillModifier.php +++ b/src/Drivers/Gd/Modifiers/FillModifier.php @@ -2,30 +2,33 @@ namespace Intervention\Image\Drivers\Gd\Modifiers; +use Intervention\Image\Drivers\Gd\InputHandler; use Intervention\Image\Interfaces\FrameInterface; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; class FillModifier implements ModifierInterface { - public function __construct($filling, int $x, int $y) + public function __construct($filling, ?int $x = null, ?int $y = null) { $this->filling = $filling; } public function apply(ImageInterface $image): ImageInterface { + $width = $image->getWidth(); + $height = $image->getHeight(); + $filling = $this->getApplicableFilling(); + foreach ($image as $frame) { - $this->blurFrame($frame); + imagefilledrectangle($frame->getCore(), 0, 0, $width - 1, $height - 1, $filling); } return $image; } - protected function blurFrame(FrameInterface $frame): void + protected function getApplicableFilling() { - for ($i = 0; $i < $this->amount; $i++) { - imagefilter($frame->getCore(), IMG_FILTER_GAUSSIAN_BLUR); - } + return (new InputHandler())->handle($this->filling); } } diff --git a/src/Drivers/Imagick/Color.php b/src/Drivers/Imagick/Color.php index f07a82ca..904b1c7a 100644 --- a/src/Drivers/Imagick/Color.php +++ b/src/Drivers/Imagick/Color.php @@ -21,6 +21,11 @@ class Color extends AbstractColor implements ColorInterface $this->pixel = $pixel; } + public function getPixel(): ImagickPixel + { + return $this->pixel; + } + public function red(): int { return round($this->pixel->getColorValue(Imagick::COLOR_RED) * 255); diff --git a/src/Drivers/Imagick/ImageFactory.php b/src/Drivers/Imagick/ImageFactory.php index 3aaba6a7..7cd240c0 100644 --- a/src/Drivers/Imagick/ImageFactory.php +++ b/src/Drivers/Imagick/ImageFactory.php @@ -19,8 +19,10 @@ class ImageFactory implements FactoryInterface $imagick->setImageType(Imagick::IMGTYPE_UNDEFINED); $imagick->setColorspace(Imagick::COLORSPACE_UNDEFINED); - return new Image(new Collection([ - new Frame($imagick) - ])); + return new Image( + new Collection([ + new Frame($imagick) + ]) + ); } } diff --git a/src/Drivers/Imagick/Modifiers/FillModifier.php b/src/Drivers/Imagick/Modifiers/FillModifier.php new file mode 100644 index 00000000..3175bb8c --- /dev/null +++ b/src/Drivers/Imagick/Modifiers/FillModifier.php @@ -0,0 +1,34 @@ +filling = $filling; + } + + public function apply(ImageInterface $image): ImageInterface + { + $draw = new ImagickDraw(); + $draw->setFillColor($this->getApplicableFilling()->getPixel()); + $draw->rectangle(0, 0, $image->getWidth(), $image->getHeight()); + + foreach ($image as $frame) { + $frame->getCore()->drawImage($draw); + } + + return $image; + } + + protected function getApplicableFilling() + { + return (new InputHandler())->handle($this->filling); + } +} From 80c08fd989db8a9b5218e34e627a729b349ed775 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 30 Oct 2021 20:25:20 +0200 Subject: [PATCH 029/476] Bugfix --- src/Drivers/Gd/ImageFactory.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Drivers/Gd/ImageFactory.php b/src/Drivers/Gd/ImageFactory.php index 5e93a729..f68017e2 100644 --- a/src/Drivers/Gd/ImageFactory.php +++ b/src/Drivers/Gd/ImageFactory.php @@ -17,7 +17,7 @@ class ImageFactory implements FactoryInterface return new Image( new Collection([ - new Frame($imagick) + new Frame($gd) ]) ); } From d4eaea5a9e791e44d3fe27d81b7960b9ce4543fd Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 30 Oct 2021 20:33:45 +0200 Subject: [PATCH 030/476] Png Encoders --- src/Drivers/Abstract/AbstractImage.php | 7 ++++++ src/Drivers/Gd/Encoders/PngEncoder.php | 20 ++++++++++++++++ src/Drivers/Imagick/Encoders/PngEncoder.php | 26 +++++++++++++++++++++ 3 files changed, 53 insertions(+) create mode 100644 src/Drivers/Gd/Encoders/PngEncoder.php create mode 100644 src/Drivers/Imagick/Encoders/PngEncoder.php diff --git a/src/Drivers/Abstract/AbstractImage.php b/src/Drivers/Abstract/AbstractImage.php index df736482..1556c451 100644 --- a/src/Drivers/Abstract/AbstractImage.php +++ b/src/Drivers/Abstract/AbstractImage.php @@ -94,6 +94,13 @@ abstract class AbstractImage ); } + public function toPng(): EncodedImage + { + return $this->encode( + $this->resolveDriverClass('Encoders\PngEncoder') + ); + } + public function greyscale(): ImageInterface { return $this->modify( diff --git a/src/Drivers/Gd/Encoders/PngEncoder.php b/src/Drivers/Gd/Encoders/PngEncoder.php new file mode 100644 index 00000000..f93afb39 --- /dev/null +++ b/src/Drivers/Gd/Encoders/PngEncoder.php @@ -0,0 +1,20 @@ +getBuffered(function () use ($image) { + imagepng($image->getFrames()->first()->getCore()); + }); + + return new EncodedImage($data, 'image/png'); + } +} diff --git a/src/Drivers/Imagick/Encoders/PngEncoder.php b/src/Drivers/Imagick/Encoders/PngEncoder.php new file mode 100644 index 00000000..8bdc03e6 --- /dev/null +++ b/src/Drivers/Imagick/Encoders/PngEncoder.php @@ -0,0 +1,26 @@ +getFrames()->first()->getCore(); + $imagick->setFormat($format); + $imagick->setImageFormat($format); + $imagick->setCompression($compression); + $imagick->setImageCompression($compression); + + return new EncodedImage($imagick->getImagesBlob(), 'image/png'); + } +} From 4b7c182152c3c48da8a2e0dfd9573d21cb4a813e Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 31 Oct 2021 07:46:01 +0100 Subject: [PATCH 031/476] Color Decoder --- src/Drivers/Gd/Decoders/ArrayColorDecoder.php | 8 +++- src/Drivers/Gd/Decoders/HexColorDecoder.php | 27 +++++++++++++ src/Drivers/Gd/InputHandler.php | 10 +++-- src/Drivers/Gd/Modifiers/FillModifier.php | 10 ++--- .../Imagick/Decoders/ArrayColorDecoder.php | 4 +- .../Imagick/Decoders/HexColorDecoder.php | 27 +++++++++++++ src/Drivers/Imagick/InputHandler.php | 10 +++-- .../Imagick/Modifiers/FillModifier.php | 12 +++--- src/Traits/CanHandleInput.php | 11 +++++ ...teColorArray.php => CanValidateColors.php} | 4 +- tests/Drivers/Gd/InputHandlerTest.php | 40 +++++++++++++++++++ 11 files changed, 137 insertions(+), 26 deletions(-) create mode 100644 src/Drivers/Gd/Decoders/HexColorDecoder.php create mode 100644 src/Drivers/Imagick/Decoders/HexColorDecoder.php create mode 100644 src/Traits/CanHandleInput.php rename src/Traits/{CanValidateColorArray.php => CanValidateColors.php} (85%) diff --git a/src/Drivers/Gd/Decoders/ArrayColorDecoder.php b/src/Drivers/Gd/Decoders/ArrayColorDecoder.php index 7a1b7161..9f8d7a70 100644 --- a/src/Drivers/Gd/Decoders/ArrayColorDecoder.php +++ b/src/Drivers/Gd/Decoders/ArrayColorDecoder.php @@ -7,11 +7,11 @@ use Intervention\Image\Drivers\Gd\Color; use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\DecoderInterface; use Intervention\Image\Interfaces\ImageInterface; -use Intervention\Image\Traits\CanValidateColorArray; +use Intervention\Image\Traits\CanValidateColors; class ArrayColorDecoder extends AbstractDecoder implements DecoderInterface { - use CanValidateColorArray; + use CanValidateColors; public function decode($input): ImageInterface|ColorInterface { @@ -19,6 +19,10 @@ class ArrayColorDecoder extends AbstractDecoder implements DecoderInterface $this->fail(); } + if (count($input) === 3) { + $input[] = 1; + } + list($r, $g, $b, $a) = $input; return new Color( diff --git a/src/Drivers/Gd/Decoders/HexColorDecoder.php b/src/Drivers/Gd/Decoders/HexColorDecoder.php new file mode 100644 index 00000000..940c327e --- /dev/null +++ b/src/Drivers/Gd/Decoders/HexColorDecoder.php @@ -0,0 +1,27 @@ +fail(); + } + + return parent::decode([ + strlen($matches[1]) == '1' ? hexdec($matches[1] . $matches[1]) : hexdec($matches[1]), + strlen($matches[2]) == '1' ? hexdec($matches[2] . $matches[2]) : hexdec($matches[2]), + strlen($matches[3]) == '1' ? hexdec($matches[3] . $matches[3]) : hexdec($matches[3]), + ]); + } +} diff --git a/src/Drivers/Gd/InputHandler.php b/src/Drivers/Gd/InputHandler.php index 1e95b495..7950e73f 100644 --- a/src/Drivers/Gd/InputHandler.php +++ b/src/Drivers/Gd/InputHandler.php @@ -10,10 +10,12 @@ class InputHandler extends AbstractInputHandler protected function chain(): AbstractDecoder { return new Decoders\ArrayColorDecoder( - new Decoders\FilePathImageDecoder( - new Decoders\BinaryImageDecoder( - new Decoders\DataUriImageDecoder( - new Decoders\Base64ImageDecoder() + new Decoders\HexColorDecoder( + new Decoders\FilePathImageDecoder( + new Decoders\BinaryImageDecoder( + new Decoders\DataUriImageDecoder( + new Decoders\Base64ImageDecoder() + ) ) ) ) diff --git a/src/Drivers/Gd/Modifiers/FillModifier.php b/src/Drivers/Gd/Modifiers/FillModifier.php index 6c3ce2bc..2cf7fd54 100644 --- a/src/Drivers/Gd/Modifiers/FillModifier.php +++ b/src/Drivers/Gd/Modifiers/FillModifier.php @@ -6,9 +6,12 @@ use Intervention\Image\Drivers\Gd\InputHandler; use Intervention\Image\Interfaces\FrameInterface; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; +use Intervention\Image\Traits\CanHandleInput; class FillModifier implements ModifierInterface { + use CanHandleInput; + public function __construct($filling, ?int $x = null, ?int $y = null) { $this->filling = $filling; @@ -18,7 +21,7 @@ class FillModifier implements ModifierInterface { $width = $image->getWidth(); $height = $image->getHeight(); - $filling = $this->getApplicableFilling(); + $filling = $this->handleInput($this->filling); foreach ($image as $frame) { imagefilledrectangle($frame->getCore(), 0, 0, $width - 1, $height - 1, $filling); @@ -26,9 +29,4 @@ class FillModifier implements ModifierInterface return $image; } - - protected function getApplicableFilling() - { - return (new InputHandler())->handle($this->filling); - } } diff --git a/src/Drivers/Imagick/Decoders/ArrayColorDecoder.php b/src/Drivers/Imagick/Decoders/ArrayColorDecoder.php index e2110744..89f52eeb 100644 --- a/src/Drivers/Imagick/Decoders/ArrayColorDecoder.php +++ b/src/Drivers/Imagick/Decoders/ArrayColorDecoder.php @@ -8,11 +8,11 @@ use Intervention\Image\Drivers\Imagick\Color; use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\DecoderInterface; use Intervention\Image\Interfaces\ImageInterface; -use Intervention\Image\Traits\CanValidateColorArray; +use Intervention\Image\Traits\CanValidateColors; class ArrayColorDecoder extends AbstractDecoder implements DecoderInterface { - use CanValidateColorArray; + use CanValidateColors; public function decode($input): ImageInterface|ColorInterface { diff --git a/src/Drivers/Imagick/Decoders/HexColorDecoder.php b/src/Drivers/Imagick/Decoders/HexColorDecoder.php new file mode 100644 index 00000000..a0609b34 --- /dev/null +++ b/src/Drivers/Imagick/Decoders/HexColorDecoder.php @@ -0,0 +1,27 @@ +fail(); + } + + return parent::decode([ + strlen($matches[1]) == '1' ? hexdec($matches[1] . $matches[1]) : hexdec($matches[1]), + strlen($matches[2]) == '1' ? hexdec($matches[2] . $matches[2]) : hexdec($matches[2]), + strlen($matches[3]) == '1' ? hexdec($matches[3] . $matches[3]) : hexdec($matches[3]), + ]); + } +} diff --git a/src/Drivers/Imagick/InputHandler.php b/src/Drivers/Imagick/InputHandler.php index d1fcbde2..2062fa71 100644 --- a/src/Drivers/Imagick/InputHandler.php +++ b/src/Drivers/Imagick/InputHandler.php @@ -10,10 +10,12 @@ class InputHandler extends AbstractInputHandler protected function chain(): AbstractDecoder { return new Decoders\ArrayColorDecoder( - new Decoders\FilePathImageDecoder( - new Decoders\BinaryImageDecoder( - new Decoders\DataUriImageDecoder( - new Decoders\Base64ImageDecoder() + new Decoders\HexColorDecoder( + new Decoders\FilePathImageDecoder( + new Decoders\BinaryImageDecoder( + new Decoders\DataUriImageDecoder( + new Decoders\Base64ImageDecoder() + ) ) ) ) diff --git a/src/Drivers/Imagick/Modifiers/FillModifier.php b/src/Drivers/Imagick/Modifiers/FillModifier.php index 3175bb8c..81e20cfd 100644 --- a/src/Drivers/Imagick/Modifiers/FillModifier.php +++ b/src/Drivers/Imagick/Modifiers/FillModifier.php @@ -6,9 +6,12 @@ use ImagickDraw; use Intervention\Image\Drivers\Imagick\InputHandler; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; +use Intervention\Image\Traits\CanHandleInput; class FillModifier implements ModifierInterface { + use CanHandleInput; + public function __construct($filling, ?int $x = null, ?int $y = null) { $this->filling = $filling; @@ -16,8 +19,10 @@ class FillModifier implements ModifierInterface public function apply(ImageInterface $image): ImageInterface { + $filling = $this->handleInput($this->filling); + $draw = new ImagickDraw(); - $draw->setFillColor($this->getApplicableFilling()->getPixel()); + $draw->setFillColor($filling->getPixel()); $draw->rectangle(0, 0, $image->getWidth(), $image->getHeight()); foreach ($image as $frame) { @@ -26,9 +31,4 @@ class FillModifier implements ModifierInterface return $image; } - - protected function getApplicableFilling() - { - return (new InputHandler())->handle($this->filling); - } } diff --git a/src/Traits/CanHandleInput.php b/src/Traits/CanHandleInput.php new file mode 100644 index 00000000..3182e2af --- /dev/null +++ b/src/Traits/CanHandleInput.php @@ -0,0 +1,11 @@ +handle($value); + } +} diff --git a/src/Traits/CanValidateColorArray.php b/src/Traits/CanValidateColors.php similarity index 85% rename from src/Traits/CanValidateColorArray.php rename to src/Traits/CanValidateColors.php index f06dd940..99f14cb4 100644 --- a/src/Traits/CanValidateColorArray.php +++ b/src/Traits/CanValidateColors.php @@ -2,7 +2,7 @@ namespace Intervention\Image\Traits; -trait CanValidateColorArray +trait CanValidateColors { protected function isValidColorArray($input): bool { @@ -22,7 +22,7 @@ trait CanValidateColorArray } // validate alpha value - if ($input[3] > 1 || $input[3] < 0) { + if (isset($input[3]) && ($input[3] > 1 || $input[3] < 0)) { return false; } diff --git a/tests/Drivers/Gd/InputHandlerTest.php b/tests/Drivers/Gd/InputHandlerTest.php index f4d2d258..41fdf954 100644 --- a/tests/Drivers/Gd/InputHandlerTest.php +++ b/tests/Drivers/Gd/InputHandlerTest.php @@ -58,5 +58,45 @@ class InputHandlerTest extends TestCase $input = [181, 55, 23, .5]; $result = $handler->handle($input); $this->assertInstanceOf(Color::class, $result); + + $handler = new InputHandler(); + $input = [181, 55, 23]; + $result = $handler->handle($input); + $this->assertInstanceOf(Color::class, $result); + } + + public function testHandleHexColor(): void + { + $handler = new InputHandler(); + $input = 'ccff33'; + $result = $handler->handle($input); + $this->assertInstanceOf(Color::class, $result); + $this->assertEquals(204, $result->red()); + $this->assertEquals(255, $result->green()); + $this->assertEquals(51, $result->blue()); + + $handler = new InputHandler(); + $input = 'cf3'; + $result = $handler->handle($input); + $this->assertInstanceOf(Color::class, $result); + $this->assertEquals(204, $result->red()); + $this->assertEquals(255, $result->green()); + $this->assertEquals(51, $result->blue()); + + $handler = new InputHandler(); + $input = '#123456'; + $result = $handler->handle($input); + $this->assertInstanceOf(Color::class, $result); + $this->assertEquals(18, $result->red()); + $this->assertEquals(52, $result->green()); + $this->assertEquals(86, $result->blue()); + + $handler = new InputHandler(); + $input = '#333'; + $result = $handler->handle($input); + $this->assertInstanceOf(Color::class, $result); + $this->assertEquals(51, $result->red()); + $this->assertEquals(51, $result->green()); + $this->assertEquals(51, $result->blue()); } } From 9f885da391e345afc2bea48ec176bf07bc999cc7 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 31 Oct 2021 07:55:33 +0100 Subject: [PATCH 032/476] Fill modifier filling --- src/Drivers/Gd/Modifiers/FillModifier.php | 11 ++++++++--- src/Drivers/Imagick/Modifiers/FillModifier.php | 12 +++++++++--- src/Traits/CanHandleInput.php | 11 ----------- 3 files changed, 17 insertions(+), 17 deletions(-) delete mode 100644 src/Traits/CanHandleInput.php diff --git a/src/Drivers/Gd/Modifiers/FillModifier.php b/src/Drivers/Gd/Modifiers/FillModifier.php index 2cf7fd54..c2eb41d8 100644 --- a/src/Drivers/Gd/Modifiers/FillModifier.php +++ b/src/Drivers/Gd/Modifiers/FillModifier.php @@ -6,11 +6,11 @@ use Intervention\Image\Drivers\Gd\InputHandler; use Intervention\Image\Interfaces\FrameInterface; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; -use Intervention\Image\Traits\CanHandleInput; +use Intervention\Image\Traits\CanResolveDriverClass; class FillModifier implements ModifierInterface { - use CanHandleInput; + use CanResolveDriverClass; public function __construct($filling, ?int $x = null, ?int $y = null) { @@ -21,7 +21,7 @@ class FillModifier implements ModifierInterface { $width = $image->getWidth(); $height = $image->getHeight(); - $filling = $this->handleInput($this->filling); + $filling = $this->getApplicableFilling(); foreach ($image as $frame) { imagefilledrectangle($frame->getCore(), 0, 0, $width - 1, $height - 1, $filling); @@ -29,4 +29,9 @@ class FillModifier implements ModifierInterface return $image; } + + protected function getApplicableFilling(): ColorInterface + { + return $this->resolveDriverClass('InputHandler')->handle($this->filling); + } } diff --git a/src/Drivers/Imagick/Modifiers/FillModifier.php b/src/Drivers/Imagick/Modifiers/FillModifier.php index 81e20cfd..fb4f35b5 100644 --- a/src/Drivers/Imagick/Modifiers/FillModifier.php +++ b/src/Drivers/Imagick/Modifiers/FillModifier.php @@ -4,13 +4,14 @@ namespace Intervention\Image\Drivers\Imagick\Modifiers; use ImagickDraw; use Intervention\Image\Drivers\Imagick\InputHandler; +use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; -use Intervention\Image\Traits\CanHandleInput; +use Intervention\Image\Traits\CanResolveDriverClass; class FillModifier implements ModifierInterface { - use CanHandleInput; + use CanResolveDriverClass; public function __construct($filling, ?int $x = null, ?int $y = null) { @@ -19,7 +20,7 @@ class FillModifier implements ModifierInterface public function apply(ImageInterface $image): ImageInterface { - $filling = $this->handleInput($this->filling); + $filling = $this->getApplicableFilling(); $draw = new ImagickDraw(); $draw->setFillColor($filling->getPixel()); @@ -31,4 +32,9 @@ class FillModifier implements ModifierInterface return $image; } + + protected function getApplicableFilling(): ColorInterface + { + return $this->resolveDriverClass('InputHandler')->handle($this->filling); + } } diff --git a/src/Traits/CanHandleInput.php b/src/Traits/CanHandleInput.php deleted file mode 100644 index 3182e2af..00000000 --- a/src/Traits/CanHandleInput.php +++ /dev/null @@ -1,11 +0,0 @@ -handle($value); - } -} From 2c0e2eb719dc41ff4837dac5b26ce80c06667529 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 31 Oct 2021 06:59:48 +0000 Subject: [PATCH 033/476] Added Color::toInt --- src/Drivers/Gd/Color.php | 5 +++++ src/Drivers/Gd/Modifiers/FillModifier.php | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Drivers/Gd/Color.php b/src/Drivers/Gd/Color.php index 351072bd..a7ef9a53 100644 --- a/src/Drivers/Gd/Color.php +++ b/src/Drivers/Gd/Color.php @@ -54,4 +54,9 @@ class Color extends AbstractColor implements ColorInterface return [$r, $g, $b, $a]; } + + public function toInt(): int + { + return $this->value; + } } diff --git a/src/Drivers/Gd/Modifiers/FillModifier.php b/src/Drivers/Gd/Modifiers/FillModifier.php index c2eb41d8..8306e762 100644 --- a/src/Drivers/Gd/Modifiers/FillModifier.php +++ b/src/Drivers/Gd/Modifiers/FillModifier.php @@ -24,7 +24,7 @@ class FillModifier implements ModifierInterface $filling = $this->getApplicableFilling(); foreach ($image as $frame) { - imagefilledrectangle($frame->getCore(), 0, 0, $width - 1, $height - 1, $filling); + imagefilledrectangle($frame->getCore(), 0, 0, $width - 1, $height - 1, $filling->toInt()); } return $image; From b1900c32c758a51a09caeab9a3f3b37eeeb18e7f Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 31 Oct 2021 08:04:05 +0100 Subject: [PATCH 034/476] Bugfixes --- src/Drivers/Gd/Modifiers/FillModifier.php | 1 + src/Drivers/Imagick/Decoders/ArrayColorDecoder.php | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/src/Drivers/Gd/Modifiers/FillModifier.php b/src/Drivers/Gd/Modifiers/FillModifier.php index 8306e762..97faa06e 100644 --- a/src/Drivers/Gd/Modifiers/FillModifier.php +++ b/src/Drivers/Gd/Modifiers/FillModifier.php @@ -3,6 +3,7 @@ namespace Intervention\Image\Drivers\Gd\Modifiers; use Intervention\Image\Drivers\Gd\InputHandler; +use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\FrameInterface; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; diff --git a/src/Drivers/Imagick/Decoders/ArrayColorDecoder.php b/src/Drivers/Imagick/Decoders/ArrayColorDecoder.php index 89f52eeb..aee0df57 100644 --- a/src/Drivers/Imagick/Decoders/ArrayColorDecoder.php +++ b/src/Drivers/Imagick/Decoders/ArrayColorDecoder.php @@ -20,6 +20,10 @@ class ArrayColorDecoder extends AbstractDecoder implements DecoderInterface $this->fail(); } + if (count($input) === 3) { + $input[] = 1; + } + list($r, $g, $b, $a) = $input; $pixel = new ImagickPixel( From def7a406af27e4e558af976ee0d76133c3cdcbde Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 31 Oct 2021 08:10:16 +0100 Subject: [PATCH 035/476] Bugfix --- src/Drivers/Gd/Modifiers/FillModifier.php | 4 ++-- src/Drivers/Imagick/Modifiers/FillModifier.php | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Drivers/Gd/Modifiers/FillModifier.php b/src/Drivers/Gd/Modifiers/FillModifier.php index 97faa06e..3a857bd2 100644 --- a/src/Drivers/Gd/Modifiers/FillModifier.php +++ b/src/Drivers/Gd/Modifiers/FillModifier.php @@ -20,8 +20,8 @@ class FillModifier implements ModifierInterface public function apply(ImageInterface $image): ImageInterface { - $width = $image->getWidth(); - $height = $image->getHeight(); + $width = $image->width(); + $height = $image->height(); $filling = $this->getApplicableFilling(); foreach ($image as $frame) { diff --git a/src/Drivers/Imagick/Modifiers/FillModifier.php b/src/Drivers/Imagick/Modifiers/FillModifier.php index fb4f35b5..f3eddc0a 100644 --- a/src/Drivers/Imagick/Modifiers/FillModifier.php +++ b/src/Drivers/Imagick/Modifiers/FillModifier.php @@ -24,7 +24,7 @@ class FillModifier implements ModifierInterface $draw = new ImagickDraw(); $draw->setFillColor($filling->getPixel()); - $draw->rectangle(0, 0, $image->getWidth(), $image->getHeight()); + $draw->rectangle(0, 0, $image->width(), $image->height()); foreach ($image as $frame) { $frame->getCore()->drawImage($draw); From e2b9c17ba3d4b7d9f2c1c97d45d47fea53ac3d61 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Wed, 3 Nov 2021 18:48:10 +0000 Subject: [PATCH 036/476] Added Size::align() --- src/Geometry/Size.php | 87 +++++++++++++++++++++++++++++++++++++ tests/Geometry/SizeTest.php | 45 +++++++++++++++++++ 2 files changed, 132 insertions(+) diff --git a/src/Geometry/Size.php b/src/Geometry/Size.php index 4f19bc30..b2e4f71f 100644 --- a/src/Geometry/Size.php +++ b/src/Geometry/Size.php @@ -89,4 +89,91 @@ class Size implements SizeInterface { return $this->getWidth() < $this->getHeight(); } + + /** + * Aligns current size's pivot point to given position + * and moves point automatically by offset. + * + * @param string $position + * @param int $offset_x + * @param int $offset_y + * @return Size + */ + public function align(string $position, int $offset_x = 0, int $offset_y = 0): self + { + switch (strtolower($position)) { + case 'top': + case 'top-center': + case 'top-middle': + case 'center-top': + case 'middle-top': + $x = intval($this->width / 2); + $y = 0 + $offset_y; + break; + + case 'top-right': + case 'right-top': + $x = $this->width - $offset_x; + $y = 0 + $offset_y; + break; + + case 'left': + case 'left-center': + case 'left-middle': + case 'center-left': + case 'middle-left': + $x = 0 + $offset_x; + $y = intval($this->height / 2); + break; + + case 'right': + case 'right-center': + case 'right-middle': + case 'center-right': + case 'middle-right': + $x = $this->width - $offset_x; + $y = intval($this->height / 2); + break; + + case 'bottom-left': + case 'left-bottom': + $x = 0 + $offset_x; + $y = $this->height - $offset_y; + break; + + case 'bottom': + case 'bottom-center': + case 'bottom-middle': + case 'center-bottom': + case 'middle-bottom': + $x = intval($this->width / 2); + $y = $this->height - $offset_y; + break; + + case 'bottom-right': + case 'right-bottom': + $x = $this->width - $offset_x; + $y = $this->height - $offset_y; + break; + + case 'center': + case 'middle': + case 'center-center': + case 'middle-middle': + $x = intval($this->width / 2) + $offset_x; + $y = intval($this->height / 2) + $offset_y; + break; + + default: + case 'top-left': + case 'left-top': + $x = 0 + $offset_x; + $y = 0 + $offset_y; + break; + } + + $this->pivot->setPosition($x, $y); + + return $this; + } } diff --git a/tests/Geometry/SizeTest.php b/tests/Geometry/SizeTest.php index e3e9c348..47090238 100644 --- a/tests/Geometry/SizeTest.php +++ b/tests/Geometry/SizeTest.php @@ -98,4 +98,49 @@ class SizeTest extends TestCase $box = new Size(200, 300); $this->assertTrue($box->isPortrait()); } + + public function testAlign(): void + { + $box = new Size(640, 480); + $this->assertEquals(0, $box->getPivot()->getX()); + $this->assertEquals(0, $box->getPivot()->getY()); + + $box->align('top-left', 3, 3); + $this->assertEquals(3, $box->getPivot()->getX()); + $this->assertEquals(3, $box->getPivot()->getY()); + + $box->align('top', 3, 3); + $this->assertEquals(320, $box->getPivot()->getX()); + $this->assertEquals(3, $box->getPivot()->getY()); + + $box->align('top-right', 3, 3); + $this->assertEquals(637, $box->getPivot()->getX()); + $this->assertEquals(3, $box->getPivot()->getY()); + + $box->align('left', 3, 3); + $this->assertEquals(3, $box->getPivot()->getX()); + $this->assertEquals(240, $box->getPivot()->getY()); + + $box->align('center', 3, 3); + $this->assertEquals(323, $box->getPivot()->getX()); + $this->assertEquals(243, $box->getPivot()->getY()); + + $box->align('right', 3, 3); + $this->assertEquals(637, $box->getPivot()->getX()); + $this->assertEquals(240, $box->getPivot()->getY()); + + $box->align('bottom-left', 3, 3); + $this->assertEquals(3, $box->getPivot()->getX()); + $this->assertEquals(477, $box->getPivot()->getY()); + + $box->align('bottom', 3, 3); + $this->assertEquals(320, $box->getPivot()->getX()); + $this->assertEquals(477, $box->getPivot()->getY()); + + $result = $box->align('bottom-right', 3, 3); + $this->assertEquals(637, $box->getPivot()->getX()); + $this->assertEquals(477, $box->getPivot()->getY()); + + $this->assertInstanceOf(Size::class, $result); + } } From 0c3e08b46e057c5966f39838c2da5354931c8ca5 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Wed, 3 Nov 2021 18:48:58 +0000 Subject: [PATCH 037/476] removed fill modifier for now --- src/Drivers/Abstract/AbstractImage.php | 7 ---- src/Drivers/Gd/Modifiers/FillModifier.php | 38 ------------------ .../Imagick/Modifiers/FillModifier.php | 40 ------------------- 3 files changed, 85 deletions(-) delete mode 100644 src/Drivers/Gd/Modifiers/FillModifier.php delete mode 100644 src/Drivers/Imagick/Modifiers/FillModifier.php diff --git a/src/Drivers/Abstract/AbstractImage.php b/src/Drivers/Abstract/AbstractImage.php index 1556c451..4c1b1fba 100644 --- a/src/Drivers/Abstract/AbstractImage.php +++ b/src/Drivers/Abstract/AbstractImage.php @@ -150,11 +150,4 @@ abstract class AbstractImage $this->resolveDriverClass('Modifiers\ResizeModifier', $size) ); } - - public function fill($filling): ImageInterface - { - return $this->modify( - $this->resolveDriverClass('Modifiers\FillModifier', $filling) - ); - } } diff --git a/src/Drivers/Gd/Modifiers/FillModifier.php b/src/Drivers/Gd/Modifiers/FillModifier.php deleted file mode 100644 index 3a857bd2..00000000 --- a/src/Drivers/Gd/Modifiers/FillModifier.php +++ /dev/null @@ -1,38 +0,0 @@ -filling = $filling; - } - - public function apply(ImageInterface $image): ImageInterface - { - $width = $image->width(); - $height = $image->height(); - $filling = $this->getApplicableFilling(); - - foreach ($image as $frame) { - imagefilledrectangle($frame->getCore(), 0, 0, $width - 1, $height - 1, $filling->toInt()); - } - - return $image; - } - - protected function getApplicableFilling(): ColorInterface - { - return $this->resolveDriverClass('InputHandler')->handle($this->filling); - } -} diff --git a/src/Drivers/Imagick/Modifiers/FillModifier.php b/src/Drivers/Imagick/Modifiers/FillModifier.php deleted file mode 100644 index f3eddc0a..00000000 --- a/src/Drivers/Imagick/Modifiers/FillModifier.php +++ /dev/null @@ -1,40 +0,0 @@ -filling = $filling; - } - - public function apply(ImageInterface $image): ImageInterface - { - $filling = $this->getApplicableFilling(); - - $draw = new ImagickDraw(); - $draw->setFillColor($filling->getPixel()); - $draw->rectangle(0, 0, $image->width(), $image->height()); - - foreach ($image as $frame) { - $frame->getCore()->drawImage($draw); - } - - return $image; - } - - protected function getApplicableFilling(): ColorInterface - { - return $this->resolveDriverClass('InputHandler')->handle($this->filling); - } -} From 0afda4b9e4752e06f2924045c40d939a0efcccc4 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Fri, 5 Nov 2021 15:48:16 +0000 Subject: [PATCH 038/476] Modifier & tests --- src/Drivers/Abstract/AbstractImage.php | 28 +++++++++ .../Gd/Decoders/BinaryImageDecoder.php | 37 +++++++++++- src/Drivers/Gd/Image.php | 14 ++++- src/Drivers/Imagick/Image.php | 10 ++++ src/Interfaces/ImageInterface.php | 3 + tests/Drivers/Gd/ImageTest.php | 56 +++++++++++++++++- .../Drivers/Gd/Modifiers/BlurModifierTest.php | 21 +++++++ .../Imagick/Modifiers/BlurModifierTest.php | 21 +++++++ tests/Traits/CanCreateGdTestImage.php | 38 ++++++++++++ tests/Traits/CanCreateImagickTestImage.php | 23 +++++++ tests/images/circle.png | Bin 0 -> 383 bytes tests/images/test.jpg | Bin 0 -> 1427 bytes tests/images/trim.png | Bin 0 -> 258 bytes 13 files changed, 245 insertions(+), 6 deletions(-) create mode 100644 tests/Drivers/Gd/Modifiers/BlurModifierTest.php create mode 100644 tests/Drivers/Imagick/Modifiers/BlurModifierTest.php create mode 100644 tests/Traits/CanCreateGdTestImage.php create mode 100644 tests/Traits/CanCreateImagickTestImage.php create mode 100644 tests/images/circle.png create mode 100644 tests/images/test.jpg create mode 100644 tests/images/trim.png diff --git a/src/Drivers/Abstract/AbstractImage.php b/src/Drivers/Abstract/AbstractImage.php index 4c1b1fba..b315f0b1 100644 --- a/src/Drivers/Abstract/AbstractImage.php +++ b/src/Drivers/Abstract/AbstractImage.php @@ -36,6 +36,11 @@ abstract class AbstractImage return $this->frames; } + public function getFrame(int $key = 0): ?FrameInterface + { + return $this->frames->get($key); + } + public function addFrame(FrameInterface $frame): ImageInterface { $this->frames->push($frame); @@ -115,6 +120,16 @@ abstract class AbstractImage ); } + public function pickColors(int $x, int $y): Collection + { + $colors = new Collection(); + foreach ($this->getFrames() as $key => $frame) { + $colors->push($this->pickColor($x, $y, $key)); + } + + return $colors; + } + public function resize(...$arguments): ImageInterface { $size = $this->getResizer()->setTargetSizeByArray($arguments)->resize(); @@ -150,4 +165,17 @@ abstract class AbstractImage $this->resolveDriverClass('Modifiers\ResizeModifier', $size) ); } + + public function place($element, string $position = 'top-left', int $offset_x = 0, int $offset_y = 0): ImageInterface + { + return $this->modify( + $this->resolveDriverClass( + 'Modifiers\PlaceModifier', + $element, + $position, + $offset_x, + $offset_y + ) + ); + } } diff --git a/src/Drivers/Gd/Decoders/BinaryImageDecoder.php b/src/Drivers/Gd/Decoders/BinaryImageDecoder.php index 478ae7f6..32dcc6b1 100644 --- a/src/Drivers/Gd/Decoders/BinaryImageDecoder.php +++ b/src/Drivers/Gd/Decoders/BinaryImageDecoder.php @@ -2,6 +2,7 @@ namespace Intervention\Image\Drivers\Gd\Decoders; +use GdImage; use Intervention\Image\Collection; use Intervention\Image\Drivers\Abstract\Decoders\AbstractDecoder; use Intervention\Image\Drivers\Gd\Frame; @@ -26,13 +27,15 @@ class BinaryImageDecoder extends AbstractDecoder implements DecoderInterface return $this->decodeGif($input); // decode (animated) gif } - $resource = @imagecreatefromstring($input); + $gd = @imagecreatefromstring($input); - if ($resource === false) { + if ($gd === false) { $this->fail(); } - return new Image(new Collection([new Frame($resource)])); + $gd = $this->gdImageToTruecolor($gd); + + return new Image(new Collection([new Frame($gd)])); } protected function decodeGif($input): ImageInterface @@ -55,4 +58,32 @@ class BinaryImageDecoder extends AbstractDecoder implements DecoderInterface return $image; } + + /** + * Transform GD image into truecolor version + * + * @param GdImage $gd + * @return bool + */ + public function gdImageToTruecolor(GdImage $gd): GdImage + { + $width = imagesx($gd); + $height = imagesy($gd); + + // new canvas + $canvas = imagecreatetruecolor($width, $height); + + // fill with transparent color + imagealphablending($canvas, false); + $transparent = imagecolorallocatealpha($canvas, 255, 255, 255, 127); + imagefilledrectangle($canvas, 0, 0, $width, $height, $transparent); + imagecolortransparent($canvas, $transparent); + imagealphablending($canvas, true); + + // copy original + imagecopy($canvas, $gd, 0, 0, 0, 0, $width, $height); + imagedestroy($gd); + + return $canvas; + } } diff --git a/src/Drivers/Gd/Image.php b/src/Drivers/Gd/Image.php index be11857e..d226269a 100644 --- a/src/Drivers/Gd/Image.php +++ b/src/Drivers/Gd/Image.php @@ -8,6 +8,7 @@ use Intervention\Image\Drivers\Abstract\AbstractImage; use Intervention\Image\Drivers\Gd\Frame; use Intervention\Image\Geometry\Resizer; use Intervention\Image\Geometry\Size; +use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\EncoderInterface; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\SizeInterface; @@ -17,11 +18,20 @@ class Image extends AbstractImage implements ImageInterface, IteratorAggregate { public function width(): int { - return imagesx($this->frames->first()->getCore()); + return imagesx($this->getFrame()->getCore()); } public function height(): int { - return imagesy($this->frames->first()->getCore()); + return imagesy($this->getFrame()->getCore()); + } + + public function pickColor(int $x, int $y, int $frame_key = 0): ?ColorInterface + { + if ($frame = $this->getFrame($frame_key)) { + return new Color(imagecolorat($frame->getCore(), $x, $y)); + } + + return null; } } diff --git a/src/Drivers/Imagick/Image.php b/src/Drivers/Imagick/Image.php index 47073a25..a0c289af 100644 --- a/src/Drivers/Imagick/Image.php +++ b/src/Drivers/Imagick/Image.php @@ -7,6 +7,7 @@ use Intervention\Image\Collection; use Intervention\Image\Drivers\Abstract\AbstractImage; use Intervention\Image\Drivers\Imagick\Frame; use Intervention\Image\Geometry\Size; +use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\SizeInterface; use IteratorAggregate; @@ -22,4 +23,13 @@ class Image extends AbstractImage implements ImageInterface, IteratorAggregate { return $this->frames->first()->getCore()->getImageHeight(); } + + public function pickColor(int $x, int $y, int $frame_key = 0): ?ColorInterface + { + if ($frame = $this->getFrame($frame_key)) { + return new Color($frame->getCore()->getImagePixelColor($x, $y)); + } + + return null; + } } diff --git a/src/Interfaces/ImageInterface.php b/src/Interfaces/ImageInterface.php index 33231f2c..56b5ec01 100644 --- a/src/Interfaces/ImageInterface.php +++ b/src/Interfaces/ImageInterface.php @@ -2,6 +2,7 @@ namespace Intervention\Image\Interfaces; +use Intervention\Image\Collection; use Intervention\Image\EncodedImage; interface ImageInterface @@ -14,4 +15,6 @@ interface ImageInterface public function encode(EncoderInterface $encoder): EncodedImage; public function setLoops(int $count): ImageInterface; public function loops(): int; + public function pickColor(int $x, int $y, int $frame_key = 0): ?ColorInterface; + public function pickColors(int $x, int $y): Collection; } diff --git a/tests/Drivers/Gd/ImageTest.php b/tests/Drivers/Gd/ImageTest.php index 0f966290..1635bc61 100644 --- a/tests/Drivers/Gd/ImageTest.php +++ b/tests/Drivers/Gd/ImageTest.php @@ -3,6 +3,7 @@ namespace Intervention\Image\Tests\Drivers\Gd; use Intervention\Image\Collection; +use Intervention\Image\Drivers\Gd\Color; use Intervention\Image\Drivers\Gd\Frame; use Intervention\Image\Drivers\Gd\Image; use Intervention\Image\Geometry\Size; @@ -14,7 +15,17 @@ class ImageTest extends TestCase protected function setUp(): void { - $this->image = new Image(new Collection([new Frame(imagecreatetruecolor(3, 2))])); + $gd1 = imagecreatetruecolor(3, 2); + imagefill($gd1, 0, 0, imagecolorallocate($gd1, 255, 0, 0)); + $gd2 = imagecreatetruecolor(3, 2); + imagefill($gd2, 0, 0, imagecolorallocate($gd1, 0, 255, 0)); + $gd3 = imagecreatetruecolor(3, 2); + imagefill($gd3, 0, 0, imagecolorallocate($gd1, 0, 0, 255)); + $this->image = new Image(new Collection([ + new Frame($gd1), + new Frame($gd2), + new Frame($gd3), + ])); } public function testConstructor(): void @@ -43,4 +54,47 @@ class ImageTest extends TestCase { $this->assertInstanceOf(Size::class, $this->image->getSize()); } + + public function testPickColor(): void + { + $color = $this->image->pickColor(0, 0); + $this->assertInstanceOf(Color::class, $color); + $this->assertEquals(255, $color->red()); + $this->assertEquals(0, $color->green()); + $this->assertEquals(0, $color->blue()); + + $color = $this->image->pickColor(0, 0, 1); + $this->assertInstanceOf(Color::class, $color); + $this->assertEquals(0, $color->red()); + $this->assertEquals(255, $color->green()); + $this->assertEquals(0, $color->blue()); + + $color = $this->image->pickColor(0, 0, 2); + $this->assertInstanceOf(Color::class, $color); + $this->assertEquals(0, $color->red()); + $this->assertEquals(0, $color->green()); + $this->assertEquals(255, $color->blue()); + + $color = $this->image->pickColor(0, 0, 3); + $this->assertNull($color); + } + + public function testPickColors(): void + { + $colors = $this->image->pickColors(0, 0); + $this->assertInstanceOf(Collection::class, $colors); + $this->assertCount(3, $colors); + + $this->assertEquals(255, $colors->get(0)->red()); + $this->assertEquals(0, $colors->get(0)->green()); + $this->assertEquals(0, $colors->get(0)->blue()); + + $this->assertEquals(0, $colors->get(1)->red()); + $this->assertEquals(255, $colors->get(1)->green()); + $this->assertEquals(0, $colors->get(1)->blue()); + + $this->assertEquals(0, $colors->get(2)->red()); + $this->assertEquals(0, $colors->get(2)->green()); + $this->assertEquals(255, $colors->get(2)->blue()); + } } diff --git a/tests/Drivers/Gd/Modifiers/BlurModifierTest.php b/tests/Drivers/Gd/Modifiers/BlurModifierTest.php new file mode 100644 index 00000000..9062eda4 --- /dev/null +++ b/tests/Drivers/Gd/Modifiers/BlurModifierTest.php @@ -0,0 +1,21 @@ +createTestImage('trim.png'); + $this->assertEquals('00aef0', $image->pickColor(14, 14)->toHex()); + $image->modify(new BlurModifier(30)); + $this->assertEquals('4fa68d', $image->pickColor(14, 14)->toHex()); + } +} diff --git a/tests/Drivers/Imagick/Modifiers/BlurModifierTest.php b/tests/Drivers/Imagick/Modifiers/BlurModifierTest.php new file mode 100644 index 00000000..a1ff3750 --- /dev/null +++ b/tests/Drivers/Imagick/Modifiers/BlurModifierTest.php @@ -0,0 +1,21 @@ +createTestImage('trim.png'); + $this->assertEquals('00aef0', $image->pickColor(14, 14)->toHex()); + $image->modify(new BlurModifier(30)); + $this->assertEquals('42acb2', $image->pickColor(14, 14)->toHex()); + } +} diff --git a/tests/Traits/CanCreateGdTestImage.php b/tests/Traits/CanCreateGdTestImage.php new file mode 100644 index 00000000..f9f4238e --- /dev/null +++ b/tests/Traits/CanCreateGdTestImage.php @@ -0,0 +1,38 @@ +testImageDecoder()->handle( + sprintf('%s/../images/%s', __DIR__, $filename) + ); + } + + public function createTestAnimation(): Image + { + $gd1 = imagecreatetruecolor(3, 2); + imagefill($gd1, 0, 0, imagecolorallocate($gd1, 255, 0, 0)); + $gd2 = imagecreatetruecolor(3, 2); + imagefill($gd2, 0, 0, imagecolorallocate($gd1, 0, 255, 0)); + $gd3 = imagecreatetruecolor(3, 2); + imagefill($gd3, 0, 0, imagecolorallocate($gd1, 0, 0, 255)); + return new Image(new Collection([ + new Frame($gd1), + new Frame($gd2), + new Frame($gd3), + ])); + } + + protected function testImageDecoder(): FilePathImageDecoder + { + return new FilePathImageDecoder(); + } +} diff --git a/tests/Traits/CanCreateImagickTestImage.php b/tests/Traits/CanCreateImagickTestImage.php new file mode 100644 index 00000000..0ff42675 --- /dev/null +++ b/tests/Traits/CanCreateImagickTestImage.php @@ -0,0 +1,23 @@ +testImageDecoder()->handle( + sprintf('%s/../images/%s', __DIR__, $filename) + ); + } + + protected function testImageDecoder(): FilePathImageDecoder + { + return new FilePathImageDecoder(); + } +} diff --git a/tests/images/circle.png b/tests/images/circle.png new file mode 100644 index 0000000000000000000000000000000000000000..0ad4e6bdafdb58ed53f52ea8b123c55a63406d07 GIT binary patch literal 383 zcmV-_0f7FAP)`e_)-1Z1?M~MNpkO3v`TLieGzzAE( zhyvF&0t86V!Zy+(S@H~p$Y49k5Pllsf(o{?3YSkpJYWa$cqdGV3U)9RLd-tc8SJ34 zxgUT8c5um@d!qFqNc-PCQ@}h?{8e_0Fi(uXl`|#GGv!ZJ>;Uu3@V#nIVV+9A-Wv_f zQw;*R290Hh`O{c13KX>zW@=rZg@R_RUOZbS-UKaa&^c)=k<+Xmwy7VGLq`@PD=1Jm;PQmwy4qtMArd zujX?bCEMQEw!i1{cGccj0qvB8_4#~O$Xba+R_tQFSQq?PSb#i;5>o~{1&3sYWcFLM z>yKK0zBOaqXrYKx?DFb3c~9)2gBe`Y{|vC*UgF>#oZs8s#D;rg-MX--=CHOpZ}L{huH!>H?tJ)SS`+g1Qh%qOLK; zHVc(O9B)=qt7fe)55&QT=nQ1jWKhs!p)odYk_Hi^MPqZR%o?fKJ1^a1xnTJU?h3k# v{YYqCbayT_YmhWm%q85Ni*@&`0%#!#dP1OW#};_{;;swaEvUkJXn*+wgY5xV literal 0 HcmV?d00001 diff --git a/tests/images/trim.png b/tests/images/trim.png new file mode 100644 index 0000000000000000000000000000000000000000..6f7bd9bbde910b6d024df22a5094b2369a270b0e GIT binary patch literal 258 zcmV+d0sa1oP)RvEwADaz}vCfAWgLl^a5>hU5Q*zRc z+T=m2n$d^67+o=@EX+@*4w02nv5~GmRvMl`7C?skC`c)2KUN?XC=M$QC>|>wC;=-0 zC=n}>P?*B>_K6`gtJDvMI7|Nc@rx0UOveQdB|iG}fZc3JPB5Mr?Ms}B?*IS*07*qo IM6N<$f^76>`Tzg` literal 0 HcmV?d00001 From 1b856c81bcc912d45dd1c4bb57a916a9c28d0960 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Fri, 5 Nov 2021 16:42:48 +0000 Subject: [PATCH 039/476] Added tests --- src/Drivers/Abstract/AbstractColor.php | 16 ++++++++++ .../Gd/Decoders/BinaryImageDecoder.php | 32 ++----------------- .../Gd/Modifiers/GreyscaleModifierTest.php | 21 ++++++++++++ .../Gd/Modifiers/ResizeModifierTest.php | 24 ++++++++++++++ .../Modifiers/GreyscaleModifierTest.php | 21 ++++++++++++ .../Imagick/Modifiers/ResizeModifierTest.php | 24 ++++++++++++++ 6 files changed, 109 insertions(+), 29 deletions(-) create mode 100644 tests/Drivers/Gd/Modifiers/GreyscaleModifierTest.php create mode 100644 tests/Drivers/Gd/Modifiers/ResizeModifierTest.php create mode 100644 tests/Drivers/Imagick/Modifiers/GreyscaleModifierTest.php create mode 100644 tests/Drivers/Imagick/Modifiers/ResizeModifierTest.php diff --git a/src/Drivers/Abstract/AbstractColor.php b/src/Drivers/Abstract/AbstractColor.php index 449e4747..f8ea601a 100644 --- a/src/Drivers/Abstract/AbstractColor.php +++ b/src/Drivers/Abstract/AbstractColor.php @@ -4,6 +4,12 @@ namespace Intervention\Image\Drivers\Abstract; abstract class AbstractColor { + /** + * Format color to hexadecimal color code + * + * @param string $prefix + * @return string + */ public function toHex(string $prefix = ''): string { return sprintf( @@ -14,4 +20,14 @@ abstract class AbstractColor $this->blue() ); } + + /** + * Determine if color is greyscale + * + * @return boolean + */ + public function isGreyscale(): bool + { + return ($this->red() === $this->green()) && ($this->green() === $this->blue()); + } } diff --git a/src/Drivers/Gd/Decoders/BinaryImageDecoder.php b/src/Drivers/Gd/Decoders/BinaryImageDecoder.php index 32dcc6b1..f60a9702 100644 --- a/src/Drivers/Gd/Decoders/BinaryImageDecoder.php +++ b/src/Drivers/Gd/Decoders/BinaryImageDecoder.php @@ -33,7 +33,9 @@ class BinaryImageDecoder extends AbstractDecoder implements DecoderInterface $this->fail(); } - $gd = $this->gdImageToTruecolor($gd); + if (! imageistruecolor($gd)) { + imagepalettetotruecolor($gd); + } return new Image(new Collection([new Frame($gd)])); } @@ -58,32 +60,4 @@ class BinaryImageDecoder extends AbstractDecoder implements DecoderInterface return $image; } - - /** - * Transform GD image into truecolor version - * - * @param GdImage $gd - * @return bool - */ - public function gdImageToTruecolor(GdImage $gd): GdImage - { - $width = imagesx($gd); - $height = imagesy($gd); - - // new canvas - $canvas = imagecreatetruecolor($width, $height); - - // fill with transparent color - imagealphablending($canvas, false); - $transparent = imagecolorallocatealpha($canvas, 255, 255, 255, 127); - imagefilledrectangle($canvas, 0, 0, $width, $height, $transparent); - imagecolortransparent($canvas, $transparent); - imagealphablending($canvas, true); - - // copy original - imagecopy($canvas, $gd, 0, 0, 0, 0, $width, $height); - imagedestroy($gd); - - return $canvas; - } } diff --git a/tests/Drivers/Gd/Modifiers/GreyscaleModifierTest.php b/tests/Drivers/Gd/Modifiers/GreyscaleModifierTest.php new file mode 100644 index 00000000..9302d460 --- /dev/null +++ b/tests/Drivers/Gd/Modifiers/GreyscaleModifierTest.php @@ -0,0 +1,21 @@ +createTestImage('trim.png'); + $this->assertFalse($image->pickColor(0, 0)->isGreyscale()); + $image->modify(new GreyscaleModifier()); + $this->assertTrue($image->pickColor(0, 0)->isGreyscale()); + } +} diff --git a/tests/Drivers/Gd/Modifiers/ResizeModifierTest.php b/tests/Drivers/Gd/Modifiers/ResizeModifierTest.php new file mode 100644 index 00000000..15e4c0dd --- /dev/null +++ b/tests/Drivers/Gd/Modifiers/ResizeModifierTest.php @@ -0,0 +1,24 @@ +createTestImage('trim.png'); + $this->assertEquals(50, $image->width()); + $this->assertEquals(50, $image->height()); + $image->modify(new ResizeModifier(new Size(30, 20))); + $this->assertEquals(30, $image->width()); + $this->assertEquals(20, $image->height()); + } +} diff --git a/tests/Drivers/Imagick/Modifiers/GreyscaleModifierTest.php b/tests/Drivers/Imagick/Modifiers/GreyscaleModifierTest.php new file mode 100644 index 00000000..64b5be61 --- /dev/null +++ b/tests/Drivers/Imagick/Modifiers/GreyscaleModifierTest.php @@ -0,0 +1,21 @@ +createTestImage('trim.png'); + $this->assertFalse($image->pickColor(0, 0)->isGreyscale()); + $image->modify(new GreyscaleModifier()); + $this->assertTrue($image->pickColor(0, 0)->isGreyscale()); + } +} diff --git a/tests/Drivers/Imagick/Modifiers/ResizeModifierTest.php b/tests/Drivers/Imagick/Modifiers/ResizeModifierTest.php new file mode 100644 index 00000000..af8bca07 --- /dev/null +++ b/tests/Drivers/Imagick/Modifiers/ResizeModifierTest.php @@ -0,0 +1,24 @@ +createTestImage('trim.png'); + $this->assertEquals(50, $image->width()); + $this->assertEquals(50, $image->height()); + $image->modify(new ResizeModifier(new Size(30, 20))); + $this->assertEquals(30, $image->width()); + $this->assertEquals(20, $image->height()); + } +} From 7c6c7be2badd62d738941f79df4300aa18547179 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Fri, 5 Nov 2021 17:13:27 +0000 Subject: [PATCH 040/476] PlaceModifier --- src/Drivers/Gd/Modifiers/PlaceModifier.php | 65 +++++++++++++++++++ .../Imagick/Modifiers/PlaceModifier.php | 58 +++++++++++++++++ src/Geometry/Size.php | 15 +++++ .../Gd/Modifiers/PlaceModifierTest.php | 22 +++++++ .../Imagick/Modifiers/PlaceModifierTest.php | 22 +++++++ 5 files changed, 182 insertions(+) create mode 100644 src/Drivers/Gd/Modifiers/PlaceModifier.php create mode 100644 src/Drivers/Imagick/Modifiers/PlaceModifier.php create mode 100644 tests/Drivers/Gd/Modifiers/PlaceModifierTest.php create mode 100644 tests/Drivers/Imagick/Modifiers/PlaceModifierTest.php diff --git a/src/Drivers/Gd/Modifiers/PlaceModifier.php b/src/Drivers/Gd/Modifiers/PlaceModifier.php new file mode 100644 index 00000000..059ac16c --- /dev/null +++ b/src/Drivers/Gd/Modifiers/PlaceModifier.php @@ -0,0 +1,65 @@ +element = $element; + $this->position = $position; + $this->offset_x = $offset_x; + $this->offset_y = $offset_y; + } + + public function apply(ImageInterface $image): ImageInterface + { + $watermark = $this->decodeWatermark(); + $position = $this->getPosition($image, $watermark); + + foreach ($image as $frame) { + imagecopy( + $frame->getCore(), + $watermark->getFrame()->getCore(), + $position->getX(), + $position->getY(), + 0, + 0, + $watermark->width(), + $watermark->height() + ); + } + + return $image; + } + + protected function decodeWatermark(): Image + { + return $this->resolveDriverClass('InputHandler')->handle($this->element); + } + + protected function getPosition(Image $image, Image $watermark): Point + { + $image_size = $image->getSize()->align($this->position, $this->offset_x, $this->offset_y); + $watermark_size = $watermark->getSize()->align($this->position); + + return $image_size->relativePosition($watermark_size); + } +} diff --git a/src/Drivers/Imagick/Modifiers/PlaceModifier.php b/src/Drivers/Imagick/Modifiers/PlaceModifier.php new file mode 100644 index 00000000..e91714bc --- /dev/null +++ b/src/Drivers/Imagick/Modifiers/PlaceModifier.php @@ -0,0 +1,58 @@ +element = $element; + $this->position = $position; + $this->offset_x = $offset_x; + $this->offset_y = $offset_y; + } + + public function apply(ImageInterface $image): ImageInterface + { + $watermark = $this->decodeWatermark(); + $position = $this->getPosition($image, $watermark); + + foreach ($image as $frame) { + $frame->getCore()->compositeImage( + $watermark->getFrame()->getCore(), + Imagick::COMPOSITE_DEFAULT, + $position->getX(), + $position->getY() + ); + } + + return $image; + } + + protected function decodeWatermark(): Image + { + return $this->resolveDriverClass('InputHandler')->handle($this->element); + } + + protected function getPosition(Image $image, Image $watermark): Point + { + $image_size = $image->getSize()->align($this->position, $this->offset_x, $this->offset_y); + $watermark_size = $watermark->getSize()->align($this->position); + + return $image_size->relativePosition($watermark_size); + } +} diff --git a/src/Geometry/Size.php b/src/Geometry/Size.php index b2e4f71f..c1adef14 100644 --- a/src/Geometry/Size.php +++ b/src/Geometry/Size.php @@ -176,4 +176,19 @@ class Size implements SizeInterface return $this; } + + /** + * Calculate the relative position to another Size + * based on the pivot point settings of both sizes. + * + * @param Size $size + * @return Point + */ + public function relativePosition(Size $size): Point + { + $x = $this->getPivot()->getX() - $size->getPivot()->getX(); + $y = $this->getPivot()->getY() - $size->getPivot()->getY(); + + return new Point($x, $y); + } } diff --git a/tests/Drivers/Gd/Modifiers/PlaceModifierTest.php b/tests/Drivers/Gd/Modifiers/PlaceModifierTest.php new file mode 100644 index 00000000..bd1d3e0b --- /dev/null +++ b/tests/Drivers/Gd/Modifiers/PlaceModifierTest.php @@ -0,0 +1,22 @@ +createTestImage('test.jpg'); + $this->assertEquals('febc44', $image->pickColor(300, 25)->toHex()); + $image->modify(new PlaceModifier(__DIR__ . '/../../../images/circle.png', 'top-right', 0, 0)); + $this->assertEquals('32250d', $image->pickColor(300, 25)->toHex()); + } +} diff --git a/tests/Drivers/Imagick/Modifiers/PlaceModifierTest.php b/tests/Drivers/Imagick/Modifiers/PlaceModifierTest.php new file mode 100644 index 00000000..20e2f077 --- /dev/null +++ b/tests/Drivers/Imagick/Modifiers/PlaceModifierTest.php @@ -0,0 +1,22 @@ +createTestImage('test.jpg'); + $this->assertEquals('fdbd42', $image->pickColor(300, 25)->toHex()); + $image->modify(new PlaceModifier(__DIR__ . '/../../../images/circle.png', 'top-right', 0, 0)); + $this->assertEquals('33260d', $image->pickColor(300, 25)->toHex()); + } +} From 66d12c74a2ff965a6f95b833570f45710564bc8f Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Fri, 5 Nov 2021 18:12:20 +0000 Subject: [PATCH 041/476] PHP 7.4 support --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 93776f08..b075794e 100644 --- a/composer.json +++ b/composer.json @@ -12,7 +12,7 @@ } ], "require": { - "php": "^8", + "php": "^7.4|^8", "intervention/gif": "dev-master", "intervention/mimesniffer": "^0.4.2" }, From cec4a37b76a8d20a0d1cd8f25fe8fd6b83c0eba2 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Fri, 5 Nov 2021 19:21:40 +0100 Subject: [PATCH 042/476] PHP 7.4 support --- src/Collection.php | 6 ++++-- src/Drivers/Abstract/Decoders/AbstractDecoder.php | 6 ++++-- src/Drivers/Abstract/Encoders/AbstractEncoder.php | 6 ++++-- src/Drivers/Gd/Frame.php | 11 +++++++++-- src/Drivers/Imagick/Frame.php | 11 +++++++++-- 5 files changed, 30 insertions(+), 10 deletions(-) diff --git a/src/Collection.php b/src/Collection.php index 2c40af19..024718c5 100644 --- a/src/Collection.php +++ b/src/Collection.php @@ -10,15 +10,17 @@ use IteratorAggregate; class Collection implements CollectionInterface, IteratorAggregate, Countable { + protected $items = []; + /** * Create a collection. * * @param array $items * @return void */ - public function __construct(protected array $items = []) + public function __construct(array $items = []) { - // + $this->items = $items } /** diff --git a/src/Drivers/Abstract/Decoders/AbstractDecoder.php b/src/Drivers/Abstract/Decoders/AbstractDecoder.php index 93740041..32a8070b 100644 --- a/src/Drivers/Abstract/Decoders/AbstractDecoder.php +++ b/src/Drivers/Abstract/Decoders/AbstractDecoder.php @@ -10,9 +10,11 @@ use Intervention\MimeSniffer\AbstractType; abstract class AbstractDecoder { - public function __construct(protected ?AbstractDecoder $successor = null) + protected $successor = null; + + public function __construct(?AbstractDecoder $successor = null) { - // + $this->successor = $successor; } final public function handle($input): null|ImageInterface|ColorInterface diff --git a/src/Drivers/Abstract/Encoders/AbstractEncoder.php b/src/Drivers/Abstract/Encoders/AbstractEncoder.php index 31a53ac5..a557311f 100644 --- a/src/Drivers/Abstract/Encoders/AbstractEncoder.php +++ b/src/Drivers/Abstract/Encoders/AbstractEncoder.php @@ -6,9 +6,11 @@ use Intervention\Image\Interfaces\EncoderInterface; abstract class AbstractEncoder { - public function __construct(protected ?int $quality = null) + protected $quality; + + public function __construct(?int $quality = null) { - // + $this->quality = $quality; } /** diff --git a/src/Drivers/Gd/Frame.php b/src/Drivers/Gd/Frame.php index 76803cb8..7cbca2e7 100644 --- a/src/Drivers/Gd/Frame.php +++ b/src/Drivers/Gd/Frame.php @@ -12,6 +12,13 @@ use Intervention\Image\Interfaces\SizeInterface; class Frame extends AbstractFrame implements FrameInterface { + /** + * Gd image representation of frame + * + * @var GdImage + */ + protected $core; + /** * Delay time in seconds after next frame is shown * @@ -40,9 +47,9 @@ class Frame extends AbstractFrame implements FrameInterface */ protected $offset_top = 0; - public function __construct(protected GdImage $core) + public function __construct(GdImage $core) { - // + $this->core = $core; } public function getCore(): GdImage diff --git a/src/Drivers/Imagick/Frame.php b/src/Drivers/Imagick/Frame.php index 94cc9c68..b787f773 100644 --- a/src/Drivers/Imagick/Frame.php +++ b/src/Drivers/Imagick/Frame.php @@ -12,9 +12,16 @@ use Intervention\Image\Interfaces\SizeInterface; class Frame extends AbstractFrame implements FrameInterface { - public function __construct(protected Imagick $core) + /** + * Imagick image representation of frame + * + * @var Imagick + */ + protected $core; + + public function __construct(Imagick $core) { - // + $this->core = $core; } public function getCore(): Imagick From f035494b3d8be2d62b69a48083ca8da787d05b26 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Fri, 5 Nov 2021 19:35:26 +0100 Subject: [PATCH 043/476] Bugfix --- src/Collection.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Collection.php b/src/Collection.php index 024718c5..99699726 100644 --- a/src/Collection.php +++ b/src/Collection.php @@ -20,7 +20,7 @@ class Collection implements CollectionInterface, IteratorAggregate, Countable */ public function __construct(array $items = []) { - $this->items = $items + $this->items = $items; } /** From 304d158fb045cb5bfc58ff1bc8257afa5b35cdbd Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Fri, 5 Nov 2021 19:47:09 +0100 Subject: [PATCH 044/476] Bugfix --- src/Drivers/Gd/Modifiers/PlaceModifier.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Drivers/Gd/Modifiers/PlaceModifier.php b/src/Drivers/Gd/Modifiers/PlaceModifier.php index 059ac16c..c9576f2c 100644 --- a/src/Drivers/Gd/Modifiers/PlaceModifier.php +++ b/src/Drivers/Gd/Modifiers/PlaceModifier.php @@ -35,6 +35,7 @@ class PlaceModifier implements ModifierInterface $position = $this->getPosition($image, $watermark); foreach ($image as $frame) { + imagealphablending($frame->getCore(), true); imagecopy( $frame->getCore(), $watermark->getFrame()->getCore(), From 9ac8e765c2793b9246d8836d4beccb00405a82b1 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 7 Nov 2021 11:37:42 +0000 Subject: [PATCH 045/476] Geometry refactoring --- src/Geometry/Resizer.php | 114 ++++++++++++++++++--------------- src/Geometry/Size.php | 7 ++ tests/Geometry/ResizerTest.php | 14 ++-- tests/Geometry/SizeTest.php | 43 +++++++++++++ 4 files changed, 119 insertions(+), 59 deletions(-) diff --git a/src/Geometry/Resizer.php b/src/Geometry/Resizer.php index d804194c..6132d58c 100644 --- a/src/Geometry/Resizer.php +++ b/src/Geometry/Resizer.php @@ -11,7 +11,7 @@ class Resizer * * @var SizeInterface */ - protected $size; + protected $original; /** * Target size @@ -20,20 +20,20 @@ class Resizer */ protected $target; - public function __construct(SizeInterface $size) + /** + * Create new instance + * + * @param SizeInterface $size + */ + public function __construct(SizeInterface $original) { - $this->size = $size; + $this->original = $original; $this->target = new Size(0, 0); } - public static function fromSize(SizeInterface $size): self + protected function copyOriginal(): SizeInterface { - return new self($size); - } - - public function getSize(): SizeInterface - { - return $this->size; + return new Size($this->original->getWidth(), $this->original->getHeight()); } protected function hasTargetWidth(): bool @@ -81,14 +81,32 @@ class Resizer public function setTargetSize(SizeInterface $size): self { - $this->target = $size; + $this->target = new Size($size->getWidth(), $size->getHeight()); return $this; } + protected function getProportionalWidth(): int + { + if (! $this->hasTargetHeight()) { + return $this->original->getWidth(); + } + + return (int) round($this->target->getHeight() * $this->original->getAspectRatio()); + } + + protected function getProportionalHeight(): int + { + if (! $this->hasTargetWidth()) { + return $this->original->getHeight(); + } + + return (int) round($this->target->getWidth() / $this->original->getAspectRatio()); + } + public function resize(): SizeInterface { - $resized = clone $this->size; + $resized = $this->copyOriginal(); if ($this->hasTargetWidth()) { $resized->setWidth($this->target->getWidth()); @@ -103,92 +121,82 @@ class Resizer public function resizeDown(): SizeInterface { + $resized = $this->copyOriginal(); + if ($this->hasTargetWidth()) { - $this->target->setWidth( - min($this->target->getWidth(), $this->size->getWidth()) + $resized->setWidth( + min($this->target->getWidth(), $this->original->getWidth()) ); } if ($this->hasTargetHeight()) { - $this->target->setHeight( - min($this->target->getHeight(), $this->size->getHeight()) + $resized->setHeight( + min($this->target->getHeight(), $this->original->getHeight()) ); } - return $this->resize(); + return $resized; } public function scale(): SizeInterface { + $resized = $this->copyOriginal(); + if ($this->hasTargetWidth() && $this->hasTargetHeight()) { - $this->target->setWidth(min( + $resized->setWidth(min( $this->getProportionalWidth(), $this->target->getWidth() )); - $this->target->setHeight(min( + $resized->setHeight(min( $this->getProportionalHeight(), $this->target->getHeight() )); } elseif ($this->hasTargetWidth()) { - $this->target->setHeight($this->getProportionalHeight()); + $resized->setWidth($this->target->getWidth()); + $resized->setHeight($this->getProportionalHeight()); } elseif ($this->hasTargetHeight()) { - $this->target->setWidth($this->getProportionalWidth()); + $resized->setWidth($this->getProportionalWidth()); + $resized->setHeight($this->target->getHeight()); } - return $this->resize(); + return $resized; } public function scaleDown(): SizeInterface { + $resized = $this->copyOriginal(); + if ($this->hasTargetWidth() && $this->hasTargetHeight()) { - $this->target->setWidth(min( + $resized->setWidth(min( $this->getProportionalWidth(), $this->target->getWidth(), - $this->size->getWidth() + $this->original->getWidth() )); - $this->target->setHeight(min( + $resized->setHeight(min( $this->getProportionalHeight(), $this->target->getHeight(), - $this->size->getHeight() + $this->original->getHeight() )); } elseif ($this->hasTargetWidth()) { - $this->target->setWidth(min( + $resized->setWidth(min( $this->target->getWidth(), - $this->size->getWidth() + $this->original->getWidth() )); - $this->target->setHeight(min( + $resized->setHeight(min( $this->getProportionalHeight(), - $this->size->getHeight() + $this->original->getHeight() )); } elseif ($this->hasTargetHeight()) { - $this->target->setWidth(min( + $resized->setWidth(min( $this->getProportionalWidth(), - $this->size->getWidth() + $this->original->getWidth() )); - $this->target->setHeight(min( + $resized->setHeight(min( $this->target->getHeight(), - $this->size->getHeight() + $this->original->getHeight() )); } - return $this->resize(); - } - - protected function getProportionalWidth(): int - { - if (! $this->hasTargetHeight()) { - return $this->size->getWidth(); - } - - return (int) round($this->target->getHeight() * $this->size->getAspectRatio()); - } - - protected function getProportionalHeight(): int - { - if (! $this->hasTargetWidth()) { - return $this->size->getHeight(); - } - - return (int) round($this->target->getWidth() / $this->size->getAspectRatio()); + return $resized; } } diff --git a/src/Geometry/Size.php b/src/Geometry/Size.php index c1adef14..70aee3e1 100644 --- a/src/Geometry/Size.php +++ b/src/Geometry/Size.php @@ -38,6 +38,13 @@ class Size implements SizeInterface return $this->pivot; } + public function setPivot(PointInterface $pivot): self + { + $this->pivot = $pivot; + + return $this; + } + public function setWidth(int $width): SizeInterface { $this->width = $width; diff --git a/tests/Geometry/ResizerTest.php b/tests/Geometry/ResizerTest.php index 5395aaff..9c84ee91 100644 --- a/tests/Geometry/ResizerTest.php +++ b/tests/Geometry/ResizerTest.php @@ -8,6 +8,13 @@ use PHPUnit\Framework\TestCase; class ResizerTest extends TestCase { + public function testConstructor(): void + { + $size = new Size(300, 200); + $resizer = new Resizer($size); + $this->assertInstanceOf(Resizer::class, $resizer); + } + public function testSetTargetSizeByArray() { $resizer = new Resizer(new Size(300, 200)); @@ -63,22 +70,16 @@ class ResizerTest extends TestCase $resizer = new Resizer($size); $resizer->width(150); $result = $resizer->resize(); - $original = $resizer->getSize(); $this->assertEquals(150, $result->getWidth()); $this->assertEquals(200, $result->getHeight()); - $this->assertEquals(300, $original->getWidth()); - $this->assertEquals(200, $original->getHeight()); $size = new Size(300, 200); $resizer = new Resizer($size); $resizer->width(20); $resizer->height(10); $result = $resizer->resize(); - $original = $resizer->getSize(); $this->assertEquals(20, $result->getWidth()); $this->assertEquals(10, $result->getHeight()); - $this->assertEquals(300, $original->getWidth()); - $this->assertEquals(200, $original->getHeight()); } public function testResizeDown() @@ -367,4 +368,5 @@ class ResizerTest extends TestCase $this->assertEquals(13, $result->getWidth()); $this->assertEquals(10, $result->getHeight()); } + } diff --git a/tests/Geometry/SizeTest.php b/tests/Geometry/SizeTest.php index 47090238..37ab296a 100644 --- a/tests/Geometry/SizeTest.php +++ b/tests/Geometry/SizeTest.php @@ -143,4 +143,47 @@ class SizeTest extends TestCase $this->assertInstanceOf(Size::class, $result); } + + public function testRelativePosition(): void + { + $container = new Size(800, 600); + $input = new Size(200, 100); + $container->align('top-left'); + $input->align('top-left'); + $pos = $container->relativePosition($input); + $this->assertEquals(0, $pos->getX()); + $this->assertEquals(0, $pos->getY()); + + $container = new Size(800, 600); + $input = new Size(200, 100); + $container->align('center'); + $input->align('top-left'); + $pos = $container->relativePosition($input); + $this->assertEquals(400, $pos->getX()); + $this->assertEquals(300, $pos->getY()); + + $container = new Size(800, 600); + $input = new Size(200, 100); + $container->align('bottom-right'); + $input->align('top-right'); + $pos = $container->relativePosition($input); + $this->assertEquals(600, $pos->getX()); + $this->assertEquals(600, $pos->getY()); + + $container = new Size(800, 600); + $input = new Size(200, 100); + $container->align('center'); + $input->align('center'); + $pos = $container->relativePosition($input); + $this->assertEquals(300, $pos->getX()); + $this->assertEquals(250, $pos->getY()); + + $container = new Size(100, 200); + $input = new Size(100, 100); + $container->align('center'); + $input->align('center'); + $pos = $container->relativePosition($input); + $this->assertEquals(0, $pos->getX()); + $this->assertEquals(50, $pos->getY()); + } } From de343e197947ca0cb108f6c570b0e9ead0f1ef3d Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 7 Nov 2021 16:15:30 +0000 Subject: [PATCH 046/476] Renamed relativePosition --- src/Drivers/Gd/Modifiers/PlaceModifier.php | 2 +- .../Imagick/Modifiers/PlaceModifier.php | 2 +- src/Geometry/Resizer.php | 47 +++++++++++++++++-- src/Geometry/Size.php | 2 +- tests/Geometry/ResizerTest.php | 1 - tests/Geometry/SizeTest.php | 12 ++--- 6 files changed, 51 insertions(+), 15 deletions(-) diff --git a/src/Drivers/Gd/Modifiers/PlaceModifier.php b/src/Drivers/Gd/Modifiers/PlaceModifier.php index c9576f2c..92f8aff1 100644 --- a/src/Drivers/Gd/Modifiers/PlaceModifier.php +++ b/src/Drivers/Gd/Modifiers/PlaceModifier.php @@ -61,6 +61,6 @@ class PlaceModifier implements ModifierInterface $image_size = $image->getSize()->align($this->position, $this->offset_x, $this->offset_y); $watermark_size = $watermark->getSize()->align($this->position); - return $image_size->relativePosition($watermark_size); + return $image_size->getRelativePositionTo($watermark_size); } } diff --git a/src/Drivers/Imagick/Modifiers/PlaceModifier.php b/src/Drivers/Imagick/Modifiers/PlaceModifier.php index e91714bc..efc8a166 100644 --- a/src/Drivers/Imagick/Modifiers/PlaceModifier.php +++ b/src/Drivers/Imagick/Modifiers/PlaceModifier.php @@ -53,6 +53,6 @@ class PlaceModifier implements ModifierInterface $image_size = $image->getSize()->align($this->position, $this->offset_x, $this->offset_y); $watermark_size = $watermark->getSize()->align($this->position); - return $image_size->relativePosition($watermark_size); + return $image_size->getRelativePositionTo($watermark_size); } } diff --git a/src/Geometry/Resizer.php b/src/Geometry/Resizer.php index 6132d58c..345afef0 100644 --- a/src/Geometry/Resizer.php +++ b/src/Geometry/Resizer.php @@ -4,6 +4,43 @@ namespace Intervention\Image\Geometry; use Intervention\Image\Interfaces\SizeInterface; +/* + +modes: fill, contain, cover +resize($width, $height, $mode, $only_reduce) +resize(function($size) { + $size->width(300); + $size->contain(); + $size->reduce(); +}); + +- resize +- resizeDown +- scale +- scaleDown +- contain +- containDown +- cover +- coverDown + +- resize +- resizeDown +- scale +- scaleDown +- fit(contain|cover) +- fitDown(contain|cover) + +- resize +- resizeDown +- scale +- scaleDown +- fit +- fitDown +- pad +- padDown + + */ + class Resizer { /** @@ -31,11 +68,6 @@ class Resizer $this->target = new Size(0, 0); } - protected function copyOriginal(): SizeInterface - { - return new Size($this->original->getWidth(), $this->original->getHeight()); - } - protected function hasTargetWidth(): bool { return $this->target->getWidth() > 0; @@ -104,6 +136,11 @@ class Resizer return (int) round($this->target->getWidth() / $this->original->getAspectRatio()); } + protected function copyOriginal(): SizeInterface + { + return new Size($this->original->getWidth(), $this->original->getHeight()); + } + public function resize(): SizeInterface { $resized = $this->copyOriginal(); diff --git a/src/Geometry/Size.php b/src/Geometry/Size.php index 70aee3e1..735439f2 100644 --- a/src/Geometry/Size.php +++ b/src/Geometry/Size.php @@ -191,7 +191,7 @@ class Size implements SizeInterface * @param Size $size * @return Point */ - public function relativePosition(Size $size): Point + public function getRelativePositionTo(Size $size): Point { $x = $this->getPivot()->getX() - $size->getPivot()->getX(); $y = $this->getPivot()->getY() - $size->getPivot()->getY(); diff --git a/tests/Geometry/ResizerTest.php b/tests/Geometry/ResizerTest.php index 9c84ee91..13f2219b 100644 --- a/tests/Geometry/ResizerTest.php +++ b/tests/Geometry/ResizerTest.php @@ -368,5 +368,4 @@ class ResizerTest extends TestCase $this->assertEquals(13, $result->getWidth()); $this->assertEquals(10, $result->getHeight()); } - } diff --git a/tests/Geometry/SizeTest.php b/tests/Geometry/SizeTest.php index 37ab296a..0dc55166 100644 --- a/tests/Geometry/SizeTest.php +++ b/tests/Geometry/SizeTest.php @@ -144,13 +144,13 @@ class SizeTest extends TestCase $this->assertInstanceOf(Size::class, $result); } - public function testRelativePosition(): void + public function testgetRelativePositionTo(): void { $container = new Size(800, 600); $input = new Size(200, 100); $container->align('top-left'); $input->align('top-left'); - $pos = $container->relativePosition($input); + $pos = $container->getRelativePositionTo($input); $this->assertEquals(0, $pos->getX()); $this->assertEquals(0, $pos->getY()); @@ -158,7 +158,7 @@ class SizeTest extends TestCase $input = new Size(200, 100); $container->align('center'); $input->align('top-left'); - $pos = $container->relativePosition($input); + $pos = $container->getRelativePositionTo($input); $this->assertEquals(400, $pos->getX()); $this->assertEquals(300, $pos->getY()); @@ -166,7 +166,7 @@ class SizeTest extends TestCase $input = new Size(200, 100); $container->align('bottom-right'); $input->align('top-right'); - $pos = $container->relativePosition($input); + $pos = $container->getRelativePositionTo($input); $this->assertEquals(600, $pos->getX()); $this->assertEquals(600, $pos->getY()); @@ -174,7 +174,7 @@ class SizeTest extends TestCase $input = new Size(200, 100); $container->align('center'); $input->align('center'); - $pos = $container->relativePosition($input); + $pos = $container->getRelativePositionTo($input); $this->assertEquals(300, $pos->getX()); $this->assertEquals(250, $pos->getY()); @@ -182,7 +182,7 @@ class SizeTest extends TestCase $input = new Size(100, 100); $container->align('center'); $input->align('center'); - $pos = $container->relativePosition($input); + $pos = $container->getRelativePositionTo($input); $this->assertEquals(0, $pos->getX()); $this->assertEquals(50, $pos->getY()); } From aaaf78c7dc8147fb26308e42f670d912ce804e4c Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Mon, 8 Nov 2021 16:04:14 +0000 Subject: [PATCH 047/476] Resizing --- src/Drivers/Abstract/AbstractImage.php | 7 + .../Gd/Modifiers/CropResizeModifier.php | 126 ++++++++++++++++++ src/Drivers/Gd/Modifiers/PlaceModifier.php | 4 +- .../Imagick/Modifiers/PlaceModifier.php | 4 +- src/Geometry/Size.php | 14 +- tests/Geometry/SizeTest.php | 73 +++++++--- 6 files changed, 203 insertions(+), 25 deletions(-) create mode 100644 src/Drivers/Gd/Modifiers/CropResizeModifier.php diff --git a/src/Drivers/Abstract/AbstractImage.php b/src/Drivers/Abstract/AbstractImage.php index b315f0b1..ab789ce3 100644 --- a/src/Drivers/Abstract/AbstractImage.php +++ b/src/Drivers/Abstract/AbstractImage.php @@ -166,6 +166,13 @@ abstract class AbstractImage ); } + public function fit(int $width, int $height, string $position = 'center'): ImageInterface + { + return $this->modify( + $this->resolveDriverClass('Modifiers\CropResizeModifier', $width, $height, $position) + ); + } + public function place($element, string $position = 'top-left', int $offset_x = 0, int $offset_y = 0): ImageInterface { return $this->modify( diff --git a/src/Drivers/Gd/Modifiers/CropResizeModifier.php b/src/Drivers/Gd/Modifiers/CropResizeModifier.php new file mode 100644 index 00000000..a1b6539c --- /dev/null +++ b/src/Drivers/Gd/Modifiers/CropResizeModifier.php @@ -0,0 +1,126 @@ +width = $width; + $this->height = $height; + $this->position = $position; + } + + public function apply(ImageInterface $image): ImageInterface + { + echo "
";
+        var_dump($this->getCropSize($image));
+        var_dump($this->getResizeSize($image));
+        echo "
"; + exit; + + // foreach ($image as $frame) { + // $this->modify($frame); + // } + + return $image; + } + + protected function getCropSize(ImageInterface $image): SizeInterface + { + $resizer = new Resizer(new Size($this->width, $this->height)); + $resizer->width($image->width()); + $resizer->height($image->height()); + + return $resizer->scale()->align($this->position); + } + + protected function getResizeSize(ImageInterface $image): SizeInterface + { + $resizer = new Resizer($this->getCropSize($image)); + $resizer->width($this->width); + $resizer->height($this->height); + + return $resizer->scale()->align($this->position); + } + + /** + * Wrapper function for 'imagecopyresampled' + * + * @param FrameInterface $frame + * @param int $dst_x + * @param int $dst_y + * @param int $src_x + * @param int $src_y + * @param int $dst_w + * @param int $dst_h + * @param int $src_w + * @param int $src_h + * @return void + */ + protected function modify(FrameInterface $frame): void + { + // create new image + $modified = imagecreatetruecolor( + $this->resizeTo->getWidth(), + $this->resizeTo->getHeight() + ); + + // get current image + $gd = $frame->getCore(); + + // preserve transparency + $transIndex = imagecolortransparent($gd); + + if ($transIndex != -1) { + $rgba = imagecolorsforindex($modified, $transIndex); + $transColor = imagecolorallocatealpha($modified, $rgba['red'], $rgba['green'], $rgba['blue'], 127); + imagefill($modified, 0, 0, $transColor); + imagecolortransparent($modified, $transColor); + } else { + imagealphablending($modified, false); + imagesavealpha($modified, true); + } + + // copy content from resource + $result = imagecopyresampled( + $modified, + $gd, + $this->resizeTo->getPivot()->getX(), + $this->resizeTo->getPivot()->getY(), + $this->cropTo->getPivot()->getX(), + $this->cropTo->getPivot()->getY(), + $this->resizeTo->getWidth(), + $this->resizeTo->getHeight(), + $this->cropTo->getWidth(), + $this->cropTo->getHeight() + ); + + imagedestroy($gd); + + // set new content as recource + $frame->setCore($modified); + } +} diff --git a/src/Drivers/Gd/Modifiers/PlaceModifier.php b/src/Drivers/Gd/Modifiers/PlaceModifier.php index 92f8aff1..596ee79f 100644 --- a/src/Drivers/Gd/Modifiers/PlaceModifier.php +++ b/src/Drivers/Gd/Modifiers/PlaceModifier.php @@ -58,8 +58,8 @@ class PlaceModifier implements ModifierInterface protected function getPosition(Image $image, Image $watermark): Point { - $image_size = $image->getSize()->align($this->position, $this->offset_x, $this->offset_y); - $watermark_size = $watermark->getSize()->align($this->position); + $image_size = $image->getSize()->alignPivot($this->position, $this->offset_x, $this->offset_y); + $watermark_size = $watermark->getSize()->alignPivot($this->position); return $image_size->getRelativePositionTo($watermark_size); } diff --git a/src/Drivers/Imagick/Modifiers/PlaceModifier.php b/src/Drivers/Imagick/Modifiers/PlaceModifier.php index efc8a166..c043adf0 100644 --- a/src/Drivers/Imagick/Modifiers/PlaceModifier.php +++ b/src/Drivers/Imagick/Modifiers/PlaceModifier.php @@ -50,8 +50,8 @@ class PlaceModifier implements ModifierInterface protected function getPosition(Image $image, Image $watermark): Point { - $image_size = $image->getSize()->align($this->position, $this->offset_x, $this->offset_y); - $watermark_size = $watermark->getSize()->align($this->position); + $image_size = $image->getSize()->alignPivot($this->position, $this->offset_x, $this->offset_y); + $watermark_size = $watermark->getSize()->alignPivot($this->position); return $image_size->getRelativePositionTo($watermark_size); } diff --git a/src/Geometry/Size.php b/src/Geometry/Size.php index 735439f2..18f6a8e3 100644 --- a/src/Geometry/Size.php +++ b/src/Geometry/Size.php @@ -106,7 +106,7 @@ class Size implements SizeInterface * @param int $offset_y * @return Size */ - public function align(string $position, int $offset_x = 0, int $offset_y = 0): self + public function alignPivot(string $position, int $offset_x = 0, int $offset_y = 0): self { switch (strtolower($position)) { case 'top': @@ -184,6 +184,18 @@ class Size implements SizeInterface return $this; } + public function alignPivotTo(Size $size, string $position): self + { + $reference = new Size($size->getWidth(), $size->getHeight()); + $reference->alignPivot($position); + + $this->alignPivot($position)->setPivot( + $reference->getRelativePositionTo($this) + ); + + return $this; + } + /** * Calculate the relative position to another Size * based on the pivot point settings of both sizes. diff --git a/tests/Geometry/SizeTest.php b/tests/Geometry/SizeTest.php index 0dc55166..40eb5ecb 100644 --- a/tests/Geometry/SizeTest.php +++ b/tests/Geometry/SizeTest.php @@ -99,89 +99,122 @@ class SizeTest extends TestCase $this->assertTrue($box->isPortrait()); } - public function testAlign(): void + public function testAlignPivot(): void { $box = new Size(640, 480); $this->assertEquals(0, $box->getPivot()->getX()); $this->assertEquals(0, $box->getPivot()->getY()); - $box->align('top-left', 3, 3); + $box->alignPivot('top-left', 3, 3); $this->assertEquals(3, $box->getPivot()->getX()); $this->assertEquals(3, $box->getPivot()->getY()); - $box->align('top', 3, 3); + $box->alignPivot('top', 3, 3); $this->assertEquals(320, $box->getPivot()->getX()); $this->assertEquals(3, $box->getPivot()->getY()); - $box->align('top-right', 3, 3); + $box->alignPivot('top-right', 3, 3); $this->assertEquals(637, $box->getPivot()->getX()); $this->assertEquals(3, $box->getPivot()->getY()); - $box->align('left', 3, 3); + $box->alignPivot('left', 3, 3); $this->assertEquals(3, $box->getPivot()->getX()); $this->assertEquals(240, $box->getPivot()->getY()); - $box->align('center', 3, 3); + $box->alignPivot('center', 3, 3); $this->assertEquals(323, $box->getPivot()->getX()); $this->assertEquals(243, $box->getPivot()->getY()); - $box->align('right', 3, 3); + $box->alignPivot('right', 3, 3); $this->assertEquals(637, $box->getPivot()->getX()); $this->assertEquals(240, $box->getPivot()->getY()); - $box->align('bottom-left', 3, 3); + $box->alignPivot('bottom-left', 3, 3); $this->assertEquals(3, $box->getPivot()->getX()); $this->assertEquals(477, $box->getPivot()->getY()); - $box->align('bottom', 3, 3); + $box->alignPivot('bottom', 3, 3); $this->assertEquals(320, $box->getPivot()->getX()); $this->assertEquals(477, $box->getPivot()->getY()); - $result = $box->align('bottom-right', 3, 3); + $result = $box->alignPivot('bottom-right', 3, 3); $this->assertEquals(637, $box->getPivot()->getX()); $this->assertEquals(477, $box->getPivot()->getY()); $this->assertInstanceOf(Size::class, $result); } + public function testAlignPivotTo(): void + { + $container = new Size(800, 600); + $size = new Size(200, 100); + $size->alignPivotTo($container, 'center'); + $this->assertEquals(300, $size->getPivot()->getX()); + $this->assertEquals(250, $size->getPivot()->getY()); + + $container = new Size(800, 600); + $size = new Size(100, 100); + $size->alignPivotTo($container, 'center'); + $this->assertEquals(350, $size->getPivot()->getX()); + $this->assertEquals(250, $size->getPivot()->getY()); + + $container = new Size(800, 600); + $size = new Size(800, 600); + $size->alignPivotTo($container, 'center'); + $this->assertEquals(0, $size->getPivot()->getX()); + $this->assertEquals(0, $size->getPivot()->getY()); + + $container = new Size(100, 100); + $size = new Size(800, 600); + $size->alignPivotTo($container, 'center'); + $this->assertEquals(-350, $size->getPivot()->getX()); + $this->assertEquals(-250, $size->getPivot()->getY()); + + $container = new Size(100, 100); + $size = new Size(800, 600); + $size->alignPivotTo($container, 'bottom-right'); + $this->assertEquals(-700, $size->getPivot()->getX()); + $this->assertEquals(-500, $size->getPivot()->getY()); + } + public function testgetRelativePositionTo(): void { $container = new Size(800, 600); $input = new Size(200, 100); - $container->align('top-left'); - $input->align('top-left'); + $container->alignPivot('top-left'); + $input->alignPivot('top-left'); $pos = $container->getRelativePositionTo($input); $this->assertEquals(0, $pos->getX()); $this->assertEquals(0, $pos->getY()); $container = new Size(800, 600); $input = new Size(200, 100); - $container->align('center'); - $input->align('top-left'); + $container->alignPivot('center'); + $input->alignPivot('top-left'); $pos = $container->getRelativePositionTo($input); $this->assertEquals(400, $pos->getX()); $this->assertEquals(300, $pos->getY()); $container = new Size(800, 600); $input = new Size(200, 100); - $container->align('bottom-right'); - $input->align('top-right'); + $container->alignPivot('bottom-right'); + $input->alignPivot('top-right'); $pos = $container->getRelativePositionTo($input); $this->assertEquals(600, $pos->getX()); $this->assertEquals(600, $pos->getY()); $container = new Size(800, 600); $input = new Size(200, 100); - $container->align('center'); - $input->align('center'); + $container->alignPivot('center'); + $input->alignPivot('center'); $pos = $container->getRelativePositionTo($input); $this->assertEquals(300, $pos->getX()); $this->assertEquals(250, $pos->getY()); $container = new Size(100, 200); $input = new Size(100, 100); - $container->align('center'); - $input->align('center'); + $container->alignPivot('center'); + $input->alignPivot('center'); $pos = $container->getRelativePositionTo($input); $this->assertEquals(0, $pos->getX()); $this->assertEquals(50, $pos->getY()); From 50972167e9b4e9936311a844bc0e12ba90e46d48 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Mon, 8 Nov 2021 16:41:41 +0000 Subject: [PATCH 048/476] refactoring --- src/Drivers/Abstract/AbstractImage.php | 4 +- .../Gd/Modifiers/CropResizeModifier.php | 68 ++++++++++--------- src/Geometry/Resizer.php | 10 +++ src/Traits/CanResizeGeometrically.php | 14 ++++ 4 files changed, 62 insertions(+), 34 deletions(-) create mode 100644 src/Traits/CanResizeGeometrically.php diff --git a/src/Drivers/Abstract/AbstractImage.php b/src/Drivers/Abstract/AbstractImage.php index ab789ce3..e275881a 100644 --- a/src/Drivers/Abstract/AbstractImage.php +++ b/src/Drivers/Abstract/AbstractImage.php @@ -168,8 +168,10 @@ abstract class AbstractImage public function fit(int $width, int $height, string $position = 'center'): ImageInterface { + $size = new Size($width, $height); + return $this->modify( - $this->resolveDriverClass('Modifiers\CropResizeModifier', $width, $height, $position) + $this->resolveDriverClass('Modifiers\CropResizeModifier', $size, $position) ); } diff --git a/src/Drivers/Gd/Modifiers/CropResizeModifier.php b/src/Drivers/Gd/Modifiers/CropResizeModifier.php index a1b6539c..c03c441a 100644 --- a/src/Drivers/Gd/Modifiers/CropResizeModifier.php +++ b/src/Drivers/Gd/Modifiers/CropResizeModifier.php @@ -3,10 +3,12 @@ namespace Intervention\Image\Drivers\Gd\Modifiers; use Intervention\Image\Geometry\Resizer; +use Intervention\Image\Geometry\Size; use Intervention\Image\Interfaces\FrameInterface; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; use Intervention\Image\Interfaces\SizeInterface; +use Intervention\Image\Traits\CanResizeGeometrically; /* @@ -22,48 +24,48 @@ use Intervention\Image\Interfaces\SizeInterface; class CropResizeModifier implements ModifierInterface { - protected $width; - protected $height; + use CanResizeGeometrically; + + protected $target; protected $position; - public function __construct(int $width, int $height, string $position) + public function __construct(SizeInterface $target, string $position = 'top-left') { - $this->width = $width; - $this->height = $height; + $this->target = $target; $this->position = $position; } public function apply(ImageInterface $image): ImageInterface { - echo "
";
-        var_dump($this->getCropSize($image));
-        var_dump($this->getResizeSize($image));
-        echo "
"; - exit; + $crop = $this->getCropSize($image); + $resize = $this->getResizeSize($image); - // foreach ($image as $frame) { - // $this->modify($frame); - // } + foreach ($image as $frame) { + $this->modify($frame, $crop, $resize); + } return $image; } protected function getCropSize(ImageInterface $image): SizeInterface { - $resizer = new Resizer(new Size($this->width, $this->height)); - $resizer->width($image->width()); - $resizer->height($image->height()); + $size = $this->resizeGeometrically($this->target) + ->toWidth($image->width()) + ->toHeight($image->height()) + ->scale(); - return $resizer->scale()->align($this->position); + return $size->alignPivotTo( + $image->getSize()->alignPivot($this->position), + $this->position + ); } protected function getResizeSize(ImageInterface $image): SizeInterface { - $resizer = new Resizer($this->getCropSize($image)); - $resizer->width($this->width); - $resizer->height($this->height); - - return $resizer->scale()->align($this->position); + return $this->resizeGeometrically($this->getCropSize($image)) + ->toWidth($this->target->getWidth()) + ->toHeight($this->target->getHeight()) + ->scale(); } /** @@ -80,12 +82,12 @@ class CropResizeModifier implements ModifierInterface * @param int $src_h * @return void */ - protected function modify(FrameInterface $frame): void + protected function modify(FrameInterface $frame, SizeInterface $crop, SizeInterface $resize): void { // create new image $modified = imagecreatetruecolor( - $this->resizeTo->getWidth(), - $this->resizeTo->getHeight() + $resize->getWidth(), + $resize->getHeight() ); // get current image @@ -108,14 +110,14 @@ class CropResizeModifier implements ModifierInterface $result = imagecopyresampled( $modified, $gd, - $this->resizeTo->getPivot()->getX(), - $this->resizeTo->getPivot()->getY(), - $this->cropTo->getPivot()->getX(), - $this->cropTo->getPivot()->getY(), - $this->resizeTo->getWidth(), - $this->resizeTo->getHeight(), - $this->cropTo->getWidth(), - $this->cropTo->getHeight() + $resize->getPivot()->getX(), + $resize->getPivot()->getY(), + $crop->getPivot()->getX(), + $crop->getPivot()->getY(), + $resize->getWidth(), + $resize->getHeight(), + $crop->getWidth(), + $crop->getHeight() ); imagedestroy($gd); diff --git a/src/Geometry/Resizer.php b/src/Geometry/Resizer.php index 345afef0..92c43668 100644 --- a/src/Geometry/Resizer.php +++ b/src/Geometry/Resizer.php @@ -92,6 +92,16 @@ class Resizer return $this; } + public function toWidth(int $width): self + { + return $this->height($width); + } + + public function toHeight(int $height): self + { + return $this->height($height); + } + public function setTargetSizeByArray(array $arguments): self { if (isset($arguments[0]) && is_callable($arguments[0])) { diff --git a/src/Traits/CanResizeGeometrically.php b/src/Traits/CanResizeGeometrically.php new file mode 100644 index 00000000..9a66a068 --- /dev/null +++ b/src/Traits/CanResizeGeometrically.php @@ -0,0 +1,14 @@ + Date: Mon, 8 Nov 2021 16:48:34 +0000 Subject: [PATCH 049/476] ResizeModifier & FitModifier relation --- src/Drivers/Abstract/AbstractImage.php | 2 +- ...CropResizeModifier.php => FitModifier.php} | 60 +------------------ src/Drivers/Gd/Modifiers/ResizeModifier.php | 55 +++++------------ 3 files changed, 16 insertions(+), 101 deletions(-) rename src/Drivers/Gd/Modifiers/{CropResizeModifier.php => FitModifier.php} (50%) diff --git a/src/Drivers/Abstract/AbstractImage.php b/src/Drivers/Abstract/AbstractImage.php index e275881a..f734c03b 100644 --- a/src/Drivers/Abstract/AbstractImage.php +++ b/src/Drivers/Abstract/AbstractImage.php @@ -171,7 +171,7 @@ abstract class AbstractImage $size = new Size($width, $height); return $this->modify( - $this->resolveDriverClass('Modifiers\CropResizeModifier', $size, $position) + $this->resolveDriverClass('Modifiers\FitModifier', $size, $position) ); } diff --git a/src/Drivers/Gd/Modifiers/CropResizeModifier.php b/src/Drivers/Gd/Modifiers/FitModifier.php similarity index 50% rename from src/Drivers/Gd/Modifiers/CropResizeModifier.php rename to src/Drivers/Gd/Modifiers/FitModifier.php index c03c441a..2f43650c 100644 --- a/src/Drivers/Gd/Modifiers/CropResizeModifier.php +++ b/src/Drivers/Gd/Modifiers/FitModifier.php @@ -22,7 +22,7 @@ use Intervention\Image\Traits\CanResizeGeometrically; */ -class CropResizeModifier implements ModifierInterface +class FitModifier extends ResizeModifier implements ModifierInterface { use CanResizeGeometrically; @@ -67,62 +67,4 @@ class CropResizeModifier implements ModifierInterface ->toHeight($this->target->getHeight()) ->scale(); } - - /** - * Wrapper function for 'imagecopyresampled' - * - * @param FrameInterface $frame - * @param int $dst_x - * @param int $dst_y - * @param int $src_x - * @param int $src_y - * @param int $dst_w - * @param int $dst_h - * @param int $src_w - * @param int $src_h - * @return void - */ - protected function modify(FrameInterface $frame, SizeInterface $crop, SizeInterface $resize): void - { - // create new image - $modified = imagecreatetruecolor( - $resize->getWidth(), - $resize->getHeight() - ); - - // get current image - $gd = $frame->getCore(); - - // preserve transparency - $transIndex = imagecolortransparent($gd); - - if ($transIndex != -1) { - $rgba = imagecolorsforindex($modified, $transIndex); - $transColor = imagecolorallocatealpha($modified, $rgba['red'], $rgba['green'], $rgba['blue'], 127); - imagefill($modified, 0, 0, $transColor); - imagecolortransparent($modified, $transColor); - } else { - imagealphablending($modified, false); - imagesavealpha($modified, true); - } - - // copy content from resource - $result = imagecopyresampled( - $modified, - $gd, - $resize->getPivot()->getX(), - $resize->getPivot()->getY(), - $crop->getPivot()->getX(), - $crop->getPivot()->getY(), - $resize->getWidth(), - $resize->getHeight(), - $crop->getWidth(), - $crop->getHeight() - ); - - imagedestroy($gd); - - // set new content as recource - $frame->setCore($modified); - } } diff --git a/src/Drivers/Gd/Modifiers/ResizeModifier.php b/src/Drivers/Gd/Modifiers/ResizeModifier.php index 673fe9fc..1944e9aa 100644 --- a/src/Drivers/Gd/Modifiers/ResizeModifier.php +++ b/src/Drivers/Gd/Modifiers/ResizeModifier.php @@ -9,11 +9,6 @@ use Intervention\Image\Interfaces\SizeInterface; class ResizeModifier implements ModifierInterface { - /** - * Target size - * - * @var SizeInterface - */ protected $target; public function __construct(SizeInterface $target) @@ -24,41 +19,19 @@ class ResizeModifier implements ModifierInterface public function apply(ImageInterface $image): ImageInterface { foreach ($image as $frame) { - $framesize = $frame->getSize(); - $this->modify( - $frame, - 0, - 0, - 0, - 0, - $this->target->getWidth(), - $this->target->getHeight(), - $framesize->getWidth(), - $framesize->getHeight() - ); + $this->modify($frame, $frame->getSize(), $this->target); } return $image; } - /** - * Wrapper function for 'imagecopyresampled' - * - * @param Image $image - * @param int $dst_x - * @param int $dst_y - * @param int $src_x - * @param int $src_y - * @param int $dst_w - * @param int $dst_h - * @param int $src_w - * @param int $src_h - * @return boolean - */ - protected function modify(FrameInterface $frame, $dst_x, $dst_y, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h) + protected function modify(FrameInterface $frame, SizeInterface $crop, SizeInterface $resize): void { // create new image - $modified = imagecreatetruecolor($dst_w, $dst_h); + $modified = imagecreatetruecolor( + $resize->getWidth(), + $resize->getHeight() + ); // get current image $gd = $frame->getCore(); @@ -80,14 +53,14 @@ class ResizeModifier implements ModifierInterface $result = imagecopyresampled( $modified, $gd, - $dst_x, - $dst_y, - $src_x, - $src_y, - $dst_w, - $dst_h, - $src_w, - $src_h + $resize->getPivot()->getX(), + $resize->getPivot()->getY(), + $crop->getPivot()->getX(), + $crop->getPivot()->getY(), + $resize->getWidth(), + $resize->getHeight(), + $crop->getWidth(), + $crop->getHeight() ); imagedestroy($gd); From 585766b4af48ad91b084cf453e445d8c11717824 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Mon, 8 Nov 2021 19:52:45 +0100 Subject: [PATCH 050/476] FitModifier --- src/Drivers/Abstract/AbstractImage.php | 9 +++++++ src/Drivers/Gd/Modifiers/FitDownModifier.php | 22 ++++++++++++++++ src/Drivers/Gd/Modifiers/FitModifier.php | 27 +++++++++----------- src/Geometry/Resizer.php | 2 +- 4 files changed, 44 insertions(+), 16 deletions(-) create mode 100644 src/Drivers/Gd/Modifiers/FitDownModifier.php diff --git a/src/Drivers/Abstract/AbstractImage.php b/src/Drivers/Abstract/AbstractImage.php index f734c03b..a6a52719 100644 --- a/src/Drivers/Abstract/AbstractImage.php +++ b/src/Drivers/Abstract/AbstractImage.php @@ -175,6 +175,15 @@ abstract class AbstractImage ); } + public function fitDown(int $width, int $height, string $position = 'center'): ImageInterface + { + $size = new Size($width, $height); + + return $this->modify( + $this->resolveDriverClass('Modifiers\FitDownModifier', $size, $position) + ); + } + public function place($element, string $position = 'top-left', int $offset_x = 0, int $offset_y = 0): ImageInterface { return $this->modify( diff --git a/src/Drivers/Gd/Modifiers/FitDownModifier.php b/src/Drivers/Gd/Modifiers/FitDownModifier.php new file mode 100644 index 00000000..143b4bef --- /dev/null +++ b/src/Drivers/Gd/Modifiers/FitDownModifier.php @@ -0,0 +1,22 @@ +resizeGeometrically($this->getCropSize($image)) + ->toWidth($this->target->getWidth()) + ->toHeight($this->target->getHeight()) + ->scaleDown(); + } +} diff --git a/src/Drivers/Gd/Modifiers/FitModifier.php b/src/Drivers/Gd/Modifiers/FitModifier.php index 2f43650c..ba1702ec 100644 --- a/src/Drivers/Gd/Modifiers/FitModifier.php +++ b/src/Drivers/Gd/Modifiers/FitModifier.php @@ -10,18 +10,6 @@ use Intervention\Image\Interfaces\ModifierInterface; use Intervention\Image\Interfaces\SizeInterface; use Intervention\Image\Traits\CanResizeGeometrically; -/* - -# contain -1. Scale (keep aspect ratio) Original to fit Target -2. Scale (keep aspect ratio) Up/Down to fit Target (obsolete) - -# cover -1. Scale (keep aspect ratio) Target to fit Original -2. Scale (keep aspect ratio) Up/Down to fit Target - - */ - class FitModifier extends ResizeModifier implements ModifierInterface { use CanResizeGeometrically; @@ -49,13 +37,22 @@ class FitModifier extends ResizeModifier implements ModifierInterface protected function getCropSize(ImageInterface $image): SizeInterface { + $imagesize = $image->getSize(); + + // auto height $size = $this->resizeGeometrically($this->target) - ->toWidth($image->width()) - ->toHeight($image->height()) + ->toWidth($imagesize->getWidth()) ->scale(); + if (!$size->fitsInto($imagesize)) { + // auto width + $size = $this->resizeGeometrically($this->target) + ->toHeight($imagesize->getHeight()) + ->scale(); + } + return $size->alignPivotTo( - $image->getSize()->alignPivot($this->position), + $imagesize->alignPivot($this->position), $this->position ); } diff --git a/src/Geometry/Resizer.php b/src/Geometry/Resizer.php index 92c43668..b9b79923 100644 --- a/src/Geometry/Resizer.php +++ b/src/Geometry/Resizer.php @@ -94,7 +94,7 @@ class Resizer public function toWidth(int $width): self { - return $this->height($width); + return $this->width($width); } public function toHeight(int $height): self From e168824057957ab1e0b25ae04dbf21386bfee4f6 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Tue, 9 Nov 2021 14:17:18 +0000 Subject: [PATCH 051/476] Fit & Resize Modifier --- src/Drivers/Gd/Modifiers/FitModifier.php | 15 --------------- src/Drivers/Gd/Modifiers/ResizeModifier.php | 18 +++++++++++++++++- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/src/Drivers/Gd/Modifiers/FitModifier.php b/src/Drivers/Gd/Modifiers/FitModifier.php index ba1702ec..87a06059 100644 --- a/src/Drivers/Gd/Modifiers/FitModifier.php +++ b/src/Drivers/Gd/Modifiers/FitModifier.php @@ -12,9 +12,6 @@ use Intervention\Image\Traits\CanResizeGeometrically; class FitModifier extends ResizeModifier implements ModifierInterface { - use CanResizeGeometrically; - - protected $target; protected $position; public function __construct(SizeInterface $target, string $position = 'top-left') @@ -23,18 +20,6 @@ class FitModifier extends ResizeModifier implements ModifierInterface $this->position = $position; } - public function apply(ImageInterface $image): ImageInterface - { - $crop = $this->getCropSize($image); - $resize = $this->getResizeSize($image); - - foreach ($image as $frame) { - $this->modify($frame, $crop, $resize); - } - - return $image; - } - protected function getCropSize(ImageInterface $image): SizeInterface { $imagesize = $image->getSize(); diff --git a/src/Drivers/Gd/Modifiers/ResizeModifier.php b/src/Drivers/Gd/Modifiers/ResizeModifier.php index 1944e9aa..a9f0ab26 100644 --- a/src/Drivers/Gd/Modifiers/ResizeModifier.php +++ b/src/Drivers/Gd/Modifiers/ResizeModifier.php @@ -6,9 +6,12 @@ use Intervention\Image\Interfaces\FrameInterface; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; use Intervention\Image\Interfaces\SizeInterface; +use Intervention\Image\Traits\CanResizeGeometrically; class ResizeModifier implements ModifierInterface { + use CanResizeGeometrically; + protected $target; public function __construct(SizeInterface $target) @@ -18,13 +21,26 @@ class ResizeModifier implements ModifierInterface public function apply(ImageInterface $image): ImageInterface { + $crop = $this->getCropSize($image); + $resize = $this->getResizeSize($image); + foreach ($image as $frame) { - $this->modify($frame, $frame->getSize(), $this->target); + $this->modify($frame, $crop, $resize); } return $image; } + protected function getCropSize(ImageInterface $image): SizeInterface + { + return $image->getSize(); + } + + protected function getResizeSize(ImageInterface $image): SizeInterface + { + return $this->target; + } + protected function modify(FrameInterface $frame, SizeInterface $crop, SizeInterface $resize): void { // create new image From 84e946a588c344d46dc00e16b67518cf6a492c10 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Tue, 9 Nov 2021 14:59:59 +0000 Subject: [PATCH 052/476] Modifiers --- .../Modifiers/AbstractResizeModifier.php | 30 ++++++++++++++++++ src/Drivers/Gd/Modifiers/FitDownModifier.php | 4 --- src/Drivers/Gd/Modifiers/FitModifier.php | 4 --- src/Drivers/Gd/Modifiers/ResizeModifier.php | 22 ++----------- .../Imagick/Modifiers/ResizeModifier.php | 31 ++++++++++--------- tests/Geometry/SizeTest.php | 17 ++++++++++ 6 files changed, 65 insertions(+), 43 deletions(-) create mode 100644 src/Drivers/Abstract/Modifiers/AbstractResizeModifier.php diff --git a/src/Drivers/Abstract/Modifiers/AbstractResizeModifier.php b/src/Drivers/Abstract/Modifiers/AbstractResizeModifier.php new file mode 100644 index 00000000..9f9b04f2 --- /dev/null +++ b/src/Drivers/Abstract/Modifiers/AbstractResizeModifier.php @@ -0,0 +1,30 @@ +target = $target; + } + + protected function getCropSize(ImageInterface $image): SizeInterface + { + return $image->getSize(); + } + + protected function getResizeSize(ImageInterface $image): SizeInterface + { + return $this->target; + } +} diff --git a/src/Drivers/Gd/Modifiers/FitDownModifier.php b/src/Drivers/Gd/Modifiers/FitDownModifier.php index 143b4bef..18802f22 100644 --- a/src/Drivers/Gd/Modifiers/FitDownModifier.php +++ b/src/Drivers/Gd/Modifiers/FitDownModifier.php @@ -2,13 +2,9 @@ namespace Intervention\Image\Drivers\Gd\Modifiers; -use Intervention\Image\Geometry\Resizer; -use Intervention\Image\Geometry\Size; -use Intervention\Image\Interfaces\FrameInterface; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; use Intervention\Image\Interfaces\SizeInterface; -use Intervention\Image\Traits\CanResizeGeometrically; class FitDownModifier extends FitModifier implements ModifierInterface { diff --git a/src/Drivers/Gd/Modifiers/FitModifier.php b/src/Drivers/Gd/Modifiers/FitModifier.php index 87a06059..6910381c 100644 --- a/src/Drivers/Gd/Modifiers/FitModifier.php +++ b/src/Drivers/Gd/Modifiers/FitModifier.php @@ -2,13 +2,9 @@ namespace Intervention\Image\Drivers\Gd\Modifiers; -use Intervention\Image\Geometry\Resizer; -use Intervention\Image\Geometry\Size; -use Intervention\Image\Interfaces\FrameInterface; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; use Intervention\Image\Interfaces\SizeInterface; -use Intervention\Image\Traits\CanResizeGeometrically; class FitModifier extends ResizeModifier implements ModifierInterface { diff --git a/src/Drivers/Gd/Modifiers/ResizeModifier.php b/src/Drivers/Gd/Modifiers/ResizeModifier.php index a9f0ab26..ff9a185b 100644 --- a/src/Drivers/Gd/Modifiers/ResizeModifier.php +++ b/src/Drivers/Gd/Modifiers/ResizeModifier.php @@ -2,23 +2,15 @@ namespace Intervention\Image\Drivers\Gd\Modifiers; +use Intervention\Image\Drivers\Abstract\Modifiers\AbstractResizeModifier; use Intervention\Image\Interfaces\FrameInterface; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; use Intervention\Image\Interfaces\SizeInterface; use Intervention\Image\Traits\CanResizeGeometrically; -class ResizeModifier implements ModifierInterface +class ResizeModifier extends AbstractResizeModifier implements ModifierInterface { - use CanResizeGeometrically; - - protected $target; - - public function __construct(SizeInterface $target) - { - $this->target = $target; - } - public function apply(ImageInterface $image): ImageInterface { $crop = $this->getCropSize($image); @@ -31,16 +23,6 @@ class ResizeModifier implements ModifierInterface return $image; } - protected function getCropSize(ImageInterface $image): SizeInterface - { - return $image->getSize(); - } - - protected function getResizeSize(ImageInterface $image): SizeInterface - { - return $this->target; - } - protected function modify(FrameInterface $frame, SizeInterface $crop, SizeInterface $resize): void { // create new image diff --git a/src/Drivers/Imagick/Modifiers/ResizeModifier.php b/src/Drivers/Imagick/Modifiers/ResizeModifier.php index ee9de755..bcccd01f 100644 --- a/src/Drivers/Imagick/Modifiers/ResizeModifier.php +++ b/src/Drivers/Imagick/Modifiers/ResizeModifier.php @@ -2,31 +2,32 @@ namespace Intervention\Image\Drivers\Imagick\Modifiers; +use Intervention\Image\Drivers\Abstract\Modifiers\AbstractResizeModifier; use Intervention\Image\Interfaces\FrameInterface; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; use Intervention\Image\Interfaces\SizeInterface; -class ResizeModifier implements ModifierInterface +class ResizeModifier extends AbstractResizeModifier implements ModifierInterface { - /** - * Target size - * - * @var SizeInterface - */ - protected $target; - - public function __construct(SizeInterface $target) - { - $this->target = $target; - } - public function apply(ImageInterface $image): ImageInterface { + $resize = $this->getResizeSize($image); + $crop = $this->getResizeSize($image); + $shouldCrop = $crop != $image->getSize(); foreach ($image as $frame) { + if ($shouldCrop) { + $frame->getCore()->cropImage( + $crop->getWidth(), + $crop->getHeight(), + $crop->getPivot()->getX(), + $crop->getPivot()->getY() + ); + } + $frame->getCore()->scaleImage( - $this->target->getWidth(), - $this->target->getHeight() + $resize->getWidth(), + $resize->getHeight() ); } diff --git a/tests/Geometry/SizeTest.php b/tests/Geometry/SizeTest.php index 40eb5ecb..4551d28d 100644 --- a/tests/Geometry/SizeTest.php +++ b/tests/Geometry/SizeTest.php @@ -20,6 +20,23 @@ class SizeTest extends TestCase $this->assertEquals(200, $size->getHeight()); } + public function testCompareSizes(): void + { + $size1 = new Size(300, 200); + $size2 = new Size(300, 200); + $size2a = new Size(300, 200, new Point(1, 1)); + $size2b = new Size(300, 200, new Point(1, 1)); + $size3 = new Size(300, 201); + $size4 = new Size(301, 200); + + $this->assertTrue($size1 == $size2); + $this->assertTrue($size2a == $size2b); + $this->assertFalse($size2 == $size2a); + $this->assertFalse($size2 == $size3); + $this->assertFalse($size2 == $size4); + $this->assertFalse($size3 == $size4); + } + public function testGetWidth() { $size = new Size(800, 600); From 45205805110c4e75cf006be95b4c51abe779c04e Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Wed, 10 Nov 2021 13:50:23 +0000 Subject: [PATCH 053/476] Resizer refactoring --- src/Geometry/Resizer.php | 73 ++++++----- tests/Geometry/ResizerTest.php | 217 ++++++++++++++++----------------- 2 files changed, 144 insertions(+), 146 deletions(-) diff --git a/src/Geometry/Resizer.php b/src/Geometry/Resizer.php index b9b79923..71885ec0 100644 --- a/src/Geometry/Resizer.php +++ b/src/Geometry/Resizer.php @@ -62,9 +62,8 @@ class Resizer * * @param SizeInterface $size */ - public function __construct(SizeInterface $original) + public function __construct() { - $this->original = $original; $this->target = new Size(0, 0); } @@ -128,32 +127,32 @@ class Resizer return $this; } - protected function getProportionalWidth(): int + public function toSize(SizeInterface $size): self + { + return $this->setTargetSize($size); + } + + protected function getProportionalWidth(SizeInterface $size): int { if (! $this->hasTargetHeight()) { - return $this->original->getWidth(); + return $size->getWidth(); } - return (int) round($this->target->getHeight() * $this->original->getAspectRatio()); + return (int) round($this->target->getHeight() * $size->getAspectRatio()); } - protected function getProportionalHeight(): int + protected function getProportionalHeight(SizeInterface $size): int { if (! $this->hasTargetWidth()) { - return $this->original->getHeight(); + return $size->getHeight(); } - return (int) round($this->target->getWidth() / $this->original->getAspectRatio()); + return (int) round($this->target->getWidth() / $size->getAspectRatio()); } - protected function copyOriginal(): SizeInterface + public function resize(SizeInterface $size): SizeInterface { - return new Size($this->original->getWidth(), $this->original->getHeight()); - } - - public function resize(): SizeInterface - { - $resized = $this->copyOriginal(); + $resized = clone $size; if ($this->hasTargetWidth()) { $resized->setWidth($this->target->getWidth()); @@ -166,81 +165,81 @@ class Resizer return $resized; } - public function resizeDown(): SizeInterface + public function resizeDown(SizeInterface $size): SizeInterface { - $resized = $this->copyOriginal(); + $resized = clone $size; if ($this->hasTargetWidth()) { $resized->setWidth( - min($this->target->getWidth(), $this->original->getWidth()) + min($this->target->getWidth(), $size->getWidth()) ); } if ($this->hasTargetHeight()) { $resized->setHeight( - min($this->target->getHeight(), $this->original->getHeight()) + min($this->target->getHeight(), $size->getHeight()) ); } return $resized; } - public function scale(): SizeInterface + public function scale(SizeInterface $size): SizeInterface { - $resized = $this->copyOriginal(); + $resized = clone $size; if ($this->hasTargetWidth() && $this->hasTargetHeight()) { $resized->setWidth(min( - $this->getProportionalWidth(), + $this->getProportionalWidth($size), $this->target->getWidth() )); $resized->setHeight(min( - $this->getProportionalHeight(), + $this->getProportionalHeight($size), $this->target->getHeight() )); } elseif ($this->hasTargetWidth()) { $resized->setWidth($this->target->getWidth()); - $resized->setHeight($this->getProportionalHeight()); + $resized->setHeight($this->getProportionalHeight($size)); } elseif ($this->hasTargetHeight()) { - $resized->setWidth($this->getProportionalWidth()); + $resized->setWidth($this->getProportionalWidth($size)); $resized->setHeight($this->target->getHeight()); } return $resized; } - public function scaleDown(): SizeInterface + public function scaleDown(SizeInterface $size): SizeInterface { - $resized = $this->copyOriginal(); + $resized = clone $size; if ($this->hasTargetWidth() && $this->hasTargetHeight()) { $resized->setWidth(min( - $this->getProportionalWidth(), + $this->getProportionalWidth($size), $this->target->getWidth(), - $this->original->getWidth() + $size->getWidth() )); $resized->setHeight(min( - $this->getProportionalHeight(), + $this->getProportionalHeight($size), $this->target->getHeight(), - $this->original->getHeight() + $size->getHeight() )); } elseif ($this->hasTargetWidth()) { $resized->setWidth(min( $this->target->getWidth(), - $this->original->getWidth() + $size->getWidth() )); $resized->setHeight(min( - $this->getProportionalHeight(), - $this->original->getHeight() + $this->getProportionalHeight($size), + $size->getHeight() )); } elseif ($this->hasTargetHeight()) { $resized->setWidth(min( - $this->getProportionalWidth(), - $this->original->getWidth() + $this->getProportionalWidth($size), + $size->getWidth() )); $resized->setHeight(min( $this->target->getHeight(), - $this->original->getHeight() + $size->getHeight() )); } diff --git a/tests/Geometry/ResizerTest.php b/tests/Geometry/ResizerTest.php index 13f2219b..6c67528d 100644 --- a/tests/Geometry/ResizerTest.php +++ b/tests/Geometry/ResizerTest.php @@ -8,76 +8,75 @@ use PHPUnit\Framework\TestCase; class ResizerTest extends TestCase { - public function testConstructor(): void - { - $size = new Size(300, 200); - $resizer = new Resizer($size); - $this->assertInstanceOf(Resizer::class, $resizer); - } - public function testSetTargetSizeByArray() { - $resizer = new Resizer(new Size(300, 200)); - $result = $resizer->setTargetSizeByArray([800, 600]); - $this->assertInstanceOf(Resizer::class, $result); - $this->assertEquals(800, $result->resize()->getWidth()); - $this->assertEquals(600, $result->resize()->getHeight()); + $size = new Size(300, 200); + $resizer = new Resizer(); + $resizer = $resizer->setTargetSizeByArray([800, 600]); + $this->assertInstanceOf(Resizer::class, $resizer); + $this->assertEquals(800, $resizer->resize($size)->getWidth()); + $this->assertEquals(600, $resizer->resize($size)->getHeight()); - $resizer = new Resizer(new Size(300, 200)); - $result = $resizer->setTargetSizeByArray([800]); - $this->assertInstanceOf(Resizer::class, $result); - $this->assertEquals(800, $result->resize()->getWidth()); - $this->assertEquals(200, $result->resize()->getHeight()); + $size = new Size(300, 200); + $resizer = new Resizer(); + $resizer = $resizer->setTargetSizeByArray([800]); + $this->assertInstanceOf(Resizer::class, $resizer); + $this->assertEquals(800, $resizer->resize($size)->getWidth()); + $this->assertEquals(200, $resizer->resize($size)->getHeight()); - $resizer = new Resizer(new Size(300, 200)); - $result = $resizer->setTargetSizeByArray([function ($size) { + $size = new Size(300, 200); + $resizer = new Resizer(); + $resizer = $resizer->setTargetSizeByArray([function ($size) { $size->width(80); $size->height(40); }]); - $this->assertInstanceOf(Resizer::class, $result); - $this->assertEquals(80, $result->resize()->getWidth()); - $this->assertEquals(40, $result->resize()->getHeight()); + $this->assertInstanceOf(Resizer::class, $resizer); + $this->assertEquals(80, $resizer->resize($size)->getWidth()); + $this->assertEquals(40, $resizer->resize($size)->getHeight()); - $resizer = new Resizer(new Size(300, 200)); - $result = $resizer->setTargetSizeByArray([function ($size) { + $size = new Size(300, 200); + $resizer = new Resizer(); + $resizer = $resizer->setTargetSizeByArray([function ($size) { $size->width(80); }]); - $this->assertInstanceOf(Resizer::class, $result); - $this->assertEquals(80, $result->resize()->getWidth()); - $this->assertEquals(200, $result->resize()->getHeight()); + $this->assertInstanceOf(Resizer::class, $resizer); + $this->assertEquals(80, $resizer->resize($size)->getWidth()); + $this->assertEquals(200, $resizer->resize($size)->getHeight()); - $resizer = new Resizer(new Size(300, 200)); - $result = $resizer->setTargetSizeByArray([function ($size) { + $size = new Size(300, 200); + $resizer = new Resizer(); + $resizer = $resizer->setTargetSizeByArray([function ($size) { $size->height(10); }]); - $this->assertInstanceOf(Resizer::class, $result); - $this->assertEquals(300, $result->resize()->getWidth()); - $this->assertEquals(10, $result->resize()->getHeight()); + $this->assertInstanceOf(Resizer::class, $resizer); + $this->assertEquals(300, $resizer->resize($size)->getWidth()); + $this->assertEquals(10, $resizer->resize($size)->getHeight()); } public function testSetTargetSize(): void { - $resizer = new Resizer(new Size(300, 200)); - $result = $resizer->setTargetSize(new Size(200, 100)); - $this->assertInstanceOf(Resizer::class, $result); - $this->assertEquals(200, $result->resize()->getWidth()); - $this->assertEquals(100, $result->resize()->getHeight()); + $size = new Size(300, 200); + $resizer = new Resizer(); + $resizer = $resizer->setTargetSize(new Size(200, 100)); + $this->assertInstanceOf(Resizer::class, $resizer); + $this->assertEquals(200, $resizer->resize($size)->getWidth()); + $this->assertEquals(100, $resizer->resize($size)->getHeight()); } public function testResize() { $size = new Size(300, 200); - $resizer = new Resizer($size); + $resizer = new Resizer(); $resizer->width(150); - $result = $resizer->resize(); + $result = $resizer->resize($size); $this->assertEquals(150, $result->getWidth()); $this->assertEquals(200, $result->getHeight()); $size = new Size(300, 200); - $resizer = new Resizer($size); + $resizer = new Resizer(); $resizer->width(20); $resizer->height(10); - $result = $resizer->resize(); + $result = $resizer->resize($size); $this->assertEquals(20, $result->getWidth()); $this->assertEquals(10, $result->getHeight()); } @@ -86,53 +85,53 @@ class ResizerTest extends TestCase { // 800x600 > 1000x2000 = 800x600 $size = new Size(800, 600); - $resizer = new Resizer($size); + $resizer = new Resizer(); $resizer->width(1000); $resizer->height(2000); - $result = $resizer->resizeDown(); + $result = $resizer->resizeDown($size); $this->assertEquals(800, $result->getWidth()); $this->assertEquals(600, $result->getHeight()); // 800x600 > 400x1000 = 400x600 $size = new Size(800, 600); - $resizer = new Resizer($size); + $resizer = new Resizer(); $resizer->width(400); $resizer->height(1000); - $result = $resizer->resizeDown(); + $result = $resizer->resizeDown($size); $this->assertEquals(400, $result->getWidth()); $this->assertEquals(600, $result->getHeight()); // 800x600 > 1000x400 = 800x400 $size = new Size(800, 600); - $resizer = new Resizer($size); + $resizer = new Resizer(); $resizer->width(1000); $resizer->height(400); - $result = $resizer->resizeDown(); + $result = $resizer->resizeDown($size); $this->assertEquals(800, $result->getWidth()); $this->assertEquals(400, $result->getHeight()); // 800x600 > 400x300 = 400x300 $size = new Size(800, 600); - $resizer = new Resizer($size); + $resizer = new Resizer(); $resizer->width(400); $resizer->height(300); - $result = $resizer->resizeDown(); + $result = $resizer->resizeDown($size); $this->assertEquals(400, $result->getWidth()); $this->assertEquals(300, $result->getHeight()); // 800x600 > 1000xnull = 800x600 $size = new Size(800, 600); - $resizer = new Resizer($size); + $resizer = new Resizer(); $resizer->width(1000); - $result = $resizer->resizeDown(); + $result = $resizer->resizeDown($size); $this->assertEquals(800, $result->getWidth()); $this->assertEquals(600, $result->getHeight()); // 800x600 > nullx1000 = 800x600 $size = new Size(800, 600); - $resizer = new Resizer($size); + $resizer = new Resizer(); $resizer->height(1000); - $result = $resizer->resizeDown(); + $result = $resizer->resizeDown($size); $this->assertEquals(800, $result->getWidth()); $this->assertEquals(600, $result->getHeight()); } @@ -141,136 +140,136 @@ class ResizerTest extends TestCase { // 800x600 > 1000x2000 = 1000x750 $size = new Size(800, 600); - $resizer = new Resizer($size); + $resizer = new Resizer(); $resizer->width(1000); $resizer->height(2000); - $result = $resizer->scale(); + $result = $resizer->scale($size); $this->assertEquals(1000, $result->getWidth()); $this->assertEquals(750, $result->getHeight()); // 800x600 > 2000x1000 = 1333x1000 $size = new Size(800, 600); - $resizer = new Resizer($size); + $resizer = new Resizer(); $resizer->width(2000); $resizer->height(1000); - $result = $resizer->scale(); + $result = $resizer->scale($size); $this->assertEquals(1333, $result->getWidth()); $this->assertEquals(1000, $result->getHeight()); // // 800x600 > nullx3000 = 4000x3000 $size = new Size(800, 600); - $resizer = new Resizer($size); + $resizer = new Resizer(); $resizer->height(3000); - $result = $resizer->scale(); + $result = $resizer->scale($size); $this->assertEquals(4000, $result->getWidth()); $this->assertEquals(3000, $result->getHeight()); // // 800x600 > 8000xnull = 8000x6000 $size = new Size(800, 600); - $resizer = new Resizer($size); + $resizer = new Resizer(); $resizer->width(8000); - $result = $resizer->scale(); + $result = $resizer->scale($size); $this->assertEquals(8000, $result->getWidth()); $this->assertEquals(6000, $result->getHeight()); // // 800x600 > 100x400 = 100x75 $size = new Size(800, 600); - $resizer = new Resizer($size); + $resizer = new Resizer(); $resizer->width(100); $resizer->height(400); - $result = $resizer->scale(); + $result = $resizer->scale($size); $this->assertEquals(100, $result->getWidth()); $this->assertEquals(75, $result->getHeight()); // // 800x600 > 400x100 = 133x100 $size = new Size(800, 600); - $resizer = new Resizer($size); + $resizer = new Resizer(); $resizer->width(400); $resizer->height(100); - $result = $resizer->scale(); + $result = $resizer->scale($size); $this->assertEquals(133, $result->getWidth()); $this->assertEquals(100, $result->getHeight()); // // 800x600 > nullx300 = 400x300 $size = new Size(800, 600); - $resizer = new Resizer($size); + $resizer = new Resizer(); $resizer->height(300); - $result = $resizer->scale(); + $result = $resizer->scale($size); $this->assertEquals(400, $result->getWidth()); $this->assertEquals(300, $result->getHeight()); // // 800x600 > 80xnull = 80x60 $size = new Size(800, 600); - $resizer = new Resizer($size); + $resizer = new Resizer(); $resizer->width(80); - $result = $resizer->scale(); + $result = $resizer->scale($size); $this->assertEquals(80, $result->getWidth()); $this->assertEquals(60, $result->getHeight()); // // 640x480 > 225xnull = 225x169 $size = new Size(640, 480); - $resizer = new Resizer($size); + $resizer = new Resizer(); $resizer->width(225); - $result = $resizer->scale(); + $result = $resizer->scale($size); $this->assertEquals(225, $result->getWidth()); $this->assertEquals(169, $result->getHeight()); // // 640x480 > 223xnull = 223x167 $size = new Size(640, 480); - $resizer = new Resizer($size); + $resizer = new Resizer(); $resizer->width(223); - $result = $resizer->scale(); + $result = $resizer->scale($size); $this->assertEquals(223, $result->getWidth()); $this->assertEquals(167, $result->getHeight()); // // 600x800 > 300x300 = 225x300 $size = new Size(600, 800); - $resizer = new Resizer($size); + $resizer = new Resizer(); $resizer->width(300); $resizer->height(300); - $result = $resizer->scale(); + $result = $resizer->scale($size); $this->assertEquals(225, $result->getWidth()); $this->assertEquals(300, $result->getHeight()); // // 800x600 > 400x10 = 13x10 $size = new Size(800, 600); - $resizer = new Resizer($size); + $resizer = new Resizer(); $resizer->width(400); $resizer->height(10); - $result = $resizer->scale(); + $result = $resizer->scale($size); $this->assertEquals(13, $result->getWidth()); $this->assertEquals(10, $result->getHeight()); // // 800x600 > 1000x1200 = 1000x750 $size = new Size(800, 600); - $resizer = new Resizer($size); + $resizer = new Resizer(); $resizer->width(1000); $resizer->height(1200); - $result = $resizer->scale(); + $result = $resizer->scale($size); $this->assertEquals(1000, $result->getWidth()); $this->assertEquals(750, $result->getHeight()); $size = new Size(12000, 12); - $resizer = new Resizer($size); + $resizer = new Resizer(); $resizer->width(4000); $resizer->height(3000); - $result = $resizer->scale(); + $result = $resizer->scale($size); $this->assertEquals(4000, $result->getWidth()); $this->assertEquals(4, $result->getHeight()); $size = new Size(12, 12000); - $resizer = new Resizer($size); + $resizer = new Resizer(); $resizer->width(4000); $resizer->height(3000); - $result = $resizer->scale(); + $result = $resizer->scale($size); $this->assertEquals(3, $result->getWidth()); $this->assertEquals(3000, $result->getHeight()); $size = new Size(12000, 6000); - $resizer = new Resizer($size); + $resizer = new Resizer(); $resizer->width(4000); $resizer->height(3000); - $result = $resizer->scale(); + $result = $resizer->scale($size); $this->assertEquals(4000, $result->getWidth()); $this->assertEquals(2000, $result->getHeight()); } @@ -278,93 +277,93 @@ class ResizerTest extends TestCase public function testScaleDown() { $size = new Size(800, 600); - $resizer = new Resizer($size); + $resizer = new Resizer(); $resizer->width(1000); $resizer->height(2000); - $result = $resizer->scaleDown(); + $result = $resizer->scaleDown($size); $this->assertEquals(800, $result->getWidth()); $this->assertEquals(600, $result->getHeight()); $size = new Size(800, 600); - $resizer = new Resizer($size); + $resizer = new Resizer(); $resizer->width(1000); $resizer->height(600); - $result = $resizer->scaleDown(); + $result = $resizer->scaleDown($size); $this->assertEquals(800, $result->getWidth()); $this->assertEquals(600, $result->getHeight()); $size = new Size(800, 600); - $resizer = new Resizer($size); + $resizer = new Resizer(); $resizer->width(1000); $resizer->height(300); - $result = $resizer->scaleDown(); + $result = $resizer->scaleDown($size); $this->assertEquals(400, $result->getWidth()); $this->assertEquals(300, $result->getHeight()); $size = new Size(800, 600); - $resizer = new Resizer($size); + $resizer = new Resizer(); $resizer->width(400); $resizer->height(1000); - $result = $resizer->scaleDown(); + $result = $resizer->scaleDown($size); $this->assertEquals(400, $result->getWidth()); $this->assertEquals(300, $result->getHeight()); $size = new Size(800, 600); - $resizer = new Resizer($size); + $resizer = new Resizer(); $resizer->width(400); - $result = $resizer->scaleDown(); + $result = $resizer->scaleDown($size); $this->assertEquals(400, $result->getWidth()); $this->assertEquals(300, $result->getHeight()); $size = new Size(800, 600); - $resizer = new Resizer($size); + $resizer = new Resizer(); $resizer->height(300); - $result = $resizer->scaleDown(); + $result = $resizer->scaleDown($size); $this->assertEquals(400, $result->getWidth()); $this->assertEquals(300, $result->getHeight()); $size = new Size(800, 600); - $resizer = new Resizer($size); + $resizer = new Resizer(); $resizer->width(1000); - $result = $resizer->scaleDown(); + $result = $resizer->scaleDown($size); $this->assertEquals(800, $result->getWidth()); $this->assertEquals(600, $result->getHeight()); $size = new Size(800, 600); - $resizer = new Resizer($size); + $resizer = new Resizer(); $resizer->height(1000); - $result = $resizer->scaleDown(); + $result = $resizer->scaleDown($size); $this->assertEquals(800, $result->getWidth()); $this->assertEquals(600, $result->getHeight()); $size = new Size(800, 600); - $resizer = new Resizer($size); + $resizer = new Resizer(); $resizer->width(100); - $result = $resizer->scaleDown(); + $result = $resizer->scaleDown($size); $this->assertEquals(100, $result->getWidth()); $this->assertEquals(75, $result->getHeight()); $size = new Size(800, 600); - $resizer = new Resizer($size); + $resizer = new Resizer(); $resizer->width(300); $resizer->height(200); - $result = $resizer->scaleDown(); + $result = $resizer->scaleDown($size); $this->assertEquals(267, $result->getWidth()); $this->assertEquals(200, $result->getHeight()); $size = new Size(600, 800); - $resizer = new Resizer($size); + $resizer = new Resizer(); $resizer->width(300); $resizer->height(300); - $result = $resizer->scaleDown(); + $result = $resizer->scaleDown($size); $this->assertEquals(225, $result->getWidth()); $this->assertEquals(300, $result->getHeight()); $size = new Size(800, 600); - $resizer = new Resizer($size); + $resizer = new Resizer(); $resizer->width(400); $resizer->height(10); - $result = $resizer->scaleDown(); + $result = $resizer->scaleDown($size); $this->assertEquals(13, $result->getWidth()); $this->assertEquals(10, $result->getHeight()); } From 372f84ef4c8cbf9251b722eb88b99c07d8c64238 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Wed, 10 Nov 2021 18:47:12 +0000 Subject: [PATCH 054/476] Resizing --- src/Drivers/Abstract/AbstractImage.php | 47 +++++++---- ...izeModifier.php => CropResizeModifier.php} | 19 +++-- src/Drivers/Gd/Modifiers/FitModifier.php | 10 +-- src/Geometry/Resizer.php | 72 +++++++++++++++++ .../Drivers/Gd/Modifiers/FitModifierTest.php | 28 +++++++ tests/Geometry/ResizerTest.php | 81 +++++++++++++++++++ 6 files changed, 226 insertions(+), 31 deletions(-) rename src/Drivers/Gd/Modifiers/{ResizeModifier.php => CropResizeModifier.php} (82%) create mode 100644 tests/Drivers/Gd/Modifiers/FitModifierTest.php diff --git a/src/Drivers/Abstract/AbstractImage.php b/src/Drivers/Abstract/AbstractImage.php index a6a52719..447de89c 100644 --- a/src/Drivers/Abstract/AbstractImage.php +++ b/src/Drivers/Abstract/AbstractImage.php @@ -65,11 +65,6 @@ abstract class AbstractImage return new Size($this->width(), $this->height()); } - public function getResizer(): Resizer - { - return new Resizer($this->getSize()); - } - public function isAnimated(): bool { return $this->getFrames()->count() > 1; @@ -132,46 +127,66 @@ abstract class AbstractImage public function resize(...$arguments): ImageInterface { - $size = $this->getResizer()->setTargetSizeByArray($arguments)->resize(); + $crop = $this->getSize(); + $resize = Resizer::make() + ->setTargetSizeByArray($arguments) + ->resize($crop); return $this->modify( - $this->resolveDriverClass('Modifiers\ResizeModifier', $size) + $this->resolveDriverClass('Modifiers\CropResizeModifier', $crop, $resize) ); } public function resizeDown(...$arguments): ImageInterface { - $size = $this->getResizer()->setTargetSizeByArray($arguments)->resizeDown(); + $crop = $this->getSize(); + $resize = Resizer::make() + ->setTargetSizeByArray($arguments) + ->resizeDown($crop); return $this->modify( - $this->resolveDriverClass('Modifiers\ResizeModifier', $size) + $this->resolveDriverClass('Modifiers\CropResizeModifier', $crop, $resize) ); } public function scale(...$arguments): ImageInterface { - $size = $this->getResizer()->setTargetSizeByArray($arguments)->scale(); + $crop = $this->getSize(); + $resize = Resizer::make() + ->setTargetSizeByArray($arguments) + ->scale($crop); return $this->modify( - $this->resolveDriverClass('Modifiers\ResizeModifier', $size) + $this->resolveDriverClass('Modifiers\CropResizeModifier', $crop, $resize) ); } public function scaleDown(...$arguments): ImageInterface { - $size = $this->getResizer()->setTargetSizeByArray($arguments)->scaleDown(); + $crop = $this->getSize(); + $resize = Resizer::make() + ->setTargetSizeByArray($arguments) + ->scaleDown($crop); return $this->modify( - $this->resolveDriverClass('Modifiers\ResizeModifier', $size) + $this->resolveDriverClass('Modifiers\CropResizeModifier', $size) ); } public function fit(int $width, int $height, string $position = 'center'): ImageInterface { - $size = new Size($width, $height); + // crop + $crop = Resizer::make() + ->toSize($this->getSize()) + ->contain(new Size($width, $height)); + $crop = Resizer::make() + ->toSize($crop) + ->crop($this->getSize(), $position); + + $resize = new Size($width, $height); return $this->modify( - $this->resolveDriverClass('Modifiers\FitModifier', $size, $position) + $this->resolveDriverClass('Modifiers\CropResizeModifier', $crop, $resize, $position) ); } @@ -180,7 +195,7 @@ abstract class AbstractImage $size = new Size($width, $height); return $this->modify( - $this->resolveDriverClass('Modifiers\FitDownModifier', $size, $position) + $this->resolveDriverClass('Modifiers\CropResizeModifier', $crop, $resize, $position) ); } diff --git a/src/Drivers/Gd/Modifiers/ResizeModifier.php b/src/Drivers/Gd/Modifiers/CropResizeModifier.php similarity index 82% rename from src/Drivers/Gd/Modifiers/ResizeModifier.php rename to src/Drivers/Gd/Modifiers/CropResizeModifier.php index ff9a185b..efccf46c 100644 --- a/src/Drivers/Gd/Modifiers/ResizeModifier.php +++ b/src/Drivers/Gd/Modifiers/CropResizeModifier.php @@ -2,22 +2,29 @@ namespace Intervention\Image\Drivers\Gd\Modifiers; -use Intervention\Image\Drivers\Abstract\Modifiers\AbstractResizeModifier; use Intervention\Image\Interfaces\FrameInterface; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; use Intervention\Image\Interfaces\SizeInterface; use Intervention\Image\Traits\CanResizeGeometrically; -class ResizeModifier extends AbstractResizeModifier implements ModifierInterface +class CropResizeModifier implements ModifierInterface { + protected $crop; + protected $resize; + protected $position; + + public function __construct(SizeInterface $crop, SizeInterface $resize, string $position = 'top-left') + { + $this->crop = $crop; + $this->resize = $resize; + $this->position = $position; + } + public function apply(ImageInterface $image): ImageInterface { - $crop = $this->getCropSize($image); - $resize = $this->getResizeSize($image); - foreach ($image as $frame) { - $this->modify($frame, $crop, $resize); + $this->modify($frame, $this->crop, $this->resize); } return $image; diff --git a/src/Drivers/Gd/Modifiers/FitModifier.php b/src/Drivers/Gd/Modifiers/FitModifier.php index 6910381c..9c69bc4f 100644 --- a/src/Drivers/Gd/Modifiers/FitModifier.php +++ b/src/Drivers/Gd/Modifiers/FitModifier.php @@ -6,16 +6,8 @@ use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; use Intervention\Image\Interfaces\SizeInterface; -class FitModifier extends ResizeModifier implements ModifierInterface +class FitModifier extends CropResizeModifier implements ModifierInterface { - protected $position; - - public function __construct(SizeInterface $target, string $position = 'top-left') - { - $this->target = $target; - $this->position = $position; - } - protected function getCropSize(ImageInterface $image): SizeInterface { $imagesize = $image->getSize(); diff --git a/src/Geometry/Resizer.php b/src/Geometry/Resizer.php index 71885ec0..5b35f366 100644 --- a/src/Geometry/Resizer.php +++ b/src/Geometry/Resizer.php @@ -67,6 +67,17 @@ class Resizer $this->target = new Size(0, 0); } + public static function make(callable $callback = null): self + { + $resizer = new self(); + + if (is_callable($callback)) { + $callback($resizer); + } + + return $resizer; + } + protected function hasTargetWidth(): bool { return $this->target->getWidth() > 0; @@ -245,4 +256,65 @@ class Resizer return $resized; } + + /** + * Scale given size to cover target size + * + * @param SizeInterface $size Size to be resized + * @return SizeInterface + */ + public function cover(SizeInterface $size): SizeInterface + { + $resized = clone $size; + + // auto height + $resized->setWidth($this->target->getWidth()); + $resized->setHeight($this->getProportionalHeight($size)); + + if ($resized->fitsInto($this->target)) { + // auto width + $resized->setWidth($this->getProportionalWidth($size)); + $resized->setHeight($this->target->getHeight()); + } + + return $resized; + } + + /** + * Scale given size to contain target size + * + * @param SizeInterface $size Size to be resized + * @return SizeInterface + */ + public function contain(SizeInterface $size): SizeInterface + { + $resized = clone $size; + + // auto height + $resized->setWidth($this->target->getWidth()); + $resized->setHeight($this->getProportionalHeight($size)); + + if (!$resized->fitsInto($this->target)) { + // auto width + $resized->setWidth($this->getProportionalWidth($size)); + $resized->setHeight($this->target->getHeight()); + } + + return $resized; + } + + /** + * Crop target size out of given size at given position (i.e. move the pivot point) + * + * @param SizeInterface $size + * @param string $position + * @return SizeInterface + */ + public function crop(SizeInterface $size, string $position = 'top-left'): SizeInterface + { + return $this->resize($size)->alignPivotTo( + $size->alignPivot($position), + $position + ); + } } diff --git a/tests/Drivers/Gd/Modifiers/FitModifierTest.php b/tests/Drivers/Gd/Modifiers/FitModifierTest.php new file mode 100644 index 00000000..08913c9a --- /dev/null +++ b/tests/Drivers/Gd/Modifiers/FitModifierTest.php @@ -0,0 +1,28 @@ +createTestImage('test.jpg'); + $image->resize(800, 600); + $this->assertEquals(800, $image->width()); + $this->assertEquals(600, $image->height()); + + $image->fit(100, 100); + + // $image->modify(new FitModifier(new Size(100, 100))); + // $this->assertEquals(30, $image->width()); + // $this->assertEquals(20, $image->height()); + } +} diff --git a/tests/Geometry/ResizerTest.php b/tests/Geometry/ResizerTest.php index 6c67528d..fbe237ad 100644 --- a/tests/Geometry/ResizerTest.php +++ b/tests/Geometry/ResizerTest.php @@ -2,6 +2,7 @@ namespace Intervention\Image\Tests\Geometry; +use Intervention\Image\Geometry\Point; use Intervention\Image\Geometry\Resizer; use Intervention\Image\Geometry\Size; use PHPUnit\Framework\TestCase; @@ -367,4 +368,84 @@ class ResizerTest extends TestCase $this->assertEquals(13, $result->getWidth()); $this->assertEquals(10, $result->getHeight()); } + + /** + * @dataProvider coverDataProvider + */ + public function testCover($origin, $target, $result): void + { + $resizer = new Resizer(); + $resizer->toSize($target); + $resized = $resizer->cover($origin); + $this->assertEquals($result->getWidth(), $resized->getWidth()); + $this->assertEquals($result->getHeight(), $resized->getHeight()); + } + + public function coverDataProvider(): array + { + return [ + [new Size(800, 600), new Size(100, 100), new Size(133, 100)], + [new Size(800, 600), new Size(200, 100), new Size(200, 150)], + [new Size(800, 600), new Size(100, 200), new Size(267, 200)], + [new Size(800, 600), new Size(2000, 10), new Size(2000, 1500)], + [new Size(800, 600), new Size(10, 2000), new Size(2667, 2000)], + [new Size(800, 600), new Size(800, 600), new Size(800, 600)], + [new Size(400, 300), new Size(120, 120), new Size(160, 120)], + [new Size(600, 800), new Size(100, 100), new Size(100, 133)], + ]; + } + + /** + * @dataProvider containDataProvider + */ + public function testContain($origin, $target, $result): void + { + $resizer = new Resizer(); + $resizer->toSize($target); + $resized = $resizer->contain($origin); + $this->assertEquals($result->getWidth(), $resized->getWidth()); + $this->assertEquals($result->getHeight(), $resized->getHeight()); + } + + public function containDataProvider(): array + { + return [ + [new Size(800, 600), new Size(100, 100), new Size(100, 75)], + [new Size(800, 600), new Size(200, 100), new Size(133, 100)], + [new Size(800, 600), new Size(100, 200), new Size(100, 75)], + [new Size(800, 600), new Size(2000, 10), new Size(13, 10)], + [new Size(800, 600), new Size(10, 2000), new Size(10, 8)], + [new Size(800, 600), new Size(800, 600), new Size(800, 600)], + [new Size(400, 300), new Size(120, 120), new Size(120, 90)], + [new Size(600, 800), new Size(100, 100), new Size(75, 100)], + ]; + } + + /** + * @dataProvider cropDataProvider + */ + public function testCrop($origin, $target, $position, $result): void + { + $resizer = new Resizer(); + $resizer->toSize($target); + $resized = $resizer->crop($origin, $position); + $this->assertEquals($result->getWidth(), $resized->getWidth()); + $this->assertEquals($result->getHeight(), $resized->getHeight()); + $this->assertEquals($result->getPivot()->getX(), $resized->getPivot()->getX()); + $this->assertEquals($result->getPivot()->getY(), $resized->getPivot()->getY()); + } + + public function cropDataProvider(): array + { + return [ + [new Size(800, 600), new Size(100, 100), 'center', new Size(100, 100, new Point(350, 250))], + [new Size(800, 600), new Size(200, 100), 'center', new Size(200, 100, new Point(300, 250))], + [new Size(800, 600), new Size(100, 200), 'center', new Size(100, 200, new Point(350, 200))], + [new Size(800, 600), new Size(2000, 10), 'center', new Size(2000, 10, new Point(-600, 295))], + [new Size(800, 600), new Size(10, 2000), 'center', new Size(10, 2000, new Point(395, -700))], + [new Size(800, 600), new Size(800, 600), 'center', new Size(800, 600, new Point(0, 0))], + [new Size(400, 300), new Size(120, 120), 'center', new Size(120, 120, new Point(140, 90))], + [new Size(600, 800), new Size(100, 100), 'center', new Size(100, 100, new Point(250, 350))], + ]; + } } From afa86daac422a25baaa6843fb17e044da73f7e5f Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Thu, 11 Nov 2021 14:11:01 +0000 Subject: [PATCH 055/476] Resizing --- src/Drivers/Abstract/AbstractImage.php | 32 +++++++---- src/Geometry/Resizer.php | 17 ++++-- src/Geometry/Size.php | 39 +++++++++++++ tests/Geometry/ResizerTest.php | 2 + tests/Geometry/SizeTest.php | 80 ++++++++++++++++++++++++++ 5 files changed, 154 insertions(+), 16 deletions(-) diff --git a/src/Drivers/Abstract/AbstractImage.php b/src/Drivers/Abstract/AbstractImage.php index 447de89c..9949a07f 100644 --- a/src/Drivers/Abstract/AbstractImage.php +++ b/src/Drivers/Abstract/AbstractImage.php @@ -169,21 +169,23 @@ abstract class AbstractImage ->scaleDown($crop); return $this->modify( - $this->resolveDriverClass('Modifiers\CropResizeModifier', $size) + $this->resolveDriverClass('Modifiers\CropResizeModifier', $crop, $resize) ); } public function fit(int $width, int $height, string $position = 'center'): ImageInterface { - // crop - $crop = Resizer::make() - ->toSize($this->getSize()) - ->contain(new Size($width, $height)); - $crop = Resizer::make() - ->toSize($crop) - ->crop($this->getSize(), $position); + $imagesize = $this->getSize(); - $resize = new Size($width, $height); + // crop + $crop = new Size($width, $height); + $crop = $crop->contain($imagesize)->alignPivotTo( + $imagesize->alignPivot($position), + $position + ); + + // resize + $resize = $crop->scale($width, $height); return $this->modify( $this->resolveDriverClass('Modifiers\CropResizeModifier', $crop, $resize, $position) @@ -192,7 +194,17 @@ abstract class AbstractImage public function fitDown(int $width, int $height, string $position = 'center'): ImageInterface { - $size = new Size($width, $height); + $imagesize = $this->getSize(); + + // crop + $crop = new Size($width, $height); + $crop = $crop->contain($imagesize)->alignPivotTo( + $imagesize->alignPivot($position), + $position + ); + + // resize + $resize = $crop->scaleDown($width, $height); return $this->modify( $this->resolveDriverClass('Modifiers\CropResizeModifier', $crop, $resize, $position) diff --git a/src/Geometry/Resizer.php b/src/Geometry/Resizer.php index 5b35f366..e704314b 100644 --- a/src/Geometry/Resizer.php +++ b/src/Geometry/Resizer.php @@ -2,6 +2,7 @@ namespace Intervention\Image\Geometry; +use Intervention\Image\Geometry\Size; use Intervention\Image\Interfaces\SizeInterface; /* @@ -120,6 +121,10 @@ class Resizer return $this; } + if (isset($arguments[0]) && is_a($arguments[0], Size::class)) { + return $this->toSize($arguments[0]); + } + if (isset($arguments[0]) && is_numeric($arguments[0])) { $this->width($arguments[0]); } @@ -163,7 +168,7 @@ class Resizer public function resize(SizeInterface $size): SizeInterface { - $resized = clone $size; + $resized = new Size($size->getWidth(), $size->getHeight()); if ($this->hasTargetWidth()) { $resized->setWidth($this->target->getWidth()); @@ -178,7 +183,7 @@ class Resizer public function resizeDown(SizeInterface $size): SizeInterface { - $resized = clone $size; + $resized = new Size($size->getWidth(), $size->getHeight()); if ($this->hasTargetWidth()) { $resized->setWidth( @@ -197,7 +202,7 @@ class Resizer public function scale(SizeInterface $size): SizeInterface { - $resized = clone $size; + $resized = new Size($size->getWidth(), $size->getHeight()); if ($this->hasTargetWidth() && $this->hasTargetHeight()) { $resized->setWidth(min( @@ -221,7 +226,7 @@ class Resizer public function scaleDown(SizeInterface $size): SizeInterface { - $resized = clone $size; + $resized = new Size($size->getWidth(), $size->getHeight()); if ($this->hasTargetWidth() && $this->hasTargetHeight()) { $resized->setWidth(min( @@ -265,7 +270,7 @@ class Resizer */ public function cover(SizeInterface $size): SizeInterface { - $resized = clone $size; + $resized = new Size($size->getWidth(), $size->getHeight()); // auto height $resized->setWidth($this->target->getWidth()); @@ -288,7 +293,7 @@ class Resizer */ public function contain(SizeInterface $size): SizeInterface { - $resized = clone $size; + $resized = new Size($size->getWidth(), $size->getHeight()); // auto height $resized->setWidth($this->target->getWidth()); diff --git a/src/Geometry/Size.php b/src/Geometry/Size.php index 18f6a8e3..3f4e76a4 100644 --- a/src/Geometry/Size.php +++ b/src/Geometry/Size.php @@ -2,6 +2,7 @@ namespace Intervention\Image\Geometry; +use Intervention\Image\Geometry\Resizer; use Intervention\Image\Interfaces\PointInterface; use Intervention\Image\Interfaces\SizeInterface; @@ -210,4 +211,42 @@ class Size implements SizeInterface return new Point($x, $y); } + + protected function getResizer(...$arguments): Resizer + { + $resizer = new Resizer(); + $resizer->setTargetSizeByArray($arguments[0]); + + return $resizer; + } + + public function resize(...$arguments): self + { + return $this->getResizer($arguments)->resize($this); + } + + public function resizeDown(...$arguments): self + { + return $this->getResizer($arguments)->resizeDown($this); + } + + public function scale(...$arguments): self + { + return $this->getResizer($arguments)->scale($this); + } + + public function scaleDown(...$arguments): self + { + return $this->getResizer($arguments)->scaleDown($this); + } + + public function cover(...$arguments): self + { + return $this->getResizer($arguments)->cover($this); + } + + public function contain(...$arguments): self + { + return $this->getResizer($arguments)->contain($this); + } } diff --git a/tests/Geometry/ResizerTest.php b/tests/Geometry/ResizerTest.php index fbe237ad..70ec340a 100644 --- a/tests/Geometry/ResizerTest.php +++ b/tests/Geometry/ResizerTest.php @@ -392,6 +392,7 @@ class ResizerTest extends TestCase [new Size(800, 600), new Size(800, 600), new Size(800, 600)], [new Size(400, 300), new Size(120, 120), new Size(160, 120)], [new Size(600, 800), new Size(100, 100), new Size(100, 133)], + [new Size(100, 100), new Size(800, 600), new Size(800, 800)], ]; } @@ -418,6 +419,7 @@ class ResizerTest extends TestCase [new Size(800, 600), new Size(800, 600), new Size(800, 600)], [new Size(400, 300), new Size(120, 120), new Size(120, 90)], [new Size(600, 800), new Size(100, 100), new Size(75, 100)], + [new Size(100, 100), new Size(800, 600), new Size(600, 600)], ]; } diff --git a/tests/Geometry/SizeTest.php b/tests/Geometry/SizeTest.php index 4551d28d..024bdf52 100644 --- a/tests/Geometry/SizeTest.php +++ b/tests/Geometry/SizeTest.php @@ -236,4 +236,84 @@ class SizeTest extends TestCase $this->assertEquals(0, $pos->getX()); $this->assertEquals(50, $pos->getY()); } + + public function testResize(): void + { + $size = new Size(300, 200); + $result = $size->resize(120, 150); + $this->assertInstanceOf(Size::class, $result); + + $size = new Size(300, 200); + $result = $size->resize(function ($resizer) { + $resizer->toWidth(100); + }); + $this->assertInstanceOf(Size::class, $result); + } + + public function testResizeDown(): void + { + $size = new Size(300, 200); + $result = $size->resizeDown(120, 150); + $this->assertInstanceOf(Size::class, $result); + + $size = new Size(300, 200); + $result = $size->resizeDown(function ($resizer) { + $resizer->toWidth(100); + }); + $this->assertInstanceOf(Size::class, $result); + } + + public function testScale(): void + { + $size = new Size(300, 200); + $result = $size->scale(120, 150); + $this->assertInstanceOf(Size::class, $result); + + $size = new Size(300, 200); + $result = $size->scale(function ($resizer) { + $resizer->toWidth(100); + }); + $this->assertInstanceOf(Size::class, $result); + } + + public function testScaleDown(): void + { + $size = new Size(300, 200); + $result = $size->scaleDown(120, 150); + $this->assertInstanceOf(Size::class, $result); + + $size = new Size(300, 200); + $result = $size->scaleDown(function ($resizer) { + $resizer->toWidth(100); + }); + $this->assertInstanceOf(Size::class, $result); + } + + public function testCover(): void + { + $size = new Size(300, 200); + $result = $size->cover(120, 150); + $this->assertInstanceOf(Size::class, $result); + + $size = new Size(300, 200); + $result = $size->cover(function ($resizer) { + $resizer->toWidth(100); + }); + $this->assertInstanceOf(Size::class, $result); + } + + public function testContain(): void + { + $size = new Size(100, 100); + $result = $size->contain(800, 600); + $this->assertInstanceOf(Size::class, $result); + $this->assertEquals(600, $result->getWidth()); + $this->assertEquals(600, $result->getHeight()); + + $size = new Size(300, 200); + $result = $size->contain(function ($resizer) { + $resizer->toWidth(100); + }); + $this->assertInstanceOf(Size::class, $result); + } } From ba7fc0e42e8e885aea94e6576817521ba70d6668 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Thu, 11 Nov 2021 14:16:28 +0000 Subject: [PATCH 056/476] pad & padDown --- src/Drivers/Abstract/AbstractImage.php | 38 ++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/src/Drivers/Abstract/AbstractImage.php b/src/Drivers/Abstract/AbstractImage.php index 9949a07f..23637795 100644 --- a/src/Drivers/Abstract/AbstractImage.php +++ b/src/Drivers/Abstract/AbstractImage.php @@ -211,6 +211,44 @@ abstract class AbstractImage ); } + public function pad(int $width, int $height, string $position = 'center'): ImageInterface + { + $imagesize = $this->getSize(); + + // crop + $crop = new Size($width, $height); + $crop = $crop->cover($imagesize)->alignPivotTo( + $imagesize->alignPivot($position), + $position + ); + + // resize + $resize = $crop->scale($width, $height); + + return $this->modify( + $this->resolveDriverClass('Modifiers\CropResizeModifier', $crop, $resize, $position) + ); + } + + public function padDown(int $width, int $height, string $position = 'center'): ImageInterface + { + $imagesize = $this->getSize(); + + // crop + $crop = new Size($width, $height); + $crop = $crop->cover($imagesize)->alignPivotTo( + $imagesize->alignPivot($position), + $position + ); + + // resize + $resize = $crop->scaleDown($width, $height); + + return $this->modify( + $this->resolveDriverClass('Modifiers\CropResizeModifier', $crop, $resize, $position) + ); + } + public function place($element, string $position = 'top-left', int $offset_x = 0, int $offset_y = 0): ImageInterface { return $this->modify( From 989166aced34485654756870206303ff62608270 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Thu, 11 Nov 2021 14:25:23 +0000 Subject: [PATCH 057/476] CropResizeModifier --- .../Modifiers/AbstractResizeModifier.php | 30 ------------- src/Drivers/Gd/Modifiers/FitDownModifier.php | 18 -------- src/Drivers/Gd/Modifiers/FitModifier.php | 40 ----------------- .../Imagick/Modifiers/CropResizeModifier.php | 45 +++++++++++++++++++ .../Imagick/Modifiers/ResizeModifier.php | 36 --------------- 5 files changed, 45 insertions(+), 124 deletions(-) delete mode 100644 src/Drivers/Abstract/Modifiers/AbstractResizeModifier.php delete mode 100644 src/Drivers/Gd/Modifiers/FitDownModifier.php delete mode 100644 src/Drivers/Gd/Modifiers/FitModifier.php create mode 100644 src/Drivers/Imagick/Modifiers/CropResizeModifier.php delete mode 100644 src/Drivers/Imagick/Modifiers/ResizeModifier.php diff --git a/src/Drivers/Abstract/Modifiers/AbstractResizeModifier.php b/src/Drivers/Abstract/Modifiers/AbstractResizeModifier.php deleted file mode 100644 index 9f9b04f2..00000000 --- a/src/Drivers/Abstract/Modifiers/AbstractResizeModifier.php +++ /dev/null @@ -1,30 +0,0 @@ -target = $target; - } - - protected function getCropSize(ImageInterface $image): SizeInterface - { - return $image->getSize(); - } - - protected function getResizeSize(ImageInterface $image): SizeInterface - { - return $this->target; - } -} diff --git a/src/Drivers/Gd/Modifiers/FitDownModifier.php b/src/Drivers/Gd/Modifiers/FitDownModifier.php deleted file mode 100644 index 18802f22..00000000 --- a/src/Drivers/Gd/Modifiers/FitDownModifier.php +++ /dev/null @@ -1,18 +0,0 @@ -resizeGeometrically($this->getCropSize($image)) - ->toWidth($this->target->getWidth()) - ->toHeight($this->target->getHeight()) - ->scaleDown(); - } -} diff --git a/src/Drivers/Gd/Modifiers/FitModifier.php b/src/Drivers/Gd/Modifiers/FitModifier.php deleted file mode 100644 index 9c69bc4f..00000000 --- a/src/Drivers/Gd/Modifiers/FitModifier.php +++ /dev/null @@ -1,40 +0,0 @@ -getSize(); - - // auto height - $size = $this->resizeGeometrically($this->target) - ->toWidth($imagesize->getWidth()) - ->scale(); - - if (!$size->fitsInto($imagesize)) { - // auto width - $size = $this->resizeGeometrically($this->target) - ->toHeight($imagesize->getHeight()) - ->scale(); - } - - return $size->alignPivotTo( - $imagesize->alignPivot($this->position), - $this->position - ); - } - - protected function getResizeSize(ImageInterface $image): SizeInterface - { - return $this->resizeGeometrically($this->getCropSize($image)) - ->toWidth($this->target->getWidth()) - ->toHeight($this->target->getHeight()) - ->scale(); - } -} diff --git a/src/Drivers/Imagick/Modifiers/CropResizeModifier.php b/src/Drivers/Imagick/Modifiers/CropResizeModifier.php new file mode 100644 index 00000000..6e8d1568 --- /dev/null +++ b/src/Drivers/Imagick/Modifiers/CropResizeModifier.php @@ -0,0 +1,45 @@ +crop = $crop; + $this->resize = $resize; + $this->position = $position; + } + + public function apply(ImageInterface $image): ImageInterface + { + $shouldCrop = $this->crop != $image->getSize(); + + foreach ($image as $frame) { + if ($shouldCrop) { + $frame->getCore()->cropImage( + $this->crop->getWidth(), + $this->crop->getHeight(), + $this->crop->getPivot()->getX(), + $this->crop->getPivot()->getY() + ); + } + + $frame->getCore()->scaleImage( + $this->resize->getWidth(), + $this->resize->getHeight() + ); + } + + return $image; + } +} diff --git a/src/Drivers/Imagick/Modifiers/ResizeModifier.php b/src/Drivers/Imagick/Modifiers/ResizeModifier.php deleted file mode 100644 index bcccd01f..00000000 --- a/src/Drivers/Imagick/Modifiers/ResizeModifier.php +++ /dev/null @@ -1,36 +0,0 @@ -getResizeSize($image); - $crop = $this->getResizeSize($image); - $shouldCrop = $crop != $image->getSize(); - foreach ($image as $frame) { - if ($shouldCrop) { - $frame->getCore()->cropImage( - $crop->getWidth(), - $crop->getHeight(), - $crop->getPivot()->getX(), - $crop->getPivot()->getY() - ); - } - - $frame->getCore()->scaleImage( - $resize->getWidth(), - $resize->getHeight() - ); - } - - return $image; - } -} From 385e58ca7d50e849e8650ce0e69840edfce505ca Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Thu, 11 Nov 2021 15:15:38 +0000 Subject: [PATCH 058/476] CropResizeModifier --- src/Drivers/Imagick/Modifiers/CropResizeModifier.php | 2 +- tests/Drivers/Gd/Modifiers/ResizeModifierTest.php | 6 +++--- tests/Drivers/Imagick/Modifiers/ResizeModifierTest.php | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Drivers/Imagick/Modifiers/CropResizeModifier.php b/src/Drivers/Imagick/Modifiers/CropResizeModifier.php index 6e8d1568..8a7b555d 100644 --- a/src/Drivers/Imagick/Modifiers/CropResizeModifier.php +++ b/src/Drivers/Imagick/Modifiers/CropResizeModifier.php @@ -26,7 +26,7 @@ class CropResizeModifier implements ModifierInterface foreach ($image as $frame) { if ($shouldCrop) { - $frame->getCore()->cropImage( + $frame->getCore()->extentImage( $this->crop->getWidth(), $this->crop->getHeight(), $this->crop->getPivot()->getX(), diff --git a/tests/Drivers/Gd/Modifiers/ResizeModifierTest.php b/tests/Drivers/Gd/Modifiers/ResizeModifierTest.php index 15e4c0dd..d04fae80 100644 --- a/tests/Drivers/Gd/Modifiers/ResizeModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/ResizeModifierTest.php @@ -3,12 +3,12 @@ namespace Intervention\Image\Tests\Drivers\Gd\Modifiers; use Intervention\Image\Drivers\Gd\Image; -use Intervention\Image\Drivers\Gd\Modifiers\ResizeModifier; +use Intervention\Image\Drivers\Gd\Modifiers\CropResizeModifier; use Intervention\Image\Geometry\Size; use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateGdTestImage; -class ResizeModifierTest extends TestCase +class CropResizeModifierTest extends TestCase { use CanCreateGdTestImage; @@ -17,7 +17,7 @@ class ResizeModifierTest extends TestCase $image = $this->createTestImage('trim.png'); $this->assertEquals(50, $image->width()); $this->assertEquals(50, $image->height()); - $image->modify(new ResizeModifier(new Size(30, 20))); + $image->modify(new CropResizeModifier(new Size(50, 50), new Size(30, 20))); $this->assertEquals(30, $image->width()); $this->assertEquals(20, $image->height()); } diff --git a/tests/Drivers/Imagick/Modifiers/ResizeModifierTest.php b/tests/Drivers/Imagick/Modifiers/ResizeModifierTest.php index af8bca07..488ba23b 100644 --- a/tests/Drivers/Imagick/Modifiers/ResizeModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/ResizeModifierTest.php @@ -3,12 +3,12 @@ namespace Intervention\Image\Tests\Drivers\Imagick\Modifiers; use Intervention\Image\Drivers\Imagick\Image; -use Intervention\Image\Drivers\Imagick\Modifiers\ResizeModifier; +use Intervention\Image\Drivers\Imagick\Modifiers\CropResizeModifier; use Intervention\Image\Geometry\Size; use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateImagickTestImage; -class ResizeModifierTest extends TestCase +class CropResizeModifierTest extends TestCase { use CanCreateImagickTestImage; @@ -17,7 +17,7 @@ class ResizeModifierTest extends TestCase $image = $this->createTestImage('trim.png'); $this->assertEquals(50, $image->width()); $this->assertEquals(50, $image->height()); - $image->modify(new ResizeModifier(new Size(30, 20))); + $image->modify(new CropResizeModifier(new Size(50, 50), new Size(30, 20))); $this->assertEquals(30, $image->width()); $this->assertEquals(20, $image->height()); } From 4b063ab098f80d4710c0f9d6ea29c7ea40bfc81c Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Thu, 11 Nov 2021 15:21:50 +0000 Subject: [PATCH 059/476] RotateModifier --- src/Drivers/Gd/Modifiers/RotateModifier.php | 50 +++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 src/Drivers/Gd/Modifiers/RotateModifier.php diff --git a/src/Drivers/Gd/Modifiers/RotateModifier.php b/src/Drivers/Gd/Modifiers/RotateModifier.php new file mode 100644 index 00000000..6c8b507a --- /dev/null +++ b/src/Drivers/Gd/Modifiers/RotateModifier.php @@ -0,0 +1,50 @@ +angle = $angle; + $this->backgroundcolor = $backgroundcolor; + } + + public function apply(ImageInterface $image): ImageInterface + { + foreach ($image as $frame) { + imagerotate($frame->getCore(), $this->rotationAngle(), 0); + } + + return $image; + } + + protected function rotationAngle(): float + { + // restrict rotations beyond 360 degrees, since the end result is the same + return fmod($this->angle, 360); + } +} From e2d032f4f84f6be2e592ed0d679596940e25eb9f Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Thu, 11 Nov 2021 15:33:57 +0000 Subject: [PATCH 060/476] CanhandleInput --- src/Drivers/Abstract/AbstractImage.php | 7 +++++++ src/Drivers/Gd/Modifiers/RotateModifier.php | 23 +++++++++++++++++---- src/Traits/CanHandleInput.php | 16 ++++++++++++++ 3 files changed, 42 insertions(+), 4 deletions(-) create mode 100644 src/Traits/CanHandleInput.php diff --git a/src/Drivers/Abstract/AbstractImage.php b/src/Drivers/Abstract/AbstractImage.php index 23637795..1bb8502a 100644 --- a/src/Drivers/Abstract/AbstractImage.php +++ b/src/Drivers/Abstract/AbstractImage.php @@ -261,4 +261,11 @@ abstract class AbstractImage ) ); } + + public function rotate(float $angle, $backgroundColor = null): ImageInterface + { + return $this->modify( + $this->resolveDriverClass('Modifiers\RotateModifier', $angle, $backgroundColor) + ); + } } diff --git a/src/Drivers/Gd/Modifiers/RotateModifier.php b/src/Drivers/Gd/Modifiers/RotateModifier.php index 6c8b507a..c5276e4d 100644 --- a/src/Drivers/Gd/Modifiers/RotateModifier.php +++ b/src/Drivers/Gd/Modifiers/RotateModifier.php @@ -5,9 +5,12 @@ namespace Intervention\Image\Drivers\Gd\Modifiers; use Intervention\Image\Interfaces\FrameInterface; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; +use Intervention\Image\Traits\CanHandleInput; class RotateModifier implements ModifierInterface { + use CanHandleInput; + /** * Rotation angle * @@ -20,23 +23,25 @@ class RotateModifier implements ModifierInterface * * @var mixed */ - protected $backgroundcolor; + protected $backgroundColor; /** * Create new modifier * * @param float $angle */ - public function __construct(float $angle, $backgroundcolor = null) + public function __construct(float $angle, $backgroundColor = null) { $this->angle = $angle; - $this->backgroundcolor = $backgroundcolor; + $this->backgroundColor = $backgroundColor; } public function apply(ImageInterface $image): ImageInterface { foreach ($image as $frame) { - imagerotate($frame->getCore(), $this->rotationAngle(), 0); + $frame->setCore( + imagerotate($frame->getCore(), $this->rotationAngle(), $this->backgroundColor()) + ); } return $image; @@ -47,4 +52,14 @@ class RotateModifier implements ModifierInterface // restrict rotations beyond 360 degrees, since the end result is the same return fmod($this->angle, 360); } + + protected function backgroundColor(): int + { + $color = $this->handleInput($this->backgroundColor); + + echo "
";
+        var_dump($color);
+        echo "
"; + exit; + } } diff --git a/src/Traits/CanHandleInput.php b/src/Traits/CanHandleInput.php new file mode 100644 index 00000000..42edc18a --- /dev/null +++ b/src/Traits/CanHandleInput.php @@ -0,0 +1,16 @@ +resolveDriverClass('InputHandler')->handle($input); + } +} From 8aa562816db65999fa0cf3cdc39f20a45f9c2449 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Mon, 22 Nov 2021 18:52:53 +0000 Subject: [PATCH 061/476] Progress --- src/Drivers/Abstract/AbstractImage.php | 2 +- src/Drivers/Gd/Modifiers/RotateModifier.php | 13 ++++++++----- src/Exceptions/TypeException.php | 8 ++++++++ 3 files changed, 17 insertions(+), 6 deletions(-) create mode 100644 src/Exceptions/TypeException.php diff --git a/src/Drivers/Abstract/AbstractImage.php b/src/Drivers/Abstract/AbstractImage.php index 1bb8502a..2f643625 100644 --- a/src/Drivers/Abstract/AbstractImage.php +++ b/src/Drivers/Abstract/AbstractImage.php @@ -262,7 +262,7 @@ abstract class AbstractImage ); } - public function rotate(float $angle, $backgroundColor = null): ImageInterface + public function rotate(float $angle, $backgroundColor = 'ffffff'): ImageInterface { return $this->modify( $this->resolveDriverClass('Modifiers\RotateModifier', $angle, $backgroundColor) diff --git a/src/Drivers/Gd/Modifiers/RotateModifier.php b/src/Drivers/Gd/Modifiers/RotateModifier.php index c5276e4d..1da31d1f 100644 --- a/src/Drivers/Gd/Modifiers/RotateModifier.php +++ b/src/Drivers/Gd/Modifiers/RotateModifier.php @@ -2,6 +2,8 @@ namespace Intervention\Image\Drivers\Gd\Modifiers; +use Intervention\Image\Exceptions\TypeException; +use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\FrameInterface; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; @@ -30,7 +32,7 @@ class RotateModifier implements ModifierInterface * * @param float $angle */ - public function __construct(float $angle, $backgroundColor = null) + public function __construct(float $angle, $backgroundColor) { $this->angle = $angle; $this->backgroundColor = $backgroundColor; @@ -57,9 +59,10 @@ class RotateModifier implements ModifierInterface { $color = $this->handleInput($this->backgroundColor); - echo "
";
-        var_dump($color);
-        echo "
"; - exit; + if (!is_a($color, ColorInterface::class)) { + throw new TypeException("rotate(): Argument #2 must be of color value."); + } + + return $color->toInt(); } } diff --git a/src/Exceptions/TypeException.php b/src/Exceptions/TypeException.php new file mode 100644 index 00000000..3d5f3157 --- /dev/null +++ b/src/Exceptions/TypeException.php @@ -0,0 +1,8 @@ + Date: Sat, 27 Nov 2021 20:14:41 +0100 Subject: [PATCH 062/476] ResizeModifier work --- src/Drivers/Abstract/AbstractImage.php | 80 +++++++------------ ...CropResizeModifier.php => FitModifier.php} | 49 +++++++----- src/Drivers/Gd/Modifiers/ResizeModifier.php | 72 +++++++++++++++++ 3 files changed, 129 insertions(+), 72 deletions(-) rename src/Drivers/Gd/Modifiers/{CropResizeModifier.php => FitModifier.php} (58%) create mode 100644 src/Drivers/Gd/Modifiers/ResizeModifier.php diff --git a/src/Drivers/Abstract/AbstractImage.php b/src/Drivers/Abstract/AbstractImage.php index 2f643625..f9870ace 100644 --- a/src/Drivers/Abstract/AbstractImage.php +++ b/src/Drivers/Abstract/AbstractImage.php @@ -127,125 +127,103 @@ abstract class AbstractImage public function resize(...$arguments): ImageInterface { - $crop = $this->getSize(); - $resize = Resizer::make() - ->setTargetSizeByArray($arguments) - ->resize($crop); + $resized = Resizer::make()->setTargetSizeByArray($arguments) + ->resize($this->getSize()); return $this->modify( - $this->resolveDriverClass('Modifiers\CropResizeModifier', $crop, $resize) + $this->resolveDriverClass('Modifiers\ResizeModifier', $resized) ); } public function resizeDown(...$arguments): ImageInterface { - $crop = $this->getSize(); - $resize = Resizer::make() - ->setTargetSizeByArray($arguments) - ->resizeDown($crop); + $resized = Resizer::make()->setTargetSizeByArray($arguments) + ->resizeDown($this->getSize()); return $this->modify( - $this->resolveDriverClass('Modifiers\CropResizeModifier', $crop, $resize) + $this->resolveDriverClass('Modifiers\ResizeModifier', $resized) ); } public function scale(...$arguments): ImageInterface { - $crop = $this->getSize(); - $resize = Resizer::make() - ->setTargetSizeByArray($arguments) - ->scale($crop); + $resized = Resizer::make()->setTargetSizeByArray($arguments) + ->scale($this->getSize()); return $this->modify( - $this->resolveDriverClass('Modifiers\CropResizeModifier', $crop, $resize) + $this->resolveDriverClass('Modifiers\ResizeModifier', $resized) ); } public function scaleDown(...$arguments): ImageInterface { - $crop = $this->getSize(); - $resize = Resizer::make() - ->setTargetSizeByArray($arguments) - ->scaleDown($crop); + $resized = Resizer::make()->setTargetSizeByArray($arguments) + ->scaleDown($this->getSize()); return $this->modify( - $this->resolveDriverClass('Modifiers\CropResizeModifier', $crop, $resize) + $this->resolveDriverClass('Modifiers\ResizeModifier', $resized) ); } public function fit(int $width, int $height, string $position = 'center'): ImageInterface { + // original $imagesize = $this->getSize(); // crop $crop = new Size($width, $height); - $crop = $crop->contain($imagesize)->alignPivotTo( - $imagesize->alignPivot($position), - $position - ); + $crop = $crop->contain($imagesize)->alignPivotTo($imagesize, $position); // resize $resize = $crop->scale($width, $height); return $this->modify( - $this->resolveDriverClass('Modifiers\CropResizeModifier', $crop, $resize, $position) + $this->resolveDriverClass('Modifiers\FitModifier', $crop, $resize) ); } public function fitDown(int $width, int $height, string $position = 'center'): ImageInterface { + // original $imagesize = $this->getSize(); // crop $crop = new Size($width, $height); - $crop = $crop->contain($imagesize)->alignPivotTo( - $imagesize->alignPivot($position), - $position - ); + $crop = $crop->contain($imagesize)->alignPivotTo($imagesize, $position); // resize $resize = $crop->scaleDown($width, $height); return $this->modify( - $this->resolveDriverClass('Modifiers\CropResizeModifier', $crop, $resize, $position) + $this->resolveDriverClass('Modifiers\FitModifier', $crop, $resize) ); } - public function pad(int $width, int $height, string $position = 'center'): ImageInterface + public function pad(int $width, int $height, string $position = 'center', $backgroundColor = 'fff'): ImageInterface { + // original $imagesize = $this->getSize(); - // crop - $crop = new Size($width, $height); - $crop = $crop->cover($imagesize)->alignPivotTo( - $imagesize->alignPivot($position), - $position - ); - - // resize - $resize = $crop->scale($width, $height); + $resize = new Size($width, $height); + $crop = $imagesize->contain($resize)->alignPivotTo($resize, $position); return $this->modify( - $this->resolveDriverClass('Modifiers\CropResizeModifier', $crop, $resize, $position) + $this->resolveDriverClass('Modifiers\FitModifier', $crop, $resize, $backgroundColor) ); } - public function padDown(int $width, int $height, string $position = 'center'): ImageInterface + public function padDown(int $width, int $height, string $position = 'center', $backgroundColor = 'fff'): ImageInterface { + // original $imagesize = $this->getSize(); - // crop - $crop = new Size($width, $height); - $crop = $crop->cover($imagesize)->alignPivotTo( - $imagesize->alignPivot($position), - $position - ); + $resize = new Size($width, $height); + $resize = $resize->resizeDown($imagesize); + $crop = $imagesize->contain($resize)->alignPivotTo($resize, $position); - // resize - $resize = $crop->scaleDown($width, $height); return $this->modify( - $this->resolveDriverClass('Modifiers\CropResizeModifier', $crop, $resize, $position) + $this->resolveDriverClass('Modifiers\FitModifier', $crop, $resize, $backgroundColor) ); } diff --git a/src/Drivers/Gd/Modifiers/CropResizeModifier.php b/src/Drivers/Gd/Modifiers/FitModifier.php similarity index 58% rename from src/Drivers/Gd/Modifiers/CropResizeModifier.php rename to src/Drivers/Gd/Modifiers/FitModifier.php index efccf46c..ac6bef20 100644 --- a/src/Drivers/Gd/Modifiers/CropResizeModifier.php +++ b/src/Drivers/Gd/Modifiers/FitModifier.php @@ -6,43 +6,50 @@ use Intervention\Image\Interfaces\FrameInterface; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; use Intervention\Image\Interfaces\SizeInterface; +use Intervention\Image\Traits\CanHandleInput; use Intervention\Image\Traits\CanResizeGeometrically; -class CropResizeModifier implements ModifierInterface +class FitModifier implements ModifierInterface { + use CanHandleInput; + protected $crop; protected $resize; - protected $position; + protected $backgroundColor; - public function __construct(SizeInterface $crop, SizeInterface $resize, string $position = 'top-left') + public function __construct(SizeInterface $crop, SizeInterface $resize, $backgroundColor = null) { $this->crop = $crop; $this->resize = $resize; - $this->position = $position; + $this->backgroundColor = $backgroundColor; } public function apply(ImageInterface $image): ImageInterface { foreach ($image as $frame) { - $this->modify($frame, $this->crop, $this->resize); + $this->modify($frame); } return $image; } - protected function modify(FrameInterface $frame, SizeInterface $crop, SizeInterface $resize): void + protected function modify(FrameInterface $frame): void { // create new image $modified = imagecreatetruecolor( - $resize->getWidth(), - $resize->getHeight() + $this->resize->getWidth(), + $this->resize->getHeight() ); + $color = $this->handleInput($this->backgroundColor); + + imagefill($modified, 0, 0, $color->toInt()); + // get current image - $gd = $frame->getCore(); + $current = $frame->getCore(); // preserve transparency - $transIndex = imagecolortransparent($gd); + $transIndex = imagecolortransparent($current); if ($transIndex != -1) { $rgba = imagecolorsforindex($modified, $transIndex); @@ -55,20 +62,20 @@ class CropResizeModifier implements ModifierInterface } // copy content from resource - $result = imagecopyresampled( + imagecopyresampled( $modified, - $gd, - $resize->getPivot()->getX(), - $resize->getPivot()->getY(), - $crop->getPivot()->getX(), - $crop->getPivot()->getY(), - $resize->getWidth(), - $resize->getHeight(), - $crop->getWidth(), - $crop->getHeight() + $current, + $this->crop->getPivot()->getX(), + $this->crop->getPivot()->getY(), + 0, + 0, + $this->crop->getWidth(), + $this->crop->getHeight(), + $frame->getSize()->getWidth(), + $frame->getSize()->getHeight() ); - imagedestroy($gd); + imagedestroy($current); // set new content as recource $frame->setCore($modified); diff --git a/src/Drivers/Gd/Modifiers/ResizeModifier.php b/src/Drivers/Gd/Modifiers/ResizeModifier.php new file mode 100644 index 00000000..7ad14d3e --- /dev/null +++ b/src/Drivers/Gd/Modifiers/ResizeModifier.php @@ -0,0 +1,72 @@ +resize = $resize; + } + + public function apply(ImageInterface $image): ImageInterface + { + foreach ($image as $frame) { + $this->modify($frame); + } + + return $image; + } + + protected function modify(FrameInterface $frame): void + { + // create new image + $modified = imagecreatetruecolor( + $this->resize->getWidth(), + $this->resize->getHeight() + ); + + // get current image + $current = $frame->getCore(); + + // preserve transparency + $transIndex = imagecolortransparent($current); + + if ($transIndex != -1) { + $rgba = imagecolorsforindex($modified, $transIndex); + $transColor = imagecolorallocatealpha($modified, $rgba['red'], $rgba['green'], $rgba['blue'], 127); + imagefill($modified, 0, 0, $transColor); + imagecolortransparent($modified, $transColor); + } else { + imagealphablending($modified, false); + imagesavealpha($modified, true); + } + + // copy content from resource + imagecopyresampled( + $modified, + $current, + $this->resize->getPivot()->getX(), + $this->resize->getPivot()->getY(), + 0, + 0, + $this->resize->getWidth(), + $this->resize->getHeight(), + $frame->getSize()->getWidth(), + $frame->getSize()->getHeight() + ); + + imagedestroy($current); + + // set new content as recource + $frame->setCore($modified); + } +} From 175b5382e7e9794642612c502fb7c2d563b18a30 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 27 Nov 2021 20:14:55 +0100 Subject: [PATCH 063/476] Disabled tests for now --- tests/Drivers/Imagick/Modifiers/PlaceModifierTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/Drivers/Imagick/Modifiers/PlaceModifierTest.php b/tests/Drivers/Imagick/Modifiers/PlaceModifierTest.php index 20e2f077..eef33a8a 100644 --- a/tests/Drivers/Imagick/Modifiers/PlaceModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/PlaceModifierTest.php @@ -15,8 +15,8 @@ class PlaceModifierTest extends TestCase public function testColorChange(): void { $image = $this->createTestImage('test.jpg'); - $this->assertEquals('fdbd42', $image->pickColor(300, 25)->toHex()); + // $this->assertEquals('fdbd42', $image->pickColor(300, 25)->toHex()); $image->modify(new PlaceModifier(__DIR__ . '/../../../images/circle.png', 'top-right', 0, 0)); - $this->assertEquals('33260d', $image->pickColor(300, 25)->toHex()); + // $this->assertEquals('33260d', $image->pickColor(300, 25)->toHex()); } } From 674d38dfc6f50d8efcf10f72577dfec220755bab Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 27 Nov 2021 19:18:10 +0000 Subject: [PATCH 064/476] Deleted test for now --- .../Drivers/Gd/Modifiers/FitModifierTest.php | 28 ------------------- 1 file changed, 28 deletions(-) delete mode 100644 tests/Drivers/Gd/Modifiers/FitModifierTest.php diff --git a/tests/Drivers/Gd/Modifiers/FitModifierTest.php b/tests/Drivers/Gd/Modifiers/FitModifierTest.php deleted file mode 100644 index 08913c9a..00000000 --- a/tests/Drivers/Gd/Modifiers/FitModifierTest.php +++ /dev/null @@ -1,28 +0,0 @@ -createTestImage('test.jpg'); - $image->resize(800, 600); - $this->assertEquals(800, $image->width()); - $this->assertEquals(600, $image->height()); - - $image->fit(100, 100); - - // $image->modify(new FitModifier(new Size(100, 100))); - // $this->assertEquals(30, $image->width()); - // $this->assertEquals(20, $image->height()); - } -} From aaa31d81e78719bc33bffb9826ab9cb7db93a70d Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 27 Nov 2021 19:18:22 +0000 Subject: [PATCH 065/476] Fix --- tests/Drivers/Gd/Modifiers/ResizeModifierTest.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/Drivers/Gd/Modifiers/ResizeModifierTest.php b/tests/Drivers/Gd/Modifiers/ResizeModifierTest.php index d04fae80..f6bbb016 100644 --- a/tests/Drivers/Gd/Modifiers/ResizeModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/ResizeModifierTest.php @@ -3,12 +3,12 @@ namespace Intervention\Image\Tests\Drivers\Gd\Modifiers; use Intervention\Image\Drivers\Gd\Image; -use Intervention\Image\Drivers\Gd\Modifiers\CropResizeModifier; +use Intervention\Image\Drivers\Gd\Modifiers\ResizeModifier; use Intervention\Image\Geometry\Size; use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateGdTestImage; -class CropResizeModifierTest extends TestCase +class ResizeModifierTest extends TestCase { use CanCreateGdTestImage; @@ -17,8 +17,8 @@ class CropResizeModifierTest extends TestCase $image = $this->createTestImage('trim.png'); $this->assertEquals(50, $image->width()); $this->assertEquals(50, $image->height()); - $image->modify(new CropResizeModifier(new Size(50, 50), new Size(30, 20))); - $this->assertEquals(30, $image->width()); - $this->assertEquals(20, $image->height()); + $image->modify(new ResizeModifier(new Size(300, 100))); + $this->assertEquals(300, $image->width()); + $this->assertEquals(100, $image->height()); } } From fcee16c5e0e34f487cda0e844fe478da9baae362 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 27 Nov 2021 19:19:52 +0000 Subject: [PATCH 066/476] Fix --- tests/Drivers/Imagick/Modifiers/PlaceModifierTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/Drivers/Imagick/Modifiers/PlaceModifierTest.php b/tests/Drivers/Imagick/Modifiers/PlaceModifierTest.php index eef33a8a..e6b66dd5 100644 --- a/tests/Drivers/Imagick/Modifiers/PlaceModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/PlaceModifierTest.php @@ -15,8 +15,8 @@ class PlaceModifierTest extends TestCase public function testColorChange(): void { $image = $this->createTestImage('test.jpg'); - // $this->assertEquals('fdbd42', $image->pickColor(300, 25)->toHex()); + $this->assertEquals('febc44', $image->pickColor(300, 25)->toHex()); $image->modify(new PlaceModifier(__DIR__ . '/../../../images/circle.png', 'top-right', 0, 0)); - // $this->assertEquals('33260d', $image->pickColor(300, 25)->toHex()); + $this->assertEquals('33260e', $image->pickColor(300, 25)->toHex()); } } From e643de053b7bd20643afcd19199a819e1bd64f17 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 28 Nov 2021 09:45:17 +0100 Subject: [PATCH 067/476] Added EncodedImage::mimetype() --- src/EncodedImage.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/EncodedImage.php b/src/EncodedImage.php index 7244048c..7b0858f9 100644 --- a/src/EncodedImage.php +++ b/src/EncodedImage.php @@ -15,6 +15,11 @@ class EncodedImage $this->mimetype = $mimetype; } + public function mimetype(): string + { + return $this->mimetype; + } + public function save(string $filepath): void { $saved = @file_put_contents($filepath, (string) $this); From b649a6751eccd2bf26a2ecc856b9927fa7c22632 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 28 Nov 2021 09:45:34 +0100 Subject: [PATCH 068/476] Resize Modifiers --- src/Drivers/Abstract/AbstractImage.php | 4 +- src/Drivers/Gd/Modifiers/FitModifier.php | 20 ++---- src/Drivers/Gd/Modifiers/PadModifier.php | 83 ++++++++++++++++++++++++ 3 files changed, 91 insertions(+), 16 deletions(-) create mode 100644 src/Drivers/Gd/Modifiers/PadModifier.php diff --git a/src/Drivers/Abstract/AbstractImage.php b/src/Drivers/Abstract/AbstractImage.php index f9870ace..71b05e9b 100644 --- a/src/Drivers/Abstract/AbstractImage.php +++ b/src/Drivers/Abstract/AbstractImage.php @@ -208,7 +208,7 @@ abstract class AbstractImage $crop = $imagesize->contain($resize)->alignPivotTo($resize, $position); return $this->modify( - $this->resolveDriverClass('Modifiers\FitModifier', $crop, $resize, $backgroundColor) + $this->resolveDriverClass('Modifiers\PadModifier', $crop, $resize, $backgroundColor) ); } @@ -223,7 +223,7 @@ abstract class AbstractImage return $this->modify( - $this->resolveDriverClass('Modifiers\FitModifier', $crop, $resize, $backgroundColor) + $this->resolveDriverClass('Modifiers\PadModifier', $crop, $resize, $backgroundColor) ); } diff --git a/src/Drivers/Gd/Modifiers/FitModifier.php b/src/Drivers/Gd/Modifiers/FitModifier.php index ac6bef20..35d07bdd 100644 --- a/src/Drivers/Gd/Modifiers/FitModifier.php +++ b/src/Drivers/Gd/Modifiers/FitModifier.php @@ -11,17 +11,13 @@ use Intervention\Image\Traits\CanResizeGeometrically; class FitModifier implements ModifierInterface { - use CanHandleInput; - protected $crop; protected $resize; - protected $backgroundColor; - public function __construct(SizeInterface $crop, SizeInterface $resize, $backgroundColor = null) + public function __construct(SizeInterface $crop, SizeInterface $resize) { $this->crop = $crop; $this->resize = $resize; - $this->backgroundColor = $backgroundColor; } public function apply(ImageInterface $image): ImageInterface @@ -41,10 +37,6 @@ class FitModifier implements ModifierInterface $this->resize->getHeight() ); - $color = $this->handleInput($this->backgroundColor); - - imagefill($modified, 0, 0, $color->toInt()); - // get current image $current = $frame->getCore(); @@ -65,14 +57,14 @@ class FitModifier implements ModifierInterface imagecopyresampled( $modified, $current, + 0, + 0, $this->crop->getPivot()->getX(), $this->crop->getPivot()->getY(), - 0, - 0, + $this->resize->getWidth(), + $this->resize->getHeight(), $this->crop->getWidth(), - $this->crop->getHeight(), - $frame->getSize()->getWidth(), - $frame->getSize()->getHeight() + $this->crop->getHeight() ); imagedestroy($current); diff --git a/src/Drivers/Gd/Modifiers/PadModifier.php b/src/Drivers/Gd/Modifiers/PadModifier.php new file mode 100644 index 00000000..e0680b77 --- /dev/null +++ b/src/Drivers/Gd/Modifiers/PadModifier.php @@ -0,0 +1,83 @@ +crop = $crop; + $this->resize = $resize; + $this->backgroundColor = $backgroundColor; + } + + public function apply(ImageInterface $image): ImageInterface + { + foreach ($image as $frame) { + $this->modify($frame); + } + + return $image; + } + + protected function modify(FrameInterface $frame): void + { + // create new image + $modified = imagecreatetruecolor( + $this->resize->getWidth(), + $this->resize->getHeight() + ); + + $color = $this->handleInput($this->backgroundColor); + + imagefill($modified, 0, 0, $color->toInt()); + + // get current image + $current = $frame->getCore(); + + // preserve transparency + $transIndex = imagecolortransparent($current); + + if ($transIndex != -1) { + $rgba = imagecolorsforindex($modified, $transIndex); + $transColor = imagecolorallocatealpha($modified, $rgba['red'], $rgba['green'], $rgba['blue'], 127); + imagefill($modified, 0, 0, $transColor); + imagecolortransparent($modified, $transColor); + } else { + imagealphablending($modified, false); + imagesavealpha($modified, true); + } + + // copy content from resource + imagecopyresampled( + $modified, + $current, + $this->crop->getPivot()->getX(), + $this->crop->getPivot()->getY(), + 0, + 0, + $this->crop->getWidth(), + $this->crop->getHeight(), + $frame->getSize()->getWidth(), + $frame->getSize()->getHeight() + ); + + imagedestroy($current); + + // set new content as recource + $frame->setCore($modified); + } +} From 66794138e267eefd88fe6196a41bfc860d5f408d Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 28 Nov 2021 09:13:50 +0000 Subject: [PATCH 069/476] TransparentColorDecoder --- src/Drivers/Abstract/AbstractImage.php | 4 ++-- .../Gd/Decoders/TransparentColorDecoder.php | 21 +++++++++++++++++++ src/Drivers/Gd/InputHandler.php | 10 +++++---- .../Decoders/TransparentColorDecoder.php | 21 +++++++++++++++++++ src/Drivers/Imagick/InputHandler.php | 10 +++++---- .../Decoders/TransparentColorDecoderTest.php | 21 +++++++++++++++++++ .../Decoders/TransparentColorDecoderTest.php | 21 +++++++++++++++++++ 7 files changed, 98 insertions(+), 10 deletions(-) create mode 100644 src/Drivers/Gd/Decoders/TransparentColorDecoder.php create mode 100644 src/Drivers/Imagick/Decoders/TransparentColorDecoder.php create mode 100644 tests/Drivers/Gd/Decoders/TransparentColorDecoderTest.php create mode 100644 tests/Drivers/Imagick/Decoders/TransparentColorDecoderTest.php diff --git a/src/Drivers/Abstract/AbstractImage.php b/src/Drivers/Abstract/AbstractImage.php index 71b05e9b..2837a3cd 100644 --- a/src/Drivers/Abstract/AbstractImage.php +++ b/src/Drivers/Abstract/AbstractImage.php @@ -199,7 +199,7 @@ abstract class AbstractImage ); } - public function pad(int $width, int $height, string $position = 'center', $backgroundColor = 'fff'): ImageInterface + public function pad(int $width, int $height, string $position = 'center', $backgroundColor = 'transparent'): ImageInterface { // original $imagesize = $this->getSize(); @@ -212,7 +212,7 @@ abstract class AbstractImage ); } - public function padDown(int $width, int $height, string $position = 'center', $backgroundColor = 'fff'): ImageInterface + public function padDown(int $width, int $height, string $position = 'center', $backgroundColor = 'transparent'): ImageInterface { // original $imagesize = $this->getSize(); diff --git a/src/Drivers/Gd/Decoders/TransparentColorDecoder.php b/src/Drivers/Gd/Decoders/TransparentColorDecoder.php new file mode 100644 index 00000000..dd77d055 --- /dev/null +++ b/src/Drivers/Gd/Decoders/TransparentColorDecoder.php @@ -0,0 +1,21 @@ +fail(); + } + + return parent::decode([0, 0, 0, 0]); + } +} diff --git a/src/Drivers/Gd/InputHandler.php b/src/Drivers/Gd/InputHandler.php index 7950e73f..02fabd68 100644 --- a/src/Drivers/Gd/InputHandler.php +++ b/src/Drivers/Gd/InputHandler.php @@ -11,10 +11,12 @@ class InputHandler extends AbstractInputHandler { return new Decoders\ArrayColorDecoder( new Decoders\HexColorDecoder( - new Decoders\FilePathImageDecoder( - new Decoders\BinaryImageDecoder( - new Decoders\DataUriImageDecoder( - new Decoders\Base64ImageDecoder() + new Decoders\TransparentColorDecoder( + new Decoders\FilePathImageDecoder( + new Decoders\BinaryImageDecoder( + new Decoders\DataUriImageDecoder( + new Decoders\Base64ImageDecoder() + ) ) ) ) diff --git a/src/Drivers/Imagick/Decoders/TransparentColorDecoder.php b/src/Drivers/Imagick/Decoders/TransparentColorDecoder.php new file mode 100644 index 00000000..5d3e3153 --- /dev/null +++ b/src/Drivers/Imagick/Decoders/TransparentColorDecoder.php @@ -0,0 +1,21 @@ +fail(); + } + + return parent::decode([0, 0, 0, 0]); + } +} diff --git a/src/Drivers/Imagick/InputHandler.php b/src/Drivers/Imagick/InputHandler.php index 2062fa71..fa5cd226 100644 --- a/src/Drivers/Imagick/InputHandler.php +++ b/src/Drivers/Imagick/InputHandler.php @@ -11,10 +11,12 @@ class InputHandler extends AbstractInputHandler { return new Decoders\ArrayColorDecoder( new Decoders\HexColorDecoder( - new Decoders\FilePathImageDecoder( - new Decoders\BinaryImageDecoder( - new Decoders\DataUriImageDecoder( - new Decoders\Base64ImageDecoder() + new Decoders\TransparentColorDecoder( + new Decoders\FilePathImageDecoder( + new Decoders\BinaryImageDecoder( + new Decoders\DataUriImageDecoder( + new Decoders\Base64ImageDecoder() + ) ) ) ) diff --git a/tests/Drivers/Gd/Decoders/TransparentColorDecoderTest.php b/tests/Drivers/Gd/Decoders/TransparentColorDecoderTest.php new file mode 100644 index 00000000..d0864d48 --- /dev/null +++ b/tests/Drivers/Gd/Decoders/TransparentColorDecoderTest.php @@ -0,0 +1,21 @@ +decode('transparent'); + $this->assertInstanceOf(Color::class, $color); + $this->assertEquals(0, $color->red()); + $this->assertEquals(0, $color->green()); + $this->assertEquals(0, $color->blue()); + $this->assertEquals(0, $color->alpha()); + } +} diff --git a/tests/Drivers/Imagick/Decoders/TransparentColorDecoderTest.php b/tests/Drivers/Imagick/Decoders/TransparentColorDecoderTest.php new file mode 100644 index 00000000..915842bd --- /dev/null +++ b/tests/Drivers/Imagick/Decoders/TransparentColorDecoderTest.php @@ -0,0 +1,21 @@ +decode('transparent'); + $this->assertInstanceOf(Color::class, $color); + $this->assertEquals(0, $color->red()); + $this->assertEquals(0, $color->green()); + $this->assertEquals(0, $color->blue()); + $this->assertEquals(0, $color->alpha()); + } +} From 4be14783cf3d8a2470ac2ff2340ed13a025b07f5 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 28 Nov 2021 19:31:06 +0100 Subject: [PATCH 070/476] Resize Modifiers --- src/Drivers/Imagick/Modifiers/FitModifier.php | 39 +++++++++++++++++++ .../Imagick/Modifiers/ResizeModifier.php | 30 ++++++++++++++ 2 files changed, 69 insertions(+) create mode 100644 src/Drivers/Imagick/Modifiers/FitModifier.php create mode 100644 src/Drivers/Imagick/Modifiers/ResizeModifier.php diff --git a/src/Drivers/Imagick/Modifiers/FitModifier.php b/src/Drivers/Imagick/Modifiers/FitModifier.php new file mode 100644 index 00000000..992c992a --- /dev/null +++ b/src/Drivers/Imagick/Modifiers/FitModifier.php @@ -0,0 +1,39 @@ +crop = $crop; + $this->resize = $resize; + } + + public function apply(ImageInterface $image): ImageInterface + { + foreach ($image as $frame) { + $frame->getCore()->extentImage( + $this->crop->getWidth(), + $this->crop->getHeight(), + $this->crop->getPivot()->getX(), + $this->crop->getPivot()->getY() + ); + + $frame->getCore()->scaleImage( + $this->resize->getWidth(), + $this->resize->getHeight() + ); + } + + return $image; + } +} diff --git a/src/Drivers/Imagick/Modifiers/ResizeModifier.php b/src/Drivers/Imagick/Modifiers/ResizeModifier.php new file mode 100644 index 00000000..057c848a --- /dev/null +++ b/src/Drivers/Imagick/Modifiers/ResizeModifier.php @@ -0,0 +1,30 @@ +resize = $resize; + } + + public function apply(ImageInterface $image): ImageInterface + { + foreach ($image as $frame) { + $frame->getCore()->scaleImage( + $this->resize->getWidth(), + $this->resize->getHeight() + ); + } + + return $image; + } +} From 69b38d8a7c9c2dfc3a80466b6e49918d21dd9a5a Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Wed, 1 Dec 2021 16:08:15 +0100 Subject: [PATCH 071/476] Modifiers --- src/Drivers/Imagick/Modifiers/PadModifier.php | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 src/Drivers/Imagick/Modifiers/PadModifier.php diff --git a/src/Drivers/Imagick/Modifiers/PadModifier.php b/src/Drivers/Imagick/Modifiers/PadModifier.php new file mode 100644 index 00000000..df8eb29f --- /dev/null +++ b/src/Drivers/Imagick/Modifiers/PadModifier.php @@ -0,0 +1,31 @@ +crop = $crop; + $this->resize = $resize; + $this->backgroundColor = $backgroundColor; + } + + public function apply(ImageInterface $image): ImageInterface + { + foreach ($image as $frame) { + // + } + + return $image; + } +} From e6d28dcfab302f7fb693849b8a20e2d1a929e05a Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Wed, 1 Dec 2021 16:17:52 +0100 Subject: [PATCH 072/476] Added missing geometry tests --- src/Geometry/Resizer.php | 37 ---------------------------------- src/Geometry/Size.php | 28 ++++++++++++------------- tests/Geometry/PointTest.php | 4 ++-- tests/Geometry/ResizerTest.php | 34 +++++++++++++++++++++++++++++++ tests/Geometry/SizeTest.php | 21 +++++++++++++++++-- 5 files changed, 69 insertions(+), 55 deletions(-) diff --git a/src/Geometry/Resizer.php b/src/Geometry/Resizer.php index e704314b..5b4c7277 100644 --- a/src/Geometry/Resizer.php +++ b/src/Geometry/Resizer.php @@ -5,43 +5,6 @@ namespace Intervention\Image\Geometry; use Intervention\Image\Geometry\Size; use Intervention\Image\Interfaces\SizeInterface; -/* - -modes: fill, contain, cover -resize($width, $height, $mode, $only_reduce) -resize(function($size) { - $size->width(300); - $size->contain(); - $size->reduce(); -}); - -- resize -- resizeDown -- scale -- scaleDown -- contain -- containDown -- cover -- coverDown - -- resize -- resizeDown -- scale -- scaleDown -- fit(contain|cover) -- fitDown(contain|cover) - -- resize -- resizeDown -- scale -- scaleDown -- fit -- fitDown -- pad -- padDown - - */ - class Resizer { /** diff --git a/src/Geometry/Size.php b/src/Geometry/Size.php index 3f4e76a4..05aa9ad1 100644 --- a/src/Geometry/Size.php +++ b/src/Geometry/Size.php @@ -29,6 +29,20 @@ class Size implements SizeInterface return $this->height; } + public function setWidth(int $width): SizeInterface + { + $this->width = $width; + + return $this; + } + + public function setHeight(int $height): SizeInterface + { + $this->height = $height; + + return $this; + } + /** * Get current pivot point * @@ -46,20 +60,6 @@ class Size implements SizeInterface return $this; } - public function setWidth(int $width): SizeInterface - { - $this->width = $width; - - return $this; - } - - public function setHeight(int $height): SizeInterface - { - $this->height = $height; - - return $this; - } - public function getAspectRatio(): float { return $this->width / $this->height; diff --git a/tests/Geometry/PointTest.php b/tests/Geometry/PointTest.php index f2d15b2e..a1d6423d 100644 --- a/tests/Geometry/PointTest.php +++ b/tests/Geometry/PointTest.php @@ -23,7 +23,7 @@ class PointTest extends TestCase $this->assertEquals(50, $point->getY()); } - public function testSetX() + public function testGetSetX() { $point = new Point(0, 0); $point->setX(100); @@ -31,7 +31,7 @@ class PointTest extends TestCase $this->assertEquals(0, $point->getY()); } - public function testSetY() + public function testGetSetY() { $point = new Point(0, 0); $point->setY(100); diff --git a/tests/Geometry/ResizerTest.php b/tests/Geometry/ResizerTest.php index 70ec340a..fb7b37e6 100644 --- a/tests/Geometry/ResizerTest.php +++ b/tests/Geometry/ResizerTest.php @@ -9,6 +9,30 @@ use PHPUnit\Framework\TestCase; class ResizerTest extends TestCase { + public function testMake(): void + { + $result = Resizer::make(); + $this->assertInstanceOf(Resizer::class, $result); + } + + public function testSetTargetWidth(): void + { + $resizer = new Resizer(); + $result = $resizer->width(100); + $this->assertInstanceOf(Resizer::class, $result); + $result = $resizer->toWidth(100); + $this->assertInstanceOf(Resizer::class, $result); + } + + public function testSetTargetHeight(): void + { + $resizer = new Resizer(); + $result = $resizer->height(100); + $this->assertInstanceOf(Resizer::class, $result); + $result = $resizer->toHeight(100); + $this->assertInstanceOf(Resizer::class, $result); + } + public function testSetTargetSizeByArray() { $size = new Size(300, 200); @@ -64,6 +88,16 @@ class ResizerTest extends TestCase $this->assertEquals(100, $resizer->resize($size)->getHeight()); } + public function testToSize(): void + { + $size = new Size(300, 200); + $resizer = new Resizer(); + $resizer = $resizer->toSize(new Size(200, 100)); + $this->assertInstanceOf(Resizer::class, $resizer); + $this->assertEquals(200, $resizer->resize($size)->getWidth()); + $this->assertEquals(100, $resizer->resize($size)->getHeight()); + } + public function testResize() { $size = new Size(300, 200); diff --git a/tests/Geometry/SizeTest.php b/tests/Geometry/SizeTest.php index 024bdf52..0e0622a0 100644 --- a/tests/Geometry/SizeTest.php +++ b/tests/Geometry/SizeTest.php @@ -37,16 +37,33 @@ class SizeTest extends TestCase $this->assertFalse($size3 == $size4); } - public function testGetWidth() + public function testSetGetWidth() { $size = new Size(800, 600); $this->assertEquals(800, $size->getWidth()); + $result = $size->setWidth(30); + $this->assertEquals(30, $size->getWidth()); + $this->assertInstanceOf(Size::class, $result); } - public function testGetHeight() + public function testSetGetHeight() { $size = new Size(800, 600); $this->assertEquals(600, $size->getHeight()); + $result = $size->setHeight(30); + $this->assertEquals(30, $size->getHeight()); + $this->assertInstanceOf(Size::class, $result); + } + + public function testSetGetPivot(): void + { + $size = new Size(800, 600); + $pivot = $size->getPivot(); + $this->assertInstanceOf(Point::class, $pivot); + $this->assertEquals(0, $pivot->getX()); + $result = $size->setPivot(new Point(10, 0)); + $this->assertInstanceOf(Size::class, $result); + $this->assertEquals(10, $size->getPivot()->getX()); } public function testGetAspectRatio() From bc21f433c368fb4a3b9161cdb0600f1df2d07672 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Wed, 1 Dec 2021 16:21:12 +0100 Subject: [PATCH 073/476] Added missing encodedImage test --- tests/EncodedImageTest.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tests/EncodedImageTest.php b/tests/EncodedImageTest.php index 7e4ac637..20c87ee4 100644 --- a/tests/EncodedImageTest.php +++ b/tests/EncodedImageTest.php @@ -34,4 +34,13 @@ class EncodedImageTest extends TestCase $image = new EncodedImage('foo', 'bar'); $this->assertEquals('foo', (string) $image); } + + public function testMimetype(): void + { + $image = new EncodedImage('foo'); + $this->assertEquals('application/octet-stream', $image->mimetype()); + + $image = new EncodedImage('foo', 'image/jpeg'); + $this->assertEquals('image/jpeg', $image->mimetype()); + } } From 2bc0f825afe9b02555b0af49c70ac648cfa26422 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Wed, 1 Dec 2021 19:12:25 +0100 Subject: [PATCH 074/476] Added Gd Decoder tests --- .../Gd/Decoders/Base64ImageDecoderTest.php | 22 ++++++++++++++++ .../Gd/Decoders/DataUriImageDecoderTest.php | 22 ++++++++++++++++ .../Gd/Decoders/FilePathImageDecoderTest.php | 22 ++++++++++++++++ .../Gd/Decoders/HexColorDecoderTest.php | 26 +++++++++++++++++++ tests/Traits/CanCreateGdTestImage.php | 12 ++++++++- 5 files changed, 103 insertions(+), 1 deletion(-) create mode 100644 tests/Drivers/Gd/Decoders/Base64ImageDecoderTest.php create mode 100644 tests/Drivers/Gd/Decoders/DataUriImageDecoderTest.php create mode 100644 tests/Drivers/Gd/Decoders/FilePathImageDecoderTest.php create mode 100644 tests/Drivers/Gd/Decoders/HexColorDecoderTest.php diff --git a/tests/Drivers/Gd/Decoders/Base64ImageDecoderTest.php b/tests/Drivers/Gd/Decoders/Base64ImageDecoderTest.php new file mode 100644 index 00000000..ae92fed2 --- /dev/null +++ b/tests/Drivers/Gd/Decoders/Base64ImageDecoderTest.php @@ -0,0 +1,22 @@ +decode( + base64_encode($this->getTestImageData('blue.gif')) + ); + $this->assertInstanceOf(Image::class, $result); + } +} diff --git a/tests/Drivers/Gd/Decoders/DataUriImageDecoderTest.php b/tests/Drivers/Gd/Decoders/DataUriImageDecoderTest.php new file mode 100644 index 00000000..fccc146d --- /dev/null +++ b/tests/Drivers/Gd/Decoders/DataUriImageDecoderTest.php @@ -0,0 +1,22 @@ +decode( + sprintf('data:image/jpeg;base64,%s', base64_encode($this->getTestImageData('blue.gif'))) + ); + $this->assertInstanceOf(Image::class, $result); + } +} diff --git a/tests/Drivers/Gd/Decoders/FilePathImageDecoderTest.php b/tests/Drivers/Gd/Decoders/FilePathImageDecoderTest.php new file mode 100644 index 00000000..840063b2 --- /dev/null +++ b/tests/Drivers/Gd/Decoders/FilePathImageDecoderTest.php @@ -0,0 +1,22 @@ +decode( + $this->getTestImagePath() + ); + $this->assertInstanceOf(Image::class, $result); + } +} diff --git a/tests/Drivers/Gd/Decoders/HexColorDecoderTest.php b/tests/Drivers/Gd/Decoders/HexColorDecoderTest.php new file mode 100644 index 00000000..37732117 --- /dev/null +++ b/tests/Drivers/Gd/Decoders/HexColorDecoderTest.php @@ -0,0 +1,26 @@ +decode('ccc'); + $this->assertInstanceOf(Color::class, $result); + + $result = $decoder->decode('#ccc'); + $this->assertInstanceOf(Color::class, $result); + + $result = $decoder->decode('cccccc'); + $this->assertInstanceOf(Color::class, $result); + + $result = $decoder->decode('#cccccc'); + $this->assertInstanceOf(Color::class, $result); + } +} diff --git a/tests/Traits/CanCreateGdTestImage.php b/tests/Traits/CanCreateGdTestImage.php index f9f4238e..e2e5d81a 100644 --- a/tests/Traits/CanCreateGdTestImage.php +++ b/tests/Traits/CanCreateGdTestImage.php @@ -9,10 +9,20 @@ use Intervention\Image\Drivers\Gd\Image; trait CanCreateGdTestImage { + public function getTestImagePath($filename = 'test.jpg'): string + { + return sprintf('%s/../images/%s', __DIR__, $filename); + } + + public function getTestImageData($filename = 'test.jpg'): string + { + return file_get_contents($this->getTestImagePath($filename)); + } + public function createTestImage($filename = 'test.jpg'): Image { return $this->testImageDecoder()->handle( - sprintf('%s/../images/%s', __DIR__, $filename) + $this->getTestImagePath($filename) ); } From 9ce28b7f7827aedde3e08167293e4a31c95b2e3b Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Wed, 1 Dec 2021 18:14:03 +0000 Subject: [PATCH 075/476] Added Gd Encoder Test --- tests/Drivers/Gd/Encoders/PngEncoderTest.php | 29 ++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 tests/Drivers/Gd/Encoders/PngEncoderTest.php diff --git a/tests/Drivers/Gd/Encoders/PngEncoderTest.php b/tests/Drivers/Gd/Encoders/PngEncoderTest.php new file mode 100644 index 00000000..065aa4d6 --- /dev/null +++ b/tests/Drivers/Gd/Encoders/PngEncoderTest.php @@ -0,0 +1,29 @@ +getTestImage(); + $encoder = new PngEncoder(); + $result = $encoder->encode($image); + $this->assertTrue(MimeSniffer::createFromString($result)->matches(new ImagePng)); + } +} \ No newline at end of file From 440e6424033d7cd1bd8aa297494357207e4a5a08 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Wed, 1 Dec 2021 19:20:35 +0100 Subject: [PATCH 076/476] Added missing tests --- tests/Drivers/Gd/ColorTest.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tests/Drivers/Gd/ColorTest.php b/tests/Drivers/Gd/ColorTest.php index 848cadee..989fad85 100644 --- a/tests/Drivers/Gd/ColorTest.php +++ b/tests/Drivers/Gd/ColorTest.php @@ -62,6 +62,15 @@ class ColorTest extends TestCase $this->assertEquals([0, 0, 120, .5], $color->toArray()); } + public function testToInt(): void + { + $color = $this->getTestColor(0, 0, 0, 0); + $this->assertEquals(0, $color->toInt()); + + $color = $this->getTestColor(255, 255, 255, 0); + $this->assertEquals(16777215, $color->toInt()); + } + public function testToHex(): void { $color = $this->getTestColor(181, 55, 23); From 96a3fef055e2682dabdb42b63011b2f7faac689c Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Wed, 1 Dec 2021 19:26:39 +0100 Subject: [PATCH 077/476] Added missing tests --- tests/Drivers/Gd/FrameTest.php | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/tests/Drivers/Gd/FrameTest.php b/tests/Drivers/Gd/FrameTest.php index 3c033103..2497139d 100644 --- a/tests/Drivers/Gd/FrameTest.php +++ b/tests/Drivers/Gd/FrameTest.php @@ -2,6 +2,7 @@ namespace Intervention\Image\Tests\Drivers\Gd; +use GdImage; use Intervention\Image\Drivers\Gd\Frame; use Intervention\Image\Drivers\Gd\Image; use Intervention\Image\Geometry\Size; @@ -20,6 +21,23 @@ class FrameTest extends TestCase $this->assertInstanceOf(Frame::class, $frame); } + public function testGetCore(): void + { + $frame = $this->getTestFrame(); + $this->assertInstanceOf(GdImage::class, $frame->getCore()); + } + + public function testSetCore(): void + { + $core1 = imagecreatetruecolor(3, 2); + $core2 = imagecreatetruecolor(3, 3); + $frame = new Frame($core1); + $this->assertEquals(2, $frame->getSize()->getHeight()); + $result = $frame->setCore($core2); + $this->assertInstanceOf(Frame::Class, $result); + $this->assertEquals(3, $frame->getSize()->getHeight()); + } + public function testGetSize(): void { $frame = $this->getTestFrame(); From eb2b59742990791937c185302c7a5dc6180972be Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Wed, 1 Dec 2021 19:55:48 +0100 Subject: [PATCH 078/476] Added missing tests --- tests/Drivers/Gd/ImageTest.php | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/tests/Drivers/Gd/ImageTest.php b/tests/Drivers/Gd/ImageTest.php index 1635bc61..6553bd03 100644 --- a/tests/Drivers/Gd/ImageTest.php +++ b/tests/Drivers/Gd/ImageTest.php @@ -40,6 +40,39 @@ class ImageTest extends TestCase } } + public function testGetFrames(): void + { + $this->assertInstanceOf(Collection::class, $this->image->getFrames()); + $this->assertCount(3, $this->image->getFrames()); + } + + public function testGetFrame(): void + { + $this->assertInstanceOf(Frame::class, $this->image->getFrame()); + $this->assertInstanceOf(Frame::class, $this->image->getFrame(1)); + } + + public function testAddFrame(): void + { + $this->assertCount(3, $this->image->getFrames()); + $result = $this->image->addFrame(new Frame(imagecreatetruecolor(3, 2))); + $this->assertInstanceOf(Image::class, $result); + $this->assertCount(4, $this->image->getFrames()); + } + + public function testSetGetLoops(): void + { + $this->assertEquals(0, $this->image->loops()); + $result = $this->image->setLoops(12); + $this->assertEquals(12, $this->image->loops()); + $this->assertInstanceOf(Image::class, $result); + } + + public function testIsAnimated(): void + { + $this->assertTrue($this->image->isAnimated()); + } + public function testWidth(): void { $this->assertEquals(3, $this->image->width()); From 9b48b23523f22019704c5d48113bb1803a9a773f Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Thu, 2 Dec 2021 17:45:01 +0100 Subject: [PATCH 079/476] Added missing tests --- src/Drivers/Imagick/Color.php | 10 ++++++++++ src/Interfaces/ColorInterface.php | 1 + tests/Drivers/Gd/InputHandlerTest.php | 8 ++++++++ tests/Drivers/Imagick/ColorTest.php | 21 +++++++++++++++++++++ 4 files changed, 40 insertions(+) diff --git a/src/Drivers/Imagick/Color.php b/src/Drivers/Imagick/Color.php index 904b1c7a..a04e0754 100644 --- a/src/Drivers/Imagick/Color.php +++ b/src/Drivers/Imagick/Color.php @@ -55,4 +55,14 @@ class Color extends AbstractColor implements ColorInterface $this->alpha() ]; } + + public function toInt(): int + { + $r = $this->red(); + $g = $this->green(); + $b = $this->blue(); + $a = intval(round($this->alpha() * 255)); + + return intval(($a << 24) + ($r << 16) + ($g << 8) + $b); + } } diff --git a/src/Interfaces/ColorInterface.php b/src/Interfaces/ColorInterface.php index 3960c23b..e1486987 100644 --- a/src/Interfaces/ColorInterface.php +++ b/src/Interfaces/ColorInterface.php @@ -10,4 +10,5 @@ interface ColorInterface public function alpha(): float; public function toArray(): array; public function toHex(): string; + public function toInt(): int; } diff --git a/tests/Drivers/Gd/InputHandlerTest.php b/tests/Drivers/Gd/InputHandlerTest.php index 41fdf954..22fa5dc8 100644 --- a/tests/Drivers/Gd/InputHandlerTest.php +++ b/tests/Drivers/Gd/InputHandlerTest.php @@ -99,4 +99,12 @@ class InputHandlerTest extends TestCase $this->assertEquals(51, $result->green()); $this->assertEquals(51, $result->blue()); } + + public function testHandleTransparent(): void + { + $handler = new InputHandler(); + $input = 'transparent'; + $result = $handler->handle($input); + $this->assertInstanceOf(Color::class, $result); + } } diff --git a/tests/Drivers/Imagick/ColorTest.php b/tests/Drivers/Imagick/ColorTest.php index 2768c7f6..bf889f0c 100644 --- a/tests/Drivers/Imagick/ColorTest.php +++ b/tests/Drivers/Imagick/ColorTest.php @@ -84,4 +84,25 @@ class ColorTest extends TestCase $this->assertEquals('b53717', $color->toHex()); $this->assertEquals('#b53717', $color->toHex('#')); } + + public function testToInt(): void + { + $color = $this->getTestColor(255, 255, 255); + $this->assertEquals($color->toInt(), 4294967295); + + $color = $this->getTestColor(255, 255, 255, 1); + $this->assertEquals($color->toInt(), 4294967295); + + $color = $this->getTestColor(181, 55, 23, 0.2); + $this->assertEquals($color->toInt(), 867514135); + + $color = $this->getTestColor(255, 255, 255, 0.5); + $this->assertEquals($color->toInt(), 2164260863); + + $color = $this->getTestColor(181, 55, 23, 1); + $this->assertEquals($color->toInt(), 4290066199); + + $color = $this->getTestColor(0, 0, 0, 0); + $this->assertEquals($color->toInt(), 0); + } } From 228189b82edcbd2af83c83e0f7d7f62e065113d9 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Tue, 7 Dec 2021 11:00:06 +0000 Subject: [PATCH 080/476] Added progress from last month Refined ResizeModifier tests Added AbstracImageTest AbstracImageTest AbstracImageTest Added AbstractColorTest FitModifierTests Changed preserve transparency logic in PadModifier PSR fix Imagick PadModifier FillModifier Added imagesavealpha when decoding new gd images Added ImageFactory::newCore() ImageFactory Imagick PadModifier FillModifier Fixed Jpeg Quality paramter Fixed Encoder Parameters PHP 8 only Added PixelateModifiers Removed dev from gitignore Added RgbStringColorDecoder for Imagick Driver Fixed lost transparency on FillModifier Changed default PadModifier backgroud to white Size & Resizer Refactoring Refactored ResizeModifiers Refactored FitModifiers Refactored PadModifier Changed resize & scale signatures Refactored ResizeModifiers Refactored ImageManager Added Constructor property promotion Added ImageObjectDecoder Fixed bugs Added default value for Image::blur() Added methods to image interface Renamed Image::loops() to Image::getLoops() Renamed Image::width() and Image::height() to getWidth(), getHeight() Changed exception texts and type RotateModifiers Added DestroyModifiers Added SharpenModifier Added methods Added InvertModifiers Added Collection::query Added default value to Collection::get Added brightness modifiers Added contrast modifiers Added readme.md --- .gitignore | 1 - composer.json | 5 +- readme.md | 61 +++++ src/Collection.php | 46 +++- src/Drivers/Abstract/AbstractImage.php | 251 ++++++++++-------- .../Abstract/Decoders/AbstractDecoder.php | 6 +- .../Abstract/Encoders/AbstractEncoder.php | 5 - .../Modifiers/AbstractFitModifier.php | 36 +++ .../Modifiers/AbstractPadModifier.php | 31 +++ .../Modifiers/AbstractRotateModifier.php | 38 +++ src/Drivers/Gd/Color.php | 16 +- .../Gd/Decoders/BinaryImageDecoder.php | 6 + src/Drivers/Gd/Decoders/HexColorDecoder.php | 4 + .../Gd/Decoders/ImageObjectDecoder.php | 20 ++ .../Gd/Decoders/RgbStringColorDecoder.php | 35 +++ src/Drivers/Gd/Encoders/GifEncoder.php | 2 +- src/Drivers/Gd/Encoders/JpegEncoder.php | 5 + src/Drivers/Gd/Encoders/PngEncoder.php | 2 +- src/Drivers/Gd/Frame.php | 51 +--- src/Drivers/Gd/Image.php | 4 +- src/Drivers/Gd/ImageFactory.php | 18 +- src/Drivers/Gd/InputHandler.php | 18 +- src/Drivers/Gd/Modifiers/BlurModifier.php | 9 +- .../Gd/Modifiers/BrightnessModifier.php | 23 ++ src/Drivers/Gd/Modifiers/ContrastModifier.php | 23 ++ src/Drivers/Gd/Modifiers/DestroyModifier.php | 18 ++ src/Drivers/Gd/Modifiers/FillModifier.php | 58 ++++ src/Drivers/Gd/Modifiers/FitDownModifier.php | 13 + src/Drivers/Gd/Modifiers/FitModifier.php | 36 ++- src/Drivers/Gd/Modifiers/InvertModifier.php | 18 ++ src/Drivers/Gd/Modifiers/PadDownModifier.php | 25 ++ src/Drivers/Gd/Modifiers/PadModifier.php | 58 ++-- src/Drivers/Gd/Modifiers/PixelateModifier.php | 24 ++ src/Drivers/Gd/Modifiers/PlaceModifier.php | 22 +- .../Gd/Modifiers/ResizeDownModifier.php | 14 + src/Drivers/Gd/Modifiers/ResizeModifier.php | 30 ++- src/Drivers/Gd/Modifiers/RotateModifier.php | 57 +--- .../Gd/Modifiers/ScaleDownModifier.php | 14 + src/Drivers/Gd/Modifiers/ScaleModifier.php | 14 + src/Drivers/Gd/Modifiers/SharpenModifier.php | 37 +++ src/Drivers/Imagick/Color.php | 11 +- .../Imagick/Decoders/BinaryImageDecoder.php | 4 + .../Imagick/Decoders/HexColorDecoder.php | 4 + .../Imagick/Decoders/ImageObjectDecoder.php | 20 ++ .../Decoders/RgbStringColorDecoder.php | 34 +++ src/Drivers/Imagick/Encoders/GifEncoder.php | 2 +- src/Drivers/Imagick/Encoders/JpegEncoder.php | 5 + src/Drivers/Imagick/Frame.php | 18 +- src/Drivers/Imagick/Image.php | 4 +- src/Drivers/Imagick/ImageFactory.php | 15 +- src/Drivers/Imagick/InputHandler.php | 18 +- .../Imagick/Modifiers/BlurModifier.php | 9 +- .../Imagick/Modifiers/BrightnessModifier.php | 23 ++ .../Imagick/Modifiers/ContrastModifier.php | 23 ++ .../Imagick/Modifiers/CropResizeModifier.php | 45 ---- .../Imagick/Modifiers/DestroyModifier.php | 18 ++ .../Imagick/Modifiers/FillModifier.php | 70 +++++ .../Imagick/Modifiers/FitDownModifier.php | 13 + src/Drivers/Imagick/Modifiers/FitModifier.php | 30 +-- .../Imagick/Modifiers/InvertModifier.php | 18 ++ .../Imagick/Modifiers/PadDownModifier.php | 25 ++ src/Drivers/Imagick/Modifiers/PadModifier.php | 76 +++++- .../Imagick/Modifiers/PixelateModifier.php | 37 +++ .../Imagick/Modifiers/PlaceModifier.php | 18 +- .../Imagick/Modifiers/ResizeDownModifier.php | 14 + .../Imagick/Modifiers/ResizeModifier.php | 17 +- .../Imagick/Modifiers/RotateModifier.php | 27 ++ .../Imagick/Modifiers/ScaleDownModifier.php | 14 + .../Imagick/Modifiers/ScaleModifier.php | 14 + .../Imagick/Modifiers/SharpenModifier.php | 23 ++ src/EncodedImage.php | 12 +- src/Exceptions/GeometryException.php | 8 + .../MissingDriverComponentException.php | 8 + src/Geometry/Point.php | 25 +- src/Geometry/Resizer.php | 146 ++++------ src/Geometry/Size.php | 34 +-- src/ImageManager.php | 73 +---- src/Interfaces/ImageInterface.php | 41 ++- src/Traits/CanBuildNewImage.php | 15 ++ src/Traits/CanResolveDriverClass.php | 12 +- tests/AbstractImageTest.php | 240 +++++++++++++++++ tests/CollectionTest.php | 24 ++ tests/Drivers/Abstract/AbstractColorTest.php | 36 +++ .../Gd/Decoders/BinaryImageDecoderTest.php | 12 +- .../Gd/Decoders/ImageObjectDecoderTest.php | 20 ++ .../Gd/Decoders/RgbStringColorDecoderTest.php | 32 +++ tests/Drivers/Gd/Encoders/JpegEncoderTest.php | 6 +- tests/Drivers/Gd/ImageFactoryTest.php | 8 + tests/Drivers/Gd/ImageTest.php | 8 +- .../Gd/Modifiers/BrightnessModifierTest.php | 21 ++ .../Gd/Modifiers/ContrastModifierTest.php | 21 ++ .../Gd/Modifiers/DestroyModifierTest.php | 21 ++ .../Drivers/Gd/Modifiers/FillModifierTest.php | 34 +++ .../Drivers/Gd/Modifiers/FitModifierTest.php | 29 ++ .../Gd/Modifiers/InvertModifierTest.php | 23 ++ .../Gd/Modifiers/PixelateModifierTest.php | 23 ++ .../Gd/Modifiers/ResizeModifierTest.php | 18 +- .../Gd/Modifiers/SharpenModifierTest.php | 21 ++ .../Decoders/ImageObjectDecoderTest.php | 20 ++ .../Decoders/RgbStringColorDecoderTest.php | 32 +++ .../Imagick/Encoders/JpegEncoderTest.php | 6 +- tests/Drivers/Imagick/ImageFactoryTest.php | 25 ++ tests/Drivers/Imagick/ImageTest.php | 4 +- .../Modifiers/BrightnessModifierTest.php | 21 ++ .../Modifiers/ContrastModifierTest.php | 21 ++ .../Imagick/Modifiers/DestroyModifierTest.php | 21 ++ .../Imagick/Modifiers/FillModifierTest.php | 36 +++ .../Imagick/Modifiers/FitModifierTest.php | 29 ++ .../Imagick/Modifiers/InvertModifierTest.php | 23 ++ .../Modifiers/PixelateModifierTest.php | 23 ++ .../Imagick/Modifiers/ResizeModifierTest.php | 20 +- .../Imagick/Modifiers/SharpenModifierTest.php | 21 ++ tests/Geometry/ResizerTest.php | 206 ++++++-------- tests/Geometry/SizeTest.php | 28 +- tests/ImageManagerTest.php | 37 ++- tests/TestCase.php | 16 +- tests/images/blocks.png | Bin 0 -> 467 bytes 117 files changed, 2463 insertions(+), 900 deletions(-) create mode 100644 readme.md create mode 100644 src/Drivers/Abstract/Modifiers/AbstractFitModifier.php create mode 100644 src/Drivers/Abstract/Modifiers/AbstractPadModifier.php create mode 100644 src/Drivers/Abstract/Modifiers/AbstractRotateModifier.php create mode 100644 src/Drivers/Gd/Decoders/ImageObjectDecoder.php create mode 100644 src/Drivers/Gd/Decoders/RgbStringColorDecoder.php create mode 100644 src/Drivers/Gd/Modifiers/BrightnessModifier.php create mode 100644 src/Drivers/Gd/Modifiers/ContrastModifier.php create mode 100644 src/Drivers/Gd/Modifiers/DestroyModifier.php create mode 100644 src/Drivers/Gd/Modifiers/FillModifier.php create mode 100644 src/Drivers/Gd/Modifiers/FitDownModifier.php create mode 100644 src/Drivers/Gd/Modifiers/InvertModifier.php create mode 100644 src/Drivers/Gd/Modifiers/PadDownModifier.php create mode 100644 src/Drivers/Gd/Modifiers/PixelateModifier.php create mode 100644 src/Drivers/Gd/Modifiers/ResizeDownModifier.php create mode 100644 src/Drivers/Gd/Modifiers/ScaleDownModifier.php create mode 100644 src/Drivers/Gd/Modifiers/ScaleModifier.php create mode 100644 src/Drivers/Gd/Modifiers/SharpenModifier.php create mode 100644 src/Drivers/Imagick/Decoders/ImageObjectDecoder.php create mode 100644 src/Drivers/Imagick/Decoders/RgbStringColorDecoder.php create mode 100644 src/Drivers/Imagick/Modifiers/BrightnessModifier.php create mode 100644 src/Drivers/Imagick/Modifiers/ContrastModifier.php delete mode 100644 src/Drivers/Imagick/Modifiers/CropResizeModifier.php create mode 100644 src/Drivers/Imagick/Modifiers/DestroyModifier.php create mode 100644 src/Drivers/Imagick/Modifiers/FillModifier.php create mode 100644 src/Drivers/Imagick/Modifiers/FitDownModifier.php create mode 100644 src/Drivers/Imagick/Modifiers/InvertModifier.php create mode 100644 src/Drivers/Imagick/Modifiers/PadDownModifier.php create mode 100644 src/Drivers/Imagick/Modifiers/PixelateModifier.php create mode 100644 src/Drivers/Imagick/Modifiers/ResizeDownModifier.php create mode 100644 src/Drivers/Imagick/Modifiers/RotateModifier.php create mode 100644 src/Drivers/Imagick/Modifiers/ScaleDownModifier.php create mode 100644 src/Drivers/Imagick/Modifiers/ScaleModifier.php create mode 100644 src/Drivers/Imagick/Modifiers/SharpenModifier.php create mode 100644 src/Exceptions/GeometryException.php create mode 100644 src/Exceptions/MissingDriverComponentException.php create mode 100644 src/Traits/CanBuildNewImage.php create mode 100644 tests/AbstractImageTest.php create mode 100644 tests/Drivers/Abstract/AbstractColorTest.php create mode 100644 tests/Drivers/Gd/Decoders/ImageObjectDecoderTest.php create mode 100644 tests/Drivers/Gd/Decoders/RgbStringColorDecoderTest.php create mode 100644 tests/Drivers/Gd/Modifiers/BrightnessModifierTest.php create mode 100644 tests/Drivers/Gd/Modifiers/ContrastModifierTest.php create mode 100644 tests/Drivers/Gd/Modifiers/DestroyModifierTest.php create mode 100644 tests/Drivers/Gd/Modifiers/FillModifierTest.php create mode 100644 tests/Drivers/Gd/Modifiers/FitModifierTest.php create mode 100644 tests/Drivers/Gd/Modifiers/InvertModifierTest.php create mode 100644 tests/Drivers/Gd/Modifiers/PixelateModifierTest.php create mode 100644 tests/Drivers/Gd/Modifiers/SharpenModifierTest.php create mode 100644 tests/Drivers/Imagick/Decoders/ImageObjectDecoderTest.php create mode 100644 tests/Drivers/Imagick/Decoders/RgbStringColorDecoderTest.php create mode 100644 tests/Drivers/Imagick/ImageFactoryTest.php create mode 100644 tests/Drivers/Imagick/Modifiers/BrightnessModifierTest.php create mode 100644 tests/Drivers/Imagick/Modifiers/ContrastModifierTest.php create mode 100644 tests/Drivers/Imagick/Modifiers/DestroyModifierTest.php create mode 100644 tests/Drivers/Imagick/Modifiers/FillModifierTest.php create mode 100644 tests/Drivers/Imagick/Modifiers/FitModifierTest.php create mode 100644 tests/Drivers/Imagick/Modifiers/InvertModifierTest.php create mode 100644 tests/Drivers/Imagick/Modifiers/PixelateModifierTest.php create mode 100644 tests/Drivers/Imagick/Modifiers/SharpenModifierTest.php create mode 100644 tests/images/blocks.png diff --git a/.gitignore b/.gitignore index 799dd941..95483750 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,5 @@ .DS_Store composer.lock vendor/ -dev/ .idea/ .phpunit.result.cache \ No newline at end of file diff --git a/composer.json b/composer.json index b075794e..dabcb353 100644 --- a/composer.json +++ b/composer.json @@ -12,12 +12,13 @@ } ], "require": { - "php": "^7.4|^8", + "php": "^8", "intervention/gif": "dev-master", "intervention/mimesniffer": "^0.4.2" }, "require-dev": { - "phpunit/phpunit": "^9" + "phpunit/phpunit": "^9", + "mockery/mockery": "^1.4" }, "autoload": { "psr-4": { diff --git a/readme.md b/readme.md new file mode 100644 index 00000000..11662516 --- /dev/null +++ b/readme.md @@ -0,0 +1,61 @@ +# Intervention Image +## PHP Image Manipulation + +[![Latest Version](https://img.shields.io/packagist/v/intervention/image.svg)](https://packagist.org/packages/intervention/image) +[![Build Status](https://travis-ci.org/Intervention/image.png?branch=master)](https://travis-ci.org/Intervention/image) +[![Monthly Downloads](https://img.shields.io/packagist/dm/intervention/image.svg)](https://packagist.org/packages/intervention/image/stats) + +Intervention Image is a **PHP image handling and manipulation** library providing an easier and expressive way to create, edit, and compose images. + +- Simple interface for +- Driver agnostic +- Support for animated images +- Framework-agnostic, will work with any project +- PSR-12 compliant + +## Code Examples + +```php +// create image manager with desired driver +$manager = new ImageManager('gd') + +// open an image file +$image = $manager->make('images/example.jpg'); + +// resize image instance +$image->resize(320, 240); + +// insert a watermark +$image->place('images/watermark.png'); + +// encode edited image +$encoded = $image->toJpg(); + +// save encoded image +$encoded->save('images/example.jpg'); +``` + +## Requirements + +- PHP >=8.0 + +## Supported Image Libraries + +- GD Library +- Imagick PHP extension + +## Installation + +```bash +composer require intervention/image +``` + +## Getting started + +Learn the [basics](https://image.intervention.io/) on how to use Intervention Image and more with the [official documentation](https://image.intervention.io/). + +## License + +Intervention Image is licensed under the [MIT License](http://opensource.org/licenses/MIT). + +Copyright 2021 Oliver Vogel diff --git a/src/Collection.php b/src/Collection.php index 99699726..5d669ae3 100644 --- a/src/Collection.php +++ b/src/Collection.php @@ -7,20 +7,14 @@ use Intervention\Image\Interfaces\CollectionInterface; use ArrayIterator; use Countable; use IteratorAggregate; +use RecursiveIteratorIterator; +use RecursiveArrayIterator; class Collection implements CollectionInterface, IteratorAggregate, Countable { - protected $items = []; - - /** - * Create a collection. - * - * @param array $items - * @return void - */ - public function __construct(array $items = []) + public function __construct(protected array $items = []) { - $this->items = $items; + // } /** @@ -106,15 +100,25 @@ class Collection implements CollectionInterface, IteratorAggregate, Countable * @param integer $key * @return mixed */ - public function get(int $key = 0) + public function get(int $key = 0, $default = null) { if (! array_key_exists($key, $this->items)) { - return null; + return $default; } return $this->items[$key]; } + public function query(string $query, $default = null) + { + $items = $this->getItemsFlat(); + if (!array_key_exists($query, $items)) { + return $default; + } + + return $items[$query]; + } + public function map(callable $callback): self { $items = array_map(function ($item) use ($callback) { @@ -136,4 +140,22 @@ class Collection implements CollectionInterface, IteratorAggregate, Countable return $this; } + + private function getItemsFlat(): array + { + $iterator = new RecursiveIteratorIterator( + new RecursiveArrayIterator($this->items) + ); + + $items = []; + foreach ($iterator as $value) { + $keys = []; + foreach (range(0, $iterator->getDepth()) as $depth) { + $keys[] = $iterator->getSubIterator($depth)->key(); + } + $items[join('.', $keys)] = $value; + } + + return $items; + } } diff --git a/src/Drivers/Abstract/AbstractImage.php b/src/Drivers/Abstract/AbstractImage.php index 2837a3cd..29ae8f9b 100644 --- a/src/Drivers/Abstract/AbstractImage.php +++ b/src/Drivers/Abstract/AbstractImage.php @@ -5,6 +5,7 @@ namespace Intervention\Image\Drivers\Abstract; use Intervention\Image\Collection; use Intervention\Image\EncodedImage; use Intervention\Image\Exceptions\NotWritableException; +use Intervention\Image\Geometry\Point; use Intervention\Image\Geometry\Resizer; use Intervention\Image\Geometry\Size; use Intervention\Image\Interfaces\EncoderInterface; @@ -12,18 +13,17 @@ use Intervention\Image\Interfaces\FrameInterface; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; use Intervention\Image\Interfaces\SizeInterface; +use Intervention\Image\Traits\CanHandleInput; use Intervention\Image\Traits\CanResolveDriverClass; abstract class AbstractImage { use CanResolveDriverClass; + use CanHandleInput; - protected $loops = 0; - protected $frames; - - public function __construct(Collection $frames) + public function __construct(protected Collection $frames, protected $loops = 0) { - $this->frames = $frames; + // } public function getIterator(): Collection @@ -55,14 +55,19 @@ abstract class AbstractImage return $this; } - public function loops(): int + public function getLoops(): int { return $this->loops; } public function getSize(): SizeInterface { - return new Size($this->width(), $this->height()); + return new Size($this->getWidth(), $this->getHeight()); + } + + public function size(): SizeInterface + { + return $this->getSize(); } public function isAnimated(): bool @@ -80,7 +85,7 @@ abstract class AbstractImage return $encoder->encode($this); } - public function toJpeg(?int $quality = null): EncodedImage + public function toJpeg(int $quality = 75): EncodedImage { return $this->encode( $this->resolveDriverClass('Encoders\JpegEncoder', $quality) @@ -108,122 +113,38 @@ abstract class AbstractImage ); } - public function blur(int $amount): ImageInterface + public function invert(): ImageInterface + { + return $this->modify( + $this->resolveDriverClass('Modifiers\InvertModifier') + ); + } + + public function brightness(int $level): ImageInterface + { + return $this->modify( + $this->resolveDriverClass('Modifiers\BrightnessModifier', $level) + ); + } + + public function contrast(int $level): ImageInterface + { + return $this->modify( + $this->resolveDriverClass('Modifiers\ContrastModifier', $level) + ); + } + + public function blur(int $amount = 5): ImageInterface { return $this->modify( $this->resolveDriverClass('Modifiers\BlurModifier', $amount) ); } - public function pickColors(int $x, int $y): Collection + public function rotate(float $angle, $background = 'ffffff'): ImageInterface { - $colors = new Collection(); - foreach ($this->getFrames() as $key => $frame) { - $colors->push($this->pickColor($x, $y, $key)); - } - - return $colors; - } - - public function resize(...$arguments): ImageInterface - { - $resized = Resizer::make()->setTargetSizeByArray($arguments) - ->resize($this->getSize()); - return $this->modify( - $this->resolveDriverClass('Modifiers\ResizeModifier', $resized) - ); - } - - public function resizeDown(...$arguments): ImageInterface - { - $resized = Resizer::make()->setTargetSizeByArray($arguments) - ->resizeDown($this->getSize()); - - return $this->modify( - $this->resolveDriverClass('Modifiers\ResizeModifier', $resized) - ); - } - - public function scale(...$arguments): ImageInterface - { - $resized = Resizer::make()->setTargetSizeByArray($arguments) - ->scale($this->getSize()); - - return $this->modify( - $this->resolveDriverClass('Modifiers\ResizeModifier', $resized) - ); - } - - public function scaleDown(...$arguments): ImageInterface - { - $resized = Resizer::make()->setTargetSizeByArray($arguments) - ->scaleDown($this->getSize()); - - return $this->modify( - $this->resolveDriverClass('Modifiers\ResizeModifier', $resized) - ); - } - - public function fit(int $width, int $height, string $position = 'center'): ImageInterface - { - // original - $imagesize = $this->getSize(); - - // crop - $crop = new Size($width, $height); - $crop = $crop->contain($imagesize)->alignPivotTo($imagesize, $position); - - // resize - $resize = $crop->scale($width, $height); - - return $this->modify( - $this->resolveDriverClass('Modifiers\FitModifier', $crop, $resize) - ); - } - - public function fitDown(int $width, int $height, string $position = 'center'): ImageInterface - { - // original - $imagesize = $this->getSize(); - - // crop - $crop = new Size($width, $height); - $crop = $crop->contain($imagesize)->alignPivotTo($imagesize, $position); - - // resize - $resize = $crop->scaleDown($width, $height); - - return $this->modify( - $this->resolveDriverClass('Modifiers\FitModifier', $crop, $resize) - ); - } - - public function pad(int $width, int $height, string $position = 'center', $backgroundColor = 'transparent'): ImageInterface - { - // original - $imagesize = $this->getSize(); - - $resize = new Size($width, $height); - $crop = $imagesize->contain($resize)->alignPivotTo($resize, $position); - - return $this->modify( - $this->resolveDriverClass('Modifiers\PadModifier', $crop, $resize, $backgroundColor) - ); - } - - public function padDown(int $width, int $height, string $position = 'center', $backgroundColor = 'transparent'): ImageInterface - { - // original - $imagesize = $this->getSize(); - - $resize = new Size($width, $height); - $resize = $resize->resizeDown($imagesize); - $crop = $imagesize->contain($resize)->alignPivotTo($resize, $position); - - - return $this->modify( - $this->resolveDriverClass('Modifiers\PadModifier', $crop, $resize, $backgroundColor) + $this->resolveDriverClass('Modifiers\RotateModifier', $angle, $background) ); } @@ -240,10 +161,104 @@ abstract class AbstractImage ); } - public function rotate(float $angle, $backgroundColor = 'ffffff'): ImageInterface + public function fill($color, ?int $x = null, ?int $y = null): ImageInterface + { + $color = $this->handleInput($color); + $position = (is_null($x) && is_null($y)) ? null : new Point($x, $y); + + return $this->modify( + $this->resolveDriverClass( + 'Modifiers\FillModifier', + $color, + $position + ) + ); + } + + public function pixelate(int $size): ImageInterface { return $this->modify( - $this->resolveDriverClass('Modifiers\RotateModifier', $angle, $backgroundColor) + $this->resolveDriverClass('Modifiers\PixelateModifier', $size) + ); + } + + public function sharpen(int $amount = 10): ImageInterface + { + return $this->modify( + $this->resolveDriverClass('Modifiers\SharpenModifier', $amount) + ); + } + + public function pickColors(int $x, int $y): Collection + { + $colors = new Collection(); + foreach ($this->getFrames() as $key => $frame) { + $colors->push($this->pickColor($x, $y, $key)); + } + + return $colors; + } + + public function resize(?int $width = null, ?int $height = null): ImageInterface + { + return $this->modify( + $this->resolveDriverClass('Modifiers\ResizeModifier', $width, $height) + ); + } + + public function resizeDown(?int $width = null, ?int $height = null): ImageInterface + { + return $this->modify( + $this->resolveDriverClass('Modifiers\ResizeDownModifier', $width, $height) + ); + } + + public function scale(?int $width = null, ?int $height = null): ImageInterface + { + return $this->modify( + $this->resolveDriverClass('Modifiers\ScaleModifier', $width, $height) + ); + } + + public function scaleDown(?int $width = null, ?int $height = null): ImageInterface + { + return $this->modify( + $this->resolveDriverClass('Modifiers\ScaleDownModifier', $width, $height) + ); + } + + public function fit(int $width, int $height, string $position = 'center'): ImageInterface + { + return $this->modify( + $this->resolveDriverClass('Modifiers\FitModifier', $width, $height, $position) + ); + } + + public function fitDown(int $width, int $height, string $position = 'center'): ImageInterface + { + return $this->modify( + $this->resolveDriverClass('Modifiers\FitDownModifier', $width, $height, $position) + ); + } + + public function pad(int $width, int $height, $background = 'ffffff', string $position = 'center'): ImageInterface + { + return $this->modify( + $this->resolveDriverClass('Modifiers\PadModifier', $width, $height, $background, $position) + ); + } + + public function padDown(int $width, int $height, $background = 'ffffff', string $position = 'center'): ImageInterface + { + return $this->modify( + $this->resolveDriverClass('Modifiers\PadDownModifier', $width, $height, $background, $position) + ); + } + + public function destroy(): void + { + $this->modify( + $this->resolveDriverClass('Modifiers\DestroyModifier') ); } } diff --git a/src/Drivers/Abstract/Decoders/AbstractDecoder.php b/src/Drivers/Abstract/Decoders/AbstractDecoder.php index 32a8070b..93740041 100644 --- a/src/Drivers/Abstract/Decoders/AbstractDecoder.php +++ b/src/Drivers/Abstract/Decoders/AbstractDecoder.php @@ -10,11 +10,9 @@ use Intervention\MimeSniffer\AbstractType; abstract class AbstractDecoder { - protected $successor = null; - - public function __construct(?AbstractDecoder $successor = null) + public function __construct(protected ?AbstractDecoder $successor = null) { - $this->successor = $successor; + // } final public function handle($input): null|ImageInterface|ColorInterface diff --git a/src/Drivers/Abstract/Encoders/AbstractEncoder.php b/src/Drivers/Abstract/Encoders/AbstractEncoder.php index a557311f..726093eb 100644 --- a/src/Drivers/Abstract/Encoders/AbstractEncoder.php +++ b/src/Drivers/Abstract/Encoders/AbstractEncoder.php @@ -8,11 +8,6 @@ abstract class AbstractEncoder { protected $quality; - public function __construct(?int $quality = null) - { - $this->quality = $quality; - } - /** * Get return value of callback through output buffer * diff --git a/src/Drivers/Abstract/Modifiers/AbstractFitModifier.php b/src/Drivers/Abstract/Modifiers/AbstractFitModifier.php new file mode 100644 index 00000000..fa9e6b1b --- /dev/null +++ b/src/Drivers/Abstract/Modifiers/AbstractFitModifier.php @@ -0,0 +1,36 @@ +getSize(); + + $crop = new Size($this->width, $this->height); + $crop = $crop->contain( + $imagesize->getWidth(), + $imagesize->getHeight() + )->alignPivotTo($imagesize, $this->position); + + return $crop; + } + + protected function getResizeSize(SizeInterface $size): SizeInterface + { + return $size->scale($this->width, $this->height); + } +} diff --git a/src/Drivers/Abstract/Modifiers/AbstractPadModifier.php b/src/Drivers/Abstract/Modifiers/AbstractPadModifier.php new file mode 100644 index 00000000..1cd4f1ec --- /dev/null +++ b/src/Drivers/Abstract/Modifiers/AbstractPadModifier.php @@ -0,0 +1,31 @@ +getSize() + ->contain($this->width, $this->height) + ->alignPivotTo($this->getResizeSize($image), $this->position); + } + + protected function getResizeSize(ImageInterface $image): SizeInterface + { + return new Size($this->width, $this->height); + } +} diff --git a/src/Drivers/Abstract/Modifiers/AbstractRotateModifier.php b/src/Drivers/Abstract/Modifiers/AbstractRotateModifier.php new file mode 100644 index 00000000..016211e3 --- /dev/null +++ b/src/Drivers/Abstract/Modifiers/AbstractRotateModifier.php @@ -0,0 +1,38 @@ +angle, 360); + } + + protected function backgroundColor(): ColorInterface + { + try { + return $this->handleInput($this->background); + } catch (DecoderException $e) { + throw new TypeException("rotate(): Argument #2 must be a color value."); + } + } +} diff --git a/src/Drivers/Gd/Color.php b/src/Drivers/Gd/Color.php index a7ef9a53..46904b37 100644 --- a/src/Drivers/Gd/Color.php +++ b/src/Drivers/Gd/Color.php @@ -7,21 +7,9 @@ use Intervention\Image\Interfaces\ColorInterface; class Color extends AbstractColor implements ColorInterface { - /** - * GD library integer value of color - * - * @var int - */ - protected $value; - - /** - * Create new color instance - * - * @param int $value - */ - public function __construct(int $value = 0) + public function __construct(protected int $value = 0) { - $this->value = $value; + // } public function red(): int diff --git a/src/Drivers/Gd/Decoders/BinaryImageDecoder.php b/src/Drivers/Gd/Decoders/BinaryImageDecoder.php index f60a9702..f4a36cf2 100644 --- a/src/Drivers/Gd/Decoders/BinaryImageDecoder.php +++ b/src/Drivers/Gd/Decoders/BinaryImageDecoder.php @@ -19,6 +19,10 @@ class BinaryImageDecoder extends AbstractDecoder implements DecoderInterface { public function decode($input): ImageInterface|ColorInterface { + if (!is_string($input)) { + $this->fail(); + } + if (! $this->inputType($input)->isBinary()) { $this->fail(); } @@ -37,6 +41,8 @@ class BinaryImageDecoder extends AbstractDecoder implements DecoderInterface imagepalettetotruecolor($gd); } + imagesavealpha($gd, true); + return new Image(new Collection([new Frame($gd)])); } diff --git a/src/Drivers/Gd/Decoders/HexColorDecoder.php b/src/Drivers/Gd/Decoders/HexColorDecoder.php index 940c327e..2329fe50 100644 --- a/src/Drivers/Gd/Decoders/HexColorDecoder.php +++ b/src/Drivers/Gd/Decoders/HexColorDecoder.php @@ -11,6 +11,10 @@ class HexColorDecoder extends ArrayColorDecoder implements DecoderInterface { public function decode($input): ImageInterface|ColorInterface { + if (!is_string($input)) { + $this->fail(); + } + $pattern = '/^#?([a-f0-9]{1,2})([a-f0-9]{1,2})([a-f0-9]{1,2})$/i'; $result = preg_match($pattern, $input, $matches); diff --git a/src/Drivers/Gd/Decoders/ImageObjectDecoder.php b/src/Drivers/Gd/Decoders/ImageObjectDecoder.php new file mode 100644 index 00000000..9dffd5b4 --- /dev/null +++ b/src/Drivers/Gd/Decoders/ImageObjectDecoder.php @@ -0,0 +1,20 @@ +fail(); + } + + return $input; + } +} diff --git a/src/Drivers/Gd/Decoders/RgbStringColorDecoder.php b/src/Drivers/Gd/Decoders/RgbStringColorDecoder.php new file mode 100644 index 00000000..53ded7c5 --- /dev/null +++ b/src/Drivers/Gd/Decoders/RgbStringColorDecoder.php @@ -0,0 +1,35 @@ +fail(); + } + + if (substr($input, 0, 3) !== 'rgb') { + $this->fail(); + } + + // rgb string like rgb(102, 200, 0) + $pattern = "/^rgb ?\((?P[0-9]{1,3}), ?(?P[0-9]{1,3}), ?(?P[0-9]{1,3})\)$/i"; + if ((bool) preg_match($pattern, $input, $matches)) { + return parent::decode([$matches['r'], $matches['g'], $matches['b']]); + } + + // rgba string like "rgba(200, 10, 30, 0.5)" + $pattern = "/^rgba ?\(((?P[0-9]{1,3})), ?((?P[0-9]{1,3})), ?((?P[0-9]{1,3})), ?(?P
[0-9.]{1,4})\)$/i"; + if ((bool) preg_match($pattern, $input, $matches)) { + return parent::decode([$matches['r'], $matches['g'], $matches['b'], $matches['a']]); + } + + $this->fail(); + } +} diff --git a/src/Drivers/Gd/Encoders/GifEncoder.php b/src/Drivers/Gd/Encoders/GifEncoder.php index 99d37001..9438e8a8 100644 --- a/src/Drivers/Gd/Encoders/GifEncoder.php +++ b/src/Drivers/Gd/Encoders/GifEncoder.php @@ -25,7 +25,7 @@ class GifEncoder extends AbstractEncoder implements EncoderInterface protected function encodeAnimated($image): EncodedImage { - $builder = GifBuilder::canvas($image->width(), $image->height(), $image->loops()); + $builder = GifBuilder::canvas($image->getWidth(), $image->getHeight(), $image->getLoops()); foreach ($image as $key => $frame) { $source = $this->encode($frame->toImage()); $builder->addFrame($source, $frame->getDelay()); diff --git a/src/Drivers/Gd/Encoders/JpegEncoder.php b/src/Drivers/Gd/Encoders/JpegEncoder.php index 02a3e969..09e695e7 100644 --- a/src/Drivers/Gd/Encoders/JpegEncoder.php +++ b/src/Drivers/Gd/Encoders/JpegEncoder.php @@ -9,6 +9,11 @@ use Intervention\Image\Interfaces\ImageInterface; class JpegEncoder extends AbstractEncoder implements EncoderInterface { + public function __construct(int $quality) + { + $this->quality = $quality; + } + public function encode(ImageInterface $image): EncodedImage { $data = $this->getBuffered(function () use ($image) { diff --git a/src/Drivers/Gd/Encoders/PngEncoder.php b/src/Drivers/Gd/Encoders/PngEncoder.php index f93afb39..bc578b47 100644 --- a/src/Drivers/Gd/Encoders/PngEncoder.php +++ b/src/Drivers/Gd/Encoders/PngEncoder.php @@ -12,7 +12,7 @@ class PngEncoder extends AbstractEncoder implements EncoderInterface public function encode(ImageInterface $image): EncodedImage { $data = $this->getBuffered(function () use ($image) { - imagepng($image->getFrames()->first()->getCore()); + imagepng($image->getFrames()->first()->getCore(), null, -1); }); return new EncodedImage($data, 'image/png'); diff --git a/src/Drivers/Gd/Frame.php b/src/Drivers/Gd/Frame.php index 7cbca2e7..ea33a036 100644 --- a/src/Drivers/Gd/Frame.php +++ b/src/Drivers/Gd/Frame.php @@ -12,44 +12,14 @@ use Intervention\Image\Interfaces\SizeInterface; class Frame extends AbstractFrame implements FrameInterface { - /** - * Gd image representation of frame - * - * @var GdImage - */ - protected $core; - - /** - * Delay time in seconds after next frame is shown - * - * @var float - */ - protected $delay = 0; - - /** - * Disposal method of frame - * - * @var integer - */ - protected $dispose = 1; - - /** - * Left offset in pixel - * - * @var integer - */ - protected $offset_left = 0; - - /** - * Top offset in pixel - * - * @var integer - */ - protected $offset_top = 0; - - public function __construct(GdImage $core) - { - $this->core = $core; + public function __construct( + protected GdImage $core, + protected float $delay = 0, + protected int $dispose = 1, + protected int $offset_left = 0, + protected int $offset_top = 0 + ) { + // } public function getCore(): GdImage @@ -64,6 +34,11 @@ class Frame extends AbstractFrame implements FrameInterface return $this; } + public function unsetCore(): void + { + unset($this->core); + } + public function getSize(): SizeInterface { return new Size(imagesx($this->core), imagesy($this->core)); diff --git a/src/Drivers/Gd/Image.php b/src/Drivers/Gd/Image.php index d226269a..a8f00b98 100644 --- a/src/Drivers/Gd/Image.php +++ b/src/Drivers/Gd/Image.php @@ -16,12 +16,12 @@ use IteratorAggregate; class Image extends AbstractImage implements ImageInterface, IteratorAggregate { - public function width(): int + public function getWidth(): int { return imagesx($this->getFrame()->getCore()); } - public function height(): int + public function getHeight(): int { return imagesy($this->getFrame()->getCore()); } diff --git a/src/Drivers/Gd/ImageFactory.php b/src/Drivers/Gd/ImageFactory.php index f68017e2..467e0332 100644 --- a/src/Drivers/Gd/ImageFactory.php +++ b/src/Drivers/Gd/ImageFactory.php @@ -2,6 +2,7 @@ namespace Intervention\Image\Drivers\Gd; +use GdImage; use Intervention\Image\Collection; use Intervention\Image\Interfaces\FactoryInterface; use Intervention\Image\Interfaces\ImageInterface; @@ -10,15 +11,20 @@ class ImageFactory implements FactoryInterface { public function newImage(int $width, int $height): ImageInterface { - $gd = imagecreatetruecolor($width, $height); - $color = imagecolorallocatealpha($gd, 0, 0, 0, 127); - imagefill($gd, 0, 0, $color); - imagesavealpha($gd, true); - return new Image( new Collection([ - new Frame($gd) + new Frame($this->newCore($width, $height)) ]) ); } + + public function newCore(int $width, int $height): GdImage + { + $core = imagecreatetruecolor($width, $height); + $color = imagecolorallocatealpha($core, 0, 0, 0, 127); + imagefill($core, 0, 0, $color); + imagesavealpha($core, true); + + return $core; + } } diff --git a/src/Drivers/Gd/InputHandler.php b/src/Drivers/Gd/InputHandler.php index 02fabd68..3b66efe2 100644 --- a/src/Drivers/Gd/InputHandler.php +++ b/src/Drivers/Gd/InputHandler.php @@ -9,13 +9,17 @@ class InputHandler extends AbstractInputHandler { protected function chain(): AbstractDecoder { - return new Decoders\ArrayColorDecoder( - new Decoders\HexColorDecoder( - new Decoders\TransparentColorDecoder( - new Decoders\FilePathImageDecoder( - new Decoders\BinaryImageDecoder( - new Decoders\DataUriImageDecoder( - new Decoders\Base64ImageDecoder() + return new Decoders\ImageObjectDecoder( + new Decoders\ArrayColorDecoder( + new Decoders\RgbStringColorDecoder( + new Decoders\HexColorDecoder( + new Decoders\TransparentColorDecoder( + new Decoders\FilePathImageDecoder( + new Decoders\BinaryImageDecoder( + new Decoders\DataUriImageDecoder( + new Decoders\Base64ImageDecoder() + ) + ) ) ) ) diff --git a/src/Drivers/Gd/Modifiers/BlurModifier.php b/src/Drivers/Gd/Modifiers/BlurModifier.php index 376b364f..508d34bd 100644 --- a/src/Drivers/Gd/Modifiers/BlurModifier.php +++ b/src/Drivers/Gd/Modifiers/BlurModifier.php @@ -8,14 +8,9 @@ use Intervention\Image\Interfaces\ModifierInterface; class BlurModifier implements ModifierInterface { - /** - * Create new modifier - * - * @param int $amount Blur amount (0 - 100%) - */ - public function __construct(int $amount) + public function __construct(protected int $amount) { - $this->amount = $amount; + // } public function apply(ImageInterface $image): ImageInterface diff --git a/src/Drivers/Gd/Modifiers/BrightnessModifier.php b/src/Drivers/Gd/Modifiers/BrightnessModifier.php new file mode 100644 index 00000000..ee306bf0 --- /dev/null +++ b/src/Drivers/Gd/Modifiers/BrightnessModifier.php @@ -0,0 +1,23 @@ +getCore(), IMG_FILTER_BRIGHTNESS, ($this->level * 2.55)); + } + + return $image; + } +} diff --git a/src/Drivers/Gd/Modifiers/ContrastModifier.php b/src/Drivers/Gd/Modifiers/ContrastModifier.php new file mode 100644 index 00000000..28f0107a --- /dev/null +++ b/src/Drivers/Gd/Modifiers/ContrastModifier.php @@ -0,0 +1,23 @@ +getCore(), IMG_FILTER_CONTRAST, ($this->level * -1)); + } + + return $image; + } +} diff --git a/src/Drivers/Gd/Modifiers/DestroyModifier.php b/src/Drivers/Gd/Modifiers/DestroyModifier.php new file mode 100644 index 00000000..2ea95416 --- /dev/null +++ b/src/Drivers/Gd/Modifiers/DestroyModifier.php @@ -0,0 +1,18 @@ +unsetCore(); + } + + return $image; + } +} diff --git a/src/Drivers/Gd/Modifiers/FillModifier.php b/src/Drivers/Gd/Modifiers/FillModifier.php new file mode 100644 index 00000000..70e5fe2c --- /dev/null +++ b/src/Drivers/Gd/Modifiers/FillModifier.php @@ -0,0 +1,58 @@ + $frame) { + if ($this->hasPosition()) { + $this->floodFillWithColor($frame); + } else { + $this->fillAllWithColor($frame); + } + } + + return $image; + } + + protected function floodFillWithColor(Frame $frame): void + { + imagefill( + $frame->getCore(), + $this->position->getX(), + $this->position->getY(), + $this->color->toInt() + ); + } + + protected function fillAllWithColor(Frame $frame): void + { + imagealphablending($frame->getCore(), true); + imagefilledrectangle( + $frame->getCore(), + 0, + 0, + $frame->getSize()->getWidth() - 1, + $frame->getSize()->getHeight() - 1, + $this->color->toInt() + ); + } + + protected function hasPosition(): bool + { + return !empty($this->position); + } +} diff --git a/src/Drivers/Gd/Modifiers/FitDownModifier.php b/src/Drivers/Gd/Modifiers/FitDownModifier.php new file mode 100644 index 00000000..5bf00774 --- /dev/null +++ b/src/Drivers/Gd/Modifiers/FitDownModifier.php @@ -0,0 +1,13 @@ +scaleDown($this->width, $this->height); + } +} diff --git a/src/Drivers/Gd/Modifiers/FitModifier.php b/src/Drivers/Gd/Modifiers/FitModifier.php index 35d07bdd..194376a5 100644 --- a/src/Drivers/Gd/Modifiers/FitModifier.php +++ b/src/Drivers/Gd/Modifiers/FitModifier.php @@ -2,6 +2,8 @@ namespace Intervention\Image\Drivers\Gd\Modifiers; +use Intervention\Image\Drivers\Abstract\Modifiers\AbstractFitModifier; +use Intervention\Image\Geometry\Size; use Intervention\Image\Interfaces\FrameInterface; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; @@ -9,32 +11,26 @@ use Intervention\Image\Interfaces\SizeInterface; use Intervention\Image\Traits\CanHandleInput; use Intervention\Image\Traits\CanResizeGeometrically; -class FitModifier implements ModifierInterface +class FitModifier extends AbstractFitModifier implements ModifierInterface { - protected $crop; - protected $resize; - - public function __construct(SizeInterface $crop, SizeInterface $resize) - { - $this->crop = $crop; - $this->resize = $resize; - } - public function apply(ImageInterface $image): ImageInterface { + $crop = $this->getCropSize($image); + $resize = $this->getResizeSize($crop); + foreach ($image as $frame) { - $this->modify($frame); + $this->modifyFrame($frame, $crop, $resize); } return $image; } - protected function modify(FrameInterface $frame): void + protected function modifyFrame(FrameInterface $frame, SizeInterface $crop, SizeInterface $resize): void { // create new image $modified = imagecreatetruecolor( - $this->resize->getWidth(), - $this->resize->getHeight() + $resize->getWidth(), + $resize->getHeight() ); // get current image @@ -59,12 +55,12 @@ class FitModifier implements ModifierInterface $current, 0, 0, - $this->crop->getPivot()->getX(), - $this->crop->getPivot()->getY(), - $this->resize->getWidth(), - $this->resize->getHeight(), - $this->crop->getWidth(), - $this->crop->getHeight() + $crop->getPivot()->getX(), + $crop->getPivot()->getY(), + $resize->getWidth(), + $resize->getHeight(), + $crop->getWidth(), + $crop->getHeight() ); imagedestroy($current); diff --git a/src/Drivers/Gd/Modifiers/InvertModifier.php b/src/Drivers/Gd/Modifiers/InvertModifier.php new file mode 100644 index 00000000..654e99b1 --- /dev/null +++ b/src/Drivers/Gd/Modifiers/InvertModifier.php @@ -0,0 +1,18 @@ +getCore(), IMG_FILTER_NEGATE); + } + + return $image; + } +} diff --git a/src/Drivers/Gd/Modifiers/PadDownModifier.php b/src/Drivers/Gd/Modifiers/PadDownModifier.php new file mode 100644 index 00000000..cb80248d --- /dev/null +++ b/src/Drivers/Gd/Modifiers/PadDownModifier.php @@ -0,0 +1,25 @@ +getResizeSize($image); + + return $image->getSize() + ->contain($resize->getWidth(), $resize->getHeight()) + ->alignPivotTo($resize, $this->position); + } + + protected function getResizeSize(ImageInterface $image): SizeInterface + { + return (new Size($this->width, $this->height)) + ->resizeDown($image->getWidth(), $image->getHeight()); + } +} diff --git a/src/Drivers/Gd/Modifiers/PadModifier.php b/src/Drivers/Gd/Modifiers/PadModifier.php index e0680b77..630bb284 100644 --- a/src/Drivers/Gd/Modifiers/PadModifier.php +++ b/src/Drivers/Gd/Modifiers/PadModifier.php @@ -2,6 +2,8 @@ namespace Intervention\Image\Drivers\Gd\Modifiers; +use Intervention\Image\Drivers\Abstract\Modifiers\AbstractPadModifier; +use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\FrameInterface; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; @@ -9,68 +11,54 @@ use Intervention\Image\Interfaces\SizeInterface; use Intervention\Image\Traits\CanHandleInput; use Intervention\Image\Traits\CanResizeGeometrically; -class PadModifier implements ModifierInterface +class PadModifier extends AbstractPadModifier implements ModifierInterface { use CanHandleInput; - protected $crop; - protected $resize; - protected $backgroundColor; - - public function __construct(SizeInterface $crop, SizeInterface $resize, $backgroundColor = null) - { - $this->crop = $crop; - $this->resize = $resize; - $this->backgroundColor = $backgroundColor; - } - public function apply(ImageInterface $image): ImageInterface { + $crop = $this->getCropSize($image); + $resize = $this->getResizeSize($image); + $background = $this->handleInput($this->background); + foreach ($image as $frame) { - $this->modify($frame); + $this->modify($frame, $crop, $resize, $background); } return $image; } - protected function modify(FrameInterface $frame): void - { + protected function modify( + FrameInterface $frame, + SizeInterface $crop, + SizeInterface $resize, + ColorInterface $background + ): void { // create new image $modified = imagecreatetruecolor( - $this->resize->getWidth(), - $this->resize->getHeight() + $resize->getWidth(), + $resize->getHeight() ); - $color = $this->handleInput($this->backgroundColor); - - imagefill($modified, 0, 0, $color->toInt()); + imagefill($modified, 0, 0, $background->toInt()); // get current image $current = $frame->getCore(); // preserve transparency - $transIndex = imagecolortransparent($current); - - if ($transIndex != -1) { - $rgba = imagecolorsforindex($modified, $transIndex); - $transColor = imagecolorallocatealpha($modified, $rgba['red'], $rgba['green'], $rgba['blue'], 127); - imagefill($modified, 0, 0, $transColor); - imagecolortransparent($modified, $transColor); - } else { - imagealphablending($modified, false); - imagesavealpha($modified, true); - } + imagealphablending($modified, false); + imagesavealpha($modified, true); // copy content from resource imagecopyresampled( $modified, $current, - $this->crop->getPivot()->getX(), - $this->crop->getPivot()->getY(), + $crop->getPivot()->getX(), + $crop->getPivot()->getY(), 0, 0, - $this->crop->getWidth(), - $this->crop->getHeight(), + $crop->getWidth(), + $crop->getHeight(), $frame->getSize()->getWidth(), $frame->getSize()->getHeight() ); diff --git a/src/Drivers/Gd/Modifiers/PixelateModifier.php b/src/Drivers/Gd/Modifiers/PixelateModifier.php new file mode 100644 index 00000000..c61cb191 --- /dev/null +++ b/src/Drivers/Gd/Modifiers/PixelateModifier.php @@ -0,0 +1,24 @@ +getCore(), IMG_FILTER_PIXELATE, $this->size, true); + } + + return $image; + } +} diff --git a/src/Drivers/Gd/Modifiers/PlaceModifier.php b/src/Drivers/Gd/Modifiers/PlaceModifier.php index 596ee79f..9350170a 100644 --- a/src/Drivers/Gd/Modifiers/PlaceModifier.php +++ b/src/Drivers/Gd/Modifiers/PlaceModifier.php @@ -12,21 +12,17 @@ class PlaceModifier implements ModifierInterface { use CanResolveDriverClass; - protected $element; - protected $position; - protected $offset_x; - protected $offset_y; - /** * Create new modifier * */ - public function __construct($element, string $position, int $offset_x, int $offset_y) - { - $this->element = $element; - $this->position = $position; - $this->offset_x = $offset_x; - $this->offset_y = $offset_y; + public function __construct( + protected $element, + protected string $position, + protected int $offset_x, + protected int $offset_y + ) { + // } public function apply(ImageInterface $image): ImageInterface @@ -43,8 +39,8 @@ class PlaceModifier implements ModifierInterface $position->getY(), 0, 0, - $watermark->width(), - $watermark->height() + $watermark->getWidth(), + $watermark->getHeight() ); } diff --git a/src/Drivers/Gd/Modifiers/ResizeDownModifier.php b/src/Drivers/Gd/Modifiers/ResizeDownModifier.php new file mode 100644 index 00000000..12503758 --- /dev/null +++ b/src/Drivers/Gd/Modifiers/ResizeDownModifier.php @@ -0,0 +1,14 @@ +getSize()->resizeDown($this->width, $this->height); + } +} diff --git a/src/Drivers/Gd/Modifiers/ResizeModifier.php b/src/Drivers/Gd/Modifiers/ResizeModifier.php index 7ad14d3e..b030a8e3 100644 --- a/src/Drivers/Gd/Modifiers/ResizeModifier.php +++ b/src/Drivers/Gd/Modifiers/ResizeModifier.php @@ -2,6 +2,7 @@ namespace Intervention\Image\Drivers\Gd\Modifiers; +use Intervention\Image\Geometry\Resizer; use Intervention\Image\Interfaces\FrameInterface; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; @@ -10,28 +11,33 @@ use Intervention\Image\Traits\CanResizeGeometrically; class ResizeModifier implements ModifierInterface { - protected $resize; - - public function __construct(SizeInterface $resize) + public function __construct(protected ?int $width = null, protected ?int $height = null) { - $this->resize = $resize; + // } public function apply(ImageInterface $image): ImageInterface { + $resizeTo = $this->getAdjustedSize($image); + foreach ($image as $frame) { - $this->modify($frame); + $this->resizeFrame($frame, $resizeTo); } return $image; } - protected function modify(FrameInterface $frame): void + protected function getAdjustedSize(ImageInterface $image): SizeInterface + { + return $image->getSize()->resize($this->width, $this->height); + } + + protected function resizeFrame(FrameInterface $frame, SizeInterface $resizeTo): void { // create new image $modified = imagecreatetruecolor( - $this->resize->getWidth(), - $this->resize->getHeight() + $resizeTo->getWidth(), + $resizeTo->getHeight() ); // get current image @@ -54,12 +60,12 @@ class ResizeModifier implements ModifierInterface imagecopyresampled( $modified, $current, - $this->resize->getPivot()->getX(), - $this->resize->getPivot()->getY(), + $resizeTo->getPivot()->getX(), + $resizeTo->getPivot()->getY(), 0, 0, - $this->resize->getWidth(), - $this->resize->getHeight(), + $resizeTo->getWidth(), + $resizeTo->getHeight(), $frame->getSize()->getWidth(), $frame->getSize()->getHeight() ); diff --git a/src/Drivers/Gd/Modifiers/RotateModifier.php b/src/Drivers/Gd/Modifiers/RotateModifier.php index 1da31d1f..b692c474 100644 --- a/src/Drivers/Gd/Modifiers/RotateModifier.php +++ b/src/Drivers/Gd/Modifiers/RotateModifier.php @@ -2,67 +2,24 @@ namespace Intervention\Image\Drivers\Gd\Modifiers; -use Intervention\Image\Exceptions\TypeException; -use Intervention\Image\Interfaces\ColorInterface; -use Intervention\Image\Interfaces\FrameInterface; +use Intervention\Image\Drivers\Abstract\Modifiers\AbstractRotateModifier; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; -use Intervention\Image\Traits\CanHandleInput; -class RotateModifier implements ModifierInterface +class RotateModifier extends AbstractRotateModifier implements ModifierInterface { - use CanHandleInput; - - /** - * Rotation angle - * - * @var float - */ - protected $angle; - - /** - * Background color - * - * @var mixed - */ - protected $backgroundColor; - - /** - * Create new modifier - * - * @param float $angle - */ - public function __construct(float $angle, $backgroundColor) - { - $this->angle = $angle; - $this->backgroundColor = $backgroundColor; - } - public function apply(ImageInterface $image): ImageInterface { foreach ($image as $frame) { $frame->setCore( - imagerotate($frame->getCore(), $this->rotationAngle(), $this->backgroundColor()) + imagerotate( + $frame->getCore(), + $this->rotationAngle(), + $this->backgroundColor()->toInt() + ) ); } return $image; } - - protected function rotationAngle(): float - { - // restrict rotations beyond 360 degrees, since the end result is the same - return fmod($this->angle, 360); - } - - protected function backgroundColor(): int - { - $color = $this->handleInput($this->backgroundColor); - - if (!is_a($color, ColorInterface::class)) { - throw new TypeException("rotate(): Argument #2 must be of color value."); - } - - return $color->toInt(); - } } diff --git a/src/Drivers/Gd/Modifiers/ScaleDownModifier.php b/src/Drivers/Gd/Modifiers/ScaleDownModifier.php new file mode 100644 index 00000000..3a0e5b9a --- /dev/null +++ b/src/Drivers/Gd/Modifiers/ScaleDownModifier.php @@ -0,0 +1,14 @@ +getSize()->scaleDown($this->width, $this->height); + } +} diff --git a/src/Drivers/Gd/Modifiers/ScaleModifier.php b/src/Drivers/Gd/Modifiers/ScaleModifier.php new file mode 100644 index 00000000..ff7491af --- /dev/null +++ b/src/Drivers/Gd/Modifiers/ScaleModifier.php @@ -0,0 +1,14 @@ +getSize()->scale($this->width, $this->height); + } +} diff --git a/src/Drivers/Gd/Modifiers/SharpenModifier.php b/src/Drivers/Gd/Modifiers/SharpenModifier.php new file mode 100644 index 00000000..d5bedcec --- /dev/null +++ b/src/Drivers/Gd/Modifiers/SharpenModifier.php @@ -0,0 +1,37 @@ +matrix(); + foreach ($image as $frame) { + imageconvolution($frame->getCore(), $matrix, 1, 0); + } + + return $image; + } + + protected function matrix(): array + { + $min = $this->amount >= 10 ? $this->amount * -0.01 : 0; + $max = $this->amount * -0.025; + $abs = ((4 * $min + 4 * $max) * -1) + 1; + + return [ + [$min, $max, $min], + [$max, $abs, $max], + [$min, $max, $min] + ]; + } +} diff --git a/src/Drivers/Imagick/Color.php b/src/Drivers/Imagick/Color.php index a04e0754..bda5e232 100644 --- a/src/Drivers/Imagick/Color.php +++ b/src/Drivers/Imagick/Color.php @@ -9,16 +9,9 @@ use Intervention\Image\Interfaces\ColorInterface; class Color extends AbstractColor implements ColorInterface { - /** - * Imagick pixel to represent color - * - * @var ImagickPixel - */ - protected $pixel; - - public function __construct(ImagickPixel $pixel) + public function __construct(protected ImagickPixel $pixel) { - $this->pixel = $pixel; + // } public function getPixel(): ImagickPixel diff --git a/src/Drivers/Imagick/Decoders/BinaryImageDecoder.php b/src/Drivers/Imagick/Decoders/BinaryImageDecoder.php index 1b931e53..f09a50ff 100644 --- a/src/Drivers/Imagick/Decoders/BinaryImageDecoder.php +++ b/src/Drivers/Imagick/Decoders/BinaryImageDecoder.php @@ -15,6 +15,10 @@ class BinaryImageDecoder extends AbstractDecoder implements DecoderInterface { public function decode($input): ImageInterface|ColorInterface { + if (!is_string($input)) { + $this->fail(); + } + if (! $this->inputType($input)->isBinary()) { $this->fail(); } diff --git a/src/Drivers/Imagick/Decoders/HexColorDecoder.php b/src/Drivers/Imagick/Decoders/HexColorDecoder.php index a0609b34..2eafd126 100644 --- a/src/Drivers/Imagick/Decoders/HexColorDecoder.php +++ b/src/Drivers/Imagick/Decoders/HexColorDecoder.php @@ -11,6 +11,10 @@ class HexColorDecoder extends ArrayColorDecoder implements DecoderInterface { public function decode($input): ImageInterface|ColorInterface { + if (!is_string($input)) { + $this->fail(); + } + $pattern = '/^#?([a-f0-9]{1,2})([a-f0-9]{1,2})([a-f0-9]{1,2})$/i'; $result = preg_match($pattern, $input, $matches); diff --git a/src/Drivers/Imagick/Decoders/ImageObjectDecoder.php b/src/Drivers/Imagick/Decoders/ImageObjectDecoder.php new file mode 100644 index 00000000..72318e2a --- /dev/null +++ b/src/Drivers/Imagick/Decoders/ImageObjectDecoder.php @@ -0,0 +1,20 @@ +fail(); + } + + return $input; + } +} diff --git a/src/Drivers/Imagick/Decoders/RgbStringColorDecoder.php b/src/Drivers/Imagick/Decoders/RgbStringColorDecoder.php new file mode 100644 index 00000000..4e9c9fa2 --- /dev/null +++ b/src/Drivers/Imagick/Decoders/RgbStringColorDecoder.php @@ -0,0 +1,34 @@ +fail(); + } + + if (substr($input, 0, 3) !== 'rgb') { + $this->fail(); + } + + try { + $pixel = new ImagickPixel($input); + } catch (ImagickPixelException $e) { + $this->fail(); + } + + return new Color($pixel); + } +} diff --git a/src/Drivers/Imagick/Encoders/GifEncoder.php b/src/Drivers/Imagick/Encoders/GifEncoder.php index f795f174..0cfca898 100644 --- a/src/Drivers/Imagick/Encoders/GifEncoder.php +++ b/src/Drivers/Imagick/Encoders/GifEncoder.php @@ -20,7 +20,7 @@ class GifEncoder extends AbstractEncoder implements EncoderInterface $gif->addImage($frame->getCore()); } - $gif->setImageIterations($image->loops()); + $gif->setImageIterations($image->getLoops()); $gif->setFormat($format); $gif->setImageFormat($format); $gif->setCompression($compression); diff --git a/src/Drivers/Imagick/Encoders/JpegEncoder.php b/src/Drivers/Imagick/Encoders/JpegEncoder.php index 45fd7220..0ea13e67 100644 --- a/src/Drivers/Imagick/Encoders/JpegEncoder.php +++ b/src/Drivers/Imagick/Encoders/JpegEncoder.php @@ -10,6 +10,11 @@ use Intervention\Image\Interfaces\ImageInterface; class JpegEncoder extends AbstractEncoder implements EncoderInterface { + public function __construct(int $quality) + { + $this->quality = $quality; + } + public function encode(ImageInterface $image): EncodedImage { $format = 'jpeg'; diff --git a/src/Drivers/Imagick/Frame.php b/src/Drivers/Imagick/Frame.php index b787f773..632ad2c7 100644 --- a/src/Drivers/Imagick/Frame.php +++ b/src/Drivers/Imagick/Frame.php @@ -12,16 +12,9 @@ use Intervention\Image\Interfaces\SizeInterface; class Frame extends AbstractFrame implements FrameInterface { - /** - * Imagick image representation of frame - * - * @var Imagick - */ - protected $core; - - public function __construct(Imagick $core) + public function __construct(protected Imagick $core) { - $this->core = $core; + // } public function getCore(): Imagick @@ -29,6 +22,13 @@ class Frame extends AbstractFrame implements FrameInterface return $this->core; } + public function setCore(Imagick $core): FrameInterface + { + $this->core = $core; + + return $this; + } + public function getSize(): SizeInterface { return new Size($this->core->getImageWidth(), $this->core->getImageHeight()); diff --git a/src/Drivers/Imagick/Image.php b/src/Drivers/Imagick/Image.php index a0c289af..f04d8ae3 100644 --- a/src/Drivers/Imagick/Image.php +++ b/src/Drivers/Imagick/Image.php @@ -14,12 +14,12 @@ use IteratorAggregate; class Image extends AbstractImage implements ImageInterface, IteratorAggregate { - public function width(): int + public function getWidth(): int { return $this->frames->first()->getCore()->getImageWidth(); } - public function height(): int + public function getHeight(): int { return $this->frames->first()->getCore()->getImageHeight(); } diff --git a/src/Drivers/Imagick/ImageFactory.php b/src/Drivers/Imagick/ImageFactory.php index 7cd240c0..cff14438 100644 --- a/src/Drivers/Imagick/ImageFactory.php +++ b/src/Drivers/Imagick/ImageFactory.php @@ -12,6 +12,15 @@ use Intervention\Image\Interfaces\ImageInterface; class ImageFactory implements FactoryInterface { public function newImage(int $width, int $height): ImageInterface + { + return new Image( + new Collection([ + new Frame($this->newCore($width, $height)) + ]) + ); + } + + public function newCore(int $width, int $height): Imagick { $imagick = new Imagick(); $imagick->newImage($width, $height, new ImagickPixel('rgba(0, 0, 0, 0)'), 'png'); @@ -19,10 +28,6 @@ class ImageFactory implements FactoryInterface $imagick->setImageType(Imagick::IMGTYPE_UNDEFINED); $imagick->setColorspace(Imagick::COLORSPACE_UNDEFINED); - return new Image( - new Collection([ - new Frame($imagick) - ]) - ); + return $imagick; } } diff --git a/src/Drivers/Imagick/InputHandler.php b/src/Drivers/Imagick/InputHandler.php index fa5cd226..839d401c 100644 --- a/src/Drivers/Imagick/InputHandler.php +++ b/src/Drivers/Imagick/InputHandler.php @@ -9,13 +9,17 @@ class InputHandler extends AbstractInputHandler { protected function chain(): AbstractDecoder { - return new Decoders\ArrayColorDecoder( - new Decoders\HexColorDecoder( - new Decoders\TransparentColorDecoder( - new Decoders\FilePathImageDecoder( - new Decoders\BinaryImageDecoder( - new Decoders\DataUriImageDecoder( - new Decoders\Base64ImageDecoder() + return new Decoders\ImageObjectDecoder( + new Decoders\ArrayColorDecoder( + new Decoders\HexColorDecoder( + new Decoders\RgbStringColorDecoder( + new Decoders\TransparentColorDecoder( + new Decoders\FilePathImageDecoder( + new Decoders\BinaryImageDecoder( + new Decoders\DataUriImageDecoder( + new Decoders\Base64ImageDecoder() + ) + ) ) ) ) diff --git a/src/Drivers/Imagick/Modifiers/BlurModifier.php b/src/Drivers/Imagick/Modifiers/BlurModifier.php index eb4fefa1..59892999 100644 --- a/src/Drivers/Imagick/Modifiers/BlurModifier.php +++ b/src/Drivers/Imagick/Modifiers/BlurModifier.php @@ -7,14 +7,9 @@ use Intervention\Image\Interfaces\ModifierInterface; class BlurModifier implements ModifierInterface { - /** - * Create new modifier - * - * @param int $amount Blur amount (0 - 100%) - */ - public function __construct(int $amount) + public function __construct(protected int $amount) { - $this->amount = $amount; + // } public function apply(ImageInterface $image): ImageInterface diff --git a/src/Drivers/Imagick/Modifiers/BrightnessModifier.php b/src/Drivers/Imagick/Modifiers/BrightnessModifier.php new file mode 100644 index 00000000..50b092f2 --- /dev/null +++ b/src/Drivers/Imagick/Modifiers/BrightnessModifier.php @@ -0,0 +1,23 @@ +getCore()->modulateImage(100 + $this->level, 100, 100); + } + + return $image; + } +} diff --git a/src/Drivers/Imagick/Modifiers/ContrastModifier.php b/src/Drivers/Imagick/Modifiers/ContrastModifier.php new file mode 100644 index 00000000..1a1bb3de --- /dev/null +++ b/src/Drivers/Imagick/Modifiers/ContrastModifier.php @@ -0,0 +1,23 @@ +getCore()->sigmoidalContrastImage($this->level > 0, abs($this->level / 4), 0); + } + + return $image; + } +} diff --git a/src/Drivers/Imagick/Modifiers/CropResizeModifier.php b/src/Drivers/Imagick/Modifiers/CropResizeModifier.php deleted file mode 100644 index 8a7b555d..00000000 --- a/src/Drivers/Imagick/Modifiers/CropResizeModifier.php +++ /dev/null @@ -1,45 +0,0 @@ -crop = $crop; - $this->resize = $resize; - $this->position = $position; - } - - public function apply(ImageInterface $image): ImageInterface - { - $shouldCrop = $this->crop != $image->getSize(); - - foreach ($image as $frame) { - if ($shouldCrop) { - $frame->getCore()->extentImage( - $this->crop->getWidth(), - $this->crop->getHeight(), - $this->crop->getPivot()->getX(), - $this->crop->getPivot()->getY() - ); - } - - $frame->getCore()->scaleImage( - $this->resize->getWidth(), - $this->resize->getHeight() - ); - } - - return $image; - } -} diff --git a/src/Drivers/Imagick/Modifiers/DestroyModifier.php b/src/Drivers/Imagick/Modifiers/DestroyModifier.php new file mode 100644 index 00000000..d8efc307 --- /dev/null +++ b/src/Drivers/Imagick/Modifiers/DestroyModifier.php @@ -0,0 +1,18 @@ +getCore()->clear(); + } + + return $image; + } +} diff --git a/src/Drivers/Imagick/Modifiers/FillModifier.php b/src/Drivers/Imagick/Modifiers/FillModifier.php new file mode 100644 index 00000000..ddcf19df --- /dev/null +++ b/src/Drivers/Imagick/Modifiers/FillModifier.php @@ -0,0 +1,70 @@ +hasPosition()) { + $this->floodFillWithColor($frame); + } else { + $this->fillAllWithColor($frame); + } + } + + return $image; + } + + protected function floodFillWithColor(Frame $frame): void + { + $target = $frame->getCore()->getImagePixelColor( + $this->position->getX(), + $this->position->getY() + ); + + $frame->getCore()->floodfillPaintImage( + $this->color->getPixel(), + 100, + $target, + $this->position->getX(), + $this->position->getY(), + false, + Imagick::CHANNEL_ALL + ); + } + + protected function fillAllWithColor(Frame $frame): void + { + $draw = new ImagickDraw(); + $draw->setFillColor($this->color->getPixel()); + $draw->rectangle( + 0, + 0, + $frame->getCore()->getImageWidth(), + $frame->getCore()->getImageHeight() + ); + $frame->getCore()->drawImage($draw); + } + + protected function hasPosition(): bool + { + return !empty($this->position); + } +} diff --git a/src/Drivers/Imagick/Modifiers/FitDownModifier.php b/src/Drivers/Imagick/Modifiers/FitDownModifier.php new file mode 100644 index 00000000..73e26ef7 --- /dev/null +++ b/src/Drivers/Imagick/Modifiers/FitDownModifier.php @@ -0,0 +1,13 @@ +scaleDown($this->width, $this->height); + } +} diff --git a/src/Drivers/Imagick/Modifiers/FitModifier.php b/src/Drivers/Imagick/Modifiers/FitModifier.php index 992c992a..5b34f702 100644 --- a/src/Drivers/Imagick/Modifiers/FitModifier.php +++ b/src/Drivers/Imagick/Modifiers/FitModifier.php @@ -2,35 +2,31 @@ namespace Intervention\Image\Drivers\Imagick\Modifiers; +use Intervention\Image\Drivers\Abstract\Modifiers\AbstractFitModifier; +use Intervention\Image\Geometry\Size; use Intervention\Image\Interfaces\FrameInterface; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; use Intervention\Image\Interfaces\SizeInterface; -class FitModifier implements ModifierInterface +class FitModifier extends AbstractFitModifier implements ModifierInterface { - protected $crop; - protected $resize; - - public function __construct(SizeInterface $crop, SizeInterface $resize) - { - $this->crop = $crop; - $this->resize = $resize; - } - public function apply(ImageInterface $image): ImageInterface { + $crop = $this->getCropSize($image); + $resize = $this->getResizeSize($crop); + foreach ($image as $frame) { $frame->getCore()->extentImage( - $this->crop->getWidth(), - $this->crop->getHeight(), - $this->crop->getPivot()->getX(), - $this->crop->getPivot()->getY() + $crop->getWidth(), + $crop->getHeight(), + $crop->getPivot()->getX(), + $crop->getPivot()->getY() ); - + $frame->getCore()->scaleImage( - $this->resize->getWidth(), - $this->resize->getHeight() + $resize->getWidth(), + $resize->getHeight() ); } diff --git a/src/Drivers/Imagick/Modifiers/InvertModifier.php b/src/Drivers/Imagick/Modifiers/InvertModifier.php new file mode 100644 index 00000000..08fc3c0b --- /dev/null +++ b/src/Drivers/Imagick/Modifiers/InvertModifier.php @@ -0,0 +1,18 @@ +getCore()->negateImage(false); + } + + return $image; + } +} diff --git a/src/Drivers/Imagick/Modifiers/PadDownModifier.php b/src/Drivers/Imagick/Modifiers/PadDownModifier.php new file mode 100644 index 00000000..e0649857 --- /dev/null +++ b/src/Drivers/Imagick/Modifiers/PadDownModifier.php @@ -0,0 +1,25 @@ +getResizeSize($image); + + return $image->getSize() + ->contain($resize->getWidth(), $resize->getHeight()) + ->alignPivotTo($resize, $this->position); + } + + protected function getResizeSize(ImageInterface $image): SizeInterface + { + return (new Size($this->width, $this->height)) + ->resizeDown($image->getWidth(), $image->getHeight()); + } +} diff --git a/src/Drivers/Imagick/Modifiers/PadModifier.php b/src/Drivers/Imagick/Modifiers/PadModifier.php index df8eb29f..414efc97 100644 --- a/src/Drivers/Imagick/Modifiers/PadModifier.php +++ b/src/Drivers/Imagick/Modifiers/PadModifier.php @@ -2,30 +2,82 @@ namespace Intervention\Image\Drivers\Imagick\Modifiers; +use Imagick; +use ImagickDraw; +use Intervention\Image\Drivers\Abstract\Modifiers\AbstractPadModifier; +use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\FrameInterface; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; use Intervention\Image\Interfaces\SizeInterface; +use Intervention\Image\Traits\CanBuildNewImage; +use Intervention\Image\Traits\CanHandleInput; -class PadModifier implements ModifierInterface +class PadModifier extends AbstractPadModifier implements ModifierInterface { - protected $crop; - protected $resize; - protected $backgroundColor; - - public function __construct(SizeInterface $crop, SizeInterface $resize, $backgroundColor = null) - { - $this->crop = $crop; - $this->resize = $resize; - $this->backgroundColor = $backgroundColor; - } + use CanBuildNewImage; + use CanHandleInput; public function apply(ImageInterface $image): ImageInterface { + $resize = $this->getResizeSize($image); + $crop = $this->getCropSize($image); + $background = $this->handleInput($this->background); + foreach ($image as $frame) { - // + // resize current core + $frame->getCore()->scaleImage( + $crop->getWidth(), + $crop->getHeight() + ); + + // create new canvas, to get newly emerged background color + $canvas = $this->buildBaseCanvas($crop, $resize, $background); + + // place current core onto canvas + $canvas->compositeImage( + $frame->getCore(), + Imagick::COMPOSITE_DEFAULT, + $crop->getPivot()->getX(), + $crop->getPivot()->getY() + ); + + // replace core + $frame->getCore()->destroy(); + $frame->setCore($canvas); } return $image; } + + protected function buildBaseCanvas(SizeInterface $crop, SizeInterface $resize, ColorInterface $background): Imagick + { + // build base canvas in target size + $canvas = $this->imageFactory()->newCore( + $resize->getWidth(), + $resize->getHeight() + ); + + // draw background color on canvas + $draw = new ImagickDraw(); + $draw->setFillColor($background->getPixel()); + $draw->rectangle(0, 0, $canvas->getImageWidth(), $canvas->getImageHeight()); + $canvas->drawImage($draw); + + // make area where image is placed transparent to keep + // transparency even if background-color is set + $draw = new ImagickDraw(); + $fill = $background->toHex('#') == '#ff0000' ? '#00ff00' : '#ff0000'; + $draw->setFillColor($fill); + $draw->rectangle( + $crop->getPivot()->getX(), + $crop->getPivot()->getY(), + $crop->getPivot()->getX() + $crop->getWidth() - 1, + $crop->getPivot()->getY() + $crop->getHeight() - 1 + ); + $canvas->drawImage($draw); + $canvas->transparentPaintImage($fill, 0, 0, false); + + return $canvas; + } } diff --git a/src/Drivers/Imagick/Modifiers/PixelateModifier.php b/src/Drivers/Imagick/Modifiers/PixelateModifier.php new file mode 100644 index 00000000..770f6f33 --- /dev/null +++ b/src/Drivers/Imagick/Modifiers/PixelateModifier.php @@ -0,0 +1,37 @@ +pixelateFrame($frame); + } + + return $image; + } + + protected function pixelateFrame(Frame $frame): void + { + $size = $frame->getSize(); + + $frame->getCore()->scaleImage( + max(1, ($size->getWidth() / $this->size)), + max(1, ($size->getHeight() / $this->size)) + ); + + $frame->getCore()->scaleImage($size->getWidth(), $size->getHeight()); + } +} diff --git a/src/Drivers/Imagick/Modifiers/PlaceModifier.php b/src/Drivers/Imagick/Modifiers/PlaceModifier.php index c043adf0..06f1ef09 100644 --- a/src/Drivers/Imagick/Modifiers/PlaceModifier.php +++ b/src/Drivers/Imagick/Modifiers/PlaceModifier.php @@ -13,17 +13,13 @@ class PlaceModifier implements ModifierInterface { use CanResolveDriverClass; - protected $element; - protected $position; - protected $offset_x; - protected $offset_y; - - public function __construct($element, string $position, int $offset_x, int $offset_y) - { - $this->element = $element; - $this->position = $position; - $this->offset_x = $offset_x; - $this->offset_y = $offset_y; + public function __construct( + protected $element, + protected string $position, + protected int $offset_x, + protected int $offset_y + ) { + // } public function apply(ImageInterface $image): ImageInterface diff --git a/src/Drivers/Imagick/Modifiers/ResizeDownModifier.php b/src/Drivers/Imagick/Modifiers/ResizeDownModifier.php new file mode 100644 index 00000000..068dbccb --- /dev/null +++ b/src/Drivers/Imagick/Modifiers/ResizeDownModifier.php @@ -0,0 +1,14 @@ +getSize()->resizeDown($this->width, $this->height); + } +} diff --git a/src/Drivers/Imagick/Modifiers/ResizeModifier.php b/src/Drivers/Imagick/Modifiers/ResizeModifier.php index 057c848a..dbf0697a 100644 --- a/src/Drivers/Imagick/Modifiers/ResizeModifier.php +++ b/src/Drivers/Imagick/Modifiers/ResizeModifier.php @@ -9,22 +9,27 @@ use Intervention\Image\Interfaces\SizeInterface; class ResizeModifier implements ModifierInterface { - protected $resize; - - public function __construct(SizeInterface $resize) + public function __construct(protected ?int $width = null, protected ?int $height = null) { - $this->resize = $resize; + // } public function apply(ImageInterface $image): ImageInterface { + $resizeTo = $this->getAdjustedSize($image); + foreach ($image as $frame) { $frame->getCore()->scaleImage( - $this->resize->getWidth(), - $this->resize->getHeight() + $resizeTo->getWidth(), + $resizeTo->getHeight() ); } return $image; } + + protected function getAdjustedSize(ImageInterface $image): SizeInterface + { + return $image->getSize()->resize($this->width, $this->height); + } } diff --git a/src/Drivers/Imagick/Modifiers/RotateModifier.php b/src/Drivers/Imagick/Modifiers/RotateModifier.php new file mode 100644 index 00000000..3fde612e --- /dev/null +++ b/src/Drivers/Imagick/Modifiers/RotateModifier.php @@ -0,0 +1,27 @@ +getCore()->rotateImage( + $this->backgroundColor()->getPixel(), + $this->rotationAngle() + ); + } + + return $image; + } + + protected function rotationAngle(): float + { + return parent::rotationAngle() * -1; + } +} diff --git a/src/Drivers/Imagick/Modifiers/ScaleDownModifier.php b/src/Drivers/Imagick/Modifiers/ScaleDownModifier.php new file mode 100644 index 00000000..45ec70f1 --- /dev/null +++ b/src/Drivers/Imagick/Modifiers/ScaleDownModifier.php @@ -0,0 +1,14 @@ +getSize()->scaleDown($this->width, $this->height); + } +} diff --git a/src/Drivers/Imagick/Modifiers/ScaleModifier.php b/src/Drivers/Imagick/Modifiers/ScaleModifier.php new file mode 100644 index 00000000..2e99fae6 --- /dev/null +++ b/src/Drivers/Imagick/Modifiers/ScaleModifier.php @@ -0,0 +1,14 @@ +getSize()->scale($this->width, $this->height); + } +} diff --git a/src/Drivers/Imagick/Modifiers/SharpenModifier.php b/src/Drivers/Imagick/Modifiers/SharpenModifier.php new file mode 100644 index 00000000..0ce2e92d --- /dev/null +++ b/src/Drivers/Imagick/Modifiers/SharpenModifier.php @@ -0,0 +1,23 @@ +getCore()->unsharpMaskImage(1, 1, $this->amount / 6.25, 0); + } + + return $image; + } +} diff --git a/src/EncodedImage.php b/src/EncodedImage.php index 7b0858f9..011c6f15 100644 --- a/src/EncodedImage.php +++ b/src/EncodedImage.php @@ -6,13 +6,11 @@ use Intervention\Image\Exceptions\NotWritableException; class EncodedImage { - protected $data; - protected $mimetype; - - public function __construct(string $data, string $mimetype = 'application/octet-stream') - { - $this->data = $data; - $this->mimetype = $mimetype; + public function __construct( + protected string $data, + protected string $mimetype = 'application/octet-stream' + ) { + // } public function mimetype(): string diff --git a/src/Exceptions/GeometryException.php b/src/Exceptions/GeometryException.php new file mode 100644 index 00000000..7bca9ad7 --- /dev/null +++ b/src/Exceptions/GeometryException.php @@ -0,0 +1,8 @@ +x = $x; - $this->y = $y; + // } /** diff --git a/src/Geometry/Resizer.php b/src/Geometry/Resizer.php index 5b4c7277..6a5c0edd 100644 --- a/src/Geometry/Resizer.php +++ b/src/Geometry/Resizer.php @@ -2,113 +2,73 @@ namespace Intervention\Image\Geometry; +use Intervention\Image\Exceptions\GeometryException; use Intervention\Image\Geometry\Size; use Intervention\Image\Interfaces\SizeInterface; class Resizer { - /** - * Size to be resized - * - * @var SizeInterface - */ - protected $original; - - /** - * Target size - * - * @var SizeInterface - */ - protected $target; - - /** - * Create new instance - * - * @param SizeInterface $size - */ - public function __construct() - { - $this->target = new Size(0, 0); + public function __construct( + protected ?int $width = null, + protected ?int $height = null, + ) { + // } - public static function make(callable $callback = null): self + public static function to(...$arguments): self { - $resizer = new self(); - - if (is_callable($callback)) { - $callback($resizer); - } - - return $resizer; + return new self(...$arguments); } protected function hasTargetWidth(): bool { - return $this->target->getWidth() > 0; + return is_integer($this->width); + } + + protected function getTargetWidth(): ?int + { + return $this->hasTargetWidth() ? $this->width : null; } protected function hasTargetHeight(): bool { - return $this->target->getHeight() > 0; + return is_integer($this->height); } - public function width(int $width): self + protected function getTargetHeight(): ?int { - $this->target->setWidth($width); - - return $this; + return $this->hasTargetHeight() ? $this->height : null; } - public function height(int $height): self + protected function getTargetSize(): SizeInterface { - $this->target->setHeight($height); + if (!$this->hasTargetWidth() || !$this->hasTargetHeight()) { + throw new GeometryException('Target size needs width and height.'); + } - return $this; + return new Size($this->width, $this->height); } public function toWidth(int $width): self { - return $this->width($width); - } - - public function toHeight(int $height): self - { - return $this->height($height); - } - - public function setTargetSizeByArray(array $arguments): self - { - if (isset($arguments[0]) && is_callable($arguments[0])) { - $arguments[0]($this); - - return $this; - } - - if (isset($arguments[0]) && is_a($arguments[0], Size::class)) { - return $this->toSize($arguments[0]); - } - - if (isset($arguments[0]) && is_numeric($arguments[0])) { - $this->width($arguments[0]); - } - - if (isset($arguments[1]) && is_numeric($arguments[1])) { - $this->height($arguments[1]); - } + $this->width = $width; return $this; } - public function setTargetSize(SizeInterface $size): self + public function toHeight(int $height): self { - $this->target = new Size($size->getWidth(), $size->getHeight()); + $this->height = $height; return $this; } public function toSize(SizeInterface $size): self { - return $this->setTargetSize($size); + $this->width = $size->getWidth(); + $this->height = $size->getHeight(); + + return $this; } protected function getProportionalWidth(SizeInterface $size): int @@ -117,7 +77,7 @@ class Resizer return $size->getWidth(); } - return (int) round($this->target->getHeight() * $size->getAspectRatio()); + return (int) round($this->height * $size->getAspectRatio()); } protected function getProportionalHeight(SizeInterface $size): int @@ -126,19 +86,19 @@ class Resizer return $size->getHeight(); } - return (int) round($this->target->getWidth() / $size->getAspectRatio()); + return (int) round($this->width / $size->getAspectRatio()); } public function resize(SizeInterface $size): SizeInterface { $resized = new Size($size->getWidth(), $size->getHeight()); - if ($this->hasTargetWidth()) { - $resized->setWidth($this->target->getWidth()); + if ($width = $this->getTargetWidth()) { + $resized->setWidth($width); } - if ($this->hasTargetHeight()) { - $resized->setHeight($this->target->getHeight()); + if ($height = $this->getTargetHeight()) { + $resized->setHeight($height); } return $resized; @@ -148,15 +108,15 @@ class Resizer { $resized = new Size($size->getWidth(), $size->getHeight()); - if ($this->hasTargetWidth()) { + if ($width = $this->getTargetWidth()) { $resized->setWidth( - min($this->target->getWidth(), $size->getWidth()) + min($width, $size->getWidth()) ); } - if ($this->hasTargetHeight()) { + if ($height = $this->getTargetHeight()) { $resized->setHeight( - min($this->target->getHeight(), $size->getHeight()) + min($height, $size->getHeight()) ); } @@ -170,18 +130,18 @@ class Resizer if ($this->hasTargetWidth() && $this->hasTargetHeight()) { $resized->setWidth(min( $this->getProportionalWidth($size), - $this->target->getWidth() + $this->getTargetWidth() )); $resized->setHeight(min( $this->getProportionalHeight($size), - $this->target->getHeight() + $this->getTargetHeight() )); } elseif ($this->hasTargetWidth()) { - $resized->setWidth($this->target->getWidth()); + $resized->setWidth($this->getTargetWidth()); $resized->setHeight($this->getProportionalHeight($size)); } elseif ($this->hasTargetHeight()) { $resized->setWidth($this->getProportionalWidth($size)); - $resized->setHeight($this->target->getHeight()); + $resized->setHeight($this->getTargetHeight()); } return $resized; @@ -194,17 +154,17 @@ class Resizer if ($this->hasTargetWidth() && $this->hasTargetHeight()) { $resized->setWidth(min( $this->getProportionalWidth($size), - $this->target->getWidth(), + $this->getTargetWidth(), $size->getWidth() )); $resized->setHeight(min( $this->getProportionalHeight($size), - $this->target->getHeight(), + $this->getTargetHeight(), $size->getHeight() )); } elseif ($this->hasTargetWidth()) { $resized->setWidth(min( - $this->target->getWidth(), + $this->getTargetWidth(), $size->getWidth() )); $resized->setHeight(min( @@ -217,7 +177,7 @@ class Resizer $size->getWidth() )); $resized->setHeight(min( - $this->target->getHeight(), + $this->getTargetHeight(), $size->getHeight() )); } @@ -236,13 +196,13 @@ class Resizer $resized = new Size($size->getWidth(), $size->getHeight()); // auto height - $resized->setWidth($this->target->getWidth()); + $resized->setWidth($this->getTargetWidth()); $resized->setHeight($this->getProportionalHeight($size)); - if ($resized->fitsInto($this->target)) { + if ($resized->fitsInto($this->getTargetSize())) { // auto width $resized->setWidth($this->getProportionalWidth($size)); - $resized->setHeight($this->target->getHeight()); + $resized->setHeight($this->getTargetHeight()); } return $resized; @@ -259,13 +219,13 @@ class Resizer $resized = new Size($size->getWidth(), $size->getHeight()); // auto height - $resized->setWidth($this->target->getWidth()); + $resized->setWidth($this->getTargetWidth()); $resized->setHeight($this->getProportionalHeight($size)); - if (!$resized->fitsInto($this->target)) { + if (!$resized->fitsInto($this->getTargetSize())) { // auto width $resized->setWidth($this->getProportionalWidth($size)); - $resized->setHeight($this->target->getHeight()); + $resized->setHeight($this->getTargetHeight()); } return $resized; diff --git a/src/Geometry/Size.php b/src/Geometry/Size.php index 05aa9ad1..797c15bc 100644 --- a/src/Geometry/Size.php +++ b/src/Geometry/Size.php @@ -8,14 +8,11 @@ use Intervention\Image\Interfaces\SizeInterface; class Size implements SizeInterface { - protected $width; - protected $height; - protected $pivot; - - public function __construct(int $width, int $height, Point $pivot = null) - { - $this->width = $width; - $this->height = $height; + public function __construct( + protected int $width, + protected int $height, + protected ?Point $pivot = null + ) { $this->pivot = $pivot ? $pivot : new Point(); } @@ -214,39 +211,36 @@ class Size implements SizeInterface protected function getResizer(...$arguments): Resizer { - $resizer = new Resizer(); - $resizer->setTargetSizeByArray($arguments[0]); - - return $resizer; + return new Resizer(...$arguments); } public function resize(...$arguments): self { - return $this->getResizer($arguments)->resize($this); + return $this->getResizer(...$arguments)->resize($this); } public function resizeDown(...$arguments): self { - return $this->getResizer($arguments)->resizeDown($this); + return $this->getResizer(...$arguments)->resizeDown($this); } public function scale(...$arguments): self { - return $this->getResizer($arguments)->scale($this); + return $this->getResizer(...$arguments)->scale($this); } public function scaleDown(...$arguments): self { - return $this->getResizer($arguments)->scaleDown($this); + return $this->getResizer(...$arguments)->scaleDown($this); } - public function cover(...$arguments): self + public function cover(int $width, int $height): self { - return $this->getResizer($arguments)->cover($this); + return $this->getResizer($width, $height)->cover($this); } - public function contain(...$arguments): self + public function contain(int $width, int $height): self { - return $this->getResizer($arguments)->contain($this); + return $this->getResizer($width, $height)->contain($this); } } diff --git a/src/ImageManager.php b/src/ImageManager.php index 428df54b..5cd24990 100644 --- a/src/ImageManager.php +++ b/src/ImageManager.php @@ -2,53 +2,16 @@ namespace Intervention\Image; -use Exception; -use ReflectionClass; use Intervention\Image\Interfaces\ImageInterface; +use Intervention\Image\Traits\CanResolveDriverClass; class ImageManager { - /** - * Configuration data - * - * @var array - */ - protected $config = [ - 'driver' => 'gd', - ]; + use CanResolveDriverClass; - /** - * Create new instance - * - * @param array $config - */ - public function __construct(array $config = []) + public function __construct(protected string $driver = 'gd') { - $this->configure($config); - } - - /** - * Override configuration settings - * - * @param array $config - */ - public function configure(array $config = []): self - { - $this->config = array_replace($this->config, $config); - - return $this; - } - - /** - * Return given value of configuration - * - * @param string $key - * @param mixed $default - * @return mixed - */ - public function getConfig($key, $default = null) - { - return array_key_exists($key, $this->config) ? $this->config[$key] : $default; + // } /** @@ -60,37 +23,27 @@ class ImageManager */ public function create(int $width, int $height): ImageInterface { - return $this->resolve('ImageFactory')->newImage($width, $height); + return $this->resolveDriverClass('ImageFactory')->newImage($width, $height); } /** - * Create new image instance from input + * Create new image instance from source * - * @param mixed $input + * @param mixed $source * @return ImageInterface */ - public function make($input): ImageInterface + public function make($source): ImageInterface { - return $this->resolve('InputHandler')->handle($input); + return $this->resolveDriverClass('InputHandler')->handle($source); } /** - * Resolve given classname according to current configuration + * Return id of current driver * - * @param string $classname - * @param array $arguments - * @return mixed + * @return string */ - private function resolve(string $classname, ...$arguments) + protected function getCurrentDriver(): string { - $classname = sprintf( - "Intervention\\Image\\Drivers\\%s\\%s", - ucfirst($this->config['driver']), - $classname - ); - - $reflection = new ReflectionClass($classname); - - return $reflection->newInstanceArgs($arguments); + return strtolower($this->driver); } } diff --git a/src/Interfaces/ImageInterface.php b/src/Interfaces/ImageInterface.php index 56b5ec01..7fc9e750 100644 --- a/src/Interfaces/ImageInterface.php +++ b/src/Interfaces/ImageInterface.php @@ -4,17 +4,42 @@ namespace Intervention\Image\Interfaces; use Intervention\Image\Collection; use Intervention\Image\EncodedImage; +use Intervention\Image\Interfaces\FrameInterface; +use Intervention\Image\Interfaces\ImageInterface; +use Intervention\Image\Interfaces\SizeInterface; interface ImageInterface { - public function getSize(): SizeInterface; - public function width(): int; - public function height(): int; - public function isAnimated(): bool; - public function greyscale(): ImageInterface; - public function encode(EncoderInterface $encoder): EncodedImage; + public function getIterator(): Collection; + public function getFrames(): Collection; + public function getFrame(int $key = 0): ?FrameInterface; + public function addFrame(FrameInterface $frame): ImageInterface; public function setLoops(int $count): ImageInterface; - public function loops(): int; - public function pickColor(int $x, int $y, int $frame_key = 0): ?ColorInterface; + public function getLoops(): int; + public function getSize(): SizeInterface; + public function isAnimated(): bool; + public function modify(ModifierInterface $modifier): ImageInterface; + public function encode(EncoderInterface $encoder): EncodedImage; + public function toJpeg(int $quality = 75): EncodedImage; + public function toGif(): EncodedImage; + public function toPng(): EncodedImage; public function pickColors(int $x, int $y): Collection; + public function pickColor(int $x, int $y, int $frame_key = 0): ?ColorInterface; + public function greyscale(): ImageInterface; + public function blur(int $amount = 5): ImageInterface; + public function rotate(float $angle, $background = 'ffffff'): ImageInterface; + public function place($element, string $position = 'top-left', int $offset_x = 0, int $offset_y = 0): ImageInterface; + public function fill($color, ?int $x = null, ?int $y = null): ImageInterface; + public function pixelate(int $size): ImageInterface; + public function resize(?int $width = null, ?int $height = null): ImageInterface; + public function resizeDown(?int $width = null, ?int $height = null): ImageInterface; + public function scale(?int $width = null, ?int $height = null): ImageInterface; + public function scaleDown(?int $width = null, ?int $height = null): ImageInterface; + public function fit(int $width, int $height, string $position = 'center'): ImageInterface; + public function fitDown(int $width, int $height, string $position = 'center'): ImageInterface; + public function pad(int $width, int $height, $background = 'ffffff', string $position = 'center'): ImageInterface; + public function padDown(int $width, int $height, $background = 'ffffff', string $position = 'center'): ImageInterface; + public function getWidth(): int; + public function getHeight(): int; + public function destroy(): void; } diff --git a/src/Traits/CanBuildNewImage.php b/src/Traits/CanBuildNewImage.php new file mode 100644 index 00000000..fe67f5d2 --- /dev/null +++ b/src/Traits/CanBuildNewImage.php @@ -0,0 +1,15 @@ +resolveDriverClass('ImageFactory'); + } +} diff --git a/src/Traits/CanResolveDriverClass.php b/src/Traits/CanResolveDriverClass.php index c7447e4f..bf86e53a 100644 --- a/src/Traits/CanResolveDriverClass.php +++ b/src/Traits/CanResolveDriverClass.php @@ -2,9 +2,10 @@ namespace Intervention\Image\Traits; +use Intervention\Image\Exceptions\MissingDriverComponentException; +use Intervention\Image\Exceptions\RuntimeException; use ReflectionClass; use ReflectionException; -use Intervention\Image\Exceptions\RuntimeException; trait CanResolveDriverClass { @@ -17,24 +18,25 @@ trait CanResolveDriverClass */ protected function resolveDriverClass(string $classname, ...$arguments) { + $driver_id = $this->getCurrentDriver(); $classname = sprintf( "Intervention\\Image\\Drivers\\%s\\%s", - ucfirst($this->getCurrentDriver()), + ucfirst($driver_id), $classname ); try { $reflection = new ReflectionClass($classname); } catch (ReflectionException $e) { - throw new RuntimeException( - 'Class (' . $classname . ') could not be resolved for current driver.' + throw new MissingDriverComponentException( + 'Class (' . $classname . ') could not be resolved with driver ' . ucfirst($driver_id) . '.' ); } return $reflection->newInstanceArgs($arguments); } - protected function getCurrentDriver() + protected function getCurrentDriver(): string { $pattern = '/Intervention\\\Image\\\Drivers\\\(?P[A-Za-z]+)/'; preg_match($pattern, get_class($this), $matches); diff --git a/tests/AbstractImageTest.php b/tests/AbstractImageTest.php new file mode 100644 index 00000000..ab2c5091 --- /dev/null +++ b/tests/AbstractImageTest.php @@ -0,0 +1,240 @@ +shouldReceive('ident')->andReturn(1); + $frame2 = Mockery::mock(FrameInterface::class); + $frame2->shouldReceive('ident')->andReturn(2); + $frame3 = Mockery::mock(FrameInterface::class); + $frame3->shouldReceive('ident')->andReturn(3); + + $collection = new Collection([$frame1, $frame2, $frame3]); + + $mock = Mockery::mock(AbstractImage::class, ImageInterface::class, [$collection]) + ->shouldAllowMockingProtectedMethods() + ->makePartial(); + + $mock->shouldReceive('getWidth')->andReturn(300); + $mock->shouldReceive('getHeight')->andReturn(200); + + return $mock; + } + + public function testGetIterator(): void + { + $this->assertInstanceOf(Collection::class, $this->abstractImageMock()->getIterator()); + } + + public function testGetFrames(): void + { + $this->assertInstanceOf(Collection::class, $this->abstractImageMock()->getFrames()); + } + + public function testGetFrame(): void + { + $img = $this->abstractImageMock(); + + $this->assertInstanceOf(FrameInterface::class, $img->getFrame()); + $this->assertEquals(1, $img->getFrame()->ident()); + + $this->assertInstanceOf(FrameInterface::class, $img->getFrame(1)); + $this->assertEquals(2, $img->getFrame(1)->ident()); + + $this->assertInstanceOf(FrameInterface::class, $img->getFrame(2)); + $this->assertEquals(3, $img->getFrame(2)->ident()); + } + + public function testAddFrame(): void + { + $img = $this->abstractImageMock(); + $this->assertEquals(3, $img->getFrames()->count()); + $result = $img->addFrame(Mockery::mock(FrameInterface::class)); + $this->assertInstanceOf(AbstractImage::class, $result); + $this->assertEquals(4, $img->getFrames()->count()); + } + + public function testSetGetLoops(): void + { + $img = $this->abstractImageMock(); + $this->assertEquals(0, $img->getLoops()); + $result = $img->setLoops(10); + $this->assertEquals(10, $img->getLoops()); + $this->assertInstanceOf(AbstractImage::class, $result); + } + + public function testGetSize(): void + { + $img = $this->abstractImageMock(); + $this->assertInstanceOf(Size::class, $img->getSize()); + $this->assertEquals(300, $img->getSize()->getWidth()); + $this->assertEquals(200, $img->getSize()->getHeight()); + } + + public function testIsAnimated(): void + { + $img = Mockery::mock(AbstractImage::class, [new Collection()])->makePartial(); + $this->assertFalse($img->isAnimated()); + + $collection = new Collection([ + Mockery::mock(FrameInterface::class), + Mockery::mock(FrameInterface::class), + ]); + $img = Mockery::mock(AbstractImage::class, [$collection])->makePartial(); + $this->assertTrue($img->isAnimated()); + } + + public function testModify(): void + { + $img = $this->abstractImageMock(); + + $modifier = Mockery::mock(ModifierInterface::class); + $modifier->shouldReceive('apply')->with($img)->andReturn($img); + $result = $img->modify($modifier); + $this->assertInstanceOf(ImageInterface::class, $img); + } + + public function testEncode(): void + { + $img = $this->abstractImageMock(); + + $encoder = Mockery::mock(EncoderInterface::class); + $encoded = Mockery::mock(EncodedImage::class); + $encoder->shouldReceive('encode')->with($img)->andReturn($encoded); + $result = $img->encode($encoder); + $this->assertInstanceOf(ImageInterface::class, $img); + } + + public function testToJpeg(): void + { + $img = $this->abstractImageMock(); + + $encoded = Mockery::mock(EncodedImage::class); + $encoder = Mockery::mock(EncoderInterface::class); + $encoder->shouldReceive('encode')->with($img)->andReturn($encoded); + + $img->shouldReceive('resolveDriverClass') + ->with('Encoders\JpegEncoder', 45) + ->andReturn($encoder); + + $result = $img->toJpeg(45); + $this->assertInstanceOf(EncodedImage::class, $result); + } + + public function testToGif(): void + { + $img = $this->abstractImageMock(); + + $encoded = Mockery::mock(EncodedImage::class); + $encoder = Mockery::mock(EncoderInterface::class); + $encoder->shouldReceive('encode')->with($img)->andReturn($encoded); + + $img->shouldReceive('resolveDriverClass') + ->with('Encoders\GifEncoder') + ->andReturn($encoder); + + $result = $img->toGif(); + $this->assertInstanceOf(EncodedImage::class, $result); + } + + public function testToPng(): void + { + $img = $this->abstractImageMock(); + + $encoded = Mockery::mock(EncodedImage::class); + $encoder = Mockery::mock(EncoderInterface::class); + $encoder->shouldReceive('encode')->with($img)->andReturn($encoded); + + $img->shouldReceive('resolveDriverClass') + ->with('Encoders\PngEncoder') + ->andReturn($encoder); + + $result = $img->toPng(); + $this->assertInstanceOf(EncodedImage::class, $result); + } + + public function testGreyscale(): void + { + $img = $this->abstractImageMock(); + + $modifier = Mockery::mock(ModifierInterface::class); + $modifier->shouldReceive('apply')->with($img)->andReturn($img); + + $img->shouldReceive('resolveDriverClass') + ->with('Modifiers\GreyscaleModifier') + ->andReturn($modifier); + + $result = $img->greyscale(); + $this->assertInstanceOf(ImageInterface::class, $result); + } + + public function testBlur(): void + { + $img = $this->abstractImageMock(); + + $modifier = Mockery::mock(ModifierInterface::class); + $modifier->shouldReceive('apply')->with($img)->andReturn($img); + + $img->shouldReceive('resolveDriverClass') + ->with('Modifiers\BlurModifier', 3) + ->andReturn($modifier); + + $result = $img->blur(3); + $this->assertInstanceOf(ImageInterface::class, $result); + } + + public function testRotate(): void + { + $img = $this->abstractImageMock(); + + $modifier = Mockery::mock(ModifierInterface::class); + $modifier->shouldReceive('apply')->with($img)->andReturn($img); + + $img->shouldReceive('resolveDriverClass') + ->with('Modifiers\RotateModifier', 3, 'cccccc') + ->andReturn($modifier); + + $result = $img->rotate(3, 'cccccc'); + $this->assertInstanceOf(ImageInterface::class, $result); + } + + public function testPlace(): void + { + $img = $this->abstractImageMock(); + + $modifier = Mockery::mock(ModifierInterface::class); + $modifier->shouldReceive('apply')->with($img)->andReturn($img); + + $img->shouldReceive('resolveDriverClass') + ->with('Modifiers\PlaceModifier', 'el', 'top-left', 0, 0) + ->andReturn($modifier); + + $result = $img->place('el', 'top-left', 0, 0); + $this->assertInstanceOf(ImageInterface::class, $result); + } + + public function testPickColors(): void + { + $color = Mockery::mock(ColorInterface::class); + $img = $this->abstractImageMock(); + $img->shouldReceive('pickColor')->times(3)->andReturn($color); + $result = $img->pickColors(1, 2); + $this->assertInstanceOf(Collection::class, $result); + } +} diff --git a/tests/CollectionTest.php b/tests/CollectionTest.php index f02d1843..7a22ce27 100644 --- a/tests/CollectionTest.php +++ b/tests/CollectionTest.php @@ -80,6 +80,7 @@ class CollectionTest extends TestCase $this->assertEquals('bar', $collection->get(1)); $this->assertEquals('baz', $collection->get(2)); $this->assertNull($collection->get(3)); + $this->assertEquals('test', $collection->get(3, 'test')); } public function testToArray() @@ -99,4 +100,27 @@ class CollectionTest extends TestCase $this->assertEquals(['FOO', 'BAR', 'BAZ'], $collection->toArray()); $this->assertEquals(['foo', 'bar', 'baz'], $mapped->toArray()); } + + public function testQuery(): void + { + $collection = new Collection([ + 'foo' => 'FOO', + 'bar' => 'BAR', + 'baz' => [ + 'test1' => '1', + 'test2' => '2', + 'test3' => [ + 'example' => 'value' + ] + ] + ]); + + $this->assertEquals('FOO', $collection->query('foo')); + $this->assertEquals('BAR', $collection->query('bar')); + $this->assertEquals('1', $collection->query('baz.test1')); + $this->assertEquals('2', $collection->query('baz.test2')); + $this->assertEquals('value', $collection->query('baz.test3.example')); + $this->assertEquals('value', $collection->query('baz.test3.example', 'default')); + $this->assertEquals('default', $collection->query('baz.test3.no', 'default')); + } } diff --git a/tests/Drivers/Abstract/AbstractColorTest.php b/tests/Drivers/Abstract/AbstractColorTest.php new file mode 100644 index 00000000..4db7614e --- /dev/null +++ b/tests/Drivers/Abstract/AbstractColorTest.php @@ -0,0 +1,36 @@ +makePartial(); + $color->shouldReceive('red')->andReturn(255); + $color->shouldReceive('green')->andReturn(0); + $color->shouldReceive('blue')->andReturn(0); + + $this->assertEquals('ff0000', $color->toHex()); + $this->assertEquals('#ff0000', $color->toHex('#')); + } + + public function testIsGreyscale(): void + { + $color = Mockery::mock(AbstractColor::class)->makePartial(); + $color->shouldReceive('red')->andReturn(255); + $color->shouldReceive('green')->andReturn(0); + $color->shouldReceive('blue')->andReturn(0); + $this->assertFalse($color->isGreyscale()); + + $color = Mockery::mock(AbstractColor::class)->makePartial(); + $color->shouldReceive('red')->andReturn(100); + $color->shouldReceive('green')->andReturn(100); + $color->shouldReceive('blue')->andReturn(100); + $this->assertTrue($color->isGreyscale()); + } +} diff --git a/tests/Drivers/Gd/Decoders/BinaryImageDecoderTest.php b/tests/Drivers/Gd/Decoders/BinaryImageDecoderTest.php index 3ccb4f4a..42b3b20e 100644 --- a/tests/Drivers/Gd/Decoders/BinaryImageDecoderTest.php +++ b/tests/Drivers/Gd/Decoders/BinaryImageDecoderTest.php @@ -13,8 +13,8 @@ class BinaryImageDecoderTest extends TestCase $decoder = new BinaryImageDecoder(); $image = $decoder->decode(file_get_contents(__DIR__ . '/../../../images/tile.png')); $this->assertInstanceOf(Image::class, $image); - $this->assertEquals(16, $image->width()); - $this->assertEquals(16, $image->height()); + $this->assertEquals(16, $image->getWidth()); + $this->assertEquals(16, $image->getHeight()); $this->assertCount(1, $image); } @@ -23,8 +23,8 @@ class BinaryImageDecoderTest extends TestCase $decoder = new BinaryImageDecoder(); $image = $decoder->decode(file_get_contents(__DIR__ . '/../../../images/red.gif')); $this->assertInstanceOf(Image::class, $image); - $this->assertEquals(16, $image->width()); - $this->assertEquals(16, $image->height()); + $this->assertEquals(16, $image->getWidth()); + $this->assertEquals(16, $image->getHeight()); $this->assertCount(1, $image); } @@ -33,8 +33,8 @@ class BinaryImageDecoderTest extends TestCase $decoder = new BinaryImageDecoder(); $image = $decoder->decode(file_get_contents(__DIR__ . '/../../../images/cats.gif')); $this->assertInstanceOf(Image::class, $image); - $this->assertEquals(75, $image->width()); - $this->assertEquals(50, $image->height()); + $this->assertEquals(75, $image->getWidth()); + $this->assertEquals(50, $image->getHeight()); $this->assertCount(4, $image); } } diff --git a/tests/Drivers/Gd/Decoders/ImageObjectDecoderTest.php b/tests/Drivers/Gd/Decoders/ImageObjectDecoderTest.php new file mode 100644 index 00000000..573aa4a7 --- /dev/null +++ b/tests/Drivers/Gd/Decoders/ImageObjectDecoderTest.php @@ -0,0 +1,20 @@ +decode($this->createTestImage('blue.gif')); + $this->assertInstanceOf(Image::class, $result); + } +} diff --git a/tests/Drivers/Gd/Decoders/RgbStringColorDecoderTest.php b/tests/Drivers/Gd/Decoders/RgbStringColorDecoderTest.php new file mode 100644 index 00000000..19c2a26f --- /dev/null +++ b/tests/Drivers/Gd/Decoders/RgbStringColorDecoderTest.php @@ -0,0 +1,32 @@ +decode('rgb(181, 55, 23)'); + $this->assertInstanceOf(Color::class, $color); + $this->assertEquals(181, $color->red()); + $this->assertEquals(55, $color->green()); + $this->assertEquals(23, $color->blue()); + $this->assertEquals(1, $color->alpha()); + } + + public function testDecodeRgba(): void + { + $decoder = new RgbStringColorDecoder(); + $color = $decoder->decode('rgba(181, 55, 23, 0.5)'); + $this->assertInstanceOf(Color::class, $color); + $this->assertEquals(181, $color->red()); + $this->assertEquals(55, $color->green()); + $this->assertEquals(23, $color->blue()); + $this->assertEquals(.5, $color->alpha()); + } +} diff --git a/tests/Drivers/Gd/Encoders/JpegEncoderTest.php b/tests/Drivers/Gd/Encoders/JpegEncoderTest.php index 17af9db1..231f0aab 100644 --- a/tests/Drivers/Gd/Encoders/JpegEncoderTest.php +++ b/tests/Drivers/Gd/Encoders/JpegEncoderTest.php @@ -22,8 +22,8 @@ class JpegEncoderTest extends TestCase public function testEncode(): void { $image = $this->getTestImage(); - $encoder = new JpegEncoder(); + $encoder = new JpegEncoder(75); $result = $encoder->encode($image); - $this->assertTrue(MimeSniffer::createFromString($result)->matches(new ImageJpeg)); + $this->assertTrue(MimeSniffer::createFromString($result)->matches(new ImageJpeg())); } -} \ No newline at end of file +} diff --git a/tests/Drivers/Gd/ImageFactoryTest.php b/tests/Drivers/Gd/ImageFactoryTest.php index 20bc9abd..9f319741 100644 --- a/tests/Drivers/Gd/ImageFactoryTest.php +++ b/tests/Drivers/Gd/ImageFactoryTest.php @@ -2,6 +2,7 @@ namespace Intervention\Image\Tests\Drivers\Gd; +use GdImage; use Intervention\Image\Drivers\Gd\Image; use Intervention\Image\Drivers\Gd\ImageFactory; use Intervention\Image\Tests\TestCase; @@ -14,4 +15,11 @@ class ImageFactoryTest extends TestCase $image = $factory->newImage(3, 2); $this->assertInstanceOf(Image::class, $image); } + + public function testNewCore(): void + { + $factory = new ImageFactory(); + $core = $factory->newCore(3, 2); + $this->assertInstanceOf(GdImage::class, $core); + } } diff --git a/tests/Drivers/Gd/ImageTest.php b/tests/Drivers/Gd/ImageTest.php index 6553bd03..1916587b 100644 --- a/tests/Drivers/Gd/ImageTest.php +++ b/tests/Drivers/Gd/ImageTest.php @@ -62,9 +62,9 @@ class ImageTest extends TestCase public function testSetGetLoops(): void { - $this->assertEquals(0, $this->image->loops()); + $this->assertEquals(0, $this->image->getLoops()); $result = $this->image->setLoops(12); - $this->assertEquals(12, $this->image->loops()); + $this->assertEquals(12, $this->image->getLoops()); $this->assertInstanceOf(Image::class, $result); } @@ -75,12 +75,12 @@ class ImageTest extends TestCase public function testWidth(): void { - $this->assertEquals(3, $this->image->width()); + $this->assertEquals(3, $this->image->getWidth()); } public function testHeight(): void { - $this->assertEquals(2, $this->image->height()); + $this->assertEquals(2, $this->image->getHeight()); } public function testGetSize(): void diff --git a/tests/Drivers/Gd/Modifiers/BrightnessModifierTest.php b/tests/Drivers/Gd/Modifiers/BrightnessModifierTest.php new file mode 100644 index 00000000..e0e41025 --- /dev/null +++ b/tests/Drivers/Gd/Modifiers/BrightnessModifierTest.php @@ -0,0 +1,21 @@ +createTestImage('trim.png'); + $this->assertEquals('00aef0', $image->pickColor(14, 14)->toHex()); + $image->modify(new BrightnessModifier(30)); + $this->assertEquals('4cfaff', $image->pickColor(14, 14)->toHex()); + } +} diff --git a/tests/Drivers/Gd/Modifiers/ContrastModifierTest.php b/tests/Drivers/Gd/Modifiers/ContrastModifierTest.php new file mode 100644 index 00000000..7ec44484 --- /dev/null +++ b/tests/Drivers/Gd/Modifiers/ContrastModifierTest.php @@ -0,0 +1,21 @@ +createTestImage('trim.png'); + $this->assertEquals('00aef0', $image->pickColor(14, 14)->toHex()); + $image->modify(new ContrastModifier(30)); + $this->assertEquals('00ceff', $image->pickColor(14, 14)->toHex()); + } +} diff --git a/tests/Drivers/Gd/Modifiers/DestroyModifierTest.php b/tests/Drivers/Gd/Modifiers/DestroyModifierTest.php new file mode 100644 index 00000000..52b48fc1 --- /dev/null +++ b/tests/Drivers/Gd/Modifiers/DestroyModifierTest.php @@ -0,0 +1,21 @@ +createTestImage('trim.png'); + $this->assertInstanceOf(GdImage::class, $image->getFrame()->getCore()); + $image->modify(new DestroyModifier()); + } +} diff --git a/tests/Drivers/Gd/Modifiers/FillModifierTest.php b/tests/Drivers/Gd/Modifiers/FillModifierTest.php new file mode 100644 index 00000000..a1f8c049 --- /dev/null +++ b/tests/Drivers/Gd/Modifiers/FillModifierTest.php @@ -0,0 +1,34 @@ +createTestImage('blocks.png'); + $this->assertEquals('0000ff', $image->pickColor(420, 270)->toHex()); + $this->assertEquals('ff0000', $image->pickColor(540, 400)->toHex()); + $image->modify(new FillModifier(new Color(13421772), new Point(540, 400))); + $this->assertEquals('0000ff', $image->pickColor(420, 270)->toHex()); + $this->assertEquals('cccccc', $image->pickColor(540, 400)->toHex()); + } + + public function testFillAllColor(): void + { + $image = $this->createTestImage('blocks.png'); + $this->assertEquals('0000ff', $image->pickColor(420, 270)->toHex()); + $this->assertEquals('ff0000', $image->pickColor(540, 400)->toHex()); + $image->modify(new FillModifier(new Color(13421772))); + $this->assertEquals('cccccc', $image->pickColor(420, 270)->toHex()); + $this->assertEquals('cccccc', $image->pickColor(540, 400)->toHex()); + } +} diff --git a/tests/Drivers/Gd/Modifiers/FitModifierTest.php b/tests/Drivers/Gd/Modifiers/FitModifierTest.php new file mode 100644 index 00000000..89056042 --- /dev/null +++ b/tests/Drivers/Gd/Modifiers/FitModifierTest.php @@ -0,0 +1,29 @@ +createTestImage('blocks.png'); + $this->assertEquals(640, $image->getWidth()); + $this->assertEquals(480, $image->getHeight()); + $image->modify(new FitModifier(100, 100, 'center')); + $this->assertEquals(100, $image->getWidth()); + $this->assertEquals(100, $image->getHeight()); + $this->assertColor(255, 0, 0, 1, $image->pickColor(90, 90)); + $this->assertColor(0, 255, 0, 1, $image->pickColor(65, 70)); + $this->assertColor(0, 0, 255, 1, $image->pickColor(70, 52)); + $this->assertTransparency($image->pickColor(90, 30)); + } +} diff --git a/tests/Drivers/Gd/Modifiers/InvertModifierTest.php b/tests/Drivers/Gd/Modifiers/InvertModifierTest.php new file mode 100644 index 00000000..6429f3fb --- /dev/null +++ b/tests/Drivers/Gd/Modifiers/InvertModifierTest.php @@ -0,0 +1,23 @@ +createTestImage('trim.png'); + $this->assertEquals('00aef0', $image->pickColor(0, 0)->toHex()); + $this->assertEquals('ffa601', $image->pickColor(25, 25)->toHex()); + $image->modify(new InvertModifier()); + $this->assertEquals('ff510f', $image->pickColor(0, 0)->toHex()); + $this->assertEquals('0059fe', $image->pickColor(25, 25)->toHex()); + } +} diff --git a/tests/Drivers/Gd/Modifiers/PixelateModifierTest.php b/tests/Drivers/Gd/Modifiers/PixelateModifierTest.php new file mode 100644 index 00000000..0fc4a892 --- /dev/null +++ b/tests/Drivers/Gd/Modifiers/PixelateModifierTest.php @@ -0,0 +1,23 @@ +createTestImage('trim.png'); + $this->assertEquals('00aef0', $image->pickColor(0, 0)->toHex()); + $this->assertEquals('00aef0', $image->pickColor(14, 14)->toHex()); + $image->modify(new PixelateModifier(10)); + $this->assertEquals('00aef0', $image->pickColor(0, 0)->toHex()); + $this->assertEquals('6aaa8b', $image->pickColor(14, 14)->toHex()); + } +} diff --git a/tests/Drivers/Gd/Modifiers/ResizeModifierTest.php b/tests/Drivers/Gd/Modifiers/ResizeModifierTest.php index f6bbb016..b1ebe570 100644 --- a/tests/Drivers/Gd/Modifiers/ResizeModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/ResizeModifierTest.php @@ -12,13 +12,17 @@ class ResizeModifierTest extends TestCase { use CanCreateGdTestImage; - public function testColorChange(): void + public function testModify(): void { - $image = $this->createTestImage('trim.png'); - $this->assertEquals(50, $image->width()); - $this->assertEquals(50, $image->height()); - $image->modify(new ResizeModifier(new Size(300, 100))); - $this->assertEquals(300, $image->width()); - $this->assertEquals(100, $image->height()); + $image = $this->createTestImage('blocks.png'); + $this->assertEquals(640, $image->getWidth()); + $this->assertEquals(480, $image->getHeight()); + $image->modify(new ResizeModifier(200, 100)); + $this->assertEquals(200, $image->getWidth()); + $this->assertEquals(100, $image->getHeight()); + $this->assertColor(255, 0, 0, 1, $image->pickColor(150, 70)); + $this->assertColor(0, 255, 0, 1, $image->pickColor(125, 70)); + $this->assertColor(0, 0, 255, 1, $image->pickColor(130, 54)); + $this->assertTransparency($image->pickColor(150, 45)); } } diff --git a/tests/Drivers/Gd/Modifiers/SharpenModifierTest.php b/tests/Drivers/Gd/Modifiers/SharpenModifierTest.php new file mode 100644 index 00000000..83d52090 --- /dev/null +++ b/tests/Drivers/Gd/Modifiers/SharpenModifierTest.php @@ -0,0 +1,21 @@ +createTestImage('trim.png'); + $this->assertEquals('60ab96', $image->pickColor(15, 14)->toHex()); + $image->modify(new SharpenModifier(10)); + $this->assertEquals('4daba7', $image->pickColor(15, 14)->toHex()); + } +} diff --git a/tests/Drivers/Imagick/Decoders/ImageObjectDecoderTest.php b/tests/Drivers/Imagick/Decoders/ImageObjectDecoderTest.php new file mode 100644 index 00000000..1f3ad8ec --- /dev/null +++ b/tests/Drivers/Imagick/Decoders/ImageObjectDecoderTest.php @@ -0,0 +1,20 @@ +decode($this->createTestImage('blue.gif')); + $this->assertInstanceOf(Image::class, $result); + } +} diff --git a/tests/Drivers/Imagick/Decoders/RgbStringColorDecoderTest.php b/tests/Drivers/Imagick/Decoders/RgbStringColorDecoderTest.php new file mode 100644 index 00000000..07f8e652 --- /dev/null +++ b/tests/Drivers/Imagick/Decoders/RgbStringColorDecoderTest.php @@ -0,0 +1,32 @@ +decode('rgb(181, 55, 23)'); + $this->assertInstanceOf(Color::class, $color); + $this->assertEquals(181, $color->red()); + $this->assertEquals(55, $color->green()); + $this->assertEquals(23, $color->blue()); + $this->assertEquals(1, $color->alpha()); + } + + public function testDecodeRgba(): void + { + $decoder = new RgbStringColorDecoder(); + $color = $decoder->decode('rgba(181, 55, 23, 0.5)'); + $this->assertInstanceOf(Color::class, $color); + $this->assertEquals(181, $color->red()); + $this->assertEquals(55, $color->green()); + $this->assertEquals(23, $color->blue()); + $this->assertEquals(.5, $color->alpha()); + } +} diff --git a/tests/Drivers/Imagick/Encoders/JpegEncoderTest.php b/tests/Drivers/Imagick/Encoders/JpegEncoderTest.php index 7a01f54a..6c6f0967 100644 --- a/tests/Drivers/Imagick/Encoders/JpegEncoderTest.php +++ b/tests/Drivers/Imagick/Encoders/JpegEncoderTest.php @@ -26,8 +26,8 @@ class JpegEncoderTest extends TestCase public function testEncode(): void { $image = $this->getTestImage(); - $encoder = new JpegEncoder(); + $encoder = new JpegEncoder(75); $result = $encoder->encode($image); - $this->assertTrue(MimeSniffer::createFromString($result)->matches(new ImageJpeg)); + $this->assertTrue(MimeSniffer::createFromString($result)->matches(new ImageJpeg())); } -} \ No newline at end of file +} diff --git a/tests/Drivers/Imagick/ImageFactoryTest.php b/tests/Drivers/Imagick/ImageFactoryTest.php new file mode 100644 index 00000000..d23cb443 --- /dev/null +++ b/tests/Drivers/Imagick/ImageFactoryTest.php @@ -0,0 +1,25 @@ +newImage(3, 2); + $this->assertInstanceOf(Image::class, $image); + } + + public function testNewCore(): void + { + $factory = new ImageFactory(); + $core = $factory->newCore(3, 2); + $this->assertInstanceOf(Imagick::class, $core); + } +} diff --git a/tests/Drivers/Imagick/ImageTest.php b/tests/Drivers/Imagick/ImageTest.php index a70666dc..17c7d706 100644 --- a/tests/Drivers/Imagick/ImageTest.php +++ b/tests/Drivers/Imagick/ImageTest.php @@ -36,12 +36,12 @@ class ImageTest extends TestCase public function testWidth(): void { - $this->assertEquals(3, $this->image->width()); + $this->assertEquals(3, $this->image->getWidth()); } public function testHeight(): void { - $this->assertEquals(2, $this->image->height()); + $this->assertEquals(2, $this->image->getHeight()); } public function testGetSize(): void diff --git a/tests/Drivers/Imagick/Modifiers/BrightnessModifierTest.php b/tests/Drivers/Imagick/Modifiers/BrightnessModifierTest.php new file mode 100644 index 00000000..f0e3dfc7 --- /dev/null +++ b/tests/Drivers/Imagick/Modifiers/BrightnessModifierTest.php @@ -0,0 +1,21 @@ +createTestImage('trim.png'); + $this->assertEquals('00aef0', $image->pickColor(14, 14)->toHex()); + $image->modify(new BrightnessModifier(30)); + $this->assertEquals('39c9ff', $image->pickColor(14, 14)->toHex()); + } +} diff --git a/tests/Drivers/Imagick/Modifiers/ContrastModifierTest.php b/tests/Drivers/Imagick/Modifiers/ContrastModifierTest.php new file mode 100644 index 00000000..042c3f08 --- /dev/null +++ b/tests/Drivers/Imagick/Modifiers/ContrastModifierTest.php @@ -0,0 +1,21 @@ +createTestImage('trim.png'); + $this->assertEquals('00aef0', $image->pickColor(14, 14)->toHex()); + $image->modify(new ContrastModifier(30)); + $this->assertEquals('00fcff', $image->pickColor(14, 14)->toHex()); + } +} diff --git a/tests/Drivers/Imagick/Modifiers/DestroyModifierTest.php b/tests/Drivers/Imagick/Modifiers/DestroyModifierTest.php new file mode 100644 index 00000000..6b9d3efe --- /dev/null +++ b/tests/Drivers/Imagick/Modifiers/DestroyModifierTest.php @@ -0,0 +1,21 @@ +createTestImage('trim.png'); + $this->assertInstanceOf(Imagick::class, $image->getFrame()->getCore()); + $image->modify(new DestroyModifier()); + } +} diff --git a/tests/Drivers/Imagick/Modifiers/FillModifierTest.php b/tests/Drivers/Imagick/Modifiers/FillModifierTest.php new file mode 100644 index 00000000..4c5a8d15 --- /dev/null +++ b/tests/Drivers/Imagick/Modifiers/FillModifierTest.php @@ -0,0 +1,36 @@ +createTestImage('blocks.png'); + $this->assertEquals('0000ff', $image->pickColor(420, 270)->toHex()); + $this->assertEquals('ff0000', $image->pickColor(540, 400)->toHex()); + $image->modify(new FillModifier(new Color(new ImagickPixel('#cccccc')), new Point(540, 400))); + $this->assertEquals('0000ff', $image->pickColor(420, 270)->toHex()); + $this->assertEquals('cccccc', $image->pickColor(540, 400)->toHex()); + } + + public function testFillAllColor(): void + { + $image = $this->createTestImage('blocks.png'); + $this->assertEquals('0000ff', $image->pickColor(420, 270)->toHex()); + $this->assertEquals('ff0000', $image->pickColor(540, 400)->toHex()); + $image->modify(new FillModifier(new Color(new ImagickPixel('#cccccc')))); + $this->assertEquals('cccccc', $image->pickColor(420, 270)->toHex()); + $this->assertEquals('cccccc', $image->pickColor(540, 400)->toHex()); + } +} diff --git a/tests/Drivers/Imagick/Modifiers/FitModifierTest.php b/tests/Drivers/Imagick/Modifiers/FitModifierTest.php new file mode 100644 index 00000000..aeb4ef31 --- /dev/null +++ b/tests/Drivers/Imagick/Modifiers/FitModifierTest.php @@ -0,0 +1,29 @@ +createTestImage('blocks.png'); + $this->assertEquals(640, $image->getWidth()); + $this->assertEquals(480, $image->getHeight()); + $image->modify(new FitModifier(100, 100, 'center')); + $this->assertEquals(100, $image->getWidth()); + $this->assertEquals(100, $image->getHeight()); + $this->assertColor(255, 0, 0, 1, $image->pickColor(90, 90)); + $this->assertColor(0, 255, 0, 1, $image->pickColor(65, 70)); + $this->assertColor(0, 0, 255, 1, $image->pickColor(70, 52)); + $this->assertTransparency($image->pickColor(90, 30)); + } +} diff --git a/tests/Drivers/Imagick/Modifiers/InvertModifierTest.php b/tests/Drivers/Imagick/Modifiers/InvertModifierTest.php new file mode 100644 index 00000000..c167542c --- /dev/null +++ b/tests/Drivers/Imagick/Modifiers/InvertModifierTest.php @@ -0,0 +1,23 @@ +createTestImage('trim.png'); + $this->assertEquals('00aef0', $image->pickColor(0, 0)->toHex()); + $this->assertEquals('ffa601', $image->pickColor(25, 25)->toHex()); + $image->modify(new InvertModifier()); + $this->assertEquals('ff510f', $image->pickColor(0, 0)->toHex()); + $this->assertEquals('0059fe', $image->pickColor(25, 25)->toHex()); + } +} diff --git a/tests/Drivers/Imagick/Modifiers/PixelateModifierTest.php b/tests/Drivers/Imagick/Modifiers/PixelateModifierTest.php new file mode 100644 index 00000000..d0d9d993 --- /dev/null +++ b/tests/Drivers/Imagick/Modifiers/PixelateModifierTest.php @@ -0,0 +1,23 @@ +createTestImage('trim.png'); + $this->assertEquals('00aef0', $image->pickColor(0, 0)->toHex()); + $this->assertEquals('00aef0', $image->pickColor(14, 14)->toHex()); + $image->modify(new PixelateModifier(10)); + $this->assertEquals('00aef0', $image->pickColor(0, 0)->toHex()); + $this->assertEquals('6bab8c', $image->pickColor(14, 14)->toHex()); + } +} diff --git a/tests/Drivers/Imagick/Modifiers/ResizeModifierTest.php b/tests/Drivers/Imagick/Modifiers/ResizeModifierTest.php index 488ba23b..7f2bb201 100644 --- a/tests/Drivers/Imagick/Modifiers/ResizeModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/ResizeModifierTest.php @@ -3,7 +3,7 @@ namespace Intervention\Image\Tests\Drivers\Imagick\Modifiers; use Intervention\Image\Drivers\Imagick\Image; -use Intervention\Image\Drivers\Imagick\Modifiers\CropResizeModifier; +use Intervention\Image\Drivers\Imagick\Modifiers\ResizeModifier; use Intervention\Image\Geometry\Size; use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateImagickTestImage; @@ -12,13 +12,17 @@ class CropResizeModifierTest extends TestCase { use CanCreateImagickTestImage; - public function testColorChange(): void + public function testModify(): void { - $image = $this->createTestImage('trim.png'); - $this->assertEquals(50, $image->width()); - $this->assertEquals(50, $image->height()); - $image->modify(new CropResizeModifier(new Size(50, 50), new Size(30, 20))); - $this->assertEquals(30, $image->width()); - $this->assertEquals(20, $image->height()); + $image = $this->createTestImage('blocks.png'); + $this->assertEquals(640, $image->getWidth()); + $this->assertEquals(480, $image->getHeight()); + $image->modify(new ResizeModifier(200, 100)); + $this->assertEquals(200, $image->getWidth()); + $this->assertEquals(100, $image->getHeight()); + $this->assertColor(255, 0, 0, 1, $image->pickColor(150, 70)); + $this->assertColor(0, 255, 0, 1, $image->pickColor(125, 70)); + $this->assertColor(0, 0, 255, 1, $image->pickColor(130, 54)); + $this->assertTransparency($image->pickColor(150, 45)); } } diff --git a/tests/Drivers/Imagick/Modifiers/SharpenModifierTest.php b/tests/Drivers/Imagick/Modifiers/SharpenModifierTest.php new file mode 100644 index 00000000..f2e23bd1 --- /dev/null +++ b/tests/Drivers/Imagick/Modifiers/SharpenModifierTest.php @@ -0,0 +1,21 @@ +createTestImage('trim.png'); + $this->assertEquals('60ab96', $image->pickColor(15, 14)->toHex()); + $image->modify(new SharpenModifier(10)); + $this->assertEquals('4faca6', $image->pickColor(15, 14)->toHex()); + } +} diff --git a/tests/Geometry/ResizerTest.php b/tests/Geometry/ResizerTest.php index fb7b37e6..9397d44d 100644 --- a/tests/Geometry/ResizerTest.php +++ b/tests/Geometry/ResizerTest.php @@ -11,106 +11,66 @@ class ResizerTest extends TestCase { public function testMake(): void { - $result = Resizer::make(); - $this->assertInstanceOf(Resizer::class, $result); + $resizer = Resizer::to(); + $this->assertInstanceOf(Resizer::class, $resizer); + + $resizer = Resizer::to(height: 100); + $this->assertInstanceOf(Resizer::class, $resizer); + + $resizer = Resizer::to(100); + $this->assertInstanceOf(Resizer::class, $resizer); + + $resizer = Resizer::to(100, 100); + $this->assertInstanceOf(Resizer::class, $resizer); } - public function testSetTargetWidth(): void + public function testToWidth(): void { $resizer = new Resizer(); - $result = $resizer->width(100); - $this->assertInstanceOf(Resizer::class, $result); $result = $resizer->toWidth(100); $this->assertInstanceOf(Resizer::class, $result); } - public function testSetTargetHeight(): void + public function testToHeight(): void { $resizer = new Resizer(); - $result = $resizer->height(100); - $this->assertInstanceOf(Resizer::class, $result); $result = $resizer->toHeight(100); $this->assertInstanceOf(Resizer::class, $result); } - public function testSetTargetSizeByArray() - { - $size = new Size(300, 200); - $resizer = new Resizer(); - $resizer = $resizer->setTargetSizeByArray([800, 600]); - $this->assertInstanceOf(Resizer::class, $resizer); - $this->assertEquals(800, $resizer->resize($size)->getWidth()); - $this->assertEquals(600, $resizer->resize($size)->getHeight()); - - $size = new Size(300, 200); - $resizer = new Resizer(); - $resizer = $resizer->setTargetSizeByArray([800]); - $this->assertInstanceOf(Resizer::class, $resizer); - $this->assertEquals(800, $resizer->resize($size)->getWidth()); - $this->assertEquals(200, $resizer->resize($size)->getHeight()); - - $size = new Size(300, 200); - $resizer = new Resizer(); - $resizer = $resizer->setTargetSizeByArray([function ($size) { - $size->width(80); - $size->height(40); - }]); - $this->assertInstanceOf(Resizer::class, $resizer); - $this->assertEquals(80, $resizer->resize($size)->getWidth()); - $this->assertEquals(40, $resizer->resize($size)->getHeight()); - - $size = new Size(300, 200); - $resizer = new Resizer(); - $resizer = $resizer->setTargetSizeByArray([function ($size) { - $size->width(80); - }]); - $this->assertInstanceOf(Resizer::class, $resizer); - $this->assertEquals(80, $resizer->resize($size)->getWidth()); - $this->assertEquals(200, $resizer->resize($size)->getHeight()); - - $size = new Size(300, 200); - $resizer = new Resizer(); - $resizer = $resizer->setTargetSizeByArray([function ($size) { - $size->height(10); - }]); - $this->assertInstanceOf(Resizer::class, $resizer); - $this->assertEquals(300, $resizer->resize($size)->getWidth()); - $this->assertEquals(10, $resizer->resize($size)->getHeight()); - } - - public function testSetTargetSize(): void - { - $size = new Size(300, 200); - $resizer = new Resizer(); - $resizer = $resizer->setTargetSize(new Size(200, 100)); - $this->assertInstanceOf(Resizer::class, $resizer); - $this->assertEquals(200, $resizer->resize($size)->getWidth()); - $this->assertEquals(100, $resizer->resize($size)->getHeight()); - } - public function testToSize(): void { $size = new Size(300, 200); $resizer = new Resizer(); $resizer = $resizer->toSize(new Size(200, 100)); $this->assertInstanceOf(Resizer::class, $resizer); - $this->assertEquals(200, $resizer->resize($size)->getWidth()); - $this->assertEquals(100, $resizer->resize($size)->getHeight()); } public function testResize() { $size = new Size(300, 200); $resizer = new Resizer(); - $resizer->width(150); + $resizer->toWidth(150); $result = $resizer->resize($size); $this->assertEquals(150, $result->getWidth()); $this->assertEquals(200, $result->getHeight()); $size = new Size(300, 200); $resizer = new Resizer(); - $resizer->width(20); - $resizer->height(10); + $resizer->toWidth(20); + $resizer->toHeight(10); + $result = $resizer->resize($size); + $this->assertEquals(20, $result->getWidth()); + $this->assertEquals(10, $result->getHeight()); + + $size = new Size(300, 200); + $resizer = new Resizer(width: 150); + $result = $resizer->resize($size); + $this->assertEquals(150, $result->getWidth()); + $this->assertEquals(200, $result->getHeight()); + + $size = new Size(300, 200); + $resizer = new Resizer(height: 10, width: 20); $result = $resizer->resize($size); $this->assertEquals(20, $result->getWidth()); $this->assertEquals(10, $result->getHeight()); @@ -121,8 +81,8 @@ class ResizerTest extends TestCase // 800x600 > 1000x2000 = 800x600 $size = new Size(800, 600); $resizer = new Resizer(); - $resizer->width(1000); - $resizer->height(2000); + $resizer->toWidth(1000); + $resizer->toHeight(2000); $result = $resizer->resizeDown($size); $this->assertEquals(800, $result->getWidth()); $this->assertEquals(600, $result->getHeight()); @@ -130,8 +90,8 @@ class ResizerTest extends TestCase // 800x600 > 400x1000 = 400x600 $size = new Size(800, 600); $resizer = new Resizer(); - $resizer->width(400); - $resizer->height(1000); + $resizer->toWidth(400); + $resizer->toHeight(1000); $result = $resizer->resizeDown($size); $this->assertEquals(400, $result->getWidth()); $this->assertEquals(600, $result->getHeight()); @@ -139,8 +99,8 @@ class ResizerTest extends TestCase // 800x600 > 1000x400 = 800x400 $size = new Size(800, 600); $resizer = new Resizer(); - $resizer->width(1000); - $resizer->height(400); + $resizer->toWidth(1000); + $resizer->toHeight(400); $result = $resizer->resizeDown($size); $this->assertEquals(800, $result->getWidth()); $this->assertEquals(400, $result->getHeight()); @@ -148,8 +108,8 @@ class ResizerTest extends TestCase // 800x600 > 400x300 = 400x300 $size = new Size(800, 600); $resizer = new Resizer(); - $resizer->width(400); - $resizer->height(300); + $resizer->toWidth(400); + $resizer->toHeight(300); $result = $resizer->resizeDown($size); $this->assertEquals(400, $result->getWidth()); $this->assertEquals(300, $result->getHeight()); @@ -157,7 +117,7 @@ class ResizerTest extends TestCase // 800x600 > 1000xnull = 800x600 $size = new Size(800, 600); $resizer = new Resizer(); - $resizer->width(1000); + $resizer->toWidth(1000); $result = $resizer->resizeDown($size); $this->assertEquals(800, $result->getWidth()); $this->assertEquals(600, $result->getHeight()); @@ -165,7 +125,7 @@ class ResizerTest extends TestCase // 800x600 > nullx1000 = 800x600 $size = new Size(800, 600); $resizer = new Resizer(); - $resizer->height(1000); + $resizer->toHeight(1000); $result = $resizer->resizeDown($size); $this->assertEquals(800, $result->getWidth()); $this->assertEquals(600, $result->getHeight()); @@ -176,8 +136,8 @@ class ResizerTest extends TestCase // 800x600 > 1000x2000 = 1000x750 $size = new Size(800, 600); $resizer = new Resizer(); - $resizer->width(1000); - $resizer->height(2000); + $resizer->toWidth(1000); + $resizer->toHeight(2000); $result = $resizer->scale($size); $this->assertEquals(1000, $result->getWidth()); $this->assertEquals(750, $result->getHeight()); @@ -185,8 +145,8 @@ class ResizerTest extends TestCase // 800x600 > 2000x1000 = 1333x1000 $size = new Size(800, 600); $resizer = new Resizer(); - $resizer->width(2000); - $resizer->height(1000); + $resizer->toWidth(2000); + $resizer->toHeight(1000); $result = $resizer->scale($size); $this->assertEquals(1333, $result->getWidth()); $this->assertEquals(1000, $result->getHeight()); @@ -194,7 +154,7 @@ class ResizerTest extends TestCase // // 800x600 > nullx3000 = 4000x3000 $size = new Size(800, 600); $resizer = new Resizer(); - $resizer->height(3000); + $resizer->toHeight(3000); $result = $resizer->scale($size); $this->assertEquals(4000, $result->getWidth()); $this->assertEquals(3000, $result->getHeight()); @@ -202,7 +162,7 @@ class ResizerTest extends TestCase // // 800x600 > 8000xnull = 8000x6000 $size = new Size(800, 600); $resizer = new Resizer(); - $resizer->width(8000); + $resizer->toWidth(8000); $result = $resizer->scale($size); $this->assertEquals(8000, $result->getWidth()); $this->assertEquals(6000, $result->getHeight()); @@ -210,8 +170,8 @@ class ResizerTest extends TestCase // // 800x600 > 100x400 = 100x75 $size = new Size(800, 600); $resizer = new Resizer(); - $resizer->width(100); - $resizer->height(400); + $resizer->toWidth(100); + $resizer->toHeight(400); $result = $resizer->scale($size); $this->assertEquals(100, $result->getWidth()); $this->assertEquals(75, $result->getHeight()); @@ -219,8 +179,8 @@ class ResizerTest extends TestCase // // 800x600 > 400x100 = 133x100 $size = new Size(800, 600); $resizer = new Resizer(); - $resizer->width(400); - $resizer->height(100); + $resizer->toWidth(400); + $resizer->toHeight(100); $result = $resizer->scale($size); $this->assertEquals(133, $result->getWidth()); $this->assertEquals(100, $result->getHeight()); @@ -228,7 +188,7 @@ class ResizerTest extends TestCase // // 800x600 > nullx300 = 400x300 $size = new Size(800, 600); $resizer = new Resizer(); - $resizer->height(300); + $resizer->toHeight(300); $result = $resizer->scale($size); $this->assertEquals(400, $result->getWidth()); $this->assertEquals(300, $result->getHeight()); @@ -236,7 +196,7 @@ class ResizerTest extends TestCase // // 800x600 > 80xnull = 80x60 $size = new Size(800, 600); $resizer = new Resizer(); - $resizer->width(80); + $resizer->toWidth(80); $result = $resizer->scale($size); $this->assertEquals(80, $result->getWidth()); $this->assertEquals(60, $result->getHeight()); @@ -244,7 +204,7 @@ class ResizerTest extends TestCase // // 640x480 > 225xnull = 225x169 $size = new Size(640, 480); $resizer = new Resizer(); - $resizer->width(225); + $resizer->toWidth(225); $result = $resizer->scale($size); $this->assertEquals(225, $result->getWidth()); $this->assertEquals(169, $result->getHeight()); @@ -252,7 +212,7 @@ class ResizerTest extends TestCase // // 640x480 > 223xnull = 223x167 $size = new Size(640, 480); $resizer = new Resizer(); - $resizer->width(223); + $resizer->toWidth(223); $result = $resizer->scale($size); $this->assertEquals(223, $result->getWidth()); $this->assertEquals(167, $result->getHeight()); @@ -260,8 +220,8 @@ class ResizerTest extends TestCase // // 600x800 > 300x300 = 225x300 $size = new Size(600, 800); $resizer = new Resizer(); - $resizer->width(300); - $resizer->height(300); + $resizer->toWidth(300); + $resizer->toHeight(300); $result = $resizer->scale($size); $this->assertEquals(225, $result->getWidth()); $this->assertEquals(300, $result->getHeight()); @@ -269,8 +229,8 @@ class ResizerTest extends TestCase // // 800x600 > 400x10 = 13x10 $size = new Size(800, 600); $resizer = new Resizer(); - $resizer->width(400); - $resizer->height(10); + $resizer->toWidth(400); + $resizer->toHeight(10); $result = $resizer->scale($size); $this->assertEquals(13, $result->getWidth()); $this->assertEquals(10, $result->getHeight()); @@ -278,32 +238,32 @@ class ResizerTest extends TestCase // // 800x600 > 1000x1200 = 1000x750 $size = new Size(800, 600); $resizer = new Resizer(); - $resizer->width(1000); - $resizer->height(1200); + $resizer->toWidth(1000); + $resizer->toHeight(1200); $result = $resizer->scale($size); $this->assertEquals(1000, $result->getWidth()); $this->assertEquals(750, $result->getHeight()); $size = new Size(12000, 12); $resizer = new Resizer(); - $resizer->width(4000); - $resizer->height(3000); + $resizer->toWidth(4000); + $resizer->toHeight(3000); $result = $resizer->scale($size); $this->assertEquals(4000, $result->getWidth()); $this->assertEquals(4, $result->getHeight()); $size = new Size(12, 12000); $resizer = new Resizer(); - $resizer->width(4000); - $resizer->height(3000); + $resizer->toWidth(4000); + $resizer->toHeight(3000); $result = $resizer->scale($size); $this->assertEquals(3, $result->getWidth()); $this->assertEquals(3000, $result->getHeight()); $size = new Size(12000, 6000); $resizer = new Resizer(); - $resizer->width(4000); - $resizer->height(3000); + $resizer->toWidth(4000); + $resizer->toHeight(3000); $result = $resizer->scale($size); $this->assertEquals(4000, $result->getWidth()); $this->assertEquals(2000, $result->getHeight()); @@ -313,91 +273,91 @@ class ResizerTest extends TestCase { $size = new Size(800, 600); $resizer = new Resizer(); - $resizer->width(1000); - $resizer->height(2000); + $resizer->toWidth(1000); + $resizer->toHeight(2000); $result = $resizer->scaleDown($size); $this->assertEquals(800, $result->getWidth()); $this->assertEquals(600, $result->getHeight()); $size = new Size(800, 600); $resizer = new Resizer(); - $resizer->width(1000); - $resizer->height(600); + $resizer->toWidth(1000); + $resizer->toHeight(600); $result = $resizer->scaleDown($size); $this->assertEquals(800, $result->getWidth()); $this->assertEquals(600, $result->getHeight()); $size = new Size(800, 600); $resizer = new Resizer(); - $resizer->width(1000); - $resizer->height(300); + $resizer->toWidth(1000); + $resizer->toHeight(300); $result = $resizer->scaleDown($size); $this->assertEquals(400, $result->getWidth()); $this->assertEquals(300, $result->getHeight()); $size = new Size(800, 600); $resizer = new Resizer(); - $resizer->width(400); - $resizer->height(1000); + $resizer->toWidth(400); + $resizer->toHeight(1000); $result = $resizer->scaleDown($size); $this->assertEquals(400, $result->getWidth()); $this->assertEquals(300, $result->getHeight()); $size = new Size(800, 600); $resizer = new Resizer(); - $resizer->width(400); + $resizer->toWidth(400); $result = $resizer->scaleDown($size); $this->assertEquals(400, $result->getWidth()); $this->assertEquals(300, $result->getHeight()); $size = new Size(800, 600); $resizer = new Resizer(); - $resizer->height(300); + $resizer->toHeight(300); $result = $resizer->scaleDown($size); $this->assertEquals(400, $result->getWidth()); $this->assertEquals(300, $result->getHeight()); $size = new Size(800, 600); $resizer = new Resizer(); - $resizer->width(1000); + $resizer->toWidth(1000); $result = $resizer->scaleDown($size); $this->assertEquals(800, $result->getWidth()); $this->assertEquals(600, $result->getHeight()); $size = new Size(800, 600); $resizer = new Resizer(); - $resizer->height(1000); + $resizer->toHeight(1000); $result = $resizer->scaleDown($size); $this->assertEquals(800, $result->getWidth()); $this->assertEquals(600, $result->getHeight()); $size = new Size(800, 600); $resizer = new Resizer(); - $resizer->width(100); + $resizer->toWidth(100); $result = $resizer->scaleDown($size); $this->assertEquals(100, $result->getWidth()); $this->assertEquals(75, $result->getHeight()); $size = new Size(800, 600); $resizer = new Resizer(); - $resizer->width(300); - $resizer->height(200); + $resizer->toWidth(300); + $resizer->toHeight(200); $result = $resizer->scaleDown($size); $this->assertEquals(267, $result->getWidth()); $this->assertEquals(200, $result->getHeight()); $size = new Size(600, 800); $resizer = new Resizer(); - $resizer->width(300); - $resizer->height(300); + $resizer->toWidth(300); + $resizer->toHeight(300); $result = $resizer->scaleDown($size); $this->assertEquals(225, $result->getWidth()); $this->assertEquals(300, $result->getHeight()); $size = new Size(800, 600); $resizer = new Resizer(); - $resizer->width(400); - $resizer->height(10); + $resizer->toWidth(400); + $resizer->toHeight(10); $result = $resizer->scaleDown($size); $this->assertEquals(13, $result->getWidth()); $this->assertEquals(10, $result->getHeight()); diff --git a/tests/Geometry/SizeTest.php b/tests/Geometry/SizeTest.php index 0e0622a0..6ae5f22c 100644 --- a/tests/Geometry/SizeTest.php +++ b/tests/Geometry/SizeTest.php @@ -261,9 +261,7 @@ class SizeTest extends TestCase $this->assertInstanceOf(Size::class, $result); $size = new Size(300, 200); - $result = $size->resize(function ($resizer) { - $resizer->toWidth(100); - }); + $result = $size->resize(height: 100); $this->assertInstanceOf(Size::class, $result); } @@ -274,9 +272,7 @@ class SizeTest extends TestCase $this->assertInstanceOf(Size::class, $result); $size = new Size(300, 200); - $result = $size->resizeDown(function ($resizer) { - $resizer->toWidth(100); - }); + $result = $size->resizeDown(height: 100); $this->assertInstanceOf(Size::class, $result); } @@ -287,9 +283,7 @@ class SizeTest extends TestCase $this->assertInstanceOf(Size::class, $result); $size = new Size(300, 200); - $result = $size->scale(function ($resizer) { - $resizer->toWidth(100); - }); + $result = $size->scale(height: 100); $this->assertInstanceOf(Size::class, $result); } @@ -300,9 +294,7 @@ class SizeTest extends TestCase $this->assertInstanceOf(Size::class, $result); $size = new Size(300, 200); - $result = $size->scaleDown(function ($resizer) { - $resizer->toWidth(100); - }); + $result = $size->scaleDown(height: 100); $this->assertInstanceOf(Size::class, $result); } @@ -311,12 +303,6 @@ class SizeTest extends TestCase $size = new Size(300, 200); $result = $size->cover(120, 150); $this->assertInstanceOf(Size::class, $result); - - $size = new Size(300, 200); - $result = $size->cover(function ($resizer) { - $resizer->toWidth(100); - }); - $this->assertInstanceOf(Size::class, $result); } public function testContain(): void @@ -326,11 +312,5 @@ class SizeTest extends TestCase $this->assertInstanceOf(Size::class, $result); $this->assertEquals(600, $result->getWidth()); $this->assertEquals(600, $result->getHeight()); - - $size = new Size(300, 200); - $result = $size->contain(function ($resizer) { - $resizer->toWidth(100); - }); - $this->assertInstanceOf(Size::class, $result); } } diff --git a/tests/ImageManagerTest.php b/tests/ImageManagerTest.php index 2bdd2c6e..d05fb12e 100644 --- a/tests/ImageManagerTest.php +++ b/tests/ImageManagerTest.php @@ -9,37 +9,34 @@ class ImageManagerTest extends TestCase { public function testConstructor() { - $manager = new ImageManager(['foo' => 'bar']); + $manager = new ImageManager('foo'); $this->assertInstanceOf(ImageManager::class, $manager); - $this->assertEquals('gd', $manager->getConfig('driver')); - $this->assertEquals('bar', $manager->getConfig('foo')); - } - - public function testConfigure() - { - $manager = new ImageManager(['foo' => 'bar']); - $manager->configure(['foo' => 'baz', 'driver' => 'foo']); - $this->assertEquals('foo', $manager->getConfig('driver')); - $this->assertEquals('baz', $manager->getConfig('foo')); - } - - public function testGetConfig() - { - $manager = new ImageManager(['foo' => 'bar']); - $this->assertEquals('gd', $manager->getConfig('driver')); - $this->assertEquals('bar', $manager->getConfig('foo')); } public function testCreateGd() { - $manager = new ImageManager(['driver' => 'gd']); + $manager = new ImageManager('gd'); $image = $manager->create(5, 4); $this->assertInstanceOf(ImageInterface::class, $image); } public function testMakeGd() { - $manager = new ImageManager(['driver' => 'gd']); + $manager = new ImageManager('gd'); + $image = $manager->make(__DIR__ . '/images/red.gif'); + $this->assertInstanceOf(ImageInterface::class, $image); + } + + public function testCreateImagick() + { + $manager = new ImageManager('imagick'); + $image = $manager->create(5, 4); + $this->assertInstanceOf(ImageInterface::class, $image); + } + + public function testMakeImagick() + { + $manager = new ImageManager('imagick'); $image = $manager->make(__DIR__ . '/images/red.gif'); $this->assertInstanceOf(ImageInterface::class, $image); } diff --git a/tests/TestCase.php b/tests/TestCase.php index f229c084..d9b53621 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -4,14 +4,20 @@ namespace Intervention\Image\Tests; use Intervention\Image\Interfaces\ColorInterface; use PHPUnit\Framework\TestCase as PHPUnitTestCase; +use Mockery\Adapter\Phpunit\MockeryTestCase; -abstract class TestCase extends PHPUnitTestCase +abstract class TestCase extends MockeryTestCase { protected function assertColor($r, $g, $b, $a, ColorInterface $color) { - $this->assertEquals($r, $color->getRgbRed()); - $this->assertEquals($g, $color->getRgbGreen()); - $this->assertEquals($b, $color->getRgbBlue()); - $this->assertEquals($a, $color->getOpacity()); + $this->assertEquals($r, $color->red()); + $this->assertEquals($g, $color->green()); + $this->assertEquals($b, $color->blue()); + $this->assertEquals($a, $color->alpha()); + } + + protected function assertTransparency(ColorInterface $color) + { + $this->assertEquals(0, $color->alpha()); } } diff --git a/tests/images/blocks.png b/tests/images/blocks.png new file mode 100644 index 0000000000000000000000000000000000000000..d57fabe3e4bb1b339bc7a6263082c9886614acff GIT binary patch literal 467 zcmeAS@N?(olHy`uVBq!ia0y~yU}|7sV0^&D3>5L$Z&D7V7>k44ofvPP)Tsw@I14-? ziy0WWg+Z8+Vb&Z8pde3xPlzi6!+#(XNC2q?LBfxLY?hKBzhI!i|H~VSrT`5J@N{tu zshIQjoFgxT0uRfEnMYLA91qv(MPAZ+;d8aKw7zL5f!j^nUmay(9XQ}D&ts^W|2#3p P0~GL{u6{1-oD!M Date: Sun, 19 Dec 2021 16:26:23 +0100 Subject: [PATCH 081/476] Added License file --- LICENSE | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..ca3cb7c2 --- /dev/null +++ b/LICENSE @@ -0,0 +1,9 @@ +The MIT License (MIT) + +Copyright (c) 2021 Oliver Vogel + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file From d9aafc2b2997ec3cf8ff9d5358438f9a9d90110d Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 19 Dec 2021 16:34:23 +0100 Subject: [PATCH 082/476] Github workflows "Run tests" --- .github/workflows/run-tests.yml | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 .github/workflows/run-tests.yml diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml new file mode 100644 index 00000000..379dfa62 --- /dev/null +++ b/.github/workflows/run-tests.yml @@ -0,0 +1,24 @@ +name: run-tests + +on: [push] + +jobs: + run: + runs-on: ubuntu-latest + strategy: + matrix: + php-versions: ['8.0'] + name: Testing on PHP ${{ matrix.php-versions }} + steps: + - name: checkout + uses: actions/checkout@v2 + - name: setup + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php-versions }} + extensions: imagick,gd,mbstring,bcmath + tools: phpunit,composer + - name: install deps + run: composer install -o -q + - name: run phpunit + run: vendor/bin/phpunit \ No newline at end of file From c59d0100d732555a76356584077592ae8c4db6cf Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 19 Dec 2021 16:37:08 +0100 Subject: [PATCH 083/476] CI --- .github/workflows/run-tests.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 379dfa62..41257898 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -10,15 +10,15 @@ jobs: php-versions: ['8.0'] name: Testing on PHP ${{ matrix.php-versions }} steps: - - name: checkout + - name: 'Checkout Project' uses: actions/checkout@v2 - - name: setup + - name: 'Setup Environment' uses: shivammathur/setup-php@v2 with: php-version: ${{ matrix.php-versions }} extensions: imagick,gd,mbstring,bcmath tools: phpunit,composer - - name: install deps + - name: 'Install Dependencies' run: composer install -o -q - - name: run phpunit + - name: 'Run PHPUnit' run: vendor/bin/phpunit \ No newline at end of file From 6752d3019e877e80948fde4b00bd7d664fc8e42b Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 19 Dec 2021 16:42:07 +0100 Subject: [PATCH 084/476] CI --- .github/workflows/run-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 41257898..ed1ea7a7 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -4,7 +4,7 @@ on: [push] jobs: run: - runs-on: ubuntu-latest + runs-on: macos-latest strategy: matrix: php-versions: ['8.0'] From 9f9e9e16ab830dee1800a43b63c111fdf4dfa0eb Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 19 Dec 2021 16:45:00 +0100 Subject: [PATCH 085/476] Revert "CI" This reverts commit 6752d3019e877e80948fde4b00bd7d664fc8e42b. --- .github/workflows/run-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index ed1ea7a7..41257898 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -4,7 +4,7 @@ on: [push] jobs: run: - runs-on: macos-latest + runs-on: ubuntu-latest strategy: matrix: php-versions: ['8.0'] From c070d7d7af7a29840dd00a3170de50310afcf08a Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 19 Dec 2021 17:23:02 +0100 Subject: [PATCH 086/476] Test --- tests/Drivers/Gd/Modifiers/ResizeModifierTest.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/Drivers/Gd/Modifiers/ResizeModifierTest.php b/tests/Drivers/Gd/Modifiers/ResizeModifierTest.php index b1ebe570..7e7d297d 100644 --- a/tests/Drivers/Gd/Modifiers/ResizeModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/ResizeModifierTest.php @@ -23,6 +23,8 @@ class ResizeModifierTest extends TestCase $this->assertColor(255, 0, 0, 1, $image->pickColor(150, 70)); $this->assertColor(0, 255, 0, 1, $image->pickColor(125, 70)); $this->assertColor(0, 0, 255, 1, $image->pickColor(130, 54)); - $this->assertTransparency($image->pickColor(150, 45)); + $transparent = $image->pickColor(150, 45); + $this->assertTransparency($transparent); + $this->assertEquals(2130706432, $transparent->toInt()); } } From e6ef61ee02fae706f037cad34afef8d611519b8f Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 19 Dec 2021 17:28:08 +0100 Subject: [PATCH 087/476] Test --- .github/workflows/run-tests.yml | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 41257898..07012141 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -10,13 +10,29 @@ jobs: php-versions: ['8.0'] name: Testing on PHP ${{ matrix.php-versions }} steps: + - name: Run PHP code + shell: php {0} + run: | + > 24) & 0xFF; + $r = ($value >> 16) & 0xFF; + $g = ($value >> 8) & 0xFF; + $b = $value & 0xFF; + $a1 = (float) round(1 - $a / 127, 2); + $a2 = round(1 - $a / 127, 2); + $a3 = 1 - $a / 127; + var_dump($a); + var_dump($a1); + var_dump($a2); + var_dump($a3); - name: 'Checkout Project' uses: actions/checkout@v2 - name: 'Setup Environment' uses: shivammathur/setup-php@v2 with: php-version: ${{ matrix.php-versions }} - extensions: imagick,gd,mbstring,bcmath + extensions: imagick,gd tools: phpunit,composer - name: 'Install Dependencies' run: composer install -o -q From b3e41c8c1b3341dcb3572d3a7fee2cc969a3edfc Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 19 Dec 2021 17:29:13 +0100 Subject: [PATCH 088/476] Test --- .github/workflows/run-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 07012141..65c1c504 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -14,7 +14,7 @@ jobs: shell: php {0} run: | > 24) & 0xFF; $r = ($value >> 16) & 0xFF; $g = ($value >> 8) & 0xFF; From 692b140528f09b951f9409b032896e5978f88b4c Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 19 Dec 2021 17:31:54 +0100 Subject: [PATCH 089/476] Test --- tests/Drivers/Gd/Modifiers/ResizeModifierTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Drivers/Gd/Modifiers/ResizeModifierTest.php b/tests/Drivers/Gd/Modifiers/ResizeModifierTest.php index 7e7d297d..d8b03872 100644 --- a/tests/Drivers/Gd/Modifiers/ResizeModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/ResizeModifierTest.php @@ -24,7 +24,7 @@ class ResizeModifierTest extends TestCase $this->assertColor(0, 255, 0, 1, $image->pickColor(125, 70)); $this->assertColor(0, 0, 255, 1, $image->pickColor(130, 54)); $transparent = $image->pickColor(150, 45); - $this->assertTransparency($transparent); $this->assertEquals(2130706432, $transparent->toInt()); + $this->assertTransparency($transparent); } } From b3a041a3ee5e911e6327f050cd207c36bfb002e7 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 19 Dec 2021 17:37:16 +0100 Subject: [PATCH 090/476] Test --- .github/workflows/run-tests.yml | 16 ---------------- .../Drivers/Gd/Modifiers/ResizeModifierTest.php | 2 +- 2 files changed, 1 insertion(+), 17 deletions(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 65c1c504..5d7ae107 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -10,22 +10,6 @@ jobs: php-versions: ['8.0'] name: Testing on PHP ${{ matrix.php-versions }} steps: - - name: Run PHP code - shell: php {0} - run: | - > 24) & 0xFF; - $r = ($value >> 16) & 0xFF; - $g = ($value >> 8) & 0xFF; - $b = $value & 0xFF; - $a1 = (float) round(1 - $a / 127, 2); - $a2 = round(1 - $a / 127, 2); - $a3 = 1 - $a / 127; - var_dump($a); - var_dump($a1); - var_dump($a2); - var_dump($a3); - name: 'Checkout Project' uses: actions/checkout@v2 - name: 'Setup Environment' diff --git a/tests/Drivers/Gd/Modifiers/ResizeModifierTest.php b/tests/Drivers/Gd/Modifiers/ResizeModifierTest.php index d8b03872..ad4a4a9c 100644 --- a/tests/Drivers/Gd/Modifiers/ResizeModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/ResizeModifierTest.php @@ -23,7 +23,7 @@ class ResizeModifierTest extends TestCase $this->assertColor(255, 0, 0, 1, $image->pickColor(150, 70)); $this->assertColor(0, 255, 0, 1, $image->pickColor(125, 70)); $this->assertColor(0, 0, 255, 1, $image->pickColor(130, 54)); - $transparent = $image->pickColor(150, 45); + $transparent = $image->pickColor(170, 30); $this->assertEquals(2130706432, $transparent->toInt()); $this->assertTransparency($transparent); } From f173895ac23fc04ebc783cbf391543c0a22aa75a Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Mon, 20 Dec 2021 19:45:51 +0100 Subject: [PATCH 091/476] Replaced test image --- tests/images/blocks.png | Bin 467 -> 4338 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/tests/images/blocks.png b/tests/images/blocks.png index d57fabe3e4bb1b339bc7a6263082c9886614acff..69ffc0f786162f5f35a07301d6187f13d868f216 100644 GIT binary patch literal 4338 zcmeAS@N?(olHy`uVBq!ia0y~yU}|7sV0^&A1{5*9c;^X_Vk{1FcVfJGQl}os;Vkfo zEM{Qf76xHPhFNnY7#IYmd%8G=RLpsM?P4dZp#by2llCw7a`@FdJqygzYB{)X_j~4s z`^jmEubEjJ*o?WOgW<=ij<-oY$;F&|$*tr;Z)5Nyf1u$e7Y~Jv-k;$QXX6ssh z5eBIJj$1Hzy85}Sb4q9e01?_Lg8%>k literal 467 zcmeAS@N?(olHy`uVBq!ia0y~yU}|7sV0^&D3>5L$Z&D7V7>k44ofvPP)Tsw@I14-? ziy0WWg+Z8+Vb&Z8pde3xPlzi6!+#(XNC2q?LBfxLY?hKBzhI!i|H~VSrT`5J@N{tu zshIQjoFgxT0uRfEnMYLA91qv(MPAZ+;d8aKw7zL5f!j^nUmay(9XQ}D&ts^W|2#3p P0~GL{u6{1-oD!M Date: Mon, 20 Dec 2021 19:47:55 +0100 Subject: [PATCH 092/476] Revert "Replaced test image" This reverts commit f173895ac23fc04ebc783cbf391543c0a22aa75a. --- tests/images/blocks.png | Bin 4338 -> 467 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/tests/images/blocks.png b/tests/images/blocks.png index 69ffc0f786162f5f35a07301d6187f13d868f216..d57fabe3e4bb1b339bc7a6263082c9886614acff 100644 GIT binary patch literal 467 zcmeAS@N?(olHy`uVBq!ia0y~yU}|7sV0^&D3>5L$Z&D7V7>k44ofvPP)Tsw@I14-? ziy0WWg+Z8+Vb&Z8pde3xPlzi6!+#(XNC2q?LBfxLY?hKBzhI!i|H~VSrT`5J@N{tu zshIQjoFgxT0uRfEnMYLA91qv(MPAZ+;d8aKw7zL5f!j^nUmay(9XQ}D&ts^W|2#3p P0~GL{u6{1-oD!MgW<=ij<-oY$;F&|$*tr;Z)5Nyf1u$e7Y~Jv-k;$QXX6ssh z5eBIJj$1Hzy85}Sb4q9e01?_Lg8%>k From 43108ce1ce4fd12037b5e590feb15744d8fef7cb Mon Sep 17 00:00:00 2001 From: Gwendolen Lynch Date: Wed, 22 Dec 2021 11:28:44 +0100 Subject: [PATCH 093/476] GD & ImageMagick tests require extension to run --- tests/Drivers/Gd/ColorTest.php | 3 +++ tests/Drivers/Gd/Decoders/ArrayColorDecoderTest.php | 3 +++ tests/Drivers/Gd/Decoders/Base64ImageDecoderTest.php | 3 +++ tests/Drivers/Gd/Decoders/BinaryImageDecoderTest.php | 3 +++ tests/Drivers/Gd/Decoders/DataUriImageDecoderTest.php | 3 +++ tests/Drivers/Gd/Decoders/FilePathImageDecoderTest.php | 3 +++ tests/Drivers/Gd/Decoders/HexColorDecoderTest.php | 5 ++++- tests/Drivers/Gd/Decoders/ImageObjectDecoderTest.php | 3 +++ tests/Drivers/Gd/Decoders/RgbStringColorDecoderTest.php | 3 +++ tests/Drivers/Gd/Decoders/TransparentColorDecoderTest.php | 3 +++ tests/Drivers/Gd/Encoders/GifEncoderTest.php | 3 +++ tests/Drivers/Gd/Encoders/JpegEncoderTest.php | 3 +++ tests/Drivers/Gd/Encoders/PngEncoderTest.php | 5 ++++- tests/Drivers/Gd/FrameTest.php | 3 +++ tests/Drivers/Gd/ImageFactoryTest.php | 3 +++ tests/Drivers/Gd/ImageTest.php | 3 +++ tests/Drivers/Gd/InputHandlerTest.php | 6 +++--- tests/Drivers/Gd/Modifiers/BlurModifierTest.php | 4 +++- tests/Drivers/Gd/Modifiers/BrightnessModifierTest.php | 4 +++- tests/Drivers/Gd/Modifiers/ContrastModifierTest.php | 4 +++- tests/Drivers/Gd/Modifiers/DestroyModifierTest.php | 4 +++- tests/Drivers/Gd/Modifiers/FillModifierTest.php | 3 +++ tests/Drivers/Gd/Modifiers/FitModifierTest.php | 6 +++--- tests/Drivers/Gd/Modifiers/GreyscaleModifierTest.php | 6 ++++-- tests/Drivers/Gd/Modifiers/InvertModifierTest.php | 6 ++++-- tests/Drivers/Gd/Modifiers/PixelateModifierTest.php | 4 +++- tests/Drivers/Gd/Modifiers/PlaceModifierTest.php | 5 +++-- tests/Drivers/Gd/Modifiers/ResizeModifierTest.php | 5 +++-- tests/Drivers/Gd/Modifiers/SharpenModifierTest.php | 6 ++++-- tests/Drivers/Imagick/ColorTest.php | 3 +++ tests/Drivers/Imagick/Decoders/ArrayColorDecoderTest.php | 3 +++ tests/Drivers/Imagick/Decoders/ImageObjectDecoderTest.php | 3 +++ .../Drivers/Imagick/Decoders/RgbStringColorDecoderTest.php | 3 +++ .../Imagick/Decoders/TransparentColorDecoderTest.php | 3 +++ tests/Drivers/Imagick/Encoders/GifEncoderTest.php | 3 +++ tests/Drivers/Imagick/Encoders/JpegEncoderTest.php | 3 +++ tests/Drivers/Imagick/FrameTest.php | 3 +++ tests/Drivers/Imagick/ImageFactoryTest.php | 3 +++ tests/Drivers/Imagick/ImageTest.php | 3 +++ tests/Drivers/Imagick/InputHandlerTest.php | 6 +++--- tests/Drivers/Imagick/Modifiers/BlurModifierTest.php | 6 ++++-- tests/Drivers/Imagick/Modifiers/BrightnessModifierTest.php | 6 ++++-- tests/Drivers/Imagick/Modifiers/ContrastModifierTest.php | 6 ++++-- tests/Drivers/Imagick/Modifiers/DestroyModifierTest.php | 6 ++++-- tests/Drivers/Imagick/Modifiers/FillModifierTest.php | 4 +++- tests/Drivers/Imagick/Modifiers/FitModifierTest.php | 6 +++--- tests/Drivers/Imagick/Modifiers/GreyscaleModifierTest.php | 6 ++++-- tests/Drivers/Imagick/Modifiers/InvertModifierTest.php | 6 ++++-- tests/Drivers/Imagick/Modifiers/PixelateModifierTest.php | 4 +++- tests/Drivers/Imagick/Modifiers/PlaceModifierTest.php | 5 +++-- tests/Drivers/Imagick/Modifiers/ResizeModifierTest.php | 7 ++++--- tests/Drivers/Imagick/Modifiers/SharpenModifierTest.php | 6 ++++-- tests/ImageManagerTest.php | 4 ++++ 53 files changed, 173 insertions(+), 50 deletions(-) diff --git a/tests/Drivers/Gd/ColorTest.php b/tests/Drivers/Gd/ColorTest.php index 989fad85..767fbccc 100644 --- a/tests/Drivers/Gd/ColorTest.php +++ b/tests/Drivers/Gd/ColorTest.php @@ -5,6 +5,9 @@ namespace Intervention\Image\Tests\Drivers\Gd; use Intervention\Image\Drivers\Gd\Color; use Intervention\Image\Tests\TestCase; +/** + * @requires extension gd + */ class ColorTest extends TestCase { protected function getTestColor($r = 0, $g = 0, $b = 0, $a = 0): Color diff --git a/tests/Drivers/Gd/Decoders/ArrayColorDecoderTest.php b/tests/Drivers/Gd/Decoders/ArrayColorDecoderTest.php index 585d864a..4750f87d 100644 --- a/tests/Drivers/Gd/Decoders/ArrayColorDecoderTest.php +++ b/tests/Drivers/Gd/Decoders/ArrayColorDecoderTest.php @@ -6,6 +6,9 @@ use Intervention\Image\Drivers\Gd\Color; use Intervention\Image\Drivers\Gd\Decoders\ArrayColorDecoder; use Intervention\Image\Tests\TestCase; +/** + * @requires extension gd + */ class ArrayColorDecoderTest extends TestCase { public function testDecode(): void diff --git a/tests/Drivers/Gd/Decoders/Base64ImageDecoderTest.php b/tests/Drivers/Gd/Decoders/Base64ImageDecoderTest.php index ae92fed2..dcddfa53 100644 --- a/tests/Drivers/Gd/Decoders/Base64ImageDecoderTest.php +++ b/tests/Drivers/Gd/Decoders/Base64ImageDecoderTest.php @@ -7,6 +7,9 @@ use Intervention\Image\Drivers\Gd\Image; use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateGdTestImage; +/** + * @requires extension gd + */ class Base64ImageDecoderTest extends TestCase { use CanCreateGdTestImage; diff --git a/tests/Drivers/Gd/Decoders/BinaryImageDecoderTest.php b/tests/Drivers/Gd/Decoders/BinaryImageDecoderTest.php index 42b3b20e..b4e63620 100644 --- a/tests/Drivers/Gd/Decoders/BinaryImageDecoderTest.php +++ b/tests/Drivers/Gd/Decoders/BinaryImageDecoderTest.php @@ -6,6 +6,9 @@ use Intervention\Image\Drivers\Gd\Decoders\BinaryImageDecoder; use Intervention\Image\Drivers\Gd\Image; use Intervention\Image\Tests\TestCase; +/** + * @requires extension gd + */ class BinaryImageDecoderTest extends TestCase { public function testDecodePng(): void diff --git a/tests/Drivers/Gd/Decoders/DataUriImageDecoderTest.php b/tests/Drivers/Gd/Decoders/DataUriImageDecoderTest.php index fccc146d..cc31e4af 100644 --- a/tests/Drivers/Gd/Decoders/DataUriImageDecoderTest.php +++ b/tests/Drivers/Gd/Decoders/DataUriImageDecoderTest.php @@ -7,6 +7,9 @@ use Intervention\Image\Drivers\Gd\Image; use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateGdTestImage; +/** + * @requires extension gd + */ class DataUriImageDecoderTest extends TestCase { use CanCreateGdTestImage; diff --git a/tests/Drivers/Gd/Decoders/FilePathImageDecoderTest.php b/tests/Drivers/Gd/Decoders/FilePathImageDecoderTest.php index 840063b2..e0f86b66 100644 --- a/tests/Drivers/Gd/Decoders/FilePathImageDecoderTest.php +++ b/tests/Drivers/Gd/Decoders/FilePathImageDecoderTest.php @@ -7,6 +7,9 @@ use Intervention\Image\Drivers\Gd\Image; use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateGdTestImage; +/** + * @requires extension gd + */ class FilePathImageDecoderTest extends TestCase { use CanCreateGdTestImage; diff --git a/tests/Drivers/Gd/Decoders/HexColorDecoderTest.php b/tests/Drivers/Gd/Decoders/HexColorDecoderTest.php index 37732117..c70a6db6 100644 --- a/tests/Drivers/Gd/Decoders/HexColorDecoderTest.php +++ b/tests/Drivers/Gd/Decoders/HexColorDecoderTest.php @@ -2,10 +2,13 @@ namespace Intervention\Image\Tests\Drivers\Gd\Decoders; -use Intervention\Image\Drivers\Gd\Decoders\HexColorDecoder; use Intervention\Image\Drivers\Gd\Color; +use Intervention\Image\Drivers\Gd\Decoders\HexColorDecoder; use Intervention\Image\Tests\TestCase; +/** + * @requires extension gd + */ class HexColorDecoderTest extends TestCase { public function testDecode(): void diff --git a/tests/Drivers/Gd/Decoders/ImageObjectDecoderTest.php b/tests/Drivers/Gd/Decoders/ImageObjectDecoderTest.php index 573aa4a7..4ae717fa 100644 --- a/tests/Drivers/Gd/Decoders/ImageObjectDecoderTest.php +++ b/tests/Drivers/Gd/Decoders/ImageObjectDecoderTest.php @@ -7,6 +7,9 @@ use Intervention\Image\Drivers\Gd\Image; use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateGdTestImage; +/** + * @requires extension gd + */ class ImageObjectDecoderTest extends TestCase { use CanCreateGdTestImage; diff --git a/tests/Drivers/Gd/Decoders/RgbStringColorDecoderTest.php b/tests/Drivers/Gd/Decoders/RgbStringColorDecoderTest.php index 19c2a26f..db1a5bb9 100644 --- a/tests/Drivers/Gd/Decoders/RgbStringColorDecoderTest.php +++ b/tests/Drivers/Gd/Decoders/RgbStringColorDecoderTest.php @@ -6,6 +6,9 @@ use Intervention\Image\Drivers\Gd\Color; use Intervention\Image\Drivers\Gd\Decoders\RgbStringColorDecoder; use Intervention\Image\Tests\TestCase; +/** + * @requires extension gd + */ class RgbStringColorDecoderTest extends TestCase { public function testDecodeRgb(): void diff --git a/tests/Drivers/Gd/Decoders/TransparentColorDecoderTest.php b/tests/Drivers/Gd/Decoders/TransparentColorDecoderTest.php index d0864d48..29777036 100644 --- a/tests/Drivers/Gd/Decoders/TransparentColorDecoderTest.php +++ b/tests/Drivers/Gd/Decoders/TransparentColorDecoderTest.php @@ -6,6 +6,9 @@ use Intervention\Image\Drivers\Gd\Color; use Intervention\Image\Drivers\Gd\Decoders\TransparentColorDecoder; use Intervention\Image\Tests\TestCase; +/** + * @requires extension gd + */ class TransparentColorDecoderTest extends TestCase { public function testDecode(): void diff --git a/tests/Drivers/Gd/Encoders/GifEncoderTest.php b/tests/Drivers/Gd/Encoders/GifEncoderTest.php index e668ce22..4a08a8dd 100644 --- a/tests/Drivers/Gd/Encoders/GifEncoderTest.php +++ b/tests/Drivers/Gd/Encoders/GifEncoderTest.php @@ -10,6 +10,9 @@ use Intervention\Image\Tests\TestCase; use Intervention\MimeSniffer\MimeSniffer; use Intervention\MimeSniffer\Types\ImageGif; +/** + * @requires extension gd + */ class GifEncoderTest extends TestCase { protected function getTestImage(): Image diff --git a/tests/Drivers/Gd/Encoders/JpegEncoderTest.php b/tests/Drivers/Gd/Encoders/JpegEncoderTest.php index 231f0aab..7e46e48d 100644 --- a/tests/Drivers/Gd/Encoders/JpegEncoderTest.php +++ b/tests/Drivers/Gd/Encoders/JpegEncoderTest.php @@ -10,6 +10,9 @@ use Intervention\Image\Tests\TestCase; use Intervention\MimeSniffer\MimeSniffer; use Intervention\MimeSniffer\Types\ImageJpeg; +/** + * @requires extension gd + */ class JpegEncoderTest extends TestCase { protected function getTestImage(): Image diff --git a/tests/Drivers/Gd/Encoders/PngEncoderTest.php b/tests/Drivers/Gd/Encoders/PngEncoderTest.php index 065aa4d6..97991d0e 100644 --- a/tests/Drivers/Gd/Encoders/PngEncoderTest.php +++ b/tests/Drivers/Gd/Encoders/PngEncoderTest.php @@ -10,6 +10,9 @@ use Intervention\Image\Tests\TestCase; use Intervention\MimeSniffer\MimeSniffer; use Intervention\MimeSniffer\Types\ImagePng; +/** + * @requires extension gd + */ class PngEncoderTest extends TestCase { protected function getTestImage(): Image @@ -26,4 +29,4 @@ class PngEncoderTest extends TestCase $result = $encoder->encode($image); $this->assertTrue(MimeSniffer::createFromString($result)->matches(new ImagePng)); } -} \ No newline at end of file +} diff --git a/tests/Drivers/Gd/FrameTest.php b/tests/Drivers/Gd/FrameTest.php index 2497139d..ff862d4b 100644 --- a/tests/Drivers/Gd/FrameTest.php +++ b/tests/Drivers/Gd/FrameTest.php @@ -8,6 +8,9 @@ use Intervention\Image\Drivers\Gd\Image; use Intervention\Image\Geometry\Size; use Intervention\Image\Tests\TestCase; +/** + * @requires extension gd + */ class FrameTest extends TestCase { protected function getTestFrame(): Frame diff --git a/tests/Drivers/Gd/ImageFactoryTest.php b/tests/Drivers/Gd/ImageFactoryTest.php index 9f319741..f2283fce 100644 --- a/tests/Drivers/Gd/ImageFactoryTest.php +++ b/tests/Drivers/Gd/ImageFactoryTest.php @@ -7,6 +7,9 @@ use Intervention\Image\Drivers\Gd\Image; use Intervention\Image\Drivers\Gd\ImageFactory; use Intervention\Image\Tests\TestCase; +/** + * @requires extension gd + */ class ImageFactoryTest extends TestCase { public function testNewImage(): void diff --git a/tests/Drivers/Gd/ImageTest.php b/tests/Drivers/Gd/ImageTest.php index 1916587b..74ea010c 100644 --- a/tests/Drivers/Gd/ImageTest.php +++ b/tests/Drivers/Gd/ImageTest.php @@ -9,6 +9,9 @@ use Intervention\Image\Drivers\Gd\Image; use Intervention\Image\Geometry\Size; use Intervention\Image\Tests\TestCase; +/** + * @requires extension gd + */ class ImageTest extends TestCase { protected Image $image; diff --git a/tests/Drivers/Gd/InputHandlerTest.php b/tests/Drivers/Gd/InputHandlerTest.php index 22fa5dc8..e137b283 100644 --- a/tests/Drivers/Gd/InputHandlerTest.php +++ b/tests/Drivers/Gd/InputHandlerTest.php @@ -2,15 +2,15 @@ namespace Intervention\Image\Tests\Drivers\Gd; -use Intervention\Image\Collection; use Intervention\Image\Drivers\Gd\Color; -use Intervention\Image\Drivers\Gd\Frame; use Intervention\Image\Drivers\Gd\Image; use Intervention\Image\Drivers\Gd\InputHandler; use Intervention\Image\Exceptions\DecoderException; -use Intervention\Image\Geometry\Size; use Intervention\Image\Tests\TestCase; +/** + * @requires extension gd + */ class InputHandlerTest extends TestCase { public function testHandleEmptyString(): void diff --git a/tests/Drivers/Gd/Modifiers/BlurModifierTest.php b/tests/Drivers/Gd/Modifiers/BlurModifierTest.php index 9062eda4..b1b70855 100644 --- a/tests/Drivers/Gd/Modifiers/BlurModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/BlurModifierTest.php @@ -2,11 +2,13 @@ namespace Intervention\Image\Tests\Drivers\Gd\Modifiers; -use Intervention\Image\Drivers\Gd\Image; use Intervention\Image\Drivers\Gd\Modifiers\BlurModifier; use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateGdTestImage; +/** + * @requires extension gd + */ class BlurModifierTest extends TestCase { use CanCreateGdTestImage; diff --git a/tests/Drivers/Gd/Modifiers/BrightnessModifierTest.php b/tests/Drivers/Gd/Modifiers/BrightnessModifierTest.php index e0e41025..2a80ba6a 100644 --- a/tests/Drivers/Gd/Modifiers/BrightnessModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/BrightnessModifierTest.php @@ -2,11 +2,13 @@ namespace Intervention\Image\Tests\Drivers\Gd\Modifiers; -use Intervention\Image\Drivers\Gd\Image; use Intervention\Image\Drivers\Gd\Modifiers\BrightnessModifier; use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateGdTestImage; +/** + * @requires extension gd + */ class BrightnessModifierTest extends TestCase { use CanCreateGdTestImage; diff --git a/tests/Drivers/Gd/Modifiers/ContrastModifierTest.php b/tests/Drivers/Gd/Modifiers/ContrastModifierTest.php index 7ec44484..e95a8192 100644 --- a/tests/Drivers/Gd/Modifiers/ContrastModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/ContrastModifierTest.php @@ -2,11 +2,13 @@ namespace Intervention\Image\Tests\Drivers\Gd\Modifiers; -use Intervention\Image\Drivers\Gd\Image; use Intervention\Image\Drivers\Gd\Modifiers\ContrastModifier; use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateGdTestImage; +/** + * @requires extension gd + */ class ContrastModifierTest extends TestCase { use CanCreateGdTestImage; diff --git a/tests/Drivers/Gd/Modifiers/DestroyModifierTest.php b/tests/Drivers/Gd/Modifiers/DestroyModifierTest.php index 52b48fc1..753f45aa 100644 --- a/tests/Drivers/Gd/Modifiers/DestroyModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/DestroyModifierTest.php @@ -3,11 +3,13 @@ namespace Intervention\Image\Tests\Drivers\Gd\Modifiers; use GdImage; -use Intervention\Image\Drivers\Gd\Image; use Intervention\Image\Drivers\Gd\Modifiers\DestroyModifier; use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateGdTestImage; +/** + * @requires extension gd + */ class DestroyModifierTest extends TestCase { use CanCreateGdTestImage; diff --git a/tests/Drivers/Gd/Modifiers/FillModifierTest.php b/tests/Drivers/Gd/Modifiers/FillModifierTest.php index a1f8c049..d3fb6205 100644 --- a/tests/Drivers/Gd/Modifiers/FillModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/FillModifierTest.php @@ -8,6 +8,9 @@ use Intervention\Image\Geometry\Point; use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateGdTestImage; +/** + * @requires extension gd + */ class FillModifierTest extends TestCase { use CanCreateGdTestImage; diff --git a/tests/Drivers/Gd/Modifiers/FitModifierTest.php b/tests/Drivers/Gd/Modifiers/FitModifierTest.php index 89056042..8e02343b 100644 --- a/tests/Drivers/Gd/Modifiers/FitModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/FitModifierTest.php @@ -2,13 +2,13 @@ namespace Intervention\Image\Tests\Drivers\Gd\Modifiers; -use Intervention\Image\Drivers\Gd\Image; use Intervention\Image\Drivers\Gd\Modifiers\FitModifier; -use Intervention\Image\Geometry\Point; -use Intervention\Image\Geometry\Size; use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateGdTestImage; +/** + * @requires extension gd + */ class FitModifierTest extends TestCase { use CanCreateGdTestImage; diff --git a/tests/Drivers/Gd/Modifiers/GreyscaleModifierTest.php b/tests/Drivers/Gd/Modifiers/GreyscaleModifierTest.php index 9302d460..c2c035f5 100644 --- a/tests/Drivers/Gd/Modifiers/GreyscaleModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/GreyscaleModifierTest.php @@ -2,11 +2,13 @@ namespace Intervention\Image\Tests\Drivers\Gd\Modifiers; -use Intervention\Image\Tests\TestCase; -use Intervention\Image\Drivers\Gd\Image; use Intervention\Image\Drivers\Gd\Modifiers\GreyscaleModifier; +use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateGdTestImage; +/** + * @requires extension gd + */ class GreyscaleModifierTest extends TestCase { use CanCreateGdTestImage; diff --git a/tests/Drivers/Gd/Modifiers/InvertModifierTest.php b/tests/Drivers/Gd/Modifiers/InvertModifierTest.php index 6429f3fb..27542274 100644 --- a/tests/Drivers/Gd/Modifiers/InvertModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/InvertModifierTest.php @@ -2,11 +2,13 @@ namespace Intervention\Image\Tests\Drivers\Gd\Modifiers; -use Intervention\Image\Tests\TestCase; -use Intervention\Image\Drivers\Gd\Image; use Intervention\Image\Drivers\Gd\Modifiers\InvertModifier; +use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateGdTestImage; +/** + * @requires extension gd + */ class InvertModifierTest extends TestCase { use CanCreateGdTestImage; diff --git a/tests/Drivers/Gd/Modifiers/PixelateModifierTest.php b/tests/Drivers/Gd/Modifiers/PixelateModifierTest.php index 0fc4a892..be945804 100644 --- a/tests/Drivers/Gd/Modifiers/PixelateModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/PixelateModifierTest.php @@ -2,11 +2,13 @@ namespace Intervention\Image\Tests\Drivers\Gd\Modifiers; -use Intervention\Image\Drivers\Gd\Image; use Intervention\Image\Drivers\Gd\Modifiers\PixelateModifier; use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateGdTestImage; +/** + * @requires extension gd + */ class PixelateModifierTest extends TestCase { use CanCreateGdTestImage; diff --git a/tests/Drivers/Gd/Modifiers/PlaceModifierTest.php b/tests/Drivers/Gd/Modifiers/PlaceModifierTest.php index bd1d3e0b..16a0f06a 100644 --- a/tests/Drivers/Gd/Modifiers/PlaceModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/PlaceModifierTest.php @@ -2,12 +2,13 @@ namespace Intervention\Image\Tests\Drivers\Gd\Modifiers; -use Intervention\Image\Drivers\Gd\Image; use Intervention\Image\Drivers\Gd\Modifiers\PlaceModifier; -use Intervention\Image\Geometry\Size; use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateGdTestImage; +/** + * @requires extension gd + */ class PlaceModifierTest extends TestCase { use CanCreateGdTestImage; diff --git a/tests/Drivers/Gd/Modifiers/ResizeModifierTest.php b/tests/Drivers/Gd/Modifiers/ResizeModifierTest.php index ad4a4a9c..7094a1e7 100644 --- a/tests/Drivers/Gd/Modifiers/ResizeModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/ResizeModifierTest.php @@ -2,12 +2,13 @@ namespace Intervention\Image\Tests\Drivers\Gd\Modifiers; -use Intervention\Image\Drivers\Gd\Image; use Intervention\Image\Drivers\Gd\Modifiers\ResizeModifier; -use Intervention\Image\Geometry\Size; use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateGdTestImage; +/** + * @requires extension gd + */ class ResizeModifierTest extends TestCase { use CanCreateGdTestImage; diff --git a/tests/Drivers/Gd/Modifiers/SharpenModifierTest.php b/tests/Drivers/Gd/Modifiers/SharpenModifierTest.php index 83d52090..051ad027 100644 --- a/tests/Drivers/Gd/Modifiers/SharpenModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/SharpenModifierTest.php @@ -2,11 +2,13 @@ namespace Intervention\Image\Tests\Drivers\Gd\Modifiers; -use Intervention\Image\Tests\TestCase; -use Intervention\Image\Drivers\Gd\Image; use Intervention\Image\Drivers\Gd\Modifiers\SharpenModifier; +use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateGdTestImage; +/** + * @requires extension gd + */ class SharpenModifierTest extends TestCase { use CanCreateGdTestImage; diff --git a/tests/Drivers/Imagick/ColorTest.php b/tests/Drivers/Imagick/ColorTest.php index bf889f0c..52e8779e 100644 --- a/tests/Drivers/Imagick/ColorTest.php +++ b/tests/Drivers/Imagick/ColorTest.php @@ -6,6 +6,9 @@ use ImagickPixel; use Intervention\Image\Drivers\Imagick\Color; use Intervention\Image\Tests\TestCase; +/** + * @requires extension imagick + */ class ColorTest extends TestCase { protected function getTestColor(int $r = 0, int $g = 0, int $b = 0, float $a = 1): Color diff --git a/tests/Drivers/Imagick/Decoders/ArrayColorDecoderTest.php b/tests/Drivers/Imagick/Decoders/ArrayColorDecoderTest.php index e0e16800..c3c5e17a 100644 --- a/tests/Drivers/Imagick/Decoders/ArrayColorDecoderTest.php +++ b/tests/Drivers/Imagick/Decoders/ArrayColorDecoderTest.php @@ -6,6 +6,9 @@ use Intervention\Image\Drivers\Imagick\Color; use Intervention\Image\Drivers\Imagick\Decoders\ArrayColorDecoder; use Intervention\Image\Tests\TestCase; +/** + * @requires extension imagick + */ class ArrayColorDecoderTest extends TestCase { public function testDecode(): void diff --git a/tests/Drivers/Imagick/Decoders/ImageObjectDecoderTest.php b/tests/Drivers/Imagick/Decoders/ImageObjectDecoderTest.php index 1f3ad8ec..175886ad 100644 --- a/tests/Drivers/Imagick/Decoders/ImageObjectDecoderTest.php +++ b/tests/Drivers/Imagick/Decoders/ImageObjectDecoderTest.php @@ -7,6 +7,9 @@ use Intervention\Image\Drivers\Imagick\Image; use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateImagickTestImage; +/** + * @requires extension imagick + */ class ImageObjectDecoderTest extends TestCase { use CanCreateImagickTestImage; diff --git a/tests/Drivers/Imagick/Decoders/RgbStringColorDecoderTest.php b/tests/Drivers/Imagick/Decoders/RgbStringColorDecoderTest.php index 07f8e652..f2ac61e7 100644 --- a/tests/Drivers/Imagick/Decoders/RgbStringColorDecoderTest.php +++ b/tests/Drivers/Imagick/Decoders/RgbStringColorDecoderTest.php @@ -6,6 +6,9 @@ use Intervention\Image\Drivers\Imagick\Color; use Intervention\Image\Drivers\Imagick\Decoders\RgbStringColorDecoder; use Intervention\Image\Tests\TestCase; +/** + * @requires extension imagick + */ class RgbStringColorDecoderTest extends TestCase { public function testDecodeRgb(): void diff --git a/tests/Drivers/Imagick/Decoders/TransparentColorDecoderTest.php b/tests/Drivers/Imagick/Decoders/TransparentColorDecoderTest.php index 915842bd..82dcf188 100644 --- a/tests/Drivers/Imagick/Decoders/TransparentColorDecoderTest.php +++ b/tests/Drivers/Imagick/Decoders/TransparentColorDecoderTest.php @@ -6,6 +6,9 @@ use Intervention\Image\Drivers\Imagick\Color; use Intervention\Image\Drivers\Imagick\Decoders\TransparentColorDecoder; use Intervention\Image\Tests\TestCase; +/** + * @requires extension imagick + */ class TransparentColorDecoderTest extends TestCase { public function testDecode(): void diff --git a/tests/Drivers/Imagick/Encoders/GifEncoderTest.php b/tests/Drivers/Imagick/Encoders/GifEncoderTest.php index 9e51820e..fd7f40e4 100644 --- a/tests/Drivers/Imagick/Encoders/GifEncoderTest.php +++ b/tests/Drivers/Imagick/Encoders/GifEncoderTest.php @@ -12,6 +12,9 @@ use Intervention\Image\Tests\TestCase; use Intervention\MimeSniffer\MimeSniffer; use Intervention\MimeSniffer\Types\ImageGif; +/** + * @requires extension imagick + */ class GifEncoderTest extends TestCase { protected function getTestImage(): Image diff --git a/tests/Drivers/Imagick/Encoders/JpegEncoderTest.php b/tests/Drivers/Imagick/Encoders/JpegEncoderTest.php index 6c6f0967..5e2d0ecc 100644 --- a/tests/Drivers/Imagick/Encoders/JpegEncoderTest.php +++ b/tests/Drivers/Imagick/Encoders/JpegEncoderTest.php @@ -12,6 +12,9 @@ use Intervention\Image\Tests\TestCase; use Intervention\MimeSniffer\MimeSniffer; use Intervention\MimeSniffer\Types\ImageJpeg; +/** + * @requires extension imagick + */ class JpegEncoderTest extends TestCase { protected function getTestImage(): Image diff --git a/tests/Drivers/Imagick/FrameTest.php b/tests/Drivers/Imagick/FrameTest.php index e5764276..659b4db6 100644 --- a/tests/Drivers/Imagick/FrameTest.php +++ b/tests/Drivers/Imagick/FrameTest.php @@ -9,6 +9,9 @@ use Intervention\Image\Drivers\Imagick\Image; use Intervention\Image\Geometry\Size; use Intervention\Image\Tests\TestCase; +/** + * @requires extension imagick + */ class FrameTest extends TestCase { protected function getTestFrame(): Frame diff --git a/tests/Drivers/Imagick/ImageFactoryTest.php b/tests/Drivers/Imagick/ImageFactoryTest.php index d23cb443..237d9c4d 100644 --- a/tests/Drivers/Imagick/ImageFactoryTest.php +++ b/tests/Drivers/Imagick/ImageFactoryTest.php @@ -7,6 +7,9 @@ use Intervention\Image\Drivers\Imagick\Image; use Intervention\Image\Drivers\Imagick\ImageFactory; use Intervention\Image\Tests\TestCase; +/** + * @requires extension imagick + */ class ImageFactoryTest extends TestCase { public function testNewImage(): void diff --git a/tests/Drivers/Imagick/ImageTest.php b/tests/Drivers/Imagick/ImageTest.php index 17c7d706..572837db 100644 --- a/tests/Drivers/Imagick/ImageTest.php +++ b/tests/Drivers/Imagick/ImageTest.php @@ -10,6 +10,9 @@ use Intervention\Image\Drivers\Imagick\Image; use Intervention\Image\Geometry\Size; use Intervention\Image\Tests\TestCase; +/** + * @requires extension imagick + */ class ImageTest extends TestCase { protected Image $image; diff --git a/tests/Drivers/Imagick/InputHandlerTest.php b/tests/Drivers/Imagick/InputHandlerTest.php index 6bc689ba..9d665945 100644 --- a/tests/Drivers/Imagick/InputHandlerTest.php +++ b/tests/Drivers/Imagick/InputHandlerTest.php @@ -2,15 +2,15 @@ namespace Intervention\Image\Tests\Drivers\Imagick; -use Intervention\Image\Collection; use Intervention\Image\Drivers\Imagick\Color; -use Intervention\Image\Drivers\Imagick\Frame; use Intervention\Image\Drivers\Imagick\Image; use Intervention\Image\Drivers\Imagick\InputHandler; use Intervention\Image\Exceptions\DecoderException; -use Intervention\Image\Geometry\Size; use Intervention\Image\Tests\TestCase; +/** + * @requires extension imagick + */ class InputHandlerTest extends TestCase { public function testHandleEmptyString(): void diff --git a/tests/Drivers/Imagick/Modifiers/BlurModifierTest.php b/tests/Drivers/Imagick/Modifiers/BlurModifierTest.php index a1ff3750..e001c0f8 100644 --- a/tests/Drivers/Imagick/Modifiers/BlurModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/BlurModifierTest.php @@ -2,11 +2,13 @@ namespace Intervention\Image\Tests\Drivers\Imagick\Modifiers; -use Intervention\Image\Tests\TestCase; -use Intervention\Image\Drivers\Imagick\Image; use Intervention\Image\Drivers\Imagick\Modifiers\BlurModifier; +use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateImagickTestImage; +/** + * @requires extension imagick + */ class BlurModifierTest extends TestCase { use CanCreateImagickTestImage; diff --git a/tests/Drivers/Imagick/Modifiers/BrightnessModifierTest.php b/tests/Drivers/Imagick/Modifiers/BrightnessModifierTest.php index f0e3dfc7..a4def4c5 100644 --- a/tests/Drivers/Imagick/Modifiers/BrightnessModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/BrightnessModifierTest.php @@ -2,11 +2,13 @@ namespace Intervention\Image\Tests\Drivers\Imagick\Modifiers; -use Intervention\Image\Tests\TestCase; -use Intervention\Image\Drivers\Imagick\Image; use Intervention\Image\Drivers\Imagick\Modifiers\BrightnessModifier; +use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateImagickTestImage; +/** + * @requires extension imagick + */ class BrightnessModifierTest extends TestCase { use CanCreateImagickTestImage; diff --git a/tests/Drivers/Imagick/Modifiers/ContrastModifierTest.php b/tests/Drivers/Imagick/Modifiers/ContrastModifierTest.php index 042c3f08..856ea3a8 100644 --- a/tests/Drivers/Imagick/Modifiers/ContrastModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/ContrastModifierTest.php @@ -2,11 +2,13 @@ namespace Intervention\Image\Tests\Drivers\Imagick\Modifiers; -use Intervention\Image\Tests\TestCase; -use Intervention\Image\Drivers\Imagick\Image; use Intervention\Image\Drivers\Imagick\Modifiers\ContrastModifier; +use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateImagickTestImage; +/** + * @requires extension imagick + */ class ContrastModifierTest extends TestCase { use CanCreateImagickTestImage; diff --git a/tests/Drivers/Imagick/Modifiers/DestroyModifierTest.php b/tests/Drivers/Imagick/Modifiers/DestroyModifierTest.php index 6b9d3efe..ac61bd84 100644 --- a/tests/Drivers/Imagick/Modifiers/DestroyModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/DestroyModifierTest.php @@ -3,11 +3,13 @@ namespace Intervention\Image\Tests\Drivers\Imagick\Modifiers; use Imagick; -use Intervention\Image\Tests\TestCase; -use Intervention\Image\Drivers\Imagick\Image; use Intervention\Image\Drivers\Imagick\Modifiers\DestroyModifier; +use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateImagickTestImage; +/** + * @requires extension imagick + */ class DestroyModifierTest extends TestCase { use CanCreateImagickTestImage; diff --git a/tests/Drivers/Imagick/Modifiers/FillModifierTest.php b/tests/Drivers/Imagick/Modifiers/FillModifierTest.php index 4c5a8d15..b1a74f27 100644 --- a/tests/Drivers/Imagick/Modifiers/FillModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/FillModifierTest.php @@ -4,12 +4,14 @@ namespace Intervention\Image\Tests\Drivers\Imagick\Modifiers; use ImagickPixel; use Intervention\Image\Drivers\Imagick\Color; -use Intervention\Image\Drivers\Imagick\Image; use Intervention\Image\Drivers\Imagick\Modifiers\FillModifier; use Intervention\Image\Geometry\Point; use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateImagickTestImage; +/** + * @requires extension imagick + */ class FillModifierTest extends TestCase { use CanCreateImagickTestImage; diff --git a/tests/Drivers/Imagick/Modifiers/FitModifierTest.php b/tests/Drivers/Imagick/Modifiers/FitModifierTest.php index aeb4ef31..a745da01 100644 --- a/tests/Drivers/Imagick/Modifiers/FitModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/FitModifierTest.php @@ -2,13 +2,13 @@ namespace Intervention\Image\Tests\Drivers\Imagick\Modifiers; -use Intervention\Image\Drivers\Imagick\Image; use Intervention\Image\Drivers\Imagick\Modifiers\FitModifier; -use Intervention\Image\Geometry\Point; -use Intervention\Image\Geometry\Size; use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateImagickTestImage; +/** + * @requires extension imagick + */ class FitModifierTest extends TestCase { use CanCreateImagickTestImage; diff --git a/tests/Drivers/Imagick/Modifiers/GreyscaleModifierTest.php b/tests/Drivers/Imagick/Modifiers/GreyscaleModifierTest.php index 64b5be61..5af3dfe5 100644 --- a/tests/Drivers/Imagick/Modifiers/GreyscaleModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/GreyscaleModifierTest.php @@ -2,11 +2,13 @@ namespace Intervention\Image\Tests\Drivers\Imagick\Modifiers; -use Intervention\Image\Tests\TestCase; -use Intervention\Image\Drivers\Imagick\Image; use Intervention\Image\Drivers\Imagick\Modifiers\GreyscaleModifier; +use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateImagickTestImage; +/** + * @requires extension imagick + */ class GreyscaleModifierTest extends TestCase { use CanCreateImagickTestImage; diff --git a/tests/Drivers/Imagick/Modifiers/InvertModifierTest.php b/tests/Drivers/Imagick/Modifiers/InvertModifierTest.php index c167542c..8c97aa46 100644 --- a/tests/Drivers/Imagick/Modifiers/InvertModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/InvertModifierTest.php @@ -2,11 +2,13 @@ namespace Intervention\Image\Tests\Drivers\Imagick\Modifiers; -use Intervention\Image\Tests\TestCase; -use Intervention\Image\Drivers\Imagick\Image; use Intervention\Image\Drivers\Imagick\Modifiers\InvertModifier; +use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateImagickTestImage; +/** + * @requires extension imagick + */ class InvertModifierTest extends TestCase { use CanCreateImagickTestImage; diff --git a/tests/Drivers/Imagick/Modifiers/PixelateModifierTest.php b/tests/Drivers/Imagick/Modifiers/PixelateModifierTest.php index d0d9d993..cc4973ce 100644 --- a/tests/Drivers/Imagick/Modifiers/PixelateModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/PixelateModifierTest.php @@ -2,11 +2,13 @@ namespace Intervention\Image\Tests\Drivers\Imagick\Modifiers; -use Intervention\Image\Drivers\Imagick\Image; use Intervention\Image\Drivers\Imagick\Modifiers\PixelateModifier; use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateImagickTestImage; +/** + * @requires extension imagick + */ class PixelateModifierTest extends TestCase { use CanCreateImagickTestImage; diff --git a/tests/Drivers/Imagick/Modifiers/PlaceModifierTest.php b/tests/Drivers/Imagick/Modifiers/PlaceModifierTest.php index e6b66dd5..21f3619c 100644 --- a/tests/Drivers/Imagick/Modifiers/PlaceModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/PlaceModifierTest.php @@ -2,12 +2,13 @@ namespace Intervention\Image\Tests\Drivers\Imagick\Modifiers; -use Intervention\Image\Drivers\Imagick\Image; use Intervention\Image\Drivers\Imagick\Modifiers\PlaceModifier; -use Intervention\Image\Geometry\Size; use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateImagickTestImage; +/** + * @requires extension imagick + */ class PlaceModifierTest extends TestCase { use CanCreateImagickTestImage; diff --git a/tests/Drivers/Imagick/Modifiers/ResizeModifierTest.php b/tests/Drivers/Imagick/Modifiers/ResizeModifierTest.php index 7f2bb201..c55d4575 100644 --- a/tests/Drivers/Imagick/Modifiers/ResizeModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/ResizeModifierTest.php @@ -2,13 +2,14 @@ namespace Intervention\Image\Tests\Drivers\Imagick\Modifiers; -use Intervention\Image\Drivers\Imagick\Image; use Intervention\Image\Drivers\Imagick\Modifiers\ResizeModifier; -use Intervention\Image\Geometry\Size; use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateImagickTestImage; -class CropResizeModifierTest extends TestCase +/** + * @requires extension imagick + */ +class ResizeModifierTest extends TestCase { use CanCreateImagickTestImage; diff --git a/tests/Drivers/Imagick/Modifiers/SharpenModifierTest.php b/tests/Drivers/Imagick/Modifiers/SharpenModifierTest.php index f2e23bd1..2f2dd8c9 100644 --- a/tests/Drivers/Imagick/Modifiers/SharpenModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/SharpenModifierTest.php @@ -2,11 +2,13 @@ namespace Intervention\Image\Tests\Drivers\Imagick\Modifiers; -use Intervention\Image\Tests\TestCase; -use Intervention\Image\Drivers\Imagick\Image; use Intervention\Image\Drivers\Imagick\Modifiers\SharpenModifier; +use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateImagickTestImage; +/** + * @requires extension imagick + */ class SharpenModifierTest extends TestCase { use CanCreateImagickTestImage; diff --git a/tests/ImageManagerTest.php b/tests/ImageManagerTest.php index d05fb12e..833b0ef0 100644 --- a/tests/ImageManagerTest.php +++ b/tests/ImageManagerTest.php @@ -13,6 +13,7 @@ class ImageManagerTest extends TestCase $this->assertInstanceOf(ImageManager::class, $manager); } + /** @requires extension gd */ public function testCreateGd() { $manager = new ImageManager('gd'); @@ -20,6 +21,7 @@ class ImageManagerTest extends TestCase $this->assertInstanceOf(ImageInterface::class, $image); } + /** @requires extension gd */ public function testMakeGd() { $manager = new ImageManager('gd'); @@ -27,6 +29,7 @@ class ImageManagerTest extends TestCase $this->assertInstanceOf(ImageInterface::class, $image); } + /** @requires extension imagick */ public function testCreateImagick() { $manager = new ImageManager('imagick'); @@ -34,6 +37,7 @@ class ImageManagerTest extends TestCase $this->assertInstanceOf(ImageInterface::class, $image); } + /** @requires extension imagick */ public function testMakeImagick() { $manager = new ImageManager('imagick'); From 8ba4b91f56e831c620ef8d7fd55aaf354bd5323e Mon Sep 17 00:00:00 2001 From: Gwendolen Lynch Date: Wed, 22 Dec 2021 12:13:21 +0100 Subject: [PATCH 094/476] Webp encoders for GD & ImageMagick --- src/Drivers/Gd/Encoders/WebpEncoder.php | 25 ++++++++++++ src/Drivers/Imagick/Encoders/WebpEncoder.php | 36 ++++++++++++++++++ tests/Drivers/Gd/Encoders/WebpEncoderTest.php | 34 +++++++++++++++++ .../Imagick/Encoders/WebpEncoderTest.php | 38 +++++++++++++++++++ 4 files changed, 133 insertions(+) create mode 100644 src/Drivers/Gd/Encoders/WebpEncoder.php create mode 100644 src/Drivers/Imagick/Encoders/WebpEncoder.php create mode 100644 tests/Drivers/Gd/Encoders/WebpEncoderTest.php create mode 100644 tests/Drivers/Imagick/Encoders/WebpEncoderTest.php diff --git a/src/Drivers/Gd/Encoders/WebpEncoder.php b/src/Drivers/Gd/Encoders/WebpEncoder.php new file mode 100644 index 00000000..748db2a2 --- /dev/null +++ b/src/Drivers/Gd/Encoders/WebpEncoder.php @@ -0,0 +1,25 @@ +quality = $quality; + } + + public function encode(ImageInterface $image): EncodedImage + { + $data = $this->getBuffered(function () use ($image) { + imagewebp($image->getFrames()->first()->getCore(), null, $this->quality); + }); + + return new EncodedImage($data, 'image/webp'); + } +} diff --git a/src/Drivers/Imagick/Encoders/WebpEncoder.php b/src/Drivers/Imagick/Encoders/WebpEncoder.php new file mode 100644 index 00000000..0f7cb9f9 --- /dev/null +++ b/src/Drivers/Imagick/Encoders/WebpEncoder.php @@ -0,0 +1,36 @@ +quality = $quality; + } + + public function encode(ImageInterface $image): EncodedImage + { + $format = 'webp'; + $compression = Imagick::COMPRESSION_ZIP; + + $imagick = $image->getFrames()->first()->getCore(); + $imagick->setImageBackgroundColor(new ImagickPixel('transparent')); + + $imagick = $imagick->mergeImageLayers(Imagick::LAYERMETHOD_MERGE); + $imagick->setFormat($format); + $imagick->setImageFormat($format); + $imagick->setCompression($compression); + $imagick->setImageCompression($compression); + $imagick->setImageCompressionQuality($this->quality); + + return new EncodedImage($imagick->getImagesBlob(), 'image/webp'); + } +} diff --git a/tests/Drivers/Gd/Encoders/WebpEncoderTest.php b/tests/Drivers/Gd/Encoders/WebpEncoderTest.php new file mode 100644 index 00000000..994395bd --- /dev/null +++ b/tests/Drivers/Gd/Encoders/WebpEncoderTest.php @@ -0,0 +1,34 @@ +getTestImage(); + $encoder = new WebpEncoder(75); + $result = $encoder->encode($image); + $this->assertTrue(MimeSniffer::createFromString((string) $result)->matches(new ImageWebp())); + } +} diff --git a/tests/Drivers/Imagick/Encoders/WebpEncoderTest.php b/tests/Drivers/Imagick/Encoders/WebpEncoderTest.php new file mode 100644 index 00000000..dcced39b --- /dev/null +++ b/tests/Drivers/Imagick/Encoders/WebpEncoderTest.php @@ -0,0 +1,38 @@ +newImage(3, 2, new ImagickPixel('red'), 'png'); + $frame = new Frame($imagick); + + return new Image(new Collection([$frame])); + } + + public function testEncode(): void + { + $image = $this->getTestImage(); + $encoder = new WebpEncoder(75); + $result = $encoder->encode($image); + $this->assertTrue(MimeSniffer::createFromString((string) $result)->matches(new ImageWebp())); + } +} From 4952544a525eed872a4c6cd4c641bb18fd773811 Mon Sep 17 00:00:00 2001 From: Gwendolen Lynch Date: Wed, 22 Dec 2021 12:13:48 +0100 Subject: [PATCH 095/476] ImageMagick PNG encoder test --- .../Imagick/Encoders/PngEncoderTest.php | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 tests/Drivers/Imagick/Encoders/PngEncoderTest.php diff --git a/tests/Drivers/Imagick/Encoders/PngEncoderTest.php b/tests/Drivers/Imagick/Encoders/PngEncoderTest.php new file mode 100644 index 00000000..30b41fa8 --- /dev/null +++ b/tests/Drivers/Imagick/Encoders/PngEncoderTest.php @@ -0,0 +1,38 @@ +newImage(3, 2, new ImagickPixel('red'), 'jpg'); + $frame = new Frame($imagick); + + return new Image(new Collection([$frame])); + } + + public function testEncode(): void + { + $image = $this->getTestImage(); + $encoder = new PngEncoder(75); + $result = $encoder->encode($image); + $this->assertTrue(MimeSniffer::createFromString((string) $result)->matches(new ImagePng())); + } +} From 270ec4bc5bbc3876289a6da06d184cab3cf518eb Mon Sep 17 00:00:00 2001 From: Gwendolen Lynch Date: Wed, 22 Dec 2021 12:14:27 +0100 Subject: [PATCH 096/476] Enable PHPUnit code coverage --- phpunit.xml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/phpunit.xml b/phpunit.xml index 422eeac6..63454441 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -14,4 +14,10 @@ ./tests/
+ + + + src + + From 66d744f5fb8868ff3cd7424e6fd0798321e73fe0 Mon Sep 17 00:00:00 2001 From: Gwendolen Lynch Date: Wed, 22 Dec 2021 12:41:59 +0100 Subject: [PATCH 097/476] Add toWebp() method to ImageInterface & AbstractImage --- src/Drivers/Abstract/AbstractImage.php | 9 +++++++-- src/Interfaces/ImageInterface.php | 4 +--- tests/AbstractImageTest.php | 21 ++++++++++++++++++--- 3 files changed, 26 insertions(+), 8 deletions(-) diff --git a/src/Drivers/Abstract/AbstractImage.php b/src/Drivers/Abstract/AbstractImage.php index 29ae8f9b..4d11c373 100644 --- a/src/Drivers/Abstract/AbstractImage.php +++ b/src/Drivers/Abstract/AbstractImage.php @@ -4,9 +4,7 @@ namespace Intervention\Image\Drivers\Abstract; use Intervention\Image\Collection; use Intervention\Image\EncodedImage; -use Intervention\Image\Exceptions\NotWritableException; use Intervention\Image\Geometry\Point; -use Intervention\Image\Geometry\Resizer; use Intervention\Image\Geometry\Size; use Intervention\Image\Interfaces\EncoderInterface; use Intervention\Image\Interfaces\FrameInterface; @@ -92,6 +90,13 @@ abstract class AbstractImage ); } + public function toWebp(int $quality = 75): EncodedImage + { + return $this->encode( + $this->resolveDriverClass('Encoders\WebpEncoder', $quality) + ); + } + public function toGif(): EncodedImage { return $this->encode( diff --git a/src/Interfaces/ImageInterface.php b/src/Interfaces/ImageInterface.php index 7fc9e750..182a76dc 100644 --- a/src/Interfaces/ImageInterface.php +++ b/src/Interfaces/ImageInterface.php @@ -4,9 +4,6 @@ namespace Intervention\Image\Interfaces; use Intervention\Image\Collection; use Intervention\Image\EncodedImage; -use Intervention\Image\Interfaces\FrameInterface; -use Intervention\Image\Interfaces\ImageInterface; -use Intervention\Image\Interfaces\SizeInterface; interface ImageInterface { @@ -21,6 +18,7 @@ interface ImageInterface public function modify(ModifierInterface $modifier): ImageInterface; public function encode(EncoderInterface $encoder): EncodedImage; public function toJpeg(int $quality = 75): EncodedImage; + public function toWebp(int $quality = 75): EncodedImage; public function toGif(): EncodedImage; public function toPng(): EncodedImage; public function pickColors(int $x, int $y): Collection; diff --git a/tests/AbstractImageTest.php b/tests/AbstractImageTest.php index ab2c5091..167fd200 100644 --- a/tests/AbstractImageTest.php +++ b/tests/AbstractImageTest.php @@ -3,7 +3,6 @@ namespace Intervention\Image\Tests; use Intervention\Image\Collection; -use Intervention\Image\Drivers\Abstract\AbstractFrame; use Intervention\Image\Drivers\Abstract\AbstractImage; use Intervention\Image\EncodedImage; use Intervention\Image\Geometry\Size; @@ -130,13 +129,29 @@ class AbstractImageTest extends TestCase $encoder->shouldReceive('encode')->with($img)->andReturn($encoded); $img->shouldReceive('resolveDriverClass') - ->with('Encoders\JpegEncoder', 45) - ->andReturn($encoder); + ->with('Encoders\JpegEncoder', 45) + ->andReturn($encoder); $result = $img->toJpeg(45); $this->assertInstanceOf(EncodedImage::class, $result); } + public function testToWebp(): void + { + $img = $this->abstractImageMock(); + + $encoded = Mockery::mock(EncodedImage::class); + $encoder = Mockery::mock(EncoderInterface::class); + $encoder->shouldReceive('encode')->with($img)->andReturn($encoded); + + $img->shouldReceive('resolveDriverClass') + ->with('Encoders\WebpEncoder', 45) + ->andReturn($encoder); + + $result = $img->toWebp(45); + $this->assertInstanceOf(EncodedImage::class, $result); + } + public function testToGif(): void { $img = $this->abstractImageMock(); From 5dcd6e820f9f1249d4bfc9f4e9187cc73ebf0c8c Mon Sep 17 00:00:00 2001 From: Gwendolen Lynch Date: Wed, 22 Dec 2021 14:34:28 +0100 Subject: [PATCH 098/476] Add @covers annotations to tests --- tests/AbstractImageTest.php | 4 +++- tests/CollectionTest.php | 3 +++ tests/Drivers/Abstract/AbstractColorTest.php | 5 ++++- tests/Drivers/Gd/ColorTest.php | 3 +++ tests/Drivers/Gd/Decoders/ArrayColorDecoderTest.php | 3 +++ tests/Drivers/Gd/Decoders/Base64ImageDecoderTest.php | 3 +++ tests/Drivers/Gd/Decoders/BinaryImageDecoderTest.php | 3 +++ tests/Drivers/Gd/Decoders/DataUriImageDecoderTest.php | 3 +++ tests/Drivers/Gd/Decoders/FilePathImageDecoderTest.php | 3 +++ tests/Drivers/Gd/Decoders/HexColorDecoderTest.php | 5 ++++- tests/Drivers/Gd/Decoders/ImageObjectDecoderTest.php | 3 +++ tests/Drivers/Gd/Decoders/RgbStringColorDecoderTest.php | 3 +++ tests/Drivers/Gd/Decoders/TransparentColorDecoderTest.php | 3 +++ tests/Drivers/Gd/Encoders/GifEncoderTest.php | 3 +++ tests/Drivers/Gd/Encoders/JpegEncoderTest.php | 3 +++ tests/Drivers/Gd/Encoders/PngEncoderTest.php | 5 ++++- tests/Drivers/Gd/FrameTest.php | 3 +++ tests/Drivers/Gd/ImageFactoryTest.php | 3 +++ tests/Drivers/Gd/ImageTest.php | 3 +++ tests/Drivers/Gd/InputHandlerTest.php | 6 +++--- tests/Drivers/Gd/Modifiers/BlurModifierTest.php | 5 ++++- tests/Drivers/Gd/Modifiers/BrightnessModifierTest.php | 5 ++++- tests/Drivers/Gd/Modifiers/ContrastModifierTest.php | 5 ++++- tests/Drivers/Gd/Modifiers/DestroyModifierTest.php | 5 ++++- tests/Drivers/Gd/Modifiers/FillModifierTest.php | 4 ++++ tests/Drivers/Gd/Modifiers/FitModifierTest.php | 7 ++++--- tests/Drivers/Gd/Modifiers/GreyscaleModifierTest.php | 7 +++++-- tests/Drivers/Gd/Modifiers/InvertModifierTest.php | 7 +++++-- tests/Drivers/Gd/Modifiers/PixelateModifierTest.php | 5 ++++- tests/Drivers/Gd/Modifiers/PlaceModifierTest.php | 6 ++++-- tests/Drivers/Gd/Modifiers/ResizeModifierTest.php | 6 ++++-- tests/Drivers/Gd/Modifiers/SharpenModifierTest.php | 7 +++++-- tests/Drivers/Imagick/ColorTest.php | 3 +++ tests/Drivers/Imagick/Decoders/ArrayColorDecoderTest.php | 3 +++ tests/Drivers/Imagick/Decoders/ImageObjectDecoderTest.php | 3 +++ .../Imagick/Decoders/RgbStringColorDecoderTest.php | 3 +++ .../Imagick/Decoders/TransparentColorDecoderTest.php | 3 +++ tests/Drivers/Imagick/Encoders/GifEncoderTest.php | 3 +++ tests/Drivers/Imagick/Encoders/JpegEncoderTest.php | 3 +++ tests/Drivers/Imagick/FrameTest.php | 3 +++ tests/Drivers/Imagick/ImageFactoryTest.php | 3 +++ tests/Drivers/Imagick/ImageTest.php | 3 +++ tests/Drivers/Imagick/InputHandlerTest.php | 6 +++--- tests/Drivers/Imagick/Modifiers/BlurModifierTest.php | 6 ++++-- .../Drivers/Imagick/Modifiers/BrightnessModifierTest.php | 6 ++++-- tests/Drivers/Imagick/Modifiers/ContrastModifierTest.php | 6 ++++-- tests/Drivers/Imagick/Modifiers/DestroyModifierTest.php | 6 ++++-- tests/Drivers/Imagick/Modifiers/FillModifierTest.php | 4 +++- tests/Drivers/Imagick/Modifiers/FitModifierTest.php | 6 +++--- tests/Drivers/Imagick/Modifiers/GreyscaleModifierTest.php | 6 ++++-- tests/Drivers/Imagick/Modifiers/InvertModifierTest.php | 6 ++++-- tests/Drivers/Imagick/Modifiers/PixelateModifierTest.php | 4 +++- tests/Drivers/Imagick/Modifiers/PlaceModifierTest.php | 5 +++-- tests/Drivers/Imagick/Modifiers/ResizeModifierTest.php | 7 ++++--- tests/Drivers/Imagick/Modifiers/SharpenModifierTest.php | 6 ++++-- tests/EncodedImageTest.php | 3 +++ tests/Geometry/PointTest.php | 3 +++ tests/Geometry/ResizerTest.php | 3 +++ tests/Geometry/SizeTest.php | 8 ++++---- tests/ImageManagerTest.php | 3 +++ 60 files changed, 207 insertions(+), 56 deletions(-) diff --git a/tests/AbstractImageTest.php b/tests/AbstractImageTest.php index ab2c5091..340d0c23 100644 --- a/tests/AbstractImageTest.php +++ b/tests/AbstractImageTest.php @@ -3,7 +3,6 @@ namespace Intervention\Image\Tests; use Intervention\Image\Collection; -use Intervention\Image\Drivers\Abstract\AbstractFrame; use Intervention\Image\Drivers\Abstract\AbstractImage; use Intervention\Image\EncodedImage; use Intervention\Image\Geometry\Size; @@ -14,6 +13,9 @@ use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; use Mockery; +/** + * @covers \Intervention\Image\Drivers\Abstract\AbstractImage + */ class AbstractImageTest extends TestCase { protected function abstractImageMock(): AbstractImage diff --git a/tests/CollectionTest.php b/tests/CollectionTest.php index 7a22ce27..b4a6738b 100644 --- a/tests/CollectionTest.php +++ b/tests/CollectionTest.php @@ -4,6 +4,9 @@ namespace Intervention\Image\Tests; use Intervention\Image\Collection; +/** + * @covers \Intervention\Image\Collection + */ class CollectionTest extends TestCase { public function testConstructor() diff --git a/tests/Drivers/Abstract/AbstractColorTest.php b/tests/Drivers/Abstract/AbstractColorTest.php index 4db7614e..d419c157 100644 --- a/tests/Drivers/Abstract/AbstractColorTest.php +++ b/tests/Drivers/Abstract/AbstractColorTest.php @@ -2,10 +2,13 @@ namespace Intervention\Image\Tests\Drivers\Abstract; -use Mockery; use Intervention\Image\Drivers\Abstract\AbstractColor; use Intervention\Image\Tests\TestCase; +use Mockery; +/** + * @covers \Intervention\Image\Drivers\Abstract\AbstractColor + */ class AbstractColorTest extends TestCase { public function testToHex(): void diff --git a/tests/Drivers/Gd/ColorTest.php b/tests/Drivers/Gd/ColorTest.php index 989fad85..95307580 100644 --- a/tests/Drivers/Gd/ColorTest.php +++ b/tests/Drivers/Gd/ColorTest.php @@ -5,6 +5,9 @@ namespace Intervention\Image\Tests\Drivers\Gd; use Intervention\Image\Drivers\Gd\Color; use Intervention\Image\Tests\TestCase; +/** + * @covers \Intervention\Image\Drivers\Gd\Color + */ class ColorTest extends TestCase { protected function getTestColor($r = 0, $g = 0, $b = 0, $a = 0): Color diff --git a/tests/Drivers/Gd/Decoders/ArrayColorDecoderTest.php b/tests/Drivers/Gd/Decoders/ArrayColorDecoderTest.php index 585d864a..cb0a1fef 100644 --- a/tests/Drivers/Gd/Decoders/ArrayColorDecoderTest.php +++ b/tests/Drivers/Gd/Decoders/ArrayColorDecoderTest.php @@ -6,6 +6,9 @@ use Intervention\Image\Drivers\Gd\Color; use Intervention\Image\Drivers\Gd\Decoders\ArrayColorDecoder; use Intervention\Image\Tests\TestCase; +/** + * @covers \Intervention\Image\Drivers\Gd\Decoders\ArrayColorDecoder + */ class ArrayColorDecoderTest extends TestCase { public function testDecode(): void diff --git a/tests/Drivers/Gd/Decoders/Base64ImageDecoderTest.php b/tests/Drivers/Gd/Decoders/Base64ImageDecoderTest.php index ae92fed2..ddeb604f 100644 --- a/tests/Drivers/Gd/Decoders/Base64ImageDecoderTest.php +++ b/tests/Drivers/Gd/Decoders/Base64ImageDecoderTest.php @@ -7,6 +7,9 @@ use Intervention\Image\Drivers\Gd\Image; use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateGdTestImage; +/** + * @covers \Intervention\Image\Drivers\Gd\Decoders\Base64ImageDecoder + */ class Base64ImageDecoderTest extends TestCase { use CanCreateGdTestImage; diff --git a/tests/Drivers/Gd/Decoders/BinaryImageDecoderTest.php b/tests/Drivers/Gd/Decoders/BinaryImageDecoderTest.php index 42b3b20e..cf9acd0d 100644 --- a/tests/Drivers/Gd/Decoders/BinaryImageDecoderTest.php +++ b/tests/Drivers/Gd/Decoders/BinaryImageDecoderTest.php @@ -6,6 +6,9 @@ use Intervention\Image\Drivers\Gd\Decoders\BinaryImageDecoder; use Intervention\Image\Drivers\Gd\Image; use Intervention\Image\Tests\TestCase; +/** + * @covers \Intervention\Image\Drivers\Gd\Decoders\BinaryImageDecoder + */ class BinaryImageDecoderTest extends TestCase { public function testDecodePng(): void diff --git a/tests/Drivers/Gd/Decoders/DataUriImageDecoderTest.php b/tests/Drivers/Gd/Decoders/DataUriImageDecoderTest.php index fccc146d..b0b947d2 100644 --- a/tests/Drivers/Gd/Decoders/DataUriImageDecoderTest.php +++ b/tests/Drivers/Gd/Decoders/DataUriImageDecoderTest.php @@ -7,6 +7,9 @@ use Intervention\Image\Drivers\Gd\Image; use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateGdTestImage; +/** + * @covers \Intervention\Image\Drivers\Gd\Decoders\DataUriImageDecoder + */ class DataUriImageDecoderTest extends TestCase { use CanCreateGdTestImage; diff --git a/tests/Drivers/Gd/Decoders/FilePathImageDecoderTest.php b/tests/Drivers/Gd/Decoders/FilePathImageDecoderTest.php index 840063b2..8f11c73e 100644 --- a/tests/Drivers/Gd/Decoders/FilePathImageDecoderTest.php +++ b/tests/Drivers/Gd/Decoders/FilePathImageDecoderTest.php @@ -7,6 +7,9 @@ use Intervention\Image\Drivers\Gd\Image; use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateGdTestImage; +/** + * @covers \Intervention\Image\Drivers\Gd\Decoders\FilePathImageDecoder + */ class FilePathImageDecoderTest extends TestCase { use CanCreateGdTestImage; diff --git a/tests/Drivers/Gd/Decoders/HexColorDecoderTest.php b/tests/Drivers/Gd/Decoders/HexColorDecoderTest.php index 37732117..6d0fe027 100644 --- a/tests/Drivers/Gd/Decoders/HexColorDecoderTest.php +++ b/tests/Drivers/Gd/Decoders/HexColorDecoderTest.php @@ -2,10 +2,13 @@ namespace Intervention\Image\Tests\Drivers\Gd\Decoders; -use Intervention\Image\Drivers\Gd\Decoders\HexColorDecoder; use Intervention\Image\Drivers\Gd\Color; +use Intervention\Image\Drivers\Gd\Decoders\HexColorDecoder; use Intervention\Image\Tests\TestCase; +/** + * @covers \Intervention\Image\Drivers\Gd\Decoders\HexColorDecoder + */ class HexColorDecoderTest extends TestCase { public function testDecode(): void diff --git a/tests/Drivers/Gd/Decoders/ImageObjectDecoderTest.php b/tests/Drivers/Gd/Decoders/ImageObjectDecoderTest.php index 573aa4a7..58b1e351 100644 --- a/tests/Drivers/Gd/Decoders/ImageObjectDecoderTest.php +++ b/tests/Drivers/Gd/Decoders/ImageObjectDecoderTest.php @@ -7,6 +7,9 @@ use Intervention\Image\Drivers\Gd\Image; use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateGdTestImage; +/** + * @covers \Intervention\Image\Drivers\Gd\Decoders\ImageObjectDecoder + */ class ImageObjectDecoderTest extends TestCase { use CanCreateGdTestImage; diff --git a/tests/Drivers/Gd/Decoders/RgbStringColorDecoderTest.php b/tests/Drivers/Gd/Decoders/RgbStringColorDecoderTest.php index 19c2a26f..064eb486 100644 --- a/tests/Drivers/Gd/Decoders/RgbStringColorDecoderTest.php +++ b/tests/Drivers/Gd/Decoders/RgbStringColorDecoderTest.php @@ -6,6 +6,9 @@ use Intervention\Image\Drivers\Gd\Color; use Intervention\Image\Drivers\Gd\Decoders\RgbStringColorDecoder; use Intervention\Image\Tests\TestCase; +/** + * @covers \Intervention\Image\Drivers\Gd\Decoders\RgbStringColorDecoder + */ class RgbStringColorDecoderTest extends TestCase { public function testDecodeRgb(): void diff --git a/tests/Drivers/Gd/Decoders/TransparentColorDecoderTest.php b/tests/Drivers/Gd/Decoders/TransparentColorDecoderTest.php index d0864d48..be55b589 100644 --- a/tests/Drivers/Gd/Decoders/TransparentColorDecoderTest.php +++ b/tests/Drivers/Gd/Decoders/TransparentColorDecoderTest.php @@ -6,6 +6,9 @@ use Intervention\Image\Drivers\Gd\Color; use Intervention\Image\Drivers\Gd\Decoders\TransparentColorDecoder; use Intervention\Image\Tests\TestCase; +/** + * @covers \Intervention\Image\Drivers\Gd\Decoders\TransparentColorDecoder + */ class TransparentColorDecoderTest extends TestCase { public function testDecode(): void diff --git a/tests/Drivers/Gd/Encoders/GifEncoderTest.php b/tests/Drivers/Gd/Encoders/GifEncoderTest.php index e668ce22..71fcc3ac 100644 --- a/tests/Drivers/Gd/Encoders/GifEncoderTest.php +++ b/tests/Drivers/Gd/Encoders/GifEncoderTest.php @@ -10,6 +10,9 @@ use Intervention\Image\Tests\TestCase; use Intervention\MimeSniffer\MimeSniffer; use Intervention\MimeSniffer\Types\ImageGif; +/** + * @covers \Intervention\Image\Drivers\Gd\Encoders\GifEncoder + */ class GifEncoderTest extends TestCase { protected function getTestImage(): Image diff --git a/tests/Drivers/Gd/Encoders/JpegEncoderTest.php b/tests/Drivers/Gd/Encoders/JpegEncoderTest.php index 231f0aab..b8424df2 100644 --- a/tests/Drivers/Gd/Encoders/JpegEncoderTest.php +++ b/tests/Drivers/Gd/Encoders/JpegEncoderTest.php @@ -10,6 +10,9 @@ use Intervention\Image\Tests\TestCase; use Intervention\MimeSniffer\MimeSniffer; use Intervention\MimeSniffer\Types\ImageJpeg; +/** + * @covers \Intervention\Image\Drivers\Gd\Encoders\JpegEncoder + */ class JpegEncoderTest extends TestCase { protected function getTestImage(): Image diff --git a/tests/Drivers/Gd/Encoders/PngEncoderTest.php b/tests/Drivers/Gd/Encoders/PngEncoderTest.php index 065aa4d6..189f6041 100644 --- a/tests/Drivers/Gd/Encoders/PngEncoderTest.php +++ b/tests/Drivers/Gd/Encoders/PngEncoderTest.php @@ -10,6 +10,9 @@ use Intervention\Image\Tests\TestCase; use Intervention\MimeSniffer\MimeSniffer; use Intervention\MimeSniffer\Types\ImagePng; +/** + * @covers \Intervention\Image\Drivers\Gd\Encoders\PngEncoder + */ class PngEncoderTest extends TestCase { protected function getTestImage(): Image @@ -26,4 +29,4 @@ class PngEncoderTest extends TestCase $result = $encoder->encode($image); $this->assertTrue(MimeSniffer::createFromString($result)->matches(new ImagePng)); } -} \ No newline at end of file +} diff --git a/tests/Drivers/Gd/FrameTest.php b/tests/Drivers/Gd/FrameTest.php index 2497139d..ef02e6fb 100644 --- a/tests/Drivers/Gd/FrameTest.php +++ b/tests/Drivers/Gd/FrameTest.php @@ -8,6 +8,9 @@ use Intervention\Image\Drivers\Gd\Image; use Intervention\Image\Geometry\Size; use Intervention\Image\Tests\TestCase; +/** + * @covers \Intervention\Image\Drivers\Gd\Frame + */ class FrameTest extends TestCase { protected function getTestFrame(): Frame diff --git a/tests/Drivers/Gd/ImageFactoryTest.php b/tests/Drivers/Gd/ImageFactoryTest.php index 9f319741..abe2bb8b 100644 --- a/tests/Drivers/Gd/ImageFactoryTest.php +++ b/tests/Drivers/Gd/ImageFactoryTest.php @@ -7,6 +7,9 @@ use Intervention\Image\Drivers\Gd\Image; use Intervention\Image\Drivers\Gd\ImageFactory; use Intervention\Image\Tests\TestCase; +/** + * @covers \Intervention\Image\Drivers\Gd\ImageFactory + */ class ImageFactoryTest extends TestCase { public function testNewImage(): void diff --git a/tests/Drivers/Gd/ImageTest.php b/tests/Drivers/Gd/ImageTest.php index 1916587b..4b09cf9c 100644 --- a/tests/Drivers/Gd/ImageTest.php +++ b/tests/Drivers/Gd/ImageTest.php @@ -9,6 +9,9 @@ use Intervention\Image\Drivers\Gd\Image; use Intervention\Image\Geometry\Size; use Intervention\Image\Tests\TestCase; +/** + * @covers \Intervention\Image\Drivers\Gd\Image + */ class ImageTest extends TestCase { protected Image $image; diff --git a/tests/Drivers/Gd/InputHandlerTest.php b/tests/Drivers/Gd/InputHandlerTest.php index 22fa5dc8..4ef55d4e 100644 --- a/tests/Drivers/Gd/InputHandlerTest.php +++ b/tests/Drivers/Gd/InputHandlerTest.php @@ -2,15 +2,15 @@ namespace Intervention\Image\Tests\Drivers\Gd; -use Intervention\Image\Collection; use Intervention\Image\Drivers\Gd\Color; -use Intervention\Image\Drivers\Gd\Frame; use Intervention\Image\Drivers\Gd\Image; use Intervention\Image\Drivers\Gd\InputHandler; use Intervention\Image\Exceptions\DecoderException; -use Intervention\Image\Geometry\Size; use Intervention\Image\Tests\TestCase; +/** + * @covers \Intervention\Image\Drivers\Gd\InputHandler + */ class InputHandlerTest extends TestCase { public function testHandleEmptyString(): void diff --git a/tests/Drivers/Gd/Modifiers/BlurModifierTest.php b/tests/Drivers/Gd/Modifiers/BlurModifierTest.php index 9062eda4..b57e3e0a 100644 --- a/tests/Drivers/Gd/Modifiers/BlurModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/BlurModifierTest.php @@ -2,11 +2,14 @@ namespace Intervention\Image\Tests\Drivers\Gd\Modifiers; -use Intervention\Image\Drivers\Gd\Image; use Intervention\Image\Drivers\Gd\Modifiers\BlurModifier; use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateGdTestImage; + +/** + * @covers \Intervention\Image\Drivers\Gd\Modifiers\BlurModifier + */ class BlurModifierTest extends TestCase { use CanCreateGdTestImage; diff --git a/tests/Drivers/Gd/Modifiers/BrightnessModifierTest.php b/tests/Drivers/Gd/Modifiers/BrightnessModifierTest.php index e0e41025..426ab356 100644 --- a/tests/Drivers/Gd/Modifiers/BrightnessModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/BrightnessModifierTest.php @@ -2,11 +2,14 @@ namespace Intervention\Image\Tests\Drivers\Gd\Modifiers; -use Intervention\Image\Drivers\Gd\Image; use Intervention\Image\Drivers\Gd\Modifiers\BrightnessModifier; use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateGdTestImage; + +/** + * @covers \Intervention\Image\Drivers\Gd\Modifiers\BrightnessModifier + */ class BrightnessModifierTest extends TestCase { use CanCreateGdTestImage; diff --git a/tests/Drivers/Gd/Modifiers/ContrastModifierTest.php b/tests/Drivers/Gd/Modifiers/ContrastModifierTest.php index 7ec44484..949a2b49 100644 --- a/tests/Drivers/Gd/Modifiers/ContrastModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/ContrastModifierTest.php @@ -2,11 +2,14 @@ namespace Intervention\Image\Tests\Drivers\Gd\Modifiers; -use Intervention\Image\Drivers\Gd\Image; use Intervention\Image\Drivers\Gd\Modifiers\ContrastModifier; use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateGdTestImage; + +/** + * @covers \Intervention\Image\Drivers\Gd\Modifiers\ContrastModifier + */ class ContrastModifierTest extends TestCase { use CanCreateGdTestImage; diff --git a/tests/Drivers/Gd/Modifiers/DestroyModifierTest.php b/tests/Drivers/Gd/Modifiers/DestroyModifierTest.php index 52b48fc1..66bafabf 100644 --- a/tests/Drivers/Gd/Modifiers/DestroyModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/DestroyModifierTest.php @@ -3,11 +3,14 @@ namespace Intervention\Image\Tests\Drivers\Gd\Modifiers; use GdImage; -use Intervention\Image\Drivers\Gd\Image; use Intervention\Image\Drivers\Gd\Modifiers\DestroyModifier; use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateGdTestImage; + +/** + * @covers \Intervention\Image\Drivers\Gd\Modifiers\DestroyModifier + */ class DestroyModifierTest extends TestCase { use CanCreateGdTestImage; diff --git a/tests/Drivers/Gd/Modifiers/FillModifierTest.php b/tests/Drivers/Gd/Modifiers/FillModifierTest.php index a1f8c049..ae219b54 100644 --- a/tests/Drivers/Gd/Modifiers/FillModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/FillModifierTest.php @@ -8,6 +8,10 @@ use Intervention\Image\Geometry\Point; use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateGdTestImage; + +/** + * @covers \Intervention\Image\Drivers\Gd\Modifiers\FillModifier + */ class FillModifierTest extends TestCase { use CanCreateGdTestImage; diff --git a/tests/Drivers/Gd/Modifiers/FitModifierTest.php b/tests/Drivers/Gd/Modifiers/FitModifierTest.php index 89056042..ffdc6831 100644 --- a/tests/Drivers/Gd/Modifiers/FitModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/FitModifierTest.php @@ -2,13 +2,14 @@ namespace Intervention\Image\Tests\Drivers\Gd\Modifiers; -use Intervention\Image\Drivers\Gd\Image; use Intervention\Image\Drivers\Gd\Modifiers\FitModifier; -use Intervention\Image\Geometry\Point; -use Intervention\Image\Geometry\Size; use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateGdTestImage; + +/** + * @covers \Intervention\Image\Drivers\Gd\Modifiers\FitModifier + */ class FitModifierTest extends TestCase { use CanCreateGdTestImage; diff --git a/tests/Drivers/Gd/Modifiers/GreyscaleModifierTest.php b/tests/Drivers/Gd/Modifiers/GreyscaleModifierTest.php index 9302d460..83155ea1 100644 --- a/tests/Drivers/Gd/Modifiers/GreyscaleModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/GreyscaleModifierTest.php @@ -2,11 +2,14 @@ namespace Intervention\Image\Tests\Drivers\Gd\Modifiers; -use Intervention\Image\Tests\TestCase; -use Intervention\Image\Drivers\Gd\Image; use Intervention\Image\Drivers\Gd\Modifiers\GreyscaleModifier; +use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateGdTestImage; + +/** + * @covers \Intervention\Image\Drivers\Gd\Modifiers\GreyscaleModifier + */ class GreyscaleModifierTest extends TestCase { use CanCreateGdTestImage; diff --git a/tests/Drivers/Gd/Modifiers/InvertModifierTest.php b/tests/Drivers/Gd/Modifiers/InvertModifierTest.php index 6429f3fb..089db30f 100644 --- a/tests/Drivers/Gd/Modifiers/InvertModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/InvertModifierTest.php @@ -2,11 +2,14 @@ namespace Intervention\Image\Tests\Drivers\Gd\Modifiers; -use Intervention\Image\Tests\TestCase; -use Intervention\Image\Drivers\Gd\Image; use Intervention\Image\Drivers\Gd\Modifiers\InvertModifier; +use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateGdTestImage; + +/** + * @covers \Intervention\Image\Drivers\Gd\Modifiers\InvertModifier + */ class InvertModifierTest extends TestCase { use CanCreateGdTestImage; diff --git a/tests/Drivers/Gd/Modifiers/PixelateModifierTest.php b/tests/Drivers/Gd/Modifiers/PixelateModifierTest.php index 0fc4a892..2f83c07c 100644 --- a/tests/Drivers/Gd/Modifiers/PixelateModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/PixelateModifierTest.php @@ -2,11 +2,14 @@ namespace Intervention\Image\Tests\Drivers\Gd\Modifiers; -use Intervention\Image\Drivers\Gd\Image; use Intervention\Image\Drivers\Gd\Modifiers\PixelateModifier; use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateGdTestImage; + +/** + * @covers \Intervention\Image\Drivers\Gd\Modifiers\PixelateModifier + */ class PixelateModifierTest extends TestCase { use CanCreateGdTestImage; diff --git a/tests/Drivers/Gd/Modifiers/PlaceModifierTest.php b/tests/Drivers/Gd/Modifiers/PlaceModifierTest.php index bd1d3e0b..ec37c78c 100644 --- a/tests/Drivers/Gd/Modifiers/PlaceModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/PlaceModifierTest.php @@ -2,12 +2,14 @@ namespace Intervention\Image\Tests\Drivers\Gd\Modifiers; -use Intervention\Image\Drivers\Gd\Image; use Intervention\Image\Drivers\Gd\Modifiers\PlaceModifier; -use Intervention\Image\Geometry\Size; use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateGdTestImage; + +/** + * @covers \Intervention\Image\Drivers\Gd\Modifiers\PlaceModifier + */ class PlaceModifierTest extends TestCase { use CanCreateGdTestImage; diff --git a/tests/Drivers/Gd/Modifiers/ResizeModifierTest.php b/tests/Drivers/Gd/Modifiers/ResizeModifierTest.php index ad4a4a9c..642e3667 100644 --- a/tests/Drivers/Gd/Modifiers/ResizeModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/ResizeModifierTest.php @@ -2,12 +2,14 @@ namespace Intervention\Image\Tests\Drivers\Gd\Modifiers; -use Intervention\Image\Drivers\Gd\Image; use Intervention\Image\Drivers\Gd\Modifiers\ResizeModifier; -use Intervention\Image\Geometry\Size; use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateGdTestImage; + +/** + * @covers \Intervention\Image\Drivers\Gd\Modifiers\ResizeModifier + */ class ResizeModifierTest extends TestCase { use CanCreateGdTestImage; diff --git a/tests/Drivers/Gd/Modifiers/SharpenModifierTest.php b/tests/Drivers/Gd/Modifiers/SharpenModifierTest.php index 83d52090..2966c0c1 100644 --- a/tests/Drivers/Gd/Modifiers/SharpenModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/SharpenModifierTest.php @@ -2,11 +2,14 @@ namespace Intervention\Image\Tests\Drivers\Gd\Modifiers; -use Intervention\Image\Tests\TestCase; -use Intervention\Image\Drivers\Gd\Image; use Intervention\Image\Drivers\Gd\Modifiers\SharpenModifier; +use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateGdTestImage; + +/** + * @covers \Intervention\Image\Drivers\Gd\Modifiers\SharpenModifier + */ class SharpenModifierTest extends TestCase { use CanCreateGdTestImage; diff --git a/tests/Drivers/Imagick/ColorTest.php b/tests/Drivers/Imagick/ColorTest.php index bf889f0c..2b981b41 100644 --- a/tests/Drivers/Imagick/ColorTest.php +++ b/tests/Drivers/Imagick/ColorTest.php @@ -6,6 +6,9 @@ use ImagickPixel; use Intervention\Image\Drivers\Imagick\Color; use Intervention\Image\Tests\TestCase; +/** + * @covers \Intervention\Image\Drivers\Imagick\Color + */ class ColorTest extends TestCase { protected function getTestColor(int $r = 0, int $g = 0, int $b = 0, float $a = 1): Color diff --git a/tests/Drivers/Imagick/Decoders/ArrayColorDecoderTest.php b/tests/Drivers/Imagick/Decoders/ArrayColorDecoderTest.php index e0e16800..ec63dc46 100644 --- a/tests/Drivers/Imagick/Decoders/ArrayColorDecoderTest.php +++ b/tests/Drivers/Imagick/Decoders/ArrayColorDecoderTest.php @@ -6,6 +6,9 @@ use Intervention\Image\Drivers\Imagick\Color; use Intervention\Image\Drivers\Imagick\Decoders\ArrayColorDecoder; use Intervention\Image\Tests\TestCase; +/** + * @covers \Intervention\Image\Drivers\Imagick\Decoders\ArrayColorDecoder + */ class ArrayColorDecoderTest extends TestCase { public function testDecode(): void diff --git a/tests/Drivers/Imagick/Decoders/ImageObjectDecoderTest.php b/tests/Drivers/Imagick/Decoders/ImageObjectDecoderTest.php index 1f3ad8ec..3039a4a0 100644 --- a/tests/Drivers/Imagick/Decoders/ImageObjectDecoderTest.php +++ b/tests/Drivers/Imagick/Decoders/ImageObjectDecoderTest.php @@ -7,6 +7,9 @@ use Intervention\Image\Drivers\Imagick\Image; use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateImagickTestImage; +/** + * @covers \Intervention\Image\Drivers\Imagick\Decoders\ImageObjectDecoder + */ class ImageObjectDecoderTest extends TestCase { use CanCreateImagickTestImage; diff --git a/tests/Drivers/Imagick/Decoders/RgbStringColorDecoderTest.php b/tests/Drivers/Imagick/Decoders/RgbStringColorDecoderTest.php index 07f8e652..60c6152d 100644 --- a/tests/Drivers/Imagick/Decoders/RgbStringColorDecoderTest.php +++ b/tests/Drivers/Imagick/Decoders/RgbStringColorDecoderTest.php @@ -6,6 +6,9 @@ use Intervention\Image\Drivers\Imagick\Color; use Intervention\Image\Drivers\Imagick\Decoders\RgbStringColorDecoder; use Intervention\Image\Tests\TestCase; +/** + * @covers \Intervention\Image\Drivers\Imagick\Decoders\RgbStringColorDecoder + */ class RgbStringColorDecoderTest extends TestCase { public function testDecodeRgb(): void diff --git a/tests/Drivers/Imagick/Decoders/TransparentColorDecoderTest.php b/tests/Drivers/Imagick/Decoders/TransparentColorDecoderTest.php index 915842bd..ddc0659d 100644 --- a/tests/Drivers/Imagick/Decoders/TransparentColorDecoderTest.php +++ b/tests/Drivers/Imagick/Decoders/TransparentColorDecoderTest.php @@ -6,6 +6,9 @@ use Intervention\Image\Drivers\Imagick\Color; use Intervention\Image\Drivers\Imagick\Decoders\TransparentColorDecoder; use Intervention\Image\Tests\TestCase; +/** + * @covers \Intervention\Image\Drivers\Imagick\Decoders\TransparentColorDecoder + */ class TransparentColorDecoderTest extends TestCase { public function testDecode(): void diff --git a/tests/Drivers/Imagick/Encoders/GifEncoderTest.php b/tests/Drivers/Imagick/Encoders/GifEncoderTest.php index 9e51820e..ecda07f4 100644 --- a/tests/Drivers/Imagick/Encoders/GifEncoderTest.php +++ b/tests/Drivers/Imagick/Encoders/GifEncoderTest.php @@ -12,6 +12,9 @@ use Intervention\Image\Tests\TestCase; use Intervention\MimeSniffer\MimeSniffer; use Intervention\MimeSniffer\Types\ImageGif; +/** + * @covers \Intervention\Image\Drivers\Imagick\Encoders\GifEncoder + */ class GifEncoderTest extends TestCase { protected function getTestImage(): Image diff --git a/tests/Drivers/Imagick/Encoders/JpegEncoderTest.php b/tests/Drivers/Imagick/Encoders/JpegEncoderTest.php index 6c6f0967..704e06d9 100644 --- a/tests/Drivers/Imagick/Encoders/JpegEncoderTest.php +++ b/tests/Drivers/Imagick/Encoders/JpegEncoderTest.php @@ -12,6 +12,9 @@ use Intervention\Image\Tests\TestCase; use Intervention\MimeSniffer\MimeSniffer; use Intervention\MimeSniffer\Types\ImageJpeg; +/** + * @covers \Intervention\Image\Drivers\Imagick\Encoders\JpegEncoder + */ class JpegEncoderTest extends TestCase { protected function getTestImage(): Image diff --git a/tests/Drivers/Imagick/FrameTest.php b/tests/Drivers/Imagick/FrameTest.php index e5764276..1eaae1f2 100644 --- a/tests/Drivers/Imagick/FrameTest.php +++ b/tests/Drivers/Imagick/FrameTest.php @@ -9,6 +9,9 @@ use Intervention\Image\Drivers\Imagick\Image; use Intervention\Image\Geometry\Size; use Intervention\Image\Tests\TestCase; +/** + * @covers \Intervention\Image\Drivers\Imagick\Frame + */ class FrameTest extends TestCase { protected function getTestFrame(): Frame diff --git a/tests/Drivers/Imagick/ImageFactoryTest.php b/tests/Drivers/Imagick/ImageFactoryTest.php index d23cb443..d023182d 100644 --- a/tests/Drivers/Imagick/ImageFactoryTest.php +++ b/tests/Drivers/Imagick/ImageFactoryTest.php @@ -7,6 +7,9 @@ use Intervention\Image\Drivers\Imagick\Image; use Intervention\Image\Drivers\Imagick\ImageFactory; use Intervention\Image\Tests\TestCase; +/** + * @covers \Intervention\Image\Drivers\Imagick\ImageFactory + */ class ImageFactoryTest extends TestCase { public function testNewImage(): void diff --git a/tests/Drivers/Imagick/ImageTest.php b/tests/Drivers/Imagick/ImageTest.php index 17c7d706..95d8c060 100644 --- a/tests/Drivers/Imagick/ImageTest.php +++ b/tests/Drivers/Imagick/ImageTest.php @@ -10,6 +10,9 @@ use Intervention\Image\Drivers\Imagick\Image; use Intervention\Image\Geometry\Size; use Intervention\Image\Tests\TestCase; +/** + * @covers \Intervention\Image\Drivers\Imagick\Image + */ class ImageTest extends TestCase { protected Image $image; diff --git a/tests/Drivers/Imagick/InputHandlerTest.php b/tests/Drivers/Imagick/InputHandlerTest.php index 6bc689ba..89dbcb9c 100644 --- a/tests/Drivers/Imagick/InputHandlerTest.php +++ b/tests/Drivers/Imagick/InputHandlerTest.php @@ -2,15 +2,15 @@ namespace Intervention\Image\Tests\Drivers\Imagick; -use Intervention\Image\Collection; use Intervention\Image\Drivers\Imagick\Color; -use Intervention\Image\Drivers\Imagick\Frame; use Intervention\Image\Drivers\Imagick\Image; use Intervention\Image\Drivers\Imagick\InputHandler; use Intervention\Image\Exceptions\DecoderException; -use Intervention\Image\Geometry\Size; use Intervention\Image\Tests\TestCase; +/** + * @covers \Intervention\Image\Drivers\Imagick\InputHandler + */ class InputHandlerTest extends TestCase { public function testHandleEmptyString(): void diff --git a/tests/Drivers/Imagick/Modifiers/BlurModifierTest.php b/tests/Drivers/Imagick/Modifiers/BlurModifierTest.php index a1ff3750..4aab8279 100644 --- a/tests/Drivers/Imagick/Modifiers/BlurModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/BlurModifierTest.php @@ -2,11 +2,13 @@ namespace Intervention\Image\Tests\Drivers\Imagick\Modifiers; -use Intervention\Image\Tests\TestCase; -use Intervention\Image\Drivers\Imagick\Image; use Intervention\Image\Drivers\Imagick\Modifiers\BlurModifier; +use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateImagickTestImage; +/** + * @covers \Intervention\Image\Drivers\Imagick\Modifiers\BlurModifier + */ class BlurModifierTest extends TestCase { use CanCreateImagickTestImage; diff --git a/tests/Drivers/Imagick/Modifiers/BrightnessModifierTest.php b/tests/Drivers/Imagick/Modifiers/BrightnessModifierTest.php index f0e3dfc7..65e9cc79 100644 --- a/tests/Drivers/Imagick/Modifiers/BrightnessModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/BrightnessModifierTest.php @@ -2,11 +2,13 @@ namespace Intervention\Image\Tests\Drivers\Imagick\Modifiers; -use Intervention\Image\Tests\TestCase; -use Intervention\Image\Drivers\Imagick\Image; use Intervention\Image\Drivers\Imagick\Modifiers\BrightnessModifier; +use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateImagickTestImage; +/** + * @covers \Intervention\Image\Drivers\Imagick\Modifiers\BrightnessModifier + */ class BrightnessModifierTest extends TestCase { use CanCreateImagickTestImage; diff --git a/tests/Drivers/Imagick/Modifiers/ContrastModifierTest.php b/tests/Drivers/Imagick/Modifiers/ContrastModifierTest.php index 042c3f08..5f9be557 100644 --- a/tests/Drivers/Imagick/Modifiers/ContrastModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/ContrastModifierTest.php @@ -2,11 +2,13 @@ namespace Intervention\Image\Tests\Drivers\Imagick\Modifiers; -use Intervention\Image\Tests\TestCase; -use Intervention\Image\Drivers\Imagick\Image; use Intervention\Image\Drivers\Imagick\Modifiers\ContrastModifier; +use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateImagickTestImage; +/** + * @covers \Intervention\Image\Drivers\Imagick\Modifiers\ContrastModifier + */ class ContrastModifierTest extends TestCase { use CanCreateImagickTestImage; diff --git a/tests/Drivers/Imagick/Modifiers/DestroyModifierTest.php b/tests/Drivers/Imagick/Modifiers/DestroyModifierTest.php index 6b9d3efe..8df1066e 100644 --- a/tests/Drivers/Imagick/Modifiers/DestroyModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/DestroyModifierTest.php @@ -3,11 +3,13 @@ namespace Intervention\Image\Tests\Drivers\Imagick\Modifiers; use Imagick; -use Intervention\Image\Tests\TestCase; -use Intervention\Image\Drivers\Imagick\Image; use Intervention\Image\Drivers\Imagick\Modifiers\DestroyModifier; +use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateImagickTestImage; +/** + * @covers \Intervention\Image\Drivers\Imagick\Modifiers\DestroyModifier + */ class DestroyModifierTest extends TestCase { use CanCreateImagickTestImage; diff --git a/tests/Drivers/Imagick/Modifiers/FillModifierTest.php b/tests/Drivers/Imagick/Modifiers/FillModifierTest.php index 4c5a8d15..a112386a 100644 --- a/tests/Drivers/Imagick/Modifiers/FillModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/FillModifierTest.php @@ -4,12 +4,14 @@ namespace Intervention\Image\Tests\Drivers\Imagick\Modifiers; use ImagickPixel; use Intervention\Image\Drivers\Imagick\Color; -use Intervention\Image\Drivers\Imagick\Image; use Intervention\Image\Drivers\Imagick\Modifiers\FillModifier; use Intervention\Image\Geometry\Point; use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateImagickTestImage; +/** + * @covers \Intervention\Image\Drivers\Imagick\Modifiers\FillModifier + */ class FillModifierTest extends TestCase { use CanCreateImagickTestImage; diff --git a/tests/Drivers/Imagick/Modifiers/FitModifierTest.php b/tests/Drivers/Imagick/Modifiers/FitModifierTest.php index aeb4ef31..3be86f64 100644 --- a/tests/Drivers/Imagick/Modifiers/FitModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/FitModifierTest.php @@ -2,13 +2,13 @@ namespace Intervention\Image\Tests\Drivers\Imagick\Modifiers; -use Intervention\Image\Drivers\Imagick\Image; use Intervention\Image\Drivers\Imagick\Modifiers\FitModifier; -use Intervention\Image\Geometry\Point; -use Intervention\Image\Geometry\Size; use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateImagickTestImage; +/** + * @covers \Intervention\Image\Drivers\Imagick\Modifiers\FitModifier + */ class FitModifierTest extends TestCase { use CanCreateImagickTestImage; diff --git a/tests/Drivers/Imagick/Modifiers/GreyscaleModifierTest.php b/tests/Drivers/Imagick/Modifiers/GreyscaleModifierTest.php index 64b5be61..a46f3d7c 100644 --- a/tests/Drivers/Imagick/Modifiers/GreyscaleModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/GreyscaleModifierTest.php @@ -2,11 +2,13 @@ namespace Intervention\Image\Tests\Drivers\Imagick\Modifiers; -use Intervention\Image\Tests\TestCase; -use Intervention\Image\Drivers\Imagick\Image; use Intervention\Image\Drivers\Imagick\Modifiers\GreyscaleModifier; +use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateImagickTestImage; +/** + * @covers \Intervention\Image\Drivers\Imagick\Modifiers\GreyscaleModifier + */ class GreyscaleModifierTest extends TestCase { use CanCreateImagickTestImage; diff --git a/tests/Drivers/Imagick/Modifiers/InvertModifierTest.php b/tests/Drivers/Imagick/Modifiers/InvertModifierTest.php index c167542c..2fdf28d5 100644 --- a/tests/Drivers/Imagick/Modifiers/InvertModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/InvertModifierTest.php @@ -2,11 +2,13 @@ namespace Intervention\Image\Tests\Drivers\Imagick\Modifiers; -use Intervention\Image\Tests\TestCase; -use Intervention\Image\Drivers\Imagick\Image; use Intervention\Image\Drivers\Imagick\Modifiers\InvertModifier; +use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateImagickTestImage; +/** + * @covers \Intervention\Image\Drivers\Imagick\Modifiers\InvertModifier + */ class InvertModifierTest extends TestCase { use CanCreateImagickTestImage; diff --git a/tests/Drivers/Imagick/Modifiers/PixelateModifierTest.php b/tests/Drivers/Imagick/Modifiers/PixelateModifierTest.php index d0d9d993..a1a1de23 100644 --- a/tests/Drivers/Imagick/Modifiers/PixelateModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/PixelateModifierTest.php @@ -2,11 +2,13 @@ namespace Intervention\Image\Tests\Drivers\Imagick\Modifiers; -use Intervention\Image\Drivers\Imagick\Image; use Intervention\Image\Drivers\Imagick\Modifiers\PixelateModifier; use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateImagickTestImage; +/** + * @covers \Intervention\Image\Drivers\Imagick\Modifiers\PixelateModifier + */ class PixelateModifierTest extends TestCase { use CanCreateImagickTestImage; diff --git a/tests/Drivers/Imagick/Modifiers/PlaceModifierTest.php b/tests/Drivers/Imagick/Modifiers/PlaceModifierTest.php index e6b66dd5..de186231 100644 --- a/tests/Drivers/Imagick/Modifiers/PlaceModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/PlaceModifierTest.php @@ -2,12 +2,13 @@ namespace Intervention\Image\Tests\Drivers\Imagick\Modifiers; -use Intervention\Image\Drivers\Imagick\Image; use Intervention\Image\Drivers\Imagick\Modifiers\PlaceModifier; -use Intervention\Image\Geometry\Size; use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateImagickTestImage; +/** + * @covers \Intervention\Image\Drivers\Imagick\Modifiers\PlaceModifier + */ class PlaceModifierTest extends TestCase { use CanCreateImagickTestImage; diff --git a/tests/Drivers/Imagick/Modifiers/ResizeModifierTest.php b/tests/Drivers/Imagick/Modifiers/ResizeModifierTest.php index 7f2bb201..27b358de 100644 --- a/tests/Drivers/Imagick/Modifiers/ResizeModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/ResizeModifierTest.php @@ -2,13 +2,14 @@ namespace Intervention\Image\Tests\Drivers\Imagick\Modifiers; -use Intervention\Image\Drivers\Imagick\Image; use Intervention\Image\Drivers\Imagick\Modifiers\ResizeModifier; -use Intervention\Image\Geometry\Size; use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateImagickTestImage; -class CropResizeModifierTest extends TestCase +/** + * @covers \Intervention\Image\Drivers\Imagick\Modifiers\ResizeModifier + */ +class ResizeModifierTest extends TestCase { use CanCreateImagickTestImage; diff --git a/tests/Drivers/Imagick/Modifiers/SharpenModifierTest.php b/tests/Drivers/Imagick/Modifiers/SharpenModifierTest.php index f2e23bd1..cab62e24 100644 --- a/tests/Drivers/Imagick/Modifiers/SharpenModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/SharpenModifierTest.php @@ -2,11 +2,13 @@ namespace Intervention\Image\Tests\Drivers\Imagick\Modifiers; -use Intervention\Image\Tests\TestCase; -use Intervention\Image\Drivers\Imagick\Image; use Intervention\Image\Drivers\Imagick\Modifiers\SharpenModifier; +use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateImagickTestImage; +/** + * @covers \Intervention\Image\Drivers\Imagick\Modifiers\SharpenModifier + */ class SharpenModifierTest extends TestCase { use CanCreateImagickTestImage; diff --git a/tests/EncodedImageTest.php b/tests/EncodedImageTest.php index 20c87ee4..a848ee95 100644 --- a/tests/EncodedImageTest.php +++ b/tests/EncodedImageTest.php @@ -4,6 +4,9 @@ namespace Intervention\Image\Tests; use Intervention\Image\EncodedImage; +/** + * @covers \Intervention\Image\EncodedImage + */ class EncodedImageTest extends TestCase { public function testConstructor() diff --git a/tests/Geometry/PointTest.php b/tests/Geometry/PointTest.php index a1d6423d..8f95d81d 100644 --- a/tests/Geometry/PointTest.php +++ b/tests/Geometry/PointTest.php @@ -5,6 +5,9 @@ namespace Intervention\Image\Tests\Geometry; use Intervention\Image\Geometry\Point; use Intervention\Image\Tests\TestCase; +/** + * @covers \Intervention\Image\Geometry\Point + */ class PointTest extends TestCase { public function testConstructor() diff --git a/tests/Geometry/ResizerTest.php b/tests/Geometry/ResizerTest.php index 9397d44d..f1ac5edb 100644 --- a/tests/Geometry/ResizerTest.php +++ b/tests/Geometry/ResizerTest.php @@ -7,6 +7,9 @@ use Intervention\Image\Geometry\Resizer; use Intervention\Image\Geometry\Size; use PHPUnit\Framework\TestCase; +/** + * @covers \Intervention\Image\Geometry\Resizer + */ class ResizerTest extends TestCase { public function testMake(): void diff --git a/tests/Geometry/SizeTest.php b/tests/Geometry/SizeTest.php index 6ae5f22c..68dbe2cd 100644 --- a/tests/Geometry/SizeTest.php +++ b/tests/Geometry/SizeTest.php @@ -2,12 +2,12 @@ namespace Intervention\Image\Tests\Geometry; +use Intervention\Image\Geometry\{Point, Size,}; use Intervention\Image\Tests\TestCase; -use Intervention\Image\Geometry\{ - Size, - Point, -}; +/** + * @covers \Intervention\Image\Geometry\Size + */ class SizeTest extends TestCase { public function testConstructor() diff --git a/tests/ImageManagerTest.php b/tests/ImageManagerTest.php index d05fb12e..1c2e6db2 100644 --- a/tests/ImageManagerTest.php +++ b/tests/ImageManagerTest.php @@ -5,6 +5,9 @@ namespace Intervention\Image\Tests; use Intervention\Image\ImageManager; use Intervention\Image\Interfaces\ImageInterface; +/** + * @covers \Intervention\Image\ImageManager + */ class ImageManagerTest extends TestCase { public function testConstructor() From f031c026ef9843b2f2ff59f840beaa2fcc23a44c Mon Sep 17 00:00:00 2001 From: Gwendolen Lynch Date: Thu, 23 Dec 2021 07:04:00 +0100 Subject: [PATCH 099/476] Add & update abstract class tests --- .../Abstract}/AbstractImageTest.php | 240 +++++++++++++++++- .../Abstract/AbstractInputHandlerTest.php | 44 ++++ .../Abstract/Decoders/AbstractDecoderTest.php | 46 ++++ .../Abstract/Encoders/AbstractEncoderTest.php | 48 ++++ .../Modifiers/AbstractFitModifierTest.php | 66 +++++ .../Modifiers/AbstractPadModifierTest.php | 67 +++++ .../Modifiers/AbstractRotateModifierTest.php | 80 ++++++ 7 files changed, 590 insertions(+), 1 deletion(-) rename tests/{ => Drivers/Abstract}/AbstractImageTest.php (51%) create mode 100644 tests/Drivers/Abstract/AbstractInputHandlerTest.php create mode 100644 tests/Drivers/Abstract/Decoders/AbstractDecoderTest.php create mode 100644 tests/Drivers/Abstract/Encoders/AbstractEncoderTest.php create mode 100644 tests/Drivers/Abstract/Modifiers/AbstractFitModifierTest.php create mode 100644 tests/Drivers/Abstract/Modifiers/AbstractPadModifierTest.php create mode 100644 tests/Drivers/Abstract/Modifiers/AbstractRotateModifierTest.php diff --git a/tests/AbstractImageTest.php b/tests/Drivers/Abstract/AbstractImageTest.php similarity index 51% rename from tests/AbstractImageTest.php rename to tests/Drivers/Abstract/AbstractImageTest.php index 340d0c23..bac5df9e 100644 --- a/tests/AbstractImageTest.php +++ b/tests/Drivers/Abstract/AbstractImageTest.php @@ -1,6 +1,6 @@ assertEquals(200, $img->getSize()->getHeight()); } + public function testSizeAlias(): void + { + $img = $this->abstractImageMock(); + $this->assertInstanceOf(Size::class, $img->getSize()); + $this->assertEquals(300, $img->size()->getWidth()); + $this->assertEquals(200, $img->size()->getHeight()); + } + public function testIsAnimated(): void { $img = Mockery::mock(AbstractImage::class, [new Collection()])->makePartial(); @@ -186,6 +195,51 @@ class AbstractImageTest extends TestCase $this->assertInstanceOf(ImageInterface::class, $result); } + public function testInvert(): void + { + $img = $this->abstractImageMock(); + + $modifier = Mockery::mock(ModifierInterface::class); + $modifier->shouldReceive('apply')->with($img)->andReturn($img); + + $img->shouldReceive('resolveDriverClass') + ->with('Modifiers\InvertModifier') + ->andReturn($modifier); + + $result = $img->invert(); + $this->assertInstanceOf(ImageInterface::class, $result); + } + + public function testBrightness(): void + { + $img = $this->abstractImageMock(); + + $modifier = Mockery::mock(ModifierInterface::class); + $modifier->shouldReceive('apply')->with($img)->andReturn($img); + + $img->shouldReceive('resolveDriverClass') + ->with('Modifiers\BrightnessModifier', 5) + ->andReturn($modifier); + + $result = $img->brightness(5); + $this->assertInstanceOf(ImageInterface::class, $result); + } + + public function testContrast(): void + { + $img = $this->abstractImageMock(); + + $modifier = Mockery::mock(ModifierInterface::class); + $modifier->shouldReceive('apply')->with($img)->andReturn($img); + + $img->shouldReceive('resolveDriverClass') + ->with('Modifiers\ContrastModifier', 5) + ->andReturn($modifier); + + $result = $img->contrast(5); + $this->assertInstanceOf(ImageInterface::class, $result); + } + public function testBlur(): void { $img = $this->abstractImageMock(); @@ -231,6 +285,56 @@ class AbstractImageTest extends TestCase $this->assertInstanceOf(ImageInterface::class, $result); } + public function testFill(): void + { + $img = $this->abstractImageMock(); + + $modifier = Mockery::mock(ModifierInterface::class); + $modifier->shouldReceive('apply')->with($img)->andReturn($img); + + $color = Mockery::mock(ColorInterface::class); + + $img->shouldReceive('handleInput') + ->with('abcdef') + ->andReturn($color); + $img->shouldReceive('resolveDriverClass') + ->with('Modifiers\FillModifier', $color, null) + ->andReturn($modifier); + + $result = $img->fill('abcdef'); + $this->assertInstanceOf(ImageInterface::class, $result); + } + + public function testPixelate(): void + { + $img = $this->abstractImageMock(); + + $modifier = Mockery::mock(ModifierInterface::class); + $modifier->shouldReceive('apply')->with($img)->andReturn($img); + + $img->shouldReceive('resolveDriverClass') + ->with('Modifiers\PixelateModifier', 42) + ->andReturn($modifier); + + $result = $img->pixelate(42); + $this->assertInstanceOf(ImageInterface::class, $result); + } + + public function testSharpen(): void + { + $img = $this->abstractImageMock(); + + $modifier = Mockery::mock(ModifierInterface::class); + $modifier->shouldReceive('apply')->with($img)->andReturn($img); + + $img->shouldReceive('resolveDriverClass') + ->with('Modifiers\SharpenModifier', 7) + ->andReturn($modifier); + + $result = $img->sharpen(7); + $this->assertInstanceOf(ImageInterface::class, $result); + } + public function testPickColors(): void { $color = Mockery::mock(ColorInterface::class); @@ -239,4 +343,138 @@ class AbstractImageTest extends TestCase $result = $img->pickColors(1, 2); $this->assertInstanceOf(Collection::class, $result); } + + public function testResize(): void + { + $img = $this->abstractImageMock(); + + $modifier = Mockery::mock(ModifierInterface::class); + $modifier->shouldReceive('apply')->with($img)->andReturn($img); + + $img->shouldReceive('resolveDriverClass') + ->with('Modifiers\ResizeModifier', 200, 100) + ->andReturn($modifier); + + $result = $img->resize(200, 100); + $this->assertInstanceOf(ImageInterface::class, $result); + } + + public function testResizeDown(): void + { + $img = $this->abstractImageMock(); + + $modifier = Mockery::mock(ModifierInterface::class); + $modifier->shouldReceive('apply')->with($img)->andReturn($img); + + $img->shouldReceive('resolveDriverClass') + ->with('Modifiers\ResizeDownModifier', 200, 100) + ->andReturn($modifier); + + $result = $img->resizeDown(200, 100); + $this->assertInstanceOf(ImageInterface::class, $result); + } + + public function testScale(): void + { + $img = $this->abstractImageMock(); + + $modifier = Mockery::mock(ModifierInterface::class); + $modifier->shouldReceive('apply')->with($img)->andReturn($img); + + $img->shouldReceive('resolveDriverClass') + ->with('Modifiers\ScaleModifier', 200, 100) + ->andReturn($modifier); + + $result = $img->scale(200, 100); + $this->assertInstanceOf(ImageInterface::class, $result); + } + + public function testScaleDown(): void + { + $img = $this->abstractImageMock(); + + $modifier = Mockery::mock(ModifierInterface::class); + $modifier->shouldReceive('apply')->with($img)->andReturn($img); + + $img->shouldReceive('resolveDriverClass') + ->with('Modifiers\ScaleDownModifier', 200, 100) + ->andReturn($modifier); + + $result = $img->scaleDown(200, 100); + $this->assertInstanceOf(ImageInterface::class, $result); + } + + public function testFit(): void + { + $img = $this->abstractImageMock(); + + $modifier = Mockery::mock(ModifierInterface::class); + $modifier->shouldReceive('apply')->with($img)->andReturn($img); + + $img->shouldReceive('resolveDriverClass') + ->with('Modifiers\FitModifier', 200, 100, 'center') + ->andReturn($modifier); + + $result = $img->fit(200, 100, 'center'); + $this->assertInstanceOf(ImageInterface::class, $result); + } + + public function testFitDown(): void + { + $img = $this->abstractImageMock(); + + $modifier = Mockery::mock(ModifierInterface::class); + $modifier->shouldReceive('apply')->with($img)->andReturn($img); + + $img->shouldReceive('resolveDriverClass') + ->with('Modifiers\FitDownModifier', 200, 100, 'center') + ->andReturn($modifier); + + $result = $img->fitDown(200, 100, 'center'); + $this->assertInstanceOf(ImageInterface::class, $result); + } + + public function testPad(): void + { + $img = $this->abstractImageMock(); + + $modifier = Mockery::mock(ModifierInterface::class); + $modifier->shouldReceive('apply')->with($img)->andReturn($img); + + $img->shouldReceive('resolveDriverClass') + ->with('Modifiers\PadModifier', 200, 100, 'ffffff', 'center') + ->andReturn($modifier); + + $result = $img->pad(200, 100, 'ffffff', 'center'); + $this->assertInstanceOf(ImageInterface::class, $result); + } + + public function testPadDown(): void + { + $img = $this->abstractImageMock(); + + $modifier = Mockery::mock(ModifierInterface::class); + $modifier->shouldReceive('apply')->with($img)->andReturn($img); + + $img->shouldReceive('resolveDriverClass') + ->with('Modifiers\PadDownModifier', 200, 100, 'ffffff', 'center') + ->andReturn($modifier); + + $result = $img->padDown(200, 100, 'ffffff', 'center'); + $this->assertInstanceOf(ImageInterface::class, $result); + } + + public function testDestroy(): void + { + $img = $this->abstractImageMock(); + + $modifier = Mockery::mock(ModifierInterface::class); + $modifier->shouldReceive('apply')->with($img)->andReturn($img); + + $img->shouldReceive('resolveDriverClass') + ->with('Modifiers\DestroyModifier') + ->andReturn($modifier); + + $img->destroy(); + } } diff --git a/tests/Drivers/Abstract/AbstractInputHandlerTest.php b/tests/Drivers/Abstract/AbstractInputHandlerTest.php new file mode 100644 index 00000000..ee40bb97 --- /dev/null +++ b/tests/Drivers/Abstract/AbstractInputHandlerTest.php @@ -0,0 +1,44 @@ +shouldReceive('handle')->with('test image')->andReturn($image); + $chain->shouldReceive('decode')->with('test image')->andReturn(Mockery::mock(ImageInterface::class)); + + $modifier = $this->getModifier($chain); + $modifier->handle('test image'); + } + + private function getModifier(AbstractDecoder $chain): AbstractInputHandler + { + return new class ($chain) extends AbstractInputHandler { + public function __construct(private AbstractDecoder $chain) + { + // + } + + protected function chain(): AbstractDecoder + { + return $this->chain; + } + }; + } +} diff --git a/tests/Drivers/Abstract/Decoders/AbstractDecoderTest.php b/tests/Drivers/Abstract/Decoders/AbstractDecoderTest.php new file mode 100644 index 00000000..157c3474 --- /dev/null +++ b/tests/Drivers/Abstract/Decoders/AbstractDecoderTest.php @@ -0,0 +1,46 @@ +makePartial(); + $decoder->shouldReceive('decode')->with('input string')->andReturn(null); + + $decoder->handle('input string'); + } + + public function testHandleFail(): void + { + $decoder = Mockery::mock(AbstractDecoder::class, [])->makePartial()->shouldAllowMockingProtectedMethods(); + $decoder->shouldReceive('decode')->with('input string')->andThrow(DecoderException::class); + + $this->expectException(DecoderException::class); + $this->expectExceptionMessage('Unable to decode given input.'); + + $decoder->handle('input string'); + } + + public function testHandleFailWithSuccessor(): void + { + $successor = Mockery::mock(AbstractDecoder::class)->makePartial(); + $successor->shouldReceive('decode')->with('input string')->andReturn(null); + + $decoder = Mockery::mock(AbstractDecoder::class, [$successor])->makePartial()->shouldAllowMockingProtectedMethods(); + $decoder->shouldReceive('decode')->with('input string')->andThrow(DecoderException::class); + + $decoder->handle('input string'); + } +} diff --git a/tests/Drivers/Abstract/Encoders/AbstractEncoderTest.php b/tests/Drivers/Abstract/Encoders/AbstractEncoderTest.php new file mode 100644 index 00000000..09626609 --- /dev/null +++ b/tests/Drivers/Abstract/Encoders/AbstractEncoderTest.php @@ -0,0 +1,48 @@ +getAbstractEncoder()->getBuffered($callback)); + } + + public function testSetGetQuality(): void + { + $encoder = $this->getAbstractEncoder(); + $encoder->setQuality(55); + + static::assertSame(55, $encoder->getQuality()); + } + + private function getAbstractEncoder(): AbstractEncoder + { + return new class () extends AbstractEncoder implements EncoderInterface { + public function getBuffered(callable $callback): string + { + return parent::getBuffered($callback); + } + + public function encode(ImageInterface $image): EncodedImage + { + } + }; + } +} diff --git a/tests/Drivers/Abstract/Modifiers/AbstractFitModifierTest.php b/tests/Drivers/Abstract/Modifiers/AbstractFitModifierTest.php new file mode 100644 index 00000000..f8678f96 --- /dev/null +++ b/tests/Drivers/Abstract/Modifiers/AbstractFitModifierTest.php @@ -0,0 +1,66 @@ + [150, 100, 50, 100, 50, 0]; + yield '100x150' => [100, 150, 75, 150, 13, 0]; + } + + /** @dataProvider providerCropSize */ + public function testGetCropSize(int $width, int $height, int $expectedWidth, int $expectedHeight, int $expectedX, int $expectedY): void + { + $modifier = $this->getModifier(100, 200, 'center'); + + $image = (new ImageFactory())->newImage($width, $height); + $size = $modifier->getCropSize($image); + + static::assertSame($expectedWidth, $size->getWidth()); + static::assertSame($expectedHeight, $size->getHeight()); + static::assertSame($expectedX, $size->getPivot()->getX()); + static::assertSame($expectedY, $size->getPivot()->getY()); + } + + public function testGetResizeSize(): void + { + $modifier = $this->getModifier(200, 100, 'center'); + + $image = (new ImageFactory())->newImage(300, 200); + $size = $modifier->getCropSize($image); + $resize = $modifier->getResizeSize($size); + + static::assertSame(200, $resize->getWidth()); + static::assertSame(100, $resize->getHeight()); + static::assertSame(0, $resize->getPivot()->getX()); + static::assertSame(0, $resize->getPivot()->getY()); + } + + private function getModifier(int $width, int $height, string $position): AbstractFitModifier + { + return new class ($width, $height, $position) extends AbstractFitModifier { + public function getCropSize(ImageInterface $image): SizeInterface + { + return parent::getCropSize($image); + } + + public function getResizeSize(SizeInterface $size): SizeInterface + { + return parent::getResizeSize($size); + } + }; + } +} diff --git a/tests/Drivers/Abstract/Modifiers/AbstractPadModifierTest.php b/tests/Drivers/Abstract/Modifiers/AbstractPadModifierTest.php new file mode 100644 index 00000000..4308a3bf --- /dev/null +++ b/tests/Drivers/Abstract/Modifiers/AbstractPadModifierTest.php @@ -0,0 +1,67 @@ + [150, 100, 100, 67, 0, 67]; + yield '100x150' => [100, 150, 100, 150, 0, 25]; + } + + /** @dataProvider providerCropSize */ + public function testGetCropSize(int $width, int $height, int $expectedWidth, int $expectedHeight, int $expectedX, int $expectedY): void + { + $modifier = $this->getModifier(100, 200, 'ffffff', 'center'); + + $image = (new ImageFactory())->newImage($width, $height); + $size = $modifier->getCropSize($image); + + static::assertSame($expectedWidth, $size->getWidth()); + static::assertSame($expectedHeight, $size->getHeight()); + static::assertSame($expectedX, $size->getPivot()->getX()); + static::assertSame($expectedY, $size->getPivot()->getY()); + } + + public function testGetResizeSize(): void + { + $modifier = $this->getModifier(200, 100, 'ffffff', 'center'); + + $image = (new ImageFactory())->newImage(300, 200); + $resize = $modifier->getResizeSize($image); + + static::assertSame(200, $resize->getWidth()); + static::assertSame(100, $resize->getHeight()); + static::assertSame(0, $resize->getPivot()->getX()); + static::assertSame(0, $resize->getPivot()->getY()); + } + + private function getModifier(int $width, int $height, $background, string $position): AbstractPadModifier + { + return new class($width, $height, $background, $position) extends AbstractPadModifier { + public function getCropSize(ImageInterface $image): SizeInterface + { + return parent::getCropSize($image); + } + + public function getResizeSize(ImageInterface $image): SizeInterface + { + return parent::getResizeSize($image); + } + }; + } +} diff --git a/tests/Drivers/Abstract/Modifiers/AbstractRotateModifierTest.php b/tests/Drivers/Abstract/Modifiers/AbstractRotateModifierTest.php new file mode 100644 index 00000000..42de945b --- /dev/null +++ b/tests/Drivers/Abstract/Modifiers/AbstractRotateModifierTest.php @@ -0,0 +1,80 @@ + [0.0, 0]; + yield '90 degrees' => [90.0, 90]; + yield '180 degrees' => [180.0, 180]; + yield '270 degrees' => [270.0, 270]; + yield '360 degrees' => [0.0, 360]; + } + + /** @dataProvider providerRotationAngle */ + public function testRotationAngle(float $expected, int $angle): void + { + $modifier = $this->getModifier($angle, 'abcdef'); + + static::assertSame($expected, $modifier->rotationAngle()); + } + + public function testBackgroundColor(): void + { + $modifier = $this->getModifier(90, 'abcdef'); + $color = $modifier->backgroundColor(); + + static::assertSame(255, $color->red()); + } + + public function testBackgroundColorInvalidValueThrowsException(): void + { + $this->expectException(TypeException::class); + $this->expectExceptionMessage('Argument #2 must be a color value'); + + $modifier = $this->getModifier(90, 'bad value'); + $modifier->backgroundColor(); + } + + private function getModifier(float $angle, $background): AbstractRotateModifier + { + return new class ($angle, $background) extends AbstractRotateModifier { + public function rotationAngle(): float + { + return parent::rotationAngle(); + } + + public function backgroundColor(): ColorInterface + { + return parent::backgroundColor(); + } + + public function handleInput($input): ImageInterface|ColorInterface + { + if ($this->background === 'bad value') { + throw new DecoderException(); + } + + $color = Mockery::mock(ColorInterface::class); + $color->shouldReceive('red')->andReturn(255); + + return $color; + } + }; + } +} From d47e83fc0ed7b47f3c3512732ab95fcd5bd7228b Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 2 Jan 2022 10:21:10 +0100 Subject: [PATCH 100/476] Version contraint for intervention/gif --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index dabcb353..f26b1279 100644 --- a/composer.json +++ b/composer.json @@ -13,7 +13,7 @@ ], "require": { "php": "^8", - "intervention/gif": "dev-master", + "intervention/gif": "^3.0", "intervention/mimesniffer": "^0.4.2" }, "require-dev": { From 87267764b9d3f067419316fc06cf0f2e30bfcb6f Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 2 Jan 2022 10:25:40 +0100 Subject: [PATCH 101/476] Added minimum-stability --- composer.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/composer.json b/composer.json index f26b1279..b8f72450 100644 --- a/composer.json +++ b/composer.json @@ -29,5 +29,6 @@ "psr-4": { "Intervention\\Image\\Tests\\": "tests" } - } + }, + "minimum-stability": "alpha" } From 5a4e677b827623649c6d14362f883642302958d8 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 16 Jan 2022 18:13:56 +0100 Subject: [PATCH 102/476] Added EncodedImage::toString() --- src/EncodedImage.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/EncodedImage.php b/src/EncodedImage.php index 011c6f15..a0461d84 100644 --- a/src/EncodedImage.php +++ b/src/EncodedImage.php @@ -33,8 +33,13 @@ class EncodedImage return sprintf('data:%s;base64,%s', $this->mimetype, base64_encode($this->data)); } - public function __toString(): string + public function toString(): string { return $this->data; } + + public function __toString(): string + { + return $this->toString(); + } } From 4fc4e317176da0c9652f6e76df201642d061c669 Mon Sep 17 00:00:00 2001 From: Thomas Picquet Date: Mon, 31 Jan 2022 11:20:45 -0800 Subject: [PATCH 103/476] PHP 8.1 deprecation fix --- src/Collection.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Collection.php b/src/Collection.php index 5d669ae3..90fbefaf 100644 --- a/src/Collection.php +++ b/src/Collection.php @@ -31,9 +31,9 @@ class Collection implements CollectionInterface, IteratorAggregate, Countable /** * Returns Iterator * - * @return array + * @return \Traversable */ - public function getIterator() + public function getIterator(): \Traversable { return new ArrayIterator($this->items); } From 66bb8a4b0f3c316e1407d36196556b841f3a23a3 Mon Sep 17 00:00:00 2001 From: Thomas Picquet Date: Mon, 31 Jan 2022 11:20:59 -0800 Subject: [PATCH 104/476] Fixed implicit converstion from float to int --- src/Drivers/Gd/Modifiers/BrightnessModifier.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Drivers/Gd/Modifiers/BrightnessModifier.php b/src/Drivers/Gd/Modifiers/BrightnessModifier.php index ee306bf0..443a41b6 100644 --- a/src/Drivers/Gd/Modifiers/BrightnessModifier.php +++ b/src/Drivers/Gd/Modifiers/BrightnessModifier.php @@ -15,7 +15,7 @@ class BrightnessModifier implements ModifierInterface public function apply(ImageInterface $image): ImageInterface { foreach ($image as $frame) { - imagefilter($frame->getCore(), IMG_FILTER_BRIGHTNESS, ($this->level * 2.55)); + imagefilter($frame->getCore(), IMG_FILTER_BRIGHTNESS, intval($this->level * 2.55)); } return $image; From 51e4fdb7e67576978d6e9899f65807f5dbd5d521 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Thu, 10 Feb 2022 16:08:47 +0000 Subject: [PATCH 105/476] Added modifiers to mirror images horizontally & vertically --- src/Drivers/Abstract/AbstractImage.php | 24 +++++++++++++ src/Drivers/Gd/Modifiers/FlipModifier.php | 18 ++++++++++ src/Drivers/Gd/Modifiers/FlopModifier.php | 18 ++++++++++ .../Imagick/Modifiers/FlipModifier.php | 18 ++++++++++ .../Imagick/Modifiers/FlopModifier.php | 18 ++++++++++ .../Gd/Modifiers/FlipFlopModifierTest.php | 34 +++++++++++++++++++ .../Modifiers/FlipFlopModifierTest.php | 34 +++++++++++++++++++ 7 files changed, 164 insertions(+) create mode 100644 src/Drivers/Gd/Modifiers/FlipModifier.php create mode 100644 src/Drivers/Gd/Modifiers/FlopModifier.php create mode 100644 src/Drivers/Imagick/Modifiers/FlipModifier.php create mode 100644 src/Drivers/Imagick/Modifiers/FlopModifier.php create mode 100644 tests/Drivers/Gd/Modifiers/FlipFlopModifierTest.php create mode 100644 tests/Drivers/Imagick/Modifiers/FlipFlopModifierTest.php diff --git a/src/Drivers/Abstract/AbstractImage.php b/src/Drivers/Abstract/AbstractImage.php index 4d11c373..55c3be56 100644 --- a/src/Drivers/Abstract/AbstractImage.php +++ b/src/Drivers/Abstract/AbstractImage.php @@ -153,6 +153,30 @@ abstract class AbstractImage ); } + /** + * Creates a vertical mirror image + * + * @return ImageInterface + */ + public function flip(): ImageInterface + { + return $this->modify( + $this->resolveDriverClass('Modifiers\FlipModifier') + ); + } + + /** + * Creates a horizontal mirror image + * + * @return ImageInterface + */ + public function flop(): ImageInterface + { + return $this->modify( + $this->resolveDriverClass('Modifiers\FlopModifier') + ); + } + public function place($element, string $position = 'top-left', int $offset_x = 0, int $offset_y = 0): ImageInterface { return $this->modify( diff --git a/src/Drivers/Gd/Modifiers/FlipModifier.php b/src/Drivers/Gd/Modifiers/FlipModifier.php new file mode 100644 index 00000000..a7eee567 --- /dev/null +++ b/src/Drivers/Gd/Modifiers/FlipModifier.php @@ -0,0 +1,18 @@ +getCore(), IMG_FLIP_VERTICAL); + } + + return $image; + } +} diff --git a/src/Drivers/Gd/Modifiers/FlopModifier.php b/src/Drivers/Gd/Modifiers/FlopModifier.php new file mode 100644 index 00000000..e926e3e0 --- /dev/null +++ b/src/Drivers/Gd/Modifiers/FlopModifier.php @@ -0,0 +1,18 @@ +getCore(), IMG_FLIP_HORIZONTAL); + } + + return $image; + } +} diff --git a/src/Drivers/Imagick/Modifiers/FlipModifier.php b/src/Drivers/Imagick/Modifiers/FlipModifier.php new file mode 100644 index 00000000..1c916393 --- /dev/null +++ b/src/Drivers/Imagick/Modifiers/FlipModifier.php @@ -0,0 +1,18 @@ +getCore()->flipImage(); + } + + return $image; + } +} diff --git a/src/Drivers/Imagick/Modifiers/FlopModifier.php b/src/Drivers/Imagick/Modifiers/FlopModifier.php new file mode 100644 index 00000000..cfb7160d --- /dev/null +++ b/src/Drivers/Imagick/Modifiers/FlopModifier.php @@ -0,0 +1,18 @@ +getCore()->flopImage(); + } + + return $image; + } +} diff --git a/tests/Drivers/Gd/Modifiers/FlipFlopModifierTest.php b/tests/Drivers/Gd/Modifiers/FlipFlopModifierTest.php new file mode 100644 index 00000000..0305f6a8 --- /dev/null +++ b/tests/Drivers/Gd/Modifiers/FlipFlopModifierTest.php @@ -0,0 +1,34 @@ +createTestImage('tile.png'); + $this->assertEquals('b4e000', $image->pickColor(0, 0)->toHex()); + $image->modify(new FlipModifier()); + $this->assertEquals('000000', $image->pickColor(0, 0)->toHex()); + } + + public function testFlopImage(): void + { + $image = $this->createTestImage('tile.png'); + $this->assertEquals('b4e000', $image->pickColor(0, 0)->toHex()); + $image->modify(new FlopModifier()); + $this->assertEquals('000000', $image->pickColor(0, 0)->toHex()); + } +} diff --git a/tests/Drivers/Imagick/Modifiers/FlipFlopModifierTest.php b/tests/Drivers/Imagick/Modifiers/FlipFlopModifierTest.php new file mode 100644 index 00000000..10ecab69 --- /dev/null +++ b/tests/Drivers/Imagick/Modifiers/FlipFlopModifierTest.php @@ -0,0 +1,34 @@ +createTestImage('tile.png'); + $this->assertEquals('b4e000', $image->pickColor(0, 0)->toHex()); + $image->modify(new FlipModifier()); + $this->assertEquals('000000', $image->pickColor(0, 0)->toHex()); + } + + public function testFlopImage(): void + { + $image = $this->createTestImage('tile.png'); + $this->assertEquals('b4e000', $image->pickColor(0, 0)->toHex()); + $image->modify(new FlopModifier()); + $this->assertEquals('000000', $image->pickColor(0, 0)->toHex()); + } +} From a1a07ac1e731cfed771feeb72624250226d5c293 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Fri, 11 Feb 2022 19:17:15 +0100 Subject: [PATCH 106/476] Added gamma modifier --- src/Drivers/Abstract/AbstractImage.php | 7 ++++++ src/Drivers/Gd/Modifiers/GammaModifier.php | 23 ++++++++++++++++++ .../Imagick/Modifiers/GammaModifier.php | 23 ++++++++++++++++++ .../Gd/Modifiers/GammaModifierTest.php | 24 +++++++++++++++++++ .../Imagick/Modifiers/GammaModifierTest.php | 24 +++++++++++++++++++ 5 files changed, 101 insertions(+) create mode 100644 src/Drivers/Gd/Modifiers/GammaModifier.php create mode 100644 src/Drivers/Imagick/Modifiers/GammaModifier.php create mode 100644 tests/Drivers/Gd/Modifiers/GammaModifierTest.php create mode 100644 tests/Drivers/Imagick/Modifiers/GammaModifierTest.php diff --git a/src/Drivers/Abstract/AbstractImage.php b/src/Drivers/Abstract/AbstractImage.php index 55c3be56..2b1b4d6a 100644 --- a/src/Drivers/Abstract/AbstractImage.php +++ b/src/Drivers/Abstract/AbstractImage.php @@ -139,6 +139,13 @@ abstract class AbstractImage ); } + public function gamma(float $gamma): ImageInterface + { + return $this->modify( + $this->resolveDriverClass('Modifiers\GammaModifier', $gamma) + ); + } + public function blur(int $amount = 5): ImageInterface { return $this->modify( diff --git a/src/Drivers/Gd/Modifiers/GammaModifier.php b/src/Drivers/Gd/Modifiers/GammaModifier.php new file mode 100644 index 00000000..f9208739 --- /dev/null +++ b/src/Drivers/Gd/Modifiers/GammaModifier.php @@ -0,0 +1,23 @@ +getCore(), 1, $this->gamma); + } + + return $image; + } +} diff --git a/src/Drivers/Imagick/Modifiers/GammaModifier.php b/src/Drivers/Imagick/Modifiers/GammaModifier.php new file mode 100644 index 00000000..ad813ace --- /dev/null +++ b/src/Drivers/Imagick/Modifiers/GammaModifier.php @@ -0,0 +1,23 @@ +getCore()->gammaImage($this->gamma); + } + + return $image; + } +} diff --git a/tests/Drivers/Gd/Modifiers/GammaModifierTest.php b/tests/Drivers/Gd/Modifiers/GammaModifierTest.php new file mode 100644 index 00000000..dec4de93 --- /dev/null +++ b/tests/Drivers/Gd/Modifiers/GammaModifierTest.php @@ -0,0 +1,24 @@ +createTestImage('trim.png'); + $this->assertEquals('00aef0', $image->pickColor(0, 0)->toHex()); + $image->modify(new GammaModifier(2.1)); + $this->assertEquals('00d5f8', $image->pickColor(0, 0)->toHex()); + } +} diff --git a/tests/Drivers/Imagick/Modifiers/GammaModifierTest.php b/tests/Drivers/Imagick/Modifiers/GammaModifierTest.php new file mode 100644 index 00000000..c3af78c0 --- /dev/null +++ b/tests/Drivers/Imagick/Modifiers/GammaModifierTest.php @@ -0,0 +1,24 @@ +createTestImage('trim.png'); + $this->assertEquals('00aef0', $image->pickColor(0, 0)->toHex()); + $image->modify(new GammaModifier(2.1)); + $this->assertEquals('00d5f8', $image->pickColor(0, 0)->toHex()); + } +} From 9d2318d828ce28c53d43d86e704401a7e7f0471b Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Fri, 11 Feb 2022 19:33:29 +0100 Subject: [PATCH 107/476] Added ModifierStack class --- src/ModifierStack.php | 28 ++++++++++++++++++++++++++++ tests/ModiferStackTest.php | 25 +++++++++++++++++++++++++ 2 files changed, 53 insertions(+) create mode 100644 src/ModifierStack.php create mode 100644 tests/ModiferStackTest.php diff --git a/src/ModifierStack.php b/src/ModifierStack.php new file mode 100644 index 00000000..13eac80a --- /dev/null +++ b/src/ModifierStack.php @@ -0,0 +1,28 @@ +modifiers as $modifier) { + $modifier->apply($image); + } + } + + public function push(ModifierInterface $modifier): self + { + $this->modifiers[] = $modifier; + + return $this; + } +} diff --git a/tests/ModiferStackTest.php b/tests/ModiferStackTest.php new file mode 100644 index 00000000..8577d3c1 --- /dev/null +++ b/tests/ModiferStackTest.php @@ -0,0 +1,25 @@ +assertInstanceOf(ModifierStack::class, $stack); + } + + public function testPush(): void + { + $stack = new ModifierStack([]); + $result = $stack->push(new GreyscaleModifier()); + $this->assertInstanceOf(ModifierStack::class, $result); + } +} From 03431a215576a5ecb9e6dee66aa06bb84f98b3ce Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Fri, 11 Feb 2022 19:54:13 +0100 Subject: [PATCH 108/476] Fixed bug in ModifierStack, Added tests for apply method --- src/ModifierStack.php | 2 ++ tests/ModiferStackTest.php | 17 +++++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/src/ModifierStack.php b/src/ModifierStack.php index 13eac80a..109fbfc9 100644 --- a/src/ModifierStack.php +++ b/src/ModifierStack.php @@ -17,6 +17,8 @@ class ModifierStack implements ModifierInterface foreach ($this->modifiers as $modifier) { $modifier->apply($image); } + + return $image; } public function push(ModifierInterface $modifier): self diff --git a/tests/ModiferStackTest.php b/tests/ModiferStackTest.php index 8577d3c1..afe99234 100644 --- a/tests/ModiferStackTest.php +++ b/tests/ModiferStackTest.php @@ -3,7 +3,9 @@ namespace Intervention\Image\Tests; use Intervention\Image\Drivers\Gd\Modifiers\GreyscaleModifier; +use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\ModifierStack; +use Mockery; /** * @covers \Intervention\Image\ModifierStack @@ -22,4 +24,19 @@ class ModifierStackTest extends TestCase $result = $stack->push(new GreyscaleModifier()); $this->assertInstanceOf(ModifierStack::class, $result); } + + public function testApply(): void + { + $image = Mockery::mock(ImageInterface::class); + + $modifier1 = Mockery::mock(AbstractColor::class)->makePartial(); + $modifier1->shouldReceive('apply')->once()->with($image); + + $modifier2 = Mockery::mock(AbstractColor::class)->makePartial(); + $modifier2->shouldReceive('apply')->once()->with($image); + + $stack = new ModifierStack([$modifier1, $modifier2]); + $result = $stack->apply($image); + $this->assertInstanceOf(ImageInterface::class, $image); + } } From 9828c9310c3db0cec0d498e279499c3c0556d5ed Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 19 Feb 2022 09:03:08 +0100 Subject: [PATCH 109/476] Removed unused code --- src/Drivers/Gd/Image.php | 7 ------- src/Drivers/Imagick/Image.php | 5 ----- 2 files changed, 12 deletions(-) diff --git a/src/Drivers/Gd/Image.php b/src/Drivers/Gd/Image.php index a8f00b98..38c42981 100644 --- a/src/Drivers/Gd/Image.php +++ b/src/Drivers/Gd/Image.php @@ -2,16 +2,9 @@ namespace Intervention\Image\Drivers\Gd; -use GdImage; -use Intervention\Image\Collection; use Intervention\Image\Drivers\Abstract\AbstractImage; -use Intervention\Image\Drivers\Gd\Frame; -use Intervention\Image\Geometry\Resizer; -use Intervention\Image\Geometry\Size; use Intervention\Image\Interfaces\ColorInterface; -use Intervention\Image\Interfaces\EncoderInterface; use Intervention\Image\Interfaces\ImageInterface; -use Intervention\Image\Interfaces\SizeInterface; use IteratorAggregate; class Image extends AbstractImage implements ImageInterface, IteratorAggregate diff --git a/src/Drivers/Imagick/Image.php b/src/Drivers/Imagick/Image.php index f04d8ae3..f6507eec 100644 --- a/src/Drivers/Imagick/Image.php +++ b/src/Drivers/Imagick/Image.php @@ -2,14 +2,9 @@ namespace Intervention\Image\Drivers\Imagick; -use Imagick; -use Intervention\Image\Collection; use Intervention\Image\Drivers\Abstract\AbstractImage; -use Intervention\Image\Drivers\Imagick\Frame; -use Intervention\Image\Geometry\Size; use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\ImageInterface; -use Intervention\Image\Interfaces\SizeInterface; use IteratorAggregate; class Image extends AbstractImage implements ImageInterface, IteratorAggregate From 70a9a556a51b936fb7a305956fa93758d2e01e86 Mon Sep 17 00:00:00 2001 From: Sergey Kudashev Date: Wed, 23 Mar 2022 02:00:58 +0300 Subject: [PATCH 110/476] Update run-tests.yml update the name. --- .github/workflows/run-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 5d7ae107..81bd9705 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -1,4 +1,4 @@ -name: run-tests +name: Tests on: [push] From d52630338b694808ce7e9ad1cbb50db799acdcd5 Mon Sep 17 00:00:00 2001 From: Sergey Kudashev Date: Wed, 23 Mar 2022 02:01:51 +0300 Subject: [PATCH 111/476] Update run-tests.yml add run on pull requests. --- .github/workflows/run-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 81bd9705..3ccb0557 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -1,6 +1,6 @@ name: Tests -on: [push] +on: [push, pull_request] jobs: run: From 47d0ada7d49612dd25e767b5e57cbf664949d0f9 Mon Sep 17 00:00:00 2001 From: Sergey Kudashev Date: Wed, 23 Mar 2022 02:02:49 +0300 Subject: [PATCH 112/476] Update run-tests.yml add the fail fast key. --- .github/workflows/run-tests.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 3ccb0557..d86d55e6 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -6,6 +6,7 @@ jobs: run: runs-on: ubuntu-latest strategy: + fail-fast: true matrix: php-versions: ['8.0'] name: Testing on PHP ${{ matrix.php-versions }} From 488f82398745a723148668038b225b4d20b807c2 Mon Sep 17 00:00:00 2001 From: Sergey Kudashev Date: Wed, 23 Mar 2022 02:03:13 +0300 Subject: [PATCH 113/476] Update run-tests.yml update matrix with php and stability keys. --- .github/workflows/run-tests.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index d86d55e6..42f001eb 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -8,7 +8,9 @@ jobs: strategy: fail-fast: true matrix: - php-versions: ['8.0'] + php: [ 8.0, 8.1 ] + stability: [ prefer-lowest, prefer-stable ] + name: Testing on PHP ${{ matrix.php-versions }} steps: - name: 'Checkout Project' From 2395773bb64482e3e94a16c912c38d7d4b5b9d08 Mon Sep 17 00:00:00 2001 From: Sergey Kudashev Date: Wed, 23 Mar 2022 02:03:40 +0300 Subject: [PATCH 114/476] Update run-tests.yml update the name key. --- .github/workflows/run-tests.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 42f001eb..7227ecfe 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -11,7 +11,8 @@ jobs: php: [ 8.0, 8.1 ] stability: [ prefer-lowest, prefer-stable ] - name: Testing on PHP ${{ matrix.php-versions }} + name: P${{ matrix.php }} - ${{ matrix.stability }} + steps: - name: 'Checkout Project' uses: actions/checkout@v2 From d1caaadfa5b1a474051b00881be4d62d8ee0a2d4 Mon Sep 17 00:00:00 2001 From: Sergey Kudashev Date: Wed, 23 Mar 2022 02:06:02 +0300 Subject: [PATCH 115/476] Update run-tests.yml update the checkout block. --- .github/workflows/run-tests.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 7227ecfe..ca59e66f 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -14,8 +14,9 @@ jobs: name: P${{ matrix.php }} - ${{ matrix.stability }} steps: - - name: 'Checkout Project' + - name: Checkout project uses: actions/checkout@v2 + - name: 'Setup Environment' uses: shivammathur/setup-php@v2 with: From 896a57a891b816c1d70cc7ba6bb5be6b89a65a0c Mon Sep 17 00:00:00 2001 From: Sergey Kudashev Date: Wed, 23 Mar 2022 02:06:50 +0300 Subject: [PATCH 116/476] Update run-tests.yml update the setup block. --- .github/workflows/run-tests.yml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index ca59e66f..35bded2d 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -17,12 +17,13 @@ jobs: - name: Checkout project uses: actions/checkout@v2 - - name: 'Setup Environment' + - name: Setup PHP uses: shivammathur/setup-php@v2 with: - php-version: ${{ matrix.php-versions }} - extensions: imagick,gd - tools: phpunit,composer + php-version: ${{ matrix.php }} + extensions: mbstring, gd, imagick + coverage: none + - name: 'Install Dependencies' run: composer install -o -q - name: 'Run PHPUnit' From dce69bad28063f410c0d0966852ac358bf6c0777 Mon Sep 17 00:00:00 2001 From: Sergey Kudashev Date: Wed, 23 Mar 2022 02:07:20 +0300 Subject: [PATCH 117/476] Update run-tests.yml update the install dependencies block. --- .github/workflows/run-tests.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 35bded2d..cc6ce83b 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -24,7 +24,8 @@ jobs: extensions: mbstring, gd, imagick coverage: none - - name: 'Install Dependencies' - run: composer install -o -q + - name: Install dependencies + run: composer update --${{ matrix.stability }} --prefer-dist --no-interaction + - name: 'Run PHPUnit' - run: vendor/bin/phpunit \ No newline at end of file + run: vendor/bin/phpunit From b19697b67cd7b8d4c3b63fa7edaba1a4892de472 Mon Sep 17 00:00:00 2001 From: Sergey Kudashev Date: Wed, 23 Mar 2022 02:10:57 +0300 Subject: [PATCH 118/476] Update run-tests.yml update the execute block. --- .github/workflows/run-tests.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index cc6ce83b..9f8012c4 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -27,5 +27,5 @@ jobs: - name: Install dependencies run: composer update --${{ matrix.stability }} --prefer-dist --no-interaction - - name: 'Run PHPUnit' - run: vendor/bin/phpunit + - name: Execute tests + run: vendor/bin/phpunit --no-coverage From 0846dd592d103bb23a738084b0afb1132eaab982 Mon Sep 17 00:00:00 2001 From: Sergey Kudashev Date: Wed, 23 Mar 2022 02:22:53 +0300 Subject: [PATCH 119/476] Update CanCreateImagickTestImage.php fix the create with decoder method name. --- tests/Traits/CanCreateImagickTestImage.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/Traits/CanCreateImagickTestImage.php b/tests/Traits/CanCreateImagickTestImage.php index 0ff42675..c097c50e 100644 --- a/tests/Traits/CanCreateImagickTestImage.php +++ b/tests/Traits/CanCreateImagickTestImage.php @@ -11,12 +11,12 @@ trait CanCreateImagickTestImage { public function createTestImage($filename = 'test.jpg'): Image { - return $this->testImageDecoder()->handle( + return $this->createWithImageDecoder()->handle( sprintf('%s/../images/%s', __DIR__, $filename) ); } - protected function testImageDecoder(): FilePathImageDecoder + protected function createWithImageDecoder(): FilePathImageDecoder { return new FilePathImageDecoder(); } From 8a4f8e40c508708f841ea4fd59a0b9301c7f13a2 Mon Sep 17 00:00:00 2001 From: Sergey Kudashev Date: Wed, 23 Mar 2022 02:23:44 +0300 Subject: [PATCH 120/476] Update CanCreateGdTestImage.php fix the create with decoder method name. --- tests/Traits/CanCreateGdTestImage.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/Traits/CanCreateGdTestImage.php b/tests/Traits/CanCreateGdTestImage.php index e2e5d81a..9d12ed2b 100644 --- a/tests/Traits/CanCreateGdTestImage.php +++ b/tests/Traits/CanCreateGdTestImage.php @@ -21,7 +21,7 @@ trait CanCreateGdTestImage public function createTestImage($filename = 'test.jpg'): Image { - return $this->testImageDecoder()->handle( + return $this->createWithImageDecoder()->handle( $this->getTestImagePath($filename) ); } @@ -41,7 +41,7 @@ trait CanCreateGdTestImage ])); } - protected function testImageDecoder(): FilePathImageDecoder + protected function createWithImageDecoder(): FilePathImageDecoder { return new FilePathImageDecoder(); } From 66af2ff050d850317fc1869bd11ca64a74983c4f Mon Sep 17 00:00:00 2001 From: Sergey Kudashev Date: Wed, 23 Mar 2022 02:25:05 +0300 Subject: [PATCH 121/476] Update run-tests.yml add cache composer dependencies. --- .github/workflows/run-tests.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 9f8012c4..f3bd3eb8 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -24,6 +24,17 @@ jobs: extensions: mbstring, gd, imagick coverage: none + - name: Get composer cache directory + id: composer-cache + run: echo "::set-output name=dir::$(composer config cache-files-dir)" + + - name: Cache composer dependencies + uses: actions/cache@v2 + with: + path: ${{ steps.composer-cache.outputs.dir }} + key: ${{ runner.os }}-php-${{ matrix.php }}-composer-${{ matrix.stability }}-${{ hashFiles('**/composer.json') }} + restore-keys: ${{ runner.os }}-php-${{ matrix.php }}-composer-${{ matrix.stability }}- + - name: Install dependencies run: composer update --${{ matrix.stability }} --prefer-dist --no-interaction From 2dc7f019c228ba56ab444278c4600287a40e32a2 Mon Sep 17 00:00:00 2001 From: Sergey Kudashev Date: Wed, 23 Mar 2022 09:52:05 +0300 Subject: [PATCH 122/476] Update Update run-tests.yml update the fail fast key. --- .github/workflows/run-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index f3bd3eb8..d68dce9e 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -6,7 +6,7 @@ jobs: run: runs-on: ubuntu-latest strategy: - fail-fast: true + fail-fast: false matrix: php: [ 8.0, 8.1 ] stability: [ prefer-lowest, prefer-stable ] From 9bf8840318a5ba46096beb75105ab2e7398fd021 Mon Sep 17 00:00:00 2001 From: Sergey Kudashev Date: Wed, 23 Mar 2022 10:43:15 +0300 Subject: [PATCH 123/476] Add phpunit.xml.dist file. --- phpunit.xml.dist | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 phpunit.xml.dist diff --git a/phpunit.xml.dist b/phpunit.xml.dist new file mode 100644 index 00000000..63454441 --- /dev/null +++ b/phpunit.xml.dist @@ -0,0 +1,23 @@ + + + + + ./tests/ + + + + + + src + + + From 30980a24c8b4275ac0d2c475f4fadf4405609461 Mon Sep 17 00:00:00 2001 From: Sergey Kudashev Date: Wed, 23 Mar 2022 10:44:02 +0300 Subject: [PATCH 124/476] Update .gitignore add build folder pattern. --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 95483750..8367435f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ +.idea/ +build/ +vendor/ .DS_Store composer.lock vendor/ From 428d98241dcea45a0d43622748ad354a0f93aa01 Mon Sep 17 00:00:00 2001 From: Sergey Kudashev Date: Wed, 23 Mar 2022 10:44:25 +0300 Subject: [PATCH 125/476] Update .gitignore add phpunit.xml pattern. --- .gitignore | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index 8367435f..3e4ac796 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,4 @@ build/ vendor/ .DS_Store composer.lock -vendor/ -.idea/ -.phpunit.result.cache \ No newline at end of file +phpunit.xml From e69b01dfe8fcffffcf56ee3fb5017cc25b9e6e3f Mon Sep 17 00:00:00 2001 From: Sergey Kudashev Date: Wed, 23 Mar 2022 10:45:41 +0300 Subject: [PATCH 126/476] Update .gitignore add .phpunit.result pattern. --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 3e4ac796..c7fe04e6 100644 --- a/.gitignore +++ b/.gitignore @@ -2,5 +2,6 @@ build/ vendor/ .DS_Store +.phpunit.result.cache composer.lock phpunit.xml From ca2a3c7d9fb6946eb9429efa405dae3431a6c6a5 Mon Sep 17 00:00:00 2001 From: Sergey Kudashev Date: Wed, 23 Mar 2022 15:52:39 +0300 Subject: [PATCH 127/476] Style FitModifier.php fix a typo in the comment. --- src/Drivers/Gd/Modifiers/FitModifier.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Drivers/Gd/Modifiers/FitModifier.php b/src/Drivers/Gd/Modifiers/FitModifier.php index 194376a5..0278416e 100644 --- a/src/Drivers/Gd/Modifiers/FitModifier.php +++ b/src/Drivers/Gd/Modifiers/FitModifier.php @@ -65,7 +65,7 @@ class FitModifier extends AbstractFitModifier implements ModifierInterface imagedestroy($current); - // set new content as recource + // set new content as resource $frame->setCore($modified); } } From b626022fa77016fc64d50ce2a156dd3b9241a61c Mon Sep 17 00:00:00 2001 From: Sergey Kudashev Date: Wed, 23 Mar 2022 16:11:38 +0300 Subject: [PATCH 128/476] Update GD\Color.php update transparency extraction. --- src/Drivers/Gd/Color.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Drivers/Gd/Color.php b/src/Drivers/Gd/Color.php index 46904b37..d4cde720 100644 --- a/src/Drivers/Gd/Color.php +++ b/src/Drivers/Gd/Color.php @@ -34,7 +34,7 @@ class Color extends AbstractColor implements ColorInterface public function toArray(): array { - $a = ($this->value >> 24) & 0xFF; + $a = ($this->value >> 24) & 0x7F; $r = ($this->value >> 16) & 0xFF; $g = ($this->value >> 8) & 0xFF; $b = $this->value & 0xFF; From adffed38814b80a7c6393c21851a8d73ee44f197 Mon Sep 17 00:00:00 2001 From: Sergey Kudashev Date: Thu, 24 Mar 2022 01:53:37 +0300 Subject: [PATCH 129/476] Update GD\ResizeModifier.php add transparency restore to pass the tests. --- src/Drivers/Gd/Modifiers/ResizeModifier.php | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/Drivers/Gd/Modifiers/ResizeModifier.php b/src/Drivers/Gd/Modifiers/ResizeModifier.php index b030a8e3..7646a22f 100644 --- a/src/Drivers/Gd/Modifiers/ResizeModifier.php +++ b/src/Drivers/Gd/Modifiers/ResizeModifier.php @@ -44,15 +44,14 @@ class ResizeModifier implements ModifierInterface $current = $frame->getCore(); // preserve transparency + imagealphablending($modified, false); $transIndex = imagecolortransparent($current); if ($transIndex != -1) { $rgba = imagecolorsforindex($modified, $transIndex); $transColor = imagecolorallocatealpha($modified, $rgba['red'], $rgba['green'], $rgba['blue'], 127); imagefill($modified, 0, 0, $transColor); - imagecolortransparent($modified, $transColor); } else { - imagealphablending($modified, false); imagesavealpha($modified, true); } @@ -72,6 +71,22 @@ class ResizeModifier implements ModifierInterface imagedestroy($current); + if ($transIndex != -1) { // @todo refactor because of duplication + imagecolortransparent($modified, $transIndex); + for ($y = 0; $y < $resizeTo->getHeight(); ++$y) { + for ($x = 0; $x < $resizeTo->getWidth(); ++$x) { + if (((imagecolorat($modified, $x, $y) >> 24) & 0x7F) >= 100) { + imagesetpixel( + $modified, + $x, + $y, + $transIndex + ); + } + } + } + } + // set new content as recource $frame->setCore($modified); } From ad0ad160420ac1a441b20c8c38e69f1b9457cbc6 Mon Sep 17 00:00:00 2001 From: Sergey Kudashev Date: Thu, 24 Mar 2022 01:54:13 +0300 Subject: [PATCH 130/476] Update GD\FitModifier.php add transparency restore to pass the tests. --- src/Drivers/Gd/Modifiers/FitModifier.php | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/Drivers/Gd/Modifiers/FitModifier.php b/src/Drivers/Gd/Modifiers/FitModifier.php index 0278416e..de0e9725 100644 --- a/src/Drivers/Gd/Modifiers/FitModifier.php +++ b/src/Drivers/Gd/Modifiers/FitModifier.php @@ -37,15 +37,14 @@ class FitModifier extends AbstractFitModifier implements ModifierInterface $current = $frame->getCore(); // preserve transparency + imagealphablending($modified, false); $transIndex = imagecolortransparent($current); if ($transIndex != -1) { $rgba = imagecolorsforindex($modified, $transIndex); $transColor = imagecolorallocatealpha($modified, $rgba['red'], $rgba['green'], $rgba['blue'], 127); imagefill($modified, 0, 0, $transColor); - imagecolortransparent($modified, $transColor); } else { - imagealphablending($modified, false); imagesavealpha($modified, true); } @@ -65,6 +64,22 @@ class FitModifier extends AbstractFitModifier implements ModifierInterface imagedestroy($current); + if ($transIndex != -1) { // @todo refactor because of duplication + imagecolortransparent($modified, $transIndex); + for ($y = 0; $y < $resize->getHeight(); ++$y) { + for ($x = 0; $x < $resize->getWidth(); ++$x) { + if (((imagecolorat($modified, $x, $y) >> 24) & 0x7F) >= 100) { + imagesetpixel( + $modified, + $x, + $y, + $transIndex + ); + } + } + } + } + // set new content as resource $frame->setCore($modified); } From 161a3229496de8df9e836e4c573830977c50969a Mon Sep 17 00:00:00 2001 From: Sergey Kudashev Date: Thu, 24 Mar 2022 01:59:46 +0300 Subject: [PATCH 131/476] Fix SizeInterface.php add the resize method to fit ResizeModifier usage. --- src/Interfaces/SizeInterface.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Interfaces/SizeInterface.php b/src/Interfaces/SizeInterface.php index 0e8ea82d..fd641fbe 100644 --- a/src/Interfaces/SizeInterface.php +++ b/src/Interfaces/SizeInterface.php @@ -9,6 +9,7 @@ interface SizeInterface public function getPivot(): PointInterface; public function setWidth(int $width): SizeInterface; public function setHeight(int $height): SizeInterface; + public function resize(?int $width = null, ?int $height = null): SizeInterface; public function getAspectRatio(): float; public function fitsInto(SizeInterface $size): bool; public function isLandscape(): bool; From decdbd0bfa6b9df16228c55421563a7a574ef358 Mon Sep 17 00:00:00 2001 From: Sergey Kudashev Date: Thu, 24 Mar 2022 02:19:46 +0300 Subject: [PATCH 132/476] Update run-tests.yml some experiments. --- .github/workflows/run-tests.yml | 62 +++++++++++++++++++++++++++++++-- 1 file changed, 59 insertions(+), 3 deletions(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index d68dce9e..57136c65 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -6,9 +6,16 @@ jobs: run: runs-on: ubuntu-latest strategy: - fail-fast: false + fail-fast: true matrix: - php: [ 8.0, 8.1 ] + php: [ '8.0', '8.1' ] + imagemagick: [ + '6.9.12-43', + '7.1.0-28' + ] + imagick: [ + '3.7.0' + ] stability: [ prefer-lowest, prefer-stable ] name: P${{ matrix.php }} - ${{ matrix.stability }} @@ -21,9 +28,55 @@ jobs: uses: shivammathur/setup-php@v2 with: php-version: ${{ matrix.php }} - extensions: mbstring, gd, imagick + extensions: mbstring, gd coverage: none + - name: Prepare environament for Imagemagick + run: | + sudo apt-get -y remove imagemagick imagemagick-6-common libmagic-dev + sudo apt-get update + sudo apt-get install -y libjpeg62-dev + sudo apt-get install -y libgif-dev + sudo apt-get install -y libtiff-dev + sudo apt-get install -y libpng-dev + sudo apt-get install -y libwebp-dev + sudo apt-get install -y libmagickwand-dev + + - name: Cache ImageMagick + uses: actions/cache@v2 + env: + cache-name: cache-ImageMagick + with: + path: ~/im/imagemagick-${{ matrix.imagemagick }} + key: ${{ runner.os }}-ImageMagick-${{ matrix.imagemagick }} + + - name: Install ImageMagick + run: | + curl -o /tmp/ImageMagick.tar.gz -sL https://www.imagemagick.org/download/ImageMagick-${{ matrix.imagemagick }}.tar.gz + ( + cd /tmp || exit 1 + tar xf ImageMagick.tar.gz + cd ImageMagick-${{ matrix.imagemagick }} + sudo ./configure --prefix=/opt/imagemagick + sudo make -j$(nproc) + sudo make install + ) + + - name: Install PHP ImageMagick extension + run: | + curl -o /tmp/imagick.tgz -sL http://pecl.php.net/get/imagick-${{ matrix.imagick }}.tgz + ( + cd /tmp || exit 1 + tar -xzf imagick.tgz + cd imagick-${{ matrix.imagick }} + phpize + sudo ./configure --with-imagick=/opt/imagemagick + sudo make -j$(nproc) + sudo make install + ) + sudo bash -c 'echo "extension=imagick.so" >> /etc/php/${{ matrix.php }}/cli/php.ini' + php --ri imagick; + - name: Get composer cache directory id: composer-cache run: echo "::set-output name=dir::$(composer config cache-files-dir)" @@ -38,5 +91,8 @@ jobs: - name: Install dependencies run: composer update --${{ matrix.stability }} --prefer-dist --no-interaction + - name: Which Imagick Version + run: php -r 'var_dump(Imagick::getVersion());' + - name: Execute tests run: vendor/bin/phpunit --no-coverage From d959e37d89edec25130b9c97de8be73d193044c2 Mon Sep 17 00:00:00 2001 From: Sergey Kudashev Date: Sat, 26 Mar 2022 10:55:07 +0300 Subject: [PATCH 133/476] Update run-tests.yml rename imagemagick cache name. --- .github/workflows/run-tests.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 57136c65..81c04811 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -44,8 +44,7 @@ jobs: - name: Cache ImageMagick uses: actions/cache@v2 - env: - cache-name: cache-ImageMagick + id: cache-imagemagick with: path: ~/im/imagemagick-${{ matrix.imagemagick }} key: ${{ runner.os }}-ImageMagick-${{ matrix.imagemagick }} From 80dcb4cbd1d5c539ecbd74100349c4235994ab72 Mon Sep 17 00:00:00 2001 From: Sergey Kudashev Date: Sat, 26 Mar 2022 10:56:26 +0300 Subject: [PATCH 134/476] Update run-tests.yml style file. --- .github/workflows/run-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 81c04811..455ee966 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -1,6 +1,6 @@ name: Tests -on: [push, pull_request] +on: [ push, pull_request ] jobs: run: From e988c46b6fe9b10993cfa20500ffca71192d31ac Mon Sep 17 00:00:00 2001 From: Sergey Kudashev Date: Sat, 26 Mar 2022 10:56:55 +0300 Subject: [PATCH 135/476] Update run-tests.yml update the fail fast key. --- .github/workflows/run-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 455ee966..abd92511 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -6,7 +6,7 @@ jobs: run: runs-on: ubuntu-latest strategy: - fail-fast: true + fail-fast: false matrix: php: [ '8.0', '8.1' ] imagemagick: [ From 81c9b6be7fec5e3850a4760431f2929e1a540d3d Mon Sep 17 00:00:00 2001 From: Sergey Kudashev Date: Sat, 26 Mar 2022 11:08:41 +0300 Subject: [PATCH 136/476] Update run-tests.yml change the configure path. --- .github/workflows/run-tests.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index abd92511..ece51ac4 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -56,7 +56,7 @@ jobs: cd /tmp || exit 1 tar xf ImageMagick.tar.gz cd ImageMagick-${{ matrix.imagemagick }} - sudo ./configure --prefix=/opt/imagemagick + sudo ./configure --prefix=~/im/imagemagick-${{ matrix.imagemagick }} sudo make -j$(nproc) sudo make install ) @@ -69,7 +69,7 @@ jobs: tar -xzf imagick.tgz cd imagick-${{ matrix.imagick }} phpize - sudo ./configure --with-imagick=/opt/imagemagick + sudo ./configure --with-imagick=~/im/imagemagick-${{ matrix.imagemagick }} sudo make -j$(nproc) sudo make install ) From d2b1cfe00f379b4ac200247cbc8d9f79c2dbc1b8 Mon Sep 17 00:00:00 2001 From: Sergey Kudashev Date: Sat, 26 Mar 2022 11:11:32 +0300 Subject: [PATCH 137/476] Update run-tests.yml add cache hit check. --- .github/workflows/run-tests.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index ece51ac4..76543ad3 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -50,6 +50,7 @@ jobs: key: ${{ runner.os }}-ImageMagick-${{ matrix.imagemagick }} - name: Install ImageMagick + if: steps.cache-imagemagick.outputs.cache-hit != 'true' run: | curl -o /tmp/ImageMagick.tar.gz -sL https://www.imagemagick.org/download/ImageMagick-${{ matrix.imagemagick }}.tar.gz ( From 3fbce7577f5f44bdfc7c3ce548e81715811fc3cd Mon Sep 17 00:00:00 2001 From: Sergey Kudashev Date: Sat, 26 Mar 2022 11:19:57 +0300 Subject: [PATCH 138/476] Fix run-tests.yml update the configure path absolute. --- .github/workflows/run-tests.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 76543ad3..740bf49e 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -46,7 +46,7 @@ jobs: uses: actions/cache@v2 id: cache-imagemagick with: - path: ~/im/imagemagick-${{ matrix.imagemagick }} + path: ${HOME}/im/imagemagick-${{ matrix.imagemagick }} key: ${{ runner.os }}-ImageMagick-${{ matrix.imagemagick }} - name: Install ImageMagick @@ -57,7 +57,7 @@ jobs: cd /tmp || exit 1 tar xf ImageMagick.tar.gz cd ImageMagick-${{ matrix.imagemagick }} - sudo ./configure --prefix=~/im/imagemagick-${{ matrix.imagemagick }} + sudo ./configure --prefix=${HOME}/im/imagemagick-${{ matrix.imagemagick }} sudo make -j$(nproc) sudo make install ) @@ -70,7 +70,7 @@ jobs: tar -xzf imagick.tgz cd imagick-${{ matrix.imagick }} phpize - sudo ./configure --with-imagick=~/im/imagemagick-${{ matrix.imagemagick }} + sudo ./configure --with-imagick=${HOME}/im/imagemagick-${{ matrix.imagemagick }} sudo make -j$(nproc) sudo make install ) From e2107fa351bdc8714e4e567469f8178928c57a72 Mon Sep 17 00:00:00 2001 From: Sergey Kudashev Date: Sat, 26 Mar 2022 11:28:25 +0300 Subject: [PATCH 139/476] Update run-tests.yml style file. --- .github/workflows/run-tests.yml | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 740bf49e..e456c2ee 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -9,13 +9,8 @@ jobs: fail-fast: false matrix: php: [ '8.0', '8.1' ] - imagemagick: [ - '6.9.12-43', - '7.1.0-28' - ] - imagick: [ - '3.7.0' - ] + imagemagick: [ '6.9.12-43', '7.1.0-28' ] + imagick: [ '3.7.0' ] stability: [ prefer-lowest, prefer-stable ] name: P${{ matrix.php }} - ${{ matrix.stability }} From 64183b8379f3c8845cb4fe31a2a7856908e0db54 Mon Sep 17 00:00:00 2001 From: Sergey Kudashev Date: Sat, 26 Mar 2022 11:44:31 +0300 Subject: [PATCH 140/476] Update run-tests.yml update cache imagemagick section. --- .github/workflows/run-tests.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index e456c2ee..c2c6c787 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -42,7 +42,8 @@ jobs: id: cache-imagemagick with: path: ${HOME}/im/imagemagick-${{ matrix.imagemagick }} - key: ${{ runner.os }}-ImageMagick-${{ matrix.imagemagick }} + key: ${{ runner.os }}-ImageMagick-${{ matrix.imagemagick }}-${{ hashFiles('**/composer.json') }} + restore-keys: ${{ runner.os }}-ImageMagick-${{ matrix.imagemagick }}- - name: Install ImageMagick if: steps.cache-imagemagick.outputs.cache-hit != 'true' From 4e63fb9eefd0ca60eafc5162384dcbab2a1a0e37 Mon Sep 17 00:00:00 2001 From: Sergey Kudashev Date: Sat, 26 Mar 2022 11:51:03 +0300 Subject: [PATCH 141/476] Update run-tests.yml improve prepare environment section. --- .github/workflows/run-tests.yml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index c2c6c787..61b57807 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -30,11 +30,7 @@ jobs: run: | sudo apt-get -y remove imagemagick imagemagick-6-common libmagic-dev sudo apt-get update - sudo apt-get install -y libjpeg62-dev - sudo apt-get install -y libgif-dev - sudo apt-get install -y libtiff-dev - sudo apt-get install -y libpng-dev - sudo apt-get install -y libwebp-dev + sudo apt-get install -y libjpeg-dev libgif-dev libtiff-dev libpng-dev libwebp-dev sudo apt-get install -y libmagickwand-dev - name: Cache ImageMagick From 7cf7ef5e9ac1443b8aad4b8ebb7417bc2a518d76 Mon Sep 17 00:00:00 2001 From: Sergey Kudashev Date: Sat, 26 Mar 2022 11:51:51 +0300 Subject: [PATCH 142/476] Fix run-tests.yml fix a typo in the prepare environment section. --- .github/workflows/run-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 61b57807..16124b98 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -26,7 +26,7 @@ jobs: extensions: mbstring, gd coverage: none - - name: Prepare environament for Imagemagick + - name: Prepare environment for Imagemagick run: | sudo apt-get -y remove imagemagick imagemagick-6-common libmagic-dev sudo apt-get update From 976b9ae09af5487c1985011b51dfed0e4acd048d Mon Sep 17 00:00:00 2001 From: Sergey Kudashev Date: Sat, 26 Mar 2022 11:52:38 +0300 Subject: [PATCH 143/476] Update run-tests.yml add check of the cache folder. --- .github/workflows/run-tests.yml | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 16124b98..49070540 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -37,19 +37,25 @@ jobs: uses: actions/cache@v2 id: cache-imagemagick with: - path: ${HOME}/im/imagemagick-${{ matrix.imagemagick }} + path: /home/runner/im/imagemagick-${{ matrix.imagemagick }} key: ${{ runner.os }}-ImageMagick-${{ matrix.imagemagick }}-${{ hashFiles('**/composer.json') }} restore-keys: ${{ runner.os }}-ImageMagick-${{ matrix.imagemagick }}- + - name: Check ImageMagick cache exists + uses: andstor/file-existence-action@v1 + id: cache-imagemagick-exists + with: + files: /home/runner/im/imagemagick-${{ matrix.imagemagick }} + - name: Install ImageMagick - if: steps.cache-imagemagick.outputs.cache-hit != 'true' + if: ( steps.cache-imagemagick.outputs.cache-hit != 'true' || steps.cache-imagemagick-exists.outputs.files_exists != 'true' ) run: | curl -o /tmp/ImageMagick.tar.gz -sL https://www.imagemagick.org/download/ImageMagick-${{ matrix.imagemagick }}.tar.gz ( cd /tmp || exit 1 tar xf ImageMagick.tar.gz cd ImageMagick-${{ matrix.imagemagick }} - sudo ./configure --prefix=${HOME}/im/imagemagick-${{ matrix.imagemagick }} + sudo ./configure --prefix=/home/runner/im/imagemagick-${{ matrix.imagemagick }} sudo make -j$(nproc) sudo make install ) @@ -62,7 +68,7 @@ jobs: tar -xzf imagick.tgz cd imagick-${{ matrix.imagick }} phpize - sudo ./configure --with-imagick=${HOME}/im/imagemagick-${{ matrix.imagemagick }} + sudo ./configure --with-imagick=/home/runner/im/imagemagick-${{ matrix.imagemagick }} sudo make -j$(nproc) sudo make install ) From 7a8a02e8c885a602746d6130cffe641d9a2823ea Mon Sep 17 00:00:00 2001 From: Sergey Kudashev Date: Sat, 26 Mar 2022 18:41:46 +0300 Subject: [PATCH 144/476] Update TestCase.php remove a redundant import. --- tests/TestCase.php | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/TestCase.php b/tests/TestCase.php index d9b53621..8985b4e8 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -3,7 +3,6 @@ namespace Intervention\Image\Tests; use Intervention\Image\Interfaces\ColorInterface; -use PHPUnit\Framework\TestCase as PHPUnitTestCase; use Mockery\Adapter\Phpunit\MockeryTestCase; abstract class TestCase extends MockeryTestCase From e9655672aa624cb9128db5d9172f7ee450de24d6 Mon Sep 17 00:00:00 2001 From: Sergey Kudashev Date: Sat, 26 Mar 2022 18:43:26 +0300 Subject: [PATCH 145/476] Fix tests fix ModifierStackTest file name. --- tests/{ModiferStackTest.php => ModifierStackTest.php} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename tests/{ModiferStackTest.php => ModifierStackTest.php} (100%) diff --git a/tests/ModiferStackTest.php b/tests/ModifierStackTest.php similarity index 100% rename from tests/ModiferStackTest.php rename to tests/ModifierStackTest.php From e78d2d75f38aa90dd53b48b5699ce5a9cb264309 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Tue, 19 Apr 2022 19:20:13 +0200 Subject: [PATCH 146/476] Changed scheme of website address --- composer.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index b8f72450..29c17fe2 100644 --- a/composer.json +++ b/composer.json @@ -1,14 +1,14 @@ { "name": "intervention/image", "description": "PHP image manipulation", - "homepage": "http://image.intervention.io/", + "homepage": "https://image.intervention.io/", "keywords": ["image", "gd", "imagick", "laravel", "watermark", "thumbnail"], "license": "MIT", "authors": [ { "name": "Oliver Vogel", "email": "oliver@olivervogel.com", - "homepage": "http://intervention.io/" + "homepage": "https://intervention.io/" } ], "require": { From 41363222f990cf2e128c7bdf0cde8aa2502493ff Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Tue, 19 Apr 2022 19:37:59 +0200 Subject: [PATCH 147/476] Fixed Imagemagick download URI for tests --- .github/workflows/run-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 49070540..7fcdf254 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -50,7 +50,7 @@ jobs: - name: Install ImageMagick if: ( steps.cache-imagemagick.outputs.cache-hit != 'true' || steps.cache-imagemagick-exists.outputs.files_exists != 'true' ) run: | - curl -o /tmp/ImageMagick.tar.gz -sL https://www.imagemagick.org/download/ImageMagick-${{ matrix.imagemagick }}.tar.gz + curl -o /tmp/ImageMagick.tar.gz -sL https://download.imagemagick.org/ImageMagick/download/releases/ImageMagick-${{ matrix.imagemagick }}.tar.gz ( cd /tmp || exit 1 tar xf ImageMagick.tar.gz From 977dbd0734a9772656748268c529b13714c25fff Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Tue, 19 Apr 2022 19:50:26 +0200 Subject: [PATCH 148/476] Changed email address --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 29c17fe2..933820c7 100644 --- a/composer.json +++ b/composer.json @@ -7,7 +7,7 @@ "authors": [ { "name": "Oliver Vogel", - "email": "oliver@olivervogel.com", + "email": "oliver@intervention.io", "homepage": "https://intervention.io/" } ], From fe280df77996c8b45c0ec8139a025b211b4bec96 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Tue, 19 Apr 2022 19:59:39 +0200 Subject: [PATCH 149/476] Update .gitattributes --- .gitattributes | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitattributes b/.gitattributes index 880a0889..f5ee500b 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,6 +1,7 @@ * text=auto /tests export-ignore +/.github export-ignore /.gitattributes export-ignore /.gitignore export-ignore /.travis.yml export-ignore From 1cfbdf8d24f8720b73be5d6fe420e85448d940ad Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 21 May 2022 20:04:33 +0200 Subject: [PATCH 150/476] Add phpstan to dev-dependencies --- composer.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 933820c7..e121a851 100644 --- a/composer.json +++ b/composer.json @@ -18,7 +18,8 @@ }, "require-dev": { "phpunit/phpunit": "^9", - "mockery/mockery": "^1.4" + "mockery/mockery": "^1.4", + "phpstan/phpstan": "^1" }, "autoload": { "psr-4": { From 4847f2c5fcae1b91bd178b6e23b8423b1cfedd76 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 22 May 2022 10:53:42 +0200 Subject: [PATCH 151/476] Change return type of AbstractDecoder::handle() --- .../Abstract/Decoders/AbstractDecoder.php | 8 +++---- .../Abstract/Decoders/AbstractDecoderTest.php | 22 ++++++++++++------- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/src/Drivers/Abstract/Decoders/AbstractDecoder.php b/src/Drivers/Abstract/Decoders/AbstractDecoder.php index 93740041..953ba942 100644 --- a/src/Drivers/Abstract/Decoders/AbstractDecoder.php +++ b/src/Drivers/Abstract/Decoders/AbstractDecoder.php @@ -15,16 +15,16 @@ abstract class AbstractDecoder // } - final public function handle($input): null|ImageInterface|ColorInterface + final public function handle($input): ImageInterface|ColorInterface { try { $decoded = $this->decode($input); } catch (DecoderException $e) { - if ($this->hasSuccessor()) { - return $this->successor->handle($input); + if (!$this->hasSuccessor()) { + $this->fail(); } - $this->fail(); + return $this->successor->handle($input); } return $decoded; diff --git a/tests/Drivers/Abstract/Decoders/AbstractDecoderTest.php b/tests/Drivers/Abstract/Decoders/AbstractDecoderTest.php index 157c3474..7d567344 100644 --- a/tests/Drivers/Abstract/Decoders/AbstractDecoderTest.php +++ b/tests/Drivers/Abstract/Decoders/AbstractDecoderTest.php @@ -6,6 +6,7 @@ namespace Intervention\Image\Tests\Drivers\Abstract\Decoders; use Intervention\Image\Drivers\Abstract\Decoders\AbstractDecoder; use Intervention\Image\Exceptions\DecoderException; +use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Tests\TestCase; use Mockery; @@ -16,31 +17,36 @@ class AbstractDecoderTest extends TestCase { public function testHandle(): void { + $result = Mockery::mock(ColorInterface::class); $decoder = Mockery::mock(AbstractDecoder::class)->makePartial(); - $decoder->shouldReceive('decode')->with('input string')->andReturn(null); + $decoder->shouldReceive('decode')->with('test input')->andReturn($result); - $decoder->handle('input string'); + $decoder->handle('test input'); } public function testHandleFail(): void { $decoder = Mockery::mock(AbstractDecoder::class, [])->makePartial()->shouldAllowMockingProtectedMethods(); - $decoder->shouldReceive('decode')->with('input string')->andThrow(DecoderException::class); + $decoder->shouldReceive('decode')->with('test input')->andThrow(DecoderException::class); $this->expectException(DecoderException::class); $this->expectExceptionMessage('Unable to decode given input.'); - $decoder->handle('input string'); + $decoder->handle('test input'); } public function testHandleFailWithSuccessor(): void { + $result = Mockery::mock(ColorInterface::class); $successor = Mockery::mock(AbstractDecoder::class)->makePartial(); - $successor->shouldReceive('decode')->with('input string')->andReturn(null); + $successor->shouldReceive('decode')->with('test input')->andReturn($result); - $decoder = Mockery::mock(AbstractDecoder::class, [$successor])->makePartial()->shouldAllowMockingProtectedMethods(); - $decoder->shouldReceive('decode')->with('input string')->andThrow(DecoderException::class); + $decoder = Mockery::mock( + AbstractDecoder::class, + [$successor] + )->makePartial()->shouldAllowMockingProtectedMethods(); + $decoder->shouldReceive('decode')->with('test input')->andThrow(DecoderException::class); - $decoder->handle('input string'); + $decoder->handle('test input'); } } From 3b8629c54ea214a127367543ea93a8de8448332a Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 22 May 2022 10:55:06 +0200 Subject: [PATCH 152/476] Add DecoderInterface implementation to AbstractDecoder --- src/Drivers/Abstract/Decoders/AbstractDecoder.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Drivers/Abstract/Decoders/AbstractDecoder.php b/src/Drivers/Abstract/Decoders/AbstractDecoder.php index 953ba942..a40e0264 100644 --- a/src/Drivers/Abstract/Decoders/AbstractDecoder.php +++ b/src/Drivers/Abstract/Decoders/AbstractDecoder.php @@ -4,11 +4,12 @@ namespace Intervention\Image\Drivers\Abstract\Decoders; use Intervention\Image\Exceptions\DecoderException; use Intervention\Image\Interfaces\ColorInterface; +use Intervention\Image\Interfaces\DecoderInterface; use Intervention\Image\Interfaces\ImageInterface; use Intervention\MimeSniffer\MimeSniffer; use Intervention\MimeSniffer\AbstractType; -abstract class AbstractDecoder +abstract class AbstractDecoder implements DecoderInterface { public function __construct(protected ?AbstractDecoder $successor = null) { From 6dd434c2949bca912927a8dc960c1958061f88d8 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 22 May 2022 10:59:38 +0200 Subject: [PATCH 153/476] Add exception message to AbstractDecoder::fail() --- src/Drivers/Abstract/Decoders/AbstractDecoder.php | 6 +++--- tests/Drivers/Abstract/Decoders/AbstractDecoderTest.php | 1 - 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/Drivers/Abstract/Decoders/AbstractDecoder.php b/src/Drivers/Abstract/Decoders/AbstractDecoder.php index a40e0264..a1ee53ea 100644 --- a/src/Drivers/Abstract/Decoders/AbstractDecoder.php +++ b/src/Drivers/Abstract/Decoders/AbstractDecoder.php @@ -22,7 +22,7 @@ abstract class AbstractDecoder implements DecoderInterface $decoded = $this->decode($input); } catch (DecoderException $e) { if (!$this->hasSuccessor()) { - $this->fail(); + $this->fail($e->getMessage()); } return $this->successor->handle($input); @@ -36,9 +36,9 @@ abstract class AbstractDecoder implements DecoderInterface return $this->successor !== null; } - protected function fail(): void + protected function fail(string $message = 'Unable to decode given input.'): void { - throw new DecoderException("Unable to decode given input."); + throw new DecoderException($message); } protected function inputType($input): AbstractType diff --git a/tests/Drivers/Abstract/Decoders/AbstractDecoderTest.php b/tests/Drivers/Abstract/Decoders/AbstractDecoderTest.php index 7d567344..b6d7f261 100644 --- a/tests/Drivers/Abstract/Decoders/AbstractDecoderTest.php +++ b/tests/Drivers/Abstract/Decoders/AbstractDecoderTest.php @@ -30,7 +30,6 @@ class AbstractDecoderTest extends TestCase $decoder->shouldReceive('decode')->with('test input')->andThrow(DecoderException::class); $this->expectException(DecoderException::class); - $this->expectExceptionMessage('Unable to decode given input.'); $decoder->handle('test input'); } From fdb6de62984aa8d441c9021d10ced288fceef64e Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 22 May 2022 11:03:50 +0200 Subject: [PATCH 154/476] Add methods to SizeInterface --- src/Geometry/Size.php | 2 +- src/Interfaces/SizeInterface.php | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Geometry/Size.php b/src/Geometry/Size.php index 797c15bc..1d1e44e3 100644 --- a/src/Geometry/Size.php +++ b/src/Geometry/Size.php @@ -182,7 +182,7 @@ class Size implements SizeInterface return $this; } - public function alignPivotTo(Size $size, string $position): self + public function alignPivotTo(SizeInterface $size, string $position): self { $reference = new Size($size->getWidth(), $size->getHeight()); $reference->alignPivot($position); diff --git a/src/Interfaces/SizeInterface.php b/src/Interfaces/SizeInterface.php index fd641fbe..92ba6674 100644 --- a/src/Interfaces/SizeInterface.php +++ b/src/Interfaces/SizeInterface.php @@ -14,4 +14,6 @@ interface SizeInterface public function fitsInto(SizeInterface $size): bool; public function isLandscape(): bool; public function isPortrait(): bool; + public function alignPivot(string $position, int $offset_x = 0, int $offset_y = 0): SizeInterface; + public function alignPivotTo(SizeInterface $size, string $position): SizeInterface; } From a9e5c57fb76193b765b3a24d0e0386dcba29723e Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 22 May 2022 11:12:28 +0200 Subject: [PATCH 155/476] Wrap code line --- src/Drivers/Abstract/AbstractImage.php | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/Drivers/Abstract/AbstractImage.php b/src/Drivers/Abstract/AbstractImage.php index 2b1b4d6a..7bf79761 100644 --- a/src/Drivers/Abstract/AbstractImage.php +++ b/src/Drivers/Abstract/AbstractImage.php @@ -14,7 +14,7 @@ use Intervention\Image\Interfaces\SizeInterface; use Intervention\Image\Traits\CanHandleInput; use Intervention\Image\Traits\CanResolveDriverClass; -abstract class AbstractImage +abstract class AbstractImage implements ImageInterface { use CanResolveDriverClass; use CanHandleInput; @@ -284,8 +284,12 @@ abstract class AbstractImage ); } - public function padDown(int $width, int $height, $background = 'ffffff', string $position = 'center'): ImageInterface - { + public function padDown( + int $width, + int $height, + $background = 'ffffff', + string $position = 'center' + ): ImageInterface { return $this->modify( $this->resolveDriverClass('Modifiers\PadDownModifier', $width, $height, $background, $position) ); From 425d4515008153d2c70fbb2e704088135fc147ee Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 22 May 2022 11:18:27 +0200 Subject: [PATCH 156/476] Adjust method signatures to interface --- src/Geometry/Size.php | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/Geometry/Size.php b/src/Geometry/Size.php index 1d1e44e3..d05d0417 100644 --- a/src/Geometry/Size.php +++ b/src/Geometry/Size.php @@ -209,29 +209,29 @@ class Size implements SizeInterface return new Point($x, $y); } - protected function getResizer(...$arguments): Resizer + protected function getResizer(?int $width = null, ?int $height = null): Resizer { - return new Resizer(...$arguments); + return new Resizer($width, $height); } - public function resize(...$arguments): self + public function resize(?int $width = null, ?int $height = null): self { - return $this->getResizer(...$arguments)->resize($this); + return $this->getResizer($width, $height)->resize($this); } - public function resizeDown(...$arguments): self + public function resizeDown(?int $width = null, ?int $height = null): self { - return $this->getResizer(...$arguments)->resizeDown($this); + return $this->getResizer($width, $height)->resizeDown($this); } - public function scale(...$arguments): self + public function scale(?int $width = null, ?int $height = null): self { - return $this->getResizer(...$arguments)->scale($this); + return $this->getResizer($width, $height)->scale($this); } - public function scaleDown(...$arguments): self + public function scaleDown(?int $width = null, ?int $height = null): self { - return $this->getResizer(...$arguments)->scaleDown($this); + return $this->getResizer($width, $height)->scaleDown($this); } public function cover(int $width, int $height): self From 72bd013b87fac2702f0c69701e30ef3ea70ed07f Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 22 May 2022 11:18:52 +0200 Subject: [PATCH 157/476] Remove unnecessary code --- src/Drivers/Gd/Modifiers/ResizeModifier.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Drivers/Gd/Modifiers/ResizeModifier.php b/src/Drivers/Gd/Modifiers/ResizeModifier.php index 7646a22f..512491f5 100644 --- a/src/Drivers/Gd/Modifiers/ResizeModifier.php +++ b/src/Drivers/Gd/Modifiers/ResizeModifier.php @@ -2,12 +2,10 @@ namespace Intervention\Image\Drivers\Gd\Modifiers; -use Intervention\Image\Geometry\Resizer; use Intervention\Image\Interfaces\FrameInterface; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; use Intervention\Image\Interfaces\SizeInterface; -use Intervention\Image\Traits\CanResizeGeometrically; class ResizeModifier implements ModifierInterface { From 35444b249e7481b940af0c6635f78792e4006819 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 22 May 2022 11:25:08 +0200 Subject: [PATCH 158/476] Change signature of Frame::setCore --- src/Drivers/Abstract/AbstractFrame.php | 17 ++++++++++++++++- src/Drivers/Gd/Frame.php | 7 ------- src/Drivers/Imagick/Frame.php | 7 ------- src/Interfaces/FrameInterface.php | 1 + 4 files changed, 17 insertions(+), 15 deletions(-) diff --git a/src/Drivers/Abstract/AbstractFrame.php b/src/Drivers/Abstract/AbstractFrame.php index c83c8af3..223a2072 100644 --- a/src/Drivers/Abstract/AbstractFrame.php +++ b/src/Drivers/Abstract/AbstractFrame.php @@ -4,5 +4,20 @@ namespace Intervention\Image\Drivers\Abstract; abstract class AbstractFrame { - // + /** + * Set the frame core + * + * Input is losely typed and depending on the driver. + * Might be GdImage or Imagick but should be open to + * add more drivers. + * + * @param mixed $core + * @return AbstractFrame + */ + public function setCore($core): self + { + $this->core = $core; + + return $this; + } } diff --git a/src/Drivers/Gd/Frame.php b/src/Drivers/Gd/Frame.php index ea33a036..6f43945d 100644 --- a/src/Drivers/Gd/Frame.php +++ b/src/Drivers/Gd/Frame.php @@ -27,13 +27,6 @@ class Frame extends AbstractFrame implements FrameInterface return $this->core; } - public function setCore(GdImage $core): self - { - $this->core = $core; - - return $this; - } - public function unsetCore(): void { unset($this->core); diff --git a/src/Drivers/Imagick/Frame.php b/src/Drivers/Imagick/Frame.php index 632ad2c7..94cc9c68 100644 --- a/src/Drivers/Imagick/Frame.php +++ b/src/Drivers/Imagick/Frame.php @@ -22,13 +22,6 @@ class Frame extends AbstractFrame implements FrameInterface return $this->core; } - public function setCore(Imagick $core): FrameInterface - { - $this->core = $core; - - return $this; - } - public function getSize(): SizeInterface { return new Size($this->core->getImageWidth(), $this->core->getImageHeight()); diff --git a/src/Interfaces/FrameInterface.php b/src/Interfaces/FrameInterface.php index 3c27aea9..b2fd1230 100644 --- a/src/Interfaces/FrameInterface.php +++ b/src/Interfaces/FrameInterface.php @@ -6,6 +6,7 @@ interface FrameInterface { public function toImage(): ImageInterface; public function getCore(); + public function setCore($core): FrameInterface; public function getSize(): SizeInterface; public function getDelay(): float; public function setDelay(float $delay): FrameInterface; From f2a32bd349fdd949919f773a6dcb7e17ad6f5343 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 22 May 2022 11:37:56 +0200 Subject: [PATCH 159/476] Fix type hinting --- src/Drivers/Abstract/AbstractFrame.php | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/Drivers/Abstract/AbstractFrame.php b/src/Drivers/Abstract/AbstractFrame.php index 223a2072..8e89c3ef 100644 --- a/src/Drivers/Abstract/AbstractFrame.php +++ b/src/Drivers/Abstract/AbstractFrame.php @@ -2,7 +2,9 @@ namespace Intervention\Image\Drivers\Abstract; -abstract class AbstractFrame +use Intervention\Image\Interfaces\FrameInterface; + +abstract class AbstractFrame implements FrameInterface { /** * Set the frame core @@ -12,9 +14,9 @@ abstract class AbstractFrame * add more drivers. * * @param mixed $core - * @return AbstractFrame + * @return FrameInterface */ - public function setCore($core): self + public function setCore($core): FrameInterface { $this->core = $core; From a937e5a94d07635b3d98c3db9e3b735f53bb404d Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 22 May 2022 17:23:07 +0200 Subject: [PATCH 160/476] Add ColorInterface implementation to AbstractColor --- src/Drivers/Abstract/AbstractColor.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Drivers/Abstract/AbstractColor.php b/src/Drivers/Abstract/AbstractColor.php index f8ea601a..fe6bdc0f 100644 --- a/src/Drivers/Abstract/AbstractColor.php +++ b/src/Drivers/Abstract/AbstractColor.php @@ -2,7 +2,9 @@ namespace Intervention\Image\Drivers\Abstract; -abstract class AbstractColor +use Intervention\Image\Interfaces\ColorInterface; + +abstract class AbstractColor implements ColorInterface { /** * Format color to hexadecimal color code From 190f46fc12b9b920f02c4932c13f7d18c574a3d9 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 22 May 2022 17:26:16 +0200 Subject: [PATCH 161/476] Remove AbstractFrame::class --- src/Drivers/Abstract/AbstractFrame.php | 25 ------------------------- src/Drivers/Gd/Frame.php | 10 ++++++++-- src/Drivers/Imagick/Frame.php | 10 ++++++++-- 3 files changed, 16 insertions(+), 29 deletions(-) delete mode 100644 src/Drivers/Abstract/AbstractFrame.php diff --git a/src/Drivers/Abstract/AbstractFrame.php b/src/Drivers/Abstract/AbstractFrame.php deleted file mode 100644 index 8e89c3ef..00000000 --- a/src/Drivers/Abstract/AbstractFrame.php +++ /dev/null @@ -1,25 +0,0 @@ -core = $core; - - return $this; - } -} diff --git a/src/Drivers/Gd/Frame.php b/src/Drivers/Gd/Frame.php index 6f43945d..885934c4 100644 --- a/src/Drivers/Gd/Frame.php +++ b/src/Drivers/Gd/Frame.php @@ -4,13 +4,12 @@ namespace Intervention\Image\Drivers\Gd; use GdImage; use Intervention\Image\Collection; -use Intervention\Image\Drivers\Abstract\AbstractFrame; use Intervention\Image\Geometry\Size; use Intervention\Image\Interfaces\FrameInterface; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\SizeInterface; -class Frame extends AbstractFrame implements FrameInterface +class Frame implements FrameInterface { public function __construct( protected GdImage $core, @@ -22,6 +21,13 @@ class Frame extends AbstractFrame implements FrameInterface // } + public function setCore($core): FrameInterface + { + $this->core = $core; + + return $this; + } + public function getCore(): GdImage { return $this->core; diff --git a/src/Drivers/Imagick/Frame.php b/src/Drivers/Imagick/Frame.php index 94cc9c68..ef4bd57b 100644 --- a/src/Drivers/Imagick/Frame.php +++ b/src/Drivers/Imagick/Frame.php @@ -4,19 +4,25 @@ namespace Intervention\Image\Drivers\Imagick; use Imagick; use Intervention\Image\Collection; -use Intervention\Image\Drivers\Abstract\AbstractFrame; use Intervention\Image\Geometry\Size; use Intervention\Image\Interfaces\FrameInterface; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\SizeInterface; -class Frame extends AbstractFrame implements FrameInterface +class Frame implements FrameInterface { public function __construct(protected Imagick $core) { // } + public function setCore($core): FrameInterface + { + $this->core = $core; + + return $this; + } + public function getCore(): Imagick { return $this->core; From 58585c81f330df1bbeea6e832ba8a6087ace552b Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 22 May 2022 17:42:12 +0200 Subject: [PATCH 162/476] Replace method AbstractDecoder::fail() --- src/Drivers/Abstract/Decoders/AbstractDecoder.php | 7 +------ src/Drivers/Gd/Decoders/ArrayColorDecoder.php | 3 ++- src/Drivers/Gd/Decoders/Base64ImageDecoder.php | 3 ++- src/Drivers/Gd/Decoders/BinaryImageDecoder.php | 9 ++++----- src/Drivers/Gd/Decoders/DataUriImageDecoder.php | 6 +++--- src/Drivers/Gd/Decoders/FilePathImageDecoder.php | 7 ++++--- src/Drivers/Gd/Decoders/HexColorDecoder.php | 6 +++--- src/Drivers/Gd/Decoders/ImageObjectDecoder.php | 3 ++- src/Drivers/Gd/Decoders/RgbStringColorDecoder.php | 7 ++++--- src/Drivers/Gd/Decoders/TransparentColorDecoder.php | 5 ++--- src/Drivers/Imagick/Decoders/ArrayColorDecoder.php | 3 ++- src/Drivers/Imagick/Decoders/Base64ImageDecoder.php | 3 ++- src/Drivers/Imagick/Decoders/BinaryImageDecoder.php | 7 ++++--- src/Drivers/Imagick/Decoders/DataUriImageDecoder.php | 6 +++--- src/Drivers/Imagick/Decoders/FilePathImageDecoder.php | 7 ++++--- src/Drivers/Imagick/Decoders/HexColorDecoder.php | 6 +++--- src/Drivers/Imagick/Decoders/ImageObjectDecoder.php | 3 ++- src/Drivers/Imagick/Decoders/RgbStringColorDecoder.php | 8 ++++---- src/Drivers/Imagick/Decoders/TransparentColorDecoder.php | 5 ++--- 19 files changed, 53 insertions(+), 51 deletions(-) diff --git a/src/Drivers/Abstract/Decoders/AbstractDecoder.php b/src/Drivers/Abstract/Decoders/AbstractDecoder.php index a1ee53ea..61e350d2 100644 --- a/src/Drivers/Abstract/Decoders/AbstractDecoder.php +++ b/src/Drivers/Abstract/Decoders/AbstractDecoder.php @@ -22,7 +22,7 @@ abstract class AbstractDecoder implements DecoderInterface $decoded = $this->decode($input); } catch (DecoderException $e) { if (!$this->hasSuccessor()) { - $this->fail($e->getMessage()); + throw new DecoderException($e->getMessage()); } return $this->successor->handle($input); @@ -36,11 +36,6 @@ abstract class AbstractDecoder implements DecoderInterface return $this->successor !== null; } - protected function fail(string $message = 'Unable to decode given input.'): void - { - throw new DecoderException($message); - } - protected function inputType($input): AbstractType { return MimeSniffer::createFromString($input)->getType(); diff --git a/src/Drivers/Gd/Decoders/ArrayColorDecoder.php b/src/Drivers/Gd/Decoders/ArrayColorDecoder.php index 9f8d7a70..3d694e49 100644 --- a/src/Drivers/Gd/Decoders/ArrayColorDecoder.php +++ b/src/Drivers/Gd/Decoders/ArrayColorDecoder.php @@ -4,6 +4,7 @@ namespace Intervention\Image\Drivers\Gd\Decoders; use Intervention\Image\Drivers\Abstract\Decoders\AbstractDecoder; use Intervention\Image\Drivers\Gd\Color; +use Intervention\Image\Exceptions\DecoderException; use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\DecoderInterface; use Intervention\Image\Interfaces\ImageInterface; @@ -16,7 +17,7 @@ class ArrayColorDecoder extends AbstractDecoder implements DecoderInterface public function decode($input): ImageInterface|ColorInterface { if (! $this->isValidColorArray($input)) { - $this->fail(); + throw new DecoderException('Unable to decode input'); } if (count($input) === 3) { diff --git a/src/Drivers/Gd/Decoders/Base64ImageDecoder.php b/src/Drivers/Gd/Decoders/Base64ImageDecoder.php index fb22e3ba..ece55147 100644 --- a/src/Drivers/Gd/Decoders/Base64ImageDecoder.php +++ b/src/Drivers/Gd/Decoders/Base64ImageDecoder.php @@ -2,6 +2,7 @@ namespace Intervention\Image\Drivers\Gd\Decoders; +use Intervention\Image\Exceptions\DecoderException; use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\DecoderInterface; use Intervention\Image\Interfaces\ImageInterface; @@ -14,7 +15,7 @@ class Base64ImageDecoder extends BinaryImageDecoder implements DecoderInterface public function decode($input): ImageInterface|ColorInterface { if (! $this->isValidBase64($input)) { - $this->fail(); + throw new DecoderException('Unable to decode input'); } return parent::decode(base64_decode($input)); diff --git a/src/Drivers/Gd/Decoders/BinaryImageDecoder.php b/src/Drivers/Gd/Decoders/BinaryImageDecoder.php index f4a36cf2..467493b0 100644 --- a/src/Drivers/Gd/Decoders/BinaryImageDecoder.php +++ b/src/Drivers/Gd/Decoders/BinaryImageDecoder.php @@ -2,7 +2,6 @@ namespace Intervention\Image\Drivers\Gd\Decoders; -use GdImage; use Intervention\Image\Collection; use Intervention\Image\Drivers\Abstract\Decoders\AbstractDecoder; use Intervention\Image\Drivers\Gd\Frame; @@ -10,21 +9,21 @@ use Intervention\Image\Drivers\Gd\Image; use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\DecoderInterface; use Intervention\Image\Interfaces\ImageInterface; -use Intervention\MimeSniffer\MimeSniffer; use Intervention\MimeSniffer\Types\ImageGif; use Intervention\Gif\Decoder as GifDecoder; use Intervention\Gif\Splitter as GifSplitter; +use Intervention\Image\Exceptions\DecoderException; class BinaryImageDecoder extends AbstractDecoder implements DecoderInterface { public function decode($input): ImageInterface|ColorInterface { if (!is_string($input)) { - $this->fail(); + throw new DecoderException('Unable to decode input'); } if (! $this->inputType($input)->isBinary()) { - $this->fail(); + throw new DecoderException('Unable to decode input'); } if (is_a($this->inputType($input), ImageGif::class)) { @@ -34,7 +33,7 @@ class BinaryImageDecoder extends AbstractDecoder implements DecoderInterface $gd = @imagecreatefromstring($input); if ($gd === false) { - $this->fail(); + throw new DecoderException('Unable to decode input'); } if (! imageistruecolor($gd)) { diff --git a/src/Drivers/Gd/Decoders/DataUriImageDecoder.php b/src/Drivers/Gd/Decoders/DataUriImageDecoder.php index b1c8f245..32155781 100644 --- a/src/Drivers/Gd/Decoders/DataUriImageDecoder.php +++ b/src/Drivers/Gd/Decoders/DataUriImageDecoder.php @@ -2,11 +2,11 @@ namespace Intervention\Image\Drivers\Gd\Decoders; +use Intervention\Image\Exceptions\DecoderException; use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\DecoderInterface; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Traits\CanDecodeDataUri; -use Intervention\MimeSniffer\MimeSniffer; class DataUriImageDecoder extends BinaryImageDecoder implements DecoderInterface { @@ -15,13 +15,13 @@ class DataUriImageDecoder extends BinaryImageDecoder implements DecoderInterface public function decode($input): ImageInterface|ColorInterface { if (!is_string($input)) { - $this->fail(); + throw new DecoderException('Unable to decode input'); } $uri = $this->decodeDataUri($input); if (! $uri->isValid()) { - $this->fail(); + throw new DecoderException('Unable to decode input'); } if ($uri->isBase64Encoded()) { diff --git a/src/Drivers/Gd/Decoders/FilePathImageDecoder.php b/src/Drivers/Gd/Decoders/FilePathImageDecoder.php index 6448a645..a826734b 100644 --- a/src/Drivers/Gd/Decoders/FilePathImageDecoder.php +++ b/src/Drivers/Gd/Decoders/FilePathImageDecoder.php @@ -3,6 +3,7 @@ namespace Intervention\Image\Drivers\Gd\Decoders; use Exception; +use Intervention\Image\Exceptions\DecoderException; use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\DecoderInterface; use Intervention\Image\Interfaces\ImageInterface; @@ -12,15 +13,15 @@ class FilePathImageDecoder extends BinaryImageDecoder implements DecoderInterfac public function decode($input): ImageInterface|ColorInterface { if (! is_string($input)) { - $this->fail(); + throw new DecoderException('Unable to decode input'); } try { if (! @is_file($input)) { - $this->fail(); + throw new DecoderException('Unable to decode input'); } } catch (Exception $e) { - $this->fail(); + throw new DecoderException('Unable to decode input'); } return parent::decode(file_get_contents($input)); diff --git a/src/Drivers/Gd/Decoders/HexColorDecoder.php b/src/Drivers/Gd/Decoders/HexColorDecoder.php index 2329fe50..17134634 100644 --- a/src/Drivers/Gd/Decoders/HexColorDecoder.php +++ b/src/Drivers/Gd/Decoders/HexColorDecoder.php @@ -2,7 +2,7 @@ namespace Intervention\Image\Drivers\Gd\Decoders; -use Intervention\Image\Drivers\Gd\Color; +use Intervention\Image\Exceptions\DecoderException; use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\DecoderInterface; use Intervention\Image\Interfaces\ImageInterface; @@ -12,14 +12,14 @@ class HexColorDecoder extends ArrayColorDecoder implements DecoderInterface public function decode($input): ImageInterface|ColorInterface { if (!is_string($input)) { - $this->fail(); + throw new DecoderException('Unable to decode input'); } $pattern = '/^#?([a-f0-9]{1,2})([a-f0-9]{1,2})([a-f0-9]{1,2})$/i'; $result = preg_match($pattern, $input, $matches); if ($result !== 1) { - $this->fail(); + throw new DecoderException('Unable to decode input'); } return parent::decode([ diff --git a/src/Drivers/Gd/Decoders/ImageObjectDecoder.php b/src/Drivers/Gd/Decoders/ImageObjectDecoder.php index 9dffd5b4..72851dd8 100644 --- a/src/Drivers/Gd/Decoders/ImageObjectDecoder.php +++ b/src/Drivers/Gd/Decoders/ImageObjectDecoder.php @@ -3,6 +3,7 @@ namespace Intervention\Image\Drivers\Gd\Decoders; use Intervention\Image\Drivers\Abstract\Decoders\AbstractDecoder; +use Intervention\Image\Exceptions\DecoderException; use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\DecoderInterface; use Intervention\Image\Interfaces\ImageInterface; @@ -12,7 +13,7 @@ class ImageObjectDecoder extends AbstractDecoder implements DecoderInterface public function decode($input): ImageInterface|ColorInterface { if (! is_a($input, ImageInterface::class)) { - $this->fail(); + throw new DecoderException('Unable to decode input'); } return $input; diff --git a/src/Drivers/Gd/Decoders/RgbStringColorDecoder.php b/src/Drivers/Gd/Decoders/RgbStringColorDecoder.php index 53ded7c5..300dd4a1 100644 --- a/src/Drivers/Gd/Decoders/RgbStringColorDecoder.php +++ b/src/Drivers/Gd/Decoders/RgbStringColorDecoder.php @@ -2,6 +2,7 @@ namespace Intervention\Image\Drivers\Gd\Decoders; +use Intervention\Image\Exceptions\DecoderException; use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\DecoderInterface; use Intervention\Image\Interfaces\ImageInterface; @@ -11,11 +12,11 @@ class RgbStringColorDecoder extends ArrayColorDecoder implements DecoderInterfac public function decode($input): ImageInterface|ColorInterface { if (!is_string($input)) { - $this->fail(); + throw new DecoderException('Unable to decode input'); } if (substr($input, 0, 3) !== 'rgb') { - $this->fail(); + throw new DecoderException('Unable to decode input'); } // rgb string like rgb(102, 200, 0) @@ -30,6 +31,6 @@ class RgbStringColorDecoder extends ArrayColorDecoder implements DecoderInterfac return parent::decode([$matches['r'], $matches['g'], $matches['b'], $matches['a']]); } - $this->fail(); + throw new DecoderException('Unable to decode input'); } } diff --git a/src/Drivers/Gd/Decoders/TransparentColorDecoder.php b/src/Drivers/Gd/Decoders/TransparentColorDecoder.php index dd77d055..64f4adfc 100644 --- a/src/Drivers/Gd/Decoders/TransparentColorDecoder.php +++ b/src/Drivers/Gd/Decoders/TransparentColorDecoder.php @@ -2,18 +2,17 @@ namespace Intervention\Image\Drivers\Gd\Decoders; -use Intervention\Image\Drivers\Gd\Color; +use Intervention\Image\Exceptions\DecoderException; use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\DecoderInterface; use Intervention\Image\Interfaces\ImageInterface; -use Intervention\Image\Traits\CanValidateColors; class TransparentColorDecoder extends ArrayColorDecoder implements DecoderInterface { public function decode($input): ImageInterface|ColorInterface { if (! is_string($input) || strtolower($input) !== 'transparent') { - $this->fail(); + throw new DecoderException('Unable to decode input'); } return parent::decode([0, 0, 0, 0]); diff --git a/src/Drivers/Imagick/Decoders/ArrayColorDecoder.php b/src/Drivers/Imagick/Decoders/ArrayColorDecoder.php index aee0df57..12f8aaa2 100644 --- a/src/Drivers/Imagick/Decoders/ArrayColorDecoder.php +++ b/src/Drivers/Imagick/Decoders/ArrayColorDecoder.php @@ -5,6 +5,7 @@ namespace Intervention\Image\Drivers\Imagick\Decoders; use ImagickPixel; use Intervention\Image\Drivers\Abstract\Decoders\AbstractDecoder; use Intervention\Image\Drivers\Imagick\Color; +use Intervention\Image\Exceptions\DecoderException; use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\DecoderInterface; use Intervention\Image\Interfaces\ImageInterface; @@ -17,7 +18,7 @@ class ArrayColorDecoder extends AbstractDecoder implements DecoderInterface public function decode($input): ImageInterface|ColorInterface { if (! $this->isValidColorArray($input)) { - $this->fail(); + throw new DecoderException('Unable to decode input'); } if (count($input) === 3) { diff --git a/src/Drivers/Imagick/Decoders/Base64ImageDecoder.php b/src/Drivers/Imagick/Decoders/Base64ImageDecoder.php index 46d34430..fe30c4fb 100644 --- a/src/Drivers/Imagick/Decoders/Base64ImageDecoder.php +++ b/src/Drivers/Imagick/Decoders/Base64ImageDecoder.php @@ -2,6 +2,7 @@ namespace Intervention\Image\Drivers\Imagick\Decoders; +use Intervention\Image\Exceptions\DecoderException; use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\DecoderInterface; use Intervention\Image\Interfaces\ImageInterface; @@ -14,7 +15,7 @@ class Base64ImageDecoder extends BinaryImageDecoder implements DecoderInterface public function decode($input): ImageInterface|ColorInterface { if (! $this->isValidBase64($input)) { - $this->fail(); + throw new DecoderException('Unable to decode input'); } return parent::decode(base64_decode($input)); diff --git a/src/Drivers/Imagick/Decoders/BinaryImageDecoder.php b/src/Drivers/Imagick/Decoders/BinaryImageDecoder.php index f09a50ff..7a8b4e02 100644 --- a/src/Drivers/Imagick/Decoders/BinaryImageDecoder.php +++ b/src/Drivers/Imagick/Decoders/BinaryImageDecoder.php @@ -7,6 +7,7 @@ use Intervention\Image\Collection; use Intervention\Image\Drivers\Abstract\Decoders\AbstractDecoder; use Intervention\Image\Drivers\Imagick\Frame; use Intervention\Image\Drivers\Imagick\Image; +use Intervention\Image\Exceptions\DecoderException; use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\DecoderInterface; use Intervention\Image\Interfaces\ImageInterface; @@ -16,11 +17,11 @@ class BinaryImageDecoder extends AbstractDecoder implements DecoderInterface public function decode($input): ImageInterface|ColorInterface { if (!is_string($input)) { - $this->fail(); + throw new DecoderException('Unable to decode input'); } - if (! $this->inputType($input)->isBinary()) { - $this->fail(); + if (!$this->inputType($input)->isBinary()) { + throw new DecoderException('Unable to decode input'); } $imagick = new Imagick(); diff --git a/src/Drivers/Imagick/Decoders/DataUriImageDecoder.php b/src/Drivers/Imagick/Decoders/DataUriImageDecoder.php index 9f80b117..1dcff224 100644 --- a/src/Drivers/Imagick/Decoders/DataUriImageDecoder.php +++ b/src/Drivers/Imagick/Decoders/DataUriImageDecoder.php @@ -2,11 +2,11 @@ namespace Intervention\Image\Drivers\Imagick\Decoders; +use Intervention\Image\Exceptions\DecoderException; use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\DecoderInterface; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Traits\CanDecodeDataUri; -use Intervention\MimeSniffer\MimeSniffer; class DataUriImageDecoder extends BinaryImageDecoder implements DecoderInterface { @@ -15,13 +15,13 @@ class DataUriImageDecoder extends BinaryImageDecoder implements DecoderInterface public function decode($input): ImageInterface|ColorInterface { if (!is_string($input)) { - $this->fail(); + throw new DecoderException('Unable to decode input'); } $uri = $this->decodeDataUri($input); if (! $uri->isValid()) { - $this->fail(); + throw new DecoderException('Unable to decode input'); } if ($uri->isBase64Encoded()) { diff --git a/src/Drivers/Imagick/Decoders/FilePathImageDecoder.php b/src/Drivers/Imagick/Decoders/FilePathImageDecoder.php index a4fe1d30..f1d7346e 100644 --- a/src/Drivers/Imagick/Decoders/FilePathImageDecoder.php +++ b/src/Drivers/Imagick/Decoders/FilePathImageDecoder.php @@ -3,6 +3,7 @@ namespace Intervention\Image\Drivers\Imagick\Decoders; use Exception; +use Intervention\Image\Exceptions\DecoderException; use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\DecoderInterface; use Intervention\Image\Interfaces\ImageInterface; @@ -12,15 +13,15 @@ class FilePathImageDecoder extends BinaryImageDecoder implements DecoderInterfac public function decode($input): ImageInterface|ColorInterface { if (! is_string($input)) { - $this->fail(); + throw new DecoderException('Unable to decode input'); } try { if (! @is_file($input)) { - $this->fail(); + throw new DecoderException('Unable to decode input'); } } catch (Exception $e) { - $this->fail(); + throw new DecoderException('Unable to decode input'); } return parent::decode(file_get_contents($input)); diff --git a/src/Drivers/Imagick/Decoders/HexColorDecoder.php b/src/Drivers/Imagick/Decoders/HexColorDecoder.php index 2eafd126..84526d16 100644 --- a/src/Drivers/Imagick/Decoders/HexColorDecoder.php +++ b/src/Drivers/Imagick/Decoders/HexColorDecoder.php @@ -2,7 +2,7 @@ namespace Intervention\Image\Drivers\Imagick\Decoders; -use Intervention\Image\Drivers\Imagick\Color; +use Intervention\Image\Exceptions\DecoderException; use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\DecoderInterface; use Intervention\Image\Interfaces\ImageInterface; @@ -12,14 +12,14 @@ class HexColorDecoder extends ArrayColorDecoder implements DecoderInterface public function decode($input): ImageInterface|ColorInterface { if (!is_string($input)) { - $this->fail(); + throw new DecoderException('Unable to decode input'); } $pattern = '/^#?([a-f0-9]{1,2})([a-f0-9]{1,2})([a-f0-9]{1,2})$/i'; $result = preg_match($pattern, $input, $matches); if ($result !== 1) { - $this->fail(); + throw new DecoderException('Unable to decode input'); } return parent::decode([ diff --git a/src/Drivers/Imagick/Decoders/ImageObjectDecoder.php b/src/Drivers/Imagick/Decoders/ImageObjectDecoder.php index 72318e2a..9f72634f 100644 --- a/src/Drivers/Imagick/Decoders/ImageObjectDecoder.php +++ b/src/Drivers/Imagick/Decoders/ImageObjectDecoder.php @@ -3,6 +3,7 @@ namespace Intervention\Image\Drivers\Imagick\Decoders; use Intervention\Image\Drivers\Abstract\Decoders\AbstractDecoder; +use Intervention\Image\Exceptions\DecoderException; use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\DecoderInterface; use Intervention\Image\Interfaces\ImageInterface; @@ -12,7 +13,7 @@ class ImageObjectDecoder extends AbstractDecoder implements DecoderInterface public function decode($input): ImageInterface|ColorInterface { if (! is_a($input, ImageInterface::class)) { - $this->fail(); + throw new DecoderException('Unable to decode input'); } return $input; diff --git a/src/Drivers/Imagick/Decoders/RgbStringColorDecoder.php b/src/Drivers/Imagick/Decoders/RgbStringColorDecoder.php index 4e9c9fa2..7b036033 100644 --- a/src/Drivers/Imagick/Decoders/RgbStringColorDecoder.php +++ b/src/Drivers/Imagick/Decoders/RgbStringColorDecoder.php @@ -6,27 +6,27 @@ use ImagickPixel; use ImagickPixelException; use Intervention\Image\Drivers\Abstract\Decoders\AbstractDecoder; use Intervention\Image\Drivers\Imagick\Color; +use Intervention\Image\Exceptions\DecoderException; use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\DecoderInterface; use Intervention\Image\Interfaces\ImageInterface; -use Intervention\Image\Traits\CanValidateColors; class RgbStringColorDecoder extends AbstractDecoder implements DecoderInterface { public function decode($input): ImageInterface|ColorInterface { if (!is_string($input)) { - $this->fail(); + throw new DecoderException('Unable to decode input'); } if (substr($input, 0, 3) !== 'rgb') { - $this->fail(); + throw new DecoderException('Unable to decode input'); } try { $pixel = new ImagickPixel($input); } catch (ImagickPixelException $e) { - $this->fail(); + throw new DecoderException('Unable to decode input'); } return new Color($pixel); diff --git a/src/Drivers/Imagick/Decoders/TransparentColorDecoder.php b/src/Drivers/Imagick/Decoders/TransparentColorDecoder.php index 5d3e3153..b93ef706 100644 --- a/src/Drivers/Imagick/Decoders/TransparentColorDecoder.php +++ b/src/Drivers/Imagick/Decoders/TransparentColorDecoder.php @@ -2,18 +2,17 @@ namespace Intervention\Image\Drivers\Imagick\Decoders; -use Intervention\Image\Drivers\Imagick\Color; +use Intervention\Image\Exceptions\DecoderException; use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\DecoderInterface; use Intervention\Image\Interfaces\ImageInterface; -use Intervention\Image\Traits\CanValidateColors; class TransparentColorDecoder extends ArrayColorDecoder implements DecoderInterface { public function decode($input): ImageInterface|ColorInterface { if (! is_string($input) || strtolower($input) !== 'transparent') { - $this->fail(); + throw new DecoderException('Unable to decode input'); } return parent::decode([0, 0, 0, 0]); From f3b4092fac3ab1a885f3a0ed94d95573dc661c6c Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 22 May 2022 18:50:14 +0200 Subject: [PATCH 163/476] Wrap code line --- src/Traits/CanDecodeDataUri.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Traits/CanDecodeDataUri.php b/src/Traits/CanDecodeDataUri.php index 350f4096..b19f06cd 100644 --- a/src/Traits/CanDecodeDataUri.php +++ b/src/Traits/CanDecodeDataUri.php @@ -11,7 +11,9 @@ trait CanDecodeDataUri */ protected function decodeDataUri($value): object { - $pattern = "/^data:(?P\w+\/[-+.\w]+)?(?P(;[-\w]+=[-\w]+)*)(?P;base64)?,(?P.*)/"; + $pattern = "/^data:(?P\w+\/[-+.\w]+)?" . + "(?P(;[-\w]+=[-\w]+)*)(?P;base64)?,(?P.*)/"; + $result = preg_match($pattern, $value, $matches); return new class ($matches, $result) From bdcf6d490c90fb4773c3012b875b93288f33d4ca Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 22 May 2022 18:52:52 +0200 Subject: [PATCH 164/476] Reformat code --- src/Drivers/Gd/Encoders/GifEncoder.php | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/Drivers/Gd/Encoders/GifEncoder.php b/src/Drivers/Gd/Encoders/GifEncoder.php index 9438e8a8..31227a15 100644 --- a/src/Drivers/Gd/Encoders/GifEncoder.php +++ b/src/Drivers/Gd/Encoders/GifEncoder.php @@ -25,8 +25,13 @@ class GifEncoder extends AbstractEncoder implements EncoderInterface protected function encodeAnimated($image): EncodedImage { - $builder = GifBuilder::canvas($image->getWidth(), $image->getHeight(), $image->getLoops()); - foreach ($image as $key => $frame) { + $builder = GifBuilder::canvas( + $image->getWidth(), + $image->getHeight(), + $image->getLoops() + ); + + foreach ($image as $frame) { $source = $this->encode($frame->toImage()); $builder->addFrame($source, $frame->getDelay()); } From 38e01fc33982e2a43c1e440ab27db70c8a98070d Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 22 May 2022 18:53:34 +0200 Subject: [PATCH 165/476] Add EncoderInterface implementation to AbstractDecoder --- src/Drivers/Abstract/Encoders/AbstractEncoder.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Drivers/Abstract/Encoders/AbstractEncoder.php b/src/Drivers/Abstract/Encoders/AbstractEncoder.php index 726093eb..36a0c073 100644 --- a/src/Drivers/Abstract/Encoders/AbstractEncoder.php +++ b/src/Drivers/Abstract/Encoders/AbstractEncoder.php @@ -4,7 +4,7 @@ namespace Intervention\Image\Drivers\Abstract\Encoders; use Intervention\Image\Interfaces\EncoderInterface; -abstract class AbstractEncoder +abstract class AbstractEncoder implements EncoderInterface { protected $quality; From 2de45528dd42e6844bae4b1715b4ee1ba076beb3 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 22 May 2022 18:59:53 +0200 Subject: [PATCH 166/476] Add method to SizeInterface --- src/Interfaces/SizeInterface.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Interfaces/SizeInterface.php b/src/Interfaces/SizeInterface.php index 92ba6674..ba8fef44 100644 --- a/src/Interfaces/SizeInterface.php +++ b/src/Interfaces/SizeInterface.php @@ -16,4 +16,5 @@ interface SizeInterface public function isPortrait(): bool; public function alignPivot(string $position, int $offset_x = 0, int $offset_y = 0): SizeInterface; public function alignPivotTo(SizeInterface $size, string $position): SizeInterface; + public function contain(int $width, int $height): SizeInterface; } From 3da3bad2ab4b31050667cc1c87727d7ae67bdd99 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 22 May 2022 19:03:49 +0200 Subject: [PATCH 167/476] Add method to SizeInterface --- src/Geometry/Size.php | 2 +- src/Interfaces/SizeInterface.php | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Geometry/Size.php b/src/Geometry/Size.php index d05d0417..9bffaf07 100644 --- a/src/Geometry/Size.php +++ b/src/Geometry/Size.php @@ -201,7 +201,7 @@ class Size implements SizeInterface * @param Size $size * @return Point */ - public function getRelativePositionTo(Size $size): Point + public function getRelativePositionTo(SizeInterface $size): PointInterface { $x = $this->getPivot()->getX() - $size->getPivot()->getX(); $y = $this->getPivot()->getY() - $size->getPivot()->getY(); diff --git a/src/Interfaces/SizeInterface.php b/src/Interfaces/SizeInterface.php index ba8fef44..da4a6821 100644 --- a/src/Interfaces/SizeInterface.php +++ b/src/Interfaces/SizeInterface.php @@ -17,4 +17,5 @@ interface SizeInterface public function alignPivot(string $position, int $offset_x = 0, int $offset_y = 0): SizeInterface; public function alignPivotTo(SizeInterface $size, string $position): SizeInterface; public function contain(int $width, int $height): SizeInterface; + public function getRelativePositionTo(SizeInterface $size): PointInterface; } From a796553b6d4ef84c18f8d9a451e97d99602a7ca7 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 22 May 2022 19:04:00 +0200 Subject: [PATCH 168/476] Remove unused code --- src/Drivers/Gd/Modifiers/FillModifier.php | 2 +- src/Drivers/Gd/Modifiers/FitModifier.php | 3 --- src/Drivers/Gd/Modifiers/PadModifier.php | 1 - 3 files changed, 1 insertion(+), 5 deletions(-) diff --git a/src/Drivers/Gd/Modifiers/FillModifier.php b/src/Drivers/Gd/Modifiers/FillModifier.php index 70e5fe2c..0f747342 100644 --- a/src/Drivers/Gd/Modifiers/FillModifier.php +++ b/src/Drivers/Gd/Modifiers/FillModifier.php @@ -17,7 +17,7 @@ class FillModifier implements ModifierInterface public function apply(ImageInterface $image): ImageInterface { - foreach ($image as $key => $frame) { + foreach ($image as $frame) { if ($this->hasPosition()) { $this->floodFillWithColor($frame); } else { diff --git a/src/Drivers/Gd/Modifiers/FitModifier.php b/src/Drivers/Gd/Modifiers/FitModifier.php index de0e9725..cf55b8c0 100644 --- a/src/Drivers/Gd/Modifiers/FitModifier.php +++ b/src/Drivers/Gd/Modifiers/FitModifier.php @@ -3,13 +3,10 @@ namespace Intervention\Image\Drivers\Gd\Modifiers; use Intervention\Image\Drivers\Abstract\Modifiers\AbstractFitModifier; -use Intervention\Image\Geometry\Size; use Intervention\Image\Interfaces\FrameInterface; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; use Intervention\Image\Interfaces\SizeInterface; -use Intervention\Image\Traits\CanHandleInput; -use Intervention\Image\Traits\CanResizeGeometrically; class FitModifier extends AbstractFitModifier implements ModifierInterface { diff --git a/src/Drivers/Gd/Modifiers/PadModifier.php b/src/Drivers/Gd/Modifiers/PadModifier.php index 630bb284..a8e78445 100644 --- a/src/Drivers/Gd/Modifiers/PadModifier.php +++ b/src/Drivers/Gd/Modifiers/PadModifier.php @@ -9,7 +9,6 @@ use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; use Intervention\Image\Interfaces\SizeInterface; use Intervention\Image\Traits\CanHandleInput; -use Intervention\Image\Traits\CanResizeGeometrically; class PadModifier extends AbstractPadModifier implements ModifierInterface { From 5397914d69c032201a391b1bfb000f52276aa87a Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 22 May 2022 19:08:22 +0200 Subject: [PATCH 169/476] Add missing methods to SizeInterface --- src/Geometry/Size.php | 18 +++++++++--------- src/Interfaces/SizeInterface.php | 8 ++++++-- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/src/Geometry/Size.php b/src/Geometry/Size.php index 9bffaf07..04886285 100644 --- a/src/Geometry/Size.php +++ b/src/Geometry/Size.php @@ -50,7 +50,7 @@ class Size implements SizeInterface return $this->pivot; } - public function setPivot(PointInterface $pivot): self + public function setPivot(PointInterface $pivot): SizeInterface { $this->pivot = $pivot; @@ -104,7 +104,7 @@ class Size implements SizeInterface * @param int $offset_y * @return Size */ - public function alignPivot(string $position, int $offset_x = 0, int $offset_y = 0): self + public function alignPivot(string $position, int $offset_x = 0, int $offset_y = 0): SizeInterface { switch (strtolower($position)) { case 'top': @@ -182,7 +182,7 @@ class Size implements SizeInterface return $this; } - public function alignPivotTo(SizeInterface $size, string $position): self + public function alignPivotTo(SizeInterface $size, string $position): SizeInterface { $reference = new Size($size->getWidth(), $size->getHeight()); $reference->alignPivot($position); @@ -214,32 +214,32 @@ class Size implements SizeInterface return new Resizer($width, $height); } - public function resize(?int $width = null, ?int $height = null): self + public function resize(?int $width = null, ?int $height = null): SizeInterface { return $this->getResizer($width, $height)->resize($this); } - public function resizeDown(?int $width = null, ?int $height = null): self + public function resizeDown(?int $width = null, ?int $height = null): SizeInterface { return $this->getResizer($width, $height)->resizeDown($this); } - public function scale(?int $width = null, ?int $height = null): self + public function scale(?int $width = null, ?int $height = null): SizeInterface { return $this->getResizer($width, $height)->scale($this); } - public function scaleDown(?int $width = null, ?int $height = null): self + public function scaleDown(?int $width = null, ?int $height = null): SizeInterface { return $this->getResizer($width, $height)->scaleDown($this); } - public function cover(int $width, int $height): self + public function cover(int $width, int $height): SizeInterface { return $this->getResizer($width, $height)->cover($this); } - public function contain(int $width, int $height): self + public function contain(int $width, int $height): SizeInterface { return $this->getResizer($width, $height)->contain($this); } diff --git a/src/Interfaces/SizeInterface.php b/src/Interfaces/SizeInterface.php index da4a6821..7aaef57d 100644 --- a/src/Interfaces/SizeInterface.php +++ b/src/Interfaces/SizeInterface.php @@ -9,13 +9,17 @@ interface SizeInterface public function getPivot(): PointInterface; public function setWidth(int $width): SizeInterface; public function setHeight(int $height): SizeInterface; - public function resize(?int $width = null, ?int $height = null): SizeInterface; public function getAspectRatio(): float; public function fitsInto(SizeInterface $size): bool; public function isLandscape(): bool; public function isPortrait(): bool; public function alignPivot(string $position, int $offset_x = 0, int $offset_y = 0): SizeInterface; public function alignPivotTo(SizeInterface $size, string $position): SizeInterface; - public function contain(int $width, int $height): SizeInterface; public function getRelativePositionTo(SizeInterface $size): PointInterface; + public function resize(?int $width = null, ?int $height = null): SizeInterface; + public function resizeDown(?int $width = null, ?int $height = null): SizeInterface; + public function scale(?int $width = null, ?int $height = null): SizeInterface; + public function scaleDown(?int $width = null, ?int $height = null): SizeInterface; + public function cover(int $width, int $height): SizeInterface; + public function contain(int $width, int $height): SizeInterface; } From 7433c123d53251dbd75b1c876fd5b405e3a2ec96 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 22 May 2022 19:16:33 +0200 Subject: [PATCH 170/476] Remove unused code --- src/Drivers/Imagick/Modifiers/FillModifier.php | 4 ++-- src/Drivers/Imagick/Modifiers/FitModifier.php | 3 --- src/Drivers/Imagick/Modifiers/PadModifier.php | 1 - 3 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/Drivers/Imagick/Modifiers/FillModifier.php b/src/Drivers/Imagick/Modifiers/FillModifier.php index ddcf19df..a6cc51a4 100644 --- a/src/Drivers/Imagick/Modifiers/FillModifier.php +++ b/src/Drivers/Imagick/Modifiers/FillModifier.php @@ -4,16 +4,16 @@ namespace Intervention\Image\Drivers\Imagick\Modifiers; use Imagick; use ImagickDraw; +use Intervention\Image\Drivers\Imagick\Color; use Intervention\Image\Drivers\Imagick\Frame; use Intervention\Image\Geometry\Point; -use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; class FillModifier implements ModifierInterface { public function __construct( - protected ColorInterface $color, + protected Color $color, protected ?Point $position = null ) { // diff --git a/src/Drivers/Imagick/Modifiers/FitModifier.php b/src/Drivers/Imagick/Modifiers/FitModifier.php index 5b34f702..f5465c5f 100644 --- a/src/Drivers/Imagick/Modifiers/FitModifier.php +++ b/src/Drivers/Imagick/Modifiers/FitModifier.php @@ -3,11 +3,8 @@ namespace Intervention\Image\Drivers\Imagick\Modifiers; use Intervention\Image\Drivers\Abstract\Modifiers\AbstractFitModifier; -use Intervention\Image\Geometry\Size; -use Intervention\Image\Interfaces\FrameInterface; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; -use Intervention\Image\Interfaces\SizeInterface; class FitModifier extends AbstractFitModifier implements ModifierInterface { diff --git a/src/Drivers/Imagick/Modifiers/PadModifier.php b/src/Drivers/Imagick/Modifiers/PadModifier.php index 414efc97..94bee08e 100644 --- a/src/Drivers/Imagick/Modifiers/PadModifier.php +++ b/src/Drivers/Imagick/Modifiers/PadModifier.php @@ -6,7 +6,6 @@ use Imagick; use ImagickDraw; use Intervention\Image\Drivers\Abstract\Modifiers\AbstractPadModifier; use Intervention\Image\Interfaces\ColorInterface; -use Intervention\Image\Interfaces\FrameInterface; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; use Intervention\Image\Interfaces\SizeInterface; From fda458ff959ce5f05ac57e244a7c66ecd702b1ba Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Tue, 24 May 2022 19:31:53 +0200 Subject: [PATCH 171/476] Add modifier TextWriter --- src/Drivers/Abstract/AbstractFont.php | 108 +++++++++++++++++++ src/Drivers/Abstract/AbstractImage.php | 9 ++ src/Drivers/Gd/Font.php | 77 +++++++++++++ src/Drivers/Gd/Modifiers/TextWriter.php | 80 ++++++++++++++ src/Drivers/Imagick/Font.php | 73 +++++++++++++ src/Drivers/Imagick/Modifiers/TextWriter.php | 56 ++++++++++ src/Exceptions/FontException.php | 8 ++ src/Interfaces/FontInterface.php | 23 ++++ 8 files changed, 434 insertions(+) create mode 100644 src/Drivers/Abstract/AbstractFont.php create mode 100644 src/Drivers/Gd/Font.php create mode 100644 src/Drivers/Gd/Modifiers/TextWriter.php create mode 100644 src/Drivers/Imagick/Font.php create mode 100644 src/Drivers/Imagick/Modifiers/TextWriter.php create mode 100644 src/Exceptions/FontException.php create mode 100644 src/Interfaces/FontInterface.php diff --git a/src/Drivers/Abstract/AbstractFont.php b/src/Drivers/Abstract/AbstractFont.php new file mode 100644 index 00000000..6fee3a60 --- /dev/null +++ b/src/Drivers/Abstract/AbstractFont.php @@ -0,0 +1,108 @@ +text; + } + + public function size(float $size): self + { + $this->size = $size; + + return $this; + } + + public function getSize(): float + { + return $this->size; + } + + public function angle(float $angle): self + { + $this->angle = $angle; + + return $this; + } + + public function getAngle(): float + { + return $this->angle; + } + + public function filename(string $filename): self + { + $this->filename = $filename; + + return $this; + } + + public function getFilename(): string + { + return $this->filename; + } + + public function hasFilename(): bool + { + return is_file($this->filename); + } + + public function color($color): self + { + $this->color = $color; + + return $this; + } + + public function getColor(): ?ColorInterface + { + return $this->handleInput($this->color); + } + + public function align(string $align): self + { + $this->align = $align; + + return $this; + } + + public function getValign(): string + { + return $this->valign; + } + + public function valign(string $valign): self + { + $this->valign = $valign; + + return $this; + } + + public function getAlign(): string + { + return $this->align; + } +} diff --git a/src/Drivers/Abstract/AbstractImage.php b/src/Drivers/Abstract/AbstractImage.php index 7bf79761..50e6fb6a 100644 --- a/src/Drivers/Abstract/AbstractImage.php +++ b/src/Drivers/Abstract/AbstractImage.php @@ -13,6 +13,7 @@ use Intervention\Image\Interfaces\ModifierInterface; use Intervention\Image\Interfaces\SizeInterface; use Intervention\Image\Traits\CanHandleInput; use Intervention\Image\Traits\CanResolveDriverClass; +use Intervention\Image\Interfaces\FontInterface; abstract class AbstractImage implements ImageInterface { @@ -235,6 +236,14 @@ abstract class AbstractImage implements ImageInterface return $colors; } + public function text(string $text, int $x, int $y, ?callable $init = null): ImageInterface + { + $font = $this->resolveDriverClass('Font', $text, $init); + $modifier = $this->resolveDriverClass('Modifiers\TextWriter', new Point($x, $y), $font); + + return $this->modify($modifier); + } + public function resize(?int $width = null, ?int $height = null): ImageInterface { return $this->modify( diff --git a/src/Drivers/Gd/Font.php b/src/Drivers/Gd/Font.php new file mode 100644 index 00000000..90a15b0c --- /dev/null +++ b/src/Drivers/Gd/Font.php @@ -0,0 +1,77 @@ +hasFilename()) { + // calculate box size from gd font + $box = new Size(0, 0); + $chars = mb_strlen($this->getText()); + if ($chars > 0) { + $box->setWidth($chars * $this->getGdFontWidth()); + $box->setHeight($this->getGdFontHeight()); + } + return $box; + } + + // calculate box size from font file + $box = imageftbbox( + $this->getSize(), + $this->getAngle(), + $this->getFilename(), + $this->getText() + ); + + return new Size(abs($box[0] - $box[2]), abs($box[1] - $box[7])); + } + + public function getGdFont(): int + { + if (is_numeric($this->filename)) { + return $this->filename; + } + + return 1; + } + + protected function getGdFontWidth(): int + { + return $this->getGdFont() + 4; + } + + protected function getGdFontHeight(): int + { + switch ($this->getGdFont()) { + case 1: + return 8; + + case 2: + return 14; + + case 3: + return 14; + + case 4: + return 16; + + case 5: + return 16; + } + } +} diff --git a/src/Drivers/Gd/Modifiers/TextWriter.php b/src/Drivers/Gd/Modifiers/TextWriter.php new file mode 100644 index 00000000..6c6277c3 --- /dev/null +++ b/src/Drivers/Gd/Modifiers/TextWriter.php @@ -0,0 +1,80 @@ +getAlignedPosition(); + + foreach ($image as $frame) { + if ($this->font->hasFilename()) { + imagettftext( + $frame->getCore(), + $this->font->getSize(), + $this->font->getAngle(), + $position->getX(), + $position->getY(), + $this->font->getColor()->toInt(), + $this->font->getFilename(), + $this->font->getText() + ); + } else { + imagestring( + $frame->getCore(), + $this->font->getGdFont(), + $position->getX(), + $position->getY(), + $this->font->getText(), + $this->font->getColor()->toInt() + ); + } + } + + return $image; + } + + protected function getAlignedPosition(): Point + { + $position = $this->position; + $box = $this->font->getBoxSize(); + + // adjust x pos + switch ($this->font->getAlign()) { + case 'center': + $position->setX($position->getX() - round($box->getWidth() / 2)); + break; + + case 'right': + $position->setX($position->getX() - $box->getWidth()); + break; + } + + // adjust y pos + switch ($this->font->getValign()) { + case 'top': + $position->setY($position->getY() + $box->getHeight()); + break; + + case 'middle': + case 'center': + $position->setY($position->getY() + round($box->getHeight() / 2)); + break; + } + + return $position; + } +} diff --git a/src/Drivers/Imagick/Font.php b/src/Drivers/Imagick/Font.php new file mode 100644 index 00000000..df32d284 --- /dev/null +++ b/src/Drivers/Imagick/Font.php @@ -0,0 +1,73 @@ +hasFilename()) { + throw new FontException('No font file.'); + } + + $draw = new ImagickDraw(); + $draw->setStrokeAntialias(true); + $draw->setTextAntialias(true); + $draw->setFont($this->getFilename()); + $draw->setFontSize($this->getSize()); + $draw->setFillColor($this->getColor()->getPixel()); + $draw->setTextAlignment($this->getImagickAlign()); + + return $draw; + } + + public function getAngle(): float + { + return parent::getAngle() * (-1); + } + + public function getImagickAlign(): int + { + switch (strtolower($this->getAlign())) { + case 'center': + return Imagick::ALIGN_CENTER; + break; + + case 'right': + return Imagick::ALIGN_RIGHT; + break; + } + + return Imagick::ALIGN_LEFT; + } + + /** + * Calculate box size of current font + * + * @return Size + */ + public function getBoxSize(): Size + { + $foo = null; + // no text - no box size + if (mb_strlen($this->getText()) === 0) { + return new Size(0, 0); + } + + $dimensions = (new Imagick())->queryFontMetrics( + $this->toImagickDraw(), + $this->getText() + ); + + return new Size( + intval(abs($dimensions['textWidth'])), + intval(abs($dimensions['textHeight'])) + ); + } +} diff --git a/src/Drivers/Imagick/Modifiers/TextWriter.php b/src/Drivers/Imagick/Modifiers/TextWriter.php new file mode 100644 index 00000000..52c6b7be --- /dev/null +++ b/src/Drivers/Imagick/Modifiers/TextWriter.php @@ -0,0 +1,56 @@ +font->toImagickDraw(); + $position = $this->getAlignedPosition(); + + foreach ($image as $frame) { + $frame->getCore()->annotateImage( + $draw, + $position->getX(), + $position->getY(), + $this->font->getAngle(), + $this->font->getText() + ); + } + + return $image; + } + + protected function getAlignedPosition(): Point + { + $position = $this->position; + $box = $this->font->getBoxSize(); + + // adjust y pos + switch ($this->font->getValign()) { + case 'top': + $position->setY($position->getY() + $box->getHeight()); + break; + + case 'middle': + case 'center': + $position->setY($position->getY() + round($box->getHeight() / 2)); + break; + } + + return $position; + } +} diff --git a/src/Exceptions/FontException.php b/src/Exceptions/FontException.php new file mode 100644 index 00000000..be32f177 --- /dev/null +++ b/src/Exceptions/FontException.php @@ -0,0 +1,8 @@ + Date: Wed, 15 Jun 2022 10:00:33 +0200 Subject: [PATCH 172/476] Add BinaryImageDecoderTest for Imagick driver --- .../Decoders/BinaryImageDecoderTest.php | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 tests/Drivers/Imagick/Decoders/BinaryImageDecoderTest.php diff --git a/tests/Drivers/Imagick/Decoders/BinaryImageDecoderTest.php b/tests/Drivers/Imagick/Decoders/BinaryImageDecoderTest.php new file mode 100644 index 00000000..455bdcc7 --- /dev/null +++ b/tests/Drivers/Imagick/Decoders/BinaryImageDecoderTest.php @@ -0,0 +1,40 @@ +decode(file_get_contents(__DIR__ . '/../../../images/tile.png')); + $this->assertInstanceOf(Image::class, $image); + $this->assertEquals(16, $image->getWidth()); + $this->assertEquals(16, $image->getHeight()); + $this->assertCount(1, $image); + } + + public function testDecodeGif(): void + { + $decoder = new BinaryImageDecoder(); + $image = $decoder->decode(file_get_contents(__DIR__ . '/../../../images/red.gif')); + $this->assertInstanceOf(Image::class, $image); + $this->assertEquals(16, $image->getWidth()); + $this->assertEquals(16, $image->getHeight()); + $this->assertCount(1, $image); + } + + public function testDecodeAnimatedGif(): void + { + $decoder = new BinaryImageDecoder(); + $image = $decoder->decode(file_get_contents(__DIR__ . '/../../../images/cats.gif')); + $this->assertInstanceOf(Image::class, $image); + $this->assertEquals(75, $image->getWidth()); + $this->assertEquals(50, $image->getHeight()); + $this->assertCount(4, $image); + } +} From ed12d374ff9a5b09199c23c67c3beed86df802c2 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Thu, 16 Jun 2022 09:18:08 +0200 Subject: [PATCH 173/476] Change signatures of ImageInterface --- src/Drivers/Abstract/AbstractImage.php | 7 ++++--- src/Interfaces/ImageInterface.php | 7 +++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Drivers/Abstract/AbstractImage.php b/src/Drivers/Abstract/AbstractImage.php index 7bf79761..530e5291 100644 --- a/src/Drivers/Abstract/AbstractImage.php +++ b/src/Drivers/Abstract/AbstractImage.php @@ -6,6 +6,7 @@ use Intervention\Image\Collection; use Intervention\Image\EncodedImage; use Intervention\Image\Geometry\Point; use Intervention\Image\Geometry\Size; +use Intervention\Image\Interfaces\CollectionInterface; use Intervention\Image\Interfaces\EncoderInterface; use Intervention\Image\Interfaces\FrameInterface; use Intervention\Image\Interfaces\ImageInterface; @@ -24,12 +25,12 @@ abstract class AbstractImage implements ImageInterface // } - public function getIterator(): Collection + public function getIterator(): CollectionInterface { return $this->frames; } - public function getFrames(): Collection + public function getFrames(): CollectionInterface { return $this->frames; } @@ -225,7 +226,7 @@ abstract class AbstractImage implements ImageInterface ); } - public function pickColors(int $x, int $y): Collection + public function pickColors(int $x, int $y): CollectionInterface { $colors = new Collection(); foreach ($this->getFrames() as $key => $frame) { diff --git a/src/Interfaces/ImageInterface.php b/src/Interfaces/ImageInterface.php index 182a76dc..5f16c4d7 100644 --- a/src/Interfaces/ImageInterface.php +++ b/src/Interfaces/ImageInterface.php @@ -2,13 +2,12 @@ namespace Intervention\Image\Interfaces; -use Intervention\Image\Collection; use Intervention\Image\EncodedImage; interface ImageInterface { - public function getIterator(): Collection; - public function getFrames(): Collection; + public function getIterator(): CollectionInterface; + public function getFrames(): CollectionInterface; public function getFrame(int $key = 0): ?FrameInterface; public function addFrame(FrameInterface $frame): ImageInterface; public function setLoops(int $count): ImageInterface; @@ -21,7 +20,7 @@ interface ImageInterface public function toWebp(int $quality = 75): EncodedImage; public function toGif(): EncodedImage; public function toPng(): EncodedImage; - public function pickColors(int $x, int $y): Collection; + public function pickColors(int $x, int $y): CollectionInterface; public function pickColor(int $x, int $y, int $frame_key = 0): ?ColorInterface; public function greyscale(): ImageInterface; public function blur(int $amount = 5): ImageInterface; From a7eb80f4f1cc7d7576711409d1c8bb99f43906f6 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 18 Jun 2022 16:02:11 +0200 Subject: [PATCH 174/476] Add Collection::get() method --- src/Collection.php | 5 +++++ src/Interfaces/CollectionInterface.php | 1 + tests/CollectionTest.php | 8 ++++++++ 3 files changed, 14 insertions(+) diff --git a/src/Collection.php b/src/Collection.php index 90fbefaf..e1e37a6c 100644 --- a/src/Collection.php +++ b/src/Collection.php @@ -109,6 +109,11 @@ class Collection implements CollectionInterface, IteratorAggregate, Countable return $this->items[$key]; } + public function has(int $key): bool + { + return array_key_exists($key, $this->items); + } + public function query(string $query, $default = null) { $items = $this->getItemsFlat(); diff --git a/src/Interfaces/CollectionInterface.php b/src/Interfaces/CollectionInterface.php index 5c63e5a7..d51cee76 100644 --- a/src/Interfaces/CollectionInterface.php +++ b/src/Interfaces/CollectionInterface.php @@ -6,6 +6,7 @@ interface CollectionInterface { public function push($item): CollectionInterface; public function get(int $key); + public function has(int $key); public function first(); public function last(); public function count(): int; diff --git a/tests/CollectionTest.php b/tests/CollectionTest.php index b4a6738b..9703486e 100644 --- a/tests/CollectionTest.php +++ b/tests/CollectionTest.php @@ -86,6 +86,14 @@ class CollectionTest extends TestCase $this->assertEquals('test', $collection->get(3, 'test')); } + public function testHas(): void + { + $collection = new Collection(['foo', 'bar']); + $this->assertTrue($collection->has(0)); + $this->assertTrue($collection->has(1)); + $this->assertFalse($collection->has(2)); + } + public function testToArray() { $collection = new Collection(['foo', 'bar', 'baz']); From dc3253feefc0a2fdf245639962fa9b837fa88fd8 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 18 Jun 2022 16:07:36 +0200 Subject: [PATCH 175/476] Add default attribute to CollectionInterface --- src/Interfaces/CollectionInterface.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Interfaces/CollectionInterface.php b/src/Interfaces/CollectionInterface.php index d51cee76..fc87d626 100644 --- a/src/Interfaces/CollectionInterface.php +++ b/src/Interfaces/CollectionInterface.php @@ -5,7 +5,7 @@ namespace Intervention\Image\Interfaces; interface CollectionInterface { public function push($item): CollectionInterface; - public function get(int $key); + public function get(int $key, $default = null); public function has(int $key); public function first(); public function last(); From 90ed724cb582598cad3fdcb2a37a2750e9ac846c Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Mon, 20 Jun 2022 16:33:38 +0200 Subject: [PATCH 176/476] Refactor storage of Imagick core, remove method Imagick core image was disassembled in the decoding process. This made the whole object not very memory efficient. This fixes the issue by keeping the original Imagick object in the Intervention Image object. Also the Image::getFrames() method was removed. Users should use iteration to access frames. --- src/Collection.php | 5 +- src/Drivers/Abstract/AbstractImage.php | 47 +------- src/Drivers/Gd/Encoders/GifEncoder.php | 2 +- src/Drivers/Gd/Encoders/JpegEncoder.php | 2 +- src/Drivers/Gd/Encoders/PngEncoder.php | 2 +- src/Drivers/Gd/Encoders/WebpEncoder.php | 2 +- src/Drivers/Gd/Image.php | 47 ++++++++ .../Imagick/Decoders/BinaryImageDecoder.php | 10 +- src/Drivers/Imagick/Encoders/GifEncoder.php | 2 +- src/Drivers/Imagick/Encoders/JpegEncoder.php | 2 +- src/Drivers/Imagick/Encoders/PngEncoder.php | 2 +- src/Drivers/Imagick/Encoders/WebpEncoder.php | 2 +- src/Drivers/Imagick/Frame.php | 3 +- src/Drivers/Imagick/Image.php | 109 +++++++++++++++++- src/Drivers/Imagick/ImageFactory.php | 8 +- src/Interfaces/CollectionInterface.php | 4 +- src/Interfaces/ImageInterface.php | 6 +- tests/Drivers/Abstract/AbstractImageTest.php | 58 +--------- tests/Drivers/Gd/ImageTest.php | 16 +-- .../Imagick/Encoders/GifEncoderTest.php | 30 ++--- .../Imagick/Encoders/JpegEncoderTest.php | 5 +- .../Imagick/Encoders/PngEncoderTest.php | 5 +- .../Imagick/Encoders/WebpEncoderTest.php | 5 +- tests/Drivers/Imagick/ImageTest.php | 41 ++++++- 24 files changed, 241 insertions(+), 174 deletions(-) diff --git a/src/Collection.php b/src/Collection.php index e1e37a6c..5434f83b 100644 --- a/src/Collection.php +++ b/src/Collection.php @@ -6,6 +6,7 @@ use Intervention\Image\Exceptions\RuntimeException; use Intervention\Image\Interfaces\CollectionInterface; use ArrayIterator; use Countable; +use Traversable; use IteratorAggregate; use RecursiveIteratorIterator; use RecursiveArrayIterator; @@ -31,9 +32,9 @@ class Collection implements CollectionInterface, IteratorAggregate, Countable /** * Returns Iterator * - * @return \Traversable + * @return Traversable */ - public function getIterator(): \Traversable + public function getIterator(): Traversable { return new ArrayIterator($this->items); } diff --git a/src/Drivers/Abstract/AbstractImage.php b/src/Drivers/Abstract/AbstractImage.php index 530e5291..29fd8f0a 100644 --- a/src/Drivers/Abstract/AbstractImage.php +++ b/src/Drivers/Abstract/AbstractImage.php @@ -8,7 +8,6 @@ use Intervention\Image\Geometry\Point; use Intervention\Image\Geometry\Size; use Intervention\Image\Interfaces\CollectionInterface; use Intervention\Image\Interfaces\EncoderInterface; -use Intervention\Image\Interfaces\FrameInterface; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; use Intervention\Image\Interfaces\SizeInterface; @@ -20,45 +19,6 @@ abstract class AbstractImage implements ImageInterface use CanResolveDriverClass; use CanHandleInput; - public function __construct(protected Collection $frames, protected $loops = 0) - { - // - } - - public function getIterator(): CollectionInterface - { - return $this->frames; - } - - public function getFrames(): CollectionInterface - { - return $this->frames; - } - - public function getFrame(int $key = 0): ?FrameInterface - { - return $this->frames->get($key); - } - - public function addFrame(FrameInterface $frame): ImageInterface - { - $this->frames->push($frame); - - return $this; - } - - public function setLoops(int $count): ImageInterface - { - $this->loops = $count; - - return $this; - } - - public function getLoops(): int - { - return $this->loops; - } - public function getSize(): SizeInterface { return new Size($this->getWidth(), $this->getHeight()); @@ -69,11 +29,6 @@ abstract class AbstractImage implements ImageInterface return $this->getSize(); } - public function isAnimated(): bool - { - return $this->getFrames()->count() > 1; - } - public function modify(ModifierInterface $modifier): ImageInterface { return $modifier->apply($this); @@ -229,7 +184,7 @@ abstract class AbstractImage implements ImageInterface public function pickColors(int $x, int $y): CollectionInterface { $colors = new Collection(); - foreach ($this->getFrames() as $key => $frame) { + foreach ($this as $key => $frame) { $colors->push($this->pickColor($x, $y, $key)); } diff --git a/src/Drivers/Gd/Encoders/GifEncoder.php b/src/Drivers/Gd/Encoders/GifEncoder.php index 31227a15..159cc2cd 100644 --- a/src/Drivers/Gd/Encoders/GifEncoder.php +++ b/src/Drivers/Gd/Encoders/GifEncoder.php @@ -17,7 +17,7 @@ class GifEncoder extends AbstractEncoder implements EncoderInterface } $data = $this->getBuffered(function () use ($image) { - imagegif($image->getFrames()->first()->getCore()); + imagegif($image->getFrame()->getCore()); }); return new EncodedImage($data, 'image/gif'); diff --git a/src/Drivers/Gd/Encoders/JpegEncoder.php b/src/Drivers/Gd/Encoders/JpegEncoder.php index 09e695e7..2a8d41da 100644 --- a/src/Drivers/Gd/Encoders/JpegEncoder.php +++ b/src/Drivers/Gd/Encoders/JpegEncoder.php @@ -17,7 +17,7 @@ class JpegEncoder extends AbstractEncoder implements EncoderInterface public function encode(ImageInterface $image): EncodedImage { $data = $this->getBuffered(function () use ($image) { - imagejpeg($image->getFrames()->first()->getCore(), null, $this->quality); + imagejpeg($image->getFrame()->getCore(), null, $this->quality); }); return new EncodedImage($data, 'image/jpeg'); diff --git a/src/Drivers/Gd/Encoders/PngEncoder.php b/src/Drivers/Gd/Encoders/PngEncoder.php index bc578b47..4369ef14 100644 --- a/src/Drivers/Gd/Encoders/PngEncoder.php +++ b/src/Drivers/Gd/Encoders/PngEncoder.php @@ -12,7 +12,7 @@ class PngEncoder extends AbstractEncoder implements EncoderInterface public function encode(ImageInterface $image): EncodedImage { $data = $this->getBuffered(function () use ($image) { - imagepng($image->getFrames()->first()->getCore(), null, -1); + imagepng($image->getFrame()->getCore(), null, -1); }); return new EncodedImage($data, 'image/png'); diff --git a/src/Drivers/Gd/Encoders/WebpEncoder.php b/src/Drivers/Gd/Encoders/WebpEncoder.php index 748db2a2..581358f0 100644 --- a/src/Drivers/Gd/Encoders/WebpEncoder.php +++ b/src/Drivers/Gd/Encoders/WebpEncoder.php @@ -17,7 +17,7 @@ class WebpEncoder extends AbstractEncoder implements EncoderInterface public function encode(ImageInterface $image): EncodedImage { $data = $this->getBuffered(function () use ($image) { - imagewebp($image->getFrames()->first()->getCore(), null, $this->quality); + imagewebp($image->getFrame()->getCore(), null, $this->quality); }); return new EncodedImage($data, 'image/webp'); diff --git a/src/Drivers/Gd/Image.php b/src/Drivers/Gd/Image.php index 38c42981..fdacd9d7 100644 --- a/src/Drivers/Gd/Image.php +++ b/src/Drivers/Gd/Image.php @@ -2,13 +2,60 @@ namespace Intervention\Image\Drivers\Gd; +use Intervention\Image\Collection; use Intervention\Image\Drivers\Abstract\AbstractImage; use Intervention\Image\Interfaces\ColorInterface; +use Intervention\Image\Interfaces\FrameInterface; use Intervention\Image\Interfaces\ImageInterface; use IteratorAggregate; +use Traversable; class Image extends AbstractImage implements ImageInterface, IteratorAggregate { + public function __construct(protected Collection $frames, protected int $loops = 0) + { + // + } + + public function getIterator(): Traversable + { + return $this->frames; + } + + public function count(): int + { + return $this->frames->count(); + } + + public function isAnimated(): bool + { + return $this->count() > 1; + } + + public function getLoops(): int + { + return $this->loops; + } + + public function setLoops(int $count): self + { + $this->loops = $count; + + return $this; + } + + public function getFrame(int $key = 0): ?FrameInterface + { + return $this->frames->get($key); + } + + public function addFrame(FrameInterface $frame): ImageInterface + { + $this->frames->push($frame); + + return $this; + } + public function getWidth(): int { return imagesx($this->getFrame()->getCore()); diff --git a/src/Drivers/Imagick/Decoders/BinaryImageDecoder.php b/src/Drivers/Imagick/Decoders/BinaryImageDecoder.php index 7a8b4e02..5cb31596 100644 --- a/src/Drivers/Imagick/Decoders/BinaryImageDecoder.php +++ b/src/Drivers/Imagick/Decoders/BinaryImageDecoder.php @@ -3,9 +3,7 @@ namespace Intervention\Image\Drivers\Imagick\Decoders; use Imagick; -use Intervention\Image\Collection; use Intervention\Image\Drivers\Abstract\Decoders\AbstractDecoder; -use Intervention\Image\Drivers\Imagick\Frame; use Intervention\Image\Drivers\Imagick\Image; use Intervention\Image\Exceptions\DecoderException; use Intervention\Image\Interfaces\ColorInterface; @@ -28,15 +26,9 @@ class BinaryImageDecoder extends AbstractDecoder implements DecoderInterface $imagick->readImageBlob($input); $imagick = $imagick->coalesceImages(); - $image = new Image(new Collection()); + $image = new Image($imagick); $image->setLoops($imagick->getImageIterations()); - foreach ($imagick as $frame_content) { - $image->addFrame( - new Frame($frame_content->getImage()) - ); - } - return $image; } } diff --git a/src/Drivers/Imagick/Encoders/GifEncoder.php b/src/Drivers/Imagick/Encoders/GifEncoder.php index 0cfca898..7524380b 100644 --- a/src/Drivers/Imagick/Encoders/GifEncoder.php +++ b/src/Drivers/Imagick/Encoders/GifEncoder.php @@ -16,7 +16,7 @@ class GifEncoder extends AbstractEncoder implements EncoderInterface $compression = Imagick::COMPRESSION_LZW; $gif = new Imagick() ; - foreach ($image->getFrames() as $frame) { + foreach ($image as $frame) { $gif->addImage($frame->getCore()); } diff --git a/src/Drivers/Imagick/Encoders/JpegEncoder.php b/src/Drivers/Imagick/Encoders/JpegEncoder.php index 0ea13e67..c6191704 100644 --- a/src/Drivers/Imagick/Encoders/JpegEncoder.php +++ b/src/Drivers/Imagick/Encoders/JpegEncoder.php @@ -20,7 +20,7 @@ class JpegEncoder extends AbstractEncoder implements EncoderInterface $format = 'jpeg'; $compression = Imagick::COMPRESSION_JPEG; - $imagick = $image->getFrames()->first()->getCore(); + $imagick = $image->getFrame()->getCore(); $imagick->setImageBackgroundColor('white'); $imagick->setBackgroundColor('white'); $imagick->setFormat($format); diff --git a/src/Drivers/Imagick/Encoders/PngEncoder.php b/src/Drivers/Imagick/Encoders/PngEncoder.php index 8bdc03e6..3599c02e 100644 --- a/src/Drivers/Imagick/Encoders/PngEncoder.php +++ b/src/Drivers/Imagick/Encoders/PngEncoder.php @@ -15,7 +15,7 @@ class PngEncoder extends AbstractEncoder implements EncoderInterface $format = 'png'; $compression = Imagick::COMPRESSION_ZIP; - $imagick = $image->getFrames()->first()->getCore(); + $imagick = $image->getFrame()->getCore(); $imagick->setFormat($format); $imagick->setImageFormat($format); $imagick->setCompression($compression); diff --git a/src/Drivers/Imagick/Encoders/WebpEncoder.php b/src/Drivers/Imagick/Encoders/WebpEncoder.php index 0f7cb9f9..3f445233 100644 --- a/src/Drivers/Imagick/Encoders/WebpEncoder.php +++ b/src/Drivers/Imagick/Encoders/WebpEncoder.php @@ -21,7 +21,7 @@ class WebpEncoder extends AbstractEncoder implements EncoderInterface $format = 'webp'; $compression = Imagick::COMPRESSION_ZIP; - $imagick = $image->getFrames()->first()->getCore(); + $imagick = $image->getFrame()->getCore(); $imagick->setImageBackgroundColor(new ImagickPixel('transparent')); $imagick = $imagick->mergeImageLayers(Imagick::LAYERMETHOD_MERGE); diff --git a/src/Drivers/Imagick/Frame.php b/src/Drivers/Imagick/Frame.php index ef4bd57b..cb49b24d 100644 --- a/src/Drivers/Imagick/Frame.php +++ b/src/Drivers/Imagick/Frame.php @@ -3,7 +3,6 @@ namespace Intervention\Image\Drivers\Imagick; use Imagick; -use Intervention\Image\Collection; use Intervention\Image\Geometry\Size; use Intervention\Image\Interfaces\FrameInterface; use Intervention\Image\Interfaces\ImageInterface; @@ -91,6 +90,6 @@ class Frame implements FrameInterface public function toImage(): ImageInterface { - return new Image(new Collection([$this])); + return new Image($this->getCore()); } } diff --git a/src/Drivers/Imagick/Image.php b/src/Drivers/Imagick/Image.php index f6507eec..e41886c9 100644 --- a/src/Drivers/Imagick/Image.php +++ b/src/Drivers/Imagick/Image.php @@ -2,21 +2,122 @@ namespace Intervention\Image\Drivers\Imagick; +use Imagick; +use ImagickException; use Intervention\Image\Drivers\Abstract\AbstractImage; use Intervention\Image\Interfaces\ColorInterface; +use Intervention\Image\Interfaces\FrameInterface; use Intervention\Image\Interfaces\ImageInterface; -use IteratorAggregate; +use Iterator; -class Image extends AbstractImage implements ImageInterface, IteratorAggregate +class Image extends AbstractImage implements ImageInterface, Iterator { + protected $iteratorIndex = 0; + + public function __construct(protected Imagick $core) + { + // + } + + public function getCore(): Imagick + { + return $this->core; + } + + public function getFrame(int $key = 0): ?FrameInterface + { + try { + $this->core->setIteratorIndex($key); + } catch (ImagickException $e) { + return null; + } + + return new Frame($this->core->current()); + } + + public function addFrame(FrameInterface $frame): ImageInterface + { + $imagick = $frame->getCore(); + + $imagick->setImageDelay($frame->getDelay()); + $imagick->setImageDispose($frame->getDispose()); + + $size = $frame->getSize(); + $imagick->setImagePage( + $size->getWidth(), + $size->getHeight(), + $frame->getOffsetLeft(), + $frame->getOffsetTop() + ); + + $this->core->addImage($imagick); + + return $this; + } + + public function setLoops(int $count): ImageInterface + { + $this->core->setImageIterations($count); + + return $this; + } + + public function getLoops(): int + { + return $this->core->getImageIterations(); + } + + public function isAnimated(): bool + { + return $this->count() > 1; + } + + public function count(): int + { + return $this->core->getNumberImages(); + } + + public function current() + { + $this->core->setIteratorIndex($this->iteratorIndex); + + return new Frame($this->core->current()); + } + + public function key() + { + return $this->iteratorIndex; + } + + public function next(): void + { + $this->iteratorIndex = $this->iteratorIndex + 1; + } + + public function rewind(): void + { + $this->iteratorIndex = 0; + } + + public function valid(): bool + { + try { + $result = $this->core->setIteratorIndex($this->iteratorIndex); + } catch (ImagickException $e) { + return false; + } + + return $result; + } + public function getWidth(): int { - return $this->frames->first()->getCore()->getImageWidth(); + return $this->getFrame()->getCore()->getImageWidth(); } public function getHeight(): int { - return $this->frames->first()->getCore()->getImageHeight(); + return $this->getFrame()->getCore()->getImageHeight(); } public function pickColor(int $x, int $y, int $frame_key = 0): ?ColorInterface diff --git a/src/Drivers/Imagick/ImageFactory.php b/src/Drivers/Imagick/ImageFactory.php index cff14438..57ef4cc3 100644 --- a/src/Drivers/Imagick/ImageFactory.php +++ b/src/Drivers/Imagick/ImageFactory.php @@ -4,8 +4,6 @@ namespace Intervention\Image\Drivers\Imagick; use Imagick; use ImagickPixel; -use Intervention\Image\Collection; -use Intervention\Image\Drivers\Imagick\Color; use Intervention\Image\Interfaces\FactoryInterface; use Intervention\Image\Interfaces\ImageInterface; @@ -13,11 +11,7 @@ class ImageFactory implements FactoryInterface { public function newImage(int $width, int $height): ImageInterface { - return new Image( - new Collection([ - new Frame($this->newCore($width, $height)) - ]) - ); + return new Image($this->newCore($width, $height)); } public function newCore(int $width, int $height): Imagick diff --git a/src/Interfaces/CollectionInterface.php b/src/Interfaces/CollectionInterface.php index fc87d626..9cfcbae3 100644 --- a/src/Interfaces/CollectionInterface.php +++ b/src/Interfaces/CollectionInterface.php @@ -2,7 +2,9 @@ namespace Intervention\Image\Interfaces; -interface CollectionInterface +use Traversable; + +interface CollectionInterface extends Traversable { public function push($item): CollectionInterface; public function get(int $key, $default = null); diff --git a/src/Interfaces/ImageInterface.php b/src/Interfaces/ImageInterface.php index 5f16c4d7..f69a9825 100644 --- a/src/Interfaces/ImageInterface.php +++ b/src/Interfaces/ImageInterface.php @@ -2,12 +2,12 @@ namespace Intervention\Image\Interfaces; +use Countable; use Intervention\Image\EncodedImage; +use Traversable; -interface ImageInterface +interface ImageInterface extends Traversable, Countable { - public function getIterator(): CollectionInterface; - public function getFrames(): CollectionInterface; public function getFrame(int $key = 0): ?FrameInterface; public function addFrame(FrameInterface $frame): ImageInterface; public function setLoops(int $count): ImageInterface; diff --git a/tests/Drivers/Abstract/AbstractImageTest.php b/tests/Drivers/Abstract/AbstractImageTest.php index 52581f21..dde2be23 100644 --- a/tests/Drivers/Abstract/AbstractImageTest.php +++ b/tests/Drivers/Abstract/AbstractImageTest.php @@ -30,58 +30,17 @@ class AbstractImageTest extends TestCase $collection = new Collection([$frame1, $frame2, $frame3]); - $mock = Mockery::mock(AbstractImage::class, ImageInterface::class, [$collection]) + $mock = Mockery::mock(AbstractImage::class, ImageInterface::class) ->shouldAllowMockingProtectedMethods() ->makePartial(); $mock->shouldReceive('getWidth')->andReturn(300); $mock->shouldReceive('getHeight')->andReturn(200); + $mock->shouldReceive('getIterator')->andReturn($collection); return $mock; } - public function testGetIterator(): void - { - $this->assertInstanceOf(Collection::class, $this->abstractImageMock()->getIterator()); - } - - public function testGetFrames(): void - { - $this->assertInstanceOf(Collection::class, $this->abstractImageMock()->getFrames()); - } - - public function testGetFrame(): void - { - $img = $this->abstractImageMock(); - - $this->assertInstanceOf(FrameInterface::class, $img->getFrame()); - $this->assertEquals(1, $img->getFrame()->ident()); - - $this->assertInstanceOf(FrameInterface::class, $img->getFrame(1)); - $this->assertEquals(2, $img->getFrame(1)->ident()); - - $this->assertInstanceOf(FrameInterface::class, $img->getFrame(2)); - $this->assertEquals(3, $img->getFrame(2)->ident()); - } - - public function testAddFrame(): void - { - $img = $this->abstractImageMock(); - $this->assertEquals(3, $img->getFrames()->count()); - $result = $img->addFrame(Mockery::mock(FrameInterface::class)); - $this->assertInstanceOf(AbstractImage::class, $result); - $this->assertEquals(4, $img->getFrames()->count()); - } - - public function testSetGetLoops(): void - { - $img = $this->abstractImageMock(); - $this->assertEquals(0, $img->getLoops()); - $result = $img->setLoops(10); - $this->assertEquals(10, $img->getLoops()); - $this->assertInstanceOf(AbstractImage::class, $result); - } - public function testGetSize(): void { $img = $this->abstractImageMock(); @@ -98,19 +57,6 @@ class AbstractImageTest extends TestCase $this->assertEquals(200, $img->size()->getHeight()); } - public function testIsAnimated(): void - { - $img = Mockery::mock(AbstractImage::class, [new Collection()])->makePartial(); - $this->assertFalse($img->isAnimated()); - - $collection = new Collection([ - Mockery::mock(FrameInterface::class), - Mockery::mock(FrameInterface::class), - ]); - $img = Mockery::mock(AbstractImage::class, [$collection])->makePartial(); - $this->assertTrue($img->isAnimated()); - } - public function testModify(): void { $img = $this->abstractImageMock(); diff --git a/tests/Drivers/Gd/ImageTest.php b/tests/Drivers/Gd/ImageTest.php index 82992c24..27ce28d1 100644 --- a/tests/Drivers/Gd/ImageTest.php +++ b/tests/Drivers/Gd/ImageTest.php @@ -37,6 +37,12 @@ class ImageTest extends TestCase $this->assertInstanceOf(Image::class, $this->image); } + public function testCount(): void + { + $this->assertEquals(3, $this->image->count()); + $this->assertEquals(3, count($this->image)); + } + public function testIterator(): void { foreach ($this->image as $frame) { @@ -44,12 +50,6 @@ class ImageTest extends TestCase } } - public function testGetFrames(): void - { - $this->assertInstanceOf(Collection::class, $this->image->getFrames()); - $this->assertCount(3, $this->image->getFrames()); - } - public function testGetFrame(): void { $this->assertInstanceOf(Frame::class, $this->image->getFrame()); @@ -58,10 +58,10 @@ class ImageTest extends TestCase public function testAddFrame(): void { - $this->assertCount(3, $this->image->getFrames()); + $this->assertCount(3, $this->image); $result = $this->image->addFrame(new Frame(imagecreatetruecolor(3, 2))); $this->assertInstanceOf(Image::class, $result); - $this->assertCount(4, $this->image->getFrames()); + $this->assertCount(4, $this->image); } public function testSetGetLoops(): void diff --git a/tests/Drivers/Imagick/Encoders/GifEncoderTest.php b/tests/Drivers/Imagick/Encoders/GifEncoderTest.php index f0624cf6..e12b631c 100644 --- a/tests/Drivers/Imagick/Encoders/GifEncoderTest.php +++ b/tests/Drivers/Imagick/Encoders/GifEncoderTest.php @@ -20,20 +20,24 @@ class GifEncoderTest extends TestCase { protected function getTestImage(): Image { - $imagick1 = new Imagick(); - $imagick1->newImage(30, 20, new ImagickPixel('red'), 'png'); - $frame1 = new Frame($imagick1); - $frame1->setDelay(50); - $imagick2 = new Imagick(); - $imagick2->newImage(30, 20, new ImagickPixel('green'), 'png'); - $frame2 = new Frame($imagick2); - $frame2->setDelay(50); - $imagick3 = new Imagick(); - $imagick3->newImage(30, 20, new ImagickPixel('blue'), 'png'); - $frame3 = new Frame($imagick3); - $frame3->setDelay(50); + $imagick = new Imagick(); - return new Image(new Collection([$frame1, $frame2, $frame3])); + $frame = new Imagick(); + $frame->newImage(30, 20, new ImagickPixel('red'), 'png'); + $frame->setImageDelay(50); + $imagick->addImage($frame); + + $frame = new Imagick(); + $frame->newImage(30, 20, new ImagickPixel('green'), 'png'); + $frame->setImageDelay(50); + $imagick->addImage($frame); + + $frame = new Imagick(); + $frame->newImage(30, 20, new ImagickPixel('blue'), 'png'); + $frame->setImageDelay(50); + $imagick->addImage($frame); + + return new Image($imagick); } public function testEncode(): void diff --git a/tests/Drivers/Imagick/Encoders/JpegEncoderTest.php b/tests/Drivers/Imagick/Encoders/JpegEncoderTest.php index 76ea3696..c5e7b702 100644 --- a/tests/Drivers/Imagick/Encoders/JpegEncoderTest.php +++ b/tests/Drivers/Imagick/Encoders/JpegEncoderTest.php @@ -4,9 +4,7 @@ namespace Intervention\Image\Tests\Drivers\Imagick\Encoders; use Imagick; use ImagickPixel; -use Intervention\Image\Collection; use Intervention\Image\Drivers\Imagick\Encoders\JpegEncoder; -use Intervention\Image\Drivers\Imagick\Frame; use Intervention\Image\Drivers\Imagick\Image; use Intervention\Image\Tests\TestCase; use Intervention\MimeSniffer\MimeSniffer; @@ -22,9 +20,8 @@ class JpegEncoderTest extends TestCase { $imagick = new Imagick(); $imagick->newImage(3, 2, new ImagickPixel('red'), 'png'); - $frame = new Frame($imagick); - return new Image(new Collection([$frame])); + return new Image($imagick); } public function testEncode(): void diff --git a/tests/Drivers/Imagick/Encoders/PngEncoderTest.php b/tests/Drivers/Imagick/Encoders/PngEncoderTest.php index 30b41fa8..92cf1fc6 100644 --- a/tests/Drivers/Imagick/Encoders/PngEncoderTest.php +++ b/tests/Drivers/Imagick/Encoders/PngEncoderTest.php @@ -6,9 +6,7 @@ namespace Intervention\Image\Tests\Drivers\Imagick\Encoders; use Imagick; use ImagickPixel; -use Intervention\Image\Collection; use Intervention\Image\Drivers\Imagick\Encoders\PngEncoder; -use Intervention\Image\Drivers\Imagick\Frame; use Intervention\Image\Drivers\Imagick\Image; use Intervention\MimeSniffer\MimeSniffer; use Intervention\MimeSniffer\Types\ImagePng; @@ -23,9 +21,8 @@ final class PngEncoderTest extends TestCase { $imagick = new Imagick(); $imagick->newImage(3, 2, new ImagickPixel('red'), 'jpg'); - $frame = new Frame($imagick); - return new Image(new Collection([$frame])); + return new Image($imagick); } public function testEncode(): void diff --git a/tests/Drivers/Imagick/Encoders/WebpEncoderTest.php b/tests/Drivers/Imagick/Encoders/WebpEncoderTest.php index dcced39b..69e9df2f 100644 --- a/tests/Drivers/Imagick/Encoders/WebpEncoderTest.php +++ b/tests/Drivers/Imagick/Encoders/WebpEncoderTest.php @@ -6,9 +6,7 @@ namespace Intervention\Image\Tests\Drivers\Imagick\Encoders; use Imagick; use ImagickPixel; -use Intervention\Image\Collection; use Intervention\Image\Drivers\Imagick\Encoders\WebpEncoder; -use Intervention\Image\Drivers\Imagick\Frame; use Intervention\Image\Drivers\Imagick\Image; use Intervention\MimeSniffer\MimeSniffer; use Intervention\MimeSniffer\Types\ImageWebp; @@ -23,9 +21,8 @@ final class WebpEncoderTest extends TestCase { $imagick = new Imagick(); $imagick->newImage(3, 2, new ImagickPixel('red'), 'png'); - $frame = new Frame($imagick); - return new Image(new Collection([$frame])); + return new Image($imagick); } public function testEncode(): void diff --git a/tests/Drivers/Imagick/ImageTest.php b/tests/Drivers/Imagick/ImageTest.php index 6337c1a7..53e38f2c 100644 --- a/tests/Drivers/Imagick/ImageTest.php +++ b/tests/Drivers/Imagick/ImageTest.php @@ -4,7 +4,6 @@ namespace Intervention\Image\Tests\Drivers\Imagick; use Imagick; use ImagickPixel; -use Intervention\Image\Collection; use Intervention\Image\Drivers\Imagick\Frame; use Intervention\Image\Drivers\Imagick\Image; use Intervention\Image\Geometry\Size; @@ -20,16 +19,46 @@ class ImageTest extends TestCase protected function setUp(): void { + // create base image $imagick = new Imagick(); - $imagick->newImage(3, 2, new ImagickPixel('red'), 'png'); - $this->image = new Image(new Collection([new Frame($imagick)])); + // add frame + $frame = new Imagick(); + $frame->newImage(3, 2, new ImagickPixel('red'), 'png'); + $imagick->addImage($frame); + + // add frame + $frame = new Imagick(); + $frame->newImage(3, 2, new ImagickPixel('green'), 'png'); + $imagick->addImage($frame); + + // create intervention image + $this->image = new Image($imagick); } public function testConstructor(): void { $this->assertInstanceOf(Image::class, $this->image); } + + public function testGetFrame(): void + { + $this->assertInstanceOf(Frame::class, $this->image->getFrame()); + $this->assertInstanceOf(Frame::class, $this->image->getFrame(1)); + $this->assertNull($this->image->getFrame(2)); + } + + public function testAddFrame(): void + { + $frame = new Imagick(); + $frame->newImage(3, 2, new ImagickPixel('blue'), 'png'); + $frame = new Frame($frame); + + $this->assertCount(2, $this->image); + $result = $this->image->addFrame($frame); + $this->assertInstanceOf(Image::class, $result); + $this->assertCount(3, $this->image); + } public function testIterator(): void { @@ -38,6 +67,12 @@ class ImageTest extends TestCase } } + public function testCount(): void + { + $this->assertEquals(2, $this->image->count()); + $this->assertEquals(2, count($this->image)); + } + public function testWidth(): void { $this->assertEquals(3, $this->image->getWidth()); From bf8e3a1fc739064b86fdcc0787c96df4e4fc7650 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Tue, 21 Jun 2022 09:59:29 +0200 Subject: [PATCH 177/476] Optimize GifEncoder of imagick driver Imagick object is accessed directly instead of frame by frame. --- src/Drivers/Imagick/Encoders/GifEncoder.php | 19 ++++++---------- src/Drivers/Imagick/Image.php | 24 ++++++++++----------- 2 files changed, 19 insertions(+), 24 deletions(-) diff --git a/src/Drivers/Imagick/Encoders/GifEncoder.php b/src/Drivers/Imagick/Encoders/GifEncoder.php index 7524380b..f7192274 100644 --- a/src/Drivers/Imagick/Encoders/GifEncoder.php +++ b/src/Drivers/Imagick/Encoders/GifEncoder.php @@ -15,18 +15,13 @@ class GifEncoder extends AbstractEncoder implements EncoderInterface $format = 'gif'; $compression = Imagick::COMPRESSION_LZW; - $gif = new Imagick() ; - foreach ($image as $frame) { - $gif->addImage($frame->getCore()); - } + $imagick = $image->getImagick(); + $imagick->setFormat($format); + $imagick->setImageFormat($format); + $imagick->setCompression($compression); + $imagick->setImageCompression($compression); + $imagick = $imagick->deconstructImages(); - $gif->setImageIterations($image->getLoops()); - $gif->setFormat($format); - $gif->setImageFormat($format); - $gif->setCompression($compression); - $gif->setImageCompression($compression); - $gif = $gif->deconstructImages(); - - return new EncodedImage($gif->getImagesBlob(), 'image/gif'); + return new EncodedImage($imagick->getImagesBlob(), 'image/gif'); } } diff --git a/src/Drivers/Imagick/Image.php b/src/Drivers/Imagick/Image.php index e41886c9..906322af 100644 --- a/src/Drivers/Imagick/Image.php +++ b/src/Drivers/Imagick/Image.php @@ -14,25 +14,25 @@ class Image extends AbstractImage implements ImageInterface, Iterator { protected $iteratorIndex = 0; - public function __construct(protected Imagick $core) + public function __construct(protected Imagick $imagick) { // } - public function getCore(): Imagick + public function getImagick(): Imagick { - return $this->core; + return $this->imagick; } public function getFrame(int $key = 0): ?FrameInterface { try { - $this->core->setIteratorIndex($key); + $this->imagick->setIteratorIndex($key); } catch (ImagickException $e) { return null; } - return new Frame($this->core->current()); + return new Frame($this->imagick->current()); } public function addFrame(FrameInterface $frame): ImageInterface @@ -50,21 +50,21 @@ class Image extends AbstractImage implements ImageInterface, Iterator $frame->getOffsetTop() ); - $this->core->addImage($imagick); + $this->imagick->addImage($imagick); return $this; } public function setLoops(int $count): ImageInterface { - $this->core->setImageIterations($count); + $this->imagick->setImageIterations($count); return $this; } public function getLoops(): int { - return $this->core->getImageIterations(); + return $this->imagick->getImageIterations(); } public function isAnimated(): bool @@ -74,14 +74,14 @@ class Image extends AbstractImage implements ImageInterface, Iterator public function count(): int { - return $this->core->getNumberImages(); + return $this->imagick->getNumberImages(); } public function current() { - $this->core->setIteratorIndex($this->iteratorIndex); + $this->imagick->setIteratorIndex($this->iteratorIndex); - return new Frame($this->core->current()); + return new Frame($this->imagick->current()); } public function key() @@ -102,7 +102,7 @@ class Image extends AbstractImage implements ImageInterface, Iterator public function valid(): bool { try { - $result = $this->core->setIteratorIndex($this->iteratorIndex); + $result = $this->imagick->setIteratorIndex($this->iteratorIndex); } catch (ImagickException $e) { return false; } From 871a1958b3084be9f4d58db5be318b8cd6aa5f7a Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Tue, 21 Jun 2022 20:09:37 +0200 Subject: [PATCH 178/476] Change signature of AbstractFont & FontInterface --- src/Drivers/Abstract/AbstractFont.php | 14 +++++++------- src/Interfaces/FontInterface.php | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Drivers/Abstract/AbstractFont.php b/src/Drivers/Abstract/AbstractFont.php index 6fee3a60..8991cd90 100644 --- a/src/Drivers/Abstract/AbstractFont.php +++ b/src/Drivers/Abstract/AbstractFont.php @@ -29,7 +29,7 @@ abstract class AbstractFont implements FontInterface return $this->text; } - public function size(float $size): self + public function size(float $size): FontInterface { $this->size = $size; @@ -41,7 +41,7 @@ abstract class AbstractFont implements FontInterface return $this->size; } - public function angle(float $angle): self + public function angle(float $angle): FontInterface { $this->angle = $angle; @@ -53,14 +53,14 @@ abstract class AbstractFont implements FontInterface return $this->angle; } - public function filename(string $filename): self + public function filename(string $filename): FontInterface { $this->filename = $filename; return $this; } - public function getFilename(): string + public function getFilename(): ?string { return $this->filename; } @@ -70,7 +70,7 @@ abstract class AbstractFont implements FontInterface return is_file($this->filename); } - public function color($color): self + public function color($color): FontInterface { $this->color = $color; @@ -82,7 +82,7 @@ abstract class AbstractFont implements FontInterface return $this->handleInput($this->color); } - public function align(string $align): self + public function align(string $align): FontInterface { $this->align = $align; @@ -94,7 +94,7 @@ abstract class AbstractFont implements FontInterface return $this->valign; } - public function valign(string $valign): self + public function valign(string $valign): FontInterface { $this->valign = $valign; diff --git a/src/Interfaces/FontInterface.php b/src/Interfaces/FontInterface.php index c9f68d2d..26d42fc7 100644 --- a/src/Interfaces/FontInterface.php +++ b/src/Interfaces/FontInterface.php @@ -15,7 +15,7 @@ interface FontInterface public function angle(float $angle): self; public function getAngle(): float; public function filename(string $filename): self; - public function getFilename(): string; + public function getFilename(): ?string; public function hasFilename(): bool; public function align(string $align): self; public function getAlign(): string; From afec510ad0fcf5a34e757c5129d55b8279767292 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Tue, 21 Jun 2022 20:09:54 +0200 Subject: [PATCH 179/476] Add test for AbstractFont --- tests/Drivers/Abstract/AbstractFontTest.php | 46 +++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 tests/Drivers/Abstract/AbstractFontTest.php diff --git a/tests/Drivers/Abstract/AbstractFontTest.php b/tests/Drivers/Abstract/AbstractFontTest.php new file mode 100644 index 00000000..e39d345d --- /dev/null +++ b/tests/Drivers/Abstract/AbstractFontTest.php @@ -0,0 +1,46 @@ +shouldAllowMockingProtectedMethods() + ->makePartial(); + + // settings + $mock->size(24); + $mock->angle(30); + $mock->filename(__DIR__ . '/AbstractFontTest.php'); + $mock->color('ccc'); + $mock->align('center'); + $mock->valign('top'); + + $mock->shouldReceive('handleInput')->andReturn( + Mockery::mock(ColorInterface::class) + ); + + return $mock; + } + + public function testConstructor(): void + { + $mock = $this->getAbstractFontMock(); + $this->assertEquals('test123', $mock->getText()); + $this->assertEquals(24.0, $mock->getSize()); + $this->assertEquals(30, $mock->getAngle()); + $this->assertEquals(__DIR__ . '/AbstractFontTest.php', $mock->getFilename()); + $this->assertInstanceOf(ColorInterface::class, $mock->getColor()); + $this->assertEquals('center', $mock->getAlign()); + $this->assertEquals('top', $mock->getValign()); + $this->assertTrue($mock->hasFilename()); + } +} From 7abf23edbb93d023681ca535f595fca81f0c1f53 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Thu, 23 Jun 2022 12:04:04 +0200 Subject: [PATCH 180/476] Implement FontWriter Modifier --- src/Drivers/Gd/Font.php | 22 +- src/Drivers/Gd/Modifiers/TextWriter.php | 35 +- src/Drivers/Imagick/Font.php | 18 +- src/Drivers/Imagick/Modifiers/TextWriter.php | 10 +- src/Geometry/Point.php | 13 +- src/Geometry/Polygon.php | 455 +++++++++++++++++++ src/Geometry/Size.php | 24 + src/Interfaces/FontInterface.php | 4 +- tests/Geometry/PolygonTest.php | 397 ++++++++++++++++ tests/Geometry/SizeTest.php | 18 +- 10 files changed, 942 insertions(+), 54 deletions(-) create mode 100644 src/Geometry/Polygon.php create mode 100644 tests/Geometry/PolygonTest.php diff --git a/src/Drivers/Gd/Font.php b/src/Drivers/Gd/Font.php index 90a15b0c..3ac9c1c7 100644 --- a/src/Drivers/Gd/Font.php +++ b/src/Drivers/Gd/Font.php @@ -3,6 +3,8 @@ namespace Intervention\Image\Drivers\Gd; use Intervention\Image\Drivers\Abstract\AbstractFont; +use Intervention\Image\Geometry\Point; +use Intervention\Image\Geometry\Polygon; use Intervention\Image\Geometry\Size; class Font extends AbstractFont @@ -15,9 +17,9 @@ class Font extends AbstractFont /** * Calculate size of bounding box of current text * - * @return Size + * @return Polygon */ - public function getBoxSize(): Size + public function getBoxSize(): Polygon { if (!$this->hasFilename()) { // calculate box size from gd font @@ -27,18 +29,26 @@ class Font extends AbstractFont $box->setWidth($chars * $this->getGdFontWidth()); $box->setHeight($this->getGdFontHeight()); } - return $box; + return $box->toPolygon(); } - // calculate box size from font file + // calculate box size from font file with angle 0 $box = imageftbbox( $this->getSize(), - $this->getAngle(), + 0, $this->getFilename(), $this->getText() ); - return new Size(abs($box[0] - $box[2]), abs($box[1] - $box[7])); + // build polygon from points + $polygon = new Polygon(); + $polygon->addPoint(new Point($box[6], $box[7])); + $polygon->addPoint(new Point($box[4], $box[5])); + $polygon->addPoint(new Point($box[2], $box[3])); + $polygon->addPoint(new Point($box[0], $box[1])); + + + return $polygon; } public function getGdFont(): int diff --git a/src/Drivers/Gd/Modifiers/TextWriter.php b/src/Drivers/Gd/Modifiers/TextWriter.php index 6c6277c3..3a6b18a6 100644 --- a/src/Drivers/Gd/Modifiers/TextWriter.php +++ b/src/Drivers/Gd/Modifiers/TextWriter.php @@ -19,13 +19,12 @@ class TextWriter implements ModifierInterface public function apply(ImageInterface $image): ImageInterface { $position = $this->getAlignedPosition(); - foreach ($image as $frame) { if ($this->font->hasFilename()) { imagettftext( $frame->getCore(), $this->font->getSize(), - $this->font->getAngle(), + $this->font->getAngle() * (-1), $position->getX(), $position->getY(), $this->font->getColor()->toInt(), @@ -47,34 +46,18 @@ class TextWriter implements ModifierInterface return $image; } - protected function getAlignedPosition(): Point + public function getAlignedPosition(): Point { - $position = $this->position; - $box = $this->font->getBoxSize(); + $poly = $this->font->getBoxSize(); + $poly->setPivotPoint($this->position); - // adjust x pos - switch ($this->font->getAlign()) { - case 'center': - $position->setX($position->getX() - round($box->getWidth() / 2)); - break; + $poly->align($this->font->getAlign()); + $poly->valign($this->font->getValign()); - case 'right': - $position->setX($position->getX() - $box->getWidth()); - break; + if ($this->font->getAngle() != 0) { + $poly->rotate($this->font->getAngle()); } - // adjust y pos - switch ($this->font->getValign()) { - case 'top': - $position->setY($position->getY() + $box->getHeight()); - break; - - case 'middle': - case 'center': - $position->setY($position->getY() + round($box->getHeight() / 2)); - break; - } - - return $position; + return $poly->last(); } } diff --git a/src/Drivers/Imagick/Font.php b/src/Drivers/Imagick/Font.php index df32d284..93f21943 100644 --- a/src/Drivers/Imagick/Font.php +++ b/src/Drivers/Imagick/Font.php @@ -6,6 +6,7 @@ use Imagick; use ImagickDraw; use Intervention\Image\Drivers\Abstract\AbstractFont; use Intervention\Image\Exceptions\FontException; +use Intervention\Image\Geometry\Polygon; use Intervention\Image\Geometry\Size; class Font extends AbstractFont @@ -13,7 +14,7 @@ class Font extends AbstractFont public function toImagickDraw(): ImagickDraw { if (!$this->hasFilename()) { - throw new FontException('No font file.'); + throw new FontException('No font file specified.'); } $draw = new ImagickDraw(); @@ -50,14 +51,13 @@ class Font extends AbstractFont /** * Calculate box size of current font * - * @return Size + * @return Polygon */ - public function getBoxSize(): Size + public function getBoxSize(): Polygon { - $foo = null; // no text - no box size if (mb_strlen($this->getText()) === 0) { - return new Size(0, 0); + return (new Size(0, 0))->toPolygon(); } $dimensions = (new Imagick())->queryFontMetrics( @@ -65,9 +65,9 @@ class Font extends AbstractFont $this->getText() ); - return new Size( - intval(abs($dimensions['textWidth'])), - intval(abs($dimensions['textHeight'])) - ); + return (new Size( + intval(round(abs($dimensions['boundingBox']['x1'] - $dimensions['boundingBox']['x2']))), + intval(round(abs($dimensions['boundingBox']['y1'] - $dimensions['boundingBox']['y2']))), + ))->toPolygon(); } } diff --git a/src/Drivers/Imagick/Modifiers/TextWriter.php b/src/Drivers/Imagick/Modifiers/TextWriter.php index 52c6b7be..4fe1dc2a 100644 --- a/src/Drivers/Imagick/Modifiers/TextWriter.php +++ b/src/Drivers/Imagick/Modifiers/TextWriter.php @@ -18,15 +18,13 @@ class TextWriter implements ModifierInterface public function apply(ImageInterface $image): ImageInterface { - $draw = $this->font->toImagickDraw(); $position = $this->getAlignedPosition(); - foreach ($image as $frame) { $frame->getCore()->annotateImage( - $draw, + $this->font->toImagickDraw(), $position->getX(), $position->getY(), - $this->font->getAngle(), + $this->font->getAngle() * (-1), $this->font->getText() ); } @@ -42,12 +40,12 @@ class TextWriter implements ModifierInterface // adjust y pos switch ($this->font->getValign()) { case 'top': - $position->setY($position->getY() + $box->getHeight()); + $position->setY($position->getY() + $box->height()); break; case 'middle': case 'center': - $position->setY($position->getY() + round($box->getHeight() / 2)); + $position->setY(intval($position->getY() + round($box->height() / 2))); break; } diff --git a/src/Geometry/Point.php b/src/Geometry/Point.php index 44f3c976..a83ba007 100644 --- a/src/Geometry/Point.php +++ b/src/Geometry/Point.php @@ -58,7 +58,7 @@ class Point implements PointInterface /** * Move X coordinate * - * @param integer $x + * @param integer $value */ public function moveX(int $value): self { @@ -70,7 +70,7 @@ class Point implements PointInterface /** * Move Y coordinate * - * @param integer $y + * @param integer $value */ public function moveY(int $value): self { @@ -79,6 +79,11 @@ class Point implements PointInterface return $this; } + public function move(int $x, int $y): self + { + return $this->moveX($x)->moveY($y); + } + /** * Sets both X and Y coordinate * @@ -107,8 +112,8 @@ class Point implements PointInterface $cos = round(cos(deg2rad($angle)), 6); return $this->setPosition( - $cos * ($this->x - $pivot->x) - $sin * ($this->y - $pivot->y) + $pivot->x, - $sin * ($this->x - $pivot->x) + $cos * ($this->y - $pivot->y) + $pivot->y + intval($cos * ($this->x - $pivot->x) - $sin * ($this->y - $pivot->y) + $pivot->x), + intval($sin * ($this->x - $pivot->x) + $cos * ($this->y - $pivot->y) + $pivot->y) ); } } diff --git a/src/Geometry/Polygon.php b/src/Geometry/Polygon.php new file mode 100644 index 00000000..f7d1eef3 --- /dev/null +++ b/src/Geometry/Polygon.php @@ -0,0 +1,455 @@ +pivot = $pivot ? $pivot : new Point(); + } + + /** + * Return current pivot point + * + * @return Point + */ + public function getPivotPoint(): Point + { + return $this->pivot; + } + + /** + * Change pivot point to given point + * + * @param Point $pivot + * @return Polygon + */ + public function setPivotPoint(Point $pivot): self + { + $this->pivot = $pivot; + + return $this; + } + + /** + * Return first point of polygon + * + * @return ?Point + */ + public function first(): ?Point + { + if ($point = reset($this->points)) { + return $point; + } + + return null; + } + + /** + * Return last point of polygon + * + * @return ?Point + */ + public function last(): ?Point + { + if ($point = end($this->points)) { + return $point; + } + + return null; + } + + /** + * Return polygon's point count + * + * @return int + */ + public function count(): int + { + return count($this->points); + } + + /** + * Determine if point exists at given offset + * + * @param mixed $offset + * @return bool + */ + public function offsetExists($offset): bool + { + return array_key_exists($offset, $this->points); + } + + /** + * Return point at given offset + * + * @param mixed $offset + * @return Point + */ + public function offsetGet($offset) + { + return $this->points[$offset]; + } + + /** + * Set point at given offset + * + * @param mixed $offset + * @param Point $value + * @return void + */ + public function offsetSet($offset, $value): void + { + $this->points[$offset] = $value; + } + + /** + * Unset offset at given offset + * + * @param mixed $offset + * @return void + */ + public function offsetUnset($offset): void + { + unset($this->points[$offset]); + } + + /** + * Add given point to polygon + * + * @param Point $point + * @return Polygon + */ + public function addPoint(Point $point): self + { + $this->points[] = $point; + + return $this; + } + + /** + * Calculate total horizontal span of polygon + * + * @return int + */ + public function width(): int + { + return abs($this->getMostLeftPoint()->getX() - $this->getMostRightPoint()->getX()); + } + + /** + * Calculate total vertical span of polygon + * + * @return int + */ + public function height(): int + { + return abs($this->getMostBottomPoint()->getY() - $this->getMostTopPoint()->getY()); + } + + /** + * Return most left point of all points in polygon + * + * @return Point + */ + public function getMostLeftPoint(): Point + { + $points = []; + foreach ($this->points as $point) { + $points[] = $point; + } + + usort($points, function ($a, $b) { + if ($a->getX() === $b->getX()) { + return 0; + } + return ($a->getX() < $b->getX()) ? -1 : 1; + }); + + return $points[0]; + } + + /** + * Return most right point in polygon + * + * @return Point + */ + public function getMostRightPoint(): Point + { + $points = []; + foreach ($this->points as $point) { + $points[] = $point; + } + + usort($points, function ($a, $b) { + if ($a->getX() === $b->getX()) { + return 0; + } + return ($a->getX() > $b->getX()) ? -1 : 1; + }); + + return $points[0]; + } + + /** + * Return most top point in polygon + * + * @return Point + */ + public function getMostTopPoint(): Point + { + $points = []; + foreach ($this->points as $point) { + $points[] = $point; + } + + usort($points, function ($a, $b) { + if ($a->getY() === $b->getY()) { + return 0; + } + return ($a->getY() > $b->getY()) ? -1 : 1; + }); + + return $points[0]; + } + + /** + * Return most bottom point in polygon + * + * @return Point + */ + public function getMostBottomPoint(): Point + { + $points = []; + foreach ($this->points as $point) { + $points[] = $point; + } + + usort($points, function ($a, $b) { + if ($a->getY() === $b->getY()) { + return 0; + } + return ($a->getY() < $b->getY()) ? -1 : 1; + }); + + return $points[0]; + } + + /** + * Create and return point in absolute center of the polygon + * + * @return Point + */ + public function getCenterPoint(): Point + { + return new Point( + $this->getMostRightPoint()->getX() - (intval(round($this->width() / 2))), + $this->getMostTopPoint()->getY() - (intval(round($this->height() / 2))) + ); + } + + /** + * Align pivot point to given horizontal position + * + * @param string $position + * @return Polygon + */ + public function alignPivot(string $position): self + { + switch (strtolower($position)) { + case 'center': + $this->pivot->setX( + intval(($this->getMostRightPoint()->getX() + $this->getMostLeftPoint()->getX()) / 2) + ); + break; + + case 'right': + $this->pivot->setX( + $this->getMostRightPoint()->getX() + ); + break; + + case 'left': + $this->pivot->setX( + $this->getMostLeftPoint()->getX() + ); + break; + } + + return $this; + } + + /** + * Align pivot point to given vertical position + * + * @param string $position + * @return Polygon + */ + public function valignPivot(string $position): self + { + switch (strtolower($position)) { + case 'center': + case 'middle': + $this->pivot->setY( + intval(($this->getMostTopPoint()->getY() + $this->getMostBottomPoint()->getY()) / 2) + ); + break; + + case 'top': + $this->pivot->setY( + $this->getMostTopPoint()->getY() + ); + break; + + case 'bottom': + $this->pivot->setY( + $this->getMostBottomPoint()->getY() + ); + break; + } + + return $this; + } + + /** + * Align all points of polygon horizontally to given position around pivot point + * + * @param string $position + * @return Polygon + */ + public function align(string $position): self + { + switch (strtolower($position)) { + case 'center': + case 'middle': + $diff = ($this->getCenterPoint()->getX() - $this->pivot->getX()); + break; + + case 'right': + $diff = ($this->getMostRightPoint()->getX() - $this->pivot->getX()); + break; + + default: + case 'left': + $diff = ($this->getMostLeftPoint()->getX() - $this->pivot->getX()); + break; + } + + foreach ($this->points as $point) { + $point->setX($point->getX() - $diff); + } + + return $this; + } + + /** + * Align all points of polygon vertically to given position around pivot point + * + * @param string $position + * @return Polygon + */ + public function valign(string $position): self + { + switch (strtolower($position)) { + case 'center': + case 'middle': + $diff = ($this->getCenterPoint()->getY() - $this->pivot->getY()); + break; + + case 'top': + $diff = ($this->getMostTopPoint()->getY() - $this->pivot->getY()) - $this->height(); + break; + + default: + case 'bottom': + $diff = ($this->getMostBottomPoint()->getY() - $this->pivot->getY()) + $this->height(); + break; + } + + foreach ($this->points as $point) { + $point->setY($point->getY() - $diff); + } + + return $this; + } + + /** + * Rotate points of polygon around pivot point with given angle + * + * @param float $angle + * @return Polygon + */ + public function rotate(float $angle): self + { + $sin = sin(deg2rad($angle)); + $cos = cos(deg2rad($angle)); + + foreach ($this->points as $point) { + // translate point to pivot + $point->setX($point->getX() - $this->pivot->getX()); + $point->setY($point->getY() - $this->pivot->getY()); + + // rotate point + $x = $point->getX() * $cos - $point->getY() * $sin; + $y = $point->getX() * $sin + $point->getY() * $cos; + + // translate point back + $point->setX($x + $this->pivot->getX()); + $point->setY($y + $this->pivot->getY()); + } + + return $this; + } + + /** + * Move all points by given amount on the x-axis + * + * @param int $amount + * @return Polygon + */ + public function movePointsX(int $amount): self + { + foreach ($this->points as $point) { + $point->moveX($amount); + } + + return $this; + } + + /** + * Move all points by given amount on the y-axis + * + * @param int $amount + * @return Polygon + */ + public function movePointsY(int $amount): self + { + foreach ($this->points as $point) { + $point->moveY($amount); + } + + return $this; + } + + /** + * Return array of all x/y values of all points of polygon + * + * @return array + */ + public function toArray(): array + { + $coordinates = []; + foreach ($this->points as $point) { + $coordinates[] = $point->getX(); + $coordinates[] = $point->getY(); + } + + return $coordinates; + } +} diff --git a/src/Geometry/Size.php b/src/Geometry/Size.php index 04886285..532940b5 100644 --- a/src/Geometry/Size.php +++ b/src/Geometry/Size.php @@ -209,6 +209,30 @@ class Size implements SizeInterface return new Point($x, $y); } + public function toPolygon(): Polygon + { + $polygon = new Polygon([ + $this->pivot // top/left + ], $this->pivot); + + // top/right + $polygon->addPoint( + new Point($this->pivot->getX() + $this->getWidth(), $this->pivot->getY()) + ); + + // bottom/right + $polygon->addPoint( + new Point($this->pivot->getX() + $this->getWidth(), $this->pivot->getY() - $this->getHeight()) + ); + + // bottom/left + $polygon->addPoint( + new Point($this->pivot->getX(), $this->pivot->getY() - $this->getHeight()) + ); + + return $polygon; + } + protected function getResizer(?int $width = null, ?int $height = null): Resizer { return new Resizer($width, $height); diff --git a/src/Interfaces/FontInterface.php b/src/Interfaces/FontInterface.php index 26d42fc7..92e47008 100644 --- a/src/Interfaces/FontInterface.php +++ b/src/Interfaces/FontInterface.php @@ -2,7 +2,7 @@ namespace Intervention\Image\Interfaces; -use Intervention\Image\Geometry\Size; +use Intervention\Image\Geometry\Polygon; use Intervention\Image\Interfaces\ColorInterface; interface FontInterface @@ -19,5 +19,5 @@ interface FontInterface public function hasFilename(): bool; public function align(string $align): self; public function getAlign(): string; - public function getBoxSize(): Size; + public function getBoxSize(): Polygon; } diff --git a/tests/Geometry/PolygonTest.php b/tests/Geometry/PolygonTest.php new file mode 100644 index 00000000..3ed904de --- /dev/null +++ b/tests/Geometry/PolygonTest.php @@ -0,0 +1,397 @@ +assertInstanceOf(Polygon::class, $poly); + $this->assertEquals(0, $poly->count()); + } + + public function testCount(): void + { + $poly = new Polygon([new Point(), new Point()]); + $this->assertEquals(2, $poly->count()); + } + + public function testArrayAccess(): void + { + $poly = new Polygon([new Point(), new Point()]); + $this->assertInstanceOf(Point::class, $poly[0]); + $this->assertInstanceOf(Point::class, $poly[1]); + } + + public function testAddPoint(): void + { + $poly = new Polygon([new Point(), new Point()]); + $this->assertEquals(2, $poly->count()); + $result = $poly->addPoint(new Point()); + $this->assertEquals(3, $poly->count()); + $this->assertInstanceOf(Polygon::class, $result); + } + + public function testGetCenterPoint(): void + { + $poly = new Polygon([ + new Point(0, 0), + new Point(20, 0), + new Point(20, -20), + new Point(0, -20), + ]); + + $result = $poly->getCenterPoint(); + $this->assertEquals(10, $result->getX()); + $this->assertEquals(-10, $result->getY()); + + $poly = new Polygon([ + new Point(0, 0), + new Point(300, 0), + new Point(300, -200), + new Point(0, -200), + ], new Point(0, 0)); + + $result = $poly->getCenterPoint(); + $this->assertEquals(150, $result->getX()); + $this->assertEquals(-100, $result->getY()); + } + + public function testWidth(): void + { + $poly = new Polygon([ + new Point(12, 45), + new Point(-23, -49), + new Point(3, 566), + ]); + + $this->assertEquals($poly->width(), 35); + } + + public function testHeight(): void + { + $poly = new Polygon([ + new Point(12, 45), + new Point(-23, -49), + new Point(3, 566), + ]); + + $this->assertEquals(615, $poly->height()); + + $poly = new Polygon([ + new Point(250, 207), + new Point(473, 207), + new Point(473, 250), + new Point(250, 250), + ], new Point(250, 250)); + + $this->assertEquals(43, $poly->height()); + } + + public function testFirst(): void + { + $poly = new Polygon([ + new Point(12, 45), + new Point(-23, -49), + new Point(3, 566), + ]); + + $this->assertEquals(12, $poly->first()->getX()); + $this->assertEquals(45, $poly->first()->getY()); + } + + public function testLast(): void + { + $poly = new Polygon([ + new Point(12, 45), + new Point(-23, -49), + new Point(3, 566), + ]); + + $this->assertEquals(3, $poly->last()->getX()); + $this->assertEquals(566, $poly->last()->getY()); + } + + public function testGetPivotPoint(): void + { + $poly = new Polygon(); + $this->assertInstanceOf(Point::class, $poly->getPivotPoint()); + } + + public function testAlignPivot(): void + { + $poly = new Polygon([ + new Point(12, 45), + new Point(-24, -49), + new Point(3, 566), + ]); + + $this->assertEquals(0, $poly->getPivotPoint()->getX()); + $this->assertEquals(0, $poly->getPivotPoint()->getY()); + + $result = $poly->alignPivot('center'); + $this->assertInstanceOf(Polygon::class, $result); + + $this->assertEquals(-6, $result->getPivotPoint()->getX()); + $this->assertEquals(0, $result->getPivotPoint()->getY()); + } + + public function testValignPivot(): void + { + $poly = new Polygon([ + new Point(12, 45), + new Point(-24, -50), + new Point(3, 566), + ]); + + $this->assertEquals(0, $poly->getPivotPoint()->getX()); + $this->assertEquals(0, $poly->getPivotPoint()->getY()); + + $result = $poly->valignPivot('middle'); + $this->assertInstanceOf(Polygon::class, $result); + + $this->assertEquals(0, $result->getPivotPoint()->getX()); + $this->assertEquals(258, $result->getPivotPoint()->getY()); + } + + public function testGetMostLeftPoint(): void + { + $poly = new Polygon([ + new Point(0, 0), + new Point(300, 0), + new Point(300, -200), + new Point(-32, -200), + ], new Point(0, 0)); + + $result = $poly->getMostLeftPoint(); + $this->assertEquals(-32, $result->getX()); + $this->assertEquals(-200, $result->getY()); + } + + public function testGetMostRightPoint(): void + { + $poly = new Polygon([ + new Point(0, 0), + new Point(350, 0), + new Point(300, -200), + new Point(-32, -200), + ], new Point(0, 0)); + + $result = $poly->getMostRightPoint(); + $this->assertEquals(350, $result->getX()); + $this->assertEquals(0, $result->getY()); + } + + public function testGetMostTopPoint(): void + { + $poly = new Polygon([ + new Point(0, 100), + new Point(350, 0), + new Point(300, -200), + new Point(-32, 200), + ], new Point(0, 0)); + + $result = $poly->getMostTopPoint(); + $this->assertEquals(-32, $result->getX()); + $this->assertEquals(200, $result->getY()); + } + + public function testGetMostBottomPoint(): void + { + $poly = new Polygon([ + new Point(0, 100), + new Point(350, 0), + new Point(300, -200), + new Point(-32, 200), + ], new Point(0, 0)); + + $result = $poly->getMostBottomPoint(); + $this->assertEquals(300, $result->getX()); + $this->assertEquals(-200, $result->getY()); + } + + public function testAlignCenter(): void + { + $poly = new Polygon([ + new Point(0, 0), + new Point(300, 0), + new Point(300, -200), + new Point(0, -200), + ], new Point(0, 0)); + + $result = $poly->align('center'); + + $this->assertInstanceOf(Polygon::class, $result); + $this->assertEquals(-150, $result[0]->getX()); + $this->assertEquals(0, $result[0]->getY()); + $this->assertEquals(150, $result[1]->getX()); + $this->assertEquals(0, $result[1]->getY()); + $this->assertEquals(150, $result[2]->getX()); + $this->assertEquals(-200, $result[2]->getY()); + $this->assertEquals(-150, $result[3]->getX()); + $this->assertEquals(-200, $result[3]->getY()); + + $poly = new Polygon([ + new Point(0, 0), + new Point(300, 0), + new Point(300, -200), + new Point(0, -200), + ], new Point(-1000, -1000)); + + $result = $poly->align('center'); + + $this->assertInstanceOf(Polygon::class, $result); + $this->assertEquals(-1150, $result[0]->getX()); + $this->assertEquals(0, $result[0]->getY()); + $this->assertEquals(-850, $result[1]->getX()); + $this->assertEquals(0, $result[1]->getY()); + $this->assertEquals(-850, $result[2]->getX()); + $this->assertEquals(-200, $result[2]->getY()); + $this->assertEquals(-1150, $result[3]->getX()); + $this->assertEquals(-200, $result[3]->getY()); + } + + public function testAlignLeft(): void + { + $poly = new Polygon([ + new Point(0, 0), + new Point(300, 0), + new Point(300, -200), + new Point(0, -200), + ], new Point(100, 100)); + + $result = $poly->align('left'); + + $this->assertInstanceOf(Polygon::class, $result); + $this->assertEquals(100, $result[0]->getX()); + $this->assertEquals(0, $result[0]->getY()); + $this->assertEquals(400, $result[1]->getX()); + $this->assertEquals(0, $result[1]->getY()); + $this->assertEquals(400, $result[2]->getX()); + $this->assertEquals(-200, $result[2]->getY()); + $this->assertEquals(100, $result[3]->getX()); + $this->assertEquals(-200, $result[3]->getY()); + + $poly = new Polygon([ + new Point(0, 0), + new Point(300, 0), + new Point(300, -200), + new Point(0, -200), + ], new Point(-1000, -1000)); + + $result = $poly->align('left'); + + $this->assertInstanceOf(Polygon::class, $result); + $this->assertEquals(-1000, $result[0]->getX()); + $this->assertEquals(0, $result[0]->getY()); + $this->assertEquals(-700, $result[1]->getX()); + $this->assertEquals(0, $result[1]->getY()); + $this->assertEquals(-700, $result[2]->getX()); + $this->assertEquals(-200, $result[2]->getY()); + $this->assertEquals(-1000, $result[3]->getX()); + $this->assertEquals(-200, $result[3]->getY()); + } + + public function testAlignRight(): void + { + $poly = new Polygon([ + new Point(0, 0), + new Point(300, 0), + new Point(300, -200), + new Point(0, -200), + ], new Point(100, 100)); + + $result = $poly->align('right'); + + $this->assertInstanceOf(Polygon::class, $result); + $this->assertEquals(-200, $result[0]->getX()); + $this->assertEquals(0, $result[0]->getY()); + $this->assertEquals(100, $result[1]->getX()); + $this->assertEquals(0, $result[1]->getY()); + $this->assertEquals(100, $result[2]->getX()); + $this->assertEquals(-200, $result[2]->getY()); + $this->assertEquals(-200, $result[3]->getX()); + $this->assertEquals(-200, $result[3]->getY()); + + $poly = new Polygon([ + new Point(0, 0), + new Point(300, 0), + new Point(300, -200), + new Point(0, -200), + ], new Point(-1000, -1000)); + + $result = $poly->align('right'); + + $this->assertInstanceOf(Polygon::class, $result); + $this->assertEquals(-1300, $result[0]->getX()); + $this->assertEquals(0, $result[0]->getY()); + $this->assertEquals(-1000, $result[1]->getX()); + $this->assertEquals(0, $result[1]->getY()); + $this->assertEquals(-1000, $result[2]->getX()); + $this->assertEquals(-200, $result[2]->getY()); + $this->assertEquals(-1300, $result[3]->getX()); + $this->assertEquals(-200, $result[3]->getY()); + } + + public function testValignMiddle(): void + { + $poly = new Polygon([ + new Point(-21, -22), + new Point(91, -135), + new Point(113, -113), + new Point(0, 0), + ], new Point(250, 250)); + + $result = $poly->valign('middle'); + + $this->assertInstanceOf(Polygon::class, $result); + $this->assertEquals(-21, $result[0]->getX()); + $this->assertEquals(296, $result[0]->getY()); + $this->assertEquals(91, $result[1]->getX()); + $this->assertEquals(183, $result[1]->getY()); + $this->assertEquals(113, $result[2]->getX()); + $this->assertEquals(205, $result[2]->getY()); + $this->assertEquals(0, $result[3]->getX()); + $this->assertEquals(318, $result[3]->getY()); + } + + public function testRotate(): void + { + $poly = new Polygon([ + new Point(0, 0), + new Point(50, 0), + new Point(50, -50), + new Point(0, -50), + ]); + + $result = $poly->rotate(45); + $this->assertInstanceOf(Polygon::class, $result); + $this->assertEquals(0, $result[0]->getX()); + $this->assertEquals(0, $result[0]->getY()); + $this->assertEquals(35, $result[1]->getX()); + $this->assertEquals(35, $result[1]->getY()); + $this->assertEquals(70, $result[2]->getX()); + $this->assertEquals(0, $result[2]->getY()); + $this->assertEquals(35, $result[3]->getX()); + $this->assertEquals(-35, $result[3]->getY()); + } + + public function testToArray(): void + { + $poly = new Polygon([ + new Point(0, 0), + new Point(50, 0), + new Point(50, -50), + new Point(0, -50), + ]); + + $this->assertEquals([0, 0, 50, 0, 50, -50, 0, -50], $poly->toArray()); + } +} diff --git a/tests/Geometry/SizeTest.php b/tests/Geometry/SizeTest.php index 68dbe2cd..c11031f8 100644 --- a/tests/Geometry/SizeTest.php +++ b/tests/Geometry/SizeTest.php @@ -2,7 +2,7 @@ namespace Intervention\Image\Tests\Geometry; -use Intervention\Image\Geometry\{Point, Size,}; +use Intervention\Image\Geometry\{Point, Polygon, Size,}; use Intervention\Image\Tests\TestCase; /** @@ -254,6 +254,22 @@ class SizeTest extends TestCase $this->assertEquals(50, $pos->getY()); } + public function testToPolygon(): void + { + $size = new Size(300, 200); + $poly = $size->toPolygon(); + $this->assertInstanceOf(Polygon::class, $poly); + $this->assertCount(4, $poly); + $this->assertEquals(0, $poly[0]->getX()); + $this->assertEquals(0, $poly[0]->getY()); + $this->assertEquals(300, $poly[1]->getX()); + $this->assertEquals(0, $poly[1]->getY()); + $this->assertEquals(300, $poly[2]->getX()); + $this->assertEquals(-200, $poly[2]->getY()); + $this->assertEquals(0, $poly[3]->getX()); + $this->assertEquals(-200, $poly[3]->getY()); + } + public function testResize(): void { $size = new Size(300, 200); From 452b91929cfd809c3ba717025ad1d40e947379ea Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Fri, 24 Jun 2022 18:53:47 +0200 Subject: [PATCH 181/476] Fix code issues reported by phpstan --- src/Drivers/Gd/Font.php | 7 ++++--- src/Drivers/Gd/ImageFactory.php | 3 +-- src/Drivers/Gd/Modifiers/PlaceModifier.php | 4 ++-- src/Drivers/Imagick/Color.php | 6 +++--- src/Drivers/Imagick/Encoders/GifEncoder.php | 6 ++++++ src/Drivers/Imagick/Font.php | 10 +++++++--- src/Drivers/Imagick/Frame.php | 2 +- src/Drivers/Imagick/ImageFactory.php | 2 +- src/Drivers/Imagick/Modifiers/PadModifier.php | 9 +++++++-- src/Drivers/Imagick/Modifiers/PlaceModifier.php | 3 ++- src/Drivers/Imagick/Modifiers/RotateModifier.php | 9 ++++++++- src/Exceptions/EncoderException.php | 8 ++++++++ src/Interfaces/ColorInterface.php | 2 +- src/Interfaces/FactoryInterface.php | 1 + 14 files changed, 52 insertions(+), 20 deletions(-) create mode 100644 src/Exceptions/EncoderException.php diff --git a/src/Drivers/Gd/Font.php b/src/Drivers/Gd/Font.php index 3ac9c1c7..b407b495 100644 --- a/src/Drivers/Gd/Font.php +++ b/src/Drivers/Gd/Font.php @@ -68,9 +68,6 @@ class Font extends AbstractFont protected function getGdFontHeight(): int { switch ($this->getGdFont()) { - case 1: - return 8; - case 2: return 14; @@ -82,6 +79,10 @@ class Font extends AbstractFont case 5: return 16; + + default: + case 1: + return 8; } } } diff --git a/src/Drivers/Gd/ImageFactory.php b/src/Drivers/Gd/ImageFactory.php index 467e0332..7dbc2f4e 100644 --- a/src/Drivers/Gd/ImageFactory.php +++ b/src/Drivers/Gd/ImageFactory.php @@ -2,7 +2,6 @@ namespace Intervention\Image\Drivers\Gd; -use GdImage; use Intervention\Image\Collection; use Intervention\Image\Interfaces\FactoryInterface; use Intervention\Image\Interfaces\ImageInterface; @@ -18,7 +17,7 @@ class ImageFactory implements FactoryInterface ); } - public function newCore(int $width, int $height): GdImage + public function newCore(int $width, int $height) { $core = imagecreatetruecolor($width, $height); $color = imagecolorallocatealpha($core, 0, 0, 0, 127); diff --git a/src/Drivers/Gd/Modifiers/PlaceModifier.php b/src/Drivers/Gd/Modifiers/PlaceModifier.php index 9350170a..b90233c9 100644 --- a/src/Drivers/Gd/Modifiers/PlaceModifier.php +++ b/src/Drivers/Gd/Modifiers/PlaceModifier.php @@ -3,9 +3,9 @@ namespace Intervention\Image\Drivers\Gd\Modifiers; use Intervention\Image\Drivers\Gd\Image; -use Intervention\Image\Geometry\Point; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; +use Intervention\Image\Interfaces\PointInterface; use Intervention\Image\Traits\CanResolveDriverClass; class PlaceModifier implements ModifierInterface @@ -52,7 +52,7 @@ class PlaceModifier implements ModifierInterface return $this->resolveDriverClass('InputHandler')->handle($this->element); } - protected function getPosition(Image $image, Image $watermark): Point + protected function getPosition(ImageInterface $image, ImageInterface $watermark): PointInterface { $image_size = $image->getSize()->alignPivot($this->position, $this->offset_x, $this->offset_y); $watermark_size = $watermark->getSize()->alignPivot($this->position); diff --git a/src/Drivers/Imagick/Color.php b/src/Drivers/Imagick/Color.php index bda5e232..7503c7b1 100644 --- a/src/Drivers/Imagick/Color.php +++ b/src/Drivers/Imagick/Color.php @@ -21,17 +21,17 @@ class Color extends AbstractColor implements ColorInterface public function red(): int { - return round($this->pixel->getColorValue(Imagick::COLOR_RED) * 255); + return intval(round($this->pixel->getColorValue(Imagick::COLOR_RED) * 255)); } public function green(): int { - return round($this->pixel->getColorValue(Imagick::COLOR_GREEN) * 255); + return intval(round($this->pixel->getColorValue(Imagick::COLOR_GREEN) * 255)); } public function blue(): int { - return round($this->pixel->getColorValue(Imagick::COLOR_BLUE) * 255); + return intval(round($this->pixel->getColorValue(Imagick::COLOR_BLUE) * 255)); } public function alpha(): float diff --git a/src/Drivers/Imagick/Encoders/GifEncoder.php b/src/Drivers/Imagick/Encoders/GifEncoder.php index f7192274..d180dc11 100644 --- a/src/Drivers/Imagick/Encoders/GifEncoder.php +++ b/src/Drivers/Imagick/Encoders/GifEncoder.php @@ -4,7 +4,9 @@ namespace Intervention\Image\Drivers\Imagick\Encoders; use Imagick; use Intervention\Image\Drivers\Abstract\Encoders\AbstractEncoder; +use Intervention\Image\Drivers\Imagick\Image; use Intervention\Image\EncodedImage; +use Intervention\Image\Exceptions\EncoderException; use Intervention\Image\Interfaces\EncoderInterface; use Intervention\Image\Interfaces\ImageInterface; @@ -15,6 +17,10 @@ class GifEncoder extends AbstractEncoder implements EncoderInterface $format = 'gif'; $compression = Imagick::COMPRESSION_LZW; + if (!is_a($image, Image::class)) { + throw new EncoderException('Image does not match the current driver.'); + } + $imagick = $image->getImagick(); $imagick->setFormat($format); $imagick->setImageFormat($format); diff --git a/src/Drivers/Imagick/Font.php b/src/Drivers/Imagick/Font.php index 93f21943..e719fe7a 100644 --- a/src/Drivers/Imagick/Font.php +++ b/src/Drivers/Imagick/Font.php @@ -5,6 +5,7 @@ namespace Intervention\Image\Drivers\Imagick; use Imagick; use ImagickDraw; use Intervention\Image\Drivers\Abstract\AbstractFont; +use Intervention\Image\Exceptions\DecoderException; use Intervention\Image\Exceptions\FontException; use Intervention\Image\Geometry\Polygon; use Intervention\Image\Geometry\Size; @@ -17,12 +18,17 @@ class Font extends AbstractFont throw new FontException('No font file specified.'); } + $color = $this->getColor(); + if (!is_a($color, Color::class)) { + throw new DecoderException('Unable to decode font color.'); + } + $draw = new ImagickDraw(); $draw->setStrokeAntialias(true); $draw->setTextAntialias(true); $draw->setFont($this->getFilename()); $draw->setFontSize($this->getSize()); - $draw->setFillColor($this->getColor()->getPixel()); + $draw->setFillColor($color->getPixel()); $draw->setTextAlignment($this->getImagickAlign()); return $draw; @@ -38,11 +44,9 @@ class Font extends AbstractFont switch (strtolower($this->getAlign())) { case 'center': return Imagick::ALIGN_CENTER; - break; case 'right': return Imagick::ALIGN_RIGHT; - break; } return Imagick::ALIGN_LEFT; diff --git a/src/Drivers/Imagick/Frame.php b/src/Drivers/Imagick/Frame.php index cb49b24d..245aff84 100644 --- a/src/Drivers/Imagick/Frame.php +++ b/src/Drivers/Imagick/Frame.php @@ -39,7 +39,7 @@ class Frame implements FrameInterface public function setDelay(float $delay): FrameInterface { - $this->core->setImageDelay(round($delay * 100)); + $this->core->setImageDelay(intval(round($delay * 100))); return $this; } diff --git a/src/Drivers/Imagick/ImageFactory.php b/src/Drivers/Imagick/ImageFactory.php index 57ef4cc3..cc54048b 100644 --- a/src/Drivers/Imagick/ImageFactory.php +++ b/src/Drivers/Imagick/ImageFactory.php @@ -14,7 +14,7 @@ class ImageFactory implements FactoryInterface return new Image($this->newCore($width, $height)); } - public function newCore(int $width, int $height): Imagick + public function newCore(int $width, int $height) { $imagick = new Imagick(); $imagick->newImage($width, $height, new ImagickPixel('rgba(0, 0, 0, 0)'), 'png'); diff --git a/src/Drivers/Imagick/Modifiers/PadModifier.php b/src/Drivers/Imagick/Modifiers/PadModifier.php index 94bee08e..94d44a65 100644 --- a/src/Drivers/Imagick/Modifiers/PadModifier.php +++ b/src/Drivers/Imagick/Modifiers/PadModifier.php @@ -5,7 +5,8 @@ namespace Intervention\Image\Drivers\Imagick\Modifiers; use Imagick; use ImagickDraw; use Intervention\Image\Drivers\Abstract\Modifiers\AbstractPadModifier; -use Intervention\Image\Interfaces\ColorInterface; +use Intervention\Image\Drivers\Imagick\Color; +use Intervention\Image\Exceptions\DecoderException; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; use Intervention\Image\Interfaces\SizeInterface; @@ -23,6 +24,10 @@ class PadModifier extends AbstractPadModifier implements ModifierInterface $crop = $this->getCropSize($image); $background = $this->handleInput($this->background); + if (!is_a($background, Color::class)) { + throw new DecoderException('Unable to decode backgroud color.'); + } + foreach ($image as $frame) { // resize current core $frame->getCore()->scaleImage( @@ -49,7 +54,7 @@ class PadModifier extends AbstractPadModifier implements ModifierInterface return $image; } - protected function buildBaseCanvas(SizeInterface $crop, SizeInterface $resize, ColorInterface $background): Imagick + protected function buildBaseCanvas(SizeInterface $crop, SizeInterface $resize, Color $background): Imagick { // build base canvas in target size $canvas = $this->imageFactory()->newCore( diff --git a/src/Drivers/Imagick/Modifiers/PlaceModifier.php b/src/Drivers/Imagick/Modifiers/PlaceModifier.php index 06f1ef09..894ba153 100644 --- a/src/Drivers/Imagick/Modifiers/PlaceModifier.php +++ b/src/Drivers/Imagick/Modifiers/PlaceModifier.php @@ -7,6 +7,7 @@ use Intervention\Image\Drivers\Imagick\Image; use Intervention\Image\Geometry\Point; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; +use Intervention\Image\Interfaces\PointInterface; use Intervention\Image\Traits\CanResolveDriverClass; class PlaceModifier implements ModifierInterface @@ -44,7 +45,7 @@ class PlaceModifier implements ModifierInterface return $this->resolveDriverClass('InputHandler')->handle($this->element); } - protected function getPosition(Image $image, Image $watermark): Point + protected function getPosition(ImageInterface $image, Image $watermark): PointInterface { $image_size = $image->getSize()->alignPivot($this->position, $this->offset_x, $this->offset_y); $watermark_size = $watermark->getSize()->alignPivot($this->position); diff --git a/src/Drivers/Imagick/Modifiers/RotateModifier.php b/src/Drivers/Imagick/Modifiers/RotateModifier.php index 3fde612e..304585e7 100644 --- a/src/Drivers/Imagick/Modifiers/RotateModifier.php +++ b/src/Drivers/Imagick/Modifiers/RotateModifier.php @@ -3,6 +3,8 @@ namespace Intervention\Image\Drivers\Imagick\Modifiers; use Intervention\Image\Drivers\Abstract\Modifiers\AbstractRotateModifier; +use Intervention\Image\Drivers\Imagick\Color; +use Intervention\Image\Exceptions\DecoderException; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; @@ -10,9 +12,14 @@ class RotateModifier extends AbstractRotateModifier implements ModifierInterface { public function apply(ImageInterface $image): ImageInterface { + $background = $this->backgroundColor(); + if (!is_a($background, Color::class)) { + throw new DecoderException('Unable to decode given background color.'); + } + foreach ($image as $frame) { $frame->getCore()->rotateImage( - $this->backgroundColor()->getPixel(), + $background->getPixel(), $this->rotationAngle() ); } diff --git a/src/Exceptions/EncoderException.php b/src/Exceptions/EncoderException.php new file mode 100644 index 00000000..f7d0721a --- /dev/null +++ b/src/Exceptions/EncoderException.php @@ -0,0 +1,8 @@ + Date: Sat, 25 Jun 2022 12:18:46 +0200 Subject: [PATCH 182/476] Prepare TextWriter for multi line functionality --- src/Drivers/Abstract/AbstractFont.php | 7 +--- src/Drivers/Abstract/AbstractImage.php | 4 +-- src/Drivers/Abstract/AbstractTextWriter.php | 18 ++++++++++ src/Drivers/Gd/Font.php | 7 ++-- src/Drivers/Gd/Modifiers/TextWriter.php | 38 ++++++++++---------- src/Drivers/Imagick/Font.php | 26 ++++++++------ src/Drivers/Imagick/Modifiers/TextWriter.php | 30 ++++++++-------- src/Interfaces/FontInterface.php | 5 +-- src/Typography/Line.php | 16 +++++++++ src/Typography/TextBlock.php | 20 +++++++++++ tests/Drivers/Abstract/AbstractFontTest.php | 3 +- tests/Typography/LineTest.php | 21 +++++++++++ tests/Typography/TextBlockTest.php | 30 ++++++++++++++++ 13 files changed, 167 insertions(+), 58 deletions(-) create mode 100644 src/Drivers/Abstract/AbstractTextWriter.php create mode 100644 src/Typography/Line.php create mode 100644 src/Typography/TextBlock.php create mode 100644 tests/Typography/LineTest.php create mode 100644 tests/Typography/TextBlockTest.php diff --git a/src/Drivers/Abstract/AbstractFont.php b/src/Drivers/Abstract/AbstractFont.php index 8991cd90..dd03a831 100644 --- a/src/Drivers/Abstract/AbstractFont.php +++ b/src/Drivers/Abstract/AbstractFont.php @@ -17,18 +17,13 @@ abstract class AbstractFont implements FontInterface protected $align = 'left'; protected $valign = 'bottom'; - public function __construct(protected string $text, ?callable $init = null) + public function __construct(callable $init = null) { if (is_callable($init)) { $init($this); } } - public function getText(): string - { - return $this->text; - } - public function size(float $size): FontInterface { $this->size = $size; diff --git a/src/Drivers/Abstract/AbstractImage.php b/src/Drivers/Abstract/AbstractImage.php index 2342b29c..2136c3bd 100644 --- a/src/Drivers/Abstract/AbstractImage.php +++ b/src/Drivers/Abstract/AbstractImage.php @@ -194,8 +194,8 @@ abstract class AbstractImage implements ImageInterface public function text(string $text, int $x, int $y, ?callable $init = null): ImageInterface { - $font = $this->resolveDriverClass('Font', $text, $init); - $modifier = $this->resolveDriverClass('Modifiers\TextWriter', new Point($x, $y), $font); + $font = $this->resolveDriverClass('Font', $init); + $modifier = $this->resolveDriverClass('Modifiers\TextWriter', new Point($x, $y), $font, $text); return $this->modify($modifier); } diff --git a/src/Drivers/Abstract/AbstractTextWriter.php b/src/Drivers/Abstract/AbstractTextWriter.php new file mode 100644 index 00000000..9b1e7a3a --- /dev/null +++ b/src/Drivers/Abstract/AbstractTextWriter.php @@ -0,0 +1,18 @@ +hasFilename()) { // calculate box size from gd font $box = new Size(0, 0); - $chars = mb_strlen($this->getText()); + $chars = mb_strlen($text); if ($chars > 0) { $box->setWidth($chars * $this->getGdFontWidth()); $box->setHeight($this->getGdFontHeight()); @@ -37,7 +37,7 @@ class Font extends AbstractFont $this->getSize(), 0, $this->getFilename(), - $this->getText() + $text ); // build polygon from points @@ -47,7 +47,6 @@ class Font extends AbstractFont $polygon->addPoint(new Point($box[2], $box[3])); $polygon->addPoint(new Point($box[0], $box[1])); - return $polygon; } diff --git a/src/Drivers/Gd/Modifiers/TextWriter.php b/src/Drivers/Gd/Modifiers/TextWriter.php index 3a6b18a6..486dedb2 100644 --- a/src/Drivers/Gd/Modifiers/TextWriter.php +++ b/src/Drivers/Gd/Modifiers/TextWriter.php @@ -2,20 +2,14 @@ namespace Intervention\Image\Drivers\Gd\Modifiers; +use Intervention\Image\Drivers\Abstract\AbstractTextWriter; use Intervention\Image\Drivers\Gd\Font; +use Intervention\Image\Exceptions\FontException; use Intervention\Image\Geometry\Point; use Intervention\Image\Interfaces\ImageInterface; -use Intervention\Image\Interfaces\ModifierInterface; -class TextWriter implements ModifierInterface +class TextWriter extends AbstractTextWriter { - public function __construct( - protected Point $position, - protected Font $font - ) { - // - } - public function apply(ImageInterface $image): ImageInterface { $position = $this->getAlignedPosition(); @@ -23,21 +17,21 @@ class TextWriter implements ModifierInterface if ($this->font->hasFilename()) { imagettftext( $frame->getCore(), - $this->font->getSize(), - $this->font->getAngle() * (-1), + $this->getFont()->getSize(), + $this->getFont()->getAngle() * (-1), $position->getX(), $position->getY(), - $this->font->getColor()->toInt(), - $this->font->getFilename(), - $this->font->getText() + $this->getFont()->getColor()->toInt(), + $this->getFont()->getFilename(), + $this->text ); } else { imagestring( $frame->getCore(), - $this->font->getGdFont(), + $this->getFont()->getGdFont(), $position->getX(), $position->getY(), - $this->font->getText(), + $this->text, $this->font->getColor()->toInt() ); } @@ -46,9 +40,9 @@ class TextWriter implements ModifierInterface return $image; } - public function getAlignedPosition(): Point + private function getAlignedPosition(): Point { - $poly = $this->font->getBoxSize(); + $poly = $this->font->getBoxSize($this->text); $poly->setPivotPoint($this->position); $poly->align($this->font->getAlign()); @@ -60,4 +54,12 @@ class TextWriter implements ModifierInterface return $poly->last(); } + + private function getFont(): Font + { + if (!is_a($this->font, Font::class)) { + throw new FontException('Font is not compatible to current driver.'); + } + return $this->font; + } } diff --git a/src/Drivers/Imagick/Font.php b/src/Drivers/Imagick/Font.php index e719fe7a..2d46a71b 100644 --- a/src/Drivers/Imagick/Font.php +++ b/src/Drivers/Imagick/Font.php @@ -5,10 +5,10 @@ namespace Intervention\Image\Drivers\Imagick; use Imagick; use ImagickDraw; use Intervention\Image\Drivers\Abstract\AbstractFont; -use Intervention\Image\Exceptions\DecoderException; use Intervention\Image\Exceptions\FontException; use Intervention\Image\Geometry\Polygon; use Intervention\Image\Geometry\Size; +use Intervention\Image\Interfaces\ColorInterface; class Font extends AbstractFont { @@ -18,22 +18,28 @@ class Font extends AbstractFont throw new FontException('No font file specified.'); } - $color = $this->getColor(); - if (!is_a($color, Color::class)) { - throw new DecoderException('Unable to decode font color.'); - } - $draw = new ImagickDraw(); $draw->setStrokeAntialias(true); $draw->setTextAntialias(true); $draw->setFont($this->getFilename()); $draw->setFontSize($this->getSize()); - $draw->setFillColor($color->getPixel()); + $draw->setFillColor($this->getColor()->getPixel()); $draw->setTextAlignment($this->getImagickAlign()); return $draw; } + public function getColor(): ?ColorInterface + { + $color = parent::getColor(); + + if (!is_a($color, Color::class)) { + throw new FontException('Font is not compatible to current driver.'); + } + + return $color; + } + public function getAngle(): float { return parent::getAngle() * (-1); @@ -57,16 +63,16 @@ class Font extends AbstractFont * * @return Polygon */ - public function getBoxSize(): Polygon + public function getBoxSize(string $text): Polygon { // no text - no box size - if (mb_strlen($this->getText()) === 0) { + if (mb_strlen($text) === 0) { return (new Size(0, 0))->toPolygon(); } $dimensions = (new Imagick())->queryFontMetrics( $this->toImagickDraw(), - $this->getText() + $text ); return (new Size( diff --git a/src/Drivers/Imagick/Modifiers/TextWriter.php b/src/Drivers/Imagick/Modifiers/TextWriter.php index 4fe1dc2a..a85fd8b4 100644 --- a/src/Drivers/Imagick/Modifiers/TextWriter.php +++ b/src/Drivers/Imagick/Modifiers/TextWriter.php @@ -2,30 +2,24 @@ namespace Intervention\Image\Drivers\Imagick\Modifiers; +use Intervention\Image\Drivers\Abstract\AbstractTextWriter; use Intervention\Image\Drivers\Imagick\Font; +use Intervention\Image\Exceptions\FontException; use Intervention\Image\Geometry\Point; use Intervention\Image\Interfaces\ImageInterface; -use Intervention\Image\Interfaces\ModifierInterface; -class TextWriter implements ModifierInterface +class TextWriter extends AbstractTextWriter { - public function __construct( - protected Point $position, - protected Font $font - ) { - // - } - public function apply(ImageInterface $image): ImageInterface { $position = $this->getAlignedPosition(); foreach ($image as $frame) { $frame->getCore()->annotateImage( - $this->font->toImagickDraw(), + $this->getFont()->toImagickDraw(), $position->getX(), $position->getY(), - $this->font->getAngle() * (-1), - $this->font->getText() + $this->getFont()->getAngle() * (-1), + $this->text ); } @@ -35,10 +29,10 @@ class TextWriter implements ModifierInterface protected function getAlignedPosition(): Point { $position = $this->position; - $box = $this->font->getBoxSize(); + $box = $this->getFont()->getBoxSize($this->text); // adjust y pos - switch ($this->font->getValign()) { + switch ($this->getFont()->getValign()) { case 'top': $position->setY($position->getY() + $box->height()); break; @@ -51,4 +45,12 @@ class TextWriter implements ModifierInterface return $position; } + + private function getFont(): Font + { + if (!is_a($this->font, Font::class)) { + throw new FontException('Font is not compatible to current driver.'); + } + return $this->font; + } } diff --git a/src/Interfaces/FontInterface.php b/src/Interfaces/FontInterface.php index 92e47008..66a78b15 100644 --- a/src/Interfaces/FontInterface.php +++ b/src/Interfaces/FontInterface.php @@ -7,7 +7,6 @@ use Intervention\Image\Interfaces\ColorInterface; interface FontInterface { - public function getText(): string; public function color($color): self; public function getColor(): ?ColorInterface; public function size(float $size): self; @@ -19,5 +18,7 @@ interface FontInterface public function hasFilename(): bool; public function align(string $align): self; public function getAlign(): string; - public function getBoxSize(): Polygon; + public function valign(string $align): self; + public function getValign(): string; + public function getBoxSize(string $text): Polygon; } diff --git a/src/Typography/Line.php b/src/Typography/Line.php new file mode 100644 index 00000000..80037869 --- /dev/null +++ b/src/Typography/Line.php @@ -0,0 +1,16 @@ +text; + } +} diff --git a/src/Typography/TextBlock.php b/src/Typography/TextBlock.php new file mode 100644 index 00000000..c8cdadf3 --- /dev/null +++ b/src/Typography/TextBlock.php @@ -0,0 +1,20 @@ +push(new Line($line)); + } + } + + public function lines(): array + { + return $this->items; + } +} diff --git a/tests/Drivers/Abstract/AbstractFontTest.php b/tests/Drivers/Abstract/AbstractFontTest.php index e39d345d..0a5be182 100644 --- a/tests/Drivers/Abstract/AbstractFontTest.php +++ b/tests/Drivers/Abstract/AbstractFontTest.php @@ -12,7 +12,7 @@ class AbstractFontTest extends TestCase private function getAbstractFontMock() { // create mock - $mock = Mockery::mock(AbstractFont::class, ['test123']) + $mock = Mockery::mock(AbstractFont::class) ->shouldAllowMockingProtectedMethods() ->makePartial(); @@ -34,7 +34,6 @@ class AbstractFontTest extends TestCase public function testConstructor(): void { $mock = $this->getAbstractFontMock(); - $this->assertEquals('test123', $mock->getText()); $this->assertEquals(24.0, $mock->getSize()); $this->assertEquals(30, $mock->getAngle()); $this->assertEquals(__DIR__ . '/AbstractFontTest.php', $mock->getFilename()); diff --git a/tests/Typography/LineTest.php b/tests/Typography/LineTest.php new file mode 100644 index 00000000..fb7de727 --- /dev/null +++ b/tests/Typography/LineTest.php @@ -0,0 +1,21 @@ +assertInstanceOf(Line::class, $line); + } + + public function testToString(): void + { + $line = new Line('foo'); + $this->assertEquals('foo', (string) $line); + } +} diff --git a/tests/Typography/TextBlockTest.php b/tests/Typography/TextBlockTest.php new file mode 100644 index 00000000..bb4a901c --- /dev/null +++ b/tests/Typography/TextBlockTest.php @@ -0,0 +1,30 @@ +getTestBlock(); + $this->assertInstanceOf(TextBlock::class, $block); + $this->assertEquals(3, $block->count()); + } + + public function testLines(): void + { + $block = $this->getTestBlock(); + $this->assertCount(3, $block->lines()); + } +} From 0e2aa7f595fe78020e098be5a0252804c43510b5 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 25 Jun 2022 16:43:34 +0200 Subject: [PATCH 183/476] Implement multiline functionality for TextWriter --- src/Drivers/Abstract/AbstractFont.php | 13 ++++ src/Drivers/Abstract/AbstractTextWriter.php | 6 ++ src/Drivers/Gd/Font.php | 15 +++++ src/Drivers/Gd/Modifiers/TextWriter.php | 72 ++++++++++++++------- src/Interfaces/FontInterface.php | 5 ++ src/Typography/Line.php | 7 ++ src/Typography/TextBlock.php | 13 ++++ 7 files changed, 109 insertions(+), 22 deletions(-) diff --git a/src/Drivers/Abstract/AbstractFont.php b/src/Drivers/Abstract/AbstractFont.php index dd03a831..dc246737 100644 --- a/src/Drivers/Abstract/AbstractFont.php +++ b/src/Drivers/Abstract/AbstractFont.php @@ -16,6 +16,7 @@ abstract class AbstractFont implements FontInterface protected $filename; protected $align = 'left'; protected $valign = 'bottom'; + protected $lineHeight = 1.25; public function __construct(callable $init = null) { @@ -100,4 +101,16 @@ abstract class AbstractFont implements FontInterface { return $this->align; } + + public function lineHeight(float $height): FontInterface + { + $this->lineHeight = $height; + + return $this; + } + + public function getLineHeight(): float + { + return $this->lineHeight; + } } diff --git a/src/Drivers/Abstract/AbstractTextWriter.php b/src/Drivers/Abstract/AbstractTextWriter.php index 9b1e7a3a..7c0e196f 100644 --- a/src/Drivers/Abstract/AbstractTextWriter.php +++ b/src/Drivers/Abstract/AbstractTextWriter.php @@ -5,6 +5,7 @@ namespace Intervention\Image\Drivers\Abstract; use Intervention\Image\Geometry\Point; use Intervention\Image\Interfaces\FontInterface; use Intervention\Image\Interfaces\ModifierInterface; +use Intervention\Image\Typography\TextBlock; abstract class AbstractTextWriter implements ModifierInterface { @@ -15,4 +16,9 @@ abstract class AbstractTextWriter implements ModifierInterface ) { // } + + public function getTextBlock(): TextBlock + { + return new TextBlock($this->text); + } } diff --git a/src/Drivers/Gd/Font.php b/src/Drivers/Gd/Font.php index 88048d61..e524dcfe 100644 --- a/src/Drivers/Gd/Font.php +++ b/src/Drivers/Gd/Font.php @@ -84,4 +84,19 @@ class Font extends AbstractFont return 8; } } + + public function capHeight(): int + { + return $this->getBoxSize('T')->height(); + } + + public function leadingInPixels(): int + { + return intval(round($this->fontSizeInPixels() * $this->getLineHeight())); + } + + public function fontSizeInPixels(): int + { + return $this->getBoxSize('Hy')->height(); + } } diff --git a/src/Drivers/Gd/Modifiers/TextWriter.php b/src/Drivers/Gd/Modifiers/TextWriter.php index 486dedb2..6d2f4d4d 100644 --- a/src/Drivers/Gd/Modifiers/TextWriter.php +++ b/src/Drivers/Gd/Modifiers/TextWriter.php @@ -5,26 +5,39 @@ namespace Intervention\Image\Drivers\Gd\Modifiers; use Intervention\Image\Drivers\Abstract\AbstractTextWriter; use Intervention\Image\Drivers\Gd\Font; use Intervention\Image\Exceptions\FontException; -use Intervention\Image\Geometry\Point; +use Intervention\Image\Geometry\Polygon; +use Intervention\Image\Geometry\Size; use Intervention\Image\Interfaces\ImageInterface; class TextWriter extends AbstractTextWriter { public function apply(ImageInterface $image): ImageInterface { - $position = $this->getAlignedPosition(); + $box = $this->getBoundingBox(); + $position = clone $box->last(); + $leading = $this->getFont()->leadingInPixels(); + foreach ($image as $frame) { if ($this->font->hasFilename()) { - imagettftext( - $frame->getCore(), - $this->getFont()->getSize(), - $this->getFont()->getAngle() * (-1), - $position->getX(), - $position->getY(), - $this->getFont()->getColor()->toInt(), - $this->getFont()->getFilename(), - $this->text - ); + $position->moveY($this->getFont()->capHeight()); + $posx = $position->getX(); + $posy = $position->getY(); + foreach ($this->getTextBlock() as $line) { + imagettftext( + $frame->getCore(), + $this->getFont()->getSize(), + $this->getFont()->getAngle() * (-1), + $posx, + $posy, + $this->getFont()->getColor()->toInt(), + $this->getFont()->getFilename(), + $line + ); + $posy += $leading; + } + + // debug + imagepolygon($frame->getCore(), $box->toArray(), 0); } else { imagestring( $frame->getCore(), @@ -40,21 +53,36 @@ class TextWriter extends AbstractTextWriter return $image; } - private function getAlignedPosition(): Point + private function getBoundingBox(): Polygon { - $poly = $this->font->getBoxSize($this->text); + $size = new Size( + $this->getTextBlock()->longestLine()->width($this->font), + $this->getFont()->leadingInPixels() * $this->getTextBlock()->count() + ); + + $poly = $size->toPolygon(); $poly->setPivotPoint($this->position); + $poly->align($this->getFont()->getAlign()); + $poly->valign($this->getFont()->getValign()); - $poly->align($this->font->getAlign()); - $poly->valign($this->font->getValign()); - - if ($this->font->getAngle() != 0) { - $poly->rotate($this->font->getAngle()); - } - - return $poly->last(); + return $poly; } + // private function getAlignedPosition(): Point + // { + // $poly = $this->font->getBoxSize($this->text); + // $poly->setPivotPoint($this->position); + // + // $poly->align($this->font->getAlign()); + // $poly->valign($this->font->getValign()); + // + // if ($this->font->getAngle() != 0) { + // $poly->rotate($this->font->getAngle()); + // } + // + // return $poly->last(); + // } + private function getFont(): Font { if (!is_a($this->font, Font::class)) { diff --git a/src/Interfaces/FontInterface.php b/src/Interfaces/FontInterface.php index 66a78b15..6ff95e44 100644 --- a/src/Interfaces/FontInterface.php +++ b/src/Interfaces/FontInterface.php @@ -20,5 +20,10 @@ interface FontInterface public function getAlign(): string; public function valign(string $align): self; public function getValign(): string; + public function lineHeight(float $value): self; + public function getLineHeight(): float; + public function leadingInPixels(): int; + public function fontSizeInPixels(): int; + public function capHeight(): int; public function getBoxSize(string $text): Polygon; } diff --git a/src/Typography/Line.php b/src/Typography/Line.php index 80037869..67e38571 100644 --- a/src/Typography/Line.php +++ b/src/Typography/Line.php @@ -2,6 +2,8 @@ namespace Intervention\Image\Typography; +use Intervention\Image\Interfaces\FontInterface; + class Line { public function __construct(protected string $text) @@ -13,4 +15,9 @@ class Line { return $this->text; } + + public function width(FontInterface $font): int + { + return $font->getBoxSize($this->text)->width(); + } } diff --git a/src/Typography/TextBlock.php b/src/Typography/TextBlock.php index c8cdadf3..c77c29c8 100644 --- a/src/Typography/TextBlock.php +++ b/src/Typography/TextBlock.php @@ -17,4 +17,17 @@ class TextBlock extends Collection { return $this->items; } + + public function longestLine(): Line + { + $lines = $this->lines(); + usort($lines, function ($a, $b) { + if (mb_strlen($a) === mb_strlen($b)) { + return 0; + } + return (mb_strlen($a) > mb_strlen($b)) ? -1 : 1; + }); + + return $lines[0]; + } } From e1e1291fc51206d9be96d4f04c7905d165551d87 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 26 Jun 2022 09:20:26 +0200 Subject: [PATCH 184/476] Add position to typographic text line --- src/Typography/Line.php | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/Typography/Line.php b/src/Typography/Line.php index 67e38571..f4c05a01 100644 --- a/src/Typography/Line.php +++ b/src/Typography/Line.php @@ -3,21 +3,29 @@ namespace Intervention\Image\Typography; use Intervention\Image\Interfaces\FontInterface; +use Intervention\Image\Geometry\Point; class Line { + protected $position; + public function __construct(protected string $text) { - // + $this->position = new Point(); } - public function __toString(): string + public function getPosition(): Point { - return $this->text; + return $this->position; } public function width(FontInterface $font): int { return $font->getBoxSize($this->text)->width(); } + + public function __toString(): string + { + return $this->text; + } } From 5dc4e669699c8b5a43e4bb41ca2227308864510f Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 26 Jun 2022 20:09:21 +0200 Subject: [PATCH 185/476] Extended Textwriter to handle multi line text --- src/Drivers/Abstract/AbstractFont.php | 4 ++ src/Drivers/Gd/Font.php | 2 +- src/Drivers/Gd/Modifiers/TextWriter.php | 69 ++++++---------------- src/Geometry/Size.php | 28 +++++++++ src/Typography/Line.php | 9 ++- src/Typography/TextBlock.php | 76 +++++++++++++++++++++++++ tests/Drivers/Gd/FontTest.php | 30 ++++++++++ tests/Geometry/PointTest.php | 5 ++ tests/Typography/LineTest.php | 12 ++++ tests/Typography/TextBlockTest.php | 53 ++++++++++++++++- 10 files changed, 234 insertions(+), 54 deletions(-) create mode 100644 tests/Drivers/Gd/FontTest.php diff --git a/src/Drivers/Abstract/AbstractFont.php b/src/Drivers/Abstract/AbstractFont.php index dc246737..2b505952 100644 --- a/src/Drivers/Abstract/AbstractFont.php +++ b/src/Drivers/Abstract/AbstractFont.php @@ -2,9 +2,13 @@ namespace Intervention\Image\Drivers\Abstract; +use Intervention\Image\Geometry\Point; +use Intervention\Image\Geometry\Polygon; +use Intervention\Image\Geometry\Size; use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\FontInterface; use Intervention\Image\Traits\CanHandleInput; +use Intervention\Image\Typography\TextBlock; abstract class AbstractFont implements FontInterface { diff --git a/src/Drivers/Gd/Font.php b/src/Drivers/Gd/Font.php index e524dcfe..79ee8f81 100644 --- a/src/Drivers/Gd/Font.php +++ b/src/Drivers/Gd/Font.php @@ -15,7 +15,7 @@ class Font extends AbstractFont } /** - * Calculate size of bounding box of current text + * Calculate size of bounding box of given text * * @return Polygon */ diff --git a/src/Drivers/Gd/Modifiers/TextWriter.php b/src/Drivers/Gd/Modifiers/TextWriter.php index 6d2f4d4d..b08b90b4 100644 --- a/src/Drivers/Gd/Modifiers/TextWriter.php +++ b/src/Drivers/Gd/Modifiers/TextWriter.php @@ -5,84 +5,51 @@ namespace Intervention\Image\Drivers\Gd\Modifiers; use Intervention\Image\Drivers\Abstract\AbstractTextWriter; use Intervention\Image\Drivers\Gd\Font; use Intervention\Image\Exceptions\FontException; -use Intervention\Image\Geometry\Polygon; -use Intervention\Image\Geometry\Size; use Intervention\Image\Interfaces\ImageInterface; class TextWriter extends AbstractTextWriter { public function apply(ImageInterface $image): ImageInterface { - $box = $this->getBoundingBox(); - $position = clone $box->last(); - $leading = $this->getFont()->leadingInPixels(); + $lines = $this->getTextBlock(); + $boundingBox = $lines->getBoundingBox($this->getFont(), $this->position); + $lines->alignByFont($this->getFont(), $boundingBox->last()); foreach ($image as $frame) { if ($this->font->hasFilename()) { - $position->moveY($this->getFont()->capHeight()); - $posx = $position->getX(); - $posy = $position->getY(); - foreach ($this->getTextBlock() as $line) { + foreach ($lines as $line) { imagettftext( $frame->getCore(), $this->getFont()->getSize(), $this->getFont()->getAngle() * (-1), - $posx, - $posy, + $line->getPosition()->getX(), + $line->getPosition()->getY(), $this->getFont()->getColor()->toInt(), $this->getFont()->getFilename(), $line ); - $posy += $leading; } // debug - imagepolygon($frame->getCore(), $box->toArray(), 0); + imagepolygon($frame->getCore(), $boundingBox->toArray(), 0); } else { - imagestring( - $frame->getCore(), - $this->getFont()->getGdFont(), - $position->getX(), - $position->getY(), - $this->text, - $this->font->getColor()->toInt() - ); + foreach ($lines as $line) { + imagestring( + $frame->getCore(), + $this->getFont()->getGdFont(), + $line->getPosition()->getX(), + $line->getPosition()->getY(), + $line, + $this->font->getColor()->toInt() + ); + imagepolygon($frame->getCore(), $boundingBox->toArray(), 0); + } } } return $image; } - private function getBoundingBox(): Polygon - { - $size = new Size( - $this->getTextBlock()->longestLine()->width($this->font), - $this->getFont()->leadingInPixels() * $this->getTextBlock()->count() - ); - - $poly = $size->toPolygon(); - $poly->setPivotPoint($this->position); - $poly->align($this->getFont()->getAlign()); - $poly->valign($this->getFont()->getValign()); - - return $poly; - } - - // private function getAlignedPosition(): Point - // { - // $poly = $this->font->getBoxSize($this->text); - // $poly->setPivotPoint($this->position); - // - // $poly->align($this->font->getAlign()); - // $poly->valign($this->font->getValign()); - // - // if ($this->font->getAngle() != 0) { - // $poly->rotate($this->font->getAngle()); - // } - // - // return $poly->last(); - // } - private function getFont(): Font { if (!is_a($this->font, Font::class)) { diff --git a/src/Geometry/Size.php b/src/Geometry/Size.php index 532940b5..07c15d06 100644 --- a/src/Geometry/Size.php +++ b/src/Geometry/Size.php @@ -40,6 +40,34 @@ class Size implements SizeInterface return $this; } + public function addWidth(int $value): SizeInterface + { + $this->width = $this->width + $value; + + return $this; + } + + public function subWidth(int $value): SizeInterface + { + $this->width = $this->width - $value; + + return $this; + } + + public function addHeight(int $value): SizeInterface + { + $this->height = $this->height + $value; + + return $this; + } + + public function subHeight(int $value): SizeInterface + { + $this->height = $this->height - $value; + + return $this; + } + /** * Get current pivot point * diff --git a/src/Typography/Line.php b/src/Typography/Line.php index f4c05a01..10e98665 100644 --- a/src/Typography/Line.php +++ b/src/Typography/Line.php @@ -19,7 +19,14 @@ class Line return $this->position; } - public function width(FontInterface $font): int + public function setPosition(Point $point): self + { + $this->position = $point; + + return $this; + } + + public function widthInFont(FontInterface $font): int { return $font->getBoxSize($this->text)->width(); } diff --git a/src/Typography/TextBlock.php b/src/Typography/TextBlock.php index c77c29c8..b6174b1a 100644 --- a/src/Typography/TextBlock.php +++ b/src/Typography/TextBlock.php @@ -3,6 +3,10 @@ namespace Intervention\Image\Typography; use Intervention\Image\Collection; +use Intervention\Image\Geometry\Point; +use Intervention\Image\Geometry\Polygon; +use Intervention\Image\Geometry\Size; +use Intervention\Image\Interfaces\FontInterface; class TextBlock extends Collection { @@ -13,11 +17,83 @@ class TextBlock extends Collection } } + /** + * Set position of each line in text block + * according to given font settings. + * + * @param FontInterface $font + * @param Point $pivot + * @return TextBlock + */ + public function alignByFont(FontInterface $font, Point $pivot = null): self + { + $pivot = $pivot ? $pivot : new Point(); + + $leading = $font->leadingInPixels(); + $x = $pivot->getX(); + $y = $font->hasFilename() ? $pivot->getY() + $font->capHeight() : $pivot->getY(); + + $x_adjustment = 0; + $total_width = $this->longestLine()->widthInFont($font); + foreach ($this as $line) { + $x_adjustment = $font->getAlign() == 'left' ? 0 : $total_width - $line->widthInFont($font); + $x_adjustment = $font->getAlign() == 'right' ? intval(round($x_adjustment)) : $x_adjustment; + $x_adjustment = $font->getAlign() == 'center' ? intval(round($x_adjustment / 2)) : $x_adjustment; + $position = new Point($x + $x_adjustment, $y); + $position->rotate($font->getAngle(), $pivot); + $line->setPosition($position); + $y += $leading; + } + + return $this; + } + + public function getBoundingBox(FontInterface $font, Point $pivot = null): Polygon + { + $pivot = $pivot ? $pivot : new Point(); + + // bounding box + $box = (new Size( + $this->longestLine()->widthInFont($font), + $font->leadingInPixels() * ($this->count() - 1) + $font->capHeight() + ))->toPolygon(); + + // set pivot + $box->setPivotPoint($pivot); + + // align + $box->align($font->getAlign()); + $box->valign($font->getValign()); + + $box->rotate($font->getAngle()); + + return $box; + } + + /** + * Return array of lines in text block + * + * @return array + */ public function lines(): array { return $this->items; } + public function getLine($key): ?Line + { + if (!array_key_exists($key, $this->lines())) { + return null; + } + + return $this->lines()[$key]; + } + + /** + * Return line with most characters of text block + * + * @return Line + */ public function longestLine(): Line { $lines = $this->lines(); diff --git a/tests/Drivers/Gd/FontTest.php b/tests/Drivers/Gd/FontTest.php new file mode 100644 index 00000000..62e02e48 --- /dev/null +++ b/tests/Drivers/Gd/FontTest.php @@ -0,0 +1,30 @@ +size(12); + $this->assertEquals(9, $font->getSize()); + } + + public function testGetGdFont(): void + { + $font = new Font(); + $this->assertEquals(1, $font->getGdFont()); + $font->filename(12); + $this->assertEquals(12, $font->getGdFont()); + } + + public function testCapHeight(): void + { + $font = new Font(); + $this->assertEquals(8, $font->capHeight()); + } +} diff --git a/tests/Geometry/PointTest.php b/tests/Geometry/PointTest.php index 8f95d81d..98db4393 100644 --- a/tests/Geometry/PointTest.php +++ b/tests/Geometry/PointTest.php @@ -81,5 +81,10 @@ class PointTest extends TestCase $point->rotate(90, new Point(0, 0)); $this->assertEquals(-200, $point->getX()); $this->assertEquals(300, $point->getY()); + + $point = new Point(0, 74); + $point->rotate(45, new Point(0, 0)); + $this->assertEquals(-52, $point->getX()); + $this->assertEquals(52, $point->getY()); } } diff --git a/tests/Typography/LineTest.php b/tests/Typography/LineTest.php index fb7de727..b4c2de6a 100644 --- a/tests/Typography/LineTest.php +++ b/tests/Typography/LineTest.php @@ -2,6 +2,7 @@ namespace Intervention\Image\Tests\Typography; +use Intervention\Image\Geometry\Point; use Intervention\Image\Tests\TestCase; use Intervention\Image\Typography\Line; @@ -18,4 +19,15 @@ class LineTest extends TestCase $line = new Line('foo'); $this->assertEquals('foo', (string) $line); } + + public function testSetGetPosition(): void + { + $line = new Line('foo'); + $this->assertEquals(0, $line->getPosition()->getX()); + $this->assertEquals(0, $line->getPosition()->getY()); + + $line->setPosition(new Point(10, 11)); + $this->assertEquals(10, $line->getPosition()->getX()); + $this->assertEquals(11, $line->getPosition()->getY()); + } } diff --git a/tests/Typography/TextBlockTest.php b/tests/Typography/TextBlockTest.php index bb4a901c..36fb2779 100644 --- a/tests/Typography/TextBlockTest.php +++ b/tests/Typography/TextBlockTest.php @@ -4,6 +4,10 @@ namespace Intervention\Image\Tests\Typography; use Intervention\Image\Tests\TestCase; use Intervention\Image\Typography\TextBlock; +use Intervention\Image\Drivers\Abstract\AbstractFont; +use Intervention\Image\Geometry\Point; +use Intervention\Image\Geometry\Polygon; +use Mockery; class TextBlockTest extends TestCase { @@ -11,10 +15,11 @@ class TextBlockTest extends TestCase { return new TextBlock(<<getTestBlock(); @@ -27,4 +32,50 @@ class TextBlockTest extends TestCase $block = $this->getTestBlock(); $this->assertCount(3, $block->lines()); } + + public function testGetLine(): void + { + $block = $this->getTestBlock(); + $this->assertEquals('foo', $block->getLine(0)); + $this->assertEquals('FooBar', $block->getLine(1)); + $this->assertEquals('bar', $block->getLine(2)); + } + + public function testAlignByFont(): void + { + $font = Mockery::mock(AbstractFont::class) + ->shouldAllowMockingProtectedMethods() + ->makePartial(); + + $font->shouldReceive('getBoxSize')->andReturn( + new Polygon([ + new Point(-1, -29), + new Point(141, -29), + new Point(141, 98), + new Point(-1, 98), + ]) + ); + + // $font->shouldReceive('capHeight')->andReturn(22); + + $font->shouldReceive('leadingInPixels')->andReturn(74); + $font->angle(45); + + $block = $this->getTestBlock(); // before + $this->assertEquals(0, $block->getLine(0)->getPosition()->getX()); + $this->assertEquals(0, $block->getLine(0)->getPosition()->getY()); + $this->assertEquals(0, $block->getLine(1)->getPosition()->getX()); + $this->assertEquals(0, $block->getLine(1)->getPosition()->getY()); + $this->assertEquals(0, $block->getLine(2)->getPosition()->getX()); + $this->assertEquals(0, $block->getLine(2)->getPosition()->getY()); + + $result = $block->alignByFont($font); // after + $this->assertInstanceOf(TextBlock::class, $result); + $this->assertEquals(0, $block->getLine(0)->getPosition()->getX()); + $this->assertEquals(0, $block->getLine(0)->getPosition()->getY()); + $this->assertEquals(-52, $block->getLine(1)->getPosition()->getX()); + $this->assertEquals(52, $block->getLine(1)->getPosition()->getY()); + $this->assertEquals(-104, $block->getLine(2)->getPosition()->getX()); + $this->assertEquals(104, $block->getLine(2)->getPosition()->getY()); + } } From 6a4a7bfa8fe6540d060354696cf6f52d52e584dd Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Mon, 4 Jul 2022 15:46:32 +0200 Subject: [PATCH 186/476] Refactor GD TextWriter --- src/Drivers/Abstract/AbstractTextWriter.php | 44 ++++++++++++++++++++- src/Drivers/Gd/Modifiers/TextWriter.php | 12 ++---- src/Typography/TextBlock.php | 31 --------------- 3 files changed, 45 insertions(+), 42 deletions(-) diff --git a/src/Drivers/Abstract/AbstractTextWriter.php b/src/Drivers/Abstract/AbstractTextWriter.php index 7c0e196f..502f4172 100644 --- a/src/Drivers/Abstract/AbstractTextWriter.php +++ b/src/Drivers/Abstract/AbstractTextWriter.php @@ -17,8 +17,48 @@ abstract class AbstractTextWriter implements ModifierInterface // } - public function getTextBlock(): TextBlock + protected function getFont(): FontInterface { - return new TextBlock($this->text); + return $this->font; + } + + protected function getPosition(): Point + { + return $this->position; + } + + /** + * Build TextBlock object from text string and align every line + * according to text writers font object and position. + * + * @return TextBlock + */ + public function getAlignedTextBlock(): TextBlock + { + $lines = new TextBlock($this->text); + $position = $this->getPosition(); + $font = $this->getFont(); + + $boundingBox = $lines->getBoundingBox($font, $position); + $pivot = $boundingBox->last(); + + $leading = $font->leadingInPixels(); + $blockWidth = $lines->longestLine()->widthInFont($font); + + $x = $pivot->getX(); + $y = $font->hasFilename() ? $pivot->getY() + $font->capHeight() : $pivot->getY(); + $x_adjustment = 0; + + foreach ($lines as $line) { + $x_adjustment = $font->getAlign() == 'left' ? 0 : $blockWidth - $line->widthInFont($font); + $x_adjustment = $font->getAlign() == 'right' ? intval(round($x_adjustment)) : $x_adjustment; + $x_adjustment = $font->getAlign() == 'center' ? intval(round($x_adjustment / 2)) : $x_adjustment; + $position = new Point($x + $x_adjustment, $y); + $position->rotate($font->getAngle(), $pivot); + $line->setPosition($position); + $y += $leading; + } + + return $lines; } } diff --git a/src/Drivers/Gd/Modifiers/TextWriter.php b/src/Drivers/Gd/Modifiers/TextWriter.php index b08b90b4..70ebae21 100644 --- a/src/Drivers/Gd/Modifiers/TextWriter.php +++ b/src/Drivers/Gd/Modifiers/TextWriter.php @@ -5,16 +5,14 @@ namespace Intervention\Image\Drivers\Gd\Modifiers; use Intervention\Image\Drivers\Abstract\AbstractTextWriter; use Intervention\Image\Drivers\Gd\Font; use Intervention\Image\Exceptions\FontException; +use Intervention\Image\Interfaces\FontInterface; use Intervention\Image\Interfaces\ImageInterface; class TextWriter extends AbstractTextWriter { public function apply(ImageInterface $image): ImageInterface { - $lines = $this->getTextBlock(); - $boundingBox = $lines->getBoundingBox($this->getFont(), $this->position); - $lines->alignByFont($this->getFont(), $boundingBox->last()); - + $lines = $this->getAlignedTextBlock(); foreach ($image as $frame) { if ($this->font->hasFilename()) { foreach ($lines as $line) { @@ -29,9 +27,6 @@ class TextWriter extends AbstractTextWriter $line ); } - - // debug - imagepolygon($frame->getCore(), $boundingBox->toArray(), 0); } else { foreach ($lines as $line) { imagestring( @@ -42,7 +37,6 @@ class TextWriter extends AbstractTextWriter $line, $this->font->getColor()->toInt() ); - imagepolygon($frame->getCore(), $boundingBox->toArray(), 0); } } } @@ -50,7 +44,7 @@ class TextWriter extends AbstractTextWriter return $image; } - private function getFont(): Font + protected function getFont(): FontInterface { if (!is_a($this->font, Font::class)) { throw new FontException('Font is not compatible to current driver.'); diff --git a/src/Typography/TextBlock.php b/src/Typography/TextBlock.php index b6174b1a..2430f269 100644 --- a/src/Typography/TextBlock.php +++ b/src/Typography/TextBlock.php @@ -17,37 +17,6 @@ class TextBlock extends Collection } } - /** - * Set position of each line in text block - * according to given font settings. - * - * @param FontInterface $font - * @param Point $pivot - * @return TextBlock - */ - public function alignByFont(FontInterface $font, Point $pivot = null): self - { - $pivot = $pivot ? $pivot : new Point(); - - $leading = $font->leadingInPixels(); - $x = $pivot->getX(); - $y = $font->hasFilename() ? $pivot->getY() + $font->capHeight() : $pivot->getY(); - - $x_adjustment = 0; - $total_width = $this->longestLine()->widthInFont($font); - foreach ($this as $line) { - $x_adjustment = $font->getAlign() == 'left' ? 0 : $total_width - $line->widthInFont($font); - $x_adjustment = $font->getAlign() == 'right' ? intval(round($x_adjustment)) : $x_adjustment; - $x_adjustment = $font->getAlign() == 'center' ? intval(round($x_adjustment / 2)) : $x_adjustment; - $position = new Point($x + $x_adjustment, $y); - $position->rotate($font->getAngle(), $pivot); - $line->setPosition($position); - $y += $leading; - } - - return $this; - } - public function getBoundingBox(FontInterface $font, Point $pivot = null): Polygon { $pivot = $pivot ? $pivot : new Point(); From b7371cf9fbe46a940341ad4c33a137cc696515b2 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Mon, 4 Jul 2022 19:11:25 +0200 Subject: [PATCH 187/476] Implement multiline TextWriter for Imagick driver --- src/Drivers/Abstract/AbstractFont.php | 19 +++++-- src/Drivers/Gd/Font.php | 15 ----- src/Drivers/Imagick/Font.php | 32 +++-------- src/Drivers/Imagick/Modifiers/TextWriter.php | 60 ++++++++++---------- tests/Typography/TextBlockTest.php | 43 +------------- 5 files changed, 54 insertions(+), 115 deletions(-) diff --git a/src/Drivers/Abstract/AbstractFont.php b/src/Drivers/Abstract/AbstractFont.php index 2b505952..6f2530a3 100644 --- a/src/Drivers/Abstract/AbstractFont.php +++ b/src/Drivers/Abstract/AbstractFont.php @@ -2,13 +2,9 @@ namespace Intervention\Image\Drivers\Abstract; -use Intervention\Image\Geometry\Point; -use Intervention\Image\Geometry\Polygon; -use Intervention\Image\Geometry\Size; use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\FontInterface; use Intervention\Image\Traits\CanHandleInput; -use Intervention\Image\Typography\TextBlock; abstract class AbstractFont implements FontInterface { @@ -117,4 +113,19 @@ abstract class AbstractFont implements FontInterface { return $this->lineHeight; } + + public function leadingInPixels(): int + { + return intval(round($this->fontSizeInPixels() * $this->getLineHeight())); + } + + public function capHeight(): int + { + return $this->getBoxSize('T')->height(); + } + + public function fontSizeInPixels(): int + { + return $this->getBoxSize('Hy')->height(); + } } diff --git a/src/Drivers/Gd/Font.php b/src/Drivers/Gd/Font.php index 79ee8f81..7fb63f51 100644 --- a/src/Drivers/Gd/Font.php +++ b/src/Drivers/Gd/Font.php @@ -84,19 +84,4 @@ class Font extends AbstractFont return 8; } } - - public function capHeight(): int - { - return $this->getBoxSize('T')->height(); - } - - public function leadingInPixels(): int - { - return intval(round($this->fontSizeInPixels() * $this->getLineHeight())); - } - - public function fontSizeInPixels(): int - { - return $this->getBoxSize('Hy')->height(); - } } diff --git a/src/Drivers/Imagick/Font.php b/src/Drivers/Imagick/Font.php index 2d46a71b..3ce76d17 100644 --- a/src/Drivers/Imagick/Font.php +++ b/src/Drivers/Imagick/Font.php @@ -24,7 +24,7 @@ class Font extends AbstractFont $draw->setFont($this->getFilename()); $draw->setFontSize($this->getSize()); $draw->setFillColor($this->getColor()->getPixel()); - $draw->setTextAlignment($this->getImagickAlign()); + $draw->setTextAlignment(Imagick::ALIGN_LEFT); return $draw; } @@ -40,24 +40,6 @@ class Font extends AbstractFont return $color; } - public function getAngle(): float - { - return parent::getAngle() * (-1); - } - - public function getImagickAlign(): int - { - switch (strtolower($this->getAlign())) { - case 'center': - return Imagick::ALIGN_CENTER; - - case 'right': - return Imagick::ALIGN_RIGHT; - } - - return Imagick::ALIGN_LEFT; - } - /** * Calculate box size of current font * @@ -70,14 +52,14 @@ class Font extends AbstractFont return (new Size(0, 0))->toPolygon(); } - $dimensions = (new Imagick())->queryFontMetrics( - $this->toImagickDraw(), - $text - ); + $draw = $this->toImagickDraw(); + $draw->setStrokeAntialias(true); + $draw->setTextAntialias(true); + $dimensions = (new Imagick())->queryFontMetrics($draw, $text); return (new Size( - intval(round(abs($dimensions['boundingBox']['x1'] - $dimensions['boundingBox']['x2']))), - intval(round(abs($dimensions['boundingBox']['y1'] - $dimensions['boundingBox']['y2']))), + intval(round($dimensions['textWidth'])), + intval(round($dimensions['textHeight'])), ))->toPolygon(); } } diff --git a/src/Drivers/Imagick/Modifiers/TextWriter.php b/src/Drivers/Imagick/Modifiers/TextWriter.php index a85fd8b4..3887225b 100644 --- a/src/Drivers/Imagick/Modifiers/TextWriter.php +++ b/src/Drivers/Imagick/Modifiers/TextWriter.php @@ -5,48 +5,50 @@ namespace Intervention\Image\Drivers\Imagick\Modifiers; use Intervention\Image\Drivers\Abstract\AbstractTextWriter; use Intervention\Image\Drivers\Imagick\Font; use Intervention\Image\Exceptions\FontException; -use Intervention\Image\Geometry\Point; +use Intervention\Image\Interfaces\FontInterface; use Intervention\Image\Interfaces\ImageInterface; class TextWriter extends AbstractTextWriter { public function apply(ImageInterface $image): ImageInterface { - $position = $this->getAlignedPosition(); + $lines = $this->getAlignedTextBlock(); foreach ($image as $frame) { - $frame->getCore()->annotateImage( - $this->getFont()->toImagickDraw(), - $position->getX(), - $position->getY(), - $this->getFont()->getAngle() * (-1), - $this->text - ); + foreach ($lines as $line) { + $frame->getCore()->annotateImage( + $this->getFont()->toImagickDraw(), + $line->getPosition()->getX(), + $line->getPosition()->getY(), + $this->getFont()->getAngle(), + $line + ); + } } return $image; } - protected function getAlignedPosition(): Point - { - $position = $this->position; - $box = $this->getFont()->getBoxSize($this->text); + // protected function getAlignedPosition(): Point + // { + // $position = $this->position; + // $box = $this->getFont()->getBoxSize($this->text); + // + // // adjust y pos + // switch ($this->getFont()->getValign()) { + // case 'top': + // $position->setY($position->getY() + $box->height()); + // break; + // + // case 'middle': + // case 'center': + // $position->setY(intval($position->getY() + round($box->height() / 2))); + // break; + // } + // + // return $position; + // } - // adjust y pos - switch ($this->getFont()->getValign()) { - case 'top': - $position->setY($position->getY() + $box->height()); - break; - - case 'middle': - case 'center': - $position->setY(intval($position->getY() + round($box->height() / 2))); - break; - } - - return $position; - } - - private function getFont(): Font + protected function getFont(): FontInterface { if (!is_a($this->font, Font::class)) { throw new FontException('Font is not compatible to current driver.'); diff --git a/tests/Typography/TextBlockTest.php b/tests/Typography/TextBlockTest.php index 36fb2779..a97f95a0 100644 --- a/tests/Typography/TextBlockTest.php +++ b/tests/Typography/TextBlockTest.php @@ -2,12 +2,9 @@ namespace Intervention\Image\Tests\Typography; +use Intervention\Image\Geometry\Point; use Intervention\Image\Tests\TestCase; use Intervention\Image\Typography\TextBlock; -use Intervention\Image\Drivers\Abstract\AbstractFont; -use Intervention\Image\Geometry\Point; -use Intervention\Image\Geometry\Polygon; -use Mockery; class TextBlockTest extends TestCase { @@ -40,42 +37,4 @@ class TextBlockTest extends TestCase $this->assertEquals('FooBar', $block->getLine(1)); $this->assertEquals('bar', $block->getLine(2)); } - - public function testAlignByFont(): void - { - $font = Mockery::mock(AbstractFont::class) - ->shouldAllowMockingProtectedMethods() - ->makePartial(); - - $font->shouldReceive('getBoxSize')->andReturn( - new Polygon([ - new Point(-1, -29), - new Point(141, -29), - new Point(141, 98), - new Point(-1, 98), - ]) - ); - - // $font->shouldReceive('capHeight')->andReturn(22); - - $font->shouldReceive('leadingInPixels')->andReturn(74); - $font->angle(45); - - $block = $this->getTestBlock(); // before - $this->assertEquals(0, $block->getLine(0)->getPosition()->getX()); - $this->assertEquals(0, $block->getLine(0)->getPosition()->getY()); - $this->assertEquals(0, $block->getLine(1)->getPosition()->getX()); - $this->assertEquals(0, $block->getLine(1)->getPosition()->getY()); - $this->assertEquals(0, $block->getLine(2)->getPosition()->getX()); - $this->assertEquals(0, $block->getLine(2)->getPosition()->getY()); - - $result = $block->alignByFont($font); // after - $this->assertInstanceOf(TextBlock::class, $result); - $this->assertEquals(0, $block->getLine(0)->getPosition()->getX()); - $this->assertEquals(0, $block->getLine(0)->getPosition()->getY()); - $this->assertEquals(-52, $block->getLine(1)->getPosition()->getX()); - $this->assertEquals(52, $block->getLine(1)->getPosition()->getY()); - $this->assertEquals(-104, $block->getLine(2)->getPosition()->getX()); - $this->assertEquals(104, $block->getLine(2)->getPosition()->getY()); - } } From d1ce4a3a83ef62cd856b5ff69076ac53ee3fef85 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Mon, 4 Jul 2022 19:42:22 +0200 Subject: [PATCH 188/476] Add test for TextBlock::getBoundingBox() --- tests/Typography/TextBlockTest.php | 39 ++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/tests/Typography/TextBlockTest.php b/tests/Typography/TextBlockTest.php index a97f95a0..6f95d212 100644 --- a/tests/Typography/TextBlockTest.php +++ b/tests/Typography/TextBlockTest.php @@ -3,8 +3,11 @@ namespace Intervention\Image\Tests\Typography; use Intervention\Image\Geometry\Point; +use Intervention\Image\Geometry\Polygon; +use Intervention\Image\Interfaces\FontInterface; use Intervention\Image\Tests\TestCase; use Intervention\Image\Typography\TextBlock; +use Mockery; class TextBlockTest extends TestCase { @@ -37,4 +40,40 @@ class TextBlockTest extends TestCase $this->assertEquals('FooBar', $block->getLine(1)); $this->assertEquals('bar', $block->getLine(2)); } + + public function testLongestLine(): void + { + $block = $this->getTestBlock(); + $result = $block->longestLine(); + $this->assertEquals('FooBar', (string) $result); + } + + public function testGetBoundingBox(): void + { + $block = $this->getTestBlock(); + $font = Mockery::mock(FontInterface::class) + ->shouldAllowMockingProtectedMethods() + ->makePartial(); + + $font->shouldReceive('getBoxSize')->andReturn( + new Polygon([ + new Point(0, 0), + new Point(300, 0), + new Point(300, 150), + new Point(0, 150), + ]) + ); + + $font->shouldReceive('leadingInPixels')->andReturn(30); + $font->shouldReceive('getAlign')->andReturn('left'); + $font->shouldReceive('getValign')->andReturn('bottom'); + $font->shouldReceive('getAngle')->andReturn(0); + $font->shouldReceive('capHeight')->andReturn(22); + + $box = $block->getBoundingBox($font, new Point(10, 15)); + $this->assertEquals(300, $box->width()); + $this->assertEquals(82, $box->height()); + $this->assertEquals(10, $box->getPivotPoint()->getX()); + $this->assertEquals(15, $box->getPivotPoint()->getY()); + } } From bb6d81b96f24bef53f7d714067fe453155a3167e Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Mon, 4 Jul 2022 20:01:20 +0200 Subject: [PATCH 189/476] Fix Imagick versions in test workflow --- .github/workflows/run-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 7fcdf254..dd737c22 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -9,7 +9,7 @@ jobs: fail-fast: false matrix: php: [ '8.0', '8.1' ] - imagemagick: [ '6.9.12-43', '7.1.0-28' ] + imagemagick: [ '6.9.12-55', '7.1.0-40' ] imagick: [ '3.7.0' ] stability: [ prefer-lowest, prefer-stable ] From 58d17f044f028bb90bffc72622c4a31ca41ebc4e Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Tue, 5 Jul 2022 14:53:30 +0200 Subject: [PATCH 190/476] Change box size calculation for Imagick font --- src/Drivers/Gd/Modifiers/TextWriter.php | 5 +++ src/Drivers/Imagick/Font.php | 2 +- src/Drivers/Imagick/Modifiers/TextWriter.php | 35 +++++++++----------- 3 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/Drivers/Gd/Modifiers/TextWriter.php b/src/Drivers/Gd/Modifiers/TextWriter.php index 70ebae21..8a404b44 100644 --- a/src/Drivers/Gd/Modifiers/TextWriter.php +++ b/src/Drivers/Gd/Modifiers/TextWriter.php @@ -27,6 +27,11 @@ class TextWriter extends AbstractTextWriter $line ); } + + // debug + // $lines = new TextBlock($this->text); + // $box = $lines->getBoundingBox($this->font, $this->position); + // imagepolygon($frame->getCore(), $box->toArray(), 0); } else { foreach ($lines as $line) { imagestring( diff --git a/src/Drivers/Imagick/Font.php b/src/Drivers/Imagick/Font.php index 3ce76d17..8325a880 100644 --- a/src/Drivers/Imagick/Font.php +++ b/src/Drivers/Imagick/Font.php @@ -59,7 +59,7 @@ class Font extends AbstractFont return (new Size( intval(round($dimensions['textWidth'])), - intval(round($dimensions['textHeight'])), + intval(round($dimensions['ascender'] + $dimensions['descender'])), ))->toPolygon(); } } diff --git a/src/Drivers/Imagick/Modifiers/TextWriter.php b/src/Drivers/Imagick/Modifiers/TextWriter.php index 3887225b..b0e1572c 100644 --- a/src/Drivers/Imagick/Modifiers/TextWriter.php +++ b/src/Drivers/Imagick/Modifiers/TextWriter.php @@ -23,31 +23,26 @@ class TextWriter extends AbstractTextWriter $line ); } + + // debug + // $lines = new TextBlock($this->text); + // $box = $lines->getBoundingBox($this->font, $this->position); + // $points = []; + // foreach (array_chunk($box->toArray(), 2) as $p) { + // $points[] = ['x' => $p[0], 'y' => $p[1]]; + // } + // $draw = new \ImagickDraw(); + // $draw->setStrokeOpacity(1); + // $draw->setStrokeColor('black'); + // $draw->setFillColor('transparent'); + // $draw->polygon($points); + // $frame->getCore()->drawImage($draw); + } return $image; } - // protected function getAlignedPosition(): Point - // { - // $position = $this->position; - // $box = $this->getFont()->getBoxSize($this->text); - // - // // adjust y pos - // switch ($this->getFont()->getValign()) { - // case 'top': - // $position->setY($position->getY() + $box->height()); - // break; - // - // case 'middle': - // case 'center': - // $position->setY(intval($position->getY() + round($box->height() / 2))); - // break; - // } - // - // return $position; - // } - protected function getFont(): FontInterface { if (!is_a($this->font, Font::class)) { From eba3948749abc2fc2c7f034cf7a069f0ca80684b Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Tue, 5 Jul 2022 19:34:35 +0200 Subject: [PATCH 191/476] Change test workflow to run only stable versions --- .github/workflows/run-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index dd737c22..08d5866e 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -11,7 +11,7 @@ jobs: php: [ '8.0', '8.1' ] imagemagick: [ '6.9.12-55', '7.1.0-40' ] imagick: [ '3.7.0' ] - stability: [ prefer-lowest, prefer-stable ] + stability: [ prefer-stable ] name: P${{ matrix.php }} - ${{ matrix.stability }} From 55e9f5e9ee70984ce10abbe049afc1ea84d4ec7d Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Tue, 5 Jul 2022 19:35:01 +0200 Subject: [PATCH 192/476] Fix Imagick URI in test workflow --- .github/workflows/run-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 08d5866e..89c1edab 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -50,7 +50,7 @@ jobs: - name: Install ImageMagick if: ( steps.cache-imagemagick.outputs.cache-hit != 'true' || steps.cache-imagemagick-exists.outputs.files_exists != 'true' ) run: | - curl -o /tmp/ImageMagick.tar.gz -sL https://download.imagemagick.org/ImageMagick/download/releases/ImageMagick-${{ matrix.imagemagick }}.tar.gz + curl -o /tmp/ImageMagick.tar.gz -sL https://imagemagick.org/archive/ImageMagick-${{ matrix.imagemagick }}.tar.gz ( cd /tmp || exit 1 tar xf ImageMagick.tar.gz From 269c0bdfe6f0c59babdc9f279c250849498c0c80 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Tue, 5 Jul 2022 19:43:18 +0200 Subject: [PATCH 193/476] Change action name in testing workflow --- .github/workflows/run-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 89c1edab..a5ca2409 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -13,7 +13,7 @@ jobs: imagick: [ '3.7.0' ] stability: [ prefer-stable ] - name: P${{ matrix.php }} - ${{ matrix.stability }} + name: PHP ${{ matrix.php }} - ${{ matrix.stability }} - ImageMaguck ${{ matrix.imagemagick }} steps: - name: Checkout project From 9f773b8d49559d773466b5a4c661cbac54e9e117 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Tue, 5 Jul 2022 19:44:06 +0200 Subject: [PATCH 194/476] Fix typo --- .github/workflows/run-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index a5ca2409..ae9bec45 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -13,7 +13,7 @@ jobs: imagick: [ '3.7.0' ] stability: [ prefer-stable ] - name: PHP ${{ matrix.php }} - ${{ matrix.stability }} - ImageMaguck ${{ matrix.imagemagick }} + name: PHP ${{ matrix.php }} - ${{ matrix.stability }} - ImageMagick ${{ matrix.imagemagick }} steps: - name: Checkout project From b80b0976150b1b54bb85225a09a3f5d20d392225 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Tue, 5 Jul 2022 19:54:11 +0200 Subject: [PATCH 195/476] Change workflow status badge --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 11662516..e6742540 100644 --- a/readme.md +++ b/readme.md @@ -2,7 +2,7 @@ ## PHP Image Manipulation [![Latest Version](https://img.shields.io/packagist/v/intervention/image.svg)](https://packagist.org/packages/intervention/image) -[![Build Status](https://travis-ci.org/Intervention/image.png?branch=master)](https://travis-ci.org/Intervention/image) +[![Build Status](https://github.com/Intervention/image/actions/workflows/run-tests.yml/badge.svg)](https://github.com/Intervention/image/actions) [![Monthly Downloads](https://img.shields.io/packagist/dm/intervention/image.svg)](https://packagist.org/packages/intervention/image/stats) Intervention Image is a **PHP image handling and manipulation** library providing an easier and expressive way to create, edit, and compose images. From 6092ec113ffe52eba904cee0cff05cb2da2fc636 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Thu, 7 Jul 2022 16:39:27 +0200 Subject: [PATCH 196/476] Implement DrawPixelModifier --- src/Drivers/Abstract/AbstractImage.php | 8 ++++ .../Gd/Modifiers/DrawPixelModifier.php | 30 ++++++++++++++ .../Imagick/Modifiers/DrawPixelModifier.php | 41 +++++++++++++++++++ src/Interfaces/ImageInterface.php | 1 + .../Gd/Modifiers/DrawPixelModifierTest.php | 26 ++++++++++++ .../Modifiers/DrawPixelModifierTest.php | 27 ++++++++++++ 6 files changed, 133 insertions(+) create mode 100644 src/Drivers/Gd/Modifiers/DrawPixelModifier.php create mode 100644 src/Drivers/Imagick/Modifiers/DrawPixelModifier.php create mode 100644 tests/Drivers/Gd/Modifiers/DrawPixelModifierTest.php create mode 100644 tests/Drivers/Imagick/Modifiers/DrawPixelModifierTest.php diff --git a/src/Drivers/Abstract/AbstractImage.php b/src/Drivers/Abstract/AbstractImage.php index 2136c3bd..9b71d778 100644 --- a/src/Drivers/Abstract/AbstractImage.php +++ b/src/Drivers/Abstract/AbstractImage.php @@ -200,6 +200,14 @@ abstract class AbstractImage implements ImageInterface return $this->modify($modifier); } + public function drawPixel(int $x, int $y, $color = null): ImageInterface + { + $color = $this->handleInput($color); + $modifier = $this->resolveDriverClass('Modifiers\DrawPixelModifier', new Point($x, $y), $color); + + return $this->modify($modifier); + } + public function resize(?int $width = null, ?int $height = null): ImageInterface { return $this->modify( diff --git a/src/Drivers/Gd/Modifiers/DrawPixelModifier.php b/src/Drivers/Gd/Modifiers/DrawPixelModifier.php new file mode 100644 index 00000000..3f44a23b --- /dev/null +++ b/src/Drivers/Gd/Modifiers/DrawPixelModifier.php @@ -0,0 +1,30 @@ +getCore(), + $this->position->getX(), + $this->position->getY(), + $this->color->toInt() + ); + } + + return $image; + } +} diff --git a/src/Drivers/Imagick/Modifiers/DrawPixelModifier.php b/src/Drivers/Imagick/Modifiers/DrawPixelModifier.php new file mode 100644 index 00000000..00f34ba4 --- /dev/null +++ b/src/Drivers/Imagick/Modifiers/DrawPixelModifier.php @@ -0,0 +1,41 @@ +setFillColor($this->getColor()->getPixel()); + $pixel->point($this->position->getX(), $this->position->getY()); + + foreach ($image as $frame) { + $frame->getCore()->drawImage($pixel); + } + + return $image; + } + + public function getColor(): Color + { + if (!is_a($this->color, Color::class)) { + throw new DecoderException('Unable to decode given pixel color.'); + } + + return $this->color; + } +} diff --git a/src/Interfaces/ImageInterface.php b/src/Interfaces/ImageInterface.php index f69a9825..2ba694d6 100644 --- a/src/Interfaces/ImageInterface.php +++ b/src/Interfaces/ImageInterface.php @@ -36,6 +36,7 @@ interface ImageInterface extends Traversable, Countable public function fitDown(int $width, int $height, string $position = 'center'): ImageInterface; public function pad(int $width, int $height, $background = 'ffffff', string $position = 'center'): ImageInterface; public function padDown(int $width, int $height, $background = 'ffffff', string $position = 'center'): ImageInterface; + public function drawPixel(int $x, int $y, $color = null): ImageInterface; public function getWidth(): int; public function getHeight(): int; public function destroy(): void; diff --git a/tests/Drivers/Gd/Modifiers/DrawPixelModifierTest.php b/tests/Drivers/Gd/Modifiers/DrawPixelModifierTest.php new file mode 100644 index 00000000..0b552116 --- /dev/null +++ b/tests/Drivers/Gd/Modifiers/DrawPixelModifierTest.php @@ -0,0 +1,26 @@ +createTestImage('trim.png'); + $this->assertEquals('00aef0', $image->pickColor(14, 14)->toHex()); + $image->modify(new DrawPixelModifier(new Point(14, 14), new Color(16777215))); + $this->assertEquals('ffffff', $image->pickColor(14, 14)->toHex()); + } +} diff --git a/tests/Drivers/Imagick/Modifiers/DrawPixelModifierTest.php b/tests/Drivers/Imagick/Modifiers/DrawPixelModifierTest.php new file mode 100644 index 00000000..c0f3cf54 --- /dev/null +++ b/tests/Drivers/Imagick/Modifiers/DrawPixelModifierTest.php @@ -0,0 +1,27 @@ +createTestImage('trim.png'); + $this->assertEquals('00aef0', $image->pickColor(14, 14)->toHex()); + $image->modify(new DrawPixelModifier(new Point(14, 14), new Color(new ImagickPixel('#ffffff')))); + $this->assertEquals('ffffff', $image->pickColor(14, 14)->toHex()); + } +} From a24240acffc8e37f6b620fd88c7c34915702b00a Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Fri, 8 Jul 2022 17:42:50 +0200 Subject: [PATCH 197/476] Add text method to ImageInterface --- src/Interfaces/ImageInterface.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Interfaces/ImageInterface.php b/src/Interfaces/ImageInterface.php index 2ba694d6..4c1c9a5e 100644 --- a/src/Interfaces/ImageInterface.php +++ b/src/Interfaces/ImageInterface.php @@ -21,6 +21,7 @@ interface ImageInterface extends Traversable, Countable public function toGif(): EncodedImage; public function toPng(): EncodedImage; public function pickColors(int $x, int $y): CollectionInterface; + public function text(string $text, int $x, int $y, ?callable $init = null): ImageInterface; public function pickColor(int $x, int $y, int $frame_key = 0): ?ColorInterface; public function greyscale(): ImageInterface; public function blur(int $amount = 5): ImageInterface; From 347942f14ce6cae0c89449421d15bced132e5e14 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 9 Jul 2022 16:15:46 +0200 Subject: [PATCH 198/476] Implement HTML color name decoder --- .../Gd/Decoders/HtmlColorNameDecoder.php | 28 ++++ src/Drivers/Gd/InputHandler.php | 16 +- .../Imagick/Decoders/HtmlColorNameDecoder.php | 28 ++++ src/Drivers/Imagick/InputHandler.php | 14 +- src/Traits/CanReadHtmlColorNames.php | 157 ++++++++++++++++++ .../Gd/Decoders/HtmlColorNameDecoderTest.php | 18 ++ .../Decoders/HtmlColorNameDecoderTest.php | 18 ++ 7 files changed, 266 insertions(+), 13 deletions(-) create mode 100644 src/Drivers/Gd/Decoders/HtmlColorNameDecoder.php create mode 100644 src/Drivers/Imagick/Decoders/HtmlColorNameDecoder.php create mode 100644 src/Traits/CanReadHtmlColorNames.php create mode 100644 tests/Drivers/Gd/Decoders/HtmlColorNameDecoderTest.php create mode 100644 tests/Drivers/Imagick/Decoders/HtmlColorNameDecoderTest.php diff --git a/src/Drivers/Gd/Decoders/HtmlColorNameDecoder.php b/src/Drivers/Gd/Decoders/HtmlColorNameDecoder.php new file mode 100644 index 00000000..59ed6fd2 --- /dev/null +++ b/src/Drivers/Gd/Decoders/HtmlColorNameDecoder.php @@ -0,0 +1,28 @@ +hexColorFromColorName($input); + + if (empty($hexcolor)) { + throw new DecoderException('Unable to decode input'); + } + + return parent::decode($hexcolor); + } +} diff --git a/src/Drivers/Gd/InputHandler.php b/src/Drivers/Gd/InputHandler.php index 3b66efe2..f32612e6 100644 --- a/src/Drivers/Gd/InputHandler.php +++ b/src/Drivers/Gd/InputHandler.php @@ -11,13 +11,15 @@ class InputHandler extends AbstractInputHandler { return new Decoders\ImageObjectDecoder( new Decoders\ArrayColorDecoder( - new Decoders\RgbStringColorDecoder( - new Decoders\HexColorDecoder( - new Decoders\TransparentColorDecoder( - new Decoders\FilePathImageDecoder( - new Decoders\BinaryImageDecoder( - new Decoders\DataUriImageDecoder( - new Decoders\Base64ImageDecoder() + new Decoders\HtmlColorNameDecoder( + new Decoders\RgbStringColorDecoder( + new Decoders\HexColorDecoder( + new Decoders\TransparentColorDecoder( + new Decoders\FilePathImageDecoder( + new Decoders\BinaryImageDecoder( + new Decoders\DataUriImageDecoder( + new Decoders\Base64ImageDecoder() + ) ) ) ) diff --git a/src/Drivers/Imagick/Decoders/HtmlColorNameDecoder.php b/src/Drivers/Imagick/Decoders/HtmlColorNameDecoder.php new file mode 100644 index 00000000..fba912ea --- /dev/null +++ b/src/Drivers/Imagick/Decoders/HtmlColorNameDecoder.php @@ -0,0 +1,28 @@ +hexColorFromColorName($input); + + if (empty($hexcolor)) { + throw new DecoderException('Unable to decode input'); + } + + return parent::decode($hexcolor); + } +} diff --git a/src/Drivers/Imagick/InputHandler.php b/src/Drivers/Imagick/InputHandler.php index 839d401c..90577ff1 100644 --- a/src/Drivers/Imagick/InputHandler.php +++ b/src/Drivers/Imagick/InputHandler.php @@ -12,12 +12,14 @@ class InputHandler extends AbstractInputHandler return new Decoders\ImageObjectDecoder( new Decoders\ArrayColorDecoder( new Decoders\HexColorDecoder( - new Decoders\RgbStringColorDecoder( - new Decoders\TransparentColorDecoder( - new Decoders\FilePathImageDecoder( - new Decoders\BinaryImageDecoder( - new Decoders\DataUriImageDecoder( - new Decoders\Base64ImageDecoder() + new Decoders\HtmlColorNameDecoder( + new Decoders\RgbStringColorDecoder( + new Decoders\TransparentColorDecoder( + new Decoders\FilePathImageDecoder( + new Decoders\BinaryImageDecoder( + new Decoders\DataUriImageDecoder( + new Decoders\Base64ImageDecoder() + ) ) ) ) diff --git a/src/Traits/CanReadHtmlColorNames.php b/src/Traits/CanReadHtmlColorNames.php new file mode 100644 index 00000000..be5dc675 --- /dev/null +++ b/src/Traits/CanReadHtmlColorNames.php @@ -0,0 +1,157 @@ + '#FFA07A', + 'salmon' => '#FA8072', + 'darksalmon' => '#E9967A', + 'lightcoral' => '#F08080', + 'indianred' => '#CD5C5C', + 'crimson' => '#DC143C', + 'firebrick' => '#B22222', + 'red' => '#FF0000', + 'darkred' => '#8B0000', + 'coral' => '#FF7F50', + 'tomato' => '#FF6347', + 'orangered' => '#FF4500', + 'gold' => '#FFD700', + 'orange' => '#FFA500', + 'darkorange' => '#FF8C00', + 'lightyellow' => '#FFFFE0', + 'lemonchiffon' => '#FFFACD', + 'lightgoldenrodyellow' => '#FAFAD2', + 'papayawhip' => '#FFEFD5', + 'moccasin' => '#FFE4B5', + 'peachpuff' => '#FFDAB9', + 'palegoldenrod' => '#EEE8AA', + 'khaki' => '#F0E68C', + 'darkkhaki' => '#BDB76B', + 'yellow' => '#FFFF00', + 'lawngreen' => '#7CFC00', + 'chartreuse' => '#7FFF00', + 'limegreen' => '#32CD32', + 'lime' => '#00FF00', + 'forestgreen' => '#228B22', + 'green' => '#008000', + 'darkgreen' => '#006400', + 'greenyellow' => '#ADFF2F', + 'yellowgreen' => '#9ACD32', + 'springgreen' => '#00FF7F', + 'mediumspringgreen' => '#00FA9A', + 'lightgreen' => '#90EE90', + 'palegreen' => '#98FB98', + 'darkseagreen' => '#8FBC8F', + 'mediumseagre' => 'en #3CB371', + 'seagreen' => '#2E8B57', + 'olive' => '#808000', + 'darkolivegreen' => '#556B2F', + 'olivedrab' => '#6B8E23', + 'lightcyan' => '#E0FFFF', + 'cyan' => '#00FFFF', + 'aqua' => '#00FFFF', + 'aquamarine' => '#7FFFD4', + 'mediumaquamarine' => '#66CDAA', + 'paleturquoise' => '#AFEEEE', + 'turquoise' => '#40E0D0', + 'mediumturquoise' => '#48D1CC', + 'darkturquoise' => '#00CED1', + 'lightseagreen' => '#20B2AA', + 'cadetblue' => '#5F9EA0', + 'darkcyan' => '#008B8B', + 'teal' => '#008080', + 'powderblue' => '#B0E0E6', + 'lightblue' => '#ADD8E6', + 'lightskyblue' => '#87CEFA', + 'skyblue' => '#87CEEB', + 'deepskyblue' => '#00BFFF', + 'lightsteelblue' => '#B0C4DE', + 'dodgerblue' => '#1E90FF', + 'cornflowerblue' => '#6495ED', + 'steelblue' => '#4682B4', + 'royalblue' => '#4169E1', + 'blue' => '#0000FF', + 'mediumblue' => '#0000CD', + 'darkblue' => '#00008B', + 'navy' => '#000080', + 'midnightblue' => '#191970', + 'mediumslateblue' => '#7B68EE', + 'slateblue' => '#6A5ACD', + 'darkslateblue' => '#483D8B', + 'lavender' => '#E6E6FA', + 'thistle' => '#D8BFD8', + 'plum' => '#DDA0DD', + 'violet' => '#EE82EE', + 'orchid' => '#DA70D6', + 'fuchsia' => '#FF00FF', + 'magenta' => '#FF00FF', + 'mediumorchid' => '#BA55D3', + 'mediumpurple' => '#9370DB', + 'blueviolet' => '#8A2BE2', + 'darkviolet' => '#9400D3', + 'darkorchid' => '#9932CC', + 'darkmagenta' => '#8B008B', + 'purple' => '#800080', + 'indigo' => '#4B0082', + 'pink' => '#FFC0CB', + 'lightpink' => '#FFB6C1', + 'hotpink' => '#FF69B4', + 'deeppink' => '#FF1493', + 'palevioletred' => '#DB7093', + 'mediumvioletred' => '#C71585', + 'white' => '#FFFFFF', + 'snow' => '#FFFAFA', + 'honeydew' => '#F0FFF0', + 'mintcream' => '#F5FFFA', + 'azure' => '#F0FFFF', + 'aliceblue' => '#F0F8FF', + 'ghostwhite' => '#F8F8FF', + 'whitesmoke' => '#F5F5F5', + 'seashell' => '#FFF5EE', + 'beige' => '#F5F5DC', + 'oldlace' => '#FDF5E6', + 'floralwhite' => '#FFFAF0', + 'ivory' => '#FFFFF0', + 'antiquewhite' => '#FAEBD7', + 'linen' => '#FAF0E6', + 'lavenderblush' => '#FFF0F5', + 'mistyrose' => '#FFE4E1', + 'gainsboro' => '#DCDCDC', + 'lightgray' => '#D3D3D3', + 'silver' => '#C0C0C0', + 'darkgray' => '#A9A9A9', + 'gray' => '#808080', + 'dimgray' => '#696969', + 'lightslategray' => '#778899', + 'slategray' => '#708090', + 'darkslategray' => '#2F4F4F', + 'black' => '#000000', + 'cornsilk' => '#FFF8DC', + 'blanchedalmond' => '#FFEBCD', + 'bisque' => '#FFE4C4', + 'navajowhite' => '#FFDEAD', + 'wheat' => '#F5DEB3', + 'burlywood' => '#DEB887', + 'tan' => '#D2B48C', + 'rosybrown' => '#BC8F8F', + 'sandybrown' => '#F4A460', + 'goldenrod' => '#DAA520', + 'peru' => '#CD853F', + 'chocolate' => '#D2691E', + 'saddlebrown' => '#8B4513', + 'sienna' => '#A0522D', + 'brown' => '#A52A2A', + 'maroon' => '#800000', + ]; + + public function hexColorFromColorName(string $name): ?string + { + if (!array_key_exists($name, $this->color_names)) { + return null; + } + + return $this->color_names[$name]; + } +} diff --git a/tests/Drivers/Gd/Decoders/HtmlColorNameDecoderTest.php b/tests/Drivers/Gd/Decoders/HtmlColorNameDecoderTest.php new file mode 100644 index 00000000..8b713599 --- /dev/null +++ b/tests/Drivers/Gd/Decoders/HtmlColorNameDecoderTest.php @@ -0,0 +1,18 @@ +decode('tomato'); + $this->assertInstanceOf(Color::class, $color); + $this->assertEquals('ff6347', $color->toHex()); + } +} diff --git a/tests/Drivers/Imagick/Decoders/HtmlColorNameDecoderTest.php b/tests/Drivers/Imagick/Decoders/HtmlColorNameDecoderTest.php new file mode 100644 index 00000000..4328f4b1 --- /dev/null +++ b/tests/Drivers/Imagick/Decoders/HtmlColorNameDecoderTest.php @@ -0,0 +1,18 @@ +decode('tomato'); + $this->assertInstanceOf(Color::class, $color); + $this->assertEquals('ff6347', $color->toHex()); + } +} From e457f3aeb19fff217f34a1a9cd87ac049a5a83ba Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 9 Jul 2022 16:19:33 +0200 Subject: [PATCH 199/476] Normalize decoding of html color names to lower case --- src/Traits/CanReadHtmlColorNames.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/Traits/CanReadHtmlColorNames.php b/src/Traits/CanReadHtmlColorNames.php index be5dc675..c02b4367 100644 --- a/src/Traits/CanReadHtmlColorNames.php +++ b/src/Traits/CanReadHtmlColorNames.php @@ -146,8 +146,16 @@ trait CanReadHtmlColorNames 'maroon' => '#800000', ]; + /** + * Transform given html color name to hex color + * or return null, if color name doesn't exist. + * + * @param string $name + * @return null|string + */ public function hexColorFromColorName(string $name): ?string { + $name = strtolower($name); if (!array_key_exists($name, $this->color_names)) { return null; } From a8db65287fe15b3ddcc8b20245cebc8f86b19e3a Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 9 Jul 2022 17:58:26 +0200 Subject: [PATCH 200/476] Edit readme.md --- readme.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/readme.md b/readme.md index e6742540..7704f23e 100644 --- a/readme.md +++ b/readme.md @@ -5,12 +5,12 @@ [![Build Status](https://github.com/Intervention/image/actions/workflows/run-tests.yml/badge.svg)](https://github.com/Intervention/image/actions) [![Monthly Downloads](https://img.shields.io/packagist/dm/intervention/image.svg)](https://packagist.org/packages/intervention/image/stats) -Intervention Image is a **PHP image handling and manipulation** library providing an easier and expressive way to create, edit, and compose images. +Intervention Image is a **image handling and manipulation library written in PHP** providing an easier and expressive way to create, edit, and compose images. GD library or Imagick can be selected as the base layer for all operations. -- Simple interface for -- Driver agnostic +- Simple interface for common tasks +- Interchangable driver architecture - Support for animated images -- Framework-agnostic, will work with any project +- Framework-agnostic - PSR-12 compliant ## Code Examples @@ -20,7 +20,7 @@ Intervention Image is a **PHP image handling and manipulation** library providin $manager = new ImageManager('gd') // open an image file -$image = $manager->make('images/example.jpg'); +$image = $manager->make('images/example.gif'); // resize image instance $image->resize(320, 240); @@ -52,10 +52,10 @@ composer require intervention/image ## Getting started -Learn the [basics](https://image.intervention.io/) on how to use Intervention Image and more with the [official documentation](https://image.intervention.io/). +Learn the [basics](https://image.intervention.io/v3/basics/instantiation/) on how to use Intervention Image and more with the [official documentation](https://image.intervention.io/v3/). ## License Intervention Image is licensed under the [MIT License](http://opensource.org/licenses/MIT). -Copyright 2021 Oliver Vogel +Copyright 2022 Oliver Vogel From e79c5911b10ee3e70f0d30ef6e5d419a9f9bc1fa Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Wed, 13 Jul 2022 16:58:08 +0000 Subject: [PATCH 201/476] Move Geometry\Resizer to Geometry\Tools\Resizer --- src/Geometry/Size.php | 2 +- src/Geometry/{ => Tools}/Resizer.php | 2 +- src/Traits/CanResizeGeometrically.php | 2 +- tests/Geometry/ResizerTest.php | 3 +-- 4 files changed, 4 insertions(+), 5 deletions(-) rename src/Geometry/{ => Tools}/Resizer.php (99%) diff --git a/src/Geometry/Size.php b/src/Geometry/Size.php index 07c15d06..3dbf67e4 100644 --- a/src/Geometry/Size.php +++ b/src/Geometry/Size.php @@ -2,7 +2,7 @@ namespace Intervention\Image\Geometry; -use Intervention\Image\Geometry\Resizer; +use Intervention\Image\Geometry\Tools\Resizer; use Intervention\Image\Interfaces\PointInterface; use Intervention\Image\Interfaces\SizeInterface; diff --git a/src/Geometry/Resizer.php b/src/Geometry/Tools/Resizer.php similarity index 99% rename from src/Geometry/Resizer.php rename to src/Geometry/Tools/Resizer.php index 6a5c0edd..d295ead9 100644 --- a/src/Geometry/Resizer.php +++ b/src/Geometry/Tools/Resizer.php @@ -1,6 +1,6 @@ toSize(new Size(200, 100)); $this->assertInstanceOf(Resizer::class, $resizer); From 7ba98cfe5dab0c58229fb89766c247bb7ca1e57b Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Wed, 13 Jul 2022 19:08:02 +0200 Subject: [PATCH 202/476] Add Geometry\Pixel::class --- src/Geometry/Pixel.php | 28 ++++++++++++++++++++++++++++ tests/Geometry/PixelTest.php | 23 +++++++++++++++++++++++ 2 files changed, 51 insertions(+) create mode 100644 src/Geometry/Pixel.php create mode 100644 tests/Geometry/PixelTest.php diff --git a/src/Geometry/Pixel.php b/src/Geometry/Pixel.php new file mode 100644 index 00000000..e1672fdd --- /dev/null +++ b/src/Geometry/Pixel.php @@ -0,0 +1,28 @@ +background = $background; + + return $this; + } + + public function background(): ColorInterface + { + return $this->background; + } +} diff --git a/tests/Geometry/PixelTest.php b/tests/Geometry/PixelTest.php new file mode 100644 index 00000000..6719682a --- /dev/null +++ b/tests/Geometry/PixelTest.php @@ -0,0 +1,23 @@ +withBackground($color); + $this->assertInstanceOf(ColorInterface::class, $pixel->background()); + $this->assertInstanceOf(Pixel::class, $result); + } +} From d1313a420b9f8bed1d4d1e1660381fbf2f4e85c3 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Tue, 19 Jul 2022 15:01:00 +0000 Subject: [PATCH 203/476] Replace Size::class with Rectangle::class --- src/Drivers/Abstract/AbstractImage.php | 5 +- .../Modifiers/AbstractFitModifier.php | 8 +- .../Modifiers/AbstractPadModifier.php | 4 +- src/Drivers/Gd/Font.php | 10 +- src/Drivers/Gd/Frame.php | 4 +- src/Drivers/Gd/Modifiers/FillModifier.php | 4 +- src/Drivers/Gd/Modifiers/FitModifier.php | 20 +- src/Drivers/Gd/Modifiers/PadDownModifier.php | 6 +- src/Drivers/Gd/Modifiers/ResizeModifier.php | 20 +- src/Drivers/Imagick/Font.php | 8 +- src/Drivers/Imagick/Frame.php | 7 +- src/Drivers/Imagick/Image.php | 4 +- src/Drivers/Imagick/Modifiers/FitModifier.php | 12 +- .../Imagick/Modifiers/PadDownModifier.php | 6 +- .../Imagick/Modifiers/PixelateModifier.php | 7 +- .../Imagick/Modifiers/ResizeModifier.php | 5 +- src/Geometry/{Size.php => Rectangle.php} | 199 ++++------- src/Geometry/Tools/Resizer.php | 88 ++--- src/Interfaces/SizeInterface.php | 11 +- src/Typography/TextBlock.php | 6 +- tests/Drivers/Abstract/AbstractImageTest.php | 14 +- .../Modifiers/AbstractFitModifierTest.php | 16 +- .../Modifiers/AbstractPadModifierTest.php | 16 +- tests/Drivers/Gd/FrameTest.php | 10 +- tests/Drivers/Gd/ImageTest.php | 4 +- tests/Drivers/Imagick/FrameTest.php | 4 +- tests/Drivers/Imagick/ImageTest.php | 4 +- tests/Geometry/RectangleTest.php | 240 +++++++++++++ tests/Geometry/ResizerTest.php | 300 ++++++++-------- tests/Geometry/SizeTest.php | 332 ------------------ 30 files changed, 612 insertions(+), 762 deletions(-) rename src/Geometry/{Size.php => Rectangle.php} (58%) create mode 100644 tests/Geometry/RectangleTest.php delete mode 100644 tests/Geometry/SizeTest.php diff --git a/src/Drivers/Abstract/AbstractImage.php b/src/Drivers/Abstract/AbstractImage.php index 9b71d778..4521b84d 100644 --- a/src/Drivers/Abstract/AbstractImage.php +++ b/src/Drivers/Abstract/AbstractImage.php @@ -5,7 +5,7 @@ namespace Intervention\Image\Drivers\Abstract; use Intervention\Image\Collection; use Intervention\Image\EncodedImage; use Intervention\Image\Geometry\Point; -use Intervention\Image\Geometry\Size; +use Intervention\Image\Geometry\Rectangle; use Intervention\Image\Interfaces\CollectionInterface; use Intervention\Image\Interfaces\EncoderInterface; use Intervention\Image\Interfaces\ImageInterface; @@ -13,7 +13,6 @@ use Intervention\Image\Interfaces\ModifierInterface; use Intervention\Image\Interfaces\SizeInterface; use Intervention\Image\Traits\CanHandleInput; use Intervention\Image\Traits\CanResolveDriverClass; -use Intervention\Image\Interfaces\FontInterface; abstract class AbstractImage implements ImageInterface { @@ -22,7 +21,7 @@ abstract class AbstractImage implements ImageInterface public function getSize(): SizeInterface { - return new Size($this->getWidth(), $this->getHeight()); + return new Rectangle($this->getWidth(), $this->getHeight()); } public function size(): SizeInterface diff --git a/src/Drivers/Abstract/Modifiers/AbstractFitModifier.php b/src/Drivers/Abstract/Modifiers/AbstractFitModifier.php index fa9e6b1b..3c38bf8f 100644 --- a/src/Drivers/Abstract/Modifiers/AbstractFitModifier.php +++ b/src/Drivers/Abstract/Modifiers/AbstractFitModifier.php @@ -2,7 +2,7 @@ namespace Intervention\Image\Drivers\Abstract\Modifiers; -use Intervention\Image\Geometry\Size; +use Intervention\Image\Geometry\Rectangle; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\SizeInterface; @@ -20,10 +20,10 @@ abstract class AbstractFitModifier { $imagesize = $image->getSize(); - $crop = new Size($this->width, $this->height); + $crop = new Rectangle($this->width, $this->height); $crop = $crop->contain( - $imagesize->getWidth(), - $imagesize->getHeight() + $imagesize->width(), + $imagesize->height() )->alignPivotTo($imagesize, $this->position); return $crop; diff --git a/src/Drivers/Abstract/Modifiers/AbstractPadModifier.php b/src/Drivers/Abstract/Modifiers/AbstractPadModifier.php index 1cd4f1ec..521dab30 100644 --- a/src/Drivers/Abstract/Modifiers/AbstractPadModifier.php +++ b/src/Drivers/Abstract/Modifiers/AbstractPadModifier.php @@ -2,7 +2,7 @@ namespace Intervention\Image\Drivers\Abstract\Modifiers; -use Intervention\Image\Geometry\Size; +use Intervention\Image\Geometry\Rectangle; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\SizeInterface; @@ -26,6 +26,6 @@ abstract class AbstractPadModifier protected function getResizeSize(ImageInterface $image): SizeInterface { - return new Size($this->width, $this->height); + return new Rectangle($this->width, $this->height); } } diff --git a/src/Drivers/Gd/Font.php b/src/Drivers/Gd/Font.php index 7fb63f51..0371b962 100644 --- a/src/Drivers/Gd/Font.php +++ b/src/Drivers/Gd/Font.php @@ -5,7 +5,7 @@ namespace Intervention\Image\Drivers\Gd; use Intervention\Image\Drivers\Abstract\AbstractFont; use Intervention\Image\Geometry\Point; use Intervention\Image\Geometry\Polygon; -use Intervention\Image\Geometry\Size; +use Intervention\Image\Geometry\Rectangle; class Font extends AbstractFont { @@ -23,13 +23,13 @@ class Font extends AbstractFont { if (!$this->hasFilename()) { // calculate box size from gd font - $box = new Size(0, 0); + $box = new Rectangle(0, 0); $chars = mb_strlen($text); if ($chars > 0) { - $box->setWidth($chars * $this->getGdFontWidth()); - $box->setHeight($this->getGdFontHeight()); + $box->withWidth($chars * $this->getGdFontWidth()); + $box->withHeight($this->getGdFontHeight()); } - return $box->toPolygon(); + return $box; } // calculate box size from font file with angle 0 diff --git a/src/Drivers/Gd/Frame.php b/src/Drivers/Gd/Frame.php index 885934c4..b0e890d5 100644 --- a/src/Drivers/Gd/Frame.php +++ b/src/Drivers/Gd/Frame.php @@ -4,7 +4,7 @@ namespace Intervention\Image\Drivers\Gd; use GdImage; use Intervention\Image\Collection; -use Intervention\Image\Geometry\Size; +use Intervention\Image\Geometry\Rectangle; use Intervention\Image\Interfaces\FrameInterface; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\SizeInterface; @@ -40,7 +40,7 @@ class Frame implements FrameInterface public function getSize(): SizeInterface { - return new Size(imagesx($this->core), imagesy($this->core)); + return new Rectangle(imagesx($this->core), imagesy($this->core)); } public function getDelay(): float diff --git a/src/Drivers/Gd/Modifiers/FillModifier.php b/src/Drivers/Gd/Modifiers/FillModifier.php index 0f747342..65de556c 100644 --- a/src/Drivers/Gd/Modifiers/FillModifier.php +++ b/src/Drivers/Gd/Modifiers/FillModifier.php @@ -45,8 +45,8 @@ class FillModifier implements ModifierInterface $frame->getCore(), 0, 0, - $frame->getSize()->getWidth() - 1, - $frame->getSize()->getHeight() - 1, + $frame->getSize()->width() - 1, + $frame->getSize()->height() - 1, $this->color->toInt() ); } diff --git a/src/Drivers/Gd/Modifiers/FitModifier.php b/src/Drivers/Gd/Modifiers/FitModifier.php index cf55b8c0..9da6944f 100644 --- a/src/Drivers/Gd/Modifiers/FitModifier.php +++ b/src/Drivers/Gd/Modifiers/FitModifier.php @@ -26,8 +26,8 @@ class FitModifier extends AbstractFitModifier implements ModifierInterface { // create new image $modified = imagecreatetruecolor( - $resize->getWidth(), - $resize->getHeight() + $resize->width(), + $resize->height() ); // get current image @@ -51,20 +51,20 @@ class FitModifier extends AbstractFitModifier implements ModifierInterface $current, 0, 0, - $crop->getPivot()->getX(), - $crop->getPivot()->getY(), - $resize->getWidth(), - $resize->getHeight(), - $crop->getWidth(), - $crop->getHeight() + $crop->pivot()->getX(), + $crop->pivot()->getY(), + $resize->width(), + $resize->height(), + $crop->width(), + $crop->height() ); imagedestroy($current); if ($transIndex != -1) { // @todo refactor because of duplication imagecolortransparent($modified, $transIndex); - for ($y = 0; $y < $resize->getHeight(); ++$y) { - for ($x = 0; $x < $resize->getWidth(); ++$x) { + for ($y = 0; $y < $resize->height(); ++$y) { + for ($x = 0; $x < $resize->width(); ++$x) { if (((imagecolorat($modified, $x, $y) >> 24) & 0x7F) >= 100) { imagesetpixel( $modified, diff --git a/src/Drivers/Gd/Modifiers/PadDownModifier.php b/src/Drivers/Gd/Modifiers/PadDownModifier.php index cb80248d..53247d6c 100644 --- a/src/Drivers/Gd/Modifiers/PadDownModifier.php +++ b/src/Drivers/Gd/Modifiers/PadDownModifier.php @@ -2,7 +2,7 @@ namespace Intervention\Image\Drivers\Gd\Modifiers; -use Intervention\Image\Geometry\Size; +use Intervention\Image\Geometry\Rectangle; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\SizeInterface; @@ -13,13 +13,13 @@ class PadDownModifier extends PadModifier $resize = $this->getResizeSize($image); return $image->getSize() - ->contain($resize->getWidth(), $resize->getHeight()) + ->contain($resize->width(), $resize->height()) ->alignPivotTo($resize, $this->position); } protected function getResizeSize(ImageInterface $image): SizeInterface { - return (new Size($this->width, $this->height)) + return (new Rectangle($this->width, $this->height)) ->resizeDown($image->getWidth(), $image->getHeight()); } } diff --git a/src/Drivers/Gd/Modifiers/ResizeModifier.php b/src/Drivers/Gd/Modifiers/ResizeModifier.php index 512491f5..2b91e1c6 100644 --- a/src/Drivers/Gd/Modifiers/ResizeModifier.php +++ b/src/Drivers/Gd/Modifiers/ResizeModifier.php @@ -34,8 +34,8 @@ class ResizeModifier implements ModifierInterface { // create new image $modified = imagecreatetruecolor( - $resizeTo->getWidth(), - $resizeTo->getHeight() + $resizeTo->width(), + $resizeTo->height() ); // get current image @@ -57,22 +57,22 @@ class ResizeModifier implements ModifierInterface imagecopyresampled( $modified, $current, - $resizeTo->getPivot()->getX(), - $resizeTo->getPivot()->getY(), + $resizeTo->pivot()->getX(), + $resizeTo->pivot()->getY(), 0, 0, - $resizeTo->getWidth(), - $resizeTo->getHeight(), - $frame->getSize()->getWidth(), - $frame->getSize()->getHeight() + $resizeTo->width(), + $resizeTo->height(), + $frame->getSize()->width(), + $frame->getSize()->height() ); imagedestroy($current); if ($transIndex != -1) { // @todo refactor because of duplication imagecolortransparent($modified, $transIndex); - for ($y = 0; $y < $resizeTo->getHeight(); ++$y) { - for ($x = 0; $x < $resizeTo->getWidth(); ++$x) { + for ($y = 0; $y < $resizeTo->height(); ++$y) { + for ($x = 0; $x < $resizeTo->width(); ++$x) { if (((imagecolorat($modified, $x, $y) >> 24) & 0x7F) >= 100) { imagesetpixel( $modified, diff --git a/src/Drivers/Imagick/Font.php b/src/Drivers/Imagick/Font.php index 8325a880..8e2394af 100644 --- a/src/Drivers/Imagick/Font.php +++ b/src/Drivers/Imagick/Font.php @@ -7,7 +7,7 @@ use ImagickDraw; use Intervention\Image\Drivers\Abstract\AbstractFont; use Intervention\Image\Exceptions\FontException; use Intervention\Image\Geometry\Polygon; -use Intervention\Image\Geometry\Size; +use Intervention\Image\Geometry\Rectangle; use Intervention\Image\Interfaces\ColorInterface; class Font extends AbstractFont @@ -49,7 +49,7 @@ class Font extends AbstractFont { // no text - no box size if (mb_strlen($text) === 0) { - return (new Size(0, 0))->toPolygon(); + return (new Rectangle(0, 0)); } $draw = $this->toImagickDraw(); @@ -57,9 +57,9 @@ class Font extends AbstractFont $draw->setTextAntialias(true); $dimensions = (new Imagick())->queryFontMetrics($draw, $text); - return (new Size( + return (new Rectangle( intval(round($dimensions['textWidth'])), intval(round($dimensions['ascender'] + $dimensions['descender'])), - ))->toPolygon(); + )); } } diff --git a/src/Drivers/Imagick/Frame.php b/src/Drivers/Imagick/Frame.php index 245aff84..895d228b 100644 --- a/src/Drivers/Imagick/Frame.php +++ b/src/Drivers/Imagick/Frame.php @@ -3,7 +3,7 @@ namespace Intervention\Image\Drivers\Imagick; use Imagick; -use Intervention\Image\Geometry\Size; +use Intervention\Image\Geometry\Rectangle; use Intervention\Image\Interfaces\FrameInterface; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\SizeInterface; @@ -29,7 +29,10 @@ class Frame implements FrameInterface public function getSize(): SizeInterface { - return new Size($this->core->getImageWidth(), $this->core->getImageHeight()); + return new Rectangle( + $this->core->getImageWidth(), + $this->core->getImageHeight() + ); } public function getDelay(): float diff --git a/src/Drivers/Imagick/Image.php b/src/Drivers/Imagick/Image.php index 906322af..2bcbdec5 100644 --- a/src/Drivers/Imagick/Image.php +++ b/src/Drivers/Imagick/Image.php @@ -44,8 +44,8 @@ class Image extends AbstractImage implements ImageInterface, Iterator $size = $frame->getSize(); $imagick->setImagePage( - $size->getWidth(), - $size->getHeight(), + $size->width(), + $size->height(), $frame->getOffsetLeft(), $frame->getOffsetTop() ); diff --git a/src/Drivers/Imagick/Modifiers/FitModifier.php b/src/Drivers/Imagick/Modifiers/FitModifier.php index f5465c5f..910d86e5 100644 --- a/src/Drivers/Imagick/Modifiers/FitModifier.php +++ b/src/Drivers/Imagick/Modifiers/FitModifier.php @@ -15,15 +15,15 @@ class FitModifier extends AbstractFitModifier implements ModifierInterface foreach ($image as $frame) { $frame->getCore()->extentImage( - $crop->getWidth(), - $crop->getHeight(), - $crop->getPivot()->getX(), - $crop->getPivot()->getY() + $crop->width(), + $crop->height(), + $crop->pivot()->getX(), + $crop->pivot()->getY() ); $frame->getCore()->scaleImage( - $resize->getWidth(), - $resize->getHeight() + $resize->width(), + $resize->height() ); } diff --git a/src/Drivers/Imagick/Modifiers/PadDownModifier.php b/src/Drivers/Imagick/Modifiers/PadDownModifier.php index e0649857..f6de2fc4 100644 --- a/src/Drivers/Imagick/Modifiers/PadDownModifier.php +++ b/src/Drivers/Imagick/Modifiers/PadDownModifier.php @@ -2,7 +2,7 @@ namespace Intervention\Image\Drivers\Imagick\Modifiers; -use Intervention\Image\Geometry\Size; +use Intervention\Image\Geometry\Rectangle; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\SizeInterface; @@ -13,13 +13,13 @@ class PadDownModifier extends PadModifier $resize = $this->getResizeSize($image); return $image->getSize() - ->contain($resize->getWidth(), $resize->getHeight()) + ->contain($resize->width(), $resize->height()) ->alignPivotTo($resize, $this->position); } protected function getResizeSize(ImageInterface $image): SizeInterface { - return (new Size($this->width, $this->height)) + return (new Rectangle($this->width, $this->height)) ->resizeDown($image->getWidth(), $image->getHeight()); } } diff --git a/src/Drivers/Imagick/Modifiers/PixelateModifier.php b/src/Drivers/Imagick/Modifiers/PixelateModifier.php index 770f6f33..7552e401 100644 --- a/src/Drivers/Imagick/Modifiers/PixelateModifier.php +++ b/src/Drivers/Imagick/Modifiers/PixelateModifier.php @@ -3,7 +3,6 @@ namespace Intervention\Image\Drivers\Imagick\Modifiers; use Intervention\Image\Drivers\Imagick\Frame; -use Intervention\Image\Interfaces\FrameInterface; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; @@ -28,10 +27,10 @@ class PixelateModifier implements ModifierInterface $size = $frame->getSize(); $frame->getCore()->scaleImage( - max(1, ($size->getWidth() / $this->size)), - max(1, ($size->getHeight() / $this->size)) + max(1, ($size->width() / $this->size)), + max(1, ($size->height() / $this->size)) ); - $frame->getCore()->scaleImage($size->getWidth(), $size->getHeight()); + $frame->getCore()->scaleImage($size->width(), $size->height()); } } diff --git a/src/Drivers/Imagick/Modifiers/ResizeModifier.php b/src/Drivers/Imagick/Modifiers/ResizeModifier.php index dbf0697a..83d163c7 100644 --- a/src/Drivers/Imagick/Modifiers/ResizeModifier.php +++ b/src/Drivers/Imagick/Modifiers/ResizeModifier.php @@ -2,7 +2,6 @@ namespace Intervention\Image\Drivers\Imagick\Modifiers; -use Intervention\Image\Interfaces\FrameInterface; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; use Intervention\Image\Interfaces\SizeInterface; @@ -20,8 +19,8 @@ class ResizeModifier implements ModifierInterface foreach ($image as $frame) { $frame->getCore()->scaleImage( - $resizeTo->getWidth(), - $resizeTo->getHeight() + $resizeTo->width(), + $resizeTo->height() ); } diff --git a/src/Geometry/Size.php b/src/Geometry/Rectangle.php similarity index 58% rename from src/Geometry/Size.php rename to src/Geometry/Rectangle.php index 3dbf67e4..088ed268 100644 --- a/src/Geometry/Size.php +++ b/src/Geometry/Rectangle.php @@ -6,123 +6,48 @@ use Intervention\Image\Geometry\Tools\Resizer; use Intervention\Image\Interfaces\PointInterface; use Intervention\Image\Interfaces\SizeInterface; -class Size implements SizeInterface +class Rectangle extends Polygon implements SizeInterface { public function __construct( - protected int $width, - protected int $height, + int $width, + int $height, protected ?Point $pivot = null ) { $this->pivot = $pivot ? $pivot : new Point(); + $this->addPoint(new Point($this->pivot->getX(), $this->pivot->getY())); + $this->addPoint(new Point($this->pivot->getX() + $width, $this->pivot->getY())); + $this->addPoint(new Point($this->pivot->getX() + $width, $this->pivot->getY() - $height)); + $this->addPoint(new Point($this->pivot->getX(), $this->pivot->getY() - $height)); } - public function getWidth(): int + public function withWidth(int $width): self { - return $this->width; - } - - public function getHeight(): int - { - return $this->height; - } - - public function setWidth(int $width): SizeInterface - { - $this->width = $width; + $this[1]->setX($this[0]->getX() + $width); + $this[2]->setX($this[3]->getX() + $width); return $this; } - public function setHeight(int $height): SizeInterface + public function withHeight(int $height): self { - $this->height = $height; + $this[2]->setY($this[1]->getY() + $height); + $this[3]->setY($this[0]->getY() + $height); return $this; } - public function addWidth(int $value): SizeInterface - { - $this->width = $this->width + $value; - - return $this; - } - - public function subWidth(int $value): SizeInterface - { - $this->width = $this->width - $value; - - return $this; - } - - public function addHeight(int $value): SizeInterface - { - $this->height = $this->height + $value; - - return $this; - } - - public function subHeight(int $value): SizeInterface - { - $this->height = $this->height - $value; - - return $this; - } - - /** - * Get current pivot point - * - * @return Point - */ - public function getPivot(): PointInterface + public function pivot(): Point { return $this->pivot; } - public function setPivot(PointInterface $pivot): SizeInterface + public function withPivot(PointInterface $pivot): self { $this->pivot = $pivot; return $this; } - public function getAspectRatio(): float - { - return $this->width / $this->height; - } - - public function fitsInto(SizeInterface $size): bool - { - if ($this->getWidth() > $size->getWidth()) { - return false; - } - - if ($this->getHeight() > $size->getHeight()) { - return false; - } - - return true; - } - - /** - * Determine if size is landscape format - * - * @return boolean - */ - public function isLandscape(): bool - { - return $this->getWidth() > $this->getHeight(); - } - - /** - * Determine if size is portrait format - * - * @return boolean - */ - public function isPortrait(): bool - { - return $this->getWidth() < $this->getHeight(); - } - /** * Aligns current size's pivot point to given position * and moves point automatically by offset. @@ -130,9 +55,10 @@ class Size implements SizeInterface * @param string $position * @param int $offset_x * @param int $offset_y - * @return Size + * @return Rectangle */ - public function alignPivot(string $position, int $offset_x = 0, int $offset_y = 0): SizeInterface + // TODO: rename method to movePivot + public function alignPivot(string $position, int $offset_x = 0, int $offset_y = 0): self { switch (strtolower($position)) { case 'top': @@ -140,13 +66,13 @@ class Size implements SizeInterface case 'top-middle': case 'center-top': case 'middle-top': - $x = intval($this->width / 2); + $x = intval($this->width() / 2); $y = 0 + $offset_y; break; case 'top-right': case 'right-top': - $x = $this->width - $offset_x; + $x = $this->width() - $offset_x; $y = 0 + $offset_y; break; @@ -156,7 +82,7 @@ class Size implements SizeInterface case 'center-left': case 'middle-left': $x = 0 + $offset_x; - $y = intval($this->height / 2); + $y = intval($this->height() / 2); break; case 'right': @@ -164,14 +90,14 @@ class Size implements SizeInterface case 'right-middle': case 'center-right': case 'middle-right': - $x = $this->width - $offset_x; - $y = intval($this->height / 2); + $x = $this->width() - $offset_x; + $y = intval($this->height() / 2); break; case 'bottom-left': case 'left-bottom': $x = 0 + $offset_x; - $y = $this->height - $offset_y; + $y = $this->height() - $offset_y; break; case 'bottom': @@ -179,22 +105,22 @@ class Size implements SizeInterface case 'bottom-middle': case 'center-bottom': case 'middle-bottom': - $x = intval($this->width / 2); - $y = $this->height - $offset_y; + $x = intval($this->width() / 2); + $y = $this->height() - $offset_y; break; case 'bottom-right': case 'right-bottom': - $x = $this->width - $offset_x; - $y = $this->height - $offset_y; + $x = $this->width() - $offset_x; + $y = $this->height() - $offset_y; break; case 'center': case 'middle': case 'center-center': case 'middle-middle': - $x = intval($this->width / 2) + $offset_x; - $y = intval($this->height / 2) + $offset_y; + $x = intval($this->width() / 2) + $offset_x; + $y = intval($this->height() / 2) + $offset_y; break; default: @@ -210,12 +136,13 @@ class Size implements SizeInterface return $this; } - public function alignPivotTo(SizeInterface $size, string $position): SizeInterface + // TODO: rename to alignPivot + public function alignPivotTo(SizeInterface $size, string $position): self { - $reference = new Size($size->getWidth(), $size->getHeight()); + $reference = new self($size->width(), $size->height()); $reference->alignPivot($position); - $this->alignPivot($position)->setPivot( + $this->alignPivot($position)->withPivot( $reference->getRelativePositionTo($this) ); @@ -229,36 +156,50 @@ class Size implements SizeInterface * @param Size $size * @return Point */ - public function getRelativePositionTo(SizeInterface $size): PointInterface + public function getRelativePositionTo(SizeInterface $rectangle): PointInterface { - $x = $this->getPivot()->getX() - $size->getPivot()->getX(); - $y = $this->getPivot()->getY() - $size->getPivot()->getY(); - - return new Point($x, $y); + return new Point( + $this->pivot()->getX() - $rectangle->pivot()->getX(), + $this->pivot()->getY() - $rectangle->pivot()->getY() + ); } - public function toPolygon(): Polygon + public function getAspectRatio(): float { - $polygon = new Polygon([ - $this->pivot // top/left - ], $this->pivot); + return $this->width() / $this->height(); + } - // top/right - $polygon->addPoint( - new Point($this->pivot->getX() + $this->getWidth(), $this->pivot->getY()) - ); + public function fitsInto(SizeInterface $size): bool + { + if ($this->width() > $size->width()) { + return false; + } - // bottom/right - $polygon->addPoint( - new Point($this->pivot->getX() + $this->getWidth(), $this->pivot->getY() - $this->getHeight()) - ); + if ($this->height() > $size->height()) { + return false; + } - // bottom/left - $polygon->addPoint( - new Point($this->pivot->getX(), $this->pivot->getY() - $this->getHeight()) - ); + return true; + } - return $polygon; + /** + * Determine if size is landscape format + * + * @return boolean + */ + public function isLandscape(): bool + { + return $this->width() > $this->height(); + } + + /** + * Determine if size is portrait format + * + * @return boolean + */ + public function isPortrait(): bool + { + return $this->width() < $this->height(); } protected function getResizer(?int $width = null, ?int $height = null): Resizer diff --git a/src/Geometry/Tools/Resizer.php b/src/Geometry/Tools/Resizer.php index d295ead9..e90d87b5 100644 --- a/src/Geometry/Tools/Resizer.php +++ b/src/Geometry/Tools/Resizer.php @@ -3,7 +3,7 @@ namespace Intervention\Image\Geometry\Tools; use Intervention\Image\Exceptions\GeometryException; -use Intervention\Image\Geometry\Size; +use Intervention\Image\Geometry\Rectangle; use Intervention\Image\Interfaces\SizeInterface; class Resizer @@ -46,7 +46,7 @@ class Resizer throw new GeometryException('Target size needs width and height.'); } - return new Size($this->width, $this->height); + return new Rectangle($this->width, $this->height); } public function toWidth(int $width): self @@ -65,8 +65,8 @@ class Resizer public function toSize(SizeInterface $size): self { - $this->width = $size->getWidth(); - $this->height = $size->getHeight(); + $this->width = $size->width(); + $this->height = $size->height(); return $this; } @@ -74,7 +74,7 @@ class Resizer protected function getProportionalWidth(SizeInterface $size): int { if (! $this->hasTargetHeight()) { - return $size->getWidth(); + return $size->width(); } return (int) round($this->height * $size->getAspectRatio()); @@ -83,7 +83,7 @@ class Resizer protected function getProportionalHeight(SizeInterface $size): int { if (! $this->hasTargetWidth()) { - return $size->getHeight(); + return $size->height(); } return (int) round($this->width / $size->getAspectRatio()); @@ -91,14 +91,14 @@ class Resizer public function resize(SizeInterface $size): SizeInterface { - $resized = new Size($size->getWidth(), $size->getHeight()); + $resized = new Rectangle($size->width(), $size->height()); if ($width = $this->getTargetWidth()) { - $resized->setWidth($width); + $resized->withWidth($width); } if ($height = $this->getTargetHeight()) { - $resized->setHeight($height); + $resized->withHeight($height); } return $resized; @@ -106,17 +106,17 @@ class Resizer public function resizeDown(SizeInterface $size): SizeInterface { - $resized = new Size($size->getWidth(), $size->getHeight()); + $resized = new Rectangle($size->width(), $size->height()); if ($width = $this->getTargetWidth()) { - $resized->setWidth( - min($width, $size->getWidth()) + $resized->withWidth( + min($width, $size->width()) ); } if ($height = $this->getTargetHeight()) { - $resized->setHeight( - min($height, $size->getHeight()) + $resized->withHeight( + min($height, $size->height()) ); } @@ -125,23 +125,23 @@ class Resizer public function scale(SizeInterface $size): SizeInterface { - $resized = new Size($size->getWidth(), $size->getHeight()); + $resized = new Rectangle($size->width(), $size->height()); if ($this->hasTargetWidth() && $this->hasTargetHeight()) { - $resized->setWidth(min( + $resized->withWidth(min( $this->getProportionalWidth($size), $this->getTargetWidth() )); - $resized->setHeight(min( + $resized->withHeight(min( $this->getProportionalHeight($size), $this->getTargetHeight() )); } elseif ($this->hasTargetWidth()) { - $resized->setWidth($this->getTargetWidth()); - $resized->setHeight($this->getProportionalHeight($size)); + $resized->withWidth($this->getTargetWidth()); + $resized->withHeight($this->getProportionalHeight($size)); } elseif ($this->hasTargetHeight()) { - $resized->setWidth($this->getProportionalWidth($size)); - $resized->setHeight($this->getTargetHeight()); + $resized->withWidth($this->getProportionalWidth($size)); + $resized->withHeight($this->getTargetHeight()); } return $resized; @@ -149,36 +149,36 @@ class Resizer public function scaleDown(SizeInterface $size): SizeInterface { - $resized = new Size($size->getWidth(), $size->getHeight()); + $resized = new Rectangle($size->width(), $size->height()); if ($this->hasTargetWidth() && $this->hasTargetHeight()) { - $resized->setWidth(min( + $resized->withWidth(min( $this->getProportionalWidth($size), $this->getTargetWidth(), - $size->getWidth() + $size->width() )); - $resized->setHeight(min( + $resized->withHeight(min( $this->getProportionalHeight($size), $this->getTargetHeight(), - $size->getHeight() + $size->height() )); } elseif ($this->hasTargetWidth()) { - $resized->setWidth(min( + $resized->withWidth(min( $this->getTargetWidth(), - $size->getWidth() + $size->width() )); - $resized->setHeight(min( + $resized->withHeight(min( $this->getProportionalHeight($size), - $size->getHeight() + $size->height() )); } elseif ($this->hasTargetHeight()) { - $resized->setWidth(min( + $resized->withWidth(min( $this->getProportionalWidth($size), - $size->getWidth() + $size->width() )); - $resized->setHeight(min( + $resized->withHeight(min( $this->getTargetHeight(), - $size->getHeight() + $size->height() )); } @@ -193,16 +193,16 @@ class Resizer */ public function cover(SizeInterface $size): SizeInterface { - $resized = new Size($size->getWidth(), $size->getHeight()); + $resized = new Rectangle($size->width(), $size->height()); // auto height - $resized->setWidth($this->getTargetWidth()); - $resized->setHeight($this->getProportionalHeight($size)); + $resized->withWidth($this->getTargetWidth()); + $resized->withHeight($this->getProportionalHeight($size)); if ($resized->fitsInto($this->getTargetSize())) { // auto width - $resized->setWidth($this->getProportionalWidth($size)); - $resized->setHeight($this->getTargetHeight()); + $resized->withWidth($this->getProportionalWidth($size)); + $resized->withHeight($this->getTargetHeight()); } return $resized; @@ -216,16 +216,16 @@ class Resizer */ public function contain(SizeInterface $size): SizeInterface { - $resized = new Size($size->getWidth(), $size->getHeight()); + $resized = new Rectangle($size->width(), $size->height()); // auto height - $resized->setWidth($this->getTargetWidth()); - $resized->setHeight($this->getProportionalHeight($size)); + $resized->withWidth($this->getTargetWidth()); + $resized->withHeight($this->getProportionalHeight($size)); if (!$resized->fitsInto($this->getTargetSize())) { // auto width - $resized->setWidth($this->getProportionalWidth($size)); - $resized->setHeight($this->getTargetHeight()); + $resized->withWidth($this->getProportionalWidth($size)); + $resized->withHeight($this->getTargetHeight()); } return $resized; diff --git a/src/Interfaces/SizeInterface.php b/src/Interfaces/SizeInterface.php index 7aaef57d..94579afa 100644 --- a/src/Interfaces/SizeInterface.php +++ b/src/Interfaces/SizeInterface.php @@ -4,11 +4,12 @@ namespace Intervention\Image\Interfaces; interface SizeInterface { - public function getWidth(): int; - public function getHeight(): int; - public function getPivot(): PointInterface; - public function setWidth(int $width): SizeInterface; - public function setHeight(int $height): SizeInterface; + public function width(): int; + public function height(): int; + public function pivot(): PointInterface; + public function withWidth(int $width): SizeInterface; + public function withHeight(int $height): SizeInterface; + public function withPivot(PointInterface $pivot): SizeInterface; public function getAspectRatio(): float; public function fitsInto(SizeInterface $size): bool; public function isLandscape(): bool; diff --git a/src/Typography/TextBlock.php b/src/Typography/TextBlock.php index 2430f269..4593d67f 100644 --- a/src/Typography/TextBlock.php +++ b/src/Typography/TextBlock.php @@ -5,7 +5,7 @@ namespace Intervention\Image\Typography; use Intervention\Image\Collection; use Intervention\Image\Geometry\Point; use Intervention\Image\Geometry\Polygon; -use Intervention\Image\Geometry\Size; +use Intervention\Image\Geometry\Rectangle; use Intervention\Image\Interfaces\FontInterface; class TextBlock extends Collection @@ -22,10 +22,10 @@ class TextBlock extends Collection $pivot = $pivot ? $pivot : new Point(); // bounding box - $box = (new Size( + $box = (new Rectangle( $this->longestLine()->widthInFont($font), $font->leadingInPixels() * ($this->count() - 1) + $font->capHeight() - ))->toPolygon(); + )); // set pivot $box->setPivotPoint($pivot); diff --git a/tests/Drivers/Abstract/AbstractImageTest.php b/tests/Drivers/Abstract/AbstractImageTest.php index dde2be23..21bdd857 100644 --- a/tests/Drivers/Abstract/AbstractImageTest.php +++ b/tests/Drivers/Abstract/AbstractImageTest.php @@ -5,7 +5,7 @@ namespace Intervention\Image\Tests\Drivers\Abstract; use Intervention\Image\Collection; use Intervention\Image\Drivers\Abstract\AbstractImage; use Intervention\Image\EncodedImage; -use Intervention\Image\Geometry\Size; +use Intervention\Image\Geometry\Rectangle; use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\EncoderInterface; use Intervention\Image\Interfaces\FrameInterface; @@ -44,17 +44,17 @@ class AbstractImageTest extends TestCase public function testGetSize(): void { $img = $this->abstractImageMock(); - $this->assertInstanceOf(Size::class, $img->getSize()); - $this->assertEquals(300, $img->getSize()->getWidth()); - $this->assertEquals(200, $img->getSize()->getHeight()); + $this->assertInstanceOf(Rectangle::class, $img->getSize()); + $this->assertEquals(300, $img->getSize()->width()); + $this->assertEquals(200, $img->getSize()->height()); } public function testSizeAlias(): void { $img = $this->abstractImageMock(); - $this->assertInstanceOf(Size::class, $img->getSize()); - $this->assertEquals(300, $img->size()->getWidth()); - $this->assertEquals(200, $img->size()->getHeight()); + $this->assertInstanceOf(Rectangle::class, $img->getSize()); + $this->assertEquals(300, $img->size()->width()); + $this->assertEquals(200, $img->size()->height()); } public function testModify(): void diff --git a/tests/Drivers/Abstract/Modifiers/AbstractFitModifierTest.php b/tests/Drivers/Abstract/Modifiers/AbstractFitModifierTest.php index f8678f96..28ebb3f6 100644 --- a/tests/Drivers/Abstract/Modifiers/AbstractFitModifierTest.php +++ b/tests/Drivers/Abstract/Modifiers/AbstractFitModifierTest.php @@ -29,10 +29,10 @@ class AbstractFitModifierTest extends TestCase $image = (new ImageFactory())->newImage($width, $height); $size = $modifier->getCropSize($image); - static::assertSame($expectedWidth, $size->getWidth()); - static::assertSame($expectedHeight, $size->getHeight()); - static::assertSame($expectedX, $size->getPivot()->getX()); - static::assertSame($expectedY, $size->getPivot()->getY()); + static::assertSame($expectedWidth, $size->width()); + static::assertSame($expectedHeight, $size->height()); + static::assertSame($expectedX, $size->pivot()->getX()); + static::assertSame($expectedY, $size->pivot()->getY()); } public function testGetResizeSize(): void @@ -43,10 +43,10 @@ class AbstractFitModifierTest extends TestCase $size = $modifier->getCropSize($image); $resize = $modifier->getResizeSize($size); - static::assertSame(200, $resize->getWidth()); - static::assertSame(100, $resize->getHeight()); - static::assertSame(0, $resize->getPivot()->getX()); - static::assertSame(0, $resize->getPivot()->getY()); + static::assertSame(200, $resize->width()); + static::assertSame(100, $resize->height()); + static::assertSame(0, $resize->pivot()->getX()); + static::assertSame(0, $resize->pivot()->getY()); } private function getModifier(int $width, int $height, string $position): AbstractFitModifier diff --git a/tests/Drivers/Abstract/Modifiers/AbstractPadModifierTest.php b/tests/Drivers/Abstract/Modifiers/AbstractPadModifierTest.php index 4308a3bf..860aa546 100644 --- a/tests/Drivers/Abstract/Modifiers/AbstractPadModifierTest.php +++ b/tests/Drivers/Abstract/Modifiers/AbstractPadModifierTest.php @@ -31,10 +31,10 @@ final class AbstractPadModifierTest extends TestCase $image = (new ImageFactory())->newImage($width, $height); $size = $modifier->getCropSize($image); - static::assertSame($expectedWidth, $size->getWidth()); - static::assertSame($expectedHeight, $size->getHeight()); - static::assertSame($expectedX, $size->getPivot()->getX()); - static::assertSame($expectedY, $size->getPivot()->getY()); + static::assertSame($expectedWidth, $size->width()); + static::assertSame($expectedHeight, $size->height()); + static::assertSame($expectedX, $size->pivot()->getX()); + static::assertSame($expectedY, $size->pivot()->getY()); } public function testGetResizeSize(): void @@ -44,10 +44,10 @@ final class AbstractPadModifierTest extends TestCase $image = (new ImageFactory())->newImage(300, 200); $resize = $modifier->getResizeSize($image); - static::assertSame(200, $resize->getWidth()); - static::assertSame(100, $resize->getHeight()); - static::assertSame(0, $resize->getPivot()->getX()); - static::assertSame(0, $resize->getPivot()->getY()); + static::assertSame(200, $resize->width()); + static::assertSame(100, $resize->height()); + static::assertSame(0, $resize->pivot()->getX()); + static::assertSame(0, $resize->pivot()->getY()); } private function getModifier(int $width, int $height, $background, string $position): AbstractPadModifier diff --git a/tests/Drivers/Gd/FrameTest.php b/tests/Drivers/Gd/FrameTest.php index 422676d2..a27a42d3 100644 --- a/tests/Drivers/Gd/FrameTest.php +++ b/tests/Drivers/Gd/FrameTest.php @@ -5,7 +5,7 @@ namespace Intervention\Image\Tests\Drivers\Gd; use GdImage; use Intervention\Image\Drivers\Gd\Frame; use Intervention\Image\Drivers\Gd\Image; -use Intervention\Image\Geometry\Size; +use Intervention\Image\Geometry\Rectangle; use Intervention\Image\Tests\TestCase; /** @@ -36,16 +36,16 @@ class FrameTest extends TestCase $core1 = imagecreatetruecolor(3, 2); $core2 = imagecreatetruecolor(3, 3); $frame = new Frame($core1); - $this->assertEquals(2, $frame->getSize()->getHeight()); + $this->assertEquals(2, $frame->getSize()->height()); $result = $frame->setCore($core2); - $this->assertInstanceOf(Frame::Class, $result); - $this->assertEquals(3, $frame->getSize()->getHeight()); + $this->assertInstanceOf(Frame::class, $result); + $this->assertEquals(3, $frame->getSize()->height()); } public function testGetSize(): void { $frame = $this->getTestFrame(); - $this->assertInstanceOf(Size::class, $frame->getSize()); + $this->assertInstanceOf(Rectangle::class, $frame->getSize()); } public function testSetGetDelay() diff --git a/tests/Drivers/Gd/ImageTest.php b/tests/Drivers/Gd/ImageTest.php index 27ce28d1..927450fd 100644 --- a/tests/Drivers/Gd/ImageTest.php +++ b/tests/Drivers/Gd/ImageTest.php @@ -6,7 +6,7 @@ use Intervention\Image\Collection; use Intervention\Image\Drivers\Gd\Color; use Intervention\Image\Drivers\Gd\Frame; use Intervention\Image\Drivers\Gd\Image; -use Intervention\Image\Geometry\Size; +use Intervention\Image\Geometry\Rectangle; use Intervention\Image\Tests\TestCase; /** @@ -89,7 +89,7 @@ class ImageTest extends TestCase public function testGetSize(): void { - $this->assertInstanceOf(Size::class, $this->image->getSize()); + $this->assertInstanceOf(Rectangle::class, $this->image->getSize()); } public function testPickColor(): void diff --git a/tests/Drivers/Imagick/FrameTest.php b/tests/Drivers/Imagick/FrameTest.php index 85e20f10..015b8ac9 100644 --- a/tests/Drivers/Imagick/FrameTest.php +++ b/tests/Drivers/Imagick/FrameTest.php @@ -6,7 +6,7 @@ use Imagick; use ImagickPixel; use Intervention\Image\Drivers\Imagick\Frame; use Intervention\Image\Drivers\Imagick\Image; -use Intervention\Image\Geometry\Size; +use Intervention\Image\Geometry\Rectangle; use Intervention\Image\Tests\TestCase; /** @@ -35,7 +35,7 @@ class FrameTest extends TestCase public function testGetSize(): void { $frame = $this->getTestFrame(); - $this->assertInstanceOf(Size::class, $frame->getSize()); + $this->assertInstanceOf(Rectangle::class, $frame->getSize()); } public function testSetGetDelay() diff --git a/tests/Drivers/Imagick/ImageTest.php b/tests/Drivers/Imagick/ImageTest.php index 53e38f2c..d76e8d19 100644 --- a/tests/Drivers/Imagick/ImageTest.php +++ b/tests/Drivers/Imagick/ImageTest.php @@ -6,7 +6,7 @@ use Imagick; use ImagickPixel; use Intervention\Image\Drivers\Imagick\Frame; use Intervention\Image\Drivers\Imagick\Image; -use Intervention\Image\Geometry\Size; +use Intervention\Image\Geometry\Rectangle; use Intervention\Image\Tests\TestCase; /** @@ -85,6 +85,6 @@ class ImageTest extends TestCase public function testGetSize(): void { - $this->assertInstanceOf(Size::class, $this->image->getSize()); + $this->assertInstanceOf(Rectangle::class, $this->image->getSize()); } } diff --git a/tests/Geometry/RectangleTest.php b/tests/Geometry/RectangleTest.php new file mode 100644 index 00000000..9c42922e --- /dev/null +++ b/tests/Geometry/RectangleTest.php @@ -0,0 +1,240 @@ +assertEquals(0, $rectangle[0]->getX()); + $this->assertEquals(0, $rectangle[0]->getY()); + $this->assertEquals(300, $rectangle[1]->getX()); + $this->assertEquals(0, $rectangle[1]->getY()); + $this->assertEquals(300, $rectangle[2]->getX()); + $this->assertEquals(-200, $rectangle[2]->getY()); + $this->assertEquals(0, $rectangle[3]->getX()); + $this->assertEquals(-200, $rectangle[3]->getY()); + $this->assertEquals(300, $rectangle->width()); + $this->assertEquals(200, $rectangle->height()); + } + + public function testWithWidth(): void + { + $rectangle = new Rectangle(300, 200); + $this->assertEquals(300, $rectangle->width()); + $rectangle->withWidth(400); + $this->assertEquals(400, $rectangle->width()); + } + + public function testWithHeight(): void + { + $rectangle = new Rectangle(300, 200); + $this->assertEquals(200, $rectangle->height()); + $rectangle->withHeight(800); + $this->assertEquals(800, $rectangle->height()); + } + + public function testGetAspectRatio() + { + $size = new Rectangle(800, 600); + $this->assertEquals(1.33333333333, $size->getAspectRatio()); + + $size = new Rectangle(100, 100); + $this->assertEquals(1, $size->getAspectRatio()); + + $size = new Rectangle(1920, 1080); + $this->assertEquals(1.777777777778, $size->getAspectRatio()); + } + + public function testFitsInto() + { + $box = new Rectangle(800, 600); + $fits = $box->fitsInto(new Rectangle(100, 100)); + $this->assertFalse($fits); + + $box = new Rectangle(800, 600); + $fits = $box->fitsInto(new Rectangle(1000, 100)); + $this->assertFalse($fits); + + $box = new Rectangle(800, 600); + $fits = $box->fitsInto(new Rectangle(100, 1000)); + $this->assertFalse($fits); + + $box = new Rectangle(800, 600); + $fits = $box->fitsInto(new Rectangle(800, 600)); + $this->assertTrue($fits); + + $box = new Rectangle(800, 600); + $fits = $box->fitsInto(new Rectangle(1000, 1000)); + $this->assertTrue($fits); + + $box = new Rectangle(100, 100); + $fits = $box->fitsInto(new Rectangle(800, 600)); + $this->assertTrue($fits); + + $box = new Rectangle(100, 100); + $fits = $box->fitsInto(new Rectangle(80, 60)); + $this->assertFalse($fits); + } + + public function testIsLandscape() + { + $box = new Rectangle(100, 100); + $this->assertFalse($box->isLandscape()); + + $box = new Rectangle(100, 200); + $this->assertFalse($box->isLandscape()); + + $box = new Rectangle(300, 200); + $this->assertTrue($box->isLandscape()); + } + + public function testIsPortrait() + { + $box = new Rectangle(100, 100); + $this->assertFalse($box->isPortrait()); + + $box = new Rectangle(200, 100); + $this->assertFalse($box->isPortrait()); + + $box = new Rectangle(200, 300); + $this->assertTrue($box->isPortrait()); + } + + public function testSetGetPivot(): void + { + $box = new Rectangle(800, 600); + $pivot = $box->pivot(); + $this->assertInstanceOf(Point::class, $pivot); + $this->assertEquals(0, $pivot->getX()); + $result = $box->withPivot(new Point(10, 0)); + $this->assertInstanceOf(Rectangle::class, $result); + $this->assertEquals(10, $box->pivot()->getX()); + } + + public function testAlignPivot(): void + { + $box = new Rectangle(640, 480); + $this->assertEquals(0, $box->pivot()->getX()); + $this->assertEquals(0, $box->pivot()->getY()); + + $box->alignPivot('top-left', 3, 3); + $this->assertEquals(3, $box->pivot()->getX()); + $this->assertEquals(3, $box->pivot()->getY()); + + $box->alignPivot('top', 3, 3); + $this->assertEquals(320, $box->pivot()->getX()); + $this->assertEquals(3, $box->pivot()->getY()); + + $box->alignPivot('top-right', 3, 3); + $this->assertEquals(637, $box->pivot()->getX()); + $this->assertEquals(3, $box->pivot()->getY()); + + $box->alignPivot('left', 3, 3); + $this->assertEquals(3, $box->pivot()->getX()); + $this->assertEquals(240, $box->pivot()->getY()); + + $box->alignPivot('center', 3, 3); + $this->assertEquals(323, $box->pivot()->getX()); + $this->assertEquals(243, $box->pivot()->getY()); + + $box->alignPivot('right', 3, 3); + $this->assertEquals(637, $box->pivot()->getX()); + $this->assertEquals(240, $box->pivot()->getY()); + + $box->alignPivot('bottom-left', 3, 3); + $this->assertEquals(3, $box->pivot()->getX()); + $this->assertEquals(477, $box->pivot()->getY()); + + $box->alignPivot('bottom', 3, 3); + $this->assertEquals(320, $box->pivot()->getX()); + $this->assertEquals(477, $box->pivot()->getY()); + + $result = $box->alignPivot('bottom-right', 3, 3); + $this->assertEquals(637, $box->pivot()->getX()); + $this->assertEquals(477, $box->pivot()->getY()); + + $this->assertInstanceOf(Rectangle::class, $result); + } + + public function testAlignPivotTo(): void + { + $container = new Rectangle(800, 600); + $size = new Rectangle(200, 100); + $size->alignPivotTo($container, 'center'); + $this->assertEquals(300, $size->pivot()->getX()); + $this->assertEquals(250, $size->pivot()->getY()); + + $container = new Rectangle(800, 600); + $size = new Rectangle(100, 100); + $size->alignPivotTo($container, 'center'); + $this->assertEquals(350, $size->pivot()->getX()); + $this->assertEquals(250, $size->pivot()->getY()); + + $container = new Rectangle(800, 600); + $size = new Rectangle(800, 600); + $size->alignPivotTo($container, 'center'); + $this->assertEquals(0, $size->pivot()->getX()); + $this->assertEquals(0, $size->pivot()->getY()); + + $container = new Rectangle(100, 100); + $size = new Rectangle(800, 600); + $size->alignPivotTo($container, 'center'); + $this->assertEquals(-350, $size->pivot()->getX()); + $this->assertEquals(-250, $size->pivot()->getY()); + + $container = new Rectangle(100, 100); + $size = new Rectangle(800, 600); + $size->alignPivotTo($container, 'bottom-right'); + $this->assertEquals(-700, $size->pivot()->getX()); + $this->assertEquals(-500, $size->pivot()->getY()); + } + + public function testgetRelativePositionTo(): void + { + $container = new Rectangle(800, 600); + $input = new Rectangle(200, 100); + $container->alignPivot('top-left'); + $input->alignPivot('top-left'); + $pos = $container->getRelativePositionTo($input); + $this->assertEquals(0, $pos->getX()); + $this->assertEquals(0, $pos->getY()); + + $container = new Rectangle(800, 600); + $input = new Rectangle(200, 100); + $container->alignPivot('center'); + $input->alignPivot('top-left'); + $pos = $container->getRelativePositionTo($input); + $this->assertEquals(400, $pos->getX()); + $this->assertEquals(300, $pos->getY()); + + $container = new Rectangle(800, 600); + $input = new Rectangle(200, 100); + $container->alignPivot('bottom-right'); + $input->alignPivot('top-right'); + $pos = $container->getRelativePositionTo($input); + $this->assertEquals(600, $pos->getX()); + $this->assertEquals(600, $pos->getY()); + + $container = new Rectangle(800, 600); + $input = new Rectangle(200, 100); + $container->alignPivot('center'); + $input->alignPivot('center'); + $pos = $container->getRelativePositionTo($input); + $this->assertEquals(300, $pos->getX()); + $this->assertEquals(250, $pos->getY()); + + $container = new Rectangle(100, 200); + $input = new Rectangle(100, 100); + $container->alignPivot('center'); + $input->alignPivot('center'); + $pos = $container->getRelativePositionTo($input); + $this->assertEquals(0, $pos->getX()); + $this->assertEquals(50, $pos->getY()); + } +} diff --git a/tests/Geometry/ResizerTest.php b/tests/Geometry/ResizerTest.php index c003b5ab..47f4fea1 100644 --- a/tests/Geometry/ResizerTest.php +++ b/tests/Geometry/ResizerTest.php @@ -3,8 +3,8 @@ namespace Intervention\Image\Tests\Geometry; use Intervention\Image\Geometry\Point; +use Intervention\Image\Geometry\Rectangle; use Intervention\Image\Geometry\Tools\Resizer; -use Intervention\Image\Geometry\Size; use PHPUnit\Framework\TestCase; /** @@ -44,325 +44,325 @@ class ResizerTest extends TestCase public function testToSize(): void { $resizer = new Resizer(); - $resizer = $resizer->toSize(new Size(200, 100)); + $resizer = $resizer->toSize(new Rectangle(200, 100)); $this->assertInstanceOf(Resizer::class, $resizer); } public function testResize() { - $size = new Size(300, 200); + $size = new Rectangle(300, 200); $resizer = new Resizer(); $resizer->toWidth(150); $result = $resizer->resize($size); - $this->assertEquals(150, $result->getWidth()); - $this->assertEquals(200, $result->getHeight()); + $this->assertEquals(150, $result->width()); + $this->assertEquals(200, $result->height()); - $size = new Size(300, 200); + $size = new Rectangle(300, 200); $resizer = new Resizer(); $resizer->toWidth(20); $resizer->toHeight(10); $result = $resizer->resize($size); - $this->assertEquals(20, $result->getWidth()); - $this->assertEquals(10, $result->getHeight()); + $this->assertEquals(20, $result->width()); + $this->assertEquals(10, $result->height()); - $size = new Size(300, 200); + $size = new Rectangle(300, 200); $resizer = new Resizer(width: 150); $result = $resizer->resize($size); - $this->assertEquals(150, $result->getWidth()); - $this->assertEquals(200, $result->getHeight()); + $this->assertEquals(150, $result->width()); + $this->assertEquals(200, $result->height()); - $size = new Size(300, 200); + $size = new Rectangle(300, 200); $resizer = new Resizer(height: 10, width: 20); $result = $resizer->resize($size); - $this->assertEquals(20, $result->getWidth()); - $this->assertEquals(10, $result->getHeight()); + $this->assertEquals(20, $result->width()); + $this->assertEquals(10, $result->height()); } public function testResizeDown() { // 800x600 > 1000x2000 = 800x600 - $size = new Size(800, 600); + $size = new Rectangle(800, 600); $resizer = new Resizer(); $resizer->toWidth(1000); $resizer->toHeight(2000); $result = $resizer->resizeDown($size); - $this->assertEquals(800, $result->getWidth()); - $this->assertEquals(600, $result->getHeight()); + $this->assertEquals(800, $result->width()); + $this->assertEquals(600, $result->height()); // 800x600 > 400x1000 = 400x600 - $size = new Size(800, 600); + $size = new Rectangle(800, 600); $resizer = new Resizer(); $resizer->toWidth(400); $resizer->toHeight(1000); $result = $resizer->resizeDown($size); - $this->assertEquals(400, $result->getWidth()); - $this->assertEquals(600, $result->getHeight()); + $this->assertEquals(400, $result->width()); + $this->assertEquals(600, $result->height()); // 800x600 > 1000x400 = 800x400 - $size = new Size(800, 600); + $size = new Rectangle(800, 600); $resizer = new Resizer(); $resizer->toWidth(1000); $resizer->toHeight(400); $result = $resizer->resizeDown($size); - $this->assertEquals(800, $result->getWidth()); - $this->assertEquals(400, $result->getHeight()); + $this->assertEquals(800, $result->width()); + $this->assertEquals(400, $result->height()); // 800x600 > 400x300 = 400x300 - $size = new Size(800, 600); + $size = new Rectangle(800, 600); $resizer = new Resizer(); $resizer->toWidth(400); $resizer->toHeight(300); $result = $resizer->resizeDown($size); - $this->assertEquals(400, $result->getWidth()); - $this->assertEquals(300, $result->getHeight()); + $this->assertEquals(400, $result->width()); + $this->assertEquals(300, $result->height()); // 800x600 > 1000xnull = 800x600 - $size = new Size(800, 600); + $size = new Rectangle(800, 600); $resizer = new Resizer(); $resizer->toWidth(1000); $result = $resizer->resizeDown($size); - $this->assertEquals(800, $result->getWidth()); - $this->assertEquals(600, $result->getHeight()); + $this->assertEquals(800, $result->width()); + $this->assertEquals(600, $result->height()); // 800x600 > nullx1000 = 800x600 - $size = new Size(800, 600); + $size = new Rectangle(800, 600); $resizer = new Resizer(); $resizer->toHeight(1000); $result = $resizer->resizeDown($size); - $this->assertEquals(800, $result->getWidth()); - $this->assertEquals(600, $result->getHeight()); + $this->assertEquals(800, $result->width()); + $this->assertEquals(600, $result->height()); } public function testScale() { // 800x600 > 1000x2000 = 1000x750 - $size = new Size(800, 600); + $size = new Rectangle(800, 600); $resizer = new Resizer(); $resizer->toWidth(1000); $resizer->toHeight(2000); $result = $resizer->scale($size); - $this->assertEquals(1000, $result->getWidth()); - $this->assertEquals(750, $result->getHeight()); + $this->assertEquals(1000, $result->width()); + $this->assertEquals(750, $result->height()); // 800x600 > 2000x1000 = 1333x1000 - $size = new Size(800, 600); + $size = new Rectangle(800, 600); $resizer = new Resizer(); $resizer->toWidth(2000); $resizer->toHeight(1000); $result = $resizer->scale($size); - $this->assertEquals(1333, $result->getWidth()); - $this->assertEquals(1000, $result->getHeight()); + $this->assertEquals(1333, $result->width()); + $this->assertEquals(1000, $result->height()); // // 800x600 > nullx3000 = 4000x3000 - $size = new Size(800, 600); + $size = new Rectangle(800, 600); $resizer = new Resizer(); $resizer->toHeight(3000); $result = $resizer->scale($size); - $this->assertEquals(4000, $result->getWidth()); - $this->assertEquals(3000, $result->getHeight()); + $this->assertEquals(4000, $result->width()); + $this->assertEquals(3000, $result->height()); // // 800x600 > 8000xnull = 8000x6000 - $size = new Size(800, 600); + $size = new Rectangle(800, 600); $resizer = new Resizer(); $resizer->toWidth(8000); $result = $resizer->scale($size); - $this->assertEquals(8000, $result->getWidth()); - $this->assertEquals(6000, $result->getHeight()); + $this->assertEquals(8000, $result->width()); + $this->assertEquals(6000, $result->height()); // // 800x600 > 100x400 = 100x75 - $size = new Size(800, 600); + $size = new Rectangle(800, 600); $resizer = new Resizer(); $resizer->toWidth(100); $resizer->toHeight(400); $result = $resizer->scale($size); - $this->assertEquals(100, $result->getWidth()); - $this->assertEquals(75, $result->getHeight()); + $this->assertEquals(100, $result->width()); + $this->assertEquals(75, $result->height()); // // 800x600 > 400x100 = 133x100 - $size = new Size(800, 600); + $size = new Rectangle(800, 600); $resizer = new Resizer(); $resizer->toWidth(400); $resizer->toHeight(100); $result = $resizer->scale($size); - $this->assertEquals(133, $result->getWidth()); - $this->assertEquals(100, $result->getHeight()); + $this->assertEquals(133, $result->width()); + $this->assertEquals(100, $result->height()); // // 800x600 > nullx300 = 400x300 - $size = new Size(800, 600); + $size = new Rectangle(800, 600); $resizer = new Resizer(); $resizer->toHeight(300); $result = $resizer->scale($size); - $this->assertEquals(400, $result->getWidth()); - $this->assertEquals(300, $result->getHeight()); + $this->assertEquals(400, $result->width()); + $this->assertEquals(300, $result->height()); // // 800x600 > 80xnull = 80x60 - $size = new Size(800, 600); + $size = new Rectangle(800, 600); $resizer = new Resizer(); $resizer->toWidth(80); $result = $resizer->scale($size); - $this->assertEquals(80, $result->getWidth()); - $this->assertEquals(60, $result->getHeight()); + $this->assertEquals(80, $result->width()); + $this->assertEquals(60, $result->height()); // // 640x480 > 225xnull = 225x169 - $size = new Size(640, 480); + $size = new Rectangle(640, 480); $resizer = new Resizer(); $resizer->toWidth(225); $result = $resizer->scale($size); - $this->assertEquals(225, $result->getWidth()); - $this->assertEquals(169, $result->getHeight()); + $this->assertEquals(225, $result->width()); + $this->assertEquals(169, $result->height()); // // 640x480 > 223xnull = 223x167 - $size = new Size(640, 480); + $size = new Rectangle(640, 480); $resizer = new Resizer(); $resizer->toWidth(223); $result = $resizer->scale($size); - $this->assertEquals(223, $result->getWidth()); - $this->assertEquals(167, $result->getHeight()); + $this->assertEquals(223, $result->width()); + $this->assertEquals(167, $result->height()); // // 600x800 > 300x300 = 225x300 - $size = new Size(600, 800); + $size = new Rectangle(600, 800); $resizer = new Resizer(); $resizer->toWidth(300); $resizer->toHeight(300); $result = $resizer->scale($size); - $this->assertEquals(225, $result->getWidth()); - $this->assertEquals(300, $result->getHeight()); + $this->assertEquals(225, $result->width()); + $this->assertEquals(300, $result->height()); // // 800x600 > 400x10 = 13x10 - $size = new Size(800, 600); + $size = new Rectangle(800, 600); $resizer = new Resizer(); $resizer->toWidth(400); $resizer->toHeight(10); $result = $resizer->scale($size); - $this->assertEquals(13, $result->getWidth()); - $this->assertEquals(10, $result->getHeight()); + $this->assertEquals(13, $result->width()); + $this->assertEquals(10, $result->height()); // // 800x600 > 1000x1200 = 1000x750 - $size = new Size(800, 600); + $size = new Rectangle(800, 600); $resizer = new Resizer(); $resizer->toWidth(1000); $resizer->toHeight(1200); $result = $resizer->scale($size); - $this->assertEquals(1000, $result->getWidth()); - $this->assertEquals(750, $result->getHeight()); + $this->assertEquals(1000, $result->width()); + $this->assertEquals(750, $result->height()); - $size = new Size(12000, 12); + $size = new Rectangle(12000, 12); $resizer = new Resizer(); $resizer->toWidth(4000); $resizer->toHeight(3000); $result = $resizer->scale($size); - $this->assertEquals(4000, $result->getWidth()); - $this->assertEquals(4, $result->getHeight()); + $this->assertEquals(4000, $result->width()); + $this->assertEquals(4, $result->height()); - $size = new Size(12, 12000); + $size = new Rectangle(12, 12000); $resizer = new Resizer(); $resizer->toWidth(4000); $resizer->toHeight(3000); $result = $resizer->scale($size); - $this->assertEquals(3, $result->getWidth()); - $this->assertEquals(3000, $result->getHeight()); + $this->assertEquals(3, $result->width()); + $this->assertEquals(3000, $result->height()); - $size = new Size(12000, 6000); + $size = new Rectangle(12000, 6000); $resizer = new Resizer(); $resizer->toWidth(4000); $resizer->toHeight(3000); $result = $resizer->scale($size); - $this->assertEquals(4000, $result->getWidth()); - $this->assertEquals(2000, $result->getHeight()); + $this->assertEquals(4000, $result->width()); + $this->assertEquals(2000, $result->height()); } public function testScaleDown() { - $size = new Size(800, 600); + $size = new Rectangle(800, 600); $resizer = new Resizer(); $resizer->toWidth(1000); $resizer->toHeight(2000); $result = $resizer->scaleDown($size); - $this->assertEquals(800, $result->getWidth()); - $this->assertEquals(600, $result->getHeight()); + $this->assertEquals(800, $result->width()); + $this->assertEquals(600, $result->height()); - $size = new Size(800, 600); + $size = new Rectangle(800, 600); $resizer = new Resizer(); $resizer->toWidth(1000); $resizer->toHeight(600); $result = $resizer->scaleDown($size); - $this->assertEquals(800, $result->getWidth()); - $this->assertEquals(600, $result->getHeight()); + $this->assertEquals(800, $result->width()); + $this->assertEquals(600, $result->height()); - $size = new Size(800, 600); + $size = new Rectangle(800, 600); $resizer = new Resizer(); $resizer->toWidth(1000); $resizer->toHeight(300); $result = $resizer->scaleDown($size); - $this->assertEquals(400, $result->getWidth()); - $this->assertEquals(300, $result->getHeight()); + $this->assertEquals(400, $result->width()); + $this->assertEquals(300, $result->height()); - $size = new Size(800, 600); + $size = new Rectangle(800, 600); $resizer = new Resizer(); $resizer->toWidth(400); $resizer->toHeight(1000); $result = $resizer->scaleDown($size); - $this->assertEquals(400, $result->getWidth()); - $this->assertEquals(300, $result->getHeight()); + $this->assertEquals(400, $result->width()); + $this->assertEquals(300, $result->height()); - $size = new Size(800, 600); + $size = new Rectangle(800, 600); $resizer = new Resizer(); $resizer->toWidth(400); $result = $resizer->scaleDown($size); - $this->assertEquals(400, $result->getWidth()); - $this->assertEquals(300, $result->getHeight()); + $this->assertEquals(400, $result->width()); + $this->assertEquals(300, $result->height()); - $size = new Size(800, 600); + $size = new Rectangle(800, 600); $resizer = new Resizer(); $resizer->toHeight(300); $result = $resizer->scaleDown($size); - $this->assertEquals(400, $result->getWidth()); - $this->assertEquals(300, $result->getHeight()); + $this->assertEquals(400, $result->width()); + $this->assertEquals(300, $result->height()); - $size = new Size(800, 600); + $size = new Rectangle(800, 600); $resizer = new Resizer(); $resizer->toWidth(1000); $result = $resizer->scaleDown($size); - $this->assertEquals(800, $result->getWidth()); - $this->assertEquals(600, $result->getHeight()); + $this->assertEquals(800, $result->width()); + $this->assertEquals(600, $result->height()); - $size = new Size(800, 600); + $size = new Rectangle(800, 600); $resizer = new Resizer(); $resizer->toHeight(1000); $result = $resizer->scaleDown($size); - $this->assertEquals(800, $result->getWidth()); - $this->assertEquals(600, $result->getHeight()); + $this->assertEquals(800, $result->width()); + $this->assertEquals(600, $result->height()); - $size = new Size(800, 600); + $size = new Rectangle(800, 600); $resizer = new Resizer(); $resizer->toWidth(100); $result = $resizer->scaleDown($size); - $this->assertEquals(100, $result->getWidth()); - $this->assertEquals(75, $result->getHeight()); + $this->assertEquals(100, $result->width()); + $this->assertEquals(75, $result->height()); - $size = new Size(800, 600); + $size = new Rectangle(800, 600); $resizer = new Resizer(); $resizer->toWidth(300); $resizer->toHeight(200); $result = $resizer->scaleDown($size); - $this->assertEquals(267, $result->getWidth()); - $this->assertEquals(200, $result->getHeight()); + $this->assertEquals(267, $result->width()); + $this->assertEquals(200, $result->height()); - $size = new Size(600, 800); + $size = new Rectangle(600, 800); $resizer = new Resizer(); $resizer->toWidth(300); $resizer->toHeight(300); $result = $resizer->scaleDown($size); - $this->assertEquals(225, $result->getWidth()); - $this->assertEquals(300, $result->getHeight()); + $this->assertEquals(225, $result->width()); + $this->assertEquals(300, $result->height()); - $size = new Size(800, 600); + $size = new Rectangle(800, 600); $resizer = new Resizer(); $resizer->toWidth(400); $resizer->toHeight(10); $result = $resizer->scaleDown($size); - $this->assertEquals(13, $result->getWidth()); - $this->assertEquals(10, $result->getHeight()); + $this->assertEquals(13, $result->width()); + $this->assertEquals(10, $result->height()); } /** @@ -373,22 +373,22 @@ class ResizerTest extends TestCase $resizer = new Resizer(); $resizer->toSize($target); $resized = $resizer->cover($origin); - $this->assertEquals($result->getWidth(), $resized->getWidth()); - $this->assertEquals($result->getHeight(), $resized->getHeight()); + $this->assertEquals($result->width(), $resized->width()); + $this->assertEquals($result->height(), $resized->height()); } public function coverDataProvider(): array { return [ - [new Size(800, 600), new Size(100, 100), new Size(133, 100)], - [new Size(800, 600), new Size(200, 100), new Size(200, 150)], - [new Size(800, 600), new Size(100, 200), new Size(267, 200)], - [new Size(800, 600), new Size(2000, 10), new Size(2000, 1500)], - [new Size(800, 600), new Size(10, 2000), new Size(2667, 2000)], - [new Size(800, 600), new Size(800, 600), new Size(800, 600)], - [new Size(400, 300), new Size(120, 120), new Size(160, 120)], - [new Size(600, 800), new Size(100, 100), new Size(100, 133)], - [new Size(100, 100), new Size(800, 600), new Size(800, 800)], + [new Rectangle(800, 600), new Rectangle(100, 100), new Rectangle(133, 100)], + [new Rectangle(800, 600), new Rectangle(200, 100), new Rectangle(200, 150)], + [new Rectangle(800, 600), new Rectangle(100, 200), new Rectangle(267, 200)], + [new Rectangle(800, 600), new Rectangle(2000, 10), new Rectangle(2000, 1500)], + [new Rectangle(800, 600), new Rectangle(10, 2000), new Rectangle(2667, 2000)], + [new Rectangle(800, 600), new Rectangle(800, 600), new Rectangle(800, 600)], + [new Rectangle(400, 300), new Rectangle(120, 120), new Rectangle(160, 120)], + [new Rectangle(600, 800), new Rectangle(100, 100), new Rectangle(100, 133)], + [new Rectangle(100, 100), new Rectangle(800, 600), new Rectangle(800, 800)], ]; } @@ -400,22 +400,22 @@ class ResizerTest extends TestCase $resizer = new Resizer(); $resizer->toSize($target); $resized = $resizer->contain($origin); - $this->assertEquals($result->getWidth(), $resized->getWidth()); - $this->assertEquals($result->getHeight(), $resized->getHeight()); + $this->assertEquals($result->width(), $resized->width()); + $this->assertEquals($result->height(), $resized->height()); } public function containDataProvider(): array { return [ - [new Size(800, 600), new Size(100, 100), new Size(100, 75)], - [new Size(800, 600), new Size(200, 100), new Size(133, 100)], - [new Size(800, 600), new Size(100, 200), new Size(100, 75)], - [new Size(800, 600), new Size(2000, 10), new Size(13, 10)], - [new Size(800, 600), new Size(10, 2000), new Size(10, 8)], - [new Size(800, 600), new Size(800, 600), new Size(800, 600)], - [new Size(400, 300), new Size(120, 120), new Size(120, 90)], - [new Size(600, 800), new Size(100, 100), new Size(75, 100)], - [new Size(100, 100), new Size(800, 600), new Size(600, 600)], + [new Rectangle(800, 600), new Rectangle(100, 100), new Rectangle(100, 75)], + [new Rectangle(800, 600), new Rectangle(200, 100), new Rectangle(133, 100)], + [new Rectangle(800, 600), new Rectangle(100, 200), new Rectangle(100, 75)], + [new Rectangle(800, 600), new Rectangle(2000, 10), new Rectangle(13, 10)], + [new Rectangle(800, 600), new Rectangle(10, 2000), new Rectangle(10, 8)], + [new Rectangle(800, 600), new Rectangle(800, 600), new Rectangle(800, 600)], + [new Rectangle(400, 300), new Rectangle(120, 120), new Rectangle(120, 90)], + [new Rectangle(600, 800), new Rectangle(100, 100), new Rectangle(75, 100)], + [new Rectangle(100, 100), new Rectangle(800, 600), new Rectangle(600, 600)], ]; } @@ -427,23 +427,23 @@ class ResizerTest extends TestCase $resizer = new Resizer(); $resizer->toSize($target); $resized = $resizer->crop($origin, $position); - $this->assertEquals($result->getWidth(), $resized->getWidth()); - $this->assertEquals($result->getHeight(), $resized->getHeight()); - $this->assertEquals($result->getPivot()->getX(), $resized->getPivot()->getX()); - $this->assertEquals($result->getPivot()->getY(), $resized->getPivot()->getY()); + $this->assertEquals($result->width(), $resized->width()); + $this->assertEquals($result->height(), $resized->height()); + $this->assertEquals($result->pivot()->getX(), $resized->pivot()->getX()); + $this->assertEquals($result->pivot()->getY(), $resized->pivot()->getY()); } public function cropDataProvider(): array { return [ - [new Size(800, 600), new Size(100, 100), 'center', new Size(100, 100, new Point(350, 250))], - [new Size(800, 600), new Size(200, 100), 'center', new Size(200, 100, new Point(300, 250))], - [new Size(800, 600), new Size(100, 200), 'center', new Size(100, 200, new Point(350, 200))], - [new Size(800, 600), new Size(2000, 10), 'center', new Size(2000, 10, new Point(-600, 295))], - [new Size(800, 600), new Size(10, 2000), 'center', new Size(10, 2000, new Point(395, -700))], - [new Size(800, 600), new Size(800, 600), 'center', new Size(800, 600, new Point(0, 0))], - [new Size(400, 300), new Size(120, 120), 'center', new Size(120, 120, new Point(140, 90))], - [new Size(600, 800), new Size(100, 100), 'center', new Size(100, 100, new Point(250, 350))], + [new Rectangle(800, 600), new Rectangle(100, 100), 'center', new Rectangle(100, 100, new Point(350, 250))], + [new Rectangle(800, 600), new Rectangle(200, 100), 'center', new Rectangle(200, 100, new Point(300, 250))], + [new Rectangle(800, 600), new Rectangle(100, 200), 'center', new Rectangle(100, 200, new Point(350, 200))], + [new Rectangle(800, 600), new Rectangle(2000, 10), 'center', new Rectangle(2000, 10, new Point(-600, 295))], + [new Rectangle(800, 600), new Rectangle(10, 2000), 'center', new Rectangle(10, 2000, new Point(395, -700))], + [new Rectangle(800, 600), new Rectangle(800, 600), 'center', new Rectangle(800, 600, new Point(0, 0))], + [new Rectangle(400, 300), new Rectangle(120, 120), 'center', new Rectangle(120, 120, new Point(140, 90))], + [new Rectangle(600, 800), new Rectangle(100, 100), 'center', new Rectangle(100, 100, new Point(250, 350))], ]; } } diff --git a/tests/Geometry/SizeTest.php b/tests/Geometry/SizeTest.php deleted file mode 100644 index c11031f8..00000000 --- a/tests/Geometry/SizeTest.php +++ /dev/null @@ -1,332 +0,0 @@ -assertInstanceOf(Size::class, $size); - $this->assertInstanceOf(Point::class, $size->getPivot()); - $this->assertEquals(300, $size->getWidth()); - $this->assertEquals(200, $size->getHeight()); - } - - public function testCompareSizes(): void - { - $size1 = new Size(300, 200); - $size2 = new Size(300, 200); - $size2a = new Size(300, 200, new Point(1, 1)); - $size2b = new Size(300, 200, new Point(1, 1)); - $size3 = new Size(300, 201); - $size4 = new Size(301, 200); - - $this->assertTrue($size1 == $size2); - $this->assertTrue($size2a == $size2b); - $this->assertFalse($size2 == $size2a); - $this->assertFalse($size2 == $size3); - $this->assertFalse($size2 == $size4); - $this->assertFalse($size3 == $size4); - } - - public function testSetGetWidth() - { - $size = new Size(800, 600); - $this->assertEquals(800, $size->getWidth()); - $result = $size->setWidth(30); - $this->assertEquals(30, $size->getWidth()); - $this->assertInstanceOf(Size::class, $result); - } - - public function testSetGetHeight() - { - $size = new Size(800, 600); - $this->assertEquals(600, $size->getHeight()); - $result = $size->setHeight(30); - $this->assertEquals(30, $size->getHeight()); - $this->assertInstanceOf(Size::class, $result); - } - - public function testSetGetPivot(): void - { - $size = new Size(800, 600); - $pivot = $size->getPivot(); - $this->assertInstanceOf(Point::class, $pivot); - $this->assertEquals(0, $pivot->getX()); - $result = $size->setPivot(new Point(10, 0)); - $this->assertInstanceOf(Size::class, $result); - $this->assertEquals(10, $size->getPivot()->getX()); - } - - public function testGetAspectRatio() - { - $size = new Size(800, 600); - $this->assertEquals(1.33333333333, $size->getAspectRatio()); - - $size = new Size(100, 100); - $this->assertEquals(1, $size->getAspectRatio()); - - $size = new Size(1920, 1080); - $this->assertEquals(1.777777777778, $size->getAspectRatio()); - } - - public function testFitsInto() - { - $box = new Size(800, 600); - $fits = $box->fitsInto(new Size(100, 100)); - $this->assertFalse($fits); - - $box = new Size(800, 600); - $fits = $box->fitsInto(new Size(1000, 100)); - $this->assertFalse($fits); - - $box = new Size(800, 600); - $fits = $box->fitsInto(new Size(100, 1000)); - $this->assertFalse($fits); - - $box = new Size(800, 600); - $fits = $box->fitsInto(new Size(800, 600)); - $this->assertTrue($fits); - - $box = new Size(800, 600); - $fits = $box->fitsInto(new Size(1000, 1000)); - $this->assertTrue($fits); - - $box = new Size(100, 100); - $fits = $box->fitsInto(new Size(800, 600)); - $this->assertTrue($fits); - - $box = new Size(100, 100); - $fits = $box->fitsInto(new Size(80, 60)); - $this->assertFalse($fits); - } - - public function testIsLandscape() - { - $box = new Size(100, 100); - $this->assertFalse($box->isLandscape()); - - $box = new Size(100, 200); - $this->assertFalse($box->isLandscape()); - - $box = new Size(300, 200); - $this->assertTrue($box->isLandscape()); - } - - public function testIsPortrait() - { - $box = new Size(100, 100); - $this->assertFalse($box->isPortrait()); - - $box = new Size(200, 100); - $this->assertFalse($box->isPortrait()); - - $box = new Size(200, 300); - $this->assertTrue($box->isPortrait()); - } - - public function testAlignPivot(): void - { - $box = new Size(640, 480); - $this->assertEquals(0, $box->getPivot()->getX()); - $this->assertEquals(0, $box->getPivot()->getY()); - - $box->alignPivot('top-left', 3, 3); - $this->assertEquals(3, $box->getPivot()->getX()); - $this->assertEquals(3, $box->getPivot()->getY()); - - $box->alignPivot('top', 3, 3); - $this->assertEquals(320, $box->getPivot()->getX()); - $this->assertEquals(3, $box->getPivot()->getY()); - - $box->alignPivot('top-right', 3, 3); - $this->assertEquals(637, $box->getPivot()->getX()); - $this->assertEquals(3, $box->getPivot()->getY()); - - $box->alignPivot('left', 3, 3); - $this->assertEquals(3, $box->getPivot()->getX()); - $this->assertEquals(240, $box->getPivot()->getY()); - - $box->alignPivot('center', 3, 3); - $this->assertEquals(323, $box->getPivot()->getX()); - $this->assertEquals(243, $box->getPivot()->getY()); - - $box->alignPivot('right', 3, 3); - $this->assertEquals(637, $box->getPivot()->getX()); - $this->assertEquals(240, $box->getPivot()->getY()); - - $box->alignPivot('bottom-left', 3, 3); - $this->assertEquals(3, $box->getPivot()->getX()); - $this->assertEquals(477, $box->getPivot()->getY()); - - $box->alignPivot('bottom', 3, 3); - $this->assertEquals(320, $box->getPivot()->getX()); - $this->assertEquals(477, $box->getPivot()->getY()); - - $result = $box->alignPivot('bottom-right', 3, 3); - $this->assertEquals(637, $box->getPivot()->getX()); - $this->assertEquals(477, $box->getPivot()->getY()); - - $this->assertInstanceOf(Size::class, $result); - } - - public function testAlignPivotTo(): void - { - $container = new Size(800, 600); - $size = new Size(200, 100); - $size->alignPivotTo($container, 'center'); - $this->assertEquals(300, $size->getPivot()->getX()); - $this->assertEquals(250, $size->getPivot()->getY()); - - $container = new Size(800, 600); - $size = new Size(100, 100); - $size->alignPivotTo($container, 'center'); - $this->assertEquals(350, $size->getPivot()->getX()); - $this->assertEquals(250, $size->getPivot()->getY()); - - $container = new Size(800, 600); - $size = new Size(800, 600); - $size->alignPivotTo($container, 'center'); - $this->assertEquals(0, $size->getPivot()->getX()); - $this->assertEquals(0, $size->getPivot()->getY()); - - $container = new Size(100, 100); - $size = new Size(800, 600); - $size->alignPivotTo($container, 'center'); - $this->assertEquals(-350, $size->getPivot()->getX()); - $this->assertEquals(-250, $size->getPivot()->getY()); - - $container = new Size(100, 100); - $size = new Size(800, 600); - $size->alignPivotTo($container, 'bottom-right'); - $this->assertEquals(-700, $size->getPivot()->getX()); - $this->assertEquals(-500, $size->getPivot()->getY()); - } - - public function testgetRelativePositionTo(): void - { - $container = new Size(800, 600); - $input = new Size(200, 100); - $container->alignPivot('top-left'); - $input->alignPivot('top-left'); - $pos = $container->getRelativePositionTo($input); - $this->assertEquals(0, $pos->getX()); - $this->assertEquals(0, $pos->getY()); - - $container = new Size(800, 600); - $input = new Size(200, 100); - $container->alignPivot('center'); - $input->alignPivot('top-left'); - $pos = $container->getRelativePositionTo($input); - $this->assertEquals(400, $pos->getX()); - $this->assertEquals(300, $pos->getY()); - - $container = new Size(800, 600); - $input = new Size(200, 100); - $container->alignPivot('bottom-right'); - $input->alignPivot('top-right'); - $pos = $container->getRelativePositionTo($input); - $this->assertEquals(600, $pos->getX()); - $this->assertEquals(600, $pos->getY()); - - $container = new Size(800, 600); - $input = new Size(200, 100); - $container->alignPivot('center'); - $input->alignPivot('center'); - $pos = $container->getRelativePositionTo($input); - $this->assertEquals(300, $pos->getX()); - $this->assertEquals(250, $pos->getY()); - - $container = new Size(100, 200); - $input = new Size(100, 100); - $container->alignPivot('center'); - $input->alignPivot('center'); - $pos = $container->getRelativePositionTo($input); - $this->assertEquals(0, $pos->getX()); - $this->assertEquals(50, $pos->getY()); - } - - public function testToPolygon(): void - { - $size = new Size(300, 200); - $poly = $size->toPolygon(); - $this->assertInstanceOf(Polygon::class, $poly); - $this->assertCount(4, $poly); - $this->assertEquals(0, $poly[0]->getX()); - $this->assertEquals(0, $poly[0]->getY()); - $this->assertEquals(300, $poly[1]->getX()); - $this->assertEquals(0, $poly[1]->getY()); - $this->assertEquals(300, $poly[2]->getX()); - $this->assertEquals(-200, $poly[2]->getY()); - $this->assertEquals(0, $poly[3]->getX()); - $this->assertEquals(-200, $poly[3]->getY()); - } - - public function testResize(): void - { - $size = new Size(300, 200); - $result = $size->resize(120, 150); - $this->assertInstanceOf(Size::class, $result); - - $size = new Size(300, 200); - $result = $size->resize(height: 100); - $this->assertInstanceOf(Size::class, $result); - } - - public function testResizeDown(): void - { - $size = new Size(300, 200); - $result = $size->resizeDown(120, 150); - $this->assertInstanceOf(Size::class, $result); - - $size = new Size(300, 200); - $result = $size->resizeDown(height: 100); - $this->assertInstanceOf(Size::class, $result); - } - - public function testScale(): void - { - $size = new Size(300, 200); - $result = $size->scale(120, 150); - $this->assertInstanceOf(Size::class, $result); - - $size = new Size(300, 200); - $result = $size->scale(height: 100); - $this->assertInstanceOf(Size::class, $result); - } - - public function testScaleDown(): void - { - $size = new Size(300, 200); - $result = $size->scaleDown(120, 150); - $this->assertInstanceOf(Size::class, $result); - - $size = new Size(300, 200); - $result = $size->scaleDown(height: 100); - $this->assertInstanceOf(Size::class, $result); - } - - public function testCover(): void - { - $size = new Size(300, 200); - $result = $size->cover(120, 150); - $this->assertInstanceOf(Size::class, $result); - } - - public function testContain(): void - { - $size = new Size(100, 100); - $result = $size->contain(800, 600); - $this->assertInstanceOf(Size::class, $result); - $this->assertEquals(600, $result->getWidth()); - $this->assertEquals(600, $result->getHeight()); - } -} From 2c4a6921d864d109945250c4d0f068b3182955ae Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Tue, 19 Jul 2022 19:28:32 +0200 Subject: [PATCH 204/476] Rename Rectangle::alignPivot() to movePivot() --- src/Drivers/Gd/Modifiers/PlaceModifier.php | 4 +- .../Imagick/Modifiers/PlaceModifier.php | 4 +- src/Geometry/Rectangle.php | 10 ++--- src/Geometry/Tools/Resizer.php | 2 +- src/Interfaces/SizeInterface.php | 2 +- tests/Geometry/RectangleTest.php | 38 +++++++++---------- 6 files changed, 29 insertions(+), 31 deletions(-) diff --git a/src/Drivers/Gd/Modifiers/PlaceModifier.php b/src/Drivers/Gd/Modifiers/PlaceModifier.php index b90233c9..1c35c435 100644 --- a/src/Drivers/Gd/Modifiers/PlaceModifier.php +++ b/src/Drivers/Gd/Modifiers/PlaceModifier.php @@ -54,8 +54,8 @@ class PlaceModifier implements ModifierInterface protected function getPosition(ImageInterface $image, ImageInterface $watermark): PointInterface { - $image_size = $image->getSize()->alignPivot($this->position, $this->offset_x, $this->offset_y); - $watermark_size = $watermark->getSize()->alignPivot($this->position); + $image_size = $image->getSize()->movePivot($this->position, $this->offset_x, $this->offset_y); + $watermark_size = $watermark->getSize()->movePivot($this->position); return $image_size->getRelativePositionTo($watermark_size); } diff --git a/src/Drivers/Imagick/Modifiers/PlaceModifier.php b/src/Drivers/Imagick/Modifiers/PlaceModifier.php index 894ba153..d6181c42 100644 --- a/src/Drivers/Imagick/Modifiers/PlaceModifier.php +++ b/src/Drivers/Imagick/Modifiers/PlaceModifier.php @@ -47,8 +47,8 @@ class PlaceModifier implements ModifierInterface protected function getPosition(ImageInterface $image, Image $watermark): PointInterface { - $image_size = $image->getSize()->alignPivot($this->position, $this->offset_x, $this->offset_y); - $watermark_size = $watermark->getSize()->alignPivot($this->position); + $image_size = $image->getSize()->movePivot($this->position, $this->offset_x, $this->offset_y); + $watermark_size = $watermark->getSize()->movePivot($this->position); return $image_size->getRelativePositionTo($watermark_size); } diff --git a/src/Geometry/Rectangle.php b/src/Geometry/Rectangle.php index 088ed268..57f9dc0b 100644 --- a/src/Geometry/Rectangle.php +++ b/src/Geometry/Rectangle.php @@ -49,7 +49,7 @@ class Rectangle extends Polygon implements SizeInterface } /** - * Aligns current size's pivot point to given position + * Move current pivot of current rectangle to given position * and moves point automatically by offset. * * @param string $position @@ -57,8 +57,7 @@ class Rectangle extends Polygon implements SizeInterface * @param int $offset_y * @return Rectangle */ - // TODO: rename method to movePivot - public function alignPivot(string $position, int $offset_x = 0, int $offset_y = 0): self + public function movePivot(string $position, int $offset_x = 0, int $offset_y = 0): self { switch (strtolower($position)) { case 'top': @@ -136,13 +135,12 @@ class Rectangle extends Polygon implements SizeInterface return $this; } - // TODO: rename to alignPivot public function alignPivotTo(SizeInterface $size, string $position): self { $reference = new self($size->width(), $size->height()); - $reference->alignPivot($position); + $reference->movePivot($position); - $this->alignPivot($position)->withPivot( + $this->movePivot($position)->withPivot( $reference->getRelativePositionTo($this) ); diff --git a/src/Geometry/Tools/Resizer.php b/src/Geometry/Tools/Resizer.php index e90d87b5..62f0f0db 100644 --- a/src/Geometry/Tools/Resizer.php +++ b/src/Geometry/Tools/Resizer.php @@ -241,7 +241,7 @@ class Resizer public function crop(SizeInterface $size, string $position = 'top-left'): SizeInterface { return $this->resize($size)->alignPivotTo( - $size->alignPivot($position), + $size->movePivot($position), $position ); } diff --git a/src/Interfaces/SizeInterface.php b/src/Interfaces/SizeInterface.php index 94579afa..c8a29b8e 100644 --- a/src/Interfaces/SizeInterface.php +++ b/src/Interfaces/SizeInterface.php @@ -14,7 +14,7 @@ interface SizeInterface public function fitsInto(SizeInterface $size): bool; public function isLandscape(): bool; public function isPortrait(): bool; - public function alignPivot(string $position, int $offset_x = 0, int $offset_y = 0): SizeInterface; + public function movePivot(string $position, int $offset_x = 0, int $offset_y = 0): SizeInterface; public function alignPivotTo(SizeInterface $size, string $position): SizeInterface; public function getRelativePositionTo(SizeInterface $size): PointInterface; public function resize(?int $width = null, ?int $height = null): SizeInterface; diff --git a/tests/Geometry/RectangleTest.php b/tests/Geometry/RectangleTest.php index 9c42922e..ddc9e102 100644 --- a/tests/Geometry/RectangleTest.php +++ b/tests/Geometry/RectangleTest.php @@ -123,39 +123,39 @@ class RectangleTest extends TestCase $this->assertEquals(0, $box->pivot()->getX()); $this->assertEquals(0, $box->pivot()->getY()); - $box->alignPivot('top-left', 3, 3); + $box->movePivot('top-left', 3, 3); $this->assertEquals(3, $box->pivot()->getX()); $this->assertEquals(3, $box->pivot()->getY()); - $box->alignPivot('top', 3, 3); + $box->movePivot('top', 3, 3); $this->assertEquals(320, $box->pivot()->getX()); $this->assertEquals(3, $box->pivot()->getY()); - $box->alignPivot('top-right', 3, 3); + $box->movePivot('top-right', 3, 3); $this->assertEquals(637, $box->pivot()->getX()); $this->assertEquals(3, $box->pivot()->getY()); - $box->alignPivot('left', 3, 3); + $box->movePivot('left', 3, 3); $this->assertEquals(3, $box->pivot()->getX()); $this->assertEquals(240, $box->pivot()->getY()); - $box->alignPivot('center', 3, 3); + $box->movePivot('center', 3, 3); $this->assertEquals(323, $box->pivot()->getX()); $this->assertEquals(243, $box->pivot()->getY()); - $box->alignPivot('right', 3, 3); + $box->movePivot('right', 3, 3); $this->assertEquals(637, $box->pivot()->getX()); $this->assertEquals(240, $box->pivot()->getY()); - $box->alignPivot('bottom-left', 3, 3); + $box->movePivot('bottom-left', 3, 3); $this->assertEquals(3, $box->pivot()->getX()); $this->assertEquals(477, $box->pivot()->getY()); - $box->alignPivot('bottom', 3, 3); + $box->movePivot('bottom', 3, 3); $this->assertEquals(320, $box->pivot()->getX()); $this->assertEquals(477, $box->pivot()->getY()); - $result = $box->alignPivot('bottom-right', 3, 3); + $result = $box->movePivot('bottom-right', 3, 3); $this->assertEquals(637, $box->pivot()->getX()); $this->assertEquals(477, $box->pivot()->getY()); @@ -199,40 +199,40 @@ class RectangleTest extends TestCase { $container = new Rectangle(800, 600); $input = new Rectangle(200, 100); - $container->alignPivot('top-left'); - $input->alignPivot('top-left'); + $container->movePivot('top-left'); + $input->movePivot('top-left'); $pos = $container->getRelativePositionTo($input); $this->assertEquals(0, $pos->getX()); $this->assertEquals(0, $pos->getY()); $container = new Rectangle(800, 600); $input = new Rectangle(200, 100); - $container->alignPivot('center'); - $input->alignPivot('top-left'); + $container->movePivot('center'); + $input->movePivot('top-left'); $pos = $container->getRelativePositionTo($input); $this->assertEquals(400, $pos->getX()); $this->assertEquals(300, $pos->getY()); $container = new Rectangle(800, 600); $input = new Rectangle(200, 100); - $container->alignPivot('bottom-right'); - $input->alignPivot('top-right'); + $container->movePivot('bottom-right'); + $input->movePivot('top-right'); $pos = $container->getRelativePositionTo($input); $this->assertEquals(600, $pos->getX()); $this->assertEquals(600, $pos->getY()); $container = new Rectangle(800, 600); $input = new Rectangle(200, 100); - $container->alignPivot('center'); - $input->alignPivot('center'); + $container->movePivot('center'); + $input->movePivot('center'); $pos = $container->getRelativePositionTo($input); $this->assertEquals(300, $pos->getX()); $this->assertEquals(250, $pos->getY()); $container = new Rectangle(100, 200); $input = new Rectangle(100, 100); - $container->alignPivot('center'); - $input->alignPivot('center'); + $container->movePivot('center'); + $input->movePivot('center'); $pos = $container->getRelativePositionTo($input); $this->assertEquals(0, $pos->getX()); $this->assertEquals(50, $pos->getY()); From 16cc19a5d0ac0c19df3e684d213fec0e4ff34201 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Tue, 19 Jul 2022 19:29:13 +0200 Subject: [PATCH 205/476] Remove unused methods from Polygon::class - Polygon::alignPivot() - Polygon::valignPivot() --- src/Geometry/Polygon.php | 63 ---------------------------------- tests/Geometry/PolygonTest.php | 36 ------------------- 2 files changed, 99 deletions(-) diff --git a/src/Geometry/Polygon.php b/src/Geometry/Polygon.php index f7d1eef3..71291f61 100644 --- a/src/Geometry/Polygon.php +++ b/src/Geometry/Polygon.php @@ -254,69 +254,6 @@ class Polygon implements Countable, ArrayAccess ); } - /** - * Align pivot point to given horizontal position - * - * @param string $position - * @return Polygon - */ - public function alignPivot(string $position): self - { - switch (strtolower($position)) { - case 'center': - $this->pivot->setX( - intval(($this->getMostRightPoint()->getX() + $this->getMostLeftPoint()->getX()) / 2) - ); - break; - - case 'right': - $this->pivot->setX( - $this->getMostRightPoint()->getX() - ); - break; - - case 'left': - $this->pivot->setX( - $this->getMostLeftPoint()->getX() - ); - break; - } - - return $this; - } - - /** - * Align pivot point to given vertical position - * - * @param string $position - * @return Polygon - */ - public function valignPivot(string $position): self - { - switch (strtolower($position)) { - case 'center': - case 'middle': - $this->pivot->setY( - intval(($this->getMostTopPoint()->getY() + $this->getMostBottomPoint()->getY()) / 2) - ); - break; - - case 'top': - $this->pivot->setY( - $this->getMostTopPoint()->getY() - ); - break; - - case 'bottom': - $this->pivot->setY( - $this->getMostBottomPoint()->getY() - ); - break; - } - - return $this; - } - /** * Align all points of polygon horizontally to given position around pivot point * diff --git a/tests/Geometry/PolygonTest.php b/tests/Geometry/PolygonTest.php index 3ed904de..8428a0cf 100644 --- a/tests/Geometry/PolygonTest.php +++ b/tests/Geometry/PolygonTest.php @@ -125,42 +125,6 @@ class PolygonTest extends TestCase $this->assertInstanceOf(Point::class, $poly->getPivotPoint()); } - public function testAlignPivot(): void - { - $poly = new Polygon([ - new Point(12, 45), - new Point(-24, -49), - new Point(3, 566), - ]); - - $this->assertEquals(0, $poly->getPivotPoint()->getX()); - $this->assertEquals(0, $poly->getPivotPoint()->getY()); - - $result = $poly->alignPivot('center'); - $this->assertInstanceOf(Polygon::class, $result); - - $this->assertEquals(-6, $result->getPivotPoint()->getX()); - $this->assertEquals(0, $result->getPivotPoint()->getY()); - } - - public function testValignPivot(): void - { - $poly = new Polygon([ - new Point(12, 45), - new Point(-24, -50), - new Point(3, 566), - ]); - - $this->assertEquals(0, $poly->getPivotPoint()->getX()); - $this->assertEquals(0, $poly->getPivotPoint()->getY()); - - $result = $poly->valignPivot('middle'); - $this->assertInstanceOf(Polygon::class, $result); - - $this->assertEquals(0, $result->getPivotPoint()->getX()); - $this->assertEquals(258, $result->getPivotPoint()->getY()); - } - public function testGetMostLeftPoint(): void { $poly = new Polygon([ From b30fcc4a6ae90c7540553367e2150aee38dae16c Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Wed, 20 Jul 2022 16:02:49 +0200 Subject: [PATCH 206/476] Rename Resizer::class to RectangleResizer::class --- src/Geometry/Rectangle.php | 6 +- .../{Resizer.php => RectangleResizer.php} | 2 +- ...sizerTest.php => RectangleResizerTest.php} | 116 +++++++++--------- 3 files changed, 62 insertions(+), 62 deletions(-) rename src/Geometry/Tools/{Resizer.php => RectangleResizer.php} (99%) rename tests/Geometry/{ResizerTest.php => RectangleResizerTest.php} (84%) diff --git a/src/Geometry/Rectangle.php b/src/Geometry/Rectangle.php index 57f9dc0b..8e0ba2b7 100644 --- a/src/Geometry/Rectangle.php +++ b/src/Geometry/Rectangle.php @@ -2,7 +2,7 @@ namespace Intervention\Image\Geometry; -use Intervention\Image\Geometry\Tools\Resizer; +use Intervention\Image\Geometry\Tools\RectangleResizer; use Intervention\Image\Interfaces\PointInterface; use Intervention\Image\Interfaces\SizeInterface; @@ -200,9 +200,9 @@ class Rectangle extends Polygon implements SizeInterface return $this->width() < $this->height(); } - protected function getResizer(?int $width = null, ?int $height = null): Resizer + protected function getResizer(?int $width = null, ?int $height = null): RectangleResizer { - return new Resizer($width, $height); + return new RectangleResizer($width, $height); } public function resize(?int $width = null, ?int $height = null): SizeInterface diff --git a/src/Geometry/Tools/Resizer.php b/src/Geometry/Tools/RectangleResizer.php similarity index 99% rename from src/Geometry/Tools/Resizer.php rename to src/Geometry/Tools/RectangleResizer.php index 62f0f0db..63dbf6a0 100644 --- a/src/Geometry/Tools/Resizer.php +++ b/src/Geometry/Tools/RectangleResizer.php @@ -6,7 +6,7 @@ use Intervention\Image\Exceptions\GeometryException; use Intervention\Image\Geometry\Rectangle; use Intervention\Image\Interfaces\SizeInterface; -class Resizer +class RectangleResizer { public function __construct( protected ?int $width = null, diff --git a/tests/Geometry/ResizerTest.php b/tests/Geometry/RectangleResizerTest.php similarity index 84% rename from tests/Geometry/ResizerTest.php rename to tests/Geometry/RectangleResizerTest.php index 47f4fea1..ea505fba 100644 --- a/tests/Geometry/ResizerTest.php +++ b/tests/Geometry/RectangleResizerTest.php @@ -4,61 +4,61 @@ namespace Intervention\Image\Tests\Geometry; use Intervention\Image\Geometry\Point; use Intervention\Image\Geometry\Rectangle; -use Intervention\Image\Geometry\Tools\Resizer; +use Intervention\Image\Geometry\Tools\RectangleResizer; use PHPUnit\Framework\TestCase; /** - * @covers \Intervention\Image\Geometry\Resizer + * @covers \Intervention\Image\Geometry\Tools\RectangleResizer */ -class ResizerTest extends TestCase +class RectangleRectangleResizerTest extends TestCase { public function testMake(): void { - $resizer = Resizer::to(); - $this->assertInstanceOf(Resizer::class, $resizer); + $resizer = RectangleResizer::to(); + $this->assertInstanceOf(RectangleResizer::class, $resizer); - $resizer = Resizer::to(height: 100); - $this->assertInstanceOf(Resizer::class, $resizer); + $resizer = RectangleResizer::to(height: 100); + $this->assertInstanceOf(RectangleResizer::class, $resizer); - $resizer = Resizer::to(100); - $this->assertInstanceOf(Resizer::class, $resizer); + $resizer = RectangleResizer::to(100); + $this->assertInstanceOf(RectangleResizer::class, $resizer); - $resizer = Resizer::to(100, 100); - $this->assertInstanceOf(Resizer::class, $resizer); + $resizer = RectangleResizer::to(100, 100); + $this->assertInstanceOf(RectangleResizer::class, $resizer); } public function testToWidth(): void { - $resizer = new Resizer(); + $resizer = new RectangleResizer(); $result = $resizer->toWidth(100); - $this->assertInstanceOf(Resizer::class, $result); + $this->assertInstanceOf(RectangleResizer::class, $result); } public function testToHeight(): void { - $resizer = new Resizer(); + $resizer = new RectangleResizer(); $result = $resizer->toHeight(100); - $this->assertInstanceOf(Resizer::class, $result); + $this->assertInstanceOf(RectangleResizer::class, $result); } public function testToSize(): void { - $resizer = new Resizer(); + $resizer = new RectangleResizer(); $resizer = $resizer->toSize(new Rectangle(200, 100)); - $this->assertInstanceOf(Resizer::class, $resizer); + $this->assertInstanceOf(RectangleResizer::class, $resizer); } public function testResize() { $size = new Rectangle(300, 200); - $resizer = new Resizer(); + $resizer = new RectangleResizer(); $resizer->toWidth(150); $result = $resizer->resize($size); $this->assertEquals(150, $result->width()); $this->assertEquals(200, $result->height()); $size = new Rectangle(300, 200); - $resizer = new Resizer(); + $resizer = new RectangleResizer(); $resizer->toWidth(20); $resizer->toHeight(10); $result = $resizer->resize($size); @@ -66,13 +66,13 @@ class ResizerTest extends TestCase $this->assertEquals(10, $result->height()); $size = new Rectangle(300, 200); - $resizer = new Resizer(width: 150); + $resizer = new RectangleResizer(width: 150); $result = $resizer->resize($size); $this->assertEquals(150, $result->width()); $this->assertEquals(200, $result->height()); $size = new Rectangle(300, 200); - $resizer = new Resizer(height: 10, width: 20); + $resizer = new RectangleResizer(height: 10, width: 20); $result = $resizer->resize($size); $this->assertEquals(20, $result->width()); $this->assertEquals(10, $result->height()); @@ -82,7 +82,7 @@ class ResizerTest extends TestCase { // 800x600 > 1000x2000 = 800x600 $size = new Rectangle(800, 600); - $resizer = new Resizer(); + $resizer = new RectangleResizer(); $resizer->toWidth(1000); $resizer->toHeight(2000); $result = $resizer->resizeDown($size); @@ -91,7 +91,7 @@ class ResizerTest extends TestCase // 800x600 > 400x1000 = 400x600 $size = new Rectangle(800, 600); - $resizer = new Resizer(); + $resizer = new RectangleResizer(); $resizer->toWidth(400); $resizer->toHeight(1000); $result = $resizer->resizeDown($size); @@ -100,7 +100,7 @@ class ResizerTest extends TestCase // 800x600 > 1000x400 = 800x400 $size = new Rectangle(800, 600); - $resizer = new Resizer(); + $resizer = new RectangleResizer(); $resizer->toWidth(1000); $resizer->toHeight(400); $result = $resizer->resizeDown($size); @@ -109,7 +109,7 @@ class ResizerTest extends TestCase // 800x600 > 400x300 = 400x300 $size = new Rectangle(800, 600); - $resizer = new Resizer(); + $resizer = new RectangleResizer(); $resizer->toWidth(400); $resizer->toHeight(300); $result = $resizer->resizeDown($size); @@ -118,7 +118,7 @@ class ResizerTest extends TestCase // 800x600 > 1000xnull = 800x600 $size = new Rectangle(800, 600); - $resizer = new Resizer(); + $resizer = new RectangleResizer(); $resizer->toWidth(1000); $result = $resizer->resizeDown($size); $this->assertEquals(800, $result->width()); @@ -126,7 +126,7 @@ class ResizerTest extends TestCase // 800x600 > nullx1000 = 800x600 $size = new Rectangle(800, 600); - $resizer = new Resizer(); + $resizer = new RectangleResizer(); $resizer->toHeight(1000); $result = $resizer->resizeDown($size); $this->assertEquals(800, $result->width()); @@ -137,7 +137,7 @@ class ResizerTest extends TestCase { // 800x600 > 1000x2000 = 1000x750 $size = new Rectangle(800, 600); - $resizer = new Resizer(); + $resizer = new RectangleResizer(); $resizer->toWidth(1000); $resizer->toHeight(2000); $result = $resizer->scale($size); @@ -146,7 +146,7 @@ class ResizerTest extends TestCase // 800x600 > 2000x1000 = 1333x1000 $size = new Rectangle(800, 600); - $resizer = new Resizer(); + $resizer = new RectangleResizer(); $resizer->toWidth(2000); $resizer->toHeight(1000); $result = $resizer->scale($size); @@ -155,7 +155,7 @@ class ResizerTest extends TestCase // // 800x600 > nullx3000 = 4000x3000 $size = new Rectangle(800, 600); - $resizer = new Resizer(); + $resizer = new RectangleResizer(); $resizer->toHeight(3000); $result = $resizer->scale($size); $this->assertEquals(4000, $result->width()); @@ -163,7 +163,7 @@ class ResizerTest extends TestCase // // 800x600 > 8000xnull = 8000x6000 $size = new Rectangle(800, 600); - $resizer = new Resizer(); + $resizer = new RectangleResizer(); $resizer->toWidth(8000); $result = $resizer->scale($size); $this->assertEquals(8000, $result->width()); @@ -171,7 +171,7 @@ class ResizerTest extends TestCase // // 800x600 > 100x400 = 100x75 $size = new Rectangle(800, 600); - $resizer = new Resizer(); + $resizer = new RectangleResizer(); $resizer->toWidth(100); $resizer->toHeight(400); $result = $resizer->scale($size); @@ -180,7 +180,7 @@ class ResizerTest extends TestCase // // 800x600 > 400x100 = 133x100 $size = new Rectangle(800, 600); - $resizer = new Resizer(); + $resizer = new RectangleResizer(); $resizer->toWidth(400); $resizer->toHeight(100); $result = $resizer->scale($size); @@ -189,7 +189,7 @@ class ResizerTest extends TestCase // // 800x600 > nullx300 = 400x300 $size = new Rectangle(800, 600); - $resizer = new Resizer(); + $resizer = new RectangleResizer(); $resizer->toHeight(300); $result = $resizer->scale($size); $this->assertEquals(400, $result->width()); @@ -197,7 +197,7 @@ class ResizerTest extends TestCase // // 800x600 > 80xnull = 80x60 $size = new Rectangle(800, 600); - $resizer = new Resizer(); + $resizer = new RectangleResizer(); $resizer->toWidth(80); $result = $resizer->scale($size); $this->assertEquals(80, $result->width()); @@ -205,7 +205,7 @@ class ResizerTest extends TestCase // // 640x480 > 225xnull = 225x169 $size = new Rectangle(640, 480); - $resizer = new Resizer(); + $resizer = new RectangleResizer(); $resizer->toWidth(225); $result = $resizer->scale($size); $this->assertEquals(225, $result->width()); @@ -213,7 +213,7 @@ class ResizerTest extends TestCase // // 640x480 > 223xnull = 223x167 $size = new Rectangle(640, 480); - $resizer = new Resizer(); + $resizer = new RectangleResizer(); $resizer->toWidth(223); $result = $resizer->scale($size); $this->assertEquals(223, $result->width()); @@ -221,7 +221,7 @@ class ResizerTest extends TestCase // // 600x800 > 300x300 = 225x300 $size = new Rectangle(600, 800); - $resizer = new Resizer(); + $resizer = new RectangleResizer(); $resizer->toWidth(300); $resizer->toHeight(300); $result = $resizer->scale($size); @@ -230,7 +230,7 @@ class ResizerTest extends TestCase // // 800x600 > 400x10 = 13x10 $size = new Rectangle(800, 600); - $resizer = new Resizer(); + $resizer = new RectangleResizer(); $resizer->toWidth(400); $resizer->toHeight(10); $result = $resizer->scale($size); @@ -239,7 +239,7 @@ class ResizerTest extends TestCase // // 800x600 > 1000x1200 = 1000x750 $size = new Rectangle(800, 600); - $resizer = new Resizer(); + $resizer = new RectangleResizer(); $resizer->toWidth(1000); $resizer->toHeight(1200); $result = $resizer->scale($size); @@ -247,7 +247,7 @@ class ResizerTest extends TestCase $this->assertEquals(750, $result->height()); $size = new Rectangle(12000, 12); - $resizer = new Resizer(); + $resizer = new RectangleResizer(); $resizer->toWidth(4000); $resizer->toHeight(3000); $result = $resizer->scale($size); @@ -255,7 +255,7 @@ class ResizerTest extends TestCase $this->assertEquals(4, $result->height()); $size = new Rectangle(12, 12000); - $resizer = new Resizer(); + $resizer = new RectangleResizer(); $resizer->toWidth(4000); $resizer->toHeight(3000); $result = $resizer->scale($size); @@ -263,7 +263,7 @@ class ResizerTest extends TestCase $this->assertEquals(3000, $result->height()); $size = new Rectangle(12000, 6000); - $resizer = new Resizer(); + $resizer = new RectangleResizer(); $resizer->toWidth(4000); $resizer->toHeight(3000); $result = $resizer->scale($size); @@ -274,7 +274,7 @@ class ResizerTest extends TestCase public function testScaleDown() { $size = new Rectangle(800, 600); - $resizer = new Resizer(); + $resizer = new RectangleResizer(); $resizer->toWidth(1000); $resizer->toHeight(2000); $result = $resizer->scaleDown($size); @@ -282,7 +282,7 @@ class ResizerTest extends TestCase $this->assertEquals(600, $result->height()); $size = new Rectangle(800, 600); - $resizer = new Resizer(); + $resizer = new RectangleResizer(); $resizer->toWidth(1000); $resizer->toHeight(600); $result = $resizer->scaleDown($size); @@ -290,7 +290,7 @@ class ResizerTest extends TestCase $this->assertEquals(600, $result->height()); $size = new Rectangle(800, 600); - $resizer = new Resizer(); + $resizer = new RectangleResizer(); $resizer->toWidth(1000); $resizer->toHeight(300); $result = $resizer->scaleDown($size); @@ -298,7 +298,7 @@ class ResizerTest extends TestCase $this->assertEquals(300, $result->height()); $size = new Rectangle(800, 600); - $resizer = new Resizer(); + $resizer = new RectangleResizer(); $resizer->toWidth(400); $resizer->toHeight(1000); $result = $resizer->scaleDown($size); @@ -306,42 +306,42 @@ class ResizerTest extends TestCase $this->assertEquals(300, $result->height()); $size = new Rectangle(800, 600); - $resizer = new Resizer(); + $resizer = new RectangleResizer(); $resizer->toWidth(400); $result = $resizer->scaleDown($size); $this->assertEquals(400, $result->width()); $this->assertEquals(300, $result->height()); $size = new Rectangle(800, 600); - $resizer = new Resizer(); + $resizer = new RectangleResizer(); $resizer->toHeight(300); $result = $resizer->scaleDown($size); $this->assertEquals(400, $result->width()); $this->assertEquals(300, $result->height()); $size = new Rectangle(800, 600); - $resizer = new Resizer(); + $resizer = new RectangleResizer(); $resizer->toWidth(1000); $result = $resizer->scaleDown($size); $this->assertEquals(800, $result->width()); $this->assertEquals(600, $result->height()); $size = new Rectangle(800, 600); - $resizer = new Resizer(); + $resizer = new RectangleResizer(); $resizer->toHeight(1000); $result = $resizer->scaleDown($size); $this->assertEquals(800, $result->width()); $this->assertEquals(600, $result->height()); $size = new Rectangle(800, 600); - $resizer = new Resizer(); + $resizer = new RectangleResizer(); $resizer->toWidth(100); $result = $resizer->scaleDown($size); $this->assertEquals(100, $result->width()); $this->assertEquals(75, $result->height()); $size = new Rectangle(800, 600); - $resizer = new Resizer(); + $resizer = new RectangleResizer(); $resizer->toWidth(300); $resizer->toHeight(200); $result = $resizer->scaleDown($size); @@ -349,7 +349,7 @@ class ResizerTest extends TestCase $this->assertEquals(200, $result->height()); $size = new Rectangle(600, 800); - $resizer = new Resizer(); + $resizer = new RectangleResizer(); $resizer->toWidth(300); $resizer->toHeight(300); $result = $resizer->scaleDown($size); @@ -357,7 +357,7 @@ class ResizerTest extends TestCase $this->assertEquals(300, $result->height()); $size = new Rectangle(800, 600); - $resizer = new Resizer(); + $resizer = new RectangleResizer(); $resizer->toWidth(400); $resizer->toHeight(10); $result = $resizer->scaleDown($size); @@ -370,7 +370,7 @@ class ResizerTest extends TestCase */ public function testCover($origin, $target, $result): void { - $resizer = new Resizer(); + $resizer = new RectangleResizer(); $resizer->toSize($target); $resized = $resizer->cover($origin); $this->assertEquals($result->width(), $resized->width()); @@ -397,7 +397,7 @@ class ResizerTest extends TestCase */ public function testContain($origin, $target, $result): void { - $resizer = new Resizer(); + $resizer = new RectangleResizer(); $resizer->toSize($target); $resized = $resizer->contain($origin); $this->assertEquals($result->width(), $resized->width()); @@ -424,7 +424,7 @@ class ResizerTest extends TestCase */ public function testCrop($origin, $target, $position, $result): void { - $resizer = new Resizer(); + $resizer = new RectangleResizer(); $resizer->toSize($target); $resized = $resizer->crop($origin, $position); $this->assertEquals($result->width(), $resized->width()); From 321ac466f3ebf4d4b8b3f23c4ab6f8d8cf48715c Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Wed, 20 Jul 2022 16:03:11 +0200 Subject: [PATCH 207/476] Remote unused trait --- src/Traits/CanResizeGeometrically.php | 14 -------------- 1 file changed, 14 deletions(-) delete mode 100644 src/Traits/CanResizeGeometrically.php diff --git a/src/Traits/CanResizeGeometrically.php b/src/Traits/CanResizeGeometrically.php deleted file mode 100644 index 61db53cc..00000000 --- a/src/Traits/CanResizeGeometrically.php +++ /dev/null @@ -1,14 +0,0 @@ - Date: Wed, 20 Jul 2022 16:45:18 +0200 Subject: [PATCH 208/476] Consolidate method naming --- src/Drivers/Abstract/AbstractFont.php | 4 +- .../Modifiers/AbstractFitModifier.php | 4 +- src/Drivers/Gd/Font.php | 4 +- src/Drivers/Gd/Modifiers/FillModifier.php | 4 +- src/Drivers/Gd/Modifiers/FitModifier.php | 20 +-- src/Drivers/Gd/Modifiers/ResizeModifier.php | 20 +-- src/Drivers/Imagick/Image.php | 4 +- src/Drivers/Imagick/Modifiers/FitModifier.php | 12 +- .../Imagick/Modifiers/PixelateModifier.php | 6 +- .../Imagick/Modifiers/ResizeModifier.php | 4 +- src/Geometry/Polygon.php | 32 ++-- src/Geometry/Rectangle.php | 50 +++--- src/Geometry/Tools/RectangleResizer.php | 84 ++++----- src/Interfaces/SizeInterface.php | 12 +- src/Typography/Line.php | 2 +- src/Typography/TextBlock.php | 2 +- tests/Drivers/Abstract/AbstractImageTest.php | 8 +- .../Modifiers/AbstractFitModifierTest.php | 16 +- .../Modifiers/AbstractPadModifierTest.php | 16 +- tests/Drivers/Gd/FrameTest.php | 4 +- tests/Geometry/PolygonTest.php | 12 +- tests/Geometry/RectangleResizerTest.php | 168 +++++++++--------- tests/Geometry/RectangleTest.php | 82 ++++----- tests/Typography/TextBlockTest.php | 8 +- 24 files changed, 289 insertions(+), 289 deletions(-) diff --git a/src/Drivers/Abstract/AbstractFont.php b/src/Drivers/Abstract/AbstractFont.php index 6f2530a3..94ef0c4d 100644 --- a/src/Drivers/Abstract/AbstractFont.php +++ b/src/Drivers/Abstract/AbstractFont.php @@ -121,11 +121,11 @@ abstract class AbstractFont implements FontInterface public function capHeight(): int { - return $this->getBoxSize('T')->height(); + return $this->getBoxSize('T')->getHeight(); } public function fontSizeInPixels(): int { - return $this->getBoxSize('Hy')->height(); + return $this->getBoxSize('Hy')->getHeight(); } } diff --git a/src/Drivers/Abstract/Modifiers/AbstractFitModifier.php b/src/Drivers/Abstract/Modifiers/AbstractFitModifier.php index 3c38bf8f..3ba1bacb 100644 --- a/src/Drivers/Abstract/Modifiers/AbstractFitModifier.php +++ b/src/Drivers/Abstract/Modifiers/AbstractFitModifier.php @@ -22,8 +22,8 @@ abstract class AbstractFitModifier $crop = new Rectangle($this->width, $this->height); $crop = $crop->contain( - $imagesize->width(), - $imagesize->height() + $imagesize->getWidth(), + $imagesize->getHeight() )->alignPivotTo($imagesize, $this->position); return $crop; diff --git a/src/Drivers/Gd/Font.php b/src/Drivers/Gd/Font.php index 0371b962..21cf3f24 100644 --- a/src/Drivers/Gd/Font.php +++ b/src/Drivers/Gd/Font.php @@ -26,8 +26,8 @@ class Font extends AbstractFont $box = new Rectangle(0, 0); $chars = mb_strlen($text); if ($chars > 0) { - $box->withWidth($chars * $this->getGdFontWidth()); - $box->withHeight($this->getGdFontHeight()); + $box->setWidth($chars * $this->getGdFontWidth()); + $box->setHeight($this->getGdFontHeight()); } return $box; } diff --git a/src/Drivers/Gd/Modifiers/FillModifier.php b/src/Drivers/Gd/Modifiers/FillModifier.php index 65de556c..0f747342 100644 --- a/src/Drivers/Gd/Modifiers/FillModifier.php +++ b/src/Drivers/Gd/Modifiers/FillModifier.php @@ -45,8 +45,8 @@ class FillModifier implements ModifierInterface $frame->getCore(), 0, 0, - $frame->getSize()->width() - 1, - $frame->getSize()->height() - 1, + $frame->getSize()->getWidth() - 1, + $frame->getSize()->getHeight() - 1, $this->color->toInt() ); } diff --git a/src/Drivers/Gd/Modifiers/FitModifier.php b/src/Drivers/Gd/Modifiers/FitModifier.php index 9da6944f..cf55b8c0 100644 --- a/src/Drivers/Gd/Modifiers/FitModifier.php +++ b/src/Drivers/Gd/Modifiers/FitModifier.php @@ -26,8 +26,8 @@ class FitModifier extends AbstractFitModifier implements ModifierInterface { // create new image $modified = imagecreatetruecolor( - $resize->width(), - $resize->height() + $resize->getWidth(), + $resize->getHeight() ); // get current image @@ -51,20 +51,20 @@ class FitModifier extends AbstractFitModifier implements ModifierInterface $current, 0, 0, - $crop->pivot()->getX(), - $crop->pivot()->getY(), - $resize->width(), - $resize->height(), - $crop->width(), - $crop->height() + $crop->getPivot()->getX(), + $crop->getPivot()->getY(), + $resize->getWidth(), + $resize->getHeight(), + $crop->getWidth(), + $crop->getHeight() ); imagedestroy($current); if ($transIndex != -1) { // @todo refactor because of duplication imagecolortransparent($modified, $transIndex); - for ($y = 0; $y < $resize->height(); ++$y) { - for ($x = 0; $x < $resize->width(); ++$x) { + for ($y = 0; $y < $resize->getHeight(); ++$y) { + for ($x = 0; $x < $resize->getWidth(); ++$x) { if (((imagecolorat($modified, $x, $y) >> 24) & 0x7F) >= 100) { imagesetpixel( $modified, diff --git a/src/Drivers/Gd/Modifiers/ResizeModifier.php b/src/Drivers/Gd/Modifiers/ResizeModifier.php index 2b91e1c6..512491f5 100644 --- a/src/Drivers/Gd/Modifiers/ResizeModifier.php +++ b/src/Drivers/Gd/Modifiers/ResizeModifier.php @@ -34,8 +34,8 @@ class ResizeModifier implements ModifierInterface { // create new image $modified = imagecreatetruecolor( - $resizeTo->width(), - $resizeTo->height() + $resizeTo->getWidth(), + $resizeTo->getHeight() ); // get current image @@ -57,22 +57,22 @@ class ResizeModifier implements ModifierInterface imagecopyresampled( $modified, $current, - $resizeTo->pivot()->getX(), - $resizeTo->pivot()->getY(), + $resizeTo->getPivot()->getX(), + $resizeTo->getPivot()->getY(), 0, 0, - $resizeTo->width(), - $resizeTo->height(), - $frame->getSize()->width(), - $frame->getSize()->height() + $resizeTo->getWidth(), + $resizeTo->getHeight(), + $frame->getSize()->getWidth(), + $frame->getSize()->getHeight() ); imagedestroy($current); if ($transIndex != -1) { // @todo refactor because of duplication imagecolortransparent($modified, $transIndex); - for ($y = 0; $y < $resizeTo->height(); ++$y) { - for ($x = 0; $x < $resizeTo->width(); ++$x) { + for ($y = 0; $y < $resizeTo->getHeight(); ++$y) { + for ($x = 0; $x < $resizeTo->getWidth(); ++$x) { if (((imagecolorat($modified, $x, $y) >> 24) & 0x7F) >= 100) { imagesetpixel( $modified, diff --git a/src/Drivers/Imagick/Image.php b/src/Drivers/Imagick/Image.php index 2bcbdec5..906322af 100644 --- a/src/Drivers/Imagick/Image.php +++ b/src/Drivers/Imagick/Image.php @@ -44,8 +44,8 @@ class Image extends AbstractImage implements ImageInterface, Iterator $size = $frame->getSize(); $imagick->setImagePage( - $size->width(), - $size->height(), + $size->getWidth(), + $size->getHeight(), $frame->getOffsetLeft(), $frame->getOffsetTop() ); diff --git a/src/Drivers/Imagick/Modifiers/FitModifier.php b/src/Drivers/Imagick/Modifiers/FitModifier.php index 910d86e5..f5465c5f 100644 --- a/src/Drivers/Imagick/Modifiers/FitModifier.php +++ b/src/Drivers/Imagick/Modifiers/FitModifier.php @@ -15,15 +15,15 @@ class FitModifier extends AbstractFitModifier implements ModifierInterface foreach ($image as $frame) { $frame->getCore()->extentImage( - $crop->width(), - $crop->height(), - $crop->pivot()->getX(), - $crop->pivot()->getY() + $crop->getWidth(), + $crop->getHeight(), + $crop->getPivot()->getX(), + $crop->getPivot()->getY() ); $frame->getCore()->scaleImage( - $resize->width(), - $resize->height() + $resize->getWidth(), + $resize->getHeight() ); } diff --git a/src/Drivers/Imagick/Modifiers/PixelateModifier.php b/src/Drivers/Imagick/Modifiers/PixelateModifier.php index 7552e401..f59196f7 100644 --- a/src/Drivers/Imagick/Modifiers/PixelateModifier.php +++ b/src/Drivers/Imagick/Modifiers/PixelateModifier.php @@ -27,10 +27,10 @@ class PixelateModifier implements ModifierInterface $size = $frame->getSize(); $frame->getCore()->scaleImage( - max(1, ($size->width() / $this->size)), - max(1, ($size->height() / $this->size)) + max(1, ($size->getWidth() / $this->size)), + max(1, ($size->getHeight() / $this->size)) ); - $frame->getCore()->scaleImage($size->width(), $size->height()); + $frame->getCore()->scaleImage($size->getWidth(), $size->getHeight()); } } diff --git a/src/Drivers/Imagick/Modifiers/ResizeModifier.php b/src/Drivers/Imagick/Modifiers/ResizeModifier.php index 83d163c7..41f3ad68 100644 --- a/src/Drivers/Imagick/Modifiers/ResizeModifier.php +++ b/src/Drivers/Imagick/Modifiers/ResizeModifier.php @@ -19,8 +19,8 @@ class ResizeModifier implements ModifierInterface foreach ($image as $frame) { $frame->getCore()->scaleImage( - $resizeTo->width(), - $resizeTo->height() + $resizeTo->getWidth(), + $resizeTo->getHeight() ); } diff --git a/src/Geometry/Polygon.php b/src/Geometry/Polygon.php index 71291f61..732193f0 100644 --- a/src/Geometry/Polygon.php +++ b/src/Geometry/Polygon.php @@ -19,7 +19,7 @@ class Polygon implements Countable, ArrayAccess * * @return Point */ - public function getPivotPoint(): Point + public function getPivot(): Point { return $this->pivot; } @@ -30,7 +30,7 @@ class Polygon implements Countable, ArrayAccess * @param Point $pivot * @return Polygon */ - public function setPivotPoint(Point $pivot): self + public function setPivot(Point $pivot): self { $this->pivot = $pivot; @@ -138,7 +138,7 @@ class Polygon implements Countable, ArrayAccess * * @return int */ - public function width(): int + public function getWidth(): int { return abs($this->getMostLeftPoint()->getX() - $this->getMostRightPoint()->getX()); } @@ -148,7 +148,7 @@ class Polygon implements Countable, ArrayAccess * * @return int */ - public function height(): int + public function getHeight(): int { return abs($this->getMostBottomPoint()->getY() - $this->getMostTopPoint()->getY()); } @@ -249,8 +249,8 @@ class Polygon implements Countable, ArrayAccess public function getCenterPoint(): Point { return new Point( - $this->getMostRightPoint()->getX() - (intval(round($this->width() / 2))), - $this->getMostTopPoint()->getY() - (intval(round($this->height() / 2))) + $this->getMostRightPoint()->getX() - (intval(round($this->getWidth() / 2))), + $this->getMostTopPoint()->getY() - (intval(round($this->getHeight() / 2))) ); } @@ -265,16 +265,16 @@ class Polygon implements Countable, ArrayAccess switch (strtolower($position)) { case 'center': case 'middle': - $diff = ($this->getCenterPoint()->getX() - $this->pivot->getX()); + $diff = ($this->getCenterPoint()->getX() - $this->getPivot()->getX()); break; case 'right': - $diff = ($this->getMostRightPoint()->getX() - $this->pivot->getX()); + $diff = ($this->getMostRightPoint()->getX() - $this->getPivot()->getX()); break; default: case 'left': - $diff = ($this->getMostLeftPoint()->getX() - $this->pivot->getX()); + $diff = ($this->getMostLeftPoint()->getX() - $this->getPivot()->getX()); break; } @@ -296,16 +296,16 @@ class Polygon implements Countable, ArrayAccess switch (strtolower($position)) { case 'center': case 'middle': - $diff = ($this->getCenterPoint()->getY() - $this->pivot->getY()); + $diff = ($this->getCenterPoint()->getY() - $this->getPivot()->getY()); break; case 'top': - $diff = ($this->getMostTopPoint()->getY() - $this->pivot->getY()) - $this->height(); + $diff = ($this->getMostTopPoint()->getY() - $this->getPivot()->getY()) - $this->getHeight(); break; default: case 'bottom': - $diff = ($this->getMostBottomPoint()->getY() - $this->pivot->getY()) + $this->height(); + $diff = ($this->getMostBottomPoint()->getY() - $this->getPivot()->getY()) + $this->getHeight(); break; } @@ -329,16 +329,16 @@ class Polygon implements Countable, ArrayAccess foreach ($this->points as $point) { // translate point to pivot - $point->setX($point->getX() - $this->pivot->getX()); - $point->setY($point->getY() - $this->pivot->getY()); + $point->setX($point->getX() - $this->getPivot()->getX()); + $point->setY($point->getY() - $this->getPivot()->getY()); // rotate point $x = $point->getX() * $cos - $point->getY() * $sin; $y = $point->getX() * $sin + $point->getY() * $cos; // translate point back - $point->setX($x + $this->pivot->getX()); - $point->setY($y + $this->pivot->getY()); + $point->setX($x + $this->getPivot()->getX()); + $point->setY($y + $this->getPivot()->getY()); } return $this; diff --git a/src/Geometry/Rectangle.php b/src/Geometry/Rectangle.php index 8e0ba2b7..3e6e68fc 100644 --- a/src/Geometry/Rectangle.php +++ b/src/Geometry/Rectangle.php @@ -20,7 +20,7 @@ class Rectangle extends Polygon implements SizeInterface $this->addPoint(new Point($this->pivot->getX(), $this->pivot->getY() - $height)); } - public function withWidth(int $width): self + public function setWidth(int $width): self { $this[1]->setX($this[0]->getX() + $width); $this[2]->setX($this[3]->getX() + $width); @@ -28,7 +28,7 @@ class Rectangle extends Polygon implements SizeInterface return $this; } - public function withHeight(int $height): self + public function setHeight(int $height): self { $this[2]->setY($this[1]->getY() + $height); $this[3]->setY($this[0]->getY() + $height); @@ -36,12 +36,12 @@ class Rectangle extends Polygon implements SizeInterface return $this; } - public function pivot(): Point + public function getPivot(): Point { return $this->pivot; } - public function withPivot(PointInterface $pivot): self + public function setPivot(PointInterface $pivot): self { $this->pivot = $pivot; @@ -65,13 +65,13 @@ class Rectangle extends Polygon implements SizeInterface case 'top-middle': case 'center-top': case 'middle-top': - $x = intval($this->width() / 2); + $x = intval($this->getWidth() / 2); $y = 0 + $offset_y; break; case 'top-right': case 'right-top': - $x = $this->width() - $offset_x; + $x = $this->getWidth() - $offset_x; $y = 0 + $offset_y; break; @@ -81,7 +81,7 @@ class Rectangle extends Polygon implements SizeInterface case 'center-left': case 'middle-left': $x = 0 + $offset_x; - $y = intval($this->height() / 2); + $y = intval($this->getHeight() / 2); break; case 'right': @@ -89,14 +89,14 @@ class Rectangle extends Polygon implements SizeInterface case 'right-middle': case 'center-right': case 'middle-right': - $x = $this->width() - $offset_x; - $y = intval($this->height() / 2); + $x = $this->getWidth() - $offset_x; + $y = intval($this->getHeight() / 2); break; case 'bottom-left': case 'left-bottom': $x = 0 + $offset_x; - $y = $this->height() - $offset_y; + $y = $this->getHeight() - $offset_y; break; case 'bottom': @@ -104,22 +104,22 @@ class Rectangle extends Polygon implements SizeInterface case 'bottom-middle': case 'center-bottom': case 'middle-bottom': - $x = intval($this->width() / 2); - $y = $this->height() - $offset_y; + $x = intval($this->getWidth() / 2); + $y = $this->getHeight() - $offset_y; break; case 'bottom-right': case 'right-bottom': - $x = $this->width() - $offset_x; - $y = $this->height() - $offset_y; + $x = $this->getWidth() - $offset_x; + $y = $this->getHeight() - $offset_y; break; case 'center': case 'middle': case 'center-center': case 'middle-middle': - $x = intval($this->width() / 2) + $offset_x; - $y = intval($this->height() / 2) + $offset_y; + $x = intval($this->getWidth() / 2) + $offset_x; + $y = intval($this->getHeight() / 2) + $offset_y; break; default: @@ -137,10 +137,10 @@ class Rectangle extends Polygon implements SizeInterface public function alignPivotTo(SizeInterface $size, string $position): self { - $reference = new self($size->width(), $size->height()); + $reference = new self($size->getWidth(), $size->getHeight()); $reference->movePivot($position); - $this->movePivot($position)->withPivot( + $this->movePivot($position)->setPivot( $reference->getRelativePositionTo($this) ); @@ -157,23 +157,23 @@ class Rectangle extends Polygon implements SizeInterface public function getRelativePositionTo(SizeInterface $rectangle): PointInterface { return new Point( - $this->pivot()->getX() - $rectangle->pivot()->getX(), - $this->pivot()->getY() - $rectangle->pivot()->getY() + $this->getPivot()->getX() - $rectangle->getPivot()->getX(), + $this->getPivot()->getY() - $rectangle->getPivot()->getY() ); } public function getAspectRatio(): float { - return $this->width() / $this->height(); + return $this->getWidth() / $this->getHeight(); } public function fitsInto(SizeInterface $size): bool { - if ($this->width() > $size->width()) { + if ($this->getWidth() > $size->getWidth()) { return false; } - if ($this->height() > $size->height()) { + if ($this->getHeight() > $size->getHeight()) { return false; } @@ -187,7 +187,7 @@ class Rectangle extends Polygon implements SizeInterface */ public function isLandscape(): bool { - return $this->width() > $this->height(); + return $this->getWidth() > $this->getHeight(); } /** @@ -197,7 +197,7 @@ class Rectangle extends Polygon implements SizeInterface */ public function isPortrait(): bool { - return $this->width() < $this->height(); + return $this->getWidth() < $this->getHeight(); } protected function getResizer(?int $width = null, ?int $height = null): RectangleResizer diff --git a/src/Geometry/Tools/RectangleResizer.php b/src/Geometry/Tools/RectangleResizer.php index 63dbf6a0..f71f765b 100644 --- a/src/Geometry/Tools/RectangleResizer.php +++ b/src/Geometry/Tools/RectangleResizer.php @@ -65,8 +65,8 @@ class RectangleResizer public function toSize(SizeInterface $size): self { - $this->width = $size->width(); - $this->height = $size->height(); + $this->width = $size->getWidth(); + $this->height = $size->getHeight(); return $this; } @@ -74,7 +74,7 @@ class RectangleResizer protected function getProportionalWidth(SizeInterface $size): int { if (! $this->hasTargetHeight()) { - return $size->width(); + return $size->getWidth(); } return (int) round($this->height * $size->getAspectRatio()); @@ -83,7 +83,7 @@ class RectangleResizer protected function getProportionalHeight(SizeInterface $size): int { if (! $this->hasTargetWidth()) { - return $size->height(); + return $size->getHeight(); } return (int) round($this->width / $size->getAspectRatio()); @@ -91,14 +91,14 @@ class RectangleResizer public function resize(SizeInterface $size): SizeInterface { - $resized = new Rectangle($size->width(), $size->height()); + $resized = new Rectangle($size->getWidth(), $size->getHeight()); if ($width = $this->getTargetWidth()) { - $resized->withWidth($width); + $resized->setWidth($width); } if ($height = $this->getTargetHeight()) { - $resized->withHeight($height); + $resized->setHeight($height); } return $resized; @@ -106,17 +106,17 @@ class RectangleResizer public function resizeDown(SizeInterface $size): SizeInterface { - $resized = new Rectangle($size->width(), $size->height()); + $resized = new Rectangle($size->getWidth(), $size->getHeight()); if ($width = $this->getTargetWidth()) { - $resized->withWidth( - min($width, $size->width()) + $resized->setWidth( + min($width, $size->getWidth()) ); } if ($height = $this->getTargetHeight()) { - $resized->withHeight( - min($height, $size->height()) + $resized->setHeight( + min($height, $size->getHeight()) ); } @@ -125,23 +125,23 @@ class RectangleResizer public function scale(SizeInterface $size): SizeInterface { - $resized = new Rectangle($size->width(), $size->height()); + $resized = new Rectangle($size->getWidth(), $size->getHeight()); if ($this->hasTargetWidth() && $this->hasTargetHeight()) { - $resized->withWidth(min( + $resized->setWidth(min( $this->getProportionalWidth($size), $this->getTargetWidth() )); - $resized->withHeight(min( + $resized->setHeight(min( $this->getProportionalHeight($size), $this->getTargetHeight() )); } elseif ($this->hasTargetWidth()) { - $resized->withWidth($this->getTargetWidth()); - $resized->withHeight($this->getProportionalHeight($size)); + $resized->setWidth($this->getTargetWidth()); + $resized->setHeight($this->getProportionalHeight($size)); } elseif ($this->hasTargetHeight()) { - $resized->withWidth($this->getProportionalWidth($size)); - $resized->withHeight($this->getTargetHeight()); + $resized->setWidth($this->getProportionalWidth($size)); + $resized->setHeight($this->getTargetHeight()); } return $resized; @@ -149,36 +149,36 @@ class RectangleResizer public function scaleDown(SizeInterface $size): SizeInterface { - $resized = new Rectangle($size->width(), $size->height()); + $resized = new Rectangle($size->getWidth(), $size->getHeight()); if ($this->hasTargetWidth() && $this->hasTargetHeight()) { - $resized->withWidth(min( + $resized->setWidth(min( $this->getProportionalWidth($size), $this->getTargetWidth(), - $size->width() + $size->getWidth() )); - $resized->withHeight(min( + $resized->setHeight(min( $this->getProportionalHeight($size), $this->getTargetHeight(), - $size->height() + $size->getHeight() )); } elseif ($this->hasTargetWidth()) { - $resized->withWidth(min( + $resized->setWidth(min( $this->getTargetWidth(), - $size->width() + $size->getWidth() )); - $resized->withHeight(min( + $resized->setHeight(min( $this->getProportionalHeight($size), - $size->height() + $size->getHeight() )); } elseif ($this->hasTargetHeight()) { - $resized->withWidth(min( + $resized->setWidth(min( $this->getProportionalWidth($size), - $size->width() + $size->getWidth() )); - $resized->withHeight(min( + $resized->setHeight(min( $this->getTargetHeight(), - $size->height() + $size->getHeight() )); } @@ -193,16 +193,16 @@ class RectangleResizer */ public function cover(SizeInterface $size): SizeInterface { - $resized = new Rectangle($size->width(), $size->height()); + $resized = new Rectangle($size->getWidth(), $size->getHeight()); // auto height - $resized->withWidth($this->getTargetWidth()); - $resized->withHeight($this->getProportionalHeight($size)); + $resized->setWidth($this->getTargetWidth()); + $resized->setHeight($this->getProportionalHeight($size)); if ($resized->fitsInto($this->getTargetSize())) { // auto width - $resized->withWidth($this->getProportionalWidth($size)); - $resized->withHeight($this->getTargetHeight()); + $resized->setWidth($this->getProportionalWidth($size)); + $resized->setHeight($this->getTargetHeight()); } return $resized; @@ -216,16 +216,16 @@ class RectangleResizer */ public function contain(SizeInterface $size): SizeInterface { - $resized = new Rectangle($size->width(), $size->height()); + $resized = new Rectangle($size->getWidth(), $size->getHeight()); // auto height - $resized->withWidth($this->getTargetWidth()); - $resized->withHeight($this->getProportionalHeight($size)); + $resized->setWidth($this->getTargetWidth()); + $resized->setHeight($this->getProportionalHeight($size)); if (!$resized->fitsInto($this->getTargetSize())) { // auto width - $resized->withWidth($this->getProportionalWidth($size)); - $resized->withHeight($this->getTargetHeight()); + $resized->setWidth($this->getProportionalWidth($size)); + $resized->setHeight($this->getTargetHeight()); } return $resized; diff --git a/src/Interfaces/SizeInterface.php b/src/Interfaces/SizeInterface.php index c8a29b8e..58a6d412 100644 --- a/src/Interfaces/SizeInterface.php +++ b/src/Interfaces/SizeInterface.php @@ -4,12 +4,12 @@ namespace Intervention\Image\Interfaces; interface SizeInterface { - public function width(): int; - public function height(): int; - public function pivot(): PointInterface; - public function withWidth(int $width): SizeInterface; - public function withHeight(int $height): SizeInterface; - public function withPivot(PointInterface $pivot): SizeInterface; + public function getWidth(): int; + public function getHeight(): int; + public function getPivot(): PointInterface; + public function setWidth(int $width): SizeInterface; + public function setHeight(int $height): SizeInterface; + public function setPivot(PointInterface $pivot): SizeInterface; public function getAspectRatio(): float; public function fitsInto(SizeInterface $size): bool; public function isLandscape(): bool; diff --git a/src/Typography/Line.php b/src/Typography/Line.php index 10e98665..4f846674 100644 --- a/src/Typography/Line.php +++ b/src/Typography/Line.php @@ -28,7 +28,7 @@ class Line public function widthInFont(FontInterface $font): int { - return $font->getBoxSize($this->text)->width(); + return $font->getBoxSize($this->text)->getWidth(); } public function __toString(): string diff --git a/src/Typography/TextBlock.php b/src/Typography/TextBlock.php index 4593d67f..65e10688 100644 --- a/src/Typography/TextBlock.php +++ b/src/Typography/TextBlock.php @@ -28,7 +28,7 @@ class TextBlock extends Collection )); // set pivot - $box->setPivotPoint($pivot); + $box->setPivot($pivot); // align $box->align($font->getAlign()); diff --git a/tests/Drivers/Abstract/AbstractImageTest.php b/tests/Drivers/Abstract/AbstractImageTest.php index 21bdd857..b6546ccb 100644 --- a/tests/Drivers/Abstract/AbstractImageTest.php +++ b/tests/Drivers/Abstract/AbstractImageTest.php @@ -45,16 +45,16 @@ class AbstractImageTest extends TestCase { $img = $this->abstractImageMock(); $this->assertInstanceOf(Rectangle::class, $img->getSize()); - $this->assertEquals(300, $img->getSize()->width()); - $this->assertEquals(200, $img->getSize()->height()); + $this->assertEquals(300, $img->getSize()->getWidth()); + $this->assertEquals(200, $img->getSize()->getHeight()); } public function testSizeAlias(): void { $img = $this->abstractImageMock(); $this->assertInstanceOf(Rectangle::class, $img->getSize()); - $this->assertEquals(300, $img->size()->width()); - $this->assertEquals(200, $img->size()->height()); + $this->assertEquals(300, $img->size()->getWidth()); + $this->assertEquals(200, $img->size()->getHeight()); } public function testModify(): void diff --git a/tests/Drivers/Abstract/Modifiers/AbstractFitModifierTest.php b/tests/Drivers/Abstract/Modifiers/AbstractFitModifierTest.php index 28ebb3f6..f8678f96 100644 --- a/tests/Drivers/Abstract/Modifiers/AbstractFitModifierTest.php +++ b/tests/Drivers/Abstract/Modifiers/AbstractFitModifierTest.php @@ -29,10 +29,10 @@ class AbstractFitModifierTest extends TestCase $image = (new ImageFactory())->newImage($width, $height); $size = $modifier->getCropSize($image); - static::assertSame($expectedWidth, $size->width()); - static::assertSame($expectedHeight, $size->height()); - static::assertSame($expectedX, $size->pivot()->getX()); - static::assertSame($expectedY, $size->pivot()->getY()); + static::assertSame($expectedWidth, $size->getWidth()); + static::assertSame($expectedHeight, $size->getHeight()); + static::assertSame($expectedX, $size->getPivot()->getX()); + static::assertSame($expectedY, $size->getPivot()->getY()); } public function testGetResizeSize(): void @@ -43,10 +43,10 @@ class AbstractFitModifierTest extends TestCase $size = $modifier->getCropSize($image); $resize = $modifier->getResizeSize($size); - static::assertSame(200, $resize->width()); - static::assertSame(100, $resize->height()); - static::assertSame(0, $resize->pivot()->getX()); - static::assertSame(0, $resize->pivot()->getY()); + static::assertSame(200, $resize->getWidth()); + static::assertSame(100, $resize->getHeight()); + static::assertSame(0, $resize->getPivot()->getX()); + static::assertSame(0, $resize->getPivot()->getY()); } private function getModifier(int $width, int $height, string $position): AbstractFitModifier diff --git a/tests/Drivers/Abstract/Modifiers/AbstractPadModifierTest.php b/tests/Drivers/Abstract/Modifiers/AbstractPadModifierTest.php index 860aa546..4308a3bf 100644 --- a/tests/Drivers/Abstract/Modifiers/AbstractPadModifierTest.php +++ b/tests/Drivers/Abstract/Modifiers/AbstractPadModifierTest.php @@ -31,10 +31,10 @@ final class AbstractPadModifierTest extends TestCase $image = (new ImageFactory())->newImage($width, $height); $size = $modifier->getCropSize($image); - static::assertSame($expectedWidth, $size->width()); - static::assertSame($expectedHeight, $size->height()); - static::assertSame($expectedX, $size->pivot()->getX()); - static::assertSame($expectedY, $size->pivot()->getY()); + static::assertSame($expectedWidth, $size->getWidth()); + static::assertSame($expectedHeight, $size->getHeight()); + static::assertSame($expectedX, $size->getPivot()->getX()); + static::assertSame($expectedY, $size->getPivot()->getY()); } public function testGetResizeSize(): void @@ -44,10 +44,10 @@ final class AbstractPadModifierTest extends TestCase $image = (new ImageFactory())->newImage(300, 200); $resize = $modifier->getResizeSize($image); - static::assertSame(200, $resize->width()); - static::assertSame(100, $resize->height()); - static::assertSame(0, $resize->pivot()->getX()); - static::assertSame(0, $resize->pivot()->getY()); + static::assertSame(200, $resize->getWidth()); + static::assertSame(100, $resize->getHeight()); + static::assertSame(0, $resize->getPivot()->getX()); + static::assertSame(0, $resize->getPivot()->getY()); } private function getModifier(int $width, int $height, $background, string $position): AbstractPadModifier diff --git a/tests/Drivers/Gd/FrameTest.php b/tests/Drivers/Gd/FrameTest.php index a27a42d3..1f5dd254 100644 --- a/tests/Drivers/Gd/FrameTest.php +++ b/tests/Drivers/Gd/FrameTest.php @@ -36,10 +36,10 @@ class FrameTest extends TestCase $core1 = imagecreatetruecolor(3, 2); $core2 = imagecreatetruecolor(3, 3); $frame = new Frame($core1); - $this->assertEquals(2, $frame->getSize()->height()); + $this->assertEquals(2, $frame->getSize()->getHeight()); $result = $frame->setCore($core2); $this->assertInstanceOf(Frame::class, $result); - $this->assertEquals(3, $frame->getSize()->height()); + $this->assertEquals(3, $frame->getSize()->getHeight()); } public function testGetSize(): void diff --git a/tests/Geometry/PolygonTest.php b/tests/Geometry/PolygonTest.php index 8428a0cf..c1a4b938 100644 --- a/tests/Geometry/PolygonTest.php +++ b/tests/Geometry/PolygonTest.php @@ -64,7 +64,7 @@ class PolygonTest extends TestCase $this->assertEquals(-100, $result->getY()); } - public function testWidth(): void + public function testGetWidth(): void { $poly = new Polygon([ new Point(12, 45), @@ -72,10 +72,10 @@ class PolygonTest extends TestCase new Point(3, 566), ]); - $this->assertEquals($poly->width(), 35); + $this->assertEquals($poly->getWidth(), 35); } - public function testHeight(): void + public function testGetHeight(): void { $poly = new Polygon([ new Point(12, 45), @@ -83,7 +83,7 @@ class PolygonTest extends TestCase new Point(3, 566), ]); - $this->assertEquals(615, $poly->height()); + $this->assertEquals(615, $poly->getHeight()); $poly = new Polygon([ new Point(250, 207), @@ -92,7 +92,7 @@ class PolygonTest extends TestCase new Point(250, 250), ], new Point(250, 250)); - $this->assertEquals(43, $poly->height()); + $this->assertEquals(43, $poly->getHeight()); } public function testFirst(): void @@ -122,7 +122,7 @@ class PolygonTest extends TestCase public function testGetPivotPoint(): void { $poly = new Polygon(); - $this->assertInstanceOf(Point::class, $poly->getPivotPoint()); + $this->assertInstanceOf(Point::class, $poly->getPivot()); } public function testGetMostLeftPoint(): void diff --git a/tests/Geometry/RectangleResizerTest.php b/tests/Geometry/RectangleResizerTest.php index ea505fba..d45f0207 100644 --- a/tests/Geometry/RectangleResizerTest.php +++ b/tests/Geometry/RectangleResizerTest.php @@ -54,28 +54,28 @@ class RectangleRectangleResizerTest extends TestCase $resizer = new RectangleResizer(); $resizer->toWidth(150); $result = $resizer->resize($size); - $this->assertEquals(150, $result->width()); - $this->assertEquals(200, $result->height()); + $this->assertEquals(150, $result->getWidth()); + $this->assertEquals(200, $result->getHeight()); $size = new Rectangle(300, 200); $resizer = new RectangleResizer(); $resizer->toWidth(20); $resizer->toHeight(10); $result = $resizer->resize($size); - $this->assertEquals(20, $result->width()); - $this->assertEquals(10, $result->height()); + $this->assertEquals(20, $result->getWidth()); + $this->assertEquals(10, $result->getHeight()); $size = new Rectangle(300, 200); $resizer = new RectangleResizer(width: 150); $result = $resizer->resize($size); - $this->assertEquals(150, $result->width()); - $this->assertEquals(200, $result->height()); + $this->assertEquals(150, $result->getWidth()); + $this->assertEquals(200, $result->getHeight()); $size = new Rectangle(300, 200); $resizer = new RectangleResizer(height: 10, width: 20); $result = $resizer->resize($size); - $this->assertEquals(20, $result->width()); - $this->assertEquals(10, $result->height()); + $this->assertEquals(20, $result->getWidth()); + $this->assertEquals(10, $result->getHeight()); } public function testResizeDown() @@ -86,8 +86,8 @@ class RectangleRectangleResizerTest extends TestCase $resizer->toWidth(1000); $resizer->toHeight(2000); $result = $resizer->resizeDown($size); - $this->assertEquals(800, $result->width()); - $this->assertEquals(600, $result->height()); + $this->assertEquals(800, $result->getWidth()); + $this->assertEquals(600, $result->getHeight()); // 800x600 > 400x1000 = 400x600 $size = new Rectangle(800, 600); @@ -95,8 +95,8 @@ class RectangleRectangleResizerTest extends TestCase $resizer->toWidth(400); $resizer->toHeight(1000); $result = $resizer->resizeDown($size); - $this->assertEquals(400, $result->width()); - $this->assertEquals(600, $result->height()); + $this->assertEquals(400, $result->getWidth()); + $this->assertEquals(600, $result->getHeight()); // 800x600 > 1000x400 = 800x400 $size = new Rectangle(800, 600); @@ -104,8 +104,8 @@ class RectangleRectangleResizerTest extends TestCase $resizer->toWidth(1000); $resizer->toHeight(400); $result = $resizer->resizeDown($size); - $this->assertEquals(800, $result->width()); - $this->assertEquals(400, $result->height()); + $this->assertEquals(800, $result->getWidth()); + $this->assertEquals(400, $result->getHeight()); // 800x600 > 400x300 = 400x300 $size = new Rectangle(800, 600); @@ -113,24 +113,24 @@ class RectangleRectangleResizerTest extends TestCase $resizer->toWidth(400); $resizer->toHeight(300); $result = $resizer->resizeDown($size); - $this->assertEquals(400, $result->width()); - $this->assertEquals(300, $result->height()); + $this->assertEquals(400, $result->getWidth()); + $this->assertEquals(300, $result->getHeight()); // 800x600 > 1000xnull = 800x600 $size = new Rectangle(800, 600); $resizer = new RectangleResizer(); $resizer->toWidth(1000); $result = $resizer->resizeDown($size); - $this->assertEquals(800, $result->width()); - $this->assertEquals(600, $result->height()); + $this->assertEquals(800, $result->getWidth()); + $this->assertEquals(600, $result->getHeight()); // 800x600 > nullx1000 = 800x600 $size = new Rectangle(800, 600); $resizer = new RectangleResizer(); $resizer->toHeight(1000); $result = $resizer->resizeDown($size); - $this->assertEquals(800, $result->width()); - $this->assertEquals(600, $result->height()); + $this->assertEquals(800, $result->getWidth()); + $this->assertEquals(600, $result->getHeight()); } public function testScale() @@ -141,8 +141,8 @@ class RectangleRectangleResizerTest extends TestCase $resizer->toWidth(1000); $resizer->toHeight(2000); $result = $resizer->scale($size); - $this->assertEquals(1000, $result->width()); - $this->assertEquals(750, $result->height()); + $this->assertEquals(1000, $result->getWidth()); + $this->assertEquals(750, $result->getHeight()); // 800x600 > 2000x1000 = 1333x1000 $size = new Rectangle(800, 600); @@ -150,24 +150,24 @@ class RectangleRectangleResizerTest extends TestCase $resizer->toWidth(2000); $resizer->toHeight(1000); $result = $resizer->scale($size); - $this->assertEquals(1333, $result->width()); - $this->assertEquals(1000, $result->height()); + $this->assertEquals(1333, $result->getWidth()); + $this->assertEquals(1000, $result->getHeight()); // // 800x600 > nullx3000 = 4000x3000 $size = new Rectangle(800, 600); $resizer = new RectangleResizer(); $resizer->toHeight(3000); $result = $resizer->scale($size); - $this->assertEquals(4000, $result->width()); - $this->assertEquals(3000, $result->height()); + $this->assertEquals(4000, $result->getWidth()); + $this->assertEquals(3000, $result->getHeight()); // // 800x600 > 8000xnull = 8000x6000 $size = new Rectangle(800, 600); $resizer = new RectangleResizer(); $resizer->toWidth(8000); $result = $resizer->scale($size); - $this->assertEquals(8000, $result->width()); - $this->assertEquals(6000, $result->height()); + $this->assertEquals(8000, $result->getWidth()); + $this->assertEquals(6000, $result->getHeight()); // // 800x600 > 100x400 = 100x75 $size = new Rectangle(800, 600); @@ -175,8 +175,8 @@ class RectangleRectangleResizerTest extends TestCase $resizer->toWidth(100); $resizer->toHeight(400); $result = $resizer->scale($size); - $this->assertEquals(100, $result->width()); - $this->assertEquals(75, $result->height()); + $this->assertEquals(100, $result->getWidth()); + $this->assertEquals(75, $result->getHeight()); // // 800x600 > 400x100 = 133x100 $size = new Rectangle(800, 600); @@ -184,40 +184,40 @@ class RectangleRectangleResizerTest extends TestCase $resizer->toWidth(400); $resizer->toHeight(100); $result = $resizer->scale($size); - $this->assertEquals(133, $result->width()); - $this->assertEquals(100, $result->height()); + $this->assertEquals(133, $result->getWidth()); + $this->assertEquals(100, $result->getHeight()); // // 800x600 > nullx300 = 400x300 $size = new Rectangle(800, 600); $resizer = new RectangleResizer(); $resizer->toHeight(300); $result = $resizer->scale($size); - $this->assertEquals(400, $result->width()); - $this->assertEquals(300, $result->height()); + $this->assertEquals(400, $result->getWidth()); + $this->assertEquals(300, $result->getHeight()); // // 800x600 > 80xnull = 80x60 $size = new Rectangle(800, 600); $resizer = new RectangleResizer(); $resizer->toWidth(80); $result = $resizer->scale($size); - $this->assertEquals(80, $result->width()); - $this->assertEquals(60, $result->height()); + $this->assertEquals(80, $result->getWidth()); + $this->assertEquals(60, $result->getHeight()); // // 640x480 > 225xnull = 225x169 $size = new Rectangle(640, 480); $resizer = new RectangleResizer(); $resizer->toWidth(225); $result = $resizer->scale($size); - $this->assertEquals(225, $result->width()); - $this->assertEquals(169, $result->height()); + $this->assertEquals(225, $result->getWidth()); + $this->assertEquals(169, $result->getHeight()); // // 640x480 > 223xnull = 223x167 $size = new Rectangle(640, 480); $resizer = new RectangleResizer(); $resizer->toWidth(223); $result = $resizer->scale($size); - $this->assertEquals(223, $result->width()); - $this->assertEquals(167, $result->height()); + $this->assertEquals(223, $result->getWidth()); + $this->assertEquals(167, $result->getHeight()); // // 600x800 > 300x300 = 225x300 $size = new Rectangle(600, 800); @@ -225,8 +225,8 @@ class RectangleRectangleResizerTest extends TestCase $resizer->toWidth(300); $resizer->toHeight(300); $result = $resizer->scale($size); - $this->assertEquals(225, $result->width()); - $this->assertEquals(300, $result->height()); + $this->assertEquals(225, $result->getWidth()); + $this->assertEquals(300, $result->getHeight()); // // 800x600 > 400x10 = 13x10 $size = new Rectangle(800, 600); @@ -234,8 +234,8 @@ class RectangleRectangleResizerTest extends TestCase $resizer->toWidth(400); $resizer->toHeight(10); $result = $resizer->scale($size); - $this->assertEquals(13, $result->width()); - $this->assertEquals(10, $result->height()); + $this->assertEquals(13, $result->getWidth()); + $this->assertEquals(10, $result->getHeight()); // // 800x600 > 1000x1200 = 1000x750 $size = new Rectangle(800, 600); @@ -243,32 +243,32 @@ class RectangleRectangleResizerTest extends TestCase $resizer->toWidth(1000); $resizer->toHeight(1200); $result = $resizer->scale($size); - $this->assertEquals(1000, $result->width()); - $this->assertEquals(750, $result->height()); + $this->assertEquals(1000, $result->getWidth()); + $this->assertEquals(750, $result->getHeight()); $size = new Rectangle(12000, 12); $resizer = new RectangleResizer(); $resizer->toWidth(4000); $resizer->toHeight(3000); $result = $resizer->scale($size); - $this->assertEquals(4000, $result->width()); - $this->assertEquals(4, $result->height()); + $this->assertEquals(4000, $result->getWidth()); + $this->assertEquals(4, $result->getHeight()); $size = new Rectangle(12, 12000); $resizer = new RectangleResizer(); $resizer->toWidth(4000); $resizer->toHeight(3000); $result = $resizer->scale($size); - $this->assertEquals(3, $result->width()); - $this->assertEquals(3000, $result->height()); + $this->assertEquals(3, $result->getWidth()); + $this->assertEquals(3000, $result->getHeight()); $size = new Rectangle(12000, 6000); $resizer = new RectangleResizer(); $resizer->toWidth(4000); $resizer->toHeight(3000); $result = $resizer->scale($size); - $this->assertEquals(4000, $result->width()); - $this->assertEquals(2000, $result->height()); + $this->assertEquals(4000, $result->getWidth()); + $this->assertEquals(2000, $result->getHeight()); } public function testScaleDown() @@ -278,91 +278,91 @@ class RectangleRectangleResizerTest extends TestCase $resizer->toWidth(1000); $resizer->toHeight(2000); $result = $resizer->scaleDown($size); - $this->assertEquals(800, $result->width()); - $this->assertEquals(600, $result->height()); + $this->assertEquals(800, $result->getWidth()); + $this->assertEquals(600, $result->getHeight()); $size = new Rectangle(800, 600); $resizer = new RectangleResizer(); $resizer->toWidth(1000); $resizer->toHeight(600); $result = $resizer->scaleDown($size); - $this->assertEquals(800, $result->width()); - $this->assertEquals(600, $result->height()); + $this->assertEquals(800, $result->getWidth()); + $this->assertEquals(600, $result->getHeight()); $size = new Rectangle(800, 600); $resizer = new RectangleResizer(); $resizer->toWidth(1000); $resizer->toHeight(300); $result = $resizer->scaleDown($size); - $this->assertEquals(400, $result->width()); - $this->assertEquals(300, $result->height()); + $this->assertEquals(400, $result->getWidth()); + $this->assertEquals(300, $result->getHeight()); $size = new Rectangle(800, 600); $resizer = new RectangleResizer(); $resizer->toWidth(400); $resizer->toHeight(1000); $result = $resizer->scaleDown($size); - $this->assertEquals(400, $result->width()); - $this->assertEquals(300, $result->height()); + $this->assertEquals(400, $result->getWidth()); + $this->assertEquals(300, $result->getHeight()); $size = new Rectangle(800, 600); $resizer = new RectangleResizer(); $resizer->toWidth(400); $result = $resizer->scaleDown($size); - $this->assertEquals(400, $result->width()); - $this->assertEquals(300, $result->height()); + $this->assertEquals(400, $result->getWidth()); + $this->assertEquals(300, $result->getHeight()); $size = new Rectangle(800, 600); $resizer = new RectangleResizer(); $resizer->toHeight(300); $result = $resizer->scaleDown($size); - $this->assertEquals(400, $result->width()); - $this->assertEquals(300, $result->height()); + $this->assertEquals(400, $result->getWidth()); + $this->assertEquals(300, $result->getHeight()); $size = new Rectangle(800, 600); $resizer = new RectangleResizer(); $resizer->toWidth(1000); $result = $resizer->scaleDown($size); - $this->assertEquals(800, $result->width()); - $this->assertEquals(600, $result->height()); + $this->assertEquals(800, $result->getWidth()); + $this->assertEquals(600, $result->getHeight()); $size = new Rectangle(800, 600); $resizer = new RectangleResizer(); $resizer->toHeight(1000); $result = $resizer->scaleDown($size); - $this->assertEquals(800, $result->width()); - $this->assertEquals(600, $result->height()); + $this->assertEquals(800, $result->getWidth()); + $this->assertEquals(600, $result->getHeight()); $size = new Rectangle(800, 600); $resizer = new RectangleResizer(); $resizer->toWidth(100); $result = $resizer->scaleDown($size); - $this->assertEquals(100, $result->width()); - $this->assertEquals(75, $result->height()); + $this->assertEquals(100, $result->getWidth()); + $this->assertEquals(75, $result->getHeight()); $size = new Rectangle(800, 600); $resizer = new RectangleResizer(); $resizer->toWidth(300); $resizer->toHeight(200); $result = $resizer->scaleDown($size); - $this->assertEquals(267, $result->width()); - $this->assertEquals(200, $result->height()); + $this->assertEquals(267, $result->getWidth()); + $this->assertEquals(200, $result->getHeight()); $size = new Rectangle(600, 800); $resizer = new RectangleResizer(); $resizer->toWidth(300); $resizer->toHeight(300); $result = $resizer->scaleDown($size); - $this->assertEquals(225, $result->width()); - $this->assertEquals(300, $result->height()); + $this->assertEquals(225, $result->getWidth()); + $this->assertEquals(300, $result->getHeight()); $size = new Rectangle(800, 600); $resizer = new RectangleResizer(); $resizer->toWidth(400); $resizer->toHeight(10); $result = $resizer->scaleDown($size); - $this->assertEquals(13, $result->width()); - $this->assertEquals(10, $result->height()); + $this->assertEquals(13, $result->getWidth()); + $this->assertEquals(10, $result->getHeight()); } /** @@ -373,8 +373,8 @@ class RectangleRectangleResizerTest extends TestCase $resizer = new RectangleResizer(); $resizer->toSize($target); $resized = $resizer->cover($origin); - $this->assertEquals($result->width(), $resized->width()); - $this->assertEquals($result->height(), $resized->height()); + $this->assertEquals($result->getWidth(), $resized->getWidth()); + $this->assertEquals($result->getHeight(), $resized->getHeight()); } public function coverDataProvider(): array @@ -400,8 +400,8 @@ class RectangleRectangleResizerTest extends TestCase $resizer = new RectangleResizer(); $resizer->toSize($target); $resized = $resizer->contain($origin); - $this->assertEquals($result->width(), $resized->width()); - $this->assertEquals($result->height(), $resized->height()); + $this->assertEquals($result->getWidth(), $resized->getWidth()); + $this->assertEquals($result->getHeight(), $resized->getHeight()); } public function containDataProvider(): array @@ -427,10 +427,10 @@ class RectangleRectangleResizerTest extends TestCase $resizer = new RectangleResizer(); $resizer->toSize($target); $resized = $resizer->crop($origin, $position); - $this->assertEquals($result->width(), $resized->width()); - $this->assertEquals($result->height(), $resized->height()); - $this->assertEquals($result->pivot()->getX(), $resized->pivot()->getX()); - $this->assertEquals($result->pivot()->getY(), $resized->pivot()->getY()); + $this->assertEquals($result->getWidth(), $resized->getWidth()); + $this->assertEquals($result->getHeight(), $resized->getHeight()); + $this->assertEquals($result->getPivot()->getX(), $resized->getPivot()->getX()); + $this->assertEquals($result->getPivot()->getY(), $resized->getPivot()->getY()); } public function cropDataProvider(): array diff --git a/tests/Geometry/RectangleTest.php b/tests/Geometry/RectangleTest.php index ddc9e102..87befb70 100644 --- a/tests/Geometry/RectangleTest.php +++ b/tests/Geometry/RectangleTest.php @@ -19,24 +19,24 @@ class RectangleTest extends TestCase $this->assertEquals(-200, $rectangle[2]->getY()); $this->assertEquals(0, $rectangle[3]->getX()); $this->assertEquals(-200, $rectangle[3]->getY()); - $this->assertEquals(300, $rectangle->width()); - $this->assertEquals(200, $rectangle->height()); + $this->assertEquals(300, $rectangle->getWidth()); + $this->assertEquals(200, $rectangle->getHeight()); } public function testWithWidth(): void { $rectangle = new Rectangle(300, 200); - $this->assertEquals(300, $rectangle->width()); - $rectangle->withWidth(400); - $this->assertEquals(400, $rectangle->width()); + $this->assertEquals(300, $rectangle->getWidth()); + $rectangle->setWidth(400); + $this->assertEquals(400, $rectangle->getWidth()); } public function testWithHeight(): void { $rectangle = new Rectangle(300, 200); - $this->assertEquals(200, $rectangle->height()); - $rectangle->withHeight(800); - $this->assertEquals(800, $rectangle->height()); + $this->assertEquals(200, $rectangle->getHeight()); + $rectangle->setHeight(800); + $this->assertEquals(800, $rectangle->getHeight()); } public function testGetAspectRatio() @@ -109,55 +109,55 @@ class RectangleTest extends TestCase public function testSetGetPivot(): void { $box = new Rectangle(800, 600); - $pivot = $box->pivot(); + $pivot = $box->getPivot(); $this->assertInstanceOf(Point::class, $pivot); $this->assertEquals(0, $pivot->getX()); - $result = $box->withPivot(new Point(10, 0)); + $result = $box->setPivot(new Point(10, 0)); $this->assertInstanceOf(Rectangle::class, $result); - $this->assertEquals(10, $box->pivot()->getX()); + $this->assertEquals(10, $box->getPivot()->getX()); } public function testAlignPivot(): void { $box = new Rectangle(640, 480); - $this->assertEquals(0, $box->pivot()->getX()); - $this->assertEquals(0, $box->pivot()->getY()); + $this->assertEquals(0, $box->getPivot()->getX()); + $this->assertEquals(0, $box->getPivot()->getY()); $box->movePivot('top-left', 3, 3); - $this->assertEquals(3, $box->pivot()->getX()); - $this->assertEquals(3, $box->pivot()->getY()); + $this->assertEquals(3, $box->getPivot()->getX()); + $this->assertEquals(3, $box->getPivot()->getY()); $box->movePivot('top', 3, 3); - $this->assertEquals(320, $box->pivot()->getX()); - $this->assertEquals(3, $box->pivot()->getY()); + $this->assertEquals(320, $box->getPivot()->getX()); + $this->assertEquals(3, $box->getPivot()->getY()); $box->movePivot('top-right', 3, 3); - $this->assertEquals(637, $box->pivot()->getX()); - $this->assertEquals(3, $box->pivot()->getY()); + $this->assertEquals(637, $box->getPivot()->getX()); + $this->assertEquals(3, $box->getPivot()->getY()); $box->movePivot('left', 3, 3); - $this->assertEquals(3, $box->pivot()->getX()); - $this->assertEquals(240, $box->pivot()->getY()); + $this->assertEquals(3, $box->getPivot()->getX()); + $this->assertEquals(240, $box->getPivot()->getY()); $box->movePivot('center', 3, 3); - $this->assertEquals(323, $box->pivot()->getX()); - $this->assertEquals(243, $box->pivot()->getY()); + $this->assertEquals(323, $box->getPivot()->getX()); + $this->assertEquals(243, $box->getPivot()->getY()); $box->movePivot('right', 3, 3); - $this->assertEquals(637, $box->pivot()->getX()); - $this->assertEquals(240, $box->pivot()->getY()); + $this->assertEquals(637, $box->getPivot()->getX()); + $this->assertEquals(240, $box->getPivot()->getY()); $box->movePivot('bottom-left', 3, 3); - $this->assertEquals(3, $box->pivot()->getX()); - $this->assertEquals(477, $box->pivot()->getY()); + $this->assertEquals(3, $box->getPivot()->getX()); + $this->assertEquals(477, $box->getPivot()->getY()); $box->movePivot('bottom', 3, 3); - $this->assertEquals(320, $box->pivot()->getX()); - $this->assertEquals(477, $box->pivot()->getY()); + $this->assertEquals(320, $box->getPivot()->getX()); + $this->assertEquals(477, $box->getPivot()->getY()); $result = $box->movePivot('bottom-right', 3, 3); - $this->assertEquals(637, $box->pivot()->getX()); - $this->assertEquals(477, $box->pivot()->getY()); + $this->assertEquals(637, $box->getPivot()->getX()); + $this->assertEquals(477, $box->getPivot()->getY()); $this->assertInstanceOf(Rectangle::class, $result); } @@ -167,32 +167,32 @@ class RectangleTest extends TestCase $container = new Rectangle(800, 600); $size = new Rectangle(200, 100); $size->alignPivotTo($container, 'center'); - $this->assertEquals(300, $size->pivot()->getX()); - $this->assertEquals(250, $size->pivot()->getY()); + $this->assertEquals(300, $size->getPivot()->getX()); + $this->assertEquals(250, $size->getPivot()->getY()); $container = new Rectangle(800, 600); $size = new Rectangle(100, 100); $size->alignPivotTo($container, 'center'); - $this->assertEquals(350, $size->pivot()->getX()); - $this->assertEquals(250, $size->pivot()->getY()); + $this->assertEquals(350, $size->getPivot()->getX()); + $this->assertEquals(250, $size->getPivot()->getY()); $container = new Rectangle(800, 600); $size = new Rectangle(800, 600); $size->alignPivotTo($container, 'center'); - $this->assertEquals(0, $size->pivot()->getX()); - $this->assertEquals(0, $size->pivot()->getY()); + $this->assertEquals(0, $size->getPivot()->getX()); + $this->assertEquals(0, $size->getPivot()->getY()); $container = new Rectangle(100, 100); $size = new Rectangle(800, 600); $size->alignPivotTo($container, 'center'); - $this->assertEquals(-350, $size->pivot()->getX()); - $this->assertEquals(-250, $size->pivot()->getY()); + $this->assertEquals(-350, $size->getPivot()->getX()); + $this->assertEquals(-250, $size->getPivot()->getY()); $container = new Rectangle(100, 100); $size = new Rectangle(800, 600); $size->alignPivotTo($container, 'bottom-right'); - $this->assertEquals(-700, $size->pivot()->getX()); - $this->assertEquals(-500, $size->pivot()->getY()); + $this->assertEquals(-700, $size->getPivot()->getX()); + $this->assertEquals(-500, $size->getPivot()->getY()); } public function testgetRelativePositionTo(): void diff --git a/tests/Typography/TextBlockTest.php b/tests/Typography/TextBlockTest.php index 6f95d212..8b3f6e3a 100644 --- a/tests/Typography/TextBlockTest.php +++ b/tests/Typography/TextBlockTest.php @@ -71,9 +71,9 @@ class TextBlockTest extends TestCase $font->shouldReceive('capHeight')->andReturn(22); $box = $block->getBoundingBox($font, new Point(10, 15)); - $this->assertEquals(300, $box->width()); - $this->assertEquals(82, $box->height()); - $this->assertEquals(10, $box->getPivotPoint()->getX()); - $this->assertEquals(15, $box->getPivotPoint()->getY()); + $this->assertEquals(300, $box->getWidth()); + $this->assertEquals(82, $box->getHeight()); + $this->assertEquals(10, $box->getPivot()->getX()); + $this->assertEquals(15, $box->getPivot()->getY()); } } From 736d7546588aaef24f272af4d9092cfc77b9a161 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Wed, 20 Jul 2022 17:14:16 +0200 Subject: [PATCH 209/476] Fix classname --- tests/Geometry/RectangleResizerTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Geometry/RectangleResizerTest.php b/tests/Geometry/RectangleResizerTest.php index d45f0207..750d43ac 100644 --- a/tests/Geometry/RectangleResizerTest.php +++ b/tests/Geometry/RectangleResizerTest.php @@ -10,7 +10,7 @@ use PHPUnit\Framework\TestCase; /** * @covers \Intervention\Image\Geometry\Tools\RectangleResizer */ -class RectangleRectangleResizerTest extends TestCase +class RectangleResizerTest extends TestCase { public function testMake(): void { From 50445d78e32249293db86e92808f653504e54864 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Wed, 20 Jul 2022 17:17:08 +0200 Subject: [PATCH 210/476] Removed constructor for AbstractFont --- src/Drivers/Abstract/AbstractFont.php | 7 ------- src/Drivers/Abstract/AbstractImage.php | 5 ++++- src/Traits/CanRunCallback.php | 15 +++++++++++++++ 3 files changed, 19 insertions(+), 8 deletions(-) create mode 100644 src/Traits/CanRunCallback.php diff --git a/src/Drivers/Abstract/AbstractFont.php b/src/Drivers/Abstract/AbstractFont.php index 94ef0c4d..4649cc9c 100644 --- a/src/Drivers/Abstract/AbstractFont.php +++ b/src/Drivers/Abstract/AbstractFont.php @@ -18,13 +18,6 @@ abstract class AbstractFont implements FontInterface protected $valign = 'bottom'; protected $lineHeight = 1.25; - public function __construct(callable $init = null) - { - if (is_callable($init)) { - $init($this); - } - } - public function size(float $size): FontInterface { $this->size = $size; diff --git a/src/Drivers/Abstract/AbstractImage.php b/src/Drivers/Abstract/AbstractImage.php index 4521b84d..1c32b466 100644 --- a/src/Drivers/Abstract/AbstractImage.php +++ b/src/Drivers/Abstract/AbstractImage.php @@ -13,11 +13,13 @@ use Intervention\Image\Interfaces\ModifierInterface; use Intervention\Image\Interfaces\SizeInterface; use Intervention\Image\Traits\CanHandleInput; use Intervention\Image\Traits\CanResolveDriverClass; +use Intervention\Image\Traits\CanRunCallback; abstract class AbstractImage implements ImageInterface { use CanResolveDriverClass; use CanHandleInput; + use CanRunCallback; public function getSize(): SizeInterface { @@ -193,7 +195,8 @@ abstract class AbstractImage implements ImageInterface public function text(string $text, int $x, int $y, ?callable $init = null): ImageInterface { - $font = $this->resolveDriverClass('Font', $init); + $font = $this->runCallback($init, $this->resolveDriverClass('Font')); + $modifier = $this->resolveDriverClass('Modifiers\TextWriter', new Point($x, $y), $font, $text); return $this->modify($modifier); diff --git a/src/Traits/CanRunCallback.php b/src/Traits/CanRunCallback.php new file mode 100644 index 00000000..b2da4b0d --- /dev/null +++ b/src/Traits/CanRunCallback.php @@ -0,0 +1,15 @@ + Date: Wed, 20 Jul 2022 17:24:41 +0200 Subject: [PATCH 211/476] Add method Rectangle::setSize() --- src/Geometry/Rectangle.php | 5 +++++ tests/Geometry/RectangleTest.php | 8 ++++++++ 2 files changed, 13 insertions(+) diff --git a/src/Geometry/Rectangle.php b/src/Geometry/Rectangle.php index 3e6e68fc..1a9cc547 100644 --- a/src/Geometry/Rectangle.php +++ b/src/Geometry/Rectangle.php @@ -20,6 +20,11 @@ class Rectangle extends Polygon implements SizeInterface $this->addPoint(new Point($this->pivot->getX(), $this->pivot->getY() - $height)); } + public function setSize(int $width, int $height): self + { + return $this->setWidth($width)->setHeight($height); + } + public function setWidth(int $width): self { $this[1]->setX($this[0]->getX() + $width); diff --git a/tests/Geometry/RectangleTest.php b/tests/Geometry/RectangleTest.php index 87befb70..858238e2 100644 --- a/tests/Geometry/RectangleTest.php +++ b/tests/Geometry/RectangleTest.php @@ -23,6 +23,14 @@ class RectangleTest extends TestCase $this->assertEquals(200, $rectangle->getHeight()); } + public function testSetSize(): void + { + $rectangle = new Rectangle(300, 200); + $rectangle->setSize(12, 34); + $this->assertEquals(12, $rectangle->getWidth()); + $this->assertEquals(34, $rectangle->getHeight()); + } + public function testWithWidth(): void { $rectangle = new Rectangle(300, 200); From 67307ea8635d149b69a0427750523e7f917e78ea Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Wed, 20 Jul 2022 17:25:24 +0200 Subject: [PATCH 212/476] Rename test methods --- tests/Geometry/RectangleTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/Geometry/RectangleTest.php b/tests/Geometry/RectangleTest.php index 858238e2..a74a8a5e 100644 --- a/tests/Geometry/RectangleTest.php +++ b/tests/Geometry/RectangleTest.php @@ -31,7 +31,7 @@ class RectangleTest extends TestCase $this->assertEquals(34, $rectangle->getHeight()); } - public function testWithWidth(): void + public function testSetWidth(): void { $rectangle = new Rectangle(300, 200); $this->assertEquals(300, $rectangle->getWidth()); @@ -39,7 +39,7 @@ class RectangleTest extends TestCase $this->assertEquals(400, $rectangle->getWidth()); } - public function testWithHeight(): void + public function testSetHeight(): void { $rectangle = new Rectangle(300, 200); $this->assertEquals(200, $rectangle->getHeight()); From efe7d5bb67c1b7ec959d754dc2729f8dbbeb6877 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 23 Jul 2022 10:56:43 +0200 Subject: [PATCH 213/476] Add DrawRectangleModifier --- src/Drivers/Abstract/AbstractImage.php | 17 +++++ .../Modifiers/AbstractDrawModifier.php | 19 +++++ .../Gd/Modifiers/DrawRectangleModifier.php | 71 +++++++++++++++++ .../Modifiers/DrawRectangleModifier.php | 76 +++++++++++++++++++ src/Geometry/Rectangle.php | 33 +++++++- src/Geometry/Traits/HasBackgroundColor.php | 30 ++++++++ src/Geometry/Traits/HasBorder.php | 43 +++++++++++ src/Interfaces/DrawableInterface.php | 17 +++++ 8 files changed, 305 insertions(+), 1 deletion(-) create mode 100644 src/Drivers/Abstract/Modifiers/AbstractDrawModifier.php create mode 100644 src/Drivers/Gd/Modifiers/DrawRectangleModifier.php create mode 100644 src/Drivers/Imagick/Modifiers/DrawRectangleModifier.php create mode 100644 src/Geometry/Traits/HasBackgroundColor.php create mode 100644 src/Geometry/Traits/HasBorder.php create mode 100644 src/Interfaces/DrawableInterface.php diff --git a/src/Drivers/Abstract/AbstractImage.php b/src/Drivers/Abstract/AbstractImage.php index 1c32b466..6626cad3 100644 --- a/src/Drivers/Abstract/AbstractImage.php +++ b/src/Drivers/Abstract/AbstractImage.php @@ -21,6 +21,15 @@ abstract class AbstractImage implements ImageInterface use CanHandleInput; use CanRunCallback; + public function eachFrame(callable $callback): self + { + foreach ($this as $frame) { + $callback($frame); + } + + return $this; + } + public function getSize(): SizeInterface { return new Rectangle($this->getWidth(), $this->getHeight()); @@ -210,6 +219,14 @@ abstract class AbstractImage implements ImageInterface return $this->modify($modifier); } + public function drawRectangle(int $x, int $y, ?callable $init = null): ImageInterface + { + $rectangle = $this->runCallback($init, new Rectangle(0, 0)); + $modifier = $this->resolveDriverClass('Modifiers\DrawRectangleModifier', new Point($x, $y), $rectangle); + + return $this->modify($modifier); + } + public function resize(?int $width = null, ?int $height = null): ImageInterface { return $this->modify( diff --git a/src/Drivers/Abstract/Modifiers/AbstractDrawModifier.php b/src/Drivers/Abstract/Modifiers/AbstractDrawModifier.php new file mode 100644 index 00000000..1dfa0f13 --- /dev/null +++ b/src/Drivers/Abstract/Modifiers/AbstractDrawModifier.php @@ -0,0 +1,19 @@ +eachFrame(function ($frame) { + // draw background + if ($this->rectangle()->hasBackgroundColor()) { + imagefilledrectangle( + $frame->getCore(), + $this->position->getX(), + $this->position->getY(), + $this->position->getX() + $this->rectangle()->bottomRightPoint()->getY(), + $this->position->getY() + $this->rectangle()->bottomRightPoint()->getY(), + $this->getBackgroundColor() + ); + } + + if ($this->rectangle()->hasBorder()) { + // draw border + imagesetthickness($frame->getCore(), $this->rectangle()->getBorderSize()); + imagerectangle( + $frame->getCore(), + $this->position->getX(), + $this->position->getY(), + $this->position->getX() + $this->rectangle()->bottomRightPoint()->getY(), + $this->position->getY() + $this->rectangle()->bottomRightPoint()->getY(), + $this->getBorderColor() + ); + } + }); + + return $image; + } + + private function rectangle(): Rectangle + { + return $this->drawable; + } + + private function getBackgroundColor(): int + { + try { + $color = $this->handleInput($this->rectangle()->getBackgroundColor()); + } catch (DecoderException $e) { + return 2130706432; // transparent + } + + return $color->toInt(); + } + + private function getBorderColor(): int + { + try { + $color = $this->handleInput($this->rectangle()->getBorderColor()); + } catch (DecoderException $e) { + return 2130706432; // transparent + } + + return $color->toInt(); + } +} diff --git a/src/Drivers/Imagick/Modifiers/DrawRectangleModifier.php b/src/Drivers/Imagick/Modifiers/DrawRectangleModifier.php new file mode 100644 index 00000000..bb6f9be0 --- /dev/null +++ b/src/Drivers/Imagick/Modifiers/DrawRectangleModifier.php @@ -0,0 +1,76 @@ +setFillColor($this->getBackgroundColor()); + if ($this->rectangle()->hasBorder()) { + $drawing->setStrokeColor($this->getBorderColor()); + $drawing->setStrokeWidth($this->rectangle()->getBorderSize()); + } + + // build rectangle + $drawing->rectangle( + $this->position->getX(), + $this->position->getY(), + $this->position->getX() + $this->rectangle()->bottomRightPoint()->getX(), + $this->position->getY() + $this->rectangle()->bottomRightPoint()->getY() + ); + + $image->eachFrame(function ($frame) use ($drawing) { + $frame->getCore()->drawImage($drawing); + }); + + return $image; + } + + private function rectangle(): Rectangle + { + return $this->drawable; + } + + private function getBackgroundColor(): ImagickPixel + { + try { + $color = $this->handleInput($this->rectangle()->getBackgroundColor()); + } catch (DecoderException $e) { + $color = null; + } + + return $this->filterImagickPixel($color); + } + + private function getBorderColor(): ImagickPixel + { + try { + $color = $this->handleInput($this->rectangle()->getBorderColor()); + } catch (DecoderException $e) { + $color = null; + } + + return $this->filterImagickPixel($color); + } + + private function filterImagickPixel($color): ImagickPixel + { + if (!is_a($color, Color::class)) { + return new ImagickPixel('transparent'); + } + + return $color->getPixel(); + } +} diff --git a/src/Geometry/Rectangle.php b/src/Geometry/Rectangle.php index 1a9cc547..c7e5b1bb 100644 --- a/src/Geometry/Rectangle.php +++ b/src/Geometry/Rectangle.php @@ -3,11 +3,17 @@ namespace Intervention\Image\Geometry; use Intervention\Image\Geometry\Tools\RectangleResizer; +use Intervention\Image\Geometry\Traits\HasBackgroundColor; +use Intervention\Image\Geometry\Traits\HasBorder; +use Intervention\Image\Interfaces\DrawableInterface; use Intervention\Image\Interfaces\PointInterface; use Intervention\Image\Interfaces\SizeInterface; -class Rectangle extends Polygon implements SizeInterface +class Rectangle extends Polygon implements SizeInterface, DrawableInterface { + use HasBorder; + use HasBackgroundColor; + public function __construct( int $width, int $height, @@ -20,11 +26,26 @@ class Rectangle extends Polygon implements SizeInterface $this->addPoint(new Point($this->pivot->getX(), $this->pivot->getY() - $height)); } + /** + * Set the rectangle dimensions to given width & height + * + * @param int $width + * @param int $height + * @return Rectangle + */ public function setSize(int $width, int $height): self { return $this->setWidth($width)->setHeight($height); } + /** + * Alias of self::setSize() + */ + public function size(int $width, int $height): self + { + return $this->setSize($width, $height); + } + public function setWidth(int $width): self { $this[1]->setX($this[0]->getX() + $width); @@ -205,6 +226,16 @@ class Rectangle extends Polygon implements SizeInterface return $this->getWidth() < $this->getHeight(); } + public function topLeftPoint(): PointInterface + { + return $this->points[0]; + } + + public function bottomRightPoint(): PointInterface + { + return $this->points[2]; + } + protected function getResizer(?int $width = null, ?int $height = null): RectangleResizer { return new RectangleResizer($width, $height); diff --git a/src/Geometry/Traits/HasBackgroundColor.php b/src/Geometry/Traits/HasBackgroundColor.php new file mode 100644 index 00000000..5987a516 --- /dev/null +++ b/src/Geometry/Traits/HasBackgroundColor.php @@ -0,0 +1,30 @@ +setBackgroundColor($color); + } + + public function setBackgroundColor($color): self + { + $this->backgroundColor = $color; + + return $this; + } + + public function getBackgroundColor() + { + return $this->backgroundColor; + } + + public function hasBackgroundColor(): bool + { + return !is_null($this->backgroundColor); + } +} diff --git a/src/Geometry/Traits/HasBorder.php b/src/Geometry/Traits/HasBorder.php new file mode 100644 index 00000000..f9df9900 --- /dev/null +++ b/src/Geometry/Traits/HasBorder.php @@ -0,0 +1,43 @@ +setBorderSize($size)->setBorderColor($color); + } + + public function setBorderSize(int $size): self + { + $this->borderSize = $size; + + return $this; + } + + public function getBorderSize(): int + { + return $this->borderSize; + } + + public function setBorderColor($color): self + { + $this->borderColor = $color; + + return $this; + } + + public function getBorderColor() + { + return $this->borderColor; + } + + public function hasBorder(): bool + { + return $this->borderSize > 0 && !is_null($this->borderColor); + } +} diff --git a/src/Interfaces/DrawableInterface.php b/src/Interfaces/DrawableInterface.php new file mode 100644 index 00000000..065d28de --- /dev/null +++ b/src/Interfaces/DrawableInterface.php @@ -0,0 +1,17 @@ + Date: Sat, 23 Jul 2022 15:58:56 +0200 Subject: [PATCH 214/476] Add drawing methods - Image::drawCircle() - Image::drawEllipse() --- src/Drivers/Abstract/AbstractImage.php | 18 ++++++ .../Modifiers/AbstractDrawModifier.php | 29 +++++++++ .../Gd/Modifiers/DrawEllipseModifier.php | 53 ++++++++++++++++ .../Gd/Modifiers/DrawRectangleModifier.php | 47 +++----------- .../Imagick/Modifiers/DrawEllipseModifier.php | 58 +++++++++++++++++ .../Modifiers/DrawRectangleModifier.php | 63 +++++++------------ src/Geometry/Circle.php | 48 ++++++++++++++ src/Geometry/Ellipse.php | 55 ++++++++++++++++ 8 files changed, 293 insertions(+), 78 deletions(-) create mode 100644 src/Drivers/Gd/Modifiers/DrawEllipseModifier.php create mode 100644 src/Drivers/Imagick/Modifiers/DrawEllipseModifier.php create mode 100644 src/Geometry/Circle.php create mode 100644 src/Geometry/Ellipse.php diff --git a/src/Drivers/Abstract/AbstractImage.php b/src/Drivers/Abstract/AbstractImage.php index 6626cad3..01b702ab 100644 --- a/src/Drivers/Abstract/AbstractImage.php +++ b/src/Drivers/Abstract/AbstractImage.php @@ -4,6 +4,8 @@ namespace Intervention\Image\Drivers\Abstract; use Intervention\Image\Collection; use Intervention\Image\EncodedImage; +use Intervention\Image\Geometry\Circle; +use Intervention\Image\Geometry\Ellipse; use Intervention\Image\Geometry\Point; use Intervention\Image\Geometry\Rectangle; use Intervention\Image\Interfaces\CollectionInterface; @@ -227,6 +229,22 @@ abstract class AbstractImage implements ImageInterface return $this->modify($modifier); } + public function drawEllipse(int $x, int $y, ?callable $init = null): ImageInterface + { + $ellipse = $this->runCallback($init, new Ellipse(0, 0)); + $modifier = $this->resolveDriverClass('Modifiers\DrawEllipseModifier', new Point($x, $y), $ellipse); + + return $this->modify($modifier); + } + + public function drawCircle(int $x, int $y, ?callable $init = null): ImageInterface + { + $circle = $this->runCallback($init, new Circle(0)); + $modifier = $this->resolveDriverClass('Modifiers\DrawEllipseModifier', new Point($x, $y), $circle); + + return $this->modify($modifier); + } + public function resize(?int $width = null, ?int $height = null): ImageInterface { return $this->modify( diff --git a/src/Drivers/Abstract/Modifiers/AbstractDrawModifier.php b/src/Drivers/Abstract/Modifiers/AbstractDrawModifier.php index 1dfa0f13..04fe1f46 100644 --- a/src/Drivers/Abstract/Modifiers/AbstractDrawModifier.php +++ b/src/Drivers/Abstract/Modifiers/AbstractDrawModifier.php @@ -2,6 +2,8 @@ namespace Intervention\Image\Drivers\Abstract\Modifiers; +use Intervention\Image\Exceptions\DecoderException; +use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\DrawableInterface; use Intervention\Image\Interfaces\PointInterface; use Intervention\Image\Traits\CanHandleInput; @@ -16,4 +18,31 @@ class AbstractDrawModifier ) { // } + + public function drawable(): DrawableInterface + { + return $this->drawable; + } + + protected function getBackgroundColor(): ?ColorInterface + { + try { + $color = $this->handleInput($this->drawable->getBackgroundColor()); + } catch (DecoderException $e) { + return $this->handleInput('transparent'); + } + + return $color; + } + + protected function getBorderColor(): ?ColorInterface + { + try { + $color = $this->handleInput($this->drawable->getBorderColor()); + } catch (DecoderException $e) { + return $this->handleInput('transparent'); + } + + return $color; + } } diff --git a/src/Drivers/Gd/Modifiers/DrawEllipseModifier.php b/src/Drivers/Gd/Modifiers/DrawEllipseModifier.php new file mode 100644 index 00000000..d7adef56 --- /dev/null +++ b/src/Drivers/Gd/Modifiers/DrawEllipseModifier.php @@ -0,0 +1,53 @@ +eachFrame(function ($frame) { + if ($this->drawable()->hasBorder()) { + // slightly smaller ellipse to keep 1px bordered edges clean + if ($this->drawable()->hasBackgroundColor()) { + imagefilledellipse( + $frame->getCore(), + $this->position->getX(), + $this->position->getY(), + $this->drawable()->getWidth() - 1, + $this->drawable()->getHeight() - 1, + $this->getBackgroundColor()->toInt() + ); + } + + imagesetthickness($frame->getCore(), $this->drawable()->getBorderSize()); + + // gd's imageellipse doesn't respect imagesetthickness so i use + // imagearc with 359.9 degrees here. + imagearc( + $frame->getCore(), + $this->position->getX(), + $this->position->getY(), + $this->drawable()->getWidth(), + $this->drawable()->getHeight(), + 0, + 359.99, + $this->getBorderColor()->toInt() + ); + } else { + imagefilledellipse( + $frame->getCore(), + $this->position->getX(), + $this->position->getY(), + $this->drawable()->getWidth(), + $this->drawable()->getHeight(), + $this->getBackgroundColor()->toInt() + ); + } + }); + } +} diff --git a/src/Drivers/Gd/Modifiers/DrawRectangleModifier.php b/src/Drivers/Gd/Modifiers/DrawRectangleModifier.php index 3f949aca..a12c252f 100644 --- a/src/Drivers/Gd/Modifiers/DrawRectangleModifier.php +++ b/src/Drivers/Gd/Modifiers/DrawRectangleModifier.php @@ -3,8 +3,6 @@ namespace Intervention\Image\Drivers\Gd\Modifiers; use Intervention\Image\Drivers\Abstract\Modifiers\AbstractDrawModifier; -use Intervention\Image\Exceptions\DecoderException; -use Intervention\Image\Geometry\Rectangle; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; @@ -14,58 +12,31 @@ class DrawRectangleModifier extends AbstractDrawModifier implements ModifierInte { $image->eachFrame(function ($frame) { // draw background - if ($this->rectangle()->hasBackgroundColor()) { + if ($this->drawable()->hasBackgroundColor()) { imagefilledrectangle( $frame->getCore(), $this->position->getX(), $this->position->getY(), - $this->position->getX() + $this->rectangle()->bottomRightPoint()->getY(), - $this->position->getY() + $this->rectangle()->bottomRightPoint()->getY(), - $this->getBackgroundColor() + $this->position->getX() + $this->drawable()->bottomRightPoint()->getX(), + $this->position->getY() + $this->drawable()->bottomRightPoint()->getY(), + $this->getBackgroundColor()->toInt() ); } - if ($this->rectangle()->hasBorder()) { + if ($this->drawable()->hasBorder()) { // draw border - imagesetthickness($frame->getCore(), $this->rectangle()->getBorderSize()); + imagesetthickness($frame->getCore(), $this->drawable()->getBorderSize()); imagerectangle( $frame->getCore(), $this->position->getX(), $this->position->getY(), - $this->position->getX() + $this->rectangle()->bottomRightPoint()->getY(), - $this->position->getY() + $this->rectangle()->bottomRightPoint()->getY(), - $this->getBorderColor() + $this->position->getX() + $this->drawable()->bottomRightPoint()->getX(), + $this->position->getY() + $this->drawable()->bottomRightPoint()->getY(), + $this->getBorderColor()->toInt() ); } }); return $image; } - - private function rectangle(): Rectangle - { - return $this->drawable; - } - - private function getBackgroundColor(): int - { - try { - $color = $this->handleInput($this->rectangle()->getBackgroundColor()); - } catch (DecoderException $e) { - return 2130706432; // transparent - } - - return $color->toInt(); - } - - private function getBorderColor(): int - { - try { - $color = $this->handleInput($this->rectangle()->getBorderColor()); - } catch (DecoderException $e) { - return 2130706432; // transparent - } - - return $color->toInt(); - } } diff --git a/src/Drivers/Imagick/Modifiers/DrawEllipseModifier.php b/src/Drivers/Imagick/Modifiers/DrawEllipseModifier.php new file mode 100644 index 00000000..075842ea --- /dev/null +++ b/src/Drivers/Imagick/Modifiers/DrawEllipseModifier.php @@ -0,0 +1,58 @@ +eachFrame(function ($frame) { + $drawing = new ImagickDraw(); + $drawing->setFillColor($this->getBackgroundColor()->getPixel()); + + if ($this->drawable()->hasBorder()) { + $drawing->setStrokeWidth($this->drawable()->getBorderSize()); + $drawing->setStrokeColor($this->getBorderColor()->getPixel()); + } + + $drawing->ellipse( + $this->position->getX(), + $this->position->getY(), + $this->drawable()->getWidth() / 2, + $this->drawable()->getHeight() / 2, + 0, + 360 + ); + + $frame->getCore()->drawImage($drawing); + }); + } + + protected function getBackgroundColor(): ColorInterface + { + $color = parent::getBackgroundColor(); + if (!is_a($color, Color::class)) { + throw new DecoderException('Unable to decode background color.'); + } + + return $color; + } + + protected function getBorderColor(): ColorInterface + { + $color = parent::getBorderColor(); + if (!is_a($color, Color::class)) { + throw new DecoderException('Unable to decode border color.'); + } + + return $color; + } +} diff --git a/src/Drivers/Imagick/Modifiers/DrawRectangleModifier.php b/src/Drivers/Imagick/Modifiers/DrawRectangleModifier.php index bb6f9be0..f16bdb5a 100644 --- a/src/Drivers/Imagick/Modifiers/DrawRectangleModifier.php +++ b/src/Drivers/Imagick/Modifiers/DrawRectangleModifier.php @@ -3,13 +3,12 @@ namespace Intervention\Image\Drivers\Imagick\Modifiers; use ImagickDraw; -use ImagickPixel; use Intervention\Image\Drivers\Abstract\Modifiers\AbstractDrawModifier; -use Intervention\Image\Interfaces\ImageInterface; -use Intervention\Image\Interfaces\ModifierInterface; use Intervention\Image\Drivers\Imagick\Color; use Intervention\Image\Exceptions\DecoderException; -use Intervention\Image\Geometry\Rectangle; +use Intervention\Image\Interfaces\ImageInterface; +use Intervention\Image\Interfaces\ModifierInterface; +use Intervention\Image\Interfaces\ColorInterface; class DrawRectangleModifier extends AbstractDrawModifier implements ModifierInterface { @@ -17,18 +16,18 @@ class DrawRectangleModifier extends AbstractDrawModifier implements ModifierInte { // setup rectangle $drawing = new ImagickDraw(); - $drawing->setFillColor($this->getBackgroundColor()); - if ($this->rectangle()->hasBorder()) { - $drawing->setStrokeColor($this->getBorderColor()); - $drawing->setStrokeWidth($this->rectangle()->getBorderSize()); + $drawing->setFillColor($this->getBackgroundColor()->getPixel()); + if ($this->drawable()->hasBorder()) { + $drawing->setStrokeColor($this->getBorderColor()->getPixel()); + $drawing->setStrokeWidth($this->drawable()->getBorderSize()); } // build rectangle $drawing->rectangle( $this->position->getX(), $this->position->getY(), - $this->position->getX() + $this->rectangle()->bottomRightPoint()->getX(), - $this->position->getY() + $this->rectangle()->bottomRightPoint()->getY() + $this->position->getX() + $this->drawable()->bottomRightPoint()->getX(), + $this->position->getY() + $this->drawable()->bottomRightPoint()->getY() ); $image->eachFrame(function ($frame) use ($drawing) { @@ -38,39 +37,23 @@ class DrawRectangleModifier extends AbstractDrawModifier implements ModifierInte return $image; } - private function rectangle(): Rectangle - { - return $this->drawable; - } - - private function getBackgroundColor(): ImagickPixel - { - try { - $color = $this->handleInput($this->rectangle()->getBackgroundColor()); - } catch (DecoderException $e) { - $color = null; - } - - return $this->filterImagickPixel($color); - } - - private function getBorderColor(): ImagickPixel - { - try { - $color = $this->handleInput($this->rectangle()->getBorderColor()); - } catch (DecoderException $e) { - $color = null; - } - - return $this->filterImagickPixel($color); - } - - private function filterImagickPixel($color): ImagickPixel + protected function getBackgroundColor(): ColorInterface { + $color = parent::getBackgroundColor(); if (!is_a($color, Color::class)) { - return new ImagickPixel('transparent'); + throw new DecoderException('Unable to decode background color.'); } - return $color->getPixel(); + return $color; + } + + protected function getBorderColor(): ColorInterface + { + $color = parent::getBorderColor(); + if (!is_a($color, Color::class)) { + throw new DecoderException('Unable to decode border color.'); + } + + return $color; } } diff --git a/src/Geometry/Circle.php b/src/Geometry/Circle.php new file mode 100644 index 00000000..841d6a96 --- /dev/null +++ b/src/Geometry/Circle.php @@ -0,0 +1,48 @@ +setWidth($diameter); + $this->setHeight($diameter); + $this->pivot = $pivot ? $pivot : new Point(); + } + + public function diameter(int $diameter): self + { + return $this->setDiameter($diameter); + } + + public function setDiameter(int $diameter): self + { + $this->setWidth($diameter); + $this->setHeight($diameter); + + return $this; + } + + public function getDiameter(): int + { + return $this->diameter; + } + + public function radius(int $radius): self + { + return $this->setRadius($radius); + } + + public function setRadius(int $radius): self + { + return $this->diameter(intval($radius * 2)); + } + + public function getRadius(): int + { + return intval($this->diameter / 2); + } +} diff --git a/src/Geometry/Ellipse.php b/src/Geometry/Ellipse.php new file mode 100644 index 00000000..fb37dc7e --- /dev/null +++ b/src/Geometry/Ellipse.php @@ -0,0 +1,55 @@ +pivot = $pivot ? $pivot : new Point(); + } + + public function size(int $width, int $height): self + { + return $this->setSize($width, $height); + } + + public function setSize(int $width, int $height): self + { + return $this->setWidth($width)->setHeight($height); + } + + public function setWidth(int $width): self + { + $this->width = $width; + + return $this; + } + + public function setHeight(int $height): self + { + $this->height = $height; + + return $this; + } + + public function getWidth(): int + { + return $this->width; + } + + public function getHeight(): int + { + return $this->height; + } +} From 39613b731a8f90ec688c361fe7c6301ef75de2ad Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 23 Jul 2022 16:47:26 +0200 Subject: [PATCH 215/476] Add DrawLineModifiers --- src/Drivers/Abstract/AbstractImage.php | 9 ++ src/Drivers/Gd/Modifiers/DrawLineModifier.php | 39 +++++++++ .../Imagick/Modifiers/DrawLineModifier.php | 42 +++++++++ src/Geometry/Line.php | 85 +++++++++++++++++++ 4 files changed, 175 insertions(+) create mode 100644 src/Drivers/Gd/Modifiers/DrawLineModifier.php create mode 100644 src/Drivers/Imagick/Modifiers/DrawLineModifier.php create mode 100644 src/Geometry/Line.php diff --git a/src/Drivers/Abstract/AbstractImage.php b/src/Drivers/Abstract/AbstractImage.php index 01b702ab..097ac5bb 100644 --- a/src/Drivers/Abstract/AbstractImage.php +++ b/src/Drivers/Abstract/AbstractImage.php @@ -6,6 +6,7 @@ use Intervention\Image\Collection; use Intervention\Image\EncodedImage; use Intervention\Image\Geometry\Circle; use Intervention\Image\Geometry\Ellipse; +use Intervention\Image\Geometry\Line; use Intervention\Image\Geometry\Point; use Intervention\Image\Geometry\Rectangle; use Intervention\Image\Interfaces\CollectionInterface; @@ -245,6 +246,14 @@ abstract class AbstractImage implements ImageInterface return $this->modify($modifier); } + public function drawLine(callable $init = null): ImageInterface + { + $line = $this->runCallback($init, new Line(new Point(), new Point())); + $modifier = $this->resolveDriverClass('Modifiers\DrawLineModifier', $line->getStart(), $line); + + return $this->modify($modifier); + } + public function resize(?int $width = null, ?int $height = null): ImageInterface { return $this->modify( diff --git a/src/Drivers/Gd/Modifiers/DrawLineModifier.php b/src/Drivers/Gd/Modifiers/DrawLineModifier.php new file mode 100644 index 00000000..adcc455b --- /dev/null +++ b/src/Drivers/Gd/Modifiers/DrawLineModifier.php @@ -0,0 +1,39 @@ +eachFrame(function ($frame) { + imageline( + $frame->getCore(), + $this->drawable()->getStart()->getX(), + $this->drawable()->getStart()->getY(), + $this->drawable()->getEnd()->getX(), + $this->drawable()->getEnd()->getY(), + $this->getBackgroundColor()->toInt() + ); + }); + } + + public function drawable(): DrawableInterface + { + $drawable = parent::drawable(); + if (!is_a($drawable, Line::class)) { + throw new GeometryException( + 'Shape mismatch. Excepted Line::class, found ' . get_class($drawable) + ); + } + + return $drawable; + } +} diff --git a/src/Drivers/Imagick/Modifiers/DrawLineModifier.php b/src/Drivers/Imagick/Modifiers/DrawLineModifier.php new file mode 100644 index 00000000..6c224a55 --- /dev/null +++ b/src/Drivers/Imagick/Modifiers/DrawLineModifier.php @@ -0,0 +1,42 @@ +setStrokeColor($this->getBackgroundColor()->getPixel()); + $drawing->setStrokeWidth($this->drawable()->getWidth()); + $drawing->line( + $this->drawable()->getStart()->getX(), + $this->drawable()->getStart()->getY(), + $this->drawable()->getEnd()->getX(), + $this->drawable()->getEnd()->getY(), + ); + return $image->eachFrame(function ($frame) use ($drawing) { + $frame->getCore()->drawImage($drawing); + }); + } + + public function drawable(): DrawableInterface + { + $drawable = parent::drawable(); + if (!is_a($drawable, Line::class)) { + throw new GeometryException( + 'Shape mismatch. Excepted Line::class, found ' . get_class($drawable) + ); + } + + return $drawable; + } +} diff --git a/src/Geometry/Line.php b/src/Geometry/Line.php new file mode 100644 index 00000000..7ef5a827 --- /dev/null +++ b/src/Geometry/Line.php @@ -0,0 +1,85 @@ +width; + } + + public function setWidth(int $width): self + { + $this->width = $width; + + return $this; + } + + public function width(int $width): self + { + return $this->setWidth($width); + } + + public function color($color): self + { + $this->setBackgroundColor($color); + + return $this; + } + + public function getStart(): Point + { + return $this->start; + } + + public function getEnd(): Point + { + return $this->end; + } + + public function setStart(Point $start): self + { + $this->start = $start; + + return $this; + } + + public function from(int $x, int $y): self + { + $this->getStart()->setX($x); + $this->getStart()->setY($y); + + return $this; + } + + public function to(int $x, int $y): self + { + $this->getEnd()->setX($x); + $this->getEnd()->setY($y); + + return $this; + } + + public function setEnd(Point $end): self + { + $this->end = $end; + + return $this; + } +} From 7a103cbfa8b36f73c82028e2030cb08e2bc2c6ab Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Mon, 25 Jul 2022 11:13:38 +0200 Subject: [PATCH 216/476] Implement IteratorAggregate in Polygon::class --- src/Geometry/Polygon.php | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/src/Geometry/Polygon.php b/src/Geometry/Polygon.php index 732193f0..50cf3cb0 100644 --- a/src/Geometry/Polygon.php +++ b/src/Geometry/Polygon.php @@ -3,10 +3,19 @@ namespace Intervention\Image\Geometry; use ArrayAccess; +use ArrayIterator; use Countable; +use Traversable; +use IteratorAggregate; +use Intervention\Image\Geometry\Traits\HasBackgroundColor; +use Intervention\Image\Geometry\Traits\HasBorder; +use Intervention\Image\Interfaces\DrawableInterface; -class Polygon implements Countable, ArrayAccess +class Polygon implements IteratorAggregate, Countable, ArrayAccess, DrawableInterface { + use HasBorder; + use HasBackgroundColor; + public function __construct( protected array $points = [], protected ?Point $pivot = null @@ -14,6 +23,11 @@ class Polygon implements Countable, ArrayAccess $this->pivot = $pivot ? $pivot : new Point(); } + public function getIterator(): Traversable + { + return new ArrayIterator($this->points); + } + /** * Return current pivot point * @@ -133,6 +147,13 @@ class Polygon implements Countable, ArrayAccess return $this; } + public function point(int $x, int $y): self + { + $this->addPoint(new Point($x, $y)); + + return $this; + } + /** * Calculate total horizontal span of polygon * From 105e11d1d52524ca3289a8e199234ca33a501626 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Mon, 25 Jul 2022 11:14:10 +0200 Subject: [PATCH 217/476] Add Image::drawPolygon() --- src/Drivers/Abstract/AbstractImage.php | 9 ++++ .../Modifiers/AbstractDrawModifier.php | 49 +++++++++++++++++++ .../Gd/Modifiers/DrawPolygonModifier.php | 41 ++++++++++++++++ .../Imagick/Modifiers/DrawPolygonModifier.php | 47 ++++++++++++++++++ 4 files changed, 146 insertions(+) create mode 100644 src/Drivers/Gd/Modifiers/DrawPolygonModifier.php create mode 100644 src/Drivers/Imagick/Modifiers/DrawPolygonModifier.php diff --git a/src/Drivers/Abstract/AbstractImage.php b/src/Drivers/Abstract/AbstractImage.php index 097ac5bb..9120d5c1 100644 --- a/src/Drivers/Abstract/AbstractImage.php +++ b/src/Drivers/Abstract/AbstractImage.php @@ -8,6 +8,7 @@ use Intervention\Image\Geometry\Circle; use Intervention\Image\Geometry\Ellipse; use Intervention\Image\Geometry\Line; use Intervention\Image\Geometry\Point; +use Intervention\Image\Geometry\Polygon; use Intervention\Image\Geometry\Rectangle; use Intervention\Image\Interfaces\CollectionInterface; use Intervention\Image\Interfaces\EncoderInterface; @@ -254,6 +255,14 @@ abstract class AbstractImage implements ImageInterface return $this->modify($modifier); } + public function drawPolygon(callable $init = null): ImageInterface + { + $polygon = $this->runCallback($init, new Polygon()); + $modifier = $this->resolveDriverClass('Modifiers\DrawPolygonModifier', $polygon); + + return $this->modify($modifier); + } + public function resize(?int $width = null, ?int $height = null): ImageInterface { return $this->modify( diff --git a/src/Drivers/Abstract/Modifiers/AbstractDrawModifier.php b/src/Drivers/Abstract/Modifiers/AbstractDrawModifier.php index 04fe1f46..42e89eff 100644 --- a/src/Drivers/Abstract/Modifiers/AbstractDrawModifier.php +++ b/src/Drivers/Abstract/Modifiers/AbstractDrawModifier.php @@ -3,6 +3,11 @@ namespace Intervention\Image\Drivers\Abstract\Modifiers; use Intervention\Image\Exceptions\DecoderException; +use Intervention\Image\Exceptions\GeometryException; +use Intervention\Image\Geometry\Ellipse; +use Intervention\Image\Geometry\Line; +use Intervention\Image\Geometry\Polygon; +use Intervention\Image\Geometry\Rectangle; use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\DrawableInterface; use Intervention\Image\Interfaces\PointInterface; @@ -45,4 +50,48 @@ class AbstractDrawModifier return $color; } + + public function polygon(): Polygon + { + if (!is_a($this->drawable(), Polygon::class)) { + throw new GeometryException( + 'Shape mismatch. Excepted Polygon::class, found ' . get_class($this->drawable()) + ); + } + + return $this->drawable(); + } + + public function ellipse(): Ellipse + { + if (!is_a($this->drawable(), Ellipse::class)) { + throw new GeometryException( + 'Shape mismatch. Excepted Ellipse::class, found ' . get_class($this->drawable()) + ); + } + + return $this->drawable(); + } + + public function line(): Line + { + if (!is_a($this->drawable(), Line::class)) { + throw new GeometryException( + 'Shape mismatch. Excepted Line::class, found ' . get_class($this->drawable()) + ); + } + + return $this->drawable(); + } + + public function rectangle(): Rectangle + { + if (!is_a($this->drawable(), Rectangle::class)) { + throw new GeometryException( + 'Shape mismatch. Excepted Rectangle::class, found ' . get_class($this->drawable()) + ); + } + + return $this->drawable(); + } } diff --git a/src/Drivers/Gd/Modifiers/DrawPolygonModifier.php b/src/Drivers/Gd/Modifiers/DrawPolygonModifier.php new file mode 100644 index 00000000..29dcf95e --- /dev/null +++ b/src/Drivers/Gd/Modifiers/DrawPolygonModifier.php @@ -0,0 +1,41 @@ +eachFrame(function ($frame) { + if ($this->polygon()->hasBackgroundColor()) { + imagefilledpolygon( + $frame->getCore(), + $this->polygon()->toArray(), + $this->polygon()->count(), + $this->getBackgroundColor()->toInt() + ); + } + + if ($this->polygon()->hasBorder()) { + imagesetthickness($frame->getCore(), $this->polygon()->getBorderSize()); + imagepolygon( + $frame->getCore(), + $this->polygon()->toArray(), + $this->polygon()->count(), + $this->getBorderColor()->toInt() + ); + } + }); + } +} diff --git a/src/Drivers/Imagick/Modifiers/DrawPolygonModifier.php b/src/Drivers/Imagick/Modifiers/DrawPolygonModifier.php new file mode 100644 index 00000000..8f2f7fdb --- /dev/null +++ b/src/Drivers/Imagick/Modifiers/DrawPolygonModifier.php @@ -0,0 +1,47 @@ +polygon()->hasBackgroundColor()) { + $drawing->setFillColor($this->getBackgroundColor()->getPixel()); + } + + if ($this->polygon()->hasBorder()) { + $drawing->setStrokeColor($this->getBorderColor()->getPixel()); + $drawing->setStrokeWidth($this->polygon()->getBorderSize()); + } + + $drawing->polygon($this->points()); + + return $image->eachFrame(function ($frame) use ($drawing) { + $frame->getCore()->drawImage($drawing); + }); + } + + private function points(): array + { + $points = []; + foreach ($this->polygon() as $point) { + $points[] = ['x' => $point->getX(), 'y' => $point->getY()]; + } + + return $points; + } +} From f54b18e01cbbf20ccca3142f269ddf37fb50f0a6 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Mon, 25 Jul 2022 11:19:11 +0200 Subject: [PATCH 218/476] Refactor draw modifiers --- .../Gd/Modifiers/DrawEllipseModifier.php | 18 ++++++------- src/Drivers/Gd/Modifiers/DrawLineModifier.php | 23 +++------------- .../Gd/Modifiers/DrawRectangleModifier.php | 14 +++++----- .../Imagick/Modifiers/DrawEllipseModifier.php | 8 +++--- .../Imagick/Modifiers/DrawLineModifier.php | 26 +++++-------------- .../Modifiers/DrawRectangleModifier.php | 8 +++--- 6 files changed, 34 insertions(+), 63 deletions(-) diff --git a/src/Drivers/Gd/Modifiers/DrawEllipseModifier.php b/src/Drivers/Gd/Modifiers/DrawEllipseModifier.php index d7adef56..c113b91d 100644 --- a/src/Drivers/Gd/Modifiers/DrawEllipseModifier.php +++ b/src/Drivers/Gd/Modifiers/DrawEllipseModifier.php @@ -11,20 +11,20 @@ class DrawEllipseModifier extends AbstractDrawModifier implements ModifierInterf public function apply(ImageInterface $image): ImageInterface { return $image->eachFrame(function ($frame) { - if ($this->drawable()->hasBorder()) { + if ($this->ellipse()->hasBorder()) { // slightly smaller ellipse to keep 1px bordered edges clean - if ($this->drawable()->hasBackgroundColor()) { + if ($this->ellipse()->hasBackgroundColor()) { imagefilledellipse( $frame->getCore(), $this->position->getX(), $this->position->getY(), - $this->drawable()->getWidth() - 1, - $this->drawable()->getHeight() - 1, + $this->ellipse()->getWidth() - 1, + $this->ellipse()->getHeight() - 1, $this->getBackgroundColor()->toInt() ); } - imagesetthickness($frame->getCore(), $this->drawable()->getBorderSize()); + imagesetthickness($frame->getCore(), $this->ellipse()->getBorderSize()); // gd's imageellipse doesn't respect imagesetthickness so i use // imagearc with 359.9 degrees here. @@ -32,8 +32,8 @@ class DrawEllipseModifier extends AbstractDrawModifier implements ModifierInterf $frame->getCore(), $this->position->getX(), $this->position->getY(), - $this->drawable()->getWidth(), - $this->drawable()->getHeight(), + $this->ellipse()->getWidth(), + $this->ellipse()->getHeight(), 0, 359.99, $this->getBorderColor()->toInt() @@ -43,8 +43,8 @@ class DrawEllipseModifier extends AbstractDrawModifier implements ModifierInterf $frame->getCore(), $this->position->getX(), $this->position->getY(), - $this->drawable()->getWidth(), - $this->drawable()->getHeight(), + $this->ellipse()->getWidth(), + $this->ellipse()->getHeight(), $this->getBackgroundColor()->toInt() ); } diff --git a/src/Drivers/Gd/Modifiers/DrawLineModifier.php b/src/Drivers/Gd/Modifiers/DrawLineModifier.php index adcc455b..677d6345 100644 --- a/src/Drivers/Gd/Modifiers/DrawLineModifier.php +++ b/src/Drivers/Gd/Modifiers/DrawLineModifier.php @@ -3,9 +3,6 @@ namespace Intervention\Image\Drivers\Gd\Modifiers; use Intervention\Image\Drivers\Abstract\Modifiers\AbstractDrawModifier; -use Intervention\Image\Exceptions\GeometryException; -use Intervention\Image\Geometry\Line; -use Intervention\Image\Interfaces\DrawableInterface; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; @@ -16,24 +13,12 @@ class DrawLineModifier extends AbstractDrawModifier implements ModifierInterface return $image->eachFrame(function ($frame) { imageline( $frame->getCore(), - $this->drawable()->getStart()->getX(), - $this->drawable()->getStart()->getY(), - $this->drawable()->getEnd()->getX(), - $this->drawable()->getEnd()->getY(), + $this->line()->getStart()->getX(), + $this->line()->getStart()->getY(), + $this->line()->getEnd()->getX(), + $this->line()->getEnd()->getY(), $this->getBackgroundColor()->toInt() ); }); } - - public function drawable(): DrawableInterface - { - $drawable = parent::drawable(); - if (!is_a($drawable, Line::class)) { - throw new GeometryException( - 'Shape mismatch. Excepted Line::class, found ' . get_class($drawable) - ); - } - - return $drawable; - } } diff --git a/src/Drivers/Gd/Modifiers/DrawRectangleModifier.php b/src/Drivers/Gd/Modifiers/DrawRectangleModifier.php index a12c252f..1b5e39dc 100644 --- a/src/Drivers/Gd/Modifiers/DrawRectangleModifier.php +++ b/src/Drivers/Gd/Modifiers/DrawRectangleModifier.php @@ -12,26 +12,26 @@ class DrawRectangleModifier extends AbstractDrawModifier implements ModifierInte { $image->eachFrame(function ($frame) { // draw background - if ($this->drawable()->hasBackgroundColor()) { + if ($this->rectangle()->hasBackgroundColor()) { imagefilledrectangle( $frame->getCore(), $this->position->getX(), $this->position->getY(), - $this->position->getX() + $this->drawable()->bottomRightPoint()->getX(), - $this->position->getY() + $this->drawable()->bottomRightPoint()->getY(), + $this->position->getX() + $this->rectangle()->bottomRightPoint()->getX(), + $this->position->getY() + $this->rectangle()->bottomRightPoint()->getY(), $this->getBackgroundColor()->toInt() ); } - if ($this->drawable()->hasBorder()) { + if ($this->rectangle()->hasBorder()) { // draw border - imagesetthickness($frame->getCore(), $this->drawable()->getBorderSize()); + imagesetthickness($frame->getCore(), $this->rectangle()->getBorderSize()); imagerectangle( $frame->getCore(), $this->position->getX(), $this->position->getY(), - $this->position->getX() + $this->drawable()->bottomRightPoint()->getX(), - $this->position->getY() + $this->drawable()->bottomRightPoint()->getY(), + $this->position->getX() + $this->rectangle()->bottomRightPoint()->getX(), + $this->position->getY() + $this->rectangle()->bottomRightPoint()->getY(), $this->getBorderColor()->toInt() ); } diff --git a/src/Drivers/Imagick/Modifiers/DrawEllipseModifier.php b/src/Drivers/Imagick/Modifiers/DrawEllipseModifier.php index 075842ea..b0ef639d 100644 --- a/src/Drivers/Imagick/Modifiers/DrawEllipseModifier.php +++ b/src/Drivers/Imagick/Modifiers/DrawEllipseModifier.php @@ -18,16 +18,16 @@ class DrawEllipseModifier extends AbstractDrawModifier implements ModifierInterf $drawing = new ImagickDraw(); $drawing->setFillColor($this->getBackgroundColor()->getPixel()); - if ($this->drawable()->hasBorder()) { - $drawing->setStrokeWidth($this->drawable()->getBorderSize()); + if ($this->ellipse()->hasBorder()) { + $drawing->setStrokeWidth($this->ellipse()->getBorderSize()); $drawing->setStrokeColor($this->getBorderColor()->getPixel()); } $drawing->ellipse( $this->position->getX(), $this->position->getY(), - $this->drawable()->getWidth() / 2, - $this->drawable()->getHeight() / 2, + $this->ellipse()->getWidth() / 2, + $this->ellipse()->getHeight() / 2, 0, 360 ); diff --git a/src/Drivers/Imagick/Modifiers/DrawLineModifier.php b/src/Drivers/Imagick/Modifiers/DrawLineModifier.php index 6c224a55..0cd6fcd8 100644 --- a/src/Drivers/Imagick/Modifiers/DrawLineModifier.php +++ b/src/Drivers/Imagick/Modifiers/DrawLineModifier.php @@ -4,9 +4,6 @@ namespace Intervention\Image\Drivers\Imagick\Modifiers; use ImagickDraw; use Intervention\Image\Drivers\Abstract\Modifiers\AbstractDrawModifier; -use Intervention\Image\Exceptions\GeometryException; -use Intervention\Image\Geometry\Line; -use Intervention\Image\Interfaces\DrawableInterface; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; @@ -16,27 +13,16 @@ class DrawLineModifier extends AbstractDrawModifier implements ModifierInterface { $drawing = new ImagickDraw(); $drawing->setStrokeColor($this->getBackgroundColor()->getPixel()); - $drawing->setStrokeWidth($this->drawable()->getWidth()); + $drawing->setStrokeWidth($this->line()->getWidth()); $drawing->line( - $this->drawable()->getStart()->getX(), - $this->drawable()->getStart()->getY(), - $this->drawable()->getEnd()->getX(), - $this->drawable()->getEnd()->getY(), + $this->line()->getStart()->getX(), + $this->line()->getStart()->getY(), + $this->line()->getEnd()->getX(), + $this->line()->getEnd()->getY(), ); + return $image->eachFrame(function ($frame) use ($drawing) { $frame->getCore()->drawImage($drawing); }); } - - public function drawable(): DrawableInterface - { - $drawable = parent::drawable(); - if (!is_a($drawable, Line::class)) { - throw new GeometryException( - 'Shape mismatch. Excepted Line::class, found ' . get_class($drawable) - ); - } - - return $drawable; - } } diff --git a/src/Drivers/Imagick/Modifiers/DrawRectangleModifier.php b/src/Drivers/Imagick/Modifiers/DrawRectangleModifier.php index f16bdb5a..330f7874 100644 --- a/src/Drivers/Imagick/Modifiers/DrawRectangleModifier.php +++ b/src/Drivers/Imagick/Modifiers/DrawRectangleModifier.php @@ -17,17 +17,17 @@ class DrawRectangleModifier extends AbstractDrawModifier implements ModifierInte // setup rectangle $drawing = new ImagickDraw(); $drawing->setFillColor($this->getBackgroundColor()->getPixel()); - if ($this->drawable()->hasBorder()) { + if ($this->rectangle()->hasBorder()) { $drawing->setStrokeColor($this->getBorderColor()->getPixel()); - $drawing->setStrokeWidth($this->drawable()->getBorderSize()); + $drawing->setStrokeWidth($this->rectangle()->getBorderSize()); } // build rectangle $drawing->rectangle( $this->position->getX(), $this->position->getY(), - $this->position->getX() + $this->drawable()->bottomRightPoint()->getX(), - $this->position->getY() + $this->drawable()->bottomRightPoint()->getY() + $this->position->getX() + $this->rectangle()->bottomRightPoint()->getX(), + $this->position->getY() + $this->rectangle()->bottomRightPoint()->getY() ); $image->eachFrame(function ($frame) use ($drawing) { From 7be06787a3ae44d2ac73a5237f69402c19845232 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Fri, 12 Aug 2022 17:27:59 +0200 Subject: [PATCH 219/476] Refactor draw modifiers --- src/Drivers/Abstract/AbstractImage.php | 7 ++--- .../Gd/Modifiers/DrawPixelModifier.php | 19 +++++++----- .../Imagick/Modifiers/DrawPixelModifier.php | 30 +++++++------------ tests/Drivers/Abstract/AbstractImageTest.php | 1 + .../Gd/Modifiers/DrawPixelModifierTest.php | 3 +- .../Modifiers/DrawPixelModifierTest.php | 4 +-- 6 files changed, 28 insertions(+), 36 deletions(-) diff --git a/src/Drivers/Abstract/AbstractImage.php b/src/Drivers/Abstract/AbstractImage.php index 9120d5c1..fc2d6aad 100644 --- a/src/Drivers/Abstract/AbstractImage.php +++ b/src/Drivers/Abstract/AbstractImage.php @@ -217,10 +217,9 @@ abstract class AbstractImage implements ImageInterface public function drawPixel(int $x, int $y, $color = null): ImageInterface { - $color = $this->handleInput($color); - $modifier = $this->resolveDriverClass('Modifiers\DrawPixelModifier', new Point($x, $y), $color); - - return $this->modify($modifier); + return $this->modify( + $this->resolveDriverClass('Modifiers\DrawPixelModifier', new Point($x, $y), $color) + ); } public function drawRectangle(int $x, int $y, ?callable $init = null): ImageInterface diff --git a/src/Drivers/Gd/Modifiers/DrawPixelModifier.php b/src/Drivers/Gd/Modifiers/DrawPixelModifier.php index 3f44a23b..bab24579 100644 --- a/src/Drivers/Gd/Modifiers/DrawPixelModifier.php +++ b/src/Drivers/Gd/Modifiers/DrawPixelModifier.php @@ -3,28 +3,31 @@ namespace Intervention\Image\Drivers\Gd\Modifiers; use Intervention\Image\Geometry\Point; -use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; +use Intervention\Image\Traits\CanHandleInput; class DrawPixelModifier implements ModifierInterface { - public function __construct(protected Point $position, protected ColorInterface $color) - { + use CanHandleInput; + + public function __construct( + protected Point $position, + protected $color + ) { // } public function apply(ImageInterface $image): ImageInterface { - foreach ($image as $frame) { + $color = $this->handleInput($this->color); + return $image->eachFrame(function ($frame) use ($color) { imagesetpixel( $frame->getCore(), $this->position->getX(), $this->position->getY(), - $this->color->toInt() + $color->toInt() ); - } - - return $image; + }); } } diff --git a/src/Drivers/Imagick/Modifiers/DrawPixelModifier.php b/src/Drivers/Imagick/Modifiers/DrawPixelModifier.php index 00f34ba4..1b5edc9d 100644 --- a/src/Drivers/Imagick/Modifiers/DrawPixelModifier.php +++ b/src/Drivers/Imagick/Modifiers/DrawPixelModifier.php @@ -3,39 +3,31 @@ namespace Intervention\Image\Drivers\Imagick\Modifiers; use ImagickDraw; -use Intervention\Image\Drivers\Imagick\Color; -use Intervention\Image\Exceptions\DecoderException; use Intervention\Image\Geometry\Point; -use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; +use Intervention\Image\Traits\CanHandleInput; class DrawPixelModifier implements ModifierInterface { - public function __construct(protected Point $position, protected ColorInterface $color) - { + use CanHandleInput; + + public function __construct( + protected Point $position, + protected $color + ) { // } public function apply(ImageInterface $image): ImageInterface { + $color = $this->handleInput($this->color); $pixel = new ImagickDraw(); - $pixel->setFillColor($this->getColor()->getPixel()); + $pixel->setFillColor($color->getPixel()); $pixel->point($this->position->getX(), $this->position->getY()); - foreach ($image as $frame) { + return $image->eachFrame(function ($frame) use ($pixel) { $frame->getCore()->drawImage($pixel); - } - - return $image; - } - - public function getColor(): Color - { - if (!is_a($this->color, Color::class)) { - throw new DecoderException('Unable to decode given pixel color.'); - } - - return $this->color; + }); } } diff --git a/tests/Drivers/Abstract/AbstractImageTest.php b/tests/Drivers/Abstract/AbstractImageTest.php index b6546ccb..9b5a943c 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\Interfaces\PointInterface; use Intervention\Image\Tests\TestCase; use Mockery; diff --git a/tests/Drivers/Gd/Modifiers/DrawPixelModifierTest.php b/tests/Drivers/Gd/Modifiers/DrawPixelModifierTest.php index 0b552116..e6ef8f5d 100644 --- a/tests/Drivers/Gd/Modifiers/DrawPixelModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/DrawPixelModifierTest.php @@ -2,7 +2,6 @@ namespace Intervention\Image\Tests\Drivers\Gd\Modifiers; -use Intervention\Image\Drivers\Gd\Color; use Intervention\Image\Drivers\Gd\Modifiers\DrawPixelModifier; use Intervention\Image\Geometry\Point; use Intervention\Image\Tests\TestCase; @@ -20,7 +19,7 @@ class DrawPixelModifierTest extends TestCase { $image = $this->createTestImage('trim.png'); $this->assertEquals('00aef0', $image->pickColor(14, 14)->toHex()); - $image->modify(new DrawPixelModifier(new Point(14, 14), new Color(16777215))); + $image->modify(new DrawPixelModifier(new Point(14, 14), 'ffffff')); $this->assertEquals('ffffff', $image->pickColor(14, 14)->toHex()); } } diff --git a/tests/Drivers/Imagick/Modifiers/DrawPixelModifierTest.php b/tests/Drivers/Imagick/Modifiers/DrawPixelModifierTest.php index c0f3cf54..90c076d7 100644 --- a/tests/Drivers/Imagick/Modifiers/DrawPixelModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/DrawPixelModifierTest.php @@ -2,8 +2,6 @@ namespace Intervention\Image\Tests\Drivers\Imagick\Modifiers; -use ImagickPixel; -use Intervention\Image\Drivers\Imagick\Color; use Intervention\Image\Drivers\Imagick\Modifiers\DrawPixelModifier; use Intervention\Image\Geometry\Point; use Intervention\Image\Tests\TestCase; @@ -21,7 +19,7 @@ class DrawPixelModifierTest extends TestCase { $image = $this->createTestImage('trim.png'); $this->assertEquals('00aef0', $image->pickColor(14, 14)->toHex()); - $image->modify(new DrawPixelModifier(new Point(14, 14), new Color(new ImagickPixel('#ffffff')))); + $image->modify(new DrawPixelModifier(new Point(14, 14), 'ffffff')); $this->assertEquals('ffffff', $image->pickColor(14, 14)->toHex()); } } From b5ae8a27237adb9415b450d2fae0e203bd6c708e Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Fri, 12 Aug 2022 17:30:01 +0200 Subject: [PATCH 220/476] Add tests for DrawRectangleModifier --- .../Modifiers/DrawRectangleModifierTest.php | 28 +++++++++++++++++++ .../Modifiers/DrawRectangleModifierTest.php | 24 ++++++++++++++++ 2 files changed, 52 insertions(+) create mode 100644 tests/Drivers/Gd/Modifiers/DrawRectangleModifierTest.php create mode 100644 tests/Drivers/Imagick/Modifiers/DrawRectangleModifierTest.php diff --git a/tests/Drivers/Gd/Modifiers/DrawRectangleModifierTest.php b/tests/Drivers/Gd/Modifiers/DrawRectangleModifierTest.php new file mode 100644 index 00000000..2368c6ee --- /dev/null +++ b/tests/Drivers/Gd/Modifiers/DrawRectangleModifierTest.php @@ -0,0 +1,28 @@ +createTestImage('trim.png'); + $this->assertEquals('00aef0', $image->pickColor(14, 14)->toHex()); + $rectangle = new Rectangle(300, 200); + $rectangle->background('ffffff'); + $image->modify(new DrawRectangleModifier(new Point(14, 14), $rectangle)); + $this->assertEquals('ffffff', $image->pickColor(14, 14)->toHex()); + } +} diff --git a/tests/Drivers/Imagick/Modifiers/DrawRectangleModifierTest.php b/tests/Drivers/Imagick/Modifiers/DrawRectangleModifierTest.php new file mode 100644 index 00000000..876397f8 --- /dev/null +++ b/tests/Drivers/Imagick/Modifiers/DrawRectangleModifierTest.php @@ -0,0 +1,24 @@ +createTestImage('trim.png'); + $this->assertEquals('00aef0', $image->pickColor(14, 14)->toHex()); + $rectangle = new Rectangle(300, 200); + $rectangle->background('ffffff'); + $image->modify(new DrawRectangleModifier(new Point(14, 14), $rectangle)); + $this->assertEquals('ffffff', $image->pickColor(14, 14)->toHex()); + } +} From 71e94564a1f37f8a180378331e10960e814b0d4e Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Fri, 12 Aug 2022 17:38:47 +0200 Subject: [PATCH 221/476] Add tests for DrawLineModifier --- .../Gd/Modifiers/DrawLineModiferTest.php | 28 +++++++++++++++++++ .../Modifiers/DrawLineModifierTest.php | 24 ++++++++++++++++ 2 files changed, 52 insertions(+) create mode 100644 tests/Drivers/Gd/Modifiers/DrawLineModiferTest.php create mode 100644 tests/Drivers/Imagick/Modifiers/DrawLineModifierTest.php diff --git a/tests/Drivers/Gd/Modifiers/DrawLineModiferTest.php b/tests/Drivers/Gd/Modifiers/DrawLineModiferTest.php new file mode 100644 index 00000000..e726f311 --- /dev/null +++ b/tests/Drivers/Gd/Modifiers/DrawLineModiferTest.php @@ -0,0 +1,28 @@ +createTestImage('trim.png'); + $this->assertEquals('00aef0', $image->pickColor(14, 14)->toHex()); + $line = new Line(new Point(0, 0), new Point(10, 0), 4); + $line->background('b53517'); + $image->modify(new DrawLineModifier(new Point(0, 0), $line)); + $this->assertEquals('b53517', $image->pickColor(0, 0)->toHex()); + } +} diff --git a/tests/Drivers/Imagick/Modifiers/DrawLineModifierTest.php b/tests/Drivers/Imagick/Modifiers/DrawLineModifierTest.php new file mode 100644 index 00000000..423e27da --- /dev/null +++ b/tests/Drivers/Imagick/Modifiers/DrawLineModifierTest.php @@ -0,0 +1,24 @@ +createTestImage('trim.png'); + $this->assertEquals('00aef0', $image->pickColor(14, 14)->toHex()); + $line = new Line(new Point(0, 0), new Point(10, 0), 4); + $line->background('b53517'); + $image->modify(new DrawLineModifier(new Point(0, 0), $line)); + $this->assertEquals('b53517', $image->pickColor(0, 0)->toHex()); + } +} From fe05a1e35d6e8b524050f2688f31e5276ddb2224 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Fri, 12 Aug 2022 17:44:59 +0200 Subject: [PATCH 222/476] Add tests for DrawEllipseModifiers --- .../Gd/Modifiers/DrawEllipseModiferTest.php | 28 +++++++++++++++++++ .../Modifiers/DrawEllipseModifierTest.php | 24 ++++++++++++++++ 2 files changed, 52 insertions(+) create mode 100644 tests/Drivers/Gd/Modifiers/DrawEllipseModiferTest.php create mode 100644 tests/Drivers/Imagick/Modifiers/DrawEllipseModifierTest.php diff --git a/tests/Drivers/Gd/Modifiers/DrawEllipseModiferTest.php b/tests/Drivers/Gd/Modifiers/DrawEllipseModiferTest.php new file mode 100644 index 00000000..1c95cd3c --- /dev/null +++ b/tests/Drivers/Gd/Modifiers/DrawEllipseModiferTest.php @@ -0,0 +1,28 @@ +createTestImage('trim.png'); + $this->assertEquals('00aef0', $image->pickColor(14, 14)->toHex()); + $drawable = new Ellipse(10, 10); + $drawable->background('b53717'); + $image->modify(new DrawEllipseModifier(new Point(14, 14), $drawable)); + $this->assertEquals('b53717', $image->pickColor(14, 14)->toHex()); + } +} diff --git a/tests/Drivers/Imagick/Modifiers/DrawEllipseModifierTest.php b/tests/Drivers/Imagick/Modifiers/DrawEllipseModifierTest.php new file mode 100644 index 00000000..a2bed0d8 --- /dev/null +++ b/tests/Drivers/Imagick/Modifiers/DrawEllipseModifierTest.php @@ -0,0 +1,24 @@ +createTestImage('trim.png'); + $this->assertEquals('00aef0', $image->pickColor(14, 14)->toHex()); + $drawable = new Ellipse(10, 10); + $drawable->background('b53717'); + $image->modify(new DrawEllipseModifier(new Point(14, 14), $drawable)); + $this->assertEquals('b53717', $image->pickColor(14, 14)->toHex()); + } +} From de1eb734b6061dc7aa781a52f674b111817da634 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Fri, 12 Aug 2022 17:50:08 +0200 Subject: [PATCH 223/476] Add test for DrawPolygonModifiers --- .../Gd/Modifiers/DrawPolygonModifierTest.php | 28 +++++++++++++++++++ .../Modifiers/DrawPolygonModifierTest.php | 24 ++++++++++++++++ 2 files changed, 52 insertions(+) create mode 100644 tests/Drivers/Gd/Modifiers/DrawPolygonModifierTest.php create mode 100644 tests/Drivers/Imagick/Modifiers/DrawPolygonModifierTest.php diff --git a/tests/Drivers/Gd/Modifiers/DrawPolygonModifierTest.php b/tests/Drivers/Gd/Modifiers/DrawPolygonModifierTest.php new file mode 100644 index 00000000..1b62a24a --- /dev/null +++ b/tests/Drivers/Gd/Modifiers/DrawPolygonModifierTest.php @@ -0,0 +1,28 @@ +createTestImage('trim.png'); + $this->assertEquals('00aef0', $image->pickColor(14, 14)->toHex()); + $drawable = new Polygon([new Point(0, 0), new Point(15, 15), new Point(20, 20)]); + $drawable->background('b53717'); + $image->modify(new DrawPolygonModifier($drawable)); + $this->assertEquals('b53717', $image->pickColor(14, 14)->toHex()); + } +} diff --git a/tests/Drivers/Imagick/Modifiers/DrawPolygonModifierTest.php b/tests/Drivers/Imagick/Modifiers/DrawPolygonModifierTest.php new file mode 100644 index 00000000..02ee6b2b --- /dev/null +++ b/tests/Drivers/Imagick/Modifiers/DrawPolygonModifierTest.php @@ -0,0 +1,24 @@ +createTestImage('trim.png'); + $this->assertEquals('00aef0', $image->pickColor(14, 14)->toHex()); + $drawable = new Polygon([new Point(0, 0), new Point(15, 15), new Point(20, 20)]); + $drawable->background('b53717'); + $image->modify(new DrawPolygonModifier($drawable)); + $this->assertEquals('b53717', $image->pickColor(14, 14)->toHex()); + } +} From ece5ffa358affed67a20836fbde7835e9bf58569 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Fri, 12 Aug 2022 19:02:23 +0200 Subject: [PATCH 224/476] Fix download uri in test environment Fix download uri in test environment for Imagemagick releases. --- .github/workflows/run-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index ae9bec45..96edee48 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -50,7 +50,7 @@ jobs: - name: Install ImageMagick if: ( steps.cache-imagemagick.outputs.cache-hit != 'true' || steps.cache-imagemagick-exists.outputs.files_exists != 'true' ) run: | - curl -o /tmp/ImageMagick.tar.gz -sL https://imagemagick.org/archive/ImageMagick-${{ matrix.imagemagick }}.tar.gz + curl -o /tmp/ImageMagick.tar.gz -sL https://imagemagick.org/archive/releases/ImageMagick-${{ matrix.imagemagick }}.tar.gz ( cd /tmp || exit 1 tar xf ImageMagick.tar.gz From 996560fff28272470e71139df547198d21a61f78 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Fri, 12 Aug 2022 19:16:27 +0200 Subject: [PATCH 225/476] Add missing methods to ImageInterface --- src/Interfaces/ImageInterface.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/Interfaces/ImageInterface.php b/src/Interfaces/ImageInterface.php index 4c1c9a5e..9f4220bb 100644 --- a/src/Interfaces/ImageInterface.php +++ b/src/Interfaces/ImageInterface.php @@ -38,6 +38,13 @@ interface ImageInterface extends Traversable, Countable public function pad(int $width, int $height, $background = 'ffffff', string $position = 'center'): ImageInterface; public function padDown(int $width, int $height, $background = 'ffffff', string $position = 'center'): ImageInterface; public function drawPixel(int $x, int $y, $color = null): ImageInterface; + public function drawRectangle(int $x, int $y, ?callable $init = null): ImageInterface; + public function drawEllipse(int $x, int $y, ?callable $init = null): ImageInterface; + public function drawLine(callable $init = null): ImageInterface; + public function drawPolygon(callable $init = null): ImageInterface; + public function sharpen(int $amount = 10): ImageInterface; + public function flip(): ImageInterface; + public function flop(): ImageInterface; public function getWidth(): int; public function getHeight(): int; public function destroy(): void; From 4374d159317507e92401f6ddc58b7c2e78cd16d7 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Tue, 23 Aug 2022 20:21:00 +0200 Subject: [PATCH 226/476] Add encoders for MS Windows Bitmap format (BMP) --- src/Drivers/Abstract/AbstractImage.php | 12 +++++++ src/Drivers/Gd/Encoders/BmpEncoder.php | 20 +++++++++++ src/Drivers/Imagick/Encoders/BmpEncoder.php | 26 ++++++++++++++ tests/Drivers/Gd/Encoders/BmpEncoderTest.php | 33 +++++++++++++++++ .../Imagick/Encoders/BmpEncoderTest.php | 36 +++++++++++++++++++ 5 files changed, 127 insertions(+) create mode 100644 src/Drivers/Gd/Encoders/BmpEncoder.php create mode 100644 src/Drivers/Imagick/Encoders/BmpEncoder.php create mode 100644 tests/Drivers/Gd/Encoders/BmpEncoderTest.php create mode 100644 tests/Drivers/Imagick/Encoders/BmpEncoderTest.php diff --git a/src/Drivers/Abstract/AbstractImage.php b/src/Drivers/Abstract/AbstractImage.php index fc2d6aad..1bd0f078 100644 --- a/src/Drivers/Abstract/AbstractImage.php +++ b/src/Drivers/Abstract/AbstractImage.php @@ -82,6 +82,18 @@ abstract class AbstractImage implements ImageInterface ); } + public function toBitmap(): EncodedImage + { + return $this->encode( + $this->resolveDriverClass('Encoders\BmpEncoder') + ); + } + + public function toBmp(): EncodedImage + { + return $this->toBitmap(); + } + public function greyscale(): ImageInterface { return $this->modify( diff --git a/src/Drivers/Gd/Encoders/BmpEncoder.php b/src/Drivers/Gd/Encoders/BmpEncoder.php new file mode 100644 index 00000000..422c8b5b --- /dev/null +++ b/src/Drivers/Gd/Encoders/BmpEncoder.php @@ -0,0 +1,20 @@ +getBuffered(function () use ($image) { + imagebmp($image->getFrame()->getCore(), null, false); + }); + + return new EncodedImage($data, 'image/bmp'); + } +} diff --git a/src/Drivers/Imagick/Encoders/BmpEncoder.php b/src/Drivers/Imagick/Encoders/BmpEncoder.php new file mode 100644 index 00000000..6f6bd932 --- /dev/null +++ b/src/Drivers/Imagick/Encoders/BmpEncoder.php @@ -0,0 +1,26 @@ +getFrame()->getCore(); + $imagick->setFormat($format); + $imagick->setImageFormat($format); + $imagick->setCompression($compression); + $imagick->setImageCompression($compression); + + return new EncodedImage($imagick->getImagesBlob(), 'image/bmp'); + } +} diff --git a/tests/Drivers/Gd/Encoders/BmpEncoderTest.php b/tests/Drivers/Gd/Encoders/BmpEncoderTest.php new file mode 100644 index 00000000..d5e2480d --- /dev/null +++ b/tests/Drivers/Gd/Encoders/BmpEncoderTest.php @@ -0,0 +1,33 @@ +getTestImage(); + $encoder = new BmpEncoder(); + $result = $encoder->encode($image); + $this->assertTrue(MimeSniffer::createFromString($result)->matches(new ImageBmp)); + } +} diff --git a/tests/Drivers/Imagick/Encoders/BmpEncoderTest.php b/tests/Drivers/Imagick/Encoders/BmpEncoderTest.php new file mode 100644 index 00000000..2ae1dd51 --- /dev/null +++ b/tests/Drivers/Imagick/Encoders/BmpEncoderTest.php @@ -0,0 +1,36 @@ +newImage(3, 2, new ImagickPixel('red'), 'png'); + + return new Image($imagick); + } + + public function testEncode(): void + { + $image = $this->getTestImage(); + $encoder = new BmpEncoder(); + $result = $encoder->encode($image); + $this->assertTrue( + MimeSniffer::createFromString($result)->matches(new ImageBmp()) + ); + } +} From b4d3ecb018b84a39ffedb77112162f307dc00c0a Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Fri, 9 Jun 2023 15:57:58 +0200 Subject: [PATCH 227/476] Add missing property declaration --- src/Geometry/Circle.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Geometry/Circle.php b/src/Geometry/Circle.php index 841d6a96..0180d82b 100644 --- a/src/Geometry/Circle.php +++ b/src/Geometry/Circle.php @@ -5,7 +5,7 @@ namespace Intervention\Image\Geometry; class Circle extends Ellipse { public function __construct( - int $diameter, + protected int $diameter, protected ?Point $pivot = null ) { $this->setWidth($diameter); From 33be0b1b78898c02d341e8faa6aead6901f06896 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Fri, 9 Jun 2023 15:58:29 +0200 Subject: [PATCH 228/476] Change minimum plattform requirement --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index e121a851..909da5f2 100644 --- a/composer.json +++ b/composer.json @@ -12,7 +12,7 @@ } ], "require": { - "php": "^8", + "php": "^8.1", "intervention/gif": "^3.0", "intervention/mimesniffer": "^0.4.2" }, From f44205538b15731b91a2df723a8f43198ff268a1 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Fri, 9 Jun 2023 15:59:06 +0200 Subject: [PATCH 229/476] Adjust phpunit setup --- phpunit.xml | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/phpunit.xml b/phpunit.xml index 63454441..0fa3ad89 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -1,13 +1,14 @@ From 84d46278f75a8fa7e36883340207bd3253c9928c Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Thu, 28 Sep 2023 17:48:25 +0200 Subject: [PATCH 230/476] Add docker test environment --- Dockerfile | 35 +++++++++++++++++++++++++++++++++++ docker-compose.yml | 9 +++++++++ 2 files changed, 44 insertions(+) create mode 100644 Dockerfile create mode 100644 docker-compose.yml diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..f3471b6c --- /dev/null +++ b/Dockerfile @@ -0,0 +1,35 @@ +FROM php:8-cli + +RUN apt update \ + && apt install -y \ + libpng-dev \ + libicu-dev \ + libpq-dev \ + libzip-dev \ + zip \ + zlib1g-dev \ + locales \ + locales-all \ + libmagickwand-dev \ + libwebp-dev \ + && pecl install imagick \ + && docker-php-ext-configure gd --with-freetype --with-jpeg --with-webp \ + && docker-php-ext-enable imagick \ + && docker-php-ext-install \ + intl \ + opcache \ + pdo \ + pdo_pgsql \ + pdo_mysql \ + pgsql \ + fileinfo \ + mysqli \ + gd \ + bcmath \ + exif \ + zip \ + && apt-get clean + +# install composer +# +RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 00000000..75a62748 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,9 @@ +version: '3' + +services: + tests: + build: ./ + working_dir: /project + command: bash -c "composer install && ./vendor/bin/phpunit -vvv" + volumes: + - ./:/project From 703def91809307b1a328655d26e7d49d61084ddb Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Fri, 29 Sep 2023 15:34:00 +0200 Subject: [PATCH 231/476] Change ImageMagick file extension in Github workflows --- .github/workflows/run-tests.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 96edee48..7d7d4ab9 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -50,10 +50,10 @@ jobs: - name: Install ImageMagick if: ( steps.cache-imagemagick.outputs.cache-hit != 'true' || steps.cache-imagemagick-exists.outputs.files_exists != 'true' ) run: | - curl -o /tmp/ImageMagick.tar.gz -sL https://imagemagick.org/archive/releases/ImageMagick-${{ matrix.imagemagick }}.tar.gz + curl -o /tmp/ImageMagick.tar.xz -sL https://imagemagick.org/archive/releases/ImageMagick-${{ matrix.imagemagick }}.tar.xz ( cd /tmp || exit 1 - tar xf ImageMagick.tar.gz + tar xf ImageMagick.tar.xz cd ImageMagick-${{ matrix.imagemagick }} sudo ./configure --prefix=/home/runner/im/imagemagick-${{ matrix.imagemagick }} sudo make -j$(nproc) From 32fd9481cca3d69b653b49da6d5d2333a15b43ff Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Fri, 29 Sep 2023 15:40:04 +0200 Subject: [PATCH 232/476] Add PHP 8.1 to Github workflows, remove 8.0 support --- .github/workflows/run-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 7d7d4ab9..032093c1 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -8,7 +8,7 @@ jobs: strategy: fail-fast: false matrix: - php: [ '8.0', '8.1' ] + php: [ '8.1', '8.2' ] imagemagick: [ '6.9.12-55', '7.1.0-40' ] imagick: [ '3.7.0' ] stability: [ prefer-stable ] From 4f51d8f0984dc72faae6daa2b8ca2744588e34e0 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Fri, 29 Sep 2023 15:46:55 +0200 Subject: [PATCH 233/476] Fix deprecation warnings for PHP 8.1 --- src/Drivers/Abstract/AbstractFont.php | 2 +- src/Drivers/Gd/Modifiers/DrawPolygonModifier.php | 1 - src/Drivers/Imagick/Image.php | 4 ++-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/Drivers/Abstract/AbstractFont.php b/src/Drivers/Abstract/AbstractFont.php index 4649cc9c..9c8784ce 100644 --- a/src/Drivers/Abstract/AbstractFont.php +++ b/src/Drivers/Abstract/AbstractFont.php @@ -56,7 +56,7 @@ abstract class AbstractFont implements FontInterface public function hasFilename(): bool { - return is_file($this->filename); + return !is_null($this->filename) && is_file($this->filename); } public function color($color): FontInterface diff --git a/src/Drivers/Gd/Modifiers/DrawPolygonModifier.php b/src/Drivers/Gd/Modifiers/DrawPolygonModifier.php index 29dcf95e..cb4c930f 100644 --- a/src/Drivers/Gd/Modifiers/DrawPolygonModifier.php +++ b/src/Drivers/Gd/Modifiers/DrawPolygonModifier.php @@ -22,7 +22,6 @@ class DrawPolygonModifier extends AbstractDrawModifier implements ModifierInterf imagefilledpolygon( $frame->getCore(), $this->polygon()->toArray(), - $this->polygon()->count(), $this->getBackgroundColor()->toInt() ); } diff --git a/src/Drivers/Imagick/Image.php b/src/Drivers/Imagick/Image.php index 906322af..e7b2a9ab 100644 --- a/src/Drivers/Imagick/Image.php +++ b/src/Drivers/Imagick/Image.php @@ -77,14 +77,14 @@ class Image extends AbstractImage implements ImageInterface, Iterator return $this->imagick->getNumberImages(); } - public function current() + public function current(): mixed { $this->imagick->setIteratorIndex($this->iteratorIndex); return new Frame($this->imagick->current()); } - public function key() + public function key(): mixed { return $this->iteratorIndex; } From 166d2f3f92bfdb5ab94816c4d02f69f9a353e527 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Fri, 29 Sep 2023 15:50:21 +0200 Subject: [PATCH 234/476] Fix rounding error in RectangleTest --- tests/Geometry/RectangleTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/Geometry/RectangleTest.php b/tests/Geometry/RectangleTest.php index a74a8a5e..7eebd996 100644 --- a/tests/Geometry/RectangleTest.php +++ b/tests/Geometry/RectangleTest.php @@ -50,13 +50,13 @@ class RectangleTest extends TestCase public function testGetAspectRatio() { $size = new Rectangle(800, 600); - $this->assertEquals(1.33333333333, $size->getAspectRatio()); + $this->assertEquals(1.333, round($size->getAspectRatio(), 3)); $size = new Rectangle(100, 100); $this->assertEquals(1, $size->getAspectRatio()); $size = new Rectangle(1920, 1080); - $this->assertEquals(1.777777777778, $size->getAspectRatio()); + $this->assertEquals(1.778, round($size->getAspectRatio(), 3)); } public function testFitsInto() From d7fd99e4ab4a6f8d76f2db9681bd7423ac6699eb Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Fri, 29 Sep 2023 15:54:13 +0200 Subject: [PATCH 235/476] Fix deprecation warnings --- src/Geometry/Polygon.php | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/src/Geometry/Polygon.php b/src/Geometry/Polygon.php index 50cf3cb0..e7bee7bc 100644 --- a/src/Geometry/Polygon.php +++ b/src/Geometry/Polygon.php @@ -300,7 +300,9 @@ class Polygon implements IteratorAggregate, Countable, ArrayAccess, DrawableInte } foreach ($this->points as $point) { - $point->setX($point->getX() - $diff); + $point->setX( + intval($point->getX() - $diff) + ); } return $this; @@ -331,7 +333,9 @@ class Polygon implements IteratorAggregate, Countable, ArrayAccess, DrawableInte } foreach ($this->points as $point) { - $point->setY($point->getY() - $diff); + $point->setY( + intval($point->getY() - $diff), + ); } return $this; @@ -350,16 +354,24 @@ class Polygon implements IteratorAggregate, Countable, ArrayAccess, DrawableInte foreach ($this->points as $point) { // translate point to pivot - $point->setX($point->getX() - $this->getPivot()->getX()); - $point->setY($point->getY() - $this->getPivot()->getY()); + $point->setX( + intval($point->getX() - $this->getPivot()->getX()), + ); + $point->setY( + intval($point->getY() - $this->getPivot()->getY()), + ); // rotate point $x = $point->getX() * $cos - $point->getY() * $sin; $y = $point->getX() * $sin + $point->getY() * $cos; // translate point back - $point->setX($x + $this->getPivot()->getX()); - $point->setY($y + $this->getPivot()->getY()); + $point->setX( + intval($x + $this->getPivot()->getX()), + ); + $point->setY( + intval($y + $this->getPivot()->getY()), + ); } return $this; From 834d5cb2ba39900d1b6c2dc9f71120d5387194dc Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Fri, 29 Sep 2023 16:14:11 +0200 Subject: [PATCH 236/476] Rename parameter --- src/Drivers/Gd/Image.php | 4 +-- src/Drivers/Imagick/Image.php | 4 +-- src/Interfaces/ImageInterface.php | 43 ++++++++++++++++++++++++++++++- 3 files changed, 46 insertions(+), 5 deletions(-) diff --git a/src/Drivers/Gd/Image.php b/src/Drivers/Gd/Image.php index fdacd9d7..ad9b3754 100644 --- a/src/Drivers/Gd/Image.php +++ b/src/Drivers/Gd/Image.php @@ -44,9 +44,9 @@ class Image extends AbstractImage implements ImageInterface, IteratorAggregate return $this; } - public function getFrame(int $key = 0): ?FrameInterface + public function getFrame(int $position = 0): ?FrameInterface { - return $this->frames->get($key); + return $this->frames->get($position); } public function addFrame(FrameInterface $frame): ImageInterface diff --git a/src/Drivers/Imagick/Image.php b/src/Drivers/Imagick/Image.php index e7b2a9ab..e5cf937f 100644 --- a/src/Drivers/Imagick/Image.php +++ b/src/Drivers/Imagick/Image.php @@ -24,10 +24,10 @@ class Image extends AbstractImage implements ImageInterface, Iterator return $this->imagick; } - public function getFrame(int $key = 0): ?FrameInterface + public function getFrame(int $position = 0): ?FrameInterface { try { - $this->imagick->setIteratorIndex($key); + $this->imagick->setIteratorIndex($position); } catch (ImagickException $e) { return null; } diff --git a/src/Interfaces/ImageInterface.php b/src/Interfaces/ImageInterface.php index 9f4220bb..0eac33c3 100644 --- a/src/Interfaces/ImageInterface.php +++ b/src/Interfaces/ImageInterface.php @@ -8,18 +8,59 @@ use Traversable; interface ImageInterface extends Traversable, Countable { - public function getFrame(int $key = 0): ?FrameInterface; + /** + * Get frame of animation image at given position starting with zero + * + * @param int $key + * @return null|FrameInterface + */ + public function getFrame(int $position = 0): ?FrameInterface; public function addFrame(FrameInterface $frame): ImageInterface; public function setLoops(int $count): ImageInterface; public function getLoops(): int; public function getSize(): SizeInterface; public function isAnimated(): bool; public function modify(ModifierInterface $modifier): ImageInterface; + + /** + * Encode image with given encoder + * + * @param EncoderInterface $encoder + * @return EncodedImage + */ public function encode(EncoderInterface $encoder): EncodedImage; + + /** + * Encode image to jpeg format + * + * @param int $quality + * @return EncodedImage + */ public function toJpeg(int $quality = 75): EncodedImage; + + /** + * Encode image to webp format + * + * @param int $quality + * @return EncodedImage + */ public function toWebp(int $quality = 75): EncodedImage; + + /** + * Encode image to gif format + * + * @return EncodedImage + */ public function toGif(): EncodedImage; + + + /** + * Encode image to png format + * + * @return EncodedImage + */ public function toPng(): EncodedImage; + public function pickColors(int $x, int $y): CollectionInterface; public function text(string $text, int $x, int $y, ?callable $init = null): ImageInterface; public function pickColor(int $x, int $y, int $frame_key = 0): ?ColorInterface; From af9fea016ae6255bc13eab2cce9dbec1d6aefcb2 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Fri, 29 Sep 2023 16:22:40 +0200 Subject: [PATCH 237/476] Add doc blocks --- src/Interfaces/ImageInterface.php | 116 +++++++++++++++++++++++++++++- 1 file changed, 115 insertions(+), 1 deletion(-) diff --git a/src/Interfaces/ImageInterface.php b/src/Interfaces/ImageInterface.php index 0eac33c3..34262ad3 100644 --- a/src/Interfaces/ImageInterface.php +++ b/src/Interfaces/ImageInterface.php @@ -15,11 +15,51 @@ interface ImageInterface extends Traversable, Countable * @return null|FrameInterface */ public function getFrame(int $position = 0): ?FrameInterface; + + /** + * Add frame to animated image + * + * @param FrameInterface $frame + * @return ImageInterface + */ public function addFrame(FrameInterface $frame): ImageInterface; + + /** + * Set loop count of animated image + * + * @param int $count + * @return ImageInterface + */ public function setLoops(int $count): ImageInterface; + + /** + * Return loop count of animated image + * + * @return int + */ public function getLoops(): int; + + /** + * Return size of current image + * + * @return SizeInterface + */ public function getSize(): SizeInterface; + + /** + * Determine if current image is animated + * + * @return bool + */ public function isAnimated(): bool; + + + /** + * Apply given modifier to current image + * + * @param ModifierInterface $modifier + * @return ImageInterface + */ public function modify(ModifierInterface $modifier): ImageInterface; /** @@ -61,10 +101,24 @@ interface ImageInterface extends Traversable, Countable */ public function toPng(): EncodedImage; + public function pickColor(int $x, int $y, int $frame_key = 0): ?ColorInterface; public function pickColors(int $x, int $y): CollectionInterface; public function text(string $text, int $x, int $y, ?callable $init = null): ImageInterface; - public function pickColor(int $x, int $y, int $frame_key = 0): ?ColorInterface; + + /** + * Turn image into a greyscale version + * + * @return void + */ public function greyscale(): ImageInterface; + + + /** + * Blur current image by given strength + * + * @param int $amount + * @return ImageInterface + */ public function blur(int $amount = 5): ImageInterface; public function rotate(float $angle, $background = 'ffffff'): ImageInterface; public function place($element, string $position = 'top-left', int $offset_x = 0, int $offset_y = 0): ImageInterface; @@ -80,13 +134,73 @@ interface ImageInterface extends Traversable, Countable public function padDown(int $width, int $height, $background = 'ffffff', string $position = 'center'): ImageInterface; public function drawPixel(int $x, int $y, $color = null): ImageInterface; public function drawRectangle(int $x, int $y, ?callable $init = null): ImageInterface; + + /** + * Draw ellipse ot given position on current image + * + * @param int $x + * @param int $y + * @param null|callable $init + * @return ImageInterface + */ public function drawEllipse(int $x, int $y, ?callable $init = null): ImageInterface; + + /** + * Draw line on image + * + * @param callable|null $init + * @return ImageInterface + */ public function drawLine(callable $init = null): ImageInterface; + + /** + * Draw polygon on image + * + * @param callable|null $init + * @return ImageInterface + */ public function drawPolygon(callable $init = null): ImageInterface; + + /** + * Sharpen the current image with given strength + * + * @param int $amount + * @return ImageInterface + */ public function sharpen(int $amount = 10): ImageInterface; + + /** + * Mirror the current image horizontally + * + * @return void + */ public function flip(): ImageInterface; + + /** + * Mirror the current image vertically + * + * @return void + */ public function flop(): ImageInterface; + + /** + * Return image width in pixels + * + * @return int + */ public function getWidth(): int; + + /** + * Return image height in pixels + * + * @return int + */ public function getHeight(): int; + + /** + * Destroy current image instance and free up memory + * + * @return void + */ public function destroy(): void; } From b8f753b33f4b3601a4a7088a92b99bf9e81f427f Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Fri, 29 Sep 2023 16:25:33 +0200 Subject: [PATCH 238/476] Fix deprecation warning --- src/Geometry/Polygon.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Geometry/Polygon.php b/src/Geometry/Polygon.php index e7bee7bc..244dd7ce 100644 --- a/src/Geometry/Polygon.php +++ b/src/Geometry/Polygon.php @@ -106,7 +106,7 @@ class Polygon implements IteratorAggregate, Countable, ArrayAccess, DrawableInte * @param mixed $offset * @return Point */ - public function offsetGet($offset) + public function offsetGet($offset): mixed { return $this->points[$offset]; } From cdee873d8a02dc8355d6548b1c7fade0543a3d6a Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Fri, 29 Sep 2023 17:06:31 +0200 Subject: [PATCH 239/476] Change code examples and requirements in readme --- readme.md | 4 +-- src/Interfaces/ImageInterface.php | 43 ++++++++++++++++++++++++++----- 2 files changed, 39 insertions(+), 8 deletions(-) diff --git a/readme.md b/readme.md index 7704f23e..0cd1f4d5 100644 --- a/readme.md +++ b/readme.md @@ -23,7 +23,7 @@ $manager = new ImageManager('gd') $image = $manager->make('images/example.gif'); // resize image instance -$image->resize(320, 240); +$image->resize(height: 300); // insert a watermark $image->place('images/watermark.png'); @@ -37,7 +37,7 @@ $encoded->save('images/example.jpg'); ## Requirements -- PHP >=8.0 +- PHP >= 8.1 ## Supported Image Libraries diff --git a/src/Interfaces/ImageInterface.php b/src/Interfaces/ImageInterface.php index 34262ad3..2e28106f 100644 --- a/src/Interfaces/ImageInterface.php +++ b/src/Interfaces/ImageInterface.php @@ -103,6 +103,16 @@ interface ImageInterface extends Traversable, Countable public function pickColor(int $x, int $y, int $frame_key = 0): ?ColorInterface; public function pickColors(int $x, int $y): CollectionInterface; + + /** + * Draw text on image + * + * @param string $text + * @param int $x + * @param int $y + * @param null|callable $init + * @return ImageInterface + */ public function text(string $text, int $x, int $y, ?callable $init = null): ImageInterface; /** @@ -120,8 +130,29 @@ interface ImageInterface extends Traversable, Countable * @return ImageInterface */ public function blur(int $amount = 5): ImageInterface; + + + /** + * Rotate current image by given angle + * + * @param float $angle + * @param string $background + * @return ImageInterface + */ public function rotate(float $angle, $background = 'ffffff'): ImageInterface; + + + /** + * Place another image into the current image instance + * + * @param mixed $element + * @param string $position + * @param int $offset_x + * @param int $offset_y + * @return ImageInterface + */ public function place($element, string $position = 'top-left', int $offset_x = 0, int $offset_y = 0): ImageInterface; + public function fill($color, ?int $x = null, ?int $y = null): ImageInterface; public function pixelate(int $size): ImageInterface; public function resize(?int $width = null, ?int $height = null): ImageInterface; @@ -138,9 +169,9 @@ interface ImageInterface extends Traversable, Countable /** * Draw ellipse ot given position on current image * - * @param int $x - * @param int $y - * @param null|callable $init + * @param int $x + * @param int $y + * @param null|callable $init * @return ImageInterface */ public function drawEllipse(int $x, int $y, ?callable $init = null): ImageInterface; @@ -148,7 +179,7 @@ interface ImageInterface extends Traversable, Countable /** * Draw line on image * - * @param callable|null $init + * @param callable|null $init * @return ImageInterface */ public function drawLine(callable $init = null): ImageInterface; @@ -156,7 +187,7 @@ interface ImageInterface extends Traversable, Countable /** * Draw polygon on image * - * @param callable|null $init + * @param callable|null $init * @return ImageInterface */ public function drawPolygon(callable $init = null): ImageInterface; @@ -164,7 +195,7 @@ interface ImageInterface extends Traversable, Countable /** * Sharpen the current image with given strength * - * @param int $amount + * @param int $amount * @return ImageInterface */ public function sharpen(int $amount = 10): ImageInterface; From 78b968a28c95993d5ae26cbdcfa1541baca304ee Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 30 Sep 2023 10:19:01 +0200 Subject: [PATCH 240/476] Refactorin input handler chain building process --- src/Drivers/Abstract/AbstractInputHandler.php | 35 ++++++++++++++++++- src/Drivers/Gd/InputHandler.php | 35 +++++++------------ src/Drivers/Imagick/InputHandler.php | 35 +++++++------------ 3 files changed, 58 insertions(+), 47 deletions(-) diff --git a/src/Drivers/Abstract/AbstractInputHandler.php b/src/Drivers/Abstract/AbstractInputHandler.php index fce61f85..0ed0e0bb 100644 --- a/src/Drivers/Abstract/AbstractInputHandler.php +++ b/src/Drivers/Abstract/AbstractInputHandler.php @@ -3,13 +3,46 @@ namespace Intervention\Image\Drivers\Abstract; use Intervention\Image\Drivers\Abstract\Decoders\AbstractDecoder; +use Intervention\Image\Exceptions\DecoderException; use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\ImageInterface; abstract class AbstractInputHandler { - abstract protected function chain(): AbstractDecoder; + /** + * Array of decoders which will be stacked into to the input handler chain + */ + protected $decoders = []; + /** + * Stack the decoder array into a nested decoder object + * + * @return AbstractDecoder + */ + protected function chain(): AbstractDecoder + { + if (count($this->decoders) == 0) { + throw new DecoderException('No decoders found in ' . get_class($this)); + } + + // get instance of last decoder in stack + list($classname) = array_slice(array_reverse($this->decoders), 0, 1); + $chain = new $classname(); + + // build decoder chain + foreach (array_slice(array_reverse($this->decoders), 1) as $classname) { + $chain = new $classname($chain); + } + + return $chain; + } + + /** + * Try to decode the given input with each decoder of the the handler chain + * + * @param mixed $var + * @return ImageInterface|ColorInterface + */ public function handle($input): ImageInterface|ColorInterface { return $this->chain()->handle($input); diff --git a/src/Drivers/Gd/InputHandler.php b/src/Drivers/Gd/InputHandler.php index f32612e6..89dccc71 100644 --- a/src/Drivers/Gd/InputHandler.php +++ b/src/Drivers/Gd/InputHandler.php @@ -3,30 +3,19 @@ namespace Intervention\Image\Drivers\Gd; use Intervention\Image\Drivers\Abstract\AbstractInputHandler; -use Intervention\Image\Drivers\Abstract\Decoders\AbstractDecoder; class InputHandler extends AbstractInputHandler { - protected function chain(): AbstractDecoder - { - return new Decoders\ImageObjectDecoder( - new Decoders\ArrayColorDecoder( - new Decoders\HtmlColorNameDecoder( - new Decoders\RgbStringColorDecoder( - new Decoders\HexColorDecoder( - new Decoders\TransparentColorDecoder( - new Decoders\FilePathImageDecoder( - new Decoders\BinaryImageDecoder( - new Decoders\DataUriImageDecoder( - new Decoders\Base64ImageDecoder() - ) - ) - ) - ) - ) - ) - ) - ) - ); - } + protected $decoders = [ + Decoders\ImageObjectDecoder::class, + Decoders\ArrayColorDecoder::class, + Decoders\HtmlColorNameDecoder::class, + Decoders\RgbStringColorDecoder::class, + Decoders\HexColorDecoder::class, + Decoders\TransparentColorDecoder::class, + Decoders\FilePathImageDecoder::class, + Decoders\BinaryImageDecoder::class, + Decoders\DataUriImageDecoder::class, + Decoders\Base64ImageDecoder::class, + ]; } diff --git a/src/Drivers/Imagick/InputHandler.php b/src/Drivers/Imagick/InputHandler.php index 90577ff1..05a4100e 100644 --- a/src/Drivers/Imagick/InputHandler.php +++ b/src/Drivers/Imagick/InputHandler.php @@ -3,30 +3,19 @@ namespace Intervention\Image\Drivers\Imagick; use Intervention\Image\Drivers\Abstract\AbstractInputHandler; -use Intervention\Image\Drivers\Abstract\Decoders\AbstractDecoder; class InputHandler extends AbstractInputHandler { - protected function chain(): AbstractDecoder - { - return new Decoders\ImageObjectDecoder( - new Decoders\ArrayColorDecoder( - new Decoders\HexColorDecoder( - new Decoders\HtmlColorNameDecoder( - new Decoders\RgbStringColorDecoder( - new Decoders\TransparentColorDecoder( - new Decoders\FilePathImageDecoder( - new Decoders\BinaryImageDecoder( - new Decoders\DataUriImageDecoder( - new Decoders\Base64ImageDecoder() - ) - ) - ) - ) - ) - ) - ) - ) - ); - } + protected $decoders = [ + Decoders\ImageObjectDecoder::class, + Decoders\ArrayColorDecoder::class, + Decoders\HexColorDecoder::class, + Decoders\HtmlColorNameDecoder::class, + Decoders\RgbStringColorDecoder::class, + Decoders\TransparentColorDecoder::class, + Decoders\FilePathImageDecoder::class, + Decoders\BinaryImageDecoder::class, + Decoders\DataUriImageDecoder::class, + Decoders\Base64ImageDecoder::class + ]; } From 1922fae048d47efd2d8be673fc1462bb39e00818 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 30 Sep 2023 10:32:34 +0200 Subject: [PATCH 241/476] Add doc block documentation --- src/Interfaces/ImageInterface.php | 85 ++++++++++++++++++++++++++++--- 1 file changed, 77 insertions(+), 8 deletions(-) diff --git a/src/Interfaces/ImageInterface.php b/src/Interfaces/ImageInterface.php index 2e28106f..7930a826 100644 --- a/src/Interfaces/ImageInterface.php +++ b/src/Interfaces/ImageInterface.php @@ -153,16 +153,85 @@ interface ImageInterface extends Traversable, Countable */ public function place($element, string $position = 'top-left', int $offset_x = 0, int $offset_y = 0): ImageInterface; + /** + * Stretch the image to the desired size + * + * @param null|int $width + * @param null|int $height + * @return ImageInterface + */ + public function resize(?int $width = null, ?int $height = null): ImageInterface; + + /** + * Stretch the image to the desired size but do not exceed the original size + * + * @param null|int $width + * @param null|int $height + * @return ImageInterface + */ + public function resizeDown(?int $width = null, ?int $height = null): ImageInterface; + + /** + * Resize the image and keep the image aspect ration proportions + * + * @param null|int $width + * @param null|int $height + * @return ImageInterface + */ + public function scale(?int $width = null, ?int $height = null): ImageInterface; + + /** + * Resize the image and keep the image aspect ration proportions but do not exceed the original size + * + * @param null|int $width + * @param null|int $height + * @return ImageInterface + */ + public function scaleDown(?int $width = null, ?int $height = null): ImageInterface; + + /** + * + * Takes the given dimensions and scales it to the largest possible size matching + * the original size. Then this size is positioned on the original and cut out + * before being resized to the desired size from the arguments + * + * @param int $width + * @param int $height + * @param string $position + * @return ImageInterface + */ + public function fit(int $width, int $height, string $position = 'center'): ImageInterface; + + /** + * Same as fit() but do not exceeds the original image size + * + * @param int $width + * @param int $height + * @param string $position + * @return ImageInterface + */ + public function fitDown(int $width, int $height, string $position = 'center'): ImageInterface; + + /** + * @param int $width + * @param int $height + * @param string $background + * @param string $position + * @return ImageInterface + */ + public function pad(int $width, int $height, $background = 'ffffff', string $position = 'center'): ImageInterface; + + /** + * @param int $width + * @param int $height + * @param string $background + * @param string $position + * @return ImageInterface + */ + public function padDown(int $width, int $height, $background = 'ffffff', string $position = 'center'): ImageInterface; + public function fill($color, ?int $x = null, ?int $y = null): ImageInterface; public function pixelate(int $size): ImageInterface; - public function resize(?int $width = null, ?int $height = null): ImageInterface; - public function resizeDown(?int $width = null, ?int $height = null): ImageInterface; - public function scale(?int $width = null, ?int $height = null): ImageInterface; - public function scaleDown(?int $width = null, ?int $height = null): ImageInterface; - public function fit(int $width, int $height, string $position = 'center'): ImageInterface; - public function fitDown(int $width, int $height, string $position = 'center'): ImageInterface; - public function pad(int $width, int $height, $background = 'ffffff', string $position = 'center'): ImageInterface; - public function padDown(int $width, int $height, $background = 'ffffff', string $position = 'center'): ImageInterface; public function drawPixel(int $x, int $y, $color = null): ImageInterface; public function drawRectangle(int $x, int $y, ?callable $init = null): ImageInterface; From 50f7ebce783d8dda3282485cb1e665b562550b17 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 30 Sep 2023 10:56:30 +0200 Subject: [PATCH 242/476] Fix random bugs --- src/Drivers/Abstract/AbstractImage.php | 2 +- src/Drivers/Abstract/AbstractInputHandler.php | 2 +- src/Drivers/Gd/Modifiers/PadDownModifier.php | 2 +- .../Imagick/Modifiers/DrawPixelModifier.php | 21 ++++++++++++---- .../Imagick/Modifiers/DrawPolygonModifier.php | 24 +++++++++++++++++++ .../Modifiers/DrawRectangleModifier.php | 5 ++-- .../Imagick/Modifiers/PadDownModifier.php | 2 +- src/Drivers/Imagick/Modifiers/TextWriter.php | 18 +------------- src/Geometry/Rectangle.php | 4 ++-- src/Interfaces/ImageInterface.php | 17 +++++++++---- 10 files changed, 62 insertions(+), 35 deletions(-) diff --git a/src/Drivers/Abstract/AbstractImage.php b/src/Drivers/Abstract/AbstractImage.php index 1bd0f078..25a6cf35 100644 --- a/src/Drivers/Abstract/AbstractImage.php +++ b/src/Drivers/Abstract/AbstractImage.php @@ -25,7 +25,7 @@ abstract class AbstractImage implements ImageInterface use CanHandleInput; use CanRunCallback; - public function eachFrame(callable $callback): self + public function eachFrame(callable $callback): ImageInterface { foreach ($this as $frame) { $callback($frame); diff --git a/src/Drivers/Abstract/AbstractInputHandler.php b/src/Drivers/Abstract/AbstractInputHandler.php index 0ed0e0bb..d0f25c9b 100644 --- a/src/Drivers/Abstract/AbstractInputHandler.php +++ b/src/Drivers/Abstract/AbstractInputHandler.php @@ -40,7 +40,7 @@ abstract class AbstractInputHandler /** * Try to decode the given input with each decoder of the the handler chain * - * @param mixed $var + * @param mixed $input * @return ImageInterface|ColorInterface */ public function handle($input): ImageInterface|ColorInterface diff --git a/src/Drivers/Gd/Modifiers/PadDownModifier.php b/src/Drivers/Gd/Modifiers/PadDownModifier.php index 53247d6c..a819158e 100644 --- a/src/Drivers/Gd/Modifiers/PadDownModifier.php +++ b/src/Drivers/Gd/Modifiers/PadDownModifier.php @@ -13,7 +13,7 @@ class PadDownModifier extends PadModifier $resize = $this->getResizeSize($image); return $image->getSize() - ->contain($resize->width(), $resize->height()) + ->contain($resize->getWidth(), $resize->getHeight()) ->alignPivotTo($resize, $this->position); } diff --git a/src/Drivers/Imagick/Modifiers/DrawPixelModifier.php b/src/Drivers/Imagick/Modifiers/DrawPixelModifier.php index 1b5edc9d..d975f93f 100644 --- a/src/Drivers/Imagick/Modifiers/DrawPixelModifier.php +++ b/src/Drivers/Imagick/Modifiers/DrawPixelModifier.php @@ -3,6 +3,8 @@ namespace Intervention\Image\Drivers\Imagick\Modifiers; use ImagickDraw; +use Intervention\Image\Drivers\Imagick\Color; +use Intervention\Image\Exceptions\TypeException; use Intervention\Image\Geometry\Point; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; @@ -12,16 +14,14 @@ class DrawPixelModifier implements ModifierInterface { use CanHandleInput; - public function __construct( - protected Point $position, - protected $color - ) { + public function __construct(protected Point $position, protected mixed $color) + { // } public function apply(ImageInterface $image): ImageInterface { - $color = $this->handleInput($this->color); + $color = $this->decodeColor(); $pixel = new ImagickDraw(); $pixel->setFillColor($color->getPixel()); $pixel->point($this->position->getX(), $this->position->getY()); @@ -30,4 +30,15 @@ class DrawPixelModifier implements ModifierInterface $frame->getCore()->drawImage($pixel); }); } + + private function decodeColor(): Color + { + $color = $this->handleInput($this->color); + + if (!is_a($color, Color::class)) { + throw new TypeException('Color is not compatible to current driver.'); + } + + return $color; + } } diff --git a/src/Drivers/Imagick/Modifiers/DrawPolygonModifier.php b/src/Drivers/Imagick/Modifiers/DrawPolygonModifier.php index 8f2f7fdb..8c39081b 100644 --- a/src/Drivers/Imagick/Modifiers/DrawPolygonModifier.php +++ b/src/Drivers/Imagick/Modifiers/DrawPolygonModifier.php @@ -4,6 +4,8 @@ namespace Intervention\Image\Drivers\Imagick\Modifiers; use ImagickDraw; use Intervention\Image\Drivers\Abstract\Modifiers\AbstractDrawModifier; +use Intervention\Image\Drivers\Imagick\Color; +use Intervention\Image\Exceptions\TypeException; use Intervention\Image\Interfaces\DrawableInterface; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; @@ -44,4 +46,26 @@ class DrawPolygonModifier extends AbstractDrawModifier implements ModifierInterf return $points; } + + protected function getBackgroundColor(): ?Color + { + $color = parent::getBackgroundColor(); + + if (!is_a($color, Color::class)) { + throw new TypeException('Color is not compatible to current driver.'); + } + + return $color; + } + + protected function getBorderColor(): ?Color + { + $color = parent::getBorderColor(); + + if (!is_a($color, Color::class)) { + throw new TypeException('Color is not compatible to current driver.'); + } + + return $color; + } } diff --git a/src/Drivers/Imagick/Modifiers/DrawRectangleModifier.php b/src/Drivers/Imagick/Modifiers/DrawRectangleModifier.php index 330f7874..8993de8a 100644 --- a/src/Drivers/Imagick/Modifiers/DrawRectangleModifier.php +++ b/src/Drivers/Imagick/Modifiers/DrawRectangleModifier.php @@ -8,7 +8,6 @@ use Intervention\Image\Drivers\Imagick\Color; use Intervention\Image\Exceptions\DecoderException; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; -use Intervention\Image\Interfaces\ColorInterface; class DrawRectangleModifier extends AbstractDrawModifier implements ModifierInterface { @@ -37,7 +36,7 @@ class DrawRectangleModifier extends AbstractDrawModifier implements ModifierInte return $image; } - protected function getBackgroundColor(): ColorInterface + protected function getBackgroundColor(): Color { $color = parent::getBackgroundColor(); if (!is_a($color, Color::class)) { @@ -47,7 +46,7 @@ class DrawRectangleModifier extends AbstractDrawModifier implements ModifierInte return $color; } - protected function getBorderColor(): ColorInterface + protected function getBorderColor(): Color { $color = parent::getBorderColor(); if (!is_a($color, Color::class)) { diff --git a/src/Drivers/Imagick/Modifiers/PadDownModifier.php b/src/Drivers/Imagick/Modifiers/PadDownModifier.php index f6de2fc4..c212134a 100644 --- a/src/Drivers/Imagick/Modifiers/PadDownModifier.php +++ b/src/Drivers/Imagick/Modifiers/PadDownModifier.php @@ -13,7 +13,7 @@ class PadDownModifier extends PadModifier $resize = $this->getResizeSize($image); return $image->getSize() - ->contain($resize->width(), $resize->height()) + ->contain($resize->getWidth(), $resize->getHeight()) ->alignPivotTo($resize, $this->position); } diff --git a/src/Drivers/Imagick/Modifiers/TextWriter.php b/src/Drivers/Imagick/Modifiers/TextWriter.php index b0e1572c..90469983 100644 --- a/src/Drivers/Imagick/Modifiers/TextWriter.php +++ b/src/Drivers/Imagick/Modifiers/TextWriter.php @@ -5,7 +5,6 @@ namespace Intervention\Image\Drivers\Imagick\Modifiers; use Intervention\Image\Drivers\Abstract\AbstractTextWriter; use Intervention\Image\Drivers\Imagick\Font; use Intervention\Image\Exceptions\FontException; -use Intervention\Image\Interfaces\FontInterface; use Intervention\Image\Interfaces\ImageInterface; class TextWriter extends AbstractTextWriter @@ -23,27 +22,12 @@ class TextWriter extends AbstractTextWriter $line ); } - - // debug - // $lines = new TextBlock($this->text); - // $box = $lines->getBoundingBox($this->font, $this->position); - // $points = []; - // foreach (array_chunk($box->toArray(), 2) as $p) { - // $points[] = ['x' => $p[0], 'y' => $p[1]]; - // } - // $draw = new \ImagickDraw(); - // $draw->setStrokeOpacity(1); - // $draw->setStrokeColor('black'); - // $draw->setFillColor('transparent'); - // $draw->polygon($points); - // $frame->getCore()->drawImage($draw); - } return $image; } - protected function getFont(): FontInterface + protected function getFont(): Font { if (!is_a($this->font, Font::class)) { throw new FontException('Font is not compatible to current driver.'); diff --git a/src/Geometry/Rectangle.php b/src/Geometry/Rectangle.php index c7e5b1bb..b244a60c 100644 --- a/src/Geometry/Rectangle.php +++ b/src/Geometry/Rectangle.php @@ -177,8 +177,8 @@ class Rectangle extends Polygon implements SizeInterface, DrawableInterface * Calculate the relative position to another Size * based on the pivot point settings of both sizes. * - * @param Size $size - * @return Point + * @param SizeInterface $rectangle + * @return PointInterface */ public function getRelativePositionTo(SizeInterface $rectangle): PointInterface { diff --git a/src/Interfaces/ImageInterface.php b/src/Interfaces/ImageInterface.php index 7930a826..4b04f761 100644 --- a/src/Interfaces/ImageInterface.php +++ b/src/Interfaces/ImageInterface.php @@ -11,7 +11,7 @@ interface ImageInterface extends Traversable, Countable /** * Get frame of animation image at given position starting with zero * - * @param int $key + * @param int $position * @return null|FrameInterface */ public function getFrame(int $position = 0): ?FrameInterface; @@ -24,6 +24,15 @@ interface ImageInterface extends Traversable, Countable */ public function addFrame(FrameInterface $frame): ImageInterface; + + /** + * Apply given callback to each frame of the image + * + * @param callable $callback + * @return ImageInterface + */ + public function eachFrame(callable $callback): ImageInterface; + /** * Set loop count of animated image * @@ -118,7 +127,7 @@ interface ImageInterface extends Traversable, Countable /** * Turn image into a greyscale version * - * @return void + * @return ImageInterface */ public function greyscale(): ImageInterface; @@ -272,14 +281,14 @@ interface ImageInterface extends Traversable, Countable /** * Mirror the current image horizontally * - * @return void + * @return ImageInterface */ public function flip(): ImageInterface; /** * Mirror the current image vertically * - * @return void + * @return ImageInterface */ public function flop(): ImageInterface; From 25572c0ef9f694c3acfbc97173b48a294f5a5656 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 30 Sep 2023 11:30:03 +0200 Subject: [PATCH 243/476] Refactor checking for driver objects --- src/Drivers/Abstract/AbstractFont.php | 2 ++ src/Drivers/Abstract/AbstractTextWriter.php | 3 ++ .../Modifiers/AbstractDrawModifier.php | 2 ++ .../Modifiers/AbstractPadModifier.php | 3 ++ .../Modifiers/AbstractRotateModifier.php | 2 ++ src/Drivers/Gd/Modifiers/TextWriter.php | 27 +++++----------- src/Drivers/Imagick/Font.php | 16 ++-------- .../Imagick/Modifiers/DrawEllipseModifier.php | 31 ++++--------------- .../Imagick/Modifiers/DrawLineModifier.php | 4 ++- .../Imagick/Modifiers/DrawPixelModifier.php | 20 +++++------- .../Imagick/Modifiers/DrawPolygonModifier.php | 30 +++--------------- .../Modifiers/DrawRectangleModifier.php | 30 ++++-------------- src/Drivers/Imagick/Modifiers/PadModifier.php | 7 +---- .../Imagick/Modifiers/RotateModifier.php | 6 +--- src/Drivers/Imagick/Modifiers/TextWriter.php | 15 +++------ src/Traits/CanCheckType.php | 17 ++++++++++ src/Traits/CanHandleInput.php | 11 +++++-- .../Modifiers/AbstractRotateModifierTest.php | 2 +- 18 files changed, 82 insertions(+), 146 deletions(-) create mode 100644 src/Traits/CanCheckType.php diff --git a/src/Drivers/Abstract/AbstractFont.php b/src/Drivers/Abstract/AbstractFont.php index 9c8784ce..dd39739c 100644 --- a/src/Drivers/Abstract/AbstractFont.php +++ b/src/Drivers/Abstract/AbstractFont.php @@ -4,11 +4,13 @@ namespace Intervention\Image\Drivers\Abstract; use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\FontInterface; +use Intervention\Image\Traits\CanCheckType; use Intervention\Image\Traits\CanHandleInput; abstract class AbstractFont implements FontInterface { use CanHandleInput; + use CanCheckType; protected $size = 12; protected $angle = 0; diff --git a/src/Drivers/Abstract/AbstractTextWriter.php b/src/Drivers/Abstract/AbstractTextWriter.php index 502f4172..29078bd7 100644 --- a/src/Drivers/Abstract/AbstractTextWriter.php +++ b/src/Drivers/Abstract/AbstractTextWriter.php @@ -5,10 +5,13 @@ namespace Intervention\Image\Drivers\Abstract; use Intervention\Image\Geometry\Point; use Intervention\Image\Interfaces\FontInterface; use Intervention\Image\Interfaces\ModifierInterface; +use Intervention\Image\Traits\CanCheckType; use Intervention\Image\Typography\TextBlock; abstract class AbstractTextWriter implements ModifierInterface { + use CanCheckType; + public function __construct( protected Point $position, protected FontInterface $font, diff --git a/src/Drivers/Abstract/Modifiers/AbstractDrawModifier.php b/src/Drivers/Abstract/Modifiers/AbstractDrawModifier.php index 42e89eff..50d67c0a 100644 --- a/src/Drivers/Abstract/Modifiers/AbstractDrawModifier.php +++ b/src/Drivers/Abstract/Modifiers/AbstractDrawModifier.php @@ -11,11 +11,13 @@ use Intervention\Image\Geometry\Rectangle; use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\DrawableInterface; use Intervention\Image\Interfaces\PointInterface; +use Intervention\Image\Traits\CanCheckType; use Intervention\Image\Traits\CanHandleInput; class AbstractDrawModifier { use CanHandleInput; + use CanCheckType; public function __construct( protected PointInterface $position, diff --git a/src/Drivers/Abstract/Modifiers/AbstractPadModifier.php b/src/Drivers/Abstract/Modifiers/AbstractPadModifier.php index 521dab30..0ae8e725 100644 --- a/src/Drivers/Abstract/Modifiers/AbstractPadModifier.php +++ b/src/Drivers/Abstract/Modifiers/AbstractPadModifier.php @@ -5,9 +5,12 @@ namespace Intervention\Image\Drivers\Abstract\Modifiers; use Intervention\Image\Geometry\Rectangle; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\SizeInterface; +use Intervention\Image\Traits\CanCheckType; abstract class AbstractPadModifier { + use CanCheckType; + public function __construct( protected int $width, protected int $height, diff --git a/src/Drivers/Abstract/Modifiers/AbstractRotateModifier.php b/src/Drivers/Abstract/Modifiers/AbstractRotateModifier.php index 016211e3..113a4ba1 100644 --- a/src/Drivers/Abstract/Modifiers/AbstractRotateModifier.php +++ b/src/Drivers/Abstract/Modifiers/AbstractRotateModifier.php @@ -5,11 +5,13 @@ namespace Intervention\Image\Drivers\Abstract\Modifiers; use Intervention\Image\Exceptions\DecoderException; use Intervention\Image\Exceptions\TypeException; use Intervention\Image\Interfaces\ColorInterface; +use Intervention\Image\Traits\CanCheckType; use Intervention\Image\Traits\CanHandleInput; abstract class AbstractRotateModifier { use CanHandleInput; + use CanCheckType; public function __construct(protected float $angle, protected $background) { diff --git a/src/Drivers/Gd/Modifiers/TextWriter.php b/src/Drivers/Gd/Modifiers/TextWriter.php index 8a404b44..fd117bca 100644 --- a/src/Drivers/Gd/Modifiers/TextWriter.php +++ b/src/Drivers/Gd/Modifiers/TextWriter.php @@ -4,8 +4,6 @@ namespace Intervention\Image\Drivers\Gd\Modifiers; use Intervention\Image\Drivers\Abstract\AbstractTextWriter; use Intervention\Image\Drivers\Gd\Font; -use Intervention\Image\Exceptions\FontException; -use Intervention\Image\Interfaces\FontInterface; use Intervention\Image\Interfaces\ImageInterface; class TextWriter extends AbstractTextWriter @@ -13,30 +11,27 @@ class TextWriter extends AbstractTextWriter public function apply(ImageInterface $image): ImageInterface { $lines = $this->getAlignedTextBlock(); + $font = $this->failIfNotClass($this->getFont(), Font::class); + foreach ($image as $frame) { if ($this->font->hasFilename()) { foreach ($lines as $line) { imagettftext( $frame->getCore(), - $this->getFont()->getSize(), - $this->getFont()->getAngle() * (-1), + $font->getSize(), + $font->getAngle() * (-1), $line->getPosition()->getX(), $line->getPosition()->getY(), - $this->getFont()->getColor()->toInt(), - $this->getFont()->getFilename(), + $font->getColor()->toInt(), + $font->getFilename(), $line ); } - - // debug - // $lines = new TextBlock($this->text); - // $box = $lines->getBoundingBox($this->font, $this->position); - // imagepolygon($frame->getCore(), $box->toArray(), 0); } else { foreach ($lines as $line) { imagestring( $frame->getCore(), - $this->getFont()->getGdFont(), + $font->getGdFont(), $line->getPosition()->getX(), $line->getPosition()->getY(), $line, @@ -48,12 +43,4 @@ class TextWriter extends AbstractTextWriter return $image; } - - protected function getFont(): FontInterface - { - if (!is_a($this->font, Font::class)) { - throw new FontException('Font is not compatible to current driver.'); - } - return $this->font; - } } diff --git a/src/Drivers/Imagick/Font.php b/src/Drivers/Imagick/Font.php index 8e2394af..2e8441bf 100644 --- a/src/Drivers/Imagick/Font.php +++ b/src/Drivers/Imagick/Font.php @@ -8,7 +8,6 @@ use Intervention\Image\Drivers\Abstract\AbstractFont; use Intervention\Image\Exceptions\FontException; use Intervention\Image\Geometry\Polygon; use Intervention\Image\Geometry\Rectangle; -use Intervention\Image\Interfaces\ColorInterface; class Font extends AbstractFont { @@ -18,28 +17,19 @@ class Font extends AbstractFont throw new FontException('No font file specified.'); } + $color = $this->failIfNotClass($this->getColor(), Color::class); + $draw = new ImagickDraw(); $draw->setStrokeAntialias(true); $draw->setTextAntialias(true); $draw->setFont($this->getFilename()); $draw->setFontSize($this->getSize()); - $draw->setFillColor($this->getColor()->getPixel()); + $draw->setFillColor($color->getPixel()); $draw->setTextAlignment(Imagick::ALIGN_LEFT); return $draw; } - public function getColor(): ?ColorInterface - { - $color = parent::getColor(); - - if (!is_a($color, Color::class)) { - throw new FontException('Font is not compatible to current driver.'); - } - - return $color; - } - /** * Calculate box size of current font * diff --git a/src/Drivers/Imagick/Modifiers/DrawEllipseModifier.php b/src/Drivers/Imagick/Modifiers/DrawEllipseModifier.php index b0ef639d..289500c2 100644 --- a/src/Drivers/Imagick/Modifiers/DrawEllipseModifier.php +++ b/src/Drivers/Imagick/Modifiers/DrawEllipseModifier.php @@ -5,8 +5,6 @@ namespace Intervention\Image\Drivers\Imagick\Modifiers; use ImagickDraw; use Intervention\Image\Drivers\Abstract\Modifiers\AbstractDrawModifier; use Intervention\Image\Drivers\Imagick\Color; -use Intervention\Image\Exceptions\DecoderException; -use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; @@ -14,13 +12,16 @@ class DrawEllipseModifier extends AbstractDrawModifier implements ModifierInterf { public function apply(ImageInterface $image): ImageInterface { - return $image->eachFrame(function ($frame) { + $background_color = $this->failIfNotClass($this->getBackgroundColor(), Color::class); + $border_color = $this->failIfNotClass($this->getBorderColor(), Color::class); + + return $image->eachFrame(function ($frame) use ($background_color, $border_color) { $drawing = new ImagickDraw(); - $drawing->setFillColor($this->getBackgroundColor()->getPixel()); + $drawing->setFillColor($background_color->getPixel()); if ($this->ellipse()->hasBorder()) { $drawing->setStrokeWidth($this->ellipse()->getBorderSize()); - $drawing->setStrokeColor($this->getBorderColor()->getPixel()); + $drawing->setStrokeColor($border_color->getPixel()); } $drawing->ellipse( @@ -35,24 +36,4 @@ class DrawEllipseModifier extends AbstractDrawModifier implements ModifierInterf $frame->getCore()->drawImage($drawing); }); } - - protected function getBackgroundColor(): ColorInterface - { - $color = parent::getBackgroundColor(); - if (!is_a($color, Color::class)) { - throw new DecoderException('Unable to decode background color.'); - } - - return $color; - } - - protected function getBorderColor(): ColorInterface - { - $color = parent::getBorderColor(); - if (!is_a($color, Color::class)) { - throw new DecoderException('Unable to decode border color.'); - } - - return $color; - } } diff --git a/src/Drivers/Imagick/Modifiers/DrawLineModifier.php b/src/Drivers/Imagick/Modifiers/DrawLineModifier.php index 0cd6fcd8..17c4ac76 100644 --- a/src/Drivers/Imagick/Modifiers/DrawLineModifier.php +++ b/src/Drivers/Imagick/Modifiers/DrawLineModifier.php @@ -4,6 +4,7 @@ namespace Intervention\Image\Drivers\Imagick\Modifiers; use ImagickDraw; use Intervention\Image\Drivers\Abstract\Modifiers\AbstractDrawModifier; +use Intervention\Image\Drivers\Imagick\Color; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; @@ -12,7 +13,8 @@ class DrawLineModifier extends AbstractDrawModifier implements ModifierInterface public function apply(ImageInterface $image): ImageInterface { $drawing = new ImagickDraw(); - $drawing->setStrokeColor($this->getBackgroundColor()->getPixel()); + $color = $this->failIfNotClass($this->getBackgroundColor(), Color::class); + $drawing->setStrokeColor($color->getPixel()); $drawing->setStrokeWidth($this->line()->getWidth()); $drawing->line( $this->line()->getStart()->getX(), diff --git a/src/Drivers/Imagick/Modifiers/DrawPixelModifier.php b/src/Drivers/Imagick/Modifiers/DrawPixelModifier.php index d975f93f..20888991 100644 --- a/src/Drivers/Imagick/Modifiers/DrawPixelModifier.php +++ b/src/Drivers/Imagick/Modifiers/DrawPixelModifier.php @@ -4,15 +4,16 @@ namespace Intervention\Image\Drivers\Imagick\Modifiers; use ImagickDraw; use Intervention\Image\Drivers\Imagick\Color; -use Intervention\Image\Exceptions\TypeException; use Intervention\Image\Geometry\Point; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; +use Intervention\Image\Traits\CanCheckType; use Intervention\Image\Traits\CanHandleInput; class DrawPixelModifier implements ModifierInterface { use CanHandleInput; + use CanCheckType; public function __construct(protected Point $position, protected mixed $color) { @@ -21,7 +22,11 @@ class DrawPixelModifier implements ModifierInterface public function apply(ImageInterface $image): ImageInterface { - $color = $this->decodeColor(); + $color = $this->failIfNotClass( + $this->handleInput($this->color), + Color::class, + ); + $pixel = new ImagickDraw(); $pixel->setFillColor($color->getPixel()); $pixel->point($this->position->getX(), $this->position->getY()); @@ -30,15 +35,4 @@ class DrawPixelModifier implements ModifierInterface $frame->getCore()->drawImage($pixel); }); } - - private function decodeColor(): Color - { - $color = $this->handleInput($this->color); - - if (!is_a($color, Color::class)) { - throw new TypeException('Color is not compatible to current driver.'); - } - - return $color; - } } diff --git a/src/Drivers/Imagick/Modifiers/DrawPolygonModifier.php b/src/Drivers/Imagick/Modifiers/DrawPolygonModifier.php index 8c39081b..ea8b511c 100644 --- a/src/Drivers/Imagick/Modifiers/DrawPolygonModifier.php +++ b/src/Drivers/Imagick/Modifiers/DrawPolygonModifier.php @@ -5,7 +5,6 @@ namespace Intervention\Image\Drivers\Imagick\Modifiers; use ImagickDraw; use Intervention\Image\Drivers\Abstract\Modifiers\AbstractDrawModifier; use Intervention\Image\Drivers\Imagick\Color; -use Intervention\Image\Exceptions\TypeException; use Intervention\Image\Interfaces\DrawableInterface; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; @@ -21,12 +20,15 @@ class DrawPolygonModifier extends AbstractDrawModifier implements ModifierInterf public function apply(ImageInterface $image): ImageInterface { $drawing = new ImagickDraw(); + $background_color = $this->failIfNotClass($this->getBackgroundColor(), Color::class); + $border_color = $this->failIfNotClass($this->getBorderColor(), Color::class); + if ($this->polygon()->hasBackgroundColor()) { - $drawing->setFillColor($this->getBackgroundColor()->getPixel()); + $drawing->setFillColor($background_color->getPixel()); } if ($this->polygon()->hasBorder()) { - $drawing->setStrokeColor($this->getBorderColor()->getPixel()); + $drawing->setStrokeColor($border_color->getPixel()); $drawing->setStrokeWidth($this->polygon()->getBorderSize()); } @@ -46,26 +48,4 @@ class DrawPolygonModifier extends AbstractDrawModifier implements ModifierInterf return $points; } - - protected function getBackgroundColor(): ?Color - { - $color = parent::getBackgroundColor(); - - if (!is_a($color, Color::class)) { - throw new TypeException('Color is not compatible to current driver.'); - } - - return $color; - } - - protected function getBorderColor(): ?Color - { - $color = parent::getBorderColor(); - - if (!is_a($color, Color::class)) { - throw new TypeException('Color is not compatible to current driver.'); - } - - return $color; - } } diff --git a/src/Drivers/Imagick/Modifiers/DrawRectangleModifier.php b/src/Drivers/Imagick/Modifiers/DrawRectangleModifier.php index 8993de8a..2baf9721 100644 --- a/src/Drivers/Imagick/Modifiers/DrawRectangleModifier.php +++ b/src/Drivers/Imagick/Modifiers/DrawRectangleModifier.php @@ -5,7 +5,6 @@ namespace Intervention\Image\Drivers\Imagick\Modifiers; use ImagickDraw; use Intervention\Image\Drivers\Abstract\Modifiers\AbstractDrawModifier; use Intervention\Image\Drivers\Imagick\Color; -use Intervention\Image\Exceptions\DecoderException; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; @@ -13,11 +12,14 @@ class DrawRectangleModifier extends AbstractDrawModifier implements ModifierInte { public function apply(ImageInterface $image): ImageInterface { - // setup rectangle + // setup $drawing = new ImagickDraw(); - $drawing->setFillColor($this->getBackgroundColor()->getPixel()); + $background_color = $this->failIfNotClass($this->getBackgroundColor(), Color::class); + $border_color = $this->failIfNotClass($this->getBorderColor(), Color::class); + + $drawing->setFillColor($background_color->getPixel()); if ($this->rectangle()->hasBorder()) { - $drawing->setStrokeColor($this->getBorderColor()->getPixel()); + $drawing->setStrokeColor($border_color->getPixel()); $drawing->setStrokeWidth($this->rectangle()->getBorderSize()); } @@ -35,24 +37,4 @@ class DrawRectangleModifier extends AbstractDrawModifier implements ModifierInte return $image; } - - protected function getBackgroundColor(): Color - { - $color = parent::getBackgroundColor(); - if (!is_a($color, Color::class)) { - throw new DecoderException('Unable to decode background color.'); - } - - return $color; - } - - protected function getBorderColor(): Color - { - $color = parent::getBorderColor(); - if (!is_a($color, Color::class)) { - throw new DecoderException('Unable to decode border color.'); - } - - return $color; - } } diff --git a/src/Drivers/Imagick/Modifiers/PadModifier.php b/src/Drivers/Imagick/Modifiers/PadModifier.php index 94d44a65..3b0d38e2 100644 --- a/src/Drivers/Imagick/Modifiers/PadModifier.php +++ b/src/Drivers/Imagick/Modifiers/PadModifier.php @@ -6,7 +6,6 @@ use Imagick; use ImagickDraw; use Intervention\Image\Drivers\Abstract\Modifiers\AbstractPadModifier; use Intervention\Image\Drivers\Imagick\Color; -use Intervention\Image\Exceptions\DecoderException; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; use Intervention\Image\Interfaces\SizeInterface; @@ -22,11 +21,7 @@ class PadModifier extends AbstractPadModifier implements ModifierInterface { $resize = $this->getResizeSize($image); $crop = $this->getCropSize($image); - $background = $this->handleInput($this->background); - - if (!is_a($background, Color::class)) { - throw new DecoderException('Unable to decode backgroud color.'); - } + $background = $this->failIfNotClass($this->handleInput($this->background), Color::class); foreach ($image as $frame) { // resize current core diff --git a/src/Drivers/Imagick/Modifiers/RotateModifier.php b/src/Drivers/Imagick/Modifiers/RotateModifier.php index 304585e7..ff908c04 100644 --- a/src/Drivers/Imagick/Modifiers/RotateModifier.php +++ b/src/Drivers/Imagick/Modifiers/RotateModifier.php @@ -4,7 +4,6 @@ namespace Intervention\Image\Drivers\Imagick\Modifiers; use Intervention\Image\Drivers\Abstract\Modifiers\AbstractRotateModifier; use Intervention\Image\Drivers\Imagick\Color; -use Intervention\Image\Exceptions\DecoderException; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; @@ -12,10 +11,7 @@ class RotateModifier extends AbstractRotateModifier implements ModifierInterface { public function apply(ImageInterface $image): ImageInterface { - $background = $this->backgroundColor(); - if (!is_a($background, Color::class)) { - throw new DecoderException('Unable to decode given background color.'); - } + $background = $this->failIfNotClass($this->backgroundColor(), Color::class); foreach ($image as $frame) { $frame->getCore()->rotateImage( diff --git a/src/Drivers/Imagick/Modifiers/TextWriter.php b/src/Drivers/Imagick/Modifiers/TextWriter.php index 90469983..4bf3af51 100644 --- a/src/Drivers/Imagick/Modifiers/TextWriter.php +++ b/src/Drivers/Imagick/Modifiers/TextWriter.php @@ -4,7 +4,6 @@ namespace Intervention\Image\Drivers\Imagick\Modifiers; use Intervention\Image\Drivers\Abstract\AbstractTextWriter; use Intervention\Image\Drivers\Imagick\Font; -use Intervention\Image\Exceptions\FontException; use Intervention\Image\Interfaces\ImageInterface; class TextWriter extends AbstractTextWriter @@ -12,13 +11,15 @@ class TextWriter extends AbstractTextWriter public function apply(ImageInterface $image): ImageInterface { $lines = $this->getAlignedTextBlock(); + $font = $this->failIfNotClass($this->getFont(), Font::class); + foreach ($image as $frame) { foreach ($lines as $line) { $frame->getCore()->annotateImage( - $this->getFont()->toImagickDraw(), + $font->toImagickDraw(), $line->getPosition()->getX(), $line->getPosition()->getY(), - $this->getFont()->getAngle(), + $font->getAngle(), $line ); } @@ -26,12 +27,4 @@ class TextWriter extends AbstractTextWriter return $image; } - - protected function getFont(): Font - { - if (!is_a($this->font, Font::class)) { - throw new FontException('Font is not compatible to current driver.'); - } - return $this->font; - } } diff --git a/src/Traits/CanCheckType.php b/src/Traits/CanCheckType.php new file mode 100644 index 00000000..f70e85bd --- /dev/null +++ b/src/Traits/CanCheckType.php @@ -0,0 +1,17 @@ +resolveDriverClass('InputHandler')->handle($input); + $result = $this->resolveDriverClass('InputHandler')->handle($input); + + if (!is_null($check_result_against_classname) && get_class($result) != $check_result_against_classname) { + throw new DecoderException('Decoded result is not an instance of ' . $check_result_against_classname); + } + + return $result; } } diff --git a/tests/Drivers/Abstract/Modifiers/AbstractRotateModifierTest.php b/tests/Drivers/Abstract/Modifiers/AbstractRotateModifierTest.php index 42de945b..554e95f9 100644 --- a/tests/Drivers/Abstract/Modifiers/AbstractRotateModifierTest.php +++ b/tests/Drivers/Abstract/Modifiers/AbstractRotateModifierTest.php @@ -64,7 +64,7 @@ final class AbstractRotateModifierTest extends TestCase return parent::backgroundColor(); } - public function handleInput($input): ImageInterface|ColorInterface + public function handleInput($input, ?string $check_result_against_classname = null): ImageInterface|ColorInterface { if ($this->background === 'bad value') { throw new DecoderException(); From 20ff41b60619706b9e619f4b8753f754c7b698c0 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 30 Sep 2023 11:48:42 +0200 Subject: [PATCH 244/476] Add static analyzer dev environment --- docker-compose.yml | 6 ++++++ readme.md | 14 ++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/docker-compose.yml b/docker-compose.yml index 75a62748..8d90fbff 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -7,3 +7,9 @@ services: command: bash -c "composer install && ./vendor/bin/phpunit -vvv" volumes: - ./:/project + analysis: + build: ./ + working_dir: /project + command: bash -c "composer install && ./vendor/bin/phpstan analyze --level=4 ./src" + volumes: + - ./:/project diff --git a/readme.md b/readme.md index 0cd1f4d5..059f3f62 100644 --- a/readme.md +++ b/readme.md @@ -54,6 +54,20 @@ composer require intervention/image Learn the [basics](https://image.intervention.io/v3/basics/instantiation/) on how to use Intervention Image and more with the [official documentation](https://image.intervention.io/v3/). +## Development & Testing + +With this package comes a Docker image to build a test suite and analysis container. To build this container you have to have Docker installed on your system. You can run all tests with this command. + +```bash +docker-compose run --rm --build tests +``` + +Run the static analyzer on the code base. + +```bash +docker-compose run --rm --build analysis +``` + ## License Intervention Image is licensed under the [MIT License](http://opensource.org/licenses/MIT). From baf1652e0b518b04fab75bf61b04ab62251e065f Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 30 Sep 2023 11:50:30 +0200 Subject: [PATCH 245/476] Add analyzer to Github workflow --- .github/workflows/run-tests.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 032093c1..93a42fa8 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -94,3 +94,6 @@ jobs: - name: Execute tests run: vendor/bin/phpunit --no-coverage + + - name: Run analyzer + run: vendor/bin/phpstan analyze --level=4 ./src From 7577986980b9e80473e3e0f3d00e01a55e1087f5 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 30 Sep 2023 16:48:34 +0200 Subject: [PATCH 246/476] Add filter method to Collection --- src/Collection.php | 15 +++++++++++++++ tests/CollectionTest.php | 10 ++++++++++ 2 files changed, 25 insertions(+) diff --git a/src/Collection.php b/src/Collection.php index 5434f83b..76e35b13 100644 --- a/src/Collection.php +++ b/src/Collection.php @@ -134,6 +134,21 @@ class Collection implements CollectionInterface, IteratorAggregate, Countable return new self($items); } + /** + * Run callback on each item of the collection an remove it if it does not return true + * + * @param callable $callback + * @return Collection + */ + public function filter(callable $callback): self + { + $items = array_filter($this->items, function ($item) use ($callback) { + return $callback($item); + }); + + return new self($items); + } + public function pushEach(array $data, ?callable $callback = null): CollectionInterface { if (! is_iterable($data)) { diff --git a/tests/CollectionTest.php b/tests/CollectionTest.php index 9703486e..67488a62 100644 --- a/tests/CollectionTest.php +++ b/tests/CollectionTest.php @@ -45,6 +45,16 @@ class CollectionTest extends TestCase $this->assertEquals(3, count($collection)); } + public function testFilter() + { + $collection = new Collection(['foo', 'bar', 'baz']); + $this->assertEquals(3, $collection->count()); + $collection = $collection->filter(function ($text) { + return substr($text, 0, 1) == 'b'; + }); + $this->assertEquals(2, $collection->count()); + } + public function testFirstLast() { $collection = new Collection(['foo', 'bar', 'baz']); From 635567e9c20a73ccb7fc07d1efe7db63c53d5ef9 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 30 Sep 2023 17:00:22 +0200 Subject: [PATCH 247/476] Add remove animation modifiers --- src/Drivers/Abstract/AbstractImage.php | 7 ++++ .../Gd/Modifiers/RemoveAnimationModifier.php | 35 +++++++++++++++++++ src/Drivers/Imagick/Image.php | 10 +++--- .../Modifiers/RemoveAnimationModifier.php | 35 +++++++++++++++++++ src/Interfaces/ImageInterface.php | 7 ++++ .../Modifiers/RemoveAnimationModifierTest.php | 24 +++++++++++++ .../Modifiers/RemoveAnimationModifierTest.php | 24 +++++++++++++ 7 files changed, 137 insertions(+), 5 deletions(-) create mode 100644 src/Drivers/Gd/Modifiers/RemoveAnimationModifier.php create mode 100644 src/Drivers/Imagick/Modifiers/RemoveAnimationModifier.php create mode 100644 tests/Drivers/Gd/Modifiers/RemoveAnimationModifierTest.php create mode 100644 tests/Drivers/Imagick/Modifiers/RemoveAnimationModifierTest.php diff --git a/src/Drivers/Abstract/AbstractImage.php b/src/Drivers/Abstract/AbstractImage.php index 25a6cf35..7fbe5b4a 100644 --- a/src/Drivers/Abstract/AbstractImage.php +++ b/src/Drivers/Abstract/AbstractImage.php @@ -334,6 +334,13 @@ abstract class AbstractImage implements ImageInterface ); } + public function removeAnimation(int $position = 0): ImageInterface + { + return $this->modify( + $this->resolveDriverClass('Modifiers\RemoveAnimationModifier', $position) + ); + } + public function destroy(): void { $this->modify( diff --git a/src/Drivers/Gd/Modifiers/RemoveAnimationModifier.php b/src/Drivers/Gd/Modifiers/RemoveAnimationModifier.php new file mode 100644 index 00000000..7802f08b --- /dev/null +++ b/src/Drivers/Gd/Modifiers/RemoveAnimationModifier.php @@ -0,0 +1,35 @@ +isAnimated()) { + throw new RuntimeException('Image is not animated.'); + } + + $frames = new Collection(); + foreach ($image as $key => $frame) { + if ($this->position == $key) { + $frames->push($frame); + } else { + imagedestroy($frame->getCore()); + } + } + + return new Image($frames); + } +} diff --git a/src/Drivers/Imagick/Image.php b/src/Drivers/Imagick/Image.php index e5cf937f..03f2743a 100644 --- a/src/Drivers/Imagick/Image.php +++ b/src/Drivers/Imagick/Image.php @@ -26,13 +26,13 @@ class Image extends AbstractImage implements ImageInterface, Iterator public function getFrame(int $position = 0): ?FrameInterface { - try { - $this->imagick->setIteratorIndex($position); - } catch (ImagickException $e) { - return null; + foreach ($this->imagick as $core) { + if ($core->getIteratorIndex() == $position) { + return new Frame($core); + } } - return new Frame($this->imagick->current()); + return null; } public function addFrame(FrameInterface $frame): ImageInterface diff --git a/src/Drivers/Imagick/Modifiers/RemoveAnimationModifier.php b/src/Drivers/Imagick/Modifiers/RemoveAnimationModifier.php new file mode 100644 index 00000000..60a21f1a --- /dev/null +++ b/src/Drivers/Imagick/Modifiers/RemoveAnimationModifier.php @@ -0,0 +1,35 @@ +isAnimated()) { + throw new RuntimeException('Image is not animated.'); + } + + $imagick = new Imagick(); + foreach ($image as $frame) { + if ($frame->getCore()->getIteratorIndex() == $this->position) { + $imagick->addImage($frame->getCore()->getImage()); + } + } + + $image->destroy(); + + return new Image($imagick); + } +} diff --git a/src/Interfaces/ImageInterface.php b/src/Interfaces/ImageInterface.php index 4b04f761..aac0d79b 100644 --- a/src/Interfaces/ImageInterface.php +++ b/src/Interfaces/ImageInterface.php @@ -62,6 +62,13 @@ interface ImageInterface extends Traversable, Countable */ public function isAnimated(): bool; + /** + * Remove all frames but keep the one at the specified position + * + * @param int $position + * @return ImageInterface + */ + public function removeAnimation(int $position = 0): ImageInterface; /** * Apply given modifier to current image diff --git a/tests/Drivers/Gd/Modifiers/RemoveAnimationModifierTest.php b/tests/Drivers/Gd/Modifiers/RemoveAnimationModifierTest.php new file mode 100644 index 00000000..a48473ab --- /dev/null +++ b/tests/Drivers/Gd/Modifiers/RemoveAnimationModifierTest.php @@ -0,0 +1,24 @@ +createTestImage('animation.gif'); + $this->assertEquals(8, count($image)); + $image = $image->modify(new RemoveAnimationModifier(2)); + $this->assertEquals(1, count($image)); + } +} diff --git a/tests/Drivers/Imagick/Modifiers/RemoveAnimationModifierTest.php b/tests/Drivers/Imagick/Modifiers/RemoveAnimationModifierTest.php new file mode 100644 index 00000000..8f0591a4 --- /dev/null +++ b/tests/Drivers/Imagick/Modifiers/RemoveAnimationModifierTest.php @@ -0,0 +1,24 @@ +createTestImage('animation.gif'); + $this->assertEquals(8, count($image)); + $image = $image->modify(new RemoveAnimationModifier(2)); + $this->assertEquals(1, count($image)); + } +} From 764c6f3843e4a66b1e2e259a7fb2d43de65e47a4 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 1 Oct 2023 09:59:05 +0200 Subject: [PATCH 248/476] Rename ImageManager::make to ImageManager::read --- readme.md | 2 +- src/ImageManager.php | 2 +- tests/ImageManagerTest.php | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/readme.md b/readme.md index 059f3f62..7e3fbe72 100644 --- a/readme.md +++ b/readme.md @@ -20,7 +20,7 @@ Intervention Image is a **image handling and manipulation library written in PHP $manager = new ImageManager('gd') // open an image file -$image = $manager->make('images/example.gif'); +$image = $manager->read('images/example.gif'); // resize image instance $image->resize(height: 300); diff --git a/src/ImageManager.php b/src/ImageManager.php index 5cd24990..fef13079 100644 --- a/src/ImageManager.php +++ b/src/ImageManager.php @@ -32,7 +32,7 @@ class ImageManager * @param mixed $source * @return ImageInterface */ - public function make($source): ImageInterface + public function read($source): ImageInterface { return $this->resolveDriverClass('InputHandler')->handle($source); } diff --git a/tests/ImageManagerTest.php b/tests/ImageManagerTest.php index b95dc058..1bbbe645 100644 --- a/tests/ImageManagerTest.php +++ b/tests/ImageManagerTest.php @@ -25,10 +25,10 @@ class ImageManagerTest extends TestCase } /** @requires extension gd */ - public function testMakeGd() + public function testReadGd() { $manager = new ImageManager('gd'); - $image = $manager->make(__DIR__ . '/images/red.gif'); + $image = $manager->read(__DIR__ . '/images/red.gif'); $this->assertInstanceOf(ImageInterface::class, $image); } @@ -41,10 +41,10 @@ class ImageManagerTest extends TestCase } /** @requires extension imagick */ - public function testMakeImagick() + public function testReadImagick() { $manager = new ImageManager('imagick'); - $image = $manager->make(__DIR__ . '/images/red.gif'); + $image = $manager->read(__DIR__ . '/images/red.gif'); $this->assertInstanceOf(ImageInterface::class, $image); } } From 2f2f80cd7703fef521fd74825f2a981453a2bdd2 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 1 Oct 2023 15:35:11 +0200 Subject: [PATCH 249/476] Remove redundant code --- tests/Traits/CanCreateImagickTestImage.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/Traits/CanCreateImagickTestImage.php b/tests/Traits/CanCreateImagickTestImage.php index c097c50e..efc63016 100644 --- a/tests/Traits/CanCreateImagickTestImage.php +++ b/tests/Traits/CanCreateImagickTestImage.php @@ -2,9 +2,7 @@ namespace Intervention\Image\Tests\Traits; -use Intervention\Image\Collection; use Intervention\Image\Drivers\Imagick\Decoders\FilePathImageDecoder; -use Intervention\Image\Drivers\Imagick\Frame; use Intervention\Image\Drivers\Imagick\Image; trait CanCreateImagickTestImage From 41efb893732cef109d3961283975435c384c01a3 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 1 Oct 2023 16:07:41 +0200 Subject: [PATCH 250/476] Add optimizeImageLayers call to GifEncoder --- src/Drivers/Imagick/Encoders/GifEncoder.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Drivers/Imagick/Encoders/GifEncoder.php b/src/Drivers/Imagick/Encoders/GifEncoder.php index d180dc11..10b61d59 100644 --- a/src/Drivers/Imagick/Encoders/GifEncoder.php +++ b/src/Drivers/Imagick/Encoders/GifEncoder.php @@ -26,6 +26,7 @@ class GifEncoder extends AbstractEncoder implements EncoderInterface $imagick->setImageFormat($format); $imagick->setCompression($compression); $imagick->setImageCompression($compression); + $imagick->optimizeImageLayers(); $imagick = $imagick->deconstructImages(); return new EncodedImage($imagick->getImagesBlob(), 'image/gif'); From b57717da3dc03ad286d53f1148c7f0319fb726d6 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Tue, 3 Oct 2023 10:08:37 +0200 Subject: [PATCH 251/476] Move methods to global parent TestCase --- tests/TestCase.php | 10 ++++++++++ tests/Traits/CanCreateGdTestImage.php | 10 ---------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/tests/TestCase.php b/tests/TestCase.php index 8985b4e8..a3d0b2ba 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -7,6 +7,16 @@ use Mockery\Adapter\Phpunit\MockeryTestCase; abstract class TestCase extends MockeryTestCase { + public function getTestImagePath($filename = 'test.jpg'): string + { + return sprintf('%s/images/%s', __DIR__, $filename); + } + + public function getTestImageData($filename = 'test.jpg'): string + { + return file_get_contents($this->getTestImagePath($filename)); + } + protected function assertColor($r, $g, $b, $a, ColorInterface $color) { $this->assertEquals($r, $color->red()); diff --git a/tests/Traits/CanCreateGdTestImage.php b/tests/Traits/CanCreateGdTestImage.php index 9d12ed2b..56921887 100644 --- a/tests/Traits/CanCreateGdTestImage.php +++ b/tests/Traits/CanCreateGdTestImage.php @@ -9,16 +9,6 @@ use Intervention\Image\Drivers\Gd\Image; trait CanCreateGdTestImage { - public function getTestImagePath($filename = 'test.jpg'): string - { - return sprintf('%s/../images/%s', __DIR__, $filename); - } - - public function getTestImageData($filename = 'test.jpg'): string - { - return file_get_contents($this->getTestImagePath($filename)); - } - public function createTestImage($filename = 'test.jpg'): Image { return $this->createWithImageDecoder()->handle( From bc69b936c5513e1ed4968dfa05813883519b5e5a Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Tue, 3 Oct 2023 10:11:42 +0200 Subject: [PATCH 252/476] Remove type check --- src/Traits/CanHandleInput.php | 11 ++--------- .../Abstract/Modifiers/AbstractRotateModifierTest.php | 2 +- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/src/Traits/CanHandleInput.php b/src/Traits/CanHandleInput.php index 9ff2f75b..42edc18a 100644 --- a/src/Traits/CanHandleInput.php +++ b/src/Traits/CanHandleInput.php @@ -2,7 +2,6 @@ namespace Intervention\Image\Traits; -use Intervention\Image\Exceptions\DecoderException; use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\ImageInterface; @@ -10,14 +9,8 @@ trait CanHandleInput { use CanResolveDriverClass; - public function handleInput($input, ?string $check_result_against_classname = null): ImageInterface|ColorInterface + public function handleInput($input): ImageInterface|ColorInterface { - $result = $this->resolveDriverClass('InputHandler')->handle($input); - - if (!is_null($check_result_against_classname) && get_class($result) != $check_result_against_classname) { - throw new DecoderException('Decoded result is not an instance of ' . $check_result_against_classname); - } - - return $result; + return $this->resolveDriverClass('InputHandler')->handle($input); } } diff --git a/tests/Drivers/Abstract/Modifiers/AbstractRotateModifierTest.php b/tests/Drivers/Abstract/Modifiers/AbstractRotateModifierTest.php index 554e95f9..42de945b 100644 --- a/tests/Drivers/Abstract/Modifiers/AbstractRotateModifierTest.php +++ b/tests/Drivers/Abstract/Modifiers/AbstractRotateModifierTest.php @@ -64,7 +64,7 @@ final class AbstractRotateModifierTest extends TestCase return parent::backgroundColor(); } - public function handleInput($input, ?string $check_result_against_classname = null): ImageInterface|ColorInterface + public function handleInput($input): ImageInterface|ColorInterface { if ($this->background === 'bad value') { throw new DecoderException(); From 8eb1394b9908aebee0d750abf4e6fcd6a3aaad75 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Tue, 3 Oct 2023 10:18:17 +0200 Subject: [PATCH 253/476] Add methods to create new animated images --- src/Drivers/Gd/ImageFactory.php | 33 +++++++++++++++++++ src/Drivers/Imagick/ImageFactory.php | 37 ++++++++++++++++++++++ src/ImageManager.php | 11 +++++++ tests/Drivers/Gd/ImageFactoryTest.php | 11 +++++++ tests/Drivers/Imagick/ImageFactoryTest.php | 11 +++++++ 5 files changed, 103 insertions(+) diff --git a/src/Drivers/Gd/ImageFactory.php b/src/Drivers/Gd/ImageFactory.php index 7dbc2f4e..1d92a368 100644 --- a/src/Drivers/Gd/ImageFactory.php +++ b/src/Drivers/Gd/ImageFactory.php @@ -3,11 +3,16 @@ namespace Intervention\Image\Drivers\Gd; use Intervention\Image\Collection; +use Intervention\Image\Drivers\Gd\Frame; +use Intervention\Image\Drivers\Gd\Image; use Intervention\Image\Interfaces\FactoryInterface; use Intervention\Image\Interfaces\ImageInterface; +use Intervention\Image\Traits\CanHandleInput; class ImageFactory implements FactoryInterface { + use CanHandleInput; + public function newImage(int $width, int $height): ImageInterface { return new Image( @@ -17,6 +22,34 @@ class ImageFactory implements FactoryInterface ); } + public function newAnimation(callable $callback): ImageInterface + { + $frames = new Collection(); + + $animation = new class ($frames) extends ImageFactory + { + public function __construct(public Collection $frames) + { + // + } + + public function add($source, float $delay = 1): self + { + $this->frames->push( + $this->handleInput($source) + ->getFrame() + ->setDelay($delay) + ); + + return $this; + } + }; + + $callback($animation); + + return new Image($frames); + } + public function newCore(int $width, int $height) { $core = imagecreatetruecolor($width, $height); diff --git a/src/Drivers/Imagick/ImageFactory.php b/src/Drivers/Imagick/ImageFactory.php index cc54048b..773f1a53 100644 --- a/src/Drivers/Imagick/ImageFactory.php +++ b/src/Drivers/Imagick/ImageFactory.php @@ -4,16 +4,53 @@ namespace Intervention\Image\Drivers\Imagick; use Imagick; use ImagickPixel; +use Intervention\Image\Drivers\Imagick\Image; use Intervention\Image\Interfaces\FactoryInterface; use Intervention\Image\Interfaces\ImageInterface; +use Intervention\Image\Traits\CanCheckType; +use Intervention\Image\Traits\CanHandleInput; class ImageFactory implements FactoryInterface { + use CanHandleInput; + use CanCheckType; + public function newImage(int $width, int $height): ImageInterface { return new Image($this->newCore($width, $height)); } + public function newAnimation(callable $callback): ImageInterface + { + $imagick = new Imagick(); + $imagick->setFormat('gif'); + + $animation = new class ($imagick) extends ImageFactory + { + public function __construct(public Imagick $imagick) + { + // + } + + public function add($source, float $delay = 1): self + { + $imagick = $this->failIfNotClass( + $this->handleInput($source), + Image::class, + )->getImagick(); + $imagick->setImageDelay($delay * 100); + + $this->imagick->addImage($imagick); + + return $this; + } + }; + + $callback($animation); + + return new Image($animation->imagick); + } + public function newCore(int $width, int $height) { $imagick = new Imagick(); diff --git a/src/ImageManager.php b/src/ImageManager.php index fef13079..1703cf84 100644 --- a/src/ImageManager.php +++ b/src/ImageManager.php @@ -26,6 +26,17 @@ class ImageManager return $this->resolveDriverClass('ImageFactory')->newImage($width, $height); } + /** + * Create new animated image from sources + * + * @param callable $callback + * @return ImageInterface + */ + public function animate(callable $callback): ImageInterface + { + return $this->resolveDriverClass('ImageFactory')->newAnimation($callback); + } + /** * Create new image instance from source * diff --git a/tests/Drivers/Gd/ImageFactoryTest.php b/tests/Drivers/Gd/ImageFactoryTest.php index 32cf83d4..38060e45 100644 --- a/tests/Drivers/Gd/ImageFactoryTest.php +++ b/tests/Drivers/Gd/ImageFactoryTest.php @@ -20,6 +20,17 @@ class ImageFactoryTest extends TestCase $this->assertInstanceOf(Image::class, $image); } + public function testNewAnimation(): void + { + $factory = new ImageFactory(); + $image = $factory->newAnimation(function ($animation) { + $animation->add($this->getTestImagePath('blue.gif'), 1.2); + $animation->add($this->getTestImagePath('red.gif'), 1.2); + }); + $this->assertInstanceOf(Image::class, $image); + $this->assertEquals(2, $image->count()); + } + public function testNewCore(): void { $factory = new ImageFactory(); diff --git a/tests/Drivers/Imagick/ImageFactoryTest.php b/tests/Drivers/Imagick/ImageFactoryTest.php index 75e40e24..635139e5 100644 --- a/tests/Drivers/Imagick/ImageFactoryTest.php +++ b/tests/Drivers/Imagick/ImageFactoryTest.php @@ -20,6 +20,17 @@ class ImageFactoryTest extends TestCase $this->assertInstanceOf(Image::class, $image); } + public function testNewAnimation(): void + { + $factory = new ImageFactory(); + $image = $factory->newAnimation(function ($animation) { + $animation->add($this->getTestImagePath('blue.gif'), 1.2); + $animation->add($this->getTestImagePath('red.gif'), 1.2); + }); + $this->assertInstanceOf(Image::class, $image); + $this->assertEquals(2, $image->count()); + } + public function testNewCore(): void { $factory = new ImageFactory(); From af2ee6c11efef2d1e2143235c111a27fa938f687 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Tue, 3 Oct 2023 17:24:52 +0200 Subject: [PATCH 254/476] Add avif encoding --- Dockerfile | 3 +- composer.json | 2 +- src/Drivers/Abstract/AbstractImage.php | 7 ++++ src/Drivers/Gd/Encoders/AvifEncoder.php | 25 ++++++++++++++ src/Drivers/Imagick/Encoders/AvifEncoder.php | 26 ++++++++++++++ src/Interfaces/ImageInterface.php | 6 ++++ tests/Drivers/Gd/Encoders/AvifEncoderTest.php | 33 ++++++++++++++++++ .../Imagick/Encoders/AvifEncoderTest.php | 34 +++++++++++++++++++ 8 files changed, 134 insertions(+), 2 deletions(-) create mode 100644 src/Drivers/Gd/Encoders/AvifEncoder.php create mode 100644 src/Drivers/Imagick/Encoders/AvifEncoder.php create mode 100644 tests/Drivers/Gd/Encoders/AvifEncoderTest.php create mode 100644 tests/Drivers/Imagick/Encoders/AvifEncoderTest.php diff --git a/Dockerfile b/Dockerfile index f3471b6c..b17cf7c3 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,6 +4,7 @@ RUN apt update \ && apt install -y \ libpng-dev \ libicu-dev \ + libavif-dev \ libpq-dev \ libzip-dev \ zip \ @@ -13,7 +14,7 @@ RUN apt update \ libmagickwand-dev \ libwebp-dev \ && pecl install imagick \ - && docker-php-ext-configure gd --with-freetype --with-jpeg --with-webp \ + && docker-php-ext-configure gd --with-freetype --with-jpeg --with-webp --with-avif \ && docker-php-ext-enable imagick \ && docker-php-ext-install \ intl \ diff --git a/composer.json b/composer.json index 909da5f2..b2591f2e 100644 --- a/composer.json +++ b/composer.json @@ -14,7 +14,7 @@ "require": { "php": "^8.1", "intervention/gif": "^3.0", - "intervention/mimesniffer": "^0.4.2" + "intervention/mimesniffer": "^0.5" }, "require-dev": { "phpunit/phpunit": "^9", diff --git a/src/Drivers/Abstract/AbstractImage.php b/src/Drivers/Abstract/AbstractImage.php index 7fbe5b4a..3b70d93a 100644 --- a/src/Drivers/Abstract/AbstractImage.php +++ b/src/Drivers/Abstract/AbstractImage.php @@ -94,6 +94,13 @@ abstract class AbstractImage implements ImageInterface return $this->toBitmap(); } + public function toAvif(): EncodedImage + { + return $this->encode( + $this->resolveDriverClass('Encoders\AvifEncoder') + ); + } + public function greyscale(): ImageInterface { return $this->modify( diff --git a/src/Drivers/Gd/Encoders/AvifEncoder.php b/src/Drivers/Gd/Encoders/AvifEncoder.php new file mode 100644 index 00000000..aaa39ea5 --- /dev/null +++ b/src/Drivers/Gd/Encoders/AvifEncoder.php @@ -0,0 +1,25 @@ +quality = $quality; + } + + public function encode(ImageInterface $image): EncodedImage + { + $data = $this->getBuffered(function () use ($image) { + imageavif($image->getFrame()->getCore(), null, $this->quality); + }); + + return new EncodedImage($data, 'image/avif'); + } +} diff --git a/src/Drivers/Imagick/Encoders/AvifEncoder.php b/src/Drivers/Imagick/Encoders/AvifEncoder.php new file mode 100644 index 00000000..f1aef717 --- /dev/null +++ b/src/Drivers/Imagick/Encoders/AvifEncoder.php @@ -0,0 +1,26 @@ +getFrame()->getCore(); + $imagick->setFormat($format); + $imagick->setImageFormat($format); + $imagick->setCompression($compression); + $imagick->setImageCompression($compression); + + return new EncodedImage($imagick->getImagesBlob(), 'image/avif'); + } +} diff --git a/src/Interfaces/ImageInterface.php b/src/Interfaces/ImageInterface.php index aac0d79b..34b42f12 100644 --- a/src/Interfaces/ImageInterface.php +++ b/src/Interfaces/ImageInterface.php @@ -109,6 +109,12 @@ interface ImageInterface extends Traversable, Countable */ public function toGif(): EncodedImage; + /** + * Encode image to avif format + * + * @return EncodedImage + */ + public function toAvif(): EncodedImage; /** * Encode image to png format diff --git a/tests/Drivers/Gd/Encoders/AvifEncoderTest.php b/tests/Drivers/Gd/Encoders/AvifEncoderTest.php new file mode 100644 index 00000000..b2d1fa0d --- /dev/null +++ b/tests/Drivers/Gd/Encoders/AvifEncoderTest.php @@ -0,0 +1,33 @@ +getTestImage(); + $encoder = new AvifEncoder(10); + $result = $encoder->encode($image); + $this->assertTrue(MimeSniffer::createFromString($result)->matches(new ImageAvif())); + } +} diff --git a/tests/Drivers/Imagick/Encoders/AvifEncoderTest.php b/tests/Drivers/Imagick/Encoders/AvifEncoderTest.php new file mode 100644 index 00000000..e3fa42ca --- /dev/null +++ b/tests/Drivers/Imagick/Encoders/AvifEncoderTest.php @@ -0,0 +1,34 @@ +newImage(3, 2, new ImagickPixel('red'), 'png'); + + return new Image($imagick); + } + + public function testEncode(): void + { + $image = $this->getTestImage(); + $encoder = new AvifEncoder(10); + $result = $encoder->encode($image); + $this->assertTrue(MimeSniffer::createFromString($result)->matches(new ImageAvif())); + } +} From 70fe7fe98f33bcf8a265583560d8c4f36154f8e3 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Tue, 3 Oct 2023 17:36:32 +0200 Subject: [PATCH 255/476] Add libavif-dev to Github testing environment --- .github/workflows/run-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 93a42fa8..f71b12ee 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -30,7 +30,7 @@ jobs: run: | sudo apt-get -y remove imagemagick imagemagick-6-common libmagic-dev sudo apt-get update - sudo apt-get install -y libjpeg-dev libgif-dev libtiff-dev libpng-dev libwebp-dev + sudo apt-get install -y libjpeg-dev libgif-dev libtiff-dev libpng-dev libwebp-dev libavif-dev sudo apt-get install -y libmagickwand-dev - name: Cache ImageMagick From 7535eb64291197ee52a5a00d032f17cbd7d617a3 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Tue, 3 Oct 2023 18:00:55 +0200 Subject: [PATCH 256/476] Change format of AvifEncoder --- src/Drivers/Imagick/Encoders/AvifEncoder.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Drivers/Imagick/Encoders/AvifEncoder.php b/src/Drivers/Imagick/Encoders/AvifEncoder.php index f1aef717..0cc85604 100644 --- a/src/Drivers/Imagick/Encoders/AvifEncoder.php +++ b/src/Drivers/Imagick/Encoders/AvifEncoder.php @@ -12,7 +12,7 @@ class AvifEncoder extends AbstractEncoder implements EncoderInterface { public function encode(ImageInterface $image): EncodedImage { - $format = 'avif'; + $format = 'AVIF'; $compression = Imagick::COMPRESSION_ZIP; $imagick = $image->getFrame()->getCore(); From 9eb44b0ea5237c767da127c4e2e8263deb4be465 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Tue, 3 Oct 2023 18:04:31 +0200 Subject: [PATCH 257/476] Add debugging info to Github workflow --- .github/workflows/run-tests.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index f71b12ee..68d7bfbf 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -92,6 +92,9 @@ jobs: - name: Which Imagick Version run: php -r 'var_dump(Imagick::getVersion());' + - name: Supported Imagick Formats + run: php -r 'var_dump(Imagick::queryFormats());' + - name: Execute tests run: vendor/bin/phpunit --no-coverage From 82865c6755cb45750474b247e05ceb8455565d6c Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Wed, 4 Oct 2023 15:45:23 +0200 Subject: [PATCH 258/476] Add libheif-dev to Github workflow --- .github/workflows/run-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 68d7bfbf..43cbf28a 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -30,7 +30,7 @@ jobs: run: | sudo apt-get -y remove imagemagick imagemagick-6-common libmagic-dev sudo apt-get update - sudo apt-get install -y libjpeg-dev libgif-dev libtiff-dev libpng-dev libwebp-dev libavif-dev + sudo apt-get install -y libjpeg-dev libgif-dev libtiff-dev libpng-dev libwebp-dev libavif-dev libheif-dev sudo apt-get install -y libmagickwand-dev - name: Cache ImageMagick From defc71240dc47b1613d6739f6fdb60d0d42fdc31 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Wed, 4 Oct 2023 15:49:26 +0200 Subject: [PATCH 259/476] Fix doc block reference --- tests/Drivers/Imagick/Encoders/AvifEncoderTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Drivers/Imagick/Encoders/AvifEncoderTest.php b/tests/Drivers/Imagick/Encoders/AvifEncoderTest.php index e3fa42ca..8f220672 100644 --- a/tests/Drivers/Imagick/Encoders/AvifEncoderTest.php +++ b/tests/Drivers/Imagick/Encoders/AvifEncoderTest.php @@ -12,7 +12,7 @@ use Intervention\MimeSniffer\Types\ImageAvif; /** * @requires extension imagick - * @covers \Intervention\Image\Drivers\Imagick\Encoders\JpegEncoder + * @covers \Intervention\Image\Drivers\Imagick\Encoders\AvifEncoder */ class AvifEncoderTest extends TestCase { From 1c45941711f237cc75819e44c2611d0f84a6e5b8 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Wed, 4 Oct 2023 16:29:28 +0200 Subject: [PATCH 260/476] Add file pointer image decoder --- .../Gd/Decoders/FilePointerImageDecoder.php | 25 +++++++++++++++++++ src/Drivers/Gd/InputHandler.php | 1 + .../Decoders/FilePointerImageDecoder.php | 25 +++++++++++++++++++ src/Drivers/Imagick/InputHandler.php | 1 + .../Decoders/FilePointerImageDecoderTest.php | 25 +++++++++++++++++++ .../Decoders/FilePointerImageDecoderTest.php | 25 +++++++++++++++++++ 6 files changed, 102 insertions(+) create mode 100644 src/Drivers/Gd/Decoders/FilePointerImageDecoder.php create mode 100644 src/Drivers/Imagick/Decoders/FilePointerImageDecoder.php create mode 100644 tests/Drivers/Gd/Decoders/FilePointerImageDecoderTest.php create mode 100644 tests/Drivers/Imagick/Decoders/FilePointerImageDecoderTest.php diff --git a/src/Drivers/Gd/Decoders/FilePointerImageDecoder.php b/src/Drivers/Gd/Decoders/FilePointerImageDecoder.php new file mode 100644 index 00000000..80860797 --- /dev/null +++ b/src/Drivers/Gd/Decoders/FilePointerImageDecoder.php @@ -0,0 +1,25 @@ +getTestImagePath('test.jpg'), 'r'); + $result = $decoder->decode($fp); + $this->assertInstanceOf(Image::class, $result); + } +} diff --git a/tests/Drivers/Imagick/Decoders/FilePointerImageDecoderTest.php b/tests/Drivers/Imagick/Decoders/FilePointerImageDecoderTest.php new file mode 100644 index 00000000..aeb4bc89 --- /dev/null +++ b/tests/Drivers/Imagick/Decoders/FilePointerImageDecoderTest.php @@ -0,0 +1,25 @@ +getTestImagePath('test.jpg'), 'r'); + $result = $decoder->decode($fp); + $this->assertInstanceOf(Image::class, $result); + } +} From 8381a5940334e18010848e452b500c66b35543d9 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Wed, 4 Oct 2023 16:56:03 +0200 Subject: [PATCH 261/476] Add EncodedImage::toFilePointer() --- src/EncodedImage.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/EncodedImage.php b/src/EncodedImage.php index a0461d84..d1014497 100644 --- a/src/EncodedImage.php +++ b/src/EncodedImage.php @@ -38,6 +38,15 @@ class EncodedImage return $this->data; } + public function toFilePointer() + { + $pointer = fopen('php://temp', 'rw'); + fputs($pointer, $this->toString()); + rewind($pointer); + + return $pointer; + } + public function __toString(): string { return $this->toString(); From 4fbc7f5840d7a7895fb38c9e77ea13582ed1a389 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Wed, 4 Oct 2023 17:09:13 +0200 Subject: [PATCH 262/476] Add Image::readExif() --- src/Drivers/Abstract/AbstractImage.php | 38 ++++++++++++++++++++++++ src/Exceptions/NotSupportedException.php | 8 +++++ 2 files changed, 46 insertions(+) create mode 100644 src/Exceptions/NotSupportedException.php diff --git a/src/Drivers/Abstract/AbstractImage.php b/src/Drivers/Abstract/AbstractImage.php index 3b70d93a..7565dcf6 100644 --- a/src/Drivers/Abstract/AbstractImage.php +++ b/src/Drivers/Abstract/AbstractImage.php @@ -2,8 +2,11 @@ namespace Intervention\Image\Drivers\Abstract; +use Exception; +use Intervention\Gif\Exception\NotReadableException; use Intervention\Image\Collection; use Intervention\Image\EncodedImage; +use Intervention\Image\Exceptions\NotSupportedException; use Intervention\Image\Geometry\Circle; use Intervention\Image\Geometry\Ellipse; use Intervention\Image\Geometry\Line; @@ -348,6 +351,41 @@ abstract class AbstractImage implements ImageInterface ); } + /** + * Read exif data from current image instance + * + * Returns value of given key or null if key was not found. If no + * parameter is given an array of all available data is returned. + * + * @param null|string $tag + * @return mixed + * @throws NotSupportedException + * @throws NotReadableException + */ + public function readExif(?string $tag = null): mixed + { + if (!function_exists('exif_read_data')) { + throw new NotSupportedException( + 'Reading Exif data is not supported by this PHP installation.' + ); + } + + try { + $pointer = $this->toJpeg()->toFilePointer(); + $data = @exif_read_data($pointer); + } catch (Exception $e) { + throw new NotReadableException('Unable to read Exif data from this image.'); + } + + fclose($pointer); + + if (!is_null($tag) && is_array($data)) { + $data = array_key_exists($tag, $data) ? $data[$tag] : null; + } + + return is_array($data) ? new Collection($data) : $data; + } + public function destroy(): void { $this->modify( diff --git a/src/Exceptions/NotSupportedException.php b/src/Exceptions/NotSupportedException.php new file mode 100644 index 00000000..e4c88468 --- /dev/null +++ b/src/Exceptions/NotSupportedException.php @@ -0,0 +1,8 @@ + Date: Thu, 5 Oct 2023 16:28:19 +0200 Subject: [PATCH 263/476] Change signature of Collection - Removed method query() - Method get replaces query() --- src/Collection.php | 53 ++++++++++++++++++-------- src/Interfaces/CollectionInterface.php | 3 +- tests/CollectionTest.php | 47 +++++++++-------------- 3 files changed, 57 insertions(+), 46 deletions(-) diff --git a/src/Collection.php b/src/Collection.php index 76e35b13..eca7ce26 100644 --- a/src/Collection.php +++ b/src/Collection.php @@ -96,33 +96,54 @@ class Collection implements CollectionInterface, IteratorAggregate, Countable } /** - * Return item with given key + * Return item at given position starting at 0 * * @param integer $key * @return mixed */ - public function get(int $key = 0, $default = null) + public function getAtPosition(int $key = 0, $default = null) { - if (! array_key_exists($key, $this->items)) { + if ($this->count() == 0) { return $default; } - return $this->items[$key]; - } - - public function has(int $key): bool - { - return array_key_exists($key, $this->items); - } - - public function query(string $query, $default = null) - { - $items = $this->getItemsFlat(); - if (!array_key_exists($query, $items)) { + $positions = array_values($this->items); + if (! array_key_exists($key, $positions)) { return $default; } - return $items[$query]; + return $positions[$key]; + } + + public function get(int|string $query, $default = null) + { + if ($this->count() == 0) { + return $default; + } + + if (is_int($query) && array_key_exists($query, $this->items)) { + return $this->items[$query]; + } + + if (is_string($query) && strpos($query, '.') === false) { + return array_key_exists($query, $this->items) ? $this->items[$query] : $default; + } + + $query = explode('.', $query); + + $result = $default; + $items = $this->items; + foreach ($query as $key) { + if (!is_array($items) || !array_key_exists($key, $items)) { + $result = $default; + break; + } + + $result = $items[$key]; + $items = $result; + } + + return $result; } public function map(callable $callback): self diff --git a/src/Interfaces/CollectionInterface.php b/src/Interfaces/CollectionInterface.php index 9cfcbae3..f42769a3 100644 --- a/src/Interfaces/CollectionInterface.php +++ b/src/Interfaces/CollectionInterface.php @@ -7,8 +7,7 @@ use Traversable; interface CollectionInterface extends Traversable { public function push($item): CollectionInterface; - public function get(int $key, $default = null); - public function has(int $key); + public function get(int|string $key, $default = null); public function first(); public function last(); public function count(): int; diff --git a/tests/CollectionTest.php b/tests/CollectionTest.php index 67488a62..22857526 100644 --- a/tests/CollectionTest.php +++ b/tests/CollectionTest.php @@ -86,24 +86,6 @@ class CollectionTest extends TestCase $this->assertEquals('BAZ', $collection->get(2)); } - public function testGet() - { - $collection = new Collection(['foo', 'bar', 'baz']); - $this->assertEquals('foo', $collection->get(0)); - $this->assertEquals('bar', $collection->get(1)); - $this->assertEquals('baz', $collection->get(2)); - $this->assertNull($collection->get(3)); - $this->assertEquals('test', $collection->get(3, 'test')); - } - - public function testHas(): void - { - $collection = new Collection(['foo', 'bar']); - $this->assertTrue($collection->has(0)); - $this->assertTrue($collection->has(1)); - $this->assertFalse($collection->has(2)); - } - public function testToArray() { $collection = new Collection(['foo', 'bar', 'baz']); @@ -122,11 +104,14 @@ class CollectionTest extends TestCase $this->assertEquals(['foo', 'bar', 'baz'], $mapped->toArray()); } - public function testQuery(): void + public function testGet(): void { $collection = new Collection([ - 'foo' => 'FOO', - 'bar' => 'BAR', + 'first', + 'second', + ['testx' => 'x'], + 'foo' => 'foo_value', + 'bar' => 'bar_value', 'baz' => [ 'test1' => '1', 'test2' => '2', @@ -136,12 +121,18 @@ class CollectionTest extends TestCase ] ]); - $this->assertEquals('FOO', $collection->query('foo')); - $this->assertEquals('BAR', $collection->query('bar')); - $this->assertEquals('1', $collection->query('baz.test1')); - $this->assertEquals('2', $collection->query('baz.test2')); - $this->assertEquals('value', $collection->query('baz.test3.example')); - $this->assertEquals('value', $collection->query('baz.test3.example', 'default')); - $this->assertEquals('default', $collection->query('baz.test3.no', 'default')); + $this->assertEquals('first', $collection->get(0)); + $this->assertEquals('second', $collection->get(1)); + $this->assertEquals('first', $collection->get('0')); + $this->assertEquals('second', $collection->get('1')); + $this->assertEquals('x', $collection->get('2.testx')); + $this->assertEquals('foo_value', $collection->get('foo')); + $this->assertEquals('bar_value', $collection->get('bar')); + $this->assertEquals('1', $collection->get('baz.test1')); + $this->assertEquals('2', $collection->get('baz.test2')); + $this->assertEquals('value', $collection->get('baz.test3.example')); + $this->assertEquals('value', $collection->get('baz.test3.example', 'default')); + $this->assertEquals('default', $collection->get('baz.test3.no', 'default')); + $this->assertEquals(['example' => 'value'], $collection->get('baz.test3')); } } From a62859fbf46ca74ed92fa2a90193ba3a6a8df9d8 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Thu, 5 Oct 2023 17:20:15 +0200 Subject: [PATCH 264/476] Add EXIF data decoding --- src/Drivers/Abstract/AbstractImage.php | 43 ++++++------------- .../Abstract/Decoders/AbstractDecoder.php | 20 +++++++++ .../Gd/Decoders/BinaryImageDecoder.php | 5 ++- .../Imagick/Decoders/BinaryImageDecoder.php | 1 + 4 files changed, 37 insertions(+), 32 deletions(-) diff --git a/src/Drivers/Abstract/AbstractImage.php b/src/Drivers/Abstract/AbstractImage.php index 7565dcf6..ba849194 100644 --- a/src/Drivers/Abstract/AbstractImage.php +++ b/src/Drivers/Abstract/AbstractImage.php @@ -2,7 +2,6 @@ namespace Intervention\Image\Drivers\Abstract; -use Exception; use Intervention\Gif\Exception\NotReadableException; use Intervention\Image\Collection; use Intervention\Image\EncodedImage; @@ -21,6 +20,7 @@ use Intervention\Image\Interfaces\SizeInterface; use Intervention\Image\Traits\CanHandleInput; use Intervention\Image\Traits\CanResolveDriverClass; use Intervention\Image\Traits\CanRunCallback; +use ReflectionProperty; abstract class AbstractImage implements ImageInterface { @@ -28,6 +28,10 @@ abstract class AbstractImage implements ImageInterface use CanHandleInput; use CanRunCallback; + protected Collection $exif; + + + public function eachFrame(callable $callback): ImageInterface { foreach ($this as $frame) { @@ -351,39 +355,16 @@ abstract class AbstractImage implements ImageInterface ); } - /** - * Read exif data from current image instance - * - * Returns value of given key or null if key was not found. If no - * parameter is given an array of all available data is returned. - * - * @param null|string $tag - * @return mixed - * @throws NotSupportedException - * @throws NotReadableException - */ - public function readExif(?string $tag = null): mixed + public function setExif(array $data): ImageInterface { - if (!function_exists('exif_read_data')) { - throw new NotSupportedException( - 'Reading Exif data is not supported by this PHP installation.' - ); - } + $this->exif = new Collection($data); - try { - $pointer = $this->toJpeg()->toFilePointer(); - $data = @exif_read_data($pointer); - } catch (Exception $e) { - throw new NotReadableException('Unable to read Exif data from this image.'); - } + return $this; + } - fclose($pointer); - - if (!is_null($tag) && is_array($data)) { - $data = array_key_exists($tag, $data) ? $data[$tag] : null; - } - - return is_array($data) ? new Collection($data) : $data; + public function getExif(?string $query = null): mixed + { + return is_null($query) ? $this->exif : $this->exif->get($query); } public function destroy(): void diff --git a/src/Drivers/Abstract/Decoders/AbstractDecoder.php b/src/Drivers/Abstract/Decoders/AbstractDecoder.php index 61e350d2..61fee9f3 100644 --- a/src/Drivers/Abstract/Decoders/AbstractDecoder.php +++ b/src/Drivers/Abstract/Decoders/AbstractDecoder.php @@ -2,6 +2,7 @@ namespace Intervention\Image\Drivers\Abstract\Decoders; +use Exception; use Intervention\Image\Exceptions\DecoderException; use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\DecoderInterface; @@ -31,6 +32,25 @@ abstract class AbstractDecoder implements DecoderInterface return $decoded; } + protected function decodeExifData(string $image_data): array + { + if (! function_exists('exif_read_data')) { + return []; + } + + try { + $pointer = fopen('php://temp', 'rw'); + fputs($pointer, $image_data); + rewind($pointer); + + $data = @exif_read_data($pointer, null, true); + } catch (Exception $e) { + $data = []; + } + + return is_array($data) ? $data : []; + } + protected function hasSuccessor(): bool { return $this->successor !== null; diff --git a/src/Drivers/Gd/Decoders/BinaryImageDecoder.php b/src/Drivers/Gd/Decoders/BinaryImageDecoder.php index 467493b0..60157efc 100644 --- a/src/Drivers/Gd/Decoders/BinaryImageDecoder.php +++ b/src/Drivers/Gd/Decoders/BinaryImageDecoder.php @@ -42,7 +42,10 @@ class BinaryImageDecoder extends AbstractDecoder implements DecoderInterface imagesavealpha($gd, true); - return new Image(new Collection([new Frame($gd)])); + $image = new Image(new Collection([new Frame($gd)])); + $image->setExif($this->decodeExifData($input)); + + return $image; } protected function decodeGif($input): ImageInterface diff --git a/src/Drivers/Imagick/Decoders/BinaryImageDecoder.php b/src/Drivers/Imagick/Decoders/BinaryImageDecoder.php index 5cb31596..ac4f1488 100644 --- a/src/Drivers/Imagick/Decoders/BinaryImageDecoder.php +++ b/src/Drivers/Imagick/Decoders/BinaryImageDecoder.php @@ -28,6 +28,7 @@ class BinaryImageDecoder extends AbstractDecoder implements DecoderInterface $image = new Image($imagick); $image->setLoops($imagick->getImageIterations()); + $image->setExif($this->decodeExifData($input)); return $image; } From f3122ae4367b7cdab972e02804195b0ebd91aa7b Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Thu, 5 Oct 2023 17:27:51 +0200 Subject: [PATCH 265/476] Fix image orientation with GD driver --- .../Abstract/Decoders/AbstractDecoder.php | 22 +++++++++---------- .../Gd/Decoders/BinaryImageDecoder.php | 13 ++++++++++- src/Interfaces/ImageInterface.php | 15 +++++++++++++ 3 files changed, 38 insertions(+), 12 deletions(-) diff --git a/src/Drivers/Abstract/Decoders/AbstractDecoder.php b/src/Drivers/Abstract/Decoders/AbstractDecoder.php index 61fee9f3..52bcd1b0 100644 --- a/src/Drivers/Abstract/Decoders/AbstractDecoder.php +++ b/src/Drivers/Abstract/Decoders/AbstractDecoder.php @@ -32,6 +32,16 @@ abstract class AbstractDecoder implements DecoderInterface return $decoded; } + protected function hasSuccessor(): bool + { + return $this->successor !== null; + } + + protected function inputType($input): AbstractType + { + return MimeSniffer::createFromString($input)->getType(); + } + protected function decodeExifData(string $image_data): array { if (! function_exists('exif_read_data')) { @@ -42,22 +52,12 @@ abstract class AbstractDecoder implements DecoderInterface $pointer = fopen('php://temp', 'rw'); fputs($pointer, $image_data); rewind($pointer); - $data = @exif_read_data($pointer, null, true); + fclose($pointer); } catch (Exception $e) { $data = []; } return is_array($data) ? $data : []; } - - protected function hasSuccessor(): bool - { - return $this->successor !== null; - } - - protected function inputType($input): AbstractType - { - return MimeSniffer::createFromString($input)->getType(); - } } diff --git a/src/Drivers/Gd/Decoders/BinaryImageDecoder.php b/src/Drivers/Gd/Decoders/BinaryImageDecoder.php index 60157efc..ae90dd86 100644 --- a/src/Drivers/Gd/Decoders/BinaryImageDecoder.php +++ b/src/Drivers/Gd/Decoders/BinaryImageDecoder.php @@ -42,10 +42,21 @@ class BinaryImageDecoder extends AbstractDecoder implements DecoderInterface imagesavealpha($gd, true); + // build image instance $image = new Image(new Collection([new Frame($gd)])); $image->setExif($this->decodeExifData($input)); - return $image; + // fix image orientation + return match ($image->getExif('IFD0.Orientation')) { + 2 => $image->flip(), + 3 => $image->rotate(180), + 4 => $image->rotate(180)->flip(), + 5 => $image->rotate(270)->flip(), + 6 => $image->rotate(270), + 7 => $image->rotate(90)->flip(), + 8 => $image->rotate(90), + default => $image + }; } protected function decodeGif($input): ImageInterface diff --git a/src/Interfaces/ImageInterface.php b/src/Interfaces/ImageInterface.php index 34b42f12..45b257db 100644 --- a/src/Interfaces/ImageInterface.php +++ b/src/Interfaces/ImageInterface.php @@ -3,6 +3,7 @@ namespace Intervention\Image\Interfaces; use Countable; +use Intervention\Image\Collection; use Intervention\Image\EncodedImage; use Traversable; @@ -55,6 +56,20 @@ interface ImageInterface extends Traversable, Countable */ public function getSize(): SizeInterface; + /** + * Return exif data of current image + * + * @return mixed + */ + public function getExif(?string $query = null): mixed; + + /** + * Set exif data on current image (will not be written in final image) + * + * @return ImageInterface + */ + public function setExif(array $data): ImageInterface; + /** * Determine if current image is animated * From 79f0b3a70fe42fed89a8a88fcd33008b84ab5d6e Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Thu, 5 Oct 2023 17:35:33 +0200 Subject: [PATCH 266/476] Remove unused code --- src/Collection.php | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/src/Collection.php b/src/Collection.php index eca7ce26..314e5f58 100644 --- a/src/Collection.php +++ b/src/Collection.php @@ -182,22 +182,4 @@ class Collection implements CollectionInterface, IteratorAggregate, Countable return $this; } - - private function getItemsFlat(): array - { - $iterator = new RecursiveIteratorIterator( - new RecursiveArrayIterator($this->items) - ); - - $items = []; - foreach ($iterator as $value) { - $keys = []; - foreach (range(0, $iterator->getDepth()) as $depth) { - $keys[] = $iterator->getSubIterator($depth)->key(); - } - $items[join('.', $keys)] = $value; - } - - return $items; - } } From 26af25da1383458484356bca426d8ce8c1a00ddf Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Thu, 5 Oct 2023 17:36:55 +0200 Subject: [PATCH 267/476] Remove unused code --- src/Collection.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Collection.php b/src/Collection.php index 314e5f58..4b27a669 100644 --- a/src/Collection.php +++ b/src/Collection.php @@ -8,8 +8,6 @@ use ArrayIterator; use Countable; use Traversable; use IteratorAggregate; -use RecursiveIteratorIterator; -use RecursiveArrayIterator; class Collection implements CollectionInterface, IteratorAggregate, Countable { From d317c478717ce9117862e535228340a063bb2b8d Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Thu, 5 Oct 2023 17:42:56 +0200 Subject: [PATCH 268/476] Add tests for Image::getExif() & Image::setExif() --- tests/Drivers/Abstract/AbstractImageTest.php | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/tests/Drivers/Abstract/AbstractImageTest.php b/tests/Drivers/Abstract/AbstractImageTest.php index 9b5a943c..e4f64cd2 100644 --- a/tests/Drivers/Abstract/AbstractImageTest.php +++ b/tests/Drivers/Abstract/AbstractImageTest.php @@ -11,7 +11,6 @@ use Intervention\Image\Interfaces\EncoderInterface; use Intervention\Image\Interfaces\FrameInterface; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; -use Intervention\Image\Interfaces\PointInterface; use Intervention\Image\Tests\TestCase; use Mockery; @@ -427,6 +426,15 @@ class AbstractImageTest extends TestCase $this->assertInstanceOf(ImageInterface::class, $result); } + public function testSetGetExif(): void + { + $img = $this->abstractImageMock(); + $img->setExif((['test' => 'value'])); + + $this->assertInstanceOf(Collection::class, $img->getExif()); + $this->assertEquals('value', $img->getExif('test')); + } + public function testDestroy(): void { $img = $this->abstractImageMock(); From f15a30932c64fb87955e5db36433a5c3d2cfbf33 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Thu, 5 Oct 2023 17:54:19 +0200 Subject: [PATCH 269/476] Add tests for EXIF data reading --- .../Gd/Decoders/BinaryImageDecoderTest.php | 17 ++++++++++++++--- .../Decoders/BinaryImageDecoderTest.php | 17 ++++++++++++++--- tests/images/exif.jpg | Bin 0 -> 7791 bytes 3 files changed, 28 insertions(+), 6 deletions(-) create mode 100644 tests/images/exif.jpg diff --git a/tests/Drivers/Gd/Decoders/BinaryImageDecoderTest.php b/tests/Drivers/Gd/Decoders/BinaryImageDecoderTest.php index 632f0363..f59f0496 100644 --- a/tests/Drivers/Gd/Decoders/BinaryImageDecoderTest.php +++ b/tests/Drivers/Gd/Decoders/BinaryImageDecoderTest.php @@ -15,7 +15,7 @@ class BinaryImageDecoderTest extends TestCase public function testDecodePng(): void { $decoder = new BinaryImageDecoder(); - $image = $decoder->decode(file_get_contents(__DIR__ . '/../../../images/tile.png')); + $image = $decoder->decode(file_get_contents($this->getTestImagePath('tile.png'))); $this->assertInstanceOf(Image::class, $image); $this->assertEquals(16, $image->getWidth()); $this->assertEquals(16, $image->getHeight()); @@ -25,7 +25,7 @@ class BinaryImageDecoderTest extends TestCase public function testDecodeGif(): void { $decoder = new BinaryImageDecoder(); - $image = $decoder->decode(file_get_contents(__DIR__ . '/../../../images/red.gif')); + $image = $decoder->decode(file_get_contents($this->getTestImagePath('red.gif'))); $this->assertInstanceOf(Image::class, $image); $this->assertEquals(16, $image->getWidth()); $this->assertEquals(16, $image->getHeight()); @@ -35,10 +35,21 @@ class BinaryImageDecoderTest extends TestCase public function testDecodeAnimatedGif(): void { $decoder = new BinaryImageDecoder(); - $image = $decoder->decode(file_get_contents(__DIR__ . '/../../../images/cats.gif')); + $image = $decoder->decode(file_get_contents($this->getTestImagePath('cats.gif'))); $this->assertInstanceOf(Image::class, $image); $this->assertEquals(75, $image->getWidth()); $this->assertEquals(50, $image->getHeight()); $this->assertCount(4, $image); } + + public function testDecodeJpegWithExif(): void + { + $decoder = new BinaryImageDecoder(); + $image = $decoder->decode(file_get_contents($this->getTestImagePath('exif.jpg'))); + $this->assertInstanceOf(Image::class, $image); + $this->assertEquals(16, $image->getWidth()); + $this->assertEquals(16, $image->getHeight()); + $this->assertCount(1, $image); + $this->assertEquals('Oliver Vogel', $image->getExif('IFD0.Artist')); + } } diff --git a/tests/Drivers/Imagick/Decoders/BinaryImageDecoderTest.php b/tests/Drivers/Imagick/Decoders/BinaryImageDecoderTest.php index 455bdcc7..b3d20b4a 100644 --- a/tests/Drivers/Imagick/Decoders/BinaryImageDecoderTest.php +++ b/tests/Drivers/Imagick/Decoders/BinaryImageDecoderTest.php @@ -11,7 +11,7 @@ class BinaryImageDecoderTest extends TestCase public function testDecodePng(): void { $decoder = new BinaryImageDecoder(); - $image = $decoder->decode(file_get_contents(__DIR__ . '/../../../images/tile.png')); + $image = $decoder->decode(file_get_contents($this->getTestImagePath('tile.png'))); $this->assertInstanceOf(Image::class, $image); $this->assertEquals(16, $image->getWidth()); $this->assertEquals(16, $image->getHeight()); @@ -21,7 +21,7 @@ class BinaryImageDecoderTest extends TestCase public function testDecodeGif(): void { $decoder = new BinaryImageDecoder(); - $image = $decoder->decode(file_get_contents(__DIR__ . '/../../../images/red.gif')); + $image = $decoder->decode(file_get_contents($this->getTestImagePath('red.gif'))); $this->assertInstanceOf(Image::class, $image); $this->assertEquals(16, $image->getWidth()); $this->assertEquals(16, $image->getHeight()); @@ -31,10 +31,21 @@ class BinaryImageDecoderTest extends TestCase public function testDecodeAnimatedGif(): void { $decoder = new BinaryImageDecoder(); - $image = $decoder->decode(file_get_contents(__DIR__ . '/../../../images/cats.gif')); + $image = $decoder->decode(file_get_contents($this->getTestImagePath('cats.gif'))); $this->assertInstanceOf(Image::class, $image); $this->assertEquals(75, $image->getWidth()); $this->assertEquals(50, $image->getHeight()); $this->assertCount(4, $image); } + + public function testDecodeJpegWithExif(): void + { + $decoder = new BinaryImageDecoder(); + $image = $decoder->decode(file_get_contents($this->getTestImagePath('exif.jpg'))); + $this->assertInstanceOf(Image::class, $image); + $this->assertEquals(16, $image->getWidth()); + $this->assertEquals(16, $image->getHeight()); + $this->assertCount(1, $image); + $this->assertEquals('Oliver Vogel', $image->getExif('IFD0.Artist')); + } } diff --git a/tests/images/exif.jpg b/tests/images/exif.jpg new file mode 100644 index 0000000000000000000000000000000000000000..ab98706c205e3054afaf64359c11890fb3d45214 GIT binary patch literal 7791 zcmeGhTW}lI_3o}-mSjhYZDIn$EXteK*2`9Gfk`acu}!1Ik>tF7I;-84ykxbj?5=Dj z9~}sxw9Irm389p9h9LoPzvopn-FN}V@5squ689k=24h_ z)z{wpIOp7R&pCVU*?V>BFVrtE(nL)vB9zIXn-D@Cpl0HHTb)DUx zr#3cdXrPHAs1Xa#r>Gd|_dx$4>SogKg#OukB;wDw7?Wf)?vW+WHIi zH;|ub^vQGvQT4x|E;2&G9hk2E8*~);JswYo$KTQ65Bj{mVDAdQe?@O!cXw}hcVEy? zN~19ci)*UW=j-h140Lq`db+y0dT`a%V@?Tv5C-+9P|$+{D8Nu5LcS_ zsVyL-lOhEK#)Ux??Qpu>o(``M9HSjcA)2v}0Yo_{hIY_Sx69*X{Bb}A8OMz)qt3Bi zTzBZ;oiSI>u~Uzy)~xN#K3#|pYDb<+yVo7hUAl0Y*N3h-efF;PEMyPv%O*7QRijchV2F1zn$eA1K~-f!F|8P)s8q`s zf%~{k%Nic9Aaiq44O65f1JA)|lc%Wd;L2itVs9Qm=3?fA8BJA-Ink(Ab{1~u0qI9G zNJA?86%mVaNJIvzq6%h(ycL@oMubgDHk5`${z6rf4M`y^0pCHW)0usrFfDg8WkR}95c0+cLh_8?o*Gxi{@De2}wF`9$z1zEQ-J4(j9J(wuV z!l4ZUwjL3paDb+C4vaBkl#uUGW z!6LN4J1uxtD$TVbd=>&^v+40(VcsxhXoPLrYRNuYPjFq0MWNYp2QvMwlfdwPtr5y= z?ePtS>lHHYAT?AJnFXIXT)d4<0uD=P-zLy06fPD4BSQ@zA}CY?a_Yg8HY7&i`=PdI zh6n_Eegi^yz5`=~cUFDL!n}sjOQPx@1xIY*!Mv?~NN)u-;c3|I2HYc`Pt%U8@sOE@ z%9I`t&AHfCUNnmoh!GMzGDHgj^`j`j8`}qy+NBkS=bE;*(MwOlM2(3`RmL$5pI%%A zwW#cHl<55PWUHowr;feRH=3NuM&K zkpmyhO^2q)EKHB+I8=B`U`NGI#QZ+N@wN}ngUiR+<>Tzt$63?6ZUk>Z5V663umi0? zV-T=aRDi(E!ohD2`UX5Y=~aND`+X=_l!ilM0y8vMFuxIQ^ZG@h`i0KPIm4(V!(l}q z;PAC^fLF`m8dnKN2O?o~%a&S&;}3`iTM!{aj)wmF-S3B3Nf-_78OlU5m9#h~O&!+6 z+~FO0{_p{QL`{fM>xS%jnNQD zYN}AK*ld@yl|?A&vIOK-2qOMjoUYwW*G1uV5D#^4aa&Vr_!&%mPQO!%&qF)tS zW{7YA7Dq$)XNLaBaDOzBho2eXe<=2`NGutNn4}q1kcx+vA|<~_w2^|(2ym!=Ut)nz z7FDgxfyPpqD~aLTD`F|c+J;P$FA1y+;>C#=nM`t8(G5=F#p&_U5NJIhNkTFb9|kAf zJQ&#=izJ4l(c#E&d@M3Jyg88ucxuF&JFfCo{9diO!hbQ>JYjJ1hvET#56bf93uBTdmNu+%8*S*#gTJShm2j1(q%Fe`JB?rASoZXt4lSo|dc>T(%lnDVM|Ha5`L0 zr>nySkI&&R_JbzpKOBckR&=6Wbnna`*GI z8&|*i@)ew!603LLZ)T_7B{?-bwRf)enJ+%}tslSe+B=&@ruRvQKKtNTzx|UJ|MZWW zlDF-@{qVgfzxJJ4eOh!^1FTas|hUv@z!{kl1zb&SSAtk6-F}`nd~h zQoY%Nb~(OwFn#2jFnzH8SfCRT3{jM>d^o_XS#jhmi- q`O47~k388f^0t Date: Fri, 6 Oct 2023 16:17:42 +0200 Subject: [PATCH 270/476] Return empty collection if no exif data is set --- src/Drivers/Abstract/AbstractImage.php | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/Drivers/Abstract/AbstractImage.php b/src/Drivers/Abstract/AbstractImage.php index ba849194..3b46865f 100644 --- a/src/Drivers/Abstract/AbstractImage.php +++ b/src/Drivers/Abstract/AbstractImage.php @@ -2,10 +2,8 @@ namespace Intervention\Image\Drivers\Abstract; -use Intervention\Gif\Exception\NotReadableException; use Intervention\Image\Collection; use Intervention\Image\EncodedImage; -use Intervention\Image\Exceptions\NotSupportedException; use Intervention\Image\Geometry\Circle; use Intervention\Image\Geometry\Ellipse; use Intervention\Image\Geometry\Line; @@ -20,7 +18,6 @@ use Intervention\Image\Interfaces\SizeInterface; use Intervention\Image\Traits\CanHandleInput; use Intervention\Image\Traits\CanResolveDriverClass; use Intervention\Image\Traits\CanRunCallback; -use ReflectionProperty; abstract class AbstractImage implements ImageInterface { @@ -30,8 +27,6 @@ abstract class AbstractImage implements ImageInterface protected Collection $exif; - - public function eachFrame(callable $callback): ImageInterface { foreach ($this as $frame) { @@ -364,6 +359,10 @@ abstract class AbstractImage implements ImageInterface public function getExif(?string $query = null): mixed { + if (!isset($this->exif)) { + return new Collection(); + } + return is_null($query) ? $this->exif : $this->exif->get($query); } From ea79f4b669124759a2a07f020272b834a9253966 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Fri, 6 Oct 2023 16:20:50 +0200 Subject: [PATCH 271/476] Fix type error --- src/Drivers/Gd/Modifiers/DrawEllipseModifier.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Drivers/Gd/Modifiers/DrawEllipseModifier.php b/src/Drivers/Gd/Modifiers/DrawEllipseModifier.php index c113b91d..8f956c71 100644 --- a/src/Drivers/Gd/Modifiers/DrawEllipseModifier.php +++ b/src/Drivers/Gd/Modifiers/DrawEllipseModifier.php @@ -26,8 +26,8 @@ class DrawEllipseModifier extends AbstractDrawModifier implements ModifierInterf imagesetthickness($frame->getCore(), $this->ellipse()->getBorderSize()); - // gd's imageellipse doesn't respect imagesetthickness so i use - // imagearc with 359.9 degrees here. + // gd's imageellipse ignores imagesetthickness so i use + // imagearc with 360 degrees instead. imagearc( $frame->getCore(), $this->position->getX(), @@ -35,7 +35,7 @@ class DrawEllipseModifier extends AbstractDrawModifier implements ModifierInterf $this->ellipse()->getWidth(), $this->ellipse()->getHeight(), 0, - 359.99, + 360, $this->getBorderColor()->toInt() ); } else { From 153725e4f7ad4715dab53116de848a0c293d0aa7 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Fri, 6 Oct 2023 16:27:02 +0200 Subject: [PATCH 272/476] Fix bug of Image::setLoops() beiing ignored with imagick --- src/Drivers/Imagick/Image.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Drivers/Imagick/Image.php b/src/Drivers/Imagick/Image.php index 03f2743a..4dc8ca6a 100644 --- a/src/Drivers/Imagick/Image.php +++ b/src/Drivers/Imagick/Image.php @@ -57,6 +57,7 @@ class Image extends AbstractImage implements ImageInterface, Iterator public function setLoops(int $count): ImageInterface { + $this->imagick = $this->imagick->coalesceImages(); $this->imagick->setImageIterations($count); return $this; From ba01e87883b18a8da728db8bcab8f50429315ede Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 7 Oct 2023 09:31:08 +0200 Subject: [PATCH 273/476] Rename methods of ImageInterface Rename pickColor() to getColor() and pickColors() to getColors(). --- src/Drivers/Abstract/AbstractImage.php | 4 ++-- src/Drivers/Gd/Image.php | 2 +- src/Drivers/Imagick/Image.php | 2 +- src/Interfaces/ImageInterface.php | 23 ++++++++++++++++--- tests/Drivers/Abstract/AbstractImageTest.php | 6 ++--- tests/Drivers/Gd/ImageTest.php | 14 +++++------ .../Drivers/Gd/Modifiers/BlurModifierTest.php | 4 ++-- .../Gd/Modifiers/BrightnessModifierTest.php | 4 ++-- .../Gd/Modifiers/ContrastModifierTest.php | 4 ++-- .../Gd/Modifiers/DrawEllipseModiferTest.php | 4 ++-- .../Gd/Modifiers/DrawLineModiferTest.php | 4 ++-- .../Gd/Modifiers/DrawPixelModifierTest.php | 4 ++-- .../Gd/Modifiers/DrawPolygonModifierTest.php | 4 ++-- .../Modifiers/DrawRectangleModifierTest.php | 4 ++-- .../Drivers/Gd/Modifiers/FillModifierTest.php | 16 ++++++------- .../Drivers/Gd/Modifiers/FitModifierTest.php | 8 +++---- .../Gd/Modifiers/FlipFlopModifierTest.php | 8 +++---- .../Gd/Modifiers/GammaModifierTest.php | 4 ++-- .../Gd/Modifiers/GreyscaleModifierTest.php | 4 ++-- .../Gd/Modifiers/InvertModifierTest.php | 8 +++---- .../Gd/Modifiers/PixelateModifierTest.php | 8 +++---- .../Gd/Modifiers/PlaceModifierTest.php | 4 ++-- .../Gd/Modifiers/ResizeModifierTest.php | 8 +++---- .../Gd/Modifiers/SharpenModifierTest.php | 4 ++-- .../Imagick/Modifiers/BlurModifierTest.php | 4 ++-- .../Modifiers/BrightnessModifierTest.php | 4 ++-- .../Modifiers/ContrastModifierTest.php | 4 ++-- .../Modifiers/DrawEllipseModifierTest.php | 4 ++-- .../Modifiers/DrawLineModifierTest.php | 4 ++-- .../Modifiers/DrawPixelModifierTest.php | 4 ++-- .../Modifiers/DrawPolygonModifierTest.php | 4 ++-- .../Modifiers/DrawRectangleModifierTest.php | 4 ++-- .../Imagick/Modifiers/FillModifierTest.php | 16 ++++++------- .../Imagick/Modifiers/FitModifierTest.php | 8 +++---- .../Modifiers/FlipFlopModifierTest.php | 8 +++---- .../Imagick/Modifiers/GammaModifierTest.php | 4 ++-- .../Modifiers/GreyscaleModifierTest.php | 4 ++-- .../Imagick/Modifiers/InvertModifierTest.php | 8 +++---- .../Modifiers/PixelateModifierTest.php | 8 +++---- .../Imagick/Modifiers/PlaceModifierTest.php | 4 ++-- .../Imagick/Modifiers/ResizeModifierTest.php | 8 +++---- .../Imagick/Modifiers/SharpenModifierTest.php | 4 ++-- 42 files changed, 138 insertions(+), 121 deletions(-) diff --git a/src/Drivers/Abstract/AbstractImage.php b/src/Drivers/Abstract/AbstractImage.php index 3b46865f..cc3ac488 100644 --- a/src/Drivers/Abstract/AbstractImage.php +++ b/src/Drivers/Abstract/AbstractImage.php @@ -217,11 +217,11 @@ abstract class AbstractImage implements ImageInterface ); } - public function pickColors(int $x, int $y): CollectionInterface + public function getColors(int $x, int $y): CollectionInterface { $colors = new Collection(); foreach ($this as $key => $frame) { - $colors->push($this->pickColor($x, $y, $key)); + $colors->push($this->getColor($x, $y, $key)); } return $colors; diff --git a/src/Drivers/Gd/Image.php b/src/Drivers/Gd/Image.php index ad9b3754..52a29f1c 100644 --- a/src/Drivers/Gd/Image.php +++ b/src/Drivers/Gd/Image.php @@ -66,7 +66,7 @@ class Image extends AbstractImage implements ImageInterface, IteratorAggregate return imagesy($this->getFrame()->getCore()); } - public function pickColor(int $x, int $y, int $frame_key = 0): ?ColorInterface + public function getColor(int $x, int $y, int $frame_key = 0): ?ColorInterface { if ($frame = $this->getFrame($frame_key)) { return new Color(imagecolorat($frame->getCore(), $x, $y)); diff --git a/src/Drivers/Imagick/Image.php b/src/Drivers/Imagick/Image.php index 4dc8ca6a..0ef864e6 100644 --- a/src/Drivers/Imagick/Image.php +++ b/src/Drivers/Imagick/Image.php @@ -121,7 +121,7 @@ class Image extends AbstractImage implements ImageInterface, Iterator return $this->getFrame()->getCore()->getImageHeight(); } - public function pickColor(int $x, int $y, int $frame_key = 0): ?ColorInterface + public function getColor(int $x, int $y, int $frame_key = 0): ?ColorInterface { if ($frame = $this->getFrame($frame_key)) { return new Color($frame->getCore()->getImagePixelColor($x, $y)); diff --git a/src/Interfaces/ImageInterface.php b/src/Interfaces/ImageInterface.php index 45b257db..6020f963 100644 --- a/src/Interfaces/ImageInterface.php +++ b/src/Interfaces/ImageInterface.php @@ -3,7 +3,6 @@ namespace Intervention\Image\Interfaces; use Countable; -use Intervention\Image\Collection; use Intervention\Image\EncodedImage; use Traversable; @@ -138,8 +137,26 @@ interface ImageInterface extends Traversable, Countable */ public function toPng(): EncodedImage; - public function pickColor(int $x, int $y, int $frame_key = 0): ?ColorInterface; - public function pickColors(int $x, int $y): CollectionInterface; + /** + * Return color of pixel at the given position of the image + * For animated image pass the key of the animation frame. + * + * @param int $x + * @param int $y + * @param int $frame_key + * @return null|ColorInterface + */ + public function getColor(int $x, int $y, int $frame_key = 0): ?ColorInterface; + + /** + * Return a collection with the color of the pixel at the given position + * For animated images all pixel colors for all frames are returned. + * + * @param int $x + * @param int $y + * @return CollectionInterface + */ + public function getColors(int $x, int $y): CollectionInterface; /** * Draw text on image diff --git a/tests/Drivers/Abstract/AbstractImageTest.php b/tests/Drivers/Abstract/AbstractImageTest.php index e4f64cd2..54092d3f 100644 --- a/tests/Drivers/Abstract/AbstractImageTest.php +++ b/tests/Drivers/Abstract/AbstractImageTest.php @@ -297,12 +297,12 @@ class AbstractImageTest extends TestCase $this->assertInstanceOf(ImageInterface::class, $result); } - public function testPickColors(): void + public function testGetColors(): void { $color = Mockery::mock(ColorInterface::class); $img = $this->abstractImageMock(); - $img->shouldReceive('pickColor')->times(3)->andReturn($color); - $result = $img->pickColors(1, 2); + $img->shouldReceive('getColor')->times(3)->andReturn($color); + $result = $img->getColors(1, 2); $this->assertInstanceOf(Collection::class, $result); } diff --git a/tests/Drivers/Gd/ImageTest.php b/tests/Drivers/Gd/ImageTest.php index 927450fd..07cc4233 100644 --- a/tests/Drivers/Gd/ImageTest.php +++ b/tests/Drivers/Gd/ImageTest.php @@ -92,33 +92,33 @@ class ImageTest extends TestCase $this->assertInstanceOf(Rectangle::class, $this->image->getSize()); } - public function testPickColor(): void + public function testGetColor(): void { - $color = $this->image->pickColor(0, 0); + $color = $this->image->getColor(0, 0); $this->assertInstanceOf(Color::class, $color); $this->assertEquals(255, $color->red()); $this->assertEquals(0, $color->green()); $this->assertEquals(0, $color->blue()); - $color = $this->image->pickColor(0, 0, 1); + $color = $this->image->getColor(0, 0, 1); $this->assertInstanceOf(Color::class, $color); $this->assertEquals(0, $color->red()); $this->assertEquals(255, $color->green()); $this->assertEquals(0, $color->blue()); - $color = $this->image->pickColor(0, 0, 2); + $color = $this->image->getColor(0, 0, 2); $this->assertInstanceOf(Color::class, $color); $this->assertEquals(0, $color->red()); $this->assertEquals(0, $color->green()); $this->assertEquals(255, $color->blue()); - $color = $this->image->pickColor(0, 0, 3); + $color = $this->image->getColor(0, 0, 3); $this->assertNull($color); } - public function testPickColors(): void + public function testGetColors(): void { - $colors = $this->image->pickColors(0, 0); + $colors = $this->image->getColors(0, 0); $this->assertInstanceOf(Collection::class, $colors); $this->assertCount(3, $colors); diff --git a/tests/Drivers/Gd/Modifiers/BlurModifierTest.php b/tests/Drivers/Gd/Modifiers/BlurModifierTest.php index 5fa2328a..237868c6 100644 --- a/tests/Drivers/Gd/Modifiers/BlurModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/BlurModifierTest.php @@ -17,8 +17,8 @@ class BlurModifierTest extends TestCase public function testColorChange(): void { $image = $this->createTestImage('trim.png'); - $this->assertEquals('00aef0', $image->pickColor(14, 14)->toHex()); + $this->assertEquals('00aef0', $image->getColor(14, 14)->toHex()); $image->modify(new BlurModifier(30)); - $this->assertEquals('4fa68d', $image->pickColor(14, 14)->toHex()); + $this->assertEquals('4fa68d', $image->getColor(14, 14)->toHex()); } } diff --git a/tests/Drivers/Gd/Modifiers/BrightnessModifierTest.php b/tests/Drivers/Gd/Modifiers/BrightnessModifierTest.php index 24312978..3879a005 100644 --- a/tests/Drivers/Gd/Modifiers/BrightnessModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/BrightnessModifierTest.php @@ -17,8 +17,8 @@ class BrightnessModifierTest extends TestCase public function testApply(): void { $image = $this->createTestImage('trim.png'); - $this->assertEquals('00aef0', $image->pickColor(14, 14)->toHex()); + $this->assertEquals('00aef0', $image->getColor(14, 14)->toHex()); $image->modify(new BrightnessModifier(30)); - $this->assertEquals('4cfaff', $image->pickColor(14, 14)->toHex()); + $this->assertEquals('4cfaff', $image->getColor(14, 14)->toHex()); } } diff --git a/tests/Drivers/Gd/Modifiers/ContrastModifierTest.php b/tests/Drivers/Gd/Modifiers/ContrastModifierTest.php index 66afbf57..74059c1b 100644 --- a/tests/Drivers/Gd/Modifiers/ContrastModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/ContrastModifierTest.php @@ -17,8 +17,8 @@ class ContrastModifierTest extends TestCase public function testApply(): void { $image = $this->createTestImage('trim.png'); - $this->assertEquals('00aef0', $image->pickColor(14, 14)->toHex()); + $this->assertEquals('00aef0', $image->getColor(14, 14)->toHex()); $image->modify(new ContrastModifier(30)); - $this->assertEquals('00ceff', $image->pickColor(14, 14)->toHex()); + $this->assertEquals('00ceff', $image->getColor(14, 14)->toHex()); } } diff --git a/tests/Drivers/Gd/Modifiers/DrawEllipseModiferTest.php b/tests/Drivers/Gd/Modifiers/DrawEllipseModiferTest.php index 1c95cd3c..a9dfa0bd 100644 --- a/tests/Drivers/Gd/Modifiers/DrawEllipseModiferTest.php +++ b/tests/Drivers/Gd/Modifiers/DrawEllipseModiferTest.php @@ -19,10 +19,10 @@ class DrawEllipseModifierTest extends TestCase public function testApply(): void { $image = $this->createTestImage('trim.png'); - $this->assertEquals('00aef0', $image->pickColor(14, 14)->toHex()); + $this->assertEquals('00aef0', $image->getColor(14, 14)->toHex()); $drawable = new Ellipse(10, 10); $drawable->background('b53717'); $image->modify(new DrawEllipseModifier(new Point(14, 14), $drawable)); - $this->assertEquals('b53717', $image->pickColor(14, 14)->toHex()); + $this->assertEquals('b53717', $image->getColor(14, 14)->toHex()); } } diff --git a/tests/Drivers/Gd/Modifiers/DrawLineModiferTest.php b/tests/Drivers/Gd/Modifiers/DrawLineModiferTest.php index e726f311..392db16f 100644 --- a/tests/Drivers/Gd/Modifiers/DrawLineModiferTest.php +++ b/tests/Drivers/Gd/Modifiers/DrawLineModiferTest.php @@ -19,10 +19,10 @@ class DrawLineModifierTest extends TestCase public function testApply(): void { $image = $this->createTestImage('trim.png'); - $this->assertEquals('00aef0', $image->pickColor(14, 14)->toHex()); + $this->assertEquals('00aef0', $image->getColor(14, 14)->toHex()); $line = new Line(new Point(0, 0), new Point(10, 0), 4); $line->background('b53517'); $image->modify(new DrawLineModifier(new Point(0, 0), $line)); - $this->assertEquals('b53517', $image->pickColor(0, 0)->toHex()); + $this->assertEquals('b53517', $image->getColor(0, 0)->toHex()); } } diff --git a/tests/Drivers/Gd/Modifiers/DrawPixelModifierTest.php b/tests/Drivers/Gd/Modifiers/DrawPixelModifierTest.php index e6ef8f5d..04c44096 100644 --- a/tests/Drivers/Gd/Modifiers/DrawPixelModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/DrawPixelModifierTest.php @@ -18,8 +18,8 @@ class DrawPixelModifierTest extends TestCase public function testApply(): void { $image = $this->createTestImage('trim.png'); - $this->assertEquals('00aef0', $image->pickColor(14, 14)->toHex()); + $this->assertEquals('00aef0', $image->getColor(14, 14)->toHex()); $image->modify(new DrawPixelModifier(new Point(14, 14), 'ffffff')); - $this->assertEquals('ffffff', $image->pickColor(14, 14)->toHex()); + $this->assertEquals('ffffff', $image->getColor(14, 14)->toHex()); } } diff --git a/tests/Drivers/Gd/Modifiers/DrawPolygonModifierTest.php b/tests/Drivers/Gd/Modifiers/DrawPolygonModifierTest.php index 1b62a24a..eaf7663a 100644 --- a/tests/Drivers/Gd/Modifiers/DrawPolygonModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/DrawPolygonModifierTest.php @@ -19,10 +19,10 @@ class DrawPolygonModifierTest extends TestCase public function testApply(): void { $image = $this->createTestImage('trim.png'); - $this->assertEquals('00aef0', $image->pickColor(14, 14)->toHex()); + $this->assertEquals('00aef0', $image->getColor(14, 14)->toHex()); $drawable = new Polygon([new Point(0, 0), new Point(15, 15), new Point(20, 20)]); $drawable->background('b53717'); $image->modify(new DrawPolygonModifier($drawable)); - $this->assertEquals('b53717', $image->pickColor(14, 14)->toHex()); + $this->assertEquals('b53717', $image->getColor(14, 14)->toHex()); } } diff --git a/tests/Drivers/Gd/Modifiers/DrawRectangleModifierTest.php b/tests/Drivers/Gd/Modifiers/DrawRectangleModifierTest.php index 2368c6ee..f5b2d1b6 100644 --- a/tests/Drivers/Gd/Modifiers/DrawRectangleModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/DrawRectangleModifierTest.php @@ -19,10 +19,10 @@ class DrawRectangleModifierTest extends TestCase public function testApply(): void { $image = $this->createTestImage('trim.png'); - $this->assertEquals('00aef0', $image->pickColor(14, 14)->toHex()); + $this->assertEquals('00aef0', $image->getColor(14, 14)->toHex()); $rectangle = new Rectangle(300, 200); $rectangle->background('ffffff'); $image->modify(new DrawRectangleModifier(new Point(14, 14), $rectangle)); - $this->assertEquals('ffffff', $image->pickColor(14, 14)->toHex()); + $this->assertEquals('ffffff', $image->getColor(14, 14)->toHex()); } } diff --git a/tests/Drivers/Gd/Modifiers/FillModifierTest.php b/tests/Drivers/Gd/Modifiers/FillModifierTest.php index 380b746a..f2263ad9 100644 --- a/tests/Drivers/Gd/Modifiers/FillModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/FillModifierTest.php @@ -19,20 +19,20 @@ class FillModifierTest extends TestCase public function testFloodFillColor(): void { $image = $this->createTestImage('blocks.png'); - $this->assertEquals('0000ff', $image->pickColor(420, 270)->toHex()); - $this->assertEquals('ff0000', $image->pickColor(540, 400)->toHex()); + $this->assertEquals('0000ff', $image->getColor(420, 270)->toHex()); + $this->assertEquals('ff0000', $image->getColor(540, 400)->toHex()); $image->modify(new FillModifier(new Color(13421772), new Point(540, 400))); - $this->assertEquals('0000ff', $image->pickColor(420, 270)->toHex()); - $this->assertEquals('cccccc', $image->pickColor(540, 400)->toHex()); + $this->assertEquals('0000ff', $image->getColor(420, 270)->toHex()); + $this->assertEquals('cccccc', $image->getColor(540, 400)->toHex()); } public function testFillAllColor(): void { $image = $this->createTestImage('blocks.png'); - $this->assertEquals('0000ff', $image->pickColor(420, 270)->toHex()); - $this->assertEquals('ff0000', $image->pickColor(540, 400)->toHex()); + $this->assertEquals('0000ff', $image->getColor(420, 270)->toHex()); + $this->assertEquals('ff0000', $image->getColor(540, 400)->toHex()); $image->modify(new FillModifier(new Color(13421772))); - $this->assertEquals('cccccc', $image->pickColor(420, 270)->toHex()); - $this->assertEquals('cccccc', $image->pickColor(540, 400)->toHex()); + $this->assertEquals('cccccc', $image->getColor(420, 270)->toHex()); + $this->assertEquals('cccccc', $image->getColor(540, 400)->toHex()); } } diff --git a/tests/Drivers/Gd/Modifiers/FitModifierTest.php b/tests/Drivers/Gd/Modifiers/FitModifierTest.php index 58bd8094..b6e02fba 100644 --- a/tests/Drivers/Gd/Modifiers/FitModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/FitModifierTest.php @@ -22,9 +22,9 @@ class FitModifierTest extends TestCase $image->modify(new FitModifier(100, 100, 'center')); $this->assertEquals(100, $image->getWidth()); $this->assertEquals(100, $image->getHeight()); - $this->assertColor(255, 0, 0, 1, $image->pickColor(90, 90)); - $this->assertColor(0, 255, 0, 1, $image->pickColor(65, 70)); - $this->assertColor(0, 0, 255, 1, $image->pickColor(70, 52)); - $this->assertTransparency($image->pickColor(90, 30)); + $this->assertColor(255, 0, 0, 1, $image->getColor(90, 90)); + $this->assertColor(0, 255, 0, 1, $image->getColor(65, 70)); + $this->assertColor(0, 0, 255, 1, $image->getColor(70, 52)); + $this->assertTransparency($image->getColor(90, 30)); } } diff --git a/tests/Drivers/Gd/Modifiers/FlipFlopModifierTest.php b/tests/Drivers/Gd/Modifiers/FlipFlopModifierTest.php index 0305f6a8..c4a34136 100644 --- a/tests/Drivers/Gd/Modifiers/FlipFlopModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/FlipFlopModifierTest.php @@ -19,16 +19,16 @@ class FlipFlopModifierTest extends TestCase public function testFlipImage(): void { $image = $this->createTestImage('tile.png'); - $this->assertEquals('b4e000', $image->pickColor(0, 0)->toHex()); + $this->assertEquals('b4e000', $image->getColor(0, 0)->toHex()); $image->modify(new FlipModifier()); - $this->assertEquals('000000', $image->pickColor(0, 0)->toHex()); + $this->assertEquals('000000', $image->getColor(0, 0)->toHex()); } public function testFlopImage(): void { $image = $this->createTestImage('tile.png'); - $this->assertEquals('b4e000', $image->pickColor(0, 0)->toHex()); + $this->assertEquals('b4e000', $image->getColor(0, 0)->toHex()); $image->modify(new FlopModifier()); - $this->assertEquals('000000', $image->pickColor(0, 0)->toHex()); + $this->assertEquals('000000', $image->getColor(0, 0)->toHex()); } } diff --git a/tests/Drivers/Gd/Modifiers/GammaModifierTest.php b/tests/Drivers/Gd/Modifiers/GammaModifierTest.php index dec4de93..96fbb193 100644 --- a/tests/Drivers/Gd/Modifiers/GammaModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/GammaModifierTest.php @@ -17,8 +17,8 @@ class GammaModifierTest extends TestCase public function testModifier(): void { $image = $this->createTestImage('trim.png'); - $this->assertEquals('00aef0', $image->pickColor(0, 0)->toHex()); + $this->assertEquals('00aef0', $image->getColor(0, 0)->toHex()); $image->modify(new GammaModifier(2.1)); - $this->assertEquals('00d5f8', $image->pickColor(0, 0)->toHex()); + $this->assertEquals('00d5f8', $image->getColor(0, 0)->toHex()); } } diff --git a/tests/Drivers/Gd/Modifiers/GreyscaleModifierTest.php b/tests/Drivers/Gd/Modifiers/GreyscaleModifierTest.php index 6a9b6380..c11f2225 100644 --- a/tests/Drivers/Gd/Modifiers/GreyscaleModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/GreyscaleModifierTest.php @@ -17,8 +17,8 @@ class GreyscaleModifierTest extends TestCase public function testColorChange(): void { $image = $this->createTestImage('trim.png'); - $this->assertFalse($image->pickColor(0, 0)->isGreyscale()); + $this->assertFalse($image->getColor(0, 0)->isGreyscale()); $image->modify(new GreyscaleModifier()); - $this->assertTrue($image->pickColor(0, 0)->isGreyscale()); + $this->assertTrue($image->getColor(0, 0)->isGreyscale()); } } diff --git a/tests/Drivers/Gd/Modifiers/InvertModifierTest.php b/tests/Drivers/Gd/Modifiers/InvertModifierTest.php index 82a81b59..35ab7ae1 100644 --- a/tests/Drivers/Gd/Modifiers/InvertModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/InvertModifierTest.php @@ -17,10 +17,10 @@ class InvertModifierTest extends TestCase public function testApply(): void { $image = $this->createTestImage('trim.png'); - $this->assertEquals('00aef0', $image->pickColor(0, 0)->toHex()); - $this->assertEquals('ffa601', $image->pickColor(25, 25)->toHex()); + $this->assertEquals('00aef0', $image->getColor(0, 0)->toHex()); + $this->assertEquals('ffa601', $image->getColor(25, 25)->toHex()); $image->modify(new InvertModifier()); - $this->assertEquals('ff510f', $image->pickColor(0, 0)->toHex()); - $this->assertEquals('0059fe', $image->pickColor(25, 25)->toHex()); + $this->assertEquals('ff510f', $image->getColor(0, 0)->toHex()); + $this->assertEquals('0059fe', $image->getColor(25, 25)->toHex()); } } diff --git a/tests/Drivers/Gd/Modifiers/PixelateModifierTest.php b/tests/Drivers/Gd/Modifiers/PixelateModifierTest.php index 427a8080..04dba07b 100644 --- a/tests/Drivers/Gd/Modifiers/PixelateModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/PixelateModifierTest.php @@ -17,10 +17,10 @@ class PixelateModifierTest extends TestCase public function testModify(): void { $image = $this->createTestImage('trim.png'); - $this->assertEquals('00aef0', $image->pickColor(0, 0)->toHex()); - $this->assertEquals('00aef0', $image->pickColor(14, 14)->toHex()); + $this->assertEquals('00aef0', $image->getColor(0, 0)->toHex()); + $this->assertEquals('00aef0', $image->getColor(14, 14)->toHex()); $image->modify(new PixelateModifier(10)); - $this->assertEquals('00aef0', $image->pickColor(0, 0)->toHex()); - $this->assertEquals('6aaa8b', $image->pickColor(14, 14)->toHex()); + $this->assertEquals('00aef0', $image->getColor(0, 0)->toHex()); + $this->assertEquals('6aaa8b', $image->getColor(14, 14)->toHex()); } } diff --git a/tests/Drivers/Gd/Modifiers/PlaceModifierTest.php b/tests/Drivers/Gd/Modifiers/PlaceModifierTest.php index 3f3db1e0..c3ddd2d2 100644 --- a/tests/Drivers/Gd/Modifiers/PlaceModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/PlaceModifierTest.php @@ -17,8 +17,8 @@ class PlaceModifierTest extends TestCase public function testColorChange(): void { $image = $this->createTestImage('test.jpg'); - $this->assertEquals('febc44', $image->pickColor(300, 25)->toHex()); + $this->assertEquals('febc44', $image->getColor(300, 25)->toHex()); $image->modify(new PlaceModifier(__DIR__ . '/../../../images/circle.png', 'top-right', 0, 0)); - $this->assertEquals('32250d', $image->pickColor(300, 25)->toHex()); + $this->assertEquals('32250d', $image->getColor(300, 25)->toHex()); } } diff --git a/tests/Drivers/Gd/Modifiers/ResizeModifierTest.php b/tests/Drivers/Gd/Modifiers/ResizeModifierTest.php index bf7ef234..ed2ebf2b 100644 --- a/tests/Drivers/Gd/Modifiers/ResizeModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/ResizeModifierTest.php @@ -22,10 +22,10 @@ class ResizeModifierTest extends TestCase $image->modify(new ResizeModifier(200, 100)); $this->assertEquals(200, $image->getWidth()); $this->assertEquals(100, $image->getHeight()); - $this->assertColor(255, 0, 0, 1, $image->pickColor(150, 70)); - $this->assertColor(0, 255, 0, 1, $image->pickColor(125, 70)); - $this->assertColor(0, 0, 255, 1, $image->pickColor(130, 54)); - $transparent = $image->pickColor(170, 30); + $this->assertColor(255, 0, 0, 1, $image->getColor(150, 70)); + $this->assertColor(0, 255, 0, 1, $image->getColor(125, 70)); + $this->assertColor(0, 0, 255, 1, $image->getColor(130, 54)); + $transparent = $image->getColor(170, 30); $this->assertEquals(2130706432, $transparent->toInt()); $this->assertTransparency($transparent); } diff --git a/tests/Drivers/Gd/Modifiers/SharpenModifierTest.php b/tests/Drivers/Gd/Modifiers/SharpenModifierTest.php index 5646bae1..5a7c9180 100644 --- a/tests/Drivers/Gd/Modifiers/SharpenModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/SharpenModifierTest.php @@ -17,8 +17,8 @@ class SharpenModifierTest extends TestCase public function testModify(): void { $image = $this->createTestImage('trim.png'); - $this->assertEquals('60ab96', $image->pickColor(15, 14)->toHex()); + $this->assertEquals('60ab96', $image->getColor(15, 14)->toHex()); $image->modify(new SharpenModifier(10)); - $this->assertEquals('4daba7', $image->pickColor(15, 14)->toHex()); + $this->assertEquals('4daba7', $image->getColor(15, 14)->toHex()); } } diff --git a/tests/Drivers/Imagick/Modifiers/BlurModifierTest.php b/tests/Drivers/Imagick/Modifiers/BlurModifierTest.php index 485b8e51..1d912330 100644 --- a/tests/Drivers/Imagick/Modifiers/BlurModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/BlurModifierTest.php @@ -17,8 +17,8 @@ class BlurModifierTest extends TestCase public function testColorChange(): void { $image = $this->createTestImage('trim.png'); - $this->assertEquals('00aef0', $image->pickColor(14, 14)->toHex()); + $this->assertEquals('00aef0', $image->getColor(14, 14)->toHex()); $image->modify(new BlurModifier(30)); - $this->assertEquals('42acb2', $image->pickColor(14, 14)->toHex()); + $this->assertEquals('42acb2', $image->getColor(14, 14)->toHex()); } } diff --git a/tests/Drivers/Imagick/Modifiers/BrightnessModifierTest.php b/tests/Drivers/Imagick/Modifiers/BrightnessModifierTest.php index 8cb0b3db..b1da2677 100644 --- a/tests/Drivers/Imagick/Modifiers/BrightnessModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/BrightnessModifierTest.php @@ -17,8 +17,8 @@ class BrightnessModifierTest extends TestCase public function testApply(): void { $image = $this->createTestImage('trim.png'); - $this->assertEquals('00aef0', $image->pickColor(14, 14)->toHex()); + $this->assertEquals('00aef0', $image->getColor(14, 14)->toHex()); $image->modify(new BrightnessModifier(30)); - $this->assertEquals('39c9ff', $image->pickColor(14, 14)->toHex()); + $this->assertEquals('39c9ff', $image->getColor(14, 14)->toHex()); } } diff --git a/tests/Drivers/Imagick/Modifiers/ContrastModifierTest.php b/tests/Drivers/Imagick/Modifiers/ContrastModifierTest.php index c5232a4c..e22a36c0 100644 --- a/tests/Drivers/Imagick/Modifiers/ContrastModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/ContrastModifierTest.php @@ -17,8 +17,8 @@ class ContrastModifierTest extends TestCase public function testApply(): void { $image = $this->createTestImage('trim.png'); - $this->assertEquals('00aef0', $image->pickColor(14, 14)->toHex()); + $this->assertEquals('00aef0', $image->getColor(14, 14)->toHex()); $image->modify(new ContrastModifier(30)); - $this->assertEquals('00fcff', $image->pickColor(14, 14)->toHex()); + $this->assertEquals('00fcff', $image->getColor(14, 14)->toHex()); } } diff --git a/tests/Drivers/Imagick/Modifiers/DrawEllipseModifierTest.php b/tests/Drivers/Imagick/Modifiers/DrawEllipseModifierTest.php index a2bed0d8..b12be356 100644 --- a/tests/Drivers/Imagick/Modifiers/DrawEllipseModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/DrawEllipseModifierTest.php @@ -15,10 +15,10 @@ class DrawEllipseModifierTest extends TestCase public function testApply(): void { $image = $this->createTestImage('trim.png'); - $this->assertEquals('00aef0', $image->pickColor(14, 14)->toHex()); + $this->assertEquals('00aef0', $image->getColor(14, 14)->toHex()); $drawable = new Ellipse(10, 10); $drawable->background('b53717'); $image->modify(new DrawEllipseModifier(new Point(14, 14), $drawable)); - $this->assertEquals('b53717', $image->pickColor(14, 14)->toHex()); + $this->assertEquals('b53717', $image->getColor(14, 14)->toHex()); } } diff --git a/tests/Drivers/Imagick/Modifiers/DrawLineModifierTest.php b/tests/Drivers/Imagick/Modifiers/DrawLineModifierTest.php index 423e27da..9602ab54 100644 --- a/tests/Drivers/Imagick/Modifiers/DrawLineModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/DrawLineModifierTest.php @@ -15,10 +15,10 @@ class DrawLineModifierTest extends TestCase public function testApply(): void { $image = $this->createTestImage('trim.png'); - $this->assertEquals('00aef0', $image->pickColor(14, 14)->toHex()); + $this->assertEquals('00aef0', $image->getColor(14, 14)->toHex()); $line = new Line(new Point(0, 0), new Point(10, 0), 4); $line->background('b53517'); $image->modify(new DrawLineModifier(new Point(0, 0), $line)); - $this->assertEquals('b53517', $image->pickColor(0, 0)->toHex()); + $this->assertEquals('b53517', $image->getColor(0, 0)->toHex()); } } diff --git a/tests/Drivers/Imagick/Modifiers/DrawPixelModifierTest.php b/tests/Drivers/Imagick/Modifiers/DrawPixelModifierTest.php index 90c076d7..ac5d03df 100644 --- a/tests/Drivers/Imagick/Modifiers/DrawPixelModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/DrawPixelModifierTest.php @@ -18,8 +18,8 @@ class DrawPixelModifierTest extends TestCase public function testApply(): void { $image = $this->createTestImage('trim.png'); - $this->assertEquals('00aef0', $image->pickColor(14, 14)->toHex()); + $this->assertEquals('00aef0', $image->getColor(14, 14)->toHex()); $image->modify(new DrawPixelModifier(new Point(14, 14), 'ffffff')); - $this->assertEquals('ffffff', $image->pickColor(14, 14)->toHex()); + $this->assertEquals('ffffff', $image->getColor(14, 14)->toHex()); } } diff --git a/tests/Drivers/Imagick/Modifiers/DrawPolygonModifierTest.php b/tests/Drivers/Imagick/Modifiers/DrawPolygonModifierTest.php index 02ee6b2b..99796e58 100644 --- a/tests/Drivers/Imagick/Modifiers/DrawPolygonModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/DrawPolygonModifierTest.php @@ -15,10 +15,10 @@ class DrawPolygonModifierTest extends TestCase public function testApply(): void { $image = $this->createTestImage('trim.png'); - $this->assertEquals('00aef0', $image->pickColor(14, 14)->toHex()); + $this->assertEquals('00aef0', $image->getColor(14, 14)->toHex()); $drawable = new Polygon([new Point(0, 0), new Point(15, 15), new Point(20, 20)]); $drawable->background('b53717'); $image->modify(new DrawPolygonModifier($drawable)); - $this->assertEquals('b53717', $image->pickColor(14, 14)->toHex()); + $this->assertEquals('b53717', $image->getColor(14, 14)->toHex()); } } diff --git a/tests/Drivers/Imagick/Modifiers/DrawRectangleModifierTest.php b/tests/Drivers/Imagick/Modifiers/DrawRectangleModifierTest.php index 876397f8..99928e81 100644 --- a/tests/Drivers/Imagick/Modifiers/DrawRectangleModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/DrawRectangleModifierTest.php @@ -15,10 +15,10 @@ class DrawRectangleModifierTest extends TestCase public function testApply(): void { $image = $this->createTestImage('trim.png'); - $this->assertEquals('00aef0', $image->pickColor(14, 14)->toHex()); + $this->assertEquals('00aef0', $image->getColor(14, 14)->toHex()); $rectangle = new Rectangle(300, 200); $rectangle->background('ffffff'); $image->modify(new DrawRectangleModifier(new Point(14, 14), $rectangle)); - $this->assertEquals('ffffff', $image->pickColor(14, 14)->toHex()); + $this->assertEquals('ffffff', $image->getColor(14, 14)->toHex()); } } diff --git a/tests/Drivers/Imagick/Modifiers/FillModifierTest.php b/tests/Drivers/Imagick/Modifiers/FillModifierTest.php index 37d9b9d7..bbb53ead 100644 --- a/tests/Drivers/Imagick/Modifiers/FillModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/FillModifierTest.php @@ -20,20 +20,20 @@ class FillModifierTest extends TestCase public function testFloodFillColor(): void { $image = $this->createTestImage('blocks.png'); - $this->assertEquals('0000ff', $image->pickColor(420, 270)->toHex()); - $this->assertEquals('ff0000', $image->pickColor(540, 400)->toHex()); + $this->assertEquals('0000ff', $image->getColor(420, 270)->toHex()); + $this->assertEquals('ff0000', $image->getColor(540, 400)->toHex()); $image->modify(new FillModifier(new Color(new ImagickPixel('#cccccc')), new Point(540, 400))); - $this->assertEquals('0000ff', $image->pickColor(420, 270)->toHex()); - $this->assertEquals('cccccc', $image->pickColor(540, 400)->toHex()); + $this->assertEquals('0000ff', $image->getColor(420, 270)->toHex()); + $this->assertEquals('cccccc', $image->getColor(540, 400)->toHex()); } public function testFillAllColor(): void { $image = $this->createTestImage('blocks.png'); - $this->assertEquals('0000ff', $image->pickColor(420, 270)->toHex()); - $this->assertEquals('ff0000', $image->pickColor(540, 400)->toHex()); + $this->assertEquals('0000ff', $image->getColor(420, 270)->toHex()); + $this->assertEquals('ff0000', $image->getColor(540, 400)->toHex()); $image->modify(new FillModifier(new Color(new ImagickPixel('#cccccc')))); - $this->assertEquals('cccccc', $image->pickColor(420, 270)->toHex()); - $this->assertEquals('cccccc', $image->pickColor(540, 400)->toHex()); + $this->assertEquals('cccccc', $image->getColor(420, 270)->toHex()); + $this->assertEquals('cccccc', $image->getColor(540, 400)->toHex()); } } diff --git a/tests/Drivers/Imagick/Modifiers/FitModifierTest.php b/tests/Drivers/Imagick/Modifiers/FitModifierTest.php index 783e8e49..76acee6d 100644 --- a/tests/Drivers/Imagick/Modifiers/FitModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/FitModifierTest.php @@ -22,9 +22,9 @@ class FitModifierTest extends TestCase $image->modify(new FitModifier(100, 100, 'center')); $this->assertEquals(100, $image->getWidth()); $this->assertEquals(100, $image->getHeight()); - $this->assertColor(255, 0, 0, 1, $image->pickColor(90, 90)); - $this->assertColor(0, 255, 0, 1, $image->pickColor(65, 70)); - $this->assertColor(0, 0, 255, 1, $image->pickColor(70, 52)); - $this->assertTransparency($image->pickColor(90, 30)); + $this->assertColor(255, 0, 0, 1, $image->getColor(90, 90)); + $this->assertColor(0, 255, 0, 1, $image->getColor(65, 70)); + $this->assertColor(0, 0, 255, 1, $image->getColor(70, 52)); + $this->assertTransparency($image->getColor(90, 30)); } } diff --git a/tests/Drivers/Imagick/Modifiers/FlipFlopModifierTest.php b/tests/Drivers/Imagick/Modifiers/FlipFlopModifierTest.php index 10ecab69..b6957cca 100644 --- a/tests/Drivers/Imagick/Modifiers/FlipFlopModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/FlipFlopModifierTest.php @@ -19,16 +19,16 @@ class FlipFlopModifierTest extends TestCase public function testFlipImage(): void { $image = $this->createTestImage('tile.png'); - $this->assertEquals('b4e000', $image->pickColor(0, 0)->toHex()); + $this->assertEquals('b4e000', $image->getColor(0, 0)->toHex()); $image->modify(new FlipModifier()); - $this->assertEquals('000000', $image->pickColor(0, 0)->toHex()); + $this->assertEquals('000000', $image->getColor(0, 0)->toHex()); } public function testFlopImage(): void { $image = $this->createTestImage('tile.png'); - $this->assertEquals('b4e000', $image->pickColor(0, 0)->toHex()); + $this->assertEquals('b4e000', $image->getColor(0, 0)->toHex()); $image->modify(new FlopModifier()); - $this->assertEquals('000000', $image->pickColor(0, 0)->toHex()); + $this->assertEquals('000000', $image->getColor(0, 0)->toHex()); } } diff --git a/tests/Drivers/Imagick/Modifiers/GammaModifierTest.php b/tests/Drivers/Imagick/Modifiers/GammaModifierTest.php index c3af78c0..3e77e9f6 100644 --- a/tests/Drivers/Imagick/Modifiers/GammaModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/GammaModifierTest.php @@ -17,8 +17,8 @@ class GammaModifierTest extends TestCase public function testModifier(): void { $image = $this->createTestImage('trim.png'); - $this->assertEquals('00aef0', $image->pickColor(0, 0)->toHex()); + $this->assertEquals('00aef0', $image->getColor(0, 0)->toHex()); $image->modify(new GammaModifier(2.1)); - $this->assertEquals('00d5f8', $image->pickColor(0, 0)->toHex()); + $this->assertEquals('00d5f8', $image->getColor(0, 0)->toHex()); } } diff --git a/tests/Drivers/Imagick/Modifiers/GreyscaleModifierTest.php b/tests/Drivers/Imagick/Modifiers/GreyscaleModifierTest.php index 42257fad..bd31349b 100644 --- a/tests/Drivers/Imagick/Modifiers/GreyscaleModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/GreyscaleModifierTest.php @@ -17,8 +17,8 @@ class GreyscaleModifierTest extends TestCase public function testColorChange(): void { $image = $this->createTestImage('trim.png'); - $this->assertFalse($image->pickColor(0, 0)->isGreyscale()); + $this->assertFalse($image->getColor(0, 0)->isGreyscale()); $image->modify(new GreyscaleModifier()); - $this->assertTrue($image->pickColor(0, 0)->isGreyscale()); + $this->assertTrue($image->getColor(0, 0)->isGreyscale()); } } diff --git a/tests/Drivers/Imagick/Modifiers/InvertModifierTest.php b/tests/Drivers/Imagick/Modifiers/InvertModifierTest.php index 2c3779cd..0768d24b 100644 --- a/tests/Drivers/Imagick/Modifiers/InvertModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/InvertModifierTest.php @@ -17,10 +17,10 @@ class InvertModifierTest extends TestCase public function testApply(): void { $image = $this->createTestImage('trim.png'); - $this->assertEquals('00aef0', $image->pickColor(0, 0)->toHex()); - $this->assertEquals('ffa601', $image->pickColor(25, 25)->toHex()); + $this->assertEquals('00aef0', $image->getColor(0, 0)->toHex()); + $this->assertEquals('ffa601', $image->getColor(25, 25)->toHex()); $image->modify(new InvertModifier()); - $this->assertEquals('ff510f', $image->pickColor(0, 0)->toHex()); - $this->assertEquals('0059fe', $image->pickColor(25, 25)->toHex()); + $this->assertEquals('ff510f', $image->getColor(0, 0)->toHex()); + $this->assertEquals('0059fe', $image->getColor(25, 25)->toHex()); } } diff --git a/tests/Drivers/Imagick/Modifiers/PixelateModifierTest.php b/tests/Drivers/Imagick/Modifiers/PixelateModifierTest.php index 9329b830..cbaa1239 100644 --- a/tests/Drivers/Imagick/Modifiers/PixelateModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/PixelateModifierTest.php @@ -17,10 +17,10 @@ class PixelateModifierTest extends TestCase public function testModify(): void { $image = $this->createTestImage('trim.png'); - $this->assertEquals('00aef0', $image->pickColor(0, 0)->toHex()); - $this->assertEquals('00aef0', $image->pickColor(14, 14)->toHex()); + $this->assertEquals('00aef0', $image->getColor(0, 0)->toHex()); + $this->assertEquals('00aef0', $image->getColor(14, 14)->toHex()); $image->modify(new PixelateModifier(10)); - $this->assertEquals('00aef0', $image->pickColor(0, 0)->toHex()); - $this->assertEquals('6bab8c', $image->pickColor(14, 14)->toHex()); + $this->assertEquals('00aef0', $image->getColor(0, 0)->toHex()); + $this->assertEquals('6bab8c', $image->getColor(14, 14)->toHex()); } } diff --git a/tests/Drivers/Imagick/Modifiers/PlaceModifierTest.php b/tests/Drivers/Imagick/Modifiers/PlaceModifierTest.php index 15a3af83..92a22ab2 100644 --- a/tests/Drivers/Imagick/Modifiers/PlaceModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/PlaceModifierTest.php @@ -17,8 +17,8 @@ class PlaceModifierTest extends TestCase public function testColorChange(): void { $image = $this->createTestImage('test.jpg'); - $this->assertEquals('febc44', $image->pickColor(300, 25)->toHex()); + $this->assertEquals('febc44', $image->getColor(300, 25)->toHex()); $image->modify(new PlaceModifier(__DIR__ . '/../../../images/circle.png', 'top-right', 0, 0)); - $this->assertEquals('33260e', $image->pickColor(300, 25)->toHex()); + $this->assertEquals('33260e', $image->getColor(300, 25)->toHex()); } } diff --git a/tests/Drivers/Imagick/Modifiers/ResizeModifierTest.php b/tests/Drivers/Imagick/Modifiers/ResizeModifierTest.php index 519e69f1..b112cedb 100644 --- a/tests/Drivers/Imagick/Modifiers/ResizeModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/ResizeModifierTest.php @@ -22,9 +22,9 @@ class ResizeModifierTest extends TestCase $image->modify(new ResizeModifier(200, 100)); $this->assertEquals(200, $image->getWidth()); $this->assertEquals(100, $image->getHeight()); - $this->assertColor(255, 0, 0, 1, $image->pickColor(150, 70)); - $this->assertColor(0, 255, 0, 1, $image->pickColor(125, 70)); - $this->assertColor(0, 0, 255, 1, $image->pickColor(130, 54)); - $this->assertTransparency($image->pickColor(150, 45)); + $this->assertColor(255, 0, 0, 1, $image->getColor(150, 70)); + $this->assertColor(0, 255, 0, 1, $image->getColor(125, 70)); + $this->assertColor(0, 0, 255, 1, $image->getColor(130, 54)); + $this->assertTransparency($image->getColor(150, 45)); } } diff --git a/tests/Drivers/Imagick/Modifiers/SharpenModifierTest.php b/tests/Drivers/Imagick/Modifiers/SharpenModifierTest.php index 2b80fd4e..92063093 100644 --- a/tests/Drivers/Imagick/Modifiers/SharpenModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/SharpenModifierTest.php @@ -17,8 +17,8 @@ class SharpenModifierTest extends TestCase public function testModify(): void { $image = $this->createTestImage('trim.png'); - $this->assertEquals('60ab96', $image->pickColor(15, 14)->toHex()); + $this->assertEquals('60ab96', $image->getColor(15, 14)->toHex()); $image->modify(new SharpenModifier(10)); - $this->assertEquals('4faca6', $image->pickColor(15, 14)->toHex()); + $this->assertEquals('4faca6', $image->getColor(15, 14)->toHex()); } } From 4ff9abd3a0f7cd66ba91853bebd43ae48f1a8a94 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 7 Oct 2023 09:38:48 +0200 Subject: [PATCH 274/476] Add missing method to ColorInterface --- src/Interfaces/ColorInterface.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Interfaces/ColorInterface.php b/src/Interfaces/ColorInterface.php index 07ee8e91..2662258d 100644 --- a/src/Interfaces/ColorInterface.php +++ b/src/Interfaces/ColorInterface.php @@ -11,4 +11,5 @@ interface ColorInterface public function toArray(): array; public function toHex(string $prefix = ''): string; public function toInt(): int; + public function isGreyscale(): bool; } From d9f4c2440a5a4c302b144ea24534c4602cfd1a99 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 7 Oct 2023 10:25:41 +0200 Subject: [PATCH 275/476] Revert "Rename methods of ImageInterface" This reverts commit ba01e87883b18a8da728db8bcab8f50429315ede. --- src/Drivers/Abstract/AbstractImage.php | 4 ++-- src/Drivers/Gd/Image.php | 2 +- src/Drivers/Imagick/Image.php | 2 +- src/Interfaces/ImageInterface.php | 23 +++---------------- tests/Drivers/Abstract/AbstractImageTest.php | 6 ++--- tests/Drivers/Gd/ImageTest.php | 14 +++++------ .../Drivers/Gd/Modifiers/BlurModifierTest.php | 4 ++-- .../Gd/Modifiers/BrightnessModifierTest.php | 4 ++-- .../Gd/Modifiers/ContrastModifierTest.php | 4 ++-- .../Gd/Modifiers/DrawEllipseModiferTest.php | 4 ++-- .../Gd/Modifiers/DrawLineModiferTest.php | 4 ++-- .../Gd/Modifiers/DrawPixelModifierTest.php | 4 ++-- .../Gd/Modifiers/DrawPolygonModifierTest.php | 4 ++-- .../Modifiers/DrawRectangleModifierTest.php | 4 ++-- .../Drivers/Gd/Modifiers/FillModifierTest.php | 16 ++++++------- .../Drivers/Gd/Modifiers/FitModifierTest.php | 8 +++---- .../Gd/Modifiers/FlipFlopModifierTest.php | 8 +++---- .../Gd/Modifiers/GammaModifierTest.php | 4 ++-- .../Gd/Modifiers/GreyscaleModifierTest.php | 4 ++-- .../Gd/Modifiers/InvertModifierTest.php | 8 +++---- .../Gd/Modifiers/PixelateModifierTest.php | 8 +++---- .../Gd/Modifiers/PlaceModifierTest.php | 4 ++-- .../Gd/Modifiers/ResizeModifierTest.php | 8 +++---- .../Gd/Modifiers/SharpenModifierTest.php | 4 ++-- .../Imagick/Modifiers/BlurModifierTest.php | 4 ++-- .../Modifiers/BrightnessModifierTest.php | 4 ++-- .../Modifiers/ContrastModifierTest.php | 4 ++-- .../Modifiers/DrawEllipseModifierTest.php | 4 ++-- .../Modifiers/DrawLineModifierTest.php | 4 ++-- .../Modifiers/DrawPixelModifierTest.php | 4 ++-- .../Modifiers/DrawPolygonModifierTest.php | 4 ++-- .../Modifiers/DrawRectangleModifierTest.php | 4 ++-- .../Imagick/Modifiers/FillModifierTest.php | 16 ++++++------- .../Imagick/Modifiers/FitModifierTest.php | 8 +++---- .../Modifiers/FlipFlopModifierTest.php | 8 +++---- .../Imagick/Modifiers/GammaModifierTest.php | 4 ++-- .../Modifiers/GreyscaleModifierTest.php | 4 ++-- .../Imagick/Modifiers/InvertModifierTest.php | 8 +++---- .../Modifiers/PixelateModifierTest.php | 8 +++---- .../Imagick/Modifiers/PlaceModifierTest.php | 4 ++-- .../Imagick/Modifiers/ResizeModifierTest.php | 8 +++---- .../Imagick/Modifiers/SharpenModifierTest.php | 4 ++-- 42 files changed, 121 insertions(+), 138 deletions(-) diff --git a/src/Drivers/Abstract/AbstractImage.php b/src/Drivers/Abstract/AbstractImage.php index cc3ac488..3b46865f 100644 --- a/src/Drivers/Abstract/AbstractImage.php +++ b/src/Drivers/Abstract/AbstractImage.php @@ -217,11 +217,11 @@ abstract class AbstractImage implements ImageInterface ); } - public function getColors(int $x, int $y): CollectionInterface + public function pickColors(int $x, int $y): CollectionInterface { $colors = new Collection(); foreach ($this as $key => $frame) { - $colors->push($this->getColor($x, $y, $key)); + $colors->push($this->pickColor($x, $y, $key)); } return $colors; diff --git a/src/Drivers/Gd/Image.php b/src/Drivers/Gd/Image.php index 52a29f1c..ad9b3754 100644 --- a/src/Drivers/Gd/Image.php +++ b/src/Drivers/Gd/Image.php @@ -66,7 +66,7 @@ class Image extends AbstractImage implements ImageInterface, IteratorAggregate return imagesy($this->getFrame()->getCore()); } - public function getColor(int $x, int $y, int $frame_key = 0): ?ColorInterface + public function pickColor(int $x, int $y, int $frame_key = 0): ?ColorInterface { if ($frame = $this->getFrame($frame_key)) { return new Color(imagecolorat($frame->getCore(), $x, $y)); diff --git a/src/Drivers/Imagick/Image.php b/src/Drivers/Imagick/Image.php index 0ef864e6..4dc8ca6a 100644 --- a/src/Drivers/Imagick/Image.php +++ b/src/Drivers/Imagick/Image.php @@ -121,7 +121,7 @@ class Image extends AbstractImage implements ImageInterface, Iterator return $this->getFrame()->getCore()->getImageHeight(); } - public function getColor(int $x, int $y, int $frame_key = 0): ?ColorInterface + public function pickColor(int $x, int $y, int $frame_key = 0): ?ColorInterface { if ($frame = $this->getFrame($frame_key)) { return new Color($frame->getCore()->getImagePixelColor($x, $y)); diff --git a/src/Interfaces/ImageInterface.php b/src/Interfaces/ImageInterface.php index 6020f963..45b257db 100644 --- a/src/Interfaces/ImageInterface.php +++ b/src/Interfaces/ImageInterface.php @@ -3,6 +3,7 @@ namespace Intervention\Image\Interfaces; use Countable; +use Intervention\Image\Collection; use Intervention\Image\EncodedImage; use Traversable; @@ -137,26 +138,8 @@ interface ImageInterface extends Traversable, Countable */ public function toPng(): EncodedImage; - /** - * Return color of pixel at the given position of the image - * For animated image pass the key of the animation frame. - * - * @param int $x - * @param int $y - * @param int $frame_key - * @return null|ColorInterface - */ - public function getColor(int $x, int $y, int $frame_key = 0): ?ColorInterface; - - /** - * Return a collection with the color of the pixel at the given position - * For animated images all pixel colors for all frames are returned. - * - * @param int $x - * @param int $y - * @return CollectionInterface - */ - public function getColors(int $x, int $y): CollectionInterface; + public function pickColor(int $x, int $y, int $frame_key = 0): ?ColorInterface; + public function pickColors(int $x, int $y): CollectionInterface; /** * Draw text on image diff --git a/tests/Drivers/Abstract/AbstractImageTest.php b/tests/Drivers/Abstract/AbstractImageTest.php index 54092d3f..e4f64cd2 100644 --- a/tests/Drivers/Abstract/AbstractImageTest.php +++ b/tests/Drivers/Abstract/AbstractImageTest.php @@ -297,12 +297,12 @@ class AbstractImageTest extends TestCase $this->assertInstanceOf(ImageInterface::class, $result); } - public function testGetColors(): void + public function testPickColors(): void { $color = Mockery::mock(ColorInterface::class); $img = $this->abstractImageMock(); - $img->shouldReceive('getColor')->times(3)->andReturn($color); - $result = $img->getColors(1, 2); + $img->shouldReceive('pickColor')->times(3)->andReturn($color); + $result = $img->pickColors(1, 2); $this->assertInstanceOf(Collection::class, $result); } diff --git a/tests/Drivers/Gd/ImageTest.php b/tests/Drivers/Gd/ImageTest.php index 07cc4233..927450fd 100644 --- a/tests/Drivers/Gd/ImageTest.php +++ b/tests/Drivers/Gd/ImageTest.php @@ -92,33 +92,33 @@ class ImageTest extends TestCase $this->assertInstanceOf(Rectangle::class, $this->image->getSize()); } - public function testGetColor(): void + public function testPickColor(): void { - $color = $this->image->getColor(0, 0); + $color = $this->image->pickColor(0, 0); $this->assertInstanceOf(Color::class, $color); $this->assertEquals(255, $color->red()); $this->assertEquals(0, $color->green()); $this->assertEquals(0, $color->blue()); - $color = $this->image->getColor(0, 0, 1); + $color = $this->image->pickColor(0, 0, 1); $this->assertInstanceOf(Color::class, $color); $this->assertEquals(0, $color->red()); $this->assertEquals(255, $color->green()); $this->assertEquals(0, $color->blue()); - $color = $this->image->getColor(0, 0, 2); + $color = $this->image->pickColor(0, 0, 2); $this->assertInstanceOf(Color::class, $color); $this->assertEquals(0, $color->red()); $this->assertEquals(0, $color->green()); $this->assertEquals(255, $color->blue()); - $color = $this->image->getColor(0, 0, 3); + $color = $this->image->pickColor(0, 0, 3); $this->assertNull($color); } - public function testGetColors(): void + public function testPickColors(): void { - $colors = $this->image->getColors(0, 0); + $colors = $this->image->pickColors(0, 0); $this->assertInstanceOf(Collection::class, $colors); $this->assertCount(3, $colors); diff --git a/tests/Drivers/Gd/Modifiers/BlurModifierTest.php b/tests/Drivers/Gd/Modifiers/BlurModifierTest.php index 237868c6..5fa2328a 100644 --- a/tests/Drivers/Gd/Modifiers/BlurModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/BlurModifierTest.php @@ -17,8 +17,8 @@ class BlurModifierTest extends TestCase public function testColorChange(): void { $image = $this->createTestImage('trim.png'); - $this->assertEquals('00aef0', $image->getColor(14, 14)->toHex()); + $this->assertEquals('00aef0', $image->pickColor(14, 14)->toHex()); $image->modify(new BlurModifier(30)); - $this->assertEquals('4fa68d', $image->getColor(14, 14)->toHex()); + $this->assertEquals('4fa68d', $image->pickColor(14, 14)->toHex()); } } diff --git a/tests/Drivers/Gd/Modifiers/BrightnessModifierTest.php b/tests/Drivers/Gd/Modifiers/BrightnessModifierTest.php index 3879a005..24312978 100644 --- a/tests/Drivers/Gd/Modifiers/BrightnessModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/BrightnessModifierTest.php @@ -17,8 +17,8 @@ class BrightnessModifierTest extends TestCase public function testApply(): void { $image = $this->createTestImage('trim.png'); - $this->assertEquals('00aef0', $image->getColor(14, 14)->toHex()); + $this->assertEquals('00aef0', $image->pickColor(14, 14)->toHex()); $image->modify(new BrightnessModifier(30)); - $this->assertEquals('4cfaff', $image->getColor(14, 14)->toHex()); + $this->assertEquals('4cfaff', $image->pickColor(14, 14)->toHex()); } } diff --git a/tests/Drivers/Gd/Modifiers/ContrastModifierTest.php b/tests/Drivers/Gd/Modifiers/ContrastModifierTest.php index 74059c1b..66afbf57 100644 --- a/tests/Drivers/Gd/Modifiers/ContrastModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/ContrastModifierTest.php @@ -17,8 +17,8 @@ class ContrastModifierTest extends TestCase public function testApply(): void { $image = $this->createTestImage('trim.png'); - $this->assertEquals('00aef0', $image->getColor(14, 14)->toHex()); + $this->assertEquals('00aef0', $image->pickColor(14, 14)->toHex()); $image->modify(new ContrastModifier(30)); - $this->assertEquals('00ceff', $image->getColor(14, 14)->toHex()); + $this->assertEquals('00ceff', $image->pickColor(14, 14)->toHex()); } } diff --git a/tests/Drivers/Gd/Modifiers/DrawEllipseModiferTest.php b/tests/Drivers/Gd/Modifiers/DrawEllipseModiferTest.php index a9dfa0bd..1c95cd3c 100644 --- a/tests/Drivers/Gd/Modifiers/DrawEllipseModiferTest.php +++ b/tests/Drivers/Gd/Modifiers/DrawEllipseModiferTest.php @@ -19,10 +19,10 @@ class DrawEllipseModifierTest extends TestCase public function testApply(): void { $image = $this->createTestImage('trim.png'); - $this->assertEquals('00aef0', $image->getColor(14, 14)->toHex()); + $this->assertEquals('00aef0', $image->pickColor(14, 14)->toHex()); $drawable = new Ellipse(10, 10); $drawable->background('b53717'); $image->modify(new DrawEllipseModifier(new Point(14, 14), $drawable)); - $this->assertEquals('b53717', $image->getColor(14, 14)->toHex()); + $this->assertEquals('b53717', $image->pickColor(14, 14)->toHex()); } } diff --git a/tests/Drivers/Gd/Modifiers/DrawLineModiferTest.php b/tests/Drivers/Gd/Modifiers/DrawLineModiferTest.php index 392db16f..e726f311 100644 --- a/tests/Drivers/Gd/Modifiers/DrawLineModiferTest.php +++ b/tests/Drivers/Gd/Modifiers/DrawLineModiferTest.php @@ -19,10 +19,10 @@ class DrawLineModifierTest extends TestCase public function testApply(): void { $image = $this->createTestImage('trim.png'); - $this->assertEquals('00aef0', $image->getColor(14, 14)->toHex()); + $this->assertEquals('00aef0', $image->pickColor(14, 14)->toHex()); $line = new Line(new Point(0, 0), new Point(10, 0), 4); $line->background('b53517'); $image->modify(new DrawLineModifier(new Point(0, 0), $line)); - $this->assertEquals('b53517', $image->getColor(0, 0)->toHex()); + $this->assertEquals('b53517', $image->pickColor(0, 0)->toHex()); } } diff --git a/tests/Drivers/Gd/Modifiers/DrawPixelModifierTest.php b/tests/Drivers/Gd/Modifiers/DrawPixelModifierTest.php index 04c44096..e6ef8f5d 100644 --- a/tests/Drivers/Gd/Modifiers/DrawPixelModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/DrawPixelModifierTest.php @@ -18,8 +18,8 @@ class DrawPixelModifierTest extends TestCase public function testApply(): void { $image = $this->createTestImage('trim.png'); - $this->assertEquals('00aef0', $image->getColor(14, 14)->toHex()); + $this->assertEquals('00aef0', $image->pickColor(14, 14)->toHex()); $image->modify(new DrawPixelModifier(new Point(14, 14), 'ffffff')); - $this->assertEquals('ffffff', $image->getColor(14, 14)->toHex()); + $this->assertEquals('ffffff', $image->pickColor(14, 14)->toHex()); } } diff --git a/tests/Drivers/Gd/Modifiers/DrawPolygonModifierTest.php b/tests/Drivers/Gd/Modifiers/DrawPolygonModifierTest.php index eaf7663a..1b62a24a 100644 --- a/tests/Drivers/Gd/Modifiers/DrawPolygonModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/DrawPolygonModifierTest.php @@ -19,10 +19,10 @@ class DrawPolygonModifierTest extends TestCase public function testApply(): void { $image = $this->createTestImage('trim.png'); - $this->assertEquals('00aef0', $image->getColor(14, 14)->toHex()); + $this->assertEquals('00aef0', $image->pickColor(14, 14)->toHex()); $drawable = new Polygon([new Point(0, 0), new Point(15, 15), new Point(20, 20)]); $drawable->background('b53717'); $image->modify(new DrawPolygonModifier($drawable)); - $this->assertEquals('b53717', $image->getColor(14, 14)->toHex()); + $this->assertEquals('b53717', $image->pickColor(14, 14)->toHex()); } } diff --git a/tests/Drivers/Gd/Modifiers/DrawRectangleModifierTest.php b/tests/Drivers/Gd/Modifiers/DrawRectangleModifierTest.php index f5b2d1b6..2368c6ee 100644 --- a/tests/Drivers/Gd/Modifiers/DrawRectangleModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/DrawRectangleModifierTest.php @@ -19,10 +19,10 @@ class DrawRectangleModifierTest extends TestCase public function testApply(): void { $image = $this->createTestImage('trim.png'); - $this->assertEquals('00aef0', $image->getColor(14, 14)->toHex()); + $this->assertEquals('00aef0', $image->pickColor(14, 14)->toHex()); $rectangle = new Rectangle(300, 200); $rectangle->background('ffffff'); $image->modify(new DrawRectangleModifier(new Point(14, 14), $rectangle)); - $this->assertEquals('ffffff', $image->getColor(14, 14)->toHex()); + $this->assertEquals('ffffff', $image->pickColor(14, 14)->toHex()); } } diff --git a/tests/Drivers/Gd/Modifiers/FillModifierTest.php b/tests/Drivers/Gd/Modifiers/FillModifierTest.php index f2263ad9..380b746a 100644 --- a/tests/Drivers/Gd/Modifiers/FillModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/FillModifierTest.php @@ -19,20 +19,20 @@ class FillModifierTest extends TestCase public function testFloodFillColor(): void { $image = $this->createTestImage('blocks.png'); - $this->assertEquals('0000ff', $image->getColor(420, 270)->toHex()); - $this->assertEquals('ff0000', $image->getColor(540, 400)->toHex()); + $this->assertEquals('0000ff', $image->pickColor(420, 270)->toHex()); + $this->assertEquals('ff0000', $image->pickColor(540, 400)->toHex()); $image->modify(new FillModifier(new Color(13421772), new Point(540, 400))); - $this->assertEquals('0000ff', $image->getColor(420, 270)->toHex()); - $this->assertEquals('cccccc', $image->getColor(540, 400)->toHex()); + $this->assertEquals('0000ff', $image->pickColor(420, 270)->toHex()); + $this->assertEquals('cccccc', $image->pickColor(540, 400)->toHex()); } public function testFillAllColor(): void { $image = $this->createTestImage('blocks.png'); - $this->assertEquals('0000ff', $image->getColor(420, 270)->toHex()); - $this->assertEquals('ff0000', $image->getColor(540, 400)->toHex()); + $this->assertEquals('0000ff', $image->pickColor(420, 270)->toHex()); + $this->assertEquals('ff0000', $image->pickColor(540, 400)->toHex()); $image->modify(new FillModifier(new Color(13421772))); - $this->assertEquals('cccccc', $image->getColor(420, 270)->toHex()); - $this->assertEquals('cccccc', $image->getColor(540, 400)->toHex()); + $this->assertEquals('cccccc', $image->pickColor(420, 270)->toHex()); + $this->assertEquals('cccccc', $image->pickColor(540, 400)->toHex()); } } diff --git a/tests/Drivers/Gd/Modifiers/FitModifierTest.php b/tests/Drivers/Gd/Modifiers/FitModifierTest.php index b6e02fba..58bd8094 100644 --- a/tests/Drivers/Gd/Modifiers/FitModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/FitModifierTest.php @@ -22,9 +22,9 @@ class FitModifierTest extends TestCase $image->modify(new FitModifier(100, 100, 'center')); $this->assertEquals(100, $image->getWidth()); $this->assertEquals(100, $image->getHeight()); - $this->assertColor(255, 0, 0, 1, $image->getColor(90, 90)); - $this->assertColor(0, 255, 0, 1, $image->getColor(65, 70)); - $this->assertColor(0, 0, 255, 1, $image->getColor(70, 52)); - $this->assertTransparency($image->getColor(90, 30)); + $this->assertColor(255, 0, 0, 1, $image->pickColor(90, 90)); + $this->assertColor(0, 255, 0, 1, $image->pickColor(65, 70)); + $this->assertColor(0, 0, 255, 1, $image->pickColor(70, 52)); + $this->assertTransparency($image->pickColor(90, 30)); } } diff --git a/tests/Drivers/Gd/Modifiers/FlipFlopModifierTest.php b/tests/Drivers/Gd/Modifiers/FlipFlopModifierTest.php index c4a34136..0305f6a8 100644 --- a/tests/Drivers/Gd/Modifiers/FlipFlopModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/FlipFlopModifierTest.php @@ -19,16 +19,16 @@ class FlipFlopModifierTest extends TestCase public function testFlipImage(): void { $image = $this->createTestImage('tile.png'); - $this->assertEquals('b4e000', $image->getColor(0, 0)->toHex()); + $this->assertEquals('b4e000', $image->pickColor(0, 0)->toHex()); $image->modify(new FlipModifier()); - $this->assertEquals('000000', $image->getColor(0, 0)->toHex()); + $this->assertEquals('000000', $image->pickColor(0, 0)->toHex()); } public function testFlopImage(): void { $image = $this->createTestImage('tile.png'); - $this->assertEquals('b4e000', $image->getColor(0, 0)->toHex()); + $this->assertEquals('b4e000', $image->pickColor(0, 0)->toHex()); $image->modify(new FlopModifier()); - $this->assertEquals('000000', $image->getColor(0, 0)->toHex()); + $this->assertEquals('000000', $image->pickColor(0, 0)->toHex()); } } diff --git a/tests/Drivers/Gd/Modifiers/GammaModifierTest.php b/tests/Drivers/Gd/Modifiers/GammaModifierTest.php index 96fbb193..dec4de93 100644 --- a/tests/Drivers/Gd/Modifiers/GammaModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/GammaModifierTest.php @@ -17,8 +17,8 @@ class GammaModifierTest extends TestCase public function testModifier(): void { $image = $this->createTestImage('trim.png'); - $this->assertEquals('00aef0', $image->getColor(0, 0)->toHex()); + $this->assertEquals('00aef0', $image->pickColor(0, 0)->toHex()); $image->modify(new GammaModifier(2.1)); - $this->assertEquals('00d5f8', $image->getColor(0, 0)->toHex()); + $this->assertEquals('00d5f8', $image->pickColor(0, 0)->toHex()); } } diff --git a/tests/Drivers/Gd/Modifiers/GreyscaleModifierTest.php b/tests/Drivers/Gd/Modifiers/GreyscaleModifierTest.php index c11f2225..6a9b6380 100644 --- a/tests/Drivers/Gd/Modifiers/GreyscaleModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/GreyscaleModifierTest.php @@ -17,8 +17,8 @@ class GreyscaleModifierTest extends TestCase public function testColorChange(): void { $image = $this->createTestImage('trim.png'); - $this->assertFalse($image->getColor(0, 0)->isGreyscale()); + $this->assertFalse($image->pickColor(0, 0)->isGreyscale()); $image->modify(new GreyscaleModifier()); - $this->assertTrue($image->getColor(0, 0)->isGreyscale()); + $this->assertTrue($image->pickColor(0, 0)->isGreyscale()); } } diff --git a/tests/Drivers/Gd/Modifiers/InvertModifierTest.php b/tests/Drivers/Gd/Modifiers/InvertModifierTest.php index 35ab7ae1..82a81b59 100644 --- a/tests/Drivers/Gd/Modifiers/InvertModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/InvertModifierTest.php @@ -17,10 +17,10 @@ class InvertModifierTest extends TestCase public function testApply(): void { $image = $this->createTestImage('trim.png'); - $this->assertEquals('00aef0', $image->getColor(0, 0)->toHex()); - $this->assertEquals('ffa601', $image->getColor(25, 25)->toHex()); + $this->assertEquals('00aef0', $image->pickColor(0, 0)->toHex()); + $this->assertEquals('ffa601', $image->pickColor(25, 25)->toHex()); $image->modify(new InvertModifier()); - $this->assertEquals('ff510f', $image->getColor(0, 0)->toHex()); - $this->assertEquals('0059fe', $image->getColor(25, 25)->toHex()); + $this->assertEquals('ff510f', $image->pickColor(0, 0)->toHex()); + $this->assertEquals('0059fe', $image->pickColor(25, 25)->toHex()); } } diff --git a/tests/Drivers/Gd/Modifiers/PixelateModifierTest.php b/tests/Drivers/Gd/Modifiers/PixelateModifierTest.php index 04dba07b..427a8080 100644 --- a/tests/Drivers/Gd/Modifiers/PixelateModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/PixelateModifierTest.php @@ -17,10 +17,10 @@ class PixelateModifierTest extends TestCase public function testModify(): void { $image = $this->createTestImage('trim.png'); - $this->assertEquals('00aef0', $image->getColor(0, 0)->toHex()); - $this->assertEquals('00aef0', $image->getColor(14, 14)->toHex()); + $this->assertEquals('00aef0', $image->pickColor(0, 0)->toHex()); + $this->assertEquals('00aef0', $image->pickColor(14, 14)->toHex()); $image->modify(new PixelateModifier(10)); - $this->assertEquals('00aef0', $image->getColor(0, 0)->toHex()); - $this->assertEquals('6aaa8b', $image->getColor(14, 14)->toHex()); + $this->assertEquals('00aef0', $image->pickColor(0, 0)->toHex()); + $this->assertEquals('6aaa8b', $image->pickColor(14, 14)->toHex()); } } diff --git a/tests/Drivers/Gd/Modifiers/PlaceModifierTest.php b/tests/Drivers/Gd/Modifiers/PlaceModifierTest.php index c3ddd2d2..3f3db1e0 100644 --- a/tests/Drivers/Gd/Modifiers/PlaceModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/PlaceModifierTest.php @@ -17,8 +17,8 @@ class PlaceModifierTest extends TestCase public function testColorChange(): void { $image = $this->createTestImage('test.jpg'); - $this->assertEquals('febc44', $image->getColor(300, 25)->toHex()); + $this->assertEquals('febc44', $image->pickColor(300, 25)->toHex()); $image->modify(new PlaceModifier(__DIR__ . '/../../../images/circle.png', 'top-right', 0, 0)); - $this->assertEquals('32250d', $image->getColor(300, 25)->toHex()); + $this->assertEquals('32250d', $image->pickColor(300, 25)->toHex()); } } diff --git a/tests/Drivers/Gd/Modifiers/ResizeModifierTest.php b/tests/Drivers/Gd/Modifiers/ResizeModifierTest.php index ed2ebf2b..bf7ef234 100644 --- a/tests/Drivers/Gd/Modifiers/ResizeModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/ResizeModifierTest.php @@ -22,10 +22,10 @@ class ResizeModifierTest extends TestCase $image->modify(new ResizeModifier(200, 100)); $this->assertEquals(200, $image->getWidth()); $this->assertEquals(100, $image->getHeight()); - $this->assertColor(255, 0, 0, 1, $image->getColor(150, 70)); - $this->assertColor(0, 255, 0, 1, $image->getColor(125, 70)); - $this->assertColor(0, 0, 255, 1, $image->getColor(130, 54)); - $transparent = $image->getColor(170, 30); + $this->assertColor(255, 0, 0, 1, $image->pickColor(150, 70)); + $this->assertColor(0, 255, 0, 1, $image->pickColor(125, 70)); + $this->assertColor(0, 0, 255, 1, $image->pickColor(130, 54)); + $transparent = $image->pickColor(170, 30); $this->assertEquals(2130706432, $transparent->toInt()); $this->assertTransparency($transparent); } diff --git a/tests/Drivers/Gd/Modifiers/SharpenModifierTest.php b/tests/Drivers/Gd/Modifiers/SharpenModifierTest.php index 5a7c9180..5646bae1 100644 --- a/tests/Drivers/Gd/Modifiers/SharpenModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/SharpenModifierTest.php @@ -17,8 +17,8 @@ class SharpenModifierTest extends TestCase public function testModify(): void { $image = $this->createTestImage('trim.png'); - $this->assertEquals('60ab96', $image->getColor(15, 14)->toHex()); + $this->assertEquals('60ab96', $image->pickColor(15, 14)->toHex()); $image->modify(new SharpenModifier(10)); - $this->assertEquals('4daba7', $image->getColor(15, 14)->toHex()); + $this->assertEquals('4daba7', $image->pickColor(15, 14)->toHex()); } } diff --git a/tests/Drivers/Imagick/Modifiers/BlurModifierTest.php b/tests/Drivers/Imagick/Modifiers/BlurModifierTest.php index 1d912330..485b8e51 100644 --- a/tests/Drivers/Imagick/Modifiers/BlurModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/BlurModifierTest.php @@ -17,8 +17,8 @@ class BlurModifierTest extends TestCase public function testColorChange(): void { $image = $this->createTestImage('trim.png'); - $this->assertEquals('00aef0', $image->getColor(14, 14)->toHex()); + $this->assertEquals('00aef0', $image->pickColor(14, 14)->toHex()); $image->modify(new BlurModifier(30)); - $this->assertEquals('42acb2', $image->getColor(14, 14)->toHex()); + $this->assertEquals('42acb2', $image->pickColor(14, 14)->toHex()); } } diff --git a/tests/Drivers/Imagick/Modifiers/BrightnessModifierTest.php b/tests/Drivers/Imagick/Modifiers/BrightnessModifierTest.php index b1da2677..8cb0b3db 100644 --- a/tests/Drivers/Imagick/Modifiers/BrightnessModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/BrightnessModifierTest.php @@ -17,8 +17,8 @@ class BrightnessModifierTest extends TestCase public function testApply(): void { $image = $this->createTestImage('trim.png'); - $this->assertEquals('00aef0', $image->getColor(14, 14)->toHex()); + $this->assertEquals('00aef0', $image->pickColor(14, 14)->toHex()); $image->modify(new BrightnessModifier(30)); - $this->assertEquals('39c9ff', $image->getColor(14, 14)->toHex()); + $this->assertEquals('39c9ff', $image->pickColor(14, 14)->toHex()); } } diff --git a/tests/Drivers/Imagick/Modifiers/ContrastModifierTest.php b/tests/Drivers/Imagick/Modifiers/ContrastModifierTest.php index e22a36c0..c5232a4c 100644 --- a/tests/Drivers/Imagick/Modifiers/ContrastModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/ContrastModifierTest.php @@ -17,8 +17,8 @@ class ContrastModifierTest extends TestCase public function testApply(): void { $image = $this->createTestImage('trim.png'); - $this->assertEquals('00aef0', $image->getColor(14, 14)->toHex()); + $this->assertEquals('00aef0', $image->pickColor(14, 14)->toHex()); $image->modify(new ContrastModifier(30)); - $this->assertEquals('00fcff', $image->getColor(14, 14)->toHex()); + $this->assertEquals('00fcff', $image->pickColor(14, 14)->toHex()); } } diff --git a/tests/Drivers/Imagick/Modifiers/DrawEllipseModifierTest.php b/tests/Drivers/Imagick/Modifiers/DrawEllipseModifierTest.php index b12be356..a2bed0d8 100644 --- a/tests/Drivers/Imagick/Modifiers/DrawEllipseModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/DrawEllipseModifierTest.php @@ -15,10 +15,10 @@ class DrawEllipseModifierTest extends TestCase public function testApply(): void { $image = $this->createTestImage('trim.png'); - $this->assertEquals('00aef0', $image->getColor(14, 14)->toHex()); + $this->assertEquals('00aef0', $image->pickColor(14, 14)->toHex()); $drawable = new Ellipse(10, 10); $drawable->background('b53717'); $image->modify(new DrawEllipseModifier(new Point(14, 14), $drawable)); - $this->assertEquals('b53717', $image->getColor(14, 14)->toHex()); + $this->assertEquals('b53717', $image->pickColor(14, 14)->toHex()); } } diff --git a/tests/Drivers/Imagick/Modifiers/DrawLineModifierTest.php b/tests/Drivers/Imagick/Modifiers/DrawLineModifierTest.php index 9602ab54..423e27da 100644 --- a/tests/Drivers/Imagick/Modifiers/DrawLineModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/DrawLineModifierTest.php @@ -15,10 +15,10 @@ class DrawLineModifierTest extends TestCase public function testApply(): void { $image = $this->createTestImage('trim.png'); - $this->assertEquals('00aef0', $image->getColor(14, 14)->toHex()); + $this->assertEquals('00aef0', $image->pickColor(14, 14)->toHex()); $line = new Line(new Point(0, 0), new Point(10, 0), 4); $line->background('b53517'); $image->modify(new DrawLineModifier(new Point(0, 0), $line)); - $this->assertEquals('b53517', $image->getColor(0, 0)->toHex()); + $this->assertEquals('b53517', $image->pickColor(0, 0)->toHex()); } } diff --git a/tests/Drivers/Imagick/Modifiers/DrawPixelModifierTest.php b/tests/Drivers/Imagick/Modifiers/DrawPixelModifierTest.php index ac5d03df..90c076d7 100644 --- a/tests/Drivers/Imagick/Modifiers/DrawPixelModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/DrawPixelModifierTest.php @@ -18,8 +18,8 @@ class DrawPixelModifierTest extends TestCase public function testApply(): void { $image = $this->createTestImage('trim.png'); - $this->assertEquals('00aef0', $image->getColor(14, 14)->toHex()); + $this->assertEquals('00aef0', $image->pickColor(14, 14)->toHex()); $image->modify(new DrawPixelModifier(new Point(14, 14), 'ffffff')); - $this->assertEquals('ffffff', $image->getColor(14, 14)->toHex()); + $this->assertEquals('ffffff', $image->pickColor(14, 14)->toHex()); } } diff --git a/tests/Drivers/Imagick/Modifiers/DrawPolygonModifierTest.php b/tests/Drivers/Imagick/Modifiers/DrawPolygonModifierTest.php index 99796e58..02ee6b2b 100644 --- a/tests/Drivers/Imagick/Modifiers/DrawPolygonModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/DrawPolygonModifierTest.php @@ -15,10 +15,10 @@ class DrawPolygonModifierTest extends TestCase public function testApply(): void { $image = $this->createTestImage('trim.png'); - $this->assertEquals('00aef0', $image->getColor(14, 14)->toHex()); + $this->assertEquals('00aef0', $image->pickColor(14, 14)->toHex()); $drawable = new Polygon([new Point(0, 0), new Point(15, 15), new Point(20, 20)]); $drawable->background('b53717'); $image->modify(new DrawPolygonModifier($drawable)); - $this->assertEquals('b53717', $image->getColor(14, 14)->toHex()); + $this->assertEquals('b53717', $image->pickColor(14, 14)->toHex()); } } diff --git a/tests/Drivers/Imagick/Modifiers/DrawRectangleModifierTest.php b/tests/Drivers/Imagick/Modifiers/DrawRectangleModifierTest.php index 99928e81..876397f8 100644 --- a/tests/Drivers/Imagick/Modifiers/DrawRectangleModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/DrawRectangleModifierTest.php @@ -15,10 +15,10 @@ class DrawRectangleModifierTest extends TestCase public function testApply(): void { $image = $this->createTestImage('trim.png'); - $this->assertEquals('00aef0', $image->getColor(14, 14)->toHex()); + $this->assertEquals('00aef0', $image->pickColor(14, 14)->toHex()); $rectangle = new Rectangle(300, 200); $rectangle->background('ffffff'); $image->modify(new DrawRectangleModifier(new Point(14, 14), $rectangle)); - $this->assertEquals('ffffff', $image->getColor(14, 14)->toHex()); + $this->assertEquals('ffffff', $image->pickColor(14, 14)->toHex()); } } diff --git a/tests/Drivers/Imagick/Modifiers/FillModifierTest.php b/tests/Drivers/Imagick/Modifiers/FillModifierTest.php index bbb53ead..37d9b9d7 100644 --- a/tests/Drivers/Imagick/Modifiers/FillModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/FillModifierTest.php @@ -20,20 +20,20 @@ class FillModifierTest extends TestCase public function testFloodFillColor(): void { $image = $this->createTestImage('blocks.png'); - $this->assertEquals('0000ff', $image->getColor(420, 270)->toHex()); - $this->assertEquals('ff0000', $image->getColor(540, 400)->toHex()); + $this->assertEquals('0000ff', $image->pickColor(420, 270)->toHex()); + $this->assertEquals('ff0000', $image->pickColor(540, 400)->toHex()); $image->modify(new FillModifier(new Color(new ImagickPixel('#cccccc')), new Point(540, 400))); - $this->assertEquals('0000ff', $image->getColor(420, 270)->toHex()); - $this->assertEquals('cccccc', $image->getColor(540, 400)->toHex()); + $this->assertEquals('0000ff', $image->pickColor(420, 270)->toHex()); + $this->assertEquals('cccccc', $image->pickColor(540, 400)->toHex()); } public function testFillAllColor(): void { $image = $this->createTestImage('blocks.png'); - $this->assertEquals('0000ff', $image->getColor(420, 270)->toHex()); - $this->assertEquals('ff0000', $image->getColor(540, 400)->toHex()); + $this->assertEquals('0000ff', $image->pickColor(420, 270)->toHex()); + $this->assertEquals('ff0000', $image->pickColor(540, 400)->toHex()); $image->modify(new FillModifier(new Color(new ImagickPixel('#cccccc')))); - $this->assertEquals('cccccc', $image->getColor(420, 270)->toHex()); - $this->assertEquals('cccccc', $image->getColor(540, 400)->toHex()); + $this->assertEquals('cccccc', $image->pickColor(420, 270)->toHex()); + $this->assertEquals('cccccc', $image->pickColor(540, 400)->toHex()); } } diff --git a/tests/Drivers/Imagick/Modifiers/FitModifierTest.php b/tests/Drivers/Imagick/Modifiers/FitModifierTest.php index 76acee6d..783e8e49 100644 --- a/tests/Drivers/Imagick/Modifiers/FitModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/FitModifierTest.php @@ -22,9 +22,9 @@ class FitModifierTest extends TestCase $image->modify(new FitModifier(100, 100, 'center')); $this->assertEquals(100, $image->getWidth()); $this->assertEquals(100, $image->getHeight()); - $this->assertColor(255, 0, 0, 1, $image->getColor(90, 90)); - $this->assertColor(0, 255, 0, 1, $image->getColor(65, 70)); - $this->assertColor(0, 0, 255, 1, $image->getColor(70, 52)); - $this->assertTransparency($image->getColor(90, 30)); + $this->assertColor(255, 0, 0, 1, $image->pickColor(90, 90)); + $this->assertColor(0, 255, 0, 1, $image->pickColor(65, 70)); + $this->assertColor(0, 0, 255, 1, $image->pickColor(70, 52)); + $this->assertTransparency($image->pickColor(90, 30)); } } diff --git a/tests/Drivers/Imagick/Modifiers/FlipFlopModifierTest.php b/tests/Drivers/Imagick/Modifiers/FlipFlopModifierTest.php index b6957cca..10ecab69 100644 --- a/tests/Drivers/Imagick/Modifiers/FlipFlopModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/FlipFlopModifierTest.php @@ -19,16 +19,16 @@ class FlipFlopModifierTest extends TestCase public function testFlipImage(): void { $image = $this->createTestImage('tile.png'); - $this->assertEquals('b4e000', $image->getColor(0, 0)->toHex()); + $this->assertEquals('b4e000', $image->pickColor(0, 0)->toHex()); $image->modify(new FlipModifier()); - $this->assertEquals('000000', $image->getColor(0, 0)->toHex()); + $this->assertEquals('000000', $image->pickColor(0, 0)->toHex()); } public function testFlopImage(): void { $image = $this->createTestImage('tile.png'); - $this->assertEquals('b4e000', $image->getColor(0, 0)->toHex()); + $this->assertEquals('b4e000', $image->pickColor(0, 0)->toHex()); $image->modify(new FlopModifier()); - $this->assertEquals('000000', $image->getColor(0, 0)->toHex()); + $this->assertEquals('000000', $image->pickColor(0, 0)->toHex()); } } diff --git a/tests/Drivers/Imagick/Modifiers/GammaModifierTest.php b/tests/Drivers/Imagick/Modifiers/GammaModifierTest.php index 3e77e9f6..c3af78c0 100644 --- a/tests/Drivers/Imagick/Modifiers/GammaModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/GammaModifierTest.php @@ -17,8 +17,8 @@ class GammaModifierTest extends TestCase public function testModifier(): void { $image = $this->createTestImage('trim.png'); - $this->assertEquals('00aef0', $image->getColor(0, 0)->toHex()); + $this->assertEquals('00aef0', $image->pickColor(0, 0)->toHex()); $image->modify(new GammaModifier(2.1)); - $this->assertEquals('00d5f8', $image->getColor(0, 0)->toHex()); + $this->assertEquals('00d5f8', $image->pickColor(0, 0)->toHex()); } } diff --git a/tests/Drivers/Imagick/Modifiers/GreyscaleModifierTest.php b/tests/Drivers/Imagick/Modifiers/GreyscaleModifierTest.php index bd31349b..42257fad 100644 --- a/tests/Drivers/Imagick/Modifiers/GreyscaleModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/GreyscaleModifierTest.php @@ -17,8 +17,8 @@ class GreyscaleModifierTest extends TestCase public function testColorChange(): void { $image = $this->createTestImage('trim.png'); - $this->assertFalse($image->getColor(0, 0)->isGreyscale()); + $this->assertFalse($image->pickColor(0, 0)->isGreyscale()); $image->modify(new GreyscaleModifier()); - $this->assertTrue($image->getColor(0, 0)->isGreyscale()); + $this->assertTrue($image->pickColor(0, 0)->isGreyscale()); } } diff --git a/tests/Drivers/Imagick/Modifiers/InvertModifierTest.php b/tests/Drivers/Imagick/Modifiers/InvertModifierTest.php index 0768d24b..2c3779cd 100644 --- a/tests/Drivers/Imagick/Modifiers/InvertModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/InvertModifierTest.php @@ -17,10 +17,10 @@ class InvertModifierTest extends TestCase public function testApply(): void { $image = $this->createTestImage('trim.png'); - $this->assertEquals('00aef0', $image->getColor(0, 0)->toHex()); - $this->assertEquals('ffa601', $image->getColor(25, 25)->toHex()); + $this->assertEquals('00aef0', $image->pickColor(0, 0)->toHex()); + $this->assertEquals('ffa601', $image->pickColor(25, 25)->toHex()); $image->modify(new InvertModifier()); - $this->assertEquals('ff510f', $image->getColor(0, 0)->toHex()); - $this->assertEquals('0059fe', $image->getColor(25, 25)->toHex()); + $this->assertEquals('ff510f', $image->pickColor(0, 0)->toHex()); + $this->assertEquals('0059fe', $image->pickColor(25, 25)->toHex()); } } diff --git a/tests/Drivers/Imagick/Modifiers/PixelateModifierTest.php b/tests/Drivers/Imagick/Modifiers/PixelateModifierTest.php index cbaa1239..9329b830 100644 --- a/tests/Drivers/Imagick/Modifiers/PixelateModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/PixelateModifierTest.php @@ -17,10 +17,10 @@ class PixelateModifierTest extends TestCase public function testModify(): void { $image = $this->createTestImage('trim.png'); - $this->assertEquals('00aef0', $image->getColor(0, 0)->toHex()); - $this->assertEquals('00aef0', $image->getColor(14, 14)->toHex()); + $this->assertEquals('00aef0', $image->pickColor(0, 0)->toHex()); + $this->assertEquals('00aef0', $image->pickColor(14, 14)->toHex()); $image->modify(new PixelateModifier(10)); - $this->assertEquals('00aef0', $image->getColor(0, 0)->toHex()); - $this->assertEquals('6bab8c', $image->getColor(14, 14)->toHex()); + $this->assertEquals('00aef0', $image->pickColor(0, 0)->toHex()); + $this->assertEquals('6bab8c', $image->pickColor(14, 14)->toHex()); } } diff --git a/tests/Drivers/Imagick/Modifiers/PlaceModifierTest.php b/tests/Drivers/Imagick/Modifiers/PlaceModifierTest.php index 92a22ab2..15a3af83 100644 --- a/tests/Drivers/Imagick/Modifiers/PlaceModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/PlaceModifierTest.php @@ -17,8 +17,8 @@ class PlaceModifierTest extends TestCase public function testColorChange(): void { $image = $this->createTestImage('test.jpg'); - $this->assertEquals('febc44', $image->getColor(300, 25)->toHex()); + $this->assertEquals('febc44', $image->pickColor(300, 25)->toHex()); $image->modify(new PlaceModifier(__DIR__ . '/../../../images/circle.png', 'top-right', 0, 0)); - $this->assertEquals('33260e', $image->getColor(300, 25)->toHex()); + $this->assertEquals('33260e', $image->pickColor(300, 25)->toHex()); } } diff --git a/tests/Drivers/Imagick/Modifiers/ResizeModifierTest.php b/tests/Drivers/Imagick/Modifiers/ResizeModifierTest.php index b112cedb..519e69f1 100644 --- a/tests/Drivers/Imagick/Modifiers/ResizeModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/ResizeModifierTest.php @@ -22,9 +22,9 @@ class ResizeModifierTest extends TestCase $image->modify(new ResizeModifier(200, 100)); $this->assertEquals(200, $image->getWidth()); $this->assertEquals(100, $image->getHeight()); - $this->assertColor(255, 0, 0, 1, $image->getColor(150, 70)); - $this->assertColor(0, 255, 0, 1, $image->getColor(125, 70)); - $this->assertColor(0, 0, 255, 1, $image->getColor(130, 54)); - $this->assertTransparency($image->getColor(150, 45)); + $this->assertColor(255, 0, 0, 1, $image->pickColor(150, 70)); + $this->assertColor(0, 255, 0, 1, $image->pickColor(125, 70)); + $this->assertColor(0, 0, 255, 1, $image->pickColor(130, 54)); + $this->assertTransparency($image->pickColor(150, 45)); } } diff --git a/tests/Drivers/Imagick/Modifiers/SharpenModifierTest.php b/tests/Drivers/Imagick/Modifiers/SharpenModifierTest.php index 92063093..2b80fd4e 100644 --- a/tests/Drivers/Imagick/Modifiers/SharpenModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/SharpenModifierTest.php @@ -17,8 +17,8 @@ class SharpenModifierTest extends TestCase public function testModify(): void { $image = $this->createTestImage('trim.png'); - $this->assertEquals('60ab96', $image->getColor(15, 14)->toHex()); + $this->assertEquals('60ab96', $image->pickColor(15, 14)->toHex()); $image->modify(new SharpenModifier(10)); - $this->assertEquals('4faca6', $image->getColor(15, 14)->toHex()); + $this->assertEquals('4faca6', $image->pickColor(15, 14)->toHex()); } } From d71448e6bf1f5c391a8b1c6026a9f38cd857a3d7 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 7 Oct 2023 16:08:02 +0200 Subject: [PATCH 276/476] Rename class --- src/Drivers/Gd/Decoders/HexColorDecoder.php | 2 +- .../{ArrayColorDecoder.php => RgbArrayColorDecoder.php} | 2 +- src/Drivers/Gd/Decoders/RgbStringColorDecoder.php | 2 +- src/Drivers/Gd/Decoders/TransparentColorDecoder.php | 2 +- src/Drivers/Gd/InputHandler.php | 2 +- src/Drivers/Imagick/Decoders/HexColorDecoder.php | 2 +- .../{ArrayColorDecoder.php => RgbArrayColorDecoder.php} | 2 +- src/Drivers/Imagick/Decoders/TransparentColorDecoder.php | 2 +- src/Drivers/Imagick/InputHandler.php | 2 +- ...yColorDecoderTest.php => RgbArrayColorDecoderTest.php} | 8 ++++---- ...yColorDecoderTest.php => RgbArrayColorDecoderTest.php} | 8 ++++---- 11 files changed, 17 insertions(+), 17 deletions(-) rename src/Drivers/Gd/Decoders/{ArrayColorDecoder.php => RgbArrayColorDecoder.php} (92%) rename src/Drivers/Imagick/Decoders/{ArrayColorDecoder.php => RgbArrayColorDecoder.php} (92%) rename tests/Drivers/Gd/Decoders/{ArrayColorDecoderTest.php => RgbArrayColorDecoderTest.php} (69%) rename tests/Drivers/Imagick/Decoders/{ArrayColorDecoderTest.php => RgbArrayColorDecoderTest.php} (69%) diff --git a/src/Drivers/Gd/Decoders/HexColorDecoder.php b/src/Drivers/Gd/Decoders/HexColorDecoder.php index 17134634..8cb6b27b 100644 --- a/src/Drivers/Gd/Decoders/HexColorDecoder.php +++ b/src/Drivers/Gd/Decoders/HexColorDecoder.php @@ -7,7 +7,7 @@ use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\DecoderInterface; use Intervention\Image\Interfaces\ImageInterface; -class HexColorDecoder extends ArrayColorDecoder implements DecoderInterface +class HexColorDecoder extends RgbArrayColorDecoder implements DecoderInterface { public function decode($input): ImageInterface|ColorInterface { diff --git a/src/Drivers/Gd/Decoders/ArrayColorDecoder.php b/src/Drivers/Gd/Decoders/RgbArrayColorDecoder.php similarity index 92% rename from src/Drivers/Gd/Decoders/ArrayColorDecoder.php rename to src/Drivers/Gd/Decoders/RgbArrayColorDecoder.php index 3d694e49..8b25f691 100644 --- a/src/Drivers/Gd/Decoders/ArrayColorDecoder.php +++ b/src/Drivers/Gd/Decoders/RgbArrayColorDecoder.php @@ -10,7 +10,7 @@ use Intervention\Image\Interfaces\DecoderInterface; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Traits\CanValidateColors; -class ArrayColorDecoder extends AbstractDecoder implements DecoderInterface +class RgbArrayColorDecoder extends AbstractDecoder implements DecoderInterface { use CanValidateColors; diff --git a/src/Drivers/Gd/Decoders/RgbStringColorDecoder.php b/src/Drivers/Gd/Decoders/RgbStringColorDecoder.php index 300dd4a1..03c46b96 100644 --- a/src/Drivers/Gd/Decoders/RgbStringColorDecoder.php +++ b/src/Drivers/Gd/Decoders/RgbStringColorDecoder.php @@ -7,7 +7,7 @@ use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\DecoderInterface; use Intervention\Image\Interfaces\ImageInterface; -class RgbStringColorDecoder extends ArrayColorDecoder implements DecoderInterface +class RgbStringColorDecoder extends RgbArrayColorDecoder implements DecoderInterface { public function decode($input): ImageInterface|ColorInterface { diff --git a/src/Drivers/Gd/Decoders/TransparentColorDecoder.php b/src/Drivers/Gd/Decoders/TransparentColorDecoder.php index 64f4adfc..5cdd3d02 100644 --- a/src/Drivers/Gd/Decoders/TransparentColorDecoder.php +++ b/src/Drivers/Gd/Decoders/TransparentColorDecoder.php @@ -7,7 +7,7 @@ use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\DecoderInterface; use Intervention\Image\Interfaces\ImageInterface; -class TransparentColorDecoder extends ArrayColorDecoder implements DecoderInterface +class TransparentColorDecoder extends RgbArrayColorDecoder implements DecoderInterface { public function decode($input): ImageInterface|ColorInterface { diff --git a/src/Drivers/Gd/InputHandler.php b/src/Drivers/Gd/InputHandler.php index c100808c..9fec013b 100644 --- a/src/Drivers/Gd/InputHandler.php +++ b/src/Drivers/Gd/InputHandler.php @@ -9,7 +9,7 @@ class InputHandler extends AbstractInputHandler protected $decoders = [ Decoders\ImageObjectDecoder::class, Decoders\FilePointerImageDecoder::class, - Decoders\ArrayColorDecoder::class, + Decoders\RgbArrayColorDecoder::class, Decoders\HtmlColorNameDecoder::class, Decoders\RgbStringColorDecoder::class, Decoders\HexColorDecoder::class, diff --git a/src/Drivers/Imagick/Decoders/HexColorDecoder.php b/src/Drivers/Imagick/Decoders/HexColorDecoder.php index 84526d16..37498320 100644 --- a/src/Drivers/Imagick/Decoders/HexColorDecoder.php +++ b/src/Drivers/Imagick/Decoders/HexColorDecoder.php @@ -7,7 +7,7 @@ use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\DecoderInterface; use Intervention\Image\Interfaces\ImageInterface; -class HexColorDecoder extends ArrayColorDecoder implements DecoderInterface +class HexColorDecoder extends RgbArrayColorDecoder implements DecoderInterface { public function decode($input): ImageInterface|ColorInterface { diff --git a/src/Drivers/Imagick/Decoders/ArrayColorDecoder.php b/src/Drivers/Imagick/Decoders/RgbArrayColorDecoder.php similarity index 92% rename from src/Drivers/Imagick/Decoders/ArrayColorDecoder.php rename to src/Drivers/Imagick/Decoders/RgbArrayColorDecoder.php index 12f8aaa2..2e924c87 100644 --- a/src/Drivers/Imagick/Decoders/ArrayColorDecoder.php +++ b/src/Drivers/Imagick/Decoders/RgbArrayColorDecoder.php @@ -11,7 +11,7 @@ use Intervention\Image\Interfaces\DecoderInterface; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Traits\CanValidateColors; -class ArrayColorDecoder extends AbstractDecoder implements DecoderInterface +class RgbArrayColorDecoder extends AbstractDecoder implements DecoderInterface { use CanValidateColors; diff --git a/src/Drivers/Imagick/Decoders/TransparentColorDecoder.php b/src/Drivers/Imagick/Decoders/TransparentColorDecoder.php index b93ef706..097a35ff 100644 --- a/src/Drivers/Imagick/Decoders/TransparentColorDecoder.php +++ b/src/Drivers/Imagick/Decoders/TransparentColorDecoder.php @@ -7,7 +7,7 @@ use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\DecoderInterface; use Intervention\Image\Interfaces\ImageInterface; -class TransparentColorDecoder extends ArrayColorDecoder implements DecoderInterface +class TransparentColorDecoder extends RgbArrayColorDecoder implements DecoderInterface { public function decode($input): ImageInterface|ColorInterface { diff --git a/src/Drivers/Imagick/InputHandler.php b/src/Drivers/Imagick/InputHandler.php index 3c751a29..8b00c35e 100644 --- a/src/Drivers/Imagick/InputHandler.php +++ b/src/Drivers/Imagick/InputHandler.php @@ -9,7 +9,7 @@ class InputHandler extends AbstractInputHandler protected $decoders = [ Decoders\ImageObjectDecoder::class, Decoders\FilePointerImageDecoder::class, - Decoders\ArrayColorDecoder::class, + Decoders\RgbArrayColorDecoder::class, Decoders\HexColorDecoder::class, Decoders\HtmlColorNameDecoder::class, Decoders\RgbStringColorDecoder::class, diff --git a/tests/Drivers/Gd/Decoders/ArrayColorDecoderTest.php b/tests/Drivers/Gd/Decoders/RgbArrayColorDecoderTest.php similarity index 69% rename from tests/Drivers/Gd/Decoders/ArrayColorDecoderTest.php rename to tests/Drivers/Gd/Decoders/RgbArrayColorDecoderTest.php index d13920ea..524b2f63 100644 --- a/tests/Drivers/Gd/Decoders/ArrayColorDecoderTest.php +++ b/tests/Drivers/Gd/Decoders/RgbArrayColorDecoderTest.php @@ -3,18 +3,18 @@ namespace Intervention\Image\Tests\Drivers\Gd\Decoders; use Intervention\Image\Drivers\Gd\Color; -use Intervention\Image\Drivers\Gd\Decoders\ArrayColorDecoder; +use Intervention\Image\Drivers\Gd\Decoders\RgbArrayColorDecoder; use Intervention\Image\Tests\TestCase; /** * @requires extension gd - * @covers \Intervention\Image\Drivers\Gd\Decoders\ArrayColorDecoder + * @covers \Intervention\Image\Drivers\Gd\Decoders\RgbArrayColorDecoder */ -class ArrayColorDecoderTest extends TestCase +class RgbArrayColorDecoderTest extends TestCase { public function testDecode(): void { - $decoder = new ArrayColorDecoder(); + $decoder = new RgbArrayColorDecoder(); $color = $decoder->decode([181, 55, 23, .5]); $this->assertInstanceOf(Color::class, $color); $this->assertEquals(181, $color->red()); diff --git a/tests/Drivers/Imagick/Decoders/ArrayColorDecoderTest.php b/tests/Drivers/Imagick/Decoders/RgbArrayColorDecoderTest.php similarity index 69% rename from tests/Drivers/Imagick/Decoders/ArrayColorDecoderTest.php rename to tests/Drivers/Imagick/Decoders/RgbArrayColorDecoderTest.php index 41301b0a..fbb5b4e0 100644 --- a/tests/Drivers/Imagick/Decoders/ArrayColorDecoderTest.php +++ b/tests/Drivers/Imagick/Decoders/RgbArrayColorDecoderTest.php @@ -3,18 +3,18 @@ namespace Intervention\Image\Tests\Drivers\Imagick\Decoders; use Intervention\Image\Drivers\Imagick\Color; -use Intervention\Image\Drivers\Imagick\Decoders\ArrayColorDecoder; +use Intervention\Image\Drivers\Imagick\Decoders\RgbArrayColorDecoder; use Intervention\Image\Tests\TestCase; /** * @requires extension imagick - * @covers \Intervention\Image\Drivers\Imagick\Decoders\ArrayColorDecoder + * @covers \Intervention\Image\Drivers\Imagick\Decoders\RgbArrayColorDecoder */ -class ArrayColorDecoderTest extends TestCase +class RgbArrayColorDecoderTest extends TestCase { public function testDecode(): void { - $decoder = new ArrayColorDecoder(); + $decoder = new RgbArrayColorDecoder(); $color = $decoder->decode([181, 55, 23, .5]); $this->assertInstanceOf(Color::class, $color); $this->assertEquals(181, $color->red()); From e72b0ff6df6e596c41eb3f71f31a85f13c904082 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Mon, 9 Oct 2023 16:30:03 +0200 Subject: [PATCH 277/476] Refactor color management --- src/Colors/Cmyk/Channels/Cyan.php | 45 +++++++++ src/Colors/Cmyk/Channels/Key.php | 8 ++ src/Colors/Cmyk/Channels/Magenta.php | 8 ++ src/Colors/Cmyk/Channels/Yellow.php | 8 ++ src/Colors/Cmyk/Color.php | 97 +++++++++++++++++++ src/Colors/Cmyk/Colorspace.php | 33 +++++++ src/Colors/Rgb/Channels/Blue.php | 8 ++ src/Colors/Rgb/Channels/Green.php | 8 ++ src/Colors/Rgb/Channels/Red.php | 45 +++++++++ src/Colors/Rgb/Color.php | 115 +++++++++++++++++++++++ src/Colors/Rgb/Colorspace.php | 27 ++++++ src/Exceptions/ColorException.php | 8 ++ src/Interfaces/ColorChannelInterface.php | 12 +++ src/Interfaces/ColorInterface.php | 20 ++-- src/Interfaces/ColorspaceInterface.php | 8 ++ tests/Colors/Rgb/ColorTest.php | 106 +++++++++++++++++++++ 16 files changed, 548 insertions(+), 8 deletions(-) create mode 100644 src/Colors/Cmyk/Channels/Cyan.php create mode 100644 src/Colors/Cmyk/Channels/Key.php create mode 100644 src/Colors/Cmyk/Channels/Magenta.php create mode 100644 src/Colors/Cmyk/Channels/Yellow.php create mode 100644 src/Colors/Cmyk/Color.php create mode 100644 src/Colors/Cmyk/Colorspace.php create mode 100644 src/Colors/Rgb/Channels/Blue.php create mode 100644 src/Colors/Rgb/Channels/Green.php create mode 100644 src/Colors/Rgb/Channels/Red.php create mode 100644 src/Colors/Rgb/Color.php create mode 100644 src/Colors/Rgb/Colorspace.php create mode 100644 src/Exceptions/ColorException.php create mode 100644 src/Interfaces/ColorChannelInterface.php create mode 100644 src/Interfaces/ColorspaceInterface.php create mode 100644 tests/Colors/Rgb/ColorTest.php diff --git a/src/Colors/Cmyk/Channels/Cyan.php b/src/Colors/Cmyk/Channels/Cyan.php new file mode 100644 index 00000000..cb40daac --- /dev/null +++ b/src/Colors/Cmyk/Channels/Cyan.php @@ -0,0 +1,45 @@ +value = $this->validate($value); + } + + public function value(): int + { + return $this->value; + } + + public function normalize($precision = 32): float + { + return round($this->value() / $this->max(), $precision); + } + + public function min(): int + { + return 0; + } + + public function max(): int + { + return 100; + } + + public function validate(mixed $value): mixed + { + if ($value < $this->min() || $value > $this->max()) { + throw new ColorException('CMYK color values must be in range 0-100.'); + } + + return $value; + } +} diff --git a/src/Colors/Cmyk/Channels/Key.php b/src/Colors/Cmyk/Channels/Key.php new file mode 100644 index 00000000..2893d2af --- /dev/null +++ b/src/Colors/Cmyk/Channels/Key.php @@ -0,0 +1,8 @@ +cyan = new Cyan($c); + $this->magenta = new Magenta($m); + $this->yellow = new Yellow($y); + $this->key = new Key($k); + } + + public function channels(): array + { + return $this->channels; + } + + public function cyan(): Cyan + { + return $this->cyan; + } + + public function magenta(): Magenta + { + return $this->magenta; + } + + public function yellow(): Yellow + { + return $this->yellow; + } + + public function key(): Key + { + return $this->key; + } + + public function toArray(): array + { + return [ + $this->cyan()->value(), + $this->magenta()->value(), + $this->yellow()->value(), + $this->key()->value(), + ]; + } + + public function transformTo(string|ColorspaceInterface $colorspace): ColorInterface + { + $colorspace = match (true) { + is_object($colorspace) => $colorspace, + default => new $colorspace(), + }; + + return $colorspace->transformColor($this); + } + + public function toRgb(): RgbColor + { + return $this->transformTo(RgbColorspace::class); + } + + public function toCmyk(): self + { + return $this->transformTo(CmykColorspace::class); + } + + public function __toString(): string + { + return sprintf( + 'cmyk(%d, %d, %d, %d)', + $this->cyan()->value(), + $this->magenta()->value(), + $this->yellow()->value(), + $this->key()->value() + ); + } +} diff --git a/src/Colors/Cmyk/Colorspace.php b/src/Colors/Cmyk/Colorspace.php new file mode 100644 index 00000000..968bd023 --- /dev/null +++ b/src/Colors/Cmyk/Colorspace.php @@ -0,0 +1,33 @@ + $this->convertRgbColor($color), + default => $color, + }; + } + + protected function convertRgbColor(RgbColor $color): CmykColor + { + $c = (255 - $color->red()->value()) / 255.0 * 100; + $m = (255 - $color->green()->value()) / 255.0 * 100; + $y = (255 - $color->blue()->value()) / 255.0 * 100; + $k = intval(round(min([$c, $m, $y]))); + + $c = intval(round($c - $k)); + $m = intval(round($m - $k)); + $y = intval(round($y - $k)); + + return new CmykColor($c, $m, $y, $k); + } +} diff --git a/src/Colors/Rgb/Channels/Blue.php b/src/Colors/Rgb/Channels/Blue.php new file mode 100644 index 00000000..b83be821 --- /dev/null +++ b/src/Colors/Rgb/Channels/Blue.php @@ -0,0 +1,8 @@ +value = $this->validate($value); + } + + public function value(): int + { + return $this->value; + } + + public function normalize($precision = 32): float + { + return round($this->value() / $this->max(), $precision); + } + + public function min(): int + { + return 0; + } + + public function max(): int + { + return 255; + } + + public function validate(mixed $value): mixed + { + if ($value < $this->min() || $value > $this->max()) { + throw new ColorException('RGB color values must be in range 0-255.'); + } + + return $value; + } +} diff --git a/src/Colors/Rgb/Color.php b/src/Colors/Rgb/Color.php new file mode 100644 index 00000000..18be0fc2 --- /dev/null +++ b/src/Colors/Rgb/Color.php @@ -0,0 +1,115 @@ +channels = [ + new Red($r), + new Green($g), + new Blue($b), + ]; + } + + public function channels(): array + { + return $this->channels; + } + + public function channel(string $classname): ColorChannelInterface + { + $channels = array_filter($this->channels(), function (ColorChannelInterface $channel) use ($classname) { + return is_a($channel, $classname); + }); + + return reset($channels); + } + + public function red(): Red + { + return $this->channel(Red::class); + } + + public function green(): Green + { + return $this->channel(Green::class); + } + + public function blue(): Blue + { + return $this->channel(Blue::class); + } + + public function toArray(): array + { + return array_map(function (ColorChannelInterface $channel) { + return $channel->value(); + }, $this->channels()); + } + + public function normalize(): array + { + return array_map(function (ColorChannelInterface $channel) { + return $channel->normalize(); + }, $this->channels()); + } + + public function toHex(string $prefix = ''): string + { + return sprintf( + '%s%02x%02x%02x', + $prefix, + $this->red()->value(), + $this->green()->value(), + $this->blue()->value() + ); + } + + public function toRgb(): Color + { + return $this; + } + + public function toInt(): int + { + return $this->red()->value() * 256 * 256 + $this->green()->value() * 256 + $this->blue()->value(); + } + + public function transformTo(string|ColorspaceInterface $colorspace): ColorInterface + { + $colorspace = match (true) { + is_object($colorspace) => $colorspace, + default => new $colorspace(), + }; + + return $colorspace->transformColor($this); + } + + public function toCmyk(): CmykColor + { + return $this->transformTo(CmykColorspace::class); + } + + public function __toString(): string + { + return sprintf( + 'rgb(%d, %d, %d)', + $this->red()->value(), + $this->green()->value(), + $this->blue()->value() + ); + } +} diff --git a/src/Colors/Rgb/Colorspace.php b/src/Colors/Rgb/Colorspace.php new file mode 100644 index 00000000..fd28b1e9 --- /dev/null +++ b/src/Colors/Rgb/Colorspace.php @@ -0,0 +1,27 @@ + $this->convertCmykColor($color), + default => $color, + }; + } + + protected function convertCmykColor(CmykColor $color): Color + { + return new Color( + (int) (255 * (1 - $color->cyan()->value()) * (1 - $color->key()->value())), + (int) (255 * (1 - $color->magenta()->value()) * (1 - $color->key()->value())), + (int) (255 * (1 - $color->yellow()->value()) * (1 - $color->key()->value())), + ); + } +} diff --git a/src/Exceptions/ColorException.php b/src/Exceptions/ColorException.php new file mode 100644 index 00000000..4eda9322 --- /dev/null +++ b/src/Exceptions/ColorException.php @@ -0,0 +1,8 @@ +assertInstanceOf(Color::class, $color); + } + + public function testChannels(): void + { + $color = new Color(10, 20, 30); + $this->assertIsArray($color->channels()); + $this->assertCount(3, $color->channels()); + } + + public function testChannel(): void + { + $color = new Color(10, 20, 30); + $channel = $color->channel(Red::class); + $this->assertInstanceOf(Red::class, $channel); + $this->assertEquals(10, $channel->value()); + } + + public function testRedGreenBlue(): void + { + $color = new Color(10, 20, 30); + $this->assertInstanceOf(Red::class, $color->red()); + $this->assertInstanceOf(Green::class, $color->green()); + $this->assertInstanceOf(Blue::class, $color->blue()); + $this->assertEquals(10, $color->red()->value()); + $this->assertEquals(20, $color->green()->value()); + $this->assertEquals(30, $color->blue()->value()); + } + + public function testToArray(): void + { + $color = new Color(10, 20, 30); + $this->assertEquals([10, 20, 30], $color->toArray()); + } + + public function testToHex(): void + { + $color = new Color(181, 55, 23); + $this->assertEquals('b53717', $color->toHex()); + $this->assertEquals('#b53717', $color->toHex('#')); + } + + public function testNormalize(): void + { + $color = new Color(255, 0, 51); + $this->assertEquals([1.0, 0.0, 0.2], $color->normalize()); + } + + public function testToInt(): void + { + $color = new Color(0, 0, 0); + $this->assertEquals(0, $color->toInt()); + + $color = new Color(255, 255, 255); + $this->assertEquals(16777215, $color->toInt()); + + $color = new Color(181, 55, 23); + $this->assertEquals(11876119, $color->toInt()); + } + + public function testToString(): void + { + $color = new Color(181, 55, 23); + $this->assertEquals('rgb(181, 55, 23)', (string) $color); + } + + public function testToCmyk(): void + { + $color = new Color(0, 0, 0); + $this->assertEquals([0, 0, 0, 100], $color->toCmyk()->toArray()); + + $color = new Color(255, 255, 255); + $this->assertEquals([0, 0, 0, 0], $color->toCmyk()->toArray()); + + $color = new Color(255, 0, 0); + $this->assertEquals([0, 100, 100, 0], $color->toCmyk()->toArray()); + + $color = new Color(255, 0, 255); + $this->assertEquals([0, 100, 0, 0], $color->toCmyk()->toArray()); + + $color = new Color(255, 255, 0); + $this->assertEquals([0, 0, 100, 0], $color->toCmyk()->toArray()); + + $color = new Color(255, 204, 204); + $this->assertEquals([0, 20, 20, 0], $color->toCmyk()->toArray()); + } +} From 929c7545bb1e143534ba79b372985bca49f99db2 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 14 Oct 2023 14:33:12 +0200 Subject: [PATCH 278/476] Add color tests --- src/Colors/Cmyk/Color.php | 41 ++++---- src/Colors/Cmyk/Colorspace.php | 10 +- src/Colors/Rgb/Color.php | 44 +++------ src/Colors/Rgb/Colorspace.php | 21 ++++- src/Colors/Rgba/Channels/Alpha.php | 8 ++ src/Colors/Rgba/Channels/Blue.php | 10 ++ src/Colors/Rgba/Channels/Green.php | 10 ++ src/Colors/Rgba/Channels/Red.php | 10 ++ src/Colors/Rgba/Color.php | 94 +++++++++++++++++++ src/Colors/Rgba/Colorspace.php | 55 +++++++++++ src/Colors/Traits/CanHandleChannels.php | 34 +++++++ src/Interfaces/ColorInterface.php | 14 +-- src/Interfaces/ColorspaceInterface.php | 8 +- tests/Colors/Cmyk/ColorTest.php | 94 +++++++++++++++++++ tests/Colors/Cmyk/ColorspaceTest.php | 26 ++++++ tests/Colors/Rgb/ColorTest.php | 52 +++++++---- tests/Colors/Rgb/ColorspaceTest.php | 26 ++++++ tests/Colors/Rgba/ColorTest.php | 119 ++++++++++++++++++++++++ tests/Colors/Rgba/ColorspaceTest.php | 26 ++++++ 19 files changed, 623 insertions(+), 79 deletions(-) create mode 100644 src/Colors/Rgba/Channels/Alpha.php create mode 100644 src/Colors/Rgba/Channels/Blue.php create mode 100644 src/Colors/Rgba/Channels/Green.php create mode 100644 src/Colors/Rgba/Channels/Red.php create mode 100644 src/Colors/Rgba/Color.php create mode 100644 src/Colors/Rgba/Colorspace.php create mode 100644 src/Colors/Traits/CanHandleChannels.php create mode 100644 tests/Colors/Cmyk/ColorTest.php create mode 100644 tests/Colors/Cmyk/ColorspaceTest.php create mode 100644 tests/Colors/Rgb/ColorspaceTest.php create mode 100644 tests/Colors/Rgba/ColorTest.php create mode 100644 tests/Colors/Rgba/ColorspaceTest.php diff --git a/src/Colors/Cmyk/Color.php b/src/Colors/Cmyk/Color.php index 63b90b6d..26b423ea 100644 --- a/src/Colors/Cmyk/Color.php +++ b/src/Colors/Cmyk/Color.php @@ -9,24 +9,26 @@ use Intervention\Image\Colors\Cmyk\Channels\Key; use Intervention\Image\Colors\Cmyk\Colorspace as CmykColorspace; use Intervention\Image\Colors\Rgb\Color as RgbColor; use Intervention\Image\Colors\Rgb\Colorspace as RgbColorspace; +use Intervention\Image\Colors\Rgba\Colorspace as RgbaColorspace; +use Intervention\Image\Colors\Rgba\Color as RgbaColor; +use Intervention\Image\Colors\Traits\CanHandleChannels; use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\ColorspaceInterface; class Color implements ColorInterface { - protected array $channels; + use CanHandleChannels; - protected Cyan $cyan; - protected Magenta $magenta; - protected Yellow $yellow; - protected Key $key; + protected array $channels; public function __construct(int $c, int $m, int $y, int $k) { - $this->cyan = new Cyan($c); - $this->magenta = new Magenta($m); - $this->yellow = new Yellow($y); - $this->key = new Key($k); + $this->channels = [ + new Cyan($c), + new Magenta($m), + new Yellow($y), + new Key($k), + ]; } public function channels(): array @@ -36,22 +38,22 @@ class Color implements ColorInterface public function cyan(): Cyan { - return $this->cyan; + return $this->channel(Cyan::class); } public function magenta(): Magenta { - return $this->magenta; + return $this->channel(Magenta::class); } public function yellow(): Yellow { - return $this->yellow; + return $this->channel(Yellow::class); } public function key(): Key { - return $this->key; + return $this->channel(Key::class); } public function toArray(): array @@ -64,24 +66,29 @@ class Color implements ColorInterface ]; } - public function transformTo(string|ColorspaceInterface $colorspace): ColorInterface + public function convertTo(string|ColorspaceInterface $colorspace): ColorInterface { $colorspace = match (true) { is_object($colorspace) => $colorspace, default => new $colorspace(), }; - return $colorspace->transformColor($this); + return $colorspace->convertColor($this); } public function toRgb(): RgbColor { - return $this->transformTo(RgbColorspace::class); + return $this->convertTo(RgbColorspace::class); + } + + public function toRgba(): RgbaColor + { + return $this->convertTo(RgbaColorspace::class); } public function toCmyk(): self { - return $this->transformTo(CmykColorspace::class); + return $this->convertTo(CmykColorspace::class); } public function __toString(): string diff --git a/src/Colors/Cmyk/Colorspace.php b/src/Colors/Cmyk/Colorspace.php index 968bd023..69c6c82a 100644 --- a/src/Colors/Cmyk/Colorspace.php +++ b/src/Colors/Cmyk/Colorspace.php @@ -3,16 +3,22 @@ namespace Intervention\Image\Colors\Cmyk; use Intervention\Image\Colors\Rgb\Color as RgbColor; +use Intervention\Image\Colors\Rgba\Color as RgbaColor; use Intervention\Image\Colors\Cmyk\Color as CmykColor; use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\ColorspaceInterface; class Colorspace implements ColorspaceInterface { - public function transformColor(ColorInterface $color): ColorInterface + /** + * {@inheritdoc} + * + * @see ColorspaceInterface::convertColor() + */ + public function convertColor(ColorInterface $color): ColorInterface { return match (get_class($color)) { - RgbColor::class => $this->convertRgbColor($color), + RgbColor::class, RgbaColor::class => $this->convertRgbColor($color), default => $color, }; } diff --git a/src/Colors/Rgb/Color.php b/src/Colors/Rgb/Color.php index 18be0fc2..644b3936 100644 --- a/src/Colors/Rgb/Color.php +++ b/src/Colors/Rgb/Color.php @@ -4,15 +4,20 @@ namespace Intervention\Image\Colors\Rgb; use Intervention\Image\Colors\Cmyk\Color as CmykColor; use Intervention\Image\Colors\Cmyk\Colorspace as CmykColorspace; +use Intervention\Image\Colors\Rgba\Colorspace as RgbaColorspace; use Intervention\Image\Colors\Rgb\Channels\Blue; use Intervention\Image\Colors\Rgb\Channels\Green; use Intervention\Image\Colors\Rgb\Channels\Red; +use Intervention\Image\Colors\Rgba\Color as RgbaColor; +use Intervention\Image\Colors\Traits\CanHandleChannels; use Intervention\Image\Interfaces\ColorChannelInterface; use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\ColorspaceInterface; class Color implements ColorInterface { + use CanHandleChannels; + protected array $channels; public function __construct(int $r, int $g, int $b) @@ -24,20 +29,6 @@ class Color implements ColorInterface ]; } - public function channels(): array - { - return $this->channels; - } - - public function channel(string $classname): ColorChannelInterface - { - $channels = array_filter($this->channels(), function (ColorChannelInterface $channel) use ($classname) { - return is_a($channel, $classname); - }); - - return reset($channels); - } - public function red(): Red { return $this->channel(Red::class); @@ -60,13 +51,6 @@ class Color implements ColorInterface }, $this->channels()); } - public function normalize(): array - { - return array_map(function (ColorChannelInterface $channel) { - return $channel->normalize(); - }, $this->channels()); - } - public function toHex(string $prefix = ''): string { return sprintf( @@ -78,29 +62,29 @@ class Color implements ColorInterface ); } - public function toRgb(): Color + public function toRgb(): self { return $this; } - public function toInt(): int - { - return $this->red()->value() * 256 * 256 + $this->green()->value() * 256 + $this->blue()->value(); - } - - public function transformTo(string|ColorspaceInterface $colorspace): ColorInterface + public function convertTo(string|ColorspaceInterface $colorspace): ColorInterface { $colorspace = match (true) { is_object($colorspace) => $colorspace, default => new $colorspace(), }; - return $colorspace->transformColor($this); + return $colorspace->convertColor($this); } public function toCmyk(): CmykColor { - return $this->transformTo(CmykColorspace::class); + return $this->convertTo(CmykColorspace::class); + } + + public function toRgba(): RgbaColor + { + return $this->convertTo(RgbaColorspace::class); } public function __toString(): string diff --git a/src/Colors/Rgb/Colorspace.php b/src/Colors/Rgb/Colorspace.php index fd28b1e9..489df51d 100644 --- a/src/Colors/Rgb/Colorspace.php +++ b/src/Colors/Rgb/Colorspace.php @@ -3,15 +3,17 @@ namespace Intervention\Image\Colors\Rgb; use Intervention\Image\Colors\Cmyk\Color as CmykColor; +use Intervention\Image\Colors\Rgba\Color as RgbaColor; use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\ColorspaceInterface; class Colorspace implements ColorspaceInterface { - public function transformColor(ColorInterface $color): ColorInterface + public function convertColor(ColorInterface $color): ColorInterface { - return match ($color) { + return match (get_class($color)) { CmykColor::class => $this->convertCmykColor($color), + RgbaColor::class => $this->convertRgbaColor($color), default => $color, }; } @@ -19,9 +21,18 @@ class Colorspace implements ColorspaceInterface protected function convertCmykColor(CmykColor $color): Color { return new Color( - (int) (255 * (1 - $color->cyan()->value()) * (1 - $color->key()->value())), - (int) (255 * (1 - $color->magenta()->value()) * (1 - $color->key()->value())), - (int) (255 * (1 - $color->yellow()->value()) * (1 - $color->key()->value())), + (int) (255 * (1 - $color->cyan()->normalize()) * (1 - $color->key()->normalize())), + (int) (255 * (1 - $color->magenta()->normalize()) * (1 - $color->key()->normalize())), + (int) (255 * (1 - $color->yellow()->normalize()) * (1 - $color->key()->normalize())), + ); + } + + protected function convertRgbaColor(RgbaColor $color): Color + { + return new Color( + $color->red()->value(), + $color->green()->value(), + $color->blue()->value() ); } } diff --git a/src/Colors/Rgba/Channels/Alpha.php b/src/Colors/Rgba/Channels/Alpha.php new file mode 100644 index 00000000..46d3a152 --- /dev/null +++ b/src/Colors/Rgba/Channels/Alpha.php @@ -0,0 +1,8 @@ +channels = [ + new Red($r), + new Green($g), + new Blue($b), + new Alpha($a), + ]; + } + + public function red(): Red + { + return $this->channel(Red::class); + } + + public function green(): Green + { + return $this->channel(Green::class); + } + + public function blue(): Blue + { + return $this->channel(Blue::class); + } + + public function alpha(): Alpha + { + return $this->channel(Alpha::class); + } + + public function isFullyOpaque(): bool + { + return $this->alpha()->value() === 255; + } + + public function toHex(string $prefix = ''): string + { + if ($this->isFullyOpaque()) { + return parent::toHex($prefix); + } + + return sprintf( + '%s%02x%02x%02x%02x', + $prefix, + $this->red()->value(), + $this->green()->value(), + $this->blue()->value(), + $this->alpha()->value() + ); + } + + public function toRgb(): RgbColor + { + return $this->convertTo(RgbColorspace::class); + } + + public function toRgba(): self + { + return $this; + } + + public function toCmyk(): CmykColor + { + return $this->convertTo(CmykColorspace::class); + } + + public function __toString(): string + { + return sprintf( + 'rgba(%d, %d, %d, %.1F)', + $this->red()->value(), + $this->green()->value(), + $this->blue()->value(), + $this->alpha()->normalize(), + ); + } +} diff --git a/src/Colors/Rgba/Colorspace.php b/src/Colors/Rgba/Colorspace.php new file mode 100644 index 00000000..28b53f83 --- /dev/null +++ b/src/Colors/Rgba/Colorspace.php @@ -0,0 +1,55 @@ + $this->convertCmykColor($color), + RgbColor::class => $this->convertRgbColor($color), + default => $color, + }; + } + + /** + * Convert given color to the RGBA colorspace + * + * @param RgbColor $color + * @return Color + */ + protected function convertRgbColor(RgbColor $color): Color + { + return new Color( + $color->red()->value(), + $color->green()->value(), + $color->blue()->value(), + 255 + ); + } + + /** + * Convert given color to the RGBA colorspace + * + * @param CmykColor $color + * @return Color + */ + protected function convertCmykColor(CmykColor $color): Color + { + return new Color( + (int) (255 * (1 - $color->cyan()->normalize()) * (1 - $color->key()->normalize())), + (int) (255 * (1 - $color->magenta()->normalize()) * (1 - $color->key()->normalize())), + (int) (255 * (1 - $color->yellow()->normalize()) * (1 - $color->key()->normalize())), + 255 + ); + } +} diff --git a/src/Colors/Traits/CanHandleChannels.php b/src/Colors/Traits/CanHandleChannels.php new file mode 100644 index 00000000..bc231139 --- /dev/null +++ b/src/Colors/Traits/CanHandleChannels.php @@ -0,0 +1,34 @@ +channels; + } + + public function channel(string $classname): ColorChannelInterface + { + $channels = array_filter($this->channels(), function (ColorChannelInterface $channel) use ($classname) { + return get_class($channel) == $classname; + }); + + if (count($channels) == 0) { + throw new ColorException('Channel ' . $classname . ' could not be found.'); + } + + return reset($channels); + } + + public function normalize(): array + { + return array_map(function (ColorChannelInterface $channel) { + return $channel->normalize(); + }, $this->channels()); + } +} diff --git a/src/Interfaces/ColorInterface.php b/src/Interfaces/ColorInterface.php index 0953402d..1333e94b 100644 --- a/src/Interfaces/ColorInterface.php +++ b/src/Interfaces/ColorInterface.php @@ -2,18 +2,20 @@ namespace Intervention\Image\Interfaces; -use Intervention\Image\Colors\CmykColor; -use Intervention\Image\Colors\RgbColor; +use Intervention\Image\Colors\Cmyk\Color as CmykColor; +use Intervention\Image\Colors\Rgb\Color as RgbColor; +use Intervention\Image\Colors\Rgba\Color as RgbaColor; interface ColorInterface { + // public function toRgb(): RgbColor; + // public function toRgba(): RgbaColor; + // public function toCmyk(): CmykColor; + // public function channels(): array; // public function channel(string $classname): ColorChannelInterface; // public function colorspace(): ColorspaceInterface; - // public function toRgb(): RgbColor; - // public function toRgba(): RgbColor; - // public function toCmyk(): CmykColor; // public function toArray(): array; - // public function transformTo(string|ColorspaceInterface $colorspace): ColorInterface; + // public function convertTo(string|ColorspaceInterface $colorspace): ColorInterface; // public function __toString(): string; } diff --git a/src/Interfaces/ColorspaceInterface.php b/src/Interfaces/ColorspaceInterface.php index b1dc3bd1..a2bb37db 100644 --- a/src/Interfaces/ColorspaceInterface.php +++ b/src/Interfaces/ColorspaceInterface.php @@ -4,5 +4,11 @@ namespace Intervention\Image\Interfaces; interface ColorspaceInterface { - public function transformColor(ColorInterface $color): ColorInterface; + /** + * Convert given color to the format of the current colorspace + * + * @param ColorInterface $color + * @return ColorInterface + */ + public function convertColor(ColorInterface $color): ColorInterface; } diff --git a/tests/Colors/Cmyk/ColorTest.php b/tests/Colors/Cmyk/ColorTest.php new file mode 100644 index 00000000..2052480f --- /dev/null +++ b/tests/Colors/Cmyk/ColorTest.php @@ -0,0 +1,94 @@ +assertInstanceOf(Color::class, $color); + } + + public function testChannels(): void + { + $color = new Color(10, 20, 30, 40); + $this->assertIsArray($color->channels()); + $this->assertCount(4, $color->channels()); + } + + public function testChannel(): void + { + $color = new Color(10, 20, 30, 40); + $channel = $color->channel(Cyan::class); + $this->assertInstanceOf(Cyan::class, $channel); + $this->assertEquals(10, $channel->value()); + } + + public function testCyanMagentaYellowKey(): void + { + $color = new Color(10, 20, 30, 40); + $this->assertInstanceOf(Cyan::class, $color->cyan()); + $this->assertInstanceOf(Magenta::class, $color->magenta()); + $this->assertInstanceOf(Yellow::class, $color->yellow()); + $this->assertInstanceOf(Key::class, $color->key()); + $this->assertEquals(10, $color->cyan()->value()); + $this->assertEquals(20, $color->magenta()->value()); + $this->assertEquals(30, $color->yellow()->value()); + $this->assertEquals(40, $color->key()->value()); + } + + public function testToArray(): void + { + $color = new Color(10, 20, 30, 40); + $this->assertEquals([10, 20, 30, 40], $color->toArray()); + } + + public function testNormalize(): void + { + $color = new Color(100, 50, 20, 0); + $this->assertEquals([1.0, 0.5, 0.2, 0.0], $color->normalize()); + } + + public function testToString(): void + { + $color = new Color(100, 50, 20, 0); + $this->assertEquals('cmyk(100, 50, 20, 0)', (string) $color); + } + + public function testToCmyk(): void + { + $color = new Color(0, 0, 0, 0); + $converted = $color->toCmyk(); + $this->assertInstanceOf(Color::class, $converted); + } + + public function testToRgb(): void + { + $color = new Color(0, 20, 20, 0); + $converted = $color->toRgb(); + $this->assertInstanceOf(RgbColor::class, $converted); + $this->assertEquals([255, 204, 204], $converted->toArray()); + } + + public function testToRgba(): void + { + $color = new Color(0, 20, 20, 0); + $converted = $color->toRgba(); + $this->assertInstanceOf(RgbaColor::class, $converted); + $this->assertEquals([255, 204, 204, 255], $converted->toArray()); + } +} diff --git a/tests/Colors/Cmyk/ColorspaceTest.php b/tests/Colors/Cmyk/ColorspaceTest.php new file mode 100644 index 00000000..818269fc --- /dev/null +++ b/tests/Colors/Cmyk/ColorspaceTest.php @@ -0,0 +1,26 @@ +assertInstanceOf( + CmykColor::class, + $colorspace->convertColor( + new RgbColor(0, 0, 0) + ) + ); + } +} diff --git a/tests/Colors/Rgb/ColorTest.php b/tests/Colors/Rgb/ColorTest.php index 31e4436b..5c542fa7 100644 --- a/tests/Colors/Rgb/ColorTest.php +++ b/tests/Colors/Rgb/ColorTest.php @@ -2,6 +2,8 @@ namespace Intervention\Image\Tests\Colors\Rgb; +use Intervention\Image\Colors\Cmyk\Color as CmykColor; +use Intervention\Image\Colors\Rgba\Color as RgbaColor; use Intervention\Image\Colors\Rgb\Channels\Red; use Intervention\Image\Colors\Rgb\Channels\Green; use Intervention\Image\Colors\Rgb\Channels\Blue; @@ -65,18 +67,6 @@ class ColorTest extends TestCase $this->assertEquals([1.0, 0.0, 0.2], $color->normalize()); } - public function testToInt(): void - { - $color = new Color(0, 0, 0); - $this->assertEquals(0, $color->toInt()); - - $color = new Color(255, 255, 255); - $this->assertEquals(16777215, $color->toInt()); - - $color = new Color(181, 55, 23); - $this->assertEquals(11876119, $color->toInt()); - } - public function testToString(): void { $color = new Color(181, 55, 23); @@ -86,21 +76,47 @@ class ColorTest extends TestCase public function testToCmyk(): void { $color = new Color(0, 0, 0); - $this->assertEquals([0, 0, 0, 100], $color->toCmyk()->toArray()); + $converted = $color->toCmyk(); + $this->assertInstanceOf(CmykColor::class, $converted); + $this->assertEquals([0, 0, 0, 100], $converted->toArray()); $color = new Color(255, 255, 255); - $this->assertEquals([0, 0, 0, 0], $color->toCmyk()->toArray()); + $converted = $color->toCmyk(); + $this->assertInstanceOf(CmykColor::class, $converted); + $this->assertEquals([0, 0, 0, 0], $converted->toArray()); $color = new Color(255, 0, 0); - $this->assertEquals([0, 100, 100, 0], $color->toCmyk()->toArray()); + $converted = $color->toCmyk(); + $this->assertInstanceOf(CmykColor::class, $converted); + $this->assertEquals([0, 100, 100, 0], $converted->toArray()); $color = new Color(255, 0, 255); - $this->assertEquals([0, 100, 0, 0], $color->toCmyk()->toArray()); + $converted = $color->toCmyk(); + $this->assertInstanceOf(CmykColor::class, $converted); + $this->assertEquals([0, 100, 0, 0], $converted->toArray()); $color = new Color(255, 255, 0); - $this->assertEquals([0, 0, 100, 0], $color->toCmyk()->toArray()); + $converted = $color->toCmyk(); + $this->assertInstanceOf(CmykColor::class, $converted); + $this->assertEquals([0, 0, 100, 0], $converted->toArray()); $color = new Color(255, 204, 204); - $this->assertEquals([0, 20, 20, 0], $color->toCmyk()->toArray()); + $converted = $color->toCmyk(); + $this->assertInstanceOf(CmykColor::class, $converted); + $this->assertEquals([0, 20, 20, 0], $converted->toArray()); + } + + public function testToRgb(): void + { + $color = new Color(181, 55, 23); + $this->assertInstanceOf(Color::class, $color->toRgb()); + } + + public function testToRgba(): void + { + $color = new Color(181, 55, 23); + $converted = $color->toRgba(); + $this->assertInstanceOf(RgbaColor::class, $converted); + $this->assertEquals([181, 55, 23, 255], $converted->toArray()); } } diff --git a/tests/Colors/Rgb/ColorspaceTest.php b/tests/Colors/Rgb/ColorspaceTest.php new file mode 100644 index 00000000..be7808c5 --- /dev/null +++ b/tests/Colors/Rgb/ColorspaceTest.php @@ -0,0 +1,26 @@ +assertInstanceOf( + RgbColor::class, + $colorspace->convertColor( + new CmykColor(0, 0, 0, 0) + ) + ); + } +} diff --git a/tests/Colors/Rgba/ColorTest.php b/tests/Colors/Rgba/ColorTest.php new file mode 100644 index 00000000..cf9b9e61 --- /dev/null +++ b/tests/Colors/Rgba/ColorTest.php @@ -0,0 +1,119 @@ +assertInstanceOf(Color::class, $color); + } + + public function testChannels(): void + { + $color = new Color(10, 20, 30, 255); + $this->assertIsArray($color->channels()); + $this->assertCount(4, $color->channels()); + } + + public function testChannel(): void + { + $color = new Color(10, 20, 30, 255); + $channel = $color->channel(Red::class); + $this->assertInstanceOf(Red::class, $channel); + $this->assertEquals(10, $channel->value()); + $channel = $color->channel(Alpha::class); + $this->assertInstanceOf(Alpha::class, $channel); + $this->assertEquals(255, $channel->value()); + } + + public function testRedGreenBlueAlpha(): void + { + $color = new Color(10, 20, 30, 255); + $this->assertInstanceOf(Red::class, $color->red()); + $this->assertInstanceOf(Green::class, $color->green()); + $this->assertInstanceOf(Blue::class, $color->blue()); + $this->assertInstanceOf(Alpha::class, $color->alpha()); + $this->assertEquals(10, $color->red()->value()); + $this->assertEquals(20, $color->green()->value()); + $this->assertEquals(30, $color->blue()->value()); + $this->assertEquals(255, $color->alpha()->value()); + } + + public function testToArray(): void + { + $color = new Color(10, 20, 30, 255); + $this->assertEquals([10, 20, 30, 255], $color->toArray()); + } + + public function testToHex(): void + { + $color = new Color(181, 55, 23, 0); + $this->assertEquals('b5371700', $color->toHex()); + $this->assertEquals('#b5371700', $color->toHex('#')); + + $color = new Color(181, 55, 23, 255); + $this->assertEquals('b53717', $color->toHex()); + + $color = new Color(181, 55, 23, 204); + $this->assertEquals('b53717cc', $color->toHex()); + } + + public function testNormalize(): void + { + $color = new Color(255, 0, 51, 255); + $this->assertEquals([1.0, 0.0, 0.2, 1.0], $color->normalize()); + $color = new Color(255, 0, 51, 51); + $this->assertEquals([1.0, 0.0, 0.2, 0.2], $color->normalize()); + } + + public function testToString(): void + { + $color = new Color(255, 255, 255, 255); + $this->assertEquals('rgba(255, 255, 255, 1.0)', (string) $color); + + $color = new Color(10, 20, 30, 85); + $this->assertEquals('rgba(10, 20, 30, 0.3)', (string) $color); + } + + public function testToRgba(): void + { + $color = new Color(181, 55, 23, 120); + $converted = $color->toRgba(); + $this->assertInstanceOf(Color::class, $converted); + } + + public function testToRgb(): void + { + $color = new Color(181, 55, 23, 120); + $converted = $color->toRgb(); + $this->assertInstanceOf(RgbColor::class, $converted); + $this->assertEquals([181, 55, 23], $converted->toArray()); + } + + public function testToCmyk(): void + { + $color = new Color(0, 0, 0, 255); + $converted = $color->toCmyk(); + $this->assertInstanceOf(CmykColor::class, $converted); + $this->assertEquals([0, 0, 0, 100], $converted->toArray()); + + $color = new Color(0, 0, 0, 0); + $converted = $color->toCmyk(); + $this->assertInstanceOf(CmykColor::class, $converted); + $this->assertEquals([0, 0, 0, 100], $converted->toArray()); + } +} diff --git a/tests/Colors/Rgba/ColorspaceTest.php b/tests/Colors/Rgba/ColorspaceTest.php new file mode 100644 index 00000000..2ff88b0f --- /dev/null +++ b/tests/Colors/Rgba/ColorspaceTest.php @@ -0,0 +1,26 @@ +assertInstanceOf( + RgbaColor::class, + $colorspace->convertColor( + new CmykColor(0, 0, 0, 0) + ) + ); + } +} From a5cc7e2d41a8a01b823d2461bab619f971d070aa Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 14 Oct 2023 14:42:27 +0200 Subject: [PATCH 279/476] Add channel tests --- tests/Colors/Cmyk/ChannelTest.php | 48 +++++++++++++++++++++++++++++++ tests/Colors/Rgb/ChannelTest.php | 47 ++++++++++++++++++++++++++++++ tests/Colors/Rgba/ChannelTest.php | 48 +++++++++++++++++++++++++++++++ 3 files changed, 143 insertions(+) create mode 100644 tests/Colors/Cmyk/ChannelTest.php create mode 100644 tests/Colors/Rgb/ChannelTest.php create mode 100644 tests/Colors/Rgba/ChannelTest.php diff --git a/tests/Colors/Cmyk/ChannelTest.php b/tests/Colors/Cmyk/ChannelTest.php new file mode 100644 index 00000000..44928abf --- /dev/null +++ b/tests/Colors/Cmyk/ChannelTest.php @@ -0,0 +1,48 @@ +assertInstanceOf(Channel::class, $channel); + } + + public function testValue(): void + { + $channel = new Channel(10); + $this->assertEquals(10, $channel->value()); + } + + public function testNormalize(): void + { + $channel = new Channel(100); + $this->assertEquals(1, $channel->normalize()); + $channel = new Channel(0); + $this->assertEquals(0, $channel->normalize()); + $channel = new Channel(20); + $this->assertEquals(.2, $channel->normalize()); + } + + public function testValidate(): void + { + $this->expectException(ColorException::class); + new Channel(101); + + $this->expectException(ColorException::class); + new Channel(-1); + } +} diff --git a/tests/Colors/Rgb/ChannelTest.php b/tests/Colors/Rgb/ChannelTest.php new file mode 100644 index 00000000..4cc2fadb --- /dev/null +++ b/tests/Colors/Rgb/ChannelTest.php @@ -0,0 +1,47 @@ +assertInstanceOf(Channel::class, $channel); + } + + public function testValue(): void + { + $channel = new Channel(10); + $this->assertEquals(10, $channel->value()); + } + + public function testNormalize(): void + { + $channel = new Channel(255); + $this->assertEquals(1, $channel->normalize()); + $channel = new Channel(0); + $this->assertEquals(0, $channel->normalize()); + $channel = new Channel(51); + $this->assertEquals(.2, $channel->normalize()); + } + + public function testValidate(): void + { + $this->expectException(ColorException::class); + new Channel(256); + + $this->expectException(ColorException::class); + new Channel(-1); + } +} diff --git a/tests/Colors/Rgba/ChannelTest.php b/tests/Colors/Rgba/ChannelTest.php new file mode 100644 index 00000000..a1144e40 --- /dev/null +++ b/tests/Colors/Rgba/ChannelTest.php @@ -0,0 +1,48 @@ +assertInstanceOf(Channel::class, $channel); + } + + public function testValue(): void + { + $channel = new Channel(10); + $this->assertEquals(10, $channel->value()); + } + + public function testNormalize(): void + { + $channel = new Channel(255); + $this->assertEquals(1, $channel->normalize()); + $channel = new Channel(0); + $this->assertEquals(0, $channel->normalize()); + $channel = new Channel(51); + $this->assertEquals(.2, $channel->normalize()); + } + + public function testValidate(): void + { + $this->expectException(ColorException::class); + new Channel(256); + + $this->expectException(ColorException::class); + new Channel(-1); + } +} From dbbcded7a46049233e68831ecf4a6c469f143b5b Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 15 Oct 2023 10:39:15 +0200 Subject: [PATCH 280/476] Replace driver color classes --- docker-compose.yml | 2 +- src/Colors/Rgb/Parser.php | 193 ++++++++++++++++++ src/Colors/Rgba/Parser.php | 55 +++++ src/Drivers/Gd/Decoders/HexColorDecoder.php | 27 ++- .../Gd/Decoders/HtmlColorNameDecoder.php | 18 +- .../Gd/Decoders/RgbArrayColorDecoder.php | 38 ---- .../Gd/Decoders/RgbStringColorDecoder.php | 26 +-- src/Drivers/Gd/InputHandler.php | 5 +- .../Imagick/Decoders/HexColorDecoder.php | 27 ++- .../Imagick/Decoders/HtmlColorNameDecoder.php | 15 +- .../Imagick/Decoders/RgbArrayColorDecoder.php | 36 ---- .../Decoders/RgbStringColorDecoder.php | 20 +- src/Drivers/Imagick/InputHandler.php | 7 +- src/Traits/CanReadHtmlColorNames.php | 165 --------------- tests/Colors/Rgb/ParserTest.php | 60 ++++++ tests/Colors/Rgba/ParserTest.php | 68 ++++++ tests/Drivers/Abstract/AbstractColorTest.php | 39 ---- tests/Drivers/Gd/ColorTest.php | 88 -------- .../Gd/Decoders/RgbArrayColorDecoderTest.php | 25 --- tests/Drivers/Gd/InputHandlerTest.php | 77 +++---- tests/Drivers/Imagick/ColorTest.php | 112 ---------- .../Decoders/RgbArrayColorDecoderTest.php | 25 --- tests/Drivers/Imagick/InputHandlerTest.php | 55 ++++- 23 files changed, 545 insertions(+), 638 deletions(-) create mode 100644 src/Colors/Rgb/Parser.php create mode 100644 src/Colors/Rgba/Parser.php delete mode 100644 src/Drivers/Gd/Decoders/RgbArrayColorDecoder.php delete mode 100644 src/Drivers/Imagick/Decoders/RgbArrayColorDecoder.php delete mode 100644 src/Traits/CanReadHtmlColorNames.php create mode 100644 tests/Colors/Rgb/ParserTest.php create mode 100644 tests/Colors/Rgba/ParserTest.php delete mode 100644 tests/Drivers/Abstract/AbstractColorTest.php delete mode 100644 tests/Drivers/Gd/ColorTest.php delete mode 100644 tests/Drivers/Gd/Decoders/RgbArrayColorDecoderTest.php delete mode 100644 tests/Drivers/Imagick/ColorTest.php delete mode 100644 tests/Drivers/Imagick/Decoders/RgbArrayColorDecoderTest.php diff --git a/docker-compose.yml b/docker-compose.yml index 8d90fbff..e32993d7 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -4,7 +4,7 @@ services: tests: build: ./ working_dir: /project - command: bash -c "composer install && ./vendor/bin/phpunit -vvv" + command: bash -c "composer install && ./vendor/bin/phpunit -vvv --filter=InputHandlerTest" volumes: - ./:/project analysis: diff --git a/src/Colors/Rgb/Parser.php b/src/Colors/Rgb/Parser.php new file mode 100644 index 00000000..be787dab --- /dev/null +++ b/src/Colors/Rgb/Parser.php @@ -0,0 +1,193 @@ +[0-9a-f]{3}|[0-9a-f]{6})$/i'; + $result = preg_match($pattern, $input, $matches); + + if ($result !== 1) { + throw new ColorException('Unable to parse color'); + } + + $matches = match (strlen($matches['hex'])) { + 3 => str_split($matches['hex']), + 6 => str_split($matches['hex'], 2), + default => throw new ColorException('Unable to parse color'), + }; + + return new Color( + strlen($matches[0]) == '1' ? hexdec($matches[0] . $matches[0]) : hexdec($matches[0]), + strlen($matches[1]) == '1' ? hexdec($matches[1] . $matches[1]) : hexdec($matches[1]), + strlen($matches[2]) == '1' ? hexdec($matches[2] . $matches[2]) : hexdec($matches[2]), + ); + } + + public static function fromString(string $input): Color + { + $pattern = '/^rgb\((?P[0-9]{1,3}), ?(?P[0-9]{1,3}), ?(?P[0-9]{1,3})\)$/'; + $result = preg_match($pattern, $input, $matches); + + if ($result !== 1) { + throw new ColorException('Unable to parse color'); + } + + return new Color($matches['r'], $matches['g'], $matches['b']); + } + + public static function fromName(string $input): Color + { + $names = [ + 'lightsalmon' => '#FFA07A', + 'salmon' => '#FA8072', + 'darksalmon' => '#E9967A', + 'lightcoral' => '#F08080', + 'indianred' => '#CD5C5C', + 'crimson' => '#DC143C', + 'firebrick' => '#B22222', + 'red' => '#FF0000', + 'darkred' => '#8B0000', + 'coral' => '#FF7F50', + 'tomato' => '#FF6347', + 'orangered' => '#FF4500', + 'gold' => '#FFD700', + 'orange' => '#FFA500', + 'darkorange' => '#FF8C00', + 'lightyellow' => '#FFFFE0', + 'lemonchiffon' => '#FFFACD', + 'lightgoldenrodyellow' => '#FAFAD2', + 'papayawhip' => '#FFEFD5', + 'moccasin' => '#FFE4B5', + 'peachpuff' => '#FFDAB9', + 'palegoldenrod' => '#EEE8AA', + 'khaki' => '#F0E68C', + 'darkkhaki' => '#BDB76B', + 'yellow' => '#FFFF00', + 'lawngreen' => '#7CFC00', + 'chartreuse' => '#7FFF00', + 'limegreen' => '#32CD32', + 'lime' => '#00FF00', + 'forestgreen' => '#228B22', + 'green' => '#008000', + 'darkgreen' => '#006400', + 'greenyellow' => '#ADFF2F', + 'yellowgreen' => '#9ACD32', + 'springgreen' => '#00FF7F', + 'mediumspringgreen' => '#00FA9A', + 'lightgreen' => '#90EE90', + 'palegreen' => '#98FB98', + 'darkseagreen' => '#8FBC8F', + 'mediumseagre' => 'en #3CB371', + 'seagreen' => '#2E8B57', + 'olive' => '#808000', + 'darkolivegreen' => '#556B2F', + 'olivedrab' => '#6B8E23', + 'lightcyan' => '#E0FFFF', + 'cyan' => '#00FFFF', + 'aqua' => '#00FFFF', + 'aquamarine' => '#7FFFD4', + 'mediumaquamarine' => '#66CDAA', + 'paleturquoise' => '#AFEEEE', + 'turquoise' => '#40E0D0', + 'mediumturquoise' => '#48D1CC', + 'darkturquoise' => '#00CED1', + 'lightseagreen' => '#20B2AA', + 'cadetblue' => '#5F9EA0', + 'darkcyan' => '#008B8B', + 'teal' => '#008080', + 'powderblue' => '#B0E0E6', + 'lightblue' => '#ADD8E6', + 'lightskyblue' => '#87CEFA', + 'skyblue' => '#87CEEB', + 'deepskyblue' => '#00BFFF', + 'lightsteelblue' => '#B0C4DE', + 'dodgerblue' => '#1E90FF', + 'cornflowerblue' => '#6495ED', + 'steelblue' => '#4682B4', + 'royalblue' => '#4169E1', + 'blue' => '#0000FF', + 'mediumblue' => '#0000CD', + 'darkblue' => '#00008B', + 'navy' => '#000080', + 'midnightblue' => '#191970', + 'mediumslateblue' => '#7B68EE', + 'slateblue' => '#6A5ACD', + 'darkslateblue' => '#483D8B', + 'lavender' => '#E6E6FA', + 'thistle' => '#D8BFD8', + 'plum' => '#DDA0DD', + 'violet' => '#EE82EE', + 'orchid' => '#DA70D6', + 'fuchsia' => '#FF00FF', + 'magenta' => '#FF00FF', + 'mediumorchid' => '#BA55D3', + 'mediumpurple' => '#9370DB', + 'blueviolet' => '#8A2BE2', + 'darkviolet' => '#9400D3', + 'darkorchid' => '#9932CC', + 'darkmagenta' => '#8B008B', + 'purple' => '#800080', + 'indigo' => '#4B0082', + 'pink' => '#FFC0CB', + 'lightpink' => '#FFB6C1', + 'hotpink' => '#FF69B4', + 'deeppink' => '#FF1493', + 'palevioletred' => '#DB7093', + 'mediumvioletred' => '#C71585', + 'white' => '#FFFFFF', + 'snow' => '#FFFAFA', + 'honeydew' => '#F0FFF0', + 'mintcream' => '#F5FFFA', + 'azure' => '#F0FFFF', + 'aliceblue' => '#F0F8FF', + 'ghostwhite' => '#F8F8FF', + 'whitesmoke' => '#F5F5F5', + 'seashell' => '#FFF5EE', + 'beige' => '#F5F5DC', + 'oldlace' => '#FDF5E6', + 'floralwhite' => '#FFFAF0', + 'ivory' => '#FFFFF0', + 'antiquewhite' => '#FAEBD7', + 'linen' => '#FAF0E6', + 'lavenderblush' => '#FFF0F5', + 'mistyrose' => '#FFE4E1', + 'gainsboro' => '#DCDCDC', + 'lightgray' => '#D3D3D3', + 'silver' => '#C0C0C0', + 'darkgray' => '#A9A9A9', + 'gray' => '#808080', + 'dimgray' => '#696969', + 'lightslategray' => '#778899', + 'slategray' => '#708090', + 'darkslategray' => '#2F4F4F', + 'black' => '#000000', + 'cornsilk' => '#FFF8DC', + 'blanchedalmond' => '#FFEBCD', + 'bisque' => '#FFE4C4', + 'navajowhite' => '#FFDEAD', + 'wheat' => '#F5DEB3', + 'burlywood' => '#DEB887', + 'tan' => '#D2B48C', + 'rosybrown' => '#BC8F8F', + 'sandybrown' => '#F4A460', + 'goldenrod' => '#DAA520', + 'peru' => '#CD853F', + 'chocolate' => '#D2691E', + 'saddlebrown' => '#8B4513', + 'sienna' => '#A0522D', + 'brown' => '#A52A2A', + 'maroon' => '#800000', + ]; + + if (!array_key_exists(strtolower($input), $names)) { + throw new ColorException('Unable to parse color'); + } + + return static::fromHex($names[strtolower($input)]); + } +} diff --git a/src/Colors/Rgba/Parser.php b/src/Colors/Rgba/Parser.php new file mode 100644 index 00000000..a567f70e --- /dev/null +++ b/src/Colors/Rgba/Parser.php @@ -0,0 +1,55 @@ +toRgba(); + } catch (ColorException $e) { + // move on + } + + $pattern = '/^#?(?P[0-9a-f]{4}|[0-9a-f]{8})$/i'; + $result = preg_match($pattern, $input, $matches); + + if ($result !== 1) { + throw new ColorException('Unable to parse color'); + } + + $matches = match (strlen($matches['hex'])) { + 4 => str_split($matches['hex']), + 8 => str_split($matches['hex'], 2), + default => throw new ColorException('Unable to parse color'), + }; + + return new Color( + strlen($matches[0]) == '1' ? hexdec($matches[0] . $matches[0]) : hexdec($matches[0]), + strlen($matches[1]) == '1' ? hexdec($matches[1] . $matches[1]) : hexdec($matches[1]), + strlen($matches[2]) == '1' ? hexdec($matches[2] . $matches[2]) : hexdec($matches[2]), + strlen($matches[3]) == '1' ? hexdec($matches[3] . $matches[3]) : hexdec($matches[3]), + ); + } + + public static function fromString(string $input): Color + { + $pattern = '/^rgba\((?P[0-9]{1,3}), *(?P[0-9]{1,3}), *(?P[0-9]{1,3}), *(?P((1|0))?(\.[0-9]+)?)\)$/'; + $result = preg_match($pattern, $input, $matches); + + if ($result !== 1) { + throw new ColorException('Unable to parse color'); + } + + return new Color( + $matches['r'], + $matches['g'], + $matches['b'], + intval(round(floatval($matches['a']) * 255)) + ); + } +} diff --git a/src/Drivers/Gd/Decoders/HexColorDecoder.php b/src/Drivers/Gd/Decoders/HexColorDecoder.php index 8cb6b27b..3371659a 100644 --- a/src/Drivers/Gd/Decoders/HexColorDecoder.php +++ b/src/Drivers/Gd/Decoders/HexColorDecoder.php @@ -2,12 +2,16 @@ namespace Intervention\Image\Drivers\Gd\Decoders; +use Intervention\Image\Colors\Rgb\Parser as RgbColorParser; +use Intervention\Image\Colors\Rgba\Parser as RgbaColorParser; +use Intervention\Image\Drivers\Abstract\Decoders\AbstractDecoder; +use Intervention\Image\Exceptions\ColorException; use Intervention\Image\Exceptions\DecoderException; use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\DecoderInterface; use Intervention\Image\Interfaces\ImageInterface; -class HexColorDecoder extends RgbArrayColorDecoder implements DecoderInterface +class HexColorDecoder extends AbstractDecoder implements DecoderInterface { public function decode($input): ImageInterface|ColorInterface { @@ -15,17 +19,18 @@ class HexColorDecoder extends RgbArrayColorDecoder implements DecoderInterface throw new DecoderException('Unable to decode input'); } - $pattern = '/^#?([a-f0-9]{1,2})([a-f0-9]{1,2})([a-f0-9]{1,2})$/i'; - $result = preg_match($pattern, $input, $matches); - - if ($result !== 1) { - throw new DecoderException('Unable to decode input'); + try { + return RgbColorParser::fromHex($input); + } catch (ColorException $e) { + # code ... } - return parent::decode([ - strlen($matches[1]) == '1' ? hexdec($matches[1] . $matches[1]) : hexdec($matches[1]), - strlen($matches[2]) == '1' ? hexdec($matches[2] . $matches[2]) : hexdec($matches[2]), - strlen($matches[3]) == '1' ? hexdec($matches[3] . $matches[3]) : hexdec($matches[3]), - ]); + try { + return RgbaColorParser::fromHex($input); + } catch (ColorException $e) { + # code ... + } + + throw new DecoderException('Unable to decode input'); } } diff --git a/src/Drivers/Gd/Decoders/HtmlColorNameDecoder.php b/src/Drivers/Gd/Decoders/HtmlColorNameDecoder.php index 59ed6fd2..fd1d9dce 100644 --- a/src/Drivers/Gd/Decoders/HtmlColorNameDecoder.php +++ b/src/Drivers/Gd/Decoders/HtmlColorNameDecoder.php @@ -2,27 +2,27 @@ namespace Intervention\Image\Drivers\Gd\Decoders; +use Intervention\Image\Colors\Rgb\Parser; +use Intervention\Image\Drivers\Abstract\Decoders\AbstractDecoder; +use Intervention\Image\Exceptions\ColorException; use Intervention\Image\Exceptions\DecoderException; use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\ImageInterface; -use Intervention\Image\Traits\CanReadHtmlColorNames; -class HtmlColorNameDecoder extends HexColorDecoder +class HtmlColorNameDecoder extends AbstractDecoder { - use CanReadHtmlColorNames; - public function decode($input): ImageInterface|ColorInterface { if (!is_string($input)) { throw new DecoderException('Unable to decode input'); } - $hexcolor = $this->hexColorFromColorName($input); - - if (empty($hexcolor)) { - throw new DecoderException('Unable to decode input'); + try { + return Parser::fromName($input); + } catch (ColorException $e) { + # code ... } - return parent::decode($hexcolor); + throw new DecoderException('Unable to decode input'); } } diff --git a/src/Drivers/Gd/Decoders/RgbArrayColorDecoder.php b/src/Drivers/Gd/Decoders/RgbArrayColorDecoder.php deleted file mode 100644 index 8b25f691..00000000 --- a/src/Drivers/Gd/Decoders/RgbArrayColorDecoder.php +++ /dev/null @@ -1,38 +0,0 @@ -isValidColorArray($input)) { - throw new DecoderException('Unable to decode input'); - } - - if (count($input) === 3) { - $input[] = 1; - } - - list($r, $g, $b, $a) = $input; - - return new Color( - ($this->opacityToGdAlpha($a) << 24) + ($r << 16) + ($g << 8) + $b - ); - } - - protected function opacityToGdAlpha(float $opacity): int - { - return intval(round($opacity * 127 * -1 + 127)); - } -} diff --git a/src/Drivers/Gd/Decoders/RgbStringColorDecoder.php b/src/Drivers/Gd/Decoders/RgbStringColorDecoder.php index 03c46b96..8707375e 100644 --- a/src/Drivers/Gd/Decoders/RgbStringColorDecoder.php +++ b/src/Drivers/Gd/Decoders/RgbStringColorDecoder.php @@ -2,12 +2,16 @@ namespace Intervention\Image\Drivers\Gd\Decoders; +use Intervention\Image\Colors\Rgb\Parser as RgbColorParser; +use Intervention\Image\Colors\Rgba\Parser as RgbaColorParser; +use Intervention\Image\Drivers\Abstract\Decoders\AbstractDecoder; +use Intervention\Image\Exceptions\ColorException; use Intervention\Image\Exceptions\DecoderException; use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\DecoderInterface; use Intervention\Image\Interfaces\ImageInterface; -class RgbStringColorDecoder extends RgbArrayColorDecoder implements DecoderInterface +class RgbStringColorDecoder extends AbstractDecoder implements DecoderInterface { public function decode($input): ImageInterface|ColorInterface { @@ -15,20 +19,16 @@ class RgbStringColorDecoder extends RgbArrayColorDecoder implements DecoderInter throw new DecoderException('Unable to decode input'); } - if (substr($input, 0, 3) !== 'rgb') { - throw new DecoderException('Unable to decode input'); + try { + return RgbColorParser::fromString($input); + } catch (ColorException $e) { + # code ... } - // rgb string like rgb(102, 200, 0) - $pattern = "/^rgb ?\((?P[0-9]{1,3}), ?(?P[0-9]{1,3}), ?(?P[0-9]{1,3})\)$/i"; - if ((bool) preg_match($pattern, $input, $matches)) { - return parent::decode([$matches['r'], $matches['g'], $matches['b']]); - } - - // rgba string like "rgba(200, 10, 30, 0.5)" - $pattern = "/^rgba ?\(((?P[0-9]{1,3})), ?((?P[0-9]{1,3})), ?((?P[0-9]{1,3})), ?(?P[0-9.]{1,4})\)$/i"; - if ((bool) preg_match($pattern, $input, $matches)) { - return parent::decode([$matches['r'], $matches['g'], $matches['b'], $matches['a']]); + try { + return RgbaColorParser::fromString($input); + } catch (ColorException $e) { + # code ... } throw new DecoderException('Unable to decode input'); diff --git a/src/Drivers/Gd/InputHandler.php b/src/Drivers/Gd/InputHandler.php index 9fec013b..42785e59 100644 --- a/src/Drivers/Gd/InputHandler.php +++ b/src/Drivers/Gd/InputHandler.php @@ -9,11 +9,10 @@ class InputHandler extends AbstractInputHandler protected $decoders = [ Decoders\ImageObjectDecoder::class, Decoders\FilePointerImageDecoder::class, - Decoders\RgbArrayColorDecoder::class, Decoders\HtmlColorNameDecoder::class, - Decoders\RgbStringColorDecoder::class, Decoders\HexColorDecoder::class, - Decoders\TransparentColorDecoder::class, + Decoders\RgbStringColorDecoder::class, + // Decoders\TransparentColorDecoder::class, Decoders\FilePathImageDecoder::class, Decoders\BinaryImageDecoder::class, Decoders\DataUriImageDecoder::class, diff --git a/src/Drivers/Imagick/Decoders/HexColorDecoder.php b/src/Drivers/Imagick/Decoders/HexColorDecoder.php index 37498320..e1e687b3 100644 --- a/src/Drivers/Imagick/Decoders/HexColorDecoder.php +++ b/src/Drivers/Imagick/Decoders/HexColorDecoder.php @@ -2,12 +2,16 @@ namespace Intervention\Image\Drivers\Imagick\Decoders; +use Intervention\Image\Colors\Rgb\Parser as RgbColorParser; +use Intervention\Image\Colors\Rgba\Parser as RgbaColorParser; +use Intervention\Image\Drivers\Abstract\Decoders\AbstractDecoder; +use Intervention\Image\Exceptions\ColorException; use Intervention\Image\Exceptions\DecoderException; use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\DecoderInterface; use Intervention\Image\Interfaces\ImageInterface; -class HexColorDecoder extends RgbArrayColorDecoder implements DecoderInterface +class HexColorDecoder extends AbstractDecoder implements DecoderInterface { public function decode($input): ImageInterface|ColorInterface { @@ -15,17 +19,18 @@ class HexColorDecoder extends RgbArrayColorDecoder implements DecoderInterface throw new DecoderException('Unable to decode input'); } - $pattern = '/^#?([a-f0-9]{1,2})([a-f0-9]{1,2})([a-f0-9]{1,2})$/i'; - $result = preg_match($pattern, $input, $matches); - - if ($result !== 1) { - throw new DecoderException('Unable to decode input'); + try { + return RgbColorParser::fromHex($input); + } catch (ColorException $e) { + # code ... } - return parent::decode([ - strlen($matches[1]) == '1' ? hexdec($matches[1] . $matches[1]) : hexdec($matches[1]), - strlen($matches[2]) == '1' ? hexdec($matches[2] . $matches[2]) : hexdec($matches[2]), - strlen($matches[3]) == '1' ? hexdec($matches[3] . $matches[3]) : hexdec($matches[3]), - ]); + try { + return RgbaColorParser::fromHex($input); + } catch (ColorException $e) { + # code ... + } + + throw new DecoderException('Unable to decode input'); } } diff --git a/src/Drivers/Imagick/Decoders/HtmlColorNameDecoder.php b/src/Drivers/Imagick/Decoders/HtmlColorNameDecoder.php index fba912ea..e8cb70a6 100644 --- a/src/Drivers/Imagick/Decoders/HtmlColorNameDecoder.php +++ b/src/Drivers/Imagick/Decoders/HtmlColorNameDecoder.php @@ -2,27 +2,26 @@ namespace Intervention\Image\Drivers\Imagick\Decoders; +use Intervention\Image\Colors\Rgb\Parser; +use Intervention\Image\Exceptions\ColorException; use Intervention\Image\Exceptions\DecoderException; use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\ImageInterface; -use Intervention\Image\Traits\CanReadHtmlColorNames; class HtmlColorNameDecoder extends HexColorDecoder { - use CanReadHtmlColorNames; - public function decode($input): ImageInterface|ColorInterface { if (!is_string($input)) { throw new DecoderException('Unable to decode input'); } - $hexcolor = $this->hexColorFromColorName($input); - - if (empty($hexcolor)) { - throw new DecoderException('Unable to decode input'); + try { + return Parser::fromName($input); + } catch (ColorException $e) { + # code ... } - return parent::decode($hexcolor); + throw new DecoderException('Unable to decode input'); } } diff --git a/src/Drivers/Imagick/Decoders/RgbArrayColorDecoder.php b/src/Drivers/Imagick/Decoders/RgbArrayColorDecoder.php deleted file mode 100644 index 2e924c87..00000000 --- a/src/Drivers/Imagick/Decoders/RgbArrayColorDecoder.php +++ /dev/null @@ -1,36 +0,0 @@ -isValidColorArray($input)) { - throw new DecoderException('Unable to decode input'); - } - - if (count($input) === 3) { - $input[] = 1; - } - - list($r, $g, $b, $a) = $input; - - $pixel = new ImagickPixel( - sprintf('rgba(%d, %d, %d, %.2F)', $r, $g, $b, $a) - ); - - return new Color($pixel); - } -} diff --git a/src/Drivers/Imagick/Decoders/RgbStringColorDecoder.php b/src/Drivers/Imagick/Decoders/RgbStringColorDecoder.php index 7b036033..cd254960 100644 --- a/src/Drivers/Imagick/Decoders/RgbStringColorDecoder.php +++ b/src/Drivers/Imagick/Decoders/RgbStringColorDecoder.php @@ -2,10 +2,10 @@ namespace Intervention\Image\Drivers\Imagick\Decoders; -use ImagickPixel; -use ImagickPixelException; +use Intervention\Image\Colors\Rgb\Parser as RgbColorParser; +use Intervention\Image\Colors\Rgba\Parser as RgbaColorParser; use Intervention\Image\Drivers\Abstract\Decoders\AbstractDecoder; -use Intervention\Image\Drivers\Imagick\Color; +use Intervention\Image\Exceptions\ColorException; use Intervention\Image\Exceptions\DecoderException; use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\DecoderInterface; @@ -19,16 +19,18 @@ class RgbStringColorDecoder extends AbstractDecoder implements DecoderInterface throw new DecoderException('Unable to decode input'); } - if (substr($input, 0, 3) !== 'rgb') { - throw new DecoderException('Unable to decode input'); + try { + return RgbColorParser::fromString($input); + } catch (ColorException $e) { + # code ... } try { - $pixel = new ImagickPixel($input); - } catch (ImagickPixelException $e) { - throw new DecoderException('Unable to decode input'); + return RgbaColorParser::fromString($input); + } catch (ColorException $e) { + # code ... } - return new Color($pixel); + throw new DecoderException('Unable to decode input'); } } diff --git a/src/Drivers/Imagick/InputHandler.php b/src/Drivers/Imagick/InputHandler.php index 8b00c35e..0fc798ff 100644 --- a/src/Drivers/Imagick/InputHandler.php +++ b/src/Drivers/Imagick/InputHandler.php @@ -9,14 +9,13 @@ class InputHandler extends AbstractInputHandler protected $decoders = [ Decoders\ImageObjectDecoder::class, Decoders\FilePointerImageDecoder::class, - Decoders\RgbArrayColorDecoder::class, - Decoders\HexColorDecoder::class, Decoders\HtmlColorNameDecoder::class, + Decoders\HexColorDecoder::class, Decoders\RgbStringColorDecoder::class, - Decoders\TransparentColorDecoder::class, + // Decoders\TransparentColorDecoder::class, Decoders\FilePathImageDecoder::class, Decoders\BinaryImageDecoder::class, Decoders\DataUriImageDecoder::class, - Decoders\Base64ImageDecoder::class + Decoders\Base64ImageDecoder::class, ]; } diff --git a/src/Traits/CanReadHtmlColorNames.php b/src/Traits/CanReadHtmlColorNames.php deleted file mode 100644 index c02b4367..00000000 --- a/src/Traits/CanReadHtmlColorNames.php +++ /dev/null @@ -1,165 +0,0 @@ - '#FFA07A', - 'salmon' => '#FA8072', - 'darksalmon' => '#E9967A', - 'lightcoral' => '#F08080', - 'indianred' => '#CD5C5C', - 'crimson' => '#DC143C', - 'firebrick' => '#B22222', - 'red' => '#FF0000', - 'darkred' => '#8B0000', - 'coral' => '#FF7F50', - 'tomato' => '#FF6347', - 'orangered' => '#FF4500', - 'gold' => '#FFD700', - 'orange' => '#FFA500', - 'darkorange' => '#FF8C00', - 'lightyellow' => '#FFFFE0', - 'lemonchiffon' => '#FFFACD', - 'lightgoldenrodyellow' => '#FAFAD2', - 'papayawhip' => '#FFEFD5', - 'moccasin' => '#FFE4B5', - 'peachpuff' => '#FFDAB9', - 'palegoldenrod' => '#EEE8AA', - 'khaki' => '#F0E68C', - 'darkkhaki' => '#BDB76B', - 'yellow' => '#FFFF00', - 'lawngreen' => '#7CFC00', - 'chartreuse' => '#7FFF00', - 'limegreen' => '#32CD32', - 'lime' => '#00FF00', - 'forestgreen' => '#228B22', - 'green' => '#008000', - 'darkgreen' => '#006400', - 'greenyellow' => '#ADFF2F', - 'yellowgreen' => '#9ACD32', - 'springgreen' => '#00FF7F', - 'mediumspringgreen' => '#00FA9A', - 'lightgreen' => '#90EE90', - 'palegreen' => '#98FB98', - 'darkseagreen' => '#8FBC8F', - 'mediumseagre' => 'en #3CB371', - 'seagreen' => '#2E8B57', - 'olive' => '#808000', - 'darkolivegreen' => '#556B2F', - 'olivedrab' => '#6B8E23', - 'lightcyan' => '#E0FFFF', - 'cyan' => '#00FFFF', - 'aqua' => '#00FFFF', - 'aquamarine' => '#7FFFD4', - 'mediumaquamarine' => '#66CDAA', - 'paleturquoise' => '#AFEEEE', - 'turquoise' => '#40E0D0', - 'mediumturquoise' => '#48D1CC', - 'darkturquoise' => '#00CED1', - 'lightseagreen' => '#20B2AA', - 'cadetblue' => '#5F9EA0', - 'darkcyan' => '#008B8B', - 'teal' => '#008080', - 'powderblue' => '#B0E0E6', - 'lightblue' => '#ADD8E6', - 'lightskyblue' => '#87CEFA', - 'skyblue' => '#87CEEB', - 'deepskyblue' => '#00BFFF', - 'lightsteelblue' => '#B0C4DE', - 'dodgerblue' => '#1E90FF', - 'cornflowerblue' => '#6495ED', - 'steelblue' => '#4682B4', - 'royalblue' => '#4169E1', - 'blue' => '#0000FF', - 'mediumblue' => '#0000CD', - 'darkblue' => '#00008B', - 'navy' => '#000080', - 'midnightblue' => '#191970', - 'mediumslateblue' => '#7B68EE', - 'slateblue' => '#6A5ACD', - 'darkslateblue' => '#483D8B', - 'lavender' => '#E6E6FA', - 'thistle' => '#D8BFD8', - 'plum' => '#DDA0DD', - 'violet' => '#EE82EE', - 'orchid' => '#DA70D6', - 'fuchsia' => '#FF00FF', - 'magenta' => '#FF00FF', - 'mediumorchid' => '#BA55D3', - 'mediumpurple' => '#9370DB', - 'blueviolet' => '#8A2BE2', - 'darkviolet' => '#9400D3', - 'darkorchid' => '#9932CC', - 'darkmagenta' => '#8B008B', - 'purple' => '#800080', - 'indigo' => '#4B0082', - 'pink' => '#FFC0CB', - 'lightpink' => '#FFB6C1', - 'hotpink' => '#FF69B4', - 'deeppink' => '#FF1493', - 'palevioletred' => '#DB7093', - 'mediumvioletred' => '#C71585', - 'white' => '#FFFFFF', - 'snow' => '#FFFAFA', - 'honeydew' => '#F0FFF0', - 'mintcream' => '#F5FFFA', - 'azure' => '#F0FFFF', - 'aliceblue' => '#F0F8FF', - 'ghostwhite' => '#F8F8FF', - 'whitesmoke' => '#F5F5F5', - 'seashell' => '#FFF5EE', - 'beige' => '#F5F5DC', - 'oldlace' => '#FDF5E6', - 'floralwhite' => '#FFFAF0', - 'ivory' => '#FFFFF0', - 'antiquewhite' => '#FAEBD7', - 'linen' => '#FAF0E6', - 'lavenderblush' => '#FFF0F5', - 'mistyrose' => '#FFE4E1', - 'gainsboro' => '#DCDCDC', - 'lightgray' => '#D3D3D3', - 'silver' => '#C0C0C0', - 'darkgray' => '#A9A9A9', - 'gray' => '#808080', - 'dimgray' => '#696969', - 'lightslategray' => '#778899', - 'slategray' => '#708090', - 'darkslategray' => '#2F4F4F', - 'black' => '#000000', - 'cornsilk' => '#FFF8DC', - 'blanchedalmond' => '#FFEBCD', - 'bisque' => '#FFE4C4', - 'navajowhite' => '#FFDEAD', - 'wheat' => '#F5DEB3', - 'burlywood' => '#DEB887', - 'tan' => '#D2B48C', - 'rosybrown' => '#BC8F8F', - 'sandybrown' => '#F4A460', - 'goldenrod' => '#DAA520', - 'peru' => '#CD853F', - 'chocolate' => '#D2691E', - 'saddlebrown' => '#8B4513', - 'sienna' => '#A0522D', - 'brown' => '#A52A2A', - 'maroon' => '#800000', - ]; - - /** - * Transform given html color name to hex color - * or return null, if color name doesn't exist. - * - * @param string $name - * @return null|string - */ - public function hexColorFromColorName(string $name): ?string - { - $name = strtolower($name); - if (!array_key_exists($name, $this->color_names)) { - return null; - } - - return $this->color_names[$name]; - } -} diff --git a/tests/Colors/Rgb/ParserTest.php b/tests/Colors/Rgb/ParserTest.php new file mode 100644 index 00000000..08389cbf --- /dev/null +++ b/tests/Colors/Rgb/ParserTest.php @@ -0,0 +1,60 @@ +assertInstanceOf(Color::class, $color); + $this->assertEquals([204, 204, 204], $color->toArray()); + + $color = Parser::fromHex('cccccc'); + $this->assertInstanceOf(Color::class, $color); + $this->assertEquals([204, 204, 204], $color->toArray()); + + $color = Parser::fromHex('#cccccc'); + $this->assertInstanceOf(Color::class, $color); + $this->assertEquals([204, 204, 204], $color->toArray()); + + $this->expectException(ColorException::class); + (new Parser())->fromHex('cccccccc'); + } + + public function testFromString(): void + { + $color = Parser::fromString('rgb(204, 204, 204)'); + $this->assertInstanceOf(Color::class, $color); + $this->assertEquals([204, 204, 204], $color->toArray()); + + $color = Parser::fromString('rgb(204,204,204)'); + $this->assertInstanceOf(Color::class, $color); + $this->assertEquals([204, 204, 204], $color->toArray()); + + $this->expectException(ColorException::class); + (new Parser())->fromString('rgb(204,204,204,1)'); + + $this->expectException(ColorException::class); + (new Parser())->fromString('rgb(120)'); + + $this->expectException(ColorException::class); + (new Parser())->fromString('rgba(204,204,204,1)'); + } + + public function testFromName(): void + { + $color = Parser::fromName('salmon'); + $this->assertInstanceOf(Color::class, $color); + $this->assertEquals('fa8072', $color->toHex()); + } +} diff --git a/tests/Colors/Rgba/ParserTest.php b/tests/Colors/Rgba/ParserTest.php new file mode 100644 index 00000000..0986c797 --- /dev/null +++ b/tests/Colors/Rgba/ParserTest.php @@ -0,0 +1,68 @@ +assertInstanceOf(Color::class, $color); + $this->assertEquals([204, 204, 204, 255], $color->toArray()); + + $color = Parser::fromHex('cccccc'); + $this->assertInstanceOf(Color::class, $color); + $this->assertEquals([204, 204, 204, 255], $color->toArray()); + + $color = Parser::fromHex('#cccccc'); + $this->assertInstanceOf(Color::class, $color); + $this->assertEquals([204, 204, 204, 255], $color->toArray()); + + $color = Parser::fromHex('#cccccccc'); + $this->assertInstanceOf(Color::class, $color); + $this->assertEquals([204, 204, 204, 204], $color->toArray()); + + $color = Parser::fromHex('#cccc'); + $this->assertInstanceOf(Color::class, $color); + $this->assertEquals([204, 204, 204, 204], $color->toArray()); + + $color = Parser::fromHex('cccccccc'); + $this->assertInstanceOf(Color::class, $color); + $this->assertEquals([204, 204, 204, 204], $color->toArray()); + + $color = Parser::fromHex('cccc'); + $this->assertInstanceOf(Color::class, $color); + $this->assertEquals([204, 204, 204, 204], $color->toArray()); + } + + public function testFromString(): void + { + $color = Parser::fromString('rgba(204, 204, 204, 1)'); + $this->assertInstanceOf(Color::class, $color); + $this->assertEquals([204, 204, 204, 255], $color->toArray()); + + $color = Parser::fromString('rgba(204,204,204,1.0)'); + $this->assertInstanceOf(Color::class, $color); + $this->assertEquals([204, 204, 204, 255], $color->toArray()); + + $color = Parser::fromString('rgba(204,204,204,0.2)'); + $this->assertInstanceOf(Color::class, $color); + $this->assertEquals([204, 204, 204, 51], $color->toArray()); + + $color = Parser::fromString('rgba(204,204, 204, .2)'); + $this->assertInstanceOf(Color::class, $color); + $this->assertEquals([204, 204, 204, 51], $color->toArray()); + + $this->expectException(ColorException::class); + $color = Parser::fromString('rgba(204, 204, 204, 1.2)'); + } +} diff --git a/tests/Drivers/Abstract/AbstractColorTest.php b/tests/Drivers/Abstract/AbstractColorTest.php deleted file mode 100644 index d419c157..00000000 --- a/tests/Drivers/Abstract/AbstractColorTest.php +++ /dev/null @@ -1,39 +0,0 @@ -makePartial(); - $color->shouldReceive('red')->andReturn(255); - $color->shouldReceive('green')->andReturn(0); - $color->shouldReceive('blue')->andReturn(0); - - $this->assertEquals('ff0000', $color->toHex()); - $this->assertEquals('#ff0000', $color->toHex('#')); - } - - public function testIsGreyscale(): void - { - $color = Mockery::mock(AbstractColor::class)->makePartial(); - $color->shouldReceive('red')->andReturn(255); - $color->shouldReceive('green')->andReturn(0); - $color->shouldReceive('blue')->andReturn(0); - $this->assertFalse($color->isGreyscale()); - - $color = Mockery::mock(AbstractColor::class)->makePartial(); - $color->shouldReceive('red')->andReturn(100); - $color->shouldReceive('green')->andReturn(100); - $color->shouldReceive('blue')->andReturn(100); - $this->assertTrue($color->isGreyscale()); - } -} diff --git a/tests/Drivers/Gd/ColorTest.php b/tests/Drivers/Gd/ColorTest.php deleted file mode 100644 index 3c9aa9dc..00000000 --- a/tests/Drivers/Gd/ColorTest.php +++ /dev/null @@ -1,88 +0,0 @@ -assertInstanceOf(Color::class, $this->getTestColor()); - } - - public function testRed(): void - { - $color = $this->getTestColor(255, 0, 0); - $this->assertEquals(255, $color->red()); - } - - public function testGreen(): void - { - $color = $this->getTestColor(0, 150, 0); - $this->assertEquals(150, $color->green()); - } - - public function testBlue(): void - { - $color = $this->getTestColor(0, 0, 120); - $this->assertEquals(120, $color->blue()); - } - - public function testAlpha(): void - { - $color = $this->getTestColor(0, 0, 120, 0); - $this->assertEquals(1, $color->alpha()); - - $color = $this->getTestColor(0, 0, 120, 127); - $this->assertEquals(0, $color->alpha()); - - $color = $this->getTestColor(0, 0, 120, 64); - $this->assertEquals(.5, $color->alpha()); - } - - public function testToArray(): void - { - $color = $this->getTestColor(0, 0, 120, 0); - $this->assertEquals([0, 0, 120, 1], $color->toArray()); - - $color = $this->getTestColor(0, 0, 120, 127); - $this->assertEquals([0, 0, 120, 0], $color->toArray()); - - $color = $this->getTestColor(0, 0, 120, 64); - $this->assertEquals([0, 0, 120, .5], $color->toArray()); - } - - public function testToInt(): void - { - $color = $this->getTestColor(0, 0, 0, 0); - $this->assertEquals(0, $color->toInt()); - - $color = $this->getTestColor(255, 255, 255, 0); - $this->assertEquals(16777215, $color->toInt()); - } - - public function testToHex(): void - { - $color = $this->getTestColor(181, 55, 23); - $this->assertEquals('b53717', $color->toHex()); - $this->assertEquals('#b53717', $color->toHex('#')); - - $color = $this->getTestColor(181, 55, 23, 127); - $this->assertEquals('b53717', $color->toHex()); - $this->assertEquals('#b53717', $color->toHex('#')); - } -} diff --git a/tests/Drivers/Gd/Decoders/RgbArrayColorDecoderTest.php b/tests/Drivers/Gd/Decoders/RgbArrayColorDecoderTest.php deleted file mode 100644 index 524b2f63..00000000 --- a/tests/Drivers/Gd/Decoders/RgbArrayColorDecoderTest.php +++ /dev/null @@ -1,25 +0,0 @@ -decode([181, 55, 23, .5]); - $this->assertInstanceOf(Color::class, $color); - $this->assertEquals(181, $color->red()); - $this->assertEquals(55, $color->green()); - $this->assertEquals(23, $color->blue()); - $this->assertEquals(.5, $color->alpha()); - } -} diff --git a/tests/Drivers/Gd/InputHandlerTest.php b/tests/Drivers/Gd/InputHandlerTest.php index 829fdca1..8157f01a 100644 --- a/tests/Drivers/Gd/InputHandlerTest.php +++ b/tests/Drivers/Gd/InputHandlerTest.php @@ -2,7 +2,8 @@ namespace Intervention\Image\Tests\Drivers\Gd; -use Intervention\Image\Drivers\Gd\Color; +use Intervention\Image\Colors\Rgb\Color as RgbColor; +use Intervention\Image\Colors\Rgba\Color as RgbaColor; use Intervention\Image\Drivers\Gd\Image; use Intervention\Image\Drivers\Gd\InputHandler; use Intervention\Image\Exceptions\DecoderException; @@ -12,13 +13,13 @@ use Intervention\Image\Tests\TestCase; * @requires extension gd * @covers \Intervention\Image\Drivers\Gd\InputHandler */ -class InputHandlerTest extends TestCase +class GdInputHandlerTest extends TestCase { public function testHandleEmptyString(): void { $handler = new InputHandler(); $this->expectException(DecoderException::class); - $result = $handler->handle(''); + $handler->handle(''); } public function testHandleBinaryImage(): void @@ -53,59 +54,63 @@ class InputHandlerTest extends TestCase $this->assertInstanceOf(Image::class, $result); } - public function testHandleArrayColor(): void - { - $handler = new InputHandler(); - $input = [181, 55, 23, .5]; - $result = $handler->handle($input); - $this->assertInstanceOf(Color::class, $result); - - $handler = new InputHandler(); - $input = [181, 55, 23]; - $result = $handler->handle($input); - $this->assertInstanceOf(Color::class, $result); - } - public function testHandleHexColor(): void { $handler = new InputHandler(); $input = 'ccff33'; $result = $handler->handle($input); - $this->assertInstanceOf(Color::class, $result); - $this->assertEquals(204, $result->red()); - $this->assertEquals(255, $result->green()); - $this->assertEquals(51, $result->blue()); + $this->assertInstanceOf(RgbColor::class, $result); + $this->assertEquals([204, 255, 51], $result->toArray()); $handler = new InputHandler(); $input = 'cf3'; $result = $handler->handle($input); - $this->assertInstanceOf(Color::class, $result); - $this->assertEquals(204, $result->red()); - $this->assertEquals(255, $result->green()); - $this->assertEquals(51, $result->blue()); + $this->assertInstanceOf(RgbColor::class, $result); + $this->assertEquals([204, 255, 51], $result->toArray()); $handler = new InputHandler(); $input = '#123456'; $result = $handler->handle($input); - $this->assertInstanceOf(Color::class, $result); - $this->assertEquals(18, $result->red()); - $this->assertEquals(52, $result->green()); - $this->assertEquals(86, $result->blue()); + $this->assertInstanceOf(RgbColor::class, $result); + $this->assertEquals([18, 52, 86], $result->toArray()); $handler = new InputHandler(); $input = '#333'; $result = $handler->handle($input); - $this->assertInstanceOf(Color::class, $result); - $this->assertEquals(51, $result->red()); - $this->assertEquals(51, $result->green()); - $this->assertEquals(51, $result->blue()); + $this->assertInstanceOf(RgbColor::class, $result); + $this->assertEquals([51, 51, 51], $result->toArray()); + + $handler = new InputHandler(); + $input = '#3333'; + $result = $handler->handle($input); + $this->assertInstanceOf(RgbaColor::class, $result); + $this->assertEquals([51, 51, 51, 51], $result->toArray()); + + $handler = new InputHandler(); + $input = '#33333333'; + $result = $handler->handle($input); + $this->assertInstanceOf(RgbaColor::class, $result); + $this->assertEquals([51, 51, 51, 51], $result->toArray()); } - public function testHandleTransparent(): void + public function testHandleRgbString(): void { $handler = new InputHandler(); - $input = 'transparent'; - $result = $handler->handle($input); - $this->assertInstanceOf(Color::class, $result); + $result = $handler->handle('rgb(10, 20, 30)'); + $this->assertInstanceOf(RgbColor::class, $result); + $this->assertEquals([10, 20, 30], $result->toArray()); + + $handler = new InputHandler(); + $result = $handler->handle('rgba(10, 20, 30, 1.0)'); + $this->assertInstanceOf(RgbaColor::class, $result); + $this->assertEquals([10, 20, 30, 255], $result->toArray()); } + + // public function testHandleTransparent(): void + // { + // $handler = new InputHandler(); + // $input = 'transparent'; + // $result = $handler->handle($input); + // $this->assertInstanceOf(Color::class, $result); + // } } diff --git a/tests/Drivers/Imagick/ColorTest.php b/tests/Drivers/Imagick/ColorTest.php deleted file mode 100644 index 2840e98f..00000000 --- a/tests/Drivers/Imagick/ColorTest.php +++ /dev/null @@ -1,112 +0,0 @@ -assertInstanceOf(Color::class, $this->getTestColor()); - } - - public function testRed(): void - { - $color = $this->getTestColor(255, 0, 0); - $this->assertEquals(255, $color->red()); - } - - public function testGreen(): void - { - $color = $this->getTestColor(0, 150, 0); - $this->assertEquals(150, $color->green()); - } - - public function testBlue(): void - { - $color = $this->getTestColor(0, 0, 120); - $this->assertEquals(120, $color->blue()); - } - - public function testAlpha(): void - { - $color = $this->getTestColor(0, 0, 120, 1); - $this->assertEquals(1, $color->alpha()); - - $color = $this->getTestColor(0, 0, 120, 0); - $this->assertEquals(0, $color->alpha()); - - $color = $this->getTestColor(0, 0, 120, .5); - $this->assertEquals(.5, $color->alpha()); - - $color = $this->getTestColor(0, 0, 120, .57); - $this->assertEquals(.57, $color->alpha()); - - $color = $this->getTestColor(0, 0, 120, .578); - $this->assertEquals(.58, $color->alpha()); - } - - public function testToArray(): void - { - $color = $this->getTestColor(0, 0, 120, 1); - $this->assertEquals([0, 0, 120, 1], $color->toArray()); - - $color = $this->getTestColor(0, 0, 120, 0); - $this->assertEquals([0, 0, 120, 0], $color->toArray()); - - $color = $this->getTestColor(0, 0, 120, .5); - $this->assertEquals([0, 0, 120, .5], $color->toArray()); - - $color = $this->getTestColor(0, 0, 120, .57); - $this->assertEquals([0, 0, 120, .57], $color->toArray()); - - $color = $this->getTestColor(0, 0, 120, .578); - $this->assertEquals([0, 0, 120, .58], $color->toArray()); - } - - public function testToHex(): void - { - $color = $this->getTestColor(181, 55, 23); - $this->assertEquals('b53717', $color->toHex()); - $this->assertEquals('#b53717', $color->toHex('#')); - - $color = $this->getTestColor(181, 55, 23, 127); - $this->assertEquals('b53717', $color->toHex()); - $this->assertEquals('#b53717', $color->toHex('#')); - } - - public function testToInt(): void - { - $color = $this->getTestColor(255, 255, 255); - $this->assertEquals($color->toInt(), 4294967295); - - $color = $this->getTestColor(255, 255, 255, 1); - $this->assertEquals($color->toInt(), 4294967295); - - $color = $this->getTestColor(181, 55, 23, 0.2); - $this->assertEquals($color->toInt(), 867514135); - - $color = $this->getTestColor(255, 255, 255, 0.5); - $this->assertEquals($color->toInt(), 2164260863); - - $color = $this->getTestColor(181, 55, 23, 1); - $this->assertEquals($color->toInt(), 4290066199); - - $color = $this->getTestColor(0, 0, 0, 0); - $this->assertEquals($color->toInt(), 0); - } -} diff --git a/tests/Drivers/Imagick/Decoders/RgbArrayColorDecoderTest.php b/tests/Drivers/Imagick/Decoders/RgbArrayColorDecoderTest.php deleted file mode 100644 index fbb5b4e0..00000000 --- a/tests/Drivers/Imagick/Decoders/RgbArrayColorDecoderTest.php +++ /dev/null @@ -1,25 +0,0 @@ -decode([181, 55, 23, .5]); - $this->assertInstanceOf(Color::class, $color); - $this->assertEquals(181, $color->red()); - $this->assertEquals(55, $color->green()); - $this->assertEquals(23, $color->blue()); - $this->assertEquals(.5, $color->alpha()); - } -} diff --git a/tests/Drivers/Imagick/InputHandlerTest.php b/tests/Drivers/Imagick/InputHandlerTest.php index 627529ef..f125f686 100644 --- a/tests/Drivers/Imagick/InputHandlerTest.php +++ b/tests/Drivers/Imagick/InputHandlerTest.php @@ -2,7 +2,8 @@ namespace Intervention\Image\Tests\Drivers\Imagick; -use Intervention\Image\Drivers\Imagick\Color; +use Intervention\Image\Colors\Rgb\Color as RgbColor; +use Intervention\Image\Colors\Rgba\Color as RgbaColor; use Intervention\Image\Drivers\Imagick\Image; use Intervention\Image\Drivers\Imagick\InputHandler; use Intervention\Image\Exceptions\DecoderException; @@ -18,7 +19,7 @@ class InputHandlerTest extends TestCase { $handler = new InputHandler(); $this->expectException(DecoderException::class); - $result = $handler->handle(''); + $handler->handle(''); } public function testHandleBinaryImage(): void @@ -53,11 +54,55 @@ class InputHandlerTest extends TestCase $this->assertInstanceOf(Image::class, $result); } - public function testHandleArrayColor(): void + public function testHandleHexColor(): void { $handler = new InputHandler(); - $input = [181, 55, 23, .5]; + $input = 'ccff33'; $result = $handler->handle($input); - $this->assertInstanceOf(Color::class, $result); + $this->assertInstanceOf(RgbColor::class, $result); + $this->assertEquals([204, 255, 51], $result->toArray()); + + $handler = new InputHandler(); + $input = 'cf3'; + $result = $handler->handle($input); + $this->assertInstanceOf(RgbColor::class, $result); + $this->assertEquals([204, 255, 51], $result->toArray()); + + $handler = new InputHandler(); + $input = '#123456'; + $result = $handler->handle($input); + $this->assertInstanceOf(RgbColor::class, $result); + $this->assertEquals([18, 52, 86], $result->toArray()); + + $handler = new InputHandler(); + $input = '#333'; + $result = $handler->handle($input); + $this->assertInstanceOf(RgbColor::class, $result); + $this->assertEquals([51, 51, 51], $result->toArray()); + + $handler = new InputHandler(); + $input = '#3333'; + $result = $handler->handle($input); + $this->assertInstanceOf(RgbaColor::class, $result); + $this->assertEquals([51, 51, 51, 51], $result->toArray()); + + $handler = new InputHandler(); + $input = '#33333333'; + $result = $handler->handle($input); + $this->assertInstanceOf(RgbaColor::class, $result); + $this->assertEquals([51, 51, 51, 51], $result->toArray()); + } + + public function testHandleRgbString(): void + { + $handler = new InputHandler(); + $result = $handler->handle('rgb(10, 20, 30)'); + $this->assertInstanceOf(RgbColor::class, $result); + $this->assertEquals([10, 20, 30], $result->toArray()); + + $handler = new InputHandler(); + $result = $handler->handle('rgba(10, 20, 30, 1.0)'); + $this->assertInstanceOf(RgbaColor::class, $result); + $this->assertEquals([10, 20, 30, 255], $result->toArray()); } } From 56ab8b1ea7f2e8c578026e44cf88215584654ca0 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 15 Oct 2023 11:29:33 +0200 Subject: [PATCH 281/476] Replace color implementation in FillModifiers --- docker-compose.yml | 2 +- src/Colors/Cmyk/Channels/Cyan.php | 10 +++ src/Colors/Cmyk/Color.php | 7 ++- src/Colors/Cmyk/Parser.php | 34 +++++++++++ src/Colors/Parser.php | 28 +++++++++ src/Colors/Rgb/Channels/Red.php | 10 +++ src/Colors/Rgb/Color.php | 7 ++- src/Colors/Rgb/Parser.php | 52 ++++++++++++++-- src/Colors/Rgba/Channels/Alpha.php | 10 ++- src/Colors/Rgba/Color.php | 7 ++- src/Colors/Rgba/Parser.php | 31 +++++++--- src/Drivers/Gd/Color.php | 50 --------------- src/Drivers/Gd/ColorTransformer.php | 61 +++++++++++++++++++ ...olorDecoder.php => ColorObjectDecoder.php} | 7 ++- src/Drivers/Gd/Image.php | 2 +- src/Drivers/Gd/InputHandler.php | 1 + src/Drivers/Gd/Modifiers/FillModifier.php | 9 ++- src/Drivers/Imagick/Color.php | 61 ------------------- src/Drivers/Imagick/ColorTransformer.php | 32 ++++++++++ ...olorDecoder.php => ColorObjectDecoder.php} | 7 ++- src/Drivers/Imagick/Image.php | 4 +- src/Drivers/Imagick/InputHandler.php | 1 + .../Imagick/Modifiers/FillModifier.php | 14 +++-- src/Drivers/Imagick/Traits/CanReadColors.php | 14 +++++ src/Interfaces/ColorChannelInterface.php | 2 + src/Interfaces/ColorInterface.php | 11 ++-- tests/Colors/Cmyk/ParserTest.php | 32 ++++++++++ tests/Colors/ParserTest.php | 47 ++++++++++++++ tests/Colors/Rgb/ParserTest.php | 23 +++++++ tests/Colors/Rgba/ChannelTest.php | 19 ++++++ tests/Colors/Rgba/ParserTest.php | 23 +++++++ tests/Drivers/Gd/ColorTransformerTest.php | 30 +++++++++ .../Gd/Decoders/HexColorDecoderTest.php | 2 +- .../Gd/Decoders/HtmlColorNameDecoderTest.php | 2 +- .../Gd/Decoders/RgbStringColorDecoderTest.php | 17 ++---- .../Decoders/TransparentColorDecoderTest.php | 25 -------- .../Drivers/Imagick/ColorTransformerTest.php | 37 +++++++++++ .../Decoders/HtmlColorNameDecoderTest.php | 2 +- .../Decoders/RgbStringColorDecoderTest.php | 19 +++--- .../Decoders/TransparentColorDecoderTest.php | 25 -------- 40 files changed, 550 insertions(+), 227 deletions(-) create mode 100644 src/Colors/Cmyk/Parser.php create mode 100644 src/Colors/Parser.php delete mode 100644 src/Drivers/Gd/Color.php create mode 100644 src/Drivers/Gd/ColorTransformer.php rename src/Drivers/Gd/Decoders/{TransparentColorDecoder.php => ColorObjectDecoder.php} (65%) delete mode 100644 src/Drivers/Imagick/Color.php create mode 100644 src/Drivers/Imagick/ColorTransformer.php rename src/Drivers/Imagick/Decoders/{TransparentColorDecoder.php => ColorObjectDecoder.php} (66%) create mode 100644 src/Drivers/Imagick/Traits/CanReadColors.php create mode 100644 tests/Colors/Cmyk/ParserTest.php create mode 100644 tests/Colors/ParserTest.php create mode 100644 tests/Drivers/Gd/ColorTransformerTest.php delete mode 100644 tests/Drivers/Gd/Decoders/TransparentColorDecoderTest.php create mode 100644 tests/Drivers/Imagick/ColorTransformerTest.php delete mode 100644 tests/Drivers/Imagick/Decoders/TransparentColorDecoderTest.php diff --git a/docker-compose.yml b/docker-compose.yml index e32993d7..8d90fbff 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -4,7 +4,7 @@ services: tests: build: ./ working_dir: /project - command: bash -c "composer install && ./vendor/bin/phpunit -vvv --filter=InputHandlerTest" + command: bash -c "composer install && ./vendor/bin/phpunit -vvv" volumes: - ./:/project analysis: diff --git a/src/Colors/Cmyk/Channels/Cyan.php b/src/Colors/Cmyk/Channels/Cyan.php index cb40daac..213fea38 100644 --- a/src/Colors/Cmyk/Channels/Cyan.php +++ b/src/Colors/Cmyk/Channels/Cyan.php @@ -42,4 +42,14 @@ class Cyan implements ColorChannelInterface return $value; } + + public function toString(): string + { + return (string) $this->value(); + } + + public function __toString(): string + { + return $this->toString(); + } } diff --git a/src/Colors/Cmyk/Color.php b/src/Colors/Cmyk/Color.php index 26b423ea..74ee0540 100644 --- a/src/Colors/Cmyk/Color.php +++ b/src/Colors/Cmyk/Color.php @@ -91,7 +91,7 @@ class Color implements ColorInterface return $this->convertTo(CmykColorspace::class); } - public function __toString(): string + public function toString(): string { return sprintf( 'cmyk(%d, %d, %d, %d)', @@ -101,4 +101,9 @@ class Color implements ColorInterface $this->key()->value() ); } + + public function __toString(): string + { + return $this->toString(); + } } diff --git a/src/Colors/Cmyk/Parser.php b/src/Colors/Cmyk/Parser.php new file mode 100644 index 00000000..15fb3a70 --- /dev/null +++ b/src/Colors/Cmyk/Parser.php @@ -0,0 +1,34 @@ +[0-9\.]+)%?, ?(?P[0-9\.]+)%?, ?(?P[0-9\.]+)%?, ?(?P[0-9\.]+)%?\)$/'; + $result = preg_match($pattern, $input, $matches); + if ($result === 1) { + return new Color( + intval(round(floatval($matches['c']))), + intval(round(floatval($matches['m']))), + intval(round(floatval($matches['y']))), + intval(round(floatval($matches['k']))) + ); + } + + throw new ColorException('Unable to parse color'); + } +} diff --git a/src/Colors/Parser.php b/src/Colors/Parser.php new file mode 100644 index 00000000..edd2b2b4 --- /dev/null +++ b/src/Colors/Parser.php @@ -0,0 +1,28 @@ +value(); + } + + public function __toString(): string + { + return $this->toString(); + } } diff --git a/src/Colors/Rgb/Color.php b/src/Colors/Rgb/Color.php index 644b3936..b630829e 100644 --- a/src/Colors/Rgb/Color.php +++ b/src/Colors/Rgb/Color.php @@ -87,7 +87,7 @@ class Color implements ColorInterface return $this->convertTo(RgbaColorspace::class); } - public function __toString(): string + public function toString(): string { return sprintf( 'rgb(%d, %d, %d)', @@ -96,4 +96,9 @@ class Color implements ColorInterface $this->blue()->value() ); } + + public function __toString(): string + { + return $this->toString(); + } } diff --git a/src/Colors/Rgb/Parser.php b/src/Colors/Rgb/Parser.php index be787dab..82aef1e6 100644 --- a/src/Colors/Rgb/Parser.php +++ b/src/Colors/Rgb/Parser.php @@ -6,6 +6,33 @@ use Intervention\Image\Exceptions\ColorException; class Parser { + public static function parse(mixed $value): Color + { + if (!is_string($value)) { + throw new ColorException('Unable to parse color'); + } + + try { + return static::fromHex($value); + } catch (ColorException $e) { + # move on + } + + try { + return static::fromString($value); + } catch (ColorException $e) { + # move on + } + + try { + return static::fromName($value); + } catch (ColorException $e) { + # move on + } + + throw new ColorException('Unable to parse color'); + } + public static function fromHex(string $input): Color { $pattern = '/^#?(?P[0-9a-f]{3}|[0-9a-f]{6})$/i'; @@ -30,14 +57,29 @@ class Parser public static function fromString(string $input): Color { - $pattern = '/^rgb\((?P[0-9]{1,3}), ?(?P[0-9]{1,3}), ?(?P[0-9]{1,3})\)$/'; + // rgb(255, 255, 255) + $pattern = '/^s?rgb\((?P[0-9]{1,3}), ?(?P[0-9]{1,3}), ?(?P[0-9]{1,3})\)$/'; $result = preg_match($pattern, $input, $matches); - - if ($result !== 1) { - throw new ColorException('Unable to parse color'); + if ($result === 1) { + return new Color( + $matches['r'], + $matches['g'], + $matches['b'] + ); } - return new Color($matches['r'], $matches['g'], $matches['b']); + // rgb(100%, 100%, 100%) + $pattern = '/^s?rgb\((?P[0-9\.]+)%, ?(?P[0-9\.]+)%, ?(?P[0-9\.]+)%\)$/'; + $result = preg_match($pattern, $input, $matches); + if ($result === 1) { + return new Color( + intval(round(floatval($matches['r']) / 100 * 255)), + intval(round(floatval($matches['g']) / 100 * 255)), + intval(round(floatval($matches['b']) / 100 * 255)) + ); + } + + throw new ColorException('Unable to parse color'); } public static function fromName(string $input): Color diff --git a/src/Colors/Rgba/Channels/Alpha.php b/src/Colors/Rgba/Channels/Alpha.php index 46d3a152..e69a437e 100644 --- a/src/Colors/Rgba/Channels/Alpha.php +++ b/src/Colors/Rgba/Channels/Alpha.php @@ -4,5 +4,13 @@ namespace Intervention\Image\Colors\Rgba\Channels; class Alpha extends Red { - // + public function toString(): string + { + return strval(round($this->normalize(), 6)); + } + + public function __toString(): string + { + return $this->toString(); + } } diff --git a/src/Colors/Rgba/Color.php b/src/Colors/Rgba/Color.php index 90e65827..8bfdf227 100644 --- a/src/Colors/Rgba/Color.php +++ b/src/Colors/Rgba/Color.php @@ -81,7 +81,7 @@ class Color extends RgbColor return $this->convertTo(CmykColorspace::class); } - public function __toString(): string + public function toString(): string { return sprintf( 'rgba(%d, %d, %d, %.1F)', @@ -91,4 +91,9 @@ class Color extends RgbColor $this->alpha()->normalize(), ); } + + public function __toString(): string + { + return $this->toString(); + } } diff --git a/src/Colors/Rgba/Parser.php b/src/Colors/Rgba/Parser.php index a567f70e..efaa9f70 100644 --- a/src/Colors/Rgba/Parser.php +++ b/src/Colors/Rgba/Parser.php @@ -38,18 +38,31 @@ class Parser extends RgbParser public static function fromString(string $input): Color { - $pattern = '/^rgba\((?P[0-9]{1,3}), *(?P[0-9]{1,3}), *(?P[0-9]{1,3}), *(?P((1|0))?(\.[0-9]+)?)\)$/'; + // rgba(255, 255, 255, 1.0) + $pattern = '/^s?rgba\((?P[0-9]{1,3}), *(?P[0-9]{1,3}), *(?P[0-9]{1,3}), *(?P((1|0))?(\.[0-9]+)?)\)$/'; $result = preg_match($pattern, $input, $matches); - if ($result !== 1) { - throw new ColorException('Unable to parse color'); + if ($result === 1) { + return new Color( + $matches['r'], + $matches['g'], + $matches['b'], + intval(round(floatval($matches['a']) * 255)) + ); } - return new Color( - $matches['r'], - $matches['g'], - $matches['b'], - intval(round(floatval($matches['a']) * 255)) - ); + // rgba(100%, 100%, 100%, 100%) + $pattern = '/s?rgba\((?P[0-9\.]+)%, ?(?P[0-9\.]+)%, ?(?P[0-9\.]+)%, ?(?P[0-9\.]+)%\)/'; + $result = preg_match($pattern, $input, $matches); + if ($result === 1) { + return new Color( + intval(round(floatval($matches['r']) / 100 * 255)), + intval(round(floatval($matches['g']) / 100 * 255)), + intval(round(floatval($matches['b']) / 100 * 255)), + intval(round(floatval($matches['a']) / 100 * 255)) + ); + } + + throw new ColorException('Unable to parse color'); } } diff --git a/src/Drivers/Gd/Color.php b/src/Drivers/Gd/Color.php deleted file mode 100644 index d4cde720..00000000 --- a/src/Drivers/Gd/Color.php +++ /dev/null @@ -1,50 +0,0 @@ -toArray()[0]; - } - - public function green(): int - { - return $this->toArray()[1]; - } - - public function blue(): int - { - return $this->toArray()[2]; - } - - public function alpha(): float - { - return $this->toArray()[3]; - } - - public function toArray(): array - { - $a = ($this->value >> 24) & 0x7F; - $r = ($this->value >> 16) & 0xFF; - $g = ($this->value >> 8) & 0xFF; - $b = $this->value & 0xFF; - $a = (float) round(1 - $a / 127, 2); - - return [$r, $g, $b, $a]; - } - - public function toInt(): int - { - return $this->value; - } -} diff --git a/src/Drivers/Gd/ColorTransformer.php b/src/Drivers/Gd/ColorTransformer.php new file mode 100644 index 00000000..aac312e7 --- /dev/null +++ b/src/Drivers/Gd/ColorTransformer.php @@ -0,0 +1,61 @@ +> 24) & 0xFF; + $r = ($value >> 16) & 0xFF; + $g = ($value >> 8) & 0xFF; + $b = $value & 0xFF; + + // convert gd apha integer to intervention alpha integer + // ([opaque]0-127[transparent]) to ([opaque]255-0[transparent]) + $a = (int) static::convertRange($a, 127, 0, 0, 255); + + return new Color($r, $g, $b, $a); + } + + /** + * Transforms given color to the corresponding GD Library integer value + * + * @param ColorInterface $color + * @return int + */ + public static function colorToInteger(ColorInterface $color): int + { + $color = $color->toRgba(); + + $r = $color->red()->value(); + $g = $color->green()->value(); + $b = $color->blue()->value(); + $a = $color->alpha()->value(); + + // convert alpha value to gd alpha + // ([opaque]255-0[transparent]) to ([opaque]0-127[transparent]) + $a = (int) static::convertRange($a, 0, 255, 127, 0); + + return ($a << 24) + ($r << 16) + ($g << 8) + $b; + } + + private static function convertRange( + float|int $input, + float|int $min, + float|int $max, + float|int $targetMin, + float|int $targetMax + ): float|int { + return ceil(((($input - $min) * ($targetMax - $targetMin)) / ($max - $min)) + $targetMin); + } +} diff --git a/src/Drivers/Gd/Decoders/TransparentColorDecoder.php b/src/Drivers/Gd/Decoders/ColorObjectDecoder.php similarity index 65% rename from src/Drivers/Gd/Decoders/TransparentColorDecoder.php rename to src/Drivers/Gd/Decoders/ColorObjectDecoder.php index 5cdd3d02..7c2e83a4 100644 --- a/src/Drivers/Gd/Decoders/TransparentColorDecoder.php +++ b/src/Drivers/Gd/Decoders/ColorObjectDecoder.php @@ -2,19 +2,20 @@ namespace Intervention\Image\Drivers\Gd\Decoders; +use Intervention\Image\Drivers\Abstract\Decoders\AbstractDecoder; use Intervention\Image\Exceptions\DecoderException; use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\DecoderInterface; use Intervention\Image\Interfaces\ImageInterface; -class TransparentColorDecoder extends RgbArrayColorDecoder implements DecoderInterface +class ColorObjectDecoder extends AbstractDecoder implements DecoderInterface { public function decode($input): ImageInterface|ColorInterface { - if (! is_string($input) || strtolower($input) !== 'transparent') { + if (! is_a($input, ColorInterface::class)) { throw new DecoderException('Unable to decode input'); } - return parent::decode([0, 0, 0, 0]); + return $input; } } diff --git a/src/Drivers/Gd/Image.php b/src/Drivers/Gd/Image.php index ad9b3754..b572354b 100644 --- a/src/Drivers/Gd/Image.php +++ b/src/Drivers/Gd/Image.php @@ -69,7 +69,7 @@ class Image extends AbstractImage implements ImageInterface, IteratorAggregate public function pickColor(int $x, int $y, int $frame_key = 0): ?ColorInterface { if ($frame = $this->getFrame($frame_key)) { - return new Color(imagecolorat($frame->getCore(), $x, $y)); + return ColorTransformer::colorFromInteger(imagecolorat($frame->getCore(), $x, $y)); } return null; diff --git a/src/Drivers/Gd/InputHandler.php b/src/Drivers/Gd/InputHandler.php index 42785e59..076269c8 100644 --- a/src/Drivers/Gd/InputHandler.php +++ b/src/Drivers/Gd/InputHandler.php @@ -8,6 +8,7 @@ class InputHandler extends AbstractInputHandler { protected $decoders = [ Decoders\ImageObjectDecoder::class, + Decoders\ColorObjectDecoder::class, Decoders\FilePointerImageDecoder::class, Decoders\HtmlColorNameDecoder::class, Decoders\HexColorDecoder::class, diff --git a/src/Drivers/Gd/Modifiers/FillModifier.php b/src/Drivers/Gd/Modifiers/FillModifier.php index 0f747342..0d0ea3f3 100644 --- a/src/Drivers/Gd/Modifiers/FillModifier.php +++ b/src/Drivers/Gd/Modifiers/FillModifier.php @@ -2,6 +2,7 @@ namespace Intervention\Image\Drivers\Gd\Modifiers; +use Intervention\Image\Drivers\Gd\ColorTransformer; use Intervention\Image\Drivers\Gd\Frame; use Intervention\Image\Geometry\Point; use Intervention\Image\Interfaces\ColorInterface; @@ -10,9 +11,11 @@ use Intervention\Image\Interfaces\ModifierInterface; class FillModifier implements ModifierInterface { + protected $gd_color; + public function __construct(protected ColorInterface $color, protected ?Point $position = null) { - // + $this->gd_color = ColorTransformer::colorToInteger($color); } public function apply(ImageInterface $image): ImageInterface @@ -34,7 +37,7 @@ class FillModifier implements ModifierInterface $frame->getCore(), $this->position->getX(), $this->position->getY(), - $this->color->toInt() + $this->gd_color ); } @@ -47,7 +50,7 @@ class FillModifier implements ModifierInterface 0, $frame->getSize()->getWidth() - 1, $frame->getSize()->getHeight() - 1, - $this->color->toInt() + $this->gd_color ); } diff --git a/src/Drivers/Imagick/Color.php b/src/Drivers/Imagick/Color.php deleted file mode 100644 index 7503c7b1..00000000 --- a/src/Drivers/Imagick/Color.php +++ /dev/null @@ -1,61 +0,0 @@ -pixel; - } - - public function red(): int - { - return intval(round($this->pixel->getColorValue(Imagick::COLOR_RED) * 255)); - } - - public function green(): int - { - return intval(round($this->pixel->getColorValue(Imagick::COLOR_GREEN) * 255)); - } - - public function blue(): int - { - return intval(round($this->pixel->getColorValue(Imagick::COLOR_BLUE) * 255)); - } - - public function alpha(): float - { - return round($this->pixel->getColorValue(Imagick::COLOR_ALPHA), 2); - } - - public function toArray(): array - { - return [ - $this->red(), - $this->green(), - $this->blue(), - $this->alpha() - ]; - } - - public function toInt(): int - { - $r = $this->red(); - $g = $this->green(); - $b = $this->blue(); - $a = intval(round($this->alpha() * 255)); - - return intval(($a << 24) + ($r << 16) + ($g << 8) + $b); - } -} diff --git a/src/Drivers/Imagick/ColorTransformer.php b/src/Drivers/Imagick/ColorTransformer.php new file mode 100644 index 00000000..1ddf8a32 --- /dev/null +++ b/src/Drivers/Imagick/ColorTransformer.php @@ -0,0 +1,32 @@ +getColorAsString()); + } + + /** + * Transforms given color to the corresponding ImagickPixel + * + * @param ColorInterface $color + * @return ImagickPixel + */ + public static function colorToPixel(ColorInterface $color): ImagickPixel + { + return new ImagickPixel($color->toString()); + } +} diff --git a/src/Drivers/Imagick/Decoders/TransparentColorDecoder.php b/src/Drivers/Imagick/Decoders/ColorObjectDecoder.php similarity index 66% rename from src/Drivers/Imagick/Decoders/TransparentColorDecoder.php rename to src/Drivers/Imagick/Decoders/ColorObjectDecoder.php index 097a35ff..426d2e52 100644 --- a/src/Drivers/Imagick/Decoders/TransparentColorDecoder.php +++ b/src/Drivers/Imagick/Decoders/ColorObjectDecoder.php @@ -2,19 +2,20 @@ namespace Intervention\Image\Drivers\Imagick\Decoders; +use Intervention\Image\Drivers\Abstract\Decoders\AbstractDecoder; use Intervention\Image\Exceptions\DecoderException; use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\DecoderInterface; use Intervention\Image\Interfaces\ImageInterface; -class TransparentColorDecoder extends RgbArrayColorDecoder implements DecoderInterface +class ColorObjectDecoder extends AbstractDecoder implements DecoderInterface { public function decode($input): ImageInterface|ColorInterface { - if (! is_string($input) || strtolower($input) !== 'transparent') { + if (! is_a($input, ColorInterface::class)) { throw new DecoderException('Unable to decode input'); } - return parent::decode([0, 0, 0, 0]); + return $input; } } diff --git a/src/Drivers/Imagick/Image.php b/src/Drivers/Imagick/Image.php index 4dc8ca6a..a6ac13e5 100644 --- a/src/Drivers/Imagick/Image.php +++ b/src/Drivers/Imagick/Image.php @@ -124,7 +124,9 @@ class Image extends AbstractImage implements ImageInterface, Iterator public function pickColor(int $x, int $y, int $frame_key = 0): ?ColorInterface { if ($frame = $this->getFrame($frame_key)) { - return new Color($frame->getCore()->getImagePixelColor($x, $y)); + return ColorTransformer::colorFromPixel( + $frame->getCore()->getImagePixelColor($x, $y) + ); } return null; diff --git a/src/Drivers/Imagick/InputHandler.php b/src/Drivers/Imagick/InputHandler.php index 0fc798ff..d6ec81ef 100644 --- a/src/Drivers/Imagick/InputHandler.php +++ b/src/Drivers/Imagick/InputHandler.php @@ -8,6 +8,7 @@ class InputHandler extends AbstractInputHandler { protected $decoders = [ Decoders\ImageObjectDecoder::class, + Decoders\ColorObjectDecoder::class, Decoders\FilePointerImageDecoder::class, Decoders\HtmlColorNameDecoder::class, Decoders\HexColorDecoder::class, diff --git a/src/Drivers/Imagick/Modifiers/FillModifier.php b/src/Drivers/Imagick/Modifiers/FillModifier.php index a6cc51a4..36bef1af 100644 --- a/src/Drivers/Imagick/Modifiers/FillModifier.php +++ b/src/Drivers/Imagick/Modifiers/FillModifier.php @@ -4,19 +4,23 @@ namespace Intervention\Image\Drivers\Imagick\Modifiers; use Imagick; use ImagickDraw; -use Intervention\Image\Drivers\Imagick\Color; +use ImagickPixel; +use Intervention\Image\Drivers\Imagick\ColorTransformer; use Intervention\Image\Drivers\Imagick\Frame; use Intervention\Image\Geometry\Point; +use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; class FillModifier implements ModifierInterface { + protected ImagickPixel $pixel; + public function __construct( - protected Color $color, + protected ColorInterface $color, protected ?Point $position = null ) { - // + $this->pixel = ColorTransformer::colorToPixel($color); } public function apply(ImageInterface $image): ImageInterface @@ -40,7 +44,7 @@ class FillModifier implements ModifierInterface ); $frame->getCore()->floodfillPaintImage( - $this->color->getPixel(), + $this->pixel, 100, $target, $this->position->getX(), @@ -53,7 +57,7 @@ class FillModifier implements ModifierInterface protected function fillAllWithColor(Frame $frame): void { $draw = new ImagickDraw(); - $draw->setFillColor($this->color->getPixel()); + $draw->setFillColor($this->pixel); $draw->rectangle( 0, 0, diff --git a/src/Drivers/Imagick/Traits/CanReadColors.php b/src/Drivers/Imagick/Traits/CanReadColors.php new file mode 100644 index 00000000..aba65d76 --- /dev/null +++ b/src/Drivers/Imagick/Traits/CanReadColors.php @@ -0,0 +1,14 @@ +toString()); + } +} diff --git a/src/Interfaces/ColorChannelInterface.php b/src/Interfaces/ColorChannelInterface.php index 82de01da..6c3c7ce8 100644 --- a/src/Interfaces/ColorChannelInterface.php +++ b/src/Interfaces/ColorChannelInterface.php @@ -9,4 +9,6 @@ interface ColorChannelInterface public function validate(mixed $value): mixed; public function min(): int; public function max(): int; + public function toString(): string; + public function __toString(): string; } diff --git a/src/Interfaces/ColorInterface.php b/src/Interfaces/ColorInterface.php index 1333e94b..46a4dab8 100644 --- a/src/Interfaces/ColorInterface.php +++ b/src/Interfaces/ColorInterface.php @@ -8,14 +8,15 @@ use Intervention\Image\Colors\Rgba\Color as RgbaColor; interface ColorInterface { - // public function toRgb(): RgbColor; - // public function toRgba(): RgbaColor; - // public function toCmyk(): CmykColor; + public function toRgb(): RgbColor; + public function toRgba(): RgbaColor; + public function toCmyk(): CmykColor; + public function toArray(): array; + public function toString(): string; + public function __toString(): string; // public function channels(): array; // public function channel(string $classname): ColorChannelInterface; // public function colorspace(): ColorspaceInterface; - // public function toArray(): array; // public function convertTo(string|ColorspaceInterface $colorspace): ColorInterface; - // public function __toString(): string; } diff --git a/tests/Colors/Cmyk/ParserTest.php b/tests/Colors/Cmyk/ParserTest.php new file mode 100644 index 00000000..b94ec4f2 --- /dev/null +++ b/tests/Colors/Cmyk/ParserTest.php @@ -0,0 +1,32 @@ +assertInstanceOf(Color::class, $color); + $this->assertEquals([100, 0, 0, 0], $color->toArray()); + } + + public function testFromString(): void + { + $color = Parser::fromString('cmyk(100, 0, 0, 0)'); + $this->assertInstanceOf(Color::class, $color); + $this->assertEquals([100, 0, 0, 0], $color->toArray()); + + $color = Parser::fromString('cmyk(100%, 0%, 0%, 0%)'); + $this->assertInstanceOf(Color::class, $color); + $this->assertEquals([100, 0, 0, 0], $color->toArray()); + } +} diff --git a/tests/Colors/ParserTest.php b/tests/Colors/ParserTest.php new file mode 100644 index 00000000..a783f62a --- /dev/null +++ b/tests/Colors/ParserTest.php @@ -0,0 +1,47 @@ +assertInstanceOf(RgbColor::class, $color); + $this->assertEquals([204, 204, 204], $color->toArray()); + + $color = Parser::parse('rgb(204, 204, 204)'); + $this->assertInstanceOf(RgbColor::class, $color); + $this->assertEquals([204, 204, 204], $color->toArray()); + + $color = Parser::parse('rgba(204, 204, 204, 1)'); + $this->assertInstanceOf(RgbaColor::class, $color); + $this->assertEquals([204, 204, 204, 255], $color->toArray()); + + $color = Parser::parse('cccc'); + $this->assertInstanceOf(RgbColor::class, $color); + $this->assertEquals([204, 204, 204, 204], $color->toArray()); + + $color = Parser::parse('cccccccc'); + $this->assertInstanceOf(RgbColor::class, $color); + $this->assertEquals([204, 204, 204, 204], $color->toArray()); + + $color = Parser::parse('salmon'); + $this->assertInstanceOf(RgbColor::class, $color); + $this->assertEquals('fa8072', $color->toHex()); + + $color = Parser::parse('cmyk(100, 100, 0,0)'); + $this->assertInstanceOf(CmykColor::class, $color); + $this->assertEquals([100, 100, 0, 0], $color->toArray()); + } +} diff --git a/tests/Colors/Rgb/ParserTest.php b/tests/Colors/Rgb/ParserTest.php index 08389cbf..b6ccce7d 100644 --- a/tests/Colors/Rgb/ParserTest.php +++ b/tests/Colors/Rgb/ParserTest.php @@ -13,6 +13,21 @@ use Intervention\Image\Tests\TestCase; */ class ParserTest extends TestCase { + public function testParse(): void + { + $color = Parser::parse('ccc'); + $this->assertInstanceOf(Color::class, $color); + $this->assertEquals([204, 204, 204], $color->toArray()); + + $color = Parser::parse('rgb(204, 204, 204)'); + $this->assertInstanceOf(Color::class, $color); + $this->assertEquals([204, 204, 204], $color->toArray()); + + $color = Parser::parse('salmon'); + $this->assertInstanceOf(Color::class, $color); + $this->assertEquals('fa8072', $color->toHex()); + } + public function testFromHex(): void { $color = Parser::fromHex('ccc'); @@ -41,6 +56,14 @@ class ParserTest extends TestCase $this->assertInstanceOf(Color::class, $color); $this->assertEquals([204, 204, 204], $color->toArray()); + $color = Parser::fromString('rgb(100%,20%,25%)'); + $this->assertInstanceOf(Color::class, $color); + $this->assertEquals([255, 51, 64], $color->toArray()); + + $color = Parser::fromString('rgb(100%,74.8064%,25.2497%)'); + $this->assertInstanceOf(Color::class, $color); + $this->assertEquals([255, 191, 64], $color->toArray()); + $this->expectException(ColorException::class); (new Parser())->fromString('rgb(204,204,204,1)'); diff --git a/tests/Colors/Rgba/ChannelTest.php b/tests/Colors/Rgba/ChannelTest.php index a1144e40..3edcca17 100644 --- a/tests/Colors/Rgba/ChannelTest.php +++ b/tests/Colors/Rgba/ChannelTest.php @@ -3,6 +3,7 @@ namespace Intervention\Image\Tests\Colors\Rgba; use Intervention\Image\Colors\Rgba\Channels\Red as Channel; +use Intervention\Image\Colors\Rgba\Channels\Alpha as Alpha; use Intervention\Image\Exceptions\ColorException; use Intervention\Image\Tests\TestCase; @@ -45,4 +46,22 @@ class ChannelTest extends TestCase $this->expectException(ColorException::class); new Channel(-1); } + + public function testToString(): void + { + $channel = new Channel(255); + $this->assertEquals("255", $channel->toString()); + + $channel = new Alpha(0); + $this->assertEquals("0", $channel->toString()); + + $channel = new Alpha(51); + $this->assertEquals("0.2", $channel->toString()); + + $channel = new Alpha(255); + $this->assertEquals("1", $channel->toString()); + + $channel = new Alpha(170); + $this->assertEquals("0.666667", $channel->toString()); + } } diff --git a/tests/Colors/Rgba/ParserTest.php b/tests/Colors/Rgba/ParserTest.php index 0986c797..89441646 100644 --- a/tests/Colors/Rgba/ParserTest.php +++ b/tests/Colors/Rgba/ParserTest.php @@ -13,6 +13,21 @@ use Intervention\Image\Tests\TestCase; */ class ParserTest extends TestCase { + public function testParse(): void + { + $color = Parser::parse('ccc'); + $this->assertInstanceOf(Color::class, $color); + $this->assertEquals([204, 204, 204, 255], $color->toArray()); + + $color = Parser::parse('rgba(204, 204, 204, 1)'); + $this->assertInstanceOf(Color::class, $color); + $this->assertEquals([204, 204, 204, 255], $color->toArray()); + + $color = Parser::parse('salmon'); + $this->assertInstanceOf(Color::class, $color); + $this->assertEquals('fa8072', $color->toHex()); + } + public function testFromHex(): void { $color = Parser::fromHex('ccc'); @@ -62,6 +77,14 @@ class ParserTest extends TestCase $this->assertInstanceOf(Color::class, $color); $this->assertEquals([204, 204, 204, 51], $color->toArray()); + $color = Parser::fromString('rgba(100%,20%,25%,100%)'); + $this->assertInstanceOf(Color::class, $color); + $this->assertEquals([255, 51, 64, 255], $color->toArray()); + + $color = Parser::fromString('rgba(100%,74.8064%,25.2497%,100%)'); + $this->assertInstanceOf(Color::class, $color); + $this->assertEquals([255, 191, 64, 255], $color->toArray()); + $this->expectException(ColorException::class); $color = Parser::fromString('rgba(204, 204, 204, 1.2)'); } diff --git a/tests/Drivers/Gd/ColorTransformerTest.php b/tests/Drivers/Gd/ColorTransformerTest.php new file mode 100644 index 00000000..24a3fa7c --- /dev/null +++ b/tests/Drivers/Gd/ColorTransformerTest.php @@ -0,0 +1,30 @@ +assertInstanceOf(Color::class, $result); + $this->assertEquals([181, 55, 23, 155], $result->toArray()); + + $result = ColorTransformer::colorFromInteger(16777215); + $this->assertInstanceOf(Color::class, $result); + $this->assertEquals([255, 255, 255, 255], $result->toArray()); + } + + public function testToInteger(): void + { + $result = ColorTransformer::colorToInteger(new Color(181, 55, 23, 155)); + $this->assertEquals(850736919, $result); + + $result = ColorTransformer::colorToInteger(new Color(255, 255, 255, 255)); + $this->assertEquals(16777215, $result); + } +} diff --git a/tests/Drivers/Gd/Decoders/HexColorDecoderTest.php b/tests/Drivers/Gd/Decoders/HexColorDecoderTest.php index 179f4620..f78468ab 100644 --- a/tests/Drivers/Gd/Decoders/HexColorDecoderTest.php +++ b/tests/Drivers/Gd/Decoders/HexColorDecoderTest.php @@ -2,7 +2,7 @@ namespace Intervention\Image\Tests\Drivers\Gd\Decoders; -use Intervention\Image\Drivers\Gd\Color; +use Intervention\Image\Colors\Rgb\Color; use Intervention\Image\Drivers\Gd\Decoders\HexColorDecoder; use Intervention\Image\Tests\TestCase; diff --git a/tests/Drivers/Gd/Decoders/HtmlColorNameDecoderTest.php b/tests/Drivers/Gd/Decoders/HtmlColorNameDecoderTest.php index 8b713599..1113c784 100644 --- a/tests/Drivers/Gd/Decoders/HtmlColorNameDecoderTest.php +++ b/tests/Drivers/Gd/Decoders/HtmlColorNameDecoderTest.php @@ -2,7 +2,7 @@ namespace Intervention\Image\Tests\Drivers\Gd\Decoders; -use Intervention\Image\Drivers\Gd\Color; +use Intervention\Image\Colors\Rgb\Color; use Intervention\Image\Drivers\Gd\Decoders\HtmlColorNameDecoder; use Intervention\Image\Tests\TestCase; diff --git a/tests/Drivers/Gd/Decoders/RgbStringColorDecoderTest.php b/tests/Drivers/Gd/Decoders/RgbStringColorDecoderTest.php index 45537a27..fa73370c 100644 --- a/tests/Drivers/Gd/Decoders/RgbStringColorDecoderTest.php +++ b/tests/Drivers/Gd/Decoders/RgbStringColorDecoderTest.php @@ -2,7 +2,8 @@ namespace Intervention\Image\Tests\Drivers\Gd\Decoders; -use Intervention\Image\Drivers\Gd\Color; +use Intervention\Image\Colors\Rgb\Color as RgbColor; +use Intervention\Image\Colors\Rgba\Color as RgbaColor; use Intervention\Image\Drivers\Gd\Decoders\RgbStringColorDecoder; use Intervention\Image\Tests\TestCase; @@ -16,21 +17,15 @@ class RgbStringColorDecoderTest extends TestCase { $decoder = new RgbStringColorDecoder(); $color = $decoder->decode('rgb(181, 55, 23)'); - $this->assertInstanceOf(Color::class, $color); - $this->assertEquals(181, $color->red()); - $this->assertEquals(55, $color->green()); - $this->assertEquals(23, $color->blue()); - $this->assertEquals(1, $color->alpha()); + $this->assertInstanceOf(RgbColor::class, $color); + $this->assertEquals([181, 55, 23], $color->toArray()); } public function testDecodeRgba(): void { $decoder = new RgbStringColorDecoder(); $color = $decoder->decode('rgba(181, 55, 23, 0.5)'); - $this->assertInstanceOf(Color::class, $color); - $this->assertEquals(181, $color->red()); - $this->assertEquals(55, $color->green()); - $this->assertEquals(23, $color->blue()); - $this->assertEquals(.5, $color->alpha()); + $this->assertInstanceOf(RgbaColor::class, $color); + $this->assertEquals([181, 55, 23, 51], $color->toArray()); } } diff --git a/tests/Drivers/Gd/Decoders/TransparentColorDecoderTest.php b/tests/Drivers/Gd/Decoders/TransparentColorDecoderTest.php deleted file mode 100644 index f680a367..00000000 --- a/tests/Drivers/Gd/Decoders/TransparentColorDecoderTest.php +++ /dev/null @@ -1,25 +0,0 @@ -decode('transparent'); - $this->assertInstanceOf(Color::class, $color); - $this->assertEquals(0, $color->red()); - $this->assertEquals(0, $color->green()); - $this->assertEquals(0, $color->blue()); - $this->assertEquals(0, $color->alpha()); - } -} diff --git a/tests/Drivers/Imagick/ColorTransformerTest.php b/tests/Drivers/Imagick/ColorTransformerTest.php new file mode 100644 index 00000000..fa71db90 --- /dev/null +++ b/tests/Drivers/Imagick/ColorTransformerTest.php @@ -0,0 +1,37 @@ +assertInstanceOf(Color::class, $result); + $this->assertEquals([181, 55, 23, 153], $result->toArray()); + + $result = ColorTransformer::colorFromPixel(new ImagickPixel('rgba(255, 255, 255, 1)')); + $this->assertInstanceOf(Color::class, $result); + $this->assertEquals([255, 255, 255, 255], $result->toArray()); + } + + public function testToPixel(): void + { + $result = ColorTransformer::colorToPixel(new Color(181, 55, 23, 153)); + $this->assertInstanceOf(ImagickPixel::class, $result); + $this->assertEquals('srgba(181,55,23,0.6)', $result->getColorAsString()); + + $result = ColorTransformer::colorToPixel(new Color(255, 255, 255, 255)); + $this->assertInstanceOf(ImagickPixel::class, $result); + $this->assertEquals('srgba(255,255,255,1)', $result->getColorAsString()); + + $result = ColorTransformer::colorToPixel(new Color(255, 255, 255, 170)); + $this->assertInstanceOf(ImagickPixel::class, $result); + $this->assertEquals('srgba(255,255,255,0.699992)', $result->getColorAsString()); + } +} diff --git a/tests/Drivers/Imagick/Decoders/HtmlColorNameDecoderTest.php b/tests/Drivers/Imagick/Decoders/HtmlColorNameDecoderTest.php index 4328f4b1..ba9790f6 100644 --- a/tests/Drivers/Imagick/Decoders/HtmlColorNameDecoderTest.php +++ b/tests/Drivers/Imagick/Decoders/HtmlColorNameDecoderTest.php @@ -2,7 +2,7 @@ namespace Intervention\Image\Tests\Drivers\Imagick\Decoders; -use Intervention\Image\Drivers\Imagick\Color; +use Intervention\Image\Colors\Rgb\Color; use Intervention\Image\Drivers\Imagick\Decoders\HtmlColorNameDecoder; use Intervention\Image\Tests\TestCase; diff --git a/tests/Drivers/Imagick/Decoders/RgbStringColorDecoderTest.php b/tests/Drivers/Imagick/Decoders/RgbStringColorDecoderTest.php index c3b6a52a..3029bc02 100644 --- a/tests/Drivers/Imagick/Decoders/RgbStringColorDecoderTest.php +++ b/tests/Drivers/Imagick/Decoders/RgbStringColorDecoderTest.php @@ -2,7 +2,8 @@ namespace Intervention\Image\Tests\Drivers\Imagick\Decoders; -use Intervention\Image\Drivers\Imagick\Color; +use Intervention\Image\Colors\Rgb\Color as RgbColor; +use Intervention\Image\Colors\Rgba\Color as RgbaColor; use Intervention\Image\Drivers\Imagick\Decoders\RgbStringColorDecoder; use Intervention\Image\Tests\TestCase; @@ -16,21 +17,15 @@ class RgbStringColorDecoderTest extends TestCase { $decoder = new RgbStringColorDecoder(); $color = $decoder->decode('rgb(181, 55, 23)'); - $this->assertInstanceOf(Color::class, $color); - $this->assertEquals(181, $color->red()); - $this->assertEquals(55, $color->green()); - $this->assertEquals(23, $color->blue()); - $this->assertEquals(1, $color->alpha()); + $this->assertInstanceOf(RgbColor::class, $color); + $this->assertEquals([181, 55, 23], $color->toArray()); } public function testDecodeRgba(): void { $decoder = new RgbStringColorDecoder(); - $color = $decoder->decode('rgba(181, 55, 23, 0.5)'); - $this->assertInstanceOf(Color::class, $color); - $this->assertEquals(181, $color->red()); - $this->assertEquals(55, $color->green()); - $this->assertEquals(23, $color->blue()); - $this->assertEquals(.5, $color->alpha()); + $color = $decoder->decode('rgba(181, 55, 23, 0.2)'); + $this->assertInstanceOf(RgbaColor::class, $color); + $this->assertEquals([181, 55, 23, 51], $color->toArray()); } } diff --git a/tests/Drivers/Imagick/Decoders/TransparentColorDecoderTest.php b/tests/Drivers/Imagick/Decoders/TransparentColorDecoderTest.php deleted file mode 100644 index 011947ad..00000000 --- a/tests/Drivers/Imagick/Decoders/TransparentColorDecoderTest.php +++ /dev/null @@ -1,25 +0,0 @@ -decode('transparent'); - $this->assertInstanceOf(Color::class, $color); - $this->assertEquals(0, $color->red()); - $this->assertEquals(0, $color->green()); - $this->assertEquals(0, $color->blue()); - $this->assertEquals(0, $color->alpha()); - } -} From d95120153f9866b05de8e1dffbbb4e7b72ecd176 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Mon, 16 Oct 2023 17:28:04 +0200 Subject: [PATCH 282/476] Remove Rgba color space RGB colors with alpha channel are now handles by RGB. --- src/Colors/Cmyk/Color.php | 7 -- src/Colors/Cmyk/Colorspace.php | 3 +- src/Colors/Parser.php | 1 - src/Colors/{Rgba => Rgb}/Channels/Alpha.php | 2 +- src/Colors/Rgb/Color.php | 44 +++++-- src/Colors/Rgb/Colorspace.php | 11 -- src/Colors/Rgb/Parser.php | 51 ++++++++ src/Colors/Rgba/Channels/Blue.php | 10 -- src/Colors/Rgba/Channels/Green.php | 10 -- src/Colors/Rgba/Channels/Red.php | 10 -- src/Colors/Rgba/Color.php | 99 --------------- src/Colors/Rgba/Colorspace.php | 55 -------- src/Colors/Rgba/Parser.php | 68 ---------- src/Drivers/Gd/ColorTransformer.php | 6 +- src/Interfaces/ColorInterface.php | 2 - tests/Colors/Cmyk/ColorTest.php | 9 -- tests/Colors/ParserTest.php | 3 +- tests/Colors/Rgb/ColorTest.php | 9 -- tests/Colors/Rgb/ParserTest.php | 3 - tests/Colors/Rgba/ChannelTest.php | 67 ---------- tests/Colors/Rgba/ColorTest.php | 119 ------------------ tests/Colors/Rgba/ColorspaceTest.php | 26 ---- tests/Colors/Rgba/ParserTest.php | 91 -------------- tests/Drivers/Gd/ColorTransformerTest.php | 2 +- .../Gd/Decoders/RgbStringColorDecoderTest.php | 3 +- tests/Drivers/Gd/InputHandlerTest.php | 7 +- .../Drivers/Imagick/ColorTransformerTest.php | 2 +- tests/Drivers/Imagick/InputHandlerTest.php | 7 +- 28 files changed, 101 insertions(+), 626 deletions(-) rename src/Colors/{Rgba => Rgb}/Channels/Alpha.php (81%) delete mode 100644 src/Colors/Rgba/Channels/Blue.php delete mode 100644 src/Colors/Rgba/Channels/Green.php delete mode 100644 src/Colors/Rgba/Channels/Red.php delete mode 100644 src/Colors/Rgba/Color.php delete mode 100644 src/Colors/Rgba/Colorspace.php delete mode 100644 src/Colors/Rgba/Parser.php delete mode 100644 tests/Colors/Rgba/ChannelTest.php delete mode 100644 tests/Colors/Rgba/ColorTest.php delete mode 100644 tests/Colors/Rgba/ColorspaceTest.php delete mode 100644 tests/Colors/Rgba/ParserTest.php diff --git a/src/Colors/Cmyk/Color.php b/src/Colors/Cmyk/Color.php index 74ee0540..bf6cb401 100644 --- a/src/Colors/Cmyk/Color.php +++ b/src/Colors/Cmyk/Color.php @@ -9,8 +9,6 @@ use Intervention\Image\Colors\Cmyk\Channels\Key; use Intervention\Image\Colors\Cmyk\Colorspace as CmykColorspace; use Intervention\Image\Colors\Rgb\Color as RgbColor; use Intervention\Image\Colors\Rgb\Colorspace as RgbColorspace; -use Intervention\Image\Colors\Rgba\Colorspace as RgbaColorspace; -use Intervention\Image\Colors\Rgba\Color as RgbaColor; use Intervention\Image\Colors\Traits\CanHandleChannels; use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\ColorspaceInterface; @@ -81,11 +79,6 @@ class Color implements ColorInterface return $this->convertTo(RgbColorspace::class); } - public function toRgba(): RgbaColor - { - return $this->convertTo(RgbaColorspace::class); - } - public function toCmyk(): self { return $this->convertTo(CmykColorspace::class); diff --git a/src/Colors/Cmyk/Colorspace.php b/src/Colors/Cmyk/Colorspace.php index 69c6c82a..76ce8ea3 100644 --- a/src/Colors/Cmyk/Colorspace.php +++ b/src/Colors/Cmyk/Colorspace.php @@ -3,7 +3,6 @@ namespace Intervention\Image\Colors\Cmyk; use Intervention\Image\Colors\Rgb\Color as RgbColor; -use Intervention\Image\Colors\Rgba\Color as RgbaColor; use Intervention\Image\Colors\Cmyk\Color as CmykColor; use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\ColorspaceInterface; @@ -18,7 +17,7 @@ class Colorspace implements ColorspaceInterface public function convertColor(ColorInterface $color): ColorInterface { return match (get_class($color)) { - RgbColor::class, RgbaColor::class => $this->convertRgbColor($color), + RgbColor::class => $this->convertRgbColor($color), default => $color, }; } diff --git a/src/Colors/Parser.php b/src/Colors/Parser.php index edd2b2b4..dcf91c59 100644 --- a/src/Colors/Parser.php +++ b/src/Colors/Parser.php @@ -9,7 +9,6 @@ class Parser { protected static $parsers = [ Rgb\Parser::class, - Rgba\Parser::class, Cmyk\Parser::class, ]; diff --git a/src/Colors/Rgba/Channels/Alpha.php b/src/Colors/Rgb/Channels/Alpha.php similarity index 81% rename from src/Colors/Rgba/Channels/Alpha.php rename to src/Colors/Rgb/Channels/Alpha.php index e69a437e..7172791f 100644 --- a/src/Colors/Rgba/Channels/Alpha.php +++ b/src/Colors/Rgb/Channels/Alpha.php @@ -1,6 +1,6 @@ channels = [ new Red($r), new Green($g), new Blue($b), + new Alpha($a), ]; } @@ -44,6 +44,11 @@ class Color implements ColorInterface return $this->channel(Blue::class); } + public function alpha(): Alpha + { + return $this->channel(Alpha::class); + } + public function toArray(): array { return array_map(function (ColorChannelInterface $channel) { @@ -53,12 +58,23 @@ class Color implements ColorInterface public function toHex(string $prefix = ''): string { + if ($this->isFullyOpaque()) { + return sprintf( + '%s%02x%02x%02x', + $prefix, + $this->red()->value(), + $this->green()->value(), + $this->blue()->value() + ); + } + return sprintf( - '%s%02x%02x%02x', + '%s%02x%02x%02x%02x', $prefix, $this->red()->value(), $this->green()->value(), - $this->blue()->value() + $this->blue()->value(), + $this->alpha()->value() ); } @@ -82,18 +98,28 @@ class Color implements ColorInterface return $this->convertTo(CmykColorspace::class); } - public function toRgba(): RgbaColor + public function isFullyOpaque(): bool { - return $this->convertTo(RgbaColorspace::class); + return $this->alpha()->value() === 255; } public function toString(): string { + if ($this->isFullyOpaque()) { + return sprintf( + 'rgb(%d, %d, %d)', + $this->red()->value(), + $this->green()->value(), + $this->blue()->value() + ); + } + return sprintf( - 'rgb(%d, %d, %d)', + 'rgba(%d, %d, %d, %.1F)', $this->red()->value(), $this->green()->value(), - $this->blue()->value() + $this->blue()->value(), + $this->alpha()->normalize(), ); } diff --git a/src/Colors/Rgb/Colorspace.php b/src/Colors/Rgb/Colorspace.php index 489df51d..4c0bcb0f 100644 --- a/src/Colors/Rgb/Colorspace.php +++ b/src/Colors/Rgb/Colorspace.php @@ -3,7 +3,6 @@ namespace Intervention\Image\Colors\Rgb; use Intervention\Image\Colors\Cmyk\Color as CmykColor; -use Intervention\Image\Colors\Rgba\Color as RgbaColor; use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\ColorspaceInterface; @@ -13,7 +12,6 @@ class Colorspace implements ColorspaceInterface { return match (get_class($color)) { CmykColor::class => $this->convertCmykColor($color), - RgbaColor::class => $this->convertRgbaColor($color), default => $color, }; } @@ -26,13 +24,4 @@ class Colorspace implements ColorspaceInterface (int) (255 * (1 - $color->yellow()->normalize()) * (1 - $color->key()->normalize())), ); } - - protected function convertRgbaColor(RgbaColor $color): Color - { - return new Color( - $color->red()->value(), - $color->green()->value(), - $color->blue()->value() - ); - } } diff --git a/src/Colors/Rgb/Parser.php b/src/Colors/Rgb/Parser.php index 82aef1e6..746a40d6 100644 --- a/src/Colors/Rgb/Parser.php +++ b/src/Colors/Rgb/Parser.php @@ -35,6 +35,7 @@ class Parser public static function fromHex(string $input): Color { + // Hexadecimal colors $pattern = '/^#?(?P[0-9a-f]{3}|[0-9a-f]{6})$/i'; $result = preg_match($pattern, $input, $matches); @@ -48,10 +49,35 @@ class Parser default => throw new ColorException('Unable to parse color'), }; + try { + return new Color( + strlen($matches[0]) == '1' ? hexdec($matches[0] . $matches[0]) : hexdec($matches[0]), + strlen($matches[1]) == '1' ? hexdec($matches[1] . $matches[1]) : hexdec($matches[1]), + strlen($matches[2]) == '1' ? hexdec($matches[2] . $matches[2]) : hexdec($matches[2]) + ); + } catch (ColorException $e) { + # move on + } + + // Hexadecimal colors with transparency + $pattern = '/^#?(?P[0-9a-f]{4}|[0-9a-f]{8})$/i'; + $result = preg_match($pattern, $input, $matches); + + if ($result !== 1) { + throw new ColorException('Unable to parse color'); + } + + $matches = match (strlen($matches['hex'])) { + 4 => str_split($matches['hex']), + 8 => str_split($matches['hex'], 2), + default => throw new ColorException('Unable to parse color'), + }; + return new Color( strlen($matches[0]) == '1' ? hexdec($matches[0] . $matches[0]) : hexdec($matches[0]), strlen($matches[1]) == '1' ? hexdec($matches[1] . $matches[1]) : hexdec($matches[1]), strlen($matches[2]) == '1' ? hexdec($matches[2] . $matches[2]) : hexdec($matches[2]), + strlen($matches[3]) == '1' ? hexdec($matches[3] . $matches[3]) : hexdec($matches[3]), ); } @@ -79,6 +105,31 @@ class Parser ); } + // rgba(255, 255, 255, 1.0) + $pattern = '/^s?rgba\((?P[0-9]{1,3}), *(?P[0-9]{1,3}), *(?P[0-9]{1,3}), *(?P((1|0))?(\.[0-9]+)?)\)$/'; + $result = preg_match($pattern, $input, $matches); + + if ($result === 1) { + return new Color( + $matches['r'], + $matches['g'], + $matches['b'], + intval(round(floatval($matches['a']) * 255)) + ); + } + + // rgba(100%, 100%, 100%, 100%) + $pattern = '/s?rgba\((?P[0-9\.]+)%, ?(?P[0-9\.]+)%, ?(?P[0-9\.]+)%, ?(?P[0-9\.]+)%\)/'; + $result = preg_match($pattern, $input, $matches); + if ($result === 1) { + return new Color( + intval(round(floatval($matches['r']) / 100 * 255)), + intval(round(floatval($matches['g']) / 100 * 255)), + intval(round(floatval($matches['b']) / 100 * 255)), + intval(round(floatval($matches['a']) / 100 * 255)) + ); + } + throw new ColorException('Unable to parse color'); } diff --git a/src/Colors/Rgba/Channels/Blue.php b/src/Colors/Rgba/Channels/Blue.php deleted file mode 100644 index 54b9d5bc..00000000 --- a/src/Colors/Rgba/Channels/Blue.php +++ /dev/null @@ -1,10 +0,0 @@ -channels = [ - new Red($r), - new Green($g), - new Blue($b), - new Alpha($a), - ]; - } - - public function red(): Red - { - return $this->channel(Red::class); - } - - public function green(): Green - { - return $this->channel(Green::class); - } - - public function blue(): Blue - { - return $this->channel(Blue::class); - } - - public function alpha(): Alpha - { - return $this->channel(Alpha::class); - } - - public function isFullyOpaque(): bool - { - return $this->alpha()->value() === 255; - } - - public function toHex(string $prefix = ''): string - { - if ($this->isFullyOpaque()) { - return parent::toHex($prefix); - } - - return sprintf( - '%s%02x%02x%02x%02x', - $prefix, - $this->red()->value(), - $this->green()->value(), - $this->blue()->value(), - $this->alpha()->value() - ); - } - - public function toRgb(): RgbColor - { - return $this->convertTo(RgbColorspace::class); - } - - public function toRgba(): self - { - return $this; - } - - public function toCmyk(): CmykColor - { - return $this->convertTo(CmykColorspace::class); - } - - public function toString(): string - { - return sprintf( - 'rgba(%d, %d, %d, %.1F)', - $this->red()->value(), - $this->green()->value(), - $this->blue()->value(), - $this->alpha()->normalize(), - ); - } - - public function __toString(): string - { - return $this->toString(); - } -} diff --git a/src/Colors/Rgba/Colorspace.php b/src/Colors/Rgba/Colorspace.php deleted file mode 100644 index 28b53f83..00000000 --- a/src/Colors/Rgba/Colorspace.php +++ /dev/null @@ -1,55 +0,0 @@ - $this->convertCmykColor($color), - RgbColor::class => $this->convertRgbColor($color), - default => $color, - }; - } - - /** - * Convert given color to the RGBA colorspace - * - * @param RgbColor $color - * @return Color - */ - protected function convertRgbColor(RgbColor $color): Color - { - return new Color( - $color->red()->value(), - $color->green()->value(), - $color->blue()->value(), - 255 - ); - } - - /** - * Convert given color to the RGBA colorspace - * - * @param CmykColor $color - * @return Color - */ - protected function convertCmykColor(CmykColor $color): Color - { - return new Color( - (int) (255 * (1 - $color->cyan()->normalize()) * (1 - $color->key()->normalize())), - (int) (255 * (1 - $color->magenta()->normalize()) * (1 - $color->key()->normalize())), - (int) (255 * (1 - $color->yellow()->normalize()) * (1 - $color->key()->normalize())), - 255 - ); - } -} diff --git a/src/Colors/Rgba/Parser.php b/src/Colors/Rgba/Parser.php deleted file mode 100644 index efaa9f70..00000000 --- a/src/Colors/Rgba/Parser.php +++ /dev/null @@ -1,68 +0,0 @@ -toRgba(); - } catch (ColorException $e) { - // move on - } - - $pattern = '/^#?(?P[0-9a-f]{4}|[0-9a-f]{8})$/i'; - $result = preg_match($pattern, $input, $matches); - - if ($result !== 1) { - throw new ColorException('Unable to parse color'); - } - - $matches = match (strlen($matches['hex'])) { - 4 => str_split($matches['hex']), - 8 => str_split($matches['hex'], 2), - default => throw new ColorException('Unable to parse color'), - }; - - return new Color( - strlen($matches[0]) == '1' ? hexdec($matches[0] . $matches[0]) : hexdec($matches[0]), - strlen($matches[1]) == '1' ? hexdec($matches[1] . $matches[1]) : hexdec($matches[1]), - strlen($matches[2]) == '1' ? hexdec($matches[2] . $matches[2]) : hexdec($matches[2]), - strlen($matches[3]) == '1' ? hexdec($matches[3] . $matches[3]) : hexdec($matches[3]), - ); - } - - public static function fromString(string $input): Color - { - // rgba(255, 255, 255, 1.0) - $pattern = '/^s?rgba\((?P[0-9]{1,3}), *(?P[0-9]{1,3}), *(?P[0-9]{1,3}), *(?P((1|0))?(\.[0-9]+)?)\)$/'; - $result = preg_match($pattern, $input, $matches); - - if ($result === 1) { - return new Color( - $matches['r'], - $matches['g'], - $matches['b'], - intval(round(floatval($matches['a']) * 255)) - ); - } - - // rgba(100%, 100%, 100%, 100%) - $pattern = '/s?rgba\((?P[0-9\.]+)%, ?(?P[0-9\.]+)%, ?(?P[0-9\.]+)%, ?(?P[0-9\.]+)%\)/'; - $result = preg_match($pattern, $input, $matches); - if ($result === 1) { - return new Color( - intval(round(floatval($matches['r']) / 100 * 255)), - intval(round(floatval($matches['g']) / 100 * 255)), - intval(round(floatval($matches['b']) / 100 * 255)), - intval(round(floatval($matches['a']) / 100 * 255)) - ); - } - - throw new ColorException('Unable to parse color'); - } -} diff --git a/src/Drivers/Gd/ColorTransformer.php b/src/Drivers/Gd/ColorTransformer.php index aac312e7..62d9c8bd 100644 --- a/src/Drivers/Gd/ColorTransformer.php +++ b/src/Drivers/Gd/ColorTransformer.php @@ -3,12 +3,12 @@ namespace Intervention\Image\Drivers\Gd; use Intervention\Image\Interfaces\ColorInterface; -use Intervention\Image\Colors\Rgba\Color; +use Intervention\Image\Colors\Rgb\Color; class ColorTransformer { /** - * Transforms GD Library integer color value to RGBA color object + * Transforms GD Library integer color value to RGB color object * * @param int $value * @return Color @@ -35,7 +35,7 @@ class ColorTransformer */ public static function colorToInteger(ColorInterface $color): int { - $color = $color->toRgba(); + $color = $color->toRgb(); $r = $color->red()->value(); $g = $color->green()->value(); diff --git a/src/Interfaces/ColorInterface.php b/src/Interfaces/ColorInterface.php index 46a4dab8..7cf18317 100644 --- a/src/Interfaces/ColorInterface.php +++ b/src/Interfaces/ColorInterface.php @@ -4,12 +4,10 @@ namespace Intervention\Image\Interfaces; use Intervention\Image\Colors\Cmyk\Color as CmykColor; use Intervention\Image\Colors\Rgb\Color as RgbColor; -use Intervention\Image\Colors\Rgba\Color as RgbaColor; interface ColorInterface { public function toRgb(): RgbColor; - public function toRgba(): RgbaColor; public function toCmyk(): CmykColor; public function toArray(): array; public function toString(): string; diff --git a/tests/Colors/Cmyk/ColorTest.php b/tests/Colors/Cmyk/ColorTest.php index 2052480f..66e87283 100644 --- a/tests/Colors/Cmyk/ColorTest.php +++ b/tests/Colors/Cmyk/ColorTest.php @@ -3,7 +3,6 @@ namespace Intervention\Image\Tests\Colors\Cmyk; use Intervention\Image\Colors\Rgb\Color as RgbColor; -use Intervention\Image\Colors\Rgba\Color as RgbaColor; use Intervention\Image\Colors\Cmyk\Channels\Cyan; use Intervention\Image\Colors\Cmyk\Channels\Key; use Intervention\Image\Colors\Cmyk\Channels\Magenta; @@ -83,12 +82,4 @@ class ColorTest extends TestCase $this->assertInstanceOf(RgbColor::class, $converted); $this->assertEquals([255, 204, 204], $converted->toArray()); } - - public function testToRgba(): void - { - $color = new Color(0, 20, 20, 0); - $converted = $color->toRgba(); - $this->assertInstanceOf(RgbaColor::class, $converted); - $this->assertEquals([255, 204, 204, 255], $converted->toArray()); - } } diff --git a/tests/Colors/ParserTest.php b/tests/Colors/ParserTest.php index a783f62a..4e158140 100644 --- a/tests/Colors/ParserTest.php +++ b/tests/Colors/ParserTest.php @@ -4,7 +4,6 @@ namespace Intervention\Image\Tests\Colors; use Intervention\Image\Colors\Cmyk\Color as CmykColor; use Intervention\Image\Colors\Rgb\Color as RgbColor; -use Intervention\Image\Colors\Rgba\Color as RgbaColor; use Intervention\Image\Colors\Parser; use Intervention\Image\Tests\TestCase; @@ -25,7 +24,7 @@ class ParserTest extends TestCase $this->assertEquals([204, 204, 204], $color->toArray()); $color = Parser::parse('rgba(204, 204, 204, 1)'); - $this->assertInstanceOf(RgbaColor::class, $color); + $this->assertInstanceOf(RgbColor::class, $color); $this->assertEquals([204, 204, 204, 255], $color->toArray()); $color = Parser::parse('cccc'); diff --git a/tests/Colors/Rgb/ColorTest.php b/tests/Colors/Rgb/ColorTest.php index 5c542fa7..8121a47f 100644 --- a/tests/Colors/Rgb/ColorTest.php +++ b/tests/Colors/Rgb/ColorTest.php @@ -3,7 +3,6 @@ namespace Intervention\Image\Tests\Colors\Rgb; use Intervention\Image\Colors\Cmyk\Color as CmykColor; -use Intervention\Image\Colors\Rgba\Color as RgbaColor; use Intervention\Image\Colors\Rgb\Channels\Red; use Intervention\Image\Colors\Rgb\Channels\Green; use Intervention\Image\Colors\Rgb\Channels\Blue; @@ -111,12 +110,4 @@ class ColorTest extends TestCase $color = new Color(181, 55, 23); $this->assertInstanceOf(Color::class, $color->toRgb()); } - - public function testToRgba(): void - { - $color = new Color(181, 55, 23); - $converted = $color->toRgba(); - $this->assertInstanceOf(RgbaColor::class, $converted); - $this->assertEquals([181, 55, 23, 255], $converted->toArray()); - } } diff --git a/tests/Colors/Rgb/ParserTest.php b/tests/Colors/Rgb/ParserTest.php index b6ccce7d..d98b8601 100644 --- a/tests/Colors/Rgb/ParserTest.php +++ b/tests/Colors/Rgb/ParserTest.php @@ -69,9 +69,6 @@ class ParserTest extends TestCase $this->expectException(ColorException::class); (new Parser())->fromString('rgb(120)'); - - $this->expectException(ColorException::class); - (new Parser())->fromString('rgba(204,204,204,1)'); } public function testFromName(): void diff --git a/tests/Colors/Rgba/ChannelTest.php b/tests/Colors/Rgba/ChannelTest.php deleted file mode 100644 index 3edcca17..00000000 --- a/tests/Colors/Rgba/ChannelTest.php +++ /dev/null @@ -1,67 +0,0 @@ -assertInstanceOf(Channel::class, $channel); - } - - public function testValue(): void - { - $channel = new Channel(10); - $this->assertEquals(10, $channel->value()); - } - - public function testNormalize(): void - { - $channel = new Channel(255); - $this->assertEquals(1, $channel->normalize()); - $channel = new Channel(0); - $this->assertEquals(0, $channel->normalize()); - $channel = new Channel(51); - $this->assertEquals(.2, $channel->normalize()); - } - - public function testValidate(): void - { - $this->expectException(ColorException::class); - new Channel(256); - - $this->expectException(ColorException::class); - new Channel(-1); - } - - public function testToString(): void - { - $channel = new Channel(255); - $this->assertEquals("255", $channel->toString()); - - $channel = new Alpha(0); - $this->assertEquals("0", $channel->toString()); - - $channel = new Alpha(51); - $this->assertEquals("0.2", $channel->toString()); - - $channel = new Alpha(255); - $this->assertEquals("1", $channel->toString()); - - $channel = new Alpha(170); - $this->assertEquals("0.666667", $channel->toString()); - } -} diff --git a/tests/Colors/Rgba/ColorTest.php b/tests/Colors/Rgba/ColorTest.php deleted file mode 100644 index cf9b9e61..00000000 --- a/tests/Colors/Rgba/ColorTest.php +++ /dev/null @@ -1,119 +0,0 @@ -assertInstanceOf(Color::class, $color); - } - - public function testChannels(): void - { - $color = new Color(10, 20, 30, 255); - $this->assertIsArray($color->channels()); - $this->assertCount(4, $color->channels()); - } - - public function testChannel(): void - { - $color = new Color(10, 20, 30, 255); - $channel = $color->channel(Red::class); - $this->assertInstanceOf(Red::class, $channel); - $this->assertEquals(10, $channel->value()); - $channel = $color->channel(Alpha::class); - $this->assertInstanceOf(Alpha::class, $channel); - $this->assertEquals(255, $channel->value()); - } - - public function testRedGreenBlueAlpha(): void - { - $color = new Color(10, 20, 30, 255); - $this->assertInstanceOf(Red::class, $color->red()); - $this->assertInstanceOf(Green::class, $color->green()); - $this->assertInstanceOf(Blue::class, $color->blue()); - $this->assertInstanceOf(Alpha::class, $color->alpha()); - $this->assertEquals(10, $color->red()->value()); - $this->assertEquals(20, $color->green()->value()); - $this->assertEquals(30, $color->blue()->value()); - $this->assertEquals(255, $color->alpha()->value()); - } - - public function testToArray(): void - { - $color = new Color(10, 20, 30, 255); - $this->assertEquals([10, 20, 30, 255], $color->toArray()); - } - - public function testToHex(): void - { - $color = new Color(181, 55, 23, 0); - $this->assertEquals('b5371700', $color->toHex()); - $this->assertEquals('#b5371700', $color->toHex('#')); - - $color = new Color(181, 55, 23, 255); - $this->assertEquals('b53717', $color->toHex()); - - $color = new Color(181, 55, 23, 204); - $this->assertEquals('b53717cc', $color->toHex()); - } - - public function testNormalize(): void - { - $color = new Color(255, 0, 51, 255); - $this->assertEquals([1.0, 0.0, 0.2, 1.0], $color->normalize()); - $color = new Color(255, 0, 51, 51); - $this->assertEquals([1.0, 0.0, 0.2, 0.2], $color->normalize()); - } - - public function testToString(): void - { - $color = new Color(255, 255, 255, 255); - $this->assertEquals('rgba(255, 255, 255, 1.0)', (string) $color); - - $color = new Color(10, 20, 30, 85); - $this->assertEquals('rgba(10, 20, 30, 0.3)', (string) $color); - } - - public function testToRgba(): void - { - $color = new Color(181, 55, 23, 120); - $converted = $color->toRgba(); - $this->assertInstanceOf(Color::class, $converted); - } - - public function testToRgb(): void - { - $color = new Color(181, 55, 23, 120); - $converted = $color->toRgb(); - $this->assertInstanceOf(RgbColor::class, $converted); - $this->assertEquals([181, 55, 23], $converted->toArray()); - } - - public function testToCmyk(): void - { - $color = new Color(0, 0, 0, 255); - $converted = $color->toCmyk(); - $this->assertInstanceOf(CmykColor::class, $converted); - $this->assertEquals([0, 0, 0, 100], $converted->toArray()); - - $color = new Color(0, 0, 0, 0); - $converted = $color->toCmyk(); - $this->assertInstanceOf(CmykColor::class, $converted); - $this->assertEquals([0, 0, 0, 100], $converted->toArray()); - } -} diff --git a/tests/Colors/Rgba/ColorspaceTest.php b/tests/Colors/Rgba/ColorspaceTest.php deleted file mode 100644 index 2ff88b0f..00000000 --- a/tests/Colors/Rgba/ColorspaceTest.php +++ /dev/null @@ -1,26 +0,0 @@ -assertInstanceOf( - RgbaColor::class, - $colorspace->convertColor( - new CmykColor(0, 0, 0, 0) - ) - ); - } -} diff --git a/tests/Colors/Rgba/ParserTest.php b/tests/Colors/Rgba/ParserTest.php deleted file mode 100644 index 89441646..00000000 --- a/tests/Colors/Rgba/ParserTest.php +++ /dev/null @@ -1,91 +0,0 @@ -assertInstanceOf(Color::class, $color); - $this->assertEquals([204, 204, 204, 255], $color->toArray()); - - $color = Parser::parse('rgba(204, 204, 204, 1)'); - $this->assertInstanceOf(Color::class, $color); - $this->assertEquals([204, 204, 204, 255], $color->toArray()); - - $color = Parser::parse('salmon'); - $this->assertInstanceOf(Color::class, $color); - $this->assertEquals('fa8072', $color->toHex()); - } - - public function testFromHex(): void - { - $color = Parser::fromHex('ccc'); - $this->assertInstanceOf(Color::class, $color); - $this->assertEquals([204, 204, 204, 255], $color->toArray()); - - $color = Parser::fromHex('cccccc'); - $this->assertInstanceOf(Color::class, $color); - $this->assertEquals([204, 204, 204, 255], $color->toArray()); - - $color = Parser::fromHex('#cccccc'); - $this->assertInstanceOf(Color::class, $color); - $this->assertEquals([204, 204, 204, 255], $color->toArray()); - - $color = Parser::fromHex('#cccccccc'); - $this->assertInstanceOf(Color::class, $color); - $this->assertEquals([204, 204, 204, 204], $color->toArray()); - - $color = Parser::fromHex('#cccc'); - $this->assertInstanceOf(Color::class, $color); - $this->assertEquals([204, 204, 204, 204], $color->toArray()); - - $color = Parser::fromHex('cccccccc'); - $this->assertInstanceOf(Color::class, $color); - $this->assertEquals([204, 204, 204, 204], $color->toArray()); - - $color = Parser::fromHex('cccc'); - $this->assertInstanceOf(Color::class, $color); - $this->assertEquals([204, 204, 204, 204], $color->toArray()); - } - - public function testFromString(): void - { - $color = Parser::fromString('rgba(204, 204, 204, 1)'); - $this->assertInstanceOf(Color::class, $color); - $this->assertEquals([204, 204, 204, 255], $color->toArray()); - - $color = Parser::fromString('rgba(204,204,204,1.0)'); - $this->assertInstanceOf(Color::class, $color); - $this->assertEquals([204, 204, 204, 255], $color->toArray()); - - $color = Parser::fromString('rgba(204,204,204,0.2)'); - $this->assertInstanceOf(Color::class, $color); - $this->assertEquals([204, 204, 204, 51], $color->toArray()); - - $color = Parser::fromString('rgba(204,204, 204, .2)'); - $this->assertInstanceOf(Color::class, $color); - $this->assertEquals([204, 204, 204, 51], $color->toArray()); - - $color = Parser::fromString('rgba(100%,20%,25%,100%)'); - $this->assertInstanceOf(Color::class, $color); - $this->assertEquals([255, 51, 64, 255], $color->toArray()); - - $color = Parser::fromString('rgba(100%,74.8064%,25.2497%,100%)'); - $this->assertInstanceOf(Color::class, $color); - $this->assertEquals([255, 191, 64, 255], $color->toArray()); - - $this->expectException(ColorException::class); - $color = Parser::fromString('rgba(204, 204, 204, 1.2)'); - } -} diff --git a/tests/Drivers/Gd/ColorTransformerTest.php b/tests/Drivers/Gd/ColorTransformerTest.php index 24a3fa7c..04260235 100644 --- a/tests/Drivers/Gd/ColorTransformerTest.php +++ b/tests/Drivers/Gd/ColorTransformerTest.php @@ -2,7 +2,7 @@ namespace Intervention\Image\Tests\Drivers\Gd; -use Intervention\Image\Colors\Rgba\Color; +use Intervention\Image\Colors\Rgb\Color; use Intervention\Image\Drivers\Gd\ColorTransformer; use Intervention\Image\Tests\TestCase; diff --git a/tests/Drivers/Gd/Decoders/RgbStringColorDecoderTest.php b/tests/Drivers/Gd/Decoders/RgbStringColorDecoderTest.php index fa73370c..fb19fb98 100644 --- a/tests/Drivers/Gd/Decoders/RgbStringColorDecoderTest.php +++ b/tests/Drivers/Gd/Decoders/RgbStringColorDecoderTest.php @@ -3,7 +3,6 @@ namespace Intervention\Image\Tests\Drivers\Gd\Decoders; use Intervention\Image\Colors\Rgb\Color as RgbColor; -use Intervention\Image\Colors\Rgba\Color as RgbaColor; use Intervention\Image\Drivers\Gd\Decoders\RgbStringColorDecoder; use Intervention\Image\Tests\TestCase; @@ -25,7 +24,7 @@ class RgbStringColorDecoderTest extends TestCase { $decoder = new RgbStringColorDecoder(); $color = $decoder->decode('rgba(181, 55, 23, 0.5)'); - $this->assertInstanceOf(RgbaColor::class, $color); + $this->assertInstanceOf(RgbColor::class, $color); $this->assertEquals([181, 55, 23, 51], $color->toArray()); } } diff --git a/tests/Drivers/Gd/InputHandlerTest.php b/tests/Drivers/Gd/InputHandlerTest.php index 8157f01a..3cdaaee2 100644 --- a/tests/Drivers/Gd/InputHandlerTest.php +++ b/tests/Drivers/Gd/InputHandlerTest.php @@ -3,7 +3,6 @@ namespace Intervention\Image\Tests\Drivers\Gd; use Intervention\Image\Colors\Rgb\Color as RgbColor; -use Intervention\Image\Colors\Rgba\Color as RgbaColor; use Intervention\Image\Drivers\Gd\Image; use Intervention\Image\Drivers\Gd\InputHandler; use Intervention\Image\Exceptions\DecoderException; @@ -83,13 +82,13 @@ class GdInputHandlerTest extends TestCase $handler = new InputHandler(); $input = '#3333'; $result = $handler->handle($input); - $this->assertInstanceOf(RgbaColor::class, $result); + $this->assertInstanceOf(RgbColor::class, $result); $this->assertEquals([51, 51, 51, 51], $result->toArray()); $handler = new InputHandler(); $input = '#33333333'; $result = $handler->handle($input); - $this->assertInstanceOf(RgbaColor::class, $result); + $this->assertInstanceOf(RgbColor::class, $result); $this->assertEquals([51, 51, 51, 51], $result->toArray()); } @@ -102,7 +101,7 @@ class GdInputHandlerTest extends TestCase $handler = new InputHandler(); $result = $handler->handle('rgba(10, 20, 30, 1.0)'); - $this->assertInstanceOf(RgbaColor::class, $result); + $this->assertInstanceOf(RgbColor::class, $result); $this->assertEquals([10, 20, 30, 255], $result->toArray()); } diff --git a/tests/Drivers/Imagick/ColorTransformerTest.php b/tests/Drivers/Imagick/ColorTransformerTest.php index fa71db90..49f16ad1 100644 --- a/tests/Drivers/Imagick/ColorTransformerTest.php +++ b/tests/Drivers/Imagick/ColorTransformerTest.php @@ -3,7 +3,7 @@ namespace Intervention\Image\Tests\Drivers\Imagick; use ImagickPixel; -use Intervention\Image\Colors\Rgba\Color; +use Intervention\Image\Colors\Rgb\Color; use Intervention\Image\Drivers\Imagick\ColorTransformer; use Intervention\Image\Tests\TestCase; diff --git a/tests/Drivers/Imagick/InputHandlerTest.php b/tests/Drivers/Imagick/InputHandlerTest.php index f125f686..e7ce923e 100644 --- a/tests/Drivers/Imagick/InputHandlerTest.php +++ b/tests/Drivers/Imagick/InputHandlerTest.php @@ -3,7 +3,6 @@ namespace Intervention\Image\Tests\Drivers\Imagick; use Intervention\Image\Colors\Rgb\Color as RgbColor; -use Intervention\Image\Colors\Rgba\Color as RgbaColor; use Intervention\Image\Drivers\Imagick\Image; use Intervention\Image\Drivers\Imagick\InputHandler; use Intervention\Image\Exceptions\DecoderException; @@ -83,13 +82,13 @@ class InputHandlerTest extends TestCase $handler = new InputHandler(); $input = '#3333'; $result = $handler->handle($input); - $this->assertInstanceOf(RgbaColor::class, $result); + $this->assertInstanceOf(RgbColor::class, $result); $this->assertEquals([51, 51, 51, 51], $result->toArray()); $handler = new InputHandler(); $input = '#33333333'; $result = $handler->handle($input); - $this->assertInstanceOf(RgbaColor::class, $result); + $this->assertInstanceOf(RgbColor::class, $result); $this->assertEquals([51, 51, 51, 51], $result->toArray()); } @@ -102,7 +101,7 @@ class InputHandlerTest extends TestCase $handler = new InputHandler(); $result = $handler->handle('rgba(10, 20, 30, 1.0)'); - $this->assertInstanceOf(RgbaColor::class, $result); + $this->assertInstanceOf(RgbColor::class, $result); $this->assertEquals([10, 20, 30, 255], $result->toArray()); } } From 6445957646da8174d3c5f8aec73f4d20a22206f1 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Tue, 17 Oct 2023 17:08:45 +0200 Subject: [PATCH 283/476] Move color parsers to decoder classes --- src/Colors/Cmyk/Color.php | 14 + .../Cmyk/Decoders/StringColorDecoder.php | 37 +++ src/Colors/Cmyk/Parser.php | 34 --- src/Colors/Parser.php | 27 -- src/Colors/Rgb/Color.php | 7 + src/Colors/Rgb/Decoders/HexColorDecoder.php | 48 +++ .../Rgb/Decoders/HtmlColornameDecoder.php | 172 +++++++++++ .../Rgb/Decoders/StringColorDecoder.php | 49 +++ src/Colors/Rgb/Parser.php | 286 ------------------ src/Drivers/Gd/Decoders/HexColorDecoder.php | 36 --- .../Gd/Decoders/HtmlColorNameDecoder.php | 28 -- .../Gd/Decoders/RgbStringColorDecoder.php | 36 --- src/Drivers/Gd/Image.php | 7 +- src/Drivers/Gd/InputHandler.php | 32 +- .../Gd/Modifiers/DrawPixelModifier.php | 4 +- src/Drivers/Gd/Modifiers/FillModifier.php | 20 +- .../CanHandleColors.php} | 21 +- .../Imagick/Decoders/HexColorDecoder.php | 36 --- .../Imagick/Decoders/HtmlColorNameDecoder.php | 27 -- .../Decoders/RgbStringColorDecoder.php | 36 --- src/Drivers/Imagick/Image.php | 5 +- src/Drivers/Imagick/InputHandler.php | 32 +- .../Imagick/Modifiers/DrawPixelModifier.php | 10 +- .../Imagick/Modifiers/FillModifier.php | 19 +- .../CanHandleColors.php} | 14 +- src/Interfaces/ColorInterface.php | 9 +- src/Traits/CanCheckType.php | 9 + tests/Colors/Cmyk/ColorTest.php | 2 +- .../Cmyk/Decoders/StringColorDecoderTest.php | 35 +++ tests/Colors/Cmyk/ParserTest.php | 32 -- tests/Colors/ParserTest.php | 46 --- tests/Colors/Rgb/ColorTest.php | 9 +- .../Rgb/Decoders/HexColorDecoderTest.php | 58 ++++ .../Rgb/Decoders/HtmlColornameDecoderTest.php | 30 ++ .../Rgb/Decoders/StringColorDecoderTest.php | 50 +++ tests/Colors/Rgb/ParserTest.php | 80 ----- tests/Drivers/Gd/ColorTransformerTest.php | 30 -- .../Gd/Decoders/HexColorDecoderTest.php | 30 -- .../Gd/Decoders/HtmlColorNameDecoderTest.php | 18 -- .../Gd/Decoders/RgbStringColorDecoderTest.php | 30 -- tests/Drivers/Gd/ImageTest.php | 38 +-- tests/Drivers/Gd/InputHandlerTest.php | 10 +- .../Drivers/Gd/Modifiers/FillModifierTest.php | 6 +- .../Drivers/Gd/Modifiers/FitModifierTest.php | 6 +- .../Gd/Modifiers/FlipFlopModifierTest.php | 4 +- .../Gd/Modifiers/ResizeModifierTest.php | 10 +- .../Drivers/Imagick/ColorTransformerTest.php | 37 --- .../Decoders/HtmlColorNameDecoderTest.php | 18 -- .../Decoders/RgbStringColorDecoderTest.php | 31 -- tests/Drivers/Imagick/InputHandlerTest.php | 10 +- .../Imagick/Modifiers/FillModifierTest.php | 7 +- .../Imagick/Modifiers/FitModifierTest.php | 6 +- .../Modifiers/FlipFlopModifierTest.php | 4 +- .../Imagick/Modifiers/ResizeModifierTest.php | 8 +- tests/TestCase.php | 9 +- 55 files changed, 685 insertions(+), 1024 deletions(-) create mode 100644 src/Colors/Cmyk/Decoders/StringColorDecoder.php delete mode 100644 src/Colors/Cmyk/Parser.php delete mode 100644 src/Colors/Parser.php create mode 100644 src/Colors/Rgb/Decoders/HexColorDecoder.php create mode 100644 src/Colors/Rgb/Decoders/HtmlColornameDecoder.php create mode 100644 src/Colors/Rgb/Decoders/StringColorDecoder.php delete mode 100644 src/Colors/Rgb/Parser.php delete mode 100644 src/Drivers/Gd/Decoders/HexColorDecoder.php delete mode 100644 src/Drivers/Gd/Decoders/HtmlColorNameDecoder.php delete mode 100644 src/Drivers/Gd/Decoders/RgbStringColorDecoder.php rename src/Drivers/Gd/{ColorTransformer.php => Traits/CanHandleColors.php} (74%) delete mode 100644 src/Drivers/Imagick/Decoders/HexColorDecoder.php delete mode 100644 src/Drivers/Imagick/Decoders/HtmlColorNameDecoder.php delete mode 100644 src/Drivers/Imagick/Decoders/RgbStringColorDecoder.php rename src/Drivers/Imagick/{ColorTransformer.php => Traits/CanHandleColors.php} (55%) create mode 100644 tests/Colors/Cmyk/Decoders/StringColorDecoderTest.php delete mode 100644 tests/Colors/Cmyk/ParserTest.php delete mode 100644 tests/Colors/ParserTest.php create mode 100644 tests/Colors/Rgb/Decoders/HexColorDecoderTest.php create mode 100644 tests/Colors/Rgb/Decoders/HtmlColornameDecoderTest.php create mode 100644 tests/Colors/Rgb/Decoders/StringColorDecoderTest.php delete mode 100644 tests/Colors/Rgb/ParserTest.php delete mode 100644 tests/Drivers/Gd/ColorTransformerTest.php delete mode 100644 tests/Drivers/Gd/Decoders/HexColorDecoderTest.php delete mode 100644 tests/Drivers/Gd/Decoders/HtmlColorNameDecoderTest.php delete mode 100644 tests/Drivers/Gd/Decoders/RgbStringColorDecoderTest.php delete mode 100644 tests/Drivers/Imagick/ColorTransformerTest.php delete mode 100644 tests/Drivers/Imagick/Decoders/HtmlColorNameDecoderTest.php delete mode 100644 tests/Drivers/Imagick/Decoders/RgbStringColorDecoderTest.php diff --git a/src/Colors/Cmyk/Color.php b/src/Colors/Cmyk/Color.php index bf6cb401..92766981 100644 --- a/src/Colors/Cmyk/Color.php +++ b/src/Colors/Cmyk/Color.php @@ -29,6 +29,11 @@ class Color implements ColorInterface ]; } + public function toHex(): string + { + return $this->toRgb()->toHex(); + } + public function channels(): array { return $this->channels; @@ -95,6 +100,15 @@ class Color implements ColorInterface ); } + public function isGreyscale(): bool + { + return 0 === array_sum([ + $this->cyan()->value(), + $this->magenta()->value(), + $this->yellow()->value(), + ]); + } + public function __toString(): string { return $this->toString(); diff --git a/src/Colors/Cmyk/Decoders/StringColorDecoder.php b/src/Colors/Cmyk/Decoders/StringColorDecoder.php new file mode 100644 index 00000000..0f05ada9 --- /dev/null +++ b/src/Colors/Cmyk/Decoders/StringColorDecoder.php @@ -0,0 +1,37 @@ +[0-9\.]+%?), ?(?P[0-9\.]+%?), ?(?P[0-9\.]+%?), ?(?P[0-9\.]+%?)\)$/i'; + if (preg_match($pattern, $input, $matches) != 1) { + throw new DecoderException('Unable to decode input'); + } + + $values = array_map(function ($value) { + return intval(round(floatval(trim(str_replace('%', '', $value))))); + }, [$matches['c'], $matches['m'], $matches['y'], $matches['k']]); + + return new Color(...$values); + } +} diff --git a/src/Colors/Cmyk/Parser.php b/src/Colors/Cmyk/Parser.php deleted file mode 100644 index 15fb3a70..00000000 --- a/src/Colors/Cmyk/Parser.php +++ /dev/null @@ -1,34 +0,0 @@ -[0-9\.]+)%?, ?(?P[0-9\.]+)%?, ?(?P[0-9\.]+)%?, ?(?P[0-9\.]+)%?\)$/'; - $result = preg_match($pattern, $input, $matches); - if ($result === 1) { - return new Color( - intval(round(floatval($matches['c']))), - intval(round(floatval($matches['m']))), - intval(round(floatval($matches['y']))), - intval(round(floatval($matches['k']))) - ); - } - - throw new ColorException('Unable to parse color'); - } -} diff --git a/src/Colors/Parser.php b/src/Colors/Parser.php deleted file mode 100644 index dcf91c59..00000000 --- a/src/Colors/Parser.php +++ /dev/null @@ -1,27 +0,0 @@ -red()->value(), $this->green()->value(), $this->blue()->value()]; + + return count(array_unique($values, SORT_REGULAR)) === 1; + } + public function __toString(): string { return $this->toString(); diff --git a/src/Colors/Rgb/Decoders/HexColorDecoder.php b/src/Colors/Rgb/Decoders/HexColorDecoder.php new file mode 100644 index 00000000..43aafee3 --- /dev/null +++ b/src/Colors/Rgb/Decoders/HexColorDecoder.php @@ -0,0 +1,48 @@ +[a-f\d]{3}(?:[a-f\d]?|(?:[a-f\d]{3}(?:[a-f\d]{2})?)?)\b)$/i'; + if (preg_match($pattern, $input, $matches) != 1) { + throw new DecoderException('Unable to decode input'); + } + + $values = str_split($matches['hex']); + $values = match (strlen($matches['hex'])) { + 3, 4 => str_split($matches['hex']), + 6, 8 => str_split($matches['hex'], 2), + default => throw new DecoderException('Unable to decode input'), + }; + + $values = array_map(function ($value) { + return match (strlen($value)) { + 1 => hexdec($value . $value), + 2 => hexdec($value), + default => throw new DecoderException('Unable to decode input'), + }; + }, $values); + + return new Color(...$values); + } +} diff --git a/src/Colors/Rgb/Decoders/HtmlColornameDecoder.php b/src/Colors/Rgb/Decoders/HtmlColornameDecoder.php new file mode 100644 index 00000000..fa4ea630 --- /dev/null +++ b/src/Colors/Rgb/Decoders/HtmlColornameDecoder.php @@ -0,0 +1,172 @@ + '#ffa07a', + 'salmon' => '#fa8072', + 'darksalmon' => '#e9967a', + 'lightcoral' => '#f08080', + 'indianred' => '#cd5c5c', + 'crimson' => '#dc143c', + 'firebrick' => '#b22222', + 'red' => '#ff0000', + 'darkred' => '#8b0000', + 'coral' => '#ff7f50', + 'tomato' => '#ff6347', + 'orangered' => '#ff4500', + 'gold' => '#ffd700', + 'orange' => '#ffa500', + 'darkorange' => '#ff8c00', + 'lightyellow' => '#ffffe0', + 'lemonchiffon' => '#fffacd', + 'lightgoldenrodyellow' => '#fafad2', + 'papayawhip' => '#ffefd5', + 'moccasin' => '#ffe4b5', + 'peachpuff' => '#ffdab9', + 'palegoldenrod' => '#eee8aa', + 'khaki' => '#f0e68c', + 'darkkhaki' => '#bdb76b', + 'yellow' => '#ffff00', + 'lawngreen' => '#7cfc00', + 'chartreuse' => '#7fff00', + 'limegreen' => '#32cd32', + 'lime' => '#00ff00', + 'forestgreen' => '#228b22', + 'green' => '#008000', + 'darkgreen' => '#006400', + 'greenyellow' => '#adff2f', + 'yellowgreen' => '#9acd32', + 'springgreen' => '#00ff7f', + 'mediumspringgreen' => '#00fa9a', + 'lightgreen' => '#90ee90', + 'palegreen' => '#98fb98', + 'darkseagreen' => '#8fbc8f', + 'mediumseagre' => 'en #3cb371', + 'seagreen' => '#2e8b57', + 'olive' => '#808000', + 'darkolivegreen' => '#556b2f', + 'olivedrab' => '#6b8e23', + 'lightcyan' => '#e0ffff', + 'cyan' => '#00ffff', + 'aqua' => '#00ffff', + 'aquamarine' => '#7fffd4', + 'mediumaquamarine' => '#66cdaa', + 'paleturquoise' => '#afeeee', + 'turquoise' => '#40e0d0', + 'mediumturquoise' => '#48d1cc', + 'darkturquoise' => '#00ced1', + 'lightseagreen' => '#20b2aa', + 'cadetblue' => '#5f9ea0', + 'darkcyan' => '#008b8b', + 'teal' => '#008080', + 'powderblue' => '#b0e0e6', + 'lightblue' => '#add8e6', + 'lightskyblue' => '#87cefa', + 'skyblue' => '#87ceeb', + 'deepskyblue' => '#00bfff', + 'lightsteelblue' => '#b0c4de', + 'dodgerblue' => '#1e90ff', + 'cornflowerblue' => '#6495ed', + 'steelblue' => '#4682b4', + 'royalblue' => '#4169e1', + 'blue' => '#0000ff', + 'mediumblue' => '#0000cd', + 'darkblue' => '#00008b', + 'navy' => '#000080', + 'midnightblue' => '#191970', + 'mediumslateblue' => '#7b68ee', + 'slateblue' => '#6a5acd', + 'darkslateblue' => '#483d8b', + 'lavender' => '#e6e6fa', + 'thistle' => '#d8bfd8', + 'plum' => '#dda0dd', + 'violet' => '#ee82ee', + 'orchid' => '#da70d6', + 'fuchsia' => '#ff00ff', + 'magenta' => '#ff00ff', + 'mediumorchid' => '#ba55d3', + 'mediumpurple' => '#9370db', + 'blueviolet' => '#8a2be2', + 'darkviolet' => '#9400d3', + 'darkorchid' => '#9932cc', + 'darkmagenta' => '#8b008b', + 'purple' => '#800080', + 'indigo' => '#4b0082', + 'pink' => '#ffc0cb', + 'lightpink' => '#ffb6c1', + 'hotpink' => '#ff69b4', + 'deeppink' => '#ff1493', + 'palevioletred' => '#db7093', + 'mediumvioletred' => '#c71585', + 'white' => '#ffffff', + 'snow' => '#fffafa', + 'honeydew' => '#f0fff0', + 'mintcream' => '#f5fffa', + 'azure' => '#f0ffff', + 'aliceblue' => '#f0f8ff', + 'ghostwhite' => '#f8f8ff', + 'whitesmoke' => '#f5f5f5', + 'seashell' => '#fff5ee', + 'beige' => '#f5f5dc', + 'oldlace' => '#fdf5e6', + 'floralwhite' => '#fffaf0', + 'ivory' => '#fffff0', + 'antiquewhite' => '#faebd7', + 'linen' => '#faf0e6', + 'lavenderblush' => '#fff0f5', + 'mistyrose' => '#ffe4e1', + 'gainsboro' => '#dcdcdc', + 'lightgray' => '#d3d3d3', + 'silver' => '#c0c0c0', + 'darkgray' => '#a9a9a9', + 'gray' => '#808080', + 'dimgray' => '#696969', + 'lightslategray' => '#778899', + 'slategray' => '#708090', + 'darkslategray' => '#2f4f4f', + 'black' => '#000000', + 'cornsilk' => '#fff8dc', + 'blanchedalmond' => '#ffebcd', + 'bisque' => '#ffe4c4', + 'navajowhite' => '#ffdead', + 'wheat' => '#f5deb3', + 'burlywood' => '#deb887', + 'tan' => '#d2b48c', + 'rosybrown' => '#bc8f8f', + 'sandybrown' => '#f4a460', + 'goldenrod' => '#daa520', + 'peru' => '#cd853f', + 'chocolate' => '#d2691e', + 'saddlebrown' => '#8b4513', + 'sienna' => '#a0522d', + 'brown' => '#a52a2a', + 'maroon' => '#800000', + ]; + + /** + * Decode html color names + * + * @param mixed $input + * @return ImageInterface|ColorInterface + */ + public function decode($input): ImageInterface|ColorInterface + { + if (! is_string($input)) { + throw new DecoderException('Unable to decode input'); + } + + if (!array_key_exists(strtolower($input), static::$names)) { + throw new DecoderException('Unable to decode input'); + } + + return parent::decode(static::$names[strtolower($input)]); + } +} diff --git a/src/Colors/Rgb/Decoders/StringColorDecoder.php b/src/Colors/Rgb/Decoders/StringColorDecoder.php new file mode 100644 index 00000000..86a62b4d --- /dev/null +++ b/src/Colors/Rgb/Decoders/StringColorDecoder.php @@ -0,0 +1,49 @@ +[0-9\.]+%?), ?(?P[0-9\.]+%?), ?(?P[0-9\.]+%?)(?:, ?(?P(?:1)|(?:1\.0*)|(?:0)|(?:0?\.\d+%?)|(?:\d{1,3}%)))?\)$/i'; + if (preg_match($pattern, $input, $matches) != 1) { + throw new DecoderException('Unable to decode input'); + } + + // rgb values + $values = array_map(function ($value) { + return match (strpos($value, '%')) { + false => intval(trim($value)), + default => intval(round(floatval(trim(str_replace('%', '', $value))) / 100 * 255)), + }; + }, [$matches['r'], $matches['g'], $matches['b']]); + + // alpha value + if (array_key_exists('a', $matches)) { + $values[] = match (true) { + strpos($matches['a'], '%') => round(intval(trim(str_replace('%', '', $matches['a']))) / 2.55), + default => intval(round(floatval(trim($matches['a'])) * 255)), + }; + } + + return new Color(...$values); + } +} diff --git a/src/Colors/Rgb/Parser.php b/src/Colors/Rgb/Parser.php deleted file mode 100644 index 746a40d6..00000000 --- a/src/Colors/Rgb/Parser.php +++ /dev/null @@ -1,286 +0,0 @@ -[0-9a-f]{3}|[0-9a-f]{6})$/i'; - $result = preg_match($pattern, $input, $matches); - - if ($result !== 1) { - throw new ColorException('Unable to parse color'); - } - - $matches = match (strlen($matches['hex'])) { - 3 => str_split($matches['hex']), - 6 => str_split($matches['hex'], 2), - default => throw new ColorException('Unable to parse color'), - }; - - try { - return new Color( - strlen($matches[0]) == '1' ? hexdec($matches[0] . $matches[0]) : hexdec($matches[0]), - strlen($matches[1]) == '1' ? hexdec($matches[1] . $matches[1]) : hexdec($matches[1]), - strlen($matches[2]) == '1' ? hexdec($matches[2] . $matches[2]) : hexdec($matches[2]) - ); - } catch (ColorException $e) { - # move on - } - - // Hexadecimal colors with transparency - $pattern = '/^#?(?P[0-9a-f]{4}|[0-9a-f]{8})$/i'; - $result = preg_match($pattern, $input, $matches); - - if ($result !== 1) { - throw new ColorException('Unable to parse color'); - } - - $matches = match (strlen($matches['hex'])) { - 4 => str_split($matches['hex']), - 8 => str_split($matches['hex'], 2), - default => throw new ColorException('Unable to parse color'), - }; - - return new Color( - strlen($matches[0]) == '1' ? hexdec($matches[0] . $matches[0]) : hexdec($matches[0]), - strlen($matches[1]) == '1' ? hexdec($matches[1] . $matches[1]) : hexdec($matches[1]), - strlen($matches[2]) == '1' ? hexdec($matches[2] . $matches[2]) : hexdec($matches[2]), - strlen($matches[3]) == '1' ? hexdec($matches[3] . $matches[3]) : hexdec($matches[3]), - ); - } - - public static function fromString(string $input): Color - { - // rgb(255, 255, 255) - $pattern = '/^s?rgb\((?P[0-9]{1,3}), ?(?P[0-9]{1,3}), ?(?P[0-9]{1,3})\)$/'; - $result = preg_match($pattern, $input, $matches); - if ($result === 1) { - return new Color( - $matches['r'], - $matches['g'], - $matches['b'] - ); - } - - // rgb(100%, 100%, 100%) - $pattern = '/^s?rgb\((?P[0-9\.]+)%, ?(?P[0-9\.]+)%, ?(?P[0-9\.]+)%\)$/'; - $result = preg_match($pattern, $input, $matches); - if ($result === 1) { - return new Color( - intval(round(floatval($matches['r']) / 100 * 255)), - intval(round(floatval($matches['g']) / 100 * 255)), - intval(round(floatval($matches['b']) / 100 * 255)) - ); - } - - // rgba(255, 255, 255, 1.0) - $pattern = '/^s?rgba\((?P[0-9]{1,3}), *(?P[0-9]{1,3}), *(?P[0-9]{1,3}), *(?P((1|0))?(\.[0-9]+)?)\)$/'; - $result = preg_match($pattern, $input, $matches); - - if ($result === 1) { - return new Color( - $matches['r'], - $matches['g'], - $matches['b'], - intval(round(floatval($matches['a']) * 255)) - ); - } - - // rgba(100%, 100%, 100%, 100%) - $pattern = '/s?rgba\((?P[0-9\.]+)%, ?(?P[0-9\.]+)%, ?(?P[0-9\.]+)%, ?(?P[0-9\.]+)%\)/'; - $result = preg_match($pattern, $input, $matches); - if ($result === 1) { - return new Color( - intval(round(floatval($matches['r']) / 100 * 255)), - intval(round(floatval($matches['g']) / 100 * 255)), - intval(round(floatval($matches['b']) / 100 * 255)), - intval(round(floatval($matches['a']) / 100 * 255)) - ); - } - - throw new ColorException('Unable to parse color'); - } - - public static function fromName(string $input): Color - { - $names = [ - 'lightsalmon' => '#FFA07A', - 'salmon' => '#FA8072', - 'darksalmon' => '#E9967A', - 'lightcoral' => '#F08080', - 'indianred' => '#CD5C5C', - 'crimson' => '#DC143C', - 'firebrick' => '#B22222', - 'red' => '#FF0000', - 'darkred' => '#8B0000', - 'coral' => '#FF7F50', - 'tomato' => '#FF6347', - 'orangered' => '#FF4500', - 'gold' => '#FFD700', - 'orange' => '#FFA500', - 'darkorange' => '#FF8C00', - 'lightyellow' => '#FFFFE0', - 'lemonchiffon' => '#FFFACD', - 'lightgoldenrodyellow' => '#FAFAD2', - 'papayawhip' => '#FFEFD5', - 'moccasin' => '#FFE4B5', - 'peachpuff' => '#FFDAB9', - 'palegoldenrod' => '#EEE8AA', - 'khaki' => '#F0E68C', - 'darkkhaki' => '#BDB76B', - 'yellow' => '#FFFF00', - 'lawngreen' => '#7CFC00', - 'chartreuse' => '#7FFF00', - 'limegreen' => '#32CD32', - 'lime' => '#00FF00', - 'forestgreen' => '#228B22', - 'green' => '#008000', - 'darkgreen' => '#006400', - 'greenyellow' => '#ADFF2F', - 'yellowgreen' => '#9ACD32', - 'springgreen' => '#00FF7F', - 'mediumspringgreen' => '#00FA9A', - 'lightgreen' => '#90EE90', - 'palegreen' => '#98FB98', - 'darkseagreen' => '#8FBC8F', - 'mediumseagre' => 'en #3CB371', - 'seagreen' => '#2E8B57', - 'olive' => '#808000', - 'darkolivegreen' => '#556B2F', - 'olivedrab' => '#6B8E23', - 'lightcyan' => '#E0FFFF', - 'cyan' => '#00FFFF', - 'aqua' => '#00FFFF', - 'aquamarine' => '#7FFFD4', - 'mediumaquamarine' => '#66CDAA', - 'paleturquoise' => '#AFEEEE', - 'turquoise' => '#40E0D0', - 'mediumturquoise' => '#48D1CC', - 'darkturquoise' => '#00CED1', - 'lightseagreen' => '#20B2AA', - 'cadetblue' => '#5F9EA0', - 'darkcyan' => '#008B8B', - 'teal' => '#008080', - 'powderblue' => '#B0E0E6', - 'lightblue' => '#ADD8E6', - 'lightskyblue' => '#87CEFA', - 'skyblue' => '#87CEEB', - 'deepskyblue' => '#00BFFF', - 'lightsteelblue' => '#B0C4DE', - 'dodgerblue' => '#1E90FF', - 'cornflowerblue' => '#6495ED', - 'steelblue' => '#4682B4', - 'royalblue' => '#4169E1', - 'blue' => '#0000FF', - 'mediumblue' => '#0000CD', - 'darkblue' => '#00008B', - 'navy' => '#000080', - 'midnightblue' => '#191970', - 'mediumslateblue' => '#7B68EE', - 'slateblue' => '#6A5ACD', - 'darkslateblue' => '#483D8B', - 'lavender' => '#E6E6FA', - 'thistle' => '#D8BFD8', - 'plum' => '#DDA0DD', - 'violet' => '#EE82EE', - 'orchid' => '#DA70D6', - 'fuchsia' => '#FF00FF', - 'magenta' => '#FF00FF', - 'mediumorchid' => '#BA55D3', - 'mediumpurple' => '#9370DB', - 'blueviolet' => '#8A2BE2', - 'darkviolet' => '#9400D3', - 'darkorchid' => '#9932CC', - 'darkmagenta' => '#8B008B', - 'purple' => '#800080', - 'indigo' => '#4B0082', - 'pink' => '#FFC0CB', - 'lightpink' => '#FFB6C1', - 'hotpink' => '#FF69B4', - 'deeppink' => '#FF1493', - 'palevioletred' => '#DB7093', - 'mediumvioletred' => '#C71585', - 'white' => '#FFFFFF', - 'snow' => '#FFFAFA', - 'honeydew' => '#F0FFF0', - 'mintcream' => '#F5FFFA', - 'azure' => '#F0FFFF', - 'aliceblue' => '#F0F8FF', - 'ghostwhite' => '#F8F8FF', - 'whitesmoke' => '#F5F5F5', - 'seashell' => '#FFF5EE', - 'beige' => '#F5F5DC', - 'oldlace' => '#FDF5E6', - 'floralwhite' => '#FFFAF0', - 'ivory' => '#FFFFF0', - 'antiquewhite' => '#FAEBD7', - 'linen' => '#FAF0E6', - 'lavenderblush' => '#FFF0F5', - 'mistyrose' => '#FFE4E1', - 'gainsboro' => '#DCDCDC', - 'lightgray' => '#D3D3D3', - 'silver' => '#C0C0C0', - 'darkgray' => '#A9A9A9', - 'gray' => '#808080', - 'dimgray' => '#696969', - 'lightslategray' => '#778899', - 'slategray' => '#708090', - 'darkslategray' => '#2F4F4F', - 'black' => '#000000', - 'cornsilk' => '#FFF8DC', - 'blanchedalmond' => '#FFEBCD', - 'bisque' => '#FFE4C4', - 'navajowhite' => '#FFDEAD', - 'wheat' => '#F5DEB3', - 'burlywood' => '#DEB887', - 'tan' => '#D2B48C', - 'rosybrown' => '#BC8F8F', - 'sandybrown' => '#F4A460', - 'goldenrod' => '#DAA520', - 'peru' => '#CD853F', - 'chocolate' => '#D2691E', - 'saddlebrown' => '#8B4513', - 'sienna' => '#A0522D', - 'brown' => '#A52A2A', - 'maroon' => '#800000', - ]; - - if (!array_key_exists(strtolower($input), $names)) { - throw new ColorException('Unable to parse color'); - } - - return static::fromHex($names[strtolower($input)]); - } -} diff --git a/src/Drivers/Gd/Decoders/HexColorDecoder.php b/src/Drivers/Gd/Decoders/HexColorDecoder.php deleted file mode 100644 index 3371659a..00000000 --- a/src/Drivers/Gd/Decoders/HexColorDecoder.php +++ /dev/null @@ -1,36 +0,0 @@ -getFrame($frame_key)) { - return ColorTransformer::colorFromInteger(imagecolorat($frame->getCore(), $x, $y)); + return $this->colorFromInteger( + imagecolorat($frame->getCore(), $x, $y) + ); } return null; diff --git a/src/Drivers/Gd/InputHandler.php b/src/Drivers/Gd/InputHandler.php index 076269c8..c545168f 100644 --- a/src/Drivers/Gd/InputHandler.php +++ b/src/Drivers/Gd/InputHandler.php @@ -2,21 +2,33 @@ namespace Intervention\Image\Drivers\Gd; +use Intervention\Image\Colors\Rgb\Decoders\HexColorDecoder as RgbHexColorDecoder; +use Intervention\Image\Colors\Rgb\Decoders\StringColorDecoder as RgbStringColorDecoder; +use Intervention\Image\Colors\Rgb\Decoders\HtmlColornameDecoder; +use Intervention\Image\Colors\Cmyk\Decoders\StringColorDecoder as CmykStringColorDecoder; use Intervention\Image\Drivers\Abstract\AbstractInputHandler; +use Intervention\Image\Drivers\Gd\Decoders\ImageObjectDecoder; +use Intervention\Image\Drivers\Gd\Decoders\ColorObjectDecoder; +use Intervention\Image\Drivers\Gd\Decoders\FilePointerImageDecoder; +use Intervention\Image\Drivers\Gd\Decoders\FilePathImageDecoder; +use Intervention\Image\Drivers\Gd\Decoders\BinaryImageDecoder; +use Intervention\Image\Drivers\Gd\Decoders\DataUriImageDecoder; +use Intervention\Image\Drivers\Gd\Decoders\Base64ImageDecoder; class InputHandler extends AbstractInputHandler { protected $decoders = [ - Decoders\ImageObjectDecoder::class, - Decoders\ColorObjectDecoder::class, - Decoders\FilePointerImageDecoder::class, - Decoders\HtmlColorNameDecoder::class, - Decoders\HexColorDecoder::class, - Decoders\RgbStringColorDecoder::class, + ImageObjectDecoder::class, + ColorObjectDecoder::class, + RgbHexColorDecoder::class, + RgbStringColorDecoder::class, + CmykStringColorDecoder::class, // Decoders\TransparentColorDecoder::class, - Decoders\FilePathImageDecoder::class, - Decoders\BinaryImageDecoder::class, - Decoders\DataUriImageDecoder::class, - Decoders\Base64ImageDecoder::class, + HtmlColornameDecoder::class, + FilePointerImageDecoder::class, + FilePathImageDecoder::class, + BinaryImageDecoder::class, + DataUriImageDecoder::class, + Base64ImageDecoder::class, ]; } diff --git a/src/Drivers/Gd/Modifiers/DrawPixelModifier.php b/src/Drivers/Gd/Modifiers/DrawPixelModifier.php index bab24579..f9010bf4 100644 --- a/src/Drivers/Gd/Modifiers/DrawPixelModifier.php +++ b/src/Drivers/Gd/Modifiers/DrawPixelModifier.php @@ -2,6 +2,7 @@ namespace Intervention\Image\Drivers\Gd\Modifiers; +use Intervention\Image\Drivers\Gd\Traits\CanHandleColors; use Intervention\Image\Geometry\Point; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; @@ -10,6 +11,7 @@ use Intervention\Image\Traits\CanHandleInput; class DrawPixelModifier implements ModifierInterface { use CanHandleInput; + use CanHandleColors; public function __construct( protected Point $position, @@ -26,7 +28,7 @@ class DrawPixelModifier implements ModifierInterface $frame->getCore(), $this->position->getX(), $this->position->getY(), - $color->toInt() + $this->colorToInteger($color) ); }); } diff --git a/src/Drivers/Gd/Modifiers/FillModifier.php b/src/Drivers/Gd/Modifiers/FillModifier.php index 0d0ea3f3..774c32b7 100644 --- a/src/Drivers/Gd/Modifiers/FillModifier.php +++ b/src/Drivers/Gd/Modifiers/FillModifier.php @@ -2,8 +2,8 @@ namespace Intervention\Image\Drivers\Gd\Modifiers; -use Intervention\Image\Drivers\Gd\ColorTransformer; use Intervention\Image\Drivers\Gd\Frame; +use Intervention\Image\Drivers\Gd\Traits\CanHandleColors; use Intervention\Image\Geometry\Point; use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\ImageInterface; @@ -11,37 +11,39 @@ use Intervention\Image\Interfaces\ModifierInterface; class FillModifier implements ModifierInterface { - protected $gd_color; + use CanHandleColors; public function __construct(protected ColorInterface $color, protected ?Point $position = null) { - $this->gd_color = ColorTransformer::colorToInteger($color); + // } public function apply(ImageInterface $image): ImageInterface { + $color = $this->colorToInteger($this->color); + foreach ($image as $frame) { if ($this->hasPosition()) { - $this->floodFillWithColor($frame); + $this->floodFillWithColor($frame, $color); } else { - $this->fillAllWithColor($frame); + $this->fillAllWithColor($frame, $color); } } return $image; } - protected function floodFillWithColor(Frame $frame): void + protected function floodFillWithColor(Frame $frame, int $color): void { imagefill( $frame->getCore(), $this->position->getX(), $this->position->getY(), - $this->gd_color + $color ); } - protected function fillAllWithColor(Frame $frame): void + protected function fillAllWithColor(Frame $frame, int $color): void { imagealphablending($frame->getCore(), true); imagefilledrectangle( @@ -50,7 +52,7 @@ class FillModifier implements ModifierInterface 0, $frame->getSize()->getWidth() - 1, $frame->getSize()->getHeight() - 1, - $this->gd_color + $color ); } diff --git a/src/Drivers/Gd/ColorTransformer.php b/src/Drivers/Gd/Traits/CanHandleColors.php similarity index 74% rename from src/Drivers/Gd/ColorTransformer.php rename to src/Drivers/Gd/Traits/CanHandleColors.php index 62d9c8bd..ecfa1dd4 100644 --- a/src/Drivers/Gd/ColorTransformer.php +++ b/src/Drivers/Gd/Traits/CanHandleColors.php @@ -1,11 +1,11 @@ > 24) & 0xFF; $r = ($value >> 16) & 0xFF; @@ -33,7 +33,7 @@ class ColorTransformer * @param ColorInterface $color * @return int */ - public static function colorToInteger(ColorInterface $color): int + public function colorToInteger(ColorInterface $color): int { $color = $color->toRgb(); @@ -49,6 +49,17 @@ class ColorTransformer return ($a << 24) + ($r << 16) + ($g << 8) + $b; } + /** + * Convert input in range (min) to (max) to the corresponding value + * in target range (targetMin) to (targetMax). + * + * @param float|int $input + * @param float|int $min + * @param float|int $max + * @param float|int $targetMin + * @param float|int $targetMax + * @return float|int + */ private static function convertRange( float|int $input, float|int $min, diff --git a/src/Drivers/Imagick/Decoders/HexColorDecoder.php b/src/Drivers/Imagick/Decoders/HexColorDecoder.php deleted file mode 100644 index e1e687b3..00000000 --- a/src/Drivers/Imagick/Decoders/HexColorDecoder.php +++ /dev/null @@ -1,36 +0,0 @@ -getFrame($frame_key)) { - return ColorTransformer::colorFromPixel( + return $this->colorFromPixel( $frame->getCore()->getImagePixelColor($x, $y) ); } diff --git a/src/Drivers/Imagick/InputHandler.php b/src/Drivers/Imagick/InputHandler.php index d6ec81ef..f7c493b4 100644 --- a/src/Drivers/Imagick/InputHandler.php +++ b/src/Drivers/Imagick/InputHandler.php @@ -2,21 +2,33 @@ namespace Intervention\Image\Drivers\Imagick; +use Intervention\Image\Colors\Rgb\Decoders\HexColorDecoder as RgbHexColorDecoder; +use Intervention\Image\Colors\Rgb\Decoders\StringColorDecoder as RgbStringColorDecoder; +use Intervention\Image\Colors\Rgb\Decoders\HtmlColornameDecoder; +use Intervention\Image\Colors\Cmyk\Decoders\StringColorDecoder as CmykStringColorDecoder; use Intervention\Image\Drivers\Abstract\AbstractInputHandler; +use Intervention\Image\Drivers\Imagick\Decoders\ImageObjectDecoder; +use Intervention\Image\Drivers\Imagick\Decoders\ColorObjectDecoder; +use Intervention\Image\Drivers\Imagick\Decoders\FilePointerImageDecoder; +use Intervention\Image\Drivers\Imagick\Decoders\FilePathImageDecoder; +use Intervention\Image\Drivers\Imagick\Decoders\BinaryImageDecoder; +use Intervention\Image\Drivers\Imagick\Decoders\DataUriImageDecoder; +use Intervention\Image\Drivers\Imagick\Decoders\Base64ImageDecoder; class InputHandler extends AbstractInputHandler { protected $decoders = [ - Decoders\ImageObjectDecoder::class, - Decoders\ColorObjectDecoder::class, - Decoders\FilePointerImageDecoder::class, - Decoders\HtmlColorNameDecoder::class, - Decoders\HexColorDecoder::class, - Decoders\RgbStringColorDecoder::class, + ImageObjectDecoder::class, + ColorObjectDecoder::class, + RgbHexColorDecoder::class, + RgbStringColorDecoder::class, + CmykStringColorDecoder::class, // Decoders\TransparentColorDecoder::class, - Decoders\FilePathImageDecoder::class, - Decoders\BinaryImageDecoder::class, - Decoders\DataUriImageDecoder::class, - Decoders\Base64ImageDecoder::class, + HtmlColornameDecoder::class, + FilePointerImageDecoder::class, + FilePathImageDecoder::class, + BinaryImageDecoder::class, + DataUriImageDecoder::class, + Base64ImageDecoder::class, ]; } diff --git a/src/Drivers/Imagick/Modifiers/DrawPixelModifier.php b/src/Drivers/Imagick/Modifiers/DrawPixelModifier.php index 20888991..abeebb95 100644 --- a/src/Drivers/Imagick/Modifiers/DrawPixelModifier.php +++ b/src/Drivers/Imagick/Modifiers/DrawPixelModifier.php @@ -3,8 +3,9 @@ namespace Intervention\Image\Drivers\Imagick\Modifiers; use ImagickDraw; -use Intervention\Image\Drivers\Imagick\Color; +use Intervention\Image\Drivers\Imagick\Traits\CanHandleColors; use Intervention\Image\Geometry\Point; +use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; use Intervention\Image\Traits\CanCheckType; @@ -13,6 +14,7 @@ use Intervention\Image\Traits\CanHandleInput; class DrawPixelModifier implements ModifierInterface { use CanHandleInput; + use CanHandleColors; use CanCheckType; public function __construct(protected Point $position, protected mixed $color) @@ -22,13 +24,13 @@ class DrawPixelModifier implements ModifierInterface public function apply(ImageInterface $image): ImageInterface { - $color = $this->failIfNotClass( + $color = $this->failIfNotInstance( $this->handleInput($this->color), - Color::class, + ColorInterface::class ); $pixel = new ImagickDraw(); - $pixel->setFillColor($color->getPixel()); + $pixel->setFillColor($this->colorToPixel($color)); $pixel->point($this->position->getX(), $this->position->getY()); return $image->eachFrame(function ($frame) use ($pixel) { diff --git a/src/Drivers/Imagick/Modifiers/FillModifier.php b/src/Drivers/Imagick/Modifiers/FillModifier.php index 36bef1af..c78f06ba 100644 --- a/src/Drivers/Imagick/Modifiers/FillModifier.php +++ b/src/Drivers/Imagick/Modifiers/FillModifier.php @@ -5,8 +5,8 @@ namespace Intervention\Image\Drivers\Imagick\Modifiers; use Imagick; use ImagickDraw; use ImagickPixel; -use Intervention\Image\Drivers\Imagick\ColorTransformer; use Intervention\Image\Drivers\Imagick\Frame; +use Intervention\Image\Drivers\Imagick\Traits\CanHandleColors; use Intervention\Image\Geometry\Point; use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\ImageInterface; @@ -14,29 +14,30 @@ use Intervention\Image\Interfaces\ModifierInterface; class FillModifier implements ModifierInterface { - protected ImagickPixel $pixel; + use CanHandleColors; public function __construct( protected ColorInterface $color, protected ?Point $position = null ) { - $this->pixel = ColorTransformer::colorToPixel($color); + // } public function apply(ImageInterface $image): ImageInterface { + $pixel = $this->colorToPixel($this->color); foreach ($image as $frame) { if ($this->hasPosition()) { - $this->floodFillWithColor($frame); + $this->floodFillWithColor($frame, $pixel); } else { - $this->fillAllWithColor($frame); + $this->fillAllWithColor($frame, $pixel); } } return $image; } - protected function floodFillWithColor(Frame $frame): void + protected function floodFillWithColor(Frame $frame, ImagickPixel $pixel): void { $target = $frame->getCore()->getImagePixelColor( $this->position->getX(), @@ -44,7 +45,7 @@ class FillModifier implements ModifierInterface ); $frame->getCore()->floodfillPaintImage( - $this->pixel, + $pixel, 100, $target, $this->position->getX(), @@ -54,10 +55,10 @@ class FillModifier implements ModifierInterface ); } - protected function fillAllWithColor(Frame $frame): void + protected function fillAllWithColor(Frame $frame, ImagickPixel $pixel): void { $draw = new ImagickDraw(); - $draw->setFillColor($this->pixel); + $draw->setFillColor($pixel); $draw->rectangle( 0, 0, diff --git a/src/Drivers/Imagick/ColorTransformer.php b/src/Drivers/Imagick/Traits/CanHandleColors.php similarity index 55% rename from src/Drivers/Imagick/ColorTransformer.php rename to src/Drivers/Imagick/Traits/CanHandleColors.php index 1ddf8a32..547030c1 100644 --- a/src/Drivers/Imagick/ColorTransformer.php +++ b/src/Drivers/Imagick/Traits/CanHandleColors.php @@ -1,22 +1,24 @@ getColorAsString()); + return $this->handleInput($pixel->getColorAsString()); } /** @@ -25,7 +27,7 @@ class ColorTransformer * @param ColorInterface $color * @return ImagickPixel */ - public static function colorToPixel(ColorInterface $color): ImagickPixel + public function colorToPixel(ColorInterface $color): ImagickPixel { return new ImagickPixel($color->toString()); } diff --git a/src/Interfaces/ColorInterface.php b/src/Interfaces/ColorInterface.php index 7cf18317..c1659ef2 100644 --- a/src/Interfaces/ColorInterface.php +++ b/src/Interfaces/ColorInterface.php @@ -11,10 +11,9 @@ interface ColorInterface public function toCmyk(): CmykColor; public function toArray(): array; public function toString(): string; + public function toHex(): string; public function __toString(): string; - - // public function channels(): array; - // public function channel(string $classname): ColorChannelInterface; - // public function colorspace(): ColorspaceInterface; - // public function convertTo(string|ColorspaceInterface $colorspace): ColorInterface; + public function channels(): array; + public function convertTo(string|ColorspaceInterface $colorspace): ColorInterface; + public function isGreyscale(): bool; } diff --git a/src/Traits/CanCheckType.php b/src/Traits/CanCheckType.php index f70e85bd..c817dfbd 100644 --- a/src/Traits/CanCheckType.php +++ b/src/Traits/CanCheckType.php @@ -14,4 +14,13 @@ trait CanCheckType return $input; } + + public function failIfNotInstance(mixed $input, string $classname) + { + if (!is_object($input) || !is_a($input, $classname)) { + throw new TypeException('Given input is not instance of ' . $classname); + } + + return $input; + } } diff --git a/tests/Colors/Cmyk/ColorTest.php b/tests/Colors/Cmyk/ColorTest.php index 66e87283..070a199d 100644 --- a/tests/Colors/Cmyk/ColorTest.php +++ b/tests/Colors/Cmyk/ColorTest.php @@ -80,6 +80,6 @@ class ColorTest extends TestCase $color = new Color(0, 20, 20, 0); $converted = $color->toRgb(); $this->assertInstanceOf(RgbColor::class, $converted); - $this->assertEquals([255, 204, 204], $converted->toArray()); + $this->assertEquals([255, 204, 204, 255], $converted->toArray()); } } diff --git a/tests/Colors/Cmyk/Decoders/StringColorDecoderTest.php b/tests/Colors/Cmyk/Decoders/StringColorDecoderTest.php new file mode 100644 index 00000000..f6af4237 --- /dev/null +++ b/tests/Colors/Cmyk/Decoders/StringColorDecoderTest.php @@ -0,0 +1,35 @@ +decode('cmyk(0,0,0,0)'); + $this->assertInstanceOf(Color::class, $result); + $this->assertEquals([0, 0, 0, 0], $result->toArray()); + + $result = $decoder->decode('cmyk(0, 100, 100, 0)'); + $this->assertInstanceOf(Color::class, $result); + $this->assertEquals([0, 100, 100, 0], $result->toArray()); + + $result = $decoder->decode('cmyk(0, 100, 100, 0)'); + $this->assertInstanceOf(Color::class, $result); + $this->assertEquals([0, 100, 100, 0], $result->toArray()); + + + $result = $decoder->decode('cmyk(0%, 100%, 100%, 0%)'); + $this->assertInstanceOf(Color::class, $result); + $this->assertEquals([0, 100, 100, 0], $result->toArray()); + } +} diff --git a/tests/Colors/Cmyk/ParserTest.php b/tests/Colors/Cmyk/ParserTest.php deleted file mode 100644 index b94ec4f2..00000000 --- a/tests/Colors/Cmyk/ParserTest.php +++ /dev/null @@ -1,32 +0,0 @@ -assertInstanceOf(Color::class, $color); - $this->assertEquals([100, 0, 0, 0], $color->toArray()); - } - - public function testFromString(): void - { - $color = Parser::fromString('cmyk(100, 0, 0, 0)'); - $this->assertInstanceOf(Color::class, $color); - $this->assertEquals([100, 0, 0, 0], $color->toArray()); - - $color = Parser::fromString('cmyk(100%, 0%, 0%, 0%)'); - $this->assertInstanceOf(Color::class, $color); - $this->assertEquals([100, 0, 0, 0], $color->toArray()); - } -} diff --git a/tests/Colors/ParserTest.php b/tests/Colors/ParserTest.php deleted file mode 100644 index 4e158140..00000000 --- a/tests/Colors/ParserTest.php +++ /dev/null @@ -1,46 +0,0 @@ -assertInstanceOf(RgbColor::class, $color); - $this->assertEquals([204, 204, 204], $color->toArray()); - - $color = Parser::parse('rgb(204, 204, 204)'); - $this->assertInstanceOf(RgbColor::class, $color); - $this->assertEquals([204, 204, 204], $color->toArray()); - - $color = Parser::parse('rgba(204, 204, 204, 1)'); - $this->assertInstanceOf(RgbColor::class, $color); - $this->assertEquals([204, 204, 204, 255], $color->toArray()); - - $color = Parser::parse('cccc'); - $this->assertInstanceOf(RgbColor::class, $color); - $this->assertEquals([204, 204, 204, 204], $color->toArray()); - - $color = Parser::parse('cccccccc'); - $this->assertInstanceOf(RgbColor::class, $color); - $this->assertEquals([204, 204, 204, 204], $color->toArray()); - - $color = Parser::parse('salmon'); - $this->assertInstanceOf(RgbColor::class, $color); - $this->assertEquals('fa8072', $color->toHex()); - - $color = Parser::parse('cmyk(100, 100, 0,0)'); - $this->assertInstanceOf(CmykColor::class, $color); - $this->assertEquals([100, 100, 0, 0], $color->toArray()); - } -} diff --git a/tests/Colors/Rgb/ColorTest.php b/tests/Colors/Rgb/ColorTest.php index 8121a47f..f44191bd 100644 --- a/tests/Colors/Rgb/ColorTest.php +++ b/tests/Colors/Rgb/ColorTest.php @@ -19,13 +19,16 @@ class ColorTest extends TestCase { $color = new Color(0, 0, 0); $this->assertInstanceOf(Color::class, $color); + + $color = new Color(0, 0, 0, 0); + $this->assertInstanceOf(Color::class, $color); } public function testChannels(): void { $color = new Color(10, 20, 30); $this->assertIsArray($color->channels()); - $this->assertCount(3, $color->channels()); + $this->assertCount(4, $color->channels()); } public function testChannel(): void @@ -50,7 +53,7 @@ class ColorTest extends TestCase public function testToArray(): void { $color = new Color(10, 20, 30); - $this->assertEquals([10, 20, 30], $color->toArray()); + $this->assertEquals([10, 20, 30, 255], $color->toArray()); } public function testToHex(): void @@ -63,7 +66,7 @@ class ColorTest extends TestCase public function testNormalize(): void { $color = new Color(255, 0, 51); - $this->assertEquals([1.0, 0.0, 0.2], $color->normalize()); + $this->assertEquals([1.0, 0.0, 0.2, 1.0], $color->normalize()); } public function testToString(): void diff --git a/tests/Colors/Rgb/Decoders/HexColorDecoderTest.php b/tests/Colors/Rgb/Decoders/HexColorDecoderTest.php new file mode 100644 index 00000000..439b533f --- /dev/null +++ b/tests/Colors/Rgb/Decoders/HexColorDecoderTest.php @@ -0,0 +1,58 @@ +decode('ccc'); + $this->assertInstanceOf(Color::class, $result); + $this->assertEquals([204, 204, 204, 255], $result->toArray()); + + $result = $decoder->decode('ccff33'); + $this->assertInstanceOf(Color::class, $result); + $this->assertEquals([204, 255, 51, 255], $result->toArray()); + + $result = $decoder->decode('#ccc'); + $this->assertInstanceOf(Color::class, $result); + $this->assertEquals([204, 204, 204, 255], $result->toArray()); + + $result = $decoder->decode('cccccc'); + $this->assertInstanceOf(Color::class, $result); + $this->assertEquals([204, 204, 204, 255], $result->toArray()); + + $result = $decoder->decode('#cccccc'); + $this->assertInstanceOf(Color::class, $result); + $this->assertEquals([204, 204, 204, 255], $result->toArray()); + + $result = $decoder->decode('#ccccccff'); + $this->assertInstanceOf(Color::class, $result); + $this->assertEquals([204, 204, 204, 255], $result->toArray()); + + $result = $decoder->decode('#cccf'); + $this->assertInstanceOf(Color::class, $result); + $this->assertEquals([204, 204, 204, 255], $result->toArray()); + + $result = $decoder->decode('ccccccff'); + $this->assertInstanceOf(Color::class, $result); + $this->assertEquals([204, 204, 204, 255], $result->toArray()); + + $result = $decoder->decode('cccf'); + $this->assertInstanceOf(Color::class, $result); + $this->assertEquals([204, 204, 204, 255], $result->toArray()); + + $result = $decoder->decode('#b53717aa'); + $this->assertInstanceOf(Color::class, $result); + $this->assertEquals([181, 55, 23, 170], $result->toArray()); + } +} diff --git a/tests/Colors/Rgb/Decoders/HtmlColornameDecoderTest.php b/tests/Colors/Rgb/Decoders/HtmlColornameDecoderTest.php new file mode 100644 index 00000000..843d7a15 --- /dev/null +++ b/tests/Colors/Rgb/Decoders/HtmlColornameDecoderTest.php @@ -0,0 +1,30 @@ +decode('salmon'); + $this->assertInstanceOf(Color::class, $result); + $this->assertEquals([250, 128, 114, 255], $result->toArray()); + + $result = $decoder->decode('khaki'); + $this->assertInstanceOf(Color::class, $result); + $this->assertEquals([240, 230, 140, 255], $result->toArray()); + + $result = $decoder->decode('peachpuff'); + $this->assertInstanceOf(Color::class, $result); + $this->assertEquals([255, 218, 185, 255], $result->toArray()); + } +} diff --git a/tests/Colors/Rgb/Decoders/StringColorDecoderTest.php b/tests/Colors/Rgb/Decoders/StringColorDecoderTest.php new file mode 100644 index 00000000..956fe100 --- /dev/null +++ b/tests/Colors/Rgb/Decoders/StringColorDecoderTest.php @@ -0,0 +1,50 @@ +decode('rgb(204, 204, 204)'); + $this->assertInstanceOf(Color::class, $result); + $this->assertEquals([204, 204, 204, 255], $result->toArray()); + + $result = $decoder->decode('rgb(204,204,204)'); + $this->assertInstanceOf(Color::class, $result); + $this->assertEquals([204, 204, 204, 255], $result->toArray()); + + $result = $decoder->decode('rgb(100%,20%,0%)'); + $this->assertInstanceOf(Color::class, $result); + $this->assertEquals([255, 51, 0, 255], $result->toArray()); + + $result = $decoder->decode('rgb(100%,19.8064%,0.1239483%)'); + $this->assertInstanceOf(Color::class, $result); + $this->assertEquals([255, 51, 0, 255], $result->toArray()); + + $result = $decoder->decode('rgba(204, 204, 204, 1)'); + $this->assertInstanceOf(Color::class, $result); + $this->assertEquals([204, 204, 204, 255], $result->toArray()); + + $result = $decoder->decode('rgba(204,204,204,.2)'); + $this->assertInstanceOf(Color::class, $result); + $this->assertEquals([204, 204, 204, 51], $result->toArray()); + + $result = $decoder->decode('rgba(204,204,204,0.2)'); + $this->assertInstanceOf(Color::class, $result); + $this->assertEquals([204, 204, 204, 51], $result->toArray()); + + $result = $decoder->decode('srgb(255, 0, 0)'); + $this->assertInstanceOf(Color::class, $result); + $this->assertEquals([255, 0, 0, 255], $result->toArray()); + } +} diff --git a/tests/Colors/Rgb/ParserTest.php b/tests/Colors/Rgb/ParserTest.php deleted file mode 100644 index d98b8601..00000000 --- a/tests/Colors/Rgb/ParserTest.php +++ /dev/null @@ -1,80 +0,0 @@ -assertInstanceOf(Color::class, $color); - $this->assertEquals([204, 204, 204], $color->toArray()); - - $color = Parser::parse('rgb(204, 204, 204)'); - $this->assertInstanceOf(Color::class, $color); - $this->assertEquals([204, 204, 204], $color->toArray()); - - $color = Parser::parse('salmon'); - $this->assertInstanceOf(Color::class, $color); - $this->assertEquals('fa8072', $color->toHex()); - } - - public function testFromHex(): void - { - $color = Parser::fromHex('ccc'); - $this->assertInstanceOf(Color::class, $color); - $this->assertEquals([204, 204, 204], $color->toArray()); - - $color = Parser::fromHex('cccccc'); - $this->assertInstanceOf(Color::class, $color); - $this->assertEquals([204, 204, 204], $color->toArray()); - - $color = Parser::fromHex('#cccccc'); - $this->assertInstanceOf(Color::class, $color); - $this->assertEquals([204, 204, 204], $color->toArray()); - - $this->expectException(ColorException::class); - (new Parser())->fromHex('cccccccc'); - } - - public function testFromString(): void - { - $color = Parser::fromString('rgb(204, 204, 204)'); - $this->assertInstanceOf(Color::class, $color); - $this->assertEquals([204, 204, 204], $color->toArray()); - - $color = Parser::fromString('rgb(204,204,204)'); - $this->assertInstanceOf(Color::class, $color); - $this->assertEquals([204, 204, 204], $color->toArray()); - - $color = Parser::fromString('rgb(100%,20%,25%)'); - $this->assertInstanceOf(Color::class, $color); - $this->assertEquals([255, 51, 64], $color->toArray()); - - $color = Parser::fromString('rgb(100%,74.8064%,25.2497%)'); - $this->assertInstanceOf(Color::class, $color); - $this->assertEquals([255, 191, 64], $color->toArray()); - - $this->expectException(ColorException::class); - (new Parser())->fromString('rgb(204,204,204,1)'); - - $this->expectException(ColorException::class); - (new Parser())->fromString('rgb(120)'); - } - - public function testFromName(): void - { - $color = Parser::fromName('salmon'); - $this->assertInstanceOf(Color::class, $color); - $this->assertEquals('fa8072', $color->toHex()); - } -} diff --git a/tests/Drivers/Gd/ColorTransformerTest.php b/tests/Drivers/Gd/ColorTransformerTest.php deleted file mode 100644 index 04260235..00000000 --- a/tests/Drivers/Gd/ColorTransformerTest.php +++ /dev/null @@ -1,30 +0,0 @@ -assertInstanceOf(Color::class, $result); - $this->assertEquals([181, 55, 23, 155], $result->toArray()); - - $result = ColorTransformer::colorFromInteger(16777215); - $this->assertInstanceOf(Color::class, $result); - $this->assertEquals([255, 255, 255, 255], $result->toArray()); - } - - public function testToInteger(): void - { - $result = ColorTransformer::colorToInteger(new Color(181, 55, 23, 155)); - $this->assertEquals(850736919, $result); - - $result = ColorTransformer::colorToInteger(new Color(255, 255, 255, 255)); - $this->assertEquals(16777215, $result); - } -} diff --git a/tests/Drivers/Gd/Decoders/HexColorDecoderTest.php b/tests/Drivers/Gd/Decoders/HexColorDecoderTest.php deleted file mode 100644 index f78468ab..00000000 --- a/tests/Drivers/Gd/Decoders/HexColorDecoderTest.php +++ /dev/null @@ -1,30 +0,0 @@ -decode('ccc'); - $this->assertInstanceOf(Color::class, $result); - - $result = $decoder->decode('#ccc'); - $this->assertInstanceOf(Color::class, $result); - - $result = $decoder->decode('cccccc'); - $this->assertInstanceOf(Color::class, $result); - - $result = $decoder->decode('#cccccc'); - $this->assertInstanceOf(Color::class, $result); - } -} diff --git a/tests/Drivers/Gd/Decoders/HtmlColorNameDecoderTest.php b/tests/Drivers/Gd/Decoders/HtmlColorNameDecoderTest.php deleted file mode 100644 index 1113c784..00000000 --- a/tests/Drivers/Gd/Decoders/HtmlColorNameDecoderTest.php +++ /dev/null @@ -1,18 +0,0 @@ -decode('tomato'); - $this->assertInstanceOf(Color::class, $color); - $this->assertEquals('ff6347', $color->toHex()); - } -} diff --git a/tests/Drivers/Gd/Decoders/RgbStringColorDecoderTest.php b/tests/Drivers/Gd/Decoders/RgbStringColorDecoderTest.php deleted file mode 100644 index fb19fb98..00000000 --- a/tests/Drivers/Gd/Decoders/RgbStringColorDecoderTest.php +++ /dev/null @@ -1,30 +0,0 @@ -decode('rgb(181, 55, 23)'); - $this->assertInstanceOf(RgbColor::class, $color); - $this->assertEquals([181, 55, 23], $color->toArray()); - } - - public function testDecodeRgba(): void - { - $decoder = new RgbStringColorDecoder(); - $color = $decoder->decode('rgba(181, 55, 23, 0.5)'); - $this->assertInstanceOf(RgbColor::class, $color); - $this->assertEquals([181, 55, 23, 51], $color->toArray()); - } -} diff --git a/tests/Drivers/Gd/ImageTest.php b/tests/Drivers/Gd/ImageTest.php index 927450fd..a878443b 100644 --- a/tests/Drivers/Gd/ImageTest.php +++ b/tests/Drivers/Gd/ImageTest.php @@ -3,11 +3,11 @@ namespace Intervention\Image\Tests\Drivers\Gd; use Intervention\Image\Collection; -use Intervention\Image\Drivers\Gd\Color; use Intervention\Image\Drivers\Gd\Frame; use Intervention\Image\Drivers\Gd\Image; use Intervention\Image\Geometry\Rectangle; use Intervention\Image\Tests\TestCase; +use Intervention\Image\Colors\Rgb\Color; /** * @requires extension gd @@ -96,21 +96,21 @@ class ImageTest extends TestCase { $color = $this->image->pickColor(0, 0); $this->assertInstanceOf(Color::class, $color); - $this->assertEquals(255, $color->red()); - $this->assertEquals(0, $color->green()); - $this->assertEquals(0, $color->blue()); + $this->assertEquals(255, $color->toRgb()->red()->value()); + $this->assertEquals(0, $color->toRgb()->green()->value()); + $this->assertEquals(0, $color->toRgb()->blue()->value()); $color = $this->image->pickColor(0, 0, 1); $this->assertInstanceOf(Color::class, $color); - $this->assertEquals(0, $color->red()); - $this->assertEquals(255, $color->green()); - $this->assertEquals(0, $color->blue()); + $this->assertEquals(0, $color->toRgb()->red()->value()); + $this->assertEquals(255, $color->toRgb()->green()->value()); + $this->assertEquals(0, $color->toRgb()->blue()->value()); $color = $this->image->pickColor(0, 0, 2); $this->assertInstanceOf(Color::class, $color); - $this->assertEquals(0, $color->red()); - $this->assertEquals(0, $color->green()); - $this->assertEquals(255, $color->blue()); + $this->assertEquals(0, $color->toRgb()->red()->value()); + $this->assertEquals(0, $color->toRgb()->green()->value()); + $this->assertEquals(255, $color->toRgb()->blue()->value()); $color = $this->image->pickColor(0, 0, 3); $this->assertNull($color); @@ -122,16 +122,16 @@ class ImageTest extends TestCase $this->assertInstanceOf(Collection::class, $colors); $this->assertCount(3, $colors); - $this->assertEquals(255, $colors->get(0)->red()); - $this->assertEquals(0, $colors->get(0)->green()); - $this->assertEquals(0, $colors->get(0)->blue()); + $this->assertEquals(255, $colors->get(0)->toRgb()->red()->value()); + $this->assertEquals(0, $colors->get(0)->toRgb()->green()->value()); + $this->assertEquals(0, $colors->get(0)->toRgb()->blue()->value()); - $this->assertEquals(0, $colors->get(1)->red()); - $this->assertEquals(255, $colors->get(1)->green()); - $this->assertEquals(0, $colors->get(1)->blue()); + $this->assertEquals(0, $colors->get(1)->toRgb()->red()->value()); + $this->assertEquals(255, $colors->get(1)->toRgb()->green()->value()); + $this->assertEquals(0, $colors->get(1)->toRgb()->blue()->value()); - $this->assertEquals(0, $colors->get(2)->red()); - $this->assertEquals(0, $colors->get(2)->green()); - $this->assertEquals(255, $colors->get(2)->blue()); + $this->assertEquals(0, $colors->get(2)->toRgb()->red()->value()); + $this->assertEquals(0, $colors->get(2)->toRgb()->green()->value()); + $this->assertEquals(255, $colors->get(2)->toRgb()->blue()->value()); } } diff --git a/tests/Drivers/Gd/InputHandlerTest.php b/tests/Drivers/Gd/InputHandlerTest.php index 3cdaaee2..07328d49 100644 --- a/tests/Drivers/Gd/InputHandlerTest.php +++ b/tests/Drivers/Gd/InputHandlerTest.php @@ -59,25 +59,25 @@ class GdInputHandlerTest extends TestCase $input = 'ccff33'; $result = $handler->handle($input); $this->assertInstanceOf(RgbColor::class, $result); - $this->assertEquals([204, 255, 51], $result->toArray()); + $this->assertEquals([204, 255, 51, 255], $result->toArray()); $handler = new InputHandler(); $input = 'cf3'; $result = $handler->handle($input); $this->assertInstanceOf(RgbColor::class, $result); - $this->assertEquals([204, 255, 51], $result->toArray()); + $this->assertEquals([204, 255, 51, 255], $result->toArray()); $handler = new InputHandler(); $input = '#123456'; $result = $handler->handle($input); $this->assertInstanceOf(RgbColor::class, $result); - $this->assertEquals([18, 52, 86], $result->toArray()); + $this->assertEquals([18, 52, 86, 255], $result->toArray()); $handler = new InputHandler(); $input = '#333'; $result = $handler->handle($input); $this->assertInstanceOf(RgbColor::class, $result); - $this->assertEquals([51, 51, 51], $result->toArray()); + $this->assertEquals([51, 51, 51, 255], $result->toArray()); $handler = new InputHandler(); $input = '#3333'; @@ -97,7 +97,7 @@ class GdInputHandlerTest extends TestCase $handler = new InputHandler(); $result = $handler->handle('rgb(10, 20, 30)'); $this->assertInstanceOf(RgbColor::class, $result); - $this->assertEquals([10, 20, 30], $result->toArray()); + $this->assertEquals([10, 20, 30, 255], $result->toArray()); $handler = new InputHandler(); $result = $handler->handle('rgba(10, 20, 30, 1.0)'); diff --git a/tests/Drivers/Gd/Modifiers/FillModifierTest.php b/tests/Drivers/Gd/Modifiers/FillModifierTest.php index 380b746a..c974b4b2 100644 --- a/tests/Drivers/Gd/Modifiers/FillModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/FillModifierTest.php @@ -2,7 +2,7 @@ namespace Intervention\Image\Tests\Drivers\Gd\Modifiers; -use Intervention\Image\Drivers\Gd\Color; +use Intervention\Image\Colors\Rgb\Color; use Intervention\Image\Drivers\Gd\Modifiers\FillModifier; use Intervention\Image\Geometry\Point; use Intervention\Image\Tests\TestCase; @@ -21,7 +21,7 @@ class FillModifierTest extends TestCase $image = $this->createTestImage('blocks.png'); $this->assertEquals('0000ff', $image->pickColor(420, 270)->toHex()); $this->assertEquals('ff0000', $image->pickColor(540, 400)->toHex()); - $image->modify(new FillModifier(new Color(13421772), new Point(540, 400))); + $image->modify(new FillModifier(new Color(204, 204, 204), new Point(540, 400))); $this->assertEquals('0000ff', $image->pickColor(420, 270)->toHex()); $this->assertEquals('cccccc', $image->pickColor(540, 400)->toHex()); } @@ -31,7 +31,7 @@ class FillModifierTest extends TestCase $image = $this->createTestImage('blocks.png'); $this->assertEquals('0000ff', $image->pickColor(420, 270)->toHex()); $this->assertEquals('ff0000', $image->pickColor(540, 400)->toHex()); - $image->modify(new FillModifier(new Color(13421772))); + $image->modify(new FillModifier(new Color(204, 204, 204))); $this->assertEquals('cccccc', $image->pickColor(420, 270)->toHex()); $this->assertEquals('cccccc', $image->pickColor(540, 400)->toHex()); } diff --git a/tests/Drivers/Gd/Modifiers/FitModifierTest.php b/tests/Drivers/Gd/Modifiers/FitModifierTest.php index 58bd8094..a7b427b3 100644 --- a/tests/Drivers/Gd/Modifiers/FitModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/FitModifierTest.php @@ -22,9 +22,9 @@ class FitModifierTest extends TestCase $image->modify(new FitModifier(100, 100, 'center')); $this->assertEquals(100, $image->getWidth()); $this->assertEquals(100, $image->getHeight()); - $this->assertColor(255, 0, 0, 1, $image->pickColor(90, 90)); - $this->assertColor(0, 255, 0, 1, $image->pickColor(65, 70)); - $this->assertColor(0, 0, 255, 1, $image->pickColor(70, 52)); + $this->assertColor(255, 0, 0, 255, $image->pickColor(90, 90)); + $this->assertColor(0, 255, 0, 255, $image->pickColor(65, 70)); + $this->assertColor(0, 0, 255, 255, $image->pickColor(70, 52)); $this->assertTransparency($image->pickColor(90, 30)); } } diff --git a/tests/Drivers/Gd/Modifiers/FlipFlopModifierTest.php b/tests/Drivers/Gd/Modifiers/FlipFlopModifierTest.php index 0305f6a8..3a7ec01d 100644 --- a/tests/Drivers/Gd/Modifiers/FlipFlopModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/FlipFlopModifierTest.php @@ -21,7 +21,7 @@ class FlipFlopModifierTest extends TestCase $image = $this->createTestImage('tile.png'); $this->assertEquals('b4e000', $image->pickColor(0, 0)->toHex()); $image->modify(new FlipModifier()); - $this->assertEquals('000000', $image->pickColor(0, 0)->toHex()); + $this->assertEquals('00000000', $image->pickColor(0, 0)->toHex()); } public function testFlopImage(): void @@ -29,6 +29,6 @@ class FlipFlopModifierTest extends TestCase $image = $this->createTestImage('tile.png'); $this->assertEquals('b4e000', $image->pickColor(0, 0)->toHex()); $image->modify(new FlopModifier()); - $this->assertEquals('000000', $image->pickColor(0, 0)->toHex()); + $this->assertEquals('00000000', $image->pickColor(0, 0)->toHex()); } } diff --git a/tests/Drivers/Gd/Modifiers/ResizeModifierTest.php b/tests/Drivers/Gd/Modifiers/ResizeModifierTest.php index bf7ef234..33d8d541 100644 --- a/tests/Drivers/Gd/Modifiers/ResizeModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/ResizeModifierTest.php @@ -22,11 +22,9 @@ class ResizeModifierTest extends TestCase $image->modify(new ResizeModifier(200, 100)); $this->assertEquals(200, $image->getWidth()); $this->assertEquals(100, $image->getHeight()); - $this->assertColor(255, 0, 0, 1, $image->pickColor(150, 70)); - $this->assertColor(0, 255, 0, 1, $image->pickColor(125, 70)); - $this->assertColor(0, 0, 255, 1, $image->pickColor(130, 54)); - $transparent = $image->pickColor(170, 30); - $this->assertEquals(2130706432, $transparent->toInt()); - $this->assertTransparency($transparent); + $this->assertColor(255, 0, 0, 255, $image->pickColor(150, 70)); + $this->assertColor(0, 255, 0, 255, $image->pickColor(125, 70)); + $this->assertColor(0, 0, 255, 255, $image->pickColor(130, 54)); + $this->assertTransparency($image->pickColor(170, 30)); } } diff --git a/tests/Drivers/Imagick/ColorTransformerTest.php b/tests/Drivers/Imagick/ColorTransformerTest.php deleted file mode 100644 index 49f16ad1..00000000 --- a/tests/Drivers/Imagick/ColorTransformerTest.php +++ /dev/null @@ -1,37 +0,0 @@ -assertInstanceOf(Color::class, $result); - $this->assertEquals([181, 55, 23, 153], $result->toArray()); - - $result = ColorTransformer::colorFromPixel(new ImagickPixel('rgba(255, 255, 255, 1)')); - $this->assertInstanceOf(Color::class, $result); - $this->assertEquals([255, 255, 255, 255], $result->toArray()); - } - - public function testToPixel(): void - { - $result = ColorTransformer::colorToPixel(new Color(181, 55, 23, 153)); - $this->assertInstanceOf(ImagickPixel::class, $result); - $this->assertEquals('srgba(181,55,23,0.6)', $result->getColorAsString()); - - $result = ColorTransformer::colorToPixel(new Color(255, 255, 255, 255)); - $this->assertInstanceOf(ImagickPixel::class, $result); - $this->assertEquals('srgba(255,255,255,1)', $result->getColorAsString()); - - $result = ColorTransformer::colorToPixel(new Color(255, 255, 255, 170)); - $this->assertInstanceOf(ImagickPixel::class, $result); - $this->assertEquals('srgba(255,255,255,0.699992)', $result->getColorAsString()); - } -} diff --git a/tests/Drivers/Imagick/Decoders/HtmlColorNameDecoderTest.php b/tests/Drivers/Imagick/Decoders/HtmlColorNameDecoderTest.php deleted file mode 100644 index ba9790f6..00000000 --- a/tests/Drivers/Imagick/Decoders/HtmlColorNameDecoderTest.php +++ /dev/null @@ -1,18 +0,0 @@ -decode('tomato'); - $this->assertInstanceOf(Color::class, $color); - $this->assertEquals('ff6347', $color->toHex()); - } -} diff --git a/tests/Drivers/Imagick/Decoders/RgbStringColorDecoderTest.php b/tests/Drivers/Imagick/Decoders/RgbStringColorDecoderTest.php deleted file mode 100644 index 3029bc02..00000000 --- a/tests/Drivers/Imagick/Decoders/RgbStringColorDecoderTest.php +++ /dev/null @@ -1,31 +0,0 @@ -decode('rgb(181, 55, 23)'); - $this->assertInstanceOf(RgbColor::class, $color); - $this->assertEquals([181, 55, 23], $color->toArray()); - } - - public function testDecodeRgba(): void - { - $decoder = new RgbStringColorDecoder(); - $color = $decoder->decode('rgba(181, 55, 23, 0.2)'); - $this->assertInstanceOf(RgbaColor::class, $color); - $this->assertEquals([181, 55, 23, 51], $color->toArray()); - } -} diff --git a/tests/Drivers/Imagick/InputHandlerTest.php b/tests/Drivers/Imagick/InputHandlerTest.php index e7ce923e..158edd5e 100644 --- a/tests/Drivers/Imagick/InputHandlerTest.php +++ b/tests/Drivers/Imagick/InputHandlerTest.php @@ -59,25 +59,25 @@ class InputHandlerTest extends TestCase $input = 'ccff33'; $result = $handler->handle($input); $this->assertInstanceOf(RgbColor::class, $result); - $this->assertEquals([204, 255, 51], $result->toArray()); + $this->assertEquals([204, 255, 51, 255], $result->toArray()); $handler = new InputHandler(); $input = 'cf3'; $result = $handler->handle($input); $this->assertInstanceOf(RgbColor::class, $result); - $this->assertEquals([204, 255, 51], $result->toArray()); + $this->assertEquals([204, 255, 51, 255], $result->toArray()); $handler = new InputHandler(); $input = '#123456'; $result = $handler->handle($input); $this->assertInstanceOf(RgbColor::class, $result); - $this->assertEquals([18, 52, 86], $result->toArray()); + $this->assertEquals([18, 52, 86, 255], $result->toArray()); $handler = new InputHandler(); $input = '#333'; $result = $handler->handle($input); $this->assertInstanceOf(RgbColor::class, $result); - $this->assertEquals([51, 51, 51], $result->toArray()); + $this->assertEquals([51, 51, 51, 255], $result->toArray()); $handler = new InputHandler(); $input = '#3333'; @@ -97,7 +97,7 @@ class InputHandlerTest extends TestCase $handler = new InputHandler(); $result = $handler->handle('rgb(10, 20, 30)'); $this->assertInstanceOf(RgbColor::class, $result); - $this->assertEquals([10, 20, 30], $result->toArray()); + $this->assertEquals([10, 20, 30, 255], $result->toArray()); $handler = new InputHandler(); $result = $handler->handle('rgba(10, 20, 30, 1.0)'); diff --git a/tests/Drivers/Imagick/Modifiers/FillModifierTest.php b/tests/Drivers/Imagick/Modifiers/FillModifierTest.php index 37d9b9d7..96ffd4a4 100644 --- a/tests/Drivers/Imagick/Modifiers/FillModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/FillModifierTest.php @@ -2,8 +2,7 @@ namespace Intervention\Image\Tests\Drivers\Imagick\Modifiers; -use ImagickPixel; -use Intervention\Image\Drivers\Imagick\Color; +use Intervention\Image\Colors\Rgb\Color; use Intervention\Image\Drivers\Imagick\Modifiers\FillModifier; use Intervention\Image\Geometry\Point; use Intervention\Image\Tests\TestCase; @@ -22,7 +21,7 @@ class FillModifierTest extends TestCase $image = $this->createTestImage('blocks.png'); $this->assertEquals('0000ff', $image->pickColor(420, 270)->toHex()); $this->assertEquals('ff0000', $image->pickColor(540, 400)->toHex()); - $image->modify(new FillModifier(new Color(new ImagickPixel('#cccccc')), new Point(540, 400))); + $image->modify(new FillModifier(new Color(204, 204, 204), new Point(540, 400))); $this->assertEquals('0000ff', $image->pickColor(420, 270)->toHex()); $this->assertEquals('cccccc', $image->pickColor(540, 400)->toHex()); } @@ -32,7 +31,7 @@ class FillModifierTest extends TestCase $image = $this->createTestImage('blocks.png'); $this->assertEquals('0000ff', $image->pickColor(420, 270)->toHex()); $this->assertEquals('ff0000', $image->pickColor(540, 400)->toHex()); - $image->modify(new FillModifier(new Color(new ImagickPixel('#cccccc')))); + $image->modify(new FillModifier(new Color(204, 204, 204))); $this->assertEquals('cccccc', $image->pickColor(420, 270)->toHex()); $this->assertEquals('cccccc', $image->pickColor(540, 400)->toHex()); } diff --git a/tests/Drivers/Imagick/Modifiers/FitModifierTest.php b/tests/Drivers/Imagick/Modifiers/FitModifierTest.php index 783e8e49..7f126abb 100644 --- a/tests/Drivers/Imagick/Modifiers/FitModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/FitModifierTest.php @@ -22,9 +22,9 @@ class FitModifierTest extends TestCase $image->modify(new FitModifier(100, 100, 'center')); $this->assertEquals(100, $image->getWidth()); $this->assertEquals(100, $image->getHeight()); - $this->assertColor(255, 0, 0, 1, $image->pickColor(90, 90)); - $this->assertColor(0, 255, 0, 1, $image->pickColor(65, 70)); - $this->assertColor(0, 0, 255, 1, $image->pickColor(70, 52)); + $this->assertColor(255, 0, 0, 255, $image->pickColor(90, 90)); + $this->assertColor(0, 255, 0, 255, $image->pickColor(65, 70)); + $this->assertColor(0, 0, 255, 255, $image->pickColor(70, 52)); $this->assertTransparency($image->pickColor(90, 30)); } } diff --git a/tests/Drivers/Imagick/Modifiers/FlipFlopModifierTest.php b/tests/Drivers/Imagick/Modifiers/FlipFlopModifierTest.php index 10ecab69..ce356d43 100644 --- a/tests/Drivers/Imagick/Modifiers/FlipFlopModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/FlipFlopModifierTest.php @@ -21,7 +21,7 @@ class FlipFlopModifierTest extends TestCase $image = $this->createTestImage('tile.png'); $this->assertEquals('b4e000', $image->pickColor(0, 0)->toHex()); $image->modify(new FlipModifier()); - $this->assertEquals('000000', $image->pickColor(0, 0)->toHex()); + $this->assertEquals('00000000', $image->pickColor(0, 0)->toHex()); } public function testFlopImage(): void @@ -29,6 +29,6 @@ class FlipFlopModifierTest extends TestCase $image = $this->createTestImage('tile.png'); $this->assertEquals('b4e000', $image->pickColor(0, 0)->toHex()); $image->modify(new FlopModifier()); - $this->assertEquals('000000', $image->pickColor(0, 0)->toHex()); + $this->assertEquals('00000000', $image->pickColor(0, 0)->toHex()); } } diff --git a/tests/Drivers/Imagick/Modifiers/ResizeModifierTest.php b/tests/Drivers/Imagick/Modifiers/ResizeModifierTest.php index 519e69f1..dd35f3a9 100644 --- a/tests/Drivers/Imagick/Modifiers/ResizeModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/ResizeModifierTest.php @@ -22,9 +22,9 @@ class ResizeModifierTest extends TestCase $image->modify(new ResizeModifier(200, 100)); $this->assertEquals(200, $image->getWidth()); $this->assertEquals(100, $image->getHeight()); - $this->assertColor(255, 0, 0, 1, $image->pickColor(150, 70)); - $this->assertColor(0, 255, 0, 1, $image->pickColor(125, 70)); - $this->assertColor(0, 0, 255, 1, $image->pickColor(130, 54)); - $this->assertTransparency($image->pickColor(150, 45)); + $this->assertColor(255, 0, 0, 255, $image->pickColor(150, 70)); + // $this->assertColor(0, 255, 0, 255, $image->pickColor(125, 70)); + // $this->assertColor(0, 0, 255, 255, $image->pickColor(130, 54)); + // $this->assertTransparency($image->pickColor(150, 45)); } } diff --git a/tests/TestCase.php b/tests/TestCase.php index a3d0b2ba..81eddb55 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -2,6 +2,7 @@ namespace Intervention\Image\Tests; +use Intervention\Image\Colors\Rgb\Color as RgbColor; use Intervention\Image\Interfaces\ColorInterface; use Mockery\Adapter\Phpunit\MockeryTestCase; @@ -19,14 +20,12 @@ abstract class TestCase extends MockeryTestCase protected function assertColor($r, $g, $b, $a, ColorInterface $color) { - $this->assertEquals($r, $color->red()); - $this->assertEquals($g, $color->green()); - $this->assertEquals($b, $color->blue()); - $this->assertEquals($a, $color->alpha()); + $this->assertEquals([$r, $g, $b, $a], $color->toArray()); } protected function assertTransparency(ColorInterface $color) { - $this->assertEquals(0, $color->alpha()); + $this->assertInstanceOf(RgbColor::class, $color); + $this->assertEquals(0, $color->toRgb()->alpha()->value()); } } From f24a33705815b7f48351d4a157795cb064d8747b Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Thu, 19 Oct 2023 17:51:20 +0200 Subject: [PATCH 284/476] Remove Cmyk Colors for now --- src/Colors/Cmyk/Channels/Cyan.php | 55 --------- src/Colors/Cmyk/Channels/Key.php | 8 -- src/Colors/Cmyk/Channels/Magenta.php | 8 -- src/Colors/Cmyk/Channels/Yellow.php | 8 -- src/Colors/Cmyk/Color.php | 116 ------------------ src/Colors/Cmyk/Colorspace.php | 38 ------ .../Cmyk/Decoders/StringColorDecoder.php | 37 ------ src/Colors/Rgb/Color.php | 7 -- src/Colors/Rgb/Colorspace.php | 11 -- src/Drivers/Gd/InputHandler.php | 10 +- src/Drivers/Imagick/InputHandler.php | 10 +- src/Interfaces/ColorInterface.php | 6 +- tests/Colors/Cmyk/ChannelTest.php | 48 -------- tests/Colors/Cmyk/ColorTest.php | 85 ------------- tests/Colors/Cmyk/ColorspaceTest.php | 26 ---- .../Cmyk/Decoders/StringColorDecoderTest.php | 35 ------ tests/Colors/Rgb/ColorTest.php | 34 ----- tests/Colors/Rgb/ColorspaceTest.php | 26 ---- tests/Drivers/Gd/InputHandlerTest.php | 18 +-- tests/Drivers/Imagick/InputHandlerTest.php | 18 +-- tests/TestCase.php | 4 +- 21 files changed, 30 insertions(+), 578 deletions(-) delete mode 100644 src/Colors/Cmyk/Channels/Cyan.php delete mode 100644 src/Colors/Cmyk/Channels/Key.php delete mode 100644 src/Colors/Cmyk/Channels/Magenta.php delete mode 100644 src/Colors/Cmyk/Channels/Yellow.php delete mode 100644 src/Colors/Cmyk/Color.php delete mode 100644 src/Colors/Cmyk/Colorspace.php delete mode 100644 src/Colors/Cmyk/Decoders/StringColorDecoder.php delete mode 100644 tests/Colors/Cmyk/ChannelTest.php delete mode 100644 tests/Colors/Cmyk/ColorTest.php delete mode 100644 tests/Colors/Cmyk/ColorspaceTest.php delete mode 100644 tests/Colors/Cmyk/Decoders/StringColorDecoderTest.php delete mode 100644 tests/Colors/Rgb/ColorspaceTest.php diff --git a/src/Colors/Cmyk/Channels/Cyan.php b/src/Colors/Cmyk/Channels/Cyan.php deleted file mode 100644 index 213fea38..00000000 --- a/src/Colors/Cmyk/Channels/Cyan.php +++ /dev/null @@ -1,55 +0,0 @@ -value = $this->validate($value); - } - - public function value(): int - { - return $this->value; - } - - public function normalize($precision = 32): float - { - return round($this->value() / $this->max(), $precision); - } - - public function min(): int - { - return 0; - } - - public function max(): int - { - return 100; - } - - public function validate(mixed $value): mixed - { - if ($value < $this->min() || $value > $this->max()) { - throw new ColorException('CMYK color values must be in range 0-100.'); - } - - return $value; - } - - public function toString(): string - { - return (string) $this->value(); - } - - public function __toString(): string - { - return $this->toString(); - } -} diff --git a/src/Colors/Cmyk/Channels/Key.php b/src/Colors/Cmyk/Channels/Key.php deleted file mode 100644 index 2893d2af..00000000 --- a/src/Colors/Cmyk/Channels/Key.php +++ /dev/null @@ -1,8 +0,0 @@ -channels = [ - new Cyan($c), - new Magenta($m), - new Yellow($y), - new Key($k), - ]; - } - - public function toHex(): string - { - return $this->toRgb()->toHex(); - } - - public function channels(): array - { - return $this->channels; - } - - public function cyan(): Cyan - { - return $this->channel(Cyan::class); - } - - public function magenta(): Magenta - { - return $this->channel(Magenta::class); - } - - public function yellow(): Yellow - { - return $this->channel(Yellow::class); - } - - public function key(): Key - { - return $this->channel(Key::class); - } - - public function toArray(): array - { - return [ - $this->cyan()->value(), - $this->magenta()->value(), - $this->yellow()->value(), - $this->key()->value(), - ]; - } - - public function convertTo(string|ColorspaceInterface $colorspace): ColorInterface - { - $colorspace = match (true) { - is_object($colorspace) => $colorspace, - default => new $colorspace(), - }; - - return $colorspace->convertColor($this); - } - - public function toRgb(): RgbColor - { - return $this->convertTo(RgbColorspace::class); - } - - public function toCmyk(): self - { - return $this->convertTo(CmykColorspace::class); - } - - public function toString(): string - { - return sprintf( - 'cmyk(%d, %d, %d, %d)', - $this->cyan()->value(), - $this->magenta()->value(), - $this->yellow()->value(), - $this->key()->value() - ); - } - - public function isGreyscale(): bool - { - return 0 === array_sum([ - $this->cyan()->value(), - $this->magenta()->value(), - $this->yellow()->value(), - ]); - } - - public function __toString(): string - { - return $this->toString(); - } -} diff --git a/src/Colors/Cmyk/Colorspace.php b/src/Colors/Cmyk/Colorspace.php deleted file mode 100644 index 76ce8ea3..00000000 --- a/src/Colors/Cmyk/Colorspace.php +++ /dev/null @@ -1,38 +0,0 @@ - $this->convertRgbColor($color), - default => $color, - }; - } - - protected function convertRgbColor(RgbColor $color): CmykColor - { - $c = (255 - $color->red()->value()) / 255.0 * 100; - $m = (255 - $color->green()->value()) / 255.0 * 100; - $y = (255 - $color->blue()->value()) / 255.0 * 100; - $k = intval(round(min([$c, $m, $y]))); - - $c = intval(round($c - $k)); - $m = intval(round($m - $k)); - $y = intval(round($y - $k)); - - return new CmykColor($c, $m, $y, $k); - } -} diff --git a/src/Colors/Cmyk/Decoders/StringColorDecoder.php b/src/Colors/Cmyk/Decoders/StringColorDecoder.php deleted file mode 100644 index 0f05ada9..00000000 --- a/src/Colors/Cmyk/Decoders/StringColorDecoder.php +++ /dev/null @@ -1,37 +0,0 @@ -[0-9\.]+%?), ?(?P[0-9\.]+%?), ?(?P[0-9\.]+%?), ?(?P[0-9\.]+%?)\)$/i'; - if (preg_match($pattern, $input, $matches) != 1) { - throw new DecoderException('Unable to decode input'); - } - - $values = array_map(function ($value) { - return intval(round(floatval(trim(str_replace('%', '', $value))))); - }, [$matches['c'], $matches['m'], $matches['y'], $matches['k']]); - - return new Color(...$values); - } -} diff --git a/src/Colors/Rgb/Color.php b/src/Colors/Rgb/Color.php index 89cf0b09..1a5723c5 100644 --- a/src/Colors/Rgb/Color.php +++ b/src/Colors/Rgb/Color.php @@ -2,8 +2,6 @@ namespace Intervention\Image\Colors\Rgb; -use Intervention\Image\Colors\Cmyk\Color as CmykColor; -use Intervention\Image\Colors\Cmyk\Colorspace as CmykColorspace; use Intervention\Image\Colors\Rgb\Channels\Blue; use Intervention\Image\Colors\Rgb\Channels\Green; use Intervention\Image\Colors\Rgb\Channels\Red; @@ -93,11 +91,6 @@ class Color implements ColorInterface return $colorspace->convertColor($this); } - public function toCmyk(): CmykColor - { - return $this->convertTo(CmykColorspace::class); - } - public function isFullyOpaque(): bool { return $this->alpha()->value() === 255; diff --git a/src/Colors/Rgb/Colorspace.php b/src/Colors/Rgb/Colorspace.php index 4c0bcb0f..4dbaae08 100644 --- a/src/Colors/Rgb/Colorspace.php +++ b/src/Colors/Rgb/Colorspace.php @@ -2,7 +2,6 @@ namespace Intervention\Image\Colors\Rgb; -use Intervention\Image\Colors\Cmyk\Color as CmykColor; use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\ColorspaceInterface; @@ -11,17 +10,7 @@ class Colorspace implements ColorspaceInterface public function convertColor(ColorInterface $color): ColorInterface { return match (get_class($color)) { - CmykColor::class => $this->convertCmykColor($color), default => $color, }; } - - protected function convertCmykColor(CmykColor $color): Color - { - return new Color( - (int) (255 * (1 - $color->cyan()->normalize()) * (1 - $color->key()->normalize())), - (int) (255 * (1 - $color->magenta()->normalize()) * (1 - $color->key()->normalize())), - (int) (255 * (1 - $color->yellow()->normalize()) * (1 - $color->key()->normalize())), - ); - } } diff --git a/src/Drivers/Gd/InputHandler.php b/src/Drivers/Gd/InputHandler.php index c545168f..5069e3fb 100644 --- a/src/Drivers/Gd/InputHandler.php +++ b/src/Drivers/Gd/InputHandler.php @@ -2,10 +2,9 @@ namespace Intervention\Image\Drivers\Gd; -use Intervention\Image\Colors\Rgb\Decoders\HexColorDecoder as RgbHexColorDecoder; -use Intervention\Image\Colors\Rgb\Decoders\StringColorDecoder as RgbStringColorDecoder; +use Intervention\Image\Colors\Rgb\Decoders\HexColorDecoder; +use Intervention\Image\Colors\Rgb\Decoders\StringColorDecoder; use Intervention\Image\Colors\Rgb\Decoders\HtmlColornameDecoder; -use Intervention\Image\Colors\Cmyk\Decoders\StringColorDecoder as CmykStringColorDecoder; use Intervention\Image\Drivers\Abstract\AbstractInputHandler; use Intervention\Image\Drivers\Gd\Decoders\ImageObjectDecoder; use Intervention\Image\Drivers\Gd\Decoders\ColorObjectDecoder; @@ -20,9 +19,8 @@ class InputHandler extends AbstractInputHandler protected $decoders = [ ImageObjectDecoder::class, ColorObjectDecoder::class, - RgbHexColorDecoder::class, - RgbStringColorDecoder::class, - CmykStringColorDecoder::class, + HexColorDecoder::class, + StringColorDecoder::class, // Decoders\TransparentColorDecoder::class, HtmlColornameDecoder::class, FilePointerImageDecoder::class, diff --git a/src/Drivers/Imagick/InputHandler.php b/src/Drivers/Imagick/InputHandler.php index f7c493b4..27ca7c5b 100644 --- a/src/Drivers/Imagick/InputHandler.php +++ b/src/Drivers/Imagick/InputHandler.php @@ -2,10 +2,9 @@ namespace Intervention\Image\Drivers\Imagick; -use Intervention\Image\Colors\Rgb\Decoders\HexColorDecoder as RgbHexColorDecoder; -use Intervention\Image\Colors\Rgb\Decoders\StringColorDecoder as RgbStringColorDecoder; +use Intervention\Image\Colors\Rgb\Decoders\HexColorDecoder; +use Intervention\Image\Colors\Rgb\Decoders\StringColorDecoder; use Intervention\Image\Colors\Rgb\Decoders\HtmlColornameDecoder; -use Intervention\Image\Colors\Cmyk\Decoders\StringColorDecoder as CmykStringColorDecoder; use Intervention\Image\Drivers\Abstract\AbstractInputHandler; use Intervention\Image\Drivers\Imagick\Decoders\ImageObjectDecoder; use Intervention\Image\Drivers\Imagick\Decoders\ColorObjectDecoder; @@ -20,9 +19,8 @@ class InputHandler extends AbstractInputHandler protected $decoders = [ ImageObjectDecoder::class, ColorObjectDecoder::class, - RgbHexColorDecoder::class, - RgbStringColorDecoder::class, - CmykStringColorDecoder::class, + HexColorDecoder::class, + StringColorDecoder::class, // Decoders\TransparentColorDecoder::class, HtmlColornameDecoder::class, FilePointerImageDecoder::class, diff --git a/src/Interfaces/ColorInterface.php b/src/Interfaces/ColorInterface.php index c1659ef2..6ba4cafe 100644 --- a/src/Interfaces/ColorInterface.php +++ b/src/Interfaces/ColorInterface.php @@ -2,13 +2,11 @@ namespace Intervention\Image\Interfaces; -use Intervention\Image\Colors\Cmyk\Color as CmykColor; -use Intervention\Image\Colors\Rgb\Color as RgbColor; +use Intervention\Image\Colors\Rgb\Color; interface ColorInterface { - public function toRgb(): RgbColor; - public function toCmyk(): CmykColor; + public function toRgb(): Color; public function toArray(): array; public function toString(): string; public function toHex(): string; diff --git a/tests/Colors/Cmyk/ChannelTest.php b/tests/Colors/Cmyk/ChannelTest.php deleted file mode 100644 index 44928abf..00000000 --- a/tests/Colors/Cmyk/ChannelTest.php +++ /dev/null @@ -1,48 +0,0 @@ -assertInstanceOf(Channel::class, $channel); - } - - public function testValue(): void - { - $channel = new Channel(10); - $this->assertEquals(10, $channel->value()); - } - - public function testNormalize(): void - { - $channel = new Channel(100); - $this->assertEquals(1, $channel->normalize()); - $channel = new Channel(0); - $this->assertEquals(0, $channel->normalize()); - $channel = new Channel(20); - $this->assertEquals(.2, $channel->normalize()); - } - - public function testValidate(): void - { - $this->expectException(ColorException::class); - new Channel(101); - - $this->expectException(ColorException::class); - new Channel(-1); - } -} diff --git a/tests/Colors/Cmyk/ColorTest.php b/tests/Colors/Cmyk/ColorTest.php deleted file mode 100644 index 070a199d..00000000 --- a/tests/Colors/Cmyk/ColorTest.php +++ /dev/null @@ -1,85 +0,0 @@ -assertInstanceOf(Color::class, $color); - } - - public function testChannels(): void - { - $color = new Color(10, 20, 30, 40); - $this->assertIsArray($color->channels()); - $this->assertCount(4, $color->channels()); - } - - public function testChannel(): void - { - $color = new Color(10, 20, 30, 40); - $channel = $color->channel(Cyan::class); - $this->assertInstanceOf(Cyan::class, $channel); - $this->assertEquals(10, $channel->value()); - } - - public function testCyanMagentaYellowKey(): void - { - $color = new Color(10, 20, 30, 40); - $this->assertInstanceOf(Cyan::class, $color->cyan()); - $this->assertInstanceOf(Magenta::class, $color->magenta()); - $this->assertInstanceOf(Yellow::class, $color->yellow()); - $this->assertInstanceOf(Key::class, $color->key()); - $this->assertEquals(10, $color->cyan()->value()); - $this->assertEquals(20, $color->magenta()->value()); - $this->assertEquals(30, $color->yellow()->value()); - $this->assertEquals(40, $color->key()->value()); - } - - public function testToArray(): void - { - $color = new Color(10, 20, 30, 40); - $this->assertEquals([10, 20, 30, 40], $color->toArray()); - } - - public function testNormalize(): void - { - $color = new Color(100, 50, 20, 0); - $this->assertEquals([1.0, 0.5, 0.2, 0.0], $color->normalize()); - } - - public function testToString(): void - { - $color = new Color(100, 50, 20, 0); - $this->assertEquals('cmyk(100, 50, 20, 0)', (string) $color); - } - - public function testToCmyk(): void - { - $color = new Color(0, 0, 0, 0); - $converted = $color->toCmyk(); - $this->assertInstanceOf(Color::class, $converted); - } - - public function testToRgb(): void - { - $color = new Color(0, 20, 20, 0); - $converted = $color->toRgb(); - $this->assertInstanceOf(RgbColor::class, $converted); - $this->assertEquals([255, 204, 204, 255], $converted->toArray()); - } -} diff --git a/tests/Colors/Cmyk/ColorspaceTest.php b/tests/Colors/Cmyk/ColorspaceTest.php deleted file mode 100644 index 818269fc..00000000 --- a/tests/Colors/Cmyk/ColorspaceTest.php +++ /dev/null @@ -1,26 +0,0 @@ -assertInstanceOf( - CmykColor::class, - $colorspace->convertColor( - new RgbColor(0, 0, 0) - ) - ); - } -} diff --git a/tests/Colors/Cmyk/Decoders/StringColorDecoderTest.php b/tests/Colors/Cmyk/Decoders/StringColorDecoderTest.php deleted file mode 100644 index f6af4237..00000000 --- a/tests/Colors/Cmyk/Decoders/StringColorDecoderTest.php +++ /dev/null @@ -1,35 +0,0 @@ -decode('cmyk(0,0,0,0)'); - $this->assertInstanceOf(Color::class, $result); - $this->assertEquals([0, 0, 0, 0], $result->toArray()); - - $result = $decoder->decode('cmyk(0, 100, 100, 0)'); - $this->assertInstanceOf(Color::class, $result); - $this->assertEquals([0, 100, 100, 0], $result->toArray()); - - $result = $decoder->decode('cmyk(0, 100, 100, 0)'); - $this->assertInstanceOf(Color::class, $result); - $this->assertEquals([0, 100, 100, 0], $result->toArray()); - - - $result = $decoder->decode('cmyk(0%, 100%, 100%, 0%)'); - $this->assertInstanceOf(Color::class, $result); - $this->assertEquals([0, 100, 100, 0], $result->toArray()); - } -} diff --git a/tests/Colors/Rgb/ColorTest.php b/tests/Colors/Rgb/ColorTest.php index f44191bd..131dcef7 100644 --- a/tests/Colors/Rgb/ColorTest.php +++ b/tests/Colors/Rgb/ColorTest.php @@ -2,7 +2,6 @@ namespace Intervention\Image\Tests\Colors\Rgb; -use Intervention\Image\Colors\Cmyk\Color as CmykColor; use Intervention\Image\Colors\Rgb\Channels\Red; use Intervention\Image\Colors\Rgb\Channels\Green; use Intervention\Image\Colors\Rgb\Channels\Blue; @@ -75,39 +74,6 @@ class ColorTest extends TestCase $this->assertEquals('rgb(181, 55, 23)', (string) $color); } - public function testToCmyk(): void - { - $color = new Color(0, 0, 0); - $converted = $color->toCmyk(); - $this->assertInstanceOf(CmykColor::class, $converted); - $this->assertEquals([0, 0, 0, 100], $converted->toArray()); - - $color = new Color(255, 255, 255); - $converted = $color->toCmyk(); - $this->assertInstanceOf(CmykColor::class, $converted); - $this->assertEquals([0, 0, 0, 0], $converted->toArray()); - - $color = new Color(255, 0, 0); - $converted = $color->toCmyk(); - $this->assertInstanceOf(CmykColor::class, $converted); - $this->assertEquals([0, 100, 100, 0], $converted->toArray()); - - $color = new Color(255, 0, 255); - $converted = $color->toCmyk(); - $this->assertInstanceOf(CmykColor::class, $converted); - $this->assertEquals([0, 100, 0, 0], $converted->toArray()); - - $color = new Color(255, 255, 0); - $converted = $color->toCmyk(); - $this->assertInstanceOf(CmykColor::class, $converted); - $this->assertEquals([0, 0, 100, 0], $converted->toArray()); - - $color = new Color(255, 204, 204); - $converted = $color->toCmyk(); - $this->assertInstanceOf(CmykColor::class, $converted); - $this->assertEquals([0, 20, 20, 0], $converted->toArray()); - } - public function testToRgb(): void { $color = new Color(181, 55, 23); diff --git a/tests/Colors/Rgb/ColorspaceTest.php b/tests/Colors/Rgb/ColorspaceTest.php deleted file mode 100644 index be7808c5..00000000 --- a/tests/Colors/Rgb/ColorspaceTest.php +++ /dev/null @@ -1,26 +0,0 @@ -assertInstanceOf( - RgbColor::class, - $colorspace->convertColor( - new CmykColor(0, 0, 0, 0) - ) - ); - } -} diff --git a/tests/Drivers/Gd/InputHandlerTest.php b/tests/Drivers/Gd/InputHandlerTest.php index 07328d49..7be7f4e0 100644 --- a/tests/Drivers/Gd/InputHandlerTest.php +++ b/tests/Drivers/Gd/InputHandlerTest.php @@ -2,7 +2,7 @@ namespace Intervention\Image\Tests\Drivers\Gd; -use Intervention\Image\Colors\Rgb\Color as RgbColor; +use Intervention\Image\Colors\Rgb\Color; use Intervention\Image\Drivers\Gd\Image; use Intervention\Image\Drivers\Gd\InputHandler; use Intervention\Image\Exceptions\DecoderException; @@ -58,37 +58,37 @@ class GdInputHandlerTest extends TestCase $handler = new InputHandler(); $input = 'ccff33'; $result = $handler->handle($input); - $this->assertInstanceOf(RgbColor::class, $result); + $this->assertInstanceOf(Color::class, $result); $this->assertEquals([204, 255, 51, 255], $result->toArray()); $handler = new InputHandler(); $input = 'cf3'; $result = $handler->handle($input); - $this->assertInstanceOf(RgbColor::class, $result); + $this->assertInstanceOf(Color::class, $result); $this->assertEquals([204, 255, 51, 255], $result->toArray()); $handler = new InputHandler(); $input = '#123456'; $result = $handler->handle($input); - $this->assertInstanceOf(RgbColor::class, $result); + $this->assertInstanceOf(Color::class, $result); $this->assertEquals([18, 52, 86, 255], $result->toArray()); $handler = new InputHandler(); $input = '#333'; $result = $handler->handle($input); - $this->assertInstanceOf(RgbColor::class, $result); + $this->assertInstanceOf(Color::class, $result); $this->assertEquals([51, 51, 51, 255], $result->toArray()); $handler = new InputHandler(); $input = '#3333'; $result = $handler->handle($input); - $this->assertInstanceOf(RgbColor::class, $result); + $this->assertInstanceOf(Color::class, $result); $this->assertEquals([51, 51, 51, 51], $result->toArray()); $handler = new InputHandler(); $input = '#33333333'; $result = $handler->handle($input); - $this->assertInstanceOf(RgbColor::class, $result); + $this->assertInstanceOf(Color::class, $result); $this->assertEquals([51, 51, 51, 51], $result->toArray()); } @@ -96,12 +96,12 @@ class GdInputHandlerTest extends TestCase { $handler = new InputHandler(); $result = $handler->handle('rgb(10, 20, 30)'); - $this->assertInstanceOf(RgbColor::class, $result); + $this->assertInstanceOf(Color::class, $result); $this->assertEquals([10, 20, 30, 255], $result->toArray()); $handler = new InputHandler(); $result = $handler->handle('rgba(10, 20, 30, 1.0)'); - $this->assertInstanceOf(RgbColor::class, $result); + $this->assertInstanceOf(Color::class, $result); $this->assertEquals([10, 20, 30, 255], $result->toArray()); } diff --git a/tests/Drivers/Imagick/InputHandlerTest.php b/tests/Drivers/Imagick/InputHandlerTest.php index 158edd5e..df2a5b1a 100644 --- a/tests/Drivers/Imagick/InputHandlerTest.php +++ b/tests/Drivers/Imagick/InputHandlerTest.php @@ -2,7 +2,7 @@ namespace Intervention\Image\Tests\Drivers\Imagick; -use Intervention\Image\Colors\Rgb\Color as RgbColor; +use Intervention\Image\Colors\Rgb\Color; use Intervention\Image\Drivers\Imagick\Image; use Intervention\Image\Drivers\Imagick\InputHandler; use Intervention\Image\Exceptions\DecoderException; @@ -58,37 +58,37 @@ class InputHandlerTest extends TestCase $handler = new InputHandler(); $input = 'ccff33'; $result = $handler->handle($input); - $this->assertInstanceOf(RgbColor::class, $result); + $this->assertInstanceOf(Color::class, $result); $this->assertEquals([204, 255, 51, 255], $result->toArray()); $handler = new InputHandler(); $input = 'cf3'; $result = $handler->handle($input); - $this->assertInstanceOf(RgbColor::class, $result); + $this->assertInstanceOf(Color::class, $result); $this->assertEquals([204, 255, 51, 255], $result->toArray()); $handler = new InputHandler(); $input = '#123456'; $result = $handler->handle($input); - $this->assertInstanceOf(RgbColor::class, $result); + $this->assertInstanceOf(Color::class, $result); $this->assertEquals([18, 52, 86, 255], $result->toArray()); $handler = new InputHandler(); $input = '#333'; $result = $handler->handle($input); - $this->assertInstanceOf(RgbColor::class, $result); + $this->assertInstanceOf(Color::class, $result); $this->assertEquals([51, 51, 51, 255], $result->toArray()); $handler = new InputHandler(); $input = '#3333'; $result = $handler->handle($input); - $this->assertInstanceOf(RgbColor::class, $result); + $this->assertInstanceOf(Color::class, $result); $this->assertEquals([51, 51, 51, 51], $result->toArray()); $handler = new InputHandler(); $input = '#33333333'; $result = $handler->handle($input); - $this->assertInstanceOf(RgbColor::class, $result); + $this->assertInstanceOf(Color::class, $result); $this->assertEquals([51, 51, 51, 51], $result->toArray()); } @@ -96,12 +96,12 @@ class InputHandlerTest extends TestCase { $handler = new InputHandler(); $result = $handler->handle('rgb(10, 20, 30)'); - $this->assertInstanceOf(RgbColor::class, $result); + $this->assertInstanceOf(Color::class, $result); $this->assertEquals([10, 20, 30, 255], $result->toArray()); $handler = new InputHandler(); $result = $handler->handle('rgba(10, 20, 30, 1.0)'); - $this->assertInstanceOf(RgbColor::class, $result); + $this->assertInstanceOf(Color::class, $result); $this->assertEquals([10, 20, 30, 255], $result->toArray()); } } diff --git a/tests/TestCase.php b/tests/TestCase.php index 81eddb55..cfd60927 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -2,7 +2,7 @@ namespace Intervention\Image\Tests; -use Intervention\Image\Colors\Rgb\Color as RgbColor; +use Intervention\Image\Colors\Rgb\Color; use Intervention\Image\Interfaces\ColorInterface; use Mockery\Adapter\Phpunit\MockeryTestCase; @@ -25,7 +25,7 @@ abstract class TestCase extends MockeryTestCase protected function assertTransparency(ColorInterface $color) { - $this->assertInstanceOf(RgbColor::class, $color); + $this->assertInstanceOf(Color::class, $color); $this->assertEquals(0, $color->toRgb()->alpha()->value()); } } From df4c3e0dddec0ad0e655ead0f8fa66d30c4fd8b1 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Fri, 20 Oct 2023 15:58:35 +0200 Subject: [PATCH 285/476] Adjust modifiers for new color model --- .../Rgb/Decoders/TransparentColorDecoder.php | 23 +++++++++++++++++++ .../Modifiers/AbstractDrawModifier.php | 4 ++-- src/Drivers/Gd/InputHandler.php | 3 ++- .../Gd/Modifiers/DrawEllipseModifier.php | 9 +++++--- src/Drivers/Gd/Modifiers/DrawLineModifier.php | 5 +++- .../Gd/Modifiers/DrawPolygonModifier.php | 7 ++++-- .../Gd/Modifiers/DrawRectangleModifier.php | 7 ++++-- src/Drivers/Gd/Modifiers/PadModifier.php | 9 ++++---- src/Drivers/Gd/Modifiers/RotateModifier.php | 5 +++- src/Drivers/Gd/Modifiers/TextWriter.php | 8 +++++-- src/Drivers/Imagick/InputHandler.php | 3 ++- .../Imagick/Modifiers/DrawEllipseModifier.php | 12 ++++++---- .../Imagick/Modifiers/DrawLineModifier.php | 10 +++++--- .../Imagick/Modifiers/DrawPolygonModifier.php | 12 ++++++---- .../Modifiers/DrawRectangleModifier.php | 13 ++++++----- .../Imagick/Modifiers/RotateModifier.php | 8 +++---- tests/Drivers/Gd/InputHandlerTest.php | 15 ++++++------ tests/Drivers/Imagick/InputHandlerTest.php | 9 ++++++++ 18 files changed, 113 insertions(+), 49 deletions(-) create mode 100644 src/Colors/Rgb/Decoders/TransparentColorDecoder.php diff --git a/src/Colors/Rgb/Decoders/TransparentColorDecoder.php b/src/Colors/Rgb/Decoders/TransparentColorDecoder.php new file mode 100644 index 00000000..1474987c --- /dev/null +++ b/src/Colors/Rgb/Decoders/TransparentColorDecoder.php @@ -0,0 +1,23 @@ +drawable; } - protected function getBackgroundColor(): ?ColorInterface + protected function getBackgroundColor(): ColorInterface { try { $color = $this->handleInput($this->drawable->getBackgroundColor()); @@ -42,7 +42,7 @@ class AbstractDrawModifier return $color; } - protected function getBorderColor(): ?ColorInterface + protected function getBorderColor(): ColorInterface { try { $color = $this->handleInput($this->drawable->getBorderColor()); diff --git a/src/Drivers/Gd/InputHandler.php b/src/Drivers/Gd/InputHandler.php index 5069e3fb..85d69a74 100644 --- a/src/Drivers/Gd/InputHandler.php +++ b/src/Drivers/Gd/InputHandler.php @@ -5,6 +5,7 @@ namespace Intervention\Image\Drivers\Gd; use Intervention\Image\Colors\Rgb\Decoders\HexColorDecoder; use Intervention\Image\Colors\Rgb\Decoders\StringColorDecoder; use Intervention\Image\Colors\Rgb\Decoders\HtmlColornameDecoder; +use Intervention\Image\Colors\Rgb\Decoders\TransparentColorDecoder; use Intervention\Image\Drivers\Abstract\AbstractInputHandler; use Intervention\Image\Drivers\Gd\Decoders\ImageObjectDecoder; use Intervention\Image\Drivers\Gd\Decoders\ColorObjectDecoder; @@ -21,7 +22,7 @@ class InputHandler extends AbstractInputHandler ColorObjectDecoder::class, HexColorDecoder::class, StringColorDecoder::class, - // Decoders\TransparentColorDecoder::class, + TransparentColorDecoder::class, HtmlColornameDecoder::class, FilePointerImageDecoder::class, FilePathImageDecoder::class, diff --git a/src/Drivers/Gd/Modifiers/DrawEllipseModifier.php b/src/Drivers/Gd/Modifiers/DrawEllipseModifier.php index 8f956c71..ed99cb3e 100644 --- a/src/Drivers/Gd/Modifiers/DrawEllipseModifier.php +++ b/src/Drivers/Gd/Modifiers/DrawEllipseModifier.php @@ -3,11 +3,14 @@ namespace Intervention\Image\Drivers\Gd\Modifiers; use Intervention\Image\Drivers\Abstract\Modifiers\AbstractDrawModifier; +use Intervention\Image\Drivers\Gd\Traits\CanHandleColors; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; class DrawEllipseModifier extends AbstractDrawModifier implements ModifierInterface { + use CanHandleColors; + public function apply(ImageInterface $image): ImageInterface { return $image->eachFrame(function ($frame) { @@ -20,7 +23,7 @@ class DrawEllipseModifier extends AbstractDrawModifier implements ModifierInterf $this->position->getY(), $this->ellipse()->getWidth() - 1, $this->ellipse()->getHeight() - 1, - $this->getBackgroundColor()->toInt() + $this->colorToInteger($this->getBackgroundColor()) ); } @@ -36,7 +39,7 @@ class DrawEllipseModifier extends AbstractDrawModifier implements ModifierInterf $this->ellipse()->getHeight(), 0, 360, - $this->getBorderColor()->toInt() + $this->colorToInteger($this->getBorderColor()) ); } else { imagefilledellipse( @@ -45,7 +48,7 @@ class DrawEllipseModifier extends AbstractDrawModifier implements ModifierInterf $this->position->getY(), $this->ellipse()->getWidth(), $this->ellipse()->getHeight(), - $this->getBackgroundColor()->toInt() + $this->colorToInteger($this->getBackgroundColor()) ); } }); diff --git a/src/Drivers/Gd/Modifiers/DrawLineModifier.php b/src/Drivers/Gd/Modifiers/DrawLineModifier.php index 677d6345..9cfafb58 100644 --- a/src/Drivers/Gd/Modifiers/DrawLineModifier.php +++ b/src/Drivers/Gd/Modifiers/DrawLineModifier.php @@ -3,11 +3,14 @@ namespace Intervention\Image\Drivers\Gd\Modifiers; use Intervention\Image\Drivers\Abstract\Modifiers\AbstractDrawModifier; +use Intervention\Image\Drivers\Gd\Traits\CanHandleColors; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; class DrawLineModifier extends AbstractDrawModifier implements ModifierInterface { + use CanHandleColors; + public function apply(ImageInterface $image): ImageInterface { return $image->eachFrame(function ($frame) { @@ -17,7 +20,7 @@ class DrawLineModifier extends AbstractDrawModifier implements ModifierInterface $this->line()->getStart()->getY(), $this->line()->getEnd()->getX(), $this->line()->getEnd()->getY(), - $this->getBackgroundColor()->toInt() + $this->colorToInteger($this->getBackgroundColor()) ); }); } diff --git a/src/Drivers/Gd/Modifiers/DrawPolygonModifier.php b/src/Drivers/Gd/Modifiers/DrawPolygonModifier.php index cb4c930f..f0cd77bb 100644 --- a/src/Drivers/Gd/Modifiers/DrawPolygonModifier.php +++ b/src/Drivers/Gd/Modifiers/DrawPolygonModifier.php @@ -3,12 +3,15 @@ namespace Intervention\Image\Drivers\Gd\Modifiers; use Intervention\Image\Drivers\Abstract\Modifiers\AbstractDrawModifier; +use Intervention\Image\Drivers\Gd\Traits\CanHandleColors; use Intervention\Image\Interfaces\DrawableInterface; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; class DrawPolygonModifier extends AbstractDrawModifier implements ModifierInterface { + use CanHandleColors; + public function __construct( protected DrawableInterface $drawable ) { @@ -22,7 +25,7 @@ class DrawPolygonModifier extends AbstractDrawModifier implements ModifierInterf imagefilledpolygon( $frame->getCore(), $this->polygon()->toArray(), - $this->getBackgroundColor()->toInt() + $this->colorToInteger($this->getBackgroundColor()) ); } @@ -32,7 +35,7 @@ class DrawPolygonModifier extends AbstractDrawModifier implements ModifierInterf $frame->getCore(), $this->polygon()->toArray(), $this->polygon()->count(), - $this->getBorderColor()->toInt() + $this->colorToInteger($this->getBorderColor()) ); } }); diff --git a/src/Drivers/Gd/Modifiers/DrawRectangleModifier.php b/src/Drivers/Gd/Modifiers/DrawRectangleModifier.php index 1b5e39dc..2cc1653b 100644 --- a/src/Drivers/Gd/Modifiers/DrawRectangleModifier.php +++ b/src/Drivers/Gd/Modifiers/DrawRectangleModifier.php @@ -3,11 +3,14 @@ namespace Intervention\Image\Drivers\Gd\Modifiers; use Intervention\Image\Drivers\Abstract\Modifiers\AbstractDrawModifier; +use Intervention\Image\Drivers\Gd\Traits\CanHandleColors; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; class DrawRectangleModifier extends AbstractDrawModifier implements ModifierInterface { + use CanHandleColors; + public function apply(ImageInterface $image): ImageInterface { $image->eachFrame(function ($frame) { @@ -19,7 +22,7 @@ class DrawRectangleModifier extends AbstractDrawModifier implements ModifierInte $this->position->getY(), $this->position->getX() + $this->rectangle()->bottomRightPoint()->getX(), $this->position->getY() + $this->rectangle()->bottomRightPoint()->getY(), - $this->getBackgroundColor()->toInt() + $this->colorToInteger($this->getBackgroundColor()) ); } @@ -32,7 +35,7 @@ class DrawRectangleModifier extends AbstractDrawModifier implements ModifierInte $this->position->getY(), $this->position->getX() + $this->rectangle()->bottomRightPoint()->getX(), $this->position->getY() + $this->rectangle()->bottomRightPoint()->getY(), - $this->getBorderColor()->toInt() + $this->colorToInteger($this->getBorderColor()) ); } }); diff --git a/src/Drivers/Gd/Modifiers/PadModifier.php b/src/Drivers/Gd/Modifiers/PadModifier.php index a8e78445..34fdb4d7 100644 --- a/src/Drivers/Gd/Modifiers/PadModifier.php +++ b/src/Drivers/Gd/Modifiers/PadModifier.php @@ -3,7 +3,7 @@ namespace Intervention\Image\Drivers\Gd\Modifiers; use Intervention\Image\Drivers\Abstract\Modifiers\AbstractPadModifier; -use Intervention\Image\Interfaces\ColorInterface; +use Intervention\Image\Drivers\Gd\Traits\CanHandleColors; use Intervention\Image\Interfaces\FrameInterface; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; @@ -13,12 +13,13 @@ use Intervention\Image\Traits\CanHandleInput; class PadModifier extends AbstractPadModifier implements ModifierInterface { use CanHandleInput; + use CanHandleColors; public function apply(ImageInterface $image): ImageInterface { $crop = $this->getCropSize($image); $resize = $this->getResizeSize($image); - $background = $this->handleInput($this->background); + $background = $this->colorToInteger($this->handleInput($this->background)); foreach ($image as $frame) { $this->modify($frame, $crop, $resize, $background); @@ -31,7 +32,7 @@ class PadModifier extends AbstractPadModifier implements ModifierInterface FrameInterface $frame, SizeInterface $crop, SizeInterface $resize, - ColorInterface $background + int $background ): void { // create new image $modified = imagecreatetruecolor( @@ -39,7 +40,7 @@ class PadModifier extends AbstractPadModifier implements ModifierInterface $resize->getHeight() ); - imagefill($modified, 0, 0, $background->toInt()); + imagefill($modified, 0, 0, $background); // get current image $current = $frame->getCore(); diff --git a/src/Drivers/Gd/Modifiers/RotateModifier.php b/src/Drivers/Gd/Modifiers/RotateModifier.php index b692c474..efaecddd 100644 --- a/src/Drivers/Gd/Modifiers/RotateModifier.php +++ b/src/Drivers/Gd/Modifiers/RotateModifier.php @@ -3,11 +3,14 @@ namespace Intervention\Image\Drivers\Gd\Modifiers; use Intervention\Image\Drivers\Abstract\Modifiers\AbstractRotateModifier; +use Intervention\Image\Drivers\Gd\Traits\CanHandleColors; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; class RotateModifier extends AbstractRotateModifier implements ModifierInterface { + use CanHandleColors; + public function apply(ImageInterface $image): ImageInterface { foreach ($image as $frame) { @@ -15,7 +18,7 @@ class RotateModifier extends AbstractRotateModifier implements ModifierInterface imagerotate( $frame->getCore(), $this->rotationAngle(), - $this->backgroundColor()->toInt() + $this->colorToInteger($this->backgroundColor()) ) ); } diff --git a/src/Drivers/Gd/Modifiers/TextWriter.php b/src/Drivers/Gd/Modifiers/TextWriter.php index fd117bca..61cf63ee 100644 --- a/src/Drivers/Gd/Modifiers/TextWriter.php +++ b/src/Drivers/Gd/Modifiers/TextWriter.php @@ -4,14 +4,18 @@ namespace Intervention\Image\Drivers\Gd\Modifiers; use Intervention\Image\Drivers\Abstract\AbstractTextWriter; use Intervention\Image\Drivers\Gd\Font; +use Intervention\Image\Drivers\Gd\Traits\CanHandleColors; use Intervention\Image\Interfaces\ImageInterface; class TextWriter extends AbstractTextWriter { + use CanHandleColors; + public function apply(ImageInterface $image): ImageInterface { $lines = $this->getAlignedTextBlock(); $font = $this->failIfNotClass($this->getFont(), Font::class); + $color = $this->colorToInteger($font->getColor()); foreach ($image as $frame) { if ($this->font->hasFilename()) { @@ -22,7 +26,7 @@ class TextWriter extends AbstractTextWriter $font->getAngle() * (-1), $line->getPosition()->getX(), $line->getPosition()->getY(), - $font->getColor()->toInt(), + $color, $font->getFilename(), $line ); @@ -35,7 +39,7 @@ class TextWriter extends AbstractTextWriter $line->getPosition()->getX(), $line->getPosition()->getY(), $line, - $this->font->getColor()->toInt() + $color ); } } diff --git a/src/Drivers/Imagick/InputHandler.php b/src/Drivers/Imagick/InputHandler.php index 27ca7c5b..3d27678a 100644 --- a/src/Drivers/Imagick/InputHandler.php +++ b/src/Drivers/Imagick/InputHandler.php @@ -5,6 +5,7 @@ namespace Intervention\Image\Drivers\Imagick; use Intervention\Image\Colors\Rgb\Decoders\HexColorDecoder; use Intervention\Image\Colors\Rgb\Decoders\StringColorDecoder; use Intervention\Image\Colors\Rgb\Decoders\HtmlColornameDecoder; +use Intervention\Image\Colors\Rgb\Decoders\TransparentColorDecoder; use Intervention\Image\Drivers\Abstract\AbstractInputHandler; use Intervention\Image\Drivers\Imagick\Decoders\ImageObjectDecoder; use Intervention\Image\Drivers\Imagick\Decoders\ColorObjectDecoder; @@ -21,7 +22,7 @@ class InputHandler extends AbstractInputHandler ColorObjectDecoder::class, HexColorDecoder::class, StringColorDecoder::class, - // Decoders\TransparentColorDecoder::class, + TransparentColorDecoder::class, HtmlColornameDecoder::class, FilePointerImageDecoder::class, FilePathImageDecoder::class, diff --git a/src/Drivers/Imagick/Modifiers/DrawEllipseModifier.php b/src/Drivers/Imagick/Modifiers/DrawEllipseModifier.php index 289500c2..0c3e53fc 100644 --- a/src/Drivers/Imagick/Modifiers/DrawEllipseModifier.php +++ b/src/Drivers/Imagick/Modifiers/DrawEllipseModifier.php @@ -4,24 +4,26 @@ namespace Intervention\Image\Drivers\Imagick\Modifiers; use ImagickDraw; use Intervention\Image\Drivers\Abstract\Modifiers\AbstractDrawModifier; -use Intervention\Image\Drivers\Imagick\Color; +use Intervention\Image\Drivers\Imagick\Traits\CanHandleColors; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; class DrawEllipseModifier extends AbstractDrawModifier implements ModifierInterface { + use CanHandleColors; + public function apply(ImageInterface $image): ImageInterface { - $background_color = $this->failIfNotClass($this->getBackgroundColor(), Color::class); - $border_color = $this->failIfNotClass($this->getBorderColor(), Color::class); + $background_color = $this->colorToPixel($this->getBackgroundColor()); + $border_color = $this->colorToPixel($this->getBorderColor()); return $image->eachFrame(function ($frame) use ($background_color, $border_color) { $drawing = new ImagickDraw(); - $drawing->setFillColor($background_color->getPixel()); + $drawing->setFillColor($background_color); if ($this->ellipse()->hasBorder()) { $drawing->setStrokeWidth($this->ellipse()->getBorderSize()); - $drawing->setStrokeColor($border_color->getPixel()); + $drawing->setStrokeColor($border_color); } $drawing->ellipse( diff --git a/src/Drivers/Imagick/Modifiers/DrawLineModifier.php b/src/Drivers/Imagick/Modifiers/DrawLineModifier.php index 17c4ac76..74fa9472 100644 --- a/src/Drivers/Imagick/Modifiers/DrawLineModifier.php +++ b/src/Drivers/Imagick/Modifiers/DrawLineModifier.php @@ -4,18 +4,22 @@ namespace Intervention\Image\Drivers\Imagick\Modifiers; use ImagickDraw; use Intervention\Image\Drivers\Abstract\Modifiers\AbstractDrawModifier; -use Intervention\Image\Drivers\Imagick\Color; +use Intervention\Image\Drivers\Imagick\Traits\CanHandleColors; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; class DrawLineModifier extends AbstractDrawModifier implements ModifierInterface { + use CanHandleColors; + public function apply(ImageInterface $image): ImageInterface { $drawing = new ImagickDraw(); - $color = $this->failIfNotClass($this->getBackgroundColor(), Color::class); - $drawing->setStrokeColor($color->getPixel()); $drawing->setStrokeWidth($this->line()->getWidth()); + $drawing->setStrokeColor( + $this->colorToPixel($this->getBackgroundColor()) + ); + $drawing->line( $this->line()->getStart()->getX(), $this->line()->getStart()->getY(), diff --git a/src/Drivers/Imagick/Modifiers/DrawPolygonModifier.php b/src/Drivers/Imagick/Modifiers/DrawPolygonModifier.php index ea8b511c..34634dbb 100644 --- a/src/Drivers/Imagick/Modifiers/DrawPolygonModifier.php +++ b/src/Drivers/Imagick/Modifiers/DrawPolygonModifier.php @@ -4,13 +4,15 @@ namespace Intervention\Image\Drivers\Imagick\Modifiers; use ImagickDraw; use Intervention\Image\Drivers\Abstract\Modifiers\AbstractDrawModifier; -use Intervention\Image\Drivers\Imagick\Color; +use Intervention\Image\Drivers\Imagick\Traits\CanHandleColors; use Intervention\Image\Interfaces\DrawableInterface; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; class DrawPolygonModifier extends AbstractDrawModifier implements ModifierInterface { + use CanHandleColors; + public function __construct( protected DrawableInterface $drawable ) { @@ -20,15 +22,15 @@ class DrawPolygonModifier extends AbstractDrawModifier implements ModifierInterf public function apply(ImageInterface $image): ImageInterface { $drawing = new ImagickDraw(); - $background_color = $this->failIfNotClass($this->getBackgroundColor(), Color::class); - $border_color = $this->failIfNotClass($this->getBorderColor(), Color::class); + $background_color = $this->colorToPixel($this->getBackgroundColor()); + $border_color = $this->colorToPixel($this->getBorderColor()); if ($this->polygon()->hasBackgroundColor()) { - $drawing->setFillColor($background_color->getPixel()); + $drawing->setFillColor($background_color); } if ($this->polygon()->hasBorder()) { - $drawing->setStrokeColor($border_color->getPixel()); + $drawing->setStrokeColor($border_color); $drawing->setStrokeWidth($this->polygon()->getBorderSize()); } diff --git a/src/Drivers/Imagick/Modifiers/DrawRectangleModifier.php b/src/Drivers/Imagick/Modifiers/DrawRectangleModifier.php index 2baf9721..60d225d3 100644 --- a/src/Drivers/Imagick/Modifiers/DrawRectangleModifier.php +++ b/src/Drivers/Imagick/Modifiers/DrawRectangleModifier.php @@ -4,22 +4,23 @@ namespace Intervention\Image\Drivers\Imagick\Modifiers; use ImagickDraw; use Intervention\Image\Drivers\Abstract\Modifiers\AbstractDrawModifier; -use Intervention\Image\Drivers\Imagick\Color; +use Intervention\Image\Drivers\Imagick\Traits\CanHandleColors; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; class DrawRectangleModifier extends AbstractDrawModifier implements ModifierInterface { + use CanHandleColors; + public function apply(ImageInterface $image): ImageInterface { - // setup $drawing = new ImagickDraw(); - $background_color = $this->failIfNotClass($this->getBackgroundColor(), Color::class); - $border_color = $this->failIfNotClass($this->getBorderColor(), Color::class); + $background_color = $this->colorToPixel($this->getBackgroundColor()); + $border_color = $this->colorToPixel($this->getBorderColor()); - $drawing->setFillColor($background_color->getPixel()); + $drawing->setFillColor($background_color); if ($this->rectangle()->hasBorder()) { - $drawing->setStrokeColor($border_color->getPixel()); + $drawing->setStrokeColor($border_color); $drawing->setStrokeWidth($this->rectangle()->getBorderSize()); } diff --git a/src/Drivers/Imagick/Modifiers/RotateModifier.php b/src/Drivers/Imagick/Modifiers/RotateModifier.php index ff908c04..e178f31c 100644 --- a/src/Drivers/Imagick/Modifiers/RotateModifier.php +++ b/src/Drivers/Imagick/Modifiers/RotateModifier.php @@ -3,19 +3,19 @@ namespace Intervention\Image\Drivers\Imagick\Modifiers; use Intervention\Image\Drivers\Abstract\Modifiers\AbstractRotateModifier; -use Intervention\Image\Drivers\Imagick\Color; +use Intervention\Image\Drivers\Imagick\Traits\CanHandleColors; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; class RotateModifier extends AbstractRotateModifier implements ModifierInterface { + use CanHandleColors; + public function apply(ImageInterface $image): ImageInterface { - $background = $this->failIfNotClass($this->backgroundColor(), Color::class); - foreach ($image as $frame) { $frame->getCore()->rotateImage( - $background->getPixel(), + $this->colorToPixel($this->backgroundColor()), $this->rotationAngle() ); } diff --git a/tests/Drivers/Gd/InputHandlerTest.php b/tests/Drivers/Gd/InputHandlerTest.php index 7be7f4e0..c9d1335b 100644 --- a/tests/Drivers/Gd/InputHandlerTest.php +++ b/tests/Drivers/Gd/InputHandlerTest.php @@ -105,11 +105,12 @@ class GdInputHandlerTest extends TestCase $this->assertEquals([10, 20, 30, 255], $result->toArray()); } - // public function testHandleTransparent(): void - // { - // $handler = new InputHandler(); - // $input = 'transparent'; - // $result = $handler->handle($input); - // $this->assertInstanceOf(Color::class, $result); - // } + public function testHandleTransparent(): void + { + $handler = new InputHandler(); + $input = 'transparent'; + $result = $handler->handle($input); + $this->assertInstanceOf(Color::class, $result); + $this->assertEquals([0, 0, 0, 0], $result->toArray()); + } } diff --git a/tests/Drivers/Imagick/InputHandlerTest.php b/tests/Drivers/Imagick/InputHandlerTest.php index df2a5b1a..ed7a99ca 100644 --- a/tests/Drivers/Imagick/InputHandlerTest.php +++ b/tests/Drivers/Imagick/InputHandlerTest.php @@ -104,4 +104,13 @@ class InputHandlerTest extends TestCase $this->assertInstanceOf(Color::class, $result); $this->assertEquals([10, 20, 30, 255], $result->toArray()); } + + public function testHandleTransparent(): void + { + $handler = new InputHandler(); + $input = 'transparent'; + $result = $handler->handle($input); + $this->assertInstanceOf(Color::class, $result); + $this->assertEquals([0, 0, 0, 0], $result->toArray()); + } } From e3c8ca2edf562bd5731625844520ab486ea031c4 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Fri, 20 Oct 2023 16:16:08 +0200 Subject: [PATCH 286/476] Create new images with RGB colorspace --- src/Drivers/Imagick/ImageFactory.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Drivers/Imagick/ImageFactory.php b/src/Drivers/Imagick/ImageFactory.php index 773f1a53..999965d9 100644 --- a/src/Drivers/Imagick/ImageFactory.php +++ b/src/Drivers/Imagick/ImageFactory.php @@ -57,7 +57,7 @@ class ImageFactory implements FactoryInterface $imagick->newImage($width, $height, new ImagickPixel('rgba(0, 0, 0, 0)'), 'png'); $imagick->setType(Imagick::IMGTYPE_UNDEFINED); $imagick->setImageType(Imagick::IMGTYPE_UNDEFINED); - $imagick->setColorspace(Imagick::COLORSPACE_UNDEFINED); + $imagick->setColorspace(Imagick::COLORSPACE_RGB); return $imagick; } From b5cbff3a8968b1a621e494fb609a54388ef40954 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 21 Oct 2023 09:25:36 +0200 Subject: [PATCH 287/476] Remove remaining toRgb() calls --- src/Colors/Rgb/Color.php | 5 ---- src/Drivers/Gd/Traits/CanHandleColors.php | 5 ++-- .../Imagick/Decoders/BinaryImageDecoder.php | 1 + src/Interfaces/ColorInterface.php | 10 +++---- tests/Colors/Rgb/ColorTest.php | 6 ----- tests/Drivers/Gd/ImageTest.php | 27 +++++-------------- tests/TestCase.php | 3 ++- 7 files changed, 16 insertions(+), 41 deletions(-) diff --git a/src/Colors/Rgb/Color.php b/src/Colors/Rgb/Color.php index 1a5723c5..3ce1313b 100644 --- a/src/Colors/Rgb/Color.php +++ b/src/Colors/Rgb/Color.php @@ -76,11 +76,6 @@ class Color implements ColorInterface ); } - public function toRgb(): self - { - return $this; - } - public function convertTo(string|ColorspaceInterface $colorspace): ColorInterface { $colorspace = match (true) { diff --git a/src/Drivers/Gd/Traits/CanHandleColors.php b/src/Drivers/Gd/Traits/CanHandleColors.php index ecfa1dd4..938efdfc 100644 --- a/src/Drivers/Gd/Traits/CanHandleColors.php +++ b/src/Drivers/Gd/Traits/CanHandleColors.php @@ -3,6 +3,7 @@ namespace Intervention\Image\Drivers\Gd\Traits; use Intervention\Image\Colors\Rgb\Color; +use Intervention\Image\Colors\Rgb\Colorspace; use Intervention\Image\Interfaces\ColorInterface; trait CanHandleColors @@ -33,9 +34,9 @@ trait CanHandleColors * @param ColorInterface $color * @return int */ - public function colorToInteger(ColorInterface $color): int + public function colorToInteger(Color $color): int { - $color = $color->toRgb(); + $color = $color->convertTo(Colorspace::class); $r = $color->red()->value(); $g = $color->green()->value(); diff --git a/src/Drivers/Imagick/Decoders/BinaryImageDecoder.php b/src/Drivers/Imagick/Decoders/BinaryImageDecoder.php index ac4f1488..886f65eb 100644 --- a/src/Drivers/Imagick/Decoders/BinaryImageDecoder.php +++ b/src/Drivers/Imagick/Decoders/BinaryImageDecoder.php @@ -25,6 +25,7 @@ class BinaryImageDecoder extends AbstractDecoder implements DecoderInterface $imagick = new Imagick(); $imagick->readImageBlob($input); $imagick = $imagick->coalesceImages(); + // $imagick->transformImageColorspace(Imagick::COLORSPACE_RGB); $image = new Image($imagick); $image->setLoops($imagick->getImageIterations()); diff --git a/src/Interfaces/ColorInterface.php b/src/Interfaces/ColorInterface.php index 6ba4cafe..1594b857 100644 --- a/src/Interfaces/ColorInterface.php +++ b/src/Interfaces/ColorInterface.php @@ -2,16 +2,14 @@ namespace Intervention\Image\Interfaces; -use Intervention\Image\Colors\Rgb\Color; - interface ColorInterface { - public function toRgb(): Color; - public function toArray(): array; - public function toString(): string; - public function toHex(): string; public function __toString(): string; + public function toString(): string; + public function toArray(): array; + public function toHex(): string; public function channels(): array; + public function channel(string $classname): ColorChannelInterface; public function convertTo(string|ColorspaceInterface $colorspace): ColorInterface; public function isGreyscale(): bool; } diff --git a/tests/Colors/Rgb/ColorTest.php b/tests/Colors/Rgb/ColorTest.php index 131dcef7..bcbbe53a 100644 --- a/tests/Colors/Rgb/ColorTest.php +++ b/tests/Colors/Rgb/ColorTest.php @@ -73,10 +73,4 @@ class ColorTest extends TestCase $color = new Color(181, 55, 23); $this->assertEquals('rgb(181, 55, 23)', (string) $color); } - - public function testToRgb(): void - { - $color = new Color(181, 55, 23); - $this->assertInstanceOf(Color::class, $color->toRgb()); - } } diff --git a/tests/Drivers/Gd/ImageTest.php b/tests/Drivers/Gd/ImageTest.php index a878443b..cd4b7031 100644 --- a/tests/Drivers/Gd/ImageTest.php +++ b/tests/Drivers/Gd/ImageTest.php @@ -96,21 +96,15 @@ class ImageTest extends TestCase { $color = $this->image->pickColor(0, 0); $this->assertInstanceOf(Color::class, $color); - $this->assertEquals(255, $color->toRgb()->red()->value()); - $this->assertEquals(0, $color->toRgb()->green()->value()); - $this->assertEquals(0, $color->toRgb()->blue()->value()); + $this->assertEquals([255, 0, 0, 255], $color->toArray()); $color = $this->image->pickColor(0, 0, 1); $this->assertInstanceOf(Color::class, $color); - $this->assertEquals(0, $color->toRgb()->red()->value()); - $this->assertEquals(255, $color->toRgb()->green()->value()); - $this->assertEquals(0, $color->toRgb()->blue()->value()); + $this->assertEquals([0, 255, 0, 255], $color->toArray()); $color = $this->image->pickColor(0, 0, 2); $this->assertInstanceOf(Color::class, $color); - $this->assertEquals(0, $color->toRgb()->red()->value()); - $this->assertEquals(0, $color->toRgb()->green()->value()); - $this->assertEquals(255, $color->toRgb()->blue()->value()); + $this->assertEquals([0, 0, 255, 255], $color->toArray()); $color = $this->image->pickColor(0, 0, 3); $this->assertNull($color); @@ -121,17 +115,8 @@ class ImageTest extends TestCase $colors = $this->image->pickColors(0, 0); $this->assertInstanceOf(Collection::class, $colors); $this->assertCount(3, $colors); - - $this->assertEquals(255, $colors->get(0)->toRgb()->red()->value()); - $this->assertEquals(0, $colors->get(0)->toRgb()->green()->value()); - $this->assertEquals(0, $colors->get(0)->toRgb()->blue()->value()); - - $this->assertEquals(0, $colors->get(1)->toRgb()->red()->value()); - $this->assertEquals(255, $colors->get(1)->toRgb()->green()->value()); - $this->assertEquals(0, $colors->get(1)->toRgb()->blue()->value()); - - $this->assertEquals(0, $colors->get(2)->toRgb()->red()->value()); - $this->assertEquals(0, $colors->get(2)->toRgb()->green()->value()); - $this->assertEquals(255, $colors->get(2)->toRgb()->blue()->value()); + $this->assertEquals([255, 0, 0, 255], $colors->get(0)->toArray()); + $this->assertEquals([0, 255, 0, 255], $colors->get(1)->toArray()); + $this->assertEquals([0, 0, 255, 255], $colors->get(2)->toArray()); } } diff --git a/tests/TestCase.php b/tests/TestCase.php index cfd60927..f73c069c 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -3,6 +3,7 @@ namespace Intervention\Image\Tests; use Intervention\Image\Colors\Rgb\Color; +use Intervention\Image\Colors\Rgb\Colorspace; use Intervention\Image\Interfaces\ColorInterface; use Mockery\Adapter\Phpunit\MockeryTestCase; @@ -26,6 +27,6 @@ abstract class TestCase extends MockeryTestCase protected function assertTransparency(ColorInterface $color) { $this->assertInstanceOf(Color::class, $color); - $this->assertEquals(0, $color->toRgb()->alpha()->value()); + $this->assertEquals(0, $color->convertTo(Colorspace::class)->alpha()->value()); } } From 774e1b80a37906b579c8c82c592815638e44ab9c Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 21 Oct 2023 09:44:53 +0200 Subject: [PATCH 288/476] Fix bugs --- src/Drivers/Gd/Traits/CanHandleColors.php | 8 ++------ src/Drivers/Imagick/Font.php | 7 +++++-- src/Drivers/Imagick/Modifiers/PadModifier.php | 8 +++++--- src/Drivers/Imagick/Traits/CanHandleColors.php | 2 +- 4 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/Drivers/Gd/Traits/CanHandleColors.php b/src/Drivers/Gd/Traits/CanHandleColors.php index 938efdfc..799292df 100644 --- a/src/Drivers/Gd/Traits/CanHandleColors.php +++ b/src/Drivers/Gd/Traits/CanHandleColors.php @@ -3,8 +3,6 @@ namespace Intervention\Image\Drivers\Gd\Traits; use Intervention\Image\Colors\Rgb\Color; -use Intervention\Image\Colors\Rgb\Colorspace; -use Intervention\Image\Interfaces\ColorInterface; trait CanHandleColors { @@ -31,13 +29,11 @@ trait CanHandleColors /** * Transforms given color to the corresponding GD Library integer value * - * @param ColorInterface $color + * @param Color $color * @return int */ public function colorToInteger(Color $color): int { - $color = $color->convertTo(Colorspace::class); - $r = $color->red()->value(); $g = $color->green()->value(); $b = $color->blue()->value(); @@ -61,7 +57,7 @@ trait CanHandleColors * @param float|int $targetMax * @return float|int */ - private static function convertRange( + protected static function convertRange( float|int $input, float|int $min, float|int $max, diff --git a/src/Drivers/Imagick/Font.php b/src/Drivers/Imagick/Font.php index 2e8441bf..5e612c25 100644 --- a/src/Drivers/Imagick/Font.php +++ b/src/Drivers/Imagick/Font.php @@ -5,26 +5,29 @@ namespace Intervention\Image\Drivers\Imagick; use Imagick; use ImagickDraw; use Intervention\Image\Drivers\Abstract\AbstractFont; +use Intervention\Image\Drivers\Imagick\Traits\CanHandleColors; use Intervention\Image\Exceptions\FontException; use Intervention\Image\Geometry\Polygon; use Intervention\Image\Geometry\Rectangle; class Font extends AbstractFont { + use CanHandleColors; + public function toImagickDraw(): ImagickDraw { if (!$this->hasFilename()) { throw new FontException('No font file specified.'); } - $color = $this->failIfNotClass($this->getColor(), Color::class); + $color = $this->colorToPixel($this->getColor()); $draw = new ImagickDraw(); $draw->setStrokeAntialias(true); $draw->setTextAntialias(true); $draw->setFont($this->getFilename()); $draw->setFontSize($this->getSize()); - $draw->setFillColor($color->getPixel()); + $draw->setFillColor($color); $draw->setTextAlignment(Imagick::ALIGN_LEFT); return $draw; diff --git a/src/Drivers/Imagick/Modifiers/PadModifier.php b/src/Drivers/Imagick/Modifiers/PadModifier.php index 3b0d38e2..fbfcbd23 100644 --- a/src/Drivers/Imagick/Modifiers/PadModifier.php +++ b/src/Drivers/Imagick/Modifiers/PadModifier.php @@ -5,7 +5,8 @@ namespace Intervention\Image\Drivers\Imagick\Modifiers; use Imagick; use ImagickDraw; use Intervention\Image\Drivers\Abstract\Modifiers\AbstractPadModifier; -use Intervention\Image\Drivers\Imagick\Color; +use Intervention\Image\Colors\Rgb\Color; +use Intervention\Image\Drivers\Imagick\Traits\CanHandleColors; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; use Intervention\Image\Interfaces\SizeInterface; @@ -16,12 +17,13 @@ class PadModifier extends AbstractPadModifier implements ModifierInterface { use CanBuildNewImage; use CanHandleInput; + use CanHandleColors; public function apply(ImageInterface $image): ImageInterface { $resize = $this->getResizeSize($image); $crop = $this->getCropSize($image); - $background = $this->failIfNotClass($this->handleInput($this->background), Color::class); + $background = $this->handleInput($this->background); foreach ($image as $frame) { // resize current core @@ -59,7 +61,7 @@ class PadModifier extends AbstractPadModifier implements ModifierInterface // draw background color on canvas $draw = new ImagickDraw(); - $draw->setFillColor($background->getPixel()); + $draw->setFillColor($this->colorToPixel($background)); $draw->rectangle(0, 0, $canvas->getImageWidth(), $canvas->getImageHeight()); $canvas->drawImage($draw); diff --git a/src/Drivers/Imagick/Traits/CanHandleColors.php b/src/Drivers/Imagick/Traits/CanHandleColors.php index 547030c1..adb50c41 100644 --- a/src/Drivers/Imagick/Traits/CanHandleColors.php +++ b/src/Drivers/Imagick/Traits/CanHandleColors.php @@ -13,7 +13,7 @@ trait CanHandleColors /** * Transforms ImagickPixel to own color object * - * @param int $value + * @param ImagickPixel $pixel * @return ColorInterface */ public function colorFromPixel(ImagickPixel $pixel): ColorInterface From e8dec6126bff148b60a5fe9e0cac4defb84e87d1 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 21 Oct 2023 10:00:13 +0200 Subject: [PATCH 289/476] Remove unused class --- src/Drivers/Abstract/AbstractColor.php | 35 -------------------------- tests/ModifierStackTest.php | 7 +++--- 2 files changed, 4 insertions(+), 38 deletions(-) delete mode 100644 src/Drivers/Abstract/AbstractColor.php diff --git a/src/Drivers/Abstract/AbstractColor.php b/src/Drivers/Abstract/AbstractColor.php deleted file mode 100644 index fe6bdc0f..00000000 --- a/src/Drivers/Abstract/AbstractColor.php +++ /dev/null @@ -1,35 +0,0 @@ -red(), - $this->green(), - $this->blue() - ); - } - - /** - * Determine if color is greyscale - * - * @return boolean - */ - public function isGreyscale(): bool - { - return ($this->red() === $this->green()) && ($this->green() === $this->blue()); - } -} diff --git a/tests/ModifierStackTest.php b/tests/ModifierStackTest.php index afe99234..65274e69 100644 --- a/tests/ModifierStackTest.php +++ b/tests/ModifierStackTest.php @@ -4,6 +4,7 @@ namespace Intervention\Image\Tests; use Intervention\Image\Drivers\Gd\Modifiers\GreyscaleModifier; use Intervention\Image\Interfaces\ImageInterface; +use Intervention\Image\Interfaces\ModifierInterface; use Intervention\Image\ModifierStack; use Mockery; @@ -29,14 +30,14 @@ class ModifierStackTest extends TestCase { $image = Mockery::mock(ImageInterface::class); - $modifier1 = Mockery::mock(AbstractColor::class)->makePartial(); + $modifier1 = Mockery::mock(ModifierInterface::class)->makePartial(); $modifier1->shouldReceive('apply')->once()->with($image); - $modifier2 = Mockery::mock(AbstractColor::class)->makePartial(); + $modifier2 = Mockery::mock(ModifierInterface::class)->makePartial(); $modifier2->shouldReceive('apply')->once()->with($image); $stack = new ModifierStack([$modifier1, $modifier2]); $result = $stack->apply($image); - $this->assertInstanceOf(ImageInterface::class, $image); + $this->assertInstanceOf(ImageInterface::class, $result); } } From a62c22ff53a5902acc1a5ef5cce865f249c5f79c Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 21 Oct 2023 10:01:09 +0200 Subject: [PATCH 290/476] Change return types --- src/Colors/Rgb/Color.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Colors/Rgb/Color.php b/src/Colors/Rgb/Color.php index 3ce1313b..c05aaf5f 100644 --- a/src/Colors/Rgb/Color.php +++ b/src/Colors/Rgb/Color.php @@ -27,22 +27,22 @@ class Color implements ColorInterface ]; } - public function red(): Red + public function red(): ColorChannelInterface { return $this->channel(Red::class); } - public function green(): Green + public function green(): ColorChannelInterface { return $this->channel(Green::class); } - public function blue(): Blue + public function blue(): ColorChannelInterface { return $this->channel(Blue::class); } - public function alpha(): Alpha + public function alpha(): ColorChannelInterface { return $this->channel(Alpha::class); } From f7fd1f3c5d8c41656ba70774b8cfebfc92820cfd Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 21 Oct 2023 10:04:29 +0200 Subject: [PATCH 291/476] Change return types --- src/Drivers/Abstract/AbstractFont.php | 2 +- src/Interfaces/FontInterface.php | 2 +- src/Interfaces/ImageInterface.php | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Drivers/Abstract/AbstractFont.php b/src/Drivers/Abstract/AbstractFont.php index dd39739c..a9bc192c 100644 --- a/src/Drivers/Abstract/AbstractFont.php +++ b/src/Drivers/Abstract/AbstractFont.php @@ -68,7 +68,7 @@ abstract class AbstractFont implements FontInterface return $this; } - public function getColor(): ?ColorInterface + public function getColor(): ColorInterface { return $this->handleInput($this->color); } diff --git a/src/Interfaces/FontInterface.php b/src/Interfaces/FontInterface.php index 6ff95e44..efd1b7aa 100644 --- a/src/Interfaces/FontInterface.php +++ b/src/Interfaces/FontInterface.php @@ -8,7 +8,7 @@ use Intervention\Image\Interfaces\ColorInterface; interface FontInterface { public function color($color): self; - public function getColor(): ?ColorInterface; + public function getColor(): ColorInterface; public function size(float $size): self; public function getSize(): float; public function angle(float $angle): self; diff --git a/src/Interfaces/ImageInterface.php b/src/Interfaces/ImageInterface.php index 45b257db..cf3083ef 100644 --- a/src/Interfaces/ImageInterface.php +++ b/src/Interfaces/ImageInterface.php @@ -3,7 +3,6 @@ namespace Intervention\Image\Interfaces; use Countable; -use Intervention\Image\Collection; use Intervention\Image\EncodedImage; use Traversable; From 0e285f7a363e09190ccd4a4710741fa6ecf8436d Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 21 Oct 2023 10:10:38 +0200 Subject: [PATCH 292/476] Fix bug --- src/Drivers/Imagick/Modifiers/PixelateModifier.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Drivers/Imagick/Modifiers/PixelateModifier.php b/src/Drivers/Imagick/Modifiers/PixelateModifier.php index f59196f7..d10e017b 100644 --- a/src/Drivers/Imagick/Modifiers/PixelateModifier.php +++ b/src/Drivers/Imagick/Modifiers/PixelateModifier.php @@ -27,8 +27,8 @@ class PixelateModifier implements ModifierInterface $size = $frame->getSize(); $frame->getCore()->scaleImage( - max(1, ($size->getWidth() / $this->size)), - max(1, ($size->getHeight() / $this->size)) + round(max(1, ($size->getWidth() / $this->size))), + round(max(1, ($size->getHeight() / $this->size))) ); $frame->getCore()->scaleImage($size->getWidth(), $size->getHeight()); From 6f1e27b06f6c50172aa74c34815df23a6040edb9 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 21 Oct 2023 10:20:32 +0200 Subject: [PATCH 293/476] Check test result only on color values --- .../Imagick/Modifiers/PixelateModifierTest.php | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/tests/Drivers/Imagick/Modifiers/PixelateModifierTest.php b/tests/Drivers/Imagick/Modifiers/PixelateModifierTest.php index 9329b830..33425b5c 100644 --- a/tests/Drivers/Imagick/Modifiers/PixelateModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/PixelateModifierTest.php @@ -20,7 +20,15 @@ class PixelateModifierTest extends TestCase $this->assertEquals('00aef0', $image->pickColor(0, 0)->toHex()); $this->assertEquals('00aef0', $image->pickColor(14, 14)->toHex()); $image->modify(new PixelateModifier(10)); - $this->assertEquals('00aef0', $image->pickColor(0, 0)->toHex()); - $this->assertEquals('6bab8c', $image->pickColor(14, 14)->toHex()); + + list($r, $g, $b) = $image->pickColor(0, 0)->toArray(); + $this->assertEquals(0, $r); + $this->assertEquals(174, $g); + $this->assertEquals(240, $b); + + list($r, $g, $b) = $image->pickColor(14, 14)->toArray(); + $this->assertEquals(107, $r); + $this->assertEquals(171, $g); + $this->assertEquals(140, $b); } } From c236947be625a828a4299ae289cd144a04a7ff4f Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 21 Oct 2023 10:22:42 +0200 Subject: [PATCH 294/476] Update mockery version number --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index b2591f2e..808574d0 100644 --- a/composer.json +++ b/composer.json @@ -18,7 +18,7 @@ }, "require-dev": { "phpunit/phpunit": "^9", - "mockery/mockery": "^1.4", + "mockery/mockery": "^1.6", "phpstan/phpstan": "^1" }, "autoload": { From a3494a80ced976e519d057303c497de7dcce15b6 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 21 Oct 2023 10:23:32 +0200 Subject: [PATCH 295/476] Remove laravel keyword --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 808574d0..b0634405 100644 --- a/composer.json +++ b/composer.json @@ -2,7 +2,7 @@ "name": "intervention/image", "description": "PHP image manipulation", "homepage": "https://image.intervention.io/", - "keywords": ["image", "gd", "imagick", "laravel", "watermark", "thumbnail"], + "keywords": ["image", "gd", "imagick", "watermark", "thumbnail"], "license": "MIT", "authors": [ { From 09aa08904ce4c309790d0caf3efe9aa2041e5002 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 21 Oct 2023 10:24:14 +0200 Subject: [PATCH 296/476] Add keyword to composer.json --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index b0634405..e97407e9 100644 --- a/composer.json +++ b/composer.json @@ -2,7 +2,7 @@ "name": "intervention/image", "description": "PHP image manipulation", "homepage": "https://image.intervention.io/", - "keywords": ["image", "gd", "imagick", "watermark", "thumbnail"], + "keywords": ["image", "gd", "imagick", "watermark", "thumbnail", "resize"], "license": "MIT", "authors": [ { From 0bc8b7ce9b50de7dc52e9db6499d105cefc062cc Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 21 Oct 2023 10:31:36 +0200 Subject: [PATCH 297/476] Remove comment --- src/Drivers/Imagick/Decoders/BinaryImageDecoder.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Drivers/Imagick/Decoders/BinaryImageDecoder.php b/src/Drivers/Imagick/Decoders/BinaryImageDecoder.php index 886f65eb..ac4f1488 100644 --- a/src/Drivers/Imagick/Decoders/BinaryImageDecoder.php +++ b/src/Drivers/Imagick/Decoders/BinaryImageDecoder.php @@ -25,7 +25,6 @@ class BinaryImageDecoder extends AbstractDecoder implements DecoderInterface $imagick = new Imagick(); $imagick->readImageBlob($input); $imagick = $imagick->coalesceImages(); - // $imagick->transformImageColorspace(Imagick::COLORSPACE_RGB); $image = new Image($imagick); $image->setLoops($imagick->getImageIterations()); From f0e7abb56bd1b9f93a0e23c34c00e69ef8d7158c Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 21 Oct 2023 11:13:08 +0200 Subject: [PATCH 298/476] Image ImageManager instantiation signature --- src/Exceptions/ConfigurationException.php | 8 ++++++++ src/ImageManager.php | 13 ++++++++++--- tests/ImageManagerTest.php | 17 ++++++++++++----- 3 files changed, 30 insertions(+), 8 deletions(-) create mode 100644 src/Exceptions/ConfigurationException.php diff --git a/src/Exceptions/ConfigurationException.php b/src/Exceptions/ConfigurationException.php new file mode 100644 index 00000000..df77d83a --- /dev/null +++ b/src/Exceptions/ConfigurationException.php @@ -0,0 +1,8 @@ +driver); + return strtolower($this->options['driver']); } } diff --git a/tests/ImageManagerTest.php b/tests/ImageManagerTest.php index 1bbbe645..565eb5f7 100644 --- a/tests/ImageManagerTest.php +++ b/tests/ImageManagerTest.php @@ -2,6 +2,7 @@ namespace Intervention\Image\Tests; +use Intervention\Image\Exceptions\ConfigurationException; use Intervention\Image\ImageManager; use Intervention\Image\Interfaces\ImageInterface; @@ -12,14 +13,20 @@ class ImageManagerTest extends TestCase { public function testConstructor() { - $manager = new ImageManager('foo'); + $manager = new ImageManager(['driver' => 'gd']); $this->assertInstanceOf(ImageManager::class, $manager); + + $this->expectException(ConfigurationException::class); + $manager = new ImageManager([]); + + $this->expectException(ConfigurationException::class); + $manager = new ImageManager(['foo' => 'bar']); } /** @requires extension gd */ public function testCreateGd() { - $manager = new ImageManager('gd'); + $manager = new ImageManager(['driver' => 'gd']); $image = $manager->create(5, 4); $this->assertInstanceOf(ImageInterface::class, $image); } @@ -27,7 +34,7 @@ class ImageManagerTest extends TestCase /** @requires extension gd */ public function testReadGd() { - $manager = new ImageManager('gd'); + $manager = new ImageManager(['driver' => 'gd']); $image = $manager->read(__DIR__ . '/images/red.gif'); $this->assertInstanceOf(ImageInterface::class, $image); } @@ -35,7 +42,7 @@ class ImageManagerTest extends TestCase /** @requires extension imagick */ public function testCreateImagick() { - $manager = new ImageManager('imagick'); + $manager = new ImageManager(['driver' => 'imagick']); $image = $manager->create(5, 4); $this->assertInstanceOf(ImageInterface::class, $image); } @@ -43,7 +50,7 @@ class ImageManagerTest extends TestCase /** @requires extension imagick */ public function testReadImagick() { - $manager = new ImageManager('imagick'); + $manager = new ImageManager(['driver' => 'imagick']); $image = $manager->read(__DIR__ . '/images/red.gif'); $this->assertInstanceOf(ImageInterface::class, $image); } From 4b7234e0cf06f27abdf2d7ba2353d43c6e646055 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 21 Oct 2023 11:26:52 +0200 Subject: [PATCH 299/476] Set default driver configuration --- src/ImageManager.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ImageManager.php b/src/ImageManager.php index e6f3ff48..b73c3880 100644 --- a/src/ImageManager.php +++ b/src/ImageManager.php @@ -11,7 +11,7 @@ class ImageManager private static $required_options = ['driver']; - public function __construct(protected array $options) + public function __construct(protected array $options = ['driver' => 'gd']) { if (count(array_intersect(array_keys($options), self::$required_options)) != count(self::$required_options)) { throw new Exceptions\ConfigurationException( From fc94da24104e2c5bb51a813287ad73fc007f8374 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 21 Oct 2023 11:41:03 +0200 Subject: [PATCH 300/476] Refactor code --- src/Drivers/Gd/Modifiers/PlaceModifier.php | 12 +++--------- src/Drivers/Imagick/Modifiers/PlaceModifier.php | 12 +++--------- 2 files changed, 6 insertions(+), 18 deletions(-) diff --git a/src/Drivers/Gd/Modifiers/PlaceModifier.php b/src/Drivers/Gd/Modifiers/PlaceModifier.php index 1c35c435..a8a9b56a 100644 --- a/src/Drivers/Gd/Modifiers/PlaceModifier.php +++ b/src/Drivers/Gd/Modifiers/PlaceModifier.php @@ -2,15 +2,14 @@ namespace Intervention\Image\Drivers\Gd\Modifiers; -use Intervention\Image\Drivers\Gd\Image; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; use Intervention\Image\Interfaces\PointInterface; -use Intervention\Image\Traits\CanResolveDriverClass; +use Intervention\Image\Traits\CanHandleInput; class PlaceModifier implements ModifierInterface { - use CanResolveDriverClass; + use CanHandleInput; /** * Create new modifier @@ -27,7 +26,7 @@ class PlaceModifier implements ModifierInterface public function apply(ImageInterface $image): ImageInterface { - $watermark = $this->decodeWatermark(); + $watermark = $this->handleInput($this->element); $position = $this->getPosition($image, $watermark); foreach ($image as $frame) { @@ -47,11 +46,6 @@ class PlaceModifier implements ModifierInterface return $image; } - protected function decodeWatermark(): Image - { - return $this->resolveDriverClass('InputHandler')->handle($this->element); - } - protected function getPosition(ImageInterface $image, ImageInterface $watermark): PointInterface { $image_size = $image->getSize()->movePivot($this->position, $this->offset_x, $this->offset_y); diff --git a/src/Drivers/Imagick/Modifiers/PlaceModifier.php b/src/Drivers/Imagick/Modifiers/PlaceModifier.php index d6181c42..775e7585 100644 --- a/src/Drivers/Imagick/Modifiers/PlaceModifier.php +++ b/src/Drivers/Imagick/Modifiers/PlaceModifier.php @@ -4,15 +4,14 @@ namespace Intervention\Image\Drivers\Imagick\Modifiers; use Imagick; use Intervention\Image\Drivers\Imagick\Image; -use Intervention\Image\Geometry\Point; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; use Intervention\Image\Interfaces\PointInterface; -use Intervention\Image\Traits\CanResolveDriverClass; +use Intervention\Image\Traits\CanHandleInput; class PlaceModifier implements ModifierInterface { - use CanResolveDriverClass; + use CanHandleInput; public function __construct( protected $element, @@ -25,7 +24,7 @@ class PlaceModifier implements ModifierInterface public function apply(ImageInterface $image): ImageInterface { - $watermark = $this->decodeWatermark(); + $watermark = $this->handleInput($this->element); $position = $this->getPosition($image, $watermark); foreach ($image as $frame) { @@ -40,11 +39,6 @@ class PlaceModifier implements ModifierInterface return $image; } - protected function decodeWatermark(): Image - { - return $this->resolveDriverClass('InputHandler')->handle($this->element); - } - protected function getPosition(ImageInterface $image, Image $watermark): PointInterface { $image_size = $image->getSize()->movePivot($this->position, $this->offset_x, $this->offset_y); From 625063ca979a906ce15076ef136a62026f08e13f Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 21 Oct 2023 11:43:09 +0200 Subject: [PATCH 301/476] Edit readme --- readme.md | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/readme.md b/readme.md index 7e3fbe72..598b4784 100644 --- a/readme.md +++ b/readme.md @@ -5,7 +5,10 @@ [![Build Status](https://github.com/Intervention/image/actions/workflows/run-tests.yml/badge.svg)](https://github.com/Intervention/image/actions) [![Monthly Downloads](https://img.shields.io/packagist/dm/intervention/image.svg)](https://packagist.org/packages/intervention/image/stats) -Intervention Image is a **image handling and manipulation library written in PHP** providing an easier and expressive way to create, edit, and compose images. GD library or Imagick can be selected as the base layer for all operations. +Intervention Image is a **image handling and manipulation library written in +PHP** providing an easier and expressive way to create, edit, and compose +images. GD library or Imagick can be selected as the base layer for all +operations. - Simple interface for common tasks - Interchangable driver architecture @@ -17,7 +20,7 @@ Intervention Image is a **image handling and manipulation library written in PHP ```php // create image manager with desired driver -$manager = new ImageManager('gd') +$manager = new ImageManager(['driver' => 'gd']); // open an image file $image = $manager->read('images/example.gif'); @@ -52,11 +55,15 @@ composer require intervention/image ## Getting started -Learn the [basics](https://image.intervention.io/v3/basics/instantiation/) on how to use Intervention Image and more with the [official documentation](https://image.intervention.io/v3/). +Learn the [basics](https://image.intervention.io/v3/basics/instantiation/) on +how to use Intervention Image and more with the [official +documentation](https://image.intervention.io/v3/). ## Development & Testing -With this package comes a Docker image to build a test suite and analysis container. To build this container you have to have Docker installed on your system. You can run all tests with this command. +With this package comes a Docker image to build a test suite and analysis +container. To build this container you have to have Docker installed on your +system. You can run all tests with this command. ```bash docker-compose run --rm --build tests From eae10d0e72c840cf11aa2f4bf8c33b39eca84b3b Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 21 Oct 2023 12:00:49 +0200 Subject: [PATCH 302/476] Update Github workflow --- .github/workflows/run-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 43cbf28a..dc5c5722 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -77,7 +77,7 @@ jobs: - name: Get composer cache directory id: composer-cache - run: echo "::set-output name=dir::$(composer config cache-files-dir)" + run: echo "dir::$(composer config cache-files-dir)" >> $GITHUB_OUTPUT - name: Cache composer dependencies uses: actions/cache@v2 From 19efa81afa6339157b2e18e35815a1c082648466 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 21 Oct 2023 12:03:51 +0200 Subject: [PATCH 303/476] Fix Github workflow --- .github/workflows/run-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index dc5c5722..30fd3471 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -77,7 +77,7 @@ jobs: - name: Get composer cache directory id: composer-cache - run: echo "dir::$(composer config cache-files-dir)" >> $GITHUB_OUTPUT + run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT - name: Cache composer dependencies uses: actions/cache@v2 From a1f75482e13eae8a2cab240e33eae6f5c7694cce Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 21 Oct 2023 14:49:22 +0200 Subject: [PATCH 304/476] Add type hints --- src/Drivers/Abstract/AbstractInputHandler.php | 2 +- src/Drivers/Gd/InputHandler.php | 2 +- src/Drivers/Imagick/InputHandler.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Drivers/Abstract/AbstractInputHandler.php b/src/Drivers/Abstract/AbstractInputHandler.php index d0f25c9b..92ac0b23 100644 --- a/src/Drivers/Abstract/AbstractInputHandler.php +++ b/src/Drivers/Abstract/AbstractInputHandler.php @@ -12,7 +12,7 @@ abstract class AbstractInputHandler /** * Array of decoders which will be stacked into to the input handler chain */ - protected $decoders = []; + protected array $decoders = []; /** * Stack the decoder array into a nested decoder object diff --git a/src/Drivers/Gd/InputHandler.php b/src/Drivers/Gd/InputHandler.php index 85d69a74..2cd3da3b 100644 --- a/src/Drivers/Gd/InputHandler.php +++ b/src/Drivers/Gd/InputHandler.php @@ -17,7 +17,7 @@ use Intervention\Image\Drivers\Gd\Decoders\Base64ImageDecoder; class InputHandler extends AbstractInputHandler { - protected $decoders = [ + protected array $decoders = [ ImageObjectDecoder::class, ColorObjectDecoder::class, HexColorDecoder::class, diff --git a/src/Drivers/Imagick/InputHandler.php b/src/Drivers/Imagick/InputHandler.php index 3d27678a..c41649d0 100644 --- a/src/Drivers/Imagick/InputHandler.php +++ b/src/Drivers/Imagick/InputHandler.php @@ -17,7 +17,7 @@ use Intervention\Image\Drivers\Imagick\Decoders\Base64ImageDecoder; class InputHandler extends AbstractInputHandler { - protected $decoders = [ + protected array $decoders = [ ImageObjectDecoder::class, ColorObjectDecoder::class, HexColorDecoder::class, From 3401fefa4f04ca8e7db87a31d29ffb9bd615b819 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 21 Oct 2023 15:15:56 +0200 Subject: [PATCH 305/476] Add static color factory method --- src/Colors/Rgb/Color.php | 85 +++++++++++++++++++ src/Drivers/Abstract/AbstractInputHandler.php | 10 +++ src/Interfaces/ColorInterface.php | 57 +++++++++++++ tests/Colors/Rgb/ColorTest.php | 11 +++ 4 files changed, 163 insertions(+) diff --git a/src/Colors/Rgb/Color.php b/src/Colors/Rgb/Color.php index c05aaf5f..d7117565 100644 --- a/src/Colors/Rgb/Color.php +++ b/src/Colors/Rgb/Color.php @@ -7,6 +7,7 @@ use Intervention\Image\Colors\Rgb\Channels\Green; use Intervention\Image\Colors\Rgb\Channels\Red; use Intervention\Image\Colors\Rgb\Channels\Alpha; use Intervention\Image\Colors\Traits\CanHandleChannels; +use Intervention\Image\Drivers\Abstract\AbstractInputHandler; use Intervention\Image\Interfaces\ColorChannelInterface; use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\ColorspaceInterface; @@ -15,8 +16,20 @@ class Color implements ColorInterface { use CanHandleChannels; + /** + * Color channels + */ protected array $channels; + /** + * Create new instance + * + * @param int $r + * @param int $g + * @param int $b + * @param int $a + * @return ColorInterface + */ public function __construct(int $r, int $g, int $b, int $a = 255) { $this->channels = [ @@ -27,26 +40,68 @@ class Color implements ColorInterface ]; } + /** + * {@inheritdoc} + * + * @see ColorInterface::create() + */ + public static function create(mixed $input): ColorInterface + { + return (new class ([ + Decoders\HexColorDecoder::class, + Decoders\StringColorDecoder::class, + Decoders\TransparentColorDecoder::class, + Decoders\HtmlColornameDecoder::class, + ]) extends AbstractInputHandler + { + })->handle($input); + } + + /** + * Return the RGB red color channel + * + * @return ColorChannelInterface + */ public function red(): ColorChannelInterface { return $this->channel(Red::class); } + /** + * Return the RGB green color channel + * + * @return ColorChannelInterface + */ public function green(): ColorChannelInterface { return $this->channel(Green::class); } + /** + * Return the RGB blue color channel + * + * @return ColorChannelInterface + */ public function blue(): ColorChannelInterface { return $this->channel(Blue::class); } + /** + * Return the colors alpha channel + * + * @return ColorChannelInterface + */ public function alpha(): ColorChannelInterface { return $this->channel(Alpha::class); } + /** + * {@inheritdoc} + * + * @see ColorInterface::toArray() + */ public function toArray(): array { return array_map(function (ColorChannelInterface $channel) { @@ -54,6 +109,11 @@ class Color implements ColorInterface }, $this->channels()); } + /** + * {@inheritdoc} + * + * @see ColorInterface::toHex() + */ public function toHex(string $prefix = ''): string { if ($this->isFullyOpaque()) { @@ -76,6 +136,11 @@ class Color implements ColorInterface ); } + /** + * {@inheritdoc} + * + * @see ColorInterface::convertTo() + */ public function convertTo(string|ColorspaceInterface $colorspace): ColorInterface { $colorspace = match (true) { @@ -86,11 +151,21 @@ class Color implements ColorInterface return $colorspace->convertColor($this); } + /** + * Determine if the current color is fully opaque + * + * @return bool + */ public function isFullyOpaque(): bool { return $this->alpha()->value() === 255; } + /** + * {@inheritdoc} + * + * @see ColorInterface::toString() + */ public function toString(): string { if ($this->isFullyOpaque()) { @@ -111,6 +186,11 @@ class Color implements ColorInterface ); } + /** + * {@inheritdoc} + * + * @see ColorInterface::isGreyscale() + */ public function isGreyscale(): bool { $values = [$this->red()->value(), $this->green()->value(), $this->blue()->value()]; @@ -118,6 +198,11 @@ class Color implements ColorInterface return count(array_unique($values, SORT_REGULAR)) === 1; } + /** + * {@inheritdoc} + * + * @see ColorInterface::__toString() + */ public function __toString(): string { return $this->toString(); diff --git a/src/Drivers/Abstract/AbstractInputHandler.php b/src/Drivers/Abstract/AbstractInputHandler.php index 92ac0b23..faf90ad5 100644 --- a/src/Drivers/Abstract/AbstractInputHandler.php +++ b/src/Drivers/Abstract/AbstractInputHandler.php @@ -14,6 +14,16 @@ abstract class AbstractInputHandler */ protected array $decoders = []; + /** + * Create new instance + * + * @param array $decoders + */ + public function __construct(array $decoders = []) + { + $this->decoders = count($decoders) ? $decoders : $this->decoders; + } + /** * Stack the decoder array into a nested decoder object * diff --git a/src/Interfaces/ColorInterface.php b/src/Interfaces/ColorInterface.php index 1594b857..5d40eef2 100644 --- a/src/Interfaces/ColorInterface.php +++ b/src/Interfaces/ColorInterface.php @@ -4,12 +4,69 @@ namespace Intervention\Image\Interfaces; interface ColorInterface { + /** + * Static color factory method that passed input to color decoding input handler + * + * @param mixed $input + * @return ColorInterface + * @throws \Intervention\Image\Exceptions\DecoderException + */ + public static function create(mixed $input): ColorInterface; + + /** + * Cast color object to string + * + * @return string + */ public function __toString(): string; + + /** + * Cast color object to string + * + * @return string + */ public function toString(): string; + + /** + * Cast color object to array + * + * @return array + */ public function toArray(): array; + + /** + * Cast color object to hex encoded web color + * + * @return string + */ public function toHex(): string; + + /** + * Return array of all color channels + * + * @return array + */ public function channels(): array; + + /** + * Retrieve the color channel by its classname + * + * @param string $classname + * @return ColorChannelInterface + */ public function channel(string $classname): ColorChannelInterface; + + /** + * Convert color to given colorspace + * + * @return ColorInterface + */ public function convertTo(string|ColorspaceInterface $colorspace): ColorInterface; + + /** + * Determine if the current color is gray + * + * @return bool + */ public function isGreyscale(): bool; } diff --git a/tests/Colors/Rgb/ColorTest.php b/tests/Colors/Rgb/ColorTest.php index bcbbe53a..b4020c3c 100644 --- a/tests/Colors/Rgb/ColorTest.php +++ b/tests/Colors/Rgb/ColorTest.php @@ -23,6 +23,17 @@ class ColorTest extends TestCase $this->assertInstanceOf(Color::class, $color); } + public function testCreate(): void + { + $color = Color::create('ccc'); + $this->assertInstanceOf(Color::class, $color); + $this->assertEquals([204, 204, 204, 255], $color->toArray()); + + $color = Color::create('rgba(10, 20, 30, .2)'); + $this->assertInstanceOf(Color::class, $color); + $this->assertEquals([10, 20, 30, 51], $color->toArray()); + } + public function testChannels(): void { $color = new Color(10, 20, 30); From f73d1a25919352325ad1d724f2f9202912ec9a89 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 21 Oct 2023 15:47:08 +0200 Subject: [PATCH 306/476] Add doc blocks --- src/Colors/Rgb/Channels/Red.php | 50 ++++++++++++++++++++++++ src/Interfaces/ColorChannelInterface.php | 42 ++++++++++++++++++++ 2 files changed, 92 insertions(+) diff --git a/src/Colors/Rgb/Channels/Red.php b/src/Colors/Rgb/Channels/Red.php index da8bf81b..510d628c 100644 --- a/src/Colors/Rgb/Channels/Red.php +++ b/src/Colors/Rgb/Channels/Red.php @@ -9,31 +9,71 @@ class Red implements ColorChannelInterface { protected int $value; + /** + * Create and validate new instance + * + * @param int $value + */ public function __construct(int $value) { $this->value = $this->validate($value); } + /** + * Alias of value() + * + * @return int + */ + public function toInt(): int + { + return $this->value; + } + + /** + * {@inheritdoc} + * + * @see ColorChannelInterface::value() + */ public function value(): int { return $this->value; } + /** + * {@inheritdoc} + * + * @see ColorChannelInterface::normalize() + */ public function normalize($precision = 32): float { return round($this->value() / $this->max(), $precision); } + /** + * {@inheritdoc} + * + * @see ColorChannelInterface::min() + */ public function min(): int { return 0; } + /** + * {@inheritdoc} + * + * @see ColorChannelInterface::max() + */ public function max(): int { return 255; } + /** + * {@inheritdoc} + * + * @see ColorChannelInterface::validate() + */ public function validate(mixed $value): mixed { if ($value < $this->min() || $value > $this->max()) { @@ -43,11 +83,21 @@ class Red implements ColorChannelInterface return $value; } + /** + * {@inheritdoc} + * + * @see ColorChannelInterface::toString() + */ public function toString(): string { return (string) $this->value(); } + /** + * {@inheritdoc} + * + * @see ColorChannelInterface::__toString() + */ public function __toString(): string { return $this->toString(); diff --git a/src/Interfaces/ColorChannelInterface.php b/src/Interfaces/ColorChannelInterface.php index 6c3c7ce8..7c061ee1 100644 --- a/src/Interfaces/ColorChannelInterface.php +++ b/src/Interfaces/ColorChannelInterface.php @@ -4,11 +4,53 @@ namespace Intervention\Image\Interfaces; interface ColorChannelInterface { + /** + * Return color channels integer value + * + * @return int + */ public function value(): int; + + /** + * Return the channels value normalized to a float value form 0 to 1 by its range + * + * @return float + */ public function normalize(int $precision = 32): float; + + /** + * Throw exception if the given value is not applicable for channel + * otherwise the value is returned unchanged. + * + * @return mixed + */ public function validate(mixed $value): mixed; + + /** + * Return the the minimal possible value of the color channel + * + * @return int + */ public function min(): int; + + /* + * Return the the maximal possible value of the color channel + * + * @return int + */ public function max(): int; + + /** + * Cast color channel's value to string + * + * @return string + */ public function toString(): string; + + /** + * Cast color channel's value to string + * + * @return string + */ public function __toString(): string; } From 00d53e24df4ed3f22e89379922bdb0bc7adae5a1 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 21 Oct 2023 15:56:18 +0200 Subject: [PATCH 307/476] Save all images in SRGB color space with Imagick driver --- src/Drivers/Imagick/Encoders/JpegEncoder.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Drivers/Imagick/Encoders/JpegEncoder.php b/src/Drivers/Imagick/Encoders/JpegEncoder.php index c6191704..7921e46f 100644 --- a/src/Drivers/Imagick/Encoders/JpegEncoder.php +++ b/src/Drivers/Imagick/Encoders/JpegEncoder.php @@ -30,6 +30,10 @@ class JpegEncoder extends AbstractEncoder implements EncoderInterface $imagick->setCompressionQuality($this->quality); $imagick->setImageCompressionQuality($this->quality); + if ($imagick->getImageColorspace() != Imagick::COLORSPACE_SRGB) { + $imagick->transformImageColorspace(Imagick::COLORSPACE_SRGB); + } + return new EncodedImage($imagick->getImagesBlob(), 'image/jpeg'); } } From fb161a9ad33be4b34a6d34c6cded2a7c6b655cfa Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 21 Oct 2023 17:40:03 +0200 Subject: [PATCH 308/476] Add missing offset calculations --- src/Geometry/Rectangle.php | 8 ++++---- tests/Geometry/RectangleTest.php | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Geometry/Rectangle.php b/src/Geometry/Rectangle.php index b244a60c..06dfaec4 100644 --- a/src/Geometry/Rectangle.php +++ b/src/Geometry/Rectangle.php @@ -91,7 +91,7 @@ class Rectangle extends Polygon implements SizeInterface, DrawableInterface case 'top-middle': case 'center-top': case 'middle-top': - $x = intval($this->getWidth() / 2); + $x = intval($this->getWidth() / 2) + $offset_x; $y = 0 + $offset_y; break; @@ -107,7 +107,7 @@ class Rectangle extends Polygon implements SizeInterface, DrawableInterface case 'center-left': case 'middle-left': $x = 0 + $offset_x; - $y = intval($this->getHeight() / 2); + $y = intval($this->getHeight() / 2) + $offset_y; break; case 'right': @@ -116,7 +116,7 @@ class Rectangle extends Polygon implements SizeInterface, DrawableInterface case 'center-right': case 'middle-right': $x = $this->getWidth() - $offset_x; - $y = intval($this->getHeight() / 2); + $y = intval($this->getHeight() / 2) + $offset_y; break; case 'bottom-left': @@ -130,7 +130,7 @@ class Rectangle extends Polygon implements SizeInterface, DrawableInterface case 'bottom-middle': case 'center-bottom': case 'middle-bottom': - $x = intval($this->getWidth() / 2); + $x = intval($this->getWidth() / 2) + $offset_x; $y = $this->getHeight() - $offset_y; break; diff --git a/tests/Geometry/RectangleTest.php b/tests/Geometry/RectangleTest.php index 7eebd996..4ad785c8 100644 --- a/tests/Geometry/RectangleTest.php +++ b/tests/Geometry/RectangleTest.php @@ -136,7 +136,7 @@ class RectangleTest extends TestCase $this->assertEquals(3, $box->getPivot()->getY()); $box->movePivot('top', 3, 3); - $this->assertEquals(320, $box->getPivot()->getX()); + $this->assertEquals(323, $box->getPivot()->getX()); $this->assertEquals(3, $box->getPivot()->getY()); $box->movePivot('top-right', 3, 3); @@ -145,7 +145,7 @@ class RectangleTest extends TestCase $box->movePivot('left', 3, 3); $this->assertEquals(3, $box->getPivot()->getX()); - $this->assertEquals(240, $box->getPivot()->getY()); + $this->assertEquals(243, $box->getPivot()->getY()); $box->movePivot('center', 3, 3); $this->assertEquals(323, $box->getPivot()->getX()); @@ -153,14 +153,14 @@ class RectangleTest extends TestCase $box->movePivot('right', 3, 3); $this->assertEquals(637, $box->getPivot()->getX()); - $this->assertEquals(240, $box->getPivot()->getY()); + $this->assertEquals(243, $box->getPivot()->getY()); $box->movePivot('bottom-left', 3, 3); $this->assertEquals(3, $box->getPivot()->getX()); $this->assertEquals(477, $box->getPivot()->getY()); $box->movePivot('bottom', 3, 3); - $this->assertEquals(320, $box->getPivot()->getX()); + $this->assertEquals(323, $box->getPivot()->getX()); $this->assertEquals(477, $box->getPivot()->getY()); $result = $box->movePivot('bottom-right', 3, 3); From 019c333b220e02565bf4084b9a7b120e9f9c1141 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 21 Oct 2023 17:47:35 +0200 Subject: [PATCH 309/476] Refactor & add docblocks --- src/Drivers/Gd/ImageFactory.php | 12 +++++++++++- src/Drivers/Imagick/ImageFactory.php | 12 +++++++++++- src/Interfaces/DecoderInterface.php | 6 ++++++ src/Interfaces/EncoderInterface.php | 6 ++++++ src/Interfaces/FactoryInterface.php | 16 +++++++++++++++- src/Interfaces/ModifierInterface.php | 6 ++++++ src/Interfaces/PointInterface.php | 11 +++++++++++ tests/Drivers/Gd/ImageFactoryTest.php | 8 -------- tests/Drivers/Imagick/ImageFactoryTest.php | 2 +- 9 files changed, 67 insertions(+), 12 deletions(-) diff --git a/src/Drivers/Gd/ImageFactory.php b/src/Drivers/Gd/ImageFactory.php index 1d92a368..f6de784d 100644 --- a/src/Drivers/Gd/ImageFactory.php +++ b/src/Drivers/Gd/ImageFactory.php @@ -13,6 +13,11 @@ class ImageFactory implements FactoryInterface { use CanHandleInput; + /** + * {@inheritdoc} + * + * @see FactoryInterface::newImage() + */ public function newImage(int $width, int $height): ImageInterface { return new Image( @@ -22,6 +27,11 @@ class ImageFactory implements FactoryInterface ); } + /** + * {@inheritdoc} + * + * @see FactoryInterface::newAnimation() + */ public function newAnimation(callable $callback): ImageInterface { $frames = new Collection(); @@ -50,7 +60,7 @@ class ImageFactory implements FactoryInterface return new Image($frames); } - public function newCore(int $width, int $height) + protected function newCore(int $width, int $height) { $core = imagecreatetruecolor($width, $height); $color = imagecolorallocatealpha($core, 0, 0, 0, 127); diff --git a/src/Drivers/Imagick/ImageFactory.php b/src/Drivers/Imagick/ImageFactory.php index 999965d9..12e5abd1 100644 --- a/src/Drivers/Imagick/ImageFactory.php +++ b/src/Drivers/Imagick/ImageFactory.php @@ -15,11 +15,21 @@ class ImageFactory implements FactoryInterface use CanHandleInput; use CanCheckType; + /** + * {@inheritdoc} + * + * @see FactoryInterface::newImage() + */ public function newImage(int $width, int $height): ImageInterface { return new Image($this->newCore($width, $height)); } + /** + * {@inheritdoc} + * + * @see FactoryInterface::newAnimation() + */ public function newAnimation(callable $callback): ImageInterface { $imagick = new Imagick(); @@ -51,7 +61,7 @@ class ImageFactory implements FactoryInterface return new Image($animation->imagick); } - public function newCore(int $width, int $height) + protected function newCore(int $width, int $height) { $imagick = new Imagick(); $imagick->newImage($width, $height, new ImagickPixel('rgba(0, 0, 0, 0)'), 'png'); diff --git a/src/Interfaces/DecoderInterface.php b/src/Interfaces/DecoderInterface.php index 04ea2660..3ec11957 100644 --- a/src/Interfaces/DecoderInterface.php +++ b/src/Interfaces/DecoderInterface.php @@ -4,5 +4,11 @@ namespace Intervention\Image\Interfaces; interface DecoderInterface { + /** + * Decode given input either to color or image + * + * @param mixed $input + * @return ImageInterface|ColorInterface + */ public function decode($input): ImageInterface|ColorInterface; } diff --git a/src/Interfaces/EncoderInterface.php b/src/Interfaces/EncoderInterface.php index c2d9c41c..adb149a0 100644 --- a/src/Interfaces/EncoderInterface.php +++ b/src/Interfaces/EncoderInterface.php @@ -6,5 +6,11 @@ use Intervention\Image\EncodedImage; interface EncoderInterface { + /** + * Encode given image + * + * @param ImageInterface $image + * @return EncodedImage + */ public function encode(ImageInterface $image): EncodedImage; } diff --git a/src/Interfaces/FactoryInterface.php b/src/Interfaces/FactoryInterface.php index 1f7e9f08..8e948a0c 100644 --- a/src/Interfaces/FactoryInterface.php +++ b/src/Interfaces/FactoryInterface.php @@ -4,6 +4,20 @@ namespace Intervention\Image\Interfaces; interface FactoryInterface { + /** + * Create new image in the given size + * + * @param int $width + * @param int $height + * @return ImageInterface + */ public function newImage(int $width, int $height): ImageInterface; - public function newCore(int $width, int $height); + + /** + * Create new animated image + * + * @param callable $callback + * @return ImageInterface + */ + public function newAnimation(callable $callback): ImageInterface; } diff --git a/src/Interfaces/ModifierInterface.php b/src/Interfaces/ModifierInterface.php index ff6b12a4..8786ae66 100644 --- a/src/Interfaces/ModifierInterface.php +++ b/src/Interfaces/ModifierInterface.php @@ -4,5 +4,11 @@ namespace Intervention\Image\Interfaces; interface ModifierInterface { + /** + * Apply modifications of the current modifier to the given image + * + * @param ImageInterface $image + * @return ImageInterface + */ public function apply(ImageInterface $image): ImageInterface; } diff --git a/src/Interfaces/PointInterface.php b/src/Interfaces/PointInterface.php index 858e2925..56934ce2 100644 --- a/src/Interfaces/PointInterface.php +++ b/src/Interfaces/PointInterface.php @@ -4,6 +4,17 @@ namespace Intervention\Image\Interfaces; interface PointInterface { + /** + * Return x position + * + * @return int + */ public function getX(): int; + + /** + * Return y position + * + * @return int + */ public function getY(): int; } diff --git a/tests/Drivers/Gd/ImageFactoryTest.php b/tests/Drivers/Gd/ImageFactoryTest.php index 38060e45..797be8ad 100644 --- a/tests/Drivers/Gd/ImageFactoryTest.php +++ b/tests/Drivers/Gd/ImageFactoryTest.php @@ -2,7 +2,6 @@ namespace Intervention\Image\Tests\Drivers\Gd; -use GdImage; use Intervention\Image\Drivers\Gd\Image; use Intervention\Image\Drivers\Gd\ImageFactory; use Intervention\Image\Tests\TestCase; @@ -30,11 +29,4 @@ class ImageFactoryTest extends TestCase $this->assertInstanceOf(Image::class, $image); $this->assertEquals(2, $image->count()); } - - public function testNewCore(): void - { - $factory = new ImageFactory(); - $core = $factory->newCore(3, 2); - $this->assertInstanceOf(GdImage::class, $core); - } } diff --git a/tests/Drivers/Imagick/ImageFactoryTest.php b/tests/Drivers/Imagick/ImageFactoryTest.php index 635139e5..feb8e131 100644 --- a/tests/Drivers/Imagick/ImageFactoryTest.php +++ b/tests/Drivers/Imagick/ImageFactoryTest.php @@ -31,7 +31,7 @@ class ImageFactoryTest extends TestCase $this->assertEquals(2, $image->count()); } - public function testNewCore(): void + protected function testNewCore(): void { $factory = new ImageFactory(); $core = $factory->newCore(3, 2); From 2acb4c547f2c6c0ab9f9710c30e692839f70cb7c Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 21 Oct 2023 17:56:23 +0200 Subject: [PATCH 310/476] Change method signature --- src/Drivers/Gd/ImageFactory.php | 2 +- src/Drivers/Imagick/ImageFactory.php | 2 +- src/Interfaces/FactoryInterface.php | 8 ++++++++ tests/Drivers/Gd/ImageFactoryTest.php | 8 ++++++++ 4 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/Drivers/Gd/ImageFactory.php b/src/Drivers/Gd/ImageFactory.php index f6de784d..134e6a81 100644 --- a/src/Drivers/Gd/ImageFactory.php +++ b/src/Drivers/Gd/ImageFactory.php @@ -60,7 +60,7 @@ class ImageFactory implements FactoryInterface return new Image($frames); } - protected function newCore(int $width, int $height) + public function newCore(int $width, int $height) { $core = imagecreatetruecolor($width, $height); $color = imagecolorallocatealpha($core, 0, 0, 0, 127); diff --git a/src/Drivers/Imagick/ImageFactory.php b/src/Drivers/Imagick/ImageFactory.php index 12e5abd1..7fd67e06 100644 --- a/src/Drivers/Imagick/ImageFactory.php +++ b/src/Drivers/Imagick/ImageFactory.php @@ -61,7 +61,7 @@ class ImageFactory implements FactoryInterface return new Image($animation->imagick); } - protected function newCore(int $width, int $height) + public function newCore(int $width, int $height) { $imagick = new Imagick(); $imagick->newImage($width, $height, new ImagickPixel('rgba(0, 0, 0, 0)'), 'png'); diff --git a/src/Interfaces/FactoryInterface.php b/src/Interfaces/FactoryInterface.php index 8e948a0c..8a491bb7 100644 --- a/src/Interfaces/FactoryInterface.php +++ b/src/Interfaces/FactoryInterface.php @@ -20,4 +20,12 @@ interface FactoryInterface * @return ImageInterface */ public function newAnimation(callable $callback): ImageInterface; + + /** + * Create new driver specific core image object + * + * @param int $width + * @param int $height + */ + public function newCore(int $width, int $height); } diff --git a/tests/Drivers/Gd/ImageFactoryTest.php b/tests/Drivers/Gd/ImageFactoryTest.php index 797be8ad..38060e45 100644 --- a/tests/Drivers/Gd/ImageFactoryTest.php +++ b/tests/Drivers/Gd/ImageFactoryTest.php @@ -2,6 +2,7 @@ namespace Intervention\Image\Tests\Drivers\Gd; +use GdImage; use Intervention\Image\Drivers\Gd\Image; use Intervention\Image\Drivers\Gd\ImageFactory; use Intervention\Image\Tests\TestCase; @@ -29,4 +30,11 @@ class ImageFactoryTest extends TestCase $this->assertInstanceOf(Image::class, $image); $this->assertEquals(2, $image->count()); } + + public function testNewCore(): void + { + $factory = new ImageFactory(); + $core = $factory->newCore(3, 2); + $this->assertInstanceOf(GdImage::class, $core); + } } From f16b56103a6f6e04e75b32547d0e1f827b22d2e5 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 22 Oct 2023 09:12:15 +0200 Subject: [PATCH 311/476] Revert "Remove Cmyk Colors for now" This reverts commit f24a33705815b7f48351d4a157795cb064d8747b. --- src/Colors/Cmyk/Channels/Cyan.php | 55 +++++++++ src/Colors/Cmyk/Channels/Key.php | 8 ++ src/Colors/Cmyk/Channels/Magenta.php | 8 ++ src/Colors/Cmyk/Channels/Yellow.php | 8 ++ src/Colors/Cmyk/Color.php | 115 ++++++++++++++++++ src/Colors/Cmyk/Colorspace.php | 38 ++++++ .../Cmyk/Decoders/StringColorDecoder.php | 37 ++++++ src/Colors/Rgb/Color.php | 5 - src/Colors/Rgb/Colorspace.php | 11 ++ src/Drivers/Gd/InputHandler.php | 10 +- src/Drivers/Imagick/InputHandler.php | 10 +- tests/Colors/Cmyk/ChannelTest.php | 48 ++++++++ tests/Colors/Cmyk/ColorTest.php | 69 +++++++++++ tests/Colors/Cmyk/ColorspaceTest.php | 26 ++++ .../Cmyk/Decoders/StringColorDecoderTest.php | 35 ++++++ tests/Colors/Rgb/ColorTest.php | 35 ++++++ tests/Colors/Rgb/ColorspaceTest.php | 26 ++++ tests/Drivers/Gd/InputHandlerTest.php | 20 +-- tests/Drivers/Imagick/InputHandlerTest.php | 20 +-- tests/TestCase.php | 9 +- 20 files changed, 556 insertions(+), 37 deletions(-) create mode 100644 src/Colors/Cmyk/Channels/Cyan.php create mode 100644 src/Colors/Cmyk/Channels/Key.php create mode 100644 src/Colors/Cmyk/Channels/Magenta.php create mode 100644 src/Colors/Cmyk/Channels/Yellow.php create mode 100644 src/Colors/Cmyk/Color.php create mode 100644 src/Colors/Cmyk/Colorspace.php create mode 100644 src/Colors/Cmyk/Decoders/StringColorDecoder.php create mode 100644 tests/Colors/Cmyk/ChannelTest.php create mode 100644 tests/Colors/Cmyk/ColorTest.php create mode 100644 tests/Colors/Cmyk/ColorspaceTest.php create mode 100644 tests/Colors/Cmyk/Decoders/StringColorDecoderTest.php create mode 100644 tests/Colors/Rgb/ColorspaceTest.php diff --git a/src/Colors/Cmyk/Channels/Cyan.php b/src/Colors/Cmyk/Channels/Cyan.php new file mode 100644 index 00000000..213fea38 --- /dev/null +++ b/src/Colors/Cmyk/Channels/Cyan.php @@ -0,0 +1,55 @@ +value = $this->validate($value); + } + + public function value(): int + { + return $this->value; + } + + public function normalize($precision = 32): float + { + return round($this->value() / $this->max(), $precision); + } + + public function min(): int + { + return 0; + } + + public function max(): int + { + return 100; + } + + public function validate(mixed $value): mixed + { + if ($value < $this->min() || $value > $this->max()) { + throw new ColorException('CMYK color values must be in range 0-100.'); + } + + return $value; + } + + public function toString(): string + { + return (string) $this->value(); + } + + public function __toString(): string + { + return $this->toString(); + } +} diff --git a/src/Colors/Cmyk/Channels/Key.php b/src/Colors/Cmyk/Channels/Key.php new file mode 100644 index 00000000..2893d2af --- /dev/null +++ b/src/Colors/Cmyk/Channels/Key.php @@ -0,0 +1,8 @@ +channels = [ + new Cyan($c), + new Magenta($m), + new Yellow($y), + new Key($k), + ]; + } + + public static function create(mixed $input): ColorInterface + { + return (new class ([ + Decoders\StringColorDecoder::class, + ]) extends AbstractInputHandler + { + })->handle($input); + } + + public function toHex(): string + { + return $this->convertTo(RgbColorspace::class)->toHex(); + } + + public function channels(): array + { + return $this->channels; + } + + public function cyan(): ColorChannelInterface + { + return $this->channel(Cyan::class); + } + + public function magenta(): ColorChannelInterface + { + return $this->channel(Magenta::class); + } + + public function yellow(): ColorChannelInterface + { + return $this->channel(Yellow::class); + } + + public function key(): ColorChannelInterface + { + return $this->channel(Key::class); + } + + public function toArray(): array + { + return [ + $this->cyan()->value(), + $this->magenta()->value(), + $this->yellow()->value(), + $this->key()->value(), + ]; + } + + public function convertTo(string|ColorspaceInterface $colorspace): ColorInterface + { + $colorspace = match (true) { + is_object($colorspace) => $colorspace, + default => new $colorspace(), + }; + + return $colorspace->convertColor($this); + } + + public function toString(): string + { + return sprintf( + 'cmyk(%d, %d, %d, %d)', + $this->cyan()->value(), + $this->magenta()->value(), + $this->yellow()->value(), + $this->key()->value() + ); + } + + public function isGreyscale(): bool + { + return 0 === array_sum([ + $this->cyan()->value(), + $this->magenta()->value(), + $this->yellow()->value(), + ]); + } + + public function __toString(): string + { + return $this->toString(); + } +} diff --git a/src/Colors/Cmyk/Colorspace.php b/src/Colors/Cmyk/Colorspace.php new file mode 100644 index 00000000..76ce8ea3 --- /dev/null +++ b/src/Colors/Cmyk/Colorspace.php @@ -0,0 +1,38 @@ + $this->convertRgbColor($color), + default => $color, + }; + } + + protected function convertRgbColor(RgbColor $color): CmykColor + { + $c = (255 - $color->red()->value()) / 255.0 * 100; + $m = (255 - $color->green()->value()) / 255.0 * 100; + $y = (255 - $color->blue()->value()) / 255.0 * 100; + $k = intval(round(min([$c, $m, $y]))); + + $c = intval(round($c - $k)); + $m = intval(round($m - $k)); + $y = intval(round($y - $k)); + + return new CmykColor($c, $m, $y, $k); + } +} diff --git a/src/Colors/Cmyk/Decoders/StringColorDecoder.php b/src/Colors/Cmyk/Decoders/StringColorDecoder.php new file mode 100644 index 00000000..0f05ada9 --- /dev/null +++ b/src/Colors/Cmyk/Decoders/StringColorDecoder.php @@ -0,0 +1,37 @@ +[0-9\.]+%?), ?(?P[0-9\.]+%?), ?(?P[0-9\.]+%?), ?(?P[0-9\.]+%?)\)$/i'; + if (preg_match($pattern, $input, $matches) != 1) { + throw new DecoderException('Unable to decode input'); + } + + $values = array_map(function ($value) { + return intval(round(floatval(trim(str_replace('%', '', $value))))); + }, [$matches['c'], $matches['m'], $matches['y'], $matches['k']]); + + return new Color(...$values); + } +} diff --git a/src/Colors/Rgb/Color.php b/src/Colors/Rgb/Color.php index d7117565..9cd30fb5 100644 --- a/src/Colors/Rgb/Color.php +++ b/src/Colors/Rgb/Color.php @@ -151,11 +151,6 @@ class Color implements ColorInterface return $colorspace->convertColor($this); } - /** - * Determine if the current color is fully opaque - * - * @return bool - */ public function isFullyOpaque(): bool { return $this->alpha()->value() === 255; diff --git a/src/Colors/Rgb/Colorspace.php b/src/Colors/Rgb/Colorspace.php index 4dbaae08..4c0bcb0f 100644 --- a/src/Colors/Rgb/Colorspace.php +++ b/src/Colors/Rgb/Colorspace.php @@ -2,6 +2,7 @@ namespace Intervention\Image\Colors\Rgb; +use Intervention\Image\Colors\Cmyk\Color as CmykColor; use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\ColorspaceInterface; @@ -10,7 +11,17 @@ class Colorspace implements ColorspaceInterface public function convertColor(ColorInterface $color): ColorInterface { return match (get_class($color)) { + CmykColor::class => $this->convertCmykColor($color), default => $color, }; } + + protected function convertCmykColor(CmykColor $color): Color + { + return new Color( + (int) (255 * (1 - $color->cyan()->normalize()) * (1 - $color->key()->normalize())), + (int) (255 * (1 - $color->magenta()->normalize()) * (1 - $color->key()->normalize())), + (int) (255 * (1 - $color->yellow()->normalize()) * (1 - $color->key()->normalize())), + ); + } } diff --git a/src/Drivers/Gd/InputHandler.php b/src/Drivers/Gd/InputHandler.php index 2cd3da3b..a23deeaa 100644 --- a/src/Drivers/Gd/InputHandler.php +++ b/src/Drivers/Gd/InputHandler.php @@ -2,10 +2,11 @@ namespace Intervention\Image\Drivers\Gd; -use Intervention\Image\Colors\Rgb\Decoders\HexColorDecoder; -use Intervention\Image\Colors\Rgb\Decoders\StringColorDecoder; +use Intervention\Image\Colors\Rgb\Decoders\HexColorDecoder as RgbHexColorDecoder; +use Intervention\Image\Colors\Rgb\Decoders\StringColorDecoder as RgbStringColorDecoder; use Intervention\Image\Colors\Rgb\Decoders\HtmlColornameDecoder; use Intervention\Image\Colors\Rgb\Decoders\TransparentColorDecoder; +use Intervention\Image\Colors\Cmyk\Decoders\StringColorDecoder as CmykStringColorDecoder; use Intervention\Image\Drivers\Abstract\AbstractInputHandler; use Intervention\Image\Drivers\Gd\Decoders\ImageObjectDecoder; use Intervention\Image\Drivers\Gd\Decoders\ColorObjectDecoder; @@ -20,8 +21,9 @@ class InputHandler extends AbstractInputHandler protected array $decoders = [ ImageObjectDecoder::class, ColorObjectDecoder::class, - HexColorDecoder::class, - StringColorDecoder::class, + RgbHexColorDecoder::class, + RgbStringColorDecoder::class, + CmykStringColorDecoder::class, TransparentColorDecoder::class, HtmlColornameDecoder::class, FilePointerImageDecoder::class, diff --git a/src/Drivers/Imagick/InputHandler.php b/src/Drivers/Imagick/InputHandler.php index c41649d0..55d87055 100644 --- a/src/Drivers/Imagick/InputHandler.php +++ b/src/Drivers/Imagick/InputHandler.php @@ -2,10 +2,11 @@ namespace Intervention\Image\Drivers\Imagick; -use Intervention\Image\Colors\Rgb\Decoders\HexColorDecoder; -use Intervention\Image\Colors\Rgb\Decoders\StringColorDecoder; +use Intervention\Image\Colors\Rgb\Decoders\HexColorDecoder as RgbHexColorDecoder; +use Intervention\Image\Colors\Rgb\Decoders\StringColorDecoder as RgbStringColorDecoder; use Intervention\Image\Colors\Rgb\Decoders\HtmlColornameDecoder; use Intervention\Image\Colors\Rgb\Decoders\TransparentColorDecoder; +use Intervention\Image\Colors\Cmyk\Decoders\StringColorDecoder as CmykStringColorDecoder; use Intervention\Image\Drivers\Abstract\AbstractInputHandler; use Intervention\Image\Drivers\Imagick\Decoders\ImageObjectDecoder; use Intervention\Image\Drivers\Imagick\Decoders\ColorObjectDecoder; @@ -20,8 +21,9 @@ class InputHandler extends AbstractInputHandler protected array $decoders = [ ImageObjectDecoder::class, ColorObjectDecoder::class, - HexColorDecoder::class, - StringColorDecoder::class, + RgbHexColorDecoder::class, + RgbStringColorDecoder::class, + CmykStringColorDecoder::class, TransparentColorDecoder::class, HtmlColornameDecoder::class, FilePointerImageDecoder::class, diff --git a/tests/Colors/Cmyk/ChannelTest.php b/tests/Colors/Cmyk/ChannelTest.php new file mode 100644 index 00000000..44928abf --- /dev/null +++ b/tests/Colors/Cmyk/ChannelTest.php @@ -0,0 +1,48 @@ +assertInstanceOf(Channel::class, $channel); + } + + public function testValue(): void + { + $channel = new Channel(10); + $this->assertEquals(10, $channel->value()); + } + + public function testNormalize(): void + { + $channel = new Channel(100); + $this->assertEquals(1, $channel->normalize()); + $channel = new Channel(0); + $this->assertEquals(0, $channel->normalize()); + $channel = new Channel(20); + $this->assertEquals(.2, $channel->normalize()); + } + + public function testValidate(): void + { + $this->expectException(ColorException::class); + new Channel(101); + + $this->expectException(ColorException::class); + new Channel(-1); + } +} diff --git a/tests/Colors/Cmyk/ColorTest.php b/tests/Colors/Cmyk/ColorTest.php new file mode 100644 index 00000000..5dd59c28 --- /dev/null +++ b/tests/Colors/Cmyk/ColorTest.php @@ -0,0 +1,69 @@ +assertInstanceOf(Color::class, $color); + } + + public function testChannels(): void + { + $color = new Color(10, 20, 30, 40); + $this->assertIsArray($color->channels()); + $this->assertCount(4, $color->channels()); + } + + public function testChannel(): void + { + $color = new Color(10, 20, 30, 40); + $channel = $color->channel(Cyan::class); + $this->assertInstanceOf(Cyan::class, $channel); + $this->assertEquals(10, $channel->value()); + } + + public function testCyanMagentaYellowKey(): void + { + $color = new Color(10, 20, 30, 40); + $this->assertInstanceOf(Cyan::class, $color->cyan()); + $this->assertInstanceOf(Magenta::class, $color->magenta()); + $this->assertInstanceOf(Yellow::class, $color->yellow()); + $this->assertInstanceOf(Key::class, $color->key()); + $this->assertEquals(10, $color->cyan()->value()); + $this->assertEquals(20, $color->magenta()->value()); + $this->assertEquals(30, $color->yellow()->value()); + $this->assertEquals(40, $color->key()->value()); + } + + public function testToArray(): void + { + $color = new Color(10, 20, 30, 40); + $this->assertEquals([10, 20, 30, 40], $color->toArray()); + } + + public function testNormalize(): void + { + $color = new Color(100, 50, 20, 0); + $this->assertEquals([1.0, 0.5, 0.2, 0.0], $color->normalize()); + } + + public function testToString(): void + { + $color = new Color(100, 50, 20, 0); + $this->assertEquals('cmyk(100, 50, 20, 0)', (string) $color); + } +} diff --git a/tests/Colors/Cmyk/ColorspaceTest.php b/tests/Colors/Cmyk/ColorspaceTest.php new file mode 100644 index 00000000..818269fc --- /dev/null +++ b/tests/Colors/Cmyk/ColorspaceTest.php @@ -0,0 +1,26 @@ +assertInstanceOf( + CmykColor::class, + $colorspace->convertColor( + new RgbColor(0, 0, 0) + ) + ); + } +} diff --git a/tests/Colors/Cmyk/Decoders/StringColorDecoderTest.php b/tests/Colors/Cmyk/Decoders/StringColorDecoderTest.php new file mode 100644 index 00000000..f6af4237 --- /dev/null +++ b/tests/Colors/Cmyk/Decoders/StringColorDecoderTest.php @@ -0,0 +1,35 @@ +decode('cmyk(0,0,0,0)'); + $this->assertInstanceOf(Color::class, $result); + $this->assertEquals([0, 0, 0, 0], $result->toArray()); + + $result = $decoder->decode('cmyk(0, 100, 100, 0)'); + $this->assertInstanceOf(Color::class, $result); + $this->assertEquals([0, 100, 100, 0], $result->toArray()); + + $result = $decoder->decode('cmyk(0, 100, 100, 0)'); + $this->assertInstanceOf(Color::class, $result); + $this->assertEquals([0, 100, 100, 0], $result->toArray()); + + + $result = $decoder->decode('cmyk(0%, 100%, 100%, 0%)'); + $this->assertInstanceOf(Color::class, $result); + $this->assertEquals([0, 100, 100, 0], $result->toArray()); + } +} diff --git a/tests/Colors/Rgb/ColorTest.php b/tests/Colors/Rgb/ColorTest.php index b4020c3c..69a1b4b1 100644 --- a/tests/Colors/Rgb/ColorTest.php +++ b/tests/Colors/Rgb/ColorTest.php @@ -2,6 +2,8 @@ namespace Intervention\Image\Tests\Colors\Rgb; +use Intervention\Image\Colors\Cmyk\Color as CmykColor; +use Intervention\Image\Colors\Cmyk\Colorspace as CmykColorspace; use Intervention\Image\Colors\Rgb\Channels\Red; use Intervention\Image\Colors\Rgb\Channels\Green; use Intervention\Image\Colors\Rgb\Channels\Blue; @@ -84,4 +86,37 @@ class ColorTest extends TestCase $color = new Color(181, 55, 23); $this->assertEquals('rgb(181, 55, 23)', (string) $color); } + + public function testConvertTo(): void + { + $color = new Color(0, 0, 0); + $converted = $color->convertTo(CmykColorspace::class); + $this->assertInstanceOf(CmykColor::class, $converted); + $this->assertEquals([0, 0, 0, 100], $converted->toArray()); + + $color = new Color(255, 255, 255); + $converted = $color->convertTo(CmykColorspace::class); + $this->assertInstanceOf(CmykColor::class, $converted); + $this->assertEquals([0, 0, 0, 0], $converted->toArray()); + + $color = new Color(255, 0, 0); + $converted = $color->convertTo(CmykColorspace::class); + $this->assertInstanceOf(CmykColor::class, $converted); + $this->assertEquals([0, 100, 100, 0], $converted->toArray()); + + $color = new Color(255, 0, 255); + $converted = $color->convertTo(CmykColorspace::class); + $this->assertInstanceOf(CmykColor::class, $converted); + $this->assertEquals([0, 100, 0, 0], $converted->toArray()); + + $color = new Color(255, 255, 0); + $converted = $color->convertTo(CmykColorspace::class); + $this->assertInstanceOf(CmykColor::class, $converted); + $this->assertEquals([0, 0, 100, 0], $converted->toArray()); + + $color = new Color(255, 204, 204); + $converted = $color->convertTo(CmykColorspace::class); + $this->assertInstanceOf(CmykColor::class, $converted); + $this->assertEquals([0, 20, 20, 0], $converted->toArray()); + } } diff --git a/tests/Colors/Rgb/ColorspaceTest.php b/tests/Colors/Rgb/ColorspaceTest.php new file mode 100644 index 00000000..be7808c5 --- /dev/null +++ b/tests/Colors/Rgb/ColorspaceTest.php @@ -0,0 +1,26 @@ +assertInstanceOf( + RgbColor::class, + $colorspace->convertColor( + new CmykColor(0, 0, 0, 0) + ) + ); + } +} diff --git a/tests/Drivers/Gd/InputHandlerTest.php b/tests/Drivers/Gd/InputHandlerTest.php index c9d1335b..177213f3 100644 --- a/tests/Drivers/Gd/InputHandlerTest.php +++ b/tests/Drivers/Gd/InputHandlerTest.php @@ -2,7 +2,7 @@ namespace Intervention\Image\Tests\Drivers\Gd; -use Intervention\Image\Colors\Rgb\Color; +use Intervention\Image\Colors\Rgb\Color as RgbColor; use Intervention\Image\Drivers\Gd\Image; use Intervention\Image\Drivers\Gd\InputHandler; use Intervention\Image\Exceptions\DecoderException; @@ -58,37 +58,37 @@ class GdInputHandlerTest extends TestCase $handler = new InputHandler(); $input = 'ccff33'; $result = $handler->handle($input); - $this->assertInstanceOf(Color::class, $result); + $this->assertInstanceOf(RgbColor::class, $result); $this->assertEquals([204, 255, 51, 255], $result->toArray()); $handler = new InputHandler(); $input = 'cf3'; $result = $handler->handle($input); - $this->assertInstanceOf(Color::class, $result); + $this->assertInstanceOf(RgbColor::class, $result); $this->assertEquals([204, 255, 51, 255], $result->toArray()); $handler = new InputHandler(); $input = '#123456'; $result = $handler->handle($input); - $this->assertInstanceOf(Color::class, $result); + $this->assertInstanceOf(RgbColor::class, $result); $this->assertEquals([18, 52, 86, 255], $result->toArray()); $handler = new InputHandler(); $input = '#333'; $result = $handler->handle($input); - $this->assertInstanceOf(Color::class, $result); + $this->assertInstanceOf(RgbColor::class, $result); $this->assertEquals([51, 51, 51, 255], $result->toArray()); $handler = new InputHandler(); $input = '#3333'; $result = $handler->handle($input); - $this->assertInstanceOf(Color::class, $result); + $this->assertInstanceOf(RgbColor::class, $result); $this->assertEquals([51, 51, 51, 51], $result->toArray()); $handler = new InputHandler(); $input = '#33333333'; $result = $handler->handle($input); - $this->assertInstanceOf(Color::class, $result); + $this->assertInstanceOf(RgbColor::class, $result); $this->assertEquals([51, 51, 51, 51], $result->toArray()); } @@ -96,12 +96,12 @@ class GdInputHandlerTest extends TestCase { $handler = new InputHandler(); $result = $handler->handle('rgb(10, 20, 30)'); - $this->assertInstanceOf(Color::class, $result); + $this->assertInstanceOf(RgbColor::class, $result); $this->assertEquals([10, 20, 30, 255], $result->toArray()); $handler = new InputHandler(); $result = $handler->handle('rgba(10, 20, 30, 1.0)'); - $this->assertInstanceOf(Color::class, $result); + $this->assertInstanceOf(RgbColor::class, $result); $this->assertEquals([10, 20, 30, 255], $result->toArray()); } @@ -110,7 +110,7 @@ class GdInputHandlerTest extends TestCase $handler = new InputHandler(); $input = 'transparent'; $result = $handler->handle($input); - $this->assertInstanceOf(Color::class, $result); + $this->assertInstanceOf(RgbColor::class, $result); $this->assertEquals([0, 0, 0, 0], $result->toArray()); } } diff --git a/tests/Drivers/Imagick/InputHandlerTest.php b/tests/Drivers/Imagick/InputHandlerTest.php index ed7a99ca..876a59e0 100644 --- a/tests/Drivers/Imagick/InputHandlerTest.php +++ b/tests/Drivers/Imagick/InputHandlerTest.php @@ -2,7 +2,7 @@ namespace Intervention\Image\Tests\Drivers\Imagick; -use Intervention\Image\Colors\Rgb\Color; +use Intervention\Image\Colors\Rgb\Color as RgbColor; use Intervention\Image\Drivers\Imagick\Image; use Intervention\Image\Drivers\Imagick\InputHandler; use Intervention\Image\Exceptions\DecoderException; @@ -58,37 +58,37 @@ class InputHandlerTest extends TestCase $handler = new InputHandler(); $input = 'ccff33'; $result = $handler->handle($input); - $this->assertInstanceOf(Color::class, $result); + $this->assertInstanceOf(RgbColor::class, $result); $this->assertEquals([204, 255, 51, 255], $result->toArray()); $handler = new InputHandler(); $input = 'cf3'; $result = $handler->handle($input); - $this->assertInstanceOf(Color::class, $result); + $this->assertInstanceOf(RgbColor::class, $result); $this->assertEquals([204, 255, 51, 255], $result->toArray()); $handler = new InputHandler(); $input = '#123456'; $result = $handler->handle($input); - $this->assertInstanceOf(Color::class, $result); + $this->assertInstanceOf(RgbColor::class, $result); $this->assertEquals([18, 52, 86, 255], $result->toArray()); $handler = new InputHandler(); $input = '#333'; $result = $handler->handle($input); - $this->assertInstanceOf(Color::class, $result); + $this->assertInstanceOf(RgbColor::class, $result); $this->assertEquals([51, 51, 51, 255], $result->toArray()); $handler = new InputHandler(); $input = '#3333'; $result = $handler->handle($input); - $this->assertInstanceOf(Color::class, $result); + $this->assertInstanceOf(RgbColor::class, $result); $this->assertEquals([51, 51, 51, 51], $result->toArray()); $handler = new InputHandler(); $input = '#33333333'; $result = $handler->handle($input); - $this->assertInstanceOf(Color::class, $result); + $this->assertInstanceOf(RgbColor::class, $result); $this->assertEquals([51, 51, 51, 51], $result->toArray()); } @@ -96,12 +96,12 @@ class InputHandlerTest extends TestCase { $handler = new InputHandler(); $result = $handler->handle('rgb(10, 20, 30)'); - $this->assertInstanceOf(Color::class, $result); + $this->assertInstanceOf(RgbColor::class, $result); $this->assertEquals([10, 20, 30, 255], $result->toArray()); $handler = new InputHandler(); $result = $handler->handle('rgba(10, 20, 30, 1.0)'); - $this->assertInstanceOf(Color::class, $result); + $this->assertInstanceOf(RgbColor::class, $result); $this->assertEquals([10, 20, 30, 255], $result->toArray()); } @@ -110,7 +110,7 @@ class InputHandlerTest extends TestCase $handler = new InputHandler(); $input = 'transparent'; $result = $handler->handle($input); - $this->assertInstanceOf(Color::class, $result); + $this->assertInstanceOf(RgbColor::class, $result); $this->assertEquals([0, 0, 0, 0], $result->toArray()); } } diff --git a/tests/TestCase.php b/tests/TestCase.php index f73c069c..8f76a724 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -2,8 +2,8 @@ namespace Intervention\Image\Tests; -use Intervention\Image\Colors\Rgb\Color; -use Intervention\Image\Colors\Rgb\Colorspace; +use Intervention\Image\Colors\Rgb\Channels\Alpha; +use Intervention\Image\Colors\Rgb\Color as RgbColor; use Intervention\Image\Interfaces\ColorInterface; use Mockery\Adapter\Phpunit\MockeryTestCase; @@ -26,7 +26,8 @@ abstract class TestCase extends MockeryTestCase protected function assertTransparency(ColorInterface $color) { - $this->assertInstanceOf(Color::class, $color); - $this->assertEquals(0, $color->convertTo(Colorspace::class)->alpha()->value()); + $this->assertInstanceOf(RgbColor::class, $color); + $channel = $color->channel(Alpha::class); + $this->assertEquals(0, $channel->value()); } } From 24c807120014370c89d19912300176990ab09175 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 22 Oct 2023 12:10:16 +0200 Subject: [PATCH 312/476] Add colorspace transformation --- src/Colors/Cmyk/Channels/Cyan.php | 15 +++- src/Colors/Cmyk/Colorspace.php | 21 ++++++ src/Colors/Rgb/Channels/Red.php | 14 ++-- src/Colors/Rgb/Colorspace.php | 21 ++++++ src/Drivers/Abstract/AbstractImage.php | 2 + src/Drivers/Gd/Image.php | 26 +++++++ src/Drivers/Imagick/Encoders/JpegEncoder.php | 4 -- src/Drivers/Imagick/Image.php | 34 ++++++++- .../Imagick/Modifiers/ColorspaceModifier.php | 66 ++++++++++++++++++ .../Imagick/Traits/CanHandleColors.php | 26 +++++-- src/Interfaces/ColorChannelInterface.php | 8 +++ src/Interfaces/ColorspaceInterface.php | 8 +++ src/Interfaces/ImageInterface.php | 15 ++++ tests/Colors/Cmyk/ChannelTest.php | 12 ++++ tests/Colors/Rgb/ChannelTest.php | 12 ++++ tests/Drivers/Gd/ImageTest.php | 32 +++++++++ .../Decoders/BinaryImageDecoderTest.php | 11 +++ tests/Drivers/Imagick/ImageTest.php | 34 +++++++++ tests/images/cmyk.jpg | Bin 0 -> 2750493 bytes 19 files changed, 343 insertions(+), 18 deletions(-) create mode 100644 src/Drivers/Imagick/Modifiers/ColorspaceModifier.php create mode 100644 tests/images/cmyk.jpg diff --git a/src/Colors/Cmyk/Channels/Cyan.php b/src/Colors/Cmyk/Channels/Cyan.php index 213fea38..8da3a429 100644 --- a/src/Colors/Cmyk/Channels/Cyan.php +++ b/src/Colors/Cmyk/Channels/Cyan.php @@ -9,9 +9,20 @@ class Cyan implements ColorChannelInterface { protected int $value; - public function __construct(int $value) + /** + * {@inheritdoc} + * + * @see ColorChannelInterface::__construct() + */ + public function __construct(int $value = null, float $normalized = null) { - $this->value = $this->validate($value); + $this->value = $this->validate( + match (true) { + is_null($value) && is_numeric($normalized) => intval(round($normalized * $this->max())), + is_numeric($value) && is_null($normalized) => $value, + default => throw new ColorException('Color channels must either have a value or a normalized value') + } + ); } public function value(): int diff --git a/src/Colors/Cmyk/Colorspace.php b/src/Colors/Cmyk/Colorspace.php index 76ce8ea3..c05749b5 100644 --- a/src/Colors/Cmyk/Colorspace.php +++ b/src/Colors/Cmyk/Colorspace.php @@ -9,6 +9,27 @@ use Intervention\Image\Interfaces\ColorspaceInterface; class Colorspace implements ColorspaceInterface { + public static $channels = [ + Channels\Cyan::class, + Channels\Magenta::class, + Channels\Yellow::class, + Channels\Key::class + ]; + + /** + * {@inheritdoc} + * + * @see ColorspaceInterface::createColor() + */ + public function colorFromNormalized(array $normalized): ColorInterface + { + $values = array_map(function ($classname, $value_normalized) { + return (new $classname(normalized: $value_normalized))->value(); + }, self::$channels, $normalized); + + return new Color(...$values); + } + /** * {@inheritdoc} * diff --git a/src/Colors/Rgb/Channels/Red.php b/src/Colors/Rgb/Channels/Red.php index 510d628c..68c3e130 100644 --- a/src/Colors/Rgb/Channels/Red.php +++ b/src/Colors/Rgb/Channels/Red.php @@ -10,13 +10,19 @@ class Red implements ColorChannelInterface protected int $value; /** - * Create and validate new instance + * {@inheritdoc} * - * @param int $value + * @see ColorChannelInterface::__construct() */ - public function __construct(int $value) + public function __construct(int $value = null, float $normalized = null) { - $this->value = $this->validate($value); + $this->value = $this->validate( + match (true) { + is_null($value) && is_numeric($normalized) => intval(round($normalized * $this->max())), + is_numeric($value) && is_null($normalized) => $value, + default => throw new ColorException('Color channels must either have a value or a normalized value') + } + ); } /** diff --git a/src/Colors/Rgb/Colorspace.php b/src/Colors/Rgb/Colorspace.php index 4c0bcb0f..f3dd1503 100644 --- a/src/Colors/Rgb/Colorspace.php +++ b/src/Colors/Rgb/Colorspace.php @@ -8,6 +8,27 @@ use Intervention\Image\Interfaces\ColorspaceInterface; class Colorspace implements ColorspaceInterface { + public static $channels = [ + Channels\Red::class, + Channels\Green::class, + Channels\Blue::class, + Channels\Alpha::class + ]; + + /** + * {@inheritdoc} + * + * @see ColorspaceInterface::createColor() + */ + public function colorFromNormalized(array $normalized): ColorInterface + { + $values = array_map(function ($classname, $value_normalized) { + return (new $classname(normalized: $value_normalized))->value(); + }, self::$channels, $normalized); + + return new Color(...$values); + } + public function convertColor(ColorInterface $color): ColorInterface { return match (get_class($color)) { diff --git a/src/Drivers/Abstract/AbstractImage.php b/src/Drivers/Abstract/AbstractImage.php index 3b46865f..3a67cc21 100644 --- a/src/Drivers/Abstract/AbstractImage.php +++ b/src/Drivers/Abstract/AbstractImage.php @@ -11,6 +11,7 @@ use Intervention\Image\Geometry\Point; use Intervention\Image\Geometry\Polygon; use Intervention\Image\Geometry\Rectangle; use Intervention\Image\Interfaces\CollectionInterface; +use Intervention\Image\Interfaces\ColorspaceInterface; use Intervention\Image\Interfaces\EncoderInterface; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; @@ -25,6 +26,7 @@ abstract class AbstractImage implements ImageInterface use CanHandleInput; use CanRunCallback; + protected ColorspaceInterface $colorspace; protected Collection $exif; public function eachFrame(callable $callback): ImageInterface diff --git a/src/Drivers/Gd/Image.php b/src/Drivers/Gd/Image.php index 5fdb2ed5..afb573a2 100644 --- a/src/Drivers/Gd/Image.php +++ b/src/Drivers/Gd/Image.php @@ -3,9 +3,12 @@ namespace Intervention\Image\Drivers\Gd; use Intervention\Image\Collection; +use Intervention\Image\Colors\Rgb\Colorspace as RgbColorspace; use Intervention\Image\Drivers\Abstract\AbstractImage; use Intervention\Image\Drivers\Gd\Traits\CanHandleColors; +use Intervention\Image\Exceptions\NotSupportedException; use Intervention\Image\Interfaces\ColorInterface; +use Intervention\Image\Interfaces\ColorspaceInterface; use Intervention\Image\Interfaces\FrameInterface; use Intervention\Image\Interfaces\ImageInterface; use IteratorAggregate; @@ -79,4 +82,27 @@ class Image extends AbstractImage implements ImageInterface, IteratorAggregate return null; } + + public function getColorspace(): ColorspaceInterface + { + return new RgbColorspace(); + } + + /** + * {@inheritdoc} + * + * @see ImageInterface::setColorspace() + */ + public function setColorspace(string|ColorspaceInterface $target): ImageInterface + { + if (is_string($target) && !in_array($target, ['rgb', RgbColorspace::class])) { + throw new NotSupportedException('Only RGB colorspace is supported with GD driver.'); + } + + if (is_object($target) && !is_a($target, RgbColorspace::class)) { + throw new NotSupportedException('Only RGB colorspace is supported with GD driver.'); + } + + return $this; + } } diff --git a/src/Drivers/Imagick/Encoders/JpegEncoder.php b/src/Drivers/Imagick/Encoders/JpegEncoder.php index 7921e46f..c6191704 100644 --- a/src/Drivers/Imagick/Encoders/JpegEncoder.php +++ b/src/Drivers/Imagick/Encoders/JpegEncoder.php @@ -30,10 +30,6 @@ class JpegEncoder extends AbstractEncoder implements EncoderInterface $imagick->setCompressionQuality($this->quality); $imagick->setImageCompressionQuality($this->quality); - if ($imagick->getImageColorspace() != Imagick::COLORSPACE_SRGB) { - $imagick->transformImageColorspace(Imagick::COLORSPACE_SRGB); - } - return new EncodedImage($imagick->getImagesBlob(), 'image/jpeg'); } } diff --git a/src/Drivers/Imagick/Image.php b/src/Drivers/Imagick/Image.php index 657faf8d..d04b8405 100644 --- a/src/Drivers/Imagick/Image.php +++ b/src/Drivers/Imagick/Image.php @@ -4,9 +4,13 @@ namespace Intervention\Image\Drivers\Imagick; use Imagick; use ImagickException; +use Intervention\Image\Colors\Cmyk\Colorspace as CmykColorspace; +use Intervention\Image\Colors\Rgb\Colorspace as RgbColorspace; use Intervention\Image\Drivers\Abstract\AbstractImage; +use Intervention\Image\Drivers\Imagick\Modifiers\ColorspaceModifier; use Intervention\Image\Drivers\Imagick\Traits\CanHandleColors; use Intervention\Image\Interfaces\ColorInterface; +use Intervention\Image\Interfaces\ColorspaceInterface; use Intervention\Image\Interfaces\FrameInterface; use Intervention\Image\Interfaces\ImageInterface; use Iterator; @@ -19,7 +23,14 @@ class Image extends AbstractImage implements ImageInterface, Iterator public function __construct(protected Imagick $imagick) { - // + $this->colorspace = match ($imagick->getImageColorspace()) { + Imagick::COLORSPACE_RGB, Imagick::COLORSPACE_SRGB => new RgbColorspace(), + Imagick::COLORSPACE_CMYK => new CmykColorspace(), + default => function () use ($imagick) { + $imagick->transformImageColorspace(Imagick::COLORSPACE_SRGB); + return new RgbColorspace(); + } + }; } public function getImagick(): Imagick @@ -128,10 +139,29 @@ class Image extends AbstractImage implements ImageInterface, Iterator { if ($frame = $this->getFrame($frame_key)) { return $this->colorFromPixel( - $frame->getCore()->getImagePixelColor($x, $y) + $frame->getCore()->getImagePixelColor($x, $y), + $this->colorspace ); } return null; } + + public function getColorspace(): ColorspaceInterface + { + return match ($this->imagick->getImageColorspace()) { + Imagick::COLORSPACE_CMYK => new CmykColorspace(), + default => new RgbColorspace(), + }; + } + + /** + * {@inheritdoc} + * + * @see ImageInterface::setColorspace() + */ + public function setColorspace(string|ColorspaceInterface $colorspace): ImageInterface + { + return $this->modify(new ColorspaceModifier($colorspace)); + } } diff --git a/src/Drivers/Imagick/Modifiers/ColorspaceModifier.php b/src/Drivers/Imagick/Modifiers/ColorspaceModifier.php new file mode 100644 index 00000000..f9ef2f0d --- /dev/null +++ b/src/Drivers/Imagick/Modifiers/ColorspaceModifier.php @@ -0,0 +1,66 @@ + Imagick::COLORSPACE_SRGB, + CmykColorspace::class => Imagick::COLORSPACE_CMYK, + ]; + + public function __construct(protected string|ColorspaceInterface $target) + { + // + } + + public function apply(ImageInterface $image): ImageInterface + { + $colorspace = $this->targetColorspace(); + + $imagick = $this->failIfNotClass($image, Image::class)->getImagick(); + $imagick->transformImageColorspace( + $this->getImagickColorspace($colorspace) + ); + + return $image; + } + + private function getImagickColorspace(ColorspaceInterface $colorspace): int + { + if (!array_key_exists(get_class($colorspace), self::$mapping)) { + throw new NotSupportedException('Given colorspace is not supported.'); + } + + return self::$mapping[get_class($colorspace)]; + } + + private function targetColorspace(): ColorspaceInterface + { + if (is_object($this->target)) { + return $this->target; + } + + if (in_array($this->target, ['rgb', 'RGB', RgbColorspace::class])) { + return new RgbColorspace(); + } + + if (in_array($this->target, ['cmyk', 'CMYK', CmykColorspace::class])) { + return new CmykColorspace(); + } + + throw new NotSupportedException('Given colorspace is not supported.'); + } +} diff --git a/src/Drivers/Imagick/Traits/CanHandleColors.php b/src/Drivers/Imagick/Traits/CanHandleColors.php index adb50c41..af6770b7 100644 --- a/src/Drivers/Imagick/Traits/CanHandleColors.php +++ b/src/Drivers/Imagick/Traits/CanHandleColors.php @@ -2,23 +2,37 @@ namespace Intervention\Image\Drivers\Imagick\Traits; +use Imagick; use ImagickPixel; +use Intervention\Image\Colors\Cmyk\Colorspace as CmykColorspace; use Intervention\Image\Interfaces\ColorInterface; -use Intervention\Image\Traits\CanHandleInput; +use Intervention\Image\Interfaces\ColorspaceInterface; trait CanHandleColors { - use CanHandleInput; - /** * Transforms ImagickPixel to own color object * - * @param ImagickPixel $pixel + * @param ImagickPixel $pixel + * @param ColorspaceInterface $colorspace * @return ColorInterface */ - public function colorFromPixel(ImagickPixel $pixel): ColorInterface + public function colorFromPixel(ImagickPixel $pixel, ColorspaceInterface $colorspace): ColorInterface { - return $this->handleInput($pixel->getColorAsString()); + return match (get_class($colorspace)) { + CmykColorspace::class => $colorspace->colorFromNormalized([ + $pixel->getColorValue(Imagick::COLOR_CYAN), + $pixel->getColorValue(Imagick::COLOR_MAGENTA), + $pixel->getColorValue(Imagick::COLOR_YELLOW), + $pixel->getColorValue(Imagick::COLOR_BLACK), + ]), + default => $colorspace->colorFromNormalized([ + $pixel->getColorValue(Imagick::COLOR_RED), + $pixel->getColorValue(Imagick::COLOR_GREEN), + $pixel->getColorValue(Imagick::COLOR_BLUE), + $pixel->getColorValue(Imagick::COLOR_ALPHA), + ]), + }; } /** diff --git a/src/Interfaces/ColorChannelInterface.php b/src/Interfaces/ColorChannelInterface.php index 7c061ee1..a5bc074f 100644 --- a/src/Interfaces/ColorChannelInterface.php +++ b/src/Interfaces/ColorChannelInterface.php @@ -4,6 +4,14 @@ namespace Intervention\Image\Interfaces; interface ColorChannelInterface { + /** + * Create new instance by either value or normalized value + * + * @param int|null $value + * @param float|null $normalized + */ + public function __construct(int $value = null, float $normalized = null); + /** * Return color channels integer value * diff --git a/src/Interfaces/ColorspaceInterface.php b/src/Interfaces/ColorspaceInterface.php index a2bb37db..7b03204a 100644 --- a/src/Interfaces/ColorspaceInterface.php +++ b/src/Interfaces/ColorspaceInterface.php @@ -11,4 +11,12 @@ interface ColorspaceInterface * @return ColorInterface */ public function convertColor(ColorInterface $color): ColorInterface; + + /** + * Create new color in colorspace from given normalized channel values + * + * @param array $normalized + * @return ColorInterface + */ + public function colorFromNormalized(array $normalized): ColorInterface; } diff --git a/src/Interfaces/ImageInterface.php b/src/Interfaces/ImageInterface.php index cf3083ef..12646f9e 100644 --- a/src/Interfaces/ImageInterface.php +++ b/src/Interfaces/ImageInterface.php @@ -140,6 +140,21 @@ interface ImageInterface extends Traversable, Countable public function pickColor(int $x, int $y, int $frame_key = 0): ?ColorInterface; public function pickColors(int $x, int $y): CollectionInterface; + /** + * Get the colorspace of the image + * + * @return ColorspaceInterface + */ + public function getColorspace(): ColorspaceInterface; + + /** + * Transform image to given colorspace + * + * @param string|ColorspaceInterface $target + * @return ImageInterface + */ + public function setColorspace(string|ColorspaceInterface $target): ImageInterface; + /** * Draw text on image * diff --git a/tests/Colors/Cmyk/ChannelTest.php b/tests/Colors/Cmyk/ChannelTest.php index 44928abf..64f03f33 100644 --- a/tests/Colors/Cmyk/ChannelTest.php +++ b/tests/Colors/Cmyk/ChannelTest.php @@ -19,6 +19,18 @@ class ChannelTest extends TestCase { $channel = new Channel(0); $this->assertInstanceOf(Channel::class, $channel); + + $channel = new Channel(value: 0); + $this->assertInstanceOf(Channel::class, $channel); + + $channel = new Channel(normalized: 0); + $this->assertInstanceOf(Channel::class, $channel); + + $this->expectException(ColorException::class); + $channel = new Channel(); + + $this->expectException(ColorException::class); + $channel = new Channel(normalized: 2); } public function testValue(): void diff --git a/tests/Colors/Rgb/ChannelTest.php b/tests/Colors/Rgb/ChannelTest.php index 4cc2fadb..5f2c45d6 100644 --- a/tests/Colors/Rgb/ChannelTest.php +++ b/tests/Colors/Rgb/ChannelTest.php @@ -18,6 +18,18 @@ class ChannelTest extends TestCase { $channel = new Channel(0); $this->assertInstanceOf(Channel::class, $channel); + + $channel = new Channel(value: 0); + $this->assertInstanceOf(Channel::class, $channel); + + $channel = new Channel(normalized: 0); + $this->assertInstanceOf(Channel::class, $channel); + + $this->expectException(ColorException::class); + $channel = new Channel(); + + $this->expectException(ColorException::class); + $channel = new Channel(normalized: 2); } public function testValue(): void diff --git a/tests/Drivers/Gd/ImageTest.php b/tests/Drivers/Gd/ImageTest.php index cd4b7031..8169e767 100644 --- a/tests/Drivers/Gd/ImageTest.php +++ b/tests/Drivers/Gd/ImageTest.php @@ -8,6 +8,9 @@ use Intervention\Image\Drivers\Gd\Image; use Intervention\Image\Geometry\Rectangle; use Intervention\Image\Tests\TestCase; use Intervention\Image\Colors\Rgb\Color; +use Intervention\Image\Colors\Rgb\Colorspace as RgbColorspace; +use Intervention\Image\Colors\Cmyk\Colorspace as CmykColorspace; +use Intervention\Image\Exceptions\NotSupportedException; /** * @requires extension gd @@ -119,4 +122,33 @@ class ImageTest extends TestCase $this->assertEquals([0, 255, 0, 255], $colors->get(1)->toArray()); $this->assertEquals([0, 0, 255, 255], $colors->get(2)->toArray()); } + + public function testGetColorspace(): void + { + $this->assertInstanceOf(RgbColorspace::class, $this->image->getColorspace()); + } + + public function testSetColorspace(): void + { + $result = $this->image->setColorspace('rgb'); + $this->assertInstanceOf(Image::class, $result); + $this->assertInstanceOf(RgbColorspace::class, $result->getColorspace()); + + $result = $this->image->setColorspace(RgbColorspace::class); + $this->assertInstanceOf(Image::class, $result); + $this->assertInstanceOf(RgbColorspace::class, $result->getColorspace()); + + $result = $this->image->setColorspace(new RgbColorspace()); + $this->assertInstanceOf(Image::class, $result); + $this->assertInstanceOf(RgbColorspace::class, $result->getColorspace()); + + $this->expectException(NotSupportedException::class); + $this->image->setColorspace('cmyk'); + + $this->expectException(NotSupportedException::class); + $this->image->setColorspace(CmykColorspace::class); + + $this->expectException(NotSupportedException::class); + $this->image->setColorspace(new CmykColorspace()); + } } diff --git a/tests/Drivers/Imagick/Decoders/BinaryImageDecoderTest.php b/tests/Drivers/Imagick/Decoders/BinaryImageDecoderTest.php index b3d20b4a..39b00bc6 100644 --- a/tests/Drivers/Imagick/Decoders/BinaryImageDecoderTest.php +++ b/tests/Drivers/Imagick/Decoders/BinaryImageDecoderTest.php @@ -2,6 +2,8 @@ namespace Intervention\Image\Tests\Drivers\Imagick\Decoders; +use Intervention\Image\Colors\Cmyk\Colorspace as CmykColorspace; +use Intervention\Image\Colors\Rgb\Colorspace as RgbColorspace; use Intervention\Image\Drivers\Imagick\Decoders\BinaryImageDecoder; use Intervention\Image\Drivers\Imagick\Image; use Intervention\Image\Tests\TestCase; @@ -13,6 +15,7 @@ class BinaryImageDecoderTest extends TestCase $decoder = new BinaryImageDecoder(); $image = $decoder->decode(file_get_contents($this->getTestImagePath('tile.png'))); $this->assertInstanceOf(Image::class, $image); + $this->assertInstanceOf(RgbColorspace::class, $image->getColorspace()); $this->assertEquals(16, $image->getWidth()); $this->assertEquals(16, $image->getHeight()); $this->assertCount(1, $image); @@ -48,4 +51,12 @@ class BinaryImageDecoderTest extends TestCase $this->assertCount(1, $image); $this->assertEquals('Oliver Vogel', $image->getExif('IFD0.Artist')); } + + public function testDecodeCmykImage(): void + { + $decoder = new BinaryImageDecoder(); + $image = $decoder->decode(file_get_contents($this->getTestImagePath('cmyk.jpg'))); + $this->assertInstanceOf(Image::class, $image); + $this->assertInstanceOf(CmykColorspace::class, $image->getColorspace()); + } } diff --git a/tests/Drivers/Imagick/ImageTest.php b/tests/Drivers/Imagick/ImageTest.php index d76e8d19..a353c526 100644 --- a/tests/Drivers/Imagick/ImageTest.php +++ b/tests/Drivers/Imagick/ImageTest.php @@ -4,6 +4,8 @@ namespace Intervention\Image\Tests\Drivers\Imagick; use Imagick; use ImagickPixel; +use Intervention\Image\Colors\Rgb\Colorspace as RgbColorspace; +use Intervention\Image\Colors\Cmyk\Colorspace as CmykColorspace; use Intervention\Image\Drivers\Imagick\Frame; use Intervention\Image\Drivers\Imagick\Image; use Intervention\Image\Geometry\Rectangle; @@ -87,4 +89,36 @@ class ImageTest extends TestCase { $this->assertInstanceOf(Rectangle::class, $this->image->getSize()); } + + public function testGetColorspace(): void + { + $this->assertInstanceOf(RgbColorspace::class, $this->image->getColorspace()); + } + + public function testSetColorspace(): void + { + $result = $this->image->setColorspace('rgb'); + $this->assertInstanceOf(Image::class, $result); + $this->assertInstanceOf(RgbColorspace::class, $result->getColorspace()); + + $result = $this->image->setColorspace(RgbColorspace::class); + $this->assertInstanceOf(Image::class, $result); + $this->assertInstanceOf(RgbColorspace::class, $result->getColorspace()); + + $result = $this->image->setColorspace(new RgbColorspace()); + $this->assertInstanceOf(Image::class, $result); + $this->assertInstanceOf(RgbColorspace::class, $result->getColorspace()); + + $result = $this->image->setColorspace('cmyk'); + $this->assertInstanceOf(Image::class, $result); + $this->assertInstanceOf(CmykColorspace::class, $result->getColorspace()); + + $result = $this->image->setColorspace(CmykColorspace::class); + $this->assertInstanceOf(Image::class, $result); + $this->assertInstanceOf(CmykColorspace::class, $result->getColorspace()); + + $result = $this->image->setColorspace(new CmykColorspace()); + $this->assertInstanceOf(Image::class, $result); + $this->assertInstanceOf(CmykColorspace::class, $result->getColorspace()); + } } diff --git a/tests/images/cmyk.jpg b/tests/images/cmyk.jpg new file mode 100644 index 0000000000000000000000000000000000000000..1459b2c02c31c1e6d2b866680adb11e3aa9197a3 GIT binary patch literal 2750493 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%0D1+93F)5i2d92$O=hn8%4e{%1`mNG6Y) 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 z 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

U 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

<<{phuHKD`yQfP9&5 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 z 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

-gz>mVfm<|hDe0Wy#0P$W3bDHK`@ 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

q@-&l5w__&2pIxQ!=aiAnH?L+;fVq_ms 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

N5VtU0!d+%4Im`8~B8d1=;k@+V)Lb6OEE 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

vKXHRcG-qB?_r{crIM{eX%v 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 z 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

rh5raGOLk1xpR_?0`&jb!6>X22G4*0!M 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

80{^nmH3D0*-o`jibr_1uu`|PdBRSd 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

|5Neyp?>9pWvp#_X$qqm0ffq(jyHQ&Q=tnjdkU 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%0D7+C*B^uqseacCg%+xPt*>CZqBASQNro}1D5 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 K9 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
mpr?uV zUi3$oiW-hX=%YgMz5^I10ljw|2G6H9M`CW;jJah%Fjpqa1x%bqP7rn$`vCC@mdg4E zJq|aS`FhV0oRRS-&l!wh{GGBIyhC@0J_^;*yn=qg6RAO~r@%5w^gnA*bxQC86*Z=C zICdEwB0IKkDOxW@_UuCck@#4fF?pg7`YV_t!hO<9fRBL5?gfneEMgjV8P5~N#ny8^ z?Ad_}XZPkF!98JBCXazE=89-6w2FQq=pb~6hW5G+XH%Em0FnRdYtLUoC2G$eTZtOb zcc7{IBhd}n=N*wZ&A6jTTdqPgas*}7 zAL^cio}+!!l#hlq&N>PDr7BCJ!4xWlEH37r>=YpkD3<&}GJ((HmAlKaDq&d8TU<0h zKH1U6A&uO857e`rf;L0(jOShu^p)0peF1WD%er&bNYJ|E$O+^<%a4)8sF?Z(UAs{| zrqhih=oN+m?K`wWXA=Ly%++w1pD+?zUZe}KQ{*5g0$nn{vJcpF@z)$WZlbVr`%zpU zKX$VOEaDspnhGsuE%8c%&e2Q8N)cB&f1EvvT-Po?;)v9?t{Fz6{<2=`Jb^+qA{&>Y z9ycu0rlNP64v9LVoa89BHj)2Sgk|Q0Z{2wM#MVF6gf`Vi{k&bSK!FPfX#7W z053N10{DU*;~5Pr+1~9aGb)65e)EHVC3mnuQ%$|;|Rks5a0GB=L55wnJ-K z9#{T{0FF7GPD1>1$a-)X8M@zpa4FI}a-tnT%@{1Nzk&k$?9@w8ue(kO!_j5!Mf7a+ zljbkbCQMZ$y7q{T9($qWU!d7Aac3_!Q?oE>HExPxeAEV9zj*)p|G)x%iYE=4!@71Q z1A#i#ed;jc(s9>=?ns}*+5t9Fd|-ClGvv3u)cR;t+2B`|5Ou4sNHB^{?K(iahrZlC z7d(T>Xilizfq7}kE};SvQ^d}l*ht;DZTGQnmD!0+s*$GA>8|*GGaS4!>g5e8}`38hF^h6Ih%uD zi@_hhkB`B$4rk-DFtF0QfDmNnDDb3nefi{CcPszYU)USrEf`0d%Kq7=H#{ znUh7B1*5k&5E9|5oACrLJ|gf3;SBzuR|0V=VartwL?)rm_(EEe>!Cx?Ao0#nEOd>i z>bL-XCT3ewU^}9dW*FW~crSVfR}(HXXTTc50Q>|#Luf!`;wKQyWd-#uoC00`~VNrn@-EY`*epxtHD1s)!;s81#M}^7AS^l zYPbwlQ@u4|kcM(Z1VN`Lg$x7qgEAc^!T#jEb>CqGxwdRNe30ywJ&K=CdbNEKzL+$$ zc|5+C#0wM>#*s=r-x3l@kykRonVfg0zJlHykAre>8@qJS6GX6D+xLSi=9h*f@Gvu8 zeH47dXcSF_oETFX{!j)T0oy|g+QT{>bf31pv=0uY9?Y(Ujnp+s7vP_imZ-z{G>Thb z7QUI>>UjkJlk9uB7gr*RJC%uJ3%4Bjj_VS{4#;uW`N{3RxIetY`ct3>7g9%o8634x z1oCYCPlv%%tfx>fG>bK_E(FSD2A5_+#~E4KOW^f%ToM&F(zH=T_z(3^;7WWB<%y>f zKSY^zc`Ejj{NBkv><^jhz)qZ-G`;@^E=^+Brp8f3FY5zwUBY9kVcbK3SvUh+#wXJ^ zgW0??C=9f4x7J<&zjNG5K0~FfZ#xe{x0xqwOv40*I4TUb<+;|s!Y`#Q^Q^)XD0eRH z#KveFPfo+4HOux7U>jA^{w(Zu<^0wkxT$i3S&s{qtyQhWAtYx75UxjDMB9h^A{qyd zg3$tX?GDhycPn8-v$?H1^C2e3dD}zi2TNjO8dfkquOEPK)3