Tuesday, March 20, 2012

A Not So Simple Image PHP Object

Extending the functionality of the original SimpleImage.php PHP class to include method daisy-chaining, n-level layering using different object instances and background image support, SimpleImage is can be not so simple anymore. Here's the code:

<?php

/*
 * File: SimpleImage.php (Not Really)
 * Author: Rumverse, Simon Jarvis
 * Copyright: 2006 Simon Jarvis, 2011-2012 Rumverse
 * Date: 08/11/06
 * Last Modified: 02/16/2012
 * Link: http://www.white-hat-web-design.co.uk/articles/php-image-resizing.php
 * Link: http://ulaptech.blogspot.com/2012/03/not-so-simple-image-php-object.html
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details:
 * http://www.gnu.org/licenses/gpl.html
 *
 * rumverse: added support for daisy-chaining via making sure that there's always
 * a version of the original, added additional get methods, added background, added
 * layer support  
 *
 */

class SimpleImage {
 /**
  * Originally loaded image
  *
  * @var resource
  */
 protected $_imageOri;
 /**
  * Transformed image buffer
  *
  * @var resource
  */
 protected $_image;
 /**
  * List of SimpleImage instances which can be used as 
  *
  * @var array
  */
 protected $_layers;
 /**
  * Should be used as the background of the image
  *
  * @var SimpleImage
  */
 protected $_background;
 /**
  * In layered mode, is image centered or not? 
  * It's true by default.
  *
  * @var bool
  */
 protected $_centered = true;
 /**
  * Horizontal alignment, center, left, right
  *
  * @var string
  */
 protected $_align;
 /**
  * Vertical alignment, center, top, bottom
  *
  * @var string
  */
 protected $_valign;
 /**
  * Image type - IMAGETYPE_GIF, IMAGETYPE_JPEG, IMAGETYPE_PNG
  *
  * @var int
  */
 protected $_image_type;
 protected $_sizeInfo;
 protected $_validMime = array('image/jpeg' => self::IMAGEJPEG,
                                 'image/jpg' => self::IMAGEJPEG,
                                 'image/png' => self::IMAGEPNG,
                                 'image/gif' => self::IMAGEGIF);

 const IMAGEJPEG = 'image/jpeg';
 const IMAGEGIF  = 'image/gif';
 const IMAGEPNG  = 'image/png';
 /**
  * x-point position relative to destination image xy plane if object
  * is used as a layer by another instance of SimpleImage
  *
  * @var int
  */
 protected $_layer_xpos = 0;
 /**
  * y-point position relative to destination image xy plane if object
  * is used as a layer by another instance of SimpleImage
  *
  * @var int
  */
 protected $_layer_ypos = 0;
 /**
  * x-point position of this object's xy plane to start copying from when
  * used as a layer of another instance of SimpleImage
  *
  * @var int
  */
 protected $_layer_xOffset = 0;
 /**
  * y-point position of this object's xy plane to start copying from when
  * used as a layer of another instance of SimpleImage
  *
  * @var int
  */
 protected $_layer_yOffset = 0;
 public function __construct($filename = null)
 {
  if (!empty($filename) && is_string($filename)) {
   $this->load($filename);
  }
 }
 
 public function load($filename)
 {

  if (!is_file($filename)) {
   trigger_error("Warning: $filename not found", E_USER_WARNING);
   return false;
  }

  $image_info = getimagesize($filename, $additional_info);

  if (!isset($this->_validMime[$image_info['mime']])) {
   trigger_error("Warning: file type {$image_info['mime']} not yet supported.", E_USER_WARNING);
   return false;
  }

  $this->_sizeInfo = array_merge($image_info, $additional_info);
  $this->_image_type = $image_info[2];

  if( $this->_image_type == IMAGETYPE_JPEG ) {
            try {
                $resource =  imagecreatefromjpeg($filename);
                if(!is_resource($resource)) {
                    trigger_error("Failed to load image but no exceptions raised: {$filename}\n", E_USER_WARNING);
                    return false;
                }
            } catch(Exception $e) {
                trigger_error("Invalid image: {$filename} - {$e->getMessage}\n", E_USER_ERROR);
                return false;
            }
   $this->_image = $resource;
   $this->_imageOri = imagecreatefromjpeg($filename);

  } elseif( $this->_image_type == IMAGETYPE_GIF ) {
            try {
                $resource =  imagecreatefromgif($filename);
                if(!is_resource($resource)) {
                    trigger_error("Failed to load image but no exceptions raised: {$filename}\n", E_USER_WARNING);
                    return false;
                }
            } catch(Exception $e) {
                trigger_error("Invalid image: {$filename} - {$e->getMessage}\n", E_USER_ERROR);
                return false;
            }
   $this->_image = $resource;
   $this->_imageOri = imagecreatefromgif($filename);

  } elseif( $this->_image_type == IMAGETYPE_PNG ) {
   
   try {
                $resource =  imagecreatefrompng($filename);
                if(!is_resource($resource)) {
                    error_log($filename . "\n", 3, 'failed-load-image.txt');
                    trigger_error("Failed to load image but no exceptions raised: {$filename}\n", E_USER_WARNING);
                    return false;
                }
            } catch(Exception $e) {
                 trigger_error("Invalid image: {$filename} - {$e->getMessage}\n", E_USER_ERROR);
                 return false;
            }
              
   $this->_image = $resource;
   $this->_imageOri = imagecreatefrompng($filename);

  }

        return true;
 }
 
 public function addLayer(SimpleImage $layer = null)
 {
  if (!empty($layer) && $layer instanceof SimpleImage) {
   $this->_layers[] = $layer; 
  }
  return $this;
 }
 
 public function hasLayers()
 {
  if (!empty($this->_layers) && $this->_layers[0] instanceof SimpleImage ) {
   return true;
  }
 }
 
 public function getValign()
 {
  return $this->_valign;
 }
 
 public function setValign($valign = 'center') 
 {
  if (!empty($valign) && is_string($valign) && in_array($valign, array('center','top','bottom'))) {
   $this->_valign = $valign; 
  }
  return $this;
 }
 
 public function getAlign()
 {
  return $this->_align;
 }
 
 public function setAlign($valign = 'center') 
 {
  if (!empty($valign) && is_string($valign) && in_array($valign, array('center','left','right'))) {

   $this->_align = $valign; 
  }
  return $this;
 }
 
 public function isCentered()
 {
  if ($this->_centered) {
   return true;
  }
 }
 
 public function setCentered($enable = true)
 {
  $this->_centered = $enable;
  return $this;
 }
 /**
  * Set the x-position of this object to the destination object
  * where it is being used as a layer.
  *
  * @param int $x
  * @return $this
  */
 public function setLayerXpos($x)
 {
  if (!empty($x)) {
   $this->_layer_xpos = (integer) $x; 
  }
  return $this;
 }
 /**
  * Set the y-position of this object to the destination object
  * where it is being used as a layer.
  *
  * @param int $y
  * @return $this;
  */
 public function setLayerYpos($y)
 {
  if (!empty($y)) {
   $this->_layer_ypos = (integer) $y; 
  }
  return $this;
 }
 /**
  * Get the x-position of this object based on the destination object
  * where it is being used as a layer.
  *
  * @return int
  */
 public function getLayerXpos($reference = null)
 {
  if (empty($this->_layer_xpos)) {
   if (empty($reference) && $reference instanceof SimpleImage ) {
    $reference = $this;
   }
   //check, for centering, will be overwritten by other positional properties like valign, align, xpos, ypos
   if ($this->isCentered()) {
    
    $xDelta = $reference->getWidth() - $this->getWidth(false);
    $destXpos = ceil($xDelta/2);
   } 
   
   if ($this->getAlign()) {
    
    $align = $this->getAlign();
    switch ($align) {
     case 'left':
      $destXpos = 0;
      break;
     case 'center':
      $xDelta = $reference->getWidth() - $this->getWidth(false);
      $destXpos = ceil($xDelta/2);
      break;
     case 'right':
      
      $destXpos = $reference->getWidth() - $this->getWidth(false);
      break;
    }
   }
   $this->_layer_xpos = $destXpos;
   
  }
  return $this->_layer_xpos;
 }
 /**
  * Set the y-position of this object to the destination object
  * where it is being used as a layer.
  *
  *
  * @param SimpleImage $reference The image where it belongs to as a layer. NULL indicates that it is not a layer of anything but itself.
  * @return int
  */
 public function getLayerYpos($reference = null)
 {
  if (empty($this->_layer_ypos)) {
   if (empty($reference) && $reference instanceof SimpleImage ) {
    $reference = $this;
   }
   //check, for centering, will be overwritten by other positional properties like valign, align, xpos, ypos
   if ($this->isCentered()) {
    $yDelta = $reference->getHeight() - $this->getHeight(false);
    $destYpos = floor($yDelta/2);
   } 
   
   if ($this->getValign()) {
    $valign = $this->getValign();
    switch ($valign) {
     case 'top':
      $destYpos = 0;
      break;
     case 'center':
      $yDelta = $reference->getHeight() - $this->getHeight(false);
      $destYpos = floor($yDelta/2);
      break;
     case 'bottom':
      $destYpos = $reference->getHeight() - $this->getHeight(false);
    }
   }
     
   $this->_layer_ypos = $destYpos;
     
  }
  return $this->_layer_ypos;
 }
 /**
  * Get the x-position where to start copying on the xy plane of this object
  * when treated as layer of another SimpleImage object
  *
  * @return int
  */
 public function getLayerXOffset()
 {
  return $this->_layer_xOffset;
 }
 /**
  * Get the y-position where to start copying on the xy plane of this object
  * when treated as layer of another SimpleImage object
  *
  * @return int
  */
 public function getLayerYOffset()
 {
  return $this->_layer_yOffset;
 }
 /**
  * Set the x-position where to start copying on the xy plane of this object
  * when treated as layer of another SimpleImage object
  *
  * @return $this
  */
 public function setLayerXOffset($x)
 {
  if (!empty($x)) {
   $this->_layer_xOffset = (integer) $x; 
  }
  return $this;
 }
 /**
  * Set the x-position where to start copying on the xy plane of this object
  * when treated as layer of another SimpleImage object
  *
  * @return $this
  */
 public function setLayerYOffset($y)
 {
  if (!empty($y)) {
   $this->_layer_yOffset = (integer) $y; 
  }
  return $this;
 }
 public function getImgResourceOri()
 {
  return $this->_imageOri;
 }
 public function getImgResource()
 {
  return $this->_image;
 }
 public function getSizeInfo()
 {
  return $this->_sizeInfo;
 }
 /**
  * buffer for merged image resource
  *
  * @var resource
  */
 protected $_imageMerged;
 /**
  * When there are layers, return a merge version of the image
  *
  * @param bool $useSource
  * @return resource
  */
 public function getImageResourceMerged($useSource = true)
 {
  
  if ($this->hasLayers()) {

   $new_layered_image = imagecreatetruecolor($this->getWidth(), $this->getHeight());
   $old_image = ($useSource) ? $this->_imageOri : $this->_image;
   imagecopyresampled($new_layered_image, $old_image, 0, 0, 0, 0, $this->getWidth(), $this->getHeight(), $this->getWidth(), $this->getHeight());

   foreach ($this->_layers as $key=>$layer) {
    if ($layer instanceof SimpleImage) {
     
     $destXpos = 0;
     $destYpos = 0;
     
     $destXpos = $layer->getLayerXpos($this);
     $destYpos = $layer->getLayerYpos($this);

     imagecopymerge($new_layered_image, //dest
           $layer->getImgResource(), //source of what to copy
           $destXpos, //dest x-pos
           $destYpos, //dest y-pos
           $layer->getLayerXOffset(), //src x-pos
           $layer->getLayerYOffset(),  //src y-pos
           $layer->getWidth(false),  //src width
           $layer->getHeight(false), //src height
           100); //pct is percent merge which affects transparency etc
    }
   }
   
   $this->_imageMerged = $new_layered_image;
   
  } else {
   
   return $this->_image;
   
  }
  
  return $this->_imageMerged;
 }
 public function save($filename, $image_type=IMAGETYPE_JPEG, $compression=75, $permissions=null)
 {

  if( $image_type == IMAGETYPE_JPEG ) {
   
            try {
             if ($this->hasLayers()) {
              imagejpeg($this->getImageResourceMerged(), $filename, $compression);
             } else {
              imagejpeg($this->getImgResource(), $filename, $compression); 
             }
             return true;
    
            } catch(Exception $e) {
             echo "Failed to save image: {$filename} - {$e->getMessage}\n";
                trigger_error("Failed to save image: {$filename} - {$e->getMessage}\n", E_USER_ERROR);
                return false;
            }

  } elseif( $image_type == IMAGETYPE_GIF ) {

   try {
    if ($this->hasLayers()) {
     imagegif($this->getImageResourceMerged(), $filename);
    } else {
     imagegif($this->getImgResource(), $filename); 
    }
    return true;
    
            } catch(Exception $e) {
             echo "Failed to save image: {$filename} - {$e->getMessage}\n";
                trigger_error("Failed to save image: {$filename} - {$e->getMessage}\n", E_USER_ERROR);
                return false;
            }

  } elseif( $image_type == IMAGETYPE_PNG ) {

   try {
    if ($this->hasLayers()) {
     imagepng($this->getImageResourceMerged(), $filename);
    } else {
     imagepng($this->getImgResource(), $filename); 
    }
    return true;
    
            } catch(Exception $e) {
             echo "Failed to save image: {$filename} - {$e->getMessage}\n";
                trigger_error("Failed to save image: {$filename} - {$e->getMessage}\n", E_USER_ERROR);
                return false;
            }
  }

  if( $permissions != null) {

   chmod($filename, $permissions);
  }

  if (is_file($filename)) {
   //if saved, the original file resource will then be removed from buffer and the newly saved one becomes the original
   $this->refresh();
   return true;
  }
  return false;

 }
 
 public function refresh()
 {
  imagedestroy($this->_imageOri);
  $this->_imageOri = $this->_image;
 }

 public function output($image_type=IMAGETYPE_JPEG)
 {

  if( $image_type == IMAGETYPE_JPEG ) {

   header("Content-Type: ". self::IMAGEJPEG);
   imagejpeg($this->_image);

  } elseif( $image_type == IMAGETYPE_GIF ) {

   header("Content-Type: ". self::IMAGEGIF);
   imagegif($this->_image);

  } elseif( $image_type == IMAGETYPE_PNG ) {

   header("Content-Type: ". self::IMAGEPNG);
   imagepng($this->_image);

  }

 }

 public function getWidth($useSource = true)
 {
  if ($useSource) {

   return imagesx($this->_imageOri);

  } else {

   return imagesx($this->_image);
  }


 }

 public function getHeight($useSource = true)
 {

  if ($useSource) {

   return imagesy($this->_imageOri);

  } else {

   return imagesy($this->_image);
  }

 }

 public function resizeToHeight($height, $useSource = true)
 {

  $ratio = $height / $this->getHeight();
  $width = $this->getWidth() * $ratio;
  $this->resize($width,
  $height,
  $useSource);
  return $this;

 }

 public function resizeToWidth($width, $useSource = true)
 {
  $ratio = $width / $this->getWidth();
  $height = $this->getHeight() * $ratio;
  $this->resize($width,
  $height,
  $useSource);
  return $this;
 }

 public function scale($scale, $useSource = true)
 {
  $width = $this->getWidth() * $scale/100;
  $height = $this->getheight() * $scale/100;
  $this->resize($width,
  $height,
  $useSource);
  return $this;
 }

 public function resize($width, $height, $useSource = true)
 {
  $new_image = imagecreatetruecolor($width, $height);
  $old_image = ($useSource) ? $this->_imageOri : $this->_image;
  imagecopyresampled($new_image, $old_image, 0, 0, 0, 0, $width, $height, $this->getWidth(), $this->getHeight());

  $this->_image = $new_image;
  return $this;
 }
}
?>
  • Related Links Widget for Blogspot

No comments: