use crate::shared::*;
#[inline]
fn can_use_small_dif(a: (u8, u8, u8, u8), b: (u8, u8, u8, u8)) -> bool {
let dr = (a.0 as i8).wrapping_sub(b.0 as i8);
let dg = (a.1 as i8).wrapping_sub(b.1 as i8);
let db = (a.2 as i8).wrapping_sub(b.2 as i8);
let da = (a.3 as i8).wrapping_sub(b.3 as i8);
if da != 0 {
return false;
}
dr >= -2 && dr <= 1 && dg >= -2 && dg <= 1 && db >= -2 && db <= 1
}
#[inline]
fn can_use_luma(a: (u8, u8, u8, u8), b: (u8, u8, u8, u8)) -> bool {
let dr = a.0 as isize - b.0 as isize;
let dg = a.1 as isize - b.1 as isize;
let db = a.2 as isize - b.2 as isize;
let da = a.3 as isize - b.3 as isize;
let drdg = dr - dg;
let dbdg = db - dg;
if da != 0 {
return false;
}
dg >= -32 && dg <= 31 && drdg >= -8 && drdg <= 7 && dbdg >= -8 && dbdg <= 7
}
pub fn encode_qoi(
img_data: &[u8],
height: usize,
width: usize,
channel_count: u8,
color_space: u8,
) -> Option<Vec<u8>> {
let mut buf = Vec::new();
let header = Header {
height: height as u32,
width: width as u32,
channel_count: channel_count,
color_space: color_space,
};
encode_header(header, &mut buf);
let mut colorhashes = [(0u8, 0u8, 0u8, 0u8); 64];
let mut last = (0u8, 0u8, 0u8, 255u8);
let mut rest = img_data;
while rest.len() > 0 {
let cpxl = (rest[0], rest[1], rest[2], rest[3]);
if cpxl.0 == last.0 && cpxl.1 == last.1 && cpxl.2 == last.2 && cpxl.3 == last.3 {
let mut runlen = 1_usize;
rest = &rest[4..];
while rest[0] == cpxl.0 && rest[1] == cpxl.1 && rest[2] == cpxl.2 && rest[3] == cpxl.3 {
runlen += 1;
rest = &rest[4..];
if rest.len() == 0 {
break;
}
if runlen == 62 {
break;
}
}
Part::Run(runlen as u8).encode(&mut buf);
} else if colorhashes[color_hash(cpxl.0, cpxl.1, cpxl.2, cpxl.3)] == cpxl {
Part::Idx(color_hash(cpxl.0, cpxl.1, cpxl.2, cpxl.3) as u8).encode(&mut buf);
rest = &rest[4..];
last = cpxl;
} else if can_use_small_dif(cpxl, last) {
let dr = (cpxl.0 as i8).wrapping_sub(last.0 as i8);
let dg = (cpxl.1 as i8).wrapping_sub(last.1 as i8);
let db = (cpxl.2 as i8).wrapping_sub(last.2 as i8);
Part::SmallDiff(dr as i8, dg as i8, db as i8).encode(&mut buf);
rest = &rest[4..];
} else if can_use_luma(cpxl, last) {
let dr = (cpxl.0 as i8).wrapping_sub(last.0 as i8);
let dg = (cpxl.1 as i8).wrapping_sub(last.1 as i8);
let db = (cpxl.2 as i8).wrapping_sub(last.2 as i8);
let drdg = dr.wrapping_sub(dg);
let dbdg = db.wrapping_sub(dg);
Part::LumaDiff(drdg as i8, dg as i8, dbdg as i8).encode(&mut buf);
rest = &rest[4..];
} else if cpxl.3 == last.3 {
let r = rest[0];
let g = rest[1];
let b = rest[2];
Part::RGB(r, g, b).encode(&mut buf);
rest = &rest[4..];
} else {
let r = rest[0];
let g = rest[1];
let b = rest[2];
let a = rest[3];
Part::RGBA(r, g, b, a).encode(&mut buf);
rest = &rest[4..];
}
add_hash_and_last(cpxl.0, cpxl.1, cpxl.2, cpxl.3, &mut colorhashes, &mut last);
}
buf.push(0x00);
buf.push(0x00);
buf.push(0x00);
buf.push(0x00);
buf.push(0x00);
buf.push(0x00);
buf.push(0x00);
buf.push(0x01);
Some(buf)
}