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