1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
use thiserror::Error;

use super::{
    chunk::Chunk,
    chunks::{old_palette::OldPaletteChunk, palette::PaletteChunk},
    header::Header,
    raw_frame::RawFrame,
    scalars::Color,
};

#[derive(Debug)]
pub struct Palette {
    pub colors: [Color; 256],
}

impl Default for Palette {
    fn default() -> Self {
        Palette {
            colors: [Color::default(); 256],
        }
    }
}

pub fn create_palette(header: &Header, frames: &[RawFrame<'_>]) -> Result<Palette, PaletteError> {
    let mut palette = Palette::default();
    let palette_chunks: Vec<_> = frames
        .iter()
        .flat_map(|frame| {
            frame.chunks.iter().filter_map(|chunk| {
                if let Chunk::Palette(palette) = chunk {
                    Some(palette)
                } else {
                    None
                }
            })
        })
        .collect();
    if !palette_chunks.is_empty() {
        process_palette_chunks(header.transparent_index, &palette_chunks, &mut palette)?;
        return Ok(palette);
    }
    let palette0004_chunks: Vec<_> = frames
        .iter()
        .flat_map(|frame| {
            frame.chunks.iter().filter_map(|chunk| {
                if let Chunk::Palette0004(palette) = chunk {
                    Some(palette)
                } else {
                    None
                }
            })
        })
        .collect();
    if !palette0004_chunks.is_empty() {
        process_old_palette_chunks(header.transparent_index, &palette0004_chunks, &mut palette)?;
        return Ok(palette);
    }
    let palette0011_chunks: Vec<_> = frames
        .iter()
        .flat_map(|frame| {
            frame.chunks.iter().filter_map(|chunk| {
                if let Chunk::Palette0011(palette) = chunk {
                    Some(palette)
                } else {
                    None
                }
            })
        })
        .collect();
    if !palette0011_chunks.is_empty() {
        process_old_palette_chunks(header.transparent_index, &palette0011_chunks, &mut palette)?;
        return Ok(palette);
    }
    Err(PaletteError::Missing)
}

fn process_palette_chunks(
    transparent_index: u8,
    chunks: &[&PaletteChunk<'_>],
    palette: &mut Palette,
) -> Result<(), PaletteError> {
    let mut ok = false;
    for chunk in chunks.iter() {
        // The aseprite palette chunk is a bit weird. Both the palette size
        // and the color indices use `DWORD` (u32) as data type. Indexed
        // colors use `BYTE` (u8) though.
        for (entry, color_idx) in chunk.entries.iter().zip(chunk.indices.clone()) {
            palette.colors[usize::from(color_idx)] = entry.color;
            ok = true;
        }
    }
    if ok {
        palette.colors[usize::from(transparent_index)].alpha = 0;
        Ok(())
    } else {
        Err(PaletteError::Empty)
    }
}

fn process_old_palette_chunks(
    transparent_index: u8,
    chunks: &[&OldPaletteChunk],
    palette: &mut Palette,
) -> Result<(), PaletteError> {
    let mut ok = false;
    for chunk in chunks.iter() {
        let mut color_idx = 0usize;
        for packet in &chunk.packets {
            color_idx += usize::from(packet.entries_to_skip);
            if color_idx + packet.colors.len() > 256 {
                return Err(PaletteError::IndexOutOfBounds);
            }
            for color in &packet.colors {
                palette.colors[color_idx] = Color {
                    red: color.red,
                    green: color.green,
                    blue: color.blue,
                    alpha: 255,
                };
                color_idx += 1;
                ok = true;
            }
        }
    }
    if ok {
        palette.colors[usize::from(transparent_index)].alpha = 0;
        Ok(())
    } else {
        Err(PaletteError::Empty)
    }
}

#[derive(Debug, Copy, Clone, Error)]
pub enum PaletteError {
    #[error("Palette is missing")]
    Missing,
    #[error("Palette is empty")]
    Empty,
    #[error("First color index not in range 0..255")]
    FirstColorIndexOutOfBounds,
    #[error("Last color index not in range 0..255")]
    LastColorIndexOutOfBounds,
    #[error("First color index > last color index")]
    FirstColorIndexGreaterThanLastColorIndex,
    #[error("Palette index out of bounds")]
    IndexOutOfBounds,
}