Skip to content

Commit 413869b

Browse files
author
HeroicKatora
authored
Merge pull request #193 from vsaase/lossless
Lossless JPEG decoding
2 parents ab6d326 + 4e18d8d commit 413869b

34 files changed

+457
-65
lines changed

.github/workflows/rust.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ jobs:
1212

1313
strategy:
1414
matrix:
15-
rust: ["1.34.2", stable, beta, nightly]
15+
rust: ["1.36.0", stable, beta, nightly]
1616
features: ["", "rayon"]
1717
command: [test, benchmark]
1818

@@ -29,11 +29,11 @@ jobs:
2929
- name: test
3030
run: >
3131
cargo test --tests --benches --no-default-features --features "$FEATURES"
32-
if: ${{ matrix.command == 'test' && matrix.rust != '1.34.2' }}
32+
if: ${{ matrix.command == 'test' && matrix.rust != '1.36.0' }}
3333
env:
3434
FEATURES: ${{ matrix.features }}
3535
- name: benchmark
3636
run: cargo bench --bench decoding_benchmark --no-default-features --features "$FEATURES" -- --warm-up-time 1 --measurement-time 1 --sample-size 25
37-
if: ${{ matrix.command == 'benchmark' && matrix.rust != '1.34.2' }}
37+
if: ${{ matrix.command == 'benchmark' && matrix.rust != '1.36.0' }}
3838
env:
3939
FEATURES: ${{ matrix.features }}

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ This project adheres to [Semantic Versioning](http://semver.org/).
44

55
## Unreleased
66

7+
- Added Lossless JPEG support
8+
- Minimum supported rust version changed to 1.36
9+
710
## v0.1.22 (2021-01-27)
811

912
- Fix panic on jpeg without frames.

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,4 @@ fn main() {
3232
```
3333

3434
## Requirements
35-
This crate compiles only with rust >= 1.34.
35+
This crate compiles only with rust >= 1.36.

benches/decoding_benchmark.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,5 +33,9 @@ fn main() {
3333
c.bench_function("extract metadata from an image", |b| b.iter(|| {
3434
read_metadata(include_bytes!("tower.jpg"))
3535
}));
36+
37+
c.bench_function("decode a 3072x2048 RGB Lossless JPEG", |b| b.iter(|| {
38+
read_image(include_bytes!("../tests/reftest/images/lossless/jpeg_lossless_sel1-rgb.jpg"))
39+
}));
3640
c.final_summary();
3741
}

examples/decode.rs

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,17 +23,27 @@ fn main() {
2323

2424
let output_file = File::create(output_path).unwrap();
2525
let mut encoder = png::Encoder::new(output_file, info.width as u32, info.height as u32);
26-
encoder.set_depth(png::BitDepth::Eight);
2726

2827
match info.pixel_format {
29-
jpeg::PixelFormat::L8 => encoder.set_color(png::ColorType::Grayscale),
30-
jpeg::PixelFormat::RGB24 => encoder.set_color(png::ColorType::RGB),
28+
jpeg::PixelFormat::L16 => {
29+
encoder.set_depth(png::BitDepth::Sixteen);
30+
encoder.set_color(png::ColorType::Grayscale);
31+
},
32+
jpeg::PixelFormat::RGB24 => {
33+
encoder.set_depth(png::BitDepth::Eight);
34+
encoder.set_color(png::ColorType::RGB);
35+
},
3136
jpeg::PixelFormat::CMYK32 => {
3237
data = cmyk_to_rgb(&mut data);
38+
encoder.set_depth(png::BitDepth::Eight);
3339
encoder.set_color(png::ColorType::RGB)
3440
},
35-
};
36-
41+
jpeg::PixelFormat::L8 => {
42+
encoder.set_depth(png::BitDepth::Eight);
43+
encoder.set_color(png::ColorType::Grayscale);
44+
},
45+
}
46+
3747
encoder.write_header()
3848
.expect("writing png header failed")
3949
.write_image_data(&data)

rust-toolchain

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
1.34.2
1+
1.36

src/decoder.rs

Lines changed: 71 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ use worker::{RowData, PlatformWorker, Worker};
1515

1616
pub const MAX_COMPONENTS: usize = 4;
1717

18+
mod lossless;
19+
use self::lossless::compute_image_lossless;
20+
1821
static UNZIGZAG: [u8; 64] = [
1922
0, 1, 8, 16, 9, 2, 3, 10,
2023
17, 24, 32, 25, 18, 11, 4, 5,
@@ -31,6 +34,8 @@ static UNZIGZAG: [u8; 64] = [
3134
pub enum PixelFormat {
3235
/// Luminance (grayscale), 8 bits
3336
L8,
37+
/// Luminance (grayscale), 16 bits
38+
L16,
3439
/// RGB, 8 bits per channel
3540
RGB24,
3641
/// CMYK, 8 bits per channel
@@ -42,6 +47,7 @@ impl PixelFormat {
4247
pub fn pixel_bytes(&self) -> usize {
4348
match self {
4449
PixelFormat::L8 => 1,
50+
PixelFormat::L16 => 2,
4551
PixelFormat::RGB24 => 3,
4652
PixelFormat::CMYK32 => 4,
4753
}
@@ -57,6 +63,8 @@ pub struct ImageInfo {
5763
pub height: u16,
5864
/// The pixel format of the image.
5965
pub pixel_format: PixelFormat,
66+
/// The coding process of the image.
67+
pub coding_process: CodingProcess,
6068
}
6169

6270
/// JPEG decoder
@@ -111,7 +119,11 @@ impl<R: Read> Decoder<R> {
111119
match self.frame {
112120
Some(ref frame) => {
113121
let pixel_format = match frame.components.len() {
114-
1 => PixelFormat::L8,
122+
1 => match frame.precision {
123+
8 => PixelFormat::L8,
124+
16 => PixelFormat::L16,
125+
_ => panic!()
126+
},
115127
3 => PixelFormat::RGB24,
116128
4 => PixelFormat::CMYK32,
117129
_ => panic!(),
@@ -121,6 +133,7 @@ impl<R: Read> Decoder<R> {
121133
width: frame.output_size.width,
122134
height: frame.output_size.height,
123135
pixel_format: pixel_format,
136+
coding_process: frame.coding_process,
124137
})
125138
},
126139
None => None,
@@ -209,7 +222,8 @@ impl<R: Read> Decoder<R> {
209222
let mut pending_marker = None;
210223
let mut worker = None;
211224
let mut scans_processed = 0;
212-
let mut planes = vec![Vec::new(); self.frame.as_ref().map_or(0, |frame| frame.components.len())];
225+
let mut planes = vec![Vec::<u8>::new(); self.frame.as_ref().map_or(0, |frame| frame.components.len())];
226+
let mut planes_u16 = vec![Vec::<u16>::new(); self.frame.as_ref().map_or(0, |frame| frame.components.len())];
213227

214228
loop {
215229
let marker = match pending_marker.take() {
@@ -234,13 +248,13 @@ impl<R: Read> Decoder<R> {
234248
if frame.is_differential {
235249
return Err(Error::Unsupported(UnsupportedFeature::Hierarchical));
236250
}
237-
if frame.coding_process == CodingProcess::Lossless {
238-
return Err(Error::Unsupported(UnsupportedFeature::Lossless));
239-
}
240251
if frame.entropy_coding == EntropyCoding::Arithmetic {
241252
return Err(Error::Unsupported(UnsupportedFeature::ArithmeticEntropyCoding));
242253
}
243-
if frame.precision != 8 {
254+
if frame.precision != 8 && frame.coding_process != CodingProcess::Lossless {
255+
return Err(Error::Unsupported(UnsupportedFeature::SamplePrecision(frame.precision)));
256+
}
257+
if frame.precision != 8 && frame.precision != 16 {
244258
return Err(Error::Unsupported(UnsupportedFeature::SamplePrecision(frame.precision)));
245259
}
246260
if component_count != 1 && component_count != 3 && component_count != 4 {
@@ -257,6 +271,7 @@ impl<R: Read> Decoder<R> {
257271
}
258272

259273
planes = vec![Vec::new(); component_count];
274+
planes_u16 = vec![Vec::new(); component_count];
260275
},
261276

262277
// Scan header
@@ -278,46 +293,57 @@ impl<R: Read> Decoder<R> {
278293
}).collect();
279294
}
280295

281-
// This was previously buggy, so let's explain the log here a bit. When a
282-
// progressive frame is encoded then the coefficients (DC, AC) of each
283-
// component (=color plane) can be split amongst scans. In particular it can
284-
// happen or at least occurs in the wild that a scan contains coefficient 0 of
285-
// all components. If now one but not all components had all other coefficients
286-
// delivered in previous scans then such a scan contains all components but
287-
// completes only some of them! (This is technically NOT permitted for all
288-
// other coefficients as the standard dictates that scans with coefficients
289-
// other than the 0th must only contain ONE component so we would either
290-
// complete it or not. We may want to detect and error in case more component
291-
// are part of a scan than allowed.) What a weird edge case.
292-
//
293-
// But this means we track precisely which components get completed here.
294-
let mut finished = [false; MAX_COMPONENTS];
295-
296-
if scan.successive_approximation_low == 0 {
297-
for (&i, component_finished) in scan.component_indices.iter().zip(&mut finished) {
298-
if self.coefficients_finished[i] == !0 {
299-
continue;
300-
}
301-
for j in scan.spectral_selection.clone() {
302-
self.coefficients_finished[i] |= 1 << j;
303-
}
304-
if self.coefficients_finished[i] == !0 {
305-
*component_finished = true;
306-
}
296+
297+
if frame.coding_process == CodingProcess::Lossless {
298+
let (marker, data) = self.decode_scan_lossless(&frame, &scan)?;
299+
300+
for (i, plane) in data.into_iter().enumerate().filter(|&(_, ref plane)| !plane.is_empty()) {
301+
planes_u16[i] = plane;
307302
}
303+
pending_marker = marker;
308304
}
305+
else {
306+
// This was previously buggy, so let's explain the log here a bit. When a
307+
// progressive frame is encoded then the coefficients (DC, AC) of each
308+
// component (=color plane) can be split amongst scans. In particular it can
309+
// happen or at least occurs in the wild that a scan contains coefficient 0 of
310+
// all components. If now one but not all components had all other coefficients
311+
// delivered in previous scans then such a scan contains all components but
312+
// completes only some of them! (This is technically NOT permitted for all
313+
// other coefficients as the standard dictates that scans with coefficients
314+
// other than the 0th must only contain ONE component so we would either
315+
// complete it or not. We may want to detect and error in case more component
316+
// are part of a scan than allowed.) What a weird edge case.
317+
//
318+
// But this means we track precisely which components get completed here.
319+
let mut finished = [false; MAX_COMPONENTS];
320+
321+
if scan.successive_approximation_low == 0 {
322+
for (&i, component_finished) in scan.component_indices.iter().zip(&mut finished) {
323+
if self.coefficients_finished[i] == !0 {
324+
continue;
325+
}
326+
for j in scan.spectral_selection.clone() {
327+
self.coefficients_finished[i] |= 1 << j;
328+
}
329+
if self.coefficients_finished[i] == !0 {
330+
*component_finished = true;
331+
}
332+
}
333+
}
309334

310-
let (marker, data) = self.decode_scan(&frame, &scan, worker.as_mut().unwrap(), &finished)?;
335+
let (marker, data) = self.decode_scan(&frame, &scan, worker.as_mut().unwrap(), &finished)?;
311336

312-
if let Some(data) = data {
313-
for (i, plane) in data.into_iter().enumerate().filter(|&(_, ref plane)| !plane.is_empty()) {
314-
if self.coefficients_finished[i] == !0 {
315-
planes[i] = plane;
337+
if let Some(data) = data {
338+
for (i, plane) in data.into_iter().enumerate().filter(|&(_, ref plane)| !plane.is_empty()) {
339+
if self.coefficients_finished[i] == !0 {
340+
planes[i] = plane;
341+
}
316342
}
317343
}
344+
pending_marker = marker;
318345
}
319346

320-
pending_marker = marker;
321347
scans_processed += 1;
322348
},
323349

@@ -465,7 +491,12 @@ impl<R: Read> Decoder<R> {
465491
}
466492
}
467493

468-
compute_image(&frame.components, planes, frame.output_size, self.is_jfif, self.color_transform)
494+
if frame.coding_process == CodingProcess::Lossless {
495+
compute_image_lossless(&frame, planes_u16)
496+
}
497+
else{
498+
compute_image(&frame.components, planes, frame.output_size, self.is_jfif, self.color_transform)
499+
}
469500
}
470501

471502
fn read_marker(&mut self) -> Result<Marker> {
@@ -964,6 +995,7 @@ fn compute_image(components: &[Component],
964995
}
965996
}
966997

998+
967999
#[cfg(feature="rayon")]
9681000
fn compute_image_parallel(components: &[Component],
9691001
data: Vec<Vec<u8>>,

0 commit comments

Comments
 (0)