use crate::sections::layer_and_mask_information_section::layer::BlendMode;
pub(crate) type Pixel = [u8; 4];
pub(crate) fn apply_opacity(pixel: &mut Pixel, opacity: u8) {
let alpha = opacity as f32 / 255.;
pixel[3] = (pixel[3] as f32 * alpha) as u8;
}
pub(crate) fn blend_pixels(top: Pixel, bottom: Pixel, blend_mode: BlendMode, out: &mut Pixel) {
let alpha_s = top[3] as f32 / 255.;
let alpha_b = bottom[3] as f32 / 255.;
let alpha_output = alpha_s + alpha_b * (1. - alpha_s);
let (r_s, g_s, b_s) = (
top[0] as f32 / 255.,
top[1] as f32 / 255.,
top[2] as f32 / 255.,
);
let (r_b, g_b, b_b) = (
bottom[0] as f32 / 255.,
bottom[1] as f32 / 255.,
bottom[2] as f32 / 255.,
);
let blend_f = map_blend_mode(blend_mode);
let (r, g, b) = (
composite(r_s, alpha_s, r_b, alpha_b, blend_f) * 255.,
composite(g_s, alpha_s, g_b, alpha_b, blend_f) * 255.,
composite(b_s, alpha_s, b_b, alpha_b, blend_f) * 255.,
);
out[0] = (r.round() / alpha_output) as u8;
out[1] = (g.round() / alpha_output) as u8;
out[2] = (b.round() / alpha_output) as u8;
out[3] = (255. * alpha_output).round() as u8;
}
type BlendFunction = dyn Fn(f32, f32) -> f32;
fn map_blend_mode(blend_mode: BlendMode) -> &'static BlendFunction {
match blend_mode {
BlendMode::PassThrough => &pass_through, BlendMode::Normal => &normal,
BlendMode::Dissolve => &dissolve,
BlendMode::Darken => &darken,
BlendMode::Multiply => &multiply,
BlendMode::ColorBurn => &color_burn,
BlendMode::LinearBurn => &linear_burn,
BlendMode::DarkerColor => &darker_color,
BlendMode::Lighten => &lighten,
BlendMode::Screen => &screen,
BlendMode::ColorDodge => &color_dodge,
BlendMode::LinearDodge => &linear_dodge,
BlendMode::LighterColor => &lighter_color,
BlendMode::Overlay => &overlay,
BlendMode::SoftLight => &soft_light,
BlendMode::HardLight => &hard_light,
BlendMode::VividLight => &vivid_light,
BlendMode::LinearLight => &linear_light,
BlendMode::PinLight => &pin_light,
BlendMode::HardMix => &hard_mix,
BlendMode::Difference => &difference,
BlendMode::Exclusion => &exclusion,
BlendMode::Subtract => &subtract,
BlendMode::Divide => ÷,
BlendMode::Hue => &hue,
BlendMode::Saturation => &saturation,
BlendMode::Color => &color,
BlendMode::Luminosity => &luminosity,
}
}
fn pass_through(color_b: f32, color_s: f32) -> f32 {
unimplemented!()
}
#[inline(always)]
fn normal(color_b: f32, color_s: f32) -> f32 {
color_s
}
fn dissolve(color_b: f32, color_s: f32) -> f32 {
unimplemented!()
}
#[inline(always)]
fn darken(color_b: f32, color_s: f32) -> f32 {
color_b.min(color_s)
}
#[inline(always)]
fn multiply(color_b: f32, color_s: f32) -> f32 {
color_b * color_s
}
#[inline(always)]
fn color_burn(color_b: f32, color_s: f32) -> f32 {
if color_b == 1. {
1.
} else {
(1. - (1. - color_s) / color_b).max(0.)
}
}
#[inline(always)]
fn linear_burn(color_b: f32, color_s: f32) -> f32 {
(color_b - color_s - 1.).max(0.)
}
fn darker_color(color_b: f32, color_s: f32) -> f32 {
unimplemented!()
}
#[inline(always)]
fn lighten(color_b: f32, color_s: f32) -> f32 {
color_b.max(color_s)
}
#[inline(always)]
fn screen(color_b: f32, color_s: f32) -> f32 {
color_b + color_s - (color_b * color_s)
}
#[inline(always)]
fn color_dodge(color_b: f32, color_s: f32) -> f32 {
if color_b == 0. {
0.
} else if color_s == 1. {
1.
} else {
(color_b / (1. - color_s)).min(1.)
}
}
#[inline(always)]
fn linear_dodge(color_b: f32, color_s: f32) -> f32 {
(color_b + color_s).min(1.)
}
fn lighter_color(color_b: f32, color_s: f32) -> f32 {
unimplemented!()
}
#[inline(always)]
fn overlay(color_b: f32, color_s: f32) -> f32 {
hard_light(color_s, color_b) }
fn soft_light(color_b: f32, color_s: f32) -> f32 {
let d = if color_b <= 0.25 {
((16. * color_b - 12.) * color_b + 4.) * color_b
} else {
color_b.sqrt()
};
if color_s <= 0.5 {
color_b - (1. - 2. * color_s) * color_b * (1. - color_b)
} else {
color_b + (2. * color_s - 1.) * (d - color_b)
}
}
#[inline(always)]
fn hard_light(color_b: f32, color_s: f32) -> f32 {
if color_s < 0.5 {
multiply(color_b, 2. * color_s)
} else {
screen(color_b, 2. * color_s - 1.)
}
}
fn vivid_light(color_b: f32, color_s: f32) -> f32 {
unimplemented!()
}
fn linear_light(color_b: f32, color_s: f32) -> f32 {
unimplemented!()
}
#[inline(always)]
fn pin_light(color_b: f32, color_s: f32) -> f32 {
unimplemented!()
}
#[inline(always)]
fn hard_mix(color_b: f32, color_s: f32) -> f32 {
unimplemented!()
}
#[inline(always)]
fn difference(color_b: f32, color_s: f32) -> f32 {
(color_b - color_s).abs()
}
#[inline(always)]
fn exclusion(color_b: f32, color_s: f32) -> f32 {
color_b + color_s - 2. * color_b * color_s
}
#[inline(always)]
fn subtract(color_b: f32, color_s: f32) -> f32 {
(color_b - color_s).max(0.)
}
#[inline(always)]
fn divide(color_b: f32, color_s: f32) -> f32 {
if color_s == 0. {
color_b
} else {
color_b / color_s
}
}
fn hue(color_b: f32, color_s: f32) -> f32 {
unimplemented!()
}
fn saturation(color_b: f32, color_s: f32) -> f32 {
unimplemented!()
}
fn color(color_b: f32, color_s: f32) -> f32 {
unimplemented!()
}
fn luminosity(color_b: f32, color_s: f32) -> f32 {
unimplemented!()
}
fn composite(
color_s: f32,
alpha_s: f32,
color_b: f32,
alpha_b: f32,
blend_f: &BlendFunction,
) -> f32 {
let color_s = (1. - alpha_b) * color_s + alpha_b * blend_f(color_b, color_s);
let cs = color_s * alpha_s;
let cb = color_b * alpha_b;
cs + cb * (1. - alpha_s)
}