High Performance Computing

 

[Cygwin Tips] [Floppy Disks] [NAG Toolpack 2.5] [Web PNG images] [Controlling symbol scope in C]

 

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.