PaletteMap is fast

August 13th, 2007

This is one of the tricks to my Doom engine. Doom has a 256 color palette, and all colors in the game come from this palette. When an index is found for a pixel, instead of converting it to an rgb value using the palette, write it directly to the image itself. In other words, the last 8 bits (the “blue” section) of every color on the bitmapData should have an index, rather than an actual blue value.

bitmapData.setPixel(x, y, index);

After all processing is complete, you can use paletteMap to convert these indexes for you. PaletteMap uses the value of each byte of an image color (four total) to come up with a new color. See the documentation for paletteMap for more details. Provide an array that converts indexes to rgb values for blue and Flash will do the work for you. If you pass arrays full of zeros for red, green, and alpha, then these values of the associated bytes will have no effect on the resulting color.

bitmapData.paletteMap(bitmapData, bitmapData.rect, new Point(0,0), _blank, _blank, _index2rgb, _blank);

PaletteMap is very fast, which is why this offers a nice improvement in speed. Passing null to a section instead of an array makes it slightly slower on my computer, so that should probably be avoided.

5 Responses to “PaletteMap is fast”

  1. admin Says:

    Sweet, any idea how much faster it is ?

  2. Max Says:

    Hard to tell, but despite adding features that slowed down the engine, adding palettemap saved me about 5ms per frame. In an engine like this, that’s a lifetime.

    This fellow claims it doubled the speed of his engine.

  3. Dewi Morgan Says:

    This is PURE JOY!

    I was wondering “how the hell can this function ever map a palette?”

    On the face of it, you can only map depending on R, G, or B values.

    So say you have a palette like:

    0 = 1,1,1
    1 = 2,2,2
    3 = 3,3,3
    4 = 1,2,3
    5 = 10,20,30

    Then if you want to remap (1,2,3) to (10,20,30) but not remap (1,1,1), (2,2,2) or (3,3,3)… it appeared that there was no way to do it, and I’d have to roll my own.

    But by populating a channel with the palette indexes, rather than the colors themselves, all the problems disappear! *And* as you pointed out, that’s faster for the non-remapped objects, too!

    Double win! You just made my day. Thank you.

  4. Dewi Morgan Says:

    That said… how can you quickly load image data into a single channel?

    It would be way better and faster if I could just do:
    new Rectangle(0,0,w,h),
    data.readBytes(image_data, 0, length)

    rather than the considerably grosser:
    for (var y:int = h -1; y >= 0; y–) {
    for (var x:int = 0; x < w; x++) {
    currentFrameData.setPixel( x, y, data.readUnsignedByte() );

    The problem with the first form is that setPixels() uses readUnsignedInt(), rather than readUnsignedByte(), so borks horribly.

    One way would be to replace readBytes with something that'd read bytes into ints, but that feels like it's just moving the for loop elsewhere, and I'm not sure it'd be any faster.

  5. Sarah Says:

    paletteMap is a weird one. It took me a while to figure it out too It’s been a while since I used it, but from lonokig at my old code I think I can remember how it works.You provide 4 arrays to the paletteMap method, one for each R, G, B and A. The method then runs through each pixel in the image and breaks the colour values out into separate channels. It then uses the value in each channel to pull a new value from the array, using the value as an index. If you omit any of the arrays, it uses the original value.So, if we provided:R = [0x0000110000,0x0000220000,0x0000330000…]G = [0x0000004400,0x0000005500,0x0000006600…]B = [0x0000000077,0x0000000088,0x0000000099…]and the current pixel value was 0xFF000102, our mapped value would be 0xFF115599, as it pulls the value at 0 in the red array, 1 in the green array and 2 in the blue array (and passes the alpha value through). Not sure if you need to use 32 bit colour values, so you might need to check that.hthOwen

Leave a Reply