Skip to content

Commit 52bf297

Browse files
committed
Improve API for IDCT scaling
1 parent ae7e1db commit 52bf297

File tree

4 files changed

+71
-20
lines changed

4 files changed

+71
-20
lines changed

src/decoder.rs

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ pub struct ImageInfo {
5050
/// JPEG decoder
5151
pub struct Decoder<R> {
5252
reader: R,
53-
scale: usize,
53+
requested_size: Option<Dimensions>,
5454

5555
frame: Option<FrameInfo>,
5656
dc_huffman_tables: Vec<Option<HuffmanTable>>,
@@ -71,15 +71,26 @@ pub struct Decoder<R> {
7171
impl<R: Read> Decoder<R> {
7272
/// Creates a new `Decoder` using the reader `reader`.
7373
pub fn new(reader: R) -> Decoder<R> {
74-
Decoder::new_scaled(reader, 8)
74+
Decoder::init(reader, None)
7575
}
7676

77-
/// Creates a new `Decoder` using the reader `reader` that scales
78-
/// down the image by a factor of `scale/8`.
79-
pub fn new_scaled(reader: R, scale: usize) -> Decoder<R> {
77+
/// Creates a new `Decoder` using the reader `reader` that returns a
78+
/// scaled image that is equal to or larger than the requested size in at
79+
/// least one axis, or the full size of the image if the requested size is
80+
/// larger.
81+
///
82+
/// This efficiently scales down the image by one of a fixed set of
83+
/// available ratios during decoding. To generate a thumbnail of an
84+
/// exact size, pass the desired size or larger and then scale to the
85+
/// final size using a traditional resampling algorithm.
86+
pub fn scaled(reader: R, requested_width: u16, requested_height: u16) -> Decoder<R> {
87+
Decoder::init(reader, Some(Dimensions{ width: requested_width, height: requested_height }))
88+
}
89+
90+
fn init(reader: R, requested_size: Option<Dimensions>) -> Decoder<R> {
8091
Decoder {
8192
reader: reader,
82-
scale: scale,
93+
requested_size,
8394
frame: None,
8495
dc_huffman_tables: vec![None, None, None, None],
8596
ac_huffman_tables: vec![None, None, None, None],
@@ -161,7 +172,7 @@ impl<R: Read> Decoder<R> {
161172
return Err(Error::Unsupported(UnsupportedFeature::Hierarchical));
162173
}
163174

164-
let frame = parse_sof(&mut self.reader, marker, self.scale)?;
175+
let frame = parse_sof(&mut self.reader, marker, self.requested_size)?;
165176
let component_count = frame.components.len();
166177

167178
if frame.is_differential {

src/idct.rs

Lines changed: 46 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,51 @@
11
// Malicious JPEG files can cause operations in the idct to overflow.
22
// One example is tests/crashtest/images/imagetestsuite/b0b8914cc5f7a6eff409f16d8cc236c5.jpg
33
// That's why wrapping operators are needed.
4+
use crate::parser::Dimensions;
5+
6+
pub fn choose_idct_size(full_size: Dimensions, requested_size: Dimensions) -> usize {
7+
fn scaled(len: u16, scale: usize) -> u16 { ((len as u32 * scale as u32 - 1) / 8 + 1) as u16 }
8+
9+
for &scale in &[1, 2, 4] {
10+
if scaled(full_size.width, scale) >= requested_size.width || scaled(full_size.height, scale) >= requested_size.height {
11+
return scale;
12+
}
13+
}
14+
15+
return 8;
16+
}
17+
18+
#[test]
19+
fn test_choose_idct_size() {
20+
assert_eq!(choose_idct_size(Dimensions{width: 5472, height: 3648}, Dimensions{width: 200, height: 200}), 1);
21+
assert_eq!(choose_idct_size(Dimensions{width: 5472, height: 3648}, Dimensions{width: 500, height: 500}), 1);
22+
assert_eq!(choose_idct_size(Dimensions{width: 5472, height: 3648}, Dimensions{width: 684, height: 456}), 1);
23+
assert_eq!(choose_idct_size(Dimensions{width: 5472, height: 3648}, Dimensions{width: 999, height: 456}), 1);
24+
assert_eq!(choose_idct_size(Dimensions{width: 5472, height: 3648}, Dimensions{width: 684, height: 999}), 1);
25+
assert_eq!(choose_idct_size(Dimensions{width: 500, height: 333}, Dimensions{width: 63, height: 42}), 1);
26+
27+
assert_eq!(choose_idct_size(Dimensions{width: 5472, height: 3648}, Dimensions{width: 685, height: 999}), 2);
28+
assert_eq!(choose_idct_size(Dimensions{width: 5472, height: 3648}, Dimensions{width: 1000, height: 1000}), 2);
29+
assert_eq!(choose_idct_size(Dimensions{width: 5472, height: 3648}, Dimensions{width: 1400, height: 1400}), 4);
30+
31+
assert_eq!(choose_idct_size(Dimensions{width: 5472, height: 3648}, Dimensions{width: 5472, height: 3648}), 8);
32+
assert_eq!(choose_idct_size(Dimensions{width: 5472, height: 3648}, Dimensions{width: 16384, height: 16384}), 8);
33+
assert_eq!(choose_idct_size(Dimensions{width: 1, height: 1}, Dimensions{width: 65535, height: 65535}), 8);
34+
assert_eq!(choose_idct_size(Dimensions{width: 5472, height: 3648}, Dimensions{width: 16384, height: 16384}), 8);
35+
}
36+
37+
pub fn dequantize_and_idct_block(scale: usize, coefficients: &[i16], quantization_table: &[u16; 64], output_linestride: usize, output: &mut [u8]) {
38+
match scale {
39+
8 => dequantize_and_idct_block_8x8(coefficients, quantization_table, output_linestride, output),
40+
4 => dequantize_and_idct_block_4x4(coefficients, quantization_table, output_linestride, output),
41+
2 => dequantize_and_idct_block_2x2(coefficients, quantization_table, output_linestride, output),
42+
1 => dequantize_and_idct_block_1x1(coefficients, quantization_table, output_linestride, output),
43+
_ => panic!("Unsupported IDCT scale {}/8", scale),
44+
}
45+
}
446

547
// This is based on stb_image's 'stbi__idct_block'.
6-
pub fn dequantize_and_idct_block_8x8(coefficients: &[i16], quantization_table: &[u16; 64], output_linestride: usize, output: &mut [u8]) {
48+
fn dequantize_and_idct_block_8x8(coefficients: &[i16], quantization_table: &[u16; 64], output_linestride: usize, output: &mut [u8]) {
749
debug_assert_eq!(coefficients.len(), 64);
850

951
let mut temp = [0i32; 64];
@@ -157,7 +199,7 @@ pub fn dequantize_and_idct_block_8x8(coefficients: &[i16], quantization_table: &
157199

158200
// 4x4 and 2x2 IDCT based on Rakesh Dugad and Narendra Ahuja: "A Fast Scheme for Image Size Change in the Compressed Domain" (2001).
159201
// http://sylvana.net/jpegcrop/jidctred/
160-
pub fn dequantize_and_idct_block_4x4(coefficients: &[i16], quantization_table: &[u16; 64], output_linestride: usize, output: &mut [u8]) {
202+
fn dequantize_and_idct_block_4x4(coefficients: &[i16], quantization_table: &[u16; 64], output_linestride: usize, output: &mut [u8]) {
161203
debug_assert_eq!(coefficients.len(), 64);
162204
let mut temp = [0i32; 4*4];
163205

@@ -214,7 +256,7 @@ pub fn dequantize_and_idct_block_4x4(coefficients: &[i16], quantization_table: &
214256
}
215257
}
216258

217-
pub fn dequantize_and_idct_block_2x2(coefficients: &[i16], quantization_table: &[u16; 64], output_linestride: usize, output: &mut [u8]) {
259+
fn dequantize_and_idct_block_2x2(coefficients: &[i16], quantization_table: &[u16; 64], output_linestride: usize, output: &mut [u8]) {
218260
debug_assert_eq!(coefficients.len(), 64);
219261

220262
const SCALE_BITS: u32 = 3;
@@ -245,7 +287,7 @@ pub fn dequantize_and_idct_block_2x2(coefficients: &[i16], quantization_table: &
245287
output[output_linestride + 1] = stbi_clamp(x2.wrapping_sub(x3).wrapping_shr(SCALE_BITS));
246288
}
247289

248-
pub fn dequantize_and_idct_block_1x1(coefficients: &[i16], quantization_table: &[u16; 64], _output_linestride: usize, output: &mut [u8]) {
290+
fn dequantize_and_idct_block_1x1(coefficients: &[i16], quantization_table: &[u16; 64], _output_linestride: usize, output: &mut [u8]) {
249291
debug_assert_eq!(coefficients.len(), 64);
250292

251293
let s0 = (coefficients[0] as i32 * quantization_table[0] as i32).wrapping_add(128 * 8) / 8;

src/parser.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ fn skip_bytes<R: Read>(reader: &mut R, length: usize) -> Result<()> {
107107
}
108108

109109
// Section B.2.2
110-
pub fn parse_sof<R: Read>(reader: &mut R, marker: Marker, scale: usize) -> Result<FrameInfo> {
110+
pub fn parse_sof<R: Read>(reader: &mut R, marker: Marker, requested_size: Option<Dimensions>) -> Result<FrameInfo> {
111111
let length = read_length(reader, marker)?;
112112

113113
if length <= 6 {
@@ -159,6 +159,10 @@ pub fn parse_sof<R: Read>(reader: &mut R, marker: Marker, scale: usize) -> Resul
159159
return Err(Error::Format("zero width in frame header".to_owned()));
160160
}
161161

162+
let scale = if let Some(req) = requested_size {
163+
crate::idct::choose_idct_size(Dimensions { width, height }, req)
164+
} else { 8 };
165+
162166
let component_count = reader.read_u8()?;
163167

164168
if component_count == 0 {

src/worker/immediate.rs

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use decoder::MAX_COMPONENTS;
22
use error::Result;
3-
use idct::{ dequantize_and_idct_block_8x8, dequantize_and_idct_block_4x4, dequantize_and_idct_block_2x2, dequantize_and_idct_block_1x1 };
3+
use idct::dequantize_and_idct_block;
44
use std::mem;
55
use std::sync::Arc;
66
use parser::Component;
@@ -47,13 +47,7 @@ impl ImmediateWorker {
4747
let coefficients = &data[i * 64..(i + 1) * 64];
4848
let output = &mut self.results[index][self.offsets[index] + y * line_stride + x..];
4949

50-
match component.dct_scale {
51-
8 => dequantize_and_idct_block_8x8(coefficients, quantization_table, line_stride, output),
52-
4 => dequantize_and_idct_block_4x4(coefficients, quantization_table, line_stride, output),
53-
2 => dequantize_and_idct_block_2x2(coefficients, quantization_table, line_stride, output),
54-
1 => dequantize_and_idct_block_1x1(coefficients, quantization_table, line_stride, output),
55-
_ => unimplemented!(),
56-
}
50+
dequantize_and_idct_block(component.dct_scale, coefficients, quantization_table, line_stride, output);
5751
}
5852

5953
self.offsets[index] += block_count * component.dct_scale * component.dct_scale;

0 commit comments

Comments
 (0)