ratatui_wgpu/backend/
wgpu_backend.rs

1use std::{
2    collections::{
3        HashMap,
4        HashSet,
5    },
6    marker::PhantomData,
7    mem::size_of,
8    num::NonZeroU64,
9};
10
11use bitvec::{
12    order::Lsb0,
13    slice::BitSlice,
14    vec::BitVec,
15};
16use indexmap::IndexMap;
17use raqote::{
18    DrawOptions,
19    DrawTarget,
20    SolidSource,
21    StrokeStyle,
22    Transform,
23};
24use ratatui::{
25    backend::{
26        Backend,
27        ClearType,
28        WindowSize,
29    },
30    buffer::Cell,
31    layout::{
32        Position,
33        Size,
34    },
35    style::Modifier,
36};
37use rustybuzz::{
38    shape_with_plan,
39    ttf_parser::{
40        GlyphId,
41        RasterGlyphImage,
42        RasterImageFormat,
43        RgbaColor,
44    },
45    GlyphBuffer,
46    UnicodeBuffer,
47};
48use unicode_bidi::{
49    Level,
50    ParagraphBidiInfo,
51};
52use unicode_properties::{
53    GeneralCategoryGroup,
54    UnicodeEmoji,
55    UnicodeGeneralCategory,
56};
57use unicode_width::{
58    UnicodeWidthChar,
59    UnicodeWidthStr,
60};
61use web_time::{
62    Duration,
63    Instant,
64};
65use wgpu::{
66    util::{
67        BufferInitDescriptor,
68        DeviceExt,
69    },
70    Buffer,
71    BufferUsages,
72    CommandEncoderDescriptor,
73    Device,
74    Extent3d,
75    IndexFormat,
76    LoadOp,
77    Operations,
78    Origin3d,
79    Queue,
80    RenderPassColorAttachment,
81    RenderPassDescriptor,
82    StoreOp,
83    Surface,
84    SurfaceConfiguration,
85    Texture,
86    TextureAspect,
87};
88
89use crate::{
90    backend::{
91        build_wgpu_state,
92        c2c,
93        private::Token,
94        PostProcessor,
95        RenderSurface,
96        RenderTexture,
97        TextBgVertexMember,
98        TextCacheBgPipeline,
99        TextCacheFgPipeline,
100        TextVertexMember,
101        Viewport,
102        WgpuState,
103    },
104    colors::Rgb,
105    fonts::{
106        Font,
107        Fonts,
108    },
109    shaders::DefaultPostProcessor,
110    utils::{
111        plan_cache::PlanCache,
112        text_atlas::{
113            Atlas,
114            CacheRect,
115            Entry,
116            Key,
117        },
118        Outline,
119        Painter,
120    },
121    RandomState,
122};
123
124const NULL_CELL: Cell = Cell::new("");
125
126pub(super) struct RenderInfo {
127    cell: usize,
128    cached: CacheRect,
129    underline_pos_min: u16,
130    underline_pos_max: u16,
131}
132/// Map from (x, y, glyph) -> (cell index, cache entry).
133/// We use an IndexMap because we want a consistent rendering order for
134/// vertices.
135type Rendered = IndexMap<(i32, i32, GlyphId), RenderInfo, RandomState>;
136
137/// Set of (x, y, glyph, char width).
138type Sourced = HashSet<(i32, i32, GlyphId, u32), RandomState>;
139
140/// A ratatui backend leveraging wgpu for rendering.
141///
142/// Constructed using a [`Builder`](crate::Builder).
143///
144/// The first lifetime parameter is the lifetime of the data for referenced
145/// [`Font`] objects. The second lifetime parameter is the lifetime of the
146/// referenced [`Surface`] (typically the lifetime of your window object).
147///
148/// Limitations:
149/// - The cursor is tracked but not rendered.
150/// - No builtin accessibilty, although [`WgpuBackend::get_text`] is provided to
151///   access the screen's contents.
152pub struct WgpuBackend<
153    'f,
154    's,
155    P: PostProcessor = DefaultPostProcessor,
156    S: RenderSurface<'s> = Surface<'s>,
157> {
158    pub(super) post_process: P,
159
160    pub(super) cells: Vec<Cell>,
161    pub(super) dirty_rows: Vec<bool>,
162    pub(super) dirty_cells: BitVec,
163    pub(super) rendered: Vec<Rendered>,
164    pub(super) sourced: Vec<Sourced>,
165    pub(super) fast_blinking: BitVec,
166    pub(super) slow_blinking: BitVec,
167
168    pub(super) cursor: (u16, u16),
169
170    pub(super) viewport: Viewport,
171
172    pub(super) surface: S,
173    pub(super) _surface: PhantomData<&'s S>,
174    pub(super) surface_config: SurfaceConfiguration,
175    pub(super) device: Device,
176    pub(super) queue: Queue,
177
178    pub(super) plan_cache: PlanCache,
179    pub(super) buffer: UnicodeBuffer,
180    pub(super) row: String,
181    pub(super) rowmap: Vec<u16>,
182
183    pub(super) cached: Atlas,
184    pub(super) text_cache: Texture,
185    pub(super) text_mask: Texture,
186    pub(super) bg_vertices: Vec<TextBgVertexMember>,
187    pub(super) text_indices: Vec<[u32; 6]>,
188    pub(super) text_vertices: Vec<TextVertexMember>,
189    pub(super) text_bg_compositor: TextCacheBgPipeline,
190    pub(super) text_fg_compositor: TextCacheFgPipeline,
191    pub(super) text_screen_size_buffer: Buffer,
192
193    pub(super) wgpu_state: WgpuState,
194
195    pub(super) fonts: Fonts<'f>,
196    pub(super) reset_fg: Rgb,
197    pub(super) reset_bg: Rgb,
198
199    pub(super) fast_duration: Duration,
200    pub(super) last_fast_toggle: Instant,
201    pub(super) show_fast: bool,
202    pub(super) slow_duration: Duration,
203    pub(super) last_slow_toggle: Instant,
204    pub(super) show_slow: bool,
205}
206
207impl<'f, 's, P: PostProcessor, S: RenderSurface<'s>> WgpuBackend<'f, 's, P, S> {
208    /// Get the [`PostProcessor`] associated with this backend.
209    pub fn post_processor(&self) -> &P {
210        &self.post_process
211    }
212
213    /// Get a mutable reference to the [`PostProcessor`] associated with this
214    /// backend.
215    pub fn post_processor_mut(&mut self) -> &mut P {
216        &mut self.post_process
217    }
218
219    /// Resize the rendering surface. This should be called e.g. to keep the
220    /// backend in sync with your window size.
221    pub fn resize(&mut self, width: u32, height: u32) {
222        let limits = self.device.limits();
223        let width = width.min(limits.max_texture_dimension_2d);
224        let height = height.min(limits.max_texture_dimension_2d);
225
226        if width == self.surface_config.width && height == self.surface_config.height
227            || width == 0
228            || height == 0
229        {
230            return;
231        }
232
233        let (inset_width, inset_height) = match self.viewport {
234            Viewport::Full => (0, 0),
235            Viewport::Shrink { width, height } => (width, height),
236        };
237
238        let dims = self.size().unwrap();
239        let current_width = dims.width;
240        let current_height = dims.height;
241
242        self.surface_config.width = width;
243        self.surface_config.height = height;
244        self.surface
245            .configure(&self.device, &self.surface_config, Token);
246
247        let width = width - inset_width;
248        let height = height - inset_height;
249
250        let chars_wide = width / self.fonts.min_width_px();
251        let chars_high = height / self.fonts.height_px();
252
253        if chars_wide != current_width as u32 || chars_high != current_height as u32 {
254            self.cells.clear();
255            self.rendered.clear();
256            self.sourced.clear();
257            self.fast_blinking.clear();
258            self.slow_blinking.clear();
259        }
260
261        // This always needs to be cleared because the surface is cleared when it is
262        // resized. If we don't re-render the rows, we end up with a blank surface when
263        // the resize is less than a character dimension.
264        self.dirty_rows.clear();
265
266        self.wgpu_state = build_wgpu_state(
267            &self.device,
268            chars_wide * self.fonts.min_width_px(),
269            chars_high * self.fonts.height_px(),
270        );
271
272        self.post_process.resize(
273            &self.device,
274            &self.wgpu_state.text_dest_view,
275            &self.surface_config,
276        );
277
278        info!(
279            "Resized from {}x{} to {}x{}",
280            current_width, current_height, chars_wide, chars_high,
281        );
282    }
283
284    /// Get the text currently displayed on the screen.
285    pub fn get_text(&self) -> String {
286        let bounds = self.size().unwrap();
287        self.cells.chunks(bounds.width as usize).fold(
288            String::with_capacity((bounds.width + 1) as usize * bounds.height as usize),
289            |dest, row| {
290                let mut dest = row.iter().fold(dest, |mut dest, s| {
291                    dest.push_str(s.symbol());
292                    dest
293                });
294                dest.push('\n');
295                dest
296            },
297        )
298    }
299
300    /// Update the fonts used for rendering. This will cause a full repaint of
301    /// the screen the next time [`WgpuBackend::flush`] is called.
302    pub fn update_fonts(&mut self, new_fonts: Fonts<'f>) {
303        self.dirty_rows.clear();
304        self.cached.match_fonts(&new_fonts);
305        self.fonts = new_fonts;
306    }
307
308    fn render(&mut self) {
309        let bounds = self.window_size().unwrap();
310
311        let mut encoder = self
312            .device
313            .create_command_encoder(&CommandEncoderDescriptor {
314                label: Some("Draw Encoder"),
315            });
316
317        if !self.text_vertices.is_empty() {
318            {
319                let mut uniforms = self
320                    .queue
321                    .write_buffer_with(
322                        &self.text_screen_size_buffer,
323                        0,
324                        NonZeroU64::new(size_of::<[f32; 4]>() as u64).unwrap(),
325                    )
326                    .unwrap();
327                uniforms.copy_from_slice(bytemuck::cast_slice(&[
328                    bounds.columns_rows.width as f32 * self.fonts.min_width_px() as f32,
329                    bounds.columns_rows.height as f32 * self.fonts.height_px() as f32,
330                    0.0,
331                    0.0,
332                ]));
333            }
334
335            let bg_vertices = self.device.create_buffer_init(&BufferInitDescriptor {
336                label: Some("Text Bg Vertices"),
337                contents: bytemuck::cast_slice(&self.bg_vertices),
338                usage: BufferUsages::VERTEX,
339            });
340
341            let fg_vertices = self.device.create_buffer_init(&BufferInitDescriptor {
342                label: Some("Text Vertices"),
343                contents: bytemuck::cast_slice(&self.text_vertices),
344                usage: BufferUsages::VERTEX,
345            });
346
347            let indices = self.device.create_buffer_init(&BufferInitDescriptor {
348                label: Some("Text Indices"),
349                contents: bytemuck::cast_slice(&self.text_indices),
350                usage: BufferUsages::INDEX,
351            });
352
353            {
354                let mut text_render_pass = encoder.begin_render_pass(&RenderPassDescriptor {
355                    label: Some("Text Render Pass"),
356                    color_attachments: &[Some(RenderPassColorAttachment {
357                        view: &self.wgpu_state.text_dest_view,
358                        resolve_target: None,
359                        ops: Operations {
360                            load: LoadOp::Load,
361                            store: StoreOp::Store,
362                        },
363                        depth_slice: None,
364                    })],
365                    ..Default::default()
366                });
367
368                text_render_pass.set_index_buffer(indices.slice(..), IndexFormat::Uint32);
369
370                text_render_pass.set_pipeline(&self.text_bg_compositor.pipeline);
371                text_render_pass.set_bind_group(0, &self.text_bg_compositor.fs_uniforms, &[]);
372                text_render_pass.set_vertex_buffer(0, bg_vertices.slice(..));
373                text_render_pass.draw_indexed(0..(self.bg_vertices.len() as u32 / 4) * 6, 0, 0..1);
374
375                text_render_pass.set_pipeline(&self.text_fg_compositor.pipeline);
376                text_render_pass.set_bind_group(0, &self.text_fg_compositor.fs_uniforms, &[]);
377                text_render_pass.set_bind_group(1, &self.text_fg_compositor.atlas_bindings, &[]);
378
379                text_render_pass.set_vertex_buffer(0, fg_vertices.slice(..));
380                text_render_pass.draw_indexed(
381                    0..(self.text_vertices.len() as u32 / 4) * 6,
382                    0,
383                    0..1,
384                );
385            }
386        }
387
388        let Some(texture) = self.surface.get_current_texture(Token) else {
389            return;
390        };
391
392        self.post_process.process(
393            &mut encoder,
394            &self.queue,
395            &self.wgpu_state.text_dest_view,
396            &self.surface_config,
397            texture.get_view(Token),
398        );
399
400        self.queue.submit(Some(encoder.finish()));
401        texture.present(Token);
402    }
403}
404
405impl<'s, P: PostProcessor, S: RenderSurface<'s>> Backend for WgpuBackend<'_, 's, P, S> {
406    fn draw<'a, I>(&mut self, content: I) -> std::io::Result<()>
407    where
408        I: Iterator<Item = (u16, u16, &'a Cell)>,
409    {
410        let bounds = self.size()?;
411
412        self.cells
413            .resize(bounds.height as usize * bounds.width as usize, Cell::EMPTY);
414        self.sourced.resize_with(
415            bounds.height as usize * bounds.width as usize,
416            Sourced::default,
417        );
418        self.rendered.resize_with(
419            bounds.height as usize * bounds.width as usize,
420            Rendered::default,
421        );
422        self.fast_blinking
423            .resize(bounds.height as usize * bounds.width as usize, false);
424        self.slow_blinking
425            .resize(bounds.height as usize * bounds.width as usize, false);
426        self.dirty_rows.resize(bounds.height as usize, true);
427
428        for (x, y, cell) in content {
429            let index = y as usize * bounds.width as usize + x as usize;
430
431            self.fast_blinking
432                .set(index, cell.modifier.contains(Modifier::RAPID_BLINK));
433            self.slow_blinking
434                .set(index, cell.modifier.contains(Modifier::SLOW_BLINK));
435
436            self.cells[index] = cell.clone();
437
438            let width = cell.symbol().width().max(1);
439            let start = (index + 1).min(self.cells.len());
440            let end = (index + width).min(self.cells.len());
441            self.cells[start..end].fill(NULL_CELL);
442            self.dirty_rows[y as usize] = true;
443        }
444
445        Ok(())
446    }
447
448    fn hide_cursor(&mut self) -> std::io::Result<()> {
449        Ok(())
450    }
451
452    fn show_cursor(&mut self) -> std::io::Result<()> {
453        Ok(())
454    }
455
456    fn get_cursor_position(&mut self) -> std::io::Result<Position> {
457        Ok(Position::new(self.cursor.0, self.cursor.1))
458    }
459
460    fn set_cursor_position<Pos: Into<Position>>(&mut self, position: Pos) -> std::io::Result<()> {
461        let bounds = self.size()?;
462        let pos: Position = position.into();
463        self.cursor = (pos.x.min(bounds.width - 1), pos.y.min(bounds.height - 1));
464        Ok(())
465    }
466
467    fn clear(&mut self) -> std::io::Result<()> {
468        self.cells.clear();
469        self.dirty_rows.clear();
470        self.cursor = (0, 0);
471
472        Ok(())
473    }
474
475    fn size(&self) -> std::io::Result<Size> {
476        let (inset_width, inset_height) = match self.viewport {
477            Viewport::Full => (0, 0),
478            Viewport::Shrink { width, height } => (width, height),
479        };
480        let width = self.surface_config.width - inset_width;
481        let height = self.surface_config.height - inset_height;
482
483        Ok(Size {
484            width: (width / self.fonts.min_width_px()) as u16,
485            height: (height / self.fonts.height_px()) as u16,
486        })
487    }
488
489    fn window_size(&mut self) -> std::io::Result<WindowSize> {
490        let (inset_width, inset_height) = match self.viewport {
491            Viewport::Full => (0, 0),
492            Viewport::Shrink { width, height } => (width, height),
493        };
494        let width = self.surface_config.width - inset_width;
495        let height = self.surface_config.height - inset_height;
496
497        Ok(WindowSize {
498            columns_rows: Size {
499                width: (width / self.fonts.min_width_px()) as u16,
500                height: (height / self.fonts.height_px()) as u16,
501            },
502            pixels: Size {
503                width: width as u16,
504                height: height as u16,
505            },
506        })
507    }
508
509    fn flush(&mut self) -> std::io::Result<()> {
510        let bounds = self.size()?;
511        self.dirty_cells.clear();
512        self.dirty_cells.resize(self.cells.len(), false);
513
514        let fast_toggle_dirty = self.last_fast_toggle.elapsed() >= self.fast_duration;
515        if fast_toggle_dirty {
516            self.last_fast_toggle = Instant::now();
517            self.show_fast = !self.show_fast;
518
519            for index in self.fast_blinking.iter_ones() {
520                self.dirty_cells.set(index, true);
521            }
522        }
523
524        let slow_toggle_dirty = self.last_slow_toggle.elapsed() >= self.slow_duration;
525        if slow_toggle_dirty {
526            self.last_slow_toggle = Instant::now();
527            self.show_slow = !self.show_slow;
528
529            for index in self.slow_blinking.iter_ones() {
530                self.dirty_cells.set(index, true);
531            }
532        }
533
534        let mut pending_cache_updates = HashMap::<_, _, RandomState>::default();
535
536        for (y, (row, sourced)) in self
537            .cells
538            .chunks(bounds.width as usize)
539            .zip(self.sourced.chunks_mut(bounds.width as usize))
540            .enumerate()
541        {
542            if !self.dirty_rows[y] {
543                continue;
544            }
545
546            self.dirty_rows[y] = false;
547            let mut new_sourced = vec![Sourced::default(); bounds.width as usize];
548
549            // This block concatenates the strings for the row into one string for bidi
550            // resolution, then maps bytes for the string to their associated cell index. It
551            // also maps the row's cell index to the font that can source all glyphs for
552            // that cell.
553            self.row.clear();
554            self.rowmap.clear();
555            let mut fontmap = Vec::with_capacity(self.rowmap.capacity());
556            for (idx, cell) in row.iter().enumerate() {
557                self.row.push_str(cell.symbol());
558                self.rowmap
559                    .resize(self.rowmap.len() + cell.symbol().len(), idx as u16);
560                fontmap.push(self.fonts.font_for_cell(cell));
561            }
562
563            let mut x = 0;
564            // rustbuzz provides a non-zero x-advance for the first character in a cluster
565            // with combining characters. The remainder of the cluster doesn't account for
566            // this advance, so if we advance prior to rendering them, we end up with all of
567            // the associated characters being offset by a cell. To combat this, we only
568            // bump the x-advance after we've finished processing all of the characters in a
569            // cell. This assumes that we 1) always get a non-zero advance at the beginning
570            // of a cluster and 2) the next cluster in the sequence starts with a non-zero
571            // advance.
572            let mut next_advance = 0;
573            let mut shape = |font: &Font,
574                             fake_bold,
575                             fake_italic,
576                             buffer: GlyphBuffer|
577             -> UnicodeBuffer {
578                let metrics = font.font();
579                let advance_scale = self.fonts.height_px() as f32 / metrics.height() as f32;
580
581                for (info, position) in buffer
582                    .glyph_infos()
583                    .iter()
584                    .zip(buffer.glyph_positions().iter())
585                {
586                    let cell_idx = self.rowmap[info.cluster as usize] as usize;
587                    let cell = &row[cell_idx];
588                    let max_width = cell.symbol().width();
589                    let sourced = &mut new_sourced[cell_idx];
590
591                    let basey = y as i32 * self.fonts.height_px() as i32
592                        + (position.y_offset as f32 * advance_scale) as i32;
593                    let mut advance = (position.x_advance as f32 * advance_scale) as i32;
594                    if advance != 0 {
595                        x += next_advance;
596                        advance =
597                            max_width as i32 * advance.signum() * self.fonts.min_width_px() as i32;
598                        next_advance = advance;
599                    }
600                    let basex = x + (position.x_offset as f32 * advance_scale) as i32;
601
602                    // This assumes that we only want to underline the first character in the
603                    // cluster, and that the remaining characters are all combining characters
604                    // which don't need an underline.
605                    let set = if advance != 0 {
606                        Modifier::BOLD | Modifier::ITALIC | Modifier::UNDERLINED
607                    } else {
608                        Modifier::BOLD | Modifier::ITALIC
609                    };
610
611                    let key = Key {
612                        style: cell.modifier.intersection(set),
613                        glyph: info.glyph_id,
614                        font: font.id(),
615                    };
616
617                    let ch = self.row[info.cluster as usize..].chars().next().unwrap();
618                    let width = (metrics
619                        .glyph_hor_advance(GlyphId(info.glyph_id as _))
620                        .unwrap_or_default() as f32
621                        * advance_scale) as u32;
622                    let chars_wide = ch.width().unwrap_or(max_width) as u32;
623                    let chars_wide = if chars_wide == 0 { 1 } else { chars_wide };
624                    let width = if width == 0 {
625                        chars_wide * self.fonts.min_width_px()
626                    } else {
627                        width
628                    };
629
630                    let cached = self.cached.get(
631                        &key,
632                        chars_wide * self.fonts.min_width_px(),
633                        self.fonts.height_px(),
634                    );
635
636                    let offset = (basey.max(0) as usize / self.fonts.height_px() as usize)
637                        .min(bounds.height as usize - 1)
638                        * bounds.width as usize
639                        + (basex.max(0) as usize / self.fonts.min_width_px() as usize)
640                            .min(bounds.width as usize - 1);
641
642                    sourced.insert((basex, basey, GlyphId(info.glyph_id as _), chars_wide));
643
644                    let mut underline_pos_min = 0;
645                    let mut underline_pos_max = 0;
646                    if key.style.contains(Modifier::UNDERLINED) {
647                        let underline_position = (metrics.ascender() as f32 * advance_scale) as u16;
648                        let underline_thickness = metrics
649                            .underline_metrics()
650                            .map(|m| (m.thickness as f32 * advance_scale) as u16)
651                            .unwrap_or(1);
652                        underline_pos_min = underline_position;
653                        underline_pos_max = underline_pos_min + underline_thickness;
654                    }
655
656                    self.rendered[offset].insert(
657                        (basex, basey, GlyphId(info.glyph_id as _)),
658                        RenderInfo {
659                            cell: y * bounds.width as usize + cell_idx,
660                            cached: *cached,
661                            underline_pos_min,
662                            underline_pos_max,
663                        },
664                    );
665                    for x_offset in 0..chars_wide as usize {
666                        self.dirty_cells.set(offset + x_offset, true);
667                    }
668
669                    if cached.cached() {
670                        continue;
671                    }
672
673                    pending_cache_updates.entry(key).or_insert_with(|| {
674                        let is_emoji = ch.is_emoji_char()
675                            && !matches!(ch.general_category_group(), GeneralCategoryGroup::Number);
676
677                        let (rect, image) = rasterize_glyph(
678                            cached,
679                            metrics,
680                            info,
681                            fake_italic & !is_emoji,
682                            fake_bold & !is_emoji,
683                            advance_scale,
684                            width,
685                        );
686                        (rect, image, is_emoji)
687                    });
688                }
689
690                buffer.clear()
691            };
692
693            let bidi = ParagraphBidiInfo::new(&self.row, None);
694            let (levels, runs) = bidi.visual_runs(0..bidi.levels.len());
695
696            let (mut current_font, mut current_fake_bold, mut current_fake_italic) = fontmap[0];
697            let mut current_level = Level::ltr();
698
699            for (level, range) in runs.into_iter().map(|run| (levels[run.start], run)) {
700                let chars = &self.row[range.clone()];
701                let cells = &self.rowmap[range.clone()];
702                for (idx, ch) in chars.char_indices() {
703                    let cell_idx = cells[idx] as usize;
704                    let (font, fake_bold, fake_italic) = fontmap[cell_idx];
705
706                    if font.id() != current_font.id()
707                        || current_fake_bold != fake_bold
708                        || current_fake_italic != fake_italic
709                        || current_level != level
710                    {
711                        let mut buffer = std::mem::take(&mut self.buffer);
712
713                        self.buffer = shape(
714                            current_font,
715                            current_fake_bold,
716                            current_fake_italic,
717                            shape_with_plan(
718                                current_font.font(),
719                                self.plan_cache.get(current_font, &mut buffer),
720                                buffer,
721                            ),
722                        );
723
724                        current_font = font;
725                        current_fake_bold = fake_bold;
726                        current_fake_italic = fake_italic;
727                        current_level = level;
728                    }
729
730                    self.buffer.add(ch, (range.start + idx) as u32);
731                }
732            }
733
734            let mut buffer = std::mem::take(&mut self.buffer);
735            self.buffer = shape(
736                current_font,
737                current_fake_bold,
738                current_fake_italic,
739                shape_with_plan(
740                    current_font.font(),
741                    self.plan_cache.get(current_font, &mut buffer),
742                    buffer,
743                ),
744            );
745
746            for (new, old) in new_sourced.into_iter().zip(sourced.iter_mut()) {
747                if new != *old {
748                    for (x, y, glyph, width) in old.difference(&new) {
749                        let cell = ((*y).max(0) as usize / self.fonts.height_px() as usize)
750                            .min(bounds.height as usize - 1)
751                            * bounds.width as usize
752                            + ((*x).max(0) as usize / self.fonts.min_width_px() as usize)
753                                .min(bounds.width as usize - 1);
754
755                        for offset_x in 0..*width as usize {
756                            if cell >= self.dirty_cells.len() {
757                                break;
758                            }
759
760                            self.dirty_cells.set(cell + offset_x, true);
761                        }
762
763                        self.rendered[cell].shift_remove(&(*x, *y, *glyph));
764                    }
765                    *old = new;
766                }
767            }
768        }
769
770        for (_, (cached, image, mask)) in pending_cache_updates {
771            self.queue.write_texture(
772                wgpu::TexelCopyTextureInfo {
773                    texture: &self.text_cache,
774                    mip_level: 0,
775                    origin: Origin3d {
776                        x: cached.x,
777                        y: cached.y,
778                        z: 0,
779                    },
780                    aspect: TextureAspect::All,
781                },
782                bytemuck::cast_slice(&image),
783                wgpu::TexelCopyBufferLayout {
784                    offset: 0,
785                    bytes_per_row: Some(cached.width * size_of::<u32>() as u32),
786                    rows_per_image: Some(cached.height),
787                },
788                Extent3d {
789                    width: cached.width,
790                    height: cached.height,
791                    depth_or_array_layers: 1,
792                },
793            );
794
795            self.queue.write_texture(
796                wgpu::TexelCopyTextureInfo {
797                    texture: &self.text_mask,
798                    mip_level: 0,
799                    origin: Origin3d {
800                        x: cached.x,
801                        y: cached.y,
802                        z: 0,
803                    },
804                    aspect: TextureAspect::All,
805                },
806                &vec![if mask { 255 } else { 0 }; (cached.width * cached.height) as usize],
807                wgpu::TexelCopyBufferLayout {
808                    offset: 0,
809                    bytes_per_row: Some(cached.width),
810                    rows_per_image: Some(cached.height),
811                },
812                Extent3d {
813                    width: cached.width,
814                    height: cached.height,
815                    depth_or_array_layers: 1,
816                },
817            )
818        }
819
820        if self.post_process.needs_update() || self.dirty_cells.any() {
821            self.bg_vertices.clear();
822            self.text_vertices.clear();
823            self.text_indices.clear();
824
825            let mut index_offset = 0;
826            for index in self.dirty_cells.iter_ones() {
827                let cell = &self.cells[index];
828                let to_render = &self.rendered[index];
829
830                let reverse = cell.modifier.contains(Modifier::REVERSED);
831                let bg_color = if reverse {
832                    c2c(cell.fg, self.reset_fg)
833                } else {
834                    c2c(cell.bg, self.reset_bg)
835                };
836
837                let [r, g, b] = bg_color;
838                let bg_color_u32: u32 = u32::from_be_bytes([r, g, b, 255]);
839
840                for (
841                    (x, y, _),
842                    RenderInfo {
843                        cell,
844                        cached,
845                        underline_pos_min,
846                        underline_pos_max,
847                    },
848                ) in to_render.iter()
849                {
850                    let cell = &self.cells[*cell];
851                    let reverse = cell.modifier.contains(Modifier::REVERSED);
852                    let fg_color = if reverse {
853                        c2c(cell.bg, self.reset_bg)
854                    } else {
855                        c2c(cell.fg, self.reset_fg)
856                    };
857
858                    let alpha = if cell.modifier.contains(Modifier::HIDDEN)
859                        | (cell.modifier.contains(Modifier::RAPID_BLINK) & !self.show_fast)
860                        | (cell.modifier.contains(Modifier::SLOW_BLINK) & !self.show_slow)
861                    {
862                        0
863                    } else if cell.modifier.contains(Modifier::DIM) {
864                        127
865                    } else {
866                        255
867                    };
868
869                    let underline_color = fg_color;
870                    let [r, g, b] = fg_color;
871                    let fg_color: u32 = u32::from_be_bytes([r, g, b, alpha]);
872
873                    let [r, g, b] = underline_color;
874                    let underline_color = u32::from_be_bytes([r, g, b, alpha]);
875
876                    for offset_x in (0..cached.width).step_by(self.fonts.min_width_px() as usize) {
877                        self.text_indices.push([
878                            index_offset,     // x, y
879                            index_offset + 1, // x + w, y
880                            index_offset + 2, // x, y + h
881                            index_offset + 2, // x, y + h
882                            index_offset + 3, // x + w, y + h
883                            index_offset + 1, // x + w y
884                        ]);
885                        index_offset += 4;
886
887                        let x = *x as f32 + offset_x as f32;
888                        let y = *y as f32;
889                        let uvx = cached.x + offset_x;
890                        let uvy = cached.y;
891
892                        self.bg_vertices.push(TextBgVertexMember {
893                            vertex: [x, y],
894                            bg_color: bg_color_u32,
895                        });
896                        self.bg_vertices.push(TextBgVertexMember {
897                            vertex: [x + self.fonts.min_width_px() as f32, y],
898                            bg_color: bg_color_u32,
899                        });
900                        self.bg_vertices.push(TextBgVertexMember {
901                            vertex: [x, y + self.fonts.height_px() as f32],
902                            bg_color: bg_color_u32,
903                        });
904                        self.bg_vertices.push(TextBgVertexMember {
905                            vertex: [
906                                x + self.fonts.min_width_px() as f32,
907                                y + self.fonts.height_px() as f32,
908                            ],
909                            bg_color: bg_color_u32,
910                        });
911
912                        let underline_pos = ((*underline_pos_min as u32 + uvy) << 16)
913                            | (*underline_pos_max as u32 + uvy);
914
915                        self.text_vertices.push(TextVertexMember {
916                            vertex: [x, y],
917                            uv: [uvx as f32, uvy as f32],
918                            fg_color,
919                            underline_pos,
920                            underline_color,
921                        });
922                        self.text_vertices.push(TextVertexMember {
923                            vertex: [x + self.fonts.min_width_px() as f32, y],
924                            uv: [uvx as f32 + self.fonts.min_width_px() as f32, uvy as f32],
925                            fg_color,
926                            underline_pos,
927                            underline_color,
928                        });
929                        self.text_vertices.push(TextVertexMember {
930                            vertex: [x, y + self.fonts.height_px() as f32],
931                            uv: [uvx as f32, uvy as f32 + self.fonts.height_px() as f32],
932                            fg_color,
933                            underline_pos,
934                            underline_color,
935                        });
936                        self.text_vertices.push(TextVertexMember {
937                            vertex: [
938                                x + self.fonts.min_width_px() as f32,
939                                y + self.fonts.height_px() as f32,
940                            ],
941                            uv: [
942                                uvx as f32 + self.fonts.min_width_px() as f32,
943                                uvy as f32 + self.fonts.height_px() as f32,
944                            ],
945                            fg_color,
946                            underline_pos,
947                            underline_color,
948                        });
949                    }
950                }
951            }
952
953            self.render();
954        }
955
956        Ok(())
957    }
958
959    fn clear_region(&mut self, clear_type: ClearType) -> std::io::Result<()> {
960        let bounds = self.size()?;
961        let line_start = self.cursor.1 as usize * bounds.width as usize;
962        let idx = line_start + self.cursor.0 as usize;
963
964        match clear_type {
965            ClearType::All => self.clear(),
966            ClearType::AfterCursor => {
967                self.cells.truncate(idx + 1);
968                Ok(())
969            }
970            ClearType::BeforeCursor => {
971                self.cells[..idx].fill(Cell::EMPTY);
972                Ok(())
973            }
974            ClearType::CurrentLine => {
975                self.cells[line_start..line_start + bounds.width as usize].fill(Cell::EMPTY);
976                Ok(())
977            }
978            ClearType::UntilNewLine => {
979                let remain = (bounds.width - self.cursor.0) as usize;
980                self.cells[idx..idx + remain].fill(Cell::EMPTY);
981                Ok(())
982            }
983        }
984    }
985}
986
987fn rasterize_glyph(
988    cached: Entry,
989    metrics: &rustybuzz::Face,
990    info: &rustybuzz::GlyphInfo,
991    fake_italic: bool,
992    fake_bold: bool,
993    advance_scale: f32,
994    actual_width: u32,
995) -> (CacheRect, Vec<u32>) {
996    let scale = cached.width as f32 / actual_width as f32;
997    let computed_offset_x = -(cached.width as f32 * (1.0 - scale));
998    let computed_offset_y = cached.height as f32 * (1.0 - scale);
999    let scale = scale * advance_scale * 2.0;
1000
1001    let skew = if fake_italic {
1002        Transform::new(
1003            /* scale x */ 1.0,
1004            /* skew x */ 0.0,
1005            /* skew y */ -0.25,
1006            /* scale y */ 1.0,
1007            /* translate x */ -0.25 * cached.width as f32,
1008            /* translate y */ 0.0,
1009        )
1010    } else {
1011        Transform::default()
1012    };
1013
1014    let mut image = vec![0u32; cached.width as usize * 2 * cached.height as usize * 2];
1015    let mut target = DrawTarget::from_backing(
1016        cached.width as i32 * 2,
1017        cached.height as i32 * 2,
1018        &mut image[..],
1019    );
1020
1021    let mut painter = Painter::new(
1022        metrics,
1023        &mut target,
1024        skew,
1025        scale,
1026        metrics.ascender() as f32 * scale + computed_offset_y,
1027        computed_offset_x,
1028    );
1029    if metrics
1030        .paint_color_glyph(
1031            GlyphId(info.glyph_id as _),
1032            0,
1033            RgbaColor::new(255, 255, 255, 255),
1034            &mut painter,
1035        )
1036        .is_some()
1037    {
1038        let mut final_image = DrawTarget::new(cached.width as i32, cached.height as i32);
1039        final_image.draw_image_with_size_at(
1040            cached.width as f32,
1041            cached.height as f32,
1042            0.,
1043            0.,
1044            &raqote::Image {
1045                width: cached.width as i32 * 2,
1046                height: cached.height as i32 * 2,
1047                data: &image,
1048            },
1049            &DrawOptions {
1050                blend_mode: raqote::BlendMode::Src,
1051                antialias: raqote::AntialiasMode::None,
1052                ..Default::default()
1053            },
1054        );
1055
1056        let mut final_image = final_image.into_vec();
1057        for argb in final_image.iter_mut() {
1058            let [a, r, g, b] = argb.to_be_bytes();
1059            *argb = u32::from_le_bytes([r, g, b, a]);
1060        }
1061
1062        return (*cached, final_image);
1063    }
1064
1065    if let Some(raster) = metrics.glyph_raster_image(GlyphId(info.glyph_id as _), u16::MAX) {
1066        if let Some(value) = extract_color_image(&mut image, raster, cached, advance_scale) {
1067            return value;
1068        }
1069    }
1070
1071    let mut render = Outline::default();
1072    if let Some(bounds) = metrics.outline_glyph(GlyphId(info.glyph_id as _), &mut render) {
1073        let path = render.finish();
1074
1075        // Some fonts return bounds that are entirely negative. I'm not sure why this
1076        // is, but it means the glyph won't render at all. We check for this here and
1077        // offset it if so. This seems to let those fonts render correctly.
1078        let x_off = if bounds.x_max < 0 {
1079            -bounds.x_min as f32
1080        } else {
1081            0.
1082        };
1083        let x_off = x_off * scale + computed_offset_x;
1084        let y_off = metrics.ascender() as f32 * scale + computed_offset_y;
1085
1086        let mut target = DrawTarget::from_backing(
1087            cached.width as i32 * 2,
1088            cached.height as i32 * 2,
1089            &mut image[..],
1090        );
1091        target.set_transform(
1092            &Transform::scale(scale, -scale)
1093                .then(&skew)
1094                .then_translate((x_off, y_off).into()),
1095        );
1096
1097        target.fill(
1098            &path,
1099            &raqote::Source::Solid(SolidSource::from_unpremultiplied_argb(255, 255, 255, 255)),
1100            &DrawOptions::default(),
1101        );
1102
1103        if fake_bold {
1104            target.stroke(
1105                &path,
1106                &raqote::Source::Solid(SolidSource::from_unpremultiplied_argb(255, 255, 255, 255)),
1107                &StrokeStyle {
1108                    width: 1.5,
1109                    ..Default::default()
1110                },
1111                &DrawOptions::new(),
1112            );
1113        }
1114
1115        let mut final_image = DrawTarget::new(cached.width as i32, cached.height as i32);
1116        final_image.draw_image_with_size_at(
1117            cached.width as f32,
1118            cached.height as f32,
1119            0.,
1120            0.,
1121            &raqote::Image {
1122                width: cached.width as i32 * 2,
1123                height: cached.height as i32 * 2,
1124                data: &image,
1125            },
1126            &DrawOptions {
1127                blend_mode: raqote::BlendMode::Src,
1128                antialias: raqote::AntialiasMode::None,
1129                ..Default::default()
1130            },
1131        );
1132
1133        return (*cached, final_image.into_vec());
1134    }
1135
1136    if let Some(raster) = metrics.glyph_raster_image(GlyphId(info.glyph_id as _), u16::MAX) {
1137        if let Some(value) = extract_bw_image(&mut image, raster, cached, advance_scale) {
1138            return value;
1139        }
1140    }
1141
1142    (
1143        *cached,
1144        vec![0u32; cached.width as usize * cached.height as usize],
1145    )
1146}
1147
1148fn extract_color_image(
1149    image: &mut Vec<u32>,
1150    raster: RasterGlyphImage,
1151    cached: Entry,
1152    scale: f32,
1153) -> Option<(CacheRect, Vec<u32>)> {
1154    match raster.format {
1155        RasterImageFormat::PNG => {
1156            #[cfg(feature = "png")]
1157            {
1158                let decoder = png::Decoder::new(std::io::Cursor::new(raster.data));
1159                if let Ok(mut info) = decoder.read_info() {
1160                    image.resize(info.output_buffer_size() / size_of::<u32>(), 0);
1161                    if info.next_frame(bytemuck::cast_slice_mut(image)).is_err() {
1162                        return None;
1163                    }
1164
1165                    for rgba in image.iter_mut() {
1166                        let [r, g, b, a] = rgba.to_be_bytes();
1167                        *rgba = u32::from_be_bytes([a, r, g, b]);
1168                    }
1169                } else {
1170                    return None;
1171                }
1172            }
1173            #[cfg(not(feature = "png"))]
1174            return None;
1175        }
1176        RasterImageFormat::BitmapPremulBgra32 => {
1177            image.resize(raster.width as usize * raster.height as usize, 0);
1178            for (y, row) in raster.data.chunks(raster.width as usize * 4).enumerate() {
1179                for (x, pixel) in row.chunks(4).enumerate() {
1180                    let pixel: &[u8; 4] = pixel.try_into().expect("Invalid chunk size");
1181                    let [b, g, r, a] = *pixel;
1182                    let pixel = u32::from_be_bytes([
1183                        a,
1184                        r.saturating_mul(255 / a),
1185                        g.saturating_mul(255 / a),
1186                        b.saturating_mul(255 / a),
1187                    ]);
1188                    image[y * raster.width as usize + x] = pixel;
1189                }
1190            }
1191        }
1192        _ => return None,
1193    }
1194
1195    let mut final_image = DrawTarget::new(cached.width as i32, cached.height as i32);
1196    final_image.draw_image_with_size_at(
1197        cached.width as f32,
1198        cached.height as f32,
1199        raster.x as f32 * scale,
1200        raster.y as f32 * scale,
1201        &raqote::Image {
1202            width: raster.width as i32,
1203            height: raster.height as i32,
1204            data: &*image,
1205        },
1206        &DrawOptions {
1207            blend_mode: raqote::BlendMode::Src,
1208            antialias: raqote::AntialiasMode::None,
1209            ..Default::default()
1210        },
1211    );
1212
1213    let mut final_image = final_image.into_vec();
1214    for argb in final_image.iter_mut() {
1215        let [a, r, g, b] = argb.to_be_bytes();
1216        *argb = u32::from_le_bytes([r, g, b, a]);
1217    }
1218
1219    Some((*cached, final_image))
1220}
1221
1222fn extract_bw_image(
1223    image: &mut Vec<u32>,
1224    raster: RasterGlyphImage,
1225    cached: Entry,
1226    scale: f32,
1227) -> Option<(CacheRect, Vec<u32>)> {
1228    image.resize(raster.width as usize * raster.height as usize, 0);
1229
1230    match raster.format {
1231        RasterImageFormat::BitmapMono => {
1232            from_gray_unpacked::<1, 2>(image, raster, LUT_1);
1233        }
1234        RasterImageFormat::BitmapMonoPacked => {
1235            from_gray_packed::<1, 2>(image, raster, LUT_1);
1236        }
1237        RasterImageFormat::BitmapGray2 => {
1238            from_gray_unpacked::<2, 4>(image, raster, LUT_2);
1239        }
1240        RasterImageFormat::BitmapGray2Packed => {
1241            from_gray_packed::<2, 4>(image, raster, LUT_2);
1242        }
1243        RasterImageFormat::BitmapGray4 => {
1244            from_gray_unpacked::<4, 16>(image, raster, LUT_4);
1245        }
1246        RasterImageFormat::BitmapGray4Packed => {
1247            from_gray_packed::<4, 16>(image, raster, LUT_4);
1248        }
1249        RasterImageFormat::BitmapGray8 => {
1250            for (byte, dst) in raster.data.iter().zip(image.iter_mut()) {
1251                *dst = u32::from_be_bytes([*byte, 255, 255, 255]);
1252            }
1253        }
1254        _ => return None,
1255    }
1256
1257    let mut final_image = DrawTarget::new(cached.width as i32, cached.height as i32);
1258    final_image.draw_image_with_size_at(
1259        cached.width as f32,
1260        cached.height as f32,
1261        raster.x as f32 * scale,
1262        raster.y as f32 * scale,
1263        &raqote::Image {
1264            width: raster.width as i32,
1265            height: raster.height as i32,
1266            data: &*image,
1267        },
1268        &DrawOptions {
1269            blend_mode: raqote::BlendMode::Src,
1270            antialias: raqote::AntialiasMode::None,
1271            ..Default::default()
1272        },
1273    );
1274
1275    let mut final_image = final_image.into_vec();
1276    for argb in final_image.iter_mut() {
1277        let [a, r, g, b] = argb.to_be_bytes();
1278        *argb = u32::from_le_bytes([r, g, b, a]);
1279    }
1280
1281    Some((*cached, final_image))
1282}
1283
1284fn from_gray_unpacked<const BITS: usize, const ENTRIES: usize>(
1285    image: &mut [u32],
1286    raster: RasterGlyphImage,
1287    steps: [u8; ENTRIES],
1288) {
1289    for (bits, dst) in raster
1290        .data
1291        .chunks((raster.width as usize / (8 / BITS)) + 1)
1292        .zip(image.chunks_mut(raster.width as usize))
1293    {
1294        let bits = BitSlice::<_, Lsb0>::from_slice(bits);
1295        for (bits, dst) in bits.chunks(BITS).zip(dst.iter_mut()) {
1296            let mut index = 0;
1297            for idx in bits.iter_ones() {
1298                index |= 1 << (BITS - idx - 1);
1299            }
1300            let value = steps[index as usize];
1301            *dst = u32::from_be_bytes([value, 255, 255, 255]);
1302        }
1303    }
1304}
1305
1306fn from_gray_packed<const BITS: usize, const ENTRIES: usize>(
1307    image: &mut [u32],
1308    raster: RasterGlyphImage,
1309    steps: [u8; ENTRIES],
1310) {
1311    let bits = BitSlice::<_, Lsb0>::from_slice(raster.data);
1312    for (bits, dst) in bits.chunks(BITS).zip(image.iter_mut()) {
1313        let mut index = 0;
1314        for idx in bits.iter_ones() {
1315            index |= 1 << (BITS - idx - 1);
1316        }
1317        let value = steps[index as usize];
1318        *dst = u32::from_be_bytes([value, 255, 255, 255]);
1319    }
1320}
1321
1322const LUT_1: [u8; 2] = [0, 255];
1323const LUT_2: [u8; 4] = [0, 255 / 3, 2 * (255 / 3), 255];
1324const LUT_4: [u8; 16] = [
1325    0,
1326    (255 / 15),
1327    2 * (255 / 15),
1328    3 * (255 / 15),
1329    4 * (255 / 15),
1330    5 * (255 / 15),
1331    6 * (255 / 15),
1332    7 * (255 / 15),
1333    8 * (255 / 15),
1334    9 * (255 / 15),
1335    10 * (255 / 15),
1336    11 * (255 / 15),
1337    12 * (255 / 15),
1338    13 * (255 / 15),
1339    14 * (255 / 15),
1340    255,
1341];
1342
1343#[cfg(test)]
1344mod tests {
1345    use std::num::NonZeroU32;
1346
1347    use image::{
1348        load_from_memory,
1349        GenericImageView,
1350        ImageBuffer,
1351        Rgba,
1352    };
1353    use ratatui::{
1354        style::{
1355            Color,
1356            Stylize,
1357        },
1358        text::Line,
1359        widgets::{
1360            Block,
1361            Paragraph,
1362        },
1363        Terminal,
1364    };
1365    use rustybuzz::ttf_parser::{
1366        RasterGlyphImage,
1367        RasterImageFormat,
1368    };
1369    use serial_test::serial;
1370    use wgpu::{
1371        wgt::PollType,
1372        CommandEncoderDescriptor,
1373        Device,
1374        Extent3d,
1375        Queue,
1376        TextureFormat,
1377    };
1378
1379    use crate::{
1380        backend::{
1381            wgpu_backend::{
1382                extract_bw_image,
1383                LUT_2,
1384                LUT_4,
1385            },
1386            HeadlessSurface,
1387        },
1388        shaders::DefaultPostProcessor,
1389        utils::text_atlas::{
1390            CacheRect,
1391            Entry,
1392        },
1393        Builder,
1394        Dimensions,
1395        Font,
1396    };
1397
1398    fn tex2buffer(device: &Device, queue: &Queue, surface: &HeadlessSurface) {
1399        let mut encoder = device.create_command_encoder(&CommandEncoderDescriptor::default());
1400        encoder.copy_texture_to_buffer(
1401            surface.texture.as_ref().unwrap().as_image_copy(),
1402            wgpu::TexelCopyBufferInfo {
1403                buffer: surface.buffer.as_ref().unwrap(),
1404                layout: wgpu::TexelCopyBufferLayout {
1405                    offset: 0,
1406                    bytes_per_row: Some(surface.buffer_width),
1407                    rows_per_image: Some(surface.height),
1408                },
1409            },
1410            Extent3d {
1411                width: surface.width,
1412                height: surface.height,
1413                depth_or_array_layers: 1,
1414            },
1415        );
1416        queue.submit(Some(encoder.finish()));
1417    }
1418
1419    #[test]
1420    #[serial]
1421    fn a_z() {
1422        let mut terminal = Terminal::new(
1423            futures_lite::future::block_on(
1424                Builder::<DefaultPostProcessor>::from_font(
1425                    Font::new(include_bytes!("fonts/CascadiaMono-Regular.ttf"))
1426                        .expect("Invalid font file"),
1427                )
1428                .with_width_and_height(Dimensions {
1429                    width: NonZeroU32::new(512).unwrap(),
1430                    height: NonZeroU32::new(72).unwrap(),
1431                })
1432                .build_headless(),
1433            )
1434            .unwrap(),
1435        )
1436        .unwrap();
1437
1438        terminal
1439            .draw(|f| {
1440                let block = Block::bordered();
1441                let area = block.inner(f.area());
1442                f.render_widget(block, f.area());
1443                f.render_widget(Paragraph::new("ABCDEFGHIJKLMNOPQRSTUVWXYZ"), area);
1444            })
1445            .unwrap();
1446
1447        let surface = &terminal.backend().surface;
1448        tex2buffer(
1449            &terminal.backend().device,
1450            &terminal.backend().queue,
1451            surface,
1452        );
1453        {
1454            let buffer = surface.buffer.as_ref().unwrap().slice(..);
1455
1456            let (send, recv) = oneshot::channel();
1457            buffer.map_async(wgpu::MapMode::Read, move |data| {
1458                send.send(data).unwrap();
1459            });
1460            terminal.backend().device.poll(PollType::Wait).unwrap();
1461            recv.recv().unwrap().unwrap();
1462
1463            let data = buffer.get_mapped_range();
1464            let image =
1465                ImageBuffer::<Rgba<u8>, _>::from_raw(surface.width, surface.height, data).unwrap();
1466
1467            let pixels = image.pixels().copied().collect::<Vec<_>>();
1468            let golden = load_from_memory(include_bytes!("goldens/a_z.png")).unwrap();
1469            let golden_pixels = golden.pixels().map(|(_, _, px)| px).collect::<Vec<_>>();
1470
1471            assert!(
1472                pixels == golden_pixels,
1473                "Rendered image differs from golden"
1474            );
1475        }
1476
1477        surface.buffer.as_ref().unwrap().unmap();
1478    }
1479
1480    #[test]
1481    #[serial]
1482    fn arabic() {
1483        let mut terminal = Terminal::new(
1484            futures_lite::future::block_on(
1485                Builder::<DefaultPostProcessor>::from_font(
1486                    Font::new(include_bytes!("fonts/CascadiaMono-Regular.ttf"))
1487                        .expect("Invalid font file"),
1488                )
1489                .with_width_and_height(Dimensions {
1490                    width: NonZeroU32::new(256).unwrap(),
1491                    height: NonZeroU32::new(72).unwrap(),
1492                })
1493                .build_headless(),
1494            )
1495            .unwrap(),
1496        )
1497        .unwrap();
1498
1499        terminal
1500            .draw(|f| {
1501                let block = Block::bordered();
1502                let area = block.inner(f.area());
1503                f.render_widget(block, f.area());
1504                f.render_widget(Paragraph::new("مرحبا بالعالم"), area);
1505            })
1506            .unwrap();
1507
1508        let surface = &terminal.backend().surface;
1509        tex2buffer(
1510            &terminal.backend().device,
1511            &terminal.backend().queue,
1512            surface,
1513        );
1514        {
1515            let buffer = surface.buffer.as_ref().unwrap().slice(..);
1516
1517            let (send, recv) = oneshot::channel();
1518            buffer.map_async(wgpu::MapMode::Read, move |data| {
1519                send.send(data).unwrap();
1520            });
1521            terminal.backend().device.poll(PollType::Wait).unwrap();
1522            recv.recv().unwrap().unwrap();
1523
1524            let data = buffer.get_mapped_range();
1525            let image =
1526                ImageBuffer::<Rgba<u8>, _>::from_raw(surface.width, surface.height, data).unwrap();
1527
1528            let pixels = image.pixels().copied().collect::<Vec<_>>();
1529            let golden = load_from_memory(include_bytes!("goldens/arabic.png")).unwrap();
1530            let golden_pixels = golden.pixels().map(|(_, _, px)| px).collect::<Vec<_>>();
1531
1532            assert!(
1533                pixels == golden_pixels,
1534                "Rendered image differs from golden"
1535            );
1536        }
1537
1538        surface.buffer.as_ref().unwrap().unmap();
1539    }
1540
1541    #[test]
1542    #[serial]
1543    fn really_wide() {
1544        let mut terminal = Terminal::new(
1545            futures_lite::future::block_on(
1546                Builder::<DefaultPostProcessor>::from_font(
1547                    Font::new(include_bytes!("fonts/Fairfax.ttf")).expect("Invalid font file"),
1548                )
1549                .with_width_and_height(Dimensions {
1550                    width: NonZeroU32::new(512).unwrap(),
1551                    height: NonZeroU32::new(72).unwrap(),
1552                })
1553                .build_headless(),
1554            )
1555            .unwrap(),
1556        )
1557        .unwrap();
1558
1559        terminal
1560            .draw(|f| {
1561                let block = Block::bordered();
1562                let area = block.inner(f.area());
1563                f.render_widget(block, f.area());
1564                f.render_widget(Paragraph::new("Hello, world!"), area);
1565            })
1566            .unwrap();
1567
1568        let surface = &terminal.backend().surface;
1569        tex2buffer(
1570            &terminal.backend().device,
1571            &terminal.backend().queue,
1572            surface,
1573        );
1574        {
1575            let buffer = surface.buffer.as_ref().unwrap().slice(..);
1576
1577            let (send, recv) = oneshot::channel();
1578            buffer.map_async(wgpu::MapMode::Read, move |data| {
1579                send.send(data).unwrap();
1580            });
1581            terminal.backend().device.poll(PollType::Wait).unwrap();
1582            recv.recv().unwrap().unwrap();
1583
1584            let data = buffer.get_mapped_range();
1585            let image =
1586                ImageBuffer::<Rgba<u8>, _>::from_raw(surface.width, surface.height, data).unwrap();
1587
1588            let pixels = image.pixels().copied().collect::<Vec<_>>();
1589            let golden = load_from_memory(include_bytes!("goldens/really_wide.png")).unwrap();
1590            let golden_pixels = golden.pixels().map(|(_, _, px)| px).collect::<Vec<_>>();
1591
1592            assert!(
1593                pixels == golden_pixels,
1594                "Rendered image differs from golden"
1595            );
1596        }
1597
1598        surface.buffer.as_ref().unwrap().unmap();
1599    }
1600
1601    #[test]
1602    #[serial]
1603    fn mixed() {
1604        let mut terminal = Terminal::new(
1605            futures_lite::future::block_on(
1606                Builder::<DefaultPostProcessor>::from_font(
1607                    Font::new(include_bytes!("fonts/CascadiaMono-Regular.ttf"))
1608                        .expect("Invalid font file"),
1609                )
1610                .with_width_and_height(Dimensions {
1611                    width: NonZeroU32::new(512).unwrap(),
1612                    height: NonZeroU32::new(72).unwrap(),
1613                })
1614                .build_headless(),
1615            )
1616            .unwrap(),
1617        )
1618        .unwrap();
1619
1620        terminal
1621            .draw(|f| {
1622                let block = Block::bordered();
1623                let area = block.inner(f.area());
1624                f.render_widget(block, f.area());
1625                f.render_widget(
1626                    Paragraph::new("Hello World! مرحبا بالعالم 0123456789000000000"),
1627                    area,
1628                );
1629            })
1630            .unwrap();
1631
1632        let surface = &terminal.backend().surface;
1633        tex2buffer(
1634            &terminal.backend().device,
1635            &terminal.backend().queue,
1636            surface,
1637        );
1638        {
1639            let buffer = surface.buffer.as_ref().unwrap().slice(..);
1640
1641            let (send, recv) = oneshot::channel();
1642            buffer.map_async(wgpu::MapMode::Read, move |data| {
1643                send.send(data).unwrap();
1644            });
1645            terminal.backend().device.poll(PollType::Wait).unwrap();
1646            recv.recv().unwrap().unwrap();
1647
1648            let data = buffer.get_mapped_range();
1649            let image =
1650                ImageBuffer::<Rgba<u8>, _>::from_raw(surface.width, surface.height, data).unwrap();
1651
1652            let pixels = image.pixels().copied().collect::<Vec<_>>();
1653            let golden = load_from_memory(include_bytes!("goldens/mixed.png")).unwrap();
1654            let golden_pixels = golden.pixels().map(|(_, _, px)| px).collect::<Vec<_>>();
1655
1656            assert!(
1657                pixels == golden_pixels,
1658                "Rendered image differs from golden"
1659            );
1660        }
1661
1662        surface.buffer.as_ref().unwrap().unmap();
1663    }
1664
1665    #[test]
1666    #[serial]
1667    fn mixed_colors() {
1668        let mut terminal = Terminal::new(
1669            futures_lite::future::block_on(
1670                Builder::<DefaultPostProcessor>::from_font(
1671                    Font::new(include_bytes!("fonts/CascadiaMono-Regular.ttf"))
1672                        .expect("Invalid font file"),
1673                )
1674                .with_width_and_height(Dimensions {
1675                    width: NonZeroU32::new(512).unwrap(),
1676                    height: NonZeroU32::new(72).unwrap(),
1677                })
1678                .build_headless(),
1679            )
1680            .unwrap(),
1681        )
1682        .unwrap();
1683
1684        terminal
1685            .draw(|f| {
1686                let block = Block::bordered();
1687                let area = block.inner(f.area());
1688                f.render_widget(block, f.area());
1689                f.render_widget(
1690                    Paragraph::new(Line::from(vec![
1691                        "Hello World!".green(),
1692                        "مرحبا بالعالم".blue(),
1693                        "0123456789".dim(),
1694                    ])),
1695                    area,
1696                );
1697            })
1698            .unwrap();
1699
1700        let surface = &terminal.backend().surface;
1701        tex2buffer(
1702            &terminal.backend().device,
1703            &terminal.backend().queue,
1704            surface,
1705        );
1706        {
1707            let buffer = surface.buffer.as_ref().unwrap().slice(..);
1708
1709            let (send, recv) = oneshot::channel();
1710            buffer.map_async(wgpu::MapMode::Read, move |data| {
1711                send.send(data).unwrap();
1712            });
1713            terminal.backend().device.poll(PollType::Wait).unwrap();
1714            recv.recv().unwrap().unwrap();
1715
1716            let data = buffer.get_mapped_range();
1717            let image =
1718                ImageBuffer::<Rgba<u8>, _>::from_raw(surface.width, surface.height, data).unwrap();
1719
1720            let pixels = image.pixels().copied().collect::<Vec<_>>();
1721            let golden = load_from_memory(include_bytes!("goldens/mixed_colors.png")).unwrap();
1722            let golden_pixels = golden.pixels().map(|(_, _, px)| px).collect::<Vec<_>>();
1723
1724            assert!(
1725                pixels == golden_pixels,
1726                "Rendered image differs from golden"
1727            );
1728        }
1729
1730        surface.buffer.as_ref().unwrap().unmap();
1731    }
1732
1733    #[test]
1734    #[serial]
1735    fn overlap() {
1736        let mut terminal = Terminal::new(
1737            futures_lite::future::block_on(
1738                Builder::<DefaultPostProcessor>::from_font(
1739                    Font::new(include_bytes!("fonts/Fairfax.ttf")).expect("Invalid font file"),
1740                )
1741                .with_width_and_height(Dimensions {
1742                    width: NonZeroU32::new(256).unwrap(),
1743                    height: NonZeroU32::new(72).unwrap(),
1744                })
1745                .build_headless(),
1746            )
1747            .unwrap(),
1748        )
1749        .unwrap();
1750
1751        terminal
1752            .draw(|f| {
1753                let block = Block::bordered();
1754                let area = block.inner(f.area());
1755                f.render_widget(block, f.area());
1756                f.render_widget(Paragraph::new("H̴̢͕̠͖͇̻͓̙̞͔͕͓̰͋͛͂̃̌͂͆͜͠".underlined()), area);
1757            })
1758            .unwrap();
1759
1760        let surface = &terminal.backend().surface;
1761        tex2buffer(
1762            &terminal.backend().device,
1763            &terminal.backend().queue,
1764            surface,
1765        );
1766        {
1767            let buffer = surface.buffer.as_ref().unwrap().slice(..);
1768
1769            let (send, recv) = oneshot::channel();
1770            buffer.map_async(wgpu::MapMode::Read, move |data| {
1771                send.send(data).unwrap();
1772            });
1773            terminal.backend().device.poll(PollType::Wait).unwrap();
1774            recv.recv().unwrap().unwrap();
1775
1776            let data = buffer.get_mapped_range();
1777            let image =
1778                ImageBuffer::<Rgba<u8>, _>::from_raw(surface.width, surface.height, data).unwrap();
1779
1780            let pixels = image.pixels().copied().collect::<Vec<_>>();
1781            let golden = load_from_memory(include_bytes!("goldens/overlap_initial.png")).unwrap();
1782            let golden_pixels = golden.pixels().map(|(_, _, px)| px).collect::<Vec<_>>();
1783
1784            assert!(
1785                pixels == golden_pixels,
1786                "Rendered image differs from golden"
1787            );
1788        }
1789        surface.buffer.as_ref().unwrap().unmap();
1790
1791        terminal
1792            .draw(|f| {
1793                let block = Block::bordered();
1794                let area = block.inner(f.area());
1795                f.render_widget(block, f.area());
1796                f.render_widget(Paragraph::new("H".underlined()), area);
1797            })
1798            .unwrap();
1799
1800        let surface = &terminal.backend().surface;
1801        tex2buffer(
1802            &terminal.backend().device,
1803            &terminal.backend().queue,
1804            surface,
1805        );
1806        {
1807            let buffer = surface.buffer.as_ref().unwrap().slice(..);
1808
1809            let (send, recv) = oneshot::channel();
1810            buffer.map_async(wgpu::MapMode::Read, move |data| {
1811                send.send(data).unwrap();
1812            });
1813            terminal.backend().device.poll(PollType::Wait).unwrap();
1814            recv.recv().unwrap().unwrap();
1815
1816            let data = buffer.get_mapped_range();
1817            let image =
1818                ImageBuffer::<Rgba<u8>, _>::from_raw(surface.width, surface.height, data).unwrap();
1819
1820            let pixels = image.pixels().copied().collect::<Vec<_>>();
1821            let golden = load_from_memory(include_bytes!("goldens/overlap_post.png")).unwrap();
1822            let golden_pixels = golden.pixels().map(|(_, _, px)| px).collect::<Vec<_>>();
1823
1824            assert!(
1825                pixels == golden_pixels,
1826                "Rendered image differs from golden"
1827            );
1828        }
1829
1830        surface.buffer.as_ref().unwrap().unmap();
1831    }
1832
1833    #[test]
1834    #[serial]
1835    fn overlap_colors() {
1836        let mut terminal = Terminal::new(
1837            futures_lite::future::block_on(
1838                Builder::<DefaultPostProcessor>::from_font(
1839                    Font::new(include_bytes!("fonts/Fairfax.ttf")).expect("Invalid font file"),
1840                )
1841                .with_width_and_height(Dimensions {
1842                    width: NonZeroU32::new(256).unwrap(),
1843                    height: NonZeroU32::new(72).unwrap(),
1844                })
1845                .build_headless(),
1846            )
1847            .unwrap(),
1848        )
1849        .unwrap();
1850
1851        terminal
1852            .draw(|f| {
1853                let block = Block::bordered();
1854                let area = block.inner(f.area());
1855                f.render_widget(block, f.area());
1856                f.render_widget(Paragraph::new("H̴̢͕̠͖͇̻͓̙̞͔͕͓̰͋͛͂̃̌͂͆͜͠".blue().on_red().underlined()), area);
1857            })
1858            .unwrap();
1859
1860        let surface = &terminal.backend().surface;
1861        tex2buffer(
1862            &terminal.backend().device,
1863            &terminal.backend().queue,
1864            surface,
1865        );
1866        {
1867            let buffer = surface.buffer.as_ref().unwrap().slice(..);
1868
1869            let (send, recv) = oneshot::channel();
1870            buffer.map_async(wgpu::MapMode::Read, move |data| {
1871                send.send(data).unwrap();
1872            });
1873            terminal.backend().device.poll(PollType::Wait).unwrap();
1874            recv.recv().unwrap().unwrap();
1875
1876            let data = buffer.get_mapped_range();
1877            let image =
1878                ImageBuffer::<Rgba<u8>, _>::from_raw(surface.width, surface.height, data).unwrap();
1879
1880            let pixels = image.pixels().copied().collect::<Vec<_>>();
1881            let golden = load_from_memory(include_bytes!("goldens/overlap_colors.png")).unwrap();
1882            let golden_pixels = golden.pixels().map(|(_, _, px)| px).collect::<Vec<_>>();
1883
1884            assert!(
1885                pixels == golden_pixels,
1886                "Rendered image differs from golden"
1887            );
1888        }
1889        surface.buffer.as_ref().unwrap().unmap();
1890    }
1891
1892    #[test]
1893    #[serial]
1894    fn rgb_conversion() {
1895        let mut terminal = Terminal::new(
1896            futures_lite::future::block_on(
1897                Builder::<DefaultPostProcessor>::from_font(
1898                    Font::new(include_bytes!("fonts/Fairfax.ttf")).expect("Invalid font file"),
1899                )
1900                .with_width_and_height(Dimensions {
1901                    width: NonZeroU32::new(256).unwrap(),
1902                    height: NonZeroU32::new(72).unwrap(),
1903                })
1904                .with_bg_color(Color::Rgb(0x1E, 0x23, 0x26))
1905                .with_fg_color(Color::White)
1906                .build_headless_with_format(TextureFormat::Rgba8Unorm),
1907            )
1908            .unwrap(),
1909        )
1910        .unwrap();
1911
1912        terminal
1913            .draw(|f| {
1914                let block = Block::bordered();
1915                let area = block.inner(f.area());
1916                f.render_widget(block, f.area());
1917                f.render_widget(Paragraph::new("TEST"), area);
1918            })
1919            .unwrap();
1920
1921        let surface = &terminal.backend().surface;
1922        tex2buffer(
1923            &terminal.backend().device,
1924            &terminal.backend().queue,
1925            surface,
1926        );
1927        {
1928            let buffer = surface.buffer.as_ref().unwrap().slice(..);
1929
1930            let (send, recv) = oneshot::channel();
1931            buffer.map_async(wgpu::MapMode::Read, move |data| {
1932                send.send(data).unwrap();
1933            });
1934            terminal.backend().device.poll(PollType::Wait).unwrap();
1935            recv.recv().unwrap().unwrap();
1936
1937            let data = buffer.get_mapped_range();
1938            let image =
1939                ImageBuffer::<Rgba<u8>, _>::from_raw(surface.width, surface.height, data).unwrap();
1940
1941            let pixels = image.pixels().copied().collect::<Vec<_>>();
1942            let golden = load_from_memory(include_bytes!("goldens/rgb_conversion.png")).unwrap();
1943            let golden_pixels = golden.pixels().map(|(_, _, px)| px).collect::<Vec<_>>();
1944
1945            assert!(
1946                pixels == golden_pixels,
1947                "Rendered image differs from golden"
1948            );
1949        }
1950        surface.buffer.as_ref().unwrap().unmap();
1951    }
1952
1953    #[test]
1954    #[serial]
1955    fn srgb_conversion() {
1956        let mut terminal = Terminal::new(
1957            futures_lite::future::block_on(
1958                Builder::<DefaultPostProcessor>::from_font(
1959                    Font::new(include_bytes!("fonts/Fairfax.ttf")).expect("Invalid font file"),
1960                )
1961                .with_width_and_height(Dimensions {
1962                    width: NonZeroU32::new(256).unwrap(),
1963                    height: NonZeroU32::new(72).unwrap(),
1964                })
1965                .with_bg_color(Color::Rgb(0x1E, 0x23, 0x26))
1966                .with_fg_color(Color::White)
1967                .build_headless_with_format(TextureFormat::Rgba8UnormSrgb),
1968            )
1969            .unwrap(),
1970        )
1971        .unwrap();
1972
1973        terminal
1974            .draw(|f| {
1975                let block = Block::bordered();
1976                let area = block.inner(f.area());
1977                f.render_widget(block, f.area());
1978                f.render_widget(Paragraph::new("TEST"), area);
1979            })
1980            .unwrap();
1981
1982        let surface = &terminal.backend().surface;
1983        tex2buffer(
1984            &terminal.backend().device,
1985            &terminal.backend().queue,
1986            surface,
1987        );
1988        {
1989            let buffer = surface.buffer.as_ref().unwrap().slice(..);
1990
1991            let (send, recv) = oneshot::channel();
1992            buffer.map_async(wgpu::MapMode::Read, move |data| {
1993                send.send(data).unwrap();
1994            });
1995            terminal.backend().device.poll(PollType::Wait).unwrap();
1996            recv.recv().unwrap().unwrap();
1997
1998            let data = buffer.get_mapped_range();
1999            let image =
2000                ImageBuffer::<Rgba<u8>, _>::from_raw(surface.width, surface.height, data).unwrap();
2001
2002            let pixels = image.pixels().copied().collect::<Vec<_>>();
2003            let golden = load_from_memory(include_bytes!("goldens/srgb_conversion.png")).unwrap();
2004            let golden_pixels = golden.pixels().map(|(_, _, px)| px).collect::<Vec<_>>();
2005
2006            assert!(
2007                pixels == golden_pixels,
2008                "Rendered image differs from golden"
2009            );
2010        }
2011        surface.buffer.as_ref().unwrap().unmap();
2012    }
2013
2014    #[test]
2015    #[cfg(feature = "png")]
2016    fn png() {
2017        use crate::backend::wgpu_backend::extract_color_image;
2018        let golden = load_from_memory(include_bytes!("goldens/A.png")).unwrap();
2019        let raster = RasterGlyphImage {
2020            x: 0,
2021            y: 0,
2022            width: golden.width() as u16,
2023            height: golden.height() as u16,
2024            pixels_per_em: 0,
2025            format: RasterImageFormat::PNG,
2026            data: include_bytes!("goldens/A.png"),
2027        };
2028
2029        let mut image = vec![];
2030        let extracted = extract_color_image(
2031            &mut image,
2032            raster,
2033            Entry::Cached(CacheRect {
2034                x: 0,
2035                y: 0,
2036                width: golden.width(),
2037                height: golden.height(),
2038            }),
2039            1.0,
2040        )
2041        .expect("Didn't extract png")
2042        .1;
2043
2044        for (l, r) in bytemuck::cast_slice::<_, u8>(&extracted)
2045            .chunks(4)
2046            .zip(golden.pixels())
2047        {
2048            let [r, g, b, a] = r.2 .0;
2049            assert_eq!(l, [a, b, g, r]);
2050        }
2051    }
2052
2053    #[test]
2054    fn bgra() {
2055        use crate::backend::wgpu_backend::extract_color_image;
2056
2057        const BLUE: u8 = 2;
2058        const GREEN: u8 = 4;
2059        const RED: u8 = 8;
2060        const ALPHA: u8 = 127;
2061        let data = [BLUE, GREEN, RED, ALPHA];
2062        let raster = RasterGlyphImage {
2063            x: 0,
2064            y: 0,
2065            width: 1,
2066            height: 1,
2067            pixels_per_em: 0,
2068            format: RasterImageFormat::BitmapPremulBgra32,
2069            data: &data,
2070        };
2071
2072        let mut image = vec![];
2073        let extracted = extract_color_image(
2074            &mut image,
2075            raster,
2076            Entry::Cached(CacheRect {
2077                x: 0,
2078                y: 0,
2079                width: 1,
2080                height: 1,
2081            }),
2082            1.0,
2083        )
2084        .expect("Didn't extract bgra")
2085        .1;
2086
2087        assert_eq!(
2088            bytemuck::bytes_of(&extracted[0]),
2089            [RED * 2, GREEN * 2, BLUE * 2, ALPHA]
2090        );
2091    }
2092
2093    #[test]
2094    fn bmp1() {
2095        let data0 = 0b1000_0001;
2096        let data1 = 0b0001_1000;
2097        let raster = RasterGlyphImage {
2098            x: 0,
2099            y: 0,
2100            width: 4,
2101            height: 2,
2102            pixels_per_em: 0,
2103            format: RasterImageFormat::BitmapMono,
2104            data: &[data0, data1],
2105        };
2106
2107        let mut image = vec![];
2108        let extracted = extract_bw_image(
2109            &mut image,
2110            raster,
2111            Entry::Cached(CacheRect {
2112                x: 0,
2113                y: 0,
2114                width: 4,
2115                height: 2,
2116            }),
2117            1.0,
2118        )
2119        .expect("Didn't extract bmp1")
2120        .1;
2121
2122        assert_eq!(
2123            bytemuck::cast_slice::<_, u8>(&extracted),
2124            bytemuck::cast_slice(&[
2125                [
2126                    [255u8, 255, 255, 255,],
2127                    [255, 255, 255, 0,],
2128                    [255, 255, 255, 0,],
2129                    [255, 255, 255, 0,],
2130                ],
2131                [
2132                    [255, 255, 255, 0,],
2133                    [255, 255, 255, 0,],
2134                    [255, 255, 255, 0,],
2135                    [255, 255, 255, 255,],
2136                ],
2137            ])
2138        );
2139    }
2140
2141    #[test]
2142    fn bmp1_packed() {
2143        let data0 = 0b1000_0001;
2144        let data1 = 0b0001_1000;
2145        let raster = RasterGlyphImage {
2146            x: 0,
2147            y: 0,
2148            width: 8,
2149            height: 2,
2150            pixels_per_em: 0,
2151            format: RasterImageFormat::BitmapMonoPacked,
2152            data: &[data0, data1],
2153        };
2154
2155        let mut image = vec![];
2156        let extracted = extract_bw_image(
2157            &mut image,
2158            raster,
2159            Entry::Cached(CacheRect {
2160                x: 0,
2161                y: 0,
2162                width: 8,
2163                height: 2,
2164            }),
2165            1.0,
2166        )
2167        .expect("Didn't extract bmp1")
2168        .1;
2169
2170        assert_eq!(
2171            bytemuck::cast_slice::<_, u8>(&extracted),
2172            bytemuck::cast_slice(&[
2173                [
2174                    [255u8, 255, 255, 255,],
2175                    [255, 255, 255, 0,],
2176                    [255, 255, 255, 0,],
2177                    [255, 255, 255, 0,],
2178                    [255, 255, 255, 0,],
2179                    [255, 255, 255, 0,],
2180                    [255, 255, 255, 0,],
2181                    [255, 255, 255, 255,],
2182                ],
2183                [
2184                    [255, 255, 255, 0,],
2185                    [255, 255, 255, 0,],
2186                    [255, 255, 255, 0,],
2187                    [255, 255, 255, 255,],
2188                    [255, 255, 255, 255,],
2189                    [255, 255, 255, 0,],
2190                    [255, 255, 255, 0,],
2191                    [255, 255, 255, 0,],
2192                ],
2193            ])
2194        );
2195    }
2196
2197    #[test]
2198    fn bmp2() {
2199        let data0 = 0b1010_1101u8.reverse_bits();
2200        let data1 = 0b0000_1000u8.reverse_bits();
2201        let data2 = 0b0111_1010u8.reverse_bits();
2202        let data3 = 0b0000_1000u8.reverse_bits();
2203        let raster = RasterGlyphImage {
2204            x: 0,
2205            y: 0,
2206            width: 6,
2207            height: 2,
2208            pixels_per_em: 0,
2209            format: RasterImageFormat::BitmapGray2,
2210            data: &[data0, data1, data2, data3],
2211        };
2212
2213        let mut image = vec![];
2214        let extracted = extract_bw_image(
2215            &mut image,
2216            raster,
2217            Entry::Cached(CacheRect {
2218                x: 0,
2219                y: 0,
2220                width: 6,
2221                height: 2,
2222            }),
2223            1.0,
2224        )
2225        .expect("Didn't extract bmp2")
2226        .1;
2227
2228        assert_eq!(
2229            bytemuck::cast_slice::<_, u8>(&extracted),
2230            bytemuck::cast_slice(&[
2231                [
2232                    [255, 255, 255, LUT_2[0b10],],
2233                    [255, 255, 255, LUT_2[0b10],],
2234                    [255, 255, 255, LUT_2[0b11],],
2235                    [255, 255, 255, LUT_2[0b01],],
2236                    [255, 255, 255, LUT_2[0b00],],
2237                    [255, 255, 255, LUT_2[0b00],],
2238                ],
2239                [
2240                    [255, 255, 255, LUT_2[0b01],],
2241                    [255, 255, 255, LUT_2[0b11],],
2242                    [255, 255, 255, LUT_2[0b10],],
2243                    [255, 255, 255, LUT_2[0b10],],
2244                    [255, 255, 255, LUT_2[0b00],],
2245                    [255, 255, 255, LUT_2[0b00],],
2246                ],
2247            ])
2248        );
2249    }
2250
2251    #[test]
2252    fn bmp2_packed() {
2253        let data0 = 0b1010_1101u8.reverse_bits();
2254        let data1 = 0b0000_1000u8.reverse_bits();
2255        let data2 = 0b0111_1010u8.reverse_bits();
2256        let data3 = 0b0000_1000u8.reverse_bits();
2257        let data4 = 0b0000_1000u8.reverse_bits();
2258        let data5 = 0b0000_1000u8.reverse_bits();
2259        let raster = RasterGlyphImage {
2260            x: 0,
2261            y: 0,
2262            width: 6,
2263            height: 4,
2264            pixels_per_em: 0,
2265            format: RasterImageFormat::BitmapGray2Packed,
2266            data: &[data0, data1, data2, data3, data4, data5],
2267        };
2268
2269        let mut image = vec![];
2270        let extracted = extract_bw_image(
2271            &mut image,
2272            raster,
2273            Entry::Cached(CacheRect {
2274                x: 0,
2275                y: 0,
2276                width: 6,
2277                height: 4,
2278            }),
2279            1.0,
2280        )
2281        .expect("Didn't extract bmp2")
2282        .1;
2283
2284        assert_eq!(
2285            bytemuck::cast_slice::<_, u8>(&extracted),
2286            bytemuck::cast_slice(&[
2287                [
2288                    [255, 255, 255, LUT_2[0b10],],
2289                    [255, 255, 255, LUT_2[0b10],],
2290                    [255, 255, 255, LUT_2[0b11],],
2291                    [255, 255, 255, LUT_2[0b01],],
2292                    [255, 255, 255, LUT_2[0b00],],
2293                    [255, 255, 255, LUT_2[0b00],],
2294                ],
2295                [
2296                    [255, 255, 255, LUT_2[0b10],],
2297                    [255, 255, 255, LUT_2[0b00],],
2298                    [255, 255, 255, LUT_2[0b01],],
2299                    [255, 255, 255, LUT_2[0b11],],
2300                    [255, 255, 255, LUT_2[0b10],],
2301                    [255, 255, 255, LUT_2[0b10],],
2302                ],
2303                [
2304                    [255, 255, 255, LUT_2[0b00],],
2305                    [255, 255, 255, LUT_2[0b00],],
2306                    [255, 255, 255, LUT_2[0b10],],
2307                    [255, 255, 255, LUT_2[0b00],],
2308                    [255, 255, 255, LUT_2[0b00],],
2309                    [255, 255, 255, LUT_2[0b00],],
2310                ],
2311                [
2312                    [255, 255, 255, LUT_2[0b10],],
2313                    [255, 255, 255, LUT_2[0b00],],
2314                    [255, 255, 255, LUT_2[0b00],],
2315                    [255, 255, 255, LUT_2[0b00],],
2316                    [255, 255, 255, LUT_2[0b10],],
2317                    [255, 255, 255, LUT_2[0b00],],
2318                ]
2319            ])
2320        );
2321    }
2322
2323    #[test]
2324    fn bmp4() {
2325        let data0 = 0b1010_1000u8.reverse_bits();
2326        let data1 = 0b0000_1000u8.reverse_bits();
2327        let raster = RasterGlyphImage {
2328            x: 0,
2329            y: 0,
2330            width: 1,
2331            height: 2,
2332            pixels_per_em: 0,
2333            format: RasterImageFormat::BitmapGray4,
2334            data: &[data0, data1],
2335        };
2336
2337        let mut image = vec![];
2338        let extracted = extract_bw_image(
2339            &mut image,
2340            raster,
2341            Entry::Cached(CacheRect {
2342                x: 0,
2343                y: 0,
2344                width: 1,
2345                height: 2,
2346            }),
2347            1.0,
2348        )
2349        .expect("Didn't extract bmp4")
2350        .1;
2351
2352        assert_eq!(
2353            bytemuck::cast_slice::<_, u8>(&extracted),
2354            bytemuck::cast_slice(&[
2355                [[255, 255, 255, LUT_4[0b1010],],],
2356                [[255, 255, 255, LUT_4[0b0000],],],
2357            ])
2358        );
2359    }
2360
2361    #[test]
2362    fn bmp4_packed() {
2363        let data0 = 0b1111_0001u8.reverse_bits();
2364        let data1 = 0b0011_1100u8.reverse_bits();
2365        let raster = RasterGlyphImage {
2366            x: 0,
2367            y: 0,
2368            width: 2,
2369            height: 2,
2370            pixels_per_em: 0,
2371            format: RasterImageFormat::BitmapGray4Packed,
2372            data: &[data0, data1],
2373        };
2374
2375        let mut image = vec![];
2376        let extracted = extract_bw_image(
2377            &mut image,
2378            raster,
2379            Entry::Cached(CacheRect {
2380                x: 0,
2381                y: 0,
2382                width: 2,
2383                height: 2,
2384            }),
2385            1.0,
2386        )
2387        .expect("Didn't extract bmp4")
2388        .1;
2389
2390        assert_eq!(
2391            bytemuck::cast_slice::<_, u8>(&extracted),
2392            bytemuck::cast_slice(&[
2393                [
2394                    [255, 255, 255, LUT_4[0b1111],],
2395                    [255, 255, 255, LUT_4[0b0001],],
2396                ],
2397                [
2398                    [255, 255, 255, LUT_4[0b0011],],
2399                    [255, 255, 255, LUT_4[0b1100],],
2400                ],
2401            ])
2402        );
2403    }
2404}