(hist: &mut HistogramInternal, palette: &mut PalF, adjust_weight: bool)
| 52 | |
| 53 | #[inline(never)] |
| 54 | pub(crate) fn iteration(hist: &mut HistogramInternal, palette: &mut PalF, adjust_weight: bool) -> Result<f64, Error> { |
| 55 | if hist.items.is_empty() { |
| 56 | return Ok(0.); |
| 57 | } |
| 58 | |
| 59 | let n = Nearest::new(palette)?; |
| 60 | let colors = palette.as_slice(); |
| 61 | let len = colors.len(); |
| 62 | |
| 63 | let tls = ThreadLocal::new(); |
| 64 | let total = hist.total_perceptual_weight; |
| 65 | |
| 66 | // chunk size is a trade-off between parallelization and overhead |
| 67 | hist.items.par_chunks_mut(256).for_each({ |
| 68 | let tls = &tls; |
| 69 | move |batch| { |
| 70 | let kmeans = tls.get_or(move || CacheLineAlign(RefCell::new(Self::new(len)))); |
| 71 | if let Ok(ref mut kmeans) = *kmeans.0.borrow_mut() { |
| 72 | kmeans.iterate_batch(batch, &n, colors, adjust_weight); |
| 73 | } |
| 74 | } |
| 75 | }); |
| 76 | |
| 77 | let diff = tls.into_iter() |
| 78 | .map(|c| c.0.into_inner()) |
| 79 | .reduce(Self::try_merge) |
| 80 | .transpose()? |
| 81 | .map_or(0., |kmeans| kmeans.finalize(palette) / total); |
| 82 | |
| 83 | replace_unused_colors(palette, hist)?; |
| 84 | Ok(diff) |
| 85 | } |
| 86 | |
| 87 | fn iterate_batch(&mut self, batch: &mut [HistItem], n: &Nearest, colors: &[f_pixel], adjust_weight: bool) { |
| 88 | self.weighed_diff_sum += batch.iter_mut().map(|item| { |
nothing calls this directly
no test coverage detected