const MASK_2: u32 = 0x0000_FF00;
const MASK_13: u32 = 0x00FF_00FF;
const MASK_ALPHA: u32 = 0xFF00_0000;
#[inline]
#[must_use]
pub(crate) fn interpolate_2(c1: u32, w1: u32, c2: u32, w2: u32, s: u32) -> u32 {
if c1 == c2 {
return c1;
}
let alpha = ((((c1 & MASK_ALPHA) >> 24) * w1 + ((c2 & MASK_ALPHA) >> 24) * w2) << (24 - s))
& MASK_ALPHA;
let green = (((c1 & MASK_2) * w1 + (c2 & MASK_2) * w2) >> s) & MASK_2;
let redblue = (((c1 & MASK_13) * w1 + (c2 & MASK_13) * w2) >> s) & MASK_13;
alpha + green + redblue
}
#[inline]
#[must_use]
pub(crate) fn interpolate_3(c1: u32, w1: u32, c2: u32, w2: u32, c3: u32, w3: u32, s: u32) -> u32 {
let alpha = ((((c1 & MASK_ALPHA) >> 24) * w1
+ ((c2 & MASK_ALPHA) >> 24) * w2
+ ((c3 & MASK_ALPHA) >> 24) * w3)
<< (24 - s))
& MASK_ALPHA;
let green = (((c1 & MASK_2) * w1 + (c2 & MASK_2) * w2 + (c3 & MASK_2) * w3) >> s) & MASK_2;
let redblue =
(((c1 & MASK_13) * w1 + (c2 & MASK_13) * w2 + (c3 & MASK_13) * w3) >> s) & MASK_13;
alpha + green + redblue
}
#[inline]
#[must_use]
pub(crate) fn interp1(c1: u32, c2: u32) -> u32 {
interpolate_2(c1, 3, c2, 1, 2)
}
#[inline]
#[must_use]
pub(crate) fn interp2(c1: u32, c2: u32, c3: u32) -> u32 {
interpolate_3(c1, 2, c2, 1, c3, 1, 2)
}
#[inline]
#[must_use]
pub(crate) fn interp3(c1: u32, c2: u32) -> u32 {
interpolate_2(c1, 7, c2, 1, 3)
}
#[inline]
#[must_use]
pub(crate) fn interp4(c1: u32, c2: u32, c3: u32) -> u32 {
interpolate_3(c1, 2, c2, 7, c3, 7, 4)
}
#[inline]
#[must_use]
pub(crate) fn interp5(c1: u32, c2: u32) -> u32 {
interpolate_2(c1, 1, c2, 1, 1)
}
#[inline]
#[must_use]
pub(crate) fn interp6(c1: u32, c2: u32, c3: u32) -> u32 {
interpolate_3(c1, 5, c2, 2, c3, 1, 3)
}
#[inline]
#[must_use]
pub(crate) fn interp7(c1: u32, c2: u32, c3: u32) -> u32 {
interpolate_3(c1, 6, c2, 1, c3, 1, 3)
}
#[inline]
#[must_use]
pub(crate) fn interp8(c1: u32, c2: u32) -> u32 {
interpolate_2(c1, 5, c2, 3, 3)
}
#[inline]
#[must_use]
pub(crate) fn interp9(c1: u32, c2: u32, c3: u32) -> u32 {
interpolate_3(c1, 2, c2, 3, c3, 3, 3)
}
#[inline]
#[must_use]
pub(crate) fn interp10(c1: u32, c2: u32, c3: u32) -> u32 {
interpolate_3(c1, 14, c2, 1, c3, 1, 4)
}
#[cfg(test)]
mod tests {
use super::*;
const A: u32 = 0xFF11_33DD;
const B: u32 = 0x8844_AA22;
const C: u32 = 0x40FE_01C0;
#[test]
fn named_blends_match_reference() {
assert_eq!(interp1(A, B), 0xE11D_50AE);
assert_eq!(interp2(A, B, C), 0xB159_44A7);
assert_eq!(interp3(A, B), 0xF017_41C5);
assert_eq!(interp4(A, B, C), 0x778F_517E);
assert_eq!(interp5(A, B), 0xC32A_6E7F);
assert_eq!(interp6(A, B, C), 0xC93B_4AAA);
assert_eq!(interp7(A, B, C), 0xD835_3BC2);
assert_eq!(interp8(A, B), 0xD224_5F96);
assert_eq!(interp9(A, B, C), 0x8A7D_4C8C);
assert_eq!(interp10(A, B, C), 0xEB23_37CF);
}
#[test]
fn two_color_blend_returns_input_when_equal() {
assert_eq!(interp1(A, A), A);
assert_eq!(interp5(A, A), A);
assert_eq!(interpolate_2(A, 5, A, 3, 3), A);
}
#[test]
fn three_color_blend_has_no_early_return() {
assert_eq!(interp2(A, A, A), A);
assert_eq!(interpolate_3(A, 2, A, 1, A, 1, 2), A);
}
#[test]
fn blend_weights_follow_documented_ratios() {
let black = 0xFF00_0000;
let white = 0xFFFF_FFFF;
assert_eq!(interp5(black, white), 0xFF7F_7F7F);
assert_eq!(interp1(0xFF00_0000, 0xFF40_4040), 0xFF10_1010);
}
#[test]
fn alpha_blends_separately_from_color() {
let lo = 0x0011_2233;
let hi = 0xFF11_2233;
let r = interpolate_3(lo, 1, hi, 1, hi, 2, 2);
assert_eq!(r >> 24, 0xBF, "alpha lane");
assert_eq!(r & 0x00FF_FFFF, 0x0011_2233, "color lanes unchanged");
}
#[test]
fn disjoint_lanes_recombine_without_carry() {
let cases = [(A, B), (B, C), (C, A), (0x0102_0304, 0xF0E0_D0C0)];
for (c1, c2) in cases {
let blended = interpolate_2(c1, 3, c2, 1, 2);
let alpha = blended & 0xFF00_0000;
let green = blended & 0x0000_FF00;
let redblue = blended & 0x00FF_00FF;
assert_eq!(alpha | green | redblue, blended);
assert_eq!(alpha + green + redblue, blended);
}
}
}