I couldn't find a suitable API in Rust to optimize and encode PNG data, here are pseudo codes:
#![deny(clippy::all)]
use napi::bindgen_prelude::*;
use napi_derive::napi;
#[napi(object)]
#[derive(Default)]
pub struct Options {
// default is 70
pub min_quality: Option<u32>,
// default is 99
pub max_quality: Option<u32>,
// 1- 10
// Faster speeds generate images of lower quality, but may be useful for real-time generation of images.
// default: 5
pub speed: Option<u32>,
// Number of least significant bits to ignore.
// Useful for generating palettes for VGA, 15-bit textures, or other retro platforms.
pub posterization: Option<u32>,
}
#[napi]
pub fn quantize(input: Buffer, options: Option<Options>) -> Result<Buffer> {
let bitmap = lodepng::decode32(input.as_ref()).map_err(|err| {
Error::new(
Status::InvalidArg,
format!("Decode png from buffer failed{}", err),
)
})?;
let options = options.unwrap_or_default();
let width = bitmap.width;
let height = bitmap.height;
let mut liq = imagequant::new();
liq.set_speed(options.speed.unwrap_or(5) as i32);
liq.set_quality(
options.min_quality.unwrap_or(70) as u8,
options.max_quality.unwrap_or(99) as u8,
);
let mut img = liq
.new_image(
bitmap.buffer.as_slice(),
width as usize,
height as usize,
0.0,
)
.map_err(|err| {
Error::new(
Status::GenericFailure,
format!("Create image failed {}", err),
)
})?;
let mut quantization_result = liq
.quantize(&mut img)
.map_err(|err| Error::new(Status::GenericFailure, format!("quantize failed {}", err)))?;
quantization_result.set_dithering_level(1.0);
let (palette, pixels) = quantization_result
.remapped(&mut img)
.map_err(|err| Error::new(Status::GenericFailure, format!("remap failed {}", err)))?;
let mut encoder = lodepng::Encoder::new();
let info_png = encoder.info_png().clone();
let inner_state = encoder.info_raw_mut();
for p in palette {
inner_state.palette_add(p).map_err(|err| {
Error::new(
Status::GenericFailure,
format!("Add palette failed {}", err),
)
})?;
}
inner_state.set_bitdepth(8);
let mut state = lodepng::State {
info_raw: inner_state.clone(),
info_png,
..Default::default()
};
let mut out_png = std::ptr::null_mut();
let mut outsize = 0usize;
unsafe {
lodepng::ffi::lodepng_encode(
&mut out_png,
&mut outsize,
pixels.as_ptr(),
width as u32,
height as u32,
&mut state,
)
};
println!("{:?}", out_png);
Ok(unsafe { Vec::from_raw_parts(out_png, outsize, outsize) }.into())
}