diff --git a/docs/en/02_Developer_Guides/14_Files/02_Images.md b/docs/en/02_Developer_Guides/14_Files/02_Images.md index 724f3ad90..71b44d55b 100644 --- a/docs/en/02_Developer_Guides/14_Files/02_Images.md +++ b/docs/en/02_Developer_Guides/14_Files/02_Images.md @@ -63,6 +63,19 @@ Image methods are chainable. Example: :::ss + +### Padded Image Resize + +The Pad method allows you to resize an image with existing ratio and will +pad any surplus space. You can specify the color of the padding using a hex code such as FFFFFF or 000000. + +You can also specify a level of transparency to apply to the padding color in a fourth param. This will only effect +png images. + + :::php + $Image.Pad(80, 80, FFFFFF, 50) // white padding with 50% transparency + $Image.Pad(80, 80, FFFFFF, 100) // white padding with 100% transparency + $Image.Pad(80, 80, FFFFFF) // white padding with no transparency ### Manipulating images in PHP diff --git a/filesystem/GD.php b/filesystem/GD.php index 3c41ff4f2..7cd4c3aad 100644 --- a/filesystem/GD.php +++ b/filesystem/GD.php @@ -417,14 +417,20 @@ class GDBackend extends Object implements Image_Backend { return $useAsMinimum ? $this->resizeByWidth( $maxWidth ) : $this->resizeByHeight( $maxHeight ); } - public static function color_web2gd($image, $webColor) { + public static function color_web2gd($image, $webColor, $transparencyPercent = 0) { if(substr($webColor,0,1) == "#") $webColor = substr($webColor,1); $r = hexdec(substr($webColor,0,2)); $g = hexdec(substr($webColor,2,2)); $b = hexdec(substr($webColor,4,2)); + if($transparencyPercent) { + if($transparencyPercent > 100) { + $transparencyPercent = 100; + } + $a = 127 * bcdiv($transparencyPercent, 100, 2); + return imagecolorallocatealpha($image, $r, $g, $b, $a); + } return imagecolorallocate($image, $r, $g, $b); - } /** @@ -433,8 +439,11 @@ class GDBackend extends Object implements Image_Backend { * @param width * @param height * @param backgroundColour + * @param transparencyPercent */ - public function paddedResize($width, $height, $backgroundColor = "FFFFFF") { + public function paddedResize($width, $height, $backgroundColor = "FFFFFF", $transparencyPercent = 0) { + //keep the % within bounds of 0-100 + $transparencyPercent = min(100, max(0, $transparencyPercent)); if(!$this->gd) return; $width = round($width); $height = round($height); @@ -450,7 +459,7 @@ class GDBackend extends Object implements Image_Backend { imagealphablending($newGD, false); imagesavealpha($newGD, true); - $bg = GD::color_web2gd($newGD, $backgroundColor); + $bg = GD::color_web2gd($newGD, $backgroundColor, $transparencyPercent); imagefilledrectangle($newGD, 0, 0, $width, $height, $bg); $destAR = $width / $height; diff --git a/filesystem/ImagickBackend.php b/filesystem/ImagickBackend.php index b86e1ac5b..add1b3385 100644 --- a/filesystem/ImagickBackend.php +++ b/filesystem/ImagickBackend.php @@ -194,11 +194,19 @@ class ImagickBackend extends Imagick implements Image_Backend { * * @param int $width * @param int $height + * @param int $transparencyPercent * @return Image_Backend */ - public function paddedResize($width, $height, $backgroundColor = "FFFFFF") { + public function paddedResize($width, $height, $backgroundColor = "FFFFFF", $transparencyPercent = 0) { + //keep the % within bounds of 0-100 + $transparencyPercent = min(100, max(0, $transparencyPercent)); $new = $this->resizeRatio($width, $height); - $new->setImageBackgroundColor("#".$backgroundColor); + if($transparencyPercent) { + $alphaHex = $this->calculateAlphaHex($transparencyPercent); + $new->setImageBackgroundColor("#{$backgroundColor}{$alphaHex}"); + } else { + $new->setImageBackgroundColor("#{$backgroundColor}"); + } $w = $new->getImageWidth(); $h = $new->getImageHeight(); $new->extentImage($width,$height,($w-$width)/2,($h-$height)/2); @@ -206,6 +214,25 @@ class ImagickBackend extends Imagick implements Image_Backend { return $new; } + /** + * Convert a percentage (or 'true') to a two char hex code to signifiy the level of an alpha channel + * + * @param $percent + * @return string + */ + public function calculateAlphaHex($percent) { + if($percent > 100) { + $percent = 100; + } + // unlike GD, this uses 255 instead of 127, and is reversed. Lower = more transparent + $alphaHex = dechex(255 - floor(255 * bcdiv($percent, 100, 2))); + if(strlen($alphaHex) == 1) { + $alphaHex = '0' .$alphaHex; + } + return $alphaHex; + } + + /** * croppedResize * diff --git a/model/Image.php b/model/Image.php index 1003e4f87..f64145640 100644 --- a/model/Image.php +++ b/model/Image.php @@ -339,12 +339,13 @@ class Image extends File implements Flushable { * * @param integer $width The width to size to * @param integer $height The height to size to + * @param integer $transparencyPercent Level of transparency * @return Image|null */ - public function Pad($width, $height, $backgroundColor='FFFFFF') { + public function Pad($width, $height, $backgroundColor='FFFFFF', $transparencyPercent = 0) { return $this->isSize($width, $height) && !Config::inst()->get('Image', 'force_resample') ? $this - : $this->getFormattedImage('Pad', $width, $height, $backgroundColor); + : $this->getFormattedImage('Pad', $width, $height, $backgroundColor, $transparencyPercent); } /** @@ -654,12 +655,13 @@ class Image extends File implements Flushable { * * @param integer $width The width to size to * @param integer $height The height to size to + * @param integer $transparencyPercent Level of transparency * @return Image * @deprecated 4.0 Use Pad instead */ - public function PaddedImage($width, $height, $backgroundColor='FFFFFF') { + public function PaddedImage($width, $height, $backgroundColor='FFFFFF', $transparencyPercent = 0) { Deprecation::notice('4.0', 'Use Pad instead'); - return $this->Pad($width, $height, $backgroundColor); + return $this->Pad($width, $height, $backgroundColor, $transparencyPercent); } /** @@ -668,12 +670,13 @@ class Image extends File implements Flushable { * @param Image_Backend $backend * @param integer $width The width to size to * @param integer $height The height to size to + * @param integer $transparencyPercent Level of transparency * @return Image_Backend * @deprecated 4.0 Generate methods are no longer applicable */ - public function generatePaddedImage(Image_Backend $backend, $width, $height, $backgroundColor='FFFFFF') { + public function generatePaddedImage(Image_Backend $backend, $width, $height, $backgroundColor = 'FFFFFF', $transparencyPercent = 0) { Deprecation::notice('4.0', 'Generate methods are no longer applicable'); - return $backend->paddedResize($width, $height, $backgroundColor); + return $backend->paddedResize($width, $height, $backgroundColor, $transparencyPercent); } /** @@ -1045,7 +1048,7 @@ class Image extends File implements Flushable { protected function onBeforeDelete() { $backend = Injector::inst()->createWithArgs(self::config()->backend, array( - Director::baseFolder()."/" . $this->Filename + Director::baseFolder()."/" . $this->Filename )); $backend->onBeforeDelete($this); diff --git a/tests/model/ImageTest.php b/tests/model/ImageTest.php index fedc13f5d..e90a22b26 100644 --- a/tests/model/ImageTest.php +++ b/tests/model/ImageTest.php @@ -282,10 +282,10 @@ class ImageTest extends SapphireTest { public function testCacheFilename() { $image = $this->objFromFixture('Image', 'imageWithoutTitle'); - $imageFirst = $image->Pad(200,200,'CCCCCC'); + $imageFirst = $image->Pad(200,200,'CCCCCC', 0); $imageFilename = $imageFirst->getFullPath(); // Encoding of the arguments is duplicated from cacheFilename - $neededPart = 'Pad' . Convert::base64url_encode(array(200,200,'CCCCCC')); + $neededPart = 'Pad' . Convert::base64url_encode(array(200,200,'CCCCCC', 0)); $this->assertContains($neededPart, $imageFilename, 'Filename for cached image is correctly generated'); } @@ -308,7 +308,12 @@ class ImageTest extends SapphireTest { $imageThird = $imageSecond->Pad(600,600,'0F0F0F'); // Encoding of the arguments is duplicated from cacheFilename - $argumentString = Convert::base64url_encode(array(600,600,'0F0F0F')); + $argumentString = Convert::base64url_encode(array( + 600, + 600, + '0F0F0F', + 0 + )); $this->assertNotNull($imageThird); $this->assertContains($argumentString, $imageThird->getFullPath(), 'Image contains background color for padded resizement');