mirror of
https://github.com/wez/wezterm.git
synced 2024-11-14 13:57:34 +03:00
refactor: move sixel parser/builder to own file
This commit is contained in:
parent
5b0edfddea
commit
15cd990e33
@ -1,25 +1,17 @@
|
|||||||
#![allow(clippy::many_single_char_names)]
|
#![allow(clippy::many_single_char_names)]
|
||||||
use crate::color::RgbColor;
|
|
||||||
use crate::escape::{
|
use crate::escape::{
|
||||||
Action, DeviceControlMode, EnterDeviceControlMode, Esc, OperatingSystemCommand,
|
Action, DeviceControlMode, EnterDeviceControlMode, Esc, OperatingSystemCommand,
|
||||||
ShortDeviceControl, Sixel, SixelData, CSI,
|
ShortDeviceControl, CSI,
|
||||||
};
|
};
|
||||||
use crate::tmux_cc::Event;
|
use crate::tmux_cc::Event;
|
||||||
use log::error;
|
use log::error;
|
||||||
use num_traits::FromPrimitive;
|
use num_traits::FromPrimitive;
|
||||||
use regex::bytes::Regex;
|
|
||||||
use std::borrow::BorrowMut;
|
use std::borrow::BorrowMut;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use vtparse::{CsiParam, VTActor, VTParser};
|
use vtparse::{CsiParam, VTActor, VTParser};
|
||||||
|
|
||||||
struct SixelBuilder {
|
mod sixel;
|
||||||
sixel: Sixel,
|
use sixel::SixelBuilder;
|
||||||
buf: Vec<u8>,
|
|
||||||
repeat_re: Regex,
|
|
||||||
raster_re: Regex,
|
|
||||||
colordef_re: Regex,
|
|
||||||
coloruse_re: Regex,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
struct GetTcapBuilder {
|
struct GetTcapBuilder {
|
||||||
@ -341,195 +333,11 @@ impl<'a, F: FnMut(Action)> VTActor for Performer<'a, F> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SixelBuilder {
|
|
||||||
fn new(params: &[i64]) -> Self {
|
|
||||||
let pan = match params.get(0).unwrap_or(&0) {
|
|
||||||
7 | 8 | 9 => 1,
|
|
||||||
0 | 1 | 5 | 6 => 2,
|
|
||||||
3 | 4 => 3,
|
|
||||||
2 => 5,
|
|
||||||
_ => 2,
|
|
||||||
};
|
|
||||||
let background_is_transparent = match params.get(1).unwrap_or(&0) {
|
|
||||||
1 => true,
|
|
||||||
_ => false,
|
|
||||||
};
|
|
||||||
let horizontal_grid_size = params.get(2).map(|&x| x);
|
|
||||||
|
|
||||||
let repeat_re = Regex::new("^!(\\d+)([\x3f-\x7e])").unwrap();
|
|
||||||
let raster_re = Regex::new("^\"(\\d+);(\\d+)(;(\\d+))?(;(\\d+))?").unwrap();
|
|
||||||
let colordef_re = Regex::new("^#(\\d+);(\\d+);(\\d+);(\\d+);(\\d+);?").unwrap();
|
|
||||||
let coloruse_re = Regex::new("^#(\\d+)([^;\\d]|$)").unwrap();
|
|
||||||
|
|
||||||
Self {
|
|
||||||
sixel: Sixel {
|
|
||||||
pan,
|
|
||||||
pad: 1,
|
|
||||||
pixel_width: None,
|
|
||||||
pixel_height: None,
|
|
||||||
background_is_transparent,
|
|
||||||
horizontal_grid_size,
|
|
||||||
data: vec![],
|
|
||||||
},
|
|
||||||
buf: vec![],
|
|
||||||
repeat_re,
|
|
||||||
raster_re,
|
|
||||||
colordef_re,
|
|
||||||
coloruse_re,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn push(&mut self, data: u8) {
|
|
||||||
self.buf.push(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn finish(&mut self) {
|
|
||||||
fn cap_int<T: std::str::FromStr>(m: regex::bytes::Match) -> Option<T> {
|
|
||||||
let bytes = m.as_bytes();
|
|
||||||
// Safe because we matched digits from the regex
|
|
||||||
let s = unsafe { std::str::from_utf8_unchecked(bytes) };
|
|
||||||
s.parse::<T>().ok()
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut remainder = &self.buf[..];
|
|
||||||
|
|
||||||
while !remainder.is_empty() {
|
|
||||||
let data = remainder[0];
|
|
||||||
|
|
||||||
if data == b'$' {
|
|
||||||
self.sixel.data.push(SixelData::CarriageReturn);
|
|
||||||
remainder = &remainder[1..];
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if data == b'-' {
|
|
||||||
self.sixel.data.push(SixelData::NewLine);
|
|
||||||
remainder = &remainder[1..];
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if data >= 0x3f && data <= 0x7e {
|
|
||||||
self.sixel.data.push(SixelData::Data(data - 0x3f));
|
|
||||||
remainder = &remainder[1..];
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(c) = self.raster_re.captures(remainder) {
|
|
||||||
let all = c.get(0).unwrap();
|
|
||||||
let matched_len = all.as_bytes().len();
|
|
||||||
|
|
||||||
let pan = cap_int(c.get(1).unwrap()).unwrap_or(2);
|
|
||||||
let pad = cap_int(c.get(2).unwrap()).unwrap_or(1);
|
|
||||||
let pixel_width = c.get(4).and_then(cap_int);
|
|
||||||
let pixel_height = c.get(6).and_then(cap_int);
|
|
||||||
|
|
||||||
self.sixel.pan = pan;
|
|
||||||
self.sixel.pad = pad;
|
|
||||||
self.sixel.pixel_width = pixel_width;
|
|
||||||
self.sixel.pixel_height = pixel_height;
|
|
||||||
|
|
||||||
if let (Some(w), Some(h)) = (pixel_width, pixel_height) {
|
|
||||||
let size = w as usize * h as usize;
|
|
||||||
// Ideally we'd just use `try_reserve` here, but that is
|
|
||||||
// nightly Rust only at the time of writing this comment:
|
|
||||||
// <https://github.com/rust-lang/rust/issues/48043>
|
|
||||||
const MAX_SIXEL_SIZE: usize = 100_000_000;
|
|
||||||
if size > MAX_SIXEL_SIZE {
|
|
||||||
log::error!(
|
|
||||||
"Ignoring sixel data {}x{} because {} bytes > max allowed {}",
|
|
||||||
w,
|
|
||||||
h,
|
|
||||||
size,
|
|
||||||
MAX_SIXEL_SIZE
|
|
||||||
);
|
|
||||||
self.sixel.pixel_width = None;
|
|
||||||
self.sixel.pixel_height = None;
|
|
||||||
self.sixel.data.clear();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
self.sixel.data.reserve(size);
|
|
||||||
}
|
|
||||||
|
|
||||||
remainder = &remainder[matched_len..];
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(c) = self.coloruse_re.captures(remainder) {
|
|
||||||
let all = c.get(0).unwrap();
|
|
||||||
let matched_len = all.as_bytes().len();
|
|
||||||
|
|
||||||
let color_number = cap_int(c.get(1).unwrap()).unwrap_or(0);
|
|
||||||
|
|
||||||
self.sixel
|
|
||||||
.data
|
|
||||||
.push(SixelData::SelectColorMapEntry(color_number));
|
|
||||||
|
|
||||||
let pop_len = matched_len - c.get(2).unwrap().as_bytes().len();
|
|
||||||
|
|
||||||
remainder = &remainder[pop_len..];
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(c) = self.colordef_re.captures(remainder) {
|
|
||||||
let all = c.get(0).unwrap();
|
|
||||||
let matched_len = all.as_bytes().len();
|
|
||||||
|
|
||||||
let color_number = cap_int(c.get(1).unwrap()).unwrap_or(0);
|
|
||||||
let system = cap_int(c.get(2).unwrap()).unwrap_or(1);
|
|
||||||
let a = cap_int(c.get(3).unwrap()).unwrap_or(0);
|
|
||||||
let b = cap_int(c.get(4).unwrap()).unwrap_or(0);
|
|
||||||
let c = cap_int(c.get(5).unwrap()).unwrap_or(0);
|
|
||||||
|
|
||||||
if system == 1 {
|
|
||||||
self.sixel.data.push(SixelData::DefineColorMapHSL {
|
|
||||||
color_number,
|
|
||||||
hue_angle: a,
|
|
||||||
lightness: b,
|
|
||||||
saturation: c,
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
let r = a as f32 * 255.0 / 100.;
|
|
||||||
let g = b as f32 * 255.0 / 100.;
|
|
||||||
let b = c as f32 * 255.0 / 100.;
|
|
||||||
let rgb = RgbColor::new_8bpc(r as u8, g as u8, b as u8); // FIXME: from linear
|
|
||||||
self.sixel
|
|
||||||
.data
|
|
||||||
.push(SixelData::DefineColorMapRGB { color_number, rgb });
|
|
||||||
}
|
|
||||||
|
|
||||||
remainder = &remainder[matched_len..];
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(c) = self.repeat_re.captures(remainder) {
|
|
||||||
let all = c.get(0).unwrap();
|
|
||||||
let matched_len = all.as_bytes().len();
|
|
||||||
|
|
||||||
let repeat_count = cap_int(c.get(1).unwrap()).unwrap_or(1);
|
|
||||||
let data = c.get(2).unwrap().as_bytes()[0] - 0x3f;
|
|
||||||
self.sixel
|
|
||||||
.data
|
|
||||||
.push(SixelData::Repeat { repeat_count, data });
|
|
||||||
remainder = &remainder[matched_len..];
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
log::error!(
|
|
||||||
"finished sixel parse with {} bytes pending {:?}",
|
|
||||||
remainder.len(),
|
|
||||||
std::str::from_utf8(&remainder[0..24.min(remainder.len())])
|
|
||||||
);
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::cell::{Intensity, Underline};
|
use crate::cell::{Intensity, Underline};
|
||||||
use crate::color::ColorSpec;
|
use crate::color::{ColorSpec, RgbColor};
|
||||||
use crate::escape::csi::{
|
use crate::escape::csi::{
|
||||||
CharacterPath, DecPrivateMode, DecPrivateModeCode, Device, Mode, Sgr, Window, XtSmGraphics,
|
CharacterPath, DecPrivateMode, DecPrivateModeCode, Device, Mode, Sgr, Window, XtSmGraphics,
|
||||||
XtSmGraphicsItem, XtermKeyModifierResource,
|
XtSmGraphicsItem, XtermKeyModifierResource,
|
||||||
@ -703,116 +511,6 @@ mod test {
|
|||||||
assert_eq!(encode(&actions), "\x1b%H");
|
assert_eq!(encode(&actions), "\x1b%H");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn sixel() {
|
|
||||||
let mut p = Parser::new();
|
|
||||||
let actions = p.parse_as_vec(b"\x1bP1;2;3;q@\x1b\\");
|
|
||||||
assert_eq!(
|
|
||||||
vec![
|
|
||||||
Action::Sixel(Box::new(Sixel {
|
|
||||||
pan: 2,
|
|
||||||
pad: 1,
|
|
||||||
pixel_width: None,
|
|
||||||
pixel_height: None,
|
|
||||||
background_is_transparent: false,
|
|
||||||
horizontal_grid_size: Some(3),
|
|
||||||
data: vec![SixelData::Data(1)]
|
|
||||||
})),
|
|
||||||
Action::Esc(Esc::Code(EscCode::StringTerminator)),
|
|
||||||
],
|
|
||||||
actions
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(format!("{}", actions[0]), "\x1bP0;0;3q@");
|
|
||||||
|
|
||||||
// This is the "HI" example from wikipedia
|
|
||||||
let mut p = Parser::new();
|
|
||||||
let actions = p.parse_as_vec(
|
|
||||||
b"\x1bPq\
|
|
||||||
#0;2;0;0;0#1;2;100;100;0#2;2;0;100;0\
|
|
||||||
#1~~@@vv@@~~@@~~$\
|
|
||||||
#2??}}GG}}??}}??-\
|
|
||||||
#1!14@\
|
|
||||||
\x1b\\",
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
format!("{}", actions[0]),
|
|
||||||
"\x1bP0;0q\
|
|
||||||
#0;2;0;0;0#1;2;100;100;0#2;2;0;100;0\
|
|
||||||
#1~~@@vv@@~~@@~~$\
|
|
||||||
#2??}}GG}}??}}??-\
|
|
||||||
#1!14@"
|
|
||||||
);
|
|
||||||
|
|
||||||
use SixelData::*;
|
|
||||||
assert_eq!(
|
|
||||||
vec![
|
|
||||||
Action::Sixel(Box::new(Sixel {
|
|
||||||
pan: 2,
|
|
||||||
pad: 1,
|
|
||||||
pixel_width: None,
|
|
||||||
pixel_height: None,
|
|
||||||
background_is_transparent: false,
|
|
||||||
horizontal_grid_size: None,
|
|
||||||
data: vec![
|
|
||||||
DefineColorMapRGB {
|
|
||||||
color_number: 0,
|
|
||||||
rgb: RgbColor::new_8bpc(0, 0, 0)
|
|
||||||
},
|
|
||||||
DefineColorMapRGB {
|
|
||||||
color_number: 1,
|
|
||||||
rgb: RgbColor::new_8bpc(255, 255, 0)
|
|
||||||
},
|
|
||||||
DefineColorMapRGB {
|
|
||||||
color_number: 2,
|
|
||||||
rgb: RgbColor::new_8bpc(0, 255, 0)
|
|
||||||
},
|
|
||||||
SelectColorMapEntry(1),
|
|
||||||
Data(63),
|
|
||||||
Data(63),
|
|
||||||
Data(1),
|
|
||||||
Data(1),
|
|
||||||
Data(55),
|
|
||||||
Data(55),
|
|
||||||
Data(1),
|
|
||||||
Data(1),
|
|
||||||
Data(63),
|
|
||||||
Data(63),
|
|
||||||
Data(1),
|
|
||||||
Data(1),
|
|
||||||
Data(63),
|
|
||||||
Data(63),
|
|
||||||
CarriageReturn,
|
|
||||||
SelectColorMapEntry(2),
|
|
||||||
Data(0),
|
|
||||||
Data(0),
|
|
||||||
Data(62),
|
|
||||||
Data(62),
|
|
||||||
Data(8),
|
|
||||||
Data(8),
|
|
||||||
Data(62),
|
|
||||||
Data(62),
|
|
||||||
Data(0),
|
|
||||||
Data(0),
|
|
||||||
Data(62),
|
|
||||||
Data(62),
|
|
||||||
Data(0),
|
|
||||||
Data(0),
|
|
||||||
NewLine,
|
|
||||||
SelectColorMapEntry(1),
|
|
||||||
Repeat {
|
|
||||||
repeat_count: 14,
|
|
||||||
data: 1
|
|
||||||
}
|
|
||||||
]
|
|
||||||
})),
|
|
||||||
Action::Esc(Esc::Code(EscCode::StringTerminator)),
|
|
||||||
],
|
|
||||||
actions
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn soft_reset() {
|
fn soft_reset() {
|
||||||
let mut p = Parser::new();
|
let mut p = Parser::new();
|
||||||
|
314
termwiz/src/escape/parser/sixel.rs
Normal file
314
termwiz/src/escape/parser/sixel.rs
Normal file
@ -0,0 +1,314 @@
|
|||||||
|
use crate::color::RgbColor;
|
||||||
|
use crate::escape::{Sixel, SixelData};
|
||||||
|
use regex::bytes::Regex;
|
||||||
|
|
||||||
|
pub struct SixelBuilder {
|
||||||
|
pub sixel: Sixel,
|
||||||
|
buf: Vec<u8>,
|
||||||
|
repeat_re: Regex,
|
||||||
|
raster_re: Regex,
|
||||||
|
colordef_re: Regex,
|
||||||
|
coloruse_re: Regex,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SixelBuilder {
|
||||||
|
pub fn new(params: &[i64]) -> Self {
|
||||||
|
let pan = match params.get(0).unwrap_or(&0) {
|
||||||
|
7 | 8 | 9 => 1,
|
||||||
|
0 | 1 | 5 | 6 => 2,
|
||||||
|
3 | 4 => 3,
|
||||||
|
2 => 5,
|
||||||
|
_ => 2,
|
||||||
|
};
|
||||||
|
let background_is_transparent = match params.get(1).unwrap_or(&0) {
|
||||||
|
1 => true,
|
||||||
|
_ => false,
|
||||||
|
};
|
||||||
|
let horizontal_grid_size = params.get(2).map(|&x| x);
|
||||||
|
|
||||||
|
let repeat_re = Regex::new("^!(\\d+)([\x3f-\x7e])").unwrap();
|
||||||
|
let raster_re = Regex::new("^\"(\\d+);(\\d+)(;(\\d+))?(;(\\d+))?").unwrap();
|
||||||
|
let colordef_re = Regex::new("^#(\\d+);(\\d+);(\\d+);(\\d+);(\\d+);?").unwrap();
|
||||||
|
let coloruse_re = Regex::new("^#(\\d+)([^;\\d]|$)").unwrap();
|
||||||
|
|
||||||
|
Self {
|
||||||
|
sixel: Sixel {
|
||||||
|
pan,
|
||||||
|
pad: 1,
|
||||||
|
pixel_width: None,
|
||||||
|
pixel_height: None,
|
||||||
|
background_is_transparent,
|
||||||
|
horizontal_grid_size,
|
||||||
|
data: vec![],
|
||||||
|
},
|
||||||
|
buf: vec![],
|
||||||
|
repeat_re,
|
||||||
|
raster_re,
|
||||||
|
colordef_re,
|
||||||
|
coloruse_re,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn push(&mut self, data: u8) {
|
||||||
|
self.buf.push(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn finish(&mut self) {
|
||||||
|
fn cap_int<T: std::str::FromStr>(m: regex::bytes::Match) -> Option<T> {
|
||||||
|
let bytes = m.as_bytes();
|
||||||
|
// Safe because we matched digits from the regex
|
||||||
|
let s = unsafe { std::str::from_utf8_unchecked(bytes) };
|
||||||
|
s.parse::<T>().ok()
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut remainder = &self.buf[..];
|
||||||
|
|
||||||
|
while !remainder.is_empty() {
|
||||||
|
let data = remainder[0];
|
||||||
|
|
||||||
|
if data == b'$' {
|
||||||
|
self.sixel.data.push(SixelData::CarriageReturn);
|
||||||
|
remainder = &remainder[1..];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if data == b'-' {
|
||||||
|
self.sixel.data.push(SixelData::NewLine);
|
||||||
|
remainder = &remainder[1..];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if data >= 0x3f && data <= 0x7e {
|
||||||
|
self.sixel.data.push(SixelData::Data(data - 0x3f));
|
||||||
|
remainder = &remainder[1..];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(c) = self.raster_re.captures(remainder) {
|
||||||
|
let all = c.get(0).unwrap();
|
||||||
|
let matched_len = all.as_bytes().len();
|
||||||
|
|
||||||
|
let pan = cap_int(c.get(1).unwrap()).unwrap_or(2);
|
||||||
|
let pad = cap_int(c.get(2).unwrap()).unwrap_or(1);
|
||||||
|
let pixel_width = c.get(4).and_then(cap_int);
|
||||||
|
let pixel_height = c.get(6).and_then(cap_int);
|
||||||
|
|
||||||
|
self.sixel.pan = pan;
|
||||||
|
self.sixel.pad = pad;
|
||||||
|
self.sixel.pixel_width = pixel_width;
|
||||||
|
self.sixel.pixel_height = pixel_height;
|
||||||
|
|
||||||
|
if let (Some(w), Some(h)) = (pixel_width, pixel_height) {
|
||||||
|
let size = w as usize * h as usize;
|
||||||
|
// Ideally we'd just use `try_reserve` here, but that is
|
||||||
|
// nightly Rust only at the time of writing this comment:
|
||||||
|
// <https://github.com/rust-lang/rust/issues/48043>
|
||||||
|
const MAX_SIXEL_SIZE: usize = 100_000_000;
|
||||||
|
if size > MAX_SIXEL_SIZE {
|
||||||
|
log::error!(
|
||||||
|
"Ignoring sixel data {}x{} because {} bytes > max allowed {}",
|
||||||
|
w,
|
||||||
|
h,
|
||||||
|
size,
|
||||||
|
MAX_SIXEL_SIZE
|
||||||
|
);
|
||||||
|
self.sixel.pixel_width = None;
|
||||||
|
self.sixel.pixel_height = None;
|
||||||
|
self.sixel.data.clear();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
self.sixel.data.reserve(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
remainder = &remainder[matched_len..];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(c) = self.coloruse_re.captures(remainder) {
|
||||||
|
let all = c.get(0).unwrap();
|
||||||
|
let matched_len = all.as_bytes().len();
|
||||||
|
|
||||||
|
let color_number = cap_int(c.get(1).unwrap()).unwrap_or(0);
|
||||||
|
|
||||||
|
self.sixel
|
||||||
|
.data
|
||||||
|
.push(SixelData::SelectColorMapEntry(color_number));
|
||||||
|
|
||||||
|
let pop_len = matched_len - c.get(2).unwrap().as_bytes().len();
|
||||||
|
|
||||||
|
remainder = &remainder[pop_len..];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(c) = self.colordef_re.captures(remainder) {
|
||||||
|
let all = c.get(0).unwrap();
|
||||||
|
let matched_len = all.as_bytes().len();
|
||||||
|
|
||||||
|
let color_number = cap_int(c.get(1).unwrap()).unwrap_or(0);
|
||||||
|
let system = cap_int(c.get(2).unwrap()).unwrap_or(1);
|
||||||
|
let a = cap_int(c.get(3).unwrap()).unwrap_or(0);
|
||||||
|
let b = cap_int(c.get(4).unwrap()).unwrap_or(0);
|
||||||
|
let c = cap_int(c.get(5).unwrap()).unwrap_or(0);
|
||||||
|
|
||||||
|
if system == 1 {
|
||||||
|
self.sixel.data.push(SixelData::DefineColorMapHSL {
|
||||||
|
color_number,
|
||||||
|
hue_angle: a,
|
||||||
|
lightness: b,
|
||||||
|
saturation: c,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
let r = a as f32 * 255.0 / 100.;
|
||||||
|
let g = b as f32 * 255.0 / 100.;
|
||||||
|
let b = c as f32 * 255.0 / 100.;
|
||||||
|
let rgb = RgbColor::new_8bpc(r as u8, g as u8, b as u8); // FIXME: from linear
|
||||||
|
self.sixel
|
||||||
|
.data
|
||||||
|
.push(SixelData::DefineColorMapRGB { color_number, rgb });
|
||||||
|
}
|
||||||
|
|
||||||
|
remainder = &remainder[matched_len..];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(c) = self.repeat_re.captures(remainder) {
|
||||||
|
let all = c.get(0).unwrap();
|
||||||
|
let matched_len = all.as_bytes().len();
|
||||||
|
|
||||||
|
let repeat_count = cap_int(c.get(1).unwrap()).unwrap_or(1);
|
||||||
|
let data = c.get(2).unwrap().as_bytes()[0] - 0x3f;
|
||||||
|
self.sixel
|
||||||
|
.data
|
||||||
|
.push(SixelData::Repeat { repeat_count, data });
|
||||||
|
remainder = &remainder[matched_len..];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
log::error!(
|
||||||
|
"finished sixel parse with {} bytes pending {:?}",
|
||||||
|
remainder.len(),
|
||||||
|
std::str::from_utf8(&remainder[0..24.min(remainder.len())])
|
||||||
|
);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
use crate::escape::parser::Parser;
|
||||||
|
use crate::escape::{Action, Esc, EscCode};
|
||||||
|
use pretty_assertions::assert_eq;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn sixel() {
|
||||||
|
let mut p = Parser::new();
|
||||||
|
let actions = p.parse_as_vec(b"\x1bP1;2;3;q@\x1b\\");
|
||||||
|
assert_eq!(
|
||||||
|
vec![
|
||||||
|
Action::Sixel(Box::new(Sixel {
|
||||||
|
pan: 2,
|
||||||
|
pad: 1,
|
||||||
|
pixel_width: None,
|
||||||
|
pixel_height: None,
|
||||||
|
background_is_transparent: false,
|
||||||
|
horizontal_grid_size: Some(3),
|
||||||
|
data: vec![SixelData::Data(1)]
|
||||||
|
})),
|
||||||
|
Action::Esc(Esc::Code(EscCode::StringTerminator)),
|
||||||
|
],
|
||||||
|
actions
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(format!("{}", actions[0]), "\x1bP0;0;3q@");
|
||||||
|
|
||||||
|
// This is the "HI" example from wikipedia
|
||||||
|
let mut p = Parser::new();
|
||||||
|
let actions = p.parse_as_vec(
|
||||||
|
b"\x1bPq\
|
||||||
|
#0;2;0;0;0#1;2;100;100;0#2;2;0;100;0\
|
||||||
|
#1~~@@vv@@~~@@~~$\
|
||||||
|
#2??}}GG}}??}}??-\
|
||||||
|
#1!14@\
|
||||||
|
\x1b\\",
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
format!("{}", actions[0]),
|
||||||
|
"\x1bP0;0q\
|
||||||
|
#0;2;0;0;0#1;2;100;100;0#2;2;0;100;0\
|
||||||
|
#1~~@@vv@@~~@@~~$\
|
||||||
|
#2??}}GG}}??}}??-\
|
||||||
|
#1!14@"
|
||||||
|
);
|
||||||
|
|
||||||
|
use SixelData::*;
|
||||||
|
assert_eq!(
|
||||||
|
vec![
|
||||||
|
Action::Sixel(Box::new(Sixel {
|
||||||
|
pan: 2,
|
||||||
|
pad: 1,
|
||||||
|
pixel_width: None,
|
||||||
|
pixel_height: None,
|
||||||
|
background_is_transparent: false,
|
||||||
|
horizontal_grid_size: None,
|
||||||
|
data: vec![
|
||||||
|
DefineColorMapRGB {
|
||||||
|
color_number: 0,
|
||||||
|
rgb: RgbColor::new_8bpc(0, 0, 0)
|
||||||
|
},
|
||||||
|
DefineColorMapRGB {
|
||||||
|
color_number: 1,
|
||||||
|
rgb: RgbColor::new_8bpc(255, 255, 0)
|
||||||
|
},
|
||||||
|
DefineColorMapRGB {
|
||||||
|
color_number: 2,
|
||||||
|
rgb: RgbColor::new_8bpc(0, 255, 0)
|
||||||
|
},
|
||||||
|
SelectColorMapEntry(1),
|
||||||
|
Data(63),
|
||||||
|
Data(63),
|
||||||
|
Data(1),
|
||||||
|
Data(1),
|
||||||
|
Data(55),
|
||||||
|
Data(55),
|
||||||
|
Data(1),
|
||||||
|
Data(1),
|
||||||
|
Data(63),
|
||||||
|
Data(63),
|
||||||
|
Data(1),
|
||||||
|
Data(1),
|
||||||
|
Data(63),
|
||||||
|
Data(63),
|
||||||
|
CarriageReturn,
|
||||||
|
SelectColorMapEntry(2),
|
||||||
|
Data(0),
|
||||||
|
Data(0),
|
||||||
|
Data(62),
|
||||||
|
Data(62),
|
||||||
|
Data(8),
|
||||||
|
Data(8),
|
||||||
|
Data(62),
|
||||||
|
Data(62),
|
||||||
|
Data(0),
|
||||||
|
Data(0),
|
||||||
|
Data(62),
|
||||||
|
Data(62),
|
||||||
|
Data(0),
|
||||||
|
Data(0),
|
||||||
|
NewLine,
|
||||||
|
SelectColorMapEntry(1),
|
||||||
|
Repeat {
|
||||||
|
repeat_count: 14,
|
||||||
|
data: 1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})),
|
||||||
|
Action::Esc(Esc::Code(EscCode::StringTerminator)),
|
||||||
|
],
|
||||||
|
actions
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user