Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
74.44% |
67 / 90 |
|
50.00% |
4 / 8 |
CRAP | |
0.00% |
0 / 1 |
Vips | |
74.44% |
67 / 90 |
|
50.00% |
4 / 8 |
57.63 | |
0.00% |
0 / 1 |
getUnsupportedDefaultOptions | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getUniqueOptions | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
1 | |||
checkOperationality | |
78.57% |
11 / 14 |
|
0.00% |
0 / 1 |
7.48 | |||
checkConvertability | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
2 | |||
createImageResource | |
54.55% |
6 / 11 |
|
0.00% |
0 / 1 |
5.50 | |||
createParamsForVipsWebPSave | |
100.00% |
22 / 22 |
|
100.00% |
1 / 1 |
9 | |||
webpsave | |
57.58% |
19 / 33 |
|
0.00% |
0 / 1 |
20.24 | |||
doActualConvert | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
1 |
1 | <?php |
2 | |
3 | namespace WebPConvert\Convert\Converters; |
4 | |
5 | use WebPConvert\Convert\Converters\AbstractConverter; |
6 | use WebPConvert\Convert\Converters\ConverterTraits\EncodingAutoTrait; |
7 | use WebPConvert\Convert\Exceptions\ConversionFailedException; |
8 | use WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperational\SystemRequirementsNotMetException; |
9 | use WebPConvert\Options\BooleanOption; |
10 | use WebPConvert\Options\IntegerOption; |
11 | |
12 | //require '/home/rosell/.composer/vendor/autoload.php'; |
13 | |
14 | /** |
15 | * Convert images to webp using Vips extension. |
16 | * |
17 | * @package WebPConvert |
18 | * @author Bjørn Rosell <it@rosell.dk> |
19 | * @since Class available since Release 2.0.0 |
20 | */ |
21 | class Vips extends AbstractConverter |
22 | { |
23 | use EncodingAutoTrait; |
24 | |
25 | protected function getUnsupportedDefaultOptions() |
26 | { |
27 | return [ |
28 | 'auto-filter', |
29 | 'size-in-percentage', |
30 | ]; |
31 | } |
32 | |
33 | /** |
34 | * Get the options unique for this converter |
35 | * |
36 | * @return array Array of options |
37 | */ |
38 | public function getUniqueOptions($imageType) |
39 | { |
40 | $ssOption = new BooleanOption('smart-subsample', false); |
41 | $ssOption->markDeprecated(); |
42 | return [ |
43 | $ssOption |
44 | ]; |
45 | } |
46 | |
47 | /** |
48 | * Check operationality of Vips converter. |
49 | * |
50 | * @throws SystemRequirementsNotMetException if system requirements are not met |
51 | */ |
52 | public function checkOperationality() |
53 | { |
54 | if (!extension_loaded('vips')) { |
55 | throw new SystemRequirementsNotMetException('Required Vips extension is not available.'); |
56 | } |
57 | |
58 | if (!function_exists('vips_image_new_from_file')) { |
59 | throw new SystemRequirementsNotMetException( |
60 | 'Vips extension seems to be installed, however something is not right: ' . |
61 | 'the function "vips_image_new_from_file" is not available.' |
62 | ); |
63 | } |
64 | |
65 | if (!function_exists('vips_call')) { |
66 | throw new SystemRequirementsNotMetException( |
67 | 'Vips extension seems to be installed, however something is not right: ' . |
68 | 'the function "vips_call" is not available.' |
69 | ); |
70 | } |
71 | |
72 | if (!function_exists('vips_error_buffer')) { |
73 | throw new SystemRequirementsNotMetException( |
74 | 'Vips extension seems to be installed, however something is not right: ' . |
75 | 'the function "vips_error_buffer" is not available.' |
76 | ); |
77 | } |
78 | |
79 | |
80 | vips_error_buffer(); // clear error buffer |
81 | $result = vips_call('webpsave', null); |
82 | if ($result === -1) { |
83 | $message = vips_error_buffer(); |
84 | if (strpos($message, 'VipsOperation: class "webpsave" not found') === 0) { |
85 | throw new SystemRequirementsNotMetException( |
86 | 'Vips has not been compiled with webp support.' |
87 | ); |
88 | } |
89 | } |
90 | } |
91 | |
92 | /** |
93 | * Check if specific file is convertable with current converter / converter settings. |
94 | * |
95 | * @throws SystemRequirementsNotMetException if Vips does not support image type |
96 | */ |
97 | public function checkConvertability() |
98 | { |
99 | // It seems that png and jpeg are always supported by Vips |
100 | // - so nothing needs to be done here |
101 | |
102 | if (function_exists('vips_version')) { |
103 | $this->logLn('vipslib version: ' . vips_version()); |
104 | } |
105 | $this->logLn('vips extension version: ' . phpversion('vips')); |
106 | } |
107 | |
108 | /** |
109 | * Create vips image resource from source file |
110 | * |
111 | * @throws ConversionFailedException if image resource cannot be created |
112 | * @return resource vips image resource |
113 | */ |
114 | private function createImageResource() |
115 | { |
116 | // We are currently using vips_image_new_from_file(), but we could consider |
117 | // calling vips_jpegload / vips_pngload instead |
118 | $result = /** @scrutinizer ignore-call */ vips_image_new_from_file($this->source, []); |
119 | if ($result === -1) { |
120 | /*throw new ConversionFailedException( |
121 | 'Failed creating new vips image from file: ' . $this->source |
122 | );*/ |
123 | $message = /** @scrutinizer ignore-call */ vips_error_buffer(); |
124 | throw new ConversionFailedException($message); |
125 | } |
126 | |
127 | if (!is_array($result)) { |
128 | throw new ConversionFailedException( |
129 | 'vips_image_new_from_file did not return an array, which we expected' |
130 | ); |
131 | } |
132 | |
133 | if (count($result) != 1) { |
134 | throw new ConversionFailedException( |
135 | 'vips_image_new_from_file did not return an array of length 1 as we expected ' . |
136 | '- length was: ' . count($result) |
137 | ); |
138 | } |
139 | |
140 | $im = array_shift($result); |
141 | return $im; |
142 | } |
143 | |
144 | /** |
145 | * Create parameters for webpsave |
146 | * |
147 | * @return array the parameters as an array |
148 | */ |
149 | private function createParamsForVipsWebPSave() |
150 | { |
151 | // webpsave options are described here: |
152 | // https://libvips.github.io/libvips/API/current/VipsForeignSave.html#vips-webpsave |
153 | // near_lossless option is described here: https://github.com/libvips/libvips/pull/430 |
154 | |
155 | // NOTE: When a new option becomes available, we MUST remember to add |
156 | // it to the array of possibly unsupported options in webpsave() ! |
157 | $options = [ |
158 | "Q" => $this->getCalculatedQuality(), |
159 | 'lossless' => ($this->options['encoding'] == 'lossless'), |
160 | 'strip' => $this->options['metadata'] == 'none', |
161 | ]; |
162 | |
163 | // Only set the following options if they differ from the default of vipslib |
164 | // This ensures we do not get warning if that property isn't supported |
165 | if ($this->options['smart-subsample'] !== false) { |
166 | // PS: The smart-subsample option is now deprecated, as it turned out |
167 | // it was corresponding to the "sharp-yuv" option (see #280) |
168 | $options['smart_subsample'] = $this->options['smart-subsample']; |
169 | $this->logLn( |
170 | '*Note: the "smart-subsample" option is now deprecated. It turned out it corresponded to ' . |
171 | 'the general option "sharp-yuv". You should use "sharp-yuv" instead.*' |
172 | ); |
173 | } |
174 | if ($this->options['sharp-yuv'] !== false) { |
175 | $options['smart_subsample'] = $this->options['sharp-yuv']; |
176 | } |
177 | |
178 | if ($this->options['alpha-quality'] !== 100) { |
179 | $options['alpha_q'] = $this->options['alpha-quality']; |
180 | } |
181 | |
182 | if (!is_null($this->options['preset']) && ($this->options['preset'] != 'none')) { |
183 | // preset. 0:default, 1:picture, 2:photo, 3:drawing, 4:icon, 5:text, 6:last |
184 | $options['preset'] = array_search( |
185 | $this->options['preset'], |
186 | ['default', 'picture', 'photo', 'drawing', 'icon', 'text'] |
187 | ); |
188 | } |
189 | if ($this->options['near-lossless'] !== 100) { |
190 | if ($this->options['encoding'] == 'lossless') { |
191 | // We only let near_lossless have effect when encoding is set to lossless |
192 | // otherwise encoding=auto would not work as expected |
193 | // Available in https://github.com/libvips/libvips/pull/430, merged 1 may 2016 |
194 | // seems it corresponds to release 8.4.2 |
195 | $options['near_lossless'] = true; |
196 | |
197 | // In Vips, the near-lossless value is controlled by Q. |
198 | // this differs from how it is done in cwebp, where it is an integer. |
199 | // We have chosen same option syntax as cwebp |
200 | $options['Q'] = $this->options['near-lossless']; |
201 | } |
202 | } |
203 | if ($this->options['method'] !== 4) { |
204 | $options['reduction_effort'] = $this->options['method']; |
205 | } |
206 | |
207 | return $options; |
208 | } |
209 | |
210 | /** |
211 | * Save as webp, using vips extension. |
212 | * |
213 | * Tries to save image resource as webp, using the supplied options. |
214 | * Vips fails when a parameter is not supported, but we detect this and unset that parameter and try again |
215 | * (recursively call itself until there is no more of these kind of errors). |
216 | * |
217 | * @param resource $im A vips image resource to save |
218 | * @throws ConversionFailedException if conversion fails. |
219 | */ |
220 | private function webpsave($im, $options) |
221 | { |
222 | /** @scrutinizer ignore-call */ vips_error_buffer(); // clear error buffer |
223 | $result = /** @scrutinizer ignore-call */ vips_call('webpsave', $im, $this->destination, $options); |
224 | |
225 | //trigger_error('test-warning', E_USER_WARNING); |
226 | if ($result === -1) { |
227 | $message = /** @scrutinizer ignore-call */ vips_error_buffer(); |
228 | |
229 | $nameOfPropertyNotFound = ''; |
230 | if (preg_match("#no property named .(.*).#", $message, $matches)) { |
231 | $nameOfPropertyNotFound = $matches[1]; |
232 | } elseif (preg_match("#(.*)\\sunsupported$#", $message, $matches)) { |
233 | // Actually, I am not quite sure if this ever happens. |
234 | // I got a "near_lossless unsupported" error message in a build, but perhaps it rather a warning |
235 | if (in_array($matches[1], [ |
236 | 'lossless', |
237 | 'alpha_q', |
238 | 'near_lossless', |
239 | 'smart_subsample', |
240 | 'reduction_effort', |
241 | 'preset' |
242 | ])) { |
243 | $nameOfPropertyNotFound = $matches[1]; |
244 | } |
245 | } |
246 | |
247 | if ($nameOfPropertyNotFound != '') { |
248 | $msg = 'Note: Your version of vipslib does not support the "' . |
249 | $nameOfPropertyNotFound . '" property'; |
250 | |
251 | switch ($nameOfPropertyNotFound) { |
252 | case 'alpha_q': |
253 | $msg .= ' (It was introduced in vips 8.4)'; |
254 | break; |
255 | case 'near_lossless': |
256 | $msg .= ' (It was introduced in vips 8.4)'; |
257 | break; |
258 | case 'smart_subsample': |
259 | $msg .= ' (its the vips equalent to the "sharp-yuv" option. It was introduced in vips 8.4)'; |
260 | break; |
261 | case 'reduction_effort': |
262 | $msg .= ' (its the vips equalent to the "method" option. It was introduced in vips 8.8.0)'; |
263 | break; |
264 | case 'preset': |
265 | $msg .= ' (It was introduced in vips 8.4)'; |
266 | break; |
267 | } |
268 | $msg .= '. The option is ignored.'; |
269 | |
270 | |
271 | $this->logLn($msg, 'bold'); |
272 | |
273 | unset($options[$nameOfPropertyNotFound]); |
274 | $this->webpsave($im, $options); |
275 | } else { |
276 | throw new ConversionFailedException($message); |
277 | } |
278 | } |
279 | } |
280 | |
281 | /** |
282 | * Convert, using vips extension. |
283 | * |
284 | * Tries to create image resource and save it as webp using the calculated options. |
285 | * PS: The Vips "webpsave" call fails when a parameter is not supported, but our webpsave() method |
286 | * detect this and unset that parameter and try again (repeat until success). |
287 | * |
288 | * @throws ConversionFailedException if conversion fails. |
289 | */ |
290 | protected function doActualConvert() |
291 | { |
292 | /* |
293 | $im = \Jcupitt\Vips\Image::newFromFile($this->source); |
294 | //$im->writeToFile(__DIR__ . '/images/small-vips.webp', ["Q" => 10]); |
295 | |
296 | $im->webpsave($this->destination, [ |
297 | "Q" => 80, |
298 | //'near_lossless' => true |
299 | ]); |
300 | return;*/ |
301 | |
302 | $im = $this->createImageResource(); |
303 | $options = $this->createParamsForVipsWebPSave(); |
304 | $this->webpsave($im, $options); |
305 | } |
306 | } |