1#![allow(clippy::identity_op, reason = "symmetry")]
4
5use super::super::{
6 DitherConf, PixelFormat, SixelColorModel, SixelEncodePolicy, SixelQuality, SIXEL_PALETTE_MAX,
7};
8use super::{SixelNode, SixelOutput};
9#[cfg(feature = "fmt")]
10use crate::NumToStr;
11use crate::{iif, sf, vec_ as vec, IoWrite, SixelError, SixelResult, Vec};
12
13impl<W: IoWrite> SixelOutput<W> {
14 fn penetrate(
21 &mut self,
22 nwrite: usize, dcs_start: &str, dcs_end: &str, ) {
26 let splitsize = Self::SCREEN_PACKET_SIZE - dcs_start.len() - dcs_end.len();
27 let mut pos = 0;
28 while pos < nwrite {
29 let _ = self.fn_write.write(dcs_start.as_bytes());
30 let _ = self.fn_write.write(&self.buffer.as_bytes()[pos..pos + splitsize]);
31 let _ = self.fn_write.write(dcs_end.as_bytes());
32 pos += splitsize;
33 }
34 }
35
36 fn advance(&mut self) {
40 if self.buffer.len() >= SixelOutput::<W>::PACKET_SIZE {
41 if self.penetrate_multiplexer {
42 self.penetrate(
43 SixelOutput::<W>::PACKET_SIZE,
44 Self::DCS_START_7BIT,
45 Self::DCS_END_7BIT,
46 );
47 } else {
48 let _ =
49 self.fn_write.write(&self.buffer.as_bytes()[..SixelOutput::<W>::PACKET_SIZE]);
50 }
51 self.buffer.drain(0..SixelOutput::<W>::PACKET_SIZE);
52 }
53 }
54
55 pub fn putc(&mut self, value: char) {
57 self.buffer.push(value);
58 }
59
60 pub fn puts(&mut self, value: &str) {
62 self.buffer.push_str(value);
63 }
64
65 pub(crate) fn puti(&mut self, i: i32) {
70 #[cfg(not(feature = "fmt"))]
71 self.puts(crate::format!("{}", i).as_str());
72 #[cfg(feature = "fmt")]
73 {
74 let mut buf = [0u8; 11];
75 self.puts(i.to_str_base(10, &mut buf));
76 }
77 }
78
79 #[expect(unused, reason = "…")]
84 pub(crate) fn putb(&mut self, b: u8) {
85 #[cfg(not(feature = "fmt"))]
86 self.puts(crate::format!("{}", b).as_str());
87 #[cfg(feature = "fmt")]
88 {
89 let mut buf = [0u8; 3];
90 self.puts(b.to_str_base(10, &mut buf));
91 }
92 }
93
94 #[rustfmt::skip]
96 fn save_pixel_char(&self) -> char {
97 #[cfg(any(feature = "safe_image", not(feature = "unsafe_str")))]
98 { if let Some(c) = char::from_u32(self.save_pixel as u32) { c } else { unreachable!() } }
99
100 #[cfg(all(not(feature = "safe_text"), feature = "unsafe_str"))]
101 unsafe { char::from_u32_unchecked(self.save_pixel as u32) }
103 }
104
105 pub fn put_flash(&mut self) -> SixelResult<()> {
110 if self.has_gri_arg_limit {
111 while self.save_count > 255 {
113 sf! {
114 self.puts("!255"); self.advance();
116 self.putc(self.save_pixel_char()); self.advance();
117 self.save_count -= 255;
118 }
119 }
120 }
121 if self.save_count > 3 {
122 sf! {
123 self.putc('!'); self.advance();
125 self.puti(self.save_count); self.advance();
126 self.putc(self.save_pixel_char()); self.advance();
127 }
128 } else {
129 for _ in 0..self.save_count {
130 self.putc(self.save_pixel_char());
131 self.advance();
132 }
133 }
134 self.save_pixel = 0;
135 self.save_count = 0;
136 Ok(())
137 }
138
139 pub fn put_pixel(&mut self, mut pix: u8) -> SixelResult<()> {
141 iif![pix > b'?'; pix = b'\0'];
142 pix += b'?';
143 if pix == self.save_pixel {
144 self.save_count += 1;
145 } else {
146 self.put_flash()?;
147 self.save_pixel = pix;
148 self.save_count = 1;
149 }
150 Ok(())
151 }
152
153 pub fn put_node(
155 &mut self, x: &mut i32, np: SixelNode, ncolors: i32, keycolor: i32,
160 ) -> SixelResult<()> {
161 if ncolors != 2 || keycolor == -1 {
162 if self.active_palette != np.pal {
164 sf! {
165 self.putc('#'); self.advance();
166 self.puti(np.pal); self.advance();
167 self.active_palette = np.pal;
168 }
169 }
170 }
171 while *x < np.sx {
172 iif![*x != keycolor; self.put_pixel(0)?];
173 *x += 1;
174 }
175 while *x < np.mx {
176 iif![*x != keycolor; self.put_pixel(np.map[*x as usize])?];
177 *x += 1;
178 }
179 self.put_flash()?;
180 Ok(())
181 }
182
183 pub fn encode_header(&mut self, width: i32, height: i32) -> SixelResult<()> {
185 let p = [0, 0, 0];
186 let mut pcount = 3;
187
188 let use_raster_attributes = true;
189
190 if !self.skip_dcs_envelope {
191 if self.has_8bit_control {
192 self.puts(Self::DCS_START_8BIT);
193 self.advance();
194 } else {
195 self.puts(Self::DCS_START_7BIT);
196 self.advance();
197 }
198 }
199 if p[2] == 0 {
200 pcount -= 1;
201 if p[1] == 0 {
202 pcount -= 1;
203 iif![p[0] == 0; pcount -= 1];
204 }
205 }
206 if pcount > 0 {
207 sf! {
208 self.puti(p[0]); self.advance();
209 if pcount > 1 {
210 self.putc(';'); self.advance();
211 self.puti(p[1]); self.advance();
212 if pcount > 2 {
213 self.putc(';'); self.advance();
214 self.puti(p[2]); self.advance();
215 }
216 }
217 }
218 }
219 self.putc('q');
220 self.advance();
221 if use_raster_attributes {
222 sf! {
223 self.puts("\"1;1;"); self.advance();
224 self.puti(width); self.advance();
225 self.putc(';'); self.advance();
226 self.puti(height); self.advance();
227 }
228 }
229 Ok(())
230 }
231
232 pub fn output_rgb_palette_definition(
234 &mut self,
235 palette: &[u8],
236 n: i32,
237 keycolor: i32,
238 ) -> SixelResult<()> {
239 if n != keycolor {
240 sf! {
241 self.putc('#'); self.advance();
243 self.puti(n); self.advance();
244 self.puts(";2;"); self.advance();
245 self.puti((palette[n as usize * 3] as i32 * 100 + 127) / 255); self.advance();
246 self.putc(';'); self.advance();
247 self.puti((palette[n as usize * 3 + 1] as i32 * 100 + 127) / 255); self.advance();
248 self.putc(';'); self.advance();
249 self.puti((palette[n as usize * 3 + 2] as i32 * 100 + 127) / 255); self.advance();
250 }
251 }
252 Ok(())
253 }
254
255 pub fn output_hls_palette_definition(
257 &mut self,
258 palette: &[u8],
259 n: i32,
260 keycolor: i32,
261 ) -> SixelResult<()> {
262 if n != keycolor {
263 let n = n as usize;
264 let r = palette[n * 3 + 0] as i32;
265 let g = palette[n * 3 + 1] as i32;
266 let b = palette[n * 3 + 2] as i32;
267 let max = r.max(g).max(b);
268 let min = r.min(g).min(b);
269 let l = ((max + min) * 100 + 255) / 510;
270 let mut h = 0;
271 let mut s = 0;
272
273 if max == min {
274 } else {
276 if l < 50 {
277 s = ((max - min) * 100) / (max + min);
278 } else {
279 s = ((max - min) * 100) / ((255 - max) + (255 - min));
280 }
281 if r == max {
282 h = 120 + (g - b) * 60 / (max - min);
283 } else if g == max {
284 h = 240 + (b - r) * 60 / (max - min);
285 } else if r < g
286 {
288 h = 360 + (r - g) * 60 / (max - min);
289 } else {
290 h = 0 + (r - g) * 60 / (max - min);
291 }
292 }
293 sf! {
295 self.putc('#'); self.advance();
296 self.puti(n as i32); self.advance();
297 self.puts(";1;"); self.advance();
298 self.puti(h); self.advance();
299 self.putc(';'); self.advance();
300 self.puti(l); self.advance();
301 self.putc(';'); self.advance();
302 self.puti(s); self.advance();
303 }
304 }
305 Ok(())
306 }
307
308 #[expect(clippy::too_many_arguments)]
310 pub fn encode_body(
311 &mut self,
312 pixels: &[u8],
313 width: i32,
314 height: i32,
315 palette: &[u8],
316 ncolors: usize,
317 keycolor: i32,
318 bodyonly: bool,
319 palstate: Option<&[i32]>,
320 ) -> SixelResult<()> {
321 if palette.is_empty() {
322 return Err(SixelError::BadArgument);
323 }
324 let len = ncolors * width as usize;
325 self.active_palette = -1;
326
327 let mut map: Vec<u8> = vec![0; len];
328
329 if !bodyonly && (ncolors != 2 || keycolor == (-1)) {
330 if matches!(self.color_model, SixelColorModel::Hls) {
331 for n in 0..ncolors {
332 self.output_hls_palette_definition(palette, n as i32, keycolor)?;
333 }
334 } else {
335 for n in 0..ncolors {
336 self.output_rgb_palette_definition(palette, n as i32, keycolor)?;
337 }
338 }
339 }
340 let mut i = 0;
341 let mut fillable: bool;
342 let mut pix;
343
344 for y in 0..height {
345 if self.encode_policy != SixelEncodePolicy::Size {
346 fillable = false;
347 } else if palstate.is_some() {
348 pix = pixels[((y - i) * width) as usize] as i32;
350 fillable = pix as usize >= ncolors;
351 } else {
352 fillable = true;
354 }
355 for x in 0..width {
356 if y > i32::MAX / width {
357 return Err(SixelError::BadIntegerOverflow);
362 }
363 let mut check_integer_overflow = y * width;
364 if check_integer_overflow > i32::MAX - x {
365 return Err(SixelError::BadIntegerOverflow);
370 }
371 pix = pixels[(check_integer_overflow + x) as usize] as i32; if pix >= 0 && (pix as usize) < ncolors && pix != keycolor {
373 if pix > i32::MAX / width {
374 return Err(SixelError::BadIntegerOverflow);
379 }
380 check_integer_overflow = pix * width;
381 if check_integer_overflow > i32::MAX - x {
382 return Err(SixelError::BadIntegerOverflow);
387 }
388 map[(pix * width + x) as usize] |= 1 << i;
389 } else if palstate.is_none() {
390 fillable = false;
391 }
392 }
393
394 i += 1;
395 if i < 6 && (y + 1) < height {
396 continue;
397 }
398 for c in 0..ncolors {
399 let mut sx = 0;
400 while sx < width {
401 if map[c * width as usize + sx as usize] == 0 {
402 sx += 1;
403 continue;
404 }
405 let mut mx = sx + 1;
406 while mx < width {
407 if map[c * width as usize + mx as usize] != 0 {
408 mx += 1;
409 continue;
410 }
411 let mut n = 1;
412 while (mx + n) < width {
413 if map[c * width as usize + mx as usize + n as usize] != 0 {
414 break;
415 }
416 n += 1;
417 }
418
419 if n >= 10 || (mx + n) >= width {
420 break;
421 }
422 mx = mx + n - 1;
423 mx += 1;
424 }
425 let np = SixelNode::new(c as i32, sx, mx, map[c * width as usize..].to_vec());
426 self.nodes.insert(0, np);
427 sx = mx - 1;
428 sx += 1;
429 }
430 }
431
432 if y != 5 {
433 self.putc('-');
435 self.advance();
436 }
437 let mut x = 0;
438 while let Some(mut np) = self.nodes.pop() {
439 if x > np.sx {
440 self.putc('$');
442 self.advance();
443 x = 0;
444 }
445
446 if fillable {
447 let v = (1 << i) - 1;
449 np.map.resize(np.mx as usize, v);
450 for j in np.sx..np.mx {
451 np.map[j as usize] = v;
452 }
453 }
454 self.put_node(&mut x, np, ncolors as i32, keycolor)?;
455
456 let mut ni = self.nodes.len() as i32 - 1;
457 while ni >= 0 {
458 let onode = &self.nodes[ni as usize];
459
460 if onode.sx < x {
461 ni -= 1;
462 continue;
463 }
464
465 if fillable {
466 let np = &mut self.nodes[ni as usize];
468 let v = (1 << i) - 1;
469 np.map.resize(np.mx as usize, v);
470 for j in np.sx..np.mx {
471 np.map[j as usize] = v;
472 }
473 }
474 let np = self.nodes.remove(ni as usize);
475 self.put_node(&mut x, np, ncolors as i32, keycolor)?;
476 ni -= 1;
477 }
478
479 fillable = false;
480 }
481
482 i = 0;
483 map.clear();
484 map.resize(len, 0);
485 }
486
487 if palstate.is_some() {
488 self.putc('$');
489 self.advance();
490 }
491 Ok(())
492 }
493
494 pub fn encode_footer(&mut self) -> SixelResult<()> {
496 if !self.skip_dcs_envelope && !self.penetrate_multiplexer {
497 if self.has_8bit_control {
498 self.puts(Self::DCS_END_8BIT);
499 self.advance();
500 } else {
501 self.puts(Self::DCS_END_7BIT);
502 self.advance();
503 }
504 }
505
506 if !self.buffer.is_empty() {
508 if self.penetrate_multiplexer {
509 self.penetrate(self.buffer.len(), Self::DCS_START_7BIT, Self::DCS_END_7BIT);
510 let _ = self.fn_write.write(b"\x1B\\");
511 } else {
512 let _ = self.fn_write.write(self.buffer.as_bytes());
513 }
514 }
515 Ok(())
516 }
517
518 pub fn encode_dither(
520 &mut self,
521 pixels: &[u8],
522 width: i32,
523 height: i32,
524 dither: &mut DitherConf,
525 ) -> SixelResult<()> {
526 use PixelFormat as P;
527 let input_pixels = match dither.pixel_format {
528 P::PAL1 | P::PAL2 | P::PAL4 | P::G1 | P::G2 | P::G4 => {
529 let mut paletted_pixels = vec![0; (width * height * 3) as usize];
530 dither.pixel_format =
531 dither.pixel_format.normalize(&mut paletted_pixels, pixels, width, height)?;
532 paletted_pixels
533 }
534 P::PAL8 | P::G8 | P::GA88 | P::AG88 => pixels.to_vec(),
535 _ => dither.apply_palette(pixels, width, height)?,
536 };
537 self.encode_header(width, height)?;
538 self.encode_body(
539 &input_pixels,
540 width,
541 height,
542 &dither.palette,
543 dither.ncolors as usize,
544 dither.keycolor,
545 dither.bodyonly,
546 None,
547 )?;
548 self.encode_footer()?;
549 Ok(())
550 }
551
552 pub fn encode_highcolor(
554 &mut self,
555 pixels: &mut [u8],
556 width: i32,
557 mut height: i32,
558 dither: &mut DitherConf,
559 ) -> SixelResult<()> {
560 let maxcolors = 1 << 15;
561 let mut px_idx = 0;
562 let mut normalized_pixels = vec![0; (width * height * 3) as usize];
563
564 let pixels = if !matches!(dither.pixel_format, PixelFormat::BGR888) {
565 dither.pixel_format.normalize(&mut normalized_pixels, pixels, width, height)?;
566 &mut normalized_pixels
567 } else {
568 pixels
569 };
570 let mut paletted_pixels: Vec<u8> = vec![0; (width * height) as usize];
571 let mut rgbhit = vec![0; maxcolors as usize];
572 let mut rgb2pal = vec![0; maxcolors as usize];
573 let mut output_count = 0;
575
576 let mut is_running = true;
577 let mut palstate: Vec<i32> = vec![0; SIXEL_PALETTE_MAX];
578 let mut palhitcount: Vec<i32> = vec![0; SIXEL_PALETTE_MAX];
579 let mut marks = vec![false; (width * 6) as usize];
580
581 while is_running {
582 let (mut dst, mut mptr, mut nextpal) = (0, 0, 0);
583 let (mut threshold, mut dirty) = (1, false);
584 let (mut y, mut mod_y) = (0, 0);
585
586 marks.clear();
587 marks.resize((width * 6) as usize, false);
588 palstate.clear();
589 palstate.resize(SIXEL_PALETTE_MAX, 0);
590
591 loop {
592 for x in 0..width {
593 if marks[mptr] {
594 paletted_pixels[dst] = 255;
595 } else {
596 dither.dither.apply_15bpp(&mut pixels[px_idx..], x, y, width, height);
597 let pix = ((pixels[px_idx] & 0xf8) as i32) << 7
598 | ((pixels[px_idx + 1] & 0xf8) as i32) << 2
599 | ((pixels[px_idx + 2] >> 3) & 0x1f) as i32;
600
601 if rgbhit[pix as usize] == 0 {
602 loop {
603 if nextpal >= 255 {
604 if threshold >= 255 {
605 break;
606 } else {
607 threshold = if threshold == 1 { 9 } else { 255 };
608 nextpal = 0;
609 }
610 } else if palstate[nextpal] != 0 || palhitcount[nextpal] > threshold
611 {
612 nextpal += 1;
613 } else {
614 break;
615 }
616 }
617 if nextpal >= 255 {
618 dirty = true;
619 paletted_pixels[dst] = 255;
620 } else {
621 let pal = nextpal * 3;
622 rgbhit[pix as usize] = 1;
623 if output_count > 0 {
624 rgbhit[((dither.palette[pal] as usize & 0xf8) << 7)
625 | ((dither.palette[pal + 1] as usize & 0xf8) << 2)
626 | ((dither.palette[pal + 2] as usize >> 3) & 0x1f)] = 0;
627 }
628 paletted_pixels[dst] = nextpal as u8;
629 rgb2pal[pix as usize] = nextpal as u8;
630 nextpal += 1;
631 marks[mptr] = true;
632 palstate[paletted_pixels[dst] as usize] = Self::PALETTE_CHANGE;
633 palhitcount[paletted_pixels[dst] as usize] = 1;
634 dither.palette[pal] = pixels[px_idx + 0];
635 dither.palette[pal + 1] = pixels[px_idx + 1];
636 dither.palette[pal + 2] = pixels[px_idx + 2];
637 }
638 } else {
639 let pp = rgb2pal[pix as usize];
640 paletted_pixels[dst] = pp;
641 let pp = pp as usize;
642 marks[mptr] = true;
643 iif![palstate[pp] != 0; palstate[pp] = Self::PALETTE_HIT];
644 iif![palhitcount[pp] < 255; palhitcount[pp] += 1];
645 }
646 }
647 mptr += 1;
648 dst += 1;
649 px_idx += 3;
650 }
651 y += 1;
652 if y >= height {
653 iif![dirty; mod_y = 5; { is_running = false; break; }];
654 }
655 if dirty && (mod_y == 5 || y >= height) {
656 let orig_height = height;
657 iif![output_count == 0; self.encode_header(width, height)?];
658 output_count += 1;
659 height = y;
660 self.encode_body(
661 &paletted_pixels,
662 width,
663 height,
664 &dither.palette,
665 dither.ncolors as usize,
666 255,
667 dither.bodyonly,
668 Some(&palstate),
669 )?;
670 if y >= orig_height {
671 is_running = false;
673 break;
674 }
675 px_idx -= (6 * width * 3) as usize;
676 height = orig_height - height + 6;
677 break; }
679 mod_y += 1;
680 if mod_y == 6 {
681 marks.clear();
682 marks.resize(maxcolors as usize, false);
683 mptr = 0;
684 mod_y = 0;
685 }
686 }
687 }
688 iif![output_count == 0; self.encode_header(width, height)?];
689 let _ = self.encode_body(
690 &paletted_pixels,
691 width,
692 height,
693 &dither.palette,
694 dither.ncolors as usize,
695 255,
696 dither.bodyonly,
697 Some(&palstate),
698 );
699 let _ = self.encode_footer();
700 Ok(())
701 }
702
703 pub fn encode(
705 &mut self,
706 pixels: &mut [u8],
707 width: i32,
708 height: i32,
709 _depth: i32, dither: &mut DitherConf,
711 ) -> SixelResult<()> {
712 iif![width < 1; return Err(SixelError::BadInput)];
713 iif![height < 1; return Err(SixelError::BadInput)];
714 match dither.quality_mode {
715 SixelQuality::Auto | SixelQuality::High | SixelQuality::Low | SixelQuality::Full => {
716 self.encode_dither(pixels, width, height, dither)?;
717 }
718 SixelQuality::HighColor => {
719 self.encode_highcolor(pixels, width, height, dither)?;
720 }
721 }
722 Ok(())
723 }
724}