Completed
Pull Request — master (#279)
by AD
09:36 queued 08:21
created

Size::runContainResize()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1.037

Importance

Changes 0
Metric Value
dl 0
loc 6
ccs 2
cts 3
cp 0.6667
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 3
crap 1.037
1
<?php
2
3
namespace League\Glide\Manipulators;
4
5
use Intervention\Image\Image;
6
7
/**
8
 * @property string $dpr
9
 * @property string $fit
10
 * @property string $h
11
 * @property string $w
12
 */
13
class Size extends BaseManipulator
14
{
15
    /**
16
     * Maximum image size in pixels.
17
     * @var integer|null
18
     */
19
    protected $maxImageSize;
20
21
    /**
22
     * Create Size instance.
23
     * @param integer|null $maxImageSize Maximum image size in pixels.
24
     */
25 66
    public function __construct($maxImageSize = null)
26
    {
27 66
        $this->maxImageSize = $maxImageSize;
28 66
    }
29
30
    /**
31
     * Set the maximum image size.
32
     * @param integer|null Maximum image size in pixels.
33
     */
34 6
    public function setMaxImageSize($maxImageSize)
35
    {
36 6
        $this->maxImageSize = $maxImageSize;
37 6
    }
38
39
    /**
40
     * Get the maximum image size.
41
     * @return integer|null Maximum image size in pixels.
42
     */
43 6
    public function getMaxImageSize()
44
    {
45 6
        return $this->maxImageSize;
46
    }
47
48
    /**
49
     * Perform size image manipulation.
50
     * @param  Image $image The source image.
51
     * @return Image The manipulated image.
52
     */
53 6
    public function run(Image $image)
54
    {
55 6
        $width = $this->getWidth();
56 6
        $height = $this->getHeight();
57 6
        $fit = $this->getFit();
58 6
        $dpr = $this->getDpr();
59
60 6
        list($width, $height) = $this->resolveMissingDimensions($image, $width, $height);
61 6
        list($width, $height) = $this->applyDpr($width, $height, $dpr);
62 6
        list($width, $height) = $this->limitImageSize($width, $height);
63
64 6
        if ((int) $width !== (int) $image->width() or (int) $height !== (int) $image->height()) {
65 6
            $image = $this->runResize($image, $fit, (int) $width, (int) $height);
66
        }
67
68 6
        return $image;
69
    }
70
71
    /**
72
     * Resolve width.
73
     * @return integer|null The resolved width.
74
     */
75 9
    public function getWidth()
76
    {
77 9
        if (!is_numeric($this->w)) {
78 3
            return;
79
        }
80
81 9
        if ($this->w <= 0) {
82 3
            return;
83
        }
84
85 9
        return (int) $this->w;
86
    }
87
88
    /**
89
     * Resolve height.
90
     * @return integer|null The resolved height.
91
     */
92 9
    public function getHeight()
93
    {
94 9
        if (!is_numeric($this->h)) {
95 6
            return;
96
        }
97
98 6
        if ($this->h <= 0) {
99 3
            return;
100
        }
101
102 6
        return (int) $this->h;
103
    }
104
105
    /**
106
     * Resolve fit.
107
     * @return string The resolved fit.
108
     */
109 9
    public function getFit()
110
    {
111 9
        if (in_array($this->fit, ['contain', 'fill', 'max', 'stretch'], true)) {
112 3
            return $this->fit;
113
        }
114
115
        // phpcs:ignore
116 9
        $regex = '/^(crop)(-top-left|-top|-top-right|-left|-center|-right|-bottom-left|-bottom|-bottom-right|-[\d]{1,3}-[\d]{1,3}(?:-[\d]{1,3}(?:\.\d+)?)?)*$/';
117 9
        if (preg_match($regex, $this->fit)) {
118 3
            return 'crop';
119
        }
120
121 9
        return 'contain';
122
    }
123
124
    /**
125
     * Resolve the device pixel ratio.
126
     * @return double The device pixel ratio.
127
     */
128 9
    public function getDpr()
129
    {
130 9
        if (!is_numeric($this->dpr)) {
131 9
            return 1.0;
132
        }
133
134 3
        if ($this->dpr < 0 or $this->dpr > 8) {
135 3
            return 1.0;
136
        }
137
138 3
        return (double) $this->dpr;
139
    }
140
141
    /**
142
     * Resolve missing image dimensions.
143
     * @param  Image        $image  The source image.
144
     * @param  integer|null $width  The image width.
145
     * @param  integer|null $height The image height.
146
     * @return integer[]    The resolved width and height.
147
     */
148 9
    public function resolveMissingDimensions(Image $image, $width, $height)
149
    {
150 9
        if (is_null($width) and is_null($height)) {
151 3
            $width = $image->width();
152 3
            $height = $image->height();
153
        }
154
155 9
        if (is_null($width)) {
156 3
            $width = $height * ($image->width() / $image->height());
157
        }
158
159 9
        if (is_null($height)) {
160 6
            $height = $width / ($image->width() / $image->height());
161
        }
162
163
        return [
164 9
            (int) $width,
165 9
            (int) $height,
166
        ];
167
    }
168
169
    /**
170
     * Apply the device pixel ratio.
171
     * @param  integer   $width  The target image width.
172
     * @param  integer   $height The target image height.
173
     * @param  integer   $dpr    The device pixel ratio.
174
     * @return integer[] The modified width and height.
175
     */
176 6
    public function applyDpr($width, $height, $dpr)
177
    {
178 6
        $width = $width * $dpr;
179 6
        $height = $height * $dpr;
180
181
        return [
182 6
            (int) $width,
183 6
            (int) $height,
184
        ];
185
    }
186
187
    /**
188
     * Limit image size to maximum allowed image size.
189
     * @param  integer   $width  The image width.
190
     * @param  integer   $height The image height.
191
     * @return integer[] The limited width and height.
192
     */
193 9
    public function limitImageSize($width, $height)
194
    {
195 9
        if ($this->maxImageSize !== null) {
196 3
            $imageSize = $width * $height;
197
198 3
            if ($imageSize > $this->maxImageSize) {
199 3
                $width = $width / sqrt($imageSize / $this->maxImageSize);
200 3
                $height = $height / sqrt($imageSize / $this->maxImageSize);
201
            }
202
        }
203
204
        return [
205 9
            (int) $width,
206 9
            (int) $height,
207
        ];
208
    }
209
210
    /**
211
     * Perform resize image manipulation.
212
     * @param  Image   $image  The source image.
213
     * @param  string  $fit    The fit.
214
     * @param  integer $width  The width.
215
     * @param  integer $height The height.
216
     * @return Image   The manipulated image.
217
     */
218 9
    public function runResize(Image $image, $fit, $width, $height)
219
    {
220 9
        if ($fit === 'contain') {
221 9
            return $this->runContainResize($image, $width, $height);
222
        }
223
224 3
        if ($fit === 'fill') {
225 3
            return $this->runFillResize($image, $width, $height);
226
        }
227
228 3
        if ($fit === 'max') {
229 3
            return $this->runMaxResize($image, $width, $height);
230
        }
231
232 3
        if ($fit === 'stretch') {
233 3
            return $this->runStretchResize($image, $width, $height);
234
        }
235
236 3
        if ($fit === 'crop') {
237 3
            return $this->runCropResize($image, $width, $height);
238
        }
239
240 3
        return $image;
241
    }
242
243
    /**
244
     * Perform contain resize image manipulation.
245
     * @param  Image   $image  The source image.
246
     * @param  integer $width  The width.
247
     * @param  integer $height The height.
248
     * @return Image   The manipulated image.
249
     */
250 12
    public function runContainResize(Image $image, $width, $height)
251
    {
252
        return $image->resize($width, $height, function ($constraint) {
253
            $constraint->aspectRatio();
254 12
        });
255
    }
256
257
    /**
258
     * Perform max resize image manipulation.
259
     * @param  Image   $image  The source image.
260
     * @param  integer $width  The width.
261
     * @param  integer $height The height.
262
     * @return Image   The manipulated image.
263
     */
264 9
    public function runMaxResize(Image $image, $width, $height)
265
    {
266
        return $image->resize($width, $height, function ($constraint) {
267
            $constraint->aspectRatio();
268
            $constraint->upsize();
269 9
        });
270
    }
271
272
    /**
273
     * Perform fill resize image manipulation.
274
     * @param  Image   $image  The source image.
275
     * @param  integer $width  The width.
276
     * @param  integer $height The height.
277
     * @return Image   The manipulated image.
278
     */
279 6
    public function runFillResize($image, $width, $height)
280
    {
281 6
        $image = $this->runMaxResize($image, $width, $height);
282
283 6
        return $image->resizeCanvas($width, $height, 'center');
284
    }
285
286
    /**
287
     * Perform stretch resize image manipulation.
288
     * @param  Image   $image  The source image.
289
     * @param  integer $width  The width.
290
     * @param  integer $height The height.
291
     * @return Image   The manipulated image.
292
     */
293 6
    public function runStretchResize(Image $image, $width, $height)
294
    {
295 6
        return $image->resize($width, $height);
296
    }
297
298
    /**
299
     * Perform crop resize image manipulation.
300
     * @param  Image   $image  The source image.
301
     * @param  integer $width  The width.
302
     * @param  integer $height The height.
303
     * @return Image   The manipulated image.
304
     */
305 6
    public function runCropResize(Image $image, $width, $height)
306
    {
307 6
        list($resize_width, $resize_height) = $this->resolveCropResizeDimensions($image, $width, $height);
308
309 6
        $zoom = $this->getCrop()[2];
310
311
        $image->resize($resize_width * $zoom, $resize_height * $zoom, function ($constraint) {
312
            $constraint->aspectRatio();
313 6
        });
314
315 6
        list($offset_x, $offset_y) = $this->resolveCropOffset($image, $width, $height);
316
317 6
        return $image->crop($width, $height, $offset_x, $offset_y);
318
    }
319
320
    /**
321
     * Resolve the crop resize dimensions.
322
     * @param  Image   $image  The source image.
323
     * @param  integer $width  The width.
324
     * @param  integer $height The height.
325
     * @return array   The resize dimensions.
326
     */
327 6
    public function resolveCropResizeDimensions(Image $image, $width, $height)
328
    {
329 6
        if ($height > $width * ($image->height() / $image->width())) {
330
            return [$height * ($image->width() / $image->height()), $height];
331
        }
332
333 6
        return [$width, $width * ($image->height() / $image->width())];
334
    }
335
336
    /**
337
     * Resolve the crop offset.
338
     * @param  Image   $image  The source image.
339
     * @param  integer $width  The width.
340
     * @param  integer $height The height.
341
     * @return array   The crop offset.
342
     */
343 6
    public function resolveCropOffset(Image $image, $width, $height)
344
    {
345 6
        list($offset_percentage_x, $offset_percentage_y) = $this->getCrop();
346
347 6
        $offset_x = (int) (($image->width() * $offset_percentage_x / 100) - ($width / 2));
348 6
        $offset_y = (int) (($image->height() * $offset_percentage_y / 100) - ($height / 2));
349
350 6
        $max_offset_x = $image->width() - $width;
351 6
        $max_offset_y = $image->height() - $height;
352
353 6
        if ($offset_x < 0) {
354
            $offset_x = 0;
355
        }
356
357 6
        if ($offset_y < 0) {
358
            $offset_y = 0;
359
        }
360
361 6
        if ($offset_x > $max_offset_x) {
362
            $offset_x = $max_offset_x;
363
        }
364
365 6
        if ($offset_y > $max_offset_y) {
366
            $offset_y = $max_offset_y;
367
        }
368
369 6
        return [$offset_x, $offset_y];
370
    }
371
372
    /**
373
     * Resolve crop with zoom.
374
     * @return integer[] The resolved crop.
375
     */
376 9
    public function getCrop()
377
    {
378
        $cropMethods = [
379 9
            'crop-top-left' => [0, 0, 1.0],
380
            'crop-top' => [50, 0, 1.0],
381
            'crop-top-right' => [100, 0, 1.0],
382
            'crop-left' => [0, 50, 1.0],
383
            'crop-center' => [50, 50, 1.0],
384
            'crop-right' => [100, 50, 1.0],
385
            'crop-bottom-left' => [0, 100, 1.0],
386
            'crop-bottom' => [50, 100, 1.0],
387
            'crop-bottom-right' => [100, 100, 1.0],
388
        ];
389
390 9
        if (array_key_exists($this->fit, $cropMethods)) {
391 3
            return $cropMethods[$this->fit];
392
        }
393
394 9
        if (preg_match('/^crop-([\d]{1,3})-([\d]{1,3})(?:-([\d]{1,3}(?:\.\d+)?))*$/', $this->fit, $matches)) {
395 3
            $matches[3] = isset($matches[3]) ? $matches[3] : 1;
396
397 3
            if ($matches[1] > 100 or $matches[2] > 100 or $matches[3] > 100) {
398 3
                return [50, 50, 1.0];
399
            }
400
401
            return [
402 3
                (int) $matches[1],
403 3
                (int) $matches[2],
404 3
                (float) $matches[3],
405
            ];
406
        }
407
408 9
        return [50, 50, 1.0];
409
    }
410
}
411