mirror of
https://github.com/GoogleChromeLabs/squoosh.git
synced 2025-11-11 16:26:20 +00:00
122 lines
3.6 KiB
Rust
122 lines
3.6 KiB
Rust
extern crate cfg_if;
|
||
extern crate resize;
|
||
extern crate wasm_bindgen;
|
||
|
||
mod utils;
|
||
|
||
use cfg_if::cfg_if;
|
||
use resize::Pixel::RGBA;
|
||
use resize::Type;
|
||
use wasm_bindgen::prelude::*;
|
||
|
||
mod srgb;
|
||
use srgb::Clamp;
|
||
|
||
cfg_if! {
|
||
// When the `wee_alloc` feature is enabled, use `wee_alloc` as the global
|
||
// allocator.
|
||
if #[cfg(feature = "wee_alloc")] {
|
||
extern crate wee_alloc;
|
||
#[global_allocator]
|
||
static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
|
||
}
|
||
}
|
||
|
||
include!("./lut.inc");
|
||
|
||
// If `with_space_conversion` is true, this function returns 2 functions that
|
||
// convert from sRGB to linear RGB and vice versa. If `with_space_conversion` is
|
||
// false, the 2 functions returned do nothing.
|
||
fn converter_funcs(with_space_conversion: bool) -> ((fn(u8) -> f32), (fn(f32) -> u8)) {
|
||
if with_space_conversion {
|
||
(
|
||
|v| SRGB_TO_LINEAR_LUT[v as usize] * 255.0,
|
||
|v| (LINEAR_TO_SRGB_LUT[v as usize] * 255.0) as u8,
|
||
)
|
||
} else {
|
||
(|v| v as f32, |v| v as u8)
|
||
}
|
||
}
|
||
|
||
// If `with_alpha_premultiplication` is true, this function returns a function
|
||
// that premultiply the alpha channel with the given channel value and another
|
||
// function that reverses that process. If `with_alpha_premultiplication` is
|
||
// false, the functions just return the channel value.
|
||
fn alpha_multiplier_funcs(
|
||
with_alpha_premultiplication: bool,
|
||
) -> ((fn(f32, u8) -> u8), (fn(u8, u8) -> f32)) {
|
||
if with_alpha_premultiplication {
|
||
(
|
||
|v, a| (v * (a as f32) / 255.0) as u8,
|
||
|v, a| ((v as f32) * 255.0 / (a as f32)).clamp(0.0, 255.0),
|
||
)
|
||
} else {
|
||
(|v, _a| v as u8, |v, _a| v as f32)
|
||
}
|
||
}
|
||
|
||
#[wasm_bindgen]
|
||
#[no_mangle]
|
||
pub fn resize(
|
||
mut input_image: Vec<u8>,
|
||
input_width: usize,
|
||
input_height: usize,
|
||
output_width: usize,
|
||
output_height: usize,
|
||
typ_idx: usize,
|
||
premultiply: bool,
|
||
color_space_conversion: bool,
|
||
) -> Vec<u8> {
|
||
let typ = match typ_idx {
|
||
0 => Type::Triangle,
|
||
1 => Type::Catrom,
|
||
2 => Type::Mitchell,
|
||
3 => Type::Lanczos3,
|
||
_ => panic!("Nope"),
|
||
};
|
||
let num_input_pixels = input_width * input_height;
|
||
let num_output_pixels = output_width * output_height;
|
||
|
||
let (to_linear, to_color_space) = converter_funcs(color_space_conversion);
|
||
let (premultiplier, demultiplier) = alpha_multiplier_funcs(premultiply);
|
||
|
||
// If both options are false, there is no preprocessing on the pixel valus
|
||
// and we can skip the loop.
|
||
if premultiply || color_space_conversion {
|
||
for i in 0..num_input_pixels {
|
||
for j in 0..3 {
|
||
input_image[4 * i + j] =
|
||
premultiplier(to_linear(input_image[4 * i + j]), input_image[4 * i + 3]);
|
||
}
|
||
}
|
||
}
|
||
|
||
let mut resizer = resize::new(
|
||
input_width,
|
||
input_height,
|
||
output_width,
|
||
output_height,
|
||
RGBA,
|
||
typ,
|
||
);
|
||
let mut output_image = Vec::<u8>::with_capacity(num_output_pixels * 4);
|
||
output_image.resize(num_output_pixels * 4, 0);
|
||
resizer.resize(input_image.as_slice(), output_image.as_mut_slice());
|
||
|
||
if premultiply || color_space_conversion {
|
||
for i in 0..num_output_pixels {
|
||
for j in 0..3 {
|
||
// We don’t need to worry about division by zero, as division by zero
|
||
// is well-defined on floats to return ±Inf. ±Inf is converted to 0
|
||
// when casting to integers.
|
||
output_image[4 * i + j] = to_color_space(demultiplier(
|
||
output_image[4 * i + j],
|
||
output_image[4 * i + 3],
|
||
));
|
||
}
|
||
}
|
||
}
|
||
|
||
return output_image;
|
||
}
|