1use crate::paths::{
6 Line, maybe_path_for_char, maybe_path_for_cursor_style, path_for_strikeout, path_for_underline,
7};
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
10pub struct Rgb {
11 pub r: u8,
12 pub g: u8,
13 pub b: u8,
14}
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
17pub enum CursorShape {
18 Block,
19 Underline,
20 Beam,
21 HollowBlock,
22 Hidden,
23}
24
25#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
26pub struct CursorStyle {
27 pub shape: CursorShape,
28 pub blinking: bool,
29}
30
31#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
32pub struct Flags(pub u8);
33
34impl Flags {
35 pub const BOLD: Self = Self(1 << 0);
36 pub const ITALIC: Self = Self(1 << 1);
37 pub const UNDERLINE: Self = Self(1 << 2);
38 pub const STRIKEOUT: Self = Self(1 << 3);
39 pub const BOLD_ITALIC: Self = Self(1 << 0 | 1 << 1);
40
41 pub fn empty() -> Self {
42 Self(0)
43 }
44
45 pub fn intersects(&self, other: Self) -> bool {
46 self.0 & other.0 != 0
47 }
48
49 pub fn contains(&self, other: Self) -> bool {
50 self.0 & other.0 == other.0
51 }
52}
53
54impl std::ops::BitOr for Flags {
55 type Output = Self;
56 fn bitor(self, rhs: Self) -> Self::Output {
57 Self(self.0 | rhs.0)
58 }
59}
60
61impl std::ops::BitAnd for Flags {
62 type Output = Self;
63 fn bitand(self, rhs: Self) -> Self::Output {
64 Self(self.0 & rhs.0)
65 }
66}
67use carnelian::Size;
68use carnelian::color::Color;
69use carnelian::drawing::{FontFace, Glyph, TextGrid};
70use carnelian::render::{
71 BlendMode, Context as RenderContext, Fill, FillRule, Layer, Raster, Style,
72};
73use carnelian::scene::{LayerGroup, SceneOrder};
74use euclid::{Rect, point2};
75use rustc_hash::{FxHashMap, FxHashSet};
76use std::collections::BTreeSet;
77use std::collections::hash_map::Entry;
78use std::mem;
79
80const SCALE_FACTORS: &[f32] = &[1.0, 1.25, 2.0, 3.0, 4.0];
85
86pub fn get_scale_factor(dpi: &BTreeSet<u32>, actual_dpi: f32) -> f32 {
88 let mut scale_factor = 0;
89 for value in dpi.iter() {
90 if *value as f32 > actual_dpi {
91 break;
92 }
93 scale_factor += 1;
94 }
95 *SCALE_FACTORS.get(scale_factor).unwrap_or(SCALE_FACTORS.last().unwrap())
96}
97
98pub fn cell_size_from_cell_height(font_set: &FontSet, height: f32) -> Size {
100 let rounded_height = height.round();
101
102 let face = &font_set.font.face;
107 let width = face.glyph_index('0').map_or(height / 2.0, |glyph_index| {
108 let ascent = face.ascender() as f32;
109 let descent = face.descender() as f32;
110 let horizontal_advance =
111 face.glyph_hor_advance(glyph_index).expect("glyph_hor_advance") as f32;
112 rounded_height * horizontal_advance / (ascent - descent)
113 });
114
115 Size::new(width.round(), rounded_height)
116}
117
118#[derive(Clone)]
119pub struct FontSet {
120 font: FontFace,
121 bold_font: Option<FontFace>,
122 italic_font: Option<FontFace>,
123 bold_italic_font: Option<FontFace>,
124 fallback_fonts: Vec<FontFace>,
125}
126
127impl FontSet {
128 pub fn new(
129 font: FontFace,
130 bold_font: Option<FontFace>,
131 italic_font: Option<FontFace>,
132 bold_italic_font: Option<FontFace>,
133 fallback_fonts: Vec<FontFace>,
134 ) -> Self {
135 Self { font, bold_font, italic_font, bold_italic_font, fallback_fonts }
136 }
137}
138
139#[derive(PartialEq, Eq, Hash, Clone, Copy, Debug)]
140pub enum LayerContent {
141 Cursor(CursorStyle),
142 Char((char, Flags)),
143}
144
145#[derive(PartialEq)]
146struct LayerId {
147 content: LayerContent,
148 rgb: Rgb,
149}
150
151fn maybe_raster_for_cursor_style(
152 render_context: &mut RenderContext,
153 cursor_style: CursorStyle,
154 cell_size: &Size,
155) -> Option<Raster> {
156 maybe_path_for_cursor_style(render_context, cursor_style, cell_size).as_ref().map(|p| {
157 let mut raster_builder = render_context.raster_builder().expect("raster_builder");
158 raster_builder.add(p, None);
159 raster_builder.build()
160 })
161}
162
163fn maybe_fallback_glyph_for_char(
164 render_context: &mut RenderContext,
165 c: char,
166 cell_size: &Size,
167) -> Option<Glyph> {
168 maybe_path_for_char(render_context, c, cell_size).as_ref().map(|p| {
169 let mut raster_builder = render_context.raster_builder().expect("raster_builder");
170 raster_builder.add(p, None);
171 let raster = raster_builder.build();
172 let bounding_box = Rect::from_size(*cell_size);
173 Glyph { raster, bounding_box }
174 })
175}
176
177fn maybe_glyph_for_char(
178 context: &mut RenderContext,
179 c: char,
180 flags: Flags,
181 textgrid: &TextGrid,
182 font_set: &FontSet,
183) -> Option<Glyph> {
184 let maybe_bold_italic_font = match flags & Flags::BOLD_ITALIC {
185 Flags::BOLD => font_set.bold_font.as_ref(),
186 Flags::ITALIC => font_set.italic_font.as_ref(),
187 Flags::BOLD_ITALIC => font_set.bold_italic_font.as_ref(),
188 _ => None,
189 };
190 let scale = textgrid.scale;
191 let offset = textgrid.offset;
192
193 for font in maybe_bold_italic_font
202 .iter()
203 .map(|font| *font)
204 .chain(std::iter::once(&font_set.font))
205 .chain(font_set.fallback_fonts.iter())
206 {
207 if let Some(glyph_index) = font.face.glyph_index(c) {
208 let glyph = Glyph::with_scale_and_offset(context, font, scale, offset, glyph_index);
209 return Some(glyph);
210 }
211 }
212
213 maybe_fallback_glyph_for_char(context, c, &textgrid.cell_size)
215}
216
217fn maybe_raster_for_char(
218 context: &mut RenderContext,
219 c: char,
220 flags: Flags,
221 textgrid: &TextGrid,
222 font_set: &FontSet,
223) -> Option<Raster> {
224 let maybe_glyph = maybe_glyph_for_char(context, c, flags, textgrid, font_set);
226
227 let maybe_extra_raster = if flags.intersects(Flags::UNDERLINE | Flags::STRIKEOUT) {
229 let mut raster_builder = context.raster_builder().expect("raster_builder");
230 if flags.contains(Flags::UNDERLINE) {
231 let line_metrics = font_set.font.face.underline_metrics();
233 raster_builder.add(
234 &path_for_underline(
235 &textgrid.cell_size,
236 context,
237 line_metrics.map(|line_metrics| Line::new(line_metrics, textgrid)),
238 ),
239 None,
240 );
241 }
242 if flags.contains(Flags::STRIKEOUT) {
243 let line_metrics = font_set.font.face.strikeout_metrics();
244 raster_builder.add(
245 &path_for_strikeout(
246 &textgrid.cell_size,
247 context,
248 line_metrics.map(|line_metrics| Line::new(line_metrics, textgrid)),
249 ),
250 None,
251 );
252 }
253 Some(raster_builder.build())
254 } else {
255 None
256 };
257
258 match (maybe_glyph, maybe_extra_raster) {
260 (Some(glyph), Some(extra_raster)) => Some(glyph.raster + extra_raster),
261 (Some(glyph), None) => Some(glyph.raster),
262 (None, Some(extra_raster)) => Some(extra_raster),
263 _ => None,
264 }
265}
266
267fn maybe_raster_for_layer_content(
268 render_context: &mut RenderContext,
269 content: &LayerContent,
270 column: usize,
271 row: usize,
272 textgrid: &TextGrid,
273 font_set: &FontSet,
274 raster_cache: &mut FxHashMap<LayerContent, Option<Raster>>,
275) -> Option<Raster> {
276 raster_cache
277 .entry(*content)
278 .or_insert_with(|| match content {
279 LayerContent::Cursor(cursor_style) => {
280 maybe_raster_for_cursor_style(render_context, *cursor_style, &textgrid.cell_size)
281 }
282 LayerContent::Char((c, flags)) => {
283 maybe_raster_for_char(render_context, *c, *flags, textgrid, font_set)
284 }
285 })
286 .as_ref()
287 .map(|r| {
288 let cell_size = &textgrid.cell_size;
289 let cell_position =
290 point2(cell_size.width * column as f32, cell_size.height * row as f32);
291 let raster = r.clone().translate(cell_position.to_vector().to_i32());
292 let empty_raster = {
295 let raster_builder = render_context.raster_builder().unwrap();
296 raster_builder.build()
297 };
298 raster + empty_raster
299 })
300}
301
302fn make_color(term_color: &Rgb) -> Color {
303 Color { r: term_color.r, g: term_color.g, b: term_color.b, a: 0xff }
304}
305
306#[derive(PartialEq, Debug)]
307pub struct RenderableLayer {
308 pub order: usize,
309 pub column: usize,
310 pub row: usize,
311 pub content: LayerContent,
312 pub rgb: Rgb,
313}
314
315pub struct Offset {
316 pub column: usize,
317 pub row: usize,
318}
319
320pub fn renderable_layers<'b>(
321 screen: &'b vt100::Screen,
322 default_bg: Rgb,
323 default_fg: Rgb,
324 offset: &'b Offset,
325) -> impl Iterator<Item = RenderableLayer> + 'b {
326 let columns = screen.size().1 as usize;
327 let rows = screen.size().0 as usize;
328 let stride = columns * 4;
329
330 let resolve_color = move |vt100_color: vt100::Color, is_fg: bool| -> Rgb {
331 match vt100_color {
332 vt100::Color::Default => {
333 if is_fg {
334 default_fg
335 } else {
336 default_bg
337 }
338 }
339 vt100::Color::Idx(idx) => {
340 let ansi_colors = [
342 Rgb { r: 0, g: 0, b: 0 },
343 Rgb { r: 170, g: 0, b: 0 },
344 Rgb { r: 0, g: 170, b: 0 },
345 Rgb { r: 170, g: 85, b: 0 },
346 Rgb { r: 0, g: 0, b: 170 },
347 Rgb { r: 170, g: 0, b: 170 },
348 Rgb { r: 0, g: 170, b: 170 },
349 Rgb { r: 170, g: 170, b: 170 },
350 Rgb { r: 85, g: 85, b: 85 },
351 Rgb { r: 255, g: 85, b: 85 },
352 Rgb { r: 85, g: 255, b: 85 },
353 Rgb { r: 255, g: 255, b: 85 },
354 Rgb { r: 85, g: 85, b: 255 },
355 Rgb { r: 255, g: 85, b: 255 },
356 Rgb { r: 85, g: 255, b: 255 },
357 Rgb { r: 255, g: 255, b: 255 },
358 ];
359 if (idx as usize) < ansi_colors.len() {
360 ansi_colors[idx as usize]
361 } else {
362 default_fg
363 }
364 }
365 vt100::Color::Rgb(r, g, b) => Rgb { r, g, b },
366 }
367 };
368
369 let cursor_pos = screen.cursor_position();
370 let hide_cursor = screen.hide_cursor();
371
372 (0..rows).flat_map(move |r| {
373 (0..columns).flat_map(move |col| {
374 let cell = screen.cell(r as u16, col as u16);
375 let row_idx = r + offset.row;
376 let cell_order = row_idx * stride + (col + offset.column);
377 let is_cursor = !hide_cursor && cursor_pos.0 == r as u16 && cursor_pos.1 == col as u16;
378
379 let (mut fg, mut bg, inverse, bold, italic, underline, contents, has_contents) =
380 if let Some(cell) = cell {
381 (
382 cell.fgcolor(),
383 cell.bgcolor(),
384 cell.inverse(),
385 cell.bold(),
386 cell.italic(),
387 cell.underline(),
388 cell.contents().to_string(),
389 cell.has_contents(),
390 )
391 } else {
392 (
393 vt100::Color::Default,
394 vt100::Color::Default,
395 false,
396 false,
397 false,
398 false,
399 String::new(),
400 false,
401 )
402 };
403
404 if inverse {
405 std::mem::swap(&mut fg, &mut bg);
406 }
407
408 if is_cursor {
409 std::mem::swap(&mut fg, &mut bg);
410 }
411
412 let fg_rgb = resolve_color(fg, true);
413 let bg_rgb = resolve_color(bg, false);
414
415 let is_default_bg = matches!(bg, vt100::Color::Default) && !is_cursor && !inverse;
416
417 let mut layers = Vec::with_capacity(3);
418
419 if !is_default_bg || is_cursor {
420 layers.push(RenderableLayer {
421 order: cell_order,
422 column: col,
423 row: row_idx,
424 content: LayerContent::Cursor(CursorStyle {
425 shape: CursorShape::Block,
426 blinking: false,
427 }),
428 rgb: bg_rgb,
429 });
430 }
431
432 let mut flags = Flags::empty();
433 if bold {
434 flags = Flags(flags.0 | Flags::BOLD.0);
435 }
436 if italic {
437 flags = Flags(flags.0 | Flags::ITALIC.0);
438 }
439 if underline {
440 flags = Flags(flags.0 | Flags::UNDERLINE.0);
441 }
442
443 let c = if has_contents { contents.chars().next().unwrap_or(' ') } else { ' ' };
444 let layer_content = LayerContent::Char((if c == '\t' { ' ' } else { c }, flags));
445
446 layers.push(RenderableLayer {
447 order: cell_order + columns * 3,
448 column: col,
449 row: row_idx,
450 content: layer_content,
451 rgb: fg_rgb,
452 });
453
454 layers.into_iter()
455 })
456 })
457}
458pub struct Renderer {
459 textgrid: TextGrid,
460 raster_cache: FxHashMap<LayerContent, Option<Raster>>,
461 layers: FxHashMap<SceneOrder, LayerId>,
462 old_layers: FxHashSet<SceneOrder>,
463 new_layers: FxHashSet<SceneOrder>,
464}
465
466impl Renderer {
467 pub fn new(font_set: &FontSet, cell_size: &Size) -> Self {
468 let textgrid = TextGrid::new(&font_set.font, cell_size);
469 let raster_cache = FxHashMap::default();
470 let layers = FxHashMap::default();
471 let old_layers = FxHashSet::default();
472 let new_layers = FxHashSet::default();
473
474 Self { textgrid, raster_cache, layers, old_layers, new_layers }
475 }
476
477 pub fn render<I>(
478 &mut self,
479 layer_group: &mut dyn LayerGroup,
480 render_context: &mut RenderContext,
481 font_set: &FontSet,
482 layers: I,
483 ) where
484 I: IntoIterator<Item = RenderableLayer>,
485 {
486 let raster_cache = &mut self.raster_cache;
487 let textgrid = &self.textgrid;
488
489 for RenderableLayer { order, column, row, content, rgb } in layers.into_iter() {
491 let id = LayerId { content, rgb };
492 let order = SceneOrder::try_from(order).unwrap_or_else(|e| panic!("{}", e));
493
494 self.old_layers.remove(&order);
496
497 match self.layers.entry(order) {
498 Entry::Occupied(entry) => {
499 if *entry.get() != id {
500 let raster = maybe_raster_for_layer_content(
501 render_context,
502 &id.content,
503 column,
504 row,
505 textgrid,
506 font_set,
507 raster_cache,
508 );
509 if let Some(raster) = raster {
510 let value = entry.into_mut();
511 *value = id;
512
513 let did_not_exist = self.new_layers.insert(order);
514 assert!(
515 did_not_exist,
516 "multiple layers with order: {}",
517 order.as_u32()
518 );
519 layer_group.insert(
520 order,
521 Layer {
522 raster,
523 clip: None,
524 style: Style {
525 fill_rule: FillRule::NonZero,
526 fill: Fill::Solid(make_color(&rgb)),
527 blend_mode: BlendMode::Over,
528 },
529 },
530 );
531 } else {
532 entry.remove_entry();
533 layer_group.remove(order);
534 }
535 } else {
536 let did_not_exist = self.new_layers.insert(order);
537 assert!(did_not_exist, "multiple layers with order: {}", order.as_u32());
538 }
539 }
540 Entry::Vacant(entry) => {
541 let raster = maybe_raster_for_layer_content(
542 render_context,
543 &id.content,
544 column,
545 row,
546 textgrid,
547 font_set,
548 raster_cache,
549 );
550 if let Some(raster) = raster {
551 entry.insert(id);
552 let did_not_exist = self.new_layers.insert(order);
553 assert!(did_not_exist, "multiple layers with order: {}", order.as_u32());
554 layer_group.insert(
555 order,
556 Layer {
557 raster,
558 clip: None,
559 style: Style {
560 fill_rule: FillRule::NonZero,
561 fill: Fill::Solid(make_color(&rgb)),
562 blend_mode: BlendMode::Over,
563 },
564 },
565 );
566 }
567 }
568 }
569 }
570
571 for order in self.old_layers.drain() {
573 self.layers.remove(&order);
574 layer_group.remove(order);
575 }
576
577 mem::swap(&mut self.old_layers, &mut self.new_layers);
579 }
580}