I want to put all my images into a single binary file and handle allocating and loading them myself.
It doesn't seem it's possible to do using LCDBitmap
.
playdate->graphics->loadBitmap
only accepts files. I don't see any other way of creating a LCDBitmap using data buffer.
I know this is doable with HEBitmap, but I need to convert my images to heb format, which takes about 25% more space on disk.
I see two different options:
-
I could write my own pdi files (LCDBitmap) from buffer using the reverse engineered spec and draw with playdate->graphics->drawBitmap
.
-
I could use playdate->graphics->getFrame
and markUpdatedRows
to draw pixels from my image buffer.
Is this something someone already did?
It seems to be a common thing to pack all images into a single file. I feel I'm missing something obvious.
You can actually do this already with the SDK. When you call getBitmapData
the uint8_t*
is the actual pixel data. if you change the data there it will change it for the next time you call draw. Here is a example of creating a pure white bitmap and then filling the data with all 0
for black.
//Allocate the LCDBitmap with 16/16 px and fill white
LCDBitmap *testBitmap = pd->graphics->newBitmap( 16, 16, kColorWhite );
// Get a pointer to the bitmap data
uint8_t testBitmapData;
int rowBytes;
pd->graphics->getBitmapData( testBitmap, 0, 0, &rowBytes, 0, &testBitmapData );
// fill the raw data with all 0s
memset( testBitmapData, 0, rowBytes * 16 );
// draw the bitmap, we changed the underlying data so it should now be black.
pd->graphics->drawBitmap( testBitmap, 0, 0, 0 );
You can read more about the format the pixel data is in getBitmapData function on the C documentation.
Hey thanks, that's useful.
I'm one step closer.
Now I need to figure out the .pdi image format conversion to bitmap.
If I load images as LCDBitmap*
using loadBitmap()
, I can skip the first 16 bytes of header to the data. If I copy that data to the pixel data from getBitmapData()
of another LCDBitmap*
, everything renders correctly (as expected).
I can't use this approach if I'm packing all assets into a binary file, since loadBitmap
expects a file path. (and each image would get allocated twice)
If instead I open the .pdi file myself, the header matches what's here.
However, the data is still in .pdi format and not bitmap data like in LCDBitmap*
. I can't copy that directly to the pixel data of getBitmapData()
.
So I just need to do this conversion.
Or I could use png files directly and convert that to bitmap instead.
You shouldn't need to worry about the format of the pdi file. If you build your packed file and call it something like img.pak
and put it somewhere in the /Source
dir. Then it will just get copied into the pdx.
Then you can
SDFile* pakFile playdate->file->open( "img.pak", kFileRead );
PakHeader header;
playdate->file->read( pakFile, &header, sizeof( PakHeader ) );
// parse offset and packed image info
// create array of offsets and images for each of the packed images
for ( int i =0; i < numOfOffsets; ++i )
{
// read the raw pixel data straight out of the file into image data buffer.
playdate->file->seek( pakFile, offsets[i], SEEK_SET );
playdate->file->read( pakFile, images[i], imgSizeInBytes );
}
playdate->file->close( pakFile );
// done
I haven't tested this code, it has no safety (should be checking the results of all these functions)
In what format do you pack the images' pixels? bitmap?
Well above is a really naive implementation and just would be the raw bytes of the whole image in playdate byte order.
The pdi format is pretty good because it only stores the pixels that it needs. and then stores meta for the offset where pixels start and end. then pads as it loads the image. (you linked the github with the format earlier)
Ideally, you know how you're packing them best. So you decide how you want to go about that. if you know that all images in a pack have the same clip_rect then you only need to store those metrics once in the pak header and then can load each packed images straight off the disk into that location within the target LCDBitmaps.
I hope this makes sense?