1use image::RgbaImage;
3
4const HIST_C0_BITS: usize = 5; const HIST_C1_BITS: usize = 6; const HIST_C2_BITS: usize = 5; const HIST_C0_ELEMS: usize = 1 << HIST_C0_BITS;
9const HIST_C1_ELEMS: usize = 1 << HIST_C1_BITS;
10const HIST_C2_ELEMS: usize = 1 << HIST_C2_BITS;
11
12const C0_SHIFT: usize = 8 - HIST_C0_BITS;
13const C1_SHIFT: usize = 8 - HIST_C1_BITS;
14const C2_SHIFT: usize = 8 - HIST_C2_BITS;
15
16const C0_SCALE: i32 = 2;
18const C1_SCALE: i32 = 3;
19const C2_SCALE: i32 = 1;
20
21const BOX_C0_LOG: usize = HIST_C0_BITS - 3;
23const BOX_C1_LOG: usize = HIST_C1_BITS - 3;
24const BOX_C2_LOG: usize = HIST_C2_BITS - 3;
25
26const BOX_C0_ELEMS: usize = 1 << BOX_C0_LOG;
27const BOX_C1_ELEMS: usize = 1 << BOX_C1_LOG;
28const BOX_C2_ELEMS: usize = 1 << BOX_C2_LOG;
29
30const BOX_C0_SHIFT: usize = C0_SHIFT + BOX_C0_LOG;
31const BOX_C1_SHIFT: usize = C1_SHIFT + BOX_C1_LOG;
32const BOX_C2_SHIFT: usize = C2_SHIFT + BOX_C2_LOG;
33
34const MAXJSAMPLE: i32 = 255;
35const MAXNUMCOLORS: usize = 256;
36
37#[derive(Clone)]
38pub struct Palette {
39 pub red: [u8; 256],
40 pub green: [u8; 256],
41 pub blue: [u8; 256],
42 pub alpha: [u8; 256],
43 pub colors_total: usize,
44}
45
46impl Default for Palette {
47 fn default() -> Self {
48 Palette {
49 red: [0; 256],
50 green: [0; 256],
51 blue: [0; 256],
52 alpha: [255; 256],
53 colors_total: 0,
54 }
55 }
56}
57
58impl Palette {
59 pub fn new() -> Self {
60 Self::default()
61 }
62
63 pub fn get(&self, idx: usize) -> (u8, u8, u8, u8) {
64 (self.red[idx], self.green[idx], self.blue[idx], self.alpha[idx])
65 }
66
67 pub fn set(&mut self, idx: usize, r: u8, g: u8, b: u8) {
68 self.red[idx] = r;
69 self.green[idx] = g;
70 self.blue[idx] = b;
71 self.alpha[idx] = 255;
72 }
73}
74
75#[derive(Clone, Copy, Default)]
76struct ColorBox {
77 c0min: i32,
78 c0max: i32,
79 c1min: i32,
80 c1max: i32,
81 c2min: i32,
82 c2max: i32,
83 volume: i64,
84 colorcount: i64,
85}
86
87pub struct Quantizer {
88 histogram: Box<[[[u16; HIST_C2_ELEMS]; HIST_C1_ELEMS]; HIST_C0_ELEMS]>,
89 fserrors: Vec<i16>,
90 error_limiter: Vec<i32>,
91 on_odd_row: bool,
92 palette: Palette,
93}
94
95impl Quantizer {
96 pub fn new(reference: &RgbaImage) -> Self {
97 let mut q = Quantizer {
98 histogram: Box::new([[[0u16; HIST_C2_ELEMS]; HIST_C1_ELEMS]; HIST_C0_ELEMS]),
99 fserrors: Vec::new(),
100 error_limiter: Vec::new(),
101 on_odd_row: false,
102 palette: Palette::new(),
103 };
104
105 q.init_error_limit();
106
107 let width = reference.width() as usize;
108 q.fserrors = vec![0i16; (width + 2) * 3];
109
110 q.prescan_quantize(reference);
111 q.select_colors(MAXNUMCOLORS);
112 q.zero_histogram();
113
114 q
115 }
116
117 fn init_error_limit(&mut self) {
118 self.error_limiter = vec![0i32; (MAXJSAMPLE * 2 + 1) as usize];
119 let table_offset = MAXJSAMPLE as usize;
120
121 const STEPSIZE: i32 = (MAXJSAMPLE + 1) / 16;
122
123 let mut out: i32 = 0;
124
125 for inp in 0..STEPSIZE {
127 self.error_limiter[table_offset.wrapping_add(inp as usize)] = out;
128 self.error_limiter[table_offset.wrapping_sub(inp as usize)] = -out;
129 out += 1;
130 }
131
132 for inp in STEPSIZE..(STEPSIZE * 3) {
134 self.error_limiter[table_offset.wrapping_add(inp as usize)] = out;
135 self.error_limiter[table_offset.wrapping_sub(inp as usize)] = -out;
136 if inp & 1 == 0 {
137 out += 1;
138 }
139 }
140
141 for inp in (STEPSIZE * 3)..=MAXJSAMPLE {
143 self.error_limiter[table_offset.wrapping_add(inp as usize)] = out;
144 self.error_limiter[table_offset.wrapping_sub(inp as usize)] = -out;
145 }
146 }
147
148 fn zero_histogram(&mut self) {
149 for c0 in 0..HIST_C0_ELEMS {
150 for c1 in 0..HIST_C1_ELEMS {
151 for c2 in 0..HIST_C2_ELEMS {
152 self.histogram[c0][c1][c2] = 0;
153 }
154 }
155 }
156 }
157
158 fn prescan_quantize(&mut self, img: &RgbaImage) {
159 for pixel in img.pixels() {
160 let r = (pixel[0] as usize) >> C0_SHIFT;
161 let g = (pixel[1] as usize) >> C1_SHIFT;
162 let b = (pixel[2] as usize) >> C2_SHIFT;
163
164 let cell = &mut self.histogram[r][g][b];
165 if *cell < u16::MAX {
166 *cell += 1;
167 }
168 }
169 }
170
171 fn find_biggest_color_pop(boxlist: &[ColorBox], numboxes: usize) -> Option<usize> {
172 let mut maxc: i64 = 0;
173 let mut which = None;
174
175 for (i, bx) in boxlist[..numboxes].iter().enumerate() {
176 if bx.colorcount > maxc && bx.volume > 0 {
177 which = Some(i);
178 maxc = bx.colorcount;
179 }
180 }
181
182 which
183 }
184
185 fn find_biggest_volume(boxlist: &[ColorBox], numboxes: usize) -> Option<usize> {
186 let mut maxv: i64 = 0;
187 let mut which = None;
188
189 for (i, bx) in boxlist[..numboxes].iter().enumerate() {
190 if bx.volume > maxv {
191 which = Some(i);
192 maxv = bx.volume;
193 }
194 }
195
196 which
197 }
198
199 fn update_box(&self, boxp: &mut ColorBox) {
200 let mut c0min = boxp.c0min;
201 let mut c0max = boxp.c0max;
202 let mut c1min = boxp.c1min;
203 let mut c1max = boxp.c1max;
204 let mut c2min = boxp.c2min;
205 let mut c2max = boxp.c2max;
206
207 if c0max > c0min {
208 'outer: for c0 in c0min..=c0max {
209 for c1 in c1min..=c1max {
210 for c2 in c2min..=c2max {
211 if self.histogram[c0 as usize][c1 as usize][c2 as usize] != 0 {
212 boxp.c0min = c0;
213 c0min = c0;
214 break 'outer;
215 }
216 }
217 }
218 }
219 }
220
221 if c0max > c0min {
222 'outer: for c0 in (c0min..=c0max).rev() {
223 for c1 in c1min..=c1max {
224 for c2 in c2min..=c2max {
225 if self.histogram[c0 as usize][c1 as usize][c2 as usize] != 0 {
226 boxp.c0max = c0;
227 c0max = c0;
228 break 'outer;
229 }
230 }
231 }
232 }
233 }
234
235 if c1max > c1min {
236 'outer: for c1 in c1min..=c1max {
237 for c0 in c0min..=c0max {
238 for c2 in c2min..=c2max {
239 if self.histogram[c0 as usize][c1 as usize][c2 as usize] != 0 {
240 boxp.c1min = c1;
241 c1min = c1;
242 break 'outer;
243 }
244 }
245 }
246 }
247 }
248
249 if c1max > c1min {
250 'outer: for c1 in (c1min..=c1max).rev() {
251 for c0 in c0min..=c0max {
252 for c2 in c2min..=c2max {
253 if self.histogram[c0 as usize][c1 as usize][c2 as usize] != 0 {
254 boxp.c1max = c1;
255 c1max = c1;
256 break 'outer;
257 }
258 }
259 }
260 }
261 }
262
263 if c2max > c2min {
264 'outer: for c2 in c2min..=c2max {
265 for c0 in c0min..=c0max {
266 for c1 in c1min..=c1max {
267 if self.histogram[c0 as usize][c1 as usize][c2 as usize] != 0 {
268 boxp.c2min = c2;
269 c2min = c2;
270 break 'outer;
271 }
272 }
273 }
274 }
275 }
276
277 if c2max > c2min {
278 'outer: for c2 in (c2min..=c2max).rev() {
279 for c0 in c0min..=c0max {
280 for c1 in c1min..=c1max {
281 if self.histogram[c0 as usize][c1 as usize][c2 as usize] != 0 {
282 boxp.c2max = c2;
283 c2max = c2;
284 break 'outer;
285 }
286 }
287 }
288 }
289 }
290
291 let dist0 = ((c0max - c0min) << C0_SHIFT) as i64 * C0_SCALE as i64;
292 let dist1 = ((c1max - c1min) << C1_SHIFT) as i64 * C1_SCALE as i64;
293 let dist2 = ((c2max - c2min) << C2_SHIFT) as i64 * C2_SCALE as i64;
294 boxp.volume = dist0 * dist0 + dist1 * dist1 + dist2 * dist2;
295
296 let mut ccount: i64 = 0;
297 for c0 in c0min..=c0max {
298 for c1 in c1min..=c1max {
299 for c2 in c2min..=c2max {
300 if self.histogram[c0 as usize][c1 as usize][c2 as usize] != 0 {
301 ccount += 1;
302 }
303 }
304 }
305 }
306 boxp.colorcount = ccount;
307 }
308
309 fn median_cut(
310 &self,
311 boxlist: &mut [ColorBox],
312 mut numboxes: usize,
313 desired_colors: usize,
314 ) -> usize {
315 while numboxes < desired_colors {
316 let b1_idx = if numboxes * 2 <= desired_colors {
318 Self::find_biggest_color_pop(boxlist, numboxes)
319 } else {
320 Self::find_biggest_volume(boxlist, numboxes)
321 };
322
323 let b1_idx = match b1_idx {
324 Some(idx) => idx,
325 None => break,
326 };
327
328 let b1 = boxlist[b1_idx];
329 let b2_idx = numboxes;
330 boxlist[b2_idx] = b1;
331
332 let c0 = ((b1.c0max - b1.c0min) << C0_SHIFT) * C0_SCALE;
333 let c1 = ((b1.c1max - b1.c1min) << C1_SHIFT) * C1_SCALE;
334 let c2 = ((b1.c2max - b1.c2min) << C2_SHIFT) * C2_SCALE;
335
336 let mut cmax = c1;
337 let mut n = 1;
338 if c2 > cmax {
339 cmax = c2;
340 n = 2;
341 }
342 if c0 > cmax {
343 n = 0;
344 }
345
346 match n {
347 0 => {
348 let lb = (b1.c0max + b1.c0min) / 2;
349 boxlist[b1_idx].c0max = lb;
350 boxlist[b2_idx].c0min = lb + 1;
351 }
352 1 => {
353 let lb = (b1.c1max + b1.c1min) / 2;
354 boxlist[b1_idx].c1max = lb;
355 boxlist[b2_idx].c1min = lb + 1;
356 }
357 2 => {
358 let lb = (b1.c2max + b1.c2min) / 2;
359 boxlist[b1_idx].c2max = lb;
360 boxlist[b2_idx].c2min = lb + 1;
361 }
362 _ => unreachable!(),
363 }
364
365 self.update_box(&mut boxlist[b1_idx]);
366 self.update_box(&mut boxlist[b2_idx]);
367 numboxes += 1;
368 }
369
370 numboxes
371 }
372
373 fn compute_color(&self, boxp: &ColorBox) -> (u8, u8, u8) {
374 let mut total: i64 = 0;
375 let mut c0total: i64 = 0;
376 let mut c1total: i64 = 0;
377 let mut c2total: i64 = 0;
378
379 for c0 in boxp.c0min..=boxp.c0max {
380 for c1 in boxp.c1min..=boxp.c1max {
381 for c2 in boxp.c2min..=boxp.c2max {
382 let count = self.histogram[c0 as usize][c1 as usize][c2 as usize] as i64;
383 if count != 0 {
384 total += count;
385 c0total += ((c0 << C0_SHIFT) + (1 << (C0_SHIFT - 1))) as i64 * count;
386 c1total += ((c1 << C1_SHIFT) + (1 << (C1_SHIFT - 1))) as i64 * count;
387 c2total += ((c2 << C2_SHIFT) + (1 << (C2_SHIFT - 1))) as i64 * count;
388 }
389 }
390 }
391 }
392
393 if total > 0 {
394 (
395 ((c0total + (total >> 1)) / total) as u8,
396 ((c1total + (total >> 1)) / total) as u8,
397 ((c2total + (total >> 1)) / total) as u8,
398 )
399 } else {
400 (255, 255, 255)
401 }
402 }
403
404 fn select_colors(&mut self, desired_colors: usize) {
405 let mut boxlist = vec![ColorBox::default(); desired_colors];
406
407 boxlist[0] = ColorBox {
409 c0min: 0,
410 c0max: (MAXJSAMPLE >> C0_SHIFT) as i32,
411 c1min: 0,
412 c1max: (MAXJSAMPLE >> C1_SHIFT) as i32,
413 c2min: 0,
414 c2max: (MAXJSAMPLE >> C2_SHIFT) as i32,
415 volume: 0,
416 colorcount: 0,
417 };
418
419 self.update_box(&mut boxlist[0]);
420 let numboxes = self.median_cut(&mut boxlist, 1, desired_colors);
421
422 for i in 0..numboxes {
423 let (r, g, b) = self.compute_color(&boxlist[i]);
424 self.palette.set(i, r, g, b);
425 }
426 self.palette.colors_total = numboxes;
427 }
428
429 pub fn palette(&self) -> &Palette {
430 &self.palette
431 }
432
433 pub fn palette_mut(&mut self) -> &mut Palette {
434 &mut self.palette
435 }
436
437 fn find_nearby_colors(
438 &self,
439 minc0: i32,
440 minc1: i32,
441 minc2: i32,
442 colorlist: &mut [u8],
443 ) -> usize {
444 let numcolors = self.palette.colors_total;
445
446 let maxc0 = minc0 + ((1 << BOX_C0_SHIFT) - (1 << C0_SHIFT));
447 let centerc0 = (minc0 + maxc0) >> 1;
448 let maxc1 = minc1 + ((1 << BOX_C1_SHIFT) - (1 << C1_SHIFT));
449 let centerc1 = (minc1 + maxc1) >> 1;
450 let maxc2 = minc2 + ((1 << BOX_C2_SHIFT) - (1 << C2_SHIFT));
451 let centerc2 = (minc2 + maxc2) >> 1;
452
453 let mut mindist = vec![0i64; MAXNUMCOLORS];
454 let mut minmaxdist: i64 = i64::MAX;
455
456 for i in 0..numcolors {
457 let x0 = self.palette.red[i] as i32;
458 let (min_dist0, max_dist0) =
459 Self::compute_dist_component(x0, minc0, maxc0, centerc0, C0_SCALE);
460
461 let x1 = self.palette.green[i] as i32;
462 let (min_dist1, max_dist1) =
463 Self::compute_dist_component(x1, minc1, maxc1, centerc1, C1_SCALE);
464
465 let x2 = self.palette.blue[i] as i32;
466 let (min_dist2, max_dist2) =
467 Self::compute_dist_component(x2, minc2, maxc2, centerc2, C2_SCALE);
468
469 mindist[i] = min_dist0 + min_dist1 + min_dist2;
470 let max_dist = max_dist0 + max_dist1 + max_dist2;
471 if max_dist < minmaxdist {
472 minmaxdist = max_dist;
473 }
474 }
475
476 let mut ncolors = 0;
477 for i in 0..numcolors {
478 if mindist[i] <= minmaxdist {
479 colorlist[ncolors] = i as u8;
480 ncolors += 1;
481 }
482 }
483
484 ncolors
485 }
486
487 fn compute_dist_component(
488 x: i32,
489 minc: i32,
490 maxc: i32,
491 centerc: i32,
492 scale: i32,
493 ) -> (i64, i64) {
494 if x < minc {
495 let tdist = (x - minc) as i64 * scale as i64;
496 let min_dist = tdist * tdist;
497 let tdist = (x - maxc) as i64 * scale as i64;
498 let max_dist = tdist * tdist;
499 (min_dist, max_dist)
500 } else if x > maxc {
501 let tdist = (x - maxc) as i64 * scale as i64;
502 let min_dist = tdist * tdist;
503 let tdist = (x - minc) as i64 * scale as i64;
504 let max_dist = tdist * tdist;
505 (min_dist, max_dist)
506 } else {
507 let tdist = if x <= centerc {
508 (x - maxc) as i64 * scale as i64
509 } else {
510 (x - minc) as i64 * scale as i64
511 };
512 (0, tdist * tdist)
513 }
514 }
515
516 fn find_best_colors(
517 &self,
518 minc0: i32,
519 minc1: i32,
520 minc2: i32,
521 numcolors: usize,
522 colorlist: &[u8],
523 bestcolor: &mut [u8],
524 ) {
525 let mut bestdist = vec![i64::MAX; BOX_C0_ELEMS * BOX_C1_ELEMS * BOX_C2_ELEMS];
526
527 const STEP_C0: i64 = ((1 << C0_SHIFT) * C0_SCALE) as i64;
528 const STEP_C1: i64 = ((1 << C1_SHIFT) * C1_SCALE) as i64;
529 const STEP_C2: i64 = ((1 << C2_SHIFT) * C2_SCALE) as i64;
530
531 for i in 0..numcolors {
532 let icolor = colorlist[i] as usize;
533 let r = self.palette.red[icolor] as i32;
534 let g = self.palette.green[icolor] as i32;
535 let b = self.palette.blue[icolor] as i32;
536
537 let mut inc0 = (minc0 - r) as i64 * C0_SCALE as i64;
538 let mut dist0 = inc0 * inc0;
539 let mut inc1 = (minc1 - g) as i64 * C1_SCALE as i64;
540 dist0 += inc1 * inc1;
541 let mut inc2 = (minc2 - b) as i64 * C2_SCALE as i64;
542 dist0 += inc2 * inc2;
543
544 inc0 = inc0 * (2 * STEP_C0) + STEP_C0 * STEP_C0;
545 inc1 = inc1 * (2 * STEP_C1) + STEP_C1 * STEP_C1;
546 inc2 = inc2 * (2 * STEP_C2) + STEP_C2 * STEP_C2;
547
548 let mut bptr_idx = 0;
549 let mut xx0 = inc0;
550
551 for _ic0 in 0..BOX_C0_ELEMS {
552 let mut dist1 = dist0;
553 let mut xx1 = inc1;
554
555 for _ic1 in 0..BOX_C1_ELEMS {
556 let mut dist2 = dist1;
557 let mut xx2 = inc2;
558
559 for _ic2 in 0..BOX_C2_ELEMS {
560 if dist2 < bestdist[bptr_idx] {
561 bestdist[bptr_idx] = dist2;
562 bestcolor[bptr_idx] = icolor as u8;
563 }
564 dist2 += xx2;
565 xx2 += 2 * STEP_C2 * STEP_C2;
566 bptr_idx += 1;
567 }
568 dist1 += xx1;
569 xx1 += 2 * STEP_C1 * STEP_C1;
570 }
571 dist0 += xx0;
572 xx0 += 2 * STEP_C0 * STEP_C0;
573 }
574 }
575 }
576
577 fn fill_inverse_cmap(&mut self, c0: i32, c1: i32, c2: i32) {
578 let mut colorlist = vec![0u8; MAXNUMCOLORS];
579 let mut bestcolor = vec![0u8; BOX_C0_ELEMS * BOX_C1_ELEMS * BOX_C2_ELEMS];
580
581 let bc0 = c0 >> BOX_C0_LOG as i32;
582 let bc1 = c1 >> BOX_C1_LOG as i32;
583 let bc2 = c2 >> BOX_C2_LOG as i32;
584
585 let minc0 = (bc0 << BOX_C0_SHIFT) + (1 << (C0_SHIFT - 1));
586 let minc1 = (bc1 << BOX_C1_SHIFT) + (1 << (C1_SHIFT - 1));
587 let minc2 = (bc2 << BOX_C2_SHIFT) + (1 << (C2_SHIFT - 1));
588
589 let numcolors = self.find_nearby_colors(minc0, minc1, minc2, &mut colorlist);
590 self.find_best_colors(minc0, minc1, minc2, numcolors, &colorlist, &mut bestcolor);
591
592 let base_c0 = (bc0 << BOX_C0_LOG as i32) as usize;
593 let base_c1 = (bc1 << BOX_C1_LOG as i32) as usize;
594 let base_c2 = (bc2 << BOX_C2_LOG as i32) as usize;
595
596 let mut cptr_idx = 0;
597 for ic0 in 0..BOX_C0_ELEMS {
598 for ic1 in 0..BOX_C1_ELEMS {
599 for ic2 in 0..BOX_C2_ELEMS {
600 self.histogram[base_c0 + ic0][base_c1 + ic1][base_c2 + ic2] =
601 bestcolor[cptr_idx] as u16 + 1;
602 cptr_idx += 1;
603 }
604 }
605 }
606 }
607
608 pub fn quantize_no_dither(&mut self, img: &RgbaImage) -> Vec<u8> {
609 let width = img.width() as usize;
610 let height = img.height() as usize;
611 let mut output = vec![0u8; width * height];
612
613 for (y, row) in img.rows().enumerate() {
614 for (x, pixel) in row.enumerate() {
615 let r = pixel[0] as usize;
616 let g = pixel[1] as usize;
617 let b = pixel[2] as usize;
618
619 let c0 = r >> C0_SHIFT;
620 let c1 = g >> C1_SHIFT;
621 let c2 = b >> C2_SHIFT;
622
623 let mut cached = self.histogram[c0][c1][c2];
624 if cached == 0 {
625 self.fill_inverse_cmap(c0 as i32, c1 as i32, c2 as i32);
626 cached = self.histogram[c0][c1][c2];
627 }
628
629 output[y * width + x] = (cached - 1) as u8;
630 }
631 }
632
633 output
634 }
635
636 pub fn quantize_fs_dither(&mut self, img: &RgbaImage) -> Vec<u8> {
637 let width = img.width() as usize;
638 let height = img.height() as usize;
639 let mut output = vec![0u8; width * height];
640
641 self.fserrors = vec![0i16; (width + 2) * 3];
642 self.on_odd_row = false;
643
644 let table_offset = MAXJSAMPLE as usize;
645
646 for row in 0..height {
647 let (dir, start_col, end_col, errorptr_start) = if self.on_odd_row {
648 (-1i32, width as i32 - 1, -1i32, (width + 1) * 3)
649 } else {
650 (1i32, 0i32, width as i32, 0usize)
651 };
652
653 let mut cur0: i32 = 0;
654 let mut cur1: i32 = 0;
655 let mut cur2: i32 = 0;
656 let mut belowerr0: i32 = 0;
657 let mut belowerr1: i32 = 0;
658 let mut belowerr2: i32 = 0;
659 let mut bpreverr0: i32 = 0;
660 let mut bpreverr1: i32 = 0;
661 let mut bpreverr2: i32 = 0;
662
663 let mut col = start_col;
664 let mut errorptr = errorptr_start as i32;
665 let dir3 = dir * 3;
666
667 while col != end_col {
668 let x = col as usize;
669 let pixel = img.get_pixel(x as u32, row as u32);
670
671 let ep_idx = (errorptr + dir3) as usize;
673 cur0 = (cur0 + self.fserrors[ep_idx] as i32 + 8) >> 4;
674 cur1 = (cur1 + self.fserrors[ep_idx + 1] as i32 + 8) >> 4;
675 cur2 = (cur2 + self.fserrors[ep_idx + 2] as i32 + 8) >> 4;
676
677 cur0 = self.error_limiter[table_offset.wrapping_add(cur0 as usize)];
678 cur1 = self.error_limiter[table_offset.wrapping_add(cur1 as usize)];
679 cur2 = self.error_limiter[table_offset.wrapping_add(cur2 as usize)];
680
681 cur0 += pixel[0] as i32;
682 cur1 += pixel[1] as i32;
683 cur2 += pixel[2] as i32;
684 cur0 = cur0.clamp(0, 255);
685 cur1 = cur1.clamp(0, 255);
686 cur2 = cur2.clamp(0, 255);
687
688 let c0 = (cur0 as usize) >> C0_SHIFT;
689 let c1 = (cur1 as usize) >> C1_SHIFT;
690 let c2 = (cur2 as usize) >> C2_SHIFT;
691
692 let mut cached = self.histogram[c0][c1][c2];
693 if cached == 0 {
694 self.fill_inverse_cmap(c0 as i32, c1 as i32, c2 as i32);
695 cached = self.histogram[c0][c1][c2];
696 }
697
698 let pixcode = (cached - 1) as usize;
699 output[row * width + x] = pixcode as u8;
700
701 cur0 -= self.palette.red[pixcode] as i32;
702 cur1 -= self.palette.green[pixcode] as i32;
703 cur2 -= self.palette.blue[pixcode] as i32;
704
705 let mut bnexterr = cur0;
706 let mut delta = cur0 * 2;
707 cur0 += delta; self.fserrors[errorptr as usize] = (bpreverr0 + cur0) as i16;
709 cur0 += delta; bpreverr0 = belowerr0 + cur0;
711 belowerr0 = bnexterr;
712 cur0 += delta; bnexterr = cur1;
715 delta = cur1 * 2;
716 cur1 += delta;
717 self.fserrors[errorptr as usize + 1] = (bpreverr1 + cur1) as i16;
718 cur1 += delta;
719 bpreverr1 = belowerr1 + cur1;
720 belowerr1 = bnexterr;
721 cur1 += delta;
722
723 bnexterr = cur2;
724 delta = cur2 * 2;
725 cur2 += delta;
726 self.fserrors[errorptr as usize + 2] = (bpreverr2 + cur2) as i16;
727 cur2 += delta;
728 bpreverr2 = belowerr2 + cur2;
729 belowerr2 = bnexterr;
730 cur2 += delta;
731
732 col += dir;
733 errorptr += dir3;
734 }
735
736 self.fserrors[errorptr as usize + 1] = bpreverr1 as i16;
737 self.fserrors[errorptr as usize + 2] = bpreverr2 as i16;
738
739 self.on_odd_row = !self.on_odd_row;
740 }
741
742 output
743 }
744
745 pub fn quantize(&mut self, img: &RgbaImage, dither: bool) -> Vec<u8> {
746 if dither { self.quantize_fs_dither(img) } else { self.quantize_no_dither(img) }
747 }
748
749 pub fn sync_palette_from(&mut self, other: &Palette) {
750 self.palette = other.clone();
751 }
752}
753
754pub fn sync_palette(from: &Palette, to: &mut Palette) {
755 *to = from.clone();
756}