Skip to content

Commit b23c53b

Browse files
committed
fix(binding): resize options and jpeg compress implementation
1 parent 635cad0 commit b23c53b

File tree

3 files changed

+88
-35
lines changed

3 files changed

+88
-35
lines changed

packages/binding/Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,11 @@ image = { version = "0.24", default-features = false, features = [
2929
"openexr",
3030
] }
3131
jpeg-decoder = "0.2"
32-
libavif = { version = "0.10", git = "https://github.com/Brooooooklyn/libavif-rs", branch = "fix-build", default-features = false, features = [
32+
libavif = { version = "0.10", git = "https://github.com/Brooooooklyn/libavif-rs", branch = "wasm-build", default-features = false, features = [
3333
"codec-aom",
3434
] }
3535
libc = "0.2"
36-
libwebp-sys = { version = "0.5", features = ["avx2", "sse41", "neon"] }
36+
libwebp-sys = { version = "0.6", features = ["avx2", "sse41", "neon"] }
3737
lodepng = "3"
3838
napi = { version = "2", default-features = false, features = ["napi3"] }
3939
napi-derive = { version = "2", default-features = false, features = [

packages/binding/src/jpeg.rs

Lines changed: 75 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use std::io::Cursor;
2+
13
use napi::{bindgen_prelude::*, JsBuffer};
24
use napi_derive::napi;
35

@@ -17,8 +19,30 @@ pub fn compress_jpeg_sync(
1719
input: Buffer,
1820
options: Option<JpegCompressOptions>,
1921
) -> Result<JsBuffer> {
22+
let options = options.unwrap_or_default();
23+
let quality = options.quality.unwrap_or(100);
24+
if quality != 100 {
25+
let img = image::load_from_memory_with_format(input.as_ref(), image::ImageFormat::Jpeg)
26+
.map_err(|err| {
27+
Error::new(
28+
Status::InvalidArg,
29+
format!("Load input jpeg image failed {}", err),
30+
)
31+
})?;
32+
let mut dest = Cursor::new(Vec::with_capacity(input.len()));
33+
let mut encoder = image::codecs::jpeg::JpegEncoder::new_with_quality(&mut dest, quality as u8);
34+
encoder.encode_image(&img).map_err(|err| {
35+
Error::new(
36+
Status::GenericFailure,
37+
format!("Encode image from input jpeg failed {}", err),
38+
)
39+
})?;
40+
return env
41+
.create_buffer_with_data(dest.into_inner())
42+
.map(|b| b.into_raw());
43+
}
2044
let (buf, outsize, de_c_info, compress_c_info) =
21-
unsafe { moz_jpeg_compress(input.as_ref(), &options.unwrap_or_default()) }?;
45+
unsafe { moz_jpeg_compress(input.as_ref(), &options) }?;
2246
unsafe {
2347
env.create_buffer_with_borrowed_data(
2448
buf,
@@ -63,9 +87,6 @@ unsafe fn moz_jpeg_compress(
6387
mozjpeg_sys::jpeg_read_header(&mut de_c_info, 1);
6488
let src_coef_arrays = mozjpeg_sys::jpeg_read_coefficients(&mut de_c_info);
6589
mozjpeg_sys::jpeg_copy_critical_parameters(&de_c_info, &mut compress_c_info);
66-
if let Some(quality) = opts.quality {
67-
mozjpeg_sys::jpeg_set_quality(&mut compress_c_info, quality as i32, 0);
68-
}
6990
if opts.optimize_scans.unwrap_or(true) {
7091
mozjpeg_sys::jpeg_c_set_bool_param(
7192
&mut compress_c_info,
@@ -138,40 +159,69 @@ pub struct CompressJpegTask {
138159
input: Buffer,
139160
}
140161

162+
pub enum JpegOptimizeOutput {
163+
Lossless(ThreadsafeMozjpegCompressOutput),
164+
Lossy(Vec<u8>),
165+
}
166+
141167
#[napi]
142168
impl Task for CompressJpegTask {
143-
type Output = ThreadsafeMozjpegCompressOutput;
169+
type Output = JpegOptimizeOutput;
144170
type JsValue = JsBuffer;
145171

146172
fn compute(&mut self) -> Result<Self::Output> {
173+
let quality = self.options.quality.unwrap_or(100);
174+
if quality != 100 {
175+
let img = image::load_from_memory_with_format(self.input.as_ref(), image::ImageFormat::Jpeg)
176+
.map_err(|err| {
177+
Error::new(
178+
Status::InvalidArg,
179+
format!("Load input jpeg image failed {}", err),
180+
)
181+
})?;
182+
let mut dest = Cursor::new(Vec::with_capacity(self.input.len()));
183+
let mut encoder =
184+
image::codecs::jpeg::JpegEncoder::new_with_quality(&mut dest, quality as u8);
185+
encoder.encode_image(&img).map_err(|err| {
186+
Error::new(
187+
Status::GenericFailure,
188+
format!("Encode image from input jpeg failed {}", err),
189+
)
190+
})?;
191+
return Ok(JpegOptimizeOutput::Lossy(dest.into_inner()));
192+
}
147193
unsafe { moz_jpeg_compress(self.input.as_ref(), &self.options) }.map(
148-
|(buf, len, de_c_info, compress_c_info)| ThreadsafeMozjpegCompressOutput {
149-
buf,
150-
len,
151-
de_c_info,
152-
compress_c_info,
194+
|(buf, len, de_c_info, compress_c_info)| {
195+
JpegOptimizeOutput::Lossless(ThreadsafeMozjpegCompressOutput {
196+
buf,
197+
len,
198+
de_c_info,
199+
compress_c_info,
200+
})
153201
},
154202
)
155203
}
156204

157205
fn resolve(&mut self, env: Env, output: Self::Output) -> Result<Self::JsValue> {
158-
let ThreadsafeMozjpegCompressOutput {
159-
buf,
160-
len,
161-
de_c_info,
162-
compress_c_info,
163-
} = output;
164-
unsafe {
165-
env.create_buffer_with_borrowed_data(
206+
match output {
207+
JpegOptimizeOutput::Lossless(ThreadsafeMozjpegCompressOutput {
166208
buf,
167209
len,
168-
(de_c_info, compress_c_info, buf),
169-
|(mut input, mut output, buf), _| {
170-
mozjpeg_sys::jpeg_destroy_decompress(&mut input);
171-
mozjpeg_sys::jpeg_destroy_compress(&mut output);
172-
libc::free(buf as *mut std::ffi::c_void);
173-
},
174-
)
210+
de_c_info,
211+
compress_c_info,
212+
}) => unsafe {
213+
env.create_buffer_with_borrowed_data(
214+
buf,
215+
len,
216+
(de_c_info, compress_c_info, buf),
217+
|(mut input, mut output, buf), _| {
218+
mozjpeg_sys::jpeg_destroy_decompress(&mut input);
219+
mozjpeg_sys::jpeg_destroy_compress(&mut output);
220+
libc::free(buf as *mut std::ffi::c_void);
221+
},
222+
)
223+
},
224+
JpegOptimizeOutput::Lossy(buf) => env.create_buffer_with_data(buf),
175225
}
176226
.map(|v| v.into_raw())
177227
}

packages/binding/src/transformer.rs

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -354,7 +354,7 @@ struct ImageTransformArgs {
354354
grayscale: bool,
355355
invert: bool,
356356
rotate: bool,
357-
resize: (u32, Option<u32>, ResizeFilterType),
357+
resize: Option<(u32, Option<u32>, ResizeFilterType)>,
358358
contrast: Option<f32>,
359359
blur: Option<f32>,
360360
unsharpen: Option<(f32, i32)>,
@@ -409,14 +409,15 @@ impl Task for EncodeTask {
409409
let raw_width = meta.image.width();
410410
let raw_height = meta.image.height();
411411
match self.image_transform_args.resize {
412-
(w, Some(h), filter_type) => meta.image = meta.image.resize(w, h, filter_type.into()),
413-
(w, None, filter_type) => {
412+
Some((w, Some(h), filter_type)) => meta.image = meta.image.resize(w, h, filter_type.into()),
413+
Some((w, None, filter_type)) => {
414414
meta.image = meta.image.resize(
415415
w,
416416
((w as f32 / raw_width as f32) * (raw_height as f32)) as u32,
417417
filter_type.into(),
418418
)
419419
}
420+
None => {}
420421
}
421422
if self.image_transform_args.grayscale {
422423
meta.image.grayscale();
@@ -444,14 +445,16 @@ impl Task for EncodeTask {
444445
}
445446
let dynamic_image = &mut meta.image;
446447
let color_type = &meta.color_type;
448+
let width = dynamic_image.width();
449+
let height = dynamic_image.height();
447450
let format = match self.options {
448451
EncodeOptions::Webp(quality_factor) => {
449452
let (output_buf, size) = unsafe {
450453
crate::webp::encode_webp_inner(
451454
dynamic_image.as_bytes(),
452455
quality_factor,
453-
dynamic_image.width(),
454-
dynamic_image.height(),
456+
width,
457+
height,
455458
color_type,
456459
)
457460
}?;
@@ -461,8 +464,8 @@ impl Task for EncodeTask {
461464
let (output_buf, size) = unsafe {
462465
crate::webp::lossless_encode_webp_inner(
463466
dynamic_image.as_bytes(),
464-
dynamic_image.width(),
465-
dynamic_image.height(),
467+
width,
468+
height,
466469
color_type,
467470
)
468471
}?;
@@ -657,7 +660,7 @@ impl Transformer {
657660
height: Option<u32>,
658661
filter_type: Option<ResizeFilterType>,
659662
) -> &Self {
660-
self.image_transform_args.resize = (width, height, filter_type.unwrap_or_default());
663+
self.image_transform_args.resize = Some((width, height, filter_type.unwrap_or_default()));
661664
self
662665
}
663666

0 commit comments

Comments
 (0)