Don't use most popular edge colour if tied (#673)
If multiple edge colours were tied for most popular, the one chosen would depend on the current palette order, which could cause it to change if run a second time. This further improves determinism for #649. There is still one more issue I've found affecting the images there which I'll try to address in another PR.
This commit is contained in:
parent
8f298be7fb
commit
e6f84264e5
1 changed files with 13 additions and 7 deletions
|
|
@ -87,7 +87,7 @@ pub fn sorted_palette(png: &PngImage) -> Option<PngImage> {
|
||||||
let mut enumerated: Vec<_> = palette.iter().enumerate().collect();
|
let mut enumerated: Vec<_> = palette.iter().enumerate().collect();
|
||||||
// Put the most popular edge color first, which can help slightly if the filter bytes are 0
|
// Put the most popular edge color first, which can help slightly if the filter bytes are 0
|
||||||
let keep_first = most_popular_edge_color(palette.len(), png);
|
let keep_first = most_popular_edge_color(palette.len(), png);
|
||||||
let first = enumerated.remove(keep_first);
|
let first = keep_first.map(|f| enumerated.remove(f));
|
||||||
|
|
||||||
// Sort the palette
|
// Sort the palette
|
||||||
enumerated.sort_by(|a, b| {
|
enumerated.sort_by(|a, b| {
|
||||||
|
|
@ -104,7 +104,9 @@ pub fn sorted_palette(png: &PngImage) -> Option<PngImage> {
|
||||||
};
|
};
|
||||||
color_val(a.1).cmp(&color_val(b.1))
|
color_val(a.1).cmp(&color_val(b.1))
|
||||||
});
|
});
|
||||||
enumerated.insert(0, first);
|
if let Some(first) = first {
|
||||||
|
enumerated.insert(0, first);
|
||||||
|
}
|
||||||
|
|
||||||
// Extract the new palette and determine if anything changed
|
// Extract the new palette and determine if anything changed
|
||||||
let (remapping, palette): (Vec<_>, Vec<RGBA8>) = enumerated.into_iter().unzip();
|
let (remapping, palette): (Vec<_>, Vec<RGBA8>) = enumerated.into_iter().unzip();
|
||||||
|
|
@ -204,7 +206,7 @@ fn apply_palette_reorder(png: &PngImage, remapping: &[usize]) -> Option<PngImage
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find the most popular color on the image edges (the pixels neighboring the filter bytes)
|
// 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 {
|
fn most_popular_edge_color(num_colors: usize, png: &PngImage) -> Option<usize> {
|
||||||
let mut counts = [0u32; 256];
|
let mut counts = [0u32; 256];
|
||||||
for line in png.scan_lines(false) {
|
for line in png.scan_lines(false) {
|
||||||
if let &[first, .., last] = line.data {
|
if let &[first, .., last] = line.data {
|
||||||
|
|
@ -212,14 +214,18 @@ fn most_popular_edge_color(num_colors: usize, png: &PngImage) -> usize {
|
||||||
counts[last as usize] += 1;
|
counts[last as usize] += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
counts
|
let max = counts
|
||||||
.iter()
|
.iter()
|
||||||
.copied()
|
|
||||||
.take(num_colors)
|
.take(num_colors)
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.max_by_key(|&(_, v)| v)
|
.max_by_key(|&(_, v)| v)
|
||||||
.unwrap_or_default()
|
.unwrap();
|
||||||
.0
|
// Ensure there's a clear winner - return None if multiple colors are tied
|
||||||
|
let max_equal = counts.iter().filter(|&v| v == max.1).count();
|
||||||
|
if max_equal > 1 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
Some(max.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find the most popular color in the image, along with its count
|
// Find the most popular color in the image, along with its count
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue