Tutorials - Big bits of code to help you do more
Improving image quality by preventing correctly sized images being resized
Tweet7 January 2011 | | | Supports v2.4, v2.3
Sometimes you'll have to create websites for clients that are very demanding when it comes to image quality, say graphic designers or photographers. SilverStripe can be a showstopper here, since images are always being re-saved when you're using something like $SetHeight in your template. This can reduce image quality quite a lot, especially when using JPEG images.
A first thing to do is to crank up the GD default quality, by putting this in your _config.php file:
GD::set_default_quality(95);
But sometimes a client might prefer to prepare his images by himself. Then you really don't want SilverStripe to mess with these images. A simple solution would be to just use $Image in your template. But what if somebody uploads a huge image by mistake?
In an ideal world, SilverStripe would leave images that already match the target size untouched and only resize the ones that actually have to be resized. Luckily, this can be achieved by subclassing the Image class. Here's the class I use in all my projects:
<?php
/**
* Prevents creation of resized images if the uploaded file already
* fits the requested dimensions
*/
class BetterImage extends Image
{
public function SetWidth($width) {
if($width == $this->getWidth()){
return $this;
}
return parent::SetWidth($width);
}
public function SetHeight($height) {
if($height == $this->getHeight()){
return $this;
}
return parent::SetHeight($height);
}
public function SetSize($width, $height) {
if($width == $this->getWidth() && $height == $this->getHeight()){
return $this;
}
return parent::SetSize($width, $height);
}
public function SetRatioSize($width, $height) {
if($width == $this->getWidth() && $height == $this->getHeight()){
return $this;
}
return parent::SetRatioSize($width, $height);
}
public function getFormattedImage($format, $arg1 = null, $arg2 = null) {
if($this->ID && $this->Filename && Director::fileExists($this->Filename)) {
$size = getimagesize(Director::baseFolder() . '/' . $this->getField('Filename'));
$preserveOriginal = false;
switch(strtolower($format)){
case 'croppedimage':
$preserveOriginal = ($arg1 == $size[0] && $arg2 == $size[1]);
break;
}
if($preserveOriginal){
return $this;
} else {
return parent::getFormattedImage($format, $arg1, $arg2);
}
}
}
}
This class overrides the SetWidth, SetHeight, SetSize, SetRatioSize and CroppedImage methods and always uses the original image if it already fits the requested dimensions.
Making use of the class is simple. Instead of having:
public static $has_one = array( 'MyImage' => 'Image' );
in your Pages/DataObjects you would now write:
public static $has_one = array( 'MyImage' => 'BetterImage' );
and use it like a regular image. Migrating an existing project to use the BetterImage class is simple as well. Just put the class (BetterImage.php) somewhere in cour mysite/code folder and replace all relations from Image to BetterImage (as shown above). Then run dev/build and you're set.
Feel free to merge in methods for rotating and grayscale conversion from Aram's tutorial, to get the "Uber-Image-Class".
13 Comments
RSS feed for comments on this page RSS feed for all comments
swaiba
07/01/2011 12:35pm (1 year ago)
Hi, does this overide the 600x600 limitation in... cms\code\ThumbnailStripField.php?
This is one of a handful of hacks I perform on the core code each time...
Roman Schmid
07/01/2011 12:41pm (1 year ago)
@swaiba: I don't think so.
swaiba
07/01/2011 12:49pm (1 year ago)
shame :( I have no idea why but this has affected a couple of sites where a graphcis guy is complaining "why are my images maxed at 600" I'll get round to a patch one day
Matty Balaam
08/01/2011 1:46pm (1 year ago)
Brilliant! I'm mainly a Graphic Designer and have been frustrated by the re-compressing of images since I started using Silverstripe, I'll definitely be putting this onto my site ASAP.
Brice Burgess
11/01/2011 6:14pm (1 year ago)
I agree with "In an ideal world, SilverStripe would leave images that already match the target size untouched and only resize the ones that actually have to be resized."
What about patching the base Image class with this behavior and submitting to open.silverstripe.org?
Roman Schmid
11/01/2011 8:20pm (1 year ago)
@Brice You're right. This could just as well be in the core. Will prepare a patch in the near future.
Matt Clegg
12/01/2011 12:22pm (1 year ago)
Hi Brice / Roman,
There is already a patch for SilverStripe GD
http://open.silverstripe.org/attachment/ticket/5416
This patch pads images in both directions as opposed to only padding one and stretching the other.
Bodkin
17/01/2011 1:16pm (1 year ago)
Agree, this should be in the Sapphire core base Image class ASAP for sure, current GD treatment gives poor results for such things as portfolios. Excellent tip, thanks Roman, would you have a quick howto on augmenting this in Arrons ImageGallery mod perhaps? I suspect this is where this mod would be extremely useful. I did try, unsuccessfully so any pointers would be great.
William Melbourne
17/01/2011 9:04pm (1 year ago)
would this effect the image tool crashing when big images are uploaded. I have had a problem / opportunity with quite a few of my clients where they tried to upload a big image from their camera. everyone has 10MP cameras these days whether needed or not and but not many of them know how to compress images or want to, so when they use the cms to upload the images it crashes. any thoughts?
Roman Schmid
25/01/2011 4:42pm (1 year ago)
@William No this won't help with memory problems, since you'll hit the memory limit as soon as the image is being read into a GD image (for example to create the thumbnail in the CMS). That would actually require another patch that would check image-size against available memory before doing any image processing whatsoever.
Ryan M
22/03/2011 1:09am (1 year ago)
I'd like to add that this function doesn't do anything to prevent smaller images from being enlarged. So the height & width operators should be changed from == to >=
Mom Bunheng
05/04/2011 4:42am (1 year ago)
I have tested it work great! Thanks for sharing.
Darren-Lee
09/04/2011 1:49pm (1 year ago)
Thanks...this snippet came in handy to satisfy the whims of a graphics designer who was complaining about the Silverstripe CMS affecting the quality of her images that she painstakingly prepared, optimised and trimmed to the correct size beforehand. Thanks for sharing.
Post a comment ...
You cannot post comments until you have logged in. Login Here.