///|
/// Explicit palette maps for reusable indexed-frame transformations.
pub(all) struct PaletteMap {
source : Palette
target : Palette
mapping : Array[Byte]
} derive(Debug)
///|
pub fn PaletteMap::source(self : PaletteMap) -> Palette {
self.source
}
///|
pub fn PaletteMap::target(self : PaletteMap) -> Palette {
self.target
}
///|
pub fn PaletteMap::mapping(self : PaletteMap) -> ArrayView[Byte] {
self.mapping[:]
}
///|
/// Build a nearest-color map between two palettes.
pub fn build_palette_map(source : Palette, target : Palette) -> PaletteMap {
let mapping : Array[Byte] = []
for color in source.colors {
mapping.push(target.nearest_rgba(color).to_byte())
}
PaletteMap::{ source, target, mapping }
}
///|
/// Apply a palette map without touching pixel geometry.
pub fn QuantizedFrame::map_palette(
self : QuantizedFrame,
map : PaletteMap,
) -> QuantizedFrame {
let indices : Array[Byte] = []
let mapping = map.mapping()
for index in self.indices() {
let value = index.to_int()
indices.push(if value < mapping.length() { mapping[value] } else { 0 })
}
QuantizedFrame::{ palette: map.target(), indices }
}
///|
/// Merge two palettes while removing exact duplicates.
pub fn merge_palettes(left : Palette, right : Palette, limit : Int) -> Palette {
let colors : Array[Rgba] = []
let safe_limit = clamp_palette_size(limit)
for palette in [left, right] {
for color in palette.colors {
let mut found = false
for existing in colors {
if existing == color {
found = true
}
}
if !found && colors.length() < safe_limit {
colors.push(color)
}
}
}
if colors.length() == 0 {
colors.push(rgb(0, 0, 0))
}
Palette::new(colors~, transparent_index=None)
}
///|
/// Remap all frames through a shared target palette.
pub fn remap_animation_palette(
animation : Animation,
palette : Palette,
) -> Animation {
let result = Animation::new(width=animation.width, height=animation.height)
.with_loop_count(loop_count=animation.loop_count)
.with_background(background=animation.background)
for frame in animation.frames {
let quantized = remap_to_palette(frame.canvas(), palette)
ignore(
result.add_frame(
frame.with_canvas(
quantized.to_canvas(
width=frame.canvas().width,
height=frame.canvas().height,
),
),
),
)
}
result
}
///|
/// Count the number of source colors that map exactly to the target palette.
pub fn PaletteMap::exact_entries(self : PaletteMap) -> Int {
let mut count = 0
for i, color in self.source.colors {
if self.target.color(self.mapping[i].to_int()) == Some(color) {
count += 1
}
}
count
}