Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
94.39% covered (success)
94.39%
269 / 285
83.33% covered (success)
83.33%
25 / 30
CRAP
0.00% covered (danger)
0.00%
0 / 1
Gd
94.39% covered (success)
94.39%
269 / 285
83.33% covered (success)
83.33%
25 / 30
123.59
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
94.74% covered (success)
94.74%
18 / 19
0.00% covered (danger)
0.00%
0 / 1
9.01
 loadFromString
100.00% covered (success)
100.00%
11 / 11
100.00% covered (success)
100.00%
1 / 1
6
 create
94.12% covered (success)
94.12%
16 / 17
0.00% covered (danger)
0.00%
0 / 1
8.01
 createIndex
92.31% covered (success)
92.31%
12 / 13
0.00% covered (danger)
0.00%
0 / 1
6.02
 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%
5 / 5
100.00% covered (success)
100.00%
1 / 1
2
 scale
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
 crop
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
1 / 1
2
 cropThumb
100.00% covered (success)
100.00%
26 / 26
100.00% covered (success)
100.00%
1 / 1
8
 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%
8 / 8
100.00% covered (success)
100.00%
1 / 1
1
 flop
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
1
 adjust
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 draw
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 effect
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 filter
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 layer
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 type
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 convert
100.00% covered (success)
100.00%
29 / 29
100.00% covered (success)
100.00%
1 / 1
12
 writeToFile
100.00% covered (success)
100.00%
11 / 11
100.00% covered (success)
100.00%
1 / 1
6
 outputToRawString
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
2
 outputToHttp
100.00% covered (success)
100.00%
12 / 12
100.00% covered (success)
100.00%
1 / 1
7
 destroy
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
4
 createColor
92.86% covered (success)
92.86%
13 / 14
0.00% covered (danger)
0.00%
0 / 1
7.02
 __toString
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
 copyImage
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
2
 parseImage
63.64% covered (warning)
63.64%
21 / 33
0.00% covered (danger)
0.00%
0 / 1
23.42
 generateImage
100.00% covered (success)
100.00%
12 / 12
100.00% covered (success)
100.00%
1 / 1
6
1<?php
2declare(strict_types=1);
3/**
4 * Pop PHP Framework (https://www.popphp.org/)
5 *
6 * @link       https://github.com/popphp/popphp-framework
7 * @author     Nick Sagona, III <dev@noladev.com>
8 * @copyright  Copyright (c) 2009-2027 NOLA Interactive, LLC.
9 * @license    https://www.popphp.org/license     New BSD License
10 */
11
12/**
13 * @namespace
14 */
15namespace Pop\Image\Adapter;
16
17use Pop\Image\Adjust;
18use Pop\Color\Color;
19use Pop\Image\Draw;
20use Pop\Image\Effect;
21use Pop\Image\Filter;
22use Pop\Image\Layer;
23use Pop\Image\Type;
24
25/**
26 * Gd adapter class
27 *
28 * @category   Pop
29 * @package    Pop\Image
30 * @author     Nick Sagona, III <dev@noladev.com>
31 * @copyright  Copyright (c) 2009-2027 NOLA Interactive, LLC.
32 * @license    https://www.popphp.org/license     New BSD License
33 * @version    5.0.0
34 */
35class Gd extends AbstractAdapter
36{
37
38    /**
39     * Create the image resource
40     *
41     * @return void
42     */
43    public function createResource(): void
44    {
45        $this->resource = null;
46    }
47
48    /**
49     * Load the image resource from the existing image file
50     *
51     * @param  ?string $name
52     * @throws Exception
53     * @return Gd
54     */
55    public function load(?string $name = null): Gd
56    {
57        if ($name !== null) {
58            $this->name = $name;
59        }
60
61        if (!file_exists($this->name)) {
62            throw new Exception('Error: The image file has not been passed to the image adapter');
63        }
64
65        if (stripos($this->name, '.gif') !== false) {
66            $this->resource = imagecreatefromgif($this->name);
67        } else if (stripos($this->name, '.jp') !== false) {
68            $this->resource = imagecreatefromjpeg($this->name);
69            if (function_exists('exif_read_data')) {
70                $exif = @exif_read_data($this->name);
71                if ($exif !== false) {
72                    $this->exif = $exif;
73                }
74            }
75        } else if (stripos($this->name, '.png') !== false) {
76            $this->resource = imagecreatefrompng($this->name);
77        } else {
78            throw new Exception('Error: The image file must be a GIF, PNG or JPG');
79        }
80
81        if ($this->resource === false) {
82            throw new Exception('Error: Unable to load image resource');
83        }
84
85        $this->parseImage(getimagesize($this->name), file_get_contents($this->name));
86        return $this;
87    }
88
89    /**
90     * Load the image resource from data
91     *
92     * @param  string  $data
93     * @param  ?string $name
94     * @throws Exception
95     * @return Gd
96     */
97    public function loadFromString(string $data, ?string $name = null): Gd
98    {
99        if ($name !== null) {
100            $this->name = $name;
101        }
102
103        $this->resource = @imagecreatefromstring($data);
104
105        if ($this->resource === false) {
106            throw new Exception('Error: Unable to load image resource');
107        }
108
109        $this->parseImage(getimagesizefromstring($data), $data);
110
111        if ((str_contains($this->format, 'jp')) && function_exists('exif_read_data')) {
112            $exif = @exif_read_data('data://image/jpeg;base64,' . base64_encode($data));
113            if ($exif !== false) {
114                $this->exif = $exif;
115            }
116        }
117
118        return $this;
119    }
120
121    /**
122     * Create a new image resource
123     *
124     * @param  ?int    $width
125     * @param  ?int    $height
126     * @param  ?string $name
127     * @throws Exception
128     * @return Gd
129     */
130    public function create(?int $width = null, ?int $height = null, ?string $name = null): Gd
131    {
132        if (($width !== null) && ($height !== null)) {
133            $this->width  = $width;
134            $this->height = $height;
135        }
136
137        if ($name !== null) {
138            $this->name = $name;
139        }
140
141        if (stripos($this->name, '.gif') !== false) {
142            $this->resource = imagecreate($this->width, $this->height);
143            $this->indexed  = true;
144            $this->format   = 'gif';
145        } else {
146            $this->resource = imagecreatetruecolor($this->width, $this->height);
147            if (stripos($this->name, '.png') !== false) {
148                $this->format = 'png';
149            } else if (stripos($this->name, '.jp') !== false) {
150                $this->format = 'jpg';
151            }
152        }
153
154        if ($this->resource === false) {
155            throw new Exception('Error: Unable to create image resource');
156        }
157
158        return $this;
159    }
160
161    /**
162     * Create a new image resource
163     *
164     * @param  ?int    $width
165     * @param  ?int    $height
166     * @param  ?string $name
167     * @throws Exception
168     * @return Gd
169     */
170    public function createIndex(?int $width = null, ?int $height = null, ?string $name = null): Gd
171    {
172        if (($width !== null) && ($height !== null)) {
173            $this->width  = $width;
174            $this->height = $height;
175        }
176
177        if ($name !== null) {
178            $this->name = $name;
179        }
180
181        $this->resource = imagecreate($this->width, $this->height);
182        $this->indexed  = true;
183
184        if (stripos($this->name, '.png') !== false) {
185            $this->format = 'png';
186        } else {
187            $this->format = 'gif';
188        }
189
190        if ($this->resource === false) {
191            throw new Exception('Error: Unable to create image resource');
192        }
193
194        return $this;
195    }
196
197    /**
198     * Resize the image object to the width parameter passed
199     *
200     * @param  int $w
201     * @return Gd
202     */
203    public function resizeToWidth(int $w): Gd
204    {
205        $scale = $w / $this->width;
206        $h     = (int)round($this->height * $scale);
207        $this->copyImage($w, $h);
208
209        return $this;
210    }
211
212    /**
213     * Resize the image object to the height parameter passed
214     *
215     * @param  int $h
216     * @return Gd
217     */
218    public function resizeToHeight(int $h): Gd
219    {
220        $scale = $h / $this->height;
221        $w     = (int)round($this->width * $scale);
222        $this->copyImage($w, $h);
223
224        return $this;
225    }
226
227    /**
228     * Resize the image object, allowing for the largest dimension
229     * to be scaled to the value of the $px argument.
230     *
231     * @param  int $px
232     * @return Gd
233     */
234    public function resize(int $px): Gd
235    {
236        $scale = ($this->width > $this->height) ? ($px / $this->width) : ($px / $this->height);
237        $w     = (int)round($this->width * $scale);
238        $h     = (int)round($this->height * $scale);
239        $this->copyImage($w, $h);
240
241        return $this;
242    }
243
244    /**
245     * Scale the image object, allowing for the dimensions to be scaled
246     * proportionally to the value of the $scale argument.
247     *
248     * @param  float $scale
249     * @return Gd
250     */
251    public function scale(float $scale): Gd
252    {
253        $w = (int)round($this->width * $scale);
254        $h = (int)round($this->height * $scale);
255        $this->copyImage($w, $h);
256
257        return $this;
258    }
259
260    /**
261     * Crop the image object to a image whose dimensions are based on the
262     * value of the $wid and $hgt argument. The optional $x and $y arguments
263     * allow for the adjustment of the crop to select a certain area of the
264     * image to be cropped.
265     *
266     * @param  int $w
267     * @param  int $h
268     * @param  int $x
269     * @param  int $y
270     * @return Gd
271     */
272    public function crop(int $w, int $h, int $x = 0, int $y = 0): Gd
273    {
274        $result = imagecreatetruecolor($w, $h);
275        imagecopyresampled(
276            $result, $this->resource, 0, 0, $x, $y, $this->width, $this->height, $this->width, $this->height
277        );
278
279        if ($this->indexed) {
280            imagetruecolortopalette($result, false, 255);
281        }
282
283        $this->resource = $result;
284        $this->width    = imagesx($this->resource);
285        $this->height   = imagesy($this->resource);
286
287        return $this;
288    }
289
290    /**
291     * Crop the image object to a square image whose dimensions are based on the
292     * value of the $px argument. The optional $offset argument allows for the
293     * adjustment of the crop to select a certain area of the image to be cropped.
294     *
295     * @param  int  $px
296     * @param  ?int $offset
297     * @return Gd
298     */
299    public function cropThumb(int $px, ?int $offset = null): Gd
300    {
301        $xOffset = 0;
302        $yOffset = 0;
303        $scale   = ($this->width > $this->height) ? ($px / $this->height) : ($px / $this->width);
304        $w       = (int)round($this->width * $scale);
305        $h       = (int)round($this->height * $scale);
306
307        if ($offset !== null) {
308            if ($this->width > $this->height) {
309                $xOffset = $offset;
310                $yOffset = 0;
311            } else if ($this->width < $this->height) {
312                $xOffset = 0;
313                $yOffset = $offset;
314            }
315        } else {
316            if ($this->width > $this->height) {
317                $xOffset = (int)round(($this->width - $this->height) / 2);
318                $yOffset = 0;
319            } else if ($this->width < $this->height) {
320                $xOffset = 0;
321                $yOffset = (int)round(($this->height - $this->width) / 2);
322            }
323        }
324
325        $result = imagecreatetruecolor($px, $px);
326        imagecopyresampled($result, $this->resource, 0, 0, $xOffset, $yOffset, $w, $h, $this->width, $this->height);
327
328        if ($this->indexed) {
329            imagetruecolortopalette($result, false, 255);
330        }
331
332        $this->resource = $result;
333        $this->width    = imagesx($this->resource);
334        $this->height   = imagesy($this->resource);
335
336        return $this;
337    }
338
339    /**
340     * Rotate the image object
341     *
342     * @param  int                   $degrees
343     * @param  ?Color\ColorInterface $bgColor
344     * @param  ?int                  $alpha
345     * @return Gd
346     */
347    public function rotate(int $degrees, ?Color\ColorInterface $bgColor = null, ?int $alpha = null): Gd
348    {
349        $this->resource = imagerotate($this->resource, $degrees, $this->createColor($bgColor, $alpha));
350        $this->width    = imagesx($this->resource);
351        $this->height   = imagesy($this->resource);
352        return $this;
353    }
354
355    /**
356     * Method to flip the image over the x-axis
357     *
358     * @return Gd
359     */
360    public function flip(): Gd
361    {
362        $curWidth     = $this->width;
363        $curHeight    = $this->height;
364        $srcX         = 0;
365        $srcY         = $this->height - 1; // Compensate for a 1-pixel space on the flipped image
366        $this->height = 0 - $this->height;
367
368        $this->copyImage($curWidth, $curHeight, $srcX , $srcY);
369        $this->height = abs($this->height);
370
371        return $this;
372    }
373
374    /**
375     * Method to flip the image over the y-axis
376     *
377     * @return Gd
378     */
379    public function flop(): Gd
380    {
381        $curWidth    = $this->width;
382        $curHeight   = $this->height;
383        $srcX        = $this->width - 1; // Compensate for a 1-pixel space on the flipped image
384        $srcY        = 0;
385        $this->width = 0 - $this->width;
386
387        $this->copyImage($curWidth, $curHeight, $srcX , $srcY);
388        $this->width = abs($this->width);
389
390        return $this;
391    }
392
393    /**
394     * Get the image adjust object
395     *
396     * @return Adjust\AdjustInterface
397     */
398    public function adjust(): Adjust\AdjustInterface
399    {
400        if ($this->adjust === null) {
401            $this->adjust = new Adjust\Gd($this);
402        }
403        return $this->adjust;
404    }
405
406    /**
407     * Get the image draw object
408     *
409     * @return Draw\DrawInterface
410     */
411    public function draw(): Draw\DrawInterface
412    {
413        if ($this->draw === null) {
414            $this->draw = new Draw\Gd($this);
415        }
416        return $this->draw;
417    }
418
419    /**
420     * Get the image effect object
421     *
422     * @return Effect\EffectInterface
423     */
424    public function effect(): Effect\EffectInterface
425    {
426        if ($this->effect === null) {
427            $this->effect = new Effect\Gd($this);
428        }
429        return $this->effect;
430    }
431
432    /**
433     * Get the image filter object
434     *
435     * @return Filter\FilterInterface
436     */
437    public function filter(): Filter\FilterInterface
438    {
439        if ($this->filter === null) {
440            $this->filter = new Filter\Gd($this);
441        }
442        return $this->filter;
443    }
444
445    /**
446     * Get the image layer object
447     *
448     * @return Layer\LayerInterface
449     */
450    public function layer(): Layer\LayerInterface
451    {
452        if ($this->layer === null) {
453            $this->layer = new Layer\Gd($this);
454        }
455        return $this->layer;
456    }
457
458    /**
459     * Get the image type object
460     *
461     * @return Type\TypeInterface
462     */
463    public function type(): Type\TypeInterface
464    {
465        if ($this->type === null) {
466            $this->type = new Type\Gd($this);
467        }
468        return $this->type;
469    }
470
471    /**
472     * Convert the image object to another format
473     *
474     * @param  string $type
475     * @throws Exception
476     * @return Gd
477     */
478    public function convert(string $type): Gd
479    {
480        $type = strtolower($type);
481
482        if (($type != 'jpg') && ($type != 'jpeg') && ($type != 'gif') && ($type != 'png')) {
483            throw new Exception('Error: The image type must be a GIF, PNG or JPG');
484        }
485
486        if ($this->resource === null) {
487            throw new Exception('Error: An image resource has not been created or loaded');
488        }
489
490        switch ($type) {
491            case 'jpg':
492            case 'jpeg':
493                $this->format  = 'jpg';
494                $this->indexed = false;
495                break;
496            case 'png':
497                $this->format = 'png';
498                break;
499            case 'gif':
500                $this->format  = 'gif';
501                $this->indexed = true;
502                break;
503        }
504
505        if (strpos($this->name, '.') !== false) {
506            $this->name = substr($this->name, 0, (strrpos($this->name, '.') + 1)) . $this->format;
507        }
508
509        $result = imagecreatetruecolor($this->width, $this->height);
510        imagecopyresampled(
511            $result, $this->resource, 0, 0, 0, 0, $this->width, $this->height, $this->width, $this->height
512        );
513
514        if ($this->indexed) {
515            imagetruecolortopalette($result, false, 255);
516        }
517
518        $this->resource = $result;
519        $this->width    = imagesx($this->resource);
520        $this->height   = imagesy($this->resource);
521
522        return $this;
523    }
524
525    /**
526     * Write the image object to a file on disk
527     *
528     * @param  ?string $to
529     * @param  ?int    $quality
530     * @throws Exception
531     * @return void
532     */
533    public function writeToFile(?string $to = null, ?int $quality = null): void
534    {
535        if ($this->resource === null) {
536            throw new Exception('Error: An image resource has not been created or loaded');
537        }
538
539        if ($quality !== null) {
540            $this->setQuality($quality);
541        }
542
543        if (((int)$this->quality < 0) || ((int)$this->quality > 100)) {
544            throw new \OutOfRangeException('Error: The quality parameter must be between 0 and 100');
545        }
546
547        $this->format = strtolower($this->format);
548
549        if ($to === null) {
550            $to = basename($this->name);
551        } else {
552            $this->name = $to;
553        }
554
555        $this->generateImage((int)$this->quality, $to);
556    }
557
558    /**
559     * Output the image object to a raw string
560     *
561     * @param  int $quality
562     * @throws Exception
563     * @return string|false
564     */
565    public function outputToRawString(int $quality = 100): string|false
566    {
567        if ($this->resource === null) {
568            throw new Exception('Error: An image resource has not been created or loaded');
569        }
570
571        ob_start();
572        $this->generateImage($quality);
573        return ob_get_clean();
574    }
575
576    /**
577     * Output the image object directly to HTTP
578     *
579     * @param  ?int    $quality
580     * @param  ?string $to
581     * @param  bool    $download
582     * @param  bool    $sendHeaders
583     * @param  array   $headers
584     * @throws Exception
585     * @return void
586     */
587    public function outputToHttp(
588        ?int $quality = null, ?string $to = null, bool $download = false, bool $sendHeaders = true, array $headers = []
589    ): void
590    {
591        if ($this->resource === null) {
592            throw new Exception('Error: An image resource has not been created or loaded');
593        }
594
595        if ($quality !== null) {
596            $this->setQuality($quality);
597        }
598
599        if (((int)$this->quality < 0) || ((int)$this->quality > 100)) {
600            throw new \OutOfRangeException('Error: The quality parameter must be between 0 and 100');
601        }
602
603        $this->format = strtolower($this->format);
604
605        if ($to === null) {
606            $to = basename($this->name);
607        }
608
609        if ($sendHeaders) {
610            $this->sendHeaders($to, $download, $headers);
611        }
612        $this->generateImage((int)$this->quality);
613    }
614
615    /**
616     * Destroy the image object and the related image file directly
617     *
618     * @param  bool $delete
619     * @return void
620     */
621    public function destroy(bool $delete = false): void
622    {
623        // Destroy the image resource.
624        if ($this->resource instanceof \GdImage) {
625            imagedestroy($this->resource);
626        }
627
628        $this->resource = null;
629        clearstatcache();
630
631        // If the $delete flag is passed, delete the image file.
632        if (($delete) && file_exists($this->name)) {
633            unlink($this->name);
634        }
635    }
636
637    /**
638     * Create and return a color
639     *
640     * @param  ?Color\ColorInterface $color
641     * @param  ?int                  $alpha
642     * @throws Exception
643     * @return mixed
644     */
645    public function createColor(?Color\ColorInterface $color = null, ?int $alpha = null): mixed
646    {
647        if ($color === null) {
648            $color = new Color\Rgb(0, 0, 0);
649        }
650
651        if (!($color instanceof Color\Rgb)) {
652            if (!method_exists($color, 'toRgb')) {
653                throw new Exception('Error: The color object does not support conversion to RGB.');
654            }
655            $color = $color->toRgb();
656        }
657
658        $r = $color->getR();
659        $g = $color->getG();
660        $b = $color->getB();
661
662        if ($alpha !== null) {
663            if (((int)$alpha < 0) || ((int)$alpha > 127)) {
664                throw new \OutOfRangeException('Error: The alpha parameter must be between 0 and 127');
665            }
666            return imagecolorallocatealpha($this->resource, (int)$r, (int)$g, (int)$b, (int)$alpha);
667        } else {
668            return imagecolorallocate($this->resource, (int)$r, (int)$g, (int)$b);
669        }
670    }
671
672    /**
673     * Output the image
674     *
675     * @return string
676     */
677    public function __toString(): string
678    {
679        $quality = $this->quality;
680        $this->sendHeaders();
681        $this->generateImage($quality);
682        return '';
683    }
684
685    /**
686     * Copy the image resource to the image output resource with the set parameters
687     *
688     * @param  int $w
689     * @param  int $h
690     * @param  int $x
691     * @param  int $y
692     * @return void
693     */
694    protected function copyImage(int $w, int $h, int $x = 0, int $y = 0): void
695    {
696        $result = imagecreatetruecolor($w, $h);
697        imagecopyresampled($result, $this->resource, 0, 0, $x, $y, $w, $h, $this->width, $this->height);
698
699        if ($this->indexed) {
700            imagetruecolortopalette($result, false, 255);
701        }
702
703        $this->resource = $result;
704        $this->width    = imagesx($this->resource);
705        $this->height   = imagesy($this->resource);
706    }
707
708    /**
709     * Parse the image data
710     *
711     * @param  array  $size
712     * @param  string $data
713     * @return void
714     */
715    protected function parseImage(array $size, string $data): void
716    {
717        $this->width  = $size[0];
718        $this->height = $size[1];
719
720        if ((($size[2] == IMAGETYPE_JPEG) || ($size[2] == IMAGETYPE_JPEG2000)) && isset($size['channels'])) {
721            switch ($size['channels']) {
722                case 1:
723                    $this->colorspace = self::IMAGE_GRAY;
724                    break;
725                case 3:
726                    $this->colorspace = self::IMAGE_RGB;
727                    break;
728                case 4:
729                    $this->colorspace = self::IMAGE_CMYK;
730                    break;
731            }
732            $this->format = 'jpg';
733        } else if ($size[2] == IMAGETYPE_PNG) {
734            switch (ord($data[25])) {
735                case 0:
736                case 4:
737                    $this->colorspace = self::IMAGE_GRAY;
738                    break;
739                case 2:
740                case 6:
741                    $this->colorspace = self::IMAGE_RGB;
742                    break;
743                case 3:
744                    $this->colorspace = self::IMAGE_RGB;
745                    $this->indexed    = true;
746                    break;
747            }
748            $this->format = 'png';
749        } else if ($size[2] == IMAGETYPE_GIF) {
750            $this->colorspace = self::IMAGE_RGB;
751            $this->indexed    = true;
752            $this->format     = 'gif';
753        }
754    }
755
756    /**
757     * Parse the image data
758     *
759     * @param  int     $quality
760     * @param  ?string $to
761     * @return void
762     */
763    protected function generateImage(int $quality, ?string $to = null): void
764    {
765        switch ($this->format) {
766            case 'jpg':
767            case 'jpeg':
768                imagejpeg($this->resource, $to, (int)$quality);
769                break;
770            case 'png':
771                $quality = ($quality < 10) ? 9 : 10 - round(($quality / 10), PHP_ROUND_HALF_DOWN);
772                imagepng($this->resource, $to, (int)$quality);
773                break;
774            case 'gif':
775                imagegif($this->resource, $to);
776                break;
777        }
778    }
779
780}