Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
91.82% covered (success)
91.82%
247 / 269
78.57% covered (success)
78.57%
33 / 42
CRAP
0.00% covered (danger)
0.00%
0 / 1
Imagick
91.82% covered (success)
91.82%
247 / 269
78.57% covered (success)
78.57%
33 / 42
150.72
0.00% covered (danger)
0.00%
0 / 1
 createResource
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 load
81.48% covered (success)
81.48%
22 / 27
0.00% covered (danger)
0.00%
0 / 1
17.63
 loadFromString
65.00% covered (warning)
65.00%
13 / 20
0.00% covered (danger)
0.00%
0 / 1
16.19
 create
92.86% covered (success)
92.86%
13 / 14
0.00% covered (danger)
0.00%
0 / 1
7.02
 createIndex
93.75% covered (success)
93.75%
15 / 16
0.00% covered (danger)
0.00%
0 / 1
7.01
 addImage
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
3
 hasImages
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getImages
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 rebuildImages
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 setResolution
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 setImageColorspace
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 setCompression
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 setImageFilter
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 setImageBlur
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 getNumberOfImages
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getCompression
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getImageFilter
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getImageBlur
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 resizeToWidth
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
 resizeToHeight
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
 resize
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 scale
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 resizeImage
72.73% covered (success)
72.73%
8 / 11
0.00% covered (danger)
0.00%
0 / 1
5.51
 crop
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 cropThumb
100.00% covered (success)
100.00%
19 / 19
100.00% covered (success)
100.00%
1 / 1
6
 cropImage
90.91% covered (success)
90.91%
10 / 11
0.00% covered (danger)
0.00%
0 / 1
3.01
 cropThumbnailImage
90.00% covered (success)
90.00%
9 / 10
0.00% covered (danger)
0.00%
0 / 1
3.01
 rotate
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
 flip
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 flop
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 adjust
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
3
 draw
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
3
 effect
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
3
 filter
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
3
 layer
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
3
 type
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
3
 convert
90.00% covered (success)
90.00%
18 / 20
0.00% covered (danger)
0.00%
0 / 1
9.08
 writeToFile
90.91% covered (success)
90.91%
10 / 11
0.00% covered (danger)
0.00%
0 / 1
9.06
 outputToHttp
100.00% covered (success)
100.00%
11 / 11
100.00% covered (success)
100.00%
1 / 1
10
 destroy
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
4
 createColor
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
4
 __toString
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
1<?php
2/**
3 * Pop PHP Framework (http://www.popphp.org/)
4 *
5 * @link       https://github.com/popphp/popphp-framework
6 * @author     Nick Sagona, III <dev@nolainteractive.com>
7 * @copyright  Copyright (c) 2009-2023 NOLA Interactive, LLC. (http://www.nolainteractive.com)
8 * @license    http://www.popphp.org/license     New BSD License
9 */
10
11/**
12 * @namespace
13 */
14namespace Pop\Image\Adapter;
15
16use Pop\Image\Adjust;
17use Pop\Image\Color;
18use Pop\Image\Draw;
19use Pop\Image\Effect;
20use Pop\Image\Filter;
21use Pop\Image\Layer;
22use Pop\Image\Type;
23
24/**
25 * Imagick adapter class
26 *
27 * @category   Pop
28 * @package    Pop\Image
29 * @author     Nick Sagona, III <dev@nolainteractive.com>
30 * @copyright  Copyright (c) 2009-2023 NOLA Interactive, LLC. (http://www.nolainteractive.com)
31 * @license    http://www.popphp.org/license     New BSD License
32 * @version    3.4.0
33 */
34class Imagick extends AbstractAdapter
35{
36
37    /**
38     * Image compression
39     * @var int
40     */
41    protected $compression = null;
42
43    /**
44     * Image filter
45     * @var int
46     */
47    protected $imageFilter = \Imagick::FILTER_LANCZOS;
48
49    /**
50     * Image blur
51     * @var float
52     */
53    protected $imageBlur = 1;
54
55    /**
56     * Create the image resource
57     *
58     * @return void
59     */
60    public function createResource()
61    {
62        $this->resource = new \Imagick();
63    }
64
65    /**
66     * Load the image resource from the existing image file
67     *
68     * @param  string $name
69     * @throws Exception
70     * @return Imagick
71     */
72    public function load($name = null)
73    {
74        $filename = null;
75        if (null !== $name) {
76            $filename = ((strpos($name, '[') !== false) && (strpos($name, ']') !== false)) ?
77                substr($name, 0, strpos($name, '[')) : $name;
78            $this->name = $name;
79        }
80
81        if ((null === $this->name) || ((null !== $filename) && !file_exists($filename))) {
82            throw new Exception('Error: The image file has not been passed to the image adapter');
83        }
84
85        if (null !== $this->resource) {
86            $this->resource->readImage($this->name);
87        } else {
88            $this->resource = new \Imagick($this->name);
89        }
90
91        $this->width  = $this->resource->getImageWidth();
92        $this->height = $this->resource->getImageHeight();
93
94        switch ($this->resource->getImageColorspace()) {
95            case \Imagick::COLORSPACE_GRAY:
96                $this->colorspace = self::IMAGE_GRAY;
97                break;
98            case \Imagick::COLORSPACE_RGB:
99            case \Imagick::COLORSPACE_SRGB:
100                $this->colorspace = self::IMAGE_RGB;
101                break;
102            case \Imagick::COLORSPACE_CMYK:
103                $this->colorspace = self::IMAGE_CMYK;
104                break;
105        }
106
107        $this->format = strtolower($this->resource->getImageFormat());
108        if ($this->resource->getImageColors() < 256) {
109            $this->indexed = true;
110        }
111
112        if ((strpos($this->format, 'jp') !== false) && function_exists('exif_read_data')) {
113            $exif = @exif_read_data($this->name);
114            if ($exif !== false) {
115                $this->exif = $exif;
116            }
117        }
118
119        return $this;
120    }
121
122    /**
123     * Load the image resource from data
124     *
125     * @param  string $data
126     * @param  string $name
127     * @return Imagick
128     */
129    public function loadFromString($data, $name = null)
130    {
131        if (null === $this->resource) {
132            $this->resource = new \Imagick();
133        }
134        $this->resource->readImageBlob($data);
135
136        if (null !== $name) {
137            $this->name = $name;
138        }
139
140        switch ($this->resource->getImageColorspace()) {
141            case \Imagick::COLORSPACE_GRAY:
142                $this->colorspace = self::IMAGE_GRAY;
143                break;
144            case \Imagick::COLORSPACE_RGB:
145            case \Imagick::COLORSPACE_SRGB:
146                $this->colorspace = self::IMAGE_RGB;
147                break;
148            case \Imagick::COLORSPACE_CMYK:
149                $this->colorspace = self::IMAGE_CMYK;
150                break;
151        }
152
153        $this->format = strtolower($this->resource->getImageFormat());
154        if ($this->resource->getImageColors() < 256) {
155            $this->indexed = true;
156        }
157
158        if ((strpos($this->format, 'jp') !== false) && function_exists('exif_read_data')) {
159            $exif = @exif_read_data('data://image/jpeg;base64,' . base64_encode($data));
160            if ($exif !== false) {
161                $this->exif = $exif;
162            }
163        }
164
165        return $this;
166    }
167
168    /**
169     * Create a new image resource
170     *
171     * @param  int    $width
172     * @param  int    $height
173     * @param  string $name
174     * @return Imagick
175     */
176    public function create($width = null, $height = null, $name = null)
177    {
178        if ((null !== $width) && (null !== $height)) {
179            $this->width  = $width;
180            $this->height = $height;
181        }
182
183        if (null !== $name) {
184            $this->name = $name;
185        }
186
187        if (null === $this->resource) {
188            $this->resource = new \Imagick();
189        }
190
191        $this->resource->newImage($this->width, $this->height, new \ImagickPixel('white'));
192
193        if (null !== $this->name) {
194            $extension = strtolower(substr($this->name, (strrpos($this->name, '.') + 1)));
195            if (!empty($extension)) {
196                $this->resource->setImageFormat($extension);
197                $this->format = $extension;
198            }
199        }
200
201        return $this;
202    }
203
204    /**
205     * Create a new image resource
206     *
207     * @param  int    $width
208     * @param  int    $height
209     * @param  string $name
210     * @return Imagick
211     */
212    public function createIndex($width = null, $height = null, $name = null)
213    {
214        if ((null !== $width) && (null !== $height)) {
215            $this->width  = $width;
216            $this->height = $height;
217        }
218
219        if (null !== $name) {
220            $this->name = $name;
221        }
222
223        if (null === $this->resource) {
224            $this->resource = new \Imagick();
225        }
226
227        $this->resource->newImage($this->width, $this->height, new \ImagickPixel('white'));
228
229        if (null !== $this->name) {
230            $extension = strtolower(substr($this->name, (strrpos($this->name, '.') + 1)));
231            if (!empty($extension)) {
232                $this->resource->setImageFormat($extension);
233                $this->format = $extension;
234            }
235        }
236
237        $this->resource->setImageType(\Imagick::IMGTYPE_PALETTE);
238        $this->indexed = true;
239
240        return $this;
241    }
242
243    /**
244     * Add image to the image resource
245     *
246     * @param  mixed $image
247     * @param  int   $delay
248     * @return Imagick
249     */
250    public function addImage($image, $delay = null)
251    {
252        if (!($image instanceof \Imagick)) {
253            $image = new \Imagick($image);
254        }
255        if (null !== $delay) {
256            $image->setImageDelay($delay);
257        }
258        $this->resource->addImage($image);
259        return $this;
260    }
261
262    /**
263     * Does image have images
264     *
265     * @return boolean
266     */
267    public function hasImages()
268    {
269        return ($this->resource->getNumberImages() > 0);
270    }
271
272    /**
273     * Get images
274     *
275     * @return array
276     */
277    public function getImages()
278    {
279        return $this->resource->coalesceImages();
280    }
281
282    /**
283     * Get images
284     *
285     * @param  \Imagick $images
286     * @return Imagick
287     */
288    public function rebuildImages(\Imagick $images)
289    {
290        $this->resource = $images->deconstructImages();
291        return $this;
292    }
293
294    /**
295     * Set the image resolution
296     *
297     * @param  int $x
298     * @param  int $y
299     * @return Imagick
300     */
301    public function setResolution($x, $y = null)
302    {
303        if (null === $y) {
304            $y = $x;
305        }
306        $this->resource->setResolution($x, $y);
307        return $this;
308    }
309
310    /**
311     * Set the image colorspace
312     *
313     * @param  int $colorspace
314     * @return Imagick
315     */
316    public function setImageColorspace($colorspace)
317    {
318        $this->resource->setImageColorspace($colorspace);
319        return $this;
320    }
321
322    /**
323     * Set the image compression
324     *
325     * @param  int $compression
326     * @return Imagick
327     */
328    public function setCompression($compression)
329    {
330        $this->compression = $compression;
331        return $this;
332    }
333
334    /**
335     * Set the image filter
336     *
337     * @param  int $filter
338     * @return Imagick
339     */
340    public function setImageFilter($filter)
341    {
342        $this->imageFilter = $filter;
343        return $this;
344    }
345
346    /**
347     * Set the image blur
348     *
349     * @param  float $blur
350     * @return Imagick
351     */
352    public function setImageBlur($blur)
353    {
354        $this->imageBlur = $blur;
355        return $this;
356    }
357
358    /**
359     * Get number of images
360     *
361     * @return int
362     */
363    public function getNumberOfImages()
364    {
365        return $this->resource->getNumberImages();
366    }
367
368    /**
369     * Get the image compression
370     *
371     * @return int
372     */
373    public function getCompression()
374    {
375        return $this->compression;
376    }
377
378    /**
379     * Get the image filter
380     *
381     * @return int
382     */
383    public function getImageFilter()
384    {
385        return $this->imageFilter;
386    }
387
388    /**
389     * Get the image blur
390     *
391     * @return float
392     */
393    public function getImageBlur()
394    {
395        return $this->imageBlur;
396    }
397
398    /**
399     * Resize the image object to the width parameter passed
400     *
401     * @param  int $w
402     * @return Imagick
403     */
404    public function resizeToWidth($w)
405    {
406        $scale        = $w / $this->width;
407        $this->width  = $w;
408        $this->height = round($this->height * $scale);
409
410        return $this->resizeImage($this->width, $this->height, $this->imageFilter, $this->imageBlur);
411    }
412
413    /**
414     * Resize the image object to the height parameter passed
415     *
416     * @param  int $h
417     * @return Imagick
418     */
419    public function resizeToHeight($h)
420    {
421        $scale        = $h / $this->height;
422        $this->height = $h;
423        $this->width  = round($this->width * $scale);
424
425        return $this->resizeImage($this->width, $this->height, $this->imageFilter, $this->imageBlur);
426    }
427
428    /**
429     * Resize the image object, allowing for the largest dimension
430     * to be scaled to the value of the $px argument.
431     *
432     * @param  int $px
433     * @return Imagick
434     */
435    public function resize($px)
436    {
437        $scale        = ($this->width > $this->height) ? ($px / $this->width) : ($px / $this->height);
438        $this->width  = round($this->width * $scale);
439        $this->height = round($this->height * $scale);
440
441        return $this->resizeImage($this->width, $this->height, $this->imageFilter, $this->imageBlur);
442    }
443
444    /**
445     * Scale the image object, allowing for the dimensions to be scaled
446     * proportionally to the value of the $scl argument.
447     *
448     * @param  float $scale
449     * @return Imagick
450     */
451    public function scale($scale)
452    {
453        $this->width  = round($this->width * $scale);
454        $this->height = round($this->height * $scale);
455
456        return $this->resizeImage($this->width, $this->height, $this->imageFilter, $this->imageBlur);
457    }
458
459    /**
460     * Resize image, checking for multiple frames
461     *
462     * @param  int $width
463     * @param  int $height
464     * @param  int $filter
465     * @param  int $blur
466     * @return Imagick
467     */
468    public function resizeImage($width, $height, $filter = null, $blur = null)
469    {
470        if (null === $filter) {
471            $filter = $this->imageFilter;
472        }
473        if (null === $blur) {
474            $blur = $this->imageBlur;
475        }
476        if ($this->resource->getNumberImages() > 0) {
477            $frames = $this->resource->coalesceImages();
478            foreach ($frames as $frame) {
479                $frame->resizeImage($width, $height, $filter, $blur);
480            }
481            $this->resource = $frames->deconstructImages();
482        } else {
483            $this->resource->resizeImage($width, $height, $filter, $blur);
484        }
485
486        return $this;
487    }
488
489    /**
490     * Crop the image object to a image whose dimensions are based on the
491     * value of the $wid and $hgt argument. The optional $x and $y arguments
492     * allow for the adjustment of the crop to select a certain area of the
493     * image to be cropped.
494     *
495     * @param  int $w
496     * @param  int $h
497     * @param  int $x
498     * @param  int $y
499     * @return Imagick
500     */
501    public function crop($w, $h, $x = 0, $y = 0)
502    {
503        $this->width  = $w;
504        $this->height = $h;
505        return $this->cropImage($this->width, $this->height, $x, $y);
506    }
507
508    /**
509     * Crop the image object to a square image whose dimensions are based on the
510     * value of the $px argument. The optional $offset argument allows for the
511     * adjustment of the crop to select a certain area of the image to be cropped.
512     *
513     * @param  int $px
514     * @param  int $offset
515     * @return Imagick
516     */
517    public function cropThumb($px, $offset = null)
518    {
519        $xOffset = 0;
520        $yOffset = 0;
521
522        if (null !== $offset) {
523            if ($this->width > $this->height) {
524                $xOffset = $offset;
525                $yOffset = 0;
526            } else if ($this->width < $this->height) {
527                $xOffset = 0;
528                $yOffset = $offset;
529            }
530        }
531
532        $scale = ($this->width > $this->height) ? ($px / $this->height) : ($px / $this->width);
533
534        $wid = round($this->width * $scale);
535        $hgt = round($this->height * $scale);
536
537        // Create a new image output resource.
538        if (null !== $offset) {
539            $this->resizeImage($wid, $hgt, $this->imageFilter, $this->imageBlur);
540            $this->cropImage($px, $px, $xOffset, $yOffset);
541        } else {
542            $this->cropThumbnailImage($px, $px);
543        }
544
545        $this->width  = $px;
546        $this->height = $px;
547        return $this;
548    }
549
550    /**
551     * Crop image, checking for multiple frames
552     *
553     * @param  int $width
554     * @param  int $height
555     * @param  int $x
556     * @param  int $y
557     * @return Imagick
558     */
559    public function cropImage($width, $height, $x, $y)
560    {
561        if ($this->resource->getNumberImages() > 0) {
562            $frames = $this->resource->coalesceImages();
563            foreach ($frames as $frame) {
564                $frame->setImageBackgroundColor('none');
565                $frame->cropImage($width, $height, $x, $y);
566                $frame->thumbnailImage($width, $height);
567                $frame->setImagePage($width, $height, 0, 0);
568            }
569            $this->resource = $frames->deconstructImages();
570            $this->resource->resizeImage($width, $height, $this->imageFilter, $this->imageBlur);
571        } else {
572            $this->resource->cropImage($width, $height, $x, $y);
573        }
574
575        return $this;
576    }
577
578    /**
579     * Crop image, checking for multiple frames
580     *
581     * @param  int $width
582     * @param  int $height
583     * @return Imagick
584     */
585    public function cropThumbnailImage($width, $height)
586    {
587        if ($this->resource->getNumberImages() > 0) {
588            $frames = $this->resource->coalesceImages();
589            foreach ($frames as $frame) {
590                $frame->setImageBackgroundColor('none');
591                $frame->cropThumbnailImage($width, $height);
592                $frame->thumbnailImage($width, $height);
593                $frame->setImagePage($width, $height, 0, 0);
594            }
595            $this->resource = $frames->deconstructImages();
596        } else {
597            $this->resource->cropThumbnailImage($width, $height);
598        }
599
600        return $this;
601    }
602
603    /**
604     * Rotate the image object
605     *
606     * @param  int                  $degrees
607     * @param  Color\ColorInterface $bgColor
608     * @return Imagick
609     */
610    public function rotate($degrees, Color\ColorInterface $bgColor = null)
611    {
612        $this->resource->rotateImage($this->createColor($bgColor), $degrees);
613        $this->width  = $this->resource->getImageWidth();
614        $this->height = $this->resource->getImageHeight();
615        return $this;
616    }
617
618    /**
619     * Method to flip the image over the x-axis
620     *
621     * @return Imagick
622     */
623    public function flip()
624    {
625        $this->resource->flipImage();
626        return $this;
627    }
628
629    /**
630     * Method to flip the image over the y-axis
631     *
632     * @return Imagick
633     */
634    public function flop()
635    {
636        $this->resource->flopImage();
637        return $this;
638    }
639
640    /**
641     * Get the image adjust object
642     *
643     * @return Adjust\AdjustInterface
644     */
645    public function adjust()
646    {
647        if (null === $this->adjust) {
648            $this->adjust = new Adjust\Imagick($this);
649        }
650        if (null === $this->adjust->getImage()) {
651            $this->adjust->setImage($this);
652        }
653
654        return $this->adjust;
655    }
656
657    /**
658     * Get the image draw object
659     *
660     * @return Draw\DrawInterface
661     */
662    public function draw()
663    {
664        if (null === $this->draw) {
665            $this->draw = new Draw\Imagick($this);
666        }
667        if (null === $this->draw->getImage()) {
668            $this->draw->setImage($this);
669        }
670        return $this->draw;
671    }
672
673    /**
674     * Get the image effect object
675     *
676     * @return Effect\EffectInterface
677     */
678    public function effect()
679    {
680        if (null === $this->effect) {
681            $this->effect = new Effect\Imagick($this);
682        }
683        if (null === $this->effect->getImage()) {
684            $this->effect->setImage($this);
685        }
686        return $this->effect;
687    }
688
689    /**
690     * Get the image filter object
691     *
692     * @return Filter\FilterInterface
693     */
694    public function filter()
695    {
696        if (null === $this->filter) {
697            $this->filter = new Filter\Imagick($this);
698        }
699        if (null === $this->filter->getImage()) {
700            $this->filter->setImage($this);
701        }
702        return $this->filter;
703    }
704
705    /**
706     * Get the image layer object
707     *
708     * @return Layer\LayerInterface
709     */
710    public function layer()
711    {
712        if (null === $this->layer) {
713            $this->layer = new Layer\Imagick($this);
714        }
715        if (null === $this->layer->getImage()) {
716            $this->layer->setImage($this);
717        }
718        return $this->layer;
719    }
720
721    /**
722     * Get the image type object
723     *
724     * @return Type\TypeInterface
725     */
726    public function type()
727    {
728        if (null === $this->type) {
729            $this->type = new Type\Imagick($this);
730        }
731        if (null === $this->type->getImage()) {
732            $this->type->setImage($this);
733        }
734        return $this->type;
735    }
736
737    /**
738     * Convert the image object to another format
739     *
740     * @param  string $to
741     * @return Imagick
742     */
743    public function convert($to)
744    {
745        $to   = strtolower($to);
746        $old  = strtolower($this->format);
747
748        if (($old == 'psd') || ($old == 'tif') || ($old == 'tiff')) {
749            $this->resource->mergeImageLayers(\Imagick::LAYERMETHOD_FLATTEN);
750        }
751
752        $this->resource->setImageFormat($to);
753
754        switch ($to) {
755            case 'jpg':
756            case 'jpeg':
757                $this->format  = 'jpg';
758                $this->indexed = false;
759                break;
760            case 'png':
761                $this->format = 'png';
762                break;
763            case 'gif':
764                $this->format  = 'gif';
765                $this->indexed = true;
766                break;
767            default:
768                $this->format = $to;
769        }
770
771        return $this;
772    }
773
774    /**
775     * Write the image object to a file on disk
776     *
777     * @param  string $to
778     * @param  int    $quality
779     * @throws Exception
780     * @return void
781     */
782    public function writeToFile($to = null, $quality = 100)
783    {
784        if ((null === $this->resource) || ((null !== $this->resource) && ($this->resource->count() == 0))) {
785            throw new Exception('Error: An image resource has not been created or loaded');
786        }
787
788        if (null !== $this->compression) {
789            $this->resource->setImageCompression($this->compression);
790        }
791
792        if (((int)$quality < 0) || ((int)$quality > 100)) {
793            throw new \OutOfRangeException('Error: The quality parameter must be between 0 and 100');
794        }
795
796        $this->resource->setImageCompressionQuality($quality);
797
798        if (null === $to) {
799            $to = (null !== $this->name) ? basename($this->name) : 'pop-image.' . $this->format;
800        } else {
801            $this->name = $to;
802        }
803
804        $this->resource->writeImage($to);
805    }
806
807    /**
808     * Output the image object directly to HTTP
809     *
810     * @param  int     $quality
811     * @param  string  $to
812     * @param  boolean $download
813     * @param  boolean $sendHeaders
814     * @param  array   $headers
815     * @throws Exception
816     * @return void
817     */
818    public function outputToHttp($quality = 100, $to = null, $download = false, $sendHeaders = true, array $headers = [])
819    {
820        if ((null === $this->resource) || ((null !== $this->resource) && ($this->resource->count() == 0))) {
821            throw new Exception('Error: An image resource has not been created or loaded');
822        }
823
824        if (((int)$quality < 0) || ((int)$quality > 100)) {
825            throw new \OutOfRangeException('Error: The quality parameter must be between 0 and 100');
826        }
827
828        if (null !== $this->compression) {
829            $this->resource->setImageCompression($this->compression);
830        }
831
832        $this->resource->setImageCompressionQuality($quality);
833
834        if (null === $to) {
835            $to = (null !== $this->name) ? basename($this->name) : 'pop-image.' . strtolower($this->format);
836        }
837
838        $this->sendHeaders($to, $download, $headers);
839        echo ($this->resource->getNumberImages() > 0) ? $this->resource->getImagesBlob() : $this->resource;
840    }
841
842    /**
843     * Destroy the image object and the related image file directly
844     *
845     * @param  boolean $delete
846     * @return void
847     */
848    public function destroy($delete = false)
849    {
850        if (null !== $this->resource) {
851            $this->resource->clear();
852            $this->resource->destroy();
853        }
854
855        $this->resource = null;
856        clearstatcache();
857
858        // If the $delete flag is passed, delete the image file.
859        if (($delete) && file_exists($this->name)) {
860            unlink($this->name);
861        }
862    }
863
864    /**
865     * Create and return a color.
866     *
867     * @param  Color\ColorInterface $color
868     * @param  int                  $alpha
869     * @return \ImagickPixel
870     */
871    public function createColor(Color\ColorInterface $color = null, $alpha = 100)
872    {
873        if (null === $color) {
874            $color = new Color\Rgb(0, 0, 0);
875        }
876
877        if (!($color instanceof Color\Rgb)) {
878            $color = $color->toRgb();
879        }
880
881        return ((int)$alpha < 100) ?
882            new \ImagickPixel('rgba(' . $color . ',' . (int)$alpha . ')') : new \ImagickPixel('rgb(' . $color . ')');
883    }
884
885    /**
886     * Output the image
887     *
888     * @return string
889     */
890    public function __toString()
891    {
892        $this->sendHeaders();
893        echo ($this->resource->getNumberImages() > 0) ? $this->resource->getImagesBlob() : $this->resource;
894        return '';
895    }
896
897}