Passed
Push — master ( ba09bf...91cd83 )
by Bjørn
04:56
created

Vips::webpsave()   A

Complexity

Conditions 6
Paths 9

Size

Total Lines 28
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 26.1045

Importance

Changes 3
Bugs 0 Features 0
Metric Value
cc 6
eloc 17
c 3
b 0
f 0
nc 9
nop 2
dl 0
loc 28
ccs 3
cts 17
cp 0.1765
crap 26.1045
rs 9.0777
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
11
//require '/home/rosell/.composer/vendor/autoload.php';
12
13
/**
14
 * Convert images to webp using Vips extension.
15
 *
16
 * @package    WebPConvert
17
 * @author     Bjørn Rosell <[email protected]>
18
 * @since      Class available since Release 2.0.0
19
 */
20
class Vips extends AbstractConverter
21
{
22
    use EncodingAutoTrait;
23
24
    protected function getUnsupportedDefaultOptions()
25
    {
26
        return [
27
            'size-in-percentage',
28
            'use-nice'
29
        ];
30
    }
31
32 6
    protected function createOptions()
33
    {
34 6
        parent::createOptions();
35
36 6
        $this->options2->addOptions(
37 6
            new BooleanOption('smart-subsample', false)
38
        );
39 6
    }
40
41
    /**
42
     * Check operationality of Vips converter.
43
     *
44
     * @throws SystemRequirementsNotMetException  if system requirements are not met
45
     */
46 4
    public function checkOperationality()
47
    {
48 4
        if (!extension_loaded('vips')) {
49 1
            throw new SystemRequirementsNotMetException('Required Vips extension is not available.');
50
        }
51
52 3
        if (!function_exists('vips_image_new_from_file')) {
53 1
            throw new SystemRequirementsNotMetException(
54
                'Vips extension seems to be installed, however something is not right: ' .
55 1
                'the function "vips_image_new_from_file" is not available.'
56
            );
57
        }
58
59
        // TODO: Should we also test if webp is available? (It seems not to be neccessary - it seems
60
        // that webp be well intergrated part of vips)
61 2
    }
62
63
    /**
64
     * Check if specific file is convertable with current converter / converter settings.
65
     *
66
     * @throws SystemRequirementsNotMetException  if Vips does not support image type
67
     */
68 1
    public function checkConvertability()
69
    {
70
        // It seems that png and jpeg are always supported by Vips
71
        // - so nothing needs to be done here
72
73 1
        if (function_exists('vips_version')) {
74 1
            $this->logLn('vipslib version: ' . vips_version());
75
        }
76 1
        $this->logLn('vips extension version: ' . phpversion('vips'));
77 1
    }
78
79
    /**
80
     * Create vips image resource from source file
81
     *
82
     * @throws  ConversionFailedException  if image resource cannot be created
83
     * @return  resource  vips image resource
84
     */
85 1
    private function createImageResource()
86
    {
87
        // We are currently using vips_image_new_from_file(), but we could consider
88
        // calling vips_jpegload / vips_pngload instead
89 1
        $result = /** @scrutinizer ignore-call */ vips_image_new_from_file($this->source, []);
90 1
        if ($result === -1) {
91
            /*throw new ConversionFailedException(
92
                'Failed creating new vips image from file: ' . $this->source
93
            );*/
94
            $message = /** @scrutinizer ignore-call */ vips_error_buffer();
95
            throw new ConversionFailedException($message);
96
        }
97
98 1
        if (!is_array($result)) {
99
            throw new ConversionFailedException(
100
                'vips_image_new_from_file did not return an array, which we expected'
101
            );
102
        }
103
104 1
        if (count($result) != 1) {
105
            throw new ConversionFailedException(
106
                'vips_image_new_from_file did not return an array of length 1 as we expected ' .
107
                '- length was: ' . count($result)
108
            );
109
        }
110
111 1
        $im = array_shift($result);
112 1
        return $im;
113
    }
114
115
    /**
116
     * Create parameters for webpsave
117
     *
118
     * @return  array  the parameters as an array
119
     */
120 3
    private function createParamsForVipsWebPSave()
121
    {
122
        // webpsave options are described here:
123
        // v 8.8.0:  https://libvips.github.io/libvips/API/current/VipsForeignSave.html#vips-webpsave
124
        // v ?.?.?:  https://jcupitt.github.io/libvips/API/current/VipsForeignSave.html#vips-webpsave
125
        // near_lossless option is described here: https://github.com/libvips/libvips/pull/430
126
127
        // Note that "method" is currently not supported (27 may 2019)
128
129
        $options = [
130 3
            "Q" => $this->getCalculatedQuality(),
131 3
            'lossless' => ($this->options['encoding'] == 'lossless'),
132 3
            'strip' => $this->options['metadata'] == 'none',
133
        ];
134
135
        // Only set the following options if they differ from the default of vipslib
136
        // This ensures we do not get warning if that property isn't supported
137 3
        if ($this->options['smart-subsample'] !== false) {
138 1
            $options['smart_subsample'] = $this->options['smart-subsample'];
139
        }
140 3
        if ($this->options['alpha-quality'] !== 100) {
141 2
            $options['alpha_q'] = $this->options['alpha-quality'];
142
        }
143
144 3
        if (!is_null($this->options['preset']) && ($this->options['preset'] != 'none')) {
145
            // preset. 0:default, 1:picture, 2:photo, 3:drawing, 4:icon, 5:text, 6:last
146 1
            $options['preset'] = array_search(
147 1
                $this->options['preset'],
148 1
                ['default', 'picture', 'photo', 'drawing', 'icon', 'text']
149
            );
150
        }
151 3
        if ($this->options['near-lossless'] !== 100) {
152 3
            if ($this->options['encoding'] == 'lossless') {
153
                // We only let near_lossless have effect when encoding is set to lossless
154
                // otherwise encoding=auto would not work as expected
155
                // Available in https://github.com/libvips/libvips/pull/430, merged 1 may 2016
156
                // seems it corresponds to release 8.4.2
157 2
                $options['near_lossless'] = true;
158
159
                // In Vips, the near-lossless value is controlled by Q.
160
                // this differs from how it is done in cwebp, where it is an integer.
161
                // We have chosen same option syntax as cwebp
162 2
                $options['Q'] = $this->options['near-lossless'];
163
            }
164
        }
165
166 3
        return $options;
167
    }
168
169
    /**
170
     * Convert with vips extension.
171
     *
172
     * Tries to create image resource and save it as webp using the calculated options.
173
     * Vips fails when a parameter is not supported, but we detect this and unset that parameter and try again
174
     * (recursively call itself until there is no more of these kind of errors).
175
     *
176
     * @param  resource  $im  A vips image resource to save
177
     * @throws  ConversionFailedException  if conversion fails.
178
     */
179 1
    private function webpsave($im, $options)
180
    {
181 1
        $result = /** @scrutinizer ignore-call */ vips_call('webpsave', $im, $this->destination, $options);
182
183
        //trigger_error('test-warning', E_USER_WARNING);
184 1
        if ($result === -1) {
185
            $message = /** @scrutinizer ignore-call */ vips_error_buffer();
186
187
            $nameOfPropertyNotFound = '';
188
            if (preg_match("#no property named .(.*).#", $message, $matches)) {
189
                $nameOfPropertyNotFound = $matches[1];
190
            } elseif (preg_match("#(.*)\\sunsupported$#", $message, $matches)) {
191
                // Actually, I am not quite sure if this ever happens.
192
                // I got a "near_lossless unsupported" error message in a build, but perhaps it rather a warning
193
                if (in_array($matches[1], ['lossless', 'alpha_q', 'near_lossless', 'smart_subsample'])) {
194
                    $nameOfPropertyNotFound = $matches[1];
195
                }
196
            }
197
198
            if ($nameOfPropertyNotFound != '') {
199
                $this->logLn(
200
                    'Your version of vipslib does not support the "' . $nameOfPropertyNotFound . '" property. ' .
201
                    'The option is ignored.'
202
                );
203
                unset($options[$nameOfPropertyNotFound]);
204
                $this->webpsave($im, $options);
205
            } else {
206
                throw new ConversionFailedException($message);
207
            }
208
        }
209 1
    }
210
211
    /**
212
     * Convert with vips extension.
213
     *
214
     * Tries to create image resource and save it as webp using the calculated options.
215
     * Vips fails when a parameter is not supported, but we detect this and unset that parameter and try again
216
     * (repeat until success).
217
     *
218
     * @throws  ConversionFailedException  if conversion fails.
219
     */
220 1
    protected function doActualConvert()
221
    {
222
/*
223
        $im = \Jcupitt\Vips\Image::newFromFile($this->source);
224
        //$im->writeToFile(__DIR__ . '/images/small-vips.webp', ["Q" => 10]);
225
226
        $im->webpsave($this->destination, [
227
            "Q" => 80,
228
            //'near_lossless' => true
229
        ]);
230
        return;*/
231
232 1
        $im = $this->createImageResource();
233 1
        $options = $this->createParamsForVipsWebPSave();
234 1
        $this->webpsave($im, $options);
235 1
    }
236
}
237