Skip to content
This repository has been archived by the owner on Nov 26, 2024. It is now read-only.

Commit

Permalink
#58 simplified glyph iterator
Browse files Browse the repository at this point in the history
  • Loading branch information
bennobuilder committed Mar 22, 2024
1 parent c96baed commit d1881c9
Show file tree
Hide file tree
Showing 6 changed files with 57 additions and 104 deletions.
18 changes: 13 additions & 5 deletions crates/attributed_string/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ pub mod fonts_cache;
pub mod glyph;
pub mod path_builder;
pub mod tokens;
pub mod utils;

use crate::tokens::shape::ShapeToken;
use attrs::{Attrs, AttrsInterval, AttrsIntervals};
Expand All @@ -14,6 +15,7 @@ use tokens::{
line::{LineToken, SpanRange},
span::SpanToken,
};
use utils::is_range_within;

#[derive(Debug, Clone)]
struct AttributedString {
Expand Down Expand Up @@ -121,20 +123,25 @@ impl AttributedString {
if line.get_span_ranges().is_empty() {
continue;
}
let line_range = line.get_range();

let mut pos = Vec2::new(0.0, 0.0);
let mut max_ascent: f32 = 0.0;
let mut max_descent: f32 = 0.0;

for span_range in line.get_span_ranges().iter() {
let span = &self.spans[span_range.index];
let span = &mut self.spans[span_range.index];
let attrs = &self.attrs_intervals.intervals[span.get_attrs_index()].val;
let font_size = attrs.get_font_size();

for glyph_token in span.iter_glyphs_in_range(line.get_range()) {
for glyph_token in span.iter_glyphs_mut() {
if !is_range_within(glyph_token.get_range(), &line_range) {
continue;
}

let advance = glyph_token.get_glyph().advance * font_size;

// glyph_token.set_transform(pos); // TODO
glyph_token.set_transform(pos);

pos += advance;
max_ascent = max_ascent.max(glyph_token.get_glyph().ascent);
Expand All @@ -151,10 +158,11 @@ impl AttributedString {
for span in self.spans.iter() {
for glyph in span.iter_glyphs() {
log::info!(
"Glyph: Range({:?}), {:?}, AttrsIndex({})",
"Glyph: Range({:?}), {:?}, AttrsIndex({}), {:?}",
glyph.get_range(),
span.get_level(),
span.get_attrs_index()
span.get_attrs_index(),
glyph.get_transform()
);
}
}
Expand Down
6 changes: 1 addition & 5 deletions crates/attributed_string/src/tokens/shape/glyph.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
use super::ShapeToken;
use crate::{
attrs::Attrs,
fonts_cache::FontsCache,
glyph::{self, Glyph},
};
use crate::glyph::Glyph;
use glam::Vec2;
use std::ops::Range;

Expand Down
4 changes: 4 additions & 0 deletions crates/attributed_string/src/tokens/shape/text_fragment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ impl TextFragmentToken {
pub fn get_tokens(&self) -> &Vec<GlyphToken> {
&self.tokens
}

pub(crate) fn get_tokens_mut(&mut self) -> &mut Vec<GlyphToken> {
&mut self.tokens
}
}

impl ShapeToken for TextFragmentToken {
Expand Down
4 changes: 4 additions & 0 deletions crates/attributed_string/src/tokens/shape/word_separator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ impl WordSeparatorToken {
pub fn get_tokens(&self) -> &Vec<GlyphToken> {
&self.tokens
}

pub(crate) fn get_tokens_mut(&mut self) -> &mut Vec<GlyphToken> {
&mut self.tokens
}
}

impl ShapeToken for WordSeparatorToken {
Expand Down
124 changes: 30 additions & 94 deletions crates/attributed_string/src/tokens/span.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use super::shape::{
glyph::GlyphToken, linebreak::LinebreakToken, text_fragment::TextFragmentToken,
word_separator::WordSeparatorToken, ShapeBuffer, ShapeToken, ShapeTokenVariant,
word_separator::WordSeparatorToken, ShapeBuffer, ShapeTokenVariant,
};
use crate::{attrs::Attrs, fonts_cache::FontsCache};
use glam::Vec2;
Expand Down Expand Up @@ -150,102 +150,38 @@ impl SpanToken {
Vec2::default()
}

#[inline]
pub fn iter_glyphs(&self) -> GlyphTokenRefIterator {
GlyphTokenRefIterator::new(self, self.range.clone())
}

#[inline]
pub fn iter_glyphs_in_range(&self, range: Range<usize>) -> GlyphTokenRefIterator {
GlyphTokenRefIterator::new(self, range)
}
}

pub struct GlyphTokenRefIterator<'a> {
span_token: &'a SpanToken,
// Tracks the current position in the top-level tokens vector
token_index: usize,
// Tracks the position within the current ShapeTokenVariant's GlyphToken vector
glyph_index: usize,
// The range of interest for yielding GlyphTokens
range: Range<usize>,
}

impl<'a> GlyphTokenRefIterator<'a> {
pub fn new(span_token: &'a SpanToken, range: Range<usize>) -> Self {
Self {
span_token,
token_index: 0,
glyph_index: 0,
range,
}
pub fn iter_glyphs<'a>(&'a self) -> impl Iterator<Item = &'a GlyphToken> + 'a {
self.tokens
.iter()
.flat_map(|token_variant| match token_variant {
ShapeTokenVariant::Glyph(token) => Box::new(std::iter::once(token))
as Box<dyn Iterator<Item = &'a GlyphToken> + 'a>,
ShapeTokenVariant::TextFragment(token) => Box::new(token.get_tokens().iter())
as Box<dyn Iterator<Item = &'a GlyphToken> + 'a>,
ShapeTokenVariant::WordSeparator(token) => Box::new(token.get_tokens().iter())
as Box<dyn Iterator<Item = &'a GlyphToken> + 'a>,
_ => Box::new(std::iter::empty()) as Box<dyn Iterator<Item = &'a GlyphToken> + 'a>,
})
}

fn is_within_range(&self, token_range: &Range<usize>) -> bool {
self.range.start <= token_range.start && token_range.end <= self.range.end
}
}

impl<'a> Iterator for GlyphTokenRefIterator<'a> {
type Item = &'a GlyphToken;

fn next(&mut self) -> Option<Self::Item> {
while self.token_index < self.span_token.tokens.len() {
match &self.span_token.tokens[self.token_index] {
// Move to next token after yielding a glyph
ShapeTokenVariant::Glyph(glyph) if self.glyph_index == 0 => {
self.token_index += 1;
if self.is_within_range(glyph.get_range()) {
return Some(glyph);
}
pub(crate) fn iter_glyphs_mut<'a>(
&'a mut self,
) -> impl Iterator<Item = &'a mut GlyphToken> + 'a {
self.tokens
.iter_mut()
.flat_map(|token_variant| match token_variant {
ShapeTokenVariant::Glyph(token) => Box::new(std::iter::once(token))
as Box<dyn Iterator<Item = &'a mut GlyphToken> + 'a>,
ShapeTokenVariant::TextFragment(token) => {
Box::new(token.get_tokens_mut().iter_mut())
as Box<dyn Iterator<Item = &'a mut GlyphToken> + 'a>
}
ShapeTokenVariant::WordSeparator(token)
if self.glyph_index < token.get_tokens().len() =>
{
let glyph = &token.get_tokens()[self.glyph_index];

if self.is_within_range(token.get_range()) {
self.glyph_index += 1;
if self.glyph_index == token.get_tokens().len() {
// Reset glyph_index and move to next token for next call
self.glyph_index = 0;
self.token_index += 1;
}
return Some(glyph);
} else {
// Reset glyph_index and move to next token for next call
self.glyph_index = 0;
self.token_index += 1;
}
ShapeTokenVariant::WordSeparator(token) => {
Box::new(token.get_tokens_mut().iter_mut())
as Box<dyn Iterator<Item = &'a mut GlyphToken> + 'a>
}
ShapeTokenVariant::TextFragment(token)
if self.glyph_index < token.get_tokens().len() =>
{
let glyph = &token.get_tokens()[self.glyph_index];

if self.is_within_range(token.get_range()) {
self.glyph_index += 1;
if self.glyph_index == token.get_tokens().len() {
// Reset glyph_index and move to next token for next call
self.glyph_index = 0;
self.token_index += 1;
}
return Some(glyph);
} else {
// Reset glyph_index and move to next token for next call
self.glyph_index = 0;
self.token_index += 1;
}
}
// For non-glyph-carrying tokens or if no more glyphs in current token, move to the next one
_ => {
self.token_index += 1;
self.glyph_index = 0;
}
}
}

// No more tokens or glyphs left
None
_ => Box::new(std::iter::empty())
as Box<dyn Iterator<Item = &'a mut GlyphToken> + 'a>,
})
}
}
5 changes: 5 additions & 0 deletions crates/attributed_string/src/utils.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
use std::ops::Range;

pub fn is_range_within(a: &Range<usize>, b: &Range<usize>) -> bool {
b.start <= a.start && a.end <= b.end
}

0 comments on commit d1881c9

Please sign in to comment.