devela/media/image/sixel/dither/
conf.rs
1#![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#[derive(Debug)]
18pub(crate) struct DitherConf {
19 pub palette: Vec<u8>,
21 pub cachetable: Option<Vec<u16>>,
23 pub reqcolors: i32,
25 pub ncolors: i32,
27 pub origcolors: i32,
29 pub optimized: bool,
31 pub optimize_palette: bool,
33 pub complexion: i32,
35 pub bodyonly: bool,
37 pub split_method: SixelSplit,
39 pub mean_method: SixelMean,
41 pub dither: Dither,
43 pub quality_mode: SixelQuality,
45 pub keycolor: i32,
47 pub pixel_format: PixelFormat,
49}
50
51impl DitherConf {
52 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 }
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 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 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 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 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 #[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 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 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 pub fn get_num_of_palette_colors(&self) -> i32 {
196 self.ncolors
197 }
198
199 pub fn get_num_of_histogram_colors(&self) -> i32 {
201 self.origcolors
202 }
203
204 pub fn get_palette(&self) -> &[u8] {
206 &self.palette
207 }
208
209 pub fn set_palette(&mut self, palette: Vec<u8>) {
211 self.palette = palette;
212 }
213
214 pub fn set_complexion_score(&mut self, score: i32) {
217 self.complexion = score;
218 }
219
220 pub fn set_body_only(&mut self, bodyonly: bool) {
224 self.bodyonly = bodyonly;
225 }
226
227 pub fn set_optimize_palette(&mut self, do_op: bool) {
231 self.optimize_palette = do_op;
232 }
233
234 pub fn set_pixel_format(&mut self, pixel_format: PixelFormat) {
236 self.pixel_format = pixel_format;
237 }
238
239 pub fn set_transparent(&mut self, index: i32) {
241 self.keycolor = index;
242 }
243
244 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 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 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}