devela/media/image/sixel/output/
tosixel.rs

1// devela::media::image::sixel::output::tosixel
2
3#![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    /* GNU Screen penetration */
15
16    /// Writes a segmented data packet to the output,
17    /// wrapped with DCS (Device Control String) start and end sequences.
18    ///
19    /// Segments data according to `SCREEN_PACKET_SIZE`, splitting if necessary.
20    fn penetrate(
21        &mut self,
22        nwrite: usize,   // output size
23        dcs_start: &str, // DCS introducer
24        dcs_end: &str,   // DCS terminato
25    ) {
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    /// Manages buffer overflow by writing buffered data in packets of `PACKET_SIZE`.
37    ///
38    /// Uses `penetrate` if multiplexing is enabled; otherwise, writes directly to output.
39    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    /// Writes a single character to the output.
56    pub fn putc(&mut self, value: char) {
57        self.buffer.push(value);
58    }
59
60    /// Writes a string to the output.
61    pub fn puts(&mut self, value: &str) {
62        self.buffer.push_str(value);
63    }
64
65    /// Writes an integer value to the output as a string.
66    ///
67    /// # Features
68    /// Uses the `fmt` feature to use [`NumToStr`][crate::NumToStr]
69    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    /// Writes a byte value to the output as a string.
80    ///
81    /// # Features
82    /// Uses the `fmt` feature to use [`NumToStr`][crate::NumToStr]
83    #[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    /// Returns the saved pixel as a character.
95    #[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        // SAFETY: TODO
102        unsafe { char::from_u32_unchecked(self.save_pixel as u32) }
103    }
104
105    /// Adds a "flash" signal in the output stream.
106    ///
107    /// # Features
108    /// This method makes use of the `unsafe_str` feature for converting a `u32` pixel to `char`.
109    pub fn put_flash(&mut self) -> SixelResult<()> {
110        if self.has_gri_arg_limit {
111            /* VT240 Max 255 ? */
112            while self.save_count > 255 {
113                sf! {
114                    /* argument of DECGRI('!') is limitted to 255 in real VT */
115                    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                /* DECGRI Graphics Repeat Introducer ! Pn Ch */
124                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    /// Outputs a single pixel to the sixel stream.
140    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    /// Writes a sixel node to the output, with additional parameters for color and position.
154    pub fn put_node(
155        &mut self,     /* output context */
156        x: &mut i32,   /* header position */
157        np: SixelNode, /* node object */
158        ncolors: i32,  /* number of palette colors */
159        keycolor: i32,
160    ) -> SixelResult<()> {
161        if ncolors != 2 || keycolor == -1 {
162            /* designate palette index */
163            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    /// Encodes and outputs the sixel image header with the specified width and height.
184    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    /// Outputs an RGB color palette definition.
233    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                /* DECGCI Graphics Color Introducer  # Pc ; Pu; Px; Py; Pz */
242                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    /// Outputs an HLS color palette definition.
256    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                // h = s = 0;
275            } 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                /* if b == max */
287                {
288                    h = 360 + (r - g) * 60 / (max - min);
289                } else {
290                    h = 0 + (r - g) * 60 / (max - min);
291                }
292            }
293            /* DECGCI Graphics Color Introducer  # Pc ; Pu; Px; Py; Pz */
294            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    /// Encodes the sixel image body, including pixel and color data.
309    #[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                /* high color sixel */
349                pix = pixels[((y - i) * width) as usize] as i32;
350                fillable = pix as usize >= ncolors;
351            } else {
352                /* normal sixel */
353                fillable = true;
354            }
355            for x in 0..width {
356                if y > i32::MAX / width {
357                    /* integer overflow */
358                    /*sixel_helper_set_additional_message(
359                    "sixel_encode_body: integer overflow detected."
360                    " (y > INT_MAX)");*/
361                    return Err(SixelError::BadIntegerOverflow);
362                }
363                let mut check_integer_overflow = y * width;
364                if check_integer_overflow > i32::MAX - x {
365                    /* integer overflow */
366                    /*sixel_helper_set_additional_message(
367                    "sixel_encode_body: integer overflow detected."
368                    " (y * width > INT_MAX - x)");*/
369                    return Err(SixelError::BadIntegerOverflow);
370                }
371                pix = pixels[(check_integer_overflow + x) as usize] as i32; /* color index */
372                if pix >= 0 && (pix as usize) < ncolors && pix != keycolor {
373                    if pix > i32::MAX / width {
374                        /* integer overflow */
375                        /*sixel_helper_set_additional_message(
376                        "sixel_encode_body: integer overflow detected."
377                        " (pix > INT_MAX / width)");*/
378                        return Err(SixelError::BadIntegerOverflow);
379                    }
380                    check_integer_overflow = pix * width;
381                    if check_integer_overflow > i32::MAX - x {
382                        /* integer overflow */
383                        /*sixel_helper_set_additional_message(
384                        "sixel_encode_body: integer overflow detected."
385                        " (pix * width > INT_MAX - x)");*/
386                        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                /* DECGNL Graphics Next Line */
434                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                    /* DECGCR Graphics Carriage Return */
441                    self.putc('$');
442                    self.advance();
443                    x = 0;
444                }
445
446                if fillable {
447                    // memset(np->map + np->sx, (1 << i) - 1, (size_t)(np->mx - np->sx));
448                    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                        // memset(np.map + np.sx, (1 << i) - 1, (size_t)(np.mx - np.sx));
467                        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    /// Encodes and outputs the sixel image footer.
495    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        /* flush buffer */
507        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    /// Encodes a sixel dithered image with specified pixels and configuration.
519    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    /// Encodes a high-color sixel image.
553    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 marks = &mut rgb2pal[maxcolors as usize..];
574        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                        // end outer loop
672                        is_running = false;
673                        break;
674                    }
675                    px_idx -= (6 * width * 3) as usize;
676                    height = orig_height - height + 6;
677                    break; // goto next outer loop
678                }
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    /// Encodes a sixel image with dither and color depth settings.
704    pub fn encode(
705        &mut self,
706        pixels: &mut [u8],
707        width: i32,
708        height: i32,
709        _depth: i32, /* color depth */
710        dither: &mut DitherConf,
711    ) -> SixelResult<()> /* output context */ {
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}