Created: 15-07-2021
Last update: 15-07-2021
Back Previous Next

Beginners howto 11 - too many colours and a use for palette groups


Unless a KiSS set uses Cherry KiSS (non-palette, truecolour KiSS cels), or is meant for the viewer UltraKiSS, which can use GIFs and PNGs instead of cels, there is a strict maximum of 256 colours (255 usable colours, and one transparent background colour), whether in the form of 17 16-colour palettes, or one 256-colour palette.

That is to say, 256 colours in one "set", in the sense of "page", of the KiSS set. Each KiSS palette file or "kcf", whether 16 or 256 colours, has up to ten palette groups, meaning, it is ten palettes rolled into one. That's a maximum of 2560 colours you can use in a KiSS set. But only one palette group can be used per page.

So you can have one page using 255 colors to depict every cel in the scene tinged red by a sunset, and another page where everything is in 255 grey tones. But you can't display one of the red cels in the grey set. Even if you put it there, it would come out in grey tones, and would probably look weird. As I explained in KiSS basics, cels and palettes in KiSS are comparable to painting by numbers: a pixel in a cel has a palette entry number, like "25", not a colour value. In one palette (or internal palette with a palette group number) colour 25 might be beige, in another it might be turquoise.

(For completeness' sake, I should mention that FKiSS4 has an action "setkcf()" with which to assign a cel or cel group another kcf on the fly. In this case, where I have to juggle too many colours in a way that would work even in plain KiSS, that is of no use to me.)

How did this even come up?

When making a demo set for the progress tracking howto, I used a graphics program with layers and truecolour image depth, as one does in the 21st century. And I had a little fun with fonts and colours. And I ended up with a collection of images waaay over 256 colours. Since one big 256-colour kcf is more economical than lots of little 16-colour ones (if two cels with different 16-colour palettes use the same colour, I need to put that same colour in both palettes, and so can use one less unique colour value in the set: the total was 255, but it just became 254) I tried to colour-reduce all the images so their colour ranges, including the transparent colour, would fit inside one 256-colour palette.

The tried-and-true way of doing this is creating one huge empty image file, flood-filling it with the transparent background colour, pasting on it all the pictures that will become cels, saving it as the truecolour image it no doubt is, reducing it to 256 colours (preferably without dithering) and saving it as an indexed image to make sure it has an internal palette: finally, also make sure that the transparent colour is the first entry (colour 0) in this palette. Well-known graphics programs like GIMP, PhotoShop etc. have options to change the order of colours in a palette, so check their helpfiles; if it comes to the worst, export the palette as a text file, cut and paste the numbers around and re-import the palette. This large file can be used to make a KCF, and the little images cut out of it and pasted into their own files, to be converted to palette-using cels. (Under Linux, unless you want CKiSS cels, the only way I've found to make cels is to use a DOS conversion tool in DosBox, or install DosBox-X and any old Windows version up to Windows Millennium, and use old Windows conversion tools.)

Or, as I ended up doing it in GIMP, this large file is not cut up, but left opened so that its indexed palette will show up in the list of available palettes, and the truecolour images that must become cels are opened one by one, and have their colours reduced by loading this indexed palette, and are saved to GIFs or PNGs. (When colour-reducing in this way, make sure that the box "Remove unused and duplicate colors from colormap" is unticked, because every little image is supposed to have ALL the colours.) But first, I needed to make an image with the right palette file.


The unticked setting outlined in red.

The big image file palette creation method didn't work for me, first because the warning cel used a gradient and, as such, had an astronomical amount of colours, and second because computers are not intelligent, especially not when reducing colours. There may be some dedicated programs that do it right, but the default method consists of:

"Let's calculate an average range of colours that the existing colours don't quite fit into, so the image acquires banding and shifts colour."

"Hey, this shade of colour occurs in only a few pixels. Let's merge it with that shade next to it which has a close numerical value but actually looks quite different, for a nice weird effect."

Here is another shade that's only used by one pixel, let's NOT merge it with its neighbour shade which looks indistinguishable from it, repeat for ten shades or so, now we have too few different colours available to colour-reduce in a way that doesn't look washed out!"

At least when the total number of colours in an image is already under the given maximum when it's changed to an indexed image, GIMP leaves the color values intact.

My method was to divide all the little images into hue groups, like stuff with green tones, stuff with gold tones and blue stuff, put each hue group in its own big image file, reduce each image file to less than 256 colours (actually checking how many pixels each colour was used in, maybe replacing it by another close colour from the palette to reduce the total number of palette entries) and then combine the palettes. It was arduous, and took several days. And I was forced to exclude that warning cel. It already needed 256 colours by itself. Now there is a standard called "enhanced KiSS", which allows viewers to load more than one 256-colour palette file in a set, but I didn't want a simple demo set to require "enhanced KiSS", even if it looked like a Christmas Easter birthday cake. I basically needed two 256-colour palettes where only one was allowed. How would I solve this?

1. The extra palette would only be needed for one, screen-filling cel.

This one screen-filling cel does not have to be on the same page as the other cels. In fact, since it's a warning cel that means "your viewer can't handle this set, close your viewer now", if the warning cel shows, all the other cels shouldn't. So there is no reason for the palettes to come in conflict with each other.

2. I can add the palette of the warning cel to the main palette file as a new palette group: group 1. (The first palette group is group 0.) I can set the default palette group to 1 in the object coordinates clock, and add FKiSS code that uses version() to unmap the warning cel and set the palette group back to 0.

3. What if the viewer doesn't understand FKiSS code? Then the warning cel stays mapped and the palette group stays 1, because they are set like that in the object declaration block and the object definition block, neither of which are the FKiSS code block.

How absolutely brilliant.

After colour-reducing the warning image to create the second palette, and combining this palette with the set's main palette in UltraKiSS (how to do this is explained in its documentation) this is the simplest form of the cnf:

; The progress tracker KiSS demo set by Cal, 2021, FKiSS2 version
;
%progtrck.kcf ; the palette file with two internal palette groups
;
(400,300)
[0
;
; Object declaration block
;
; warning
#0.999 warning.cel      *0 :0                   ; your viewer doesn't grok FKiSS
#0.999 warning2.cel     *0 :0                   ; your viewer doesn't do FKiSS2
; warning font:
; intro screen
#1.999 intro.cel        *0 :0                   ; introduction, explanation
;[more cels follow]
;
; Both object declaration lines use palette *0 (progtrck.kcf).

; Here begins the code block:
;@EventHandler
; If the viewer handles FKiSS, it makes the first warning disappear:
;@initialize()
;@  unmap("warning.cel") ; or unmap(#0)
;
; A combination of version() and a dummy alarm unmaps the second warning:
;@alarm(1) ; dummy wrapper for FKiSS-only viewers
;@version(2)
;@  unmap("warning2.cel")
;@  changecol(0)  ; go back to first palette group
;
;[more FKiSS code]
;
; Here begins the object coordinates block:
;  ** Object coordinates **
;
; set #0
; This "1" after the "$" means "start with palette group 1 active"
$1 20,18 0,0
;
; set #1
$0 * *
;
; set #2
$0 * *
;
[etc. until]
;
; set #9
$0 * *

In UltraKiSS, this works a treat. Fortunately, I was wise enough to also check in another viewer, GnomeKiSS to be exact. And in GnomeKiSS, despite the "changecol(0)" under version(), the palette group stays 1, so what should be a pink intro screen is drab and brown from using the warning picture's palette.

I suspect this is because version() is processed before the first page in the set appears, and when the page does appear, it immediately takes the palette group from its line in the object definition block: the number after the dollar sign. I know that changecol() works, but clearly it should happen a bit later. But that's what alarms are for. The improved FKiSS code block goes like this:

;@EventHandler
; If the viewer handles FKiSS, it makes the first warning disappear:
;@initialize()
;@  unmap("warning.cel") ; or unmap(#0)
;
; A combination of version() and a dummy alarm unmaps the second warning:
;@alarm(1) ; dummy wrapper for FKiSS-only viewers
;@version(2)
;@  unmap("warning2.cel") changecol(0) ; this changecol works in UltraKiSS
;@  timer(0,1) ; in case the above changecol() doesn't work
;
;@alarm(0)
;@  changecol(0)

A possible problem, depending on how fast the viewer loads the set, is that the alarm might go off and cause a changecol() too soon, before the page is rendered, so the page still ends up its default palette group 1, or the alarm might take too long, causing the intro cel to be displayed in brown tones before changecol(0) kicks in and gives it the right colour. The shortest possible timer duration is 1, which is one millisecond, and in GnomeKiSS, that works. Apparently that one millisecond is sufficient delay.

In a FKiSS-only cnf, I can dispense with warning2.cel and I don't need version(), because a viewer that doesn't understand FKiSS will ignore anything under ";@EventHandler" anyway. As timers should not be set under initialize(), the timer() command goes under begin() instead of version():

;@EventHandler
; If the viewer handles FKiSS, it makes the first warning disappear:
;@initialize()
;@  unmap("warning.cel") ; or unmap(#0)
;
;@begin()
;@  timer(0,1)
;
;@alarm(0)
;@  changecol(0)

I tried putting changecol() directly under begin(), which is supposed to fire when the set has finished loading, but apparently "finished loading" still happens just before "started rendering first page with initial palette group". It's a good thing viewers generally have palette group controls, so the player can change the palette group by hand if needed.

The code examples above are used in the demo set for the tracking progress howto.



Back Previous Next