1use crate::OrdFloat;
2use arrayvec::ArrayVec;
3use rgb::prelude::*;
4use std::ops::{Deref, DerefMut};
5
6pub type RGBA = rgb::Rgba<u8>;
8
9#[allow(clippy::upper_case_acronyms)]
10pub type ARGBF = rgb::Argb<f32>;
11
12pub const INTERNAL_GAMMA: f64 = 0.57;
13pub const LIQ_WEIGHT_A: f32 = 0.625;
14pub const LIQ_WEIGHT_R: f32 = 0.5;
15pub const LIQ_WEIGHT_G: f32 = 1.;
16pub const LIQ_WEIGHT_B: f32 = 0.45;
17
18pub const LIQ_WEIGHT_MSE: f64 = 0.45;
20
21pub const MIN_OPAQUE_A: f32 = 1. / 256. * LIQ_WEIGHT_A;
22pub const MAX_TRANSP_A: f32 = 255. / 256. * LIQ_WEIGHT_A;
23
24#[cfg_attr(
29 any(target_arch = "x86_64", all(target_feature = "neon", target_arch = "aarch64")),
30 repr(C, align(16))
31)]
32#[derive(Debug, Copy, Clone, Default, PartialEq)]
33#[allow(non_camel_case_types)]
34pub struct f_pixel(pub ARGBF);
35
36impl f_pixel {
37 #[cfg(not(any(target_arch = "x86_64", all(target_feature = "neon", target_arch = "aarch64"))))]
38 #[inline(always)]
39 pub fn diff(&self, other: &f_pixel) -> f32 {
40 let alphas = other.0.a - self.0.a;
41 let black = self.0 - other.0;
42 let white = ARGBF {
43 a: 0.,
44 r: black.r + alphas,
45 g: black.g + alphas,
46 b: black.b + alphas,
47 };
48 (black.r * black.r).max(white.r * white.r) +
49 (black.g * black.g).max(white.g * white.g) +
50 (black.b * black.b).max(white.b * white.b)
51 }
52
53 #[cfg(all(target_feature = "neon", target_arch = "aarch64"))]
54 #[inline(always)]
55 pub fn diff(&self, other: &Self) -> f32 {
56 unsafe {
57 use std::arch::aarch64::*;
58
59 let px = vld1q_f32((self as *const Self).cast::<f32>());
60 let py = vld1q_f32((other as *const Self).cast::<f32>());
61
62 let mut alphas = vsubq_f32(py, px);
64 alphas = vdupq_laneq_f32(alphas, 0); let mut onblack = vsubq_f32(px, py); let mut onwhite = vaddq_f32(onblack, alphas); onblack = vmulq_f32(onblack, onblack);
70 onwhite = vmulq_f32(onwhite, onwhite);
71
72 let max = vmaxq_f32(onwhite, onblack);
73
74 let mut max_r = [0.; 4];
75 vst1q_f32(max_r.as_mut_ptr(), max);
76
77 let mut max_gb = [0.; 4];
78 vst1q_f32(max_gb.as_mut_ptr(), vpaddq_f32(max, max));
79
80 max_r[1] + max_gb[1]
83 }
84 }
85
86 #[cfg(target_arch = "x86_64")]
87 #[inline(always)]
88 pub fn diff(&self, other: &f_pixel) -> f32 {
89 unsafe {
90 use std::arch::x86_64::*;
91
92 let px = _mm_loadu_ps(self as *const f_pixel as *const f32);
93 let py = _mm_loadu_ps(other as *const f_pixel as *const f32);
94
95 let mut alphas = _mm_sub_ss(py, px);
97 alphas = _mm_shuffle_ps(alphas, alphas, 0); let mut onblack = _mm_sub_ps(px, py); let mut onwhite = _mm_add_ps(onblack, alphas); onblack = _mm_mul_ps(onblack, onblack);
103 onwhite = _mm_mul_ps(onwhite, onwhite);
104 let max = _mm_max_ps(onwhite, onblack);
105
106 let mut tmp = [0.; 4];
108 _mm_storeu_ps(tmp.as_mut_ptr(), max);
109
110 let res = tmp[1] + tmp[2] + tmp[3];
112 res
113 }
114 }
115
116 #[inline]
117 pub(crate) fn to_rgb(self, gamma: f64) -> RGBA {
118 if self.a < MIN_OPAQUE_A {
119 return RGBA::new(0, 0, 0, 0);
120 }
121
122 let r = (LIQ_WEIGHT_A / LIQ_WEIGHT_R) * self.r / self.a;
123 let g = (LIQ_WEIGHT_A / LIQ_WEIGHT_G) * self.g / self.a;
124 let b = (LIQ_WEIGHT_A / LIQ_WEIGHT_B) * self.b / self.a;
125 let a = (256. / LIQ_WEIGHT_A) * self.a;
126
127 let gamma = (gamma / INTERNAL_GAMMA) as f32;
128 debug_assert!(gamma.is_finite());
129
130 RGBA {
132 r: (r.powf(gamma) * 256.) as u8,
133 g: (g.powf(gamma) * 256.) as u8,
134 b: (b.powf(gamma) * 256.) as u8,
135 a: a as u8,
136 }
137 }
138
139 pub fn from_rgba(gamma_lut: &[f32; 256], px: RGBA) -> Self {
140 let a = f32::from(px.a) / 255.;
141 Self(ARGBF {
142 a: a * LIQ_WEIGHT_A,
143 r: gamma_lut[px.r as usize] * LIQ_WEIGHT_R * a,
144 g: gamma_lut[px.g as usize] * LIQ_WEIGHT_G * a,
145 b: gamma_lut[px.b as usize] * LIQ_WEIGHT_B * a,
146 })
147 }
148}
149
150impl Deref for f_pixel {
151 type Target = ARGBF;
152
153 #[inline(always)]
154 fn deref(&self) -> &Self::Target {
155 &self.0
156 }
157}
158
159impl DerefMut for f_pixel {
160 #[inline(always)]
161 fn deref_mut(&mut self) -> &mut Self::Target {
162 &mut self.0
163 }
164}
165
166impl From<ARGBF> for f_pixel {
167 #[inline(always)]
168 fn from(x: ARGBF) -> Self {
169 Self(x)
170 }
171}
172
173#[derive(Copy, Clone, Debug)]
175pub(crate) struct PalPop(f32);
176
177impl PalPop {
178 #[inline(always)]
179 pub fn is_fixed(self) -> bool {
180 self.0 < 0.
181 }
182
183 #[must_use]
184 pub fn to_fixed(self) -> Self {
185 if self.0 < 0. {
186 return self;
187 }
188 Self(if self.0 > 0. { -self.0 } else { -1. })
189 }
190
191 #[inline]
192 #[cfg_attr(debug_assertions, track_caller)]
193 pub fn new(popularity: f32) -> Self {
194 debug_assert!(popularity >= 0.);
195 Self(popularity)
196 }
197
198 #[inline(always)]
199 #[must_use]
200 pub fn popularity(self) -> f32 {
201 self.0.abs()
202 }
203}
204
205#[cfg(feature = "large_palettes")]
206pub type PalIndex = u16;
207
208#[cfg(not(feature = "large_palettes"))]
209pub type PalIndex = u8;
210
211pub type PalIndexRemap = u8;
213pub type PalLen = u16;
214
215pub(crate) const MAX_COLORS: usize = if PalIndex::MAX == 255 { 256 } else { 2048 };
217
218#[derive(Clone)]
220pub(crate) struct PalF {
221 colors: ArrayVec<f_pixel, MAX_COLORS>,
222 pops: ArrayVec<PalPop, MAX_COLORS>,
223}
224
225impl PalF {
226 #[inline]
227 pub fn new() -> Self {
228 debug_assert!(PalIndex::MAX as usize + 1 >= MAX_COLORS);
229 debug_assert!(PalLen::MAX as usize >= MAX_COLORS);
230 Self {
231 colors: ArrayVec::default(),
232 pops: ArrayVec::default(),
233 }
234 }
235
236 #[inline(always)]
237 pub fn push(&mut self, color: f_pixel, popularity: PalPop) {
238 self.pops.push(popularity);
239 self.colors.push(color);
240 }
241
242 pub fn set(&mut self, idx: usize, color: f_pixel, popularity: PalPop) {
243 debug_assert!(idx < self.colors.len() && idx < self.pops.len());
244
245 if let Some(pops_idx) = self.pops.get_mut(idx) {
246 *pops_idx = popularity;
247 }
248 if let Some(colors_idx) = self.colors.get_mut(idx) {
249 *colors_idx = color;
250 }
251 }
252
253 #[inline(always)]
254 pub fn as_slice(&self) -> &[f_pixel] {
255 &self.colors
256 }
257
258 #[inline(always)]
259 pub fn pop_as_slice(&self) -> &[PalPop] {
260 &self.pops
261 }
262
263 pub(crate) fn with_fixed_colors(mut self, max_colors: PalLen, fixed_colors: &[f_pixel]) -> Self {
265 if fixed_colors.is_empty() {
266 return self;
267 }
268
269 let max_fixed_colors = fixed_colors.len().min(max_colors as usize);
271 if self.len() < max_fixed_colors {
272 let needs_extra = max_fixed_colors - self.len();
273 self.colors.extend(fixed_colors.iter().copied().take(needs_extra));
274 self.pops.extend(std::iter::repeat(PalPop::new(0.)).take(needs_extra));
275 debug_assert_eq!(self.len(), max_fixed_colors);
276 }
277
278 for (i, fixed_color) in fixed_colors.iter().enumerate().take(self.len()) {
281 let (best_idx, _) = self.colors.iter().enumerate().skip(i).min_by_key(|(_, pal_color)| {
282 OrdFloat::new(pal_color.diff(fixed_color))
284 }).expect("logic bug in fixed colors, please report a bug");
285 debug_assert!(best_idx >= i);
286 self.swap(i, best_idx);
287 self.set(i, *fixed_color, self.pops[i].to_fixed());
288 }
289
290 debug_assert!(self.colors.iter().zip(fixed_colors).all(|(p, f)| p == f));
291 debug_assert!(self.pops.iter().take(fixed_colors.len()).all(|pop| pop.is_fixed()));
292 self
293 }
294
295 #[inline(always)]
296 pub(crate) fn len(&self) -> usize {
297 debug_assert_eq!(self.colors.len(), self.pops.len());
298 self.colors.len()
299 }
300
301 #[inline(always)]
302 pub fn iter_mut(&mut self) -> impl Iterator<Item = (&mut f_pixel, &mut PalPop)> {
303 let c = &mut self.colors[..];
304 let pop = &mut self.pops[..c.len()];
305 c.iter_mut().zip(pop)
306 }
307
308 #[cfg_attr(debug_assertions, track_caller)]
309 pub(crate) fn swap(&mut self, a: usize, b: usize) {
310 self.colors.swap(a, b);
311 self.pops.swap(a, b);
312 }
313
314 pub(crate) fn init_int_palette(&mut self, int_palette: &mut Palette, gamma: f64, posterize: u8) {
316 let lut = gamma_lut(gamma);
317 for ((f_color, f_pop), int_pal) in self.iter_mut().zip(&mut int_palette.entries) {
318 let mut px = f_color.to_rgb(gamma)
319 .map(move |c| posterize_channel(c, posterize));
320 *f_color = f_pixel::from_rgba(&lut, px);
321 if px.a == 0 && !f_pop.is_fixed() {
322 px.r = 71u8;
323 px.g = 112u8;
324 px.b = 76u8;
325 }
326 *int_pal = px;
327 }
328 int_palette.count = self.len() as _;
329 }
330}
331
332#[inline]
333const fn posterize_channel(color: u8, bits: u8) -> u8 {
334 if bits == 0 {
335 color
336 } else {
337 (color & !((1 << bits) - 1)) | (color >> (8 - bits))
338 }
339}
340
341#[inline(always)]
342pub fn gamma_lut(gamma: f64) -> [f32; 256] {
343 debug_assert!(gamma > 0.);
344 let mut tmp = [0.; 256];
345 for (i, t) in tmp.iter_mut().enumerate() {
346 *t = ((i as f32) / 255.).powf((INTERNAL_GAMMA / gamma) as f32);
347 }
348 tmp
349}
350
351#[repr(C)]
354#[derive(Clone)]
355pub struct Palette {
356 pub count: std::os::raw::c_uint,
358 pub entries: [RGBA; MAX_COLORS],
360}
361
362impl std::ops::Deref for Palette {
363 type Target = [RGBA];
364
365 #[inline(always)]
366 fn deref(&self) -> &Self::Target {
367 self.as_slice()
368 }
369}
370
371impl std::ops::DerefMut for Palette {
372 #[inline(always)]
373 fn deref_mut(&mut self) -> &mut Self::Target {
374 self.as_mut_slice()
375 }
376}
377
378impl Palette {
379 #[inline(always)]
381 #[must_use]
382 pub fn as_slice(&self) -> &[RGBA] {
383 &self.entries[..self.count as usize]
384 }
385
386 #[inline(always)]
387 pub(crate) fn as_mut_slice(&mut self) -> &mut [RGBA] {
388 &mut self.entries[..self.count as usize]
389 }
390}
391
392#[test]
393fn diff_test() {
394 let a = f_pixel(ARGBF {a: 1., r: 0.2, g: 0.3, b: 0.5});
395 let b = f_pixel(ARGBF {a: 1., r: 0.3, g: 0.3, b: 0.5});
396 let c = f_pixel(ARGBF {a: 1., r: 1., g: 0.3, b: 0.5});
397 let d = f_pixel(ARGBF {a: 0., r: 1., g: 0.3, b: 0.5});
398 assert!(a.diff(&b) < b.diff(&c));
399 assert!(c.diff(&b) < c.diff(&d));
400
401 let a = f_pixel(ARGBF {a: 1., b: 0.2, r: 0.3, g: 0.5});
402 let b = f_pixel(ARGBF {a: 1., b: 0.3, r: 0.3, g: 0.5});
403 let c = f_pixel(ARGBF {a: 1., b: 1., r: 0.3, g: 0.5});
404 let d = f_pixel(ARGBF {a: 0., b: 1., r: 0.3, g: 0.5});
405 assert!(a.diff(&b) < b.diff(&c));
406 assert!(c.diff(&b) < c.diff(&d));
407
408 let a = f_pixel(ARGBF {a: 1., g: 0.2, b: 0.3, r: 0.5});
409 let b = f_pixel(ARGBF {a: 1., g: 0.3, b: 0.3, r: 0.5});
410 let c = f_pixel(ARGBF {a: 1., g: 1., b: 0.3, r: 0.5});
411 let d = f_pixel(ARGBF {a: 0., g: 1., b: 0.3, r: 0.5});
412 assert!(a.diff(&b) < b.diff(&c));
413 assert!(c.diff(&b) < c.diff(&d));
414}
415
416#[test]
417fn pal_test() {
418 let mut p = PalF::new();
419 let gamma = gamma_lut(0.45455);
420 for i in 0..=255u8 {
421 let rgba = RGBA::new(i, i, i, 100 + i / 2);
422 p.push(f_pixel::from_rgba(&gamma, rgba), PalPop::new(1.));
423 assert_eq!(i as usize + 1, p.len());
424 assert_eq!(i as usize + 1, p.pop_as_slice().len());
425 assert_eq!(i as usize + 1, p.as_slice().len());
426 assert_eq!(i as usize + 1, p.colors.len());
427 assert_eq!(i as usize + 1, p.pops.len());
428 assert_eq!(i as usize + 1, p.iter_mut().count());
429 }
430
431 let mut int_pal = Palette {
432 count: 0,
433 entries: [RGBA::default(); MAX_COLORS],
434 };
435 p.init_int_palette(&mut int_pal, 0.45455, 0);
436
437 for i in 0..=255u8 {
438 let rgba = p.as_slice()[i as usize].to_rgb(0.45455);
439 assert_eq!(rgba, RGBA::new(i, i, i, 100 + i / 2));
440 assert_eq!(int_pal[i as usize], RGBA::new(i, i, i, 100 + i / 2));
441 }
442}
443
444#[test]
445#[cfg(feature = "large_palettes")]
446fn largepal() {
447 let gamma = gamma_lut(0.5);
448 let mut p = PalF::new();
449 for i in 0..1000 {
450 let rgba = RGBA::new(i as u8, (i/2) as u8, (i/4) as u8, 255);
451 p.push(f_pixel::from_rgba(&gamma, rgba), PalPop::new(1.));
452 }
453}