From: Ben Sherratt Date: Sat, 25 May 2024 21:02:50 +0000 (+0100) Subject: Fixed Aseprite rendering system X-Git-Url: https://git.bts.cx/benzene.git/commitdiff_plain/refs/heads/mainline Fixed Aseprite rendering system --- diff --git a/src/bz/gfx/aseprite.c b/src/bz/gfx/aseprite.c index 046ca24..b2a829f 100644 --- a/src/bz/gfx/aseprite.c +++ b/src/bz/gfx/aseprite.c @@ -376,13 +376,141 @@ static bool readASEFrame(ASE_Frame *frameOut, ASE_DWORD *numChunksOut, BZResourc return true; } +static void renderChunkCel(uint8_t *outData, ASE_SHORT *zBuffer, BZMemoryArenaID arena, BZResourceID handle, ASE_WORD layerIndex, const ASE_Header *header, size_t pixelDataSize, size_t frameIdx, size_t frameStartPositions[]) { + bzResourcesSeek(handle, frameStartPositions[frameIdx]); + + ASE_Frame frame; + ASE_DWORD numChunks; + readASEFrame(&frame, &numChunks, handle); + + for (ASE_DWORD chunkIdx = 0; chunkIdx < numChunks; ++chunkIdx) { + size_t chunkStartPosition = bzResourcesTell(handle); + + ASE_Chunk chunk; + bzResourcesReadBytes(handle, &chunk, sizeof(chunk)); + + if (chunk.type == ASE_ChunkType_CelChunk) { + ASE_Chunk_Cel cel; + bzResourcesReadBytes(handle, &cel, sizeof(cel)); + + if (cel.layerIndex == layerIndex) { + if (cel.celType == ASE_Chunk_Cel_CelType_LinkedCel) { + ASE_Chunk_Cel_LinkedCel linkedCel; + bzResourcesReadBytes(handle, &linkedCel, sizeof(linkedCel)); + renderChunkCel(outData, zBuffer, arena, handle, layerIndex, header, pixelDataSize, linkedCel.framePosition, frameStartPositions); + } else if (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_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); + + size_t imagePixelDataSize = image.pixelWidth * image.pixelHeight * pixelDataSize; + int8_t *imagePixelData = bzMemoryAlloc(arena, imagePixelDataSize); + stbi_zlib_decode_buffer(imagePixelData, imagePixelDataSize, compressedData, compressedSize); + + for (size_t y = 0; y < image.pixelHeight; ++y) { + for (size_t x = 0; x < image.pixelWidth; ++x) { + size_t outX = cel.positionX + x; + size_t outY = cel.positionY + y; + + //ASE_SHORT *currentZ = &zBuffer[outY * header->width + outX]; + //if (*currentZ > layerIndex) { + for (size_t i = 0; i < pixelDataSize; ++i) { + uint8_t pixelData = imagePixelData[(y * image.pixelWidth + x) * pixelDataSize + i]; + if (pixelData > 0) { // FIXME, alpha... + outData[outY * header->width + outX * pixelDataSize + i] = pixelData; + } + } + //*currentZ = layerIndex;//cel.zIndex; + //} + } + } + + bzMemoryArenaPopWatermark(arena); + } + + // Found what we needed to render... + break; + } + } + + bzResourcesSeek(handle, chunkStartPosition + chunk.chunkSize); + } +} + +static bool renderCelsForLayer(uint8_t *outData, ASE_SHORT *zBuffer, BZMemoryArenaID arena, BZResourceID handle, ASE_WORD layerIndex, const ASE_Header *header, size_t pixelDataSize) { + size_t framesStartPosition = bzResourcesTell(handle); + + // Layers are .. complicated, you only really need to read them for the first frame... + ASE_Frame firstFrame; + ASE_DWORD firstFrameChunks; + readASEFrame(&firstFrame, &firstFrameChunks, handle); + + bool layerExists = false; + bool layerVisible = false; + for (ASE_DWORD chunkIdx = 0, layerCount = 0; chunkIdx < firstFrameChunks; ++chunkIdx) { + size_t chunkStartPosition = bzResourcesTell(handle); + + ASE_Chunk chunk; + bzResourcesReadBytes(handle, &chunk, sizeof(chunk)); + + if (chunk.type == ASE_ChunkType_LayerChunk) { + ASE_Chunk_Layer layer; + bzResourcesReadBytes(handle, &layer, sizeof(layer)); + + if (layerCount == layerIndex) { + layerExists = true; + layerVisible = (layer.flags & 1) > 0; // FIXME, named flag + break; + } else { + layerCount++; + } + }// else { + // Skip this chunk... + bzResourcesSeek(handle, chunkStartPosition + chunk.chunkSize); + //} + } + + if (layerExists && layerVisible) { + bzResourcesSeek(handle, framesStartPosition); + + size_t *frameStartPositions = alloca(header->frames * sizeof(size_t)); // FIXME, alloca -> tmp + + for (ASE_WORD frameIdx = 0; frameIdx < header->frames; ++frameIdx) { + frameStartPositions[frameIdx] = bzResourcesTell(handle); + + ASE_Frame frame; + ASE_DWORD numChunks; + readASEFrame(&frame, &numChunks, handle); + + for (ASE_DWORD chunkIdx = 0; chunkIdx < numChunks; ++chunkIdx) { + size_t chunkStartPosition = bzResourcesTell(handle); + + ASE_Chunk chunk; + bzResourcesReadBytes(handle, &chunk, sizeof(chunk)); + + if (chunk.type == ASE_ChunkType_CelChunk) { + uint8_t *frameOutData = &outData[frameIdx * header->height * header->width * pixelDataSize]; + ASE_SHORT *frameZBuffer = NULL;//&zBuffer[frameIdx * header->height * header->width * sizeof(ASE_SHORT)]; + renderChunkCel(frameOutData, frameZBuffer, arena, handle, layerIndex, header, pixelDataSize, frameIdx, frameStartPositions); + } + + bzResourcesSeek(handle, chunkStartPosition + chunk.chunkSize); + } + } + } + + return layerExists; +} + 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); - //PHYSFS_sint64 length = PHYSFS_fileLength(handle); - //void *data = alloca(length); - //PHYSFS_readBytes(handle, data, length); ASE_Header header; readASEHeader(&header, handle); @@ -402,121 +530,37 @@ void *bzGfxLoadAsepriteImage(BZMemoryArenaID arena, size_t *framesOut, size_t *w break; default: - assert(false);//, "Invalid image"); + bzAssertMessage(false, "Invalid image"); break; } - uint8_t *outData = (uint8_t *)bzMemoryAlloc(arena, header.frames * header.width * header.height * pixelDataSize); // Side by side loading needs to happen... + uint8_t *outData = (uint8_t *)bzMemoryAlloc(arena, header.frames * header.height * header.width * pixelDataSize); // Side by side loading needs to happen... *framesOut = (size_t)header.frames; *widthOut = (size_t)header.width; *heightOut = (size_t)header.height; - bzMemoryArenaPushWatermark(arena); - size_t zBufferSize = header.width * header.height * sizeof(ASE_SHORT); - ASE_SHORT *zBuffer = (ASE_SHORT *)bzMemoryAlloc(arena, zBufferSize); + //bzMemoryArenaPushWatermark(arena); + ASE_SHORT *zBuffer = NULL;//(ASE_SHORT *)bzMemoryAlloc(arena, header.frames * header.height * header.width * sizeof(ASE_SHORT)); + //memset(zBuffer, 0, header.frames * header.height * header.width * sizeof(ASE_SHORT)); // FIXME - size_t frameImageSize = header.width * header.height * pixelDataSize; - - for (ASE_WORD frameIdx = 0; frameIdx < header.frames; ++frameIdx) { - ASE_Frame frame; - ASE_DWORD numChunks = 0; - readASEFrame(&frame, &numChunks, handle); - bzLog("FRAME %d: %d chunks", frameIdx, numChunks); + size_t startPosition = bzResourcesTell(handle); + for (ASE_WORD layerIndex = 0; ; ++layerIndex) { + // Reset... + bzResourcesSeek(handle, startPosition); - memset(zBuffer, 0, zBufferSize); - - bool layerVisible[16]; - size_t nextLayer = 0; - - size_t layerStartOffset[16]; - - 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 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)); - -///// 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_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); - - size_t imagePixelDataSize = image.pixelWidth * image.pixelHeight * pixelDataSize; - int8_t *imagePixelData = bzMemoryAlloc(arena, imagePixelDataSize); - stbi_zlib_decode_buffer(imagePixelData, imagePixelDataSize, compressedData, compressedSize); - - for (size_t y = 0; y < image.pixelHeight; ++y) { - for (size_t x = 0; x < image.pixelWidth; ++x) { - size_t outX = cel.positionX + x; - size_t outY = cel.positionY + y; - - 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); - } else { - // Skip this chunk... - bzResourcesSeek(handle, chunkStartPosition + chunk.chunkSize); - } - - break; - } - - default: - // Skip this chunk... - bzResourcesSeek(handle, chunkStartPosition + chunk.chunkSize); - break; - } + bool rendered = renderCelsForLayer(outData, zBuffer, arena, handle, layerIndex, &header, pixelDataSize); + if (rendered == false) { + break; } } - - bzMemoryArenaPopWatermark(arena); - bzResourcesCloseResource(handle); + //bzMemoryArenaPopWatermark(arena); return outData; } size_t bzGfxLoadAsepritePalette(uint32_t *colorsOut, size_t maxColors, const char *filename) { - BZResourceID handle = bzResourcesOpenResource("palette", "assets/palettes/%s", filename); + BZResourceID handle = bzResourcesOpenResource("palette", "assets/palettes/%s.aseprite", filename); ASE_Header header; readASEHeader(&header, handle); @@ -525,16 +569,8 @@ size_t bzGfxLoadAsepritePalette(uint32_t *colorsOut, size_t maxColors, const cha for (ASE_WORD frame = 0; frame < header.frames; ++frame) { ASE_Frame frame; - bzResourcesReadBytes(handle, &frame, sizeof(frame)); - - ASE_DWORD numChunks = frame.___numChunks; - - if (numChunks == 0xFFFF) { - numChunks = frame.numChunks; - if (numChunks == 0) { - numChunks = frame.___numChunks; // See docs. - } - } + ASE_DWORD numChunks; + readASEFrame(&frame, &numChunks, handle); for (ASE_DWORD chunk = 0; chunk < numChunks; ++chunk) { size_t chunkStartPosition = bzResourcesTell(handle);