#include "pxl8_colormap.h" #include static void rgb_to_hsl(u8 r, u8 g, u8 b, i32* h, i32* s, i32* l) { i32 max = r > g ? (r > b ? r : b) : (g > b ? g : b); i32 min = r < g ? (r < b ? r : b) : (g < b ? g : b); i32 chroma = max - min; *l = (max + min) / 2; if (chroma == 0) { *h = 0; *s = 0; } else { i32 denom = 255 - (*l > 127 ? 2 * (*l) - 255 : 255 - 2 * (*l)); if (denom <= 0) denom = 1; *s = (chroma * 255) / denom; if (*s > 255) *s = 255; if (max == (i32)r) { *h = (((i32)g - (i32)b) * 60) / chroma; if (*h < 0) *h += 360; } else if (max == (i32)g) { *h = 120 + (((i32)b - (i32)r) * 60) / chroma; } else { *h = 240 + (((i32)r - (i32)g) * 60) / chroma; } } } static u8 find_closest_color(const u32* palette, u8 target_r, u8 target_g, u8 target_b) { u8 best_idx = 1; u32 best_dist = 0xFFFFFFFF; u8 dynamic_end = PXL8_DYNAMIC_RANGE_START + PXL8_DYNAMIC_RANGE_COUNT; i32 th, ts, tl; rgb_to_hsl(target_r, target_g, target_b, &th, &ts, &tl); for (u32 i = 1; i < PXL8_FULLBRIGHT_START; i++) { if (i >= PXL8_DYNAMIC_RANGE_START && i < dynamic_end) { continue; } u32 c = palette[i]; u8 pr = c & 0xFF; u8 pg = (c >> 8) & 0xFF; u8 pb = (c >> 16) & 0xFF; i32 ph, ps, pl; rgb_to_hsl(pr, pg, pb, &ph, &ps, &pl); i32 dh = th - ph; if (dh > 180) dh -= 360; if (dh < -180) dh += 360; i32 ds = ts - ps; i32 dl = tl - pl; u32 dist = (u32)(dh * dh * 4 + ds * ds + dl * dl); if (dist < best_dist) { best_dist = dist; best_idx = (u8)i; if (dist == 0) break; } } return best_idx; } void pxl8_colormap_generate(pxl8_colormap* cm, const u32* palette, const pxl8_level_tint* tint) { if (!cm || !palette) return; u8 dark_r, dark_g, dark_b; if (tint && tint->tint_strength > 0.0f) { f32 t = tint->tint_strength; f32 inv = 1.0f - t; dark_r = (u8)(tint->dark_r * inv + tint->tint_r * t); dark_g = (u8)(tint->dark_g * inv + tint->tint_g * t); dark_b = (u8)(tint->dark_b * inv + tint->tint_b * t); } else if (tint) { dark_r = tint->dark_r; dark_g = tint->dark_g; dark_b = tint->dark_b; } else { dark_r = dark_g = dark_b = 0; } for (u32 light = 0; light < PXL8_LIGHT_LEVELS; light++) { f32 brightness = (f32)light / (f32)(PXL8_LIGHT_LEVELS - 1); for (u32 pal_idx = 0; pal_idx < 256; pal_idx++) { u8 result_idx; if (pal_idx == PXL8_TRANSPARENT) { result_idx = PXL8_TRANSPARENT; } else if (pal_idx >= PXL8_FULLBRIGHT_START) { result_idx = (u8)pal_idx; } else { u32 c = palette[pal_idx]; u8 r = c & 0xFF; u8 g = (c >> 8) & 0xFF; u8 b = (c >> 16) & 0xFF; u8 target_r = (u8)(dark_r + (r - dark_r) * brightness); u8 target_g = (u8)(dark_g + (g - dark_g) * brightness); u8 target_b = (u8)(dark_b + (b - dark_b) * brightness); result_idx = find_closest_color(palette, target_r, target_g, target_b); } cm->table[light * 256 + pal_idx] = result_idx; } } }