ref: 5fcf2feec81e753d0188bb10da9db0a5ee061957
dir: /src/wipeout/image.c/
#include "../types.h" #include "../mem.h" #include "../utils.h" #include "object.h" #include "track.h" #include "ship.h" #include "weapon.h" #include "droid.h" #include "camera.h" #include "object.h" #include "scene.h" #include "game.h" #include "hud.h" #include "image.h" #define STB_IMAGE_WRITE_IMPLEMENTATION #include "../libs/stb_image_write.h" #define TIM_TYPE_PALETTED_4_BPP 0x08 #define TIM_TYPE_PALETTED_8_BPP 0x09 #define TIM_TYPE_TRUE_COLOR_16_BPP 0x02 static inline rgba_t tim_16bit_to_rgba(uint16_t c, bool transparent_bit) { return rgba( ((c >> 0) & 0x1f) << 3, ((c >> 5) & 0x1f) << 3, ((c >> 10) & 0x1f) << 3, (c == 0 ? 0x00 : transparent_bit && (c & 0x7fff) == 0 ? 0x00 : 0xff ) ); } image_t *image_alloc(uint32_t width, uint32_t height) { image_t *image = mem_temp_alloc(sizeof(image_t) + width * height * sizeof(rgba_t)); image->width = width; image->height = height; image->pixels = (rgba_t *)(((uint8_t *)image) + sizeof(image_t)); return image; } image_t *image_load_from_bytes(uint8_t *bytes, bool transparent) { uint32_t p = 0; uint32_t magic = get_i32_le(bytes, &p); uint32_t type = get_i32_le(bytes, &p); uint16_t *palette = NULL; if ( type == TIM_TYPE_PALETTED_4_BPP || type == TIM_TYPE_PALETTED_8_BPP ) { uint32_t header_length = get_i32_le(bytes, &p); uint16_t palette_x = get_i16_le(bytes, &p); uint16_t palette_y = get_i16_le(bytes, &p); uint16_t palette_colors = get_i16_le(bytes, &p); uint16_t palettes = get_i16_le(bytes, &p); palette = (uint16_t *)(bytes + p); p += palette_colors * 2; } uint32_t data_size = get_i32_le(bytes, &p); int32_t pixels_per_16bit = 1; if (type == TIM_TYPE_PALETTED_8_BPP) { pixels_per_16bit = 2; } else if (type == TIM_TYPE_PALETTED_4_BPP) { pixels_per_16bit = 4; } uint16_t skip_x = get_i16_le(bytes, &p); uint16_t skip_y = get_i16_le(bytes, &p); uint16_t entries_per_row = get_i16_le(bytes, &p); uint16_t rows = get_i16_le(bytes, &p); int32_t width = entries_per_row * pixels_per_16bit; int32_t height = rows; int32_t entries = entries_per_row * rows; image_t *image = image_alloc(width, height); int32_t pixel_pos = 0; if (type == TIM_TYPE_TRUE_COLOR_16_BPP) { for (int i = 0; i < entries; i++) { image->pixels[pixel_pos++] = tim_16bit_to_rgba(get_i16_le(bytes, &p), transparent); } } else if (type == TIM_TYPE_PALETTED_8_BPP) { for (int i = 0; i < entries; i++) { int32_t palette_pos = get_i16_le(bytes, &p); image->pixels[pixel_pos++] = tim_16bit_to_rgba(palette[(palette_pos >> 0) & 0xff], transparent); image->pixels[pixel_pos++] = tim_16bit_to_rgba(palette[(palette_pos >> 8) & 0xff], transparent); } } else if (type == TIM_TYPE_PALETTED_4_BPP) { for (int i = 0; i < entries; i++) { int32_t palette_pos = get_i16_le(bytes, &p); image->pixels[pixel_pos++] = tim_16bit_to_rgba(palette[(palette_pos >> 0) & 0xf], transparent); image->pixels[pixel_pos++] = tim_16bit_to_rgba(palette[(palette_pos >> 4) & 0xf], transparent); image->pixels[pixel_pos++] = tim_16bit_to_rgba(palette[(palette_pos >> 8) & 0xf], transparent); image->pixels[pixel_pos++] = tim_16bit_to_rgba(palette[(palette_pos >> 12) & 0xf], transparent); } } return image; } #define LZSS_INDEX_BIT_COUNT 13 #define LZSS_LENGTH_BIT_COUNT 4 #define LZSS_WINDOW_SIZE (1 << LZSS_INDEX_BIT_COUNT) #define LZSS_BREAK_EVEN ((1 + LZSS_INDEX_BIT_COUNT + LZSS_LENGTH_BIT_COUNT) / 9) #define LZSS_END_OF_STREAM 0 #define LZSS_MOD_WINDOW(a) ((a) & (LZSS_WINDOW_SIZE - 1)) void lzss_decompress(uint8_t *in_data, uint8_t *out_data) { int16_t i; int16_t current_position; uint8_t cc; int16_t match_length; int16_t match_position; uint32_t mask; uint32_t return_value; uint8_t in_bfile_mask; int16_t in_bfile_rack; int16_t value; uint8_t window[LZSS_WINDOW_SIZE]; in_bfile_rack = 0; in_bfile_mask = 0x80; current_position = 1; while (true) { if (in_bfile_mask == 0x80) { in_bfile_rack = (int16_t) * in_data++; } value = in_bfile_rack & in_bfile_mask; in_bfile_mask >>= 1; if (in_bfile_mask == 0) { in_bfile_mask = 0x80; } if (value) { mask = 1L << (8 - 1); return_value = 0; while (mask != 0) { if (in_bfile_mask == 0x80) { in_bfile_rack = (int16_t) * in_data++; } if (in_bfile_rack & in_bfile_mask) { return_value |= mask; } mask >>= 1; in_bfile_mask >>= 1; if (in_bfile_mask == 0) { in_bfile_mask = 0x80; } } cc = (uint8_t) return_value; *out_data++ = cc; window[ current_position ] = cc; current_position = LZSS_MOD_WINDOW(current_position + 1); } else { mask = 1L << (LZSS_INDEX_BIT_COUNT - 1); return_value = 0; while (mask != 0) { if (in_bfile_mask == 0x80) { in_bfile_rack = (int16_t) * in_data++; } if (in_bfile_rack & in_bfile_mask) { return_value |= mask; } mask >>= 1; in_bfile_mask >>= 1; if (in_bfile_mask == 0) { in_bfile_mask = 0x80; } } match_position = (int16_t) return_value; if (match_position == LZSS_END_OF_STREAM) { break; } mask = 1L << (LZSS_LENGTH_BIT_COUNT - 1); return_value = 0; while (mask != 0) { if (in_bfile_mask == 0x80) { in_bfile_rack = (int16_t) * in_data++; } if (in_bfile_rack & in_bfile_mask) { return_value |= mask; } mask >>= 1; in_bfile_mask >>= 1; if (in_bfile_mask == 0) { in_bfile_mask = 0x80; } } match_length = (int16_t) return_value; match_length += LZSS_BREAK_EVEN; for (i = 0 ; i <= match_length ; i++) { cc = window[LZSS_MOD_WINDOW(match_position + i)]; *out_data++ = cc; window[current_position] = cc; current_position = LZSS_MOD_WINDOW(current_position + 1); } } } } cmp_t *image_load_compressed(char *name) { printf("load cmp %s\n", name); uint32_t compressed_size; uint8_t *compressed_bytes = file_load(name, &compressed_size); uint32_t p = 0; int32_t decompressed_size = 0; int32_t image_count = get_i32_le(compressed_bytes, &p); // Calculate the total uncompressed size for (int i = 0; i < image_count; i++) { decompressed_size += get_i32_le(compressed_bytes, &p); } uint32_t struct_size = sizeof(cmp_t) + sizeof(uint8_t *) * image_count; cmp_t *cmp = mem_temp_alloc(struct_size + decompressed_size); cmp->len = image_count; uint8_t *decompressed_bytes = ((uint8_t *)cmp) + struct_size; // Rewind and load all offsets p = 4; uint32_t offset = 0; for (int i = 0; i < image_count; i++) { cmp->entries[i] = decompressed_bytes + offset; offset += get_i32_le(compressed_bytes, &p); } lzss_decompress(compressed_bytes + p, decompressed_bytes); mem_temp_free(compressed_bytes); return cmp; } uint16_t image_get_texture(char *name) { printf("load: %s\n", name); uint32_t size; uint8_t *bytes = file_load(name, &size); image_t *image = image_load_from_bytes(bytes, false); uint32_t texture_index = render_texture_create(image->width, image->height, image->pixels); mem_temp_free(image); mem_temp_free(bytes); return texture_index; } uint16_t image_get_texture_semi_trans(char *name) { printf("load: %s\n", name); uint32_t size; uint8_t *bytes = file_load(name, &size); image_t *image = image_load_from_bytes(bytes, true); uint32_t texture_index = render_texture_create(image->width, image->height, image->pixels); mem_temp_free(image); mem_temp_free(bytes); return texture_index; } texture_list_t image_get_compressed_textures(char *name) { cmp_t *cmp = image_load_compressed(name); texture_list_t list = {.start = render_textures_len(), .len = cmp->len}; for (int i = 0; i < cmp->len; i++) { int32_t width, height; image_t *image = image_load_from_bytes(cmp->entries[i], false); // char png_name[1024] = {0}; // sprintf(png_name, "%s.%d.png", name, i); // stbi_write_png(png_name, image->width, image->height, 4, image->pixels, 0); render_texture_create(image->width, image->height, image->pixels); mem_temp_free(image); } mem_temp_free(cmp); return list; } uint16_t texture_from_list(texture_list_t tl, uint16_t index) { error_if(index >= tl.len, "Texture %d not in list of len %d", index, tl.len); return tl.start + index; } void image_copy(image_t *src, image_t *dst, uint32_t sx, uint32_t sy, uint32_t sw, uint32_t sh, uint32_t dx, uint32_t dy) { rgba_t *src_pixels = src->pixels + sy * src->width + sx; rgba_t *dst_pixels = dst->pixels + dy * dst->width + dx; for (uint32_t y = 0; y < sh; y++) { for (uint32_t x = 0; x < sw; x++) { *(dst_pixels++) = *(src_pixels++); } src_pixels += src->width - sw; dst_pixels += dst->width - sw; } }