use crate::hct::cam16::Cam16;
use crate::hct::hct_color::Hct;
use crate::utils::color_utils::Argb;
use crate::utils::math_utils::MathUtils;
pub struct Blend;
impl Blend {
#[must_use]
pub fn harmonize(design_color: Argb, source_color: Argb) -> Argb {
let from_hct = Hct::from_argb(design_color);
let to_hct = Hct::from_argb(source_color);
let difference_degrees = MathUtils::difference_degrees(from_hct.hue(), to_hct.hue());
let rotation_degrees = (difference_degrees * 0.5).min(15.0);
let output_hue = MathUtils::sanitize_degrees_double(
from_hct.hue()
+ rotation_degrees * MathUtils::rotation_direction(from_hct.hue(), to_hct.hue()),
);
Hct::new(output_hue, from_hct.chroma(), from_hct.tone()).to_argb()
}
#[must_use]
pub fn hct_hue(from: Argb, to: Argb, amount: f64) -> Argb {
let ucs = Self::cam16_ucs(from, to, amount);
let ucs_cam = Cam16::from_argb(ucs);
let from_cam = Cam16::from_argb(from);
let blended = Hct::new(ucs_cam.hue, from_cam.chroma, from.lstar());
blended.to_argb()
}
#[must_use]
pub fn cam16_ucs(from: Argb, to: Argb, amount: f64) -> Argb {
let from_cam = Cam16::from_argb(from);
let to_cam = Cam16::from_argb(to);
let jstar = MathUtils::lerp(from_cam.jstar, to_cam.jstar, amount);
let astar = MathUtils::lerp(from_cam.astar, to_cam.astar, amount);
let bstar = MathUtils::lerp(from_cam.bstar, to_cam.bstar, amount);
Cam16::from_ucs(jstar, astar, bstar).to_argb()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_harmonize() {
let design_color = Argb(0xFFFF0000); let source_color = Argb(0xFF0000FF); let harmonized = Blend::harmonize(design_color, source_color);
let from_hct = Hct::from_argb(design_color);
let result_hct = Hct::from_argb(harmonized);
assert!((result_hct.hue() - from_hct.hue()).abs() > 0.0);
assert!((result_hct.tone() - from_hct.tone()).abs() < 1.0);
}
#[test]
fn test_hct_hue() {
let from = Argb(0xFFFF0000); let to = Argb(0xFF00FF00); let blended = Blend::hct_hue(from, to, 0.5);
let from_hct = Hct::from_argb(from);
let result_hct = Hct::from_argb(blended);
assert!((result_hct.hue() - from_hct.hue()).abs() > 0.0);
assert!((result_hct.tone() - from_hct.tone()).abs() < 1.0);
}
#[test]
fn test_cam16_ucs() {
let from = Argb(0xFFFF0000); let to = Argb(0xFF00FF00); let blended = Blend::cam16_ucs(from, to, 0.5);
let from_cam = Cam16::from_argb(from);
let to_cam = Cam16::from_argb(to);
let result_cam = Cam16::from_argb(blended);
assert!(result_cam.jstar > from_cam.jstar.min(to_cam.jstar));
assert!(result_cam.jstar < from_cam.jstar.max(to_cam.jstar));
}
}