1use alloc::vec::Vec;
4
5use crate::std::sync::atomic::{AtomicU8, AtomicUsize, Ordering};
6use crate::{dimension::*, dither::*, quant::MethodForDiffuse, std::fmt, Error, Result};
7
8mod color_map;
9mod constants;
10mod raster;
11mod repeat;
12mod scrolling_mode;
13mod selector;
14mod sixel_char;
15mod sixel_control;
16
17pub use color_map::*;
18pub use constants::*;
19pub use raster::*;
20pub use repeat::*;
21pub use scrolling_mode::*;
22pub use selector::*;
23pub use sixel_char::*;
24pub use sixel_control::*;
25
26static CUR_CHAR: AtomicU8 = AtomicU8::new(0);
27static CUR_COLOR: AtomicUsize = AtomicUsize::new(0);
28static SAVE_CHAR_COUNT: AtomicUsize = AtomicUsize::new(0);
29
30const COLOR_THRESHOLD: usize = 24;
31
32fn current_char() -> SixelChar {
33 CUR_CHAR.load(Ordering::Relaxed).into()
34}
35
36fn set_current_char(sixel_char: SixelChar) {
37 CUR_CHAR.store(sixel_char.into(), Ordering::SeqCst);
38}
39
40fn current_color() -> BasicColor {
41 CUR_COLOR.load(Ordering::Relaxed).into()
42}
43
44fn set_current_color(color: usize) {
45 CUR_COLOR.store(color, Ordering::SeqCst);
46}
47
48fn save_char_count() -> usize {
49 SAVE_CHAR_COUNT.load(Ordering::Relaxed)
50}
51
52fn set_save_char_count(count: usize) {
53 SAVE_CHAR_COUNT.store(count, Ordering::SeqCst);
54}
55
56fn increment_save_char_count() {
57 SAVE_CHAR_COUNT.store(save_char_count() + 1, Ordering::SeqCst);
58}
59
60pub enum SixelItem {
62 Color(BasicColor),
63 Char(SixelChar),
64 Raster(Raster),
65 Repeat(Repeat),
66 Control(SixelControl),
67}
68
69impl fmt::Display for SixelItem {
70 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
71 match self {
72 Self::Color(data) => write!(f, "{data}"),
73 Self::Char(data) => write!(f, "{data}"),
74 Self::Raster(data) => write!(f, "{data}"),
75 Self::Repeat(data) => write!(f, "{data}"),
76 Self::Control(data) => write!(f, "{data}"),
77 }
78 }
79}
80
81pub struct SixelCounts {
83 pub rows: usize,
84 pub sixel_len: usize,
85 pub sixel_rem: usize,
86 pub rgb_len: usize,
87 pub hits: Vec<u8>,
88 pub threshold: u8,
89}
90
91impl SixelCounts {
92 pub const fn new() -> Self {
94 Self {
95 rows: 0,
96 sixel_len: 0,
97 sixel_rem: 0,
98 rgb_len: 0,
99 hits: Vec::new(),
100 threshold: 1,
101 }
102 }
103
104 pub fn create(
106 rows: usize,
107 sixel_len: usize,
108 sixel_rem: usize,
109 rgb_len: usize,
110 pixels: usize,
111 ) -> Self {
112 Self {
113 rows,
114 sixel_len,
115 sixel_rem,
116 rgb_len,
117 hits: vec![0; pixels],
118 threshold: 1,
119 }
120 }
121}
122
123struct SixelState<'s> {
125 pub pixels: &'s mut [u8],
126 pub sixel_data: &'s mut Vec<SixelItem>,
127 pub color_map: &'s mut ColorMap,
128 pub cur_color: &'s mut [u8; 3],
129 pub pal_idx: &'s mut usize,
130}
131
132pub struct DeviceControlString {
156 mode: DcsMode,
157 ratio: PixelAspectRatio,
158 background_color: DcsBackground,
159 raster: Raster,
160 color_map: ColorMap,
161 sixel_data: Vec<SixelItem>,
162}
163
164impl DeviceControlString {
165 pub const fn new() -> Self {
167 Self {
168 mode: DcsMode::new(),
169 ratio: PixelAspectRatio::new(),
170 background_color: DcsBackground::new(),
171 raster: Raster::new(),
172 color_map: ColorMap::new(),
173 sixel_data: Vec::new(),
174 }
175 }
176
177 pub const fn mode(&self) -> DcsMode {
179 self.mode
180 }
181
182 pub fn set_mode(&mut self, mode: DcsMode) {
184 self.mode = mode;
185 }
186
187 pub fn with_mode(mut self, mode: DcsMode) -> Self {
189 self.set_mode(mode);
190 self
191 }
192
193 pub const fn ratio(&self) -> PixelAspectRatio {
195 self.ratio
196 }
197
198 pub fn set_ratio(&mut self, ratio: PixelAspectRatio) {
200 self.ratio = ratio;
201 }
202
203 pub fn with_ratio(mut self, ratio: PixelAspectRatio) -> Self {
205 self.set_ratio(ratio);
206 self
207 }
208
209 pub const fn background_color(&self) -> DcsBackground {
211 self.background_color
212 }
213
214 pub fn set_background_color(&mut self, background_color: DcsBackground) {
216 self.background_color = background_color;
217 }
218
219 pub fn with_background_color(mut self, background_color: DcsBackground) -> Self {
221 self.set_background_color(background_color);
222 self
223 }
224
225 pub const fn raster(&self) -> Raster {
227 self.raster
228 }
229
230 pub fn set_raster(&mut self, raster: Raster) {
232 self.raster = raster;
233 }
234
235 pub fn with_raster(mut self, raster: Raster) -> Self {
237 self.set_raster(raster);
238 self
239 }
240
241 pub const fn color_map(&self) -> &ColorMap {
243 &self.color_map
244 }
245
246 pub fn set_color_map(&mut self, color_map: ColorMap) {
248 self.color_map = color_map;
249 }
250
251 pub fn with_color_map(mut self, color_map: ColorMap) -> Self {
253 self.set_color_map(color_map);
254 self
255 }
256
257 pub fn sixel_data(&self) -> &[SixelItem] {
259 self.sixel_data.as_ref()
260 }
261
262 pub fn set_sixel_data<S: Into<Vec<SixelItem>>>(&mut self, sixel_data: S) {
264 self.sixel_data = sixel_data.into();
265 }
266
267 pub fn with_sixel_data<S: Into<Vec<SixelItem>>>(mut self, sixel_data: S) -> Self {
269 self.set_sixel_data(sixel_data);
270 self
271 }
272
273 pub fn from_rgb(
275 pixels: &mut [u8],
276 width: usize,
277 height: usize,
278 diffuse_method: MethodForDiffuse,
279 ) -> Result<Self> {
280 let depth = 3;
281 let area = width.saturating_mul(height).saturating_mul(depth);
282 let pixel_len = pixels.len();
283
284 if pixel_len < area {
285 Err(Error::Output(format!("invalid area [width*height*depth(3)] ({area}) is out-of-bounds, pixel buffer length ({pixel_len})")))
286 } else if width == 0 || height == 0 {
287 Err(Error::Output(format!(
288 "invalid width/height, must be non-zero | width: {width}, height: {height}"
289 )))
290 } else {
291 let mut color_map = ColorMap::new();
292 color_map.inner_mut().push(ColorMapItem::new());
294
295 let mut pal_idx = 0;
296 let mut cur_color = [0, 0, 0];
297
298 let rgb_len = width.saturating_mul(3);
300
301 let sixel_len = rgb_len.saturating_mul(6);
304 let sixel_rows = area / sixel_len;
305 let sixel_rem = area % sixel_len;
306
307 let max_sixels = width.saturating_mul(sixel_rows).saturating_add(sixel_rem);
308 let mut sixel_data = Vec::with_capacity(max_sixels);
309
310 let mut sixel_counts = SixelCounts::create(
311 sixel_rows,
312 sixel_len,
313 sixel_rem,
314 rgb_len,
315 width.saturating_mul(height),
316 );
317
318 let mut sd = SpaceDimension {
319 width,
320 height,
321 ..Default::default()
322 };
323
324 let mut state = SixelState {
325 pixels,
326 sixel_data: &mut sixel_data,
327 color_map: &mut color_map,
328 cur_color: &mut cur_color,
329 pal_idx: &mut pal_idx,
330 };
331
332 state.build_color_map();
333
334 state.encode_body(&mut sd, &mut sixel_counts, diffuse_method)?;
335
336 Ok(Self::new()
337 .with_background_color(DcsBackground::Background)
338 .with_color_map(color_map)
339 .with_raster(Raster::create(1, 1, width, height))
340 .with_sixel_data(sixel_data))
341 }
342 }
343}
344
345impl<'s> SixelState<'s> {
346 fn build_color_map(&mut self) {
347 let mut counts: hashbrown::HashMap<u16, usize> = hashbrown::HashMap::with_capacity(1 << 15);
348
349 self.pixels.chunks_exact_mut(3).for_each(|c| {
350 c[0] &= 0xf8;
352 c[1] &= 0xf8;
353 c[2] &= 0xf8;
354
355 let idx = ((c[0] as u16) << 7) | ((c[1] as u16) << 2) | (c[2] >> 3) as u16;
356
357 let count = counts.entry(idx).or_insert(0);
358 *count += 1;
359 });
360
361 for (key, val) in counts.iter() {
362 let item = ColorMapItem::create_rgb(
363 0,
364 ((key & 0b111_1100_0000_0000) >> 7) as u8,
365 ((key & 0b11_1110_0000) >> 2) as u8,
366 ((key & 0b11_111) << 3) as u8,
367 );
368
369 let has_item = self
370 .color_map
371 .items()
372 .iter()
373 .any(|c| c.x() == item.x() && c.y() == item.y() && c.z() == item.z());
374
375 if val >= &COLOR_THRESHOLD && !has_item {
376 let idx = self.color_map.items().len();
377 self.color_map.inner_mut().push(item.with_number(idx));
378 }
379 }
380 }
381
382 fn encode_body(
383 &mut self,
384 sd: &mut SpaceDimension,
385 sixel_counts: &mut SixelCounts,
386 diffuse_method: MethodForDiffuse,
387 ) -> Result<()> {
388 let (sixel_rows, sixel_len, sixel_rem) = (
389 sixel_counts.rows,
390 sixel_counts.sixel_len,
391 sixel_counts.sixel_rem,
392 );
393
394 for row in 0..sixel_rows {
395 let row_idx = row * sixel_len;
396
397 for six_idx in 0..6 {
399 self.encode_row(sd, sixel_counts, six_idx, row_idx, diffuse_method);
400
401 put_flash(self.sixel_data);
402
403 if six_idx < 5 {
404 self.sixel_data
406 .push(SixelItem::Control(SixelControl::CarriageReturn));
407 }
408 }
409
410 self.sixel_data
411 .push(SixelItem::Control(SixelControl::NewLine));
412 }
413
414 if sixel_rem != 0 {
416 for six_idx in 0..sixel_rem {
417 self.encode_remainder_row(sd, sixel_counts, six_idx, diffuse_method);
418
419 put_flash(self.sixel_data);
420
421 if six_idx < sixel_rem - 1 {
422 self.sixel_data
424 .push(SixelItem::Control(SixelControl::CarriageReturn));
425 }
426 }
427
428 self.sixel_data
429 .push(SixelItem::Control(SixelControl::NewLine));
430 }
431
432 Ok(())
433 }
434
435 fn encode_row(
436 &mut self,
437 sd: &mut SpaceDimension,
438 sixel_counts: &mut SixelCounts,
439 six_idx: usize,
440 row_idx: usize,
441 diffuse_method: MethodForDiffuse,
442 ) -> bool {
443 let mut overwrite = false;
444
445 for col_idx in (0..sixel_counts.rgb_len).step_by(3) {
446 sd.x = col_idx;
447 sd.y = row_idx;
448
449 let pos =
450 (row_idx + col_idx).saturating_add(six_idx.saturating_mul(sixel_counts.sixel_len));
451 if pos.saturating_add(sixel_counts.sixel_len) < self.pixels.len() {
452 method::apply_15bpp_dither(&mut self.pixels[pos..], *sd, diffuse_method).ok();
453 }
454
455 let off = row_idx + col_idx;
458
459 overwrite |= self.inner_encode(sixel_counts, six_idx, off);
460 }
461
462 overwrite
463 }
464
465 fn inner_encode(&mut self, sixel_counts: &mut SixelCounts, six_idx: usize, off: usize) -> bool {
466 let rgb_len = sixel_counts.rgb_len;
467 let offset = off + (rgb_len * six_idx);
468
469 let (r, g, b) = (
470 self.pixels[offset],
471 self.pixels[offset + 1],
472 self.pixels[offset + 2],
473 );
474
475 let mut color_item = ColorMapItem::create_rgb(0, r, g, b);
476
477 if color_item.is_black() {
478 *self.pal_idx = 0;
479 } else {
480 let mut delta = i16::MAX;
482
483 self.color_map.items().iter().for_each(|c| {
485 let id = (color_item.x() as i16 - c.x() as i16).abs()
486 + (color_item.y() as i16 - c.y() as i16).abs()
487 + (color_item.z() as i16 - c.z() as i16).abs();
488
489 if id <= delta {
490 delta = id;
491 color_item.set_number(c.number());
492 }
493 });
494
495 let idx = color_item.number();
496 if idx != 0 {
497 *self.pal_idx = idx;
498 }
499 }
500
501 let sixel_char = if color_item.is_black() {
504 SixelChar::full()
505 } else {
506 SixelChar::from_index(six_idx)
507 };
508 put_sixel_char(self.sixel_data, sixel_char, *self.pal_idx);
509
510 true
511 }
512
513 fn encode_remainder_row(
514 &mut self,
515 sd: &mut SpaceDimension,
516 sixel_counts: &SixelCounts,
517 six_idx: usize,
518 diffuse_method: MethodForDiffuse,
519 ) -> bool {
520 let (sixel_rows, sixel_len, sixel_rem, rgb_len) = (
521 sixel_counts.rows,
522 sixel_counts.sixel_len,
523 sixel_counts.sixel_rem,
524 sixel_counts.rgb_len,
525 );
526 let row_idx = sixel_rows * sixel_len;
527
528 let mut overwrite = false;
529
530 for col_idx in (0..rgb_len).step_by(3) {
531 sd.x = col_idx;
532 sd.y = row_idx;
533
534 let pos =
535 (row_idx + col_idx).saturating_add(six_idx.saturating_mul(sixel_counts.sixel_len));
536 if pos < self.pixels.len() {
537 method::apply_15bpp_dither(&mut self.pixels[pos..], *sd, diffuse_method).ok();
538 }
539
540 let p1_off = row_idx * col_idx;
543 let p2_off = row_idx * col_idx + rgb_len;
544 let p3_off = row_idx * col_idx + (rgb_len * 2);
545 let p4_off = row_idx * col_idx + (rgb_len * 3);
546 let p5_off = row_idx * col_idx + (rgb_len * 4);
547
548 let sixel_plane = [
549 [
550 self.pixels[p1_off],
551 self.pixels[p1_off + 1],
552 self.pixels[p1_off + 2],
553 ],
554 if sixel_rem >= 2 {
555 [
556 self.pixels[p2_off],
557 self.pixels[p2_off + 1],
558 self.pixels[p2_off + 2],
559 ]
560 } else {
561 [0, 0, 0]
562 },
563 if sixel_rem >= 3 {
564 [
565 self.pixels[p3_off],
566 self.pixels[p3_off + 1],
567 self.pixels[p3_off + 2],
568 ]
569 } else {
570 [0, 0, 0]
571 },
572 if sixel_rem >= 4 {
573 [
574 self.pixels[p4_off],
575 self.pixels[p4_off + 1],
576 self.pixels[p4_off + 2],
577 ]
578 } else {
579 [0, 0, 0]
580 },
581 if sixel_rem >= 5 {
582 [
583 self.pixels[p5_off],
584 self.pixels[p5_off + 1],
585 self.pixels[p5_off + 2],
586 ]
587 } else {
588 [0, 0, 0]
589 },
590 [0, 0, 0],
591 ];
592
593 let sixel_char = SixelChar::from_plane(
596 &sixel_plane,
597 six_idx,
598 &sixel_counts.hits[(p1_off / 3)..],
599 sixel_counts.threshold,
600 );
601 put_sixel_char(self.sixel_data, sixel_char, *self.pal_idx);
602
603 overwrite |= sixel_plane
604 .iter()
605 .map(|c| (c != self.cur_color) as u8)
606 .sum::<u8>()
607 != 0;
608 }
609
610 overwrite
611 }
612}
613
614fn put_sixel_char(sixel_data: &mut Vec<SixelItem>, sixel_char: SixelChar, color_idx: usize) {
615 let cur_char = current_char();
616 let color = BasicColor::from(color_idx);
617
618 let cur_color = current_color();
620 let color_change = cur_color != color;
621 if color_change {
622 log::trace!("Changing color: current {cur_color}, new {color}");
623 sixel_data.push(SixelItem::Color(color));
624 set_current_color(color.into());
625 }
626
627 if cur_char == sixel_char && !color_change {
628 increment_save_char_count();
629
630 let count = save_char_count();
631 log::trace!("Incrementing current character: char {cur_char}, count {count}");
632 } else {
633 let count = save_char_count();
634 log::trace!("Flashing character from put_sixel_char: char {sixel_char}, count {count}, saved char {cur_char}");
635
636 put_flash(sixel_data);
637
638 set_current_char(sixel_char);
639 set_save_char_count(1);
640
641 let cur_char = current_char();
642 log::trace!("Flashed character from put_sixel_char, new saved char: {cur_char}");
643 }
644}
645
646fn put_flash(sixel_data: &mut Vec<SixelItem>) {
647 let cur_char = current_char();
648 let save_count = save_char_count();
649
650 if save_count >= 3 {
651 log::trace!("Outputting repeat: count {save_count}, char {cur_char}");
652 sixel_data.push(SixelItem::Repeat(Repeat::create(save_count, cur_char)));
653 } else {
654 log::trace!("Outputting manual repeat: count {save_count}, char {cur_char}");
655 for _ in 0..save_count {
656 sixel_data.push(SixelItem::Char(cur_char));
657 }
658 }
659
660 set_current_char(SixelChar::new());
661 set_save_char_count(0);
662}
663
664impl fmt::Display for DeviceControlString {
665 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
666 let dcs = DcsFunction::from(self.mode);
667 let st = StFunction::from(self.mode);
668
669 write!(f, "{dcs}q{}", self.raster)?;
670 write!(f, "{}", self.color_map)?;
671 for s in self.sixel_data.iter() {
672 write!(f, "{s}")?;
673 }
674 write!(f, "{st}")
675 }
676}