Tips for Programmers
Following is a list of tips for programmers:
- Use the correct pixel depth
Count colors! Or at least do so
when the compression setting is ``best'' and you don't know that the image is
grayscale--it doesn't take that long, and computers are good
at that sort of thing. If there are 256 or fewer colors, write a colormapped
image; doing so will translate to a factor-of-three savings in the PNG file
size relative to an RGB image.
- Use the correct pixel depth II
If the image is colormapped, don't
assume that the pixels must be 8 bits deep. If there are only one or two
colors, write a 1-bit image. If there are three or four colors, write a
2-bit image. If there are between 5 and 16 colors, write a 4-bit image.
(These are the only useful cases for PNG.) The compression engine cannot
compensate for bloated pixels! Choosing the correct depth for a palette-based
image will reduce the file size by a factor of anywhere from two to eight
relative to an 8-bit image.
- Use grayscale if possible
If you know the image is gray, see if it can be written more compactly
as a grayscale PNG than as a colormapped PNG--this is automatically
true if there are more than 16 shades of gray. Doing so will save up
to 780 bytes by eliminating the palette. But don't assume that 16 or
fewer shades automatically means the image can be written as 4-bit (or
smaller) grayscale. Grayscale necessarily implies that the shades are
evenly distributed from black to white. If, for example, the 16 shades
are bunched up in one part of the gray spectrum, the image must be
written as 8-bit grayscale or 4-bit palette-based. For larger images,
the palette-based approach is almost certainly better; for small ones
it depends, but the 8-bit grayscale case may end up being smaller. Try
both, if possible; it's very fast for small images.
- Set the compression and filtering options intelligently
For programs that use libpng (discussed at length in Part III, "Programming with PNG"), this
is not a serious issue; it will automatically do the right thing if
left to itself. But if you are writing custom PNG code, follow the
guidelines in the PNG specification for matching filter strategies
with image types. In particular, use filter type None for colormapped
images and for grayscale images less than 8 bits deep. Use adaptive
filtering (the ``minimum sum of absolute differences'' heuristic) for
all other cases.
- Truncate the palette
Unlike GIF, PNG's palette size is determined by
the chunk size, so there is no need to include 256 entries if only 173 are
used in the image. At 3 bytes per entry, wasted slots can make a big
difference in icons and other small images.
- Truncate the transparency chunk
It is extremely rare for every palette entry to be partially or fully
transparent. If there are any opaque entries--in particular, if all
but one are opaque--reorder the palette so that the opaque entries
are at the end. The transparency entries corresponding to these opaque
colors can then be omitted. The absolute worst possible approach is to
put the single transparent entry at the end of the palette!
Those 255 extra bytes are a lot for a file that would otherwise be 500
(or even 150) bytes long.
- Do transparency intelligently
Understand how PNG's alpha channels and tRNS chunk work. If the alpha
mask is binary (that is, either fully transparent or fully opaque),
see if the transparent parts correspond to a single color or gray
shade; if so, eliminate the alpha channel from the PNG file and use
the tRNS chunk (``cheap transparency'') instead. Alternatively, see
if the total number of color+alpha combinations is 256 or fewer; if
so, write a colormapped image with a tRNS chunk. If the user requests
that an RGBA image be converted to indexed color, do so intelligently.
The combination of PNG's PLTE and tRNS chunks amounts to a palette
whose entries are RGBA values. The exact same algorithms that
quantize and dither a 24-bit RGB image down to an 8-bit palette-based
image can be used to quantize and dither a 32-bit RGBA or 16-bit
grayscale+alpha image down to an 8-bit RGBA palette. In particular,
you cannot treat color values and transparency values as if they are
separate, unrelated entities; attempting to partition the palette into
a ``color part'' and a ``transparent part'' makes no more sense than
attempting to partition a standard RGB palette into red, green, and
blue parts. If you do cheap transparency poorly, the user will be
forced to use a full alpha channel, quadrupling her file size. For
grayscale, an alpha channel ``merely'' doubles the size. Note that
the icicle image in Figure C-1 in the color insert is
actually colormapped. Aside from the garish background--which was actually
generated by the viewing application--the full-resolution half looks pretty
darned good, doesn't it?
- Don't include unnecessary chunks in small images
Gamma information (or
the sRGB chunk) is always good, but a full ICC profile may quadruple the size
of a small image file. Consider not including a Software text chunk or tIME
chunk, or do so only for images larger than, say, 100 × 100 pixels.
Include dots-per-inch information (pHYs chunk) only if it is actually relevant
to the image; but the user may be the only one who can make that call.
- Offer the user reasonable options
Don't overwhelm him with unnecessary detail about filters or other
technical jargon. For example, offer a simple checkbox to turn on
interlacing. Offer a simple dial or even just two or three choices
for compression level--fastest, typical, and best,
perhaps. Even though it will make the file bigger, offer to include
at least a few text annotations--Author, Title, Description, and/or
Copyright, for example. On the other hand, offer to omit certain
optional information, such as that described in the previous item.
- Warn the user about data loss
If a region is completely transparent,
don't zero out the underlying color pixels in order to improve compression
unless you've notified the user in some way. Make sure she understands that
quantization and dithering are lossy transformations, but don't make this an
overly scary issue.