Merge branch 'master' into docs/repology-packaging-status

This commit is contained in:
andrews05 2024-03-29 09:24:50 +13:00 committed by GitHub
commit e8716e6fd1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 86 additions and 38 deletions

View file

@ -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 }}
@ -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

View file

@ -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,
})
}
}

View file

@ -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];
@ -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
@ -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;
@ -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 {

Binary file not shown.

After

Width:  |  Height:  |  Size: 138 B

View file

@ -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(