Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Image manipulation class, provides cropping, resampling and canvas resize
<?php
class ImageManipulator
{
/**
* @var int
*/
protected $width;
/**
* @var int
*/
protected $height;
/**
* @var resource
*/
protected $image;
/**
* Image manipulator constructor
*
* @param string $file OPTIONAL Path to image file or image data as string
* @return void
*/
public function __construct($file = null)
{
if (null !== $file) {
if (is_file($file)) {
$this->setImageFile($file);
} else {
$this->setImageString($file);
}
}
}
/**
* Set image resource from file
*
* @param string $file Path to image file
* @return ImageManipulator for a fluent interface
* @throws InvalidArgumentException
*/
public function setImageFile($file)
{
if (!(is_readable($file) && is_file($file))) {
throw new InvalidArgumentException("Image file $file is not readable");
}
if (is_resource($this->image)) {
imagedestroy($this->image);
}
list ($this->width, $this->height, $type) = getimagesize($file);
switch ($type) {
case IMAGETYPE_GIF :
$this->image = imagecreatefromgif($file);
break;
case IMAGETYPE_JPEG :
$this->image = imagecreatefromjpeg($file);
break;
case IMAGETYPE_PNG :
$this->image = imagecreatefrompng($file);
break;
default :
throw new InvalidArgumentException("Image type $type not supported");
}
return $this;
}
/**
* Set image resource from string data
*
* @param string $data
* @return ImageManipulator for a fluent interface
* @throws RuntimeException
*/
public function setImageString($data)
{
if (is_resource($this->image)) {
imagedestroy($this->image);
}
if (!$this->image = imagecreatefromstring($data)) {
throw new RuntimeException('Cannot create image from data string');
}
$this->width = imagesx($this->image);
$this->height = imagesy($this->image);
return $this;
}
/**
* Resamples the current image
*
* @param int $width New width
* @param int $height New height
* @param bool $constrainProportions Constrain current image proportions when resizing
* @return ImageManipulator for a fluent interface
* @throws RuntimeException
*/
public function resample($width, $height, $constrainProportions = true)
{
if (!is_resource($this->image)) {
throw new RuntimeException('No image set');
}
if ($constrainProportions) {
if ($this->height >= $this->width) {
$width = round($height / $this->height * $this->width);
} else {
$height = round($width / $this->width * $this->height);
}
}
$temp = imagecreatetruecolor($width, $height);
imagecopyresampled($temp, $this->image, 0, 0, 0, 0, $width, $height, $this->width, $this->height);
return $this->_replace($temp);
}
/**
* Enlarge canvas
*
* @param int $width Canvas width
* @param int $height Canvas height
* @param array $rgb RGB colour values
* @param int $xpos X-Position of image in new canvas, null for centre
* @param int $ypos Y-Position of image in new canvas, null for centre
* @return ImageManipulator for a fluent interface
* @throws RuntimeException
*/
public function enlargeCanvas($width, $height, array $rgb = array(), $xpos = null, $ypos = null)
{
if (!is_resource($this->image)) {
throw new RuntimeException('No image set');
}
$width = max($width, $this->width);
$height = max($height, $this->height);
$temp = imagecreatetruecolor($width, $height);
if (count($rgb) == 3) {
$bg = imagecolorallocate($temp, $rgb[0], $rgb[1], $rgb[2]);
imagefill($temp, 0, 0, $bg);
}
if (null === $xpos) {
$xpos = round(($width - $this->width) / 2);
}
if (null === $ypos) {
$ypos = round(($height - $this->height) / 2);
}
imagecopy($temp, $this->image, (int) $xpos, (int) $ypos, 0, 0, $this->width, $this->height);
return $this->_replace($temp);
}
/**
* Crop image
*
* @param int|array $x1 Top left x-coordinate of crop box or array of coordinates
* @param int $y1 Top left y-coordinate of crop box
* @param int $x2 Bottom right x-coordinate of crop box
* @param int $y2 Bottom right y-coordinate of crop box
* @return ImageManipulator for a fluent interface
* @throws RuntimeException
*/
public function crop($x1, $y1 = 0, $x2 = 0, $y2 = 0)
{
if (!is_resource($this->image)) {
throw new RuntimeException('No image set');
}
if (is_array($x1) && 4 == count($x1)) {
list($x1, $y1, $x2, $y2) = $x1;
}
$x1 = max($x1, 0);
$y1 = max($y1, 0);
$x2 = min($x2, $this->width);
$y2 = min($y2, $this->height);
$width = $x2 - $x1;
$height = $y2 - $y1;
$temp = imagecreatetruecolor($width, $height);
imagecopy($temp, $this->image, 0, 0, $x1, $y1, $width, $height);
return $this->_replace($temp);
}
/**
* Replace current image resource with a new one
*
* @param resource $res New image resource
* @return ImageManipulator for a fluent interface
* @throws UnexpectedValueException
*/
protected function _replace($res)
{
if (!is_resource($res)) {
throw new UnexpectedValueException('Invalid resource');
}
if (is_resource($this->image)) {
imagedestroy($this->image);
}
$this->image = $res;
$this->width = imagesx($res);
$this->height = imagesy($res);
return $this;
}
/**
* Save current image to file
*
* @param string $fileName
* @return void
* @throws RuntimeException
*/
public function save($fileName, $type = IMAGETYPE_JPEG)
{
$dir = dirname($fileName);
if (!is_dir($dir)) {
if (!mkdir($dir, 0755, true)) {
throw new RuntimeException('Error creating directory ' . $dir);
}
}
try {
switch ($type) {
case IMAGETYPE_GIF :
if (!imagegif($this->image, $fileName)) {
throw new RuntimeException;
}
break;
case IMAGETYPE_PNG :
if (!imagepng($this->image, $fileName)) {
throw new RuntimeException;
}
break;
case IMAGETYPE_JPEG :
default :
if (!imagejpeg($this->image, $fileName, 95)) {
throw new RuntimeException;
}
}
} catch (Exception $ex) {
throw new RuntimeException('Error saving image file to ' . $fileName);
}
}
/**
* Returns the GD image resource
*
* @return resource
*/
public function getResource()
{
return $this->image;
}
/**
* Get current image resource width
*
* @return int
*/
public function getWidth()
{
return $this->width;
}
/**
* Get current image height
*
* @return int
*/
public function getHeight()
{
return $this->height;
}
}
@tochie

This comment has been minimized.

Copy link

tochie commented Jan 18, 2013

This looks good. will test it

@ToddWelch

This comment has been minimized.

Copy link

ToddWelch commented Feb 22, 2013

I want my images to be no more than 960px wide AND no more than 400px height. How can I use this code to do that. When I run the resample and the image and width are larger then it just does the width and ignores the height so that I end up with an image that is say 960x550 when it should have re-sized on the width.

@ghost

This comment has been minimized.

Copy link

ghost commented May 19, 2013

This is great! However, I'm having the same problem as ToddWelch. Any idea what is causing this?

@derwydd

This comment has been minimized.

Copy link

derwydd commented Jun 17, 2013

Everything works great, but when it saves an image it rotates it 90 degrees to the left. How can I fix this?

Thanks.

@shajanjp

This comment has been minimized.

Copy link

shajanjp commented Jul 12, 2013

Not working for me, Am getting a blank page. Need help.

@roumor

This comment has been minimized.

Copy link

roumor commented Jul 17, 2013

that php function imagecopyresampled() in the ->resamle() method sometimes turns photos taken with iphone 90deg. I tried turning photoes in windows viewer and then resamle() to see if it happens too but it seems it's some iphone strange method of writing pics.

@nesta32, ToddWelch:
resample() function itself is no good with that "$constrainProportions = true". Set it to false and maintain proportions how you need them.

@jamesbxiong

This comment has been minimized.

Copy link

jamesbxiong commented Jul 26, 2013

Is there a way to adjust the max size of the original image? It work good for me but it will fail if my original file is larger than 2000k.That mean I have to use other application to resize all my picture to less than 2000k before I upload. That is the whole piont I don't want to do in the first place.

@YoshiPeters

This comment has been minimized.

Copy link

YoshiPeters commented Aug 1, 2013

Is it only me or is the transparency of a PNG turning black after a resample?

@robinbonnes

This comment has been minimized.

Copy link

robinbonnes commented Aug 27, 2013

@VoxeIIT:

Change resample function to:

public function resample($width, $height, $constrainProportions = true)
{
    if (!is_resource($this->image)) {
        throw new RuntimeException('No image set');
    }
    if ($constrainProportions) {
        if ($this->height >= $this->width) {
            $width  = round($height / $this->height * $this->width);
        } else {
            $height = round($width / $this->width * $this->height);
        }
    }

    $temp = imagecreatetruecolor($width, $height);

    // PNG/GIF Transparency
    imagealphablending($temp, false);
    imagesavealpha($temp,true);
    $transparent = imagecolorallocatealpha($temp, 255, 255, 255, 127);
    imagefilledrectangle($temp, 0, 0, $width, $height, $transparent);

    imagecopyresampled($temp, $this->image, 0, 0, 0, 0, $width, $height, $this->width, $this->height);
    return $this->_replace($temp);
}

and change save function to:

public function save($fileName, $type = IMAGETYPE_PNG)
{
    $dir = dirname($fileName);
    if (!is_dir($dir)) {
        if (!mkdir($dir, 0755, true)) {
            throw new RuntimeException('Error creating directory ' . $dir);
        }
    }

    try {
        switch ($type) {
            case IMAGETYPE_GIF  :
                if (!imagegif($this->image, $fileName)) {
                    throw new RuntimeException;
                }
                break;
            case IMAGETYPE_PNG  :

                // PNG Transparency
                imagealphablending($this->image, false);
                imagesavealpha($this->image,true);

                if (!imagepng($this->image, $fileName)) {
                    throw new RuntimeException;
                }
                break;
            case IMAGETYPE_JPEG :
            default             :
                if (!imagejpeg($this->image, $fileName, 95)) {
                    throw new RuntimeException;
                }
        }
    } catch (Exception $ex) {
        throw new RuntimeException('Error saving image file to ' . $fileName);
    }
}
@xhava

This comment has been minimized.

Copy link

xhava commented Jun 12, 2014

hi @robinbonnes . how can i change that . because if i have this public function save($fileName, $type = IMAGETYPE_PNG) mi jpg images are not saved.

@paulo-sorrentino

This comment has been minimized.

Copy link

paulo-sorrentino commented Aug 20, 2014

This has been very useful. Thanks a lot!!

@suseno

This comment has been minimized.

Copy link

suseno commented Aug 26, 2014

hai mister, iam trouble,
-server linux
-php5_gd

error blank page

if server in window work

@gargakk

This comment has been minimized.

Copy link

gargakk commented Mar 1, 2015

Hi, really nice script but i have a problem. Uploaded images are rotated. Is there a solution for this?

Thanks

@shaneparsons

This comment has been minimized.

Copy link

shaneparsons commented Mar 24, 2015

@ToddWelch @nesta32 and anyone else who has a similar issue, switching around line 109 and 111 worked for me.

ex.

if ($constrainProportions) {
   if ($this->height >= $this->width) {
       $height = round($width / $this->width * $this->height);
   } else {
       $width  = round($height / $this->height * $this->width);
   }
}
@Cartman34

This comment has been minimized.

Copy link

Cartman34 commented Apr 6, 2016

Thank you, you should right your license with author things.

@rohitkhatri

This comment has been minimized.

Copy link

rohitkhatri commented Aug 19, 2016

How to crop and return image binary from the centre, like I have an image which has 1900px width and 1000px height, and I want to get it cropped from the middle of height. Here's the example https://s4.postimg.org/mppvxfzy5/tesla.jpg and I want the selected area of image. Thanks

@igorkrnjajic

This comment has been minimized.

Copy link

igorkrnjajic commented Oct 26, 2016

@rohitkhatri
You probably already figured this out, but here's how I managed to get this solved if anyone else runs into this problem:

  • Just add the following function to this class file
public function crop_from_center($width, $height)
    {
        if (!is_resource($this->image)) {
            throw new RuntimeException('No image set');
        }

        $src_x = ($this->width/2) - ($width/2);
        $src_y = ($this->height/2) - ($height/2);

        $temp = imagecreatetruecolor($width, $height);
        imagecopy($temp, $this->image, 0, 0, $src_x, $src_y, $width, $height);

        return $this->_replace($temp);
    }

This should crop the image from the center after inputting the desired width and height of final image.

Btw, @philBrown, thanks for sharing, this worked great for me ;)

@jonkootje

This comment has been minimized.

Copy link

jonkootje commented Mar 9, 2017

Sometimes i got a problem that the image gets rotated 90deg after saving, how to fix?... oh i now see the other comments XD Sorry bout that

@kressly

This comment has been minimized.

Copy link

kressly commented Mar 7, 2018

How to use this with a Jpeg file practically ? Can you please provide us with an example on how to use ?

@phucnguyenbw

This comment has been minimized.

Copy link

phucnguyenbw commented Jun 18, 2018

Thank you

@sajadshafizadeh

This comment has been minimized.

Copy link

sajadshafizadeh commented Oct 13, 2018

The issue is I cannot save a moving image (I mean .gif format, something like this). It will be a saved as a non-moving image (I guess the first frame of that .gif) image. Something like this. Any idea how can I fix it?

@krupalpatel92

This comment has been minimized.

Copy link

krupalpatel92 commented Feb 11, 2019

@philBrown

Warning: imagecreatefrompng(): '/xxx/xxx/hello.png' is not a valid PNG file

Thanks! for this class. It's awesome save my day 👍

I'm facing an issue with PNG file format after resample, crop, enlargecanvas, and save but JPEG/JPG file is working correct.

Error

Warning: imagecreatefrompng(): '/xxxxx/project/start_img_thumb.png' is not a valid PNG file in /xxxxx/project/index.php on line 259

Code

$im = new ImageManipulator(getcwd()."/index.png");

/* Resize Image */

$canvas_width = 500;
$canvas_height = 150;
$proportions = true;
$im->resample($canvas_width,$canvas_height,$proportions);

$centreX = round($im->getWidth() / 2);
$centreY = round($im->getHeight() / 2);
$x1 = $centreX - ($canvas_width / 2);
$y1 = $centreY - ($canvas_height / 2);
$x2 = $centreX + ($canvas_width / 2);
$y2 = $centreY + ($canvas_height / 2);

$im->crop($x1, $y1, $x2, $y2); // takes care of out of boundary conditions automatically
$im->enlargeCanvas($canvas_width,$canvas_height,[255,255,255]);
$im->save(getcwd()."/start_img_thumb.png");*/

try{
    imagecreatefrompng(getcwd()."/start_img_thumb.png");
}catch(Exception $e){

print_r($e);

}

Note

  1. Error facing with only PNG file type after manipulating it.
  2. Tested with diffrent kind of PNG file transparent and none transparent. Images are created from Photoshop directly. So, there is zero possiblity about corrupt image.

Can anyone help about it

@eeverts

This comment has been minimized.

Copy link

eeverts commented Dec 9, 2019

Hi, really nice script but i have a problem. Uploaded images are rotated. Is there a solution for this?

Thanks

I made a fork to correct this. Needed: function exif_read_data.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.