Having Greys Makes a Bit of Difference

The ZX Spectrum Next uses 9-bits for its colours, giving a total of 512 possible different colours. The Next's palettes are only 256 colours in size however. The default palette uses 8-bit colours of the format RRRGGGBB, generated by the taking the numbers 0 - 255 in order. The third B bit is taken by ORing the other two B bits together. So the colour at index 60 in the palette is the colour given by the number 60, which is 00111100 in binary, or a smidgen of red (001), loads of green (111) and no blue. (00)

Whilst this gives a reasonable selection of colours it does obviously lean towards red and green as they have more bits in the 8-bit colour. One big problem with the default palette however are greys - there aren't many. To get a grey colour you need the R, G and B components to all be the same value. As there are only 2 bits for B that means 4 possible greys. However one of those is black and one is white, giving only 2 shades of grey in the default palette. I can see this proving a problem for a lot of games as drawing things like metal and rocks will require the extra greys.

The second Oakley demo requires a few greys for it's sprites so I've made sure there are methods in the language for easily loading 9-bit palettes. For example to load a palette for the sprites from disk using ESXDOS we can do the following:

Display.Load9BitColourPalette(PaletteType.Sprites, "sprites.9pl");

This loads the palette using the ULANext Palette Extension register, which accepts 9-bit colours.

How to generate the file in the first place? Well converting from 24-bit colours to 9-bit colours is fairly easy - just shift the bits for each component right 5 places, leaving the 3 most significant bits. For example if we had an 8-bit R component 11001110 that would become the first 3 bits, or 110. Rather than have some separate utility to do this I've baked support for generating 9-bit palettes on the fly into the Oakley compiler using via the new project support.

A project is basically a folder filled with *.oakley code files, an oakley.json project file and optional resource files, which is currently limited to indexed BMP images. Images can be referenced in the project file and outputs specified, for example:

   "path": "Resources\\bg1.bmp",
   "output": ["image", "palette9"]

This will cause the compiler to output two files, bg1.9pl, which is the 9-bit palette, and bg1.img which is the image itself. The image format is simply a series of bytes, one per pixel, corresponding to the palette index for that pixel. There is currently one other output option, "sprites", which is similar to image except that it outputs the image in 16x16 blocks to be used as sprite patterns.

To edit images I've been using a combination of paint packages, namely Paint.NET because I'm used to it, followed by GIMP for palette specific stuff; as far as I'm aware Paint.NET doesn't have support for limited palettes. To help with that I've written two further snippets of code that I might either package up as utilities to be bundled with the Oakley compiler or allow to be called via options from the compiler itself.

The first is to generate palettes for use by GIMP. This snippet of C# can be used to output both 8 and 9-bit Next palettes. I've tried to interpolate the colours a bit; for example rather than just use the 2/3 bits of a colour shifted left I've tried to divide the range between the available bits. This means that 8-bit white (111-111-11) comes out as #fcfcfc which is much closer to full white (#ffffff) than #e0e0e0 is, which is what you would get by just shifting left. (e0 = 11100000) It will still convert to the corresponding 8-bit colour by trimming the right most bits however. You can grab the code for that, or just grab the GIMP .gpl files it outputs for 8-bit and 9-bit palettes instead.

The second snippet of C# code loads an image and adjusts all the colours to the nearest colour from one of the palettes above and saves as a new image. I couldn't find an easy way to do this with GIMP, especially for the 9-bit palette as it doesn't support palettes larger than 256 colours for a lot of its functions. I found this snippet handy when taking existing graphics rendered in more colours - I could quickly reduce it to the Next colours before further editing. Download the code here.