I remember in 1999 reading on slashdot about ESR getting rich, and that he'd mentioned hacking on a program to translate between PNG images and SNG, a plain-text format he was inventing. I never looked in to SNG at the time because he'd only just started it, but about a month ago it finally got added to Debian unstable, so I decided to have a play.
Here's a little example SNG file, a 2×2 checkerboard of purple and blue:
#SNG: 2x2 pixel blue and purple checkerboard
IHDR {
width: 2; height: 2; bitdepth: 8;
using color palette;
}
PLTE {
(127, 0, 127)
(0, 0, 191)
}
IMAGE {
pixels base64
01
10
}
To generate a PNG version of that (tiny-checkerboard.png)
you run the sng command like this (the PNG is output to a new file):
sng tiny-checkerboard.sng
Translating the other way works too, and sng works as a filter
(reading from stdin and writing to stdout) if you don't give a filename.
The intention of this is that you can tweak esoteric types of PNG chunk, which may not be supported by other graphics software you use. For me, it sounds more interesting as a way of keeping small images under CVS. You could easily have a makefile that builds the PNGs from the SNGs, and just keep the SNG version under revision control. For small images like icons it might actually give you readable diffs.
Another possible use is an easy way to generate images automatically. Of course, you can use libraries like ImageMagick to generate image files, but for simple ad-hoc things SNG might be easier. I gave it a try, with a little Perl program to generate a fade of the sort you might use as a background image for part of the layout of a website, like this:

The code is below. The output can be piped directly into sng,
and it will dump the PNG to its standard output.
#!/usr/bin/perl -w use strict; # This little program generates a spiffy 2-dimensional fade between two # colours, in SNG format. Use the 'sng' program to turn it into a PNG image. # # Geoff Richards, 2003. This program is public domain. No warranty. # The size of the image produced, in pixels. my ($width, $height) = (100, 100); # The two colours we fade between (red, green, blue, all in the range 0-1). # @col1 is the colour of the pixel in the top left corner, at position (0,0). # @col2 is the colour in the bottom right corner. my @col1 = (0, 0, 0); my @col2 = (1, 0.9, 0.9); # Precalculate various things from the image size. my ($max_x, $max_y) = ($width - 1, $height - 1); my $max = $max_x * $max_y; my $sqrt_max = sqrt($max_x) * sqrt($max_y); print header(), palette(), image(); # Return the magic and IHDR chunks in SNG format. sub header { return "#SNG:\n" . "IHDR {\n" . " width: $width; height: $height\n" . " bitdepth: 8;\n" . " using color palette;\n" . "}\n"; } # Return a palette in SNG format, with smoothly interpolated colours. sub palette { return "PLTE {\n" . join('', map { " (" . shade($_ / 255) . ")\n" } 0 .. 255) . "}\n"; } # Return a SNG colour interpolated between @col1 and @col2. The argument # indicates where inbetween, with 0 giving @col1 and 1 giving @col2. sub shade { my ($shade) = @_; my $antishade = 1 - $shade; return join(', ', int(($col1[0] * $antishade + $col2[0] * $shade) * 255 + 0.5), int(($col1[1] * $antishade + $col2[1] * $shade) * 255 + 0.5), int(($col1[2] * $antishade + $col2[2] * $shade) * 255 + 0.5)); } # Return the image data in SNG format. sub image { my ($r1, $g1, $b1, $r2, $g2, $b2) = @_; return "IMAGE {\n" . " pixels hex\n" . join('', map { " " . unpack('H*', line($_)) . "\n" } 0 .. $max_y) . "}\n"; } # Return, as a single string, a line of pixels, one per byte. # To get the colour of each pixel we multiply x by y to concentrate one # colour round the bottom right corner. The square root function is used # to make it less circular and push the 'high' colour further towards the # top left corner. sub line { my ($y) = @_; return join('', map { chr } map { int((sqrt($_) * sqrt($y)) / $sqrt_max * 255 + 0.5) } 0 .. $max_x); } # vi:ts=4 sw=4 expandtab