Completed
Push — master ( 36e2d9...033daa )
by Bjørn
03:15
created

Vips::createParamsForVipsWebPSave()   A

Complexity

Conditions 6
Paths 24

Size

Total Lines 40
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 17
CRAP Score 6

Importance

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