diff --git a/README.md b/README.md index 1e3862f0..24236d8a 100644 --- a/README.md +++ b/README.md @@ -54,6 +54,7 @@ Add the facade of this package to the `$aliases` array. * Image::crop - Crop the current image * Image::grab - Cut out a detail of the image in given ratio and resize to output size * Image::resizeCanvas - Resize image boundaries +* Image::mask - Apply given image as alpha mask on current image * Image::insert - Insert another image on top of the current image * Image::brightness - Changes brightness of current image (-100 = min brightness, 0 = no change, +100 = max brightness) * Image::contrast - Changes contrast of current image (-100 = min contrast, 0 = no change, +100 = max contrast) @@ -217,6 +218,9 @@ $img->circle('ae051f', 400, 300, 100, false); // draw a red line from point 10,10 to point 300,300 pixel $img->line('ae051f', 10, 10, 300, 300); +// Apply image as alpha mask on image +$img->mask('public/mask.png', true); + // rotate image 90° clockwise $img->rotate(90); diff --git a/public/mask1.png b/public/mask1.png new file mode 100644 index 00000000..5a6688be Binary files /dev/null and b/public/mask1.png differ diff --git a/public/mask2.png b/public/mask2.png new file mode 100644 index 00000000..82be6226 Binary files /dev/null and b/public/mask2.png differ diff --git a/src/Intervention/Image/Image.php b/src/Intervention/Image/Image.php index 9f1e672e..8a8f796d 100644 --- a/src/Intervention/Image/Image.php +++ b/src/Intervention/Image/Image.php @@ -678,6 +678,56 @@ class Image return $this; } + /** + * Apply given image as alpha mask on current image + * + * @param mixed $file + * @return Image + */ + public function mask($file, $mask_with_alpha = false) + { + // create new empty image + $maskedImage = new Image(null, $this->width, $this->height); + + // create mask + $mask = is_a($file, 'Intervention\Image\Image') ? $file : (new Image($file)); + + // resize mask to size of current image (if necessary) + if ($mask->width != $this->width || $mask->height != $this->height) { + $mask->resize($this->width, $this->height); + } + + // redraw old image pixel by pixel considering alpha map + for ($x=0; $x < $this->width; $x++) { + for ($y=0; $y < $this->height; $y++) { + + $color = $this->pickColor($x, $y, 'array'); + $alpha = $mask->pickColor($x, $y, 'array'); + + if ($mask_with_alpha) { + $alpha = $alpha['alpha']; // use alpha channel as mask + } else { + $alpha = 127 - floor($alpha['red'] / 2); // use red channel as mask + } + + // preserve alpha of original image... + if ($color['alpha'] > $alpha) { + $alpha = $color['alpha']; + } + + $pixelColor = $this->parseColor(array($color['red'], $color['green'], $color['blue'], $alpha)); + $maskedImage->pixel($pixelColor, $x, $y); + } + } + + // apply masked image to current instance + $this->resource = $maskedImage->resource; + $this->width = $maskedImage->width; + $this->height = $maskedImage->height; + + return $this; + } + /** * Rotate image with given angle * diff --git a/tests/ImageTest.php b/tests/ImageTest.php index b69b7e39..4ca86651 100644 --- a/tests/ImageTest.php +++ b/tests/ImageTest.php @@ -433,7 +433,67 @@ class ImageTest extends PHPUnit_Framework_Testcase $img = $this->getTestImage(); $img->insert('public/test.jpg', 10, 10); $this->assertInstanceOf('Intervention\Image\Image', $img); + } + public function testMaskImage() + { + // simple image mask + $img = Image::make('public/test.jpg'); + $img->resize(32, 32)->mask('public/mask1.png', false); + $this->assertInstanceOf('Intervention\Image\Image', $img); + $this->assertInternalType('int', $img->width); + $this->assertInternalType('int', $img->height); + $this->assertEquals($img->width, 32); + $this->assertEquals($img->height, 32); + $checkColor = $img->pickColor(16, 2, 'array'); + $this->assertEquals($checkColor['red'], 254); + $this->assertEquals($checkColor['green'], 230); + $this->assertEquals($checkColor['blue'], 186); + $this->assertEquals($checkColor['alpha'], 22); + $checkColor = $img->pickColor(31, 31, 'array'); + $this->assertEquals($checkColor['red'], 0); + $this->assertEquals($checkColor['green'], 0); + $this->assertEquals($checkColor['blue'], 0); + $this->assertEquals($checkColor['alpha'], 127); + + // use alpha channel as mask + $img = Image::make('public/test.jpg'); + $img->resize(32, 32)->mask('public/mask2.png', true); + $this->assertInstanceOf('Intervention\Image\Image', $img); + $this->assertInternalType('int', $img->width); + $this->assertInternalType('int', $img->height); + $this->assertEquals($img->width, 32); + $this->assertEquals($img->height, 32); + $checkColor = $img->pickColor(5, 5, 'array'); + $this->assertEquals($checkColor['red'], 0); + $this->assertEquals($checkColor['green'], 0); + $this->assertEquals($checkColor['blue'], 0); + $this->assertEquals($checkColor['alpha'], 127); + $checkColor = $img->pickColor(20, 15, 'array'); + $this->assertEquals($checkColor['red'], 254); + $this->assertEquals($checkColor['green'], 190); + $this->assertEquals($checkColor['blue'], 69); + $this->assertEquals($checkColor['alpha'], 0); + + // preserve existing alpha channel + $img = Image::make('public/circle.png'); + $img->resize(32, 32)->mask('public/mask2.png', true); + $this->assertInstanceOf('Intervention\Image\Image', $img); + $this->assertInternalType('int', $img->width); + $this->assertInternalType('int', $img->height); + $this->assertEquals($img->width, 32); + $this->assertEquals($img->height, 32); + $checkColor = $img->pickColor(5, 5, 'array'); + $this->assertEquals($checkColor['red'], 0); + $this->assertEquals($checkColor['green'], 0); + $this->assertEquals($checkColor['blue'], 0); + $this->assertEquals($checkColor['alpha'], 127); + $checkColor = $img->pickColor(15, 15, 'array'); + $this->assertEquals($checkColor['red'], 0); + $this->assertEquals($checkColor['green'], 0); + $this->assertEquals($checkColor['blue'], 0); + $this->assertEquals($checkColor['alpha'], 25); + } public function testPixelateImage()