Passed
Push — master ( 26c56b...7d441f )
by Bjørn
08:31
created

Vips   A

Complexity

Total Complexity 20

Size/Duplication

Total Lines 186
Duplicated Lines 0 %

Test Coverage

Coverage 37.29%

Importance

Changes 0
Metric Value
eloc 59
dl 0
loc 186
ccs 22
cts 59
cp 0.3729
rs 10
c 0
b 0
f 0
wmc 20

7 Methods

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