Skip to content

Commit 7140296

Browse files
Add support for row pitch in ImageView and ImageViewMut (image-rs#66)
1 parent 511649a commit 7140296

23 files changed

+998
-655
lines changed

src/decode/astc.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -84,13 +84,12 @@ macro_rules! astc_decoder {
8484
process_blocks,
8585
)
8686
},
87-
|RArgs(r, out, row_pitch, rect, context)| {
87+
|RArgs(r, out, offset, context)| {
8888
for_each_block_rect_untyped::<BLOCK_WIDTH, BLOCK_HEIGHT, BYTES_PER_BLOCK>(
8989
r,
9090
out,
91-
row_pitch,
91+
offset,
9292
context,
93-
rect,
9493
NATIVE_COLOR,
9594
process_blocks,
9695
)

src/decode/bc.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,12 @@ macro_rules! underlying {
3939
process_blocks,
4040
)
4141
},
42-
|RArgs(r, out, row_pitch, rect, context)| {
42+
|RArgs(r, out, offset, context)| {
4343
for_each_block_rect_untyped::<4, 4, BYTES_PER_BLOCK>(
4444
r,
4545
out,
46-
row_pitch,
46+
offset,
4747
context,
48-
rect,
4948
NATIVE_COLOR,
5049
process_blocks,
5150
)

src/decode/bi_planar.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,13 +42,12 @@ macro_rules! underlying {
4242
|Args(r, out, context)| {
4343
for_each_bi_planar(r, out, context, NATIVE_COLOR, INFO, process_bi_planar)
4444
},
45-
|RArgs(r, out, row_pitch, rect, context)| {
45+
|RArgs(r, out, offset, context)| {
4646
for_each_bi_planar_rect(
4747
r,
4848
out,
49-
row_pitch,
49+
offset,
5050
context,
51-
rect,
5251
NATIVE_COLOR,
5352
INFO,
5453
process_bi_planar,

src/decode/decoder.rs

Lines changed: 27 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use std::io::{Read, Seek};
22
use std::mem::size_of;
33

44
use crate::{
5-
Channels, ColorFormat, ColorFormatSet, DecodingError, ImageViewMut, Precision, Rect, Size,
5+
Channels, ColorFormat, ColorFormatSet, DecodingError, ImageViewMut, Offset, Precision, Size,
66
};
77

88
use super::DecodeOptions;
@@ -11,8 +11,7 @@ pub(crate) type DecodeFn = fn(args: Args) -> Result<(), DecodingError>;
1111
pub(crate) type DecodeRectFn = fn(args: RArgs) -> Result<(), DecodingError>;
1212

1313
pub(crate) struct DecodeContext {
14-
pub color: ColorFormat,
15-
pub size: Size,
14+
pub surface_size: Size,
1615
pub memory_limit: usize,
1716
}
1817
impl DecodeContext {
@@ -51,75 +50,18 @@ impl<T: Read + Seek> ReadSeek for T {}
5150
/// The "fix" is to wrap all mutable references in a struct so that compiler
5251
/// can't see them in the type signature of the function pointer anymore. Truly
5352
/// silly, and thankfully not necessary on never compiler versions.
54-
pub(crate) struct Args<'a, 'b>(pub &'a mut dyn Read, pub &'b mut [u8], pub DecodeContext);
55-
impl<'a, 'b> Args<'a, 'b> {
56-
pub fn new(
57-
reader: &'a mut dyn Read,
58-
output: &'b mut [u8],
59-
context: DecodeContext,
60-
) -> Result<Self, DecodingError> {
61-
let bytes_per_pixel = context.color.bytes_per_pixel() as u64;
62-
assert_eq!(
63-
output.len() as u64,
64-
context.size.pixels().saturating_mul(bytes_per_pixel)
65-
);
66-
67-
Ok(Self(reader, output, context))
68-
}
69-
}
53+
pub(crate) struct Args<'a, 'b, 'c>(
54+
pub &'a mut dyn Read,
55+
pub &'b mut ImageViewMut<'c>,
56+
pub DecodeContext,
57+
);
7058

71-
pub(crate) struct RArgs<'a, 'b>(
59+
pub(crate) struct RArgs<'a, 'b, 'c>(
7260
pub &'a mut dyn ReadSeek,
73-
pub &'b mut [u8],
74-
pub usize,
75-
pub Rect,
61+
pub &'b mut ImageViewMut<'c>,
62+
pub Offset,
7663
pub DecodeContext,
7764
);
78-
impl<'a, 'b> RArgs<'a, 'b> {
79-
pub fn new(
80-
reader: &'a mut dyn ReadSeek,
81-
output: &'b mut [u8],
82-
row_pitch: usize,
83-
rect: Rect,
84-
context: DecodeContext,
85-
) -> Result<Self, DecodingError> {
86-
// Check that the rect is within the bounds of the image.
87-
if !rect.is_within_bounds(context.size) {
88-
return Err(DecodingError::RectOutOfBounds);
89-
}
90-
91-
// Check row pitch
92-
let min_row_pitch = if rect.size().is_empty() {
93-
0
94-
} else {
95-
usize::saturating_mul(
96-
rect.width as usize,
97-
context.color.bytes_per_pixel() as usize,
98-
)
99-
};
100-
if row_pitch < min_row_pitch {
101-
return Err(DecodingError::RowPitchTooSmall {
102-
required_minimum: min_row_pitch,
103-
});
104-
}
105-
106-
// Check that the buffer is long enough
107-
// saturate to usize::MAX on overflow
108-
let required_bytes = if rect.size().is_empty() {
109-
0
110-
} else {
111-
usize::saturating_mul(row_pitch, (rect.height - 1) as usize)
112-
.saturating_add(min_row_pitch)
113-
};
114-
if output.len() < required_bytes {
115-
return Err(DecodingError::RectBufferTooSmall {
116-
required_minimum: required_bytes,
117-
});
118-
}
119-
120-
Ok(Self(reader, output, row_pitch, rect, context))
121-
}
122-
}
12365

12466
/// Contains decode functions directly. These functions can be used as is.
12567
pub(crate) struct Decoder {
@@ -219,21 +161,20 @@ impl DecoderSet {
219161
pub fn decode(
220162
&self,
221163
reader: &mut dyn Read,
222-
mut image: ImageViewMut,
164+
image: &mut ImageViewMut,
223165
options: &DecodeOptions,
224166
) -> Result<(), DecodingError> {
225167
let color = image.color();
226168
let size = image.size();
227169

228-
let args = Args::new(
170+
let args = Args(
229171
reader,
230-
image.data(),
172+
image,
231173
DecodeContext {
232-
color,
233-
size,
174+
surface_size: size,
234175
memory_limit: options.memory_limit,
235176
},
236-
)?;
177+
);
237178

238179
// never decode empty images
239180
if size.is_empty() {
@@ -251,33 +192,27 @@ impl DecoderSet {
251192
(decoder.decode_fn)(args)
252193
}
253194

254-
#[allow(clippy::too_many_arguments)]
255195
pub fn decode_rect(
256196
&self,
257-
color: ColorFormat,
258197
reader: &mut dyn ReadSeek,
259-
size: Size,
260-
rect: Rect,
261-
output: &mut [u8],
262-
row_pitch: usize,
198+
image: &mut ImageViewMut,
199+
offset: Offset,
200+
surface_size: Size,
263201
options: &DecodeOptions,
264202
) -> Result<(), DecodingError> {
265-
let args = RArgs::new(
203+
let color = image.color();
204+
205+
debug_assert!(!image.size().is_empty());
206+
207+
let args = RArgs(
266208
reader,
267-
output,
268-
row_pitch,
269-
rect,
209+
image,
210+
offset,
270211
DecodeContext {
271-
color,
272-
size,
212+
surface_size,
273213
memory_limit: options.memory_limit,
274214
},
275-
)?;
276-
277-
// never decode empty rectangles
278-
if rect.size().is_empty() {
279-
return Ok(());
280-
}
215+
);
281216

282217
let decoder = self.get_decoder(color);
283218
(decoder.decode_rect_fn)(args)

src/decode/mod.rs

Lines changed: 34 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ pub(crate) use decoder::*;
1818
use sub_sampled::*;
1919
use uncompressed::*;
2020

21-
use crate::{ColorFormat, DecodingError, Format, ImageViewMut, Rect, Size};
21+
use crate::{util, DecodingError, Format, ImageViewMut, Offset, PixelInfo, Size};
2222

2323
pub(crate) const fn get_decoders(format: Format) -> DecoderSet {
2424
match format {
@@ -129,20 +129,18 @@ pub(crate) const fn get_decoders(format: Format) -> DecoderSet {
129129
/// This method will only panic in the given reader panics while reading.
130130
pub fn decode(
131131
reader: &mut dyn Read,
132-
image: ImageViewMut,
132+
mut image: ImageViewMut,
133133
format: Format,
134134
options: &DecodeOptions,
135135
) -> Result<(), DecodingError> {
136-
get_decoders(format).decode(reader, image, options)
136+
get_decoders(format).decode(reader, &mut image, options)
137137
}
138138

139139
/// Decodes a rectangle of the image data of a surface from the given reader
140140
/// and writes it to the given output buffer.
141141
///
142-
/// ## Row pitch and the output buffer
143-
///
144-
/// The `row_pitch` parameter specifies the number of bytes between the start
145-
/// of one row and the start of the next row in the output buffer.
142+
/// Note: The rectangle to decode is defined by `offset` and the size of
143+
/// `image`. The rectangle must be within the bounds of the surface size.
146144
///
147145
/// ## State of the reader
148146
///
@@ -159,20 +157,41 @@ pub fn decode(
159157
/// ## Panics
160158
///
161159
/// This method will only panic in the given reader panics while reading.
162-
#[allow(clippy::too_many_arguments)]
163160
pub fn decode_rect<R: Read + Seek>(
164161
reader: &mut R,
165-
output: &mut [u8],
166-
row_pitch: usize,
167-
color: ColorFormat,
168-
size: Size,
169-
rect: Rect,
162+
mut image: ImageViewMut,
163+
offset: Offset,
164+
surface_size: Size,
170165
format: Format,
171166
options: &DecodeOptions,
172167
) -> Result<(), DecodingError> {
173168
let reader = reader as &mut dyn ReadSeek;
174-
let decoders = get_decoders(format);
175-
decoders.decode_rect(color, reader, size, rect, output, row_pitch, options)
169+
170+
return inner(reader, &mut image, offset, surface_size, format, options);
171+
172+
fn inner(
173+
reader: &mut dyn ReadSeek,
174+
image: &mut ImageViewMut,
175+
offset: Offset,
176+
surface_size: Size,
177+
format: Format,
178+
options: &DecodeOptions,
179+
) -> Result<(), DecodingError> {
180+
if !surface_size.contains_rect(offset, image.size()) {
181+
return Err(DecodingError::RectOutOfBounds);
182+
}
183+
184+
if image.size().is_empty() {
185+
// skip the entire surface
186+
let pixel_info = PixelInfo::from(format);
187+
let bytes = pixel_info.surface_bytes(surface_size).unwrap_or(u64::MAX);
188+
util::io_skip_exact(reader, bytes)?;
189+
return Ok(());
190+
}
191+
192+
let decoders = get_decoders(format);
193+
decoders.decode_rect(reader, image, offset, surface_size, options)
194+
}
176195
}
177196

178197
#[derive(Debug, Clone, PartialEq, Eq, Hash)]

0 commit comments

Comments
 (0)