Skip to content

Commit 5f65ebe

Browse files
author
HeroicKatora
authored
Merge pull request #150 from lovasoa/optimize_compute_image
Optimize final grayscale image creation
2 parents 0920c4c + 7e220e8 commit 5f65ebe

File tree

3 files changed

+32
-28
lines changed

3 files changed

+32
-28
lines changed

benches/decoding_benchmark.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@ fn main() {
2626
read_image(include_bytes!("tower_progressive.jpg"))
2727
}));
2828

29+
c.bench_function("decode a 512x512 grayscale JPEG", |b| b.iter(|| {
30+
read_image(include_bytes!("tower_grayscale.jpg"))
31+
}));
32+
2933
c.bench_function("extract metadata from an image", |b| b.iter(|| {
3034
read_metadata(include_bytes!("tower.jpg"))
3135
}));

benches/tower_grayscale.jpg

53 KB
Loading

src/decoder.rs

Lines changed: 28 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -346,7 +346,7 @@ impl<R: Read> Decoder<R> {
346346
}
347347

348348
let frame = self.frame.as_ref().unwrap();
349-
compute_image(&frame.components, &planes, frame.output_size, self.is_jfif, self.color_transform)
349+
compute_image(&frame.components, planes, frame.output_size, self.is_jfif, self.color_transform)
350350
}
351351

352352
fn read_marker(&mut self) -> Result<Marker> {
@@ -766,7 +766,7 @@ fn refine_non_zeroes<R: Read>(reader: &mut R,
766766
}
767767

768768
fn compute_image(components: &[Component],
769-
data: &[Vec<u8>],
769+
mut data: Vec<Vec<u8>>,
770770
output_size: Dimensions,
771771
is_jfif: bool,
772772
color_transform: Option<AdobeColorTransform>) -> Result<Vec<u8>> {
@@ -776,29 +776,29 @@ fn compute_image(components: &[Component],
776776

777777
if components.len() == 1 {
778778
let component = &components[0];
779-
let decoded = &data[0];
779+
let mut decoded: Vec<u8> = data.remove(0);
780780

781781
let width = component.size.width as usize;
782782
let height = component.size.height as usize;
783783
let size = width * height;
784-
785-
// if the image size is a multiple of the block size
786-
if decoded.len() == size {
787-
return Ok(decoded.to_vec())
788-
}
789-
790-
let mut buffer = vec![0u8; size];
791784
let line_stride = component.block_size.width as usize * component.dct_scale;
792785

793-
for y in 0..height {
794-
let destination_idx = y * width;
795-
let source_idx = y * line_stride;
796-
let destination = &mut buffer[destination_idx..][..width];
797-
let source = &decoded[source_idx..][..width];
798-
destination.copy_from_slice(source);
786+
// if the image width is a multiple of the block size,
787+
// then we don't have to move bytes in the decoded data
788+
if usize::from(output_size.width) != line_stride {
789+
let mut buffer = vec![0u8; width];
790+
// The first line already starts at index 0, so we need to move only lines 1..height
791+
for y in 1..height {
792+
let destination_idx = y * width;
793+
let source_idx = y * line_stride;
794+
// We could use copy_within, but we need to support old rust versions
795+
buffer.copy_from_slice(&decoded[source_idx..][..width]);
796+
let destination = &mut decoded[destination_idx..][..width];
797+
destination.copy_from_slice(&buffer);
798+
}
799799
}
800-
801-
Ok(buffer)
800+
decoded.resize(size, 0);
801+
Ok(decoded)
802802
}
803803
else {
804804
compute_image_parallel(components, data, output_size, is_jfif, color_transform)
@@ -807,10 +807,10 @@ fn compute_image(components: &[Component],
807807

808808
#[cfg(feature="rayon")]
809809
fn compute_image_parallel(components: &[Component],
810-
data: &[Vec<u8>],
811-
output_size: Dimensions,
812-
is_jfif: bool,
813-
color_transform: Option<AdobeColorTransform>) -> Result<Vec<u8>> {
810+
data: Vec<Vec<u8>>,
811+
output_size: Dimensions,
812+
is_jfif: bool,
813+
color_transform: Option<AdobeColorTransform>) -> Result<Vec<u8>> {
814814
use rayon::prelude::*;
815815

816816
let color_convert_func = choose_color_convert_func(components.len(), is_jfif, color_transform)?;
@@ -822,7 +822,7 @@ fn compute_image_parallel(components: &[Component],
822822
.with_max_len(1)
823823
.enumerate()
824824
.for_each(|(row, line)| {
825-
upsampler.upsample_and_interleave_row(data, row, output_size.width as usize, line);
825+
upsampler.upsample_and_interleave_row(&data, row, output_size.width as usize, line);
826826
color_convert_func(line, output_size.width as usize);
827827
});
828828

@@ -831,18 +831,18 @@ fn compute_image_parallel(components: &[Component],
831831

832832
#[cfg(not(feature="rayon"))]
833833
fn compute_image_parallel(components: &[Component],
834-
data: &[Vec<u8>],
835-
output_size: Dimensions,
836-
is_jfif: bool,
837-
color_transform: Option<AdobeColorTransform>) -> Result<Vec<u8>> {
834+
data: Vec<Vec<u8>>,
835+
output_size: Dimensions,
836+
is_jfif: bool,
837+
color_transform: Option<AdobeColorTransform>) -> Result<Vec<u8>> {
838838
let color_convert_func = choose_color_convert_func(components.len(), is_jfif, color_transform)?;
839839
let upsampler = Upsampler::new(components, output_size.width, output_size.height)?;
840840
let line_size = output_size.width as usize * components.len();
841841
let mut image = vec![0u8; line_size * output_size.height as usize];
842842

843843
for (row, line) in image.chunks_mut(line_size)
844844
.enumerate() {
845-
upsampler.upsample_and_interleave_row(data, row, output_size.width as usize, line);
845+
upsampler.upsample_and_interleave_row(&data, row, output_size.width as usize, line);
846846
color_convert_func(line, output_size.width as usize);
847847
}
848848

0 commit comments

Comments
 (0)