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