/    Sign up×
Community /Pin to ProfileBookmark

[RESOLVED] Alpha Transparency in PNG Images

[font=trebuchet ms]Hello fellas,

For the past few weeks, I’ve been working on a small webapp, and I need it to convert PNGs with alpha transparency into grayscale. I’ve built all the necessary functions to get GIFs, PNGs, and JPGs appear in grayscale, but PNGs with alpha transparency don’t work — the area that was previously semi- or fully-transparent have an opaque color (usually black, but not always). Additionally, the image becomes aliased, and I want it to be antialiased. Can someone point me in the right direction, please? I’ve experimented for 3 days with this thing, and usually 3 days is more than enough time for me to figure something out in PHP.

I’ll post the code upon request (it’s a little long).[/font]

to post a comment
PHP

19 Comments(s)

Copy linkTweet thisAlerts:
@bokehMar 01.2006 — Can I have a look at the code you are using. Are you saying You want a grayscale image but the transparent area to remain transparent?
Copy linkTweet thisAlerts:
@JonaauthorMar 01.2006 — [font=trebuchet ms]Thanks for the reply. I was afraid I would have to figure this out for myself with no help!

Yes, that’s exactly what I’m saying. The code is rather long and can’t be implemented directly, but hopefully you’ll be able to see how to make use of the function.[/font]

[code=php]
# This function converts palette images
# into truecolor images. This is for
# GIFs and PNGs, mostly.
function imagepalettetotruecolor($img)
{
if (!imageistruecolor($img))
{
$w = imagesx($img);
$h = imagesy($img);
$img1 = imagecreatetruecolor($w,$h);
imagecopy($img1,$img,0,0,0,0,$w,$h);
$img = $img1;
}
return $img;
}

function greyImage($url){
$type = 'jpeg';

$fullPath = explode(".",$url);
$lastIndex = sizeof($fullPath)-1;
$extension = $fullPath[$lastIndex];
if (preg_match("/jpg|jpeg/i", $extension)){
$sourceImage = imagecreatefromjpeg($url);
$type = 'jpeg';
} else if(preg_match("/gif/i", $extension)){
$sourceImage = imagecreatefromgif ($url);
$type = 'gif';
} else if(preg_match("/png/i", $extension)){
$sourceImage = imagecreatefrompng ($url);
$type = 'png';
}

if(!imageistruecolor($sourceImage)){
$sourceImage = imagepalettetotruecolor($sourceImage);
}

$img_width = imageSX($sourceImage);
$img_height = imageSY($sourceImage);

for ($y = 0; $y <$img_height; $y++) {
for ($x = 0; $x <$img_width; $x++) {
$rgb = imagecolorat($sourceImage, $x, $y);
$red = ($rgb >> 16) & 255;
$green = ($rgb >> 8) & 255;
$blue = $rgb & 255;
$alpha = ($rgb >> 24) & 255;
$gray = round(.299*$red + .587*$green + .114*$blue);

$grayR = $gray << 16; // R: red
$grayG = $gray << 8; // G: green
$grayB = $gray; // B: blue
$grayColor = $grayR | $grayG | $grayB;
$grayColor = $grayR | $grayG | $grayB;

if($type == 'png'){
$grayColor = imagecolorallocatealpha($sourceImage, $gray, $gray, $gray, $alpha);
} else {
$grayColor = imagecolorallocate ($sourceImage, $gray, $gray, $gray);
}
imagesetpixel ($sourceImage, $x, $y, $grayColor);
}
}

$destinationImage = ImageCreateTrueColor($img_width, $img_height);
imagecopy($destinationImage, $sourceImage, 0, 0, 0, 0, $img_width, $img_height);

if($type == 'jpeg'){
$return = imagejpeg($destinationImage);
} else if($type == 'gif'){
$return = imagegif($destinationImage);
} else if($type == 'png'){
$return = imagepng($destinationImage);
}

imagedestroy($destinationImage);

imagedestroy($sourceImage);

return $return;
}
[/code]
Copy linkTweet thisAlerts:
@bokehMar 02.2006 — Try this... Are we heading in the right direction?[code=php]Code removed... See post #11[/code]
Copy linkTweet thisAlerts:
@bokehMar 02.2006 — The above seems to work with 24 bit PNGs but not properly with 8 bit ones. I am working on that and do know what the problem is. Right now I am trying to discover a reliable way to distinguish between 24 and pallete PNGs using GD. Update shortly. Did you try the above?
Copy linkTweet thisAlerts:
@bokehMar 02.2006 — Ok Jona, here's what I came up with. It is one simple function that works on [B]all image types[/B] and can either send the output to the browser or to a file of your choice. Output has the same mime type as the source file, i.e. .jpg source produces .jpg output, PNG source produces PNG output. The bias each prime color has on the final output is also customisible from outside the function. Let me know how you get on with this.[code=php]Code removed... See post #11[/code]
Copy linkTweet thisAlerts:
@JonaauthorMar 02.2006 — [font=trebuchet ms]Hi Bokeh,

Thanks for your help. Unfortunately, although it does work to an extent (the transparent background indeed becomes transparent), the image still appears aliased on its corners.

See it at: < http://68.116.218.192/php/contrastico/imgTest.php >

Original image: < http://www.accessites.org/images/wrapback.png >

Thanks again![/font]
Copy linkTweet thisAlerts:
@JonaauthorMar 02.2006 — [font=trebuchet ms]I&#8217;ve tried playing with all kinds of variations of the code you gave, in addition to testing your code, but so far the closest thing I&#8217;ve got is transparency without antialiasing.

Question&#8211;why do you convert the image from truecolor to palette? Doesn&#8217;t a PNG24 have to be truecolor in order for it to maintain its alpha channels?[/font]
Copy linkTweet thisAlerts:
@bokehMar 02.2006 — [font=trebuchet ms]Question–why do you convert the image from truecolor to palette? Doesn’t a PNG24 have to be truecolor in order for it to maintain its alpha channels?[/font][/QUOTE]To be honest I just did that to save on file size and didn't see any side affect when I tested it. You should just be able to comment that line out. Anyway I am going to look at your images now.
Copy linkTweet thisAlerts:
@bokehMar 02.2006 — Ok! Getting closer. I didn't have an image such as yours to test against. If you comment out the palette line [I][code=php]#imagetruecolortopalette($slate, false, 256);[/code][/I] and then manually enter a transparency value it is possible to get the image looking like yours:[code=php]$pixel_color = ImageColorAllocateAlpha($slate, $average, $average, $average, 105/*$alpha*/);[/code]So now we need to back track to find out why it is not happening automatically.
Copy linkTweet thisAlerts:
@bokehMar 02.2006 — Ok, I think I've cracked it. [URL=http://bokehman.com/wrapback.grayscaled]Here's your test image after running it through the function below.[/URL] [code=php]<?php

function grayscale($source, $destination = NULL, $bias = array('r'=>50,'g'=>35,'b'=>15))
{
###############################################################################
## ##
## Function: grayscale ##
## Author: "Bokeh" ##
## Website: http://bokehman.com/ ##
## Send mods to: http://bokehman.com/email ##
## Forum thread: https://webdeveloper.com/forum/showthread.php?t=97163 ##
## ##
## Converts true colour and palette images to grayscale ##
## ##
## Works on all file types on which it was tested (PNG8, PNG24, GIF & JPEG) ##
## ##
## grayscale( string source, [string destination, [array bias]]) ##
## ##
## 'source': the path and filename of the source image. ##
## 'destination': path and filename to which the image will be saved. ##
## 'bias': the weight the respect colors exert on the final image. ##
## ##
## If 'destination' is NULL, headers and the grayscale image will be output ##
## to the user agent. Destination image will be output with the same file ##
## type as the source file. ##
## ##
## The combined additive values of 'bias' should be 100. ##
## ##
###############################################################################

# probe the source file and open it if possible
file_exists($source) or die($source.' does not exist');
is_readable($source) or die($source.' is not readable');
$image_details = @getimagesize($source) or die($source.' is not a valid image');
$image = @imagecreatefromstring(
@file_get_contents($source))
or die($source.' could not be opened');

# get source image dimmensions
$w = imagesx($image);
$h = imagesy($image);

# create a slate to draw on
$slate = imagecreate($w, $h);

# build the grayscale image
for ($x = 0; $x < $w; $x++)
{
for ($y = 0; $y < $h; $y++)
{
$colors = imagecolorsforindex($image, ImageColorAt($image,$x,$y));
$average = round(($colors['red']*($bias['r']/100)) +
($colors['green']*($bias['g']/100)) +
($colors['blue']*($bias['b']/100)));
if($average > 255) $average = 255;
elseif($average < 0) $average = 0;
if(($pixel_color = imagecolorexactAlpha($slate, $average, $average, $average, $colors['alpha'])) === (-1))
{
$pixel_color = ImageColorAllocateAlpha($slate, $average, $average, $average, $colors['alpha']);
}
ImageSetPixel($slate, $x, $y, $pixel_color);
}
}

# GIF transparency
if(($image_details['mime'] === 'image/gif') and (false !== ($trans_colors = @imagecolorsforindex($image, imagecolortransparent($image)))))
{
$average = round(($trans_colors['red']*($bias['r']/100)) +
($trans_colors['green']*($bias['g']/100)) +
($trans_colors['blue']*($bias['b']/100)));
imagecolortransparent($slate, imagecolorexactAlpha($slate, $average, $average, $average, $trans_colors['alpha']));
}

# output the image to browser ($destination = NULL)
# or output the image to the path/filename in $destination
if(!$destination) header('Content-Type: '.$image_details['mime']);
preg_match('@image/(.+?)$@', $image_details['mime'], $matches);
$eval = ($destination)
?'@image'.$matches[1].'($slate, $destination)
or die('Directory permission problem');'
:'@image'.$matches[1].'($slate);';
eval($eval);
if(!$destination) exit;
return TRUE;
}

?>[/code]
Copy linkTweet thisAlerts:
@JonaauthorMar 02.2006 — [font=trebuchet ms]Awesome, thanks, Bokeh! I&#8217;m going to test it out in a minute and try to incorporate your changes into my original function. I&#8217;ll let you know how it goes.[/font]
Copy linkTweet thisAlerts:
@bokehMar 02.2006 — Incidently your source image is indexed colour and 4792 bytes in size while the grayscale image is true colour and 11286 bytes in size. I only have Photoshop 6 and it displays the grayscale image as completely transparent, i.e. just the check pattern. Very strange. What gimp are you using to produce theser images?
Copy linkTweet thisAlerts:
@JonaauthorMar 02.2006 — [font=trebuchet ms]I didn&#8217;t create the image, but I&#8217;m fairly certain it was created in Photoshop. A friend of mine created it. I just need my program to work with it, along with just about any other possible images out there on the web.

Thanks to you, I think I can succeed in this goal! I&#8217;ve worked with the code you provided, and after a little study and research, I am understanding exactly why it works (and mine didn&#8217;t!). I&#8217;ve gotten it to work with my original function, and although more customization and work will need to be done with it, I really, genuinely appreciate your assistance with something as intense as this &#8212; most people skip over the more difficult requests for help! Thanks again, you&#8217;ve been an incredible help to me, Bokeh. ? [/font]
Copy linkTweet thisAlerts:
@JonaauthorMar 02.2006 — [font=trebuchet ms]Actually, I do have a few more questions, if you don&#8217;t mind. They regard efficiency.

Currently my server becomes overloaded when processing a large number of images (which, I presume, it attemps to process simultaneously). Is there any way to buffer the output (e.g., stop processing of an image, wait 10 seconds, then continue processing it, if the server is bogged down?). I don&#8217;t know if PHP can even do that, but the server tries to process all the images at once, instead of one at a time, and crashes as a result. Maybe I can fix the problem just by making the image processing function faster/more efficient?[/font]
Copy linkTweet thisAlerts:
@bokehMar 03.2006 — I guess running through the image pixel by pixel is not too efficient but I can't think of a better way right now. I can't think of a way to throttle the server either and PHP certainly has no way of knowing what other processes/scripts are running parallel.

I tested the function on my laptop and on my server (P4 3.0 GHz Hyperthreading) and timed the [I]for[/I] loop. Against your 500 x 400 (200,000) pixel image and it takes about 2500 milliseconds to run. If time is not a consideration you could put a [I]usleep()[/I] in the outer loop, maybe for example [I]usleep(10000);[/I] Not nice but it might stop the server crashing.

The other thing is to think of a way so the images are called sequencially like a bit of javascript that requests the next image once the current one has finished loading. I did this in the [URL=http://bokehman.com/rotate/]slide show on my site[/URL].

And your final option of course is to cache the images once they are created.

Oh yeah, while I remember, according to the manual imagetruecolortopalette() is supposed to preserve the alpha channel but doesn't work as it is supposed to and the manual recommends to save in true colour.
Copy linkTweet thisAlerts:
@JonaauthorMar 03.2006 — [font=trebuchet ms]Thanks for all the help, Bokeh.

Interestingly, after further modification, the server no longer crashes on sites with lots of (large) images. I don’t know exactly what it was, perhaps just the reduced amount of code, but things work much better now. I will keep in mind the usleep() idea if I find it’s necessary in the future.

Thanks again, you’ve helped loads!

By the way, this being part of a small webapp, I hope you won’t mind my linking to your site and attributing you credit for assisting with the construction of the webapp, with your permission.[/font]
Copy linkTweet thisAlerts:
@bokehMar 03.2006 — [font=trebuchet ms]I hope you won’t mind my linking to your site and attributing you credit for assisting with the construction of the webapp, with your permission.[/font][/QUOTE]You're welcome to link to my site.

Questions like this make an interesting break from the norm; I wish there were more like it.
Copy linkTweet thisAlerts:
@LiLcRaZyFuZzYMar 03.2006 — I wish there were more like it.[/QUOTE]
be careful with what you wish! hehe ?

anyway..impressive work bokeh!

i so don't understand the whole GD thing, i have no clue how to work with images in php.. :o
Copy linkTweet thisAlerts:
@bokehMar 03.2006 — i have no clue how to work with images in php..[/QUOTE]Well the truth is I had no clue how I would go about it until someone asked the question and I attempted to answer it. I didn't look at any tutirials either (there don't seem to be any). Anyway I started working on my script before Jona posted his but we must have been thinking on similar lines, the for loop for example, but if you look closer Jona is scanning left to right and I am scanning top to bottom. Left to right seems more logical but I did it the other way for no other reason than x comes before y alphabetically.

I'm starting to build up a library of functions I have written with the GD functions now. I've done several captchs, text and image based watermarking, all different thumbnail and resizing functions, outputing text blocks as images, USM and now this. What else could GD be used for?
×

Success!

Help @Jona spread the word by sharing this article on Twitter...

Tweet This
Sign in
Forgot password?
Sign in with TwitchSign in with GithubCreate Account
about: ({
version: 0.1.9 BETA 6.17,
whats_new: community page,
up_next: more Davinci•003 tasks,
coming_soon: events calendar,
social: @webDeveloperHQ
});

legal: ({
terms: of use,
privacy: policy
});
changelog: (
version: 0.1.9,
notes: added community page

version: 0.1.8,
notes: added Davinci•003

version: 0.1.7,
notes: upvote answers to bounties

version: 0.1.6,
notes: article editor refresh
)...
recent_tips: (
tipper: @nearjob,
tipped: article
amount: 1000 SATS,

tipper: @meenaratha,
tipped: article
amount: 1000 SATS,

tipper: @meenaratha,
tipped: article
amount: 1000 SATS,
)...