Passed
Push — master ( d22187...2d77cb )
by Mathieu
13:32
created

Image::setResource()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 11
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 3
eloc 6
c 1
b 0
f 0
nc 2
nop 1
dl 0
loc 11
ccs 0
cts 6
cp 0
crap 12
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Suricate;
6
7
use InvalidArgumentException;
8
use RuntimeException;
9
10
class Image
11
{
12
    use Traits\ImageFilter;
13
14
    public $source;
15
    private $destination;
16
17
    private $width;
18
    private $height;
19
    private int $orientation = 0;
20
21
    public function load($filename)
22
    {
23
        if (is_file($filename) && ($imgString = file_get_contents($filename))) {
24
            $imgString = @imagecreatefromstring($imgString);
25
            if ($imgString !== false) {
26
                $this->source = $imgString;
27
                $this->destination = $this->source;
28
                $this->width = imagesx($this->source);
29
                $this->height = imagesy($this->source);
30
                $this->orientation = 0;
31
                if (is_callable('exif_read_data')) {
32
                    $exif = exif_read_data($filename, 'IFD0');
33
                    $exifOrientation = $exif['Orientation'] ?? 0;
34
35
                    if (in_array($exifOrientation, [3, 6, 8])) {
36
                        $this->orientation = $exifOrientation;
37
                    }
38
                }
39
                return $this;
40
            }
41
42
            throw new InvalidArgumentException(
43
                'Cannot load ' . $filename . ', not an image'
44
            );
45
        }
46
47
        throw new InvalidArgumentException(
48
            'Cannot load ' . $filename . ', file unreadable'
49
        );
50
    }
51
52
    public function getWidth()
53
    {
54
        return $this->width;
55
    }
56
57
    public function getHeight()
58
    {
59
        return $this->height;
60
    }
61
62
    public function getResource()
63
    {
64
        return $this->source;
65
    }
66
67
    public function setResource($source)
68
    {
69
        if (!is_resource($source) && !($source instanceof \GdImage)) {
70
            throw new InvalidArgumentException("Invalid source");
71
        }
72
73
        $this->source = $source;
74
        $this->width = imagesx($this->source);
75
        $this->height = imagesy($this->source);
76
77
        return $this;
78
    }
79
80
    public function create(int $width, int $height)
81
    {
82
        $this->source = imagecreatetruecolor($width, $height);
83
        $this->destination = $this->source;
84
        $this->width = $width;
85
        $this->height = $height;
86
87
        return $this;
88
    }
89
90
    /**
91
     * Fill an image with a color
92
     *
93
     * @param integer $pointX  X point coordinate
94
     * @param integer $pointY  Y point coordinate
95
     * @param array   $color   RGB color
96
     *
97
     */
98
    public function fill(int $pointX, int $pointY, $color = [0, 0, 0])
99
    {
100
        $color = imagecolorallocate(
101
            $this->destination,
102
            $color[0],
103
            $color[1],
104
            $color[2]
105
        );
106
107
        imagefill($this->destination, $pointX, $pointY, $color);
108
109
        return $this->chain();
110
    }
111
112
    /**
113
     * Return true if image is in portrait mode
114
     *
115
     * @return boolean
116
     */
117
    public function isPortrait(): bool
118
    {
119
        return $this->width < $this->height;
120
    }
121
122
    /**
123
     * Return true if image is in landscape mode
124
     *
125
     * @return boolean
126
     */
127
    public function isLandscape(): bool
128
    {
129
        return $this->height < $this->width;
130
    }
131
132
    /**
133
     * Apply modification to destination image
134
     *
135
     */
136
    public function chain(): self
137
    {
138
        $this->source = $this->destination;
139
        $this->width = imagesx($this->source);
140
        $this->height = imagesy($this->source);
141
142
        return $this;
143
    }
144
145
    public function resize($width = null, $height = null)
146
    {
147
        if ($this->source) {
148
            if ($width == null) {
149
                $width = intval(
150
                    round(($height / $this->height) * $this->width, 0)
151
                );
152
            } elseif ($height == null) {
153
                $height = intval(
154
                    round(($width / $this->width) * $this->height, 0)
155
                );
156
            }
157
158
            $this->destination = imagecreatetruecolor($width, $height);
159
            if ($this->destination === false) {
160
                throw new RuntimeException("Can't create destination image");
161
            }
162
            imagecopyresampled(
163
                $this->destination,
164
                $this->source,
165
                0,
166
                0,
167
                0,
168
                0,
169
                $width,
170
                $height,
171
                $this->width,
172
                $this->height
173
            );
174
175
            if ($this->orientation == 3) {
176
                $this->destination = imagerotate($this->destination, 180, 0);
177
            }
178
            if ($this->orientation == 8) {
179
                $this->destination = imagerotate($this->destination, 90, 0);
180
            }
181
            if ($this->orientation == 6) {
182
                $this->destination = imagerotate($this->destination, -90, 0);
183
            }
184
185
            return $this->chain();
186
        }
187
        return $this;
188
    }
189
190
    public function crop($width, $height)
191
    {
192
        $centerX = round($this->width / 2);
193
        $centerY = round($this->height / 2);
194
195
        $cropWidthHalf = round($width / 2);
196
        $cropHeightHalf = round($height / 2);
197
198
        $x1 = intval(max(0, $centerX - $cropWidthHalf));
199
        $y1 = intval(max(0, $centerY - $cropHeightHalf));
200
201
        $this->destination = imagecreatetruecolor($width, $height);
202
        if ($this->destination === false) {
203
            throw new RuntimeException("Can't create destination image");
204
        }
205
        imagecopy(
206
            $this->destination,
207
            $this->source,
208
            0,
209
            0,
210
            $x1,
211
            $y1,
212
            $width,
213
            $height
214
        );
215
216
        return $this->chain();
217
    }
218
219
    public function resizeCanvas(
220
        $width,
221
        $height,
222
        $position = null,
223
        $color = [0, 0, 0]
224
    ) {
225
        $this->destination = imagecreatetruecolor($width, $height);
226
        if ($this->destination === false) {
227
            throw new RuntimeException("Can't create destination image");
228
        }
229
        $colorRes = imagecolorallocate(
230
            $this->destination,
231
            $color[0],
232
            $color[1],
233
            $color[2]
234
        );
235
        $imageObj = new Image();
236
        $imageObj->width = $width;
237
        $imageObj->height = $height;
238
        imagefill($this->destination, 0, 0, $colorRes);
239
240
        if ($position !== null) {
241
            list($x, $y) = $imageObj->getCoordinatesFromString(
242
                $position,
243
                $this->width,
244
                $this->height
245
            );
246
        } else {
247
            $x = 0;
248
            $y = 0;
249
        }
250
        imagecopy(
251
            $this->destination,
252
            $this->source,
253
            $x,
254
            $y,
255
            0,
256
            0,
257
            $this->width,
258
            $this->height
259
        );
260
261
        return $this->chain();
262
    }
263
264
    public function rotate()
265
    {
266
    }
267
268
    public function mirror()
269
    {
270
    }
271
272
    public function flip()
273
    {
274
    }
275
276
    public function merge(
277
        $source,
278
        $position = null,
279
        $x = null,
280
        $y = null,
281
        $percent = 100
282
    ) {
283
        if ($source instanceof \Suricate\Image) {
284
        } else {
285
            $source = with(new Image())->load($source);
286
        }
287
288
        if ($position !== null) {
289
            list($x, $y) = $this->getCoordinatesFromString(
290
                $position,
291
                $source->width,
292
                $source->height
293
            );
294
        }
295
        $x = $x !== null ? $x : 0;
296
        $y = $y !== null ? $y : 0;
297
298
        // Handle transparent image
299
        // creating a cut resource
300
        $cut = imagecreatetruecolor($source->getWidth(), $source->getHeight());
301
        if ($cut === false) {
302
            throw new RuntimeException("Can't create destination image");
303
        }
304
        // copying relevant section from background to the cut resource
305
        imagecopy(
306
            $cut,
307
            $this->destination,
308
            0,
309
            0,
310
            $x,
311
            $y,
312
            $source->getWidth(),
313
            $source->getHeight()
314
        );
315
316
        // copying relevant section from watermark to the cut resource
317
        imagecopy(
318
            $cut,
319
            $source->source,
320
            0,
321
            0,
322
            0,
323
            0,
324
            $source->getWidth(),
325
            $source->getHeight()
326
        );
327
328
        imagecopymerge(
329
            $this->destination,
330
            $cut,
331
            $x,
332
            $y,
333
            0,
334
            0,
335
            $source->getWidth(),
336
            $source->getHeight(),
337
            $percent
338
        );
339
340
        return $this->chain();
341
    }
342
343
    public function writeText($text, $x = 0, $y = 0, \Closure $callback = null)
344
    {
345
        if ($x < 0) {
346
            $x = $this->width + $x;
347
        }
348
        if ($y < 0) {
349
            $y = $this->height + $y;
350
        }
351
        $imageFont = new ImageFont($text);
352
353
        if ($callback != null) {
354
            $callback($imageFont);
355
        }
356
357
        $imageFont->apply($this->source, $x, $y);
358
359
        return $this;
360
    }
361
362
    public function line($x1, $y1, $x2, $y2, \Closure $callback = null)
363
    {
364
        $imageShape = new ImageShape();
365
        $imageShape->setImage($this->source);
366
        if ($callback != null) {
367
            $callback($imageShape);
368
        }
369
370
        $imageShape->drawLine($x1, $y1, $x2, $y2);
371
372
        return $this;
373
    }
374
375
    /**
376
     * Export image
377
     *
378
     * @param string $outputType output format
379
     * @param integer $quality   Output quality, when available
380
     *
381
     * @return void
382
     */
383
    public function export($outputType, $quality = 70)
384
    {
385
        switch ($outputType) {
386
            case 'jpg':
387
            case 'jpeg':
388
                imagejpeg($this->source, null, $quality);
389
                break;
390
            case 'png':
391
                imagepng($this->source);
392
                break;
393
            case 'gif':
394
                imagegif($this->source);
395
                break;
396
            case 'webp':
397
                imagewebp($this->source, null, $quality);
398
                break;
399
            default:
400
                throw new InvalidArgumentException(
401
                    sprintf("Invalid output format %s", $outputType)
402
                );
403
        }
404
    }
405
406
    public function save($filename, $outputType = null, $quality = 70)
407
    {
408
        $result = false;
409
410
        $extension =
411
            $outputType === null
412
            ? pathinfo($filename, PATHINFO_EXTENSION)
413
            : $outputType;
414
415
        if ($extension !== false) {
416
            switch (strtolower($extension)) {
0 ignored issues
show
Bug introduced by
It seems like $extension can also be of type array; however, parameter $string of strtolower() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

416
            switch (strtolower(/** @scrutinizer ignore-type */ $extension)) {
Loading history...
417
                case 'jpg':
418
                case 'jpeg':
419
                    $result = imagejpeg($this->source, $filename, $quality);
420
                    break;
421
                case 'png':
422
                    $result = imagepng($this->source, $filename);
423
                    break;
424
                case 'gif':
425
                    $result = imagegif($this->source, $filename);
426
                    break;
427
                case 'webp':
428
                    $result = imagewebp($this->source, $filename, $quality);
429
                    break;
430
            }
431
        }
432
433
        return $result;
434
    }
435
436
    private function getCoordinatesFromString(
437
        $position,
438
        $offsetWidth = 0,
439
        $offsetHeight = 0
440
    ) {
441
        switch ($position) {
442
            case 'top':
443
                $x = floor($this->width / 2 - $offsetWidth / 2);
444
                $y = 0;
445
                break;
446
            case 'top-right':
447
                $x = $this->width - $offsetWidth;
448
                $y = 0;
449
                break;
450
            case 'left':
451
                $x = 0;
452
                $y = floor($this->height / 2 - $offsetHeight / 2);
453
                break;
454
            case 'center':
455
                $x = floor($this->width / 2 - $offsetWidth / 2);
456
                $y = floor($this->height / 2 - $offsetHeight / 2);
457
                break;
458
            case 'right':
459
                $x = $this->width - $offsetWidth;
460
                $y = floor($this->height / 2 - $offsetHeight / 2);
461
                break;
462
            case 'bottom-left':
463
                $x = 0;
464
                $y = $this->height - $offsetHeight;
465
                break;
466
            case 'bottom':
467
                $x = floor($this->width / 2 - $offsetWidth / 2);
468
                $y = $this->height - $offsetHeight;
469
                break;
470
            case 'bottom-right':
471
                $x = $this->width - $offsetWidth;
472
                $y = $this->height - $offsetHeight;
473
                break;
474
475
            case 'top-left':
476
            default:
477
                $x = 0;
478
                $y = 0;
479
                break;
480
        }
481
482
        return [intval($x), intval($y)];
483
    }
484
}
485