use crate::encode::encoder::Component;
use crate::encode::huffman::{CodingClass, HuffmanTable};
use crate::encode::marker::{Marker, SOFType};
use crate::encode::quantization::QuantizationTable;
use crate::encode::EncodingError;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct PixelDensity {
pub density: (u16, u16),
pub unit: PixelDensityUnit,
}
impl PixelDensity {
#[must_use]
pub fn dpi(density: u16) -> Self {
PixelDensity {
density: (density, density),
unit: PixelDensityUnit::Inches,
}
}
}
impl Default for PixelDensity {
fn default() -> Self {
PixelDensity {
density: (1, 1),
unit: PixelDensityUnit::PixelAspectRatio,
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum PixelDensityUnit {
PixelAspectRatio,
Inches,
Centimeters,
}
pub static ZIGZAG: [u8; 64] = [
0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, 11, 4, 5, 12, 19, 26, 33, 40, 48, 41, 34, 27, 20,
13, 6, 7, 14, 21, 28, 35, 42, 49, 56, 57, 50, 43, 36, 29, 22, 15, 23, 30, 37, 44, 51, 58, 59,
52, 45, 38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63,
];
const BUFFER_SIZE: usize = core::mem::size_of::<usize>() * 8;
pub trait JfifWrite {
fn write_all(&mut self, buf: &[u8]) -> Result<(), EncodingError>;
}
#[cfg(not(feature = "std"))]
impl<W: JfifWrite + ?Sized> JfifWrite for &mut W {
fn write_all(&mut self, buf: &[u8]) -> Result<(), EncodingError> {
(**self).write_all(buf)
}
}
#[cfg(not(feature = "std"))]
impl JfifWrite for alloc::vec::Vec<u8> {
fn write_all(&mut self, buf: &[u8]) -> Result<(), EncodingError> {
self.extend_from_slice(buf);
Ok(())
}
}
#[cfg(feature = "std")]
impl<W: std::io::Write + ?Sized> JfifWrite for W {
#[inline(always)]
fn write_all(&mut self, buf: &[u8]) -> Result<(), EncodingError> {
self.write_all(buf)?;
Ok(())
}
}
pub(crate) struct JfifWriter<W: JfifWrite> {
w: W,
bit_buffer: usize,
free_bits: i8,
}
impl<W: JfifWrite> JfifWriter<W> {
pub fn new(w: W) -> Self {
JfifWriter {
w,
bit_buffer: 0,
free_bits: BUFFER_SIZE as i8,
}
}
pub fn flush_output(&mut self) -> Result<(), EncodingError> {
Ok(())
}
#[inline(always)]
pub fn write(&mut self, buf: &[u8]) -> Result<(), EncodingError> {
self.w.write_all(buf)
}
#[inline(always)]
pub fn write_u8(&mut self, value: u8) -> Result<(), EncodingError> {
self.w.write_all(&[value])
}
#[inline(always)]
pub fn write_u16(&mut self, value: u16) -> Result<(), EncodingError> {
self.w.write_all(&value.to_be_bytes())
}
pub fn finalize_bit_buffer(&mut self) -> Result<(), EncodingError> {
self.write_bits(0x7F, 7)?;
self.flush_bit_buffer()?;
self.bit_buffer = 0;
self.free_bits = BUFFER_SIZE as i8;
Ok(())
}
pub fn flush_bit_buffer(&mut self) -> Result<(), EncodingError> {
while self.free_bits <= (BUFFER_SIZE as i8 - 8) {
self.flush_byte_from_bit_buffer(self.free_bits)?;
self.free_bits += 8;
}
Ok(())
}
#[inline(always)]
fn flush_byte_from_bit_buffer(&mut self, free_bits: i8) -> Result<(), EncodingError> {
let value = (self.bit_buffer >> (BUFFER_SIZE as i8 - 8 - free_bits)) & 0xFF;
self.write_u8(value as u8)?;
if value == 0xFF {
self.write_u8(0x00)?;
}
Ok(())
}
#[inline(always)]
#[allow(overflowing_literals)]
fn write_bit_buffer(&mut self) -> Result<(), EncodingError> {
crate::prof::bump(crate::prof::Count::BufferFlushes, 1);
if (self.bit_buffer
& 0x8080808080808080
& !(self.bit_buffer.wrapping_add(0x0101010101010101)))
!= 0
{
crate::prof::bump(crate::prof::Count::StuffedFlushes, 1);
for i in 0..(BUFFER_SIZE / 8) {
self.flush_byte_from_bit_buffer((i * 8) as i8)?;
}
Ok(())
} else {
self.w.write_all(&self.bit_buffer.to_be_bytes())
}
}
#[inline]
pub fn write_bits(&mut self, value: u32, size: u8) -> Result<(), EncodingError> {
crate::prof::bump(crate::prof::Count::BitWrites, 1);
crate::prof::bump(crate::prof::Count::Bits, size as u64);
let size = size as i8;
let value = value as usize;
let free_bits = self.free_bits - size;
if free_bits < 0 {
self.bit_buffer = (self.bit_buffer << (size + free_bits)) | (value >> -free_bits);
self.write_bit_buffer()?;
self.bit_buffer = value;
self.free_bits = free_bits + BUFFER_SIZE as i8;
} else {
self.free_bits = free_bits;
self.bit_buffer = (self.bit_buffer << size) | value;
}
Ok(())
}
pub fn write_marker(&mut self, marker: Marker) -> Result<(), EncodingError> {
self.write(&[0xFF, marker.into()])
}
pub fn write_segment(&mut self, marker: Marker, data: &[u8]) -> Result<(), EncodingError> {
self.write_marker(marker)?;
self.write_u16(data.len() as u16 + 2)?;
self.write(data)?;
Ok(())
}
pub fn write_header(&mut self, density: &PixelDensity) -> Result<(), EncodingError> {
self.write_marker(Marker::APP(0))?;
self.write_u16(16)?;
self.write(b"JFIF\0")?;
self.write(&[0x01, 0x02])?;
match density.unit {
PixelDensityUnit::PixelAspectRatio => {
self.write_u8(0x00)?;
}
PixelDensityUnit::Inches => {
self.write_u8(0x01)?;
}
PixelDensityUnit::Centimeters => {
self.write_u8(0x02)?;
}
}
let (x, y) = density.density;
self.write_u16(x)?;
self.write_u16(y)?;
self.write(&[0x00, 0x00])
}
pub fn write_huffman_segment(
&mut self,
class: CodingClass,
destination: u8,
table: &HuffmanTable,
) -> Result<(), EncodingError> {
assert!(destination < 4, "Bad destination: {}", destination);
self.write_marker(Marker::DHT)?;
self.write_u16(2 + 1 + 16 + table.values().len() as u16)?;
self.write_u8(((class as u8) << 4) | destination)?;
self.write(table.length())?;
self.write(table.values())?;
Ok(())
}
pub fn write_quantization_segment(
&mut self,
destination: u8,
table: &QuantizationTable,
) -> Result<(), EncodingError> {
assert!(destination < 4, "Bad destination: {}", destination);
self.write_marker(Marker::DQT)?;
self.write_u16(2 + 1 + 64)?;
self.write_u8(destination)?;
for &v in ZIGZAG.iter() {
self.write_u8(table.get(v as usize))?;
}
Ok(())
}
pub fn write_dri(&mut self, restart_interval: u16) -> Result<(), EncodingError> {
self.write_marker(Marker::DRI)?;
self.write_u16(4)?;
self.write_u16(restart_interval)
}
#[inline]
pub fn huffman_encode(&mut self, val: u8, table: &HuffmanTable) -> Result<(), EncodingError> {
crate::prof::bump(crate::prof::Count::Symbols, 1);
let &(size, code) = table.get_for_value(val);
self.write_bits(code as u32, size)
}
#[inline]
pub fn huffman_encode_value(
&mut self,
size: u8,
symbol: u8,
value: u16,
table: &HuffmanTable,
) -> Result<(), EncodingError> {
crate::prof::bump(crate::prof::Count::Symbols, 1);
let &(num_bits, code) = table.get_for_value(symbol);
let mut temp = value as u32;
temp |= (code as u32) << size;
let size = size + num_bits;
self.write_bits(temp, size)
}
pub fn write_block(
&mut self,
block: &[i16; 64],
prev_dc: i16,
dc_table: &HuffmanTable,
ac_table: &HuffmanTable,
) -> Result<(), EncodingError> {
self.write_dc(block[0], prev_dc, dc_table)?;
self.write_ac_block(block, 1, 64, ac_table)
}
pub fn count_block(block: &[i16; 64], prev_dc: i16, dc_freq: &mut [u32], ac_freq: &mut [u32]) {
let (size, _) = get_code(block[0] - prev_dc);
dc_freq[size as usize] += 1;
let mut zero_run: u8 = 0;
for &value in &block[1..64] {
if value == 0 {
zero_run += 1;
} else {
while zero_run > 15 {
ac_freq[0xF0] += 1;
zero_run -= 16;
}
let (size, _) = get_code(value);
ac_freq[usize::from((zero_run << 4) | size)] += 1;
zero_run = 0;
}
}
if zero_run > 0 {
ac_freq[0x00] += 1;
}
}
#[inline]
pub fn write_dc(
&mut self,
value: i16,
prev_dc: i16,
dc_table: &HuffmanTable,
) -> Result<(), EncodingError> {
let diff = value - prev_dc;
let (size, value) = get_code(diff);
self.huffman_encode_value(size, size, value, dc_table)?;
Ok(())
}
pub fn write_ac_block(
&mut self,
block: &[i16; 64],
start: usize,
end: usize,
ac_table: &HuffmanTable,
) -> Result<(), EncodingError> {
let mut mask = nonzero_mask(block);
mask &= u64::MAX << start;
if end < 64 {
mask &= !(u64::MAX << end);
}
let mut prev = start;
while mask != 0 {
let i = mask.trailing_zeros() as usize;
mask &= mask - 1;
let mut zero_run = (i - prev) as u8;
while zero_run > 15 {
self.huffman_encode(0xF0, ac_table)?;
zero_run -= 16;
}
crate::prof::bump(crate::prof::Count::NonZeroAc, 1);
let (size, value) = get_code(block[i]);
self.huffman_encode_value(size, (zero_run << 4) | size, value, ac_table)?;
prev = i + 1;
}
if prev < end {
self.huffman_encode(0x00, ac_table)?;
}
Ok(())
}
pub fn write_frame_header(
&mut self,
width: u16,
height: u16,
components: &[Component],
progressive: bool,
) -> Result<(), EncodingError> {
if progressive {
self.write_marker(Marker::SOF(SOFType::ProgressiveDCT))?;
} else {
self.write_marker(Marker::SOF(SOFType::BaselineDCT))?;
}
self.write_u16(2 + 1 + 2 + 2 + 1 + (components.len() as u16) * 3)?;
self.write_u8(8)?;
self.write_u16(height)?;
self.write_u16(width)?;
self.write_u8(components.len() as u8)?;
for component in components.iter() {
self.write_u8(component.id)?;
self.write_u8(
(component.horizontal_sampling_factor << 4) | component.vertical_sampling_factor,
)?;
self.write_u8(component.quantization_table)?;
}
Ok(())
}
pub fn write_scan_header(
&mut self,
components: &[&Component],
spectral: Option<(u8, u8)>,
) -> Result<(), EncodingError> {
self.write_marker(Marker::SOS)?;
self.write_u16(2 + 1 + (components.len() as u16) * 2 + 3)?;
self.write_u8(components.len() as u8)?;
for component in components.iter() {
self.write_u8(component.id)?;
self.write_u8((component.dc_huffman_table << 4) | component.ac_huffman_table)?;
}
let (spectral_start, spectral_end) = spectral.unwrap_or((0, 63));
self.write_u8(spectral_start)?;
self.write_u8(spectral_end)?;
self.write_u8(0)?;
Ok(())
}
}
#[cfg(all(feature = "simd", any(target_arch = "x86", target_arch = "x86_64")))]
#[target_feature(enable = "avx2")]
unsafe fn nonzero_mask_avx2(block: &[i16; 64]) -> u64 {
#[cfg(target_arch = "x86")]
use core::arch::x86::*;
#[cfg(target_arch = "x86_64")]
use core::arch::x86_64::*;
let p = block.as_ptr();
let zero = _mm256_setzero_si256();
let mut mask = 0u64;
for i in 0..4 {
let v = _mm256_loadu_si256(p.add(i * 16) as *const __m256i);
let eq = _mm256_cmpeq_epi16(v, zero);
let packed = _mm256_packs_epi16(eq, eq);
let ordered = _mm256_permute4x64_epi64::<0b11_01_10_00>(packed);
let m = !(_mm256_movemask_epi8(ordered) as u32) & 0xFFFF;
mask |= (m as u64) << (i * 16);
}
mask
}
#[inline]
fn nonzero_mask_scalar(block: &[i16; 64]) -> u64 {
let mut mask = 0u64;
for (i, &v) in block.iter().enumerate() {
mask |= ((v != 0) as u64) << i;
}
mask
}
#[inline]
fn nonzero_mask(block: &[i16; 64]) -> u64 {
#[cfg(all(feature = "simd", any(target_arch = "x86", target_arch = "x86_64")))]
{
if std::is_x86_feature_detected!("avx2") {
#[allow(unsafe_code)]
unsafe {
return nonzero_mask_avx2(block);
}
}
}
nonzero_mask_scalar(block)
}
#[inline]
pub(crate) fn get_code(value: i16) -> (u8, u16) {
let temp = value - (value.is_negative() as i16);
let temp2 = value.abs();
let num_bits = 15 - (temp2 << 1 | 1).leading_zeros() as u16;
let coefficient = temp & ((1 << num_bits as usize) - 1);
(num_bits as u8, coefficient as u16)
}
#[cfg(test)]
mod nonzero_mask_tests {
use super::*;
#[test]
fn nonzero_mask_matches_scalar() {
let mut state = 0x2545_F491_4F6C_DD1Du64;
let mut next = move || {
state ^= state << 13;
state ^= state >> 7;
state ^= state << 17;
state
};
for round in 0..3000 {
let mut b = [0i16; 64];
for (i, v) in b.iter_mut().enumerate() {
*v = match round {
0 => 0,
1 => 1,
2 => -1,
3 => i16::MIN,
4 => i16::MAX,
5..=68 => {
if i == round - 5 {
1
} else {
0
}
}
_ => {
if next() % 100 < 16 {
(next() % 64) as i16 - 32
} else {
0
}
}
};
}
assert_eq!(nonzero_mask(&b), nonzero_mask_scalar(&b), "round {round}");
}
}
}