From b7e2fafc855ba934bda9920fc8d98307d4384e27 Mon Sep 17 00:00:00 2001 From: jacob Date: Fri, 30 Jan 2026 05:27:47 -0600 Subject: [PATCH] animation wip --- src/ase/ase.c | 1429 ++++++++----------------------- src/ase/ase.h | 427 +++------ src/meta/meta.c | 2 +- src/pp/pp_vis/pp_vis_core.c | 1 + src/pp/pp_vis/pp_vis_gpu.g | 5 +- src/pp/pp_vis/pp_vis_shared.cgh | 1 + src/sprite/sprite.c | 50 +- src/sprite/sprite.h | 4 +- src/ui/ui.lay | 2 - src/ui/ui_core.c | 204 ++--- src/ui/ui_core.h | 5 - 11 files changed, 615 insertions(+), 1515 deletions(-) diff --git a/src/ase/ase.c b/src/ase/ase.c index 70b34f45..5509092d 100644 --- a/src/ase/ase.c +++ b/src/ase/ase.c @@ -1,14 +1,12 @@ -// DEFLATE decoder based on Handmade Hero's png parser - //////////////////////////////////////////////////////////// //~ Inflate +// +// DEFLATE decoder based on Handmade Hero's png parser //- Bitbuff u32 ASE_PeekBits(ASE_Bitbuff *bb, u32 nbits) { - Assert(nbits <= 32); - u64 cur_byte = bb->cur_bit >> 3; u8 bit_index = bb->cur_bit % 8; u64 nbytes = (nbits + bit_index + 7) >> 3; @@ -108,7 +106,7 @@ ASE_HuffDict ASE_InitHuffDict(Arena *arena, u32 max_code_bits, u32 *bl_counts, u u32 code = next_code[code_bits]++; u32 arbitrary_bits = result.max_code_bits - code_bits; u32 entry_count = (1 << arbitrary_bits); - // TODO: Optimize this. It's bloating load times. + // TODO: Optimize this for (u32 entry_index = 0; entry_index < entry_count; ++entry_index) { u32 base_index = (code << arbitrary_bits) | entry_index; @@ -316,11 +314,343 @@ void ASE_Inflate(u8 *dst, u8 *encoded) } //////////////////////////////////////////////////////////// -//~ Meta +//~ Decode + +// https://github.com/aseprite/aseprite/blob/main/docs/ase-file-specs.md ASE_Meta ASE_DecodeMeta(Arena *arena, String encoded) { + TempArena scratch = BeginScratch(arena); + b32 ok = 1; + + BB_Buff bb = BB_BuffFromString(encoded); + BB_Reader bbr = BB_ReaderFromBuffNoDebug(&bb); + + ASE_Header ase_header; + BB_ReadBytes(&bbr, StringFromStruct(&ase_header)); + ok = ok && ase_header.magic == 0xA5E0; + ok = ok && ase_header.color_depth == 32; + + Vec2 canvas_dims = VEC2(ase_header.width, ase_header.height); + Rng2 canvas_bounds = RNG2(VEC2(0, 0), canvas_dims); + + ////////////////////////////// + //- Decode chunks + + i64 layer_chunks_count = 0; + ASE_Chunk *first_chunk = 0; + ASE_Chunk *last_chunk = 0; + + i64 frames_count = ase_header.frames_count; + if (ok) + { + for (i64 frame_idx = 0; frame_idx < frames_count; ++frame_idx) + { + ASE_FrameHeader frame_header; + BB_ReadBytes(&bbr, StringFromStruct(&frame_header)); + ok = ok && frame_header.magic == 0xF1FA; + + if (ok) + { + i64 chunks_count = frame_header.chunks_count1; + if (chunks_count == 0) + { + chunks_count = frame_header.chunks_count0; + } + + for (i64 chunk_idx = 0; chunk_idx < chunks_count; ++chunk_idx) + { + i64 chunk_size = BB_ReadUBits(&bbr, 32); + ASE_EncodedChunkKind encoded_chunk_kind = BB_ReadUBits(&bbr, 16); + chunk_size -= 6; // Chunk size includes size of chunk_size & chunk_kind + + if (chunk_size > 0) + { + u64 chunk_end_pos = BB_GetCurrentReaderByte(&bbr) + chunk_size; + switch (encoded_chunk_kind) + { + //- Layer chunk + case ASE_EncodedChunkKind_Layer: + { + ASE_Chunk *chunk = PushStruct(scratch.arena, ASE_Chunk); + SllQueuePush(first_chunk, last_chunk, chunk); + chunk->kind = ASE_ChunkKind_Layer; + chunk->layer.flags = BB_ReadUBits(&bbr, 16); + chunk->layer.kind = BB_ReadUBits(&bbr, 16); + u32 child_level = BB_ReadUBits(&bbr, 16); + BB_ReadSeekBytes(&bbr, sizeof(u16) * 2); + u32 blend_mode = BB_ReadUBits(&bbr, 16); + { + u8 opacity_u8 = BB_ReadUBits(&bbr, 8); + if (!(ase_header.flags & 1)) + { + opacity_u8 = 255; + } + chunk->opacity = (f32)opacity_u8 / 255.0f; + } + BB_ReadSeekBytes(&bbr, 3); + { + chunk->name.len = BB_ReadUBits(&bbr, 16); + chunk->name.text = BB_ReadBytesRaw(&bbr, chunk->name.len); + if (!chunk->name.text) + { + chunk->name.len = 0; + } + } + if (chunk->layer.kind == ASE_LayerKind_Tilemap) + { + u32 tileset_index = BB_ReadUBits(&bbr, 32); + } + chunk->layer.cel_chunks = PushStructs(scratch.arena, ASE_Chunk *, frames_count); + chunk->chunk_layer_idx = layer_chunks_count++; + } break; + + //- Cel chunk + case ASE_EncodedChunkKind_Cel: + { + ASE_Chunk *chunk = PushStruct(scratch.arena, ASE_Chunk); + SllQueuePush(first_chunk, last_chunk, chunk); + chunk->kind = ASE_ChunkKind_Cel; + chunk->chunk_layer_idx =BB_ReadUBits(&bbr, 16); + + chunk->cel.bounds.p0.x = BB_ReadIBits(&bbr, 16); + chunk->cel.bounds.p0.y = BB_ReadIBits(&bbr, 16); + chunk->opacity = BB_ReadUBits(&bbr, 8) / 255.0f; + + chunk->cel.kind = BB_ReadUBits(&bbr, 16); + i32 z_index = BB_ReadIBits(&bbr, 16); + BB_ReadSeekBytes(&bbr, 5); + + chunk->cel.frame_idx = frame_idx; + chunk->cel.linked_frame_idx = frame_idx; + + switch (chunk->cel.kind) + { + case ASE_CelKind_CompressedTilemap: + case ASE_CelKind_Raw: + { + // Unsupported + BB_ReadSeekToByte(&bbr, chunk_end_pos); + } break; + + case ASE_CelKind_Linked: + { + chunk->cel.linked_frame_idx = BB_ReadUBits(&bbr, 16); + } break; + + case ASE_CelKind_CompressedImage: + { + i64 width = BB_ReadUBits(&bbr, 16); + i64 height = BB_ReadUBits(&bbr, 16); + chunk->cel.bounds.p1.x = chunk->cel.bounds.p0.x + width; + chunk->cel.bounds.p1.y = chunk->cel.bounds.p0.y + height; + chunk->cel.encoded.len = chunk_end_pos - BB_GetCurrentReaderByte(&bbr); + chunk->cel.encoded.text = BB_ReadBytesRaw(&bbr, chunk->cel.encoded.len); + } break; + } + } break; + + //- Tags chunk + case ASE_EncodedChunkKind_Tags: + { + i64 tags_count = BB_ReadUBits(&bbr, 16); + BB_ReadSeekBytes(&bbr, 8); + + for (i64 tag_idx = 0; tag_idx < tags_count; ++tag_idx) + { + ASE_Chunk *chunk = PushStruct(scratch.arena, ASE_Chunk); + SllQueuePush(first_chunk, last_chunk, chunk); + chunk->kind = ASE_ChunkKind_Tag; + + chunk->tag.from = BB_ReadUBits(&bbr, 16); + chunk->tag.to = BB_ReadUBits(&bbr, 16); + BB_ReadSeekBytes(&bbr, 13); + + chunk->name.len = BB_ReadUBits(&bbr, 16); + chunk->name.text = BB_ReadBytesRaw(&bbr, chunk->name.len); + if (!chunk->name.text) + { + chunk->name.len = 0; + } + } + } break; + } + BB_ReadSeekToByte(&bbr, chunk_end_pos); + } + } + } + } + // Sanity check to make sure all data was read + Assert(BB_NumBytesRemaining(&bbr) == 0); + } + + ////////////////////////////// + //- Create layer/cel chunk lookup + + ASE_Chunk **layer_chunks = PushStructs(scratch.arena, ASE_Chunk *, layer_chunks_count); + { + // Fill layer chunks + for (ASE_Chunk *chunk = first_chunk; chunk; chunk = chunk->next) + { + if ( + chunk->kind == ASE_ChunkKind_Layer && + chunk->chunk_layer_idx < layer_chunks_count + ) + { + layer_chunks[chunk->chunk_layer_idx] = chunk; + } + } + // Fill layer chunk cels + for (ASE_Chunk *chunk = first_chunk; chunk; chunk = chunk->next) + { + if ( + chunk->kind == ASE_ChunkKind_Cel && + chunk->chunk_layer_idx < layer_chunks_count && + chunk->cel.frame_idx < frames_count + ) + { + ASE_Chunk *layer_chunk = layer_chunks[chunk->chunk_layer_idx]; + if (layer_chunk != 0) + { + layer_chunk->layer.cel_chunks[chunk->cel.frame_idx] = chunk; + } + } + } + } + + ////////////////////////////// + //- Push layers + + i64 layers_count = 0; + ASE_Layer *first_layer = 0; + ASE_Layer *last_layer = 0; + if (ok) + { + i64 chunk_layer_idx = 0; + for (ASE_Chunk *chunk = first_chunk; chunk; chunk = chunk->next) + { + b32 allowed = ( + chunk->kind == ASE_ChunkKind_Layer && + chunk->layer.kind == ASE_LayerKind_Normal && + chunk->chunk_layer_idx < layer_chunks_count && + !AnyBit(chunk->layer.flags, ASE_LayerFlag_ReferenceLayer) + ); + + if (allowed) + { + ASE_Layer *layer = PushStruct(arena, ASE_Layer); + DllQueuePush(first_layer, last_layer, layer); + layer->name = chunk->name; + layer->cels = PushStructs(arena, ASE_Cel, frames_count); + chunk->layer.final = layer; + ++layers_count; + } + } + } + + ////////////////////////////// + //- Push cels + + if (ok) + { + for (ASE_Chunk *chunk = first_chunk; chunk; chunk = chunk->next) + { + b32 allowed = ( + chunk->kind == ASE_ChunkKind_Cel && + chunk->cel.kind != ASE_CelKind_CompressedTilemap && + chunk->cel.frame_idx < frames_count && + chunk->cel.linked_frame_idx < frames_count && + chunk->chunk_layer_idx < layer_chunks_count + ); + + ASE_Chunk *layer_chunk = 0; + ASE_Layer *layer = 0; + if (allowed) + { + layer_chunk = layer_chunks[chunk->chunk_layer_idx]; + if (layer_chunk) + { + layer = layer_chunk->layer.final; + } + allowed = layer != 0; + } + + ASE_Chunk *linked_chunk = 0; + Rng2 clipped_bounds = Zi; + if (allowed) + { + linked_chunk = layer_chunk->layer.cel_chunks[chunk->cel.linked_frame_idx]; + if (!linked_chunk) + { + linked_chunk = chunk; + } + clipped_bounds = linked_chunk->cel.bounds; + clipped_bounds = IntersectRng2(clipped_bounds, canvas_bounds); + allowed = !IsRng2Empty(clipped_bounds); + } + + if (allowed) + { + ASE_Cel *cel = &layer->cels[chunk->cel.frame_idx]; + // Original cel properties + cel->frame_idx = chunk->cel.frame_idx; + cel->linked_frame_idx = linked_chunk->cel.frame_idx; + // Linked cel properties + cel->opacity = linked_chunk->opacity; + cel->frame_idx = linked_chunk->cel.frame_idx; + cel->encoded = linked_chunk->cel.encoded; + cel->bounds = clipped_bounds; + } + } + } + + ////////////////////////////// + //- Push spans + + i64 spans_count = 0; + ASE_Span *first_span = 0; + ASE_Span *last_span = 0; + if (ok) + { + for (ASE_Chunk *chunk = first_chunk; chunk; chunk = chunk->next) + { + b32 allowed = ( + chunk->kind == ASE_ChunkKind_Tag && + chunk->tag.from >= 0 && + chunk->tag.to <= frames_count && + chunk->tag.to > chunk->tag.from + ); + + if (allowed) + { + ASE_Span *span = PushStruct(arena, ASE_Span); + DllQueuePush(first_span, last_span, span); + span->name = chunk->name; + span->from = chunk->tag.from; + span->to = chunk->tag.to; + } + } + } + + ////////////////////////////// + //- Build result + ASE_Meta result = Zi; + { + result.frames_count = frames_count; + + result.layers_count = layers_count; + result.first_layer = first_layer; + result.last_layer = last_layer; + + result.spans_count = spans_count; + result.first_span = first_span; + result.last_span = last_span; + + result.ok = ok; + } + + EndScratch(scratch); return result; } @@ -342,13 +672,15 @@ ASE_Image ASE_PushBlankImage(Arena *arena, Rng2 bounds) ASE_Image ASE_DecompressImageFromCel(Arena *arena, ASE_Cel *cel) { ASE_Image result = Zi; - result.bounds = cel->bounds; - result.opacity = cel->opacity; - - i64 pixels_count = AreaFromVec2(DimsFromRng2(result.bounds)); - result.pixels = PushStructsNoZero(arena, u32, pixels_count); - ASE_Inflate((u8 *)result.pixels, cel->encoded.text); - + b32 ok = !IsRng2Empty(cel->bounds) && cel->encoded.len > 0; + if (ok) + { + result.bounds = cel->bounds; + result.opacity = cel->opacity; + i64 pixels_count = AreaFromVec2(DimsFromRng2(result.bounds)); + result.pixels = PushStructsNoZero(arena, u32, pixels_count); + ASE_Inflate((u8 *)result.pixels, cel->encoded.text); + } return result; } @@ -404,1074 +736,3 @@ void ASE_BlendImage(ASE_Image src_img, ASE_Image dst_img) } } } - - - - - - - - - - - - - -// //////////////////////////////////////////////////////////// -// //~ Query - -// u32 ASE_FirstPixelFromCel(ASE_Cel *cel) -// { -// u32 result = 0; -// return result; -// } - -// //////////////////////////////////////////////////////////// -// //~ Rasterize - -// void ASE_RasterizeCel(ASE_Cel *cel, u32 *dst_pixels, Vec2 dst_dims) -// { -// TempArena scratch = BeginScratchNoConflict(); - -// // ASE_Layer *layer = cel->layer; -// // ASE_Meta *meta = layer->meta; -// // u8 opacity = (cel->opacity / 255.0f) * (layer->opacity / 255.0f) * 255.0f; - -// // // Adjust bounds to ensure pixels outside of frame boundaries -// // // aren't processed (aseprite keeps chunks outside of frame -// // // around in project file). -// // { -// // i32 frame_right = cel_width + frame_left; -// // i32 frame_bottom = frame_top + cel_height; -// // if (frame_left < 0) -// // { -// // cel_left += -frame_left; -// // frame_left = 0; -// // } -// // if (frame_top < 0) -// // { -// // cel_top += -frame_top; -// // frame_top = 0; -// // } -// // if (frame_right > (i32)frame_width) -// // { -// // cel_right -= (frame_right - frame_width); -// // frame_right = frame_width; -// // } -// // if (frame_bottom > (i32)frame_height) -// // { -// // cel_bottom -= (frame_bottom - frame_height); -// // frame_bottom = frame_height; -// // } -// // } - -// i64 src_pixels_count = DimsFromRng2(cel->bounds).x * DimsFromRng2(cel->bounds).y; -// u32 *src_pixels = PushStructsNoZero(scratch.arena, u32, src_pixels_count); -// { - -// } - -// u8 opacity_u8 = (u8)(cel->opacity * 255.0f); - -// Rng2 bounds = IntersectRng2(cel->bounds, RNG2(Vec2(0, 0), dst_dims)); -// Vec2 dims = DimsFromRng2(bounds); - -// for (i32 cel_y = 0; cel_y < dims.y; ++cel_y) -// { -// i32 dst_y = bounds.p0.y + cel_y; -// i32 cel_offset = cel_y * cel_width; -// i32 dst_offset = dst_y * dst_width; -// for (i32 cel_x = 0; cel_x < dims.x; ++cel_x) -// { -// i32 dst_x = bounds.p0.x + cel_x; -// u32 *src_pixel = &cel->pixels[cel_x + cel_offset]; -// u32 *dst_pixel = &dst_pixels[dst_x + dst_offset]; -// *dst_pixel = ASE_Blend(*src_pixel, *dst_pixel, opacity_u8); -// } -// } - -// EndScratch(scratch); -// } - - - - - - - - - - - - - - - -// // DEFLATE decoder based on Handmade Hero's png parser - -// //////////////////////////////////////////////////////////// -// //~ Shared constants - -// Global Readonly u32 ASE_HuffHclenOrder[] = { -// 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 -// }; - -// Global Readonly ASE_HuffEntry ASE_HuffLenTable[] = { -// {3, 0}, // 257 -// {4, 0}, // 258 -// {5, 0}, // 259 -// {6, 0}, // 260 -// {7, 0}, // 261 -// {8, 0}, // 262 -// {9, 0}, // 263 -// {10, 0}, // 264 -// {11, 1}, // 265 -// {13, 1}, // 266 -// {15, 1}, // 267 -// {17, 1}, // 268 -// {19, 2}, // 269 -// {23, 2}, // 270 -// {27, 2}, // 271 -// {31, 2}, // 272 -// {35, 3}, // 273 -// {43, 3}, // 274 -// {51, 3}, // 275 -// {59, 3}, // 276 -// {67, 4}, // 277 -// {83, 4}, // 278 -// {99, 4}, // 279 -// {115, 4}, // 280 -// {131, 5}, // 281 -// {163, 5}, // 282 -// {195, 5}, // 283 -// {227, 5}, // 284 -// {258, 0}, // 285 -// }; - -// Global Readonly ASE_HuffEntry ASE_HuffDistTable[] = { -// {1, 0}, // 0 -// {2, 0}, // 1 -// {3, 0}, // 2 -// {4, 0}, // 3 -// {5, 1}, // 4 -// {7, 1}, // 5 -// {9, 2}, // 6 -// {13, 2}, // 7 -// {17, 3}, // 8 -// {25, 3}, // 9 -// {33, 4}, // 10 -// {49, 4}, // 11 -// {65, 5}, // 12 -// {97, 5}, // 13 -// {129, 6}, // 14 -// {193, 6}, // 15 -// {257, 7}, // 16 -// {385, 7}, // 17 -// {513, 8}, // 18 -// {769, 8}, // 19 -// {1025, 9}, // 20 -// {1537, 9}, // 21 -// {2049, 10}, // 22 -// {3073, 10}, // 23 -// {4097, 11}, // 24 -// {6145, 11}, // 25 -// {8193, 12}, // 26 -// {12289, 12}, // 27 -// {16385, 13}, // 28 -// {24577, 13}, // 29 -// }; - -// Global Readonly u32 ASE_HuffBlCounts[][2] = { -// {143, 8}, -// {255, 9}, -// {279, 7}, -// {287, 8}, -// {319, 5}, -// }; - -// //////////////////////////////////////////////////////////// -// //~ Ase bitbuff - -// u32 ASE_PeekBits(ASE_Bitbuff *bb, u32 nbits) -// { -// Assert(nbits <= 32); - -// u64 cur_byte = bb->cur_bit >> 3; -// u8 bit_index = bb->cur_bit % 8; -// u64 nbytes = (nbits + bit_index + 7) >> 3; - -// u64 val64 = 0; -// CopyBytes(&val64, &bb->data[cur_byte], nbytes); -// u32 val32 = (u32)(val64 >> bit_index); -// val32 &= U32Max >> (32 - nbits); - -// return val32; -// } - -// u32 ASE_ConsumeBits(ASE_Bitbuff *bb, u32 nbits) -// { -// u32 val = ASE_PeekBits(bb, nbits); -// bb->cur_bit += nbits; -// return val; -// } - -// void ASE_SkipBits(ASE_Bitbuff *bb, u32 nbits) -// { -// bb->cur_bit += nbits; -// } - -// //////////////////////////////////////////////////////////// -// //~ Inflate - -// u32 ASE_ReverseBits(u32 v, u32 bit_count) -// { -// // 7 & 15 seem to be the most common bit_counts, so a -// // more optimal path is laid out for them. -// if (bit_count == 15) -// { -// u32 b1 = v & 0xFF; -// b1 = (b1 & 0xF0) >> 4 | (b1 & 0x0F) << 4; -// b1 = (b1 & 0xCC) >> 2 | (b1 & 0x33) << 2; -// b1 = (b1 & 0xAA) >> 1 | (b1 & 0x55) << 1; - -// u32 b2 = (v & 0xFF00) >> 8; -// b2 = (b2 & 0xF0) >> 4 | (b2 & 0x0F) << 4; -// b2 = (b2 & 0xCC) >> 2 | (b2 & 0x33) << 2; -// b2 = (b2 & 0xAA) >> 1 | (b2 & 0x55) << 1; -// b2 >>= 1; - -// return (b1 << 7) | b2; -// } -// else if (bit_count == 7) -// { -// v = (v & 0xF0) >> 4 | (v & 0x0F) << 4; -// v = (v & 0xCC) >> 2 | (v & 0x33) << 2; -// v = (v & 0xAA) >> 1 | (v & 0x55) << 1; -// return v >> 1; -// } -// else -// { -// u32 result = 0; -// for (u32 i = 0; i <= (bit_count / 2); ++i) -// { -// u32 inv = (bit_count - (i + 1)); -// result |= ((v >> i) & 0x1) << inv; -// result |= ((v >> inv) & 0x1) << i; -// } -// return result; -// } -// } - -// ASE_HuffDict ASE_InitHuffDict(Arena *arena, u32 max_code_bits, u32 *bl_counts, u32 bl_counts_count) -// { -// ASE_HuffDict result = Zi; -// result.max_code_bits = max_code_bits; -// result.entries_count = (1 << max_code_bits); -// result.entries = PushStructsNoZero(arena, ASE_HuffEntry, result.entries_count); - -// u32 code_length_hist[ASE_HuffBitCount] = Zi; -// for (u32 bl_count_index = 0; bl_count_index < bl_counts_count; ++bl_count_index) -// { -// u32 count = bl_counts[bl_count_index]; -// Assert(count <= countof(code_length_hist)); -// ++code_length_hist[count]; -// } - -// u32 next_code[ASE_HuffBitCount] = Zi; -// next_code[0] = 0; -// code_length_hist[0] = 0; -// for (u32 code_index = 1; code_index < countof(next_code); ++code_index) -// { -// next_code[code_index] = ((next_code[code_index - 1] + code_length_hist[code_index - 1]) << 1); -// } - -// for (u32 bl_count_index = 0; bl_count_index < bl_counts_count; ++bl_count_index) -// { -// u32 code_bits = bl_counts[bl_count_index]; -// if (code_bits) -// { -// Assert(code_bits < countof(next_code)); -// u32 code = next_code[code_bits]++; -// u32 arbitrary_bits = result.max_code_bits - code_bits; -// u32 entry_count = (1 << arbitrary_bits); -// // TODO: Optimize this. It's bloating load times. -// for (u32 entry_index = 0; entry_index < entry_count; ++entry_index) -// { -// u32 base_index = (code << arbitrary_bits) | entry_index; -// u32 index = ASE_ReverseBits(base_index, result.max_code_bits); -// ASE_HuffEntry *entry = &result.entries[index]; -// entry->symbol = (u16)bl_count_index; -// entry->bits_used = (u16)code_bits; -// } -// } -// } - -// return result; -// } - -// u16 ASE_DecodeHuffDict(ASE_HuffDict *huffman, ASE_Bitbuff *bb) -// { -// u32 index = ASE_PeekBits(bb, huffman->max_code_bits); -// Assert(index < huffman->entries_count); - -// ASE_HuffEntry *entry = &huffman->entries[index]; -// u16 result = entry->symbol; -// ASE_SkipBits(bb, entry->bits_used); -// Assert(entry->bits_used > 0); -// return result; -// } - -// void ASE_Inflate(u8 *dst, u8 *encoded) -// { -// TempArena scratch = BeginScratchNoConflict(); - -// ASE_Bitbuff bb = { .data = encoded }; - -// // ZLIB header -// u32 cm = ASE_ConsumeBits(&bb, 4); -// u32 cinfo = ASE_ConsumeBits(&bb, 4); -// Assert(cm == 8); -// Assert(cinfo == 7); - -// u32 fcheck = ASE_ConsumeBits(&bb, 5); -// u32 fdict = ASE_ConsumeBits(&bb, 1); -// u32 flevl = ASE_ConsumeBits(&bb, 2); -// Assert(fdict == 0); - -// u8 cmf = (u8)(cm | (cinfo << 4)); -// u8 flg = fcheck | (fdict << 5) | (flevl << 6); -// Assert(((cmf * 256) + flg) % 31 == 0); - -// u8 bfinal = 0; -// while (!bfinal) -// { -// bfinal = ASE_ConsumeBits(&bb, 1); -// u8 btype = ASE_ConsumeBits(&bb, 2); -// switch (btype) -// { -// case ASE_BlockType_Uncompressed: -// { -// ASE_SkipBits(&bb, (8 - (bb.cur_bit % 8)) % 8); -// i16 len = ASE_ConsumeBits(&bb, 16); -// i16 nlen = ASE_ConsumeBits(&bb, 16); -// Assert(len == ~nlen); // Validation -// while (len-- > 0) -// { -// *dst++ = ASE_ConsumeBits(&bb, 8); -// } -// } break; - -// case ASE_BlockType_CompressedFixed: -// case ASE_BlockType_CompressedDynamic: -// { -// TempArena temp = BeginTempArena(scratch.arena); -// u32 lit_len_dist_table[512] = Zi; -// u32 hlit; -// u32 hdist; - -// if (btype == ASE_BlockType_CompressedDynamic) -// { -// // Dynamic table - -// // Read huffman table -// hlit = ASE_ConsumeBits(&bb, 5) + 257; -// hdist = ASE_ConsumeBits(&bb, 5) + 1; -// u32 hclen = ASE_ConsumeBits(&bb, 4) + 4; - -// // Init dict huffman (hclen) -// u32 hclen_bl_counts[19] = Zi; -// for (u32 i = 0; i < hclen; ++i) -// { -// u32 code = ASE_HuffHclenOrder[i]; -// hclen_bl_counts[code] = ASE_ConsumeBits(&bb, 3); -// } -// ASE_HuffDict dict_huffman = ASE_InitHuffDict(temp.arena, 7, hclen_bl_counts, countof(hclen_bl_counts)); - -// // Decode dict huffman -// u32 lit_len_count = 0; -// u32 len_count = hlit + hdist; -// Assert(len_count <= countof(lit_len_dist_table)); -// while (lit_len_count < len_count) -// { -// u32 rep_count = 1; -// u32 rep_val = 0; -// u32 encoded_len = ASE_DecodeHuffDict(&dict_huffman, &bb); -// if (encoded_len <= 15) -// { -// rep_val = encoded_len; -// } -// else if (encoded_len == 16) -// { -// rep_count = 3 + ASE_ConsumeBits(&bb, 2); -// Assert(lit_len_count > 0); -// rep_val = lit_len_dist_table[lit_len_count - 1]; -// } -// else if (encoded_len == 17) -// { -// rep_count = 3 + ASE_ConsumeBits(&bb, 3); -// } -// else if (encoded_len == 18) -// { -// rep_count = 11 + ASE_ConsumeBits(&bb, 7); -// } -// else -// { -// // Invalid len -// Assert(0); -// } - -// while (rep_count--) -// { -// lit_len_dist_table[lit_len_count++] = rep_val; -// } -// } -// Assert(lit_len_count == len_count); -// } -// else -// { -// // Fixed table -// hlit = 288; -// hdist = 32; -// u32 index = 0; -// for (u32 i = 0; i < countof(ASE_HuffBlCounts); ++i) -// { -// u32 bit_count = ASE_HuffBlCounts[i][1]; -// u32 last_valuie = ASE_HuffBlCounts[i][0]; -// while (index <= last_valuie) -// { -// lit_len_dist_table[index++] = bit_count; -// } -// } -// } - -// // Decode -// ASE_HuffDict lit_len_huffman = ASE_InitHuffDict(temp.arena, 15, lit_len_dist_table, hlit); -// ASE_HuffDict dist_huffman = ASE_InitHuffDict(temp.arena, 15, lit_len_dist_table + hlit, hdist); -// for (;;) -// { -// u32 lit_len = ASE_DecodeHuffDict(&lit_len_huffman, &bb); -// if (lit_len <= 255) -// { -// *dst++ = lit_len & 0xFF; -// } -// else if (lit_len >= 257) -// { -// u32 length_index = (lit_len - 257); -// ASE_HuffEntry length_entry = ASE_HuffLenTable[length_index]; -// u32 length = length_entry.symbol; -// if (length_entry.bits_used > 0) -// { -// u32 extra_bits = ASE_ConsumeBits(&bb, length_entry.bits_used); -// length += extra_bits; -// } - -// u32 dist_index = ASE_DecodeHuffDict(&dist_huffman, &bb); -// ASE_HuffEntry dist_entry = ASE_HuffDistTable[dist_index]; -// u32 distance = dist_entry.symbol; -// if (dist_entry.bits_used > 0) -// { -// u32 extra_bits = ASE_ConsumeBits(&bb, dist_entry.bits_used); -// distance += extra_bits; -// } -// u8 *src = dst - distance; -// while (length--) -// { -// *dst++ = *src++; -// } -// } -// else -// { -// break; -// } -// } - -// EndTempArena(temp); -// } break; - -// case ASE_BlockType_Reserved: -// { -// // TODO -// Assert(0); -// } break; -// } -// } - -// EndScratch(scratch); -// } - -// //////////////////////////////////////////////////////////// -// //~ Error helpers - -// void ASE_PushError(Arena *arena, ASE_ErrorList *list, String msg_src) -// { -// ASE_Error *e = PushStruct(arena, ASE_Error); -// e->msg = PushString(arena, msg_src); -// if (!list->first) -// { -// list->first = e; -// } -// else -// { -// list->last->next = e; -// } -// list->last = e; -// ++list->count; -// } - -// //////////////////////////////////////////////////////////// -// //~ Decode helpers - -// u32 ASE_Blend(u32 src, u32 dst, u8 opacity) -// { -// u32 dst_r = (dst & 0xff); -// u32 dst_g = (dst >> 8) & 0xff; -// u32 dst_b = (dst >> 16) & 0xff; -// u32 dst_a = (dst >> 24) & 0xff; - -// u32 src_r = (src & 0xff); -// u32 src_g = (src >> 8) & 0xff; -// u32 src_b = (src >> 16) & 0xff; -// u32 src_a = (src >> 24) & 0xff; - -// src_a = (u8)MulNormalizedU8(src_a, opacity); -// u32 a = src_a + dst_a - MulNormalizedU8(src_a, dst_a); - -// u32 r = 0; -// u32 g = 0; -// u32 b = 0; -// if (a != 0) -// { -// r = dst_r + (src_r - dst_r) * src_a / a; -// g = dst_g + (src_g - dst_g) * src_a / a; -// b = dst_b + (src_b - dst_b) * src_a / a; -// } - -// return (r << 0) | (g << 8) | (b << 16) | (a << 24); -// } - -// void ASE_MakeDimensionsSquareish(ASE_Header *header, u32 *frames_x, u32 *frames_y, u64 *image_width, u64 *image_height) -// { -// // Try and get image resolution into as much of a square as possible by -// // separating frames into multiple rows. -// while (*frames_x > 1) -// { -// u64 new_frames_x = *frames_x - 1; -// u64 new_frames_y = ((header->frames - 1) / new_frames_x) + 1; -// u64 new_image_width = header->width * new_frames_x; -// u64 new_image_height = header->height * new_frames_y; -// if (new_image_width >= new_image_height) -// { -// *frames_x = new_frames_x; -// *frames_y = new_frames_y; -// *image_width = new_image_width; -// *image_height = new_image_height; -// } -// else -// { -// break; -// } -// } -// } - -// //////////////////////////////////////////////////////////// -// //~ Decode image - -// ASE_DecodedImage ASE_DecodeImage(Arena *arena, String encoded) -// { -// TempArena scratch = BeginScratch(arena); -// ASE_DecodedImage result = Zi; - -// BB_Buff bb = BB_BuffFromString(encoded); -// BB_Reader bbr = BB_ReaderFromBuffNoDebug(&bb); -// ASE_Header ase_header; -// BB_ReadBytes(&bbr, StringFromStruct(&ase_header)); - -// if (ase_header.magic != 0xA5E0) -// { -// ASE_PushError(arena, &result.errors, Lit("Not a valid aseprite file")); -// goto abort; -// } - -// if (ase_header.color_depth != 32) -// { -// String msg = StringF( -// scratch.arena, -// "Only 32 bit rgba color mode is supported (got %F)", -// FmtUint(ase_header.color_depth) -// ); -// ASE_PushError(arena, &result.errors, msg); -// goto abort; -// } - -// u64 frame_width = ase_header.width; -// u64 frame_height = ase_header.height; - -// u32 frames_x = ase_header.frames; -// u32 frames_y = 1; -// u64 image_width = frame_width * frames_x; -// u64 image_height = frame_height * frames_y; -// ASE_MakeDimensionsSquareish(&ase_header, &frames_x, &frames_y, &image_width, &image_height); - -// result.width = image_width; -// result.height = image_height; -// // TODO: Optimize this. Naive memzero is bloating the decode time for large images. -// result.pixels = PushStructs(arena, u32, image_width * image_height); - -// u32 layers_count = 0; -// ASE_Layer *layer_head = 0; - -// ASE_Cel *cel_head = 0; -// ASE_Cel *cel_tail = 0; - -// ////////////////////////////// -// //- Iterate frames - -// u32 frames_count = 0; -// for (u16 i = 0; i < ase_header.frames; ++i) -// { -// ASE_FrameHeader frame_header; -// BB_ReadBytes(&bbr, StringFromStruct(&frame_header)); - -// u32 chunks_count = frame_header.chunks_new; -// if (chunks_count == 0) -// { -// Assert(frame_header.chunks_old != 0xFFFF); -// chunks_count = frame_header.chunks_old; -// } - -// ////////////////////////////// -// //- Iterate chunks - -// for (u32 j = 0; j < chunks_count; ++j) -// { -// u32 chunk_size = BB_ReadUBits(&bbr, 32); -// ASE_ChunkKind chunk_type = BB_ReadUBits(&bbr, 16); - -// // Chunk size includes size & type -// Assert(chunk_size >= 6); -// chunk_size -= 6; - -// u64 chunk_end_pos = BB_GetCurrentReaderByte(&bbr) + chunk_size; - -// switch (chunk_type) -// { -// default: -// { -// BB_ReadSeekToByte(&bbr, chunk_end_pos); -// } break; - -// ////////////////////////////// -// //- Decode layer - -// case ASE_ChunkKind_Layer: -// { -// ASE_Layer *layer = PushStruct(scratch.arena, ASE_Layer); -// layer->next = layer_head; -// layer_head = layer; - -// layer->flags = BB_ReadUBits(&bbr, 16); -// layer->type = BB_ReadUBits(&bbr, 16); -// layer->child_level = BB_ReadUBits(&bbr, 16); - -// // Ignoring layer default width & height -// BB_ReadSeekBytes(&bbr, sizeof(u16) * 2); - -// layer->blend_mode = BB_ReadUBits(&bbr, 16); -// if (layer->blend_mode != 0) -// { -// ASE_PushError( -// arena, -// &result.errors, -// Lit("Layer has unsupported blend mode (only 'Normal' mode is supported). Tip: Try using 'merge down' to create a normal layer as a workaround") -// ); -// goto abort; -// } - -// layer->opacity = BB_ReadUBits(&bbr, 8); -// if (!(ase_header.flags & 1)) -// { -// layer->opacity = 255; -// } - -// BB_ReadSeekBytes(&bbr, sizeof(u8) * 3); - -// u16 str_len = BB_ReadUBits(&bbr, 16); -// layer->name = (String) { str_len, PushStructsNoZero(scratch.arena, u8, str_len) }; -// BB_ReadBytes(&bbr, layer->name); - -// if (layer->type == 2) -// { -// layer->tileset_index = BB_ReadUBits(&bbr, 32); -// } - -// layer->index = layers_count++; -// } break; - -// ////////////////////////////// -// //- Decode cel - -// case ASE_ChunkKind_Cel: -// { -// ASE_Cel *cel = PushStruct(scratch.arena, ASE_Cel); -// if (cel_tail) -// { -// cel_tail->next = cel; -// } -// else -// { -// cel_head = cel; -// } -// cel_tail = cel; - -// cel->layer_index = BB_ReadUBits(&bbr, 16); -// cel->x_pos = BB_ReadIBits(&bbr, 16); -// cel->y_pos = BB_ReadIBits(&bbr, 16); -// cel->opacity = BB_ReadUBits(&bbr, 8); -// cel->type = BB_ReadUBits(&bbr, 16); -// cel->z_index = BB_ReadIBits(&bbr, 16); -// BB_ReadSeekBytes(&bbr, sizeof(u8) * 5); - -// cel->frame_index = frames_count; - -// switch (cel->type) -// { -// case ASE_CelKind_RawImage: -// { -// // Unsupported -// BB_ReadSeekToByte(&bbr, chunk_end_pos); -// } break; - -// case ASE_CelKind_Linked: -// { -// cel->frame_pos = BB_ReadUBits(&bbr, 16); -// // Actual linking happens later after iteration -// } break; - -// case ASE_CelKind_CompressedImage: -// { -// cel->width = BB_ReadUBits(&bbr, 16); -// cel->height = BB_ReadUBits(&bbr, 16); - -// cel->pixels = PushStructsNoZero(scratch.arena, u32, cel->width * cel->height); -// u8 *huffman_encoded = BB_ReadBytesRaw(&bbr, chunk_end_pos - BB_GetCurrentReaderByte(&bbr)); -// if (huffman_encoded) -// { -// ASE_Inflate((u8 *)cel->pixels, huffman_encoded); -// } -// } break; - -// case ASE_CelKind_CompressedTilemap: -// { -// // Unsupported -// ASE_PushError(arena, &result.errors, Lit("Tilemaps are not supported")); -// goto abort; -// } break; -// } -// } break; -// } -// } -// ++frames_count; -// } - -// ////////////////////////////// -// //- Create ordered layers array - -// ASE_Layer **layers_ordered = PushStructsNoZero(scratch.arena, ASE_Layer *, layers_count); -// for (ASE_Layer *layer = layer_head; layer; layer = layer->next) -// { -// layers_ordered[layer->index] = layer; -// } - -// ////////////////////////////// -// //- Link cels - -// ASE_Cel **cels_ordered = PushStructsNoZero(scratch.arena, ASE_Cel *, frames_count * layers_count); -// for (ASE_Cel *cel = cel_head; cel; cel = cel->next) -// { -// cels_ordered[(cel->frame_index * layers_count) + cel->layer_index] = cel; -// if (cel->type == ASE_CelKind_Linked) -// { -// ASE_Cel *ref_cel = cels_ordered[(cel->frame_pos * layers_count) + cel->layer_index]; -// cel->width = ref_cel->width; -// cel->height = ref_cel->height; -// cel->pixels = ref_cel->pixels; -// } -// } - -// ////////////////////////////// -// //- Assemble image from cels - -// { -// for (ASE_Cel *cel = cel_head; cel; cel = cel->next) -// { -// ASE_Layer *layer = layers_ordered[cel->layer_index]; -// // Only draw visible layers -// if (layer->flags & 1) -// { -// u8 opacity = (cel->opacity / 255.0f) * (layer->opacity / 255.0f) * 255.0f; - -// i32 cel_width = cel->width; -// i32 cel_height = cel->height; - -// i32 cel_left = 0; -// i32 cel_right = cel_width; -// i32 cel_top = 0; -// i32 cel_bottom = cel_height; - -// i32 frame_left = cel->x_pos; -// i32 frame_top = cel->y_pos; - -// i32 image_left = frame_left + ((cel->frame_index % frames_x) * frame_width); -// i32 image_top = frame_top + ((cel->frame_index / frames_x) * frame_height); - -// // Adjust bounds to ensure pixels outside of frame boundaries -// // aren't processed (aseprite keeps chunks outside of frame -// // around in project file). -// { -// i32 frame_right = cel_width + frame_left; -// i32 frame_bottom = frame_top + cel_height; -// if (frame_left < 0) -// { -// cel_left += -frame_left; -// frame_left = 0; -// } -// if (frame_top < 0) -// { -// cel_top += -frame_top; -// frame_top = 0; -// } -// if (frame_right > (i32)frame_width) -// { -// cel_right -= (frame_right - frame_width); -// frame_right = frame_width; -// } -// if (frame_bottom > (i32)frame_height) -// { -// cel_bottom -= (frame_bottom - frame_height); -// frame_bottom = frame_height; -// } -// } - -// for (i32 cel_y = cel_top; cel_y < cel_bottom; ++cel_y) -// { -// i32 image_y = image_top + cel_y; -// i32 cel_stride = cel_y * cel_width; -// i32 image_stride = image_y * image_width; -// for (i32 cel_x = cel_left; cel_x < cel_right; ++cel_x) -// { -// i32 image_x = image_left + cel_x; -// u32 cel_pixel = cel->pixels[cel_x + cel_stride]; -// u32 *image_pixel = &result.pixels[image_x + image_stride]; -// *image_pixel = ASE_Blend(cel_pixel, *image_pixel, opacity); -// } -// } -// } -// } -// } - -// // Sanity check to ensure all data was read -// Assert(BB_NumBytesRemaining(&bbr) == 0); - -// abort: - -// if (result.errors.count <= 0) -// { -// result.ok = 1; -// } - -// EndScratch(scratch); -// return result; -// } - -// //////////////////////////////////////////////////////////// -// //~ Decode sheet - -// ASE_DecodedSheet ASE_DecodeSheet(Arena *arena, String encoded) -// { -// ASE_DecodedSheet result = Zi; - -// BB_Buff bb = BB_BuffFromString(encoded); -// BB_Reader bbr = BB_ReaderFromBuffNoDebug(&bb); -// ASE_Header ase_header; -// BB_ReadBytes(&bbr, StringFromStruct(&ase_header)); - -// if (ase_header.magic != 0xA5E0) -// { -// ASE_PushError(arena, &result.errors, Lit("Not a valid aseprite file")); -// goto abort; -// } - -// u64 frame_width = ase_header.width; -// u64 frame_height = ase_header.height; - -// u32 frames_x = ase_header.frames; -// u32 frames_y = 1; -// u64 image_width = frame_width * frames_x; -// u64 image_height = frame_height * frames_y; -// ASE_MakeDimensionsSquareish(&ase_header, &frames_x, &frames_y, &image_width, &image_height); - -// u32 frames_count = 0; -// ASE_Frame *first_frame = 0; - -// u32 spans_count = 0; -// ASE_Span *first_span = 0; - -// u32 slice_keys_count = 0; -// ASE_SliceKey *first_slice_key = 0; - -// ////////////////////////////// -// //- Iterate frames - -// for (u16 i = 0; i < ase_header.frames; ++i) -// { -// ASE_FrameHeader frame_header; -// BB_ReadBytes(&bbr, StringFromStruct(&frame_header)); - -// u32 chunks_count = frame_header.chunks_new; -// if (chunks_count == 0) -// { -// Assert(frame_header.chunks_old != 0xFFFF); -// chunks_count = frame_header.chunks_old; -// } - -// ASE_Frame *frame = PushStruct(arena, ASE_Frame); -// frame->next = first_frame; -// first_frame = frame; - -// frame->index = i; -// frame->duration = frame_header.frame_duration_ms / 1000.0; - -// u32 frame_tile_x = i % frames_x; -// u32 frame_tile_y = i / frames_x; - -// Vec2I32 frame_p0 = VEC2I32(frame_tile_x * frame_width, frame_tile_y * frame_height); -// Vec2I32 frame_p1 = AddVec2I32(frame_p0, VEC2I32(frame_width, frame_height)); -// frame->rect.p0 = frame_p0; -// frame->rect.p1 = frame_p1; - -// frame->index = i; -// frame->duration = frame_header.frame_duration_ms / 1000.0; - -// ////////////////////////////// -// //- Iterate chunks - -// for (u32 j = 0; j < chunks_count; ++j) -// { -// u32 chunk_size = BB_ReadUBits(&bbr, 32); -// ASE_ChunkKind chunk_type = BB_ReadUBits(&bbr, 16); - -// // Chunk size includes size & type -// Assert(chunk_size >= 6); -// chunk_size -= 6; - -// u64 chunk_end_pos = BB_GetCurrentReaderByte(&bbr) + chunk_size; - -// switch (chunk_type) -// { -// default: -// { -// BB_ReadSeekToByte(&bbr, chunk_end_pos); -// } break; - -// //- Decode tags -// case ASE_ChunkKind_Tags: -// { -// u16 frame_span_count = BB_ReadUBits(&bbr, 16); -// BB_ReadSeekBytes(&bbr, 8); - -// for (u16 k = 0; k < frame_span_count; ++k) -// { -// ASE_Span *span = PushStruct(arena, ASE_Span); -// span->next = first_span; -// first_span = span; - -// span->start = BB_ReadUBits(&bbr, 16); -// span->end = BB_ReadUBits(&bbr, 16); -// BB_ReadSeekBytes(&bbr, 13); - -// u16 str_len = BB_ReadUBits(&bbr, 16); -// span->name = (String) { str_len, PushStructsNoZero(arena, u8, str_len) }; -// BB_ReadBytes(&bbr, span->name); - -// ++spans_count; -// } - -// } break; - -// ////////////////////////////// -// //- Decode slice - -// case ASE_ChunkKind_Slice: -// { -// ASE_SliceKey *slice_key = PushStruct(arena, ASE_SliceKey); -// slice_key->next = first_slice_key; -// first_slice_key = slice_key; - -// u32 slices_count = BB_ReadUBits(&bbr, 32); -// slice_key->slices_count = slices_count; - -// u32 flags = BB_ReadUBits(&bbr, 32); -// BB_ReadSeekBytes(&bbr, 4); - -// u16 str_len = BB_ReadUBits(&bbr, 16); -// slice_key->name = (String) { str_len, PushStructsNoZero(arena, u8, str_len) }; -// BB_ReadBytes(&bbr, slice_key->name); - -// for (u32 k = 0; k < slices_count; ++k) -// { -// ASE_Slice *slice = PushStruct(arena, ASE_Slice); -// slice->next = slice_key->first_slice; -// slice_key->first_slice = slice; - -// u32 start = BB_ReadUBits(&bbr, 32); -// i32 x = BB_ReadIBits(&bbr, 32); -// i32 y = BB_ReadIBits(&bbr, 32); -// u32 width = BB_ReadUBits(&bbr, 32); -// u32 height = BB_ReadUBits(&bbr, 32); -// if (flags & 0x01) -// { -// // Skip 9-patches info -// BB_ReadSeekBytes(&bbr, 128); -// } -// if (flags & 0x02) -// { -// // Skip pivot info -// BB_ReadSeekBytes(&bbr, 64); -// } - -// slice->start = start; -// slice->rect.p0 = VEC2I32(x, y); -// slice->rect.p1 = VEC2I32(x + width, y + width); -// } - -// ++slice_keys_count; -// } break; - -// // TODO -// //case ASE_ChunkKind_Userdata -// } -// } -// ++frames_count; -// } - -// // Assert all data was read -// Assert(BB_NumBytesRemaining(&bbr) == 0); - -// result.image_size = VEC2(image_width, image_height); -// result.frame_size = VEC2(frame_width, frame_height); -// result.frames_count = frames_count; -// result.spans_count = spans_count; -// result.slice_keys_count = slice_keys_count; -// result.first_frame = first_frame; -// result.first_span = first_span; -// result.first_slice_key = first_slice_key; - -// abort: - -// if (result.errors.count <= 0) -// { -// result.ok = 1; -// } - -// return result; -// } diff --git a/src/ase/ase.h b/src/ase/ase.h index 01fcb372..41e6b2e6 100644 --- a/src/ase/ase.h +++ b/src/ase/ase.h @@ -1,4 +1,132 @@ -// DEFLATE decoder based on Handmade Hero's png parser +//////////////////////////////////////////////////////////// +//~ Aseprite decode types + +// https://github.com/aseprite/aseprite/blob/main/docs/ase-file-specs.md + +Packed(Struct(ASE_Header) +{ + u32 file_size; + u16 magic; + u16 frames_count; + u16 width; + u16 height; + u16 color_depth; + u32 flags; + u16 speed; + u8 _0[8]; + u8 palette_entry; + u8 _1[3]; + u16 colors_count; + u8 pixel_width; + u8 pixel_height; + i16 grid_x; + i16 grid_y; + u16 grid_width; + u16 grid_height; + u8 _2[84]; +}); + +Packed(Struct(ASE_FrameHeader) +{ + u32 bytes_count; + u16 magic; + u16 chunks_count0; + u16 frame_duration_ms; + u8 _[2]; + u32 chunks_count1; +}); + +Enum(ASE_EncodedChunkKind) +{ + ASE_EncodedChunkKind_OldPalette1 = 0x0004, + ASE_EncodedChunkKind_OldPalette2 = 0x0011, + ASE_EncodedChunkKind_Layer = 0x2004, + ASE_EncodedChunkKind_Cel = 0x2005, + ASE_EncodedChunkKind_CelExtra = 0x2006, + ASE_EncodedChunkKind_ColorProfile = 0x2007, + ASE_EncodedChunkKind_ExternalFiles = 0x2008, + ASE_EncodedChunkKind_Mask = 0x2016, + ASE_EncodedChunkKind_Path = 0x2017, + ASE_EncodedChunkKind_Tags = 0x2018, + ASE_EncodedChunkKind_Palette = 0x2019, + ASE_EncodedChunkKind_Userdata = 0x2020, + ASE_EncodedChunkKind_Slice = 0x2022, + ASE_EncodedChunkKind_Tileset = 0x2023 +}; + +Enum(ASE_ChunkKind) +{ + ASE_ChunkKind_Layer, + ASE_ChunkKind_Cel, + ASE_ChunkKind_Tag, +}; + +Enum(ASE_LayerKind) +{ + ASE_LayerKind_Normal, + ASE_LayerKind_Group, + ASE_LayerKind_Tilemap, +}; + +Enum(ASE_LayerFlag) +{ + ASE_LayerFlag_None = 0, + ASE_LayerFlag_Visible = (1 << 0), + ASE_LayerFlag_Editable = (1 << 1), + ASE_LayerFlag_LockMovement = (1 << 2), + ASE_LayerFlag_Background = (1 << 3), + ASE_LayerFlag_PreferLinkedCels = (1 << 4), + ASE_LayerFlag_Collapsed = (1 << 5), + ASE_LayerFlag_ReferenceLayer = (1 << 6), +}; + +Enum(ASE_CelKind) +{ + ASE_CelKind_Raw = 0, + ASE_CelKind_Linked = 1, + ASE_CelKind_CompressedImage = 2, + ASE_CelKind_CompressedTilemap = 3, +}; + +Struct(ASE_Chunk) +{ + ASE_Chunk *next; + + ASE_ChunkKind kind; + + //- Common + f32 opacity; + String name; + i64 chunk_layer_idx; + + //- Layer + struct + { + ASE_LayerKind kind; + ASE_LayerFlag flags; + + ASE_Chunk **cel_chunks; + struct ASE_Layer *final; + } layer; + + //- Cel + struct + { + ASE_CelKind kind; + i64 frame_idx; + i64 linked_frame_idx; + + Rng2 bounds; + String encoded; + } cel; + + //- Tag + struct + { + i64 from; + i64 to; + } tag; +}; //////////////////////////////////////////////////////////// //~ Inflate types @@ -138,21 +266,23 @@ Struct(ASE_Span) ASE_Span *prev; String name; - i64 start; - i64 end; + i64 from; + i64 to; }; Struct(ASE_Meta) { i64 frames_count; + i64 layers_count; + ASE_Layer *first_layer; + ASE_Layer *last_layer; + i64 spans_count; ASE_Span *first_span; ASE_Span *last_span; - i64 layers_count; - ASE_Layer *first_layer; - ASE_Layer *last_layer; + b32 ok; }; //////////////////////////////////////////////////////////// @@ -184,7 +314,7 @@ u16 ASE_DecodeHuffDict(ASE_HuffDict *huffman, ASE_Bitbuff *bb); void ASE_Inflate(u8 *dst, u8 *encoded); //////////////////////////////////////////////////////////// -//~ Meta +//~ Decode ASE_Meta ASE_DecodeMeta(Arena *arena, String encoded); @@ -199,286 +329,3 @@ ASE_Image ASE_DecompressImageFromCel(Arena *arena, ASE_Cel *cel); u32 ASE_BlendPixel(u32 src, u32 dst, u8 opacity); void ASE_BlendImage(ASE_Image src_img, ASE_Image dst_img); - - - - - - - - - - -// //////////////////////////////////////////////////////////// -// //~ Query - -// u32 ASE_FirstPixelFromCel(ASE_Cel *cel); - -// //////////////////////////////////////////////////////////// -// //~ Rasterize - -// void ASE_RasterizeCel(ASE_Cel *cel, u32 *dst_pixels, Vec2 dst_dims); - - - - - - - - - - - - - - - - - - - - - - - -// //////////////////////////////////////////////////////////// -// //~ Sheet types - -// Struct(ASE_Slice) -// { -// u32 start; -// Rng2I32 rect; -// ASE_Slice *next; -// }; - -// Struct(ASE_Span) -// { -// String name; -// u32 start; -// u32 end; -// ASE_Span *next; -// }; - -// Struct(ASE_Frame) -// { -// u32 index; -// Rng2I32 rect; -// f64 duration; -// ASE_Frame *next; -// }; - -// Struct(ASE_SliceKey) -// { -// String name; -// u32 slices_count; -// ASE_Slice *first_slice; -// ASE_SliceKey *next; -// }; - -// //////////////////////////////////////////////////////////// -// //~ Decoder result types - -// Struct(ASE_Error) -// { -// String msg; -// ASE_Error *next; -// }; - -// Struct(ASE_ErrorList) -// { -// u64 count; -// ASE_Error *first; -// ASE_Error *last; -// }; - -// Struct(ASE_DecodedImage) -// { -// u32 width; -// u32 height; -// u32 *pixels; // Array of [width * height] pixels -// ASE_ErrorList errors; -// b32 ok; -// }; - -// Struct(ASE_DecodedSheet) -// { -// Vec2 image_size; -// Vec2 frame_size; -// u32 frames_count; -// u32 spans_count; -// u32 slice_keys_count; -// ASE_Frame *first_frame; -// ASE_Span *first_span; -// ASE_SliceKey *first_slice_key; -// ASE_ErrorList errors; -// b32 ok; -// }; - -// //////////////////////////////////////////////////////////// -// //~ Inflator types - -// #define ASE_HuffBitCount 16 - -// Struct(ASE_Bitbuff) -// { -// u8 *data; -// u64 cur_bit; -// }; - -// Enum(ASE_BlockType) -// { -// ASE_BlockType_Uncompressed = 0, -// ASE_BlockType_CompressedFixed = 1, -// ASE_BlockType_CompressedDynamic = 2, -// ASE_BlockType_Reserved = 3 -// }; - -// Struct(ASE_HuffEntry) -// { -// u16 symbol; -// u16 bits_used; -// }; - -// Struct(ASE_HuffDict) -// { -// u32 max_code_bits; -// u32 entries_count; -// ASE_HuffEntry *entries; -// }; - -// //////////////////////////////////////////////////////////// -// //~ Header types - -// Packed(Struct(ASE_Header) -// { -// u32 file_size; -// u16 magic; -// u16 frames; -// u16 width; -// u16 height; -// u16 color_depth; -// u32 flags; -// u16 speed; -// u32 _1; -// u32 _2; -// u8 palette_entry; -// u8 _3[3]; -// u16 colors_count; -// u8 pixel_width; -// u8 pixel_height; -// i16 grid_x; -// i16 grid_y; -// u16 grid_width; -// u16 grid_height; -// u8 _4[84]; -// }); - -// Packed(Struct(ASE_FrameHeader) -// { -// u32 bytes; -// u16 magic; -// u16 chunks_old; -// u16 frame_duration_ms; -// u8 _[2]; -// u32 chunks_new; -// }); - -// //////////////////////////////////////////////////////////// -// //~ Image decoder types - -// Enum(ASE_ChunkKind) -// { -// ASE_ChunkKind_OldPalette1 = 0x0004, -// ASE_ChunkKind_OldPalette2 = 0x0011, -// ASE_ChunkKind_Layer = 0x2004, -// ASE_ChunkKind_Cel = 0x2005, -// ASE_ChunkKind_CelExtra = 0x2006, -// ASE_ChunkKind_ColorProfile = 0x2007, -// ASE_ChunkKind_ExternalFiles = 0x2008, -// ASE_ChunkKind_Mask = 0x2016, -// ASE_ChunkKind_Path = 0x2017, -// ASE_ChunkKind_Tags = 0x2018, -// ASE_ChunkKind_Palette = 0x2019, -// ASE_ChunkKind_Userdata = 0x2020, -// ASE_ChunkKind_Slice = 0x2022, -// ASE_ChunkKind_Tileset = 0x2023 -// }; - -// Enum(ASE_CelKind) -// { -// ASE_CelKind_RawImage = 0, -// ASE_CelKind_Linked = 1, -// ASE_CelKind_CompressedImage = 2, -// ASE_CelKind_CompressedTilemap = 3 -// }; - -// Struct(ASE_Layer) -// { -// u16 flags; -// u16 type; -// u16 child_level; -// u16 blend_mode; -// u8 opacity; -// String name; -// u32 tileset_index; - -// u32 index; -// ASE_Layer *next; -// }; - -// Struct(ASE_Cel) -// { -// u16 layer_index; -// i16 x_pos; -// i16 y_pos; -// u8 opacity; -// ASE_CelKind type; -// i16 z_index; - -// // Linked cel -// u16 frame_pos; - -// // Compressed image -// u32 width; -// u32 height; -// u32 *pixels; - -// u16 frame_index; -// ASE_Cel *next; -// }; - -// //////////////////////////////////////////////////////////// -// //~ Ase bitbuff helpers - -// u32 ASE_PeekBits(ASE_Bitbuff *bb, u32 nbits); -// u32 ASE_ConsumeBits(ASE_Bitbuff *bb, u32 nbits); -// void ASE_SkipBits(ASE_Bitbuff *bb, u32 nbits); - -// //////////////////////////////////////////////////////////// -// //~ Inflate - -// u32 ASE_ReverseBits(u32 v, u32 bit_count); -// ASE_HuffDict ASE_InitHuffDict(Arena *arena, u32 max_code_bits, u32 *bl_counts, u32 bl_counts_count); -// u16 ASE_DecodeHuffDict(ASE_HuffDict *huffman, ASE_Bitbuff *bb); -// void ASE_Inflate(u8 *dst, u8 *encoded); - -// //////////////////////////////////////////////////////////// -// //~ Error helpers - -// void ASE_PushError(Arena *arena, ASE_ErrorList *list, String msg_src); - -// //////////////////////////////////////////////////////////// -// //~ Decode helpers - -// u32 ASE_Blend(u32 src, u32 dst, u8 opacity); -// void ASE_MakeDimensionsSquareish(ASE_Header *header, u32 *frames_x, u32 *frames_y, u64 *image_width, u64 *image_height); - -// //////////////////////////////////////////////////////////// -// //~ Decode image - -// ASE_DecodedImage ASE_DecodeImage(Arena *arena, String encoded); - -// //////////////////////////////////////////////////////////// -// //~ Decode sheet - -// ASE_DecodedSheet ASE_DecodeSheet(Arena *arena, String encoded); diff --git a/src/meta/meta.c b/src/meta/meta.c index 80336c2c..0d769e74 100644 --- a/src/meta/meta.c +++ b/src/meta/meta.c @@ -372,7 +372,7 @@ void BuildEntryPoint(WaveLaneCtx *lane) PushStringToList(perm, &cp.warnings_msvc, Lit("-W4")); PushStringToList(perm, &cp.warnings_msvc, Lit("-WX")); // PushStringToList(perm, &cp.warnings_msvc, Lit("-we4013")); // function undefined; assuming extern returning int - PushStringToList(perm, &cp.warnings_msvc, Lit("-we4668")); // 'X' is not defined as a preprocessor macro, replacing with '0' for '#if/#elif' + PushStringToList(perm, &cp.warnings_msvc, Lit("-we4668")); // X is not defined as a preprocessor macro, replacing with '0' for '#if/#elif' // Disable warnings PushStringToList(perm, &cp.warnings_msvc, Lit("-wd4244")); // 'function': conversion from 'int' to 'f32', possible loss of data diff --git a/src/pp/pp_vis/pp_vis_core.c b/src/pp/pp_vis/pp_vis_core.c index fb6f2c6a..aa28ba89 100644 --- a/src/pp/pp_vis/pp_vis_core.c +++ b/src/pp/pp_vis/pp_vis_core.c @@ -4265,6 +4265,7 @@ void V_TickForever(WaveLaneCtx *lane) } V_TileDesc tile_desc = Zi; { + tile_desc.tex_slice_uv = tile_slice.tex_rect_uv; tile_desc.tex = tile_slice.tex; } params.tile_descs[tile_kind] = tile_desc; diff --git a/src/pp/pp_vis/pp_vis_gpu.g b/src/pp/pp_vis/pp_vis_gpu.g index 14ad4eee..b0e55b6b 100644 --- a/src/pp/pp_vis/pp_vis_gpu.g +++ b/src/pp/pp_vis/pp_vis_gpu.g @@ -493,7 +493,7 @@ ComputeShader2D(V_ShadeCS, 8, 8) Texture2D tiles = G_Dereference(params.tiles); RWTexture2D stains = G_Dereference(params.stains); RWTexture2D drynesses = G_Dereference(params.drynesses); - SamplerState wrap_sampler = G_Dereference(params.pt_wrap_sampler); + SamplerState clamp_sampler = G_Dereference(params.pt_clamp_sampler); Vec2 shade_pos = SV_DispatchThreadID + Vec2(0.5, 0.5); Vec2 world_pos = mul(params.af.shade_to_world, Vec3(shade_pos, 1)); @@ -548,7 +548,8 @@ ComputeShader2D(V_ShadeCS, 8, 8) { V_TileDesc tile_desc = params.tile_descs[tile]; Texture2D tile_tex = G_Dereference(tile_desc.tex); - tile_color = tile_tex.SampleLevel(wrap_sampler, world_pos, 0); + Vec2 tile_samp_uv = lerp(tile_desc.tex_slice_uv.p0, tile_desc.tex_slice_uv.p1, frac(world_pos)); + tile_color = tile_tex.SampleLevel(clamp_sampler, tile_samp_uv, 0); } // Checkered grid else if (tile == P_TileKind_Empty) diff --git a/src/pp/pp_vis/pp_vis_shared.cgh b/src/pp/pp_vis/pp_vis_shared.cgh index f5d53ee1..84539167 100644 --- a/src/pp/pp_vis/pp_vis_shared.cgh +++ b/src/pp/pp_vis/pp_vis_shared.cgh @@ -48,6 +48,7 @@ Struct(V_Affines) Struct(V_TileDesc) { + Rng2 tex_slice_uv; G_Texture2DRef tex; }; diff --git a/src/sprite/sprite.c b/src/sprite/sprite.c index 1f2fd639..d1dbd433 100644 --- a/src/sprite/sprite.c +++ b/src/sprite/sprite.c @@ -108,6 +108,7 @@ SPR_Slice SPR_SliceFromSheet(SPR_SheetKey sheet_key, SPR_SpanKey span_key, i64 f { SPR_SliceEntry *slice = &sheet->slices[slice_idx]; slice->bounds = Rng2Empty; + Atomic64Set(&slice->atlas_copy_completion_target, I64Max); for (SPR_RayKind ray_kind = 0; ray_kind < SPR_RayKind_COUNT; ++ray_kind) { slice->rays[ray_kind] = XformIdentity; @@ -204,33 +205,46 @@ SPR_Slice SPR_SliceFromSheet(SPR_SheetKey sheet_key, SPR_SpanKey span_key, i64 f } //- Init spans - sheet->span_bins_count = 256; - sheet->span_bins = PushStructs(perm, SPR_SpanBin, sheet->span_bins_count); - for (ASE_Span *ase_span = sheet->meta.first_span; ase_span; ase_span = ase_span->next) { - SPR_SpanKey new_span_key = { .v = HashString(ase_span->name) }; - SPR_SpanBin *span_bin = &sheet->span_bins[new_span_key.v % sheet->span_bins_count]; - SPR_SpanEntry *span = 0; - for (span = span_bin->first; span; span = span->next_in_bin) + sheet->span_bins_count = MaxI64(1, NextPow2U64(sheet->meta.spans_count * 4)); + sheet->span_bins = PushStructs(perm, SPR_SpanBin, sheet->span_bins_count); + for (ASE_Span *ase_span = sheet->meta.first_span; ase_span; ase_span = ase_span->next) { - if (span->key.v == new_span_key.v) + SPR_SpanKey new_span_key = { .v = HashString(ase_span->name) }; + SPR_SpanBin *span_bin = &sheet->span_bins[new_span_key.v % sheet->span_bins_count]; + SPR_SpanEntry *span = 0; + for (span = span_bin->first; span; span = span->next_in_bin) { - break; + if (span->key.v == new_span_key.v) + { + break; + } + } + if (!span) + { + span = PushStruct(perm, SPR_SpanEntry); + SllQueuePush(sheet->first_span, sheet->last_span, span); + SllStackPushN(span_bin->first, span, next_in_bin); + span->key = new_span_key; + span->from = ase_span->from; + span->to = ase_span->to; } } - if (!span) + + // Insert nil span { - span = PushStruct(perm, SPR_SpanEntry); + SPR_SpanBin *span_bin = &sheet->span_bins[0]; + SPR_SpanEntry *span = PushStruct(perm, SPR_SpanEntry); SllQueuePush(sheet->first_span, sheet->last_span, span); SllStackPushN(span_bin->first, span, next_in_bin); - span->key = new_span_key; - span->start = ase_span->start; - span->end = ase_span->end; + span->key = SPR_NilSpanKey; + span->from = 0; + span->to = sheet->slices_count; } } - // TODO: Proper validation - sheet->ok = 1; + // TODO: More rigorous validation + sheet->ok = sheet->meta.ok; } } Unlock(&sheet_bin_lock); @@ -263,8 +277,8 @@ SPR_Slice SPR_SliceFromSheet(SPR_SheetKey sheet_key, SPR_SpanKey span_key, i64 f } if (span) { - span_start = span->start; - span_end = span->end; + span_start = span->from; + span_end = span->to; } } diff --git a/src/sprite/sprite.h b/src/sprite/sprite.h index 98af9323..fbf00e2c 100644 --- a/src/sprite/sprite.h +++ b/src/sprite/sprite.h @@ -86,8 +86,8 @@ Struct(SPR_SpanEntry) SPR_SpanEntry *next_in_bin; SPR_SpanKey key; - i64 start; - i64 end; + i64 from; + i64 to; }; Struct(SPR_SpanBin) diff --git a/src/ui/ui.lay b/src/ui/ui.lay index e7700152..5544521e 100644 --- a/src/ui/ui.lay +++ b/src/ui/ui.lay @@ -29,8 +29,6 @@ @IncludeG ui_gpu.gh -@Bootstrap UI_Bootstrap - ////////////////////////////// //- Impl diff --git a/src/ui/ui_core.c b/src/ui/ui_core.c index 269c446c..1a0876b1 100644 --- a/src/ui/ui_core.c +++ b/src/ui/ui_core.c @@ -1,12 +1,5 @@ UI_Ctx UI = Zi; -//////////////////////////////////////////////////////////// -//~ Bootstrap - -void UI_Bootstrap(void) -{ -} - //////////////////////////////////////////////////////////// //~ Key helpers @@ -537,13 +530,11 @@ void UI_SetRawTexture(UI_Key key, G_Texture2DRef tex, Rng2 uv) UI_BoxReports UI_ReportsFromKey(UI_Key key) { UI_BoxReports result = Zi; - UI_Box *box = UI_BoxFromKey(key); if (box) { result = box->reports; } - return result; } @@ -629,11 +620,11 @@ UI_Frame *UI_BeginFrame(UI_FrameFlag frame_flags) { ControllerEventsArray controller_events = frame->window_frame.controller_events; - // Locate boxes + //- Locate boxes UI_Box *top_hovered_box = 0; UI_Box *active_box = UI_BoxFromKey(prev_frame->active_box); - // Update cursor pos + //- Update cursor pos for (u64 cev_index = 0; cev_index < controller_events.count; ++cev_index) { ControllerEvent cev = controller_events.events[cev_index]; @@ -643,7 +634,7 @@ UI_Frame *UI_BeginFrame(UI_FrameFlag frame_flags) } } - // Init box reports + //- Init box reports for (u64 pre_index = UI.boxes_count; pre_index-- > 0;) { UI_Box *box = prev_frame->boxes_pre[pre_index]; @@ -690,7 +681,7 @@ UI_Frame *UI_BeginFrame(UI_FrameFlag frame_flags) report->m3.presses = 0; } - // Update state from controller events + //- Update state from controller events i32 mouse_downs = 0; for (u64 cev_index = 0; cev_index < controller_events.count; ++cev_index) { @@ -788,7 +779,7 @@ UI_Frame *UI_BeginFrame(UI_FrameFlag frame_flags) hot_box = top_hovered_box; } - // Update box reports + //- Update box reports { f32 lower_target = TweakFloat("UI lower blend target", -0.05, -1, 0); f32 upper_target = TweakFloat("UI upper blend target", 1.05, 1, 10); @@ -804,9 +795,6 @@ UI_Frame *UI_BeginFrame(UI_FrameFlag frame_flags) f32 target_active = box == active_box ? Inf : lower_target; f64 target_misc = box->desc.misc; - - - // TODO: Configurable per-box blend rates f32 exists_blend_rate = (30 * frame->dt); f32 hot_blend_rate = (15 * frame->dt); @@ -896,7 +884,6 @@ GC_Run UI_ScaleRun(Arena *arena, GC_Run unscaled_run, Vec2 scale) { GC_RunRect *src = &unscaled_run.rects[rect_idx]; GC_RunRect *dst = &result.rects[rect_idx]; - *dst = *src; dst->bounds = MulRng2Vec2(dst->bounds, scale); dst->advance *= scale.x; @@ -923,121 +910,116 @@ void UI_EndFrame(UI_Frame *frame, i32 vsync) Rng2 draw_scissor = RNG2(VEC2(0, 0), VEC2(draw_size.x, draw_size.y)); ////////////////////////////// - //- Process commands + //- Create boxes from build cmds + for (UI_CmdNode *cmd_node = frame->first_cmd_node; cmd_node; cmd_node = cmd_node->next) { - ////////////////////////////// - //- Create boxes from build cmds - - for (UI_CmdNode *cmd_node = frame->first_cmd_node; cmd_node; cmd_node = cmd_node->next) + UI_Cmd cmd = cmd_node->cmd; + if (cmd.kind == UI_CmdKind_BuildBox) { - UI_Cmd cmd = cmd_node->cmd; - if (cmd.kind == UI_CmdKind_BuildBox) + UI_Key key = cmd.box.key; + UI_Box *box = 0; { - UI_Key key = cmd.box.key; - UI_Box *box = 0; + UI_BoxBin *bin = &UI.box_bins[key.v % countof(UI.box_bins)]; + for (box = bin->first; box; box = box->next_in_bin) { - UI_BoxBin *bin = &UI.box_bins[key.v % countof(UI.box_bins)]; - for (box = bin->first; box; box = box->next_in_bin) + if (UI_MatchKey(box->key, key)) { - if (UI_MatchKey(box->key, key)) - { - break; - } + break; } + } + // Allocate new box + if (box == 0) + { // Allocate new box - if (box == 0) + box = UI.first_free_box; + i64 old_gen = 0; + if (box) { - // Allocate new box - box = UI.first_free_box; - i64 old_gen = 0; - if (box) - { - old_gen = box->gen; - SllStackPop(UI.first_free_box); - ZeroStruct(box); - } - else - { - box = PushStruct(UI.box_arena, UI_Box); - } - box->key = key; - box->old_gen = old_gen; - box->gen = old_gen + 1; - DllQueuePushNP(bin->first, bin->last, box, next_in_bin, prev_in_bin); - ++UI.boxes_count; + old_gen = box->gen; + SllStackPop(UI.first_free_box); + ZeroStruct(box); } + else + { + box = PushStruct(UI.box_arena, UI_Box); + } + box->key = key; + box->old_gen = old_gen; + box->gen = old_gen + 1; + DllQueuePushNP(bin->first, bin->last, box, next_in_bin, prev_in_bin); + ++UI.boxes_count; } } } + } - ////////////////////////////// - //- Update boxes from cmds + ////////////////////////////// + //- Update boxes from cmds - for (UI_CmdNode *cmd_node = frame->first_cmd_node; cmd_node; cmd_node = cmd_node->next) + for (UI_CmdNode *cmd_node = frame->first_cmd_node; cmd_node; cmd_node = cmd_node->next) + { + UI_Cmd cmd = cmd_node->cmd; + switch (cmd.kind) { - UI_Cmd cmd = cmd_node->cmd; - switch (cmd.kind) + case UI_CmdKind_BuildBox: { - case UI_CmdKind_BuildBox: + UI_Key key = cmd.box.key; + if (UI_MatchKey(key, UI_NilKey)) { - UI_Key key = cmd.box.key; - if (UI_MatchKey(key, UI_NilKey)) - { - key = UI_RandKey(); - } - UI_Box *box = UI_BoxFromKey(key); + key = UI_RandKey(); + } + UI_Box *box = UI_BoxFromKey(key); - UI_Box *parent = 0; - if (box != UI.root_box) - { - parent = UI_BoxFromKey(cmd.box.parent); - } - - // Update parent - if (box->parent) - { - // Remove from old parent - DllQueueRemove(box->parent->first, box->parent->last, box); - --box->parent->count; - } - if (parent) - { - // Add to new parent - DllQueuePush(parent->first, parent->last, box); - ++parent->count; - } - box->parent = parent; - - // Update box from cmd - { - box->desc = cmd.box; - String32 codepoints = Zi; - if (box->desc.icon != UI_Icon_None) - { - codepoints.len = 1; - codepoints.text = (u32 *)&box->desc.icon; - } - else - { - codepoints = String32FromString(scratch.arena, box->desc.text); - } - box->glyph_run = GC_RunFromString32(frame->arena, codepoints, box->desc.font, box->desc.font_size); - } - box->last_build_tick = frame->tick; - } break; - - case UI_CmdKind_SetRawTexture: + UI_Box *parent = 0; + if (box != UI.root_box) { - UI_Key key = cmd.set_raw_texture.key; - UI_Box *box = UI_BoxFromKey(key); - if (box) + parent = UI_BoxFromKey(cmd.box.parent); + } + + // Update parent + if (box->parent) + { + // Remove from old parent + DllQueueRemove(box->parent->first, box->parent->last, box); + --box->parent->count; + } + if (parent) + { + // Add to new parent + DllQueuePush(parent->first, parent->last, box); + ++parent->count; + } + box->parent = parent; + + // Update box from cmd + { + box->desc = cmd.box; + String32 codepoints = Zi; + if (box->desc.icon != UI_Icon_None) { - box->raw_texture = cmd.set_raw_texture.tex; - box->raw_texture_slice_uv = cmd.set_raw_texture.slice_uv; + codepoints.len = 1; + codepoints.text = (u32 *)&box->desc.icon; } - } break; - } + else + { + codepoints = String32FromString(scratch.arena, box->desc.text); + } + box->glyph_run = GC_RunFromString32(frame->arena, codepoints, box->desc.font, box->desc.font_size); + } + box->last_build_tick = frame->tick; + } break; + + case UI_CmdKind_SetRawTexture: + { + UI_Key key = cmd.set_raw_texture.key; + UI_Box *box = UI_BoxFromKey(key); + if (box) + { + box->raw_texture = cmd.set_raw_texture.tex; + box->raw_texture_slice_uv = cmd.set_raw_texture.slice_uv; + } + } break; } } diff --git a/src/ui/ui_core.h b/src/ui/ui_core.h index f0388c6b..3f8a35b0 100644 --- a/src/ui/ui_core.h +++ b/src/ui/ui_core.h @@ -420,11 +420,6 @@ Struct(UI_Ctx) extern UI_Ctx UI; -//////////////////////////////////////////////////////////// -//~ Bootstrap - -void UI_Bootstrap(void); - //////////////////////////////////////////////////////////// //~ Key helpers