<?php 

/**
 * Thumbnail class.
 * 
 * @author Tamlyn Rhodes <tam at zenology dot co dot uk>
 * @license http://opensource.org/licenses/gpl-license.php GNU General Public License
 * @copyright (c)2003-2005 Tamlyn Rhodes
 * @version $Id: thumbnail.class.php,v 1.13 2006/06/24 20:33:00 tamlyn Exp $
 */


/**
 * Creates and manages image thumbnails
 * @package singapore
 * @author Tamlyn Rhodes <tam at zenology dot co dot uk>
 * @copyright (c)2003-2005 Tamlyn Rhodes
 */
class sgThumbnail
{
  var $config;
  var $maxWidth = 0;
  var $maxHeight = 0;
  var $thumbWidth = 0;
  var $thumbHeight = 0;
  var $cropWidth = 0;
  var $cropHeight = 0;
  var $forceSize = false;
  var $imagePath = "";
  var $thumbPath = "";
  
  function sgThumbnail(&$img, $type)
  {
    $this->config =& sgConfig::getInstance();
    $this->image  =& $img; 
    
    $widthVar  = "thumb_width_".$type;
    $heightVar = "thumb_height_".$type;
    $cropVar   = "thumb_crop_".$type;
    $this->maxWidth  = $this->config->$widthVar;
    $this->maxHeight = $this->config->$heightVar;
    if(isset($this->config->$cropVar))
      $this->forceSize = $this->config->$cropVar;
    
    if($this->image == null) return;
    
    $this->imagePath = $this->image->realPath();
    $this->thumbPath = $this->config->base_path.Singapore::thumbnailPath($this->image->parent->id, $this->image->id, $this->maxWidth, $this->maxHeight, $this->forceSize);
    $this->thumbURL  = $this->config->base_url .Singapore::thumbnailPath($this->image->parent->id, $this->image->id, $this->maxWidth, $this->maxHeight, $this->forceSize);

    //security check: make sure requested file is in galleries directory
    if(!Singapore::isSubPath($this->config->base_path.$this->config->pathto_galleries, $this->imagePath) && !$this->image->isRemote())
      return;
    
    //security check: make sure $image has a valid extension
    if(!$this->image->isRemote() && !preg_match("/.+\.(".$this->config->recognised_extensions.")$/i",$this->image->id))
      return;
  
    $this->calculateDimensions();
    
    //link straight to image if it smaller than required size
    if($this->image->width <= $this->thumbWidth && $this->image->height <= $this->thumbHeight) {
      $this->thumbURL = $this->image->realURL();
      return;
    }
    
    $imageModified = @filemtime($this->imagePath);
    $thumbModified = @filemtime($this->thumbPath);
    
    if($imageModified > $thumbModified || !$thumbModified)
      $this->buildThumbnail();
  }
  
  /** Accessor methods */
  function width()  { return $this->thumbWidth; }
  function height() { return $this->thumbHeight; }
  function URL()    { return $this->thumbURL; }
  
  /** Private methods */
  
  /**
   * Calculates thumbnail dimensions.
   */
  function calculateDimensions()
  {
    //if aspect ratio is to be constrained set crop size
    if($this->forceSize) {
      $newAspect = $this->maxWidth/$this->maxHeight;
      $oldAspect = $this->image->realWidth()/$this->image->realHeight();
      if($newAspect > $oldAspect) {
        $this->cropWidth  = $this->image->realWidth();
        $this->cropHeight = round($this->image->realHeight()*($oldAspect/$newAspect));
      } else {
        $this->cropWidth  = round($this->image->realWidth()*($newAspect/$oldAspect));
        $this->cropHeight = $this->image->realHeight();
      }
    //else crop size is image size
    } else {
      $this->cropWidth = $this->image->realWidth();
      $this->cropHeight = $this->image->realHeight();
    }
    
    if($this->cropHeight > $this->maxHeight && ($this->cropWidth <= $this->maxWidth 
       || ($this->cropWidth > $this->maxWidth && round($this->cropHeight/$this->cropWidth * $this->maxWidth) > $this->maxHeight))) {
      $this->thumbWidth  = round($this->cropWidth/$this->cropHeight * $this->maxHeight);
      $this->thumbHeight = $this->maxHeight; 
    } elseif($this->cropWidth > $this->maxWidth) {
      $this->thumbWidth  = $this->maxWidth;
      $this->thumbHeight = round($this->cropHeight/$this->cropWidth * $this->maxWidth);
    } else {
      $this->thumbWidth  = $this->image->realWidth();
      $this->thumbHeight = $this->image->realHeight();
    }
  }
  
  function buildThumbnail() {
    //set cropping offset
    $cropX = floor(($this->image->width-$this->cropWidth)/2);
    $cropY = floor(($this->image->height-$this->cropHeight)/2);
    
    //check thumbs directory exists and create it if not
    if(!file_exists(dirname($this->thumbPath)))
      Singapore::mkdir(dirname($this->thumbPath));
      
    //if file is remote then copy locally first
    if($this->image->isRemote()) {
      $ip = @fopen($this->imagePath, "rb");
      $tp = @fopen($this->thumbPath, "wb");
      if($ip && $tp) {
        while(fwrite($tp,fread($ip, 4095)));
        fclose($tp);
        fclose($ip);
        $this->imagePath = $this->thumbPath;
      }
    }
    
    switch($this->config->thumbnail_software) {
      case "im" : //use ImageMagick v5.x
        $cmd  = '"'.$this->config->pathto_convert.'"';
        if($this->forceSize) $cmd .= " -crop {$this->cropWidth}x{$this->cropHeight}+{$this->cropX}+{$this->cropY}";
        $cmd .= " -geometry {$this->thumbWidth}x{$this->thumbHeight}";
        if($this->image->type == 2) $cmd .= " -quality ".$this->config->thumbnail_quality;
        if($this->config->progressive_thumbs) $cmd .= " -interlace Plane";
        if($this->config->remove_jpeg_profile) $cmd .= ' +profile "*"';
        $cmd .= ' '.escapeshellarg($this->imagePath).' '.escapeshellarg($this->thumbPath);
        
        exec($cmd);
        
        break;
      case "im6" : //use ImageMagick v6.x  
        $cmd  = '"'.$this->config->pathto_convert.'"';
        $cmd .= ' '.escapeshellarg($this->imagePath);
        if($this->config->progressive_thumbs) $cmd .= " -interlace Plane";
        if($this->image->type == 2) $cmd .= " -quality ".$this->config->thumbnail_quality;
        if($this->forceSize) $cmd .= " -crop {$this->cropWidth}x{$this->cropHeight}+{$this->cropX}+{$this->cropY}";
        $cmd .= " -resize {$this->thumbWidth}x{$this->thumbHeight}";
        if($this->config->remove_jpeg_profile) $cmd .= ' +profile "*"';
        $cmd .= ' '.escapeshellarg($this->thumbPath);
        
        exec($cmd);
        
        break;
      case "gd2" :
      case "gd1" :
      default : //use GD by default
        //read in image as appropriate type
        switch($this->image->type) {
          case 1 : $image = ImageCreateFromGIF($this->imagePath); break;
          case 3 : $image = ImageCreateFromPNG($this->imagePath); break;
          case 2 : 
          default: $image = ImageCreateFromJPEG($this->imagePath); break;
        }
        
        if($image) {
          switch($this->config->thumbnail_software) {
          case "gd2" :
            //create blank truecolor image
            $thumb = ImageCreateTrueColor($this->thumbWidth,$this->thumbHeight);
            //resize image with resampling
            ImageCopyResampled(
              $thumb,                                $image,
              0,                 0,                  $cropX,     $cropY,
              $this->thumbWidth, $this->thumbHeight, $this->cropWidth, $this->cropHeight);
            break;
          case "gd1" :
          default :
            //create blank 256 color image
            $thumb = ImageCreate($this->thumbWidth,$this->thumbHeight);
            //resize image
            ImageCopyResized(
              $thumb,                                $image,
              0,                 0,                  $cropX,     $cropY,
              $this->thumbWidth, $this->thumbHeight, $this->cropWidth, $this->cropHeight);
            break;
          }
        } /*else {
          $thumb = ImageCreate($this->thumbWidth, $this->thumbHeight);
          $bg = ImageColorAllocate($thumb, 255, 255, 255);
          $text = ImageColorAllocate($thumb, 255, 0, 0);
          ImageString($thumb, 1, 0, 0, "Cannot load source image", $text);
        }*/
        
        //set image interlacing
        @ImageInterlace($thumb, $this->config->progressive_thumbs);
        
        //output image of appropriate type
        switch($this->image->type) {
          case 1 :
            //GIF images are saved as PNG
          case 3 :
            ImagePNG($thumb, $this->thumbPath); 
            break;
          case 2 :
          default: 
            ImageJPEG($thumb, $this->thumbPath, $this->config->thumbnail_quality); 
            break;
        }
        @ImageDestroy($image);
        @ImageDestroy($thumb);
    }
    
    //set file permissions on newly created thumbnail
    @chmod($this->thumbPath, octdec($this->config->file_mode));
  }

}

?>
