ImageEditor::pasteImage()   B
last analyzed

Complexity

Conditions 6
Paths 32

Size

Total Lines 47
Code Lines 34

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 1 Features 0
Metric Value
c 3
b 1
f 0
dl 0
loc 47
rs 8.5125
cc 6
eloc 34
nc 32
nop 9

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
namespace Gwa\Image;
3
4
/**
5
 * Class containing methods for editing images.
6
 */
7
class ImageEditor
8
{
9
    /**
10
     * @var string
11
     */
12
    private $filepath;
13
14
    /**
15
     * @var int
16
     */
17
    private $type;
18
19
    /**
20
     * @var int
21
     */
22
    private $width;
23
24
    /**
25
     * @var int
26
     */
27
    private $height;
28
29
    /**
30
     * @var string
31
     */
32
    private $mimetype;
33
34
    /**
35
     * @var resource
36
     */
37
    private $resource;
38
39
    const DEFAULT_JPEG_QUALITY = 80;
40
41
    /**
42
     * Constuctor
43
     *
44
     * @param string $filepath Path to an existing image
45
     * @throws InvalidArgumentException
46
     * @throws Exception
47
     */
48
    public function __construct($filepath)
49
    {
50
        // make sure the GD library is installed
51
        if (!function_exists('gd_info')) {
52
            trigger_error('You do not have the GD Library installed.');
53
        }
54
55
        if (!file_exists($filepath)) {
56
            throw new \InvalidArgumentException('File does not exist: ' . $filepath);
57
        }
58
59
        if (!is_readable($filepath)) {
60
            throw new \Exception('File is not readable: ' . $filepath);
61
        }
62
63
        $this->filepath = $filepath;
64
        $this->extractFileData();
65
        $this->createResource();
66
    }
67
68
    /**
69
     * Destructor
70
     */
71
    public function __destruct()
72
    {
73
        if (is_resource($this->resource)) {
74
            imagedestroy($this->resource);
75
        }
76
    }
77
78
    /* resize ---------------- */
79
80
    /**
81
     * Resizes image to be within a maximum width and a maximum height
82
     *
83
     * @param int $maxwidth
84
     * @param int $maxheight
85
     * @return ImageEditor
86
     */
87
    public function resizeToWithin($maxwidth, $maxheight)
88
    {
89
        $dimensions = new Dimensions();
90
91
        if ($newd = $dimensions->resizeToWithin($this->width, $this->height, $maxwidth, $maxheight)) {
92
            $this->resizeToDimensions($newd);
93
        }
94
95
        return $this;
96
    }
97
98
    /**
99
     * Resizes image to an exact width and height, maintaining aspect ratio. Any overhang is cropped.
100
     * If `null` is passed as width or height, image is resized to the new width or height with maintained aspect ratio, without cropping.
101
     *
102
     * @param int|null $width
103
     * @param int|null $height
104
     * @return ImageEditor
105
     */
106
    public function resizeTo($width, $height = null)
107
    {
108
        $dimensions = new Dimensions();
109
110
        if ($newd = $dimensions->resizeTo($this->width, $this->height, $width, $height)) {
111
            $this->resizeToDimensions($newd);
112
            if ($newd->overhang) {
113
                $this->cropFromCenter($width, $height);
114
            }
115
        }
116
117
        return $this;
118
    }
119
120
    private function resizeToDimensions(\stdClass $dimensions)
121
    {
122
        $this->resizeImage($dimensions->width, $dimensions->height);
123
    }
124
125
    private function resizeImage($newwidth, $newheight)
126
    {
127
        $newimage = $this->createImage($newwidth, $newheight);
128
        imagecopyresampled(
129
            $newimage,
130
            $this->resource,
131
            0,
132
            0,
133
            0,
134
            0,
135
            $newwidth,
136
            $newheight,
137
            $this->width,
138
            $this->height
139
        );
140
        $this->setResource($newimage);
141
    }
142
143
    /* crop -------- */
144
145
    /**
146
     * Crops the current image
147
     *
148
     * @param int $x
149
     * @param int $y
150
     * @param int $width
151
     * @param int $height
152
     *
153
     * @return ImageEditor
154
     */
155
    public function crop($x, $y, $width, $height)
156
    {
157
        // check that crop is within bounds of image
158
        if ($x + $width > $this->width || $y + $height > $this->height) {
159
            throw new \InvalidArgumentException('crop out of bounds');
160
        }
161
162
        $newimage = $this->createImage($width, $height);
163
        imagecopy(
164
            $newimage,
165
            $this->resource,
166
            0,
167
            0,
168
            $x,
169
            $y,
170
            $width,
171
            $height
172
        );
173
        $this->setResource($newimage);
174
175
        return $this;
176
    }
177
178
    /**
179
     * Crops the image from the center
180
     *
181
     * @param int $width
182
     * @param int $height
183
     *
184
     * @return ImageEditor
185
     */
186
    public function cropFromCenter($width, $height)
187
    {
188
        $x = ($this->width / 2) - ($width / 2);
189
        $y = ($this->height / 2) - ($height / 2);
190
191
        return $this->crop($x, $y, $width, $height);
192
    }
193
194
    /* rotation -------- */
195
196
    /**
197
     * @return ImageEditor
198
     */
199
    public function rotateClockwise()
200
    {
201
        return $this->rotate(270);
202
    }
203
204
    /**
205
     * @return ImageEditor
206
     */
207
    public function rotateCounterClockwise()
208
    {
209
        return $this->rotate(90);
210
    }
211
212
    /**
213
     * @return ImageEditor
214
     */
215
    public function rotate180()
216
    {
217
        return $this->rotate(180);
218
    }
219
220
    /**
221
     * @param int $deg
222
     * @return ImageEditor
223
     */
224
    private function rotate($deg)
225
    {
226
        imagealphablending($this->resource, false);
227
        $this->setResource(imagerotate($this->resource, $deg, 0));
228
        return $this;
229
    }
230
231
    /* filter -------- */
232
233
    /**
234
     * @return ImageEditor
235
     */
236
    public function grayscale()
237
    {
238
        imagefilter($this->resource, IMG_FILTER_GRAYSCALE);
239
        return $this;
240
    }
241
242
    /**
243
     * @param int $red [0, +255]
244
     * @param int $green [0, +255]
245
     * @param int $blue [0, +255]
246
     * @param int $alpha [0, 1]
247
     *
248
     * @return ImageEditor
249
     */
250
    public function coloroverlay($red, $green, $blue, $alpha = 50)
251
    {
252
        imagealphablending($this->resource, true);
253
        $color = imagecolorallocatealpha($this->resource, $red, $green, $blue, $alpha * 1.27);
254
        imagefilledrectangle($this->resource, 0, 0, $this->width, $this->height, $color);
255
        imagealphablending($this->resource, false);
256
        return $this;
257
    }
258
259
    /**
260
     * @param int $red [0, 255]
261
     * @param int $green [0, 255]
262
     * @param int $blue [0, 255]
263
     * @param int $alpha [0, 127]
264
     *
265
     * @return ImageEditor
266
     */
267
    public function colorize($red, $green, $blue, $alpha = 0)
268
    {
269
        imagefilter($this->resource, IMG_FILTER_COLORIZE, $red, $green, $blue, $alpha);
270
        return $this;
271
    }
272
273
    /**
274
     * Paste another image onto this one.
275
     * @note Basically a wrapper method for http://www.php.net/manual/en/function.imagecopyresampled.php
276
     *
277
     * @param ImageEditor|string $imageeditor
278
     * @param int $dst_x
279
     * @param int $dst_y
280
     * @param int $dst_w
281
     * @param int $dst_h
282
     * @param int $src_x
283
     * @param int $src_y
284
     * @param int $src_w
285
     * @param int $src_h
286
     */
287
    public function pasteImage(
288
        $imageeditor,
289
        $dst_x = 0,
290
        $dst_y = 0,
291
        $dst_w = null,
292
        $dst_h = null,
293
        $src_x = null,
294
        $src_y = null,
295
        $src_w = null,
296
        $src_h = null
297
    ) {
298
        if (is_string($imageeditor)) {
299
            $imageeditor = new ImageEditor($imageeditor);
300
        }
301
302
        if ($src_w === null) {
303
            $src_w = $imageeditor->getWidth();
304
        }
305
        if ($src_h === null) {
306
            $src_h = $imageeditor->getHeight();
307
        }
308
        if ($dst_w === null) {
309
            $dst_w = $src_w;
310
        }
311
        if ($dst_h === null) {
312
            $dst_h = $src_h;
313
        }
314
315
        imagealphablending($this->resource, true);
316
317
        imagecopyresampled(
318
            $this->resource,
319
            $imageeditor->getResource(),
320
            $dst_x,
321
            $dst_y,
322
            $src_x,
323
            $src_y,
324
            $dst_w,
325
            $dst_h,
326
            $src_w,
327
            $src_h
328
        );
329
330
        imagealphablending($this->resource, false);
331
332
        return $this;
333
    }
334
335
    /* -------- */
336
337
    /**
338
     * "Duplicates" the image.
339
     * Basically unsets the filepath, so we have to use `saveAs()` and not `save()`.
340
     *
341
     * @return ImageEditor
342
     */
343
    public function duplicate()
344
    {
345
        $this->filepath = null;
346
        return $this;
347
    }
348
349
    /**
350
     * Saves the image
351
     *
352
     * @param int $quality 0-100 (only for jpegs)
353
     *
354
     * @return ImageEditor
355
     */
356
    public function save($quality = self::DEFAULT_JPEG_QUALITY)
357
    {
358
        if (!isset($this->filepath)) {
359
            throw new \LogicException('Use saveTo() to save an unnamed file.');
360
        }
361
        return $this->saveAs($this->filepath, $quality);
362
    }
363
364
    /**
365
     * Saves the image under a path
366
     *
367
     * @param string $filepath
368
     * @param int $type IMAGETYPE constant
369
     * @param int $quality 0-100 (only for jpegs)
370
     *
371
     * @return ImageEditor
372
     */
373
    public function saveAs($filepath, $type = null, $quality = self::DEFAULT_JPEG_QUALITY)
374
    {
375
        $this->outputImage($filepath, $type, $quality);
376
        $this->filepath = $filepath;
377
378
        return $this;
379
    }
380
381
    /**
382
     * Outputs the image with the correct header.
383
     *
384
     * @param int $quality 0-100 (only for jpegs)
385
     */
386
    public function output($quality = self::DEFAULT_JPEG_QUALITY)
387
    {
388
        header('Content-type: ' . $this->mimetype);
389
        $this->outputImage(null, $quality);
390
    }
391
392
    /**
393
     * @param string $filepath
394
     * @param int $type IMAGETYPE constant
395
     * @param int $quality 0-100 (only for jpegs)
396
     */
397
    private function outputImage($filepath = null, $type = null, $quality = self::DEFAULT_JPEG_QUALITY)
398
    {
399
        $type = !isset($type) ? $this->type : $type;
400
401
        switch ($type) {
402
            case IMAGETYPE_JPEG:
403
                imagejpeg($this->resource, $filepath, $quality);
404
                break;
405
406
            case IMAGETYPE_PNG:
407
                imagepng($this->resource, $filepath);
408
                break;
409
410
            case IMAGETYPE_GIF:
411
                imagegif($this->resource, $filepath);
412
                break;
413
        }
414
    }
415
416
    /* -------- */
417
418
    /**
419
     * Retrieves format.
420
     */
421
    private function extractFileData()
422
    {
423
        if (!$this->type = exif_imagetype($this->filepath)) {
424
            throw new \Exception('Wrong file type');
425
        }
426
427
        $mimetypes = array(
428
            IMAGETYPE_GIF  => 'image/gif',
429
            IMAGETYPE_JPEG => 'image/jpeg',
430
            IMAGETYPE_PNG  => 'image/png'
431
        );
432
433
        if (!array_key_exists($this->type, $mimetypes)) {
434
            throw new \Exception('Unsupported image type');
435
        }
436
437
        $this->mimetype = $mimetypes[$this->type];
438
    }
439
440
    private function createResource()
441
    {
442
        switch ($this->type) {
443
            case IMAGETYPE_GIF:
444
                $this->setResource(imagecreatefromgif($this->filepath));
445
                break;
446
            case IMAGETYPE_JPEG:
447
                $this->setResource(imagecreatefromjpeg($this->filepath));
448
                break;
449
            case IMAGETYPE_PNG:
450
                $this->setResource(imagecreatefrompng($this->filepath));
451
                break;
452
        }
453
    }
454
455
    /**
456
     * @param resource $resource
457
     */
458
    private function setResource($resource)
459
    {
460
        if (is_resource($this->resource)) {
461
            imagedestroy($this->resource);
462
        }
463
464
        $this->width = imagesx($resource);
465
        $this->height = imagesy($resource);
466
467
        imagealphablending($resource, false);
468
        imagesavealpha($resource, true);
469
470
        $this->resource = $resource;
471
    }
472
473
    /**
474
     * @param int $width
475
     * @param int $height
476
     *
477
     * @return resource
478
     */
479
    private function createImage($width, $height)
480
    {
481
        $resource = imagecreatetruecolor($width, $height);
482
483
        imagealphablending($resource, false);
484
        imagesavealpha($resource, true);
485
486
        return $resource;
487
    }
488
489
    /* -------- GETTER / SETTERS -------- */
490
491
    /**
492
     * Gets format of this image [IMAGETYPE_GIF|IMAGETYPE_GIF|IMAGETYPE_GIF]
493
     *
494
     * @return int
495
     */
496
    public function getType()
497
    {
498
        return $this->type;
499
    }
500
501
    /**
502
     * Gets mimetype
503
     *
504
     * @return string
505
     */
506
    public function getMimeType()
507
    {
508
        return $this->mimetype;
509
    }
510
511
    /**
512
     * Returns the width.
513
     *
514
     * @return int
515
     */
516
    public function getWidth()
517
    {
518
        return $this->width;
519
    }
520
521
    /**
522
     * Returns the height.
523
     *
524
     * @return int
525
     */
526
    public function getHeight()
527
    {
528
        return $this->height;
529
    }
530
531
    /**
532
     * @return resource
533
     */
534
    public function getResource()
535
    {
536
        return $this->resource;
537
    }
538
}
539