1use alloc::vec::Vec;
2
3use crate::{
4 dither::SixelDither,
5 pixel_format::PixelFormat,
6 std::{cmp, fmt},
7 Error, Result,
8};
9
10use super::{ColorMap, Sample};
11
12pub const PALETTE_MAX: usize = 256;
14
15#[repr(u32)]
17#[derive(Clone, Copy, Debug, Default, PartialEq)]
18pub enum LookupFunction {
19 #[default]
20 Normal,
21 Fast,
22 MonoDarkBg,
23 MonoLightBg,
24}
25
26impl LookupFunction {
27 pub const fn new() -> Self {
29 Self::Normal
30 }
31}
32
33#[repr(u8)]
35#[derive(Clone, Copy, Debug, Default, PartialEq)]
36pub enum MethodForLargest {
37 #[default]
39 Auto = 0,
40 Norm,
42 Lum,
44}
45
46impl MethodForLargest {
47 pub const fn new() -> Self {
49 Self::Auto
50 }
51}
52
53impl From<MethodForLargest> for &'static str {
54 fn from(val: MethodForLargest) -> Self {
55 match val {
56 MethodForLargest::Auto => "auto",
57 MethodForLargest::Norm => "normalized",
58 MethodForLargest::Lum => "luminosity",
59 }
60 }
61}
62
63impl From<&MethodForLargest> for &'static str {
64 fn from(val: &MethodForLargest) -> Self {
65 (*val).into()
66 }
67}
68
69impl fmt::Display for MethodForLargest {
70 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
71 write!(f, "{}", <&str>::from(self))
72 }
73}
74
75#[repr(u8)]
77#[derive(Clone, Copy, Debug, Default, PartialEq)]
78pub enum MethodForRep {
79 #[default]
81 Auto = 0,
82 CenterBox,
84 AverageColors,
86 AveragePixels,
88}
89
90impl MethodForRep {
91 pub const fn new() -> Self {
93 Self::Auto
94 }
95}
96
97impl From<MethodForRep> for &'static str {
98 fn from(val: MethodForRep) -> Self {
99 match val {
100 MethodForRep::Auto => "auto",
101 MethodForRep::CenterBox => "center box",
102 MethodForRep::AverageColors => "average colors",
103 MethodForRep::AveragePixels => "average pixels",
104 }
105 }
106}
107
108impl From<&MethodForRep> for &'static str {
109 fn from(val: &MethodForRep) -> Self {
110 (*val).into()
111 }
112}
113
114impl fmt::Display for MethodForRep {
115 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
116 write!(f, "{}", <&str>::from(self))
117 }
118}
119
120#[repr(u8)]
122#[derive(Clone, Copy, Debug, Default, PartialEq)]
123pub enum MethodForDiffuse {
124 #[default]
126 Auto = 0,
127 None,
129 Atkinson,
131 Fs,
133 Jajuni,
135 Stucki,
137 Burkes,
139 ADither,
141 XDither,
143}
144
145impl MethodForDiffuse {
146 pub const fn new() -> Self {
148 Self::Auto
149 }
150
151 pub const fn is_mask(&self) -> bool {
153 matches!(self, Self::ADither | Self::XDither)
154 }
155}
156
157#[repr(u8)]
159#[derive(Clone, Copy, Debug, Default, PartialEq)]
160pub enum MethodForResampling {
161 Nearest = 0,
163 Gaussian,
165 Hanning,
167 Hamming,
169 #[default]
171 Bilinear,
172 Welsh,
174 Bicubic,
176 Lanczos2,
178 Lanczos3,
180 Lanczos4,
182}
183
184impl MethodForResampling {
185 pub const fn new() -> Self {
187 Self::Bilinear
188 }
189}
190
191#[repr(u8)]
193#[derive(Clone, Copy, Debug, Default, PartialEq)]
194pub enum QualityMode {
195 #[default]
197 Auto = 0,
198 High,
200 Low,
202 Full,
204 HighColor,
206}
207
208impl QualityMode {
209 pub const fn new() -> Self {
211 Self::Auto
212 }
213}
214
215pub struct PaletteConfig {
217 pub pixel_format: PixelFormat,
218 pub method_for_largest: MethodForLargest,
219 pub method_for_rep: MethodForRep,
220 pub quality_mode: QualityMode,
221}
222
223impl PaletteConfig {
224 pub const fn new() -> Self {
226 Self {
227 pixel_format: PixelFormat::new(),
228 method_for_largest: MethodForLargest::new(),
229 method_for_rep: MethodForRep::new(),
230 quality_mode: QualityMode::new(),
231 }
232 }
233}
234
235pub fn compute_hash(data: &[u8], depth: usize) -> usize {
237 let depth = cmp::min(depth, data.len());
238 let mut hash = 0;
239
240 for (n, b) in data[..depth].iter().enumerate().rev() {
241 hash |= (b.wrapping_shr(3) as usize).wrapping_shl((depth - n).saturating_mul(5) as u32);
242 }
243
244 hash
245}
246
247pub fn largest_by_norm(min_val: &[Sample], max_val: &[Sample]) -> usize {
249 let depth = min_val.len();
250
251 let mut largest_spread = 0;
252 let mut largest_dimension = 0;
253
254 for (plane, (&minv, &maxv)) in min_val[..depth]
255 .iter()
256 .zip(max_val[..depth].iter())
257 .enumerate()
258 {
259 let delta = maxv.saturating_sub(minv);
260 if delta > largest_spread {
261 largest_spread = delta;
262 largest_dimension = plane;
263 }
264 }
265
266 largest_dimension
267}
268
269pub fn largest_by_luminosity(min_val: &[Sample], max_val: &[Sample]) -> usize {
281 let depth = min_val.len();
282 let min_val_len = min_val.len();
283 let max_val_len = max_val.len();
284
285 if min_val_len != 3 || max_val_len != 3 || depth == 1 || min_val_len != max_val_len {
286 0
287 } else {
288 let lumin_factor = [0.2989f32, 0.5866f32, 0.1145f32];
289 let (mut max_lumin, mut max_dimension): (f32, usize) = (0.0, 0);
290
291 for (plane, (&minv, &maxv)) in min_val[..3].iter().zip(max_val[..3].iter()).enumerate() {
292 let lumin = lumin_factor[plane] * maxv.saturating_sub(minv) as f32;
293 if lumin > max_lumin {
294 max_lumin = lumin;
295 max_dimension = plane;
296 }
297 }
298
299 max_dimension
300 }
301}
302
303pub fn error_diffuse(
305 data: &mut [u8],
306 pos: i32,
307 depth: i32,
308 error: i32,
309 numerator: i32,
310 denominator: i32,
311 area: i32,
312) -> Result<()> {
313 if pos < 0 || pos >= area {
314 Err(Error::Quant(format!(
315 "invalid position: {pos}, area: {area}"
316 )))
317 } else if denominator == 0 {
318 Err(Error::Quant("invalid denominator, divide-by-zero".into()))
319 } else if let Some(ret) = data.get_mut((pos * depth) as usize) {
320 let mut c = (*ret as i32)
321 .saturating_add(error.saturating_mul(numerator).saturating_div(denominator));
322
323 if c < 0 {
324 c = 0;
325 }
326
327 if c >= 1 << 8 {
328 c = (1 << 8) - 1;
329 }
330
331 *ret = c as u8;
332
333 Ok(())
334 } else {
335 let data_len = data.len();
336 Err(Error::Quant(format!(
337 "pos({pos}) * depth({depth}) is out-of-bounds, max value: {data_len})"
338 )))
339 }
340}
341
342type DiffuseFn = fn(&mut [u8], i32, i32, i32, i32, i32, i32) -> Result<()>;
343
344pub fn diffuse_none(
346 _data: &mut [u8],
347 _width: i32,
348 _height: i32,
349 _x: i32,
350 _y: i32,
351 _depth: i32,
352 _error: i32,
353) -> Result<()> {
354 Ok(())
355}
356
357pub fn diffuse_fs(
364 data: &mut [u8],
365 width: i32,
366 height: i32,
367 x: i32,
368 y: i32,
369 depth: i32,
370 error: i32,
371) -> Result<()> {
372 let pos = y.saturating_mul(width).saturating_add(x);
373
374 if x < width.saturating_sub(1) && y < height.saturating_sub(1) {
375 error_diffuse(
377 data,
378 pos.saturating_add(1),
379 depth,
380 error,
381 7,
382 16,
383 width.saturating_mul(height),
384 )?;
385 error_diffuse(
387 data,
388 pos.saturating_add(width.saturating_sub(1)),
389 depth,
390 error,
391 3,
392 16,
393 width.saturating_mul(height),
394 )?;
395 error_diffuse(
397 data,
398 pos.saturating_add(width),
399 depth,
400 error,
401 5,
402 16,
403 width.saturating_mul(height),
404 )?;
405 error_diffuse(
407 data,
408 pos.saturating_add(width.saturating_add(1)),
409 depth,
410 error,
411 1,
412 16,
413 width.saturating_mul(height),
414 )?;
415 }
416
417 Ok(())
418}
419
420pub fn diffuse_atkinson(
428 data: &mut [u8],
429 width: i32,
430 height: i32,
431 x: i32,
432 y: i32,
433 depth: i32,
434 error: i32,
435) -> Result<()> {
436 let pos = y.saturating_mul(width).saturating_add(x);
437
438 if y < height.saturating_sub(2) {
439 let area = width.saturating_mul(height);
440
441 error_diffuse(data, pos.saturating_add(1), depth, error, 1, 8, area)?;
443 error_diffuse(data, pos.saturating_add(2), depth, error, 1, 8, area)?;
445 error_diffuse(
447 data,
448 pos.saturating_add(width.saturating_sub(1)),
449 depth,
450 error,
451 1,
452 8,
453 area,
454 )?;
455 error_diffuse(data, pos.saturating_add(width), depth, error, 1, 8, area)?;
457 error_diffuse(
459 data,
460 pos.saturating_add(width.saturating_add(1)),
461 depth,
462 error,
463 1,
464 8,
465 area,
466 )?;
467 error_diffuse(
469 data,
470 pos.saturating_add(width.saturating_mul(2)),
471 depth,
472 error,
473 1,
474 8,
475 area,
476 )?;
477 }
478
479 Ok(())
480}
481
482pub fn diffuse_jajuni(
490 data: &mut [u8],
491 width: i32,
492 height: i32,
493 x: i32,
494 y: i32,
495 depth: i32,
496 error: i32,
497) -> Result<()> {
498 let pos = y.saturating_mul(width).saturating_add(x);
499
500 if y < height.saturating_sub(2) {
501 let area = width.saturating_mul(height);
502 let width2 = width.saturating_mul(2);
503
504 error_diffuse(data, pos.saturating_add(1), depth, error, 7, 48, area)?;
505 error_diffuse(data, pos.saturating_add(2), depth, error, 5, 48, area)?;
506 error_diffuse(
507 data,
508 pos.saturating_add(width.saturating_sub(2)),
509 depth,
510 error,
511 3,
512 48,
513 area,
514 )?;
515 error_diffuse(
516 data,
517 pos.saturating_add(width.saturating_sub(1)),
518 depth,
519 error,
520 5,
521 48,
522 area,
523 )?;
524 error_diffuse(data, pos.saturating_add(width), depth, error, 7, 48, area)?;
525 error_diffuse(
526 data,
527 pos.saturating_add(width.saturating_add(1)),
528 depth,
529 error,
530 5,
531 48,
532 area,
533 )?;
534 error_diffuse(
535 data,
536 pos.saturating_add(width.saturating_add(2)),
537 depth,
538 error,
539 3,
540 48,
541 area,
542 )?;
543 error_diffuse(
544 data,
545 pos.saturating_add(width2.saturating_sub(2)),
546 depth,
547 error,
548 1,
549 48,
550 area,
551 )?;
552 error_diffuse(
553 data,
554 pos.saturating_add(width2.saturating_sub(1)),
555 depth,
556 error,
557 3,
558 48,
559 area,
560 )?;
561 error_diffuse(data, pos.saturating_add(width2), depth, error, 5, 48, area)?;
562 error_diffuse(
563 data,
564 pos.saturating_add(width2.saturating_add(1)),
565 depth,
566 error,
567 3,
568 48,
569 area,
570 )?;
571 error_diffuse(
572 data,
573 pos.saturating_add(width2.saturating_add(2)),
574 depth,
575 error,
576 1,
577 48,
578 area,
579 )?;
580 }
581
582 Ok(())
583}
584
585pub fn diffuse_stucki(
593 data: &mut [u8],
594 width: i32,
595 height: i32,
596 x: i32,
597 y: i32,
598 depth: i32,
599 error: i32,
600) -> Result<()> {
601 let pos = y.saturating_mul(width).saturating_add(x);
602
603 if y < height.saturating_sub(2) {
604 let area = width.saturating_mul(height);
605 let width2 = width.saturating_mul(2);
606
607 error_diffuse(data, pos.saturating_add(1), depth, error, 1, 6, area)?;
608 error_diffuse(data, pos.saturating_add(2), depth, error, 1, 12, area)?;
609 error_diffuse(
610 data,
611 pos.saturating_add(width.saturating_sub(2)),
612 depth,
613 error,
614 1,
615 24,
616 area,
617 )?;
618 error_diffuse(
619 data,
620 pos.saturating_add(width.saturating_sub(1)),
621 depth,
622 error,
623 1,
624 12,
625 area,
626 )?;
627 error_diffuse(data, pos.saturating_add(width), depth, error, 1, 6, area)?;
628 error_diffuse(
629 data,
630 pos.saturating_add(width.saturating_add(1)),
631 depth,
632 error,
633 1,
634 12,
635 area,
636 )?;
637 error_diffuse(
638 data,
639 pos.saturating_add(width.saturating_add(2)),
640 depth,
641 error,
642 1,
643 24,
644 area,
645 )?;
646 error_diffuse(
647 data,
648 pos.saturating_add(width2.saturating_sub(2)),
649 depth,
650 error,
651 1,
652 48,
653 area,
654 )?;
655 error_diffuse(
656 data,
657 pos.saturating_add(width2.saturating_sub(1)),
658 depth,
659 error,
660 1,
661 24,
662 area,
663 )?;
664 error_diffuse(data, pos.saturating_add(width2), depth, error, 1, 12, area)?;
665 error_diffuse(
666 data,
667 pos.saturating_add(width2.saturating_add(1)),
668 depth,
669 error,
670 1,
671 24,
672 area,
673 )?;
674 error_diffuse(
675 data,
676 pos.saturating_add(width2.saturating_add(2)),
677 depth,
678 error,
679 1,
680 48,
681 area,
682 )?;
683 }
684
685 Ok(())
686}
687
688pub fn diffuse_burkes(
695 data: &mut [u8],
696 width: i32,
697 height: i32,
698 x: i32,
699 y: i32,
700 depth: i32,
701 error: i32,
702) -> Result<()> {
703 let pos = y.saturating_mul(width).saturating_add(x);
704
705 if y < height.saturating_sub(2) {
706 let area = width.saturating_mul(height);
707
708 error_diffuse(data, pos.saturating_add(1), depth, error, 1, 4, area)?;
709 error_diffuse(data, pos.saturating_add(2), depth, error, 1, 8, area)?;
710 error_diffuse(
711 data,
712 pos.saturating_add(width.saturating_sub(2)),
713 depth,
714 error,
715 1,
716 16,
717 area,
718 )?;
719 error_diffuse(
720 data,
721 pos.saturating_add(width.saturating_sub(1)),
722 depth,
723 error,
724 1,
725 8,
726 area,
727 )?;
728 error_diffuse(data, pos.saturating_add(width), depth, error, 1, 4, area)?;
729 error_diffuse(
730 data,
731 pos.saturating_add(width.saturating_add(1)),
732 depth,
733 error,
734 1,
735 8,
736 area,
737 )?;
738 error_diffuse(
739 data,
740 pos.saturating_add(width.saturating_add(2)),
741 depth,
742 error,
743 1,
744 16,
745 area,
746 )?;
747 }
748
749 Ok(())
750}
751
752type MaskFn = fn(i32, i32, i32) -> f32;
753
754fn mask_a(x: i32, y: i32, c: i32) -> f32 {
755 let x_mul = x.saturating_add(c.saturating_mul(67));
756 let y_mul = y.saturating_mul(236);
757
758 ((x_mul.saturating_add(y_mul).saturating_mul(119) & 255) as f32) / 128f32 - 1f32
759}
760
761fn mask_x(x: i32, y: i32, c: i32) -> f32 {
762 let x_mul = x.saturating_add(c.saturating_mul(29));
763 let y_mul = y.saturating_mul(149);
764
765 ((x_mul.saturating_add(y_mul).saturating_mul(1234) & 511) as f32) / 256f32 - 1f32
766}
767
768fn mask_none(_x: i32, _y: i32, _c: i32) -> f32 {
769 0.0
770}
771
772type LookupFn = fn(&[u8], usize, &[u8], usize, &mut [u16], i32) -> Option<usize>;
773
774pub fn lookup_none(
776 _pixel: &[u8],
777 _depth: usize,
778 _palette: &[u8],
779 _req_color: usize,
780 _cache_table: &mut [u16],
781 _complexion: i32,
782) -> Option<usize> {
783 None
784}
785
786pub fn lookup_normal(
788 pixel: &[u8],
789 depth: usize,
790 palette: &[u8],
791 req_color: usize,
792 _cache_table: &mut [u16],
793 complexion: i32,
794) -> Option<usize> {
795 let max_palette = req_color.saturating_mul(depth).saturating_add(depth);
796
797 if palette.len() < max_palette || pixel.len() < depth || max_palette == usize::MAX {
798 None
799 } else {
800 let mut result = None;
801 let mut diff = i32::MAX;
802
803 for i in 0..req_color {
804 let r = pixel[0].saturating_sub(palette[i * depth]) as i32;
805
806 let mut distant = r.saturating_mul(r).saturating_mul(complexion);
807
808 for n in 1..depth {
809 let r = pixel[n].saturating_sub(palette[i * depth + n]) as i32;
810
811 distant = distant.saturating_add(r.saturating_mul(r));
812 }
813
814 if distant < diff {
815 diff = distant;
816 result = Some(i);
817 }
818 }
819
820 result
821 }
822}
823
824pub fn lookup_fast(
826 pixel: &[u8],
827 _depth: usize,
828 palette: &[u8],
829 req_color: usize,
830 cache_table: &mut [u16],
831 complexion: i32,
832) -> Option<usize> {
833 let depth = 3;
834 let max_palette = req_color.saturating_mul(depth).saturating_add(depth);
835
836 if palette.len() < max_palette || pixel.len() < depth {
837 None
838 } else {
839 let hash = compute_hash(pixel, depth);
840 let cache = cache_table.get(hash).unwrap_or(&0);
841
842 if *cache > 0 {
843 Some((cache - 1) as usize)
845 } else {
846 let mut result = None;
847 let mut diff = i32::MAX;
848
849 for i in 0..req_color {
850 let p0 = (pixel[0] as i32)
852 .saturating_sub(palette[i * depth] as i32)
853 .saturating_mul(pixel[0].saturating_sub(palette[i * depth]) as i32)
854 .saturating_mul(complexion);
855
856 let p1 = (pixel[1] as i32)
857 .saturating_sub(palette[i * depth + 1] as i32)
858 .saturating_mul(pixel[1].saturating_sub(palette[i * depth + 1]) as i32);
859
860 let p2 = (pixel[2] as i32)
861 .saturating_sub(palette[i * depth + 1] as i32)
862 .saturating_mul(pixel[2].saturating_sub(palette[i * depth + 2]) as i32);
863
864 let distant = p0.saturating_add(p1).saturating_add(p2);
865
866 if distant < diff {
867 diff = distant;
868 result = Some(i);
869 }
870 }
871
872 if let Some(r) = result {
873 cache_table[hash] = (r + 1) as u16;
874 }
875
876 result
877 }
878 }
879}
880
881pub fn lookup_mono_darkbg(
883 pixel: &[u8],
884 depth: usize,
885 _palette: &[u8],
886 req_color: usize,
887 _cache_table: &mut [u16],
888 _complexion: i32,
889) -> Option<usize> {
890 let distant: usize = pixel[..depth].iter().map(|&p| p as usize).sum();
891
892 if distant >= req_color.saturating_mul(128) {
893 Some(1)
894 } else {
895 Some(0)
896 }
897}
898
899pub fn lookup_mono_lightbg(
901 pixel: &[u8],
902 depth: usize,
903 _palette: &[u8],
904 req_color: usize,
905 _cache_table: &mut [u16],
906 _complexion: i32,
907) -> Option<usize> {
908 let distant: usize = pixel[..depth].iter().map(|&p| p as usize).sum();
909
910 if distant < req_color.saturating_mul(128) {
911 Some(1)
912 } else {
913 Some(0)
914 }
915}
916
917impl SixelDither {
918 const MAX_DEPTH: usize = 4;
919
920 pub fn make_palette(&mut self, data: &[u8]) -> Result<Vec<u8>> {
922 let depth = self.pixel_format.compute_depth();
923
924 let color_map = ColorMap::compute_from_input(
925 data,
926 depth,
927 self.req_colors,
928 self.method_for_largest,
929 self.method_for_rep,
930 self.quality_mode,
931 &mut self.orig_colors,
932 )?;
933
934 self.ncolors = color_map.table.len();
935
936 let mut result: Vec<u8> = Vec::with_capacity(self.ncolors.wrapping_mul(depth));
937
938 let colors = self.ncolors;
939 for t in color_map.table[..colors].iter() {
940 for &tuple in t.tuple[..depth].iter() {
941 result.push(tuple as u8);
942 }
943 }
944
945 Ok(result)
946 }
947
948 pub fn quant_apply_palette(&mut self, data: &mut [u8]) -> Result<Vec<u8>> {
950 let (height, width, depth) = (self.height, self.width, self.depth);
951
952 if self.req_colors < 1 {
953 Err(Error::Quant("apply_palette: req_color == 0".into()))
954 } else if depth > Self::MAX_DEPTH {
955 let max = Self::MAX_DEPTH;
956
957 Err(Error::Quant(format!(
958 "apply_palette: depth is out-of-bounds: {depth}, max: {max}"
959 )))
960 } else {
961 let mut result = vec![0u8; width * height * depth];
962
963 let (f_mask, f_diffuse): (MaskFn, DiffuseFn) = match self.method_for_diffuse {
964 MethodForDiffuse::None => (mask_none, diffuse_none),
965 MethodForDiffuse::Atkinson => (mask_none, diffuse_atkinson),
966 MethodForDiffuse::Fs => (mask_none, diffuse_fs),
967 MethodForDiffuse::Jajuni => (mask_none, diffuse_jajuni),
968 MethodForDiffuse::Stucki => (mask_none, diffuse_stucki),
969 MethodForDiffuse::Burkes => (mask_none, diffuse_burkes),
970 MethodForDiffuse::ADither => (mask_a, diffuse_none),
971 MethodForDiffuse::XDither => (mask_x, diffuse_none),
972 MethodForDiffuse::Auto => {
973 return Err(Error::Quant(
974 "apply_palette: invalid method_for_diffuse(Auto)".into(),
975 ))
976 }
977 };
978
979 let (f_lookup, lookup_fn): (LookupFn, LookupFunction) = if self.req_colors == 2 {
980 let sum1: u32 = self.palette[..depth].iter().map(|&p| u32::from(p)).sum();
981 let sum2: u32 = self.palette[..depth + depth]
982 .iter()
983 .map(|&p| u32::from(p))
984 .sum();
985
986 if sum1 == 0 && sum2 == 255 * 3 {
987 (lookup_mono_darkbg, LookupFunction::MonoDarkBg)
988 } else if sum1 == 255 * 3 && sum2 == 0 {
989 (lookup_mono_lightbg, LookupFunction::MonoLightBg)
990 } else if self.optimized && depth == 3 {
991 (lookup_fast, LookupFunction::Fast)
992 } else {
993 (lookup_normal, LookupFunction::Normal)
994 }
995 } else if self.optimized && depth == 3 {
996 (lookup_fast, LookupFunction::Fast)
997 } else {
998 (lookup_normal, LookupFunction::Normal)
999 };
1000
1001 let mut index_table = self.cache_table.clone();
1002 if index_table.is_empty() && lookup_fn == LookupFunction::Fast {
1003 index_table.resize(1usize << (depth * 5), 0u16);
1004 }
1005
1006 if self.optimize_palette {
1007 self.ncolors = 0;
1008
1009 let mut new_palette = vec![0u8; PALETTE_MAX * depth];
1010 let mut migration_map = vec![0u16; PALETTE_MAX];
1011
1012 let max_area = height.saturating_mul(width).saturating_add(width);
1013 let max_depth = max_area.saturating_mul(depth).saturating_add(depth);
1014 let data_len = data.len();
1015
1016 if data_len < max_area || data_len < max_depth {
1017 return Err(Error::Quant(format!("apply_palette: max area and/or depth are out-of-bounds: max area: {max_area}, max depth: {max_depth}, data length: {data_len}")));
1018 }
1019
1020 if self.method_for_diffuse.is_mask() {
1021 self.apply_mask_optimize(
1022 data,
1023 new_palette.as_mut(),
1024 result.as_mut(),
1025 migration_map.as_mut(),
1026 index_table.as_mut(),
1027 (f_mask, f_lookup),
1028 )?;
1029 } else {
1030 self.apply_no_mask_optimize(
1031 data,
1032 new_palette.as_mut(),
1033 result.as_mut(),
1034 migration_map.as_mut(),
1035 index_table.as_mut(),
1036 (f_lookup, f_diffuse),
1037 )?;
1038 }
1039 } else {
1040 if self.method_for_diffuse.is_mask() {
1041 self.apply_mask(
1042 data,
1043 result.as_mut(),
1044 index_table.as_mut(),
1045 (f_mask, f_lookup),
1046 )?;
1047 } else {
1048 self.apply_no_mask(
1049 data,
1050 result.as_mut(),
1051 index_table.as_mut(),
1052 (f_lookup, f_diffuse),
1053 )?;
1054 }
1055
1056 self.ncolors = self.req_colors;
1057 }
1058
1059 self.cache_table = index_table;
1060
1061 Ok(result)
1062 }
1063 }
1064
1065 fn apply_mask_optimize(
1066 &mut self,
1067 data: &mut [u8],
1068 new_palette: &mut [u8],
1069 result: &mut [u8],
1070 migration_map: &mut [u16],
1071 index_table: &mut [u16],
1072 apply_fns: (MaskFn, LookupFn),
1073 ) -> Result<()> {
1074 let (height, width, depth) = (self.height, self.width, self.depth);
1075 let (f_mask, f_lookup) = apply_fns;
1076
1077 for y in 0..height {
1078 for x in 0..width {
1079 let mut copy = vec![0u8; Self::MAX_DEPTH];
1080
1081 let pos = y * width + x;
1082 for d in 0..depth {
1083 let val = data[pos * depth + d] as f32
1084 + (f_mask(x as i32, y as i32, d as i32) * 32.0);
1085 copy[d] = if val.is_nan() || val.is_infinite() || val < 0.0 {
1086 0
1087 } else if val > 255.0 {
1088 255
1089 } else {
1090 val as u8
1091 }
1092 }
1093
1094 let color_index = f_lookup(
1095 copy.as_ref(),
1096 depth,
1097 self.palette.as_mut(),
1098 self.req_colors,
1099 index_table,
1100 self.complexion,
1101 )
1102 .unwrap_or(0);
1103
1104 if migration_map[color_index] == 0 {
1105 result[pos] = self.ncolors as u8;
1106 for n in 0..depth {
1107 new_palette[self.ncolors * depth + n] =
1108 self.palette[color_index * depth + n];
1109 }
1110 self.ncolors += 1;
1111 migration_map[color_index] = self.ncolors as u16;
1112 } else {
1113 result[pos] = (migration_map[color_index] - 1) as u8;
1114 }
1115 }
1116 }
1117
1118 let copy_len = self.ncolors * depth;
1119 self.palette[..copy_len].copy_from_slice(new_palette[..copy_len].as_ref());
1120
1121 Ok(())
1122 }
1123
1124 fn apply_no_mask_optimize(
1125 &mut self,
1126 data: &mut [u8],
1127 new_palette: &mut [u8],
1128 result: &mut [u8],
1129 migration_map: &mut [u16],
1130 index_table: &mut [u16],
1131 apply_fns: (LookupFn, DiffuseFn),
1132 ) -> Result<()> {
1133 let (height, width, depth) = (self.height, self.width, self.depth);
1134 let (f_lookup, f_diffuse) = apply_fns;
1135
1136 for y in 0..height {
1137 for x in 0..width {
1138 let pos = y * width + x;
1139 let color_index = f_lookup(
1140 data[pos * depth..].as_mut(),
1141 depth,
1142 self.palette.as_mut(),
1143 self.req_colors,
1144 index_table,
1145 self.complexion,
1146 )
1147 .unwrap_or(0);
1148
1149 if migration_map[color_index] == 0 {
1150 result[pos] = self.ncolors as u8;
1151
1152 for n in 0..depth {
1153 new_palette[self.ncolors * depth + n] =
1154 self.palette[color_index * depth + n];
1155 }
1156 self.ncolors += 1;
1157 migration_map[color_index] = self.ncolors as u16;
1158 } else {
1159 result[pos] = (migration_map[color_index] - 1) as u8;
1160 }
1161
1162 for n in 0..depth {
1163 let offset =
1164 data[pos * depth + n].saturating_sub(self.palette[color_index * depth + n]);
1165
1166 f_diffuse(
1167 data[n..].as_mut(),
1168 width as i32,
1169 height as i32,
1170 x as i32,
1171 y as i32,
1172 depth as i32,
1173 offset as i32,
1174 )?;
1175 }
1176 }
1177 }
1178
1179 let copy_len = self.ncolors * depth;
1180 self.palette[..copy_len].copy_from_slice(new_palette[..copy_len].as_ref());
1181
1182 Ok(())
1183 }
1184
1185 fn apply_mask(
1186 &mut self,
1187 data: &mut [u8],
1188 result: &mut [u8],
1189 index_table: &mut [u16],
1190 apply_fns: (MaskFn, LookupFn),
1191 ) -> Result<()> {
1192 let (height, width, depth) = (self.height, self.width, self.depth);
1193 let (f_mask, f_lookup) = apply_fns;
1194
1195 for y in 0..height {
1196 for x in 0..width {
1197 let mut copy = vec![0u8; Self::MAX_DEPTH];
1198 let pos = y * width + x;
1199 for d in 0..depth {
1200 let val =
1201 data[pos * depth + d] as f32 + f_mask(x as i32, y as i32, d as i32) * 32.0;
1202 copy[d] = if val.is_nan() || val.is_infinite() || val < 0.0 {
1203 0
1204 } else if val > 255.0 {
1205 255
1206 } else {
1207 val as u8
1208 };
1209
1210 result[pos] = f_lookup(
1211 copy.as_ref(),
1212 depth,
1213 self.palette.as_mut(),
1214 self.req_colors,
1215 index_table,
1216 self.complexion,
1217 )
1218 .unwrap_or(0) as u8;
1219 }
1220 }
1221 }
1222
1223 Ok(())
1224 }
1225
1226 fn apply_no_mask(
1227 &mut self,
1228 data: &mut [u8],
1229 result: &mut [u8],
1230 index_table: &mut [u16],
1231 apply_fns: (LookupFn, DiffuseFn),
1232 ) -> Result<()> {
1233 let (height, width, depth) = (self.height, self.width, self.depth);
1234 let (f_lookup, f_diffuse) = apply_fns;
1235
1236 for y in 0..height {
1237 for x in 0..width {
1238 let pos = y * width + x;
1239 let color_index = f_lookup(
1240 data[pos * depth..].as_ref(),
1241 depth,
1242 self.palette.as_mut(),
1243 self.req_colors,
1244 index_table,
1245 self.complexion,
1246 )
1247 .unwrap_or(0);
1248
1249 result[pos] = color_index as u8;
1250
1251 for n in 0..depth {
1252 let offset =
1253 data[pos * depth + n].saturating_sub(self.palette[color_index * depth + n]);
1254
1255 f_diffuse(
1256 data[n..].as_mut(),
1257 width as i32,
1258 height as i32,
1259 x as i32,
1260 y as i32,
1261 depth as i32,
1262 offset as i32,
1263 )?;
1264 }
1265 }
1266 }
1267
1268 Ok(())
1269 }
1270}