Size::setMaxImageSize()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

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