Skip to content

Commit

Permalink
Merge branch 'master' into docs/repology-packaging-status
Browse files Browse the repository at this point in the history
  • Loading branch information
andrews05 committed Mar 28, 2024
2 parents 9d9c540 + db7da03 commit e8716e6
Show file tree
Hide file tree
Showing 5 changed files with 86 additions and 38 deletions.
12 changes: 2 additions & 10 deletions .github/workflows/oxipng.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,9 @@ jobs:
- target: i686-pc-windows-msvc
os: windows-latest
- target: x86_64-apple-darwin
os: macos-latest
os: macos-12
- target: aarch64-apple-darwin
os: macos-latest
os: macos-14 # ARM64 runner

env:
CARGO_BUILD_TARGET: ${{ matrix.target }}
Expand Down Expand Up @@ -122,19 +122,11 @@ jobs:
reporter: github-check
fail_on_error: true

# There aren't good user-mode ARM64 emulators we can use on x64 macOS hosts.
# QEMU doesn't have any plans to add such support due to a lack of kernel
# syscall stability guarantees: https://gitlab.com/qemu-project/qemu/-/issues/1682
- name: Run tests
if: matrix.target != 'aarch64-apple-darwin'
run: |
cargo nextest run --release --features sanity-checks
cargo test --doc --release --features sanity-checks
- name: Build tests (ARM64 macOS only)
if: matrix.target == 'aarch64-apple-darwin'
run: cargo test --release --features sanity-checks --no-run

- name: Build benchmarks
run: cargo bench --no-run

Expand Down
34 changes: 20 additions & 14 deletions src/png/scan_lines.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,20 +24,26 @@ impl<'a> Iterator for ScanLines<'a> {
type Item = ScanLine<'a>;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
self.iter.next().map(|(len, pass, num_pixels)| {
let (data, rest) = self.raw_data.split_at(len);
self.raw_data = rest;
let (&filter, data) = if self.has_filter {
data.split_first().unwrap()
} else {
(&0, data)
};
ScanLine {
filter,
data,
pass,
num_pixels,
}
let (len, pass, num_pixels) = self.iter.next()?;
debug_assert!(self.raw_data.len() >= len);
debug_assert!(!self.has_filter || len > 1);
// The data length should always be correct here but this check assures
// the compiler that it doesn't need to account for a potential panic
if self.raw_data.len() < len {
return None;
}
let (data, rest) = self.raw_data.split_at(len);
self.raw_data = rest;
let (&filter, data) = if self.has_filter {
data.split_first().unwrap()
} else {
(&0, data)
};
Some(ScanLine {
filter,
data,
pass,
num_pixels,
})
}
}
Expand Down
41 changes: 27 additions & 14 deletions src/reduction/palette.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,8 @@ pub fn reduced_palette(png: &PngImage, optimize_alpha: bool) -> Option<PngImage>
if png.ihdr.bit_depth != BitDepth::Eight {
return None;
}
let palette = match &png.ihdr.color_type {
ColorType::Indexed { palette } if palette.len() > 1 => palette,
_ => return None,
let ColorType::Indexed { palette } = &png.ihdr.color_type else {
return None;
};

let mut used = [false; 256];
Expand All @@ -43,8 +42,9 @@ pub fn reduced_palette(png: &PngImage, optimize_alpha: bool) -> Option<PngImage>
let data = if did_change {
// Reassign data bytes to new indices
png.data.iter().map(|b| byte_map[*b as usize]).collect()
} else if condensed.len() < palette.len() {
// Data is unchanged but palette will be truncated
} else if condensed.len() != palette.len() {
// Data is unchanged but palette is different size
// Note the new palette could potentially be larger if the original had a missing entry
png.data.clone()
} else {
// Nothing has changed
Expand Down Expand Up @@ -183,23 +183,36 @@ pub fn sorted_palette_battiato(png: &PngImage) -> Option<PngImage> {

// Find the most popular color on the image edges (the pixels neighboring the filter bytes)
fn most_popular_edge_color(num_colors: usize, png: &PngImage) -> usize {
let mut counts = vec![0; num_colors];
let mut counts = [0u32; 256];
for line in png.scan_lines(false) {
counts[line.data[0] as usize] += 1;
counts[line.data[line.data.len() - 1] as usize] += 1;
if let &[first, .., last] = line.data {
counts[first as usize] += 1;
counts[last as usize] += 1;
}
}
counts.iter().enumerate().max_by_key(|(_, &v)| v).unwrap().0
counts
.iter()
.copied()
.take(num_colors)
.enumerate()
.max_by_key(|&(_, v)| v)
.unwrap_or_default()
.0
}

// Calculate co-occurences matrix
fn co_occurrence_matrix(num_colors: usize, png: &PngImage) -> Vec<Vec<usize>> {
let mut matrix = vec![vec![0; num_colors]; num_colors];
fn co_occurrence_matrix(num_colors: usize, png: &PngImage) -> Vec<Vec<u32>> {
let mut matrix = vec![vec![0u32; num_colors]; num_colors];
let mut prev: Option<ScanLine> = None;
let mut prev_val = None;
for line in png.scan_lines(false) {
for i in 0..line.data.len() {
let val = line.data[i] as usize;
if i > 0 {
matrix[line.data[i - 1] as usize][val] += 1;
if val > num_colors {
continue;
}
if let Some(prev_val) = prev_val.replace(val) {
matrix[prev_val][val] += 1;
}
if let Some(prev) = &prev {
matrix[prev.data[i] as usize][val] += 1;
Expand All @@ -211,7 +224,7 @@ fn co_occurrence_matrix(num_colors: usize, png: &PngImage) -> Vec<Vec<usize>> {
}

// Calculate edge list sorted by weight
fn weighted_edges(matrix: &[Vec<usize>]) -> Vec<(usize, usize)> {
fn weighted_edges(matrix: &[Vec<u32>]) -> Vec<(usize, usize)> {
let mut edges = Vec::new();
for i in 0..matrix.len() {
for j in 0..i {
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
37 changes: 37 additions & 0 deletions tests/reduction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1049,6 +1049,43 @@ fn palette_should_be_reduced_with_both() {
remove_file(output).ok();
}

#[test]
fn palette_should_be_reduced_with_missing() {
let input = PathBuf::from("tests/files/palette_should_be_reduced_with_missing.png");
let (output, opts) = get_opts(&input);

let png = PngData::new(&input, &opts).unwrap();

assert_eq!(png.raw.ihdr.color_type.png_header_code(), INDEXED);
assert_eq!(png.raw.ihdr.bit_depth, BitDepth::Eight);
if let ColorType::Indexed { palette } = &png.raw.ihdr.color_type {
assert_eq!(palette.len(), 2);
}

match oxipng::optimize(&InFile::Path(input), &output, &opts) {
Ok(_) => (),
Err(x) => panic!("{}", x),
};
let output = output.path().unwrap();
assert!(output.exists());

let png = match PngData::new(output, &opts) {
Ok(x) => x,
Err(x) => {
remove_file(output).ok();
panic!("{}", x)
}
};

assert_eq!(png.raw.ihdr.color_type.png_header_code(), INDEXED);
assert_eq!(png.raw.ihdr.bit_depth, BitDepth::Two);
if let ColorType::Indexed { palette } = &png.raw.ihdr.color_type {
assert_eq!(palette.len(), 3);
}

remove_file(output).ok();
}

#[test]
fn rgba_16_reduce_alpha() {
test_it_converts(
Expand Down

0 comments on commit e8716e6

Please sign in to comment.