]> git.bts.cx Git - benzene.git/blob - src/bz/gfx/aseprite.c
69dc446fbef8b1bbe2b2b178501ed3fde6737130
[benzene.git] / src / bz / gfx / aseprite.c
1 #include <bz/gfx/aseprite_internal.h>
2
3 #include <bz/math/math.h>
4 #include <bz/memory/allocator.h>
5 #include <bz/renderer/palette_internal.h>
6 #include <bz/resources/resource.h>
7 #include <bz/types/identifier_internal.h>
8 #include <stdio.h> // Apparently stb_image.h need this??
9 #include <stb_image.h>
10
11 #include <assert.h> // Static assertions
12
13 #define PACKED_STRUCT __attribute__((__packed__))
14
15 typedef uint8_t  ASE_BYTE;
16 typedef uint16_t ASE_WORD;
17 typedef int16_t  ASE_SHORT;
18 typedef uint32_t ASE_DWORD;
19 typedef uint32_t ASE_LONG;
20 typedef uint32_t ASE_FIXED;
21 typedef float    ASE_FLOAT;
22 typedef double   ASE_DOUBLE;
23 typedef uint64_t ASE_QWORD;
24 typedef int64_t  ASE_LONG64;
25
26
27
28 struct PACKED_STRUCT ASE_STRING {
29         ASE_WORD length;
30         //ASE_BYTE characters[];
31 };
32 typedef struct ASE_STRING ASE_STRING;
33 static_assert(sizeof(ASE_STRING) == 2, "ASE_STRING is wrong size");
34
35 struct PACKED_STRUCT ASE_POINT {
36         ASE_LONG x;
37         ASE_LONG y;
38 };
39 typedef struct ASE_POINT ASE_POINT;
40 static_assert(sizeof(ASE_POINT) == 8, "ASE_POINT is wrong size");
41
42 struct PACKED_STRUCT ASE_SIZE {
43         ASE_LONG width;
44         ASE_LONG height;
45 };
46 typedef struct ASE_SIZE ASE_SIZE;
47 static_assert(sizeof(ASE_SIZE) == 8, "ASE_SIZE is wrong size");
48
49 struct PACKED_STRUCT ASE_RECT {
50         ASE_POINT origin;
51         ASE_SIZE size;
52 };
53 typedef struct ASE_RECT ASE_RECT;
54 static_assert(sizeof(ASE_RECT) == 16, "ASE_RECT is wrong size");
55
56 struct PACKED_STRUCT ASE_PIXEL_RBZA {
57         ASE_BYTE r;
58         ASE_BYTE g;
59         ASE_BYTE b;
60         ASE_BYTE a;
61 };
62 typedef struct ASE_PIXEL_RBZA ASE_PIXEL_RBZA;
63 static_assert(sizeof(ASE_PIXEL_RBZA) == 4, "ASE_PIXEL_RBZA is wrong size");
64
65 struct PACKED_STRUCT ASE_PIXEL_GRAYSCALE {
66         ASE_BYTE v;
67         ASE_BYTE a;
68 };
69 typedef struct ASE_PIXEL_GRAYSCALE ASE_PIXEL_GRAYSCALE;
70 static_assert(sizeof(ASE_PIXEL_GRAYSCALE) == 2, "ASE_PIXEL_GRAYSCALE is wrong size");
71
72 struct PACKED_STRUCT ASE_PIXEL_INDEXED {
73         ASE_BYTE idx;
74 };
75 typedef struct ASE_PIXEL_INDEXED ASE_PIXEL_INDEXED;
76 static_assert(sizeof(ASE_PIXEL_INDEXED) == 1, "ASE_PIXEL_INDEXED is wrong size");
77
78 struct PACKED_STRUCT ASE_Header {
79         ASE_DWORD fileSize;
80         ASE_WORD magicNumber; // 0xA5E0
81         ASE_WORD frames;
82         ASE_WORD width;
83         ASE_WORD height;
84         ASE_WORD depth;
85         ASE_DWORD flags;
86         ASE_WORD ___speed;
87         ASE_DWORD zero1;
88         ASE_DWORD zero2;
89         ASE_BYTE transparentColorIdx;
90         ASE_BYTE ___ignore[3];
91         ASE_WORD numColors;
92         ASE_BYTE pixelWidth;
93         ASE_BYTE pixelHeight;
94         ASE_SHORT gridX;
95         ASE_SHORT gridY;
96         ASE_WORD gridWidth;
97         ASE_WORD gridHeight;
98         ASE_BYTE ___reserved[84];
99 };
100 typedef struct ASE_Header ASE_Header;
101 static_assert(sizeof(ASE_Header) == 128, "ASE_Header is wrong size");
102
103 struct PACKED_STRUCT ASE_Frame {
104         ASE_DWORD frameBytes;
105         ASE_WORD magicNumber; // 0xF1FA
106         ASE_WORD ___numChunks;
107         ASE_WORD duration;
108         ASE_BYTE ___reserved[2];
109         ASE_DWORD numChunks;
110 };
111 typedef struct ASE_Frame ASE_Frame;
112 static_assert(sizeof(ASE_Frame) == 16, "ASE_Frame is wrong size");
113
114 struct PACKED_STRUCT ASE_Chunk {
115         ASE_DWORD chunkSize;
116         ASE_WORD type;
117         //ASE_BYTE data[];
118 };
119 typedef struct ASE_Chunk ASE_Chunk;
120 static_assert(sizeof(ASE_Chunk) == 6, "ASE_Chunk is wrong size");
121
122 #if 0
123 struct PACKED_STRUCT ASE_Chunk_OldPalette {
124         ASE_WORD numPackets;
125         //BZAsepriteOldPalettePacket packets[];
126 };
127 typedef struct ASE_Chunk_OldPalette ASE_Chunk_OldPalette;
128 static_assert(sizeof(ASE_Chunk_OldPalette) == 2, "ASE_Chunk_OldPalette is wrong size");
129
130 struct PACKED_STRUCT ASE_Chunk_OldPalette_Packet {
131         ASE_BYTE skip;
132         ASE_BYTE numColors;
133         //BZAsepriteOldPaletteColor colors[];
134 };
135 typedef struct ASE_Chunk_OldPalette_Packet ASE_Chunk_OldPalette_Packet;
136 static_assert(sizeof(ASE_Chunk_OldPalette_Packet) == 2, "ASE_Chunk_OldPalette_Packet is wrong size");
137
138 struct PACKED_STRUCT ASE_Chunk_OldPalette_Packet_Color {
139         ASE_BYTE r;
140         ASE_BYTE g;
141         ASE_BYTE b;
142 };
143 typedef struct ASE_Chunk_OldPalette_Packet_Color ASE_Chunk_OldPalette_Packet_Color;
144 static_assert(sizeof(ASE_Chunk_OldPalette_Packet_Color) == 3, "ASE_Chunk_OldPalette_Packet_Color is wrong size");
145 #endif
146
147 struct PACKED_STRUCT ASE_Chunk_Layer {
148         ASE_WORD flags;
149         ASE_WORD layerType;
150         ASE_WORD childLevel;
151         ASE_WORD width;
152         ASE_WORD height;
153         ASE_WORD blendMode;
154         ASE_BYTE opacity;
155         ASE_BYTE ___reserved[3];
156         ASE_STRING name;
157 };
158 typedef struct ASE_Chunk_Layer ASE_Chunk_Layer;
159 static_assert(sizeof(ASE_Chunk_Layer) == 18, "ASE_Chunk_Layer is wrong size");
160
161 struct PACKED_STRUCT ASE_Chunk_Layer_Tileset {
162         ASE_DWORD tilesetIndex;
163 };
164 typedef struct ASE_Chunk_Layer_Tileset ASE_Chunk_Layer_Tileset;
165 static_assert(sizeof(ASE_Chunk_Layer_Tileset) == 4, "ASE_Chunk_Layer_Tileset is wrong size");
166
167 struct PACKED_STRUCT ASE_Chunk_Cel {
168         ASE_WORD layerIndex;
169         ASE_SHORT positionX;
170         ASE_SHORT positionY;
171         ASE_BYTE opacity;
172         ASE_WORD celType;
173         ASE_SHORT zIndex;
174         ASE_BYTE ___reserved[5];
175 };
176 typedef struct ASE_Chunk_Cel ASE_Chunk_Cel;
177 static_assert(sizeof(ASE_Chunk_Cel) == 16, "ASE_Chunk_Cel is wrong size");
178
179 struct PACKED_STRUCT ASE_Chunk_Cel_RawImage {
180         ASE_WORD pixelWidth;
181         ASE_WORD pixelHeight;
182         //ASE_BYTE pixelData[];
183 };
184 typedef struct ASE_Chunk_Cel_RawImage ASE_Chunk_Cel_RawImage;
185 static_assert(sizeof(ASE_Chunk_Cel_RawImage) == 4, "ASE_Chunk_Cel_RawImage is wrong size");
186
187 struct PACKED_STRUCT ASE_Chunk_Cel_LinkedCel {
188         ASE_WORD framePosition;
189 };
190 typedef struct ASE_Chunk_Cel_LinkedCel ASE_Chunk_Cel_LinkedCel;
191 static_assert(sizeof(ASE_Chunk_Cel_LinkedCel) == 2, "ASE_Chunk_Cel_LinkedCel is wrong size");
192
193 struct PACKED_STRUCT ASE_Chunk_Cel_CompressedImage {
194         ASE_WORD pixelWidth;
195         ASE_WORD pixelHeight;
196         //ASE_BYTE pixelData[];
197 };
198 typedef struct ASE_Chunk_Cel_CompressedImage ASE_Chunk_Cel_CompressedImage;
199 static_assert(sizeof(ASE_Chunk_Cel_CompressedImage) == 4, "ASE_Chunk_Cel_CompressedImage is wrong size");
200
201 struct PACKED_STRUCT ASE_Chunk_Cel_CompressedTilemap {
202         ASE_WORD tileWidth;
203         ASE_WORD tileHeight;
204         ASE_WORD tileBits;
205         ASE_DWORD tileIDBitmap;
206         ASE_DWORD flipXBitmap;
207         ASE_DWORD flipYBitmap;
208         ASE_DWORD rotationBitmap;
209         ASE_BYTE ___reserved[10];
210         //ASE_DWORD tileData[];
211 };
212 typedef struct ASE_Chunk_Cel_CompressedTilemap ASE_Chunk_Cel_CompressedTilemap;
213 static_assert(sizeof(ASE_Chunk_Cel_CompressedTilemap) == 32, "ASE_Chunk_Cel_CompressedTilemap is wrong size");
214
215 struct PACKED_STRUCT ASE_Chunk_Cel_Extra {
216         ASE_DWORD flags;
217         ASE_FIXED positionX;
218         ASE_FIXED positionY;
219         ASE_FIXED width;
220         ASE_FIXED height;
221         ASE_BYTE ___reserved[16];
222 };
223 typedef struct ASE_Chunk_Cel_Extra ASE_Chunk_Cel_Extra;
224 static_assert(sizeof(ASE_Chunk_Cel_Extra) == 36, "ASE_Chunk_Cel_Extra is wrong size");
225
226 struct PACKED_STRUCT ASE_Chunk_ColorProfile {
227         ASE_WORD type;
228         ASE_WORD flags;
229         ASE_FIXED gamma;
230         ASE_BYTE ___reserved[8];
231 };
232 typedef struct ASE_Chunk_ColorProfile ASE_Chunk_ColorProfile;
233 static_assert(sizeof(ASE_Chunk_ColorProfile) == 16, "ASE_Chunk_ColorProfile is wrong size");
234
235 struct PACKED_STRUCT ASE_Chunk_ColorProfile_ICC {
236         ASE_DWORD length;
237         //ASE_BYTE data[];
238 };
239 typedef struct ASE_Chunk_ColorProfile_ICC ASE_Chunk_ColorProfile_ICC;
240 static_assert(sizeof(ASE_Chunk_ColorProfile_ICC) == 4, "ASE_Chunk_ColorProfile_ICC is wrong size");
241
242 struct PACKED_STRUCT ASE_Chunk_ExternalFiles {
243         ASE_DWORD entries;
244         ASE_BYTE ___reserved[8];
245 };
246 typedef struct ASE_Chunk_ExternalFiles ASE_Chunk_ExternalFiles;
247 static_assert(sizeof(ASE_Chunk_ExternalFiles) == 12, "ASE_Chunk_ExternalFiles is wrong size");
248
249 struct PACKED_STRUCT ASE_Chunk_ExternalFiles_Entry {
250         ASE_DWORD entryID;
251         ASE_BYTE type;
252         ASE_BYTE ___reserved[7];
253         ASE_STRING externalFilename;
254 };
255 typedef struct ASE_Chunk_ExternalFiles_Entry ASE_Chunk_ExternalFiles_Entry;
256 static_assert(sizeof(ASE_Chunk_ExternalFiles_Entry) == 14, "ASE_Chunk_ExternalFiles_Entry is wrong size");
257
258 struct PACKED_STRUCT ASE_Chunk_Mask {
259         ASE_SHORT positionX;
260         ASE_SHORT positionY;
261         ASE_WORD width;
262         ASE_WORD height;
263         ASE_BYTE ___reserved[8];
264         ASE_STRING maskName;
265 };
266 typedef struct ASE_Chunk_Mask ASE_Chunk_Mask;
267 static_assert(sizeof(ASE_Chunk_Mask) == 18, "ASE_Chunk_Mask is wrong size");
268
269 struct PACKED_STRUCT ASE_Chunk_Tags {
270         ASE_WORD numTags;
271         ASE_BYTE ___reserved[8];
272 };
273 typedef struct ASE_Chunk_Tags ASE_Chunk_Tags;
274 static_assert(sizeof(ASE_Chunk_Tags) == 10, "ASE_Chunk_Tags is wrong size");
275
276 struct PACKED_STRUCT ASE_Chunk_Tags_Tag {
277         ASE_WORD fromFrame;
278         ASE_WORD toFrame;
279         ASE_BYTE loopDirection;
280         ASE_WORD repeat;
281         ASE_BYTE ___reserved[6];
282         ASE_BYTE ___tagRBZ[3];
283         ASE_BYTE ___extra;
284         ASE_STRING tagName;
285 };
286 typedef struct ASE_Chunk_Tags_Tag ASE_Chunk_Tags_Tag;
287 static_assert(sizeof(ASE_Chunk_Tags_Tag) == 19, "ASE_Chunk_Tags_Tag is wrong size");
288
289 struct PACKED_STRUCT ASE_Chunk_Palette {
290         ASE_DWORD paletteSize;
291         ASE_DWORD firstColorIdx;
292         ASE_DWORD lastColorIdx;
293         ASE_BYTE ___reserved[8];
294 };
295 typedef struct ASE_Chunk_Palette ASE_Chunk_Palette;
296 static_assert(sizeof(ASE_Chunk_Palette) == 20, "ASE_Chunk_Palette is wrong size");
297
298 struct PACKED_STRUCT ASE_Chunk_Palette_Entry {
299         ASE_WORD flags;
300         ASE_BYTE r;
301         ASE_BYTE g;
302         ASE_BYTE b;
303         ASE_BYTE a;
304 };
305 typedef struct ASE_Chunk_Palette_Entry ASE_Chunk_Palette_Entry;
306 static_assert(sizeof(ASE_Chunk_Palette_Entry) == 6, "ASE_Chunk_Palette_Entry is wrong size");
307
308 struct PACKED_STRUCT ASE_Chunk_Palette_Entry_Name {
309         ASE_STRING name;
310 };
311 typedef struct ASE_Chunk_Palette_Entry_Name ASE_Chunk_Palette_Entry_Name;
312 static_assert(sizeof(ASE_Chunk_Palette_Entry_Name) == 2, "ASE_Chunk_Palette_Entry_Name is wrong size");
313
314 void *bzGfxLoadAsepriteImage(BZMemoryArenaID arena, size_t *widthOut, size_t *heightOut, const char *identifierFmt, ...) {
315         bzMakeIdentifier(identifier, identifierFmt);
316
317         BZResourceID handle = bzResourcesOpenResource("sprite", "assets/sprites/%s.aseprite", identifier);
318         //PHYSFS_sint64 length = PHYSFS_fileLength(handle);
319         //void *data = alloca(length);
320         //PHYSFS_readBytes(handle, data, length);
321         
322         ASE_Header header;
323         bzResourcesReadBytes(handle, &header, sizeof(header));
324         
325         bzAssert(header.magicNumber == 0xA5E0);
326
327         size_t pixelDataSize;
328         switch (header.depth) {
329                 case 8:
330                         pixelDataSize = sizeof(ASE_PIXEL_INDEXED);
331                         break;
332
333                 case 16:
334                         pixelDataSize = sizeof(ASE_PIXEL_GRAYSCALE);
335                         break;
336
337                 case 32:
338                         pixelDataSize = sizeof(ASE_PIXEL_RBZA);
339                         break;
340         
341                 default:
342                         assert(false);//, "Invalid image");
343                         break;
344         }
345
346         uint8_t *outData = (uint8_t *)bzMemoryAlloc(arena, header.width * header.height * pixelDataSize);
347         *widthOut = (size_t)header.width;
348         *heightOut = (size_t)header.height;
349
350         for (ASE_WORD frame = 0; frame < header.frames; ++frame) {
351                 ASE_Frame frame;
352                 bzResourcesReadBytes(handle, &frame, sizeof(frame));
353
354                 for (ASE_DWORD chunk = 0; chunk < frame.numChunks; ++chunk) {
355                         size_t chunkStartPosition = bzResourcesTell(handle);
356
357                         ASE_Chunk chunk;
358                         bzResourcesReadBytes(handle, &chunk, sizeof(chunk));
359
360                         switch (chunk.type) {
361                                 case 0x2005: {
362                                         ASE_Chunk_Cel cel;
363                                         bzResourcesReadBytes(handle, &cel, sizeof(cel));
364
365                                         if (cel.celType == 2) {
366                                                 bzMemoryArenaPushWatermark(arena);
367
368                                                 ASE_Chunk_Cel_CompressedImage image;
369                                                 bzResourcesReadBytes(handle, &image, sizeof(image));
370
371                                                 size_t compressedSize = chunk.chunkSize - sizeof(ASE_Chunk_Cel_CompressedImage) - sizeof(ASE_Chunk);
372                                                 int8_t *compressedData = bzMemoryAlloc(arena, compressedSize); // If we do this on the stack (alloca) the Playdate will explode!
373                                                 bzResourcesReadBytes(handle, compressedData, compressedSize);
374
375                                                 size_t imagePixelDataSize = image.pixelWidth * image.pixelHeight * pixelDataSize;
376                                                 int8_t *imagePixelData = bzMemoryAlloc(arena, imagePixelDataSize);
377                                                 stbi_zlib_decode_buffer(imagePixelData, imagePixelDataSize, compressedData, compressedSize);
378
379                                                 for (size_t y = 0; y < image.pixelHeight; ++y) {
380                                                         for (size_t x = 0; x < image.pixelWidth; ++x) {
381                                                                 size_t outX = cel.positionX + x;
382                                                                 size_t outY = cel.positionY + y;
383
384                                                                 for (size_t i = 0; i < pixelDataSize; ++i) {
385                                                                         outData[(outY * header.width + outX) * pixelDataSize + i] = imagePixelData[(y * image.pixelWidth + x) * pixelDataSize + i];
386                                                                 }
387                                                         }
388                                                 }
389
390                                                 bzMemoryArenaPopWatermark(arena);
391
392                                                 // FIXME
393                                                 //bzResourcesSeek(handle, chunkStartPosition + chunk.chunkSize);
394                                         } else {
395                                                 // Skip this chunk...
396                                                 bzResourcesSeek(handle, chunkStartPosition + chunk.chunkSize);
397                                         }
398
399                                         break;
400                                 }
401                                 
402                                 default:
403                                         // Skip this chunk...
404                                         bzResourcesSeek(handle, chunkStartPosition + chunk.chunkSize);
405                                         break;
406                         }
407                 }
408         }
409         
410         bzResourcesCloseResource(handle);
411
412         return outData;
413 }
414
415 size_t bzGfxLoadAsepritePalette(uint32_t *colorsOut, size_t maxColors, const char *filename) {
416         BZResourceID handle = bzResourcesOpenResource("palette", "assets/palettes/%s", filename);
417         
418         ASE_Header header;
419         bzResourcesReadBytes(handle, &header, sizeof(header));
420         
421         bzAssert(header.magicNumber == 0xA5E0);
422
423         size_t colorCount = 0;
424
425         for (ASE_WORD frame = 0; frame < header.frames; ++frame) {
426                 ASE_Frame frame;
427                 bzResourcesReadBytes(handle, &frame, sizeof(frame));
428
429                 for (ASE_DWORD chunk = 0; chunk < frame.numChunks; ++chunk) {
430                         size_t chunkStartPosition = bzResourcesTell(handle);
431
432                         ASE_Chunk chunk;
433                         bzResourcesReadBytes(handle, &chunk, sizeof(chunk));
434
435                         switch (chunk.type) {
436                                 case 0x2019: {
437                                         ASE_Chunk_Palette palette;
438                                         bzResourcesReadBytes(handle, &palette, sizeof(palette));
439
440                                         if (palette.firstColorIdx < maxColors) {
441                                                 size_t lastIdx = bzMin(palette.lastColorIdx + 1, maxColors);
442                                                 for (size_t i = palette.firstColorIdx; i < lastIdx; ++i) {
443                                                         ASE_Chunk_Palette_Entry paletteEntry;
444                                                         bzResourcesReadBytes(handle, &paletteEntry, sizeof(paletteEntry));
445                                                         colorsOut[i] = bzPaletteMakeColor(paletteEntry.r, paletteEntry.g, paletteEntry.b);
446                                                 }
447                                                 colorCount = bzMax(colorCount, lastIdx);
448                                         }
449                                         
450                                         // FIXME
451                                         bzResourcesSeek(handle, chunkStartPosition + chunk.chunkSize);
452
453                                         break;
454                                 }
455                                 
456                                 default:
457                                         // Skip this chunk...
458                                         bzResourcesSeek(handle, chunkStartPosition + chunk.chunkSize);
459                                         break;
460                         }
461                 }
462         }
463         
464         bzResourcesCloseResource(handle);
465
466         return colorCount;
467 }