739 lines
24 KiB
PHP
739 lines
24 KiB
PHP
<?php
|
|
|
|
namespace BRSystems\Core\Library\cImage;
|
|
|
|
use Exception;
|
|
|
|
/**
|
|
* Image manipulation support. Allows images to be resized, cropped, etc.
|
|
*
|
|
* @package \Utils\Image
|
|
* @copyright Copyright (c) 2011, Edgars Kohs
|
|
* @license http://www.gnu.org/licenses/gpl.html, GPL
|
|
* @author Edgars Kohs <edgars@kohs.lv>
|
|
* @version 2.0 2011-07-15
|
|
*
|
|
* $image = new cImage('source/image.jpg')->resize(100,100)->save('target/image.jpg');
|
|
* $image = new cImage('source/image.jpg')->resize(100)->crop(80)->save('target/image.jpg');
|
|
* $image = new cImage('source/image.jpg')->resize(null,50)->rotate(40)->save('target/image.jpg');
|
|
* $image = new cImage('source/image.png')->background('#f00f00',90)->save('target/image.png');
|
|
*/
|
|
class cImage
|
|
{
|
|
/**
|
|
* @var string image file path
|
|
*/
|
|
public $file;
|
|
|
|
/**
|
|
* @var integer image width
|
|
*/
|
|
public $width;
|
|
|
|
/**
|
|
* @var integer image height
|
|
*/
|
|
public $height;
|
|
|
|
/**
|
|
* @var integer one of the IMAGETYPE_* constants
|
|
*/
|
|
public $type;
|
|
|
|
/**
|
|
* @var string mime type of the image
|
|
*/
|
|
public $mime;
|
|
|
|
private $_image; // Temporary image resource
|
|
private $_create_function; // Function name to open Image
|
|
|
|
private static $_bundled = false; // Is GD bundled or separate?
|
|
private static $_checked = false; // Status of the driver check
|
|
|
|
// Resizing contraints
|
|
const NONE = 0x01;
|
|
const WIDTH = 0x02;
|
|
const HEIGHT = 0x03;
|
|
const AUTO = 0x04;
|
|
const INVERSE = 0x05;
|
|
|
|
// Flipping directions
|
|
const HORIZONTAL = 0x11;
|
|
const VERTICAL = 0x12;
|
|
|
|
/**
|
|
* Loads an image and prepares it for manipulation.
|
|
*
|
|
* @access public static
|
|
* @param string image file path
|
|
* @return cImage
|
|
*/
|
|
public static function factory($file){
|
|
return new self($file);
|
|
}
|
|
|
|
/**
|
|
* Loads information about the image. Will throw an exception if the image
|
|
* does not exist or is not an image.
|
|
*
|
|
* @access public
|
|
* @param string image file path
|
|
* @return void
|
|
* @throws Exception
|
|
*/
|
|
public function __construct($file){
|
|
try{
|
|
$file = realpath($file);
|
|
$info = getimagesize($file);
|
|
}catch(Exception $e){
|
|
throw new Exception('Not an image or invalid image: '.$file);
|
|
}
|
|
$this->file = $file;
|
|
$this->width = $info[0];
|
|
$this->height = $info[1];
|
|
$this->type = $info[2];
|
|
$this->mime = image_type_to_mime_type($this->type);
|
|
switch ($this->type){
|
|
case IMAGETYPE_JPEG:
|
|
$create = 'imagecreatefromjpeg';
|
|
break;
|
|
case IMAGETYPE_GIF:
|
|
$create = 'imagecreatefromgif';
|
|
break;
|
|
case IMAGETYPE_PNG:
|
|
$create = 'imagecreatefrompng';
|
|
break;
|
|
}
|
|
if(!isset($create) || !function_exists($create)) throw new Exception('Installed GD does not support '.image_type_to_extension($this->type, false).' images');
|
|
$this->_create_function = $create;
|
|
$this->_image = $this->file;
|
|
|
|
// Call check for class init
|
|
$this->check();
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Destroys the loaded image to free up resources.
|
|
*
|
|
* @access public
|
|
* @return void
|
|
*/
|
|
public function __destruct(){
|
|
if(is_resource($this->_image)) imagedestroy($this->_image);
|
|
}
|
|
|
|
/**
|
|
* Render the current image.
|
|
*
|
|
* @access public
|
|
* @return string
|
|
*/
|
|
public function __toString(){
|
|
return $this->render();
|
|
}
|
|
|
|
/**
|
|
* Resize the image to the given size. Either the width or the height can
|
|
* be omitted and the image will be resized proportionally.
|
|
*
|
|
* @access public
|
|
* @param integer new width
|
|
* @param integer new height
|
|
* @param integer master dimension
|
|
* @return $this
|
|
* @uses cImage::_do_resize
|
|
*/
|
|
public function resize($width = null, $height = null, $master = null){
|
|
if(is_null($master)) $master = self::AUTO;
|
|
if(empty($width)){
|
|
if($master === self::NONE){
|
|
$width = $this->width;
|
|
}else{
|
|
$master = self::HEIGHT;
|
|
}
|
|
}
|
|
if(empty($height)){
|
|
if($master === self::NONE){
|
|
$height = $this->height;
|
|
}else{
|
|
$master = self::WIDTH;
|
|
}
|
|
}
|
|
switch($master){
|
|
case self::AUTO:
|
|
$master = ($this->width/$width) > ($this->height/$height) ? self::WIDTH : self::HEIGHT;
|
|
break;
|
|
case self::INVERSE:
|
|
$master = ($this->width/$width) > ($this->height/$height) ? self::HEIGHT : self::WIDTH;
|
|
break;
|
|
}
|
|
switch($master){
|
|
case self::WIDTH:
|
|
$height = $this->height*$width/$this->width;
|
|
break;
|
|
case self::HEIGHT:
|
|
$width = $this->width*$height/$this->height;
|
|
break;
|
|
}
|
|
$width = max(round($width), 1);
|
|
$height = max(round($height), 1);
|
|
$this->_do_resize($width, $height);
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Crop an image to the given size. Either the width or the height can be
|
|
* omitted and the current width or height will be used.
|
|
*
|
|
* @access public
|
|
* @param integer new width
|
|
* @param integer new height
|
|
* @param mixed offset from the left
|
|
* @param mixed offset from the top
|
|
* @return $this
|
|
* @uses cImage::_do_crop
|
|
*/
|
|
public function crop($width, $height, $offset_x = null, $offset_y = null){
|
|
if($width > $this->width) $width = $this->width;
|
|
if($height > $this->height) $height = $this->height;
|
|
if(is_null($offset_x)){
|
|
$offset_x = round(($this->width-$width)/2);
|
|
}elseif(is_null($offset_x)){
|
|
$offset_x = $this->width-$width;
|
|
}elseif($offset_x < 0){
|
|
$offset_x = $this->width-$width+$offset_x;
|
|
}
|
|
if(is_null($offset_y)){
|
|
$offset_y = round(($this->height-$height)/2);
|
|
}elseif(is_null($offset_y)){
|
|
$offset_y = $this->height-$height;
|
|
}elseif($offset_y < 0){
|
|
$offset_y = $this->height-$height+$offset_y;
|
|
}
|
|
$max_width = $this->width-$offset_x;
|
|
$max_height = $this->height-$offset_y;
|
|
if($width > $max_width) $width = $max_width;
|
|
if($height > $max_height) $height = $max_height;
|
|
$this->_do_crop($width, $height, $offset_x, $offset_y);
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Rotate the image by a given amount.
|
|
*
|
|
* @access public
|
|
* @param integer degrees to rotate: -360-360
|
|
* @return $this
|
|
* @uses cImage::_do_rotate
|
|
*/
|
|
public function rotate($degrees){
|
|
$degrees = (int)$degrees;
|
|
if($degrees > 180){
|
|
do{
|
|
$degrees-= 360;
|
|
}while($degrees > 180);
|
|
}
|
|
if($degrees < -180){
|
|
do{
|
|
$degrees+= 360;
|
|
}while($degrees < -180);
|
|
}
|
|
$this->_do_rotate($degrees);
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Flip the image along the horizontal or vertical axis.
|
|
*
|
|
* @access public
|
|
* @param integer direction: cImage::HORIZONTAL, cImage::VERTICAL
|
|
* @return $this
|
|
* @uses cImage::_do_flip
|
|
*/
|
|
public function flip($direction){
|
|
if($direction !== self::HORIZONTAL) $direction = self::VERTICAL;
|
|
$this->_do_flip($direction);
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Sharpen the image by a given amount.
|
|
*
|
|
* @access public
|
|
* @param integer amount to sharpen: 1-100
|
|
* @return $this
|
|
* @uses cImage::_do_sharpen
|
|
*/
|
|
public function sharpen($amount){
|
|
$amount = min(max($amount, 1), 100);
|
|
$this->_do_sharpen($amount);
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Add a reflection to an image. The most opaque part of the reflection
|
|
* will be equal to the opacity setting and fade out to full transparent.
|
|
*
|
|
* @access public
|
|
* @param integer reflection height
|
|
* @param integer reflection opacity: 0-100
|
|
* @param boolean TRUE to fade in, FALSE to fade out
|
|
* @return $this
|
|
* @uses cImage::_do_reflection
|
|
*/
|
|
public function reflection($height = null, $opacity = 100, $fade_in = false){
|
|
if(is_null($height) || $height > $this->height) $height = $this->height;
|
|
$opacity = min(max($opacity, 0), 100);
|
|
$this->_do_reflection($height, $opacity, $fade_in);
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Add a watermark to an image with a specified opacity. Alpha transparency
|
|
* will be preserved.
|
|
*
|
|
* @access public
|
|
* @param cImage watermark cImage instance
|
|
* @param integer offset from the left
|
|
* @param integer offset from the top
|
|
* @param integer opacity of watermark: 1-100
|
|
* @return $this
|
|
* @uses cImage::_do_watermark
|
|
*/
|
|
public function watermark(cImage $watermark, $offset_x = null, $offset_y = null, $opacity = 100){
|
|
|
|
if(is_null($offset_x)){
|
|
$offset_x = round(($this->width-$watermark->width)/2);
|
|
}elseif(is_null($offset_x)){
|
|
$offset_x = $this->width-$watermark->width;
|
|
}elseif($offset_x < 0){
|
|
$offset_x = $this->width-$watermark->width+$offset_x;
|
|
}
|
|
if(is_null($offset_y)){
|
|
$offset_y = round(($this->height-$watermark->height)/2);
|
|
}elseif(is_null($offset_y)){
|
|
$offset_y = $this->height-$watermark->height;
|
|
}elseif($offset_y < 0){
|
|
$offset_y = $this->height-$watermark->height+$offset_y;
|
|
}
|
|
$opacity = min(max($opacity, 1), 100);
|
|
$this->_do_watermark($watermark, $offset_x, $offset_y, $opacity);
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Set the background color of an image. This is only useful for images
|
|
* with alpha transparency.
|
|
*
|
|
* @access public
|
|
* @param string hexadecimal color value
|
|
* @param integer background opacity: 0-100
|
|
* @return $this
|
|
* @uses cImage::_do_background
|
|
*/
|
|
public function background($color, $opacity = 100){
|
|
if($color[0] === '#') $color = substr($color, 1);
|
|
if(strlen($color) === 3) $color = preg_replace('/./', '$0$0', $color);
|
|
list($r, $g, $b) = array_map('hexdec', str_split($color, 2));
|
|
$opacity = min(max($opacity, 0), 100);
|
|
$this->_do_background($r, $g, $b, $opacity);
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Save the image. If the filename is omitted, the original image will
|
|
* be overwritten.
|
|
*
|
|
* @access public
|
|
* @param string new image path
|
|
* @param integer quality of image: 1-100
|
|
* @return boolean
|
|
* @uses cImage::_save
|
|
* @throws Exception
|
|
*/
|
|
public function save($file = null, $quality = 100){
|
|
if(is_null($file)) $file = $this->file;
|
|
if(is_file($file)){
|
|
if(!is_writable($file)) throw new Exception('File must be writable: '.$file);
|
|
}else{
|
|
$directory = realpath(pathinfo($file, PATHINFO_DIRNAME));
|
|
if(!is_dir($directory) || !is_writable($directory)) throw new Exception('Directory must be writable: '.$directory);
|
|
}
|
|
$quality = min(max($quality, 1), 100);
|
|
return $this->_do_save($file, $quality);
|
|
}
|
|
|
|
/**
|
|
* Render the image and return the binary string.
|
|
*
|
|
* @access public
|
|
* @param string image type to return: png, jpg, gif, etc
|
|
* @param integer quality of image: 1-100
|
|
* @return string
|
|
* @uses cImage::_do_render
|
|
*/
|
|
public function render($type = null, $quality = 100){
|
|
if(is_null($type)) $type = image_type_to_extension($this->type, false);
|
|
return $this->_do_render($type, $quality);
|
|
}
|
|
|
|
/**
|
|
* Checks if GD is enabled and bundled. Bundled GD is required for some
|
|
* methods to work. Exceptions will be thrown from those methods when GD is
|
|
* not bundled.
|
|
*
|
|
* @access private
|
|
* @return boolean
|
|
*/
|
|
public static function check(){
|
|
if(!function_exists('gd_info')) throw new Exception('GD is either not installed or not enabled, check your configuration');
|
|
if(defined('GD_BUNDLED')){
|
|
self::$_bundled = GD_BUNDLED;
|
|
}else{
|
|
$info = gd_info();
|
|
self::$_bundled = (bool)preg_match('/\bbundled\b/i', $info['GD Version']);
|
|
}
|
|
if(defined('GD_VERSION')){
|
|
$version = GD_VERSION;
|
|
}else{
|
|
$info = gd_info();
|
|
preg_match('/\d+\.\d+(?:\.\d+)?/', $info['GD Version'], $matches);
|
|
$version = $matches[0];
|
|
}
|
|
if(!version_compare($version, '2.0.1', '>=')) throw new Exception('Image requires GD version 2.0.1 or greater, you have '.$version);
|
|
return self::$_checked = true;
|
|
}
|
|
|
|
/**
|
|
* Loads an image into GD.
|
|
*
|
|
* @access private
|
|
* @return void
|
|
*/
|
|
private function _load_image(){
|
|
if(!is_resource($this->_image)){
|
|
$this->_image = call_user_func($this->_create_function, $this->file);
|
|
imagesavealpha($this->_image, true);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Execute a resize.
|
|
*
|
|
* @access private
|
|
* @param integer new width
|
|
* @param integer new height
|
|
* @return void
|
|
*/
|
|
private function _do_resize($width, $height){
|
|
$pre_width = $this->width;
|
|
$pre_height = $this->height;
|
|
$this->_load_image();
|
|
if($width > ($this->width/2) && $height > ($this->height/2)){
|
|
$reduction_width = round($width*1.1);
|
|
$reduction_height = round($height*1.1);
|
|
while($pre_width/2 > $reduction_width && $pre_height/2 > $reduction_height){
|
|
$pre_width/= 2;
|
|
$pre_height/= 2;
|
|
}
|
|
$image = $this->_create($pre_width, $pre_height);
|
|
if(imagecopyresized($image, $this->_image, 0, 0, 0, 0, $pre_width, $pre_height, $this->width, $this->height)){
|
|
imagedestroy($this->_image);
|
|
$this->_image = $image;
|
|
}
|
|
}
|
|
$image = $this->_create($width, $height);
|
|
if(imagecopyresampled($image, $this->_image, 0, 0, 0, 0, $width, $height, $pre_width, $pre_height)){
|
|
imagedestroy($this->_image);
|
|
$this->_image = $image;
|
|
$this->width = imagesx($image);
|
|
$this->height = imagesy($image);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Execute a crop.
|
|
*
|
|
* @access private
|
|
* @param integer new width
|
|
* @param integer new height
|
|
* @param integer offset from the left
|
|
* @param integer offset from the top
|
|
* @return void
|
|
*/
|
|
private function _do_crop($width, $height, $offset_x, $offset_y){
|
|
$image = $this->_create($width, $height);
|
|
$this->_load_image();
|
|
if(imagecopyresampled($image, $this->_image, 0, 0, $offset_x, $offset_y, $width, $height, $width, $height)){
|
|
imagedestroy($this->_image);
|
|
$this->_image = $image;
|
|
$this->width = imagesx($image);
|
|
$this->height = imagesy($image);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Execute a rotation.
|
|
*
|
|
* @access private
|
|
* @param integer degrees to rotate
|
|
* @return void
|
|
*/
|
|
private function _do_rotate($degrees){
|
|
if(!self::$_bundled) throw new Exception('This method requires imagerotate, which is only available in the bundled version of GD');
|
|
$this->_load_image();
|
|
$transparent = imagecolorallocatealpha($this->_image, 0, 0, 0, 127);
|
|
$image = imagerotate($this->_image, 360-$degrees, $transparent, 1);
|
|
imagesavealpha($image, true);
|
|
$width = imagesx($image);
|
|
$height = imagesy($image);
|
|
if(imagecopymerge($this->_image, $image, 0, 0, 0, 0, $width, $height, 100)){
|
|
imagedestroy($this->_image);
|
|
$this->_image = $image;
|
|
$this->width = $width;
|
|
$this->height = $height;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Execute a flip.
|
|
*
|
|
* @access private
|
|
* @param integer direction to flip
|
|
* @return void
|
|
*/
|
|
private function _do_flip($direction){
|
|
$flipped = $this->_create($this->width, $this->height);
|
|
$this->_load_image();
|
|
if($direction === self::HORIZONTAL){
|
|
for($x = 0; $x < $this->width; $x++) imagecopy($flipped, $this->_image, $x, 0, $this->width-$x-1, 0, 1, $this->height);
|
|
}else{
|
|
for($y = 0; $y < $this->height; $y++) imagecopy($flipped, $this->_image, 0, $y, 0, $this->height-$y-1, $this->width, 1);
|
|
}
|
|
imagedestroy($this->_image);
|
|
$this->_image = $flipped;
|
|
$this->width = imagesx($flipped);
|
|
$this->height = imagesy($flipped);
|
|
}
|
|
|
|
/**
|
|
* Execute a sharpen.
|
|
*
|
|
* @access private
|
|
* @param integer amount to sharpen
|
|
* @return void
|
|
*/
|
|
private function _do_sharpen($amount){
|
|
if(!self::$_bundled) throw new Exception('This method requires imageconvolution, which is only available in the bundled version of GD');
|
|
$this->_load_image();
|
|
$amount = round(abs(-18+($amount*0.08)), 2);
|
|
$matrix = array(array(-1, -1, -1), array(-1, $amount, -1), array(-1, -1, -1),);
|
|
if(imageconvolution($this->_image, $matrix, $amount-8, 0)){
|
|
$this->width = imagesx($this->_image);
|
|
$this->height = imagesy($this->_image);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Execute a reflection.
|
|
*
|
|
* @access private
|
|
* @param integer reflection height
|
|
* @param integer reflection opacity
|
|
* @param boolean TRUE to fade out, FALSE to fade in
|
|
* @return void
|
|
*/
|
|
private function _do_reflection($height, $opacity, $fade_in){
|
|
if(!self::$_bundled) throw new Exception('This method requires imagefilter, which is only available in the bundled version of GD');
|
|
$this->_load_image();
|
|
$opacity = round(abs(($opacity*127/100)-127));
|
|
if($opacity < 127){
|
|
$stepping = (127-$opacity)/$height;
|
|
}else{
|
|
$stepping = 127/$height;
|
|
}
|
|
$reflection = $this->_create($this->width, $this->height+$height);
|
|
imagecopy($reflection, $this->_image, 0, 0, 0, 0, $this->width, $this->height);
|
|
for($offset = 0; $height >= $offset; $offset++){
|
|
$src_y = $this->height-$offset-1;
|
|
$dst_y = $this->height+$offset;
|
|
$dst_opacity = round($fade_in ? $opacity+($stepping*($height-$offset)) : $opacity + ($stepping * $offset));
|
|
$line = $this->_create($this->width, 1);
|
|
imagecopy($line, $this->_image, 0, 0, 0, $src_y, $this->width, 1);
|
|
imagefilter($line, IMG_FILTER_COLORIZE, 0, 0, 0, $dst_opacity);
|
|
imagecopy($reflection, $line, 0, $dst_y, 0, 0, $this->width, 1);
|
|
}
|
|
imagedestroy($this->_image);
|
|
$this->_image = $reflection;
|
|
$this->width = imagesx($reflection);
|
|
$this->height = imagesy($reflection);
|
|
}
|
|
|
|
/**
|
|
* Execute a watermarking.
|
|
*
|
|
* @access private
|
|
* @param object watermarking Image
|
|
* @param integer offset from the left
|
|
* @param integer offset from the top
|
|
* @param integer opacity of watermark
|
|
* @return void
|
|
*/
|
|
private function _do_watermark(cImage $watermark, $offset_x, $offset_y, $opacity){
|
|
|
|
/** @noinspection PhpUndefinedClassInspection */
|
|
//if(!cImage::$_bundled) throw new Exception('This method requires imagelayereffect, which is only available in the bundled version of GD');
|
|
|
|
// Loads image if not yet loaded
|
|
$this->_load_image();
|
|
|
|
// Create the watermark image resource
|
|
$overlay = imagecreatefromstring($watermark->render());
|
|
|
|
imagesavealpha($overlay, TRUE);
|
|
|
|
// Get the width and height of the watermark
|
|
$width = imagesx($overlay);
|
|
$height = imagesy($overlay);
|
|
|
|
if ($opacity < 100)
|
|
{
|
|
// Convert an opacity range of 0-100 to 127-0
|
|
$opacity = round(abs(($opacity * 127 / 100) - 127));
|
|
|
|
// Allocate transparent gray
|
|
$color = imagecolorallocatealpha($overlay, 127, 127, 127, $opacity);
|
|
|
|
// The transparent image will overlay the watermark
|
|
imagelayereffect($overlay, IMG_EFFECT_OVERLAY);
|
|
|
|
// Fill the background with the transparent color
|
|
imagefilledrectangle($overlay, 0, 0, $width, $height, $color);
|
|
}
|
|
|
|
// Alpha blending must be enabled on the background!
|
|
imagealphablending($this->_image, TRUE);
|
|
|
|
if (imagecopy($this->_image, $overlay, $offset_x, $offset_y, 0, 0, $width, $height))
|
|
{
|
|
// Destroy the overlay image
|
|
imagedestroy($overlay);
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* Execute a background.
|
|
*
|
|
* @access private
|
|
* @param integer red
|
|
* @param integer green
|
|
* @param integer blue
|
|
* @param integer opacity
|
|
* @return void
|
|
*/
|
|
private function _do_background($r, $g, $b, $opacity){
|
|
$this->_load_image();
|
|
$opacity = round(abs(($opacity*127/100)-127));
|
|
$background = $this->_create($this->width, $this->height);
|
|
$color = imagecolorallocatealpha($background, $r, $g, $b, $opacity);
|
|
imagefilledrectangle($background, 0, 0, $this->width, $this->height, $color);
|
|
imagealphablending($background, true);
|
|
if(imagecopy($background, $this->_image, 0, 0, 0, 0, $this->width, $this->height)){
|
|
imagedestroy($this->_image);
|
|
$this->_image = $background;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Execute a save.
|
|
*
|
|
* @access private
|
|
* @param string new image filename
|
|
* @param integer quality
|
|
* @return boolean
|
|
*/
|
|
private function _do_save($file, $quality){
|
|
$this->_load_image();
|
|
$extension = pathinfo($file, PATHINFO_EXTENSION);
|
|
list($save, $type) = $this->_save_function($extension, $quality);
|
|
$status = isset($quality) ? $save($this->_image, $file, $quality) : $save($this->_image, $file);
|
|
if($status && $type !== $this->type){
|
|
$this->type = $type;
|
|
$this->mime = image_type_to_mime_type($type);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Execute a render.
|
|
*
|
|
* @access private
|
|
* @param string image type: png, jpg, gif, etc
|
|
* @param integer quality
|
|
* @return string
|
|
*/
|
|
private function _do_render($type, $quality){
|
|
$this->_load_image();
|
|
list($save, $type) = $this->_save_function($type, $quality);
|
|
ob_start();
|
|
$status = isset($quality) ? $save($this->_image, null, $quality) : $save($this->_image, null);
|
|
if($status && $type !== $this->type){
|
|
$this->type = $type;
|
|
$this->mime = image_type_to_mime_type($type);
|
|
}
|
|
return ob_get_clean();
|
|
}
|
|
|
|
/**
|
|
* Get the GD saving function and image type for this extension.
|
|
* Also normalizes the quality setting
|
|
*
|
|
* @access private
|
|
* @param string image type: png, jpg, etc
|
|
* @param integer image quality
|
|
* @return array save function, IMAGETYPE_* constant
|
|
* @throws Exception
|
|
*/
|
|
private function _save_function($extension, &$quality){
|
|
switch(strtolower($extension)){
|
|
case 'jpg':
|
|
case 'jpeg':
|
|
$save = 'imagejpeg';
|
|
$type = IMAGETYPE_JPEG;
|
|
break;
|
|
case 'gif':
|
|
$save = 'imagegif';
|
|
$type = IMAGETYPE_GIF;
|
|
$quality = null;
|
|
break;
|
|
case 'png':
|
|
$save = 'imagepng';
|
|
$type = IMAGETYPE_PNG;
|
|
$quality = 9;
|
|
break;
|
|
default:
|
|
throw new Exception('Installed GD does not support '.$extension.' images');
|
|
break;
|
|
}
|
|
return array($save, $type);
|
|
}
|
|
|
|
/**
|
|
* Create an empty image with the given width and height.
|
|
*
|
|
* @access private
|
|
* @param integer image width
|
|
* @param integer image height
|
|
* @return resource
|
|
*/
|
|
private function _create($width, $height){
|
|
$image = imagecreatetruecolor($width, $height);
|
|
imagealphablending($image, false);
|
|
imagesavealpha($image, true);
|
|
return $image;
|
|
}
|
|
}
|