1
0
mirror of https://github.com/e107inc/e107.git synced 2025-08-11 09:04:38 +02:00

Upgrade PHP thumb and watermarks for images added

This commit is contained in:
CaMer0n
2012-07-02 01:32:56 +00:00
parent 92fc540ac6
commit cadadb29b9
11 changed files with 631 additions and 114 deletions

View File

@@ -149,6 +149,73 @@ class GdThumb extends ThumbBase
##############################
# ----- API FUNCTIONS ------ #
##############################
/**
* Pad an image to desired dimensions if required
*
* Moves the image into the center and fills the rest with $color
*
* Author: Blake Kus <http://blakek.us>
*
* @param mixed $width
* @param mixed $height
* @param mixed $color
*/
public function pad ($width, $height, $color=array(255, 255, 255))
{
// no resize - woohoo!
if($width == $this->currentDimensions['width'] && $height == $this->currentDimensions['height']){
return $this;
}
// create the working image
if (function_exists('imagecreatetruecolor'))
{
$this->workingImage = imagecreatetruecolor($width, $height);
}
else
{
$this->workingImage = imagecreate($width, $height);
}
// create the fill color
$fillColor = imagecolorallocate(
$this->workingImage,
$color[0],
$color[1],
$color[2]
);
// fill our working image with the fill color
imagefill(
$this->workingImage,
0,
0,
$fillColor
);
// copy the image into the center of our working image
imagecopyresampled
(
$this->workingImage,
$this->oldImage,
intval(($width-$this->currentDimensions['width'])/2),
intval(($height-$this->currentDimensions['height'])/2),
0,
0,
$this->currentDimensions['width'],
$this->currentDimensions['height'],
$this->currentDimensions['width'],
$this->currentDimensions['height']
);
// update all the variables and resources to be correct
$this->oldImage = $this->workingImage;
$this->currentDimensions['width'] = $width;
$this->currentDimensions['height'] = $height;
return $this;
}
/**
* Resizes an image to be no larger than $maxWidth or $maxHeight
@@ -237,14 +304,19 @@ class GdThumb extends ThumbBase
public function adaptiveResize ($width, $height)
{
// make sure our arguments are valid
if (!is_numeric($width) || $width == 0)
if ((!is_numeric($width) || $width == 0) && (!is_numeric($height) || $height == 0))
{
throw new InvalidArgumentException('$width must be numeric and greater than zero');
throw new InvalidArgumentException('$width and $height must be numeric and greater than zero');
}
if (!is_numeric($height) || $height == 0)
if (!is_numeric($width) || $width == 0)
{
throw new InvalidArgumentException('$height must be numeric and greater than zero');
$width = ( $height * $this->currentDimensions['width'] ) / $this->currentDimensions['height'];
}
if (!is_numeric($height) || $height == 0)
{
$height = ( $width * $this->currentDimensions['height'] ) / $this->currentDimensions['width'];
}
// make sure we're not exceeding our image size if we're not supposed to
@@ -324,7 +396,289 @@ class GdThumb extends ThumbBase
return $this;
}
/**
* Adaptively Resizes the Image and Crops Using a Percentage
*
* This function attempts to get the image to as close to the provided dimensions as possible, and then crops the
* remaining overflow using a provided percentage to get the image to be the size specified.
*
* The percentage mean different things depending on the orientation of the original image.
*
* For Landscape images:
* ---------------------
*
* A percentage of 1 would crop the image all the way to the left, which would be the same as
* using adaptiveResizeQuadrant() with $quadrant = 'L'
*
* A percentage of 50 would crop the image to the center which would be the same as using
* adaptiveResizeQuadrant() with $quadrant = 'C', or even the original adaptiveResize()
*
* A percentage of 100 would crop the image to the image all the way to the right, etc, etc.
* Note that you can use any percentage between 1 and 100.
*
* For Portrait images:
* --------------------
*
* This works the same as for Landscape images except that a percentage of 1 means top and 100 means bottom
*
* @param int $maxWidth
* @param int $maxHeight
* @param int $percent
* @return GdThumb
*/
public function adaptiveResizePercent ($width, $height, $percent = 50)
{
// make sure our arguments are valid
if (!is_numeric($width) || $width == 0)
{
throw new InvalidArgumentException('$width must be numeric and greater than zero');
}
if (!is_numeric($height) || $height == 0)
{
throw new InvalidArgumentException('$height must be numeric and greater than zero');
}
// make sure we're not exceeding our image size if we're not supposed to
if ($this->options['resizeUp'] === false)
{
$this->maxHeight = (intval($height) > $this->currentDimensions['height']) ? $this->currentDimensions['height'] : $height;
$this->maxWidth = (intval($width) > $this->currentDimensions['width']) ? $this->currentDimensions['width'] : $width;
}
else
{
$this->maxHeight = intval($height);
$this->maxWidth = intval($width);
}
$this->calcImageSizeStrict($this->currentDimensions['width'], $this->currentDimensions['height']);
// resize the image to be close to our desired dimensions
$this->resize($this->newDimensions['newWidth'], $this->newDimensions['newHeight']);
// reset the max dimensions...
if ($this->options['resizeUp'] === false)
{
$this->maxHeight = (intval($height) > $this->currentDimensions['height']) ? $this->currentDimensions['height'] : $height;
$this->maxWidth = (intval($width) > $this->currentDimensions['width']) ? $this->currentDimensions['width'] : $width;
}
else
{
$this->maxHeight = intval($height);
$this->maxWidth = intval($width);
}
// create the working image
if (function_exists('imagecreatetruecolor'))
{
$this->workingImage = imagecreatetruecolor($this->maxWidth, $this->maxHeight);
}
else
{
$this->workingImage = imagecreate($this->maxWidth, $this->maxHeight);
}
$this->preserveAlpha();
$cropWidth = $this->maxWidth;
$cropHeight = $this->maxHeight;
$cropX = 0;
$cropY = 0;
// Crop the rest of the image using the quadrant
if ($percent > 100) {
$percent = 100;
} elseif ($percent < 1) {
$percent = 1;
}
if ($this->currentDimensions['width'] > $this->maxWidth)
{
// Image is landscape
$maxCropX = $this->currentDimensions['width'] - $this->maxWidth;
$cropX = intval(($percent / 100) * $maxCropX);
} elseif ($this->currentDimensions['height'] > $this->maxHeight)
{
// Image is portrait
$maxCropY = $this->currentDimensions['height'] - $this->maxHeight;
$cropY = intval(($percent / 100) * $maxCropY);
}
imagecopyresampled
(
$this->workingImage,
$this->oldImage,
0,
0,
$cropX,
$cropY,
$cropWidth,
$cropHeight,
$cropWidth,
$cropHeight
);
// update all the variables and resources to be correct
$this->oldImage = $this->workingImage;
$this->currentDimensions['width'] = $this->maxWidth;
$this->currentDimensions['height'] = $this->maxHeight;
return $this;
}
/**
* Adaptively Resizes the Image and Crops Using a Quadrant
*
* This function attempts to get the image to as close to the provided dimensions as possible, and then crops the
* remaining overflow using the quadrant to get the image to be the size specified.
*
* The quadrants available are Top, Bottom, Center, Left, and Right:
*
*
* +---+---+---+
* | | T | |
* +---+---+---+
* | L | C | R |
* +---+---+---+
* | | B | |
* +---+---+---+
*
* Note that if your image is Landscape and you choose either of the Top or Bottom quadrants (which won't
* make sence since only the Left and Right would be available, then the Center quadrant will be used
* to crop. This would have exactly the same result as using adaptiveResize().
* The same goes if your image is portrait and you choose either the Left or Right quadrants.
*
* @param int $maxWidth
* @param int $maxHeight
* @param string $quadrant T, B, C, L, R
* @return GdThumb
*/
public function adaptiveResizeQuadrant ($width, $height, $quadrant = 'C')
{
// make sure our arguments are valid
if (!is_numeric($width) || $width == 0)
{
throw new InvalidArgumentException('$width must be numeric and greater than zero');
}
if (!is_numeric($height) || $height == 0)
{
throw new InvalidArgumentException('$height must be numeric and greater than zero');
}
// make sure we're not exceeding our image size if we're not supposed to
if ($this->options['resizeUp'] === false)
{
$this->maxHeight = (intval($height) > $this->currentDimensions['height']) ? $this->currentDimensions['height'] : $height;
$this->maxWidth = (intval($width) > $this->currentDimensions['width']) ? $this->currentDimensions['width'] : $width;
}
else
{
$this->maxHeight = intval($height);
$this->maxWidth = intval($width);
}
$this->calcImageSizeStrict($this->currentDimensions['width'], $this->currentDimensions['height']);
// resize the image to be close to our desired dimensions
$this->resize($this->newDimensions['newWidth'], $this->newDimensions['newHeight']);
// reset the max dimensions...
if ($this->options['resizeUp'] === false)
{
$this->maxHeight = (intval($height) > $this->currentDimensions['height']) ? $this->currentDimensions['height'] : $height;
$this->maxWidth = (intval($width) > $this->currentDimensions['width']) ? $this->currentDimensions['width'] : $width;
}
else
{
$this->maxHeight = intval($height);
$this->maxWidth = intval($width);
}
// create the working image
if (function_exists('imagecreatetruecolor'))
{
$this->workingImage = imagecreatetruecolor($this->maxWidth, $this->maxHeight);
}
else
{
$this->workingImage = imagecreate($this->maxWidth, $this->maxHeight);
}
$this->preserveAlpha();
$cropWidth = $this->maxWidth;
$cropHeight = $this->maxHeight;
$cropX = 0;
$cropY = 0;
// Crop the rest of the image using the quadrant
if ($this->currentDimensions['width'] > $this->maxWidth)
{
// Image is landscape
switch ($quadrant) {
case 'L':
$cropX = 0;
break;
case 'R':
$cropX = intval(($this->currentDimensions['width'] - $this->maxWidth));
break;
case 'C':
default:
$cropX = intval(($this->currentDimensions['width'] - $this->maxWidth) / 2);
break;
}
} elseif ($this->currentDimensions['height'] > $this->maxHeight)
{
// Image is portrait
switch ($quadrant) {
case 'T':
$cropY = 0;
break;
case 'B':
$cropY = intval(($this->currentDimensions['height'] - $this->maxHeight));
break;
case 'C':
default:
$cropY = intval(($this->currentDimensions['height'] - $this->maxHeight) / 2);
break;
}
}
imagecopyresampled
(
$this->workingImage,
$this->oldImage,
0,
0,
$cropX,
$cropY,
$cropWidth,
$cropHeight,
$cropWidth,
$cropHeight
);
// update all the variables and resources to be correct
$this->oldImage = $this->workingImage;
$this->currentDimensions['width'] = $this->maxWidth;
$this->currentDimensions['height'] = $this->maxHeight;
return $this;
}
/**
* Resizes an image by a given percent uniformly
*
@@ -552,6 +906,41 @@ class GdThumb extends ThumbBase
return $this;
}
/**
* Applies a filter to the image
*
* @param int $filter
* @return GdThumb
*/
public function imageFilter ($filter, $arg1 = false, $arg2 = false, $arg3 = false, $arg4 = false)
{
if (!is_numeric($filter))
{
throw new InvalidArgumentException('$filter must be numeric');
}
if (!function_exists('imagefilter'))
{
throw new RuntimeException('Your version of GD does not support image filters.');
}
$result = false;
if ( $arg1 === false ) $result = imagefilter($this->oldImage, $filter);
else if ( $arg2 === false ) $result = imagefilter($this->oldImage, $filter, $arg1);
else if ( $arg3 === false ) $result = imagefilter($this->oldImage, $filter, $arg1, $arg2);
else if ( $arg4 === false ) $result = imagefilter($this->oldImage, $filter, $arg1, $arg2, $arg3);
else $result = imagefilter($this->oldImage, $filter, $arg1, $arg2, $arg3, $arg4);
if (!$result)
{
throw new RuntimeException('GD imagefilter failed');
}
$this->workingImage = $this->oldImage;
return $this;
}
/**
* Shows an image
*
@@ -564,12 +953,18 @@ class GdThumb extends ThumbBase
*/
public function show ($rawData = false)
{
if (headers_sent())
if (headers_sent() && php_sapi_name() != 'cli')
{
throw new RuntimeException('Cannot show image, headers have already been sent');
}
switch ($this->format)
// When the interlace option equals true or false call imageinterlace else leave it to default
if ($this->options['interlace'] === true)
imageinterlace($this->oldImage, 1);
elseif ($this->options['interlace'] === false)
imageinterlace($this->oldImage, 0);
switch ($this->format)
{
case 'GIF':
if ($rawData === false)
@@ -663,7 +1058,13 @@ class GdThumb extends ThumbBase
}
}
switch ($format)
// When the interlace option equals true or false call imageinterlace else leave it to default
if ($this->options['interlace'] === true)
imageinterlace($this->oldImage, 1);
elseif ($this->options['interlace'] === false)
imageinterlace($this->oldImage, 0);
switch ($format)
{
case 'GIF':
imagegif($this->oldImage, $fileName);
@@ -713,7 +1114,8 @@ class GdThumb extends ThumbBase
'preserveAlpha' => true,
'alphaMaskColor' => array (255, 255, 255),
'preserveTransparency' => true,
'transparencyMaskColor' => array (0, 0, 0)
'transparencyMaskColor' => array (0, 0, 0),
'interlace' => null
);
}
// otherwise, let's use what we've got already

View File

@@ -1,44 +1,44 @@
<?php
/**
* PhpThumb Library Definition File
*
*
* This file contains the definitions for the PhpThumb class.
*
*
* PHP Version 5 with GD 2.0+
* PhpThumb : PHP Thumb Library <http://phpthumb.gxdlabs.com>
* Copyright (c) 2009, Ian Selby/Gen X Design
*
*
* Author(s): Ian Selby <ian@gen-x-design.com>
*
*
* Licensed under the MIT License
* Redistributions of files must retain the above copyright notice.
*
*
* @author Ian Selby <ian@gen-x-design.com>
* @copyright Copyright (c) 2009 Gen X Design
* @link http://phpthumb.gxdlabs.com
* @license http://www.opensource.org/licenses/mit-license.php The MIT License
* @version 3.0
* @package PhpThumb
* @filesource $URL$
* @filesource
*/
/**
* PhpThumb Object
*
* This singleton object is essentially a function library that helps with core validation
* and loading of the core classes and plugins. There isn't really any need to access it directly,
* unless you're developing a plugin and need to take advantage of any of the functionality contained
*
* This singleton object is essentially a function library that helps with core validation
* and loading of the core classes and plugins. There isn't really any need to access it directly,
* unless you're developing a plugin and need to take advantage of any of the functionality contained
* within.
*
* If you're not familiar with singleton patterns, here's how you get an instance of this class (since you
*
* If you're not familiar with singleton patterns, here's how you get an instance of this class (since you
* can't create one via the new keyword):
* <code>$pt = PhpThumb::getInstance();</code>
*
* It's that simple! Outside of that, there's no need to modify anything within this class, unless you're doing
*
* It's that simple! Outside of that, there's no need to modify anything within this class, unless you're doing
* some crazy customization... then knock yourself out! :)
*
*
* @package PhpThumb
* @subpackage Core
*/
@@ -46,36 +46,36 @@ class PhpThumb
{
/**
* Instance of self
*
*
* @var object PhpThumb
*/
protected static $_instance;
/**
* The plugin registry
*
* This is where all plugins to be loaded are stored. Data about the plugin is
*
* This is where all plugins to be loaded are stored. Data about the plugin is
* provided, and currently consists of:
* - loaded: true/false
* - implementation: gd/imagick/both
*
*
* @var array
*/
protected $_registry;
/**
* What implementations are available
*
* This stores what implementations are available based on the loaded
*
* This stores what implementations are available based on the loaded
* extensions in PHP, NOT whether or not the class files are present.
*
*
* @var array
*/
protected $_implementations;
/**
* Returns an instance of self
*
*
* This is the usual singleton function that returns / instantiates the object
*
*
* @return PhpThumb
*/
public static function getInstance ()
@@ -87,29 +87,29 @@ class PhpThumb
return self::$_instance;
}
/**
* Class constructor
*
*
* Initializes all the variables, and does some preliminary validation / checking of stuff
*
*
*/
private function __construct ()
{
$this->_registry = array();
$this->_implementations = array('gd' => false, 'imagick' => false);
$this->getImplementations();
}
/**
* Finds out what implementations are available
*
*
* This function loops over $this->_implementations and validates that the required extensions are loaded.
*
* I had planned on attempting to load them dynamically via dl(), but that would provide more overhead than I
*
* I had planned on attempting to load them dynamically via dl(), but that would provide more overhead than I
* was comfortable with (and would probably fail 99% of the time anyway)
*
*
*/
private function getImplementations ()
{
@@ -119,22 +119,22 @@ class PhpThumb
{
continue;
}
if(extension_loaded($extension))
{
$this->_implementations[$extension] = true;
}
}
}
/**
* Returns whether or not $implementation is valid (available)
*
*
* If 'all' is passed, true is only returned if ALL implementations are available.
*
*
* You can also pass 'n/a', which always returns true
*
* @return bool
*
* @return bool
* @param string $implementation
*/
public function isValidImplementation ($implementation)
@@ -143,7 +143,7 @@ class PhpThumb
{
return true;
}
if ($implementation == 'all')
{
foreach ($this->_implementations as $imp => $value)
@@ -153,32 +153,32 @@ class PhpThumb
return false;
}
}
return true;
}
if (array_key_exists($implementation, $this->_implementations))
{
return $this->_implementations[$implementation];
}
return false;
}
/**
* Registers a plugin in the registry
*
* Adds a plugin to the registry if it isn't already loaded, and if the provided
* implementation is valid. Note that you can pass the following special keywords
*
* Adds a plugin to the registry if it isn't already loaded, and if the provided
* implementation is valid. Note that you can pass the following special keywords
* for implementation:
* - all - Requires that all implementations be available
* - n/a - Doesn't require any implementation
*
* When a plugin is added to the registry, it's added as a key on $this->_registry with the value
*
* When a plugin is added to the registry, it's added as a key on $this->_registry with the value
* being an array containing the following keys:
* - loaded - whether or not the plugin has been "loaded" into the core class
* - implementation - what implementation this plugin is valid for
*
*
* @return bool
* @param string $pluginName
* @param string $implementation
@@ -190,16 +190,16 @@ class PhpThumb
$this->_registry[$pluginName] = array('loaded' => false, 'implementation' => $implementation);
return true;
}
return false;
}
/**
* Loads all the plugins in $pluginPath
*
* All this function does is include all files inside the $pluginPath directory. The plugins themselves
*
* All this function does is include all files inside the $pluginPath directory. The plugins themselves
* will not be added to the registry unless you've properly added the code to do so inside your plugin file.
*
*
* @param string $pluginPath
*/
public function loadPlugins ($pluginPath)
@@ -209,7 +209,7 @@ class PhpThumb
{
$pluginPath = substr($pluginPath, 0, strlen($pluginPath) - 1);
}
if ($handle = opendir($pluginPath))
{
while (false !== ($file = readdir($handle)))
@@ -218,22 +218,22 @@ class PhpThumb
{
continue;
}
include_once($pluginPath . '/' . $file);
}
}
}
/**
* Returns the plugin registry for the supplied implementation
*
*
* @return array
* @param string $implementation
*/
public function getPluginRegistry ($implementation)
{
$returnArray = array();
foreach ($this->_registry as $plugin => $meta)
{
if ($meta['implementation'] == 'n/a' || $meta['implementation'] == $implementation)
@@ -241,7 +241,7 @@ class PhpThumb
$returnArray[$plugin] = $meta;
}
}
return $returnArray;
}
}

View File

@@ -161,7 +161,7 @@ abstract class ThumbBase
return;
}
if (stristr($this->fileName, 'http://') !== false)
if (preg_match('/https?:\/\//', $this->fileName) !== 0)
{
$this->remoteImage = true;
return;
@@ -208,7 +208,7 @@ abstract class ThumbBase
{
if( array_key_exists($method, $this->importedFunctions))
{
$args[] = $this;
$args[] =& $this;
return call_user_func_array(array($this->importedFunctions[$method], $method), $args);
}

View File

@@ -1,30 +1,30 @@
<?php
/**
* PhpThumb Library Definition File
*
*
* This file contains the definitions for the PhpThumbFactory class.
* It also includes the other required base class files.
*
*
* If you've got some auto-loading magic going on elsewhere in your code, feel free to
* remove the include_once statements at the beginning of this file... just make sure that
* these files get included one way or another in your code.
*
*
* PHP Version 5 with GD 2.0+
* PhpThumb : PHP Thumb Library <http://phpthumb.gxdlabs.com>
* Copyright (c) 2009, Ian Selby/Gen X Design
*
*
* Author(s): Ian Selby <ian@gen-x-design.com>
*
*
* Licensed under the MIT License
* Redistributions of files must retain the above copyright notice.
*
*
* @author Ian Selby <ian@gen-x-design.com>
* @copyright Copyright (c) 2009 Gen X Design
* @link http://phpthumb.gxdlabs.com
* @license http://www.opensource.org/licenses/mit-license.php The MIT License
* @version 3.0
* @package PhpThumb
* @filesource $URL$
* @filesource
*/
// define some useful constants
@@ -47,19 +47,19 @@ require_once THUMBLIB_BASE_PATH . '/GdThumb.inc.php';
/**
* PhpThumbFactory Object
*
*
* This class is responsible for making sure everything is set up and initialized properly,
* and returning the appropriate thumbnail class instance. It is the only recommended way
* and returning the appropriate thumbnail class instance. It is the only recommended way
* of using this library, and if you try and circumvent it, the sky will fall on your head :)
*
*
* Basic use is easy enough. First, make sure all the settings meet your needs and environment...
* these are the static variables defined at the beginning of the class.
*
*
* Once that's all set, usage is pretty easy. You can simply do something like:
* <code>$thumb = PhpThumbFactory::create('/path/to/file.png');</code>
*
*
* Refer to the documentation for the create function for more information
*
*
* @package PhpThumb
* @subpackage Core
*/
@@ -67,30 +67,30 @@ class PhpThumbFactory
{
/**
* Which implemenation of the class should be used by default
*
*
* Currently, valid options are:
* - imagick
* - gd
*
*
* These are defined in the implementation map variable, inside the create function
*
*
* @var string
*/
public static $defaultImplemenation = DEFAULT_THUMBLIB_IMPLEMENTATION;
/**
* Where the plugins can be loaded from
*
* Note, it's important that this path is properly defined. It is very likely that you'll
*
* Note, it's important that this path is properly defined. It is very likely that you'll
* have to change this, as the assumption here is based on a relative path.
*
*
* @var string
*/
public static $pluginPath = THUMBLIB_PLUGIN_PATH;
/**
* Factory Function
*
* This function returns the correct thumbnail object, augmented with any appropriate plugins.
*
* This function returns the correct thumbnail object, augmented with any appropriate plugins.
* It does so by doing the following:
* - Getting an instance of PhpThumb
* - Loading plugins
@@ -98,7 +98,7 @@ class PhpThumbFactory
* - Returning the desired default implementation if possible
* - Returning the GD implemenation if the default isn't available
* - Throwing an exception if no required libraries are present
*
*
* @return GdThumb
* @uses PhpThumb
* @param string $filename The path and file to load [optional]
@@ -111,15 +111,15 @@ class PhpThumbFactory
'imagick' => 'ImagickThumb',
'gd' => 'GdThumb'
);
// grab an instance of PhpThumb
$pt = PhpThumb::getInstance();
// load the plugins
$pt->loadPlugins(self::$pluginPath);
$toReturn = null;
$implementation = self::$defaultImplemenation;
// attempt to load the default implementation
if ($pt->isValidImplementation(self::$defaultImplemenation))
{
@@ -138,7 +138,7 @@ class PhpThumbFactory
{
throw new Exception('You must have either the GD or iMagick extension loaded to use this library');
}
$registry = $pt->getPluginRegistry($implementation);
$toReturn->importPlugins($registry);
return $toReturn;

View File

@@ -46,12 +46,16 @@ class GdReflectionLib
public function createReflection ($percent, $reflection, $white, $border, $borderColor, &$that)
{
// bring stuff from the parent class into this class...
$this->parentInstance = $that;
$this->currentDimensions = $this->parentInstance->getCurrentDimensions();
$this->workingImage = $this->parentInstance->getWorkingImage();
$this->newImage = $this->parentInstance->getOldImage();
$this->options = $this->parentInstance->getOptions();
$width = $this->currentDimensions['width'];
$height = $this->currentDimensions['height'];
$reflectionHeight = intval($height * ($reflection / 100));