X-Git-Url: https://git.bts.cx/benzene.git/blobdiff_plain/88fe7f92d3fd592960bc2534fcdc534021c870bc..9dd75ef0de4f64d4847b3b50658e322f9b144eea:/src/bz/gfx/aseprite.c diff --git a/src/bz/gfx/aseprite.c b/src/bz/gfx/aseprite.c index 69dc446..046ca24 100644 --- a/src/bz/gfx/aseprite.c +++ b/src/bz/gfx/aseprite.c @@ -1,12 +1,15 @@ #include +#include +#include #include #include #include #include #include -#include // Apparently stb_image.h need this?? +#include // Apparently stb_image.h needs this?? #include +#include // memset #include // Static assertions @@ -23,8 +26,6 @@ typedef double ASE_DOUBLE; typedef uint64_t ASE_QWORD; typedef int64_t ASE_LONG64; - - struct PACKED_STRUCT ASE_STRING { ASE_WORD length; //ASE_BYTE characters[]; @@ -53,14 +54,14 @@ struct PACKED_STRUCT ASE_RECT { typedef struct ASE_RECT ASE_RECT; static_assert(sizeof(ASE_RECT) == 16, "ASE_RECT is wrong size"); -struct PACKED_STRUCT ASE_PIXEL_RBZA { +struct PACKED_STRUCT ASE_PIXEL_RBGA { ASE_BYTE r; ASE_BYTE g; ASE_BYTE b; ASE_BYTE a; }; -typedef struct ASE_PIXEL_RBZA ASE_PIXEL_RBZA; -static_assert(sizeof(ASE_PIXEL_RBZA) == 4, "ASE_PIXEL_RBZA is wrong size"); +typedef struct ASE_PIXEL_RBGA ASE_PIXEL_RBGA; +static_assert(sizeof(ASE_PIXEL_RBGA) == 4, "ASE_PIXEL_RBGA is wrong size"); struct PACKED_STRUCT ASE_PIXEL_GRAYSCALE { ASE_BYTE v; @@ -119,6 +120,25 @@ struct PACKED_STRUCT ASE_Chunk { typedef struct ASE_Chunk ASE_Chunk; static_assert(sizeof(ASE_Chunk) == 6, "ASE_Chunk is wrong size"); +enum ASE_ChunkTypes { + ASE_ChunkType_LayerChunk = 0x2004, + ASE_ChunkType_CelChunk = 0x2005, + ASE_ChunkType_CelExtraChunk = 0x2006, + ASE_ChunkType_ColorProfileChunk = 0x2007, + ASE_ChunkType_ExternalFilesChunk = 0x2008, + ASE_ChunkType_TagsChunk = 0x2018, + ASE_ChunkType_PaletteChunk = 0x2019, + ASE_ChunkType_UserDataChunk = 0x2020, + ASE_ChunkType_SliceChunk = 0x2022, + ASE_ChunkType_TilesetChunk = 0x2023, + + // Ignore these, deprecated or unused... + ASE_ChunkType_OldPaletteChunk1 = 0x0004, + ASE_ChunkType_OldPaletteChunk2 = 0x0011, + ASE_ChunkType_MaskChunk = 0x2016, + ASE_ChunkType_PathChunk = 0x2017, +}; + #if 0 struct PACKED_STRUCT ASE_Chunk_OldPalette { ASE_WORD numPackets; @@ -158,6 +178,10 @@ struct PACKED_STRUCT ASE_Chunk_Layer { typedef struct ASE_Chunk_Layer ASE_Chunk_Layer; static_assert(sizeof(ASE_Chunk_Layer) == 18, "ASE_Chunk_Layer is wrong size"); +enum ASE_Chunk_Layer_BlendModes { + ASE_Chunk_Layer_BlendMode_Normal = 0, // Should always be this +}; + struct PACKED_STRUCT ASE_Chunk_Layer_Tileset { ASE_DWORD tilesetIndex; }; @@ -176,6 +200,13 @@ struct PACKED_STRUCT ASE_Chunk_Cel { typedef struct ASE_Chunk_Cel ASE_Chunk_Cel; static_assert(sizeof(ASE_Chunk_Cel) == 16, "ASE_Chunk_Cel is wrong size"); +enum ASE_Chunk_Cel_CelTypes { + ASE_Chunk_Cel_CelType_RawImage = 0, + ASE_Chunk_Cel_CelType_LinkedCel = 1, + ASE_Chunk_Cel_CelType_CompressedImage = 2, + ASE_Chunk_Cel_CelType_CompressedTilemap = 3, +}; + struct PACKED_STRUCT ASE_Chunk_Cel_RawImage { ASE_WORD pixelWidth; ASE_WORD pixelHeight; @@ -255,6 +286,13 @@ struct PACKED_STRUCT ASE_Chunk_ExternalFiles_Entry { typedef struct ASE_Chunk_ExternalFiles_Entry ASE_Chunk_ExternalFiles_Entry; static_assert(sizeof(ASE_Chunk_ExternalFiles_Entry) == 14, "ASE_Chunk_ExternalFiles_Entry is wrong size"); +enum ASE_Chunk_ExternalFiles_Entry_Types { + ASE_Chunk_ExternalFiles_EntryType_ExternalPalette = 0, + ASE_Chunk_ExternalFiles_EntryType_ExternalTileset = 1, + ASE_Chunk_ExternalFiles_EntryType_PropertiesExtension = 2, + ASE_Chunk_ExternalFiles_EntryType_TileManagement = 3, +}; + struct PACKED_STRUCT ASE_Chunk_Mask { ASE_SHORT positionX; ASE_SHORT positionY; @@ -311,7 +349,34 @@ struct PACKED_STRUCT ASE_Chunk_Palette_Entry_Name { typedef struct ASE_Chunk_Palette_Entry_Name ASE_Chunk_Palette_Entry_Name; static_assert(sizeof(ASE_Chunk_Palette_Entry_Name) == 2, "ASE_Chunk_Palette_Entry_Name is wrong size"); -void *bzGfxLoadAsepriteImage(BZMemoryArenaID arena, size_t *widthOut, size_t *heightOut, const char *identifierFmt, ...) { +static bool readASEHeader(ASE_Header *headerOut, BZResourceID handle) { + bzAssert(headerOut != NULL); + bzResourcesReadBytes(handle, headerOut, sizeof(ASE_Header)); + bzAssert(headerOut->magicNumber == 0xA5E0); + return true; +} + +static bool readASEFrame(ASE_Frame *frameOut, ASE_DWORD *numChunksOut, BZResourceID handle) { + bzAssert(frameOut != NULL); + bzAssert(numChunksOut != NULL); + + bzResourcesReadBytes(handle, frameOut, sizeof(ASE_Frame)); + + ASE_DWORD numChunks = frameOut->___numChunks; + + if (numChunks == 0xFFFF) { + numChunks = frameOut->numChunks; + if (numChunks == 0) { + numChunks = frameOut->___numChunks; // See docs. + } + } + + *numChunksOut = numChunks; + + return true; +} + +void *bzGfxLoadAsepriteImage(BZMemoryArenaID arena, size_t *framesOut, size_t *widthOut, size_t *heightOut, const char *identifierFmt, ...) { bzMakeIdentifier(identifier, identifierFmt); BZResourceID handle = bzResourcesOpenResource("sprite", "assets/sprites/%s.aseprite", identifier); @@ -320,9 +385,7 @@ void *bzGfxLoadAsepriteImage(BZMemoryArenaID arena, size_t *widthOut, size_t *he //PHYSFS_readBytes(handle, data, length); ASE_Header header; - bzResourcesReadBytes(handle, &header, sizeof(header)); - - bzAssert(header.magicNumber == 0xA5E0); + readASEHeader(&header, handle); size_t pixelDataSize; switch (header.depth) { @@ -335,7 +398,7 @@ void *bzGfxLoadAsepriteImage(BZMemoryArenaID arena, size_t *widthOut, size_t *he break; case 32: - pixelDataSize = sizeof(ASE_PIXEL_RBZA); + pixelDataSize = sizeof(ASE_PIXEL_RBGA); break; default: @@ -343,32 +406,65 @@ void *bzGfxLoadAsepriteImage(BZMemoryArenaID arena, size_t *widthOut, size_t *he break; } - uint8_t *outData = (uint8_t *)bzMemoryAlloc(arena, header.width * header.height * pixelDataSize); + uint8_t *outData = (uint8_t *)bzMemoryAlloc(arena, header.frames * header.width * header.height * pixelDataSize); // Side by side loading needs to happen... + *framesOut = (size_t)header.frames; *widthOut = (size_t)header.width; *heightOut = (size_t)header.height; - for (ASE_WORD frame = 0; frame < header.frames; ++frame) { + bzMemoryArenaPushWatermark(arena); + size_t zBufferSize = header.width * header.height * sizeof(ASE_SHORT); + ASE_SHORT *zBuffer = (ASE_SHORT *)bzMemoryAlloc(arena, zBufferSize); + + size_t frameImageSize = header.width * header.height * pixelDataSize; + + for (ASE_WORD frameIdx = 0; frameIdx < header.frames; ++frameIdx) { ASE_Frame frame; - bzResourcesReadBytes(handle, &frame, sizeof(frame)); + ASE_DWORD numChunks = 0; + readASEFrame(&frame, &numChunks, handle); + bzLog("FRAME %d: %d chunks", frameIdx, numChunks); + + memset(zBuffer, 0, zBufferSize); + + bool layerVisible[16]; + size_t nextLayer = 0; + + size_t layerStartOffset[16]; - for (ASE_DWORD chunk = 0; chunk < frame.numChunks; ++chunk) { + for (ASE_DWORD chunk = 0; chunk < numChunks; ++chunk) { size_t chunkStartPosition = bzResourcesTell(handle); ASE_Chunk chunk; bzResourcesReadBytes(handle, &chunk, sizeof(chunk)); switch (chunk.type) { - case 0x2005: { + case ASE_ChunkType_LayerChunk: { + bzLog("LAYER %d: %d chunks", frameIdx, numChunks); + + + ASE_Chunk_Layer layer; + bzResourcesReadBytes(handle, &layer, sizeof(layer)); + + layerVisible[nextLayer] = (layer.flags & 1) > 0; + ++nextLayer; + + bzResourcesSeek(handle, chunkStartPosition + chunk.chunkSize); + break; + } + + case ASE_ChunkType_CelChunk: { + bzLog("CELL %d: %d chunks", frameIdx, numChunks); ASE_Chunk_Cel cel; bzResourcesReadBytes(handle, &cel, sizeof(cel)); - if (cel.celType == 2) { +///// ASE_Chunk_Cel_CelType_LinkedCel aaaahhh + + if (layerVisible[cel.layerIndex] && cel.celType == ASE_Chunk_Cel_CelType_CompressedImage) { bzMemoryArenaPushWatermark(arena); ASE_Chunk_Cel_CompressedImage image; bzResourcesReadBytes(handle, &image, sizeof(image)); - size_t compressedSize = chunk.chunkSize - sizeof(ASE_Chunk_Cel_CompressedImage) - sizeof(ASE_Chunk); + size_t compressedSize = chunk.chunkSize - sizeof(ASE_Chunk_Cel_CompressedImage) - sizeof(ASE_Chunk_Cel) - sizeof(ASE_Chunk); int8_t *compressedData = bzMemoryAlloc(arena, compressedSize); // If we do this on the stack (alloca) the Playdate will explode! bzResourcesReadBytes(handle, compressedData, compressedSize); @@ -381,16 +477,21 @@ void *bzGfxLoadAsepriteImage(BZMemoryArenaID arena, size_t *widthOut, size_t *he size_t outX = cel.positionX + x; size_t outY = cel.positionY + y; - for (size_t i = 0; i < pixelDataSize; ++i) { - outData[(outY * header.width + outX) * pixelDataSize + i] = imagePixelData[(y * image.pixelWidth + x) * pixelDataSize + i]; + ASE_SHORT *currentZ = &zBuffer[outY * header.width + outX]; + + if (*currentZ <= cel.zIndex) { + for (size_t i = 0; i < pixelDataSize; ++i) { + uint8_t pixelData = imagePixelData[(y * image.pixelWidth + x) * pixelDataSize + i]; + if (pixelData > 0) { // FIXME, alpha... + outData[frameIdx * frameImageSize + (outY * header.width + outX) * pixelDataSize + i] = pixelData; + } + } + *currentZ = cel.zIndex; } } } bzMemoryArenaPopWatermark(arena); - - // FIXME - //bzResourcesSeek(handle, chunkStartPosition + chunk.chunkSize); } else { // Skip this chunk... bzResourcesSeek(handle, chunkStartPosition + chunk.chunkSize); @@ -407,6 +508,8 @@ void *bzGfxLoadAsepriteImage(BZMemoryArenaID arena, size_t *widthOut, size_t *he } } + bzMemoryArenaPopWatermark(arena); + bzResourcesCloseResource(handle); return outData; @@ -416,9 +519,7 @@ size_t bzGfxLoadAsepritePalette(uint32_t *colorsOut, size_t maxColors, const cha BZResourceID handle = bzResourcesOpenResource("palette", "assets/palettes/%s", filename); ASE_Header header; - bzResourcesReadBytes(handle, &header, sizeof(header)); - - bzAssert(header.magicNumber == 0xA5E0); + readASEHeader(&header, handle); size_t colorCount = 0; @@ -426,14 +527,23 @@ size_t bzGfxLoadAsepritePalette(uint32_t *colorsOut, size_t maxColors, const cha ASE_Frame frame; bzResourcesReadBytes(handle, &frame, sizeof(frame)); - for (ASE_DWORD chunk = 0; chunk < frame.numChunks; ++chunk) { + ASE_DWORD numChunks = frame.___numChunks; + + if (numChunks == 0xFFFF) { + numChunks = frame.numChunks; + if (numChunks == 0) { + numChunks = frame.___numChunks; // See docs. + } + } + + for (ASE_DWORD chunk = 0; chunk < numChunks; ++chunk) { size_t chunkStartPosition = bzResourcesTell(handle); ASE_Chunk chunk; bzResourcesReadBytes(handle, &chunk, sizeof(chunk)); switch (chunk.type) { - case 0x2019: { + case ASE_ChunkType_PaletteChunk: { ASE_Chunk_Palette palette; bzResourcesReadBytes(handle, &palette, sizeof(palette));