use crate::error::JpegError;
use crate::info::{ColorSpace, DecodeOptions, Info, RestartIndex, SofKind};
use crate::parse::header::{parse_header, parse_header_with_external_live, ParsedHeader};
use j2k_core::{CompressedPayloadKind, PassthroughCandidate};
use super::core_traits::jpeg_passthrough_syntax;
use super::{find_component_index, restart_index_for_stream};
#[derive(Debug)]
pub struct JpegView<'a> {
pub(super) bytes: &'a [u8],
pub(super) header: ParsedHeader,
pub(super) info: Info,
pub(super) options: DecodeOptions,
}
impl<'a> JpegView<'a> {
pub fn parse(input: &'a [u8]) -> Result<Self, JpegError> {
Self::parse_with_options(input, DecodeOptions::default())
}
pub(crate) fn parse_with_external_live(
input: &'a [u8],
external_live_bytes: usize,
) -> Result<Self, JpegError> {
Self::parse_with_options_and_external_live(
input,
DecodeOptions::default(),
external_live_bytes,
)
}
pub fn parse_with_options(input: &'a [u8], options: DecodeOptions) -> Result<Self, JpegError> {
let header = parse_header(input)?;
Ok(Self::from_header(input, header, options))
}
fn parse_with_options_and_external_live(
input: &'a [u8],
options: DecodeOptions,
external_live_bytes: usize,
) -> Result<Self, JpegError> {
let header = parse_header_with_external_live(input, external_live_bytes)?;
Ok(Self::from_header(input, header, options))
}
fn from_header(input: &'a [u8], header: ParsedHeader, options: DecodeOptions) -> Self {
let mut info = header.info();
options.apply_to_info(&mut info);
Self {
bytes: input,
header,
info,
options,
}
}
#[must_use]
pub fn info(&self) -> &Info {
&self.info
}
#[must_use]
pub fn bytes(&self) -> &'a [u8] {
self.bytes
}
pub(crate) fn parsed_header(&self) -> &ParsedHeader {
&self.header
}
#[must_use]
pub fn passthrough_candidate(&self) -> Option<PassthroughCandidate<'a>> {
jpeg_passthrough_syntax(&self.info).map(|transfer_syntax| {
PassthroughCandidate::new(
self.bytes,
transfer_syntax,
CompressedPayloadKind::JpegInterchange,
self.info.to_core_info(),
)
})
}
pub fn restart_index(&self) -> Result<Option<RestartIndex>, JpegError> {
restart_index_for_stream(
self.bytes,
self.header.sos_offset,
&self.info,
self.info.restart_interval,
)
}
pub(crate) fn has_lossless_subsampled_color_capability_shape(&self) -> bool {
if self.info.sof_kind != SofKind::Lossless
|| !matches!(self.info.color_space, ColorSpace::Rgb | ColorSpace::YCbCr)
|| !matches!(self.info.bit_depth, 8 | 16)
|| self.info.sampling.len() != 3
|| !self
.info
.sampling
.components()
.iter()
.any(|&(h, v)| h != 1 || v != 1)
|| self.header.scan_count != 1
{
return false;
}
let Some(scan) = self.header.scan.as_ref() else {
return false;
};
if !(1..=7).contains(&scan.ss)
|| scan.se != 0
|| scan.ah != 0
|| scan.al != 0
|| scan.components.len() != 3
{
return false;
}
scan.components.iter().all(|scan_component| {
find_component_index(&self.header.component_ids, scan_component.id).is_some()
&& self.header.huffman_tables.dc[scan_component.dc_table as usize].is_some()
})
}
}