├── LICENSE.txt ├── README.md ├── composer.json └── src ├── Access.php ├── Align.php ├── Angle.php ├── Angle45.php ├── ArgumentFlags.php ├── BandFormat.php ├── BlendMode.php ├── Coding.php ├── Combine.php ├── CombineMode.php ├── CompassDirection.php ├── Config.php ├── Connection.php ├── DebugLogger.php ├── Direction.php ├── Exception.php ├── Extend.php ├── FFI.php ├── FailOn.php ├── ForeignDzContainer.php ├── ForeignDzDepth.php ├── ForeignDzLayout.php ├── ForeignHeifCompression.php ├── ForeignHeifEncoder.php ├── ForeignKeep.php ├── ForeignPngFilter.php ├── ForeignPpmFormat.php ├── ForeignSubsample.php ├── ForeignTiffCompression.php ├── ForeignTiffPredictor.php ├── ForeignTiffResunit.php ├── ForeignWebpPreset.php ├── GObject.php ├── GValue.php ├── Image.php ├── ImageAutodoc.php ├── Intent.php ├── Interesting.php ├── Interpolate.php ├── Interpretation.php ├── Introspect.php ├── Kernel.php ├── OperationBoolean.php ├── OperationComplex.php ├── OperationComplex2.php ├── OperationComplexget.php ├── OperationMath.php ├── OperationMath2.php ├── OperationMorphology.php ├── OperationRelational.php ├── OperationRound.php ├── PCS.php ├── Precision.php ├── RegionShrink.php ├── SdfShape.php ├── Size.php ├── Source.php ├── SourceCustom.php ├── SourceResource.php ├── Target.php ├── TargetCustom.php ├── TargetResource.php ├── TextWrap.php ├── Utils.php ├── VipsObject.php └── VipsOperation.php /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016 John Cupitt 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PHP binding for libvips 2 | 3 | [![CI](https://github.com/libvips/php-vips/workflows/CI/badge.svg)](https://github.com/libvips/php-vips/actions) 4 | 5 | `php-vips` is a binding for [libvips](https://github.com/libvips/libvips) 8.7 6 | and later that runs on PHP 7.4 and later. 7 | 8 | libvips is fast and needs little memory. The 9 | [`vips-php-bench`](https://github.com/jcupitt/php-vips-bench) repository 10 | tests `php-vips` against `imagick` and `gd`. On that test, and on my laptop, 11 | `php-vips` is around four times faster than `imagick` and needs 10 times 12 | less memory. 13 | 14 | Programs that use libvips don't manipulate images directly, instead they 15 | create pipelines of image processing operations starting from a source 16 | image. When the pipe is connected to a destination, the whole pipeline 17 | executes at once and in parallel, streaming the image from source to 18 | destination in a set of small fragments. 19 | 20 | ### Install 21 | 22 | You need to [install the libvips 23 | library](https://www.libvips.org/install.html). It's in the linux 24 | package managers, homebrew and MacPorts, and there are Windows binaries on 25 | the vips website. For example, on Debian: 26 | 27 | ``` 28 | sudo apt-get install --no-install-recommends libvips42 29 | ``` 30 | 31 | (`--no-install-recommends` stops Debian installing a *lot* of extra packages) 32 | 33 | Or macOS: 34 | 35 | ``` 36 | brew install vips 37 | ``` 38 | 39 | You'll need to [enable FFI in your 40 | PHP](https://www.php.net/manual/en/ffi.configuration.php), then add vips 41 | to your `composer.json`: 42 | 43 | ``` 44 | "require": { 45 | "jcupitt/vips" : "2.4.0" 46 | } 47 | ``` 48 | 49 | php-vips does not yet support preloading, so you need to enable FFI globally. 50 | This has some security implications, since anyone who can run php on your 51 | server can use it to call any native library they have access to. 52 | 53 | Of course if attackers are running their own PHP code on your webserver you 54 | are probably already toast, unfortunately. 55 | 56 | Finally, on php 8.3 and later you need to disable stack overflow 57 | tests. php-vips executes FFI callbacks off the main thread and this confuses 58 | those checks, at least in php 8.3.0. 59 | 60 | Add: 61 | 62 | ``` 63 | zend.max_allowed_stack_size=-1 64 | ``` 65 | 66 | To your `php.ini`. 67 | 68 | ### Example 69 | 70 | ```php 71 | #!/usr/bin/env php 72 | writeToFile('tiny.jpg'); 85 | 86 | // load an image, get fields, process, save 87 | $image = Vips\Image::newFromFile($argv[1]); 88 | echo "width = $image->width\n"; 89 | $image = $image->invert(); 90 | $image->writeToFile($argv[2]); 91 | ``` 92 | 93 | Run with: 94 | 95 | ``` 96 | $ composer install 97 | $ ./try1.php ~/pics/k2.jpg x.tif 98 | ``` 99 | 100 | See `examples/`. We have a [complete set of formatted API 101 | docs](https://libvips.github.io/php-vips/classes/Jcupitt-Vips-Image.html). 102 | 103 | 104 | ### How it works 105 | 106 | php-vips uses [php-ffi](https://www.php.net/manual/en/book.ffi.php) to 107 | call directly into the libvips binary. It introspects the library binary 108 | and presents the methods it finds as members of the `Image` class. 109 | 110 | This means that the API you see depends on the version of libvips that 111 | php-vips finds at runtime, and not on php-vips. php-vips documentation assumes 112 | you are using the latest stable version of the libvips library. 113 | 114 | The previous php-vips version that relied on a binary extension 115 | and not on php-ffi is still available and supported in [the 1.x 116 | branch](https://github.com/libvips/php-vips/tree/1.x). 117 | 118 | ### Introduction to the API 119 | 120 | Almost all methods return a new image as the result, so you can chain them. 121 | For example: 122 | 123 | ```php 124 | $new_image = $image->more(12)->ifthenelse(255, $image); 125 | ``` 126 | 127 | will make a mask of pixels greater than 12, then use the mask to set pixels to 128 | either 255 or the original image. 129 | 130 | Note that libvips operators always make new images, they don't modify existing 131 | images, so after the line above, `$image` is unchanged. 132 | 133 | You can use long, double, array and image as parameters. For example: 134 | 135 | ```php 136 | $image = $image->add(2); 137 | ``` 138 | 139 | to add two to every band element, or: 140 | 141 | ```php 142 | $image = $image->add([1, 2, 3]); 143 | ``` 144 | 145 | to add 1 to the first band, 2 to the second and 3 to the third. Or: 146 | 147 | ```php 148 | $image = $image->add($image2); 149 | ``` 150 | 151 | to add two images. Or: 152 | 153 | ```php 154 | $image = $image->add([[1, 2, 3], [4, 5, 6]]); 155 | ``` 156 | 157 | To make a 3 x 2 image from the array, then add that image to the original. 158 | 159 | Almost all methods can take an extra final argument: an array of options. 160 | For example: 161 | 162 | ```php 163 | $image->writeToFile("fred.jpg", ["Q" => 90]); 164 | ``` 165 | 166 | `php-vips` comes [with API 167 | docs](https://libvips.github.io/php-vips/classes/Jcupitt-Vips-Image.html). 168 | To regenerate these from your sources, type: 169 | 170 | ``` 171 | $ vendor/bin/phpdoc 172 | ``` 173 | 174 | And look in `docs/`. 175 | 176 | Unfortunatly, due to php-doc limitations, these do not list every option 177 | to every operation. For a full API description you need to see the main 178 | libvips documentation: 179 | 180 | https://libvips.org/API/current 181 | 182 | ### Test and install 183 | 184 | ``` 185 | $ composer install 186 | $ composer test 187 | $ vendor/bin/phpdoc 188 | ``` 189 | 190 | ### Regenerate auto docs 191 | 192 | ``` 193 | $ cd src 194 | $ ../examples/generate_phpdoc.py 195 | ``` 196 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jcupitt/vips", 3 | "description": "A high-level interface to the libvips image processing library.", 4 | "keywords": [ 5 | "image", 6 | "processing", 7 | "libvips" 8 | ], 9 | "homepage": "https://github.com/libvips/php-vips", 10 | "license": "MIT", 11 | "authors": [ 12 | { 13 | "name": "John Cupitt", 14 | "email": "jcupitt@gmail.com", 15 | "homepage": "https://github.com/jcupitt", 16 | "role": "Developer" 17 | } 18 | ], 19 | "require": { 20 | "php": ">=7.4", 21 | "ext-ffi": "*", 22 | "psr/log": "^1.1.3|^2.0|^3.0" 23 | }, 24 | "require-dev": { 25 | "php-parallel-lint/php-parallel-lint": "^1.3", 26 | "phpdocumentor/shim": "^3.3", 27 | "phpunit/phpunit": "^9.5", 28 | "squizlabs/php_codesniffer": "^3.7" 29 | }, 30 | "autoload": { 31 | "psr-4": { 32 | "Jcupitt\\Vips\\": "src" 33 | } 34 | }, 35 | "autoload-dev": { 36 | "psr-4": { 37 | "Jcupitt\\Vips\\Test\\": "tests" 38 | } 39 | }, 40 | "extra": { 41 | "branch-alias": { 42 | "dev-master": "2.0.x-dev" 43 | } 44 | }, 45 | "scripts": { 46 | "test": [ 47 | "parallel-lint . --exclude vendor", 48 | "phpunit", 49 | "phpcs --standard=phpcs-ruleset.xml ." 50 | ] 51 | }, 52 | "minimum-stability": "dev", 53 | "prefer-stable": true, 54 | "config": { 55 | "allow-plugins": { 56 | "phpdocumentor/shim": true 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/Access.php: -------------------------------------------------------------------------------- 1 | 34 | * @copyright 2016 John Cupitt 35 | * @license https://opensource.org/licenses/MIT MIT 36 | * @link https://github.com/jcupitt/php-vips 37 | */ 38 | 39 | namespace Jcupitt\Vips; 40 | 41 | /** 42 | * The Access enum. 43 | * @category Images 44 | * @package Jcupitt\Vips 45 | * @author John Cupitt 46 | * @copyright 2016 John Cupitt 47 | * @license https://opensource.org/licenses/MIT MIT 48 | * @link https://github.com/jcupitt/php-vips 49 | */ 50 | abstract class Access 51 | { 52 | const RANDOM = 'random'; 53 | const SEQUENTIAL = 'sequential'; 54 | const SEQUENTIAL_UNBUFFERED = 'sequential-unbuffered'; 55 | } 56 | -------------------------------------------------------------------------------- /src/Align.php: -------------------------------------------------------------------------------- 1 | 34 | * @copyright 2016 John Cupitt 35 | * @license https://opensource.org/licenses/MIT MIT 36 | * @link https://github.com/jcupitt/php-vips 37 | */ 38 | 39 | namespace Jcupitt\Vips; 40 | 41 | /** 42 | * The Align enum. 43 | * @category Images 44 | * @package Jcupitt\Vips 45 | * @author John Cupitt 46 | * @copyright 2016 John Cupitt 47 | * @license https://opensource.org/licenses/MIT MIT 48 | * @link https://github.com/jcupitt/php-vips 49 | */ 50 | abstract class Align 51 | { 52 | const LOW = 'low'; 53 | const CENTRE = 'centre'; 54 | const HIGH = 'high'; 55 | } 56 | -------------------------------------------------------------------------------- /src/Angle.php: -------------------------------------------------------------------------------- 1 | 34 | * @copyright 2016 John Cupitt 35 | * @license https://opensource.org/licenses/MIT MIT 36 | * @link https://github.com/jcupitt/php-vips 37 | */ 38 | 39 | namespace Jcupitt\Vips; 40 | 41 | /** 42 | * The Angle enum. 43 | * @category Images 44 | * @package Jcupitt\Vips 45 | * @author John Cupitt 46 | * @copyright 2016 John Cupitt 47 | * @license https://opensource.org/licenses/MIT MIT 48 | * @link https://github.com/jcupitt/php-vips 49 | */ 50 | abstract class Angle 51 | { 52 | const D0 = 'd0'; 53 | const D90 = 'd90'; 54 | const D180 = 'd180'; 55 | const D270 = 'd270'; 56 | } 57 | -------------------------------------------------------------------------------- /src/Angle45.php: -------------------------------------------------------------------------------- 1 | 34 | * @copyright 2016 John Cupitt 35 | * @license https://opensource.org/licenses/MIT MIT 36 | * @link https://github.com/jcupitt/php-vips 37 | */ 38 | 39 | namespace Jcupitt\Vips; 40 | 41 | /** 42 | * The Angle45 enum. 43 | * @category Images 44 | * @package Jcupitt\Vips 45 | * @author John Cupitt 46 | * @copyright 2016 John Cupitt 47 | * @license https://opensource.org/licenses/MIT MIT 48 | * @link https://github.com/jcupitt/php-vips 49 | */ 50 | abstract class Angle45 51 | { 52 | const D0 = 'd0'; 53 | const D45 = 'd45'; 54 | const D90 = 'd90'; 55 | const D135 = 'd135'; 56 | const D180 = 'd180'; 57 | const D225 = 'd225'; 58 | const D270 = 'd270'; 59 | const D315 = 'd315'; 60 | } 61 | -------------------------------------------------------------------------------- /src/ArgumentFlags.php: -------------------------------------------------------------------------------- 1 | 32 | * @copyright 2016 John Cupitt 33 | * @license https://opensource.org/licenses/MIT MIT 34 | * @link https://github.com/jcupitt/php-vips 35 | */ 36 | 37 | namespace Jcupitt\Vips; 38 | 39 | /** 40 | * The ArgumentFlags enum. 41 | * @category Images 42 | * @package Jcupitt\Vips 43 | * @author John Cupitt 44 | * @copyright 2016 John Cupitt 45 | * @license https://opensource.org/licenses/MIT MIT 46 | * @link https://github.com/jcupitt/php-vips 47 | */ 48 | abstract class ArgumentFlags 49 | { 50 | const REQUIRED = 1; 51 | const CONSTRUCT = 2; 52 | const SET_ONCE = 4; 53 | const SET_ALWAYS = 8; 54 | const INPUT = 16; 55 | const OUTPUT = 32; 56 | const DEPRECATED = 64; 57 | const MODIFY = 128; 58 | 59 | const NAMES = [ 60 | "REQUIRED" => self::REQUIRED, 61 | "CONSTRUCT" => self::CONSTRUCT, 62 | "SET_ONCE" => self::SET_ONCE, 63 | "SET_ALWAYS" => self::SET_ALWAYS, 64 | "INPUT" => self::INPUT, 65 | "OUTPUT" => self::OUTPUT, 66 | "DEPRECATED" => self::DEPRECATED, 67 | "MODIFY" => self::MODIFY, 68 | ]; 69 | } 70 | -------------------------------------------------------------------------------- /src/BandFormat.php: -------------------------------------------------------------------------------- 1 | 34 | * @copyright 2016 John Cupitt 35 | * @license https://opensource.org/licenses/MIT MIT 36 | * @link https://github.com/jcupitt/php-vips 37 | */ 38 | 39 | namespace Jcupitt\Vips; 40 | 41 | /** 42 | * The BandFormat enum. 43 | * @category Images 44 | * @package Jcupitt\Vips 45 | * @author John Cupitt 46 | * @copyright 2016 John Cupitt 47 | * @license https://opensource.org/licenses/MIT MIT 48 | * @link https://github.com/jcupitt/php-vips 49 | */ 50 | abstract class BandFormat 51 | { 52 | const NOTSET = 'notset'; 53 | const UCHAR = 'uchar'; 54 | const CHAR = 'char'; 55 | const USHORT = 'ushort'; 56 | const SHORT = 'short'; 57 | const UINT = 'uint'; 58 | const INT = 'int'; 59 | const FLOAT = 'float'; 60 | const COMPLEX = 'complex'; 61 | const DOUBLE = 'double'; 62 | const DPCOMPLEX = 'dpcomplex'; 63 | } 64 | -------------------------------------------------------------------------------- /src/BlendMode.php: -------------------------------------------------------------------------------- 1 | 34 | * @copyright 2016 John Cupitt 35 | * @license https://opensource.org/licenses/MIT MIT 36 | * @link https://github.com/jcupitt/php-vips 37 | */ 38 | 39 | namespace Jcupitt\Vips; 40 | 41 | /** 42 | * The BlendMode enum. 43 | * @category Images 44 | * @package Jcupitt\Vips 45 | * @author John Cupitt 46 | * @copyright 2016 John Cupitt 47 | * @license https://opensource.org/licenses/MIT MIT 48 | * @link https://github.com/jcupitt/php-vips 49 | */ 50 | abstract class BlendMode 51 | { 52 | const CLEAR = 'clear'; 53 | const SOURCE = 'source'; 54 | const OVER = 'over'; 55 | const IN = 'in'; 56 | const OUT = 'out'; 57 | const ATOP = 'atop'; 58 | const DEST = 'dest'; 59 | const DEST_OVER = 'dest-over'; 60 | const DEST_IN = 'dest-in'; 61 | const DEST_OUT = 'dest-out'; 62 | const DEST_ATOP = 'dest-atop'; 63 | const XOR1 = 'xor'; 64 | const ADD = 'add'; 65 | const SATURATE = 'saturate'; 66 | const MULTIPLY = 'multiply'; 67 | const SCREEN = 'screen'; 68 | const OVERLAY = 'overlay'; 69 | const DARKEN = 'darken'; 70 | const LIGHTEN = 'lighten'; 71 | const COLOUR_DODGE = 'colour-dodge'; 72 | const COLOUR_BURN = 'colour-burn'; 73 | const HARD_LIGHT = 'hard-light'; 74 | const SOFT_LIGHT = 'soft-light'; 75 | const DIFFERENCE = 'difference'; 76 | const EXCLUSION = 'exclusion'; 77 | } 78 | -------------------------------------------------------------------------------- /src/Coding.php: -------------------------------------------------------------------------------- 1 | 34 | * @copyright 2016 John Cupitt 35 | * @license https://opensource.org/licenses/MIT MIT 36 | * @link https://github.com/jcupitt/php-vips 37 | */ 38 | 39 | namespace Jcupitt\Vips; 40 | 41 | /** 42 | * The Coding enum. 43 | * @category Images 44 | * @package Jcupitt\Vips 45 | * @author John Cupitt 46 | * @copyright 2016 John Cupitt 47 | * @license https://opensource.org/licenses/MIT MIT 48 | * @link https://github.com/jcupitt/php-vips 49 | */ 50 | abstract class Coding 51 | { 52 | const ERROR = 'error'; 53 | const NONE = 'none'; 54 | const LABQ = 'labq'; 55 | const RAD = 'rad'; 56 | } 57 | -------------------------------------------------------------------------------- /src/Combine.php: -------------------------------------------------------------------------------- 1 | 34 | * @copyright 2016 John Cupitt 35 | * @license https://opensource.org/licenses/MIT MIT 36 | * @link https://github.com/jcupitt/php-vips 37 | */ 38 | 39 | namespace Jcupitt\Vips; 40 | 41 | /** 42 | * The Combine enum. 43 | * @category Images 44 | * @package Jcupitt\Vips 45 | * @author John Cupitt 46 | * @copyright 2016 John Cupitt 47 | * @license https://opensource.org/licenses/MIT MIT 48 | * @link https://github.com/jcupitt/php-vips 49 | */ 50 | abstract class Combine 51 | { 52 | const MAX = 'max'; 53 | const SUM = 'sum'; 54 | const MIN = 'min'; 55 | } 56 | -------------------------------------------------------------------------------- /src/CombineMode.php: -------------------------------------------------------------------------------- 1 | 34 | * @copyright 2016 John Cupitt 35 | * @license https://opensource.org/licenses/MIT MIT 36 | * @link https://github.com/jcupitt/php-vips 37 | */ 38 | 39 | namespace Jcupitt\Vips; 40 | 41 | /** 42 | * The CombineMode enum. 43 | * @category Images 44 | * @package Jcupitt\Vips 45 | * @author John Cupitt 46 | * @copyright 2016 John Cupitt 47 | * @license https://opensource.org/licenses/MIT MIT 48 | * @link https://github.com/jcupitt/php-vips 49 | */ 50 | abstract class CombineMode 51 | { 52 | const SET = 'set'; 53 | const ADD = 'add'; 54 | } 55 | -------------------------------------------------------------------------------- /src/CompassDirection.php: -------------------------------------------------------------------------------- 1 | 34 | * @copyright 2016 John Cupitt 35 | * @license https://opensource.org/licenses/MIT MIT 36 | * @link https://github.com/jcupitt/php-vips 37 | */ 38 | 39 | namespace Jcupitt\Vips; 40 | 41 | /** 42 | * The CompassDirection enum. 43 | * @category Images 44 | * @package Jcupitt\Vips 45 | * @author John Cupitt 46 | * @copyright 2016 John Cupitt 47 | * @license https://opensource.org/licenses/MIT MIT 48 | * @link https://github.com/jcupitt/php-vips 49 | */ 50 | abstract class CompassDirection 51 | { 52 | const CENTRE = 'centre'; 53 | const NORTH = 'north'; 54 | const EAST = 'east'; 55 | const SOUTH = 'south'; 56 | const WEST = 'west'; 57 | const NORTH_EAST = 'north-east'; 58 | const SOUTH_EAST = 'south-east'; 59 | const SOUTH_WEST = 'south-west'; 60 | const NORTH_WEST = 'north-west'; 61 | } 62 | -------------------------------------------------------------------------------- /src/Config.php: -------------------------------------------------------------------------------- 1 | 34 | * @copyright 2016 John Cupitt 35 | * @license https://opensource.org/licenses/MIT MIT 36 | * @link https://github.com/jcupitt/php-vips 37 | */ 38 | 39 | namespace Jcupitt\Vips; 40 | 41 | use Psr\Log\LoggerInterface; 42 | 43 | /** 44 | * This class contains the top-level libvips control methods. 45 | * 46 | * @category Images 47 | * @package Jcupitt\Vips 48 | * @author John Cupitt 49 | * @copyright 2016 John Cupitt 50 | * @license https://opensource.org/licenses/MIT MIT 51 | * @link https://github.com/jcupitt/php-vips 52 | */ 53 | class Config 54 | { 55 | 56 | /** 57 | * The logger instance. 58 | */ 59 | private static ?LoggerInterface $logger = null; 60 | 61 | /** 62 | * Sets a logger. This can be handy for debugging. For example: 63 | * 64 | * ```php 65 | * Vips\Config::setLogger(new Vips\Logger); 66 | * ``` 67 | * 68 | * @param LoggerInterface $logger 69 | * 70 | * @return void 71 | */ 72 | public static function setLogger(LoggerInterface $logger): void 73 | { 74 | self::$logger = $logger; 75 | } 76 | 77 | /** 78 | * Gets a logger. 79 | * 80 | * @return LoggerInterface|null The logger or null. 81 | */ 82 | public static function getLogger(): ?LoggerInterface 83 | { 84 | return self::$logger; 85 | } 86 | 87 | /** 88 | * Set the maximum number of operations to hold in the libvips operation 89 | * cache. 90 | * 91 | * @param integer $value The maximum number of operations to cache. 92 | * 93 | * @return void 94 | */ 95 | public static function cacheSetMax(int $value): void 96 | { 97 | FFI::vips()->vips_cache_set_max($value); 98 | } 99 | 100 | /** 101 | * Set the maximum amount of memory to allow cached operations to use, in 102 | * bytes. 103 | * 104 | * @param integer $value The maximum amount of memory cached operations can 105 | * hold, in bytes. 106 | * 107 | * @return void 108 | */ 109 | public static function cacheSetMaxMem(int $value): void 110 | { 111 | FFI::vips()->vips_cache_set_max_mem($value); 112 | } 113 | 114 | /** 115 | * Set the maximum number of open files cached operations can use. 116 | * 117 | * @param integer $value The maximum number of open files cached operations 118 | * can use. 119 | * 120 | * @return void 121 | */ 122 | public static function cacheSetMaxFiles(int $value): void 123 | { 124 | FFI::vips()->vips_cache_set_max_files($value); 125 | } 126 | 127 | /** 128 | * Set the size of the pools of worker threads vips uses for image 129 | * evaluation. 130 | * 131 | * @param integer $value The size of the pools of worker threads vips uses 132 | * for image evaluation. 133 | * 134 | * @return void 135 | */ 136 | public static function concurrencySet(int $value): void 137 | { 138 | FFI::vips()->vips_concurrency_set($value); 139 | } 140 | 141 | /** 142 | * Gets the libvips version number as a string of the form 143 | * MAJOR.MINOR.MICRO, for example "8.6.1". 144 | * 145 | * @return string 146 | */ 147 | public static function version(): string 148 | { 149 | return FFI::version(); 150 | } 151 | 152 | /** 153 | * Is this at least libvips major.minor[.patch]? 154 | * @param int $x Major component. 155 | * @param int $y Minor component. 156 | * @param int $z Patch component. 157 | * @return bool `true` if at least libvips major.minor[.patch]; otherwise, `false`. 158 | */ 159 | public static function atLeast(int $x, int $y, int $z = 0): bool 160 | { 161 | return FFI::atLeast($x, $y, $z); 162 | } 163 | } 164 | 165 | /* 166 | * Local variables: 167 | * tab-width: 4 168 | * c-basic-offset: 4 169 | * End: 170 | * vim600: expandtab sw=4 ts=4 fdm=marker 171 | * vim<600: expandtab sw=4 ts=4 172 | */ 173 | -------------------------------------------------------------------------------- /src/Connection.php: -------------------------------------------------------------------------------- 1 | pointer = FFI::vips()->cast(FFI::ctypes('VipsConnection'), $pointer); 18 | parent::__construct($pointer); 19 | } 20 | 21 | /** 22 | * Get the filename associated with a connection. Return null if there is no associated file. 23 | */ 24 | public function filename(): ?string 25 | { 26 | return FFI::vips()->vips_connection_filename($this->pointer); 27 | } 28 | 29 | /** 30 | * Make a human-readable name for a connection suitable for error messages. 31 | */ 32 | public function nick(): ?string 33 | { 34 | return FFI::vips()->vips_connection_nick($this->pointer); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/DebugLogger.php: -------------------------------------------------------------------------------- 1 | 34 | * @copyright 2016 John Cupitt 35 | * @license https://opensource.org/licenses/MIT MIT 36 | * @link https://github.com/jcupitt/php-vips 37 | */ 38 | 39 | namespace Jcupitt\Vips; 40 | 41 | use Psr\Log\LoggerInterface; 42 | use Psr\Log\LoggerTrait; 43 | 44 | const LOG_FORMAT = "[%datetime%] %level_name%: %message% %context%\n"; 45 | const DATE_FORMAT = "Y-m-d\TH:i:sP"; 46 | 47 | /** 48 | * A simple logger, handy for debugging. See Main::setLogger(). 49 | * 50 | * @category Images 51 | * @package Jcupitt\Vips 52 | * @author John Cupitt 53 | * @copyright 2016 John Cupitt 54 | * @license https://opensource.org/licenses/MIT MIT 55 | * @link https://github.com/jcupitt/php-vips 56 | */ 57 | class DebugLogger implements LoggerInterface 58 | { 59 | // Use the LoggerTrait so that we only have to implement the generic 60 | // log method. 61 | use LoggerTrait; 62 | 63 | /** 64 | * Logs with an arbitrary level. 65 | * 66 | * @param mixed $level 67 | * @param string $message 68 | * @param array $context 69 | * 70 | * @return void 71 | */ 72 | public function log($level, $message, array $context = []): void 73 | { 74 | // `Vips\Image` to string convert 75 | array_walk_recursive($context, function (&$value) { 76 | if ($value instanceof Image) { 77 | $value = (string) $value; 78 | } 79 | }); 80 | 81 | $strParams = [ 82 | '%datetime%' => date(DATE_FORMAT), 83 | '%level_name%' => $level, 84 | '%message%' => $message, 85 | '%context%' => json_encode( 86 | $context, 87 | JSON_UNESCAPED_SLASHES | 88 | JSON_UNESCAPED_UNICODE | 89 | JSON_PRESERVE_ZERO_FRACTION 90 | ), 91 | ]; 92 | 93 | echo strtr(LOG_FORMAT, $strParams); 94 | } 95 | } 96 | 97 | /* 98 | * Local variables: 99 | * tab-width: 4 100 | * c-basic-offset: 4 101 | * End: 102 | * vim600: expandtab sw=4 ts=4 fdm=marker 103 | * vim<600: expandtab sw=4 ts=4 104 | */ 105 | -------------------------------------------------------------------------------- /src/Direction.php: -------------------------------------------------------------------------------- 1 | 34 | * @copyright 2016 John Cupitt 35 | * @license https://opensource.org/licenses/MIT MIT 36 | * @link https://github.com/jcupitt/php-vips 37 | */ 38 | 39 | namespace Jcupitt\Vips; 40 | 41 | /** 42 | * The Direction enum. 43 | * @category Images 44 | * @package Jcupitt\Vips 45 | * @author John Cupitt 46 | * @copyright 2016 John Cupitt 47 | * @license https://opensource.org/licenses/MIT MIT 48 | * @link https://github.com/jcupitt/php-vips 49 | */ 50 | abstract class Direction 51 | { 52 | const HORIZONTAL = 'horizontal'; 53 | const VERTICAL = 'vertical'; 54 | } 55 | -------------------------------------------------------------------------------- /src/Exception.php: -------------------------------------------------------------------------------- 1 | 34 | * @copyright 2016 John Cupitt 35 | * @license https://opensource.org/licenses/MIT MIT 36 | * @link https://github.com/jcupitt/php-vips 37 | */ 38 | 39 | namespace Jcupitt\Vips; 40 | 41 | /** 42 | * The vips exception class. All exceptions thrown by php-vips are subclasses 43 | * of this. 44 | * 45 | * @category Images 46 | * @package Jcupitt\Vips 47 | * @author John Cupitt 48 | * @copyright 2016 John Cupitt 49 | * @license https://opensource.org/licenses/MIT MIT 50 | * @link https://github.com/jcupitt/php-vips 51 | */ 52 | class Exception extends \Exception 53 | { 54 | public function __construct($message = "", $code = 0, ?\Throwable $previous = null) 55 | { 56 | if ($message == "") { 57 | $message = "libvips error: " . FFI::vips()->vips_error_buffer(); 58 | FFI::vips()->vips_error_clear(); 59 | } 60 | 61 | Utils::errorLog($message); 62 | 63 | parent::__construct($message, $code, $previous); 64 | } 65 | } 66 | 67 | /* 68 | * Local variables: 69 | * tab-width: 4 70 | * c-basic-offset: 4 71 | * End: 72 | * vim600: expandtab sw=4 ts=4 fdm=marker 73 | * vim<600: expandtab sw=4 ts=4 74 | */ 75 | -------------------------------------------------------------------------------- /src/Extend.php: -------------------------------------------------------------------------------- 1 | 34 | * @copyright 2016 John Cupitt 35 | * @license https://opensource.org/licenses/MIT MIT 36 | * @link https://github.com/jcupitt/php-vips 37 | */ 38 | 39 | namespace Jcupitt\Vips; 40 | 41 | /** 42 | * The Extend enum. 43 | * @category Images 44 | * @package Jcupitt\Vips 45 | * @author John Cupitt 46 | * @copyright 2016 John Cupitt 47 | * @license https://opensource.org/licenses/MIT MIT 48 | * @link https://github.com/jcupitt/php-vips 49 | */ 50 | abstract class Extend 51 | { 52 | const BLACK = 'black'; 53 | const COPY = 'copy'; 54 | const REPEAT = 'repeat'; 55 | const MIRROR = 'mirror'; 56 | const WHITE = 'white'; 57 | const BACKGROUND = 'background'; 58 | } 59 | -------------------------------------------------------------------------------- /src/FFI.php: -------------------------------------------------------------------------------- 1 | 34 | * @copyright 2016 John Cupitt 35 | * @license https://opensource.org/licenses/MIT MIT 36 | * @link https://github.com/jcupitt/php-vips 37 | */ 38 | 39 | namespace Jcupitt\Vips; 40 | 41 | /** 42 | * This class contains the libvips FFI methods. 43 | * 44 | * @category Images 45 | * @package Jcupitt\Vips 46 | * @author John Cupitt 47 | * @copyright 2016 John Cupitt 48 | * @license https://opensource.org/licenses/MIT MIT 49 | * @link https://github.com/jcupitt/php-vips 50 | */ 51 | class FFI 52 | { 53 | 54 | /** 55 | * The FFI handle we use for the glib binary. 56 | * 57 | * @internal 58 | */ 59 | private static \FFI $glib; 60 | 61 | /** 62 | * The FFI handle we use for the gobject binary. 63 | * 64 | * @internal 65 | */ 66 | private static \FFI $gobject; 67 | 68 | /** 69 | * The FFI handle we use for the libvips binary. 70 | * 71 | * @internal 72 | */ 73 | private static \FFI $vips; 74 | 75 | /** 76 | * Are the above FFI handles initialized? 77 | * 78 | * @internal 79 | */ 80 | private static bool $ffi_inited = false; 81 | 82 | /** 83 | * A list of paths where libvips might reside. 84 | * 85 | * @internal 86 | */ 87 | private static array $libraryPaths = [ 88 | "" // system library 89 | ]; 90 | 91 | /** 92 | * Look up these once. 93 | * 94 | * @internal 95 | */ 96 | private static array $ctypes; 97 | private static array $gtypes; 98 | private static array $ftypes; 99 | 100 | /** 101 | * The library version number we detect. 102 | * 103 | * @internal 104 | */ 105 | private static int $library_major; 106 | private static int $library_minor; 107 | private static int $library_micro; 108 | 109 | public static function glib(): \FFI 110 | { 111 | self::init(); 112 | 113 | return self::$glib; 114 | } 115 | 116 | public static function gobject(): \FFI 117 | { 118 | self::init(); 119 | 120 | return self::$gobject; 121 | } 122 | 123 | public static function vips(): \FFI 124 | { 125 | self::init(); 126 | 127 | return self::$vips; 128 | } 129 | 130 | public static function ctypes(string $name): \FFI\CType 131 | { 132 | self::init(); 133 | 134 | return self::$ctypes[$name]; 135 | } 136 | 137 | public static function gtypes(string $name): int 138 | { 139 | self::init(); 140 | 141 | return self::$gtypes[$name]; 142 | } 143 | 144 | public static function ftypes(string $name): string 145 | { 146 | self::init(); 147 | 148 | return self::$ftypes[$name]; 149 | } 150 | 151 | /** 152 | * Gets the libvips version number as a string of the form 153 | * MAJOR.MINOR.MICRO, for example "8.6.1". 154 | * 155 | * @return string 156 | */ 157 | public static function version(): string 158 | { 159 | self::init(); 160 | 161 | return self::$library_major . "." . 162 | self::$library_minor . "." . 163 | self::$library_micro; 164 | } 165 | 166 | /** 167 | * Is this at least libvips major.minor[.patch]? 168 | * @param int $x Major component. 169 | * @param int $y Minor component. 170 | * @param int $z Patch component. 171 | * @return bool `true` if at least libvips major.minor[.patch]; otherwise, `false`. 172 | */ 173 | public static function atLeast(int $x, int $y, int $z = 0): bool 174 | { 175 | return self::$library_major > $x || 176 | (self::$library_major === $x && self::$library_minor > $y) || 177 | (self::$library_major === $x && self::$library_minor === $y && 178 | self::$library_micro >= $z); 179 | } 180 | 181 | /** 182 | * Adds a directory to the search path for shared libraries. 183 | * 184 | * This method has no effect if FFI handles are already initialized, 185 | * if the specified path is non-existent, or if the path is already 186 | * included. 187 | * 188 | * @param string $path The path of the library. 189 | * @return bool `true` if the path was added; otherwise, `false`. 190 | */ 191 | public static function addLibraryPath(string $path): bool 192 | { 193 | // Already initialized. 194 | if (self::$ffi_inited) { 195 | return false; 196 | } 197 | 198 | $path = realpath($path); 199 | if ($path === false) { 200 | return false; 201 | } 202 | 203 | $path .= DIRECTORY_SEPARATOR; 204 | 205 | if (in_array($path, self::$libraryPaths)) { 206 | return false; 207 | } 208 | 209 | self::$libraryPaths[] = $path; 210 | 211 | return true; 212 | } 213 | 214 | /** 215 | * Shut down libvips. Call this just before process exit. 216 | * 217 | * @return void 218 | */ 219 | public static function shutDown(): void 220 | { 221 | self::vips()->vips_shutdown(); 222 | } 223 | 224 | public static function newGClosure(): \FFI\CData 225 | { 226 | // GClosure measures 32-bit with the first few fields until marshal. 227 | // - Marshal is a function pointer, thus platform-dependant. 228 | // - Data is a pointer, thus platform-dependant. 229 | // - Notifiers is an array-pointer, thus platform-dependant. 230 | // All in all it's basically 4 (bytes) + 3 * POINTER_SIZE 231 | // However, gobject wants 8 (bytes) + 3 * POINTER_SIZE. 232 | // I'm not sure where that extra byte comes from. Padding on 64-bit machines? 233 | return self::gobject()->g_closure_new_simple(8 + 3 * PHP_INT_SIZE, null); 234 | } 235 | 236 | private static function libraryName(string $name, int $abi): string 237 | { 238 | switch (PHP_OS_FAMILY) { 239 | case "Windows": 240 | return "$name-$abi.dll"; 241 | 242 | case "OSX": 243 | case "Darwin": 244 | return "$name.$abi.dylib"; 245 | 246 | default: 247 | // most *nix 248 | return "$name.so.$abi"; 249 | } 250 | } 251 | 252 | private static function libraryLoad( 253 | string $libraryName, 254 | string $interface 255 | ): ?\FFI { 256 | Utils::debugLog("trying to open", ["libraryName" => $libraryName]); 257 | foreach (self::$libraryPaths as $path) { 258 | Utils::debugLog("trying path", ["path" => $path]); 259 | try { 260 | $library = \FFI::cdef($interface, $path . $libraryName); 261 | Utils::debugLog("success", []); 262 | return $library; 263 | } catch (\FFI\Exception $e) { 264 | Utils::debugLog("init", [ 265 | "msg" => "library load failed", 266 | "exception" => $e->getMessage() 267 | ]); 268 | } 269 | } 270 | return null; 271 | } 272 | 273 | private static function init(): void 274 | { 275 | // Already initialized. 276 | if (self::$ffi_inited) { 277 | return; 278 | } 279 | 280 | // detect the most common installation problems 281 | if (!extension_loaded('ffi')) { 282 | throw new Exception('FFI extension not loaded'); 283 | } 284 | if (!ini_get('ffi.enable')) { 285 | throw new Exception("ffi.enable not set to 'true'"); 286 | } 287 | 288 | $vips_libname = self::libraryName("libvips", 42); 289 | $glib_libname = self::libraryName("libglib-2.0", 0); 290 | $gobject_libname = self::libraryName("libgobject-2.0", 0); 291 | 292 | Utils::debugLog("init", ["library" => $vips_libname]); 293 | 294 | $is_64bits = PHP_INT_SIZE === 8; 295 | 296 | if (PHP_OS_FAMILY === "OSX" || PHP_OS_FAMILY === "Darwin") { 297 | // Homebrew on Apple Silicon 298 | self::addLibraryPath("/opt/homebrew/lib"); 299 | // See https://github.com/Homebrew/brew/issues/13481#issuecomment-1207203483 300 | self::addLibraryPath("/usr/local/lib"); 301 | } 302 | 303 | $vips = self::libraryLoad($vips_libname, <<<'CPP' 304 | int vips_init (const char *argv0); 305 | const char *vips_error_buffer (void); 306 | int vips_version(int flag); 307 | CPP); 308 | 309 | if ($vips === null) { 310 | // drop the "" (system path) member 311 | array_shift(self::$libraryPaths); 312 | $msg = "Unable to open library '$vips_libname'"; 313 | if (!empty(self::$libraryPaths)) { 314 | $msg .= " in any of ['" . implode("', '", self::$libraryPaths) . "']"; 315 | } 316 | $msg .= ". Make sure that you've installed libvips and that '$vips_libname'"; 317 | $msg .= " is on your system's library search path."; 318 | throw new Exception($msg); 319 | } 320 | 321 | $result = $vips->vips_init(""); 322 | if ($result != 0) { 323 | throw new Exception("libvips error: " . $vips->vips_error_buffer()); 324 | } 325 | Utils::debugLog("init", ["vips_init" => $result]); 326 | 327 | # get the library version number, then we can build the API 328 | self::$library_major = $vips->vips_version(0); 329 | self::$library_minor = $vips->vips_version(1); 330 | self::$library_micro = $vips->vips_version(2); 331 | Utils::debugLog("init", [ 332 | "libvips version" => [ 333 | self::$library_major, 334 | self::$library_minor, 335 | self::$library_micro 336 | ] 337 | ]); 338 | 339 | if (!self::atLeast(8, 7)) { 340 | throw new Exception("your libvips is too old -- " . 341 | "8.7 or later required"); 342 | } 343 | 344 | // Typedefs shared across the libvips, GLib and GObject declarations 345 | $typedefs = <<<'CPP' 346 | // we need the glib names for these types 347 | typedef uint32_t guint32; 348 | typedef int32_t gint32; 349 | typedef uint64_t guint64; 350 | typedef int64_t gint64; 351 | typedef void* gpointer; 352 | 353 | CPP; 354 | 355 | // GType is the size of a pointer 356 | $typedefs .= 'typedef ' . ($is_64bits ? 'guint64' : 'guint32') . ' GType;'; 357 | 358 | $typedefs .= <<<'CPP' 359 | 360 | typedef struct _GData GData; 361 | 362 | typedef struct _GTypeClass GTypeClass; 363 | 364 | typedef struct _GTypeInstance { 365 | GTypeClass *g_class; 366 | } GTypeInstance; 367 | 368 | typedef struct _GObject { 369 | GTypeInstance g_type_instance; 370 | unsigned int ref_count; 371 | GData *qdata; 372 | } GObject; 373 | CPP; 374 | 375 | // GLib declarations 376 | $glib_decls = $typedefs . <<<'CPP' 377 | void* g_malloc (size_t size); 378 | void g_free (void* data); 379 | void g_strfreev (char** str_array); 380 | CPP; 381 | 382 | // GObject declarations 383 | $gobject_decls = $typedefs . <<<'CPP' 384 | typedef struct _GValue { 385 | GType g_type; 386 | guint64 data[2]; 387 | } GValue; 388 | 389 | typedef struct _GParamSpec { 390 | GTypeInstance g_type_instance; 391 | 392 | const char* name; 393 | unsigned int flags; 394 | GType value_type; 395 | GType owner_type; 396 | 397 | // private, but cffi in API mode needs these to be able to get the 398 | // offset of any member 399 | char* _nick; 400 | char* _blurb; 401 | GData* qdata; 402 | unsigned int ref_count; 403 | unsigned int param_id; 404 | } GParamSpec; 405 | 406 | const char* g_type_name (GType gtype); 407 | GType g_type_from_name (const char* name); 408 | 409 | void g_value_init (GValue* value, GType gtype); 410 | void g_value_unset (GValue* value); 411 | GType g_type_fundamental (GType gtype); 412 | 413 | void g_value_set_boolean (GValue* value, bool v_boolean); 414 | void g_value_set_int (GValue* value, int i); 415 | void g_value_set_uint64 (GValue* value, guint64 ull); 416 | void g_value_set_int64 (GValue* value, guint64 ull); 417 | void g_value_set_double (GValue* value, double d); 418 | void g_value_set_enum (GValue* value, int e); 419 | void g_value_set_flags (GValue* value, unsigned int f); 420 | void g_value_set_string (GValue* value, const char* str); 421 | void g_value_set_object (GValue* value, void* object); 422 | void g_value_set_pointer (GValue* value, gpointer pointer); 423 | 424 | bool g_value_get_boolean (const GValue* value); 425 | int g_value_get_int (GValue* value); 426 | guint64 g_value_get_uint64 (GValue* value); 427 | gint64 g_value_get_int64 (GValue* value); 428 | double g_value_get_double (GValue* value); 429 | int g_value_get_enum (GValue* value); 430 | unsigned int g_value_get_flags (GValue* value); 431 | const char* g_value_get_string (GValue* value); 432 | void* g_value_get_object (GValue* value); 433 | gpointer g_value_get_pointer (GValue* value); 434 | 435 | typedef struct _GEnumValue { 436 | int value; 437 | 438 | const char *value_name; 439 | const char *value_nick; 440 | } GEnumValue; 441 | 442 | typedef struct _GEnumClass { 443 | GTypeClass *g_type_class; 444 | 445 | int minimum; 446 | int maximum; 447 | unsigned int n_values; 448 | GEnumValue *values; 449 | } GEnumClass; 450 | 451 | typedef struct _GFlagsValue { 452 | unsigned int value; 453 | 454 | const char *value_name; 455 | const char *value_nick; 456 | } GFlagsValue; 457 | 458 | typedef struct _GFlagsClass { 459 | GTypeClass *g_type_class; 460 | 461 | unsigned int mask; 462 | unsigned int n_values; 463 | GFlagsValue *values; 464 | } GFlagsClass; 465 | 466 | void* g_type_class_ref (GType type); 467 | 468 | void* g_object_new (GType type, void*); 469 | void g_object_ref (void* object); 470 | void g_object_unref (void* object); 471 | 472 | void g_object_set_property (GObject* object, 473 | const char *name, GValue* value); 474 | void g_object_get_property (GObject* object, 475 | const char* name, GValue* value); 476 | 477 | typedef void (*GCallback)(void); 478 | typedef void (*GClosureNotify)(void* data, struct _GClosure *); 479 | long g_signal_connect_data (GObject* object, 480 | const char* detailed_signal, 481 | GCallback c_handler, 482 | void* data, 483 | GClosureNotify destroy_data, 484 | int connect_flags); 485 | 486 | const char* g_param_spec_get_blurb (GParamSpec* psp); 487 | 488 | typedef void *GClosure; 489 | typedef void (*marshaler)( 490 | struct GClosure* closure, 491 | GValue* return_value, 492 | int n_param_values, 493 | const GValue* param_values, 494 | void* invocation_hint, 495 | void* marshal_data 496 | ); 497 | void g_closure_set_marshal(GClosure* closure, marshaler marshal); 498 | long g_signal_connect_closure(GObject* object, const char* detailed_signal, GClosure *closure, bool after); 499 | GClosure* g_closure_new_simple (int sizeof_closure, void* data); 500 | CPP; 501 | 502 | # the whole libvips API, mostly adapted from pyvips 503 | $vips_decls = $typedefs . <<<'CPP' 504 | typedef struct _VipsImage VipsImage; 505 | typedef struct _VipsProgress VipsProgress; 506 | 507 | // Defined in GObject, just typedef to void 508 | typedef void GParamSpec; 509 | typedef void GValue; 510 | 511 | int vips_init (const char *argv0); 512 | int vips_shutdown (void); 513 | 514 | const char *vips_error_buffer (void); 515 | void vips_error_clear (void); 516 | void vips_error_freeze (void); 517 | void vips_error_thaw (void); 518 | 519 | int vips_version(int flag); 520 | 521 | void vips_leak_set (int leak); 522 | 523 | GType vips_type_find (const char* basename, const char* nickname); 524 | const char* vips_nickname_find (GType type); 525 | 526 | typedef void* (*VipsTypeMap2Fn) (GType type, void* a, void* b); 527 | void* vips_type_map (GType base, VipsTypeMap2Fn fn, void* a, void* b); 528 | 529 | int vips_enum_from_nick (const char* domain, 530 | GType gtype, const char* str); 531 | const char *vips_enum_nick (GType gtype, int value); 532 | 533 | void vips_value_set_ref_string (GValue* value, const char* str); 534 | void vips_value_set_array_double (GValue* value, 535 | const double* array, int n ); 536 | void vips_value_set_array_int (GValue* value, 537 | const int* array, int n ); 538 | void vips_value_set_array_image (GValue *value, int n); 539 | typedef void (*FreeFn)(void* a); 540 | void vips_value_set_blob (GValue* value, 541 | FreeFn free_fn, void* data, size_t length); 542 | void* vips_blob_copy (const void *data, size_t length); 543 | void vips_area_unref (void *area); 544 | 545 | const char* vips_value_get_ref_string (const GValue* value, 546 | size_t* length); 547 | double* vips_value_get_array_double (const GValue* value, int* n); 548 | int* vips_value_get_array_int (const GValue* value, int* n); 549 | VipsImage** vips_value_get_array_image (const GValue* value, int* n); 550 | void* vips_value_get_blob (const GValue* value, size_t* length); 551 | 552 | // need to make some of these by hand 553 | GType vips_interpretation_get_type (void); 554 | GType vips_operation_flags_get_type (void); 555 | GType vips_band_format_get_type (void); 556 | 557 | void vips_image_set_progress (VipsImage* image, bool progress); 558 | void vips_image_set_kill (VipsImage* image, bool kill); 559 | 560 | typedef struct _VipsProgress { 561 | VipsImage* im; 562 | 563 | int run; 564 | int eta; 565 | gint64 tpels; 566 | gint64 npels; 567 | int percent; 568 | void* start; 569 | } VipsProgress; 570 | 571 | typedef struct _VipsObject { 572 | GObject parent_instance; 573 | 574 | bool constructed; 575 | bool static_object; 576 | void *argument_table; 577 | char *nickname; 578 | char *description; 579 | bool preclose; 580 | bool close; 581 | bool postclose; 582 | size_t local_memory; 583 | } VipsObject; 584 | 585 | typedef struct _VipsObjectClass VipsObjectClass; 586 | 587 | typedef struct _VipsArgument { 588 | GParamSpec *pspec; 589 | } VipsArgument; 590 | 591 | typedef struct _VipsArgumentInstance { 592 | VipsArgument parent; 593 | 594 | // more 595 | } VipsArgumentInstance; 596 | 597 | typedef enum _VipsArgumentFlags { 598 | VIPS_ARGUMENT_NONE = 0, 599 | VIPS_ARGUMENT_REQUIRED = 1, 600 | VIPS_ARGUMENT_CONSTRUCT = 2, 601 | VIPS_ARGUMENT_SET_ONCE = 4, 602 | VIPS_ARGUMENT_SET_ALWAYS = 8, 603 | VIPS_ARGUMENT_INPUT = 16, 604 | VIPS_ARGUMENT_OUTPUT = 32, 605 | VIPS_ARGUMENT_DEPRECATED = 64, 606 | VIPS_ARGUMENT_MODIFY = 128 607 | } VipsArgumentFlags; 608 | 609 | typedef struct _VipsArgumentClass { 610 | VipsArgument parent; 611 | 612 | VipsObjectClass *object_class; 613 | VipsArgumentFlags flags; 614 | int priority; 615 | unsigned int offset; 616 | } VipsArgumentClass; 617 | 618 | int vips_object_get_argument (VipsObject* object, const char *name, 619 | GParamSpec** pspec, 620 | VipsArgumentClass** argument_class, 621 | VipsArgumentInstance** argument_instance); 622 | 623 | void vips_object_print_all (void); 624 | 625 | int vips_object_set_from_string (VipsObject* object, 626 | const char* options); 627 | 628 | const char* vips_object_get_description (VipsObject* object); 629 | 630 | typedef struct _VipsImage { 631 | VipsObject parent_instance; 632 | // more 633 | } VipsImage; 634 | 635 | const char* vips_foreign_find_load (const char* name); 636 | const char* vips_foreign_find_load_buffer (const void* data, 637 | size_t size); 638 | const char* vips_foreign_find_save (const char* name); 639 | const char* vips_foreign_find_save_buffer (const char* suffix); 640 | 641 | VipsImage* vips_image_new_matrix_from_array (int width, int height, 642 | const double* array, int size); 643 | VipsImage* vips_image_new_from_memory (const void* data, size_t size, 644 | int width, int height, int bands, int format); 645 | VipsImage* vips_image_new_from_memory_copy (const void *data, size_t size, 646 | int width, int height, int bands, int format); 647 | 648 | VipsImage* vips_image_copy_memory (VipsImage* image); 649 | 650 | GType vips_image_get_typeof (const VipsImage* image, 651 | const char* name); 652 | int vips_image_get (const VipsImage* image, 653 | const char* name, GValue* value_copy); 654 | void vips_image_set (VipsImage* image, 655 | const char* name, GValue* value); 656 | int vips_image_remove (VipsImage* image, const char* name); 657 | 658 | char* vips_filename_get_filename (const char* vips_filename); 659 | char* vips_filename_get_options (const char* vips_filename); 660 | 661 | VipsImage* vips_image_new_temp_file (const char* format); 662 | 663 | int vips_image_write (VipsImage* image, VipsImage* out); 664 | void* vips_image_write_to_memory (VipsImage* in, size_t* size_out); 665 | 666 | typedef struct _VipsInterpolate { 667 | VipsObject parent_object; 668 | 669 | // more 670 | } VipsInterpolate; 671 | 672 | VipsInterpolate* vips_interpolate_new (const char* name); 673 | 674 | typedef struct _VipsOperation { 675 | VipsObject parent_instance; 676 | 677 | // more 678 | } VipsOperation; 679 | 680 | VipsOperation* vips_operation_new (const char* name); 681 | 682 | typedef void* (*VipsArgumentMapFn) (VipsObject* object, 683 | GParamSpec* pspec, 684 | VipsArgumentClass* argument_class, 685 | VipsArgumentInstance* argument_instance, 686 | void* a, void* b); 687 | 688 | void* vips_argument_map (VipsObject* object, 689 | VipsArgumentMapFn fn, void* a, void* b); 690 | 691 | typedef struct _VipsRegion { 692 | VipsObject parent_object; 693 | 694 | // more 695 | } VipsRegion; 696 | VipsRegion* vips_region_new (VipsImage*); 697 | 698 | VipsOperation* vips_cache_operation_build (VipsOperation* operation); 699 | void vips_object_unref_outputs (VipsObject* object); 700 | 701 | int vips_operation_get_flags (VipsOperation* operation); 702 | 703 | void vips_concurrency_set( int concurrency ); 704 | 705 | void vips_cache_set_max (int max); 706 | void vips_cache_set_max_mem (size_t max_mem); 707 | void vips_cache_set_max_files (int max_files); 708 | void vips_cache_set_trace (int trace); 709 | 710 | int vips_cache_get_max(); 711 | int vips_cache_get_size(); 712 | size_t vips_cache_get_max_mem(); 713 | int vips_cache_get_max_files(); 714 | 715 | size_t vips_tracked_get_mem_highwater(); 716 | size_t vips_tracked_get_mem(); 717 | int vips_tracked_get_allocs(); 718 | int vips_tracked_get_files(); 719 | 720 | char** vips_image_get_fields (VipsImage* image); 721 | int vips_image_hasalpha (VipsImage* image); 722 | 723 | GType vips_blend_mode_get_type (void); 724 | void vips_value_set_blob_free (GValue* value, void* data, size_t length); 725 | 726 | int vips_object_get_args (VipsObject* object, 727 | const char*** names, int** flags, int* n_args); 728 | CPP; 729 | 730 | if (self::atLeast(8, 8)) { 731 | $vips_decls = $vips_decls . <<<'CPP' 732 | char** vips_foreign_get_suffixes (void); 733 | 734 | void* vips_region_fetch (VipsRegion*, int, int, int, int, 735 | size_t* length); 736 | int vips_region_width (VipsRegion*); 737 | int vips_region_height (VipsRegion*); 738 | int vips_image_get_page_height (VipsImage*); 739 | int vips_image_get_n_pages (VipsImage*); 740 | CPP; 741 | } 742 | 743 | if (self::atLeast(8, 8)) { 744 | $vips_decls = $vips_decls . <<<'CPP' 745 | typedef struct _VipsConnection { 746 | VipsObject parent_object; 747 | 748 | // more 749 | } VipsConnection; 750 | 751 | const char* vips_connection_filename (VipsConnection* stream); 752 | const char* vips_connection_nick (VipsConnection* stream); 753 | 754 | typedef struct _VipsSource { 755 | VipsConnection parent_object; 756 | 757 | // more 758 | } VipsSource; 759 | 760 | VipsSource* vips_source_new_from_descriptor (int descriptor); 761 | VipsSource* vips_source_new_from_file (const char* filename); 762 | VipsSource* vips_source_new_from_blob (void* blob); 763 | 764 | typedef struct _VipsSourceCustom { 765 | VipsSource parent_object; 766 | 767 | // more 768 | } VipsSourceCustom; 769 | 770 | VipsSourceCustom* vips_source_custom_new (void); 771 | 772 | typedef struct _VipsTarget { 773 | VipsConnection parent_object; 774 | 775 | // more 776 | } VipsTarget; 777 | 778 | VipsTarget* vips_target_new_to_descriptor (int descriptor); 779 | VipsTarget* vips_target_new_to_file (const char* filename); 780 | VipsTarget* vips_target_new_to_memory (void); 781 | 782 | typedef struct _VipsTargetCustom { 783 | VipsTarget parent_object; 784 | 785 | // more 786 | } VipsTargetCustom; 787 | 788 | VipsTargetCustom* vips_target_custom_new (void); 789 | 790 | const char* vips_foreign_find_load_source (VipsSource *source); 791 | const char* vips_foreign_find_save_target (const char* suffix); 792 | CPP; 793 | } 794 | 795 | Utils::debugLog("init", ["binding ..."]); 796 | 797 | /** 798 | * We can sometimes get dependent libraries from libvips -- either the platform 799 | * will open dependencies for us automatically, or the libvips binary has been 800 | * built to includes all main dependencies (common on Windows, can happen 801 | * elsewhere). 802 | * 803 | * We must get GLib functions from libvips if we can, since it will be the 804 | * one that libvips itself is using, and they will share runtime types. 805 | */ 806 | self::$glib = 807 | self::libraryLoad($vips_libname, $glib_decls) ?? 808 | self::libraryLoad($glib_libname, $glib_decls); 809 | self::$gobject = 810 | self::libraryLoad($vips_libname, $gobject_decls) ?? 811 | self::libraryLoad($gobject_libname, $gobject_decls); 812 | 813 | self::$vips = self::libraryLoad($vips_libname, $vips_decls); 814 | 815 | # Useful for debugging 816 | # self::$vips->vips_leak_set(1); 817 | 818 | # force the creation of some types we need 819 | self::$vips->vips_blend_mode_get_type(); 820 | self::$vips->vips_interpretation_get_type(); 821 | self::$vips->vips_operation_flags_get_type(); 822 | self::$vips->vips_band_format_get_type(); 823 | 824 | // look these up in advance 825 | self::$ctypes = [ 826 | "GObject" => self::$gobject->type("GObject*"), 827 | "GClosure" => self::$gobject->type("GClosure"), 828 | "GParamSpec" => self::$gobject->type("GParamSpec*"), 829 | "VipsObject" => self::$vips->type("VipsObject*"), 830 | "VipsOperation" => self::$vips->type("VipsOperation*"), 831 | "VipsImage" => self::$vips->type("VipsImage*"), 832 | "VipsInterpolate" => self::$vips->type("VipsInterpolate*"), 833 | "VipsProgress" => self::$vips->type("VipsProgress*"), 834 | ]; 835 | 836 | if (self::atLeast(8, 9)) { 837 | self::$ctypes = array_merge(self::$ctypes, [ 838 | "VipsConnection" => self::$vips->type("VipsConnection*"), 839 | "VipsSource" => self::$vips->type("VipsSource*"), 840 | "VipsSourceCustom" => self::$vips->type("VipsSourceCustom*"), 841 | "VipsTarget" => self::$vips->type("VipsTarget*"), 842 | "VipsTargetCustom" => self::$vips->type("VipsTargetCustom*"), 843 | ]); 844 | } 845 | 846 | self::$gtypes = [ 847 | "gboolean" => self::$gobject->g_type_from_name("gboolean"), 848 | "gint" => self::$gobject->g_type_from_name("gint"), 849 | "gint64" => self::$gobject->g_type_from_name("gint64"), 850 | "guint64" => self::$gobject->g_type_from_name("guint64"), 851 | "gdouble" => self::$gobject->g_type_from_name("gdouble"), 852 | "gchararray" => self::$gobject->g_type_from_name("gchararray"), 853 | "VipsRefString" => self::$gobject->g_type_from_name("VipsRefString"), 854 | 855 | "GEnum" => self::$gobject->g_type_from_name("GEnum"), 856 | "GFlags" => self::$gobject->g_type_from_name("GFlags"), 857 | "VipsBandFormat" => self::$gobject->g_type_from_name("VipsBandFormat"), 858 | "VipsBlendMode" => self::$gobject->g_type_from_name("VipsBlendMode"), 859 | "VipsArrayInt" => self::$gobject->g_type_from_name("VipsArrayInt"), 860 | "VipsArrayDouble" => 861 | self::$gobject->g_type_from_name("VipsArrayDouble"), 862 | "VipsArrayImage" => self::$gobject->g_type_from_name("VipsArrayImage"), 863 | "VipsBlob" => self::$gobject->g_type_from_name("VipsBlob"), 864 | 865 | "GObject" => self::$gobject->g_type_from_name("GObject"), 866 | "VipsImage" => self::$gobject->g_type_from_name("VipsImage"), 867 | 868 | "GClosure" => self::$gobject->g_type_from_name("GClosure"), 869 | ]; 870 | 871 | // map vips format names to c type names 872 | self::$ftypes = [ 873 | "char" => "char", 874 | "uchar" => "unsigned char", 875 | "short" => "short", 876 | "ushort" => "unsigned short", 877 | "int" => "int", 878 | "uint" => "unsigned int", 879 | "float" => "float", 880 | "double" => "double", 881 | "complex" => "float", 882 | "dpcomplex" => "double", 883 | ]; 884 | 885 | Utils::debugLog("init", ["done"]); 886 | self::$ffi_inited = true; 887 | } 888 | } 889 | 890 | /* 891 | * Local variables: 892 | * tab-width: 4 893 | * c-basic-offset: 4 894 | * End: 895 | * vim600: expandtab sw=4 ts=4 fdm=marker 896 | * vim<600: expandtab sw=4 ts=4 897 | */ 898 | -------------------------------------------------------------------------------- /src/FailOn.php: -------------------------------------------------------------------------------- 1 | 34 | * @copyright 2016 John Cupitt 35 | * @license https://opensource.org/licenses/MIT MIT 36 | * @link https://github.com/jcupitt/php-vips 37 | */ 38 | 39 | namespace Jcupitt\Vips; 40 | 41 | /** 42 | * The FailOn enum. 43 | * @category Images 44 | * @package Jcupitt\Vips 45 | * @author John Cupitt 46 | * @copyright 2016 John Cupitt 47 | * @license https://opensource.org/licenses/MIT MIT 48 | * @link https://github.com/jcupitt/php-vips 49 | */ 50 | abstract class FailOn 51 | { 52 | const NONE = 'none'; 53 | const TRUNCATED = 'truncated'; 54 | const ERROR = 'error'; 55 | const WARNING = 'warning'; 56 | } 57 | -------------------------------------------------------------------------------- /src/ForeignDzContainer.php: -------------------------------------------------------------------------------- 1 | 34 | * @copyright 2016 John Cupitt 35 | * @license https://opensource.org/licenses/MIT MIT 36 | * @link https://github.com/jcupitt/php-vips 37 | */ 38 | 39 | namespace Jcupitt\Vips; 40 | 41 | /** 42 | * The ForeignDzContainer enum. 43 | * @category Images 44 | * @package Jcupitt\Vips 45 | * @author John Cupitt 46 | * @copyright 2016 John Cupitt 47 | * @license https://opensource.org/licenses/MIT MIT 48 | * @link https://github.com/jcupitt/php-vips 49 | */ 50 | abstract class ForeignDzContainer 51 | { 52 | const FS = 'fs'; 53 | const ZIP = 'zip'; 54 | const SZI = 'szi'; 55 | } 56 | -------------------------------------------------------------------------------- /src/ForeignDzDepth.php: -------------------------------------------------------------------------------- 1 | 34 | * @copyright 2016 John Cupitt 35 | * @license https://opensource.org/licenses/MIT MIT 36 | * @link https://github.com/jcupitt/php-vips 37 | */ 38 | 39 | namespace Jcupitt\Vips; 40 | 41 | /** 42 | * The ForeignDzDepth enum. 43 | * @category Images 44 | * @package Jcupitt\Vips 45 | * @author John Cupitt 46 | * @copyright 2016 John Cupitt 47 | * @license https://opensource.org/licenses/MIT MIT 48 | * @link https://github.com/jcupitt/php-vips 49 | */ 50 | abstract class ForeignDzDepth 51 | { 52 | const ONEPIXEL = 'onepixel'; 53 | const ONETILE = 'onetile'; 54 | const ONE = 'one'; 55 | } 56 | -------------------------------------------------------------------------------- /src/ForeignDzLayout.php: -------------------------------------------------------------------------------- 1 | 34 | * @copyright 2016 John Cupitt 35 | * @license https://opensource.org/licenses/MIT MIT 36 | * @link https://github.com/jcupitt/php-vips 37 | */ 38 | 39 | namespace Jcupitt\Vips; 40 | 41 | /** 42 | * The ForeignDzLayout enum. 43 | * @category Images 44 | * @package Jcupitt\Vips 45 | * @author John Cupitt 46 | * @copyright 2016 John Cupitt 47 | * @license https://opensource.org/licenses/MIT MIT 48 | * @link https://github.com/jcupitt/php-vips 49 | */ 50 | abstract class ForeignDzLayout 51 | { 52 | const DZ = 'dz'; 53 | const ZOOMIFY = 'zoomify'; 54 | const GOOGLE = 'google'; 55 | const IIIF = 'iiif'; 56 | const IIIF3 = 'iiif3'; 57 | } 58 | -------------------------------------------------------------------------------- /src/ForeignHeifCompression.php: -------------------------------------------------------------------------------- 1 | 34 | * @copyright 2016 John Cupitt 35 | * @license https://opensource.org/licenses/MIT MIT 36 | * @link https://github.com/jcupitt/php-vips 37 | */ 38 | 39 | namespace Jcupitt\Vips; 40 | 41 | /** 42 | * The ForeignHeifCompression enum. 43 | * @category Images 44 | * @package Jcupitt\Vips 45 | * @author John Cupitt 46 | * @copyright 2016 John Cupitt 47 | * @license https://opensource.org/licenses/MIT MIT 48 | * @link https://github.com/jcupitt/php-vips 49 | */ 50 | abstract class ForeignHeifCompression 51 | { 52 | const HEVC = 'hevc'; 53 | const AVC = 'avc'; 54 | const JPEG = 'jpeg'; 55 | const AV1 = 'av1'; 56 | } 57 | -------------------------------------------------------------------------------- /src/ForeignHeifEncoder.php: -------------------------------------------------------------------------------- 1 | 34 | * @copyright 2016 John Cupitt 35 | * @license https://opensource.org/licenses/MIT MIT 36 | * @link https://github.com/jcupitt/php-vips 37 | */ 38 | 39 | namespace Jcupitt\Vips; 40 | 41 | /** 42 | * The ForeignHeifEncoder enum. 43 | * @category Images 44 | * @package Jcupitt\Vips 45 | * @author John Cupitt 46 | * @copyright 2016 John Cupitt 47 | * @license https://opensource.org/licenses/MIT MIT 48 | * @link https://github.com/jcupitt/php-vips 49 | */ 50 | abstract class ForeignHeifEncoder 51 | { 52 | const AUTO = 'auto'; 53 | const AOM = 'aom'; 54 | const RAV1E = 'rav1e'; 55 | const SVT = 'svt'; 56 | const X265 = 'x265'; 57 | } 58 | -------------------------------------------------------------------------------- /src/ForeignKeep.php: -------------------------------------------------------------------------------- 1 | 34 | * @copyright 2016 John Cupitt 35 | * @license https://opensource.org/licenses/MIT MIT 36 | * @link https://github.com/jcupitt/php-vips 37 | */ 38 | 39 | namespace Jcupitt\Vips; 40 | 41 | /** 42 | * The ForeignKeep flags. 43 | * @category Images 44 | * @package Jcupitt\Vips 45 | * @author John Cupitt 46 | * @copyright 2016 John Cupitt 47 | * @license https://opensource.org/licenses/MIT MIT 48 | * @link https://github.com/jcupitt/php-vips 49 | */ 50 | abstract class ForeignKeep 51 | { 52 | const NONE = 0; 53 | const EXIF = 1; 54 | const XMP = 2; 55 | const IPTC = 4; 56 | const ICC = 8; 57 | const OTHER = 16; 58 | const ALL = 31; 59 | } 60 | -------------------------------------------------------------------------------- /src/ForeignPngFilter.php: -------------------------------------------------------------------------------- 1 | 34 | * @copyright 2016 John Cupitt 35 | * @license https://opensource.org/licenses/MIT MIT 36 | * @link https://github.com/jcupitt/php-vips 37 | */ 38 | 39 | namespace Jcupitt\Vips; 40 | 41 | /** 42 | * The ForeignPngFilter flags. 43 | * @category Images 44 | * @package Jcupitt\Vips 45 | * @author John Cupitt 46 | * @copyright 2016 John Cupitt 47 | * @license https://opensource.org/licenses/MIT MIT 48 | * @link https://github.com/jcupitt/php-vips 49 | */ 50 | abstract class ForeignPngFilter 51 | { 52 | const NONE = 8; 53 | const SUB = 16; 54 | const UP = 32; 55 | const AVG = 64; 56 | const PAETH = 128; 57 | const ALL = 248; 58 | } 59 | -------------------------------------------------------------------------------- /src/ForeignPpmFormat.php: -------------------------------------------------------------------------------- 1 | 34 | * @copyright 2016 John Cupitt 35 | * @license https://opensource.org/licenses/MIT MIT 36 | * @link https://github.com/jcupitt/php-vips 37 | */ 38 | 39 | namespace Jcupitt\Vips; 40 | 41 | /** 42 | * The ForeignPpmFormat enum. 43 | * @category Images 44 | * @package Jcupitt\Vips 45 | * @author John Cupitt 46 | * @copyright 2016 John Cupitt 47 | * @license https://opensource.org/licenses/MIT MIT 48 | * @link https://github.com/jcupitt/php-vips 49 | */ 50 | abstract class ForeignPpmFormat 51 | { 52 | const PBM = 'pbm'; 53 | const PGM = 'pgm'; 54 | const PPM = 'ppm'; 55 | const PFM = 'pfm'; 56 | const PNM = 'pnm'; 57 | } 58 | -------------------------------------------------------------------------------- /src/ForeignSubsample.php: -------------------------------------------------------------------------------- 1 | 34 | * @copyright 2016 John Cupitt 35 | * @license https://opensource.org/licenses/MIT MIT 36 | * @link https://github.com/jcupitt/php-vips 37 | */ 38 | 39 | namespace Jcupitt\Vips; 40 | 41 | /** 42 | * The ForeignSubsample enum. 43 | * @category Images 44 | * @package Jcupitt\Vips 45 | * @author John Cupitt 46 | * @copyright 2016 John Cupitt 47 | * @license https://opensource.org/licenses/MIT MIT 48 | * @link https://github.com/jcupitt/php-vips 49 | */ 50 | abstract class ForeignSubsample 51 | { 52 | const AUTO = 'auto'; 53 | const ON = 'on'; 54 | const OFF = 'off'; 55 | } 56 | -------------------------------------------------------------------------------- /src/ForeignTiffCompression.php: -------------------------------------------------------------------------------- 1 | 34 | * @copyright 2016 John Cupitt 35 | * @license https://opensource.org/licenses/MIT MIT 36 | * @link https://github.com/jcupitt/php-vips 37 | */ 38 | 39 | namespace Jcupitt\Vips; 40 | 41 | /** 42 | * The ForeignTiffCompression enum. 43 | * @category Images 44 | * @package Jcupitt\Vips 45 | * @author John Cupitt 46 | * @copyright 2016 John Cupitt 47 | * @license https://opensource.org/licenses/MIT MIT 48 | * @link https://github.com/jcupitt/php-vips 49 | */ 50 | abstract class ForeignTiffCompression 51 | { 52 | const NONE = 'none'; 53 | const JPEG = 'jpeg'; 54 | const DEFLATE = 'deflate'; 55 | const PACKBITS = 'packbits'; 56 | const CCITTFAX4 = 'ccittfax4'; 57 | const LZW = 'lzw'; 58 | const WEBP = 'webp'; 59 | const ZSTD = 'zstd'; 60 | const JP2K = 'jp2k'; 61 | } 62 | -------------------------------------------------------------------------------- /src/ForeignTiffPredictor.php: -------------------------------------------------------------------------------- 1 | 34 | * @copyright 2016 John Cupitt 35 | * @license https://opensource.org/licenses/MIT MIT 36 | * @link https://github.com/jcupitt/php-vips 37 | */ 38 | 39 | namespace Jcupitt\Vips; 40 | 41 | /** 42 | * The ForeignTiffPredictor enum. 43 | * @category Images 44 | * @package Jcupitt\Vips 45 | * @author John Cupitt 46 | * @copyright 2016 John Cupitt 47 | * @license https://opensource.org/licenses/MIT MIT 48 | * @link https://github.com/jcupitt/php-vips 49 | */ 50 | abstract class ForeignTiffPredictor 51 | { 52 | const NONE = 'none'; 53 | const HORIZONTAL = 'horizontal'; 54 | const FLOAT = 'float'; 55 | } 56 | -------------------------------------------------------------------------------- /src/ForeignTiffResunit.php: -------------------------------------------------------------------------------- 1 | 34 | * @copyright 2016 John Cupitt 35 | * @license https://opensource.org/licenses/MIT MIT 36 | * @link https://github.com/jcupitt/php-vips 37 | */ 38 | 39 | namespace Jcupitt\Vips; 40 | 41 | /** 42 | * The ForeignTiffResunit enum. 43 | * @category Images 44 | * @package Jcupitt\Vips 45 | * @author John Cupitt 46 | * @copyright 2016 John Cupitt 47 | * @license https://opensource.org/licenses/MIT MIT 48 | * @link https://github.com/jcupitt/php-vips 49 | */ 50 | abstract class ForeignTiffResunit 51 | { 52 | const CM = 'cm'; 53 | const INCH = 'inch'; 54 | } 55 | -------------------------------------------------------------------------------- /src/ForeignWebpPreset.php: -------------------------------------------------------------------------------- 1 | 34 | * @copyright 2016 John Cupitt 35 | * @license https://opensource.org/licenses/MIT MIT 36 | * @link https://github.com/jcupitt/php-vips 37 | */ 38 | 39 | namespace Jcupitt\Vips; 40 | 41 | /** 42 | * The ForeignWebpPreset enum. 43 | * @category Images 44 | * @package Jcupitt\Vips 45 | * @author John Cupitt 46 | * @copyright 2016 John Cupitt 47 | * @license https://opensource.org/licenses/MIT MIT 48 | * @link https://github.com/jcupitt/php-vips 49 | */ 50 | abstract class ForeignWebpPreset 51 | { 52 | const DEFAULT1 = 'default'; 53 | const PICTURE = 'picture'; 54 | const PHOTO = 'photo'; 55 | const DRAWING = 'drawing'; 56 | const ICON = 'icon'; 57 | const TEXT = 'text'; 58 | } 59 | -------------------------------------------------------------------------------- /src/GObject.php: -------------------------------------------------------------------------------- 1 | 34 | * @copyright 2016 John Cupitt 35 | * @license https://opensource.org/licenses/MIT MIT 36 | * @link https://github.com/libvips/php-vips 37 | */ 38 | 39 | namespace Jcupitt\Vips; 40 | 41 | use Closure; 42 | use FFI\CData; 43 | 44 | /** 45 | * This class holds a pointer to a GObject and manages object lifetime. 46 | * 47 | * @category Images 48 | * @package Jcupitt\Vips 49 | * @author John Cupitt 50 | * @copyright 2016 John Cupitt 51 | * @license https://opensource.org/licenses/MIT MIT 52 | * @link https://github.com/libvips/php-vips 53 | */ 54 | abstract class GObject 55 | { 56 | /** 57 | * A pointer to the underlying GObject. 58 | * 59 | * @internal 60 | */ 61 | private CData $pointer; 62 | 63 | /** 64 | * libvips executes FFI callbacks off the main thread and this confuses 65 | * the stack limit checks available since PHP 8.3.0. We need to check 66 | * if `zend.max_allowed_stack_size` is set to `-1`. 67 | * See: https://github.com/libvips/php-vips/pull/237. 68 | */ 69 | private static bool $check_max_stack_size = true; 70 | 71 | /** 72 | * Wrap a GObject around an underlying vips resource. The GObject takes 73 | * ownership of the pointer and will unref it on finalize. 74 | * 75 | * Don't call this yourself, users should stick to (for example) 76 | * Image::newFromFile(). 77 | * 78 | * @param CData $pointer The underlying pointer that this 79 | * object should wrap. 80 | * 81 | * @internal 82 | */ 83 | public function __construct(CData $pointer) 84 | { 85 | $this->pointer = FFI::vips()->cast(FFI::ctypes("GObject"), $pointer); 86 | } 87 | 88 | public function __destruct() 89 | { 90 | $this->unref(); 91 | } 92 | 93 | public function __clone() 94 | { 95 | $this->ref(); 96 | } 97 | 98 | public function ref(): void 99 | { 100 | FFI::gobject()->g_object_ref($this->pointer); 101 | } 102 | 103 | public function unref(): void 104 | { 105 | FFI::gobject()->g_object_unref($this->pointer); 106 | } 107 | 108 | /** 109 | * Connect to a signal on this object. 110 | * The callback will be triggered every time this signal is issued on this instance. 111 | * @throws Exception 112 | */ 113 | public function signalConnect(string $name, callable $callback): void 114 | { 115 | if (self::$check_max_stack_size) { 116 | $max_allowed_stack_size = ini_get('zend.max_allowed_stack_size'); 117 | if ($max_allowed_stack_size !== false && 118 | $max_allowed_stack_size !== '-1') { 119 | throw new Exception("signalConnect() requires zend.max_allowed_stack_size set to '-1'"); 120 | } 121 | 122 | self::$check_max_stack_size = false; 123 | } 124 | 125 | $marshaler = self::getMarshaler($name, $callback); 126 | if ($marshaler === null) { 127 | throw new Exception("unsupported signal $name"); 128 | } 129 | 130 | $gc = FFI::newGClosure(); 131 | FFI::gobject()->g_closure_set_marshal($gc, $marshaler); 132 | FFI::gobject()->g_signal_connect_closure($this->pointer, $name, $gc, 0); 133 | } 134 | 135 | private static function getMarshaler(string $name, callable $callback): ?Closure 136 | { 137 | switch ($name) { 138 | case 'preeval': 139 | case 'eval': 140 | case 'posteval': 141 | return static function ( 142 | CData $gClosure, 143 | ?CData $returnValue, 144 | int $numberOfParams, 145 | CData $params, 146 | CData $hint, 147 | ?CData $data 148 | ) use (&$callback) { 149 | assert($numberOfParams === 2); 150 | /** 151 | * Signature: void(VipsImage* image, void* progress, void* handle) 152 | */ 153 | $vi = FFI::gobject()->g_value_get_object(\FFI::addr($params[0])); 154 | FFI::gobject()->g_object_ref($vi); 155 | $image = new Image($vi); 156 | $pr = FFI::vips()->cast( 157 | FFI::ctypes('VipsProgress'), 158 | FFI::gobject()->g_value_get_pointer(\FFI::addr($params[1])) 159 | ); 160 | $callback($image, $pr); 161 | }; 162 | case 'read': 163 | if (FFI::atLeast(8, 9)) { 164 | return static function ( 165 | CData $gClosure, 166 | CData $returnValue, 167 | int $numberOfParams, 168 | CData $params, 169 | CData $hint, 170 | ?CData $data 171 | ) use (&$callback): void { 172 | assert($numberOfParams === 3); 173 | /** 174 | * Signature: gint64(VipsSourceCustom* source, void* buffer, gint64 length, void* handle) 175 | */ 176 | $bufferLength = (int)FFI::gobject()->g_value_get_int64(\FFI::addr($params[2])); 177 | $returnBuffer = $callback($bufferLength); 178 | $returnBufferLength = 0; 179 | 180 | if ($returnBuffer !== null) { 181 | $returnBufferLength = strlen($returnBuffer); 182 | $bufferPointer = FFI::gobject()->g_value_get_pointer(\FFI::addr($params[1])); 183 | \FFI::memcpy($bufferPointer, $returnBuffer, $returnBufferLength); 184 | } 185 | FFI::gobject()->g_value_set_int64($returnValue, $returnBufferLength); 186 | }; 187 | } 188 | 189 | return null; 190 | case 'seek': 191 | if (FFI::atLeast(8, 9)) { 192 | return static function ( 193 | CData $gClosure, 194 | CData $returnValue, 195 | int $numberOfParams, 196 | CData $params, 197 | CData $hint, 198 | ?CData $data 199 | ) use (&$callback): void { 200 | assert($numberOfParams === 3); 201 | /** 202 | * Signature: gint64(VipsSourceCustom* source, gint64 offset, int whence, void* handle) 203 | */ 204 | $offset = (int)FFI::gobject()->g_value_get_int64(\FFI::addr($params[1])); 205 | $whence = (int)FFI::gobject()->g_value_get_int(\FFI::addr($params[2])); 206 | FFI::gobject()->g_value_set_int64($returnValue, $callback($offset, $whence)); 207 | }; 208 | } 209 | 210 | return null; 211 | case 'write': 212 | if (FFI::atLeast(8, 9)) { 213 | return static function ( 214 | CData $gClosure, 215 | CData $returnValue, 216 | int $numberOfParams, 217 | CData $params, 218 | CData $hint, 219 | ?CData $data 220 | ) use (&$callback): void { 221 | assert($numberOfParams === 3); 222 | /** 223 | * Signature: gint64(VipsTargetCustom* target, void* buffer, gint64 length, void* handle) 224 | */ 225 | $bufferPointer = FFI::gobject()->g_value_get_pointer(\FFI::addr($params[1])); 226 | $bufferLength = (int)FFI::gobject()->g_value_get_int64(\FFI::addr($params[2])); 227 | $buffer = \FFI::string($bufferPointer, $bufferLength); 228 | $returnBufferLength = $callback($buffer); 229 | FFI::gobject()->g_value_set_int64($returnValue, $returnBufferLength); 230 | }; 231 | } 232 | 233 | return null; 234 | case 'finish': 235 | if (FFI::atLeast(8, 9)) { 236 | return static function ( 237 | CData $gClosure, 238 | ?CData $returnValue, 239 | int $numberOfParams, 240 | CData $params, 241 | CData $hint, 242 | ?CData $data 243 | ) use (&$callback): void { 244 | assert($numberOfParams === 1); 245 | /** 246 | * Signature: void(VipsTargetCustom* target, void* handle) 247 | */ 248 | $callback(); 249 | }; 250 | } 251 | 252 | return null; 253 | case 'end': 254 | if (FFI::atLeast(8, 13)) { 255 | return static function ( 256 | CData $gClosure, 257 | CData $returnValue, 258 | int $numberOfParams, 259 | CData $params, 260 | CData $hint, 261 | ?CData $data 262 | ) use (&$callback): void { 263 | assert($numberOfParams === 1); 264 | /** 265 | * Signature: int(VipsTargetCustom* target, void* handle) 266 | */ 267 | FFI::gobject()->g_value_set_int($returnValue, $callback()); 268 | }; 269 | } 270 | 271 | return null; 272 | default: 273 | return null; 274 | } 275 | } 276 | } 277 | 278 | /* 279 | * Local variables: 280 | * tab-width: 4 281 | * c-basic-offset: 4 282 | * End: 283 | * vim600: expandtab sw=4 ts=4 fdm=marker 284 | * vim<600: expandtab sw=4 ts=4 285 | */ 286 | -------------------------------------------------------------------------------- /src/GValue.php: -------------------------------------------------------------------------------- 1 | 34 | * @copyright 2016 John Cupitt 35 | * @license https://opensource.org/licenses/MIT MIT 36 | * @link https://github.com/libvips/php-vips 37 | */ 38 | 39 | namespace Jcupitt\Vips; 40 | 41 | class GValue 42 | { 43 | private \FFI\CData $struct; 44 | public \FFI\CData $pointer; 45 | 46 | public function __construct() 47 | { 48 | # allocate a gvalue on the heap, and make it persistent between requests 49 | $this->struct = FFI::gobject()->new("GValue", true, true); 50 | $this->pointer = \FFI::addr($this->struct); 51 | 52 | # GValue needs to be inited to all zero 53 | \FFI::memset($this->pointer, 0, \FFI::sizeof($this->struct)); 54 | } 55 | 56 | /** 57 | * Turn a string into an enum value, if possible 58 | * @throws Exception 59 | */ 60 | public static function toEnum(int $gtype, $value): int 61 | { 62 | if (is_string($value)) { 63 | $enum_value = FFI::vips()-> 64 | vips_enum_from_nick("php-vips", $gtype, $value); 65 | if ($enum_value < 0) { 66 | throw new Exception(); 67 | } 68 | } else { 69 | $enum_value = $value; 70 | } 71 | 72 | return $enum_value; 73 | } 74 | 75 | /** 76 | * Turn an enum into a string, if possible 77 | * @throws Exception 78 | */ 79 | public static function fromEnum(int $gtype, int $value): string 80 | { 81 | $result = FFI::vips()->vips_enum_nick($gtype, $value); 82 | if ($result === null) { 83 | throw new Exception("value not in enum"); 84 | } 85 | 86 | return $result; 87 | } 88 | 89 | public function __destruct() 90 | { 91 | FFI::gobject()->g_value_unset($this->pointer); 92 | } 93 | 94 | public function setType(int $gtype): void 95 | { 96 | FFI::gobject()->g_value_init($this->pointer, $gtype); 97 | } 98 | 99 | public function getType(): int 100 | { 101 | return $this->pointer->g_type; 102 | } 103 | 104 | /** 105 | * Set a GValue. 106 | * 107 | * @param mixed $value Value to be set. 108 | * 109 | * @throws Exception 110 | */ 111 | public function set($value): void 112 | { 113 | $gtype = $this->getType(); 114 | 115 | switch ($gtype) { 116 | case FFI::gtypes("gboolean"): 117 | FFI::gobject()->g_value_set_boolean($this->pointer, $value); 118 | break; 119 | 120 | case FFI::gtypes("gint"): 121 | FFI::gobject()->g_value_set_int($this->pointer, $value); 122 | break; 123 | 124 | case FFI::gtypes("gint64"): 125 | FFI::gobject()->g_value_set_int64($this->pointer, $value); 126 | break; 127 | 128 | case FFI::gtypes("guint64"): 129 | FFI::gobject()->g_value_set_uint64($this->pointer, $value); 130 | break; 131 | 132 | case FFI::gtypes("gdouble"): 133 | FFI::gobject()->g_value_set_double($this->pointer, $value); 134 | break; 135 | 136 | case FFI::gtypes("gchararray"): 137 | FFI::gobject()->g_value_set_string($this->pointer, $value); 138 | break; 139 | 140 | case FFI::gtypes("VipsRefString"): 141 | FFI::vips()-> 142 | vips_value_set_ref_string($this->pointer, $value); 143 | break; 144 | 145 | case FFI::gtypes("VipsArrayInt"): 146 | if (!is_array($value)) { 147 | $value = [$value]; 148 | } 149 | $n = count($value); 150 | $array = FFI::vips()->new("int[$n]"); 151 | for ($i = 0; $i < $n; $i++) { 152 | $array[$i] = $value[$i]; 153 | } 154 | FFI::vips()-> 155 | vips_value_set_array_int($this->pointer, $array, $n); 156 | break; 157 | 158 | case FFI::gtypes("VipsArrayDouble"): 159 | if (!is_array($value)) { 160 | $value = [$value]; 161 | } 162 | $n = count($value); 163 | $array = FFI::vips()->new("double[$n]"); 164 | for ($i = 0; $i < $n; $i++) { 165 | $array[$i] = $value[$i]; 166 | } 167 | FFI::vips()-> 168 | vips_value_set_array_double($this->pointer, $array, $n); 169 | break; 170 | 171 | case FFI::gtypes("VipsArrayImage"): 172 | if (!is_array($value)) { 173 | $value = [$value]; 174 | } 175 | $n = count($value); 176 | FFI::vips()->vips_value_set_array_image($this->pointer, $n); 177 | $array = FFI::vips()-> 178 | vips_value_get_array_image($this->pointer, null); 179 | for ($i = 0; $i < $n; $i++) { 180 | $image = $value[$i]; 181 | $array[$i] = $image->pointer; 182 | $image->ref(); 183 | } 184 | break; 185 | 186 | case FFI::gtypes("VipsBlob"): 187 | # we need to set the blob to a copy of the data that vips_lib 188 | # can own and free 189 | $n = strlen($value); 190 | $memory = FFI::vips()->new("char[$n]", false, true); 191 | \FFI::memcpy($memory, $value, $n); 192 | FFI::vips()-> 193 | vips_value_set_blob_free($this->pointer, $memory, $n); 194 | break; 195 | 196 | default: 197 | $fundamental = FFI::gobject()->g_type_fundamental($gtype); 198 | switch ($fundamental) { 199 | case FFI::gtypes("GObject"): 200 | FFI::gobject()-> 201 | g_value_set_object($this->pointer, $value->pointer); 202 | break; 203 | 204 | case FFI::gtypes("GEnum"): 205 | FFI::gobject()->g_value_set_enum( 206 | $this->pointer, 207 | self::toEnum($gtype, $value) 208 | ); 209 | break; 210 | 211 | case FFI::gtypes("GFlags"): 212 | /* Just set as int. 213 | */ 214 | FFI::gobject()-> 215 | g_value_set_flags($this->pointer, $value); 216 | break; 217 | 218 | default: 219 | $typeName = FFI::gobject()->g_type_name($gtype); 220 | throw new \BadMethodCallException( 221 | "gtype $typeName ($gtype) not implemented" 222 | ); 223 | } 224 | } 225 | } 226 | 227 | /** 228 | * Get the contents of a GValue. 229 | * 230 | * @return mixed The contents of this GValue. 231 | * 232 | * @throws Exception 233 | */ 234 | public function get() 235 | { 236 | $gtype = $this->getType(); 237 | $result = null; 238 | 239 | switch ($gtype) { 240 | case FFI::gtypes("gboolean"): 241 | $result = FFI::gobject()->g_value_get_boolean($this->pointer); 242 | break; 243 | 244 | case FFI::gtypes("gint"): 245 | $result = FFI::gobject()->g_value_get_int($this->pointer); 246 | break; 247 | 248 | case FFI::gtypes("gint64"): 249 | $result = FFI::gobject()->g_value_get_int64($this->pointer); 250 | break; 251 | 252 | case FFI::gtypes("guint64"): 253 | $result = FFI::gobject()->g_value_get_uint64($this->pointer); 254 | break; 255 | 256 | case FFI::gtypes("gdouble"): 257 | $result = FFI::gobject()->g_value_get_double($this->pointer); 258 | break; 259 | 260 | case FFI::gtypes("gchararray"): 261 | $result = FFI::gobject()->g_value_get_string($this->pointer); 262 | break; 263 | 264 | case FFI::gtypes("VipsRefString"): 265 | $p_size = FFI::vips()->new("size_t[1]"); 266 | $result = FFI::vips()-> 267 | vips_value_get_ref_string($this->pointer, $p_size); 268 | # $p_size[0] will be the string length, but assume it's null 269 | # terminated 270 | break; 271 | 272 | case FFI::gtypes("VipsImage"): 273 | $pointer = FFI::gobject()->g_value_get_object($this->pointer); 274 | $result = new Image($pointer); 275 | // get_object does not increment the ref count 276 | $result->ref(); 277 | break; 278 | 279 | case FFI::gtypes("VipsArrayInt"): 280 | $p_len = FFI::vips()->new("int[1]"); 281 | $pointer = FFI::vips()-> 282 | vips_value_get_array_int($this->pointer, $p_len); 283 | $result = []; 284 | for ($i = 0; $i < $p_len[0]; $i++) { 285 | $result[] = $pointer[$i]; 286 | } 287 | break; 288 | 289 | case FFI::gtypes("VipsArrayDouble"): 290 | $p_len = FFI::vips()->new("int[1]"); 291 | $pointer = FFI::vips()-> 292 | vips_value_get_array_double($this->pointer, $p_len); 293 | $result = []; 294 | for ($i = 0; $i < $p_len[0]; $i++) { 295 | $result[] = $pointer[$i]; 296 | } 297 | break; 298 | 299 | case FFI::gtypes("VipsArrayImage"): 300 | $p_len = FFI::vips()->new("int[1]"); 301 | $pointer = FFI::vips()-> 302 | vips_value_get_array_image($this->pointer, $p_len); 303 | $result = []; 304 | for ($i = 0; $i < $p_len[0]; $i++) { 305 | $image = new Image($pointer[$i]); 306 | $image->ref(); 307 | $result[] = $image; 308 | } 309 | break; 310 | 311 | case FFI::gtypes("VipsBlob"): 312 | $p_len = FFI::vips()->new("size_t[1]"); 313 | $pointer = FFI::vips()-> 314 | vips_value_get_blob($this->pointer, $p_len); 315 | $result = \FFI::string($pointer, $p_len[0]); 316 | break; 317 | 318 | default: 319 | $fundamental = FFI::gobject()->g_type_fundamental($gtype); 320 | switch ($fundamental) { 321 | case FFI::gtypes("GEnum"): 322 | $result = FFI::gobject()-> 323 | g_value_get_enum($this->pointer); 324 | $result = self::fromEnum($gtype, $result); 325 | break; 326 | 327 | case FFI::gtypes("GFlags"): 328 | /* Just get as int. 329 | */ 330 | $result = FFI::gobject()-> 331 | g_value_get_flags($this->pointer); 332 | break; 333 | 334 | default: 335 | $typeName = FFI::gobject()->g_type_name($gtype); 336 | throw new \BadMethodCallException( 337 | "gtype $typeName ($gtype) not implemented" 338 | ); 339 | } 340 | } 341 | 342 | return $result; 343 | } 344 | } 345 | 346 | /* 347 | * Local variables: 348 | * tab-width: 4 349 | * c-basic-offset: 4 350 | * End: 351 | * vim600: expandtab sw=4 ts=4 fdm=marker 352 | * vim<600: expandtab sw=4 ts=4 353 | */ 354 | -------------------------------------------------------------------------------- /src/Intent.php: -------------------------------------------------------------------------------- 1 | 34 | * @copyright 2016 John Cupitt 35 | * @license https://opensource.org/licenses/MIT MIT 36 | * @link https://github.com/jcupitt/php-vips 37 | */ 38 | 39 | namespace Jcupitt\Vips; 40 | 41 | /** 42 | * The Intent enum. 43 | * @category Images 44 | * @package Jcupitt\Vips 45 | * @author John Cupitt 46 | * @copyright 2016 John Cupitt 47 | * @license https://opensource.org/licenses/MIT MIT 48 | * @link https://github.com/jcupitt/php-vips 49 | */ 50 | abstract class Intent 51 | { 52 | const PERCEPTUAL = 'perceptual'; 53 | const RELATIVE = 'relative'; 54 | const SATURATION = 'saturation'; 55 | const ABSOLUTE = 'absolute'; 56 | const AUTO = 'auto'; 57 | } 58 | -------------------------------------------------------------------------------- /src/Interesting.php: -------------------------------------------------------------------------------- 1 | 34 | * @copyright 2016 John Cupitt 35 | * @license https://opensource.org/licenses/MIT MIT 36 | * @link https://github.com/jcupitt/php-vips 37 | */ 38 | 39 | namespace Jcupitt\Vips; 40 | 41 | /** 42 | * The Interesting enum. 43 | * @category Images 44 | * @package Jcupitt\Vips 45 | * @author John Cupitt 46 | * @copyright 2016 John Cupitt 47 | * @license https://opensource.org/licenses/MIT MIT 48 | * @link https://github.com/jcupitt/php-vips 49 | */ 50 | abstract class Interesting 51 | { 52 | const NONE = 'none'; 53 | const CENTRE = 'centre'; 54 | const ENTROPY = 'entropy'; 55 | const ATTENTION = 'attention'; 56 | const LOW = 'low'; 57 | const HIGH = 'high'; 58 | const ALL = 'all'; 59 | } 60 | -------------------------------------------------------------------------------- /src/Interpolate.php: -------------------------------------------------------------------------------- 1 | 34 | * @copyright 2016 John Cupitt 35 | * @license https://opensource.org/licenses/MIT MIT 36 | * @link https://github.com/libvips/php-vips 37 | */ 38 | 39 | namespace Jcupitt\Vips; 40 | 41 | /** 42 | * This class holds a pointer to a VipsInterpolate (the libvips 43 | * base class for interpolators) and manages argument introspection and 44 | * operation call. 45 | * 46 | * @category Images 47 | * @package Jcupitt\Vips 48 | * @author John Cupitt 49 | * @copyright 2016 John Cupitt 50 | * @license https://opensource.org/licenses/MIT MIT 51 | * @link https://github.com/libvips/php-vips 52 | */ 53 | class Interpolate extends VipsObject 54 | { 55 | /** 56 | * A pointer to the underlying Interpolate. This is the same as the 57 | * GObject, just cast to Interpolate to help FFI. 58 | * 59 | * @internal 60 | */ 61 | public \FFI\CData $pointer; 62 | 63 | public function __construct(\FFI\CData $pointer) 64 | { 65 | $this->pointer = FFI::vips()->cast(FFI::ctypes("VipsInterpolate"), $pointer); 66 | 67 | parent::__construct($pointer); 68 | } 69 | 70 | /** 71 | * Make an interpolator from a name. 72 | * 73 | * @param string $name Name of the interpolator. 74 | * 75 | * Possible interpolators are: 76 | * - `'nearest'`: Use nearest neighbour interpolation. 77 | * - `'bicubic'`: Use bicubic interpolation. 78 | * - `'bilinear'`: Use bilinear interpolation (the default). 79 | * - `'nohalo'`: Use Nohalo interpolation. 80 | * - `'lbb'`: Use LBB interpolation. 81 | * - `'vsqbs'`: Use the VSQBS interpolation. 82 | * 83 | * @return Interpolate The interpolator. 84 | * @throws Exception If unable to make a new interpolator from $name. 85 | */ 86 | public static function newFromName(string $name): Interpolate 87 | { 88 | $pointer = FFI::vips()->vips_interpolate_new($name); 89 | if ($pointer == null) { 90 | throw new Exception(); 91 | } 92 | 93 | return new Interpolate($pointer); 94 | } 95 | } 96 | 97 | /* 98 | * Local variables: 99 | * tab-width: 4 100 | * c-basic-offset: 4 101 | * End: 102 | * vim600: expandtab sw=4 ts=4 fdm=marker 103 | * vim<600: expandtab sw=4 ts=4 104 | */ 105 | -------------------------------------------------------------------------------- /src/Interpretation.php: -------------------------------------------------------------------------------- 1 | 34 | * @copyright 2016 John Cupitt 35 | * @license https://opensource.org/licenses/MIT MIT 36 | * @link https://github.com/jcupitt/php-vips 37 | */ 38 | 39 | namespace Jcupitt\Vips; 40 | 41 | /** 42 | * The Interpretation enum. 43 | * @category Images 44 | * @package Jcupitt\Vips 45 | * @author John Cupitt 46 | * @copyright 2016 John Cupitt 47 | * @license https://opensource.org/licenses/MIT MIT 48 | * @link https://github.com/jcupitt/php-vips 49 | */ 50 | abstract class Interpretation 51 | { 52 | const ERROR = 'error'; 53 | const MULTIBAND = 'multiband'; 54 | const B_W = 'b-w'; 55 | const HISTOGRAM = 'histogram'; 56 | const XYZ = 'xyz'; 57 | const LAB = 'lab'; 58 | const CMYK = 'cmyk'; 59 | const LABQ = 'labq'; 60 | const RGB = 'rgb'; 61 | const CMC = 'cmc'; 62 | const LCH = 'lch'; 63 | const LABS = 'labs'; 64 | const SRGB = 'srgb'; 65 | const YXY = 'yxy'; 66 | const FOURIER = 'fourier'; 67 | const RGB16 = 'rgb16'; 68 | const GREY16 = 'grey16'; 69 | const MATRIX = 'matrix'; 70 | const SCRGB = 'scrgb'; 71 | const HSV = 'hsv'; 72 | } 73 | -------------------------------------------------------------------------------- /src/Introspect.php: -------------------------------------------------------------------------------- 1 | 34 | * @copyright 2016 John Cupitt 35 | * @license https://opensource.org/licenses/MIT MIT 36 | * @link https://github.com/libvips/php-vips 37 | */ 38 | 39 | namespace Jcupitt\Vips; 40 | 41 | /** 42 | * Introspect a VipsOperation and discover everything we can. This is called 43 | * on demand once per operation and the results held in a cache. 44 | * 45 | * @category Images 46 | * @package Jcupitt\Vips 47 | * @author John Cupitt 48 | * @copyright 2016 John Cupitt 49 | * @license https://opensource.org/licenses/MIT MIT 50 | * @link https://github.com/libvips/php-vips 51 | */ 52 | class Introspect 53 | { 54 | /** 55 | * The operation nickname (eg. "add"). 56 | */ 57 | public string $name; 58 | 59 | /** 60 | * The operation description (e.g. "add two images"). 61 | */ 62 | public string $description; 63 | 64 | /** 65 | * The operation flags (e.g. SEQUENTIAL | DEPRECATED). 66 | */ 67 | public int $flags; 68 | 69 | /** 70 | * A hash from arg name to a hash of details. 71 | */ 72 | public array $arguments; 73 | 74 | /** 75 | * Arrays of arg names, in order and by category, eg. $this->required_input 76 | * = ["filename"]. 77 | */ 78 | public array $required_input; 79 | public array $optional_input; 80 | public array $required_output; 81 | public array $optional_output; 82 | 83 | /** 84 | * The name of the arg this operation uses as "this". 85 | */ 86 | public string $member_this; 87 | 88 | /** 89 | * And the required input args, without the "this". 90 | */ 91 | public array $method_args; 92 | 93 | /** 94 | * @throws Exception 95 | */ 96 | public function __construct($operation_name) 97 | { 98 | $this->name = $operation_name; 99 | 100 | $operation = VipsOperation::newFromName($operation_name); 101 | 102 | $this->description = $operation->getDescription(); 103 | 104 | $p_names = FFI::vips()->new("char**[1]"); 105 | $p_flags = FFI::vips()->new("int*[1]"); 106 | $p_n_args = FFI::vips()->new("int[1]"); 107 | $result = FFI::vips()->vips_object_get_args( 108 | FFI::vips()->cast(FFI::ctypes("VipsObject"), $operation->pointer), 109 | $p_names, 110 | $p_flags, 111 | $p_n_args 112 | ); 113 | if ($result != 0) { 114 | throw new Exception(); 115 | } 116 | $p_names = $p_names[0]; 117 | $p_flags = $p_flags[0]; 118 | $n_args = $p_n_args[0]; 119 | 120 | # make a hash from arg name to flags 121 | $argumentFlags = []; 122 | for ($i = 0; $i < $n_args; $i++) { 123 | if (($p_flags[$i] & ArgumentFlags::CONSTRUCT) != 0) { 124 | # make sure we're using "_" to separate arg components, though 125 | # I think libvips is "_" everywhere now 126 | $name = \FFI::string($p_names[$i]); 127 | $name = str_replace("-", "_", $name); 128 | $argumentFlags[$name] = $p_flags[$i]; 129 | } 130 | } 131 | 132 | # make a hash from arg name to detailed arg info 133 | $this->arguments = []; 134 | foreach ($argumentFlags as $name => $flags) { 135 | $this->arguments[$name] = [ 136 | "name" => $name, 137 | "flags" => $flags, 138 | "blurb" => $operation->getBlurb($name), 139 | "type" => $operation->getType($name) 140 | ]; 141 | } 142 | 143 | # split args into categories 144 | $this->required_input = []; 145 | $this->optional_input = []; 146 | $this->required_output = []; 147 | $this->optional_output = []; 148 | 149 | foreach ($this->arguments as $name => $details) { 150 | $flags = $details["flags"]; 151 | 152 | if (($flags & ArgumentFlags::INPUT) && 153 | ($flags & ArgumentFlags::REQUIRED) && 154 | !($flags & ArgumentFlags::DEPRECATED)) { 155 | $this->required_input[] = $name; 156 | 157 | # required inputs which we MODIFY are also required outputs 158 | if ($flags & ArgumentFlags::MODIFY) { 159 | $this->required_output[] = $name; 160 | } 161 | } 162 | 163 | if (($flags & ArgumentFlags::OUTPUT) && 164 | ($flags & ArgumentFlags::REQUIRED) && 165 | !($flags & ArgumentFlags::DEPRECATED)) { 166 | $this->required_output[] = $name; 167 | } 168 | 169 | # we let deprecated optional args through, but warn about them 170 | # if they get used, see below 171 | if (($flags & ArgumentFlags::INPUT) && 172 | !($flags & ArgumentFlags::REQUIRED)) { 173 | $this->optional_input[] = $name; 174 | } 175 | 176 | if (($flags & ArgumentFlags::OUTPUT) && 177 | !($flags & ArgumentFlags::REQUIRED)) { 178 | $this->optional_output[] = $name; 179 | } 180 | } 181 | 182 | # find the first required input image arg, if any ... that will be self 183 | $this->member_this = ""; 184 | foreach ($this->required_input as $name) { 185 | $type = $this->arguments[$name]["type"]; 186 | if ($type == FFI::gtypes("VipsImage")) { 187 | $this->member_this = $name; 188 | break; 189 | } 190 | } 191 | 192 | # method args are required args, but without the image they are a 193 | # method on 194 | $this->method_args = $this->required_input; 195 | if ($this->member_this != "") { 196 | $index = array_search($this->member_this, $this->method_args); 197 | array_splice($this->method_args, $index); 198 | } 199 | 200 | Utils::debugLog($operation_name, ['introspect' => strval($this)]); 201 | } 202 | 203 | public function __toString(): string 204 | { 205 | $result = "$this->name:\n"; 206 | 207 | foreach ($this->arguments as $name => $details) { 208 | $flags = $details["flags"]; 209 | $blurb = $details["blurb"]; 210 | $type = $details["type"]; 211 | $typeName = FFI::gobject()->g_type_name($type); 212 | 213 | $result .= " $name:\n"; 214 | 215 | $result .= " flags: $flags\n"; 216 | foreach (ArgumentFlags::NAMES as $flag_name => $flag) { 217 | if ($flags & $flag) { 218 | $result .= " $flag_name\n"; 219 | } 220 | } 221 | 222 | $result .= " blurb: $blurb\n"; 223 | $result .= " type: $typeName\n"; 224 | } 225 | 226 | $info = implode(", ", $this->required_input); 227 | $result .= "required input: $info\n"; 228 | $info = implode(", ", $this->required_output); 229 | $result .= "required output: $info\n"; 230 | $info = implode(", ", $this->optional_input); 231 | $result .= "optional input: $info\n"; 232 | $info = implode(", ", $this->optional_output); 233 | $result .= "optional output: $info\n"; 234 | $result .= "member_this: $this->member_this\n"; 235 | $info = implode(", ", $this->method_args); 236 | $result .= "method args: $info\n"; 237 | 238 | return $result; 239 | } 240 | } 241 | 242 | /* 243 | * Local variables: 244 | * tab-width: 4 245 | * c-basic-offset: 4 246 | * End: 247 | * vim600: expandtab sw=4 ts=4 fdm=marker 248 | * vim<600: expandtab sw=4 ts=4 249 | */ 250 | -------------------------------------------------------------------------------- /src/Kernel.php: -------------------------------------------------------------------------------- 1 | 34 | * @copyright 2016 John Cupitt 35 | * @license https://opensource.org/licenses/MIT MIT 36 | * @link https://github.com/jcupitt/php-vips 37 | */ 38 | 39 | namespace Jcupitt\Vips; 40 | 41 | /** 42 | * The Kernel enum. 43 | * @category Images 44 | * @package Jcupitt\Vips 45 | * @author John Cupitt 46 | * @copyright 2016 John Cupitt 47 | * @license https://opensource.org/licenses/MIT MIT 48 | * @link https://github.com/jcupitt/php-vips 49 | */ 50 | abstract class Kernel 51 | { 52 | const NEAREST = 'nearest'; 53 | const LINEAR = 'linear'; 54 | const CUBIC = 'cubic'; 55 | const MITCHELL = 'mitchell'; 56 | const LANCZOS2 = 'lanczos2'; 57 | const LANCZOS3 = 'lanczos3'; 58 | const MKS2013 = 'mks2013'; 59 | const MKS2021 = 'mks2021'; 60 | } 61 | -------------------------------------------------------------------------------- /src/OperationBoolean.php: -------------------------------------------------------------------------------- 1 | 34 | * @copyright 2016 John Cupitt 35 | * @license https://opensource.org/licenses/MIT MIT 36 | * @link https://github.com/jcupitt/php-vips 37 | */ 38 | 39 | namespace Jcupitt\Vips; 40 | 41 | /** 42 | * The OperationBoolean enum. 43 | * @category Images 44 | * @package Jcupitt\Vips 45 | * @author John Cupitt 46 | * @copyright 2016 John Cupitt 47 | * @license https://opensource.org/licenses/MIT MIT 48 | * @link https://github.com/jcupitt/php-vips 49 | */ 50 | abstract class OperationBoolean 51 | { 52 | const AND1 = 'and'; 53 | const OR1 = 'or'; 54 | const EOR = 'eor'; 55 | const LSHIFT = 'lshift'; 56 | const RSHIFT = 'rshift'; 57 | } 58 | -------------------------------------------------------------------------------- /src/OperationComplex.php: -------------------------------------------------------------------------------- 1 | 34 | * @copyright 2016 John Cupitt 35 | * @license https://opensource.org/licenses/MIT MIT 36 | * @link https://github.com/jcupitt/php-vips 37 | */ 38 | 39 | namespace Jcupitt\Vips; 40 | 41 | /** 42 | * The OperationComplex enum. 43 | * @category Images 44 | * @package Jcupitt\Vips 45 | * @author John Cupitt 46 | * @copyright 2016 John Cupitt 47 | * @license https://opensource.org/licenses/MIT MIT 48 | * @link https://github.com/jcupitt/php-vips 49 | */ 50 | abstract class OperationComplex 51 | { 52 | const POLAR = 'polar'; 53 | const RECT = 'rect'; 54 | const CONJ = 'conj'; 55 | } 56 | -------------------------------------------------------------------------------- /src/OperationComplex2.php: -------------------------------------------------------------------------------- 1 | 34 | * @copyright 2016 John Cupitt 35 | * @license https://opensource.org/licenses/MIT MIT 36 | * @link https://github.com/jcupitt/php-vips 37 | */ 38 | 39 | namespace Jcupitt\Vips; 40 | 41 | /** 42 | * The OperationComplex2 enum. 43 | * @category Images 44 | * @package Jcupitt\Vips 45 | * @author John Cupitt 46 | * @copyright 2016 John Cupitt 47 | * @license https://opensource.org/licenses/MIT MIT 48 | * @link https://github.com/jcupitt/php-vips 49 | */ 50 | abstract class OperationComplex2 51 | { 52 | const CROSS_PHASE = 'cross-phase'; 53 | } 54 | -------------------------------------------------------------------------------- /src/OperationComplexget.php: -------------------------------------------------------------------------------- 1 | 34 | * @copyright 2016 John Cupitt 35 | * @license https://opensource.org/licenses/MIT MIT 36 | * @link https://github.com/jcupitt/php-vips 37 | */ 38 | 39 | namespace Jcupitt\Vips; 40 | 41 | /** 42 | * The OperationComplexget enum. 43 | * @category Images 44 | * @package Jcupitt\Vips 45 | * @author John Cupitt 46 | * @copyright 2016 John Cupitt 47 | * @license https://opensource.org/licenses/MIT MIT 48 | * @link https://github.com/jcupitt/php-vips 49 | */ 50 | abstract class OperationComplexget 51 | { 52 | const REAL = 'real'; 53 | const IMAG = 'imag'; 54 | } 55 | -------------------------------------------------------------------------------- /src/OperationMath.php: -------------------------------------------------------------------------------- 1 | 34 | * @copyright 2016 John Cupitt 35 | * @license https://opensource.org/licenses/MIT MIT 36 | * @link https://github.com/jcupitt/php-vips 37 | */ 38 | 39 | namespace Jcupitt\Vips; 40 | 41 | /** 42 | * The OperationMath enum. 43 | * @category Images 44 | * @package Jcupitt\Vips 45 | * @author John Cupitt 46 | * @copyright 2016 John Cupitt 47 | * @license https://opensource.org/licenses/MIT MIT 48 | * @link https://github.com/jcupitt/php-vips 49 | */ 50 | abstract class OperationMath 51 | { 52 | const SIN = 'sin'; 53 | const COS = 'cos'; 54 | const TAN = 'tan'; 55 | const ASIN = 'asin'; 56 | const ACOS = 'acos'; 57 | const ATAN = 'atan'; 58 | const LOG = 'log'; 59 | const LOG10 = 'log10'; 60 | const EXP = 'exp'; 61 | const EXP10 = 'exp10'; 62 | const SINH = 'sinh'; 63 | const COSH = 'cosh'; 64 | const TANH = 'tanh'; 65 | const ASINH = 'asinh'; 66 | const ACOSH = 'acosh'; 67 | const ATANH = 'atanh'; 68 | } 69 | -------------------------------------------------------------------------------- /src/OperationMath2.php: -------------------------------------------------------------------------------- 1 | 34 | * @copyright 2016 John Cupitt 35 | * @license https://opensource.org/licenses/MIT MIT 36 | * @link https://github.com/jcupitt/php-vips 37 | */ 38 | 39 | namespace Jcupitt\Vips; 40 | 41 | /** 42 | * The OperationMath2 enum. 43 | * @category Images 44 | * @package Jcupitt\Vips 45 | * @author John Cupitt 46 | * @copyright 2016 John Cupitt 47 | * @license https://opensource.org/licenses/MIT MIT 48 | * @link https://github.com/jcupitt/php-vips 49 | */ 50 | abstract class OperationMath2 51 | { 52 | const POW = 'pow'; 53 | const WOP = 'wop'; 54 | const ATAN2 = 'atan2'; 55 | } 56 | -------------------------------------------------------------------------------- /src/OperationMorphology.php: -------------------------------------------------------------------------------- 1 | 34 | * @copyright 2016 John Cupitt 35 | * @license https://opensource.org/licenses/MIT MIT 36 | * @link https://github.com/jcupitt/php-vips 37 | */ 38 | 39 | namespace Jcupitt\Vips; 40 | 41 | /** 42 | * The OperationMorphology enum. 43 | * @category Images 44 | * @package Jcupitt\Vips 45 | * @author John Cupitt 46 | * @copyright 2016 John Cupitt 47 | * @license https://opensource.org/licenses/MIT MIT 48 | * @link https://github.com/jcupitt/php-vips 49 | */ 50 | abstract class OperationMorphology 51 | { 52 | const ERODE = 'erode'; 53 | const DILATE = 'dilate'; 54 | } 55 | -------------------------------------------------------------------------------- /src/OperationRelational.php: -------------------------------------------------------------------------------- 1 | 34 | * @copyright 2016 John Cupitt 35 | * @license https://opensource.org/licenses/MIT MIT 36 | * @link https://github.com/jcupitt/php-vips 37 | */ 38 | 39 | namespace Jcupitt\Vips; 40 | 41 | /** 42 | * The OperationRelational enum. 43 | * @category Images 44 | * @package Jcupitt\Vips 45 | * @author John Cupitt 46 | * @copyright 2016 John Cupitt 47 | * @license https://opensource.org/licenses/MIT MIT 48 | * @link https://github.com/jcupitt/php-vips 49 | */ 50 | abstract class OperationRelational 51 | { 52 | const EQUAL = 'equal'; 53 | const NOTEQ = 'noteq'; 54 | const LESS = 'less'; 55 | const LESSEQ = 'lesseq'; 56 | const MORE = 'more'; 57 | const MOREEQ = 'moreeq'; 58 | } 59 | -------------------------------------------------------------------------------- /src/OperationRound.php: -------------------------------------------------------------------------------- 1 | 34 | * @copyright 2016 John Cupitt 35 | * @license https://opensource.org/licenses/MIT MIT 36 | * @link https://github.com/jcupitt/php-vips 37 | */ 38 | 39 | namespace Jcupitt\Vips; 40 | 41 | /** 42 | * The OperationRound enum. 43 | * @category Images 44 | * @package Jcupitt\Vips 45 | * @author John Cupitt 46 | * @copyright 2016 John Cupitt 47 | * @license https://opensource.org/licenses/MIT MIT 48 | * @link https://github.com/jcupitt/php-vips 49 | */ 50 | abstract class OperationRound 51 | { 52 | const RINT = 'rint'; 53 | const CEIL = 'ceil'; 54 | const FLOOR = 'floor'; 55 | } 56 | -------------------------------------------------------------------------------- /src/PCS.php: -------------------------------------------------------------------------------- 1 | 34 | * @copyright 2016 John Cupitt 35 | * @license https://opensource.org/licenses/MIT MIT 36 | * @link https://github.com/jcupitt/php-vips 37 | */ 38 | 39 | namespace Jcupitt\Vips; 40 | 41 | /** 42 | * The PCS enum. 43 | * @category Images 44 | * @package Jcupitt\Vips 45 | * @author John Cupitt 46 | * @copyright 2016 John Cupitt 47 | * @license https://opensource.org/licenses/MIT MIT 48 | * @link https://github.com/jcupitt/php-vips 49 | */ 50 | abstract class PCS 51 | { 52 | const LAB = 'lab'; 53 | const XYZ = 'xyz'; 54 | } 55 | -------------------------------------------------------------------------------- /src/Precision.php: -------------------------------------------------------------------------------- 1 | 34 | * @copyright 2016 John Cupitt 35 | * @license https://opensource.org/licenses/MIT MIT 36 | * @link https://github.com/jcupitt/php-vips 37 | */ 38 | 39 | namespace Jcupitt\Vips; 40 | 41 | /** 42 | * The Precision enum. 43 | * @category Images 44 | * @package Jcupitt\Vips 45 | * @author John Cupitt 46 | * @copyright 2016 John Cupitt 47 | * @license https://opensource.org/licenses/MIT MIT 48 | * @link https://github.com/jcupitt/php-vips 49 | */ 50 | abstract class Precision 51 | { 52 | const INTEGER = 'integer'; 53 | const FLOAT = 'float'; 54 | const APPROXIMATE = 'approximate'; 55 | } 56 | -------------------------------------------------------------------------------- /src/RegionShrink.php: -------------------------------------------------------------------------------- 1 | 34 | * @copyright 2016 John Cupitt 35 | * @license https://opensource.org/licenses/MIT MIT 36 | * @link https://github.com/jcupitt/php-vips 37 | */ 38 | 39 | namespace Jcupitt\Vips; 40 | 41 | /** 42 | * The RegionShrink enum. 43 | * @category Images 44 | * @package Jcupitt\Vips 45 | * @author John Cupitt 46 | * @copyright 2016 John Cupitt 47 | * @license https://opensource.org/licenses/MIT MIT 48 | * @link https://github.com/jcupitt/php-vips 49 | */ 50 | abstract class RegionShrink 51 | { 52 | const MEAN = 'mean'; 53 | const MEDIAN = 'median'; 54 | const MODE = 'mode'; 55 | const MAX = 'max'; 56 | const MIN = 'min'; 57 | const NEAREST = 'nearest'; 58 | } 59 | -------------------------------------------------------------------------------- /src/SdfShape.php: -------------------------------------------------------------------------------- 1 | 34 | * @copyright 2016 John Cupitt 35 | * @license https://opensource.org/licenses/MIT MIT 36 | * @link https://github.com/jcupitt/php-vips 37 | */ 38 | 39 | namespace Jcupitt\Vips; 40 | 41 | /** 42 | * The SdfShape enum. 43 | * @category Images 44 | * @package Jcupitt\Vips 45 | * @author John Cupitt 46 | * @copyright 2016 John Cupitt 47 | * @license https://opensource.org/licenses/MIT MIT 48 | * @link https://github.com/jcupitt/php-vips 49 | */ 50 | abstract class SdfShape 51 | { 52 | const CIRCLE = 'circle'; 53 | const BOX = 'box'; 54 | const ROUNDED_BOX = 'rounded-box'; 55 | const LINE = 'line'; 56 | } 57 | -------------------------------------------------------------------------------- /src/Size.php: -------------------------------------------------------------------------------- 1 | 34 | * @copyright 2016 John Cupitt 35 | * @license https://opensource.org/licenses/MIT MIT 36 | * @link https://github.com/jcupitt/php-vips 37 | */ 38 | 39 | namespace Jcupitt\Vips; 40 | 41 | /** 42 | * The Size enum. 43 | * @category Images 44 | * @package Jcupitt\Vips 45 | * @author John Cupitt 46 | * @copyright 2016 John Cupitt 47 | * @license https://opensource.org/licenses/MIT MIT 48 | * @link https://github.com/jcupitt/php-vips 49 | */ 50 | abstract class Size 51 | { 52 | const BOTH = 'both'; 53 | const UP = 'up'; 54 | const DOWN = 'down'; 55 | const FORCE = 'force'; 56 | } 57 | -------------------------------------------------------------------------------- /src/Source.php: -------------------------------------------------------------------------------- 1 | pointer = FFI::vips()->cast(FFI::ctypes('VipsSource'), $pointer); 18 | parent::__construct($pointer); 19 | } 20 | 21 | /** 22 | * Make a new source from a file descriptor. For example: 23 | * 24 | * ```php 25 | * $source = Source::newFromDescriptor(0); 26 | * ``` 27 | * 28 | * Makes a descriptor attached to stdin. 29 | * You can pass this source to (for example) @see Image::newFromSource() 30 | * @throws Exception 31 | */ 32 | public static function newFromDescriptor(int $descriptor): self 33 | { 34 | $pointer = FFI::vips()->vips_source_new_from_descriptor($descriptor); 35 | 36 | if ($pointer === null) { 37 | throw new Exception("can't create source from descriptor $descriptor"); 38 | } 39 | 40 | return new self($pointer); 41 | } 42 | 43 | /** 44 | * Make a new source from a filename. For example: 45 | * 46 | * ```php 47 | * $source = Source::newFromFile("myfile.jpg"); 48 | * ``` 49 | * 50 | * You can pass this source to (for example) @see Image::newFromSource() 51 | * @throws Exception 52 | */ 53 | public static function newFromFile(string $filename): self 54 | { 55 | $pointer = FFI::vips()->vips_source_new_from_file($filename); 56 | 57 | if ($pointer === null) { 58 | throw new Exception("can't create source from filename $filename"); 59 | } 60 | 61 | return new self($pointer); 62 | } 63 | 64 | /** 65 | * Make a new source from a memory buffer. For example: 66 | * 67 | * ```php 68 | * $source = Source::newFromMemory(file_get_contents("myfile.jpg")); 69 | * ``` 70 | * 71 | * You can pass this source to (for example) @see Image::newFromSource() 72 | * @throws Exception 73 | */ 74 | public static function newFromMemory(string $data): self 75 | { 76 | $blob = FFI::vips()->vips_blob_copy($data, strlen($data)); 77 | if ($blob === null) { 78 | throw new Exception("can't create source from memory"); 79 | } 80 | 81 | $pointer = FFI::vips()->vips_source_new_from_blob($blob); 82 | if ($pointer === null) { 83 | FFI::vips()->vips_area_unref($blob); 84 | throw new Exception("can't create source from memory"); 85 | } 86 | 87 | $source = new self($pointer); 88 | FFI::vips()->vips_area_unref($blob); 89 | return $source; 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/SourceCustom.php: -------------------------------------------------------------------------------- 1 | pointer = FFI::vips()->vips_source_custom_new(); 18 | parent::__construct($this->pointer); 19 | } 20 | 21 | /** 22 | * Attach a read handler. 23 | * 24 | * The interface is similar to fread. The handler is given a number 25 | * of bytes to fetch, and should return a bytes-like object containing up 26 | * to that number of bytes. If there is no more data available, it should 27 | * return null. 28 | */ 29 | public function onRead(callable $callback): void 30 | { 31 | $this->signalConnect('read', $callback); 32 | } 33 | 34 | /** 35 | * Attach a seek handler. 36 | * 37 | * The interface is the same as fseek, so the handler is passed 38 | * parameters for $offset and $whence with the same meanings. 39 | * However, the handler MUST return the new seek position. A simple way 40 | * to do this is to call ftell() and return that result. 41 | * Seek handlers are optional. If you do not set one, your source will be 42 | * treated as unseekable and libvips will do extra caching. 43 | * 44 | * $whence in particular: 45 | * - 0 => start 46 | * - 1 => current position 47 | * - 2 => end 48 | */ 49 | public function onSeek(callable $callback): void 50 | { 51 | $this->signalConnect('seek', $callback); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/SourceResource.php: -------------------------------------------------------------------------------- 1 | resource = $resource; 21 | parent::__construct(); 22 | 23 | $this->onRead(static function (int $length) use (&$resource): ?string { 24 | return fread($resource, $length) ?: null; 25 | }); 26 | 27 | if (stream_get_meta_data($resource)['seekable']) { 28 | $this->onSeek(static function (int $offset, int $whence) use (&$resource): int { 29 | fseek($resource, $offset, $whence); 30 | return ftell($resource); 31 | }); 32 | } 33 | } 34 | 35 | public function __destruct() 36 | { 37 | fclose($this->resource); 38 | parent::__destruct(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Target.php: -------------------------------------------------------------------------------- 1 | pointer = FFI::vips()->cast(FFI::ctypes('VipsTarget'), $pointer); 18 | parent::__construct($pointer); 19 | } 20 | 21 | /** 22 | * Make a new target to write to a file descriptor. For example: 23 | * 24 | * ```php 25 | * $target = Target::newToDescriptor(1); 26 | * ``` 27 | * 28 | * Makes a descriptor attached to stdout. 29 | * You can pass this target to (for example) @see Image::writeToTarget() 30 | * @throws Exception 31 | */ 32 | public static function newToDescriptor(int $descriptor): self 33 | { 34 | $pointer = FFI::vips()->vips_target_new_to_descriptor($descriptor); 35 | if ($pointer === null) { 36 | throw new Exception("can't create output target from descriptor $descriptor"); 37 | } 38 | 39 | return new self($pointer); 40 | } 41 | 42 | /** 43 | * Make a new target to write to a filename. For example: 44 | * 45 | * ```php 46 | * $target = Target::newToFile("myfile.jpg"); 47 | * ``` 48 | * 49 | * You can pass this target to (for example) @see Image::writeToTarget() 50 | * @throws Exception 51 | */ 52 | public static function newToFile(string $filename): self 53 | { 54 | $pointer = FFI::vips()->vips_target_new_to_file($filename); 55 | 56 | if ($pointer === null) { 57 | throw new Exception("can't create output target from filename $filename"); 58 | } 59 | 60 | return new self($pointer); 61 | } 62 | 63 | /** 64 | * Make a new target to write to a memory buffer. For example: 65 | * 66 | * ```php 67 | * $target = Target::newToMemory(); 68 | * ``` 69 | * 70 | * You can pass this target to (for example) @see Image::writeToTarget() 71 | * @throws Exception 72 | */ 73 | public static function newToMemory(): self 74 | { 75 | $pointer = FFI::vips()->vips_target_new_to_memory(); 76 | 77 | if ($pointer === null) { 78 | throw new Exception("can't create output target from memory"); 79 | } 80 | 81 | return new self($pointer); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/TargetCustom.php: -------------------------------------------------------------------------------- 1 | pointer = FFI::vips()->vips_target_custom_new(); 18 | parent::__construct($this->pointer); 19 | } 20 | 21 | /** 22 | * Attach a write handler. 23 | * 24 | * The interface is similar to fwrite(). The handler is given a 25 | * bytes-like object to write, and should return the number of bytes 26 | * written. 27 | * 28 | * Unlike fwrite, you should return -1 on error. 29 | * 30 | * @throws Exception 31 | */ 32 | public function onWrite(callable $callback): void 33 | { 34 | $this->signalConnect('write', $callback); 35 | } 36 | 37 | /** 38 | * Attach a read handler. 39 | * 40 | * The interface is similar to fread(). The handler is given a number 41 | * of bytes to fetch, and should return a bytes-like object containing up 42 | * to that number of bytes. If there is no more data available, it should 43 | * return null. 44 | * 45 | * Read handlers on VipsTarget are optional. If you do not set one, your 46 | * target will be treated as unreadable and libvips will be unable to 47 | * write some file types (just TIFF, as of the time of writing). 48 | */ 49 | public function onRead(callable $callback): void 50 | { 51 | if (FFI::atLeast(8, 13)) { 52 | $this->signalConnect('read', $callback); 53 | } 54 | } 55 | 56 | /** 57 | * Attach a seek handler. 58 | * 59 | * The interface is similar to fseek, so the handler is passed 60 | * parameters for $offset and $whence with the same meanings. 61 | * However, the handler MUST return the new seek position. A simple way 62 | * to do this is to call ftell() and return that result. 63 | * 64 | * Seek handlers are optional. If you do not set one, your source will be 65 | * treated as unseekable and libvips will do extra caching. 66 | * 67 | * $whence in particular: 68 | * - 0 => start 69 | * - 1 => current position 70 | * - 2 => end 71 | */ 72 | public function onSeek(callable $callback): void 73 | { 74 | if (FFI::atLeast(8, 13)) { 75 | $this->signalConnect('seek', $callback); 76 | } 77 | } 78 | 79 | /** 80 | * Attach an end handler. 81 | * 82 | * This optional handler is called at the end of write. It should do any 83 | * cleaning up necessary, and return 0 on success and -1 on error. 84 | * 85 | * Automatically falls back to onFinish if libvips <8.13. 86 | * 87 | * @throws Exception 88 | */ 89 | public function onEnd(callable $callback): void 90 | { 91 | if (FFI::atLeast(8, 13)) { 92 | $this->signalConnect('end', $callback); 93 | } else { 94 | $this->onFinish($callback); 95 | } 96 | } 97 | 98 | /** 99 | * Attach a finish handler. 100 | * 101 | * For libvips 8.13 and later, this method is deprecated in favour of 102 | * onEnd(). 103 | * 104 | * @see TargetCustom::onEnd() 105 | * @throws Exception 106 | */ 107 | public function onFinish(callable $callback): void 108 | { 109 | $this->signalConnect('finish', $callback); 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /src/TargetResource.php: -------------------------------------------------------------------------------- 1 | resource = $resource; 21 | parent::__construct(); 22 | 23 | $this->onWrite(static function (string $buffer) use (&$resource): int { 24 | return fwrite($resource, $buffer) ?: 0; 25 | }); 26 | 27 | $this->onEnd(static function () use (&$resource): void { 28 | fclose($resource); 29 | }); 30 | 31 | $meta = stream_get_meta_data($resource); 32 | // See: https://www.php.net/manual/en/function.fopen.php 33 | if (substr($meta['mode'], -1) === '+') { 34 | $this->onRead(static function (int $length) use (&$resource): ?string { 35 | return fread($resource, $length) ?: null; 36 | }); 37 | } 38 | 39 | if ($meta['seekable']) { 40 | $this->onSeek(static function (int $offset, int $whence) use (&$resource): int { 41 | fseek($resource, $offset, $whence); 42 | return ftell($resource); 43 | }); 44 | } 45 | } 46 | 47 | public function __destruct() 48 | { 49 | if (is_resource($this->resource)) { 50 | fclose($this->resource); 51 | } 52 | parent::__destruct(); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/TextWrap.php: -------------------------------------------------------------------------------- 1 | 34 | * @copyright 2016 John Cupitt 35 | * @license https://opensource.org/licenses/MIT MIT 36 | * @link https://github.com/jcupitt/php-vips 37 | */ 38 | 39 | namespace Jcupitt\Vips; 40 | 41 | /** 42 | * The TextWrap enum. 43 | * @category Images 44 | * @package Jcupitt\Vips 45 | * @author John Cupitt 46 | * @copyright 2016 John Cupitt 47 | * @license https://opensource.org/licenses/MIT MIT 48 | * @link https://github.com/jcupitt/php-vips 49 | */ 50 | abstract class TextWrap 51 | { 52 | const WORD = 'word'; 53 | const CHAR = 'char'; 54 | const WORD_CHAR = 'word-char'; 55 | const NONE = 'none'; 56 | } 57 | -------------------------------------------------------------------------------- /src/Utils.php: -------------------------------------------------------------------------------- 1 | 34 | * @copyright 2016 John Cupitt 35 | * @license https://opensource.org/licenses/MIT MIT 36 | * @link https://github.com/jcupitt/php-vips 37 | */ 38 | 39 | namespace Jcupitt\Vips; 40 | 41 | /** 42 | * Various utilities. 43 | * 44 | * @category Images 45 | * @package Jcupitt\Vips 46 | * @author John Cupitt 47 | * @copyright 2016 John Cupitt 48 | * @license https://opensource.org/licenses/MIT MIT 49 | * @link https://github.com/jcupitt/php-vips 50 | */ 51 | class Utils 52 | { 53 | /** 54 | * Log a debug message. 55 | * 56 | * @param string $name The method creating the messages. 57 | * @param array $arguments The method arguments. 58 | * 59 | * @return void 60 | */ 61 | public static function debugLog(string $name, array $arguments): void 62 | { 63 | $logger = Config::getLogger(); 64 | if ($logger) { 65 | $logger->debug($name, $arguments); 66 | } 67 | } 68 | 69 | /** 70 | * Log an error message. 71 | * 72 | * @param string $message The error message. 73 | * @param \Exception|null $exception The exception, if any. 74 | * 75 | * @return void 76 | */ 77 | public static function errorLog(string $message, ?\Exception $exception = null): void 78 | { 79 | $logger = Config::getLogger(); 80 | if ($logger) { 81 | $logger->error($message, $exception == null ? [] : ['exception' => $exception]); 82 | } 83 | } 84 | 85 | /** 86 | * Look up the GTyoe from a type name. If the type does not exist, 87 | * return 0. 88 | * 89 | * @param string $name The type name. 90 | * 91 | * @return int 92 | */ 93 | public static function typeFromName(string $name): int 94 | { 95 | return FFI::gobject()->g_type_from_name($name); 96 | } 97 | 98 | public static function filenameGetFilename(string $name): string 99 | { 100 | $pointer = FFI::vips()->vips_filename_get_filename($name); 101 | $filename = \FFI::string($pointer); 102 | FFI::glib()->g_free($pointer); 103 | 104 | return $filename; 105 | } 106 | 107 | public static function filenameGetOptions(string $name): string 108 | { 109 | $pointer = FFI::vips()->vips_filename_get_options($name); 110 | $options = \FFI::string($pointer); 111 | FFI::glib()->g_free($pointer); 112 | 113 | return $options; 114 | } 115 | } 116 | 117 | /* 118 | * Local variables: 119 | * tab-width: 4 120 | * c-basic-offset: 4 121 | * End: 122 | * vim600: expandtab sw=4 ts=4 fdm=marker 123 | * vim<600: expandtab sw=4 ts=4 124 | */ 125 | -------------------------------------------------------------------------------- /src/VipsObject.php: -------------------------------------------------------------------------------- 1 | 34 | * @copyright 2016 John Cupitt 35 | * @license https://opensource.org/licenses/MIT MIT 36 | * @link https://github.com/libvips/php-vips 37 | */ 38 | 39 | namespace Jcupitt\Vips; 40 | 41 | /** 42 | * This class holds a pointer to a VipsObject (the libvips base class) and 43 | * manages properties. 44 | * 45 | * @category Images 46 | * @package Jcupitt\Vips 47 | * @author John Cupitt 48 | * @copyright 2016 John Cupitt 49 | * @license https://opensource.org/licenses/MIT MIT 50 | * @link https://github.com/libvips/php-vips 51 | */ 52 | abstract class VipsObject extends GObject 53 | { 54 | /** 55 | * A pointer to the underlying VipsObject. 56 | * 57 | * @internal 58 | */ 59 | private \FFI\CData $pointer; 60 | 61 | /** 62 | * A pointer to the underlying GObject. This is the same as the 63 | * VipsObject, just cast. 64 | * 65 | * @internal 66 | */ 67 | private \FFI\CData $gObject; 68 | 69 | public function __construct(\FFI\CData $pointer) 70 | { 71 | $this->pointer = FFI::vips()->cast(FFI::ctypes("VipsObject"), $pointer); 72 | $this->gObject = FFI::vips()->cast(FFI::ctypes("GObject"), $pointer); 73 | 74 | parent::__construct($pointer); 75 | } 76 | 77 | // print a table of all active vipsobjects ... handy for debugging 78 | public static function printAll(): void 79 | { 80 | FFI::vips()->vips_object_print_all(); 81 | } 82 | 83 | public function getDescription(): string 84 | { 85 | return FFI::vips()->vips_object_get_description($this->pointer); 86 | } 87 | 88 | // get the pspec for a property 89 | // NULL for no such name 90 | // very slow! avoid if possible 91 | // FIXME add a cache for this thing, see code in pyvips 92 | public function getPspec(string $name): ?\FFI\CData 93 | { 94 | $name = str_replace("-", "_", $name); 95 | $pspec_array = FFI::gobject()->new("GParamSpec*[1]"); 96 | $argument_class_array = FFI::vips()->new("VipsArgumentClass*[1]"); 97 | $argument_instance_array = FFI::vips()->new("VipsArgumentInstance*[1]"); 98 | $result = FFI::vips()->vips_object_get_argument( 99 | $this->pointer, 100 | $name, 101 | $pspec_array, 102 | $argument_class_array, 103 | $argument_instance_array 104 | ); 105 | 106 | if ($result != 0) { 107 | $pspec = null; 108 | } else { 109 | /* php-ffi seems to leak if we do the obvious $pspec_array[0] to 110 | * get the return result ... instead, we must make a new pointer 111 | * object and copy the value ourselves 112 | * 113 | * the returns values from vips_object_get_argument() are static, 114 | * so this is safe 115 | */ 116 | $pspec = FFI::gobject()->new("GParamSpec*"); 117 | $to_pointer = \FFI::addr($pspec); 118 | $from_pointer = \FFI::addr($pspec_array); 119 | $size = \FFI::sizeof($from_pointer); 120 | \FFI::memcpy($to_pointer, $from_pointer, $size); 121 | } 122 | 123 | return $pspec; 124 | } 125 | 126 | // get the type of a property from a VipsObject 127 | // 0 if no such property 128 | public function getType(string $name): int 129 | { 130 | $pspec = $this->getPspec($name); 131 | if (\FFI::isNull($pspec)) { 132 | # need to clear any error, this is horrible 133 | FFI::vips()->vips_error_clear(); 134 | return 0; 135 | } else { 136 | return $pspec->value_type; 137 | } 138 | } 139 | 140 | public function getBlurb(string $name): string 141 | { 142 | $pspec = $this->getPspec($name); 143 | return FFI::gobject()->g_param_spec_get_blurb($pspec); 144 | } 145 | 146 | public function getArgumentDescription(string $name): string 147 | { 148 | $pspec = $this->getPspec($name); 149 | return FFI::gobject()->g_param_spec_get_description($pspec); 150 | } 151 | 152 | /** 153 | * @throws Exception 154 | */ 155 | public function get(string $name) 156 | { 157 | $name = str_replace("-", "_", $name); 158 | $gvalue = new GValue(); 159 | $gvalue->setType($this->getType($name)); 160 | 161 | FFI::gobject()-> 162 | g_object_get_property($this->gObject, $name, $gvalue->pointer); 163 | $value = $gvalue->get(); 164 | 165 | Utils::debugLog("get", [$name => var_export($value, true)]); 166 | 167 | return $value; 168 | } 169 | 170 | /** 171 | * @throws Exception 172 | */ 173 | public function set(string $name, $value): void 174 | { 175 | Utils::debugLog("set", [$name => $value]); 176 | 177 | $name = str_replace("-", "_", $name); 178 | $gvalue = new GValue(); 179 | $gvalue->setType($this->getType($name)); 180 | $gvalue->set($value); 181 | 182 | FFI::gobject()-> 183 | g_object_set_property($this->gObject, $name, $gvalue->pointer); 184 | } 185 | 186 | public function setString(string $string_options): bool 187 | { 188 | $result = FFI::vips()-> 189 | vips_object_set_from_string($this->pointer, $string_options); 190 | 191 | return $result == 0; 192 | } 193 | 194 | public function unrefOutputs(): void 195 | { 196 | FFI::vips()->vips_object_unref_outputs($this->pointer); 197 | } 198 | } 199 | 200 | /* 201 | * Local variables: 202 | * tab-width: 4 203 | * c-basic-offset: 4 204 | * End: 205 | * vim600: expandtab sw=4 ts=4 fdm=marker 206 | * vim<600: expandtab sw=4 ts=4 207 | */ 208 | -------------------------------------------------------------------------------- /src/VipsOperation.php: -------------------------------------------------------------------------------- 1 | 34 | * @copyright 2016 John Cupitt 35 | * @license https://opensource.org/licenses/MIT MIT 36 | * @link https://github.com/libvips/php-vips 37 | */ 38 | 39 | namespace Jcupitt\Vips; 40 | 41 | /** 42 | * This class holds a pointer to a VipsOperation (the libvips operation base 43 | * class) and manages argument introspection and operation call. 44 | * 45 | * @category Images 46 | * @package Jcupitt\Vips 47 | * @author John Cupitt 48 | * @copyright 2016 John Cupitt 49 | * @license https://opensource.org/licenses/MIT MIT 50 | * @link https://github.com/libvips/php-vips 51 | */ 52 | class VipsOperation extends VipsObject 53 | { 54 | /** 55 | * A pointer to the underlying VipsOperation. This is the same as the 56 | * GObject, just cast to VipsOperation to help FFI. 57 | * 58 | * @internal 59 | */ 60 | public \FFI\CData $pointer; 61 | 62 | /** 63 | * Introspection data for this operation. 64 | */ 65 | public Introspect $introspect; 66 | 67 | public function __construct(\FFI\CData $pointer) 68 | { 69 | $this->pointer = FFI::vips()-> 70 | cast(FFI::ctypes("VipsOperation"), $pointer); 71 | 72 | parent::__construct($pointer); 73 | } 74 | 75 | /** 76 | * @throws Exception 77 | */ 78 | public static function newFromName($name): VipsOperation 79 | { 80 | $pointer = FFI::vips()->vips_operation_new($name); 81 | if ($pointer == null) { 82 | throw new Exception(); 83 | } 84 | $operation = new VipsOperation($pointer); 85 | 86 | return $operation; 87 | } 88 | 89 | public function setMatch($name, $match_image, $value): void 90 | { 91 | $flags = $this->introspect->arguments[$name]["flags"]; 92 | $gtype = $this->introspect->arguments[$name]["type"]; 93 | 94 | if ($match_image != null) { 95 | if ($gtype == FFI::gtypes("VipsImage")) { 96 | $value = $match_image->imageize($value); 97 | } elseif ($gtype == FFI::gtypes("VipsArrayImage") && 98 | is_array($value)) { 99 | $new_value = []; 100 | foreach ($value as $x) { 101 | $new_value[] = $match_image->imageize($x); 102 | } 103 | $value = $new_value; 104 | } 105 | } 106 | 107 | # MODIFY args need to be copied before they are set 108 | if (($flags & ArgumentFlags::MODIFY) != 0) { 109 | # logger.debug('copying MODIFY arg %s', name) 110 | # make sure we have a unique copy 111 | $value = $value->copyMemory(); 112 | } 113 | 114 | parent::set($name, $value); 115 | } 116 | 117 | private static function introspect($name): Introspect 118 | { 119 | static $cache = []; 120 | 121 | if (!array_key_exists($name, $cache)) { 122 | $cache[$name] = new Introspect($name); 123 | } 124 | 125 | return $cache[$name]; 126 | } 127 | 128 | private static function findInside($predicate, $x) 129 | { 130 | if ($predicate($x)) { 131 | return $x; 132 | } 133 | 134 | if (is_array($x)) { 135 | foreach ($x as $y) { 136 | $result = self::findInside($predicate, $y); 137 | 138 | if ($result != null) { 139 | return $result; 140 | } 141 | } 142 | } 143 | 144 | return null; 145 | } 146 | 147 | /** 148 | * Is $value a VipsImage. 149 | * 150 | * @param mixed $value The thing to test. 151 | * 152 | * @return bool true if this is a ffi VipsImage*. 153 | * 154 | * @internal 155 | */ 156 | private static function isImagePointer($value): bool 157 | { 158 | return $value instanceof \FFI\CData && 159 | \FFI::typeof($value) == FFI::ctypes("VipsImage"); 160 | } 161 | 162 | /** 163 | * Wrap up the result of a vips_ call ready to return it to PHP. We do 164 | * two things: 165 | * 166 | * - If the array is a singleton, we strip it off. For example, many 167 | * operations return a single result and there's no sense handling 168 | * this as an array of values, so we transform ['out' => x] -> x. 169 | * 170 | * - Any VipsImage resources are rewrapped as instances of Image. 171 | * 172 | * @param mixed $result Wrap this up. 173 | * 174 | * @return mixed $result, but wrapped up as a php class. 175 | * 176 | * @internal 177 | */ 178 | private static function wrapResult($result) 179 | { 180 | if (!is_array($result)) { 181 | $result = ['x' => $result]; 182 | } 183 | 184 | array_walk_recursive($result, function (&$item) { 185 | if (self::isImagePointer($item)) { 186 | $item = new Image($item); 187 | } 188 | }); 189 | 190 | if (count($result) === 1) { 191 | $result = array_shift($result); 192 | } 193 | 194 | return $result; 195 | } 196 | 197 | /** 198 | * Call any vips operation. The final element of $arguments can be 199 | * (but doesn't have to be) an array of options to pass to the operation. 200 | * 201 | * We can't have a separate arg for the options since this will be run from 202 | * __call(), which cannot know which args are required and which are 203 | * optional. See call() below for a version with the options broken out. 204 | * 205 | * @param string $operation_name The operation name. 206 | * @param Image|null $instance The instance this operation is being invoked 207 | * from. 208 | * @param array $arguments An array of arguments to pass to the 209 | * operation. 210 | * 211 | * @throws Exception 212 | * 213 | * @return mixed The result(s) of the operation. 214 | */ 215 | public static function callBase( 216 | string $operation_name, 217 | ?Image $instance, 218 | array $arguments 219 | ) { 220 | Utils::debugLog($operation_name, [ 221 | 'instance' => $instance, 222 | 'arguments' => $arguments 223 | ]); 224 | 225 | $operation = self::newFromName($operation_name); 226 | $operation->introspect = self::introspect($operation_name); 227 | $introspect = $operation->introspect; 228 | 229 | /* the first image argument is the thing we expand constants to 230 | * match ... look inside tables for images, since we may be passing 231 | * an array of images as a single param. 232 | */ 233 | if ($instance != null) { 234 | $match_image = $instance; 235 | } else { 236 | $match_image = self::findInside( 237 | fn($x) => $x instanceof Image, 238 | $arguments 239 | ); 240 | } 241 | 242 | /* Because of the way php callStatic works, we can sometimes be given 243 | * an instance even when no instance was given. 244 | * 245 | * We must loop over the required args and set them from the supplied 246 | * args, using instance if required, and only check the nargs after 247 | * this pass. 248 | */ 249 | $n_required = count($introspect->required_input); 250 | $n_supplied = count($arguments); 251 | $n_used = 0; 252 | foreach ($introspect->required_input as $name) { 253 | if ($name == $introspect->member_this) { 254 | if (!$instance) { 255 | $operation->unrefOutputs(); 256 | throw new Exception("instance argument not supplied"); 257 | } 258 | $operation->setMatch($name, $match_image, $instance); 259 | } elseif ($n_used < $n_supplied) { 260 | $operation->setMatch($name, $match_image, $arguments[$n_used]); 261 | $n_used += 1; 262 | } else { 263 | $operation->unrefOutputs(); 264 | throw new Exception("$n_required arguments required, " . 265 | "but $n_supplied supplied"); 266 | } 267 | } 268 | 269 | /* If there's one extra arg and it's an array, use it as our options. 270 | */ 271 | $options = []; 272 | if ($n_supplied == $n_used + 1 && is_array($arguments[$n_used])) { 273 | $options = array_pop($arguments); 274 | $n_supplied -= 1; 275 | } 276 | 277 | if ($n_supplied != $n_used) { 278 | $operation->unrefOutputs(); 279 | throw new Exception("$n_required arguments required, " . 280 | "but $n_supplied supplied"); 281 | } 282 | 283 | /* set any string options before any args so they can't be 284 | * overridden. 285 | */ 286 | if (array_key_exists("string_options", $options)) { 287 | $string_options = $options["string_options"]; 288 | unset($options["string_options"]); 289 | $operation->setString($string_options); 290 | } 291 | 292 | /* Set optional. 293 | */ 294 | foreach ($options as $name => $value) { 295 | $name = str_replace("-", "_", $name); 296 | if (!in_array($name, $introspect->optional_input) && 297 | !in_array($name, $introspect->optional_output)) { 298 | $operation->unrefOutputs(); 299 | throw new Exception("optional argument '$name' does not exist"); 300 | } 301 | 302 | $operation->setMatch($name, $match_image, $value); 303 | } 304 | 305 | /* Build the operation 306 | */ 307 | $pointer = FFI::vips()-> 308 | vips_cache_operation_build($operation->pointer); 309 | if ($pointer == null) { 310 | $operation->unrefOutputs(); 311 | throw new Exception(); 312 | } 313 | $operation = new VipsOperation($pointer); 314 | $operation->introspect = $introspect; 315 | 316 | # TODO .. need to attach input refs to output, see _find_inside in 317 | # pyvips 318 | 319 | /* Fetch required output args (and modified input args). 320 | */ 321 | $result = []; 322 | foreach ($introspect->required_output as $name) { 323 | $result[$name] = $operation->get($name); 324 | } 325 | 326 | /* Any optional output args. 327 | */ 328 | $option_keys = array_keys($options); 329 | foreach ($introspect->optional_output as $name) { 330 | $name = str_replace("-", "_", $name); 331 | if (in_array($name, $option_keys)) { 332 | $result[$name] = $operation->get($name); 333 | } 334 | } 335 | 336 | /* Free any outputs we've not used. 337 | */ 338 | $operation->unrefOutputs(); 339 | 340 | $result = self::wrapResult($result); 341 | 342 | Utils::debugLog($operation_name, [ 343 | 'result' => var_export($result, true) 344 | ]); 345 | 346 | return $result; 347 | } 348 | 349 | /** 350 | * Call any vips operation, with an explicit set of options. This is more 351 | * convenient than callBase() if you have a set of known options. 352 | * 353 | * @param string $name The operation name. 354 | * @param Image|null $instance The instance this operation is being invoked 355 | * from. 356 | * @param array $arguments An array of arguments to pass to the 357 | * operation. 358 | * @param array $options An array of optional arguments to pass to 359 | * the operation. 360 | * 361 | * @throws Exception 362 | * 363 | * @return mixed The result(s) of the operation. 364 | */ 365 | public static function call( 366 | string $name, 367 | ?Image $instance, 368 | array $arguments, 369 | array $options = [] 370 | ) { 371 | return self::callBase( 372 | $name, 373 | $instance, 374 | array_merge($arguments, [$options]) 375 | ); 376 | } 377 | } 378 | 379 | /* 380 | * Local variables: 381 | * tab-width: 4 382 | * c-basic-offset: 4 383 | * End: 384 | * vim600: expandtab sw=4 ts=4 fdm=marker 385 | * vim<600: expandtab sw=4 ts=4 386 | */ 387 | --------------------------------------------------------------------------------