render.rs 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. /* This file is part of DarkFi (https://dark.fi)
  2. *
  3. * Copyright (C) 2020-2026 Dyne.org foundation
  4. *
  5. * This program is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU Affero General Public License as
  7. * published by the Free Software Foundation, either version 3 of the
  8. * License, or (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU Affero General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU Affero General Public License
  16. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  17. */
  18. use crate::{
  19. gfx::{DebugTag, DrawInstruction, DrawMesh, Point, Rectangle, Renderer},
  20. mesh::{Color, MeshBuilder, COLOR_WHITE},
  21. };
  22. use super::atlas::{Atlas, RenderedAtlas, RunIdx};
  23. #[derive(Copy, Clone, Debug, PartialEq, Eq)]
  24. pub struct DebugRenderOptions(u32);
  25. impl DebugRenderOptions {
  26. pub const OFF: DebugRenderOptions = DebugRenderOptions(0b00);
  27. pub const GLYPH: DebugRenderOptions = DebugRenderOptions(0b01);
  28. pub const BASELINE: DebugRenderOptions = DebugRenderOptions(0b10);
  29. pub fn has(self, other: Self) -> bool {
  30. (self.0 & other.0) == other.0
  31. }
  32. }
  33. impl std::ops::BitOr for DebugRenderOptions {
  34. type Output = Self;
  35. fn bitor(self, rhs: Self) -> Self {
  36. Self(self.0 | rhs.0)
  37. }
  38. }
  39. impl std::ops::BitOrAssign for DebugRenderOptions {
  40. fn bitor_assign(&mut self, rhs: Self) {
  41. self.0 |= rhs.0;
  42. }
  43. }
  44. pub fn render_layout(
  45. layout: &parley::Layout<Color>,
  46. renderer: &Renderer,
  47. tag: DebugTag,
  48. ) -> Vec<DrawInstruction> {
  49. render_layout_with_opts(layout, DebugRenderOptions::OFF, renderer, tag)
  50. }
  51. pub fn render_layout_with_opts(
  52. layout: &parley::Layout<Color>,
  53. opts: DebugRenderOptions,
  54. renderer: &Renderer,
  55. tag: DebugTag,
  56. ) -> Vec<DrawInstruction> {
  57. // First pass to create atlas
  58. let mut scale_ctx = swash::scale::ScaleContext::new();
  59. let mut atlas = Atlas::new(renderer, tag);
  60. let mut run_idx = 0;
  61. for line in layout.lines() {
  62. for item in line.items() {
  63. match item {
  64. parley::PositionedLayoutItem::GlyphRun(glyph_run) => {
  65. push_glyphs(&mut atlas, &glyph_run, run_idx, &mut scale_ctx);
  66. run_idx += 1;
  67. }
  68. parley::PositionedLayoutItem::InlineBox(_) => {}
  69. }
  70. }
  71. }
  72. // Render the atlas
  73. let atlas = atlas.make();
  74. // Second pass to draw glyphs
  75. let mut run_idx = 0;
  76. let mut instrs = vec![];
  77. for line in layout.lines() {
  78. for item in line.items() {
  79. match item {
  80. parley::PositionedLayoutItem::GlyphRun(glyph_run) => {
  81. let mesh = render_glyph_run(&glyph_run, run_idx, opts, &atlas, renderer, tag);
  82. instrs.push(DrawInstruction::Draw(mesh));
  83. run_idx += 1;
  84. }
  85. parley::PositionedLayoutItem::InlineBox(_) => {}
  86. }
  87. }
  88. }
  89. instrs
  90. }
  91. fn push_glyphs(
  92. atlas: &mut Atlas,
  93. glyph_run: &parley::GlyphRun<'_, Color>,
  94. run_idx: RunIdx,
  95. scale_ctx: &mut swash::scale::ScaleContext,
  96. ) {
  97. let run = glyph_run.run();
  98. let font = run.font();
  99. let font_size = run.font_size();
  100. let normalized_coords = run.normalized_coords();
  101. let font_ref = swash::FontRef::from_index(font.data.as_ref(), font.index as usize).unwrap();
  102. let mut scaler = scale_ctx
  103. .builder(font_ref)
  104. .size(font_size)
  105. .hint(true)
  106. .normalized_coords(normalized_coords)
  107. .build();
  108. for glyph in glyph_run.glyphs() {
  109. atlas.push_glyph(glyph.id as u16, run_idx, &mut scaler);
  110. }
  111. }
  112. fn render_glyph_run(
  113. glyph_run: &parley::GlyphRun<'_, Color>,
  114. run_idx: usize,
  115. opts: DebugRenderOptions,
  116. atlas: &RenderedAtlas,
  117. renderer: &Renderer,
  118. tag: DebugTag,
  119. ) -> DrawMesh {
  120. let mut run_x = glyph_run.offset();
  121. let run_y = glyph_run.baseline();
  122. let style = glyph_run.style();
  123. let color = style.brush;
  124. //trace!(target: "text::render", "render_glyph_run run_idx={run_idx} baseline={run_y}");
  125. let mut mesh = MeshBuilder::new(tag);
  126. if let Some(underline) = &style.underline {
  127. render_underline(underline, glyph_run, &mut mesh);
  128. }
  129. for glyph in glyph_run.glyphs() {
  130. let glyph_inf = atlas.fetch_uv(glyph.id as u16, run_idx).expect("missing glyph UV rect");
  131. let glyph_x = run_x + glyph.x;
  132. let glyph_y = run_y - glyph.y;
  133. run_x += glyph.advance;
  134. let glyph_rect = Rectangle::new(
  135. glyph_x + glyph_inf.place.left as f32,
  136. glyph_y - glyph_inf.place.top as f32,
  137. glyph_inf.place.width as f32,
  138. glyph_inf.place.height as f32,
  139. );
  140. if opts.has(DebugRenderOptions::GLYPH) {
  141. mesh.draw_outline(&glyph_rect, [0., 1., 0., 0.7], 1.);
  142. }
  143. let color = if glyph_inf.is_color { COLOR_WHITE } else { color };
  144. mesh.draw_box(&glyph_rect, color, &glyph_inf.uv_rect);
  145. }
  146. if opts.has(DebugRenderOptions::BASELINE) {
  147. mesh.draw_filled_box(
  148. &Rectangle::new(glyph_run.offset(), glyph_run.baseline(), glyph_run.advance(), 1.),
  149. [0., 0., 1., 0.7],
  150. );
  151. }
  152. mesh.alloc(renderer).draw_with_textures(vec![atlas.texture.clone()])
  153. }
  154. fn render_underline(
  155. underline: &parley::layout::Decoration<Color>,
  156. glyph_run: &parley::GlyphRun<'_, Color>,
  157. mesh: &mut MeshBuilder,
  158. ) {
  159. let color = underline.brush;
  160. let run_metrics = glyph_run.run().metrics();
  161. let offset = match underline.offset {
  162. Some(offset) => offset,
  163. None => run_metrics.underline_offset,
  164. };
  165. let width = match underline.size {
  166. Some(size) => size,
  167. None => run_metrics.underline_size,
  168. };
  169. // The `offset` is the distance from the baseline to the top of the underline
  170. // so we move the line down by half the width
  171. // Remember that we are using a y-down coordinate system
  172. // If there's a custom width, because this is an underline, we want the custom
  173. // width to go down from the default expectation
  174. let y = glyph_run.baseline() - offset + width / 2.;
  175. let start_x = glyph_run.offset();
  176. let end_x = start_x + glyph_run.advance();
  177. let start = Point::new(start_x, y);
  178. let end = Point::new(end_x, y);
  179. mesh.draw_line(start, end, color, width);
  180. }