-
Notifications
You must be signed in to change notification settings - Fork 145
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Splat adam7 interlacing #576
base: master
Are you sure you want to change the base?
Splat adam7 interlacing #576
Conversation
@anforowicz Please review this PR and share your suggestions on the TODOs. |
for i in 0..m { | ||
let start = i * n; | ||
let end = start + n; | ||
dst[start..end].copy_from_slice(src); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
for dst in dst.chunks_exact_mut(n)
will reduce bounds checks.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the PR!
#[derive(Clone, Copy, Debug, PartialEq, Eq)] | ||
pub struct Adam7Info { | ||
pub(crate) pass: u8, | ||
pub(crate) line: u32, | ||
pub(crate) width: u32, | ||
} | ||
|
||
// Pairs are (x_repeat, y_repeat) | ||
static SPLAT_EXPAND: &[(usize, usize)] = &[(8, 8), (4, 8), (4, 4), (2, 4), (2, 2), (1, 2), (1, 1)]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are a few different pass-specific constants in adam7.rs
:
line_mul
,line_off
,samp_mul
,samp_off
infn expand_adam7_bits
here- hardcoded constants in
fn init_pass
here - The new constants from this PR.
I wonder if these constants (and their usages) could be consolidated into something like:
struct PassConstants {
line_mul: u8,
line_off: u8,
samp_mul: u8,
samp_off: u8,
}
impl PassConstants {
const fn new(line_mul: u8, line_off: u8, samp_mul: u8, samp_off: u8) -> Self {
Self { line_mul, line_off, samp_mul, samp_off }
}
const fn x_repeat(&self) -> u8 { self.samp_mul - self.samp_off } // right?
const fn y_repeat(&self) -> u8 { self.line_mul - self.line_off } // right?
}
const PASS_CONSTANTS: [PassConstants; 7] = {
PassConstants::new(8, 0, 8, 0),
PassConstants::new(8, 0, 8, 4),
PassConstants::new(8, 4, 4, 0),
PassConstants::new(4, 0, 4, 2),
PassConstants::new(4, 2, 2, 0),
PassConstants::new(2, 0, 2, 1),
PassConstants::new(2, 1, 1, 0),
}
); | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(no action needed) I wondered for a moment whether to deduplicate against expand_pass
, but these are fairly short functions, so it doesn't seem worth the extra complexity.
bits_pp: usize, | ||
bit_pos: usize, | ||
) { | ||
let height = ((img.len() * 8 - bit_pos) as f32 / stride as f32).ceil() as usize; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need floating-point math here? Would img.len() * 8 - bit_pos + stride - 1) / stride
also work?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, this math should likely be using u64
rather than usize
. On 32-bit platforms it only takes 256 MB worth of bits to overflow a usize
.
Another thing you might want to look at are the provided methods on the builtin integer types. In particular, div_ceil
might be helpful here.
// |stride| is bit-count here, unlike in |byte_splat_expand|, where it is byte-count. | ||
fn bit_splat_expand( | ||
img: &mut [u8], | ||
stride: usize, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Could you please rename stride
to stride_in_bits
?
} | ||
} | ||
|
||
fn copy_nbits_mtimes(img: &mut [u8], bit_pos: usize, bits_pp: usize, px: u8, m: usize) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IIUC the implementation assumes that we never need to copy across two bytes. So maybe the asserts below could be used to document this:
debug_assert_eq!(bit_pos % bits_pp, 0);
debug_assert!(bits_pp == 1 || bits_pp == 2 || bits_pp == 4);
bytes_pp: usize, | ||
start_byte: usize, | ||
) { | ||
let height = ((img.len() - start_byte) as f32 / stride as f32).ceil() as usize; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same question here: can we avoid floating-point math here?
} | ||
|
||
// TODO: Can we eliminate the duplicate code in |bit_splat_expand| and |byte_splat_expand|? | ||
// |stride| is bit-count here, unlike in |byte_splat_expand|, where it is byte-count. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(no action needed) Maybe this can be tried as a follow-up PR? I am not sure if the deduplication would be better or not, but hopefully looking at a specific/actual change will help us decide.
); | ||
assert_eq!(actual_img, expected_img); | ||
|
||
// Third pass |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This confused me for a moment. I wonder if you could please expand the comment to something like:
/// Third pass. (Not "second pass" because for a 4-pixel-wide image `Adam7Iterator` will skip the no-op 2nd pass.)
Did I get that right in the proposed comment?
@@ -233,6 +238,98 @@ pub fn expand_pass( | |||
} | |||
} | |||
|
|||
// TODO: Export this function in lib.rs. | |||
// Should we have a separate function, or can we use an enum to determine whether | |||
// to use default or splat? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(no action needed) FWIW I vote for a separate function - ending up with png::expand_interlaced_row
and png::splat_interlaced_row
, but I am curious what others think. At any rate, we can probably discuss later, in a PR that adds a public API for this.
// TODO: Export this function in lib.rs. | ||
// Should we have a separate function, or can we use an enum to determine whether | ||
// to use default or splat? | ||
pub fn splat_expand_pass( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be good to add a doc comment here explaining what "splat expand" means. I understand from the linked chromium issue, but having it right in the code will help for future reference.
I think filling the image pixels completely is the expected behavior, so it would be nice to make this easy/default, but I'm not sure how such thing could be integrated with the library API. Doing this in |
As a follow-up to this issue in Chromium, I created a corresponding PR in
image-png
.CC: @anforowicz