Imported Upstream version 0.6.24+dfsg1
This commit is contained in:
379
lib/kohana/system/libraries/drivers/Image/GD.php
Normal file
379
lib/kohana/system/libraries/drivers/Image/GD.php
Normal file
@@ -0,0 +1,379 @@
|
||||
<?php defined('SYSPATH') OR die('No direct access allowed.');
|
||||
/**
|
||||
* GD Image Driver.
|
||||
*
|
||||
* $Id: GD.php 3769 2008-12-15 00:48:56Z zombor $
|
||||
*
|
||||
* @package Image
|
||||
* @author Kohana Team
|
||||
* @copyright (c) 2007-2008 Kohana Team
|
||||
* @license http://kohanaphp.com/license.html
|
||||
*/
|
||||
class Image_GD_Driver extends Image_Driver {
|
||||
|
||||
// A transparent PNG as a string
|
||||
protected static $blank_png;
|
||||
protected static $blank_png_width;
|
||||
protected static $blank_png_height;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
// Make sure that GD2 is available
|
||||
if ( ! function_exists('gd_info'))
|
||||
throw new Kohana_Exception('image.gd.requires_v2');
|
||||
|
||||
// Get the GD information
|
||||
$info = gd_info();
|
||||
|
||||
// Make sure that the GD2 is installed
|
||||
if (strpos($info['GD Version'], '2.') === FALSE)
|
||||
throw new Kohana_Exception('image.gd.requires_v2');
|
||||
}
|
||||
|
||||
public function process($image, $actions, $dir, $file, $render = FALSE)
|
||||
{
|
||||
// Set the "create" function
|
||||
switch ($image['type'])
|
||||
{
|
||||
case IMAGETYPE_JPEG:
|
||||
$create = 'imagecreatefromjpeg';
|
||||
break;
|
||||
case IMAGETYPE_GIF:
|
||||
$create = 'imagecreatefromgif';
|
||||
break;
|
||||
case IMAGETYPE_PNG:
|
||||
$create = 'imagecreatefrompng';
|
||||
break;
|
||||
}
|
||||
|
||||
// Set the "save" function
|
||||
switch (strtolower(substr(strrchr($file, '.'), 1)))
|
||||
{
|
||||
case 'jpg':
|
||||
case 'jpeg':
|
||||
$save = 'imagejpeg';
|
||||
break;
|
||||
case 'gif':
|
||||
$save = 'imagegif';
|
||||
break;
|
||||
case 'png':
|
||||
$save = 'imagepng';
|
||||
break;
|
||||
}
|
||||
|
||||
// Make sure the image type is supported for import
|
||||
if (empty($create) OR ! function_exists($create))
|
||||
throw new Kohana_Exception('image.type_not_allowed', $image['file']);
|
||||
|
||||
// Make sure the image type is supported for saving
|
||||
if (empty($save) OR ! function_exists($save))
|
||||
throw new Kohana_Exception('image.type_not_allowed', $dir.$file);
|
||||
|
||||
// Load the image
|
||||
$this->image = $image;
|
||||
|
||||
// Create the GD image resource
|
||||
$this->tmp_image = $create($image['file']);
|
||||
|
||||
// Get the quality setting from the actions
|
||||
$quality = arr::remove('quality', $actions);
|
||||
|
||||
if ($status = $this->execute($actions))
|
||||
{
|
||||
// Prevent the alpha from being lost
|
||||
imagealphablending($this->tmp_image, TRUE);
|
||||
imagesavealpha($this->tmp_image, TRUE);
|
||||
|
||||
switch ($save)
|
||||
{
|
||||
case 'imagejpeg':
|
||||
// Default the quality to 95
|
||||
($quality === NULL) and $quality = 95;
|
||||
break;
|
||||
case 'imagegif':
|
||||
// Remove the quality setting, GIF doesn't use it
|
||||
unset($quality);
|
||||
break;
|
||||
case 'imagepng':
|
||||
// Always use a compression level of 9 for PNGs. This does not
|
||||
// affect quality, it only increases the level of compression!
|
||||
$quality = 9;
|
||||
break;
|
||||
}
|
||||
|
||||
if ($render === FALSE)
|
||||
{
|
||||
// Set the status to the save return value, saving with the quality requested
|
||||
$status = isset($quality) ? $save($this->tmp_image, $dir.$file, $quality) : $save($this->tmp_image, $dir.$file);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Output the image directly to the browser
|
||||
switch ($save)
|
||||
{
|
||||
case 'imagejpeg':
|
||||
header('Content-Type: image/jpeg');
|
||||
break;
|
||||
case 'imagegif':
|
||||
header('Content-Type: image/gif');
|
||||
break;
|
||||
case 'imagepng':
|
||||
header('Content-Type: image/png');
|
||||
break;
|
||||
}
|
||||
|
||||
$status = isset($quality) ? $save($this->tmp_image, NULL, $quality) : $save($this->tmp_image);
|
||||
}
|
||||
|
||||
// Destroy the temporary image
|
||||
imagedestroy($this->tmp_image);
|
||||
}
|
||||
|
||||
return $status;
|
||||
}
|
||||
|
||||
public function flip($direction)
|
||||
{
|
||||
// Get the current width and height
|
||||
$width = imagesx($this->tmp_image);
|
||||
$height = imagesy($this->tmp_image);
|
||||
|
||||
// Create the flipped image
|
||||
$flipped = $this->imagecreatetransparent($width, $height);
|
||||
|
||||
if ($direction === Image::HORIZONTAL)
|
||||
{
|
||||
for ($x = 0; $x < $width; $x++)
|
||||
{
|
||||
$status = imagecopy($flipped, $this->tmp_image, $x, 0, $width - $x - 1, 0, 1, $height);
|
||||
}
|
||||
}
|
||||
elseif ($direction === Image::VERTICAL)
|
||||
{
|
||||
for ($y = 0; $y < $height; $y++)
|
||||
{
|
||||
$status = imagecopy($flipped, $this->tmp_image, 0, $y, 0, $height - $y - 1, $width, 1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Do nothing
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if ($status === TRUE)
|
||||
{
|
||||
// Swap the new image for the old one
|
||||
imagedestroy($this->tmp_image);
|
||||
$this->tmp_image = $flipped;
|
||||
}
|
||||
|
||||
return $status;
|
||||
}
|
||||
|
||||
public function crop($properties)
|
||||
{
|
||||
// Sanitize the cropping settings
|
||||
$this->sanitize_geometry($properties);
|
||||
|
||||
// Get the current width and height
|
||||
$width = imagesx($this->tmp_image);
|
||||
$height = imagesy($this->tmp_image);
|
||||
|
||||
// Create the temporary image to copy to
|
||||
$img = $this->imagecreatetransparent($properties['width'], $properties['height']);
|
||||
|
||||
// Execute the crop
|
||||
if ($status = imagecopyresampled($img, $this->tmp_image, 0, 0, $properties['left'], $properties['top'], $width, $height, $width, $height))
|
||||
{
|
||||
// Swap the new image for the old one
|
||||
imagedestroy($this->tmp_image);
|
||||
$this->tmp_image = $img;
|
||||
}
|
||||
|
||||
return $status;
|
||||
}
|
||||
|
||||
public function resize($properties)
|
||||
{
|
||||
// Get the current width and height
|
||||
$width = imagesx($this->tmp_image);
|
||||
$height = imagesy($this->tmp_image);
|
||||
|
||||
if (substr($properties['width'], -1) === '%')
|
||||
{
|
||||
// Recalculate the percentage to a pixel size
|
||||
$properties['width'] = round($width * (substr($properties['width'], 0, -1) / 100));
|
||||
}
|
||||
|
||||
if (substr($properties['height'], -1) === '%')
|
||||
{
|
||||
// Recalculate the percentage to a pixel size
|
||||
$properties['height'] = round($height * (substr($properties['height'], 0, -1) / 100));
|
||||
}
|
||||
|
||||
// Recalculate the width and height, if they are missing
|
||||
empty($properties['width']) and $properties['width'] = round($width * $properties['height'] / $height);
|
||||
empty($properties['height']) and $properties['height'] = round($height * $properties['width'] / $width);
|
||||
|
||||
if ($properties['master'] === Image::AUTO)
|
||||
{
|
||||
// Change an automatic master dim to the correct type
|
||||
$properties['master'] = (($width / $properties['width']) > ($height / $properties['height'])) ? Image::WIDTH : Image::HEIGHT;
|
||||
}
|
||||
|
||||
if (empty($properties['height']) OR $properties['master'] === Image::WIDTH)
|
||||
{
|
||||
// Recalculate the height based on the width
|
||||
$properties['height'] = round($height * $properties['width'] / $width);
|
||||
}
|
||||
|
||||
if (empty($properties['width']) OR $properties['master'] === Image::HEIGHT)
|
||||
{
|
||||
// Recalculate the width based on the height
|
||||
$properties['width'] = round($width * $properties['height'] / $height);
|
||||
}
|
||||
|
||||
// Test if we can do a resize without resampling to speed up the final resize
|
||||
if ($properties['width'] > $width / 2 AND $properties['height'] > $height / 2)
|
||||
{
|
||||
// Presize width and height
|
||||
$pre_width = $width;
|
||||
$pre_height = $height;
|
||||
|
||||
// The maximum reduction is 10% greater than the final size
|
||||
$max_reduction_width = round($properties['width'] * 1.1);
|
||||
$max_reduction_height = round($properties['height'] * 1.1);
|
||||
|
||||
// Reduce the size using an O(2n) algorithm, until it reaches the maximum reduction
|
||||
while ($pre_width / 2 > $max_reduction_width AND $pre_height / 2 > $max_reduction_height)
|
||||
{
|
||||
$pre_width /= 2;
|
||||
$pre_height /= 2;
|
||||
}
|
||||
|
||||
// Create the temporary image to copy to
|
||||
$img = $this->imagecreatetransparent($pre_width, $pre_height);
|
||||
|
||||
if ($status = imagecopyresized($img, $this->tmp_image, 0, 0, 0, 0, $pre_width, $pre_height, $width, $height))
|
||||
{
|
||||
// Swap the new image for the old one
|
||||
imagedestroy($this->tmp_image);
|
||||
$this->tmp_image = $img;
|
||||
}
|
||||
|
||||
// Set the width and height to the presize
|
||||
$width = $pre_width;
|
||||
$height = $pre_height;
|
||||
}
|
||||
|
||||
// Create the temporary image to copy to
|
||||
$img = $this->imagecreatetransparent($properties['width'], $properties['height']);
|
||||
|
||||
// Execute the resize
|
||||
if ($status = imagecopyresampled($img, $this->tmp_image, 0, 0, 0, 0, $properties['width'], $properties['height'], $width, $height))
|
||||
{
|
||||
// Swap the new image for the old one
|
||||
imagedestroy($this->tmp_image);
|
||||
$this->tmp_image = $img;
|
||||
}
|
||||
|
||||
return $status;
|
||||
}
|
||||
|
||||
public function rotate($amount)
|
||||
{
|
||||
// Use current image to rotate
|
||||
$img = $this->tmp_image;
|
||||
|
||||
// White, with an alpha of 0
|
||||
$transparent = imagecolorallocatealpha($img, 255, 255, 255, 127);
|
||||
|
||||
// Rotate, setting the transparent color
|
||||
$img = imagerotate($img, 360 - $amount, $transparent, -1);
|
||||
|
||||
// Fill the background with the transparent "color"
|
||||
imagecolortransparent($img, $transparent);
|
||||
|
||||
// Merge the images
|
||||
if ($status = imagecopymerge($this->tmp_image, $img, 0, 0, 0, 0, imagesx($this->tmp_image), imagesy($this->tmp_image), 100))
|
||||
{
|
||||
// Prevent the alpha from being lost
|
||||
imagealphablending($img, TRUE);
|
||||
imagesavealpha($img, TRUE);
|
||||
|
||||
// Swap the new image for the old one
|
||||
imagedestroy($this->tmp_image);
|
||||
$this->tmp_image = $img;
|
||||
}
|
||||
|
||||
return $status;
|
||||
}
|
||||
|
||||
public function sharpen($amount)
|
||||
{
|
||||
// Make sure that the sharpening function is available
|
||||
if ( ! function_exists('imageconvolution'))
|
||||
throw new Kohana_Exception('image.unsupported_method', __FUNCTION__);
|
||||
|
||||
// Amount should be in the range of 18-10
|
||||
$amount = round(abs(-18 + ($amount * 0.08)), 2);
|
||||
|
||||
// Gaussian blur matrix
|
||||
$matrix = array
|
||||
(
|
||||
array(-1, -1, -1),
|
||||
array(-1, $amount, -1),
|
||||
array(-1, -1, -1),
|
||||
);
|
||||
|
||||
// Perform the sharpen
|
||||
return imageconvolution($this->tmp_image, $matrix, $amount - 8, 0);
|
||||
}
|
||||
|
||||
protected function properties()
|
||||
{
|
||||
return array(imagesx($this->tmp_image), imagesy($this->tmp_image));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an image with a transparent background. Used for rotating to
|
||||
* prevent unfilled backgrounds.
|
||||
*
|
||||
* @param integer image width
|
||||
* @param integer image height
|
||||
* @return resource
|
||||
*/
|
||||
protected function imagecreatetransparent($width, $height)
|
||||
{
|
||||
if (self::$blank_png === NULL)
|
||||
{
|
||||
// Decode the blank PNG if it has not been done already
|
||||
self::$blank_png = imagecreatefromstring(base64_decode
|
||||
(
|
||||
'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29'.
|
||||
'mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAADqSURBVHjaYvz//z/DYAYAAcTEMMgBQAANegcCBN'.
|
||||
'CgdyBAAA16BwIE0KB3IEAADXoHAgTQoHcgQAANegcCBNCgdyBAAA16BwIE0KB3IEAADXoHAgTQoHcgQ'.
|
||||
'AANegcCBNCgdyBAAA16BwIE0KB3IEAADXoHAgTQoHcgQAANegcCBNCgdyBAAA16BwIE0KB3IEAADXoH'.
|
||||
'AgTQoHcgQAANegcCBNCgdyBAAA16BwIE0KB3IEAADXoHAgTQoHcgQAANegcCBNCgdyBAAA16BwIE0KB'.
|
||||
'3IEAADXoHAgTQoHcgQAANegcCBNCgdyBAgAEAMpcDTTQWJVEAAAAASUVORK5CYII='
|
||||
));
|
||||
|
||||
// Set the blank PNG width and height
|
||||
self::$blank_png_width = imagesx(self::$blank_png);
|
||||
self::$blank_png_height = imagesy(self::$blank_png);
|
||||
}
|
||||
|
||||
$img = imagecreatetruecolor($width, $height);
|
||||
|
||||
// Resize the blank image
|
||||
imagecopyresized($img, self::$blank_png, 0, 0, 0, 0, $width, $height, self::$blank_png_width, self::$blank_png_height);
|
||||
|
||||
// Prevent the alpha from being lost
|
||||
imagealphablending($img, FALSE);
|
||||
imagesavealpha($img, TRUE);
|
||||
|
||||
return $img;
|
||||
}
|
||||
|
||||
} // End Image GD Driver
|
||||
211
lib/kohana/system/libraries/drivers/Image/GraphicsMagick.php
Normal file
211
lib/kohana/system/libraries/drivers/Image/GraphicsMagick.php
Normal file
@@ -0,0 +1,211 @@
|
||||
<?php defined('SYSPATH') OR die('No direct access allowed.');
|
||||
/**
|
||||
* GraphicsMagick Image Driver.
|
||||
*
|
||||
* @package Image
|
||||
* @author Kohana Team
|
||||
* @copyright (c) 2007-2008 Kohana Team
|
||||
* @license http://kohanaphp.com/license.html
|
||||
*/
|
||||
class Image_GraphicsMagick_Driver extends Image_Driver {
|
||||
|
||||
// Directory that GM is installed in
|
||||
protected $dir = '';
|
||||
|
||||
// Command extension (exe for windows)
|
||||
protected $ext = '';
|
||||
|
||||
// Temporary image filename
|
||||
protected $tmp_image;
|
||||
|
||||
/**
|
||||
* Attempts to detect the GraphicsMagick installation directory.
|
||||
*
|
||||
* @throws Kohana_Exception
|
||||
* @param array configuration
|
||||
* @return void
|
||||
*/
|
||||
public function __construct($config)
|
||||
{
|
||||
if (empty($config['directory']))
|
||||
{
|
||||
// Attempt to locate GM by using "which" (only works for *nix!)
|
||||
if ( ! is_file($path = exec('which gm')))
|
||||
throw new Kohana_Exception('image.graphicsmagick.not_found');
|
||||
|
||||
$config['directory'] = dirname($path);
|
||||
}
|
||||
|
||||
// Set the command extension
|
||||
$this->ext = (PHP_SHLIB_SUFFIX === 'dll') ? '.exe' : '';
|
||||
|
||||
// Check to make sure the provided path is correct
|
||||
if ( ! is_file(realpath($config['directory']).'/gm'.$this->ext))
|
||||
throw new Kohana_Exception('image.graphicsmagick.not_found', 'gm'.$this->ext);
|
||||
|
||||
|
||||
// Set the installation directory
|
||||
$this->dir = str_replace('\\', '/', realpath($config['directory'])).'/';
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a temporary image and executes the given actions. By creating a
|
||||
* temporary copy of the image before manipulating it, this process is atomic.
|
||||
*/
|
||||
public function process($image, $actions, $dir, $file, $render = FALSE)
|
||||
{
|
||||
// We only need the filename
|
||||
$image = $image['file'];
|
||||
|
||||
// Unique temporary filename
|
||||
$this->tmp_image = $dir.'k2img--'.sha1(time().$dir.$file).substr($file, strrpos($file, '.'));
|
||||
|
||||
// Copy the image to the temporary file
|
||||
copy($image, $this->tmp_image);
|
||||
|
||||
// Quality change is done last
|
||||
$quality = (int) arr::remove('quality', $actions);
|
||||
|
||||
// Use 95 for the default quality
|
||||
empty($quality) and $quality = 95;
|
||||
|
||||
// All calls to these will need to be escaped, so do it now
|
||||
$this->cmd_image = escapeshellarg($this->tmp_image);
|
||||
$this->new_image = ($render)? $this->cmd_image : escapeshellarg($dir.$file);
|
||||
|
||||
if ($status = $this->execute($actions))
|
||||
{
|
||||
// Use convert to change the image into its final version. This is
|
||||
// done to allow the file type to change correctly, and to handle
|
||||
// the quality conversion in the most effective way possible.
|
||||
if ($error = exec(escapeshellcmd($this->dir.'gm'.$this->ext.' convert').' -quality '.$quality.'% '.$this->cmd_image.' '.$this->new_image))
|
||||
{
|
||||
$this->errors[] = $error;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Output the image directly to the browser
|
||||
if ($render !== FALSE)
|
||||
{
|
||||
$contents = file_get_contents($this->tmp_image);
|
||||
switch (substr($file, strrpos($file, '.') + 1))
|
||||
{
|
||||
case 'jpg':
|
||||
case 'jpeg':
|
||||
header('Content-Type: image/jpeg');
|
||||
break;
|
||||
case 'gif':
|
||||
header('Content-Type: image/gif');
|
||||
break;
|
||||
case 'png':
|
||||
header('Content-Type: image/png');
|
||||
break;
|
||||
}
|
||||
echo $contents;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Remove the temporary image
|
||||
unlink($this->tmp_image);
|
||||
$this->tmp_image = '';
|
||||
|
||||
return $status;
|
||||
}
|
||||
|
||||
public function crop($prop)
|
||||
{
|
||||
// Sanitize and normalize the properties into geometry
|
||||
$this->sanitize_geometry($prop);
|
||||
|
||||
// Set the IM geometry based on the properties
|
||||
$geometry = escapeshellarg($prop['width'].'x'.$prop['height'].'+'.$prop['left'].'+'.$prop['top']);
|
||||
|
||||
if ($error = exec(escapeshellcmd($this->dir.'gm'.$this->ext.' convert').' -crop '.$geometry.' '.$this->cmd_image.' '.$this->cmd_image))
|
||||
{
|
||||
$this->errors[] = $error;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
public function flip($dir)
|
||||
{
|
||||
// Convert the direction into a GM command
|
||||
$dir = ($dir === Image::HORIZONTAL) ? '-flop' : '-flip';
|
||||
|
||||
if ($error = exec(escapeshellcmd($this->dir.'gm'.$this->ext.' convert').' '.$dir.' '.$this->cmd_image.' '.$this->cmd_image))
|
||||
{
|
||||
$this->errors[] = $error;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
public function resize($prop)
|
||||
{
|
||||
switch ($prop['master'])
|
||||
{
|
||||
case Image::WIDTH: // Wx
|
||||
$dim = escapeshellarg($prop['width'].'x');
|
||||
break;
|
||||
case Image::HEIGHT: // xH
|
||||
$dim = escapeshellarg('x'.$prop['height']);
|
||||
break;
|
||||
case Image::AUTO: // WxH
|
||||
$dim = escapeshellarg($prop['width'].'x'.$prop['height']);
|
||||
break;
|
||||
case Image::NONE: // WxH!
|
||||
$dim = escapeshellarg($prop['width'].'x'.$prop['height'].'!');
|
||||
break;
|
||||
}
|
||||
|
||||
// Use "convert" to change the width and height
|
||||
if ($error = exec(escapeshellcmd($this->dir.'gm'.$this->ext.' convert').' -resize '.$dim.' '.$this->cmd_image.' '.$this->cmd_image))
|
||||
{
|
||||
$this->errors[] = $error;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
public function rotate($amt)
|
||||
{
|
||||
if ($error = exec(escapeshellcmd($this->dir.'gm'.$this->ext.' convert').' -rotate '.escapeshellarg($amt).' -background transparent '.$this->cmd_image.' '.$this->cmd_image))
|
||||
{
|
||||
$this->errors[] = $error;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
public function sharpen($amount)
|
||||
{
|
||||
// Set the sigma, radius, and amount. The amount formula allows a nice
|
||||
// spread between 1 and 100 without pixelizing the image badly.
|
||||
$sigma = 0.5;
|
||||
$radius = $sigma * 2;
|
||||
$amount = round(($amount / 80) * 3.14, 2);
|
||||
|
||||
// Convert the amount to an GM command
|
||||
$sharpen = escapeshellarg($radius.'x'.$sigma.'+'.$amount.'+0');
|
||||
|
||||
if ($error = exec(escapeshellcmd($this->dir.'gm'.$this->ext.' convert').' -unsharp '.$sharpen.' '.$this->cmd_image.' '.$this->cmd_image))
|
||||
{
|
||||
$this->errors[] = $error;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
protected function properties()
|
||||
{
|
||||
return array_slice(getimagesize($this->tmp_image), 0, 2, FALSE);
|
||||
}
|
||||
|
||||
} // End Image GraphicsMagick Driver
|
||||
212
lib/kohana/system/libraries/drivers/Image/ImageMagick.php
Normal file
212
lib/kohana/system/libraries/drivers/Image/ImageMagick.php
Normal file
@@ -0,0 +1,212 @@
|
||||
<?php defined('SYSPATH') OR die('No direct access allowed.');
|
||||
/**
|
||||
* ImageMagick Image Driver.
|
||||
*
|
||||
* $Id: ImageMagick.php 3769 2008-12-15 00:48:56Z zombor $
|
||||
*
|
||||
* @package Image
|
||||
* @author Kohana Team
|
||||
* @copyright (c) 2007-2008 Kohana Team
|
||||
* @license http://kohanaphp.com/license.html
|
||||
*/
|
||||
class Image_ImageMagick_Driver extends Image_Driver {
|
||||
|
||||
// Directory that IM is installed in
|
||||
protected $dir = '';
|
||||
|
||||
// Command extension (exe for windows)
|
||||
protected $ext = '';
|
||||
|
||||
// Temporary image filename
|
||||
protected $tmp_image;
|
||||
|
||||
/**
|
||||
* Attempts to detect the ImageMagick installation directory.
|
||||
*
|
||||
* @throws Kohana_Exception
|
||||
* @param array configuration
|
||||
* @return void
|
||||
*/
|
||||
public function __construct($config)
|
||||
{
|
||||
if (empty($config['directory']))
|
||||
{
|
||||
// Attempt to locate IM by using "which" (only works for *nix!)
|
||||
if ( ! is_file($path = exec('which convert')))
|
||||
throw new Kohana_Exception('image.imagemagick.not_found');
|
||||
|
||||
$config['directory'] = dirname($path);
|
||||
}
|
||||
|
||||
// Set the command extension
|
||||
$this->ext = (PHP_SHLIB_SUFFIX === 'dll') ? '.exe' : '';
|
||||
|
||||
// Check to make sure the provided path is correct
|
||||
if ( ! is_file(realpath($config['directory']).'/convert'.$this->ext))
|
||||
throw new Kohana_Exception('image.imagemagick.not_found', 'convert'.$this->ext);
|
||||
|
||||
// Set the installation directory
|
||||
$this->dir = str_replace('\\', '/', realpath($config['directory'])).'/';
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a temporary image and executes the given actions. By creating a
|
||||
* temporary copy of the image before manipulating it, this process is atomic.
|
||||
*/
|
||||
public function process($image, $actions, $dir, $file, $render = FALSE)
|
||||
{
|
||||
// We only need the filename
|
||||
$image = $image['file'];
|
||||
|
||||
// Unique temporary filename
|
||||
$this->tmp_image = $dir.'k2img--'.sha1(time().$dir.$file).substr($file, strrpos($file, '.'));
|
||||
|
||||
// Copy the image to the temporary file
|
||||
copy($image, $this->tmp_image);
|
||||
|
||||
// Quality change is done last
|
||||
$quality = (int) arr::remove('quality', $actions);
|
||||
|
||||
// Use 95 for the default quality
|
||||
empty($quality) and $quality = 95;
|
||||
|
||||
// All calls to these will need to be escaped, so do it now
|
||||
$this->cmd_image = escapeshellarg($this->tmp_image);
|
||||
$this->new_image = ($render)? $this->cmd_image : escapeshellarg($dir.$file);
|
||||
|
||||
if ($status = $this->execute($actions))
|
||||
{
|
||||
// Use convert to change the image into its final version. This is
|
||||
// done to allow the file type to change correctly, and to handle
|
||||
// the quality conversion in the most effective way possible.
|
||||
if ($error = exec(escapeshellcmd($this->dir.'convert'.$this->ext).' -quality '.$quality.'% '.$this->cmd_image.' '.$this->new_image))
|
||||
{
|
||||
$this->errors[] = $error;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Output the image directly to the browser
|
||||
if ($render !== FALSE)
|
||||
{
|
||||
$contents = file_get_contents($this->tmp_image);
|
||||
switch (substr($file, strrpos($file, '.') + 1))
|
||||
{
|
||||
case 'jpg':
|
||||
case 'jpeg':
|
||||
header('Content-Type: image/jpeg');
|
||||
break;
|
||||
case 'gif':
|
||||
header('Content-Type: image/gif');
|
||||
break;
|
||||
case 'png':
|
||||
header('Content-Type: image/png');
|
||||
break;
|
||||
}
|
||||
echo $contents;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Remove the temporary image
|
||||
unlink($this->tmp_image);
|
||||
$this->tmp_image = '';
|
||||
|
||||
return $status;
|
||||
}
|
||||
|
||||
public function crop($prop)
|
||||
{
|
||||
// Sanitize and normalize the properties into geometry
|
||||
$this->sanitize_geometry($prop);
|
||||
|
||||
// Set the IM geometry based on the properties
|
||||
$geometry = escapeshellarg($prop['width'].'x'.$prop['height'].'+'.$prop['left'].'+'.$prop['top']);
|
||||
|
||||
if ($error = exec(escapeshellcmd($this->dir.'convert'.$this->ext).' -crop '.$geometry.' '.$this->cmd_image.' '.$this->cmd_image))
|
||||
{
|
||||
$this->errors[] = $error;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
public function flip($dir)
|
||||
{
|
||||
// Convert the direction into a IM command
|
||||
$dir = ($dir === Image::HORIZONTAL) ? '-flop' : '-flip';
|
||||
|
||||
if ($error = exec(escapeshellcmd($this->dir.'convert'.$this->ext).' '.$dir.' '.$this->cmd_image.' '.$this->cmd_image))
|
||||
{
|
||||
$this->errors[] = $error;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
public function resize($prop)
|
||||
{
|
||||
switch ($prop['master'])
|
||||
{
|
||||
case Image::WIDTH: // Wx
|
||||
$dim = escapeshellarg($prop['width'].'x');
|
||||
break;
|
||||
case Image::HEIGHT: // xH
|
||||
$dim = escapeshellarg('x'.$prop['height']);
|
||||
break;
|
||||
case Image::AUTO: // WxH
|
||||
$dim = escapeshellarg($prop['width'].'x'.$prop['height']);
|
||||
break;
|
||||
case Image::NONE: // WxH!
|
||||
$dim = escapeshellarg($prop['width'].'x'.$prop['height'].'!');
|
||||
break;
|
||||
}
|
||||
|
||||
// Use "convert" to change the width and height
|
||||
if ($error = exec(escapeshellcmd($this->dir.'convert'.$this->ext).' -resize '.$dim.' '.$this->cmd_image.' '.$this->cmd_image))
|
||||
{
|
||||
$this->errors[] = $error;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
public function rotate($amt)
|
||||
{
|
||||
if ($error = exec(escapeshellcmd($this->dir.'convert'.$this->ext).' -rotate '.escapeshellarg($amt).' -background transparent '.$this->cmd_image.' '.$this->cmd_image))
|
||||
{
|
||||
$this->errors[] = $error;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
public function sharpen($amount)
|
||||
{
|
||||
// Set the sigma, radius, and amount. The amount formula allows a nice
|
||||
// spread between 1 and 100 without pixelizing the image badly.
|
||||
$sigma = 0.5;
|
||||
$radius = $sigma * 2;
|
||||
$amount = round(($amount / 80) * 3.14, 2);
|
||||
|
||||
// Convert the amount to an IM command
|
||||
$sharpen = escapeshellarg($radius.'x'.$sigma.'+'.$amount.'+0');
|
||||
|
||||
if ($error = exec(escapeshellcmd($this->dir.'convert'.$this->ext).' -unsharp '.$sharpen.' '.$this->cmd_image.' '.$this->cmd_image))
|
||||
{
|
||||
$this->errors[] = $error;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
protected function properties()
|
||||
{
|
||||
return array_slice(getimagesize($this->tmp_image), 0, 2, FALSE);
|
||||
}
|
||||
|
||||
} // End Image ImageMagick Driver
|
||||
Reference in New Issue
Block a user