Skip to content

Commit

Permalink
Merge pull request #161 from embedded-graphics/dedup
Browse files Browse the repository at this point in the history
Clean up some more
  • Loading branch information
bugadani authored Oct 9, 2023
2 parents 7fe5a77 + 6bf4527 commit 10ac6fd
Show file tree
Hide file tree
Showing 6 changed files with 102 additions and 92 deletions.
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
0.6.3 (???)
==================

## Changed:

- [#161] Internal improvements

[#161]: https://github.com/embedded-graphics/embedded-text/pull/161

0.6.2 (2023-09-04)
==================

Expand Down
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,4 @@ object-chain = "0.1"
[dev-dependencies]
embedded-graphics-simulator = "0.5.0"
sdl2 = "0.35.2"
rayon-core = "=1.11"
58 changes: 23 additions & 35 deletions src/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,25 @@ where
pub fn as_str(&self) -> &str {
self.inner.as_str()
}

fn consume_string(&mut self, string: &'a str, c: char) -> &'a str {
// pointer arithmetic to get the offset of `c` relative to `string`
let offset = {
let ptr_start = string.as_ptr() as usize;
let ptr_cur = self.inner.as_str().as_ptr() as usize;
ptr_cur - ptr_start - c.len_utf8()
};

debug_assert!(string.is_char_boundary(offset));

unsafe {
// SAFETY: we only work with character boundaries and
// offset is <= length
self.inner = string.get_unchecked(offset..).chars();

string.get_unchecked(0..offset)
}
}
}

impl<'a, C> Iterator for Parser<'a, C>
Expand All @@ -131,23 +150,8 @@ where
// find the longest consecutive slice of text for a Word token
for c in &mut self.inner {
if !is_word_char(c) {
// pointer arithmetic to get the offset of `c` relative to `string`
let offset = {
let ptr_start = string.as_ptr() as usize;
let ptr_cur = self.inner.as_str().as_ptr() as usize;
ptr_cur - ptr_start - c.len_utf8()
};
debug_assert!(string.is_char_boundary(offset));
let (word, remainder) = unsafe {
// SAFETY: we only work with character boundaries and
// offset is <= length
(
string.get_unchecked(0..offset),
string.get_unchecked(offset..).chars(),
)
};
self.inner = remainder;
return Some(Token::Word(word));
let consumed = self.consume_string(string, c);
return Some(Token::Word(consumed));
}
}

Expand Down Expand Up @@ -182,24 +186,8 @@ where
len += 1;
}
} else {
// pointer arithmetic to get the offset of `c` relative to `string`
let offset = {
let ptr_start = string.as_ptr() as usize;
let ptr_cur = self.inner.as_str().as_ptr() as usize;
ptr_cur - ptr_start - c.len_utf8()
};
debug_assert!(string.is_char_boundary(offset));
// consume the whitespaces
let (sequence, remainder) = unsafe {
// SAFETY: we only work with character boundaries and
// offset is <= length
(
string.get_unchecked(0..offset),
string.get_unchecked(offset..).chars(),
)
};
self.inner = remainder;
return Some(Token::Whitespace(len, sequence));
let consumed = self.consume_string(string, c);
return Some(Token::Whitespace(len, consumed));
}
}

Expand Down
42 changes: 28 additions & 14 deletions src/plugin/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use core::{
cell::UnsafeCell,
hash::{Hash, Hasher},
marker::PhantomData,
ptr::addr_of,
};
use embedded_graphics::{
draw_target::DrawTarget,
Expand Down Expand Up @@ -74,7 +75,7 @@ where

#[derive(Clone, Debug)]
pub(crate) struct PluginInner<'a, M, C> {
pub(crate) plugin: M,
plugin: M,
state: ProcessingState,
peeked_token: Option<Token<'a, C>>,
}
Expand All @@ -87,7 +88,11 @@ pub(crate) struct PluginWrapper<'a, M, C> {
impl<'a, M: Clone, C: Clone> Clone for PluginWrapper<'a, M, C> {
fn clone(&self) -> Self {
Self {
inner: UnsafeCell::new(self.inner(|this| this.clone())),
inner: UnsafeCell::new(self.with(|this| PluginInner {
plugin: this.plugin.clone(),
state: this.state.clone(),
peeked_token: unsafe { addr_of!(this.peeked_token).read() },
})),
}
}
}
Expand All @@ -97,7 +102,7 @@ where
C: PixelColor,
{
fn hash<H: Hasher>(&self, state: &mut H) {
self.inner(|this| this.state.hash(state))
self.with(|this| this.state.hash(state))
}
}

Expand All @@ -116,10 +121,19 @@ impl<'a, M, C> PluginWrapper<'a, M, C> {
self.inner.into_inner().plugin
}

fn inner<R>(&self, cb: impl FnOnce(&mut PluginInner<'a, M, C>) -> R) -> R {
fn with<R>(&self, cb: impl FnOnce(&PluginInner<'a, M, C>) -> R) -> R {
let inner = unsafe {
// SAFETY: This is safe because we aren't exposing the reference.
core::ptr::NonNull::new_unchecked(self.inner.get()).as_mut()
self.inner.get().as_ref().unwrap_unchecked()
};

cb(inner)
}

fn with_mut<R>(&self, cb: impl FnOnce(&mut PluginInner<'a, M, C>) -> R) -> R {
let inner = unsafe {
// SAFETY: This is safe because we aren't exposing the reference.
self.inner.get().as_mut().unwrap_unchecked()
};

cb(inner)
Expand All @@ -132,23 +146,23 @@ where
M: private::Plugin<'a, C>,
{
pub fn new_line(&self) {
self.inner(|this| this.plugin.new_line());
self.with_mut(|this| this.plugin.new_line());
}

pub fn set_state(&self, state: ProcessingState) {
self.inner(|this| this.state = state);
self.with_mut(|this| this.state = state);
}

#[inline]
pub fn render_token(&self, token: Token<'a, C>) -> Option<Token<'a, C>> {
self.inner(|this| match this.state {
self.with_mut(|this| match this.state {
ProcessingState::Measure => Some(token),
ProcessingState::Render => this.plugin.render_token(token),
})
}

pub fn peek_token(&self, source: &mut Parser<'a, C>) -> Option<Token<'a, C>> {
self.inner(|this| {
self.with_mut(|this| {
if this.peeked_token.is_none() {
this.peeked_token = this.plugin.next_token(|| source.next());
}
Expand All @@ -158,11 +172,11 @@ where
}

pub fn consume_peeked_token(&self) {
self.inner(|this| this.peeked_token = None);
self.with_mut(|this| this.peeked_token = None);
}

pub fn consume_partial(&self, len: usize) {
self.inner(|this| {
self.with_mut(|this| {
// Only string-like tokens can be partially consumed.
debug_assert!(matches!(
this.peeked_token,
Expand Down Expand Up @@ -196,15 +210,15 @@ where
cursor: &mut Cursor,
props: TextBoxProperties<'_, S>,
) {
self.inner(|this| {
self.with_mut(|this| {
this.peeked_token = None;

this.plugin.on_start_render(cursor, &props);
});
}

pub fn on_rendering_finished(&self) {
self.inner(|this| this.plugin.on_rendering_finished());
self.with_mut(|this| this.plugin.on_rendering_finished());
}

pub fn post_render<T, D>(
Expand All @@ -218,7 +232,7 @@ where
T: TextRenderer<Color = C>,
D: DrawTarget<Color = C>,
{
self.inner(|this| {
self.with_mut(|this| {
this.plugin
.post_render(draw_target, character_style, text, bounds)
})
Expand Down
45 changes: 24 additions & 21 deletions src/rendering/line.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,26 @@ where
plugin: &'b PluginWrapper<'a, M, F::Color>,
}

impl<'a, 'b, F, D, M> RenderElementHandler<'a, 'b, F, D, M>
where
F: CharacterStyle + TextRenderer,
<F as CharacterStyle>::Color: From<Rgb888>,
D: DrawTarget<Color = <F as TextRenderer>::Color>,
M: Plugin<'a, <F as TextRenderer>::Color>,
{
fn post_print(&mut self, width: u32, st: &str) -> Result<(), D::Error> {
let bounds = Rectangle::new(
self.pos,
Size::new(width, self.style.line_height().saturating_as()),
);

self.pos += Point::new(width.saturating_as(), 0);

self.plugin
.post_render(self.display, self.style, Some(st), bounds)
}
}

impl<'a, 'c, F, D, M> ElementHandler for RenderElementHandler<'a, 'c, F, D, M>
where
F: CharacterStyle + TextRenderer,
Expand All @@ -105,39 +125,22 @@ where
}

fn whitespace(&mut self, st: &str, _space_count: u32, width: u32) -> Result<(), Self::Error> {
let top_left = self.pos;
if width > 0 {
self.pos = self
.style
self.style
.draw_whitespace(width, self.pos, Baseline::Top, self.display)?;
}

let size = Size::new(width, self.style.line_height().saturating_as());
let bounds = Rectangle::new(top_left, size);

self.plugin
.post_render(self.display, self.style, Some(st), bounds)?;

Ok(())
self.post_print(width, st)
}

fn printed_characters(&mut self, st: &str, width: Option<u32>) -> Result<(), Self::Error> {
let top_left = self.pos;
let render_width = self
.style
.draw_string(st, self.pos, Baseline::Top, self.display)?;

let width = width.unwrap_or((render_width - top_left).x as u32);
let width = width.unwrap_or((render_width - self.pos).x as u32);

self.pos += Point::new(width.saturating_as(), 0);

let size = Size::new(width, self.style.line_height().saturating_as());
let bounds = Rectangle::new(top_left, size);

self.plugin
.post_render(self.display, self.style, Some(st), bounds)?;

Ok(())
self.post_print(width, st)
}

fn move_cursor(&mut self, by: i32) -> Result<(), Self::Error> {
Expand Down
39 changes: 17 additions & 22 deletions src/rendering/line_iter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use crate::{
rendering::{cursor::LineCursor, space_config::SpaceConfig},
style::TextBoxStyle,
};
use az::{SaturatingAs, SaturatingCast};
use az::SaturatingAs;
use embedded_graphics::{pixelcolor::Rgb888, prelude::PixelColor};

/// Parser to break down a line into primitive elements used by measurement and rendering.
Expand Down Expand Up @@ -156,11 +156,10 @@ where
(w, "")
}

fn next_word_fits<E: ElementHandler>(&self, space_width: i32, handler: &E) -> bool {
fn next_word_fits<E: ElementHandler>(&self, handler: &E) -> bool {
let mut cursor = self.cursor.clone();
let mut spaces = self.spaces;

let mut exit = false;
let mut spaces = self.spaces;

// This looks extremely inefficient.
let lookahead = self.plugin.clone();
Expand All @@ -169,9 +168,7 @@ where
// We don't want to count the current token.
lookahead.consume_peeked_token();

if cursor.move_cursor(space_width).is_err() {
return false;
}
let mut exit = false;
while !exit {
let width = match lookahead.peek_token(&mut lookahead_parser) {
Some(Token::Word(w)) | Some(Token::Break(w, _)) => {
Expand Down Expand Up @@ -219,17 +216,13 @@ where
handler.whitespace(string, space_count, 0)?;
return Ok(false);
}
let signed_width = space_width.saturating_as();
let draw_whitespace = (self.empty && self.render_leading_spaces())
|| self.render_trailing_spaces()
|| self.next_word_fits(signed_width, handler);

match self.move_cursor(signed_width) {
match self.move_cursor(space_width.saturating_as()) {
Ok(moved) => {
handler.whitespace(
string,
space_count,
moved.saturating_as::<u32>() * draw_whitespace as u32,
moved.saturating_as::<u32>() * self.should_draw_whitespace(handler) as u32,
)?;
}

Expand Down Expand Up @@ -273,16 +266,12 @@ where
return Ok(());
}

let draw_whitespace = (self.empty && self.render_leading_spaces())
|| self.render_trailing_spaces()
|| self.next_word_fits(space_width.saturating_as(), handler);

match self.move_cursor(space_width.saturating_cast()) {
Ok(moved) if draw_whitespace => handler.whitespace("\t", 0, moved.saturating_as())?,

Ok(moved) | Err(moved) => {
handler.move_cursor(moved.saturating_as())?;
match self.move_cursor(space_width.saturating_as()) {
Ok(moved) if self.should_draw_whitespace(handler) => {
handler.whitespace("\t", 0, moved.saturating_as())?
}

Ok(moved) | Err(moved) => handler.move_cursor(moved)?,
}
Ok(())
}
Expand Down Expand Up @@ -459,6 +448,12 @@ where

Ok(())
}

fn should_draw_whitespace<E: ElementHandler>(&self, handler: &E) -> bool {
(self.empty && self.render_leading_spaces())
|| self.render_trailing_spaces()
|| self.next_word_fits(handler)
}
}

#[cfg(test)]
Expand Down

0 comments on commit 10ac6fd

Please sign in to comment.