semi-opacity with GD

R

Richard Bennett

Hi,
i tried this question in comp.lang.perl.modules without any luck - maybe
someone here has an idea about it? :

I have GD2.0.33, and the most recent GD.pm on Linux.
I want to write a script that will load a PNG image, and make the image
semi-transparent.
I want to show this image over another one in the browser, and be able to
see the background image through the top one.
(I know this can be done in CSS, but am looking for a Firefox-only solution
using PNG alpha-channel)

I can make an image which does the right thing when I create a new image
like this:

#!/usr/bin/perl -w
use GD;
my $im; # The Image object
my $xsize = 160; my $ysize = 160;
my $bgcol = colourARGB( 70, 100, 255, 255 );
$im = new GD::Image->newTrueColor( $xsize, $ysize )
|| die "$0: Failed to create image -- $!\n";
$im->saveAlpha(1);
$im->alphaBlending(0);
$im->filledRectangle( 0, 0, $xsize - 1, $ysize - 1, $bgcol );
$im->alphaBlending(1);

binmode STDOUT;
print "Content-type: image/png\n\n";
print $im->png;

sub colourARGB($$$$) {
my $alpha = shift; my $red = shift;
my $green = shift; my $blue = shift;
# Alpha is in range 0 (opaque) to 127 (fully transparent),
# R, G, B in range 0 .. 255. Force values into range.
$alpha &= 0x7f; $red &= 0xff;
$green &= 0xff; $blue &= 0xff;
return $alpha << 24 | $red << 16 | $green << 8 | $blue;
}


But I cannot seem to find out how to manipulate an existing image to make it
semi-opaic. I tried like this:

#!/usr/bin/perl -w
use GD;
my $im; # The Image object
my $bgcol = colourARGB( 50, 200, 200, 200 );
$im = newFromPng GD::Image('top.png');
$im->saveAlpha(1);
$im->alphaBlending(0);
binmode STDOUT;
print "Content-type: image/png\n\n";
print $im->png;

sub colourARGB($$$$) {
my $alpha = shift; my $red = shift; my $green = shift; my $blue =
shift;
# Alpha is in range 0 (opaque) to 127 (fully transparent),
# R, G, B in range 0 .. 255. Force values into range.
$alpha &= 0x7f; $red &= 0xff; $green &= 0xff; $blue &= 0xff;
return $alpha << 24 | $red << 16 | $green << 8 | $blue;
}


The image displays ok, but I can't see how to add the opacity in.
Thanks for any tips,

richard
 
S

Sisyphus

Richard Bennett said:
I can make an image which does the right thing when I create a new image
like this:

#!/usr/bin/perl -w
use GD;
my $im; # The Image object
my $xsize = 160; my $ysize = 160;
my $bgcol = colourARGB( 70, 100, 255, 255 );
$im = new GD::Image->newTrueColor( $xsize, $ysize )
|| die "$0: Failed to create image -- $!\n";
$im->saveAlpha(1);
$im->alphaBlending(0);
$im->filledRectangle( 0, 0, $xsize - 1, $ysize - 1, $bgcol );
$im->alphaBlending(1);

binmode STDOUT;
print "Content-type: image/png\n\n";
print $im->png;

sub colourARGB($$$$) {
my $alpha = shift; my $red = shift;
my $green = shift; my $blue = shift;
# Alpha is in range 0 (opaque) to 127 (fully transparent),
# R, G, B in range 0 .. 255. Force values into range.
$alpha &= 0x7f; $red &= 0xff;
$green &= 0xff; $blue &= 0xff;
return $alpha << 24 | $red << 16 | $green << 8 | $blue;
}


But I cannot seem to find out how to manipulate an existing image to make it
semi-opaic. I tried like this:

#!/usr/bin/perl -w
use GD;
my $im; # The Image object
my $bgcol = colourARGB( 50, 200, 200, 200 );
$im = newFromPng GD::Image('top.png');
$im->saveAlpha(1);
$im->alphaBlending(0);
binmode STDOUT;
print "Content-type: image/png\n\n";
print $im->png;

sub colourARGB($$$$) {
my $alpha = shift; my $red = shift; my $green = shift; my $blue =
shift;
# Alpha is in range 0 (opaque) to 127 (fully transparent),
# R, G, B in range 0 .. 255. Force values into range.
$alpha &= 0x7f; $red &= 0xff; $green &= 0xff; $blue &= 0xff;
return $alpha << 24 | $red << 16 | $green << 8 | $blue;
}

In the second script you set $bgcol to a value ...... but then, afaict,
$bgcol doesn't get used. Why is that variable (and the subroutine that sets
its value) included in the script ? Didn't you get a warning that $bgcol was
used only once in the script ?

I don't know much about GD and opacity - perhaps those questions are totally
irrelevant to the problem. I wondered about that when I saw your post on
c.l.p.modules .... and I'm still wondering :)

Do you have a demo script that demonstrates the effect you're after - only
it prints the result to a png file rather than to stdout ? (I really don't
want to be stuffing around with demos that require a web server to view -
which is another reason that I didn't look too hard at your c.l.p.modules
post.) I tried modifying the first of the 2 scripts that you provided so
that it printed to a png file, but all I see is a pale blue square - and
that doesn't tell me much about the effect that you're seeking.

Cheers,
Rob
 
S

Sisyphus

I tried modifying the first of the 2 scripts that you provided so
that it printed to a png file, but all I see is a pale blue square - and
that doesn't tell me much about the effect that you're seeking.

I think I finally worked out what you're after - not entirely your fault
that it required effort on my part :)
The following script seems to do the job for me. It prints to file
(opacity2.png) rather than stdout and it uses the same value for $alpha as
was used in your original posting.

use warnings;
my $im2 = newFromPng GD::Image('top.png', 1);
$im2->saveAlpha(1);
$im2->alphaBlending(0);

my $alpha_val = (70 & 0x7f) << 24;
for my $x(0..$xsize-1) {
for my $y(0..$ysize-1) {
$index = $im2->getPixel($x,$y);
$index |= $alpha_val;
$im2->setPixel($x,$y,$index);
}
}

open(PNG2, ">opacity2.png") or die "Can't open opacity2.png for writing:
$!";
binmode PNG2;
print PNG2 $im2->png;
close(PNG2) or die "Can't close opacity2.png after writing: $!";

__END__

Cheers,
Rob
 
R

Richard Bennett

Sisyphus said:
use warnings;
my $im2 = newFromPng GD::Image('top.png', 1);
$im2->saveAlpha(1);
$im2->alphaBlending(0);

my $alpha_val = (70 & 0x7f) << 24;
for my $x(0..$xsize-1) {
for my $y(0..$ysize-1) {
$index = $im2->getPixel($x,$y);
$index |= $alpha_val;
$im2->setPixel($x,$y,$index);
}
}

open(PNG2, ">opacity2.png") or die "Can't open opacity2.png for writing:
$!";
binmode PNG2;
print PNG2 $im2->png;
close(PNG2) or die "Can't close opacity2.png after writing: $!";

__END__

Hi,
Thanks a lot for that. It looks like your code should work, but it doesn't
for me...
Firstly it seems that $xsize and $ysize are not actually being set
anywhere... I added:
($xsize,$ysize) = $im2->getBounds();

but it still didn't work...
The PNG is being output OK, it's not translucent though.
The idea is that this PNG is positioned over another image in an HTML page,
and the other image will show through the translucent PNG.
This is a test.html page:
<html><body>
<img style="position:absolute;left:0px;top:0px;" src="/map.png" />
<img style="position:absolute;left:0px;top:0px;" src="opacity2.png" />
</body></html>

if you use the original light-blue square I had, you will see map.png (or
any image you put in as background) will show through the blue square
faintly.

Here's an example of a translucent PNG:
http://marginalhacks.com/Hacks/album/Themes/OldPhoto/Overlay.1.png

It looks like your code does what I want, but it doesn't seems to work...

Thanks again for your help,

Richard.
 
S

Sisyphus

Richard Bennett said:
Hi,
Thanks a lot for that. It looks like your code should work, but it doesn't
for me...
Firstly it seems that $xsize and $ysize are not actually being set
anywhere... I added:
($xsize,$ysize) = $im2->getBounds();

Serves me right for cutting and pasting .... and *then* altering. I always
think that I can do that correctly .... but I often fail. (Sorry 'bout
that - I should know better.)
but it still didn't work...
The PNG is being output OK, it's not translucent though.
The idea is that this PNG is positioned over another image in an HTML page,
and the other image will show through the translucent PNG.
This is a test.html page:
<html><body>
<img style="position:absolute;left:0px;top:0px;" src="/map.png" />
<img style="position:absolute;left:0px;top:0px;" src="opacity2.png" />
</body></html>

How about, instead of creating the overlay as a file, you create it on the
fly - which, I think, is what you wanted to do anyway:

use warnings;
my $im2 = newFromPng GD::Image('top.png', 1);
$im2->saveAlpha(1);
$im2->alphaBlending(0);

($xsize,$ysize) = $im2->getBounds();

my $alpha_val = (70 & 0x7f) << 24;
for my $x(0..$xsize-1) {
for my $y(0..$ysize-1) {
$index = $im2->getPixel($x,$y);
$index |= $alpha_val;
$im2->setPixel($x,$y,$index);
}
}

binmode STDOUT;
print "Content-type: image/png\n\n";
print $im2->png;
__END__

The aim, as I now understand it, is to display top.png as a translucent
overlay. You probably can't do that by using "opacity2.png" - instead you
need to create the image on the fly (as per your original script). Hopefully
the above does that ... I'm curious now ... so I'll probably get around to
firing up my web server so that I can see for myself.

Cheers,
Rob
 
R

Richard Bennett

Sisyphus said:
use warnings;
my $im2 = newFromPng GD::Image('top.png', 1);
$im2->saveAlpha(1);
$im2->alphaBlending(0);

($xsize,$ysize) = $im2->getBounds();

my $alpha_val = (70 & 0x7f) << 24;
for my $x(0..$xsize-1) {
for my $y(0..$ysize-1) {
$index = $im2->getPixel($x,$y);
$index |= $alpha_val;
$im2->setPixel($x,$y,$index);
}
}

binmode STDOUT;
print "Content-type: image/png\n\n";
print $im2->png;
__END__

The aim, as I now understand it, is to display top.png as a translucent
overlay. You probably can't do that by using "opacity2.png" - instead you
need to create the image on the fly (as per your original script).
Hopefully the above does that ... I'm curious now ... so I'll probably get
around to firing up my web server so that I can see for myself.

hi,
Yep, it works. In fact, you can also output to file - that works too. The
only thing I was still doing wrong was to load an 8bit PNG. The script only
works if you load a 24bit image.
That's fine for me, as the images I want to process are fullcolor, but a
possible improvement would be to resample the image as fullcolor.
The 1 or 0 flag in newFromPng does prevent a 24bit image from working, when
set to 0, but doesn't convert an 8bit image to fullcolor when set to 1.

Anyway, you helped me a lot with that double loop, there's so little example
code for gd.pm, I doubt I would have found that myself.

Richard.
 
S

Sisyphus

Richard Bennett said:
hi,
Yep, it works. In fact, you can also output to file - that works too.

Heh .... I eventually got round to starting Apache and writing a cgi script
so that I could view these images that were created on the fly. I ran the
first of the 2 scripts that you posted, and I can see that the image gets
paler as $alpha is increased, but there's definitely no translucence there
for me. It is definitely opaque. Anyway ... the aim was to get it working
for you, and I can live without finding out the whole story :)
The only thing I was still doing wrong was to load an 8bit PNG. The script only
works if you load a 24bit image.
That's fine for me, as the images I want to process are fullcolor, but a
possible improvement would be to resample the image as fullcolor.
The 1 or 0 flag in newFromPng does prevent a 24bit image from working, when
set to 0, but doesn't convert an 8bit image to fullcolor when set to 1.

Anyway, you helped me a lot with that double loop, there's so little example
code for gd.pm, I doubt I would have found that myself.

It may well be that the double loop can be replaced by something a little
neater - that was just the first way I found of giving every pixel that
additional aplha value.

Cheers,
Rob
 
R

Richard Bennett

Sisyphus said:
Heh .... I eventually got round to starting Apache and writing a cgi
script so that I could view these images that were created on the fly. I
ran the first of the 2 scripts that you posted, and I can see that the
image gets paler as $alpha is increased, but there's definitely no
translucence there for me. It is definitely opaque.
You are testing with a browser like Firefox? IE doesn't support this effect.
Also, you have to layer the generated image over another one, to see it
showing through.

here's an example of the output of the last version of the script;
http://212.23.52.148/cgi-bin/img4.pl

of course it just looks pale, but if you display it over something else the
background shows through.

Thanks again,

richard.
 
S

Sisyphus

Richard Bennett said:
Sisyphus wrote:
You are testing with a browser like Firefox? IE doesn't support this effect.
Also, you have to layer the generated image over another one, to see it
showing through.

Aaah - I was layering the generated image over another one ... but looking
at it with IE :)

Cheers,
Rob
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,010
Latest member
MerrillEic

Latest Threads

Top