Skip to content

Commit

Permalink
fix(binding): resize options and jpeg compress implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
Brooooooklyn committed Apr 21, 2022
1 parent 635cad0 commit b23c53b
Show file tree
Hide file tree
Showing 3 changed files with 88 additions and 35 deletions.
4 changes: 2 additions & 2 deletions packages/binding/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,11 @@ image = { version = "0.24", default-features = false, features = [
"openexr",
] }
jpeg-decoder = "0.2"
libavif = { version = "0.10", git = "https://github.com/Brooooooklyn/libavif-rs", branch = "fix-build", default-features = false, features = [
libavif = { version = "0.10", git = "https://github.com/Brooooooklyn/libavif-rs", branch = "wasm-build", default-features = false, features = [
"codec-aom",
] }
libc = "0.2"
libwebp-sys = { version = "0.5", features = ["avx2", "sse41", "neon"] }
libwebp-sys = { version = "0.6", features = ["avx2", "sse41", "neon"] }
lodepng = "3"
napi = { version = "2", default-features = false, features = ["napi3"] }
napi-derive = { version = "2", default-features = false, features = [
Expand Down
100 changes: 75 additions & 25 deletions packages/binding/src/jpeg.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::io::Cursor;

use napi::{bindgen_prelude::*, JsBuffer};
use napi_derive::napi;

Expand All @@ -17,8 +19,30 @@ pub fn compress_jpeg_sync(
input: Buffer,
options: Option<JpegCompressOptions>,
) -> Result<JsBuffer> {
let options = options.unwrap_or_default();
let quality = options.quality.unwrap_or(100);
if quality != 100 {
let img = image::load_from_memory_with_format(input.as_ref(), image::ImageFormat::Jpeg)
.map_err(|err| {
Error::new(
Status::InvalidArg,
format!("Load input jpeg image failed {}", err),
)
})?;
let mut dest = Cursor::new(Vec::with_capacity(input.len()));
let mut encoder = image::codecs::jpeg::JpegEncoder::new_with_quality(&mut dest, quality as u8);
encoder.encode_image(&img).map_err(|err| {
Error::new(
Status::GenericFailure,
format!("Encode image from input jpeg failed {}", err),
)
})?;
return env
.create_buffer_with_data(dest.into_inner())
.map(|b| b.into_raw());
}
let (buf, outsize, de_c_info, compress_c_info) =
unsafe { moz_jpeg_compress(input.as_ref(), &options.unwrap_or_default()) }?;
unsafe { moz_jpeg_compress(input.as_ref(), &options) }?;
unsafe {
env.create_buffer_with_borrowed_data(
buf,
Expand Down Expand Up @@ -63,9 +87,6 @@ unsafe fn moz_jpeg_compress(
mozjpeg_sys::jpeg_read_header(&mut de_c_info, 1);
let src_coef_arrays = mozjpeg_sys::jpeg_read_coefficients(&mut de_c_info);
mozjpeg_sys::jpeg_copy_critical_parameters(&de_c_info, &mut compress_c_info);
if let Some(quality) = opts.quality {
mozjpeg_sys::jpeg_set_quality(&mut compress_c_info, quality as i32, 0);
}
if opts.optimize_scans.unwrap_or(true) {
mozjpeg_sys::jpeg_c_set_bool_param(
&mut compress_c_info,
Expand Down Expand Up @@ -138,40 +159,69 @@ pub struct CompressJpegTask {
input: Buffer,
}

pub enum JpegOptimizeOutput {
Lossless(ThreadsafeMozjpegCompressOutput),
Lossy(Vec<u8>),
}

#[napi]
impl Task for CompressJpegTask {
type Output = ThreadsafeMozjpegCompressOutput;
type Output = JpegOptimizeOutput;
type JsValue = JsBuffer;

fn compute(&mut self) -> Result<Self::Output> {
let quality = self.options.quality.unwrap_or(100);
if quality != 100 {
let img = image::load_from_memory_with_format(self.input.as_ref(), image::ImageFormat::Jpeg)
.map_err(|err| {
Error::new(
Status::InvalidArg,
format!("Load input jpeg image failed {}", err),
)
})?;
let mut dest = Cursor::new(Vec::with_capacity(self.input.len()));
let mut encoder =
image::codecs::jpeg::JpegEncoder::new_with_quality(&mut dest, quality as u8);
encoder.encode_image(&img).map_err(|err| {
Error::new(
Status::GenericFailure,
format!("Encode image from input jpeg failed {}", err),
)
})?;
return Ok(JpegOptimizeOutput::Lossy(dest.into_inner()));
}
unsafe { moz_jpeg_compress(self.input.as_ref(), &self.options) }.map(
|(buf, len, de_c_info, compress_c_info)| ThreadsafeMozjpegCompressOutput {
buf,
len,
de_c_info,
compress_c_info,
|(buf, len, de_c_info, compress_c_info)| {
JpegOptimizeOutput::Lossless(ThreadsafeMozjpegCompressOutput {
buf,
len,
de_c_info,
compress_c_info,
})
},
)
}

fn resolve(&mut self, env: Env, output: Self::Output) -> Result<Self::JsValue> {
let ThreadsafeMozjpegCompressOutput {
buf,
len,
de_c_info,
compress_c_info,
} = output;
unsafe {
env.create_buffer_with_borrowed_data(
match output {
JpegOptimizeOutput::Lossless(ThreadsafeMozjpegCompressOutput {
buf,
len,
(de_c_info, compress_c_info, buf),
|(mut input, mut output, buf), _| {
mozjpeg_sys::jpeg_destroy_decompress(&mut input);
mozjpeg_sys::jpeg_destroy_compress(&mut output);
libc::free(buf as *mut std::ffi::c_void);
},
)
de_c_info,
compress_c_info,
}) => unsafe {
env.create_buffer_with_borrowed_data(
buf,
len,
(de_c_info, compress_c_info, buf),
|(mut input, mut output, buf), _| {
mozjpeg_sys::jpeg_destroy_decompress(&mut input);
mozjpeg_sys::jpeg_destroy_compress(&mut output);
libc::free(buf as *mut std::ffi::c_void);
},
)
},
JpegOptimizeOutput::Lossy(buf) => env.create_buffer_with_data(buf),
}
.map(|v| v.into_raw())
}
Expand Down
19 changes: 11 additions & 8 deletions packages/binding/src/transformer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -354,7 +354,7 @@ struct ImageTransformArgs {
grayscale: bool,
invert: bool,
rotate: bool,
resize: (u32, Option<u32>, ResizeFilterType),
resize: Option<(u32, Option<u32>, ResizeFilterType)>,
contrast: Option<f32>,
blur: Option<f32>,
unsharpen: Option<(f32, i32)>,
Expand Down Expand Up @@ -409,14 +409,15 @@ impl Task for EncodeTask {
let raw_width = meta.image.width();
let raw_height = meta.image.height();
match self.image_transform_args.resize {
(w, Some(h), filter_type) => meta.image = meta.image.resize(w, h, filter_type.into()),
(w, None, filter_type) => {
Some((w, Some(h), filter_type)) => meta.image = meta.image.resize(w, h, filter_type.into()),
Some((w, None, filter_type)) => {
meta.image = meta.image.resize(
w,
((w as f32 / raw_width as f32) * (raw_height as f32)) as u32,
filter_type.into(),
)
}
None => {}
}
if self.image_transform_args.grayscale {
meta.image.grayscale();
Expand Down Expand Up @@ -444,14 +445,16 @@ impl Task for EncodeTask {
}
let dynamic_image = &mut meta.image;
let color_type = &meta.color_type;
let width = dynamic_image.width();
let height = dynamic_image.height();
let format = match self.options {
EncodeOptions::Webp(quality_factor) => {
let (output_buf, size) = unsafe {
crate::webp::encode_webp_inner(
dynamic_image.as_bytes(),
quality_factor,
dynamic_image.width(),
dynamic_image.height(),
width,
height,
color_type,
)
}?;
Expand All @@ -461,8 +464,8 @@ impl Task for EncodeTask {
let (output_buf, size) = unsafe {
crate::webp::lossless_encode_webp_inner(
dynamic_image.as_bytes(),
dynamic_image.width(),
dynamic_image.height(),
width,
height,
color_type,
)
}?;
Expand Down Expand Up @@ -657,7 +660,7 @@ impl Transformer {
height: Option<u32>,
filter_type: Option<ResizeFilterType>,
) -> &Self {
self.image_transform_args.resize = (width, height, filter_type.unwrap_or_default());
self.image_transform_args.resize = Some((width, height, filter_type.unwrap_or_default()));
self
}

Expand Down

0 comments on commit b23c53b

Please sign in to comment.