render.rs 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  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, RenderApi},
  20. mesh::{Color, MeshBuilder, COLOR_WHITE},
  21. };
  22. use super::atlas::{Atlas, RenderedAtlas};
  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. render_api: &RenderApi,
  47. tag: DebugTag,
  48. ) -> Vec<DrawInstruction> {
  49. render_layout_with_opts(layout, DebugRenderOptions::OFF, render_api, tag)
  50. }
  51. pub fn render_layout_with_opts(
  52. layout: &parley::Layout<Color>,
  53. opts: DebugRenderOptions,
  54. render_api: &RenderApi,
  55. tag: DebugTag,
  56. ) -> Vec<DrawInstruction> {
  57. let mut scale_cx = swash::scale::ScaleContext::new();
  58. let mut run_idx = 0;
  59. let mut instrs = vec![];
  60. for line in layout.lines() {
  61. for item in line.items() {
  62. match item {
  63. parley::PositionedLayoutItem::GlyphRun(glyph_run) => {
  64. let mesh =
  65. render_glyph_run(&mut scale_cx, &glyph_run, run_idx, opts, render_api, tag);
  66. instrs.push(DrawInstruction::Draw(mesh));
  67. run_idx += 1;
  68. }
  69. parley::PositionedLayoutItem::InlineBox(_) => {}
  70. }
  71. }
  72. }
  73. instrs
  74. }
  75. fn render_glyph_run(
  76. scale_ctx: &mut swash::scale::ScaleContext,
  77. glyph_run: &parley::GlyphRun<'_, Color>,
  78. _run_idx: usize,
  79. opts: DebugRenderOptions,
  80. render_api: &RenderApi,
  81. tag: DebugTag,
  82. ) -> DrawMesh {
  83. let mut run_x = glyph_run.offset();
  84. let run_y = glyph_run.baseline();
  85. let style = glyph_run.style();
  86. let color = style.brush;
  87. //trace!(target: "text::render", "render_glyph_run run_idx={run_idx} baseline={run_y}");
  88. let atlas = create_atlas(scale_ctx, glyph_run, render_api, tag);
  89. let mut mesh = MeshBuilder::new(tag);
  90. if let Some(underline) = &style.underline {
  91. render_underline(underline, glyph_run, &mut mesh);
  92. }
  93. for glyph in glyph_run.glyphs() {
  94. let glyph_inf = atlas.fetch_uv(glyph.id as u16).expect("missing glyph UV rect");
  95. let glyph_x = run_x + glyph.x;
  96. let glyph_y = run_y - glyph.y;
  97. run_x += glyph.advance;
  98. let glyph_rect = Rectangle::new(
  99. glyph_x + glyph_inf.place.left as f32,
  100. glyph_y - glyph_inf.place.top as f32,
  101. glyph_inf.place.width as f32,
  102. glyph_inf.place.height as f32,
  103. );
  104. if opts.has(DebugRenderOptions::GLYPH) {
  105. mesh.draw_outline(&glyph_rect, [0., 1., 0., 0.7], 1.);
  106. }
  107. let color = if glyph_inf.is_color { COLOR_WHITE } else { color };
  108. mesh.draw_box(&glyph_rect, color, &glyph_inf.uv_rect);
  109. }
  110. if opts.has(DebugRenderOptions::BASELINE) {
  111. mesh.draw_filled_box(
  112. &Rectangle::new(glyph_run.offset(), glyph_run.baseline(), glyph_run.advance(), 1.),
  113. [0., 0., 1., 0.7],
  114. );
  115. }
  116. mesh.alloc(render_api).draw_with_textures(vec![atlas.texture])
  117. }
  118. fn render_underline(
  119. underline: &parley::layout::Decoration<Color>,
  120. glyph_run: &parley::GlyphRun<'_, Color>,
  121. mesh: &mut MeshBuilder,
  122. ) {
  123. let color = underline.brush;
  124. let run_metrics = glyph_run.run().metrics();
  125. let offset = match underline.offset {
  126. Some(offset) => offset,
  127. None => run_metrics.underline_offset,
  128. };
  129. let width = match underline.size {
  130. Some(size) => size,
  131. None => run_metrics.underline_size,
  132. };
  133. // The `offset` is the distance from the baseline to the top of the underline
  134. // so we move the line down by half the width
  135. // Remember that we are using a y-down coordinate system
  136. // If there's a custom width, because this is an underline, we want the custom
  137. // width to go down from the default expectation
  138. let y = glyph_run.baseline() - offset + width / 2.;
  139. let start_x = glyph_run.offset();
  140. let end_x = start_x + glyph_run.advance();
  141. let start = Point::new(start_x, y);
  142. let end = Point::new(end_x, y);
  143. mesh.draw_line(start, end, color, width);
  144. }
  145. fn create_atlas(
  146. scale_ctx: &mut swash::scale::ScaleContext,
  147. glyph_run: &parley::GlyphRun<'_, Color>,
  148. render_api: &RenderApi,
  149. tag: DebugTag,
  150. ) -> RenderedAtlas {
  151. let run = glyph_run.run();
  152. let font = run.font();
  153. let font_size = run.font_size();
  154. let normalized_coords = run.normalized_coords();
  155. let font_ref = swash::FontRef::from_index(font.data.as_ref(), font.index as usize).unwrap();
  156. let scaler = scale_ctx
  157. .builder(font_ref)
  158. .size(font_size)
  159. .hint(true)
  160. .normalized_coords(normalized_coords)
  161. .build();
  162. let mut atlas = Atlas::new(scaler, render_api, tag);
  163. for glyph in glyph_run.glyphs() {
  164. atlas.push_glyph(glyph.id as u16);
  165. }
  166. //atlas.dump(&format!("/tmp/atlas_{run_idx}.png"));
  167. atlas.make()
  168. }