mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 14:05:37 +02:00
NEW: Enable multiple image manipulation back-ends on the Image class
This commit is contained in:
parent
d8c920285c
commit
d24b586830
@ -4,7 +4,7 @@
|
|||||||
* @package framework
|
* @package framework
|
||||||
* @subpackage filesystem
|
* @subpackage filesystem
|
||||||
*/
|
*/
|
||||||
class GD extends Object {
|
class GDBackend extends Object implements Image_Backend {
|
||||||
protected $gd, $width, $height;
|
protected $gd, $width, $height;
|
||||||
protected $quality;
|
protected $quality;
|
||||||
|
|
||||||
@ -28,29 +28,48 @@ class GD extends Object {
|
|||||||
// We use getimagesize instead of extension checking, because sometimes extensions are wrong.
|
// We use getimagesize instead of extension checking, because sometimes extensions are wrong.
|
||||||
list($width, $height, $type, $attr) = getimagesize($filename);
|
list($width, $height, $type, $attr) = getimagesize($filename);
|
||||||
switch($type) {
|
switch($type) {
|
||||||
case 1: if(function_exists('imagecreatefromgif')) $this->setGD(imagecreatefromgif($filename)); break;
|
case 1:
|
||||||
case 2: if(function_exists('imagecreatefromjpeg')) $this->setGD(imagecreatefromjpeg($filename)); break;
|
if(function_exists('imagecreatefromgif'))
|
||||||
case 3: if(function_exists('imagecreatefrompng')) {
|
$this->setImageResource(imagecreatefromgif($filename));
|
||||||
$img = imagecreatefrompng($filename);
|
break;
|
||||||
imagesavealpha($img, true); // save alphablending setting (important)
|
case 2:
|
||||||
$this->setGD($img);
|
if(function_exists('imagecreatefromjpeg'))
|
||||||
|
$this->setImageResource(imagecreatefromjpeg($filename));
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
if(function_exists('imagecreatefrompng')) {
|
||||||
|
$img = imagecreatefrompng($filename);
|
||||||
|
imagesavealpha($img, true); // save alphablending setting (important)
|
||||||
|
$this->setImageResource($img);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->quality = self::$default_quality;
|
$this->quality = self::$default_quality;
|
||||||
parent::__construct();
|
parent::__construct();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function setImageResource($resource) {
|
||||||
|
$this->gd = $resource;
|
||||||
|
$this->width = imagesx($resource);
|
||||||
|
$this->height = imagesy($resource);
|
||||||
|
}
|
||||||
|
|
||||||
public function setGD($gd) {
|
public function setGD($gd) {
|
||||||
$this->gd = $gd;
|
Deprecation::notice('3.1', 'Use GD::setImageResource instead',
|
||||||
$this->width = imagesx($gd);
|
Deprecation::SCOPE_CLASS);
|
||||||
$this->height = imagesy($gd);
|
return $this->setImageResource($gd);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getImageResource() {
|
||||||
|
return $this->gd;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getGD() {
|
public function getGD() {
|
||||||
return $this->gd;
|
Deprecation::notice('3.1', 'GD::getImageResource instead',
|
||||||
|
Deprecation::SCOPE_CLASS);
|
||||||
|
return $this->getImageResource();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -106,7 +125,7 @@ class GD extends Object {
|
|||||||
imagecopyresampled($newGD, $this->gd, 0,0, $srcX, $srcY, $width, $height, $srcWidth, $srcHeight);
|
imagecopyresampled($newGD, $this->gd, 0,0, $srcX, $srcY, $width, $height, $srcWidth, $srcHeight);
|
||||||
}
|
}
|
||||||
$output = clone $this;
|
$output = clone $this;
|
||||||
$output->setGD($newGD);
|
$output->setImageResource($newGD);
|
||||||
return $output;
|
return $output;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -121,10 +140,21 @@ class GD extends Object {
|
|||||||
return $gd;
|
return $gd;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function hasGD() {
|
/**
|
||||||
|
* hasImageResource
|
||||||
|
*
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public function hasImageResource() {
|
||||||
return $this->gd ? true : false;
|
return $this->gd ? true : false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function hasGD() {
|
||||||
|
Deprecation::notice('3.1', 'GD::hasImageResource instead',
|
||||||
|
Deprecation::SCOPE_CLASS);
|
||||||
|
return $this->hasImageResource();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resize an image, skewing it as necessary.
|
* Resize an image, skewing it as necessary.
|
||||||
@ -153,7 +183,7 @@ class GD extends Object {
|
|||||||
imagecopyresampled($newGD, $this->gd, 0,0, 0, 0, $width, $height, $this->width, $this->height);
|
imagecopyresampled($newGD, $this->gd, 0,0, 0, 0, $width, $height, $this->width, $this->height);
|
||||||
|
|
||||||
$output = clone $this;
|
$output = clone $this;
|
||||||
$output->setGD($newGD);
|
$output->setImageResource($newGD);
|
||||||
return $output;
|
return $output;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -175,7 +205,7 @@ class GD extends Object {
|
|||||||
$newGD = $this->rotatePixelByPixel($angle);
|
$newGD = $this->rotatePixelByPixel($angle);
|
||||||
}
|
}
|
||||||
$output = clone $this;
|
$output = clone $this;
|
||||||
$output->setGD($newGD);
|
$output->setImageResource($newGD);
|
||||||
return $output;
|
return $output;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -237,7 +267,7 @@ class GD extends Object {
|
|||||||
imagecopyresampled($newGD, $this->gd, 0, 0, $left, $top, $width, $height, $width, $height);
|
imagecopyresampled($newGD, $this->gd, 0, 0, $left, $top, $width, $height, $width, $height);
|
||||||
|
|
||||||
$output = clone $this;
|
$output = clone $this;
|
||||||
$output->setGD($newGD);
|
$output->setImageResource($newGD);
|
||||||
return $output;
|
return $output;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -356,7 +386,7 @@ class GD extends Object {
|
|||||||
$destWidth, $destHeight, $this->width, $this->height);
|
$destWidth, $destHeight, $this->width, $this->height);
|
||||||
}
|
}
|
||||||
$output = clone $this;
|
$output = clone $this;
|
||||||
$output->setGD($newGD);
|
$output->setImageResource($newGD);
|
||||||
return $output;
|
return $output;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -388,7 +418,7 @@ class GD extends Object {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$output = clone $this;
|
$output = clone $this;
|
||||||
$output->setGD($newGD);
|
$output->setImageResource($newGD);
|
||||||
return $output;
|
return $output;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -429,4 +459,4 @@ class GD extends Object {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
class_alias("GDBackend", "GD");
|
||||||
|
266
filesystem/ImagickBackend.php
Normal file
266
filesystem/ImagickBackend.php
Normal file
@ -0,0 +1,266 @@
|
|||||||
|
<?php
|
||||||
|
class ImagickBackend extends Imagick implements Image_Backend {
|
||||||
|
protected static $default_quality = 75;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* __construct
|
||||||
|
*
|
||||||
|
* @param string $filename = null
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function __construct($filename = null) {
|
||||||
|
if(is_string($filename)) {
|
||||||
|
parent::__construct($filename);
|
||||||
|
} else {
|
||||||
|
self::setImageCompressionQuality(self::$default_quality);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* writeTo
|
||||||
|
*
|
||||||
|
* @param string $path
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function writeTo($path) {
|
||||||
|
Filesystem::makeFolder(dirname($path));
|
||||||
|
if(is_dir(dirname($path)))
|
||||||
|
self::writeImage($path);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* set_default_quality
|
||||||
|
*
|
||||||
|
* @static
|
||||||
|
* @param int $quality
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public static function set_default_quality($quality) {
|
||||||
|
self::$default_quality = $quality;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* setQuality
|
||||||
|
*
|
||||||
|
* @param int $quality
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function setQuality($quality) {
|
||||||
|
self::setImageCompressionQuality($quality);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* setImageResource
|
||||||
|
*
|
||||||
|
* Set the backend-specific resource handling the manipulations. Replaces Image::setGD()
|
||||||
|
*
|
||||||
|
* @param mixed $resource
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function setImageResource($resource) {
|
||||||
|
trigger_error("Imagick::setImageResource is not supported", E_USER_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* getImageResource
|
||||||
|
*
|
||||||
|
* Get the backend-specific resource handling the manipulations. Replaces Image::getGD()
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function getImageResource() {
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* hasImageResource
|
||||||
|
*
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public function hasImageResource() {
|
||||||
|
return true; // $this is the resource, necessarily
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* resize
|
||||||
|
*
|
||||||
|
* @param int $width
|
||||||
|
* @param int $height
|
||||||
|
* @return Image_Backend
|
||||||
|
*/
|
||||||
|
public function resize($width, $height) {
|
||||||
|
if(!$this->valid()) return;
|
||||||
|
|
||||||
|
$width = round($width);
|
||||||
|
$height = round($height);
|
||||||
|
|
||||||
|
$geometry = $this->getImageGeometry();
|
||||||
|
|
||||||
|
// Check that a resize is actually necessary.
|
||||||
|
if ($width == $geometry["width"] && $height == $geometry["height"]) {
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!$width && !$height) user_error("No dimensions given", E_USER_ERROR);
|
||||||
|
if(!$width) user_error("Width not given", E_USER_ERROR);
|
||||||
|
if(!$height) user_error("Height not given", E_USER_ERROR);
|
||||||
|
|
||||||
|
$new = clone $this;
|
||||||
|
$new->resizeImage($width, $height, self::FILTER_LANCZOS, 1);
|
||||||
|
|
||||||
|
return $new;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* resizeRatio
|
||||||
|
*
|
||||||
|
* @param int $width
|
||||||
|
* @param int $height
|
||||||
|
* @return Image_Backend
|
||||||
|
*/
|
||||||
|
public function resizeRatio($maxWidth, $maxHeight, $useAsMinimum = false) {
|
||||||
|
if(!$this->valid()) return;
|
||||||
|
|
||||||
|
$geometry = $this->getImageGeometry();
|
||||||
|
|
||||||
|
$widthRatio = $maxWidth / $geometry["width"];
|
||||||
|
$heightRatio = $maxHeight / $geometry["height"];
|
||||||
|
|
||||||
|
if( $widthRatio < $heightRatio )
|
||||||
|
return $useAsMinimum ? $this->resizeByHeight( $maxHeight ) : $this->resizeByWidth( $maxWidth );
|
||||||
|
else
|
||||||
|
return $useAsMinimum ? $this->resizeByWidth( $maxWidth ) : $this->resizeByHeight( $maxHeight );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* resizeByWidth
|
||||||
|
*
|
||||||
|
* @param int $width
|
||||||
|
* @return Image_Backend
|
||||||
|
*/
|
||||||
|
public function resizeByWidth($width) {
|
||||||
|
if(!$this->valid()) return;
|
||||||
|
|
||||||
|
$geometry = $this->getImageGeometry();
|
||||||
|
|
||||||
|
$heightScale = $width / $geometry["width"];
|
||||||
|
return $this->resize( $width, $heightScale * $geometry["height"] );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* resizeByHeight
|
||||||
|
*
|
||||||
|
* @param int $height
|
||||||
|
* @return Image_Backend
|
||||||
|
*/
|
||||||
|
public function resizeByHeight($height) {
|
||||||
|
if(!$this->valid()) return;
|
||||||
|
|
||||||
|
$geometry = $this->getImageGeometry();
|
||||||
|
|
||||||
|
$scale = $height / $geometry["height"];
|
||||||
|
return $this->resize( $scale * $geometry["width"], $height );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* paddedResize
|
||||||
|
*
|
||||||
|
* @param int $width
|
||||||
|
* @param int $height
|
||||||
|
* @return Image_Backend
|
||||||
|
*/
|
||||||
|
public function paddedResize($width, $height, $backgroundColor = "FFFFFF") {
|
||||||
|
if(!$this->valid()) return;
|
||||||
|
|
||||||
|
$width = round($width);
|
||||||
|
$height = round($height);
|
||||||
|
$geometry = $this->getImageGeometry();
|
||||||
|
|
||||||
|
// Check that a resize is actually necessary.
|
||||||
|
if ($width == $geometry["width"] && $height == $geometry["height"]) {
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
$new = clone $this;
|
||||||
|
$new->setBackgroundColor($backgroundColor);
|
||||||
|
|
||||||
|
$destAR = $width / $height;
|
||||||
|
if ($geometry["width"] > 0 && $geometry["height"] > 0) {
|
||||||
|
// We can't divide by zero theres something wrong.
|
||||||
|
|
||||||
|
$srcAR = $geometry["width"] / $geometry["height"];
|
||||||
|
|
||||||
|
// Destination narrower than the source
|
||||||
|
if($destAR > $srcAR) {
|
||||||
|
$destY = 0;
|
||||||
|
$destHeight = $height;
|
||||||
|
|
||||||
|
$destWidth = round( $height * $srcAR );
|
||||||
|
$destX = round( ($width - $destWidth) / 2 );
|
||||||
|
|
||||||
|
// Destination shorter than the source
|
||||||
|
} else {
|
||||||
|
$destX = 0;
|
||||||
|
$destWidth = $width;
|
||||||
|
|
||||||
|
$destHeight = round( $width / $srcAR );
|
||||||
|
$destY = round( ($height - $destHeight) / 2 );
|
||||||
|
}
|
||||||
|
|
||||||
|
$new->extentImage($width, $height, $destX, $destY);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $new;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* croppedResize
|
||||||
|
*
|
||||||
|
* @param int $width
|
||||||
|
* @param int $height
|
||||||
|
* @return Image_Backend
|
||||||
|
*/
|
||||||
|
public function croppedResize($width, $height) {
|
||||||
|
if(!$this->valid()) return;
|
||||||
|
|
||||||
|
$width = round($width);
|
||||||
|
$height = round($height);
|
||||||
|
$geometry = $this->getImageGeometry();
|
||||||
|
|
||||||
|
// Check that a resize is actually necessary.
|
||||||
|
if ($width == $geometry["width"] && $height == $geometry["height"]) {
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
$new = clone $this;
|
||||||
|
$new->setBackgroundColor($backgroundColor);
|
||||||
|
|
||||||
|
$destAR = $width / $height;
|
||||||
|
if ($geometry["width"] > 0 && $geometry["height"] > 0) {
|
||||||
|
// We can't divide by zero theres something wrong.
|
||||||
|
|
||||||
|
$srcAR = $this->width / $this->height;
|
||||||
|
|
||||||
|
// Destination narrower than the source
|
||||||
|
if($destAR < $srcAR) {
|
||||||
|
$srcY = 0;
|
||||||
|
$srcHeight = $this->height;
|
||||||
|
|
||||||
|
$srcWidth = round( $this->height * $destAR );
|
||||||
|
$srcX = round( ($this->width - $srcWidth) / 2 );
|
||||||
|
|
||||||
|
// Destination shorter than the source
|
||||||
|
} else {
|
||||||
|
$srcX = 0;
|
||||||
|
$srcWidth = $this->width;
|
||||||
|
|
||||||
|
$srcHeight = round( $this->width / $destAR );
|
||||||
|
$srcY = round( ($this->height - $srcHeight) / 2 );
|
||||||
|
}
|
||||||
|
|
||||||
|
$new->extentImage($width, $height, $destX, $destY);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $new;
|
||||||
|
}
|
||||||
|
}
|
@ -12,6 +12,8 @@ class Image extends File {
|
|||||||
const ORIENTATION_PORTRAIT = 1;
|
const ORIENTATION_PORTRAIT = 1;
|
||||||
const ORIENTATION_LANDSCAPE = 2;
|
const ORIENTATION_LANDSCAPE = 2;
|
||||||
|
|
||||||
|
static $backend = "GDBackend";
|
||||||
|
|
||||||
static $casting = array(
|
static $casting = array(
|
||||||
'Tag' => 'HTMLText',
|
'Tag' => 'HTMLText',
|
||||||
);
|
);
|
||||||
@ -59,6 +61,14 @@ class Image extends File {
|
|||||||
*/
|
*/
|
||||||
public static $asset_preview_height = 200;
|
public static $asset_preview_height = 200;
|
||||||
|
|
||||||
|
public static function set_backend($backend) {
|
||||||
|
self::$backend = $backend;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function get_backend() {
|
||||||
|
return self::$backend;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set up template methods to access the transformations generated by 'generate' methods.
|
* Set up template methods to access the transformations generated by 'generate' methods.
|
||||||
*/
|
*/
|
||||||
@ -195,32 +205,32 @@ class Image extends File {
|
|||||||
return $this->getFormattedImage('SetRatioSize', $width, $height);
|
return $this->getFormattedImage('SetRatioSize', $width, $height);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function generateSetRatioSize(GD $gd, $width, $height) {
|
public function generateSetRatioSize(Image_Backend $backend, $width, $height) {
|
||||||
return $gd->resizeRatio($width, $height);
|
return $backend->resizeRatio($width, $height);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resize this Image by width, keeping aspect ratio. Use in templates with $SetWidth.
|
* Resize this Image by width, keeping aspect ratio. Use in templates with $SetWidth.
|
||||||
* @return GD
|
* @return Image_Backend
|
||||||
*/
|
*/
|
||||||
public function generateSetWidth(GD $gd, $width) {
|
public function generateSetWidth(Image_Backend $backend, $width) {
|
||||||
return $gd->resizeByWidth($width);
|
return $backend->resizeByWidth($width);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resize this Image by height, keeping aspect ratio. Use in templates with $SetHeight.
|
* Resize this Image by height, keeping aspect ratio. Use in templates with $SetHeight.
|
||||||
* @return GD
|
* @return Image_Backend
|
||||||
*/
|
*/
|
||||||
public function generateSetHeight(GD $gd, $height){
|
public function generateSetHeight(Image_Backend $backend, $height){
|
||||||
return $gd->resizeByHeight($height);
|
return $backend->resizeByHeight($height);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resize this Image by both width and height, using padded resize. Use in templates with $SetSize.
|
* Resize this Image by both width and height, using padded resize. Use in templates with $SetSize.
|
||||||
* @return GD
|
* @return Image_Backend
|
||||||
*/
|
*/
|
||||||
public function generateSetSize(GD $gd, $width, $height) {
|
public function generateSetSize(Image_Backend $backend, $width, $height) {
|
||||||
return $gd->paddedResize($width, $height);
|
return $backend->paddedResize($width, $height);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function CMSThumbnail() {
|
public function CMSThumbnail() {
|
||||||
@ -229,38 +239,38 @@ class Image extends File {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Resize this image for the CMS. Use in templates with $CMSThumbnail.
|
* Resize this image for the CMS. Use in templates with $CMSThumbnail.
|
||||||
* @return GD
|
* @return Image_Backend
|
||||||
*/
|
*/
|
||||||
public function generateCMSThumbnail(GD $gd) {
|
public function generateCMSThumbnail(Image_Backend $backend) {
|
||||||
return $gd->paddedResize($this->stat('cms_thumbnail_width'),$this->stat('cms_thumbnail_height'));
|
return $backend->paddedResize($this->stat('cms_thumbnail_width'),$this->stat('cms_thumbnail_height'));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resize this image for preview in the Asset section. Use in templates with $AssetLibraryPreview.
|
* Resize this image for preview in the Asset section. Use in templates with $AssetLibraryPreview.
|
||||||
* @return GD
|
* @return Image_Backend
|
||||||
*/
|
*/
|
||||||
public function generateAssetLibraryPreview(GD $gd) {
|
public function generateAssetLibraryPreview(Image_Backend $backend) {
|
||||||
return $gd->paddedResize($this->stat('asset_preview_width'),$this->stat('asset_preview_height'));
|
return $backend->paddedResize($this->stat('asset_preview_width'),$this->stat('asset_preview_height'));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resize this image for thumbnail in the Asset section. Use in templates with $AssetLibraryThumbnail.
|
* Resize this image for thumbnail in the Asset section. Use in templates with $AssetLibraryThumbnail.
|
||||||
* @return GD
|
* @return Image_Backend
|
||||||
*/
|
*/
|
||||||
public function generateAssetLibraryThumbnail(GD $gd) {
|
public function generateAssetLibraryThumbnail(Image_Backend $backend) {
|
||||||
return $gd->paddedResize($this->stat('asset_thumbnail_width'),$this->stat('asset_thumbnail_height'));
|
return $backend->paddedResize($this->stat('asset_thumbnail_width'),$this->stat('asset_thumbnail_height'));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resize this image for use as a thumbnail in a strip. Use in templates with $StripThumbnail.
|
* Resize this image for use as a thumbnail in a strip. Use in templates with $StripThumbnail.
|
||||||
* @return GD
|
* @return Image_Backend
|
||||||
*/
|
*/
|
||||||
public function generateStripThumbnail(GD $gd) {
|
public function generateStripThumbnail(Image_Backend $backend) {
|
||||||
return $gd->croppedResize($this->stat('strip_thumbnail_width'),$this->stat('strip_thumbnail_height'));
|
return $backend->croppedResize($this->stat('strip_thumbnail_width'),$this->stat('strip_thumbnail_height'));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function generatePaddedImage(GD $gd, $width, $height) {
|
public function generatePaddedImage(Image_Backend $backend, $width, $height) {
|
||||||
return $gd->paddedResize($width, $height);
|
return $backend->paddedResize($width, $height);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -312,16 +322,18 @@ class Image extends File {
|
|||||||
*/
|
*/
|
||||||
public function generateFormattedImage($format, $arg1 = null, $arg2 = null) {
|
public function generateFormattedImage($format, $arg1 = null, $arg2 = null) {
|
||||||
$cacheFile = $this->cacheFilename($format, $arg1, $arg2);
|
$cacheFile = $this->cacheFilename($format, $arg1, $arg2);
|
||||||
|
|
||||||
$gd = new GD(Director::baseFolder()."/" . $this->Filename);
|
|
||||||
|
|
||||||
if($gd->hasGD()){
|
$backend = Injector::inst()->createWithArgs(self::$backend, array(
|
||||||
|
Director::baseFolder()."/" . $this->Filename
|
||||||
|
));
|
||||||
|
|
||||||
|
if($backend->hasImageResource()) {
|
||||||
|
|
||||||
$generateFunc = "generate$format";
|
$generateFunc = "generate$format";
|
||||||
if($this->hasMethod($generateFunc)){
|
if($this->hasMethod($generateFunc)){
|
||||||
$gd = $this->$generateFunc($gd, $arg1, $arg2);
|
$backend = $this->$generateFunc($backend, $arg1, $arg2);
|
||||||
if($gd){
|
if($backend){
|
||||||
$gd->writeTo(Director::baseFolder()."/" . $cacheFile);
|
$backend->writeTo(Director::baseFolder()."/" . $cacheFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
@ -334,12 +346,12 @@ class Image extends File {
|
|||||||
* Generate a resized copy of this image with the given width & height.
|
* Generate a resized copy of this image with the given width & height.
|
||||||
* Use in templates with $ResizedImage.
|
* Use in templates with $ResizedImage.
|
||||||
*/
|
*/
|
||||||
public function generateResizedImage($gd, $width, $height) {
|
public function generateResizedImage(Image_Backend $backend, $width, $height) {
|
||||||
if(is_numeric($gd) || !$gd){
|
if(!$backend){
|
||||||
user_error("Image::generateFormattedImage - generateResizedImage is being called by legacy code"
|
user_error("Image::generateFormattedImage - generateResizedImage is being called by legacy code"
|
||||||
. " or gd is not set.",E_USER_WARNING);
|
. " or Image::\$backend is not set.",E_USER_WARNING);
|
||||||
}else{
|
}else{
|
||||||
return $gd->resize($width, $height);
|
return $backend->resize($width, $height);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -347,8 +359,8 @@ class Image extends File {
|
|||||||
* Generate a resized copy of this image with the given width & height, cropping to maintain aspect ratio.
|
* Generate a resized copy of this image with the given width & height, cropping to maintain aspect ratio.
|
||||||
* Use in templates with $CroppedImage
|
* Use in templates with $CroppedImage
|
||||||
*/
|
*/
|
||||||
public function generateCroppedImage($gd, $width, $height) {
|
public function generateCroppedImage(Image_Backend $backend, $width, $height) {
|
||||||
return $gd->croppedResize($width, $height);
|
return $backend->croppedResize($width, $height);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
122
model/Image_Backend.php
Normal file
122
model/Image_Backend.php
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Image_Backend
|
||||||
|
*
|
||||||
|
* A backend for manipulation of images via the Image class
|
||||||
|
*
|
||||||
|
* @package framework
|
||||||
|
* @subpackage filesystem
|
||||||
|
*/
|
||||||
|
interface Image_Backend {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* __construct
|
||||||
|
*
|
||||||
|
* @param string $filename = null
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function __construct($filename = null);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* writeTo
|
||||||
|
*
|
||||||
|
* @param string $path
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function writeTo($path);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* set_default_quality
|
||||||
|
*
|
||||||
|
* @static
|
||||||
|
* @param int $quality
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public static function set_default_quality($quality);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* setQuality
|
||||||
|
*
|
||||||
|
* @param int $quality
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function setQuality($quality);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* setImageResource
|
||||||
|
*
|
||||||
|
* Set the backend-specific resource handling the manipulations. Replaces Image::setGD()
|
||||||
|
*
|
||||||
|
* @param mixed $resource
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function setImageResource($resource);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* getImageResource
|
||||||
|
*
|
||||||
|
* Get the backend-specific resource handling the manipulations. Replaces Image::getGD()
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function getImageResource();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* hasImageResource
|
||||||
|
*
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public function hasImageResource();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* resize
|
||||||
|
*
|
||||||
|
* @param int $width
|
||||||
|
* @param int $height
|
||||||
|
* @return Image_Backend
|
||||||
|
*/
|
||||||
|
public function resize($width, $height);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* resizeRatio
|
||||||
|
*
|
||||||
|
* @param int $width
|
||||||
|
* @param int $height
|
||||||
|
* @return Image_Backend
|
||||||
|
*/
|
||||||
|
public function resizeRatio($maxWidth, $maxHeight, $useAsMinimum = false);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* resizeByWidth
|
||||||
|
*
|
||||||
|
* @param int $width
|
||||||
|
* @return Image_Backend
|
||||||
|
*/
|
||||||
|
public function resizeByWidth($width);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* resizeByHeight
|
||||||
|
*
|
||||||
|
* @param int $height
|
||||||
|
* @return Image_Backend
|
||||||
|
*/
|
||||||
|
public function resizeByHeight($height);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* paddedResize
|
||||||
|
*
|
||||||
|
* @param int $width
|
||||||
|
* @param int $height
|
||||||
|
* @return Image_Backend
|
||||||
|
*/
|
||||||
|
public function paddedResize($width, $height, $backgroundColor = "FFFFFF");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* croppedResize
|
||||||
|
*
|
||||||
|
* @param int $width
|
||||||
|
* @param int $height
|
||||||
|
* @return Image_Backend
|
||||||
|
*/
|
||||||
|
public function croppedResize($width, $height);
|
||||||
|
}
|
28
tests/model/GDImageTest.php
Normal file
28
tests/model/GDImageTest.php
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
class GDImageTest extends ImageTest {
|
||||||
|
public function setUp() {
|
||||||
|
if(!extension_loaded("gd")) {
|
||||||
|
$this->markTestSkipped("The GD extension is required");
|
||||||
|
$this->skipTest = true;
|
||||||
|
parent::setUp();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
parent::setUp();
|
||||||
|
|
||||||
|
Image::set_backend("GDBackend");
|
||||||
|
|
||||||
|
// Create a test files for each of the fixture references
|
||||||
|
$fileIDs = $this->allFixtureIDs('Image');
|
||||||
|
foreach($fileIDs as $fileID) {
|
||||||
|
$file = DataObject::get_by_id('Image', $fileID);
|
||||||
|
|
||||||
|
$image = imagecreatetruecolor(300,300);
|
||||||
|
|
||||||
|
imagepng($image, BASE_PATH."/{$file->Filename}");
|
||||||
|
imagedestroy($image);
|
||||||
|
|
||||||
|
$file->write();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -7,10 +7,20 @@
|
|||||||
class ImageTest extends SapphireTest {
|
class ImageTest extends SapphireTest {
|
||||||
|
|
||||||
static $fixture_file = 'ImageTest.yml';
|
static $fixture_file = 'ImageTest.yml';
|
||||||
|
|
||||||
|
protected $origBackend;
|
||||||
|
|
||||||
public function setUp() {
|
public function setUp() {
|
||||||
|
if(get_class($this) == "ImageTest")
|
||||||
|
$this->skipTest = true;
|
||||||
|
|
||||||
parent::setUp();
|
parent::setUp();
|
||||||
|
|
||||||
|
if($this->skipTest)
|
||||||
|
return;
|
||||||
|
|
||||||
|
$this->origBackend = Image::get_backend();
|
||||||
|
|
||||||
if(!file_exists(ASSETS_PATH)) mkdir(ASSETS_PATH);
|
if(!file_exists(ASSETS_PATH)) mkdir(ASSETS_PATH);
|
||||||
|
|
||||||
// Create a test folders for each of the fixture references
|
// Create a test folders for each of the fixture references
|
||||||
@ -21,18 +31,28 @@ class ImageTest extends SapphireTest {
|
|||||||
|
|
||||||
if(!file_exists(BASE_PATH."/$folder->Filename")) mkdir(BASE_PATH."/$folder->Filename");
|
if(!file_exists(BASE_PATH."/$folder->Filename")) mkdir(BASE_PATH."/$folder->Filename");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// Create a test files for each of the fixture references
|
|
||||||
|
public function tearDown() {
|
||||||
|
Image::set_backend($this->origBackend);
|
||||||
|
|
||||||
|
/* Remove the test files that we've created */
|
||||||
$fileIDs = $this->allFixtureIDs('Image');
|
$fileIDs = $this->allFixtureIDs('Image');
|
||||||
foreach($fileIDs as $fileID) {
|
foreach($fileIDs as $fileID) {
|
||||||
$file = DataObject::get_by_id('Image', $fileID);
|
$file = DataObject::get_by_id('Image', $fileID);
|
||||||
$image = imagecreatetruecolor(300,300);
|
if($file && file_exists(BASE_PATH."/$file->Filename")) unlink(BASE_PATH."/$file->Filename");
|
||||||
|
|
||||||
imagepng($image, BASE_PATH."/$file->Filename");
|
|
||||||
imagedestroy($image);
|
|
||||||
|
|
||||||
$file->write();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Remove the test folders that we've crated */
|
||||||
|
$folderIDs = $this->allFixtureIDs('Folder');
|
||||||
|
foreach($folderIDs as $folderID) {
|
||||||
|
$folder = DataObject::get_by_id('Folder', $folderID);
|
||||||
|
if($folder && file_exists(BASE_PATH."/$folder->Filename")) {
|
||||||
|
Filesystem::removeFolder(BASE_PATH."/$folder->Filename");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
parent::tearDown();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testGetTagWithTitle() {
|
public function testGetTagWithTitle() {
|
||||||
@ -61,26 +81,6 @@ class ImageTest extends SapphireTest {
|
|||||||
$this->assertEquals($expected, $actual);
|
$this->assertEquals($expected, $actual);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function tearDown() {
|
|
||||||
/* Remove the test files that we've created */
|
|
||||||
$fileIDs = $this->allFixtureIDs('Image');
|
|
||||||
foreach($fileIDs as $fileID) {
|
|
||||||
$file = DataObject::get_by_id('Image', $fileID);
|
|
||||||
if($file && file_exists(BASE_PATH."/$file->Filename")) unlink(BASE_PATH."/$file->Filename");
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Remove the test folders that we've crated */
|
|
||||||
$folderIDs = $this->allFixtureIDs('Folder');
|
|
||||||
foreach($folderIDs as $folderID) {
|
|
||||||
$folder = DataObject::get_by_id('Folder', $folderID);
|
|
||||||
if($folder && file_exists(BASE_PATH."/$folder->Filename")) {
|
|
||||||
Filesystem::removeFolder(BASE_PATH."/$folder->Filename");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
parent::tearDown();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testMultipleGenerateManipulationCalls() {
|
public function testMultipleGenerateManipulationCalls() {
|
||||||
$image = $this->objFromFixture('Image', 'imageWithoutTitle');
|
$image = $this->objFromFixture('Image', 'imageWithoutTitle');
|
||||||
|
|
||||||
|
29
tests/model/ImagickImageTest.php
Normal file
29
tests/model/ImagickImageTest.php
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
<?php
|
||||||
|
class ImagickImageTest extends ImageTest {
|
||||||
|
public function setUp() {
|
||||||
|
if(!extension_loaded("imagick")) {
|
||||||
|
$this->markTestSkipped("The Imagick extension is not available.");
|
||||||
|
$this->skipTest = true;
|
||||||
|
parent::setUp();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
parent::setUp();
|
||||||
|
|
||||||
|
Image::set_backend("ImagickBackend");
|
||||||
|
|
||||||
|
// Create a test files for each of the fixture references
|
||||||
|
$fileIDs = $this->allFixtureIDs('Image');
|
||||||
|
foreach($fileIDs as $fileID) {
|
||||||
|
$file = DataObject::get_by_id('Image', $fileID);
|
||||||
|
|
||||||
|
$image = new Imagick();
|
||||||
|
|
||||||
|
$image->newImage(300,300, new ImagickPixel("white"));
|
||||||
|
$image->setImageFormat("png");
|
||||||
|
$image->writeImage(BASE_PATH."/{$file->Filename}");
|
||||||
|
|
||||||
|
$file->write();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user