diff --git a/examples/text/src/buffer.rs b/examples/text/src/buffer.rs new file mode 100644 index 00000000..56faa24a --- /dev/null +++ b/examples/text/src/buffer.rs @@ -0,0 +1 @@ +pub struct TextBuffer; diff --git a/examples/text/src/font/layout.rs b/examples/text/src/font/layout.rs new file mode 100644 index 00000000..0b0047dc --- /dev/null +++ b/examples/text/src/font/layout.rs @@ -0,0 +1,42 @@ +use core::marker::PhantomData; + +pub struct FontLayoutGlyph<'a, T: 'a> { + pub start: usize, + pub end: usize, + pub x: f32, + pub w: f32, + #[cfg(feature = "ab_glyph")] + pub inner: Option, + #[cfg(feature = "rusttype")] + pub inner: rusttype::PositionedGlyph<'a>, + pub phantom: PhantomData<&'a T>, +} + +pub struct FontLayoutLine<'a> { + pub glyphs: Vec>, +} + +impl<'a> FontLayoutLine<'a> { + pub fn draw(&self, mut f: F) { + for glyph in self.glyphs.iter() { + #[cfg(feature = "ab_glyph")] + if let Some(ref outline) = glyph.inner { + let bb = outline.px_bounds(); + let x = bb.min.x as i32; + let y = bb.min.y as i32; + outline.draw(|off_x, off_y, v| { + f(x + off_x as i32, y + off_y as i32, v); + }); + } + + #[cfg(feature = "rusttype")] + if let Some(bb) = glyph.inner.pixel_bounding_box() { + let x = bb.min.x; + let y = bb.min.y; + glyph.inner.draw(|off_x, off_y, v| { + f(x + off_x as i32, y + off_y as i32, v); + }); + } + } + } +} diff --git a/examples/text/src/font/matches.rs b/examples/text/src/font/matches.rs new file mode 100644 index 00000000..4f5675c0 --- /dev/null +++ b/examples/text/src/font/matches.rs @@ -0,0 +1,177 @@ +use super::{Font, FontShapeGlyph, FontShapeLine, FontShapeSpan}; + +pub struct FontMatches<'a> { + pub fonts: Vec<&'a Font<'a>>, +} + +impl<'a> FontMatches<'a> { + fn shape_span(&self, string: &str, start_span: usize, end_span: usize) -> FontShapeSpan { + let span = &string[start_span..end_span]; + + let mut spans_by_font = Vec::with_capacity(self.fonts.len()); + for (font_i, font) in self.fonts.iter().enumerate() { + let font_scale = font.rustybuzz.units_per_em() as f32; + + let mut buffer = rustybuzz::UnicodeBuffer::new(); + buffer.push_str(span); + buffer.guess_segment_properties(); + let direction = buffer.direction(); + if font_i == 0 { + //println!("{:?}, {:?}: '{}'", script, direction, span); + } + + let glyph_buffer = rustybuzz::shape(&font.rustybuzz, &[], buffer); + let glyph_infos = glyph_buffer.glyph_infos(); + let glyph_positions = glyph_buffer.glyph_positions(); + + let mut misses = 0; + let mut glyphs = Vec::with_capacity(glyph_infos.len()); + for (info, pos) in glyph_infos.iter().zip(glyph_positions.iter()) { + let x_advance = pos.x_advance as f32 / font_scale; + let y_advance = pos.y_advance as f32 / font_scale; + let x_offset = pos.x_offset as f32 / font_scale; + let y_offset = pos.y_offset as f32 / font_scale; + + //println!(" {:?} {:?}", info, pos); + if info.glyph_id == 0 { + misses += 1; + } + + #[cfg(feature = "ab_glyph")] + let inner = ab_glyph::GlyphId(info.glyph_id as u16); + + #[cfg(feature = "rusttype")] + let inner = font.rusttype.glyph(rusttype::GlyphId(info.glyph_id as u16)); + + glyphs.push(FontShapeGlyph { + start: start_span + info.cluster as usize, + end: end_span, // Set later + x_advance, + y_advance, + x_offset, + y_offset, + #[cfg(feature = "ab_glyph")] + font: &font.ab_glyph, + inner, + }); + } + + // Adjust end of glyphs + match direction { + rustybuzz::Direction::LeftToRight => { + for i in (1..glyphs.len()).rev() { + let next_start = glyphs[i].start; + let next_end = glyphs[i].end; + let prev = &mut glyphs[i - 1]; + if prev.start == next_start { + prev.end = next_end; + } else { + prev.end = next_start; + } + } + }, + rustybuzz::Direction::RightToLeft => { + for i in 1..glyphs.len() { + let next_start = glyphs[i - 1].start; + let next_end = glyphs[i - 1].end; + let prev = &mut glyphs[i]; + if prev.start == next_start { + prev.end = next_end; + } else { + prev.end = next_start; + } + } + }, + //TODO: other directions + _ => (), + } + + let span = FontShapeSpan { + direction, + glyphs + }; + if misses == 0 { + return span; + } else { + spans_by_font.push((misses, span)); + } + } + + let mut least_misses_i = 0; + let mut least_misses = usize::MAX; + for (i, (misses, _)) in spans_by_font.iter().enumerate() { + if *misses < least_misses { + least_misses_i = i; + least_misses = *misses; + } + } + + if least_misses_i > 0 { + //println!("MISSES {}, {}", least_misses_i, least_misses); + } + + spans_by_font.remove(least_misses_i).1 + } + + fn shape_line(&self, string: &str, start_line: usize, end_line: usize) -> FontShapeLine { + use unicode_script::{Script, UnicodeScript}; + + let line = &string[start_line..end_line]; + + //TODO: more special handling of characters + let mut spans = Vec::new(); + + let mut start = 0; + let mut prev = Script::Unknown; + for (i, c) in line.char_indices() { + if ! line.is_char_boundary(i) { + continue; + } + + let cur = c.script(); + if prev != cur && prev != Script::Unknown { + // No combination, start new span + spans.push(self.shape_span(string, start_line + start, start_line + i)); + start = i; + prev = Script::Unknown; + } else { + prev = cur; + } + } + + spans.push(self.shape_span(string, start_line + start, start_line + line.len())); + + let bidi = unicode_bidi::BidiInfo::new(line, None); + let rtl = if bidi.paragraphs.is_empty() { + false + } else { + assert_eq!(bidi.paragraphs.len(), 1); + bidi.paragraphs[0].level.is_rtl() + }; + + FontShapeLine { + rtl, + spans, + } + } + + pub fn shape(&self, string: &str) -> Vec { + let mut lines = Vec::new(); + + let mut start = 0; + for (i, c) in string.char_indices() { + if ! string.is_char_boundary(i) { + continue; + } + + if c == '\n' { + lines.push(self.shape_line(string, start, i)); + start = i + 1; + } + } + + lines.push(self.shape_line(string, start, string.len())); + + lines + } +} diff --git a/examples/text/src/font/mod.rs b/examples/text/src/font/mod.rs new file mode 100644 index 00000000..a596347c --- /dev/null +++ b/examples/text/src/font/mod.rs @@ -0,0 +1,33 @@ +pub use self::layout::*; +mod layout; + +pub use self::matches::*; +mod matches; + +pub use self::shape::*; +mod shape; + +pub use self::system::*; +mod system; + +pub struct Font<'a> { + data: &'a [u8], + pub rustybuzz: rustybuzz::Face<'a>, + #[cfg(feature = "ab_glyph")] + pub ab_glyph: ab_glyph::FontRef<'a>, + #[cfg(feature = "rusttype")] + pub rusttype: rusttype::Font<'a>, +} + +impl<'a> Font<'a> { + pub fn new(data: &'a [u8], index: u32) -> Option { + Some(Self { + data, + rustybuzz: rustybuzz::Face::from_slice(data, index)?, + #[cfg(feature = "ab_glyph")] + ab_glyph: ab_glyph::FontRef::try_from_slice_and_index(data, index).ok()?, + #[cfg(feature = "rusttype")] + rusttype: rusttype::Font::try_from_bytes_and_index(data, index)?, + }) + } +} diff --git a/examples/text/src/font/shape.rs b/examples/text/src/font/shape.rs new file mode 100644 index 00000000..877ae336 --- /dev/null +++ b/examples/text/src/font/shape.rs @@ -0,0 +1,123 @@ +#[cfg(feature = "ab_glyph")] +use ab_glyph::Font; +use core::marker::PhantomData; + +use super::{FontLayoutGlyph, FontLayoutLine}; + +pub struct FontShapeGlyph<'a> { + pub start: usize, + pub end: usize, + pub x_advance: f32, + pub y_advance: f32, + pub x_offset: f32, + pub y_offset: f32, + #[cfg(feature = "ab_glyph")] + pub font: &'a ab_glyph::FontRef<'a>, + #[cfg(feature = "ab_glyph")] + pub inner: ab_glyph::GlyphId, + #[cfg(feature = "rusttype")] + pub inner: rusttype::Glyph<'a>, +} + +pub struct FontShapeSpan<'a> { + pub direction: rustybuzz::Direction, + pub glyphs: Vec>, +} + +pub struct FontShapeLine<'a> { + pub rtl: bool, + pub spans: Vec>, +} + +impl<'a> FontShapeLine<'a> { + pub fn layout(&self, font_size: i32, line_width: i32, lines: &mut Vec>) { + let mut push_line = true; + let mut glyphs = Vec::new(); + + let mut x = 0.0; + let mut y = 0.0; + for span in self.spans.iter() { + let mut span_width = 0.0; + for glyph in span.glyphs.iter() { + let x_advance = font_size as f32 * glyph.x_advance; + span_width += x_advance; + } + + if self.rtl { + if glyphs.is_empty() { + x = line_width as f32; + } + x -= span_width; + } + + for glyph in span.glyphs.iter() { + let x_advance = font_size as f32 * glyph.x_advance; + let y_advance = font_size as f32 * glyph.y_advance; + let x_offset = font_size as f32 * glyph.x_offset; + let y_offset = font_size as f32 * glyph.y_offset; + + //TODO: make wrapping optional + if self.rtl { + if x < 0.0 { + let mut glyphs_swap = Vec::new(); + std::mem::swap(&mut glyphs, &mut glyphs_swap); + lines.push(FontLayoutLine { glyphs: glyphs_swap }); + + x = line_width as f32 - x_advance; + y = 0.0; + } + } else { + if x + x_advance > line_width as f32 { + let mut glyphs_swap = Vec::new(); + std::mem::swap(&mut glyphs, &mut glyphs_swap); + lines.push(FontLayoutLine { glyphs: glyphs_swap }); + push_line = false; + + x = 0.0; + y = 0.0; + } + } + + #[cfg(feature = "ab_glyph")] + let inner = glyph.font.outline_glyph( + glyph.inner.with_scale_and_position( + font_size as f32, + ab_glyph::point( + x + x_offset, + y + y_offset, + ) + ) + ); + + #[cfg(feature = "rusttype")] + let inner = glyph.inner.clone() + .scaled(rusttype::Scale::uniform(font_size as f32)) + .positioned(rusttype::point( + x + x_offset, + y + y_offset, + )); + + glyphs.push(FontLayoutGlyph { + start: glyph.start, + end: glyph.end, + x, + w: x_advance, + inner, + phantom: PhantomData, + }); + push_line = true; + + x += x_advance; + y += y_advance; + } + + if self.rtl { + x -= span_width; + } + } + + if push_line { + lines.push(FontLayoutLine { glyphs }); + } + } +} diff --git a/examples/text/src/font/system.rs b/examples/text/src/font/system.rs new file mode 100644 index 00000000..5edebc88 --- /dev/null +++ b/examples/text/src/font/system.rs @@ -0,0 +1,61 @@ +use super::{Font, FontMatches}; + +pub struct FontSystem<'a> { + fonts: Vec>, +} + +impl<'a> FontSystem<'a> { + pub fn new() -> Self { + Self { + fonts: Vec::new(), + } + } + + pub fn add(&mut self, font: Font<'a>) { + self.fonts.push(font); + } + + pub fn matches(&'a self, patterns: &[&str]) -> Option> { + let mut fonts = Vec::new(); + for font in self.fonts.iter() { + for rec in font.rustybuzz.names() { + if rec.name_id == 4 && rec.is_unicode() { + let mut words: Vec = Vec::new(); + + let mut i = 0; + while i + 1 < rec.name.len() { + words.push( + (rec.name[i + 1] as u16) | + ((rec.name[i] as u16) << 8) + ); + i += 2; + } + + match String::from_utf16(&words) { + Ok(name) => { + let mut matched = false; + for pattern in patterns.iter() { + println!("Matching font name '{}' with pattern '{}'", name, pattern); + if name.contains(pattern) { + matched = true; + } + } + if matched { + println!("Matched font name '{}'", name); + fonts.push(font); + } else { + println!("Did not match font name '{}'", name); + } + }, + Err(_) => () + } + } + } + } + if ! fonts.is_empty() { + Some(FontMatches { fonts }) + } else { + None + } + } +} diff --git a/examples/text/src/lib.rs b/examples/text/src/lib.rs new file mode 100644 index 00000000..8d9b486c --- /dev/null +++ b/examples/text/src/lib.rs @@ -0,0 +1,2 @@ +pub use self::font::*; +mod font; diff --git a/examples/text/src/main.rs b/examples/text/src/main.rs index df00693b..d580d1da 100644 --- a/examples/text/src/main.rs +++ b/examples/text/src/main.rs @@ -1,6 +1,3 @@ -#[cfg(feature = "ab_glyph")] -use ab_glyph::Font as AbGlyphFont; - use orbclient::{Color, EventOption, Renderer, Window, WindowFlag}; use std::{ cmp, @@ -9,436 +6,10 @@ use std::{ marker::PhantomData, time::Instant, }; - -struct FontLayoutGlyph<'a, T: 'a> { - start: usize, - end: usize, - x: f32, - w: f32, - #[cfg(feature = "ab_glyph")] - inner: Option, - #[cfg(feature = "rusttype")] - inner: rusttype::PositionedGlyph<'a>, - phantom: PhantomData<&'a T>, -} - -struct FontLayoutLine<'a> { - glyphs: Vec>, -} - -impl<'a> FontLayoutLine<'a> { - pub fn draw( - &self, - r: &mut R, - line_x: i32, - line_y: i32, - color: Color, - ) { - for glyph in self.glyphs.iter() { - #[cfg(feature = "ab_glyph")] - if let Some(ref outline) = glyph.inner { - let bb = outline.px_bounds(); - let x = line_x + bb.min.x as i32; - let y = line_y + bb.min.y as i32; - outline.draw(|off_x, off_y, v| { - let c = (v * 255.0) as u32; - r.pixel(x + off_x as i32, y + off_y as i32, Color{ - data: c << 24 | (color.data & 0x00FF_FFFF) - }); - }); - } - - #[cfg(feature = "rusttype")] - if let Some(bb) = glyph.inner.pixel_bounding_box() { - let x = line_x + bb.min.x; - let y = line_y + bb.min.y; - glyph.inner.draw(|off_x, off_y, v| { - let c = (v * 255.0) as u32; - r.pixel(x + off_x as i32, y + off_y as i32, Color{ - data: c << 24 | (color.data & 0x00FF_FFFF) - }); - }); - } - } - } -} - -struct FontShapeGlyph<'a> { - start: usize, - end: usize, - x_advance: f32, - y_advance: f32, - x_offset: f32, - y_offset: f32, - #[cfg(feature = "ab_glyph")] - font: &'a ab_glyph::FontRef<'a>, - #[cfg(feature = "ab_glyph")] - inner: ab_glyph::GlyphId, - #[cfg(feature = "rusttype")] - inner: rusttype::Glyph<'a>, -} - -struct FontShapeSpan<'a> { - direction: rustybuzz::Direction, - glyphs: Vec>, -} - -struct FontShapeLine<'a> { - rtl: bool, - spans: Vec>, -} - -impl<'a> FontShapeLine<'a> { - pub fn layout(&self, font_size: i32, line_width: i32, lines: &mut Vec>) { - let mut push_line = true; - let mut glyphs = Vec::new(); - - let mut x = 0.0; - let mut y = 0.0; - for span in self.spans.iter() { - let mut span_width = 0.0; - for glyph in span.glyphs.iter() { - let x_advance = font_size as f32 * glyph.x_advance; - span_width += x_advance; - } - - if self.rtl { - if glyphs.is_empty() { - x = line_width as f32; - } - x -= span_width; - } - - for glyph in span.glyphs.iter() { - let x_advance = font_size as f32 * glyph.x_advance; - let y_advance = font_size as f32 * glyph.y_advance; - let x_offset = font_size as f32 * glyph.x_offset; - let y_offset = font_size as f32 * glyph.y_offset; - - //TODO: make wrapping optional - if self.rtl { - if x < 0.0 { - let mut glyphs_swap = Vec::new(); - std::mem::swap(&mut glyphs, &mut glyphs_swap); - lines.push(FontLayoutLine { glyphs: glyphs_swap }); - - x = line_width as f32 - x_advance; - y = 0.0; - } - } else { - if x + x_advance > line_width as f32 { - let mut glyphs_swap = Vec::new(); - std::mem::swap(&mut glyphs, &mut glyphs_swap); - lines.push(FontLayoutLine { glyphs: glyphs_swap }); - push_line = false; - - x = 0.0; - y = 0.0; - } - } - - #[cfg(feature = "ab_glyph")] - let inner = glyph.font.outline_glyph( - glyph.inner.with_scale_and_position( - font_size as f32, - ab_glyph::point( - x + x_offset, - y + y_offset, - ) - ) - ); - - #[cfg(feature = "rusttype")] - let inner = glyph.inner.clone() - .scaled(rusttype::Scale::uniform(font_size as f32)) - .positioned(rusttype::point( - x + x_offset, - y + y_offset, - )); - - glyphs.push(FontLayoutGlyph { - start: glyph.start, - end: glyph.end, - x, - w: x_advance, - inner, - phantom: PhantomData, - }); - push_line = true; - - x += x_advance; - y += y_advance; - } - - if self.rtl { - x -= span_width; - } - } - - if push_line { - lines.push(FontLayoutLine { glyphs }); - } - } -} - -struct FontMatches<'a> { - fonts: Vec<&'a Font<'a>>, -} - -impl<'a> FontMatches<'a> { - fn shape_span(&self, string: &str, start_span: usize, end_span: usize) -> FontShapeSpan { - let span = &string[start_span..end_span]; - - let mut spans_by_font = Vec::with_capacity(self.fonts.len()); - for (font_i, font) in self.fonts.iter().enumerate() { - let font_scale = font.rustybuzz.units_per_em() as f32; - - let mut buffer = rustybuzz::UnicodeBuffer::new(); - buffer.push_str(span); - buffer.guess_segment_properties(); - let script = buffer.script(); - let direction = buffer.direction(); - if font_i == 0 { - println!("{:?}, {:?}: '{}'", script, direction, span); - } - - let glyph_buffer = rustybuzz::shape(&font.rustybuzz, &[], buffer); - let glyph_infos = glyph_buffer.glyph_infos(); - let glyph_positions = glyph_buffer.glyph_positions(); - - let mut misses = 0; - let mut glyphs = Vec::with_capacity(glyph_infos.len()); - for (info, pos) in glyph_infos.iter().zip(glyph_positions.iter()) { - let x_advance = pos.x_advance as f32 / font_scale; - let y_advance = pos.y_advance as f32 / font_scale; - let x_offset = pos.x_offset as f32 / font_scale; - let y_offset = pos.y_offset as f32 / font_scale; - - //println!(" {:?} {:?}", info, pos); - if info.glyph_id == 0 { - misses += 1; - } - - #[cfg(feature = "ab_glyph")] - let inner = ab_glyph::GlyphId(info.glyph_id as u16); - - #[cfg(feature = "rusttype")] - let inner = font.rusttype.glyph(rusttype::GlyphId(info.glyph_id as u16)); - - glyphs.push(FontShapeGlyph { - start: start_span + info.cluster as usize, - end: end_span, // Set later - x_advance, - y_advance, - x_offset, - y_offset, - #[cfg(feature = "ab_glyph")] - font: &font.ab_glyph, - inner, - }); - } - - // Adjust end of glyphs - match direction { - rustybuzz::Direction::LeftToRight => { - for i in (1..glyphs.len()).rev() { - let next_start = glyphs[i].start; - let next_end = glyphs[i].end; - let prev = &mut glyphs[i - 1]; - if prev.start == next_start { - prev.end = next_end; - } else { - prev.end = next_start; - } - } - }, - rustybuzz::Direction::RightToLeft => { - for i in 1..glyphs.len() { - let next_start = glyphs[i - 1].start; - let next_end = glyphs[i - 1].end; - let prev = &mut glyphs[i]; - if prev.start == next_start { - prev.end = next_end; - } else { - prev.end = next_start; - } - } - }, - //TODO: other directions - _ => (), - } - - let span = FontShapeSpan { - direction, - glyphs - }; - if misses == 0 { - return span; - } else { - spans_by_font.push((misses, span)); - } - } - - let mut least_misses_i = 0; - let mut least_misses = usize::MAX; - for (i, (misses, _)) in spans_by_font.iter().enumerate() { - if *misses < least_misses { - least_misses_i = i; - least_misses = *misses; - } - } - - if least_misses_i > 0 { - println!("MISSES {}, {}", least_misses_i, least_misses); - } - - spans_by_font.remove(least_misses_i).1 - } - - fn shape_line(&self, string: &str, start_line: usize, end_line: usize) -> FontShapeLine { - use unicode_script::{Script, UnicodeScript}; - - let line = &string[start_line..end_line]; - - //TODO: more special handling of characters - let mut spans = Vec::new(); - - let mut start = 0; - let mut prev = Script::Unknown; - for (i, c) in line.char_indices() { - if ! line.is_char_boundary(i) { - continue; - } - - let cur = c.script(); - if prev != cur && prev != Script::Unknown { - // No combination, start new span - spans.push(self.shape_span(string, start_line + start, start_line + i)); - start = i; - prev = Script::Unknown; - } else { - prev = cur; - } - } - - spans.push(self.shape_span(string, start_line + start, start_line + line.len())); - - let bidi = unicode_bidi::BidiInfo::new(line, None); - let rtl = if bidi.paragraphs.is_empty() { - false - } else { - assert_eq!(bidi.paragraphs.len(), 1); - bidi.paragraphs[0].level.is_rtl() - }; - - FontShapeLine { - rtl, - spans, - } - } - - pub fn shape(&self, string: &str) -> Vec { - let mut lines = Vec::new(); - - let mut start = 0; - for (i, c) in string.char_indices() { - if ! string.is_char_boundary(i) { - continue; - } - - if c == '\n' { - lines.push(self.shape_line(string, start, i)); - start = i + 1; - } - } - - lines.push(self.shape_line(string, start, string.len())); - - lines - } -} - -struct Font<'a> { - data: &'a [u8], - pub rustybuzz: rustybuzz::Face<'a>, - #[cfg(feature = "ab_glyph")] - pub ab_glyph: ab_glyph::FontRef<'a>, - #[cfg(feature = "rusttype")] - pub rusttype: rusttype::Font<'a>, -} - -impl<'a> Font<'a> { - pub fn new(data: &'a [u8], index: u32) -> Option { - Some(Self { - data, - rustybuzz: rustybuzz::Face::from_slice(data, index)?, - #[cfg(feature = "ab_glyph")] - ab_glyph: ab_glyph::FontRef::try_from_slice_and_index(data, index).ok()?, - #[cfg(feature = "rusttype")] - rusttype: rusttype::Font::try_from_bytes_and_index(data, index)?, - }) - } -} - -struct FontSystem<'a> { - fonts: Vec>, -} - -impl<'a> FontSystem<'a> { - pub fn new() -> Self { - Self { - fonts: Vec::new(), - } - } - - pub fn add(&mut self, font: Font<'a>) { - self.fonts.push(font); - } - - pub fn matches(&'a self, patterns: &[&str]) -> Option> { - let mut fonts = Vec::new(); - for font in self.fonts.iter() { - for rec in font.rustybuzz.names() { - if rec.name_id == 4 && rec.is_unicode() { - let mut words: Vec = Vec::new(); - - let mut i = 0; - while i + 1 < rec.name.len() { - words.push( - (rec.name[i + 1] as u16) | - ((rec.name[i] as u16) << 8) - ); - i += 2; - } - - match String::from_utf16(&words) { - Ok(name) => { - let mut matched = false; - for pattern in patterns.iter() { - println!("Matching font name '{}' with pattern '{}'", name, pattern); - if name.contains(pattern) { - matched = true; - } - } - if matched { - println!("Matched font name '{}'", name); - fonts.push(font); - } else { - println!("Did not match font name '{}'", name); - } - }, - Err(_) => () - } - } - } - } - if ! fonts.is_empty() { - Some(FontMatches { fonts }) - } else { - None - } - } -} +use text::{ + Font, + FontSystem, +}; fn main() { let display_scale = match orbclient::get_display_size() { @@ -614,12 +185,12 @@ fn main() { } } - line.draw( - &mut window, - line_x, - line_y, - font_color - ); + line.draw(|x, y, v| { + let c = (v * 255.0) as u32; + window.pixel(line_x + x, line_y + y, Color{ + data: c << 24 | (font_color.data & 0x00FF_FFFF) + }); + }); } line_y += line_height;