1 #include <bz/gfx/aseprite_internal.h>
3 #include <bz/debug/assert.h>
4 #include <bz/debug/log.h>
5 #include <bz/math/math.h>
6 #include <bz/memory/allocator.h>
7 #include <bz/renderer/palette_internal.h>
8 #include <bz/resources/resource.h>
9 #include <bz/types/identifier_internal.h>
10 #include <stdio.h> // Apparently stb_image.h needs this??
11 #include <stb_image.h>
12 #include <string.h> // memset
14 #include <assert.h> // Static assertions
16 #define PACKED_STRUCT __attribute__((__packed__))
18 typedef uint8_t ASE_BYTE
;
19 typedef uint16_t ASE_WORD
;
20 typedef int16_t ASE_SHORT
;
21 typedef uint32_t ASE_DWORD
;
22 typedef uint32_t ASE_LONG
;
23 typedef uint32_t ASE_FIXED
;
24 typedef float ASE_FLOAT
;
25 typedef double ASE_DOUBLE
;
26 typedef uint64_t ASE_QWORD
;
27 typedef int64_t ASE_LONG64
;
29 struct PACKED_STRUCT ASE_STRING
{
31 //ASE_BYTE characters[];
33 typedef struct ASE_STRING ASE_STRING
;
34 static_assert(sizeof(ASE_STRING
) == 2, "ASE_STRING is wrong size");
36 struct PACKED_STRUCT ASE_POINT
{
40 typedef struct ASE_POINT ASE_POINT
;
41 static_assert(sizeof(ASE_POINT
) == 8, "ASE_POINT is wrong size");
43 struct PACKED_STRUCT ASE_SIZE
{
47 typedef struct ASE_SIZE ASE_SIZE
;
48 static_assert(sizeof(ASE_SIZE
) == 8, "ASE_SIZE is wrong size");
50 struct PACKED_STRUCT ASE_RECT
{
54 typedef struct ASE_RECT ASE_RECT
;
55 static_assert(sizeof(ASE_RECT
) == 16, "ASE_RECT is wrong size");
57 struct PACKED_STRUCT ASE_PIXEL_RBGA
{
63 typedef struct ASE_PIXEL_RBGA ASE_PIXEL_RBGA
;
64 static_assert(sizeof(ASE_PIXEL_RBGA
) == 4, "ASE_PIXEL_RBGA is wrong size");
66 struct PACKED_STRUCT ASE_PIXEL_GRAYSCALE
{
70 typedef struct ASE_PIXEL_GRAYSCALE ASE_PIXEL_GRAYSCALE
;
71 static_assert(sizeof(ASE_PIXEL_GRAYSCALE
) == 2, "ASE_PIXEL_GRAYSCALE is wrong size");
73 struct PACKED_STRUCT ASE_PIXEL_INDEXED
{
76 typedef struct ASE_PIXEL_INDEXED ASE_PIXEL_INDEXED
;
77 static_assert(sizeof(ASE_PIXEL_INDEXED
) == 1, "ASE_PIXEL_INDEXED is wrong size");
79 struct PACKED_STRUCT ASE_Header
{
81 ASE_WORD magicNumber
; // 0xA5E0
90 ASE_BYTE transparentColorIdx
;
91 ASE_BYTE ___ignore
[3];
99 ASE_BYTE ___reserved
[84];
101 typedef struct ASE_Header ASE_Header
;
102 static_assert(sizeof(ASE_Header
) == 128, "ASE_Header is wrong size");
104 struct PACKED_STRUCT ASE_Frame
{
105 ASE_DWORD frameBytes
;
106 ASE_WORD magicNumber
; // 0xF1FA
107 ASE_WORD ___numChunks
;
109 ASE_BYTE ___reserved
[2];
112 typedef struct ASE_Frame ASE_Frame
;
113 static_assert(sizeof(ASE_Frame
) == 16, "ASE_Frame is wrong size");
115 struct PACKED_STRUCT ASE_Chunk
{
120 typedef struct ASE_Chunk ASE_Chunk
;
121 static_assert(sizeof(ASE_Chunk
) == 6, "ASE_Chunk is wrong size");
123 enum ASE_ChunkTypes
{
124 ASE_ChunkType_LayerChunk
= 0x2004,
125 ASE_ChunkType_CelChunk
= 0x2005,
126 ASE_ChunkType_CelExtraChunk
= 0x2006,
127 ASE_ChunkType_ColorProfileChunk
= 0x2007,
128 ASE_ChunkType_ExternalFilesChunk
= 0x2008,
129 ASE_ChunkType_TagsChunk
= 0x2018,
130 ASE_ChunkType_PaletteChunk
= 0x2019,
131 ASE_ChunkType_UserDataChunk
= 0x2020,
132 ASE_ChunkType_SliceChunk
= 0x2022,
133 ASE_ChunkType_TilesetChunk
= 0x2023,
135 // Ignore these, deprecated or unused...
136 ASE_ChunkType_OldPaletteChunk1
= 0x0004,
137 ASE_ChunkType_OldPaletteChunk2
= 0x0011,
138 ASE_ChunkType_MaskChunk
= 0x2016,
139 ASE_ChunkType_PathChunk
= 0x2017,
143 struct PACKED_STRUCT ASE_Chunk_OldPalette
{
145 //BZAsepriteOldPalettePacket packets[];
147 typedef struct ASE_Chunk_OldPalette ASE_Chunk_OldPalette
;
148 static_assert(sizeof(ASE_Chunk_OldPalette
) == 2, "ASE_Chunk_OldPalette is wrong size");
150 struct PACKED_STRUCT ASE_Chunk_OldPalette_Packet
{
153 //BZAsepriteOldPaletteColor colors[];
155 typedef struct ASE_Chunk_OldPalette_Packet ASE_Chunk_OldPalette_Packet
;
156 static_assert(sizeof(ASE_Chunk_OldPalette_Packet
) == 2, "ASE_Chunk_OldPalette_Packet is wrong size");
158 struct PACKED_STRUCT ASE_Chunk_OldPalette_Packet_Color
{
163 typedef struct ASE_Chunk_OldPalette_Packet_Color ASE_Chunk_OldPalette_Packet_Color
;
164 static_assert(sizeof(ASE_Chunk_OldPalette_Packet_Color
) == 3, "ASE_Chunk_OldPalette_Packet_Color is wrong size");
167 struct PACKED_STRUCT ASE_Chunk_Layer
{
175 ASE_BYTE ___reserved
[3];
178 typedef struct ASE_Chunk_Layer ASE_Chunk_Layer
;
179 static_assert(sizeof(ASE_Chunk_Layer
) == 18, "ASE_Chunk_Layer is wrong size");
181 enum ASE_Chunk_Layer_BlendModes
{
182 ASE_Chunk_Layer_BlendMode_Normal
= 0, // Should always be this
185 struct PACKED_STRUCT ASE_Chunk_Layer_Tileset
{
186 ASE_DWORD tilesetIndex
;
188 typedef struct ASE_Chunk_Layer_Tileset ASE_Chunk_Layer_Tileset
;
189 static_assert(sizeof(ASE_Chunk_Layer_Tileset
) == 4, "ASE_Chunk_Layer_Tileset is wrong size");
191 struct PACKED_STRUCT ASE_Chunk_Cel
{
198 ASE_BYTE ___reserved
[5];
200 typedef struct ASE_Chunk_Cel ASE_Chunk_Cel
;
201 static_assert(sizeof(ASE_Chunk_Cel
) == 16, "ASE_Chunk_Cel is wrong size");
203 enum ASE_Chunk_Cel_CelTypes
{
204 ASE_Chunk_Cel_CelType_RawImage
= 0,
205 ASE_Chunk_Cel_CelType_LinkedCel
= 1,
206 ASE_Chunk_Cel_CelType_CompressedImage
= 2,
207 ASE_Chunk_Cel_CelType_CompressedTilemap
= 3,
210 struct PACKED_STRUCT ASE_Chunk_Cel_RawImage
{
212 ASE_WORD pixelHeight
;
213 //ASE_BYTE pixelData[];
215 typedef struct ASE_Chunk_Cel_RawImage ASE_Chunk_Cel_RawImage
;
216 static_assert(sizeof(ASE_Chunk_Cel_RawImage
) == 4, "ASE_Chunk_Cel_RawImage is wrong size");
218 struct PACKED_STRUCT ASE_Chunk_Cel_LinkedCel
{
219 ASE_WORD framePosition
;
221 typedef struct ASE_Chunk_Cel_LinkedCel ASE_Chunk_Cel_LinkedCel
;
222 static_assert(sizeof(ASE_Chunk_Cel_LinkedCel
) == 2, "ASE_Chunk_Cel_LinkedCel is wrong size");
224 struct PACKED_STRUCT ASE_Chunk_Cel_CompressedImage
{
226 ASE_WORD pixelHeight
;
227 //ASE_BYTE pixelData[];
229 typedef struct ASE_Chunk_Cel_CompressedImage ASE_Chunk_Cel_CompressedImage
;
230 static_assert(sizeof(ASE_Chunk_Cel_CompressedImage
) == 4, "ASE_Chunk_Cel_CompressedImage is wrong size");
232 struct PACKED_STRUCT ASE_Chunk_Cel_CompressedTilemap
{
236 ASE_DWORD tileIDBitmap
;
237 ASE_DWORD flipXBitmap
;
238 ASE_DWORD flipYBitmap
;
239 ASE_DWORD rotationBitmap
;
240 ASE_BYTE ___reserved
[10];
241 //ASE_DWORD tileData[];
243 typedef struct ASE_Chunk_Cel_CompressedTilemap ASE_Chunk_Cel_CompressedTilemap
;
244 static_assert(sizeof(ASE_Chunk_Cel_CompressedTilemap
) == 32, "ASE_Chunk_Cel_CompressedTilemap is wrong size");
246 struct PACKED_STRUCT ASE_Chunk_Cel_Extra
{
252 ASE_BYTE ___reserved
[16];
254 typedef struct ASE_Chunk_Cel_Extra ASE_Chunk_Cel_Extra
;
255 static_assert(sizeof(ASE_Chunk_Cel_Extra
) == 36, "ASE_Chunk_Cel_Extra is wrong size");
257 struct PACKED_STRUCT ASE_Chunk_ColorProfile
{
261 ASE_BYTE ___reserved
[8];
263 typedef struct ASE_Chunk_ColorProfile ASE_Chunk_ColorProfile
;
264 static_assert(sizeof(ASE_Chunk_ColorProfile
) == 16, "ASE_Chunk_ColorProfile is wrong size");
266 struct PACKED_STRUCT ASE_Chunk_ColorProfile_ICC
{
270 typedef struct ASE_Chunk_ColorProfile_ICC ASE_Chunk_ColorProfile_ICC
;
271 static_assert(sizeof(ASE_Chunk_ColorProfile_ICC
) == 4, "ASE_Chunk_ColorProfile_ICC is wrong size");
273 struct PACKED_STRUCT ASE_Chunk_ExternalFiles
{
275 ASE_BYTE ___reserved
[8];
277 typedef struct ASE_Chunk_ExternalFiles ASE_Chunk_ExternalFiles
;
278 static_assert(sizeof(ASE_Chunk_ExternalFiles
) == 12, "ASE_Chunk_ExternalFiles is wrong size");
280 struct PACKED_STRUCT ASE_Chunk_ExternalFiles_Entry
{
283 ASE_BYTE ___reserved
[7];
284 ASE_STRING externalFilename
;
286 typedef struct ASE_Chunk_ExternalFiles_Entry ASE_Chunk_ExternalFiles_Entry
;
287 static_assert(sizeof(ASE_Chunk_ExternalFiles_Entry
) == 14, "ASE_Chunk_ExternalFiles_Entry is wrong size");
289 enum ASE_Chunk_ExternalFiles_Entry_Types
{
290 ASE_Chunk_ExternalFiles_EntryType_ExternalPalette
= 0,
291 ASE_Chunk_ExternalFiles_EntryType_ExternalTileset
= 1,
292 ASE_Chunk_ExternalFiles_EntryType_PropertiesExtension
= 2,
293 ASE_Chunk_ExternalFiles_EntryType_TileManagement
= 3,
296 struct PACKED_STRUCT ASE_Chunk_Mask
{
301 ASE_BYTE ___reserved
[8];
304 typedef struct ASE_Chunk_Mask ASE_Chunk_Mask
;
305 static_assert(sizeof(ASE_Chunk_Mask
) == 18, "ASE_Chunk_Mask is wrong size");
307 struct PACKED_STRUCT ASE_Chunk_Tags
{
309 ASE_BYTE ___reserved
[8];
311 typedef struct ASE_Chunk_Tags ASE_Chunk_Tags
;
312 static_assert(sizeof(ASE_Chunk_Tags
) == 10, "ASE_Chunk_Tags is wrong size");
314 struct PACKED_STRUCT ASE_Chunk_Tags_Tag
{
317 ASE_BYTE loopDirection
;
319 ASE_BYTE ___reserved
[6];
320 ASE_BYTE ___tagRBZ
[3];
324 typedef struct ASE_Chunk_Tags_Tag ASE_Chunk_Tags_Tag
;
325 static_assert(sizeof(ASE_Chunk_Tags_Tag
) == 19, "ASE_Chunk_Tags_Tag is wrong size");
327 struct PACKED_STRUCT ASE_Chunk_Palette
{
328 ASE_DWORD paletteSize
;
329 ASE_DWORD firstColorIdx
;
330 ASE_DWORD lastColorIdx
;
331 ASE_BYTE ___reserved
[8];
333 typedef struct ASE_Chunk_Palette ASE_Chunk_Palette
;
334 static_assert(sizeof(ASE_Chunk_Palette
) == 20, "ASE_Chunk_Palette is wrong size");
336 struct PACKED_STRUCT ASE_Chunk_Palette_Entry
{
343 typedef struct ASE_Chunk_Palette_Entry ASE_Chunk_Palette_Entry
;
344 static_assert(sizeof(ASE_Chunk_Palette_Entry
) == 6, "ASE_Chunk_Palette_Entry is wrong size");
346 struct PACKED_STRUCT ASE_Chunk_Palette_Entry_Name
{
349 typedef struct ASE_Chunk_Palette_Entry_Name ASE_Chunk_Palette_Entry_Name
;
350 static_assert(sizeof(ASE_Chunk_Palette_Entry_Name
) == 2, "ASE_Chunk_Palette_Entry_Name is wrong size");
352 static bool readASEHeader(ASE_Header
*headerOut
, BZResourceID handle
) {
353 bzAssert(headerOut
!= NULL
);
354 bzResourcesReadBytes(handle
, headerOut
, sizeof(ASE_Header
));
355 bzAssert(headerOut
->magicNumber
== 0xA5E0);
359 static bool readASEFrame(ASE_Frame
*frameOut
, ASE_DWORD
*numChunksOut
, BZResourceID handle
) {
360 bzAssert(frameOut
!= NULL
);
361 bzAssert(numChunksOut
!= NULL
);
363 bzResourcesReadBytes(handle
, frameOut
, sizeof(ASE_Frame
));
365 ASE_DWORD numChunks
= frameOut
->___numChunks
;
367 if (numChunks
== 0xFFFF) {
368 numChunks
= frameOut
->numChunks
;
369 if (numChunks
== 0) {
370 numChunks
= frameOut
->___numChunks
; // See docs.
374 *numChunksOut
= numChunks
;
379 void *bzGfxLoadAsepriteImage(BZMemoryArenaID arena
, size_t *framesOut
, size_t *widthOut
, size_t *heightOut
, const char *identifierFmt
, ...) {
380 bzMakeIdentifier(identifier
, identifierFmt
);
382 BZResourceID handle
= bzResourcesOpenResource("sprite", "assets/sprites/%s.aseprite", identifier
);
383 //PHYSFS_sint64 length = PHYSFS_fileLength(handle);
384 //void *data = alloca(length);
385 //PHYSFS_readBytes(handle, data, length);
388 readASEHeader(&header
, handle
);
390 size_t pixelDataSize
;
391 switch (header
.depth
) {
393 pixelDataSize
= sizeof(ASE_PIXEL_INDEXED
);
397 pixelDataSize
= sizeof(ASE_PIXEL_GRAYSCALE
);
401 pixelDataSize
= sizeof(ASE_PIXEL_RBGA
);
405 assert(false);//, "Invalid image");
409 uint8_t *outData
= (uint8_t *)bzMemoryAlloc(arena
, header
.frames
* header
.width
* header
.height
* pixelDataSize
); // Side by side loading needs to happen...
410 *framesOut
= (size_t)header
.frames
;
411 *widthOut
= (size_t)header
.width
;
412 *heightOut
= (size_t)header
.height
;
414 bzMemoryArenaPushWatermark(arena
);
415 size_t zBufferSize
= header
.width
* header
.height
* sizeof(ASE_SHORT
);
416 ASE_SHORT
*zBuffer
= (ASE_SHORT
*)bzMemoryAlloc(arena
, zBufferSize
);
418 size_t frameImageSize
= header
.width
* header
.height
* pixelDataSize
;
420 for (ASE_WORD frameIdx
= 0; frameIdx
< header
.frames
; ++frameIdx
) {
422 ASE_DWORD numChunks
= 0;
423 readASEFrame(&frame
, &numChunks
, handle
);
424 bzLog("FRAME %d: %d chunks", frameIdx
, numChunks
);
426 memset(zBuffer
, 0, zBufferSize
);
428 bool layerVisible
[16];
429 size_t nextLayer
= 0;
431 size_t layerStartOffset
[16];
433 for (ASE_DWORD chunk
= 0; chunk
< numChunks
; ++chunk
) {
434 size_t chunkStartPosition
= bzResourcesTell(handle
);
437 bzResourcesReadBytes(handle
, &chunk
, sizeof(chunk
));
439 switch (chunk
.type
) {
440 case ASE_ChunkType_LayerChunk
: {
441 bzLog("LAYER %d: %d chunks", frameIdx
, numChunks
);
444 ASE_Chunk_Layer layer
;
445 bzResourcesReadBytes(handle
, &layer
, sizeof(layer
));
447 layerVisible
[nextLayer
] = (layer
.flags
& 1) > 0;
450 bzResourcesSeek(handle
, chunkStartPosition
+ chunk
.chunkSize
);
454 case ASE_ChunkType_CelChunk
: {
455 bzLog("CELL %d: %d chunks", frameIdx
, numChunks
);
457 bzResourcesReadBytes(handle
, &cel
, sizeof(cel
));
459 ///// ASE_Chunk_Cel_CelType_LinkedCel aaaahhh
461 if (layerVisible
[cel
.layerIndex
] && cel
.celType
== ASE_Chunk_Cel_CelType_CompressedImage
) {
462 bzMemoryArenaPushWatermark(arena
);
464 ASE_Chunk_Cel_CompressedImage image
;
465 bzResourcesReadBytes(handle
, &image
, sizeof(image
));
467 size_t compressedSize
= chunk
.chunkSize
- sizeof(ASE_Chunk_Cel_CompressedImage
) - sizeof(ASE_Chunk_Cel
) - sizeof(ASE_Chunk
);
468 int8_t *compressedData
= bzMemoryAlloc(arena
, compressedSize
); // If we do this on the stack (alloca) the Playdate will explode!
469 bzResourcesReadBytes(handle
, compressedData
, compressedSize
);
471 size_t imagePixelDataSize
= image
.pixelWidth
* image
.pixelHeight
* pixelDataSize
;
472 int8_t *imagePixelData
= bzMemoryAlloc(arena
, imagePixelDataSize
);
473 stbi_zlib_decode_buffer(imagePixelData
, imagePixelDataSize
, compressedData
, compressedSize
);
475 for (size_t y
= 0; y
< image
.pixelHeight
; ++y
) {
476 for (size_t x
= 0; x
< image
.pixelWidth
; ++x
) {
477 size_t outX
= cel
.positionX
+ x
;
478 size_t outY
= cel
.positionY
+ y
;
480 ASE_SHORT
*currentZ
= &zBuffer
[outY
* header
.width
+ outX
];
482 if (*currentZ
<= cel
.zIndex
) {
483 for (size_t i
= 0; i
< pixelDataSize
; ++i
) {
484 uint8_t pixelData
= imagePixelData
[(y
* image
.pixelWidth
+ x
) * pixelDataSize
+ i
];
485 if (pixelData
> 0) { // FIXME, alpha...
486 outData
[frameIdx
* frameImageSize
+ (outY
* header
.width
+ outX
) * pixelDataSize
+ i
] = pixelData
;
489 *currentZ
= cel
.zIndex
;
494 bzMemoryArenaPopWatermark(arena
);
496 // Skip this chunk...
497 bzResourcesSeek(handle
, chunkStartPosition
+ chunk
.chunkSize
);
504 // Skip this chunk...
505 bzResourcesSeek(handle
, chunkStartPosition
+ chunk
.chunkSize
);
511 bzMemoryArenaPopWatermark(arena
);
513 bzResourcesCloseResource(handle
);
518 size_t bzGfxLoadAsepritePalette(uint32_t *colorsOut
, size_t maxColors
, const char *filename
) {
519 BZResourceID handle
= bzResourcesOpenResource("palette", "assets/palettes/%s", filename
);
522 readASEHeader(&header
, handle
);
524 size_t colorCount
= 0;
526 for (ASE_WORD frame
= 0; frame
< header
.frames
; ++frame
) {
528 bzResourcesReadBytes(handle
, &frame
, sizeof(frame
));
530 ASE_DWORD numChunks
= frame
.___numChunks
;
532 if (numChunks
== 0xFFFF) {
533 numChunks
= frame
.numChunks
;
534 if (numChunks
== 0) {
535 numChunks
= frame
.___numChunks
; // See docs.
539 for (ASE_DWORD chunk
= 0; chunk
< numChunks
; ++chunk
) {
540 size_t chunkStartPosition
= bzResourcesTell(handle
);
543 bzResourcesReadBytes(handle
, &chunk
, sizeof(chunk
));
545 switch (chunk
.type
) {
546 case ASE_ChunkType_PaletteChunk
: {
547 ASE_Chunk_Palette palette
;
548 bzResourcesReadBytes(handle
, &palette
, sizeof(palette
));
550 if (palette
.firstColorIdx
< maxColors
) {
551 size_t lastIdx
= bzMin(palette
.lastColorIdx
+ 1, maxColors
);
552 for (size_t i
= palette
.firstColorIdx
; i
< lastIdx
; ++i
) {
553 ASE_Chunk_Palette_Entry paletteEntry
;
554 bzResourcesReadBytes(handle
, &paletteEntry
, sizeof(paletteEntry
));
555 colorsOut
[i
] = bzPaletteMakeColor(paletteEntry
.r
, paletteEntry
.g
, paletteEntry
.b
);
557 colorCount
= bzMax(colorCount
, lastIdx
);
561 bzResourcesSeek(handle
, chunkStartPosition
+ chunk
.chunkSize
);
567 // Skip this chunk...
568 bzResourcesSeek(handle
, chunkStartPosition
+ chunk
.chunkSize
);
574 bzResourcesCloseResource(handle
);