mirror of
https://github.com/darkrenaissance/darkfi.git
synced 2026-01-09 14:48:08 -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
|
/// Responsible for aggregating glyphs, and then producing a single software
|
||||||
/// blitted texture usable in a single draw call.
|
/// 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
|
/// ```rust
|
||||||
/// let mut atlas = Atlas::new(&render_api);
|
/// 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 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;
|
/// let atlas_texture_id = atlas.texture_id;
|
||||||
/// ```
|
/// ```
|
||||||
pub struct Atlas<'a> {
|
pub struct Atlas<'a> {
|
||||||
scaler: swash::scale::Scaler<'a>,
|
glyph_keys: Vec<GlyphKey>,
|
||||||
glyph_ids: Vec<swash::GlyphId>,
|
|
||||||
sprites: Vec<swash::scale::image::Image>,
|
sprites: Vec<swash::scale::image::Image>,
|
||||||
// LHS x pos of glyph
|
// LHS x pos of glyph
|
||||||
x_pos: Vec<usize>,
|
x_pos: Vec<usize>,
|
||||||
@@ -62,10 +62,9 @@ pub struct Atlas<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> 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 {
|
Self {
|
||||||
scaler,
|
glyph_keys: vec![],
|
||||||
glyph_ids: vec![],
|
|
||||||
sprites: vec![],
|
sprites: vec![],
|
||||||
x_pos: vec![],
|
x_pos: vec![],
|
||||||
|
|
||||||
@@ -80,12 +79,18 @@ impl<'a> Atlas<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn push_glyph(&mut self, glyph_id: swash::GlyphId) {
|
pub fn push_glyph(
|
||||||
if self.glyph_ids.contains(&glyph_id) {
|
&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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
self.glyph_ids.push(glyph_id);
|
self.glyph_keys.push(glyph_key);
|
||||||
|
|
||||||
let rendered_glyph = swash::scale::Render::new(
|
let rendered_glyph = swash::scale::Render::new(
|
||||||
// Select our source order
|
// Select our source order
|
||||||
@@ -97,7 +102,7 @@ impl<'a> Atlas<'a> {
|
|||||||
)
|
)
|
||||||
// Select the simple alpha (non-subpixel) format
|
// Select the simple alpha (non-subpixel) format
|
||||||
.format(zeno::Format::Alpha)
|
.format(zeno::Format::Alpha)
|
||||||
.render(&mut self.scaler, glyph_id)
|
.render(scaler, glyph_id)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let glyph_width = rendered_glyph.placement.width as usize;
|
let glyph_width = rendered_glyph.placement.width as usize;
|
||||||
@@ -171,12 +176,12 @@ impl<'a> Atlas<'a> {
|
|||||||
/// `rendered_atlas.fetch_uv(my_glyph_id)`.
|
/// `rendered_atlas.fetch_uv(my_glyph_id)`.
|
||||||
/// The texture ID is a struct member: `rendered_atlas.texture_id`.
|
/// The texture ID is a struct member: `rendered_atlas.texture_id`.
|
||||||
pub fn make(self) -> RenderedAtlas {
|
pub fn make(self) -> RenderedAtlas {
|
||||||
//if self.glyph_ids.is_empty() {
|
//if self.glyph_keys.is_empty() {
|
||||||
// return Err(Error::AtlasIsEmpty)
|
// return Err(Error::AtlasIsEmpty)
|
||||||
//}
|
//}
|
||||||
|
|
||||||
assert_eq!(self.glyph_ids.len(), self.sprites.len());
|
assert_eq!(self.glyph_keys.len(), self.sprites.len());
|
||||||
assert_eq!(self.glyph_ids.len(), self.x_pos.len());
|
assert_eq!(self.glyph_keys.len(), self.x_pos.len());
|
||||||
|
|
||||||
let atlas = self.render();
|
let atlas = self.render();
|
||||||
let texture = self.render_api.new_texture(
|
let texture = self.render_api.new_texture(
|
||||||
@@ -188,7 +193,7 @@ impl<'a> Atlas<'a> {
|
|||||||
);
|
);
|
||||||
|
|
||||||
let uv_rects = self.compute_uvs();
|
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());
|
let mut infos = Vec::with_capacity(self.sprites.len());
|
||||||
for (uv_rect, sprite) in uv_rects.into_iter().zip(self.sprites.into_iter()) {
|
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 });
|
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()`.
|
/// Final result computed from `Atlas::make()`.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct RenderedAtlas {
|
pub struct RenderedAtlas {
|
||||||
glyph_ids: Vec<swash::GlyphId>,
|
glyph_keys: Vec<GlyphKey>,
|
||||||
infos: Vec<GlyphInfo>,
|
infos: Vec<GlyphInfo>,
|
||||||
/// Allocated atlas texture.
|
/// Allocated atlas texture.
|
||||||
pub texture: ManagedTexturePtr,
|
pub texture: ManagedTexturePtr,
|
||||||
@@ -277,12 +282,13 @@ pub struct RenderedAtlas {
|
|||||||
|
|
||||||
impl RenderedAtlas {
|
impl RenderedAtlas {
|
||||||
/// Get UV coords for a glyph within the rendered atlas.
|
/// Get UV coords for a glyph within the rendered atlas.
|
||||||
pub fn fetch_uv(&self, glyph_id: swash::GlyphId) -> Option<&GlyphInfo> {
|
pub fn fetch_uv(&self, glyph_id: swash::GlyphId, run_idx: RunIdx) -> Option<&GlyphInfo> {
|
||||||
let glyphs_len = self.glyph_ids.len();
|
let glyphs_len = self.glyph_keys.len();
|
||||||
assert_eq!(glyphs_len, self.infos.len());
|
assert_eq!(glyphs_len, self.infos.len());
|
||||||
|
|
||||||
|
let glyph_key = (glyph_id, run_idx);
|
||||||
for i in 0..glyphs_len {
|
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])
|
return Some(&self.infos[i])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ use crate::{
|
|||||||
mesh::{Color, MeshBuilder, COLOR_WHITE},
|
mesh::{Color, MeshBuilder, COLOR_WHITE},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::atlas::{Atlas, RenderedAtlas};
|
use super::atlas::{Atlas, RenderedAtlas, RunIdx};
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||||
pub struct DebugRenderOptions(u32);
|
pub struct DebugRenderOptions(u32);
|
||||||
@@ -63,15 +63,40 @@ pub fn render_layout_with_opts(
|
|||||||
render_api: &RenderApi,
|
render_api: &RenderApi,
|
||||||
tag: DebugTag,
|
tag: DebugTag,
|
||||||
) -> Vec<DrawInstruction> {
|
) -> 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 run_idx = 0;
|
||||||
let mut instrs = vec![];
|
let mut instrs = vec![];
|
||||||
for line in layout.lines() {
|
for line in layout.lines() {
|
||||||
for item in line.items() {
|
for item in line.items() {
|
||||||
match item {
|
match item {
|
||||||
parley::PositionedLayoutItem::GlyphRun(glyph_run) => {
|
parley::PositionedLayoutItem::GlyphRun(glyph_run) => {
|
||||||
let mesh =
|
let mesh = render_glyph_run(
|
||||||
render_glyph_run(&mut scale_cx, &glyph_run, run_idx, opts, render_api, tag);
|
&mut scale_ctx,
|
||||||
|
&glyph_run,
|
||||||
|
run_idx,
|
||||||
|
opts,
|
||||||
|
&atlas,
|
||||||
|
render_api,
|
||||||
|
tag,
|
||||||
|
);
|
||||||
instrs.push(DrawInstruction::Draw(mesh));
|
instrs.push(DrawInstruction::Draw(mesh));
|
||||||
run_idx += 1;
|
run_idx += 1;
|
||||||
}
|
}
|
||||||
@@ -82,11 +107,38 @@ pub fn render_layout_with_opts(
|
|||||||
instrs
|
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(
|
fn render_glyph_run(
|
||||||
scale_ctx: &mut swash::scale::ScaleContext,
|
scale_ctx: &mut swash::scale::ScaleContext,
|
||||||
glyph_run: &parley::GlyphRun<'_, Color>,
|
glyph_run: &parley::GlyphRun<'_, Color>,
|
||||||
_run_idx: usize,
|
run_idx: usize,
|
||||||
opts: DebugRenderOptions,
|
opts: DebugRenderOptions,
|
||||||
|
atlas: &RenderedAtlas,
|
||||||
render_api: &RenderApi,
|
render_api: &RenderApi,
|
||||||
tag: DebugTag,
|
tag: DebugTag,
|
||||||
) -> DrawMesh {
|
) -> DrawMesh {
|
||||||
@@ -96,8 +148,6 @@ fn render_glyph_run(
|
|||||||
let color = style.brush;
|
let color = style.brush;
|
||||||
//trace!(target: "text::render", "render_glyph_run run_idx={run_idx} baseline={run_y}");
|
//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);
|
let mut mesh = MeshBuilder::new(tag);
|
||||||
|
|
||||||
if let Some(underline) = &style.underline {
|
if let Some(underline) = &style.underline {
|
||||||
@@ -105,7 +155,7 @@ fn render_glyph_run(
|
|||||||
}
|
}
|
||||||
|
|
||||||
for glyph in glyph_run.glyphs() {
|
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_x = run_x + glyph.x;
|
||||||
let glyph_y = run_y - glyph.y;
|
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(
|
fn render_underline(
|
||||||
@@ -170,6 +220,7 @@ fn render_underline(
|
|||||||
fn create_atlas(
|
fn create_atlas(
|
||||||
scale_ctx: &mut swash::scale::ScaleContext,
|
scale_ctx: &mut swash::scale::ScaleContext,
|
||||||
glyph_run: &parley::GlyphRun<'_, Color>,
|
glyph_run: &parley::GlyphRun<'_, Color>,
|
||||||
|
run_idx: usize,
|
||||||
render_api: &RenderApi,
|
render_api: &RenderApi,
|
||||||
tag: DebugTag,
|
tag: DebugTag,
|
||||||
) -> RenderedAtlas {
|
) -> RenderedAtlas {
|
||||||
@@ -179,16 +230,16 @@ fn create_atlas(
|
|||||||
let normalized_coords = run.normalized_coords();
|
let normalized_coords = run.normalized_coords();
|
||||||
let font_ref = swash::FontRef::from_index(font.data.as_ref(), font.index as usize).unwrap();
|
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)
|
.builder(font_ref)
|
||||||
.size(font_size)
|
.size(font_size)
|
||||||
.hint(true)
|
.hint(true)
|
||||||
.normalized_coords(normalized_coords)
|
.normalized_coords(normalized_coords)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
let mut atlas = Atlas::new(scaler, render_api, tag);
|
let mut atlas = Atlas::new(render_api, tag);
|
||||||
for glyph in glyph_run.glyphs() {
|
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.dump(&format!("/tmp/atlas_{run_idx}.png"));
|
||||||
atlas.make()
|
atlas.make()
|
||||||
|
|||||||
Reference in New Issue
Block a user