a_sixel/
bit.rs

1//! Encodes a palette by bucketing bit ranges into a power of two number of
2//! buckets. This is very fast and produces ok results for most images at larger
3//! palette sizes (e.g. 256).
4
5use 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            // Since elements to the right will get shifted off first, we put them in grb
65            // order (order of most-least significant for luminance). This probably doesn't
66            // make a huge difference, but the theory is nice.
67            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}