vendor/gumlet/php-image-resize/lib/ImageResize.php line 217

Open in your IDE?
  1. <?php
  2. namespace Gumlet;
  3. use Exception;
  4. /**
  5.  * PHP class to resize and scale images
  6.  */
  7. class ImageResize
  8. {
  9.     const CROPTOP 1;
  10.     const CROPCENTRE 2;
  11.     const CROPCENTER 2;
  12.     const CROPBOTTOM 3;
  13.     const CROPLEFT 4;
  14.     const CROPRIGHT 5;
  15.     const CROPTOPCENTER 6;
  16.     const IMG_FLIP_HORIZONTAL 0;
  17.     const IMG_FLIP_VERTICAL 1;
  18.     const IMG_FLIP_BOTH 2;
  19.     public $quality_jpg 85;
  20.     public $quality_webp 85;
  21.     public $quality_png 6;
  22.     public $quality_truecolor true;
  23.     public $gamma_correct true;
  24.     public $interlace 1;
  25.     public $source_type;
  26.     protected $source_image;
  27.     protected $original_w;
  28.     protected $original_h;
  29.     protected $dest_x 0;
  30.     protected $dest_y 0;
  31.     protected $source_x;
  32.     protected $source_y;
  33.     protected $dest_w;
  34.     protected $dest_h;
  35.     protected $source_w;
  36.     protected $source_h;
  37.     protected $source_info;
  38.     
  39.     protected $filters = [];
  40.     /**
  41.      * Create instance from a strng
  42.      *
  43.      * @param string $image_data
  44.      * @return ImageResize
  45.      * @throws ImageResizeException
  46.      */
  47.     public static function createFromString($image_data)
  48.     {
  49.         if (empty($image_data) || $image_data === null) {
  50.             throw new ImageResizeException('image_data must not be empty');
  51.         }
  52.         $resize = new self('data://application/octet-stream;base64,' base64_encode($image_data));
  53.         return $resize;
  54.     }
  55.     /**
  56.      * Add filter function for use right before save image to file.
  57.      *
  58.      * @param callable $filter
  59.      * @return $this
  60.      */
  61.     public function addFilter(callable $filter)
  62.     {
  63.         $this->filters[] = $filter;
  64.         return $this;
  65.     }
  66.     /**
  67.      * Apply filters.
  68.      *
  69.      * @param $image resource an image resource identifier
  70.      * @param $filterType filter type and default value is IMG_FILTER_NEGATE
  71.      */
  72.     protected function applyFilter($image$filterType IMG_FILTER_NEGATE)
  73.     {
  74.         foreach ($this->filters as $function) {
  75.             $function($image$filterType);
  76.         }
  77.     }
  78.     /**
  79.      * Loads image source and its properties to the instanciated object
  80.      *
  81.      * @param string $filename
  82.      * @return ImageResize
  83.      * @throws ImageResizeException
  84.      */
  85.     public function __construct($filename)
  86.     {
  87.         if (!defined('IMAGETYPE_WEBP')) {
  88.             define('IMAGETYPE_WEBP'18);
  89.         }
  90.         if ($filename === null || empty($filename) || (substr($filename05) !== 'data:' && !is_file($filename))) {
  91.             throw new ImageResizeException('File does not exist');
  92.         }
  93.         $finfo finfo_open(FILEINFO_MIME_TYPE);
  94.         if (strstr(finfo_file($finfo$filename), 'image') === false) {
  95.             throw new ImageResizeException('Unsupported file type');
  96.         }
  97.         if (!$image_info getimagesize($filename$this->source_info)) {
  98.             $image_info getimagesize($filename);
  99.         }
  100.         if (!$image_info) {
  101.             throw new ImageResizeException('Could not read file');
  102.         }
  103.         list(
  104.             $this->original_w,
  105.             $this->original_h,
  106.             $this->source_type
  107.         ) = $image_info;
  108.         switch ($this->source_type) {
  109.         case IMAGETYPE_GIF:
  110.             $this->source_image imagecreatefromgif($filename);
  111.             break;
  112.         case IMAGETYPE_JPEG:
  113.             $this->source_image $this->imageCreateJpegfromExif($filename);
  114.             // set new width and height for image, maybe it has changed
  115.             $this->original_w imagesx($this->source_image);
  116.             $this->original_h imagesy($this->source_image);
  117.             break;
  118.         case IMAGETYPE_PNG:
  119.             $this->source_image imagecreatefrompng($filename);
  120.             break;
  121.         case IMAGETYPE_WEBP:
  122.             if (version_compare(PHP_VERSION'5.5.0''<')) {
  123.                 throw new ImageResizeException('For WebP support PHP >= 5.5.0 is required');
  124.             }
  125.             $this->source_image imagecreatefromwebp($filename);
  126.             break;
  127.         default:
  128.             throw new ImageResizeException('Unsupported image type');
  129.         }
  130.         if (!$this->source_image) {
  131.             throw new ImageResizeException('Could not load image');
  132.         }
  133.         return $this->resize($this->getSourceWidth(), $this->getSourceHeight());
  134.     }
  135.     // http://stackoverflow.com/a/28819866
  136.     public function imageCreateJpegfromExif($filename)
  137.     {
  138.         $img imagecreatefromjpeg($filename);
  139.         if (!function_exists('exif_read_data') || !isset($this->source_info['APP1'])  || strpos($this->source_info['APP1'], 'Exif') !== 0) {
  140.             return $img;
  141.         }
  142.         try {
  143.             $exif = @exif_read_data($filename);
  144.         } catch (Exception $e) {
  145.             $exif null;
  146.         }
  147.         if (!$exif || !isset($exif['Orientation'])) {
  148.             return $img;
  149.         }
  150.         $orientation $exif['Orientation'];
  151.         if ($orientation === || $orientation === 5) {
  152.             $img imagerotate($img270null);
  153.         } elseif ($orientation === || $orientation === 4) {
  154.             $img imagerotate($img180null);
  155.         } elseif ($orientation === || $orientation === 7) {
  156.             $img imagerotate($img90null);
  157.         }
  158.         if ($orientation === || $orientation === || $orientation === 7) {
  159.             if(function_exists('imageflip')) {
  160.                 imageflip($imgIMG_FLIP_HORIZONTAL);
  161.             } else {
  162.                 $this->imageFlip($imgIMG_FLIP_HORIZONTAL);
  163.             }
  164.         }
  165.         return $img;
  166.     }
  167.     /**
  168.      * Saves new image
  169.      *
  170.      * @param string $filename
  171.      * @param string $image_type
  172.      * @param integer $quality
  173.      * @param integer $permissions
  174.      * @param boolean $exact_size
  175.      * @return static
  176.      */
  177.     public function save($filename$image_type null$quality null$permissions null$exact_size false)
  178.     {
  179.         $image_type $image_type ?: $this->source_type;
  180.         $quality is_numeric($quality) ? (int) abs($quality) : null;
  181.         switch ($image_type) {
  182.         case IMAGETYPE_GIF:
  183.             if( !empty($exact_size) && is_array($exact_size) ){
  184.                 $dest_image imagecreatetruecolor($exact_size[0], $exact_size[1]);
  185.             } else{
  186.                 $dest_image imagecreatetruecolor($this->getDestWidth(), $this->getDestHeight());
  187.             }
  188.             $background imagecolorallocatealpha($dest_image2552552551);
  189.             imagecolortransparent($dest_image$background);
  190.             imagefill($dest_image00$background);
  191.             imagesavealpha($dest_imagetrue);
  192.             break;
  193.         case IMAGETYPE_JPEG:
  194.             if( !empty($exact_size) && is_array($exact_size) ){
  195.                 $dest_image imagecreatetruecolor($exact_size[0], $exact_size[1]);
  196.                 $background imagecolorallocate($dest_image255255255);
  197.                 imagefilledrectangle($dest_image00$exact_size[0], $exact_size[1], $background);
  198.             } else{
  199.                 $dest_image imagecreatetruecolor($this->getDestWidth(), $this->getDestHeight());
  200.                 $background imagecolorallocate($dest_image255255255);
  201.                 imagefilledrectangle($dest_image00$this->getDestWidth(), $this->getDestHeight(), $background);
  202.             }
  203.             break;
  204.         case IMAGETYPE_WEBP:
  205.             if (version_compare(PHP_VERSION'5.5.0''<')) {
  206.                 throw new ImageResizeException('For WebP support PHP >= 5.5.0 is required');
  207.             }
  208.             if( !empty($exact_size) && is_array($exact_size) ){
  209.                 $dest_image imagecreatetruecolor($exact_size[0], $exact_size[1]);
  210.                 $background imagecolorallocate($dest_image255255255);
  211.                 imagefilledrectangle($dest_image00$exact_size[0], $exact_size[1], $background);
  212.             } else{
  213.                 $dest_image imagecreatetruecolor($this->getDestWidth(), $this->getDestHeight());
  214.                 $background imagecolorallocate($dest_image255255255);
  215.                 imagefilledrectangle($dest_image00$this->getDestWidth(), $this->getDestHeight(), $background);
  216.             }
  217.             break;
  218.         case IMAGETYPE_PNG:
  219.             if (!$this->quality_truecolor && !imageistruecolor($this->source_image)) {
  220.                 if( !empty($exact_size) && is_array($exact_size) ){
  221.                     $dest_image imagecreate($exact_size[0], $exact_size[1]);
  222.                 } else{
  223.                     $dest_image imagecreate($this->getDestWidth(), $this->getDestHeight());
  224.                 }
  225.             } else {
  226.                 if( !empty($exact_size) && is_array($exact_size) ){
  227.                     $dest_image imagecreatetruecolor($exact_size[0], $exact_size[1]);
  228.                 } else{
  229.                     $dest_image imagecreatetruecolor($this->getDestWidth(), $this->getDestHeight());
  230.                 }
  231.             }
  232.             imagealphablending($dest_imagefalse);
  233.             imagesavealpha($dest_imagetrue);
  234.             $background imagecolorallocatealpha($dest_image255255255127);
  235.             imagecolortransparent($dest_image$background);
  236.             imagefill($dest_image00$background);
  237.             break;
  238.         }
  239.         imageinterlace($dest_image$this->interlace);
  240.         if ($this->gamma_correct) {
  241.             imagegammacorrect($this->source_image2.21.0);
  242.         }
  243.         if( !empty($exact_size) && is_array($exact_size) ) {
  244.             if ($this->getSourceHeight() < $this->getSourceWidth()) {
  245.                 $this->dest_x 0;
  246.                 $this->dest_y = ($exact_size[1] - $this->getDestHeight()) / 2;
  247.             }
  248.             if ($this->getSourceHeight() > $this->getSourceWidth()) {
  249.                 $this->dest_x = ($exact_size[0] - $this->getDestWidth()) / 2;
  250.                 $this->dest_y 0;
  251.             }
  252.         }
  253.         
  254.         imagecopyresampled(
  255.             $dest_image,
  256.             $this->source_image,
  257.             $this->dest_x,
  258.             $this->dest_y,
  259.             $this->source_x,
  260.             $this->source_y,
  261.             $this->getDestWidth(),
  262.             $this->getDestHeight(),
  263.             $this->source_w,
  264.             $this->source_h
  265.         );
  266.         
  267.         if ($this->gamma_correct) {
  268.             imagegammacorrect($dest_image1.02.2);
  269.         }
  270.         $this->applyFilter($dest_image);
  271.         switch ($image_type) {
  272.         case IMAGETYPE_GIF:
  273.             imagegif($dest_image$filename);
  274.             break;
  275.         case IMAGETYPE_JPEG:
  276.             if ($quality === null || $quality 100) {
  277.                 $quality $this->quality_jpg;
  278.             }
  279.             imagejpeg($dest_image$filename$quality);
  280.             break;
  281.         case IMAGETYPE_WEBP:
  282.             if (version_compare(PHP_VERSION'5.5.0''<')) {
  283.                 throw new ImageResizeException('For WebP support PHP >= 5.5.0 is required');
  284.             }
  285.             if ($quality === null) {
  286.                 $quality $this->quality_webp;
  287.             }
  288.             imagewebp($dest_image$filename$quality);
  289.             break;
  290.         case IMAGETYPE_PNG:
  291.             if ($quality === null || $quality 9) {
  292.                 $quality $this->quality_png;
  293.             }
  294.             imagepng($dest_image$filename$quality);
  295.             break;
  296.         }
  297.         if ($permissions) {
  298.             chmod($filename$permissions);
  299.         }
  300.         imagedestroy($dest_image);
  301.         return $this;
  302.     }
  303.     /**
  304.      * Convert the image to string
  305.      *
  306.      * @param int $image_type
  307.      * @param int $quality
  308.      * @return string
  309.      */
  310.     public function getImageAsString($image_type null$quality null)
  311.     {
  312.         $string_temp tempnam(sys_get_temp_dir(), '');
  313.         $this->save($string_temp$image_type$quality);
  314.         $string file_get_contents($string_temp);
  315.         unlink($string_temp);
  316.         return $string;
  317.     }
  318.     /**
  319.      * Convert the image to string with the current settings
  320.      *
  321.      * @return string
  322.      */
  323.     public function __toString()
  324.     {
  325.         return $this->getImageAsString();
  326.     }
  327.     /**
  328.      * Outputs image to browser
  329.      * @param string $image_type
  330.      * @param integer $quality
  331.      */
  332.     public function output($image_type null$quality null)
  333.     {
  334.         $image_type $image_type ?: $this->source_type;
  335.         header('Content-Type: ' image_type_to_mime_type($image_type));
  336.         $this->save(null$image_type$quality);
  337.     }
  338.     /**
  339.      * Resizes image according to the given short side (short side proportional)
  340.      *
  341.      * @param integer $max_short
  342.      * @param boolean $allow_enlarge
  343.      * @return static
  344.      */
  345.     public function resizeToShortSide($max_short$allow_enlarge false)
  346.     {
  347.         if ($this->getSourceHeight() < $this->getSourceWidth()) {
  348.             $ratio $max_short $this->getSourceHeight();
  349.             $long $this->getSourceWidth() * $ratio;
  350.             $this->resize($long$max_short$allow_enlarge);
  351.         } else {
  352.             $ratio $max_short $this->getSourceWidth();
  353.             $long $this->getSourceHeight() * $ratio;
  354.             $this->resize($max_short$long$allow_enlarge);
  355.         }
  356.         return $this;
  357.     }
  358.     /**
  359.      * Resizes image according to the given long side (short side proportional)
  360.      *
  361.      * @param integer $max_long
  362.      * @param boolean $allow_enlarge
  363.      * @return static
  364.      */
  365.     public function resizeToLongSide($max_long$allow_enlarge false)
  366.     {
  367.         if ($this->getSourceHeight() > $this->getSourceWidth()) {
  368.             $ratio $max_long $this->getSourceHeight();
  369.             $short $this->getSourceWidth() * $ratio;
  370.             $this->resize($short$max_long$allow_enlarge);
  371.         } else {
  372.             $ratio $max_long $this->getSourceWidth();
  373.             $short $this->getSourceHeight() * $ratio;
  374.             $this->resize($max_long$short$allow_enlarge);
  375.         }
  376.         return $this;
  377.     }
  378.     /**
  379.      * Resizes image according to the given height (width proportional)
  380.      *
  381.      * @param integer $height
  382.      * @param boolean $allow_enlarge
  383.      * @return static
  384.      */
  385.     public function resizeToHeight($height$allow_enlarge false)
  386.     {
  387.         $ratio $height $this->getSourceHeight();
  388.         $width $this->getSourceWidth() * $ratio;
  389.         $this->resize($width$height$allow_enlarge);
  390.         return $this;
  391.     }
  392.     /**
  393.      * Resizes image according to the given width (height proportional)
  394.      *
  395.      * @param integer $width
  396.      * @param boolean $allow_enlarge
  397.      * @return static
  398.      */
  399.     public function resizeToWidth($width$allow_enlarge false)
  400.     {
  401.         $ratio  $width $this->getSourceWidth();
  402.         $height $this->getSourceHeight() * $ratio;
  403.         $this->resize($width$height$allow_enlarge);
  404.         return $this;
  405.     }
  406.     /**
  407.      * Resizes image to best fit inside the given dimensions
  408.      *
  409.      * @param integer $max_width
  410.      * @param integer $max_height
  411.      * @param boolean $allow_enlarge
  412.      * @return static
  413.      */
  414.     public function resizeToBestFit($max_width$max_height$allow_enlarge false)
  415.     {
  416.         if ($this->getSourceWidth() <= $max_width && $this->getSourceHeight() <= $max_height && $allow_enlarge === false) {
  417.             return $this;
  418.         }
  419.         $ratio  $this->getSourceHeight() / $this->getSourceWidth();
  420.         $width $max_width;
  421.         $height $width $ratio;
  422.         if ($height $max_height) {
  423.             $height $max_height;
  424.             $width $height $ratio;
  425.         }
  426.         return $this->resize($width$height$allow_enlarge);
  427.     }
  428.     /**
  429.      * Resizes image according to given scale (proportionally)
  430.      *
  431.      * @param integer|float $scale
  432.      * @return static
  433.      */
  434.     public function scale($scale)
  435.     {
  436.         $width  $this->getSourceWidth() * $scale 100;
  437.         $height $this->getSourceHeight() * $scale 100;
  438.         $this->resize($width$heighttrue);
  439.         return $this;
  440.     }
  441.     /**
  442.      * Resizes image according to the given width and height
  443.      *
  444.      * @param integer $width
  445.      * @param integer $height
  446.      * @param boolean $allow_enlarge
  447.      * @return static
  448.      */
  449.     public function resize($width$height$allow_enlarge false)
  450.     {
  451.         if (!$allow_enlarge) {
  452.             // if the user hasn't explicitly allowed enlarging,
  453.             // but either of the dimensions are larger then the original,
  454.             // then just use original dimensions - this logic may need rethinking
  455.             if ($width $this->getSourceWidth() || $height $this->getSourceHeight()) {
  456.                 $width  $this->getSourceWidth();
  457.                 $height $this->getSourceHeight();
  458.             }
  459.         }
  460.         $this->source_x 0;
  461.         $this->source_y 0;
  462.         $this->dest_w $width;
  463.         $this->dest_h $height;
  464.         $this->source_w $this->getSourceWidth();
  465.         $this->source_h $this->getSourceHeight();
  466.         return $this;
  467.     }
  468.     /**
  469.      * Crops image according to the given width, height and crop position
  470.      *
  471.      * @param integer $width
  472.      * @param integer $height
  473.      * @param boolean $allow_enlarge
  474.      * @param integer $position
  475.      * @return static
  476.      */
  477.     public function crop($width$height$allow_enlarge false$position self::CROPCENTER)
  478.     {
  479.         if (!$allow_enlarge) {
  480.             // this logic is slightly different to resize(),
  481.             // it will only reset dimensions to the original
  482.             // if that particular dimenstion is larger
  483.             if ($width $this->getSourceWidth()) {
  484.                 $width  $this->getSourceWidth();
  485.             }
  486.             if ($height $this->getSourceHeight()) {
  487.                 $height $this->getSourceHeight();
  488.             }
  489.         }
  490.         $ratio_source $this->getSourceWidth() / $this->getSourceHeight();
  491.         $ratio_dest $width $height;
  492.         if ($ratio_dest $ratio_source) {
  493.             $this->resizeToHeight($height$allow_enlarge);
  494.             $excess_width = ($this->getDestWidth() - $width) / $this->getDestWidth() * $this->getSourceWidth();
  495.             $this->source_w $this->getSourceWidth() - $excess_width;
  496.             $this->source_x $this->getCropPosition($excess_width$position);
  497.             $this->dest_w $width;
  498.         } else {
  499.             $this->resizeToWidth($width$allow_enlarge);
  500.             $excess_height = ($this->getDestHeight() - $height) / $this->getDestHeight() * $this->getSourceHeight();
  501.             $this->source_h $this->getSourceHeight() - $excess_height;
  502.             $this->source_y $this->getCropPosition($excess_height$position);
  503.             $this->dest_h $height;
  504.         }
  505.         return $this;
  506.     }
  507.     /**
  508.      * Crops image according to the given width, height, x and y
  509.      *
  510.      * @param integer $width
  511.      * @param integer $height
  512.      * @param integer $x
  513.      * @param integer $y
  514.      * @return static
  515.      */
  516.     public function freecrop($width$height$x false$y false)
  517.     {
  518.         if ($x === false || $y === false) {
  519.             return $this->crop($width$height);
  520.         }
  521.         $this->source_x $x;
  522.         $this->source_y $y;
  523.         if ($width $this->getSourceWidth() - $x) {
  524.             $this->source_w $this->getSourceWidth() - $x;
  525.         } else {
  526.             $this->source_w $width;
  527.         }
  528.         if ($height $this->getSourceHeight() - $y) {
  529.             $this->source_h $this->getSourceHeight() - $y;
  530.         } else {
  531.             $this->source_h $height;
  532.         }
  533.         $this->dest_w $width;
  534.         $this->dest_h $height;
  535.         return $this;
  536.     }
  537.     /**
  538.      * Gets source width
  539.      *
  540.      * @return integer
  541.      */
  542.     public function getSourceWidth()
  543.     {
  544.         return $this->original_w;
  545.     }
  546.     /**
  547.      * Gets source height
  548.      *
  549.      * @return integer
  550.      */
  551.     public function getSourceHeight()
  552.     {
  553.         return $this->original_h;
  554.     }
  555.     /**
  556.      * Gets width of the destination image
  557.      *
  558.      * @return integer
  559.      */
  560.     public function getDestWidth()
  561.     {
  562.         return $this->dest_w;
  563.     }
  564.     /**
  565.      * Gets height of the destination image
  566.      * @return integer
  567.      */
  568.     public function getDestHeight()
  569.     {
  570.         return $this->dest_h;
  571.     }
  572.     /**
  573.      * Gets crop position (X or Y) according to the given position
  574.      *
  575.      * @param integer $expectedSize
  576.      * @param integer $position
  577.      * @return float|integer
  578.      */
  579.     protected function getCropPosition($expectedSize$position self::CROPCENTER)
  580.     {
  581.         $size 0;
  582.         switch ($position) {
  583.         case self::CROPBOTTOM:
  584.         case self::CROPRIGHT:
  585.             $size $expectedSize;
  586.             break;
  587.         case self::CROPCENTER:
  588.         case self::CROPCENTRE:
  589.             $size $expectedSize 2;
  590.             break;
  591.         case self::CROPTOPCENTER:
  592.             $size $expectedSize 4;
  593.             break;
  594.         }
  595.         return $size;
  596.     }
  597.     /**
  598.      *  Flips an image using a given mode if PHP version is lower than 5.5
  599.      *
  600.      * @param  resource $image
  601.      * @param  integer  $mode
  602.      * @return null
  603.      */
  604.     public function imageFlip($image$mode)
  605.     {
  606.         switch($mode) {
  607.             case self::IMG_FLIP_HORIZONTAL: {
  608.                 $max_x imagesx($image) - 1;
  609.                 $half_x $max_x 2;
  610.                 $sy imagesy($image);
  611.                 $temp_image imageistruecolor($image)? imagecreatetruecolor(1$sy): imagecreate(1$sy);
  612.                 for ($x 0$x $half_x; ++$x) {
  613.                     imagecopy($temp_image$image00$x01$sy);
  614.                     imagecopy($image$image$x0$max_x $x01$sy);
  615.                     imagecopy($image$temp_image$max_x $x0001$sy);
  616.                 }
  617.                 break;
  618.             }
  619.             case self::IMG_FLIP_VERTICAL: {
  620.                 $sx imagesx($image);
  621.                 $max_y imagesy($image) - 1;
  622.                 $half_y $max_y 2;
  623.                 $temp_image imageistruecolor($image)? imagecreatetruecolor($sx1): imagecreate($sx1);
  624.                 for ($y 0$y $half_y; ++$y) {
  625.                     imagecopy($temp_image$image000$y$sx1);
  626.                     imagecopy($image$image0$y0$max_y $y$sx1);
  627.                     imagecopy($image$temp_image0$max_y $y00$sx1);
  628.                 }
  629.                 break;
  630.             }
  631.             case self::IMG_FLIP_BOTH: {
  632.                 $sx imagesx($image);
  633.                 $sy imagesy($image);
  634.                 $temp_image imagerotate($image1800);
  635.                 imagecopy($image$temp_image0000$sx$sy);
  636.                 break;
  637.             }
  638.             default:
  639.                 return null;
  640.         }
  641.         imagedestroy($temp_image);
  642.     }
  643.     /**
  644.      * Enable or not the gamma color correction on the image, enabled by default
  645.      *
  646.      * @param bool $enable
  647.      * @return static
  648.      */
  649.     public function gamma($enable true)
  650.     {
  651.         $this->gamma_correct $enable;
  652.         return $this;
  653.     }
  654. }