Magento 2 Documentation  2.3
Documentation for Magento 2 CMS v2.3 (December 2018)
Gd2.php
Go to the documentation of this file.
1 <?php
7 
13 class Gd2 extends \Magento\Framework\Image\Adapter\AbstractAdapter
14 {
20  protected $_requiredExtensions = ["gd"];
21 
27  private static $_callbacks = [
28  IMAGETYPE_GIF => ['output' => 'imagegif', 'create' => 'imagecreatefromgif'],
29  IMAGETYPE_JPEG => ['output' => 'imagejpeg', 'create' => 'imagecreatefromjpeg'],
30  IMAGETYPE_PNG => ['output' => 'imagepng', 'create' => 'imagecreatefrompng'],
31  IMAGETYPE_XBM => ['output' => 'imagexbm', 'create' => 'imagecreatefromxbm'],
32  IMAGETYPE_WBMP => ['output' => 'imagewbmp', 'create' => 'imagecreatefromxbm'],
33  ];
34 
40  protected $_resized = false;
41 
47  protected function _reset()
48  {
49  $this->_fileMimeType = null;
50  $this->_fileType = null;
51  }
52 
60  public function open($filename)
61  {
62  $this->_fileName = $filename;
63  $this->_reset();
64  $this->getMimeType();
65  $this->_getFileAttributes();
66  if ($this->_isMemoryLimitReached()) {
67  throw new \OverflowException('Memory limit has been reached.');
68  }
69  $this->imageDestroy();
70  $this->_imageHandler = call_user_func(
71  $this->_getCallback('create', null, sprintf('Unsupported image format. File: %s', $this->_fileName)),
72  $this->_fileName
73  );
74  $fileType = $this->getImageType();
75  if (in_array($fileType, [IMAGETYPE_PNG, IMAGETYPE_GIF])) {
76  $this->_keepTransparency = true;
77  if ($this->_imageHandler) {
78  $isAlpha = $this->checkAlpha($this->_fileName);
79  if ($isAlpha) {
80  $this->_fillBackgroundColor($this->_imageHandler);
81  }
82  }
83  }
84  }
85 
91  protected function _isMemoryLimitReached()
92  {
93  $limit = $this->_convertToByte(ini_get('memory_limit'));
94  $requiredMemory = $this->_getImageNeedMemorySize($this->_fileName);
95  if ($limit === -1) {
96  // A limit of -1 means no limit: http://www.php.net/manual/en/ini.core.php#ini.memory-limit
97  return false;
98  }
99  return memory_get_usage(true) + $requiredMemory > $limit;
100  }
101 
108  protected function _getImageNeedMemorySize($file)
109  {
110  $imageInfo = getimagesize($file);
111  if (!isset($imageInfo[0]) || !isset($imageInfo[1])) {
112  return 0;
113  }
114  if (!isset($imageInfo['channels'])) {
115  // if there is no info about this parameter lets set it for maximum
116  $imageInfo['channels'] = 4;
117  }
118  if (!isset($imageInfo['bits'])) {
119  // if there is no info about this parameter lets set it for maximum
120  $imageInfo['bits'] = 8;
121  }
122 
123  return round(
124  ($imageInfo[0] * $imageInfo[1] * $imageInfo['bits'] * $imageInfo['channels'] / 8 + pow(2, 16)) * 1.65
125  );
126  }
127 
136  protected function _convertToByte($memoryValue)
137  {
138  if (stripos($memoryValue, 'G') !== false) {
139  return (int)$memoryValue * pow(1024, 3);
140  } elseif (stripos($memoryValue, 'M') !== false) {
141  return (int)$memoryValue * 1024 * 1024;
142  } elseif (stripos($memoryValue, 'K') !== false) {
143  return (int)$memoryValue * 1024;
144  }
145 
146  return (int)$memoryValue;
147  }
148 
159  public function save($destination = null, $newName = null)
160  {
161  $fileName = $this->_prepareDestination($destination, $newName);
162 
163  if (!$this->_resized) {
164  // keep alpha transparency
165  $isAlpha = false;
166  $isTrueColor = false;
167  $this->_getTransparency($this->_imageHandler, $this->_fileType, $isAlpha, $isTrueColor);
168  if ($isAlpha) {
169  if ($isTrueColor) {
170  $newImage = imagecreatetruecolor($this->_imageSrcWidth, $this->_imageSrcHeight);
171  } else {
172  $newImage = imagecreate($this->_imageSrcWidth, $this->_imageSrcHeight);
173  }
174  $this->_fillBackgroundColor($newImage);
175  imagecopy($newImage, $this->_imageHandler, 0, 0, 0, 0, $this->_imageSrcWidth, $this->_imageSrcHeight);
176  $this->imageDestroy();
177  $this->_imageHandler = $newImage;
178  }
179  }
180 
181  // Enable interlace
182  imageinterlace($this->_imageHandler, true);
183 
184  // Set image quality value
185  switch ($this->_fileType) {
186  case IMAGETYPE_PNG:
187  $quality = 9; // For PNG files compression level must be from 0 (no compression) to 9.
188  break;
189 
190  case IMAGETYPE_JPEG:
191  $quality = $this->quality();
192  break;
193 
194  default:
195  $quality = null; // No compression.
196  }
197 
198  // Prepare callback method parameters
199  $functionParameters = [$this->_imageHandler, $fileName];
200  if ($quality) {
201  $functionParameters[] = $quality;
202  }
203 
204  call_user_func_array($this->_getCallback('output'), $functionParameters);
205  }
206 
214  public function getImage()
215  {
216  ob_start();
217  call_user_func($this->_getCallback('output'), $this->_imageHandler);
218  return ob_get_clean();
219  }
220 
230  private function _getCallback($callbackType, $fileType = null, $unsupportedText = 'Unsupported image format.')
231  {
232  if (null === $fileType) {
233  $fileType = $this->_fileType;
234  }
235  if (empty(self::$_callbacks[$fileType])) {
236  throw new \Exception($unsupportedText);
237  }
238  if (empty(self::$_callbacks[$fileType][$callbackType])) {
239  throw new \Exception('Callback not found.');
240  }
241  return self::$_callbacks[$fileType][$callbackType];
242  }
243 
254  private function _fillBackgroundColor(&$imageResourceTo)
255  {
256  // try to keep transparency, if any
257  if ($this->_keepTransparency) {
258  $isAlpha = false;
259  $transparentIndex = $this->_getTransparency($this->_imageHandler, $this->_fileType, $isAlpha);
260  try {
261  // fill truecolor png with alpha transparency
262  if ($isAlpha) {
263  if (!imagealphablending($imageResourceTo, false)) {
264  throw new \Exception('Failed to set alpha blending for PNG image.');
265  }
266  $transparentAlphaColor = imagecolorallocatealpha($imageResourceTo, 0, 0, 0, 127);
267  if (false === $transparentAlphaColor) {
268  throw new \Exception('Failed to allocate alpha transparency for PNG image.');
269  }
270  if (!imagefill($imageResourceTo, 0, 0, $transparentAlphaColor)) {
271  throw new \Exception('Failed to fill PNG image with alpha transparency.');
272  }
273  if (!imagesavealpha($imageResourceTo, true)) {
274  throw new \Exception('Failed to save alpha transparency into PNG image.');
275  }
276 
277  return $transparentAlphaColor;
278  } elseif (false !== $transparentIndex) {
279  // fill image with indexed non-alpha transparency
280  $transparentColor = false;
281  if ($transparentIndex >= 0 && $transparentIndex <= imagecolorstotal($this->_imageHandler)) {
282  list($r, $g, $b) = array_values(imagecolorsforindex($this->_imageHandler, $transparentIndex));
283  $transparentColor = imagecolorallocate($imageResourceTo, $r, $g, $b);
284  }
285  if (false === $transparentColor) {
286  throw new \Exception('Failed to allocate transparent color for image.');
287  }
288  if (!imagefill($imageResourceTo, 0, 0, $transparentColor)) {
289  throw new \Exception('Failed to fill image with transparency.');
290  }
291  imagecolortransparent($imageResourceTo, $transparentColor);
292  return $transparentColor;
293  }
294  } catch (\Exception $e) {
295  // fallback to default background color
296  }
297  }
298  list($r, $g, $b) = $this->_backgroundColor;
299  $color = imagecolorallocate($imageResourceTo, $r, $g, $b);
300  if (!imagefill($imageResourceTo, 0, 0, $color)) {
301  throw new \Exception("Failed to fill image background with color {$r} {$g} {$b}.");
302  }
303 
304  return $color;
305  }
306 
313  public function checkAlpha($fileName)
314  {
315  return (ord(file_get_contents($fileName, false, null, 25, 1)) & 6 & 4) == 4;
316  }
317 
328  private function _getTransparency($imageResource, $fileType, &$isAlpha = false, &$isTrueColor = false)
329  {
330  $isAlpha = false;
331  $isTrueColor = false;
332  // assume that transparency is supported by gif/png only
333  if (IMAGETYPE_GIF === $fileType || IMAGETYPE_PNG === $fileType) {
334  // check for specific transparent color
335  $transparentIndex = imagecolortransparent($imageResource);
336  if ($transparentIndex >= 0) {
337  return $transparentIndex;
338  } elseif (IMAGETYPE_PNG === $fileType) {
339  // assume that truecolor PNG has transparency
340  $isAlpha = $this->checkAlpha($this->_fileName);
341  $isTrueColor = true;
342  // -1
343  return $transparentIndex;
344  }
345  }
346  if (IMAGETYPE_JPEG === $fileType) {
347  $isTrueColor = true;
348  }
349  return false;
350  }
351 
359  public function resize($frameWidth = null, $frameHeight = null)
360  {
361  $dims = $this->_adaptResizeValues($frameWidth, $frameHeight);
362 
363  // create new image
364  $isAlpha = false;
365  $isTrueColor = false;
366  $this->_getTransparency($this->_imageHandler, $this->_fileType, $isAlpha, $isTrueColor);
367  if ($isTrueColor) {
368  $newImage = imagecreatetruecolor($dims['frame']['width'], $dims['frame']['height']);
369  } else {
370  $newImage = imagecreate($dims['frame']['width'], $dims['frame']['height']);
371  }
372 
373  if ($isAlpha) {
374  $this->_saveAlpha($newImage);
375  }
376 
377  // fill new image with required color
378  $this->_fillBackgroundColor($newImage);
379 
380  if ($this->_imageHandler) {
381  // resample source image and copy it into new frame
382  imagecopyresampled(
383  $newImage,
384  $this->_imageHandler,
385  $dims['dst']['x'],
386  $dims['dst']['y'],
387  $dims['src']['x'],
388  $dims['src']['y'],
389  $dims['dst']['width'],
390  $dims['dst']['height'],
391  $this->_imageSrcWidth,
392  $this->_imageSrcHeight
393  );
394  }
395  $this->imageDestroy();
396  $this->_imageHandler = $newImage;
397  $this->refreshImageDimensions();
398  $this->_resized = true;
399  }
400 
407  public function rotate($angle)
408  {
409  $rotatedImage = imagerotate($this->_imageHandler, $angle, $this->imageBackgroundColor);
410  $this->imageDestroy();
411  $this->_imageHandler = $rotatedImage;
412  $this->refreshImageDimensions();
413  }
414 
429  public function watermark($imagePath, $positionX = 0, $positionY = 0, $opacity = 30, $tile = false)
430  {
431  list($watermarkSrcWidth, $watermarkSrcHeight, $watermarkFileType,) = $this->_getImageOptions($imagePath);
432  $this->_getFileAttributes();
433  $watermark = call_user_func(
434  $this->_getCallback('create', $watermarkFileType, 'Unsupported watermark image format.'),
435  $imagePath
436  );
437 
438  $merged = false;
439 
440  if ($this->getWatermarkWidth() &&
441  $this->getWatermarkHeight() &&
442  $this->getWatermarkPosition() != self::POSITION_STRETCH
443  ) {
444  $newWatermark = imagecreatetruecolor($this->getWatermarkWidth(), $this->getWatermarkHeight());
445  imagealphablending($newWatermark, false);
446  $col = imagecolorallocate($newWatermark, 255, 255, 255);
447  imagecolortransparent($newWatermark, $col);
448  imagefilledrectangle($newWatermark, 0, 0, $this->getWatermarkWidth(), $this->getWatermarkHeight(), $col);
449  imagesavealpha($newWatermark, true);
450  imagecopyresampled(
451  $newWatermark,
452  $watermark,
453  0,
454  0,
455  0,
456  0,
457  $this->getWatermarkWidth(),
458  $this->getWatermarkHeight(),
459  imagesx($watermark),
460  imagesy($watermark)
461  );
462  $watermark = $newWatermark;
463  }
464 
465  if ($this->getWatermarkPosition() == self::POSITION_TILE) {
466  $tile = true;
467  } elseif ($this->getWatermarkPosition() == self::POSITION_STRETCH) {
468  $newWatermark = imagecreatetruecolor($this->_imageSrcWidth, $this->_imageSrcHeight);
469  imagealphablending($newWatermark, false);
470  $col = imagecolorallocate($newWatermark, 255, 255, 255);
471  imagecolortransparent($newWatermark, $col);
472  imagefilledrectangle($newWatermark, 0, 0, $this->_imageSrcWidth, $this->_imageSrcHeight, $col);
473  imagesavealpha($newWatermark, true);
474  imagecopyresampled(
475  $newWatermark,
476  $watermark,
477  0,
478  0,
479  0,
480  0,
481  $this->_imageSrcWidth,
482  $this->_imageSrcHeight,
483  imagesx($watermark),
484  imagesy($watermark)
485  );
486  $watermark = $newWatermark;
487  } elseif ($this->getWatermarkPosition() == self::POSITION_CENTER) {
488  $positionX = $this->_imageSrcWidth / 2 - imagesx($watermark) / 2;
489  $positionY = $this->_imageSrcHeight / 2 - imagesy($watermark) / 2;
490  $this->imagecopymergeWithAlphaFix(
491  $this->_imageHandler,
492  $watermark,
493  $positionX,
494  $positionY,
495  0,
496  0,
497  imagesx($watermark),
498  imagesy($watermark),
499  $this->getWatermarkImageOpacity()
500  );
501  } elseif ($this->getWatermarkPosition() == self::POSITION_TOP_RIGHT) {
502  $positionX = $this->_imageSrcWidth - imagesx($watermark);
503  $this->imagecopymergeWithAlphaFix(
504  $this->_imageHandler,
505  $watermark,
506  $positionX,
507  $positionY,
508  0,
509  0,
510  imagesx($watermark),
511  imagesy($watermark),
512  $this->getWatermarkImageOpacity()
513  );
514  } elseif ($this->getWatermarkPosition() == self::POSITION_TOP_LEFT) {
515  $this->imagecopymergeWithAlphaFix(
516  $this->_imageHandler,
517  $watermark,
518  $positionX,
519  $positionY,
520  0,
521  0,
522  imagesx($watermark),
523  imagesy($watermark),
524  $this->getWatermarkImageOpacity()
525  );
526  } elseif ($this->getWatermarkPosition() == self::POSITION_BOTTOM_RIGHT) {
527  $positionX = $this->_imageSrcWidth - imagesx($watermark);
528  $positionY = $this->_imageSrcHeight - imagesy($watermark);
529  $this->imagecopymergeWithAlphaFix(
530  $this->_imageHandler,
531  $watermark,
532  $positionX,
533  $positionY,
534  0,
535  0,
536  imagesx($watermark),
537  imagesy($watermark),
538  $this->getWatermarkImageOpacity()
539  );
540  } elseif ($this->getWatermarkPosition() == self::POSITION_BOTTOM_LEFT) {
541  $positionY = $this->_imageSrcHeight - imagesy($watermark);
542  $this->imagecopymergeWithAlphaFix(
543  $this->_imageHandler,
544  $watermark,
545  $positionX,
546  $positionY,
547  0,
548  0,
549  imagesx($watermark),
550  imagesy($watermark),
551  $this->getWatermarkImageOpacity()
552  );
553  }
554 
555  if ($tile === false && $merged === false) {
556  $this->imagecopymergeWithAlphaFix(
557  $this->_imageHandler,
558  $watermark,
559  $positionX,
560  $positionY,
561  0,
562  0,
563  imagesx($watermark),
564  imagesy($watermark),
565  $this->getWatermarkImageOpacity()
566  );
567  } else {
568  $offsetX = $positionX;
569  $offsetY = $positionY;
570  while ($offsetY <= $this->_imageSrcHeight + imagesy($watermark)) {
571  while ($offsetX <= $this->_imageSrcWidth + imagesx($watermark)) {
572  $this->imagecopymergeWithAlphaFix(
573  $this->_imageHandler,
574  $watermark,
575  $offsetX,
576  $offsetY,
577  0,
578  0,
579  imagesx($watermark),
580  imagesy($watermark),
581  $this->getWatermarkImageOpacity()
582  );
583  $offsetX += imagesx($watermark);
584  }
585  $offsetX = $positionX;
586  $offsetY += imagesy($watermark);
587  }
588  }
589 
590  imagedestroy($watermark);
591  $this->refreshImageDimensions();
592  }
593 
603  public function crop($top = 0, $left = 0, $right = 0, $bottom = 0)
604  {
605  if ($left == 0 && $top == 0 && $right == 0 && $bottom == 0) {
606  return false;
607  }
608 
609  $newWidth = $this->_imageSrcWidth - $left - $right;
610  $newHeight = $this->_imageSrcHeight - $top - $bottom;
611 
612  $canvas = imagecreatetruecolor($newWidth, $newHeight);
613 
614  if ($this->_fileType == IMAGETYPE_PNG) {
615  $this->_saveAlpha($canvas);
616  }
617 
618  imagecopyresampled(
619  $canvas,
620  $this->_imageHandler,
621  0,
622  0,
623  $left,
624  $top,
625  $newWidth,
626  $newHeight,
627  $newWidth,
628  $newHeight
629  );
630  $this->imageDestroy();
631  $this->_imageHandler = $canvas;
632  $this->refreshImageDimensions();
633  return true;
634  }
635 
642  public function checkDependencies()
643  {
644  foreach ($this->_requiredExtensions as $value) {
645  if (!extension_loaded($value)) {
646  throw new \Exception("Required PHP extension '{$value}' was not loaded.");
647  }
648  }
649  }
650 
656  public function refreshImageDimensions()
657  {
658  $this->_imageSrcWidth = imagesx($this->_imageHandler);
659  $this->_imageSrcHeight = imagesy($this->_imageHandler);
660  }
661 
665  public function __destruct()
666  {
667  $this->imageDestroy();
668  }
669 
675  private function imageDestroy()
676  {
677  if (is_resource($this->_imageHandler)) {
678  imagedestroy($this->_imageHandler);
679  }
680  }
681 
688  private function _saveAlpha($imageHandler)
689  {
690  $background = imagecolorallocate($imageHandler, 0, 0, 0);
691  imagecolortransparent($imageHandler, $background);
692  imagealphablending($imageHandler, false);
693  imagesavealpha($imageHandler, true);
694  }
695 
703  public function getColorAt($x, $y)
704  {
705  $colorIndex = imagecolorat($this->_imageHandler, $x, $y);
706  return imagecolorsforindex($this->_imageHandler, $colorIndex);
707  }
708 
716  public function createPngFromString($text, $font = '')
717  {
718  $error = false;
719  $this->_resized = true;
720  try {
721  $this->_createImageFromTtfText($text, $font);
722  } catch (\Exception $e) {
723  $error = true;
724  }
725 
726  if ($error || empty($this->_imageHandler)) {
727  $this->_createImageFromText($text);
728  }
729 
730  return $this;
731  }
732 
739  protected function _createImageFromText($text)
740  {
741  $width = imagefontwidth($this->_fontSize) * strlen($text);
742  $height = imagefontheight($this->_fontSize);
743 
744  $this->_createEmptyImage($width, $height);
745 
746  $black = imagecolorallocate($this->_imageHandler, 0, 0, 0);
747  imagestring($this->_imageHandler, $this->_fontSize, 0, 0, $text, $black);
748  }
749 
760  protected function _createImageFromTtfText($text, $font)
761  {
762  $boundingBox = imagettfbbox($this->_fontSize, 0, $font, $text);
763  $width = abs($boundingBox[4] - $boundingBox[0]);
764  $height = abs($boundingBox[5] - $boundingBox[1]);
765 
766  $this->_createEmptyImage($width, $height);
767 
768  $black = imagecolorallocate($this->_imageHandler, 0, 0, 0);
769  $result = imagettftext(
770  $this->_imageHandler,
771  $this->_fontSize,
772  0,
773  0,
774  $height - $boundingBox[1],
775  $black,
776  $font,
777  $text
778  );
779  if ($result === false) {
780  throw new \Exception('Unable to create TTF text');
781  }
782  }
783 
791  protected function _createEmptyImage($width, $height)
792  {
793  $this->_fileType = IMAGETYPE_PNG;
794  $image = imagecreatetruecolor($width, $height);
795  $colorWhite = imagecolorallocatealpha($image, 255, 255, 255, 127);
796 
797  imagealphablending($image, true);
798  imagesavealpha($image, true);
799 
800  imagefill($image, 0, 0, $colorWhite);
801  $this->imageDestroy();
802  $this->_imageHandler = $image;
803  }
804 
820  private function imagecopymergeWithAlphaFix(
821  $dst_im,
822  $src_im,
823  $dst_x,
824  $dst_y,
825  $src_x,
826  $src_y,
827  $src_w,
828  $src_h,
829  $pct
830  ) {
831  if ($pct >= 100) {
832  return imagecopy($dst_im, $src_im, $dst_x, $dst_y, $src_x, $src_y, $src_w, $src_h);
833  }
834 
835  if ($pct < 0) {
836  return false;
837  }
838 
839  $sizeX = imagesx($src_im);
840  $sizeY = imagesy($src_im);
841  if (false === $sizeX || false === $sizeY) {
842  return false;
843  }
844 
845  $tmpImg = imagecreatetruecolor($src_w, $src_h);
846  if (false === $tmpImg) {
847  return false;
848  }
849 
850  if (false === imagealphablending($tmpImg, false)) {
851  return false;
852  }
853 
854  if (false === imagecopy($tmpImg, $src_im, 0, 0, 0, 0, $sizeX, $sizeY)) {
855  return false;
856  }
857 
858  $transparancy = 127 - (($pct*127)/100);
859  if (false === imagefilter($tmpImg, IMG_FILTER_COLORIZE, 0, 0, 0, $transparancy)) {
860  return false;
861  }
862 
863  $result = imagecopy($dst_im, $tmpImg, $dst_x, $dst_y, $src_x, $src_y, $src_w, $src_h);
864  imagedestroy($tmpImg);
865 
866  return $result;
867  }
868 }
elseif(isset( $params[ 'redirect_parent']))
Definition: iframe.phtml:17
save($destination=null, $newName=null)
Definition: Gd2.php:159
endifif( $block->getLastPageNum()>1)( 'Page') ?></strong >< ul class $text
Definition: pager.phtml:43
_createEmptyImage($width, $height)
Definition: Gd2.php:791
$fileName
Definition: translate.phtml:15
_createImageFromTtfText($text, $font)
Definition: Gd2.php:760
crop($top=0, $left=0, $right=0, $bottom=0)
Definition: Gd2.php:603
$value
Definition: gender.phtml:16
watermark($imagePath, $positionX=0, $positionY=0, $opacity=30, $tile=false)
Definition: Gd2.php:429
resize($frameWidth=null, $frameHeight=null)
Definition: Gd2.php:359
_prepareDestination($destination=null, $newName=null)
createPngFromString($text, $font='')
Definition: Gd2.php:716