Background
Long ago, the only way to put transparency on the web was by
using GIF files. There were a number of problems with GIF:
- The Unisys patent
issue.
- They could only render 256 colours. Actually, since
many older displays used a colour lookup table, it was desirable to
restrict the GIFs to the 216 colours in which each of the red, green and
blue intensities was limited to a value from the set
0,51,102,143,204,255.
- They only supported a single transparency bit,
actually a single colour. This in turn meant that antialiasing and
transparency interacted badly; the antialiased boarder would be opaque
and, if displayed over a background of a different colour to that
originally used, one would see a horrid edge speckle effect.
JPEG is a lossy compression format that is good for
photographic images, but for graphs, text, overlays etcetera we needed a
lossless format with proper transparency support. Nowadays, we can use
PNG format graphics with modern
browsers such as Internet Explorer (5.5+), Netscape (v6+) and Mozilla. Note that
special code
is needed in your web page to activate proper PNG transparency in Internet
Explorer.
Making properly antialiased PNGs
Many older tools will not generate bitmap images with
transparency enabled. There are various workarounds, but it is difficult to
achieve all the required objectives:
- All colours can be used within the image. Thus white
and black are unambiguously different from transparent.
- The image itself is properly antialiased between
colours.
- The image border is properly antialiased in the alpha
channel, so that the transition from image colour to background is
smoothed.
Quite simply, there is no unambiguous way to turn a single RGB
image into one with an alpha channel. It is, on the other hand, straightforward
to extract the necessary information from two otherwise identical images, built
on contrasting backgrounds. The difference between the two images becomes the
alpha channel, and this in turn can be used to correct the RGB of one of the
images. There are lots of ways to generate images on contrasting backgrounds,
but for our example we will use Postscript; almost all Windows/Unix/Linux
applications can be persuaded to generate Postscript output.
- Here are a couple of example postscript files. They
both contain white and black glyphs, as well as unwritten background.
- A simple hand-created file, with discs and text:
test.ps
- A simple file generated by Microsoft Word, using
the Postscript printer driver: word_w.prn
- Here is how the test page
should look, I have used both a normal IMG tag and the special
Microsoft
AlphaImageLoader to display the PNG.
Non-Internet Explorer users will (correctly) see only one image.
Internet Explorer users will see two slightly different ones; both are
antialiased, the first against white and the second (using AlphaImageLoader )
against the document background. Interestingly, even with a white
background, the two images differ slightly; I think there is a (another)
problem with IE's native PNG rendering. The first image should print OK
on all browsers;
IE prints them both the same, although it garbles the Print Preview
of the AlphaImageLoader version.
- And here is how the Word page
should look. I have used the
pngbehavior trick this time, to simplify the HTML and ensure
compatibility between browsers. There is, unfortunately, a downside. If
you are using Internet Explorer, this trick has the effect of preventing
the PNG images from printing at all.
Generating the PNGs
I use a script based on the
Ghostscript
PostScript
engine and the Netpbm bitmap image
tools. You also need the pnmtopng
tool and a special piece of code I have written and called alphaconv. This takes
two colour PPM files, one on a black background and one on white, as input and
generates a PPM containing the RGB information and a PGM containing the alpha
channel, which is based on the difference between the two input files. The
Netpbm tools cannot do this unaided, as generating the RGB requires
pixel-by-pixel division between two images.
- The pstopng script.
- The C source for alphaconv. This
is also a simple example of reading and writing PPM/PGM files.
- We also use a helper awk script, cut.awk
Note that, so far as I can tell by examining the source,
versions of Ghostscript up to and including 8.00 are unable to generate alpha
images directly from Postscript. There is some alpha support associated with PDF
1.4 but the test output device pngw is currently disabled.
How the script works
Read this rather carefully; there was quite a bit of trial and
error in working around problems.
#!/bin/sh
# Ask for the sh shell (we porobably get BASH)
gs -q -dSAFER -dBATCH -dNOPAUSE -sDEVICE=ppmraw -r96 -dTextAlphaBits=4 -dGraphicsAlphaBits=4 -sOutputFile=/tmp/$$_wh.ppm -f$1
# Process the input file through ghostscript.
# -q -dSAFER -dBATCH -dNOPAUSE
# are standard
Ghostscript switches for batch mode.
# -sDEVICE=ppmraw
# sets the correct output file
format.
# -r96
# sets 96 dpi resolution for the output
images. This is the
# standard default screen resolution under Microsoft Windows.
# -dTextAlphaBits=4 -dGraphicsAlphaBits=4
# enable antialiasing of the image.
# -sOutputFile=/tmp/$$_wh.ppm
# defines the name of
the output PPM file.
#
The $$ is a standard shell escape to get s unique temporary
#
file name based on the process ID.
# -f$1
# this is the input file. The -f protects
filenames that start
# with "-".
You should probably still worry about filenames
#
with embedded spaces.
sed -e "s/%%EndPageSetup/&\\
gsave 0 setgray clippath fill grestore/" <$1 > /tmp/$$_bk.ps
# These two lines are a single command to the sed stream editor;
this is a standard
# part of UNIX, Linux and Cygwin. They serve to create a new Postscript file
# which has the Postscript commands "gsave 0 setgray clippath fill grestore"
# jammed in after each "%%EndPageSetup" line. This should ensure a black
# background for each page.
gs -q -dSAFER -dBATCH -dNOPAUSE -sDEVICE=ppmraw -r96 -dTextAlphaBits=4 -dGraphicsAlphaBits=4 -sOutputFile=/tmp/$$_bk.ppm -c gsave 0 setgray clippath fill grestore -f/tmp/$$_bk.ps
# We rerun Ghostscript on the black background.
# -c gsave 0 setgray clippath fill grestore
#
jams the black background commands in front of the
#
Postscript input, in case there is no "%%" annotation.
rm /tmp/$$_bk.ps
# We remove unwanted temporary files as we go along.
./alphaconv /tmp/$$_bk.ppm /tmp/$$_wh.ppm /tmp/$$_rgb.ppm /tmp/$$_a.pgm
# This is my code, it turns the two PPM files into an RGB
and an alpha.
rm /tmp/$$_bk.ppm /tmp/$$_wh.ppm
pnmcrop -black -verbose /tmp/$$_a.pgm >/tmp/$$_ac.pgm 2>/tmp/$$_crops.txt
# We crop the alpha greymap to throw away all transparent
border.
# We have to save the cropping information so we can crop the RGB too.
rm /tmp/$$_a.pgm
pnmcut `awk -f cut.awk </tmp/$$_crops.txt` /tmp/$$_rgb.ppm >/tmp/$$_rgbc.ppm
# We have to use awk to edit the cut information from
pnmcrop into
# a form usable by pnmcut, so we can make the RGB the same size as the
# alpha.
rm /tmp/$$_crops.txt /tmp/$$_rgb.ppm
pnmtopng -force -background white -interlace -alpha /tmp/$$_ac.pgm /tmp/$$_rgbc.ppm > $2
# We finally build the PNG
# -force turns off PNG optimisations. This is
needed because
#
Internet Explorer mishandles indexed PNGs.
# - background white
#
is also needed by Internet Explorer. By default, it
#
resolves alpha against the image's own background
#
colour. So putting this in gives a good (but not
#
transparent) antialiaed image on a typical white
#
background page.
# - interlace
#
produces progressive loading.
rm /tmp/$$_ac.pgm /tmp/$$_rgbc.ppm
Finally, after all this, we are still only handling single
page Postscript generating a single PNG image. To modify the script for multiple
images, you should look at
the
use of "%d" in output filenames for Ghostscript. You might also want to look
at pnmmargin if you want to leave a border around the cropped image
How it can all look horrid...
We can see the various possibilities for displaying
bitmaps built from the Word file:
- Simple use of Ghostscript, without antialiasing, to
produce a GIF file. We set white as transparent in the GIF.
The result is horribly jagged, and the
white text does not appear. The
commands used were:
gs -q -dSAFER -dBATCH -dNOPAUSE -sDEVICE=ppmraw -r96 -dTextAlphaBits=4 -dGraphicsAlphaBits=4 -sOutputFile=word_whn.ppm word_w.prn
pnmcrop -white word_whn.ppm | ppmquant 256 | ppmtogif -transparent white > wordn.gif
gs -q -dSAFER -dBATCH -dNOPAUSE -sDEVICE=ppmraw -r96 -sOutputFile=word_whn.ppm word_w.prn
pnmcrop -white word_whn.ppm | ppmquant 256 | ppmtogif -transparent white > wordn.gif
- Internet Explorer does not treat PNG transparency properly by
default. If we do not apply the PNGbehaviour style, we get
this under IE. Here the PNG is being rendered
over white, regardless of the page background. Even getting it to
look this good required special care; see the comments on the
pstopng script. Everything will, however,
look fine under Mozilla.
|