Because this needs drag-and-drop, the above web-embed is only to make the cart easy to download.
Source code available at GitHub: https://github.com/ChristopherDrum/1bitwonder
What is it?
This cart takes 4 1-bit images and combines them into a single image, where each "layer" of the image is encoded by its pixel color. Set your pal()
to the image you want, then spr()
as normal; that's it. Bit-planes made simple.
I've seen any number of 1-bit graphics games lately (especially with the Porklike sub-genre) and this could possibly quadruple the available space for sprites in games like that. Expand the environment and enemy types, maybe?
In action
Here's four pieces of classic MacPaint artwork being extracted one-by-one via pal()
calls, plus the composite from which the images are being pulled. Here's the composite to make it more clear what this program generates.
(original artwork by Susan Kare, Bert Monroy, Martin Melin, and Brian Thomas)
-->
Alternately you could cut a full-screen (or whatever size) image into slices and put those slices into 1/4 the data space. (this is what I did for Mystery House)
Usage
Upon launch, One Bit Wonder is ready to receive images. Drag in up to four (4) 128x128 pixel 1-bit images. They will be automatically added to a compressed image and previewed for you, using the same pal()
method of image extraction you would use in your own code. After you've added your images...
- Save out the palette code (which really doesn't change much, your artwork depending)
- Export the spritesheet
Controls
Left/right arrows
: cycle through the loaded images, including the full composite.z
: Save out a .txt file that includes the palettes and transparency palettes for your images.x
: Clear out everything and start a new composite (don't forget to export your composite spritesheet first!)
The composite is cstore()
to the cart. Exit the program and run export your_filename.png
as usual to export the spritesheet. You could also, of course, copy/paste from this cart's spritesheet into your target cart.
Note
- Images must be black background with one-color artwork.
- Images must be 128x128 (but you don't have to USE the whole space; I only used the upper 1/4 for Mystery House).
- The palettes generated will respect the original color of the line-art.
- Remember, the palettes generated are just a baseline; replace the values with other colors as needed.
- You don't have to use the transparency palettes. Draw a full-screen image in any two colors, for example.
- The example shows four full-screen images, but you could split an image into four parts and stack those into the storage space of just one of those. Slice and dice as needed.
Background
While working on Mystery House (Remodeled), I needed image compression to fit everything into 64K. Most of the problem was solved with a custom illustration program I wrote called Versawriter-8.
However, there was still the matter of some sprites (game logos, etc) and detailed objects that weren't a great fit for Versawriter-8's vector-based drawing. For those elements I used the spritesheet, but I didn't have the luxury of using the entire spritesheet ROM area. At most, I could only use 1/4 of it, from address 0x0000 to 0x0800.
Looking into Pico-8 discussions on image compression I saw a lot of talk about RLE and LZW compression, trying to minimize the token count of a decoder. I wanted a "back to basics" solution, so this gave me the opportunity to build something I'd been toying around with in my head for a while with an almost-free decompression method.
Last thoughts
Pico-8 color is 4-bit, so here I've compressed one image per bit. But we could do different combinations. Two, 2-bit images, or a 3-bit image and 1-bit image, or a 2-bit plus two 1-bit. I might spend some time making this little tool a bit more robust to a variety of source images and image sizes.
Personally I made an lspr()
function which accepts normal sspr()
parameters plus the "layer" number for the sprite, which does the pal()
setup for the request.
Does this really qualify as compression? You still have 4 images stored in the spritepage at full size, pixel by pixel. You just used a different storage method. This is really closer to palette cycling.
@kimiyoribaka Four images in the space of one is compression, is it not? Maybe I'm not understanding the concern. Is it MAXIMUM compression? No, probably not, but I also didn't claim it was. 4:1 storage with basically free extraction seems not so bad? I mean, you could split a spritesheet into 4 horizontal slices and stack them all into the space of one slice. That is conceptually identical to this demo, but maybe the choice of images is obscuring what's happening?
A simple, easy way to build up such images proved useful to me, so I thought it might be useful to someone else.
EDIT: added another example to show an alternate usage that perhaps better illustrates the compression angle.
My concern is that if someone reads this post to learn about the "basics" of compression, they'll end up with a mostly useless understanding. To use your own claim as an example, 4:1 storage wouldn't be bad at %100 quality. At %25 quality, I'm not sure it'd usually be considered meaningful. The fact that %25 quality is what's needed and intended should make it obvious that your way of framing it is off. "Four images in the space of one" isn't really what's happening. What's happening is you're storing 4 images in the space of 4, but in an environment that uses a different format as default.
@kimiyoribaka I feel I'm pretty explicit about what it does and what it's doing in the very first sentences, "Bit planes made easy". It even says "One Bit" right in the name.
There is no mechanism in Pico-8 to distinguish between "% quality" of images, and so there is no mechanism for being more maximal in the usage of the spritesheet when one's needs are less than full 16 color images. It's kind of all-or-nothing. Whether someone uses 2 colors or 16 colors; they're treated the same by Pico-8's spritesheet.
I can see the angle you're coming from, which is absolutely true from the actual computer science point of view. I absolutely agree. But since Pico-8 itself doesn't distinguish between images of varying "quality", images of fewer colors are (from an average dev's point of view) wasting spritesheet space. I guess what I'm trying to get at is that while you and I know the image is 25% quality, Pico-8 still devotes 100% of the spritesheet ROM to storing it. So from a general audience development point of view, the concept of "% quality" while accurate maybe isn't "practical" framing?
I don't know what else to call it. It allows people with specific aesthetic needs to make efficient use of the given space in a simple way. The 1-bit image limit is stated in the very first words, so I don't feel I've claimed more than what it does. I certainly don't want to lead anyone astray, so I dunno.
maybe ‘packing’ would be ok as a middle ground?
this technique is effectively interleaving image information.
fyi - it has been used already
, possibly the first document being : https://www.tumblr.com/8bit-caaz/171458093376/layering-sprite-data
I applied that technique to pack the x-wing cockpit in Attack of the Deathstar and some other games did.
Nice write up though!
@freds72 I was thinking about it last night and also started thinking about "packing" as the descriptor. I'd no doubt someone had used it, but my searches didn't turn up anything, especially as a general purpose tool to make it easy to do. My apologies to those who were overlooked. Thanks for your insight, as always!
is this possible to combine with the px9 image compressor?
@arrowonionbelly This takes up to 4 1-bit images and packs them into a single 4-bit image. The resulting image is no different than any other image, so you could certainly then apply px9 image compression, yes.
[Please log in to post a comment]