use core::convert::TryInto;
use core::fmt;
use bytes::{Buf, BufMut, Bytes, BytesMut};
use super::markers::{self, has_entropy, has_length};
use crate::encoder::{EncodeAt, ImageEncoder};
use crate::util::{read_checked, split_to_checked};
use crate::{Error, Result, EXIF_DATA_PREFIX};
const ICC_DATA_PREFIX: &[u8] = b"ICC_PROFILE\0";
#[derive(Clone, PartialEq, Eq)]
pub struct JpegSegment {
marker: u8,
contents: Bytes,
entropy: Bytes,
}
#[allow(clippy::len_without_is_empty)]
impl JpegSegment {
#[inline]
pub fn new(marker: u8) -> JpegSegment {
JpegSegment {
marker,
contents: Bytes::new(),
entropy: Bytes::new(),
}
}
#[inline]
pub fn new_with_contents(marker: u8, contents: Bytes) -> JpegSegment {
JpegSegment {
marker,
contents,
entropy: Bytes::new(),
}
}
#[inline]
pub fn new_with_entropy(marker: u8, contents: Bytes, entropy: Bytes) -> JpegSegment {
JpegSegment {
marker,
contents,
entropy,
}
}
pub(super) fn new_icc(seqno: u8, num: u8, buf: Bytes) -> JpegSegment {
let mut contents = BytesMut::with_capacity(ICC_DATA_PREFIX.len() + 2 + buf.len());
contents.put(ICC_DATA_PREFIX);
contents.put_u8(seqno);
contents.put_u8(num);
contents.put(buf);
JpegSegment::new_with_contents(markers::APP2, contents.freeze())
}
pub(super) fn new_exif(buf: Bytes) -> JpegSegment {
let mut contents = BytesMut::with_capacity(EXIF_DATA_PREFIX.len() + buf.len());
contents.put(EXIF_DATA_PREFIX);
contents.put(buf);
JpegSegment::new_with_contents(markers::APP1, contents.freeze())
}
pub(crate) fn from_bytes(marker: u8, b: &mut Bytes) -> Result<JpegSegment> {
let size = read_checked(b, |b| b.get_u16())?
.checked_sub(2)
.ok_or(Error::Truncated)?;
let contents = split_to_checked(b, size as usize)?;
if !has_entropy(marker) {
Ok(JpegSegment::new_with_contents(marker, contents))
} else {
Ok(JpegSegment::new_with_entropy(marker, contents, b.clone()))
}
}
pub fn len(&self) -> usize {
if has_length(self.marker) {
2 + 2 + self.contents.len()
} else {
2 + self.contents.len()
}
}
pub fn len_with_entropy(&self) -> usize {
self.len() + self.entropy.len()
}
#[inline]
pub fn marker(&self) -> u8 {
self.marker
}
#[inline]
pub fn contents(&self) -> &Bytes {
&self.contents
}
#[inline]
pub fn has_entropy(&self) -> bool {
!self.entropy.is_empty()
}
pub(super) fn icc(&self) -> Option<(u8, u8, Bytes)> {
if self.marker != markers::APP2 || !self.contents.starts_with(ICC_DATA_PREFIX) {
return None;
}
let mut b = self.contents.slice(ICC_DATA_PREFIX.len()..);
let seqno = b.get_u8();
let num = b.get_u8();
Some((seqno, num, b))
}
pub(super) fn exif(&self) -> Option<Bytes> {
if self.marker == markers::APP1 && self.contents.starts_with(EXIF_DATA_PREFIX) {
Some(self.contents.slice(EXIF_DATA_PREFIX.len()..))
} else {
None
}
}
#[inline]
pub fn encoder(self) -> ImageEncoder<Self> {
ImageEncoder::from(self)
}
}
impl EncodeAt for JpegSegment {
fn encode_at(&self, pos: &mut usize) -> Option<Bytes> {
match pos {
0 => {
let mut vec = BytesMut::with_capacity(4);
vec.put_u8(markers::P);
vec.put_u8(self.marker());
vec.put_u16((self.len() - 2).try_into().unwrap());
Some(vec.freeze())
}
1 if !self.contents.is_empty() => Some(self.contents.clone()),
2 if !self.entropy.is_empty() => Some(self.entropy.clone()),
_ => {
*pos -= 1 + !self.contents.is_empty() as usize + !self.entropy.is_empty() as usize;
None
}
}
}
fn len(&self) -> usize {
self.len()
}
}
impl fmt::Debug for JpegSegment {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("JpegSegment")
.field("marker", &self.marker)
.finish()
}
}