Search code examples
phpimagemagickimagickalpha-transparency

Imagick - create two different levels of transparency


I have an image I am using for testing purposes. Here it is:

test image

I have been able to do everything I've wanted with PHP Imagick, except create a png with multiple levels of transparency. The problem is probably my lack of understanding of how transparency is stored in a png.

Let's say I am trying to make a transparent triangle area, but then create a polygon area with a second level of transparency, an area that is semi-transparent.

I've tried using setOpacity() before and after the modifications, with no luck.

I've also created two separate ImagickDraw() objects, and giving them a different fillColor with no luck. Here is an example of my last attempt:

$img = new Imagick('rec.png');
$height = $img->getImageHeight();
$width = $img->getImageWidth();

//Create a new transparent image of the same size
$mask = new Imagick();
$mask->newImage($width, $height, new ImagickPixel('none'));
$mask->setImageFormat('png');

//Draw onto the new image the areas you want to be transparent in the original
$draw = new ImagickDraw();
$draw->setFillColor(new ImagickPixel('#999999')); 
//$draw->rectangle( 10,10,100,100 );
$points = [
    ['x' => 400, 'y' => 0],
    ['x' => 400, 'y' => 200], 
    ['x' => 700, 'y' => 0], 
    ['x' => 400, 'y' => 0],
];
$draw->polygon($points);

$tdraw = new ImagickDraw();
$tdraw->setFillColor('rgb(90, 90, 90)');
$npoints = [
    ['x' => 0, 'y' => 0],
    ['x' => 0, 'y' => 200], 
    ['x' => 400, 'y' => 200], 
    ['x' => 700, 'y' => 0],
    ['x' => 0, 'y' => 0],
];
$tdraw->polygon($npoints);

$mask->drawImage( $draw );
$mask->drawImage( $tdraw );
$mask->negateImage(true, Imagick::CHANNEL_ALPHA);

// Composite the images using Imagick::COMPOSITE_DSTOUT
$img->compositeImage($mask, Imagick::COMPOSITE_COPYOPACITY, 0, 0); 

Anything pointing me in the right direction would be a huge help ... thanks!


Solution

  • Not really sure if I understand the issue, but I believe you want to create something like..

    1. Draw two separate shapes of different transparency values.
    2. Composite them together.
    3. Apply resulting image as an image mask.

    I would suggest rewriting the code as...

    $img = new Imagick('rec.png');
    $height = $img->getImageHeight();
    $width = $img->getImageWidth();
    
    // Create first mask from original (and possible preserve original transparancies).
    $mask = clone($img);
    // We can "extract" the alpha channel to create a full white image.
    $mask->setImageAlphaChannel(Imagick::ALPHACHANNEL_EXTRACT);
    // Create second mask from frist.
    $mask2 = clone($mask);
    
    //Draw onto the new image the areas you want to be transparent in the original
    $draw = new ImagickDraw();
    $draw->setFillColor('gray90'); //<= Simplify with common color names
    $points = [
        ['x' => 400, 'y' => 0],
        ['x' => 400, 'y' => 200], 
        ['x' => 700, 'y' => 0], 
        ['x' => 400, 'y' => 0],
    ];
    $draw->polygon($points);
    
    $tdraw = new ImagickDraw();
    $tdraw->setFillColor('gray50'); //<= Something diffrent for visiblity.
    $npoints = [
        ['x' => 0, 'y' => 0],
        ['x' => 0, 'y' => 200], 
        ['x' => 400, 'y' => 200], 
        ['x' => 700, 'y' => 0],
        ['x' => 0, 'y' => 0],
    ];
    $tdraw->polygon($npoints);
    
    $mask->drawImage( $draw );
    $mask2->drawImage( $tdraw );
    // We can merge the values by multiplication. Might be worth exploring "SCREEN" & "BLEND" options
    $mask->compositeImage($mask2, Imagick::COMPOSITE_MULTIPLY, 0, 0);
    // Copy the values to alpha/opacity channel
    $mask->setImageAlphaChannel(Imagick::ALPHACHANNEL_COPY);
    // Copy the opacity from the mask to the original image.
    $img->compositeImage($mask, Imagick::COMPOSITE_COPYOPACITY, 0, 0); 
    $img->writeImage('output.png');
    

    two different levels of transparency