Welcome to WebmasterWorld Guest from 107.20.122.81

Forum Moderators: coopster & jatar k

Message Too Old, No Replies

Cutting a Triangle out of PNG and leaving the background transparent

     

adder

3:18 pm on Dec 15, 2012 (gmt 0)

10+ Year Member Top Contributors Of The Month



Hi,

I'm trying to write a script that takes a PNG, cuts out a triangle, leaves the PNG data within the triangle and creates transparent background outside the triangle.

This is what I've done and it seemed pretty logical to me before I tested the script :)
As a result it simply displays the original PNG unaltered.

<?php
//create the image
$image = imagecreatefrompng('landscape.png');
//define the foreground
$fground = imagecolorallocatealpha($image, 0, 0, 0, 127);
//define the background
$bground = imagecolorallocate($image, 0, 0, 0);

//make the background transparent
imagecolortransparent($image, $bground);
//draw a triangle and fill with $image data
imagefilledpolygon($image, array(0,0, 120,0, 120,120), 3, $fground);

//save the image
header('Content-type: image/png');
imagepng($image);
imagedestroy($image);
?>


What am I doing wrong?

rainborick

7:05 pm on Dec 15, 2012 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



If you change your code to:

$fground = imagecolorallocatealpha($image, 0, 0, 0, 0);

you'll draw the polygon with full transparency. The alpha channel controls opacity, so 0 = transparent and 127 = fully opaque.

adder

7:41 pm on Dec 15, 2012 (gmt 0)

10+ Year Member Top Contributors Of The Month



rainborick, thanks but I actually want it the other way round. I want the polygon to be filled with data from the original PNG and the transparency to be outside of the polygon.

In other words, I want to get rid of everything except that triangle.

lucy24

10:09 pm on Dec 15, 2012 (gmt 0)

WebmasterWorld Senior Member lucy24 is a WebmasterWorld Top Contributor of All Time Top Contributors Of The Month



Obvious backtracking question: What aspect of the triangle is dynamic? There has to be some reason why the png wasn't simply made with an alpha channel in the first place-- but sometimes there's more than one way to achieve the result you want.

adder

11:03 pm on Dec 15, 2012 (gmt 0)

10+ Year Member Top Contributors Of The Month



@lucy24, well, actually, the triangle is dynamic and the x,y points will vary. The points will be retrieved from another script but that's not a problem. I'll simply make the array in imagefilledpolygon line dynamic.

What do you have in mind regarding that alpha channel? I actually control the making of png so maybe I can create that alpha channel before all this cutting happens? Would that be a better approach? And if yes, how do I do it?

Sorry if this is a stupid question, I've just started learning about GD :)

swa66

12:14 am on Dec 16, 2012 (gmt 0)

WebmasterWorld Senior Member swa66 is a WebmasterWorld Top Contributor of All Time 10+ Year Member



Why don't you draw the transparant color to all but the triangle ?

Assuming maxx and maxy to be the dimensions of the image

You could use a polygon to draw
(0,0, // star in a corner
x1,y1, //triangle 1
x2,y2, //triangle 2
x3,y3, //triangle 3
0,0 // go back to the first corner again
maxx,0 // corner
maxx,maxy //corner
0,maxy //corner
)

Guess you will need to take care with the case wwhere an X or Y of your triangle gets to be 0 or maxx or maxy.

rainborick

5:06 pm on Dec 16, 2012 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



I found a handful of references to masking with GD, but I didn't have any luck working up an example. So I fell back on ImageMagick where I knew the tools existed.

// Polygonal Masking using ImageMagick

$pContent = "Content-type: image/png"; // MIME-type for PNG files
$old_image = 'landscape.png'; // Original image file.

// Always output .png
$theContent = $pContent;
$theWMType = 'png:-';

// Get original image dimensions
$image = imagecreatefrompng($old_image);
$width = imagesx($image);
$height = imagesy($image);

// Create the mask image file - same size as original
// "black" in a mask is transparent, white is opaque
$wmCmd = "convert -size " . $width . "x" . $height . " xc:black";
// Set up drawing color to create mask
$wmCmd .= " -fill white -stroke white";
// -draw the desired mask
$wmCmd .= " -draw \"polygon 0,0, 120,0, 120,120\"";
// Output the result to file
$wmCmd .= " mask.png";
system($wmCmd);

// Composite the mask with the image
$wmCmd = "convert $old_image mask.png -alpha Off -compose CopyOpacity -composite";
// Crop to fit (optional for this example only!)
$wmCmd .= " -crop 120x120+0+0"; // (width)x(height)(x-offset)(y-offset)
$wmCmd .= " $theWMType";

header($theContent);
passthru($wmCmd); // use passthru() to output binary image data to browser
exit;

This should work on any server that has ImageMagick installed as long as the script has write permission in the directory where the script resides. No PHP libraries are required.

swa66

9:20 pm on Dec 16, 2012 (gmt 0)

WebmasterWorld Senior Member swa66 is a WebmasterWorld Top Contributor of All Time 10+ Year Member



Oh, I just realized that the coordiante I gave need to be properly sorted so that the way you turn around the traigle and the way you turn around the edge are in the same sense". Not that hard to fix (for testing: if it's wrong: just swap 2 corners of the triangle).

lucy24

11:55 pm on Dec 16, 2012 (gmt 0)

WebmasterWorld Senior Member lucy24 is a WebmasterWorld Top Contributor of All Time Top Contributors Of The Month



This should work on any server that has ImageMagick installed as long as the script has write permission in the directory where the script resides.

Uh-oh, that's about two more "if"s that you generally want to see :( What a shame there isn't something more one-size-fits-all.

rainborick

3:39 am on Dec 17, 2012 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



ImageMagick is fairly ubiquitous - especially on shared hosts - and is easily installed on dedicated servers that might lack it. I mentioned the directory write permission issue only to cover all of the details. A more experienced ImageMagick developer could almost certainly create a script that would do the job completely in memory without resorting to the mask file. I intend to play around with this a bit myself to see if I can do it.

As I said, I found several references to using GD to do image masking via Google, but couldn't locate a working script. I'll keep looking.

rainborick

3:59 am on Dec 17, 2012 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



Wouldn't you know it... two seconds into a second search, I found some GD code that works thanks to a contributor on php.net:

<?php
// Polygonal masking using GD
// imagealphamask() Courtesy of author at http://www.php.net/manual/en/function.imagesavealpha.php#94986

// Load the image
$image = imagecreatefrompng('landscape.png');
$width = imagesx($image); // get dimensions
$height = imagesy($image);

// Create mask
$mask = imagecreatetruecolor($width, $height);
// "black" in a mask is transparent, while white is opaque
$opaque = imagecolorallocate($mask, 255, 255, 255);
imagefilledpolygon($mask, array(0,0, 120,0, 120,120), 3, $opaque);

// Apply mask to source
imagealphamask( $image, $mask );

// Output image and free memory
header('Content-type: image/png');
imagepng($image);
imagedestroy($image);
imagedestroy($mask);
exit;

function imagealphamask( &$picture, $mask ) {
// Get sizes and set up new picture
$xSize = imagesx( $picture );
$ySize = imagesy( $picture );
$newPicture = imagecreatetruecolor( $xSize, $ySize );
imagesavealpha( $newPicture, true );
imagefill( $newPicture, 0, 0, imagecolorallocatealpha( $newPicture, 0, 0, 0, 127 ) );

// Resize mask if necessary
if( $xSize != imagesx( $mask ) || $ySize != imagesy( $mask ) ) {
$tempPic = imagecreatetruecolor( $xSize, $ySize );
imagecopyresampled( $tempPic, $mask, 0, 0, 0, 0, $xSize, $ySize, imagesx( $mask ), imagesy( $mask ) );
imagedestroy( $mask );
$mask = $tempPic;
}

// Perform pixel-based alpha map application
for( $x = 0; $x < $xSize; $x++ ) {
for( $y = 0; $y < $ySize; $y++ ) {
$alpha = imagecolorsforindex( $mask, imagecolorat( $mask, $x, $y ) );
$alpha = 127 - floor( $alpha[ 'red' ] / 2 );
$color = imagecolorsforindex( $picture, imagecolorat( $picture, $x, $y ) );
imagesetpixel( $newPicture, $x, $y, imagecolorallocatealpha( $newPicture, $color[ 'red' ], $color[ 'green' ], $color[ 'blue' ], $alpha ) );
}
}

// Copy back to original picture
imagedestroy( $picture );
$picture = $newPicture;
} // end imagealphamask()

?>

adder

10:53 pm on Jan 30, 2013 (gmt 0)

10+ Year Member Top Contributors Of The Month



@rainborick this is much appreciated, thank you very much for the solution. It works really well! (sorry, took a long time to answer, was too stuck in the offline world :)
 

Featured Threads

Hot Threads This Week

Hot Threads This Month