pub trait Pixel {
fn red_f32(&self) -> f32;
fn blue_f32(&self) -> f32;
fn green_f32(&self) -> f32;
fn alpha_f32(&self) -> f32;
fn red_u8(&self) -> u8;
fn green_u8(&self) -> u8;
fn blue_u8(&self) -> u8;
fn alpha_u8(&self) -> u8;
}
pub fn color_f32_to_u32(r: f32, g: f32, b: f32, a: f32) -> u32 {
color_u8_to_u32(r as u8, g as u8, b as u8, a as u8)
}
#[rustfmt::skip]
pub fn color_u8_to_u32(r: u8, g: u8, b: u8, a: u8) -> u32 {
(r as u32) << 24 |
(g as u32) << 16 |
(b as u32) << 8 |
(a as u32) << 0
}
impl Pixel for u32 {
fn red_f32(&self) -> f32 {
self.red_u8() as f32
}
fn green_f32(&self) -> f32 {
self.green_u8() as f32
}
fn blue_f32(&self) -> f32 {
self.blue_u8() as f32
}
fn alpha_f32(&self) -> f32 {
self.alpha_u8() as f32
}
fn red_u8(&self) -> u8 {
(self >> 24) as u8
}
fn green_u8(&self) -> u8 {
(self >> 16) as u8
}
fn blue_u8(&self) -> u8 {
(self >> 8) as u8
}
fn alpha_u8(&self) -> u8 {
(self >> 0) as u8
}
}
pub fn diff<T: Pixel>(pixel_a: T, pixel_b: T) -> f32 {
let alpha_a = pixel_a.alpha_u8();
let alpha_b = pixel_b.alpha_u8();
if alpha_a == 0 && alpha_b == 0 {
return 0.0;
}
if alpha_a == 0 || alpha_b == 0 {
return 1_000_000.0;
}
const Y_WEIGHT: f32 = 48.0;
const U_WEIGHT: f32 = 7.0;
const V_WEIGHT: f32 = 6.0;
let r = (pixel_a.red_f32() - pixel_b.red_f32()).abs();
let b = (pixel_a.blue_f32() - pixel_b.blue_f32()).abs();
let g = (pixel_a.green_f32() - pixel_b.green_f32()).abs();
let yuv = rgb_to_yuv(r, g, b);
let weight = (yuv.y * Y_WEIGHT) + (yuv.u * U_WEIGHT) + (yuv.v * V_WEIGHT);
weight
}
pub struct Yuv {
pub y: f32,
pub u: f32,
pub v: f32,
}
pub fn yuv<T: Pixel>(pixel: T) -> Yuv {
rgb_to_yuv(pixel.red_f32(), pixel.green_f32(), pixel.blue_f32())
}
pub fn rgb_to_yuv(r: f32, g: f32, b: f32) -> Yuv {
let y = r * 0.299000 + g * 0.587000 + b * 0.114000;
let u = r * -0.168736 + g * -0.331264 + b * 0.500000;
let v = r * 0.500000 + g * -0.418688 + b * -0.081312;
Yuv { y, u, v }
}
pub fn is_equal<T: Pixel>(pixel_a: T, pixel_b: T) -> bool {
const THRESHOLD_Y: f32 = 48.0;
const THRESHOLD_U: f32 = 7.0;
const THRESHOLD_V: f32 = 6.0;
let alpha_a = pixel_a.alpha_u8();
let alpha_b = pixel_b.alpha_u8();
if alpha_a == 0 && alpha_b == 0 {
return true;
}
if alpha_a == 0 || alpha_b == 0 {
return false;
}
let yuv_a = yuv(pixel_a);
let yuv_b = yuv(pixel_b);
if (yuv_a.y - yuv_b.y).abs() > THRESHOLD_Y {
return false;
}
if (yuv_a.u - yuv_b.u).abs() > THRESHOLD_U {
return false;
}
if (yuv_a.v - yuv_b.v).abs() > THRESHOLD_V {
return false;
}
return true;
}
pub fn blend<T: Pixel>(pixel_a: T, pixel_b: T, q1: f32, q2: f32) -> u32 {
let dist = q1 + q2;
let one_over_dist = 1.0 / dist;
color_f32_to_u32(
(q1 * pixel_a.red_f32() + q2 * pixel_b.red_f32()) * one_over_dist,
(q1 * pixel_a.green_f32() + q2 * pixel_b.green_f32()) * one_over_dist,
(q1 * pixel_a.blue_f32() + q2 * pixel_b.blue_f32()) * one_over_dist,
(q1 * pixel_a.alpha_f32() + q2 * pixel_b.alpha_f32()) * one_over_dist,
)
}
pub fn blend_64w<T: Pixel>(dst: T, src: T) -> u32 {
blend(dst, src, 3.0, 1.0)
}
pub fn blend_128w<T: Pixel>(dst: T, src: T) -> u32 {
blend(dst, src, 1.0, 1.0)
}
pub fn blend_192w<T: Pixel>(dst: T, src: T) -> u32 {
blend(dst, src, 1.0, 3.0)
}
pub fn left2<T: Pixel + Copy>(n3: T, n2: T, pixel: T) -> [u32; 2] {
[blend_192w(n3, pixel), blend_64w(n2, pixel)]
}
pub fn up2<T: Pixel + Copy>(n3: T, n1: T, pixel: T) -> [u32; 2] {
[blend_192w(n3, pixel), blend_64w(n1, pixel)]
}
pub fn dia<T: Pixel + Copy>(n3: T, pixel: T) -> u32 {
blend_128w(n3, pixel)
}