Passed
Push — release/0.4.4 ( f80651...b3ee47 )
by Mathieu
03:07
created

Image::create()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

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