b2evolution PHP Cross Reference Blogging Systems

Source: /inc/files/model/_image.funcs.php - 589 lines - 16055 bytes - Summary - Text - Print

Description: This file implements various Image File handling functions. This file is part of the evoCore framework - {@link http://evocore.net/} See also {@link http://sourceforge.net/projects/evocms/}.

   1  <?php
   2  /**
   3   * This file implements various Image File handling functions.
   4   *
   5   * This file is part of the evoCore framework - {@link http://evocore.net/}
   6   * See also {@link http://sourceforge.net/projects/evocms/}.
   7   *
   8   * @copyright (c)2003-2014 by Francois Planque - {@link http://fplanque.com/}
   9   *
  10   * {@internal License choice
  11   * - If you have received this file as part of a package, please find the license.txt file in
  12   *   the same folder or the closest folder above for complete license terms.
  13   * - If you have received this file individually (e-g: from http://evocms.cvs.sourceforge.net/)
  14   *   then you must choose one of the following licenses before using the file:
  15   *   - GNU General Public License 2 (GPL) - http://www.opensource.org/licenses/gpl-license.php
  16   *   - Mozilla Public License 1.1 (MPL) - http://www.opensource.org/licenses/mozilla1.1.php
  17   * }}
  18   *
  19   * {@internal Open Source relicensing agreement:
  20   * }}
  21   *
  22   * @package evocore
  23   *
  24   * {@internal Below is a list of authors who have contributed to design/coding of this file: }}
  25   * @author fplanque: Francois PLANQUE.
  26   *
  27   * @version $Id: _image.funcs.php 6136 2014-03-08 07:59:48Z manuel $
  28   */
  29  if( !defined('EVO_MAIN_INIT') ) die( 'Please, do not access this page directly.' );
  30  
  31  
  32  /**
  33   * Get available thumbnail sizes
  34   *
  35   * @param string The text that used for the "None" option 
  36   * @return array 'key'=>'name'
  37   */
  38  function get_available_thumb_sizes( $allow_none_text = NULL )
  39  {
  40      global $thumbnail_sizes;
  41  
  42      $thumb_size_names = array();
  43  
  44      if( !empty( $allow_none_text ) )
  45      {    // 'None' option
  46          $thumb_size_names[''] = $allow_none_text;
  47      }
  48  
  49      foreach( $thumbnail_sizes as $key=>$dummy )
  50      {
  51          $thumb_size_names[$key] = $key;
  52      }
  53  
  54      return $thumb_size_names;
  55  }
  56  
  57  
  58  /**
  59   * Crop dimensions to fit into a constrained size, while preserving aspect ratio.
  60   *
  61   * @param integer source width
  62   * @param integer source height
  63   * @param integer constrained width
  64   * @param integer constrained height
  65   * @param string align: center, top
  66   * @return array ( x, y, width, height )
  67   */
  68  function crop_to_constraint( $src_width, $src_height, $max_width, $max_height, $align = 'center' )
  69  {
  70      $src_ratio = $src_width / $src_height;
  71      $max_ratio = $max_width / $max_height;
  72      if( $max_ratio <= $src_ratio )
  73      {
  74          $y = 0;
  75          $x = ($src_width - ($src_width * ($max_ratio/$src_ratio))) / 2;
  76          $src_width = $src_width * ($max_ratio/$src_ratio);
  77      }
  78      else
  79      {
  80          $x = 0;
  81          if( $align == 'top' )
  82          {    // top - 15%
  83              $y = ( $src_height - $src_width ) * 0.15;
  84          }
  85          else
  86          {    // center
  87              $y = ($src_height - $src_width/$max_ratio) / 2;
  88          }
  89          $src_height = $src_width/$max_ratio;
  90      }
  91  
  92      return array( $x, $y, $src_width, $src_height );
  93  }
  94  
  95  
  96  /**
  97   * Scale dimensions to fit into a constrained size, while preserving aspect ratio.
  98   *
  99   * @param integer source width
 100   * @param integer source height
 101   * @param integer constrained width (might be NULL/0 to use source width)
 102   * @param integer constrained height (might be NULL/0 to use source height)
 103   * @return array (width, height)
 104   */
 105  function scale_to_constraint( $src_width, $src_height, $max_width, $max_height )
 106  {
 107      if( $max_width == 0 )
 108          $max_width = $src_width;
 109      if( $max_height == 0 )
 110          $max_height = $src_height;
 111  
 112      $src_ratio = $src_width / $src_height;
 113      if( $max_width / $max_height <= $src_ratio )
 114      {
 115          $width = $max_width;
 116          $height = (int)round( $max_width / $src_ratio );
 117      }
 118      else
 119      {
 120          $width = (int)round( $max_height * $src_ratio );
 121          $height = $max_height;
 122      }
 123  
 124      return array( $width, $height );
 125  }
 126  
 127  
 128  /**
 129   * Scale dimensions to fit into a constrained size, while preserving aspect ratio.
 130   * The scaling only happens if the source is larger than the constraint.
 131   *
 132   * @param integer source width
 133   * @param integer source height
 134   * @param integer constrained width
 135   * @param integer constrained height
 136   */
 137  function fit_into_constraint( $src_width, $src_height, $max_width, $max_height )
 138  {
 139      if( $src_width > $max_width || $src_height > $max_height )
 140      {
 141          return scale_to_constraint( $src_width, $src_height, $max_width, $max_height );
 142      }
 143  
 144      return array( $src_width, $src_height );
 145  }
 146  
 147  
 148  /**
 149   * Load an image from a file into memory
 150   *
 151   * @param string pathname of image file
 152   * @param string
 153   * @return array resource image handle or NULL
 154   */
 155  function load_image( $path, $mimetype )
 156  {
 157      // yabs> GD library uses shedloads of memory
 158      // fp> 256M is way too high to sneak this in here. There should be some checks in the systems page to warn against low memory conditions. Also i'm not sure it makes sense to bump memory just for images. If you allow memory you might as well allow it for anything. Anyways, this is too much to be snuk in.
 159      // @ini_set('memory_limit', '256M'); // artificially inflate memory if we can
 160      $err = NULL;
 161      $imh = NULL;
 162      $function = NULL;
 163  
 164      $image_info = getimagesize($path);
 165  
 166      if( ! $image_info || $image_info['mime'] != $mimetype )
 167      {
 168          $FiletypeCache = get_Cache('FiletypeCache');
 169          $correct_Filetype = $FiletypeCache->get_by_mimetype($image_info['mime']);
 170          $correct_extension = array_shift($correct_Filetype->get_extensions());
 171  
 172          $path_info = pathinfo($path);
 173          $wrong_extension = $path_info['extension'];
 174  
 175          $err = '!'.$correct_extension.' extension mismatch: use .'.$correct_extension.' instead of .'.$wrong_extension;
 176      }
 177      else
 178      {
 179          $mime_function = array(
 180                  'image/jpeg' => 'imagecreatefromjpeg',
 181                  'image/gif'  => 'imagecreatefromgif',
 182                  'image/png'  => 'imagecreatefrompng',
 183              );
 184  
 185          if( isset($mime_function[$mimetype]) )
 186          {
 187              $function = $mime_function[$mimetype];
 188          }
 189          else
 190          { // Unrecognized mime type
 191              $err = '!Unsupported format '.$mimetype.' (load_image)';
 192          }
 193      }
 194      //pre_dump( $function );
 195      if( $function )
 196      {    // Call GD built-in function to load image
 197          // fp> Note: sometimes this GD call will die and there is no real way to recover :/
 198          load_funcs( 'tools/model/_system.funcs.php' );
 199          $memory_limit = system_check_memory_limit();
 200          $curr_mem_usage = memory_get_usage( true );
 201          // Calculate the aproximative memory size which would be required to create the image resource
 202          $tweakfactor = 1.8; // Or whatever works for you
 203          $memory_needed = round( ( $image_info[0] * $image_info[1]
 204                  * ( isset( $image_info['bits'] ) ? $image_info['bits'] : 4 )
 205                  * ( isset( $image_info['channels'] ) ? $image_info['channels'] / 8 : 1 )
 206                  + Pow( 2, 16 ) // number of bytes in 64K
 207              ) * $tweakfactor );
 208          if( ( $memory_limit - $curr_mem_usage ) < $memory_needed )// ( 4 * $image_info[0] * $image_info[1] ) )
 209          { // Don't try to load the image into the memory because it would cause 'Allowed memory size exhausted' error
 210              return array( "!Cannot resize too large image", false );
 211          }
 212          $imh = $function( $path );
 213      }
 214  
 215      if( $imh === false )
 216      {
 217          trigger_error( 'load_image failed: '.$path.' / '.$mimetype ); // DEBUG
 218          // e.g. "imagecreatefromjpeg(): $FILE is not a valid JPEG file"
 219          $err = '!load_image failed (no valid image?)';
 220      }
 221      if( $err )
 222      {
 223          error_log( 'load_image failed: '.substr($err, 1).' ('.$path.' / '.$mimetype.')' );
 224      }
 225  
 226      return array( $err, $imh );
 227  }
 228  
 229  
 230  /**
 231   * Output an image from memory to web client
 232   *
 233   * @param resource image handle
 234   * @param string pathname of image file
 235   * @param string
 236   * @param integer
 237   * @param string permissions
 238   * @return string
 239   */
 240  function save_image( $imh, $path, $mimetype, $quality = 90, $chmod = NULL )
 241  {
 242      $err = NULL;
 243  
 244      switch( $mimetype )
 245      {
 246          case 'image/jpeg':
 247              $r = @imagejpeg( $imh, $path, $quality );
 248              break;
 249  
 250          case 'image/gif':
 251              $r = @imagegif( $imh, $path );
 252              break;
 253  
 254          case 'image/png':
 255              $r = @imagepng( $imh, $path );
 256              break;
 257  
 258           default:
 259              // Unrecognized mime type
 260              $err = '!Unsupported format '.$mimetype.' (save_image)';
 261              break;
 262      }
 263  
 264      // Catch any errors by image* functions:
 265      if( ! $r )
 266      {
 267          // TODO: dh> This might become a generic function, since it's useful. Something similar is used in DB, too.
 268          if( isset($php_errormsg) )
 269              $err = '!'.$php_errormsg;
 270          elseif( function_exists('error_get_last') ) // PHP 5.2
 271          {
 272              $err = error_get_last();
 273              $err = '!'.$err['message'];
 274          }
 275  
 276          if( ! isset($err) )
 277              $err = '!Unknown error in save_image().';
 278      }
 279  
 280      if( empty( $err ) )
 281      {
 282          // Make sure the file has the default permissions we want:
 283          if( $chmod === NULL )
 284          {
 285              global $Settings;
 286              $chmod = $Settings->get('fm_default_chmod_file');
 287          }
 288          chmod( $path, octdec( $chmod ) );
 289      }
 290  
 291      return $err;
 292  }
 293  
 294  
 295  /**
 296   * Output an image from memory to web client
 297   *
 298   * @param resource image handle
 299   * @param string
 300   * @return string
 301   */
 302  function output_image( $imh, $mimetype )
 303  {
 304      $err = NULL;
 305  
 306      switch( $mimetype )
 307      {
 308          case 'image/jpeg':
 309              header('Content-type: '.$mimetype );
 310              imagejpeg( $imh );
 311              break;
 312  
 313          case 'image/gif':
 314              header('Content-type: '.$mimetype );
 315              imagegif( $imh );
 316              break;
 317  
 318           default:
 319              // Unrecognized mime type
 320              $err = 'Emime';    // Sort error code
 321              break;
 322      }
 323  
 324      return $err;
 325  }
 326  
 327  
 328  
 329  
 330  /**
 331   * Generate a thumbnail
 332   *
 333   * @param resource Image resource
 334   * @param string Thumbnail type ('crop'|'fit')
 335   * @param int Thumbnail width
 336   * @param int Thumbnail height
 337   * @param int Thumbnail percent of blur effect (0 - No blur, 1% - Max blur effect, 99% - Min blur effect)
 338   * @return array short error code + dest image handler
 339   */
 340  function generate_thumb( $src_imh, $thumb_type, $thumb_width, $thumb_height, $thumb_percent_blur = 0 )
 341  {
 342      $src_width = imagesx( $src_imh ) ;
 343      $src_height = imagesy( $src_imh );
 344  
 345      if( $src_width <= $thumb_width && $src_height <= $thumb_height )
 346      {    // There is no need to resample, use original!
 347          return array( NULL, $src_imh );
 348      }
 349  
 350      switch( $thumb_type )
 351      {
 352          case 'crop':
 353          case 'crop-top':
 354              $align = $thumb_type == 'crop-top' ? 'top' : 'center';
 355              list( $src_x, $src_y, $src_width, $src_height) = crop_to_constraint( $src_width, $src_height, $thumb_width, $thumb_height, $align );
 356              $dest_width = $thumb_width;
 357              $dest_height = $thumb_height;
 358              break;
 359  
 360          case 'fit':
 361          default:
 362              list( $dest_width, $dest_height ) = scale_to_constraint( $src_width, $src_height, $thumb_width, $thumb_height );
 363              $src_x = $src_y = 0;
 364      }
 365  
 366      // pre_dump( $src_x, $src_y, $dest_width, $dest_height, $src_width, $src_height );
 367  
 368      // Create a transparent image:
 369      $dest_imh = imagecreatetruecolor( $dest_width, $dest_height );
 370      imagealphablending($dest_imh, true);
 371      imagefill($dest_imh, 0, 0, imagecolortransparent($dest_imh, imagecolorallocatealpha($dest_imh, 0, 0, 0, 127)));
 372      imagesavealpha($dest_imh, true);
 373  
 374      if( ! imagecopyresampled( $dest_imh, $src_imh, 0, 0, $src_x, $src_y, $dest_width, $dest_height, $src_width, $src_height ) )
 375      {
 376          return array( '!GD-library internal error (resample)', $dest_imh );
 377      }
 378  
 379      if( $thumb_percent_blur > 0 )
 380      {    // Apply blur effect
 381          $dest_imh = pixelblur( $dest_imh, $dest_width, $dest_height, $thumb_percent_blur );
 382      }
 383  
 384      // TODO: imageinterlace();
 385  
 386      return array( NULL, $dest_imh );
 387  }
 388  
 389  
 390  /**
 391   * Apply blur effect
 392   *
 393   * @param resource Image resource
 394   * @param int Source width
 395   * @param int Source height
 396   * @param int Percent of blur effect (0 - No blur, 1% - Max blur effect, 99% - Min blur effect)
 397   * @return resource Image resource
 398   */
 399  function pixelblur( $image_source, $width_source, $height_source, $percent_blur )
 400  {
 401      if( $percent_blur < 1 && $percent_blur > 99 )
 402      {    // Don't spend a time for processing of blur effect with bad percent request
 403          return $image_source;
 404      }
 405  
 406      $width_resized = ceil( $width_source * $percent_blur / 100 );
 407      $height_resized = ceil( $height_source * $percent_blur / 100 );
 408  
 409      $image_resized = imagecreatetruecolor( $width_resized, $height_resized );
 410      // Reduce image size by given percent
 411      imagecopyresampled( $image_resized, $image_source, 0, 0, 0, 0, $width_resized, $height_resized, $width_source, $height_source );
 412      // Apply blur effect from GD library
 413    if( function_exists('imagefilter') )
 414    {
 415      imagefilter( $image_resized, IMG_FILTER_GAUSSIAN_BLUR );
 416    }
 417      // Expand image to the source size
 418      imagecopyresampled( $image_source, $image_resized, 0, 0, 0, 0, $width_source, $height_source, $width_resized, $height_resized );
 419  
 420      return $image_source;
 421  }
 422  
 423  
 424  /**
 425   * Rotate image
 426   *
 427   * @param object File
 428   * @param integer # degrees to rotate
 429   * @return boolean TRUE if rotating is successful
 430   */
 431  function rotate_image( $File, $degrees )
 432  {
 433      $Filetype = & $File->get_Filetype();
 434      if( !$Filetype )
 435      {    // Error
 436          return false;
 437      }
 438  
 439      // Load image
 440      list( $err, $imh ) = load_image( $File->get_full_path(), $Filetype->mimetype );
 441      if( !empty( $err ) )
 442      {    // Error
 443          return false;
 444      }
 445  
 446      // Rotate image
 447      if( ! $imh = @imagerotate( $imh, (int)$degrees, 0 ) )
 448      {    // If func imagerorate is not defined for example:
 449          return false;
 450      }
 451  
 452      // Save image
 453      save_image( $imh, $File->get_full_path(), $Filetype->mimetype );
 454  
 455      // Remove the old thumbnails
 456      $File->rm_cache();
 457  
 458      return true;
 459  }
 460  
 461  
 462  /**
 463   * Provide imagerotate for undefined cases
 464   *
 465   * Rotate an image with a given angle
 466   * @param resource Image: An image resource, returned by one of the image creation functions, such as imagecreatetruecolor().
 467   * @param integer Angle: Rotation angle, in degrees. The rotation angle is interpreted as the number of degrees to rotate the image anticlockwise.
 468   * @param string Bgd_color: Specifies the color of the uncovered zone after the rotation
 469   * @param integer Ignore_transparent: If set and non-zero, transparent colors are ignored (otherwise kept).
 470   * @return resource Returns an image resource for the rotated image, or FALSE on failure.
 471   */
 472  if( !function_exists( 'imagerotate' ) )
 473  {
 474      /**
 475       * Imagerotate replacement. ignore_transparent is work for png images
 476       * Also, have some standard functions for 90, 180 and 270 degrees.
 477       */
 478  	function imagerotate( $srcImg, $angle, $bgcolor, $ignore_transparent = 0 )
 479      {
 480  		function rotateX( $x, $y, $theta )
 481          {
 482              return $x * cos( $theta ) - $y * sin( $theta );
 483          }
 484  		function rotateY( $x, $y, $theta )
 485          {
 486              return $x * sin( $theta ) + $y * cos( $theta );
 487          }
 488  
 489          $srcw = imagesx( $srcImg );
 490          $srch = imagesy( $srcImg );
 491  
 492          //Normalize angle
 493          $angle %= 360;
 494  
 495          if( $angle == 0 )
 496          {
 497              if( $ignore_transparent == 0 )
 498              {
 499                  imagesavealpha( $srcImg, true );
 500              }
 501              return $srcImg;
 502          }
 503  
 504          // Convert the angle to radians
 505          $theta = deg2rad( $angle );
 506  
 507          //Standart case of rotate
 508          if( ( abs( $angle ) == 90 ) || ( abs( $angle ) == 270) )
 509          {
 510              $width = $srch;
 511              $height = $srcw;
 512              if( ( $angle == 90 ) || ( $angle == -270 ) )
 513              {
 514                  $minX = 0;
 515                  $maxX = $width;
 516                  $minY = -$height+1;
 517                  $maxY = 1;
 518              }
 519              else if( ( $angle == -90 ) || ( $angle == 270 ) )
 520              {
 521                  $minX = -$width+1;
 522                  $maxX = 1;
 523                  $minY = 0;
 524                  $maxY = $height;
 525              }
 526          }
 527          else if( abs( $angle ) === 180 )
 528          {
 529              $width = $srcw;
 530              $height = $srch;
 531              $minX = -$width+1;
 532              $maxX = 1;
 533              $minY = -$height+1;
 534              $maxY = 1;
 535          }
 536          else
 537          {
 538              // Calculate the width of the destination image.
 539              $temp = array( rotateX( 0, 0, 0-$theta ),
 540                      rotateX( $srcw, 0, 0-$theta ),
 541                      rotateX( 0, $srch, 0-$theta ),
 542                      rotateX( $srcw, $srch, 0-$theta )
 543                  );
 544              $minX = floor( min( $temp ) );
 545              $maxX = ceil( max( $temp ) );
 546              $width = $maxX - $minX;
 547  
 548              // Calculate the height of the destination image.
 549              $temp = array( rotateY( 0, 0, 0-$theta ),
 550                      rotateY( $srcw, 0, 0-$theta ),
 551                      rotateY( 0, $srch, 0-$theta ),
 552                      rotateY( $srcw, $srch, 0-$theta )
 553                  );
 554              $minY = floor( min( $temp ) );
 555              $maxY = ceil( max( $temp ) );
 556              $height = $maxY - $minY;
 557          }
 558  
 559          $destimg = imagecreatetruecolor( $width, $height );
 560          if( $ignore_transparent == 0 )
 561          {
 562              imagefill( $destimg, 0, 0, imagecolorallocatealpha( $destimg, 255,255, 255, 127 ) );
 563              imagesavealpha( $destimg, true );
 564          }
 565  
 566          // sets all pixels in the new image
 567          for( $x = $minX; $x < $maxX; $x++ )
 568          {
 569              for( $y = $minY; $y < $maxY; $y++ )
 570              {
 571                  // fetch corresponding pixel from the source image
 572                  $srcX = round( rotateX( $x, $y, $theta ) );
 573                  $srcY = round( rotateY( $x, $y, $theta ) );
 574                  if( $srcX >= 0 && $srcX < $srcw && $srcY >= 0 && $srcY < $srch )
 575                  {
 576                      $color = imagecolorat( $srcImg, $srcX, $srcY );
 577                  }
 578                  else
 579                  {
 580                      $color = $bgcolor;
 581                  }
 582                  imagesetpixel( $destimg, $x-$minX, $y-$minY, $color );
 583              }
 584          }
 585          return $destimg;
 586      }
 587  }
 588  
 589  ?>

title

Description

title

Description

title

Description

title

title

Body