1use std::collections::HashSet;
6
7use dilate::DilateExpand;
8use ordered_float::OrderedFloat;
9use palette::{
10 IntoColor,
11 Lab,
12 Srgb,
13};
14
15use crate::{
16 dither::Sierra,
17 private,
18 PaletteBuilder,
19 SixelEncoder,
20};
21
22pub type BitSixelEncoderMono<D = Sierra> = SixelEncoder<BitPaletteBuilder<2>, D>;
23pub type BitSixelEncoder4<D = Sierra> = SixelEncoder<BitPaletteBuilder<4>, D>;
24pub type BitSixelEncoder8<D = Sierra> = SixelEncoder<BitPaletteBuilder<8>, D>;
25pub type BitSixelEncoder16<D = Sierra> = SixelEncoder<BitPaletteBuilder<16>, D>;
26pub type BitSixelEncoder32<D = Sierra> = SixelEncoder<BitPaletteBuilder<32>, D>;
27pub type BitSixelEncoder64<D = Sierra> = SixelEncoder<BitPaletteBuilder<64>, D>;
28pub type BitSixelEncoder128<D = Sierra> = SixelEncoder<BitPaletteBuilder<128>, D>;
29pub type BitSixelEncoder256<D = Sierra> = SixelEncoder<BitPaletteBuilder<256>, D>;
30
31#[derive(Debug, Clone, Copy)]
32struct Bucket {
33 color: (u64, u64, u64),
34 count: usize,
35}
36
37#[derive(Debug)]
38pub struct BitPaletteBuilder<const PALETTE_SIZE: usize> {
39 buckets: Vec<Bucket>,
40}
41
42impl<const PALETTE_SIZE: usize> BitPaletteBuilder<PALETTE_SIZE> {
43 const PALETTE_DEPTH: usize = PALETTE_SIZE.ilog2() as usize;
44 const SHIFT: usize = 24 - Self::PALETTE_DEPTH;
45
46 fn new() -> Self {
47 BitPaletteBuilder {
48 buckets: vec![
49 Bucket {
50 color: (0, 0, 0),
51 count: 0,
52 };
53 PALETTE_SIZE
54 ],
55 }
56 }
57
58 fn insert(&mut self, color: Srgb<u8>) {
59 let index = {
60 let r = color.red.dilate_expand::<3>().value();
61 let g = color.green.dilate_expand::<3>().value();
62 let b = color.blue.dilate_expand::<3>().value();
63
64 let rgb = g << 2 | r << 1 | b;
68
69 (rgb >> Self::SHIFT) as usize
70 };
71
72 let node = &mut self.buckets[index];
73 node.color.0 += color.red as u64;
74 node.color.1 += color.green as u64;
75 node.color.2 += color.blue as u64;
76 node.count += 1;
77 }
78}
79
80impl<const PALETTE_SIZE: usize> private::Sealed for BitPaletteBuilder<PALETTE_SIZE> {}
81impl<const PALETTE_SIZE: usize> PaletteBuilder for BitPaletteBuilder<PALETTE_SIZE> {
82 const PALETTE_SIZE: usize = PALETTE_SIZE;
83
84 fn build_palette(image: &image::RgbImage) -> Vec<Lab> {
85 let mut builder = Self::new();
86
87 for pixel in image.pixels() {
88 builder.insert(Srgb::<u8>::new(pixel[0], pixel[1], pixel[2]));
89 }
90
91 builder
92 .buckets
93 .into_iter()
94 .filter(|node| node.count > 0)
95 .map(|node| {
96 let rgb = Srgb::new(
97 (node.color.0 / node.count as u64) as u8,
98 (node.color.1 / node.count as u64) as u8,
99 (node.color.2 / node.count as u64) as u8,
100 );
101 let lab: Lab = rgb.into_format().into_color();
102 [
103 OrderedFloat(lab.l),
104 OrderedFloat(lab.a),
105 OrderedFloat(lab.b),
106 ]
107 })
108 .collect::<HashSet<_>>()
109 .into_iter()
110 .map(|[l, a, b]| Lab::new(*l, *a, *b))
111 .collect::<Vec<_>>()
112 }
113}