diff --git a/public/gradient.png b/public/gradient.png new file mode 100644 index 00000000..f92ba4c7 Binary files /dev/null and b/public/gradient.png differ diff --git a/src/Intervention/Image/Exception/TrimToleranceOutOfBoundsException.php b/src/Intervention/Image/Exception/TrimToleranceOutOfBoundsException.php new file mode 100644 index 00000000..fa202ece --- /dev/null +++ b/src/Intervention/Image/Exception/TrimToleranceOutOfBoundsException.php @@ -0,0 +1,8 @@ + 100) { + throw new Exception\TrimToleranceOutOfBoundsException( + 'Tolerance level must be between 0 and 100' + ); + } else { + $color_tolerance = round($tolerance * 2.55); + $alpha_tolerance = round($tolerance * 1.27); + } + // pick base color $color = imagecolorsforindex($this->resource, imagecolorat($this->resource, $base_x, $base_y)); + // compare colors + $matches = function($c1, $c2) use ($checkTransparency, $color_tolerance, $alpha_tolerance) { + + if ($checkTransparency == true) { + + $alpha_delta = abs($c1['alpha'] - $c2['alpha']); + return($alpha_delta <= $alpha_tolerance); + + } else { + + $color_delta = round(( + abs($c1['red'] - $c2['red']) + + abs($c1['green'] - $c2['green']) + + abs($c1['blue'] - $c2['blue'])) / 3 + ); + + $alpha_delta = abs($c1['alpha'] - $c2['alpha']); + return($color_delta <= $color_tolerance && $alpha_delta <= $alpha_tolerance); + } + + }; + $top_x = 0; $top_y = 0; $bottom_x = $this->width; $bottom_y = $this->height; + // search upper part of image for colors to trim away if (in_array('top', $away)) { - for ($y=0; $y < $this->height; $y++) { + for ($y=0; $y < ceil($this->height/2); $y++) { for ($x=0; $x < $this->width; $x++) { $checkColor = imagecolorsforindex($this->resource, imagecolorat($this->resource, $x, $y)); - if (($checkTransparency == false && $checkColor != $color) or ($checkTransparency == true && $checkColor['alpha'] != 127)) { + if ( ! $matches($color, $checkColor)) { $top_y = $y; break 2; } @@ -788,11 +824,12 @@ class Image } } + // search left part of image for colors to trim away if (in_array('left', $away)) { - for ($x=0; $x < $this->width; $x++) { + for ($x=0; $x < ceil($this->width/2); $x++) { for ($y=$top_y; $y < $this->height; $y++) { $checkColor = imagecolorsforindex($this->resource, imagecolorat($this->resource, $x, $y)); - if (($checkTransparency == false && $checkColor != $color) or ($checkTransparency == true && $checkColor['alpha'] != 127)) { + if ( ! $matches($color, $checkColor)) { $top_x = $x; break 2; } @@ -800,11 +837,12 @@ class Image } } + // search lower part of image for colors to trim away if (in_array('bottom', $away)) { - for ($y=($this->height-1); $y >= 0; $y--) { + for ($y=($this->height-1); $y >= floor($this->height/2); $y--) { for ($x=$top_x; $x < $this->width; $x++) { $checkColor = imagecolorsforindex($this->resource, imagecolorat($this->resource, $x, $y)); - if (($checkTransparency == false && $checkColor != $color) or ($checkTransparency == true && $checkColor['alpha'] != 127)) { + if ( ! $matches($color, $checkColor)) { $bottom_y = $y+1; break 2; } @@ -812,11 +850,12 @@ class Image } } + // search right part of image for colors to trim away if (in_array('right', $away)) { - for ($x=($this->width-1); $x >= 0; $x--) { + for ($x=($this->width-1); $x >= floor($this->width/2); $x--) { for ($y=$top_y; $y < $bottom_y; $y++) { $checkColor = imagecolorsforindex($this->resource, imagecolorat($this->resource, $x, $y)); - if (($checkTransparency == false && $checkColor != $color) or ($checkTransparency == true && $checkColor['alpha'] != 127)) { + if ( ! $matches($color, $checkColor)) { $bottom_x = $x+1; break 2; } @@ -824,6 +863,7 @@ class Image } } + // trim parts of image $this->modify(0, 0, $top_x, $top_y, ($bottom_x-$top_x), ($bottom_y-$top_y), ($bottom_x-$top_x), ($bottom_y-$top_y)); return $this; @@ -1579,6 +1619,7 @@ class Image case 'integer': # in gd2 library color already is int... break; + default: case 'array': $color = imagecolorsforindex($this->resource, $color); diff --git a/tests/ImageTest.php b/tests/ImageTest.php index cb8bfcad..ed8824bb 100644 --- a/tests/ImageTest.php +++ b/tests/ImageTest.php @@ -1937,6 +1937,198 @@ class ImageTest extends PHPUnit_Framework_Testcase $this->assertEquals($img->width, 1); $this->assertEquals($img->height, 1); $this->assertEquals('#000000', $img->pickColor(0, 0, 'hex')); + + // try to trim non-transparent image with transparency + $img = Image::make('public/gradient.png'); + $img->trim('transparent', null); + $this->assertEquals($img->width, 50); + $this->assertEquals($img->height, 50); + } + + public function testTrimWithTolerance() + { + // prepare test image + $canvas = Image::canvas(1, 1, '000000'); + $canvas->resizeCanvas(5, 5, 'center', false, '808080'); + $canvas->resizeCanvas(11, 11, 'center', false, 'ffffff'); + $this->assertEquals($canvas->width, 11); + $this->assertEquals($canvas->height, 11); + $this->assertEquals('#000000', $canvas->pickColor(5, 5, 'hex')); + $this->assertEquals('#808080', $canvas->pickColor(3, 3, 'hex')); + $this->assertEquals('#ffffff', $canvas->pickColor(0, 0, 'hex')); + + $img = clone $canvas; + $img->trim(); // trim without tolerance (should trim away ffffff) + $this->assertEquals($img->width, 5); + $this->assertEquals($img->height, 5); + $this->assertEquals('#000000', $img->pickColor(2, 2, 'hex')); + $this->assertEquals('#808080', $img->pickColor(0, 0, 'hex')); + + $img = clone $canvas; + $img->trim(null, null, 30); // trim with 40 tolerance (should not touch 808080) + $this->assertEquals($img->width, 5); + $this->assertEquals($img->height, 5); + $this->assertEquals('#000000', $img->pickColor(2, 2, 'hex')); + $this->assertEquals('#808080', $img->pickColor(0, 0, 'hex')); + + $img = clone $canvas; + $img->trim(null, null, 50); // trim with 50 tolerance (should only leave 000000) + $this->assertEquals($img->width, 1); + $this->assertEquals($img->height, 1); + $this->assertEquals('#000000', $img->pickColor(0, 0, 'hex')); + + $img = clone $canvas; + $img->trim(null, null, 100); // trim with 100 tolerance (should leave image as is) + $this->assertEquals($img->width, 11); + $this->assertEquals($img->height, 11); + $this->assertEquals('#000000', $canvas->pickColor(5, 5, 'hex')); + $this->assertEquals('#808080', $canvas->pickColor(3, 3, 'hex')); + $this->assertEquals('#ffffff', $canvas->pickColor(0, 0, 'hex')); + + // prepare test image + $canvas = Image::canvas(1, 1, '000000'); + $canvas->resizeCanvas(5, 5, 'center', false, '804040'); + $canvas->resizeCanvas(11, 11, 'center', false, 'ffffff'); + $this->assertEquals($canvas->width, 11); + $this->assertEquals($canvas->height, 11); + $this->assertEquals('#000000', $canvas->pickColor(5, 5, 'hex')); + $this->assertEquals('#804040', $canvas->pickColor(3, 3, 'hex')); + $this->assertEquals('#ffffff', $canvas->pickColor(0, 0, 'hex')); + + $img = clone $canvas; + $img->trim(); // trim without tolerance (should trim away ffffff) + $this->assertEquals($img->width, 5); + $this->assertEquals($img->height, 5); + $this->assertEquals('#000000', $img->pickColor(2, 2, 'hex')); + $this->assertEquals('#804040', $img->pickColor(0, 0, 'hex')); + + $img = clone $canvas; + $img->trim(null, null, 30); // trim with 40 tolerance (should not touch 804040) + $this->assertEquals($img->width, 5); + $this->assertEquals($img->height, 5); + $this->assertEquals('#000000', $img->pickColor(2, 2, 'hex')); + $this->assertEquals('#804040', $img->pickColor(0, 0, 'hex')); + + $img = clone $canvas; + $img->trim(null, null, 50); // trim with 50 tolerance (should not touch 804040) + $this->assertEquals($img->width, 5); + $this->assertEquals($img->height, 5); + $this->assertEquals('#000000', $img->pickColor(2, 2, 'hex')); + $this->assertEquals('#804040', $img->pickColor(0, 0, 'hex')); + + $img = clone $canvas; + $img->trim(null, null, 70); // trim with 70 tolerance (should only leave 000000) + $this->assertEquals($img->width, 1); + $this->assertEquals($img->height, 1); + $this->assertEquals('#000000', $img->pickColor(0, 0, 'hex')); + + $img = clone $canvas; + $img->trim(null, null, 100); // trim with 100 tolerance (should leave image as is) + $this->assertEquals($img->width, 11); + $this->assertEquals($img->height, 11); + $this->assertEquals('#000000', $canvas->pickColor(5, 5, 'hex')); + $this->assertEquals('#804040', $canvas->pickColor(3, 3, 'hex')); + $this->assertEquals('#ffffff', $canvas->pickColor(0, 0, 'hex')); + + // prepare test image + $canvas = Image::canvas(1, 1, '000000'); + $canvas->resizeCanvas(5, 5, 'center', false, array(255, 255, 255, 0.5)); + $canvas->resizeCanvas(11, 11, 'center', false, array(0, 0, 0, 0)); + $this->assertEquals($canvas->width, 11); + $this->assertEquals($canvas->height, 11); + $this->assertEquals('rgba(0, 0, 0, 0.00)', $canvas->pickColor(0, 0, 'rgba')); + $this->assertEquals('rgba(255, 255, 255, 0.50)', $canvas->pickColor(3, 3, 'rgba')); + $this->assertEquals('rgba(0, 0, 0, 1.00)', $canvas->pickColor(5, 5, 'rgba')); + + $img = clone $canvas; + $img->trim('transparent', null); + $this->assertEquals($img->width, 5); + $this->assertEquals($img->height, 5); + $this->assertEquals('rgba(255, 255, 255, 0.50)', $img->pickColor(0, 0, 'rgba')); + $this->assertEquals('rgba(0, 0, 0, 1.00)', $img->pickColor(2, 2, 'rgba')); + + $img = clone $canvas; + $img->trim('transparent', null, 40); + $this->assertEquals($img->width, 5); + $this->assertEquals($img->height, 5); + $this->assertEquals('rgba(255, 255, 255, 0.50)', $img->pickColor(0, 0, 'rgba')); + $this->assertEquals('rgba(0, 0, 0, 1.00)', $img->pickColor(2, 2, 'rgba')); + + $img = clone $canvas; + $img->trim('transparent', null, 50); + $this->assertEquals($img->width, 1); + $this->assertEquals($img->height, 1); + $this->assertEquals('rgba(0, 0, 0, 1.00)', $img->pickColor(0, 0, 'rgba')); + + $img = clone $canvas; + $img->trim('transparent', null, 100); + $this->assertEquals($canvas->width, 11); + $this->assertEquals($canvas->height, 11); + $this->assertEquals('rgba(0, 0, 0, 0.00)', $canvas->pickColor(0, 0, 'rgba')); + $this->assertEquals('rgba(255, 255, 255, 0.50)', $canvas->pickColor(3, 3, 'rgba')); + $this->assertEquals('rgba(0, 0, 0, 1.00)', $canvas->pickColor(5, 5, 'rgba')); + + // trim gradient + $canvas = Image::make('public/gradient.png'); + + $img = clone $canvas; + $img->trim(); + $this->assertEquals($img->width, 46); + $this->assertEquals($img->height, 46); + + $img = clone $canvas; + $img->trim(null, null, 10); + $this->assertEquals($img->width, 38); + $this->assertEquals($img->height, 38); + + $img = clone $canvas; + $img->trim(null, null, 20); + $this->assertEquals($img->width, 34); + $this->assertEquals($img->height, 34); + + $img = clone $canvas; + $img->trim(null, null, 30); + $this->assertEquals($img->width, 30); + $this->assertEquals($img->height, 30); + + $img = clone $canvas; + $img->trim(null, null, 40); + $this->assertEquals($img->width, 26); + $this->assertEquals($img->height, 26); + + $img = clone $canvas; + $img->trim(null, null, 50); + $this->assertEquals($img->width, 22); + $this->assertEquals($img->height, 22); + + $img = clone $canvas; + $img->trim(null, null, 60); + $this->assertEquals($img->width, 20); + $this->assertEquals($img->height, 20); + + $img = clone $canvas; + $img->trim(null, null, 70); + $this->assertEquals($img->width, 16); + $this->assertEquals($img->height, 16); + + $img = clone $canvas; + $img->trim(null, null, 80); + $this->assertEquals($img->width, 12); + $this->assertEquals($img->height, 12); + + $img = clone $canvas; + $img->trim(null, null, 90); + $this->assertEquals($img->width, 8); + $this->assertEquals($img->height, 8); + } + + /** + * @expectedException Intervention\Image\Exception\TrimToleranceOutOfBoundsException + */ + public function testTrimToleranceOutOfBounds() + { + $img = new Image; + $img->trim(null, null, 200); } public function testEncoded()