Skip to content

Commit

Permalink
frame and scan headers, add encoding order
Browse files Browse the repository at this point in the history
  • Loading branch information
friendlymatthew committed Jun 6, 2024
1 parent 3af4093 commit 3128267
Show file tree
Hide file tree
Showing 5 changed files with 71 additions and 46 deletions.
1 change: 0 additions & 1 deletion src/coding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ pub struct ProcessSchema {
pub(crate) entropy_table_count: (usize, usize),
}


impl CodingProcess {
pub(crate) fn schema(&self) -> ProcessSchema {
match self {
Expand Down
31 changes: 19 additions & 12 deletions src/decoder.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
use crate::coding::CodingProcess;
use crate::huffman_tree::HuffmanClass;
use crate::marker::{Marker, MarkerType};
use crate::parser::Parser;
use crate::sample_precision::SamplePrecision;
use anyhow::{anyhow, Result};
use memmap::Mmap;
use rayon::iter::IntoParallelRefIterator;
use rayon::iter::ParallelIterator;
use std::collections::HashMap;
use std::fs::File;
use std::simd::prelude::*;
use crate::huffman_tree::HuffmanClass;
use crate::sample_precision::SamplePrecision;

type Marlen = (usize, usize); // offset, length

Expand Down Expand Up @@ -173,29 +173,36 @@ impl Decoder {

// 0. Make sure headers align
if frame_header.component_type != scan_header.component_type {
return Err(anyhow!("header component types do not align."))
return Err(anyhow!("header component types do not align."));
}

// 1. since huffman, huffman. no need to check lmao

// 2. count num ac, dc tables for entropy coding
let (num_ac_tables, num_dc_tables) = huffman_trees.iter().fold((0, 0), |(ac_count, dc_count), ht| {
match ht.class {
HuffmanClass::AC => (ac_count + 1, dc_count),
HuffmanClass::DC => (ac_count, dc_count + 1),
}
});
let (num_ac_tables, num_dc_tables) =
huffman_trees
.iter()
.fold((0, 0), |(ac_count, dc_count), ht| match ht.class {
HuffmanClass::AC => (ac_count + 1, dc_count),
HuffmanClass::DC => (ac_count, dc_count + 1),
});

let (expected_ac_tables, expected_dc_tables) = code_schema.entropy_table_count;
if num_ac_tables != expected_ac_tables || num_dc_tables != expected_dc_tables {
return Err(anyhow!("number of ac & dc entropy tables mismatch from expected."))
return Err(anyhow!(
"number of ac & dc entropy tables mismatch from expected."
));
}

// 3. Check precision
let precisions: Vec<SamplePrecision> = quantization_tables.iter().map(|qt| qt.precision).collect();
let precisions: Vec<SamplePrecision> =
quantization_tables.iter().map(|qt| qt.precision).collect();

if !precisions.iter().all(|p| *p == SamplePrecision::EightBit) {
return Err(anyhow!(format!("expected 8-bit samples within each component. Got {:?}", &precisions)))
return Err(anyhow!(format!(
"expected 8-bit samples within each component. Got {:?}",
&precisions
)));
}
}
_ => todo!(),
Expand Down
7 changes: 4 additions & 3 deletions src/frame_header.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::sample_precision::SamplePrecision;
use crate::scan_header::EncodingOrder;

#[derive(Debug)]
pub struct FrameHeader {
Expand Down Expand Up @@ -29,10 +30,10 @@ pub(crate) enum ComponentType {
}

impl ComponentType {
pub(crate) fn from(b: u8) -> Self {
pub(crate) fn from(b: u8) -> (Self, EncodingOrder) {
match b {
1 => ComponentType::Grayscale,
3 => ComponentType::Color,
1 => (ComponentType::Grayscale, EncodingOrder::NonInterleaved),
3 => (ComponentType::Color, EncodingOrder::Interleaved),
_ => unreachable!(),
}
}
Expand Down
30 changes: 15 additions & 15 deletions src/parser.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
use crate::coding::CodingProcess;
use crate::frame_header::{Component, ComponentType, FrameHeader};
use crate::huffman_tree::HuffmanTree;
use crate::marker::Marker;
use crate::quantization_table::QuantizationTable;
use crate::sample_precision::SamplePrecision;
use crate::scan_header::{ScanComponentSelector, ScanHeader};
use crate::scan_header::{EncodingOrder, ScanComponentSelector, ScanHeader};
use anyhow::{anyhow, Result};
use std::collections::HashMap;
use std::iter;
use std::simd::prelude::*;
use crate::coding::CodingProcess;

pub const QUANTIZATION_TABLE_BYTES: usize = 64;

Expand Down Expand Up @@ -155,7 +155,7 @@ impl Parser {

let mut current_offset = offset;

let component_type = ComponentType::from(self.buffer[current_offset]);
let (component_type, encoding_order) = ComponentType::from(self.buffer[current_offset]);
current_offset += 1;

debug_assert_eq!(
Expand Down Expand Up @@ -197,10 +197,10 @@ impl Parser {

current_offset += 2 * (component_type as usize);

let predictor_selection = self.buffer[current_offset];
let start_of_spectral = self.buffer[current_offset];
current_offset += 1;

let end_of_spectral_selection = self.buffer[current_offset];
let end_of_spectral= self.buffer[current_offset];
current_offset += 1;

let approx_bit_chunk = self.buffer[current_offset];
Expand All @@ -211,10 +211,11 @@ impl Parser {

Ok((
ScanHeader {
encoding_order,
component_type,
scan_component_selectors,
predictor_selection,
end_of_spectral_selection,
start_of_spectral,
end_of_spectral,
successive_approx_bit_position_high,
point_transform,
},
Expand Down Expand Up @@ -246,8 +247,8 @@ impl Parser {

let mut components = vec![];

match component_type {
ComponentType::Grayscale => {
match component_type.1 {
EncodingOrder::NonInterleaved=> {
// naive solution
let component_id = self.buffer[current_offset];
current_offset += 1;
Expand All @@ -264,7 +265,7 @@ impl Parser {
qt_table_id,
))
}
ComponentType::Color => {
EncodingOrder::Interleaved => {
let component_ids = Simd::from([
self.buffer[current_offset],
self.buffer[current_offset + 3],
Expand Down Expand Up @@ -307,7 +308,7 @@ impl Parser {
precision,
image_height,
image_width,
component_type,
component_type: component_type.0,
components,
})
}
Expand Down Expand Up @@ -413,8 +414,7 @@ mod tests {
0x00, 0x10, b'J', b'F', b'I', b'F', 0x00, 0x01, 0x01, 0x01, 0x00, 0x48, 0x00, 0x48,
0x00, 0x00, // 16
0xFF, 0xDB, // QT 1
0x00, 0x03, 0x00,
0xFF, 0xDB, // QT 2
0x00, 0x03, 0x00, 0xFF, 0xDB, // QT 2
0x00, 0x03, 0x00, 0xFF, 0xC0, // START OF FRAME
0x00, 0x11, 0x08, 0x00, 0x02, 0x00, 0x06, 0x03, 0x01, 0x22, 0x00, 0x02, 0x11, 0x01,
0x03, 0x11, 0x01, // 17
Expand Down Expand Up @@ -526,8 +526,8 @@ mod tests {

let (scan_header, s_idx) = parser.parse_start_of_scan()?;

assert_eq!(scan_header.predictor_selection, 0x01);
assert_eq!(scan_header.end_of_spectral_selection, 63);
assert_eq!(scan_header.start_of_spectral, 0x01);
assert_eq!(scan_header.end_of_spectral, 63);
assert_eq!(scan_header.successive_approx_bit_position_high, 1);
assert_eq!(scan_header.point_transform, 0);

Expand Down
48 changes: 33 additions & 15 deletions src/scan_header.rs
Original file line number Diff line number Diff line change
@@ -1,33 +1,54 @@
use crate::frame_header::ComponentType;

/// (Pg. 25) For a given scan, if the scan header parameter `component_type` is 1, then data from only
/// one source component - the component specified by parameter `Components`[0] - shall be present
/// within the scan. If `component_type` > 1, then data from `Components` shall be present within
/// the scan. The order of components in a scan shall be according to the order specified in the
/// `FrameHeader`.
#[derive(Debug)]
pub(crate) enum EncodingOrder {
/// The encoder compressed all image data units in component A before beginning component B
NonInterleaved,

/// The encoder compresses a data unit from A, a data unit from B, a data unit from C, then
/// back to A, etc...
Interleaved,
}

#[derive(Debug)]
pub struct ScanHeader {
/// Number of image components in frame -- Specifies the number of source image components in
pub(crate) encoding_order: EncodingOrder,

/// NS: Number of image components in frame -- Specifies the number of source image components in
/// the frame. The value of `num_components` shall be equal to the number of sets of frame
///component specification parameters (Ci, Hi, Vi, Tqi) present in the frame header.
pub(crate) component_type: ComponentType,

/// Csj: where J is the length of the `Vec<ScanComponentSelector>`.
/// Selects which of the component type specified in the frame parameters shall be the jth
/// component in the scan. Each scan component selector shall match one of the Ci values
/// specified in the frame header.
pub(crate) scan_component_selectors: Vec<ScanComponentSelector>,

/// In DCT modes of operation, this parameter specifies the first DCT coefficient in each block
/// in zig-zag order which shall be coded in the scan. This parameter is set to zero for
/// sequential DCT processes. In the lossless mode of operations this parameter is used to
/// select the predictor
pub(crate) predictor_selection: u8,
/// Ss: Start of spectral or predictior selection. In DCT modes of operation, this parameter
/// specifies the first DCT coefficient in each block in zig-zag order which shall be coded in
/// the scan. This parameter is set to zero for sequential DCT processes. In the lossless mode
/// of operations this parameter is used to select the predictor
pub(crate) start_of_spectral: u8,

/// Specifies the last DCT coefficient in each block in zig-zag order which shall be coded in
/// Se: Specifies the last DCT coefficient in each block in zig-zag order which shall be coded in
/// the scan. This parameter shall be set to 63 for sequential DCT processes. In the lossless
/// mode of operations this parameter has no meaning. It shall be set to zero.
pub(crate) end_of_spectral_selection: u8,
pub(crate) end_of_spectral: u8,

/// This parameter specifies the point transform used in the preceding scan (the successive
/// Ah: This parameter specifies the point transform used in the preceding scan (the successive
/// approximation bit position low in the preceding scan) for the band of coefficients specified
/// by `predictor_selection` and `end_of_spectral_selection`. This parameter shall be set to
/// zero for the first scan of each band of coefficients. In the lossless mode of operations
/// this parameter has no meaning. It shall be set to zero.
pub(crate) successive_approx_bit_position_high: u8,

/// In DCT modes of operation, this parameter specifies the point transform (bit position low,
/// Al:In DCT modes of operation, this parameter specifies the point transform (bit position low,
/// used before coding the band of coefficients specified by `predictor_selection` and
/// `end_of_spectral_selection`. This parameter shall be set to zero for sequential DCT processes.
/// In the lossless mode of operations, this parameter specifies the point transform Pt.
Expand All @@ -36,16 +57,13 @@ pub struct ScanHeader {

#[derive(Debug)]
pub struct ScanComponentSelector {
/// Selects which of the component type specified in the frame parameters shall be the jth
/// component in the scan. Each scan component selector shall match one of the Ci values
/// specified in the frame header.
component_id: u8,

/// Specifies one of four possible DC entropy coding table destinations from which the entropy
/// Tdj: Specifies one of four possible DC entropy coding table destinations from which the entropy
/// table needed for decoding of the DC coefficients of component selector j is retrieved.
dc_destination_id: u8,

/// Specifies one of four possible AC entropy coding table destinations from which the entropy
/// Taj: Specifies one of four possible AC entropy coding table destinations from which the entropy
/// table needed for decoding of the AC coefficients of component selector j is retrieved.
ac_destination_id: u8,
}
Expand Down

0 comments on commit 3128267

Please sign in to comment.