Bitrix-D7 23.9
 
Загрузка...
Поиск...
Не найдено
Imagick.php
1<?php
2
11
13
19class Imagick extends Engine
20{
22 protected $image;
23 protected $animated = false;
25 protected $jpegSize;
26
30 public function getExifData()
31 {
32 if(function_exists("exif_read_data"))
33 {
34 return parent::getExifData();
35 }
36
37 $result = [];
38
39 if($this->image !== null)
40 {
41 $exif = $this->image->getImageProperties("exif:*");
42
43 foreach($exif as $name => $property)
44 {
45 $result[substr($name, 5)] = $property;
46 }
47 }
48
49 return $result;
50 }
51
55 public function load()
56 {
57 $this->clear();
58
59 try
60 {
61 $this->image = new \Imagick();
62
63 $info = $this->getInfo();
64 if (!$info)
65 {
66 return false;
67 }
68
69 if ($this->exceedsMaxSize())
70 {
71 // the image exceeds maximum sizes, optionally substitute it
72 return $this->substituteImage();
73 }
74
75 $needResize = false;
76 if ($info->getFormat() == File\Image::FORMAT_JPEG)
77 {
78 // only for JPEGs
79 $needResize = $this->setJpegOptions();
80 }
81
82 $allowAnimated = (isset($this->options["allowAnimatedImages"]) && $this->options["allowAnimatedImages"] === true);
83
84 // read only the first frame
85 $suffix = ($allowAnimated ? '' : '[0]');
86
87 $this->image->readImage($this->file . $suffix);
88
89 if ($needResize)
90 {
91 // JPEG memory usage optimization
92 $this->image->thumbnailImage($this->jpegSize->getWidth(), $this->jpegSize->getHeight());
93 }
94
95 if ($allowAnimated && $this->image->getNumberImages() > 1)
96 {
97 $this->animated = true;
98 $this->image = $this->image->coalesceImages();
99 }
100
101 return true;
102 }
103 catch (\ImagickException $e)
104 {
105 return false;
106 }
107 }
108
109 protected function substituteImage()
110 {
111 if (isset($this->options["substImage"]))
112 {
113 // substitute the file with a blank image
114 $substImage = new Imagick($this->options["substImage"]);
115
116 if ($substImage->load())
117 {
118 $this->image = clone $substImage->image;
119 $this->substituted = true;
120
121 return true;
122 }
123 }
124 return false;
125 }
126
127 protected function setJpegOptions()
128 {
129 $this->jpegSize = $this->getJpegSize();
130
131 if ($this->jpegSize)
132 {
133 $source = $this->getInfo()->toRectangle();
134
135 $needResize = $source->resize($this->jpegSize, File\Image::RESIZE_PROPORTIONAL);
136
137 if ($needResize)
138 {
139 // the image is too large to fit into memory
140 $this->image->setOption('jpeg:size', $this->jpegSize->getWidth() . 'x' . $this->jpegSize->getHeight());
141 $this->image->setSize($this->jpegSize->getWidth(), $this->jpegSize->getHeight());
142
143 return true;
144 }
145 }
146 return false;
147 }
148
152 public function rotate($angle, Color $bgColor)
153 {
154 if ($this->image === null)
155 {
156 return false;
157 }
158
159 $color = new \ImagickPixel($bgColor->toRgba());
160
161 foreach ($this->image as $frame)
162 {
163 $frame->rotateImage($color, $angle);
164 }
165
166 return true;
167 }
168
172 public function flipVertical()
173 {
174 if ($this->image === null)
175 {
176 return false;
177 }
178
179 foreach ($this->image as $frame)
180 {
181 $frame->flipImage();
182 }
183
184 return true;
185 }
186
190 public function flipHorizontal()
191 {
192 if ($this->image === null)
193 {
194 return false;
195 }
196
197 foreach ($this->image as $frame)
198 {
199 $frame->flopImage();
200 }
201
202 return true;
203 }
204
208 public function setOrientation($orientation)
209 {
210 if($this->image === null)
211 {
212 return false;
213 }
214
215 return $this->image->setImageOrientation($orientation);
216 }
217
221 public function resize(Rectangle $source, Rectangle $destination)
222 {
223 if($this->image === null)
224 {
225 return false;
226 }
227
228 //need crop
229 if($source->getX() <> 0 || $source->getY() <> 0)
230 {
231 $this->crop($source);
232 }
233
234 //hope Imagick will use the best filter automatically
235 $filter = \Imagick::FILTER_UNDEFINED;
236
237 foreach ($this->image as $frame)
238 {
239 //resizeImage has better quality than scaleImage (scaleImage uses a filter similar to FILTER_BOX)
240 $frame->resizeImage($destination->getWidth(), $destination->getHeight(), $filter, 1);
241 }
242
243 return true;
244 }
245
250 public function crop(Rectangle $source)
251 {
252 if($this->image === null)
253 {
254 return false;
255 }
256
257 $this->image->setImagePage(0, 0, 0, 0);
258
259 foreach ($this->image as $frame)
260 {
261 $frame->cropImage($source->getWidth(), $source->getHeight(), $source->getX(), $source->getY());
262 }
263
264 return true;
265 }
266
270 public function filter(Mask $mask)
271 {
272 if($this->image === null)
273 {
274 return false;
275 }
276
277 $imVersion = \Imagick::getVersion();
278 if ($imVersion['versionNumber'] < 0x700 || phpversion('imagick') < '3.4.0')
279 {
280 $this->image->convolveImage($mask->getVector());
281 }
282 else
283 {
284 $kernel = \ImagickKernel::fromMatrix($mask->getValue());
285 $this->image->convolveImage($kernel);
286 }
287
288 return true;
289 }
290
294 public function drawTextWatermark(TextWatermark $watermark)
295 {
296 if($this->image === null)
297 {
298 return false;
299 }
300
301 $font = $watermark->getFont();
302
303 if(!file_exists($font))
304 {
305 return false;
306 }
307
308 $utfText = $watermark->getUtfText();
309
310 $width = $this->getWidth();
311 $height = $this->getHeight();
312
313 $draw = new \ImagickDraw();
314 $draw->setFont($font);
315 $draw->setFillColor(new \ImagickPixel($watermark->getColor()->toRgba()));
316
317 if(($textWidth = $watermark->getWidth()) > 0)
318 {
319 $draw->setFontSize(20);
320
321 $metrics = $this->image->queryFontMetrics($draw, $utfText);
322
323 $scale = 1.0;
324 if($metrics["textWidth"] > 0)
325 {
326 $scale = $textWidth / $metrics["textWidth"];
327 }
328
329 $fontSize = 20 * $scale;
330 $draw->setFontSize($fontSize);
331
332 $position = new Rectangle($textWidth, $metrics["textHeight"] * $scale);
333 }
334 else
335 {
336 //in GD resolution is 96 dpi, we should increase size
337 $fontSize = $watermark->getFontSize($width) * (96/72);
338 $draw->setFontSize($fontSize);
339
340 $metrics = $this->image->queryFontMetrics($draw, $utfText);
341
342 $position = new Rectangle($metrics["textWidth"], $metrics["textHeight"]);
343 }
344
345 $watermark->alignPosition($width, $height, $position);
346
347 $fontSize *= (72/90); //back to pixels
348
350 {
351 //Try to take into consideration font's descenders.
352 //Coordinates in annotateImage are for font's *baseline*.
353 //Let the descenders be 20% of the font size.
354 $descender = $fontSize * 0.2;
355 $y = $position->getY() + $position->getHeight() - $descender; //baseline
356 }
357 else
358 {
359 $y = $position->getY() + $fontSize;
360 }
361
362 return $this->image->annotateImage($draw, $position->getX(), $y, 0, $utfText);
363 }
364
368 public function drawImageWatermark(ImageWatermark $watermark)
369 {
370 if($this->image === null)
371 {
372 return false;
373 }
374
375 if(($image = $this->loadWatermark($watermark)) === null)
376 {
377 return false;
378 }
379
380 $watermarkWidth = $image->getWidth();
381 $watermarkHeight = $image->getHeight();
382
383 $position = new Rectangle($watermarkWidth, $watermarkHeight);
384
385 $width = $this->getWidth();
386 $height = $this->getHeight();
387
388 $watermark->alignPosition($width, $height, $position);
389
390 $watermarkAlpha = $watermark->getAlpha();
391
392 if(intval(round($watermarkAlpha, 2)) < 1) //1% precision
393 {
394 //apply alpha to the watermark
395 $image->image->evaluateImage(\Imagick::EVALUATE_MULTIPLY, $watermarkAlpha, \Imagick::CHANNEL_ALPHA);
396 }
397
398 $repeat = ($watermark->getMode() == ImageWatermark::MODE_REPEAT);
399
400 $posY = $position->getY();
401 while(true)
402 {
403 $posX = $position->getX();
404 while(true)
405 {
406 $this->image->compositeImage($image->image, \Imagick::COMPOSITE_OVER, $posX, $posY);
407
408 $posX += $watermarkWidth;
409 if($repeat == false || $posX > $width)
410 {
411 break;
412 }
413 }
414
415 $posY += $watermarkHeight;
416 if($repeat == false || $posY > $height)
417 {
418 break;
419 }
420 }
421
422 $image->clear();
423
424 return true;
425 }
426
430 public function save($file, $quality = 95, $format = null)
431 {
432 if ($this->image === null)
433 {
434 return false;
435 }
436
437 $prefix = "";
438 if ($format !== null)
439 {
440 $format = static::convertFormat($format);
441 if($format !== null)
442 {
443 $prefix = "{$format}:";
444 }
445 }
446
447 $this->image->setImageCompressionQuality($quality);
448
449 if ($format === "gif" || ($format = $this->image->getImageFormat()) === "GIF" || $format === "GIF87")
450 {
451 //strange artefacts with transparency - we limit the palette to 255 colors to fix it
452 $this->image->quantizeImage(255, \Imagick::COLORSPACE_SRGB, 0, false, false);
453 }
454
455 if ($this->animated)
456 {
457 return $this->image->deconstructImages()->writeImages($prefix.$file, true);
458 }
459 else
460 {
461 return $this->image->writeImage($prefix.$file);
462 }
463 }
464
468 public function getWidth()
469 {
470 return $this->image->getImageWidth();
471 }
472
476 public function getHeight()
477 {
478 return $this->image->getImageHeight();
479 }
480
484 public function clear()
485 {
486 if($this->image !== null)
487 {
488 $this->image->clear();
489 $this->image = null;
490 $this->animated = false;
491 $this->jpegSize = null;
492 }
493 }
494
495 protected static function convertFormat($format)
496 {
497 static $formats = [
498 File\Image::FORMAT_BMP => "bmp",
499 File\Image::FORMAT_GIF => "gif",
500 File\Image::FORMAT_JPEG => "jpg",
501 File\Image::FORMAT_PNG => "png",
502 File\Image::FORMAT_WEBP => "webp",
503 ];
504
505 if(isset($formats[$format]))
506 {
507 return $formats[$format];
508 }
509 return null;
510 }
511
512 protected function getJpegSize()
513 {
514 if (isset($this->options["jpegLoadSize"]) && is_array($this->options["jpegLoadSize"]))
515 {
516 return new Rectangle($this->options["jpegLoadSize"][0], $this->options["jpegLoadSize"][1]);
517 }
518
519 return null;
520 }
521}
getInfo($flashEnabled=false)
Definition Engine.php:57
loadWatermark(ImageWatermark $watermark)
Definition Engine.php:222
rotate($angle, Color $bgColor)
Definition Imagick.php:152
drawTextWatermark(TextWatermark $watermark)
Definition Imagick.php:294
save($file, $quality=95, $format=null)
Definition Imagick.php:430
static convertFormat($format)
Definition Imagick.php:495
crop(Rectangle $source)
Definition Imagick.php:250
drawImageWatermark(ImageWatermark $watermark)
Definition Imagick.php:368
resize(Rectangle $source, Rectangle $destination)
Definition Imagick.php:221
alignPosition($width, $height, Rectangle $position)
Definition Watermark.php:61
const RESIZE_PROPORTIONAL
Definition Image.php:24