Log In  

I wondered if it was practical to save tokens and do more "visual" editing of palettes, color ramps, etc. by embedding a palette in the sprite sheet.

The answer is "no", but it was an interesting experiment:

Cart #kistaro_sprite_sheet_palette-1 | 2023-10-07 | Code ▽ | Embed ▽ | License: CC4-BY-NC-SA

This cartridge contains two different formats for reading a palette out of a sprite sheet: as a pair of 4x4 blocks (total space 8x4) and as a pair of rows (total space 16x2). One 16-pixel unit is the "palette" part, the other is "control bits". The control bits are:

  • 0x1 -- "high color" (add 128 to the color)
  • 0x2 -- transparent: mark this color as transparent in sprites
  • 0x4 -- opaque: mark this color as not transparent in sprites
  • 0x8 -- colorless: do not include this color in the palette

Row position from 0 to 15 represents the palette index in row format; it's packed into four rows of four columns each (starting at 0 in the upper left, same as the layout in the sprite editor) for the box format. The control bit box is to the right of the palette box; the control bit row is under the palette row.

Flag 0x8 means the color and value of flag 0x1 are ignored, but the index is still considered for transparency palettes (control bits 0x2 and 0x4). If neither 0x2 nor 0x4 is set for any color in the palette, then palette:do_palt() (including do_both) uses a loop of palt calls and skips the color that has no transparency specified; if every color has an explicit transparency (no "do not change previous value" setting) then the transparency bits are merged into an int at load time and do_palt calls palt only once with a complete transparency mask. There's no comparable optimization/pessimization based on whether there are any skipped colors (flag 0x8) in the set, since pal with a table argument exists and is perfectly happy to skip colors.

This hasn't been through token squishing - this is intended to be legible, not efficient -- but it convinced me that there are basically no circumstances where this is better than just writing out palettes. It's slightly unfortunate that there's no way to force split to start at index 0 but the loop to shuffle everything down an index is still shorter and more performant than any plausible "read palette out of sprite sheet" loop, and moving cell 16 to position 0 is even more efficient at the cost of slightly more confusing representation of the palette in the code.

Anyway, this is here as a curiosity. I'm definitely open to the idea that I have underestimated just how much optimization is possible and this might actually be viable in some cases! (Especially with control bit mechanics and square-shaped palette support removed entirely. Control bits came in mostly because I needed the "high color" bit and then decided what to do with the remaining three bits.)

The cartridge itself is a palette demo; use X to switch between draw and screen palettes, and Z to switch between blocks mode and rows mode. The row and block palettes are not the same, I was just experimenting some. You're encouraged to open up the cartridge in the editor and replace the palettes, maybe it's still a useful tool for visualizing those in some way. The palette example display is just color bars (from 0 to 15) with a "zigzag" sprite in each color drawn in a stripe over it, finished with a "color pattern block" sprite. This isn't intended to show dithering or anything, it just points out how the transparency color works. The 0-colored sprite column has a 5-color outline (dark gray by default) because 0 is the background color for all the other zigzags and it would just be an all-zeroes sprite otherwise; maybe I should have left it that way, dunno.

P#135593 2023-10-07 19:38 ( Edited 2023-10-07 19:44)

not sure what you are trying to do.
certainly storing palettes on spritesheet is a common technique (say to store a gradient).

this requires very little code to change game colors:

function _draw()
— set palette from the first 16 pixels of the spritesheet
pal({peek(0x0,16})
— draw things
spr(…)
…
end
P#135604 2023-10-08 06:14 ( Edited 2023-10-08 10:03)

Okay, I didn't know peek had that mode - useful! It seems like it won't remap color 0 (which is probably fine because that is usually transparent), and it doesn't use the first 16 pixels, it uses the first 32 pixels -- each pixel is half a byte. It's little-endian, so it would pick up the 0th, 2nd, 4th, 6th, etc... pixels for the low byte, and allows putting color 8 in the high byte to get the high colors (128+) for screen palettes; it also allows color pairs for representing fillp secondaries, with somewhat different code. The multi-output peek definitely helps.

P#135631 2023-10-08 19:36

[Please log in to post a comment]