Image Spectrumizer

Or, putting graphics onto the infernal machine

For one reason or another, I got this idea that I should do something on the computer I played with as a child, namely Sinclair ZX Spectrum. I've written all sorts of things about this project elsewhere, but here I'm concentrating on graphics, or more precisely, a tool I wrote for it.

Some years ago, I wrote a bunch of photoshop filters, and one of those converted graphics to the zx spectrum limitations. The problem with that is that the limitations are so severe that you pretty much have to massage the underlaying image first if you want to get any decent output... and doing that in photoshop is difficult, because you can't see the result unless you use some kind of convoluted edit-apply filter-undo-edit cycle.

So I wrote a standalone application with a modifier stack and... I'm getting ahead of myself.

Let's talk about those limitations for a bit.

First, the one and only screen mode the spectrum has is 256x192 bitmap with a 32x24 color overlay. In other words, each 8x8 block of the screen can use two colors out of a 16 color palette. Except that both of the colors must come form the same 8 color subset of the palette (either dark or bright). Both 8 color palettes have a black color, so the total number of colors is actually 15.

So if we take this image..

..and convert it to spectrum format, we'll do the following:

  • Find the closest color for each pixel (Pythagoras helps here)
  • For each 8x8 cell, figure out if majority of the pixels are bright or not.
  • Next, assuming we're only using bright or dark colors, find the closest color for each pixel again.
  • Now, find the most popular and second most popular colors in the cell.
  • Re-map yet again, now just checking which of the two remaining colors is closest.

Simple! Well, not quite. The result looks like this:

Which isn't the most pleasing result, but this picture is actually one of the easier ones to make look decent. Most source images just don't map well.

Zooming in, we see that each 8x8 cell only uses two colors:

This is also the source of the famous zx spectrum attribute clashes. It's no wonder most speccy games were largely monochrome.

Let's look at the histograms. Here's the histogram of the source image:

Red, green and blue, and how many pixels hit each value. It's pretty continuous. Now, let's look at the result image histogram:

Boom. But that's just how the graphics mode works. One of the annoying things is the bright attribute; instead of being able to pick freely from the 16 color palette for both background and foreground color (or "paper" and "ink"), the colors have one bit for brightness.. and one bit for "blink". The blink swaps the foreground and background every 16 frames. I suppose this was a good idea at the time, but in hindsight it's just annoying. Some speccy clones and modifications ditch the blink bit and replace it with a second bright bit.

If you look at any continuous tone image (like a photograph), and find a bright blue spot.. what's the most likely color next to it: bright green, bright red, bright yellow or dark blue? You can't put dark blue next to bright blue, sorry. Most images that have both bright and dark attributes are pretty jarring, so I've included an option in the Image Spectrumizer to just use dark colors (or bright colors). This reduces the number of possible colors on screen, but may be better than living with the glitches.

You can also force the background color to some value. That helps with some images.

Here's the bitmap and color layers separated:

As we can see from the bitmap, the source image is actually pretty noisy, which makes the hat look relatively good. Adding a bit of ordered dither and tweaking contrast a bit, we can try to make the image a little bit better. I'm no artist, but: (okay, I am, but not that kind)

With a bunch of modifiers (RGB, YUV, HSV, contrast, noise, etc) the user can tweak the source image and see, in real time, how the spectrumized image looks like. Since so much information is lost, there's no "perfect" conversion. It may be that a completely false color image is way better than trying a "direct" conversion.

The spectrumizer can also track changes in source images on disk and reload whenever the image changes. This means that you can have the image open in a image editor (such as photoshop) and whenever you save the file, you can see what your changes mean in the spectrum land.

To reproduce the results later on, the workspace - including the modifier stack - can be saved to disk.

Finally, spectrumizer can be run from the command line to batch convert images. This may be useful depending on how your development toolchain works.

After I had the basics together, I started looking at "tweaked" modes for spectrum. Some people have routines where they switch the attributes while the image is being drawn, and have managed to create 8x4 cells. Since doing it was easy, I added support for generating 8x4, 8x2 and 8x1 cell data as well. I don't know if anyone will ever use that feature, however.

There were also other tweaked modes, but they were largely based on either exotic hardware (like different ULA chips) or blinking like crazy. One idea was to use the page flipping in 128k spectrums, and this gave me the idea that what if we'd swap just the attributes? That could be easily done with the 48k spectrum (the whole attribute swap fits into top border).

There's three combinations to think of: dark+dark, dark+light and light+light. Each of the sets has 8 colors, meaning 8*8=64 colors each. So the "mode" has 3x64 colors - still tied to the 2 colors per 8x8 cell limitation, and an additional limitation that the two colors must be from one of the 3 64 color palettes.

Plenty of colors. So I spent some time adding support for "spectrumizing" to this mode, and the results are pretty nice..

Unfortunately, emulators (which are the most common use of spectrum software these days) don't show these well at all, and are horribly blinky. I talked about this on the world of spectrum forums (very nice community there), and some pointed out that the results are not going to be stable on CRTs either. Others said that on certain CRTs they are. So your mileage may vary.

I loaded up the image on my spectrum, and with my usb tv grabber the image is stable. Unfortunately, the colors, however..

..are horrible. Apparently the colors don't mix like I thought. So what's a coder to do? Well, I made a calibration image and loaded it up instead:

Taking that screengrab I generated new reference palette, and, using that, the results are much more pleasing:

Quite far from the original spectrumized image. Unfortunately, since this "mode" wasn't popular, like, 30 years ago, the emulators don't handle it, and thus it is less useful. But if you want it, the spectrumizer can do it.

Image Spectrumizer is available on github under zlib license, and windows binaries are also available.

I've toyed with the idea of expanding the spectrumizer to support other restricted graphics targets.. such as c64 graphics, or CGA, or some such. It would require removing some of the hardcoded values and wrapping the converter in a class, but those are relatively small tasks... but maybe, just maybe, now that I have this thing written down, I can stop working on it for a while and do other things.

Comments, etc, appreciated, as always.