Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,15 @@ jobs:
run: cargo build --verbose --no-default-features
- name: Run tests
run: cargo test --verbose --no-default-features --tests

rustfmt:

runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt
- name: Run rustfmt check
run: cargo fmt -- --check
2 changes: 1 addition & 1 deletion src/avx2/fdct.rs
Original file line number Diff line number Diff line change
Expand Up @@ -481,4 +481,4 @@ fn avx_store(input: __m256i, output: &mut [i16]) {
assert!(core::mem::size_of::<[i16; 16]>() == core::mem::size_of::<__m256i>());
// SAFETY: we've checked sizes above. The load is unaligned, so no alignment requirements.
unsafe { _mm256_storeu_si256(output.as_mut_ptr() as *mut __m256i, input) }
}
}
10 changes: 6 additions & 4 deletions src/avx2/ycbcr.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
#[cfg(target_arch = "x86")]
use core::arch::x86::{
__m256i, _mm256_add_epi32, _mm256_mullo_epi32, _mm256_set1_epi32, _mm256_set_epi32,
__m256i, _mm256_add_epi32, _mm256_mullo_epi32, _mm256_set_epi32, _mm256_set1_epi32,
_mm256_srli_epi32, _mm256_sub_epi32,
};

#[cfg(target_arch = "x86_64")]
use core::arch::x86_64::{
__m256i, _mm256_add_epi32, _mm256_mullo_epi32, _mm256_set1_epi32, _mm256_set_epi32,
__m256i, _mm256_add_epi32, _mm256_mullo_epi32, _mm256_set_epi32, _mm256_set1_epi32,
_mm256_srli_epi32, _mm256_sub_epi32,
};

use alloc::vec::Vec;

use crate::{rgb_to_ycbcr, ImageBuffer, JpegColorType};
use crate::{ImageBuffer, JpegColorType, rgb_to_ycbcr};

macro_rules! ycbcr_image_avx2 {
($name:ident, $num_colors:expr, $o1:expr, $o2:expr, $o3:expr) => {
Expand Down Expand Up @@ -229,7 +229,9 @@ mod tests {
for (i, pixel) in scalar_result.iter().copied().enumerate() {
let avx_pixel: [u8; 3] = [buffers[0][i], buffers[1][i], buffers[2][i]];
if pixel != avx_pixel {
panic!("Mismatch at index {i}: scalar result is {pixel:?}, avx result is {avx_pixel:?}");
panic!(
"Mismatch at index {i}: scalar result is {pixel:?}, avx result is {avx_pixel:?}"
);
}
}
}
Expand Down
14 changes: 4 additions & 10 deletions src/encoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use crate::image_buffer::*;
use crate::marker::Marker;
use crate::quantization::{QuantizationTable, QuantizationTableType};
use crate::writer::{JfifWrite, JfifWriter, ZIGZAG};
use crate::{PixelDensity, EncodingError};
use crate::{EncodingError, PixelDensity};

use alloc::vec;
use alloc::vec::Vec;
Expand Down Expand Up @@ -353,11 +353,7 @@ impl<W: JfifWrite> Encoder<W> {
/// # Errors
///
/// Returns an error if the segment number is invalid or data exceeds the allowed size
pub fn add_app_segment(
&mut self,
segment_nr: u8,
data: Vec<u8>,
) -> Result<(), EncodingError> {
pub fn add_app_segment(&mut self, segment_nr: u8, data: Vec<u8>) -> Result<(), EncodingError> {
if segment_nr == 0 || segment_nr > 15 {
Err(EncodingError::InvalidAppSegment(segment_nr))
} else if data.len() > 65533 {
Expand Down Expand Up @@ -749,10 +745,8 @@ impl<W: JfifWrite> Encoder<W> {
&row[i],
block_x * 8 * max_h_sampling + (h_offset * 8),
v_offset * 8,
max_h_sampling
/ component.horizontal_sampling_factor as usize,
max_v_sampling
/ component.vertical_sampling_factor as usize,
max_h_sampling / component.horizontal_sampling_factor as usize,
max_v_sampling / component.vertical_sampling_factor as usize,
buffer_width,
);

Expand Down
2 changes: 0 additions & 2 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,6 @@ impl Display for EncodingError {
}

impl Error for EncodingError {

#[cfg(feature = "std")]
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
Expand All @@ -74,4 +73,3 @@ impl Error for EncodingError {
}
}
}

10 changes: 2 additions & 8 deletions src/fdct.rs
Original file line number Diff line number Diff line change
Expand Up @@ -134,14 +134,8 @@ pub fn fdct(data: &mut [i16; 64]) {
data2[offset + 4] = (tmp10 - tmp11) << PASS1_BITS;

let z1 = (tmp12 + tmp13) * FIX_0_541196100;
data2[offset + 2] = descale(
z1 + (tmp13 * FIX_0_765366865),
CONST_BITS - PASS1_BITS,
);
data2[offset + 6] = descale(
z1 + (tmp12 * -FIX_1_847759065),
CONST_BITS - PASS1_BITS,
);
data2[offset + 2] = descale(z1 + (tmp13 * FIX_0_765366865), CONST_BITS - PASS1_BITS);
data2[offset + 6] = descale(z1 + (tmp12 * -FIX_1_847759065), CONST_BITS - PASS1_BITS);

/* Odd part per figure 8 --- note paper omits factor of sqrt(2).
* cK represents cos(K*pi/16).
Expand Down
34 changes: 13 additions & 21 deletions src/image_buffer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,11 +122,11 @@ impl<'a> ImageBuffer for GrayImage<'a> {
}

#[inline(always)]
fn get_line(data: &[u8], y: u16, width:u16, num_colors: usize) -> &[u8] {
let width= usize::from(width);
fn get_line(data: &[u8], y: u16, width: u16, num_colors: usize) -> &[u8] {
let width = usize::from(width);
let y = usize::from(y);

let start = y *width * num_colors;
let start = y * width * num_colors;
let end = start + width * num_colors;

&data[start..end]
Expand Down Expand Up @@ -155,13 +155,13 @@ macro_rules! ycbcr_image {

// Doing the convertion in chunks allows the compiler to vectorize the code
// A size of 16 seems optimal for SSE and AVX capable hardware
const CHUNK_SIZE:usize = 16;
const CHUNK_SIZE: usize = 16;

let mut y_buffer = [0;CHUNK_SIZE];
let mut cb_buffer = [0;CHUNK_SIZE];
let mut cr_buffer = [0;CHUNK_SIZE];
let mut y_buffer = [0; CHUNK_SIZE];
let mut cb_buffer = [0; CHUNK_SIZE];
let mut cr_buffer = [0; CHUNK_SIZE];

for chuck in line.chunks_exact($num_colors*CHUNK_SIZE) {
for chuck in line.chunks_exact($num_colors * CHUNK_SIZE) {
for i in (0..CHUNK_SIZE) {
let (y, cb, cr) = rgb_to_ycbcr(
chuck[i * $num_colors + $o1],
Expand All @@ -182,11 +182,11 @@ macro_rules! ycbcr_image {
// Add the remaining pixels in case the number of
// pixels is not a multiple of CHUNK_SIZE
let pixel = line.len() / $num_colors;
for i in pixel/CHUNK_SIZE*CHUNK_SIZE..pixel {
for i in pixel / CHUNK_SIZE * CHUNK_SIZE..pixel {
let (y, cb, cr) = rgb_to_ycbcr(
line[i*$num_colors + $o1],
line[i*$num_colors + $o2],
line[i*$num_colors + $o3],
line[i * $num_colors + $o1],
line[i * $num_colors + $o2],
line[i * $num_colors + $o3],
);

buffers[0].push(y);
Expand Down Expand Up @@ -275,13 +275,7 @@ impl<'a> ImageBuffer for CmykAsYcckImage<'a> {
let line = get_line(self.0, y, self.width(), 4);

for pixel in line.chunks_exact(4) {

let (y, cb, cr, k) = cmyk_to_ycck(
pixel[0],
pixel[1],
pixel[2],
pixel[3],
);
let (y, cb, cr, k) = cmyk_to_ycck(pixel[0], pixel[1], pixel[2], pixel[3]);

buffers[0].push(y);
buffers[1].push(cb);
Expand Down Expand Up @@ -310,7 +304,6 @@ impl<'a> ImageBuffer for YcckImage<'a> {
let line = get_line(self.0, y, self.width(), 4);

for pixel in line.chunks_exact(4) {

buffers[0].push(pixel[0]);
buffers[1].push(pixel[1]);
buffers[2].push(pixel[2]);
Expand All @@ -330,7 +323,6 @@ mod tests {

#[test]
fn test_rgb_to_ycbcr() {

assert_rgb_to_ycbcr([0, 0, 0], [0, 128, 128]);
assert_rgb_to_ycbcr([255, 255, 255], [255, 128, 128]);
assert_rgb_to_ycbcr([255, 0, 0], [76, 85, 255]);
Expand Down
56 changes: 36 additions & 20 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,20 +44,28 @@ mod writer;

pub use encoder::{ColorType, Encoder, JpegColorType, SamplingFactor};
pub use error::EncodingError;
pub use image_buffer::{cmyk_to_ycck, rgb_to_ycbcr, ImageBuffer};
pub use image_buffer::{ImageBuffer, cmyk_to_ycck, rgb_to_ycbcr};
pub use quantization::QuantizationTableType;
pub use writer::{PixelDensity, PixelDensityUnit, JfifWrite};
pub use writer::{JfifWrite, PixelDensity, PixelDensityUnit};

#[cfg(feature = "benchmark")]
pub use fdct::fdct;

#[cfg(feature = "benchmark")]
pub use image_buffer::RgbImage;

#[cfg(all(feature = "benchmark", feature = "simd", any(target_arch = "x86", target_arch = "x86_64")))]
#[cfg(all(
feature = "benchmark",
feature = "simd",
any(target_arch = "x86", target_arch = "x86_64")
))]
pub use avx2::fdct_avx2;

#[cfg(all(feature = "benchmark", feature = "simd", any(target_arch = "x86", target_arch = "x86_64")))]
#[cfg(all(
feature = "benchmark",
feature = "simd",
any(target_arch = "x86", target_arch = "x86_64")
))]
pub use avx2::RgbImageAVX2;

#[cfg(test)]
Expand Down Expand Up @@ -412,10 +420,12 @@ mod tests {
.encode(&data, width, height, ColorType::Rgb)
.unwrap();

assert!(result
.as_slice()
.windows(DRI_DATA.len())
.any(|w| w == DRI_DATA));
assert!(
result
.as_slice()
.windows(DRI_DATA.len())
.any(|w| w == DRI_DATA)
);

check_result(data, width, height, &mut result, PixelFormat::RGB24);
}
Expand All @@ -435,10 +445,12 @@ mod tests {
.encode(&data, width, height, ColorType::Rgb)
.unwrap();

assert!(result
.as_slice()
.windows(DRI_DATA.len())
.any(|w| w == DRI_DATA));
assert!(
result
.as_slice()
.windows(DRI_DATA.len())
.any(|w| w == DRI_DATA)
);

check_result(data, width, height, &mut result, PixelFormat::RGB24);
}
Expand All @@ -458,10 +470,12 @@ mod tests {
.encode(&data, width, height, ColorType::Rgb)
.unwrap();

assert!(result
.as_slice()
.windows(DRI_DATA.len())
.any(|w| w == DRI_DATA));
assert!(
result
.as_slice()
.windows(DRI_DATA.len())
.any(|w| w == DRI_DATA)
);

check_result(data, width, height, &mut result, PixelFormat::RGB24);
}
Expand All @@ -481,10 +495,12 @@ mod tests {

let segment_data = b"\xEF\0\x09HOHOHO\0";

assert!(result
.as_slice()
.windows(segment_data.len())
.any(|w| w == segment_data));
assert!(
result
.as_slice()
.windows(segment_data.len())
.any(|w| w == segment_data)
);
}

#[test]
Expand Down
2 changes: 1 addition & 1 deletion src/writer.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use crate::EncodingError;
use crate::encoder::Component;
use crate::huffman::{CodingClass, HuffmanTable};
use crate::marker::{Marker, SOFType};
use crate::quantization::QuantizationTable;
use crate::EncodingError;

/// Represents the pixel density of an image
///
Expand Down
Loading