better lighting

This commit is contained in:
asrael 2026-01-31 09:31:17 -06:00
parent 805a2713a3
commit 6ed4e17065
75 changed files with 6417 additions and 3667 deletions

View file

@ -7,11 +7,11 @@
#define MINIZ_NO_TIME
#define MINIZ_NO_ARCHIVE_APIS
#define MINIZ_NO_ARCHIVE_WRITING_APIS
#define MINIZ_NO_DEFLATE_APIS
#define MINIZ_NO_ZLIB_COMPATIBLE_NAMES
#include <miniz.h>
#include "pxl8_color.h"
#include "pxl8_io.h"
#include "pxl8_log.h"
#include "pxl8_mem.h"
@ -635,3 +635,302 @@ void pxl8_ase_destroy(pxl8_ase_file* ase_file) {
memset(ase_file, 0, sizeof(pxl8_ase_file));
}
pxl8_result pxl8_ase_load_palette(const char* filepath, u32* colors, u32* count) {
if (!filepath || !colors || !count) {
return PXL8_ERROR_NULL_POINTER;
}
pxl8_ase_file ase;
pxl8_result result = pxl8_ase_load(filepath, &ase);
if (result != PXL8_OK) {
return result;
}
u32 n = ase.palette.entry_count;
if (n > 256) n = 256;
for (u32 i = 0; i < n; i++) {
colors[i] = ase.palette.colors[i];
}
*count = n;
pxl8_ase_destroy(&ase);
return PXL8_OK;
}
pxl8_result pxl8_ase_remap(const char* input_path, const char* output_path, const pxl8_ase_remap_config* config) {
if (!input_path || !output_path || !config) {
return PXL8_ERROR_NULL_POINTER;
}
if (!config->palette || config->palette_count == 0) {
return PXL8_ERROR_INVALID_ARGUMENT;
}
f32 hue_tol = config->hue_tolerance > 0.0f ? config->hue_tolerance : 0.08f;
u8* file_data;
usize file_size;
pxl8_result result = pxl8_io_read_binary_file(input_path, &file_data, &file_size);
if (result != PXL8_OK) {
return result;
}
if (file_size < 128) {
pxl8_io_free_binary_data(file_data);
return PXL8_ERROR_ASE_TRUNCATED_FILE;
}
u8* output_data = (u8*)pxl8_malloc(file_size + 65536);
if (!output_data) {
pxl8_io_free_binary_data(file_data);
return PXL8_ERROR_OUT_OF_MEMORY;
}
memcpy(output_data, file_data, file_size);
pxl8_stream stream = pxl8_stream_create(file_data, (u32)file_size);
pxl8_stream_seek(&stream, 128);
u32 frame_start = 128;
u32 frame_size = pxl8_read_u32(&stream);
pxl8_skip_bytes(&stream, 2);
u16 old_chunks = pxl8_read_u16(&stream);
pxl8_skip_bytes(&stream, 2);
u16 num_chunks = old_chunks;
if (old_chunks == 0xFFFF || old_chunks == 0xFFFE) {
num_chunks = (u16)pxl8_read_u32(&stream);
} else {
pxl8_skip_bytes(&stream, 4);
}
u32 palette_chunk_offset = 0;
u32 palette_entry_start = 0;
u32 orig_colors[256] = {0};
u32 chunk_offset = frame_start + 16;
for (u16 c = 0; c < num_chunks; c++) {
pxl8_stream_seek(&stream, chunk_offset);
u32 chunk_size = pxl8_read_u32(&stream);
u16 chunk_type = pxl8_read_u16(&stream);
if (chunk_type == PXL8_ASE_CHUNK_PALETTE) {
palette_chunk_offset = chunk_offset;
pxl8_skip_bytes(&stream, 4);
u32 first_color = pxl8_read_u32(&stream);
u32 last_color = pxl8_read_u32(&stream);
pxl8_skip_bytes(&stream, 8);
palette_entry_start = pxl8_stream_position(&stream);
for (u32 i = first_color; i <= last_color && i < 256; i++) {
u16 flags = pxl8_read_u16(&stream);
u8 r = pxl8_read_u8(&stream);
u8 g = pxl8_read_u8(&stream);
u8 b = pxl8_read_u8(&stream);
pxl8_skip_bytes(&stream, 1);
orig_colors[i] = r | (g << 8) | (b << 16);
if (flags & 1) {
u16 name_len = pxl8_read_u16(&stream);
pxl8_skip_bytes(&stream, name_len);
}
}
}
chunk_offset += chunk_size;
}
if (palette_entry_start == 0) {
pxl8_free(output_data);
pxl8_io_free_binary_data(file_data);
return PXL8_ERROR_ASE_MALFORMED_CHUNK;
}
u8 remap[256];
bool used[256] = {0};
for (u32 i = 0; i < 256; i++) {
u32 src = orig_colors[i];
f32 src_hue = pxl8_color_hue(src);
f32 src_sat = pxl8_color_saturation(src);
f32 src_lum = pxl8_color_luminance(src);
u32 best_idx = 0;
f32 best_score = 999999.0f;
for (u32 j = 0; j < config->palette_count; j++) {
u32 tgt = config->palette[j];
f32 tgt_hue = pxl8_color_hue(tgt);
f32 tgt_sat = pxl8_color_saturation(tgt);
f32 tgt_lum = pxl8_color_luminance(tgt);
f32 hue_diff = pxl8_color_hue_diff(src_hue, tgt_hue);
f32 lum_diff = src_lum > tgt_lum ? src_lum - tgt_lum : tgt_lum - src_lum;
f32 sat_diff = src_sat > tgt_sat ? src_sat - tgt_sat : tgt_sat - src_sat;
f32 score;
if (src_sat < 0.1f) {
score = lum_diff + sat_diff * 100.0f;
} else if (hue_diff <= hue_tol) {
score = lum_diff + sat_diff * 50.0f;
} else {
score = hue_diff * 1000.0f + lum_diff;
}
if (score < best_score) {
best_score = score;
best_idx = j;
}
}
remap[i] = (u8)best_idx;
used[best_idx] = true;
}
u8 compact[256];
u32 compact_colors[256];
u32 compact_count = 0;
for (u32 i = 0; i < config->palette_count; i++) {
if (used[i]) {
compact[i] = (u8)compact_count;
compact_colors[compact_count] = config->palette[i];
compact_count++;
}
}
for (u32 i = 0; i < 256; i++) {
remap[i] = compact[remap[i]];
}
for (u32 i = 0; i < compact_count; i++) {
u32 offset = palette_entry_start + i * 6;
u32 color = compact_colors[i];
output_data[offset + 0] = 0;
output_data[offset + 1] = 0;
output_data[offset + 2] = color & 0xFF;
output_data[offset + 3] = (color >> 8) & 0xFF;
output_data[offset + 4] = (color >> 16) & 0xFF;
output_data[offset + 5] = 0xFF;
}
u32 new_last_color = compact_count > 0 ? compact_count - 1 : 0;
output_data[palette_chunk_offset + 6] = compact_count & 0xFF;
output_data[palette_chunk_offset + 7] = (compact_count >> 8) & 0xFF;
output_data[palette_chunk_offset + 8] = (compact_count >> 16) & 0xFF;
output_data[palette_chunk_offset + 9] = (compact_count >> 24) & 0xFF;
output_data[palette_chunk_offset + 14] = new_last_color & 0xFF;
output_data[palette_chunk_offset + 15] = (new_last_color >> 8) & 0xFF;
output_data[palette_chunk_offset + 16] = (new_last_color >> 16) & 0xFF;
output_data[palette_chunk_offset + 17] = (new_last_color >> 24) & 0xFF;
chunk_offset = frame_start + 16;
usize output_size = file_size;
for (u16 c = 0; c < num_chunks; c++) {
pxl8_stream_seek(&stream, chunk_offset);
u32 chunk_size = pxl8_read_u32(&stream);
u16 chunk_type = pxl8_read_u16(&stream);
if (chunk_type == PXL8_ASE_CHUNK_CEL) {
pxl8_skip_bytes(&stream, 7);
u16 cel_type = pxl8_read_u16(&stream);
if (cel_type == 2) {
pxl8_skip_bytes(&stream, 7);
u16 width = pxl8_read_u16(&stream);
u16 height = pxl8_read_u16(&stream);
u32 pixels_size = width * height;
u32 compressed_start = pxl8_stream_position(&stream);
u32 compressed_size = chunk_size - (compressed_start - chunk_offset);
u8* pixels = (u8*)pxl8_malloc(pixels_size);
if (!pixels) {
pxl8_free(output_data);
pxl8_io_free_binary_data(file_data);
return PXL8_ERROR_OUT_OF_MEMORY;
}
const u8* compressed_data = file_data + compressed_start;
mz_ulong dest_len = pixels_size;
i32 mz_result = mz_uncompress(pixels, &dest_len, compressed_data, compressed_size);
if (mz_result != MZ_OK) {
pxl8_free(pixels);
pxl8_free(output_data);
pxl8_io_free_binary_data(file_data);
return PXL8_ERROR_ASE_MALFORMED_CHUNK;
}
for (u32 i = 0; i < pixels_size; i++) {
pixels[i] = remap[pixels[i]];
}
mz_ulong new_compressed_size = mz_compressBound(pixels_size);
u8* new_compressed = (u8*)pxl8_malloc(new_compressed_size);
if (!new_compressed) {
pxl8_free(pixels);
pxl8_free(output_data);
pxl8_io_free_binary_data(file_data);
return PXL8_ERROR_OUT_OF_MEMORY;
}
mz_result = mz_compress2(new_compressed, &new_compressed_size, pixels, pixels_size, 6);
pxl8_free(pixels);
if (mz_result != MZ_OK) {
pxl8_free(new_compressed);
pxl8_free(output_data);
pxl8_io_free_binary_data(file_data);
return PXL8_ERROR_ASE_MALFORMED_CHUNK;
}
i32 size_diff = (i32)new_compressed_size - (i32)compressed_size;
u8* new_output = (u8*)pxl8_malloc(output_size + size_diff + 65536);
if (!new_output) {
pxl8_free(new_compressed);
pxl8_free(output_data);
pxl8_io_free_binary_data(file_data);
return PXL8_ERROR_OUT_OF_MEMORY;
}
memcpy(new_output, output_data, compressed_start);
memcpy(new_output + compressed_start, new_compressed, new_compressed_size);
memcpy(new_output + compressed_start + new_compressed_size,
output_data + compressed_start + compressed_size,
output_size - compressed_start - compressed_size);
u32 new_chunk_size = chunk_size + size_diff;
new_output[chunk_offset + 0] = new_chunk_size & 0xFF;
new_output[chunk_offset + 1] = (new_chunk_size >> 8) & 0xFF;
new_output[chunk_offset + 2] = (new_chunk_size >> 16) & 0xFF;
new_output[chunk_offset + 3] = (new_chunk_size >> 24) & 0xFF;
u32 new_frame_size = frame_size + size_diff;
new_output[frame_start + 0] = new_frame_size & 0xFF;
new_output[frame_start + 1] = (new_frame_size >> 8) & 0xFF;
new_output[frame_start + 2] = (new_frame_size >> 16) & 0xFF;
new_output[frame_start + 3] = (new_frame_size >> 24) & 0xFF;
output_size += size_diff;
new_output[0] = output_size & 0xFF;
new_output[1] = (output_size >> 8) & 0xFF;
new_output[2] = (output_size >> 16) & 0xFF;
new_output[3] = (output_size >> 24) & 0xFF;
pxl8_free(new_compressed);
pxl8_free(output_data);
output_data = new_output;
break;
}
}
chunk_offset += chunk_size;
}
pxl8_io_free_binary_data(file_data);
result = pxl8_io_write_binary_file(output_path, output_data, output_size);
pxl8_free(output_data);
if (result == PXL8_OK) {
pxl8_info("Remapped %s -> %s", input_path, output_path);
}
return result;
}

View file

@ -141,12 +141,20 @@ typedef struct pxl8_ase_file {
pxl8_ase_tileset* tilesets;
} pxl8_ase_file;
typedef struct pxl8_ase_remap_config {
const u32* palette;
u32 palette_count;
f32 hue_tolerance;
} pxl8_ase_remap_config;
#ifdef __cplusplus
extern "C" {
#endif
pxl8_result pxl8_ase_load(const char* filepath, pxl8_ase_file* ase_file);
pxl8_result pxl8_ase_load_palette(const char* filepath, u32* colors, u32* count);
void pxl8_ase_destroy(pxl8_ase_file* ase_file);
pxl8_result pxl8_ase_remap(const char* input_path, const char* output_path, const pxl8_ase_remap_config* config);
#ifdef __cplusplus
}