use crate::{Page, sizer::Sizer};
use std::io::{Result, Write};
pub struct Region<'a> {
pub(super) page: &'a mut Page,
pub(super) oy: i32,
pub(super) ox: i32,
pub(super) sy: i32,
pub(super) sx: i32,
pub(super) cy0: i32,
pub(super) cx0: i32,
pub(super) cy1: i32,
pub(super) cx1: i32,
pub(super) wy: i32,
pub(super) wx: i32,
pub(super) hfb: u16,
}
impl Region<'_> {
#[inline]
pub fn full(&mut self) -> Region<'_> {
Region {
page: self.page,
oy: self.oy,
ox: self.ox,
sy: self.sy,
sx: self.sx,
cy0: self.cy0,
cx0: self.cx0,
cy1: self.cy1,
cx1: self.cx1,
wy: 0,
wx: 0,
hfb: 99,
}
}
#[inline]
pub fn region(&mut self, y: i32, x: i32, sy: i32, sx: i32) -> Region<'_> {
let oy = self.oy + y;
let ox = self.ox + x;
Region {
page: self.page,
oy,
ox,
sy,
sx,
cy0: self.cy0.max(oy),
cx0: self.cx0.max(ox),
cy1: self.cy1.min(oy + sy),
cx1: self.cx1.min(ox + sx),
wy: 0,
wx: 0,
hfb: 99,
}
}
#[inline]
pub fn region_inplace(&mut self, y: i32, x: i32, sy: i32, sx: i32) {
let oy = self.oy + y;
let ox = self.ox + x;
self.oy = oy;
self.ox = ox;
self.sy = sy;
self.sx = sx;
self.cy0 = self.cy0.max(oy);
self.cx0 = self.cx0.max(ox);
self.cy1 = self.cy1.min(oy + sy);
self.cx1 = self.cx1.min(ox + sx);
self.wy = 0;
self.wx = 0;
self.hfb = 99;
}
pub fn sizer(&self) -> Sizer {
self.page.sizer.clone()
}
pub fn size(&self) -> (i32, i32) {
(self.sy, self.sx)
}
pub fn sy(&self) -> i32 {
self.sy
}
pub fn sx(&self) -> i32 {
self.sx
}
pub fn get_y(&self) -> i32 {
self.wy
}
pub fn get_x(&self) -> i32 {
self.wx
}
pub fn is_visible(&self) -> bool {
self.cy0 < self.cy1 && self.cx0 < self.cx1
}
pub fn hfb(&mut self, hfb: u16) -> &mut Self {
self.hfb = hfb;
self
}
pub fn at(&mut self, wy: i32, wx: i32) -> &mut Self {
self.wy = wy;
self.wx = wx;
self
}
pub fn char(&mut self, ch: char) -> &mut Self {
let mut buf = [0; 4];
self.writeb(ch.encode_utf8(&mut buf).as_bytes());
self
}
pub fn spaces(&mut self, n: i32) -> &mut Self {
let buf = [b' '; 1];
for _ in 0..n {
self.writeb(&buf[..1]);
}
self
}
pub fn repl_char(&mut self) -> &mut Self {
self.writeb("\u{FFFD}".as_bytes());
self
}
pub fn origin(&mut self) -> &mut Self {
self.wy = 0;
self.wx = 0;
self
}
pub fn skip(&mut self, n: i32) -> &mut Self {
self.wx += n;
while self.wx > self.sx {
self.wx -= self.sx;
self.wy += 1;
}
self
}
pub fn newline(&mut self) -> &mut Self {
self.wy += 1;
self.wx = 0;
self
}
pub fn clear_all_99(&mut self) -> &mut Self {
self.hfb = 99;
self.clear_all()
}
pub fn clear_all(&mut self) -> &mut Self {
for y in self.cy0..self.cy1 {
let row = &mut self.page.rows[y as usize];
row.setn(self.cx0, self.cx1, self.hfb, b" ".as_slice());
}
self.wy = 0;
self.wx = 0;
self
}
pub fn clear_eol_99(&mut self) -> &mut Self {
self.hfb = 99;
self.clear_eol()
}
pub fn clear_eol(&mut self) -> &mut Self {
self.spaces(self.sx - self.wx)
}
#[inline]
pub fn text(&mut self, text: &str) -> &mut Self {
self.writeb(text.as_bytes());
self
}
#[inline]
pub fn bytes(&mut self, text: &[u8]) -> &mut Self {
self.writeb(text);
self
}
pub fn get(&mut self, y: i32, x: i32) -> Option<(u16, &[u8])> {
let y = y + self.cy0;
let x = x + self.cx0;
if y < self.cx0 || y >= self.cy1 || x < self.cx0 || x >= self.cx1 {
return None;
}
self.page.rows[y as usize].get(x)
}
pub fn set_hfb(&mut self, y: i32, x: i32, hfb: u16) {
let y = y + self.cy0;
let x = x + self.cx0;
if y >= self.cx0 && y < self.cy1 && x >= self.cx0 && x < self.cx1 {
self.page.rows[y as usize].set_hfb(x, hfb);
}
}
pub fn set(&mut self, y: i32, x: i32, hfb: u16, data: &[u8]) {
let y = y + self.cy0;
let x = x + self.cx0;
if y >= self.cy0 && y < self.cy1 && x >= self.cx0 && x < self.cx1 {
self.page.rows[y as usize].set(x, hfb, data);
}
}
pub fn braille_plot(&mut self, py: i32, px: i32, hfb: u16, set: bool) {
let y = (py >> 2) + self.cy0;
let x = (px >> 1) + self.cx0;
if y >= self.cy0 && y < self.cy1 && x >= self.cx0 && x < self.cx1 {
let (b1, b2) = match ((py & 3) << 1) + (px & 1) {
0 => (0x00, 0x01),
1 => (0x00, 0x08),
2 => (0x00, 0x02),
3 => (0x00, 0x10),
4 => (0x00, 0x04),
5 => (0x00, 0x20),
6 => (0x01, 0x00),
_ => (0x02, 0x00),
};
let row = &mut self.page.rows[y as usize];
let mut glyph = [0xE2, 0xA0, 0x80];
if let Some((_, s)) = row.get(x)
&& s.len() == 3
&& s[0] == 0xE2
&& (s[1] & 0xFC) == 0xA0
{
glyph[1] = s[1];
glyph[2] = s[2];
}
if set {
glyph[1] |= b1;
glyph[2] |= b2;
} else if 0 == ((glyph[1] & b1) | (glyph[2] & b2)) {
return;
} else {
glyph[1] &= !b1;
glyph[2] &= !b2;
}
row.set(x, hfb, &glyph);
}
}
fn writeb(&mut self, text: &[u8]) {
let mut p = Scan::new(text);
let mut y = self.wy + self.oy;
let mut y_in_clip = y >= self.cy0 && y < self.cy1;
loop {
let start = p;
if p.consume_lf() {
self.wx = 0;
self.wy += 1;
if self.wy >= self.sy {
return;
}
y = self.wy + self.oy;
y_in_clip = y >= self.cy0 && y < self.cy1;
continue;
}
if p.consume_hfb(&mut self.hfb) {
continue;
}
if let Some((inc, repl)) = p.measure(&self.page.sizer) {
if self.wx >= self.sx {
self.wx = 0;
self.wy += 1;
if self.wy >= self.sy {
return;
}
y = self.wy + self.oy;
y_in_clip = y >= self.cy0 && y < self.cy1;
}
let x = self.wx + self.ox;
if y_in_clip && x >= self.cx0 && x < self.cx1 {
let row = &mut self.page.rows[y as usize];
if inc == 1 {
if !repl {
row.set(x, self.hfb, start.slice_to(&p));
} else {
row.set_repl(x, self.hfb);
}
self.wx += 1;
} else if self.wx + 2 > self.sx {
p = start;
row.set(x, self.hfb, b" ".as_slice());
self.wx += 1;
} else if x + 1 < self.cx1 {
row.set_left(x, self.hfb, start.slice_to(&p));
row.set_right(x + 1, self.hfb);
self.wx += 2;
} else {
row.set_repl(x, self.hfb);
self.wx += 2;
}
} else {
if inc == 1 {
self.wx += 1;
} else if self.wx + 2 > self.sx {
p = start;
self.wx += 1;
} else if y_in_clip && x + 1 == self.cx0 && x + 1 < self.cx1 {
self.page.rows[y as usize].set_repl(x + 1, self.hfb);
self.wx += 2;
} else {
self.wx += 2;
}
}
continue;
}
break;
}
}
}
impl Write for Region<'_> {
fn write(&mut self, buf: &[u8]) -> Result<usize> {
self.writeb(buf);
Ok(buf.len())
}
fn flush(&mut self) -> Result<()> {
Ok(())
}
}
impl std::fmt::Debug for Region<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Region")
.field("oy", &self.oy)
.field("ox", &self.ox)
.field("sy", &self.sy)
.field("sx", &self.sx)
.field("cy0", &self.cy0)
.field("cx0", &self.cx0)
.field("cy1", &self.cy1)
.field("cx1", &self.cx1)
.field("wy", &self.wy)
.field("wx", &self.wx)
.field("hfb", &self.hfb)
.finish()
}
}
#[derive(Copy, Clone)]
pub struct Scan<'a> {
p: &'a [u8],
}
impl<'a> Scan<'a> {
pub fn new(slice: &'a [u8]) -> Self {
Scan { p: slice }
}
#[inline]
fn len(&self) -> usize {
self.p.len()
}
#[inline]
fn skip(&mut self, skip: usize) {
self.p = &self.p[skip..];
}
fn consume_lf(&mut self) -> bool {
if !self.p.is_empty() && self.p[0] == b'\n' {
self.skip(1);
true
} else {
false
}
}
fn consume_hfb(&mut self, hfb: &mut u16) -> bool {
if !self.p.is_empty() && self.p[0] == 0 && self.p.len() >= 4 {
if self.p[1] < b'@' {
*hfb = (self.p[1] as u16 & 15) * 100
+ (self.p[2] as u16 & 15) * 10
+ (self.p[3] as u16 & 15);
} else {
*hfb = ((self.p[1] as u16 & 15) << 12)
| ((self.p[2] as u16 & 63) << 6)
| (self.p[3] as u16 & 63);
}
self.skip(4);
true
} else {
false
}
}
fn measure(&mut self, sizer: &Sizer) -> Option<(u16, bool)> {
if !self.p.is_empty() {
let (len, wid) = sizer.measure(self.p);
self.skip(len);
Some((wid.unsigned_abs(), wid < 0))
} else {
None
}
}
pub fn measure_rest(&mut self, sizer: &Sizer) -> u16 {
let mut x = 0;
while !self.p.is_empty() {
match self.p[0] {
0 => {
if self.p.len() < 4 {
break;
}
self.skip(4);
}
b'\n' => {
break;
}
_ => {
let (len, wid) = sizer.measure(self.p);
self.skip(len);
x += wid.unsigned_abs();
}
}
}
x
}
fn slice_to(&'a self, end: &'a Scan<'a>) -> &'a [u8] {
let len0 = self.len();
let len1 = end.len();
&self.p[..len0 - len1]
}
}
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
pub struct HFB(pub u16);
impl std::fmt::Display for HFB {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let c0 = char::from_u32(((self.0 as u32) >> 12) + 64).unwrap();
let c1 = char::from_u32((((self.0 as u32) >> 6) & 63) + 64).unwrap();
let c2 = char::from_u32((self.0 as u32) + 64).unwrap();
write!(f, "\0{c0}{c1}{c2}")
}
}