| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195 |
- /* This file is part of DarkFi (https://dark.fi)
- *
- * Copyright (C) 2020-2026 Dyne.org foundation
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- */
- use crate::{
- gfx::{DebugTag, DrawInstruction, DrawMesh, Point, Rectangle, RenderApi},
- mesh::{Color, MeshBuilder, COLOR_WHITE},
- };
- use super::atlas::{Atlas, RenderedAtlas};
- #[derive(Copy, Clone, Debug, PartialEq, Eq)]
- pub struct DebugRenderOptions(u32);
- impl DebugRenderOptions {
- pub const OFF: DebugRenderOptions = DebugRenderOptions(0b00);
- pub const GLYPH: DebugRenderOptions = DebugRenderOptions(0b01);
- pub const BASELINE: DebugRenderOptions = DebugRenderOptions(0b10);
- pub fn has(self, other: Self) -> bool {
- (self.0 & other.0) == other.0
- }
- }
- impl std::ops::BitOr for DebugRenderOptions {
- type Output = Self;
- fn bitor(self, rhs: Self) -> Self {
- Self(self.0 | rhs.0)
- }
- }
- impl std::ops::BitOrAssign for DebugRenderOptions {
- fn bitor_assign(&mut self, rhs: Self) {
- self.0 |= rhs.0;
- }
- }
- pub fn render_layout(
- layout: &parley::Layout<Color>,
- render_api: &RenderApi,
- tag: DebugTag,
- ) -> Vec<DrawInstruction> {
- render_layout_with_opts(layout, DebugRenderOptions::OFF, render_api, tag)
- }
- pub fn render_layout_with_opts(
- layout: &parley::Layout<Color>,
- opts: DebugRenderOptions,
- render_api: &RenderApi,
- tag: DebugTag,
- ) -> Vec<DrawInstruction> {
- let mut scale_cx = swash::scale::ScaleContext::new();
- let mut run_idx = 0;
- let mut instrs = vec![];
- for line in layout.lines() {
- for item in line.items() {
- match item {
- parley::PositionedLayoutItem::GlyphRun(glyph_run) => {
- let mesh =
- render_glyph_run(&mut scale_cx, &glyph_run, run_idx, opts, render_api, tag);
- instrs.push(DrawInstruction::Draw(mesh));
- run_idx += 1;
- }
- parley::PositionedLayoutItem::InlineBox(_) => {}
- }
- }
- }
- instrs
- }
- fn render_glyph_run(
- scale_ctx: &mut swash::scale::ScaleContext,
- glyph_run: &parley::GlyphRun<'_, Color>,
- _run_idx: usize,
- opts: DebugRenderOptions,
- render_api: &RenderApi,
- tag: DebugTag,
- ) -> DrawMesh {
- let mut run_x = glyph_run.offset();
- let run_y = glyph_run.baseline();
- let style = glyph_run.style();
- let color = style.brush;
- //trace!(target: "text::render", "render_glyph_run run_idx={run_idx} baseline={run_y}");
- let atlas = create_atlas(scale_ctx, glyph_run, render_api, tag);
- let mut mesh = MeshBuilder::new(tag);
- if let Some(underline) = &style.underline {
- render_underline(underline, glyph_run, &mut mesh);
- }
- for glyph in glyph_run.glyphs() {
- let glyph_inf = atlas.fetch_uv(glyph.id as u16).expect("missing glyph UV rect");
- let glyph_x = run_x + glyph.x;
- let glyph_y = run_y - glyph.y;
- run_x += glyph.advance;
- let glyph_rect = Rectangle::new(
- glyph_x + glyph_inf.place.left as f32,
- glyph_y - glyph_inf.place.top as f32,
- glyph_inf.place.width as f32,
- glyph_inf.place.height as f32,
- );
- if opts.has(DebugRenderOptions::GLYPH) {
- mesh.draw_outline(&glyph_rect, [0., 1., 0., 0.7], 1.);
- }
- let color = if glyph_inf.is_color { COLOR_WHITE } else { color };
- mesh.draw_box(&glyph_rect, color, &glyph_inf.uv_rect);
- }
- if opts.has(DebugRenderOptions::BASELINE) {
- mesh.draw_filled_box(
- &Rectangle::new(glyph_run.offset(), glyph_run.baseline(), glyph_run.advance(), 1.),
- [0., 0., 1., 0.7],
- );
- }
- mesh.alloc(render_api).draw_with_textures(vec![atlas.texture])
- }
- fn render_underline(
- underline: &parley::layout::Decoration<Color>,
- glyph_run: &parley::GlyphRun<'_, Color>,
- mesh: &mut MeshBuilder,
- ) {
- let color = underline.brush;
- let run_metrics = glyph_run.run().metrics();
- let offset = match underline.offset {
- Some(offset) => offset,
- None => run_metrics.underline_offset,
- };
- let width = match underline.size {
- Some(size) => size,
- None => run_metrics.underline_size,
- };
- // The `offset` is the distance from the baseline to the top of the underline
- // so we move the line down by half the width
- // Remember that we are using a y-down coordinate system
- // If there's a custom width, because this is an underline, we want the custom
- // width to go down from the default expectation
- let y = glyph_run.baseline() - offset + width / 2.;
- let start_x = glyph_run.offset();
- let end_x = start_x + glyph_run.advance();
- let start = Point::new(start_x, y);
- let end = Point::new(end_x, y);
- mesh.draw_line(start, end, color, width);
- }
- fn create_atlas(
- scale_ctx: &mut swash::scale::ScaleContext,
- glyph_run: &parley::GlyphRun<'_, Color>,
- render_api: &RenderApi,
- tag: DebugTag,
- ) -> RenderedAtlas {
- let run = glyph_run.run();
- let font = run.font();
- let font_size = run.font_size();
- let normalized_coords = run.normalized_coords();
- let font_ref = swash::FontRef::from_index(font.data.as_ref(), font.index as usize).unwrap();
- let scaler = scale_ctx
- .builder(font_ref)
- .size(font_size)
- .hint(true)
- .normalized_coords(normalized_coords)
- .build();
- let mut atlas = Atlas::new(scaler, render_api, tag);
- for glyph in glyph_run.glyphs() {
- atlas.push_glyph(glyph.id as u16);
- }
- //atlas.dump(&format!("/tmp/atlas_{run_idx}.png"));
- atlas.make()
- }
|