use crate::error::{DecodeError, DecodeErrorKind};
use crate::limits::MAX_PIXELS;
use crate::types::{Channels, DecodedImage, Format};
use std::os::raw::{c_int, c_uchar};
#[allow(unused_imports)]
use mozjpeg_sys::jpeg_std_error as _force_libjpeg_link;
const RID_OK: c_int = 0;
const RID_ERR_CMYK: c_int = -1;
const RID_ERR_TOO_LARGE: c_int = -2;
const RID_ERR_UNEXPECTED_COMPONENTS: c_int = -3;
const RID_ERR_ALLOC: c_int = -4;
const RID_ERR_BAD_HEADER: c_int = -5;
const RID_ERR_LIBJPEG_BASE: c_int = 1000;
extern "C" {
fn rid_decode_jpeg(
bytes: *const c_uchar,
len: usize,
max_pixels: usize,
out_width: *mut u32,
out_height: *mut u32,
out_buf: *mut *mut c_uchar,
) -> c_int;
fn rid_free_buf(buf: *mut c_uchar);
}
pub(crate) fn decode_jpeg(bytes: &[u8]) -> Result<DecodedImage, DecodeError> {
if bytes.is_empty() {
return Err(DecodeError::new(
DecodeErrorKind::CorruptInput,
Some(Format::Jpeg),
"empty input",
));
}
let mut width: u32 = 0;
let mut height: u32 = 0;
let mut buf: *mut c_uchar = std::ptr::null_mut();
let rc = unsafe {
rid_decode_jpeg(
bytes.as_ptr(),
bytes.len(),
MAX_PIXELS,
&mut width,
&mut height,
&mut buf,
)
};
if rc != RID_OK {
debug_assert!(buf.is_null());
return Err(map_shim_error(rc));
}
let width = width as usize;
let height = height as usize;
let size = width * height * 3;
let mut data = vec![0u8; size];
unsafe {
std::ptr::copy_nonoverlapping(buf, data.as_mut_ptr(), size);
rid_free_buf(buf);
}
Ok(DecodedImage {
width,
height,
data,
channels: Channels::Rgb,
format: Format::Jpeg,
})
}
fn map_shim_error(rc: c_int) -> DecodeError {
match rc {
RID_ERR_CMYK => DecodeError::new(
DecodeErrorKind::UnsupportedFeature,
Some(Format::Jpeg),
"CMYK color space",
),
RID_ERR_TOO_LARGE => DecodeError::new(
DecodeErrorKind::ImageTooLarge,
Some(Format::Jpeg),
format!("declared dimensions exceed MAX_PIXELS = {}", MAX_PIXELS),
),
RID_ERR_UNEXPECTED_COMPONENTS => DecodeError::new(
DecodeErrorKind::CorruptInput,
Some(Format::Jpeg),
"unexpected output_components (libjpeg returned non-RGB)",
),
RID_ERR_ALLOC => DecodeError::new(
DecodeErrorKind::CorruptInput,
Some(Format::Jpeg),
"out of memory allocating output buffer",
),
RID_ERR_BAD_HEADER => DecodeError::new(
DecodeErrorKind::CorruptInput,
Some(Format::Jpeg),
"jpeg_read_header returned non-OK (corrupt header)",
),
x if x >= RID_ERR_LIBJPEG_BASE => DecodeError::new(
DecodeErrorKind::CorruptInput,
Some(Format::Jpeg),
format!("libjpeg fatal error msg_code={}", x - RID_ERR_LIBJPEG_BASE),
),
_ => DecodeError::new(
DecodeErrorKind::CorruptInput,
Some(Format::Jpeg),
format!("unknown jpeg shim error code: {}", rc),
),
}
}