mirror of
https://github.com/darkrenaissance/darkfi.git
synced 2026-01-07 22:04:03 -05:00
app/text2: modify text rendering so we build a single atlas for an entire parley Layout. this is handy if a layout does not change (text/styles) just the wrapping. then we can reuse the atlas.
This commit is contained in:
@@ -34,7 +34,8 @@ pub fn make_texture_atlas(render_api: &RenderApi, glyphs: &Vec<Glyph>) -> Render
|
||||
}
|
||||
*/
|
||||
|
||||
//pub struct Sprite(swash::scale::image::Image);
|
||||
pub(super) type RunIdx = usize;
|
||||
type GlyphKey = (swash::GlyphId, RunIdx);
|
||||
|
||||
/// Responsible for aggregating glyphs, and then producing a single software
|
||||
/// blitted texture usable in a single draw call.
|
||||
@@ -42,14 +43,13 @@ pub fn make_texture_atlas(render_api: &RenderApi, glyphs: &Vec<Glyph>) -> Render
|
||||
///
|
||||
/// ```rust
|
||||
/// let mut atlas = Atlas::new(&render_api);
|
||||
/// atlas.push(&glyphs); // repeat as needed for shaped lines
|
||||
/// atlas.push_glyph(glyph, run_idx, &mut scaler);
|
||||
/// let atlas = atlas.make().unwrap();
|
||||
/// let uv = atlas.fetch_uv(glyph_id).unwrap();
|
||||
/// let uv = atlas.fetch_uv(glyph_id, run_idx).unwrap();
|
||||
/// let atlas_texture_id = atlas.texture_id;
|
||||
/// ```
|
||||
pub struct Atlas<'a> {
|
||||
scaler: swash::scale::Scaler<'a>,
|
||||
glyph_ids: Vec<swash::GlyphId>,
|
||||
glyph_keys: Vec<GlyphKey>,
|
||||
sprites: Vec<swash::scale::image::Image>,
|
||||
// LHS x pos of glyph
|
||||
x_pos: Vec<usize>,
|
||||
@@ -62,10 +62,9 @@ pub struct Atlas<'a> {
|
||||
}
|
||||
|
||||
impl<'a> Atlas<'a> {
|
||||
pub fn new(scaler: swash::scale::Scaler<'a>, render_api: &'a RenderApi, tag: DebugTag) -> Self {
|
||||
pub fn new(render_api: &'a RenderApi, tag: DebugTag) -> Self {
|
||||
Self {
|
||||
scaler,
|
||||
glyph_ids: vec![],
|
||||
glyph_keys: vec![],
|
||||
sprites: vec![],
|
||||
x_pos: vec![],
|
||||
|
||||
@@ -80,12 +79,18 @@ impl<'a> Atlas<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn push_glyph(&mut self, glyph_id: swash::GlyphId) {
|
||||
if self.glyph_ids.contains(&glyph_id) {
|
||||
pub fn push_glyph(
|
||||
&mut self,
|
||||
glyph_id: swash::GlyphId,
|
||||
run_idx: RunIdx,
|
||||
scaler: &mut swash::scale::Scaler,
|
||||
) {
|
||||
let glyph_key = (glyph_id, run_idx);
|
||||
if self.glyph_keys.contains(&glyph_key) {
|
||||
return
|
||||
}
|
||||
|
||||
self.glyph_ids.push(glyph_id);
|
||||
self.glyph_keys.push(glyph_key);
|
||||
|
||||
let rendered_glyph = swash::scale::Render::new(
|
||||
// Select our source order
|
||||
@@ -97,7 +102,7 @@ impl<'a> Atlas<'a> {
|
||||
)
|
||||
// Select the simple alpha (non-subpixel) format
|
||||
.format(zeno::Format::Alpha)
|
||||
.render(&mut self.scaler, glyph_id)
|
||||
.render(scaler, glyph_id)
|
||||
.unwrap();
|
||||
|
||||
let glyph_width = rendered_glyph.placement.width as usize;
|
||||
@@ -171,12 +176,12 @@ impl<'a> Atlas<'a> {
|
||||
/// `rendered_atlas.fetch_uv(my_glyph_id)`.
|
||||
/// The texture ID is a struct member: `rendered_atlas.texture_id`.
|
||||
pub fn make(self) -> RenderedAtlas {
|
||||
//if self.glyph_ids.is_empty() {
|
||||
//if self.glyph_keys.is_empty() {
|
||||
// return Err(Error::AtlasIsEmpty)
|
||||
//}
|
||||
|
||||
assert_eq!(self.glyph_ids.len(), self.sprites.len());
|
||||
assert_eq!(self.glyph_ids.len(), self.x_pos.len());
|
||||
assert_eq!(self.glyph_keys.len(), self.sprites.len());
|
||||
assert_eq!(self.glyph_keys.len(), self.x_pos.len());
|
||||
|
||||
let atlas = self.render();
|
||||
let texture = self.render_api.new_texture(
|
||||
@@ -188,7 +193,7 @@ impl<'a> Atlas<'a> {
|
||||
);
|
||||
|
||||
let uv_rects = self.compute_uvs();
|
||||
let glyph_ids = self.glyph_ids;
|
||||
let glyph_keys = self.glyph_keys;
|
||||
|
||||
let mut infos = Vec::with_capacity(self.sprites.len());
|
||||
for (uv_rect, sprite) in uv_rects.into_iter().zip(self.sprites.into_iter()) {
|
||||
@@ -200,7 +205,7 @@ impl<'a> Atlas<'a> {
|
||||
infos.push(GlyphInfo { uv_rect, place: sprite.placement, is_color });
|
||||
}
|
||||
|
||||
RenderedAtlas { glyph_ids, infos, texture }
|
||||
RenderedAtlas { glyph_keys, infos, texture }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -269,7 +274,7 @@ pub struct GlyphInfo {
|
||||
/// Final result computed from `Atlas::make()`.
|
||||
#[derive(Clone)]
|
||||
pub struct RenderedAtlas {
|
||||
glyph_ids: Vec<swash::GlyphId>,
|
||||
glyph_keys: Vec<GlyphKey>,
|
||||
infos: Vec<GlyphInfo>,
|
||||
/// Allocated atlas texture.
|
||||
pub texture: ManagedTexturePtr,
|
||||
@@ -277,12 +282,13 @@ pub struct RenderedAtlas {
|
||||
|
||||
impl RenderedAtlas {
|
||||
/// Get UV coords for a glyph within the rendered atlas.
|
||||
pub fn fetch_uv(&self, glyph_id: swash::GlyphId) -> Option<&GlyphInfo> {
|
||||
let glyphs_len = self.glyph_ids.len();
|
||||
pub fn fetch_uv(&self, glyph_id: swash::GlyphId, run_idx: RunIdx) -> Option<&GlyphInfo> {
|
||||
let glyphs_len = self.glyph_keys.len();
|
||||
assert_eq!(glyphs_len, self.infos.len());
|
||||
|
||||
let glyph_key = (glyph_id, run_idx);
|
||||
for i in 0..glyphs_len {
|
||||
if self.glyph_ids[i] == glyph_id {
|
||||
if self.glyph_keys[i] == glyph_key {
|
||||
return Some(&self.infos[i])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ use crate::{
|
||||
mesh::{Color, MeshBuilder, COLOR_WHITE},
|
||||
};
|
||||
|
||||
use super::atlas::{Atlas, RenderedAtlas};
|
||||
use super::atlas::{Atlas, RenderedAtlas, RunIdx};
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub struct DebugRenderOptions(u32);
|
||||
@@ -63,15 +63,40 @@ pub fn render_layout_with_opts(
|
||||
render_api: &RenderApi,
|
||||
tag: DebugTag,
|
||||
) -> Vec<DrawInstruction> {
|
||||
let mut scale_cx = swash::scale::ScaleContext::new();
|
||||
// First pass to create atlas
|
||||
let mut scale_ctx = swash::scale::ScaleContext::new();
|
||||
let mut atlas = Atlas::new(render_api, tag);
|
||||
let mut run_idx = 0;
|
||||
for line in layout.lines() {
|
||||
for item in line.items() {
|
||||
match item {
|
||||
parley::PositionedLayoutItem::GlyphRun(glyph_run) => {
|
||||
push_glyphs(&mut atlas, &glyph_run, run_idx, &mut scale_ctx, render_api, tag);
|
||||
run_idx += 1;
|
||||
}
|
||||
parley::PositionedLayoutItem::InlineBox(_) => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Render the atlas
|
||||
let atlas = atlas.make();
|
||||
|
||||
// Second pass to draw glyphs
|
||||
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);
|
||||
let mesh = render_glyph_run(
|
||||
&mut scale_ctx,
|
||||
&glyph_run,
|
||||
run_idx,
|
||||
opts,
|
||||
&atlas,
|
||||
render_api,
|
||||
tag,
|
||||
);
|
||||
instrs.push(DrawInstruction::Draw(mesh));
|
||||
run_idx += 1;
|
||||
}
|
||||
@@ -82,11 +107,38 @@ pub fn render_layout_with_opts(
|
||||
instrs
|
||||
}
|
||||
|
||||
fn push_glyphs(
|
||||
atlas: &mut Atlas,
|
||||
glyph_run: &parley::GlyphRun<'_, Color>,
|
||||
run_idx: RunIdx,
|
||||
scale_ctx: &mut swash::scale::ScaleContext,
|
||||
render_api: &RenderApi,
|
||||
tag: DebugTag,
|
||||
) {
|
||||
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 mut scaler = scale_ctx
|
||||
.builder(font_ref)
|
||||
.size(font_size)
|
||||
.hint(true)
|
||||
.normalized_coords(normalized_coords)
|
||||
.build();
|
||||
|
||||
for glyph in glyph_run.glyphs() {
|
||||
atlas.push_glyph(glyph.id as u16, run_idx, &mut scaler);
|
||||
}
|
||||
}
|
||||
|
||||
fn render_glyph_run(
|
||||
scale_ctx: &mut swash::scale::ScaleContext,
|
||||
glyph_run: &parley::GlyphRun<'_, Color>,
|
||||
_run_idx: usize,
|
||||
run_idx: usize,
|
||||
opts: DebugRenderOptions,
|
||||
atlas: &RenderedAtlas,
|
||||
render_api: &RenderApi,
|
||||
tag: DebugTag,
|
||||
) -> DrawMesh {
|
||||
@@ -96,8 +148,6 @@ fn render_glyph_run(
|
||||
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 {
|
||||
@@ -105,7 +155,7 @@ fn render_glyph_run(
|
||||
}
|
||||
|
||||
for glyph in glyph_run.glyphs() {
|
||||
let glyph_inf = atlas.fetch_uv(glyph.id as u16).expect("missing glyph UV rect");
|
||||
let glyph_inf = atlas.fetch_uv(glyph.id as u16, run_idx).expect("missing glyph UV rect");
|
||||
|
||||
let glyph_x = run_x + glyph.x;
|
||||
let glyph_y = run_y - glyph.y;
|
||||
@@ -133,7 +183,7 @@ fn render_glyph_run(
|
||||
);
|
||||
}
|
||||
|
||||
mesh.alloc(render_api).draw_with_textures(vec![atlas.texture])
|
||||
mesh.alloc(render_api).draw_with_textures(vec![atlas.texture.clone()])
|
||||
}
|
||||
|
||||
fn render_underline(
|
||||
@@ -170,6 +220,7 @@ fn render_underline(
|
||||
fn create_atlas(
|
||||
scale_ctx: &mut swash::scale::ScaleContext,
|
||||
glyph_run: &parley::GlyphRun<'_, Color>,
|
||||
run_idx: usize,
|
||||
render_api: &RenderApi,
|
||||
tag: DebugTag,
|
||||
) -> RenderedAtlas {
|
||||
@@ -179,16 +230,16 @@ fn create_atlas(
|
||||
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
|
||||
let mut 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);
|
||||
let mut atlas = Atlas::new(render_api, tag);
|
||||
for glyph in glyph_run.glyphs() {
|
||||
atlas.push_glyph(glyph.id as u16);
|
||||
atlas.push_glyph(glyph.id as u16, run_idx, &mut scaler);
|
||||
}
|
||||
//atlas.dump(&format!("/tmp/atlas_{run_idx}.png"));
|
||||
atlas.make()
|
||||
|
||||
Reference in New Issue
Block a user