]> git.bts.cx Git - benzene.git/blob - src/bz/gfx/aseprite.c
Fixed Aseprite rendering system
[benzene.git] / src / bz / gfx / aseprite.c
1 #include <bz/gfx/aseprite_internal.h>
2
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
13
14 #include <assert.h> // Static assertions
15
16 #define PACKED_STRUCT __attribute__((__packed__))
17
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;
28
29 struct PACKED_STRUCT ASE_STRING {
30 ASE_WORD length;
31 //ASE_BYTE characters[];
32 };
33 typedef struct ASE_STRING ASE_STRING;
34 static_assert(sizeof(ASE_STRING) == 2, "ASE_STRING is wrong size");
35
36 struct PACKED_STRUCT ASE_POINT {
37 ASE_LONG x;
38 ASE_LONG y;
39 };
40 typedef struct ASE_POINT ASE_POINT;
41 static_assert(sizeof(ASE_POINT) == 8, "ASE_POINT is wrong size");
42
43 struct PACKED_STRUCT ASE_SIZE {
44 ASE_LONG width;
45 ASE_LONG height;
46 };
47 typedef struct ASE_SIZE ASE_SIZE;
48 static_assert(sizeof(ASE_SIZE) == 8, "ASE_SIZE is wrong size");
49
50 struct PACKED_STRUCT ASE_RECT {
51 ASE_POINT origin;
52 ASE_SIZE size;
53 };
54 typedef struct ASE_RECT ASE_RECT;
55 static_assert(sizeof(ASE_RECT) == 16, "ASE_RECT is wrong size");
56
57 struct PACKED_STRUCT ASE_PIXEL_RBGA {
58 ASE_BYTE r;
59 ASE_BYTE g;
60 ASE_BYTE b;
61 ASE_BYTE a;
62 };
63 typedef struct ASE_PIXEL_RBGA ASE_PIXEL_RBGA;
64 static_assert(sizeof(ASE_PIXEL_RBGA) == 4, "ASE_PIXEL_RBGA is wrong size");
65
66 struct PACKED_STRUCT ASE_PIXEL_GRAYSCALE {
67 ASE_BYTE v;
68 ASE_BYTE a;
69 };
70 typedef struct ASE_PIXEL_GRAYSCALE ASE_PIXEL_GRAYSCALE;
71 static_assert(sizeof(ASE_PIXEL_GRAYSCALE) == 2, "ASE_PIXEL_GRAYSCALE is wrong size");
72
73 struct PACKED_STRUCT ASE_PIXEL_INDEXED {
74 ASE_BYTE idx;
75 };
76 typedef struct ASE_PIXEL_INDEXED ASE_PIXEL_INDEXED;
77 static_assert(sizeof(ASE_PIXEL_INDEXED) == 1, "ASE_PIXEL_INDEXED is wrong size");
78
79 struct PACKED_STRUCT ASE_Header {
80 ASE_DWORD fileSize;
81 ASE_WORD magicNumber; // 0xA5E0
82 ASE_WORD frames;
83 ASE_WORD width;
84 ASE_WORD height;
85 ASE_WORD depth;
86 ASE_DWORD flags;
87 ASE_WORD ___speed;
88 ASE_DWORD zero1;
89 ASE_DWORD zero2;
90 ASE_BYTE transparentColorIdx;
91 ASE_BYTE ___ignore[3];
92 ASE_WORD numColors;
93 ASE_BYTE pixelWidth;
94 ASE_BYTE pixelHeight;
95 ASE_SHORT gridX;
96 ASE_SHORT gridY;
97 ASE_WORD gridWidth;
98 ASE_WORD gridHeight;
99 ASE_BYTE ___reserved[84];
100 };
101 typedef struct ASE_Header ASE_Header;
102 static_assert(sizeof(ASE_Header) == 128, "ASE_Header is wrong size");
103
104 struct PACKED_STRUCT ASE_Frame {
105 ASE_DWORD frameBytes;
106 ASE_WORD magicNumber; // 0xF1FA
107 ASE_WORD ___numChunks;
108 ASE_WORD duration;
109 ASE_BYTE ___reserved[2];
110 ASE_DWORD numChunks;
111 };
112 typedef struct ASE_Frame ASE_Frame;
113 static_assert(sizeof(ASE_Frame) == 16, "ASE_Frame is wrong size");
114
115 struct PACKED_STRUCT ASE_Chunk {
116 ASE_DWORD chunkSize;
117 ASE_WORD type;
118 //ASE_BYTE data[];
119 };
120 typedef struct ASE_Chunk ASE_Chunk;
121 static_assert(sizeof(ASE_Chunk) == 6, "ASE_Chunk is wrong size");
122
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,
134
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,
140 };
141
142 #if 0
143 struct PACKED_STRUCT ASE_Chunk_OldPalette {
144 ASE_WORD numPackets;
145 //BZAsepriteOldPalettePacket packets[];
146 };
147 typedef struct ASE_Chunk_OldPalette ASE_Chunk_OldPalette;
148 static_assert(sizeof(ASE_Chunk_OldPalette) == 2, "ASE_Chunk_OldPalette is wrong size");
149
150 struct PACKED_STRUCT ASE_Chunk_OldPalette_Packet {
151 ASE_BYTE skip;
152 ASE_BYTE numColors;
153 //BZAsepriteOldPaletteColor colors[];
154 };
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");
157
158 struct PACKED_STRUCT ASE_Chunk_OldPalette_Packet_Color {
159 ASE_BYTE r;
160 ASE_BYTE g;
161 ASE_BYTE b;
162 };
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");
165 #endif
166
167 struct PACKED_STRUCT ASE_Chunk_Layer {
168 ASE_WORD flags;
169 ASE_WORD layerType;
170 ASE_WORD childLevel;
171 ASE_WORD width;
172 ASE_WORD height;
173 ASE_WORD blendMode;
174 ASE_BYTE opacity;
175 ASE_BYTE ___reserved[3];
176 ASE_STRING name;
177 };
178 typedef struct ASE_Chunk_Layer ASE_Chunk_Layer;
179 static_assert(sizeof(ASE_Chunk_Layer) == 18, "ASE_Chunk_Layer is wrong size");
180
181 enum ASE_Chunk_Layer_BlendModes {
182 ASE_Chunk_Layer_BlendMode_Normal = 0, // Should always be this
183 };
184
185 struct PACKED_STRUCT ASE_Chunk_Layer_Tileset {
186 ASE_DWORD tilesetIndex;
187 };
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");
190
191 struct PACKED_STRUCT ASE_Chunk_Cel {
192 ASE_WORD layerIndex;
193 ASE_SHORT positionX;
194 ASE_SHORT positionY;
195 ASE_BYTE opacity;
196 ASE_WORD celType;
197 ASE_SHORT zIndex;
198 ASE_BYTE ___reserved[5];
199 };
200 typedef struct ASE_Chunk_Cel ASE_Chunk_Cel;
201 static_assert(sizeof(ASE_Chunk_Cel) == 16, "ASE_Chunk_Cel is wrong size");
202
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,
208 };
209
210 struct PACKED_STRUCT ASE_Chunk_Cel_RawImage {
211 ASE_WORD pixelWidth;
212 ASE_WORD pixelHeight;
213 //ASE_BYTE pixelData[];
214 };
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");
217
218 struct PACKED_STRUCT ASE_Chunk_Cel_LinkedCel {
219 ASE_WORD framePosition;
220 };
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");
223
224 struct PACKED_STRUCT ASE_Chunk_Cel_CompressedImage {
225 ASE_WORD pixelWidth;
226 ASE_WORD pixelHeight;
227 //ASE_BYTE pixelData[];
228 };
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");
231
232 struct PACKED_STRUCT ASE_Chunk_Cel_CompressedTilemap {
233 ASE_WORD tileWidth;
234 ASE_WORD tileHeight;
235 ASE_WORD tileBits;
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[];
242 };
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");
245
246 struct PACKED_STRUCT ASE_Chunk_Cel_Extra {
247 ASE_DWORD flags;
248 ASE_FIXED positionX;
249 ASE_FIXED positionY;
250 ASE_FIXED width;
251 ASE_FIXED height;
252 ASE_BYTE ___reserved[16];
253 };
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");
256
257 struct PACKED_STRUCT ASE_Chunk_ColorProfile {
258 ASE_WORD type;
259 ASE_WORD flags;
260 ASE_FIXED gamma;
261 ASE_BYTE ___reserved[8];
262 };
263 typedef struct ASE_Chunk_ColorProfile ASE_Chunk_ColorProfile;
264 static_assert(sizeof(ASE_Chunk_ColorProfile) == 16, "ASE_Chunk_ColorProfile is wrong size");
265
266 struct PACKED_STRUCT ASE_Chunk_ColorProfile_ICC {
267 ASE_DWORD length;
268 //ASE_BYTE data[];
269 };
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");
272
273 struct PACKED_STRUCT ASE_Chunk_ExternalFiles {
274 ASE_DWORD entries;
275 ASE_BYTE ___reserved[8];
276 };
277 typedef struct ASE_Chunk_ExternalFiles ASE_Chunk_ExternalFiles;
278 static_assert(sizeof(ASE_Chunk_ExternalFiles) == 12, "ASE_Chunk_ExternalFiles is wrong size");
279
280 struct PACKED_STRUCT ASE_Chunk_ExternalFiles_Entry {
281 ASE_DWORD entryID;
282 ASE_BYTE type;
283 ASE_BYTE ___reserved[7];
284 ASE_STRING externalFilename;
285 };
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");
288
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,
294 };
295
296 struct PACKED_STRUCT ASE_Chunk_Mask {
297 ASE_SHORT positionX;
298 ASE_SHORT positionY;
299 ASE_WORD width;
300 ASE_WORD height;
301 ASE_BYTE ___reserved[8];
302 ASE_STRING maskName;
303 };
304 typedef struct ASE_Chunk_Mask ASE_Chunk_Mask;
305 static_assert(sizeof(ASE_Chunk_Mask) == 18, "ASE_Chunk_Mask is wrong size");
306
307 struct PACKED_STRUCT ASE_Chunk_Tags {
308 ASE_WORD numTags;
309 ASE_BYTE ___reserved[8];
310 };
311 typedef struct ASE_Chunk_Tags ASE_Chunk_Tags;
312 static_assert(sizeof(ASE_Chunk_Tags) == 10, "ASE_Chunk_Tags is wrong size");
313
314 struct PACKED_STRUCT ASE_Chunk_Tags_Tag {
315 ASE_WORD fromFrame;
316 ASE_WORD toFrame;
317 ASE_BYTE loopDirection;
318 ASE_WORD repeat;
319 ASE_BYTE ___reserved[6];
320 ASE_BYTE ___tagRBZ[3];
321 ASE_BYTE ___extra;
322 ASE_STRING tagName;
323 };
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");
326
327 struct PACKED_STRUCT ASE_Chunk_Palette {
328 ASE_DWORD paletteSize;
329 ASE_DWORD firstColorIdx;
330 ASE_DWORD lastColorIdx;
331 ASE_BYTE ___reserved[8];
332 };
333 typedef struct ASE_Chunk_Palette ASE_Chunk_Palette;
334 static_assert(sizeof(ASE_Chunk_Palette) == 20, "ASE_Chunk_Palette is wrong size");
335
336 struct PACKED_STRUCT ASE_Chunk_Palette_Entry {
337 ASE_WORD flags;
338 ASE_BYTE r;
339 ASE_BYTE g;
340 ASE_BYTE b;
341 ASE_BYTE a;
342 };
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");
345
346 struct PACKED_STRUCT ASE_Chunk_Palette_Entry_Name {
347 ASE_STRING name;
348 };
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");
351
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);
356 return true;
357 }
358
359 static bool readASEFrame(ASE_Frame *frameOut, ASE_DWORD *numChunksOut, BZResourceID handle) {
360 bzAssert(frameOut != NULL);
361 bzAssert(numChunksOut != NULL);
362
363 bzResourcesReadBytes(handle, frameOut, sizeof(ASE_Frame));
364
365 ASE_DWORD numChunks = frameOut->___numChunks;
366
367 if (numChunks == 0xFFFF) {
368 numChunks = frameOut->numChunks;
369 if (numChunks == 0) {
370 numChunks = frameOut->___numChunks; // See docs.
371 }
372 }
373
374 *numChunksOut = numChunks;
375
376 return true;
377 }
378
379 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[]) {
380 bzResourcesSeek(handle, frameStartPositions[frameIdx]);
381
382 ASE_Frame frame;
383 ASE_DWORD numChunks;
384 readASEFrame(&frame, &numChunks, handle);
385
386 for (ASE_DWORD chunkIdx = 0; chunkIdx < numChunks; ++chunkIdx) {
387 size_t chunkStartPosition = bzResourcesTell(handle);
388
389 ASE_Chunk chunk;
390 bzResourcesReadBytes(handle, &chunk, sizeof(chunk));
391
392 if (chunk.type == ASE_ChunkType_CelChunk) {
393 ASE_Chunk_Cel cel;
394 bzResourcesReadBytes(handle, &cel, sizeof(cel));
395
396 if (cel.layerIndex == layerIndex) {
397 if (cel.celType == ASE_Chunk_Cel_CelType_LinkedCel) {
398 ASE_Chunk_Cel_LinkedCel linkedCel;
399 bzResourcesReadBytes(handle, &linkedCel, sizeof(linkedCel));
400 renderChunkCel(outData, zBuffer, arena, handle, layerIndex, header, pixelDataSize, linkedCel.framePosition, frameStartPositions);
401 } else if (cel.celType == ASE_Chunk_Cel_CelType_CompressedImage) {
402 bzMemoryArenaPushWatermark(arena);
403
404 ASE_Chunk_Cel_CompressedImage image;
405 bzResourcesReadBytes(handle, &image, sizeof(image));
406
407 size_t compressedSize = chunk.chunkSize - sizeof(ASE_Chunk_Cel_CompressedImage) - sizeof(ASE_Chunk_Cel) - sizeof(ASE_Chunk);
408 int8_t *compressedData = bzMemoryAlloc(arena, compressedSize); // If we do this on the stack (alloca) the Playdate will explode!
409 bzResourcesReadBytes(handle, compressedData, compressedSize);
410
411 size_t imagePixelDataSize = image.pixelWidth * image.pixelHeight * pixelDataSize;
412 int8_t *imagePixelData = bzMemoryAlloc(arena, imagePixelDataSize);
413 stbi_zlib_decode_buffer(imagePixelData, imagePixelDataSize, compressedData, compressedSize);
414
415 for (size_t y = 0; y < image.pixelHeight; ++y) {
416 for (size_t x = 0; x < image.pixelWidth; ++x) {
417 size_t outX = cel.positionX + x;
418 size_t outY = cel.positionY + y;
419
420 //ASE_SHORT *currentZ = &zBuffer[outY * header->width + outX];
421 //if (*currentZ > layerIndex) {
422 for (size_t i = 0; i < pixelDataSize; ++i) {
423 uint8_t pixelData = imagePixelData[(y * image.pixelWidth + x) * pixelDataSize + i];
424 if (pixelData > 0) { // FIXME, alpha...
425 outData[outY * header->width + outX * pixelDataSize + i] = pixelData;
426 }
427 }
428 //*currentZ = layerIndex;//cel.zIndex;
429 //}
430 }
431 }
432
433 bzMemoryArenaPopWatermark(arena);
434 }
435
436 // Found what we needed to render...
437 break;
438 }
439 }
440
441 bzResourcesSeek(handle, chunkStartPosition + chunk.chunkSize);
442 }
443 }
444
445 static bool renderCelsForLayer(uint8_t *outData, ASE_SHORT *zBuffer, BZMemoryArenaID arena, BZResourceID handle, ASE_WORD layerIndex, const ASE_Header *header, size_t pixelDataSize) {
446 size_t framesStartPosition = bzResourcesTell(handle);
447
448 // Layers are .. complicated, you only really need to read them for the first frame...
449 ASE_Frame firstFrame;
450 ASE_DWORD firstFrameChunks;
451 readASEFrame(&firstFrame, &firstFrameChunks, handle);
452
453 bool layerExists = false;
454 bool layerVisible = false;
455 for (ASE_DWORD chunkIdx = 0, layerCount = 0; chunkIdx < firstFrameChunks; ++chunkIdx) {
456 size_t chunkStartPosition = bzResourcesTell(handle);
457
458 ASE_Chunk chunk;
459 bzResourcesReadBytes(handle, &chunk, sizeof(chunk));
460
461 if (chunk.type == ASE_ChunkType_LayerChunk) {
462 ASE_Chunk_Layer layer;
463 bzResourcesReadBytes(handle, &layer, sizeof(layer));
464
465 if (layerCount == layerIndex) {
466 layerExists = true;
467 layerVisible = (layer.flags & 1) > 0; // FIXME, named flag
468 break;
469 } else {
470 layerCount++;
471 }
472 }// else {
473 // Skip this chunk...
474 bzResourcesSeek(handle, chunkStartPosition + chunk.chunkSize);
475 //}
476 }
477
478 if (layerExists && layerVisible) {
479 bzResourcesSeek(handle, framesStartPosition);
480
481 size_t *frameStartPositions = alloca(header->frames * sizeof(size_t)); // FIXME, alloca -> tmp
482
483 for (ASE_WORD frameIdx = 0; frameIdx < header->frames; ++frameIdx) {
484 frameStartPositions[frameIdx] = bzResourcesTell(handle);
485
486 ASE_Frame frame;
487 ASE_DWORD numChunks;
488 readASEFrame(&frame, &numChunks, handle);
489
490 for (ASE_DWORD chunkIdx = 0; chunkIdx < numChunks; ++chunkIdx) {
491 size_t chunkStartPosition = bzResourcesTell(handle);
492
493 ASE_Chunk chunk;
494 bzResourcesReadBytes(handle, &chunk, sizeof(chunk));
495
496 if (chunk.type == ASE_ChunkType_CelChunk) {
497 uint8_t *frameOutData = &outData[frameIdx * header->height * header->width * pixelDataSize];
498 ASE_SHORT *frameZBuffer = NULL;//&zBuffer[frameIdx * header->height * header->width * sizeof(ASE_SHORT)];
499 renderChunkCel(frameOutData, frameZBuffer, arena, handle, layerIndex, header, pixelDataSize, frameIdx, frameStartPositions);
500 }
501
502 bzResourcesSeek(handle, chunkStartPosition + chunk.chunkSize);
503 }
504 }
505 }
506
507 return layerExists;
508 }
509
510 void *bzGfxLoadAsepriteImage(BZMemoryArenaID arena, size_t *framesOut, size_t *widthOut, size_t *heightOut, const char *identifierFmt, ...) {
511 bzMakeIdentifier(identifier, identifierFmt);
512
513 BZResourceID handle = bzResourcesOpenResource("sprite", "assets/sprites/%s.aseprite", identifier);
514
515 ASE_Header header;
516 readASEHeader(&header, handle);
517
518 size_t pixelDataSize;
519 switch (header.depth) {
520 case 8:
521 pixelDataSize = sizeof(ASE_PIXEL_INDEXED);
522 break;
523
524 case 16:
525 pixelDataSize = sizeof(ASE_PIXEL_GRAYSCALE);
526 break;
527
528 case 32:
529 pixelDataSize = sizeof(ASE_PIXEL_RBGA);
530 break;
531
532 default:
533 bzAssertMessage(false, "Invalid image");
534 break;
535 }
536
537 uint8_t *outData = (uint8_t *)bzMemoryAlloc(arena, header.frames * header.height * header.width * pixelDataSize); // Side by side loading needs to happen...
538 *framesOut = (size_t)header.frames;
539 *widthOut = (size_t)header.width;
540 *heightOut = (size_t)header.height;
541
542 //bzMemoryArenaPushWatermark(arena);
543 ASE_SHORT *zBuffer = NULL;//(ASE_SHORT *)bzMemoryAlloc(arena, header.frames * header.height * header.width * sizeof(ASE_SHORT));
544 //memset(zBuffer, 0, header.frames * header.height * header.width * sizeof(ASE_SHORT)); // FIXME
545
546 size_t startPosition = bzResourcesTell(handle);
547 for (ASE_WORD layerIndex = 0; ; ++layerIndex) {
548 // Reset...
549 bzResourcesSeek(handle, startPosition);
550
551 bool rendered = renderCelsForLayer(outData, zBuffer, arena, handle, layerIndex, &header, pixelDataSize);
552 if (rendered == false) {
553 break;
554 }
555 }
556
557 //bzMemoryArenaPopWatermark(arena);
558
559 return outData;
560 }
561
562 size_t bzGfxLoadAsepritePalette(uint32_t *colorsOut, size_t maxColors, const char *filename) {
563 BZResourceID handle = bzResourcesOpenResource("palette", "assets/palettes/%s.aseprite", filename);
564
565 ASE_Header header;
566 readASEHeader(&header, handle);
567
568 size_t colorCount = 0;
569
570 for (ASE_WORD frame = 0; frame < header.frames; ++frame) {
571 ASE_Frame frame;
572 ASE_DWORD numChunks;
573 readASEFrame(&frame, &numChunks, handle);
574
575 for (ASE_DWORD chunk = 0; chunk < numChunks; ++chunk) {
576 size_t chunkStartPosition = bzResourcesTell(handle);
577
578 ASE_Chunk chunk;
579 bzResourcesReadBytes(handle, &chunk, sizeof(chunk));
580
581 switch (chunk.type) {
582 case ASE_ChunkType_PaletteChunk: {
583 ASE_Chunk_Palette palette;
584 bzResourcesReadBytes(handle, &palette, sizeof(palette));
585
586 if (palette.firstColorIdx < maxColors) {
587 size_t lastIdx = bzMin(palette.lastColorIdx + 1, maxColors);
588 for (size_t i = palette.firstColorIdx; i < lastIdx; ++i) {
589 ASE_Chunk_Palette_Entry paletteEntry;
590 bzResourcesReadBytes(handle, &paletteEntry, sizeof(paletteEntry));
591 colorsOut[i] = bzPaletteMakeColor(paletteEntry.r, paletteEntry.g, paletteEntry.b);
592 }
593 colorCount = bzMax(colorCount, lastIdx);
594 }
595
596 // FIXME
597 bzResourcesSeek(handle, chunkStartPosition + chunk.chunkSize);
598
599 break;
600 }
601
602 default:
603 // Skip this chunk...
604 bzResourcesSeek(handle, chunkStartPosition + chunk.chunkSize);
605 break;
606 }
607 }
608 }
609
610 bzResourcesCloseResource(handle);
611
612 return colorCount;
613 }