use std::convert::TryInto as _;
use crate::{Colorspace, Image, YuvImage, YuvPlanesImage, raw};
use crate::buf::{OwnedBuf, OutputBuf};
use crate::common::{Subsamp, Result, Error};
use crate::handle::Handle;
#[derive(Debug)]
#[doc(alias = "tjhandle")]
pub struct Compressor {
handle: Handle,
subsamp: Subsamp,
}
static DEFAULT_QUALITY: i32 = 95;
static DEFAULT_SUBSAMP: Subsamp = Subsamp::None;
unsafe impl Send for Compressor {}
impl Compressor {
#[doc(alias = "tj3Init")]
pub fn new() -> Result<Compressor> {
let mut handle = Handle::new(raw::TJINIT_TJINIT_COMPRESS)?;
handle.set(raw::TJPARAM_TJPARAM_QUALITY, DEFAULT_QUALITY as libc::c_int)?;
handle.set(raw::TJPARAM_TJPARAM_SUBSAMP, DEFAULT_SUBSAMP as i32 as libc::c_int)?;
Ok(Compressor { handle, subsamp: DEFAULT_SUBSAMP })
}
#[doc(alias = "TJPARAM_QUALITY")]
pub fn set_quality(&mut self, quality: i32) -> Result<()> {
self.handle.set(raw::TJPARAM_TJPARAM_QUALITY, quality as libc::c_int)
}
#[doc(alias = "TJPARAM_LOSSLESS")]
pub fn set_lossless(&mut self, enabled: bool) -> Result<()> {
self.handle
.set(raw::TJPARAM_TJPARAM_LOSSLESS, enabled as libc::c_int)
}
#[doc(alias = "TJPARAM_SUBSAMP")]
pub fn set_subsamp(&mut self, subsamp: Subsamp) -> Result<()> {
self.handle.set(raw::TJPARAM_TJPARAM_SUBSAMP, subsamp as i32 as libc::c_int)
}
#[doc(alias = "TJPARAM_COLORSPACE")]
pub fn set_colorspace(&mut self, colorspace: Colorspace) -> Result<()> {
self.handle.set(raw::TJPARAM_TJPARAM_COLORSPACE, colorspace as i32 as libc::c_int)
}
#[doc(alias = "TJPARAM_OPTIMIZE")]
pub fn set_optimize(&mut self, optimize: bool) -> Result<()> {
self.handle.set(raw::TJPARAM_TJPARAM_OPTIMIZE, optimize as libc::c_int)
}
#[doc(alias = "TJPARAM_PROGRESSIVE")]
pub fn set_progressive(&mut self, progressive: bool) -> Result<()> {
self.handle.set(raw::TJPARAM_TJPARAM_PROGRESSIVE, progressive as libc::c_int)
}
#[doc(alias = "TJPARAM_ARITHMETIC")]
pub fn set_arithmetic(&mut self, arithmetic: bool) -> Result<()> {
self.handle.set(raw::TJPARAM_TJPARAM_ARITHMETIC, arithmetic as libc::c_int)
}
#[doc(alias = "tj3Compress8")]
pub fn compress(&mut self, image: Image<&[u8]>, output: &mut OutputBuf) -> Result<()> {
image.assert_valid(image.pixels.len());
let Image { pixels, width, pitch, height, format } = image;
let width = width.try_into().map_err(|_| Error::IntegerOverflow("width"))?;
let pitch = pitch.try_into().map_err(|_| Error::IntegerOverflow("pitch"))?;
let height = height.try_into().map_err(|_| Error::IntegerOverflow("height"))?;
self.handle.set(
raw::TJPARAM_TJPARAM_NOREALLOC,
if output.is_owned { 0 } else { 1 } as libc::c_int,
)?;
let mut output_len = output.len as raw::size_t;
let res = unsafe {
raw::tj3Compress8(
self.handle.as_ptr(),
pixels.as_ptr(), width, pitch, height, format as libc::c_int,
&mut output.ptr, &mut output_len,
)
};
output.len = output_len as usize;
if res != 0 {
return Err(self.handle.get_error())
} else if output.ptr.is_null() {
output.len = 0;
return Err(Error::Null)
}
Ok(())
}
pub fn compress_to_owned(&mut self, image: Image<&[u8]>) -> Result<OwnedBuf> {
let mut buf = OutputBuf::new_owned();
self.compress(image, &mut buf)?;
Ok(buf.into_owned())
}
pub fn compress_to_vec(&mut self, image: Image<&[u8]>) -> Result<Vec<u8>> {
let mut buf = OutputBuf::new_owned();
self.compress(image, &mut buf)?;
Ok(buf.to_vec())
}
pub fn compress_to_slice(&mut self, image: Image<&[u8]>, output: &mut [u8]) -> Result<usize> {
let mut buf = OutputBuf::borrowed(output);
self.compress(image, &mut buf)?;
Ok(buf.len())
}
#[doc(alias = "tj3CompressFromYUV8")]
pub fn compress_yuv(&mut self, image: YuvImage<&[u8]>, output: &mut OutputBuf) -> Result<()> {
image.assert_valid(image.pixels.len());
let YuvImage { pixels, width, align, height, subsamp } = image;
self.set_subsamp(subsamp)?;
let width: libc::c_int = width.try_into().map_err(|_| Error::IntegerOverflow("width"))?;
let align = align.try_into().map_err(|_| Error::IntegerOverflow("align"))?;
let height: libc::c_int = height.try_into().map_err(|_| Error::IntegerOverflow("height"))?;
self.handle.set(
raw::TJPARAM_TJPARAM_NOREALLOC,
if output.is_owned { 0 } else { 1 } as libc::c_int,
)?;
let mut output_len = output.len as raw::size_t;
let res = unsafe {
raw::tj3CompressFromYUV8(
self.handle.as_ptr(),
pixels.as_ptr(), width, align, height,
&mut output.ptr, &mut output_len,
)
};
output.len = output_len as usize;
if res != 0 {
return Err(self.handle.get_error())
} else if output.ptr.is_null() {
output.len = 0;
return Err(Error::Null)
}
Ok(())
}
pub fn compress_yuv_to_owned(&mut self, image: YuvImage<&[u8]>) -> Result<OwnedBuf> {
let mut buf = OutputBuf::new_owned();
self.compress_yuv(image, &mut buf)?;
Ok(buf.into_owned())
}
pub fn compress_yuv_to_vec(&mut self, image: YuvImage<&[u8]>) -> Result<Vec<u8>> {
let mut buf = OutputBuf::new_owned();
self.compress_yuv(image, &mut buf)?;
Ok(buf.to_vec())
}
pub fn compress_yuv_to_slice(&mut self, image: YuvImage<&[u8]>, output: &mut [u8]) -> Result<usize> {
let mut buf = OutputBuf::borrowed(output);
self.compress_yuv(image, &mut buf)?;
Ok(buf.len())
}
#[doc(alias = "tj3CompressFromYUVPlanes8")]
pub fn compress_yuv_planes(
&mut self,
image: &YuvPlanesImage<&[u8]>,
output: &mut OutputBuf,
) -> Result<()> {
image.assert_valid(image.y_plane.len(), image.u_plane.len(), image.v_plane.len());
let width = image.width.try_into().map_err(|_| Error::IntegerOverflow("width"))?;
let height = image.height.try_into().map_err(|_| Error::IntegerOverflow("height"))?;
let planes = [
image.y_plane.as_ptr(),
image.u_plane.as_ptr(),
image.v_plane.as_ptr(),
];
let strides = [
image.y_stride.try_into().map_err(|_| Error::IntegerOverflow("y_stride"))?,
image.u_stride.try_into().map_err(|_| Error::IntegerOverflow("u_stride"))?,
image.v_stride.try_into().map_err(|_| Error::IntegerOverflow("v_stride"))?,
];
self.set_subsamp(image.subsamp)?;
self.handle.set(
raw::TJPARAM_TJPARAM_NOREALLOC,
if output.is_owned { 0 } else { 1 } as libc::c_int,
)?;
let mut output_len = output.len as raw::size_t;
let result = unsafe {
raw::tj3CompressFromYUVPlanes8(
self.handle.as_ptr(),
planes.as_ptr(),
width,
strides.as_ptr(),
height,
&mut output.ptr,
&mut output_len,
)
};
output.len = output_len as usize;
if result != 0 {
return Err(self.handle.get_error());
} else if output.ptr.is_null() {
output.len = 0;
return Err(Error::Null);
}
Ok(())
}
pub fn compress_yuv_planes_to_owned(
&mut self,
image: &YuvPlanesImage<&[u8]>,
) -> Result<OwnedBuf> {
let mut buf = OutputBuf::new_owned();
self.compress_yuv_planes(image, &mut buf)?;
Ok(buf.into_owned())
}
pub fn compress_yuv_planes_to_vec(
&mut self,
image: &YuvPlanesImage<&[u8]>,
) -> Result<Vec<u8>> {
let mut buf = OutputBuf::new_owned();
self.compress_yuv_planes(image, &mut buf)?;
Ok(buf.to_vec())
}
pub fn compress_yuv_planes_to_slice(
&mut self,
image: &YuvPlanesImage<&[u8]>,
output: &mut [u8],
) -> Result<usize> {
let mut buf = OutputBuf::borrowed(output);
self.compress_yuv_planes(image, &mut buf)?;
Ok(buf.len())
}
#[doc(alias = "tj3JPEGBufSize")]
pub fn buf_len(&self, width: usize, height: usize) -> Result<usize> {
compressed_buf_len(width, height, self.subsamp)
}
}
pub fn compress(image: Image<&[u8]>, quality: i32, subsamp: Subsamp) -> Result<OwnedBuf> {
let mut compressor = Compressor::new()?;
compressor.set_quality(quality)?;
compressor.set_subsamp(subsamp)?;
compressor.compress_to_owned(image)
}
pub fn compress_yuv(image: YuvImage<&[u8]>, quality: i32) -> Result<OwnedBuf> {
let mut compressor = Compressor::new()?;
compressor.set_quality(quality)?;
compressor.compress_yuv_to_owned(image)
}
pub fn compress_yuv_planes(image: &YuvPlanesImage<&[u8]>, quality: i32) -> Result<OwnedBuf> {
let mut compressor = Compressor::new()?;
compressor.set_quality(quality)?;
compressor.compress_yuv_planes_to_owned(image)
}
#[doc(alias = "tj3JPEGBufSize")]
pub fn compressed_buf_len(width: usize, height: usize, subsamp: Subsamp) -> Result<usize> {
let width = width.try_into().map_err(|_| Error::IntegerOverflow("width"))?;
let height = height.try_into().map_err(|_| Error::IntegerOverflow("height"))?;
let len = unsafe { raw::tj3JPEGBufSize(width, height, subsamp as libc::c_int) };
let len = len.try_into().map_err(|_| Error::IntegerOverflow("buf len"))?;
Ok(len)
}