├── CHANGELOG.md ├── LICENSE ├── README.md ├── composer.json └── src ├── Draw ├── AlphaBlendingAwareDrawerInterface.php └── DrawerInterface.php ├── Driver ├── AbstractInfo.php ├── Info.php └── InfoProvider.php ├── Effects └── EffectsInterface.php ├── Exception ├── Exception.php ├── InvalidArgumentException.php ├── NotFoundException.php ├── NotSupportedException.php ├── OutOfBoundsException.php └── RuntimeException.php ├── Factory ├── ClassFactory.php ├── ClassFactoryAwareInterface.php └── ClassFactoryInterface.php ├── File ├── Loader.php └── LoaderInterface.php ├── Filter ├── Advanced │ ├── BlackWhite.php │ ├── Border.php │ ├── BorderDetection.php │ ├── Canvas.php │ ├── Grayscale.php │ ├── Negation.php │ ├── Neighborhood.php │ ├── OnPixelBased.php │ └── RelativeResize.php ├── Basic │ ├── ApplyMask.php │ ├── Autorotate.php │ ├── Copy.php │ ├── Crop.php │ ├── Fill.php │ ├── FlipHorizontally.php │ ├── FlipVertically.php │ ├── Paste.php │ ├── Resize.php │ ├── Rotate.php │ ├── Save.php │ ├── Show.php │ ├── Strip.php │ ├── Thumbnail.php │ └── WebOptimization.php ├── FilterInterface.php ├── ImagineAware.php └── Transformation.php ├── Gd ├── Drawer.php ├── DriverInfo.php ├── Effects.php ├── Font.php ├── Image.php ├── Imagine.php └── Layers.php ├── Gmagick ├── Drawer.php ├── DriverInfo.php ├── Effects.php ├── Font.php ├── Image.php ├── Imagine.php └── Layers.php ├── Image ├── AbstractFont.php ├── AbstractImage.php ├── AbstractImagine.php ├── AbstractLayers.php ├── Box.php ├── BoxInterface.php ├── Fill │ ├── FillInterface.php │ └── Gradient │ │ ├── Horizontal.php │ │ ├── Linear.php │ │ └── Vertical.php ├── FontInterface.php ├── Format.php ├── FormatList.php ├── Histogram │ ├── Bucket.php │ └── Range.php ├── ImageInterface.php ├── ImagineInterface.php ├── LayersInterface.php ├── ManipulatorInterface.php ├── Metadata │ ├── AbstractMetadataReader.php │ ├── DefaultMetadataReader.php │ ├── ExifMetadataReader.php │ ├── MetadataBag.php │ └── MetadataReaderInterface.php ├── Palette │ ├── CMYK.php │ ├── Color │ │ ├── CMYK.php │ │ ├── ColorInterface.php │ │ ├── Gray.php │ │ └── RGB.php │ ├── ColorParser.php │ ├── Grayscale.php │ ├── PaletteInterface.php │ └── RGB.php ├── Point.php ├── Point │ └── Center.php ├── PointInterface.php ├── PointSigned.php ├── Profile.php └── ProfileInterface.php ├── Imagick ├── Drawer.php ├── DriverInfo.php ├── Effects.php ├── Font.php ├── Image.php ├── Imagine.php └── Layers.php ├── Utils ├── ErrorHandling.php └── Matrix.php └── resources ├── Adobe ├── CMYK │ ├── CoatedFOGRA27.icc │ ├── CoatedFOGRA39.icc │ ├── CoatedGRACoL2006.icc │ ├── JapanColor2001Coated.icc │ ├── JapanColor2001Uncoated.icc │ ├── JapanColor2002Newspaper.icc │ ├── JapanColor2003WebCoated.icc │ ├── JapanWebCoated.icc │ ├── USWebCoatedSWOP.icc │ ├── USWebUncoated.icc │ ├── UncoatedFOGRA29.icc │ ├── WebCoatedFOGRA28.icc │ ├── WebCoatedSWOP2006Grade3.icc │ └── WebCoatedSWOP2006Grade5.icc ├── Color Profile Bundling License_10.15.08.md ├── RGB │ ├── AdobeRGB1998.icc │ ├── AppleRGB.icc │ ├── ColorMatchRGB.icc │ ├── PAL_SECAM.icc │ ├── SMPTE-C.icc │ ├── VideoHD.icc │ ├── VideoNTSC.icc │ └── VideoPAL.icc └── Trademark Information.md ├── color.org ├── sRGB_IEC61966-2-1_black_scaled.icc └── sRGB_IEC61966-2-1_no_black_scaling.icc └── colormanagement.org └── ISOcoated_v2_grey1c_bas.ICC /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2004-2012 Bulat Shakirzyanov 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is furnished 8 | to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | 21 | This software embeds Adobe ICC Profiles, see license at 22 | http://www.adobe.com/support/downloads/iccprofiles/icc_eula_mac_dist.html . 23 | 24 | This software also embeds ICC Profile from colormanagement.org. Please 25 | find information about their license at http://colormanagement.org/ . 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Imagine 2 | [](https://github.com/php-imagine/Imagine/actions/workflows/phpunit.yml) 3 | [](https://github.com/php-imagine/Imagine/actions/workflows/coding-style.yml) 4 | 5 | Tweet about it using the [#php_imagine](https://twitter.com/search?q=%23php_imagine) hashtag. 6 | 7 | Image manipulation library for PHP inspired by Python's PIL and other image 8 | libraries. 9 | 10 | ## Requirements 11 | 12 | The Imagine library has the following requirements: 13 | 14 | - PHP 7.1+ 15 | 16 | Older version of the library support also older PHP Version: 17 | 18 | - PHP 5.5 - 7.0 use version ^1.3 19 | - PHP 5.3 - 5.4 use version ^1.2 20 | 21 | Depending on the chosen Image implementation, you may need one of the following PHP extensions: 22 | 23 | - GD2 24 | - Imagick (with ImageMagick version 6.2.9 or later, except version 7.0.7-32) 25 | - Gmagick 26 | 27 | To read EXIF metadata (e.g. for autorotation), activate the PHP ``exif`` extension. This is optional: Imagine works 28 | without the PHP ``exif`` extension, but then it can't read and act on image orientation or other EXIF metadata. 29 | 30 | ### Installation using composer 31 | `php composer.phar require imagine/imagine` 32 | 33 | ## Basic Principles 34 | 35 | The main purpose of Imagine is to provide all the necessary functionality to bring all native low level image processing libraries in PHP to the same simple and intuitive OO API. 36 | 37 | Several things are necessary to accomplish that: 38 | 39 | * Image manipulation tools, such as resize, crop, etc. 40 | * Drawing API - to create basic shapes and advanced charts, write text on the image 41 | * Masking functionality - ability to apply black&white or grayscale images as masks, leading to semi-transparency or absolute transparency of the image the mask is being applied to 42 | 43 | The above tools should be the basic foundation for a more powerful set of tools that are called ``Filters`` in Imagine. 44 | 45 | Some of the ideas for upcoming filters: 46 | 47 | * Charting and graphing filters - pie and bar charts, linear graphs with annotations 48 | * Reflection - apple style 49 | * Rounded corners - web 2.0 50 | 51 | ## Documentation ## 52 | 53 | - [Hosted by Read The Docs](http://imagine.readthedocs.org/) 54 | 55 | ## Presentations ## 56 | 57 | - [Introduction to Imagine](http://www.slideshare.net/avalanche123/introduction-toimagine) 58 | - [How to Take Over the World with Lithium](http://speakerdeck.com/u/nateabele/p/how-to-take-over-the-world-with-lithium?slide=33) 59 | 60 | ## Articles ## 61 | 62 | - [Image Processing with Imagine](http://www.phparch.com/2011/03/image-processing-with-imagine) 63 | 64 | ## Contributing ## 65 | 66 | ### Branches 67 | 68 | New pull requests should be based on the `develop` branch. 69 | The `master` branch is the stable branch: it usually matches the latest a release but in can be a bit ahead. 70 | 71 | ### Test groups 72 | 73 | Some PHPUnit test is marked as skipped (for example, tests that require a driver that support multiple layers and executed with the GD driver). 74 | In addition, if you don't have installed gmagick, the gmagick tests will be marked as skipped. 75 | 76 | If you don't want to run tests that are marked as "always skipped" you can tell PHPUnit to exclude the `always-skipped` group. 77 | The same for the tests that require a specific driver (`gd`, `imagick`, `imagick`). 78 | 79 | So, for example, to exclude the `always-skipped` and the `gmagick` tests, you can launch phpunit with this command options: 80 | 81 | ``` 82 | composer run test -- --exclude-group always-skipped,gmagick 83 | ``` 84 | 85 | 86 | ### Development environment 87 | 88 | Setting up an environment with all the required libraries may be very hard. 89 | In order to run the tests locally, you can use the same [docker images](https://github.com/php-imagine/docker-builder/pkgs/container/test) used by Imagine to test the pull requests. 90 | 91 | For example, if you have Imagine locally in the `/home/me/imagine` folder, you can run tests for PHP 8.1 with the GD and Imagick with this very simple approach: 92 | 93 | 1. Launch a temporary docker container with: 94 | ```sh 95 | docker run --rm -it -v /home/me/imagine:/app -w /app ghcr.io/php-imagine/test:8.1-gd-imagick bash 96 | ``` 97 | 2. Inside the docker container, run these commands: 98 | ```sh 99 | # Start a local web server: some tests require it 100 | cd tests 101 | php -n -S 0.0.0.0:8013 >/dev/null 2>&1 & 102 | cd .. 103 | # Tell the tests that the local web server is available at the port 8013 104 | export IMAGINE_TEST_WEBSERVERURL=http://localhost:8013 105 | # Install the composer dependencies 106 | composer update 107 | # Run the tests 108 | composer run test -- --exclude-group always-skipped,gmagick 109 | ``` 110 | 111 | > Note: This approach works on Windows too: simply launch the docker container with 112 | > ``` 113 | > docker run --rm -it -v C:\Path\To\Imagine:/app -w /app ghcr.io/php-imagine/test:8.1-gd-imagick bash 114 | > ``` 115 | 116 | ### Built test files 117 | 118 | Many tests create temporary files (in the `tests/tmp` directory) containing built images. 119 | Those temporary files are compared with expected images, and then are deleted. 120 | If you want to keep those temporary files (for example, to check what's being built), you can set the `IMAGINE_TEST_KEEP_TEMPFILES` environment variable. 121 | If the `IMAGINE_TEST_KEEP_TEMPFILES` is configured in the GitHub Action tests, those temporary files are attached to tests as an articact. 122 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "imagine/imagine", 3 | "description": "Image processing for PHP", 4 | "keywords": [ 5 | "image manipulation", 6 | "image processing", 7 | "drawing", 8 | "graphics" 9 | ], 10 | "homepage": "http://imagine.readthedocs.org/", 11 | "license": "MIT", 12 | "authors": [ 13 | { 14 | "name": "Bulat Shakirzyanov", 15 | "email": "mallluhuct@gmail.com", 16 | "homepage": "http://avalanche123.com" 17 | } 18 | ], 19 | "require": { 20 | "php": ">=7.1" 21 | }, 22 | "require-dev": { 23 | "phpunit/phpunit": "^4.8 || ^5.7 || ^6.5 || ^7.5 || ^8.4 || ^9.3" 24 | }, 25 | "suggest": { 26 | "ext-exif": "to read EXIF metadata", 27 | "ext-gd": "to use the GD implementation", 28 | "ext-imagick": "to use the Imagick implementation", 29 | "ext-gmagick": "to use the Gmagick implementation" 30 | }, 31 | "autoload": { 32 | "psr-4": { 33 | "Imagine\\": "src/" 34 | } 35 | }, 36 | "autoload-dev": { 37 | "psr-4": { 38 | "Imagine\\Test\\": "tests/tests/" 39 | } 40 | }, 41 | "extra": { 42 | "branch-alias": { 43 | "dev-develop": "1.x-dev" 44 | } 45 | }, 46 | "archive": { 47 | "exclude": [ 48 | "/.*", 49 | "/tests", 50 | "/vendor", 51 | "/bin", 52 | "docs/_build", 53 | "Imagine-*.tgz", 54 | "imagine-*.phar", 55 | "composer.phar" 56 | ] 57 | }, 58 | "scripts": { 59 | "test": "phpunit --verbose" 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/Draw/AlphaBlendingAwareDrawerInterface.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | namespace Imagine\Draw; 4 | 5 | /** 6 | * Interface for the drawers that support configuring the alpha blending. 7 | */ 8 | interface AlphaBlendingAwareDrawerInterface extends DrawerInterface 9 | { 10 | /** 11 | * Is the alpha blending activated? 12 | * 13 | * @return bool 14 | */ 15 | public function getAlphaBlending(); 16 | 17 | /** 18 | * Enable/disable the alpha blending. 19 | * 20 | * @param bool $value 21 | * 22 | * @return $this 23 | */ 24 | public function setAlphaBlending($value); 25 | 26 | /** 27 | * Create a new instance of this drawer with the specified alpha blending value. 28 | * 29 | * @param bool $value 30 | * 31 | * @return static 32 | */ 33 | public function withAlphaBlending($value); 34 | } 35 | -------------------------------------------------------------------------------- /src/Draw/DrawerInterface.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | /* 4 | * This file is part of the Imagine package. 5 | * 6 | * (c) Bulat Shakirzyanov <mallluhuct@gmail.com> 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Imagine\Draw; 13 | 14 | use Imagine\Image\AbstractFont; 15 | use Imagine\Image\BoxInterface; 16 | use Imagine\Image\Palette\Color\ColorInterface; 17 | use Imagine\Image\PointInterface; 18 | 19 | /** 20 | * Interface for the drawer. 21 | */ 22 | interface DrawerInterface 23 | { 24 | /** 25 | * Draws an arc on a starting at a given x, y coordinates under a given 26 | * start and end angles. 27 | * 28 | * @param \Imagine\Image\PointInterface $center 29 | * @param \Imagine\Image\BoxInterface $size 30 | * @param int $start 31 | * @param int $end 32 | * @param \Imagine\Image\Palette\Color\ColorInterface $color 33 | * @param int $thickness 34 | * 35 | * @throws \Imagine\Exception\RuntimeException 36 | * 37 | * @return $this 38 | */ 39 | public function arc(PointInterface $center, BoxInterface $size, $start, $end, ColorInterface $color, $thickness = 1); 40 | 41 | /** 42 | * Same as arc, but also connects end points with a straight line. 43 | * 44 | * @param \Imagine\Image\PointInterface $center 45 | * @param \Imagine\Image\BoxInterface $size 46 | * @param int $start 47 | * @param int $end 48 | * @param \Imagine\Image\Palette\Color\ColorInterface $color 49 | * @param bool $fill 50 | * @param int $thickness 51 | * 52 | * @throws \Imagine\Exception\RuntimeException 53 | * 54 | * @return $this 55 | */ 56 | public function chord(PointInterface $center, BoxInterface $size, $start, $end, ColorInterface $color, $fill = false, $thickness = 1); 57 | 58 | /** 59 | * Draws and circle with center at the given x, y coordinates, and given radius. 60 | * 61 | * @param \Imagine\Image\PointInterface $center 62 | * @param int $radius 63 | * @param \Imagine\Image\Palette\Color\ColorInterface $color 64 | * @param bool $fill 65 | * @param int $thickness 66 | * 67 | * @throws \Imagine\Exception\RuntimeException 68 | * 69 | * @return $this 70 | */ 71 | public function circle(PointInterface $center, $radius, ColorInterface $color, $fill = false, $thickness = 1); 72 | 73 | /** 74 | * Draws and ellipse with center at the given x, y coordinates, and given width and height. 75 | * 76 | * @param \Imagine\Image\PointInterface $center 77 | * @param \Imagine\Image\BoxInterface $size 78 | * @param \Imagine\Image\Palette\Color\ColorInterface $color 79 | * @param bool $fill 80 | * @param int $thickness 81 | * 82 | * @throws \Imagine\Exception\RuntimeException 83 | * 84 | * @return $this 85 | */ 86 | public function ellipse(PointInterface $center, BoxInterface $size, ColorInterface $color, $fill = false, $thickness = 1); 87 | 88 | /** 89 | * Draws a line from start(x, y) to end(x, y) coordinates. 90 | * 91 | * @param \Imagine\Image\PointInterface $start 92 | * @param \Imagine\Image\PointInterface $end 93 | * @param \Imagine\Image\Palette\Color\ColorInterface $outline 94 | * @param int $thickness 95 | * 96 | * @return $this 97 | */ 98 | public function line(PointInterface $start, PointInterface $end, ColorInterface $outline, $thickness = 1); 99 | 100 | /** 101 | * Same as arc, but connects end points and the center. 102 | * 103 | * @param \Imagine\Image\PointInterface $center 104 | * @param \Imagine\Image\BoxInterface $size 105 | * @param int $start 106 | * @param int $end 107 | * @param \Imagine\Image\Palette\Color\ColorInterface $color 108 | * @param bool $fill 109 | * @param int $thickness 110 | * 111 | * @throws \Imagine\Exception\RuntimeException 112 | * 113 | * @return $this 114 | */ 115 | public function pieSlice(PointInterface $center, BoxInterface $size, $start, $end, ColorInterface $color, $fill = false, $thickness = 1); 116 | 117 | /** 118 | * Places a one pixel point at specific coordinates and fills it with 119 | * specified color. 120 | * 121 | * @param \Imagine\Image\PointInterface $position 122 | * @param \Imagine\Image\Palette\Color\ColorInterface $color 123 | * 124 | * @throws \Imagine\Exception\RuntimeException 125 | * 126 | * @return $this 127 | */ 128 | public function dot(PointInterface $position, ColorInterface $color); 129 | 130 | /** 131 | * Draws a rectangle from left, top(x, y) to right, bottom(x, y) coordinates. 132 | * 133 | * @param \Imagine\Image\PointInterface $leftTop 134 | * @param \Imagine\Image\PointInterface $rightBottom 135 | * @param \Imagine\Image\Palette\Color\ColorInterface $color 136 | * @param bool $fill 137 | * @param int $thickness 138 | * 139 | * @throws \Imagine\Exception\RuntimeException 140 | * 141 | * @return $this 142 | */ 143 | public function rectangle(PointInterface $leftTop, PointInterface $rightBottom, ColorInterface $color, $fill = false, $thickness = 1); 144 | 145 | /** 146 | * Draws a polygon using array of x, y coordinates. Must contain at least three coordinates. 147 | * 148 | * @param \Imagine\Image\PointInterface[] $coordinates 149 | * @param \Imagine\Image\Palette\Color\ColorInterface $color 150 | * @param bool $fill 151 | * @param int $thickness 152 | * 153 | * @throws \Imagine\Exception\RuntimeException 154 | * 155 | * @return $this 156 | */ 157 | public function polygon(array $coordinates, ColorInterface $color, $fill = false, $thickness = 1); 158 | 159 | /** 160 | * Annotates image with specified text at a given position starting on the top left of the final text box. 161 | * 162 | * The rotation is done CW 163 | * 164 | * @param string $string 165 | * @param \Imagine\Image\AbstractFont $font 166 | * @param \Imagine\Image\PointInterface $position 167 | * @param int $angle 168 | * @param int $width 169 | * 170 | * @throws \Imagine\Exception\RuntimeException 171 | * 172 | * @return $this 173 | */ 174 | public function text($string, AbstractFont $font, PointInterface $position, $angle = 0, $width = null); 175 | } 176 | -------------------------------------------------------------------------------- /src/Driver/AbstractInfo.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | namespace Imagine\Driver; 4 | 5 | use Imagine\Exception\NotSupportedException; 6 | use Imagine\Image\Palette\PaletteInterface; 7 | 8 | /** 9 | * Base class for the default DriverInfo classes. 10 | * 11 | * @since 1.3.0 12 | */ 13 | abstract class AbstractInfo implements Info 14 | { 15 | /** 16 | * @var static|\Imagine\Exception\NotSupportedException|null 17 | */ 18 | private static $instance; 19 | 20 | /** 21 | * @var string 22 | */ 23 | private $driverRawVersion; 24 | 25 | /** 26 | * @var string 27 | */ 28 | private $driverSemverVersion; 29 | 30 | /** 31 | * @var string 32 | */ 33 | private $engineRawVersion; 34 | 35 | /** 36 | * @var string 37 | */ 38 | private $engineSemverVersion; 39 | 40 | /** 41 | * @var \Imagine\Image\FormatList|null 42 | */ 43 | private $supportedFormats = null; 44 | 45 | /** 46 | * @param string $driverRawVersion 47 | * @param string $driverSemverVersion 48 | * @param string $engineRawVersion 49 | * @param string $engineSemverVersion 50 | */ 51 | protected function __construct($driverRawVersion, $driverSemverVersion, $engineRawVersion, $engineSemverVersion) 52 | { 53 | $this->driverRawVersion = $driverRawVersion; 54 | $this->driverSemverVersion = $driverSemverVersion; 55 | $this->engineRawVersion = $engineRawVersion; 56 | $this->engineSemverVersion = $engineSemverVersion; 57 | } 58 | 59 | /** 60 | * {@inheritdoc} 61 | * 62 | * @see \Imagine\Driver\Info::getDriverVersion() 63 | */ 64 | public function getDriverVersion($raw = false) 65 | { 66 | return $raw ? $this->driverRawVersion : $this->driverSemverVersion; 67 | } 68 | 69 | /** 70 | * {@inheritdoc} 71 | * 72 | * @see \Imagine\Driver\Info::getEngineVersion() 73 | */ 74 | public function getEngineVersion($raw = false) 75 | { 76 | return $raw ? $this->engineRawVersion : $this->engineSemverVersion; 77 | } 78 | 79 | /** 80 | * Check if the driver has a specific feature. 81 | * 82 | * @param int $feature The feature to be checked (see the Info::FEATURE_... constants) 83 | * 84 | * @throws \Imagine\Exception\NotSupportedException if any of the requested features is not supported 85 | */ 86 | abstract protected function checkFeature($feature); 87 | 88 | /** 89 | * {@inheritdoc} 90 | * 91 | * @see \Imagine\Driver\Info::checkVersionIsSupported() 92 | */ 93 | public function checkVersionIsSupported() 94 | { 95 | if (!defined('PHP_VERSION_ID') || PHP_VERSION_ID < 70100) { 96 | throw new NotSupportedException('Imagine requires PHP 7.1 or later'); 97 | } 98 | } 99 | 100 | /** 101 | * {@inheritdoc} 102 | * 103 | * @see \Imagine\Driver\Info::requireFeature() 104 | */ 105 | public function requireFeature($features) 106 | { 107 | $features = array_map('intval', is_array($features) ? $features : array($features)); 108 | foreach ($features as $feature) { 109 | $this->checkFeature($feature); 110 | } 111 | } 112 | 113 | /** 114 | * {@inheritdoc} 115 | * 116 | * @see \Imagine\Driver\Info::hasFeature() 117 | */ 118 | public function hasFeature($features) 119 | { 120 | try { 121 | $this->requireFeature($features); 122 | } catch (NotSupportedException $x) { 123 | return false; 124 | } 125 | 126 | return true; 127 | } 128 | 129 | /** 130 | * Build the list of supported file formats. 131 | * 132 | * @return \Imagine\Image\FormatList 133 | */ 134 | abstract protected function buildSupportedFormats(); 135 | 136 | /** 137 | * {@inheritdoc} 138 | * 139 | * @see \Imagine\Driver\Info::getSupportedFormats() 140 | */ 141 | public function getSupportedFormats() 142 | { 143 | if ($this->supportedFormats === null) { 144 | $this->supportedFormats = $this->buildSupportedFormats(); 145 | } 146 | 147 | return $this->supportedFormats; 148 | } 149 | 150 | /** 151 | * {@inheritdoc} 152 | * 153 | * @see \Imagine\Driver\Info::isFormatSupported() 154 | */ 155 | public function isFormatSupported($format) 156 | { 157 | return $this->getSupportedFormats()->find($format) !== null; 158 | } 159 | 160 | /** 161 | * {@inheritdoc} 162 | * 163 | * @see \Imagine\Driver\Info::requirePaletteSupport() 164 | */ 165 | public function requirePaletteSupport(PaletteInterface $palette) 166 | { 167 | } 168 | 169 | /** 170 | * {@inheritdoc} 171 | * 172 | * @see \Imagine\Driver\Info::isPaletteSupported() 173 | */ 174 | public function isPaletteSupported(PaletteInterface $palette) 175 | { 176 | try { 177 | $this->requirePaletteSupport($palette); 178 | } catch (NotSupportedException $x) { 179 | return false; 180 | } 181 | 182 | return true; 183 | } 184 | } 185 | -------------------------------------------------------------------------------- /src/Driver/InfoProvider.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | namespace Imagine\Driver; 4 | 5 | /** 6 | * Interface implemented by classes that provide info about a graphics driver. 7 | * 8 | * @since 1.3.0 9 | */ 10 | interface InfoProvider 11 | { 12 | /** 13 | * Get the info about this driver. 14 | * 15 | * @param bool $required when the driver is not available: if FALSE the function returns NULL, if TRUE the driver throws a \Imagine\Exception\NotSupportedException 16 | * 17 | * @return \Imagine\Driver\Info|null 18 | */ 19 | public static function getDriverInfo($required = true); 20 | } 21 | -------------------------------------------------------------------------------- /src/Effects/EffectsInterface.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | /* 4 | * This file is part of the Imagine package. 5 | * 6 | * (c) Bulat Shakirzyanov <mallluhuct@gmail.com> 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Imagine\Effects; 13 | 14 | use Imagine\Image\Palette\Color\ColorInterface; 15 | use Imagine\Utils\Matrix; 16 | 17 | /** 18 | * Interface for the effects. 19 | */ 20 | interface EffectsInterface 21 | { 22 | /** 23 | * Apply gamma correction. 24 | * 25 | * @param float $correction 26 | * 27 | * @throws \Imagine\Exception\RuntimeException 28 | * 29 | * @return $this 30 | */ 31 | public function gamma($correction); 32 | 33 | /** 34 | * Invert the colors of the image. 35 | * 36 | * @throws \Imagine\Exception\RuntimeException 37 | * 38 | * @return $this 39 | */ 40 | public function negative(); 41 | 42 | /** 43 | * Grayscale the image. 44 | * 45 | * @throws \Imagine\Exception\RuntimeException 46 | * 47 | * @return $this 48 | */ 49 | public function grayscale(); 50 | 51 | /** 52 | * Colorize the image. 53 | * 54 | * @param \Imagine\Image\Palette\Color\ColorInterface $color 55 | * 56 | * @throws \Imagine\Exception\RuntimeException 57 | * 58 | * @return $this 59 | */ 60 | public function colorize(ColorInterface $color); 61 | 62 | /** 63 | * Sharpens the image. 64 | * 65 | * @throws \Imagine\Exception\RuntimeException 66 | * 67 | * @return $this 68 | */ 69 | public function sharpen(); 70 | 71 | /** 72 | * Blur the image. 73 | * 74 | * @param float|int $sigma 75 | * 76 | * @throws \Imagine\Exception\RuntimeException 77 | * 78 | * @return $this 79 | */ 80 | public function blur($sigma); 81 | 82 | /** 83 | * Changes the brightness of the image. 84 | * 85 | * @param int $brightness The level of brightness (-100 (black) to 100 (white)) 86 | * 87 | * @throws \Imagine\Exception\RuntimeException 88 | * 89 | * @return $this 90 | */ 91 | public function brightness($brightness); 92 | 93 | /** 94 | * Convolves the image. 95 | * 96 | * @param \Imagine\Utils\Matrix $matrix The matrix from which derive the convolution kernel 97 | * 98 | * @throws \Imagine\Exception\RuntimeException 99 | * 100 | * @return $this 101 | */ 102 | public function convolve(Matrix $matrix); 103 | } 104 | -------------------------------------------------------------------------------- /src/Exception/Exception.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | /* 4 | * This file is part of the Imagine package. 5 | * 6 | * (c) Bulat Shakirzyanov <mallluhuct@gmail.com> 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Imagine\Exception; 13 | 14 | /** 15 | * Imagine-specific exception. 16 | */ 17 | interface Exception 18 | { 19 | } 20 | -------------------------------------------------------------------------------- /src/Exception/InvalidArgumentException.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | /* 4 | * This file is part of the Imagine package. 5 | * 6 | * (c) Bulat Shakirzyanov <mallluhuct@gmail.com> 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Imagine\Exception; 13 | 14 | /** 15 | * Imagine-specific invalid argument exception. 16 | */ 17 | class InvalidArgumentException extends \InvalidArgumentException implements Exception 18 | { 19 | } 20 | -------------------------------------------------------------------------------- /src/Exception/NotFoundException.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | /* 4 | * This file is part of the Imagine package. 5 | * 6 | * (c) Bulat Shakirzyanov <mallluhuct@gmail.com> 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Imagine\Exception; 13 | 14 | /** 15 | * Imagine-specific invalid not found exception. 16 | */ 17 | class NotFoundException extends InvalidArgumentException 18 | { 19 | } 20 | -------------------------------------------------------------------------------- /src/Exception/NotSupportedException.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | /* 4 | * This file is part of the Imagine package. 5 | * 6 | * (c) Bulat Shakirzyanov <mallluhuct@gmail.com> 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Imagine\Exception; 13 | 14 | /** 15 | * Should be used when a driver does not support an operation. 16 | */ 17 | class NotSupportedException extends RuntimeException implements Exception 18 | { 19 | } 20 | -------------------------------------------------------------------------------- /src/Exception/OutOfBoundsException.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | /* 4 | * This file is part of the Imagine package. 5 | * 6 | * (c) Bulat Shakirzyanov <mallluhuct@gmail.com> 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Imagine\Exception; 13 | 14 | /** 15 | * Imagine-specific out of bounds exception. 16 | */ 17 | class OutOfBoundsException extends \OutOfBoundsException implements Exception 18 | { 19 | } 20 | -------------------------------------------------------------------------------- /src/Exception/RuntimeException.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | /* 4 | * This file is part of the Imagine package. 5 | * 6 | * (c) Bulat Shakirzyanov <mallluhuct@gmail.com> 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Imagine\Exception; 13 | 14 | /** 15 | * Imagine-specific runtime exception. 16 | */ 17 | class RuntimeException extends \RuntimeException implements Exception 18 | { 19 | } 20 | -------------------------------------------------------------------------------- /src/Factory/ClassFactoryAwareInterface.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | namespace Imagine\Factory; 4 | 5 | /** 6 | * An interface that classes that accepts a class factory should implement. 7 | */ 8 | interface ClassFactoryAwareInterface 9 | { 10 | /** 11 | * Set the class factory instance to be used. 12 | * 13 | * @param \Imagine\Factory\ClassFactoryInterface $classFactory 14 | * 15 | * @return $this 16 | */ 17 | public function setClassFactory(ClassFactoryInterface $classFactory); 18 | 19 | /** 20 | * Get the class factory instance to be used. 21 | * 22 | * @return \Imagine\Factory\ClassFactoryInterface 23 | */ 24 | public function getClassFactory(); 25 | } 26 | -------------------------------------------------------------------------------- /src/Factory/ClassFactoryInterface.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | namespace Imagine\Factory; 4 | 5 | use Imagine\Image\Palette\Color\ColorInterface; 6 | 7 | /** 8 | * The interface that class factories must implement. 9 | */ 10 | interface ClassFactoryInterface 11 | { 12 | /** 13 | * The handle to be used for the GD manipulation library. 14 | * 15 | * @var string 16 | */ 17 | const HANDLE_GD = 'gd'; 18 | 19 | /** 20 | * The handle to be used for the Gmagick manipulation library. 21 | * 22 | * @var string 23 | */ 24 | const HANDLE_GMAGICK = 'gmagick'; 25 | 26 | /** 27 | * The handle to be used for the Imagick manipulation library. 28 | * 29 | * @var string 30 | */ 31 | const HANDLE_IMAGICK = 'imagick'; 32 | 33 | /** 34 | * Create a new instance of a metadata reader. 35 | * 36 | * @return \Imagine\Image\Metadata\MetadataReaderInterface 37 | */ 38 | public function createMetadataReader(); 39 | 40 | /** 41 | * Create new BoxInterface instance. 42 | * 43 | * @param int $width The box width 44 | * @param int $height The box height 45 | * 46 | * @return \Imagine\Image\BoxInterface 47 | */ 48 | public function createBox($width, $height); 49 | 50 | /** 51 | * Create new FontInterface instance. 52 | * 53 | * @param string $handle The handle that identifies the manipulation library (one of the HANDLE_... constants, or your own implementation). 54 | * @param string $file 55 | * @param int $size the font size in points (e.g. 10pt means 10) 56 | * @param \Imagine\Image\Palette\Color\ColorInterface $color 57 | * 58 | * @return \Imagine\Image\FontInterface 59 | */ 60 | public function createFont($handle, $file, $size, ColorInterface $color); 61 | 62 | /** 63 | * Create a new instance of a file loader. 64 | * 65 | * @param string|mixed $path 66 | * 67 | * @return \Imagine\File\LoaderInterface 68 | */ 69 | public function createFileLoader($path); 70 | 71 | /** 72 | * Crate a new instance of a layers interface. 73 | * 74 | * @param string $handle The handle that identifies the manipulation library (one of the HANDLE_... constants, or your own implementation). 75 | * @param \Imagine\Image\ImageInterface $image 76 | * @param mixed|null $initialKey the key of the initially selected layer 77 | * 78 | * @return \Imagine\Image\LayersInterface 79 | */ 80 | public function createLayers($handle, \Imagine\Image\ImageInterface $image, $initialKey = null); 81 | 82 | /** 83 | * Create a new ImageInterface instance. 84 | * 85 | * @param string $handle The handle that identifies the manipulation library (one of the HANDLE_... constants, or your own implementation). 86 | * @param mixed $resource 87 | * @param \Imagine\Image\Palette\PaletteInterface $palette 88 | * @param \Imagine\Image\Metadata\MetadataBag $metadata 89 | * 90 | * @return \Imagine\Image\ImageInterface 91 | */ 92 | public function createImage($handle, $resource, \Imagine\Image\Palette\PaletteInterface $palette, \Imagine\Image\Metadata\MetadataBag $metadata); 93 | 94 | /** 95 | * Create a new DrawerInterface instance. 96 | * 97 | * @param string $handle The handle that identifies the manipulation library (one of the HANDLE_... constants, or your own implementation). 98 | * @param mixed $resource 99 | * 100 | * @return \Imagine\Draw\DrawerInterface 101 | */ 102 | public function createDrawer($handle, $resource); 103 | 104 | /** 105 | * Create a new EffectsInterface instance. 106 | * 107 | * @param string $handle The handle that identifies the manipulation library (one of the HANDLE_... constants, or your own implementation). 108 | * @param mixed $resource 109 | * 110 | * @return \Imagine\Effects\EffectsInterface 111 | */ 112 | public function createEffects($handle, $resource); 113 | } 114 | -------------------------------------------------------------------------------- /src/File/LoaderInterface.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | /* 4 | * This file is part of the Imagine package. 5 | * 6 | * For the full copyright and license information, please view the LICENSE file that was distributed with this source code. 7 | */ 8 | 9 | namespace Imagine\File; 10 | 11 | /** 12 | * Interface for classes that can load local or remote files. 13 | */ 14 | interface LoaderInterface 15 | { 16 | /** 17 | * Is this a local file. 18 | * 19 | * @return bool 20 | */ 21 | public function isLocalFile(); 22 | 23 | /** 24 | * Get the path of the file (local or remote). 25 | * 26 | * @return string 27 | */ 28 | public function getPath(); 29 | 30 | /** 31 | * Is the binary content already loaded? 32 | * 33 | * @return bool 34 | */ 35 | public function hasReadData(); 36 | 37 | /** 38 | * Get the file binary contents. 39 | * 40 | * @return string 41 | */ 42 | public function getData(); 43 | 44 | /** 45 | * The string representation of this object must be the file path (local or remote). 46 | * 47 | * @return string 48 | */ 49 | public function __toString(); 50 | } 51 | -------------------------------------------------------------------------------- /src/Filter/Advanced/BlackWhite.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | /* 4 | * This file is part of the Imagine package. 5 | * 6 | * (c) Bulat Shakirzyanov <mallluhuct@gmail.com> 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Imagine\Filter\Advanced; 13 | 14 | use Imagine\Exception\InvalidArgumentException; 15 | use Imagine\Filter\FilterInterface; 16 | use Imagine\Image\ImageInterface; 17 | use Imagine\Image\Palette\Color\ColorInterface; 18 | use Imagine\Image\Palette\RGB; 19 | use Imagine\Image\Point; 20 | 21 | /** 22 | * This filter calculates, for each pixel of an image, whether it is ligher or darker than a threshold. 23 | * If the pixel is lighter than the thresold it will be black, otherwise it will be light. 24 | * The result is an image with only black and white pixels (black pixels for ligher colors, white pixels for darker colors). 25 | */ 26 | class BlackWhite extends OnPixelBased implements FilterInterface 27 | { 28 | /** 29 | * @var \Imagine\Filter\Advanced\Grayscale 30 | */ 31 | protected $grayScaleFilter; 32 | 33 | /** 34 | * Initialize this filter. 35 | * 36 | * @param int $threshold the dask/light threshold, from 0 (all black) to 255 (all white) 37 | * 38 | * @throws \Imagine\Exception\InvalidArgumentException 39 | */ 40 | public function __construct($threshold) 41 | { 42 | if (!($threshold >= 0 && $threshold <= 255)) { 43 | throw new InvalidArgumentException('$threshold has to be between 0 and 255'); 44 | } 45 | 46 | $this->grayScaleFilter = new Grayscale(); 47 | 48 | $rgb = new RGB(); 49 | parent::__construct( 50 | function (ImageInterface $image, Point $point) use ($threshold, $rgb) { 51 | $newRedValue = $image->getColorAt($point)->getValue(ColorInterface::COLOR_RED) < $threshold ? 255 : 0; 52 | $image->draw()->dot($point, $rgb->color(array($newRedValue, $newRedValue, $newRedValue))); 53 | } 54 | ); 55 | } 56 | 57 | /** 58 | * {@inheritdoc} 59 | * 60 | * @see \Imagine\Filter\Advanced\OnPixelBased::apply() 61 | */ 62 | public function apply(ImageInterface $image) 63 | { 64 | $grayScaledImage = $this->grayScaleFilter->apply($image); 65 | 66 | return parent::apply($grayScaledImage); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/Filter/Advanced/Border.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | /* 4 | * This file is part of the Imagine package. 5 | * 6 | * (c) Bulat Shakirzyanov <mallluhuct@gmail.com> 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Imagine\Filter\Advanced; 13 | 14 | use Imagine\Filter\FilterInterface; 15 | use Imagine\Image\ImageInterface; 16 | use Imagine\Image\Palette\Color\ColorInterface; 17 | use Imagine\Image\Point; 18 | 19 | /** 20 | * A border filter. 21 | */ 22 | class Border implements FilterInterface 23 | { 24 | /** 25 | * @var \Imagine\Image\Palette\Color\ColorInterface 26 | */ 27 | private $color; 28 | 29 | /** 30 | * @var int 31 | */ 32 | private $width; 33 | 34 | /** 35 | * @var int 36 | */ 37 | private $height; 38 | 39 | /** 40 | * Constructs Border filter with given color, width and height. 41 | * 42 | * @param \Imagine\Image\Palette\Color\ColorInterface $color 43 | * @param int $width Width of the border on the left and right sides of the image 44 | * @param int $height Height of the border on the top and bottom sides of the image 45 | */ 46 | public function __construct(ColorInterface $color, $width = 1, $height = 1) 47 | { 48 | $this->color = $color; 49 | $this->width = $width; 50 | $this->height = $height; 51 | } 52 | 53 | /** 54 | * {@inheritdoc} 55 | * 56 | * @see \Imagine\Filter\FilterInterface::apply() 57 | */ 58 | public function apply(ImageInterface $image) 59 | { 60 | $size = $image->getSize(); 61 | $width = $size->getWidth(); 62 | $height = $size->getHeight(); 63 | 64 | $draw = $image->draw(); 65 | 66 | // Draw top and bottom lines 67 | $draw 68 | ->line( 69 | new Point(0, 0), 70 | new Point($width - 1, 0), 71 | $this->color, 72 | $this->height 73 | ) 74 | ->line( 75 | new Point($width - 1, $height - 1), 76 | new Point(0, $height - 1), 77 | $this->color, 78 | $this->height 79 | ) 80 | ; 81 | 82 | // Draw sides 83 | $draw 84 | ->line( 85 | new Point(0, 0), 86 | new Point(0, $height - 1), 87 | $this->color, 88 | $this->width 89 | ) 90 | ->line( 91 | new Point($width - 1, 0), 92 | new Point($width - 1, $height - 1), 93 | $this->color, 94 | $this->width 95 | ) 96 | ; 97 | 98 | return $image; 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/Filter/Advanced/BorderDetection.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | /* 4 | * This file is part of the Imagine package. 5 | * 6 | * (c) Bulat Shakirzyanov <mallluhuct@gmail.com> 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Imagine\Filter\Advanced; 13 | 14 | use Imagine\Exception\InvalidArgumentException; 15 | use Imagine\Filter\FilterInterface; 16 | use Imagine\Utils\Matrix; 17 | 18 | /** 19 | * BorderDetection based on Laplace-Operator. Three different variants are offered:. 20 | * <code><pre> 21 | * First Second Third 22 | * 0, 1, 0 1, 1, 1, -1, 2, -1, 23 | * 1, -4, 1 and 1, -8, 1, and 2, -4, 2, 24 | * 0, 1, 0 1, 1, 1 -1, 2, -1 25 | * </pre></code>. 26 | * 27 | * Consider to apply this filter on a grayscaled image. 28 | */ 29 | class BorderDetection extends Neighborhood implements FilterInterface 30 | { 31 | /** 32 | * First variant of the detection matrix. 33 | * 34 | * @var int 35 | */ 36 | const VARIANT_ONE = 0; 37 | 38 | /** 39 | * Second variant of the detection matrix. 40 | * 41 | * @var int 42 | */ 43 | const VARIANT_TWO = 1; 44 | 45 | /** 46 | * Third variant of the detection matrix. 47 | * 48 | * @var int 49 | */ 50 | const VARIANT_THREE = 2; 51 | 52 | /** 53 | * Initialize this filter. 54 | * 55 | * @param int $variant One of the BorderDetection::VARIANT_... constants. 56 | * 57 | * @throws \Imagine\Exception\InvalidArgumentException throws an InvalidArgumentException if $variant is not valid 58 | */ 59 | public function __construct($variant = self::VARIANT_ONE) 60 | { 61 | $matrix = null; 62 | 63 | switch ($variant) { 64 | case self::VARIANT_ONE: 65 | $matrix = new Matrix(3, 3, array( 66 | 0, 1, 0, 67 | 1, -4, 1, 68 | 0, 1, 0, 69 | )); 70 | break; 71 | case self::VARIANT_TWO: 72 | $matrix = new Matrix(3, 3, array( 73 | 1, 1, 1, 74 | 1, -8, 1, 75 | 1, 1, 1, 76 | )); 77 | break; 78 | case self::VARIANT_THREE: 79 | $matrix = new Matrix(3, 3, array( 80 | -1, 2, -1, 81 | 2, -4, 2, 82 | -1, 2, -1, 83 | )); 84 | break; 85 | default: 86 | throw new InvalidArgumentException('Unknown variant ' . $variant); 87 | } 88 | 89 | parent::__construct($matrix); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/Filter/Advanced/Canvas.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | /* 4 | * This file is part of the Imagine package. 5 | * 6 | * (c) Bulat Shakirzyanov <mallluhuct@gmail.com> 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Imagine\Filter\Advanced; 13 | 14 | use Imagine\Filter\FilterInterface; 15 | use Imagine\Image\BoxInterface; 16 | use Imagine\Image\ImageInterface; 17 | use Imagine\Image\ImagineInterface; 18 | use Imagine\Image\Palette\Color\ColorInterface; 19 | use Imagine\Image\Point; 20 | use Imagine\Image\PointInterface; 21 | 22 | /** 23 | * A canvas filter. 24 | */ 25 | class Canvas implements FilterInterface 26 | { 27 | /** 28 | * @var \Imagine\Image\BoxInterface 29 | */ 30 | private $size; 31 | 32 | /** 33 | * @var \Imagine\Image\PointInterface 34 | */ 35 | private $placement; 36 | 37 | /** 38 | * @var \Imagine\Image\Palette\Color\ColorInterface 39 | */ 40 | private $background; 41 | 42 | /** 43 | * @var \Imagine\Image\ImagineInterface 44 | */ 45 | private $imagine; 46 | 47 | /** 48 | * Constructs Canvas filter with given width and height and the placement of the current image inside the new canvas. 49 | * 50 | * @param \Imagine\Image\ImagineInterface $imagine 51 | * @param \Imagine\Image\BoxInterface $size 52 | * @param \Imagine\Image\PointInterface $placement 53 | * @param \Imagine\Image\Palette\Color\ColorInterface $background 54 | */ 55 | public function __construct(ImagineInterface $imagine, BoxInterface $size, ?PointInterface $placement = null, ?ColorInterface $background = null) 56 | { 57 | $this->imagine = $imagine; 58 | $this->size = $size; 59 | $this->placement = $placement ?: new Point(0, 0); 60 | $this->background = $background; 61 | } 62 | 63 | /** 64 | * {@inheritdoc} 65 | * 66 | * @see \Imagine\Filter\FilterInterface::apply() 67 | */ 68 | public function apply(ImageInterface $image) 69 | { 70 | $canvas = $this->imagine->create($this->size, $this->background); 71 | $canvas->paste($image, $this->placement); 72 | 73 | return $canvas; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/Filter/Advanced/Grayscale.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | /* 4 | * This file is part of the Imagine package. 5 | * 6 | * (c) Bulat Shakirzyanov <mallluhuct@gmail.com> 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Imagine\Filter\Advanced; 13 | 14 | use Imagine\Filter\FilterInterface; 15 | use Imagine\Image\ImageInterface; 16 | use Imagine\Image\Point; 17 | 18 | /** 19 | * The Grayscale filter calculates the gray-value based on RGB. 20 | */ 21 | class Grayscale extends OnPixelBased implements FilterInterface 22 | { 23 | public function __construct() 24 | { 25 | parent::__construct(function (ImageInterface $image, Point $point) { 26 | $color = $image->getColorAt($point); 27 | $image->draw()->dot($point, $color->grayscale()); 28 | }); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Filter/Advanced/Negation.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | /* 4 | * This file is part of the Imagine package. 5 | * 6 | * (c) Bulat Shakirzyanov <mallluhuct@gmail.com> 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Imagine\Filter\Advanced; 13 | 14 | use Imagine\Filter\FilterInterface; 15 | use Imagine\Image\ImageInterface; 16 | 17 | /** 18 | * This filter negates every color of every pixel of an image. 19 | */ 20 | class Negation implements FilterInterface 21 | { 22 | /** 23 | * {@inheritdoc} 24 | * 25 | * @see \Imagine\Filter\FilterInterface::apply() 26 | */ 27 | public function apply(ImageInterface $image) 28 | { 29 | $image->effects()->negative(); 30 | 31 | return $image; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/Filter/Advanced/Neighborhood.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | /* 4 | * This file is part of the Imagine package. 5 | * 6 | * (c) Bulat Shakirzyanov <mallluhuct@gmail.com> 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Imagine\Filter\Advanced; 13 | 14 | use Imagine\Filter\FilterInterface; 15 | use Imagine\Image\ImageInterface; 16 | use Imagine\Image\Point; 17 | use Imagine\Utils\Matrix; 18 | 19 | /** 20 | * The Neighborhood filter takes a matrix and calculates the color current pixel based on its neighborhood. 21 | * 22 | * @example 23 | * <code><pre> 24 | * a, b, c 25 | * Matrix = d, e, f 26 | * g, h, i 27 | * </pre></code> 28 | * 29 | * and color{i, j} the color of the pixel at position (i, j). It calculates the color of pixel (x, y) like that: 30 | * 31 | * <code><pre> 32 | * color (x, y) = a * color(x - 1, y - 1) + b * color(x, y - 1) + c * color(x + 1, y - 1) 33 | * + d * color(x - 1, y) + e * color(x, y) + f * color(x + 1, y) 34 | * + g * color(x - 1, y + 1) + h * color(x, y + 1) + i * color(x + 1, y + 1) 35 | * </pre></code> 36 | */ 37 | class Neighborhood implements FilterInterface 38 | { 39 | /** 40 | * @var \Imagine\Utils\Matrix 41 | */ 42 | protected $matrix; 43 | 44 | /** 45 | * Initialize the instance. 46 | * 47 | * @param \Imagine\Utils\Matrix $matrix 48 | */ 49 | public function __construct(Matrix $matrix) 50 | { 51 | $this->matrix = $matrix; 52 | } 53 | 54 | /** 55 | * {@inheritdoc} 56 | * 57 | * @see \Imagine\Filter\FilterInterface::apply() 58 | */ 59 | public function apply(ImageInterface $image) 60 | { 61 | // We reduce the usage of methods on the image to dramatically increase the performance of this algorithm. 62 | // Really... We need that performance... 63 | // Therefore we first build a matrix, that holds the colors of the image. 64 | $width = $image->getSize()->getWidth(); 65 | $height = $image->getSize()->getHeight(); 66 | $byteData = new Matrix($width, $height); 67 | 68 | for ($y = 0; $y < $height; $y++) { 69 | for ($x = 0; $x < $width; $x++) { 70 | $byteData->setElementAt($x, $y, $image->getColorAt(new Point($x, $y))); 71 | } 72 | } 73 | 74 | $dWidth = (int) (($this->matrix->getWidth() - 1) / 2); 75 | $dHeight = (int) (($this->matrix->getHeight() - 1) / 2); 76 | 77 | // foreach point, which has a big enough neighborhood 78 | for ($y = $dHeight; $y < $height - $dHeight; $y++) { 79 | for ($x = $dWidth; $x < $width - $dWidth; $x++) { 80 | $currentColor = array_fill(0, count($image->palette()->pixelDefinition()), 0); 81 | 82 | // calculate the new color based on the neighborhood 83 | for ($boxY = $y - $dHeight, $matrixY = 0; $boxY <= $y + $dHeight; $boxY++, $matrixY++) { 84 | for ($boxX = $x - $dWidth, $matrixX = 0; $boxX <= $x + $dWidth; $boxX++, $matrixX++) { 85 | foreach ($image->palette()->pixelDefinition() as $index => $stream) { 86 | $currentColor[$index] += 87 | $byteData->getElementAt($boxX, $boxY)->getValue($stream) * 88 | $this->matrix->getElementAt($matrixX, $matrixY) 89 | ; 90 | } 91 | } 92 | } 93 | 94 | $image->draw()->dot(new Point($x, $y), $image->palette()->color($currentColor)); 95 | } 96 | } 97 | 98 | return $image; 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/Filter/Advanced/OnPixelBased.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | /* 4 | * This file is part of the Imagine package. 5 | * 6 | * (c) Bulat Shakirzyanov <mallluhuct@gmail.com> 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Imagine\Filter\Advanced; 13 | 14 | use Imagine\Exception\InvalidArgumentException; 15 | use Imagine\Filter\FilterInterface; 16 | use Imagine\Image\ImageInterface; 17 | use Imagine\Image\Point; 18 | 19 | /** 20 | * The OnPixelBased takes a callable, and for each pixel, this callable is called with the 21 | * image (\Imagine\Image\ImageInterface) and the current point (\Imagine\Image\Point). 22 | */ 23 | class OnPixelBased implements FilterInterface 24 | { 25 | /** 26 | * @var callable 27 | */ 28 | protected $callback; 29 | 30 | /** 31 | * Initialize the instance. 32 | * 33 | * @param callable $callback 34 | * 35 | * @throws \Imagine\Exception\InvalidArgumentException 36 | */ 37 | public function __construct($callback) 38 | { 39 | if (!is_callable($callback)) { 40 | throw new InvalidArgumentException('$callback has to be callable'); 41 | } 42 | 43 | $this->callback = $callback; 44 | } 45 | 46 | /** 47 | * {@inheritdoc} 48 | * 49 | * @see \Imagine\Filter\FilterInterface::apply() 50 | */ 51 | public function apply(ImageInterface $image) 52 | { 53 | $size = $image->getSize(); 54 | $w = $size->getWidth(); 55 | $h = $size->getHeight(); 56 | for ($y = 0; $y < $h; $y++) { 57 | for ($x = 0; $x < $w; $x++) { 58 | call_user_func($this->callback, $image, new Point($x, $y)); 59 | } 60 | } 61 | 62 | return $image; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/Filter/Advanced/RelativeResize.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | /* 4 | * This file is part of the Imagine package. 5 | * 6 | * (c) Bulat Shakirzyanov <mallluhuct@gmail.com> 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Imagine\Filter\Advanced; 13 | 14 | use Imagine\Exception\InvalidArgumentException; 15 | use Imagine\Filter\FilterInterface; 16 | use Imagine\Image\ImageInterface; 17 | 18 | /** 19 | * The RelativeResize filter allows images to be resized relative to their existing dimensions. 20 | */ 21 | class RelativeResize implements FilterInterface 22 | { 23 | /** 24 | * @var string 25 | */ 26 | private $method; 27 | 28 | /** 29 | * @var mixed 30 | */ 31 | private $parameter; 32 | 33 | /** 34 | * Constructs a RelativeResize filter with the given method and argument. 35 | * 36 | * @param string $method BoxInterface method 37 | * @param mixed $parameter Parameter for BoxInterface method 38 | */ 39 | public function __construct($method, $parameter) 40 | { 41 | if (!in_array($method, array('heighten', 'increase', 'scale', 'widen'))) { 42 | throw new InvalidArgumentException(sprintf('Unsupported method: ', $method)); 43 | } 44 | 45 | $this->method = $method; 46 | $this->parameter = $parameter; 47 | } 48 | 49 | /** 50 | * {@inheritdoc} 51 | * 52 | * @see \Imagine\Filter\FilterInterface::apply() 53 | */ 54 | public function apply(ImageInterface $image) 55 | { 56 | return $image->resize(call_user_func(array($image->getSize(), $this->method), $this->parameter)); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/Filter/Basic/ApplyMask.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | /* 4 | * This file is part of the Imagine package. 5 | * 6 | * (c) Bulat Shakirzyanov <mallluhuct@gmail.com> 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Imagine\Filter\Basic; 13 | 14 | use Imagine\Filter\FilterInterface; 15 | use Imagine\Image\ImageInterface; 16 | 17 | /** 18 | * An apply mask filter. 19 | */ 20 | class ApplyMask implements FilterInterface 21 | { 22 | /** 23 | * @var \Imagine\Image\ImageInterface 24 | */ 25 | private $mask; 26 | 27 | /** 28 | * Initialize the instance. 29 | * 30 | * @param \Imagine\Image\ImageInterface $mask 31 | */ 32 | public function __construct(ImageInterface $mask) 33 | { 34 | $this->mask = $mask; 35 | } 36 | 37 | /** 38 | * {@inheritdoc} 39 | * 40 | * @see \Imagine\Filter\FilterInterface::apply() 41 | */ 42 | public function apply(ImageInterface $image) 43 | { 44 | return $image->applyMask($this->mask); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/Filter/Basic/Autorotate.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | /* 4 | * This file is part of the Imagine package. 5 | * 6 | * (c) Bulat Shakirzyanov <mallluhuct@gmail.com> 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Imagine\Filter\Basic; 13 | 14 | use Imagine\Filter\FilterInterface; 15 | use Imagine\Image\ImageInterface; 16 | use Imagine\Image\Palette\Color\ColorInterface; 17 | 18 | /** 19 | * Rotates an image automatically based on exif information. 20 | * 21 | * Your attention please: This filter requires the use of the 22 | * ExifMetadataReader to work. 23 | * 24 | * @see https://imagine.readthedocs.org/en/latest/usage/metadata.html 25 | */ 26 | class Autorotate implements FilterInterface 27 | { 28 | /** 29 | * Image transformation: flip vertically. 30 | * 31 | * @var string 32 | */ 33 | const FLIP_VERTICALLY = 'V'; 34 | 35 | /** 36 | * Image transformation: flip horizontally. 37 | * 38 | * @var string 39 | */ 40 | const FLIP_HORIZONTALLY = 'H'; 41 | 42 | /** 43 | * @var string|array|\Imagine\Image\Palette\Color\ColorInterface 44 | */ 45 | private $color; 46 | 47 | /** 48 | * @param string|array|\Imagine\Image\Palette\Color\ColorInterface $color A color 49 | */ 50 | public function __construct($color = '000000') 51 | { 52 | $this->color = $color; 53 | } 54 | 55 | /** 56 | * {@inheritdoc} 57 | * 58 | * @see \Imagine\Filter\FilterInterface::apply() 59 | */ 60 | public function apply(ImageInterface $image) 61 | { 62 | foreach ($this->getTransformations($image) as $transformation) { 63 | if ($transformation === self::FLIP_HORIZONTALLY) { 64 | $image->flipHorizontally(); 65 | } elseif ($transformation === self::FLIP_VERTICALLY) { 66 | $image->flipVertically(); 67 | } elseif (is_int($transformation)) { 68 | $image->rotate($transformation, $this->getColor($image)); 69 | } 70 | } 71 | 72 | return $image; 73 | } 74 | 75 | /** 76 | * Get the transformations. 77 | * 78 | * @param \Imagine\Image\ImageInterface $image 79 | * 80 | * @return array an array containing Autorotate::FLIP_VERTICALLY, Autorotate::FLIP_HORIZONTALLY, rotation degrees 81 | */ 82 | public function getTransformations(ImageInterface $image) 83 | { 84 | $transformations = array(); 85 | $metadata = $image->metadata(); 86 | switch (isset($metadata['ifd0.Orientation']) ? $metadata['ifd0.Orientation'] : null) { 87 | case 1: // top-left 88 | break; 89 | case 2: // top-right 90 | $transformations[] = self::FLIP_HORIZONTALLY; 91 | break; 92 | case 3: // bottom-right 93 | $transformations[] = 180; 94 | break; 95 | case 4: // bottom-left 96 | $transformations[] = self::FLIP_HORIZONTALLY; 97 | $transformations[] = 180; 98 | break; 99 | case 5: // left-top 100 | $transformations[] = self::FLIP_HORIZONTALLY; 101 | $transformations[] = -90; 102 | break; 103 | case 6: // right-top 104 | $transformations[] = 90; 105 | break; 106 | case 7: // right-bottom 107 | $transformations[] = self::FLIP_HORIZONTALLY; 108 | $transformations[] = 90; 109 | break; 110 | case 8: // left-bottom 111 | $transformations[] = -90; 112 | break; 113 | default: // Invalid orientation 114 | break; 115 | } 116 | 117 | return $transformations; 118 | } 119 | 120 | /** 121 | * @param \Imagine\Image\ImageInterface $image 122 | * 123 | * @return \Imagine\Image\Palette\Color\ColorInterface 124 | */ 125 | private function getColor(ImageInterface $image) 126 | { 127 | if ($this->color instanceof ColorInterface) { 128 | return $this->color; 129 | } 130 | 131 | return $image->palette()->color($this->color); 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /src/Filter/Basic/Copy.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | /* 4 | * This file is part of the Imagine package. 5 | * 6 | * (c) Bulat Shakirzyanov <mallluhuct@gmail.com> 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Imagine\Filter\Basic; 13 | 14 | use Imagine\Filter\FilterInterface; 15 | use Imagine\Image\ImageInterface; 16 | 17 | /** 18 | * A copy filter. 19 | */ 20 | class Copy implements FilterInterface 21 | { 22 | /** 23 | * {@inheritdoc} 24 | * 25 | * @see \Imagine\Filter\FilterInterface::apply() 26 | */ 27 | public function apply(ImageInterface $image) 28 | { 29 | return $image->copy(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Filter/Basic/Crop.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | /* 4 | * This file is part of the Imagine package. 5 | * 6 | * (c) Bulat Shakirzyanov <mallluhuct@gmail.com> 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Imagine\Filter\Basic; 13 | 14 | use Imagine\Filter\FilterInterface; 15 | use Imagine\Image\BoxInterface; 16 | use Imagine\Image\ImageInterface; 17 | use Imagine\Image\PointInterface; 18 | 19 | /** 20 | * A crop filter. 21 | */ 22 | class Crop implements FilterInterface 23 | { 24 | /** 25 | * @var \Imagine\Image\PointInterface 26 | */ 27 | private $start; 28 | 29 | /** 30 | * @var \Imagine\Image\BoxInterface 31 | */ 32 | private $size; 33 | 34 | /** 35 | * Constructs a Crop filter with given x, y, coordinates and crop width and height values. 36 | * 37 | * @param \Imagine\Image\PointInterface $start 38 | * @param \Imagine\Image\BoxInterface $size 39 | */ 40 | public function __construct(PointInterface $start, BoxInterface $size) 41 | { 42 | $this->start = $start; 43 | $this->size = $size; 44 | } 45 | 46 | /** 47 | * {@inheritdoc} 48 | * 49 | * @see \Imagine\Filter\FilterInterface::apply() 50 | */ 51 | public function apply(ImageInterface $image) 52 | { 53 | return $image->crop($this->start, $this->size); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/Filter/Basic/Fill.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | /* 4 | * This file is part of the Imagine package. 5 | * 6 | * (c) Bulat Shakirzyanov <mallluhuct@gmail.com> 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Imagine\Filter\Basic; 13 | 14 | use Imagine\Filter\FilterInterface; 15 | use Imagine\Image\Fill\FillInterface; 16 | use Imagine\Image\ImageInterface; 17 | 18 | /** 19 | * A fill filter. 20 | */ 21 | class Fill implements FilterInterface 22 | { 23 | /** 24 | * @var \Imagine\Image\Fill\FillInterface 25 | */ 26 | private $fill; 27 | 28 | /** 29 | * @param \Imagine\Image\Fill\FillInterface $fill 30 | */ 31 | public function __construct(FillInterface $fill) 32 | { 33 | $this->fill = $fill; 34 | } 35 | 36 | /** 37 | * {@inheritdoc} 38 | * 39 | * @see \Imagine\Filter\FilterInterface::apply() 40 | */ 41 | public function apply(ImageInterface $image) 42 | { 43 | return $image->fill($this->fill); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/Filter/Basic/FlipHorizontally.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | /* 4 | * This file is part of the Imagine package. 5 | * 6 | * (c) Bulat Shakirzyanov <mallluhuct@gmail.com> 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Imagine\Filter\Basic; 13 | 14 | use Imagine\Filter\FilterInterface; 15 | use Imagine\Image\ImageInterface; 16 | 17 | /** 18 | * A "flip horizontally" filter. 19 | */ 20 | class FlipHorizontally implements FilterInterface 21 | { 22 | /** 23 | * {@inheritdoc} 24 | * 25 | * @see \Imagine\Filter\FilterInterface::apply() 26 | */ 27 | public function apply(ImageInterface $image) 28 | { 29 | return $image->flipHorizontally(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Filter/Basic/FlipVertically.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | /* 4 | * This file is part of the Imagine package. 5 | * 6 | * (c) Bulat Shakirzyanov <mallluhuct@gmail.com> 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Imagine\Filter\Basic; 13 | 14 | use Imagine\Filter\FilterInterface; 15 | use Imagine\Image\ImageInterface; 16 | 17 | /** 18 | * A "flip vertically" filter. 19 | */ 20 | class FlipVertically implements FilterInterface 21 | { 22 | /** 23 | * {@inheritdoc} 24 | * 25 | * @see \Imagine\Filter\FilterInterface::apply() 26 | */ 27 | public function apply(ImageInterface $image) 28 | { 29 | return $image->flipVertically(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Filter/Basic/Paste.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | /* 4 | * This file is part of the Imagine package. 5 | * 6 | * (c) Bulat Shakirzyanov <mallluhuct@gmail.com> 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Imagine\Filter\Basic; 13 | 14 | use Imagine\Exception\InvalidArgumentException; 15 | use Imagine\Filter\FilterInterface; 16 | use Imagine\Image\ImageInterface; 17 | use Imagine\Image\PointInterface; 18 | 19 | /** 20 | * A paste filter. 21 | */ 22 | class Paste implements FilterInterface 23 | { 24 | /** 25 | * @var \Imagine\Image\ImageInterface 26 | */ 27 | private $image; 28 | 29 | /** 30 | * @var \Imagine\Image\PointInterface 31 | */ 32 | private $start; 33 | 34 | /** 35 | * How to paste the image, from 0 (fully transparent) to 100 (fully opaque). 36 | * 37 | * @var int 38 | */ 39 | private $alpha; 40 | 41 | /** 42 | * Constructs a Paste filter with given ImageInterface to paste and x, y 43 | * coordinates of target position. 44 | * 45 | * @param \Imagine\Image\ImageInterface $image 46 | * @param \Imagine\Image\PointInterface $start 47 | * @param int $alpha how to paste the image, from 0 (fully transparent) to 100 (fully opaque) 48 | */ 49 | public function __construct(ImageInterface $image, PointInterface $start, $alpha = 100) 50 | { 51 | $this->image = $image; 52 | $this->start = $start; 53 | $alpha = (int) round($alpha); 54 | if ($alpha < 0 || $alpha > 100) { 55 | throw new InvalidArgumentException(sprintf('The %1$s argument can range from %2$d to %3$d, but you specified %4$d.', '$alpha', 0, 100, $alpha)); 56 | } 57 | $this->alpha = $alpha; 58 | } 59 | 60 | /** 61 | * {@inheritdoc} 62 | * 63 | * @see \Imagine\Filter\FilterInterface::apply() 64 | */ 65 | public function apply(ImageInterface $image) 66 | { 67 | return $image->paste($this->image, $this->start, $this->alpha); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/Filter/Basic/Resize.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | /* 4 | * This file is part of the Imagine package. 5 | * 6 | * (c) Bulat Shakirzyanov <mallluhuct@gmail.com> 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Imagine\Filter\Basic; 13 | 14 | use Imagine\Filter\FilterInterface; 15 | use Imagine\Image\BoxInterface; 16 | use Imagine\Image\ImageInterface; 17 | 18 | /** 19 | * A resize filter. 20 | */ 21 | class Resize implements FilterInterface 22 | { 23 | /** 24 | * @var \Imagine\Image\BoxInterface 25 | */ 26 | private $size; 27 | 28 | /** 29 | * @var string 30 | */ 31 | private $filter; 32 | 33 | /** 34 | * Constructs Resize filter with given width and height. 35 | * 36 | * @param \Imagine\Image\BoxInterface $size 37 | * @param string $filter 38 | */ 39 | public function __construct(BoxInterface $size, $filter = ImageInterface::FILTER_UNDEFINED) 40 | { 41 | $this->size = $size; 42 | $this->filter = $filter; 43 | } 44 | 45 | /** 46 | * {@inheritdoc} 47 | * 48 | * @see \Imagine\Filter\FilterInterface::apply() 49 | */ 50 | public function apply(ImageInterface $image) 51 | { 52 | return $image->resize($this->size, $this->filter); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/Filter/Basic/Rotate.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | /* 4 | * This file is part of the Imagine package. 5 | * 6 | * (c) Bulat Shakirzyanov <mallluhuct@gmail.com> 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Imagine\Filter\Basic; 13 | 14 | use Imagine\Filter\FilterInterface; 15 | use Imagine\Image\ImageInterface; 16 | use Imagine\Image\Palette\Color\ColorInterface; 17 | 18 | /** 19 | * A rotate filter. 20 | */ 21 | class Rotate implements FilterInterface 22 | { 23 | /** 24 | * @var int 25 | */ 26 | private $angle; 27 | 28 | /** 29 | * @var \Imagine\Image\Palette\Color\ColorInterface 30 | */ 31 | private $background; 32 | 33 | /** 34 | * Constructs Rotate filter with given angle and background color. 35 | * 36 | * @param int $angle 37 | * @param \Imagine\Image\Palette\Color\ColorInterface $background 38 | */ 39 | public function __construct($angle, ?ColorInterface $background = null) 40 | { 41 | $this->angle = $angle; 42 | $this->background = $background; 43 | } 44 | 45 | /** 46 | * {@inheritdoc} 47 | * 48 | * @see \Imagine\Filter\FilterInterface::apply() 49 | */ 50 | public function apply(ImageInterface $image) 51 | { 52 | return $image->rotate($this->angle, $this->background); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/Filter/Basic/Save.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | /* 4 | * This file is part of the Imagine package. 5 | * 6 | * (c) Bulat Shakirzyanov <mallluhuct@gmail.com> 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Imagine\Filter\Basic; 13 | 14 | use Imagine\Filter\FilterInterface; 15 | use Imagine\Image\ImageInterface; 16 | 17 | /** 18 | * A save filter. 19 | */ 20 | class Save implements FilterInterface 21 | { 22 | /** 23 | * @var string 24 | */ 25 | private $path; 26 | 27 | /** 28 | * @var array 29 | */ 30 | private $options; 31 | 32 | /** 33 | * Constructs Save filter with given path and options. 34 | * 35 | * @param string $path 36 | * @param array $options 37 | */ 38 | public function __construct($path = null, array $options = array()) 39 | { 40 | $this->path = $path; 41 | $this->options = $options; 42 | } 43 | 44 | /** 45 | * {@inheritdoc} 46 | * 47 | * @see \Imagine\Filter\FilterInterface::apply() 48 | */ 49 | public function apply(ImageInterface $image) 50 | { 51 | return $image->save($this->path, $this->options); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/Filter/Basic/Show.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | /* 4 | * This file is part of the Imagine package. 5 | * 6 | * (c) Bulat Shakirzyanov <mallluhuct@gmail.com> 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Imagine\Filter\Basic; 13 | 14 | use Imagine\Filter\FilterInterface; 15 | use Imagine\Image\ImageInterface; 16 | 17 | /** 18 | * A show filter. 19 | */ 20 | class Show implements FilterInterface 21 | { 22 | /** 23 | * @var string 24 | */ 25 | private $format; 26 | 27 | /** 28 | * @var array 29 | */ 30 | private $options; 31 | 32 | /** 33 | * Constructs the Show filter with given format and options. 34 | * 35 | * @param string $format 36 | * @param array $options 37 | */ 38 | public function __construct($format, array $options = array()) 39 | { 40 | $this->format = $format; 41 | $this->options = $options; 42 | } 43 | 44 | /** 45 | * {@inheritdoc} 46 | * 47 | * @see \Imagine\Filter\FilterInterface::apply() 48 | */ 49 | public function apply(ImageInterface $image) 50 | { 51 | return $image->show($this->format, $this->options); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/Filter/Basic/Strip.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | /* 4 | * This file is part of the Imagine package. 5 | * 6 | * (c) Bulat Shakirzyanov <mallluhuct@gmail.com> 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Imagine\Filter\Basic; 13 | 14 | use Imagine\Filter\FilterInterface; 15 | use Imagine\Image\ImageInterface; 16 | 17 | /** 18 | * A strip filter. 19 | */ 20 | class Strip implements FilterInterface 21 | { 22 | /** 23 | * {@inheritdoc} 24 | * 25 | * @see \Imagine\Filter\FilterInterface::apply() 26 | */ 27 | public function apply(ImageInterface $image) 28 | { 29 | return $image->strip(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Filter/Basic/Thumbnail.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | /* 4 | * This file is part of the Imagine package. 5 | * 6 | * (c) Bulat Shakirzyanov <mallluhuct@gmail.com> 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Imagine\Filter\Basic; 13 | 14 | use Imagine\Filter\FilterInterface; 15 | use Imagine\Image\BoxInterface; 16 | use Imagine\Image\ImageInterface; 17 | 18 | /** 19 | * A thumbnail filter. 20 | */ 21 | class Thumbnail implements FilterInterface 22 | { 23 | /** 24 | * @var \Imagine\Image\BoxInterface 25 | */ 26 | private $size; 27 | 28 | /** 29 | * @var int|string 30 | */ 31 | private $settings; 32 | 33 | /** 34 | * @var string 35 | */ 36 | private $filter; 37 | 38 | /** 39 | * Constructs the Thumbnail filter. 40 | * 41 | * @param \Imagine\Image\BoxInterface $size 42 | * @param int|string $settings One or more of the ManipulatorInterface::THUMBNAIL_ flags (joined with |). It may be a string for backward compatibility with old constant values that were strings. 43 | * @param string $filter See ImageInterface::FILTER_... constants 44 | */ 45 | public function __construct(BoxInterface $size, $settings = ImageInterface::THUMBNAIL_INSET, $filter = ImageInterface::FILTER_UNDEFINED) 46 | { 47 | $this->size = $size; 48 | $this->settings = $settings; 49 | $this->filter = $filter; 50 | } 51 | 52 | /** 53 | * {@inheritdoc} 54 | * 55 | * @see \Imagine\Filter\FilterInterface::apply() 56 | */ 57 | public function apply(ImageInterface $image) 58 | { 59 | return $image->thumbnail($this->size, $this->settings, $this->filter); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/Filter/Basic/WebOptimization.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | /* 4 | * This file is part of the Imagine package. 5 | * 6 | * (c) Bulat Shakirzyanov <mallluhuct@gmail.com> 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Imagine\Filter\Basic; 13 | 14 | use Imagine\Filter\FilterInterface; 15 | use Imagine\Image\ImageInterface; 16 | use Imagine\Image\Palette\RGB; 17 | 18 | /** 19 | * A filter to render web-optimized images. 20 | */ 21 | class WebOptimization implements FilterInterface 22 | { 23 | /** 24 | * @var \Imagine\Image\Palette\RGB 25 | */ 26 | private $palette; 27 | 28 | /** 29 | * @var string|callable|null 30 | */ 31 | private $path; 32 | 33 | /** 34 | * @var array 35 | */ 36 | private $options; 37 | 38 | /** 39 | * @param string|callable|null $path 40 | * @param array $options 41 | */ 42 | public function __construct($path = null, array $options = array()) 43 | { 44 | $this->path = $path; 45 | $this->options = array_replace(array( 46 | 'resolution-units' => ImageInterface::RESOLUTION_PIXELSPERINCH, 47 | 'resolution-y' => 72, 48 | 'resolution-x' => 72, 49 | ), $options); 50 | $this->palette = new RGB(); 51 | } 52 | 53 | /** 54 | * {@inheritdoc} 55 | * 56 | * @see \Imagine\Filter\FilterInterface::apply() 57 | */ 58 | public function apply(ImageInterface $image) 59 | { 60 | $image 61 | ->usePalette($this->palette) 62 | ->strip(); 63 | 64 | if (is_callable($this->path)) { 65 | $path = call_user_func($this->path, $image); 66 | } elseif ($this->path !== null) { 67 | $path = $this->path; 68 | } else { 69 | return $image; 70 | } 71 | 72 | return $image->save($path, $this->options); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/Filter/FilterInterface.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | /* 4 | * This file is part of the Imagine package. 5 | * 6 | * (c) Bulat Shakirzyanov <mallluhuct@gmail.com> 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Imagine\Filter; 13 | 14 | use Imagine\Image\ImageInterface; 15 | 16 | /** 17 | * Interface for imagine filters. 18 | */ 19 | interface FilterInterface 20 | { 21 | /** 22 | * Applies scheduled transformation to an ImageInterface instance. 23 | * 24 | * @param \Imagine\Image\ImageInterface $image 25 | * 26 | * @return \Imagine\Image\ImageInterface returns the processed ImageInterface instance 27 | */ 28 | public function apply(ImageInterface $image); 29 | } 30 | -------------------------------------------------------------------------------- /src/Filter/ImagineAware.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | /* 4 | * This file is part of the Imagine package. 5 | * 6 | * (c) Bulat Shakirzyanov <mallluhuct@gmail.com> 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Imagine\Filter; 13 | 14 | use Imagine\Exception\InvalidArgumentException; 15 | use Imagine\Image\ImagineInterface; 16 | 17 | /** 18 | * ImagineAware base class. 19 | */ 20 | abstract class ImagineAware implements FilterInterface 21 | { 22 | /** 23 | * An ImagineInterface instance. 24 | * 25 | * @var \Imagine\Image\ImagineInterface 26 | */ 27 | private $imagine; 28 | 29 | /** 30 | * Set ImagineInterface instance. 31 | * 32 | * @param \Imagine\Image\ImagineInterface $imagine An ImagineInterface instance 33 | */ 34 | public function setImagine(ImagineInterface $imagine) 35 | { 36 | $this->imagine = $imagine; 37 | } 38 | 39 | /** 40 | * Get ImagineInterface instance. 41 | * 42 | * @throws \Imagine\Exception\InvalidArgumentException 43 | * 44 | * @return \Imagine\Image\ImagineInterface 45 | */ 46 | public function getImagine() 47 | { 48 | if (!$this->imagine instanceof ImagineInterface) { 49 | throw new InvalidArgumentException(sprintf('In order to use %s pass an Imagine\Image\ImagineInterface instance to filter constructor', get_class($this))); 50 | } 51 | 52 | return $this->imagine; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/Gd/Effects.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | /* 4 | * This file is part of the Imagine package. 5 | * 6 | * (c) Bulat Shakirzyanov <mallluhuct@gmail.com> 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Imagine\Gd; 13 | 14 | use Imagine\Driver\InfoProvider; 15 | use Imagine\Effects\EffectsInterface; 16 | use Imagine\Exception\InvalidArgumentException; 17 | use Imagine\Exception\RuntimeException; 18 | use Imagine\Image\Palette\Color\ColorInterface; 19 | use Imagine\Image\Palette\Color\RGB as RGBColor; 20 | use Imagine\Utils\Matrix; 21 | 22 | /** 23 | * Effects implementation using the GD PHP extension. 24 | */ 25 | class Effects implements EffectsInterface, InfoProvider 26 | { 27 | /** 28 | * @var resource|\GdImage 29 | */ 30 | private $resource; 31 | 32 | /** 33 | * Initialize the instance. 34 | * 35 | * @param resource|\GdImage $resource 36 | */ 37 | public function __construct($resource) 38 | { 39 | $this->resource = $resource; 40 | } 41 | 42 | /** 43 | * {@inheritdoc} 44 | * 45 | * @see \Imagine\Driver\InfoProvider::getDriverInfo() 46 | * @since 1.3.0 47 | */ 48 | public static function getDriverInfo($required = true) 49 | { 50 | return DriverInfo::get($required); 51 | } 52 | 53 | /** 54 | * {@inheritdoc} 55 | * 56 | * @see \Imagine\Effects\EffectsInterface::gamma() 57 | */ 58 | public function gamma($correction) 59 | { 60 | if (imagegammacorrect($this->resource, 1.0, $correction) === false) { 61 | throw new RuntimeException('Failed to apply gamma correction to the image'); 62 | } 63 | 64 | return $this; 65 | } 66 | 67 | /** 68 | * {@inheritdoc} 69 | * 70 | * @see \Imagine\Effects\EffectsInterface::negative() 71 | */ 72 | public function negative() 73 | { 74 | if (imagefilter($this->resource, IMG_FILTER_NEGATE) === false) { 75 | throw new RuntimeException('Failed to negate the image'); 76 | } 77 | 78 | return $this; 79 | } 80 | 81 | /** 82 | * {@inheritdoc} 83 | * 84 | * @see \Imagine\Effects\EffectsInterface::grayscale() 85 | */ 86 | public function grayscale() 87 | { 88 | if (imagefilter($this->resource, IMG_FILTER_GRAYSCALE) === false) { 89 | throw new RuntimeException('Failed to grayscale the image'); 90 | } 91 | 92 | return $this; 93 | } 94 | 95 | /** 96 | * {@inheritdoc} 97 | * 98 | * @see \Imagine\Effects\EffectsInterface::colorize() 99 | */ 100 | public function colorize(ColorInterface $color) 101 | { 102 | if (!$color instanceof RGBColor) { 103 | throw new RuntimeException('Colorize effects only accepts RGB color in GD context'); 104 | } 105 | 106 | if (imagefilter($this->resource, IMG_FILTER_COLORIZE, $color->getRed(), $color->getGreen(), $color->getBlue()) === false) { 107 | throw new RuntimeException('Failed to colorize the image'); 108 | } 109 | 110 | return $this; 111 | } 112 | 113 | /** 114 | * {@inheritdoc} 115 | * 116 | * @see \Imagine\Effects\EffectsInterface::sharpen() 117 | */ 118 | public function sharpen() 119 | { 120 | $sharpenMatrix = array(array(-1, -1, -1), array(-1, 16, -1), array(-1, -1, -1)); 121 | $divisor = array_sum(array_map('array_sum', $sharpenMatrix)); 122 | 123 | if (imageconvolution($this->resource, $sharpenMatrix, $divisor, 0) === false) { 124 | throw new RuntimeException('Failed to sharpen the image'); 125 | } 126 | 127 | return $this; 128 | } 129 | 130 | /** 131 | * {@inheritdoc} 132 | * 133 | * @see \Imagine\Effects\EffectsInterface::blur() 134 | */ 135 | public function blur($sigma = 1) 136 | { 137 | if (imagefilter($this->resource, IMG_FILTER_GAUSSIAN_BLUR) === false) { 138 | throw new RuntimeException('Failed to blur the image'); 139 | } 140 | 141 | return $this; 142 | } 143 | 144 | /** 145 | * {@inheritdoc} 146 | * 147 | * @see \Imagine\Effects\EffectsInterface::brightness() 148 | */ 149 | public function brightness($brightness) 150 | { 151 | $gdBrightness = (int) round($brightness / 100 * 255); 152 | if ($gdBrightness < -255 || $gdBrightness > 255) { 153 | throw new InvalidArgumentException(sprintf('The %1$s argument can range from %2$d to %3$d, but you specified %4$d.', '$brightness', -100, 100, $brightness)); 154 | } 155 | if (imagefilter($this->resource, IMG_FILTER_BRIGHTNESS, $gdBrightness) === false) { 156 | throw new RuntimeException('Failed to brightness the image'); 157 | } 158 | 159 | return $this; 160 | } 161 | 162 | /** 163 | * {@inheritdoc} 164 | * 165 | * @see \Imagine\Effects\EffectsInterface::convolve() 166 | */ 167 | public function convolve(Matrix $matrix) 168 | { 169 | if ($matrix->getWidth() !== 3 || $matrix->getHeight() !== 3) { 170 | throw new InvalidArgumentException(sprintf('A convolution matrix must be 3x3 (%dx%d provided).', $matrix->getWidth(), $matrix->getHeight())); 171 | } 172 | if (imageconvolution($this->resource, $matrix->getMatrix(), 1, 0) === false) { 173 | throw new RuntimeException('Failed to convolve the image'); 174 | } 175 | 176 | return $this; 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /src/Gd/Font.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | /* 4 | * This file is part of the Imagine package. 5 | * 6 | * (c) Bulat Shakirzyanov <mallluhuct@gmail.com> 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Imagine\Gd; 13 | 14 | use Imagine\Driver\InfoProvider; 15 | use Imagine\Image\AbstractFont; 16 | 17 | /** 18 | * Font implementation using the GD library. 19 | */ 20 | final class Font extends AbstractFont implements InfoProvider 21 | { 22 | /** 23 | * {@inheritdoc} 24 | * 25 | * @see \Imagine\Driver\InfoProvider::getDriverInfo() 26 | * @since 1.3.0 27 | */ 28 | public static function getDriverInfo($required = true) 29 | { 30 | return DriverInfo::get($required); 31 | } 32 | 33 | /** 34 | * {@inheritdoc} 35 | * 36 | * @see \Imagine\Image\FontInterface::box() 37 | */ 38 | public function box($string, $angle = 0) 39 | { 40 | static::getDriverInfo()->requireFeature(DriverInfo::FEATURE_TEXTFUNCTIONS); 41 | $fontfile = $this->file; 42 | if ($fontfile && DIRECTORY_SEPARATOR === '\\') { 43 | // On Windows imageftbbox() throws a "Could not find/open font" error if $fontfile is not an absolute path. 44 | $fontfileRealpath = realpath($fontfile); 45 | if ($fontfileRealpath !== false) { 46 | $fontfile = $fontfileRealpath; 47 | } 48 | } 49 | 50 | $angle = -1 * $angle; 51 | $info = imageftbbox($this->size, $angle, $fontfile, $string); 52 | $xs = array($info[0], $info[2], $info[4], $info[6]); 53 | $ys = array($info[1], $info[3], $info[5], $info[7]); 54 | $width = abs(max($xs) - min($xs)); 55 | $height = abs(max($ys) - min($ys)); 56 | 57 | return $this->getClassFactory()->createBox($width, $height); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/Gd/Layers.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | /* 4 | * This file is part of the Imagine package. 5 | * 6 | * (c) Bulat Shakirzyanov <mallluhuct@gmail.com> 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Imagine\Gd; 13 | 14 | use Imagine\Driver\InfoProvider; 15 | use Imagine\Exception\InvalidArgumentException; 16 | use Imagine\Exception\NotSupportedException; 17 | use Imagine\Exception\RuntimeException; 18 | use Imagine\Factory\ClassFactoryInterface; 19 | use Imagine\Image\AbstractLayers; 20 | use Imagine\Image\Metadata\MetadataBag; 21 | use Imagine\Image\Palette\PaletteInterface; 22 | 23 | class Layers extends AbstractLayers implements InfoProvider 24 | { 25 | /** 26 | * @var \Imagine\Gd\Image 27 | */ 28 | private $image; 29 | 30 | /** 31 | * @var int 32 | */ 33 | private $offset; 34 | 35 | /** 36 | * @var resource|\GdImage 37 | */ 38 | private $resource; 39 | 40 | /** 41 | * @var \Imagine\Image\Palette\PaletteInterface 42 | */ 43 | private $palette; 44 | 45 | /** 46 | * @param \Imagine\Gd\Image $image 47 | * @param \Imagine\Image\Palette\PaletteInterface $palette 48 | * @param resource|\GdImage $resource 49 | * @param int $initialOffset 50 | * 51 | * @throws \Imagine\Exception\RuntimeException 52 | */ 53 | public function __construct(Image $image, PaletteInterface $palette, $resource, $initialOffset = 0) 54 | { 55 | if (!$resource instanceof \GdImage && !is_resource($resource)) { 56 | throw new RuntimeException('Invalid Gd resource provided'); 57 | } 58 | 59 | $this->image = $image; 60 | $this->resource = $resource; 61 | $this->offset = (int) $initialOffset; 62 | $this->palette = $palette; 63 | } 64 | 65 | /** 66 | * {@inheritdoc} 67 | * 68 | * @see \Imagine\Driver\InfoProvider::getDriverInfo() 69 | * @since 1.3.0 70 | */ 71 | public static function getDriverInfo($required = true) 72 | { 73 | return DriverInfo::get($required); 74 | } 75 | 76 | /** 77 | * {@inheritdoc} 78 | * 79 | * @see \Imagine\Image\LayersInterface::merge() 80 | */ 81 | public function merge() 82 | { 83 | } 84 | 85 | /** 86 | * {@inheritdoc} 87 | * 88 | * @see \Imagine\Image\LayersInterface::coalesce() 89 | */ 90 | public function coalesce() 91 | { 92 | return $this; 93 | } 94 | 95 | /** 96 | * {@inheritdoc} 97 | * 98 | * @see \Imagine\Image\LayersInterface::animate() 99 | */ 100 | public function animate($format, $delay, $loops) 101 | { 102 | return $this; 103 | } 104 | 105 | /** 106 | * {@inheritdoc} 107 | * 108 | * @see \Iterator::current() 109 | * 110 | * @return mixed 111 | */ 112 | #[\ReturnTypeWillChange] 113 | public function current() 114 | { 115 | return $this->getClassFactory()->createImage(ClassFactoryInterface::HANDLE_GD, $this->resource, $this->palette, new MetadataBag()); 116 | } 117 | 118 | /** 119 | * {@inheritdoc} 120 | * 121 | * @see \Iterator::key() 122 | * 123 | * @return mixed 124 | */ 125 | #[\ReturnTypeWillChange] 126 | public function key() 127 | { 128 | return $this->offset; 129 | } 130 | 131 | /** 132 | * {@inheritdoc} 133 | * 134 | * @see \Iterator::next() 135 | * 136 | * @return mixed 137 | */ 138 | #[\ReturnTypeWillChange] 139 | public function next() 140 | { 141 | ++$this->offset; 142 | } 143 | 144 | /** 145 | * {@inheritdoc} 146 | * 147 | * @see \Iterator::rewind() 148 | * 149 | * @return void 150 | */ 151 | #[\ReturnTypeWillChange] 152 | public function rewind() 153 | { 154 | $this->offset = 0; 155 | } 156 | 157 | /** 158 | * {@inheritdoc} 159 | * 160 | * @see \Iterator::valid() 161 | * 162 | * @return bool 163 | */ 164 | #[\ReturnTypeWillChange] 165 | public function valid() 166 | { 167 | return $this->offset < 1; 168 | } 169 | 170 | /** 171 | * {@inheritdoc} 172 | * 173 | * @see \Countable::count() 174 | * 175 | * @return int 176 | */ 177 | #[\ReturnTypeWillChange] 178 | public function count() 179 | { 180 | return 1; 181 | } 182 | 183 | /** 184 | * {@inheritdoc} 185 | * 186 | * @see \ArrayAccess::offsetExists() 187 | * 188 | * @return bool 189 | */ 190 | #[\ReturnTypeWillChange] 191 | public function offsetExists($offset) 192 | { 193 | return $offset === 0; 194 | } 195 | 196 | /** 197 | * {@inheritdoc} 198 | * 199 | * @see \ArrayAccess::offsetGet() 200 | * 201 | * @return mixed 202 | */ 203 | #[\ReturnTypeWillChange] 204 | public function offsetGet($offset) 205 | { 206 | if ($offset === 0) { 207 | return $this->getClassFactory()->createImage(ClassFactoryInterface::HANDLE_GD, $this->resource, $this->palette, new MetadataBag()); 208 | } 209 | 210 | throw new InvalidArgumentException('GD only supports one layer at offset 0'); 211 | } 212 | 213 | /** 214 | * {@inheritdoc} 215 | * 216 | * @see \ArrayAccess::offsetSet() 217 | * 218 | * @return void 219 | */ 220 | #[\ReturnTypeWillChange] 221 | public function offsetSet($offset, $value) 222 | { 223 | throw new NotSupportedException('GD does not support layer set'); 224 | } 225 | 226 | /** 227 | * {@inheritdoc} 228 | * 229 | * @see \ArrayAccess::offsetUnset() 230 | * 231 | * @return void 232 | */ 233 | #[\ReturnTypeWillChange] 234 | public function offsetUnset($offset) 235 | { 236 | throw new NotSupportedException('GD does not support layer unset'); 237 | } 238 | } 239 | -------------------------------------------------------------------------------- /src/Gmagick/DriverInfo.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | namespace Imagine\Gmagick; 4 | 5 | use Imagine\Driver\AbstractInfo; 6 | use Imagine\Exception\NotSupportedException; 7 | use Imagine\Image\Format; 8 | use Imagine\Image\FormatList; 9 | 10 | /** 11 | * Provide information and features supported by the Gmagick graphics driver. 12 | * 13 | * @since 1.3.0 14 | */ 15 | class DriverInfo extends AbstractInfo 16 | { 17 | /** 18 | * @var static|\Imagine\Exception\NotSupportedException|null 19 | */ 20 | private static $instance; 21 | 22 | private $availableMethods = array(); 23 | 24 | /** 25 | * @throws \Imagine\Exception\NotSupportedException 26 | */ 27 | protected function __construct() 28 | { 29 | if (!class_exists('Gmagick') || !extension_loaded('gmagick')) { 30 | throw new NotSupportedException('Gmagick driver not installed'); 31 | } 32 | $m = null; 33 | $extensionVersion = phpversion('gmagick'); 34 | $driverRawVersion = is_string($extensionVersion) ? $extensionVersion : ''; 35 | $driverSemverVersion = preg_match('/^.*?(\d+\.\d+\.\d+)/', $driverRawVersion, $m) ? $m[1] : ''; 36 | $gmagick = new \Gmagick(); 37 | $engineVersion = $gmagick->getversion(); 38 | if (is_array($engineVersion) && isset($engineVersion['versionString']) && is_string($engineVersion['versionString'])) { 39 | if (preg_match('/^.*?(\d+\.\d+\.\d+(-\d+)?(\s+Q\d+)?)/i', $engineVersion['versionString'], $m)) { 40 | $engineRawVersion = $m[1]; 41 | } else { 42 | $engineRawVersion = $engineVersion['versionString']; 43 | } 44 | } else { 45 | $engineRawVersion = ''; 46 | } 47 | $engineSemverVersion = preg_match('/^.*?(\d+\.\d+\.\d+)/', $engineRawVersion, $m) ? $m[1] : ''; 48 | parent::__construct($driverRawVersion, $driverSemverVersion, $engineRawVersion, $engineSemverVersion); 49 | } 50 | 51 | /** 52 | * {@inheritdoc} 53 | * 54 | * @see \Imagine\Driver\Info::get() 55 | */ 56 | public static function get($required = true) 57 | { 58 | if (self::$instance === null) { 59 | try { 60 | self::$instance = new static(); 61 | } catch (NotSupportedException $x) { 62 | self::$instance = $x; 63 | } 64 | } 65 | if (self::$instance instanceof self) { 66 | return self::$instance; 67 | } 68 | 69 | if ($required) { 70 | throw self::$instance; 71 | } 72 | 73 | return null; 74 | } 75 | 76 | /** 77 | * {@inheritdoc} 78 | * 79 | * @see \Imagine\Driver\AbstractInfo::checkFeature() 80 | */ 81 | protected function checkFeature($feature) 82 | { 83 | switch ($feature) { 84 | case static::FEATURE_COALESCELAYERS: 85 | throw new NotSupportedException('Gmagick does not support coalescing'); 86 | case static::FEATURE_NEGATEIMAGE: 87 | if (!$this->isMethodAvailale('negateimage')) { 88 | throw new NotSupportedException('Gmagick version 1.1.0 RC3 is required for negative effect'); 89 | } 90 | break; 91 | case static::FEATURE_COLORIZEIMAGE: 92 | throw new NotSupportedException('Gmagick does not support colorize'); 93 | case static::FEATURE_SHARPENIMAGE: 94 | throw new NotSupportedException('Gmagick does not support sharpen yet'); 95 | case static::FEATURE_CONVOLVEIMAGE: 96 | if (!$this->isMethodAvailale('convolveimage')) { 97 | throw new NotSupportedException('The version of Gmagick extension is too old: it does not support convolve (you need gmagick 2.0.1RC2 or later.'); 98 | } 99 | break; 100 | case static::FEATURE_CUSTOMRESOLUTION: 101 | throw new NotSupportedException('Gmagick does not support setting custom resolutions'); 102 | case static::FEATURE_GETCMYKCOLORSCORRECTLY: 103 | throw new NotSupportedException('Gmagick fails to read CMYK colors properly, see https://bugs.php.net/bug.php?id=67435'); 104 | case static::FEATURE_TRANSPARENCY: 105 | throw new NotSupportedException("Gmagick doesn't support transparency"); 106 | case static::FEATURE_ADDLAYERSTOEMPTYIMAGE: 107 | throw new NotSupportedException("Can't animate empty images because Gmagick is affected by bug https://bugs.php.net/bug.php?id=62309"); 108 | case static::FEATURE_DETECTGRAYCOLORSPACE: 109 | throw new NotSupportedException('Gmagick does not support gray colorspace, because of the lack of image type support'); 110 | } 111 | } 112 | 113 | /** 114 | * {@inheritdoc} 115 | * 116 | * @see \Imagine\Driver\AbstractInfo::buildSupportedFormats() 117 | */ 118 | protected function buildSupportedFormats() 119 | { 120 | $supportedFormats = array(); 121 | $gmagick = new \Gmagick(); 122 | $magickFormats = array_map('strtolower', $gmagick->queryFormats()); 123 | foreach (Format::getAll() as $format) { 124 | if (in_array($format->getID(), $magickFormats, true) || array_intersect($magickFormats, $format->getAlternativeIDs()) !== array()) { 125 | $supportedFormats[] = $format; 126 | } 127 | } 128 | 129 | return new FormatList($supportedFormats); 130 | } 131 | 132 | /** 133 | * @param string $methodName 134 | * 135 | * @return bool 136 | */ 137 | private function isMethodAvailale($methodName) 138 | { 139 | if (!isset($this->availableMethods[$methodName])) { 140 | $gmagick = new \Gmagick(); 141 | $this->availableMethods[$methodName] = method_exists($gmagick, $methodName); 142 | } 143 | 144 | return $this->availableMethods[$methodName]; 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /src/Gmagick/Effects.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | /* 4 | * This file is part of the Imagine package. 5 | * 6 | * (c) Bulat Shakirzyanov <mallluhuct@gmail.com> 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Imagine\Gmagick; 13 | 14 | use Imagine\Driver\InfoProvider; 15 | use Imagine\Effects\EffectsInterface; 16 | use Imagine\Exception\InvalidArgumentException; 17 | use Imagine\Exception\RuntimeException; 18 | use Imagine\Image\Palette\Color\ColorInterface; 19 | use Imagine\Utils\Matrix; 20 | 21 | /** 22 | * Effects implementation using the Gmagick PHP extension. 23 | */ 24 | class Effects implements EffectsInterface, InfoProvider 25 | { 26 | /** 27 | * @var \Gmagick 28 | */ 29 | private $gmagick; 30 | 31 | /** 32 | * Initialize the instance. 33 | * 34 | * @param \Gmagick $gmagick 35 | */ 36 | public function __construct(\Gmagick $gmagick) 37 | { 38 | $this->gmagick = $gmagick; 39 | } 40 | 41 | /** 42 | * {@inheritdoc} 43 | * 44 | * @see \Imagine\Driver\InfoProvider::getDriverInfo() 45 | * @since 1.3.0 46 | */ 47 | public static function getDriverInfo($required = true) 48 | { 49 | return DriverInfo::get($required); 50 | } 51 | 52 | /** 53 | * {@inheritdoc} 54 | * 55 | * @see \Imagine\Effects\EffectsInterface::gamma() 56 | */ 57 | public function gamma($correction) 58 | { 59 | try { 60 | $this->gmagick->gammaimage($correction); 61 | } catch (\GmagickException $e) { 62 | throw new RuntimeException('Failed to apply gamma correction to the image', $e->getCode(), $e); 63 | } 64 | 65 | return $this; 66 | } 67 | 68 | /** 69 | * {@inheritdoc} 70 | * 71 | * @see \Imagine\Effects\EffectsInterface::negative() 72 | */ 73 | public function negative() 74 | { 75 | static::getDriverInfo()->requireFeature(DriverInfo::FEATURE_NEGATEIMAGE); 76 | try { 77 | $this->gmagick->negateimage(false, \Gmagick::CHANNEL_ALL); 78 | } catch (\GmagickException $e) { 79 | throw new RuntimeException('Failed to negate the image', $e->getCode(), $e); 80 | } 81 | 82 | return $this; 83 | } 84 | 85 | /** 86 | * {@inheritdoc} 87 | * 88 | * @see \Imagine\Effects\EffectsInterface::grayscale() 89 | */ 90 | public function grayscale() 91 | { 92 | try { 93 | $this->gmagick->setimagetype(defined('Gmagick::IMGTYPE_GRAYSCALE') ? \Gmagick::IMGTYPE_GRAYSCALE : 2); 94 | } catch (\GmagickException $e) { 95 | throw new RuntimeException('Failed to grayscale the image', $e->getCode(), $e); 96 | } 97 | 98 | return $this; 99 | } 100 | 101 | /** 102 | * {@inheritdoc} 103 | * 104 | * @see \Imagine\Effects\EffectsInterface::colorize() 105 | */ 106 | public function colorize(ColorInterface $color) 107 | { 108 | static::getDriverInfo()->requireFeature(DriverInfo::FEATURE_COLORIZEIMAGE); 109 | } 110 | 111 | /** 112 | * {@inheritdoc} 113 | * 114 | * @see \Imagine\Effects\EffectsInterface::sharpen() 115 | */ 116 | public function sharpen() 117 | { 118 | static::getDriverInfo()->requireFeature(DriverInfo::FEATURE_SHARPENIMAGE); 119 | } 120 | 121 | /** 122 | * {@inheritdoc} 123 | * 124 | * @see \Imagine\Effects\EffectsInterface::blur() 125 | */ 126 | public function blur($sigma = 1) 127 | { 128 | try { 129 | $this->gmagick->blurimage(0, $sigma); 130 | } catch (\GmagickException $e) { 131 | throw new RuntimeException('Failed to blur the image', $e->getCode(), $e); 132 | } 133 | 134 | return $this; 135 | } 136 | 137 | /** 138 | * {@inheritdoc} 139 | * 140 | * @see \Imagine\Effects\EffectsInterface::brightness() 141 | */ 142 | public function brightness($brightness) 143 | { 144 | $brightness = (int) round($brightness); 145 | if ($brightness < -100 || $brightness > 100) { 146 | throw new InvalidArgumentException(sprintf('The %1$s argument can range from %2$d to %3$d, but you specified %4$d.', '$brightness', -100, 100, $brightness)); 147 | } 148 | try { 149 | // This *emulates* setting the brightness 150 | $sign = $brightness < 0 ? -1 : 1; 151 | $v = abs($brightness) / 100; 152 | if ($sign > 0) { 153 | $v = (2 / (sin(($v * .99999 * M_PI_2) + M_PI_2))) - 2; 154 | } 155 | $this->gmagick->modulateimage(100 + $sign * $v * 100, 100, 100); 156 | } catch (\GmagickException $e) { 157 | throw new RuntimeException('Failed to brightness the image'); 158 | } 159 | 160 | return $this; 161 | } 162 | 163 | /** 164 | * {@inheritdoc} 165 | * 166 | * @see \Imagine\Effects\EffectsInterface::convolve() 167 | */ 168 | public function convolve(Matrix $matrix) 169 | { 170 | static::getDriverInfo()->requireFeature(DriverInfo::FEATURE_CONVOLVEIMAGE); 171 | if ($matrix->getWidth() !== 3 || $matrix->getHeight() !== 3) { 172 | throw new InvalidArgumentException(sprintf('A convolution matrix must be 3x3 (%dx%d provided).', $matrix->getWidth(), $matrix->getHeight())); 173 | } 174 | try { 175 | $this->gmagick->convolveimage($matrix->getValueList()); 176 | } catch (\GmagickException $e) { 177 | throw new RuntimeException('Failed to convolve the image'); 178 | } 179 | 180 | return $this; 181 | } 182 | } 183 | -------------------------------------------------------------------------------- /src/Gmagick/Font.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | /* 4 | * This file is part of the Imagine package. 5 | * 6 | * (c) Bulat Shakirzyanov <mallluhuct@gmail.com> 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Imagine\Gmagick; 13 | 14 | use Imagine\Driver\InfoProvider; 15 | use Imagine\Image\AbstractFont; 16 | use Imagine\Image\Palette\Color\ColorInterface; 17 | 18 | /** 19 | * Font implementation using the Gmagick PHP extension. 20 | */ 21 | final class Font extends AbstractFont implements InfoProvider 22 | { 23 | /** 24 | * @var \Gmagick 25 | */ 26 | private $gmagick; 27 | 28 | /** 29 | * @param \Gmagick $gmagick 30 | * @param string $file 31 | * @param int $size 32 | * @param \Imagine\Image\Palette\Color\ColorInterface $color 33 | */ 34 | public function __construct(\Gmagick $gmagick, $file, $size, ColorInterface $color) 35 | { 36 | $this->gmagick = $gmagick; 37 | 38 | parent::__construct($file, $size, $color); 39 | } 40 | 41 | /** 42 | * {@inheritdoc} 43 | * 44 | * @see \Imagine\Driver\InfoProvider::getDriverInfo() 45 | * @since 1.3.0 46 | */ 47 | public static function getDriverInfo($required = true) 48 | { 49 | return DriverInfo::get($required); 50 | } 51 | 52 | /** 53 | * {@inheritdoc} 54 | * 55 | * @see \Imagine\Image\FontInterface::box() 56 | */ 57 | public function box($string, $angle = 0) 58 | { 59 | $text = new \GmagickDraw(); 60 | 61 | $text->setfont($this->file); 62 | /* 63 | * @see http://www.php.net/manual/en/imagick.queryfontmetrics.php#101027 64 | * 65 | * ensure font resolution is the same as GD's hard-coded 96 66 | */ 67 | $text->setfontsize((int) ($this->size * (96 / 72))); 68 | $text->setfontstyle(\Gmagick::STYLE_OBLIQUE); 69 | 70 | $info = $this->gmagick->queryfontmetrics($text, $string); 71 | 72 | $box = $this->getClassFactory()->createBox($info['textWidth'], $info['textHeight']); 73 | 74 | return $box; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/Image/AbstractFont.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | /* 4 | * This file is part of the Imagine package. 5 | * 6 | * (c) Bulat Shakirzyanov <mallluhuct@gmail.com> 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Imagine\Image; 13 | 14 | use Imagine\Exception\InvalidArgumentException; 15 | use Imagine\Factory\ClassFactory; 16 | use Imagine\Factory\ClassFactoryAwareInterface; 17 | use Imagine\Factory\ClassFactoryInterface; 18 | use Imagine\Image\Palette\Color\ColorInterface; 19 | 20 | /** 21 | * Abstract font base class. 22 | */ 23 | abstract class AbstractFont implements FontInterface, ClassFactoryAwareInterface 24 | { 25 | /** 26 | * @var \Imagine\Factory\ClassFactoryInterface|null 27 | */ 28 | private $classFactory; 29 | 30 | /** 31 | * @var string 32 | */ 33 | protected $file; 34 | 35 | /** 36 | * @var int 37 | */ 38 | protected $size; 39 | 40 | /** 41 | * @var \Imagine\Image\Palette\Color\ColorInterface 42 | */ 43 | protected $color; 44 | 45 | /** 46 | * Constructs a font with specified $file, $size and $color. 47 | * 48 | * The font size is to be specified in points (e.g. 10pt means 10) 49 | * 50 | * @param string $file 51 | * @param int $size 52 | * @param \Imagine\Image\Palette\Color\ColorInterface $color 53 | */ 54 | public function __construct($file, $size, ColorInterface $color) 55 | { 56 | $this->file = $file; 57 | $this->size = $size; 58 | $this->color = $color; 59 | } 60 | 61 | /** 62 | * {@inheritdoc} 63 | * 64 | * @see \Imagine\Image\FontInterface::getFile() 65 | */ 66 | final public function getFile() 67 | { 68 | return $this->file; 69 | } 70 | 71 | /** 72 | * {@inheritdoc} 73 | * 74 | * @see \Imagine\Image\FontInterface::getSize() 75 | */ 76 | final public function getSize() 77 | { 78 | return $this->size; 79 | } 80 | 81 | /** 82 | * {@inheritdoc} 83 | * 84 | * @see \Imagine\Image\FontInterface::getColor() 85 | */ 86 | final public function getColor() 87 | { 88 | return $this->color; 89 | } 90 | 91 | /** 92 | * {@inheritdoc} 93 | * 94 | * @see \Imagine\Image\FontInterface::wrapText() 95 | */ 96 | public function wrapText($string, $maxWidth, $angle = 0) 97 | { 98 | $string = (string) $string; 99 | if ($string === '') { 100 | return $string; 101 | } 102 | $maxWidth = (int) round($maxWidth); 103 | if ($maxWidth < 1) { 104 | throw new InvalidArgumentException(sprintf('The $maxWidth parameter of wrapText must be greater than 0.')); 105 | } 106 | $words = explode(' ', $string); 107 | $lines = array(); 108 | $currentLine = null; 109 | foreach ($words as $word) { 110 | if ($currentLine === null) { 111 | $currentLine = $word; 112 | } else { 113 | $testLine = $currentLine . ' ' . $word; 114 | $testbox = $this->box($testLine, $angle); 115 | if ($testbox->getWidth() <= $maxWidth) { 116 | $currentLine = $testLine; 117 | } else { 118 | $lines[] = $currentLine; 119 | $currentLine = $word; 120 | } 121 | } 122 | } 123 | if ($currentLine !== null) { 124 | $lines[] = $currentLine; 125 | } 126 | 127 | return implode("\n", $lines); 128 | } 129 | 130 | /** 131 | * {@inheritdoc} 132 | * 133 | * @see \Imagine\Factory\ClassFactoryAwareInterface::getClassFactory() 134 | */ 135 | public function getClassFactory() 136 | { 137 | if ($this->classFactory === null) { 138 | $this->classFactory = new ClassFactory(); 139 | } 140 | 141 | return $this->classFactory; 142 | } 143 | 144 | /** 145 | * {@inheritdoc} 146 | * 147 | * @see \Imagine\Factory\ClassFactoryAwareInterface::setClassFactory() 148 | */ 149 | public function setClassFactory(ClassFactoryInterface $classFactory) 150 | { 151 | $this->classFactory = $classFactory; 152 | 153 | return $this; 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /src/Image/AbstractImagine.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | /* 4 | * This file is part of the Imagine package. 5 | * 6 | * (c) Bulat Shakirzyanov <mallluhuct@gmail.com> 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Imagine\Image; 13 | 14 | use Imagine\Exception\InvalidArgumentException; 15 | use Imagine\Factory\ClassFactory; 16 | use Imagine\Factory\ClassFactoryInterface; 17 | use Imagine\Image\Metadata\MetadataReaderInterface; 18 | 19 | abstract class AbstractImagine implements ImagineInterface 20 | { 21 | /** 22 | * @var \Imagine\Image\Metadata\MetadataReaderInterface|null 23 | */ 24 | private $metadataReader; 25 | 26 | /** 27 | * @var \Imagine\Factory\ClassFactoryInterface 28 | */ 29 | private $classFactory; 30 | 31 | /** 32 | * {@inheritdoc} 33 | * 34 | * @see \Imagine\Image\ImagineInterface::setMetadataReader() 35 | */ 36 | public function setMetadataReader(MetadataReaderInterface $metadataReader) 37 | { 38 | $this->metadataReader = $metadataReader; 39 | 40 | return $this; 41 | } 42 | 43 | /** 44 | * {@inheritdoc} 45 | * 46 | * @see \Imagine\Image\ImagineInterface::getMetadataReader() 47 | */ 48 | public function getMetadataReader() 49 | { 50 | if ($this->metadataReader === null) { 51 | $this->metadataReader = $this->getClassFactory()->createMetadataReader(); 52 | } 53 | 54 | return $this->metadataReader; 55 | } 56 | 57 | /** 58 | * {@inheritdoc} 59 | * 60 | * @see \Imagine\Factory\ClassFactoryAwareInterface::setClassFactory() 61 | */ 62 | public function setClassFactory(ClassFactoryInterface $classFactory) 63 | { 64 | $this->classFactory = $classFactory; 65 | 66 | return $this; 67 | } 68 | 69 | /** 70 | * {@inheritdoc} 71 | * 72 | * @see \Imagine\Factory\ClassFactoryAwareInterface::getClassFactory() 73 | */ 74 | public function getClassFactory() 75 | { 76 | if ($this->classFactory === null) { 77 | $this->classFactory = new ClassFactory(); 78 | } 79 | 80 | return $this->classFactory; 81 | } 82 | 83 | /** 84 | * Checks a path that could be used with ImagineInterface::open and returns 85 | * a proper string. 86 | * 87 | * @param string|object $path 88 | * 89 | * @throws \Imagine\Exception\InvalidArgumentException in case the given path is invalid 90 | * 91 | * @return string 92 | */ 93 | protected function checkPath($path) 94 | { 95 | // provide compatibility with objects such as \SplFileInfo 96 | if (is_object($path) && method_exists($path, '__toString')) { 97 | $path = (string) $path; 98 | } 99 | 100 | $handle = @fopen($path, 'r'); 101 | 102 | if ($handle === false) { 103 | throw new InvalidArgumentException(sprintf('File %s does not exist.', $path)); 104 | } 105 | 106 | fclose($handle); 107 | 108 | return $path; 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /src/Image/AbstractLayers.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | /* 4 | * This file is part of the Imagine package. 5 | * 6 | * (c) Bulat Shakirzyanov <mallluhuct@gmail.com> 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Imagine\Image; 13 | 14 | use Imagine\Factory\ClassFactory; 15 | use Imagine\Factory\ClassFactoryAwareInterface; 16 | use Imagine\Factory\ClassFactoryInterface; 17 | 18 | abstract class AbstractLayers implements LayersInterface, ClassFactoryAwareInterface 19 | { 20 | /** 21 | * @var \Imagine\Factory\ClassFactoryInterface|null 22 | */ 23 | private $classFactory; 24 | 25 | /** 26 | * {@inheritdoc} 27 | * 28 | * @see \Imagine\Image\LayersInterface::add() 29 | */ 30 | public function add(ImageInterface $image) 31 | { 32 | $this[] = $image; 33 | 34 | return $this; 35 | } 36 | 37 | /** 38 | * {@inheritdoc} 39 | * 40 | * @see \Imagine\Image\LayersInterface::set() 41 | */ 42 | public function set($offset, ImageInterface $image) 43 | { 44 | $this[$offset] = $image; 45 | 46 | return $this; 47 | } 48 | 49 | /** 50 | * {@inheritdoc} 51 | * 52 | * @see \Imagine\Image\LayersInterface::remove() 53 | */ 54 | public function remove($offset) 55 | { 56 | unset($this[$offset]); 57 | 58 | return $this; 59 | } 60 | 61 | /** 62 | * {@inheritdoc} 63 | * 64 | * @see \Imagine\Image\LayersInterface::get() 65 | */ 66 | public function get($offset) 67 | { 68 | return $this[$offset]; 69 | } 70 | 71 | /** 72 | * {@inheritdoc} 73 | * 74 | * @see \Imagine\Image\LayersInterface::has() 75 | */ 76 | public function has($offset) 77 | { 78 | return isset($this[$offset]); 79 | } 80 | 81 | /** 82 | * {@inheritdoc} 83 | * 84 | * @see \Imagine\Factory\ClassFactoryAwareInterface::setClassFactory() 85 | */ 86 | public function setClassFactory(ClassFactoryInterface $classFactory) 87 | { 88 | $this->classFactory = $classFactory; 89 | 90 | return $this; 91 | } 92 | 93 | /** 94 | * {@inheritdoc} 95 | * 96 | * @see \Imagine\Factory\ClassFactoryAwareInterface::getClassFactory() 97 | */ 98 | public function getClassFactory() 99 | { 100 | if ($this->classFactory === null) { 101 | $this->classFactory = new ClassFactory(); 102 | } 103 | 104 | return $this->classFactory; 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/Image/Box.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | /* 4 | * This file is part of the Imagine package. 5 | * 6 | * (c) Bulat Shakirzyanov <mallluhuct@gmail.com> 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Imagine\Image; 13 | 14 | use Imagine\Exception\InvalidArgumentException; 15 | 16 | /** 17 | * A box implementation. 18 | */ 19 | final class Box implements BoxInterface 20 | { 21 | /** 22 | * @var int 23 | */ 24 | private $width; 25 | 26 | /** 27 | * @var int 28 | */ 29 | private $height; 30 | 31 | /** 32 | * Constructs the Size with given width and height. 33 | * 34 | * @param int $width 35 | * @param int $height 36 | * 37 | * @throws \Imagine\Exception\InvalidArgumentException 38 | */ 39 | public function __construct($width, $height) 40 | { 41 | if (!\is_int($width)) { 42 | $width = (int) round($width); 43 | } 44 | if (!\is_int($height)) { 45 | $height = (int) round($height); 46 | } 47 | $this->width = $width; 48 | $this->height = $height; 49 | if ($this->width < 1 || $this->height < 1) { 50 | throw new InvalidArgumentException(sprintf('Length of either side cannot be 0 or negative, current size is %sx%s', $width, $height)); 51 | } 52 | } 53 | 54 | /** 55 | * {@inheritdoc} 56 | * 57 | * @see \Imagine\Image\BoxInterface::getWidth() 58 | */ 59 | public function getWidth() 60 | { 61 | return $this->width; 62 | } 63 | 64 | /** 65 | * {@inheritdoc} 66 | * 67 | * @see \Imagine\Image\BoxInterface::getHeight() 68 | */ 69 | public function getHeight() 70 | { 71 | return $this->height; 72 | } 73 | 74 | /** 75 | * {@inheritdoc} 76 | * 77 | * @see \Imagine\Image\BoxInterface::scale() 78 | */ 79 | public function scale($ratio) 80 | { 81 | $width = max(1, round($ratio * $this->width)); 82 | $height = max(1, round($ratio * $this->height)); 83 | 84 | return new self($width, $height); 85 | } 86 | 87 | /** 88 | * {@inheritdoc} 89 | * 90 | * @see \Imagine\Image\BoxInterface::increase() 91 | */ 92 | public function increase($size) 93 | { 94 | return new self((int) $size + $this->width, (int) $size + $this->height); 95 | } 96 | 97 | /** 98 | * {@inheritdoc} 99 | * 100 | * @see \Imagine\Image\BoxInterface::contains() 101 | */ 102 | public function contains(BoxInterface $box, ?PointInterface $start = null) 103 | { 104 | $start = $start ? $start : new Point(0, 0); 105 | 106 | return $start->in($this) && $this->width >= $box->getWidth() + $start->getX() && $this->height >= $box->getHeight() + $start->getY(); 107 | } 108 | 109 | /** 110 | * {@inheritdoc} 111 | * 112 | * @see \Imagine\Image\BoxInterface::square() 113 | */ 114 | public function square() 115 | { 116 | return $this->width * $this->height; 117 | } 118 | 119 | /** 120 | * {@inheritdoc} 121 | * 122 | * @see \Imagine\Image\BoxInterface::__toString() 123 | */ 124 | public function __toString() 125 | { 126 | return sprintf('%dx%d px', $this->width, $this->height); 127 | } 128 | 129 | /** 130 | * {@inheritdoc} 131 | * 132 | * @see \Imagine\Image\BoxInterface::widen() 133 | */ 134 | public function widen($width) 135 | { 136 | return $this->scale($width / $this->width); 137 | } 138 | 139 | /** 140 | * {@inheritdoc} 141 | * 142 | * @see \Imagine\Image\BoxInterface::heighten() 143 | */ 144 | public function heighten($height) 145 | { 146 | return $this->scale($height / $this->height); 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /src/Image/BoxInterface.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | /* 4 | * This file is part of the Imagine package. 5 | * 6 | * (c) Bulat Shakirzyanov <mallluhuct@gmail.com> 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Imagine\Image; 13 | 14 | /** 15 | * Interface for a box. 16 | */ 17 | interface BoxInterface 18 | { 19 | /** 20 | * Gets box height. 21 | * 22 | * @return int 23 | */ 24 | public function getHeight(); 25 | 26 | /** 27 | * Gets box width. 28 | * 29 | * @return int 30 | */ 31 | public function getWidth(); 32 | 33 | /** 34 | * Creates new BoxInterface instance with ratios applied to both sides. 35 | * 36 | * @param float $ratio 37 | * 38 | * @return static 39 | */ 40 | public function scale($ratio); 41 | 42 | /** 43 | * Creates new BoxInterface, adding given size to both sides. 44 | * 45 | * @param int $size 46 | * 47 | * @return static 48 | */ 49 | public function increase($size); 50 | 51 | /** 52 | * Checks whether current box can fit given box at a given start position, 53 | * start position defaults to top left corner xy(0,0). 54 | * 55 | * @param \Imagine\Image\BoxInterface $box 56 | * @param \Imagine\Image\PointInterface $start 57 | * 58 | * @return bool 59 | */ 60 | public function contains(BoxInterface $box, ?PointInterface $start = null); 61 | 62 | /** 63 | * Gets current box square, useful for getting total number of pixels in a 64 | * given box. 65 | * 66 | * @return int 67 | */ 68 | public function square(); 69 | 70 | /** 71 | * Returns a string representation of the current box. 72 | * 73 | * @return string 74 | */ 75 | public function __toString(); 76 | 77 | /** 78 | * Resizes box to given width, constraining proportions and returns the new box. 79 | * 80 | * @param int $width 81 | * 82 | * @return static 83 | */ 84 | public function widen($width); 85 | 86 | /** 87 | * Resizes box to given height, constraining proportions and returns the new box. 88 | * 89 | * @param int $height 90 | * 91 | * @return static 92 | */ 93 | public function heighten($height); 94 | } 95 | -------------------------------------------------------------------------------- /src/Image/Fill/FillInterface.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | /* 4 | * This file is part of the Imagine package. 5 | * 6 | * (c) Bulat Shakirzyanov <mallluhuct@gmail.com> 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Imagine\Image\Fill; 13 | 14 | use Imagine\Image\PointInterface; 15 | 16 | /** 17 | * Interface for the fill. 18 | */ 19 | interface FillInterface 20 | { 21 | /** 22 | * Gets color of the fill for the given position. 23 | * 24 | * @param \Imagine\Image\PointInterface $position 25 | * 26 | * @return \Imagine\Image\Palette\Color\ColorInterface 27 | */ 28 | public function getColor(PointInterface $position); 29 | } 30 | -------------------------------------------------------------------------------- /src/Image/Fill/Gradient/Horizontal.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | /* 4 | * This file is part of the Imagine package. 5 | * 6 | * (c) Bulat Shakirzyanov <mallluhuct@gmail.com> 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Imagine\Image\Fill\Gradient; 13 | 14 | use Imagine\Image\PointInterface; 15 | 16 | /** 17 | * Horizontal gradient fill. 18 | */ 19 | final class Horizontal extends Linear 20 | { 21 | /** 22 | * {@inheritdoc} 23 | * 24 | * @see \Imagine\Image\Fill\Gradient\Linear::getDistance() 25 | */ 26 | public function getDistance(PointInterface $position) 27 | { 28 | return $position->getX(); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Image/Fill/Gradient/Linear.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | /* 4 | * This file is part of the Imagine package. 5 | * 6 | * (c) Bulat Shakirzyanov <mallluhuct@gmail.com> 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Imagine\Image\Fill\Gradient; 13 | 14 | use Imagine\Image\Fill\FillInterface; 15 | use Imagine\Image\Palette\Color\ColorInterface; 16 | use Imagine\Image\PointInterface; 17 | 18 | /** 19 | * Linear gradient fill. 20 | */ 21 | abstract class Linear implements FillInterface 22 | { 23 | /** 24 | * @var int 25 | */ 26 | private $length; 27 | 28 | /** 29 | * @var \Imagine\Image\Palette\Color\ColorInterface 30 | */ 31 | private $start; 32 | 33 | /** 34 | * @var \Imagine\Image\Palette\Color\ColorInterface 35 | */ 36 | private $end; 37 | 38 | /** 39 | * Constructs a linear gradient with overall gradient length, and start and 40 | * end shades, which default to 0 and 255 accordingly. 41 | * 42 | * @param int $length 43 | * @param \Imagine\Image\Palette\Color\ColorInterface $start 44 | * @param \Imagine\Image\Palette\Color\ColorInterface $end 45 | */ 46 | final public function __construct($length, ColorInterface $start, ColorInterface $end) 47 | { 48 | $this->length = $length; 49 | $this->start = $start; 50 | $this->end = $end; 51 | } 52 | 53 | /** 54 | * {@inheritdoc} 55 | * 56 | * @see \Imagine\Image\Fill\FillInterface::getColor() 57 | */ 58 | final public function getColor(PointInterface $position) 59 | { 60 | $l = $this->getDistance($position); 61 | 62 | if ($l >= $this->length) { 63 | return $this->end; 64 | } 65 | 66 | if ($l < 0) { 67 | return $this->start; 68 | } 69 | 70 | return $this->start->getPalette()->blend($this->start, $this->end, $l / $this->length); 71 | } 72 | 73 | /** 74 | * @return \Imagine\Image\Palette\Color\ColorInterface 75 | */ 76 | final public function getStart() 77 | { 78 | return $this->start; 79 | } 80 | 81 | /** 82 | * @return \Imagine\Image\Palette\Color\ColorInterface 83 | */ 84 | final public function getEnd() 85 | { 86 | return $this->end; 87 | } 88 | 89 | /** 90 | * Get the distance of the position relative to the beginning of the gradient. 91 | * 92 | * @param \Imagine\Image\PointInterface $position 93 | * 94 | * @return int 95 | */ 96 | abstract protected function getDistance(PointInterface $position); 97 | } 98 | -------------------------------------------------------------------------------- /src/Image/Fill/Gradient/Vertical.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | /* 4 | * This file is part of the Imagine package. 5 | * 6 | * (c) Bulat Shakirzyanov <mallluhuct@gmail.com> 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Imagine\Image\Fill\Gradient; 13 | 14 | use Imagine\Image\PointInterface; 15 | 16 | /** 17 | * Vertical gradient fill. 18 | */ 19 | final class Vertical extends Linear 20 | { 21 | /** 22 | * {@inheritdoc} 23 | * 24 | * @see \Imagine\Image\Fill\Gradient\Linear::getDistance() 25 | */ 26 | public function getDistance(PointInterface $position) 27 | { 28 | return $position->getY(); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Image/FontInterface.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | /* 4 | * This file is part of the Imagine package. 5 | * 6 | * (c) Bulat Shakirzyanov <mallluhuct@gmail.com> 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Imagine\Image; 13 | 14 | /** 15 | * The font interface. 16 | */ 17 | interface FontInterface 18 | { 19 | /** 20 | * Gets the fontfile for current font. 21 | * 22 | * @return string 23 | */ 24 | public function getFile(); 25 | 26 | /** 27 | * Gets font's integer point size. 28 | * 29 | * @return int 30 | */ 31 | public function getSize(); 32 | 33 | /** 34 | * Gets font's color. 35 | * 36 | * @return \Imagine\Image\Palette\Color\ColorInterface 37 | */ 38 | public function getColor(); 39 | 40 | /** 41 | * Gets BoxInterface of font size on the image based on string and angle. 42 | * 43 | * @param string $string 44 | * @param int $angle 45 | * 46 | * @return \Imagine\Image\BoxInterface 47 | */ 48 | public function box($string, $angle = 0); 49 | 50 | /** 51 | * Split a string into multiple lines so that it fits a specific width. 52 | * 53 | * @param string $string The text to be wrapped 54 | * @param int $maxWidth The maximum width of the text 55 | * @param int $angle 56 | * 57 | * @return string 58 | */ 59 | public function wrapText($string, $maxWidth, $angle = 0); 60 | } 61 | -------------------------------------------------------------------------------- /src/Image/Format.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | namespace Imagine\Image; 4 | 5 | use ReflectionClass; 6 | 7 | /** 8 | * Represent an image format. 9 | * 10 | * @since 1.3.0 11 | */ 12 | class Format 13 | { 14 | const ID_AVIF = 'avif'; 15 | 16 | const ID_BMP = 'bmp'; 17 | 18 | const ID_GIF = 'gif'; 19 | 20 | const ID_HEIC = 'heic'; 21 | 22 | const ID_JPEG = 'jpeg'; 23 | 24 | const ID_JXL = 'jxl'; 25 | 26 | const ID_PNG = 'png'; 27 | 28 | const ID_WBMP = 'wbmp'; 29 | 30 | const ID_WEBP = 'webp'; 31 | 32 | const ID_XBM = 'xbm'; 33 | 34 | /** 35 | * @var \Imagine\Image\FormatList|null 36 | */ 37 | private static $all = null; 38 | 39 | /** 40 | * @var string 41 | */ 42 | private $id; 43 | 44 | /** 45 | * @var string 46 | */ 47 | private $mimeType; 48 | 49 | /** 50 | * @var string 51 | */ 52 | private $canonicalFileExtension; 53 | 54 | /** 55 | * @var string[] 56 | */ 57 | private $alternativeIDs; 58 | 59 | /** 60 | * @param string $id 61 | * @param string $fileExtension 62 | * @param string $mimeType 63 | * @param string[] $alternativeIDs 64 | * @param mixed $canonicalFileExtension 65 | */ 66 | private function __construct($id, $mimeType, $canonicalFileExtension, $alternativeIDs = array()) 67 | { 68 | $this->id = $id; 69 | $this->mimeType = $mimeType; 70 | $this->canonicalFileExtension = $canonicalFileExtension; 71 | $this->alternativeIDs = $alternativeIDs; 72 | } 73 | 74 | /** 75 | * @return string 76 | */ 77 | public function getID() 78 | { 79 | return $this->id; 80 | } 81 | 82 | /** 83 | * @return string 84 | */ 85 | public function getMimeType() 86 | { 87 | return $this->mimeType; 88 | } 89 | 90 | /** 91 | * @return string 92 | */ 93 | public function getCanonicalFileExtension() 94 | { 95 | return $this->canonicalFileExtension; 96 | } 97 | 98 | /** 99 | * @return string[] 100 | */ 101 | public function getAlternativeIDs() 102 | { 103 | return $this->alternativeIDs; 104 | } 105 | 106 | /** 107 | * Get a format given its ID. 108 | * 109 | * @param static|string $format the format (a Format instance of a format ID) 110 | * 111 | * @return static|null 112 | */ 113 | public static function get($format) 114 | { 115 | return static::getList()->find($format); 116 | } 117 | 118 | /** 119 | * @return static[] 120 | */ 121 | public static function getAll() 122 | { 123 | return static::getList()->getAll(); 124 | } 125 | 126 | /** 127 | * @return \Imagine\Image\FormatList 128 | */ 129 | protected static function getList() 130 | { 131 | if (self::$all !== null) { 132 | return self::$all; 133 | } 134 | $class = new ReflectionClass(get_called_class()); 135 | $formats = array(); 136 | foreach ($class->getConstants() as $constantName => $constantValue) { 137 | if (strpos($constantName, 'ID_') === 0) { 138 | $formats[] = static::create($constantValue); 139 | } 140 | } 141 | self::$all = new FormatList($formats); 142 | 143 | return self::$all; 144 | } 145 | 146 | /** 147 | * @param string $formatID 148 | * 149 | * @return static 150 | */ 151 | protected static function create($formatID) 152 | { 153 | switch ($formatID) { 154 | case static::ID_JPEG: 155 | return new static($formatID, 'image/jpeg', 'jpg', array('jpg', 'pjpeg', 'jfif')); 156 | case static::ID_WBMP: 157 | return new static($formatID, 'image/vnd.wap.wbmp', $formatID); 158 | default: 159 | return new static($formatID, "image/{$formatID}", $formatID); 160 | } 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /src/Image/FormatList.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | namespace Imagine\Image; 4 | 5 | /** 6 | * Holds a list of image formats. 7 | * 8 | * @since 1.3.0 9 | */ 10 | class FormatList 11 | { 12 | /** 13 | * @var \Imagine\Image\Format[] 14 | */ 15 | private $formats; 16 | 17 | public function __construct(array $formats) 18 | { 19 | $this->formats = $formats; 20 | } 21 | 22 | /** 23 | * @return \Imagine\Image\Format[] 24 | */ 25 | public function getAll() 26 | { 27 | return $this->formats; 28 | } 29 | 30 | /** 31 | * @return string[] 32 | */ 33 | public function getAllIDs() 34 | { 35 | $result = array(); 36 | foreach ($this->getAll() as $format) { 37 | $result[] = $format->getID(); 38 | } 39 | 40 | return $result; 41 | } 42 | 43 | /** 44 | * Get a format given its ID. 45 | * 46 | * @param \Imagine\Image\Format|string $format the format (a Format instance of a format ID) 47 | * 48 | * @return \Imagine\Image\Format|null 49 | */ 50 | public function find($format) 51 | { 52 | if (is_string($format)) { 53 | $format = strtolower(trim($format)); 54 | if ($format === '') { 55 | return null; 56 | } 57 | foreach ($this->getAll() as $f) { 58 | if ($f->getID() === $format || in_array($format, $f->getAlternativeIDs(), true)) { 59 | return $f; 60 | } 61 | } 62 | 63 | return null; 64 | } 65 | 66 | return in_array($format, $this->getAll(), true) ? $format : null; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/Image/Histogram/Bucket.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | /* 4 | * This file is part of the Imagine package. 5 | * 6 | * (c) Bulat Shakirzyanov <mallluhuct@gmail.com> 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Imagine\Image\Histogram; 13 | 14 | /** 15 | * Bucket histogram. 16 | */ 17 | final class Bucket implements \Countable 18 | { 19 | /** 20 | * @var \Imagine\Image\Histogram\Range 21 | */ 22 | private $range; 23 | 24 | /** 25 | * @var int 26 | */ 27 | private $count; 28 | 29 | /** 30 | * @param \Imagine\Image\Histogram\Range $range 31 | * @param int $count 32 | */ 33 | public function __construct(Range $range, $count = 0) 34 | { 35 | $this->range = $range; 36 | $this->count = $count; 37 | } 38 | 39 | /** 40 | * @param int $value 41 | * 42 | * @return $this 43 | */ 44 | public function add($value) 45 | { 46 | if ($this->range->contains($value)) { 47 | $this->count++; 48 | } 49 | 50 | return $this; 51 | } 52 | 53 | /** 54 | * Get the number of elements in the bucket. 55 | * 56 | * @return int 57 | */ 58 | #[\ReturnTypeWillChange] 59 | public function count() 60 | { 61 | return $this->count; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/Image/Histogram/Range.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | /* 4 | * This file is part of the Imagine package. 5 | * 6 | * (c) Bulat Shakirzyanov <mallluhuct@gmail.com> 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Imagine\Image\Histogram; 13 | 14 | use Imagine\Exception\OutOfBoundsException; 15 | 16 | /** 17 | * Range histogram. 18 | */ 19 | final class Range 20 | { 21 | /** 22 | * @var int 23 | */ 24 | private $start; 25 | 26 | /** 27 | * @var int 28 | */ 29 | private $end; 30 | 31 | /** 32 | * @param int $start 33 | * @param int $end 34 | * 35 | * @throws \Imagine\Exception\OutOfBoundsException 36 | */ 37 | public function __construct($start, $end) 38 | { 39 | if ($end <= $start) { 40 | throw new OutOfBoundsException(sprintf('Range end cannot be bigger than start, %d %d given accordingly', $this->start, $this->end)); 41 | } 42 | 43 | $this->start = $start; 44 | $this->end = $end; 45 | } 46 | 47 | /** 48 | * @param int $value 49 | * 50 | * @return bool 51 | */ 52 | public function contains($value) 53 | { 54 | return $value >= $this->start && $value < $this->end; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/Image/ImagineInterface.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | /* 4 | * This file is part of the Imagine package. 5 | * 6 | * (c) Bulat Shakirzyanov <mallluhuct@gmail.com> 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Imagine\Image; 13 | 14 | use Imagine\Factory\ClassFactoryAwareInterface; 15 | use Imagine\Image\Metadata\MetadataReaderInterface; 16 | use Imagine\Image\Palette\Color\ColorInterface; 17 | 18 | /** 19 | * The imagine interface. 20 | */ 21 | interface ImagineInterface extends ClassFactoryAwareInterface 22 | { 23 | const VERSION = '1.5.1-dev'; 24 | 25 | /** 26 | * Creates a new empty image with an optional background color. 27 | * 28 | * @param \Imagine\Image\BoxInterface $size 29 | * @param \Imagine\Image\Palette\Color\ColorInterface $color 30 | * 31 | * @throws \Imagine\Exception\InvalidArgumentException 32 | * @throws \Imagine\Exception\RuntimeException 33 | * 34 | * @return \Imagine\Image\ImageInterface 35 | */ 36 | public function create(BoxInterface $size, ?ColorInterface $color = null); 37 | 38 | /** 39 | * Opens an existing image from $path. 40 | * 41 | * @param string|\Imagine\File\LoaderInterface|mixed $path the file path, a LoaderInterface instance, or an object whose string representation is the image path 42 | * 43 | * @throws \Imagine\Exception\RuntimeException 44 | * 45 | * @return \Imagine\Image\ImageInterface 46 | */ 47 | public function open($path); 48 | 49 | /** 50 | * Loads an image from a binary $string. 51 | * 52 | * @param string $string 53 | * 54 | * @throws \Imagine\Exception\RuntimeException 55 | * 56 | * @return \Imagine\Image\ImageInterface 57 | */ 58 | public function load($string); 59 | 60 | /** 61 | * Loads an image from a resource $resource. 62 | * 63 | * @param resource $resource 64 | * 65 | * @throws \Imagine\Exception\RuntimeException 66 | * 67 | * @return \Imagine\Image\ImageInterface 68 | */ 69 | public function read($resource); 70 | 71 | /** 72 | * Constructs a font with specified $file, $size and $color. 73 | * 74 | * The font size is to be specified in points (e.g. 10pt means 10) 75 | * 76 | * @param string $file 77 | * @param int $size 78 | * @param \Imagine\Image\Palette\Color\ColorInterface $color 79 | * 80 | * @return \Imagine\Image\FontInterface 81 | */ 82 | public function font($file, $size, ColorInterface $color); 83 | 84 | /** 85 | * Set the object to be used to read image metadata. 86 | * 87 | * @param \Imagine\Image\Metadata\MetadataReaderInterface $metadataReader 88 | * 89 | * @return $this 90 | */ 91 | public function setMetadataReader(MetadataReaderInterface $metadataReader); 92 | 93 | /** 94 | * Get the object to be used to read image metadata. 95 | * 96 | * @return \Imagine\Image\Metadata\MetadataReaderInterface 97 | */ 98 | public function getMetadataReader(); 99 | } 100 | -------------------------------------------------------------------------------- /src/Image/LayersInterface.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | /* 4 | * This file is part of the Imagine package. 5 | * 6 | * (c) Bulat Shakirzyanov <mallluhuct@gmail.com> 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Imagine\Image; 13 | 14 | /** 15 | * The layers interface. 16 | */ 17 | interface LayersInterface extends \Iterator, \Countable, \ArrayAccess 18 | { 19 | /** 20 | * Merge layers into the original objects. 21 | * 22 | * @throws \Imagine\Exception\RuntimeException 23 | */ 24 | public function merge(); 25 | 26 | /** 27 | * Animates layers. 28 | * 29 | * @param string $format The output output format 30 | * @param int $delay The delay in milliseconds between two frames 31 | * @param int $loops The number of loops, 0 means infinite 32 | * 33 | * @throws \Imagine\Exception\InvalidArgumentException In case an invalid argument is provided 34 | * @throws \Imagine\Exception\RuntimeException In case the driver fails to animate 35 | * 36 | * @return $this 37 | */ 38 | public function animate($format, $delay, $loops); 39 | 40 | /** 41 | * Coalesce layers. Each layer in the sequence is the same size as the first and composited with the next layer in 42 | * the sequence. 43 | * 44 | * @throws \Imagine\Exception\NotSupportedException 45 | * 46 | * @return $this 47 | */ 48 | public function coalesce(); 49 | 50 | /** 51 | * Adds an image at the end of the layers stack. 52 | * 53 | * @param \Imagine\Image\ImageInterface $image 54 | * 55 | * @throws \Imagine\Exception\RuntimeException 56 | * 57 | * @return $this 58 | */ 59 | public function add(ImageInterface $image); 60 | 61 | /** 62 | * Set an image at offset. 63 | * 64 | * @param int $offset 65 | * @param \Imagine\Image\ImageInterface $image 66 | * 67 | * @throws \Imagine\Exception\RuntimeException 68 | * @throws \Imagine\Exception\InvalidArgumentException 69 | * @throws \Imagine\Exception\OutOfBoundsException 70 | * 71 | * @return $this 72 | */ 73 | public function set($offset, ImageInterface $image); 74 | 75 | /** 76 | * Removes the image at offset. 77 | * 78 | * @param int $offset 79 | * 80 | * @throws \Imagine\Exception\RuntimeException 81 | * @throws \Imagine\Exception\InvalidArgumentException 82 | * 83 | * @return $this 84 | */ 85 | public function remove($offset); 86 | 87 | /** 88 | * Returns the image at offset. 89 | * 90 | * @param int $offset 91 | * 92 | * @throws \Imagine\Exception\RuntimeException 93 | * @throws \Imagine\Exception\InvalidArgumentException 94 | * 95 | * @return \Imagine\Image\ImageInterface 96 | */ 97 | public function get($offset); 98 | 99 | /** 100 | * Returns true if a layer at offset is preset. 101 | * 102 | * @param int $offset 103 | * 104 | * @return bool 105 | */ 106 | public function has($offset); 107 | } 108 | -------------------------------------------------------------------------------- /src/Image/Metadata/AbstractMetadataReader.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | /* 4 | * This file is part of the Imagine package. 5 | * 6 | * (c) Bulat Shakirzyanov <mallluhuct@gmail.com> 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Imagine\Image\Metadata; 13 | 14 | use Imagine\Exception\InvalidArgumentException; 15 | use Imagine\File\Loader; 16 | use Imagine\File\LoaderInterface; 17 | 18 | /** 19 | * Base class for the default metadata readers. 20 | */ 21 | abstract class AbstractMetadataReader implements MetadataReaderInterface 22 | { 23 | /** 24 | * {@inheritdoc} 25 | * 26 | * @see \Imagine\Image\Metadata\MetadataReaderInterface::readFile() 27 | */ 28 | public function readFile($file) 29 | { 30 | $loader = $file instanceof LoaderInterface ? $file : new Loader($file); 31 | 32 | return new MetadataBag(array_merge($this->getStreamMetadata($loader), $this->extractFromFile($loader))); 33 | } 34 | 35 | /** 36 | * {@inheritdoc} 37 | * 38 | * @see \Imagine\Image\Metadata\MetadataReaderInterface::readData() 39 | */ 40 | public function readData($data, $originalResource = null) 41 | { 42 | if ($originalResource !== null) { 43 | return new MetadataBag(array_merge($this->getStreamMetadata($originalResource), $this->extractFromData($data))); 44 | } 45 | 46 | return new MetadataBag($this->extractFromData($data)); 47 | } 48 | 49 | /** 50 | * {@inheritdoc} 51 | * 52 | * @see \Imagine\Image\Metadata\MetadataReaderInterface::readStream() 53 | */ 54 | public function readStream($resource) 55 | { 56 | if (!is_resource($resource)) { 57 | throw new InvalidArgumentException('Invalid resource provided.'); 58 | } 59 | 60 | return new MetadataBag(array_merge($this->getStreamMetadata($resource), $this->extractFromStream($resource))); 61 | } 62 | 63 | /** 64 | * Gets the URI from a stream resource. 65 | * 66 | * @param resource|\Imagine\File\LoaderInterface $resource 67 | * 68 | * @return array 69 | */ 70 | private function getStreamMetadata($resource) 71 | { 72 | $metadata = array(); 73 | 74 | if ($resource instanceof LoaderInterface) { 75 | $metadata['uri'] = $resource->getPath(); 76 | if ($resource->isLocalFile()) { 77 | $metadata['filepath'] = realpath($resource->getPath()); 78 | } 79 | } elseif (false !== $data = @stream_get_meta_data($resource)) { 80 | if (isset($data['uri'])) { 81 | $metadata['uri'] = $data['uri']; 82 | if (stream_is_local($resource)) { 83 | $metadata['filepath'] = realpath($data['uri']); 84 | } 85 | } 86 | } 87 | 88 | return $metadata; 89 | } 90 | 91 | /** 92 | * Extracts metadata from a file. 93 | * 94 | * @param string|\Imagine\File\LoaderInterface $file 95 | * 96 | * @return array An associative array of metadata 97 | */ 98 | abstract protected function extractFromFile($file); 99 | 100 | /** 101 | * Extracts metadata from raw data. 102 | * 103 | * @param string $data 104 | * 105 | * @return array An associative array of metadata 106 | */ 107 | abstract protected function extractFromData($data); 108 | 109 | /** 110 | * Extracts metadata from a stream. 111 | * 112 | * @param resource $resource 113 | * 114 | * @return array An associative array of metadata 115 | */ 116 | abstract protected function extractFromStream($resource); 117 | } 118 | -------------------------------------------------------------------------------- /src/Image/Metadata/DefaultMetadataReader.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | /* 4 | * This file is part of the Imagine package. 5 | * 6 | * (c) Bulat Shakirzyanov <mallluhuct@gmail.com> 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Imagine\Image\Metadata; 13 | 14 | /** 15 | * A metadata reader that actually doesn't try to extract metadata. 16 | */ 17 | class DefaultMetadataReader extends AbstractMetadataReader 18 | { 19 | /** 20 | * {@inheritdoc} 21 | * 22 | * @see \Imagine\Image\Metadata\AbstractMetadataReader::extractFromFile() 23 | */ 24 | protected function extractFromFile($file) 25 | { 26 | return array(); 27 | } 28 | 29 | /** 30 | * {@inheritdoc} 31 | * 32 | * @see \Imagine\Image\Metadata\AbstractMetadataReader::extractFromData() 33 | */ 34 | protected function extractFromData($data) 35 | { 36 | return array(); 37 | } 38 | 39 | /** 40 | * {@inheritdoc} 41 | * 42 | * @see \Imagine\Image\Metadata\AbstractMetadataReader::extractFromStream() 43 | */ 44 | protected function extractFromStream($resource) 45 | { 46 | return array(); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/Image/Metadata/ExifMetadataReader.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | /* 4 | * This file is part of the Imagine package. 5 | * 6 | * (c) Bulat Shakirzyanov <mallluhuct@gmail.com> 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Imagine\Image\Metadata; 13 | 14 | use Imagine\Exception\NotSupportedException; 15 | use Imagine\File\Loader; 16 | use Imagine\File\LoaderInterface; 17 | use Imagine\Utils\ErrorHandling; 18 | 19 | /** 20 | * Metadata driven by Exif information. 21 | */ 22 | class ExifMetadataReader extends AbstractMetadataReader 23 | { 24 | /** 25 | * @throws \Imagine\Exception\NotSupportedException 26 | */ 27 | public function __construct() 28 | { 29 | $whyNot = static::getUnsupportedReason(); 30 | if ($whyNot !== '') { 31 | throw new NotSupportedException($whyNot); 32 | } 33 | } 34 | 35 | /** 36 | * Get the reason why this metadata reader is not supported. 37 | * 38 | * @return string empty string if the reader is available 39 | */ 40 | public static function getUnsupportedReason() 41 | { 42 | if (!function_exists('exif_read_data')) { 43 | return 'The PHP EXIF extension is required to use the ExifMetadataReader'; 44 | } 45 | 46 | if (PHP_VERSION_ID < 70200) { 47 | if (!in_array('data', stream_get_wrappers(), true)) { 48 | return 'The data:// stream wrapper must be enabled'; 49 | } 50 | if (in_array(ini_get('allow_url_fopen'), array('', '0', 0), true)) { 51 | return 'The allow_url_fopen php.ini configuration key must be set to 1'; 52 | } 53 | } 54 | 55 | return ''; 56 | } 57 | 58 | /** 59 | * Is this metadata reader supported? 60 | * 61 | * @return bool 62 | */ 63 | public static function isSupported() 64 | { 65 | return static::getUnsupportedReason() === ''; 66 | } 67 | 68 | /** 69 | * {@inheritdoc} 70 | * 71 | * @see \Imagine\Image\Metadata\AbstractMetadataReader::extractFromFile() 72 | */ 73 | protected function extractFromFile($file) 74 | { 75 | $loader = $file instanceof LoaderInterface ? $file : new Loader($file); 76 | 77 | if ($loader->isLocalFile()) { 78 | return $this->extract($loader->getPath()); 79 | } 80 | 81 | return $this->doReadData($loader->getData()); 82 | } 83 | 84 | /** 85 | * {@inheritdoc} 86 | * 87 | * @see \Imagine\Image\Metadata\AbstractMetadataReader::extractFromData() 88 | */ 89 | protected function extractFromData($data) 90 | { 91 | return $this->doReadData($data); 92 | } 93 | 94 | /** 95 | * {@inheritdoc} 96 | * 97 | * @see \Imagine\Image\Metadata\AbstractMetadataReader::extractFromStream() 98 | */ 99 | protected function extractFromStream($resource) 100 | { 101 | return PHP_VERSION_ID < 70200 ? $this->doReadData(stream_get_contents($resource)) : $this->extract($resource); 102 | } 103 | 104 | /** 105 | * Extracts metadata from raw data, merges with existing metadata. 106 | * 107 | * @param string $data 108 | * 109 | * @return array 110 | */ 111 | private function doReadData($data) 112 | { 113 | if (PHP_VERSION_ID >= 70200) { 114 | $stream = fopen('php://memory', 'r+'); 115 | fwrite($stream, $data); 116 | rewind($stream); 117 | 118 | return $this->extract($stream); 119 | } 120 | 121 | if (substr($data, 0, 2) === 'II') { 122 | $mime = 'image/tiff'; 123 | } else { 124 | $mime = 'image/jpeg'; 125 | } 126 | 127 | return $this->extract('data://' . $mime . ';base64,' . base64_encode($data)); 128 | } 129 | 130 | /** 131 | * Performs the exif data extraction given a path, data-URI representation or stream. 132 | * 133 | * @param string|resource $path the path to the file, the data-URI representation or a stream 134 | * 135 | * @return array 136 | */ 137 | private function extract($path) 138 | { 139 | try { 140 | $exifData = ErrorHandling::ignoring(-1, function () use ($path) { 141 | return @exif_read_data($path, null, true, false); 142 | }); 143 | } catch (\Exception $e) { 144 | $exifData = false; 145 | } catch (\Throwable $e) { 146 | $exifData = false; 147 | } 148 | if (!is_array($exifData)) { 149 | return array(); 150 | } 151 | 152 | $metadata = array(); 153 | foreach ($exifData as $prefix => $values) { 154 | if (is_array($values)) { 155 | $prefix = strtolower($prefix); 156 | foreach ($values as $prop => $value) { 157 | $metadata[$prefix . '.' . $prop] = $value; 158 | } 159 | } 160 | } 161 | 162 | return $metadata; 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /src/Image/Metadata/MetadataBag.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | /* 4 | * This file is part of the Imagine package. 5 | * 6 | * (c) Bulat Shakirzyanov <mallluhuct@gmail.com> 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Imagine\Image\Metadata; 13 | 14 | /** 15 | * The container of the data extracted from metadata. 16 | */ 17 | class MetadataBag implements \ArrayAccess, \IteratorAggregate, \Countable 18 | { 19 | /** 20 | * @var array 21 | */ 22 | private $data; 23 | 24 | /** 25 | * @param array $data 26 | */ 27 | public function __construct(array $data = array()) 28 | { 29 | $this->data = $data; 30 | } 31 | 32 | /** 33 | * Returns the metadata key, default value if it does not exist. 34 | * 35 | * @param string $key 36 | * @param mixed|null $default 37 | * 38 | * @return mixed 39 | */ 40 | public function get($key, $default = null) 41 | { 42 | return array_key_exists($key, $this->data) ? $this->data[$key] : $default; 43 | } 44 | 45 | /** 46 | * {@inheritdoc} 47 | * 48 | * @see \Countable::count() 49 | * 50 | * @return int 51 | */ 52 | #[\ReturnTypeWillChange] 53 | public function count() 54 | { 55 | return count($this->data); 56 | } 57 | 58 | /** 59 | * {@inheritdoc} 60 | * 61 | * @see \IteratorAggregate::getIterator() 62 | * 63 | * @return \ArrayIterator 64 | */ 65 | #[\ReturnTypeWillChange] 66 | public function getIterator() 67 | { 68 | return new \ArrayIterator($this->data); 69 | } 70 | 71 | /** 72 | * {@inheritdoc} 73 | * 74 | * @see \ArrayAccess::offsetExists() 75 | * 76 | * @return bool 77 | */ 78 | #[\ReturnTypeWillChange] 79 | public function offsetExists($offset) 80 | { 81 | return array_key_exists($offset, $this->data); 82 | } 83 | 84 | /** 85 | * {@inheritdoc} 86 | * 87 | * @see \ArrayAccess::offsetSet() 88 | * 89 | * @return void 90 | */ 91 | #[\ReturnTypeWillChange] 92 | public function offsetSet($offset, $value) 93 | { 94 | $this->data[$offset] = $value; 95 | } 96 | 97 | /** 98 | * {@inheritdoc} 99 | * 100 | * @see \ArrayAccess::offsetUnset() 101 | * 102 | * @return void 103 | */ 104 | #[\ReturnTypeWillChange] 105 | public function offsetUnset($offset) 106 | { 107 | unset($this->data[$offset]); 108 | } 109 | 110 | /** 111 | * {@inheritdoc} 112 | * 113 | * @see \ArrayAccess::offsetGet() 114 | * 115 | * @return mixed 116 | */ 117 | #[\ReturnTypeWillChange] 118 | public function offsetGet($offset) 119 | { 120 | return $this->get($offset); 121 | } 122 | 123 | /** 124 | * Returns metadata as an associative array. 125 | * 126 | * @return array 127 | */ 128 | public function toArray() 129 | { 130 | return $this->data; 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /src/Image/Metadata/MetadataReaderInterface.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | /* 4 | * This file is part of the Imagine package. 5 | * 6 | * (c) Bulat Shakirzyanov <mallluhuct@gmail.com> 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Imagine\Image\Metadata; 13 | 14 | /** 15 | * Interface that metadata readers must implement. 16 | */ 17 | interface MetadataReaderInterface 18 | { 19 | /** 20 | * Reads metadata from a file. 21 | * 22 | * @param string|\Imagine\File\LoaderInterface $file the path to the file where to read metadata 23 | * 24 | * @throws \Imagine\Exception\InvalidArgumentException in case the file does not exist 25 | * 26 | * @return \Imagine\Image\Metadata\MetadataBag 27 | */ 28 | public function readFile($file); 29 | 30 | /** 31 | * Reads metadata from a binary string. 32 | * 33 | * @param string $data the binary string to read 34 | * @param resource|null $originalResource an optional resource to gather stream metadata 35 | * 36 | * @return \Imagine\Image\Metadata\MetadataBag 37 | */ 38 | public function readData($data, $originalResource = null); 39 | 40 | /** 41 | * Reads metadata from a stream. 42 | * 43 | * @param resource $resource the stream to read 44 | * 45 | * @throws \Imagine\Exception\InvalidArgumentException in case the resource is not valid 46 | * 47 | * @return \Imagine\Image\Metadata\MetadataBag 48 | */ 49 | public function readStream($resource); 50 | } 51 | -------------------------------------------------------------------------------- /src/Image/Palette/CMYK.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | /* 4 | * This file is part of the Imagine package. 5 | * 6 | * (c) Bulat Shakirzyanov <mallluhuct@gmail.com> 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Imagine\Image\Palette; 13 | 14 | use Imagine\Exception\InvalidArgumentException; 15 | use Imagine\Exception\RuntimeException; 16 | use Imagine\Image\Palette\Color\CMYK as CMYKColor; 17 | use Imagine\Image\Palette\Color\ColorInterface; 18 | use Imagine\Image\Profile; 19 | use Imagine\Image\ProfileInterface; 20 | 21 | /** 22 | * The CMYK palette. 23 | */ 24 | class CMYK implements PaletteInterface 25 | { 26 | /** 27 | * @var \Imagine\Image\Palette\ColorParser 28 | */ 29 | private $parser; 30 | 31 | /** 32 | * @var \Imagine\Image\ProfileInterface|null 33 | */ 34 | private $profile; 35 | 36 | /** 37 | * @var \Imagine\Image\Palette\Color\CMYK[] 38 | */ 39 | private static $colors = array(); 40 | 41 | public function __construct() 42 | { 43 | $this->parser = new ColorParser(); 44 | } 45 | 46 | /** 47 | * {@inheritdoc} 48 | * 49 | * @see \Imagine\Image\Palette\PaletteInterface::name() 50 | */ 51 | public function name() 52 | { 53 | return PaletteInterface::PALETTE_CMYK; 54 | } 55 | 56 | /** 57 | * {@inheritdoc} 58 | * 59 | * @see \Imagine\Image\Palette\PaletteInterface::pixelDefinition() 60 | */ 61 | public function pixelDefinition() 62 | { 63 | return array( 64 | ColorInterface::COLOR_CYAN, 65 | ColorInterface::COLOR_MAGENTA, 66 | ColorInterface::COLOR_YELLOW, 67 | ColorInterface::COLOR_KEYLINE, 68 | ); 69 | } 70 | 71 | /** 72 | * {@inheritdoc} 73 | * 74 | * @see \Imagine\Image\Palette\PaletteInterface::supportsAlpha() 75 | */ 76 | public function supportsAlpha() 77 | { 78 | return false; 79 | } 80 | 81 | /** 82 | * {@inheritdoc} 83 | * 84 | * @see \Imagine\Image\Palette\PaletteInterface::getChannelsMaxValue() 85 | */ 86 | public function getChannelsMaxValue() 87 | { 88 | return 100; 89 | } 90 | 91 | /** 92 | * {@inheritdoc} 93 | * 94 | * @see \Imagine\Image\Palette\PaletteInterface::color() 95 | */ 96 | public function color($color, $alpha = null) 97 | { 98 | if ($alpha !== null && $alpha !== 100) { 99 | throw new InvalidArgumentException('CMYK palette does not support alpha'); 100 | } 101 | 102 | $color = $this->parser->parseToCMYK($color); 103 | $index = sprintf('cmyk(%d, %d, %d, %d)', $color[0], $color[1], $color[2], $color[3]); 104 | 105 | if (array_key_exists($index, self::$colors) === false) { 106 | self::$colors[$index] = new CMYKColor($this, $color); 107 | } 108 | 109 | return self::$colors[$index]; 110 | } 111 | 112 | /** 113 | * {@inheritdoc} 114 | * 115 | * @see \Imagine\Image\Palette\PaletteInterface::blend() 116 | */ 117 | public function blend(ColorInterface $color1, ColorInterface $color2, $amount) 118 | { 119 | if (!$color1 instanceof CMYKColor || !$color2 instanceof CMYKColor) { 120 | throw new RuntimeException('CMYK palette can only blend CMYK colors'); 121 | } 122 | $max = $this->getChannelsMaxValue(); 123 | 124 | return $this->color(array( 125 | min($max, $color1->getCyan() + $color2->getCyan() * $amount), 126 | min($max, $color1->getMagenta() + $color2->getMagenta() * $amount), 127 | min($max, $color1->getYellow() + $color2->getYellow() * $amount), 128 | min($max, $color1->getKeyline() + $color2->getKeyline() * $amount), 129 | )); 130 | } 131 | 132 | /** 133 | * {@inheritdoc} 134 | * 135 | * @see \Imagine\Image\Palette\PaletteInterface::useProfile() 136 | */ 137 | public function useProfile(ProfileInterface $profile) 138 | { 139 | $this->profile = $profile; 140 | 141 | return $this; 142 | } 143 | 144 | /** 145 | * {@inheritdoc} 146 | * 147 | * @see \Imagine\Image\Palette\PaletteInterface::profile() 148 | */ 149 | public function profile() 150 | { 151 | if (!$this->profile) { 152 | $this->profile = Profile::fromPath(__DIR__ . '/../../resources/Adobe/CMYK/USWebUncoated.icc'); 153 | } 154 | 155 | return $this->profile; 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /src/Image/Palette/Color/ColorInterface.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | /* 4 | * This file is part of the Imagine package. 5 | * 6 | * (c) Bulat Shakirzyanov <mallluhuct@gmail.com> 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Imagine\Image\Palette\Color; 13 | 14 | interface ColorInterface 15 | { 16 | /** 17 | * Channel name: red. 18 | * 19 | * @var string 20 | */ 21 | const COLOR_RED = 'red'; 22 | 23 | /** 24 | * Channel name: green. 25 | * 26 | * @var string 27 | */ 28 | const COLOR_GREEN = 'green'; 29 | 30 | /** 31 | * Channel name: blue. 32 | * 33 | * @var string 34 | */ 35 | const COLOR_BLUE = 'blue'; 36 | 37 | /** 38 | * Channel name: cyan. 39 | * 40 | * @var string 41 | */ 42 | const COLOR_CYAN = 'cyan'; 43 | 44 | /** 45 | * Channel name: magenta. 46 | * 47 | * @var string 48 | */ 49 | const COLOR_MAGENTA = 'magenta'; 50 | 51 | /** 52 | * Channel name: yellow. 53 | * 54 | * @var string 55 | */ 56 | const COLOR_YELLOW = 'yellow'; 57 | 58 | /** 59 | * Channel name: key (black). 60 | * 61 | * @var string 62 | */ 63 | const COLOR_KEYLINE = 'keyline'; 64 | 65 | /** 66 | * Channel name: gray. 67 | * 68 | * @var string 69 | */ 70 | const COLOR_GRAY = 'gray'; 71 | 72 | /** 73 | * Return the value of one of the component. 74 | * 75 | * @param string $component One of the ColorInterface::COLOR_* component 76 | * 77 | * @throws \Imagine\Exception\InvalidArgumentException if $component is not valid 78 | * 79 | * @return int|null 80 | */ 81 | public function getValue($component); 82 | 83 | /** 84 | * Returns percentage of transparency of the color (from 0 - fully transparent, to 100 - fully opaque). 85 | * 86 | * @return int|null return NULL if the color type does not support transparency 87 | */ 88 | public function getAlpha(); 89 | 90 | /** 91 | * Returns the palette attached to the current color. 92 | * 93 | * @return \Imagine\Image\Palette\PaletteInterface 94 | */ 95 | public function getPalette(); 96 | 97 | /** 98 | * Returns a copy of current color, incrementing the alpha channel by the given amount. 99 | * 100 | * @param int $alpha 101 | * 102 | * @throws \Imagine\Exception\RuntimeException if the color type does not support transparency 103 | * 104 | * @return static 105 | */ 106 | public function dissolve($alpha); 107 | 108 | /** 109 | * Returns a copy of the current color, lightened by the specified number of shades. 110 | * 111 | * @param int $shade 112 | * 113 | * @return static 114 | */ 115 | public function lighten($shade); 116 | 117 | /** 118 | * Returns a copy of the current color, darkened by the specified number of shades. 119 | * 120 | * @param int $shade 121 | * 122 | * @return static 123 | */ 124 | public function darken($shade); 125 | 126 | /** 127 | * Returns a gray related to the current color. 128 | * 129 | * @return static 130 | */ 131 | public function grayscale(); 132 | 133 | /** 134 | * Checks if the current color is opaque. 135 | * 136 | * @return bool 137 | */ 138 | public function isOpaque(); 139 | 140 | /** 141 | * Returns hex representation of the color. 142 | * 143 | * @return string 144 | */ 145 | public function __toString(); 146 | } 147 | -------------------------------------------------------------------------------- /src/Image/Palette/Color/Gray.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | /* 4 | * This file is part of the Imagine package. 5 | * 6 | * (c) Bulat Shakirzyanov <mallluhuct@gmail.com> 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Imagine\Image\Palette\Color; 13 | 14 | use Imagine\Exception\InvalidArgumentException; 15 | use Imagine\Image\Palette\Grayscale; 16 | 17 | final class Gray implements ColorInterface 18 | { 19 | /** 20 | * @var int 21 | */ 22 | private $gray; 23 | 24 | /** 25 | * @var int 26 | */ 27 | private $alpha; 28 | 29 | /** 30 | * @var \Imagine\Image\Palette\Grayscale 31 | */ 32 | private $palette; 33 | 34 | /** 35 | * @param \Imagine\Image\Palette\Grayscale $palette 36 | * @param int[] $color 37 | * @param int $alpha 38 | */ 39 | public function __construct(Grayscale $palette, array $color, $alpha) 40 | { 41 | $this->palette = $palette; 42 | $this->setColor($color); 43 | $this->setAlpha($alpha); 44 | } 45 | 46 | /** 47 | * {@inheritdoc} 48 | * 49 | * @see \Imagine\Image\Palette\Color\ColorInterface::getValue() 50 | */ 51 | public function getValue($component) 52 | { 53 | switch ($component) { 54 | case ColorInterface::COLOR_GRAY: 55 | return $this->getGray(); 56 | default: 57 | throw new InvalidArgumentException(sprintf('Color component %s is not valid', $component)); 58 | } 59 | } 60 | 61 | /** 62 | * Returns Gray value of the color (from 0 to 255). 63 | * 64 | * @return int 65 | */ 66 | public function getGray() 67 | { 68 | return $this->gray; 69 | } 70 | 71 | /** 72 | * {@inheritdoc} 73 | * 74 | * @see \Imagine\Image\Palette\Color\ColorInterface::getPalette() 75 | */ 76 | public function getPalette() 77 | { 78 | return $this->palette; 79 | } 80 | 81 | /** 82 | * {@inheritdoc} 83 | * 84 | * @see \Imagine\Image\Palette\Color\ColorInterface::getAlpha() 85 | */ 86 | public function getAlpha() 87 | { 88 | return $this->alpha; 89 | } 90 | 91 | /** 92 | * {@inheritdoc} 93 | * 94 | * @see \Imagine\Image\Palette\Color\ColorInterface::dissolve() 95 | */ 96 | public function dissolve($alpha) 97 | { 98 | return $this->palette->color( 99 | array($this->gray), 100 | min(max((int) round($this->alpha + $alpha), 0), 100) 101 | ); 102 | } 103 | 104 | /** 105 | * {@inheritdoc} 106 | * 107 | * @see \Imagine\Image\Palette\Color\ColorInterface::lighten() 108 | */ 109 | public function lighten($shade) 110 | { 111 | return $this->palette->color(array(min(255, $this->gray + $shade)), $this->alpha); 112 | } 113 | 114 | /** 115 | * {@inheritdoc} 116 | * 117 | * @see \Imagine\Image\Palette\Color\ColorInterface::darken() 118 | */ 119 | public function darken($shade) 120 | { 121 | return $this->palette->color(array(max(0, $this->gray - $shade)), $this->alpha); 122 | } 123 | 124 | /** 125 | * {@inheritdoc} 126 | * 127 | * @see \Imagine\Image\Palette\Color\ColorInterface::grayscale() 128 | */ 129 | public function grayscale() 130 | { 131 | return $this; 132 | } 133 | 134 | /** 135 | * {@inheritdoc} 136 | * 137 | * @see \Imagine\Image\Palette\Color\ColorInterface::isOpaque() 138 | */ 139 | public function isOpaque() 140 | { 141 | return $this->alpha === 100; 142 | } 143 | 144 | /** 145 | * {@inheritdoc} 146 | * 147 | * @see \Imagine\Image\Palette\Color\ColorInterface::__toString() 148 | */ 149 | public function __toString() 150 | { 151 | return sprintf('#%02x%02x%02x', $this->gray, $this->gray, $this->gray); 152 | } 153 | 154 | /** 155 | * Performs checks for validity of given alpha value and sets it. 156 | * 157 | * @param int $alpha 158 | * 159 | * @throws \Imagine\Exception\InvalidArgumentException 160 | */ 161 | private function setAlpha($alpha) 162 | { 163 | if (!is_int($alpha) || $alpha < 0 || $alpha > 100) { 164 | throw new InvalidArgumentException(sprintf('Alpha must be an integer between 0 and 100, %s given', $alpha)); 165 | } 166 | 167 | $this->alpha = $alpha; 168 | } 169 | 170 | /** 171 | * Performs checks for color validity (array of array(gray)). 172 | * 173 | * @param int[] $color 174 | * 175 | * @throws \Imagine\Exception\InvalidArgumentException 176 | */ 177 | private function setColor(array $color) 178 | { 179 | if (count($color) !== 1) { 180 | throw new InvalidArgumentException('Color argument must look like array(gray), where gray is the integer value between 0 and 255 for the grayscale'); 181 | } 182 | 183 | $color = array_values($color); 184 | $color[0] = max(0, min(255, $color[0])); 185 | 186 | list($this->gray) = $color; 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /src/Image/Palette/ColorParser.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | /* 4 | * This file is part of the Imagine package. 5 | * 6 | * (c) Bulat Shakirzyanov <mallluhuct@gmail.com> 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Imagine\Image\Palette; 13 | 14 | use Imagine\Exception\InvalidArgumentException; 15 | 16 | class ColorParser 17 | { 18 | /** 19 | * Parses a color to a RGB tuple. 20 | * 21 | * @param string|array|int $color 22 | * 23 | * @throws \Imagine\Exception\InvalidArgumentException 24 | * 25 | * @return array 26 | */ 27 | public function parseToRGB($color) 28 | { 29 | $color = $this->parse($color); 30 | 31 | if (count($color) === 4) { 32 | $color = array( 33 | 255 * (1 - $color[0] / 100) * (1 - $color[3] / 100), 34 | 255 * (1 - $color[1] / 100) * (1 - $color[3] / 100), 35 | 255 * (1 - $color[2] / 100) * (1 - $color[3] / 100), 36 | ); 37 | } 38 | 39 | return $color; 40 | } 41 | 42 | /** 43 | * Parses a color to a CMYK tuple. 44 | * 45 | * @param string|array|int $color 46 | * 47 | * @throws \Imagine\Exception\InvalidArgumentException 48 | * 49 | * @return array 50 | */ 51 | public function parseToCMYK($color) 52 | { 53 | $color = $this->parse($color); 54 | 55 | if (count($color) === 3) { 56 | $r = $color[0] / 255; 57 | $g = $color[1] / 255; 58 | $b = $color[2] / 255; 59 | 60 | $k = 1 - max($r, $g, $b); 61 | 62 | $color = array( 63 | $k === 1 ? 0 : round((1 - $r - $k) / (1 - $k) * 100), 64 | $k === 1 ? 0 : round((1 - $g - $k) / (1 - $k) * 100), 65 | $k === 1 ? 0 : round((1 - $b - $k) / (1 - $k) * 100), 66 | round($k * 100), 67 | ); 68 | } 69 | 70 | return $color; 71 | } 72 | 73 | /** 74 | * Parses a color to a grayscale value. 75 | * 76 | * @param string|array|int $color 77 | * 78 | * @throws \Imagine\Exception\InvalidArgumentException 79 | * 80 | * @return int[] 81 | */ 82 | public function parseToGrayscale($color) 83 | { 84 | if (is_array($color) && count($color) === 1) { 85 | return array((int) round(array_pop($color))); 86 | } 87 | 88 | $color = array_unique($this->parse($color)); 89 | 90 | if (count($color) !== 1) { 91 | throw new InvalidArgumentException('The provided color has different values of red, green and blue components. Grayscale colors must have the same values for these.'); 92 | } 93 | 94 | return $color; 95 | } 96 | 97 | /** 98 | * Parses a color. 99 | * 100 | * @param string|array|int $color 101 | * 102 | * @throws \Imagine\Exception\InvalidArgumentException 103 | * 104 | * @return int[] 105 | */ 106 | private function parse($color) 107 | { 108 | if (!is_string($color) && !is_array($color) && !is_int($color)) { 109 | throw new InvalidArgumentException(sprintf('Color must be specified as a hexadecimal string, array or integer, %s given', gettype($color))); 110 | } 111 | 112 | if (is_array($color)) { 113 | if (count($color) === 3 || count($color) === 4) { 114 | return array_map( 115 | function ($component) { 116 | return (int) round($component); 117 | }, 118 | array_values($color) 119 | ); 120 | } 121 | throw new InvalidArgumentException('Color argument if array, must look like array(R, G, B), or array(C, M, Y, K) where R, G, B are the integer values between 0 and 255 for red, green and blue or cyan, magenta, yellow and black color indexes accordingly'); 122 | } 123 | if (is_string($color)) { 124 | if (strpos($color, 'cmyk(') === 0) { 125 | $substrColor = substr($color, 5, strlen($color) - 6); 126 | 127 | $components = array_map(function ($component) { 128 | return (int) round(trim($component, ' %')); 129 | }, explode(',', $substrColor)); 130 | 131 | if (count($components) !== 4) { 132 | throw new InvalidArgumentException(sprintf('Unable to parse color %s', $color)); 133 | } 134 | 135 | return $components; 136 | } else { 137 | $color = ltrim($color, '#'); 138 | 139 | if (strlen($color) !== 3 && strlen($color) !== 6) { 140 | throw new InvalidArgumentException(sprintf('Color must be a hex value in regular (6 characters) or short (3 characters) notation, "%s" given', $color)); 141 | } 142 | 143 | if (strlen($color) === 3) { 144 | $color = $color[0] . $color[0] . $color[1] . $color[1] . $color[2] . $color[2]; 145 | } 146 | 147 | $color = array_map('hexdec', str_split($color, 2)); 148 | } 149 | } 150 | 151 | if (is_int($color)) { 152 | $color = array(255 & ($color >> 16), 255 & ($color >> 8), 255 & $color); 153 | } 154 | 155 | return $color; 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /src/Image/Palette/Grayscale.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | /* 4 | * This file is part of the Imagine package. 5 | * 6 | * (c) Bulat Shakirzyanov <mallluhuct@gmail.com> 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Imagine\Image\Palette; 13 | 14 | use Imagine\Exception\RuntimeException; 15 | use Imagine\Image\Palette\Color\ColorInterface; 16 | use Imagine\Image\Palette\Color\Gray as GrayColor; 17 | use Imagine\Image\Profile; 18 | use Imagine\Image\ProfileInterface; 19 | 20 | /** 21 | * The grayscale palette. 22 | */ 23 | class Grayscale implements PaletteInterface 24 | { 25 | /** 26 | * @var \Imagine\Image\Palette\ColorParser 27 | */ 28 | private $parser; 29 | 30 | /** 31 | * @var \Imagine\Image\ProfileInterface|null 32 | */ 33 | private $profile; 34 | 35 | /** 36 | * @var \Imagine\Image\Palette\Color\Gray[] 37 | */ 38 | protected static $colors = array(); 39 | 40 | public function __construct() 41 | { 42 | $this->parser = new ColorParser(); 43 | } 44 | 45 | /** 46 | * {@inheritdoc} 47 | * 48 | * @see \Imagine\Image\Palette\PaletteInterface::name() 49 | */ 50 | public function name() 51 | { 52 | return PaletteInterface::PALETTE_GRAYSCALE; 53 | } 54 | 55 | /** 56 | * {@inheritdoc} 57 | * 58 | * @see \Imagine\Image\Palette\PaletteInterface::pixelDefinition() 59 | */ 60 | public function pixelDefinition() 61 | { 62 | return array(ColorInterface::COLOR_GRAY); 63 | } 64 | 65 | /** 66 | * {@inheritdoc} 67 | * 68 | * @see \Imagine\Image\Palette\PaletteInterface::supportsAlpha() 69 | */ 70 | public function supportsAlpha() 71 | { 72 | return true; 73 | } 74 | 75 | /** 76 | * {@inheritdoc} 77 | * 78 | * @see \Imagine\Image\Palette\PaletteInterface::getChannelsMaxValue() 79 | */ 80 | public function getChannelsMaxValue() 81 | { 82 | return 255; 83 | } 84 | 85 | /** 86 | * {@inheritdoc} 87 | * 88 | * @see \Imagine\Image\Palette\PaletteInterface::useProfile() 89 | */ 90 | public function useProfile(ProfileInterface $profile) 91 | { 92 | $this->profile = $profile; 93 | 94 | return $this; 95 | } 96 | 97 | /** 98 | * {@inheritdoc} 99 | * 100 | * @see \Imagine\Image\Palette\PaletteInterface::profile() 101 | */ 102 | public function profile() 103 | { 104 | if (!$this->profile) { 105 | $this->profile = Profile::fromPath(__DIR__ . '/../../resources/colormanagement.org/ISOcoated_v2_grey1c_bas.ICC'); 106 | } 107 | 108 | return $this->profile; 109 | } 110 | 111 | /** 112 | * {@inheritdoc} 113 | * 114 | * @see \Imagine\Image\Palette\PaletteInterface::color() 115 | */ 116 | public function color($color, $alpha = null) 117 | { 118 | if ($alpha === null) { 119 | $alpha = 0; 120 | } 121 | 122 | $color = $this->parser->parseToGrayscale($color); 123 | $index = sprintf('#%02x%02x%02x-%d', $color[0], $color[0], $color[0], $alpha); 124 | 125 | if (array_key_exists($index, static::$colors) === false) { 126 | static::$colors[$index] = new GrayColor($this, $color, $alpha); 127 | } 128 | 129 | return static::$colors[$index]; 130 | } 131 | 132 | /** 133 | * {@inheritdoc} 134 | * 135 | * @see \Imagine\Image\Palette\PaletteInterface::blend() 136 | */ 137 | public function blend(ColorInterface $color1, ColorInterface $color2, $amount) 138 | { 139 | if (!$color1 instanceof GrayColor || !$color2 instanceof GrayColor) { 140 | throw new RuntimeException('Grayscale palette can only blend Grayscale colors'); 141 | } 142 | $max = $this->getChannelsMaxValue(); 143 | 144 | return $this->color( 145 | array( 146 | (int) min($max, min($color1->getGray(), $color2->getGray()) + round(abs($color2->getGray() - $color1->getGray()) * $amount)), 147 | ), 148 | (int) min(100, min($color1->getAlpha(), $color2->getAlpha()) + round(abs($color2->getAlpha() - $color1->getAlpha()) * $amount)) 149 | ); 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /src/Image/Palette/PaletteInterface.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | /* 4 | * This file is part of the Imagine package. 5 | * 6 | * (c) Bulat Shakirzyanov <mallluhuct@gmail.com> 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Imagine\Image\Palette; 13 | 14 | use Imagine\Image\Palette\Color\ColorInterface; 15 | use Imagine\Image\ProfileInterface; 16 | 17 | /** 18 | * Interface that any palette must implement. 19 | */ 20 | interface PaletteInterface 21 | { 22 | /** 23 | * Palette name: grayscale. 24 | * 25 | * @var string 26 | */ 27 | const PALETTE_GRAYSCALE = 'gray'; 28 | 29 | /** 30 | * Palette name: RGB. 31 | * 32 | * @var string 33 | */ 34 | const PALETTE_RGB = 'rgb'; 35 | 36 | /** 37 | * Palette name: CMYK. 38 | * 39 | * @var string 40 | */ 41 | const PALETTE_CMYK = 'cmyk'; 42 | 43 | /** 44 | * Returns a color given some values. 45 | * 46 | * @param string|int[]|int $color A color 47 | * @param int|null $alpha Set alpha to null to disable it 48 | * 49 | * @throws \Imagine\Exception\InvalidArgumentException In case you pass an alpha value to a Palette that does not support alpha 50 | * 51 | * @return \Imagine\Image\Palette\Color\ColorInterface 52 | */ 53 | public function color($color, $alpha = null); 54 | 55 | /** 56 | * Blend two colors given an amount. 57 | * 58 | * @param \Imagine\Image\Palette\Color\ColorInterface $color1 59 | * @param \Imagine\Image\Palette\Color\ColorInterface $color2 60 | * @param float $amount The amount of color2 in color1 61 | * 62 | * @return \Imagine\Image\Palette\Color\ColorInterface 63 | */ 64 | public function blend(ColorInterface $color1, ColorInterface $color2, $amount); 65 | 66 | /** 67 | * Attachs an ICC profile to this Palette. 68 | * 69 | * (A default profile is provided by default) 70 | * 71 | * @param \Imagine\Image\ProfileInterface $profile 72 | * 73 | * @return $this 74 | */ 75 | public function useProfile(ProfileInterface $profile); 76 | 77 | /** 78 | * Returns the ICC profile attached to this Palette. 79 | * 80 | * @return \Imagine\Image\ProfileInterface 81 | */ 82 | public function profile(); 83 | 84 | /** 85 | * Returns the name of this Palette, one of PaletteInterface::PALETTE_ constants. 86 | * 87 | * @return string 88 | */ 89 | public function name(); 90 | 91 | /** 92 | * Returns an array containing ColorInterface::COLOR_* constants that 93 | * define the structure of colors for a pixel. 94 | * 95 | * @return string[] 96 | */ 97 | public function pixelDefinition(); 98 | 99 | /** 100 | * Tells if alpha channel is supported in this palette. 101 | * 102 | * @return bool 103 | */ 104 | public function supportsAlpha(); 105 | 106 | /** 107 | * Get the max value of palette components (255 for RGB and Grayscale, 100 for CMYK). 108 | * 109 | * @return int 110 | */ 111 | public function getChannelsMaxValue(); 112 | } 113 | -------------------------------------------------------------------------------- /src/Image/Palette/RGB.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | /* 4 | * This file is part of the Imagine package. 5 | * 6 | * (c) Bulat Shakirzyanov <mallluhuct@gmail.com> 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Imagine\Image\Palette; 13 | 14 | use Imagine\Exception\RuntimeException; 15 | use Imagine\Image\Palette\Color\ColorInterface; 16 | use Imagine\Image\Palette\Color\RGB as RGBColor; 17 | use Imagine\Image\Profile; 18 | use Imagine\Image\ProfileInterface; 19 | 20 | /** 21 | * The RGB palette. 22 | */ 23 | class RGB implements PaletteInterface 24 | { 25 | /** 26 | * @var \Imagine\Image\Palette\ColorParser 27 | */ 28 | private $parser; 29 | 30 | /** 31 | * @var \Imagine\Image\ProfileInterface|null 32 | */ 33 | private $profile; 34 | 35 | /** 36 | * @var \Imagine\Image\Palette\Color\RGB[] 37 | */ 38 | protected static $colors = array(); 39 | 40 | public function __construct() 41 | { 42 | $this->parser = new ColorParser(); 43 | } 44 | 45 | /** 46 | * {@inheritdoc} 47 | * 48 | * @see \Imagine\Image\Palette\PaletteInterface::name() 49 | */ 50 | public function name() 51 | { 52 | return PaletteInterface::PALETTE_RGB; 53 | } 54 | 55 | /** 56 | * {@inheritdoc} 57 | * 58 | * @see \Imagine\Image\Palette\PaletteInterface::pixelDefinition() 59 | */ 60 | public function pixelDefinition() 61 | { 62 | return array( 63 | ColorInterface::COLOR_RED, 64 | ColorInterface::COLOR_GREEN, 65 | ColorInterface::COLOR_BLUE, 66 | ); 67 | } 68 | 69 | /** 70 | * {@inheritdoc} 71 | * 72 | * @see \Imagine\Image\Palette\PaletteInterface::supportsAlpha() 73 | */ 74 | public function supportsAlpha() 75 | { 76 | return true; 77 | } 78 | 79 | /** 80 | * {@inheritdoc} 81 | * 82 | * @see \Imagine\Image\Palette\PaletteInterface::getChannelsMaxValue() 83 | */ 84 | public function getChannelsMaxValue() 85 | { 86 | return 255; 87 | } 88 | 89 | /** 90 | * {@inheritdoc} 91 | * 92 | * @see \Imagine\Image\Palette\PaletteInterface::useProfile() 93 | */ 94 | public function useProfile(ProfileInterface $profile) 95 | { 96 | $this->profile = $profile; 97 | 98 | return $this; 99 | } 100 | 101 | /** 102 | * {@inheritdoc} 103 | * 104 | * @see \Imagine\Image\Palette\PaletteInterface::profile() 105 | */ 106 | public function profile() 107 | { 108 | if (!$this->profile) { 109 | $this->profile = Profile::fromPath(__DIR__ . '/../../resources/color.org/sRGB_IEC61966-2-1_black_scaled.icc'); 110 | } 111 | 112 | return $this->profile; 113 | } 114 | 115 | /** 116 | * {@inheritdoc} 117 | * 118 | * @see \Imagine\Image\Palette\PaletteInterface::color() 119 | */ 120 | public function color($color, $alpha = null) 121 | { 122 | if ($alpha === null) { 123 | $alpha = 100; 124 | } 125 | 126 | $color = $this->parser->parseToRGB($color); 127 | $index = sprintf('#%02x%02x%02x-%d', $color[0], $color[1], $color[2], $alpha); 128 | 129 | if (array_key_exists($index, static::$colors) === false) { 130 | static::$colors[$index] = new RGBColor($this, $color, $alpha); 131 | } 132 | 133 | return static::$colors[$index]; 134 | } 135 | 136 | /** 137 | * {@inheritdoc} 138 | * 139 | * @see \Imagine\Image\Palette\PaletteInterface::blend() 140 | */ 141 | public function blend(ColorInterface $color1, ColorInterface $color2, $amount) 142 | { 143 | if (!$color1 instanceof RGBColor || !$color2 instanceof RGBColor) { 144 | throw new RuntimeException('RGB palette can only blend RGB colors'); 145 | } 146 | $amount = (float) $amount; 147 | $amountComplement = 1 - $amount; 148 | $max = $this->getChannelsMaxValue(); 149 | 150 | return $this->color( 151 | array( 152 | min(max((int) round($color2->getRed() * $amount + $color1->getRed() * $amountComplement), 0), $max), 153 | min(max((int) round($color2->getGreen() * $amount + $color1->getGreen() * $amountComplement), 0), $max), 154 | min(max((int) round($color2->getBlue() * $amount + $color1->getBlue() * $amountComplement), 0), $max), 155 | ), 156 | min(max((int) round($color2->getAlpha() * $amount + $color1->getAlpha() * $amountComplement), 0), 100) 157 | ); 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /src/Image/Point.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | /* 4 | * This file is part of the Imagine package. 5 | * 6 | * (c) Bulat Shakirzyanov <mallluhuct@gmail.com> 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Imagine\Image; 13 | 14 | use Imagine\Exception\InvalidArgumentException; 15 | 16 | /** 17 | * The point class. 18 | */ 19 | final class Point implements PointInterface 20 | { 21 | /** 22 | * @var int 23 | */ 24 | private $x; 25 | 26 | /** 27 | * @var int 28 | */ 29 | private $y; 30 | 31 | /** 32 | * Constructs a point of coordinates. 33 | * 34 | * @param int $x 35 | * @param int $y 36 | * 37 | * @throws \Imagine\Exception\InvalidArgumentException 38 | */ 39 | public function __construct($x, $y) 40 | { 41 | if (!is_numeric($x) || !is_numeric($y)) { 42 | throw new InvalidArgumentException('x or y must be numeric'); 43 | } 44 | 45 | $x = (int) round((float) $x); 46 | $y = (int) round((float) $y); 47 | 48 | if ($x < 0 || $y < 0) { 49 | throw new InvalidArgumentException(sprintf('A coordinate cannot be positioned outside of a bounding box (x: %s, y: %s given)', $x, $y)); 50 | } 51 | 52 | $this->x = $x; 53 | $this->y = $y; 54 | } 55 | 56 | /** 57 | * {@inheritdoc} 58 | * 59 | * @see \Imagine\Image\PointInterface::getX() 60 | */ 61 | public function getX() 62 | { 63 | return $this->x; 64 | } 65 | 66 | /** 67 | * {@inheritdoc} 68 | * 69 | * @see \Imagine\Image\PointInterface::getY() 70 | */ 71 | public function getY() 72 | { 73 | return $this->y; 74 | } 75 | 76 | /** 77 | * {@inheritdoc} 78 | * 79 | * @see \Imagine\Image\PointInterface::in() 80 | */ 81 | public function in(BoxInterface $box) 82 | { 83 | return $this->x < $box->getWidth() && $this->y < $box->getHeight(); 84 | } 85 | 86 | /** 87 | * {@inheritdoc} 88 | * 89 | * @see \Imagine\Image\PointInterface::move() 90 | */ 91 | public function move($amount) 92 | { 93 | return new self($this->x + $amount, $this->y + $amount); 94 | } 95 | 96 | /** 97 | * {@inheritdoc} 98 | * 99 | * @see \Imagine\Image\PointInterface::__toString() 100 | */ 101 | public function __toString() 102 | { 103 | return sprintf('(%d, %d)', $this->x, $this->y); 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /src/Image/Point/Center.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | /* 4 | * This file is part of the Imagine package. 5 | * 6 | * (c) Bulat Shakirzyanov <mallluhuct@gmail.com> 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Imagine\Image\Point; 13 | 14 | use Imagine\Image\BoxInterface; 15 | use Imagine\Image\Point as OriginalPoint; 16 | use Imagine\Image\PointInterface; 17 | 18 | /** 19 | * Center point of a box. 20 | */ 21 | final class Center implements PointInterface 22 | { 23 | /** 24 | * @var \Imagine\Image\BoxInterface 25 | */ 26 | private $box; 27 | 28 | /** 29 | * Constructs coordinate with size instance, it needs to be relative to. 30 | * 31 | * @param \Imagine\Image\BoxInterface $box 32 | */ 33 | public function __construct(BoxInterface $box) 34 | { 35 | $this->box = $box; 36 | } 37 | 38 | /** 39 | * {@inheritdoc} 40 | * 41 | * @see \Imagine\Image\PointInterface::getX() 42 | */ 43 | public function getX() 44 | { 45 | return ceil($this->box->getWidth() / 2); 46 | } 47 | 48 | /** 49 | * {@inheritdoc} 50 | * 51 | * @see \Imagine\Image\PointInterface::getY() 52 | */ 53 | public function getY() 54 | { 55 | return ceil($this->box->getHeight() / 2); 56 | } 57 | 58 | /** 59 | * {@inheritdoc} 60 | * 61 | * @see \Imagine\Image\PointInterface::in() 62 | */ 63 | public function in(BoxInterface $box) 64 | { 65 | return $this->getX() < $box->getWidth() && $this->getY() < $box->getHeight(); 66 | } 67 | 68 | /** 69 | * {@inheritdoc} 70 | * 71 | * @see \Imagine\Image\PointInterface::move() 72 | */ 73 | public function move($amount) 74 | { 75 | return new OriginalPoint($this->getX() + $amount, $this->getY() + $amount); 76 | } 77 | 78 | /** 79 | * {@inheritdoc} 80 | * 81 | * @see \Imagine\Image\PointInterface::__toString() 82 | */ 83 | public function __toString() 84 | { 85 | return sprintf('(%d, %d)', $this->getX(), $this->getY()); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/Image/PointInterface.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | /* 4 | * This file is part of the Imagine package. 5 | * 6 | * (c) Bulat Shakirzyanov <mallluhuct@gmail.com> 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Imagine\Image; 13 | 14 | /** 15 | * The point interface. 16 | */ 17 | interface PointInterface 18 | { 19 | /** 20 | * Gets points x coordinate. 21 | * 22 | * @return int 23 | */ 24 | public function getX(); 25 | 26 | /** 27 | * Gets points y coordinate. 28 | * 29 | * @return int 30 | */ 31 | public function getY(); 32 | 33 | /** 34 | * Checks if current coordinate is inside a given box. 35 | * 36 | * @param \Imagine\Image\BoxInterface $box 37 | * 38 | * @return bool 39 | */ 40 | public function in(BoxInterface $box); 41 | 42 | /** 43 | * Returns another point, moved by a given amount from current coordinates. 44 | * 45 | * @param int $amount 46 | * 47 | * @return \Imagine\Image\PointInterface 48 | */ 49 | public function move($amount); 50 | 51 | /** 52 | * Gets a string representation for the current point. 53 | * 54 | * @return string 55 | */ 56 | public function __toString(); 57 | } 58 | -------------------------------------------------------------------------------- /src/Image/PointSigned.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | /* 4 | * This file is part of the Imagine package. 5 | * 6 | * (c) Bulat Shakirzyanov <mallluhuct@gmail.com> 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Imagine\Image; 13 | 14 | /** 15 | * A point class that allows negative values of coordinates. 16 | */ 17 | final class PointSigned implements PointInterface 18 | { 19 | /** 20 | * @var int 21 | */ 22 | private $x; 23 | 24 | /** 25 | * @var int 26 | */ 27 | private $y; 28 | 29 | /** 30 | * Constructs a point of coordinates. 31 | * 32 | * @param int $x 33 | * @param int $y 34 | * 35 | * @throws \Imagine\Exception\InvalidArgumentException 36 | */ 37 | public function __construct($x, $y) 38 | { 39 | $this->x = $x; 40 | $this->y = $y; 41 | } 42 | 43 | /** 44 | * {@inheritdoc} 45 | * 46 | * @see \Imagine\Image\PointInterface::getX() 47 | */ 48 | public function getX() 49 | { 50 | return $this->x; 51 | } 52 | 53 | /** 54 | * {@inheritdoc} 55 | * 56 | * @see \Imagine\Image\PointInterface::getY() 57 | */ 58 | public function getY() 59 | { 60 | return $this->y; 61 | } 62 | 63 | /** 64 | * {@inheritdoc} 65 | * 66 | * @see \Imagine\Image\PointInterface::in() 67 | */ 68 | public function in(BoxInterface $box) 69 | { 70 | return $this->x >= 0 && $this->x < $box->getWidth() && $this->y >= 0 && $this->y < $box->getHeight(); 71 | } 72 | 73 | /** 74 | * {@inheritdoc} 75 | * 76 | * @see \Imagine\Image\PointInterface::move() 77 | */ 78 | public function move($amount) 79 | { 80 | return new self($this->x + $amount, $this->y + $amount); 81 | } 82 | 83 | /** 84 | * {@inheritdoc} 85 | * 86 | * @see \Imagine\Image\PointInterface::__toString() 87 | */ 88 | public function __toString() 89 | { 90 | return sprintf('(%d, %d)', $this->x, $this->y); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/Image/Profile.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | /* 4 | * This file is part of the Imagine package. 5 | * 6 | * (c) Bulat Shakirzyanov <mallluhuct@gmail.com> 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Imagine\Image; 13 | 14 | use Imagine\Exception\InvalidArgumentException; 15 | 16 | /** 17 | * The default implementation of ProfileInterface. 18 | */ 19 | class Profile implements ProfileInterface 20 | { 21 | /** 22 | * @var string 23 | */ 24 | private $data; 25 | 26 | /** 27 | * @var string 28 | */ 29 | private $name; 30 | 31 | /** 32 | * @param string $name 33 | * @param string $data 34 | */ 35 | public function __construct($name, $data) 36 | { 37 | $this->name = $name; 38 | $this->data = $data; 39 | } 40 | 41 | /** 42 | * {@inheritdoc} 43 | * 44 | * @see \Imagine\Image\ProfileInterface::name() 45 | */ 46 | public function name() 47 | { 48 | return $this->name; 49 | } 50 | 51 | /** 52 | * {@inheritdoc} 53 | * 54 | * @see \Imagine\Image\ProfileInterface::data() 55 | */ 56 | public function data() 57 | { 58 | return $this->data; 59 | } 60 | 61 | /** 62 | * Creates a profile from a path to a file. 63 | * 64 | * @param string $path 65 | * 66 | * @throws \Imagine\Exception\InvalidArgumentException In case the provided path is not valid 67 | * 68 | * @return static 69 | */ 70 | public static function fromPath($path) 71 | { 72 | if (!file_exists($path) || !is_file($path) || !is_readable($path)) { 73 | throw new InvalidArgumentException(sprintf('Path %s is an invalid profile file or is not readable', $path)); 74 | } 75 | 76 | return new static(basename($path), file_get_contents($path)); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/Image/ProfileInterface.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | /* 4 | * This file is part of the Imagine package. 5 | * 6 | * (c) Bulat Shakirzyanov <mallluhuct@gmail.com> 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Imagine\Image; 13 | 14 | interface ProfileInterface 15 | { 16 | /** 17 | * Returns the name of the profile. 18 | * 19 | * @return string 20 | */ 21 | public function name(); 22 | 23 | /** 24 | * Returns the profile data. 25 | * 26 | * @return string 27 | */ 28 | public function data(); 29 | } 30 | -------------------------------------------------------------------------------- /src/Imagick/Font.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | /* 4 | * This file is part of the Imagine package. 5 | * 6 | * (c) Bulat Shakirzyanov <mallluhuct@gmail.com> 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Imagine\Imagick; 13 | 14 | use Imagine\Driver\InfoProvider; 15 | use Imagine\Image\AbstractFont; 16 | use Imagine\Image\Palette\Color\ColorInterface; 17 | 18 | /** 19 | * Font implementation using the Imagick PHP extension. 20 | */ 21 | final class Font extends AbstractFont implements InfoProvider 22 | { 23 | /** 24 | * @var \Imagick 25 | */ 26 | private $imagick; 27 | 28 | /** 29 | * @param \Imagick $imagick 30 | * @param string $file 31 | * @param int $size 32 | * @param \Imagine\Image\Palette\Color\ColorInterface $color 33 | */ 34 | public function __construct(\Imagick $imagick, $file, $size, ColorInterface $color) 35 | { 36 | $this->imagick = $imagick; 37 | 38 | parent::__construct($file, $size, $color); 39 | } 40 | 41 | /** 42 | * {@inheritdoc} 43 | * 44 | * @see \Imagine\Driver\InfoProvider::getDriverInfo() 45 | * @since 1.3.0 46 | */ 47 | public static function getDriverInfo($required = true) 48 | { 49 | return DriverInfo::get($required); 50 | } 51 | 52 | /** 53 | * {@inheritdoc} 54 | * 55 | * @see \Imagine\Image\FontInterface::box() 56 | */ 57 | public function box($string, $angle = 0) 58 | { 59 | $text = new \ImagickDraw(); 60 | 61 | $text->setFont($this->file); 62 | 63 | // ensure font resolution is the same as GD's hard-coded 96 64 | if (static::getDriverInfo()->hasFeature(DriverInfo::FEATURE_CUSTOMRESOLUTION)) { 65 | $text->setResolution(96, 96); 66 | $text->setFontSize($this->size); 67 | } else { 68 | $text->setFontSize((int) ($this->size * (96 / 72))); 69 | } 70 | 71 | $info = $this->imagick->queryFontMetrics($text, $string); 72 | 73 | $box = $this->getClassFactory()->createBox($info['textWidth'], $info['textHeight']); 74 | 75 | return $box; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/Utils/ErrorHandling.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | /* 4 | * This file is part of the Imagine package. 5 | * 6 | * (c) Bulat Shakirzyanov <mallluhuct@gmail.com> 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Imagine\Utils; 13 | 14 | use ErrorException; 15 | use Exception; 16 | use Imagine\Exception\RuntimeException; 17 | use Throwable; 18 | 19 | class ErrorHandling 20 | { 21 | /** 22 | * Call a callback ignoring $flags warnings. 23 | * 24 | * @param int $flags The flags to be ignored (eg E_WARNING | E_NOTICE) 25 | * @param callable $callback The callable to be called 26 | * 27 | * @throws \Exception Throws an Exception if $callback throws an Exception 28 | * @throws \Throwable Throws an Throwable if $callback throws an Throwable 29 | * 30 | * @return mixed Returns the result of $callback 31 | */ 32 | public static function ignoring($flags, $callback) 33 | { 34 | set_error_handler( 35 | function () { 36 | }, 37 | $flags 38 | ); 39 | try { 40 | $result = $callback(); 41 | $exception = null; 42 | } catch (Exception $x) { 43 | $exception = $x; 44 | } catch (Throwable $x) { 45 | $exception = $x; 46 | } 47 | restore_error_handler(); 48 | if ($exception !== null) { 49 | throw $exception; 50 | } 51 | 52 | return $result; 53 | } 54 | 55 | /** 56 | * Call a callback and throws a RuntimeException if a $flags warning is thrown. 57 | * 58 | * @param int $flags The flags to be intercepted (eg E_WARNING | E_NOTICE) 59 | * @param callable $callback The callable to be called 60 | * 61 | * @throws RuntimeException 62 | * @throws \Imagine\Exception\RuntimeException 63 | * @throws \Exception 64 | * @throws \Throwable 65 | * 66 | * @return mixed Returns the result of $callback 67 | */ 68 | public static function throwingRuntimeException($flags, $callback) 69 | { 70 | set_error_handler( 71 | function ($errno, $errstr, $errfile, $errline) { 72 | if (error_reporting() !== 0) { 73 | throw new RuntimeException($errstr, $errno, new ErrorException($errstr, 0, $errno, $errfile, $errline)); 74 | } 75 | }, 76 | $flags 77 | ); 78 | try { 79 | $result = $callback(); 80 | $exception = null; 81 | } catch (Exception $x) { 82 | $exception = $x; 83 | } catch (Throwable $x) { 84 | $exception = $x; 85 | } 86 | restore_error_handler(); 87 | if ($exception !== null) { 88 | throw $exception; 89 | } 90 | 91 | return $result; 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/Utils/Matrix.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | /* 4 | * This file is part of the Imagine package. 5 | * 6 | * (c) Bulat Shakirzyanov <mallluhuct@gmail.com> 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Imagine\Utils; 13 | 14 | use Imagine\Exception\InvalidArgumentException; 15 | use Imagine\Exception\OutOfBoundsException; 16 | 17 | class Matrix 18 | { 19 | /** 20 | * The array of elements. 21 | * 22 | * @var int[]|float[] 23 | */ 24 | protected $elements = array(); 25 | 26 | /** 27 | * The matrix width. 28 | * 29 | * @var int 30 | */ 31 | protected $width; 32 | 33 | /** 34 | * The matrix height. 35 | * 36 | * @var int 37 | */ 38 | protected $height; 39 | 40 | /** 41 | * The given $elements get arranged as follows: The elements will be set from left to right in a row until the 42 | * row is full. Then, the next line begins alike and so on. 43 | * 44 | * @param int $width the matrix width 45 | * @param int $height he matrix height 46 | * @param int[]|float[] $elements the matrix elements 47 | * 48 | * @throws \Imagine\Exception\InvalidArgumentException 49 | */ 50 | public function __construct($width, $height, $elements = array()) 51 | { 52 | $this->width = (int) round($width); 53 | if ($this->width < 1) { 54 | throw new InvalidArgumentException('width has to be > 0'); 55 | } 56 | $this->height = (int) round($height); 57 | if ($this->height < 1) { 58 | throw new InvalidArgumentException('height has to be > 0'); 59 | } 60 | $expectedElements = $width * $height; 61 | $providedElements = count($elements); 62 | if ($providedElements > $expectedElements) { 63 | throw new InvalidArgumentException('there are more provided elements than space in the matrix'); 64 | } 65 | $this->elements = array_values($elements); 66 | if ($providedElements < $expectedElements) { 67 | $this->elements = array_merge( 68 | $this->elements, 69 | array_fill($providedElements, $expectedElements - $providedElements, 0) 70 | ); 71 | } 72 | } 73 | 74 | /** 75 | * Get the matrix width. 76 | * 77 | * @return int 78 | */ 79 | public function getWidth() 80 | { 81 | return $this->width; 82 | } 83 | 84 | /** 85 | * Get the matrix height. 86 | * 87 | * @return int 88 | */ 89 | public function getHeight() 90 | { 91 | return $this->height; 92 | } 93 | 94 | /** 95 | * Set the value of a cell. 96 | * 97 | * @param int $x 98 | * @param int $y 99 | * @param int|float $value 100 | */ 101 | public function setElementAt($x, $y, $value) 102 | { 103 | $this->elements[$this->calculatePosition($x, $y)] = $value; 104 | } 105 | 106 | /** 107 | * Get the value of a cell. 108 | * 109 | * @param int $x 110 | * @param int $y 111 | * 112 | * @return int|float 113 | */ 114 | public function getElementAt($x, $y) 115 | { 116 | return $this->elements[$this->calculatePosition($x, $y)]; 117 | } 118 | 119 | /** 120 | * Return all the matrix values, as a monodimensional array. 121 | * 122 | * @return int[]|float[] 123 | */ 124 | public function getValueList() 125 | { 126 | return $this->elements; 127 | } 128 | 129 | /** 130 | * Return all the matrix values, as a bidimensional array (every array item contains the values of a row). 131 | * 132 | * @return int[]|float[] 133 | */ 134 | public function getMatrix() 135 | { 136 | return array_chunk($this->elements, $this->getWidth()); 137 | } 138 | 139 | /** 140 | * Returns a new Matrix instance, representing the normalized value of this matrix. 141 | * 142 | * @return static 143 | */ 144 | public function normalize() 145 | { 146 | $values = $this->getValueList(); 147 | $divisor = array_sum($values); 148 | if ($divisor == 0 || $divisor == 1) { 149 | return clone $this; 150 | } 151 | $normalizedElements = array(); 152 | foreach ($values as $value) { 153 | $normalizedElements[] = $value / $divisor; 154 | } 155 | 156 | return new static($this->getWidth(), $this->getHeight(), $normalizedElements); 157 | } 158 | 159 | /** 160 | * Calculate the offset position of a cell. 161 | * 162 | * @param int $x 163 | * @param int $y 164 | * 165 | * @throws \Imagine\Exception\OutOfBoundsException 166 | * 167 | * @return int 168 | */ 169 | protected function calculatePosition($x, $y) 170 | { 171 | if ($x < 0 || $y < 0 || $this->width <= $x || $this->height <= $y) { 172 | throw new OutOfBoundsException(sprintf('There is no position (%s, %s) in this matrix', $x, $y)); 173 | } 174 | 175 | return $y * $this->height + $x; 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /src/resources/Adobe/CMYK/CoatedFOGRA27.icc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/php-imagine/Imagine/43856ce80debe1c7b0d7a5fc8497ac64bb6870c3/src/resources/Adobe/CMYK/CoatedFOGRA27.icc -------------------------------------------------------------------------------- /src/resources/Adobe/CMYK/CoatedFOGRA39.icc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/php-imagine/Imagine/43856ce80debe1c7b0d7a5fc8497ac64bb6870c3/src/resources/Adobe/CMYK/CoatedFOGRA39.icc -------------------------------------------------------------------------------- /src/resources/Adobe/CMYK/CoatedGRACoL2006.icc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/php-imagine/Imagine/43856ce80debe1c7b0d7a5fc8497ac64bb6870c3/src/resources/Adobe/CMYK/CoatedGRACoL2006.icc -------------------------------------------------------------------------------- /src/resources/Adobe/CMYK/JapanColor2001Coated.icc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/php-imagine/Imagine/43856ce80debe1c7b0d7a5fc8497ac64bb6870c3/src/resources/Adobe/CMYK/JapanColor2001Coated.icc -------------------------------------------------------------------------------- /src/resources/Adobe/CMYK/JapanColor2001Uncoated.icc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/php-imagine/Imagine/43856ce80debe1c7b0d7a5fc8497ac64bb6870c3/src/resources/Adobe/CMYK/JapanColor2001Uncoated.icc -------------------------------------------------------------------------------- /src/resources/Adobe/CMYK/JapanColor2002Newspaper.icc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/php-imagine/Imagine/43856ce80debe1c7b0d7a5fc8497ac64bb6870c3/src/resources/Adobe/CMYK/JapanColor2002Newspaper.icc -------------------------------------------------------------------------------- /src/resources/Adobe/CMYK/JapanColor2003WebCoated.icc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/php-imagine/Imagine/43856ce80debe1c7b0d7a5fc8497ac64bb6870c3/src/resources/Adobe/CMYK/JapanColor2003WebCoated.icc -------------------------------------------------------------------------------- /src/resources/Adobe/CMYK/JapanWebCoated.icc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/php-imagine/Imagine/43856ce80debe1c7b0d7a5fc8497ac64bb6870c3/src/resources/Adobe/CMYK/JapanWebCoated.icc -------------------------------------------------------------------------------- /src/resources/Adobe/CMYK/USWebCoatedSWOP.icc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/php-imagine/Imagine/43856ce80debe1c7b0d7a5fc8497ac64bb6870c3/src/resources/Adobe/CMYK/USWebCoatedSWOP.icc -------------------------------------------------------------------------------- /src/resources/Adobe/CMYK/USWebUncoated.icc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/php-imagine/Imagine/43856ce80debe1c7b0d7a5fc8497ac64bb6870c3/src/resources/Adobe/CMYK/USWebUncoated.icc -------------------------------------------------------------------------------- /src/resources/Adobe/CMYK/UncoatedFOGRA29.icc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/php-imagine/Imagine/43856ce80debe1c7b0d7a5fc8497ac64bb6870c3/src/resources/Adobe/CMYK/UncoatedFOGRA29.icc -------------------------------------------------------------------------------- /src/resources/Adobe/CMYK/WebCoatedFOGRA28.icc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/php-imagine/Imagine/43856ce80debe1c7b0d7a5fc8497ac64bb6870c3/src/resources/Adobe/CMYK/WebCoatedFOGRA28.icc -------------------------------------------------------------------------------- /src/resources/Adobe/CMYK/WebCoatedSWOP2006Grade3.icc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/php-imagine/Imagine/43856ce80debe1c7b0d7a5fc8497ac64bb6870c3/src/resources/Adobe/CMYK/WebCoatedSWOP2006Grade3.icc -------------------------------------------------------------------------------- /src/resources/Adobe/CMYK/WebCoatedSWOP2006Grade5.icc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/php-imagine/Imagine/43856ce80debe1c7b0d7a5fc8497ac64bb6870c3/src/resources/Adobe/CMYK/WebCoatedSWOP2006Grade5.icc -------------------------------------------------------------------------------- /src/resources/Adobe/RGB/AdobeRGB1998.icc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/php-imagine/Imagine/43856ce80debe1c7b0d7a5fc8497ac64bb6870c3/src/resources/Adobe/RGB/AdobeRGB1998.icc -------------------------------------------------------------------------------- /src/resources/Adobe/RGB/AppleRGB.icc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/php-imagine/Imagine/43856ce80debe1c7b0d7a5fc8497ac64bb6870c3/src/resources/Adobe/RGB/AppleRGB.icc -------------------------------------------------------------------------------- /src/resources/Adobe/RGB/ColorMatchRGB.icc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/php-imagine/Imagine/43856ce80debe1c7b0d7a5fc8497ac64bb6870c3/src/resources/Adobe/RGB/ColorMatchRGB.icc -------------------------------------------------------------------------------- /src/resources/Adobe/RGB/PAL_SECAM.icc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/php-imagine/Imagine/43856ce80debe1c7b0d7a5fc8497ac64bb6870c3/src/resources/Adobe/RGB/PAL_SECAM.icc -------------------------------------------------------------------------------- /src/resources/Adobe/RGB/SMPTE-C.icc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/php-imagine/Imagine/43856ce80debe1c7b0d7a5fc8497ac64bb6870c3/src/resources/Adobe/RGB/SMPTE-C.icc -------------------------------------------------------------------------------- /src/resources/Adobe/RGB/VideoHD.icc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/php-imagine/Imagine/43856ce80debe1c7b0d7a5fc8497ac64bb6870c3/src/resources/Adobe/RGB/VideoHD.icc -------------------------------------------------------------------------------- /src/resources/Adobe/RGB/VideoNTSC.icc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/php-imagine/Imagine/43856ce80debe1c7b0d7a5fc8497ac64bb6870c3/src/resources/Adobe/RGB/VideoNTSC.icc -------------------------------------------------------------------------------- /src/resources/Adobe/RGB/VideoPAL.icc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/php-imagine/Imagine/43856ce80debe1c7b0d7a5fc8497ac64bb6870c3/src/resources/Adobe/RGB/VideoPAL.icc -------------------------------------------------------------------------------- /src/resources/Adobe/Trademark Information.md: -------------------------------------------------------------------------------- 1 | *5/10/05* 2 | 3 | # Trademark Information 4 | 5 | Adobe is either a registered trademark or trademark of Adobe Systems Incorporated in the United States and/or other countries. All instances of the name “Adobe RGB” are references to the Adobe RGB (1998) color space and color encodings as defined by Adobe, unless otherwise stated. The name “Adobe RGB (1998)” also is used as a software product trademark for Adobe’s implementation of the Adobe RGB (1998) ICC profile. Adobe does not permit the use of the Adobe RGB trademark for software, hardware, or other related products from companies other than Adobe, unless the company has obtained a prior written license from Adobe to do so. 6 | 7 | ## Distributing the Adobe RGB (1998) ICC profile 8 | 9 | You may distribute the Adobe RGB (1998) ICC profile as embedded within digital image files and on a standalone basis so long as you accept the terms and conditions of the Color Profile License Agreement on this page and the Adobe end-user license agreement. 10 | 11 | Keep in mind that you must sign a supplementary license agreement with Adobe if you are interested in distributing the Adobe RGB (1998) ICC profile software embedded or as a bundle with a camera, display or other hardware device or software application. If you have not already signed an agreement, please contact your Adobe representative. 12 | -------------------------------------------------------------------------------- /src/resources/color.org/sRGB_IEC61966-2-1_black_scaled.icc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/php-imagine/Imagine/43856ce80debe1c7b0d7a5fc8497ac64bb6870c3/src/resources/color.org/sRGB_IEC61966-2-1_black_scaled.icc -------------------------------------------------------------------------------- /src/resources/color.org/sRGB_IEC61966-2-1_no_black_scaling.icc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/php-imagine/Imagine/43856ce80debe1c7b0d7a5fc8497ac64bb6870c3/src/resources/color.org/sRGB_IEC61966-2-1_no_black_scaling.icc -------------------------------------------------------------------------------- /src/resources/colormanagement.org/ISOcoated_v2_grey1c_bas.ICC: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/php-imagine/Imagine/43856ce80debe1c7b0d7a5fc8497ac64bb6870c3/src/resources/colormanagement.org/ISOcoated_v2_grey1c_bas.ICC --------------------------------------------------------------------------------