use std::io::Write;
use super::quantize::{CLEAR, Indexed};
use super::{MAX_SIDE, within_limits};
const REGISTERS: usize = 1024;
const VT340: [[u8; 3]; 16] = [
[0, 0, 0],
[51, 51, 204],
[204, 33, 33],
[51, 204, 51],
[204, 51, 204],
[51, 204, 204],
[204, 204, 51],
[120, 120, 120],
[69, 69, 69],
[87, 87, 153],
[153, 69, 69],
[87, 153, 87],
[153, 87, 153],
[87, 153, 153],
[153, 153, 87],
[204, 204, 204],
];
const SIXELS: std::ops::RangeInclusive<u8> = b'?'..=b'~';
enum Op<'a> {
Raster { width: u32, height: u32 },
Colour { register: usize, rgb: Option<[u8; 3]> },
Sixels { x: u32, band: u32, sixels: &'a [u8] },
Repeat { x: u32, band: u32, count: u32, sixel: u8 },
}
#[derive(Default)]
struct Reader {
introducer: Option<u8>,
params: [u32; 5],
len: usize,
x: u32,
band: u32,
}
impl Reader {
fn read<'a>(&mut self, data: &'a [u8], each: &mut impl FnMut(Op<'a>)) {
let mut i = 0;
while i < data.len() {
if self.introducer.is_some() && self.param(data[i], each) {
i += 1;
continue;
}
let run = data[i..].iter().take_while(|b| SIXELS.contains(*b)).count();
if run > 0 {
each(Op::Sixels { x: self.x, band: self.band, sixels: &data[i..i + run] });
self.x = self.x.saturating_add(run as u32);
i += run;
continue;
}
match data[i] {
introducer @ (b'"' | b'#' | b'!') => {
self.introducer = Some(introducer);
self.params[0] = 0;
self.len = 1;
}
b'$' => self.x = 0,
b'-' => {
self.x = 0;
self.band = self.band.saturating_add(1);
}
_ => {}
}
i += 1;
}
}
fn param<'a>(&mut self, b: u8, each: &mut impl FnMut(Op<'a>)) -> bool {
match b {
b'0'..=b'9' => {
let n = &mut self.params[self.len - 1];
*n = n.saturating_mul(10).saturating_add(u32::from(b - b'0'));
}
b';' if self.introducer != Some(b'!') && self.len < self.params.len() => {
self.params[self.len] = 0;
self.len += 1;
}
sixel if self.introducer == Some(b'!') && SIXELS.contains(&sixel) => {
self.introducer = None;
let count = self.params[0].max(1);
each(Op::Repeat { x: self.x, band: self.band, count, sixel });
self.x = self.x.saturating_add(count);
}
_ => {
self.end_params(each);
return false;
}
}
true
}
fn end_params<'a>(&mut self, each: &mut impl FnMut(Op<'a>)) {
match (self.introducer.take(), &self.params[..self.len]) {
(Some(b'"'), &[_, _, width, height, ..]) => each(Op::Raster { width, height }),
(Some(b'#'), &[register, ref rgb @ ..]) => {
let rgb = match *rgb {
[1, h, l, s] => Some(hls(h, l, s)),
[_, r, g, b] => Some([percent(r), percent(g), percent(b)]),
_ => None,
};
each(Op::Colour { register: register as usize % REGISTERS, rgb });
}
_ => {}
}
}
}
#[derive(Default)]
pub struct Measure {
reader: Reader,
extent: Extent,
}
impl Measure {
pub fn read(&mut self, data: &[u8]) {
let extent = &mut self.extent;
self.reader.read(data, &mut |op| extent.take(op));
}
pub fn size(mut self) -> Option<(u32, u32)> {
let extent = &mut self.extent;
self.reader.end_params(&mut |op| extent.take(op));
self.extent.size()
}
}
#[derive(Default)]
struct Extent {
announced: (u32, u32),
width: u32,
height: u32,
over: bool,
}
impl Extent {
fn take(&mut self, op: Op<'_>) {
match op {
Op::Raster { width, height } => self.announced = (width.min(MAX_SIDE), height.min(MAX_SIDE)),
Op::Colour { .. } => {}
Op::Sixels { x, band, sixels } => {
let set = sixels.iter().rposition(|s| *s != b'?').map(|last| {
let bits = sixels[..=last].iter().fold(0, |bits, s| bits | (s - b'?'));
(last as u64 + 1, bits)
});
self.reach(x, band, sixels.len() as u64, set);
}
Op::Repeat { x, band, count, sixel } => {
let set = (sixel != b'?').then_some((u64::from(count), sixel - b'?'));
self.reach(x, band, u64::from(count), set);
}
}
}
fn reach(&mut self, x: u32, band: u32, columns: u64, set: Option<(u64, u8)>) {
self.width = self.width.max((u64::from(x) + columns).min(u64::from(MAX_SIDE)) as u32);
let Some((columns, bits)) = set else { return };
let right = u64::from(x) + columns;
let bottom = u64::from(band) * 6 + u64::from(u8::BITS - bits.leading_zeros());
if right > u64::from(MAX_SIDE) || bottom > u64::from(MAX_SIDE) {
self.over = true;
return;
}
self.width = self.width.max(right as u32);
self.height = self.height.max(bottom as u32);
}
fn size(&self) -> Option<(u32, u32)> {
let (width, height) = (self.width.max(self.announced.0), self.height.max(self.announced.1));
(!self.over && within_limits(width, height)).then_some((width, height))
}
}
pub fn paint(data: &[u8], (width, height): (u32, u32)) -> Vec<u8> {
let mut canvas = Canvas::new(width as usize, height as usize);
Reader::default().read(data, &mut |op| canvas.draw(op));
canvas.pixels
}
#[cfg(test)]
pub fn decode(data: &[u8]) -> Option<((u32, u32), Vec<u8>)> {
let mut measure = Measure::default();
measure.read(data);
let size = measure.size()?;
Some((size, paint(data, size)))
}
struct Canvas {
width: usize,
height: usize,
pixels: Vec<u8>,
palette: Vec<[u8; 3]>,
colour: [u8; 4],
}
impl Canvas {
fn new(width: usize, height: usize) -> Canvas {
let mut palette = vec![[0u8; 3]; REGISTERS];
palette[..16].copy_from_slice(&VT340);
let [r, g, b] = palette[0];
Canvas { width, height, pixels: vec![0; width * height * 4], palette, colour: [r, g, b, 255] }
}
fn draw(&mut self, op: Op<'_>) {
match op {
Op::Raster { .. } => {}
Op::Colour { register, rgb } => {
if let Some(rgb) = rgb {
self.palette[register] = rgb;
}
let [r, g, b] = self.palette[register];
self.colour = [r, g, b, 255];
}
Op::Sixels { x, band, sixels } => {
for (x, sixel) in (x as usize..).zip(sixels) {
self.set(x, band, 1, *sixel);
}
}
Op::Repeat { x, band, count, sixel } => self.set(x as usize, band, count as usize, sixel),
}
}
fn set(&mut self, x: usize, band: u32, count: usize, sixel: u8) {
let right = x.saturating_add(count).min(self.width);
let x = x.min(right);
let bits = sixel - b'?';
let rows = (0..6).filter(|bit| bits & (1 << bit) != 0).map(|bit| band as usize * 6 + bit);
for y in rows.take_while(|y| *y < self.height) {
let row = y * self.width;
for pixel in self.pixels[(row + x) * 4..(row + right) * 4].as_chunks_mut::<4>().0 {
*pixel = self.colour;
}
}
}
}
fn percent(n: u32) -> u8 {
(n.min(100) * 255 / 100) as u8
}
fn hls(h: u32, l: u32, s: u32) -> [u8; 3] {
let h = ((h % 360) + 240) % 360;
let (l, s) = (f64::from(l.min(100)) / 100.0, f64::from(s.min(100)) / 100.0);
let c = (1.0 - (2.0 * l - 1.0).abs()) * s;
let hp = f64::from(h) / 60.0;
let x = c * (1.0 - (hp % 2.0 - 1.0).abs());
let (r, g, b) = match h / 60 {
0 => (c, x, 0.0),
1 => (x, c, 0.0),
2 => (0.0, c, x),
3 => (0.0, x, c),
4 => (x, 0.0, c),
_ => (c, 0.0, x),
};
let m = l - c / 2.0;
let to = |v: f64| ((v + m) * 255.0).round().clamp(0.0, 255.0) as u8;
[to(r), to(g), to(b)]
}
pub fn encode(image: &Indexed, x: u32, y: u32, width: u32, height: u32, out: &mut Vec<u8>) {
let w = width.min(image.width.saturating_sub(x)) as usize;
let h = height.min(image.height.saturating_sub(y)) as usize;
if w == 0 || h == 0 {
return;
}
let stride = image.width as usize;
let row = |r: usize| {
let at = (y as usize + r) * stride + x as usize;
&image.pixels[at..at + w]
};
let _ = write!(out, "\x1bP0;1;0q\"1;1;{w};{h}");
let mut used = [false; 256];
for r in 0..h {
for &p in row(r) {
used[usize::from(p)] = true;
}
}
for (i, [r, g, b]) in image.palette.iter().enumerate().take(usize::from(CLEAR)) {
if used[i] {
let pct = |v: u8| (u32::from(v) * 100 + 127) / 255;
let _ = write!(out, "#{i};2;{};{};{}", pct(*r), pct(*g), pct(*b));
}
}
let mut masks = vec![0u8; 256 * w];
let mut span = [(usize::MAX, 0usize); 256];
let mut present: Vec<u8> = Vec::with_capacity(256);
for band in 0..h.div_ceil(6) {
if band > 0 {
out.push(b'-');
}
present.clear();
for bit in 0..6 {
let r = band * 6 + bit;
if r >= h {
break;
}
for (px, &p) in row(r).iter().enumerate() {
if p == CLEAR {
continue;
}
let c = usize::from(p);
if span[c].0 == usize::MAX {
present.push(p);
span[c] = (px, px);
} else {
span[c].0 = span[c].0.min(px);
span[c].1 = span[c].1.max(px);
}
masks[c * w + px] |= 1 << bit;
}
}
for (k, &p) in present.iter().enumerate() {
let c = usize::from(p);
if k > 0 {
out.push(b'$');
}
let _ = write!(out, "#{c}");
let (from, to) = span[c];
if from > 0 {
run(out, from, 0);
}
let bits = &mut masks[c * w + from..=c * w + to];
let mut i = 0;
while i < bits.len() {
let v = bits[i];
let n = bits[i..].iter().take_while(|b| **b == v).count();
run(out, n, v);
i += n;
}
bits.fill(0);
span[c] = (usize::MAX, 0);
}
}
out.extend_from_slice(b"\x1b\\");
}
fn run(out: &mut Vec<u8>, n: usize, bits: u8) {
let c = 0x3f + bits;
if n > 3 {
let _ = write!(out, "!{n}");
out.push(c);
} else {
out.extend(std::iter::repeat_n(c, n));
}
}