]> git.bts.cx Git - benzene.git/blob - src/bz/gfx/aseprite.c
Initial version
[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 }