662 lines
19 KiB
Text
662 lines
19 KiB
Text
|
<?php namespace ProcessWire;
|
||
|
|
||
|
/**
|
||
|
* ImageSizer Engine IMagick by Horst
|
||
|
*
|
||
|
* @todo some class properties need phpdoc
|
||
|
*
|
||
|
*/
|
||
|
class ImageSizerEngineIMagick extends ImageSizerEngine {
|
||
|
|
||
|
public static function getModuleInfo() {
|
||
|
return array(
|
||
|
'title' => 'IMagick Image Sizer',
|
||
|
'version' => 3,
|
||
|
'summary' => "Upgrades image manipulations to use PHP's ImageMagick library when possible.",
|
||
|
'author' => 'Horst Nogajski',
|
||
|
'autoload' => false,
|
||
|
'singular' => false,
|
||
|
);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* The (main) IMagick bitimage handler for regular image variations, (JPEG PNG)
|
||
|
*
|
||
|
* @var \IMagick|null
|
||
|
*
|
||
|
*/
|
||
|
protected $im = null;
|
||
|
|
||
|
/**
|
||
|
* The (optionally) IMagick bitimage handler for additional WebP copies
|
||
|
*
|
||
|
* @var \IMagick|null
|
||
|
*
|
||
|
*/
|
||
|
protected $imWebp = null;
|
||
|
|
||
|
/**
|
||
|
* Static cache of formats and whether or not supported, as used by the supportsFormat() method (RJC)
|
||
|
*
|
||
|
* @var array of ['FORMAT' => true|false]
|
||
|
*
|
||
|
*/
|
||
|
static protected $formatSupport = array();
|
||
|
|
||
|
// @todo the following need phpdoc
|
||
|
protected $workspaceColorspace;
|
||
|
protected $imageFormat;
|
||
|
protected $imageColorspace;
|
||
|
protected $imageMetadata;
|
||
|
protected $imageDepth;
|
||
|
protected $imageGamma;
|
||
|
|
||
|
/**
|
||
|
* @var bool
|
||
|
*
|
||
|
*/
|
||
|
protected $hasICC;
|
||
|
|
||
|
/**
|
||
|
* @var bool
|
||
|
*
|
||
|
*/
|
||
|
protected $hasIPTC;
|
||
|
|
||
|
/**
|
||
|
* @var bool
|
||
|
*
|
||
|
*/
|
||
|
protected $hasEXIF;
|
||
|
|
||
|
/**
|
||
|
* @var bool
|
||
|
*
|
||
|
*/
|
||
|
protected $hasXMP;
|
||
|
|
||
|
/**
|
||
|
* Class constructor
|
||
|
*
|
||
|
*/
|
||
|
public function __construct() {
|
||
|
// set a lower default quality of 80, which is more like 90 in GD
|
||
|
$this->setQuality(80);
|
||
|
parent::__construct();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Class destructor
|
||
|
*
|
||
|
*/
|
||
|
public function __destruct() {
|
||
|
$this->release();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Release resources used by IMagick
|
||
|
*
|
||
|
*/
|
||
|
protected function release() {
|
||
|
if(is_object($this->im)) {
|
||
|
$this->im->clear();
|
||
|
$this->im->destroy();
|
||
|
}
|
||
|
if(is_object($this->imWebp)) {
|
||
|
$this->imWebp->clear();
|
||
|
$this->imWebp->destroy();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get valid image source formats
|
||
|
*
|
||
|
* @return array
|
||
|
*
|
||
|
*/
|
||
|
protected function validSourceImageFormats() {
|
||
|
// 2019/06/07: “PNG8” removed because some versions of ImageMagick have some bug, may be able to add back later
|
||
|
return array('JPG', 'JPEG', 'PNG24', 'PNG', 'GIF', 'GIF87');
|
||
|
//return array(
|
||
|
// 'PNG', 'PNG8', 'PNG24',
|
||
|
// 'JPG', 'JPEG',
|
||
|
// 'GIF', 'GIF87'
|
||
|
//);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get valid target image formats
|
||
|
*
|
||
|
* @return array
|
||
|
*
|
||
|
*/
|
||
|
protected function validTargetImageFormats() {
|
||
|
$formats = $this->validSourceImageFormats();
|
||
|
if($this->supportsFormat('WEBP')) $formats[] = 'WEBP';
|
||
|
return $formats;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get library version string
|
||
|
*
|
||
|
* @return string Returns version string or blank string if not applicable/available
|
||
|
* @since 3.0.138
|
||
|
*
|
||
|
*/
|
||
|
public function getLibraryVersion() {
|
||
|
$a = \Imagick::getVersion();
|
||
|
return "$a[versionString] n=$a[versionNumber]";
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Is the given image format supported by this IMagick for source and target? (RJC)
|
||
|
*
|
||
|
* @param string $format String like png, jpg, jpg, png8, png24, png24-trans, png24-alpha, etc.
|
||
|
* @return bool
|
||
|
*
|
||
|
*/
|
||
|
public function supportsFormat($format) {
|
||
|
if(strpos($format, '-')) list($format,) = explode('-', $format);
|
||
|
$format = strtoupper($format);
|
||
|
if(isset(self::$formatSupport[$format])) return self::$formatSupport[$format];
|
||
|
try {
|
||
|
$im = new \IMagick();
|
||
|
$formats = $im->queryformats($format);
|
||
|
$supported = count($formats) > 0;
|
||
|
} catch(\Exception $e) {
|
||
|
$supported = false;
|
||
|
}
|
||
|
self::$formatSupport[$format] = $supported;
|
||
|
return $supported;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Is IMagick supported? Is the current image(sub)format supported?
|
||
|
*
|
||
|
* @param string $action
|
||
|
* @return bool
|
||
|
*
|
||
|
*/
|
||
|
public function supported($action = 'imageformat') {
|
||
|
|
||
|
// first we check parts that are mandatory for all $actions
|
||
|
if(!class_exists("\\IMagick")) return false;
|
||
|
|
||
|
// and if it passes the mandatory requirements, we check particularly aspects here
|
||
|
$supported = false;
|
||
|
if($action === 'imageformat') {
|
||
|
// compare current imagefile infos fetched from ImageInspector
|
||
|
$requested = $this->getImageInfo(false);
|
||
|
$supported = $this->supportsFormat($requested);
|
||
|
} else if($action === 'webp') {
|
||
|
$supported = $this->supportsFormat('WEBP');
|
||
|
} else if($action === 'install') {
|
||
|
$supported = true;
|
||
|
}
|
||
|
|
||
|
return $supported;
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Process the image resize
|
||
|
*
|
||
|
* Processing is as follows:
|
||
|
* 1. first do a check if the given image(type) can be processed, if not do an early return false
|
||
|
* 2. than (try) to process all required steps, if one failes, return false
|
||
|
* 3. if all is successful, finally return true
|
||
|
*
|
||
|
* @param string $srcFilename Source file
|
||
|
* @param string $dstFilename Destination file
|
||
|
* @param int $fullWidth Current width
|
||
|
* @param int $fullHeight Current height
|
||
|
* @param int $finalWidth Requested final width
|
||
|
* @param int $finalHeight Requested final height
|
||
|
* @return bool True if successful, false if not
|
||
|
* @throws WireException
|
||
|
*
|
||
|
*/
|
||
|
protected function processResize($srcFilename, $dstFilename, $fullWidth, $fullHeight, $finalWidth, $finalHeight) {
|
||
|
|
||
|
$this->setTimeLimit(120);
|
||
|
|
||
|
// start image magick
|
||
|
$this->im = new \IMagick();
|
||
|
|
||
|
// set the working colorspace: COLORSPACE_RGB or COLORSPACE_SRGB ( whats about COLORSPACE_GRAY ??)
|
||
|
$this->workspaceColorspace = \Imagick::COLORSPACE_SRGB;
|
||
|
$this->im->setColorspace($this->workspaceColorspace);
|
||
|
|
||
|
if(!$this->im->readImage($srcFilename)) { // actually we get a filecopy from origFilename to destFilename from PageImage
|
||
|
$this->release();
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// check validity against image magick
|
||
|
if(!$this->im->valid()) {
|
||
|
$this->release();
|
||
|
throw new WireException(sprintf($this->_("loaded file '%s' is not a valid image"), basename($srcFilename)));
|
||
|
}
|
||
|
|
||
|
// get image format
|
||
|
$this->imageFormat = strtoupper($this->im->getImageFormat());
|
||
|
|
||
|
// only for JPEGs and 24bit PNGs
|
||
|
if(!in_array($this->imageFormat, $this->validSourceImageFormats())) {
|
||
|
$this->release();
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// check validity against PW (this does not seem to be reachable due to above code, so commented it out —Ryan)
|
||
|
// if(!in_array($this->imageFormat, $this->validSourceImageFormats())) {
|
||
|
// $this->release();
|
||
|
// throw new WireException(sprintf($this->_("loaded file '%s' is not in the list of valid images"), basename($dstFilename)));
|
||
|
// }
|
||
|
|
||
|
// check and retrieve different image parts and information: ICC, Colorspace, Colordepth, Metadata, etc
|
||
|
$this->imageColorspace = $this->im->getImageColorspace();
|
||
|
$this->workspaceColorspace = \Imagick::COLORSPACE_GRAY == $this->imageColorspace ? \Imagick::COLORSPACE_GRAY : $this->workspaceColorspace;
|
||
|
$this->im->setColorspace($this->workspaceColorspace);
|
||
|
$this->imageMetadata = $this->im->getImageProfiles('*');
|
||
|
if(!is_array($this->imageMetadata)) $this->imageMetadata = array();
|
||
|
$this->hasICC = array_key_exists('icc', $this->imageMetadata);
|
||
|
$this->hasIPTC = array_key_exists('iptc', $this->imageMetadata);
|
||
|
$this->hasEXIF = array_key_exists('exif', $this->imageMetadata);
|
||
|
$this->hasXMP = array_key_exists('xmp', $this->imageMetadata);
|
||
|
$this->imageType = $this->im->getImageType();
|
||
|
$this->imageDepth = $this->im->getImageDepth();
|
||
|
$this->imageGamma = $this->im->getImageGamma();
|
||
|
|
||
|
if(0 == $this->imageGamma) {
|
||
|
// we seem to running on a IMagick version that lacks some features,
|
||
|
// at least the 'getImageGamma()', therefor we asume a Gamma of 2.2 here
|
||
|
$this->imageGamma = 0.454545;
|
||
|
}
|
||
|
|
||
|
// remove not wanted / needed Metadata = this is the same behave as processed via GD-lib
|
||
|
foreach(array_keys($this->imageMetadata) as $k) {
|
||
|
#if('icc'==$k) continue; // we keep embedded icc profiles
|
||
|
#if('iptc' == $k) continue; // we keep embedded iptc data
|
||
|
#if('exif'==$k && $this->data['keepEXIF']) continue; // we have to keep exif data too
|
||
|
#if('xmp'==$k && $this->data['keepXMP']) continue; // we have to keep xmp data too
|
||
|
$this->im->profileImage("$k", null); // remove this one
|
||
|
}
|
||
|
|
||
|
$this->im->setImageDepth(16);
|
||
|
|
||
|
$resetGamma = false;
|
||
|
if($this->imageGamma && $this->imageGamma != 1) {
|
||
|
$resetGamma = $this->im->gammaImage($this->imageGamma);
|
||
|
}
|
||
|
|
||
|
$orientations = null;
|
||
|
if($this->autoRotation !== true) {
|
||
|
$needRotation = false;
|
||
|
} else if($this->checkOrientation($orientations) && (!empty($orientations[0]) || !empty($orientations[1]))) {
|
||
|
$needRotation = true;
|
||
|
} else {
|
||
|
$needRotation = false;
|
||
|
}
|
||
|
|
||
|
if($this->rotate || $needRotation) { // @horst
|
||
|
if($this->rotate) {
|
||
|
$degrees = $this->rotate;
|
||
|
} else if((is_float($orientations[0]) || is_int($orientations[0])) && $orientations[0] > -361 && $orientations[0] < 361) {
|
||
|
$degrees = $orientations[0];
|
||
|
} else {
|
||
|
$degrees = false;
|
||
|
}
|
||
|
if($degrees !== false && !in_array($degrees, array(-360, 0, 360))) {
|
||
|
$this->im->rotateImage(new \IMagickPixel('none'), $degrees);
|
||
|
if(abs($degrees) == 90 || abs($degrees) == 270) {
|
||
|
// we have to swap width & height now!
|
||
|
$tmp = array($this->getWidth(), $this->getHeight());
|
||
|
$this->setImageInfo($tmp[1], $tmp[0]);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if($this->flip || $needRotation) {
|
||
|
$vertical = null;
|
||
|
if($this->flip) {
|
||
|
$vertical = $this->flip == 'v';
|
||
|
} else if($orientations[1] > 0) {
|
||
|
$vertical = $orientations[1] == 2;
|
||
|
}
|
||
|
if(!is_null($vertical)) {
|
||
|
$res = $vertical ? $this->im->flipImage() : $this->im->flopImage();
|
||
|
if(!$res) {
|
||
|
$this->release();
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
$zoom = $this->getFocusZoomPercent();
|
||
|
if($zoom > 1) {
|
||
|
// we need to configure a cropExtra call to respect the zoom factor
|
||
|
$this->cropExtra = $this->getFocusZoomCropDimensions($zoom, $fullWidth, $fullHeight, $finalWidth, $finalHeight);
|
||
|
$this->cropping = false;
|
||
|
}
|
||
|
|
||
|
if(is_array($this->cropExtra) && 4 == count($this->cropExtra)) { // crop before resize
|
||
|
list($cropX, $cropY, $cropWidth, $cropHeight) = $this->cropExtra;
|
||
|
#list($x, $y, $w, $h) = $this->cropExtra;
|
||
|
if(!$this->im->cropImage($cropWidth, $cropHeight, $cropX, $cropY)) {
|
||
|
$this->release();
|
||
|
return false;
|
||
|
}
|
||
|
$this->im->setImagePage(0, 0, 0, 0); //remove the canvas
|
||
|
$this->setImageInfo($this->im->getImageWidth(), $this->im->getImageHeight());
|
||
|
}
|
||
|
|
||
|
$bgX = $bgY = 0;
|
||
|
$bgWidth = $fullWidth;
|
||
|
$bgHeight = $fullHeight;
|
||
|
$resizemethod = $this->getResizeMethod($bgWidth, $bgHeight, $finalWidth, $finalHeight, $bgX, $bgY);
|
||
|
|
||
|
if(0 == $resizemethod) {
|
||
|
$this->sharpening = 'none'; // no need for sharpening because we use original copy without scaling
|
||
|
// oh, do we need to save with more compression for JPEGs ??
|
||
|
#return true;
|
||
|
|
||
|
} else if(2 == $resizemethod) { // 2 = resize with aspect ratio
|
||
|
if(!$this->im->resizeImage($finalWidth, $finalHeight, \Imagick::FILTER_LANCZOS, 1)) {
|
||
|
$this->release();
|
||
|
return false;
|
||
|
}
|
||
|
$this->setImageInfo($this->im->getImageWidth(), $this->im->getImageHeight());
|
||
|
|
||
|
} else if(4 == $resizemethod) { // 4 = resize and crop from center with aspect ratio
|
||
|
if(!$this->im->resizeImage($bgWidth, $bgHeight, \Imagick::FILTER_LANCZOS, 1)) {
|
||
|
$this->release();
|
||
|
return false;
|
||
|
}
|
||
|
$this->setImageInfo($this->im->getImageWidth(), $this->im->getImageHeight());
|
||
|
if(!$this->im->cropImage($finalWidth, $finalHeight, $bgX, $bgY)) {
|
||
|
$this->release();
|
||
|
return false;
|
||
|
}
|
||
|
$this->im->setImagePage(0, 0, 0, 0); //remove the canvas
|
||
|
$this->setImageInfo($this->im->getImageWidth(), $this->im->getImageHeight());
|
||
|
}
|
||
|
|
||
|
if($this->sharpening && $this->sharpening != 'none') {
|
||
|
$this->imSharpen($this->sharpening);
|
||
|
}
|
||
|
|
||
|
// optionally apply interlace bit to the final image. This will result in progressive JPEGs
|
||
|
if($this->interlace && in_array(strtoupper($this->imageFormat), array('JPG', 'JPEG'))) {
|
||
|
$this->im->setInterlaceScheme(\Imagick::INTERLACE_JPEG);
|
||
|
}
|
||
|
|
||
|
if(isset($resetGamma) && $this->imageGamma && $this->imageGamma != 1) {
|
||
|
$this->im->gammaImage(1 / $this->imageGamma);
|
||
|
}
|
||
|
|
||
|
$this->im->setImageDepth(($this->imageDepth > 8 ? 8 : $this->imageDepth));
|
||
|
|
||
|
// determine whether webp should be created as well (or on its own)
|
||
|
$webpOnly = $this->webpOnly && $this->supported('webp');
|
||
|
$webpAdd = $webpOnly || ($this->webpAdd && $this->supported('webp'));
|
||
|
|
||
|
if($webpOnly) {
|
||
|
// only a webp file will be created
|
||
|
$this->imWebp = $this->im;
|
||
|
} else {
|
||
|
if($webpAdd) $this->imWebp = clone $this->im; // make a copy before compressions take effect
|
||
|
$this->im->setImageFormat($this->imageFormat);
|
||
|
$this->im->setImageType($this->imageType);
|
||
|
if(in_array(strtoupper($this->imageFormat), array('JPG', 'JPEG'))) {
|
||
|
$this->im->setImageCompression(\Imagick::COMPRESSION_JPEG);
|
||
|
$this->im->setImageCompressionQuality($this->quality);
|
||
|
} else if(in_array(strtoupper($this->imageFormat), array('PNG', 'PNG8', 'PNG24'))) {
|
||
|
$this->im->setImageCompression(\Imagick::COMPRESSION_ZIP);
|
||
|
$this->im->setImageCompressionQuality($this->quality);
|
||
|
} else {
|
||
|
$this->im->setImageCompression(\Imagick::COMPRESSION_UNDEFINED);
|
||
|
$this->im->setImageCompressionQuality($this->quality);
|
||
|
}
|
||
|
|
||
|
// write to file
|
||
|
if(file_exists($dstFilename)) $this->wire('files')->unlink($dstFilename);
|
||
|
@clearstatcache(dirname($dstFilename));
|
||
|
##if(!$this->im->writeImage($this->destFilename)) {
|
||
|
// We use this approach for saving so that it behaves the same like core ImageSizer with images that
|
||
|
// have a wrong extension in their filename. When using writeImage() it automatically corrects the
|
||
|
// mimetype to match the fileextension, <- we want to avoid this!
|
||
|
if(!file_put_contents($dstFilename, $this->im)) {
|
||
|
$this->release();
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// set modified flag and delete optional webp dependency file
|
||
|
$this->modified = true;
|
||
|
$return = true;
|
||
|
|
||
|
// if there is a corresponding webp file present, remove it
|
||
|
$pathinfo = pathinfo($srcFilename);
|
||
|
$webpFilename = $pathinfo['dirname'] . '/' . $pathinfo['filename'] . '.webp';
|
||
|
if(file_exists($webpFilename)) $this->wire('files')->unlink($webpFilename);
|
||
|
|
||
|
// optionally create a WebP dependency file
|
||
|
if($webpAdd) {
|
||
|
// prepare for webp output
|
||
|
$this->imWebp->setImageFormat('webp');
|
||
|
$this->imWebp->setImageCompressionQuality($this->webpQuality);
|
||
|
$this->imWebp->setOption('webp:method', '6');
|
||
|
//$this->imWebp->setOption('webp:lossless', 'true'); // is this useful?
|
||
|
//$this->imWebp->setImageAlphaChannel(imagick::ALPHACHANNEL_ACTIVATE); // is this useful?
|
||
|
//$this->imWebp->setBackgroundColor(new ImagickPixel('transparent')); // is this useful?
|
||
|
// save to file
|
||
|
$return = $this->imWebp->writeImage($webpFilename);
|
||
|
}
|
||
|
|
||
|
// release and return to event-object
|
||
|
$this->release();
|
||
|
|
||
|
return $return;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Process rotate of an image
|
||
|
*
|
||
|
* @param string $srcFilename
|
||
|
* @param string $dstFilename
|
||
|
* @param int $degrees Clockwise degrees, i.e. 90, 180, 270, -90, -180, -270
|
||
|
* @return bool
|
||
|
*
|
||
|
*/
|
||
|
protected function processRotate($srcFilename, $dstFilename, $degrees) {
|
||
|
|
||
|
$success = false;
|
||
|
$imagick = $this->getImagick($srcFilename);
|
||
|
if($imagick->rotateImage(new \ImagickPixel('#00000000'), $degrees)) {
|
||
|
$success = $this->processSave($imagick, $dstFilename);
|
||
|
}
|
||
|
|
||
|
return $success;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Process vertical or horizontal flip of an image
|
||
|
*
|
||
|
* @param string $srcFilename
|
||
|
* @param string $dstFilename
|
||
|
* @param string $flipType Specify vertical, horizontal or both
|
||
|
* @return bool
|
||
|
*
|
||
|
*/
|
||
|
protected function processFlip($srcFilename, $dstFilename, $flipType) {
|
||
|
|
||
|
$imagick = $this->getImagick($srcFilename);
|
||
|
|
||
|
if($flipType == 'vertical') {
|
||
|
$success = $imagick->flipImage();
|
||
|
} else if($flipType == 'horizontal') {
|
||
|
$success = $imagick->flopImage();
|
||
|
} else {
|
||
|
$success = $imagick->flipImage() && $imagick->flopImage();
|
||
|
}
|
||
|
|
||
|
if($success) $success = $this->processSave($imagick, $dstFilename);
|
||
|
|
||
|
return $success;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Reduce dimensions of image by half (using Imagick minifyImage method)
|
||
|
*
|
||
|
* @param string $dstFilename If different from filename specified by setFilename()
|
||
|
* @return bool
|
||
|
*
|
||
|
*/
|
||
|
public function reduceByHalf($dstFilename = '') {
|
||
|
$imagick = $this->getImagick($this->filename);
|
||
|
$success = $imagick->minifyImage();
|
||
|
if($success) $success = $this->processSave($imagick, $dstFilename);
|
||
|
return $success;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Convert image to greyscale
|
||
|
*
|
||
|
* @param string $dstFilename
|
||
|
* @return bool
|
||
|
*
|
||
|
*/
|
||
|
public function convertToGreyscale($dstFilename = '') {
|
||
|
$imagick = $this->getImagick($this->filename);
|
||
|
$success = $imagick->transformImageColorspace(\imagick::COLORSPACE_GRAY);
|
||
|
if($success) $success = $this->processSave($imagick, $dstFilename);
|
||
|
return $success;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Convert image to sepia
|
||
|
*
|
||
|
* @param string $dstFilename
|
||
|
* @param float|int $sepia Sepia threshold
|
||
|
* @return bool
|
||
|
*
|
||
|
*/
|
||
|
public function convertToSepia($dstFilename = '', $sepia = 55) {
|
||
|
$sepia += 35;
|
||
|
$imagick = $this->getImagick($this->filename);
|
||
|
$success = $imagick->sepiaToneImage((float) $sepia);
|
||
|
if($success) $success = $this->processSave($imagick, $dstFilename);
|
||
|
return $success;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Save action image to file
|
||
|
*
|
||
|
* @param \IMagick $imagick
|
||
|
* @param string $dstFilename
|
||
|
* @return bool
|
||
|
*
|
||
|
*/
|
||
|
protected function processSave(\IMagick $imagick, $dstFilename) {
|
||
|
if(empty($dstFilename)) $dstFilename = $this->filename;
|
||
|
$ext = strtolower(pathinfo($dstFilename, PATHINFO_EXTENSION));
|
||
|
if(in_array($ext, array('jpg', 'jpeg'))) {
|
||
|
if($this->interlace) {
|
||
|
$imagick->setInterlaceScheme(\Imagick::INTERLACE_JPEG);
|
||
|
}
|
||
|
}
|
||
|
$imagick->setImageCompressionQuality($this->quality);
|
||
|
$fp = fopen($dstFilename, 'wb');
|
||
|
if($fp === false) return false;
|
||
|
$success = $imagick->writeImageFile($fp);
|
||
|
fclose($fp);
|
||
|
return $success;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get instance of Imagick
|
||
|
*
|
||
|
* @param string $filename Optional filename to read
|
||
|
* @return \Imagick
|
||
|
* @throws WireException
|
||
|
*
|
||
|
*/
|
||
|
public function getImagick($filename = '') {
|
||
|
$imagick = new \Imagick();
|
||
|
if($filename) {
|
||
|
if(!$imagick->readImage($filename)) {
|
||
|
throw new WireException("Imagick unable to load file: " . basename($filename));
|
||
|
}
|
||
|
}
|
||
|
return $imagick;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Sharpen the image
|
||
|
*
|
||
|
* @param string $mode May be none|string|medium|soft
|
||
|
* @return bool
|
||
|
*
|
||
|
*/
|
||
|
protected function imSharpen($mode) {
|
||
|
if('none' == $mode) return true;
|
||
|
$mp = intval($this->finalHeight * $this->finalWidth);
|
||
|
if($mp > 1440000) {
|
||
|
switch($mode) {
|
||
|
case 'strong':
|
||
|
$m = array(0, 0.5, 4.6, 0.03);
|
||
|
break;
|
||
|
case 'medium':
|
||
|
$m = array(0, 0.5, 3.0, 0.04);
|
||
|
break;
|
||
|
case 'soft':
|
||
|
default:
|
||
|
$m = array(0, 0.5, 2.3, 0.07);
|
||
|
break;
|
||
|
}
|
||
|
} else if($mp > 480000) {
|
||
|
switch($mode) {
|
||
|
case 'strong':
|
||
|
$m = array(0, 0.5, 3.0, 0.04);
|
||
|
break;
|
||
|
case 'medium':
|
||
|
$m = array(0, 0.5, 2.3, 0.06);
|
||
|
break;
|
||
|
case 'soft':
|
||
|
default:
|
||
|
$m = array(0, 0.5, 1.8, 0.08);
|
||
|
break;
|
||
|
}
|
||
|
} else {
|
||
|
switch($mode) {
|
||
|
case 'strong':
|
||
|
$m = array(0, 0.5, 2.0, 0.06);
|
||
|
break;
|
||
|
case 'medium':
|
||
|
$m = array(0, 0.5, 1.7, 0.08);
|
||
|
break;
|
||
|
case 'soft':
|
||
|
default:
|
||
|
$m = array(0, 0.5, 1.2, 0.1);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
$this->im->unsharpMaskImage($m[0], $m[1], $m[2], $m[3]);
|
||
|
$this->modified = true;
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Module install
|
||
|
*
|
||
|
* @throws WireException
|
||
|
*
|
||
|
*/
|
||
|
public function ___install() {
|
||
|
if(!$this->supported('install')) {
|
||
|
throw new WireException("This module requires that you have PHP's IMagick (Image Magick) extension installed");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|