devela/media/image/sixel/dither/
conf.rs

1// devela::media::image::sixel::dither::conf
2
3#![allow(dead_code, reason = "unused DitherConf methods")]
4
5use crate::{vec_ as vec, Vec};
6
7use super::super::{
8    sixel_quant_apply_palette, sixel_quant_make_palette, Dither, PixelFormat, SixelError,
9    SixelMean, SixelPalette, SixelQuality, SixelResult, SixelSplit, SIXEL_PALETTE_MAX,
10};
11
12/// Configuration for sixel dithering.
13//
14// # Adaptation
15// - Based on `sixel_dither_t` from the `libsixel` C library,
16//   adapted with adjustments for idiomatic Rust usage.
17#[derive(Debug)]
18pub(crate) struct DitherConf {
19    /// Palette definition.
20    pub palette: Vec<u8>,
21    /// Cache table.
22    pub cachetable: Option<Vec<u16>>,
23    /// The number of requested colors.
24    pub reqcolors: i32,
25    /// The number of active colors.
26    pub ncolors: i32,
27    /// The number of original colors.
28    pub origcolors: i32,
29    /// Pixel is 15bpp compressible.
30    pub optimized: bool,
31    /// Minimize palette size.
32    pub optimize_palette: bool,
33    /// For complexion correction.
34    pub complexion: i32,
35    /// Do not output palette section if true.
36    pub bodyonly: bool,
37    /// Method for finding the largest dimention for splitting.
38    pub split_method: SixelSplit,
39    /// Method for choosing a color from the box.
40    pub mean_method: SixelMean,
41    /// Method for diffusing
42    pub dither: Dither,
43    /// Quality of histogram.
44    pub quality_mode: SixelQuality,
45    /// Background color.
46    pub keycolor: i32,
47    /// Pixelformat for internal processing.
48    pub pixel_format: PixelFormat,
49}
50
51impl DitherConf {
52    /// Creates a new dither configuration with the specified number of colors.
53    pub fn new(mut ncolors: i32) -> SixelResult<Self> {
54        let quality_mode = if ncolors < 0 {
55            ncolors = SIXEL_PALETTE_MAX as i32;
56            SixelQuality::HighColor
57        } else {
58            if ncolors > SIXEL_PALETTE_MAX as i32 {
59                return Err(SixelError::BadInput);
60            }
61            if ncolors < 1 {
62                return Err(SixelError::BadInput);
63                // sixel_helper_set_additional_message(
64                // "DitherConf::new: palette ncolors must be more than 0");
65            }
66            SixelQuality::Low
67        };
68        Ok(Self {
69            palette: vec![0; ncolors as usize * 3],
70            cachetable: None,
71            reqcolors: ncolors,
72            ncolors,
73            origcolors: (-1),
74            keycolor: (-1),
75            optimized: false,
76            optimize_palette: false,
77            complexion: 1,
78            bodyonly: false,
79            split_method: SixelSplit::Norm,
80            mean_method: SixelMean::Center,
81            dither: Dither::FS,
82            quality_mode,
83            pixel_format: PixelFormat::RGB888,
84        })
85    }
86
87    /// TODO
88    pub fn with_palette(palette: SixelPalette) -> SixelResult<Self> {
89        let mut result = DitherConf::new(palette.num_colors() as i32)?;
90        result.palette = palette.palette().to_vec();
91        result.keycolor = palette.keycolor();
92        result.optimized = true;
93        result.optimize_palette = false;
94        Ok(result)
95    }
96
97    /// TODO
98    pub fn set_split_method(&mut self, split_method: SixelSplit) {
99        self.split_method = if matches!(split_method, SixelSplit::Auto) {
100            SixelSplit::Norm
101        } else {
102            split_method
103        };
104    }
105
106    /// TODO
107    pub fn set_mean_method(&mut self, mean_method: SixelMean) {
108        self.mean_method = if matches!(mean_method, SixelMean::Auto) {
109            SixelMean::Center
110        } else {
111            mean_method
112        };
113    }
114
115    /// TODO
116    pub fn set_quality_mode(&mut self, quality_mode: SixelQuality) {
117        self.quality_mode = if matches!(quality_mode, SixelQuality::Auto) {
118            if self.ncolors <= 8 {
119                SixelQuality::High
120            } else {
121                SixelQuality::Low
122            }
123        } else {
124            quality_mode
125        };
126    }
127
128    /// TODO
129    #[allow(clippy::too_many_arguments)]
130    pub fn initialize(
131        &mut self,
132        data: &[u8],
133        width: i32,
134        height: i32,
135        pixel_format: PixelFormat,
136        split_method: SixelSplit,
137        mean_method: SixelMean,
138        quality_mode: SixelQuality,
139    ) -> SixelResult<()> {
140        self.set_pixel_format(pixel_format);
141        #[expect(clippy::single_match_else, reason = "could be extended")]
142        let input_pixels = match pixel_format {
143            PixelFormat::RGB888 => data.to_vec(),
144            _ => {
145                /* normalize pixel_format */
146                let mut normalized_pixels = vec![0; (width * height * 3) as usize];
147                self.set_pixel_format(pixel_format.normalize(
148                    &mut normalized_pixels,
149                    data,
150                    width,
151                    height,
152                )?);
153                normalized_pixels
154            }
155        };
156
157        self.set_split_method(split_method);
158        self.set_mean_method(mean_method);
159        self.set_quality_mode(quality_mode);
160
161        let buf = sixel_quant_make_palette(
162            &input_pixels,
163            width * height * 3,
164            PixelFormat::RGB888,
165            self.reqcolors,
166            &mut self.ncolors,
167            &mut self.origcolors,
168            self.split_method,
169            self.mean_method,
170            self.quality_mode,
171        )?;
172
173        self.palette = buf;
174        self.optimized = true;
175        if self.origcolors <= self.reqcolors {
176            self.dither = Dither::None;
177        }
178        Ok(())
179    }
180
181    /// Set diffusion method.
182    pub fn set_diffusion_method(&mut self, method: Dither) {
183        self.dither = if matches!(method, Dither::Auto) {
184            if self.ncolors > 16 {
185                Dither::FS
186            } else {
187                Dither::Atkinson
188            }
189        } else {
190            method
191        };
192    }
193
194    /// Get number of palette colors.
195    pub fn get_num_of_palette_colors(&self) -> i32 {
196        self.ncolors
197    }
198
199    /// Get number of histogram colors.
200    pub fn get_num_of_histogram_colors(&self) -> i32 {
201        self.origcolors
202    }
203
204    /// Get the palette.
205    pub fn get_palette(&self) -> &[u8] {
206        &self.palette
207    }
208
209    /// Set the palette.
210    pub fn set_palette(&mut self, palette: Vec<u8>) {
211        self.palette = palette;
212    }
213
214    /// set the factor of complexion color correcting
215    //  complexion score (>= 1)
216    pub fn set_complexion_score(&mut self, score: i32) {
217        self.complexion = score;
218    }
219
220    /// Set whether omitting palette definition.
221    ///
222    /// false: outputs palette section.
223    pub fn set_body_only(&mut self, bodyonly: bool) {
224        self.bodyonly = bodyonly;
225    }
226
227    /// Set whether optimize palette size.
228    ///
229    /// false: optimizes the palette size.
230    pub fn set_optimize_palette(&mut self, do_op: bool) {
231        self.optimize_palette = do_op;
232    }
233
234    /// Set the pixel format
235    pub fn set_pixel_format(&mut self, pixel_format: PixelFormat) {
236        self.pixel_format = pixel_format;
237    }
238
239    /// Set the transparent color index.
240    pub fn set_transparent(&mut self, index: i32) {
241        self.keycolor = index;
242    }
243
244    /* set transparent */
245    pub fn apply_palette(
246        &mut self,
247        pixels: &[u8],
248        width: i32,
249        height: i32,
250    ) -> SixelResult<Vec<u8>> {
251        let bufsize = width * height;
252        let mut dest = vec![0; bufsize as usize];
253
254        /* if quality_mode is full, do not use palette caching */
255        if matches!(self.quality_mode, SixelQuality::Full) {
256            self.optimized = false;
257        }
258
259        if self.cachetable.is_none()
260            && self.optimized
261            && self.palette != SixelPalette::PAL_MONO_DARK
262            && self.palette != SixelPalette::PAL_MONO_LIGHT
263        {
264            self.cachetable = Some(vec![0; 1 << (3 * 5)]);
265        }
266
267        let mut input_pixels = if !matches!(self.pixel_format, PixelFormat::RGB888) {
268            /* normalize pixel_format */
269            let mut normalized_pixels = vec![0; (width * height * 3) as usize];
270            self.pixel_format =
271                self.pixel_format.normalize(&mut normalized_pixels, pixels, width, height)?;
272            normalized_pixels
273        } else {
274            pixels.to_vec()
275        };
276        let ncolors = sixel_quant_apply_palette(
277            &mut dest,
278            &mut input_pixels,
279            width,
280            height,
281            3,
282            &mut self.palette,
283            self.ncolors,
284            self.dither,
285            self.optimized,
286            self.optimize_palette,
287            self.complexion,
288            Some(self.cachetable.as_mut().unwrap()),
289        )?;
290        self.ncolors = ncolors;
291
292        Ok(dest)
293    }
294}