fix the rest of encoder bugs (trying to calculate luma/diff when alpha channels of cur and prev aren't equal)
This commit is contained in:
parent
99c4262155
commit
f86d040c8b
BIN
out/dice.qoi
BIN
out/dice.qoi
Binary file not shown.
BIN
out/kodim23.qoi
Normal file
BIN
out/kodim23.qoi
Normal file
Binary file not shown.
@ -74,25 +74,44 @@ pub fn hash ([r, g, b, a]: [u8; 4]) -> usize {
|
||||
(r as usize * 3 + g as usize * 5 + b as usize * 7 + a as usize * 11) % 64
|
||||
}
|
||||
|
||||
pub fn calc_diff ([r0, g0, b0]: [u8; 3], [r1, g1, b1]: [u8; 3]) -> [i16; 3] {
|
||||
[idiff!(r0, r1), idiff!(g0, g1), idiff!(b0, b1)]
|
||||
pub fn calc_diff ([r0, g0, b0, _]: [i16; 4], [r1, g1, b1, _]: [i16; 4]) -> [i16; 3] {
|
||||
[diff(r0, r1), diff(g0, g1), diff(b0, b1)]
|
||||
}
|
||||
|
||||
pub fn diff_ops (left: [u8; 3], right: [u8; 3]) -> Option<Ops> {
|
||||
pub fn diff_ops (left: [u8; 4], right: [u8; 4]) -> Option<Ops> {
|
||||
if left[3] != right[3] { return None; }
|
||||
let left: [i16; 4] = left.map(i16::from);
|
||||
let right: [i16; 4] = right.map(i16::from);
|
||||
|
||||
let diff = calc_diff(left, right);
|
||||
|
||||
if diff.iter().all(|d| (-2..2).contains(d)) {
|
||||
Some(Ops::Diff(diff.map(|e| e as i8)))
|
||||
} else if (-32..32).contains(&diff[1]) && (-8..8).contains(&(diff[0] - diff[1])) && (-8..8).contains(&(diff[2] - diff[1])) {
|
||||
Some(Ops::Luma([(diff[0] - diff[1]) as i8, diff[1] as i8, (diff[2] - diff[1]) as i8]))
|
||||
} else if let Some(luma) = new_luma(diff) {
|
||||
Some(luma)
|
||||
} else { None }
|
||||
}
|
||||
|
||||
pub fn new_luma ([r, g, b]: [i16; 3]) -> Option<Ops> {
|
||||
let drdg = r - g;
|
||||
let dbdg = b - g;
|
||||
if (-32..32).contains(&g) && (-8..8).contains(&drdg) && (-8..8).contains(&dbdg) {
|
||||
return Some(Ops::Luma([drdg as i8, g as i8, dbdg as i8]));
|
||||
}
|
||||
return None;
|
||||
}
|
||||
|
||||
fn diff (lhs: i16, rhs: i16) -> i16 {
|
||||
let mut diff = lhs - rhs;
|
||||
if diff.abs() > 128 {
|
||||
diff = 256 - rhs + lhs + 1 * match diff.is_negative() {
|
||||
true => 1,
|
||||
false => -1
|
||||
};
|
||||
}
|
||||
diff
|
||||
}
|
||||
|
||||
pub fn cast_with_bias (num: i8, bias: i8) -> u8 {
|
||||
(num + bias) as u8
|
||||
}
|
||||
|
||||
pub macro idiff {
|
||||
($t1: expr, $t2: expr) => {
|
||||
$t1 as i16 - $t2 as i16
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,7 +1,5 @@
|
||||
use std::mem::discriminant;
|
||||
|
||||
use itertools::Itertools;
|
||||
|
||||
use crate::common::*;
|
||||
|
||||
|
||||
@ -9,11 +7,11 @@ impl Ops {
|
||||
// @TODO don't alloc here, get a mut reference and extend here
|
||||
fn format (self) -> Vec<u8> {
|
||||
// cast_diff
|
||||
let cd = |diff: i8| cast_with_bias(diff, -2);
|
||||
let cd = |diff: i8| cast_with_bias(diff, 2);
|
||||
// cast_luma_green
|
||||
let clg = |diff: i8| cast_with_bias(diff, -32);
|
||||
let clg = |diff: i8| cast_with_bias(diff, 32);
|
||||
// cast_luma_red_blue
|
||||
let clrb = |diff: i8| cast_with_bias(diff, -8);
|
||||
let clrb = |diff: i8| cast_with_bias(diff, 8);
|
||||
|
||||
match self {
|
||||
Ops::Nop => vec![],
|
||||
@ -45,6 +43,7 @@ fn encode_body (header: Header, data: &[u8]) -> Option<Vec<u8>> {
|
||||
let mut last = [0u8, 0, 0, 255];
|
||||
let mut out = vec![];
|
||||
let mut cur = Ops::Nop;
|
||||
let mut counter = 0;
|
||||
|
||||
for mut chunk in &data.iter().chunks(header.channels.num()) {
|
||||
let (&r, &g, &b) = chunk.next_tuple()?;
|
||||
@ -75,7 +74,7 @@ fn encode_body (header: Header, data: &[u8]) -> Option<Vec<u8>> {
|
||||
// INDEX -> DIFF -> LUMA -> RGB(A)
|
||||
if known[hash] == rgba {
|
||||
cur = Ops::Index(hash);
|
||||
} else if let Some(ops) = diff_ops([last[0], last[1], last[2]], [r, g, b]) {
|
||||
} else if let Some(ops) = diff_ops([r, g, b, a], [last[0], last[1], last[2], last[3]]) {
|
||||
cur = ops;
|
||||
} else {
|
||||
cur = header.channels.ops_color(&rgba[..header.channels.num()])?;
|
||||
@ -86,6 +85,11 @@ fn encode_body (header: Header, data: &[u8]) -> Option<Vec<u8>> {
|
||||
}
|
||||
}
|
||||
|
||||
// if an image buffer ends with a run, flush it as well
|
||||
if discriminant(&cur) == discriminant(&Ops::Run(0)) {
|
||||
out.extend_from_slice(&cur.format());
|
||||
}
|
||||
|
||||
Some(out)
|
||||
}
|
||||
|
||||
@ -129,4 +133,17 @@ mod test {
|
||||
|
||||
let _write = std::fs::write("out/1kb.qoi", &encoded);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn kodim23 () {
|
||||
let file = imageproc::image::io::Reader::open("in/kodim23.png").unwrap().decode().unwrap();
|
||||
let file = file.into_rgb8().into_raw();
|
||||
let header = Header { width: 768, height: 512, channels: Channels::RGB, colorspace: Colorspace::Srgb };
|
||||
|
||||
|
||||
let inst = Instant::now();
|
||||
let encoded = encode_full(header, &file).unwrap();
|
||||
println!("kodim23 encoding done in {} ms", inst.elapsed().as_millis());
|
||||
let _write = std::fs::write("out/kodim23.qoi", &encoded);
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user